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