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