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