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, 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