tests.py: Added more tests.
[rrq/fuse_xattrs.git] / passthrough.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 #include <fuse.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <dirent.h>
23 #include <errno.h>
24
25 #include "xattrs_config.h"
26 #include "utils.h"
27
28 int xmp_getattr(const char *path, struct stat *stbuf) {
29     int res;
30
31     if (xattrs_config.show_sidecar == 0 && filename_is_sidecar(path) == 1)  {
32         return -ENOENT;
33     }
34
35     char *_path = prepend_source_directory(path);
36     res = lstat(_path, stbuf);
37     free(_path);
38
39     if (res == -1)
40         return -errno;
41
42     return 0;
43 }
44
45 int xmp_access(const char *path, int mask) {
46     int res;
47     if (xattrs_config.show_sidecar == 0 && filename_is_sidecar(path) == 1)  {
48         return -ENOENT;
49     }
50
51     char *_path = prepend_source_directory(path);
52     res = access(_path, mask);
53     free(_path);
54
55     if (res == -1)
56         return -errno;
57
58     return 0;
59 }
60
61 int xmp_readlink(const char *path, char *buf, size_t size) {
62     int res;
63     if (xattrs_config.show_sidecar == 0 && filename_is_sidecar(path) == 1)  {
64         return -ENOENT;
65     }
66
67     char *_path = prepend_source_directory(path);
68     res = readlink(_path, buf, size - 1);
69     free(_path);
70
71     if (res == -1)
72         return -errno;
73
74     buf[res] = '\0';
75     return 0;
76 }
77
78 int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
79                 off_t offset, struct fuse_file_info *fi)
80 {
81     DIR *dp;
82     struct dirent *de;
83
84     (void) offset;
85
86     if (fi != NULL && fi->fh != 0) {
87         dp = fdopendir(fi->fh);
88     } else {
89         char *_path = prepend_source_directory(path);
90         dp = opendir(_path);
91         free(_path);
92     }
93
94     if (dp == NULL)
95         return -errno;
96
97     while ((de = readdir(dp)) != NULL) {
98         if (xattrs_config.show_sidecar == 0 && filename_is_sidecar(de->d_name) == 1) {
99             continue;
100         }
101
102         struct stat st;
103         memset(&st, 0, sizeof(st));
104         st.st_ino = de->d_ino;
105         st.st_mode = de->d_type << 12;
106         if (filler(buf, de->d_name, &st, 0))
107             break;
108     }
109
110     closedir(dp);
111     return 0;
112 }
113
114 int xmp_mknod(const char *path, mode_t mode, dev_t rdev) {
115     int res;
116     if (xattrs_config.show_sidecar == 0 && filename_is_sidecar(path) == 1)  {
117         return -ENOENT;
118     }
119
120     char *_path = prepend_source_directory(path);
121
122     /* On Linux this could just be 'mknod(path, mode, rdev)' but this
123        is more portable */
124     if (S_ISREG(mode)) {
125         res = open(_path, O_CREAT | O_EXCL | O_WRONLY, mode);
126         if (res >= 0)
127             res = close(res);
128     } else if (S_ISFIFO(mode))
129         res = mkfifo(_path, mode);
130     else
131         res = mknod(_path, mode, rdev);
132
133     free(_path);
134     if (res == -1)
135         return -errno;
136
137     return 0;
138 }
139
140 int xmp_mkdir(const char *path, mode_t mode) {
141     int res;
142     if (xattrs_config.show_sidecar == 0 && filename_is_sidecar(path) == 1)  {
143         return -ENOENT;
144     }
145
146     char *_path = prepend_source_directory(path);
147     res = mkdir(_path, mode);
148     free(_path);
149
150     if (res == -1)
151         return -errno;
152
153     return 0;
154 }
155
156 int xmp_unlink(const char *path) {
157     int res;
158     if (xattrs_config.show_sidecar == 0 && filename_is_sidecar(path) == 1)  {
159         return -ENOENT;
160     }
161
162     char *_path = prepend_source_directory(path);
163     res = unlink(_path);
164
165     if (res == -1) {
166         free(_path);
167         return -errno;
168     }
169
170     char *sidecar_path = get_sidecar_path(_path);
171     if (is_regular_file(sidecar_path)) {
172         if (unlink(sidecar_path) == -1) {
173             error_print("Error removing sidecar file: %s\n", sidecar_path);
174         }
175     }
176     free(sidecar_path);
177     free(_path);
178
179     return 0;
180 }
181
182 // FIXME: remove sidecar
183 int xmp_rmdir(const char *path) {
184     int res;
185     if (xattrs_config.show_sidecar == 0 && filename_is_sidecar(path) == 1)  {
186         return -ENOENT;
187     }
188
189     char *_path = prepend_source_directory(path);
190     res = rmdir(_path);
191     free(_path);
192
193     if (res == -1)
194         return -errno;
195
196     return 0;
197 }
198
199 int xmp_symlink(const char *from, const char *to) {
200     int res;
201     if (xattrs_config.show_sidecar == 0) {
202         if (filename_is_sidecar(from) == 1 || filename_is_sidecar(to)) {
203             return -ENOENT;
204         }
205     }
206
207     char *_to = prepend_source_directory(to);
208     res = symlink(from, _to);
209     free(_to);
210
211     if (res == -1)
212         return -errno;
213
214     return 0;
215 }
216
217 int xmp_rename(const char *from, const char *to) {
218     int res;
219     if (xattrs_config.show_sidecar == 0) {
220         if (filename_is_sidecar(from) == 1 || filename_is_sidecar(to)) {
221             return -ENOENT;
222         }
223     }
224
225     char *_from = prepend_source_directory(from);
226     char *_to = prepend_source_directory(to);
227     res = rename(_from, _to);
228
229     if (res == -1) {
230         free(_from);
231         free(_to);
232         return -errno;
233     }
234
235     char *from_sidecar_path = get_sidecar_path(_from);
236     char *to_sidecar_path = get_sidecar_path(_to);
237
238     // FIXME: Remove to_sidecar_path if it exists ?
239     if (is_regular_file(from_sidecar_path)) {
240         if (rename(from_sidecar_path, to_sidecar_path) == -1) {
241             error_print("Error renaming sidecar. from: %s to: %s\n", from_sidecar_path, to_sidecar_path);
242         }
243     }
244     free(from_sidecar_path);
245     free(to_sidecar_path);
246
247     free(_from);
248     free(_to);
249
250     return 0;
251 }
252
253 // TODO: handle sidecar file ?
254 int xmp_link(const char *from, const char *to) {
255     int res;
256     if (xattrs_config.show_sidecar == 0) {
257         if (filename_is_sidecar(from) == 1 || filename_is_sidecar(to)) {
258             return -ENOENT;
259         }
260     }
261
262     char *_from = prepend_source_directory(from);
263     char *_to = prepend_source_directory(to);
264     res = link(_from, _to);
265     free(_from);
266     free(_to);
267
268     if (res == -1)
269         return -errno;
270
271     return 0;
272 }
273
274 int xmp_chmod(const char *path, mode_t mode) {
275     int res;
276     if (xattrs_config.show_sidecar == 0 && filename_is_sidecar(path) == 1)  {
277         return -ENOENT;
278     }
279
280     char *_path = prepend_source_directory(path);
281     res = chmod(_path, mode);
282     free(_path);
283
284     if (res == -1)
285         return -errno;
286
287     return 0;
288 }
289
290 int xmp_chown(const char *path, uid_t uid, gid_t gid) {
291     int res;
292     if (xattrs_config.show_sidecar == 0 && filename_is_sidecar(path) == 1)  {
293         return -ENOENT;
294     }
295
296     char *_path = prepend_source_directory(path);
297     res = lchown(_path, uid, gid);
298     free(_path);
299
300     if (res == -1)
301         return -errno;
302
303     return 0;
304 }
305
306 int xmp_truncate(const char *path, off_t size) {
307     int res;
308     if (xattrs_config.show_sidecar == 0 && filename_is_sidecar(path) == 1)  {
309         return -ENOENT;
310     }
311
312     char *_path = prepend_source_directory(path);
313     res = truncate(_path, size);
314     free(_path);
315
316     if (res == -1)
317         return -errno;
318
319     return 0;
320 }
321
322 #ifdef HAVE_UTIMENSAT
323 int xmp_utimens(const char *path, const struct timespec ts[2],
324 struct fuse_file_info *fi)
325 {
326     if (xattrs_config.show_sidecar == 0 && filename_is_sidecar(path) == 1)  {
327         return -ENOENT;
328     }
329
330     (void) fi;
331     int res;
332
333     char *_path = prepend_source_directory(path);
334     /* don't use utime/utimes since they follow symlinks */
335     res = utimensat(0, _path, ts, AT_SYMLINK_NOFOLLOW);
336     free(_path);
337     if (res == -1)
338         return -errno;
339
340     return 0;
341 }
342 #endif
343
344 int xmp_open(const char *path, struct fuse_file_info *fi) {
345     int fd;
346     if (xattrs_config.show_sidecar == 0 && filename_is_sidecar(path) == 1)  {
347         return -ENOENT;
348     }
349
350     char *_path = prepend_source_directory(path);
351     fd = open(_path, fi->flags);
352     free(_path);
353
354     if (fd == -1)
355         return -errno;
356
357     fi->fh = fd;
358     return 0;
359 }
360
361 int xmp_read(const char *path, char *buf, size_t size, off_t offset,
362              struct fuse_file_info *fi)
363 {
364     (void) path;
365     if (fi == NULL || fi->fh == 0) {
366         return -1;
367     }
368
369     int res = pread(fi->fh, buf, size, offset);
370     if (res == -1)
371         res = -errno;
372
373     return res;
374 }
375
376 int xmp_write(const char *path, const char *buf, size_t size,
377               off_t offset, struct fuse_file_info *fi)
378 {
379     (void) path;
380     if (fi == NULL || fi->fh == 0) {
381         return -1;
382     }
383
384     int res = pwrite(fi->fh, buf, size, offset);
385     if (res == -1)
386         res = -errno;
387
388     return res;
389 }
390
391 int xmp_statfs(const char *path, struct statvfs *stbuf) {
392     int res;
393     if (xattrs_config.show_sidecar == 0 && filename_is_sidecar(path) == 1)  {
394         return -ENOENT;
395     }
396
397     char *_path = prepend_source_directory(path);
398     res = statvfs(_path, stbuf);
399     free(_path);
400
401     if (res == -1)
402         return -errno;
403
404     return 0;
405 }
406
407 int xmp_release(const char *path, struct fuse_file_info *fi) {
408     (void) path;
409     return close(fi->fh);
410 }
411
412 int xmp_fsync(const char *path, int isdatasync,
413               struct fuse_file_info *fi) {
414     /* Just a stub.      This method is optional and can safely be left
415        unimplemented */
416
417     (void) path;
418     (void) isdatasync;
419     (void) fi;
420     return 0;
421 }
422
423 #ifdef HAVE_POSIX_FALLOCATE
424 int xmp_fallocate(const char *path, int mode,
425                   off_t offset, off_t length, struct fuse_file_info *fi)
426 {
427     (void) path;
428     if (fi == NULL || fi->fh == 0) {
429         return -1;
430     }
431
432     int res;
433     if (mode)
434         return -EOPNOTSUPP;
435
436     res = -posix_fallocate(fi->fh, offset, length);
437     return res;
438 }
439 #endif
440