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