bugfix: binary_storage: check before writing a key with an empty value. Added test.
[rrq/fuse_xattrs.git] / binary_storage.c
1 /*
2   fuse_xattrs - Add xattrs support using sidecar files
3
4   Copyright (C) 2016  Felipe Barriga Richards <felipe {at} felipebarriga.cl>
5
6   This program can be distributed under the terms of the GNU GPL.
7   See the file COPYING.
8 */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <errno.h>
14 #include <assert.h>
15
16 #include "binary_storage.h"
17 #include "utils.h"
18 #include "fuse_xattrs_config.h"
19
20 #include <attr/xattr.h>
21
22 struct on_memory_attr {
23     u_int16_t name_size;
24     size_t value_size;
25
26     char *name;
27     char *value;
28 };
29
30 void __print_on_memory_attr(struct on_memory_attr *attr)
31 {
32 #ifdef DEBUG
33     char *sanitized_value = sanitize_value(attr->value, attr->value_size);
34     debug_print("--------------\n");
35     debug_print("name size: %hu\n", attr->name_size);
36     debug_print("name: '%s'\n", attr->name);
37     debug_print("value size: %zu\n", attr->value_size);
38     debug_print("sanitized_value: '%s'\n", sanitized_value);
39     debug_print("--------------\n");
40     free(sanitized_value);
41 #endif
42 }
43
44 void __free_on_memory_attr(struct on_memory_attr *attr)
45 {
46     if(attr->name != NULL)
47         free(attr->name);
48
49     if(attr->value != NULL)
50         free(attr->value);
51
52     free(attr);
53 }
54
55 char *__read_file(const char *path, int *buffer_size)
56 {
57     FILE *file = fopen(path, "r");
58     char *buffer = NULL;
59
60     if (file == NULL) {
61         debug_print("file not found: %s\n", path);
62         *buffer_size = -ENOENT;
63         return NULL;
64     }
65
66     debug_print("file found, reading it: %s\n", path);
67
68     fseek(file, 0, SEEK_END);
69     *buffer_size = (int)ftell(file);
70
71     if (*buffer_size == -1) {
72         error_print("error: path: %s, size: %d, errno=%d\n", path, *buffer_size, errno);
73         *buffer_size = errno;
74         fclose(file);
75         return NULL;
76     }
77
78     if (*buffer_size > MAX_METADATA_SIZE) {
79         error_print("metadata file too big. path: %s, size: %d\n", path, *buffer_size);
80         *buffer_size = -ENOSPC;
81         fclose(file);
82         return NULL;
83     }
84
85     if (*buffer_size == 0) {
86         debug_print("empty file.\n");
87         *buffer_size = -ENOENT;
88         fclose(file);
89         return NULL;
90     }
91     assert(*buffer_size > 0);
92     size_t _buffer_size = (size_t) *buffer_size;
93
94     fseek(file, 0, SEEK_SET);
95     buffer = malloc(_buffer_size);
96     if (buffer == NULL) {
97         *buffer_size = -ENOMEM;
98         error_print("cannot allocate memory.\n");
99         fclose(file);
100         return NULL;
101     }
102
103     memset(buffer, '\0', _buffer_size);
104     fread(buffer, 1, _buffer_size, file);
105     fclose(file);
106
107     return buffer;
108 }
109
110 char *__read_file_sidecar(const char *path, int *buffer_size)
111 {
112     char *sidecar_path = get_sidecar_path(path);
113     debug_print("path=%s sidecar_path=%s\n", path, sidecar_path);
114
115     char *buffer = __read_file(sidecar_path, buffer_size);
116     free (sidecar_path);
117     return buffer;
118 }
119
120 int __cmp_name(const char *name, size_t name_length, struct on_memory_attr *attr)
121 {
122     if (attr->name_size == name_length && memcmp(attr->name, name, name_length) == 0) {
123         debug_print("match: name=%s, name_length=%zu\n", name, name_length);
124         __print_on_memory_attr(attr);
125         return 1;
126     }
127
128     debug_print("doesn't match: name=%s, name_length=%zu\n", name, name_length);
129     __print_on_memory_attr(attr);
130
131     return 0;
132 }
133
134 struct on_memory_attr *__read_on_memory_attr(size_t *offset, char *buffer, size_t buffer_size)
135 {
136     debug_print("offset=%zu\n", *offset);
137     struct on_memory_attr *attr = malloc(sizeof(struct on_memory_attr));
138     attr->name = NULL;
139     attr->value = NULL;
140
141     ////////////////////////////////
142     // Read name size
143     size_t data_size = sizeof(u_int16_t);
144     if (*offset + data_size > buffer_size) {
145         error_print("Error, sizes doesn't match.\n");
146         __free_on_memory_attr(attr);
147         return NULL;
148     }
149     memcpy(&attr->name_size, buffer + *offset, data_size);
150     *offset += data_size;
151     debug_print("attr->name_size=%hu\n", attr->name_size);
152
153     ////////////////////////////////
154     // Read name data
155     data_size = attr->name_size;
156     if (*offset + data_size > buffer_size) {
157         error_print("Error, sizes doesn't match.\n");
158         __free_on_memory_attr(attr);
159         return NULL;
160     }
161     attr->name = malloc(data_size);
162     memcpy(attr->name, buffer + *offset, data_size);
163     *offset += data_size;
164
165     ////////////////////////////////
166     // Read value size
167     data_size = sizeof(size_t);
168     if (*offset + data_size > buffer_size) {
169         error_print("Error, sizes doesn't match.\n");
170         __free_on_memory_attr(attr);
171         return NULL;
172     }
173     memcpy(&attr->value_size, buffer + *offset, data_size);
174     *offset += data_size;
175     debug_print("attr->value_size=%zu\n", attr->value_size);
176
177     ////////////////////////////////
178     // Read value data
179     data_size = attr->value_size;
180     if (*offset + data_size > buffer_size) {
181         error_print("Error, sizes doesn't match. data_size=%zu buffer_size=%zu\n",
182             data_size, buffer_size);
183
184         __free_on_memory_attr(attr);
185         return NULL;
186     }
187     attr->value = malloc(data_size);
188     memcpy(attr->value, buffer + *offset, data_size);
189     *offset += data_size;
190
191     return attr;
192 }
193
194 int __write_to_file(FILE *file, const char *name, const char *value, const size_t value_size)
195 {
196     const u_int16_t name_size = (int) strlen(name) + 1;
197
198 #ifdef DEBUG
199     char *sanitized_value = sanitize_value(value, value_size);
200     debug_print("name='%s' name_size=%zu sanitized_value='%s' value_size=%zu\n", name, name_size, sanitized_value, value_size);
201     free(sanitized_value);
202 #endif
203
204     // write name
205     if (fwrite(&name_size, sizeof(u_int16_t), 1, file) != 1) {
206         return -1;
207     }
208     if (fwrite(name, name_size, 1, file) != 1) {
209         return -1;
210     }
211
212     // write value
213     if (fwrite(&value_size, sizeof(size_t), 1, file) != 1) {
214         return -1;
215     }
216     // write value content only if we have something to write.
217     if (value_size > 0) {
218         if (fwrite(value, value_size, 1, file) != 1) {
219             return -1;
220         }
221     }
222
223     return 0;
224 }
225
226 /**
227  *
228  * @param path - path to file.
229  * @param name - attribute name. string null terminated. size <= XATTR_NAME_MAX bytes
230  * @param value - attribute value. size < XATTR_SIZE_MAX
231  * @param size
232  * @param flags - XATTR_CREATE and/or XATTR_REPLACE
233  * @return On success, zero is returned.  On failure, -errno is returnted.
234  */
235 int binary_storage_write_key(const char *path, const char *name, const char *value, size_t size, int flags)
236 {
237 #ifdef DEBUG
238     char *sanitized_value = sanitize_value(value, size);
239     debug_print("path=%s name=%s sanitized_value=%s size=%zu flags=%d\n", path, name, sanitized_value, size, flags);
240     free(sanitized_value);
241 #endif
242
243     int buffer_size;
244     char *buffer = __read_file_sidecar(path, &buffer_size);
245
246     if (buffer == NULL && buffer_size == -ENOENT && flags & XATTR_REPLACE) {
247         error_print("No xattr. (flag XATTR_REPLACE)");
248         return -ENODATA;
249     }
250
251     if (buffer == NULL && buffer_size != -ENOENT) {
252         return buffer_size;
253     }
254
255     int status;
256     char *sidecar_path = get_sidecar_path(path);
257     FILE *file = fopen(sidecar_path, "w");
258     free(sidecar_path);
259
260     if (buffer == NULL) {
261         debug_print("new file, writing directly...\n");
262         status = __write_to_file(file, name, value, size);
263         assert(status == 0);
264         fclose(file);
265         free(buffer);
266         return 0;
267     }
268     assert(buffer_size >= 0);
269     size_t _buffer_size = (size_t)buffer_size;
270
271     int res = 0;
272     size_t offset = 0;
273     size_t name_len = strlen(name) + 1; // null byte
274     int replaced = 0;
275     while(offset < _buffer_size)
276     {
277         debug_print("replaced=%d offset=%zu buffer_size=%zu\n", replaced, offset, _buffer_size);
278         struct on_memory_attr *attr = __read_on_memory_attr(&offset, buffer, _buffer_size);
279
280         // FIXME: handle attr == NULL
281         assert(attr != NULL);
282
283         if (memcmp(attr->name, name, name_len) == 0) {
284             assert(replaced == 0);
285             if (flags & XATTR_CREATE) {
286                 error_print("Key already exists. (flag XATTR_CREATE)");
287                 status = __write_to_file(file, attr->name, attr->value, attr->value_size);
288                 assert(status == 0);
289                 res = -EEXIST;
290             } else {
291                 status = __write_to_file(file, name, value, size);
292                 assert(status == 0);
293                 replaced = 1;
294             }
295         } else {
296             status = __write_to_file(file, attr->name, attr->value, attr->value_size);
297             assert(status == 0);
298         }
299         __free_on_memory_attr(attr);
300     }
301
302     if (replaced == 0 && res == 0) {
303         if (flags & XATTR_REPLACE) {
304             error_print("Key doesn't exists. (flag XATTR_REPLACE)");
305             res = -ENODATA;
306         } else {
307             status = __write_to_file(file, name, value, size);
308             assert(status == 0);
309         }
310     }
311
312     fclose(file);
313     free(buffer);
314     return res;
315 }
316
317 int binary_storage_read_key(const char *path, const char *name, char *value, size_t size)
318 {
319     int buffer_size;
320     char *buffer = __read_file_sidecar(path, &buffer_size);
321     
322     if (buffer == NULL) {
323         if (buffer_size == -ENOENT) {
324             return -ENOATTR;
325         }
326         return buffer_size;
327     }
328     assert(buffer_size >= 0);
329     size_t _buffer_size = (size_t)buffer_size;
330
331     if (size > 0) {
332         memset(value, '\0', size);
333     }
334
335     size_t name_len = strlen(name) + 1; // null byte \0
336     size_t offset = 0;
337     while(offset < _buffer_size)
338     {
339         struct on_memory_attr *attr = __read_on_memory_attr(&offset, buffer, _buffer_size);
340         if (attr == NULL) {
341             free(buffer);
342             return -EILSEQ;
343         }
344
345         if (__cmp_name(name, name_len, attr) == 1) {
346             int value_size = (int)attr->value_size;
347             int res;
348             if (size == 0) {
349                 res = value_size;
350             } else if (attr->value_size <= size) {
351                 memcpy(value, attr->value, attr->value_size);
352                 res = value_size;
353             } else {
354                 error_print("error, attr->value_size=%zu > size=%zu\n", attr->value_size, size);
355                 res = -ERANGE;
356             }
357             free(buffer);
358             __free_on_memory_attr(attr);
359             return res;
360         }
361         __free_on_memory_attr(attr);
362     }
363     free(buffer);
364
365     return -ENOATTR;
366 }
367
368 int binary_storage_list_keys(const char *path, char *list, size_t size)
369 {
370     int buffer_size;
371     char *buffer = __read_file_sidecar(path, &buffer_size);
372
373     if (buffer == NULL) {
374         debug_print("buffer == NULL buffer_size=%d\n", buffer_size);
375         if (buffer_size == -ENOENT) {
376             return 0;
377         }
378         return buffer_size;
379     }
380
381     assert(buffer_size > 0);
382     size_t _buffer_size = (size_t)buffer_size;
383
384     if (size > 0) {
385         memset(list, '\0', size);
386     }
387
388     size_t res = 0;
389     size_t offset = 0;
390     while(offset < _buffer_size)
391     {
392         struct on_memory_attr *attr = __read_on_memory_attr(&offset, buffer, _buffer_size);
393         if (attr == NULL) {
394             free(buffer);
395             return -EILSEQ;
396         }
397
398         if (size > 0) {
399             if (attr->name_size + res > size) {
400                 error_print("Not enough memory allocated. allocated=%zu required=%ld\n",
401                             size, attr->name_size + res);
402                 __free_on_memory_attr(attr);
403                 free(buffer);
404                 return -ERANGE;
405             } else {
406                 memcpy(list + res, attr->name, attr->name_size);
407                 res += attr->name_size;
408             }
409         } else {
410             res += attr->name_size;
411         }
412         __free_on_memory_attr(attr);
413     }
414     free(buffer);
415
416     if (size == 0 && res > XATTR_LIST_MAX) {
417         // FIXME: we should return the size or an error ?
418         return -E2BIG;
419     }
420
421     return (int)res;
422 }
423
424 int binary_storage_remove_key(const char *path, const char *name)
425 {
426     debug_print("path=%s name=%s\n", path, name);
427     int buffer_size;
428     char *buffer = __read_file_sidecar(path, &buffer_size);
429
430     if (buffer == NULL) {
431         return buffer_size;
432     }
433     assert(buffer_size > 0);
434     size_t _buffer_size = (size_t) buffer_size;
435
436     char *sidecar_path = get_sidecar_path(path);
437     FILE *file = fopen(sidecar_path, "w");
438     free(sidecar_path);
439
440     size_t offset = 0;
441     size_t name_len = strlen(name) + 1; // null byte \0
442
443     int removed = 0;
444     while(offset < _buffer_size)
445     {
446         debug_print("removed=%d offset=%zu buffer_size=%zu\n", removed, offset, _buffer_size);
447
448         struct on_memory_attr *attr = __read_on_memory_attr(&offset, buffer, _buffer_size);
449         if (attr == NULL) {
450             error_print("error reading file. corrupted ?");
451             break;
452         }
453
454         if (memcmp(attr->name, name, name_len) == 0) {
455             removed++;
456         } else {
457             int status = __write_to_file(file, attr->name, attr->value, attr->value_size);
458             assert(status == 0);
459         }
460         __free_on_memory_attr(attr);
461     }
462
463     int res = 0;
464     if (removed == 1) {
465         debug_print("key removed successfully.\n");
466     } else if (removed == 0) {
467         error_print("key not found.\n");
468         res = -ENOATTR;
469     } else {
470         debug_print("removed %d keys (was duplicated)\n", removed);
471         res = -EILSEQ;
472     }
473
474     fclose(file);
475     free(buffer);
476     return res;
477 }