Deleted Added
full compact
ext2_vnops.c (68307) ext2_vnops.c (76167)
1/*
2 * modified for EXT2FS support in Lites 1.1
3 *
4 * Aug 1995, Godmar Back (gback@cs.utah.edu)
5 * University of Utah, Department of Computer Science
6 */
7/*
8 * Copyright (c) 1982, 1986, 1989, 1993
9 * The Regents of the University of California. All rights reserved.
10 * (c) UNIX System Laboratories, Inc.
11 * All or some portions of this file are derived from material licensed
12 * to the University of California by American Telephone and Telegraph
13 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
14 * the permission of UNIX System Laboratories, Inc.
15 *
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
18 * are met:
19 * 1. Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
24 * 3. All advertising materials mentioning features or use of this software
25 * must display the following acknowledgement:
26 * This product includes software developed by the University of
27 * California, Berkeley and its contributors.
28 * 4. Neither the name of the University nor the names of its contributors
29 * may be used to endorse or promote products derived from this software
30 * without specific prior written permission.
31 *
32 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 * SUCH DAMAGE.
43 *
44 * @(#)ufs_vnops.c 8.27 (Berkeley) 5/27/95
45 * @(#)ext2_vnops.c 8.7 (Berkeley) 2/3/94
1/*
2 * modified for EXT2FS support in Lites 1.1
3 *
4 * Aug 1995, Godmar Back (gback@cs.utah.edu)
5 * University of Utah, Department of Computer Science
6 */
7/*
8 * Copyright (c) 1982, 1986, 1989, 1993
9 * The Regents of the University of California. All rights reserved.
10 * (c) UNIX System Laboratories, Inc.
11 * All or some portions of this file are derived from material licensed
12 * to the University of California by American Telephone and Telegraph
13 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
14 * the permission of UNIX System Laboratories, Inc.
15 *
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
18 * are met:
19 * 1. Redistributions of source code must retain the above copyright
20 * notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 * notice, this list of conditions and the following disclaimer in the
23 * documentation and/or other materials provided with the distribution.
24 * 3. All advertising materials mentioning features or use of this software
25 * must display the following acknowledgement:
26 * This product includes software developed by the University of
27 * California, Berkeley and its contributors.
28 * 4. Neither the name of the University nor the names of its contributors
29 * may be used to endorse or promote products derived from this software
30 * without specific prior written permission.
31 *
32 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 * SUCH DAMAGE.
43 *
44 * @(#)ufs_vnops.c 8.27 (Berkeley) 5/27/95
45 * @(#)ext2_vnops.c 8.7 (Berkeley) 2/3/94
46 * $FreeBSD: head/sys/gnu/fs/ext2fs/ext2_vnops.c 68307 2000-11-04 08:10:56Z bde $
46 * $FreeBSD: head/sys/gnu/fs/ext2fs/ext2_vnops.c 76167 2001-05-01 08:34:45Z phk $
47 */
48
49#include "opt_quota.h"
50#include "opt_suiddir.h"
51
52#include <sys/param.h>
53#include <sys/systm.h>
54#include <sys/resourcevar.h>
55#include <sys/kernel.h>
56#include <sys/stat.h>
57#include <sys/bio.h>
58#include <sys/buf.h>
59#include <sys/proc.h>
60#include <sys/mount.h>
61#include <sys/time.h>
62#include <sys/vnode.h>
63#include <sys/namei.h>
64
65#include <vm/vm.h>
66#include <vm/vm_extern.h>
67#include <vm/vnode_pager.h>
68
69#include <sys/signalvar.h>
70#include <ufs/ufs/dir.h>
71#include <ufs/ufs/extattr.h>
72#include <ufs/ufs/quota.h>
73#include <ufs/ufs/inode.h>
74#include <ufs/ufs/ufsmount.h>
75#include <ufs/ufs/ufs_extern.h>
76
77#include <gnu/ext2fs/ext2_fs_sb.h>
78#include <gnu/ext2fs/fs.h>
79#include <gnu/ext2fs/ext2_extern.h>
80#include <gnu/ext2fs/ext2_fs.h>
81
82static int ext2_makeinode __P((int mode, struct vnode *, struct vnode **, struct componentname *));
83
84static int ext2_fsync __P((struct vop_fsync_args *));
85static int ext2_read __P((struct vop_read_args *));
86static int ext2_write __P((struct vop_write_args *));
87static int ext2_remove __P((struct vop_remove_args *));
88static int ext2_link __P((struct vop_link_args *));
89static int ext2_rename __P((struct vop_rename_args *));
90static int ext2_mkdir __P((struct vop_mkdir_args *));
91static int ext2_rmdir __P((struct vop_rmdir_args *));
92static int ext2_create __P((struct vop_create_args *));
93static int ext2_mknod __P((struct vop_mknod_args *));
94static int ext2_symlink __P((struct vop_symlink_args *));
47 */
48
49#include "opt_quota.h"
50#include "opt_suiddir.h"
51
52#include <sys/param.h>
53#include <sys/systm.h>
54#include <sys/resourcevar.h>
55#include <sys/kernel.h>
56#include <sys/stat.h>
57#include <sys/bio.h>
58#include <sys/buf.h>
59#include <sys/proc.h>
60#include <sys/mount.h>
61#include <sys/time.h>
62#include <sys/vnode.h>
63#include <sys/namei.h>
64
65#include <vm/vm.h>
66#include <vm/vm_extern.h>
67#include <vm/vnode_pager.h>
68
69#include <sys/signalvar.h>
70#include <ufs/ufs/dir.h>
71#include <ufs/ufs/extattr.h>
72#include <ufs/ufs/quota.h>
73#include <ufs/ufs/inode.h>
74#include <ufs/ufs/ufsmount.h>
75#include <ufs/ufs/ufs_extern.h>
76
77#include <gnu/ext2fs/ext2_fs_sb.h>
78#include <gnu/ext2fs/fs.h>
79#include <gnu/ext2fs/ext2_extern.h>
80#include <gnu/ext2fs/ext2_fs.h>
81
82static int ext2_makeinode __P((int mode, struct vnode *, struct vnode **, struct componentname *));
83
84static int ext2_fsync __P((struct vop_fsync_args *));
85static int ext2_read __P((struct vop_read_args *));
86static int ext2_write __P((struct vop_write_args *));
87static int ext2_remove __P((struct vop_remove_args *));
88static int ext2_link __P((struct vop_link_args *));
89static int ext2_rename __P((struct vop_rename_args *));
90static int ext2_mkdir __P((struct vop_mkdir_args *));
91static int ext2_rmdir __P((struct vop_rmdir_args *));
92static int ext2_create __P((struct vop_create_args *));
93static int ext2_mknod __P((struct vop_mknod_args *));
94static int ext2_symlink __P((struct vop_symlink_args *));
95static int ext2_getpages __P((struct vop_getpages_args *));
96static int ext2_putpages __P((struct vop_putpages_args *));
97
98/* Global vfs data structures for ufs. */
99vop_t **ext2_vnodeop_p;
100static struct vnodeopv_entry_desc ext2_vnodeop_entries[] = {
101 { &vop_default_desc, (vop_t *) ufs_vnoperate },
102 { &vop_cachedlookup_desc, (vop_t *) ext2_lookup },
103 { &vop_fsync_desc, (vop_t *) ext2_fsync },
104 { &vop_inactive_desc, (vop_t *) ext2_inactive },
105 { &vop_lookup_desc, (vop_t *) vfs_cache_lookup },
106 { &vop_read_desc, (vop_t *) ext2_read },
107 { &vop_readdir_desc, (vop_t *) ext2_readdir },
108 { &vop_reallocblks_desc, (vop_t *) ext2_reallocblks },
109 { &vop_write_desc, (vop_t *) ext2_write },
110 { &vop_remove_desc, (vop_t *) ext2_remove },
111 { &vop_link_desc, (vop_t *) ext2_link },
112 { &vop_rename_desc, (vop_t *) ext2_rename },
113 { &vop_mkdir_desc, (vop_t *) ext2_mkdir },
114 { &vop_rmdir_desc, (vop_t *) ext2_rmdir },
115 { &vop_create_desc, (vop_t *) ext2_create },
116 { &vop_mknod_desc, (vop_t *) ext2_mknod },
117 { &vop_symlink_desc, (vop_t *) ext2_symlink },
95
96/* Global vfs data structures for ufs. */
97vop_t **ext2_vnodeop_p;
98static struct vnodeopv_entry_desc ext2_vnodeop_entries[] = {
99 { &vop_default_desc, (vop_t *) ufs_vnoperate },
100 { &vop_cachedlookup_desc, (vop_t *) ext2_lookup },
101 { &vop_fsync_desc, (vop_t *) ext2_fsync },
102 { &vop_inactive_desc, (vop_t *) ext2_inactive },
103 { &vop_lookup_desc, (vop_t *) vfs_cache_lookup },
104 { &vop_read_desc, (vop_t *) ext2_read },
105 { &vop_readdir_desc, (vop_t *) ext2_readdir },
106 { &vop_reallocblks_desc, (vop_t *) ext2_reallocblks },
107 { &vop_write_desc, (vop_t *) ext2_write },
108 { &vop_remove_desc, (vop_t *) ext2_remove },
109 { &vop_link_desc, (vop_t *) ext2_link },
110 { &vop_rename_desc, (vop_t *) ext2_rename },
111 { &vop_mkdir_desc, (vop_t *) ext2_mkdir },
112 { &vop_rmdir_desc, (vop_t *) ext2_rmdir },
113 { &vop_create_desc, (vop_t *) ext2_create },
114 { &vop_mknod_desc, (vop_t *) ext2_mknod },
115 { &vop_symlink_desc, (vop_t *) ext2_symlink },
118 { &vop_getpages_desc, (vop_t *) ext2_getpages },
119 { &vop_putpages_desc, (vop_t *) ext2_putpages },
120 { NULL, NULL }
121};
122static struct vnodeopv_desc ext2fs_vnodeop_opv_desc =
123 { &ext2_vnodeop_p, ext2_vnodeop_entries };
124
125vop_t **ext2_specop_p;
126static struct vnodeopv_entry_desc ext2_specop_entries[] = {
127 { &vop_default_desc, (vop_t *) ufs_vnoperatespec },
128 { &vop_fsync_desc, (vop_t *) ext2_fsync },
129 { &vop_inactive_desc, (vop_t *) ext2_inactive },
130 { NULL, NULL }
131};
132static struct vnodeopv_desc ext2fs_specop_opv_desc =
133 { &ext2_specop_p, ext2_specop_entries };
134
135vop_t **ext2_fifoop_p;
136static struct vnodeopv_entry_desc ext2_fifoop_entries[] = {
137 { &vop_default_desc, (vop_t *) ufs_vnoperatefifo },
138 { &vop_fsync_desc, (vop_t *) ext2_fsync },
139 { &vop_inactive_desc, (vop_t *) ext2_inactive },
140 { NULL, NULL }
141};
142static struct vnodeopv_desc ext2fs_fifoop_opv_desc =
143 { &ext2_fifoop_p, ext2_fifoop_entries };
144
145 VNODEOP_SET(ext2fs_vnodeop_opv_desc);
146 VNODEOP_SET(ext2fs_specop_opv_desc);
147 VNODEOP_SET(ext2fs_fifoop_opv_desc);
148
149#include <gnu/ext2fs/ext2_readwrite.c>
150
151/*
152 * A virgin directory (no blushing please).
153 * Note that the type and namlen fields are reversed relative to ufs.
154 * Also, we don't use `struct odirtemplate', since it would just cause
155 * endianness problems.
156 */
157static struct dirtemplate mastertemplate = {
158 0, 12, 1, EXT2_FT_DIR, ".",
159 0, DIRBLKSIZ - 12, 2, EXT2_FT_DIR, ".."
160};
161static struct dirtemplate omastertemplate = {
162 0, 12, 1, EXT2_FT_UNKNOWN, ".",
163 0, DIRBLKSIZ - 12, 2, EXT2_FT_UNKNOWN, ".."
164};
165
166/*
167 * Create a regular file
168 */
169static int
170ext2_create(ap)
171 struct vop_create_args /* {
172 struct vnode *a_dvp;
173 struct vnode **a_vpp;
174 struct componentname *a_cnp;
175 struct vattr *a_vap;
176 } */ *ap;
177{
178 int error;
179
180 error =
181 ext2_makeinode(MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode),
182 ap->a_dvp, ap->a_vpp, ap->a_cnp);
183 if (error)
184 return (error);
185 return (0);
186}
187
188/*
189 * Synch an open file.
190 */
191/* ARGSUSED */
192static int
193ext2_fsync(ap)
194 struct vop_fsync_args /* {
195 struct vnode *a_vp;
196 struct ucred *a_cred;
197 int a_waitfor;
198 struct proc *a_p;
199 } */ *ap;
200{
201 register struct vnode *vp = ap->a_vp;
202 register struct buf *bp;
203 struct buf *nbp;
204 int s;
205
206 /*
207 * XXX why is all this fs specific?
208 */
209
210 /*
211 * Flush all dirty buffers associated with a vnode.
212 */
213 ext2_discard_prealloc(VTOI(vp));
214
215loop:
216 s = splbio();
217 for (bp = TAILQ_FIRST(&vp->v_dirtyblkhd); bp; bp = nbp) {
218 nbp = TAILQ_NEXT(bp, b_vnbufs);
219 if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT))
220 continue;
221 if ((bp->b_flags & B_DELWRI) == 0)
222 panic("ext2_fsync: not dirty");
223 bremfree(bp);
224 splx(s);
225 /*
226 * Wait for I/O associated with indirect blocks to complete,
227 * since there is no way to quickly wait for them below.
228 */
229 if (bp->b_vp == vp || ap->a_waitfor == MNT_NOWAIT)
230 (void) bawrite(bp);
231 else
232 (void) bwrite(bp);
233 goto loop;
234 }
235 if (ap->a_waitfor == MNT_WAIT) {
236 while (vp->v_numoutput) {
237 vp->v_flag |= VBWAIT;
238 tsleep(&vp->v_numoutput, PRIBIO + 1, "e2fsyn", 0);
239 }
240#if DIAGNOSTIC
241 if (!TAILQ_EMPTY(&vp->v_dirtyblkhd)) {
242 vprint("ext2_fsync: dirty", vp);
243 goto loop;
244 }
245#endif
246 }
247 splx(s);
248 return (UFS_UPDATE(ap->a_vp, ap->a_waitfor == MNT_WAIT));
249}
250
251/*
252 * Mknod vnode call
253 */
254/* ARGSUSED */
255static int
256ext2_mknod(ap)
257 struct vop_mknod_args /* {
258 struct vnode *a_dvp;
259 struct vnode **a_vpp;
260 struct componentname *a_cnp;
261 struct vattr *a_vap;
262 } */ *ap;
263{
264 struct vattr *vap = ap->a_vap;
265 struct vnode **vpp = ap->a_vpp;
266 struct inode *ip;
267 ino_t ino;
268 int error;
269
270 error = ext2_makeinode(MAKEIMODE(vap->va_type, vap->va_mode),
271 ap->a_dvp, vpp, ap->a_cnp);
272 if (error)
273 return (error);
274 ip = VTOI(*vpp);
275 ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
276 if (vap->va_rdev != VNOVAL) {
277 /*
278 * Want to be able to use this to make badblock
279 * inodes, so don't truncate the dev number.
280 */
281 ip->i_rdev = vap->va_rdev;
282 }
283 /*
284 * Remove inode, then reload it through VFS_VGET so it is
285 * checked to see if it is an alias of an existing entry in
286 * the inode cache.
287 */
288 vput(*vpp);
289 (*vpp)->v_type = VNON;
290 ino = ip->i_number; /* Save this before vgone() invalidates ip. */
291 vgone(*vpp);
292 error = VFS_VGET(ap->a_dvp->v_mount, ino, vpp);
293 if (error) {
294 *vpp = NULL;
295 return (error);
296 }
297 return (0);
298}
299
300static int
301ext2_remove(ap)
302 struct vop_remove_args /* {
303 struct vnode *a_dvp;
304 struct vnode *a_vp;
305 struct componentname *a_cnp;
306 } */ *ap;
307{
308 struct inode *ip;
309 struct vnode *vp = ap->a_vp;
310 struct vnode *dvp = ap->a_dvp;
311 int error;
312
313 ip = VTOI(vp);
314 if ((ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
315 (VTOI(dvp)->i_flags & APPEND)) {
316 error = EPERM;
317 goto out;
318 }
319 error = ext2_dirremove(dvp, ap->a_cnp);
320 if (error == 0) {
321 ip->i_nlink--;
322 ip->i_flag |= IN_CHANGE;
323 }
324out:
325 return (error);
326}
327
328/*
329 * link vnode call
330 */
331static int
332ext2_link(ap)
333 struct vop_link_args /* {
334 struct vnode *a_tdvp;
335 struct vnode *a_vp;
336 struct componentname *a_cnp;
337 } */ *ap;
338{
339 struct vnode *vp = ap->a_vp;
340 struct vnode *tdvp = ap->a_tdvp;
341 struct componentname *cnp = ap->a_cnp;
342 struct proc *p = cnp->cn_proc;
343 struct inode *ip;
344 int error;
345
346#ifdef DIAGNOSTIC
347 if ((cnp->cn_flags & HASBUF) == 0)
348 panic("ufs_link: no name");
349#endif
350 if (tdvp->v_mount != vp->v_mount) {
351 error = EXDEV;
352 goto out2;
353 }
354 if (tdvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE, p))) {
355 goto out2;
356 }
357 ip = VTOI(vp);
358 if ((nlink_t)ip->i_nlink >= LINK_MAX) {
359 error = EMLINK;
360 goto out1;
361 }
362 if (ip->i_flags & (IMMUTABLE | APPEND)) {
363 error = EPERM;
364 goto out1;
365 }
366 ip->i_nlink++;
367 ip->i_flag |= IN_CHANGE;
368 error = UFS_UPDATE(vp, 1);
369 if (!error)
370 error = ext2_direnter(ip, tdvp, cnp);
371 if (error) {
372 ip->i_nlink--;
373 ip->i_flag |= IN_CHANGE;
374 }
375out1:
376 if (tdvp != vp)
377 VOP_UNLOCK(vp, 0, p);
378out2:
379 return (error);
380}
381
382/*
383 * Rename system call.
384 * See comments in sys/ufs/ufs/ufs_vnops.c
385 */
386static int
387ext2_rename(ap)
388 struct vop_rename_args /* {
389 struct vnode *a_fdvp;
390 struct vnode *a_fvp;
391 struct componentname *a_fcnp;
392 struct vnode *a_tdvp;
393 struct vnode *a_tvp;
394 struct componentname *a_tcnp;
395 } */ *ap;
396{
397 struct vnode *tvp = ap->a_tvp;
398 register struct vnode *tdvp = ap->a_tdvp;
399 struct vnode *fvp = ap->a_fvp;
400 struct vnode *fdvp = ap->a_fdvp;
401 struct componentname *tcnp = ap->a_tcnp;
402 struct componentname *fcnp = ap->a_fcnp;
403 struct proc *p = fcnp->cn_proc;
404 struct inode *ip, *xp, *dp;
405 struct dirtemplate dirbuf;
406 int doingdirectory = 0, oldparent = 0, newparent = 0;
407 int error = 0;
408 u_char namlen;
409
410#ifdef DIAGNOSTIC
411 if ((tcnp->cn_flags & HASBUF) == 0 ||
412 (fcnp->cn_flags & HASBUF) == 0)
413 panic("ufs_rename: no name");
414#endif
415 /*
416 * Check for cross-device rename.
417 */
418 if ((fvp->v_mount != tdvp->v_mount) ||
419 (tvp && (fvp->v_mount != tvp->v_mount))) {
420 error = EXDEV;
421abortit:
422 if (tdvp == tvp)
423 vrele(tdvp);
424 else
425 vput(tdvp);
426 if (tvp)
427 vput(tvp);
428 vrele(fdvp);
429 vrele(fvp);
430 return (error);
431 }
432
433 if (tvp && ((VTOI(tvp)->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
434 (VTOI(tdvp)->i_flags & APPEND))) {
435 error = EPERM;
436 goto abortit;
437 }
438
439 /*
440 * Check if just deleting a link name or if we've lost a race.
441 * If another process completes the same rename after we've looked
442 * up the source and have blocked looking up the target, then the
443 * source and target inodes may be identical now although the
444 * names were never linked.
445 */
446 if (fvp == tvp) {
447 if (fvp->v_type == VDIR) {
448 /*
449 * Linked directories are impossible, so we must
450 * have lost the race. Pretend that the rename
451 * completed before the lookup.
452 */
453#ifdef UFS_RENAME_DEBUG
454 printf("ufs_rename: fvp == tvp for directories\n");
455#endif
456 error = ENOENT;
457 goto abortit;
458 }
459
460 /* Release destination completely. */
461 vput(tdvp);
462 vput(tvp);
463
464 /*
465 * Delete source. There is another race now that everything
466 * is unlocked, but this doesn't cause any new complications.
467 * Relookup() may find a file that is unrelated to the
468 * original one, or it may fail. Too bad.
469 */
470 vrele(fdvp);
471 vrele(fvp);
472 fcnp->cn_flags &= ~MODMASK;
473 fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
474 fcnp->cn_nameiop = DELETE;
475 VREF(fdvp);
476 error = relookup(fdvp, &fvp, fcnp);
477 if (error == 0)
478 vrele(fdvp);
479 if (fvp == NULL) {
480#ifdef UFS_RENAME_DEBUG
481 printf("ufs_rename: from name disappeared\n");
482#endif
483 return (ENOENT);
484 }
485 error = VOP_REMOVE(fdvp, fvp, fcnp);
486 if (fdvp == fvp)
487 vrele(fdvp);
488 else
489 vput(fdvp);
490 if (fvp != NULLVP)
491 vput(fvp);
492 return (error);
493 }
494 if ((error = vn_lock(fvp, LK_EXCLUSIVE, p)) != 0)
495 goto abortit;
496 dp = VTOI(fdvp);
497 ip = VTOI(fvp);
498 if (ip->i_nlink >= LINK_MAX) {
499 VOP_UNLOCK(fvp, 0, p);
500 error = EMLINK;
501 goto abortit;
502 }
503 if ((ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND))
504 || (dp->i_flags & APPEND)) {
505 VOP_UNLOCK(fvp, 0, p);
506 error = EPERM;
507 goto abortit;
508 }
509 if ((ip->i_mode & IFMT) == IFDIR) {
510 /*
511 * Avoid ".", "..", and aliases of "." for obvious reasons.
512 */
513 if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
514 dp == ip || (fcnp->cn_flags | tcnp->cn_flags) & ISDOTDOT ||
515 (ip->i_flag & IN_RENAME)) {
516 VOP_UNLOCK(fvp, 0, p);
517 error = EINVAL;
518 goto abortit;
519 }
520 ip->i_flag |= IN_RENAME;
521 oldparent = dp->i_number;
522 doingdirectory++;
523 }
524 vrele(fdvp);
525
526 /*
527 * When the target exists, both the directory
528 * and target vnodes are returned locked.
529 */
530 dp = VTOI(tdvp);
531 xp = NULL;
532 if (tvp)
533 xp = VTOI(tvp);
534
535 /*
536 * 1) Bump link count while we're moving stuff
537 * around. If we crash somewhere before
538 * completing our work, the link count
539 * may be wrong, but correctable.
540 */
541 ip->i_nlink++;
542 ip->i_flag |= IN_CHANGE;
543 if ((error = UFS_UPDATE(fvp, 1)) != 0) {
544 VOP_UNLOCK(fvp, 0, p);
545 goto bad;
546 }
547
548 /*
549 * If ".." must be changed (ie the directory gets a new
550 * parent) then the source directory must not be in the
551 * directory heirarchy above the target, as this would
552 * orphan everything below the source directory. Also
553 * the user must have write permission in the source so
554 * as to be able to change "..". We must repeat the call
555 * to namei, as the parent directory is unlocked by the
556 * call to checkpath().
557 */
558 error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
559 VOP_UNLOCK(fvp, 0, p);
560 if (oldparent != dp->i_number)
561 newparent = dp->i_number;
562 if (doingdirectory && newparent) {
563 if (error) /* write access check above */
564 goto bad;
565 if (xp != NULL)
566 vput(tvp);
567 error = ext2_checkpath(ip, dp, tcnp->cn_cred);
568 if (error)
569 goto out;
570 VREF(tdvp);
571 error = relookup(tdvp, &tvp, tcnp);
572 if (error)
573 goto out;
574 vrele(tdvp);
575 dp = VTOI(tdvp);
576 xp = NULL;
577 if (tvp)
578 xp = VTOI(tvp);
579 }
580 /*
581 * 2) If target doesn't exist, link the target
582 * to the source and unlink the source.
583 * Otherwise, rewrite the target directory
584 * entry to reference the source inode and
585 * expunge the original entry's existence.
586 */
587 if (xp == NULL) {
588 if (dp->i_dev != ip->i_dev)
589 panic("ufs_rename: EXDEV");
590 /*
591 * Account for ".." in new directory.
592 * When source and destination have the same
593 * parent we don't fool with the link count.
594 */
595 if (doingdirectory && newparent) {
596 if ((nlink_t)dp->i_nlink >= LINK_MAX) {
597 error = EMLINK;
598 goto bad;
599 }
600 dp->i_nlink++;
601 dp->i_flag |= IN_CHANGE;
602 error = UFS_UPDATE(tdvp, 1);
603 if (error)
604 goto bad;
605 }
606 error = ext2_direnter(ip, tdvp, tcnp);
607 if (error) {
608 if (doingdirectory && newparent) {
609 dp->i_nlink--;
610 dp->i_flag |= IN_CHANGE;
611 (void)UFS_UPDATE(tdvp, 1);
612 }
613 goto bad;
614 }
615 vput(tdvp);
616 } else {
617 if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev)
618 panic("ufs_rename: EXDEV");
619 /*
620 * Short circuit rename(foo, foo).
621 */
622 if (xp->i_number == ip->i_number)
623 panic("ufs_rename: same file");
624 /*
625 * If the parent directory is "sticky", then the user must
626 * own the parent directory, or the destination of the rename,
627 * otherwise the destination may not be changed (except by
628 * root). This implements append-only directories.
629 */
630 if ((dp->i_mode & S_ISTXT) && tcnp->cn_cred->cr_uid != 0 &&
631 tcnp->cn_cred->cr_uid != dp->i_uid &&
632 xp->i_uid != tcnp->cn_cred->cr_uid) {
633 error = EPERM;
634 goto bad;
635 }
636 /*
637 * Target must be empty if a directory and have no links
638 * to it. Also, ensure source and target are compatible
639 * (both directories, or both not directories).
640 */
641 if ((xp->i_mode&IFMT) == IFDIR) {
642 if (! ext2_dirempty(xp, dp->i_number, tcnp->cn_cred) ||
643 xp->i_nlink > 2) {
644 error = ENOTEMPTY;
645 goto bad;
646 }
647 if (!doingdirectory) {
648 error = ENOTDIR;
649 goto bad;
650 }
651 cache_purge(tdvp);
652 } else if (doingdirectory) {
653 error = EISDIR;
654 goto bad;
655 }
656 error = ext2_dirrewrite(dp, ip, tcnp);
657 if (error)
658 goto bad;
659 /*
660 * If the target directory is in the same
661 * directory as the source directory,
662 * decrement the link count on the parent
663 * of the target directory.
664 */
665 if (doingdirectory && !newparent) {
666 dp->i_nlink--;
667 dp->i_flag |= IN_CHANGE;
668 }
669 vput(tdvp);
670 /*
671 * Adjust the link count of the target to
672 * reflect the dirrewrite above. If this is
673 * a directory it is empty and there are
674 * no links to it, so we can squash the inode and
675 * any space associated with it. We disallowed
676 * renaming over top of a directory with links to
677 * it above, as the remaining link would point to
678 * a directory without "." or ".." entries.
679 */
680 xp->i_nlink--;
681 if (doingdirectory) {
682 if (--xp->i_nlink != 0)
683 panic("ufs_rename: linked directory");
684 error = UFS_TRUNCATE(tvp, (off_t)0, IO_SYNC,
685 tcnp->cn_cred, tcnp->cn_proc);
686 }
687 xp->i_flag |= IN_CHANGE;
688 vput(tvp);
689 xp = NULL;
690 }
691
692 /*
693 * 3) Unlink the source.
694 */
695 fcnp->cn_flags &= ~MODMASK;
696 fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
697 VREF(fdvp);
698 error = relookup(fdvp, &fvp, fcnp);
699 if (error == 0)
700 vrele(fdvp);
701 if (fvp != NULL) {
702 xp = VTOI(fvp);
703 dp = VTOI(fdvp);
704 } else {
705 /*
706 * From name has disappeared.
707 */
708 if (doingdirectory)
709 panic("ufs_rename: lost dir entry");
710 vrele(ap->a_fvp);
711 return (0);
712 }
713 /*
714 * Ensure that the directory entry still exists and has not
715 * changed while the new name has been entered. If the source is
716 * a file then the entry may have been unlinked or renamed. In
717 * either case there is no further work to be done. If the source
718 * is a directory then it cannot have been rmdir'ed; its link
719 * count of three would cause a rmdir to fail with ENOTEMPTY.
720 * The IN_RENAME flag ensures that it cannot be moved by another
721 * rename.
722 */
723 if (xp != ip) {
724 if (doingdirectory)
725 panic("ufs_rename: lost dir entry");
726 } else {
727 /*
728 * If the source is a directory with a
729 * new parent, the link count of the old
730 * parent directory must be decremented
731 * and ".." set to point to the new parent.
732 */
733 if (doingdirectory && newparent) {
734 dp->i_nlink--;
735 dp->i_flag |= IN_CHANGE;
736 error = vn_rdwr(UIO_READ, fvp, (caddr_t)&dirbuf,
737 sizeof (struct dirtemplate), (off_t)0,
738 UIO_SYSSPACE, IO_NODELOCKED,
739 tcnp->cn_cred, (int *)0, (struct proc *)0);
740 if (error == 0) {
741 /* Like ufs little-endian: */
742 namlen = dirbuf.dotdot_type;
743 if (namlen != 2 ||
744 dirbuf.dotdot_name[0] != '.' ||
745 dirbuf.dotdot_name[1] != '.') {
746 ufs_dirbad(xp, (doff_t)12,
747 "rename: mangled dir");
748 } else {
749 dirbuf.dotdot_ino = newparent;
750 (void) vn_rdwr(UIO_WRITE, fvp,
751 (caddr_t)&dirbuf,
752 sizeof (struct dirtemplate),
753 (off_t)0, UIO_SYSSPACE,
754 IO_NODELOCKED|IO_SYNC,
755 tcnp->cn_cred, (int *)0,
756 (struct proc *)0);
757 cache_purge(fdvp);
758 }
759 }
760 }
761 error = ext2_dirremove(fdvp, fcnp);
762 if (!error) {
763 xp->i_nlink--;
764 xp->i_flag |= IN_CHANGE;
765 }
766 xp->i_flag &= ~IN_RENAME;
767 }
768 if (dp)
769 vput(fdvp);
770 if (xp)
771 vput(fvp);
772 vrele(ap->a_fvp);
773 return (error);
774
775bad:
776 if (xp)
777 vput(ITOV(xp));
778 vput(ITOV(dp));
779out:
780 if (doingdirectory)
781 ip->i_flag &= ~IN_RENAME;
782 if (vn_lock(fvp, LK_EXCLUSIVE, p) == 0) {
783 ip->i_nlink--;
784 ip->i_flag |= IN_CHANGE;
785 ip->i_flag &= ~IN_RENAME;
786 vput(fvp);
787 } else
788 vrele(fvp);
789 return (error);
790}
791
792/*
793 * Mkdir system call
794 */
795static int
796ext2_mkdir(ap)
797 struct vop_mkdir_args /* {
798 struct vnode *a_dvp;
799 struct vnode **a_vpp;
800 struct componentname *a_cnp;
801 struct vattr *a_vap;
802 } */ *ap;
803{
804 register struct vnode *dvp = ap->a_dvp;
805 register struct vattr *vap = ap->a_vap;
806 register struct componentname *cnp = ap->a_cnp;
807 register struct inode *ip, *dp;
808 struct vnode *tvp;
809 struct dirtemplate dirtemplate, *dtp;
810 int error, dmode;
811
812#ifdef DIAGNOSTIC
813 if ((cnp->cn_flags & HASBUF) == 0)
814 panic("ufs_mkdir: no name");
815#endif
816 dp = VTOI(dvp);
817 if ((nlink_t)dp->i_nlink >= LINK_MAX) {
818 error = EMLINK;
819 goto out;
820 }
821 dmode = vap->va_mode & 0777;
822 dmode |= IFDIR;
823 /*
824 * Must simulate part of ext2_makeinode here to acquire the inode,
825 * but not have it entered in the parent directory. The entry is
826 * made later after writing "." and ".." entries.
827 */
828 error = UFS_VALLOC(dvp, dmode, cnp->cn_cred, &tvp);
829 if (error)
830 goto out;
831 ip = VTOI(tvp);
832 ip->i_gid = dp->i_gid;
833#ifdef SUIDDIR
834 {
835#ifdef QUOTA
836 struct ucred ucred, *ucp;
837 ucp = cnp->cn_cred;
838#endif I
839 /*
840 * if we are hacking owners here, (only do this where told to)
841 * and we are not giving it TOO root, (would subvert quotas)
842 * then go ahead and give it to the other user.
843 * The new directory also inherits the SUID bit.
844 * If user's UID and dir UID are the same,
845 * 'give it away' so that the SUID is still forced on.
846 */
847 if ( (dvp->v_mount->mnt_flag & MNT_SUIDDIR) &&
848 (dp->i_mode & ISUID) && dp->i_uid) {
849 dmode |= ISUID;
850 ip->i_uid = dp->i_uid;
851#ifdef QUOTA
852 if (dp->i_uid != cnp->cn_cred->cr_uid) {
853 /*
854 * make sure the correct user gets charged
855 * for the space.
856 * Make a dummy credential for the victim.
857 * XXX This seems to never be accessed out of
858 * our context so a stack variable is ok.
859 */
860 ucred.cr_ref = 1;
861 ucred.cr_uid = ip->i_uid;
862 ucred.cr_ngroups = 1;
863 ucred.cr_groups[0] = dp->i_gid;
864 ucp = &ucred;
865 }
866#endif I
867 } else {
868 ip->i_uid = cnp->cn_cred->cr_uid;
869 }
870#ifdef QUOTA
871 if ((error = getinoquota(ip)) ||
872 (error = chkiq(ip, 1, ucp, 0))) {
873 UFS_VFREE(tvp, ip->i_number, dmode);
874 vput(tvp);
875 return (error);
876 }
877#endif
878 }
879#else
880 ip->i_uid = cnp->cn_cred->cr_uid;
881#ifdef QUOTA
882 if ((error = getinoquota(ip)) ||
883 (error = chkiq(ip, 1, cnp->cn_cred, 0))) {
884 UFS_VFREE(tvp, ip->i_number, dmode);
885 vput(tvp);
886 return (error);
887 }
888#endif
889#endif
890 ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
891 ip->i_mode = dmode;
892 tvp->v_type = VDIR; /* Rest init'd in getnewvnode(). */
893 ip->i_nlink = 2;
894 if (cnp->cn_flags & ISWHITEOUT)
895 ip->i_flags |= UF_OPAQUE;
896 error = UFS_UPDATE(tvp, 1);
897
898 /*
899 * Bump link count in parent directory
900 * to reflect work done below. Should
901 * be done before reference is created
902 * so reparation is possible if we crash.
903 */
904 dp->i_nlink++;
905 dp->i_flag |= IN_CHANGE;
906 error = UFS_UPDATE(dvp, 1);
907 if (error)
908 goto bad;
909
910 /* Initialize directory with "." and ".." from static template. */
911 if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs->s_es,
912 EXT2_FEATURE_INCOMPAT_FILETYPE))
913 dtp = &mastertemplate;
914 else
915 dtp = &omastertemplate;
916 dirtemplate = *dtp;
917 dirtemplate.dot_ino = ip->i_number;
918 dirtemplate.dotdot_ino = dp->i_number;
919 /* note that in ext2 DIRBLKSIZ == blocksize, not DEV_BSIZE
920 * so let's just redefine it - for this function only
921 */
922#undef DIRBLKSIZ
923#define DIRBLKSIZ VTOI(dvp)->i_e2fs->s_blocksize
924 dirtemplate.dotdot_reclen = DIRBLKSIZ - 12;
925 error = vn_rdwr(UIO_WRITE, tvp, (caddr_t)&dirtemplate,
926 sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE,
927 IO_NODELOCKED|IO_SYNC, cnp->cn_cred, (int *)0, (struct proc *)0);
928 if (error) {
929 dp->i_nlink--;
930 dp->i_flag |= IN_CHANGE;
931 goto bad;
932 }
933 if (DIRBLKSIZ > VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
934 panic("ufs_mkdir: blksize"); /* XXX should grow with balloc() */
935 else {
936 ip->i_size = DIRBLKSIZ;
937 ip->i_flag |= IN_CHANGE;
938 }
939
940 /* Directory set up, now install its entry in the parent directory. */
941 error = ext2_direnter(ip, dvp, cnp);
942 if (error) {
943 dp->i_nlink--;
944 dp->i_flag |= IN_CHANGE;
945 }
946bad:
947 /*
948 * No need to do an explicit VOP_TRUNCATE here, vrele will do this
949 * for us because we set the link count to 0.
950 */
951 if (error) {
952 ip->i_nlink = 0;
953 ip->i_flag |= IN_CHANGE;
954 vput(tvp);
955 } else
956 *ap->a_vpp = tvp;
957out:
958 return (error);
959#undef DIRBLKSIZ
960#define DIRBLKSIZ DEV_BSIZE
961}
962
963/*
964 * Rmdir system call.
965 */
966static int
967ext2_rmdir(ap)
968 struct vop_rmdir_args /* {
969 struct vnode *a_dvp;
970 struct vnode *a_vp;
971 struct componentname *a_cnp;
972 } */ *ap;
973{
974 struct vnode *vp = ap->a_vp;
975 struct vnode *dvp = ap->a_dvp;
976 struct componentname *cnp = ap->a_cnp;
977 struct proc *p = cnp->cn_proc;
978 struct inode *ip, *dp;
979 int error;
980
981 ip = VTOI(vp);
982 dp = VTOI(dvp);
983
984 /*
985 * Verify the directory is empty (and valid).
986 * (Rmdir ".." won't be valid since
987 * ".." will contain a reference to
988 * the current directory and thus be
989 * non-empty.)
990 */
991 error = 0;
992 if (ip->i_nlink != 2 || !ext2_dirempty(ip, dp->i_number, cnp->cn_cred)) {
993 error = ENOTEMPTY;
994 goto out;
995 }
996 if ((dp->i_flags & APPEND)
997 || (ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND))) {
998 error = EPERM;
999 goto out;
1000 }
1001 /*
1002 * Delete reference to directory before purging
1003 * inode. If we crash in between, the directory
1004 * will be reattached to lost+found,
1005 */
1006 error = ext2_dirremove(dvp, cnp);
1007 if (error)
1008 goto out;
1009 dp->i_nlink--;
1010 dp->i_flag |= IN_CHANGE;
1011 cache_purge(dvp);
1012 VOP_UNLOCK(dvp, 0, p);
1013 /*
1014 * Truncate inode. The only stuff left
1015 * in the directory is "." and "..". The
1016 * "." reference is inconsequential since
1017 * we're quashing it. The ".." reference
1018 * has already been adjusted above. We've
1019 * removed the "." reference and the reference
1020 * in the parent directory, but there may be
1021 * other hard links so decrement by 2 and
1022 * worry about them later.
1023 */
1024 ip->i_nlink -= 2;
1025 error = UFS_TRUNCATE(vp, (off_t)0, IO_SYNC, cnp->cn_cred, p);
1026 cache_purge(ITOV(ip));
1027 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p);
1028out:
1029 return (error);
1030}
1031
1032/*
1033 * symlink -- make a symbolic link
1034 */
1035static int
1036ext2_symlink(ap)
1037 struct vop_symlink_args /* {
1038 struct vnode *a_dvp;
1039 struct vnode **a_vpp;
1040 struct componentname *a_cnp;
1041 struct vattr *a_vap;
1042 char *a_target;
1043 } */ *ap;
1044{
1045 register struct vnode *vp, **vpp = ap->a_vpp;
1046 register struct inode *ip;
1047 int len, error;
1048
1049 error = ext2_makeinode(IFLNK | ap->a_vap->va_mode, ap->a_dvp,
1050 vpp, ap->a_cnp);
1051 if (error)
1052 return (error);
1053 vp = *vpp;
1054 len = strlen(ap->a_target);
1055 if (len < vp->v_mount->mnt_maxsymlinklen) {
1056 ip = VTOI(vp);
1057 bcopy(ap->a_target, (char *)ip->i_shortlink, len);
1058 ip->i_size = len;
1059 ip->i_flag |= IN_CHANGE | IN_UPDATE;
1060 } else
1061 error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0,
1062 UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, (int *)0,
1063 (struct proc *)0);
1064 if (error)
1065 vput(vp);
1066 return (error);
1067}
1068
1069/*
1070 * Allocate a new inode.
1071 */
1072static int
1073ext2_makeinode(mode, dvp, vpp, cnp)
1074 int mode;
1075 struct vnode *dvp;
1076 struct vnode **vpp;
1077 struct componentname *cnp;
1078{
1079 register struct inode *ip, *pdir;
1080 struct vnode *tvp;
1081 int error;
1082
1083 pdir = VTOI(dvp);
1084#ifdef DIAGNOSTIC
1085 if ((cnp->cn_flags & HASBUF) == 0)
1086 panic("ext2_makeinode: no name");
1087#endif
1088 *vpp = NULL;
1089 if ((mode & IFMT) == 0)
1090 mode |= IFREG;
1091
1092 error = UFS_VALLOC(dvp, mode, cnp->cn_cred, &tvp);
1093 if (error) {
1094 return (error);
1095 }
1096 ip = VTOI(tvp);
1097 ip->i_gid = pdir->i_gid;
1098#ifdef SUIDDIR
1099 {
1100#ifdef QUOTA
1101 struct ucred ucred, *ucp;
1102 ucp = cnp->cn_cred;
1103#endif I
1104 /*
1105 * if we are
1106 * not the owner of the directory,
1107 * and we are hacking owners here, (only do this where told to)
1108 * and we are not giving it TOO root, (would subvert quotas)
1109 * then go ahead and give it to the other user.
1110 * Note that this drops off the execute bits for security.
1111 */
1112 if ( (dvp->v_mount->mnt_flag & MNT_SUIDDIR) &&
1113 (pdir->i_mode & ISUID) &&
1114 (pdir->i_uid != cnp->cn_cred->cr_uid) && pdir->i_uid) {
1115 ip->i_uid = pdir->i_uid;
1116 mode &= ~07111;
1117#ifdef QUOTA
1118 /*
1119 * make sure the correct user gets charged
1120 * for the space.
1121 * Quickly knock up a dummy credential for the victim.
1122 * XXX This seems to never be accessed out of our
1123 * context so a stack variable is ok.
1124 */
1125 ucred.cr_ref = 1;
1126 ucred.cr_uid = ip->i_uid;
1127 ucred.cr_ngroups = 1;
1128 ucred.cr_groups[0] = pdir->i_gid;
1129 ucp = &ucred;
1130#endif I
1131 } else {
1132 ip->i_uid = cnp->cn_cred->cr_uid;
1133 }
1134
1135#ifdef QUOTA
1136 if ((error = getinoquota(ip)) ||
1137 (error = chkiq(ip, 1, ucp, 0))) {
1138 UFS_VFREE(tvp, ip->i_number, mode);
1139 vput(tvp);
1140 return (error);
1141 }
1142#endif
1143 }
1144#else
1145 ip->i_uid = cnp->cn_cred->cr_uid;
1146#ifdef QUOTA
1147 if ((error = getinoquota(ip)) ||
1148 (error = chkiq(ip, 1, cnp->cn_cred, 0))) {
1149 UFS_VFREE(tvp, ip->i_number, mode);
1150 vput(tvp);
1151 return (error);
1152 }
1153#endif
1154#endif
1155 ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
1156 ip->i_mode = mode;
1157 tvp->v_type = IFTOVT(mode); /* Rest init'd in getnewvnode(). */
1158 ip->i_nlink = 1;
1159 if ((ip->i_mode & ISGID) && !groupmember(ip->i_gid, cnp->cn_cred) &&
1160 suser_xxx(cnp->cn_cred, 0, PRISON_ROOT))
1161 ip->i_mode &= ~ISGID;
1162
1163 if (cnp->cn_flags & ISWHITEOUT)
1164 ip->i_flags |= UF_OPAQUE;
1165
1166 /*
1167 * Make sure inode goes to disk before directory entry.
1168 */
1169 error = UFS_UPDATE(tvp, 1);
1170 if (error)
1171 goto bad;
1172 error = ext2_direnter(ip, dvp, cnp);
1173 if (error)
1174 goto bad;
1175
1176 *vpp = tvp;
1177 return (0);
1178
1179bad:
1180 /*
1181 * Write error occurred trying to update the inode
1182 * or the directory so must deallocate the inode.
1183 */
1184 ip->i_nlink = 0;
1185 ip->i_flag |= IN_CHANGE;
1186 vput(tvp);
1187 return (error);
1188}
116 { NULL, NULL }
117};
118static struct vnodeopv_desc ext2fs_vnodeop_opv_desc =
119 { &ext2_vnodeop_p, ext2_vnodeop_entries };
120
121vop_t **ext2_specop_p;
122static struct vnodeopv_entry_desc ext2_specop_entries[] = {
123 { &vop_default_desc, (vop_t *) ufs_vnoperatespec },
124 { &vop_fsync_desc, (vop_t *) ext2_fsync },
125 { &vop_inactive_desc, (vop_t *) ext2_inactive },
126 { NULL, NULL }
127};
128static struct vnodeopv_desc ext2fs_specop_opv_desc =
129 { &ext2_specop_p, ext2_specop_entries };
130
131vop_t **ext2_fifoop_p;
132static struct vnodeopv_entry_desc ext2_fifoop_entries[] = {
133 { &vop_default_desc, (vop_t *) ufs_vnoperatefifo },
134 { &vop_fsync_desc, (vop_t *) ext2_fsync },
135 { &vop_inactive_desc, (vop_t *) ext2_inactive },
136 { NULL, NULL }
137};
138static struct vnodeopv_desc ext2fs_fifoop_opv_desc =
139 { &ext2_fifoop_p, ext2_fifoop_entries };
140
141 VNODEOP_SET(ext2fs_vnodeop_opv_desc);
142 VNODEOP_SET(ext2fs_specop_opv_desc);
143 VNODEOP_SET(ext2fs_fifoop_opv_desc);
144
145#include <gnu/ext2fs/ext2_readwrite.c>
146
147/*
148 * A virgin directory (no blushing please).
149 * Note that the type and namlen fields are reversed relative to ufs.
150 * Also, we don't use `struct odirtemplate', since it would just cause
151 * endianness problems.
152 */
153static struct dirtemplate mastertemplate = {
154 0, 12, 1, EXT2_FT_DIR, ".",
155 0, DIRBLKSIZ - 12, 2, EXT2_FT_DIR, ".."
156};
157static struct dirtemplate omastertemplate = {
158 0, 12, 1, EXT2_FT_UNKNOWN, ".",
159 0, DIRBLKSIZ - 12, 2, EXT2_FT_UNKNOWN, ".."
160};
161
162/*
163 * Create a regular file
164 */
165static int
166ext2_create(ap)
167 struct vop_create_args /* {
168 struct vnode *a_dvp;
169 struct vnode **a_vpp;
170 struct componentname *a_cnp;
171 struct vattr *a_vap;
172 } */ *ap;
173{
174 int error;
175
176 error =
177 ext2_makeinode(MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode),
178 ap->a_dvp, ap->a_vpp, ap->a_cnp);
179 if (error)
180 return (error);
181 return (0);
182}
183
184/*
185 * Synch an open file.
186 */
187/* ARGSUSED */
188static int
189ext2_fsync(ap)
190 struct vop_fsync_args /* {
191 struct vnode *a_vp;
192 struct ucred *a_cred;
193 int a_waitfor;
194 struct proc *a_p;
195 } */ *ap;
196{
197 register struct vnode *vp = ap->a_vp;
198 register struct buf *bp;
199 struct buf *nbp;
200 int s;
201
202 /*
203 * XXX why is all this fs specific?
204 */
205
206 /*
207 * Flush all dirty buffers associated with a vnode.
208 */
209 ext2_discard_prealloc(VTOI(vp));
210
211loop:
212 s = splbio();
213 for (bp = TAILQ_FIRST(&vp->v_dirtyblkhd); bp; bp = nbp) {
214 nbp = TAILQ_NEXT(bp, b_vnbufs);
215 if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT))
216 continue;
217 if ((bp->b_flags & B_DELWRI) == 0)
218 panic("ext2_fsync: not dirty");
219 bremfree(bp);
220 splx(s);
221 /*
222 * Wait for I/O associated with indirect blocks to complete,
223 * since there is no way to quickly wait for them below.
224 */
225 if (bp->b_vp == vp || ap->a_waitfor == MNT_NOWAIT)
226 (void) bawrite(bp);
227 else
228 (void) bwrite(bp);
229 goto loop;
230 }
231 if (ap->a_waitfor == MNT_WAIT) {
232 while (vp->v_numoutput) {
233 vp->v_flag |= VBWAIT;
234 tsleep(&vp->v_numoutput, PRIBIO + 1, "e2fsyn", 0);
235 }
236#if DIAGNOSTIC
237 if (!TAILQ_EMPTY(&vp->v_dirtyblkhd)) {
238 vprint("ext2_fsync: dirty", vp);
239 goto loop;
240 }
241#endif
242 }
243 splx(s);
244 return (UFS_UPDATE(ap->a_vp, ap->a_waitfor == MNT_WAIT));
245}
246
247/*
248 * Mknod vnode call
249 */
250/* ARGSUSED */
251static int
252ext2_mknod(ap)
253 struct vop_mknod_args /* {
254 struct vnode *a_dvp;
255 struct vnode **a_vpp;
256 struct componentname *a_cnp;
257 struct vattr *a_vap;
258 } */ *ap;
259{
260 struct vattr *vap = ap->a_vap;
261 struct vnode **vpp = ap->a_vpp;
262 struct inode *ip;
263 ino_t ino;
264 int error;
265
266 error = ext2_makeinode(MAKEIMODE(vap->va_type, vap->va_mode),
267 ap->a_dvp, vpp, ap->a_cnp);
268 if (error)
269 return (error);
270 ip = VTOI(*vpp);
271 ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
272 if (vap->va_rdev != VNOVAL) {
273 /*
274 * Want to be able to use this to make badblock
275 * inodes, so don't truncate the dev number.
276 */
277 ip->i_rdev = vap->va_rdev;
278 }
279 /*
280 * Remove inode, then reload it through VFS_VGET so it is
281 * checked to see if it is an alias of an existing entry in
282 * the inode cache.
283 */
284 vput(*vpp);
285 (*vpp)->v_type = VNON;
286 ino = ip->i_number; /* Save this before vgone() invalidates ip. */
287 vgone(*vpp);
288 error = VFS_VGET(ap->a_dvp->v_mount, ino, vpp);
289 if (error) {
290 *vpp = NULL;
291 return (error);
292 }
293 return (0);
294}
295
296static int
297ext2_remove(ap)
298 struct vop_remove_args /* {
299 struct vnode *a_dvp;
300 struct vnode *a_vp;
301 struct componentname *a_cnp;
302 } */ *ap;
303{
304 struct inode *ip;
305 struct vnode *vp = ap->a_vp;
306 struct vnode *dvp = ap->a_dvp;
307 int error;
308
309 ip = VTOI(vp);
310 if ((ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
311 (VTOI(dvp)->i_flags & APPEND)) {
312 error = EPERM;
313 goto out;
314 }
315 error = ext2_dirremove(dvp, ap->a_cnp);
316 if (error == 0) {
317 ip->i_nlink--;
318 ip->i_flag |= IN_CHANGE;
319 }
320out:
321 return (error);
322}
323
324/*
325 * link vnode call
326 */
327static int
328ext2_link(ap)
329 struct vop_link_args /* {
330 struct vnode *a_tdvp;
331 struct vnode *a_vp;
332 struct componentname *a_cnp;
333 } */ *ap;
334{
335 struct vnode *vp = ap->a_vp;
336 struct vnode *tdvp = ap->a_tdvp;
337 struct componentname *cnp = ap->a_cnp;
338 struct proc *p = cnp->cn_proc;
339 struct inode *ip;
340 int error;
341
342#ifdef DIAGNOSTIC
343 if ((cnp->cn_flags & HASBUF) == 0)
344 panic("ufs_link: no name");
345#endif
346 if (tdvp->v_mount != vp->v_mount) {
347 error = EXDEV;
348 goto out2;
349 }
350 if (tdvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE, p))) {
351 goto out2;
352 }
353 ip = VTOI(vp);
354 if ((nlink_t)ip->i_nlink >= LINK_MAX) {
355 error = EMLINK;
356 goto out1;
357 }
358 if (ip->i_flags & (IMMUTABLE | APPEND)) {
359 error = EPERM;
360 goto out1;
361 }
362 ip->i_nlink++;
363 ip->i_flag |= IN_CHANGE;
364 error = UFS_UPDATE(vp, 1);
365 if (!error)
366 error = ext2_direnter(ip, tdvp, cnp);
367 if (error) {
368 ip->i_nlink--;
369 ip->i_flag |= IN_CHANGE;
370 }
371out1:
372 if (tdvp != vp)
373 VOP_UNLOCK(vp, 0, p);
374out2:
375 return (error);
376}
377
378/*
379 * Rename system call.
380 * See comments in sys/ufs/ufs/ufs_vnops.c
381 */
382static int
383ext2_rename(ap)
384 struct vop_rename_args /* {
385 struct vnode *a_fdvp;
386 struct vnode *a_fvp;
387 struct componentname *a_fcnp;
388 struct vnode *a_tdvp;
389 struct vnode *a_tvp;
390 struct componentname *a_tcnp;
391 } */ *ap;
392{
393 struct vnode *tvp = ap->a_tvp;
394 register struct vnode *tdvp = ap->a_tdvp;
395 struct vnode *fvp = ap->a_fvp;
396 struct vnode *fdvp = ap->a_fdvp;
397 struct componentname *tcnp = ap->a_tcnp;
398 struct componentname *fcnp = ap->a_fcnp;
399 struct proc *p = fcnp->cn_proc;
400 struct inode *ip, *xp, *dp;
401 struct dirtemplate dirbuf;
402 int doingdirectory = 0, oldparent = 0, newparent = 0;
403 int error = 0;
404 u_char namlen;
405
406#ifdef DIAGNOSTIC
407 if ((tcnp->cn_flags & HASBUF) == 0 ||
408 (fcnp->cn_flags & HASBUF) == 0)
409 panic("ufs_rename: no name");
410#endif
411 /*
412 * Check for cross-device rename.
413 */
414 if ((fvp->v_mount != tdvp->v_mount) ||
415 (tvp && (fvp->v_mount != tvp->v_mount))) {
416 error = EXDEV;
417abortit:
418 if (tdvp == tvp)
419 vrele(tdvp);
420 else
421 vput(tdvp);
422 if (tvp)
423 vput(tvp);
424 vrele(fdvp);
425 vrele(fvp);
426 return (error);
427 }
428
429 if (tvp && ((VTOI(tvp)->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
430 (VTOI(tdvp)->i_flags & APPEND))) {
431 error = EPERM;
432 goto abortit;
433 }
434
435 /*
436 * Check if just deleting a link name or if we've lost a race.
437 * If another process completes the same rename after we've looked
438 * up the source and have blocked looking up the target, then the
439 * source and target inodes may be identical now although the
440 * names were never linked.
441 */
442 if (fvp == tvp) {
443 if (fvp->v_type == VDIR) {
444 /*
445 * Linked directories are impossible, so we must
446 * have lost the race. Pretend that the rename
447 * completed before the lookup.
448 */
449#ifdef UFS_RENAME_DEBUG
450 printf("ufs_rename: fvp == tvp for directories\n");
451#endif
452 error = ENOENT;
453 goto abortit;
454 }
455
456 /* Release destination completely. */
457 vput(tdvp);
458 vput(tvp);
459
460 /*
461 * Delete source. There is another race now that everything
462 * is unlocked, but this doesn't cause any new complications.
463 * Relookup() may find a file that is unrelated to the
464 * original one, or it may fail. Too bad.
465 */
466 vrele(fdvp);
467 vrele(fvp);
468 fcnp->cn_flags &= ~MODMASK;
469 fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
470 fcnp->cn_nameiop = DELETE;
471 VREF(fdvp);
472 error = relookup(fdvp, &fvp, fcnp);
473 if (error == 0)
474 vrele(fdvp);
475 if (fvp == NULL) {
476#ifdef UFS_RENAME_DEBUG
477 printf("ufs_rename: from name disappeared\n");
478#endif
479 return (ENOENT);
480 }
481 error = VOP_REMOVE(fdvp, fvp, fcnp);
482 if (fdvp == fvp)
483 vrele(fdvp);
484 else
485 vput(fdvp);
486 if (fvp != NULLVP)
487 vput(fvp);
488 return (error);
489 }
490 if ((error = vn_lock(fvp, LK_EXCLUSIVE, p)) != 0)
491 goto abortit;
492 dp = VTOI(fdvp);
493 ip = VTOI(fvp);
494 if (ip->i_nlink >= LINK_MAX) {
495 VOP_UNLOCK(fvp, 0, p);
496 error = EMLINK;
497 goto abortit;
498 }
499 if ((ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND))
500 || (dp->i_flags & APPEND)) {
501 VOP_UNLOCK(fvp, 0, p);
502 error = EPERM;
503 goto abortit;
504 }
505 if ((ip->i_mode & IFMT) == IFDIR) {
506 /*
507 * Avoid ".", "..", and aliases of "." for obvious reasons.
508 */
509 if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
510 dp == ip || (fcnp->cn_flags | tcnp->cn_flags) & ISDOTDOT ||
511 (ip->i_flag & IN_RENAME)) {
512 VOP_UNLOCK(fvp, 0, p);
513 error = EINVAL;
514 goto abortit;
515 }
516 ip->i_flag |= IN_RENAME;
517 oldparent = dp->i_number;
518 doingdirectory++;
519 }
520 vrele(fdvp);
521
522 /*
523 * When the target exists, both the directory
524 * and target vnodes are returned locked.
525 */
526 dp = VTOI(tdvp);
527 xp = NULL;
528 if (tvp)
529 xp = VTOI(tvp);
530
531 /*
532 * 1) Bump link count while we're moving stuff
533 * around. If we crash somewhere before
534 * completing our work, the link count
535 * may be wrong, but correctable.
536 */
537 ip->i_nlink++;
538 ip->i_flag |= IN_CHANGE;
539 if ((error = UFS_UPDATE(fvp, 1)) != 0) {
540 VOP_UNLOCK(fvp, 0, p);
541 goto bad;
542 }
543
544 /*
545 * If ".." must be changed (ie the directory gets a new
546 * parent) then the source directory must not be in the
547 * directory heirarchy above the target, as this would
548 * orphan everything below the source directory. Also
549 * the user must have write permission in the source so
550 * as to be able to change "..". We must repeat the call
551 * to namei, as the parent directory is unlocked by the
552 * call to checkpath().
553 */
554 error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
555 VOP_UNLOCK(fvp, 0, p);
556 if (oldparent != dp->i_number)
557 newparent = dp->i_number;
558 if (doingdirectory && newparent) {
559 if (error) /* write access check above */
560 goto bad;
561 if (xp != NULL)
562 vput(tvp);
563 error = ext2_checkpath(ip, dp, tcnp->cn_cred);
564 if (error)
565 goto out;
566 VREF(tdvp);
567 error = relookup(tdvp, &tvp, tcnp);
568 if (error)
569 goto out;
570 vrele(tdvp);
571 dp = VTOI(tdvp);
572 xp = NULL;
573 if (tvp)
574 xp = VTOI(tvp);
575 }
576 /*
577 * 2) If target doesn't exist, link the target
578 * to the source and unlink the source.
579 * Otherwise, rewrite the target directory
580 * entry to reference the source inode and
581 * expunge the original entry's existence.
582 */
583 if (xp == NULL) {
584 if (dp->i_dev != ip->i_dev)
585 panic("ufs_rename: EXDEV");
586 /*
587 * Account for ".." in new directory.
588 * When source and destination have the same
589 * parent we don't fool with the link count.
590 */
591 if (doingdirectory && newparent) {
592 if ((nlink_t)dp->i_nlink >= LINK_MAX) {
593 error = EMLINK;
594 goto bad;
595 }
596 dp->i_nlink++;
597 dp->i_flag |= IN_CHANGE;
598 error = UFS_UPDATE(tdvp, 1);
599 if (error)
600 goto bad;
601 }
602 error = ext2_direnter(ip, tdvp, tcnp);
603 if (error) {
604 if (doingdirectory && newparent) {
605 dp->i_nlink--;
606 dp->i_flag |= IN_CHANGE;
607 (void)UFS_UPDATE(tdvp, 1);
608 }
609 goto bad;
610 }
611 vput(tdvp);
612 } else {
613 if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev)
614 panic("ufs_rename: EXDEV");
615 /*
616 * Short circuit rename(foo, foo).
617 */
618 if (xp->i_number == ip->i_number)
619 panic("ufs_rename: same file");
620 /*
621 * If the parent directory is "sticky", then the user must
622 * own the parent directory, or the destination of the rename,
623 * otherwise the destination may not be changed (except by
624 * root). This implements append-only directories.
625 */
626 if ((dp->i_mode & S_ISTXT) && tcnp->cn_cred->cr_uid != 0 &&
627 tcnp->cn_cred->cr_uid != dp->i_uid &&
628 xp->i_uid != tcnp->cn_cred->cr_uid) {
629 error = EPERM;
630 goto bad;
631 }
632 /*
633 * Target must be empty if a directory and have no links
634 * to it. Also, ensure source and target are compatible
635 * (both directories, or both not directories).
636 */
637 if ((xp->i_mode&IFMT) == IFDIR) {
638 if (! ext2_dirempty(xp, dp->i_number, tcnp->cn_cred) ||
639 xp->i_nlink > 2) {
640 error = ENOTEMPTY;
641 goto bad;
642 }
643 if (!doingdirectory) {
644 error = ENOTDIR;
645 goto bad;
646 }
647 cache_purge(tdvp);
648 } else if (doingdirectory) {
649 error = EISDIR;
650 goto bad;
651 }
652 error = ext2_dirrewrite(dp, ip, tcnp);
653 if (error)
654 goto bad;
655 /*
656 * If the target directory is in the same
657 * directory as the source directory,
658 * decrement the link count on the parent
659 * of the target directory.
660 */
661 if (doingdirectory && !newparent) {
662 dp->i_nlink--;
663 dp->i_flag |= IN_CHANGE;
664 }
665 vput(tdvp);
666 /*
667 * Adjust the link count of the target to
668 * reflect the dirrewrite above. If this is
669 * a directory it is empty and there are
670 * no links to it, so we can squash the inode and
671 * any space associated with it. We disallowed
672 * renaming over top of a directory with links to
673 * it above, as the remaining link would point to
674 * a directory without "." or ".." entries.
675 */
676 xp->i_nlink--;
677 if (doingdirectory) {
678 if (--xp->i_nlink != 0)
679 panic("ufs_rename: linked directory");
680 error = UFS_TRUNCATE(tvp, (off_t)0, IO_SYNC,
681 tcnp->cn_cred, tcnp->cn_proc);
682 }
683 xp->i_flag |= IN_CHANGE;
684 vput(tvp);
685 xp = NULL;
686 }
687
688 /*
689 * 3) Unlink the source.
690 */
691 fcnp->cn_flags &= ~MODMASK;
692 fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
693 VREF(fdvp);
694 error = relookup(fdvp, &fvp, fcnp);
695 if (error == 0)
696 vrele(fdvp);
697 if (fvp != NULL) {
698 xp = VTOI(fvp);
699 dp = VTOI(fdvp);
700 } else {
701 /*
702 * From name has disappeared.
703 */
704 if (doingdirectory)
705 panic("ufs_rename: lost dir entry");
706 vrele(ap->a_fvp);
707 return (0);
708 }
709 /*
710 * Ensure that the directory entry still exists and has not
711 * changed while the new name has been entered. If the source is
712 * a file then the entry may have been unlinked or renamed. In
713 * either case there is no further work to be done. If the source
714 * is a directory then it cannot have been rmdir'ed; its link
715 * count of three would cause a rmdir to fail with ENOTEMPTY.
716 * The IN_RENAME flag ensures that it cannot be moved by another
717 * rename.
718 */
719 if (xp != ip) {
720 if (doingdirectory)
721 panic("ufs_rename: lost dir entry");
722 } else {
723 /*
724 * If the source is a directory with a
725 * new parent, the link count of the old
726 * parent directory must be decremented
727 * and ".." set to point to the new parent.
728 */
729 if (doingdirectory && newparent) {
730 dp->i_nlink--;
731 dp->i_flag |= IN_CHANGE;
732 error = vn_rdwr(UIO_READ, fvp, (caddr_t)&dirbuf,
733 sizeof (struct dirtemplate), (off_t)0,
734 UIO_SYSSPACE, IO_NODELOCKED,
735 tcnp->cn_cred, (int *)0, (struct proc *)0);
736 if (error == 0) {
737 /* Like ufs little-endian: */
738 namlen = dirbuf.dotdot_type;
739 if (namlen != 2 ||
740 dirbuf.dotdot_name[0] != '.' ||
741 dirbuf.dotdot_name[1] != '.') {
742 ufs_dirbad(xp, (doff_t)12,
743 "rename: mangled dir");
744 } else {
745 dirbuf.dotdot_ino = newparent;
746 (void) vn_rdwr(UIO_WRITE, fvp,
747 (caddr_t)&dirbuf,
748 sizeof (struct dirtemplate),
749 (off_t)0, UIO_SYSSPACE,
750 IO_NODELOCKED|IO_SYNC,
751 tcnp->cn_cred, (int *)0,
752 (struct proc *)0);
753 cache_purge(fdvp);
754 }
755 }
756 }
757 error = ext2_dirremove(fdvp, fcnp);
758 if (!error) {
759 xp->i_nlink--;
760 xp->i_flag |= IN_CHANGE;
761 }
762 xp->i_flag &= ~IN_RENAME;
763 }
764 if (dp)
765 vput(fdvp);
766 if (xp)
767 vput(fvp);
768 vrele(ap->a_fvp);
769 return (error);
770
771bad:
772 if (xp)
773 vput(ITOV(xp));
774 vput(ITOV(dp));
775out:
776 if (doingdirectory)
777 ip->i_flag &= ~IN_RENAME;
778 if (vn_lock(fvp, LK_EXCLUSIVE, p) == 0) {
779 ip->i_nlink--;
780 ip->i_flag |= IN_CHANGE;
781 ip->i_flag &= ~IN_RENAME;
782 vput(fvp);
783 } else
784 vrele(fvp);
785 return (error);
786}
787
788/*
789 * Mkdir system call
790 */
791static int
792ext2_mkdir(ap)
793 struct vop_mkdir_args /* {
794 struct vnode *a_dvp;
795 struct vnode **a_vpp;
796 struct componentname *a_cnp;
797 struct vattr *a_vap;
798 } */ *ap;
799{
800 register struct vnode *dvp = ap->a_dvp;
801 register struct vattr *vap = ap->a_vap;
802 register struct componentname *cnp = ap->a_cnp;
803 register struct inode *ip, *dp;
804 struct vnode *tvp;
805 struct dirtemplate dirtemplate, *dtp;
806 int error, dmode;
807
808#ifdef DIAGNOSTIC
809 if ((cnp->cn_flags & HASBUF) == 0)
810 panic("ufs_mkdir: no name");
811#endif
812 dp = VTOI(dvp);
813 if ((nlink_t)dp->i_nlink >= LINK_MAX) {
814 error = EMLINK;
815 goto out;
816 }
817 dmode = vap->va_mode & 0777;
818 dmode |= IFDIR;
819 /*
820 * Must simulate part of ext2_makeinode here to acquire the inode,
821 * but not have it entered in the parent directory. The entry is
822 * made later after writing "." and ".." entries.
823 */
824 error = UFS_VALLOC(dvp, dmode, cnp->cn_cred, &tvp);
825 if (error)
826 goto out;
827 ip = VTOI(tvp);
828 ip->i_gid = dp->i_gid;
829#ifdef SUIDDIR
830 {
831#ifdef QUOTA
832 struct ucred ucred, *ucp;
833 ucp = cnp->cn_cred;
834#endif I
835 /*
836 * if we are hacking owners here, (only do this where told to)
837 * and we are not giving it TOO root, (would subvert quotas)
838 * then go ahead and give it to the other user.
839 * The new directory also inherits the SUID bit.
840 * If user's UID and dir UID are the same,
841 * 'give it away' so that the SUID is still forced on.
842 */
843 if ( (dvp->v_mount->mnt_flag & MNT_SUIDDIR) &&
844 (dp->i_mode & ISUID) && dp->i_uid) {
845 dmode |= ISUID;
846 ip->i_uid = dp->i_uid;
847#ifdef QUOTA
848 if (dp->i_uid != cnp->cn_cred->cr_uid) {
849 /*
850 * make sure the correct user gets charged
851 * for the space.
852 * Make a dummy credential for the victim.
853 * XXX This seems to never be accessed out of
854 * our context so a stack variable is ok.
855 */
856 ucred.cr_ref = 1;
857 ucred.cr_uid = ip->i_uid;
858 ucred.cr_ngroups = 1;
859 ucred.cr_groups[0] = dp->i_gid;
860 ucp = &ucred;
861 }
862#endif I
863 } else {
864 ip->i_uid = cnp->cn_cred->cr_uid;
865 }
866#ifdef QUOTA
867 if ((error = getinoquota(ip)) ||
868 (error = chkiq(ip, 1, ucp, 0))) {
869 UFS_VFREE(tvp, ip->i_number, dmode);
870 vput(tvp);
871 return (error);
872 }
873#endif
874 }
875#else
876 ip->i_uid = cnp->cn_cred->cr_uid;
877#ifdef QUOTA
878 if ((error = getinoquota(ip)) ||
879 (error = chkiq(ip, 1, cnp->cn_cred, 0))) {
880 UFS_VFREE(tvp, ip->i_number, dmode);
881 vput(tvp);
882 return (error);
883 }
884#endif
885#endif
886 ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
887 ip->i_mode = dmode;
888 tvp->v_type = VDIR; /* Rest init'd in getnewvnode(). */
889 ip->i_nlink = 2;
890 if (cnp->cn_flags & ISWHITEOUT)
891 ip->i_flags |= UF_OPAQUE;
892 error = UFS_UPDATE(tvp, 1);
893
894 /*
895 * Bump link count in parent directory
896 * to reflect work done below. Should
897 * be done before reference is created
898 * so reparation is possible if we crash.
899 */
900 dp->i_nlink++;
901 dp->i_flag |= IN_CHANGE;
902 error = UFS_UPDATE(dvp, 1);
903 if (error)
904 goto bad;
905
906 /* Initialize directory with "." and ".." from static template. */
907 if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs->s_es,
908 EXT2_FEATURE_INCOMPAT_FILETYPE))
909 dtp = &mastertemplate;
910 else
911 dtp = &omastertemplate;
912 dirtemplate = *dtp;
913 dirtemplate.dot_ino = ip->i_number;
914 dirtemplate.dotdot_ino = dp->i_number;
915 /* note that in ext2 DIRBLKSIZ == blocksize, not DEV_BSIZE
916 * so let's just redefine it - for this function only
917 */
918#undef DIRBLKSIZ
919#define DIRBLKSIZ VTOI(dvp)->i_e2fs->s_blocksize
920 dirtemplate.dotdot_reclen = DIRBLKSIZ - 12;
921 error = vn_rdwr(UIO_WRITE, tvp, (caddr_t)&dirtemplate,
922 sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE,
923 IO_NODELOCKED|IO_SYNC, cnp->cn_cred, (int *)0, (struct proc *)0);
924 if (error) {
925 dp->i_nlink--;
926 dp->i_flag |= IN_CHANGE;
927 goto bad;
928 }
929 if (DIRBLKSIZ > VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
930 panic("ufs_mkdir: blksize"); /* XXX should grow with balloc() */
931 else {
932 ip->i_size = DIRBLKSIZ;
933 ip->i_flag |= IN_CHANGE;
934 }
935
936 /* Directory set up, now install its entry in the parent directory. */
937 error = ext2_direnter(ip, dvp, cnp);
938 if (error) {
939 dp->i_nlink--;
940 dp->i_flag |= IN_CHANGE;
941 }
942bad:
943 /*
944 * No need to do an explicit VOP_TRUNCATE here, vrele will do this
945 * for us because we set the link count to 0.
946 */
947 if (error) {
948 ip->i_nlink = 0;
949 ip->i_flag |= IN_CHANGE;
950 vput(tvp);
951 } else
952 *ap->a_vpp = tvp;
953out:
954 return (error);
955#undef DIRBLKSIZ
956#define DIRBLKSIZ DEV_BSIZE
957}
958
959/*
960 * Rmdir system call.
961 */
962static int
963ext2_rmdir(ap)
964 struct vop_rmdir_args /* {
965 struct vnode *a_dvp;
966 struct vnode *a_vp;
967 struct componentname *a_cnp;
968 } */ *ap;
969{
970 struct vnode *vp = ap->a_vp;
971 struct vnode *dvp = ap->a_dvp;
972 struct componentname *cnp = ap->a_cnp;
973 struct proc *p = cnp->cn_proc;
974 struct inode *ip, *dp;
975 int error;
976
977 ip = VTOI(vp);
978 dp = VTOI(dvp);
979
980 /*
981 * Verify the directory is empty (and valid).
982 * (Rmdir ".." won't be valid since
983 * ".." will contain a reference to
984 * the current directory and thus be
985 * non-empty.)
986 */
987 error = 0;
988 if (ip->i_nlink != 2 || !ext2_dirempty(ip, dp->i_number, cnp->cn_cred)) {
989 error = ENOTEMPTY;
990 goto out;
991 }
992 if ((dp->i_flags & APPEND)
993 || (ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND))) {
994 error = EPERM;
995 goto out;
996 }
997 /*
998 * Delete reference to directory before purging
999 * inode. If we crash in between, the directory
1000 * will be reattached to lost+found,
1001 */
1002 error = ext2_dirremove(dvp, cnp);
1003 if (error)
1004 goto out;
1005 dp->i_nlink--;
1006 dp->i_flag |= IN_CHANGE;
1007 cache_purge(dvp);
1008 VOP_UNLOCK(dvp, 0, p);
1009 /*
1010 * Truncate inode. The only stuff left
1011 * in the directory is "." and "..". The
1012 * "." reference is inconsequential since
1013 * we're quashing it. The ".." reference
1014 * has already been adjusted above. We've
1015 * removed the "." reference and the reference
1016 * in the parent directory, but there may be
1017 * other hard links so decrement by 2 and
1018 * worry about them later.
1019 */
1020 ip->i_nlink -= 2;
1021 error = UFS_TRUNCATE(vp, (off_t)0, IO_SYNC, cnp->cn_cred, p);
1022 cache_purge(ITOV(ip));
1023 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p);
1024out:
1025 return (error);
1026}
1027
1028/*
1029 * symlink -- make a symbolic link
1030 */
1031static int
1032ext2_symlink(ap)
1033 struct vop_symlink_args /* {
1034 struct vnode *a_dvp;
1035 struct vnode **a_vpp;
1036 struct componentname *a_cnp;
1037 struct vattr *a_vap;
1038 char *a_target;
1039 } */ *ap;
1040{
1041 register struct vnode *vp, **vpp = ap->a_vpp;
1042 register struct inode *ip;
1043 int len, error;
1044
1045 error = ext2_makeinode(IFLNK | ap->a_vap->va_mode, ap->a_dvp,
1046 vpp, ap->a_cnp);
1047 if (error)
1048 return (error);
1049 vp = *vpp;
1050 len = strlen(ap->a_target);
1051 if (len < vp->v_mount->mnt_maxsymlinklen) {
1052 ip = VTOI(vp);
1053 bcopy(ap->a_target, (char *)ip->i_shortlink, len);
1054 ip->i_size = len;
1055 ip->i_flag |= IN_CHANGE | IN_UPDATE;
1056 } else
1057 error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0,
1058 UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, (int *)0,
1059 (struct proc *)0);
1060 if (error)
1061 vput(vp);
1062 return (error);
1063}
1064
1065/*
1066 * Allocate a new inode.
1067 */
1068static int
1069ext2_makeinode(mode, dvp, vpp, cnp)
1070 int mode;
1071 struct vnode *dvp;
1072 struct vnode **vpp;
1073 struct componentname *cnp;
1074{
1075 register struct inode *ip, *pdir;
1076 struct vnode *tvp;
1077 int error;
1078
1079 pdir = VTOI(dvp);
1080#ifdef DIAGNOSTIC
1081 if ((cnp->cn_flags & HASBUF) == 0)
1082 panic("ext2_makeinode: no name");
1083#endif
1084 *vpp = NULL;
1085 if ((mode & IFMT) == 0)
1086 mode |= IFREG;
1087
1088 error = UFS_VALLOC(dvp, mode, cnp->cn_cred, &tvp);
1089 if (error) {
1090 return (error);
1091 }
1092 ip = VTOI(tvp);
1093 ip->i_gid = pdir->i_gid;
1094#ifdef SUIDDIR
1095 {
1096#ifdef QUOTA
1097 struct ucred ucred, *ucp;
1098 ucp = cnp->cn_cred;
1099#endif I
1100 /*
1101 * if we are
1102 * not the owner of the directory,
1103 * and we are hacking owners here, (only do this where told to)
1104 * and we are not giving it TOO root, (would subvert quotas)
1105 * then go ahead and give it to the other user.
1106 * Note that this drops off the execute bits for security.
1107 */
1108 if ( (dvp->v_mount->mnt_flag & MNT_SUIDDIR) &&
1109 (pdir->i_mode & ISUID) &&
1110 (pdir->i_uid != cnp->cn_cred->cr_uid) && pdir->i_uid) {
1111 ip->i_uid = pdir->i_uid;
1112 mode &= ~07111;
1113#ifdef QUOTA
1114 /*
1115 * make sure the correct user gets charged
1116 * for the space.
1117 * Quickly knock up a dummy credential for the victim.
1118 * XXX This seems to never be accessed out of our
1119 * context so a stack variable is ok.
1120 */
1121 ucred.cr_ref = 1;
1122 ucred.cr_uid = ip->i_uid;
1123 ucred.cr_ngroups = 1;
1124 ucred.cr_groups[0] = pdir->i_gid;
1125 ucp = &ucred;
1126#endif I
1127 } else {
1128 ip->i_uid = cnp->cn_cred->cr_uid;
1129 }
1130
1131#ifdef QUOTA
1132 if ((error = getinoquota(ip)) ||
1133 (error = chkiq(ip, 1, ucp, 0))) {
1134 UFS_VFREE(tvp, ip->i_number, mode);
1135 vput(tvp);
1136 return (error);
1137 }
1138#endif
1139 }
1140#else
1141 ip->i_uid = cnp->cn_cred->cr_uid;
1142#ifdef QUOTA
1143 if ((error = getinoquota(ip)) ||
1144 (error = chkiq(ip, 1, cnp->cn_cred, 0))) {
1145 UFS_VFREE(tvp, ip->i_number, mode);
1146 vput(tvp);
1147 return (error);
1148 }
1149#endif
1150#endif
1151 ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
1152 ip->i_mode = mode;
1153 tvp->v_type = IFTOVT(mode); /* Rest init'd in getnewvnode(). */
1154 ip->i_nlink = 1;
1155 if ((ip->i_mode & ISGID) && !groupmember(ip->i_gid, cnp->cn_cred) &&
1156 suser_xxx(cnp->cn_cred, 0, PRISON_ROOT))
1157 ip->i_mode &= ~ISGID;
1158
1159 if (cnp->cn_flags & ISWHITEOUT)
1160 ip->i_flags |= UF_OPAQUE;
1161
1162 /*
1163 * Make sure inode goes to disk before directory entry.
1164 */
1165 error = UFS_UPDATE(tvp, 1);
1166 if (error)
1167 goto bad;
1168 error = ext2_direnter(ip, dvp, cnp);
1169 if (error)
1170 goto bad;
1171
1172 *vpp = tvp;
1173 return (0);
1174
1175bad:
1176 /*
1177 * Write error occurred trying to update the inode
1178 * or the directory so must deallocate the inode.
1179 */
1180 ip->i_nlink = 0;
1181 ip->i_flag |= IN_CHANGE;
1182 vput(tvp);
1183 return (error);
1184}
1189
1190/*
1191 * get page routine
1192 *
1193 * XXX By default, wimp out... note that a_offset is ignored (and always
1194 * XXX has been).
1195 */
1196static int
1197ext2_getpages(ap)
1198 struct vop_getpages_args *ap;
1199{
1200 return (vnode_pager_generic_getpages(ap->a_vp, ap->a_m, ap->a_count,
1201 ap->a_reqpage));
1202}
1203
1204/*
1205 * put page routine
1206 *
1207 * XXX By default, wimp out... note that a_offset is ignored (and always
1208 * XXX has been).
1209 */
1210static int
1211ext2_putpages(ap)
1212 struct vop_putpages_args *ap;
1213{
1214 return (vnode_pager_generic_putpages(ap->a_vp, ap->a_m, ap->a_count,
1215 ap->a_sync, ap->a_rtvals));
1216}