• 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;
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 
read_key(const char * pathname,const char * key,int icase)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 
textfile_put(const char * pathname,const char * key,const char * value)373 int textfile_put(const char *pathname, const char *key, const char *value)
374 {
375 	return write_key(pathname, key, value, 0);
376 }
377 
textfile_caseput(const char * pathname,const char * key,const char * value)378 int textfile_caseput(const char *pathname, const char *key, const char *value)
379 {
380 	return write_key(pathname, key, value, 1);
381 }
382 
textfile_del(const char * pathname,const char * key)383 int textfile_del(const char *pathname, const char *key)
384 {
385 	return write_key(pathname, key, NULL, 0);
386 }
387 
textfile_casedel(const char * pathname,const char * key)388 int textfile_casedel(const char *pathname, const char *key)
389 {
390 	return write_key(pathname, key, NULL, 1);
391 }
392 
textfile_get(const char * pathname,const char * key)393 char *textfile_get(const char *pathname, const char *key)
394 {
395 	return read_key(pathname, key, 0);
396 }
397 
textfile_caseget(const char * pathname,const char * key)398 char *textfile_caseget(const char *pathname, const char *key)
399 {
400 	return read_key(pathname, key, 1);
401 }
402 
textfile_foreach(const char * pathname,textfile_cb func,void * data)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