• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel@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 
create_dirs(const char * filename,const mode_t mode)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 
create_file(const char * filename,const mode_t mode)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 
create_name(char * buf,size_t size,const char * path,const char * address,const char * name)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 
find_key(char * map,size_t size,const char * key,size_t len,int icase)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 
write_key_value(int fd,const char * key,const char * value)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 
strnpbrk(const char * s,ssize_t len,const char * accept)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 
write_key(const char * pathname,const char * key,const char * value,int icase)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 
read_key(const char * pathname,const char * key,int icase)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 
textfile_put(const char * pathname,const char * key,const char * value)372 int textfile_put(const char *pathname, const char *key, const char *value)
373 {
374 	return write_key(pathname, key, value, 0);
375 }
376 
textfile_caseput(const char * pathname,const char * key,const char * value)377 int textfile_caseput(const char *pathname, const char *key, const char *value)
378 {
379 	return write_key(pathname, key, value, 1);
380 }
381 
textfile_del(const char * pathname,const char * key)382 int textfile_del(const char *pathname, const char *key)
383 {
384 	return write_key(pathname, key, NULL, 0);
385 }
386 
textfile_casedel(const char * pathname,const char * key)387 int textfile_casedel(const char *pathname, const char *key)
388 {
389 	return write_key(pathname, key, NULL, 1);
390 }
391 
textfile_get(const char * pathname,const char * key)392 char *textfile_get(const char *pathname, const char *key)
393 {
394 	return read_key(pathname, key, 0);
395 }
396 
textfile_caseget(const char * pathname,const char * key)397 char *textfile_caseget(const char *pathname, const char *key)
398 {
399 	return read_key(pathname, key, 1);
400 }
401 
textfile_foreach(const char * pathname,void (* func)(char * key,char * value,void * data),void * data)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