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