tests.py: Check xattr module version. Fix to work with version xattr 0.9.1
[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 sanitized_value=%s value_size=%zu\n", name, 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     if (fwrite(value, value_size, 1, file) != 1) {
217         return -1;
218     }
219
220     return 0;
221 }
222
223 /**
224  *
225  * @param path - path to file.
226  * @param name - attribute name. string null terminated. size <= XATTR_NAME_MAX bytes
227  * @param value - attribute value. size < XATTR_SIZE_MAX
228  * @param size
229  * @param flags - XATTR_CREATE and/or XATTR_REPLACE
230  * @return On success, zero is returned.  On failure, -errno is returnted.
231  */
232 int binary_storage_write_key(const char *path, const char *name, const char *value, size_t size, int flags)
233 {
234 #ifdef DEBUG
235     char *sanitized_value = sanitize_value(value, size);
236     debug_print("path=%s name=%s sanitized_value=%s size=%zu flags=%d\n", path, name, sanitized_value, size, flags);
237     free(sanitized_value);
238 #endif
239
240     int buffer_size;
241     char *buffer = __read_file_sidecar(path, &buffer_size);
242
243     if (buffer == NULL && buffer_size == -ENOENT && flags & XATTR_REPLACE) {
244         error_print("No xattr. (flag XATTR_REPLACE)");
245         return -ENODATA;
246     }
247
248     if (buffer == NULL && buffer_size != -ENOENT) {
249         return buffer_size;
250     }
251
252     int status;
253     char *sidecar_path = get_sidecar_path(path);
254     FILE *file = fopen(sidecar_path, "w");
255     free(sidecar_path);
256
257     if (buffer == NULL) {
258         debug_print("new file, writing directly...\n");
259         status = __write_to_file(file, name, value, size);
260         assert(status == 0);
261         fclose(file);
262         free(buffer);
263         return 0;
264     }
265     assert(buffer_size >= 0);
266     size_t _buffer_size = (size_t)buffer_size;
267
268     int res = 0;
269     size_t offset = 0;
270     size_t name_len = strlen(name) + 1; // null byte
271     int replaced = 0;
272     while(offset < _buffer_size)
273     {
274         debug_print("replaced=%d offset=%zu buffer_size=%zu\n", replaced, offset, _buffer_size);
275         struct on_memory_attr *attr = __read_on_memory_attr(&offset, buffer, _buffer_size);
276
277         // FIXME: handle attr == NULL
278         assert(attr != NULL);
279
280         if (memcmp(attr->name, name, name_len) == 0) {
281             assert(replaced == 0);
282             if (flags & XATTR_CREATE) {
283                 error_print("Key already exists. (flag XATTR_CREATE)");
284                 status = __write_to_file(file, attr->name, attr->value, attr->value_size);
285                 assert(status == 0);
286                 res = -EEXIST;
287             } else {
288                 status = __write_to_file(file, name, value, size);
289                 assert(status == 0);
290                 replaced = 1;
291             }
292         } else {
293             status = __write_to_file(file, attr->name, attr->value, attr->value_size);
294             assert(status == 0);
295         }
296         __free_on_memory_attr(attr);
297     }
298
299     if (replaced == 0 && res == 0) {
300         if (flags & XATTR_REPLACE) {
301             error_print("Key doesn't exists. (flag XATTR_REPLACE)");
302             res = -ENODATA;
303         } else {
304             status = __write_to_file(file, name, value, size);
305             assert(status == 0);
306         }
307     }
308
309     fclose(file);
310     free(buffer);
311     return res;
312 }
313
314 int binary_storage_read_key(const char *path, const char *name, char *value, size_t size)
315 {
316     int buffer_size;
317     char *buffer = __read_file_sidecar(path, &buffer_size);
318     
319     if (buffer == NULL) {
320         if (buffer_size == -ENOENT) {
321             return -ENOATTR;
322         }
323         return buffer_size;
324     }
325     assert(buffer_size >= 0);
326     size_t _buffer_size = (size_t)buffer_size;
327
328     if (size > 0) {
329         memset(value, '\0', size);
330     }
331
332     size_t name_len = strlen(name) + 1; // null byte \0
333     size_t offset = 0;
334     while(offset < _buffer_size)
335     {
336         struct on_memory_attr *attr = __read_on_memory_attr(&offset, buffer, _buffer_size);
337         if (attr == NULL) {
338             free(buffer);
339             return -EILSEQ;
340         }
341
342         if (__cmp_name(name, name_len, attr) == 1) {
343             int value_size = (int)attr->value_size;
344             int res;
345             if (size == 0) {
346                 res = value_size;
347             } else if (attr->value_size <= size) {
348                 memcpy(value, attr->value, attr->value_size);
349                 res = value_size;
350             } else {
351                 error_print("error, attr->value_size=%zu > size=%zu\n", attr->value_size, size);
352                 res = -ERANGE;
353             }
354             free(buffer);
355             __free_on_memory_attr(attr);
356             return res;
357         }
358         __free_on_memory_attr(attr);
359     }
360     free(buffer);
361
362     return -ENOATTR;
363 }
364
365 int binary_storage_list_keys(const char *path, char *list, size_t size)
366 {
367     int buffer_size;
368     char *buffer = __read_file_sidecar(path, &buffer_size);
369
370     if (buffer == NULL) {
371         debug_print("buffer == NULL buffer_size=%d\n", buffer_size);
372         if (buffer_size == -ENOENT) {
373             return 0;
374         }
375         return buffer_size;
376     }
377
378     assert(buffer_size > 0);
379     size_t _buffer_size = (size_t)buffer_size;
380
381     if (size > 0) {
382         memset(list, '\0', size);
383     }
384
385     size_t res = 0;
386     size_t offset = 0;
387     while(offset < _buffer_size)
388     {
389         struct on_memory_attr *attr = __read_on_memory_attr(&offset, buffer, _buffer_size);
390         if (attr == NULL) {
391             free(buffer);
392             return -EILSEQ;
393         }
394
395         if (size > 0) {
396             if (attr->name_size + res > size) {
397                 error_print("Not enough memory allocated. allocated=%zu required=%ld\n",
398                             size, attr->name_size + res);
399                 __free_on_memory_attr(attr);
400                 free(buffer);
401                 return -ERANGE;
402             } else {
403                 memcpy(list + res, attr->name, attr->name_size);
404                 res += attr->name_size;
405             }
406         } else {
407             res += attr->name_size;
408         }
409         __free_on_memory_attr(attr);
410     }
411     free(buffer);
412
413     if (size == 0 && res > XATTR_LIST_MAX) {
414         // FIXME: we should return the size or an error ?
415         return -E2BIG;
416     }
417
418     return (int)res;
419 }
420
421 int binary_storage_remove_key(const char *path, const char *name)
422 {
423     debug_print("path=%s name=%s\n", path, name);
424     int buffer_size;
425     char *buffer = __read_file_sidecar(path, &buffer_size);
426
427     if (buffer == NULL) {
428         return buffer_size;
429     }
430     assert(buffer_size > 0);
431     size_t _buffer_size = (size_t) buffer_size;
432
433     char *sidecar_path = get_sidecar_path(path);
434     FILE *file = fopen(sidecar_path, "w");
435     free(sidecar_path);
436
437     size_t offset = 0;
438     size_t name_len = strlen(name) + 1; // null byte \0
439
440     int removed = 0;
441     while(offset < _buffer_size)
442     {
443         debug_print("removed=%d offset=%zu buffer_size=%zu\n", removed, offset, _buffer_size);
444
445         struct on_memory_attr *attr = __read_on_memory_attr(&offset, buffer, _buffer_size);
446         if (attr == NULL) {
447             error_print("error reading file. corrupted ?");
448             break;
449         }
450
451         if (memcmp(attr->name, name, name_len) == 0) {
452             removed++;
453         } else {
454             int status = __write_to_file(file, attr->name, attr->value, attr->value_size);
455             assert(status == 0);
456         }
457         __free_on_memory_attr(attr);
458     }
459
460     int res = 0;
461     if (removed == 1) {
462         debug_print("key removed successfully.\n");
463     } else if (removed == 0) {
464         error_print("key not found.\n");
465         res = -ENOATTR;
466     } else {
467         debug_print("removed %d keys (was duplicated)\n", removed);
468         res = -EILSEQ;
469     }
470
471     fclose(file);
472     free(buffer);
473     return res;
474 }