2 fuse_xattrs - Add xattrs support using sidecar files
4 Copyright (C) 2016 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>
29 #include "passthrough.h"
30 #include "fuse_xattrs_config.h"
32 #include "binary_storage.h"
35 static int xmp_setxattr(const char *path, const char *name, const char *value, size_t size, int flags)
37 if (get_namespace(name) != USER) {
38 debug_print("Only user namespace is supported. name=%s\n", name);
41 if (strlen(name) > XATTR_NAME_MAX) {
42 debug_print("attribute name must be equal or smaller than %d bytes\n", XATTR_NAME_MAX);
45 if (size > XATTR_SIZE_MAX) {
46 debug_print("attribute value cannot be bigger than %d bytes\n", XATTR_SIZE_MAX);
50 char *_path = prepend_source_directory(xattrs_config.source_dir, path);
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);
57 free(sanitized_value);
60 int rtval = binary_storage_write_key(_path, name, value, size, flags);
66 static int xmp_getxattr(const char *path, const char *name, char *value, size_t size)
68 if (get_namespace(name) != USER) {
69 debug_print("Only user namespace is supported. name=%s\n", name);
72 if (strlen(name) > XATTR_NAME_MAX) {
73 debug_print("attribute name must be equal or smaller than %d bytes\n", XATTR_NAME_MAX);
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);
85 static int xmp_listxattr(const char *path, char *list, size_t size)
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");
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);
100 static int xmp_removexattr(const char *path, const char *name)
102 if (get_namespace(name) != USER) {
103 debug_print("Only user namespace is supported. name=%s\n", name);
106 if (strlen(name) > XATTR_NAME_MAX) {
107 debug_print("attribute name must be equal or smaller than %d bytes\n", XATTR_NAME_MAX);
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);
119 static struct fuse_operations xmp_oper = {
120 .getattr = xmp_getattr,
121 .access = xmp_access,
122 .readlink = xmp_readlink,
123 .readdir = xmp_readdir,
126 .symlink = xmp_symlink,
127 .unlink = xmp_unlink,
129 .rename = xmp_rename,
133 .truncate = xmp_truncate,
134 #ifdef HAVE_UTIMENSAT
135 .utimens = xmp_utimens,
140 .statfs = xmp_statfs,
141 .release = xmp_release,
143 #ifdef HAVE_POSIX_FALLOCATE
144 .fallocate = xmp_fallocate,
146 .setxattr = xmp_setxattr,
147 .getxattr = xmp_getxattr,
148 .listxattr = xmp_listxattr,
149 .removexattr = xmp_removexattr,
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),
167 int is_directory(const char *path) {
169 if (stat(path, &statbuf) != 0) {
170 fprintf(stderr, "cannot get source directory status: %s\n", path);
174 if (!S_ISDIR(statbuf.st_mode)) {
175 fprintf(stderr, "source directory must be a directory: %s\n", path);
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
188 const char *sanitized_source_directory(const char *path) {
190 if (strlen(path) == 0) {
194 /* absolute path, we don't do anything */
195 if (path[0] == '/') {
196 if (is_directory(path) == -1) {
199 absolute_path = strdup(path);
200 return absolute_path;
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)
209 absolute_path = (char*) malloc(sizeof(char) * len);
210 memset(absolute_path, '\0', len);
211 sprintf(absolute_path, "%s/%s", pwd, path);
213 if(!has_trailing_backslash)
214 absolute_path[len-2] = '/';
216 if (is_directory(absolute_path) == -1) {
221 return absolute_path;
224 static int xattrs_opt_proc(void *data, const char *arg, int key,
225 struct fuse_args *outargs) {
228 case FUSE_OPT_KEY_NONOPT:
229 if (!xattrs_config.source_dir) {
230 xattrs_config.source_dir = sanitized_source_directory(arg);
237 "usage: %s source_dir mountpoint [options]\n"
240 " -o opt,[opt...] mount options\n"
241 " -h --help print help\n"
242 " -V --version print version\n"
244 "FUSE XATTRS options:\n"
245 "\n", outargs->argv[0]);
247 fuse_opt_add_arg(outargs, "-ho");
248 fuse_main(outargs->argc, outargs->argv, &xmp_oper, NULL);
252 fuse_opt_add_arg(outargs, "--version");
253 fuse_main(outargs->argc, outargs->argv, &xmp_oper, NULL);
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) {
267 if (!xattrs_config.source_dir) {
268 fprintf(stderr, "missing source directory\n");
269 fprintf(stderr, "see `%s -h' for usage\n", argv[0]);
274 return fuse_main(args.argc, args.argv, &xmp_oper, NULL);