1/*
2 * YAFFS: Yet Another Flash File System. A NAND-flash specific file system.
3 *
4 * Copyright (C) 2002-2011 Aleph One Ltd.
5 *   for Toby Churchill Ltd and Brightstar Engineering
6 *
7 * Created by Charles Manning <charles@aleph1.co.uk>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14/*
15 * This simple implementation of a name-value store assumes a small number of
16* values and fits into a small finite buffer.
17 *
18 * Each attribute is stored as a record:
19 *  sizeof(int) bytes   record size.
20 *  yaffs_strnlen+1 bytes name null terminated.
21 *  nbytes    value.
22 *  ----------
23 *  total size  stored in record size
24 *
25 * This code has not been tested with unicode yet.
26 */
27
28#include "yaffs_nameval.h"
29
30#include "yportenv.h"
31
32static int nval_find(const char *xb, int xb_size, const YCHAR *name,
33		     int *exist_size)
34{
35	int pos = 0;
36	int size;
37
38	memcpy(&size, xb, sizeof(int));
39	while (size > 0 && (size < xb_size) && (pos + size < xb_size)) {
40		if (!yaffs_strncmp((YCHAR *) (xb + pos + sizeof(int)),
41				name, size)) {
42			if (exist_size)
43				*exist_size = size;
44			return pos;
45		}
46		pos += size;
47		if (pos < xb_size - sizeof(int))
48			memcpy(&size, xb + pos, sizeof(int));
49		else
50			size = 0;
51	}
52	if (exist_size)
53		*exist_size = 0;
54	return -ENODATA;
55}
56
57static int nval_used(const char *xb, int xb_size)
58{
59	int pos = 0;
60	int size;
61
62	memcpy(&size, xb + pos, sizeof(int));
63	while (size > 0 && (size < xb_size) && (pos + size < xb_size)) {
64		pos += size;
65		if (pos < xb_size - sizeof(int))
66			memcpy(&size, xb + pos, sizeof(int));
67		else
68			size = 0;
69	}
70	return pos;
71}
72
73int nval_del(char *xb, int xb_size, const YCHAR *name)
74{
75	int pos = nval_find(xb, xb_size, name, NULL);
76	int size;
77
78	if (pos < 0 || pos >= xb_size)
79		return -ENODATA;
80
81	/* Find size, shift rest over this record,
82	 * then zero out the rest of buffer */
83	memcpy(&size, xb + pos, sizeof(int));
84	memcpy(xb + pos, xb + pos + size, xb_size - (pos + size));
85	memset(xb + (xb_size - size), 0, size);
86	return 0;
87}
88
89int nval_set(char *xb, int xb_size, const YCHAR *name, const char *buf,
90		int bsize, int flags)
91{
92	int pos;
93	int namelen = yaffs_strnlen(name, xb_size);
94	int reclen;
95	int size_exist = 0;
96	int space;
97	int start;
98
99	pos = nval_find(xb, xb_size, name, &size_exist);
100
101	if (flags & XATTR_CREATE && pos >= 0)
102		return -EEXIST;
103	if (flags & XATTR_REPLACE && pos < 0)
104		return -ENODATA;
105
106	start = nval_used(xb, xb_size);
107	space = xb_size - start + size_exist;
108
109	reclen = (sizeof(int) + namelen + 1 + bsize);
110
111	if (reclen > space)
112		return -ENOSPC;
113
114	if (pos >= 0) {
115		nval_del(xb, xb_size, name);
116		start = nval_used(xb, xb_size);
117	}
118
119	pos = start;
120
121	memcpy(xb + pos, &reclen, sizeof(int));
122	pos += sizeof(int);
123	yaffs_strncpy((YCHAR *) (xb + pos), name, reclen);
124	pos += (namelen + 1);
125	memcpy(xb + pos, buf, bsize);
126	return 0;
127}
128
129int nval_get(const char *xb, int xb_size, const YCHAR * name, char *buf,
130	     int bsize)
131{
132	int pos = nval_find(xb, xb_size, name, NULL);
133	int size;
134
135	if (pos >= 0 && pos < xb_size) {
136
137		memcpy(&size, xb + pos, sizeof(int));
138		pos += sizeof(int);	/* advance past record length */
139		size -= sizeof(int);
140
141		/* Advance over name string */
142		while (xb[pos] && size > 0 && pos < xb_size) {
143			pos++;
144			size--;
145		}
146		/*Advance over NUL */
147		pos++;
148		size--;
149
150		/* If bsize is zero then this is a size query.
151		 * Return the size, but don't copy.
152		 */
153		if (!bsize)
154			return size;
155
156		if (size <= bsize) {
157			memcpy(buf, xb + pos, size);
158			return size;
159		}
160	}
161	if (pos >= 0)
162		return -ERANGE;
163
164	return -ENODATA;
165}
166
167int nval_list(const char *xb, int xb_size, char *buf, int bsize)
168{
169	int pos = 0;
170	int size;
171	int name_len;
172	int ncopied = 0;
173	int filled = 0;
174
175	memcpy(&size, xb + pos, sizeof(int));
176	while (size > sizeof(int) &&
177		size <= xb_size &&
178		(pos + size) < xb_size &&
179		!filled) {
180		pos += sizeof(int);
181		size -= sizeof(int);
182		name_len = yaffs_strnlen((YCHAR *) (xb + pos), size);
183		if (ncopied + name_len + 1 < bsize) {
184			memcpy(buf, xb + pos, name_len * sizeof(YCHAR));
185			buf += name_len;
186			*buf = '\0';
187			buf++;
188			if (sizeof(YCHAR) > 1) {
189				*buf = '\0';
190				buf++;
191			}
192			ncopied += (name_len + 1);
193		} else {
194			filled = 1;
195		}
196		pos += size;
197		if (pos < xb_size - sizeof(int))
198			memcpy(&size, xb + pos, sizeof(int));
199		else
200			size = 0;
201	}
202	return ncopied;
203}
204
205int nval_hasvalues(const char *xb, int xb_size)
206{
207	return nval_used(xb, xb_size) > 0;
208}
209