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; 190 size_t base; 191 int fd, len, err = 0; 192 193 fd = open(pathname, O_RDWR); 194 if (fd < 0) 195 return -errno; 196 197 if (flock(fd, LOCK_EX) < 0) { 198 err = errno; 199 goto close; 200 } 201 202 if (fstat(fd, &st) < 0) { 203 err = errno; 204 goto unlock; 205 } 206 207 size = st.st_size; 208 209 if (!size) { 210 if (value) { 211 lseek(fd, size, SEEK_SET); 212 err = write_key_value(fd, key, value); 213 } 214 goto unlock; 215 } 216 217 map = mmap(NULL, size, PROT_READ | PROT_WRITE, 218 MAP_PRIVATE | MAP_LOCKED, fd, 0); 219 if (!map || map == MAP_FAILED) { 220 err = errno; 221 goto unlock; 222 } 223 224 len = strlen(key); 225 off = find_key(map, size, key, len, icase); 226 if (!off) { 227 if (value) { 228 munmap(map, size); 229 lseek(fd, size, SEEK_SET); 230 err = write_key_value(fd, key, value); 231 } 232 goto unlock; 233 } 234 235 base = off - map; 236 237 end = strnpbrk(off, size, "\r\n"); 238 if (!end) { 239 err = EILSEQ; 240 goto unmap; 241 } 242 243 if (value && ((ssize_t) strlen(value) == end - off - len - 1) && 244 !strncmp(off + len + 1, value, end - off - len - 1)) 245 goto unmap; 246 247 len = strspn(end, "\r\n"); 248 end += len; 249 250 len = size - (end - map); 251 if (!len) { 252 munmap(map, size); 253 if (ftruncate(fd, base) < 0) { 254 err = errno; 255 goto unlock; 256 } 257 lseek(fd, base, SEEK_SET); 258 if (value) 259 err = write_key_value(fd, key, value); 260 261 goto unlock; 262 } 263 264 if (len < 0 || len > size) { 265 err = EILSEQ; 266 goto unmap; 267 } 268 269 str = malloc(len); 270 if (!str) { 271 err = errno; 272 goto unmap; 273 } 274 275 memcpy(str, end, len); 276 277 munmap(map, size); 278 if (ftruncate(fd, base) < 0) { 279 err = errno; 280 free(str); 281 goto unlock; 282 } 283 lseek(fd, base, SEEK_SET); 284 if (value) 285 err = write_key_value(fd, key, value); 286 287 if (write(fd, str, len) < 0) 288 err = errno; 289 290 free(str); 291 292 goto unlock; 293 294 unmap: 295 munmap(map, size); 296 297 unlock: 298 flock(fd, LOCK_UN); 299 300 close: 301 fdatasync(fd); 302 303 close(fd); 304 errno = err; 305 306 return -err; 307 } 308 309 static char *read_key(const char *pathname, const char *key, int icase) 310 { 311 struct stat st; 312 char *map, *off, *end, *str = NULL; 313 off_t size; size_t len; 314 int fd, err = 0; 315 316 fd = open(pathname, O_RDONLY); 317 if (fd < 0) 318 return NULL; 319 320 if (flock(fd, LOCK_SH) < 0) { 321 err = errno; 322 goto close; 323 } 324 325 if (fstat(fd, &st) < 0) { 326 err = errno; 327 goto unlock; 328 } 329 330 size = st.st_size; 331 332 map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); 333 if (!map || map == MAP_FAILED) { 334 err = errno; 335 goto unlock; 336 } 337 338 len = strlen(key); 339 off = find_key(map, size, key, len, icase); 340 if (!off) { 341 err = EILSEQ; 342 goto unmap; 343 } 344 345 end = strnpbrk(off, size - (map - off), "\r\n"); 346 if (!end) { 347 err = EILSEQ; 348 goto unmap; 349 } 350 351 str = malloc(end - off - len); 352 if (!str) { 353 err = EILSEQ; 354 goto unmap; 355 } 356 357 memset(str, 0, end - off - len); 358 strncpy(str, off + len + 1, end - off - len - 1); 359 360 unmap: 361 munmap(map, size); 362 363 unlock: 364 flock(fd, LOCK_UN); 365 366 close: 367 close(fd); 368 errno = err; 369 370 return str; 371 } 372 373 int textfile_put(const char *pathname, const char *key, const char *value) 374 { 375 return write_key(pathname, key, value, 0); 376 } 377 378 int textfile_caseput(const char *pathname, const char *key, const char *value) 379 { 380 return write_key(pathname, key, value, 1); 381 } 382 383 int textfile_del(const char *pathname, const char *key) 384 { 385 return write_key(pathname, key, NULL, 0); 386 } 387 388 int textfile_casedel(const char *pathname, const char *key) 389 { 390 return write_key(pathname, key, NULL, 1); 391 } 392 393 char *textfile_get(const char *pathname, const char *key) 394 { 395 return read_key(pathname, key, 0); 396 } 397 398 char *textfile_caseget(const char *pathname, const char *key) 399 { 400 return read_key(pathname, key, 1); 401 } 402 403 int textfile_foreach(const char *pathname, textfile_cb func, 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