1/* $NetBSD: fs.c,v 1.2 2023/07/14 02:43:50 pho Exp $ */
2
3/*
4 * Copyright (c) 2021 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote
16 *    products derived from this software without specific prior written
17 *    permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
20 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
25 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33#if !defined(lint)
34__RCSID("$NetBSD: fs.c,v 1.2 2023/07/14 02:43:50 pho Exp $");
35#endif /* !lint */
36
37/*
38 * Filesystem Stacking API, appeared on FUSE 2.7.
39 *
40 * So many callback functions in struct fuse_operations have different
41 * prototypes between versions. We use the stacking API to abstract
42 * that away to implement puffs operations in a manageable way.
43 */
44
45#include <err.h>
46#include <fuse_internal.h>
47#include <stdlib.h>
48#include <string.h>
49#include <sys/dirent.h>
50#include <sys/errno.h>
51
52struct fuse_fs {
53    void* op;
54    int   op_version;
55    void* user_data;
56};
57
58#define UNKNOWN_VERSION(op_version)                                     \
59    errc(EXIT_FAILURE, ENOSYS, "%s: unknown fuse_operations version: %d", \
60         __func__, op_version)
61
62static void*
63clone_op(const void* op, int op_version) {
64    void* cloned;
65
66    switch (op_version) {
67#define CLONE_OP(VER)                                                   \
68    case VER:                                                           \
69        cloned = malloc(sizeof(struct __CONCAT(fuse_operations_v,VER)));      \
70        if (!cloned)                                                    \
71            return NULL;                                                \
72        memcpy(cloned, op, sizeof(struct __CONCAT(fuse_operations_v,VER)));   \
73        return cloned
74
75        CLONE_OP(11);
76        CLONE_OP(21);
77        CLONE_OP(22);
78        CLONE_OP(23);
79        CLONE_OP(25);
80        CLONE_OP(26);
81        CLONE_OP(28);
82        CLONE_OP(29);
83        CLONE_OP(30);
84        CLONE_OP(34);
85        CLONE_OP(35);
86        CLONE_OP(38);
87#undef CLONE_OP
88    default:
89        UNKNOWN_VERSION(op_version);
90    }
91}
92
93struct fuse_fs*
94__fuse_fs_new(const void* op, int op_version, void* user_data) {
95    struct fuse_fs* fs;
96
97    fs = malloc(sizeof(struct fuse_fs));
98    if (!fs)
99        err(EXIT_FAILURE, __func__);
100
101    /* Callers aren't obliged to keep "op" valid during the lifetime
102     * of struct fuse_fs*. We must clone it now, even though it's
103     * non-trivial. */
104    fs->op = clone_op(op, op_version);
105    if (!fs->op)
106        err(EXIT_FAILURE, __func__);
107
108    fs->op_version = op_version;
109    fs->user_data  = user_data;
110
111    return fs;
112}
113
114/* Clobber the context private_data with that of this filesystem
115 * layer. This function needs to be called before invoking any of
116 * operation callbacks. */
117static void
118clobber_context_user_data(struct fuse_fs* fs) {
119    fuse_get_context()->private_data = fs->user_data;
120}
121
122/* Ugly... These are like hand-written vtables... */
123int
124fuse_fs_getattr_v27(struct fuse_fs *fs, const char *path, struct stat *buf) {
125    return fuse_fs_getattr_v30(fs, path, buf, NULL);
126}
127
128int
129fuse_fs_getattr_v30(struct fuse_fs* fs, const char* path,
130                    struct stat* buf, struct fuse_file_info* fi) {
131    clobber_context_user_data(fs);
132    switch (fs->op_version) {
133#define CALL_OLD_GETATTR(VER)                                           \
134    case VER:                                                           \
135        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->getattr) \
136            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->getattr(path, buf); \
137        else                                                            \
138            return -ENOSYS
139        CALL_OLD_GETATTR(11);
140        CALL_OLD_GETATTR(21);
141        CALL_OLD_GETATTR(22);
142        CALL_OLD_GETATTR(23);
143        CALL_OLD_GETATTR(25);
144        CALL_OLD_GETATTR(26);
145        CALL_OLD_GETATTR(28);
146        CALL_OLD_GETATTR(29);
147#undef CALL_OLD_GETATTR
148
149#define CALL_GETATTR(VER)                                               \
150    case VER:                                                           \
151        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->getattr) \
152            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->getattr(path, buf, fi); \
153        else                                                            \
154            return -ENOSYS
155        CALL_GETATTR(30);
156        CALL_GETATTR(34);
157        CALL_GETATTR(35);
158        CALL_GETATTR(38);
159#undef CALL_GETATTR
160    default:
161        UNKNOWN_VERSION(fs->op_version);
162    }
163}
164
165int
166fuse_fs_fgetattr(struct fuse_fs* fs, const char* path, struct stat* buf,
167                 struct fuse_file_info* fi) {
168    clobber_context_user_data(fs);
169    /* fgetattr() was introduced on FUSE 2.5 then disappeared on FUSE
170     * 3.0. Fall back to getattr() if it's missing. */
171    switch (fs->op_version) {
172    case 11:
173    case 21:
174    case 22:
175    case 23:
176        return fuse_fs_getattr_v30(fs, path, buf, fi);
177
178#define CALL_FGETATTR_OR_OLD_GETATTR(VER)       \
179    case VER:                                   \
180        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->fgetattr) \
181            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->fgetattr(path, buf, fi); \
182        else if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->getattr) \
183            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->getattr(path, buf); \
184        else                                                            \
185            return -ENOSYS
186        CALL_FGETATTR_OR_OLD_GETATTR(25);
187        CALL_FGETATTR_OR_OLD_GETATTR(26);
188        CALL_FGETATTR_OR_OLD_GETATTR(28);
189        CALL_FGETATTR_OR_OLD_GETATTR(29);
190#undef CALL_FGETATTR_OR_OLD_GETATTR
191
192    case 30:
193    case 34:
194    case 35:
195    case 38:
196        return fuse_fs_getattr_v30(fs, path, buf, fi);
197    default:
198        UNKNOWN_VERSION(fs->op_version);
199    }
200}
201
202int
203fuse_fs_rename_v27(struct fuse_fs* fs, const char* oldpath, const char* newpath) {
204    return fuse_fs_rename_v30(fs, oldpath, newpath, 0);
205}
206
207int
208fuse_fs_rename_v30(struct fuse_fs* fs, const char* oldpath,
209                   const char* newpath, unsigned int flags) {
210    clobber_context_user_data(fs);
211    switch (fs->op_version) {
212#define CALL_OLD_RENAME(VER)                                            \
213    case VER:                                                           \
214        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->rename) \
215            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->rename(oldpath, newpath); \
216        else                                                            \
217            return -ENOSYS
218        CALL_OLD_RENAME(11);
219        CALL_OLD_RENAME(21);
220        CALL_OLD_RENAME(22);
221        CALL_OLD_RENAME(23);
222        CALL_OLD_RENAME(25);
223        CALL_OLD_RENAME(26);
224        CALL_OLD_RENAME(28);
225        CALL_OLD_RENAME(29);
226#undef CALL_OLD_RENAME
227
228#define CALL_RENAME(VER)                                                \
229    case VER:                                                           \
230        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->rename) \
231            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->rename(oldpath, newpath, flags); \
232        else                                                            \
233            return -ENOSYS
234        CALL_RENAME(30);
235        CALL_RENAME(34);
236        CALL_RENAME(35);
237        CALL_RENAME(38);
238#undef CALL_RENAME
239    default:
240        UNKNOWN_VERSION(fs->op_version);
241    }
242}
243
244int
245fuse_fs_unlink(struct fuse_fs* fs, const char* path) {
246    clobber_context_user_data(fs);
247    switch (fs->op_version) {
248#define CALL_UNLINK(VER)                                                \
249    case VER:                                                           \
250        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->unlink) \
251            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->unlink(path); \
252        else                                                            \
253            return -ENOSYS
254        CALL_UNLINK(11);
255        CALL_UNLINK(21);
256        CALL_UNLINK(22);
257        CALL_UNLINK(23);
258        CALL_UNLINK(25);
259        CALL_UNLINK(26);
260        CALL_UNLINK(28);
261        CALL_UNLINK(29);
262        CALL_UNLINK(30);
263        CALL_UNLINK(34);
264        CALL_UNLINK(35);
265        CALL_UNLINK(38);
266#undef CALL_UNLINK
267    default:
268        UNKNOWN_VERSION(fs->op_version);
269    }
270}
271
272int
273fuse_fs_rmdir(struct fuse_fs* fs, const char* path) {
274    clobber_context_user_data(fs);
275    switch (fs->op_version) {
276#define CALL_RMDIR(VER)                                                 \
277    case VER:                                                           \
278        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->rmdir) \
279            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->rmdir(path); \
280        else                                                            \
281            return -ENOSYS
282        CALL_RMDIR(11);
283        CALL_RMDIR(21);
284        CALL_RMDIR(22);
285        CALL_RMDIR(23);
286        CALL_RMDIR(25);
287        CALL_RMDIR(26);
288        CALL_RMDIR(28);
289        CALL_RMDIR(29);
290        CALL_RMDIR(30);
291        CALL_RMDIR(34);
292        CALL_RMDIR(35);
293        CALL_RMDIR(38);
294#undef CALL_RMDIR
295    default:
296        UNKNOWN_VERSION(fs->op_version);
297    }
298}
299
300int
301fuse_fs_symlink(struct fuse_fs* fs, const char* linkname, const char* path) {
302    clobber_context_user_data(fs);
303    switch (fs->op_version) {
304#define CALL_SYMLINK(VER)                                               \
305    case VER:                                                           \
306        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->symlink) \
307            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->symlink(linkname, path); \
308        else                                                            \
309            return -ENOSYS
310        CALL_SYMLINK(11);
311        CALL_SYMLINK(21);
312        CALL_SYMLINK(22);
313        CALL_SYMLINK(23);
314        CALL_SYMLINK(25);
315        CALL_SYMLINK(26);
316        CALL_SYMLINK(28);
317        CALL_SYMLINK(29);
318        CALL_SYMLINK(30);
319        CALL_SYMLINK(34);
320        CALL_SYMLINK(35);
321        CALL_SYMLINK(38);
322#undef CALL_SYMLINK
323    default:
324        UNKNOWN_VERSION(fs->op_version);
325    }
326}
327
328int
329fuse_fs_link(struct fuse_fs* fs, const char* oldpath, const char* newpath) {
330    clobber_context_user_data(fs);
331    switch (fs->op_version) {
332#define CALL_LINK(VER)                                               \
333    case VER:                                                           \
334        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->link) \
335            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->link(oldpath, newpath); \
336        else                                                            \
337            return -ENOSYS
338        CALL_LINK(11);
339        CALL_LINK(21);
340        CALL_LINK(22);
341        CALL_LINK(23);
342        CALL_LINK(25);
343        CALL_LINK(26);
344        CALL_LINK(28);
345        CALL_LINK(29);
346        CALL_LINK(30);
347        CALL_LINK(34);
348        CALL_LINK(35);
349        CALL_LINK(38);
350#undef CALL_LINK
351    default:
352        UNKNOWN_VERSION(fs->op_version);
353    }
354}
355
356int
357fuse_fs_release(struct fuse_fs* fs, const char* path, struct fuse_file_info* fi) {
358    clobber_context_user_data(fs);
359    switch (fs->op_version) {
360#define CALL_OLD_RELEASE(VER)                                           \
361    case VER:                                                           \
362        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->release) \
363            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->release(path, fi->flags); \
364        else                                                            \
365            return 0 /* Special case */
366        CALL_OLD_RELEASE(11);
367        CALL_OLD_RELEASE(21);
368#undef CALL_OLD_RELEASE
369
370#define CALL_RELEASE(VER)                                               \
371    case VER:                                                           \
372        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->release) \
373            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->release(path, fi); \
374        else                                                            \
375            return 0 /* Special case */
376        CALL_RELEASE(22);
377        CALL_RELEASE(23);
378        CALL_RELEASE(25);
379        CALL_RELEASE(26);
380        CALL_RELEASE(28);
381        CALL_RELEASE(29);
382        CALL_RELEASE(30);
383        CALL_RELEASE(34);
384        CALL_RELEASE(35);
385        CALL_RELEASE(38);
386#undef CALL_RELEASE
387    default:
388        UNKNOWN_VERSION(fs->op_version);
389    }
390}
391
392int
393fuse_fs_open(struct fuse_fs* fs, const char* path, struct fuse_file_info* fi) {
394    clobber_context_user_data(fs);
395    switch (fs->op_version) {
396#define CALL_OLD_OPEN(VER)                                              \
397    case VER:                                                           \
398        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->open) \
399            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->open(path, fi->flags); \
400        else                                                            \
401            return 0 /* Special case */
402        CALL_OLD_OPEN(11);
403        CALL_OLD_OPEN(21);
404#undef CALL_OLD_OPEN
405
406#define CALL_OPEN(VER)                                                  \
407    case VER:                                                           \
408        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->open) \
409            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->open(path, fi); \
410        else                                                            \
411            return 0 /* Special case */
412        CALL_OPEN(22);
413        CALL_OPEN(23);
414        CALL_OPEN(25);
415        CALL_OPEN(26);
416        CALL_OPEN(28);
417        CALL_OPEN(29);
418        CALL_OPEN(30);
419        CALL_OPEN(34);
420        CALL_OPEN(35);
421        CALL_OPEN(38);
422#undef CALL_OPEN
423    default:
424        UNKNOWN_VERSION(fs->op_version);
425    }
426}
427
428int
429fuse_fs_read(struct fuse_fs* fs, const char* path, char* buf,
430             size_t size, off_t off, struct fuse_file_info* fi) {
431    clobber_context_user_data(fs);
432    switch (fs->op_version) {
433#define CALL_OLD_READ(VER)                                              \
434    case VER:                                                           \
435        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->read) \
436            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->read(path, buf, size, off); \
437        else                                                            \
438            return -ENOSYS
439        CALL_OLD_READ(11);
440        CALL_OLD_READ(21);
441#undef CALL_OLD_READ
442
443#define CALL_READ(VER)                                                  \
444    case VER:                                                           \
445        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->read) \
446            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->read(path, buf, size, off, fi); \
447        else                                                            \
448            return -ENOSYS
449        CALL_READ(22);
450        CALL_READ(23);
451        CALL_READ(25);
452        CALL_READ(26);
453        CALL_READ(28);
454        CALL_READ(29);
455        CALL_READ(30);
456        CALL_READ(34);
457        CALL_READ(35);
458        CALL_READ(38);
459#undef CALL_READ
460    default:
461        UNKNOWN_VERSION(fs->op_version);
462    }
463}
464
465int
466fuse_fs_read_buf(struct fuse_fs* fs, const char* path,
467                 struct fuse_bufvec** bufp, size_t size, off_t off,
468                 struct fuse_file_info* fi) {
469    clobber_context_user_data(fs);
470    switch (fs->op_version) {
471        /* FUSE < 2.9 didn't have read_buf(). */
472    case 11:
473    case 21:
474    case 22:
475    case 23:
476    case 25:
477    case 26:
478    case 28:
479        return -ENOSYS;
480#define CALL_READ_BUF(VER)                                              \
481    case VER:                                                           \
482        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->read_buf) \
483            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->read_buf(path, bufp, size, off, fi); \
484        else                                                            \
485            return -ENOSYS
486        CALL_READ_BUF(29);
487        CALL_READ_BUF(30);
488        CALL_READ_BUF(34);
489        CALL_READ_BUF(35);
490        CALL_READ_BUF(38);
491#undef CALL_READ_BUF
492    default:
493        UNKNOWN_VERSION(fs->op_version);
494    }
495}
496
497int
498fuse_fs_write(struct fuse_fs* fs, const char* path, const char* buf,
499              size_t size, off_t off, struct fuse_file_info* fi) {
500    clobber_context_user_data(fs);
501    switch (fs->op_version) {
502#define CALL_OLD_WRITE(VER)                                             \
503    case VER:                                                           \
504        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->write) \
505            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->write(path, buf, size, off); \
506        else                                                            \
507            return -ENOSYS
508        CALL_OLD_WRITE(11);
509        CALL_OLD_WRITE(21);
510#undef CALL_OLD_WRITE
511
512#define CALL_WRITE(VER)                                                 \
513    case VER:                                                           \
514        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->write) \
515            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->write(path, buf, size, off, fi); \
516        else                                                            \
517            return -ENOSYS
518        CALL_WRITE(22);
519        CALL_WRITE(23);
520        CALL_WRITE(25);
521        CALL_WRITE(26);
522        CALL_WRITE(28);
523        CALL_WRITE(29);
524        CALL_WRITE(30);
525        CALL_WRITE(34);
526        CALL_WRITE(35);
527        CALL_WRITE(38);
528#undef CALL_WRITE
529    default:
530        UNKNOWN_VERSION(fs->op_version);
531    }
532}
533
534int
535fuse_fs_write_buf(struct fuse_fs* fs, const char* path,
536                  struct fuse_bufvec* bufp, off_t off,
537                  struct fuse_file_info* fi) {
538    clobber_context_user_data(fs);
539    switch (fs->op_version) {
540        /* FUSE < 2.9 didn't have write_buf(). */
541    case 11:
542    case 21:
543    case 22:
544    case 23:
545    case 25:
546    case 26:
547    case 28:
548        return -ENOSYS;
549#define CALL_WRITE_BUF(VER)                                             \
550    case VER:                                                           \
551        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->write_buf) \
552            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->write_buf(path, bufp, off, fi); \
553        else                                                            \
554            return -ENOSYS
555        CALL_WRITE_BUF(29);
556        CALL_WRITE_BUF(30);
557        CALL_WRITE_BUF(34);
558        CALL_WRITE_BUF(35);
559        CALL_WRITE_BUF(38);
560#undef CALL_WRITE_BUF
561    default:
562        UNKNOWN_VERSION(fs->op_version);
563    }
564}
565
566int
567fuse_fs_fsync(struct fuse_fs* fs, const char* path, int datasync, struct fuse_file_info* fi) {
568    clobber_context_user_data(fs);
569    switch (fs->op_version) {
570#define CALL_OLD_FSYNC(VER)                                             \
571    case VER:                                                           \
572        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->fsync) \
573            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->fsync(path, datasync); \
574        else                                                            \
575            return -ENOSYS
576        CALL_OLD_FSYNC(11);
577        CALL_OLD_FSYNC(21);
578#undef CALL_OLD_FSYNC
579
580#define CALL_FSYNC(VER)                                                 \
581    case VER:                                                           \
582        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->fsync) \
583            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->fsync(path, datasync, fi); \
584        else                                                            \
585            return -ENOSYS
586        CALL_FSYNC(22);
587        CALL_FSYNC(23);
588        CALL_FSYNC(25);
589        CALL_FSYNC(26);
590        CALL_FSYNC(28);
591        CALL_FSYNC(29);
592        CALL_FSYNC(30);
593        CALL_FSYNC(34);
594        CALL_FSYNC(35);
595        CALL_FSYNC(38);
596#undef CALL_FSYNC
597    default:
598        UNKNOWN_VERSION(fs->op_version);
599    }
600}
601
602int
603fuse_fs_flush(struct fuse_fs* fs, const char* path, struct fuse_file_info* fi) {
604    clobber_context_user_data(fs);
605    /* flush() appeared on FUSE 2.1 and its prototype was changed on
606     * 2.2. */
607    switch (fs->op_version) {
608    case 11:
609        return -ENOSYS;
610    case 21:
611        if (((const struct fuse_operations_v21 *)fs->op)->flush)
612            return ((const struct fuse_operations_v21 *)fs->op)->flush(path);
613        else
614            return -ENOSYS;
615
616#define CALL_FLUSH(VER)                                                 \
617    case VER:                                                           \
618        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->flush) \
619            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->flush(path, fi); \
620        else                                                            \
621            return -ENOSYS
622        CALL_FLUSH(22);
623        CALL_FLUSH(23);
624        CALL_FLUSH(25);
625        CALL_FLUSH(26);
626        CALL_FLUSH(28);
627        CALL_FLUSH(29);
628        CALL_FLUSH(30);
629        CALL_FLUSH(34);
630        CALL_FLUSH(35);
631        CALL_FLUSH(38);
632#undef CALL_FLUSH
633    default:
634        UNKNOWN_VERSION(fs->op_version);
635    }
636}
637
638static void
639zero_statvfs(struct statvfs* dst) {
640    dst->f_bsize   = 0;
641    dst->f_frsize  = 0;
642    dst->f_blocks  = 0;
643    dst->f_bfree   = 0;
644    dst->f_bavail  = 0;
645    dst->f_files   = 0;
646    dst->f_ffree   = 0;
647    dst->f_fresvd  = 0;
648}
649static void
650fuse_statfs_to_statvfs(struct statvfs* dst, const struct fuse_statfs* src) {
651    dst->f_bsize   = (unsigned long)src->block_size;
652    dst->f_frsize  = (unsigned long)src->block_size; /* Dunno if this is correct. */
653    dst->f_blocks  = (fsblkcnt_t)src->blocks;
654    dst->f_bfree   = (fsblkcnt_t)src->blocks_free;
655    dst->f_bavail  = (fsblkcnt_t)src->blocks_free;
656    dst->f_files   = (fsfilcnt_t)src->files;
657    dst->f_ffree   = (fsfilcnt_t)src->files_free;
658}
659static void
660linux_statfs_to_statvfs(struct statvfs* dst, const struct statfs* src) {
661    dst->f_bsize   = (unsigned long)src->f_bsize;
662    dst->f_frsize  = (unsigned long)src->f_bsize; /* Dunno if this is correct. */
663    dst->f_blocks  = src->f_blocks;
664    dst->f_bfree   = src->f_bfree;
665    dst->f_bavail  = src->f_bavail;
666    dst->f_files   = src->f_files;
667    dst->f_ffree   = src->f_ffree;
668}
669int
670fuse_fs_statfs(struct fuse_fs* fs, const char* path, struct statvfs* buf) {
671    clobber_context_user_data(fs);
672
673    zero_statvfs(buf);
674
675    switch (fs->op_version) {
676        /* FUSE < 2.1 used "struct fuse_statfs". */
677    case 11:
678        if (((const struct fuse_operations_v11*)fs->op)->statfs) {
679            struct fuse_statfs statfs_v11;
680            int ret;
681
682            ret = ((const struct fuse_operations_v11*)fs->op)->statfs(path, &statfs_v11);
683            if (ret == 0)
684                fuse_statfs_to_statvfs(buf, &statfs_v11);
685
686            return ret;
687        }
688        else
689            return 0; /* Special case */
690
691        /* FUSE >= 2.2 && < 2.5 used Linux-specific "struct
692         * statfs". */
693#define CALL_LINUX_STATFS(VER)                                          \
694    case VER:                                                           \
695        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->statfs) { \
696            struct statfs statfs_v22;                                   \
697            int ret;                                                    \
698                                                                        \
699            ret = ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->statfs(path, &statfs_v22); \
700            if (ret == 0)                                               \
701                linux_statfs_to_statvfs(buf, &statfs_v22);              \
702                                                                        \
703            return ret;                                                 \
704        }                                                               \
705        else                                                            \
706            return 0; /* Special case */
707        CALL_LINUX_STATFS(22);
708        CALL_LINUX_STATFS(23);
709#undef CALL_STATFS
710
711        /* FUSE >= 2.5 use struct statvfs. */
712#define CALL_STATFS(VER)                                                \
713    case VER:                                                           \
714        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->statfs) \
715            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->statfs(path, buf); \
716        else                                                            \
717            return 0; /* Special case */
718        CALL_STATFS(25);
719        CALL_STATFS(26);
720        CALL_STATFS(28);
721        CALL_STATFS(29);
722        CALL_STATFS(30);
723        CALL_STATFS(34);
724        CALL_STATFS(35);
725        CALL_STATFS(38);
726#undef CALL_STATFS
727    default:
728        UNKNOWN_VERSION(fs->op_version);
729    }
730}
731
732int
733fuse_fs_opendir(struct fuse_fs* fs, const char* path, struct fuse_file_info* fi) {
734    clobber_context_user_data(fs);
735    switch (fs->op_version) {
736        /* FUSE < 2.3 didn't have opendir() and used to read
737         * directories without opening them. */
738    case 11:
739    case 21:
740    case 22:
741        return 0; /* Special case */
742
743#define CALL_OPENDIR(VER)                                               \
744    case VER:                                                           \
745        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->opendir) \
746            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->opendir(path, fi); \
747        else                                                            \
748            return 0 /* Special case */
749        CALL_OPENDIR(23);
750        CALL_OPENDIR(25);
751        CALL_OPENDIR(26);
752        CALL_OPENDIR(28);
753        CALL_OPENDIR(29);
754        CALL_OPENDIR(30);
755        CALL_OPENDIR(34);
756        CALL_OPENDIR(35);
757        CALL_OPENDIR(38);
758#undef CALL_OPENDIR
759    default:
760        UNKNOWN_VERSION(fs->op_version);
761    }
762}
763
764/* ===================================
765 *     -=- The readdir Madness -=-
766 *     Juggling with Nested Shims
767 * =================================== */
768
769struct fuse_fill_dir_v23_shim {
770    void*               dirh;
771    fuse_fill_dir_t_v23 fill_dir_v23;
772};
773
774/* Translate dirent DT_* to mode_t. Needed by shim functions. */
775static mode_t
776dt_to_mode(int dt) {
777    switch (dt) {
778    case DT_UNKNOWN: return 0;
779    case DT_FIFO:    return S_IFIFO;
780    case DT_CHR:     return S_IFCHR;
781    case DT_DIR:     return S_IFCHR;
782    case DT_BLK:     return S_IFBLK;
783    case DT_REG:     return S_IFREG;
784    case DT_LNK:     return S_IFLNK;
785    case DT_SOCK:    return S_IFSOCK;
786    case DT_WHT:     return S_IFWHT;
787    default:
788        errx(EXIT_FAILURE, "%s: unknown dirent type: %d",
789             __func__, dt);
790    }
791}
792
793/* This is a shim function that satisfies the type of
794 * fuse_dirfil_t_v11 but calls fuse_fill_dir_v23. */
795static int
796fuse_dirfil_v11_to_fill_dir_v23(fuse_dirh_t handle, const char* name, int type) {
797    struct fuse_fill_dir_v23_shim* shim = handle;
798    struct stat stbuf;
799    int res; /* 1 or 0 */
800
801    memset(&stbuf, 0, sizeof(stbuf));
802    stbuf.st_mode = dt_to_mode(type);
803
804    res = shim->fill_dir_v23(shim->dirh, name, &stbuf, 0);
805    return res ? -ENOMEM : 0;
806}
807
808/* This is a shim function that satisfies the type of
809 * fuse_dirfil_t_v22 but calls fuse_fill_dir_v23. */
810static int
811fuse_dirfil_v22_to_fill_dir_v23(fuse_dirh_t handle, const char* name, int type, ino_t ino) {
812    struct fuse_fill_dir_v23_shim* shim = handle;
813    struct stat stbuf;
814    int res; /* 1 or 0 */
815
816    memset(&stbuf, 0, sizeof(stbuf));
817    stbuf.st_mode = dt_to_mode(type);
818    stbuf.st_ino  = ino;
819
820    res = shim->fill_dir_v23(shim->dirh, name, &stbuf, 0);
821    return res ? -ENOMEM : 0;
822}
823
824struct fuse_fill_dir_v30_shim {
825    void*               dirh;
826    fuse_fill_dir_t_v30 fill_dir_v30;
827};
828
829/* This is a shim function that satisfies the type of
830 * fuse_fill_dir_v23 but calls fuse_fill_dir_v30. */
831static int
832fuse_fill_dir_v23_to_v30(void* buf, const char* name,
833                         const struct stat* stat, off_t off) {
834
835    struct fuse_fill_dir_v30_shim* shim = buf;
836
837    return shim->fill_dir_v30(shim->dirh, name, stat, off, (enum fuse_fill_dir_flags)0);
838}
839
840int
841fuse_fs_readdir_v27(struct fuse_fs* fs, const char* path, void* buf,
842                    fuse_fill_dir_t_v23 filler, off_t off,
843                    struct fuse_file_info* fi) {
844
845    struct fuse_fill_dir_v23_shim v23_shim;
846
847    v23_shim.dirh         = buf;
848    v23_shim.fill_dir_v23 = filler;
849
850    clobber_context_user_data(fs);
851
852    switch (fs->op_version) {
853        /* FUSE < 2.2 had getdir() that used fuse_dirfil_t_v11. */
854#define CALL_GETDIR_V11(VER)                                        \
855    case VER:                                                       \
856        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->getdir) \
857            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->getdir(path, &v23_shim, fuse_dirfil_v11_to_fill_dir_v23); \
858        else                                                            \
859            return -ENOSYS
860        CALL_GETDIR_V11(11);
861        CALL_GETDIR_V11(21);
862#undef CALL_GETDIR_V11
863
864        /* FUSE 2.2 had getdir() that used fuse_dirfil_t_v22 but
865         * didn't have readdir(). */
866    case 22:
867        if (((const struct fuse_operations_v22*)fs->op)->getdir)
868            return ((const struct fuse_operations_v22*)fs->op)->getdir(path, &v23_shim, fuse_dirfil_v22_to_fill_dir_v23);
869        else
870            return -ENOSYS;
871
872        /* FUSE 2.3 introduced readdir() but still had getdir() as
873         * a deprecated operation. It had been this way until FUSE 3.0
874         * finally removed getdir() and also changed the prototype of
875         * readdir(). */
876#define CALL_READDIR_OR_GETDIR(VER)                                     \
877    case VER:                                                           \
878        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->readdir) \
879            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->readdir(path, buf, filler, off, fi); \
880        else if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->getdir) \
881            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->getdir(path, &v23_shim, fuse_dirfil_v22_to_fill_dir_v23); \
882        else                                                            \
883            return -ENOSYS
884        CALL_READDIR_OR_GETDIR(23);
885        CALL_READDIR_OR_GETDIR(25);
886        CALL_READDIR_OR_GETDIR(26);
887        CALL_READDIR_OR_GETDIR(28);
888        CALL_READDIR_OR_GETDIR(29);
889#undef CALL_READDIR_OR_GETDIR
890
891    default:
892        /* FUSE >= 3.0 filesystems will never call this function. We
893         * can safely ignore them here. */
894        UNKNOWN_VERSION(fs->op_version);
895    }
896}
897
898int
899fuse_fs_readdir_v30(struct fuse_fs* fs, const char* path, void* buf,
900                    fuse_fill_dir_t_v30 filler, off_t off,
901                    struct fuse_file_info* fi, enum fuse_readdir_flags flags) {
902    clobber_context_user_data(fs);
903
904    if (fs->op_version < 30) {
905        struct fuse_fill_dir_v30_shim v30_shim;
906
907        v30_shim.dirh         = buf;
908        v30_shim.fill_dir_v30 = filler;
909
910        return fuse_fs_readdir_v27(fs, path, &v30_shim, fuse_fill_dir_v23_to_v30, off, fi);
911    }
912    else {
913        switch (fs->op_version) {
914#define CALL_READDIR(VER)                                               \
915            case VER:                                                   \
916                if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->readdir) \
917                    return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->readdir(path, buf, filler, off, fi, flags); \
918                else                                                    \
919                    return -ENOSYS
920            CALL_READDIR(30);
921            CALL_READDIR(34);
922            CALL_READDIR(35);
923            CALL_READDIR(38);
924#undef CALL_READDIR
925        default:
926            UNKNOWN_VERSION(fs->op_version);
927        }
928    }
929}
930
931/* ==============================
932 *   The End of readdir Madness
933 * ============================== */
934
935int
936fuse_fs_fsyncdir(struct fuse_fs* fs, const char* path, int datasync, struct fuse_file_info* fi) {
937    clobber_context_user_data(fs);
938    /* fsyncdir() appeared on FUSE 2.3. */
939    switch (fs->op_version) {
940    case 11:
941    case 21:
942    case 22:
943        return -ENOSYS;
944
945#define CALL_FSYNCDIR(VER)                                              \
946    case VER:                                                           \
947        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->fsyncdir) \
948            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->fsyncdir(path, datasync, fi); \
949        else                                                            \
950            return -ENOSYS
951        CALL_FSYNCDIR(23);
952        CALL_FSYNCDIR(25);
953        CALL_FSYNCDIR(26);
954        CALL_FSYNCDIR(28);
955        CALL_FSYNCDIR(29);
956        CALL_FSYNCDIR(30);
957        CALL_FSYNCDIR(34);
958        CALL_FSYNCDIR(35);
959        CALL_FSYNCDIR(38);
960#undef CALL_FSYNCDIR
961    default:
962        UNKNOWN_VERSION(fs->op_version);
963    }
964}
965
966int
967fuse_fs_releasedir(struct fuse_fs* fs, const char* path, struct fuse_file_info* fi) {
968    clobber_context_user_data(fs);
969    switch (fs->op_version) {
970        /* FUSE < 2.3 didn't have releasedir() and was reading
971         * directories without opening them. */
972    case 11:
973    case 21:
974    case 22:
975        return 0; /* Special case */
976
977#define CALL_RELEASEDIR(VER)                                            \
978    case VER:                                                           \
979        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->releasedir) \
980            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->releasedir(path, fi); \
981        else                                                            \
982            return 0 /* Special case */
983        CALL_RELEASEDIR(23);
984        CALL_RELEASEDIR(25);
985        CALL_RELEASEDIR(26);
986        CALL_RELEASEDIR(28);
987        CALL_RELEASEDIR(29);
988        CALL_RELEASEDIR(30);
989        CALL_RELEASEDIR(34);
990        CALL_RELEASEDIR(35);
991        CALL_RELEASEDIR(38);
992#undef CALL_RELEASEDIR
993    default:
994        UNKNOWN_VERSION(fs->op_version);
995    }
996}
997
998int
999fuse_fs_create(struct fuse_fs* fs, const char* path, mode_t mode, struct fuse_file_info* fi) {
1000    clobber_context_user_data(fs);
1001    switch (fs->op_version) {
1002        /* FUSE < 2.5 didn't have create(). */
1003    case 11:
1004    case 21:
1005    case 22:
1006    case 23:
1007        return -ENOSYS;
1008
1009#define CALL_CREATE(VER)                                                \
1010    case VER:                                                           \
1011        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->create) \
1012            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->create(path, mode, fi); \
1013        else                                                            \
1014            return -ENOSYS
1015        CALL_CREATE(25);
1016        CALL_CREATE(26);
1017        CALL_CREATE(28);
1018        CALL_CREATE(29);
1019        CALL_CREATE(30);
1020        CALL_CREATE(34);
1021        CALL_CREATE(35);
1022        CALL_CREATE(38);
1023#undef CALL_CREATE
1024    default:
1025        UNKNOWN_VERSION(fs->op_version);
1026    }
1027}
1028
1029int
1030fuse_fs_lock(struct fuse_fs* fs, const char* path, struct fuse_file_info* fi,
1031             int cmd, struct flock* lock) {
1032    clobber_context_user_data(fs);
1033    /* locK() appeared on FUSE 2.6. */
1034    switch (fs->op_version) {
1035    case 11:
1036    case 21:
1037    case 22:
1038    case 23:
1039    case 25:
1040        return -ENOSYS;
1041
1042#define CALL_LOCK(VER)                                                  \
1043    case VER:                                                           \
1044        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->lock) \
1045            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->lock(path, fi, cmd, lock); \
1046        else                                                            \
1047            return -ENOSYS
1048        CALL_LOCK(26);
1049        CALL_LOCK(28);
1050        CALL_LOCK(29);
1051        CALL_LOCK(30);
1052        CALL_LOCK(34);
1053        CALL_LOCK(35);
1054        CALL_LOCK(38);
1055#undef CALL_LOCK
1056    default:
1057        UNKNOWN_VERSION(fs->op_version);
1058    }
1059}
1060
1061int
1062fuse_fs_flock(struct fuse_fs* fs, const char* path, struct fuse_file_info* fi, int op) {
1063    clobber_context_user_data(fs);
1064    /* flocK() appeared on FUSE 2.9. */
1065    switch (fs->op_version) {
1066    case 11:
1067    case 21:
1068    case 22:
1069    case 23:
1070    case 25:
1071    case 26:
1072    case 28:
1073        return -ENOSYS;
1074
1075#define CALL_FLOCK(VER)                                                 \
1076    case VER:                                                           \
1077        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->flock) \
1078            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->flock(path, fi, op); \
1079        else                                                            \
1080            return -ENOSYS
1081        CALL_FLOCK(29);
1082        CALL_FLOCK(30);
1083        CALL_FLOCK(34);
1084        CALL_FLOCK(35);
1085        CALL_FLOCK(38);
1086#undef CALL_FLOCK
1087    default:
1088        UNKNOWN_VERSION(fs->op_version);
1089    }
1090}
1091
1092int
1093fuse_fs_chmod_v27(struct fuse_fs *fs, const char *path, mode_t mode) {
1094    return fuse_fs_chmod_v30(fs, path, mode, NULL);
1095}
1096
1097int
1098fuse_fs_chmod_v30(struct fuse_fs* fs, const char* path,
1099                  mode_t mode, struct fuse_file_info* fi) {
1100    clobber_context_user_data(fs);
1101    switch (fs->op_version) {
1102#define CALL_OLD_CHMOD(VER)                                             \
1103    case VER:                                                           \
1104        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->chmod) \
1105            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->chmod(path, mode); \
1106        else                                                            \
1107            return -ENOSYS
1108        CALL_OLD_CHMOD(11);
1109        CALL_OLD_CHMOD(21);
1110        CALL_OLD_CHMOD(22);
1111        CALL_OLD_CHMOD(23);
1112        CALL_OLD_CHMOD(25);
1113        CALL_OLD_CHMOD(26);
1114        CALL_OLD_CHMOD(28);
1115        CALL_OLD_CHMOD(29);
1116#undef CALL_OLD_CHMOD
1117
1118#define CALL_CHMOD(VER)                                                 \
1119    case VER:                                                           \
1120        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->chmod) \
1121            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->chmod(path, mode, fi); \
1122        else                                                            \
1123            return -ENOSYS
1124        CALL_CHMOD(30);
1125        CALL_CHMOD(34);
1126        CALL_CHMOD(35);
1127        CALL_CHMOD(38);
1128#undef CALL_CHMOD
1129    default:
1130        UNKNOWN_VERSION(fs->op_version);
1131    }
1132}
1133
1134int fuse_fs_chown_v27(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid) {
1135    return fuse_fs_chown_v30(fs, path, uid, gid, NULL);
1136}
1137
1138int
1139fuse_fs_chown_v30(struct fuse_fs* fs, const char* path,
1140                  uid_t uid, gid_t gid, struct fuse_file_info* fi) {
1141    clobber_context_user_data(fs);
1142    switch (fs->op_version) {
1143#define CALL_OLD_CHOWN(VER)                                             \
1144    case VER:                                                           \
1145        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->chown) \
1146            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->chown(path, uid, gid); \
1147        else                                                            \
1148            return -ENOSYS
1149        CALL_OLD_CHOWN(11);
1150        CALL_OLD_CHOWN(21);
1151        CALL_OLD_CHOWN(22);
1152        CALL_OLD_CHOWN(23);
1153        CALL_OLD_CHOWN(25);
1154        CALL_OLD_CHOWN(26);
1155        CALL_OLD_CHOWN(28);
1156        CALL_OLD_CHOWN(29);
1157#undef CALL_OLD_CHOWN
1158
1159#define CALL_CHOWN(VER)                         \
1160    case VER:                                   \
1161        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->chown) \
1162            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->chown(path, uid, gid, fi); \
1163        else                                                            \
1164            return -ENOSYS
1165        CALL_CHOWN(30);
1166        CALL_CHOWN(34);
1167        CALL_CHOWN(35);
1168        CALL_CHOWN(38);
1169#undef CALL_CHOWN
1170    default:
1171        UNKNOWN_VERSION(fs->op_version);
1172    }
1173}
1174
1175int fuse_fs_truncate_v27(struct fuse_fs *fs, const char *path, off_t size) {
1176    return fuse_fs_truncate_v30(fs, path, size, NULL);
1177}
1178
1179int
1180fuse_fs_truncate_v30(struct fuse_fs* fs, const char* path, off_t size, struct fuse_file_info* fi) {
1181    clobber_context_user_data(fs);
1182    switch (fs->op_version) {
1183#define CALL_OLD_TRUNCATE(VER)                                          \
1184    case VER:                                                           \
1185        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->truncate) \
1186            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->truncate(path, size); \
1187        else                                                            \
1188            return -ENOSYS
1189        CALL_OLD_TRUNCATE(11);
1190        CALL_OLD_TRUNCATE(21);
1191        CALL_OLD_TRUNCATE(22);
1192        CALL_OLD_TRUNCATE(23);
1193        CALL_OLD_TRUNCATE(25);
1194        CALL_OLD_TRUNCATE(26);
1195        CALL_OLD_TRUNCATE(28);
1196        CALL_OLD_TRUNCATE(29);
1197#undef CALL_OLD_TRUNCATE
1198
1199#define CALL_TRUNCATE(VER)                      \
1200    case VER:                                   \
1201        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->truncate) \
1202            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->truncate(path, size, fi); \
1203        else                                                            \
1204            return -ENOSYS
1205        CALL_TRUNCATE(30);
1206        CALL_TRUNCATE(34);
1207        CALL_TRUNCATE(35);
1208        CALL_TRUNCATE(38);
1209#undef CALL_TRUNCATE
1210    default:
1211        UNKNOWN_VERSION(fs->op_version);
1212    }
1213}
1214
1215int
1216fuse_fs_ftruncate(struct fuse_fs* fs, const char* path, off_t size, struct fuse_file_info* fi) {
1217    clobber_context_user_data(fs);
1218    switch (fs->op_version) {
1219        /* FUSE < 2.5 didn't have ftruncate(). Always fall back to
1220         * truncate(). */
1221#define CALL_OLD_TRUNCATE(VER)                                          \
1222    case VER:                                                           \
1223        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->truncate) \
1224            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->truncate(path, size); \
1225        else                                                            \
1226            return -ENOSYS
1227        CALL_OLD_TRUNCATE(11);
1228        CALL_OLD_TRUNCATE(21);
1229        CALL_OLD_TRUNCATE(22);
1230        CALL_OLD_TRUNCATE(23);
1231#undef CALL_OLD_TRUNCATE
1232
1233        /* ftruncate() appeared on FUSE 2.5 and then disappeared on
1234         * FUSE 3.0. Call it if it exists, or fall back to truncate()
1235         * otherwise. */
1236#define CALL_FTRUNCATE_OR_TRUNCATE(VER)                                 \
1237    case VER:                                                           \
1238        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->ftruncate) \
1239            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->ftruncate(path, size, fi); \
1240        else if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->truncate) \
1241            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->truncate(path, size); \
1242        else                                                            \
1243            return -ENOSYS
1244        CALL_FTRUNCATE_OR_TRUNCATE(25);
1245        CALL_FTRUNCATE_OR_TRUNCATE(26);
1246        CALL_FTRUNCATE_OR_TRUNCATE(28);
1247        CALL_FTRUNCATE_OR_TRUNCATE(29);
1248#undef CALL_FTRUNCATE_OR_TRUNCATE
1249
1250        /* FUSE >= 3.0 have truncate() but with a different function
1251         * type. */
1252#define CALL_TRUNCATE(VER)                      \
1253    case VER:                                   \
1254        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->truncate) \
1255            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->truncate(path, size, fi); \
1256        else                                                            \
1257            return -ENOSYS
1258        CALL_TRUNCATE(30);
1259        CALL_TRUNCATE(34);
1260        CALL_TRUNCATE(35);
1261        CALL_TRUNCATE(38);
1262#undef CALL_TRUNCATE
1263    default:
1264        UNKNOWN_VERSION(fs->op_version);
1265    }
1266}
1267
1268int
1269fuse_fs_utimens_v27(struct fuse_fs *fs, const char *path, const struct timespec tv[2]) {
1270    return fuse_fs_utimens_v30(fs, path, tv, NULL);
1271}
1272
1273int
1274fuse_fs_utimens_v30(struct fuse_fs* fs, const char* path,
1275                    const struct timespec tv[2], struct fuse_file_info* fi) {
1276    struct utimbuf timbuf;
1277
1278    timbuf.actime  = tv[0].tv_sec;
1279    timbuf.modtime = tv[1].tv_sec;
1280
1281    clobber_context_user_data(fs);
1282
1283    switch (fs->op_version) {
1284        /* FUSE < 2.6 didn't have utimens() but had utime()
1285         * instead. */
1286#define CALL_UTIME(VER)                                                 \
1287    case VER:                                                           \
1288        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->utime) \
1289            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->utime(path, &timbuf); \
1290        else                                                            \
1291            return -ENOSYS
1292        CALL_UTIME(11);
1293        CALL_UTIME(21);
1294        CALL_UTIME(22);
1295        CALL_UTIME(23);
1296        CALL_UTIME(25);
1297#undef CALL_UTIME
1298
1299        /* utimens() appeared on FUSE 2.6. Call it if it exists, or fall back to
1300         * utime() otherwise. */
1301#define CALL_UTIMENS_OR_UTIME(VER)                                      \
1302    case VER:                                                           \
1303        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->utimens) \
1304            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->utimens(path, tv); \
1305        else if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->utime) \
1306            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->utime(path, &timbuf); \
1307        else                                                            \
1308            return -ENOSYS
1309        CALL_UTIMENS_OR_UTIME(26);
1310        CALL_UTIMENS_OR_UTIME(28);
1311        CALL_UTIMENS_OR_UTIME(29);
1312#undef CALL_UTIMENS_OR_UTIME
1313
1314        /* utime() disappeared on FUSE 3.0. */
1315#define CALL_UTIMENS(VER)                       \
1316    case VER:                                   \
1317        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->utimens) \
1318            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->utimens(path, tv, fi); \
1319        else                                                            \
1320            return -ENOSYS
1321        CALL_UTIMENS(30);
1322        CALL_UTIMENS(34);
1323        CALL_UTIMENS(35);
1324        CALL_UTIMENS(38);
1325#undef CALL_UTIMENS
1326    default:
1327        UNKNOWN_VERSION(fs->op_version);
1328    }
1329}
1330
1331int
1332fuse_fs_access(struct fuse_fs* fs, const char* path, int mask) {
1333    clobber_context_user_data(fs);
1334    /* access() appeared on FUSE 2.5. */
1335    switch (fs->op_version) {
1336    case 11:
1337    case 21:
1338    case 22:
1339    case 23:
1340        return -ENOSYS;
1341#define CALL_ACCESS(VER)                                                \
1342    case VER:                                                           \
1343        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->access) \
1344            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->access(path, mask); \
1345        else                                                            \
1346            return -ENOSYS
1347        CALL_ACCESS(25);
1348        CALL_ACCESS(26);
1349        CALL_ACCESS(28);
1350        CALL_ACCESS(29);
1351        CALL_ACCESS(30);
1352        CALL_ACCESS(34);
1353        CALL_ACCESS(35);
1354        CALL_ACCESS(38);
1355#undef CALL_ACCESS
1356    default:
1357        UNKNOWN_VERSION(fs->op_version);
1358    }
1359}
1360
1361int
1362fuse_fs_readlink(struct fuse_fs* fs, const char* path, char* buf, size_t len) {
1363    clobber_context_user_data(fs);
1364    switch (fs->op_version) {
1365#define CALL_READLINK(VER)                                              \
1366    case VER:                                                           \
1367        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->readlink) \
1368            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->readlink(path, buf, len); \
1369        else                                                            \
1370            return -ENOSYS
1371        CALL_READLINK(11);
1372        CALL_READLINK(21);
1373        CALL_READLINK(22);
1374        CALL_READLINK(23);
1375        CALL_READLINK(25);
1376        CALL_READLINK(26);
1377        CALL_READLINK(28);
1378        CALL_READLINK(29);
1379        CALL_READLINK(30);
1380        CALL_READLINK(34);
1381        CALL_READLINK(35);
1382        CALL_READLINK(38);
1383#undef CALL_READLINK
1384    default:
1385        UNKNOWN_VERSION(fs->op_version);
1386    }
1387}
1388
1389int
1390fuse_fs_mknod(struct fuse_fs* fs, const char* path, mode_t mode, dev_t rdev) {
1391    clobber_context_user_data(fs);
1392    switch (fs->op_version) {
1393#define CALL_MKNOD(VER)                                                 \
1394    case VER:                                                           \
1395        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->mknod) \
1396            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->mknod(path, mode, rdev); \
1397        else                                                            \
1398            return -ENOSYS
1399        CALL_MKNOD(11);
1400        CALL_MKNOD(21);
1401        CALL_MKNOD(22);
1402        CALL_MKNOD(23);
1403        CALL_MKNOD(25);
1404        CALL_MKNOD(26);
1405        CALL_MKNOD(28);
1406        CALL_MKNOD(29);
1407        CALL_MKNOD(30);
1408        CALL_MKNOD(34);
1409        CALL_MKNOD(35);
1410        CALL_MKNOD(38);
1411#undef CALL_MKNOD
1412    default:
1413        UNKNOWN_VERSION(fs->op_version);
1414    }
1415}
1416
1417int
1418fuse_fs_mkdir(struct fuse_fs* fs, const char* path, mode_t mode) {
1419    clobber_context_user_data(fs);
1420    switch (fs->op_version) {
1421#define CALL_MKDIR(VER)                                                 \
1422    case VER:                                                           \
1423        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->mkdir) \
1424            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->mkdir(path, mode); \
1425        else                                                            \
1426            return -ENOSYS
1427        CALL_MKDIR(11);
1428        CALL_MKDIR(21);
1429        CALL_MKDIR(22);
1430        CALL_MKDIR(23);
1431        CALL_MKDIR(25);
1432        CALL_MKDIR(26);
1433        CALL_MKDIR(28);
1434        CALL_MKDIR(29);
1435        CALL_MKDIR(30);
1436        CALL_MKDIR(34);
1437        CALL_MKDIR(35);
1438        CALL_MKDIR(38);
1439#undef CALL_MKDIR
1440    default:
1441        UNKNOWN_VERSION(fs->op_version);
1442    }
1443}
1444
1445int fuse_fs_setxattr(struct fuse_fs* fs, const char* path, const char* name,
1446                     const char* value, size_t size, int flags) {
1447    clobber_context_user_data(fs);
1448    /* setxattr() appeared on FUSE 2.1. */
1449    switch (fs->op_version) {
1450    case 11:
1451        return -ENOSYS;
1452#define CALL_SETXATTR(VER)                                              \
1453    case VER:                                                           \
1454        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->setxattr) \
1455            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->setxattr(path, name, value, size, flags); \
1456        else                                                            \
1457            return -ENOSYS
1458        CALL_SETXATTR(21);
1459        CALL_SETXATTR(22);
1460        CALL_SETXATTR(23);
1461        CALL_SETXATTR(25);
1462        CALL_SETXATTR(26);
1463        CALL_SETXATTR(28);
1464        CALL_SETXATTR(29);
1465        CALL_SETXATTR(30);
1466        CALL_SETXATTR(34);
1467        CALL_SETXATTR(35);
1468        CALL_SETXATTR(38);
1469#undef CALL_SETXATTR
1470    default:
1471        UNKNOWN_VERSION(fs->op_version);
1472    }
1473}
1474
1475int
1476fuse_fs_getxattr(struct fuse_fs* fs, const char* path, const char* name,
1477                 char* value, size_t size) {
1478    clobber_context_user_data(fs);
1479    /* getxattr() appeared on FUSE 2.1. */
1480    switch (fs->op_version) {
1481    case 11:
1482        return -ENOSYS;
1483#define CALL_GETXATTR(VER)                                              \
1484    case VER:                                                           \
1485        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->getxattr) \
1486            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->getxattr(path, name, value, size); \
1487        else                                                            \
1488            return -ENOSYS
1489        CALL_GETXATTR(21);
1490        CALL_GETXATTR(22);
1491        CALL_GETXATTR(23);
1492        CALL_GETXATTR(25);
1493        CALL_GETXATTR(26);
1494        CALL_GETXATTR(28);
1495        CALL_GETXATTR(29);
1496        CALL_GETXATTR(30);
1497        CALL_GETXATTR(34);
1498        CALL_GETXATTR(35);
1499        CALL_GETXATTR(38);
1500#undef CALL_GETXATTR
1501    default:
1502        UNKNOWN_VERSION(fs->op_version);
1503    }
1504}
1505
1506int fuse_fs_listxattr(struct fuse_fs* fs, const char* path, char* list, size_t size) {
1507    clobber_context_user_data(fs);
1508    /* listxattr() appeared on FUSE 2.1. */
1509    switch (fs->op_version) {
1510    case 11:
1511        return -ENOSYS;
1512#define CALL_LISTXATTR(VER)                                             \
1513    case VER:                                                           \
1514        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->listxattr) \
1515            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->listxattr(path, list, size); \
1516        else                                                            \
1517            return -ENOSYS
1518        CALL_LISTXATTR(21);
1519        CALL_LISTXATTR(22);
1520        CALL_LISTXATTR(23);
1521        CALL_LISTXATTR(25);
1522        CALL_LISTXATTR(26);
1523        CALL_LISTXATTR(28);
1524        CALL_LISTXATTR(29);
1525        CALL_LISTXATTR(30);
1526        CALL_LISTXATTR(34);
1527        CALL_LISTXATTR(35);
1528        CALL_LISTXATTR(38);
1529#undef CALL_LISTXATTR
1530    default:
1531        UNKNOWN_VERSION(fs->op_version);
1532    }
1533}
1534
1535int
1536fuse_fs_removexattr(struct fuse_fs* fs, const char* path, const char* name) {
1537    clobber_context_user_data(fs);
1538    /* removexattr() appeared on FUSE 2.1. */
1539    switch (fs->op_version) {
1540    case 11:
1541        return -ENOSYS;
1542#define CALL_REMOVEXATTR(VER)                                           \
1543    case VER:                                                           \
1544        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->removexattr) \
1545            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->removexattr(path, name); \
1546        else                                                            \
1547            return -ENOSYS
1548        CALL_REMOVEXATTR(21);
1549        CALL_REMOVEXATTR(22);
1550        CALL_REMOVEXATTR(23);
1551        CALL_REMOVEXATTR(25);
1552        CALL_REMOVEXATTR(26);
1553        CALL_REMOVEXATTR(28);
1554        CALL_REMOVEXATTR(29);
1555        CALL_REMOVEXATTR(30);
1556        CALL_REMOVEXATTR(34);
1557        CALL_REMOVEXATTR(35);
1558        CALL_REMOVEXATTR(38);
1559#undef CALL_REMOVEXATTR
1560    default:
1561        UNKNOWN_VERSION(fs->op_version);
1562    }
1563}
1564
1565int
1566fuse_fs_bmap(struct fuse_fs* fs, const char* path, size_t blocksize, uint64_t *idx) {
1567    clobber_context_user_data(fs);
1568    /* bmap() appeared on FUSE 2.6. */
1569    switch (fs->op_version) {
1570    case 11:
1571    case 22:
1572    case 23:
1573    case 25:
1574        return -ENOSYS;
1575#define CALL_BMAP(VER)                                                  \
1576    case VER:                                                           \
1577        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->bmap) \
1578            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->bmap(path, blocksize, idx); \
1579        else                                                            \
1580            return -ENOSYS
1581        CALL_BMAP(26);
1582        CALL_BMAP(28);
1583        CALL_BMAP(29);
1584        CALL_BMAP(30);
1585        CALL_BMAP(34);
1586        CALL_BMAP(35);
1587        CALL_BMAP(38);
1588#undef CALL_BMAP
1589    default:
1590        UNKNOWN_VERSION(fs->op_version);
1591    }
1592}
1593
1594int fuse_fs_ioctl_v28(struct fuse_fs* fs, const char* path, int cmd, void* arg,
1595                      struct fuse_file_info* fi, unsigned int flags, void* data) {
1596    return fuse_fs_ioctl_v35(fs, path, (unsigned int)cmd, arg, fi, flags, data);
1597}
1598
1599int fuse_fs_ioctl_v35(struct fuse_fs* fs, const char* path, unsigned int cmd, void* arg,
1600                      struct fuse_file_info* fi, unsigned int flags, void* data) {
1601    clobber_context_user_data(fs);
1602    switch (fs->op_version) {
1603        /* ioctl() appeared on FUSE 2.8 but with (int)cmd. */
1604    case 11:
1605    case 22:
1606    case 23:
1607    case 25:
1608    case 26:
1609        return -ENOSYS;
1610#define CALL_OLD_IOCTL(VER)                                                 \
1611    case VER:                                                           \
1612        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->ioctl) \
1613            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->ioctl(path, (int)cmd, arg, fi, flags, data); \
1614        else                                                            \
1615            return -ENOSYS
1616        CALL_OLD_IOCTL(28);
1617        CALL_OLD_IOCTL(29);
1618        CALL_OLD_IOCTL(30);
1619        CALL_OLD_IOCTL(34);
1620#undef CALL_OLD_IOCTL
1621
1622        /* It was then changed to (unsigned int)cmd on FUSE 3.5. */
1623#define CALL_IOCTL(VER)                                                 \
1624    case VER:                                                           \
1625        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->ioctl) \
1626            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->ioctl(path, cmd, arg, fi, flags, data); \
1627        else                                                            \
1628            return -ENOSYS
1629        CALL_IOCTL(35);
1630        CALL_IOCTL(38);
1631#undef CALL_IOCTL
1632    default:
1633        UNKNOWN_VERSION(fs->op_version);
1634    }
1635}
1636
1637int
1638fuse_fs_poll(struct fuse_fs* fs, const char* path, struct fuse_file_info* fi,
1639             struct fuse_pollhandle* ph, unsigned* reventsp) {
1640    clobber_context_user_data(fs);
1641    /* poll() appeared on FUSE 2.8. */
1642    switch (fs->op_version) {
1643    case 11:
1644    case 22:
1645    case 23:
1646    case 25:
1647    case 26:
1648        return -ENOSYS;
1649#define CALL_POLL(VER)                                                  \
1650    case VER:                                                           \
1651        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->poll) \
1652            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->poll(path, fi, ph, reventsp); \
1653        else                                                            \
1654            return -ENOSYS
1655        CALL_POLL(28);
1656        CALL_POLL(29);
1657        CALL_POLL(30);
1658        CALL_POLL(34);
1659        CALL_POLL(35);
1660        CALL_POLL(38);
1661#undef CALL_POLL
1662    default:
1663        UNKNOWN_VERSION(fs->op_version);
1664    }
1665}
1666
1667int
1668fuse_fs_fallocate(struct fuse_fs* fs, const char* path, int mode, off_t offset,
1669                  off_t length, struct fuse_file_info* fi) {
1670    clobber_context_user_data(fs);
1671    /* fallocate() appeared on FUSE 2.9. */
1672    switch (fs->op_version) {
1673    case 11:
1674    case 22:
1675    case 23:
1676    case 25:
1677    case 26:
1678    case 28:
1679        return -ENOSYS;
1680#define CALL_FALLOCATE(VER)                                             \
1681    case VER:                                                           \
1682        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->fallocate) \
1683            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->fallocate(path, mode, offset, length, fi); \
1684        else                                                            \
1685            return -ENOSYS
1686        CALL_FALLOCATE(29);
1687        CALL_FALLOCATE(30);
1688        CALL_FALLOCATE(34);
1689        CALL_FALLOCATE(35);
1690        CALL_FALLOCATE(38);
1691#undef CALL_FALLOCATE
1692    default:
1693        UNKNOWN_VERSION(fs->op_version);
1694    }
1695}
1696
1697ssize_t
1698fuse_fs_copy_file_range(struct fuse_fs *fs,
1699                        const char *path_in, struct fuse_file_info *fi_in, off_t off_in,
1700                        const char *path_out, struct fuse_file_info *fi_out, off_t off_out,
1701                        size_t len, int flags) {
1702    clobber_context_user_data(fs);
1703    /* copy_file_range() appeared on FUSE 3.4. */
1704    switch (fs->op_version) {
1705    case 11:
1706    case 22:
1707    case 23:
1708    case 25:
1709    case 26:
1710    case 28:
1711    case 29:
1712    case 30:
1713        return -ENOSYS;
1714#define CALL_COPY_FILE_RANGE(VER)                                       \
1715    case VER:                                                           \
1716        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->copy_file_range) \
1717            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->copy_file_range(path_in, fi_in, off_in, path_out, fi_out, off_out, len, flags); \
1718        else                                                            \
1719            return -ENOSYS
1720        CALL_COPY_FILE_RANGE(34);
1721        CALL_COPY_FILE_RANGE(35);
1722        CALL_COPY_FILE_RANGE(38);
1723#undef CALL_COPY_FILE_RANGE
1724    default:
1725        UNKNOWN_VERSION(fs->op_version);
1726    }
1727}
1728
1729off_t
1730fuse_fs_lseek(struct fuse_fs* fs, const char* path, off_t off, int whence,
1731              struct fuse_file_info* fi) {
1732    clobber_context_user_data(fs);
1733    /* lseek() appeared on FUSE 3.8. */
1734    switch (fs->op_version) {
1735    case 11:
1736    case 22:
1737    case 23:
1738    case 25:
1739    case 26:
1740    case 28:
1741    case 29:
1742    case 30:
1743    case 34:
1744    case 35:
1745        return -ENOSYS;
1746#define CALL_LSEEK(VER)                         \
1747    case VER:                                   \
1748        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->lseek) \
1749            return ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->lseek(path, off, whence, fi); \
1750        else                                                            \
1751            return -ENOSYS
1752        CALL_LSEEK(38);
1753#undef CALL_LSEEK
1754    default:
1755        UNKNOWN_VERSION(fs->op_version);
1756    }
1757}
1758
1759void
1760fuse_fs_init_v27(struct fuse_fs *fs, struct fuse_conn_info *conn) {
1761    fuse_fs_init_v30(fs, conn, NULL);
1762}
1763
1764void
1765fuse_fs_init_v30(struct fuse_fs* fs, struct fuse_conn_info* conn,
1766                 struct fuse_config* cfg) {
1767    clobber_context_user_data(fs);
1768    switch (fs->op_version) {
1769    case 11:
1770    case 21:
1771    case 22:
1772        break;
1773
1774        /* init() appeared on FUSE 2.3 as init(void). */
1775#define CALL_NULLARY_INIT(VER)                                          \
1776    case VER:                                                           \
1777        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->init) \
1778            fs->user_data = ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->init(); \
1779        break
1780        CALL_NULLARY_INIT(23);
1781        CALL_NULLARY_INIT(25);
1782#undef CALL_NULLARY_INIT
1783
1784        /* It was changed to init(struct fuse_conn_info*) on FUSE
1785         * 2.6. */
1786#define CALL_UNARY_INIT(VER)                                            \
1787    case VER:                                                           \
1788        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->init) \
1789            fs->user_data = ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->init(conn); \
1790        break
1791        CALL_UNARY_INIT(26);
1792        CALL_UNARY_INIT(28);
1793        CALL_UNARY_INIT(29);
1794#undef CALL_INIT
1795
1796        /* It was again changed to init(struct fuse_conn_info*, struct
1797         * fuse_config*) on FUSE 3.0. */
1798#define CALL_BINARY_INIT(VER)                   \
1799    case VER:                                   \
1800        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->init) \
1801            fs->user_data = ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->init(conn, cfg); \
1802        break
1803        CALL_BINARY_INIT(30);
1804        CALL_BINARY_INIT(34);
1805        CALL_BINARY_INIT(35);
1806        CALL_BINARY_INIT(38);
1807#undef CALL_BINARY_INIT
1808    default:
1809        UNKNOWN_VERSION(fs->op_version);
1810    }
1811}
1812
1813void
1814fuse_fs_destroy(struct fuse_fs *fs) {
1815    clobber_context_user_data(fs);
1816    switch (fs->op_version) {
1817        /* destroy() appeared on FUSE 2.3. */
1818    case 11:
1819    case 21:
1820    case 22:
1821        break;
1822
1823#define CALL_DESTROY(VER)                                               \
1824    case VER:                                                           \
1825        if (((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->destroy) \
1826            ((const struct __CONCAT(fuse_operations_v,VER)*)fs->op)->destroy(fs->user_data); \
1827        break
1828        CALL_DESTROY(23);
1829        CALL_DESTROY(25);
1830        CALL_DESTROY(26);
1831        CALL_DESTROY(28);
1832        CALL_DESTROY(29);
1833        CALL_DESTROY(30);
1834        CALL_DESTROY(34);
1835        CALL_DESTROY(35);
1836        CALL_DESTROY(38);
1837#undef CALL_DESTROY
1838    default:
1839        UNKNOWN_VERSION(fs->op_version);
1840    }
1841
1842    /* fuse_fs_destroy(3) also deallocates struct fuse_fs itself. */
1843    free(fs->op);
1844    free(fs);
1845}
1846