• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2004-2008  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 
create_dirs(const char * filename,const mode_t mode)43 int create_dirs(const char *filename, const mode_t mode)
44 {
45 	struct stat st;
46 	char dir[PATH_MAX + 1], *prev, *next;
47 	int err;
48 
49 	err = stat(filename, &st);
50 	if (!err && S_ISREG(st.st_mode))
51 		return 0;
52 
53 	memset(dir, 0, PATH_MAX + 1);
54 	strcat(dir, "/");
55 
56 	prev = strchr(filename, '/');
57 
58 	while (prev) {
59 		next = strchr(prev + 1, '/');
60 		if (!next)
61 			break;
62 
63 		if (next - prev == 1) {
64 			prev = next;
65 			continue;
66 		}
67 
68 		strncat(dir, prev + 1, next - prev);
69 		mkdir(dir, mode);
70 
71 		prev = next;
72 	}
73 
74 	return 0;
75 }
76 
create_file(const char * filename,const mode_t mode)77 int create_file(const char *filename, const mode_t mode)
78 {
79 	int fd;
80 
81 	umask(S_IWGRP | S_IWOTH);
82 	create_dirs(filename, S_IRUSR | S_IWUSR | S_IXUSR |
83 					S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
84 
85 	fd = open(filename, O_RDWR | O_CREAT, mode);
86 	if (fd < 0)
87 		return fd;
88 
89 	close(fd);
90 
91 	return 0;
92 }
93 
create_name(char * buf,size_t size,const char * path,const char * address,const char * name)94 int create_name(char *buf, size_t size, const char *path, const char *address, const char *name)
95 {
96 	return snprintf(buf, size, "%s/%s/%s", path, address, name);
97 }
98 
find_key(char * map,size_t size,const char * key,size_t len,int icase)99 static inline char *find_key(char *map, size_t size, const char *key, size_t len, int icase)
100 {
101 	char *ptr = map;
102 	size_t ptrlen = size;
103 
104 	while (ptrlen > len + 1) {
105 		int cmp = (icase) ? strncasecmp(ptr, key, len) : strncmp(ptr, key, len);
106 		if (cmp == 0) {
107 			if (ptr == map)
108 				return ptr;
109 
110 			if ((*(ptr - 1) == '\r' || *(ptr - 1) == '\n') &&
111 							*(ptr + len) == ' ')
112 				return ptr;
113 		}
114 
115 		if (icase) {
116 			char *p1 = memchr(ptr + 1, tolower(*key), ptrlen - 1);
117 			char *p2 = memchr(ptr + 1, toupper(*key), ptrlen - 1);
118 
119 			if (!p1)
120 				ptr = p2;
121 			else if (!p2)
122 				ptr = p1;
123 			else
124 				ptr = (p1 < p2) ? p1 : p2;
125 		} else
126 			ptr = memchr(ptr + 1, *key, ptrlen - 1);
127 
128 		if (!ptr)
129 			return NULL;
130 
131 		ptrlen = size - (ptr - map);
132 	}
133 
134 	return NULL;
135 }
136 
write_key_value(int fd,const char * key,const char * value)137 static inline int write_key_value(int fd, const char *key, const char *value)
138 {
139 	char *str;
140 	size_t size;
141 	int err = 0;
142 
143 	size = strlen(key) + strlen(value) + 2;
144 
145 	str = malloc(size + 1);
146 	if (!str)
147 		return ENOMEM;
148 
149 	sprintf(str, "%s %s\n", key, value);
150 
151 	if (write(fd, str, size) < 0)
152 		err = errno;
153 
154 	free(str);
155 
156 	return err;
157 }
158 
write_key(const char * pathname,const char * key,const char * value,int icase)159 static int write_key(const char *pathname, const char *key, const char *value, int icase)
160 {
161 	struct stat st;
162 	char *map, *off, *end, *str;
163 	off_t size, pos; size_t base, len;
164 	int fd, err = 0;
165 
166 	fd = open(pathname, O_RDWR);
167 	if (fd < 0)
168 		return -errno;
169 
170 	if (flock(fd, LOCK_EX) < 0) {
171 		err = errno;
172 		goto close;
173 	}
174 
175 	if (fstat(fd, &st) < 0) {
176 		err = errno;
177 		goto unlock;
178 	}
179 
180 	size = st.st_size;
181 
182 	if (!size) {
183 		if (value) {
184 			pos = lseek(fd, size, SEEK_SET);
185 			err = write_key_value(fd, key, value);
186 		}
187 		goto unlock;
188 	}
189 
190 	map = mmap(NULL, size, PROT_READ | PROT_WRITE,
191 					MAP_PRIVATE | MAP_LOCKED, fd, 0);
192 	if (!map || map == MAP_FAILED) {
193 		err = errno;
194 		goto unlock;
195 	}
196 
197 	len = strlen(key);
198 	off = find_key(map, size, key, len, icase);
199 	if (!off) {
200 		if (value) {
201 			munmap(map, size);
202 			pos = lseek(fd, size, SEEK_SET);
203 			err = write_key_value(fd, key, value);
204 		}
205 		goto unlock;
206 	}
207 
208 	base = off - map;
209 
210 	end = strpbrk(off, "\r\n");
211 	if (!end) {
212 		err = EILSEQ;
213 		goto unmap;
214 	}
215 
216 	if (value && (strlen(value) == end - off - len - 1) &&
217 			!strncmp(off + len + 1, value, end - off - len - 1))
218 		goto unmap;
219 
220 	len = strspn(end, "\r\n");
221 	end += len;
222 
223 	len = size - (end - map);
224 	if (!len) {
225 		munmap(map, size);
226 		if (ftruncate(fd, base) < 0) {
227 			err = errno;
228 			goto unlock;
229 		}
230 		pos = lseek(fd, base, SEEK_SET);
231 		if (value)
232 			err = write_key_value(fd, key, value);
233 
234 		goto unlock;
235 	}
236 
237 	if (len < 0 || len > size) {
238 		err = EILSEQ;
239 		goto unmap;
240 	}
241 
242 	str = malloc(len);
243 	if (!str) {
244 		err = errno;
245 		goto unmap;
246 	}
247 
248 	memcpy(str, end, len);
249 
250 	munmap(map, size);
251 	if (ftruncate(fd, base) < 0) {
252 		err = errno;
253 		free(str);
254 		goto unlock;
255 	}
256 	pos = lseek(fd, base, SEEK_SET);
257 	if (value)
258 		err = write_key_value(fd, key, value);
259 
260 	if (write(fd, str, len) < 0)
261 		err = errno;
262 
263 	free(str);
264 
265 	goto unlock;
266 
267 unmap:
268 	munmap(map, size);
269 
270 unlock:
271 	flock(fd, LOCK_UN);
272 
273 close:
274 	close(fd);
275 	errno = err;
276 
277 	return -err;
278 }
279 
read_key(const char * pathname,const char * key,int icase)280 static char *read_key(const char *pathname, const char *key, int icase)
281 {
282 	struct stat st;
283 	char *map, *off, *end, *str = NULL;
284 	off_t size; size_t len;
285 	int fd, err = 0;
286 
287 	fd = open(pathname, O_RDONLY);
288 	if (fd < 0)
289 		return NULL;
290 
291 	if (flock(fd, LOCK_SH) < 0) {
292 		err = errno;
293 		goto close;
294 	}
295 
296 	if (fstat(fd, &st) < 0) {
297 		err = errno;
298 		goto unlock;
299 	}
300 
301 	size = st.st_size;
302 
303 	map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
304 	if (!map || map == MAP_FAILED) {
305 		err = errno;
306 		goto unlock;
307 	}
308 
309 	len = strlen(key);
310 	off = find_key(map, size, key, len, icase);
311 	if (!off) {
312 		err = EILSEQ;
313 		goto unmap;
314 	}
315 
316 	end = strpbrk(off, "\r\n");
317 	if (!end) {
318 		err = EILSEQ;
319 		goto unmap;
320 	}
321 
322 	str = malloc(end - off - len);
323 	if (!str) {
324 		err = EILSEQ;
325 		goto unmap;
326 	}
327 
328 	memset(str, 0, end - off - len);
329 	strncpy(str, off + len + 1, end - off - len - 1);
330 
331 unmap:
332 	munmap(map, size);
333 
334 unlock:
335 	flock(fd, LOCK_UN);
336 
337 close:
338 	close(fd);
339 	errno = err;
340 
341 	return str;
342 }
343 
textfile_put(const char * pathname,const char * key,const char * value)344 int textfile_put(const char *pathname, const char *key, const char *value)
345 {
346 	return write_key(pathname, key, value, 0);
347 }
348 
textfile_caseput(const char * pathname,const char * key,const char * value)349 int textfile_caseput(const char *pathname, const char *key, const char *value)
350 {
351 	return write_key(pathname, key, value, 1);
352 }
353 
textfile_del(const char * pathname,const char * key)354 int textfile_del(const char *pathname, const char *key)
355 {
356 	return write_key(pathname, key, NULL, 0);
357 }
358 
textfile_casedel(const char * pathname,const char * key)359 int textfile_casedel(const char *pathname, const char *key)
360 {
361 	return write_key(pathname, key, NULL, 1);
362 }
363 
textfile_get(const char * pathname,const char * key)364 char *textfile_get(const char *pathname, const char *key)
365 {
366 	return read_key(pathname, key, 0);
367 }
368 
textfile_caseget(const char * pathname,const char * key)369 char *textfile_caseget(const char *pathname, const char *key)
370 {
371 	return read_key(pathname, key, 1);
372 }
373 
textfile_foreach(const char * pathname,void (* func)(char * key,char * value,void * data),void * data)374 int textfile_foreach(const char *pathname,
375 		void (*func)(char *key, char *value, void *data), void *data)
376 {
377 	struct stat st;
378 	char *map, *off, *end, *key, *value;
379 	off_t size; size_t len;
380 	int fd, err = 0;
381 
382 	fd = open(pathname, O_RDONLY);
383 	if (fd < 0)
384 		return -errno;
385 
386 	if (flock(fd, LOCK_SH) < 0) {
387 		err = errno;
388 		goto close;
389 	}
390 
391 	if (fstat(fd, &st) < 0) {
392 		err = errno;
393 		goto unlock;
394 	}
395 
396 	size = st.st_size;
397 
398 	map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
399 	if (!map || map == MAP_FAILED) {
400 		err = errno;
401 		goto unlock;
402 	}
403 
404 	off = map;
405 
406 	while (1) {
407 		end = strpbrk(off, " ");
408 		if (!end) {
409 			err = EILSEQ;
410 			break;
411 		}
412 
413 		len = end - off;
414 
415 		key = malloc(len + 1);
416 		if (!key) {
417 			err = errno;
418 			break;
419 		}
420 
421 		memset(key, 0, len + 1);
422 		memcpy(key, off, len);
423 
424 		off = end + 1;
425 
426 		end = strpbrk(off, "\r\n");
427 		if (!end) {
428 			err = EILSEQ;
429 			free(key);
430 			break;
431 		}
432 
433 		len = end - off;
434 
435 		value = malloc(len + 1);
436 		if (!value) {
437 			err = errno;
438 			free(key);
439 			break;
440 		}
441 
442 		memset(value, 0, len + 1);
443 		memcpy(value, off, len);
444 
445 		func(key, value, data);
446 
447 		free(key);
448 		free(value);
449 
450 		off = end + 1;
451 	}
452 
453 	munmap(map, size);
454 
455 unlock:
456 	flock(fd, LOCK_UN);
457 
458 close:
459 	close(fd);
460 	errno = err;
461 
462 	return 0;
463 }
464