Home | History | Annotate | Download | only in common
      1 /*
      2  *
      3  *  BlueZ - Bluetooth protocol stack for Linux
      4  *
      5  *  Copyright (C) 2004-2009  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 int write_key(const char *pathname, const char *key, const char *value, int icase)
    164 {
    165 	struct stat st;
    166 	char *map, *off, *end, *str;
    167 	off_t size, pos; size_t base;
    168 	int fd, len, err = 0;
    169 
    170 	fd = open(pathname, O_RDWR);
    171 	if (fd < 0)
    172 		return -errno;
    173 
    174 	if (flock(fd, LOCK_EX) < 0) {
    175 		err = errno;
    176 		goto close;
    177 	}
    178 
    179 	if (fstat(fd, &st) < 0) {
    180 		err = errno;
    181 		goto unlock;
    182 	}
    183 
    184 	size = st.st_size;
    185 
    186 	if (!size) {
    187 		if (value) {
    188 			pos = lseek(fd, size, SEEK_SET);
    189 			err = write_key_value(fd, key, value);
    190 		}
    191 		goto unlock;
    192 	}
    193 
    194 	map = mmap(NULL, size, PROT_READ | PROT_WRITE,
    195 					MAP_PRIVATE | MAP_LOCKED, fd, 0);
    196 	if (!map || map == MAP_FAILED) {
    197 		err = errno;
    198 		goto unlock;
    199 	}
    200 
    201 	len = strlen(key);
    202 	off = find_key(map, size, key, len, icase);
    203 	if (!off) {
    204 		if (value) {
    205 			munmap(map, size);
    206 			pos = lseek(fd, size, SEEK_SET);
    207 			err = write_key_value(fd, key, value);
    208 		}
    209 		goto unlock;
    210 	}
    211 
    212 	base = off - map;
    213 
    214 	end = strpbrk(off, "\r\n");
    215 	if (!end) {
    216 		err = EILSEQ;
    217 		goto unmap;
    218 	}
    219 
    220 	if (value && ((ssize_t) strlen(value) == end - off - len - 1) &&
    221 			!strncmp(off + len + 1, value, end - off - len - 1))
    222 		goto unmap;
    223 
    224 	len = strspn(end, "\r\n");
    225 	end += len;
    226 
    227 	len = size - (end - map);
    228 	if (!len) {
    229 		munmap(map, size);
    230 		if (ftruncate(fd, base) < 0) {
    231 			err = errno;
    232 			goto unlock;
    233 		}
    234 		pos = lseek(fd, base, SEEK_SET);
    235 		if (value)
    236 			err = write_key_value(fd, key, value);
    237 
    238 		goto unlock;
    239 	}
    240 
    241 	if (len < 0 || len > size) {
    242 		err = EILSEQ;
    243 		goto unmap;
    244 	}
    245 
    246 	str = malloc(len);
    247 	if (!str) {
    248 		err = errno;
    249 		goto unmap;
    250 	}
    251 
    252 	memcpy(str, end, len);
    253 
    254 	munmap(map, size);
    255 	if (ftruncate(fd, base) < 0) {
    256 		err = errno;
    257 		free(str);
    258 		goto unlock;
    259 	}
    260 	pos = lseek(fd, base, SEEK_SET);
    261 	if (value)
    262 		err = write_key_value(fd, key, value);
    263 
    264 	if (write(fd, str, len) < 0)
    265 		err = errno;
    266 
    267 	free(str);
    268 
    269 	goto unlock;
    270 
    271 unmap:
    272 	munmap(map, size);
    273 
    274 unlock:
    275 	flock(fd, LOCK_UN);
    276 
    277 close:
    278 	fdatasync(fd);
    279 
    280 	close(fd);
    281 	errno = err;
    282 
    283 	return -err;
    284 }
    285 
    286 static char *read_key(const char *pathname, const char *key, int icase)
    287 {
    288 	struct stat st;
    289 	char *map, *off, *end, *str = NULL;
    290 	off_t size; size_t len;
    291 	int fd, err = 0;
    292 
    293 	fd = open(pathname, O_RDONLY);
    294 	if (fd < 0)
    295 		return NULL;
    296 
    297 	if (flock(fd, LOCK_SH) < 0) {
    298 		err = errno;
    299 		goto close;
    300 	}
    301 
    302 	if (fstat(fd, &st) < 0) {
    303 		err = errno;
    304 		goto unlock;
    305 	}
    306 
    307 	size = st.st_size;
    308 
    309 	map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
    310 	if (!map || map == MAP_FAILED) {
    311 		err = errno;
    312 		goto unlock;
    313 	}
    314 
    315 	len = strlen(key);
    316 	off = find_key(map, size, key, len, icase);
    317 	if (!off) {
    318 		err = EILSEQ;
    319 		goto unmap;
    320 	}
    321 
    322 	end = strpbrk(off, "\r\n");
    323 	if (!end) {
    324 		err = EILSEQ;
    325 		goto unmap;
    326 	}
    327 
    328 	str = malloc(end - off - len);
    329 	if (!str) {
    330 		err = EILSEQ;
    331 		goto unmap;
    332 	}
    333 
    334 	memset(str, 0, end - off - len);
    335 	strncpy(str, off + len + 1, end - off - len - 1);
    336 
    337 unmap:
    338 	munmap(map, size);
    339 
    340 unlock:
    341 	flock(fd, LOCK_UN);
    342 
    343 close:
    344 	close(fd);
    345 	errno = err;
    346 
    347 	return str;
    348 }
    349 
    350 int textfile_put(const char *pathname, const char *key, const char *value)
    351 {
    352 	return write_key(pathname, key, value, 0);
    353 }
    354 
    355 int textfile_caseput(const char *pathname, const char *key, const char *value)
    356 {
    357 	return write_key(pathname, key, value, 1);
    358 }
    359 
    360 int textfile_del(const char *pathname, const char *key)
    361 {
    362 	return write_key(pathname, key, NULL, 0);
    363 }
    364 
    365 int textfile_casedel(const char *pathname, const char *key)
    366 {
    367 	return write_key(pathname, key, NULL, 1);
    368 }
    369 
    370 char *textfile_get(const char *pathname, const char *key)
    371 {
    372 	return read_key(pathname, key, 0);
    373 }
    374 
    375 char *textfile_caseget(const char *pathname, const char *key)
    376 {
    377 	return read_key(pathname, key, 1);
    378 }
    379 
    380 int textfile_foreach(const char *pathname,
    381 		void (*func)(char *key, char *value, void *data), void *data)
    382 {
    383 	struct stat st;
    384 	char *map, *off, *end, *key, *value;
    385 	off_t size; size_t len;
    386 	int fd, err = 0;
    387 
    388 	fd = open(pathname, O_RDONLY);
    389 	if (fd < 0)
    390 		return -errno;
    391 
    392 	if (flock(fd, LOCK_SH) < 0) {
    393 		err = errno;
    394 		goto close;
    395 	}
    396 
    397 	if (fstat(fd, &st) < 0) {
    398 		err = errno;
    399 		goto unlock;
    400 	}
    401 
    402 	size = st.st_size;
    403 
    404 	map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
    405 	if (!map || map == MAP_FAILED) {
    406 		err = errno;
    407 		goto unlock;
    408 	}
    409 
    410 	off = map;
    411 
    412 	while (1) {
    413 		end = strpbrk(off, " ");
    414 		if (!end) {
    415 			err = EILSEQ;
    416 			break;
    417 		}
    418 
    419 		len = end - off;
    420 
    421 		key = malloc(len + 1);
    422 		if (!key) {
    423 			err = errno;
    424 			break;
    425 		}
    426 
    427 		memset(key, 0, len + 1);
    428 		memcpy(key, off, len);
    429 
    430 		off = end + 1;
    431 
    432 		end = strpbrk(off, "\r\n");
    433 		if (!end) {
    434 			err = EILSEQ;
    435 			free(key);
    436 			break;
    437 		}
    438 
    439 		len = end - off;
    440 
    441 		value = malloc(len + 1);
    442 		if (!value) {
    443 			err = errno;
    444 			free(key);
    445 			break;
    446 		}
    447 
    448 		memset(value, 0, len + 1);
    449 		memcpy(value, off, len);
    450 
    451 		func(key, value, data);
    452 
    453 		free(key);
    454 		free(value);
    455 
    456 		off = end + 1;
    457 	}
    458 
    459 	munmap(map, size);
    460 
    461 unlock:
    462 	flock(fd, LOCK_UN);
    463 
    464 close:
    465 	close(fd);
    466 	errno = err;
    467 
    468 	return 0;
    469 }
    470