Fix build break for rpm
[framework/connectivity/bluez.git] / src / textfile.c
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 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
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
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
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
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
159 static char *strnpbrk(const char *s, ssize_t len, const char *accept)
160 {
161         const char *p = s;
162         const char *end;
163
164         end = s + len - 1;
165
166         while (p <= end && *p) {
167                 const char *a = accept;
168
169                 while (*a) {
170                         if (*p == *a)
171                                 return (char *) p;
172                         a++;
173                 }
174
175                 p++;
176         }
177
178         return NULL;
179 }
180
181 static int write_key(const char *pathname, const char *key, const char *value, int icase)
182 {
183         struct stat st;
184         char *map, *off, *end, *str;
185         off_t size;
186         size_t base;
187         int fd, len, err = 0;
188
189         fd = open(pathname, O_RDWR);
190         if (fd < 0)
191                 return -errno;
192
193         if (flock(fd, LOCK_EX) < 0) {
194                 err = -errno;
195                 goto close;
196         }
197
198         if (fstat(fd, &st) < 0) {
199                 err = -errno;
200                 goto unlock;
201         }
202
203         size = st.st_size;
204
205         if (!size) {
206                 if (value) {
207                         lseek(fd, size, SEEK_SET);
208                         err = write_key_value(fd, key, value);
209                 }
210                 goto unlock;
211         }
212
213         map = mmap(NULL, size, PROT_READ | PROT_WRITE,
214                                         MAP_PRIVATE | MAP_LOCKED, fd, 0);
215         if (!map || map == MAP_FAILED) {
216                 err = -errno;
217                 goto unlock;
218         }
219
220         len = strlen(key);
221         off = find_key(map, size, key, len, icase);
222         if (!off) {
223                 munmap(map, size);
224                 if (value) {
225                         lseek(fd, size, SEEK_SET);
226                         err = write_key_value(fd, key, value);
227                 }
228                 goto unlock;
229         }
230
231         base = off - map;
232
233         end = strnpbrk(off, size, "\r\n");
234         if (!end) {
235                 err = -EILSEQ;
236                 goto unmap;
237         }
238
239         if (value && ((ssize_t) strlen(value) == end - off - len - 1) &&
240                         !strncmp(off + len + 1, value, end - off - len - 1))
241                 goto unmap;
242
243         len = strspn(end, "\r\n");
244         end += len;
245
246         len = size - (end - map);
247         if (!len) {
248                 munmap(map, size);
249                 if (ftruncate(fd, base) < 0) {
250                         err = -errno;
251                         goto unlock;
252                 }
253                 lseek(fd, base, SEEK_SET);
254                 if (value)
255                         err = write_key_value(fd, key, value);
256
257                 goto unlock;
258         }
259
260         if (len < 0 || len > size) {
261                 err = -EILSEQ;
262                 goto unmap;
263         }
264
265         str = malloc(len);
266         if (!str) {
267                 err = -errno;
268                 goto unmap;
269         }
270
271         memcpy(str, end, len);
272
273         munmap(map, size);
274         if (ftruncate(fd, base) < 0) {
275                 err = -errno;
276                 free(str);
277                 goto unlock;
278         }
279         lseek(fd, base, SEEK_SET);
280         if (value)
281                 err = write_key_value(fd, key, value);
282
283         if (write(fd, str, len) < 0)
284                 err = -errno;
285
286         free(str);
287
288         goto unlock;
289
290 unmap:
291         munmap(map, size);
292
293 unlock:
294         flock(fd, LOCK_UN);
295
296 close:
297         fdatasync(fd);
298
299         close(fd);
300         errno = -err;
301
302         return err;
303 }
304
305 static char *read_key(const char *pathname, const char *key, int icase)
306 {
307         struct stat st;
308         char *map, *off, *end, *str = NULL;
309         off_t size; size_t len;
310         int fd, err = 0;
311
312         fd = open(pathname, O_RDONLY);
313         if (fd < 0)
314                 return NULL;
315
316         if (flock(fd, LOCK_SH) < 0) {
317                 err = -errno;
318                 goto close;
319         }
320
321         if (fstat(fd, &st) < 0) {
322                 err = -errno;
323                 goto unlock;
324         }
325
326         size = st.st_size;
327
328         map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
329         if (!map || map == MAP_FAILED) {
330                 err = -errno;
331                 goto unlock;
332         }
333
334         len = strlen(key);
335         off = find_key(map, size, key, len, icase);
336         if (!off) {
337                 err = -EILSEQ;
338                 goto unmap;
339         }
340
341         end = strnpbrk(off, size - (map - off), "\r\n");
342         if (!end) {
343                 err = -EILSEQ;
344                 goto unmap;
345         }
346
347         str = malloc(end - off - len);
348         if (!str) {
349                 err = -EILSEQ;
350                 goto unmap;
351         }
352
353         memset(str, 0, end - off - len);
354         strncpy(str, off + len + 1, end - off - len - 1);
355
356 unmap:
357         munmap(map, size);
358
359 unlock:
360         flock(fd, LOCK_UN);
361
362 close:
363         close(fd);
364         errno = -err;
365
366         return str;
367 }
368
369 int textfile_put(const char *pathname, const char *key, const char *value)
370 {
371         return write_key(pathname, key, value, 0);
372 }
373
374 int textfile_caseput(const char *pathname, const char *key, const char *value)
375 {
376         return write_key(pathname, key, value, 1);
377 }
378
379 int textfile_del(const char *pathname, const char *key)
380 {
381         return write_key(pathname, key, NULL, 0);
382 }
383
384 int textfile_casedel(const char *pathname, const char *key)
385 {
386         return write_key(pathname, key, NULL, 1);
387 }
388
389 char *textfile_get(const char *pathname, const char *key)
390 {
391         return read_key(pathname, key, 0);
392 }
393
394 char *textfile_caseget(const char *pathname, const char *key)
395 {
396         return read_key(pathname, key, 1);
397 }
398
399 int textfile_foreach(const char *pathname, textfile_cb func, void *data)
400 {
401         struct stat st;
402         char *map, *off, *end, *key, *value;
403         off_t size; size_t len;
404         int fd, err = 0;
405
406         fd = open(pathname, O_RDONLY);
407         if (fd < 0)
408                 return -errno;
409
410         if (flock(fd, LOCK_SH) < 0) {
411                 err = -errno;
412                 goto close;
413         }
414
415         if (fstat(fd, &st) < 0) {
416                 err = -errno;
417                 goto unlock;
418         }
419
420         size = st.st_size;
421
422         map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
423         if (!map || map == MAP_FAILED) {
424                 err = -errno;
425                 goto unlock;
426         }
427
428         off = map;
429
430         while (size - (off - map) > 0) {
431                 end = strnpbrk(off, size - (off - map), " ");
432                 if (!end) {
433                         err = -EILSEQ;
434                         break;
435                 }
436
437                 len = end - off;
438
439                 key = malloc(len + 1);
440                 if (!key) {
441                         err = -errno;
442                         break;
443                 }
444
445                 memset(key, 0, len + 1);
446                 memcpy(key, off, len);
447
448                 off = end + 1;
449
450                 if (size - (off - map) < 0) {
451                         err = -EILSEQ;
452                         free(key);
453                         break;
454                 }
455
456                 end = strnpbrk(off, size - (off - map), "\r\n");
457                 if (!end) {
458                         err = -EILSEQ;
459                         free(key);
460                         break;
461                 }
462
463                 len = end - off;
464
465                 value = malloc(len + 1);
466                 if (!value) {
467                         err = -errno;
468                         free(key);
469                         break;
470                 }
471
472                 memset(value, 0, len + 1);
473                 memcpy(value, off, len);
474
475                 func(key, value, data);
476
477                 free(key);
478                 free(value);
479
480                 off = end + 1;
481         }
482
483         munmap(map, size);
484
485 unlock:
486         flock(fd, LOCK_UN);
487
488 close:
489         close(fd);
490         errno = -err;
491
492         return 0;
493 }