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