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
47int 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
81int 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
98int 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
103static 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
141static 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
163static 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
271unmap:
272	munmap(map, size);
273
274unlock:
275	flock(fd, LOCK_UN);
276
277close:
278	fdatasync(fd);
279
280	close(fd);
281	errno = err;
282
283	return -err;
284}
285
286static 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
337unmap:
338	munmap(map, size);
339
340unlock:
341	flock(fd, LOCK_UN);
342
343close:
344	close(fd);
345	errno = err;
346
347	return str;
348}
349
350int textfile_put(const char *pathname, const char *key, const char *value)
351{
352	return write_key(pathname, key, value, 0);
353}
354
355int textfile_caseput(const char *pathname, const char *key, const char *value)
356{
357	return write_key(pathname, key, value, 1);
358}
359
360int textfile_del(const char *pathname, const char *key)
361{
362	return write_key(pathname, key, NULL, 0);
363}
364
365int textfile_casedel(const char *pathname, const char *key)
366{
367	return write_key(pathname, key, NULL, 1);
368}
369
370char *textfile_get(const char *pathname, const char *key)
371{
372	return read_key(pathname, key, 0);
373}
374
375char *textfile_caseget(const char *pathname, const char *key)
376{
377	return read_key(pathname, key, 1);
378}
379
380int 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
461unlock:
462	flock(fd, LOCK_UN);
463
464close:
465	close(fd);
466	errno = err;
467
468	return 0;
469}
470