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