xref: /freebsd/contrib/lib9p/backend/fs.c (revision 134e17798c9af53632b372348ab828e75e65bf46)
1*134e1779SJakub Wojciech Klama /*
2*134e1779SJakub Wojciech Klama  * Copyright 2016 Jakub Klama <jceel@FreeBSD.org>
3*134e1779SJakub Wojciech Klama  * All rights reserved
4*134e1779SJakub Wojciech Klama  *
5*134e1779SJakub Wojciech Klama  * Redistribution and use in source and binary forms, with or without
6*134e1779SJakub Wojciech Klama  * modification, are permitted providing that the following conditions
7*134e1779SJakub Wojciech Klama  * are met:
8*134e1779SJakub Wojciech Klama  * 1. Redistributions of source code must retain the above copyright
9*134e1779SJakub Wojciech Klama  *    notice, this list of conditions and the following disclaimer.
10*134e1779SJakub Wojciech Klama  * 2. Redistributions in binary form must reproduce the above copyright
11*134e1779SJakub Wojciech Klama  *    notice, this list of conditions and the following disclaimer in the
12*134e1779SJakub Wojciech Klama  *    documentation and/or other materials provided with the distribution.
13*134e1779SJakub Wojciech Klama  *
14*134e1779SJakub Wojciech Klama  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15*134e1779SJakub Wojciech Klama  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16*134e1779SJakub Wojciech Klama  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*134e1779SJakub Wojciech Klama  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18*134e1779SJakub Wojciech Klama  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*134e1779SJakub Wojciech Klama  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*134e1779SJakub Wojciech Klama  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*134e1779SJakub Wojciech Klama  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22*134e1779SJakub Wojciech Klama  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
23*134e1779SJakub Wojciech Klama  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24*134e1779SJakub Wojciech Klama  * POSSIBILITY OF SUCH DAMAGE.
25*134e1779SJakub Wojciech Klama  *
26*134e1779SJakub Wojciech Klama  */
27*134e1779SJakub Wojciech Klama 
28*134e1779SJakub Wojciech Klama /*
29*134e1779SJakub Wojciech Klama  * Based on libixp code: �2007-2010 Kris Maglione <maglione.k at Gmail>
30*134e1779SJakub Wojciech Klama  */
31*134e1779SJakub Wojciech Klama 
32*134e1779SJakub Wojciech Klama #include <stdlib.h>
33*134e1779SJakub Wojciech Klama #include <string.h>
34*134e1779SJakub Wojciech Klama #include <unistd.h>
35*134e1779SJakub Wojciech Klama #include <stdbool.h>
36*134e1779SJakub Wojciech Klama #include <fcntl.h>
37*134e1779SJakub Wojciech Klama #include <errno.h>
38*134e1779SJakub Wojciech Klama #include <assert.h>
39*134e1779SJakub Wojciech Klama #include <sys/types.h>
40*134e1779SJakub Wojciech Klama #include <sys/stat.h>
41*134e1779SJakub Wojciech Klama #include <sys/mount.h>
42*134e1779SJakub Wojciech Klama #include <sys/param.h>
43*134e1779SJakub Wojciech Klama #include <sys/queue.h>
44*134e1779SJakub Wojciech Klama #include <sys/socket.h>
45*134e1779SJakub Wojciech Klama #include <sys/un.h>
46*134e1779SJakub Wojciech Klama #include <dirent.h>
47*134e1779SJakub Wojciech Klama #include <pwd.h>
48*134e1779SJakub Wojciech Klama #include <grp.h>
49*134e1779SJakub Wojciech Klama #include <libgen.h>
50*134e1779SJakub Wojciech Klama #include <pthread.h>
51*134e1779SJakub Wojciech Klama #include "../lib9p.h"
52*134e1779SJakub Wojciech Klama #include "../lib9p_impl.h"
53*134e1779SJakub Wojciech Klama #include "../fid.h"
54*134e1779SJakub Wojciech Klama #include "../log.h"
55*134e1779SJakub Wojciech Klama #include "../rfuncs.h"
56*134e1779SJakub Wojciech Klama #include "../genacl.h"
57*134e1779SJakub Wojciech Klama #include "backend.h"
58*134e1779SJakub Wojciech Klama #include "fs.h"
59*134e1779SJakub Wojciech Klama 
60*134e1779SJakub Wojciech Klama #if defined(WITH_CASPER)
61*134e1779SJakub Wojciech Klama   #include <libcasper.h>
62*134e1779SJakub Wojciech Klama   #include <casper/cap_pwd.h>
63*134e1779SJakub Wojciech Klama   #include <casper/cap_grp.h>
64*134e1779SJakub Wojciech Klama #endif
65*134e1779SJakub Wojciech Klama 
66*134e1779SJakub Wojciech Klama #if defined(__FreeBSD__)
67*134e1779SJakub Wojciech Klama   #include <sys/param.h>
68*134e1779SJakub Wojciech Klama   #if __FreeBSD_version >= 1000000
69*134e1779SJakub Wojciech Klama     #define	HAVE_BINDAT
70*134e1779SJakub Wojciech Klama   #endif
71*134e1779SJakub Wojciech Klama #endif
72*134e1779SJakub Wojciech Klama 
73*134e1779SJakub Wojciech Klama #if defined(__FreeBSD__)
74*134e1779SJakub Wojciech Klama   #define	HAVE_BIRTHTIME
75*134e1779SJakub Wojciech Klama #endif
76*134e1779SJakub Wojciech Klama 
77*134e1779SJakub Wojciech Klama #if defined(__APPLE__)
78*134e1779SJakub Wojciech Klama   #include <sys/syscall.h>
79*134e1779SJakub Wojciech Klama   #include "Availability.h"
80*134e1779SJakub Wojciech Klama   #define ACL_TYPE_NFS4 ACL_TYPE_EXTENDED
81*134e1779SJakub Wojciech Klama #endif
82*134e1779SJakub Wojciech Klama 
83*134e1779SJakub Wojciech Klama struct fs_softc {
84*134e1779SJakub Wojciech Klama 	int 	fs_rootfd;
85*134e1779SJakub Wojciech Klama 	bool	fs_readonly;
86*134e1779SJakub Wojciech Klama #if defined(WITH_CASPER)
87*134e1779SJakub Wojciech Klama 	cap_channel_t *fs_cappwd;
88*134e1779SJakub Wojciech Klama 	cap_channel_t *fs_capgrp;
89*134e1779SJakub Wojciech Klama #endif
90*134e1779SJakub Wojciech Klama };
91*134e1779SJakub Wojciech Klama 
92*134e1779SJakub Wojciech Klama struct fs_fid {
93*134e1779SJakub Wojciech Klama 	DIR	*ff_dir;
94*134e1779SJakub Wojciech Klama 	int	ff_dirfd;
95*134e1779SJakub Wojciech Klama 	int	ff_fd;
96*134e1779SJakub Wojciech Klama 	int	ff_flags;
97*134e1779SJakub Wojciech Klama 	char	*ff_name;
98*134e1779SJakub Wojciech Klama 	struct fs_authinfo *ff_ai;
99*134e1779SJakub Wojciech Klama 	pthread_mutex_t ff_mtx;
100*134e1779SJakub Wojciech Klama 	struct l9p_acl *ff_acl; /* cached ACL if any */
101*134e1779SJakub Wojciech Klama };
102*134e1779SJakub Wojciech Klama 
103*134e1779SJakub Wojciech Klama #define	FF_NO_NFSV4_ACL	0x01	/* don't go looking for NFSv4 ACLs */
104*134e1779SJakub Wojciech Klama /*	FF_NO_POSIX_ACL	0x02	-- not yet */
105*134e1779SJakub Wojciech Klama 
106*134e1779SJakub Wojciech Klama /*
107*134e1779SJakub Wojciech Klama  * Our authinfo consists of:
108*134e1779SJakub Wojciech Klama  *
109*134e1779SJakub Wojciech Klama  *  - a reference count
110*134e1779SJakub Wojciech Klama  *  - a uid
111*134e1779SJakub Wojciech Klama  *  - a gid-set
112*134e1779SJakub Wojciech Klama  *
113*134e1779SJakub Wojciech Klama  * The "default" gid is the first gid in the git-set, provided the
114*134e1779SJakub Wojciech Klama  * set size is at least 1.  The set-size may be zero, though.
115*134e1779SJakub Wojciech Klama  *
116*134e1779SJakub Wojciech Klama  * Adjustments to the ref-count must be atomic, once it's shared.
117*134e1779SJakub Wojciech Klama  * It would be nice to use C11 atomics here but they are not common
118*134e1779SJakub Wojciech Klama  * enough to all systems just yet; for now, we use a mutex.
119*134e1779SJakub Wojciech Klama  *
120*134e1779SJakub Wojciech Klama  * Note that some ops (Linux style ones) pass an effective gid for
121*134e1779SJakub Wojciech Klama  * the op, in which case, that gid may override.  To achieve this
122*134e1779SJakub Wojciech Klama  * effect, permissions testing functions also take an extra gid.
123*134e1779SJakub Wojciech Klama  * If this gid is (gid_t)-1 it is not used and only the remaining
124*134e1779SJakub Wojciech Klama  * gids take part.
125*134e1779SJakub Wojciech Klama  *
126*134e1779SJakub Wojciech Klama  * The uid may also be (uid_t)-1, meaning "no uid was available
127*134e1779SJakub Wojciech Klama  * at all at attach time".  In this case, new files inherit parent
128*134e1779SJakub Wojciech Klama  * directory uids.
129*134e1779SJakub Wojciech Klama  *
130*134e1779SJakub Wojciech Klama  * The refcount is simply the number of "openfile"s using this
131*134e1779SJakub Wojciech Klama  * authinfo (so that when the last ref goes away, we can free it).
132*134e1779SJakub Wojciech Klama  *
133*134e1779SJakub Wojciech Klama  * There are also master ACL flags (same as in ff_flags).
134*134e1779SJakub Wojciech Klama  */
135*134e1779SJakub Wojciech Klama struct fs_authinfo {
136*134e1779SJakub Wojciech Klama 	pthread_mutex_t ai_mtx;	/* lock for refcnt */
137*134e1779SJakub Wojciech Klama 	uint32_t ai_refcnt;
138*134e1779SJakub Wojciech Klama 	int	ai_flags;
139*134e1779SJakub Wojciech Klama 	uid_t	ai_uid;
140*134e1779SJakub Wojciech Klama 	int	ai_ngids;
141*134e1779SJakub Wojciech Klama 	gid_t	ai_gids[];	/* NB: flexible array member */
142*134e1779SJakub Wojciech Klama };
143*134e1779SJakub Wojciech Klama 
144*134e1779SJakub Wojciech Klama /*
145*134e1779SJakub Wojciech Klama  * We have a global-static mutex for single-threading Tattach
146*134e1779SJakub Wojciech Klama  * requests, which use getpwnam (and indirectly, getgr* functions)
147*134e1779SJakub Wojciech Klama  * which are not reentrant.
148*134e1779SJakub Wojciech Klama  */
149*134e1779SJakub Wojciech Klama static bool fs_attach_mutex_inited;
150*134e1779SJakub Wojciech Klama static pthread_mutex_t fs_attach_mutex;
151*134e1779SJakub Wojciech Klama 
152*134e1779SJakub Wojciech Klama /*
153*134e1779SJakub Wojciech Klama  * Internal functions (except inline functions).
154*134e1779SJakub Wojciech Klama  */
155*134e1779SJakub Wojciech Klama static struct passwd *fs_getpwuid(struct fs_softc *, uid_t, struct r_pgdata *);
156*134e1779SJakub Wojciech Klama static struct group *fs_getgrgid(struct fs_softc *, gid_t, struct r_pgdata *);
157*134e1779SJakub Wojciech Klama static int fs_buildname(struct l9p_fid *, char *, char *, size_t);
158*134e1779SJakub Wojciech Klama static int fs_pdir(struct fs_softc *, struct l9p_fid *, char *, size_t,
159*134e1779SJakub Wojciech Klama     struct stat *st);
160*134e1779SJakub Wojciech Klama static int fs_dpf(char *, char *, size_t);
161*134e1779SJakub Wojciech Klama static int fs_oflags_dotu(int, int *);
162*134e1779SJakub Wojciech Klama static int fs_oflags_dotl(uint32_t, int *, enum l9p_omode *);
163*134e1779SJakub Wojciech Klama static int fs_nde(struct fs_softc *, struct l9p_fid *, bool, gid_t,
164*134e1779SJakub Wojciech Klama     struct stat *, uid_t *, gid_t *);
165*134e1779SJakub Wojciech Klama static struct fs_fid *open_fid(int, const char *, struct fs_authinfo *, bool);
166*134e1779SJakub Wojciech Klama static void dostat(struct fs_softc *, struct l9p_stat *, char *,
167*134e1779SJakub Wojciech Klama     struct stat *, bool dotu);
168*134e1779SJakub Wojciech Klama static void dostatfs(struct l9p_statfs *, struct statfs *, long);
169*134e1779SJakub Wojciech Klama static void fillacl(struct fs_fid *ff);
170*134e1779SJakub Wojciech Klama static struct l9p_acl *getacl(struct fs_fid *ff, int fd, const char *path);
171*134e1779SJakub Wojciech Klama static void dropacl(struct fs_fid *ff);
172*134e1779SJakub Wojciech Klama static struct l9p_acl *look_for_nfsv4_acl(struct fs_fid *ff, int fd,
173*134e1779SJakub Wojciech Klama     const char *path);
174*134e1779SJakub Wojciech Klama static int check_access(int32_t,
175*134e1779SJakub Wojciech Klama     struct l9p_acl *, struct stat *, struct l9p_acl *, struct stat *,
176*134e1779SJakub Wojciech Klama     struct fs_authinfo *, gid_t);
177*134e1779SJakub Wojciech Klama static void generate_qid(struct stat *, struct l9p_qid *);
178*134e1779SJakub Wojciech Klama 
179*134e1779SJakub Wojciech Klama static int fs_icreate(void *, struct l9p_fid *, char *, int,
180*134e1779SJakub Wojciech Klama     bool, mode_t, gid_t, struct stat *);
181*134e1779SJakub Wojciech Klama static int fs_iopen(void *, struct l9p_fid *, int, enum l9p_omode,
182*134e1779SJakub Wojciech Klama     gid_t, struct stat *);
183*134e1779SJakub Wojciech Klama static int fs_imkdir(void *, struct l9p_fid *, char *,
184*134e1779SJakub Wojciech Klama     bool, mode_t, gid_t, struct stat *);
185*134e1779SJakub Wojciech Klama static int fs_imkfifo(void *, struct l9p_fid *, char *,
186*134e1779SJakub Wojciech Klama     bool, mode_t, gid_t, struct stat *);
187*134e1779SJakub Wojciech Klama static int fs_imknod(void *, struct l9p_fid *, char *,
188*134e1779SJakub Wojciech Klama     bool, mode_t, dev_t, gid_t, struct stat *);
189*134e1779SJakub Wojciech Klama static int fs_imksocket(void *, struct l9p_fid *, char *,
190*134e1779SJakub Wojciech Klama     bool, mode_t, gid_t, struct stat *);
191*134e1779SJakub Wojciech Klama static int fs_isymlink(void *, struct l9p_fid *, char *, char *,
192*134e1779SJakub Wojciech Klama     gid_t, struct stat *);
193*134e1779SJakub Wojciech Klama 
194*134e1779SJakub Wojciech Klama /*
195*134e1779SJakub Wojciech Klama  * Internal functions implementing backend.
196*134e1779SJakub Wojciech Klama  */
197*134e1779SJakub Wojciech Klama static int fs_attach(void *, struct l9p_request *);
198*134e1779SJakub Wojciech Klama static int fs_clunk(void *, struct l9p_fid *);
199*134e1779SJakub Wojciech Klama static int fs_create(void *, struct l9p_request *);
200*134e1779SJakub Wojciech Klama static int fs_open(void *, struct l9p_request *);
201*134e1779SJakub Wojciech Klama static int fs_read(void *, struct l9p_request *);
202*134e1779SJakub Wojciech Klama static int fs_remove(void *, struct l9p_fid *);
203*134e1779SJakub Wojciech Klama static int fs_stat(void *, struct l9p_request *);
204*134e1779SJakub Wojciech Klama static int fs_walk(void *, struct l9p_request *);
205*134e1779SJakub Wojciech Klama static int fs_write(void *, struct l9p_request *);
206*134e1779SJakub Wojciech Klama static int fs_wstat(void *, struct l9p_request *);
207*134e1779SJakub Wojciech Klama static int fs_statfs(void *, struct l9p_request *);
208*134e1779SJakub Wojciech Klama static int fs_lopen(void *, struct l9p_request *);
209*134e1779SJakub Wojciech Klama static int fs_lcreate(void *, struct l9p_request *);
210*134e1779SJakub Wojciech Klama static int fs_symlink(void *, struct l9p_request *);
211*134e1779SJakub Wojciech Klama static int fs_mknod(void *, struct l9p_request *);
212*134e1779SJakub Wojciech Klama static int fs_rename(void *, struct l9p_request *);
213*134e1779SJakub Wojciech Klama static int fs_readlink(void *, struct l9p_request *);
214*134e1779SJakub Wojciech Klama static int fs_getattr(void *, struct l9p_request *);
215*134e1779SJakub Wojciech Klama static int fs_setattr(void *, struct l9p_request *);
216*134e1779SJakub Wojciech Klama static int fs_xattrwalk(void *, struct l9p_request *);
217*134e1779SJakub Wojciech Klama static int fs_xattrcreate(void *, struct l9p_request *);
218*134e1779SJakub Wojciech Klama static int fs_readdir(void *, struct l9p_request *);
219*134e1779SJakub Wojciech Klama static int fs_fsync(void *, struct l9p_request *);
220*134e1779SJakub Wojciech Klama static int fs_lock(void *, struct l9p_request *);
221*134e1779SJakub Wojciech Klama static int fs_getlock(void *, struct l9p_request *);
222*134e1779SJakub Wojciech Klama static int fs_link(void *, struct l9p_request *);
223*134e1779SJakub Wojciech Klama static int fs_renameat(void *, struct l9p_request *);
224*134e1779SJakub Wojciech Klama static int fs_unlinkat(void *, struct l9p_request *);
225*134e1779SJakub Wojciech Klama static void fs_freefid(void *, struct l9p_fid *);
226*134e1779SJakub Wojciech Klama 
227*134e1779SJakub Wojciech Klama /*
228*134e1779SJakub Wojciech Klama  * Convert from 9p2000 open/create mode to Unix-style O_* flags.
229*134e1779SJakub Wojciech Klama  * This includes 9p2000.u extensions, but not 9p2000.L protocol,
230*134e1779SJakub Wojciech Klama  * which has entirely different open, create, etc., flag bits.
231*134e1779SJakub Wojciech Klama  *
232*134e1779SJakub Wojciech Klama  * The <mode> given here is the one-byte (uint8_t) "mode"
233*134e1779SJakub Wojciech Klama  * argument to Tcreate or Topen, so it can have at most 8 bits.
234*134e1779SJakub Wojciech Klama  *
235*134e1779SJakub Wojciech Klama  * https://swtch.com/plan9port/man/man9/open.html and
236*134e1779SJakub Wojciech Klama  * http://plan9.bell-labs.com/magic/man2html/5/open
237*134e1779SJakub Wojciech Klama  * both say:
238*134e1779SJakub Wojciech Klama  *
239*134e1779SJakub Wojciech Klama  *   The [low two bits of the] mode field determines the
240*134e1779SJakub Wojciech Klama  *   type of I/O ... [I]f mode has the OTRUNC (0x10) bit
241*134e1779SJakub Wojciech Klama  *   set, the file is to be truncated, which requires write
242*134e1779SJakub Wojciech Klama  *   permission ...; if the mode has the ORCLOSE (0x40) bit
243*134e1779SJakub Wojciech Klama  *   set, the file is to be removed when the fid is clunked,
244*134e1779SJakub Wojciech Klama  *   which requires permission to remove the file from its
245*134e1779SJakub Wojciech Klama  *   directory.  All other bits in mode should be zero.  It
246*134e1779SJakub Wojciech Klama  *   is illegal to write a directory, truncate it, or
247*134e1779SJakub Wojciech Klama  *   attempt to remove it on close.
248*134e1779SJakub Wojciech Klama  *
249*134e1779SJakub Wojciech Klama  * 9P2000.u may add ODIRECT (0x80); this is not completely clear.
250*134e1779SJakub Wojciech Klama  * The fcall.h header defines OCEXEC (0x20) as well, but it makes
251*134e1779SJakub Wojciech Klama  * no sense to send this to a server.  There seem to be no bits
252*134e1779SJakub Wojciech Klama  * 0x04 and 0x08.
253*134e1779SJakub Wojciech Klama  *
254*134e1779SJakub Wojciech Klama  * We always turn on O_NOCTTY since as a server, we never want
255*134e1779SJakub Wojciech Klama  * to gain a controlling terminal.  We always turn on O_NOFOLLOW
256*134e1779SJakub Wojciech Klama  * for reasons described elsewhere.
257*134e1779SJakub Wojciech Klama  */
258*134e1779SJakub Wojciech Klama static int
fs_oflags_dotu(int mode,int * aflags)259*134e1779SJakub Wojciech Klama fs_oflags_dotu(int mode, int *aflags)
260*134e1779SJakub Wojciech Klama {
261*134e1779SJakub Wojciech Klama 	int flags;
262*134e1779SJakub Wojciech Klama #define	CONVERT(theirs, ours) \
263*134e1779SJakub Wojciech Klama 	do { \
264*134e1779SJakub Wojciech Klama 		if (mode & (theirs)) { \
265*134e1779SJakub Wojciech Klama 			mode &= ~(theirs); \
266*134e1779SJakub Wojciech Klama 			flags |= ours; \
267*134e1779SJakub Wojciech Klama 		} \
268*134e1779SJakub Wojciech Klama 	} while (0)
269*134e1779SJakub Wojciech Klama 
270*134e1779SJakub Wojciech Klama 	switch (mode & L9P_OACCMODE) {
271*134e1779SJakub Wojciech Klama 
272*134e1779SJakub Wojciech Klama 	case L9P_OREAD:
273*134e1779SJakub Wojciech Klama 	default:
274*134e1779SJakub Wojciech Klama 		flags = O_RDONLY;
275*134e1779SJakub Wojciech Klama 		break;
276*134e1779SJakub Wojciech Klama 
277*134e1779SJakub Wojciech Klama 	case L9P_OWRITE:
278*134e1779SJakub Wojciech Klama 		flags = O_WRONLY;
279*134e1779SJakub Wojciech Klama 		break;
280*134e1779SJakub Wojciech Klama 
281*134e1779SJakub Wojciech Klama 	case L9P_ORDWR:
282*134e1779SJakub Wojciech Klama 		flags = O_RDWR;
283*134e1779SJakub Wojciech Klama 		break;
284*134e1779SJakub Wojciech Klama 
285*134e1779SJakub Wojciech Klama 	case L9P_OEXEC:
286*134e1779SJakub Wojciech Klama 		if (mode & L9P_OTRUNC)
287*134e1779SJakub Wojciech Klama 			return (EINVAL);
288*134e1779SJakub Wojciech Klama 		flags = O_RDONLY;
289*134e1779SJakub Wojciech Klama 		break;
290*134e1779SJakub Wojciech Klama 	}
291*134e1779SJakub Wojciech Klama 
292*134e1779SJakub Wojciech Klama 	flags |= O_NOCTTY | O_NOFOLLOW;
293*134e1779SJakub Wojciech Klama 
294*134e1779SJakub Wojciech Klama 	CONVERT(L9P_OTRUNC, O_TRUNC);
295*134e1779SJakub Wojciech Klama 
296*134e1779SJakub Wojciech Klama 	/*
297*134e1779SJakub Wojciech Klama 	 * Now take away some flags locally:
298*134e1779SJakub Wojciech Klama 	 *   the access mode (already translated)
299*134e1779SJakub Wojciech Klama 	 *   ORCLOSE - caller only
300*134e1779SJakub Wojciech Klama 	 *   OCEXEC - makes no sense in server
301*134e1779SJakub Wojciech Klama 	 *   ODIRECT - not applicable here
302*134e1779SJakub Wojciech Klama 	 * If there are any flag bits left after this,
303*134e1779SJakub Wojciech Klama 	 * we were unable to translate them.  For now, let's
304*134e1779SJakub Wojciech Klama 	 * treat this as EINVAL so that we can catch problems.
305*134e1779SJakub Wojciech Klama 	 */
306*134e1779SJakub Wojciech Klama 	mode &= ~(L9P_OACCMODE | L9P_ORCLOSE | L9P_OCEXEC | L9P_ODIRECT);
307*134e1779SJakub Wojciech Klama 	if (mode != 0) {
308*134e1779SJakub Wojciech Klama 		L9P_LOG(L9P_INFO,
309*134e1779SJakub Wojciech Klama 		    "fs_oflags_dotu: untranslated bits: %#x",
310*134e1779SJakub Wojciech Klama 		    (unsigned)mode);
311*134e1779SJakub Wojciech Klama 		return (EINVAL);
312*134e1779SJakub Wojciech Klama 	}
313*134e1779SJakub Wojciech Klama 
314*134e1779SJakub Wojciech Klama 	*aflags = flags;
315*134e1779SJakub Wojciech Klama 	return (0);
316*134e1779SJakub Wojciech Klama #undef CONVERT
317*134e1779SJakub Wojciech Klama }
318*134e1779SJakub Wojciech Klama 
319*134e1779SJakub Wojciech Klama /*
320*134e1779SJakub Wojciech Klama  * Convert from 9P2000.L (Linux) open mode bits to O_* flags.
321*134e1779SJakub Wojciech Klama  * See fs_oflags_dotu above.
322*134e1779SJakub Wojciech Klama  *
323*134e1779SJakub Wojciech Klama  * Linux currently does not have open-for-exec, but there is a
324*134e1779SJakub Wojciech Klama  * proposal for it using O_PATH|O_NOFOLLOW, now handled here.
325*134e1779SJakub Wojciech Klama  *
326*134e1779SJakub Wojciech Klama  * We may eventually also set L9P_ORCLOSE for L_O_TMPFILE.
327*134e1779SJakub Wojciech Klama  */
328*134e1779SJakub Wojciech Klama static int
fs_oflags_dotl(uint32_t l_mode,int * aflags,enum l9p_omode * ap9)329*134e1779SJakub Wojciech Klama fs_oflags_dotl(uint32_t l_mode, int *aflags, enum l9p_omode *ap9)
330*134e1779SJakub Wojciech Klama {
331*134e1779SJakub Wojciech Klama 	int flags;
332*134e1779SJakub Wojciech Klama 	enum l9p_omode p9;
333*134e1779SJakub Wojciech Klama #define	CLEAR(theirs)	l_mode &= ~(uint32_t)(theirs)
334*134e1779SJakub Wojciech Klama #define	CONVERT(theirs, ours) \
335*134e1779SJakub Wojciech Klama 	do { \
336*134e1779SJakub Wojciech Klama 		if (l_mode & (theirs)) { \
337*134e1779SJakub Wojciech Klama 			CLEAR(theirs); \
338*134e1779SJakub Wojciech Klama 			flags |= ours; \
339*134e1779SJakub Wojciech Klama 		} \
340*134e1779SJakub Wojciech Klama 	} while (0)
341*134e1779SJakub Wojciech Klama 
342*134e1779SJakub Wojciech Klama 	/*
343*134e1779SJakub Wojciech Klama 	 * Linux O_RDONLY, O_WRONLY, O_RDWR (0,1,2) match BSD/MacOS.
344*134e1779SJakub Wojciech Klama 	 */
345*134e1779SJakub Wojciech Klama 	flags = l_mode & O_ACCMODE;
346*134e1779SJakub Wojciech Klama 	if (flags == 3)
347*134e1779SJakub Wojciech Klama 		return (EINVAL);
348*134e1779SJakub Wojciech Klama 	CLEAR(O_ACCMODE);
349*134e1779SJakub Wojciech Klama 
350*134e1779SJakub Wojciech Klama 	if ((l_mode & (L9P_L_O_PATH | L9P_L_O_NOFOLLOW)) ==
351*134e1779SJakub Wojciech Klama 		    (L9P_L_O_PATH | L9P_L_O_NOFOLLOW)) {
352*134e1779SJakub Wojciech Klama 		CLEAR(L9P_L_O_PATH | L9P_L_O_NOFOLLOW);
353*134e1779SJakub Wojciech Klama 		p9 = L9P_OEXEC;
354*134e1779SJakub Wojciech Klama 	} else {
355*134e1779SJakub Wojciech Klama 		/*
356*134e1779SJakub Wojciech Klama 		 * Slightly dirty, but same dirt, really, as
357*134e1779SJakub Wojciech Klama 		 * setting flags from l_mode & O_ACCMODE.
358*134e1779SJakub Wojciech Klama 		 */
359*134e1779SJakub Wojciech Klama 		p9 = (enum l9p_omode)flags;	/* slightly dirty */
360*134e1779SJakub Wojciech Klama 	}
361*134e1779SJakub Wojciech Klama 
362*134e1779SJakub Wojciech Klama 	/* turn L_O_TMPFILE into L9P_ORCLOSE in *p9? */
363*134e1779SJakub Wojciech Klama 	if (l_mode & L9P_L_O_TRUNC)
364*134e1779SJakub Wojciech Klama 		p9 |= L9P_OTRUNC;	/* but don't CLEAR yet */
365*134e1779SJakub Wojciech Klama 
366*134e1779SJakub Wojciech Klama 	flags |= O_NOCTTY | O_NOFOLLOW;
367*134e1779SJakub Wojciech Klama 
368*134e1779SJakub Wojciech Klama 	/*
369*134e1779SJakub Wojciech Klama 	 * L_O_CREAT seems to be noise, since we get separate open
370*134e1779SJakub Wojciech Klama 	 * and create.  But it is actually set sometimes.  We just
371*134e1779SJakub Wojciech Klama 	 * throw it out here; create ops must set it themselves and
372*134e1779SJakub Wojciech Klama 	 * open ops have no permissions bits and hence cannot create.
373*134e1779SJakub Wojciech Klama 	 *
374*134e1779SJakub Wojciech Klama 	 * L_O_EXCL does make sense on create ops, i.e., we can
375*134e1779SJakub Wojciech Klama 	 * take a create op with or without L_O_EXCL.  We pass that
376*134e1779SJakub Wojciech Klama 	 * through.
377*134e1779SJakub Wojciech Klama 	 */
378*134e1779SJakub Wojciech Klama 	CLEAR(L9P_L_O_CREAT);
379*134e1779SJakub Wojciech Klama 	CONVERT(L9P_L_O_EXCL, O_EXCL);
380*134e1779SJakub Wojciech Klama 	CONVERT(L9P_L_O_TRUNC, O_TRUNC);
381*134e1779SJakub Wojciech Klama 	CONVERT(L9P_L_O_DIRECTORY, O_DIRECTORY);
382*134e1779SJakub Wojciech Klama 	CONVERT(L9P_L_O_APPEND, O_APPEND);
383*134e1779SJakub Wojciech Klama 	CONVERT(L9P_L_O_NONBLOCK, O_NONBLOCK);
384*134e1779SJakub Wojciech Klama 
385*134e1779SJakub Wojciech Klama 	/*
386*134e1779SJakub Wojciech Klama 	 * Discard these as useless noise at our (server) end.
387*134e1779SJakub Wojciech Klama 	 * (NOATIME might be useful but we can only set it on a
388*134e1779SJakub Wojciech Klama 	 * per-mount basis.)
389*134e1779SJakub Wojciech Klama 	 */
390*134e1779SJakub Wojciech Klama 	CLEAR(L9P_L_O_CLOEXEC);
391*134e1779SJakub Wojciech Klama 	CLEAR(L9P_L_O_DIRECT);
392*134e1779SJakub Wojciech Klama 	CLEAR(L9P_L_O_DSYNC);
393*134e1779SJakub Wojciech Klama 	CLEAR(L9P_L_O_FASYNC);
394*134e1779SJakub Wojciech Klama 	CLEAR(L9P_L_O_LARGEFILE);
395*134e1779SJakub Wojciech Klama 	CLEAR(L9P_L_O_NOATIME);
396*134e1779SJakub Wojciech Klama 	CLEAR(L9P_L_O_NOCTTY);
397*134e1779SJakub Wojciech Klama 	CLEAR(L9P_L_O_NOFOLLOW);
398*134e1779SJakub Wojciech Klama 	CLEAR(L9P_L_O_SYNC);
399*134e1779SJakub Wojciech Klama 
400*134e1779SJakub Wojciech Klama 	if (l_mode != 0) {
401*134e1779SJakub Wojciech Klama 		L9P_LOG(L9P_INFO,
402*134e1779SJakub Wojciech Klama 		    "fs_oflags_dotl: untranslated bits: %#x",
403*134e1779SJakub Wojciech Klama 		    (unsigned)l_mode);
404*134e1779SJakub Wojciech Klama 		return (EINVAL);
405*134e1779SJakub Wojciech Klama 	}
406*134e1779SJakub Wojciech Klama 
407*134e1779SJakub Wojciech Klama 	*aflags = flags;
408*134e1779SJakub Wojciech Klama 	*ap9 = p9;
409*134e1779SJakub Wojciech Klama 	return (0);
410*134e1779SJakub Wojciech Klama #undef CLEAR
411*134e1779SJakub Wojciech Klama #undef CONVERT
412*134e1779SJakub Wojciech Klama }
413*134e1779SJakub Wojciech Klama 
414*134e1779SJakub Wojciech Klama static struct passwd *
fs_getpwuid(struct fs_softc * sc,uid_t uid,struct r_pgdata * pg)415*134e1779SJakub Wojciech Klama fs_getpwuid(struct fs_softc *sc, uid_t uid, struct r_pgdata *pg)
416*134e1779SJakub Wojciech Klama {
417*134e1779SJakub Wojciech Klama #if defined(WITH_CASPER)
418*134e1779SJakub Wojciech Klama 	return (r_cap_getpwuid(sc->fs_cappwd, uid, pg));
419*134e1779SJakub Wojciech Klama #else
420*134e1779SJakub Wojciech Klama 	(void)sc;
421*134e1779SJakub Wojciech Klama 	return (r_getpwuid(uid, pg));
422*134e1779SJakub Wojciech Klama #endif
423*134e1779SJakub Wojciech Klama }
424*134e1779SJakub Wojciech Klama 
425*134e1779SJakub Wojciech Klama static struct group *
fs_getgrgid(struct fs_softc * sc,gid_t gid,struct r_pgdata * pg)426*134e1779SJakub Wojciech Klama fs_getgrgid(struct fs_softc *sc, gid_t gid, struct r_pgdata *pg)
427*134e1779SJakub Wojciech Klama {
428*134e1779SJakub Wojciech Klama #if defined(WITH_CASPER)
429*134e1779SJakub Wojciech Klama 	return (r_cap_getgrgid(sc->fs_capgrp, gid, pg));
430*134e1779SJakub Wojciech Klama #else
431*134e1779SJakub Wojciech Klama 	(void)sc;
432*134e1779SJakub Wojciech Klama 	return (r_getgrgid(gid, pg));
433*134e1779SJakub Wojciech Klama #endif
434*134e1779SJakub Wojciech Klama }
435*134e1779SJakub Wojciech Klama 
436*134e1779SJakub Wojciech Klama /*
437*134e1779SJakub Wojciech Klama  * Build full name of file by appending given name to directory name.
438*134e1779SJakub Wojciech Klama  */
439*134e1779SJakub Wojciech Klama static int
fs_buildname(struct l9p_fid * dir,char * name,char * buf,size_t size)440*134e1779SJakub Wojciech Klama fs_buildname(struct l9p_fid *dir, char *name, char *buf, size_t size)
441*134e1779SJakub Wojciech Klama {
442*134e1779SJakub Wojciech Klama 	struct fs_fid *dirf = dir->lo_aux;
443*134e1779SJakub Wojciech Klama 	size_t dlen, nlen1;
444*134e1779SJakub Wojciech Klama 
445*134e1779SJakub Wojciech Klama 	assert(dirf != NULL);
446*134e1779SJakub Wojciech Klama 	dlen = strlen(dirf->ff_name);
447*134e1779SJakub Wojciech Klama 	nlen1 = strlen(name) + 1;	/* +1 for '\0' */
448*134e1779SJakub Wojciech Klama 	if (dlen + 1 + nlen1 > size)
449*134e1779SJakub Wojciech Klama 		return (ENAMETOOLONG);
450*134e1779SJakub Wojciech Klama 	memcpy(buf, dirf->ff_name, dlen);
451*134e1779SJakub Wojciech Klama 	buf[dlen] = '/';
452*134e1779SJakub Wojciech Klama 	memcpy(buf + dlen + 1, name, nlen1);
453*134e1779SJakub Wojciech Klama 	return (0);
454*134e1779SJakub Wojciech Klama }
455*134e1779SJakub Wojciech Klama 
456*134e1779SJakub Wojciech Klama /*
457*134e1779SJakub Wojciech Klama  * Build parent name of file by splitting it off.  Return an error
458*134e1779SJakub Wojciech Klama  * if the given fid represents the root, so that there is no such
459*134e1779SJakub Wojciech Klama  * parent, or if the discovered parent is not a directory.
460*134e1779SJakub Wojciech Klama  */
461*134e1779SJakub Wojciech Klama static int
fs_pdir(struct fs_softc * sc __unused,struct l9p_fid * fid,char * buf,size_t size,struct stat * st)462*134e1779SJakub Wojciech Klama fs_pdir(struct fs_softc *sc __unused, struct l9p_fid *fid, char *buf,
463*134e1779SJakub Wojciech Klama     size_t size, struct stat *st)
464*134e1779SJakub Wojciech Klama {
465*134e1779SJakub Wojciech Klama 	struct fs_fid *ff;
466*134e1779SJakub Wojciech Klama 	char *path;
467*134e1779SJakub Wojciech Klama 
468*134e1779SJakub Wojciech Klama 	ff = fid->lo_aux;
469*134e1779SJakub Wojciech Klama 	assert(ff != NULL);
470*134e1779SJakub Wojciech Klama 	path = ff->ff_name;
471*134e1779SJakub Wojciech Klama 	path = r_dirname(path, buf, size);
472*134e1779SJakub Wojciech Klama 	if (path == NULL)
473*134e1779SJakub Wojciech Klama 		return (ENAMETOOLONG);
474*134e1779SJakub Wojciech Klama 	if (fstatat(ff->ff_dirfd, path, st, AT_SYMLINK_NOFOLLOW) != 0)
475*134e1779SJakub Wojciech Klama 		return (errno);
476*134e1779SJakub Wojciech Klama 	if (!S_ISDIR(st->st_mode))
477*134e1779SJakub Wojciech Klama 		return (ENOTDIR);
478*134e1779SJakub Wojciech Klama 	return (0);
479*134e1779SJakub Wojciech Klama }
480*134e1779SJakub Wojciech Klama 
481*134e1779SJakub Wojciech Klama /*
482*134e1779SJakub Wojciech Klama  * Like fs_buildname() but for adding a file name to a buffer
483*134e1779SJakub Wojciech Klama  * already holding a directory name.  Essentially does
484*134e1779SJakub Wojciech Klama  *     strcat(dbuf, "/");
485*134e1779SJakub Wojciech Klama  *     strcat(dbuf, fname);
486*134e1779SJakub Wojciech Klama  * but with size checking and an ENAMETOOLONG error as needed.
487*134e1779SJakub Wojciech Klama  *
488*134e1779SJakub Wojciech Klama  * (Think of the function name as "directory plus-equals file".)
489*134e1779SJakub Wojciech Klama  */
490*134e1779SJakub Wojciech Klama static int
fs_dpf(char * dbuf,char * fname,size_t size)491*134e1779SJakub Wojciech Klama fs_dpf(char *dbuf, char *fname, size_t size)
492*134e1779SJakub Wojciech Klama {
493*134e1779SJakub Wojciech Klama 	size_t dlen, nlen1;
494*134e1779SJakub Wojciech Klama 
495*134e1779SJakub Wojciech Klama 	dlen = strlen(dbuf);
496*134e1779SJakub Wojciech Klama 	nlen1 = strlen(fname) + 1;
497*134e1779SJakub Wojciech Klama 	if (dlen + 1 + nlen1 > size)
498*134e1779SJakub Wojciech Klama 		return (ENAMETOOLONG);
499*134e1779SJakub Wojciech Klama 	dbuf[dlen] = '/';
500*134e1779SJakub Wojciech Klama 	memcpy(dbuf + dlen + 1, fname, nlen1);
501*134e1779SJakub Wojciech Klama 	return (0);
502*134e1779SJakub Wojciech Klama }
503*134e1779SJakub Wojciech Klama 
504*134e1779SJakub Wojciech Klama /*
505*134e1779SJakub Wojciech Klama  * Prepare to create a new directory entry (open with O_CREAT,
506*134e1779SJakub Wojciech Klama  * mkdir, etc -- any operation that creates a new inode),
507*134e1779SJakub Wojciech Klama  * operating in parent data <dir>, based on authinfo <ai> and
508*134e1779SJakub Wojciech Klama  * effective gid <egid>.
509*134e1779SJakub Wojciech Klama  *
510*134e1779SJakub Wojciech Klama  * The new entity should be owned by user/group <*nuid, *ngid>,
511*134e1779SJakub Wojciech Klama  * if it's really a new entity.  It will be a directory if isdir.
512*134e1779SJakub Wojciech Klama  *
513*134e1779SJakub Wojciech Klama  * Returns an error number if the entry should not be created
514*134e1779SJakub Wojciech Klama  * (e.g., read-only file system or no permission to write in
515*134e1779SJakub Wojciech Klama  * parent directory).  Always sets *nuid and *ngid on success:
516*134e1779SJakub Wojciech Klama  * in the worst case, when there is no available ID, this will
517*134e1779SJakub Wojciech Klama  * use the parent directory's IDs.  Fills in <*st> on success.
518*134e1779SJakub Wojciech Klama  */
519*134e1779SJakub Wojciech Klama static int
fs_nde(struct fs_softc * sc,struct l9p_fid * dir,bool isdir,gid_t egid,struct stat * st,uid_t * nuid,gid_t * ngid)520*134e1779SJakub Wojciech Klama fs_nde(struct fs_softc *sc, struct l9p_fid *dir, bool isdir, gid_t egid,
521*134e1779SJakub Wojciech Klama     struct stat *st, uid_t *nuid, gid_t *ngid)
522*134e1779SJakub Wojciech Klama {
523*134e1779SJakub Wojciech Klama 	struct fs_fid *dirf;
524*134e1779SJakub Wojciech Klama 	struct fs_authinfo *ai;
525*134e1779SJakub Wojciech Klama 	int32_t op;
526*134e1779SJakub Wojciech Klama 	int error;
527*134e1779SJakub Wojciech Klama 
528*134e1779SJakub Wojciech Klama 	if (sc->fs_readonly)
529*134e1779SJakub Wojciech Klama 		return (EROFS);
530*134e1779SJakub Wojciech Klama 	dirf = dir->lo_aux;
531*134e1779SJakub Wojciech Klama 	assert(dirf != NULL);
532*134e1779SJakub Wojciech Klama 	if (fstatat(dirf->ff_dirfd, dirf->ff_name, st,
533*134e1779SJakub Wojciech Klama 	    AT_SYMLINK_NOFOLLOW) != 0)
534*134e1779SJakub Wojciech Klama 		return (errno);
535*134e1779SJakub Wojciech Klama 	if (!S_ISDIR(st->st_mode))
536*134e1779SJakub Wojciech Klama 		return (ENOTDIR);
537*134e1779SJakub Wojciech Klama 	dirf = dir->lo_aux;
538*134e1779SJakub Wojciech Klama 	ai = dirf->ff_ai;
539*134e1779SJakub Wojciech Klama 	fillacl(dirf);
540*134e1779SJakub Wojciech Klama 	op = isdir ? L9P_ACE_ADD_SUBDIRECTORY : L9P_ACE_ADD_FILE;
541*134e1779SJakub Wojciech Klama 	error = check_access(op, dirf->ff_acl, st, NULL, NULL, ai, egid);
542*134e1779SJakub Wojciech Klama 	if (error)
543*134e1779SJakub Wojciech Klama 		return (EPERM);
544*134e1779SJakub Wojciech Klama 
545*134e1779SJakub Wojciech Klama 	*nuid = ai->ai_uid != (uid_t)-1 ? ai->ai_uid : st->st_uid;
546*134e1779SJakub Wojciech Klama 	*ngid = egid != (gid_t)-1 ? egid :
547*134e1779SJakub Wojciech Klama 	    ai->ai_ngids > 0 ?  ai->ai_gids[0] : st->st_gid;
548*134e1779SJakub Wojciech Klama 	return (0);
549*134e1779SJakub Wojciech Klama }
550*134e1779SJakub Wojciech Klama 
551*134e1779SJakub Wojciech Klama /*
552*134e1779SJakub Wojciech Klama  * Allocate new open-file data structure to attach to a fid.
553*134e1779SJakub Wojciech Klama  *
554*134e1779SJakub Wojciech Klama  * The new file's authinfo is the same as the old one's, and
555*134e1779SJakub Wojciech Klama  * we gain a reference.
556*134e1779SJakub Wojciech Klama  */
557*134e1779SJakub Wojciech Klama static struct fs_fid *
open_fid(int dirfd,const char * path,struct fs_authinfo * ai,bool creating)558*134e1779SJakub Wojciech Klama open_fid(int dirfd, const char *path, struct fs_authinfo *ai, bool creating)
559*134e1779SJakub Wojciech Klama {
560*134e1779SJakub Wojciech Klama 	struct fs_fid *ret;
561*134e1779SJakub Wojciech Klama 	uint32_t newcount;
562*134e1779SJakub Wojciech Klama 	int error;
563*134e1779SJakub Wojciech Klama 
564*134e1779SJakub Wojciech Klama 	ret = l9p_calloc(1, sizeof(*ret));
565*134e1779SJakub Wojciech Klama 	error = pthread_mutex_init(&ret->ff_mtx, NULL);
566*134e1779SJakub Wojciech Klama 	if (error) {
567*134e1779SJakub Wojciech Klama 		free(ret);
568*134e1779SJakub Wojciech Klama 		return (NULL);
569*134e1779SJakub Wojciech Klama 	}
570*134e1779SJakub Wojciech Klama 	ret->ff_fd = -1;
571*134e1779SJakub Wojciech Klama 	ret->ff_dirfd = dirfd;
572*134e1779SJakub Wojciech Klama 	ret->ff_name = strdup(path);
573*134e1779SJakub Wojciech Klama 	if (ret->ff_name == NULL) {
574*134e1779SJakub Wojciech Klama 		pthread_mutex_destroy(&ret->ff_mtx);
575*134e1779SJakub Wojciech Klama 		free(ret);
576*134e1779SJakub Wojciech Klama 		return (NULL);
577*134e1779SJakub Wojciech Klama 	}
578*134e1779SJakub Wojciech Klama 	pthread_mutex_lock(&ai->ai_mtx);
579*134e1779SJakub Wojciech Klama 	newcount = ++ai->ai_refcnt;
580*134e1779SJakub Wojciech Klama 	pthread_mutex_unlock(&ai->ai_mtx);
581*134e1779SJakub Wojciech Klama 	/*
582*134e1779SJakub Wojciech Klama 	 * If we just incremented the count to 1, we're the *first*
583*134e1779SJakub Wojciech Klama 	 * reference.  This is only allowed when creating the authinfo,
584*134e1779SJakub Wojciech Klama 	 * otherwise it means something has gone wrong.  This cannot
585*134e1779SJakub Wojciech Klama 	 * catch every bad (re)use of a freed authinfo but it may catch
586*134e1779SJakub Wojciech Klama 	 * a few.
587*134e1779SJakub Wojciech Klama 	 */
588*134e1779SJakub Wojciech Klama 	assert(newcount > 1 || creating);
589*134e1779SJakub Wojciech Klama 	L9P_LOG(L9P_DEBUG, "authinfo %p now used by %lu",
590*134e1779SJakub Wojciech Klama 	    (void *)ai, (u_long)newcount);
591*134e1779SJakub Wojciech Klama 	ret->ff_ai = ai;
592*134e1779SJakub Wojciech Klama 	return (ret);
593*134e1779SJakub Wojciech Klama }
594*134e1779SJakub Wojciech Klama 
595*134e1779SJakub Wojciech Klama static void
dostat(struct fs_softc * sc,struct l9p_stat * s,char * name,struct stat * buf,bool dotu)596*134e1779SJakub Wojciech Klama dostat(struct fs_softc *sc, struct l9p_stat *s, char *name,
597*134e1779SJakub Wojciech Klama     struct stat *buf, bool dotu)
598*134e1779SJakub Wojciech Klama {
599*134e1779SJakub Wojciech Klama 	struct passwd *user;
600*134e1779SJakub Wojciech Klama 	struct group *group;
601*134e1779SJakub Wojciech Klama 
602*134e1779SJakub Wojciech Klama 	memset(s, 0, sizeof(struct l9p_stat));
603*134e1779SJakub Wojciech Klama 
604*134e1779SJakub Wojciech Klama 	generate_qid(buf, &s->qid);
605*134e1779SJakub Wojciech Klama 
606*134e1779SJakub Wojciech Klama 	s->type = 0;
607*134e1779SJakub Wojciech Klama 	s->dev = 0;
608*134e1779SJakub Wojciech Klama 	s->mode = buf->st_mode & 0777;
609*134e1779SJakub Wojciech Klama 
610*134e1779SJakub Wojciech Klama 	if (S_ISDIR(buf->st_mode))
611*134e1779SJakub Wojciech Klama 		s->mode |= L9P_DMDIR;
612*134e1779SJakub Wojciech Klama 
613*134e1779SJakub Wojciech Klama 	if (S_ISLNK(buf->st_mode) && dotu)
614*134e1779SJakub Wojciech Klama 		s->mode |= L9P_DMSYMLINK;
615*134e1779SJakub Wojciech Klama 
616*134e1779SJakub Wojciech Klama 	if (S_ISCHR(buf->st_mode) || S_ISBLK(buf->st_mode))
617*134e1779SJakub Wojciech Klama 		s->mode |= L9P_DMDEVICE;
618*134e1779SJakub Wojciech Klama 
619*134e1779SJakub Wojciech Klama 	if (S_ISSOCK(buf->st_mode))
620*134e1779SJakub Wojciech Klama 		s->mode |= L9P_DMSOCKET;
621*134e1779SJakub Wojciech Klama 
622*134e1779SJakub Wojciech Klama 	if (S_ISFIFO(buf->st_mode))
623*134e1779SJakub Wojciech Klama 		s->mode |= L9P_DMNAMEDPIPE;
624*134e1779SJakub Wojciech Klama 
625*134e1779SJakub Wojciech Klama 	s->atime = (uint32_t)buf->st_atime;
626*134e1779SJakub Wojciech Klama 	s->mtime = (uint32_t)buf->st_mtime;
627*134e1779SJakub Wojciech Klama 	s->length = (uint64_t)buf->st_size;
628*134e1779SJakub Wojciech Klama 
629*134e1779SJakub Wojciech Klama 	s->name = r_basename(name, NULL, 0);
630*134e1779SJakub Wojciech Klama 
631*134e1779SJakub Wojciech Klama 	if (!dotu) {
632*134e1779SJakub Wojciech Klama 		struct r_pgdata udata, gdata;
633*134e1779SJakub Wojciech Klama 
634*134e1779SJakub Wojciech Klama 		user = fs_getpwuid(sc, buf->st_uid, &udata);
635*134e1779SJakub Wojciech Klama 		group = fs_getgrgid(sc, buf->st_gid, &gdata);
636*134e1779SJakub Wojciech Klama 		s->uid = user != NULL ? strdup(user->pw_name) : NULL;
637*134e1779SJakub Wojciech Klama 		s->gid = group != NULL ? strdup(group->gr_name) : NULL;
638*134e1779SJakub Wojciech Klama 		s->muid = user != NULL ? strdup(user->pw_name) : NULL;
639*134e1779SJakub Wojciech Klama 		r_pgfree(&udata);
640*134e1779SJakub Wojciech Klama 		r_pgfree(&gdata);
641*134e1779SJakub Wojciech Klama 	} else {
642*134e1779SJakub Wojciech Klama 		/*
643*134e1779SJakub Wojciech Klama 		 * When using 9P2000.u, we don't need to bother about
644*134e1779SJakub Wojciech Klama 		 * providing user and group names in textual form.
645*134e1779SJakub Wojciech Klama 		 *
646*134e1779SJakub Wojciech Klama 		 * NB: if the asprintf()s fail, s->extension should
647*134e1779SJakub Wojciech Klama 		 * be unset so we can ignore these.
648*134e1779SJakub Wojciech Klama 		 */
649*134e1779SJakub Wojciech Klama 		s->n_uid = buf->st_uid;
650*134e1779SJakub Wojciech Klama 		s->n_gid = buf->st_gid;
651*134e1779SJakub Wojciech Klama 		s->n_muid = buf->st_uid;
652*134e1779SJakub Wojciech Klama 
653*134e1779SJakub Wojciech Klama 		if (S_ISLNK(buf->st_mode)) {
654*134e1779SJakub Wojciech Klama 			char target[MAXPATHLEN];
655*134e1779SJakub Wojciech Klama 			ssize_t ret = readlink(name, target, MAXPATHLEN);
656*134e1779SJakub Wojciech Klama 
657*134e1779SJakub Wojciech Klama 			if (ret < 0) {
658*134e1779SJakub Wojciech Klama 				s->extension = NULL;
659*134e1779SJakub Wojciech Klama 				return;
660*134e1779SJakub Wojciech Klama 			}
661*134e1779SJakub Wojciech Klama 
662*134e1779SJakub Wojciech Klama 			s->extension = strndup(target, (size_t)ret);
663*134e1779SJakub Wojciech Klama 		}
664*134e1779SJakub Wojciech Klama 
665*134e1779SJakub Wojciech Klama 		if (S_ISBLK(buf->st_mode)) {
666*134e1779SJakub Wojciech Klama 			asprintf(&s->extension, "b %d %d", major(buf->st_rdev),
667*134e1779SJakub Wojciech Klama 			    minor(buf->st_rdev));
668*134e1779SJakub Wojciech Klama 		}
669*134e1779SJakub Wojciech Klama 
670*134e1779SJakub Wojciech Klama 		if (S_ISCHR(buf->st_mode)) {
671*134e1779SJakub Wojciech Klama 			asprintf(&s->extension, "c %d %d", major(buf->st_rdev),
672*134e1779SJakub Wojciech Klama 			    minor(buf->st_rdev));
673*134e1779SJakub Wojciech Klama 		}
674*134e1779SJakub Wojciech Klama 	}
675*134e1779SJakub Wojciech Klama }
676*134e1779SJakub Wojciech Klama 
dostatfs(struct l9p_statfs * out,struct statfs * in,long namelen)677*134e1779SJakub Wojciech Klama static void dostatfs(struct l9p_statfs *out, struct statfs *in, long namelen)
678*134e1779SJakub Wojciech Klama {
679*134e1779SJakub Wojciech Klama 
680*134e1779SJakub Wojciech Klama 	out->type = L9P_FSTYPE;
681*134e1779SJakub Wojciech Klama 	out->bsize = in->f_bsize;
682*134e1779SJakub Wojciech Klama 	out->blocks = in->f_blocks;
683*134e1779SJakub Wojciech Klama 	out->bfree = in->f_bfree;
684*134e1779SJakub Wojciech Klama 	out->bavail = in->f_bavail;
685*134e1779SJakub Wojciech Klama 	out->files = in->f_files;
686*134e1779SJakub Wojciech Klama 	out->ffree = in->f_ffree;
687*134e1779SJakub Wojciech Klama 	out->namelen = (uint32_t)namelen;
688*134e1779SJakub Wojciech Klama 	out->fsid = ((uint64_t)in->f_fsid.val[0] << 32) |
689*134e1779SJakub Wojciech Klama 	    (uint64_t)in->f_fsid.val[1];
690*134e1779SJakub Wojciech Klama }
691*134e1779SJakub Wojciech Klama 
692*134e1779SJakub Wojciech Klama static void
generate_qid(struct stat * buf,struct l9p_qid * qid)693*134e1779SJakub Wojciech Klama generate_qid(struct stat *buf, struct l9p_qid *qid)
694*134e1779SJakub Wojciech Klama {
695*134e1779SJakub Wojciech Klama 	qid->path = buf->st_ino;
696*134e1779SJakub Wojciech Klama 	qid->version = 0;
697*134e1779SJakub Wojciech Klama 
698*134e1779SJakub Wojciech Klama 	if (S_ISREG(buf->st_mode))
699*134e1779SJakub Wojciech Klama 		qid->type |= L9P_QTFILE;
700*134e1779SJakub Wojciech Klama 
701*134e1779SJakub Wojciech Klama 	if (S_ISDIR(buf->st_mode))
702*134e1779SJakub Wojciech Klama 		qid->type |= L9P_QTDIR;
703*134e1779SJakub Wojciech Klama 
704*134e1779SJakub Wojciech Klama 	if (S_ISLNK(buf->st_mode))
705*134e1779SJakub Wojciech Klama 		qid->type |= L9P_QTSYMLINK;
706*134e1779SJakub Wojciech Klama }
707*134e1779SJakub Wojciech Klama 
708*134e1779SJakub Wojciech Klama /*
709*134e1779SJakub Wojciech Klama  * Fill in ff->ff_acl if it's not set yet.  Skip if the "don't use
710*134e1779SJakub Wojciech Klama  * ACLs" flag is set, and use the flag to remember failure so
711*134e1779SJakub Wojciech Klama  * we don't bother retrying either.
712*134e1779SJakub Wojciech Klama  */
713*134e1779SJakub Wojciech Klama static void
fillacl(struct fs_fid * ff)714*134e1779SJakub Wojciech Klama fillacl(struct fs_fid *ff)
715*134e1779SJakub Wojciech Klama {
716*134e1779SJakub Wojciech Klama 
717*134e1779SJakub Wojciech Klama 	if (ff->ff_acl == NULL && (ff->ff_flags & FF_NO_NFSV4_ACL) == 0) {
718*134e1779SJakub Wojciech Klama 		ff->ff_acl = look_for_nfsv4_acl(ff, ff->ff_fd, ff->ff_name);
719*134e1779SJakub Wojciech Klama 		if (ff->ff_acl == NULL)
720*134e1779SJakub Wojciech Klama 			ff->ff_flags |= FF_NO_NFSV4_ACL;
721*134e1779SJakub Wojciech Klama 	}
722*134e1779SJakub Wojciech Klama }
723*134e1779SJakub Wojciech Klama 
724*134e1779SJakub Wojciech Klama /*
725*134e1779SJakub Wojciech Klama  * Get an ACL given fd and/or path name.  We check for the "don't get
726*134e1779SJakub Wojciech Klama  * ACL" flag in the given ff_fid data structure first, but don't set
727*134e1779SJakub Wojciech Klama  * the flag here.  The fillacl() code is similar but will set the
728*134e1779SJakub Wojciech Klama  * flag; it also uses the ff_fd and ff_name directly.
729*134e1779SJakub Wojciech Klama  *
730*134e1779SJakub Wojciech Klama  * (This is used to get ACLs for parent directories, for instance.)
731*134e1779SJakub Wojciech Klama  */
732*134e1779SJakub Wojciech Klama static struct l9p_acl *
getacl(struct fs_fid * ff,int fd,const char * path)733*134e1779SJakub Wojciech Klama getacl(struct fs_fid *ff, int fd, const char *path)
734*134e1779SJakub Wojciech Klama {
735*134e1779SJakub Wojciech Klama 
736*134e1779SJakub Wojciech Klama 	if (ff->ff_flags & FF_NO_NFSV4_ACL)
737*134e1779SJakub Wojciech Klama 		return (NULL);
738*134e1779SJakub Wojciech Klama 	return look_for_nfsv4_acl(ff, fd, path);
739*134e1779SJakub Wojciech Klama }
740*134e1779SJakub Wojciech Klama 
741*134e1779SJakub Wojciech Klama /*
742*134e1779SJakub Wojciech Klama  * Drop cached ff->ff_acl, e.g., after moving from one directory to
743*134e1779SJakub Wojciech Klama  * another, where inherited ACLs might change.
744*134e1779SJakub Wojciech Klama  */
745*134e1779SJakub Wojciech Klama static void
dropacl(struct fs_fid * ff)746*134e1779SJakub Wojciech Klama dropacl(struct fs_fid *ff)
747*134e1779SJakub Wojciech Klama {
748*134e1779SJakub Wojciech Klama 
749*134e1779SJakub Wojciech Klama 	l9p_acl_free(ff->ff_acl);
750*134e1779SJakub Wojciech Klama 	ff->ff_acl = NULL;
751*134e1779SJakub Wojciech Klama 	ff->ff_flags = ff->ff_ai->ai_flags;
752*134e1779SJakub Wojciech Klama }
753*134e1779SJakub Wojciech Klama 
754*134e1779SJakub Wojciech Klama /*
755*134e1779SJakub Wojciech Klama  * Check to see if we can find NFSv4 ACLs for the given file.
756*134e1779SJakub Wojciech Klama  * If we have an open fd, we can use that, otherwise we need
757*134e1779SJakub Wojciech Klama  * to use the path.
758*134e1779SJakub Wojciech Klama  */
759*134e1779SJakub Wojciech Klama static struct l9p_acl *
look_for_nfsv4_acl(struct fs_fid * ff,int fd,const char * path)760*134e1779SJakub Wojciech Klama look_for_nfsv4_acl(struct fs_fid *ff, int fd, const char *path)
761*134e1779SJakub Wojciech Klama {
762*134e1779SJakub Wojciech Klama 	struct l9p_acl *acl;
763*134e1779SJakub Wojciech Klama 	acl_t sysacl;
764*134e1779SJakub Wojciech Klama 	int doclose = 0;
765*134e1779SJakub Wojciech Klama 
766*134e1779SJakub Wojciech Klama 	if (fd < 0) {
767*134e1779SJakub Wojciech Klama 		fd = openat(ff->ff_dirfd, path, 0);
768*134e1779SJakub Wojciech Klama 		doclose = 1;
769*134e1779SJakub Wojciech Klama 	}
770*134e1779SJakub Wojciech Klama 
771*134e1779SJakub Wojciech Klama 	sysacl = acl_get_fd_np(fd, ACL_TYPE_NFS4);
772*134e1779SJakub Wojciech Klama 	if (sysacl == NULL) {
773*134e1779SJakub Wojciech Klama 		/*
774*134e1779SJakub Wojciech Klama 		 * EINVAL means no NFSv4 ACLs apply for this file.
775*134e1779SJakub Wojciech Klama 		 * Other error numbers indicate some kind of problem.
776*134e1779SJakub Wojciech Klama 		 */
777*134e1779SJakub Wojciech Klama 		if (errno != EINVAL) {
778*134e1779SJakub Wojciech Klama 			L9P_LOG(L9P_ERROR,
779*134e1779SJakub Wojciech Klama 			    "error retrieving NFSv4 ACL from "
780*134e1779SJakub Wojciech Klama 			    "fdesc %d (%s): %s", fd,
781*134e1779SJakub Wojciech Klama 			    path, strerror(errno));
782*134e1779SJakub Wojciech Klama 		}
783*134e1779SJakub Wojciech Klama 
784*134e1779SJakub Wojciech Klama 		if (doclose)
785*134e1779SJakub Wojciech Klama 			close(fd);
786*134e1779SJakub Wojciech Klama 
787*134e1779SJakub Wojciech Klama 		return (NULL);
788*134e1779SJakub Wojciech Klama 	}
789*134e1779SJakub Wojciech Klama #if defined(HAVE_FREEBSD_ACLS)
790*134e1779SJakub Wojciech Klama 	acl = l9p_freebsd_nfsv4acl_to_acl(sysacl);
791*134e1779SJakub Wojciech Klama #else
792*134e1779SJakub Wojciech Klama 	acl = NULL; /* XXX need a l9p_darwin_acl_to_acl */
793*134e1779SJakub Wojciech Klama #endif
794*134e1779SJakub Wojciech Klama 	acl_free(sysacl);
795*134e1779SJakub Wojciech Klama 
796*134e1779SJakub Wojciech Klama 	if (doclose)
797*134e1779SJakub Wojciech Klama 		close(fd);
798*134e1779SJakub Wojciech Klama 
799*134e1779SJakub Wojciech Klama 	return (acl);
800*134e1779SJakub Wojciech Klama }
801*134e1779SJakub Wojciech Klama 
802*134e1779SJakub Wojciech Klama /*
803*134e1779SJakub Wojciech Klama  * Verify that the user whose authinfo is in <ai> and effective
804*134e1779SJakub Wojciech Klama  * group ID is <egid> ((gid_t)-1 means no egid supplied) has
805*134e1779SJakub Wojciech Klama  * permission to do something.
806*134e1779SJakub Wojciech Klama  *
807*134e1779SJakub Wojciech Klama  * The "something" may be rather complex: we allow NFSv4 style
808*134e1779SJakub Wojciech Klama  * operation masks here, and provide parent and child ACLs and
809*134e1779SJakub Wojciech Klama  * stat data.  At most one of pacl+pst and cacl+cst can be NULL,
810*134e1779SJakub Wojciech Klama  * unless ACLs are not supported; then pacl and cacl can both
811*134e1779SJakub Wojciech Klama  * be NULL but pst or cst must be non-NULL depending on the
812*134e1779SJakub Wojciech Klama  * operation.
813*134e1779SJakub Wojciech Klama  */
814*134e1779SJakub Wojciech Klama static int
check_access(int32_t opmask,struct l9p_acl * pacl,struct stat * pst,struct l9p_acl * cacl,struct stat * cst,struct fs_authinfo * ai,gid_t egid)815*134e1779SJakub Wojciech Klama check_access(int32_t opmask,
816*134e1779SJakub Wojciech Klama     struct l9p_acl *pacl, struct stat *pst,
817*134e1779SJakub Wojciech Klama     struct l9p_acl *cacl, struct stat *cst,
818*134e1779SJakub Wojciech Klama     struct fs_authinfo *ai, gid_t egid)
819*134e1779SJakub Wojciech Klama {
820*134e1779SJakub Wojciech Klama 	struct l9p_acl_check_args args;
821*134e1779SJakub Wojciech Klama 
822*134e1779SJakub Wojciech Klama 	/*
823*134e1779SJakub Wojciech Klama 	 * If we have ACLs, use them exclusively, ignoring Unix
824*134e1779SJakub Wojciech Klama 	 * permissions.  Otherwise, fall back on stat st_mode
825*134e1779SJakub Wojciech Klama 	 * bits, and allow super-user as well.
826*134e1779SJakub Wojciech Klama 	 */
827*134e1779SJakub Wojciech Klama 	args.aca_uid = ai->ai_uid;
828*134e1779SJakub Wojciech Klama 	args.aca_gid = egid;
829*134e1779SJakub Wojciech Klama 	args.aca_groups = ai->ai_gids;
830*134e1779SJakub Wojciech Klama 	args.aca_ngroups = (size_t)ai->ai_ngids;
831*134e1779SJakub Wojciech Klama 	args.aca_parent = pacl;
832*134e1779SJakub Wojciech Klama 	args.aca_pstat = pst;
833*134e1779SJakub Wojciech Klama 	args.aca_child = cacl;
834*134e1779SJakub Wojciech Klama 	args.aca_cstat = cst;
835*134e1779SJakub Wojciech Klama 	args.aca_aclmode = pacl == NULL && cacl == NULL
836*134e1779SJakub Wojciech Klama 	    ? L9P_ACM_STAT_MODE
837*134e1779SJakub Wojciech Klama 	    : L9P_ACM_NFS_ACL | L9P_ACM_ZFS_ACL;
838*134e1779SJakub Wojciech Klama 
839*134e1779SJakub Wojciech Klama 	args.aca_superuser = true;
840*134e1779SJakub Wojciech Klama 	return (l9p_acl_check_access(opmask, &args));
841*134e1779SJakub Wojciech Klama }
842*134e1779SJakub Wojciech Klama 
843*134e1779SJakub Wojciech Klama static int
fs_attach(void * softc,struct l9p_request * req)844*134e1779SJakub Wojciech Klama fs_attach(void *softc, struct l9p_request *req)
845*134e1779SJakub Wojciech Klama {
846*134e1779SJakub Wojciech Klama 	struct fs_authinfo *ai;
847*134e1779SJakub Wojciech Klama 	struct fs_softc *sc = (struct fs_softc *)softc;
848*134e1779SJakub Wojciech Klama 	struct fs_fid *file;
849*134e1779SJakub Wojciech Klama 	struct passwd *pwd;
850*134e1779SJakub Wojciech Klama 	struct stat st;
851*134e1779SJakub Wojciech Klama 	struct r_pgdata udata;
852*134e1779SJakub Wojciech Klama 	uint32_t n_uname;
853*134e1779SJakub Wojciech Klama 	gid_t *gids;
854*134e1779SJakub Wojciech Klama 	uid_t uid;
855*134e1779SJakub Wojciech Klama 	int error;
856*134e1779SJakub Wojciech Klama 	int ngroups;
857*134e1779SJakub Wojciech Klama 
858*134e1779SJakub Wojciech Klama 	assert(req->lr_fid != NULL);
859*134e1779SJakub Wojciech Klama 
860*134e1779SJakub Wojciech Klama 	/*
861*134e1779SJakub Wojciech Klama 	 * Single-thread pwd/group related items.  We have a reentrant
862*134e1779SJakub Wojciech Klama 	 * r_getpwuid but not a reentrant r_getpwnam, and l9p_getgrlist
863*134e1779SJakub Wojciech Klama 	 * may use non-reentrant C library getgr* routines.
864*134e1779SJakub Wojciech Klama 	 */
865*134e1779SJakub Wojciech Klama 	pthread_mutex_lock(&fs_attach_mutex);
866*134e1779SJakub Wojciech Klama 
867*134e1779SJakub Wojciech Klama 	n_uname = req->lr_req.tattach.n_uname;
868*134e1779SJakub Wojciech Klama 	if (n_uname != L9P_NONUNAME) {
869*134e1779SJakub Wojciech Klama 		uid = (uid_t)n_uname;
870*134e1779SJakub Wojciech Klama 		pwd = fs_getpwuid(sc, uid, &udata);
871*134e1779SJakub Wojciech Klama 		if (pwd == NULL)
872*134e1779SJakub Wojciech Klama 			L9P_LOG(L9P_DEBUG,
873*134e1779SJakub Wojciech Klama 			    "Tattach: uid %ld: no such user", (long)uid);
874*134e1779SJakub Wojciech Klama 	} else {
875*134e1779SJakub Wojciech Klama 		uid = (uid_t)-1;
876*134e1779SJakub Wojciech Klama #if defined(WITH_CASPER)
877*134e1779SJakub Wojciech Klama 		pwd = cap_getpwnam(sc->fs_cappwd, req->lr_req.tattach.uname);
878*134e1779SJakub Wojciech Klama #else
879*134e1779SJakub Wojciech Klama 		pwd = getpwnam(req->lr_req.tattach.uname);
880*134e1779SJakub Wojciech Klama #endif
881*134e1779SJakub Wojciech Klama 		if (pwd == NULL)
882*134e1779SJakub Wojciech Klama 			L9P_LOG(L9P_DEBUG,
883*134e1779SJakub Wojciech Klama 			    "Tattach: %s: no such user",
884*134e1779SJakub Wojciech Klama 			    req->lr_req.tattach.uname);
885*134e1779SJakub Wojciech Klama 	}
886*134e1779SJakub Wojciech Klama 
887*134e1779SJakub Wojciech Klama 	/*
888*134e1779SJakub Wojciech Klama 	 * If caller didn't give a numeric UID, pick it up from pwd
889*134e1779SJakub Wojciech Klama 	 * if possible.  If that doesn't work we can't continue.
890*134e1779SJakub Wojciech Klama 	 *
891*134e1779SJakub Wojciech Klama 	 * Note that pwd also supplies the group set.  This assumes
892*134e1779SJakub Wojciech Klama 	 * the server has the right mapping; this needs improvement.
893*134e1779SJakub Wojciech Klama 	 * We do at least support ai->ai_ngids==0 properly now though.
894*134e1779SJakub Wojciech Klama 	 */
895*134e1779SJakub Wojciech Klama 	if (uid == (uid_t)-1 && pwd != NULL)
896*134e1779SJakub Wojciech Klama 		uid = pwd->pw_uid;
897*134e1779SJakub Wojciech Klama 	if (uid == (uid_t)-1)
898*134e1779SJakub Wojciech Klama 		error = EPERM;
899*134e1779SJakub Wojciech Klama 	else {
900*134e1779SJakub Wojciech Klama 		error = 0;
901*134e1779SJakub Wojciech Klama 		if (fstat(sc->fs_rootfd, &st) != 0)
902*134e1779SJakub Wojciech Klama 			error = errno;
903*134e1779SJakub Wojciech Klama 		else if (!S_ISDIR(st.st_mode))
904*134e1779SJakub Wojciech Klama 			error = ENOTDIR;
905*134e1779SJakub Wojciech Klama 	}
906*134e1779SJakub Wojciech Klama 	if (error) {
907*134e1779SJakub Wojciech Klama 		pthread_mutex_unlock(&fs_attach_mutex);
908*134e1779SJakub Wojciech Klama 		L9P_LOG(L9P_DEBUG,
909*134e1779SJakub Wojciech Klama 		    "Tattach: denying uid=%ld access to rootdir: %s",
910*134e1779SJakub Wojciech Klama 		    (long)uid, strerror(error));
911*134e1779SJakub Wojciech Klama 		/*
912*134e1779SJakub Wojciech Klama 		 * Pass ENOENT and ENOTDIR through for diagnosis;
913*134e1779SJakub Wojciech Klama 		 * others become EPERM.  This should not leak too
914*134e1779SJakub Wojciech Klama 		 * much security.
915*134e1779SJakub Wojciech Klama 		 */
916*134e1779SJakub Wojciech Klama 		return (error == ENOENT || error == ENOTDIR ? error : EPERM);
917*134e1779SJakub Wojciech Klama 	}
918*134e1779SJakub Wojciech Klama 
919*134e1779SJakub Wojciech Klama 	if (pwd != NULL) {
920*134e1779SJakub Wojciech Klama 		/*
921*134e1779SJakub Wojciech Klama 		 * This either succeeds and fills in ngroups and
922*134e1779SJakub Wojciech Klama 		 * returns non-NULL, or fails and sets ngroups to 0
923*134e1779SJakub Wojciech Klama 		 * and returns NULL.  Either way ngroups is correct.
924*134e1779SJakub Wojciech Klama 		 */
925*134e1779SJakub Wojciech Klama 		gids = l9p_getgrlist(pwd->pw_name, pwd->pw_gid, &ngroups);
926*134e1779SJakub Wojciech Klama 	} else {
927*134e1779SJakub Wojciech Klama 		gids = NULL;
928*134e1779SJakub Wojciech Klama 		ngroups = 0;
929*134e1779SJakub Wojciech Klama 	}
930*134e1779SJakub Wojciech Klama 
931*134e1779SJakub Wojciech Klama 	/*
932*134e1779SJakub Wojciech Klama 	 * Done with pwd and group related items that may use
933*134e1779SJakub Wojciech Klama 	 * non-reentrant C library routines; allow other threads in.
934*134e1779SJakub Wojciech Klama 	 */
935*134e1779SJakub Wojciech Klama 	pthread_mutex_unlock(&fs_attach_mutex);
936*134e1779SJakub Wojciech Klama 
937*134e1779SJakub Wojciech Klama 	ai = malloc(sizeof(*ai) + (size_t)ngroups * sizeof(gid_t));
938*134e1779SJakub Wojciech Klama 	if (ai == NULL) {
939*134e1779SJakub Wojciech Klama 		free(gids);
940*134e1779SJakub Wojciech Klama 		return (ENOMEM);
941*134e1779SJakub Wojciech Klama 	}
942*134e1779SJakub Wojciech Klama 	error = pthread_mutex_init(&ai->ai_mtx, NULL);
943*134e1779SJakub Wojciech Klama 	if (error) {
944*134e1779SJakub Wojciech Klama 		free(gids);
945*134e1779SJakub Wojciech Klama 		free(ai);
946*134e1779SJakub Wojciech Klama 		return (error);
947*134e1779SJakub Wojciech Klama 	}
948*134e1779SJakub Wojciech Klama 	ai->ai_refcnt = 0;
949*134e1779SJakub Wojciech Klama 	ai->ai_uid = uid;
950*134e1779SJakub Wojciech Klama 	ai->ai_flags = 0;	/* XXX for now */
951*134e1779SJakub Wojciech Klama 	ai->ai_ngids = ngroups;
952*134e1779SJakub Wojciech Klama 	memcpy(ai->ai_gids, gids, (size_t)ngroups * sizeof(gid_t));
953*134e1779SJakub Wojciech Klama 	free(gids);
954*134e1779SJakub Wojciech Klama 
955*134e1779SJakub Wojciech Klama 	file = open_fid(sc->fs_rootfd, ".", ai, true);
956*134e1779SJakub Wojciech Klama 	if (file == NULL) {
957*134e1779SJakub Wojciech Klama 		pthread_mutex_destroy(&ai->ai_mtx);
958*134e1779SJakub Wojciech Klama 		free(ai);
959*134e1779SJakub Wojciech Klama 		return (ENOMEM);
960*134e1779SJakub Wojciech Klama 	}
961*134e1779SJakub Wojciech Klama 
962*134e1779SJakub Wojciech Klama 	req->lr_fid->lo_aux = file;
963*134e1779SJakub Wojciech Klama 	generate_qid(&st, &req->lr_resp.rattach.qid);
964*134e1779SJakub Wojciech Klama 	return (0);
965*134e1779SJakub Wojciech Klama }
966*134e1779SJakub Wojciech Klama 
967*134e1779SJakub Wojciech Klama static int
fs_clunk(void * softc __unused,struct l9p_fid * fid)968*134e1779SJakub Wojciech Klama fs_clunk(void *softc __unused, struct l9p_fid *fid)
969*134e1779SJakub Wojciech Klama {
970*134e1779SJakub Wojciech Klama 	struct fs_fid *file;
971*134e1779SJakub Wojciech Klama 
972*134e1779SJakub Wojciech Klama 	file = fid->lo_aux;
973*134e1779SJakub Wojciech Klama 	assert(file != NULL);
974*134e1779SJakub Wojciech Klama 
975*134e1779SJakub Wojciech Klama 	if (file->ff_dir) {
976*134e1779SJakub Wojciech Klama 		closedir(file->ff_dir);
977*134e1779SJakub Wojciech Klama 		file->ff_dir = NULL;
978*134e1779SJakub Wojciech Klama 	} else if (file->ff_fd != -1) {
979*134e1779SJakub Wojciech Klama 		close(file->ff_fd);
980*134e1779SJakub Wojciech Klama 		file->ff_fd = -1;
981*134e1779SJakub Wojciech Klama 	}
982*134e1779SJakub Wojciech Klama 
983*134e1779SJakub Wojciech Klama 	return (0);
984*134e1779SJakub Wojciech Klama }
985*134e1779SJakub Wojciech Klama 
986*134e1779SJakub Wojciech Klama /*
987*134e1779SJakub Wojciech Klama  * Create ops.
988*134e1779SJakub Wojciech Klama  *
989*134e1779SJakub Wojciech Klama  * We are to create a new file under some existing path,
990*134e1779SJakub Wojciech Klama  * where the new file's name is in the Tcreate request and the
991*134e1779SJakub Wojciech Klama  * existing path is due to a fid-based file (req->lr_fid).
992*134e1779SJakub Wojciech Klama  *
993*134e1779SJakub Wojciech Klama  * One op (create regular file) sets file->fd, the rest do not.
994*134e1779SJakub Wojciech Klama  */
995*134e1779SJakub Wojciech Klama static int
fs_create(void * softc,struct l9p_request * req)996*134e1779SJakub Wojciech Klama fs_create(void *softc, struct l9p_request *req)
997*134e1779SJakub Wojciech Klama {
998*134e1779SJakub Wojciech Klama 	struct l9p_fid *dir;
999*134e1779SJakub Wojciech Klama 	struct stat st;
1000*134e1779SJakub Wojciech Klama 	uint32_t dmperm;
1001*134e1779SJakub Wojciech Klama 	mode_t perm;
1002*134e1779SJakub Wojciech Klama 	char *name;
1003*134e1779SJakub Wojciech Klama 	int error;
1004*134e1779SJakub Wojciech Klama 
1005*134e1779SJakub Wojciech Klama 	dir = req->lr_fid;
1006*134e1779SJakub Wojciech Klama 	name = req->lr_req.tcreate.name;
1007*134e1779SJakub Wojciech Klama 	dmperm = req->lr_req.tcreate.perm;
1008*134e1779SJakub Wojciech Klama 	perm = (mode_t)(dmperm & 0777);
1009*134e1779SJakub Wojciech Klama 
1010*134e1779SJakub Wojciech Klama 	if (dmperm & L9P_DMDIR)
1011*134e1779SJakub Wojciech Klama 		error = fs_imkdir(softc, dir, name, true,
1012*134e1779SJakub Wojciech Klama 		    perm, (gid_t)-1, &st);
1013*134e1779SJakub Wojciech Klama 	else if (dmperm & L9P_DMSYMLINK)
1014*134e1779SJakub Wojciech Klama 		error = fs_isymlink(softc, dir, name,
1015*134e1779SJakub Wojciech Klama 		    req->lr_req.tcreate.extension, (gid_t)-1, &st);
1016*134e1779SJakub Wojciech Klama 	else if (dmperm & L9P_DMNAMEDPIPE)
1017*134e1779SJakub Wojciech Klama 		error = fs_imkfifo(softc, dir, name, true,
1018*134e1779SJakub Wojciech Klama 		    perm, (gid_t)-1, &st);
1019*134e1779SJakub Wojciech Klama 	else if (dmperm & L9P_DMSOCKET)
1020*134e1779SJakub Wojciech Klama 		error = fs_imksocket(softc, dir, name, true,
1021*134e1779SJakub Wojciech Klama 		    perm, (gid_t)-1, &st);
1022*134e1779SJakub Wojciech Klama 	else if (dmperm & L9P_DMDEVICE) {
1023*134e1779SJakub Wojciech Klama 		unsigned int major, minor;
1024*134e1779SJakub Wojciech Klama 		char type;
1025*134e1779SJakub Wojciech Klama 		dev_t dev;
1026*134e1779SJakub Wojciech Klama 
1027*134e1779SJakub Wojciech Klama 		/*
1028*134e1779SJakub Wojciech Klama 		 * ??? Should this be testing < 3?  For now, allow a single
1029*134e1779SJakub Wojciech Klama 		 * integer mode with minor==0 implied.
1030*134e1779SJakub Wojciech Klama 		 */
1031*134e1779SJakub Wojciech Klama 		minor = 0;
1032*134e1779SJakub Wojciech Klama 		if (sscanf(req->lr_req.tcreate.extension, "%c %u %u",
1033*134e1779SJakub Wojciech Klama 		    &type, &major, &minor) < 2) {
1034*134e1779SJakub Wojciech Klama 			return (EINVAL);
1035*134e1779SJakub Wojciech Klama 		}
1036*134e1779SJakub Wojciech Klama 
1037*134e1779SJakub Wojciech Klama 		switch (type) {
1038*134e1779SJakub Wojciech Klama 		case 'b':
1039*134e1779SJakub Wojciech Klama 			perm |= S_IFBLK;
1040*134e1779SJakub Wojciech Klama 			break;
1041*134e1779SJakub Wojciech Klama 		case 'c':
1042*134e1779SJakub Wojciech Klama 			perm |= S_IFCHR;
1043*134e1779SJakub Wojciech Klama 			break;
1044*134e1779SJakub Wojciech Klama 		default:
1045*134e1779SJakub Wojciech Klama 			return (EINVAL);
1046*134e1779SJakub Wojciech Klama 		}
1047*134e1779SJakub Wojciech Klama 		dev = makedev(major, minor);
1048*134e1779SJakub Wojciech Klama 		error = fs_imknod(softc, dir, name, true, perm, dev,
1049*134e1779SJakub Wojciech Klama 		    (gid_t)-1, &st);
1050*134e1779SJakub Wojciech Klama 	} else {
1051*134e1779SJakub Wojciech Klama 		enum l9p_omode p9;
1052*134e1779SJakub Wojciech Klama 		int flags;
1053*134e1779SJakub Wojciech Klama 
1054*134e1779SJakub Wojciech Klama 		p9 = req->lr_req.tcreate.mode;
1055*134e1779SJakub Wojciech Klama 		error = fs_oflags_dotu(p9, &flags);
1056*134e1779SJakub Wojciech Klama 		if (error)
1057*134e1779SJakub Wojciech Klama 			return (error);
1058*134e1779SJakub Wojciech Klama 		error = fs_icreate(softc, dir, name, flags,
1059*134e1779SJakub Wojciech Klama 		    true, perm, (gid_t)-1, &st);
1060*134e1779SJakub Wojciech Klama 		req->lr_resp.rcreate.iounit = req->lr_conn->lc_max_io_size;
1061*134e1779SJakub Wojciech Klama 	}
1062*134e1779SJakub Wojciech Klama 
1063*134e1779SJakub Wojciech Klama 	if (error == 0)
1064*134e1779SJakub Wojciech Klama 		generate_qid(&st, &req->lr_resp.rcreate.qid);
1065*134e1779SJakub Wojciech Klama 
1066*134e1779SJakub Wojciech Klama 	return (error);
1067*134e1779SJakub Wojciech Klama }
1068*134e1779SJakub Wojciech Klama 
1069*134e1779SJakub Wojciech Klama /*
1070*134e1779SJakub Wojciech Klama  * https://swtch.com/plan9port/man/man9/open.html and
1071*134e1779SJakub Wojciech Klama  * http://plan9.bell-labs.com/magic/man2html/5/open
1072*134e1779SJakub Wojciech Klama  * say that permissions are actually
1073*134e1779SJakub Wojciech Klama  *     perm & (~0666 | (dir.perm & 0666))
1074*134e1779SJakub Wojciech Klama  * for files, and
1075*134e1779SJakub Wojciech Klama  *     perm & (~0777 | (dir.perm & 0777))
1076*134e1779SJakub Wojciech Klama  * for directories.  That is, the parent directory may
1077*134e1779SJakub Wojciech Klama  * take away permissions granted by the operation.
1078*134e1779SJakub Wojciech Klama  *
1079*134e1779SJakub Wojciech Klama  * This seems a bit restrictive; probably
1080*134e1779SJakub Wojciech Klama  * there should be a control knob for this.
1081*134e1779SJakub Wojciech Klama  */
1082*134e1779SJakub Wojciech Klama static inline mode_t
fs_p9perm(mode_t perm,mode_t dir_perm,bool isdir)1083*134e1779SJakub Wojciech Klama fs_p9perm(mode_t perm, mode_t dir_perm, bool isdir)
1084*134e1779SJakub Wojciech Klama {
1085*134e1779SJakub Wojciech Klama 
1086*134e1779SJakub Wojciech Klama 	if (isdir)
1087*134e1779SJakub Wojciech Klama 		perm &= ~0777 | (dir_perm & 0777);
1088*134e1779SJakub Wojciech Klama 	else
1089*134e1779SJakub Wojciech Klama 		perm &= ~0666 | (dir_perm & 0666);
1090*134e1779SJakub Wojciech Klama 	return (perm);
1091*134e1779SJakub Wojciech Klama }
1092*134e1779SJakub Wojciech Klama 
1093*134e1779SJakub Wojciech Klama /*
1094*134e1779SJakub Wojciech Klama  * Internal form of create (plain file).
1095*134e1779SJakub Wojciech Klama  *
1096*134e1779SJakub Wojciech Klama  * Our caller takes care of splitting off all the special
1097*134e1779SJakub Wojciech Klama  * types of create (mknod, etc), so this is purely for files.
1098*134e1779SJakub Wojciech Klama  * We receive the fs_softc <softc>, the directory fid <dir>
1099*134e1779SJakub Wojciech Klama  * in which the new file is to be created, the name of the
1100*134e1779SJakub Wojciech Klama  * new file, a flag <isp9> indicating whether to do plan9 style
1101*134e1779SJakub Wojciech Klama  * permissions or Linux style permissions, the permissions <perm>,
1102*134e1779SJakub Wojciech Klama  * an effective group id <egid>, and a pointer to a stat structure
1103*134e1779SJakub Wojciech Klama  * <st> to fill in describing the final result on success.
1104*134e1779SJakub Wojciech Klama  *
1105*134e1779SJakub Wojciech Klama  * On successful create, the fid switches to the newly created
1106*134e1779SJakub Wojciech Klama  * file, which is now open; its associated file-name changes too.
1107*134e1779SJakub Wojciech Klama  *
1108*134e1779SJakub Wojciech Klama  * Note that the original (dir) fid is never currently open,
1109*134e1779SJakub Wojciech Klama  * so there is nothing to close.
1110*134e1779SJakub Wojciech Klama  */
1111*134e1779SJakub Wojciech Klama static int
fs_icreate(void * softc,struct l9p_fid * dir,char * name,int flags,bool isp9,mode_t perm,gid_t egid,struct stat * st)1112*134e1779SJakub Wojciech Klama fs_icreate(void *softc, struct l9p_fid *dir, char *name, int flags,
1113*134e1779SJakub Wojciech Klama     bool isp9, mode_t perm, gid_t egid, struct stat *st)
1114*134e1779SJakub Wojciech Klama {
1115*134e1779SJakub Wojciech Klama 	struct fs_fid *file;
1116*134e1779SJakub Wojciech Klama 	gid_t gid;
1117*134e1779SJakub Wojciech Klama 	uid_t uid;
1118*134e1779SJakub Wojciech Klama 	char newname[MAXPATHLEN];
1119*134e1779SJakub Wojciech Klama 	int error, fd;
1120*134e1779SJakub Wojciech Klama 
1121*134e1779SJakub Wojciech Klama 	file = dir->lo_aux;
1122*134e1779SJakub Wojciech Klama 
1123*134e1779SJakub Wojciech Klama 	/*
1124*134e1779SJakub Wojciech Klama 	 * Build full path name from directory + file name.  We'll
1125*134e1779SJakub Wojciech Klama 	 * check permissions on the parent directory, then race to
1126*134e1779SJakub Wojciech Klama 	 * create the file before anything bad happens like symlinks.
1127*134e1779SJakub Wojciech Klama 	 *
1128*134e1779SJakub Wojciech Klama 	 * (To close this race we need to use openat(), which is
1129*134e1779SJakub Wojciech Klama 	 * left for a later version of this code.)
1130*134e1779SJakub Wojciech Klama 	 */
1131*134e1779SJakub Wojciech Klama 	error = fs_buildname(dir, name, newname, sizeof(newname));
1132*134e1779SJakub Wojciech Klama 	if (error)
1133*134e1779SJakub Wojciech Klama 		return (error);
1134*134e1779SJakub Wojciech Klama 
1135*134e1779SJakub Wojciech Klama 	/* In case of success, we will need a new file->ff_name. */
1136*134e1779SJakub Wojciech Klama 	name = strdup(newname);
1137*134e1779SJakub Wojciech Klama 	if (name == NULL)
1138*134e1779SJakub Wojciech Klama 		return (ENOMEM);
1139*134e1779SJakub Wojciech Klama 
1140*134e1779SJakub Wojciech Klama 	/* Check create permission and compute new file ownership. */
1141*134e1779SJakub Wojciech Klama 	error = fs_nde(softc, dir, false, egid, st, &uid, &gid);
1142*134e1779SJakub Wojciech Klama 	if (error) {
1143*134e1779SJakub Wojciech Klama 		free(name);
1144*134e1779SJakub Wojciech Klama 		return (error);
1145*134e1779SJakub Wojciech Klama 	}
1146*134e1779SJakub Wojciech Klama 
1147*134e1779SJakub Wojciech Klama 	/* Adjust new-file permissions for Plan9 protocol. */
1148*134e1779SJakub Wojciech Klama 	if (isp9)
1149*134e1779SJakub Wojciech Klama 		perm = fs_p9perm(perm, st->st_mode, false);
1150*134e1779SJakub Wojciech Klama 
1151*134e1779SJakub Wojciech Klama 	/* Create is always exclusive so O_TRUNC is irrelevant. */
1152*134e1779SJakub Wojciech Klama 	fd = openat(file->ff_dirfd, newname, flags | O_CREAT | O_EXCL, perm);
1153*134e1779SJakub Wojciech Klama 	if (fd < 0) {
1154*134e1779SJakub Wojciech Klama 		error = errno;
1155*134e1779SJakub Wojciech Klama 		free(name);
1156*134e1779SJakub Wojciech Klama 		return (error);
1157*134e1779SJakub Wojciech Klama 	}
1158*134e1779SJakub Wojciech Klama 
1159*134e1779SJakub Wojciech Klama 	/* Fix permissions and owner. */
1160*134e1779SJakub Wojciech Klama 	if (fchmod(fd, perm) != 0 ||
1161*134e1779SJakub Wojciech Klama 	    fchown(fd, uid, gid) != 0 ||
1162*134e1779SJakub Wojciech Klama 	    fstat(fd, st) != 0) {
1163*134e1779SJakub Wojciech Klama 		error = errno;
1164*134e1779SJakub Wojciech Klama 		(void) close(fd);
1165*134e1779SJakub Wojciech Klama 		/* unlink(newname); ? */
1166*134e1779SJakub Wojciech Klama 		free(name);
1167*134e1779SJakub Wojciech Klama 		return (error);
1168*134e1779SJakub Wojciech Klama 	}
1169*134e1779SJakub Wojciech Klama 
1170*134e1779SJakub Wojciech Klama 	/* It *was* a directory; now it's a file, and it's open. */
1171*134e1779SJakub Wojciech Klama 	free(file->ff_name);
1172*134e1779SJakub Wojciech Klama 	file->ff_name = name;
1173*134e1779SJakub Wojciech Klama 	file->ff_fd = fd;
1174*134e1779SJakub Wojciech Klama 	return (0);
1175*134e1779SJakub Wojciech Klama }
1176*134e1779SJakub Wojciech Klama 
1177*134e1779SJakub Wojciech Klama /*
1178*134e1779SJakub Wojciech Klama  * Internal form of open: stat file and verify permissions (from p9
1179*134e1779SJakub Wojciech Klama  * argument), then open the file-or-directory, leaving the internal
1180*134e1779SJakub Wojciech Klama  * fs_fid fields set up.  If we cannot open the file, return a
1181*134e1779SJakub Wojciech Klama  * suitable error number, and leave everything unchanged.
1182*134e1779SJakub Wojciech Klama  *
1183*134e1779SJakub Wojciech Klama  * To mitigate the race between permissions testing and the actual
1184*134e1779SJakub Wojciech Klama  * open, we can stat the file twice (once with lstat() before open,
1185*134e1779SJakub Wojciech Klama  * then with fstat() after).  We assume O_NOFOLLOW is set in flags,
1186*134e1779SJakub Wojciech Klama  * so if some other race-winner substitutes in a symlink we won't
1187*134e1779SJakub Wojciech Klama  * open it here.  (However, embedded symlinks, if they occur, are
1188*134e1779SJakub Wojciech Klama  * still an issue.  Ideally we would like to have an O_NEVERFOLLOW
1189*134e1779SJakub Wojciech Klama  * that fails on embedded symlinks, and a way to pass this to
1190*134e1779SJakub Wojciech Klama  * lstat() as well.)
1191*134e1779SJakub Wojciech Klama  *
1192*134e1779SJakub Wojciech Klama  * When we use opendir() we cannot pass O_NOFOLLOW, so we must rely
1193*134e1779SJakub Wojciech Klama  * on substitution-detection via fstat().  To simplify the code we
1194*134e1779SJakub Wojciech Klama  * just always re-check.
1195*134e1779SJakub Wojciech Klama  *
1196*134e1779SJakub Wojciech Klama  * (For a proper fix in the future, we can require openat(), keep
1197*134e1779SJakub Wojciech Klama  * each parent directory open during walk etc, and allow only final
1198*134e1779SJakub Wojciech Klama  * name components with O_NOFOLLOW.)
1199*134e1779SJakub Wojciech Klama  *
1200*134e1779SJakub Wojciech Klama  * On successful return, st has been filled in.
1201*134e1779SJakub Wojciech Klama  */
1202*134e1779SJakub Wojciech Klama static int
fs_iopen(void * softc,struct l9p_fid * fid,int flags,enum l9p_omode p9,gid_t egid __unused,struct stat * st)1203*134e1779SJakub Wojciech Klama fs_iopen(void *softc, struct l9p_fid *fid, int flags, enum l9p_omode p9,
1204*134e1779SJakub Wojciech Klama     gid_t egid __unused, struct stat *st)
1205*134e1779SJakub Wojciech Klama {
1206*134e1779SJakub Wojciech Klama 	struct fs_softc *sc = softc;
1207*134e1779SJakub Wojciech Klama 	struct fs_fid *file;
1208*134e1779SJakub Wojciech Klama 	struct stat first;
1209*134e1779SJakub Wojciech Klama 	int32_t op;
1210*134e1779SJakub Wojciech Klama 	char *name;
1211*134e1779SJakub Wojciech Klama 	int error;
1212*134e1779SJakub Wojciech Klama 	int fd;
1213*134e1779SJakub Wojciech Klama 	DIR *dirp;
1214*134e1779SJakub Wojciech Klama 
1215*134e1779SJakub Wojciech Klama 	/* Forbid write ops on read-only file system. */
1216*134e1779SJakub Wojciech Klama 	if (sc->fs_readonly) {
1217*134e1779SJakub Wojciech Klama 		if ((flags & O_TRUNC) != 0)
1218*134e1779SJakub Wojciech Klama 			return (EROFS);
1219*134e1779SJakub Wojciech Klama 		if ((flags & O_ACCMODE) != O_RDONLY)
1220*134e1779SJakub Wojciech Klama 			return (EROFS);
1221*134e1779SJakub Wojciech Klama 		if (p9 & L9P_ORCLOSE)
1222*134e1779SJakub Wojciech Klama 			return (EROFS);
1223*134e1779SJakub Wojciech Klama 	}
1224*134e1779SJakub Wojciech Klama 
1225*134e1779SJakub Wojciech Klama 	file = fid->lo_aux;
1226*134e1779SJakub Wojciech Klama 	assert(file != NULL);
1227*134e1779SJakub Wojciech Klama 	name = file->ff_name;
1228*134e1779SJakub Wojciech Klama 
1229*134e1779SJakub Wojciech Klama 	if (fstatat(file->ff_dirfd, name, &first, AT_SYMLINK_NOFOLLOW) != 0)
1230*134e1779SJakub Wojciech Klama 		return (errno);
1231*134e1779SJakub Wojciech Klama 	if (S_ISLNK(first.st_mode))
1232*134e1779SJakub Wojciech Klama 		return (EPERM);
1233*134e1779SJakub Wojciech Klama 
1234*134e1779SJakub Wojciech Klama 	/* Can we rely on O_APPEND here?  Best not, can be cleared. */
1235*134e1779SJakub Wojciech Klama 	switch (flags & O_ACCMODE) {
1236*134e1779SJakub Wojciech Klama 	case O_RDONLY:
1237*134e1779SJakub Wojciech Klama 		op = L9P_ACE_READ_DATA;
1238*134e1779SJakub Wojciech Klama 		break;
1239*134e1779SJakub Wojciech Klama 	case O_WRONLY:
1240*134e1779SJakub Wojciech Klama 		op = L9P_ACE_WRITE_DATA;
1241*134e1779SJakub Wojciech Klama 		break;
1242*134e1779SJakub Wojciech Klama 	case O_RDWR:
1243*134e1779SJakub Wojciech Klama 		op = L9P_ACE_READ_DATA | L9P_ACE_WRITE_DATA;
1244*134e1779SJakub Wojciech Klama 		break;
1245*134e1779SJakub Wojciech Klama 	default:
1246*134e1779SJakub Wojciech Klama 		return (EINVAL);
1247*134e1779SJakub Wojciech Klama 	}
1248*134e1779SJakub Wojciech Klama 	fillacl(file);
1249*134e1779SJakub Wojciech Klama 	error = check_access(op, NULL, NULL, file->ff_acl, &first,
1250*134e1779SJakub Wojciech Klama 	    file->ff_ai, (gid_t)-1);
1251*134e1779SJakub Wojciech Klama 	if (error)
1252*134e1779SJakub Wojciech Klama 		return (error);
1253*134e1779SJakub Wojciech Klama 
1254*134e1779SJakub Wojciech Klama 	if (S_ISDIR(first.st_mode)) {
1255*134e1779SJakub Wojciech Klama 		/* Forbid write or truncate on directory. */
1256*134e1779SJakub Wojciech Klama 		if ((flags & O_ACCMODE) != O_RDONLY || (flags & O_TRUNC))
1257*134e1779SJakub Wojciech Klama 			return (EPERM);
1258*134e1779SJakub Wojciech Klama 		fd = openat(file->ff_dirfd, name, O_DIRECTORY);
1259*134e1779SJakub Wojciech Klama 		dirp = fdopendir(fd);
1260*134e1779SJakub Wojciech Klama 		if (dirp == NULL)
1261*134e1779SJakub Wojciech Klama 			return (EPERM);
1262*134e1779SJakub Wojciech Klama 		fd = dirfd(dirp);
1263*134e1779SJakub Wojciech Klama 	} else {
1264*134e1779SJakub Wojciech Klama 		dirp = NULL;
1265*134e1779SJakub Wojciech Klama 		fd = openat(file->ff_dirfd, name, flags);
1266*134e1779SJakub Wojciech Klama 		if (fd < 0)
1267*134e1779SJakub Wojciech Klama 			return (EPERM);
1268*134e1779SJakub Wojciech Klama 	}
1269*134e1779SJakub Wojciech Klama 
1270*134e1779SJakub Wojciech Klama 	/*
1271*134e1779SJakub Wojciech Klama 	 * We have a valid fd, and maybe non-null dirp.  Re-check
1272*134e1779SJakub Wojciech Klama 	 * the file, and fail if st_dev or st_ino changed.
1273*134e1779SJakub Wojciech Klama 	 */
1274*134e1779SJakub Wojciech Klama 	if (fstat(fd, st) != 0 ||
1275*134e1779SJakub Wojciech Klama 	    first.st_dev != st->st_dev ||
1276*134e1779SJakub Wojciech Klama 	    first.st_ino != st->st_ino) {
1277*134e1779SJakub Wojciech Klama 		if (dirp != NULL)
1278*134e1779SJakub Wojciech Klama 			(void) closedir(dirp);
1279*134e1779SJakub Wojciech Klama 		else
1280*134e1779SJakub Wojciech Klama 			(void) close(fd);
1281*134e1779SJakub Wojciech Klama 		return (EPERM);
1282*134e1779SJakub Wojciech Klama 	}
1283*134e1779SJakub Wojciech Klama 	if (dirp != NULL)
1284*134e1779SJakub Wojciech Klama 		file->ff_dir = dirp;
1285*134e1779SJakub Wojciech Klama 	else
1286*134e1779SJakub Wojciech Klama 		file->ff_fd = fd;
1287*134e1779SJakub Wojciech Klama 	return (0);
1288*134e1779SJakub Wojciech Klama }
1289*134e1779SJakub Wojciech Klama 
1290*134e1779SJakub Wojciech Klama /*
1291*134e1779SJakub Wojciech Klama  * Internal form of mkdir (common code for all forms).
1292*134e1779SJakub Wojciech Klama  * We receive the fs_softc <softc>, the directory fid <dir>
1293*134e1779SJakub Wojciech Klama  * in which the new entry is to be created, the name of the
1294*134e1779SJakub Wojciech Klama  * new entry, a flag <isp9> indicating whether to do plan9 style
1295*134e1779SJakub Wojciech Klama  * permissions or Linux style permissions, the permissions <perm>,
1296*134e1779SJakub Wojciech Klama  * an effective group id <egid>, and a pointer to a stat structure
1297*134e1779SJakub Wojciech Klama  * <st> to fill in describing the final result on success.
1298*134e1779SJakub Wojciech Klama  *
1299*134e1779SJakub Wojciech Klama  * See also fs_icreate() above.
1300*134e1779SJakub Wojciech Klama  */
1301*134e1779SJakub Wojciech Klama static int
fs_imkdir(void * softc,struct l9p_fid * dir,char * name,bool isp9,mode_t perm,gid_t egid,struct stat * st)1302*134e1779SJakub Wojciech Klama fs_imkdir(void *softc, struct l9p_fid *dir, char *name,
1303*134e1779SJakub Wojciech Klama     bool isp9, mode_t perm, gid_t egid, struct stat *st)
1304*134e1779SJakub Wojciech Klama {
1305*134e1779SJakub Wojciech Klama 	struct fs_fid *ff;
1306*134e1779SJakub Wojciech Klama 	gid_t gid;
1307*134e1779SJakub Wojciech Klama 	uid_t uid;
1308*134e1779SJakub Wojciech Klama 	char newname[MAXPATHLEN];
1309*134e1779SJakub Wojciech Klama 	int error, fd;
1310*134e1779SJakub Wojciech Klama 
1311*134e1779SJakub Wojciech Klama 	ff = dir->lo_aux;
1312*134e1779SJakub Wojciech Klama 	error = fs_buildname(dir, name, newname, sizeof(newname));
1313*134e1779SJakub Wojciech Klama 	if (error)
1314*134e1779SJakub Wojciech Klama 		return (error);
1315*134e1779SJakub Wojciech Klama 
1316*134e1779SJakub Wojciech Klama 	error = fs_nde(softc, dir, true, egid, st, &uid, &gid);
1317*134e1779SJakub Wojciech Klama 	if (error)
1318*134e1779SJakub Wojciech Klama 		return (error);
1319*134e1779SJakub Wojciech Klama 
1320*134e1779SJakub Wojciech Klama 	if (isp9)
1321*134e1779SJakub Wojciech Klama 		perm = fs_p9perm(perm, st->st_mode, true);
1322*134e1779SJakub Wojciech Klama 
1323*134e1779SJakub Wojciech Klama 	if (mkdirat(ff->ff_dirfd, newname, perm) != 0)
1324*134e1779SJakub Wojciech Klama 		return (errno);
1325*134e1779SJakub Wojciech Klama 
1326*134e1779SJakub Wojciech Klama 	fd = openat(ff->ff_dirfd, newname,
1327*134e1779SJakub Wojciech Klama 	    O_DIRECTORY | O_RDONLY | O_NOFOLLOW);
1328*134e1779SJakub Wojciech Klama 	if (fd < 0 ||
1329*134e1779SJakub Wojciech Klama 	    fchown(fd, uid, gid) != 0 ||
1330*134e1779SJakub Wojciech Klama 	    fchmod(fd, perm) != 0 ||
1331*134e1779SJakub Wojciech Klama 	    fstat(fd, st) != 0) {
1332*134e1779SJakub Wojciech Klama 		error = errno;
1333*134e1779SJakub Wojciech Klama 		/* rmdir(newname) ? */
1334*134e1779SJakub Wojciech Klama 	}
1335*134e1779SJakub Wojciech Klama 	if (fd >= 0)
1336*134e1779SJakub Wojciech Klama 		(void) close(fd);
1337*134e1779SJakub Wojciech Klama 
1338*134e1779SJakub Wojciech Klama 	return (error);
1339*134e1779SJakub Wojciech Klama }
1340*134e1779SJakub Wojciech Klama 
1341*134e1779SJakub Wojciech Klama #ifdef __APPLE__
1342*134e1779SJakub Wojciech Klama /*
1343*134e1779SJakub Wojciech Klama  * This is an undocumented OS X syscall. It would be best to avoid it,
1344*134e1779SJakub Wojciech Klama  * but there doesn't seem to be another safe way to implement mknodat.
1345*134e1779SJakub Wojciech Klama  * Dear Apple, please implement mknodat before you remove this syscall.
1346*134e1779SJakub Wojciech Klama  */
fs_ifchdir_thread_local(int fd)1347*134e1779SJakub Wojciech Klama static int fs_ifchdir_thread_local(int fd)
1348*134e1779SJakub Wojciech Klama {
1349*134e1779SJakub Wojciech Klama #pragma clang diagnostic push
1350*134e1779SJakub Wojciech Klama #pragma clang diagnostic ignored "-Wdeprecated-declarations"
1351*134e1779SJakub Wojciech Klama 	return syscall(SYS___pthread_fchdir, fd);
1352*134e1779SJakub Wojciech Klama #pragma clang diagnostic pop
1353*134e1779SJakub Wojciech Klama }
1354*134e1779SJakub Wojciech Klama #endif
1355*134e1779SJakub Wojciech Klama 
1356*134e1779SJakub Wojciech Klama /*
1357*134e1779SJakub Wojciech Klama  * Internal form of mknod (special device).
1358*134e1779SJakub Wojciech Klama  *
1359*134e1779SJakub Wojciech Klama  * The device type (S_IFBLK, S_IFCHR) is included in the <mode> parameter.
1360*134e1779SJakub Wojciech Klama  */
1361*134e1779SJakub Wojciech Klama static int
fs_imknod(void * softc,struct l9p_fid * dir,char * name,bool isp9,mode_t mode,dev_t dev,gid_t egid,struct stat * st)1362*134e1779SJakub Wojciech Klama fs_imknod(void *softc, struct l9p_fid *dir, char *name,
1363*134e1779SJakub Wojciech Klama     bool isp9, mode_t mode, dev_t dev, gid_t egid, struct stat *st)
1364*134e1779SJakub Wojciech Klama {
1365*134e1779SJakub Wojciech Klama 	struct fs_fid *ff;
1366*134e1779SJakub Wojciech Klama 	mode_t perm;
1367*134e1779SJakub Wojciech Klama 	gid_t gid;
1368*134e1779SJakub Wojciech Klama 	uid_t uid;
1369*134e1779SJakub Wojciech Klama 	char newname[MAXPATHLEN];
1370*134e1779SJakub Wojciech Klama 	int error;
1371*134e1779SJakub Wojciech Klama 
1372*134e1779SJakub Wojciech Klama 	ff = dir->lo_aux;
1373*134e1779SJakub Wojciech Klama 	error = fs_buildname(dir, name, newname, sizeof(newname));
1374*134e1779SJakub Wojciech Klama 	if (error)
1375*134e1779SJakub Wojciech Klama 		return (error);
1376*134e1779SJakub Wojciech Klama 
1377*134e1779SJakub Wojciech Klama 	error = fs_nde(softc, dir, false, egid, st, &uid, &gid);
1378*134e1779SJakub Wojciech Klama 	if (error)
1379*134e1779SJakub Wojciech Klama 		return (error);
1380*134e1779SJakub Wojciech Klama 
1381*134e1779SJakub Wojciech Klama 	if (isp9) {
1382*134e1779SJakub Wojciech Klama 		perm = fs_p9perm(mode & 0777, st->st_mode, false);
1383*134e1779SJakub Wojciech Klama 		mode = (mode & ~0777) | perm;
1384*134e1779SJakub Wojciech Klama 	} else {
1385*134e1779SJakub Wojciech Klama 		perm = mode & 0777;
1386*134e1779SJakub Wojciech Klama 	}
1387*134e1779SJakub Wojciech Klama 
1388*134e1779SJakub Wojciech Klama #ifdef __APPLE__
1389*134e1779SJakub Wojciech Klama 	if (fs_ifchdir_thread_local(ff->ff_dirfd) < 0) {
1390*134e1779SJakub Wojciech Klama 		return -1;
1391*134e1779SJakub Wojciech Klama 	}
1392*134e1779SJakub Wojciech Klama 	error = mknod(newname, mode, dev);
1393*134e1779SJakub Wojciech Klama 	int preserved_errno = errno;
1394*134e1779SJakub Wojciech Klama 	/* Stop using the thread-local cwd */
1395*134e1779SJakub Wojciech Klama 	fs_ifchdir_thread_local(-1);
1396*134e1779SJakub Wojciech Klama 	if (error < 0) {
1397*134e1779SJakub Wojciech Klama 		errno = preserved_errno;
1398*134e1779SJakub Wojciech Klama 		return errno;
1399*134e1779SJakub Wojciech Klama 	}
1400*134e1779SJakub Wojciech Klama #else
1401*134e1779SJakub Wojciech Klama 	if (mknodat(ff->ff_dirfd, newname, mode, dev) != 0)
1402*134e1779SJakub Wojciech Klama 		return (errno);
1403*134e1779SJakub Wojciech Klama #endif
1404*134e1779SJakub Wojciech Klama 
1405*134e1779SJakub Wojciech Klama 	/* We cannot open the new name; race to use l* syscalls. */
1406*134e1779SJakub Wojciech Klama 	if (fchownat(ff->ff_dirfd, newname, uid, gid, AT_SYMLINK_NOFOLLOW) != 0 ||
1407*134e1779SJakub Wojciech Klama 	    fchmodat(ff->ff_dirfd, newname, perm, AT_SYMLINK_NOFOLLOW) != 0 ||
1408*134e1779SJakub Wojciech Klama 	    fstatat(ff->ff_dirfd, newname, st, AT_SYMLINK_NOFOLLOW) != 0)
1409*134e1779SJakub Wojciech Klama 		error = errno;
1410*134e1779SJakub Wojciech Klama 	else if ((st->st_mode & S_IFMT) != (mode & S_IFMT))
1411*134e1779SJakub Wojciech Klama 		error = EPERM;		/* ??? lost a race anyway */
1412*134e1779SJakub Wojciech Klama 
1413*134e1779SJakub Wojciech Klama 	/* if (error) unlink(newname) ? */
1414*134e1779SJakub Wojciech Klama 
1415*134e1779SJakub Wojciech Klama 	return (error);
1416*134e1779SJakub Wojciech Klama }
1417*134e1779SJakub Wojciech Klama 
1418*134e1779SJakub Wojciech Klama /*
1419*134e1779SJakub Wojciech Klama  * Internal form of mkfifo.
1420*134e1779SJakub Wojciech Klama  */
1421*134e1779SJakub Wojciech Klama static int
fs_imkfifo(void * softc,struct l9p_fid * dir,char * name,bool isp9,mode_t perm,gid_t egid,struct stat * st)1422*134e1779SJakub Wojciech Klama fs_imkfifo(void *softc, struct l9p_fid *dir, char *name,
1423*134e1779SJakub Wojciech Klama     bool isp9, mode_t perm, gid_t egid, struct stat *st)
1424*134e1779SJakub Wojciech Klama {
1425*134e1779SJakub Wojciech Klama 	struct fs_fid *ff;
1426*134e1779SJakub Wojciech Klama 	gid_t gid;
1427*134e1779SJakub Wojciech Klama 	uid_t uid;
1428*134e1779SJakub Wojciech Klama 	char newname[MAXPATHLEN];
1429*134e1779SJakub Wojciech Klama 	int error;
1430*134e1779SJakub Wojciech Klama 
1431*134e1779SJakub Wojciech Klama 	ff = dir->lo_aux;
1432*134e1779SJakub Wojciech Klama 	error = fs_buildname(dir, name, newname, sizeof(newname));
1433*134e1779SJakub Wojciech Klama 	if (error)
1434*134e1779SJakub Wojciech Klama 		return (error);
1435*134e1779SJakub Wojciech Klama 
1436*134e1779SJakub Wojciech Klama 	error = fs_nde(softc, dir, false, egid, st, &uid, &gid);
1437*134e1779SJakub Wojciech Klama 	if (error)
1438*134e1779SJakub Wojciech Klama 		return (error);
1439*134e1779SJakub Wojciech Klama 
1440*134e1779SJakub Wojciech Klama 	if (isp9)
1441*134e1779SJakub Wojciech Klama 		perm = fs_p9perm(perm, st->st_mode, false);
1442*134e1779SJakub Wojciech Klama 
1443*134e1779SJakub Wojciech Klama 	if (mkfifo(newname, perm) != 0)
1444*134e1779SJakub Wojciech Klama 		return (errno);
1445*134e1779SJakub Wojciech Klama 
1446*134e1779SJakub Wojciech Klama 	/* We cannot open the new name; race to use l* syscalls. */
1447*134e1779SJakub Wojciech Klama 	if (fchownat(ff->ff_dirfd, newname, uid, gid, AT_SYMLINK_NOFOLLOW) != 0 ||
1448*134e1779SJakub Wojciech Klama 	    fchmodat(ff->ff_dirfd, newname, perm, AT_SYMLINK_NOFOLLOW) != 0 ||
1449*134e1779SJakub Wojciech Klama 	    fstatat(ff->ff_dirfd, newname, st, AT_SYMLINK_NOFOLLOW) != 0)
1450*134e1779SJakub Wojciech Klama 		error = errno;
1451*134e1779SJakub Wojciech Klama 	else if (!S_ISFIFO(st->st_mode))
1452*134e1779SJakub Wojciech Klama 		error = EPERM;		/* ??? lost a race anyway */
1453*134e1779SJakub Wojciech Klama 
1454*134e1779SJakub Wojciech Klama 	/* if (error) unlink(newname) ? */
1455*134e1779SJakub Wojciech Klama 
1456*134e1779SJakub Wojciech Klama 	return (error);
1457*134e1779SJakub Wojciech Klama }
1458*134e1779SJakub Wojciech Klama 
1459*134e1779SJakub Wojciech Klama /*
1460*134e1779SJakub Wojciech Klama  * Internal form of mksocket.
1461*134e1779SJakub Wojciech Klama  *
1462*134e1779SJakub Wojciech Klama  * This is a bit different because of the horrible socket naming
1463*134e1779SJakub Wojciech Klama  * system (bind() with sockaddr_un sun_path).
1464*134e1779SJakub Wojciech Klama  */
1465*134e1779SJakub Wojciech Klama static int
fs_imksocket(void * softc,struct l9p_fid * dir,char * name,bool isp9,mode_t perm,gid_t egid,struct stat * st)1466*134e1779SJakub Wojciech Klama fs_imksocket(void *softc, struct l9p_fid *dir, char *name,
1467*134e1779SJakub Wojciech Klama     bool isp9, mode_t perm, gid_t egid, struct stat *st)
1468*134e1779SJakub Wojciech Klama {
1469*134e1779SJakub Wojciech Klama 	struct fs_fid *ff;
1470*134e1779SJakub Wojciech Klama 	struct sockaddr_un sun;
1471*134e1779SJakub Wojciech Klama 	char *path;
1472*134e1779SJakub Wojciech Klama 	char newname[MAXPATHLEN];
1473*134e1779SJakub Wojciech Klama 	gid_t gid;
1474*134e1779SJakub Wojciech Klama 	uid_t uid;
1475*134e1779SJakub Wojciech Klama 	int error = 0, s, fd;
1476*134e1779SJakub Wojciech Klama 
1477*134e1779SJakub Wojciech Klama 	ff = dir->lo_aux;
1478*134e1779SJakub Wojciech Klama 	error = fs_buildname(dir, name, newname, sizeof(newname));
1479*134e1779SJakub Wojciech Klama 	if (error)
1480*134e1779SJakub Wojciech Klama 		return (error);
1481*134e1779SJakub Wojciech Klama 
1482*134e1779SJakub Wojciech Klama 	error = fs_nde(softc, dir, false, egid, st, &uid, &gid);
1483*134e1779SJakub Wojciech Klama 	if (error)
1484*134e1779SJakub Wojciech Klama 		return (error);
1485*134e1779SJakub Wojciech Klama 
1486*134e1779SJakub Wojciech Klama 	if (isp9)
1487*134e1779SJakub Wojciech Klama 		perm = fs_p9perm(perm, st->st_mode, false);
1488*134e1779SJakub Wojciech Klama 
1489*134e1779SJakub Wojciech Klama 	s = socket(AF_UNIX, SOCK_STREAM, 0);
1490*134e1779SJakub Wojciech Klama 	if (s < 0)
1491*134e1779SJakub Wojciech Klama 		return (errno);
1492*134e1779SJakub Wojciech Klama 
1493*134e1779SJakub Wojciech Klama 	path = newname;
1494*134e1779SJakub Wojciech Klama 	fd = -1;
1495*134e1779SJakub Wojciech Klama #ifdef HAVE_BINDAT
1496*134e1779SJakub Wojciech Klama 	/* Try bindat() if needed. */
1497*134e1779SJakub Wojciech Klama 	if (strlen(path) >= sizeof(sun.sun_path)) {
1498*134e1779SJakub Wojciech Klama 		fd = openat(ff->ff_dirfd, ff->ff_name,
1499*134e1779SJakub Wojciech Klama 		    O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
1500*134e1779SJakub Wojciech Klama 		if (fd >= 0)
1501*134e1779SJakub Wojciech Klama 			path = name;
1502*134e1779SJakub Wojciech Klama 	}
1503*134e1779SJakub Wojciech Klama #endif
1504*134e1779SJakub Wojciech Klama 
1505*134e1779SJakub Wojciech Klama 	/*
1506*134e1779SJakub Wojciech Klama 	 * Can only create the socket if the path will fit.
1507*134e1779SJakub Wojciech Klama 	 * Even if we are using bindat() there are limits
1508*134e1779SJakub Wojciech Klama 	 * (the API for AF_UNIX sockets is ... not good).
1509*134e1779SJakub Wojciech Klama 	 *
1510*134e1779SJakub Wojciech Klama 	 * Note: in theory we can fill sun_path to the end
1511*134e1779SJakub Wojciech Klama 	 * (omitting a terminating '\0') but in at least one
1512*134e1779SJakub Wojciech Klama 	 * Unix-like system, this was known to behave oddly,
1513*134e1779SJakub Wojciech Klama 	 * so we test for ">=" rather than just ">".
1514*134e1779SJakub Wojciech Klama 	 */
1515*134e1779SJakub Wojciech Klama 	if (strlen(path) >= sizeof(sun.sun_path)) {
1516*134e1779SJakub Wojciech Klama 		error = ENAMETOOLONG;
1517*134e1779SJakub Wojciech Klama 		goto out;
1518*134e1779SJakub Wojciech Klama 	}
1519*134e1779SJakub Wojciech Klama 	sun.sun_family = AF_UNIX;
1520*134e1779SJakub Wojciech Klama 	sun.sun_len = sizeof(struct sockaddr_un);
1521*134e1779SJakub Wojciech Klama 	strncpy(sun.sun_path, path, sizeof(sun.sun_path));
1522*134e1779SJakub Wojciech Klama 
1523*134e1779SJakub Wojciech Klama #ifdef HAVE_BINDAT
1524*134e1779SJakub Wojciech Klama 	if (fd >= 0) {
1525*134e1779SJakub Wojciech Klama 		if (bindat(fd, s, (struct sockaddr *)&sun, sun.sun_len) < 0)
1526*134e1779SJakub Wojciech Klama 			error = errno;
1527*134e1779SJakub Wojciech Klama 		goto out;	/* done now, for good or ill */
1528*134e1779SJakub Wojciech Klama 	}
1529*134e1779SJakub Wojciech Klama #endif
1530*134e1779SJakub Wojciech Klama 
1531*134e1779SJakub Wojciech Klama 	if (bind(s, (struct sockaddr *)&sun, sun.sun_len) < 0)
1532*134e1779SJakub Wojciech Klama 		error = errno;
1533*134e1779SJakub Wojciech Klama out:
1534*134e1779SJakub Wojciech Klama 
1535*134e1779SJakub Wojciech Klama 	if (error == 0) {
1536*134e1779SJakub Wojciech Klama 		/*
1537*134e1779SJakub Wojciech Klama 		 * We believe we created the socket-inode.  Fix
1538*134e1779SJakub Wojciech Klama 		 * permissions etc.  Note that we cannot use
1539*134e1779SJakub Wojciech Klama 		 * fstat() on the socket descriptor: it succeeds,
1540*134e1779SJakub Wojciech Klama 		 * but we get bogus data!
1541*134e1779SJakub Wojciech Klama 		 */
1542*134e1779SJakub Wojciech Klama 		if (fchownat(ff->ff_dirfd, newname, uid, gid, AT_SYMLINK_NOFOLLOW) != 0 ||
1543*134e1779SJakub Wojciech Klama 		    fchmodat(ff->ff_dirfd, newname, perm, AT_SYMLINK_NOFOLLOW) != 0 ||
1544*134e1779SJakub Wojciech Klama 		    fstatat(ff->ff_dirfd, newname, st, AT_SYMLINK_NOFOLLOW) != 0)
1545*134e1779SJakub Wojciech Klama 			error = errno;
1546*134e1779SJakub Wojciech Klama 		else if (!S_ISSOCK(st->st_mode))
1547*134e1779SJakub Wojciech Klama 			error = EPERM;		/* ??? lost a race anyway */
1548*134e1779SJakub Wojciech Klama 
1549*134e1779SJakub Wojciech Klama 		/* if (error) unlink(newname) ? */
1550*134e1779SJakub Wojciech Klama 	}
1551*134e1779SJakub Wojciech Klama 
1552*134e1779SJakub Wojciech Klama 	/*
1553*134e1779SJakub Wojciech Klama 	 * It's not clear which error should override, although
1554*134e1779SJakub Wojciech Klama 	 * ideally we should never see either close() call fail.
1555*134e1779SJakub Wojciech Klama 	 * In any case we do want to try to close both fd and s,
1556*134e1779SJakub Wojciech Klama 	 * always.  Let's set error only if it is not already set,
1557*134e1779SJakub Wojciech Klama 	 * so that all exit paths can use the same code.
1558*134e1779SJakub Wojciech Klama 	 */
1559*134e1779SJakub Wojciech Klama 	if (fd >= 0 && close(fd) != 0)
1560*134e1779SJakub Wojciech Klama 		if (error == 0)
1561*134e1779SJakub Wojciech Klama 			error = errno;
1562*134e1779SJakub Wojciech Klama 	if (close(s) != 0)
1563*134e1779SJakub Wojciech Klama 		if (error == 0)
1564*134e1779SJakub Wojciech Klama 			error = errno;
1565*134e1779SJakub Wojciech Klama 
1566*134e1779SJakub Wojciech Klama 	return (error);
1567*134e1779SJakub Wojciech Klama }
1568*134e1779SJakub Wojciech Klama 
1569*134e1779SJakub Wojciech Klama /*
1570*134e1779SJakub Wojciech Klama  * Internal form of symlink.
1571*134e1779SJakub Wojciech Klama  *
1572*134e1779SJakub Wojciech Klama  * Note that symlinks are presumed to carry no permission bits.
1573*134e1779SJakub Wojciech Klama  * They do have owners, however (who may be charged for quotas).
1574*134e1779SJakub Wojciech Klama  */
1575*134e1779SJakub Wojciech Klama static int
fs_isymlink(void * softc,struct l9p_fid * dir,char * name,char * symtgt,gid_t egid,struct stat * st)1576*134e1779SJakub Wojciech Klama fs_isymlink(void *softc, struct l9p_fid *dir, char *name,
1577*134e1779SJakub Wojciech Klama     char *symtgt, gid_t egid, struct stat *st)
1578*134e1779SJakub Wojciech Klama {
1579*134e1779SJakub Wojciech Klama 	struct fs_fid *ff;
1580*134e1779SJakub Wojciech Klama 	gid_t gid;
1581*134e1779SJakub Wojciech Klama 	uid_t uid;
1582*134e1779SJakub Wojciech Klama 	char newname[MAXPATHLEN];
1583*134e1779SJakub Wojciech Klama 	int error;
1584*134e1779SJakub Wojciech Klama 
1585*134e1779SJakub Wojciech Klama 	ff = dir->lo_aux;
1586*134e1779SJakub Wojciech Klama 	error = fs_buildname(dir, name, newname, sizeof(newname));
1587*134e1779SJakub Wojciech Klama 	if (error)
1588*134e1779SJakub Wojciech Klama 		return (error);
1589*134e1779SJakub Wojciech Klama 
1590*134e1779SJakub Wojciech Klama 	error = fs_nde(softc, dir, false, egid, st, &uid, &gid);
1591*134e1779SJakub Wojciech Klama 	if (error)
1592*134e1779SJakub Wojciech Klama 		return (error);
1593*134e1779SJakub Wojciech Klama 
1594*134e1779SJakub Wojciech Klama 	if (symlinkat(symtgt, ff->ff_dirfd, newname) != 0)
1595*134e1779SJakub Wojciech Klama 		return (errno);
1596*134e1779SJakub Wojciech Klama 
1597*134e1779SJakub Wojciech Klama 	/* We cannot open the new name; race to use l* syscalls. */
1598*134e1779SJakub Wojciech Klama 	if (fchownat(ff->ff_dirfd, newname, uid, gid, AT_SYMLINK_NOFOLLOW) != 0 ||
1599*134e1779SJakub Wojciech Klama 	    fstatat(ff->ff_dirfd, newname, st, AT_SYMLINK_NOFOLLOW) != 0)
1600*134e1779SJakub Wojciech Klama 		error = errno;
1601*134e1779SJakub Wojciech Klama 	else if (!S_ISLNK(st->st_mode))
1602*134e1779SJakub Wojciech Klama 		error = EPERM;		/* ??? lost a race anyway */
1603*134e1779SJakub Wojciech Klama 
1604*134e1779SJakub Wojciech Klama 	/* if (error) unlink(newname) ? */
1605*134e1779SJakub Wojciech Klama 
1606*134e1779SJakub Wojciech Klama 	return (error);
1607*134e1779SJakub Wojciech Klama }
1608*134e1779SJakub Wojciech Klama 
1609*134e1779SJakub Wojciech Klama static int
fs_open(void * softc,struct l9p_request * req)1610*134e1779SJakub Wojciech Klama fs_open(void *softc, struct l9p_request *req)
1611*134e1779SJakub Wojciech Klama {
1612*134e1779SJakub Wojciech Klama 	struct l9p_fid *fid = req->lr_fid;
1613*134e1779SJakub Wojciech Klama 	struct stat st;
1614*134e1779SJakub Wojciech Klama 	enum l9p_omode p9;
1615*134e1779SJakub Wojciech Klama 	int error, flags;
1616*134e1779SJakub Wojciech Klama 
1617*134e1779SJakub Wojciech Klama 	p9 = req->lr_req.topen.mode;
1618*134e1779SJakub Wojciech Klama 	error = fs_oflags_dotu(p9, &flags);
1619*134e1779SJakub Wojciech Klama 	if (error)
1620*134e1779SJakub Wojciech Klama 		return (error);
1621*134e1779SJakub Wojciech Klama 
1622*134e1779SJakub Wojciech Klama 	error = fs_iopen(softc, fid, flags, p9, (gid_t)-1, &st);
1623*134e1779SJakub Wojciech Klama 	if (error)
1624*134e1779SJakub Wojciech Klama 		return (error);
1625*134e1779SJakub Wojciech Klama 
1626*134e1779SJakub Wojciech Klama 	generate_qid(&st, &req->lr_resp.ropen.qid);
1627*134e1779SJakub Wojciech Klama 	req->lr_resp.ropen.iounit = req->lr_conn->lc_max_io_size;
1628*134e1779SJakub Wojciech Klama 	return (0);
1629*134e1779SJakub Wojciech Klama }
1630*134e1779SJakub Wojciech Klama 
1631*134e1779SJakub Wojciech Klama /*
1632*134e1779SJakub Wojciech Klama  * Helper for directory read.  We want to run an lstat on each
1633*134e1779SJakub Wojciech Klama  * file name within the directory.  This is a lot faster if we
1634*134e1779SJakub Wojciech Klama  * have lstatat (or fstatat with AT_SYMLINK_NOFOLLOW), but not
1635*134e1779SJakub Wojciech Klama  * all systems do, so hide the ifdef-ed code in an inline function.
1636*134e1779SJakub Wojciech Klama  */
1637*134e1779SJakub Wojciech Klama static inline int
fs_lstatat(struct fs_fid * file,char * name,struct stat * st)1638*134e1779SJakub Wojciech Klama fs_lstatat(struct fs_fid *file, char *name, struct stat *st)
1639*134e1779SJakub Wojciech Klama {
1640*134e1779SJakub Wojciech Klama 
1641*134e1779SJakub Wojciech Klama 	return (fstatat(dirfd(file->ff_dir), name, st, AT_SYMLINK_NOFOLLOW));
1642*134e1779SJakub Wojciech Klama }
1643*134e1779SJakub Wojciech Klama 
1644*134e1779SJakub Wojciech Klama static int
fs_read(void * softc,struct l9p_request * req)1645*134e1779SJakub Wojciech Klama fs_read(void *softc, struct l9p_request *req)
1646*134e1779SJakub Wojciech Klama {
1647*134e1779SJakub Wojciech Klama 	struct l9p_stat l9stat;
1648*134e1779SJakub Wojciech Klama 	struct fs_softc *sc;
1649*134e1779SJakub Wojciech Klama 	struct fs_fid *file;
1650*134e1779SJakub Wojciech Klama 	bool dotu = req->lr_conn->lc_version >= L9P_2000U;
1651*134e1779SJakub Wojciech Klama 	ssize_t ret;
1652*134e1779SJakub Wojciech Klama 
1653*134e1779SJakub Wojciech Klama 	sc = softc;
1654*134e1779SJakub Wojciech Klama 	file = req->lr_fid->lo_aux;
1655*134e1779SJakub Wojciech Klama 	assert(file != NULL);
1656*134e1779SJakub Wojciech Klama 
1657*134e1779SJakub Wojciech Klama 	if (file->ff_dir != NULL) {
1658*134e1779SJakub Wojciech Klama 		struct dirent *d;
1659*134e1779SJakub Wojciech Klama 		struct stat st;
1660*134e1779SJakub Wojciech Klama 		struct l9p_message msg;
1661*134e1779SJakub Wojciech Klama 		long o;
1662*134e1779SJakub Wojciech Klama 
1663*134e1779SJakub Wojciech Klama 		pthread_mutex_lock(&file->ff_mtx);
1664*134e1779SJakub Wojciech Klama 
1665*134e1779SJakub Wojciech Klama 		/*
1666*134e1779SJakub Wojciech Klama 		 * Must use telldir before readdir since seekdir
1667*134e1779SJakub Wojciech Klama 		 * takes cookie values.  Unfortunately this wastes
1668*134e1779SJakub Wojciech Klama 		 * a lot of time (and memory) building unneeded
1669*134e1779SJakub Wojciech Klama 		 * cookies that can only be flushed by closing
1670*134e1779SJakub Wojciech Klama 		 * the directory.
1671*134e1779SJakub Wojciech Klama 		 *
1672*134e1779SJakub Wojciech Klama 		 * NB: FreeBSD libc seekdir has SINGLEUSE defined,
1673*134e1779SJakub Wojciech Klama 		 * so in fact, we can discard the cookies by
1674*134e1779SJakub Wojciech Klama 		 * calling seekdir on them.  This clears up wasted
1675*134e1779SJakub Wojciech Klama 		 * memory at the cost of even more wasted time...
1676*134e1779SJakub Wojciech Klama 		 *
1677*134e1779SJakub Wojciech Klama 		 * XXX: readdir/telldir/seekdir not thread safe
1678*134e1779SJakub Wojciech Klama 		 */
1679*134e1779SJakub Wojciech Klama 		l9p_init_msg(&msg, req, L9P_PACK);
1680*134e1779SJakub Wojciech Klama 		for (;;) {
1681*134e1779SJakub Wojciech Klama 			o = telldir(file->ff_dir);
1682*134e1779SJakub Wojciech Klama 			d = readdir(file->ff_dir);
1683*134e1779SJakub Wojciech Klama 			if (d == NULL)
1684*134e1779SJakub Wojciech Klama 				break;
1685*134e1779SJakub Wojciech Klama 			if (fs_lstatat(file, d->d_name, &st))
1686*134e1779SJakub Wojciech Klama 				continue;
1687*134e1779SJakub Wojciech Klama 			dostat(sc, &l9stat, d->d_name, &st, dotu);
1688*134e1779SJakub Wojciech Klama 			if (l9p_pack_stat(&msg, req, &l9stat) != 0) {
1689*134e1779SJakub Wojciech Klama 				seekdir(file->ff_dir, o);
1690*134e1779SJakub Wojciech Klama 				break;
1691*134e1779SJakub Wojciech Klama 			}
1692*134e1779SJakub Wojciech Klama #if defined(__FreeBSD__)
1693*134e1779SJakub Wojciech Klama 			seekdir(file->ff_dir, o);
1694*134e1779SJakub Wojciech Klama 			(void) readdir(file->ff_dir);
1695*134e1779SJakub Wojciech Klama #endif
1696*134e1779SJakub Wojciech Klama 		}
1697*134e1779SJakub Wojciech Klama 
1698*134e1779SJakub Wojciech Klama 		pthread_mutex_unlock(&file->ff_mtx);
1699*134e1779SJakub Wojciech Klama 	} else {
1700*134e1779SJakub Wojciech Klama 		size_t niov = l9p_truncate_iov(req->lr_data_iov,
1701*134e1779SJakub Wojciech Klama                     req->lr_data_niov, req->lr_req.io.count);
1702*134e1779SJakub Wojciech Klama 
1703*134e1779SJakub Wojciech Klama #if defined(__FreeBSD__)
1704*134e1779SJakub Wojciech Klama 		ret = preadv(file->ff_fd, req->lr_data_iov, niov,
1705*134e1779SJakub Wojciech Klama 		    req->lr_req.io.offset);
1706*134e1779SJakub Wojciech Klama #else
1707*134e1779SJakub Wojciech Klama 		/* XXX: not thread safe, should really use aio_listio. */
1708*134e1779SJakub Wojciech Klama 		if (lseek(file->ff_fd, (off_t)req->lr_req.io.offset, SEEK_SET) < 0)
1709*134e1779SJakub Wojciech Klama 			return (errno);
1710*134e1779SJakub Wojciech Klama 
1711*134e1779SJakub Wojciech Klama 		ret = (uint32_t)readv(file->ff_fd, req->lr_data_iov, (int)niov);
1712*134e1779SJakub Wojciech Klama #endif
1713*134e1779SJakub Wojciech Klama 
1714*134e1779SJakub Wojciech Klama 		if (ret < 0)
1715*134e1779SJakub Wojciech Klama 			return (errno);
1716*134e1779SJakub Wojciech Klama 
1717*134e1779SJakub Wojciech Klama 		req->lr_resp.io.count = (uint32_t)ret;
1718*134e1779SJakub Wojciech Klama 	}
1719*134e1779SJakub Wojciech Klama 
1720*134e1779SJakub Wojciech Klama 	return (0);
1721*134e1779SJakub Wojciech Klama }
1722*134e1779SJakub Wojciech Klama 
1723*134e1779SJakub Wojciech Klama static int
fs_remove(void * softc,struct l9p_fid * fid)1724*134e1779SJakub Wojciech Klama fs_remove(void *softc, struct l9p_fid *fid)
1725*134e1779SJakub Wojciech Klama {
1726*134e1779SJakub Wojciech Klama 	struct fs_softc *sc = softc;
1727*134e1779SJakub Wojciech Klama 	struct l9p_acl *parent_acl;
1728*134e1779SJakub Wojciech Klama 	struct fs_fid *file;
1729*134e1779SJakub Wojciech Klama 	struct stat pst, cst;
1730*134e1779SJakub Wojciech Klama 	char dirname[MAXPATHLEN];
1731*134e1779SJakub Wojciech Klama 	int error;
1732*134e1779SJakub Wojciech Klama 
1733*134e1779SJakub Wojciech Klama 	if (sc->fs_readonly)
1734*134e1779SJakub Wojciech Klama 		return (EROFS);
1735*134e1779SJakub Wojciech Klama 
1736*134e1779SJakub Wojciech Klama 	error = fs_pdir(sc, fid, dirname, sizeof(dirname), &pst);
1737*134e1779SJakub Wojciech Klama 	if (error)
1738*134e1779SJakub Wojciech Klama 		return (error);
1739*134e1779SJakub Wojciech Klama 
1740*134e1779SJakub Wojciech Klama 	file = fid->lo_aux;
1741*134e1779SJakub Wojciech Klama 	if (fstatat(file->ff_dirfd, file->ff_name, &cst, AT_SYMLINK_NOFOLLOW) != 0)
1742*134e1779SJakub Wojciech Klama 		return (error);
1743*134e1779SJakub Wojciech Klama 
1744*134e1779SJakub Wojciech Klama 	parent_acl = getacl(file, -1, dirname);
1745*134e1779SJakub Wojciech Klama 	fillacl(file);
1746*134e1779SJakub Wojciech Klama 
1747*134e1779SJakub Wojciech Klama 	error = check_access(L9P_ACOP_UNLINK,
1748*134e1779SJakub Wojciech Klama 	    parent_acl, &pst, file->ff_acl, &cst, file->ff_ai, (gid_t)-1);
1749*134e1779SJakub Wojciech Klama 	l9p_acl_free(parent_acl);
1750*134e1779SJakub Wojciech Klama 	if (error)
1751*134e1779SJakub Wojciech Klama 		return (error);
1752*134e1779SJakub Wojciech Klama 
1753*134e1779SJakub Wojciech Klama 	if (unlinkat(file->ff_dirfd, file->ff_name,
1754*134e1779SJakub Wojciech Klama 	    S_ISDIR(cst.st_mode) ? AT_REMOVEDIR : 0) != 0)
1755*134e1779SJakub Wojciech Klama 		error = errno;
1756*134e1779SJakub Wojciech Klama 
1757*134e1779SJakub Wojciech Klama 	return (error);
1758*134e1779SJakub Wojciech Klama }
1759*134e1779SJakub Wojciech Klama 
1760*134e1779SJakub Wojciech Klama static int
fs_stat(void * softc,struct l9p_request * req)1761*134e1779SJakub Wojciech Klama fs_stat(void *softc, struct l9p_request *req)
1762*134e1779SJakub Wojciech Klama {
1763*134e1779SJakub Wojciech Klama 	struct fs_softc *sc;
1764*134e1779SJakub Wojciech Klama 	struct fs_fid *file;
1765*134e1779SJakub Wojciech Klama 	struct stat st;
1766*134e1779SJakub Wojciech Klama 	bool dotu = req->lr_conn->lc_version >= L9P_2000U;
1767*134e1779SJakub Wojciech Klama 
1768*134e1779SJakub Wojciech Klama 	sc = softc;
1769*134e1779SJakub Wojciech Klama 	file = req->lr_fid->lo_aux;
1770*134e1779SJakub Wojciech Klama 	assert(file);
1771*134e1779SJakub Wojciech Klama 
1772*134e1779SJakub Wojciech Klama 	if (fstatat(file->ff_dirfd, file->ff_name, &st,
1773*134e1779SJakub Wojciech Klama 	    AT_SYMLINK_NOFOLLOW) != 0)
1774*134e1779SJakub Wojciech Klama 		return (errno);
1775*134e1779SJakub Wojciech Klama 
1776*134e1779SJakub Wojciech Klama 	dostat(sc, &req->lr_resp.rstat.stat, file->ff_name, &st, dotu);
1777*134e1779SJakub Wojciech Klama 	return (0);
1778*134e1779SJakub Wojciech Klama }
1779*134e1779SJakub Wojciech Klama 
1780*134e1779SJakub Wojciech Klama static int
fs_walk(void * softc,struct l9p_request * req)1781*134e1779SJakub Wojciech Klama fs_walk(void *softc, struct l9p_request *req)
1782*134e1779SJakub Wojciech Klama {
1783*134e1779SJakub Wojciech Klama 	struct l9p_acl *acl;
1784*134e1779SJakub Wojciech Klama 	struct fs_authinfo *ai;
1785*134e1779SJakub Wojciech Klama 	struct fs_fid *file = req->lr_fid->lo_aux;
1786*134e1779SJakub Wojciech Klama 	struct fs_fid *newfile;
1787*134e1779SJakub Wojciech Klama 	struct stat st;
1788*134e1779SJakub Wojciech Klama 	size_t clen, namelen, need;
1789*134e1779SJakub Wojciech Klama 	char *comp, *succ, *next, *swtmp;
1790*134e1779SJakub Wojciech Klama 	bool atroot;
1791*134e1779SJakub Wojciech Klama 	bool dotdot;
1792*134e1779SJakub Wojciech Klama 	int i, nwname;
1793*134e1779SJakub Wojciech Klama 	int error = 0;
1794*134e1779SJakub Wojciech Klama 	char namebufs[2][MAXPATHLEN];
1795*134e1779SJakub Wojciech Klama 
1796*134e1779SJakub Wojciech Klama 	/*
1797*134e1779SJakub Wojciech Klama 	 * https://swtch.com/plan9port/man/man9/walk.html:
1798*134e1779SJakub Wojciech Klama 	 *
1799*134e1779SJakub Wojciech Klama 	 *    It is legal for nwname to be zero, in which case newfid
1800*134e1779SJakub Wojciech Klama 	 *    will represent the same file as fid and the walk will
1801*134e1779SJakub Wojciech Klama 	 *    usually succeed; this is equivalent to walking to dot.
1802*134e1779SJakub Wojciech Klama 	 * [Aside: it's not clear if we should test S_ISDIR here.]
1803*134e1779SJakub Wojciech Klama 	 *    ...
1804*134e1779SJakub Wojciech Klama 	 *    The name ".." ... represents the parent directory.
1805*134e1779SJakub Wojciech Klama 	 *    The name "." ... is not used in the protocol.
1806*134e1779SJakub Wojciech Klama 	 *    ... A walk of the name ".." in the root directory
1807*134e1779SJakub Wojciech Klama 	 *    of the server is equivalent to a walk with no name
1808*134e1779SJakub Wojciech Klama 	 *    elements.
1809*134e1779SJakub Wojciech Klama 	 *
1810*134e1779SJakub Wojciech Klama 	 * Note that req.twalk.nwname never exceeds L9P_MAX_WELEM,
1811*134e1779SJakub Wojciech Klama 	 * so it is safe to convert to plain int.
1812*134e1779SJakub Wojciech Klama 	 *
1813*134e1779SJakub Wojciech Klama 	 * We are to return an error only if the first walk fails,
1814*134e1779SJakub Wojciech Klama 	 * else stop at the end of the names or on the first error.
1815*134e1779SJakub Wojciech Klama 	 * The final fid is based on the last name successfully
1816*134e1779SJakub Wojciech Klama 	 * walked.
1817*134e1779SJakub Wojciech Klama 	 *
1818*134e1779SJakub Wojciech Klama 	 * Note that we *do* get Twalk requests with nwname==0 on files.
1819*134e1779SJakub Wojciech Klama 	 *
1820*134e1779SJakub Wojciech Klama 	 * Set up "successful name" buffer pointer with base fid name,
1821*134e1779SJakub Wojciech Klama 	 * initially.  We'll swap each new success into it as we go.
1822*134e1779SJakub Wojciech Klama 	 *
1823*134e1779SJakub Wojciech Klama 	 * Invariant: atroot and stat data correspond to current
1824*134e1779SJakub Wojciech Klama 	 * (succ) path.
1825*134e1779SJakub Wojciech Klama 	 */
1826*134e1779SJakub Wojciech Klama 	succ = namebufs[0];
1827*134e1779SJakub Wojciech Klama 	next = namebufs[1];
1828*134e1779SJakub Wojciech Klama 	namelen = strlcpy(succ, file->ff_name, MAXPATHLEN);
1829*134e1779SJakub Wojciech Klama 	if (namelen >= MAXPATHLEN)
1830*134e1779SJakub Wojciech Klama 		return (ENAMETOOLONG);
1831*134e1779SJakub Wojciech Klama 	if (fstatat(file->ff_dirfd, succ, &st, AT_SYMLINK_NOFOLLOW) < 0)
1832*134e1779SJakub Wojciech Klama 		return (errno);
1833*134e1779SJakub Wojciech Klama 	ai = file->ff_ai;
1834*134e1779SJakub Wojciech Klama 	atroot = strlen(succ) == 0; /* XXX? */
1835*134e1779SJakub Wojciech Klama 	fillacl(file);
1836*134e1779SJakub Wojciech Klama 	acl = file->ff_acl;
1837*134e1779SJakub Wojciech Klama 
1838*134e1779SJakub Wojciech Klama 	nwname = (int)req->lr_req.twalk.nwname;
1839*134e1779SJakub Wojciech Klama 
1840*134e1779SJakub Wojciech Klama 	for (i = 0; i < nwname; i++) {
1841*134e1779SJakub Wojciech Klama 		/*
1842*134e1779SJakub Wojciech Klama 		 * Must have execute permission to search a directory.
1843*134e1779SJakub Wojciech Klama 		 * Then, look up each component in its directory-so-far.
1844*134e1779SJakub Wojciech Klama 		 * Check for ".." along the way, handlng specially
1845*134e1779SJakub Wojciech Klama 		 * as needed.  Forbid "/" in name components.
1846*134e1779SJakub Wojciech Klama 		 *
1847*134e1779SJakub Wojciech Klama 		 */
1848*134e1779SJakub Wojciech Klama 		if (!S_ISDIR(st.st_mode)) {
1849*134e1779SJakub Wojciech Klama 			error = ENOTDIR;
1850*134e1779SJakub Wojciech Klama 			goto out;
1851*134e1779SJakub Wojciech Klama 		}
1852*134e1779SJakub Wojciech Klama 		error = check_access(L9P_ACE_EXECUTE,
1853*134e1779SJakub Wojciech Klama 		     NULL, NULL, acl, &st, ai, (gid_t)-1);
1854*134e1779SJakub Wojciech Klama 		if (error) {
1855*134e1779SJakub Wojciech Klama 			L9P_LOG(L9P_DEBUG,
1856*134e1779SJakub Wojciech Klama 			    "Twalk: denying dir-walk on \"%s\" for uid %u",
1857*134e1779SJakub Wojciech Klama 			    succ, (unsigned)ai->ai_uid);
1858*134e1779SJakub Wojciech Klama 			error = EPERM;
1859*134e1779SJakub Wojciech Klama 			goto out;
1860*134e1779SJakub Wojciech Klama 		}
1861*134e1779SJakub Wojciech Klama 		comp = req->lr_req.twalk.wname[i];
1862*134e1779SJakub Wojciech Klama 		if (strchr(comp, '/') != NULL) {
1863*134e1779SJakub Wojciech Klama 			error = EINVAL;
1864*134e1779SJakub Wojciech Klama 			break;
1865*134e1779SJakub Wojciech Klama 		}
1866*134e1779SJakub Wojciech Klama 
1867*134e1779SJakub Wojciech Klama 		clen = strlen(comp);
1868*134e1779SJakub Wojciech Klama 		dotdot = false;
1869*134e1779SJakub Wojciech Klama 
1870*134e1779SJakub Wojciech Klama 		/*
1871*134e1779SJakub Wojciech Klama 		 * Build next pathname (into "next").  If "..",
1872*134e1779SJakub Wojciech Klama 		 * just strip one name component off the success
1873*134e1779SJakub Wojciech Klama 		 * name so far.  Since we know this name fits, the
1874*134e1779SJakub Wojciech Klama 		 * stripped down version also fits.  Otherwise,
1875*134e1779SJakub Wojciech Klama 		 * the name is the base name plus '/' plus the
1876*134e1779SJakub Wojciech Klama 		 * component name plus terminating '\0'; this may
1877*134e1779SJakub Wojciech Klama 		 * or may not fit.
1878*134e1779SJakub Wojciech Klama 		 */
1879*134e1779SJakub Wojciech Klama 		if (comp[0] == '.') {
1880*134e1779SJakub Wojciech Klama 			if (clen == 1) {
1881*134e1779SJakub Wojciech Klama 				error = EINVAL;
1882*134e1779SJakub Wojciech Klama 				break;
1883*134e1779SJakub Wojciech Klama 			}
1884*134e1779SJakub Wojciech Klama 			if (comp[1] == '.' && clen == 2)
1885*134e1779SJakub Wojciech Klama 				dotdot = true;
1886*134e1779SJakub Wojciech Klama 		}
1887*134e1779SJakub Wojciech Klama 		if (dotdot) {
1888*134e1779SJakub Wojciech Klama 			/*
1889*134e1779SJakub Wojciech Klama 			 * It's not clear how ".." at root should
1890*134e1779SJakub Wojciech Klama 			 * be handled when i > 0.  Obeying the man
1891*134e1779SJakub Wojciech Klama 			 * page exactly, we reset i to 0 and stop,
1892*134e1779SJakub Wojciech Klama 			 * declaring terminal success.
1893*134e1779SJakub Wojciech Klama 			 *
1894*134e1779SJakub Wojciech Klama 			 * Otherwise, we just climbed up one level
1895*134e1779SJakub Wojciech Klama 			 * so adjust "atroot".
1896*134e1779SJakub Wojciech Klama 			 */
1897*134e1779SJakub Wojciech Klama 			if (atroot) {
1898*134e1779SJakub Wojciech Klama 				i = 0;
1899*134e1779SJakub Wojciech Klama 				break;
1900*134e1779SJakub Wojciech Klama 			}
1901*134e1779SJakub Wojciech Klama 			(void) r_dirname(succ, next, MAXPATHLEN);
1902*134e1779SJakub Wojciech Klama 			namelen = strlen(next);
1903*134e1779SJakub Wojciech Klama 			atroot = strlen(next) == 0; /* XXX? */
1904*134e1779SJakub Wojciech Klama 		} else {
1905*134e1779SJakub Wojciech Klama 			need = namelen + 1 + clen + 1;
1906*134e1779SJakub Wojciech Klama 			if (need > MAXPATHLEN) {
1907*134e1779SJakub Wojciech Klama 				error = ENAMETOOLONG;
1908*134e1779SJakub Wojciech Klama 				break;
1909*134e1779SJakub Wojciech Klama 			}
1910*134e1779SJakub Wojciech Klama 			memcpy(next, succ, namelen);
1911*134e1779SJakub Wojciech Klama 			next[namelen++] = '/';
1912*134e1779SJakub Wojciech Klama 			memcpy(&next[namelen], comp, clen + 1);
1913*134e1779SJakub Wojciech Klama 			namelen += clen;
1914*134e1779SJakub Wojciech Klama 			/*
1915*134e1779SJakub Wojciech Klama 			 * Since name is never ".", we are necessarily
1916*134e1779SJakub Wojciech Klama 			 * descending below the root now.
1917*134e1779SJakub Wojciech Klama 			 */
1918*134e1779SJakub Wojciech Klama 			atroot = false;
1919*134e1779SJakub Wojciech Klama 		}
1920*134e1779SJakub Wojciech Klama 
1921*134e1779SJakub Wojciech Klama 		if (fstatat(file->ff_dirfd, next, &st, AT_SYMLINK_NOFOLLOW) < 0) {
1922*134e1779SJakub Wojciech Klama 			error = ENOENT;
1923*134e1779SJakub Wojciech Klama 			break;
1924*134e1779SJakub Wojciech Klama 		}
1925*134e1779SJakub Wojciech Klama 
1926*134e1779SJakub Wojciech Klama 		/*
1927*134e1779SJakub Wojciech Klama 		 * Success: generate qid and swap this
1928*134e1779SJakub Wojciech Klama 		 * successful name into place.  Update acl.
1929*134e1779SJakub Wojciech Klama 		 */
1930*134e1779SJakub Wojciech Klama 		generate_qid(&st, &req->lr_resp.rwalk.wqid[i]);
1931*134e1779SJakub Wojciech Klama 		swtmp = succ;
1932*134e1779SJakub Wojciech Klama 		succ = next;
1933*134e1779SJakub Wojciech Klama 		next = swtmp;
1934*134e1779SJakub Wojciech Klama 		if (acl != NULL && acl != file->ff_acl)
1935*134e1779SJakub Wojciech Klama 			l9p_acl_free(acl);
1936*134e1779SJakub Wojciech Klama 		acl = getacl(file, -1, next);
1937*134e1779SJakub Wojciech Klama 	}
1938*134e1779SJakub Wojciech Klama 
1939*134e1779SJakub Wojciech Klama 	/*
1940*134e1779SJakub Wojciech Klama 	 * Fail only if we failed on the first name.
1941*134e1779SJakub Wojciech Klama 	 * Otherwise we succeeded on something, and "succ"
1942*134e1779SJakub Wojciech Klama 	 * points to the last successful name in namebufs[].
1943*134e1779SJakub Wojciech Klama 	 */
1944*134e1779SJakub Wojciech Klama 	if (error) {
1945*134e1779SJakub Wojciech Klama 		if (i == 0)
1946*134e1779SJakub Wojciech Klama 			goto out;
1947*134e1779SJakub Wojciech Klama 		error = 0;
1948*134e1779SJakub Wojciech Klama 	}
1949*134e1779SJakub Wojciech Klama 
1950*134e1779SJakub Wojciech Klama 	newfile = open_fid(file->ff_dirfd, succ, ai, false);
1951*134e1779SJakub Wojciech Klama 	if (newfile == NULL) {
1952*134e1779SJakub Wojciech Klama 		error = ENOMEM;
1953*134e1779SJakub Wojciech Klama 		goto out;
1954*134e1779SJakub Wojciech Klama 	}
1955*134e1779SJakub Wojciech Klama 	if (req->lr_newfid == req->lr_fid) {
1956*134e1779SJakub Wojciech Klama 		/*
1957*134e1779SJakub Wojciech Klama 		 * Before overwriting fid->lo_aux, free the old value.
1958*134e1779SJakub Wojciech Klama 		 * Note that this doesn't free the l9p_fid data,
1959*134e1779SJakub Wojciech Klama 		 * just the fs_fid data.  (But it does ditch ff_acl.)
1960*134e1779SJakub Wojciech Klama 		 */
1961*134e1779SJakub Wojciech Klama 		if (acl == file->ff_acl)
1962*134e1779SJakub Wojciech Klama 			acl = NULL;
1963*134e1779SJakub Wojciech Klama 		fs_freefid(softc, req->lr_fid);
1964*134e1779SJakub Wojciech Klama 		file = NULL;
1965*134e1779SJakub Wojciech Klama 	}
1966*134e1779SJakub Wojciech Klama 	req->lr_newfid->lo_aux = newfile;
1967*134e1779SJakub Wojciech Klama 	if (file != NULL && acl != file->ff_acl) {
1968*134e1779SJakub Wojciech Klama 		newfile->ff_acl = acl;
1969*134e1779SJakub Wojciech Klama 		acl = NULL;
1970*134e1779SJakub Wojciech Klama 	}
1971*134e1779SJakub Wojciech Klama 	req->lr_resp.rwalk.nwqid = (uint16_t)i;
1972*134e1779SJakub Wojciech Klama out:
1973*134e1779SJakub Wojciech Klama 	if (file != NULL && acl != file->ff_acl)
1974*134e1779SJakub Wojciech Klama 		l9p_acl_free(acl);
1975*134e1779SJakub Wojciech Klama 	return (error);
1976*134e1779SJakub Wojciech Klama }
1977*134e1779SJakub Wojciech Klama 
1978*134e1779SJakub Wojciech Klama static int
fs_write(void * softc,struct l9p_request * req)1979*134e1779SJakub Wojciech Klama fs_write(void *softc, struct l9p_request *req)
1980*134e1779SJakub Wojciech Klama {
1981*134e1779SJakub Wojciech Klama 	struct fs_softc *sc = softc;
1982*134e1779SJakub Wojciech Klama 	struct fs_fid *file;
1983*134e1779SJakub Wojciech Klama 	ssize_t ret;
1984*134e1779SJakub Wojciech Klama 
1985*134e1779SJakub Wojciech Klama 	file = req->lr_fid->lo_aux;
1986*134e1779SJakub Wojciech Klama 	assert(file != NULL);
1987*134e1779SJakub Wojciech Klama 
1988*134e1779SJakub Wojciech Klama 	if (sc->fs_readonly)
1989*134e1779SJakub Wojciech Klama 		return (EROFS);
1990*134e1779SJakub Wojciech Klama 
1991*134e1779SJakub Wojciech Klama 	size_t niov = l9p_truncate_iov(req->lr_data_iov,
1992*134e1779SJakub Wojciech Klama             req->lr_data_niov, req->lr_req.io.count);
1993*134e1779SJakub Wojciech Klama 
1994*134e1779SJakub Wojciech Klama #if defined(__FreeBSD__)
1995*134e1779SJakub Wojciech Klama 	ret = pwritev(file->ff_fd, req->lr_data_iov, niov,
1996*134e1779SJakub Wojciech Klama 	    req->lr_req.io.offset);
1997*134e1779SJakub Wojciech Klama #else
1998*134e1779SJakub Wojciech Klama 	/* XXX: not thread safe, should really use aio_listio. */
1999*134e1779SJakub Wojciech Klama 	if (lseek(file->ff_fd, (off_t)req->lr_req.io.offset, SEEK_SET) < 0)
2000*134e1779SJakub Wojciech Klama 		return (errno);
2001*134e1779SJakub Wojciech Klama 
2002*134e1779SJakub Wojciech Klama 	ret = writev(file->ff_fd, req->lr_data_iov,
2003*134e1779SJakub Wojciech Klama 	    (int)niov);
2004*134e1779SJakub Wojciech Klama #endif
2005*134e1779SJakub Wojciech Klama 
2006*134e1779SJakub Wojciech Klama 	if (ret < 0)
2007*134e1779SJakub Wojciech Klama 		return (errno);
2008*134e1779SJakub Wojciech Klama 
2009*134e1779SJakub Wojciech Klama 	req->lr_resp.io.count = (uint32_t)ret;
2010*134e1779SJakub Wojciech Klama 	return (0);
2011*134e1779SJakub Wojciech Klama }
2012*134e1779SJakub Wojciech Klama 
2013*134e1779SJakub Wojciech Klama static int
fs_wstat(void * softc,struct l9p_request * req)2014*134e1779SJakub Wojciech Klama fs_wstat(void *softc, struct l9p_request *req)
2015*134e1779SJakub Wojciech Klama {
2016*134e1779SJakub Wojciech Klama 	struct fs_softc *sc = softc;
2017*134e1779SJakub Wojciech Klama 	struct l9p_stat *l9stat = &req->lr_req.twstat.stat;
2018*134e1779SJakub Wojciech Klama 	struct l9p_fid *fid;
2019*134e1779SJakub Wojciech Klama 	struct fs_fid *file;
2020*134e1779SJakub Wojciech Klama 	int error = 0;
2021*134e1779SJakub Wojciech Klama 
2022*134e1779SJakub Wojciech Klama 	fid = req->lr_fid;
2023*134e1779SJakub Wojciech Klama 	file = fid->lo_aux;
2024*134e1779SJakub Wojciech Klama 	assert(file != NULL);
2025*134e1779SJakub Wojciech Klama 
2026*134e1779SJakub Wojciech Klama 	/*
2027*134e1779SJakub Wojciech Klama 	 * XXX:
2028*134e1779SJakub Wojciech Klama 	 *
2029*134e1779SJakub Wojciech Klama 	 * stat(9P) sez:
2030*134e1779SJakub Wojciech Klama 	 *
2031*134e1779SJakub Wojciech Klama 	 * Either all the changes in wstat request happen, or none of them
2032*134e1779SJakub Wojciech Klama 	 * does: if the request succeeds, all changes were made; if it fails,
2033*134e1779SJakub Wojciech Klama 	 * none were.
2034*134e1779SJakub Wojciech Klama 	 *
2035*134e1779SJakub Wojciech Klama 	 * Atomicity is clearly missing in current implementation.
2036*134e1779SJakub Wojciech Klama 	 */
2037*134e1779SJakub Wojciech Klama 
2038*134e1779SJakub Wojciech Klama 	if (sc->fs_readonly)
2039*134e1779SJakub Wojciech Klama 		return (EROFS);
2040*134e1779SJakub Wojciech Klama 
2041*134e1779SJakub Wojciech Klama 	if (l9stat->atime != (uint32_t)~0) {
2042*134e1779SJakub Wojciech Klama 		/* XXX: not implemented, ignore */
2043*134e1779SJakub Wojciech Klama 	}
2044*134e1779SJakub Wojciech Klama 
2045*134e1779SJakub Wojciech Klama 	if (l9stat->mtime != (uint32_t)~0) {
2046*134e1779SJakub Wojciech Klama 		/* XXX: not implemented, ignore */
2047*134e1779SJakub Wojciech Klama 	}
2048*134e1779SJakub Wojciech Klama 
2049*134e1779SJakub Wojciech Klama 	if (l9stat->dev != (uint32_t)~0) {
2050*134e1779SJakub Wojciech Klama 		error = EPERM;
2051*134e1779SJakub Wojciech Klama 		goto out;
2052*134e1779SJakub Wojciech Klama 	}
2053*134e1779SJakub Wojciech Klama 
2054*134e1779SJakub Wojciech Klama 	if (l9stat->length != (uint64_t)~0) {
2055*134e1779SJakub Wojciech Klama 		if (file->ff_dir != NULL) {
2056*134e1779SJakub Wojciech Klama 			error = EINVAL;
2057*134e1779SJakub Wojciech Klama 			goto out;
2058*134e1779SJakub Wojciech Klama 		}
2059*134e1779SJakub Wojciech Klama 
2060*134e1779SJakub Wojciech Klama 		if (truncate(file->ff_name, (off_t)l9stat->length) != 0) {
2061*134e1779SJakub Wojciech Klama 			error = errno;
2062*134e1779SJakub Wojciech Klama 			goto out;
2063*134e1779SJakub Wojciech Klama 		}
2064*134e1779SJakub Wojciech Klama 	}
2065*134e1779SJakub Wojciech Klama 
2066*134e1779SJakub Wojciech Klama 	if (req->lr_conn->lc_version >= L9P_2000U) {
2067*134e1779SJakub Wojciech Klama 		if (fchownat(file->ff_dirfd, file->ff_name, l9stat->n_uid,
2068*134e1779SJakub Wojciech Klama 		    l9stat->n_gid, AT_SYMLINK_NOFOLLOW) != 0) {
2069*134e1779SJakub Wojciech Klama 			error = errno;
2070*134e1779SJakub Wojciech Klama 			goto out;
2071*134e1779SJakub Wojciech Klama 		}
2072*134e1779SJakub Wojciech Klama 	}
2073*134e1779SJakub Wojciech Klama 
2074*134e1779SJakub Wojciech Klama 	if (l9stat->mode != (uint32_t)~0) {
2075*134e1779SJakub Wojciech Klama 		if (fchmodat(file->ff_dirfd, file->ff_name,
2076*134e1779SJakub Wojciech Klama 		    l9stat->mode & 0777, 0) != 0) {
2077*134e1779SJakub Wojciech Klama 			error = errno;
2078*134e1779SJakub Wojciech Klama 			goto out;
2079*134e1779SJakub Wojciech Klama 		}
2080*134e1779SJakub Wojciech Klama 	}
2081*134e1779SJakub Wojciech Klama 
2082*134e1779SJakub Wojciech Klama 	if (strlen(l9stat->name) > 0) {
2083*134e1779SJakub Wojciech Klama 		struct l9p_acl *parent_acl;
2084*134e1779SJakub Wojciech Klama 		struct stat st;
2085*134e1779SJakub Wojciech Klama 		char *tmp;
2086*134e1779SJakub Wojciech Klama 		char newname[MAXPATHLEN];
2087*134e1779SJakub Wojciech Klama 
2088*134e1779SJakub Wojciech Klama 		/*
2089*134e1779SJakub Wojciech Klama 		 * Rename-within-directory: it's not deleting anything,
2090*134e1779SJakub Wojciech Klama 		 * but we need write permission on the directory.  This
2091*134e1779SJakub Wojciech Klama 		 * should suffice.
2092*134e1779SJakub Wojciech Klama 		 */
2093*134e1779SJakub Wojciech Klama 		error = fs_pdir(softc, fid, newname, sizeof(newname), &st);
2094*134e1779SJakub Wojciech Klama 		if (error)
2095*134e1779SJakub Wojciech Klama 			goto out;
2096*134e1779SJakub Wojciech Klama 		parent_acl = getacl(file, -1, newname);
2097*134e1779SJakub Wojciech Klama 		error = check_access(L9P_ACE_ADD_FILE,
2098*134e1779SJakub Wojciech Klama 		    parent_acl, &st, NULL, NULL, file->ff_ai, (gid_t)-1);
2099*134e1779SJakub Wojciech Klama 		l9p_acl_free(parent_acl);
2100*134e1779SJakub Wojciech Klama 		if (error)
2101*134e1779SJakub Wojciech Klama 			goto out;
2102*134e1779SJakub Wojciech Klama 		error = fs_dpf(newname, l9stat->name, sizeof(newname));
2103*134e1779SJakub Wojciech Klama 		if (error)
2104*134e1779SJakub Wojciech Klama 			goto out;
2105*134e1779SJakub Wojciech Klama 		tmp = strdup(newname);
2106*134e1779SJakub Wojciech Klama 		if (tmp == NULL) {
2107*134e1779SJakub Wojciech Klama 			error = ENOMEM;
2108*134e1779SJakub Wojciech Klama 			goto out;
2109*134e1779SJakub Wojciech Klama 		}
2110*134e1779SJakub Wojciech Klama 		if (renameat(file->ff_dirfd, file->ff_name, file->ff_dirfd,
2111*134e1779SJakub Wojciech Klama 		    tmp) != 0) {
2112*134e1779SJakub Wojciech Klama 			error = errno;
2113*134e1779SJakub Wojciech Klama 			free(tmp);
2114*134e1779SJakub Wojciech Klama 			goto out;
2115*134e1779SJakub Wojciech Klama 		}
2116*134e1779SJakub Wojciech Klama 		/* Successful rename, update file->ff_name.  ACL can stay. */
2117*134e1779SJakub Wojciech Klama 		free(file->ff_name);
2118*134e1779SJakub Wojciech Klama 		file->ff_name = tmp;
2119*134e1779SJakub Wojciech Klama 	}
2120*134e1779SJakub Wojciech Klama out:
2121*134e1779SJakub Wojciech Klama 	return (error);
2122*134e1779SJakub Wojciech Klama }
2123*134e1779SJakub Wojciech Klama 
2124*134e1779SJakub Wojciech Klama static int
fs_statfs(void * softc __unused,struct l9p_request * req)2125*134e1779SJakub Wojciech Klama fs_statfs(void *softc __unused, struct l9p_request *req)
2126*134e1779SJakub Wojciech Klama {
2127*134e1779SJakub Wojciech Klama 	struct fs_fid *file;
2128*134e1779SJakub Wojciech Klama 	struct stat st;
2129*134e1779SJakub Wojciech Klama 	struct statfs f;
2130*134e1779SJakub Wojciech Klama 	long name_max;
2131*134e1779SJakub Wojciech Klama 	int error;
2132*134e1779SJakub Wojciech Klama 	int fd;
2133*134e1779SJakub Wojciech Klama 
2134*134e1779SJakub Wojciech Klama 	file = req->lr_fid->lo_aux;
2135*134e1779SJakub Wojciech Klama 	assert(file);
2136*134e1779SJakub Wojciech Klama 
2137*134e1779SJakub Wojciech Klama 	if (fstatat(file->ff_dirfd, file->ff_name, &st,
2138*134e1779SJakub Wojciech Klama 	    AT_SYMLINK_NOFOLLOW) != 0)
2139*134e1779SJakub Wojciech Klama 		return (errno);
2140*134e1779SJakub Wojciech Klama 
2141*134e1779SJakub Wojciech Klama 	/*
2142*134e1779SJakub Wojciech Klama 	 * Not entirely clear what access to require; we'll go
2143*134e1779SJakub Wojciech Klama 	 * for "read data".
2144*134e1779SJakub Wojciech Klama 	 */
2145*134e1779SJakub Wojciech Klama 	fillacl(file);
2146*134e1779SJakub Wojciech Klama 	error = check_access(L9P_ACE_READ_DATA, NULL, NULL,
2147*134e1779SJakub Wojciech Klama 	    file->ff_acl, &st, file->ff_ai, (gid_t)-1);
2148*134e1779SJakub Wojciech Klama 	if (error)
2149*134e1779SJakub Wojciech Klama 		return (error);
2150*134e1779SJakub Wojciech Klama 
2151*134e1779SJakub Wojciech Klama 	fd = openat(file->ff_dirfd, file->ff_name, 0);
2152*134e1779SJakub Wojciech Klama 	if (fd < 0)
2153*134e1779SJakub Wojciech Klama 		return (errno);
2154*134e1779SJakub Wojciech Klama 
2155*134e1779SJakub Wojciech Klama 	if (fstatfs(fd, &f) != 0)
2156*134e1779SJakub Wojciech Klama 		return (errno);
2157*134e1779SJakub Wojciech Klama 
2158*134e1779SJakub Wojciech Klama 	name_max = fpathconf(fd, _PC_NAME_MAX);
2159*134e1779SJakub Wojciech Klama 	error = errno;
2160*134e1779SJakub Wojciech Klama 	close(fd);
2161*134e1779SJakub Wojciech Klama 
2162*134e1779SJakub Wojciech Klama 	if (name_max == -1)
2163*134e1779SJakub Wojciech Klama 		return (error);
2164*134e1779SJakub Wojciech Klama 
2165*134e1779SJakub Wojciech Klama 	dostatfs(&req->lr_resp.rstatfs.statfs, &f, name_max);
2166*134e1779SJakub Wojciech Klama 
2167*134e1779SJakub Wojciech Klama 	return (0);
2168*134e1779SJakub Wojciech Klama }
2169*134e1779SJakub Wojciech Klama 
2170*134e1779SJakub Wojciech Klama static int
fs_lopen(void * softc,struct l9p_request * req)2171*134e1779SJakub Wojciech Klama fs_lopen(void *softc, struct l9p_request *req)
2172*134e1779SJakub Wojciech Klama {
2173*134e1779SJakub Wojciech Klama 	struct l9p_fid *fid = req->lr_fid;
2174*134e1779SJakub Wojciech Klama 	struct stat st;
2175*134e1779SJakub Wojciech Klama 	enum l9p_omode p9;
2176*134e1779SJakub Wojciech Klama 	gid_t gid;
2177*134e1779SJakub Wojciech Klama 	int error, flags;
2178*134e1779SJakub Wojciech Klama 
2179*134e1779SJakub Wojciech Klama 	error = fs_oflags_dotl(req->lr_req.tlopen.flags, &flags, &p9);
2180*134e1779SJakub Wojciech Klama 	if (error)
2181*134e1779SJakub Wojciech Klama 		return (error);
2182*134e1779SJakub Wojciech Klama 
2183*134e1779SJakub Wojciech Klama 	gid = req->lr_req.tlopen.gid;
2184*134e1779SJakub Wojciech Klama 	error = fs_iopen(softc, fid, flags, p9, gid, &st);
2185*134e1779SJakub Wojciech Klama 	if (error)
2186*134e1779SJakub Wojciech Klama 		return (error);
2187*134e1779SJakub Wojciech Klama 
2188*134e1779SJakub Wojciech Klama 	generate_qid(&st, &req->lr_resp.rlopen.qid);
2189*134e1779SJakub Wojciech Klama 	req->lr_resp.rlopen.iounit = req->lr_conn->lc_max_io_size;
2190*134e1779SJakub Wojciech Klama 	return (0);
2191*134e1779SJakub Wojciech Klama }
2192*134e1779SJakub Wojciech Klama 
2193*134e1779SJakub Wojciech Klama static int
fs_lcreate(void * softc,struct l9p_request * req)2194*134e1779SJakub Wojciech Klama fs_lcreate(void *softc, struct l9p_request *req)
2195*134e1779SJakub Wojciech Klama {
2196*134e1779SJakub Wojciech Klama 	struct l9p_fid *dir;
2197*134e1779SJakub Wojciech Klama 	struct stat st;
2198*134e1779SJakub Wojciech Klama 	enum l9p_omode p9;
2199*134e1779SJakub Wojciech Klama 	char *name;
2200*134e1779SJakub Wojciech Klama 	mode_t perm;
2201*134e1779SJakub Wojciech Klama 	gid_t gid;
2202*134e1779SJakub Wojciech Klama 	int error, flags;
2203*134e1779SJakub Wojciech Klama 
2204*134e1779SJakub Wojciech Klama 	dir = req->lr_fid;
2205*134e1779SJakub Wojciech Klama 	name = req->lr_req.tlcreate.name;
2206*134e1779SJakub Wojciech Klama 
2207*134e1779SJakub Wojciech Klama 	error = fs_oflags_dotl(req->lr_req.tlcreate.flags, &flags, &p9);
2208*134e1779SJakub Wojciech Klama 	if (error)
2209*134e1779SJakub Wojciech Klama 		return (error);
2210*134e1779SJakub Wojciech Klama 
2211*134e1779SJakub Wojciech Klama 	perm = (mode_t)req->lr_req.tlcreate.mode & 0777; /* ? set-id bits? */
2212*134e1779SJakub Wojciech Klama 	gid = req->lr_req.tlcreate.gid;
2213*134e1779SJakub Wojciech Klama 	error = fs_icreate(softc, dir, name, flags, false, perm, gid, &st);
2214*134e1779SJakub Wojciech Klama 	if (error == 0)
2215*134e1779SJakub Wojciech Klama 		generate_qid(&st, &req->lr_resp.rlcreate.qid);
2216*134e1779SJakub Wojciech Klama 	req->lr_resp.rlcreate.iounit = req->lr_conn->lc_max_io_size;
2217*134e1779SJakub Wojciech Klama 	return (error);
2218*134e1779SJakub Wojciech Klama }
2219*134e1779SJakub Wojciech Klama 
2220*134e1779SJakub Wojciech Klama static int
fs_symlink(void * softc,struct l9p_request * req)2221*134e1779SJakub Wojciech Klama fs_symlink(void *softc, struct l9p_request *req)
2222*134e1779SJakub Wojciech Klama {
2223*134e1779SJakub Wojciech Klama 	struct l9p_fid *dir;
2224*134e1779SJakub Wojciech Klama 	struct stat st;
2225*134e1779SJakub Wojciech Klama 	gid_t gid;
2226*134e1779SJakub Wojciech Klama 	char *name, *symtgt;
2227*134e1779SJakub Wojciech Klama 	int error;
2228*134e1779SJakub Wojciech Klama 
2229*134e1779SJakub Wojciech Klama 	dir = req->lr_fid;
2230*134e1779SJakub Wojciech Klama 	name = req->lr_req.tsymlink.name;
2231*134e1779SJakub Wojciech Klama 	symtgt = req->lr_req.tsymlink.symtgt;
2232*134e1779SJakub Wojciech Klama 	gid = req->lr_req.tsymlink.gid;
2233*134e1779SJakub Wojciech Klama 	error = fs_isymlink(softc, dir, name, symtgt, gid, &st);
2234*134e1779SJakub Wojciech Klama 	if (error == 0)
2235*134e1779SJakub Wojciech Klama 		generate_qid(&st, &req->lr_resp.rsymlink.qid);
2236*134e1779SJakub Wojciech Klama 	return (error);
2237*134e1779SJakub Wojciech Klama }
2238*134e1779SJakub Wojciech Klama 
2239*134e1779SJakub Wojciech Klama static int
fs_mknod(void * softc,struct l9p_request * req)2240*134e1779SJakub Wojciech Klama fs_mknod(void *softc, struct l9p_request *req)
2241*134e1779SJakub Wojciech Klama {
2242*134e1779SJakub Wojciech Klama 	struct l9p_fid *dir;
2243*134e1779SJakub Wojciech Klama 	struct stat st;
2244*134e1779SJakub Wojciech Klama 	uint32_t mode, major, minor;
2245*134e1779SJakub Wojciech Klama 	dev_t dev;
2246*134e1779SJakub Wojciech Klama 	gid_t gid;
2247*134e1779SJakub Wojciech Klama 	char *name;
2248*134e1779SJakub Wojciech Klama 	int error;
2249*134e1779SJakub Wojciech Klama 
2250*134e1779SJakub Wojciech Klama 	dir = req->lr_fid;
2251*134e1779SJakub Wojciech Klama 	name = req->lr_req.tmknod.name;
2252*134e1779SJakub Wojciech Klama 	mode = req->lr_req.tmknod.mode;
2253*134e1779SJakub Wojciech Klama 	gid = req->lr_req.tmknod.gid;
2254*134e1779SJakub Wojciech Klama 
2255*134e1779SJakub Wojciech Klama 	switch (mode & S_IFMT) {
2256*134e1779SJakub Wojciech Klama 	case S_IFBLK:
2257*134e1779SJakub Wojciech Klama 	case S_IFCHR:
2258*134e1779SJakub Wojciech Klama 		mode = (mode & S_IFMT) | (mode & 0777);	/* ??? */
2259*134e1779SJakub Wojciech Klama 		major = req->lr_req.tmknod.major;
2260*134e1779SJakub Wojciech Klama 		minor = req->lr_req.tmknod.major;
2261*134e1779SJakub Wojciech Klama 		dev = makedev(major, minor);
2262*134e1779SJakub Wojciech Klama 		error = fs_imknod(softc, dir, name, false,
2263*134e1779SJakub Wojciech Klama 		    (mode_t)mode, dev, gid, &st);
2264*134e1779SJakub Wojciech Klama 		break;
2265*134e1779SJakub Wojciech Klama 
2266*134e1779SJakub Wojciech Klama 	case S_IFIFO:
2267*134e1779SJakub Wojciech Klama 		error = fs_imkfifo(softc, dir, name, false,
2268*134e1779SJakub Wojciech Klama 		    (mode_t)(mode & 0777), gid, &st);
2269*134e1779SJakub Wojciech Klama 		break;
2270*134e1779SJakub Wojciech Klama 
2271*134e1779SJakub Wojciech Klama 	case S_IFSOCK:
2272*134e1779SJakub Wojciech Klama 		error = fs_imksocket(softc, dir, name, false,
2273*134e1779SJakub Wojciech Klama 		    (mode_t)(mode & 0777), gid, &st);
2274*134e1779SJakub Wojciech Klama 		break;
2275*134e1779SJakub Wojciech Klama 
2276*134e1779SJakub Wojciech Klama 	default:
2277*134e1779SJakub Wojciech Klama 		error = EINVAL;
2278*134e1779SJakub Wojciech Klama 		break;
2279*134e1779SJakub Wojciech Klama 	}
2280*134e1779SJakub Wojciech Klama 	if (error == 0)
2281*134e1779SJakub Wojciech Klama 		generate_qid(&st, &req->lr_resp.rmknod.qid);
2282*134e1779SJakub Wojciech Klama 	return (error);
2283*134e1779SJakub Wojciech Klama }
2284*134e1779SJakub Wojciech Klama 
2285*134e1779SJakub Wojciech Klama static int
fs_rename(void * softc,struct l9p_request * req)2286*134e1779SJakub Wojciech Klama fs_rename(void *softc, struct l9p_request *req)
2287*134e1779SJakub Wojciech Klama {
2288*134e1779SJakub Wojciech Klama 	struct fs_softc *sc = softc;
2289*134e1779SJakub Wojciech Klama 	struct fs_authinfo *ai;
2290*134e1779SJakub Wojciech Klama 	struct l9p_acl *oparent_acl;
2291*134e1779SJakub Wojciech Klama 	struct l9p_fid *fid, *f2;
2292*134e1779SJakub Wojciech Klama 	struct fs_fid *file, *f2ff;
2293*134e1779SJakub Wojciech Klama 	struct stat cst, opst, npst;
2294*134e1779SJakub Wojciech Klama 	int32_t op;
2295*134e1779SJakub Wojciech Klama 	bool reparenting;
2296*134e1779SJakub Wojciech Klama 	char *tmp;
2297*134e1779SJakub Wojciech Klama 	char olddir[MAXPATHLEN], newname[MAXPATHLEN];
2298*134e1779SJakub Wojciech Klama 	int error;
2299*134e1779SJakub Wojciech Klama 
2300*134e1779SJakub Wojciech Klama 	if (sc->fs_readonly)
2301*134e1779SJakub Wojciech Klama 		return (EROFS);
2302*134e1779SJakub Wojciech Klama 
2303*134e1779SJakub Wojciech Klama 	/*
2304*134e1779SJakub Wojciech Klama 	 * Note: lr_fid represents the file that is to be renamed,
2305*134e1779SJakub Wojciech Klama 	 * so we must locate its parent directory and verify that
2306*134e1779SJakub Wojciech Klama 	 * both this parent directory and the new directory f2 are
2307*134e1779SJakub Wojciech Klama 	 * writable.  But if the new parent directory is the same
2308*134e1779SJakub Wojciech Klama 	 * path as the old parent directory, our job is simpler.
2309*134e1779SJakub Wojciech Klama 	 */
2310*134e1779SJakub Wojciech Klama 	fid = req->lr_fid;
2311*134e1779SJakub Wojciech Klama 	file = fid->lo_aux;
2312*134e1779SJakub Wojciech Klama 	assert(file != NULL);
2313*134e1779SJakub Wojciech Klama 	ai = file->ff_ai;
2314*134e1779SJakub Wojciech Klama 
2315*134e1779SJakub Wojciech Klama 	error = fs_pdir(sc, fid, olddir, sizeof(olddir), &opst);
2316*134e1779SJakub Wojciech Klama 	if (error)
2317*134e1779SJakub Wojciech Klama 		return (error);
2318*134e1779SJakub Wojciech Klama 
2319*134e1779SJakub Wojciech Klama 	f2 = req->lr_fid2;
2320*134e1779SJakub Wojciech Klama 	f2ff = f2->lo_aux;
2321*134e1779SJakub Wojciech Klama 	assert(f2ff != NULL);
2322*134e1779SJakub Wojciech Klama 
2323*134e1779SJakub Wojciech Klama 	reparenting = strcmp(olddir, f2ff->ff_name) != 0;
2324*134e1779SJakub Wojciech Klama 
2325*134e1779SJakub Wojciech Klama 	fillacl(file);
2326*134e1779SJakub Wojciech Klama 	fillacl(f2ff);
2327*134e1779SJakub Wojciech Klama 
2328*134e1779SJakub Wojciech Klama 	if (fstatat(file->ff_dirfd, file->ff_name, &cst,
2329*134e1779SJakub Wojciech Klama 	    AT_SYMLINK_NOFOLLOW) != 0)
2330*134e1779SJakub Wojciech Klama 		return (errno);
2331*134e1779SJakub Wojciech Klama 
2332*134e1779SJakub Wojciech Klama 	/*
2333*134e1779SJakub Wojciech Klama 	 * Are we moving from olddir?  If so, we're unlinking
2334*134e1779SJakub Wojciech Klama 	 * from it, in terms of ACL access.
2335*134e1779SJakub Wojciech Klama 	 */
2336*134e1779SJakub Wojciech Klama 	if (reparenting) {
2337*134e1779SJakub Wojciech Klama 		oparent_acl = getacl(file, -1, olddir);
2338*134e1779SJakub Wojciech Klama 		error = check_access(L9P_ACOP_UNLINK,
2339*134e1779SJakub Wojciech Klama 		    oparent_acl, &opst, file->ff_acl, &cst, ai, (gid_t)-1);
2340*134e1779SJakub Wojciech Klama 		l9p_acl_free(oparent_acl);
2341*134e1779SJakub Wojciech Klama 		if (error)
2342*134e1779SJakub Wojciech Klama 			return (error);
2343*134e1779SJakub Wojciech Klama 	}
2344*134e1779SJakub Wojciech Klama 
2345*134e1779SJakub Wojciech Klama 	/*
2346*134e1779SJakub Wojciech Klama 	 * Now check that we're allowed to "create" a file or directory in
2347*134e1779SJakub Wojciech Klama 	 * f2.  (Should we do this, too, only if reparenting?  Maybe check
2348*134e1779SJakub Wojciech Klama 	 * for dir write permission if not reparenting -- but that's just
2349*134e1779SJakub Wojciech Klama 	 * add-file/add-subdir, which means doing this always.)
2350*134e1779SJakub Wojciech Klama 	 */
2351*134e1779SJakub Wojciech Klama 	if (fstatat(f2ff->ff_dirfd, f2ff->ff_name, &npst,
2352*134e1779SJakub Wojciech Klama 	    AT_SYMLINK_NOFOLLOW) != 0)
2353*134e1779SJakub Wojciech Klama 		return (errno);
2354*134e1779SJakub Wojciech Klama 
2355*134e1779SJakub Wojciech Klama 	op = S_ISDIR(cst.st_mode) ? L9P_ACE_ADD_SUBDIRECTORY : L9P_ACE_ADD_FILE;
2356*134e1779SJakub Wojciech Klama 	error = check_access(op, f2ff->ff_acl, &npst, NULL, NULL,
2357*134e1779SJakub Wojciech Klama 	    ai, (gid_t)-1);
2358*134e1779SJakub Wojciech Klama 	if (error)
2359*134e1779SJakub Wojciech Klama 		return (error);
2360*134e1779SJakub Wojciech Klama 
2361*134e1779SJakub Wojciech Klama 	/*
2362*134e1779SJakub Wojciech Klama 	 * Directories OK, file systems not R/O, etc; build final name.
2363*134e1779SJakub Wojciech Klama 	 * f2ff->ff_name cannot exceed MAXPATHLEN, but out of general
2364*134e1779SJakub Wojciech Klama 	 * paranoia, let's double check anyway.
2365*134e1779SJakub Wojciech Klama 	 */
2366*134e1779SJakub Wojciech Klama 	if (strlcpy(newname, f2ff->ff_name, sizeof(newname)) >= sizeof(newname))
2367*134e1779SJakub Wojciech Klama 		return (ENAMETOOLONG);
2368*134e1779SJakub Wojciech Klama 	error = fs_dpf(newname, req->lr_req.trename.name, sizeof(newname));
2369*134e1779SJakub Wojciech Klama 	if (error)
2370*134e1779SJakub Wojciech Klama 		return (error);
2371*134e1779SJakub Wojciech Klama 	tmp = strdup(newname);
2372*134e1779SJakub Wojciech Klama 	if (tmp == NULL)
2373*134e1779SJakub Wojciech Klama 		return (ENOMEM);
2374*134e1779SJakub Wojciech Klama 
2375*134e1779SJakub Wojciech Klama 	if (renameat(file->ff_dirfd, file->ff_name, file->ff_dirfd, tmp) != 0) {
2376*134e1779SJakub Wojciech Klama 		error = errno;
2377*134e1779SJakub Wojciech Klama 		free(tmp);
2378*134e1779SJakub Wojciech Klama 		return (error);
2379*134e1779SJakub Wojciech Klama 	}
2380*134e1779SJakub Wojciech Klama 
2381*134e1779SJakub Wojciech Klama 	/* file has been renamed but old fid is not clunked */
2382*134e1779SJakub Wojciech Klama 	free(file->ff_name);
2383*134e1779SJakub Wojciech Klama 	file->ff_name = tmp;
2384*134e1779SJakub Wojciech Klama 
2385*134e1779SJakub Wojciech Klama 	dropacl(file);
2386*134e1779SJakub Wojciech Klama 	return (0);
2387*134e1779SJakub Wojciech Klama }
2388*134e1779SJakub Wojciech Klama 
2389*134e1779SJakub Wojciech Klama static int
fs_readlink(void * softc __unused,struct l9p_request * req)2390*134e1779SJakub Wojciech Klama fs_readlink(void *softc __unused, struct l9p_request *req)
2391*134e1779SJakub Wojciech Klama {
2392*134e1779SJakub Wojciech Klama 	struct fs_fid *file;
2393*134e1779SJakub Wojciech Klama 	ssize_t linklen;
2394*134e1779SJakub Wojciech Klama 	char buf[MAXPATHLEN];
2395*134e1779SJakub Wojciech Klama 	int error = 0;
2396*134e1779SJakub Wojciech Klama 
2397*134e1779SJakub Wojciech Klama 	file = req->lr_fid->lo_aux;
2398*134e1779SJakub Wojciech Klama 	assert(file);
2399*134e1779SJakub Wojciech Klama 
2400*134e1779SJakub Wojciech Klama 	linklen = readlinkat(file->ff_dirfd, file->ff_name, buf, sizeof(buf));
2401*134e1779SJakub Wojciech Klama 	if (linklen < 0)
2402*134e1779SJakub Wojciech Klama 		error = errno;
2403*134e1779SJakub Wojciech Klama 	else if ((size_t)linklen >= sizeof(buf))
2404*134e1779SJakub Wojciech Klama 		error = ENOMEM; /* todo: allocate dynamically */
2405*134e1779SJakub Wojciech Klama 	else if ((req->lr_resp.rreadlink.target = strndup(buf,
2406*134e1779SJakub Wojciech Klama 	    (size_t)linklen)) == NULL)
2407*134e1779SJakub Wojciech Klama 		error = ENOMEM;
2408*134e1779SJakub Wojciech Klama 	return (error);
2409*134e1779SJakub Wojciech Klama }
2410*134e1779SJakub Wojciech Klama 
2411*134e1779SJakub Wojciech Klama static int
fs_getattr(void * softc __unused,struct l9p_request * req)2412*134e1779SJakub Wojciech Klama fs_getattr(void *softc __unused, struct l9p_request *req)
2413*134e1779SJakub Wojciech Klama {
2414*134e1779SJakub Wojciech Klama 	uint64_t mask, valid;
2415*134e1779SJakub Wojciech Klama 	struct fs_fid *file;
2416*134e1779SJakub Wojciech Klama 	struct stat st;
2417*134e1779SJakub Wojciech Klama 	int error = 0;
2418*134e1779SJakub Wojciech Klama 
2419*134e1779SJakub Wojciech Klama 	file = req->lr_fid->lo_aux;
2420*134e1779SJakub Wojciech Klama 	assert(file);
2421*134e1779SJakub Wojciech Klama 
2422*134e1779SJakub Wojciech Klama 	valid = 0;
2423*134e1779SJakub Wojciech Klama 	if (fstatat(file->ff_dirfd, file->ff_name, &st, AT_SYMLINK_NOFOLLOW)) {
2424*134e1779SJakub Wojciech Klama 		error = errno;
2425*134e1779SJakub Wojciech Klama 		goto out;
2426*134e1779SJakub Wojciech Klama 	}
2427*134e1779SJakub Wojciech Klama 	/* ?? Can we provide items not-requested? If so, can skip tests. */
2428*134e1779SJakub Wojciech Klama 	mask = req->lr_req.tgetattr.request_mask;
2429*134e1779SJakub Wojciech Klama 	if (mask & L9PL_GETATTR_MODE) {
2430*134e1779SJakub Wojciech Klama 		/* It is not clear if we need any translations. */
2431*134e1779SJakub Wojciech Klama 		req->lr_resp.rgetattr.mode = st.st_mode;
2432*134e1779SJakub Wojciech Klama 		valid |= L9PL_GETATTR_MODE;
2433*134e1779SJakub Wojciech Klama 	}
2434*134e1779SJakub Wojciech Klama 	if (mask & L9PL_GETATTR_NLINK) {
2435*134e1779SJakub Wojciech Klama 		req->lr_resp.rgetattr.nlink = st.st_nlink;
2436*134e1779SJakub Wojciech Klama 		valid |= L9PL_GETATTR_NLINK;
2437*134e1779SJakub Wojciech Klama 	}
2438*134e1779SJakub Wojciech Klama 	if (mask & L9PL_GETATTR_UID) {
2439*134e1779SJakub Wojciech Klama 		/* provide st_uid, or file->ff_uid? */
2440*134e1779SJakub Wojciech Klama 		req->lr_resp.rgetattr.uid = st.st_uid;
2441*134e1779SJakub Wojciech Klama 		valid |= L9PL_GETATTR_UID;
2442*134e1779SJakub Wojciech Klama 	}
2443*134e1779SJakub Wojciech Klama 	if (mask & L9PL_GETATTR_GID) {
2444*134e1779SJakub Wojciech Klama 		/* provide st_gid, or file->ff_gid? */
2445*134e1779SJakub Wojciech Klama 		req->lr_resp.rgetattr.gid = st.st_gid;
2446*134e1779SJakub Wojciech Klama 		valid |= L9PL_GETATTR_GID;
2447*134e1779SJakub Wojciech Klama 	}
2448*134e1779SJakub Wojciech Klama 	if (mask & L9PL_GETATTR_RDEV) {
2449*134e1779SJakub Wojciech Klama 		/* It is not clear if we need any translations. */
2450*134e1779SJakub Wojciech Klama 		req->lr_resp.rgetattr.rdev = (uint64_t)st.st_rdev;
2451*134e1779SJakub Wojciech Klama 		valid |= L9PL_GETATTR_RDEV;
2452*134e1779SJakub Wojciech Klama 	}
2453*134e1779SJakub Wojciech Klama 	if (mask & L9PL_GETATTR_ATIME) {
2454*134e1779SJakub Wojciech Klama 		req->lr_resp.rgetattr.atime_sec =
2455*134e1779SJakub Wojciech Klama 		    (uint64_t)st.st_atimespec.tv_sec;
2456*134e1779SJakub Wojciech Klama 		req->lr_resp.rgetattr.atime_nsec =
2457*134e1779SJakub Wojciech Klama 		    (uint64_t)st.st_atimespec.tv_nsec;
2458*134e1779SJakub Wojciech Klama 		valid |= L9PL_GETATTR_ATIME;
2459*134e1779SJakub Wojciech Klama 	}
2460*134e1779SJakub Wojciech Klama 	if (mask & L9PL_GETATTR_MTIME) {
2461*134e1779SJakub Wojciech Klama 		req->lr_resp.rgetattr.mtime_sec =
2462*134e1779SJakub Wojciech Klama 		    (uint64_t)st.st_mtimespec.tv_sec;
2463*134e1779SJakub Wojciech Klama 		req->lr_resp.rgetattr.mtime_nsec =
2464*134e1779SJakub Wojciech Klama 		    (uint64_t)st.st_mtimespec.tv_nsec;
2465*134e1779SJakub Wojciech Klama 		valid |= L9PL_GETATTR_MTIME;
2466*134e1779SJakub Wojciech Klama 	}
2467*134e1779SJakub Wojciech Klama 	if (mask & L9PL_GETATTR_CTIME) {
2468*134e1779SJakub Wojciech Klama 		req->lr_resp.rgetattr.ctime_sec =
2469*134e1779SJakub Wojciech Klama 		    (uint64_t)st.st_ctimespec.tv_sec;
2470*134e1779SJakub Wojciech Klama 		req->lr_resp.rgetattr.ctime_nsec =
2471*134e1779SJakub Wojciech Klama 		    (uint64_t)st.st_ctimespec.tv_nsec;
2472*134e1779SJakub Wojciech Klama 		valid |= L9PL_GETATTR_CTIME;
2473*134e1779SJakub Wojciech Klama 	}
2474*134e1779SJakub Wojciech Klama 	if (mask & L9PL_GETATTR_BTIME) {
2475*134e1779SJakub Wojciech Klama #if defined(HAVE_BIRTHTIME)
2476*134e1779SJakub Wojciech Klama 		req->lr_resp.rgetattr.btime_sec =
2477*134e1779SJakub Wojciech Klama 		    (uint64_t)st.st_birthtim.tv_sec;
2478*134e1779SJakub Wojciech Klama 		req->lr_resp.rgetattr.btime_nsec =
2479*134e1779SJakub Wojciech Klama 		    (uint64_t)st.st_birthtim.tv_nsec;
2480*134e1779SJakub Wojciech Klama #else
2481*134e1779SJakub Wojciech Klama 		req->lr_resp.rgetattr.btime_sec = 0;
2482*134e1779SJakub Wojciech Klama 		req->lr_resp.rgetattr.btime_nsec = 0;
2483*134e1779SJakub Wojciech Klama #endif
2484*134e1779SJakub Wojciech Klama 		valid |= L9PL_GETATTR_BTIME;
2485*134e1779SJakub Wojciech Klama 	}
2486*134e1779SJakub Wojciech Klama 	if (mask & L9PL_GETATTR_INO)
2487*134e1779SJakub Wojciech Klama 		valid |= L9PL_GETATTR_INO;
2488*134e1779SJakub Wojciech Klama 	if (mask & L9PL_GETATTR_SIZE) {
2489*134e1779SJakub Wojciech Klama 		req->lr_resp.rgetattr.size = (uint64_t)st.st_size;
2490*134e1779SJakub Wojciech Klama 		valid |= L9PL_GETATTR_SIZE;
2491*134e1779SJakub Wojciech Klama 	}
2492*134e1779SJakub Wojciech Klama 	if (mask & L9PL_GETATTR_BLOCKS) {
2493*134e1779SJakub Wojciech Klama 		req->lr_resp.rgetattr.blksize = (uint64_t)st.st_blksize;
2494*134e1779SJakub Wojciech Klama 		req->lr_resp.rgetattr.blocks = (uint64_t)st.st_blocks;
2495*134e1779SJakub Wojciech Klama 		valid |= L9PL_GETATTR_BLOCKS;
2496*134e1779SJakub Wojciech Klama 	}
2497*134e1779SJakub Wojciech Klama 	if (mask & L9PL_GETATTR_GEN) {
2498*134e1779SJakub Wojciech Klama 		req->lr_resp.rgetattr.gen = st.st_gen;
2499*134e1779SJakub Wojciech Klama 		valid |= L9PL_GETATTR_GEN;
2500*134e1779SJakub Wojciech Klama 	}
2501*134e1779SJakub Wojciech Klama 	/* don't know what to do with data version yet */
2502*134e1779SJakub Wojciech Klama 
2503*134e1779SJakub Wojciech Klama 	generate_qid(&st, &req->lr_resp.rgetattr.qid);
2504*134e1779SJakub Wojciech Klama out:
2505*134e1779SJakub Wojciech Klama 	req->lr_resp.rgetattr.valid = valid;
2506*134e1779SJakub Wojciech Klama 	return (error);
2507*134e1779SJakub Wojciech Klama }
2508*134e1779SJakub Wojciech Klama 
2509*134e1779SJakub Wojciech Klama /*
2510*134e1779SJakub Wojciech Klama  * Should combine some of this with wstat code.
2511*134e1779SJakub Wojciech Klama  */
2512*134e1779SJakub Wojciech Klama static int
fs_setattr(void * softc,struct l9p_request * req)2513*134e1779SJakub Wojciech Klama fs_setattr(void *softc, struct l9p_request *req)
2514*134e1779SJakub Wojciech Klama {
2515*134e1779SJakub Wojciech Klama 	uint64_t mask;
2516*134e1779SJakub Wojciech Klama 	struct fs_softc *sc = softc;
2517*134e1779SJakub Wojciech Klama 	struct timespec ts[2];
2518*134e1779SJakub Wojciech Klama 	struct fs_fid *file;
2519*134e1779SJakub Wojciech Klama 	struct stat st;
2520*134e1779SJakub Wojciech Klama 	int error = 0;
2521*134e1779SJakub Wojciech Klama 	uid_t uid, gid;
2522*134e1779SJakub Wojciech Klama 
2523*134e1779SJakub Wojciech Klama 	file = req->lr_fid->lo_aux;
2524*134e1779SJakub Wojciech Klama 	assert(file);
2525*134e1779SJakub Wojciech Klama 
2526*134e1779SJakub Wojciech Klama 	if (sc->fs_readonly)
2527*134e1779SJakub Wojciech Klama 		return (EROFS);
2528*134e1779SJakub Wojciech Klama 
2529*134e1779SJakub Wojciech Klama 	/*
2530*134e1779SJakub Wojciech Klama 	 * As with WSTAT we have atomicity issues.
2531*134e1779SJakub Wojciech Klama 	 */
2532*134e1779SJakub Wojciech Klama 	mask = req->lr_req.tsetattr.valid;
2533*134e1779SJakub Wojciech Klama 
2534*134e1779SJakub Wojciech Klama 	if (fstatat(file->ff_dirfd, file->ff_name, &st, AT_SYMLINK_NOFOLLOW)) {
2535*134e1779SJakub Wojciech Klama 		error = errno;
2536*134e1779SJakub Wojciech Klama 		goto out;
2537*134e1779SJakub Wojciech Klama 	}
2538*134e1779SJakub Wojciech Klama 
2539*134e1779SJakub Wojciech Klama 	if ((mask & L9PL_SETATTR_SIZE) && S_ISDIR(st.st_mode)) {
2540*134e1779SJakub Wojciech Klama 		error = EISDIR;
2541*134e1779SJakub Wojciech Klama 		goto out;
2542*134e1779SJakub Wojciech Klama 	}
2543*134e1779SJakub Wojciech Klama 
2544*134e1779SJakub Wojciech Klama 	if (mask & L9PL_SETATTR_MODE) {
2545*134e1779SJakub Wojciech Klama 		if (fchmodat(file->ff_dirfd, file->ff_name,
2546*134e1779SJakub Wojciech Klama 		    req->lr_req.tsetattr.mode & 0777,
2547*134e1779SJakub Wojciech Klama 		    AT_SYMLINK_NOFOLLOW)) {
2548*134e1779SJakub Wojciech Klama 			error = errno;
2549*134e1779SJakub Wojciech Klama 			goto out;
2550*134e1779SJakub Wojciech Klama 		}
2551*134e1779SJakub Wojciech Klama 	}
2552*134e1779SJakub Wojciech Klama 
2553*134e1779SJakub Wojciech Klama 	if (mask & (L9PL_SETATTR_UID | L9PL_SETATTR_GID)) {
2554*134e1779SJakub Wojciech Klama 		uid = mask & L9PL_SETATTR_UID
2555*134e1779SJakub Wojciech Klama 		    ? req->lr_req.tsetattr.uid
2556*134e1779SJakub Wojciech Klama 		    : (uid_t)-1;
2557*134e1779SJakub Wojciech Klama 
2558*134e1779SJakub Wojciech Klama 		gid = mask & L9PL_SETATTR_GID
2559*134e1779SJakub Wojciech Klama 		    ? req->lr_req.tsetattr.gid
2560*134e1779SJakub Wojciech Klama 		    : (gid_t)-1;
2561*134e1779SJakub Wojciech Klama 
2562*134e1779SJakub Wojciech Klama 		if (fchownat(file->ff_dirfd, file->ff_name, uid, gid,
2563*134e1779SJakub Wojciech Klama 		    AT_SYMLINK_NOFOLLOW)) {
2564*134e1779SJakub Wojciech Klama 			error = errno;
2565*134e1779SJakub Wojciech Klama 			goto out;
2566*134e1779SJakub Wojciech Klama 		}
2567*134e1779SJakub Wojciech Klama 	}
2568*134e1779SJakub Wojciech Klama 
2569*134e1779SJakub Wojciech Klama 	if (mask & L9PL_SETATTR_SIZE) {
2570*134e1779SJakub Wojciech Klama 		/* Truncate follows symlinks, is this OK? */
2571*134e1779SJakub Wojciech Klama 		int fd = openat(file->ff_dirfd, file->ff_name, O_RDWR);
2572*134e1779SJakub Wojciech Klama 		if (ftruncate(fd, (off_t)req->lr_req.tsetattr.size)) {
2573*134e1779SJakub Wojciech Klama 			error = errno;
2574*134e1779SJakub Wojciech Klama 			(void) close(fd);
2575*134e1779SJakub Wojciech Klama 			goto out;
2576*134e1779SJakub Wojciech Klama 		}
2577*134e1779SJakub Wojciech Klama 		(void) close(fd);
2578*134e1779SJakub Wojciech Klama 	}
2579*134e1779SJakub Wojciech Klama 
2580*134e1779SJakub Wojciech Klama 	if (mask & (L9PL_SETATTR_ATIME | L9PL_SETATTR_MTIME)) {
2581*134e1779SJakub Wojciech Klama 		ts[0].tv_sec = st.st_atimespec.tv_sec;
2582*134e1779SJakub Wojciech Klama 		ts[0].tv_nsec = st.st_atimespec.tv_nsec;
2583*134e1779SJakub Wojciech Klama 		ts[1].tv_sec = st.st_mtimespec.tv_sec;
2584*134e1779SJakub Wojciech Klama 		ts[1].tv_nsec = st.st_mtimespec.tv_nsec;
2585*134e1779SJakub Wojciech Klama 
2586*134e1779SJakub Wojciech Klama 		if (mask & L9PL_SETATTR_ATIME) {
2587*134e1779SJakub Wojciech Klama 			if (mask & L9PL_SETATTR_ATIME_SET) {
2588*134e1779SJakub Wojciech Klama 				ts[0].tv_sec = req->lr_req.tsetattr.atime_sec;
2589*134e1779SJakub Wojciech Klama 				ts[0].tv_nsec = req->lr_req.tsetattr.atime_nsec;
2590*134e1779SJakub Wojciech Klama 			} else {
2591*134e1779SJakub Wojciech Klama 				if (clock_gettime(CLOCK_REALTIME, &ts[0]) != 0) {
2592*134e1779SJakub Wojciech Klama 					error = errno;
2593*134e1779SJakub Wojciech Klama 					goto out;
2594*134e1779SJakub Wojciech Klama 				}
2595*134e1779SJakub Wojciech Klama 			}
2596*134e1779SJakub Wojciech Klama 		}
2597*134e1779SJakub Wojciech Klama 
2598*134e1779SJakub Wojciech Klama 		if (mask & L9PL_SETATTR_MTIME) {
2599*134e1779SJakub Wojciech Klama 			if (mask & L9PL_SETATTR_MTIME_SET) {
2600*134e1779SJakub Wojciech Klama 				ts[1].tv_sec = req->lr_req.tsetattr.mtime_sec;
2601*134e1779SJakub Wojciech Klama 				ts[1].tv_nsec = req->lr_req.tsetattr.mtime_nsec;
2602*134e1779SJakub Wojciech Klama 			} else {
2603*134e1779SJakub Wojciech Klama 				if (clock_gettime(CLOCK_REALTIME, &ts[1]) != 0) {
2604*134e1779SJakub Wojciech Klama 					error = errno;
2605*134e1779SJakub Wojciech Klama 					goto out;
2606*134e1779SJakub Wojciech Klama 				}
2607*134e1779SJakub Wojciech Klama 			}
2608*134e1779SJakub Wojciech Klama 		}
2609*134e1779SJakub Wojciech Klama 
2610*134e1779SJakub Wojciech Klama 		if (utimensat(file->ff_dirfd, file->ff_name, ts,
2611*134e1779SJakub Wojciech Klama 		    AT_SYMLINK_NOFOLLOW)) {
2612*134e1779SJakub Wojciech Klama 			error = errno;
2613*134e1779SJakub Wojciech Klama 			goto out;
2614*134e1779SJakub Wojciech Klama 		}
2615*134e1779SJakub Wojciech Klama 	}
2616*134e1779SJakub Wojciech Klama out:
2617*134e1779SJakub Wojciech Klama 	return (error);
2618*134e1779SJakub Wojciech Klama }
2619*134e1779SJakub Wojciech Klama 
2620*134e1779SJakub Wojciech Klama static int
fs_xattrwalk(void * softc __unused,struct l9p_request * req __unused)2621*134e1779SJakub Wojciech Klama fs_xattrwalk(void *softc __unused, struct l9p_request *req __unused)
2622*134e1779SJakub Wojciech Klama {
2623*134e1779SJakub Wojciech Klama 	return (EOPNOTSUPP);
2624*134e1779SJakub Wojciech Klama }
2625*134e1779SJakub Wojciech Klama 
2626*134e1779SJakub Wojciech Klama static int
fs_xattrcreate(void * softc __unused,struct l9p_request * req __unused)2627*134e1779SJakub Wojciech Klama fs_xattrcreate(void *softc __unused, struct l9p_request *req __unused)
2628*134e1779SJakub Wojciech Klama {
2629*134e1779SJakub Wojciech Klama 	return (EOPNOTSUPP);
2630*134e1779SJakub Wojciech Klama }
2631*134e1779SJakub Wojciech Klama 
2632*134e1779SJakub Wojciech Klama static int
fs_readdir(void * softc __unused,struct l9p_request * req)2633*134e1779SJakub Wojciech Klama fs_readdir(void *softc __unused, struct l9p_request *req)
2634*134e1779SJakub Wojciech Klama {
2635*134e1779SJakub Wojciech Klama 	struct l9p_message msg;
2636*134e1779SJakub Wojciech Klama 	struct l9p_dirent de;
2637*134e1779SJakub Wojciech Klama 	struct fs_fid *file;
2638*134e1779SJakub Wojciech Klama 	struct dirent *dp;
2639*134e1779SJakub Wojciech Klama 	struct stat st;
2640*134e1779SJakub Wojciech Klama 	uint32_t count;
2641*134e1779SJakub Wojciech Klama 	int error = 0;
2642*134e1779SJakub Wojciech Klama 
2643*134e1779SJakub Wojciech Klama 	file = req->lr_fid->lo_aux;
2644*134e1779SJakub Wojciech Klama 	assert(file);
2645*134e1779SJakub Wojciech Klama 
2646*134e1779SJakub Wojciech Klama 	if (file->ff_dir == NULL)
2647*134e1779SJakub Wojciech Klama 		return (ENOTDIR);
2648*134e1779SJakub Wojciech Klama 
2649*134e1779SJakub Wojciech Klama 	pthread_mutex_lock(&file->ff_mtx);
2650*134e1779SJakub Wojciech Klama 
2651*134e1779SJakub Wojciech Klama 	/*
2652*134e1779SJakub Wojciech Klama 	 * It's not clear whether we can use the same trick for
2653*134e1779SJakub Wojciech Klama 	 * discarding offsets here as we do in fs_read.  It
2654*134e1779SJakub Wojciech Klama 	 * probably should work, we'll have to see if some
2655*134e1779SJakub Wojciech Klama 	 * client(s) use the zero-offset thing to rescan without
2656*134e1779SJakub Wojciech Klama 	 * clunking the directory first.
2657*134e1779SJakub Wojciech Klama 	 *
2658*134e1779SJakub Wojciech Klama 	 * Probably the thing to do is switch to calling
2659*134e1779SJakub Wojciech Klama 	 * getdirentries() / getdents() directly, instead of
2660*134e1779SJakub Wojciech Klama 	 * going through libc.
2661*134e1779SJakub Wojciech Klama 	 */
2662*134e1779SJakub Wojciech Klama 	if (req->lr_req.io.offset == 0)
2663*134e1779SJakub Wojciech Klama 		rewinddir(file->ff_dir);
2664*134e1779SJakub Wojciech Klama 	else
2665*134e1779SJakub Wojciech Klama 		seekdir(file->ff_dir, (long)req->lr_req.io.offset);
2666*134e1779SJakub Wojciech Klama 
2667*134e1779SJakub Wojciech Klama 	l9p_init_msg(&msg, req, L9P_PACK);
2668*134e1779SJakub Wojciech Klama 	count = (uint32_t)msg.lm_size; /* in case we get no entries */
2669*134e1779SJakub Wojciech Klama 	while ((dp = readdir(file->ff_dir)) != NULL) {
2670*134e1779SJakub Wojciech Klama 		/*
2671*134e1779SJakub Wojciech Klama 		 * Although "." is forbidden in naming and ".." is
2672*134e1779SJakub Wojciech Klama 		 * special cased, testing shows that we must transmit
2673*134e1779SJakub Wojciech Klama 		 * them through readdir.  (For ".." at root, we
2674*134e1779SJakub Wojciech Klama 		 * should perhaps alter the inode number, but not
2675*134e1779SJakub Wojciech Klama 		 * yet.)
2676*134e1779SJakub Wojciech Klama 		 */
2677*134e1779SJakub Wojciech Klama 
2678*134e1779SJakub Wojciech Klama 		/*
2679*134e1779SJakub Wojciech Klama 		 * TODO: we do a full lstat here; could use dp->d_*
2680*134e1779SJakub Wojciech Klama 		 * to construct the qid more efficiently, as long
2681*134e1779SJakub Wojciech Klama 		 * as dp->d_type != DT_UNKNOWN.
2682*134e1779SJakub Wojciech Klama 		 */
2683*134e1779SJakub Wojciech Klama 		if (fs_lstatat(file, dp->d_name, &st))
2684*134e1779SJakub Wojciech Klama 			continue;
2685*134e1779SJakub Wojciech Klama 
2686*134e1779SJakub Wojciech Klama 		de.qid.type = 0;
2687*134e1779SJakub Wojciech Klama 		generate_qid(&st, &de.qid);
2688*134e1779SJakub Wojciech Klama 		de.offset = (uint64_t)telldir(file->ff_dir);
2689*134e1779SJakub Wojciech Klama 		de.type = dp->d_type;
2690*134e1779SJakub Wojciech Klama 		de.name = dp->d_name;
2691*134e1779SJakub Wojciech Klama 
2692*134e1779SJakub Wojciech Klama 		/* Update count only if we completely pack the dirent. */
2693*134e1779SJakub Wojciech Klama 		if (l9p_pudirent(&msg, &de) < 0)
2694*134e1779SJakub Wojciech Klama 			break;
2695*134e1779SJakub Wojciech Klama 		count = (uint32_t)msg.lm_size;
2696*134e1779SJakub Wojciech Klama 	}
2697*134e1779SJakub Wojciech Klama 
2698*134e1779SJakub Wojciech Klama 	pthread_mutex_unlock(&file->ff_mtx);
2699*134e1779SJakub Wojciech Klama 	req->lr_resp.io.count = count;
2700*134e1779SJakub Wojciech Klama 	return (error);
2701*134e1779SJakub Wojciech Klama }
2702*134e1779SJakub Wojciech Klama 
2703*134e1779SJakub Wojciech Klama static int
fs_fsync(void * softc __unused,struct l9p_request * req)2704*134e1779SJakub Wojciech Klama fs_fsync(void *softc __unused, struct l9p_request *req)
2705*134e1779SJakub Wojciech Klama {
2706*134e1779SJakub Wojciech Klama 	struct fs_fid *file;
2707*134e1779SJakub Wojciech Klama 	int error = 0;
2708*134e1779SJakub Wojciech Klama 
2709*134e1779SJakub Wojciech Klama 	file = req->lr_fid->lo_aux;
2710*134e1779SJakub Wojciech Klama 	assert(file);
2711*134e1779SJakub Wojciech Klama 	if (fsync(file->ff_dir != NULL ? dirfd(file->ff_dir) : file->ff_fd))
2712*134e1779SJakub Wojciech Klama 		error = errno;
2713*134e1779SJakub Wojciech Klama 	return (error);
2714*134e1779SJakub Wojciech Klama }
2715*134e1779SJakub Wojciech Klama 
2716*134e1779SJakub Wojciech Klama static int
fs_lock(void * softc __unused,struct l9p_request * req)2717*134e1779SJakub Wojciech Klama fs_lock(void *softc __unused, struct l9p_request *req)
2718*134e1779SJakub Wojciech Klama {
2719*134e1779SJakub Wojciech Klama 
2720*134e1779SJakub Wojciech Klama 	switch (req->lr_req.tlock.type) {
2721*134e1779SJakub Wojciech Klama 	case L9PL_LOCK_TYPE_RDLOCK:
2722*134e1779SJakub Wojciech Klama 	case L9PL_LOCK_TYPE_WRLOCK:
2723*134e1779SJakub Wojciech Klama 	case L9PL_LOCK_TYPE_UNLOCK:
2724*134e1779SJakub Wojciech Klama 		break;
2725*134e1779SJakub Wojciech Klama 	default:
2726*134e1779SJakub Wojciech Klama 		return (EINVAL);
2727*134e1779SJakub Wojciech Klama 	}
2728*134e1779SJakub Wojciech Klama 
2729*134e1779SJakub Wojciech Klama 	req->lr_resp.rlock.status = L9PL_LOCK_SUCCESS;
2730*134e1779SJakub Wojciech Klama 	return (0);
2731*134e1779SJakub Wojciech Klama }
2732*134e1779SJakub Wojciech Klama 
2733*134e1779SJakub Wojciech Klama static int
fs_getlock(void * softc __unused,struct l9p_request * req)2734*134e1779SJakub Wojciech Klama fs_getlock(void *softc __unused, struct l9p_request *req)
2735*134e1779SJakub Wojciech Klama {
2736*134e1779SJakub Wojciech Klama 
2737*134e1779SJakub Wojciech Klama 	/*
2738*134e1779SJakub Wojciech Klama 	 * Client wants to see if a request to lock a region would
2739*134e1779SJakub Wojciech Klama 	 * block.  This is, of course, not atomic anyway, so the
2740*134e1779SJakub Wojciech Klama 	 * op is useless.  QEMU simply says "unlocked!", so we do
2741*134e1779SJakub Wojciech Klama 	 * too.
2742*134e1779SJakub Wojciech Klama 	 */
2743*134e1779SJakub Wojciech Klama 	switch (req->lr_req.getlock.type) {
2744*134e1779SJakub Wojciech Klama 	case L9PL_LOCK_TYPE_RDLOCK:
2745*134e1779SJakub Wojciech Klama 	case L9PL_LOCK_TYPE_WRLOCK:
2746*134e1779SJakub Wojciech Klama 	case L9PL_LOCK_TYPE_UNLOCK:
2747*134e1779SJakub Wojciech Klama 		break;
2748*134e1779SJakub Wojciech Klama 	default:
2749*134e1779SJakub Wojciech Klama 		return (EINVAL);
2750*134e1779SJakub Wojciech Klama 	}
2751*134e1779SJakub Wojciech Klama 
2752*134e1779SJakub Wojciech Klama 	req->lr_resp.getlock = req->lr_req.getlock;
2753*134e1779SJakub Wojciech Klama 	req->lr_resp.getlock.type = L9PL_LOCK_TYPE_UNLOCK;
2754*134e1779SJakub Wojciech Klama 	req->lr_resp.getlock.client_id = strdup("");  /* XXX what should go here? */
2755*134e1779SJakub Wojciech Klama 	return (0);
2756*134e1779SJakub Wojciech Klama }
2757*134e1779SJakub Wojciech Klama 
2758*134e1779SJakub Wojciech Klama static int
fs_link(void * softc __unused,struct l9p_request * req)2759*134e1779SJakub Wojciech Klama fs_link(void *softc __unused, struct l9p_request *req)
2760*134e1779SJakub Wojciech Klama {
2761*134e1779SJakub Wojciech Klama 	struct l9p_fid *dir;
2762*134e1779SJakub Wojciech Klama 	struct fs_fid *file;
2763*134e1779SJakub Wojciech Klama 	struct fs_fid *dirf;
2764*134e1779SJakub Wojciech Klama 	struct stat fst, tdst;
2765*134e1779SJakub Wojciech Klama 	int32_t op;
2766*134e1779SJakub Wojciech Klama 	char *name;
2767*134e1779SJakub Wojciech Klama 	char newname[MAXPATHLEN];
2768*134e1779SJakub Wojciech Klama 	int error;
2769*134e1779SJakub Wojciech Klama 
2770*134e1779SJakub Wojciech Klama 	/* N.B.: lr_fid is the file to link, lr_fid2 is the target dir */
2771*134e1779SJakub Wojciech Klama 	dir = req->lr_fid2;
2772*134e1779SJakub Wojciech Klama 	dirf = dir->lo_aux;
2773*134e1779SJakub Wojciech Klama 	assert(dirf != NULL);
2774*134e1779SJakub Wojciech Klama 
2775*134e1779SJakub Wojciech Klama 	name = req->lr_req.tlink.name;
2776*134e1779SJakub Wojciech Klama 	error = fs_buildname(dir, name, newname, sizeof(newname));
2777*134e1779SJakub Wojciech Klama 	if (error)
2778*134e1779SJakub Wojciech Klama 		return (error);
2779*134e1779SJakub Wojciech Klama 
2780*134e1779SJakub Wojciech Klama 	file = req->lr_fid->lo_aux;
2781*134e1779SJakub Wojciech Klama 	assert(file != NULL);
2782*134e1779SJakub Wojciech Klama 
2783*134e1779SJakub Wojciech Klama 	if (fstatat(dirf->ff_dirfd, dirf->ff_name, &tdst, AT_SYMLINK_NOFOLLOW) != 0 ||
2784*134e1779SJakub Wojciech Klama 	    fstatat(file->ff_dirfd, file->ff_name, &fst, AT_SYMLINK_NOFOLLOW) != 0)
2785*134e1779SJakub Wojciech Klama 		return (errno);
2786*134e1779SJakub Wojciech Klama 	if (S_ISDIR(fst.st_mode))
2787*134e1779SJakub Wojciech Klama 		return (EISDIR);
2788*134e1779SJakub Wojciech Klama 	fillacl(dirf);
2789*134e1779SJakub Wojciech Klama 	op = S_ISDIR(fst.st_mode) ? L9P_ACE_ADD_SUBDIRECTORY : L9P_ACE_ADD_FILE;
2790*134e1779SJakub Wojciech Klama 	error = check_access(op,
2791*134e1779SJakub Wojciech Klama 	    dirf->ff_acl, &tdst, NULL, NULL, file->ff_ai, (gid_t)-1);
2792*134e1779SJakub Wojciech Klama 	if (error)
2793*134e1779SJakub Wojciech Klama 		return (error);
2794*134e1779SJakub Wojciech Klama 
2795*134e1779SJakub Wojciech Klama 	if (linkat(file->ff_dirfd, file->ff_name, file->ff_dirfd,
2796*134e1779SJakub Wojciech Klama 	    newname, 0) != 0)
2797*134e1779SJakub Wojciech Klama 		error = errno;
2798*134e1779SJakub Wojciech Klama 	else
2799*134e1779SJakub Wojciech Klama 		dropacl(file);
2800*134e1779SJakub Wojciech Klama 
2801*134e1779SJakub Wojciech Klama 	return (error);
2802*134e1779SJakub Wojciech Klama }
2803*134e1779SJakub Wojciech Klama 
2804*134e1779SJakub Wojciech Klama static int
fs_mkdir(void * softc,struct l9p_request * req)2805*134e1779SJakub Wojciech Klama fs_mkdir(void *softc, struct l9p_request *req)
2806*134e1779SJakub Wojciech Klama {
2807*134e1779SJakub Wojciech Klama 	struct l9p_fid *dir;
2808*134e1779SJakub Wojciech Klama 	struct stat st;
2809*134e1779SJakub Wojciech Klama 	mode_t perm;
2810*134e1779SJakub Wojciech Klama 	gid_t gid;
2811*134e1779SJakub Wojciech Klama 	char *name;
2812*134e1779SJakub Wojciech Klama 	int error;
2813*134e1779SJakub Wojciech Klama 
2814*134e1779SJakub Wojciech Klama 	dir = req->lr_fid;
2815*134e1779SJakub Wojciech Klama 	name = req->lr_req.tmkdir.name;
2816*134e1779SJakub Wojciech Klama 	perm = (mode_t)req->lr_req.tmkdir.mode;
2817*134e1779SJakub Wojciech Klama 	gid = req->lr_req.tmkdir.gid;
2818*134e1779SJakub Wojciech Klama 
2819*134e1779SJakub Wojciech Klama 	error = fs_imkdir(softc, dir, name, false, perm, gid, &st);
2820*134e1779SJakub Wojciech Klama 	if (error == 0)
2821*134e1779SJakub Wojciech Klama 		generate_qid(&st, &req->lr_resp.rmkdir.qid);
2822*134e1779SJakub Wojciech Klama 	return (error);
2823*134e1779SJakub Wojciech Klama }
2824*134e1779SJakub Wojciech Klama 
2825*134e1779SJakub Wojciech Klama static int
fs_renameat(void * softc,struct l9p_request * req)2826*134e1779SJakub Wojciech Klama fs_renameat(void *softc, struct l9p_request *req)
2827*134e1779SJakub Wojciech Klama {
2828*134e1779SJakub Wojciech Klama 	struct fs_softc *sc = softc;
2829*134e1779SJakub Wojciech Klama 	struct l9p_fid *olddir, *newdir;
2830*134e1779SJakub Wojciech Klama 	struct l9p_acl *facl;
2831*134e1779SJakub Wojciech Klama 	struct fs_fid *off, *nff;
2832*134e1779SJakub Wojciech Klama 	struct stat odst, ndst, fst;
2833*134e1779SJakub Wojciech Klama 	int32_t op;
2834*134e1779SJakub Wojciech Klama 	bool reparenting;
2835*134e1779SJakub Wojciech Klama 	char *onp, *nnp;
2836*134e1779SJakub Wojciech Klama 	char onb[MAXPATHLEN], nnb[MAXPATHLEN];
2837*134e1779SJakub Wojciech Klama 	int error;
2838*134e1779SJakub Wojciech Klama 
2839*134e1779SJakub Wojciech Klama 	if (sc->fs_readonly)
2840*134e1779SJakub Wojciech Klama 		return (EROFS);
2841*134e1779SJakub Wojciech Klama 
2842*134e1779SJakub Wojciech Klama 	olddir = req->lr_fid;
2843*134e1779SJakub Wojciech Klama 	newdir = req->lr_fid2;
2844*134e1779SJakub Wojciech Klama 	assert(olddir != NULL && newdir != NULL);
2845*134e1779SJakub Wojciech Klama 	off = olddir->lo_aux;
2846*134e1779SJakub Wojciech Klama 	nff = newdir->lo_aux;
2847*134e1779SJakub Wojciech Klama 	assert(off != NULL && nff != NULL);
2848*134e1779SJakub Wojciech Klama 
2849*134e1779SJakub Wojciech Klama 	onp = req->lr_req.trenameat.oldname;
2850*134e1779SJakub Wojciech Klama 	nnp = req->lr_req.trenameat.newname;
2851*134e1779SJakub Wojciech Klama 	error = fs_buildname(olddir, onp, onb, sizeof(onb));
2852*134e1779SJakub Wojciech Klama 	if (error)
2853*134e1779SJakub Wojciech Klama 		return (error);
2854*134e1779SJakub Wojciech Klama 	error = fs_buildname(newdir, nnp, nnb, sizeof(nnb));
2855*134e1779SJakub Wojciech Klama 	if (error)
2856*134e1779SJakub Wojciech Klama 		return (error);
2857*134e1779SJakub Wojciech Klama 	if (fstatat(off->ff_dirfd, onb, &fst, AT_SYMLINK_NOFOLLOW) != 0)
2858*134e1779SJakub Wojciech Klama 		return (errno);
2859*134e1779SJakub Wojciech Klama 
2860*134e1779SJakub Wojciech Klama 	reparenting = olddir != newdir &&
2861*134e1779SJakub Wojciech Klama 	    strcmp(off->ff_name, nff->ff_name) != 0;
2862*134e1779SJakub Wojciech Klama 
2863*134e1779SJakub Wojciech Klama 	if (fstatat(off->ff_dirfd, off->ff_name, &odst, AT_SYMLINK_NOFOLLOW) != 0)
2864*134e1779SJakub Wojciech Klama 		return (errno);
2865*134e1779SJakub Wojciech Klama 	if (!S_ISDIR(odst.st_mode))
2866*134e1779SJakub Wojciech Klama 		return (ENOTDIR);
2867*134e1779SJakub Wojciech Klama 	fillacl(off);
2868*134e1779SJakub Wojciech Klama 
2869*134e1779SJakub Wojciech Klama 	if (reparenting) {
2870*134e1779SJakub Wojciech Klama 		if (fstatat(nff->ff_dirfd, nff->ff_name, &ndst, AT_SYMLINK_NOFOLLOW) != 0)
2871*134e1779SJakub Wojciech Klama 			return (errno);
2872*134e1779SJakub Wojciech Klama 		if (!S_ISDIR(ndst.st_mode))
2873*134e1779SJakub Wojciech Klama 			return (ENOTDIR);
2874*134e1779SJakub Wojciech Klama 		facl = getacl(off, -1, onb);
2875*134e1779SJakub Wojciech Klama 		fillacl(nff);
2876*134e1779SJakub Wojciech Klama 
2877*134e1779SJakub Wojciech Klama 		error = check_access(L9P_ACOP_UNLINK,
2878*134e1779SJakub Wojciech Klama 		    off->ff_acl, &odst, facl, &fst, off->ff_ai, (gid_t)-1);
2879*134e1779SJakub Wojciech Klama 		l9p_acl_free(facl);
2880*134e1779SJakub Wojciech Klama 		if (error)
2881*134e1779SJakub Wojciech Klama 			return (error);
2882*134e1779SJakub Wojciech Klama 		op = S_ISDIR(fst.st_mode) ? L9P_ACE_ADD_SUBDIRECTORY :
2883*134e1779SJakub Wojciech Klama 		    L9P_ACE_ADD_FILE;
2884*134e1779SJakub Wojciech Klama 		error = check_access(op,
2885*134e1779SJakub Wojciech Klama 		    nff->ff_acl, &ndst, NULL, NULL, nff->ff_ai, (gid_t)-1);
2886*134e1779SJakub Wojciech Klama 		if (error)
2887*134e1779SJakub Wojciech Klama 			return (error);
2888*134e1779SJakub Wojciech Klama 	}
2889*134e1779SJakub Wojciech Klama 
2890*134e1779SJakub Wojciech Klama 	if (renameat(off->ff_dirfd, onb, nff->ff_dirfd, nnb))
2891*134e1779SJakub Wojciech Klama 		error = errno;
2892*134e1779SJakub Wojciech Klama 
2893*134e1779SJakub Wojciech Klama 	return (error);
2894*134e1779SJakub Wojciech Klama }
2895*134e1779SJakub Wojciech Klama 
2896*134e1779SJakub Wojciech Klama /*
2897*134e1779SJakub Wojciech Klama  * Unlink file in given directory, or remove directory in given
2898*134e1779SJakub Wojciech Klama  * directory, based on flags.
2899*134e1779SJakub Wojciech Klama  */
2900*134e1779SJakub Wojciech Klama static int
fs_unlinkat(void * softc,struct l9p_request * req)2901*134e1779SJakub Wojciech Klama fs_unlinkat(void *softc, struct l9p_request *req)
2902*134e1779SJakub Wojciech Klama {
2903*134e1779SJakub Wojciech Klama 	struct fs_softc *sc = softc;
2904*134e1779SJakub Wojciech Klama 	struct l9p_acl *facl;
2905*134e1779SJakub Wojciech Klama 	struct l9p_fid *dir;
2906*134e1779SJakub Wojciech Klama 	struct fs_fid *dirff;
2907*134e1779SJakub Wojciech Klama 	struct stat dirst, fst;
2908*134e1779SJakub Wojciech Klama 	char *name;
2909*134e1779SJakub Wojciech Klama 	char newname[MAXPATHLEN];
2910*134e1779SJakub Wojciech Klama 	int error;
2911*134e1779SJakub Wojciech Klama 
2912*134e1779SJakub Wojciech Klama 	if (sc->fs_readonly)
2913*134e1779SJakub Wojciech Klama 		return (EROFS);
2914*134e1779SJakub Wojciech Klama 
2915*134e1779SJakub Wojciech Klama 	dir = req->lr_fid;
2916*134e1779SJakub Wojciech Klama 	dirff = dir->lo_aux;
2917*134e1779SJakub Wojciech Klama 	assert(dirff != NULL);
2918*134e1779SJakub Wojciech Klama 	name = req->lr_req.tunlinkat.name;
2919*134e1779SJakub Wojciech Klama 	error = fs_buildname(dir, name, newname, sizeof(newname));
2920*134e1779SJakub Wojciech Klama 	if (error)
2921*134e1779SJakub Wojciech Klama 		return (error);
2922*134e1779SJakub Wojciech Klama 	if (fstatat(dirff->ff_dirfd, newname, &fst, AT_SYMLINK_NOFOLLOW) != 0 ||
2923*134e1779SJakub Wojciech Klama 	    fstatat(dirff->ff_dirfd, dirff->ff_name, &dirst, AT_SYMLINK_NOFOLLOW) != 0)
2924*134e1779SJakub Wojciech Klama 		return (errno);
2925*134e1779SJakub Wojciech Klama 	fillacl(dirff);
2926*134e1779SJakub Wojciech Klama 	facl = getacl(dirff, -1, newname);
2927*134e1779SJakub Wojciech Klama 	error = check_access(L9P_ACOP_UNLINK,
2928*134e1779SJakub Wojciech Klama 	    dirff->ff_acl, &dirst, facl, &fst, dirff->ff_ai, (gid_t)-1);
2929*134e1779SJakub Wojciech Klama 	l9p_acl_free(facl);
2930*134e1779SJakub Wojciech Klama 	if (error)
2931*134e1779SJakub Wojciech Klama 		return (error);
2932*134e1779SJakub Wojciech Klama 
2933*134e1779SJakub Wojciech Klama 	if (req->lr_req.tunlinkat.flags & L9PL_AT_REMOVEDIR) {
2934*134e1779SJakub Wojciech Klama 		if (unlinkat(dirff->ff_dirfd, newname, AT_REMOVEDIR) != 0)
2935*134e1779SJakub Wojciech Klama 			error = errno;
2936*134e1779SJakub Wojciech Klama 	} else {
2937*134e1779SJakub Wojciech Klama 		if (unlinkat(dirff->ff_dirfd, newname, 0) != 0)
2938*134e1779SJakub Wojciech Klama 			error = errno;
2939*134e1779SJakub Wojciech Klama 	}
2940*134e1779SJakub Wojciech Klama 	return (error);
2941*134e1779SJakub Wojciech Klama }
2942*134e1779SJakub Wojciech Klama 
2943*134e1779SJakub Wojciech Klama static void
fs_freefid(void * softc __unused,struct l9p_fid * fid)2944*134e1779SJakub Wojciech Klama fs_freefid(void *softc __unused, struct l9p_fid *fid)
2945*134e1779SJakub Wojciech Klama {
2946*134e1779SJakub Wojciech Klama 	struct fs_fid *f = fid->lo_aux;
2947*134e1779SJakub Wojciech Klama 	struct fs_authinfo *ai;
2948*134e1779SJakub Wojciech Klama 	uint32_t newcount;
2949*134e1779SJakub Wojciech Klama 
2950*134e1779SJakub Wojciech Klama 	if (f == NULL) {
2951*134e1779SJakub Wojciech Klama 		/* Nothing to do here */
2952*134e1779SJakub Wojciech Klama 		return;
2953*134e1779SJakub Wojciech Klama 	}
2954*134e1779SJakub Wojciech Klama 
2955*134e1779SJakub Wojciech Klama 	if (f->ff_fd != -1)
2956*134e1779SJakub Wojciech Klama 		close(f->ff_fd);
2957*134e1779SJakub Wojciech Klama 
2958*134e1779SJakub Wojciech Klama 	if (f->ff_dir)
2959*134e1779SJakub Wojciech Klama 		closedir(f->ff_dir);
2960*134e1779SJakub Wojciech Klama 
2961*134e1779SJakub Wojciech Klama 	pthread_mutex_destroy(&f->ff_mtx);
2962*134e1779SJakub Wojciech Klama 	free(f->ff_name);
2963*134e1779SJakub Wojciech Klama 	ai = f->ff_ai;
2964*134e1779SJakub Wojciech Klama 	l9p_acl_free(f->ff_acl);
2965*134e1779SJakub Wojciech Klama 	free(f);
2966*134e1779SJakub Wojciech Klama 	pthread_mutex_lock(&ai->ai_mtx);
2967*134e1779SJakub Wojciech Klama 	newcount = --ai->ai_refcnt;
2968*134e1779SJakub Wojciech Klama 	pthread_mutex_unlock(&ai->ai_mtx);
2969*134e1779SJakub Wojciech Klama 	if (newcount == 0) {
2970*134e1779SJakub Wojciech Klama 		/*
2971*134e1779SJakub Wojciech Klama 		 * We *were* the last ref, no one can have gained a ref.
2972*134e1779SJakub Wojciech Klama 		 */
2973*134e1779SJakub Wojciech Klama 		L9P_LOG(L9P_DEBUG, "dropped last ref to authinfo %p",
2974*134e1779SJakub Wojciech Klama 		    (void *)ai);
2975*134e1779SJakub Wojciech Klama 		pthread_mutex_destroy(&ai->ai_mtx);
2976*134e1779SJakub Wojciech Klama 		free(ai);
2977*134e1779SJakub Wojciech Klama 	} else {
2978*134e1779SJakub Wojciech Klama 		L9P_LOG(L9P_DEBUG, "authinfo %p now used by %lu",
2979*134e1779SJakub Wojciech Klama 		    (void *)ai, (u_long)newcount);
2980*134e1779SJakub Wojciech Klama 	}
2981*134e1779SJakub Wojciech Klama }
2982*134e1779SJakub Wojciech Klama 
2983*134e1779SJakub Wojciech Klama int
l9p_backend_fs_init(struct l9p_backend ** backendp,int rootfd,bool ro)2984*134e1779SJakub Wojciech Klama l9p_backend_fs_init(struct l9p_backend **backendp, int rootfd, bool ro)
2985*134e1779SJakub Wojciech Klama {
2986*134e1779SJakub Wojciech Klama 	struct l9p_backend *backend;
2987*134e1779SJakub Wojciech Klama 	struct fs_softc *sc;
2988*134e1779SJakub Wojciech Klama 	int error;
2989*134e1779SJakub Wojciech Klama #if defined(WITH_CASPER)
2990*134e1779SJakub Wojciech Klama 	cap_channel_t *capcas;
2991*134e1779SJakub Wojciech Klama #endif
2992*134e1779SJakub Wojciech Klama 
2993*134e1779SJakub Wojciech Klama 	if (!fs_attach_mutex_inited) {
2994*134e1779SJakub Wojciech Klama 		error = pthread_mutex_init(&fs_attach_mutex, NULL);
2995*134e1779SJakub Wojciech Klama 		if (error) {
2996*134e1779SJakub Wojciech Klama 			errno = error;
2997*134e1779SJakub Wojciech Klama 			return (-1);
2998*134e1779SJakub Wojciech Klama 		}
2999*134e1779SJakub Wojciech Klama 		fs_attach_mutex_inited = true;
3000*134e1779SJakub Wojciech Klama 	}
3001*134e1779SJakub Wojciech Klama 
3002*134e1779SJakub Wojciech Klama 	backend = l9p_malloc(sizeof(*backend));
3003*134e1779SJakub Wojciech Klama 	backend->attach = fs_attach;
3004*134e1779SJakub Wojciech Klama 	backend->clunk = fs_clunk;
3005*134e1779SJakub Wojciech Klama 	backend->create = fs_create;
3006*134e1779SJakub Wojciech Klama 	backend->open = fs_open;
3007*134e1779SJakub Wojciech Klama 	backend->read = fs_read;
3008*134e1779SJakub Wojciech Klama 	backend->remove = fs_remove;
3009*134e1779SJakub Wojciech Klama 	backend->stat = fs_stat;
3010*134e1779SJakub Wojciech Klama 	backend->walk = fs_walk;
3011*134e1779SJakub Wojciech Klama 	backend->write = fs_write;
3012*134e1779SJakub Wojciech Klama 	backend->wstat = fs_wstat;
3013*134e1779SJakub Wojciech Klama 	backend->statfs = fs_statfs;
3014*134e1779SJakub Wojciech Klama 	backend->lopen = fs_lopen;
3015*134e1779SJakub Wojciech Klama 	backend->lcreate = fs_lcreate;
3016*134e1779SJakub Wojciech Klama 	backend->symlink = fs_symlink;
3017*134e1779SJakub Wojciech Klama 	backend->mknod = fs_mknod;
3018*134e1779SJakub Wojciech Klama 	backend->rename = fs_rename;
3019*134e1779SJakub Wojciech Klama 	backend->readlink = fs_readlink;
3020*134e1779SJakub Wojciech Klama 	backend->getattr = fs_getattr;
3021*134e1779SJakub Wojciech Klama 	backend->setattr = fs_setattr;
3022*134e1779SJakub Wojciech Klama 	backend->xattrwalk = fs_xattrwalk;
3023*134e1779SJakub Wojciech Klama 	backend->xattrcreate = fs_xattrcreate;
3024*134e1779SJakub Wojciech Klama 	backend->readdir = fs_readdir;
3025*134e1779SJakub Wojciech Klama 	backend->fsync = fs_fsync;
3026*134e1779SJakub Wojciech Klama 	backend->lock = fs_lock;
3027*134e1779SJakub Wojciech Klama 	backend->getlock = fs_getlock;
3028*134e1779SJakub Wojciech Klama 	backend->link = fs_link;
3029*134e1779SJakub Wojciech Klama 	backend->mkdir = fs_mkdir;
3030*134e1779SJakub Wojciech Klama 	backend->renameat = fs_renameat;
3031*134e1779SJakub Wojciech Klama 	backend->unlinkat = fs_unlinkat;
3032*134e1779SJakub Wojciech Klama 	backend->freefid = fs_freefid;
3033*134e1779SJakub Wojciech Klama 
3034*134e1779SJakub Wojciech Klama 	sc = l9p_malloc(sizeof(*sc));
3035*134e1779SJakub Wojciech Klama 	sc->fs_rootfd = rootfd;
3036*134e1779SJakub Wojciech Klama 	sc->fs_readonly = ro;
3037*134e1779SJakub Wojciech Klama 	backend->softc = sc;
3038*134e1779SJakub Wojciech Klama 
3039*134e1779SJakub Wojciech Klama #if defined(WITH_CASPER)
3040*134e1779SJakub Wojciech Klama 	capcas = cap_init();
3041*134e1779SJakub Wojciech Klama 	if (capcas == NULL)
3042*134e1779SJakub Wojciech Klama 		return (-1);
3043*134e1779SJakub Wojciech Klama 
3044*134e1779SJakub Wojciech Klama 	sc->fs_cappwd = cap_service_open(capcas, "system.pwd");
3045*134e1779SJakub Wojciech Klama 	if (sc->fs_cappwd == NULL)
3046*134e1779SJakub Wojciech Klama 		return (-1);
3047*134e1779SJakub Wojciech Klama 
3048*134e1779SJakub Wojciech Klama 	sc->fs_capgrp = cap_service_open(capcas, "system.grp");
3049*134e1779SJakub Wojciech Klama 	if (sc->fs_capgrp == NULL)
3050*134e1779SJakub Wojciech Klama 		return (-1);
3051*134e1779SJakub Wojciech Klama 
3052*134e1779SJakub Wojciech Klama 	cap_setpassent(sc->fs_cappwd, 1);
3053*134e1779SJakub Wojciech Klama 	cap_setgroupent(sc->fs_capgrp, 1);
3054*134e1779SJakub Wojciech Klama 	cap_close(capcas);
3055*134e1779SJakub Wojciech Klama #else
3056*134e1779SJakub Wojciech Klama 	setpassent(1);
3057*134e1779SJakub Wojciech Klama #endif
3058*134e1779SJakub Wojciech Klama 
3059*134e1779SJakub Wojciech Klama 	*backendp = backend;
3060*134e1779SJakub Wojciech Klama 	return (0);
3061*134e1779SJakub Wojciech Klama }
3062