1 /* 2 * 3 * BlueZ - Bluetooth protocol stack for Linux 4 * 5 * Copyright (C) 2004-2010 Marcel Holtmann <marcel (at) holtmann.org> 6 * 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 21 * 22 */ 23 24 #ifdef HAVE_CONFIG_H 25 #include <config.h> 26 #endif 27 28 #define _GNU_SOURCE 29 #include <stdio.h> 30 #include <errno.h> 31 #include <ctype.h> 32 #include <fcntl.h> 33 #include <unistd.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <sys/file.h> 37 #include <sys/stat.h> 38 #include <sys/mman.h> 39 #include <sys/param.h> 40 41 #include "textfile.h" 42 43 #ifndef HAVE_FDATASYNC 44 #define fdatasync fsync 45 #endif 46 47 int create_dirs(const char *filename, const mode_t mode) 48 { 49 struct stat st; 50 char dir[PATH_MAX + 1], *prev, *next; 51 int err; 52 53 err = stat(filename, &st); 54 if (!err && S_ISREG(st.st_mode)) 55 return 0; 56 57 memset(dir, 0, PATH_MAX + 1); 58 strcat(dir, "/"); 59 60 prev = strchr(filename, '/'); 61 62 while (prev) { 63 next = strchr(prev + 1, '/'); 64 if (!next) 65 break; 66 67 if (next - prev == 1) { 68 prev = next; 69 continue; 70 } 71 72 strncat(dir, prev + 1, next - prev); 73 mkdir(dir, mode); 74 75 prev = next; 76 } 77 78 return 0; 79 } 80 81 int create_file(const char *filename, const mode_t mode) 82 { 83 int fd; 84 85 umask(S_IWGRP | S_IWOTH); 86 create_dirs(filename, S_IRUSR | S_IWUSR | S_IXUSR | 87 S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); 88 89 fd = open(filename, O_RDWR | O_CREAT, mode); 90 if (fd < 0) 91 return fd; 92 93 close(fd); 94 95 return 0; 96 } 97 98 int create_name(char *buf, size_t size, const char *path, const char *address, const char *name) 99 { 100 return snprintf(buf, size, "%s/%s/%s", path, address, name); 101 } 102 103 static inline char *find_key(char *map, size_t size, const char *key, size_t len, int icase) 104 { 105 char *ptr = map; 106 size_t ptrlen = size; 107 108 while (ptrlen > len + 1) { 109 int cmp = (icase) ? strncasecmp(ptr, key, len) : strncmp(ptr, key, len); 110 if (cmp == 0) { 111 if (ptr == map) 112 return ptr; 113 114 if ((*(ptr - 1) == '\r' || *(ptr - 1) == '\n') && 115 *(ptr + len) == ' ') 116 return ptr; 117 } 118 119 if (icase) { 120 char *p1 = memchr(ptr + 1, tolower(*key), ptrlen - 1); 121 char *p2 = memchr(ptr + 1, toupper(*key), ptrlen - 1); 122 123 if (!p1) 124 ptr = p2; 125 else if (!p2) 126 ptr = p1; 127 else 128 ptr = (p1 < p2) ? p1 : p2; 129 } else 130 ptr = memchr(ptr + 1, *key, ptrlen - 1); 131 132 if (!ptr) 133 return NULL; 134 135 ptrlen = size - (ptr - map); 136 } 137 138 return NULL; 139 } 140 141 static inline int write_key_value(int fd, const char *key, const char *value) 142 { 143 char *str; 144 size_t size; 145 int err = 0; 146 147 size = strlen(key) + strlen(value) + 2; 148 149 str = malloc(size + 1); 150 if (!str) 151 return ENOMEM; 152 153 sprintf(str, "%s %s\n", key, value); 154 155 if (write(fd, str, size) < 0) 156 err = errno; 157 158 free(str); 159 160 return err; 161 } 162 163 static char *strnpbrk(const char *s, ssize_t len, const char *accept) 164 { 165 const char *p = s; 166 const char *end; 167 168 end = s + len - 1; 169 170 while (p <= end && *p) { 171 const char *a = accept; 172 173 while (*a) { 174 if (*p == *a) 175 return (char *) p; 176 a++; 177 } 178 179 p++; 180 } 181 182 return NULL; 183 } 184 185 static int write_key(const char *pathname, const char *key, const char *value, int icase) 186 { 187 struct stat st; 188 char *map, *off, *end, *str; 189 off_t size, pos; size_t base; 190 int fd, len, err = 0; 191 192 fd = open(pathname, O_RDWR); 193 if (fd < 0) 194 return -errno; 195 196 if (flock(fd, LOCK_EX) < 0) { 197 err = errno; 198 goto close; 199 } 200 201 if (fstat(fd, &st) < 0) { 202 err = errno; 203 goto unlock; 204 } 205 206 size = st.st_size; 207 208 if (!size) { 209 if (value) { 210 pos = lseek(fd, size, SEEK_SET); 211 err = write_key_value(fd, key, value); 212 } 213 goto unlock; 214 } 215 216 map = mmap(NULL, size, PROT_READ | PROT_WRITE, 217 MAP_PRIVATE | MAP_LOCKED, fd, 0); 218 if (!map || map == MAP_FAILED) { 219 err = errno; 220 goto unlock; 221 } 222 223 len = strlen(key); 224 off = find_key(map, size, key, len, icase); 225 if (!off) { 226 if (value) { 227 munmap(map, size); 228 pos = lseek(fd, size, SEEK_SET); 229 err = write_key_value(fd, key, value); 230 } 231 goto unlock; 232 } 233 234 base = off - map; 235 236 end = strnpbrk(off, size, "\r\n"); 237 if (!end) { 238 err = EILSEQ; 239 goto unmap; 240 } 241 242 if (value && ((ssize_t) strlen(value) == end - off - len - 1) && 243 !strncmp(off + len + 1, value, end - off - len - 1)) 244 goto unmap; 245 246 len = strspn(end, "\r\n"); 247 end += len; 248 249 len = size - (end - map); 250 if (!len) { 251 munmap(map, size); 252 if (ftruncate(fd, base) < 0) { 253 err = errno; 254 goto unlock; 255 } 256 pos = lseek(fd, base, SEEK_SET); 257 if (value) 258 err = write_key_value(fd, key, value); 259 260 goto unlock; 261 } 262 263 if (len < 0 || len > size) { 264 err = EILSEQ; 265 goto unmap; 266 } 267 268 str = malloc(len); 269 if (!str) { 270 err = errno; 271 goto unmap; 272 } 273 274 memcpy(str, end, len); 275 276 munmap(map, size); 277 if (ftruncate(fd, base) < 0) { 278 err = errno; 279 free(str); 280 goto unlock; 281 } 282 pos = lseek(fd, base, SEEK_SET); 283 if (value) 284 err = write_key_value(fd, key, value); 285 286 if (write(fd, str, len) < 0) 287 err = errno; 288 289 free(str); 290 291 goto unlock; 292 293 unmap: 294 munmap(map, size); 295 296 unlock: 297 flock(fd, LOCK_UN); 298 299 close: 300 fdatasync(fd); 301 302 close(fd); 303 errno = err; 304 305 return -err; 306 } 307 308 static char *read_key(const char *pathname, const char *key, int icase) 309 { 310 struct stat st; 311 char *map, *off, *end, *str = NULL; 312 off_t size; size_t len; 313 int fd, err = 0; 314 315 fd = open(pathname, O_RDONLY); 316 if (fd < 0) 317 return NULL; 318 319 if (flock(fd, LOCK_SH) < 0) { 320 err = errno; 321 goto close; 322 } 323 324 if (fstat(fd, &st) < 0) { 325 err = errno; 326 goto unlock; 327 } 328 329 size = st.st_size; 330 331 map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); 332 if (!map || map == MAP_FAILED) { 333 err = errno; 334 goto unlock; 335 } 336 337 len = strlen(key); 338 off = find_key(map, size, key, len, icase); 339 if (!off) { 340 err = EILSEQ; 341 goto unmap; 342 } 343 344 end = strnpbrk(off, size - (map - off), "\r\n"); 345 if (!end) { 346 err = EILSEQ; 347 goto unmap; 348 } 349 350 str = malloc(end - off - len); 351 if (!str) { 352 err = EILSEQ; 353 goto unmap; 354 } 355 356 memset(str, 0, end - off - len); 357 strncpy(str, off + len + 1, end - off - len - 1); 358 359 unmap: 360 munmap(map, size); 361 362 unlock: 363 flock(fd, LOCK_UN); 364 365 close: 366 close(fd); 367 errno = err; 368 369 return str; 370 } 371 372 int textfile_put(const char *pathname, const char *key, const char *value) 373 { 374 return write_key(pathname, key, value, 0); 375 } 376 377 int textfile_caseput(const char *pathname, const char *key, const char *value) 378 { 379 return write_key(pathname, key, value, 1); 380 } 381 382 int textfile_del(const char *pathname, const char *key) 383 { 384 return write_key(pathname, key, NULL, 0); 385 } 386 387 int textfile_casedel(const char *pathname, const char *key) 388 { 389 return write_key(pathname, key, NULL, 1); 390 } 391 392 char *textfile_get(const char *pathname, const char *key) 393 { 394 return read_key(pathname, key, 0); 395 } 396 397 char *textfile_caseget(const char *pathname, const char *key) 398 { 399 return read_key(pathname, key, 1); 400 } 401 402 int textfile_foreach(const char *pathname, 403 void (*func)(char *key, char *value, void *data), void *data) 404 { 405 struct stat st; 406 char *map, *off, *end, *key, *value; 407 off_t size; size_t len; 408 int fd, err = 0; 409 410 fd = open(pathname, O_RDONLY); 411 if (fd < 0) 412 return -errno; 413 414 if (flock(fd, LOCK_SH) < 0) { 415 err = errno; 416 goto close; 417 } 418 419 if (fstat(fd, &st) < 0) { 420 err = errno; 421 goto unlock; 422 } 423 424 size = st.st_size; 425 426 map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); 427 if (!map || map == MAP_FAILED) { 428 err = errno; 429 goto unlock; 430 } 431 432 off = map; 433 434 while (size - (off - map) > 0) { 435 end = strnpbrk(off, size - (off - map), " "); 436 if (!end) { 437 err = EILSEQ; 438 break; 439 } 440 441 len = end - off; 442 443 key = malloc(len + 1); 444 if (!key) { 445 err = errno; 446 break; 447 } 448 449 memset(key, 0, len + 1); 450 memcpy(key, off, len); 451 452 off = end + 1; 453 454 if (size - (off - map) < 0) { 455 err = EILSEQ; 456 free(key); 457 break; 458 } 459 460 end = strnpbrk(off, size - (off - map), "\r\n"); 461 if (!end) { 462 err = EILSEQ; 463 free(key); 464 break; 465 } 466 467 len = end - off; 468 469 value = malloc(len + 1); 470 if (!value) { 471 err = errno; 472 free(key); 473 break; 474 } 475 476 memset(value, 0, len + 1); 477 memcpy(value, off, len); 478 479 func(key, value, data); 480 481 free(key); 482 free(value); 483 484 off = end + 1; 485 } 486 487 munmap(map, size); 488 489 unlock: 490 flock(fd, LOCK_UN); 491 492 close: 493 close(fd); 494 errno = err; 495 496 return 0; 497 } 498