fuse_xattrs.c: Force to run single-threaded. Update ChangeLog / TODO.md
[rrq/fuse_xattrs.git] / fuse_xattrs.c
1 /*
2   fuse_xattrs - Add xattrs support using sidecar files
3
4   Copyright (C) 2016-2017  Felipe Barriga Richards <felipe {at} felipebarriga.cl>
5
6   Based on passthrough.c (libfuse example)
7
8   This program can be distributed under the terms of the GNU GPL.
9   See the file COPYING.
10 */
11
12 #define FUSE_USE_VERSION 30
13
14 /* For pread()/pwrite()/utimensat() */
15 #define _XOPEN_SOURCE 700
16
17 /* For get_current_dir_name */
18 #define _GNU_SOURCE
19
20 #include <errno.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <stddef.h>
25
26 #include <fuse.h>
27 #include <sys/xattr.h>
28
29 #include "fuse_xattrs_config.h"
30
31 #include "xattrs_config.h"
32 #include "utils.h"
33 #include "passthrough.h"
34
35 #include "binary_storage.h"
36
37 static int xmp_setxattr(const char *path, const char *name, const char *value, size_t size, int flags)
38 {
39     if (xattrs_config.show_sidecar == 0 && filename_is_sidecar(path) == 1)  {
40         return -ENOENT;
41     }
42
43     if (get_namespace(name) != USER) {
44         debug_print("Only user namespace is supported. name=%s\n", name);
45         return -ENOTSUP;
46     }
47     if (strlen(name) > XATTR_NAME_MAX) {
48         debug_print("attribute name must be equal or smaller than %d bytes\n", XATTR_NAME_MAX);
49         return -ERANGE;
50     }
51     if (size > XATTR_SIZE_MAX) {
52         debug_print("attribute value cannot be bigger than %d bytes\n", XATTR_SIZE_MAX);
53         return -ENOSPC;
54     }
55
56     char *_path = prepend_source_directory(path);
57
58 #ifdef DEBUG
59     char *sanitized_value = sanitize_value(value, size);
60     debug_print("path=%s name=%s value=%s size=%zu XATTR_CREATE=%d XATTR_REPLACE=%d\n",
61                 _path, name, sanitized_value, size, flags & XATTR_CREATE, flags & XATTR_REPLACE);
62
63     free(sanitized_value);
64 #endif
65
66     int rtval = binary_storage_write_key(_path, name, value, size, flags);
67     free(_path);
68
69     return rtval;
70 }
71
72 static int xmp_getxattr(const char *path, const char *name, char *value, size_t size)
73 {
74     if (xattrs_config.show_sidecar == 0 && filename_is_sidecar(path) == 1)  {
75         return -ENOENT;
76     }
77
78     if (get_namespace(name) != USER) {
79         debug_print("Only user namespace is supported. name=%s\n", name);
80         return -ENOTSUP;
81     }
82     if (strlen(name) > XATTR_NAME_MAX) {
83         debug_print("attribute name must be equal or smaller than %d bytes\n", XATTR_NAME_MAX);
84         return -ERANGE;
85     }
86
87     char *_path = prepend_source_directory(path);
88     debug_print("path=%s name=%s size=%zu\n", _path, name, size);
89     int rtval = binary_storage_read_key(_path, name, value, size);
90     free(_path);
91
92     return rtval;
93 }
94
95 static int xmp_listxattr(const char *path, char *list, size_t size)
96 {
97     if (xattrs_config.show_sidecar == 0 && filename_is_sidecar(path) == 1)  {
98         return -ENOENT;
99     }
100
101     if (size > XATTR_LIST_MAX) {
102         debug_print("The size of the list of attribute names for this file exceeds the system-imposed limit.\n");
103         return -E2BIG;
104     }
105
106     char *_path = prepend_source_directory(path);
107     debug_print("path=%s size=%zu\n", _path, size);
108     int rtval = binary_storage_list_keys(_path, list, size);
109     free(_path);
110
111     return rtval;
112 }
113
114 static int xmp_removexattr(const char *path, const char *name)
115 {
116     if (xattrs_config.show_sidecar == 0 && filename_is_sidecar(path) == 1)  {
117         return -ENOENT;
118     }
119
120     if (get_namespace(name) != USER) {
121         debug_print("Only user namespace is supported. name=%s\n", name);
122         return -ENOTSUP;
123     }
124     if (strlen(name) > XATTR_NAME_MAX) {
125         debug_print("attribute name must be equal or smaller than %d bytes\n", XATTR_NAME_MAX);
126         return -ERANGE;
127     }
128
129     char *_path = prepend_source_directory(path);
130     debug_print("path=%s name=%s\n", _path, name);
131     int rtval = binary_storage_remove_key(_path, name);
132     free(_path);
133
134     return rtval;
135 }
136
137 static struct fuse_operations xmp_oper = {
138         .getattr     = xmp_getattr,
139         .access      = xmp_access,
140         .readlink    = xmp_readlink,
141         .readdir     = xmp_readdir,
142         .mknod       = xmp_mknod,
143         .mkdir       = xmp_mkdir,
144         .symlink     = xmp_symlink,
145         .unlink      = xmp_unlink,
146         .rmdir       = xmp_rmdir,
147         .rename      = xmp_rename,
148         .link        = xmp_link,
149         .chmod       = xmp_chmod,
150         .chown       = xmp_chown,
151         .truncate    = xmp_truncate,
152 #ifdef HAVE_UTIMENSAT
153         .utimens     = xmp_utimens,
154 #endif
155         .open        = xmp_open,
156         .read        = xmp_read,
157         .write       = xmp_write,
158         .statfs      = xmp_statfs,
159         .release     = xmp_release,
160         .fsync       = xmp_fsync,
161 #ifdef HAVE_POSIX_FALLOCATE
162         .fallocate   = xmp_fallocate,
163 #endif
164         .setxattr    = xmp_setxattr,
165         .getxattr    = xmp_getxattr,
166         .listxattr   = xmp_listxattr,
167         .removexattr = xmp_removexattr,
168 };
169
170 /**
171  * Check if the path is valid. If it's a relative path,
172  * prepend the working path.
173  * @param path relative or absolute path to eval.
174  * @return new string with absolute path
175  */
176 const char *sanitized_source_directory(const char *path) {
177     char *absolute_path;
178     if (strlen(path) == 0) {
179         return NULL;
180     }
181
182     /* absolute path, we don't do anything */
183     if (path[0] == '/') {
184         if (is_directory(path) == -1) {
185             return NULL;
186         }
187         absolute_path = strdup(path);
188         return absolute_path;
189     }
190
191     char *pwd = get_current_dir_name();
192     size_t len = strlen(pwd) + 1 + strlen(path) + 1;
193     int has_trailing_backslash = (path[strlen(path)-1] == '/');
194     if (!has_trailing_backslash)
195         len++;
196
197     absolute_path = (char*) malloc(sizeof(char) * len);
198     memset(absolute_path, '\0', len);
199     sprintf(absolute_path, "%s/%s", pwd, path);
200
201     if(!has_trailing_backslash)
202         absolute_path[len-2] = '/';
203
204     if (is_directory(absolute_path) == -1) {
205         free(absolute_path);
206         return NULL;
207     }
208
209     return absolute_path;
210 }
211
212 enum {
213     KEY_HELP,
214     KEY_VERSION,
215 };
216
217 #define FUSE_XATTRS_OPT(t, p, v) { t, offsetof(struct xattrs_config, p), v }
218
219 static struct fuse_opt xattrs_opts[] = {
220         FUSE_XATTRS_OPT("show_sidecar",    show_sidecar, 1),
221
222         FUSE_OPT_KEY("-V",                 KEY_VERSION),
223         FUSE_OPT_KEY("--version",          KEY_VERSION),
224         FUSE_OPT_KEY("-h",                 KEY_HELP),
225         FUSE_OPT_KEY("--help",             KEY_HELP),
226         FUSE_OPT_END
227 };
228
229 static int xattrs_opt_proc(void *data, const char *arg, int key,
230                            struct fuse_args *outargs) {
231     (void) data;
232     switch (key) {
233         case FUSE_OPT_KEY_NONOPT:
234             if (!xattrs_config.source_dir) {
235                 xattrs_config.source_dir = sanitized_source_directory(arg);
236                 xattrs_config.source_dir_size = strlen(xattrs_config.source_dir);
237                 return 0;
238             }
239             break;
240
241         case KEY_HELP:
242             fprintf(stderr,
243                     "usage: %s source_dir mountpoint [options]\n"
244                             "\n"
245                             "general options:\n"
246                             "    -o opt,[opt...]  mount options\n"
247                             "    -h   --help      print help\n"
248                             "    -V   --version   print version\n"
249                             "\n"
250                             "FUSE XATTRS options:\n"
251                             "    -o show_sidecar  don't hide sidecar files\n"
252                             "\n", outargs->argv[0]);
253
254             fuse_opt_add_arg(outargs, "-ho");
255             fuse_main(outargs->argc, outargs->argv, &xmp_oper, NULL);
256             exit(1);
257
258         case KEY_VERSION:
259             printf("FUSE_XATTRS version %d.%d\n", FUSE_XATTRS_VERSION_MAJOR, FUSE_XATTRS_VERSION_MINOR);
260             fuse_opt_add_arg(outargs, "--version");
261             fuse_main(outargs->argc, outargs->argv, &xmp_oper, NULL);
262             exit(0);
263     }
264     return 1;
265 }
266
267
268
269 int main(int argc, char *argv[]) {
270     struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
271     if (fuse_opt_parse(&args, &xattrs_config, xattrs_opts, xattrs_opt_proc) == -1) {
272         exit(1);
273     }
274
275     if (!xattrs_config.source_dir) {
276         fprintf(stderr, "missing source directory\n");
277         fprintf(stderr, "see `%s -h' for usage\n", argv[0]);
278         exit(1);
279     }
280
281     umask(0);
282
283     // disable multi-threading
284     fuse_opt_add_arg(&args, "-s");
285     return fuse_main(args.argc, args.argv, &xmp_oper, NULL);
286 }