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