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