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