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