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