• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2004-2009  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 
write_key(const char * pathname,const char * key,const char * value,int icase)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 
read_key(const char * pathname,const char * key,int icase)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 
textfile_put(const char * pathname,const char * key,const char * value)350 int textfile_put(const char *pathname, const char *key, const char *value)
351 {
352 	return write_key(pathname, key, value, 0);
353 }
354 
textfile_caseput(const char * pathname,const char * key,const char * value)355 int textfile_caseput(const char *pathname, const char *key, const char *value)
356 {
357 	return write_key(pathname, key, value, 1);
358 }
359 
textfile_del(const char * pathname,const char * key)360 int textfile_del(const char *pathname, const char *key)
361 {
362 	return write_key(pathname, key, NULL, 0);
363 }
364 
textfile_casedel(const char * pathname,const char * key)365 int textfile_casedel(const char *pathname, const char *key)
366 {
367 	return write_key(pathname, key, NULL, 1);
368 }
369 
textfile_get(const char * pathname,const char * key)370 char *textfile_get(const char *pathname, const char *key)
371 {
372 	return read_key(pathname, key, 0);
373 }
374 
textfile_caseget(const char * pathname,const char * key)375 char *textfile_caseget(const char *pathname, const char *key)
376 {
377 	return read_key(pathname, key, 1);
378 }
379 
textfile_foreach(const char * pathname,void (* func)(char * key,char * value,void * data),void * data)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