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