2 fuse_xattrs - Add xattrs support using sidecar files
4 Copyright (C) 2016 Felipe Barriga Richards <felipe {at} felipebarriga.cl>
6 This program can be distributed under the terms of the GNU GPL.
17 #include <sys/types.h>
19 #include "binary_storage.h"
21 #include "fuse_xattrs_config.h"
22 #include "stringmem.h"
23 #include "xattrs_config.h"
25 #include <sys/xattr.h>
28 #define ENOATTR ENODATA
31 struct on_memory_attr {
39 void __print_on_memory_attr(struct on_memory_attr *attr)
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);
53 void __free_on_memory_attr(struct on_memory_attr *attr)
55 if(attr->name != NULL)
58 if(attr->value != NULL)
64 char *__read_file(const char *path, int *buffer_size)
66 FILE *file = fopen(path, "r");
70 debug_print("file not found: %s\n", path);
71 *buffer_size = -ENOENT;
75 debug_print("file found, reading it: %s\n", path);
77 fseek(file, 0, SEEK_END);
78 *buffer_size = (int)ftell(file);
80 if (*buffer_size == -1) {
81 error_print("error: path: %s, size: %d, errno=%d\n", path, *buffer_size, errno);
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;
94 if (*buffer_size == 0) {
95 debug_print("empty file.\n");
96 *buffer_size = -ENOENT;
100 assert(*buffer_size > 0);
101 size_t _buffer_size = (size_t) *buffer_size;
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");
112 memset(buffer, '\0', _buffer_size);
113 fread(buffer, 1, _buffer_size, file);
119 char *__read_file_sidecar(const char *path, int *buffer_size)
121 char *sidecar_path = get_sidecar_path(path);
122 debug_print("path=%s sidecar_path=%s\n", path, sidecar_path);
124 char *buffer = __read_file(sidecar_path, buffer_size);
125 strfree (sidecar_path);
129 int __cmp_name(const char *name, size_t name_length, struct on_memory_attr *attr)
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);
137 debug_print("doesn't match: name=%s, name_length=%zu\n", name, name_length);
138 __print_on_memory_attr(attr);
143 struct on_memory_attr *__read_on_memory_attr(size_t *offset, char *buffer, size_t buffer_size)
145 debug_print("offset=%zu\n", *offset);
146 struct on_memory_attr *attr = malloc(sizeof(struct on_memory_attr));
150 ////////////////////////////////
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);
158 memcpy(&attr->name_size, buffer + *offset, data_size);
159 *offset += data_size;
160 debug_print("attr->name_size=%hu\n", attr->name_size);
162 ////////////////////////////////
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);
170 attr->name = malloc(data_size);
171 memcpy(attr->name, buffer + *offset, data_size);
172 *offset += data_size;
174 ////////////////////////////////
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);
182 memcpy(&attr->value_size, buffer + *offset, data_size);
183 *offset += data_size;
184 debug_print("attr->value_size=%zu\n", attr->value_size);
186 ////////////////////////////////
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);
193 __free_on_memory_attr(attr);
196 attr->value = malloc(data_size);
197 memcpy(attr->value, buffer + *offset, data_size);
198 *offset += data_size;
203 int __write_to_file(FILE *file, const char *name, const char *value, const size_t value_size)
205 const u_int16_t name_size = (int) strlen(name) + 1;
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);
214 if (fwrite(&name_size, sizeof(u_int16_t), 1, file) != 1) {
217 if (fwrite(name, name_size, 1, file) != 1) {
222 if (fwrite(&value_size, sizeof(size_t), 1, file) != 1) {
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) {
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 );
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;
252 while ( *e && *e != '/' ) e++;
255 if ( access( p, F_OK ) && mkdir( p, 0777 ) ) {
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
271 * @param flags - XATTR_CREATE and/or XATTR_REPLACE
272 * @return On success, zero is returned. On failure, -errno is returned.
274 int binary_storage_write_key(const char *path, const char *name, const char *value, size_t size, int flags)
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);
283 char *buffer = __read_file_sidecar(path, &buffer_size);
285 if (buffer == NULL && buffer_size == -ENOENT && flags & XATTR_REPLACE) {
286 error_print("No xattr. (flag XATTR_REPLACE)");
290 if (buffer == NULL && buffer_size != -ENOENT) {
295 char *sidecar_path = get_sidecar_path(path);
297 if ( ensure_dirs( sidecar_path ) ) {
301 FILE *file = fopen(sidecar_path, "w");
302 strfree(sidecar_path);
304 if (buffer == NULL) {
305 debug_print("new file, writing directly...\n");
306 status = __write_to_file(file, name, value, size);
312 assert(buffer_size >= 0);
313 size_t _buffer_size = (size_t)buffer_size;
317 size_t name_len = strlen(name) + 1; // null byte
319 while(offset < _buffer_size)
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);
324 // FIXME: handle attr == NULL
325 assert(attr != NULL);
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);
335 status = __write_to_file(file, name, value, size);
340 status = __write_to_file(file, attr->name, attr->value, attr->value_size);
343 __free_on_memory_attr(attr);
346 if (replaced == 0 && res == 0) {
347 if (flags & XATTR_REPLACE) {
348 error_print("Key doesn't exists. (flag XATTR_REPLACE)");
351 status = __write_to_file(file, name, value, size);
361 int binary_storage_read_key(const char *path, const char *name, char *value, size_t size)
364 char *buffer = __read_file_sidecar(path, &buffer_size);
366 if (buffer == NULL) {
367 if (buffer_size == -ENOENT) {
372 assert(buffer_size >= 0);
373 size_t _buffer_size = (size_t)buffer_size;
376 memset(value, '\0', size);
379 size_t name_len = strlen(name) + 1; // null byte \0
381 while(offset < _buffer_size)
383 struct on_memory_attr *attr = __read_on_memory_attr(&offset, buffer, _buffer_size);
389 if (__cmp_name(name, name_len, attr) == 1) {
390 int value_size = (int)attr->value_size;
394 } else if (attr->value_size <= size) {
395 memcpy(value, attr->value, attr->value_size);
398 error_print("error, attr->value_size=%zu > size=%zu\n", attr->value_size, size);
402 __free_on_memory_attr(attr);
405 __free_on_memory_attr(attr);
412 int binary_storage_list_keys(const char *path, char *list, size_t size)
415 char *buffer = __read_file_sidecar(path, &buffer_size);
417 if (buffer == NULL) {
418 debug_print("buffer == NULL buffer_size=%d\n", buffer_size);
419 if (buffer_size == -ENOENT) {
425 assert(buffer_size > 0);
426 size_t _buffer_size = (size_t)buffer_size;
429 memset(list, '\0', size);
434 while(offset < _buffer_size)
436 struct on_memory_attr *attr = __read_on_memory_attr(&offset, buffer, _buffer_size);
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);
450 memcpy(list + res, attr->name, attr->name_size);
451 res += attr->name_size;
454 res += attr->name_size;
456 __free_on_memory_attr(attr);
460 if (size == 0 && res > XATTR_LIST_MAX) {
461 // FIXME: we should return the size or an error ?
468 int binary_storage_remove_key(const char *path, const char *name)
470 debug_print("path=%s name=%s\n", path, name);
472 char *buffer = __read_file_sidecar(path, &buffer_size);
474 if (buffer == NULL) {
477 assert(buffer_size > 0);
478 size_t _buffer_size = (size_t) buffer_size;
480 char *sidecar_path = get_sidecar_path(path);
481 FILE *file = fopen(sidecar_path, "w");
482 strfree(sidecar_path);
485 size_t name_len = strlen(name) + 1; // null byte \0
488 while(offset < _buffer_size)
490 debug_print("removed=%d offset=%zu buffer_size=%zu\n", removed, offset, _buffer_size);
492 struct on_memory_attr *attr = __read_on_memory_attr(&offset, buffer, _buffer_size);
494 error_print("error reading file. corrupted ?");
498 if (memcmp(attr->name, name, name_len) == 0) {
501 int status = __write_to_file(file, attr->name, attr->value, attr->value_size);
504 __free_on_memory_attr(attr);
509 debug_print("key removed successfully.\n");
510 } else if (removed == 0) {
511 error_print("key not found.\n");
514 debug_print("removed %d keys (was duplicated)\n", removed);