xref: /netbsd-src/sys/fs/udf/udf_vnops.c (revision 040768683fc73d9bbf7e5765a7f6adb6fef82b08)
1*04076868Sreinoud /* $NetBSD: udf_vnops.c,v 1.127 2023/06/27 09:58:50 reinoud Exp $ */
2cff5e7adSreinoud 
3cff5e7adSreinoud /*
4e979c658Sreinoud  * Copyright (c) 2006, 2008 Reinoud Zandijk
5cff5e7adSreinoud  * All rights reserved.
6cff5e7adSreinoud  *
7cff5e7adSreinoud  * Redistribution and use in source and binary forms, with or without
8cff5e7adSreinoud  * modification, are permitted provided that the following conditions
9cff5e7adSreinoud  * are met:
10cff5e7adSreinoud  * 1. Redistributions of source code must retain the above copyright
11cff5e7adSreinoud  *    notice, this list of conditions and the following disclaimer.
12cff5e7adSreinoud  * 2. Redistributions in binary form must reproduce the above copyright
13cff5e7adSreinoud  *    notice, this list of conditions and the following disclaimer in the
14cff5e7adSreinoud  *    documentation and/or other materials provided with the distribution.
15cff5e7adSreinoud  *
16cff5e7adSreinoud  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17cff5e7adSreinoud  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18cff5e7adSreinoud  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19cff5e7adSreinoud  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20cff5e7adSreinoud  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21cff5e7adSreinoud  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22cff5e7adSreinoud  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23cff5e7adSreinoud  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24cff5e7adSreinoud  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25cff5e7adSreinoud  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26cff5e7adSreinoud  *
27e979c658Sreinoud  * Generic parts are derived from software contributed to The NetBSD Foundation
28e979c658Sreinoud  * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
29e979c658Sreinoud  * 2005 program.
30e979c658Sreinoud  *
31cff5e7adSreinoud  */
32cff5e7adSreinoud 
33cff5e7adSreinoud #include <sys/cdefs.h>
34cff5e7adSreinoud #ifndef lint
35*04076868Sreinoud __KERNEL_RCSID(0, "$NetBSD: udf_vnops.c,v 1.127 2023/06/27 09:58:50 reinoud Exp $");
36cff5e7adSreinoud #endif /* not lint */
37cff5e7adSreinoud 
38cff5e7adSreinoud 
39cff5e7adSreinoud #include <sys/param.h>
40cff5e7adSreinoud #include <sys/systm.h>
41cff5e7adSreinoud #include <sys/namei.h>
42cff5e7adSreinoud #include <sys/resourcevar.h>	/* defines plimit structure in proc struct */
43cff5e7adSreinoud #include <sys/kernel.h>
44cff5e7adSreinoud #include <sys/file.h>		/* define FWRITE ... */
45cff5e7adSreinoud #include <sys/stat.h>
46cff5e7adSreinoud #include <sys/buf.h>
47cff5e7adSreinoud #include <sys/proc.h>
48cff5e7adSreinoud #include <sys/mount.h>
49cff5e7adSreinoud #include <sys/vnode.h>
50cff5e7adSreinoud #include <sys/signalvar.h>
51cff5e7adSreinoud #include <sys/malloc.h>
52cff5e7adSreinoud #include <sys/dirent.h>
53cff5e7adSreinoud #include <sys/lockf.h>
54e979c658Sreinoud #include <sys/kauth.h>
55cff5e7adSreinoud 
56cff5e7adSreinoud #include <miscfs/genfs/genfs.h>
57cff5e7adSreinoud #include <uvm/uvm_extern.h>
58cff5e7adSreinoud 
59cff5e7adSreinoud #include <fs/udf/ecma167-udf.h>
60cff5e7adSreinoud #include <fs/udf/udf_mount.h>
61ab09c24fSreinoud #include <sys/dirhash.h>
62ab09c24fSreinoud 
63cff5e7adSreinoud #include "udf.h"
64cff5e7adSreinoud #include "udf_subr.h"
65cff5e7adSreinoud #include "udf_bswap.h"
66cff5e7adSreinoud 
67cff5e7adSreinoud 
68e979c658Sreinoud #define VTOI(vnode) ((struct udf_node *) (vnode)->v_data)
69cff5e7adSreinoud 
70e3b6b345Sreinoud /* forward declarations */
71e3b6b345Sreinoud static int udf_do_readlink(struct udf_node *udf_node, uint64_t filesize,
72e3b6b345Sreinoud 	uint8_t *targetbuf, int *length);
73cff5e7adSreinoud 
74cff5e7adSreinoud /* implementations of vnode functions; table follows at end */
75cff5e7adSreinoud /* --------------------------------------------------------------------- */
76cff5e7adSreinoud 
77cff5e7adSreinoud int
udf_inactive(void * v)78cff5e7adSreinoud udf_inactive(void *v)
79cff5e7adSreinoud {
8087fb3229Sriastradh 	struct vop_inactive_v2_args /* {
81cff5e7adSreinoud 		struct vnode *a_vp;
82e979c658Sreinoud 		bool         *a_recycle;
83cff5e7adSreinoud 	} */ *ap = v;
84cff5e7adSreinoud 	struct vnode *vp = ap->a_vp;
85e979c658Sreinoud 	struct udf_node *udf_node = VTOI(vp);
86e979c658Sreinoud 	int refcnt;
87cff5e7adSreinoud 
88e979c658Sreinoud 	DPRINTF(NODE, ("udf_inactive called for udf_node %p\n", VTOI(vp)));
89e979c658Sreinoud 
90e979c658Sreinoud 	if (udf_node == NULL) {
91e979c658Sreinoud 		DPRINTF(NODE, ("udf_inactive: inactive NULL UDF node\n"));
92e979c658Sreinoud 		return 0;
93e979c658Sreinoud 	}
94cff5e7adSreinoud 
95cff5e7adSreinoud 	/*
9694abdca3Shannken 	 * Optionally flush metadata to disc.
97cff5e7adSreinoud 	 */
98e979c658Sreinoud 	if (udf_node->fe) {
99e979c658Sreinoud 		refcnt = udf_rw16(udf_node->fe->link_cnt);
100e979c658Sreinoud 	} else {
101e979c658Sreinoud 		assert(udf_node->efe);
102e979c658Sreinoud 		refcnt = udf_rw16(udf_node->efe->link_cnt);
103e979c658Sreinoud 	}
104e979c658Sreinoud 
1051f6bbe27Sreinoud 	if ((refcnt == 0) && (vp->v_vflag & VV_SYSTEM)) {
106e979c658Sreinoud 		DPRINTF(VOLUMES, ("UDF_INACTIVE deleting VV_SYSTEM\n"));
1077f459241Sandvar 		/* system nodes are not written out on inactive, so flush */
1081f6bbe27Sreinoud 		udf_node->i_flags = 0;
1091f6bbe27Sreinoud 	}
110e979c658Sreinoud 
111e979c658Sreinoud 	*ap->a_recycle = false;
112e979c658Sreinoud 	if ((refcnt == 0) && ((vp->v_vflag & VV_SYSTEM) == 0)) {
113e979c658Sreinoud 		*ap->a_recycle = true;
114e979c658Sreinoud 		return 0;
115e979c658Sreinoud 	}
116e979c658Sreinoud 
117e979c658Sreinoud 	/* write out its node */
118e979c658Sreinoud 	if (udf_node->i_flags & (IN_CHANGE | IN_UPDATE | IN_MODIFIED))
1199d15fe51Sreinoud 		udf_update(vp, NULL, NULL, NULL, 0);
120cff5e7adSreinoud 
121cff5e7adSreinoud 	return 0;
122cff5e7adSreinoud }
123cff5e7adSreinoud 
124cff5e7adSreinoud /* --------------------------------------------------------------------- */
125cff5e7adSreinoud 
126cff5e7adSreinoud int
udf_reclaim(void * v)127cff5e7adSreinoud udf_reclaim(void *v)
128cff5e7adSreinoud {
1297f7aad09Sriastradh 	struct vop_reclaim_v2_args /* {
130cff5e7adSreinoud 		struct vnode *a_vp;
131cff5e7adSreinoud 	} */ *ap = v;
132cff5e7adSreinoud 	struct vnode *vp = ap->a_vp;
133e979c658Sreinoud 	struct udf_node *udf_node = VTOI(vp);
13494abdca3Shannken 	int refcnt;
135cff5e7adSreinoud 
1367f7aad09Sriastradh 	VOP_UNLOCK(vp);
1377f7aad09Sriastradh 
138e979c658Sreinoud 	DPRINTF(NODE, ("udf_reclaim called for node %p\n", udf_node));
139cff5e7adSreinoud 
140e979c658Sreinoud 	if (udf_node == NULL) {
141e979c658Sreinoud 		DPRINTF(NODE, ("udf_reclaim(): null udfnode\n"));
142e979c658Sreinoud 		return 0;
143e979c658Sreinoud 	}
144e979c658Sreinoud 
14594abdca3Shannken 	/*
14694abdca3Shannken 	 * If the file has not been referenced anymore in a directory
14794abdca3Shannken 	 * we ought to free up the resources on disc if applicable.
14894abdca3Shannken 	 */
14994abdca3Shannken 	if (udf_node->fe) {
15094abdca3Shannken 		refcnt = udf_rw16(udf_node->fe->link_cnt);
15194abdca3Shannken 	} else {
15294abdca3Shannken 		assert(udf_node->efe);
15394abdca3Shannken 		refcnt = udf_rw16(udf_node->efe->link_cnt);
15494abdca3Shannken 	}
15594abdca3Shannken 
15694abdca3Shannken 	if ((refcnt == 0) && ((vp->v_vflag & VV_SYSTEM) == 0)) {
15794abdca3Shannken 	 	/* remove this file's allocation */
15894abdca3Shannken 		DPRINTF(NODE, ("udf_inactive deleting unlinked file\n"));
15994abdca3Shannken 		udf_delete_node(udf_node);
16094abdca3Shannken 	}
16194abdca3Shannken 
162e979c658Sreinoud 	/* update note for closure */
1639d15fe51Sreinoud 	udf_update(vp, NULL, NULL, NULL, UPDATE_CLOSE);
164e979c658Sreinoud 
165e979c658Sreinoud 	/* async check to see if all node descriptors are written out */
166*04076868Sreinoud 	mutex_enter(&udf_node->node_mutex);
167e979c658Sreinoud 	while ((volatile int) udf_node->outstanding_nodedscr > 0) {
168e979c658Sreinoud 		vprint("udf_reclaim(): waiting for writeout\n", vp);
169*04076868Sreinoud 		cv_timedwait(&udf_node->node_lock, &udf_node->node_mutex, hz/8);
170e979c658Sreinoud 	}
171*04076868Sreinoud 	mutex_exit(&udf_node->node_mutex);
172e979c658Sreinoud 
173cff5e7adSreinoud 	/* dispose all node knowledge */
174e979c658Sreinoud 	udf_dispose_node(udf_node);
175cff5e7adSreinoud 
176cff5e7adSreinoud 	return 0;
177cff5e7adSreinoud }
178cff5e7adSreinoud 
179cff5e7adSreinoud /* --------------------------------------------------------------------- */
180cff5e7adSreinoud 
181cff5e7adSreinoud int
udf_read(void * v)182cff5e7adSreinoud udf_read(void *v)
183cff5e7adSreinoud {
184cff5e7adSreinoud 	struct vop_read_args /* {
185cff5e7adSreinoud 		struct vnode *a_vp;
186cff5e7adSreinoud 		struct uio *a_uio;
187cff5e7adSreinoud 		int a_ioflag;
188fc9422c9Selad 		kauth_cred_t a_cred;
189cff5e7adSreinoud 	} */ *ap = v;
190cff5e7adSreinoud 	struct vnode *vp     = ap->a_vp;
191cff5e7adSreinoud 	struct uio   *uio    = ap->a_uio;
192cff5e7adSreinoud 	int           ioflag = ap->a_ioflag;
193e979c658Sreinoud 	int           advice = IO_ADV_DECODE(ap->a_ioflag);
194cff5e7adSreinoud 	struct uvm_object    *uobj;
195cff5e7adSreinoud 	struct udf_node      *udf_node = VTOI(vp);
196cff5e7adSreinoud 	struct file_entry    *fe;
197cff5e7adSreinoud 	struct extfile_entry *efe;
198cff5e7adSreinoud 	uint64_t file_size;
199cff5e7adSreinoud 	vsize_t len;
200cff5e7adSreinoud 	int error;
201cff5e7adSreinoud 
202e979c658Sreinoud 	/*
203e979c658Sreinoud 	 * XXX reading from extended attributes not yet implemented. FreeBSD
204e979c658Sreinoud 	 * has it in mind to forward the IO_EXT read call to the
205e979c658Sreinoud 	 * VOP_READEXTATTR().
206e979c658Sreinoud 	 */
207cff5e7adSreinoud 
208cff5e7adSreinoud 	DPRINTF(READ, ("udf_read called\n"));
209cff5e7adSreinoud 
210cff5e7adSreinoud 	/* can this happen? some filingsystems have this check */
211cff5e7adSreinoud 	if (uio->uio_offset < 0)
212cff5e7adSreinoud 		return EINVAL;
213e979c658Sreinoud 	if (uio->uio_resid == 0)
214e979c658Sreinoud 		return 0;
215e979c658Sreinoud 
216e979c658Sreinoud 	/* protect against rogue programs reading raw directories and links */
217e979c658Sreinoud 	if ((ioflag & IO_ALTSEMANTICS) == 0) {
218e979c658Sreinoud 		if (vp->v_type == VDIR)
219e979c658Sreinoud 			return EISDIR;
220e979c658Sreinoud 		/* all but regular files just give EINVAL */
221e979c658Sreinoud 		if (vp->v_type != VREG)
222e979c658Sreinoud 			return EINVAL;
223e979c658Sreinoud 	}
224cff5e7adSreinoud 
225cff5e7adSreinoud 	assert(udf_node);
226cff5e7adSreinoud 	assert(udf_node->fe || udf_node->efe);
227cff5e7adSreinoud 
228e979c658Sreinoud 	/* get file/directory filesize */
229cff5e7adSreinoud 	if (udf_node->fe) {
230cff5e7adSreinoud 		fe = udf_node->fe;
231cff5e7adSreinoud 		file_size = udf_rw64(fe->inf_len);
232cff5e7adSreinoud 	} else {
233cff5e7adSreinoud 		assert(udf_node->efe);
234cff5e7adSreinoud 		efe = udf_node->efe;
235cff5e7adSreinoud 		file_size = udf_rw64(efe->inf_len);
2360c1391f0Schristos 	}
237cff5e7adSreinoud 
238cff5e7adSreinoud 	/* read contents using buffercache */
239cff5e7adSreinoud 	uobj = &vp->v_uobj;
240cff5e7adSreinoud 	error = 0;
241cff5e7adSreinoud 	while (uio->uio_resid > 0) {
242cff5e7adSreinoud 		/* reached end? */
243cff5e7adSreinoud 		if (file_size <= uio->uio_offset)
244cff5e7adSreinoud 			break;
245cff5e7adSreinoud 
246cff5e7adSreinoud 		/* maximise length to file extremity */
247cff5e7adSreinoud 		len = MIN(file_size - uio->uio_offset, uio->uio_resid);
248cff5e7adSreinoud 		if (len == 0)
249cff5e7adSreinoud 			break;
250cff5e7adSreinoud 
251cff5e7adSreinoud 		/* ubc, here we come, prepare to trap */
252b4099c3eSpooka 		error = ubc_uiomove(uobj, uio, len, advice,
253f5ad84fdSad 		    UBC_READ | UBC_PARTIALOK | UBC_VNODE_FLAGS(vp));
254cff5e7adSreinoud 		if (error)
255cff5e7adSreinoud 			break;
256cff5e7adSreinoud 	}
257e979c658Sreinoud 
258e979c658Sreinoud 	/* note access time unless not requested */
259e979c658Sreinoud 	if (!(vp->v_mount->mnt_flag & MNT_NOATIME)) {
260e979c658Sreinoud 		udf_node->i_flags |= IN_ACCESS;
261a26f29c6Sriastradh 		if ((ioflag & IO_SYNC) == IO_SYNC) {
262a26f29c6Sriastradh 			int uerror;
263a26f29c6Sriastradh 
264a26f29c6Sriastradh 			uerror = udf_update(vp, NULL, NULL, NULL, UPDATE_WAIT);
265a26f29c6Sriastradh 			if (error == 0)
266a26f29c6Sriastradh 				error = uerror;
267a26f29c6Sriastradh 		}
2680c1391f0Schristos 	}
269cff5e7adSreinoud 
270e979c658Sreinoud 	return error;
271cff5e7adSreinoud }
272cff5e7adSreinoud 
273cff5e7adSreinoud /* --------------------------------------------------------------------- */
274cff5e7adSreinoud 
275cff5e7adSreinoud int
udf_write(void * v)276cff5e7adSreinoud udf_write(void *v)
277cff5e7adSreinoud {
278cff5e7adSreinoud 	struct vop_write_args /* {
279cff5e7adSreinoud 		struct vnode *a_vp;
280cff5e7adSreinoud 		struct uio *a_uio;
281cff5e7adSreinoud 		int a_ioflag;
282fc9422c9Selad 		kauth_cred_t a_cred;
283cff5e7adSreinoud 	} */ *ap = v;
284e979c658Sreinoud 	struct vnode *vp     = ap->a_vp;
285e979c658Sreinoud 	struct uio   *uio    = ap->a_uio;
286e979c658Sreinoud 	int           ioflag = ap->a_ioflag;
287c76ee532Sreinoud 	kauth_cred_t  cred   = ap->a_cred;
288e979c658Sreinoud 	int           advice = IO_ADV_DECODE(ap->a_ioflag);
289e979c658Sreinoud 	struct uvm_object    *uobj;
290e979c658Sreinoud 	struct udf_node      *udf_node = VTOI(vp);
291e979c658Sreinoud 	struct file_entry    *fe;
292e979c658Sreinoud 	struct extfile_entry *efe;
29378466aa1Sreinoud 	uint64_t file_size, old_size, old_offset;
294e979c658Sreinoud 	vsize_t len;
295706de0e5Sreinoud 	int aflag = ioflag & IO_SYNC ? B_SYNC : 0;
296e979c658Sreinoud 	int error;
297b4099c3eSpooka 	int resid, extended;
298cff5e7adSreinoud 
299e979c658Sreinoud 	/*
300e979c658Sreinoud 	 * XXX writing to extended attributes not yet implemented. FreeBSD has
301e979c658Sreinoud 	 * it in mind to forward the IO_EXT read call to the
302e979c658Sreinoud 	 * VOP_READEXTATTR().
303e979c658Sreinoud 	 */
304cff5e7adSreinoud 
305e979c658Sreinoud 	DPRINTF(WRITE, ("udf_write called\n"));
306e979c658Sreinoud 
307e979c658Sreinoud 	/* can this happen? some filingsystems have this check */
308e979c658Sreinoud 	if (uio->uio_offset < 0)
309e979c658Sreinoud 		return EINVAL;
310e979c658Sreinoud 	if (uio->uio_resid == 0)
311e979c658Sreinoud 		return 0;
312e979c658Sreinoud 
313e979c658Sreinoud 	/* protect against rogue programs writing raw directories or links */
314e979c658Sreinoud 	if ((ioflag & IO_ALTSEMANTICS) == 0) {
315e979c658Sreinoud 		if (vp->v_type == VDIR)
316e979c658Sreinoud 			return EISDIR;
317e979c658Sreinoud 		/* all but regular files just give EINVAL for now */
318e979c658Sreinoud 		if (vp->v_type != VREG)
319e979c658Sreinoud 			return EINVAL;
320e979c658Sreinoud 	}
321e979c658Sreinoud 
322e979c658Sreinoud 	assert(udf_node);
323e979c658Sreinoud 	assert(udf_node->fe || udf_node->efe);
324e979c658Sreinoud 
325e979c658Sreinoud 	/* get file/directory filesize */
326e979c658Sreinoud 	if (udf_node->fe) {
327e979c658Sreinoud 		fe = udf_node->fe;
328e979c658Sreinoud 		file_size = udf_rw64(fe->inf_len);
329e979c658Sreinoud 	} else {
330e979c658Sreinoud 		assert(udf_node->efe);
331e979c658Sreinoud 		efe = udf_node->efe;
332e979c658Sreinoud 		file_size = udf_rw64(efe->inf_len);
333e979c658Sreinoud 	}
334e979c658Sreinoud 	old_size = file_size;
335e979c658Sreinoud 
336e979c658Sreinoud 	/* if explicitly asked to append, uio_offset can be wrong? */
337e979c658Sreinoud 	if (ioflag & IO_APPEND)
338e979c658Sreinoud 		uio->uio_offset = file_size;
339e979c658Sreinoud 
340e979c658Sreinoud 	extended = (uio->uio_offset + uio->uio_resid > file_size);
341e979c658Sreinoud 	if (extended) {
342e979c658Sreinoud 		DPRINTF(WRITE, ("extending file from %"PRIu64" to %"PRIu64"\n",
343e979c658Sreinoud 			file_size, uio->uio_offset + uio->uio_resid));
344e979c658Sreinoud 		error = udf_grow_node(udf_node, uio->uio_offset + uio->uio_resid);
345e979c658Sreinoud 		if (error)
346e979c658Sreinoud 			return error;
347e979c658Sreinoud 		file_size = uio->uio_offset + uio->uio_resid;
348e979c658Sreinoud 	}
349e979c658Sreinoud 
350e979c658Sreinoud 	/* write contents using buffercache */
351e979c658Sreinoud 	uobj = &vp->v_uobj;
352e979c658Sreinoud 	resid = uio->uio_resid;
353e979c658Sreinoud 	error = 0;
354e979c658Sreinoud 
355e979c658Sreinoud 	uvm_vnp_setwritesize(vp, file_size);
356193ccd2dSreinoud 	old_offset = uio->uio_offset;
357e979c658Sreinoud 	while (uio->uio_resid > 0) {
358e979c658Sreinoud 		/* maximise length to file extremity */
359e979c658Sreinoud 		len = MIN(file_size - uio->uio_offset, uio->uio_resid);
360e979c658Sreinoud 		if (len == 0)
361e979c658Sreinoud 			break;
362e979c658Sreinoud 
363706de0e5Sreinoud 		genfs_node_wrlock(vp);
364706de0e5Sreinoud 		error = GOP_ALLOC(vp, uio->uio_offset, len, aflag, cred);
365706de0e5Sreinoud 		genfs_node_unlock(vp);
366706de0e5Sreinoud 		if (error)
367706de0e5Sreinoud 			break;
368706de0e5Sreinoud 
369e979c658Sreinoud 		/* ubc, here we come, prepare to trap */
370b4099c3eSpooka 		error = ubc_uiomove(uobj, uio, len, advice,
371f5ad84fdSad 		    UBC_WRITE | UBC_VNODE_FLAGS(vp));
372e979c658Sreinoud 		if (error)
373e979c658Sreinoud 			break;
37478466aa1Sreinoud 
37578466aa1Sreinoud 		/*
37678466aa1Sreinoud 		 * flush what we just wrote if necessary.
37778466aa1Sreinoud 		 * XXXUBC simplistic async flushing.
378193ccd2dSreinoud 		 *
379c1a508d1Sreinoud 		 * Directories are excluded since its file data that we want
380c1a508d1Sreinoud 		 * to purge.
38178466aa1Sreinoud 		 */
382793c315aSreinoud 		if ((vp->v_type != VDIR) &&
383c1a508d1Sreinoud 		  (old_offset >> 16 != uio->uio_offset >> 16)) {
384d2a0ebb6Sad 			rw_enter(vp->v_uobj.vmobjlock, RW_WRITER);
385c1a508d1Sreinoud 			error = VOP_PUTPAGES(vp, (old_offset >> 16) << 16,
3868306a9edSchs 			    (uio->uio_offset >> 16) << 16,
3878306a9edSchs 			    PGO_CLEANIT | PGO_LAZY);
388193ccd2dSreinoud 			old_offset = uio->uio_offset;
38978466aa1Sreinoud 		}
390e979c658Sreinoud 	}
391e979c658Sreinoud 	uvm_vnp_setsize(vp, file_size);
392e979c658Sreinoud 
393e979c658Sreinoud 	/* mark node changed and request update */
394e979c658Sreinoud 	udf_node->i_flags |= IN_CHANGE | IN_UPDATE;
395d11ea3eaSchristos 	if (vp->v_mount->mnt_flag & MNT_RELATIME)
396d11ea3eaSchristos 		udf_node->i_flags |= IN_ACCESS;
397e979c658Sreinoud 
398e979c658Sreinoud 	/*
399e979c658Sreinoud 	 * XXX TODO FFS has code here to reset setuid & setgid when we're not
400e979c658Sreinoud 	 * the superuser as a precaution against tampering.
401e979c658Sreinoud 	 */
402e979c658Sreinoud 
403e979c658Sreinoud 	if (error) {
404e979c658Sreinoud 		/* bring back file size to its former size */
405e979c658Sreinoud 		/* take notice of its errors? */
4068f6cc2bdSreinoud 		(void) udf_chsize(vp, (u_quad_t) old_size, cred);
407e979c658Sreinoud 
408e979c658Sreinoud 		/* roll back uio */
409e979c658Sreinoud 		uio->uio_offset -= resid - uio->uio_resid;
410e979c658Sreinoud 		uio->uio_resid = resid;
411e979c658Sreinoud 	} else {
412e979c658Sreinoud 		/* if we write and we're synchronous, update node */
413e979c658Sreinoud 		if ((resid > uio->uio_resid) && ((ioflag & IO_SYNC) == IO_SYNC))
4149d15fe51Sreinoud 			error = udf_update(vp, NULL, NULL, NULL, UPDATE_WAIT);
415e979c658Sreinoud 	}
416e979c658Sreinoud 
417e979c658Sreinoud 	return error;
418cff5e7adSreinoud }
419cff5e7adSreinoud 
420cff5e7adSreinoud 
421cff5e7adSreinoud /* --------------------------------------------------------------------- */
422cff5e7adSreinoud 
423cff5e7adSreinoud /*
42409fb1792Sandvar  * `Special' bmap functionality that translates all incoming requests to
425cff5e7adSreinoud  * translate to vop_strategy() calls with the same blocknumbers effectively
426cff5e7adSreinoud  * not translating at all.
427cff5e7adSreinoud  */
428cff5e7adSreinoud 
429cff5e7adSreinoud int
udf_trivial_bmap(void * v)430cff5e7adSreinoud udf_trivial_bmap(void *v)
431cff5e7adSreinoud {
432cff5e7adSreinoud 	struct vop_bmap_args /* {
433cff5e7adSreinoud 		struct vnode *a_vp;
434cff5e7adSreinoud 		daddr_t a_bn;
435cff5e7adSreinoud 		struct vnode **a_vpp;
436cff5e7adSreinoud 		daddr_t *a_bnp;
437cff5e7adSreinoud 		int *a_runp;
438cff5e7adSreinoud 	} */ *ap = v;
439cff5e7adSreinoud 	struct vnode  *vp  = ap->a_vp;	/* our node	*/
440cff5e7adSreinoud 	struct vnode **vpp = ap->a_vpp;	/* return node	*/
441cff5e7adSreinoud 	daddr_t *bnp  = ap->a_bnp;	/* translated	*/
44209fb1792Sandvar 	daddr_t  bn   = ap->a_bn;	/* original	*/
443cff5e7adSreinoud 	int     *runp = ap->a_runp;
444cff5e7adSreinoud 	struct udf_node *udf_node = VTOI(vp);
445cff5e7adSreinoud 	uint32_t lb_size;
446cff5e7adSreinoud 
447cff5e7adSreinoud 	/* get logical block size */
448cff5e7adSreinoud 	lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size);
449cff5e7adSreinoud 
450cff5e7adSreinoud 	/* could return `-1' to indicate holes/zeros */
451cff5e7adSreinoud 	/* translate 1:1 */
452cff5e7adSreinoud 	*bnp = bn;
453cff5e7adSreinoud 
454cff5e7adSreinoud 	/* set the vnode to read the data from with strategy on itself */
455cff5e7adSreinoud 	if (vpp)
456cff5e7adSreinoud 		*vpp = vp;
457cff5e7adSreinoud 
458cff5e7adSreinoud 	/* set runlength of maximum block size */
459cff5e7adSreinoud 	if (runp)
460cff5e7adSreinoud 		*runp = MAXPHYS / lb_size;	/* or with -1 ? */
461cff5e7adSreinoud 
462cff5e7adSreinoud 	/* return success */
463cff5e7adSreinoud 	return 0;
464cff5e7adSreinoud }
465cff5e7adSreinoud 
466cff5e7adSreinoud /* --------------------------------------------------------------------- */
467cff5e7adSreinoud 
468cff5e7adSreinoud int
udf_vfsstrategy(void * v)469e979c658Sreinoud udf_vfsstrategy(void *v)
470cff5e7adSreinoud {
471cff5e7adSreinoud 	struct vop_strategy_args /* {
472cff5e7adSreinoud 		struct vnode *a_vp;
473cff5e7adSreinoud 		struct buf *a_bp;
474cff5e7adSreinoud 	} */ *ap = v;
475cff5e7adSreinoud 	struct vnode *vp = ap->a_vp;
476cff5e7adSreinoud 	struct buf   *bp = ap->a_bp;
477cff5e7adSreinoud 	struct udf_node *udf_node = VTOI(vp);
47891b2f42cSchristos 	uint32_t lb_size, sectors;
479cff5e7adSreinoud 
480cff5e7adSreinoud 	DPRINTF(STRATEGY, ("udf_strategy called\n"));
481cff5e7adSreinoud 
482cff5e7adSreinoud 	/* check if we ought to be here */
483cff5e7adSreinoud 	if (vp->v_type == VBLK || vp->v_type == VCHR)
484cff5e7adSreinoud 		panic("udf_strategy: spec");
485cff5e7adSreinoud 
486e979c658Sreinoud 	/* only filebuffers ought to be read/write by this, no descriptors */
487cff5e7adSreinoud 	assert(bp->b_blkno >= 0);
488cff5e7adSreinoud 
489cff5e7adSreinoud 	/* get sector size */
490cff5e7adSreinoud 	lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size);
491cff5e7adSreinoud 
492cff5e7adSreinoud 	/* calculate length to fetch/store in sectors */
493cff5e7adSreinoud 	sectors = bp->b_bcount / lb_size;
494cff5e7adSreinoud 	assert(bp->b_bcount > 0);
495cff5e7adSreinoud 
496d97db384Ssnj 	/* NEVER assume later that this buffer is already translated */
497cff5e7adSreinoud 	/* bp->b_lblkno = bp->b_blkno; */
498cff5e7adSreinoud 
499e979c658Sreinoud 	/* check assertions: we OUGHT to always get multiples of this */
500e979c658Sreinoud 	assert(sectors * lb_size == bp->b_bcount);
5010f18888bSchristos 	__USE(sectors);
502e979c658Sreinoud 
503e979c658Sreinoud 	/* issue buffer */
504e979c658Sreinoud 	if (bp->b_flags & B_READ) {
505cff5e7adSreinoud 		DPRINTF(STRATEGY, ("\tread vp %p buf %p (blk no %"PRIu64")"
50691b2f42cSchristos 		    ", for %d sectors\n",
50791b2f42cSchristos 		    vp, bp, bp->b_blkno, sectors));
508cff5e7adSreinoud 
509cff5e7adSreinoud 		/* read buffer from the udf_node, translate vtop on the way*/
510cff5e7adSreinoud 		udf_read_filebuf(udf_node, bp);
511e979c658Sreinoud 	} else {
512e979c658Sreinoud 		DPRINTF(STRATEGY, ("\twrite vp %p buf %p (blk no %"PRIu64")"
51391b2f42cSchristos 		    ", for %d sectors\n",
51491b2f42cSchristos 		    vp, bp, bp->b_blkno, sectors));
515e979c658Sreinoud 
516e979c658Sreinoud 		/* write buffer to the udf_node, translate vtop on the way*/
517e979c658Sreinoud 		udf_write_filebuf(udf_node, bp);
5180c1391f0Schristos 	}
519cff5e7adSreinoud 
520e979c658Sreinoud 	return bp->b_error;
521cff5e7adSreinoud }
522cff5e7adSreinoud 
523cff5e7adSreinoud /* --------------------------------------------------------------------- */
524cff5e7adSreinoud 
525cff5e7adSreinoud int
udf_readdir(void * v)526cff5e7adSreinoud udf_readdir(void *v)
527cff5e7adSreinoud {
528cff5e7adSreinoud 	struct vop_readdir_args /* {
529cff5e7adSreinoud 		struct vnode *a_vp;
530cff5e7adSreinoud 		struct uio *a_uio;
531fc9422c9Selad 		kauth_cred_t a_cred;
532cff5e7adSreinoud 		int *a_eofflag;
533cff5e7adSreinoud 		off_t **a_cookies;
534cff5e7adSreinoud 		int *a_ncookies;
535cff5e7adSreinoud 	} */ *ap = v;
536cff5e7adSreinoud 	struct uio *uio = ap->a_uio;
537cff5e7adSreinoud 	struct vnode *vp = ap->a_vp;
538cff5e7adSreinoud 	struct udf_node *udf_node = VTOI(vp);
539cff5e7adSreinoud 	struct file_entry    *fe;
540cff5e7adSreinoud 	struct extfile_entry *efe;
541cff5e7adSreinoud 	struct fileid_desc *fid;
5420ae0a486Srumble 	struct dirent *dirent;
543cff5e7adSreinoud 	uint64_t file_size, diroffset, transoffset;
544cff5e7adSreinoud 	uint32_t lb_size;
545cff5e7adSreinoud 	int error;
546cff5e7adSreinoud 
547cff5e7adSreinoud 	DPRINTF(READDIR, ("udf_readdir called\n"));
548cff5e7adSreinoud 
549cff5e7adSreinoud 	/* This operation only makes sense on directory nodes. */
550cff5e7adSreinoud 	if (vp->v_type != VDIR)
551cff5e7adSreinoud 		return ENOTDIR;
552cff5e7adSreinoud 
553cff5e7adSreinoud 	/* get directory filesize */
554cff5e7adSreinoud 	if (udf_node->fe) {
555cff5e7adSreinoud 		fe = udf_node->fe;
556cff5e7adSreinoud 		file_size = udf_rw64(fe->inf_len);
557cff5e7adSreinoud 	} else {
558cff5e7adSreinoud 		assert(udf_node->efe);
559cff5e7adSreinoud 		efe = udf_node->efe;
560cff5e7adSreinoud 		file_size = udf_rw64(efe->inf_len);
5610c1391f0Schristos 	}
562cff5e7adSreinoud 
5630ae0a486Srumble 	dirent = malloc(sizeof(struct dirent), M_UDFTEMP, M_WAITOK | M_ZERO);
5640ae0a486Srumble 
565cff5e7adSreinoud 	/*
566cff5e7adSreinoud 	 * Add `.' pseudo entry if at offset zero since its not in the fid
567cff5e7adSreinoud 	 * stream
568cff5e7adSreinoud 	 */
569cff5e7adSreinoud 	if (uio->uio_offset == 0) {
570cff5e7adSreinoud 		DPRINTF(READDIR, ("\t'.' inserted\n"));
5710ae0a486Srumble 		strcpy(dirent->d_name, ".");
572c093c252Sreinoud 		dirent->d_fileno = udf_get_node_id(&udf_node->loc);
5730ae0a486Srumble 		dirent->d_type = DT_DIR;
5740ae0a486Srumble 		dirent->d_namlen = strlen(dirent->d_name);
5750ae0a486Srumble 		dirent->d_reclen = _DIRENT_SIZE(dirent);
5760ae0a486Srumble 		uiomove(dirent, _DIRENT_SIZE(dirent), uio);
577cff5e7adSreinoud 
578cff5e7adSreinoud 		/* mark with magic value that we have done the dummy */
579cff5e7adSreinoud 		uio->uio_offset = UDF_DIRCOOKIE_DOT;
5800c1391f0Schristos 	}
581cff5e7adSreinoud 
582cff5e7adSreinoud 	/* we are called just as long as we keep on pushing data in */
583cff5e7adSreinoud 	error = 0;
5840be5b3feSreinoud 	if (uio->uio_offset < file_size) {
585cff5e7adSreinoud 		/* allocate temporary space for fid */
586cff5e7adSreinoud 		lb_size = udf_rw32(udf_node->ump->logical_vol->lb_size);
5870ae0a486Srumble 		fid = malloc(lb_size, M_UDFTEMP, M_WAITOK);
588cff5e7adSreinoud 
589cff5e7adSreinoud 		if (uio->uio_offset == UDF_DIRCOOKIE_DOT)
590cff5e7adSreinoud 			uio->uio_offset = 0;
591cff5e7adSreinoud 
592cff5e7adSreinoud 		diroffset   = uio->uio_offset;
593cff5e7adSreinoud 		transoffset = diroffset;
594cff5e7adSreinoud 		while (diroffset < file_size) {
595cff5e7adSreinoud 			DPRINTF(READDIR, ("\tread in fid stream\n"));
596cff5e7adSreinoud 			/* transfer a new fid/dirent */
5972f9a24b6Sreinoud 			error = udf_read_fid_stream(vp, &diroffset, fid, dirent);
598cff5e7adSreinoud 			DPRINTFIF(READDIR, error, ("read error in read fid "
599cff5e7adSreinoud 			    "stream : %d\n", error));
600cff5e7adSreinoud 			if (error)
601cff5e7adSreinoud 				break;
602cff5e7adSreinoud 
603cff5e7adSreinoud 			/*
604cff5e7adSreinoud 			 * If there isn't enough space in the uio to return a
605cff5e7adSreinoud 			 * whole dirent, break off read
606cff5e7adSreinoud 			 */
6070ae0a486Srumble 			if (uio->uio_resid < _DIRENT_SIZE(dirent))
608cff5e7adSreinoud 				break;
609cff5e7adSreinoud 
610a0403cdeSmsaitoh 			/* remember the last entry we transferred */
611cff5e7adSreinoud 			transoffset = diroffset;
612cff5e7adSreinoud 
613cff5e7adSreinoud 			/* skip deleted entries */
614cff5e7adSreinoud 			if (fid->file_char & UDF_FILE_CHAR_DEL)
615cff5e7adSreinoud 				continue;
616cff5e7adSreinoud 
617cff5e7adSreinoud 			/* skip not visible files */
618cff5e7adSreinoud 			if (fid->file_char & UDF_FILE_CHAR_VIS)
619cff5e7adSreinoud 				continue;
620cff5e7adSreinoud 
621cff5e7adSreinoud 			/* copy dirent to the caller */
622cff5e7adSreinoud 			DPRINTF(READDIR, ("\tread dirent `%s', type %d\n",
6230ae0a486Srumble 			    dirent->d_name, dirent->d_type));
6240ae0a486Srumble 			uiomove(dirent, _DIRENT_SIZE(dirent), uio);
6250c1391f0Schristos 		}
626cff5e7adSreinoud 
6277991f5a7Sandvar 		/* pass on last transferred offset */
628cff5e7adSreinoud 		uio->uio_offset = transoffset;
6290ae0a486Srumble 		free(fid, M_UDFTEMP);
6300c1391f0Schristos 	}
631cff5e7adSreinoud 
632cff5e7adSreinoud 	if (ap->a_eofflag)
6330be5b3feSreinoud 		*ap->a_eofflag = (uio->uio_offset >= file_size);
634cff5e7adSreinoud 
635cff5e7adSreinoud #ifdef DEBUG
636cff5e7adSreinoud 	if (udf_verbose & UDF_DEBUG_READDIR) {
637cff5e7adSreinoud 		printf("returning offset %d\n", (uint32_t) uio->uio_offset);
638cff5e7adSreinoud 		if (ap->a_eofflag)
639cff5e7adSreinoud 			printf("returning EOF ? %d\n", *ap->a_eofflag);
640cff5e7adSreinoud 		if (error)
641cff5e7adSreinoud 			printf("readdir returning error %d\n", error);
6420c1391f0Schristos 	}
643cff5e7adSreinoud #endif
644cff5e7adSreinoud 
6450ae0a486Srumble 	free(dirent, M_UDFTEMP);
646cff5e7adSreinoud 	return error;
647cff5e7adSreinoud }
648cff5e7adSreinoud 
649cff5e7adSreinoud /* --------------------------------------------------------------------- */
650cff5e7adSreinoud 
651cff5e7adSreinoud int
udf_lookup(void * v)652cff5e7adSreinoud udf_lookup(void *v)
653cff5e7adSreinoud {
65497834f7bShannken 	struct vop_lookup_v2_args /* {
655cff5e7adSreinoud 		struct vnode *a_dvp;
656cff5e7adSreinoud 		struct vnode **a_vpp;
657cff5e7adSreinoud 		struct componentname *a_cnp;
658cff5e7adSreinoud 	} */ *ap = v;
659cff5e7adSreinoud 	struct vnode *dvp = ap->a_dvp;
660cff5e7adSreinoud 	struct vnode **vpp = ap->a_vpp;
661cff5e7adSreinoud 	struct componentname *cnp = ap->a_cnp;
662cff5e7adSreinoud 	struct udf_node  *dir_node, *res_node;
663cff5e7adSreinoud 	struct udf_mount *ump;
664cff5e7adSreinoud 	struct long_ad    icb_loc;
66516cc237eSreinoud 	mode_t mode;
66616cc237eSreinoud 	uid_t d_uid;
66716cc237eSreinoud 	gid_t d_gid;
668cff5e7adSreinoud 	const char *name;
669c398ae97Schs 	int namelen, nameiop, islastcn, mounted_ro;
670cff5e7adSreinoud 	int error, found;
671cff5e7adSreinoud 
672cff5e7adSreinoud 	dir_node = VTOI(dvp);
673cff5e7adSreinoud 	ump = dir_node->ump;
674cff5e7adSreinoud 	*vpp = NULL;
675cff5e7adSreinoud 
676a7795a09Sreinoud 	DPRINTF(LOOKUP, ("udf_lookup called, lookup `%s`\n",
677a7795a09Sreinoud 		cnp->cn_nameptr));
678cff5e7adSreinoud 
679cff5e7adSreinoud 	/* simplify/clarification flags */
680cff5e7adSreinoud 	nameiop     = cnp->cn_nameiop;
681cff5e7adSreinoud 	islastcn    = cnp->cn_flags & ISLASTCN;
682cff5e7adSreinoud 	mounted_ro  = dvp->v_mount->mnt_flag & MNT_RDONLY;
683cff5e7adSreinoud 
684cff5e7adSreinoud 	/* check exec/dirread permissions first */
68561e8303eSpooka 	error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred);
686cff5e7adSreinoud 	if (error)
687cff5e7adSreinoud 		return error;
688cff5e7adSreinoud 
689cff5e7adSreinoud 	DPRINTF(LOOKUP, ("\taccess ok\n"));
690cff5e7adSreinoud 
691cff5e7adSreinoud 	/*
692cff5e7adSreinoud 	 * If requesting a modify on the last path element on a read-only
693cff5e7adSreinoud 	 * filingsystem, reject lookup; XXX why is this repeated in every FS ?
694cff5e7adSreinoud 	 */
695cff5e7adSreinoud 	if (islastcn && mounted_ro && (nameiop == DELETE || nameiop == RENAME))
696cff5e7adSreinoud 		return EROFS;
697cff5e7adSreinoud 
698cff5e7adSreinoud 	DPRINTF(LOOKUP, ("\tlooking up cnp->cn_nameptr '%s'\n",
699cff5e7adSreinoud 	    cnp->cn_nameptr));
7001617a81dSdholland 	/* look in the namecache */
70135ed6905Sdholland 	if (cache_lookup(dvp, cnp->cn_nameptr, cnp->cn_namelen,
70235ed6905Sdholland 			 cnp->cn_nameiop, cnp->cn_flags, NULL, vpp)) {
7031617a81dSdholland 		return *vpp == NULLVP ? ENOENT : 0;
7041617a81dSdholland 	}
705cff5e7adSreinoud 
706cff5e7adSreinoud 	DPRINTF(LOOKUP, ("\tNOT found in cache\n"));
707cff5e7adSreinoud 
708cff5e7adSreinoud 	/*
709cff5e7adSreinoud 	 * Obviously, the file is not (anymore) in the namecache, we have to
710cff5e7adSreinoud 	 * search for it. There are three basic cases: '.', '..' and others.
711cff5e7adSreinoud 	 *
712cff5e7adSreinoud 	 * Following the guidelines of VOP_LOOKUP manpage and tmpfs.
713cff5e7adSreinoud 	 */
714cff5e7adSreinoud 	error = 0;
715cff5e7adSreinoud 	if ((cnp->cn_namelen == 1) && (cnp->cn_nameptr[0] == '.')) {
716cff5e7adSreinoud 		DPRINTF(LOOKUP, ("\tlookup '.'\n"));
717cff5e7adSreinoud 		/* special case 1 '.' */
71816cc237eSreinoud 		if (islastcn && cnp->cn_nameiop == RENAME) {
71916cc237eSreinoud 			error = EISDIR;
72016cc237eSreinoud 			goto out;
72116cc237eSreinoud 		}
722c3183f32Spooka 		vref(dvp);
723cff5e7adSreinoud 		*vpp = dvp;
724cff5e7adSreinoud 		/* done */
72516cc237eSreinoud 		goto done;
726cff5e7adSreinoud 	} else if (cnp->cn_flags & ISDOTDOT) {
727cff5e7adSreinoud 		/* special case 2 '..' */
728cff5e7adSreinoud 		DPRINTF(LOOKUP, ("\tlookup '..'\n"));
729cff5e7adSreinoud 
730a7795a09Sreinoud 		if (islastcn && cnp->cn_nameiop == RENAME) {
731a7795a09Sreinoud 			error = EINVAL;
732a7795a09Sreinoud 			goto out;
733a7795a09Sreinoud 		}
734a7795a09Sreinoud 
735cff5e7adSreinoud 		/* get our node */
736cff5e7adSreinoud 		name    = "..";
737cff5e7adSreinoud 		namelen = 2;
7382ac28d55Sreinoud 		error = udf_lookup_name_in_dir(dvp, name, namelen,
7392ac28d55Sreinoud 				&icb_loc, &found);
7402ac28d55Sreinoud 		if (error)
7412ac28d55Sreinoud 			goto out;
742cff5e7adSreinoud 		if (!found)
743cff5e7adSreinoud 			error = ENOENT;
744cff5e7adSreinoud 
745e4424d53Sreinoud 		/* first unlock parent */
7461423e65bShannken 		VOP_UNLOCK(dvp);
747e4424d53Sreinoud 
748e979c658Sreinoud 		if (error == 0) {
749cff5e7adSreinoud 			DPRINTF(LOOKUP, ("\tfound '..'\n"));
750cff5e7adSreinoud 			/* try to create/reuse the node */
751c2e9cb94Sad 			error = udf_get_node(ump, &icb_loc, &res_node,
752c2e9cb94Sad 			    LK_EXCLUSIVE);
753cff5e7adSreinoud 
754cff5e7adSreinoud 			if (!error) {
7552ac28d55Sreinoud 				DPRINTF(LOOKUP,
7562ac28d55Sreinoud 					("\tnode retrieved/created OK\n"));
757cff5e7adSreinoud 				*vpp = res_node->vnode;
7580c1391f0Schristos 			}
7590c1391f0Schristos 		}
760cff5e7adSreinoud 
761c398ae97Schs 		/* try to relock parent */
762c398ae97Schs 		vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
76316cc237eSreinoud 		goto out;
76416cc237eSreinoud 	}
76516cc237eSreinoud 
766cff5e7adSreinoud 	/* all other files */
76716cc237eSreinoud 	DPRINTF(LOOKUP, ("\tlookup file/dir in directory\n"));
76816cc237eSreinoud 
769cff5e7adSreinoud 	/* lookup filename in the directory; location icb_loc */
770cff5e7adSreinoud 	name    = cnp->cn_nameptr;
771cff5e7adSreinoud 	namelen = cnp->cn_namelen;
7722ac28d55Sreinoud 	error = udf_lookup_name_in_dir(dvp, name, namelen,
7732ac28d55Sreinoud 			&icb_loc, &found);
7742ac28d55Sreinoud 	if (error)
7752ac28d55Sreinoud 		goto out;
776cff5e7adSreinoud 	if (!found) {
777cff5e7adSreinoud 		DPRINTF(LOOKUP, ("\tNOT found\n"));
778cff5e7adSreinoud 		/*
77916cc237eSreinoud 		 * The entry was not found in the directory.  This is
78016cc237eSreinoud 		 * valid if we are creating or renaming an entry and
78116cc237eSreinoud 		 * are working on the last component of the path name.
782cff5e7adSreinoud 		 */
78316cc237eSreinoud 		if (islastcn && (cnp->cn_nameiop == CREATE ||
78416cc237eSreinoud 				 cnp->cn_nameiop == RENAME)) {
78561e8303eSpooka 			error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred);
78616cc237eSreinoud 			if (error) {
78716cc237eSreinoud 				goto out;
7880c1391f0Schristos 			}
78916cc237eSreinoud 			error = EJUSTRETURN;
79016cc237eSreinoud 		} else {
79116cc237eSreinoud 			error = ENOENT;
7920c1391f0Schristos 		}
793cff5e7adSreinoud 		/* done */
79416cc237eSreinoud 		goto done;
79516cc237eSreinoud 	}
79616cc237eSreinoud 
79716cc237eSreinoud 	/*
79816cc237eSreinoud 	 * XXX NOTE tmpfs has a test here that tests that intermediate
79916cc237eSreinoud 	 * components i.e. not the last one ought to be either a directory or
80016cc237eSreinoud 	 * a link. It seems to function well without this code.
80116cc237eSreinoud 	 */
80216cc237eSreinoud 
803a0054fd5Sreinoud 	/* try to create/reuse the node */
804c2e9cb94Sad 	error = udf_get_node(ump, &icb_loc, &res_node, LK_EXCLUSIVE);
805a0054fd5Sreinoud 	if (error)
806a0054fd5Sreinoud 		goto out;
807a0054fd5Sreinoud 
8081cabaf0eSreinoud 	/* check permissions */
80916cc237eSreinoud 	if (islastcn && (cnp->cn_nameiop == DELETE ||
81016cc237eSreinoud 			 cnp->cn_nameiop == RENAME)  ) {
81116cc237eSreinoud 		error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred);
812a0054fd5Sreinoud 		if (error) {
813a0054fd5Sreinoud 			vput(res_node->vnode);
81416cc237eSreinoud 			goto out;
815a0054fd5Sreinoud 		}
81616cc237eSreinoud 
8171cabaf0eSreinoud 		/*
8181cabaf0eSreinoud 		 * Check if the directory has its sticky bit set. If so, ask
8191cabaf0eSreinoud 		 * for clearance since only the owner of a file or directory
8200ac7f4ddSandvar 		 * can remove/rename from that directory.
8211cabaf0eSreinoud 		 */
82216cc237eSreinoud 		mode = udf_getaccessmode(dir_node);
82316cc237eSreinoud 		if ((mode & S_ISTXT) != 0) {
8241cabaf0eSreinoud 			udf_getownership(dir_node, &d_uid, &d_gid);
82516cc237eSreinoud 			error = kauth_authorize_vnode(cnp->cn_cred,
82616cc237eSreinoud 			    KAUTH_VNODE_DELETE, res_node->vnode,
8279aa2a9c3Schristos 			    dir_node->vnode, genfs_can_sticky(dvp, cnp->cn_cred,
82816cc237eSreinoud 			    d_uid, d_uid));
82916cc237eSreinoud 			if (error) {
83016cc237eSreinoud 				error = EPERM;
831a0054fd5Sreinoud 				vput(res_node->vnode);
83216cc237eSreinoud 				goto out;
83316cc237eSreinoud 			}
83416cc237eSreinoud 		}
83516cc237eSreinoud 	}
836a0054fd5Sreinoud 
837cff5e7adSreinoud 	*vpp = res_node->vnode;
838cff5e7adSreinoud 
83916cc237eSreinoud done:
840cff5e7adSreinoud 	/*
841cff5e7adSreinoud 	 * Store result in the cache if requested. If we are creating a file,
842cff5e7adSreinoud 	 * the file might not be found and thus putting it into the namecache
843cff5e7adSreinoud 	 * might be seen as negative caching.
844cff5e7adSreinoud 	 */
845d65753d9Srmind 	if (nameiop != CREATE)
84635ed6905Sdholland 		cache_enter(dvp, *vpp, cnp->cn_nameptr, cnp->cn_namelen,
84735ed6905Sdholland 			    cnp->cn_flags);
848cff5e7adSreinoud 
84916cc237eSreinoud out:
85097834f7bShannken 	if (error == 0 && *vpp != dvp)
85197834f7bShannken 		VOP_UNLOCK(*vpp);
85275d2abaeSandvar 	DPRINTFIF(LOOKUP, error, ("udf_lookup returning error %d\n", error));
853cff5e7adSreinoud 
854cff5e7adSreinoud 	return error;
855cff5e7adSreinoud }
856cff5e7adSreinoud 
857cff5e7adSreinoud /* --------------------------------------------------------------------- */
858cff5e7adSreinoud 
859cff5e7adSreinoud int
udf_getattr(void * v)860cff5e7adSreinoud udf_getattr(void *v)
861cff5e7adSreinoud {
862cff5e7adSreinoud 	struct vop_getattr_args /* {
863cff5e7adSreinoud 		struct vnode *a_vp;
864cff5e7adSreinoud 		struct vattr *a_vap;
865fc9422c9Selad 		kauth_cred_t a_cred;
866e979c658Sreinoud 		struct lwp   *a_l;
867cff5e7adSreinoud 	} */ *ap = v;
868cff5e7adSreinoud 	struct vnode *vp = ap->a_vp;
869cff5e7adSreinoud 	struct udf_node *udf_node = VTOI(vp);
870cff5e7adSreinoud 	struct udf_mount *ump = udf_node->ump;
871e979c658Sreinoud 	struct file_entry    *fe  = udf_node->fe;
872e979c658Sreinoud 	struct extfile_entry *efe = udf_node->efe;
873a88cc852Sreinoud 	struct filetimes_extattr_entry *ft_extattr;
874e979c658Sreinoud 	struct device_extattr_entry *devattr;
875cff5e7adSreinoud 	struct vattr *vap = ap->a_vap;
876e979c658Sreinoud 	struct timestamp *atime, *mtime, *attrtime, *creatime;
877cff5e7adSreinoud 	uint64_t filesize, blkssize;
878e979c658Sreinoud 	uint32_t nlink;
879a88cc852Sreinoud 	uint32_t offset, a_l;
880e3b6b345Sreinoud 	uint8_t *filedata, *targetbuf;
881cff5e7adSreinoud 	uid_t uid;
882cff5e7adSreinoud 	gid_t gid;
883e3b6b345Sreinoud 	int length, error;
884cff5e7adSreinoud 
885cff5e7adSreinoud 	DPRINTF(CALL, ("udf_getattr called\n"));
886cff5e7adSreinoud 
887e979c658Sreinoud 	/* update times before we returning values */
888e979c658Sreinoud 	udf_itimes(udf_node, NULL, NULL, NULL);
889e979c658Sreinoud 
890cff5e7adSreinoud 	/* get descriptor information */
891e979c658Sreinoud 	if (fe) {
892cff5e7adSreinoud 		nlink    = udf_rw16(fe->link_cnt);
893cff5e7adSreinoud 		uid      = (uid_t)udf_rw32(fe->uid);
894cff5e7adSreinoud 		gid      = (gid_t)udf_rw32(fe->gid);
895cff5e7adSreinoud 		filesize = udf_rw64(fe->inf_len);
896cff5e7adSreinoud 		blkssize = udf_rw64(fe->logblks_rec);
897cff5e7adSreinoud 		atime    = &fe->atime;
898cff5e7adSreinoud 		mtime    = &fe->mtime;
899cff5e7adSreinoud 		attrtime = &fe->attrtime;
900e979c658Sreinoud 		filedata = fe->data;
901a88cc852Sreinoud 
902a88cc852Sreinoud 		/* initial guess */
903a88cc852Sreinoud 		creatime = mtime;
904a88cc852Sreinoud 
905a88cc852Sreinoud 		/* check our extended attribute if present */
906a88cc852Sreinoud 		error = udf_extattr_search_intern(udf_node,
907a88cc852Sreinoud 			UDF_FILETIMES_ATTR_NO, "", &offset, &a_l);
908a88cc852Sreinoud 		if (!error) {
909a88cc852Sreinoud 			ft_extattr = (struct filetimes_extattr_entry *)
910a88cc852Sreinoud 				(filedata + offset);
911a88cc852Sreinoud 			if (ft_extattr->existence & UDF_FILETIMES_FILE_CREATION)
912a88cc852Sreinoud 				creatime = &ft_extattr->times[0];
913a88cc852Sreinoud 		}
914cff5e7adSreinoud 	} else {
915cff5e7adSreinoud 		assert(udf_node->efe);
916cff5e7adSreinoud 		nlink    = udf_rw16(efe->link_cnt);
917cff5e7adSreinoud 		uid      = (uid_t)udf_rw32(efe->uid);
918cff5e7adSreinoud 		gid      = (gid_t)udf_rw32(efe->gid);
919cff5e7adSreinoud 		filesize = udf_rw64(efe->inf_len);	/* XXX or obj_size? */
920cff5e7adSreinoud 		blkssize = udf_rw64(efe->logblks_rec);
921cff5e7adSreinoud 		atime    = &efe->atime;
922cff5e7adSreinoud 		mtime    = &efe->mtime;
923cff5e7adSreinoud 		attrtime = &efe->attrtime;
924e979c658Sreinoud 		creatime = &efe->ctime;
925e979c658Sreinoud 		filedata = efe->data;
9260c1391f0Schristos 	}
927cff5e7adSreinoud 
928cff5e7adSreinoud 	/* do the uid/gid translation game */
9294b1a8129Sreinoud 	if (uid == (uid_t) -1)
930cff5e7adSreinoud 		uid = ump->mount_args.anon_uid;
9314b1a8129Sreinoud 	if (gid == (gid_t) -1)
932cff5e7adSreinoud 		gid = ump->mount_args.anon_gid;
933cff5e7adSreinoud 
934cff5e7adSreinoud 	/* fill in struct vattr with values from the node */
935c3183f32Spooka 	vattr_null(vap);
936cff5e7adSreinoud 	vap->va_type      = vp->v_type;
937cff5e7adSreinoud 	vap->va_mode      = udf_getaccessmode(udf_node);
938cff5e7adSreinoud 	vap->va_nlink     = nlink;
939cff5e7adSreinoud 	vap->va_uid       = uid;
940cff5e7adSreinoud 	vap->va_gid       = gid;
941cff5e7adSreinoud 	vap->va_fsid      = vp->v_mount->mnt_stat.f_fsidx.__fsid_val[0];
942c093c252Sreinoud 	vap->va_fileid    = udf_get_node_id(&udf_node->loc);   /* inode hash XXX */
943cff5e7adSreinoud 	vap->va_size      = filesize;
944cff5e7adSreinoud 	vap->va_blocksize = udf_node->ump->discinfo.sector_size;  /* wise? */
945cff5e7adSreinoud 
946cff5e7adSreinoud 	/*
947cff5e7adSreinoud 	 * BUG-ALERT: UDF doesn't count '.' as an entry, so we'll have to add
948cff5e7adSreinoud 	 * 1 to the link count if its a directory we're requested attributes
949cff5e7adSreinoud 	 * of.
950cff5e7adSreinoud 	 */
951cff5e7adSreinoud 	if (vap->va_type == VDIR)
952cff5e7adSreinoud 		vap->va_nlink++;
953cff5e7adSreinoud 
954e3b6b345Sreinoud 	/*
955e3b6b345Sreinoud 	 * BUG-ALERT: Posix requires the va_size to be pathlength for symbolic
956e3b6b345Sreinoud 	 * links.
957e3b6b345Sreinoud 	 */
958e3b6b345Sreinoud 	if (vap->va_type == VLNK) {
959e3b6b345Sreinoud 		/* claim temporary buffers for translation */
960e3b6b345Sreinoud 		targetbuf = malloc(PATH_MAX+1, M_UDFTEMP, M_WAITOK);
961e3b6b345Sreinoud 		error = udf_do_readlink(udf_node, filesize, targetbuf, &length);
962e3b6b345Sreinoud 		if (!error) {
963e3b6b345Sreinoud 			vap->va_size = length;
964e3b6b345Sreinoud 			KASSERT(length == strlen(targetbuf));
965e3b6b345Sreinoud 		}
966e3b6b345Sreinoud 		free(targetbuf, M_UDFTEMP);
967e3b6b345Sreinoud 		/* XXX return error? */
968e3b6b345Sreinoud 	}
969e3b6b345Sreinoud 
970cff5e7adSreinoud 	/* access times */
971cff5e7adSreinoud 	udf_timestamp_to_timespec(ump, atime,    &vap->va_atime);
972cff5e7adSreinoud 	udf_timestamp_to_timespec(ump, mtime,    &vap->va_mtime);
973cff5e7adSreinoud 	udf_timestamp_to_timespec(ump, attrtime, &vap->va_ctime);
974e979c658Sreinoud 	udf_timestamp_to_timespec(ump, creatime, &vap->va_birthtime);
975cff5e7adSreinoud 
976cff5e7adSreinoud 	vap->va_gen       = 1;		/* no multiple generations yes (!?) */
977cff5e7adSreinoud 	vap->va_flags     = 0;		/* no flags */
9781d48d181Sreinoud 	vap->va_bytes     = blkssize * udf_node->ump->discinfo.sector_size;
979cff5e7adSreinoud 	vap->va_filerev   = 1;		/* TODO file revision numbers? */
980e979c658Sreinoud 	vap->va_vaflags   = 0;
981e979c658Sreinoud 	/* TODO get vaflags from the extended attributes? */
982e979c658Sreinoud 
983e979c658Sreinoud 	if ((vap->va_type == VBLK) || (vap->va_type == VCHR)) {
984e979c658Sreinoud 		error = udf_extattr_search_intern(udf_node,
985e979c658Sreinoud 				UDF_DEVICESPEC_ATTR_NO, "",
986a88cc852Sreinoud 				&offset, &a_l);
987e979c658Sreinoud 		/* if error, deny access */
988e979c658Sreinoud 		if (error || (filedata == NULL)) {
989e979c658Sreinoud 			vap->va_mode = 0;	/* or v_type = VNON?  */
990e979c658Sreinoud 		} else {
991e979c658Sreinoud 			devattr = (struct device_extattr_entry *)
992e979c658Sreinoud 				filedata + offset;
993e979c658Sreinoud 			vap->va_rdev = makedev(
994e979c658Sreinoud 				udf_rw32(devattr->major),
995e979c658Sreinoud 				udf_rw32(devattr->minor)
996e979c658Sreinoud 				);
997e979c658Sreinoud 			/* TODO we could check the implementator */
998e979c658Sreinoud 		}
999e979c658Sreinoud 	}
1000cff5e7adSreinoud 
1001cff5e7adSreinoud 	return 0;
1002cff5e7adSreinoud }
1003cff5e7adSreinoud 
1004cff5e7adSreinoud /* --------------------------------------------------------------------- */
1005cff5e7adSreinoud 
1006e979c658Sreinoud static int
udf_chown(struct vnode * vp,uid_t new_uid,gid_t new_gid,kauth_cred_t cred)1007e979c658Sreinoud udf_chown(struct vnode *vp, uid_t new_uid, gid_t new_gid,
1008e979c658Sreinoud 	  kauth_cred_t cred)
1009e979c658Sreinoud {
1010e979c658Sreinoud 	struct udf_node  *udf_node = VTOI(vp);
1011386808d4Selad 	uid_t uid;
1012386808d4Selad 	gid_t gid;
1013e979c658Sreinoud 	int error;
1014e979c658Sreinoud 
1015e979c658Sreinoud #ifdef notyet
1016e979c658Sreinoud 	/* TODO get vaflags from the extended attributes? */
1017e979c658Sreinoud 	/* Immutable or append-only files cannot be modified, either. */
1018e979c658Sreinoud 	if (udf_node->flags & (IMMUTABLE | APPEND))
1019e979c658Sreinoud 		return EPERM;
1020e979c658Sreinoud #endif
1021e979c658Sreinoud 
1022e979c658Sreinoud 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
1023e979c658Sreinoud 		return EROFS;
1024e979c658Sreinoud 
1025e979c658Sreinoud 	/* retrieve old values */
1026e979c658Sreinoud 	udf_getownership(udf_node, &uid, &gid);
1027e979c658Sreinoud 
1028e979c658Sreinoud 	/* only one could be specified */
1029e979c658Sreinoud 	if (new_uid == VNOVAL)
1030e979c658Sreinoud 		new_uid = uid;
1031e979c658Sreinoud 	if (new_gid == VNOVAL)
1032e979c658Sreinoud 		new_gid = gid;
1033e979c658Sreinoud 
1034e979c658Sreinoud 	/* check if we can fit it in an 32 bits */
10357cbe9b45Sreinoud 	if ((uid_t) ((uint32_t) new_uid) != new_uid)
1036e979c658Sreinoud 		return EINVAL;
10377cbe9b45Sreinoud 	if ((gid_t) ((uint32_t) new_gid) != new_gid)
1038e979c658Sreinoud 		return EINVAL;
1039e979c658Sreinoud 
1040e979c658Sreinoud 	/* check permissions */
10410c9d8d15Selad 	error = kauth_authorize_vnode(cred, KAUTH_VNODE_CHANGE_OWNERSHIP,
10429aa2a9c3Schristos 	    vp, NULL, genfs_can_chown(vp, cred, uid, gid, new_uid, new_gid));
1043386808d4Selad 	if (error)
1044386808d4Selad 		return (error);
1045e979c658Sreinoud 
1046e979c658Sreinoud 	/* change the ownership */
1047e979c658Sreinoud 	udf_setownership(udf_node, new_uid, new_gid);
1048e979c658Sreinoud 
1049e979c658Sreinoud 	/* mark node changed */
1050e979c658Sreinoud 	udf_node->i_flags |= IN_CHANGE;
1051e979c658Sreinoud 
1052e979c658Sreinoud 	return 0;
1053e979c658Sreinoud }
1054e979c658Sreinoud 
1055e979c658Sreinoud 
1056e979c658Sreinoud static int
udf_chmod(struct vnode * vp,mode_t mode,kauth_cred_t cred)1057e979c658Sreinoud udf_chmod(struct vnode *vp, mode_t mode, kauth_cred_t cred)
1058e979c658Sreinoud {
1059e979c658Sreinoud 	struct udf_node  *udf_node = VTOI(vp);
1060386808d4Selad 	uid_t uid;
1061386808d4Selad 	gid_t gid;
1062e979c658Sreinoud 	int error;
1063e979c658Sreinoud 
1064e979c658Sreinoud #ifdef notyet
1065e979c658Sreinoud 	/* TODO get vaflags from the extended attributes? */
1066e979c658Sreinoud 	/* Immutable or append-only files cannot be modified, either. */
1067e979c658Sreinoud 	if (udf_node->flags & (IMMUTABLE | APPEND))
1068e979c658Sreinoud 		return EPERM;
1069e979c658Sreinoud #endif
1070e979c658Sreinoud 
1071e979c658Sreinoud 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
1072e979c658Sreinoud 		return EROFS;
1073e979c658Sreinoud 
1074e979c658Sreinoud 	/* retrieve uid/gid values */
1075e979c658Sreinoud 	udf_getownership(udf_node, &uid, &gid);
1076e979c658Sreinoud 
1077e979c658Sreinoud 	/* check permissions */
10780c9d8d15Selad 	error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_SECURITY, vp,
10799aa2a9c3Schristos 	    NULL, genfs_can_chmod(vp, cred, uid, gid, mode));
1080386808d4Selad 	if (error)
1081386808d4Selad 		return (error);
1082e979c658Sreinoud 
1083e979c658Sreinoud 	/* change mode */
1084e979c658Sreinoud 	udf_setaccessmode(udf_node, mode);
1085e979c658Sreinoud 
1086e979c658Sreinoud 	/* mark node changed */
1087e979c658Sreinoud 	udf_node->i_flags |= IN_CHANGE;
1088e979c658Sreinoud 
1089e979c658Sreinoud 	return 0;
1090e979c658Sreinoud }
1091e979c658Sreinoud 
1092e979c658Sreinoud 
1093e979c658Sreinoud /* exported */
1094e979c658Sreinoud int
udf_chsize(struct vnode * vp,u_quad_t newsize,kauth_cred_t cred)1095e979c658Sreinoud udf_chsize(struct vnode *vp, u_quad_t newsize, kauth_cred_t cred)
1096e979c658Sreinoud {
1097e979c658Sreinoud 	struct udf_node  *udf_node = VTOI(vp);
1098e979c658Sreinoud 	int error, extended;
1099e979c658Sreinoud 
1100e979c658Sreinoud 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
1101e979c658Sreinoud 		return EROFS;
1102e979c658Sreinoud 
1103e979c658Sreinoud 	/* Decide whether this is a valid operation based on the file type. */
1104e979c658Sreinoud 	switch (vp->v_type) {
1105e979c658Sreinoud 	case VDIR:
1106e979c658Sreinoud 		return EISDIR;
1107e979c658Sreinoud 	case VREG:
1108e979c658Sreinoud 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
1109e979c658Sreinoud 			return EROFS;
1110e979c658Sreinoud 		break;
1111e979c658Sreinoud 	case VBLK:
1112e979c658Sreinoud 		/* FALLTHROUGH */
1113e979c658Sreinoud 	case VCHR:
1114e979c658Sreinoud 		/* FALLTHROUGH */
1115e979c658Sreinoud 	case VFIFO:
1116e979c658Sreinoud 		/* Allow modifications of special files even if in the file
1117e979c658Sreinoud 		 * system is mounted read-only (we are not modifying the
1118e979c658Sreinoud 		 * files themselves, but the objects they represent). */
1119e979c658Sreinoud 		return 0;
1120e979c658Sreinoud 	default:
1121e979c658Sreinoud 		/* Anything else is unsupported. */
1122e979c658Sreinoud 		return EOPNOTSUPP;
1123e979c658Sreinoud 	}
1124e979c658Sreinoud 
1125e979c658Sreinoud #if notyet
1126e979c658Sreinoud 	/* TODO get vaflags from the extended attributes? */
1127e979c658Sreinoud 	/* Immutable or append-only files cannot be modified, either. */
1128e979c658Sreinoud 	if (node->flags & (IMMUTABLE | APPEND))
1129e979c658Sreinoud 		return EPERM;
1130e979c658Sreinoud #endif
1131e979c658Sreinoud 
1132e979c658Sreinoud 	/* resize file to the requested size */
1133e979c658Sreinoud 	error = udf_resize_node(udf_node, newsize, &extended);
1134e979c658Sreinoud 
1135e979c658Sreinoud 	if (error == 0) {
1136e979c658Sreinoud 		/* mark change */
1137e979c658Sreinoud 		udf_node->i_flags |= IN_CHANGE | IN_MODIFY;
1138d11ea3eaSchristos 		if (vp->v_mount->mnt_flag & MNT_RELATIME)
1139d11ea3eaSchristos 			udf_node->i_flags |= IN_ACCESS;
11409d15fe51Sreinoud 		udf_update(vp, NULL, NULL, NULL, 0);
1141e979c658Sreinoud 	}
1142e979c658Sreinoud 
1143e979c658Sreinoud 	return error;
1144e979c658Sreinoud }
1145e979c658Sreinoud 
1146e979c658Sreinoud 
1147e979c658Sreinoud static int
udf_chflags(struct vnode * vp,mode_t mode,kauth_cred_t cred)1148e979c658Sreinoud udf_chflags(struct vnode *vp, mode_t mode, kauth_cred_t cred)
1149e979c658Sreinoud {
1150e979c658Sreinoud 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
1151e979c658Sreinoud 		return EROFS;
1152e979c658Sreinoud 
1153d2126d96Sreinoud 	/*
1154d2126d96Sreinoud 	 * XXX we can't do this yet, as its not described in the standard yet
1155d2126d96Sreinoud 	 */
1156e979c658Sreinoud 
1157d2126d96Sreinoud 	return EOPNOTSUPP;
1158e979c658Sreinoud }
1159e979c658Sreinoud 
1160e979c658Sreinoud 
1161e979c658Sreinoud static int
udf_chtimes(struct vnode * vp,struct timespec * atime,struct timespec * mtime,struct timespec * birthtime,int setattrflags,kauth_cred_t cred)1162e979c658Sreinoud udf_chtimes(struct vnode *vp,
1163e979c658Sreinoud 	struct timespec *atime, struct timespec *mtime,
1164e979c658Sreinoud 	struct timespec *birthtime, int setattrflags,
1165e979c658Sreinoud 	kauth_cred_t cred)
1166e979c658Sreinoud {
1167e979c658Sreinoud 	struct udf_node  *udf_node = VTOI(vp);
1168863a01b5Selad 	uid_t uid;
1169e979c658Sreinoud 	gid_t gid;
1170e979c658Sreinoud 	int error;
1171e979c658Sreinoud 
1172e979c658Sreinoud #ifdef notyet
1173e979c658Sreinoud 	/* TODO get vaflags from the extended attributes? */
1174e979c658Sreinoud 	/* Immutable or append-only files cannot be modified, either. */
1175e979c658Sreinoud 	if (udf_node->flags & (IMMUTABLE | APPEND))
1176e979c658Sreinoud 		return EPERM;
1177e979c658Sreinoud #endif
1178e979c658Sreinoud 
1179e979c658Sreinoud 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
1180e979c658Sreinoud 		return EROFS;
1181e979c658Sreinoud 
1182e979c658Sreinoud 	/* retrieve uid/gid values */
1183e979c658Sreinoud 	udf_getownership(udf_node, &uid, &gid);
1184e979c658Sreinoud 
1185e979c658Sreinoud 	/* check permissions */
11860c9d8d15Selad 	error = kauth_authorize_vnode(cred, KAUTH_VNODE_WRITE_TIMES, vp,
11879aa2a9c3Schristos 	    NULL, genfs_can_chtimes(vp, cred, uid, setattrflags));
1188e979c658Sreinoud 	if (error)
1189863a01b5Selad 		return (error);
1190e979c658Sreinoud 
1191e979c658Sreinoud 	/* update node flags depending on what times are passed */
1192e979c658Sreinoud 	if (atime->tv_sec != VNOVAL)
1193e979c658Sreinoud 		if (!(vp->v_mount->mnt_flag & MNT_NOATIME))
1194e979c658Sreinoud 			udf_node->i_flags |= IN_ACCESS;
1195d11ea3eaSchristos 	if ((mtime->tv_sec != VNOVAL) || (birthtime->tv_sec != VNOVAL)) {
1196e979c658Sreinoud 		udf_node->i_flags |= IN_CHANGE | IN_UPDATE;
1197d11ea3eaSchristos 		if (vp->v_mount->mnt_flag & MNT_RELATIME)
1198d11ea3eaSchristos 			udf_node->i_flags |= IN_ACCESS;
1199d11ea3eaSchristos 	}
1200e979c658Sreinoud 
12019d15fe51Sreinoud 	return udf_update(vp, atime, mtime, birthtime, 0);
1202e979c658Sreinoud }
1203e979c658Sreinoud 
1204e979c658Sreinoud 
1205cff5e7adSreinoud int
udf_setattr(void * v)1206cff5e7adSreinoud udf_setattr(void *v)
1207cff5e7adSreinoud {
1208cff5e7adSreinoud 	struct vop_setattr_args /* {
1209cff5e7adSreinoud 		struct vnode *a_vp;
1210cff5e7adSreinoud 		struct vattr *a_vap;
1211fc9422c9Selad 		kauth_cred_t a_cred;
1212e979c658Sreinoud 		struct lwp   *a_l;
1213cff5e7adSreinoud 	} */ *ap = v;
1214cff5e7adSreinoud 	struct vnode *vp = ap->a_vp;
1215e979c658Sreinoud /*	struct udf_node  *udf_node = VTOI(vp); */
1216e979c658Sreinoud /*	struct udf_mount *ump = udf_node->ump; */
1217e979c658Sreinoud 	kauth_cred_t cred = ap->a_cred;
1218cff5e7adSreinoud 	struct vattr *vap = ap->a_vap;
1219e979c658Sreinoud 	int error;
1220cff5e7adSreinoud 
1221e979c658Sreinoud 	DPRINTF(CALL, ("udf_setattr called\n"));
1222cff5e7adSreinoud 
1223e979c658Sreinoud 	/* Abort if any unsettable attribute is given. */
1224e979c658Sreinoud 	error = 0;
1225e979c658Sreinoud 	if (vap->va_type != VNON ||
1226e979c658Sreinoud 	    vap->va_nlink != VNOVAL ||
1227e979c658Sreinoud 	    vap->va_fsid != VNOVAL ||
1228e979c658Sreinoud 	    vap->va_fileid != VNOVAL ||
1229e979c658Sreinoud 	    vap->va_blocksize != VNOVAL ||
1230e979c658Sreinoud #ifdef notyet
12319d15fe51Sreinoud 	    /* checks are debated */
1232e979c658Sreinoud 	    vap->va_ctime.tv_sec != VNOVAL ||
1233e979c658Sreinoud 	    vap->va_ctime.tv_nsec != VNOVAL ||
1234e979c658Sreinoud 	    vap->va_birthtime.tv_sec != VNOVAL ||
1235e979c658Sreinoud 	    vap->va_birthtime.tv_nsec != VNOVAL ||
12369d15fe51Sreinoud #endif
1237e979c658Sreinoud 	    vap->va_gen != VNOVAL ||
1238e979c658Sreinoud 	    vap->va_rdev != VNOVAL ||
1239e979c658Sreinoud 	    vap->va_bytes != VNOVAL)
1240e979c658Sreinoud 		error = EINVAL;
1241cff5e7adSreinoud 
1242e979c658Sreinoud 	DPRINTF(ATTR, ("setattr changing:\n"));
1243e979c658Sreinoud 	if (error == 0 && (vap->va_flags != VNOVAL)) {
1244e979c658Sreinoud 		DPRINTF(ATTR, ("\tchflags\n"));
1245e979c658Sreinoud 	 	error = udf_chflags(vp, vap->va_flags, cred);
12460c1391f0Schristos 	}
1247cff5e7adSreinoud 
1248e979c658Sreinoud 	if (error == 0 && (vap->va_size != VNOVAL)) {
1249e979c658Sreinoud 		DPRINTF(ATTR, ("\tchsize\n"));
1250e979c658Sreinoud 		error = udf_chsize(vp, vap->va_size, cred);
1251e979c658Sreinoud 	}
1252e979c658Sreinoud 
1253e979c658Sreinoud 	if (error == 0 && (vap->va_uid != VNOVAL || vap->va_gid != VNOVAL)) {
1254e979c658Sreinoud 		DPRINTF(ATTR, ("\tchown\n"));
1255e979c658Sreinoud 		error = udf_chown(vp, vap->va_uid, vap->va_gid, cred);
1256e979c658Sreinoud 	}
1257e979c658Sreinoud 
1258e979c658Sreinoud 	if (error == 0 && (vap->va_mode != VNOVAL)) {
1259e979c658Sreinoud 		DPRINTF(ATTR, ("\tchmod\n"));
1260e979c658Sreinoud 		error = udf_chmod(vp, vap->va_mode, cred);
1261e979c658Sreinoud 	}
1262e979c658Sreinoud 
1263e979c658Sreinoud 	if (error == 0 &&
1264e979c658Sreinoud 	    ((vap->va_atime.tv_sec != VNOVAL &&
1265e979c658Sreinoud 	      vap->va_atime.tv_nsec != VNOVAL)   ||
1266e979c658Sreinoud 	     (vap->va_mtime.tv_sec != VNOVAL &&
1267e979c658Sreinoud 	      vap->va_mtime.tv_nsec != VNOVAL))
1268e979c658Sreinoud 	    ) {
1269e979c658Sreinoud 		DPRINTF(ATTR, ("\tchtimes\n"));
1270e979c658Sreinoud 		error = udf_chtimes(vp, &vap->va_atime, &vap->va_mtime,
1271e979c658Sreinoud 		    &vap->va_birthtime, vap->va_vaflags, cred);
1272e979c658Sreinoud 	}
1273e979c658Sreinoud 
1274e979c658Sreinoud 	return error;
1275cff5e7adSreinoud }
1276cff5e7adSreinoud 
1277cff5e7adSreinoud /* --------------------------------------------------------------------- */
1278cff5e7adSreinoud 
1279cff5e7adSreinoud /*
1280cff5e7adSreinoud  * Return POSIX pathconf information for UDF file systems.
1281cff5e7adSreinoud  */
1282cff5e7adSreinoud int
udf_pathconf(void * v)1283cff5e7adSreinoud udf_pathconf(void *v)
1284cff5e7adSreinoud {
1285cff5e7adSreinoud 	struct vop_pathconf_args /* {
1286cff5e7adSreinoud 		struct vnode *a_vp;
1287cff5e7adSreinoud 		int a_name;
1288cff5e7adSreinoud 		register_t *a_retval;
1289cff5e7adSreinoud 	} */ *ap = v;
1290cff5e7adSreinoud 	uint32_t bits;
1291cff5e7adSreinoud 
1292cff5e7adSreinoud 	DPRINTF(CALL, ("udf_pathconf called\n"));
1293cff5e7adSreinoud 
1294cff5e7adSreinoud 	switch (ap->a_name) {
1295cff5e7adSreinoud 	case _PC_LINK_MAX:
1296cff5e7adSreinoud 		*ap->a_retval = (1<<16)-1;	/* 16 bits */
1297cff5e7adSreinoud 		return 0;
1298cff5e7adSreinoud 	case _PC_NAME_MAX:
12992fcd0204Schristos 		*ap->a_retval = UDF_MAXNAMLEN;
1300cff5e7adSreinoud 		return 0;
1301cff5e7adSreinoud 	case _PC_PATH_MAX:
1302cff5e7adSreinoud 		*ap->a_retval = PATH_MAX;
1303cff5e7adSreinoud 		return 0;
1304cff5e7adSreinoud 	case _PC_PIPE_BUF:
1305cff5e7adSreinoud 		*ap->a_retval = PIPE_BUF;
1306cff5e7adSreinoud 		return 0;
1307cff5e7adSreinoud 	case _PC_CHOWN_RESTRICTED:
1308cff5e7adSreinoud 		*ap->a_retval = 1;
1309cff5e7adSreinoud 		return 0;
1310cff5e7adSreinoud 	case _PC_NO_TRUNC:
1311cff5e7adSreinoud 		*ap->a_retval = 1;
1312cff5e7adSreinoud 		return 0;
1313cff5e7adSreinoud 	case _PC_SYNC_IO:
1314cff5e7adSreinoud 		*ap->a_retval = 0;     /* synchronised is off for performance */
1315cff5e7adSreinoud 		return 0;
1316cff5e7adSreinoud 	case _PC_FILESIZEBITS:
1317e979c658Sreinoud 		/* 64 bit file offsets -> 2+floor(2log(2^64-1)) = 2 + 63 = 65 */
1318e979c658Sreinoud 		bits = 64; /* XXX ought to deliver 65 */
1319e979c658Sreinoud #if 0
1320cff5e7adSreinoud 		if (udf_node)
1321e979c658Sreinoud 			bits = 64 * vp->v_mount->mnt_dev_bshift;
1322e979c658Sreinoud #endif
1323cff5e7adSreinoud 		*ap->a_retval = bits;
1324cff5e7adSreinoud 		return 0;
132579e3c74fSchristos 	default:
132679e3c74fSchristos 		return genfs_pathconf(ap);
13270c1391f0Schristos 	}
1328cff5e7adSreinoud }
1329cff5e7adSreinoud 
1330cff5e7adSreinoud 
1331cff5e7adSreinoud /* --------------------------------------------------------------------- */
1332cff5e7adSreinoud 
1333cff5e7adSreinoud int
udf_open(void * v)1334cff5e7adSreinoud udf_open(void *v)
1335cff5e7adSreinoud {
1336cff5e7adSreinoud 	struct vop_open_args /* {
1337cff5e7adSreinoud 		struct vnode *a_vp;
1338cff5e7adSreinoud 		int a_mode;
1339fc9422c9Selad 		kauth_cred_t a_cred;
1340cff5e7adSreinoud 		struct proc *a_p;
13414d595fd7Schristos 	} */ *ap = v;
1342e979c658Sreinoud 	int flags;
1343cff5e7adSreinoud 
1344cff5e7adSreinoud 	DPRINTF(CALL, ("udf_open called\n"));
1345e979c658Sreinoud 
1346e979c658Sreinoud 	/*
1347e979c658Sreinoud 	 * Files marked append-only must be opened for appending.
134809fb1792Sandvar 	 * TODO: get chflags(2) flags from extended attribute.
1349e979c658Sreinoud 	 */
1350e979c658Sreinoud 	flags = 0;
1351e979c658Sreinoud 	if ((flags & APPEND) && (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
1352e979c658Sreinoud 		return (EPERM);
1353cff5e7adSreinoud 
1354cff5e7adSreinoud 	return 0;
1355cff5e7adSreinoud }
1356cff5e7adSreinoud 
1357cff5e7adSreinoud 
1358cff5e7adSreinoud /* --------------------------------------------------------------------- */
1359cff5e7adSreinoud 
1360cff5e7adSreinoud int
udf_close(void * v)1361cff5e7adSreinoud udf_close(void *v)
1362cff5e7adSreinoud {
1363cff5e7adSreinoud 	struct vop_close_args /* {
1364cff5e7adSreinoud 		struct vnode *a_vp;
1365cff5e7adSreinoud 		int a_fflag;
1366fc9422c9Selad 		kauth_cred_t a_cred;
1367cff5e7adSreinoud 		struct proc *a_p;
1368cff5e7adSreinoud 	} */ *ap = v;
1369cff5e7adSreinoud 	struct vnode *vp = ap->a_vp;
1370cff5e7adSreinoud 	struct udf_node *udf_node = VTOI(vp);
1371c1a508d1Sreinoud 	int async = vp->v_mount->mnt_flag & MNT_ASYNC;
1372c1a508d1Sreinoud 	int error;
1373cff5e7adSreinoud 
1374cff5e7adSreinoud 	DPRINTF(CALL, ("udf_close called\n"));
1375cff5e7adSreinoud 	udf_node = udf_node;	/* shut up gcc */
1376cff5e7adSreinoud 
1377c1a508d1Sreinoud 	if (!async && (vp->v_type != VDIR)) {
1378d2a0ebb6Sad 		rw_enter(vp->v_uobj.vmobjlock, RW_WRITER);
1379c1a508d1Sreinoud 		error = VOP_PUTPAGES(vp, 0, 0, PGO_CLEANIT);
1380c1a508d1Sreinoud 		if (error)
1381c1a508d1Sreinoud 			return error;
1382c1a508d1Sreinoud 	}
1383c1a508d1Sreinoud 
1384e225b7bdSrmind 	mutex_enter(vp->v_interlock);
138523bf8800Sad 	if (vrefcnt(vp) > 1)
1386e979c658Sreinoud 		udf_itimes(udf_node, NULL, NULL, NULL);
1387e225b7bdSrmind 	mutex_exit(vp->v_interlock);
1388cff5e7adSreinoud 
1389cff5e7adSreinoud 	return 0;
1390cff5e7adSreinoud }
1391cff5e7adSreinoud 
1392cff5e7adSreinoud 
1393cff5e7adSreinoud /* --------------------------------------------------------------------- */
1394cff5e7adSreinoud 
1395009f5d2fSelad static int
udf_check_possible(struct vnode * vp,struct vattr * vap,mode_t mode)1396c13b143eSpgoyette udf_check_possible(struct vnode *vp, struct vattr *vap, mode_t mode)
1397cff5e7adSreinoud {
1398e979c658Sreinoud 	int flags;
1399cff5e7adSreinoud 
1400cff5e7adSreinoud 	/* check if we are allowed to write */
1401009f5d2fSelad 	switch (vap->va_type) {
1402cff5e7adSreinoud 	case VDIR:
1403cff5e7adSreinoud 	case VLNK:
1404cff5e7adSreinoud 	case VREG:
1405cff5e7adSreinoud 		/*
1406cff5e7adSreinoud 		 * normal nodes: check if we're on a read-only mounted
1407cff5e7adSreinoud 		 * filingsystem and bomb out if we're trying to write.
1408cff5e7adSreinoud 		 */
1409cff5e7adSreinoud 		if ((mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY))
1410cff5e7adSreinoud 			return EROFS;
1411cff5e7adSreinoud 		break;
1412cff5e7adSreinoud 	case VBLK:
1413cff5e7adSreinoud 	case VCHR:
1414cff5e7adSreinoud 	case VSOCK:
1415cff5e7adSreinoud 	case VFIFO:
1416cff5e7adSreinoud 		/*
1417cff5e7adSreinoud 		 * special nodes: even on read-only mounted filingsystems
1418cff5e7adSreinoud 		 * these are allowed to be written to if permissions allow.
1419cff5e7adSreinoud 		 */
1420cff5e7adSreinoud 		break;
1421cff5e7adSreinoud 	default:
1422cff5e7adSreinoud 		/* no idea what this is */
1423cff5e7adSreinoud 		return EINVAL;
1424cff5e7adSreinoud 	}
1425cff5e7adSreinoud 
1426e979c658Sreinoud 	/* noone may write immutable files */
142709fb1792Sandvar 	/* TODO: get chflags(2) flags from extended attribute. */
1428e979c658Sreinoud 	flags = 0;
1429e979c658Sreinoud 	if ((mode & VWRITE) && (flags & IMMUTABLE))
1430e979c658Sreinoud 		return EPERM;
1431cff5e7adSreinoud 
1432009f5d2fSelad 	return 0;
1433009f5d2fSelad }
1434009f5d2fSelad 
1435009f5d2fSelad static int
udf_check_permitted(struct vnode * vp,struct vattr * vap,accmode_t accmode,kauth_cred_t cred)14369aa2a9c3Schristos udf_check_permitted(struct vnode *vp, struct vattr *vap, accmode_t accmode,
1437009f5d2fSelad     kauth_cred_t cred)
1438009f5d2fSelad {
143987092026Selad 	/* ask the generic genfs_can_access to advice on security */
14409aa2a9c3Schristos 	return kauth_authorize_vnode(cred, KAUTH_ACCESS_ACTION(accmode,
14419aa2a9c3Schristos 	    vp->v_type, vap->va_mode), vp, NULL, genfs_can_access(vp, cred,
14429aa2a9c3Schristos 	    vap->va_uid, vap->va_gid, vap->va_mode, NULL, accmode));
1443cff5e7adSreinoud }
1444cff5e7adSreinoud 
1445009f5d2fSelad int
udf_access(void * v)1446009f5d2fSelad udf_access(void *v)
1447009f5d2fSelad {
1448009f5d2fSelad 	struct vop_access_args /* {
1449009f5d2fSelad 		struct vnode *a_vp;
14509aa2a9c3Schristos 		accmode_t a_accmode;
1451009f5d2fSelad 		kauth_cred_t a_cred;
1452009f5d2fSelad 		struct proc *a_p;
1453009f5d2fSelad 	} */ *ap = v;
1454009f5d2fSelad 	struct vnode    *vp   = ap->a_vp;
14559aa2a9c3Schristos 	accmode_t	 accmode = ap->a_accmode;
1456009f5d2fSelad 	kauth_cred_t     cred = ap->a_cred;
1457009f5d2fSelad 	/* struct udf_node *udf_node = VTOI(vp); */
1458009f5d2fSelad 	struct vattr vap;
1459009f5d2fSelad 	int error;
1460009f5d2fSelad 
1461009f5d2fSelad 	DPRINTF(CALL, ("udf_access called\n"));
1462009f5d2fSelad 
1463009f5d2fSelad 	error = VOP_GETATTR(vp, &vap, NULL);
1464009f5d2fSelad 	if (error)
1465009f5d2fSelad 		return error;
1466009f5d2fSelad 
14679aa2a9c3Schristos 	error = udf_check_possible(vp, &vap, accmode);
1468009f5d2fSelad 	if (error)
1469009f5d2fSelad 		return error;
1470009f5d2fSelad 
14719aa2a9c3Schristos 	error = udf_check_permitted(vp, &vap, accmode, cred);
1472009f5d2fSelad 
1473009f5d2fSelad 	return error;
1474009f5d2fSelad }
1475009f5d2fSelad 
1476cff5e7adSreinoud /* --------------------------------------------------------------------- */
1477cff5e7adSreinoud 
1478cff5e7adSreinoud int
udf_create(void * v)1479cff5e7adSreinoud udf_create(void *v)
1480cff5e7adSreinoud {
148104c776e5Shannken 	struct vop_create_v3_args /* {
1482cff5e7adSreinoud 		struct vnode *a_dvp;
1483cff5e7adSreinoud 		struct vnode **a_vpp;
1484cff5e7adSreinoud 		struct componentname *a_cnp;
1485cff5e7adSreinoud 		struct vattr *a_vap;
1486cff5e7adSreinoud 	} */ *ap = v;
1487e979c658Sreinoud 	struct vnode  *dvp = ap->a_dvp;
1488e979c658Sreinoud 	struct vnode **vpp = ap->a_vpp;
1489e979c658Sreinoud 	struct vattr  *vap  = ap->a_vap;
1490cff5e7adSreinoud 	struct componentname *cnp = ap->a_cnp;
1491e979c658Sreinoud 	int error;
1492cff5e7adSreinoud 
1493e979c658Sreinoud 	DPRINTF(CALL, ("udf_create called\n"));
1494e979c658Sreinoud 	error = udf_create_node(dvp, vpp, vap, cnp);
1495cff5e7adSreinoud 
1496e979c658Sreinoud 	return error;
1497cff5e7adSreinoud }
1498cff5e7adSreinoud 
1499cff5e7adSreinoud /* --------------------------------------------------------------------- */
1500cff5e7adSreinoud 
1501cff5e7adSreinoud int
udf_mknod(void * v)1502cff5e7adSreinoud udf_mknod(void *v)
1503cff5e7adSreinoud {
150404c776e5Shannken 	struct vop_mknod_v3_args /* {
1505cff5e7adSreinoud 		struct vnode *a_dvp;
1506cff5e7adSreinoud 		struct vnode **a_vpp;
1507cff5e7adSreinoud 		struct componentname *a_cnp;
1508cff5e7adSreinoud 		struct vattr *a_vap;
1509cff5e7adSreinoud 	} */ *ap = v;
1510e979c658Sreinoud 	struct vnode  *dvp = ap->a_dvp;
1511e979c658Sreinoud 	struct vnode **vpp = ap->a_vpp;
1512e979c658Sreinoud 	struct vattr  *vap  = ap->a_vap;
1513e979c658Sreinoud 	struct componentname *cnp = ap->a_cnp;
1514e979c658Sreinoud 	int error;
1515cff5e7adSreinoud 
1516e979c658Sreinoud 	DPRINTF(CALL, ("udf_mknod called\n"));
1517e979c658Sreinoud 	error = udf_create_node(dvp, vpp, vap, cnp);
1518cff5e7adSreinoud 
1519e979c658Sreinoud 	return error;
1520cff5e7adSreinoud }
1521cff5e7adSreinoud 
1522cff5e7adSreinoud /* --------------------------------------------------------------------- */
1523cff5e7adSreinoud 
1524cff5e7adSreinoud int
udf_mkdir(void * v)1525e979c658Sreinoud udf_mkdir(void *v)
1526cff5e7adSreinoud {
152704c776e5Shannken 	struct vop_mkdir_v3_args /* {
1528cff5e7adSreinoud 		struct vnode *a_dvp;
1529e979c658Sreinoud 		struct vnode **a_vpp;
1530cff5e7adSreinoud 		struct componentname *a_cnp;
1531e979c658Sreinoud 		struct vattr *a_vap;
1532cff5e7adSreinoud 	} */ *ap = v;
1533cff5e7adSreinoud 	struct vnode  *dvp = ap->a_dvp;
1534e979c658Sreinoud 	struct vnode **vpp = ap->a_vpp;
1535e979c658Sreinoud 	struct vattr  *vap  = ap->a_vap;
1536e979c658Sreinoud 	struct componentname *cnp = ap->a_cnp;
1537e979c658Sreinoud 	int error;
1538cff5e7adSreinoud 
1539e979c658Sreinoud 	DPRINTF(CALL, ("udf_mkdir called\n"));
1540e979c658Sreinoud 	error = udf_create_node(dvp, vpp, vap, cnp);
1541cff5e7adSreinoud 
1542e979c658Sreinoud 	return error;
1543cff5e7adSreinoud }
1544cff5e7adSreinoud 
1545cff5e7adSreinoud /* --------------------------------------------------------------------- */
1546cff5e7adSreinoud 
1547cff5e7adSreinoud int
udf_link(void * v)1548cff5e7adSreinoud udf_link(void *v)
1549cff5e7adSreinoud {
155046e71c7dSriastradh 	struct vop_link_v2_args /* {
1551cff5e7adSreinoud 		struct vnode *a_dvp;
1552cff5e7adSreinoud 		struct vnode *a_vp;
1553cff5e7adSreinoud 		struct componentname *a_cnp;
1554cff5e7adSreinoud 	} */ *ap = v;
1555cff5e7adSreinoud 	struct vnode *dvp = ap->a_dvp;
1556cff5e7adSreinoud 	struct vnode *vp  = ap->a_vp;
1557cff5e7adSreinoud 	struct componentname *cnp = ap->a_cnp;
155889513510Schristos 	struct udf_node *udf_node, *dir_node;
155989513510Schristos 	struct vattr vap;
156089513510Schristos 	int error, abrt = 1;
1561cff5e7adSreinoud 
156289513510Schristos 	DPRINTF(CALL, ("udf_link called\n"));
156389513510Schristos 	KASSERT(dvp != vp);
156489513510Schristos 	KASSERT(vp->v_type != VDIR);
156589513510Schristos 	KASSERT(dvp->v_mount == vp->v_mount);
156689513510Schristos 
156789513510Schristos 	/* get attributes */
156889513510Schristos 	dir_node = VTOI(dvp);
156989513510Schristos 	udf_node = VTOI(vp);
157089513510Schristos 
157189513510Schristos 	if ((error = vn_lock(vp, LK_EXCLUSIVE))) {
1572a7123e48Sreinoud 		DPRINTF(LOCKING, ("exclusive lock failed for vnode %p\n", vp));
157389513510Schristos 		goto out;
157489513510Schristos 	}
157589513510Schristos 
157689513510Schristos 	error = VOP_GETATTR(vp, &vap, FSCRED);
1577e979c658Sreinoud 	if (error)
157889513510Schristos 		goto out1;
1579cff5e7adSreinoud 
158089513510Schristos 	/* check link count overflow */
158189513510Schristos 	if (vap.va_nlink >= (1<<16)-1) {	/* uint16_t */
158289513510Schristos 		error = EMLINK;
158389513510Schristos 		goto out1;
158489513510Schristos 	}
158589513510Schristos 	error = kauth_authorize_vnode(cnp->cn_cred, KAUTH_VNODE_ADD_LINK, vp,
158689513510Schristos 	    dvp, 0);
158789513510Schristos 	if (error)
158889513510Schristos 		goto out1;
158989513510Schristos 	abrt = 0;
159089513510Schristos 	error = udf_dir_attach(dir_node->ump, dir_node, udf_node, &vap, cnp);
159189513510Schristos out1:
159289513510Schristos 	VOP_UNLOCK(vp);
159389513510Schristos out:
159489513510Schristos 	if (abrt)
159589513510Schristos 		VOP_ABORTOP(dvp, cnp);
1596e979c658Sreinoud 	return error;
1597cff5e7adSreinoud }
1598cff5e7adSreinoud 
1599cff5e7adSreinoud /* --------------------------------------------------------------------- */
1600cff5e7adSreinoud 
1601e979c658Sreinoud static int
udf_do_symlink(struct udf_node * udf_node,char * target)1602e979c658Sreinoud udf_do_symlink(struct udf_node *udf_node, char *target)
1603e979c658Sreinoud {
1604e979c658Sreinoud 	struct pathcomp pathcomp;
1605e979c658Sreinoud 	uint8_t *pathbuf, *pathpos, *compnamepos;
1606e979c658Sreinoud 	char *mntonname;
1607e979c658Sreinoud 	int pathlen, len, compnamelen, mntonnamelen;
1608e979c658Sreinoud 	int error;
1609e979c658Sreinoud 
1610e979c658Sreinoud 	/* process `target' to an UDF structure */
1611e979c658Sreinoud 	pathbuf = malloc(UDF_SYMLINKBUFLEN, M_UDFTEMP, M_WAITOK);
1612e979c658Sreinoud 	pathpos = pathbuf;
1613e979c658Sreinoud 	pathlen = 0;
1614e979c658Sreinoud 
1615e979c658Sreinoud 	if (*target == '/') {
1616e979c658Sreinoud 		/* symlink starts from the root */
1617e979c658Sreinoud 		len = UDF_PATH_COMP_SIZE;
1618e979c658Sreinoud 		memset(&pathcomp, 0, len);
1619e979c658Sreinoud 		pathcomp.type = UDF_PATH_COMP_ROOT;
1620e979c658Sreinoud 
1621e979c658Sreinoud 		/* check if its mount-point relative! */
1622e979c658Sreinoud 		mntonname    = udf_node->ump->vfs_mountp->mnt_stat.f_mntonname;
1623e979c658Sreinoud 		mntonnamelen = strlen(mntonname);
1624e979c658Sreinoud 		if (strlen(target) >= mntonnamelen) {
1625e979c658Sreinoud 			if (strncmp(target, mntonname, mntonnamelen) == 0) {
1626e979c658Sreinoud 				pathcomp.type = UDF_PATH_COMP_MOUNTROOT;
1627e979c658Sreinoud 				target += mntonnamelen;
1628e979c658Sreinoud 			}
1629e979c658Sreinoud 		} else {
1630e979c658Sreinoud 			target++;
1631e979c658Sreinoud 		}
1632e979c658Sreinoud 
1633e979c658Sreinoud 		memcpy(pathpos, &pathcomp, len);
1634e979c658Sreinoud 		pathpos += len;
1635e979c658Sreinoud 		pathlen += len;
1636e979c658Sreinoud 	}
1637e979c658Sreinoud 
1638e979c658Sreinoud 	error = 0;
1639e979c658Sreinoud 	while (*target) {
1640e979c658Sreinoud 		/* ignore multiple '/' */
1641e979c658Sreinoud 		while (*target == '/') {
1642e979c658Sreinoud 			target++;
1643e979c658Sreinoud 		}
1644e979c658Sreinoud 		if (!*target)
1645e979c658Sreinoud 			break;
1646e979c658Sreinoud 
1647e979c658Sreinoud 		/* extract component name */
1648e979c658Sreinoud 		compnamelen = 0;
1649e979c658Sreinoud 		compnamepos = target;
1650e979c658Sreinoud 		while ((*target) && (*target != '/')) {
1651e979c658Sreinoud 			target++;
1652e979c658Sreinoud 			compnamelen++;
1653e979c658Sreinoud 		}
1654e979c658Sreinoud 
1655e979c658Sreinoud 		/* just trunc if too long ?? (security issue) */
1656e979c658Sreinoud 		if (compnamelen >= 127) {
1657e979c658Sreinoud 			error = ENAMETOOLONG;
1658e979c658Sreinoud 			break;
1659e979c658Sreinoud 		}
1660e979c658Sreinoud 
1661e979c658Sreinoud 		/* convert unix name to UDF name */
1662e979c658Sreinoud 		len = sizeof(struct pathcomp);
1663e979c658Sreinoud 		memset(&pathcomp, 0, len);
1664e979c658Sreinoud 		pathcomp.type = UDF_PATH_COMP_NAME;
1665e979c658Sreinoud 		len = UDF_PATH_COMP_SIZE;
1666e979c658Sreinoud 
1667e979c658Sreinoud 		if ((compnamelen == 2) && (strncmp(compnamepos, "..", 2) == 0))
1668e979c658Sreinoud 			pathcomp.type = UDF_PATH_COMP_PARENTDIR;
1669e979c658Sreinoud 		if ((compnamelen == 1) && (*compnamepos == '.'))
1670e979c658Sreinoud 			pathcomp.type = UDF_PATH_COMP_CURDIR;
1671e979c658Sreinoud 
1672e979c658Sreinoud 		if (pathcomp.type == UDF_PATH_COMP_NAME) {
1673e979c658Sreinoud 			unix_to_udf_name(
1674e979c658Sreinoud 				(char *) &pathcomp.ident, &pathcomp.l_ci,
1675e979c658Sreinoud 				compnamepos, compnamelen,
1676e979c658Sreinoud 				&udf_node->ump->logical_vol->desc_charset);
1677e979c658Sreinoud 			len = UDF_PATH_COMP_SIZE + pathcomp.l_ci;
1678e979c658Sreinoud 		}
1679e979c658Sreinoud 
1680e979c658Sreinoud 		if (pathlen + len >= UDF_SYMLINKBUFLEN) {
1681e979c658Sreinoud 			error = ENAMETOOLONG;
1682e979c658Sreinoud 			break;
1683e979c658Sreinoud 		}
1684e979c658Sreinoud 
1685e979c658Sreinoud 		memcpy(pathpos, &pathcomp, len);
1686e979c658Sreinoud 		pathpos += len;
1687e979c658Sreinoud 		pathlen += len;
1688e979c658Sreinoud 	}
1689e979c658Sreinoud 
1690e979c658Sreinoud 	if (error) {
1691183889cbSandvar 		/* apparently too big */
1692e979c658Sreinoud 		free(pathbuf, M_UDFTEMP);
1693e979c658Sreinoud 		return error;
1694e979c658Sreinoud 	}
1695e979c658Sreinoud 
1696e979c658Sreinoud 	error = udf_grow_node(udf_node, pathlen);
1697e979c658Sreinoud 	if (error) {
1698e979c658Sreinoud 		/* failed to pregrow node */
1699e979c658Sreinoud 		free(pathbuf, M_UDFTEMP);
1700e979c658Sreinoud 		return error;
1701e979c658Sreinoud 	}
1702e979c658Sreinoud 
1703e979c658Sreinoud 	/* write out structure on the new file */
1704e979c658Sreinoud 	error = vn_rdwr(UIO_WRITE, udf_node->vnode,
1705e979c658Sreinoud 		pathbuf, pathlen, 0,
17068188ee76Shannken 		UIO_SYSSPACE, IO_ALTSEMANTICS,
1707e979c658Sreinoud 		FSCRED, NULL, NULL);
1708a3665ba5Sreinoud 
1709a3665ba5Sreinoud 	/* return status of symlink contents writeout */
1710e979c658Sreinoud 	free(pathbuf, M_UDFTEMP);
1711e979c658Sreinoud 	return error;
1712e979c658Sreinoud }
1713e979c658Sreinoud 
1714e979c658Sreinoud 
1715cff5e7adSreinoud int
udf_symlink(void * v)1716cff5e7adSreinoud udf_symlink(void *v)
1717cff5e7adSreinoud {
171804c776e5Shannken 	struct vop_symlink_v3_args /* {
1719cff5e7adSreinoud 		struct vnode *a_dvp;
1720cff5e7adSreinoud 		struct vnode **a_vpp;
1721cff5e7adSreinoud 		struct componentname *a_cnp;
1722cff5e7adSreinoud 		struct vattr *a_vap;
1723cff5e7adSreinoud 		char *a_target;
1724cff5e7adSreinoud 	} */ *ap = v;
1725cff5e7adSreinoud 	struct vnode  *dvp = ap->a_dvp;
1726e979c658Sreinoud 	struct vnode **vpp = ap->a_vpp;
1727e979c658Sreinoud 	struct vattr  *vap  = ap->a_vap;
1728cff5e7adSreinoud 	struct componentname *cnp = ap->a_cnp;
1729e979c658Sreinoud 	struct udf_node *dir_node;
1730e979c658Sreinoud 	struct udf_node *udf_node;
1731e979c658Sreinoud 	int error;
1732cff5e7adSreinoud 
1733e979c658Sreinoud 	DPRINTF(CALL, ("udf_symlink called\n"));
1734e979c658Sreinoud 	DPRINTF(CALL, ("\tlinking to `%s`\n",  ap->a_target));
1735e979c658Sreinoud 	error = udf_create_node(dvp, vpp, vap, cnp);
1736e979c658Sreinoud 	KASSERT(((error == 0) && (*vpp != NULL)) || ((error && (*vpp == NULL))));
1737e979c658Sreinoud 	if (!error) {
1738e979c658Sreinoud 		dir_node = VTOI(dvp);
1739e979c658Sreinoud 		udf_node = VTOI(*vpp);
1740e979c658Sreinoud 		KASSERT(udf_node);
1741e979c658Sreinoud 		error = udf_do_symlink(udf_node, ap->a_target);
1742e979c658Sreinoud 		if (error) {
1743e979c658Sreinoud 			/* remove node */
1744e979c658Sreinoud 			udf_dir_detach(udf_node->ump, dir_node, udf_node, cnp);
17455d76927eSreinoud 			vrele(*vpp);
17465d76927eSreinoud 			*vpp = NULL;
1747e979c658Sreinoud 		}
1748e979c658Sreinoud 	}
1749e979c658Sreinoud 	return error;
1750cff5e7adSreinoud }
1751cff5e7adSreinoud 
1752cff5e7adSreinoud /* --------------------------------------------------------------------- */
1753cff5e7adSreinoud 
1754e3b6b345Sreinoud static int
udf_do_readlink(struct udf_node * udf_node,uint64_t filesize,uint8_t * targetbuf,int * length)1755e3b6b345Sreinoud udf_do_readlink(struct udf_node *udf_node, uint64_t filesize,
1756e3b6b345Sreinoud 	uint8_t *targetbuf, int *length)
1757cff5e7adSreinoud {
1758e979c658Sreinoud 	struct pathcomp pathcomp;
1759e3b6b345Sreinoud 	uint8_t *pathbuf, *tmpname;
1760e979c658Sreinoud 	uint8_t *pathpos, *targetpos;
1761e979c658Sreinoud 	char *mntonname;
1762e979c658Sreinoud 	int pathlen, targetlen, namelen, mntonnamelen, len, l_ci;
1763e979c658Sreinoud 	int first, error;
1764cff5e7adSreinoud 
1765e979c658Sreinoud 	pathbuf   = malloc(UDF_SYMLINKBUFLEN, M_UDFTEMP, M_WAITOK);
1766e979c658Sreinoud 	tmpname   = malloc(PATH_MAX+1, M_UDFTEMP, M_WAITOK);
1767e979c658Sreinoud 	memset(pathbuf, 0, UDF_SYMLINKBUFLEN);
1768e979c658Sreinoud 	memset(targetbuf, 0, PATH_MAX);
1769e979c658Sreinoud 
1770e979c658Sreinoud 	/* read contents of file in our temporary buffer */
1771e979c658Sreinoud 	error = vn_rdwr(UIO_READ, udf_node->vnode,
1772e3b6b345Sreinoud 		pathbuf, filesize, 0,
1773e979c658Sreinoud 		UIO_SYSSPACE, IO_NODELOCKED | IO_ALTSEMANTICS,
1774e979c658Sreinoud 		FSCRED, NULL, NULL);
1775e979c658Sreinoud 	if (error) {
1776e979c658Sreinoud 		/* failed to read in symlink contents */
1777e979c658Sreinoud 		free(pathbuf, M_UDFTEMP);
1778e979c658Sreinoud 		free(tmpname, M_UDFTEMP);
1779e979c658Sreinoud 		return error;
1780e979c658Sreinoud 	}
1781e979c658Sreinoud 
1782e979c658Sreinoud 	/* convert to a unix path */
1783e979c658Sreinoud 	pathpos   = pathbuf;
1784e979c658Sreinoud 	pathlen   = 0;
1785e979c658Sreinoud 	targetpos = targetbuf;
1786e979c658Sreinoud 	targetlen = PATH_MAX;
1787e979c658Sreinoud 	mntonname    = udf_node->ump->vfs_mountp->mnt_stat.f_mntonname;
1788e979c658Sreinoud 	mntonnamelen = strlen(mntonname);
1789e979c658Sreinoud 
1790e979c658Sreinoud 	error = 0;
1791e979c658Sreinoud 	first = 1;
1792e3b6b345Sreinoud 	while (filesize - pathlen >= UDF_PATH_COMP_SIZE) {
1793e979c658Sreinoud 		len = UDF_PATH_COMP_SIZE;
1794e979c658Sreinoud 		memcpy(&pathcomp, pathpos, len);
1795e979c658Sreinoud 		l_ci = pathcomp.l_ci;
1796e979c658Sreinoud 		switch (pathcomp.type) {
1797e979c658Sreinoud 		case UDF_PATH_COMP_ROOT :
179806993e5fSreinoud 			/* XXX should check for l_ci; bugcompatible now */
179906993e5fSreinoud 			if ((targetlen < 1) || !first) {
1800e979c658Sreinoud 				error = EINVAL;
1801e979c658Sreinoud 				break;
1802e979c658Sreinoud 			}
1803e979c658Sreinoud 			*targetpos++ = '/'; targetlen--;
1804e979c658Sreinoud 			break;
1805e979c658Sreinoud 		case UDF_PATH_COMP_MOUNTROOT :
180606993e5fSreinoud 			/* XXX what should it be if l_ci > 0 ? [4/48.16.1.2] */
1807e979c658Sreinoud 			if (l_ci || (targetlen < mntonnamelen+1) || !first) {
1808e979c658Sreinoud 				error = EINVAL;
1809e979c658Sreinoud 				break;
1810e979c658Sreinoud 			}
1811e979c658Sreinoud 			memcpy(targetpos, mntonname, mntonnamelen);
1812e979c658Sreinoud 			targetpos += mntonnamelen; targetlen -= mntonnamelen;
1813e3b6b345Sreinoud 			if (filesize-pathlen > UDF_PATH_COMP_SIZE+l_ci) {
1814e979c658Sreinoud 				/* more follows, so must be directory */
1815e979c658Sreinoud 				*targetpos++ = '/'; targetlen--;
1816e979c658Sreinoud 			}
1817e979c658Sreinoud 			break;
1818e979c658Sreinoud 		case UDF_PATH_COMP_PARENTDIR :
181906993e5fSreinoud 			/* XXX should check for l_ci; bugcompatible now */
182006993e5fSreinoud 			if (targetlen < 3) {
1821e979c658Sreinoud 				error = EINVAL;
1822e979c658Sreinoud 				break;
1823e979c658Sreinoud 			}
1824e979c658Sreinoud 			*targetpos++ = '.'; targetlen--;
1825e979c658Sreinoud 			*targetpos++ = '.'; targetlen--;
1826e979c658Sreinoud 			*targetpos++ = '/'; targetlen--;
1827e979c658Sreinoud 			break;
1828e979c658Sreinoud 		case UDF_PATH_COMP_CURDIR :
182906993e5fSreinoud 			/* XXX should check for l_ci; bugcompatible now */
183006993e5fSreinoud 			if (targetlen < 2) {
1831e979c658Sreinoud 				error = EINVAL;
1832e979c658Sreinoud 				break;
1833e979c658Sreinoud 			}
1834e979c658Sreinoud 			*targetpos++ = '.'; targetlen--;
1835e979c658Sreinoud 			*targetpos++ = '/'; targetlen--;
1836e979c658Sreinoud 			break;
1837e979c658Sreinoud 		case UDF_PATH_COMP_NAME :
1838dc6314f8Sreinoud 			if (l_ci == 0) {
1839dc6314f8Sreinoud 				error = EINVAL;
1840dc6314f8Sreinoud 				break;
1841dc6314f8Sreinoud 			}
1842e979c658Sreinoud 			memset(tmpname, 0, PATH_MAX);
1843e979c658Sreinoud 			memcpy(&pathcomp, pathpos, len + l_ci);
18448902baa6Sreinoud 			udf_to_unix_name(tmpname, MAXPATHLEN,
18458902baa6Sreinoud 				pathcomp.ident, l_ci,
1846e979c658Sreinoud 				&udf_node->ump->logical_vol->desc_charset);
1847e979c658Sreinoud 			namelen = strlen(tmpname);
1848e979c658Sreinoud 			if (targetlen < namelen + 1) {
1849e979c658Sreinoud 				error = EINVAL;
1850e979c658Sreinoud 				break;
1851e979c658Sreinoud 			}
1852e979c658Sreinoud 			memcpy(targetpos, tmpname, namelen);
1853e979c658Sreinoud 			targetpos += namelen; targetlen -= namelen;
1854e3b6b345Sreinoud 			if (filesize-pathlen > UDF_PATH_COMP_SIZE+l_ci) {
1855e979c658Sreinoud 				/* more follows, so must be directory */
1856e979c658Sreinoud 				*targetpos++ = '/'; targetlen--;
1857e979c658Sreinoud 			}
1858e979c658Sreinoud 			break;
1859e979c658Sreinoud 		default :
1860e979c658Sreinoud 			error = EINVAL;
1861e979c658Sreinoud 			break;
1862e979c658Sreinoud 		}
1863e979c658Sreinoud 		first = 0;
1864e979c658Sreinoud 		if (error)
1865e979c658Sreinoud 			break;
1866e979c658Sreinoud 		pathpos += UDF_PATH_COMP_SIZE + l_ci;
1867e979c658Sreinoud 		pathlen += UDF_PATH_COMP_SIZE + l_ci;
1868e979c658Sreinoud 
1869e979c658Sreinoud 	}
1870e979c658Sreinoud 	/* all processed? */
1871e3b6b345Sreinoud 	if (filesize - pathlen > 0)
1872e979c658Sreinoud 		error = EINVAL;
1873e979c658Sreinoud 
1874e3b6b345Sreinoud 	free(pathbuf, M_UDFTEMP);
1875e3b6b345Sreinoud 	free(tmpname, M_UDFTEMP);
1876e3b6b345Sreinoud 
1877e3b6b345Sreinoud 	*length = PATH_MAX - targetlen;
1878e3b6b345Sreinoud 	return error;
1879e3b6b345Sreinoud }
1880e3b6b345Sreinoud 
1881e3b6b345Sreinoud 
1882e3b6b345Sreinoud int
udf_readlink(void * v)1883e3b6b345Sreinoud udf_readlink(void *v)
1884e3b6b345Sreinoud {
1885e3b6b345Sreinoud 	struct vop_readlink_args /* {
1886e3b6b345Sreinoud 		struct vnode *a_vp;
1887e3b6b345Sreinoud 		struct uio *a_uio;
1888e3b6b345Sreinoud 		kauth_cred_t a_cred;
1889e3b6b345Sreinoud 	} */ *ap = v;
1890e3b6b345Sreinoud 	struct vnode *vp = ap->a_vp;
1891e3b6b345Sreinoud 	struct udf_node *udf_node = VTOI(vp);
1892e3b6b345Sreinoud 	struct file_entry    *fe  = udf_node->fe;
1893e3b6b345Sreinoud 	struct extfile_entry *efe = udf_node->efe;
1894e3b6b345Sreinoud 	struct uio *uio = ap->a_uio;
1895e3b6b345Sreinoud 	uint64_t filesize;
1896e3b6b345Sreinoud 	uint8_t *targetbuf;
1897e3b6b345Sreinoud 	int length;
1898e3b6b345Sreinoud 	int error;
1899e3b6b345Sreinoud 
1900e3b6b345Sreinoud 	DPRINTF(CALL, ("udf_readlink called\n"));
1901e3b6b345Sreinoud 
1902e3b6b345Sreinoud 	if (fe) {
1903e3b6b345Sreinoud 		filesize = udf_rw64(fe->inf_len);
1904e3b6b345Sreinoud 	} else {
1905e3b6b345Sreinoud 		assert(udf_node->efe);
1906e3b6b345Sreinoud 		filesize = udf_rw64(efe->inf_len);
1907e3b6b345Sreinoud 	}
1908e3b6b345Sreinoud 
1909e3b6b345Sreinoud 	/* claim temporary buffers for translation */
1910e3b6b345Sreinoud 	targetbuf = malloc(PATH_MAX+1, M_UDFTEMP, M_WAITOK);
1911e3b6b345Sreinoud 
1912e3b6b345Sreinoud 	error = udf_do_readlink(udf_node, filesize, targetbuf, &length);
1913e3b6b345Sreinoud 
1914e979c658Sreinoud 	/* uiomove() to destination */
1915e979c658Sreinoud 	if (!error)
1916e3b6b345Sreinoud 		uiomove(targetbuf, length, uio);
1917e979c658Sreinoud 
1918e979c658Sreinoud 	free(targetbuf, M_UDFTEMP);
1919e979c658Sreinoud 	return error;
1920cff5e7adSreinoud }
1921cff5e7adSreinoud 
1922cff5e7adSreinoud /* --------------------------------------------------------------------- */
1923cff5e7adSreinoud 
19245e29d122Sreinoud /*
1925a7795a09Sreinoud  * udf_rename() moved to udf_rename.c
19265e29d122Sreinoud  */
19275e29d122Sreinoud 
1928cff5e7adSreinoud /* --------------------------------------------------------------------- */
1929cff5e7adSreinoud 
1930cff5e7adSreinoud int
udf_remove(void * v)1931e979c658Sreinoud udf_remove(void *v)
1932cff5e7adSreinoud {
1933982ae832Sthorpej 	struct vop_remove_v3_args /* {
1934cff5e7adSreinoud 		struct vnode *a_dvp;
1935e979c658Sreinoud 		struct vnode *a_vp;
1936cff5e7adSreinoud 		struct componentname *a_cnp;
1937982ae832Sthorpej 		nlink_t ctx_vp_new_nlink;
1938cff5e7adSreinoud 	} */ *ap = v;
1939cff5e7adSreinoud 	struct vnode *dvp = ap->a_dvp;
1940e979c658Sreinoud 	struct vnode *vp  = ap->a_vp;
1941cff5e7adSreinoud 	struct componentname *cnp = ap->a_cnp;
1942982581e0Smbalmer 	struct udf_node *dir_node = VTOI(dvp);
1943e979c658Sreinoud 	struct udf_node *udf_node = VTOI(vp);
1944e979c658Sreinoud 	struct udf_mount *ump = dir_node->ump;
1945e979c658Sreinoud 	int error;
1946cff5e7adSreinoud 
1947e979c658Sreinoud 	DPRINTF(CALL, ("udf_remove called\n"));
1948e979c658Sreinoud 	if (vp->v_type != VDIR) {
1949e979c658Sreinoud 		error = udf_dir_detach(ump, dir_node, udf_node, cnp);
1950e979c658Sreinoud 		DPRINTFIF(NODE, error, ("\tgot error removing file\n"));
1951982ae832Sthorpej 		if (error == 0) {
1952982ae832Sthorpej 			if (udf_node->fe) {
1953982ae832Sthorpej 				ap->ctx_vp_new_nlink =
1954982ae832Sthorpej 				    udf_rw16(udf_node->fe->link_cnt);
1955982ae832Sthorpej 			} else {
1956982ae832Sthorpej 				KASSERT(udf_node->efe != NULL);
1957982ae832Sthorpej 				ap->ctx_vp_new_nlink =
1958982ae832Sthorpej 				    udf_rw16(udf_node->efe->link_cnt);
1959982ae832Sthorpej 			}
1960982ae832Sthorpej 		}
1961e979c658Sreinoud 	} else {
1962e979c658Sreinoud 		DPRINTF(NODE, ("\tis a directory: perm. denied\n"));
1963e979c658Sreinoud 		error = EPERM;
1964e979c658Sreinoud 	}
1965cff5e7adSreinoud 
1966e979c658Sreinoud 	if (dvp == vp)
1967e979c658Sreinoud 		vrele(vp);
1968e979c658Sreinoud 	else
1969e979c658Sreinoud 		vput(vp);
1970cff5e7adSreinoud 
1971e979c658Sreinoud 	return error;
1972cff5e7adSreinoud }
1973cff5e7adSreinoud 
1974cff5e7adSreinoud /* --------------------------------------------------------------------- */
1975cff5e7adSreinoud 
1976cff5e7adSreinoud int
udf_rmdir(void * v)1977cff5e7adSreinoud udf_rmdir(void *v)
1978cff5e7adSreinoud {
19796fa7b158Sriastradh 	struct vop_rmdir_v2_args /* {
1980cff5e7adSreinoud 		struct vnode *a_dvp;
1981cff5e7adSreinoud 		struct vnode *a_vp;
1982cff5e7adSreinoud 		struct componentname *a_cnp;
1983cff5e7adSreinoud 	} */ *ap = v;
1984cff5e7adSreinoud 	struct vnode *vp = ap->a_vp;
1985cff5e7adSreinoud 	struct vnode *dvp = ap->a_dvp;
1986cff5e7adSreinoud 	struct componentname *cnp = ap->a_cnp;
1987982581e0Smbalmer 	struct udf_node *dir_node = VTOI(dvp);
1988e979c658Sreinoud 	struct udf_node *udf_node = VTOI(vp);
1989e979c658Sreinoud 	struct udf_mount *ump = dir_node->ump;
1990eb513d51Sreinoud 	int error, isempty;
1991cff5e7adSreinoud 
1992b3745214Sreinoud 	DPRINTF(CALL, ("udf_rmdir '%s' called\n", cnp->cn_nameptr));
1993cff5e7adSreinoud 
1994e979c658Sreinoud 	/* don't allow '.' to be deleted */
1995e979c658Sreinoud 	if (dir_node == udf_node) {
19966fa7b158Sriastradh 		vrele(vp);
1997e979c658Sreinoud 		return EINVAL;
1998e979c658Sreinoud 	}
1999e979c658Sreinoud 
2000eb513d51Sreinoud 	/* make sure our `leaf' node's hash is populated */
2001eb513d51Sreinoud 	dirhash_get(&udf_node->dir_hash);
2002eb513d51Sreinoud 	error = udf_dirhash_fill(udf_node);
2003eb513d51Sreinoud 	if (error) {
2004eb513d51Sreinoud 		dirhash_put(udf_node->dir_hash);
2005eb513d51Sreinoud 		return error;
2006e979c658Sreinoud 	}
2007eb513d51Sreinoud 
2008eb513d51Sreinoud 	/* check to see if the directory is empty */
2009eb513d51Sreinoud 	isempty = dirhash_dir_isempty(udf_node->dir_hash);
2010eb513d51Sreinoud 	dirhash_put(udf_node->dir_hash);
2011eb513d51Sreinoud 
2012eb513d51Sreinoud 	if (!isempty) {
2013cff5e7adSreinoud 		vput(vp);
2014e979c658Sreinoud 		return ENOTEMPTY;
2015cff5e7adSreinoud 	}
2016cff5e7adSreinoud 
2017ab09c24fSreinoud 	/* detach the node from the directory, udf_node is an empty dir here */
2018e979c658Sreinoud 	error = udf_dir_detach(ump, dir_node, udf_node, cnp);
2019e979c658Sreinoud 	if (error == 0) {
2020e979c658Sreinoud 		cache_purge(vp);
2021e979c658Sreinoud //		cache_purge(dvp);	/* XXX from msdosfs, why? */
2022ab09c24fSreinoud 		/*
2023ab09c24fSreinoud 		 * Bug alert: we need to remove '..' from the detaching
2024ab09c24fSreinoud 		 * udf_node so further lookups of this are not possible. This
2025ab09c24fSreinoud 		 * prevents a process in a deleted directory from going to its
202609fb1792Sandvar 		 * deleted parent. Since `udf_node' is guaranteed to be empty
2027ab09c24fSreinoud 		 * here, trunc it so no fids are there.
2028ab09c24fSreinoud 		 */
2029ab09c24fSreinoud 		dirhash_purge(&udf_node->dir_hash);
2030ab09c24fSreinoud 		udf_shrink_node(udf_node, 0);
2031e979c658Sreinoud 	}
2032e6b5374eSreinoud 	DPRINTFIF(NODE, error, ("\tgot error removing dir\n"));
2033e979c658Sreinoud 
20346fa7b158Sriastradh 	/* put the node and exit */
2035e979c658Sreinoud 	vput(vp);
2036e979c658Sreinoud 
2037cff5e7adSreinoud 	return error;
2038cff5e7adSreinoud }
2039cff5e7adSreinoud 
2040cff5e7adSreinoud /* --------------------------------------------------------------------- */
2041cff5e7adSreinoud 
2042cff5e7adSreinoud int
udf_fsync(void * v)2043cff5e7adSreinoud udf_fsync(void *v)
2044cff5e7adSreinoud {
2045cff5e7adSreinoud 	struct vop_fsync_args /* {
2046cff5e7adSreinoud 		struct vnode *a_vp;
2047fc9422c9Selad 		kauth_cred_t a_cred;
2048cff5e7adSreinoud 		int a_flags;
2049cff5e7adSreinoud 		off_t offlo;
2050cff5e7adSreinoud 		off_t offhi;
2051cff5e7adSreinoud 		struct proc *a_p;
2052cff5e7adSreinoud 	} */ *ap = v;
2053cff5e7adSreinoud 	struct vnode *vp = ap->a_vp;
2054e979c658Sreinoud 	struct udf_node *udf_node = VTOI(vp);
2055e979c658Sreinoud 	int error, flags, wait;
2056cff5e7adSreinoud 
205742dbf4bbSreinoud 	DPRINTF(SYNC, ("udf_fsync called on %p : %s, %s\n",
205842dbf4bbSreinoud 		udf_node,
2059e979c658Sreinoud 		(ap->a_flags & FSYNC_WAIT)     ? "wait":"no wait",
2060e979c658Sreinoud 		(ap->a_flags & FSYNC_DATAONLY) ? "data_only":"complete"));
2061cff5e7adSreinoud 
2062e979c658Sreinoud 	/* flush data and wait for it when requested */
2063e979c658Sreinoud 	wait = (ap->a_flags & FSYNC_WAIT) ? UPDATE_WAIT : 0;
20648306a9edSchs 	error = vflushbuf(vp, ap->a_flags);
206587522af4Shannken 	if (error)
206687522af4Shannken 		return error;
2067e979c658Sreinoud 
20684d5c88faSreinoud 	if (udf_node == NULL) {
20694d5c88faSreinoud 		printf("udf_fsync() called on NULL udf_node!\n");
20704d5c88faSreinoud 		return 0;
20714d5c88faSreinoud 	}
20724d5c88faSreinoud 	if (vp->v_tag != VT_UDF) {
20734d5c88faSreinoud 		printf("udf_fsync() called on node not tagged as UDF node!\n");
20744d5c88faSreinoud 		return 0;
20754d5c88faSreinoud 	}
20764d5c88faSreinoud 
2077e979c658Sreinoud 	/* set our times */
2078e979c658Sreinoud 	udf_itimes(udf_node, NULL, NULL, NULL);
2079e979c658Sreinoud 
2080e979c658Sreinoud 	/* if called when mounted readonly, never write back */
2081e979c658Sreinoud 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
2082e979c658Sreinoud 		return 0;
2083e979c658Sreinoud 
2084e979c658Sreinoud 	/* if only data is requested, return */
2085e979c658Sreinoud 	if (ap->a_flags & FSYNC_DATAONLY)
2086e979c658Sreinoud 		return 0;
2087e979c658Sreinoud 
2088e979c658Sreinoud 	/* check if the node is dirty 'enough'*/
2089e979c658Sreinoud 	flags = udf_node->i_flags & (IN_MODIFIED | IN_ACCESSED);
2090e979c658Sreinoud 	if (flags == 0)
2091e979c658Sreinoud 		return 0;
2092e979c658Sreinoud 
2093e979c658Sreinoud 	/* if we don't have to wait, check for IO pending */
2094e979c658Sreinoud 	if (!wait) {
2095e979c658Sreinoud 		if (vp->v_numoutput > 0) {
209642dbf4bbSreinoud 			DPRINTF(SYNC, ("udf_fsync %p, rejecting on v_numoutput\n", udf_node));
2097e979c658Sreinoud 			return 0;
2098e979c658Sreinoud 		}
2099e979c658Sreinoud 		if (udf_node->outstanding_bufs > 0) {
210042dbf4bbSreinoud 			DPRINTF(SYNC, ("udf_fsync %p, rejecting on outstanding_bufs\n", udf_node));
2101e979c658Sreinoud 			return 0;
2102e979c658Sreinoud 		}
2103e979c658Sreinoud 		if (udf_node->outstanding_nodedscr > 0) {
210442dbf4bbSreinoud 			DPRINTF(SYNC, ("udf_fsync %p, rejecting on outstanding_nodedscr\n", udf_node));
2105e979c658Sreinoud 			return 0;
2106e979c658Sreinoud 		}
2107e979c658Sreinoud 	}
2108e979c658Sreinoud 
2109e979c658Sreinoud 	/* wait until vp->v_numoutput reaches zero i.e. is finished */
2110e979c658Sreinoud 	if (wait) {
211142dbf4bbSreinoud 		DPRINTF(SYNC, ("udf_fsync %p, waiting\n", udf_node));
2112e225b7bdSrmind 		mutex_enter(vp->v_interlock);
2113e979c658Sreinoud 		while (vp->v_numoutput) {
211442dbf4bbSreinoud 			DPRINTF(SYNC, ("udf_fsync %p, v_numoutput %d\n", udf_node, vp->v_numoutput));
2115e225b7bdSrmind 			cv_timedwait(&vp->v_cv, vp->v_interlock, hz/8);
2116e979c658Sreinoud 		}
2117e225b7bdSrmind 		mutex_exit(vp->v_interlock);
211842dbf4bbSreinoud 		DPRINTF(SYNC, ("udf_fsync %p, fin wait\n", udf_node));
2119e979c658Sreinoud 	}
2120e979c658Sreinoud 
2121e979c658Sreinoud 	/* write out node and wait for it if requested */
212242dbf4bbSreinoud 	DPRINTF(SYNC, ("udf_fsync %p, writeout node\n", udf_node));
2123e979c658Sreinoud 	error = udf_writeout_node(udf_node, wait);
2124e979c658Sreinoud 	if (error)
2125e979c658Sreinoud 		return error;
2126e979c658Sreinoud 
2127e979c658Sreinoud 	/* TODO/XXX if ap->a_flags & FSYNC_CACHE, we ought to do a disc sync */
2128cff5e7adSreinoud 
2129cff5e7adSreinoud 	return 0;
2130cff5e7adSreinoud }
2131cff5e7adSreinoud 
2132cff5e7adSreinoud /* --------------------------------------------------------------------- */
2133cff5e7adSreinoud 
2134cff5e7adSreinoud int
udf_advlock(void * v)2135cff5e7adSreinoud udf_advlock(void *v)
2136cff5e7adSreinoud {
2137cff5e7adSreinoud 	struct vop_advlock_args /* {
2138cff5e7adSreinoud 		struct vnode *a_vp;
2139cff5e7adSreinoud 		void *a_id;
2140cff5e7adSreinoud 		int a_op;
2141cff5e7adSreinoud 		struct flock *a_fl;
2142cff5e7adSreinoud 		int a_flags;
2143cff5e7adSreinoud 	} */ *ap = v;
2144cff5e7adSreinoud 	struct vnode *vp = ap->a_vp;
2145cff5e7adSreinoud 	struct udf_node *udf_node = VTOI(vp);
2146cff5e7adSreinoud 	struct file_entry    *fe;
2147cff5e7adSreinoud 	struct extfile_entry *efe;
2148cff5e7adSreinoud 	uint64_t file_size;
2149cff5e7adSreinoud 
2150cff5e7adSreinoud 	DPRINTF(LOCKING, ("udf_advlock called\n"));
2151cff5e7adSreinoud 
2152cff5e7adSreinoud 	/* get directory filesize */
2153cff5e7adSreinoud 	if (udf_node->fe) {
2154cff5e7adSreinoud 		fe = udf_node->fe;
2155cff5e7adSreinoud 		file_size = udf_rw64(fe->inf_len);
2156cff5e7adSreinoud 	} else {
2157cff5e7adSreinoud 		assert(udf_node->efe);
2158cff5e7adSreinoud 		efe = udf_node->efe;
2159cff5e7adSreinoud 		file_size = udf_rw64(efe->inf_len);
21600c1391f0Schristos 	}
2161cff5e7adSreinoud 
2162cff5e7adSreinoud 	return lf_advlock(ap, &udf_node->lockf, file_size);
2163cff5e7adSreinoud }
2164cff5e7adSreinoud 
2165cff5e7adSreinoud /* --------------------------------------------------------------------- */
2166cff5e7adSreinoud 
2167cff5e7adSreinoud /* Global vfs vnode data structures for udfs */
216802cdf4d2Sdsl int (**udf_vnodeop_p)(void *);
2169cff5e7adSreinoud 
2170cff5e7adSreinoud const struct vnodeopv_entry_desc udf_vnodeop_entries[] = {
2171cff5e7adSreinoud 	{ &vop_default_desc, vn_default_error },
2172c6c16cd0Sdholland 	{ &vop_parsepath_desc, genfs_parsepath }, /* parsepath */
2173cff5e7adSreinoud 	{ &vop_lookup_desc, udf_lookup },	/* lookup */
2174e979c658Sreinoud 	{ &vop_create_desc, udf_create },	/* create */
2175cff5e7adSreinoud 	{ &vop_mknod_desc, udf_mknod },		/* mknod */	/* TODO */
2176cff5e7adSreinoud 	{ &vop_open_desc, udf_open },		/* open */
2177cff5e7adSreinoud 	{ &vop_close_desc, udf_close },		/* close */
2178cff5e7adSreinoud 	{ &vop_access_desc, udf_access },	/* access */
21799aa2a9c3Schristos 	{ &vop_accessx_desc, genfs_accessx },	/* accessx */
2180cff5e7adSreinoud 	{ &vop_getattr_desc, udf_getattr },	/* getattr */
2181e979c658Sreinoud 	{ &vop_setattr_desc, udf_setattr },	/* setattr */	/* TODO chflags */
2182cff5e7adSreinoud 	{ &vop_read_desc, udf_read },		/* read */
2183cff5e7adSreinoud 	{ &vop_write_desc, udf_write },		/* write */	/* WRITE */
218405d075b3Sdholland 	{ &vop_fallocate_desc, genfs_eopnotsupp }, /* fallocate */
218505d075b3Sdholland 	{ &vop_fdiscard_desc, genfs_eopnotsupp }, /* fdiscard */
2186cff5e7adSreinoud 	{ &vop_fcntl_desc, genfs_fcntl },	/* fcntl */	/* TODO? */
2187cff5e7adSreinoud 	{ &vop_ioctl_desc, genfs_enoioctl },	/* ioctl */	/* TODO? */
2188cff5e7adSreinoud 	{ &vop_poll_desc, genfs_poll },		/* poll */	/* TODO/OK? */
2189cff5e7adSreinoud 	{ &vop_kqfilter_desc, genfs_kqfilter },	/* kqfilter */	/* ? */
2190cff5e7adSreinoud 	{ &vop_revoke_desc, genfs_revoke },	/* revoke */	/* TODO? */
2191cff5e7adSreinoud 	{ &vop_mmap_desc, genfs_mmap },		/* mmap */	/* OK? */
2192e979c658Sreinoud 	{ &vop_fsync_desc, udf_fsync },		/* fsync */
2193cff5e7adSreinoud 	{ &vop_seek_desc, genfs_seek },		/* seek */
2194e979c658Sreinoud 	{ &vop_remove_desc, udf_remove },	/* remove */
2195cff5e7adSreinoud 	{ &vop_link_desc, udf_link },		/* link */	/* TODO */
2196cff5e7adSreinoud 	{ &vop_rename_desc, udf_rename },	/* rename */ 	/* TODO */
2197e979c658Sreinoud 	{ &vop_mkdir_desc, udf_mkdir },		/* mkdir */
2198e979c658Sreinoud 	{ &vop_rmdir_desc, udf_rmdir },		/* rmdir */
2199cff5e7adSreinoud 	{ &vop_symlink_desc, udf_symlink },	/* symlink */	/* TODO */
2200cff5e7adSreinoud 	{ &vop_readdir_desc, udf_readdir },	/* readdir */
2201cff5e7adSreinoud 	{ &vop_readlink_desc, udf_readlink },	/* readlink */	/* TEST ME */
2202cff5e7adSreinoud 	{ &vop_abortop_desc, genfs_abortop },	/* abortop */	/* TODO/OK? */
2203cff5e7adSreinoud 	{ &vop_inactive_desc, udf_inactive },	/* inactive */
2204cff5e7adSreinoud 	{ &vop_reclaim_desc, udf_reclaim },	/* reclaim */
2205cff5e7adSreinoud 	{ &vop_lock_desc, genfs_lock },		/* lock */
2206cff5e7adSreinoud 	{ &vop_unlock_desc, genfs_unlock },	/* unlock */
2207cff5e7adSreinoud 	{ &vop_bmap_desc, udf_trivial_bmap },	/* bmap */	/* 1:1 bmap */
2208e979c658Sreinoud 	{ &vop_strategy_desc, udf_vfsstrategy },/* strategy */
2209cff5e7adSreinoud /*	{ &vop_print_desc, udf_print },	*/	/* print */
2210cff5e7adSreinoud 	{ &vop_islocked_desc, genfs_islocked },	/* islocked */
2211cff5e7adSreinoud 	{ &vop_pathconf_desc, udf_pathconf },	/* pathconf */
2212cff5e7adSreinoud 	{ &vop_advlock_desc, udf_advlock },	/* advlock */	/* TEST ME */
2213cff5e7adSreinoud 	{ &vop_bwrite_desc, vn_bwrite },	/* bwrite */	/* ->strategy */
2214cff5e7adSreinoud 	{ &vop_getpages_desc, genfs_getpages },	/* getpages */
2215cff5e7adSreinoud 	{ &vop_putpages_desc, genfs_putpages },	/* putpages */
2216cff5e7adSreinoud 	{ NULL, NULL }
2217cff5e7adSreinoud };
2218cff5e7adSreinoud 
2219cff5e7adSreinoud 
2220cff5e7adSreinoud const struct vnodeopv_desc udf_vnodeop_opv_desc = {
2221cff5e7adSreinoud 	&udf_vnodeop_p, udf_vnodeop_entries
2222cff5e7adSreinoud };
2223