1*84d9c625SLionel Sambuc /* $NetBSD: refuse.c,v 1.96 2012/12/30 10:04:22 tron Exp $ */
2490e0de5SThomas Veerman
3490e0de5SThomas Veerman /*
4490e0de5SThomas Veerman * Copyright � 2007 Alistair Crooks. All rights reserved.
5490e0de5SThomas Veerman * Copyright � 2007 Antti Kantee. All rights reserved.
6490e0de5SThomas Veerman *
7490e0de5SThomas Veerman * Redistribution and use in source and binary forms, with or without
8490e0de5SThomas Veerman * modification, are permitted provided that the following conditions
9490e0de5SThomas Veerman * are met:
10490e0de5SThomas Veerman * 1. Redistributions of source code must retain the above copyright
11490e0de5SThomas Veerman * notice, this list of conditions and the following disclaimer.
12490e0de5SThomas Veerman * 2. Redistributions in binary form must reproduce the above copyright
13490e0de5SThomas Veerman * notice, this list of conditions and the following disclaimer in the
14490e0de5SThomas Veerman * documentation and/or other materials provided with the distribution.
15490e0de5SThomas Veerman * 3. The name of the author may not be used to endorse or promote
16490e0de5SThomas Veerman * products derived from this software without specific prior written
17490e0de5SThomas Veerman * permission.
18490e0de5SThomas Veerman *
19490e0de5SThomas Veerman * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
20490e0de5SThomas Veerman * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21490e0de5SThomas Veerman * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22490e0de5SThomas Veerman * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23490e0de5SThomas Veerman * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24490e0de5SThomas Veerman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
25490e0de5SThomas Veerman * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26490e0de5SThomas Veerman * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27490e0de5SThomas Veerman * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28490e0de5SThomas Veerman * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29490e0de5SThomas Veerman * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30490e0de5SThomas Veerman */
31490e0de5SThomas Veerman
32490e0de5SThomas Veerman #include <sys/cdefs.h>
33490e0de5SThomas Veerman #if !defined(lint)
34*84d9c625SLionel Sambuc __RCSID("$NetBSD: refuse.c,v 1.96 2012/12/30 10:04:22 tron Exp $");
35490e0de5SThomas Veerman #endif /* !lint */
36490e0de5SThomas Veerman
37490e0de5SThomas Veerman #include <sys/types.h>
38490e0de5SThomas Veerman
39490e0de5SThomas Veerman #include <assert.h>
40490e0de5SThomas Veerman #include <err.h>
41490e0de5SThomas Veerman #include <errno.h>
42490e0de5SThomas Veerman #include <fuse.h>
43490e0de5SThomas Veerman #include <paths.h>
44490e0de5SThomas Veerman #include <stdio.h>
45490e0de5SThomas Veerman #include <stdlib.h>
46490e0de5SThomas Veerman #include <string.h>
47490e0de5SThomas Veerman #include <unistd.h>
48490e0de5SThomas Veerman #ifdef MULTITHREADED_REFUSE
49490e0de5SThomas Veerman #include <pthread.h>
50490e0de5SThomas Veerman #endif
51490e0de5SThomas Veerman
52490e0de5SThomas Veerman typedef uint64_t fuse_ino_t;
53490e0de5SThomas Veerman
54490e0de5SThomas Veerman struct fuse_config {
55490e0de5SThomas Veerman uid_t uid;
56490e0de5SThomas Veerman gid_t gid;
57490e0de5SThomas Veerman mode_t umask;
58490e0de5SThomas Veerman double entry_timeout;
59490e0de5SThomas Veerman double negative_timeout;
60490e0de5SThomas Veerman double attr_timeout;
61490e0de5SThomas Veerman double ac_attr_timeout;
62490e0de5SThomas Veerman int ac_attr_timeout_set;
63490e0de5SThomas Veerman int debug;
64490e0de5SThomas Veerman int hard_remove;
65490e0de5SThomas Veerman int use_ino;
66490e0de5SThomas Veerman int readdir_ino;
67490e0de5SThomas Veerman int set_mode;
68490e0de5SThomas Veerman int set_uid;
69490e0de5SThomas Veerman int set_gid;
70490e0de5SThomas Veerman int direct_io;
71490e0de5SThomas Veerman int kernel_cache;
72490e0de5SThomas Veerman int auto_cache;
73490e0de5SThomas Veerman int intr;
74490e0de5SThomas Veerman int intr_signal;
75490e0de5SThomas Veerman };
76490e0de5SThomas Veerman
77490e0de5SThomas Veerman struct fuse_chan {
78490e0de5SThomas Veerman const char *dir;
79490e0de5SThomas Veerman struct fuse_args *args;
80490e0de5SThomas Veerman struct puffs_usermount *pu;
81490e0de5SThomas Veerman int dead;
82490e0de5SThomas Veerman };
83490e0de5SThomas Veerman
84490e0de5SThomas Veerman /* this is the private fuse structure */
85490e0de5SThomas Veerman struct fuse {
86490e0de5SThomas Veerman struct fuse_chan *fc; /* fuse channel pointer */
87490e0de5SThomas Veerman struct fuse_operations op; /* switch table of operations */
88490e0de5SThomas Veerman int compat; /* compat level -
89490e0de5SThomas Veerman * not used in puffs_fuse */
90490e0de5SThomas Veerman struct node **name_table;
91490e0de5SThomas Veerman size_t name_table_size;
92490e0de5SThomas Veerman struct node **id_table;
93490e0de5SThomas Veerman size_t id_table_size;
94490e0de5SThomas Veerman fuse_ino_t ctr;
95490e0de5SThomas Veerman unsigned int generation;
96490e0de5SThomas Veerman unsigned int hidectr;
97*84d9c625SLionel Sambuc #if !defined(__minix)
98490e0de5SThomas Veerman pthread_mutex_t lock;
99490e0de5SThomas Veerman pthread_rwlock_t tree_lock;
100*84d9c625SLionel Sambuc #endif /* !defined(__minix) */
101490e0de5SThomas Veerman void *user_data;
102490e0de5SThomas Veerman struct fuse_config conf;
103490e0de5SThomas Veerman int intr_installed;
104490e0de5SThomas Veerman };
105490e0de5SThomas Veerman
106490e0de5SThomas Veerman struct puffs_fuse_dirh {
107490e0de5SThomas Veerman void *dbuf;
108490e0de5SThomas Veerman struct dirent *d;
109490e0de5SThomas Veerman
110490e0de5SThomas Veerman size_t reslen;
111490e0de5SThomas Veerman size_t bufsize;
112490e0de5SThomas Veerman };
113490e0de5SThomas Veerman
114490e0de5SThomas Veerman struct refusenode {
115490e0de5SThomas Veerman struct fuse_file_info file_info;
116490e0de5SThomas Veerman struct puffs_fuse_dirh dirh;
117490e0de5SThomas Veerman int opencount;
118490e0de5SThomas Veerman int flags;
119490e0de5SThomas Veerman };
120490e0de5SThomas Veerman #define RN_ROOT 0x01
121490e0de5SThomas Veerman #define RN_OPEN 0x02 /* XXX: could just use opencount */
122490e0de5SThomas Veerman
123490e0de5SThomas Veerman static int fuse_setattr(struct fuse *, struct puffs_node *,
124490e0de5SThomas Veerman const char *, const struct vattr *);
125490e0de5SThomas Veerman
126490e0de5SThomas Veerman static struct puffs_node *
newrn(struct puffs_usermount * pu)127490e0de5SThomas Veerman newrn(struct puffs_usermount *pu)
128490e0de5SThomas Veerman {
129490e0de5SThomas Veerman struct puffs_node *pn;
130490e0de5SThomas Veerman struct refusenode *rn;
131490e0de5SThomas Veerman
132490e0de5SThomas Veerman if ((rn = calloc(1, sizeof(*rn))) == NULL) {
133490e0de5SThomas Veerman err(EXIT_FAILURE, "newrn");
134490e0de5SThomas Veerman }
135490e0de5SThomas Veerman pn = puffs_pn_new(pu, rn);
136490e0de5SThomas Veerman
137490e0de5SThomas Veerman return pn;
138490e0de5SThomas Veerman }
139490e0de5SThomas Veerman
140490e0de5SThomas Veerman static void
nukern(struct puffs_node * pn)141490e0de5SThomas Veerman nukern(struct puffs_node *pn)
142490e0de5SThomas Veerman {
143490e0de5SThomas Veerman struct refusenode *rn = pn->pn_data;
144490e0de5SThomas Veerman
145490e0de5SThomas Veerman free(rn->dirh.dbuf);
146490e0de5SThomas Veerman free(rn);
147490e0de5SThomas Veerman puffs_pn_put(pn);
148490e0de5SThomas Veerman }
149490e0de5SThomas Veerman
150490e0de5SThomas Veerman /* XXX - not threadsafe */
151490e0de5SThomas Veerman static ino_t fakeino = 3;
152490e0de5SThomas Veerman
153490e0de5SThomas Veerman /***************** start of pthread context routines ************************/
154490e0de5SThomas Veerman
155490e0de5SThomas Veerman /*
156490e0de5SThomas Veerman * Notes on fuse_context:
157490e0de5SThomas Veerman * we follow fuse's lead and use the pthread specific information to hold
158490e0de5SThomas Veerman * a reference to the fuse_context structure for this thread.
159490e0de5SThomas Veerman */
160490e0de5SThomas Veerman #ifdef MULTITHREADED_REFUSE
161490e0de5SThomas Veerman static pthread_mutex_t context_mutex = PTHREAD_MUTEX_INITIALIZER;
162490e0de5SThomas Veerman static pthread_key_t context_key;
163490e0de5SThomas Veerman static unsigned long context_refc;
164490e0de5SThomas Veerman #endif
165490e0de5SThomas Veerman
166490e0de5SThomas Veerman /* return the fuse_context struct related to this thread */
167490e0de5SThomas Veerman struct fuse_context *
fuse_get_context(void)168490e0de5SThomas Veerman fuse_get_context(void)
169490e0de5SThomas Veerman {
170490e0de5SThomas Veerman #ifdef MULTITHREADED_REFUSE
171490e0de5SThomas Veerman struct fuse_context *ctxt;
172490e0de5SThomas Veerman
173490e0de5SThomas Veerman if ((ctxt = pthread_getspecific(context_key)) == NULL) {
174490e0de5SThomas Veerman if ((ctxt = calloc(1, sizeof(struct fuse_context))) == NULL) {
175490e0de5SThomas Veerman abort();
176490e0de5SThomas Veerman }
177490e0de5SThomas Veerman pthread_setspecific(context_key, ctxt);
178490e0de5SThomas Veerman }
179490e0de5SThomas Veerman return ctxt;
180490e0de5SThomas Veerman #else
181490e0de5SThomas Veerman static struct fuse_context fcon;
182490e0de5SThomas Veerman
183490e0de5SThomas Veerman return &fcon;
184490e0de5SThomas Veerman #endif
185490e0de5SThomas Veerman }
186490e0de5SThomas Veerman
187490e0de5SThomas Veerman /* used as a callback function */
188490e0de5SThomas Veerman #ifdef MULTITHREADED_REFUSE
189490e0de5SThomas Veerman static void
free_context(void * ctxt)190490e0de5SThomas Veerman free_context(void *ctxt)
191490e0de5SThomas Veerman {
192490e0de5SThomas Veerman free(ctxt);
193490e0de5SThomas Veerman }
194490e0de5SThomas Veerman #endif
195490e0de5SThomas Veerman
196490e0de5SThomas Veerman /*
197490e0de5SThomas Veerman * Create the pthread key. The reason for the complexity is to
198490e0de5SThomas Veerman * enable use of multiple fuse instances within a single process.
199490e0de5SThomas Veerman */
200490e0de5SThomas Veerman static int
create_context_key(void)201490e0de5SThomas Veerman create_context_key(void)
202490e0de5SThomas Veerman {
203490e0de5SThomas Veerman #ifdef MULTITHREADED_REFUSE
204490e0de5SThomas Veerman int rv;
205490e0de5SThomas Veerman
206490e0de5SThomas Veerman rv = pthread_mutex_lock(&context_mutex);
207490e0de5SThomas Veerman assert(rv == 0);
208490e0de5SThomas Veerman
209490e0de5SThomas Veerman if (context_refc == 0) {
210490e0de5SThomas Veerman if (pthread_key_create(&context_key, free_context) != 0) {
211490e0de5SThomas Veerman warnx("create_context_key: pthread_key_create failed");
212490e0de5SThomas Veerman pthread_mutex_unlock(&context_mutex);
213490e0de5SThomas Veerman return 0;
214490e0de5SThomas Veerman }
215490e0de5SThomas Veerman }
216490e0de5SThomas Veerman context_refc += 1;
217490e0de5SThomas Veerman pthread_mutex_unlock(&context_mutex);
218490e0de5SThomas Veerman return 1;
219490e0de5SThomas Veerman #else
220490e0de5SThomas Veerman return 1;
221490e0de5SThomas Veerman #endif
222490e0de5SThomas Veerman }
223490e0de5SThomas Veerman
224490e0de5SThomas Veerman static void
delete_context_key(void)225490e0de5SThomas Veerman delete_context_key(void)
226490e0de5SThomas Veerman {
227490e0de5SThomas Veerman #ifdef MULTITHREADED_REFUSE
228490e0de5SThomas Veerman pthread_mutex_lock(&context_mutex);
229490e0de5SThomas Veerman /* If we are the last fuse instances using the key, delete it */
230490e0de5SThomas Veerman if (--context_refc == 0) {
231490e0de5SThomas Veerman free(pthread_getspecific(context_key));
232490e0de5SThomas Veerman pthread_key_delete(context_key);
233490e0de5SThomas Veerman }
234490e0de5SThomas Veerman pthread_mutex_unlock(&context_mutex);
235490e0de5SThomas Veerman #endif
236490e0de5SThomas Veerman }
237490e0de5SThomas Veerman
238490e0de5SThomas Veerman /* set the uid and gid of the calling process in the current fuse context */
239490e0de5SThomas Veerman static void
set_fuse_context_uid_gid(const struct puffs_cred * cred)240490e0de5SThomas Veerman set_fuse_context_uid_gid(const struct puffs_cred *cred)
241490e0de5SThomas Veerman {
242490e0de5SThomas Veerman struct fuse_context *fusectx;
243490e0de5SThomas Veerman uid_t uid;
244490e0de5SThomas Veerman gid_t gid;
245490e0de5SThomas Veerman
246490e0de5SThomas Veerman fusectx = fuse_get_context();
247490e0de5SThomas Veerman if (puffs_cred_getuid(cred, &uid) == 0) {
248490e0de5SThomas Veerman fusectx->uid = uid;
249490e0de5SThomas Veerman }
250490e0de5SThomas Veerman if (puffs_cred_getgid(cred, &gid) == 0) {
251490e0de5SThomas Veerman fusectx->gid = gid;
252490e0de5SThomas Veerman }
253490e0de5SThomas Veerman }
254490e0de5SThomas Veerman
255490e0de5SThomas Veerman /* set the pid of the calling process in the current fuse context */
256490e0de5SThomas Veerman static void
set_fuse_context_pid(struct puffs_usermount * pu)257490e0de5SThomas Veerman set_fuse_context_pid(struct puffs_usermount *pu)
258490e0de5SThomas Veerman {
259490e0de5SThomas Veerman struct puffs_cc *pcc = puffs_cc_getcc(pu);
260490e0de5SThomas Veerman struct fuse_context *fusectx;
261490e0de5SThomas Veerman
262490e0de5SThomas Veerman fusectx = fuse_get_context();
263490e0de5SThomas Veerman puffs_cc_getcaller(pcc, &fusectx->pid, NULL);
264490e0de5SThomas Veerman }
265490e0de5SThomas Veerman
266490e0de5SThomas Veerman /***************** end of pthread context routines ************************/
267490e0de5SThomas Veerman
268490e0de5SThomas Veerman #define DIR_CHUNKSIZE 4096
269490e0de5SThomas Veerman static int
fill_dirbuf(struct puffs_fuse_dirh * dh,const char * name,ino_t dino,uint8_t dtype)270490e0de5SThomas Veerman fill_dirbuf(struct puffs_fuse_dirh *dh, const char *name, ino_t dino,
271490e0de5SThomas Veerman uint8_t dtype)
272490e0de5SThomas Veerman {
273490e0de5SThomas Veerman
274490e0de5SThomas Veerman /* initial? */
275490e0de5SThomas Veerman if (dh->bufsize == 0) {
276490e0de5SThomas Veerman if ((dh->dbuf = calloc(1, DIR_CHUNKSIZE)) == NULL) {
277490e0de5SThomas Veerman abort();
278490e0de5SThomas Veerman }
279490e0de5SThomas Veerman dh->d = dh->dbuf;
280490e0de5SThomas Veerman dh->reslen = dh->bufsize = DIR_CHUNKSIZE;
281490e0de5SThomas Veerman }
282490e0de5SThomas Veerman
283490e0de5SThomas Veerman if (puffs_nextdent(&dh->d, name, dino, dtype, &dh->reslen)) {
284490e0de5SThomas Veerman return 0;
285490e0de5SThomas Veerman }
286490e0de5SThomas Veerman
287490e0de5SThomas Veerman /* try to increase buffer space */
288490e0de5SThomas Veerman dh->dbuf = realloc(dh->dbuf, dh->bufsize + DIR_CHUNKSIZE);
289490e0de5SThomas Veerman if (dh->dbuf == NULL) {
290490e0de5SThomas Veerman abort();
291490e0de5SThomas Veerman }
292490e0de5SThomas Veerman dh->d = (void *)((uint8_t *)dh->dbuf + (dh->bufsize - dh->reslen));
293490e0de5SThomas Veerman dh->reslen += DIR_CHUNKSIZE;
294490e0de5SThomas Veerman dh->bufsize += DIR_CHUNKSIZE;
295490e0de5SThomas Veerman
296490e0de5SThomas Veerman return !puffs_nextdent(&dh->d, name, dino, dtype, &dh->reslen);
297490e0de5SThomas Veerman }
298490e0de5SThomas Veerman
299490e0de5SThomas Veerman /* ARGSUSED3 */
300490e0de5SThomas Veerman /* XXX: I have no idea how "off" is supposed to be used */
301490e0de5SThomas Veerman static int
puffs_fuse_fill_dir(void * buf,const char * name,const struct stat * stbuf,off_t off)302490e0de5SThomas Veerman puffs_fuse_fill_dir(void *buf, const char *name,
303490e0de5SThomas Veerman const struct stat *stbuf, off_t off)
304490e0de5SThomas Veerman {
305490e0de5SThomas Veerman struct puffs_fuse_dirh *deh = buf;
306490e0de5SThomas Veerman ino_t dino;
307490e0de5SThomas Veerman uint8_t dtype;
308490e0de5SThomas Veerman
309490e0de5SThomas Veerman if (stbuf == NULL) {
310490e0de5SThomas Veerman dtype = DT_UNKNOWN;
311490e0de5SThomas Veerman dino = fakeino++;
312490e0de5SThomas Veerman } else {
313490e0de5SThomas Veerman dtype = puffs_vtype2dt(puffs_mode2vt(stbuf->st_mode));
314490e0de5SThomas Veerman dino = stbuf->st_ino;
315490e0de5SThomas Veerman
316490e0de5SThomas Veerman /*
317490e0de5SThomas Veerman * Some FUSE file systems like to always use 0 as the
318490e0de5SThomas Veerman * inode number. Our readdir() doesn't like to show
319490e0de5SThomas Veerman * directory entries with inode number 0 ==> workaround.
320490e0de5SThomas Veerman */
321490e0de5SThomas Veerman if (dino == 0) {
322490e0de5SThomas Veerman dino = fakeino++;
323490e0de5SThomas Veerman }
324490e0de5SThomas Veerman }
325490e0de5SThomas Veerman
326490e0de5SThomas Veerman return fill_dirbuf(deh, name, dino, dtype);
327490e0de5SThomas Veerman }
328490e0de5SThomas Veerman
329490e0de5SThomas Veerman static int
puffs_fuse_dirfil(fuse_dirh_t h,const char * name,int type,ino_t ino)330490e0de5SThomas Veerman puffs_fuse_dirfil(fuse_dirh_t h, const char *name, int type, ino_t ino)
331490e0de5SThomas Veerman {
332490e0de5SThomas Veerman ino_t dino;
333490e0de5SThomas Veerman int dtype;
334490e0de5SThomas Veerman
335490e0de5SThomas Veerman if ((dtype = type) == 0) {
336490e0de5SThomas Veerman dtype = DT_UNKNOWN;
337490e0de5SThomas Veerman }
338490e0de5SThomas Veerman
339490e0de5SThomas Veerman dino = (ino) ? ino : fakeino++;
340490e0de5SThomas Veerman
341490e0de5SThomas Veerman return fill_dirbuf(h, name, dino, dtype);
342490e0de5SThomas Veerman }
343490e0de5SThomas Veerman
344490e0de5SThomas Veerman /* place the refuse file system name into `name' */
345490e0de5SThomas Veerman static void
set_refuse_mount_name(char ** argv,char * name,size_t size)346490e0de5SThomas Veerman set_refuse_mount_name(char **argv, char *name, size_t size)
347490e0de5SThomas Veerman {
348490e0de5SThomas Veerman char *slash;
349490e0de5SThomas Veerman
350490e0de5SThomas Veerman if (argv == NULL || *argv == NULL) {
351490e0de5SThomas Veerman (void) strlcpy(name, "refuse", size);
352490e0de5SThomas Veerman } else {
353490e0de5SThomas Veerman if ((slash = strrchr(*argv, '/')) == NULL) {
354490e0de5SThomas Veerman slash = *argv;
355490e0de5SThomas Veerman } else {
356490e0de5SThomas Veerman slash += 1;
357490e0de5SThomas Veerman }
358490e0de5SThomas Veerman if (strncmp(*argv, "refuse:", 7) == 0) {
359490e0de5SThomas Veerman /* we've already done this */
360490e0de5SThomas Veerman (void) strlcpy(name, *argv, size);
361490e0de5SThomas Veerman } else {
362490e0de5SThomas Veerman (void) snprintf(name, size, "refuse:%s", slash);
363490e0de5SThomas Veerman }
364490e0de5SThomas Veerman }
365490e0de5SThomas Veerman }
366490e0de5SThomas Veerman
367490e0de5SThomas Veerman
368490e0de5SThomas Veerman /* this function exposes struct fuse to userland */
369490e0de5SThomas Veerman static struct fuse *
fuse_setup_real(int argc,char ** argv,const struct fuse_operations * ops,size_t size,char ** mountpoint,int * multithreaded,int * fd,void * user_data)370490e0de5SThomas Veerman fuse_setup_real(int argc, char **argv, const struct fuse_operations *ops,
371490e0de5SThomas Veerman size_t size, char **mountpoint, int *multithreaded, int *fd,
372490e0de5SThomas Veerman void *user_data)
373490e0de5SThomas Veerman {
374490e0de5SThomas Veerman struct fuse_chan *fc;
375490e0de5SThomas Veerman struct fuse_args *args;
376490e0de5SThomas Veerman struct fuse *fuse;
377490e0de5SThomas Veerman char name[64];
378490e0de5SThomas Veerman int i;
379490e0de5SThomas Veerman
380490e0de5SThomas Veerman /* set up the proper name */
381490e0de5SThomas Veerman set_refuse_mount_name(argv, name, sizeof(name));
382490e0de5SThomas Veerman
383490e0de5SThomas Veerman /* grab the pthread context key */
384490e0de5SThomas Veerman if (!create_context_key()) {
385490e0de5SThomas Veerman return NULL;
386490e0de5SThomas Veerman }
387490e0de5SThomas Veerman
388490e0de5SThomas Veerman /* stuff name into fuse_args */
389490e0de5SThomas Veerman args = fuse_opt_deep_copy_args(argc, argv);
390490e0de5SThomas Veerman if (args->argc > 0) {
391490e0de5SThomas Veerman free(args->argv[0]);
392490e0de5SThomas Veerman }
393490e0de5SThomas Veerman if ((args->argv[0] = strdup(name)) == NULL) {
394490e0de5SThomas Veerman fuse_opt_free_args(args);
395490e0de5SThomas Veerman return NULL;
396490e0de5SThomas Veerman }
397490e0de5SThomas Veerman
398490e0de5SThomas Veerman /* count back from the end over arguments starting with '-' */
399490e0de5SThomas Veerman for (i = argc - 1 ; i > 0 && *argv[i] == '-' ; --i) {
400490e0de5SThomas Veerman }
401490e0de5SThomas Veerman
402490e0de5SThomas Veerman fc = fuse_mount(*mountpoint = argv[i], args);
403490e0de5SThomas Veerman fuse = fuse_new(fc, args, ops, size, user_data);
404490e0de5SThomas Veerman
405490e0de5SThomas Veerman fuse_opt_free_args(args);
406490e0de5SThomas Veerman free(args);
407490e0de5SThomas Veerman
408490e0de5SThomas Veerman /* XXX - wait for puffs to become multi-threaded */
409490e0de5SThomas Veerman if (multithreaded) {
410490e0de5SThomas Veerman *multithreaded = 0;
411490e0de5SThomas Veerman }
412490e0de5SThomas Veerman
413490e0de5SThomas Veerman /* XXX - this is unused */
414490e0de5SThomas Veerman if (fd) {
415490e0de5SThomas Veerman *fd = 0;
416490e0de5SThomas Veerman }
417490e0de5SThomas Veerman
418490e0de5SThomas Veerman return fuse;
419490e0de5SThomas Veerman }
420490e0de5SThomas Veerman
421490e0de5SThomas Veerman #ifdef fuse_setup
422490e0de5SThomas Veerman #undef fuse_setup
423490e0de5SThomas Veerman #endif
424490e0de5SThomas Veerman
425490e0de5SThomas Veerman struct fuse *
fuse_setup(int argc,char ** argv,const struct fuse_operations * ops,size_t size,char ** mountpoint,int * multithreaded,int * fd)426490e0de5SThomas Veerman fuse_setup(int argc, char **argv, const struct fuse_operations *ops,
427490e0de5SThomas Veerman size_t size, char **mountpoint, int *multithreaded, int *fd)
428490e0de5SThomas Veerman {
429490e0de5SThomas Veerman return fuse_setup_real(argc, argv, ops, size, mountpoint,
430490e0de5SThomas Veerman multithreaded, fd, NULL);
431490e0de5SThomas Veerman }
432490e0de5SThomas Veerman
433490e0de5SThomas Veerman struct fuse *
fuse_setup26(int argc,char ** argv,const struct fuse_operations * ops,size_t size,char ** mountpoint,int * multithreaded,void * user_data)434490e0de5SThomas Veerman fuse_setup26(int argc, char **argv, const struct fuse_operations *ops,
435490e0de5SThomas Veerman size_t size, char **mountpoint, int *multithreaded, void *user_data)
436490e0de5SThomas Veerman {
437490e0de5SThomas Veerman return fuse_setup_real(argc, argv, ops, size, mountpoint,
438490e0de5SThomas Veerman multithreaded, NULL, user_data);
439490e0de5SThomas Veerman }
440490e0de5SThomas Veerman
441490e0de5SThomas Veerman #define FUSE_ERR_UNLINK(fuse, file) if (fuse->op.unlink) fuse->op.unlink(file)
442490e0de5SThomas Veerman #define FUSE_ERR_RMDIR(fuse, dir) if (fuse->op.rmdir) fuse->op.rmdir(dir)
443490e0de5SThomas Veerman
444490e0de5SThomas Veerman /* ARGSUSED1 */
445490e0de5SThomas Veerman static int
fuse_getattr(struct fuse * fuse,struct puffs_node * pn,const char * path,struct vattr * va)446490e0de5SThomas Veerman fuse_getattr(struct fuse *fuse, struct puffs_node *pn, const char *path,
447490e0de5SThomas Veerman struct vattr *va)
448490e0de5SThomas Veerman {
449490e0de5SThomas Veerman struct stat st;
450490e0de5SThomas Veerman int ret;
451490e0de5SThomas Veerman
452490e0de5SThomas Veerman if (fuse->op.getattr == NULL) {
453490e0de5SThomas Veerman return ENOSYS;
454490e0de5SThomas Veerman }
455490e0de5SThomas Veerman
456490e0de5SThomas Veerman /* wrap up return code */
457490e0de5SThomas Veerman memset(&st, 0, sizeof(st));
458490e0de5SThomas Veerman ret = (*fuse->op.getattr)(path, &st);
459490e0de5SThomas Veerman
460490e0de5SThomas Veerman if (ret == 0) {
461490e0de5SThomas Veerman if (st.st_blksize == 0)
462490e0de5SThomas Veerman st.st_blksize = DEV_BSIZE;
463490e0de5SThomas Veerman puffs_stat2vattr(va, &st);
464490e0de5SThomas Veerman }
465490e0de5SThomas Veerman
466490e0de5SThomas Veerman return -ret;
467490e0de5SThomas Veerman }
468490e0de5SThomas Veerman
469490e0de5SThomas Veerman /* utility function to set various elements of the attribute */
470490e0de5SThomas Veerman static int
fuse_setattr(struct fuse * fuse,struct puffs_node * pn,const char * path,const struct vattr * va)471490e0de5SThomas Veerman fuse_setattr(struct fuse *fuse, struct puffs_node *pn, const char *path,
472490e0de5SThomas Veerman const struct vattr *va)
473490e0de5SThomas Veerman {
474490e0de5SThomas Veerman struct refusenode *rn = pn->pn_data;
475490e0de5SThomas Veerman mode_t mode;
476490e0de5SThomas Veerman uid_t uid;
477490e0de5SThomas Veerman gid_t gid;
478490e0de5SThomas Veerman int error, ret;
479490e0de5SThomas Veerman
480490e0de5SThomas Veerman error = 0;
481490e0de5SThomas Veerman
482490e0de5SThomas Veerman mode = va->va_mode;
483490e0de5SThomas Veerman uid = va->va_uid;
484490e0de5SThomas Veerman gid = va->va_gid;
485490e0de5SThomas Veerman
486490e0de5SThomas Veerman if (mode != (mode_t)PUFFS_VNOVAL) {
487490e0de5SThomas Veerman ret = 0;
488490e0de5SThomas Veerman
489490e0de5SThomas Veerman if (fuse->op.chmod == NULL) {
490490e0de5SThomas Veerman error = -ENOSYS;
491490e0de5SThomas Veerman } else {
492490e0de5SThomas Veerman ret = fuse->op.chmod(path, mode);
493490e0de5SThomas Veerman if (ret)
494490e0de5SThomas Veerman error = ret;
495490e0de5SThomas Veerman }
496490e0de5SThomas Veerman }
497490e0de5SThomas Veerman if (uid != (uid_t)PUFFS_VNOVAL || gid != (gid_t)PUFFS_VNOVAL) {
498490e0de5SThomas Veerman ret = 0;
499490e0de5SThomas Veerman
500490e0de5SThomas Veerman if (fuse->op.chown == NULL) {
501490e0de5SThomas Veerman error = -ENOSYS;
502490e0de5SThomas Veerman } else {
503490e0de5SThomas Veerman ret = fuse->op.chown(path, uid, gid);
504490e0de5SThomas Veerman if (ret)
505490e0de5SThomas Veerman error = ret;
506490e0de5SThomas Veerman }
507490e0de5SThomas Veerman }
508490e0de5SThomas Veerman if (va->va_atime.tv_sec != (time_t)PUFFS_VNOVAL
509490e0de5SThomas Veerman || va->va_mtime.tv_sec != (long)PUFFS_VNOVAL) {
510490e0de5SThomas Veerman ret = 0;
511490e0de5SThomas Veerman
512490e0de5SThomas Veerman if (fuse->op.utimens) {
513490e0de5SThomas Veerman struct timespec tv[2];
514490e0de5SThomas Veerman
515490e0de5SThomas Veerman tv[0].tv_sec = va->va_atime.tv_sec;
516490e0de5SThomas Veerman tv[0].tv_nsec = va->va_atime.tv_nsec;
517490e0de5SThomas Veerman tv[1].tv_sec = va->va_mtime.tv_sec;
518490e0de5SThomas Veerman tv[1].tv_nsec = va->va_mtime.tv_nsec;
519490e0de5SThomas Veerman
520490e0de5SThomas Veerman ret = fuse->op.utimens(path, tv);
521490e0de5SThomas Veerman } else if (fuse->op.utime) {
522490e0de5SThomas Veerman struct utimbuf timbuf;
523490e0de5SThomas Veerman
524490e0de5SThomas Veerman timbuf.actime = va->va_atime.tv_sec;
525490e0de5SThomas Veerman timbuf.modtime = va->va_mtime.tv_sec;
526490e0de5SThomas Veerman
527490e0de5SThomas Veerman ret = fuse->op.utime(path, &timbuf);
528490e0de5SThomas Veerman } else {
529490e0de5SThomas Veerman error = -ENOSYS;
530490e0de5SThomas Veerman }
531490e0de5SThomas Veerman
532490e0de5SThomas Veerman if (ret)
533490e0de5SThomas Veerman error = ret;
534490e0de5SThomas Veerman }
535490e0de5SThomas Veerman if (va->va_size != (u_quad_t)PUFFS_VNOVAL) {
536490e0de5SThomas Veerman ret = 0;
537490e0de5SThomas Veerman
538490e0de5SThomas Veerman if (fuse->op.truncate) {
539490e0de5SThomas Veerman ret = fuse->op.truncate(path, (off_t)va->va_size);
540490e0de5SThomas Veerman } else if (fuse->op.ftruncate) {
541490e0de5SThomas Veerman ret = fuse->op.ftruncate(path, (off_t)va->va_size,
542490e0de5SThomas Veerman &rn->file_info);
543490e0de5SThomas Veerman } else {
544490e0de5SThomas Veerman error = -ENOSYS;
545490e0de5SThomas Veerman }
546490e0de5SThomas Veerman
547490e0de5SThomas Veerman if (ret)
548490e0de5SThomas Veerman error = ret;
549490e0de5SThomas Veerman }
550490e0de5SThomas Veerman /* XXX: no reflection with reality */
551490e0de5SThomas Veerman puffs_setvattr(&pn->pn_va, va);
552490e0de5SThomas Veerman
553490e0de5SThomas Veerman return -error;
554490e0de5SThomas Veerman
555490e0de5SThomas Veerman }
556490e0de5SThomas Veerman
557490e0de5SThomas Veerman static int
fuse_newnode(struct puffs_usermount * pu,const char * path,const struct vattr * va,struct fuse_file_info * fi,struct puffs_newinfo * pni,struct puffs_node ** pn_new)558490e0de5SThomas Veerman fuse_newnode(struct puffs_usermount *pu, const char *path,
559490e0de5SThomas Veerman const struct vattr *va, struct fuse_file_info *fi,
560490e0de5SThomas Veerman struct puffs_newinfo *pni, struct puffs_node **pn_new)
561490e0de5SThomas Veerman {
562490e0de5SThomas Veerman struct puffs_node *pn;
563490e0de5SThomas Veerman struct refusenode *rn;
564490e0de5SThomas Veerman struct vattr newva;
565490e0de5SThomas Veerman struct fuse *fuse;
566490e0de5SThomas Veerman
567490e0de5SThomas Veerman fuse = puffs_getspecific(pu);
568490e0de5SThomas Veerman
569490e0de5SThomas Veerman /* fix up nodes */
570490e0de5SThomas Veerman pn = newrn(pu);
571490e0de5SThomas Veerman if (pn == NULL) {
572490e0de5SThomas Veerman if (va->va_type == VDIR) {
573490e0de5SThomas Veerman FUSE_ERR_RMDIR(fuse, path);
574490e0de5SThomas Veerman } else {
575490e0de5SThomas Veerman FUSE_ERR_UNLINK(fuse, path);
576490e0de5SThomas Veerman }
577490e0de5SThomas Veerman return ENOMEM;
578490e0de5SThomas Veerman }
579490e0de5SThomas Veerman fuse_setattr(fuse, pn, path, va);
580490e0de5SThomas Veerman if (fuse_getattr(fuse, pn, path, &newva) == 0)
581490e0de5SThomas Veerman puffs_setvattr(&pn->pn_va, &newva);
582490e0de5SThomas Veerman
583490e0de5SThomas Veerman rn = pn->pn_data;
584490e0de5SThomas Veerman if (fi)
585490e0de5SThomas Veerman memcpy(&rn->file_info, fi, sizeof(struct fuse_file_info));
586490e0de5SThomas Veerman
587490e0de5SThomas Veerman puffs_newinfo_setcookie(pni, pn);
588490e0de5SThomas Veerman if (pn_new)
589490e0de5SThomas Veerman *pn_new = pn;
590490e0de5SThomas Veerman
591490e0de5SThomas Veerman return 0;
592490e0de5SThomas Veerman }
593490e0de5SThomas Veerman
594490e0de5SThomas Veerman
595490e0de5SThomas Veerman /* operation wrappers start here */
596490e0de5SThomas Veerman
597490e0de5SThomas Veerman /* lookup the path */
598490e0de5SThomas Veerman /* ARGSUSED1 */
599490e0de5SThomas Veerman static int
puffs_fuse_node_lookup(struct puffs_usermount * pu,void * opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn)600490e0de5SThomas Veerman puffs_fuse_node_lookup(struct puffs_usermount *pu, void *opc,
601490e0de5SThomas Veerman struct puffs_newinfo *pni, const struct puffs_cn *pcn)
602490e0de5SThomas Veerman {
603490e0de5SThomas Veerman struct puffs_node *pn_res;
604490e0de5SThomas Veerman struct stat st;
605490e0de5SThomas Veerman struct fuse *fuse;
606490e0de5SThomas Veerman const char *path = PCNPATH(pcn);
607490e0de5SThomas Veerman int ret;
608490e0de5SThomas Veerman
609490e0de5SThomas Veerman fuse = puffs_getspecific(pu);
610490e0de5SThomas Veerman
611490e0de5SThomas Veerman set_fuse_context_uid_gid(pcn->pcn_cred);
612490e0de5SThomas Veerman
613490e0de5SThomas Veerman ret = fuse->op.getattr(path, &st);
614490e0de5SThomas Veerman
615490e0de5SThomas Veerman if (ret != 0) {
616490e0de5SThomas Veerman return -ret;
617490e0de5SThomas Veerman }
618490e0de5SThomas Veerman
619490e0de5SThomas Veerman /* XXX: fiXXXme unconst */
620490e0de5SThomas Veerman pn_res = puffs_pn_nodewalk(pu, puffs_path_walkcmp,
621490e0de5SThomas Veerman __UNCONST(&pcn->pcn_po_full));
622490e0de5SThomas Veerman if (pn_res == NULL) {
623490e0de5SThomas Veerman pn_res = newrn(pu);
624490e0de5SThomas Veerman if (pn_res == NULL)
625490e0de5SThomas Veerman return errno;
626490e0de5SThomas Veerman puffs_stat2vattr(&pn_res->pn_va, &st);
627490e0de5SThomas Veerman }
628490e0de5SThomas Veerman
629490e0de5SThomas Veerman puffs_newinfo_setcookie(pni, pn_res);
630490e0de5SThomas Veerman puffs_newinfo_setvtype(pni, pn_res->pn_va.va_type);
631490e0de5SThomas Veerman puffs_newinfo_setsize(pni, (voff_t)pn_res->pn_va.va_size);
632490e0de5SThomas Veerman puffs_newinfo_setrdev(pni, pn_res->pn_va.va_rdev);
633490e0de5SThomas Veerman
634490e0de5SThomas Veerman return 0;
635490e0de5SThomas Veerman }
636490e0de5SThomas Veerman
637490e0de5SThomas Veerman /* get attributes for the path name */
638490e0de5SThomas Veerman /* ARGSUSED3 */
639490e0de5SThomas Veerman static int
puffs_fuse_node_getattr(struct puffs_usermount * pu,void * opc,struct vattr * va,const struct puffs_cred * pcr)640490e0de5SThomas Veerman puffs_fuse_node_getattr(struct puffs_usermount *pu, void *opc, struct vattr *va,
641490e0de5SThomas Veerman const struct puffs_cred *pcr)
642490e0de5SThomas Veerman {
643490e0de5SThomas Veerman struct puffs_node *pn = opc;
644490e0de5SThomas Veerman struct fuse *fuse;
645490e0de5SThomas Veerman const char *path = PNPATH(pn);
646490e0de5SThomas Veerman
647490e0de5SThomas Veerman fuse = puffs_getspecific(pu);
648490e0de5SThomas Veerman
649490e0de5SThomas Veerman set_fuse_context_uid_gid(pcr);
650490e0de5SThomas Veerman
651490e0de5SThomas Veerman return fuse_getattr(fuse, pn, path, va);
652490e0de5SThomas Veerman }
653490e0de5SThomas Veerman
654490e0de5SThomas Veerman /* read the contents of the symbolic link */
655490e0de5SThomas Veerman /* ARGSUSED2 */
656490e0de5SThomas Veerman static int
puffs_fuse_node_readlink(struct puffs_usermount * pu,void * opc,const struct puffs_cred * cred,char * linkname,size_t * linklen)657490e0de5SThomas Veerman puffs_fuse_node_readlink(struct puffs_usermount *pu, void *opc,
658490e0de5SThomas Veerman const struct puffs_cred *cred, char *linkname, size_t *linklen)
659490e0de5SThomas Veerman {
660490e0de5SThomas Veerman struct puffs_node *pn = opc;
661490e0de5SThomas Veerman struct fuse *fuse;
662490e0de5SThomas Veerman const char *path = PNPATH(pn), *p;
663490e0de5SThomas Veerman int ret;
664490e0de5SThomas Veerman
665490e0de5SThomas Veerman fuse = puffs_getspecific(pu);
666490e0de5SThomas Veerman if (fuse->op.readlink == NULL) {
667490e0de5SThomas Veerman return ENOSYS;
668490e0de5SThomas Veerman }
669490e0de5SThomas Veerman
670490e0de5SThomas Veerman set_fuse_context_uid_gid(cred);
671490e0de5SThomas Veerman
672490e0de5SThomas Veerman /* wrap up return code */
673490e0de5SThomas Veerman ret = (*fuse->op.readlink)(path, linkname, *linklen);
674490e0de5SThomas Veerman
675490e0de5SThomas Veerman if (ret == 0) {
676490e0de5SThomas Veerman p = memchr(linkname, '\0', *linklen);
677490e0de5SThomas Veerman if (!p)
678490e0de5SThomas Veerman return EINVAL;
679490e0de5SThomas Veerman
680490e0de5SThomas Veerman *linklen = p - linkname;
681490e0de5SThomas Veerman }
682490e0de5SThomas Veerman
683490e0de5SThomas Veerman return -ret;
684490e0de5SThomas Veerman }
685490e0de5SThomas Veerman
686490e0de5SThomas Veerman /* make the special node */
687490e0de5SThomas Veerman /* ARGSUSED1 */
688490e0de5SThomas Veerman static int
puffs_fuse_node_mknod(struct puffs_usermount * pu,void * opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn,const struct vattr * va)689490e0de5SThomas Veerman puffs_fuse_node_mknod(struct puffs_usermount *pu, void *opc,
690490e0de5SThomas Veerman struct puffs_newinfo *pni, const struct puffs_cn *pcn,
691490e0de5SThomas Veerman const struct vattr *va)
692490e0de5SThomas Veerman {
693490e0de5SThomas Veerman struct fuse *fuse;
694490e0de5SThomas Veerman mode_t mode;
695490e0de5SThomas Veerman const char *path = PCNPATH(pcn);
696490e0de5SThomas Veerman int ret;
697490e0de5SThomas Veerman
698490e0de5SThomas Veerman fuse = puffs_getspecific(pu);
699490e0de5SThomas Veerman if (fuse->op.mknod == NULL) {
700490e0de5SThomas Veerman return ENOSYS;
701490e0de5SThomas Veerman }
702490e0de5SThomas Veerman
703490e0de5SThomas Veerman set_fuse_context_uid_gid(pcn->pcn_cred);
704490e0de5SThomas Veerman
705490e0de5SThomas Veerman /* wrap up return code */
706490e0de5SThomas Veerman mode = puffs_addvtype2mode(va->va_mode, va->va_type);
707490e0de5SThomas Veerman ret = (*fuse->op.mknod)(path, mode, va->va_rdev);
708490e0de5SThomas Veerman
709490e0de5SThomas Veerman if (ret == 0) {
710490e0de5SThomas Veerman ret = fuse_newnode(pu, path, va, NULL, pni, NULL);
711490e0de5SThomas Veerman }
712490e0de5SThomas Veerman
713490e0de5SThomas Veerman return -ret;
714490e0de5SThomas Veerman }
715490e0de5SThomas Veerman
716490e0de5SThomas Veerman /* make a directory */
717490e0de5SThomas Veerman /* ARGSUSED1 */
718490e0de5SThomas Veerman static int
puffs_fuse_node_mkdir(struct puffs_usermount * pu,void * opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn,const struct vattr * va)719490e0de5SThomas Veerman puffs_fuse_node_mkdir(struct puffs_usermount *pu, void *opc,
720490e0de5SThomas Veerman struct puffs_newinfo *pni, const struct puffs_cn *pcn,
721490e0de5SThomas Veerman const struct vattr *va)
722490e0de5SThomas Veerman {
723490e0de5SThomas Veerman struct fuse *fuse;
724490e0de5SThomas Veerman mode_t mode = va->va_mode;
725490e0de5SThomas Veerman const char *path = PCNPATH(pcn);
726490e0de5SThomas Veerman int ret;
727490e0de5SThomas Veerman
728490e0de5SThomas Veerman fuse = puffs_getspecific(pu);
729490e0de5SThomas Veerman
730490e0de5SThomas Veerman set_fuse_context_uid_gid(pcn->pcn_cred);
731490e0de5SThomas Veerman
732490e0de5SThomas Veerman if (fuse->op.mkdir == NULL) {
733490e0de5SThomas Veerman return ENOSYS;
734490e0de5SThomas Veerman }
735490e0de5SThomas Veerman
736490e0de5SThomas Veerman /* wrap up return code */
737490e0de5SThomas Veerman ret = (*fuse->op.mkdir)(path, mode);
738490e0de5SThomas Veerman
739490e0de5SThomas Veerman if (ret == 0) {
740490e0de5SThomas Veerman ret = fuse_newnode(pu, path, va, NULL, pni, NULL);
741490e0de5SThomas Veerman }
742490e0de5SThomas Veerman
743490e0de5SThomas Veerman return -ret;
744490e0de5SThomas Veerman }
745490e0de5SThomas Veerman
746490e0de5SThomas Veerman /*
747490e0de5SThomas Veerman * create a regular file
748490e0de5SThomas Veerman *
749490e0de5SThomas Veerman * since linux/fuse sports using mknod for creating regular files
750490e0de5SThomas Veerman * instead of having a separate call for it in some versions, if
751490e0de5SThomas Veerman * we don't have create, just jump to op->mknod.
752490e0de5SThomas Veerman */
753490e0de5SThomas Veerman /*ARGSUSED1*/
754490e0de5SThomas Veerman static int
puffs_fuse_node_create(struct puffs_usermount * pu,void * opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn,const struct vattr * va)755490e0de5SThomas Veerman puffs_fuse_node_create(struct puffs_usermount *pu, void *opc,
756490e0de5SThomas Veerman struct puffs_newinfo *pni, const struct puffs_cn *pcn,
757490e0de5SThomas Veerman const struct vattr *va)
758490e0de5SThomas Veerman {
759490e0de5SThomas Veerman struct fuse *fuse;
760490e0de5SThomas Veerman struct fuse_file_info fi;
761490e0de5SThomas Veerman struct puffs_node *pn;
762490e0de5SThomas Veerman mode_t mode = va->va_mode;
763490e0de5SThomas Veerman const char *path = PCNPATH(pcn);
764490e0de5SThomas Veerman int ret, created;
765490e0de5SThomas Veerman
766490e0de5SThomas Veerman fuse = puffs_getspecific(pu);
767490e0de5SThomas Veerman
768490e0de5SThomas Veerman set_fuse_context_uid_gid(pcn->pcn_cred);
769490e0de5SThomas Veerman
770490e0de5SThomas Veerman created = 0;
771490e0de5SThomas Veerman if (fuse->op.create) {
772490e0de5SThomas Veerman ret = fuse->op.create(path, mode | S_IFREG, &fi);
773490e0de5SThomas Veerman if (ret == 0)
774490e0de5SThomas Veerman created = 1;
775490e0de5SThomas Veerman
776490e0de5SThomas Veerman } else if (fuse->op.mknod) {
777490e0de5SThomas Veerman ret = fuse->op.mknod(path, mode | S_IFREG, 0);
778490e0de5SThomas Veerman
779490e0de5SThomas Veerman } else {
780490e0de5SThomas Veerman ret = -ENOSYS;
781490e0de5SThomas Veerman }
782490e0de5SThomas Veerman
783490e0de5SThomas Veerman if (ret == 0) {
784490e0de5SThomas Veerman ret = fuse_newnode(pu, path, va, &fi, pni, &pn);
785490e0de5SThomas Veerman
786490e0de5SThomas Veerman /* sweet.. create also open the file */
787490e0de5SThomas Veerman if (created) {
788490e0de5SThomas Veerman struct refusenode *rn;
789490e0de5SThomas Veerman
790490e0de5SThomas Veerman rn = pn->pn_data;
791490e0de5SThomas Veerman rn->flags |= RN_OPEN;
792490e0de5SThomas Veerman rn->opencount++;
793490e0de5SThomas Veerman }
794490e0de5SThomas Veerman }
795490e0de5SThomas Veerman
796490e0de5SThomas Veerman return -ret;
797490e0de5SThomas Veerman }
798490e0de5SThomas Veerman
799490e0de5SThomas Veerman /* remove the directory entry */
800490e0de5SThomas Veerman /* ARGSUSED1 */
801490e0de5SThomas Veerman static int
puffs_fuse_node_remove(struct puffs_usermount * pu,void * opc,void * targ,const struct puffs_cn * pcn)802490e0de5SThomas Veerman puffs_fuse_node_remove(struct puffs_usermount *pu, void *opc, void *targ,
803490e0de5SThomas Veerman const struct puffs_cn *pcn)
804490e0de5SThomas Veerman {
805490e0de5SThomas Veerman struct puffs_node *pn_targ = targ;
806490e0de5SThomas Veerman struct fuse *fuse;
807490e0de5SThomas Veerman const char *path = PNPATH(pn_targ);
808490e0de5SThomas Veerman int ret;
809490e0de5SThomas Veerman
810490e0de5SThomas Veerman fuse = puffs_getspecific(pu);
811490e0de5SThomas Veerman
812490e0de5SThomas Veerman set_fuse_context_uid_gid(pcn->pcn_cred);
813490e0de5SThomas Veerman
814490e0de5SThomas Veerman if (fuse->op.unlink == NULL) {
815490e0de5SThomas Veerman return ENOSYS;
816490e0de5SThomas Veerman }
817490e0de5SThomas Veerman
818490e0de5SThomas Veerman /* wrap up return code */
819490e0de5SThomas Veerman ret = (*fuse->op.unlink)(path);
820490e0de5SThomas Veerman
821490e0de5SThomas Veerman return -ret;
822490e0de5SThomas Veerman }
823490e0de5SThomas Veerman
824490e0de5SThomas Veerman /* remove the directory */
825490e0de5SThomas Veerman /* ARGSUSED1 */
826490e0de5SThomas Veerman static int
puffs_fuse_node_rmdir(struct puffs_usermount * pu,void * opc,void * targ,const struct puffs_cn * pcn)827490e0de5SThomas Veerman puffs_fuse_node_rmdir(struct puffs_usermount *pu, void *opc, void *targ,
828490e0de5SThomas Veerman const struct puffs_cn *pcn)
829490e0de5SThomas Veerman {
830490e0de5SThomas Veerman struct puffs_node *pn_targ = targ;
831490e0de5SThomas Veerman struct fuse *fuse;
832490e0de5SThomas Veerman const char *path = PNPATH(pn_targ);
833490e0de5SThomas Veerman int ret;
834490e0de5SThomas Veerman
835490e0de5SThomas Veerman fuse = puffs_getspecific(pu);
836490e0de5SThomas Veerman
837490e0de5SThomas Veerman set_fuse_context_uid_gid(pcn->pcn_cred);
838490e0de5SThomas Veerman
839490e0de5SThomas Veerman if (fuse->op.rmdir == NULL) {
840490e0de5SThomas Veerman return ENOSYS;
841490e0de5SThomas Veerman }
842490e0de5SThomas Veerman
843490e0de5SThomas Veerman /* wrap up return code */
844490e0de5SThomas Veerman ret = (*fuse->op.rmdir)(path);
845490e0de5SThomas Veerman
846490e0de5SThomas Veerman return -ret;
847490e0de5SThomas Veerman }
848490e0de5SThomas Veerman
849490e0de5SThomas Veerman /* create a symbolic link */
850490e0de5SThomas Veerman /* ARGSUSED1 */
851490e0de5SThomas Veerman static int
puffs_fuse_node_symlink(struct puffs_usermount * pu,void * opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn_src,const struct vattr * va,const char * link_target)852490e0de5SThomas Veerman puffs_fuse_node_symlink(struct puffs_usermount *pu, void *opc,
853490e0de5SThomas Veerman struct puffs_newinfo *pni, const struct puffs_cn *pcn_src,
854490e0de5SThomas Veerman const struct vattr *va, const char *link_target)
855490e0de5SThomas Veerman {
856490e0de5SThomas Veerman struct fuse *fuse;
857490e0de5SThomas Veerman const char *path = PCNPATH(pcn_src);
858490e0de5SThomas Veerman int ret;
859490e0de5SThomas Veerman
860490e0de5SThomas Veerman fuse = puffs_getspecific(pu);
861490e0de5SThomas Veerman
862490e0de5SThomas Veerman set_fuse_context_uid_gid(pcn_src->pcn_cred);
863490e0de5SThomas Veerman
864490e0de5SThomas Veerman if (fuse->op.symlink == NULL) {
865490e0de5SThomas Veerman return ENOSYS;
866490e0de5SThomas Veerman }
867490e0de5SThomas Veerman
868490e0de5SThomas Veerman /* wrap up return code */
869490e0de5SThomas Veerman ret = fuse->op.symlink(link_target, path);
870490e0de5SThomas Veerman
871490e0de5SThomas Veerman if (ret == 0) {
872490e0de5SThomas Veerman ret = fuse_newnode(pu, path, va, NULL, pni, NULL);
873490e0de5SThomas Veerman }
874490e0de5SThomas Veerman
875490e0de5SThomas Veerman return -ret;
876490e0de5SThomas Veerman }
877490e0de5SThomas Veerman
878490e0de5SThomas Veerman /* rename a directory entry */
879490e0de5SThomas Veerman /* ARGSUSED1 */
880490e0de5SThomas Veerman static int
puffs_fuse_node_rename(struct puffs_usermount * pu,void * opc,void * src,const struct puffs_cn * pcn_src,void * targ_dir,void * targ,const struct puffs_cn * pcn_targ)881490e0de5SThomas Veerman puffs_fuse_node_rename(struct puffs_usermount *pu, void *opc, void *src,
882490e0de5SThomas Veerman const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
883490e0de5SThomas Veerman const struct puffs_cn *pcn_targ)
884490e0de5SThomas Veerman {
885490e0de5SThomas Veerman struct fuse *fuse;
886490e0de5SThomas Veerman const char *path_src = PCNPATH(pcn_src);
887490e0de5SThomas Veerman const char *path_dest = PCNPATH(pcn_targ);
888490e0de5SThomas Veerman int ret;
889490e0de5SThomas Veerman
890490e0de5SThomas Veerman fuse = puffs_getspecific(pu);
891490e0de5SThomas Veerman
892490e0de5SThomas Veerman set_fuse_context_uid_gid(pcn_targ->pcn_cred);
893490e0de5SThomas Veerman
894490e0de5SThomas Veerman if (fuse->op.rename == NULL) {
895490e0de5SThomas Veerman return ENOSYS;
896490e0de5SThomas Veerman }
897490e0de5SThomas Veerman
898490e0de5SThomas Veerman ret = fuse->op.rename(path_src, path_dest);
899490e0de5SThomas Veerman
900490e0de5SThomas Veerman if (ret == 0) {
901490e0de5SThomas Veerman }
902490e0de5SThomas Veerman
903490e0de5SThomas Veerman return -ret;
904490e0de5SThomas Veerman }
905490e0de5SThomas Veerman
906490e0de5SThomas Veerman /* create a link in the file system */
907490e0de5SThomas Veerman /* ARGSUSED1 */
908490e0de5SThomas Veerman static int
puffs_fuse_node_link(struct puffs_usermount * pu,void * opc,void * targ,const struct puffs_cn * pcn)909490e0de5SThomas Veerman puffs_fuse_node_link(struct puffs_usermount *pu, void *opc, void *targ,
910490e0de5SThomas Veerman const struct puffs_cn *pcn)
911490e0de5SThomas Veerman {
912490e0de5SThomas Veerman struct puffs_node *pn = targ;
913490e0de5SThomas Veerman struct fuse *fuse;
914490e0de5SThomas Veerman int ret;
915490e0de5SThomas Veerman
916490e0de5SThomas Veerman fuse = puffs_getspecific(pu);
917490e0de5SThomas Veerman
918490e0de5SThomas Veerman set_fuse_context_uid_gid(pcn->pcn_cred);
919490e0de5SThomas Veerman
920490e0de5SThomas Veerman if (fuse->op.link == NULL) {
921490e0de5SThomas Veerman return ENOSYS;
922490e0de5SThomas Veerman }
923490e0de5SThomas Veerman
924490e0de5SThomas Veerman /* wrap up return code */
925490e0de5SThomas Veerman ret = (*fuse->op.link)(PNPATH(pn), PCNPATH(pcn));
926490e0de5SThomas Veerman
927490e0de5SThomas Veerman return -ret;
928490e0de5SThomas Veerman }
929490e0de5SThomas Veerman
930490e0de5SThomas Veerman /*
931490e0de5SThomas Veerman * fuse's regular interface provides chmod(), chown(), utimes()
932490e0de5SThomas Veerman * and truncate() + some variations, so try to fit the square block
933490e0de5SThomas Veerman * in the circle hole and the circle block .... something like that
934490e0de5SThomas Veerman */
935490e0de5SThomas Veerman /* ARGSUSED3 */
936490e0de5SThomas Veerman static int
puffs_fuse_node_setattr(struct puffs_usermount * pu,void * opc,const struct vattr * va,const struct puffs_cred * pcr)937490e0de5SThomas Veerman puffs_fuse_node_setattr(struct puffs_usermount *pu, void *opc,
938490e0de5SThomas Veerman const struct vattr *va, const struct puffs_cred *pcr)
939490e0de5SThomas Veerman {
940490e0de5SThomas Veerman struct puffs_node *pn = opc;
941490e0de5SThomas Veerman struct fuse *fuse;
942490e0de5SThomas Veerman const char *path = PNPATH(pn);
943490e0de5SThomas Veerman
944490e0de5SThomas Veerman fuse = puffs_getspecific(pu);
945490e0de5SThomas Veerman
946490e0de5SThomas Veerman set_fuse_context_uid_gid(pcr);
947490e0de5SThomas Veerman
948490e0de5SThomas Veerman return fuse_setattr(fuse, pn, path, va);
949490e0de5SThomas Veerman }
950490e0de5SThomas Veerman
951490e0de5SThomas Veerman /* ARGSUSED2 */
952490e0de5SThomas Veerman static int
puffs_fuse_node_open(struct puffs_usermount * pu,void * opc,int mode,const struct puffs_cred * cred)953490e0de5SThomas Veerman puffs_fuse_node_open(struct puffs_usermount *pu, void *opc, int mode,
954490e0de5SThomas Veerman const struct puffs_cred *cred)
955490e0de5SThomas Veerman {
956490e0de5SThomas Veerman struct puffs_node *pn = opc;
957490e0de5SThomas Veerman struct refusenode *rn = pn->pn_data;
958490e0de5SThomas Veerman struct fuse_file_info *fi = &rn->file_info;
959490e0de5SThomas Veerman struct fuse *fuse;
960490e0de5SThomas Veerman const char *path = PNPATH(pn);
961490e0de5SThomas Veerman
962490e0de5SThomas Veerman fuse = puffs_getspecific(pu);
963490e0de5SThomas Veerman
964490e0de5SThomas Veerman set_fuse_context_uid_gid(cred);
965490e0de5SThomas Veerman
966490e0de5SThomas Veerman /* if open, don't open again, lest risk nuking file private info */
967490e0de5SThomas Veerman if (rn->flags & RN_OPEN) {
968490e0de5SThomas Veerman rn->opencount++;
969490e0de5SThomas Veerman return 0;
970490e0de5SThomas Veerman }
971490e0de5SThomas Veerman
972490e0de5SThomas Veerman /* OFLAGS(), need to convert FREAD/FWRITE to O_RD/WR */
973490e0de5SThomas Veerman fi->flags = (mode & ~(O_CREAT | O_EXCL | O_TRUNC)) - 1;
974490e0de5SThomas Veerman
975490e0de5SThomas Veerman if (pn->pn_va.va_type == VDIR) {
976490e0de5SThomas Veerman if (fuse->op.opendir)
977490e0de5SThomas Veerman fuse->op.opendir(path, fi);
978490e0de5SThomas Veerman } else {
979490e0de5SThomas Veerman if (fuse->op.open)
980490e0de5SThomas Veerman fuse->op.open(path, fi);
981490e0de5SThomas Veerman }
982490e0de5SThomas Veerman
983490e0de5SThomas Veerman rn->flags |= RN_OPEN;
984490e0de5SThomas Veerman rn->opencount++;
985490e0de5SThomas Veerman
986490e0de5SThomas Veerman return 0;
987490e0de5SThomas Veerman }
988490e0de5SThomas Veerman
989490e0de5SThomas Veerman /* ARGSUSED2 */
990490e0de5SThomas Veerman static int
puffs_fuse_node_close(struct puffs_usermount * pu,void * opc,int fflag,const struct puffs_cred * pcr)991490e0de5SThomas Veerman puffs_fuse_node_close(struct puffs_usermount *pu, void *opc, int fflag,
992490e0de5SThomas Veerman const struct puffs_cred *pcr)
993490e0de5SThomas Veerman {
994490e0de5SThomas Veerman struct puffs_node *pn = opc;
995490e0de5SThomas Veerman struct refusenode *rn = pn->pn_data;
996490e0de5SThomas Veerman struct fuse *fuse;
997490e0de5SThomas Veerman struct fuse_file_info *fi;
998490e0de5SThomas Veerman const char *path = PNPATH(pn);
999490e0de5SThomas Veerman int ret;
1000490e0de5SThomas Veerman
1001490e0de5SThomas Veerman fuse = puffs_getspecific(pu);
1002490e0de5SThomas Veerman fi = &rn->file_info;
1003490e0de5SThomas Veerman ret = 0;
1004490e0de5SThomas Veerman
1005490e0de5SThomas Veerman set_fuse_context_uid_gid(pcr);
1006490e0de5SThomas Veerman
1007490e0de5SThomas Veerman if (rn->flags & RN_OPEN) {
1008490e0de5SThomas Veerman if (pn->pn_va.va_type == VDIR) {
1009490e0de5SThomas Veerman if (fuse->op.releasedir)
1010490e0de5SThomas Veerman ret = fuse->op.releasedir(path, fi);
1011490e0de5SThomas Veerman } else {
1012490e0de5SThomas Veerman if (fuse->op.release)
1013490e0de5SThomas Veerman ret = fuse->op.release(path, fi);
1014490e0de5SThomas Veerman }
1015490e0de5SThomas Veerman }
1016490e0de5SThomas Veerman rn->flags &= ~RN_OPEN;
1017490e0de5SThomas Veerman rn->opencount--;
1018490e0de5SThomas Veerman
1019490e0de5SThomas Veerman return ret;
1020490e0de5SThomas Veerman }
1021490e0de5SThomas Veerman
1022490e0de5SThomas Veerman /* read some more from the file */
1023490e0de5SThomas Veerman /* ARGSUSED5 */
1024490e0de5SThomas Veerman static int
puffs_fuse_node_read(struct puffs_usermount * pu,void * opc,uint8_t * buf,off_t offset,size_t * resid,const struct puffs_cred * pcr,int ioflag)1025490e0de5SThomas Veerman puffs_fuse_node_read(struct puffs_usermount *pu, void *opc, uint8_t *buf,
1026490e0de5SThomas Veerman off_t offset, size_t *resid, const struct puffs_cred *pcr,
1027490e0de5SThomas Veerman int ioflag)
1028490e0de5SThomas Veerman {
1029490e0de5SThomas Veerman struct puffs_node *pn = opc;
1030490e0de5SThomas Veerman struct refusenode *rn = pn->pn_data;
1031490e0de5SThomas Veerman struct fuse *fuse;
1032490e0de5SThomas Veerman const char *path = PNPATH(pn);
1033490e0de5SThomas Veerman size_t maxread;
1034490e0de5SThomas Veerman int ret;
1035490e0de5SThomas Veerman
1036490e0de5SThomas Veerman fuse = puffs_getspecific(pu);
1037490e0de5SThomas Veerman if (fuse->op.read == NULL) {
1038490e0de5SThomas Veerman return ENOSYS;
1039490e0de5SThomas Veerman }
1040490e0de5SThomas Veerman
1041490e0de5SThomas Veerman set_fuse_context_uid_gid(pcr);
1042490e0de5SThomas Veerman
1043490e0de5SThomas Veerman maxread = *resid;
1044490e0de5SThomas Veerman if (maxread > pn->pn_va.va_size - offset) {
1045490e0de5SThomas Veerman /*LINTED*/
1046490e0de5SThomas Veerman maxread = pn->pn_va.va_size - offset;
1047490e0de5SThomas Veerman }
1048490e0de5SThomas Veerman if (maxread == 0)
1049490e0de5SThomas Veerman return 0;
1050490e0de5SThomas Veerman
1051490e0de5SThomas Veerman ret = (*fuse->op.read)(path, (char *)buf, maxread, offset,
1052490e0de5SThomas Veerman &rn->file_info);
1053490e0de5SThomas Veerman
1054490e0de5SThomas Veerman if (ret > 0) {
1055490e0de5SThomas Veerman *resid -= ret;
1056490e0de5SThomas Veerman ret = 0;
1057490e0de5SThomas Veerman }
1058490e0de5SThomas Veerman
1059490e0de5SThomas Veerman return -ret;
1060490e0de5SThomas Veerman }
1061490e0de5SThomas Veerman
1062490e0de5SThomas Veerman /* write to the file */
1063490e0de5SThomas Veerman /* ARGSUSED0 */
1064490e0de5SThomas Veerman static int
puffs_fuse_node_write(struct puffs_usermount * pu,void * opc,uint8_t * buf,off_t offset,size_t * resid,const struct puffs_cred * pcr,int ioflag)1065490e0de5SThomas Veerman puffs_fuse_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf,
1066490e0de5SThomas Veerman off_t offset, size_t *resid, const struct puffs_cred *pcr,
1067490e0de5SThomas Veerman int ioflag)
1068490e0de5SThomas Veerman {
1069490e0de5SThomas Veerman struct puffs_node *pn = opc;
1070490e0de5SThomas Veerman struct refusenode *rn = pn->pn_data;
1071490e0de5SThomas Veerman struct fuse *fuse;
1072490e0de5SThomas Veerman const char *path = PNPATH(pn);
1073490e0de5SThomas Veerman int ret;
1074490e0de5SThomas Veerman
1075490e0de5SThomas Veerman fuse = puffs_getspecific(pu);
1076490e0de5SThomas Veerman if (fuse->op.write == NULL) {
1077490e0de5SThomas Veerman return ENOSYS;
1078490e0de5SThomas Veerman }
1079490e0de5SThomas Veerman
1080490e0de5SThomas Veerman set_fuse_context_uid_gid(pcr);
1081490e0de5SThomas Veerman
1082490e0de5SThomas Veerman if (ioflag & PUFFS_IO_APPEND)
1083490e0de5SThomas Veerman offset = pn->pn_va.va_size;
1084490e0de5SThomas Veerman
1085490e0de5SThomas Veerman ret = (*fuse->op.write)(path, (char *)buf, *resid, offset,
1086490e0de5SThomas Veerman &rn->file_info);
1087490e0de5SThomas Veerman
1088*84d9c625SLionel Sambuc if (ret >= 0) {
1089490e0de5SThomas Veerman if ((uint64_t)(offset + ret) > pn->pn_va.va_size)
1090490e0de5SThomas Veerman pn->pn_va.va_size = offset + ret;
1091490e0de5SThomas Veerman *resid -= ret;
1092*84d9c625SLionel Sambuc ret = (*resid == 0) ? 0 : ENOSPC;
1093*84d9c625SLionel Sambuc } else {
1094*84d9c625SLionel Sambuc ret = -ret;
1095490e0de5SThomas Veerman }
1096490e0de5SThomas Veerman
1097*84d9c625SLionel Sambuc return ret;
1098490e0de5SThomas Veerman }
1099490e0de5SThomas Veerman
1100490e0de5SThomas Veerman
1101490e0de5SThomas Veerman /* ARGSUSED3 */
1102490e0de5SThomas Veerman static int
puffs_fuse_node_readdir(struct puffs_usermount * pu,void * opc,struct dirent * dent,off_t * readoff,size_t * reslen,const struct puffs_cred * pcr,int * eofflag,off_t * cookies,size_t * ncookies)1103490e0de5SThomas Veerman puffs_fuse_node_readdir(struct puffs_usermount *pu, void *opc,
1104490e0de5SThomas Veerman struct dirent *dent, off_t *readoff, size_t *reslen,
1105490e0de5SThomas Veerman const struct puffs_cred *pcr, int *eofflag,
1106490e0de5SThomas Veerman off_t *cookies, size_t *ncookies)
1107490e0de5SThomas Veerman {
1108490e0de5SThomas Veerman struct puffs_node *pn = opc;
1109490e0de5SThomas Veerman struct refusenode *rn = pn->pn_data;
1110490e0de5SThomas Veerman struct puffs_fuse_dirh *dirh;
1111490e0de5SThomas Veerman struct fuse *fuse;
1112490e0de5SThomas Veerman struct dirent *fromdent;
1113490e0de5SThomas Veerman const char *path = PNPATH(pn);
1114490e0de5SThomas Veerman int ret;
1115490e0de5SThomas Veerman
1116490e0de5SThomas Veerman fuse = puffs_getspecific(pu);
1117490e0de5SThomas Veerman if (fuse->op.readdir == NULL && fuse->op.getdir == NULL) {
1118490e0de5SThomas Veerman return ENOSYS;
1119490e0de5SThomas Veerman }
1120490e0de5SThomas Veerman
1121490e0de5SThomas Veerman set_fuse_context_uid_gid(pcr);
1122490e0de5SThomas Veerman
1123490e0de5SThomas Veerman if (pn->pn_va.va_type != VDIR)
1124490e0de5SThomas Veerman return ENOTDIR;
1125490e0de5SThomas Veerman
1126490e0de5SThomas Veerman dirh = &rn->dirh;
1127490e0de5SThomas Veerman
1128490e0de5SThomas Veerman /*
1129490e0de5SThomas Veerman * if we are starting from the beginning, slurp entire directory
1130490e0de5SThomas Veerman * into our buffers
1131490e0de5SThomas Veerman */
1132490e0de5SThomas Veerman if (*readoff == 0) {
1133490e0de5SThomas Veerman /* free old buffers */
1134490e0de5SThomas Veerman free(dirh->dbuf);
1135490e0de5SThomas Veerman memset(dirh, 0, sizeof(struct puffs_fuse_dirh));
1136490e0de5SThomas Veerman
1137490e0de5SThomas Veerman if (fuse->op.readdir)
1138490e0de5SThomas Veerman ret = fuse->op.readdir(path, dirh, puffs_fuse_fill_dir,
1139490e0de5SThomas Veerman 0, &rn->file_info);
1140490e0de5SThomas Veerman else
1141490e0de5SThomas Veerman ret = fuse->op.getdir(path, dirh, puffs_fuse_dirfil);
1142490e0de5SThomas Veerman if (ret)
1143490e0de5SThomas Veerman return -ret;
1144490e0de5SThomas Veerman }
1145490e0de5SThomas Veerman
1146490e0de5SThomas Veerman /* Both op.readdir and op.getdir read full directory */
1147490e0de5SThomas Veerman *eofflag = 1;
1148490e0de5SThomas Veerman
1149490e0de5SThomas Veerman /* now, stuff results into the kernel buffers */
1150490e0de5SThomas Veerman while (*readoff < (off_t)(dirh->bufsize - dirh->reslen)) {
1151490e0de5SThomas Veerman /*LINTED*/
1152490e0de5SThomas Veerman fromdent = (struct dirent *)((uint8_t *)dirh->dbuf + *readoff);
1153490e0de5SThomas Veerman
1154490e0de5SThomas Veerman if (*reslen < _DIRENT_SIZE(fromdent))
1155490e0de5SThomas Veerman break;
1156490e0de5SThomas Veerman
1157490e0de5SThomas Veerman memcpy(dent, fromdent, _DIRENT_SIZE(fromdent));
1158490e0de5SThomas Veerman *readoff += _DIRENT_SIZE(fromdent);
1159490e0de5SThomas Veerman *reslen -= _DIRENT_SIZE(fromdent);
1160490e0de5SThomas Veerman
1161490e0de5SThomas Veerman dent = _DIRENT_NEXT(dent);
1162490e0de5SThomas Veerman }
1163490e0de5SThomas Veerman
1164490e0de5SThomas Veerman return 0;
1165490e0de5SThomas Veerman }
1166490e0de5SThomas Veerman
1167490e0de5SThomas Veerman /* ARGSUSED */
1168490e0de5SThomas Veerman static int
puffs_fuse_node_reclaim(struct puffs_usermount * pu,void * opc)1169490e0de5SThomas Veerman puffs_fuse_node_reclaim(struct puffs_usermount *pu, void *opc)
1170490e0de5SThomas Veerman {
1171490e0de5SThomas Veerman struct puffs_node *pn = opc;
1172490e0de5SThomas Veerman
1173490e0de5SThomas Veerman nukern(pn);
1174490e0de5SThomas Veerman return 0;
1175490e0de5SThomas Veerman }
1176490e0de5SThomas Veerman
1177490e0de5SThomas Veerman /* ARGSUSED1 */
1178490e0de5SThomas Veerman static int
puffs_fuse_fs_unmount(struct puffs_usermount * pu,int flags)1179490e0de5SThomas Veerman puffs_fuse_fs_unmount(struct puffs_usermount *pu, int flags)
1180490e0de5SThomas Veerman {
1181490e0de5SThomas Veerman struct fuse *fuse;
1182490e0de5SThomas Veerman
1183490e0de5SThomas Veerman fuse = puffs_getspecific(pu);
1184490e0de5SThomas Veerman if (fuse->op.destroy == NULL) {
1185490e0de5SThomas Veerman return 0;
1186490e0de5SThomas Veerman }
1187490e0de5SThomas Veerman (*fuse->op.destroy)(fuse);
1188490e0de5SThomas Veerman return 0;
1189490e0de5SThomas Veerman }
1190490e0de5SThomas Veerman
1191490e0de5SThomas Veerman /* ARGSUSED0 */
1192490e0de5SThomas Veerman static int
puffs_fuse_fs_sync(struct puffs_usermount * pu,int flags,const struct puffs_cred * cr)1193490e0de5SThomas Veerman puffs_fuse_fs_sync(struct puffs_usermount *pu, int flags,
1194490e0de5SThomas Veerman const struct puffs_cred *cr)
1195490e0de5SThomas Veerman {
1196490e0de5SThomas Veerman set_fuse_context_uid_gid(cr);
1197490e0de5SThomas Veerman return 0;
1198490e0de5SThomas Veerman }
1199490e0de5SThomas Veerman
1200490e0de5SThomas Veerman /* ARGSUSED2 */
1201490e0de5SThomas Veerman static int
puffs_fuse_fs_statvfs(struct puffs_usermount * pu,struct statvfs * svfsb)1202490e0de5SThomas Veerman puffs_fuse_fs_statvfs(struct puffs_usermount *pu, struct statvfs *svfsb)
1203490e0de5SThomas Veerman {
1204490e0de5SThomas Veerman struct fuse *fuse;
1205490e0de5SThomas Veerman int ret;
1206490e0de5SThomas Veerman
1207490e0de5SThomas Veerman fuse = puffs_getspecific(pu);
1208490e0de5SThomas Veerman if (fuse->op.statfs == NULL) {
1209490e0de5SThomas Veerman if ((ret = statvfs(PNPATH(puffs_getroot(pu)), svfsb)) == -1) {
1210490e0de5SThomas Veerman return errno;
1211490e0de5SThomas Veerman }
1212490e0de5SThomas Veerman } else {
1213490e0de5SThomas Veerman ret = fuse->op.statfs(PNPATH(puffs_getroot(pu)), svfsb);
1214490e0de5SThomas Veerman }
1215490e0de5SThomas Veerman
1216490e0de5SThomas Veerman return -ret;
1217490e0de5SThomas Veerman }
1218490e0de5SThomas Veerman
1219490e0de5SThomas Veerman
1220490e0de5SThomas Veerman /* End of puffs_fuse operations */
1221490e0de5SThomas Veerman /* ARGSUSED3 */
1222490e0de5SThomas Veerman int
fuse_main_real(int argc,char ** argv,const struct fuse_operations * ops,size_t size,void * userdata)1223490e0de5SThomas Veerman fuse_main_real(int argc, char **argv, const struct fuse_operations *ops,
1224490e0de5SThomas Veerman size_t size, void *userdata)
1225490e0de5SThomas Veerman {
1226490e0de5SThomas Veerman struct fuse *fuse;
1227490e0de5SThomas Veerman char *mountpoint;
1228490e0de5SThomas Veerman int multithreaded;
1229490e0de5SThomas Veerman int fd;
1230490e0de5SThomas Veerman
1231*84d9c625SLionel Sambuc fuse = fuse_setup_real(argc, argv, ops, size, &mountpoint,
1232*84d9c625SLionel Sambuc &multithreaded, &fd, userdata);
1233490e0de5SThomas Veerman
1234490e0de5SThomas Veerman return fuse_loop(fuse);
1235490e0de5SThomas Veerman }
1236490e0de5SThomas Veerman
1237490e0de5SThomas Veerman /*
1238490e0de5SThomas Veerman * XXX: just defer the operation until fuse_new() when we have more
1239490e0de5SThomas Veerman * info on our hands. The real beef is why's this separate in fuse in
1240490e0de5SThomas Veerman * the first place?
1241490e0de5SThomas Veerman */
1242490e0de5SThomas Veerman /* ARGSUSED1 */
1243490e0de5SThomas Veerman struct fuse_chan *
fuse_mount(const char * dir,struct fuse_args * args)1244490e0de5SThomas Veerman fuse_mount(const char *dir, struct fuse_args *args)
1245490e0de5SThomas Veerman {
1246490e0de5SThomas Veerman struct fuse_chan *fc;
1247490e0de5SThomas Veerman char name[64];
1248490e0de5SThomas Veerman
1249490e0de5SThomas Veerman if ((fc = calloc(1, sizeof(*fc))) == NULL) {
1250490e0de5SThomas Veerman err(EXIT_FAILURE, "fuse_mount");
1251490e0de5SThomas Veerman }
1252490e0de5SThomas Veerman fc->dead = 0;
1253490e0de5SThomas Veerman
1254490e0de5SThomas Veerman if ((fc->dir = strdup(dir)) == NULL) {
1255490e0de5SThomas Veerman err(EXIT_FAILURE, "fuse_mount");
1256490e0de5SThomas Veerman }
1257490e0de5SThomas Veerman
1258490e0de5SThomas Veerman /*
1259490e0de5SThomas Veerman * we need to deep copy the args struct - some fuse file
1260490e0de5SThomas Veerman * systems "clean up" the argument vector for "security
1261490e0de5SThomas Veerman * reasons"
1262490e0de5SThomas Veerman */
1263490e0de5SThomas Veerman fc->args = fuse_opt_deep_copy_args(args->argc, args->argv);
1264490e0de5SThomas Veerman
1265490e0de5SThomas Veerman if (args->argc > 0) {
1266490e0de5SThomas Veerman set_refuse_mount_name(args->argv, name, sizeof(name));
1267490e0de5SThomas Veerman if ((args->argv[0] = strdup(name)) == NULL)
1268490e0de5SThomas Veerman err(1, "fuse_mount");
1269490e0de5SThomas Veerman }
1270490e0de5SThomas Veerman
1271490e0de5SThomas Veerman return fc;
1272490e0de5SThomas Veerman }
1273490e0de5SThomas Veerman
1274490e0de5SThomas Veerman /* ARGSUSED1 */
1275490e0de5SThomas Veerman struct fuse *
fuse_new(struct fuse_chan * fc,struct fuse_args * args,const struct fuse_operations * ops,size_t size,void * userdata)1276490e0de5SThomas Veerman fuse_new(struct fuse_chan *fc, struct fuse_args *args,
1277490e0de5SThomas Veerman const struct fuse_operations *ops, size_t size, void *userdata)
1278490e0de5SThomas Veerman {
1279490e0de5SThomas Veerman struct puffs_usermount *pu;
1280490e0de5SThomas Veerman struct fuse_context *fusectx;
1281490e0de5SThomas Veerman struct puffs_pathobj *po_root;
1282490e0de5SThomas Veerman struct puffs_node *pn_root;
1283490e0de5SThomas Veerman struct puffs_ops *pops;
1284490e0de5SThomas Veerman struct refusenode *rn_root;
1285490e0de5SThomas Veerman struct statvfs svfsb;
1286490e0de5SThomas Veerman struct stat st;
1287490e0de5SThomas Veerman struct fuse *fuse;
1288490e0de5SThomas Veerman extern int puffs_fakecc;
1289490e0de5SThomas Veerman char name[64];
1290490e0de5SThomas Veerman char *argv0;
1291490e0de5SThomas Veerman
1292490e0de5SThomas Veerman if ((fuse = calloc(1, sizeof(*fuse))) == NULL) {
1293490e0de5SThomas Veerman err(EXIT_FAILURE, "fuse_new");
1294490e0de5SThomas Veerman }
1295490e0de5SThomas Veerman
1296490e0de5SThomas Veerman /* copy fuse ops to their own structure */
1297490e0de5SThomas Veerman (void) memcpy(&fuse->op, ops, sizeof(fuse->op));
1298490e0de5SThomas Veerman
1299490e0de5SThomas Veerman fusectx = fuse_get_context();
1300490e0de5SThomas Veerman fusectx->fuse = fuse;
1301490e0de5SThomas Veerman fusectx->uid = 0;
1302490e0de5SThomas Veerman fusectx->gid = 0;
1303490e0de5SThomas Veerman fusectx->pid = 0;
1304490e0de5SThomas Veerman fusectx->private_data = userdata;
1305490e0de5SThomas Veerman
1306490e0de5SThomas Veerman fuse->fc = fc;
1307490e0de5SThomas Veerman
1308*84d9c625SLionel Sambuc if (fuse->op.init != NULL)
1309*84d9c625SLionel Sambuc fusectx->private_data = fuse->op.init(NULL); /* XXX */
1310*84d9c625SLionel Sambuc
1311490e0de5SThomas Veerman /* initialise the puffs operations structure */
1312490e0de5SThomas Veerman PUFFSOP_INIT(pops);
1313490e0de5SThomas Veerman
1314490e0de5SThomas Veerman PUFFSOP_SET(pops, puffs_fuse, fs, sync);
1315490e0de5SThomas Veerman PUFFSOP_SET(pops, puffs_fuse, fs, statvfs);
1316490e0de5SThomas Veerman PUFFSOP_SET(pops, puffs_fuse, fs, unmount);
1317490e0de5SThomas Veerman
1318490e0de5SThomas Veerman /*
1319490e0de5SThomas Veerman * XXX: all of these don't possibly need to be
1320490e0de5SThomas Veerman * unconditionally set
1321490e0de5SThomas Veerman */
1322490e0de5SThomas Veerman PUFFSOP_SET(pops, puffs_fuse, node, lookup);
1323490e0de5SThomas Veerman PUFFSOP_SET(pops, puffs_fuse, node, getattr);
1324490e0de5SThomas Veerman PUFFSOP_SET(pops, puffs_fuse, node, setattr);
1325490e0de5SThomas Veerman PUFFSOP_SET(pops, puffs_fuse, node, readdir);
1326490e0de5SThomas Veerman PUFFSOP_SET(pops, puffs_fuse, node, readlink);
1327490e0de5SThomas Veerman PUFFSOP_SET(pops, puffs_fuse, node, mknod);
1328490e0de5SThomas Veerman PUFFSOP_SET(pops, puffs_fuse, node, create);
1329490e0de5SThomas Veerman PUFFSOP_SET(pops, puffs_fuse, node, remove);
1330490e0de5SThomas Veerman PUFFSOP_SET(pops, puffs_fuse, node, mkdir);
1331490e0de5SThomas Veerman PUFFSOP_SET(pops, puffs_fuse, node, rmdir);
1332490e0de5SThomas Veerman PUFFSOP_SET(pops, puffs_fuse, node, symlink);
1333490e0de5SThomas Veerman PUFFSOP_SET(pops, puffs_fuse, node, rename);
1334490e0de5SThomas Veerman PUFFSOP_SET(pops, puffs_fuse, node, link);
1335490e0de5SThomas Veerman PUFFSOP_SET(pops, puffs_fuse, node, open);
1336490e0de5SThomas Veerman PUFFSOP_SET(pops, puffs_fuse, node, close);
1337490e0de5SThomas Veerman PUFFSOP_SET(pops, puffs_fuse, node, read);
1338490e0de5SThomas Veerman PUFFSOP_SET(pops, puffs_fuse, node, write);
1339490e0de5SThomas Veerman PUFFSOP_SET(pops, puffs_fuse, node, reclaim);
1340490e0de5SThomas Veerman
1341490e0de5SThomas Veerman argv0 = (*args->argv[0] == 0x0) ? fc->args->argv[0] : args->argv[0];
1342490e0de5SThomas Veerman set_refuse_mount_name(&argv0, name, sizeof(name));
1343490e0de5SThomas Veerman
1344490e0de5SThomas Veerman puffs_fakecc = 1; /* XXX */
1345490e0de5SThomas Veerman pu = puffs_init(pops, _PATH_PUFFS, name, fuse,
1346490e0de5SThomas Veerman PUFFS_FLAG_BUILDPATH
1347490e0de5SThomas Veerman | PUFFS_FLAG_HASHPATH
1348490e0de5SThomas Veerman | PUFFS_KFLAG_NOCACHE);
1349490e0de5SThomas Veerman if (pu == NULL) {
1350490e0de5SThomas Veerman err(EXIT_FAILURE, "puffs_init");
1351490e0de5SThomas Veerman }
1352490e0de5SThomas Veerman fc->pu = pu;
1353490e0de5SThomas Veerman
1354490e0de5SThomas Veerman pn_root = newrn(pu);
1355490e0de5SThomas Veerman puffs_setroot(pu, pn_root);
1356490e0de5SThomas Veerman rn_root = pn_root->pn_data;
1357490e0de5SThomas Veerman rn_root->flags |= RN_ROOT;
1358490e0de5SThomas Veerman
1359490e0de5SThomas Veerman po_root = puffs_getrootpathobj(pu);
1360490e0de5SThomas Veerman if ((po_root->po_path = strdup("/")) == NULL)
1361490e0de5SThomas Veerman err(1, "fuse_new");
1362490e0de5SThomas Veerman po_root->po_len = 1;
1363490e0de5SThomas Veerman puffs_path_buildhash(pu, po_root);
1364490e0de5SThomas Veerman
1365490e0de5SThomas Veerman /* sane defaults */
1366490e0de5SThomas Veerman puffs_vattr_null(&pn_root->pn_va);
1367490e0de5SThomas Veerman pn_root->pn_va.va_type = VDIR;
1368490e0de5SThomas Veerman pn_root->pn_va.va_mode = 0755;
1369490e0de5SThomas Veerman if (fuse->op.getattr)
1370490e0de5SThomas Veerman if (fuse->op.getattr(po_root->po_path, &st) == 0)
1371490e0de5SThomas Veerman puffs_stat2vattr(&pn_root->pn_va, &st);
1372490e0de5SThomas Veerman assert(pn_root->pn_va.va_type == VDIR);
1373490e0de5SThomas Veerman
1374490e0de5SThomas Veerman puffs_set_prepost(pu, set_fuse_context_pid, NULL);
1375490e0de5SThomas Veerman
1376490e0de5SThomas Veerman puffs_zerostatvfs(&svfsb);
1377490e0de5SThomas Veerman if (puffs_mount(pu, fc->dir, MNT_NODEV | MNT_NOSUID, pn_root) == -1) {
1378490e0de5SThomas Veerman err(EXIT_FAILURE, "puffs_mount: directory \"%s\"", fc->dir);
1379490e0de5SThomas Veerman }
1380490e0de5SThomas Veerman
1381490e0de5SThomas Veerman return fuse;
1382490e0de5SThomas Veerman }
1383490e0de5SThomas Veerman
1384490e0de5SThomas Veerman int
fuse_loop(struct fuse * fuse)1385490e0de5SThomas Veerman fuse_loop(struct fuse *fuse)
1386490e0de5SThomas Veerman {
1387490e0de5SThomas Veerman
1388490e0de5SThomas Veerman return puffs_mainloop(fuse->fc->pu);
1389490e0de5SThomas Veerman }
1390490e0de5SThomas Veerman
1391490e0de5SThomas Veerman void
fuse_destroy(struct fuse * fuse)1392490e0de5SThomas Veerman fuse_destroy(struct fuse *fuse)
1393490e0de5SThomas Veerman {
1394490e0de5SThomas Veerman
1395490e0de5SThomas Veerman /*
1396490e0de5SThomas Veerman * TODO: needs to assert the fs is quiescent, i.e. no other
1397490e0de5SThomas Veerman * threads exist
1398490e0de5SThomas Veerman */
1399490e0de5SThomas Veerman
1400490e0de5SThomas Veerman delete_context_key();
1401490e0de5SThomas Veerman /* XXXXXX: missing stuff */
1402490e0de5SThomas Veerman free(fuse);
1403490e0de5SThomas Veerman }
1404490e0de5SThomas Veerman
1405490e0de5SThomas Veerman void
fuse_exit(struct fuse * fuse)1406490e0de5SThomas Veerman fuse_exit(struct fuse *fuse)
1407490e0de5SThomas Veerman {
1408490e0de5SThomas Veerman
1409490e0de5SThomas Veerman /* XXX: puffs_exit() is WRONG */
1410490e0de5SThomas Veerman if (fuse->fc->dead == 0)
1411490e0de5SThomas Veerman puffs_exit(fuse->fc->pu, 1);
1412490e0de5SThomas Veerman fuse->fc->dead = 1;
1413490e0de5SThomas Veerman }
1414490e0de5SThomas Veerman
1415490e0de5SThomas Veerman /*
1416490e0de5SThomas Veerman * XXX: obviously not the most perfect of functions, but needs some
1417490e0de5SThomas Veerman * puffs tweaking for a better tomorrow
1418490e0de5SThomas Veerman */
1419490e0de5SThomas Veerman /*ARGSUSED*/
1420490e0de5SThomas Veerman void
fuse_unmount(const char * mp,struct fuse_chan * fc)1421490e0de5SThomas Veerman fuse_unmount(const char *mp, struct fuse_chan *fc)
1422490e0de5SThomas Veerman {
1423490e0de5SThomas Veerman
1424490e0de5SThomas Veerman /* XXX: puffs_exit() is WRONG */
1425490e0de5SThomas Veerman if (fc->dead == 0)
1426490e0de5SThomas Veerman puffs_exit(fc->pu, 1);
1427490e0de5SThomas Veerman fc->dead = 1;
1428490e0de5SThomas Veerman }
1429490e0de5SThomas Veerman
1430490e0de5SThomas Veerman /*ARGSUSED*/
1431490e0de5SThomas Veerman void
fuse_unmount_compat22(const char * mp)1432490e0de5SThomas Veerman fuse_unmount_compat22(const char *mp)
1433490e0de5SThomas Veerman {
1434490e0de5SThomas Veerman
1435490e0de5SThomas Veerman return;
1436490e0de5SThomas Veerman }
1437490e0de5SThomas Veerman
1438490e0de5SThomas Veerman /* The next function "exposes" struct fuse to userland. Not much
1439490e0de5SThomas Veerman * that we can do about this, as we're conforming to a defined
1440490e0de5SThomas Veerman * interface. */
1441490e0de5SThomas Veerman
1442490e0de5SThomas Veerman void
fuse_teardown(struct fuse * fuse,char * mountpoint)1443490e0de5SThomas Veerman fuse_teardown(struct fuse *fuse, char *mountpoint)
1444490e0de5SThomas Veerman {
1445490e0de5SThomas Veerman fuse_unmount(mountpoint, fuse->fc);
1446490e0de5SThomas Veerman fuse_destroy(fuse);
1447490e0de5SThomas Veerman }
1448