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