2 fuse_xattrs - Add xattrs support using sidecar files
4 Copyright (C) 2016-2017 Felipe Barriga Richards <felipe {at} felipebarriga.cl>
6 Based on passthrough.c (libfuse example)
8 This program can be distributed under the terms of the GNU GPL.
12 #define FUSE_USE_VERSION 30
14 /* For pread()/pwrite()/utimensat() */
15 #define _XOPEN_SOURCE 700
17 /* For get_current_dir_name */
26 #include <sys/xattr.h>
28 #include "fuse_xattrs_config.h"
30 #include "xattrs_config.h"
32 #include "passthrough.h"
34 #include "binary_storage.h"
36 static int xmp_setxattr(const char *path, const char *name, const char *value, size_t size, int flags)
38 if (get_namespace(name) != USER) {
39 debug_print("Only user namespace is supported. name=%s\n", name);
42 if (strlen(name) > XATTR_NAME_MAX) {
43 debug_print("attribute name must be equal or smaller than %d bytes\n", XATTR_NAME_MAX);
46 if (size > XATTR_SIZE_MAX) {
47 debug_print("attribute value cannot be bigger than %d bytes\n", XATTR_SIZE_MAX);
51 char *_path = prepend_source_directory(xattrs_config.source_dir, path);
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);
58 free(sanitized_value);
61 int rtval = binary_storage_write_key(_path, name, value, size, flags);
67 static int xmp_getxattr(const char *path, const char *name, char *value, size_t size)
69 if (get_namespace(name) != USER) {
70 debug_print("Only user namespace is supported. name=%s\n", name);
73 if (strlen(name) > XATTR_NAME_MAX) {
74 debug_print("attribute name must be equal or smaller than %d bytes\n", XATTR_NAME_MAX);
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);
86 static int xmp_listxattr(const char *path, char *list, size_t size)
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");
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);
101 static int xmp_removexattr(const char *path, const char *name)
103 if (get_namespace(name) != USER) {
104 debug_print("Only user namespace is supported. name=%s\n", name);
107 if (strlen(name) > XATTR_NAME_MAX) {
108 debug_print("attribute name must be equal or smaller than %d bytes\n", XATTR_NAME_MAX);
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);
120 static struct fuse_operations xmp_oper = {
121 .getattr = xmp_getattr,
122 .access = xmp_access,
123 .readlink = xmp_readlink,
124 .readdir = xmp_readdir,
127 .symlink = xmp_symlink,
128 .unlink = xmp_unlink,
130 .rename = xmp_rename,
134 .truncate = xmp_truncate,
135 #ifdef HAVE_UTIMENSAT
136 .utimens = xmp_utimens,
141 .statfs = xmp_statfs,
142 .release = xmp_release,
144 #ifdef HAVE_POSIX_FALLOCATE
145 .fallocate = xmp_fallocate,
147 .setxattr = xmp_setxattr,
148 .getxattr = xmp_getxattr,
149 .listxattr = xmp_listxattr,
150 .removexattr = xmp_removexattr,
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),
168 int is_directory(const char *path) {
170 if (stat(path, &statbuf) != 0) {
171 fprintf(stderr, "cannot get source directory status: %s\n", path);
175 if (!S_ISDIR(statbuf.st_mode)) {
176 fprintf(stderr, "source directory must be a directory: %s\n", path);
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
189 const char *sanitized_source_directory(const char *path) {
191 if (strlen(path) == 0) {
195 /* absolute path, we don't do anything */
196 if (path[0] == '/') {
197 if (is_directory(path) == -1) {
200 absolute_path = strdup(path);
201 return absolute_path;
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)
210 absolute_path = (char*) malloc(sizeof(char) * len);
211 memset(absolute_path, '\0', len);
212 sprintf(absolute_path, "%s/%s", pwd, path);
214 if(!has_trailing_backslash)
215 absolute_path[len-2] = '/';
217 if (is_directory(absolute_path) == -1) {
222 return absolute_path;
225 static int xattrs_opt_proc(void *data, const char *arg, int key,
226 struct fuse_args *outargs) {
229 case FUSE_OPT_KEY_NONOPT:
230 if (!xattrs_config.source_dir) {
231 xattrs_config.source_dir = sanitized_source_directory(arg);
238 "usage: %s source_dir mountpoint [options]\n"
241 " -o opt,[opt...] mount options\n"
242 " -h --help print help\n"
243 " -V --version print version\n"
245 "FUSE XATTRS options:\n"
246 "\n", outargs->argv[0]);
248 fuse_opt_add_arg(outargs, "-ho");
249 fuse_main(outargs->argc, outargs->argv, &xmp_oper, NULL);
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);
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) {
269 if (!xattrs_config.source_dir) {
270 fprintf(stderr, "missing source directory\n");
271 fprintf(stderr, "see `%s -h' for usage\n", argv[0]);
276 return fuse_main(args.argc, args.argv, &xmp_oper, NULL);