Home | History | Annotate | Download | only in src
      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