1 /* $OpenBSD: ext2fs_vnops.c,v 1.41 2005/08/14 12:41:44 pedro Exp $ */
2 /* $NetBSD: ext2fs_vnops.c,v 1.1 1997/06/11 09:34:09 bouyer Exp $ */
3
4 /*
5 * Copyright (c) 1997 Manuel Bouyer.
6 * Copyright (c) 1982, 1986, 1989, 1993
7 * The Regents of the University of California. All rights reserved.
8 * (c) UNIX System Laboratories, Inc.
9 * All or some portions of this file are derived from material licensed
10 * to the University of California by American Telephone and Telegraph
11 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
12 * the permission of UNIX System Laboratories, Inc.
13 *
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 * 3. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 *
38 * @(#)ufs_vnops.c 8.14 (Berkeley) 10/26/94
39 * Modified for ext2fs by Manuel Bouyer.
40 */
41
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/resourcevar.h>
45 #include <sys/kernel.h>
46 #include <sys/file.h>
47 #include <sys/stat.h>
48 #include <sys/buf.h>
49 #include <sys/proc.h>
50 #include <sys/conf.h>
51 #include <sys/mount.h>
52 #include <sys/namei.h>
53 #include <sys/vnode.h>
54 #include <sys/lockf.h>
55 #include <sys/malloc.h>
56 #include <sys/pool.h>
57 #include <sys/signalvar.h>
58
59 #include <uvm/uvm_extern.h>
60
61 #include <miscfs/fifofs/fifo.h>
62 #include <miscfs/specfs/specdev.h>
63
64 #include <ufs/ufs/quota.h>
65 #include <ufs/ufs/inode.h>
66 #include <ufs/ufs/ufs_extern.h>
67 #include <ufs/ufs/ufsmount.h>
68
69 #include <ufs/ext2fs/ext2fs.h>
70 #include <ufs/ext2fs/ext2fs_extern.h>
71 #include <ufs/ext2fs/ext2fs_dir.h>
72
73
74 static int ext2fs_chmod(struct vnode *, mode_t, struct ucred *, struct proc *);
75 static int ext2fs_chown(struct vnode *, uid_t, gid_t, struct ucred *, struct proc *);
76
77 union _qcvt {
78 int64_t qcvt;
79 int32_t val[2];
80 };
81 #define SETHIGH(q, h) { \
82 union _qcvt tmp; \
83 tmp.qcvt = (q); \
84 tmp.val[_QUAD_HIGHWORD] = (h); \
85 (q) = tmp.qcvt; \
86 }
87 #define SETLOW(q, l) { \
88 union _qcvt tmp; \
89 tmp.qcvt = (q); \
90 tmp.val[_QUAD_LOWWORD] = (l); \
91 (q) = tmp.qcvt; \
92 }
93
94 /*
95 * Create a regular file
96 */
97 int
ext2fs_create(v)98 ext2fs_create(v)
99 void *v;
100 {
101 struct vop_create_args /* {
102 struct vnode *a_dvp;
103 struct vnode **a_vpp;
104 struct componentname *a_cnp;
105 struct vattr *a_vap;
106 } */ *ap = v;
107 return ext2fs_makeinode(MAKEIMODE(ap->a_vap->va_type,
108 ap->a_vap->va_mode),
109 ap->a_dvp, ap->a_vpp, ap->a_cnp);
110 }
111
112 /*
113 * Mknod vnode call
114 */
115 /* ARGSUSED */
116 int
ext2fs_mknod(v)117 ext2fs_mknod(v)
118 void *v;
119 {
120 struct vop_mknod_args /* {
121 struct vnode *a_dvp;
122 struct vnode **a_vpp;
123 struct componentname *a_cnp;
124 struct vattr *a_vap;
125 } */ *ap = v;
126 register struct vattr *vap = ap->a_vap;
127 register struct vnode **vpp = ap->a_vpp;
128 register struct inode *ip;
129 int error;
130
131 if ((error =
132 ext2fs_makeinode(MAKEIMODE(vap->va_type, vap->va_mode),
133 ap->a_dvp, vpp, ap->a_cnp)) != 0)
134 return (error);
135 ip = VTOI(*vpp);
136 ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
137 if (vap->va_rdev != VNOVAL) {
138 /*
139 * Want to be able to use this to make badblock
140 * inodes, so don't truncate the dev number.
141 */
142 ip->i_e2din.e2di_rdev = h2fs32(vap->va_rdev);
143 }
144 /*
145 * Remove inode so that it will be reloaded by VFS_VGET and
146 * checked to see if it is an alias of an existing entry in
147 * the inode cache.
148 */
149 vput(*vpp);
150 (*vpp)->v_type = VNON;
151 vgone(*vpp);
152 *vpp = 0;
153 return (0);
154 }
155
156 /*
157 * Open called.
158 *
159 * Just check the APPEND flag.
160 */
161 /* ARGSUSED */
162 int
ext2fs_open(v)163 ext2fs_open(v)
164 void *v;
165 {
166 struct vop_open_args /* {
167 struct vnode *a_vp;
168 int a_mode;
169 struct ucred *a_cred;
170 struct proc *a_p;
171 } */ *ap = v;
172
173 /*
174 * Files marked append-only must be opened for appending.
175 */
176 if ((VTOI(ap->a_vp)->i_e2fs_flags & EXT2_APPEND) &&
177 (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
178 return (EPERM);
179 return (0);
180 }
181
182 int
ext2fs_access(v)183 ext2fs_access(v)
184 void *v;
185 {
186 struct vop_access_args /* {
187 struct vnode *a_vp;
188 int a_mode;
189 struct ucred *a_cred;
190 struct proc *a_p;
191 } */ *ap = v;
192 register struct vnode *vp = ap->a_vp;
193 register struct inode *ip = VTOI(vp);
194 mode_t mode = ap->a_mode;
195
196 /* If immutable bit set, nobody gets to write it. */
197 if ((mode & VWRITE) && (ip->i_e2fs_flags & EXT2_IMMUTABLE))
198 return (EPERM);
199
200 return (vaccess(ip->i_e2fs_mode, ip->i_e2fs_uid, ip->i_e2fs_gid, mode,
201 ap->a_cred));
202 }
203
204 /* ARGSUSED */
205 int
ext2fs_getattr(v)206 ext2fs_getattr(v)
207 void *v;
208 {
209 struct vop_getattr_args /* {
210 struct vnode *a_vp;
211 struct vattr *a_vap;
212 struct ucred *a_cred;
213 struct proc *a_p;
214 } */ *ap = v;
215 register struct vnode *vp = ap->a_vp;
216 register struct inode *ip = VTOI(vp);
217 register struct vattr *vap = ap->a_vap;
218
219 EXT2FS_ITIMES(ip, &time, &time);
220 /*
221 * Copy from inode table
222 */
223 vap->va_fsid = ip->i_dev;
224 vap->va_fileid = ip->i_number;
225 vap->va_mode = ip->i_e2fs_mode & ALLPERMS;
226 vap->va_nlink = ip->i_e2fs_nlink;
227 vap->va_uid = ip->i_e2fs_uid;
228 vap->va_gid = ip->i_e2fs_gid;
229 vap->va_rdev = (dev_t)fs2h32(ip->i_e2din.e2di_rdev);
230 vap->va_size = ext2fs_size(ip);
231 vap->va_atime.tv_sec = ip->i_e2fs_atime;
232 vap->va_atime.tv_nsec = 0;
233 vap->va_mtime.tv_sec = ip->i_e2fs_mtime;
234 vap->va_mtime.tv_nsec = 0;
235 vap->va_ctime.tv_sec = ip->i_e2fs_ctime;
236 vap->va_ctime.tv_nsec = 0;
237 #ifdef EXT2FS_SYSTEM_FLAGS
238 vap->va_flags = (ip->i_e2fs_flags & EXT2_APPEND) ? SF_APPEND : 0;
239 vap->va_flags |= (ip->i_e2fs_flags & EXT2_IMMUTABLE) ? SF_IMMUTABLE : 0;
240 #else
241 vap->va_flags = (ip->i_e2fs_flags & EXT2_APPEND) ? UF_APPEND : 0;
242 vap->va_flags |= (ip->i_e2fs_flags & EXT2_IMMUTABLE) ? UF_IMMUTABLE : 0;
243 #endif
244 vap->va_gen = ip->i_e2fs_gen;
245 /* this doesn't belong here */
246 if (vp->v_type == VBLK)
247 vap->va_blocksize = BLKDEV_IOSIZE;
248 else if (vp->v_type == VCHR)
249 vap->va_blocksize = MAXBSIZE;
250 else
251 vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize;
252 vap->va_bytes = dbtob((u_quad_t)ip->i_e2fs_nblock);
253 vap->va_type = vp->v_type;
254 vap->va_filerev = ip->i_modrev;
255 return (0);
256 }
257
258 /*
259 * Set attribute vnode op. called from several syscalls
260 */
261 int
ext2fs_setattr(v)262 ext2fs_setattr(v)
263 void *v;
264 {
265 struct vop_setattr_args /* {
266 struct vnode *a_vp;
267 struct vattr *a_vap;
268 struct ucred *a_cred;
269 struct proc *a_p;
270 } */ *ap = v;
271 register struct vattr *vap = ap->a_vap;
272 register struct vnode *vp = ap->a_vp;
273 register struct inode *ip = VTOI(vp);
274 register struct ucred *cred = ap->a_cred;
275 register struct proc *p = ap->a_p;
276 int error;
277
278 /*
279 * Check for unsettable attributes.
280 */
281 if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
282 (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
283 (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
284 ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
285 return (EINVAL);
286 }
287 if (vap->va_flags != VNOVAL) {
288 if (vp->v_mount->mnt_flag & MNT_RDONLY)
289 return (EROFS);
290 if (cred->cr_uid != ip->i_e2fs_uid &&
291 (error = suser_ucred(cred)))
292 return (error);
293 #ifdef EXT2FS_SYSTEM_FLAGS
294 if (cred->cr_uid == 0) {
295 if ((ip->i_e2fs_flags &
296 (EXT2_APPEND | EXT2_IMMUTABLE)) && securelevel > 0)
297 return (EPERM);
298 ip->i_e2fs_flags &= ~(EXT2_APPEND | EXT2_IMMUTABLE);
299 ip->i_e2fs_flags |=
300 (vap->va_flags & SF_APPEND) ? EXT2_APPEND : 0 |
301 (vap->va_flags & SF_IMMUTABLE) ? EXT2_IMMUTABLE: 0;
302 } else {
303 return (EPERM);
304 }
305 #else
306 ip->i_e2fs_flags &= ~(EXT2_APPEND | EXT2_IMMUTABLE);
307 ip->i_e2fs_flags |=
308 (vap->va_flags & UF_APPEND) ? EXT2_APPEND : 0 |
309 (vap->va_flags & UF_IMMUTABLE) ? EXT2_IMMUTABLE: 0;
310 #endif
311 ip->i_flag |= IN_CHANGE;
312 if (vap->va_flags & (IMMUTABLE | APPEND))
313 return (0);
314 }
315 if (ip->i_e2fs_flags & (EXT2_APPEND | EXT2_IMMUTABLE))
316 return (EPERM);
317 /*
318 * Go through the fields and update iff not VNOVAL.
319 */
320 if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
321 if (vp->v_mount->mnt_flag & MNT_RDONLY)
322 return (EROFS);
323 error = ext2fs_chown(vp, vap->va_uid, vap->va_gid, cred, p);
324 if (error)
325 return (error);
326 }
327 if (vap->va_size != VNOVAL) {
328 /*
329 * Disallow write attempts on read-only file systems;
330 * unless the file is a socket, fifo, or a block or
331 * character device resident on the file system.
332 */
333 switch (vp->v_type) {
334 case VDIR:
335 return (EISDIR);
336 case VLNK:
337 case VREG:
338 if (vp->v_mount->mnt_flag & MNT_RDONLY)
339 return (EROFS);
340 default:
341 break;
342 }
343 error = ext2fs_truncate(ip, vap->va_size, 0, cred);
344 if (error)
345 return (error);
346 }
347 if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
348 if (vp->v_mount->mnt_flag & MNT_RDONLY)
349 return (EROFS);
350 if (cred->cr_uid != ip->i_e2fs_uid &&
351 (error = suser_ucred(cred)) &&
352 ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
353 (error = VOP_ACCESS(vp, VWRITE, cred, p))))
354 return (error);
355 if (vap->va_atime.tv_sec != VNOVAL)
356 if (!(vp->v_mount->mnt_flag & MNT_NOATIME))
357 ip->i_flag |= IN_ACCESS;
358 if (vap->va_mtime.tv_sec != VNOVAL)
359 ip->i_flag |= IN_CHANGE | IN_UPDATE;
360 error = ext2fs_update(ip, &vap->va_atime, &vap->va_mtime, 1);
361 if (error)
362 return (error);
363 }
364 error = 0;
365 if (vap->va_mode != (mode_t)VNOVAL) {
366 if (vp->v_mount->mnt_flag & MNT_RDONLY)
367 return (EROFS);
368 error = ext2fs_chmod(vp, (int)vap->va_mode, cred, p);
369 }
370 return (error);
371 }
372
373 /*
374 * Change the mode on a file.
375 * Inode must be locked before calling.
376 */
377 static int
ext2fs_chmod(vp,mode,cred,p)378 ext2fs_chmod(vp, mode, cred, p)
379 register struct vnode *vp;
380 register mode_t mode;
381 register struct ucred *cred;
382 struct proc *p;
383 {
384 register struct inode *ip = VTOI(vp);
385 int error;
386
387 if (cred->cr_uid != ip->i_e2fs_uid && (error = suser_ucred(cred)))
388 return (error);
389 if (cred->cr_uid) {
390 if (vp->v_type != VDIR && (mode & S_ISTXT))
391 return (EFTYPE);
392 if (!groupmember(ip->i_e2fs_gid, cred) && (mode & ISGID))
393 return (EPERM);
394 }
395 ip->i_e2fs_mode &= ~ALLPERMS;
396 ip->i_e2fs_mode |= (mode & ALLPERMS);
397 ip->i_flag |= IN_CHANGE;
398 if ((vp->v_flag & VTEXT) && (ip->i_e2fs_mode & S_ISTXT) == 0)
399 (void) uvm_vnp_uncache(vp);
400 return (0);
401 }
402
403 /*
404 * Perform chown operation on inode ip;
405 * inode must be locked prior to call.
406 */
407 static int
ext2fs_chown(vp,uid,gid,cred,p)408 ext2fs_chown(vp, uid, gid, cred, p)
409 register struct vnode *vp;
410 uid_t uid;
411 gid_t gid;
412 struct ucred *cred;
413 struct proc *p;
414 {
415 register struct inode *ip = VTOI(vp);
416 uid_t ouid;
417 gid_t ogid;
418 int error = 0;
419
420 if (uid == (uid_t)VNOVAL)
421 uid = ip->i_e2fs_uid;
422 if (gid == (gid_t)VNOVAL)
423 gid = ip->i_e2fs_gid;
424 /*
425 * If we don't own the file, are trying to change the owner
426 * of the file, or are not a member of the target group,
427 * the caller must be superuser or the call fails.
428 */
429 if ((cred->cr_uid != ip->i_e2fs_uid || uid != ip->i_e2fs_uid ||
430 (gid != ip->i_e2fs_gid && !groupmember((gid_t)gid, cred))) &&
431 (error = suser_ucred(cred)))
432 return (error);
433 ogid = ip->i_e2fs_gid;
434 ouid = ip->i_e2fs_uid;
435
436 ip->i_e2fs_gid = gid;
437 ip->i_e2fs_uid = uid;
438 if (ouid != uid || ogid != gid)
439 ip->i_flag |= IN_CHANGE;
440 if (ouid != uid && cred->cr_uid != 0)
441 ip->i_e2fs_mode &= ~ISUID;
442 if (ogid != gid && cred->cr_uid != 0)
443 ip->i_e2fs_mode &= ~ISGID;
444 return (0);
445 }
446
447 int
ext2fs_remove(v)448 ext2fs_remove(v)
449 void *v;
450 {
451 struct vop_remove_args /* {
452 struct vnode *a_dvp;
453 struct vnode *a_vp;
454 struct componentname *a_cnp;
455 } */ *ap = v;
456 register struct inode *ip;
457 register struct vnode *vp = ap->a_vp;
458 register struct vnode *dvp = ap->a_dvp;
459 int error;
460
461 ip = VTOI(vp);
462 if (vp->v_type == VDIR ||
463 (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) ||
464 (VTOI(dvp)->i_e2fs_flags & EXT2_APPEND)) {
465 error = EPERM;
466 goto out;
467 }
468 error = ext2fs_dirremove(dvp, ap->a_cnp);
469 if (error == 0) {
470 ip->i_e2fs_nlink--;
471 ip->i_flag |= IN_CHANGE;
472 }
473 out:
474 if (dvp == vp)
475 vrele(vp);
476 else
477 vput(vp);
478 vput(dvp);
479 return (error);
480 }
481
482 /*
483 * link vnode call
484 */
485 int
ext2fs_link(v)486 ext2fs_link(v)
487 void *v;
488 {
489 struct vop_link_args /* {
490 struct vnode *a_dvp;
491 struct vnode *a_vp;
492 struct componentname *a_cnp;
493 } */ *ap = v;
494 register struct vnode *dvp = ap->a_dvp;
495 register struct vnode *vp = ap->a_vp;
496 register struct componentname *cnp = ap->a_cnp;
497 struct proc *p = cnp->cn_proc;
498 register struct inode *ip;
499 int error;
500
501 #ifdef DIAGNOSTIC
502 if ((cnp->cn_flags & HASBUF) == 0)
503 panic("ext2fs_link: no name");
504 #endif
505 if (vp->v_type == VDIR) {
506 VOP_ABORTOP(dvp, cnp);
507 error = EISDIR;
508 goto out2;
509 }
510 if (dvp->v_mount != vp->v_mount) {
511 VOP_ABORTOP(dvp, cnp);
512 error = EXDEV;
513 goto out2;
514 }
515 if (dvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE, p))) {
516 VOP_ABORTOP(dvp, cnp);
517 goto out2;
518 }
519 ip = VTOI(vp);
520 if ((nlink_t)ip->i_e2fs_nlink >= LINK_MAX) {
521 VOP_ABORTOP(dvp, cnp);
522 error = EMLINK;
523 goto out1;
524 }
525 if (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) {
526 VOP_ABORTOP(dvp, cnp);
527 error = EPERM;
528 goto out1;
529 }
530 ip->i_e2fs_nlink++;
531 ip->i_flag |= IN_CHANGE;
532 error = ext2fs_update(ip, NULL, NULL, 1);
533 if (!error)
534 error = ext2fs_direnter(ip, dvp, cnp);
535 if (error) {
536 ip->i_e2fs_nlink--;
537 ip->i_flag |= IN_CHANGE;
538 }
539 pool_put(&namei_pool, cnp->cn_pnbuf);
540 out1:
541 if (dvp != vp)
542 VOP_UNLOCK(vp, 0, p);
543 out2:
544 vput(dvp);
545 return (error);
546 }
547
548 /*
549 * Rename system call.
550 * rename("foo", "bar");
551 * is essentially
552 * unlink("bar");
553 * link("foo", "bar");
554 * unlink("foo");
555 * but ``atomically''. Can't do full commit without saving state in the
556 * inode on disk which isn't feasible at this time. Best we can do is
557 * always guarantee the target exists.
558 *
559 * Basic algorithm is:
560 *
561 * 1) Bump link count on source while we're linking it to the
562 * target. This also ensure the inode won't be deleted out
563 * from underneath us while we work (it may be truncated by
564 * a concurrent `trunc' or `open' for creation).
565 * 2) Link source to destination. If destination already exists,
566 * delete it first.
567 * 3) Unlink source reference to inode if still around. If a
568 * directory was moved and the parent of the destination
569 * is different from the source, patch the ".." entry in the
570 * directory.
571 */
572 int
ext2fs_rename(v)573 ext2fs_rename(v)
574 void *v;
575 {
576 struct vop_rename_args /* {
577 struct vnode *a_fdvp;
578 struct vnode *a_fvp;
579 struct componentname *a_fcnp;
580 struct vnode *a_tdvp;
581 struct vnode *a_tvp;
582 struct componentname *a_tcnp;
583 } */ *ap = v;
584 struct vnode *tvp = ap->a_tvp;
585 register struct vnode *tdvp = ap->a_tdvp;
586 struct vnode *fvp = ap->a_fvp;
587 register struct vnode *fdvp = ap->a_fdvp;
588 register struct componentname *tcnp = ap->a_tcnp;
589 register struct componentname *fcnp = ap->a_fcnp;
590 register struct inode *ip, *xp, *dp;
591 struct proc *p = fcnp->cn_proc;
592 struct ext2fs_dirtemplate dirbuf;
593 //struct timespec ts;
594 int doingdirectory = 0, oldparent = 0, newparent = 0;
595 int error = 0;
596 u_char namlen;
597
598 #ifdef DIAGNOSTIC
599 if ((tcnp->cn_flags & HASBUF) == 0 ||
600 (fcnp->cn_flags & HASBUF) == 0)
601 panic("ext2fs_rename: no name");
602 #endif
603 /*
604 * Check for cross-device rename.
605 */
606 if ((fvp->v_mount != tdvp->v_mount) ||
607 (tvp && (fvp->v_mount != tvp->v_mount))) {
608 error = EXDEV;
609 abortit:
610 VOP_ABORTOP(tdvp, tcnp); /* XXX, why not in NFS? */
611 if (tdvp == tvp)
612 vrele(tdvp);
613 else
614 vput(tdvp);
615 if (tvp)
616 vput(tvp);
617 VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */
618 vrele(fdvp);
619 vrele(fvp);
620 return (error);
621 }
622
623 /*
624 * Check if just deleting a link name.
625 */
626 if (tvp && ((VTOI(tvp)->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) ||
627 (VTOI(tdvp)->i_e2fs_flags & EXT2_APPEND))) {
628 error = EPERM;
629 goto abortit;
630 }
631 if (fvp == tvp) {
632 if (fvp->v_type == VDIR) {
633 error = EINVAL;
634 goto abortit;
635 }
636
637 /* Release destination completely. */
638 VOP_ABORTOP(tdvp, tcnp);
639 vput(tdvp);
640 vput(tvp);
641
642 /* Delete source. */
643 vrele(fdvp);
644 vrele(fvp);
645 fcnp->cn_flags &= ~MODMASK;
646 fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
647 if ((fcnp->cn_flags & SAVESTART) == 0)
648 panic("ext2fs_rename: lost from startdir");
649 fcnp->cn_nameiop = DELETE;
650 (void) relookup(fdvp, &fvp, fcnp);
651 return (VOP_REMOVE(fdvp, fvp, fcnp));
652 }
653 if ((error = vn_lock(fvp, LK_EXCLUSIVE, p)) != 0)
654 goto abortit;
655 dp = VTOI(fdvp);
656 ip = VTOI(fvp);
657 if ((nlink_t)ip->i_e2fs_nlink >= LINK_MAX) {
658 VOP_UNLOCK(fvp, 0, p);
659 error = EMLINK;
660 goto abortit;
661 }
662 if ((ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) ||
663 (dp->i_e2fs_flags & EXT2_APPEND)) {
664 VOP_UNLOCK(fvp, 0, p);
665 error = EPERM;
666 goto abortit;
667 }
668 if ((ip->i_e2fs_mode & IFMT) == IFDIR) {
669 error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
670 if (!error && tvp)
671 error = VOP_ACCESS(tvp, VWRITE, tcnp->cn_cred,
672 tcnp->cn_proc);
673 if (error) {
674 VOP_UNLOCK(fvp, 0, p);
675 error = EACCES;
676 goto abortit;
677 }
678 /*
679 * Avoid ".", "..", and aliases of "." for obvious reasons.
680 */
681 if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
682 dp == ip ||
683 (fcnp->cn_flags&ISDOTDOT) ||
684 (tcnp->cn_flags & ISDOTDOT) ||
685 (ip->i_flag & IN_RENAME)) {
686 VOP_UNLOCK(fvp, 0, p);
687 error = EINVAL;
688 goto abortit;
689 }
690 ip->i_flag |= IN_RENAME;
691 oldparent = dp->i_number;
692 doingdirectory++;
693 }
694 vrele(fdvp);
695
696 /*
697 * When the target exists, both the directory
698 * and target vnodes are returned locked.
699 */
700 dp = VTOI(tdvp);
701 xp = NULL;
702 if (tvp)
703 xp = VTOI(tvp);
704
705 /*
706 * 1) Bump link count while we're moving stuff
707 * around. If we crash somewhere before
708 * completing our work, the link count
709 * may be wrong, but correctable.
710 */
711 ip->i_e2fs_nlink++;
712 ip->i_flag |= IN_CHANGE;
713 if ((error = ext2fs_update(ip, NULL, NULL, 1)) != 0) {
714 VOP_UNLOCK(fvp, 0, p);
715 goto bad;
716 }
717
718 /*
719 * If ".." must be changed (ie the directory gets a new
720 * parent) then the source directory must not be in the
721 * directory hierarchy above the target, as this would
722 * orphan everything below the source directory. Also
723 * the user must have write permission in the source so
724 * as to be able to change "..". We must repeat the call
725 * to namei, as the parent directory is unlocked by the
726 * call to checkpath().
727 */
728 error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
729 VOP_UNLOCK(fvp, 0, p);
730 if (oldparent != dp->i_number)
731 newparent = dp->i_number;
732 if (doingdirectory && newparent) {
733 if (error) /* write access check above */
734 goto bad;
735 if (xp != NULL)
736 vput(tvp);
737 error = ext2fs_checkpath(ip, dp, tcnp->cn_cred);
738 if (error != 0)
739 goto out;
740 if ((tcnp->cn_flags & SAVESTART) == 0)
741 panic("ext2fs_rename: lost to startdir");
742 if ((error = relookup(tdvp, &tvp, tcnp)) != 0)
743 goto out;
744 dp = VTOI(tdvp);
745 xp = NULL;
746 if (tvp)
747 xp = VTOI(tvp);
748 }
749 /*
750 * 2) If target doesn't exist, link the target
751 * to the source and unlink the source.
752 * Otherwise, rewrite the target directory
753 * entry to reference the source inode and
754 * expunge the original entry's existence.
755 */
756 if (xp == NULL) {
757 if (dp->i_dev != ip->i_dev)
758 panic("rename: EXDEV");
759 /*
760 * Account for ".." in new directory.
761 * When source and destination have the same
762 * parent we don't fool with the link count.
763 */
764 if (doingdirectory && newparent) {
765 if ((nlink_t)dp->i_e2fs_nlink >= LINK_MAX) {
766 error = EMLINK;
767 goto bad;
768 }
769 dp->i_e2fs_nlink++;
770 dp->i_flag |= IN_CHANGE;
771 if ((error = ext2fs_update(dp, NULL, NULL, 1)) != 0)
772 goto bad;
773 }
774 error = ext2fs_direnter(ip, tdvp, tcnp);
775 if (error != 0) {
776 if (doingdirectory && newparent) {
777 dp->i_e2fs_nlink--;
778 dp->i_flag |= IN_CHANGE;
779 (void)ext2fs_update(dp, NULL, NULL, 1);
780 }
781 goto bad;
782 }
783 vput(tdvp);
784 } else {
785 if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev)
786 panic("rename: EXDEV");
787 /*
788 * Short circuit rename(foo, foo).
789 */
790 if (xp->i_number == ip->i_number)
791 panic("rename: same file");
792 /*
793 * If the parent directory is "sticky", then the user must
794 * own the parent directory, or the destination of the rename,
795 * otherwise the destination may not be changed (except by
796 * root). This implements append-only directories.
797 */
798 if ((dp->i_e2fs_mode & S_ISTXT) && tcnp->cn_cred->cr_uid != 0 &&
799 tcnp->cn_cred->cr_uid != dp->i_e2fs_uid &&
800 xp->i_e2fs_uid != tcnp->cn_cred->cr_uid) {
801 error = EPERM;
802 goto bad;
803 }
804 /*
805 * Target must be empty if a directory and have no links
806 * to it. Also, ensure source and target are compatible
807 * (both directories, or both not directories).
808 */
809 if ((xp->i_e2fs_mode & IFMT) == IFDIR) {
810 if (!ext2fs_dirempty(xp, dp->i_number, tcnp->cn_cred) ||
811 xp->i_e2fs_nlink > 2) {
812 error = ENOTEMPTY;
813 goto bad;
814 }
815 if (!doingdirectory) {
816 error = ENOTDIR;
817 goto bad;
818 }
819 cache_purge(tdvp);
820 } else if (doingdirectory) {
821 error = EISDIR;
822 goto bad;
823 }
824 error = ext2fs_dirrewrite(dp, ip, tcnp);
825 if (error != 0)
826 goto bad;
827 /*
828 * If the target directory is in the same
829 * directory as the source directory,
830 * decrement the link count on the parent
831 * of the target directory.
832 */
833 if (doingdirectory && !newparent) {
834 dp->i_e2fs_nlink--;
835 dp->i_flag |= IN_CHANGE;
836 }
837 vput(tdvp);
838 /*
839 * Adjust the link count of the target to
840 * reflect the dirrewrite above. If this is
841 * a directory it is empty and there are
842 * no links to it, so we can squash the inode and
843 * any space associated with it. We disallowed
844 * renaming over top of a directory with links to
845 * it above, as the remaining link would point to
846 * a directory without "." or ".." entries.
847 */
848 xp->i_e2fs_nlink--;
849 if (doingdirectory) {
850 if (--xp->i_e2fs_nlink != 0)
851 panic("rename: linked directory");
852 error = ext2fs_truncate(xp, (off_t)0, IO_SYNC,
853 tcnp->cn_cred);
854 }
855 xp->i_flag |= IN_CHANGE;
856 vput(tvp);
857 xp = NULL;
858 }
859
860 /*
861 * 3) Unlink the source.
862 */
863 fcnp->cn_flags &= ~MODMASK;
864 fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
865 if ((fcnp->cn_flags & SAVESTART) == 0)
866 panic("ext2fs_rename: lost from startdir");
867 (void) relookup(fdvp, &fvp, fcnp);
868 if (fvp != NULL) {
869 xp = VTOI(fvp);
870 dp = VTOI(fdvp);
871 } else {
872 /*
873 * From name has disappeared.
874 */
875 if (doingdirectory)
876 panic("ext2fs_rename: lost dir entry");
877 vrele(ap->a_fvp);
878 return (0);
879 }
880 /*
881 * Ensure that the directory entry still exists and has not
882 * changed while the new name has been entered. If the source is
883 * a file then the entry may have been unlinked or renamed. In
884 * either case there is no further work to be done. If the source
885 * is a directory then it cannot have been rmdir'ed; its link
886 * count of three would cause a rmdir to fail with ENOTEMPTY.
887 * The IRENAME flag ensures that it cannot be moved by another
888 * rename.
889 */
890 if (xp != ip) {
891 if (doingdirectory)
892 panic("ext2fs_rename: lost dir entry");
893 } else {
894 /*
895 * If the source is a directory with a
896 * new parent, the link count of the old
897 * parent directory must be decremented
898 * and ".." set to point to the new parent.
899 */
900 if (doingdirectory && newparent) {
901 dp->i_e2fs_nlink--;
902 dp->i_flag |= IN_CHANGE;
903 error = vn_rdwr(UIO_READ, fvp, (caddr_t)&dirbuf,
904 sizeof (struct ext2fs_dirtemplate), (off_t)0,
905 UIO_SYSSPACE, IO_NODELOCKED,
906 tcnp->cn_cred, (size_t *)0, (struct proc *)0);
907 if (error == 0) {
908 namlen = dirbuf.dotdot_namlen;
909 if (namlen != 2 ||
910 dirbuf.dotdot_name[0] != '.' ||
911 dirbuf.dotdot_name[1] != '.') {
912 ufs_dirbad(xp, (doff_t)12,
913 "ext2fs_rename: mangled dir");
914 } else {
915 dirbuf.dotdot_ino = h2fs32(newparent);
916 (void) vn_rdwr(UIO_WRITE, fvp,
917 (caddr_t)&dirbuf,
918 sizeof (struct dirtemplate),
919 (off_t)0, UIO_SYSSPACE,
920 IO_NODELOCKED|IO_SYNC,
921 tcnp->cn_cred, (size_t *)0,
922 (struct proc *)0);
923 cache_purge(fdvp);
924 }
925 }
926 }
927 error = ext2fs_dirremove(fdvp, fcnp);
928 if (!error) {
929 xp->i_e2fs_nlink--;
930 xp->i_flag |= IN_CHANGE;
931 }
932 xp->i_flag &= ~IN_RENAME;
933 }
934 if (dp)
935 vput(fdvp);
936 if (xp)
937 vput(fvp);
938 vrele(ap->a_fvp);
939 return (error);
940
941 bad:
942 if (xp)
943 vput(ITOV(xp));
944 vput(ITOV(dp));
945 out:
946 if (doingdirectory)
947 ip->i_flag &= ~IN_RENAME;
948 if (vn_lock(fvp, LK_EXCLUSIVE, p) == 0) {
949 ip->i_e2fs_nlink--;
950 ip->i_flag |= IN_CHANGE;
951 vput(fvp);
952 } else
953 vrele(fvp);
954 return (error);
955 }
956
957 /*
958 * Mkdir system call
959 */
960 int
ext2fs_mkdir(v)961 ext2fs_mkdir(v)
962 void *v;
963 {
964 struct vop_mkdir_args /* {
965 struct vnode *a_dvp;
966 struct vnode **a_vpp;
967 struct componentname *a_cnp;
968 struct vattr *a_vap;
969 } */ *ap = v;
970 register struct vnode *dvp = ap->a_dvp;
971 register struct vattr *vap = ap->a_vap;
972 register struct componentname *cnp = ap->a_cnp;
973 register struct inode *ip, *dp;
974 struct vnode *tvp;
975 struct ext2fs_dirtemplate dirtemplate;
976 mode_t dmode;
977 int error;
978
979 #ifdef DIAGNOSTIC
980 if ((cnp->cn_flags & HASBUF) == 0)
981 panic("ext2fs_mkdir: no name");
982 #endif
983 dp = VTOI(dvp);
984 if ((nlink_t)dp->i_e2fs_nlink >= LINK_MAX) {
985 error = EMLINK;
986 goto out;
987 }
988 dmode = vap->va_mode & ACCESSPERMS;
989 dmode |= IFDIR;
990 /*
991 * Must simulate part of ext2fs_makeinode here to acquire the inode,
992 * but not have it entered in the parent directory. The entry is
993 * made later after writing "." and ".." entries.
994 */
995 if ((error = ext2fs_inode_alloc(dp, dmode, cnp->cn_cred, &tvp)) != 0)
996 goto out;
997 ip = VTOI(tvp);
998 ip->i_e2fs_uid = cnp->cn_cred->cr_uid;
999 ip->i_e2fs_gid = dp->i_e2fs_gid;
1000 ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
1001 ip->i_e2fs_mode = dmode;
1002 tvp->v_type = VDIR; /* Rest init'd in getnewvnode(). */
1003 ip->i_e2fs_nlink = 2;
1004 error = ext2fs_update(ip, NULL, NULL, 1);
1005
1006 /*
1007 * Bump link count in parent directory
1008 * to reflect work done below. Should
1009 * be done before reference is created
1010 * so reparation is possible if we crash.
1011 */
1012 dp->i_e2fs_nlink++;
1013 dp->i_flag |= IN_CHANGE;
1014 if ((error = ext2fs_update(dp, NULL, NULL, 1)) != 0)
1015 goto bad;
1016
1017 /* Initialize directory with "." and ".." from static template. */
1018 bzero(&dirtemplate, sizeof(dirtemplate));
1019 dirtemplate.dot_ino = h2fs32(ip->i_number);
1020 dirtemplate.dot_reclen = h2fs16(12);
1021 dirtemplate.dot_namlen = 1;
1022 if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 &&
1023 (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) {
1024 dirtemplate.dot_type = EXT2_FT_DIR;
1025 }
1026 dirtemplate.dot_name[0] = '.';
1027 dirtemplate.dotdot_ino = h2fs32(dp->i_number);
1028 dirtemplate.dotdot_reclen = h2fs16(VTOI(dvp)->i_e2fs->e2fs_bsize - 12);
1029 dirtemplate.dotdot_namlen = 2;
1030 if (ip->i_e2fs->e2fs.e2fs_rev > E2FS_REV0 &&
1031 (ip->i_e2fs->e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE)) {
1032 dirtemplate.dotdot_type = EXT2_FT_DIR;
1033 }
1034 dirtemplate.dotdot_name[0] = dirtemplate.dotdot_name[1] = '.';
1035 error = vn_rdwr(UIO_WRITE, tvp, (caddr_t)&dirtemplate,
1036 sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE,
1037 IO_NODELOCKED|IO_SYNC, cnp->cn_cred, (size_t *)0, (struct proc *)0);
1038 if (error) {
1039 dp->i_e2fs_nlink--;
1040 dp->i_flag |= IN_CHANGE;
1041 goto bad;
1042 }
1043 if (VTOI(dvp)->i_e2fs->e2fs_bsize >
1044 VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
1045 panic("ext2fs_mkdir: blksize"); /* XXX should grow with balloc() */
1046 else {
1047 error = ext2fs_setsize(ip, VTOI(dvp)->i_e2fs->e2fs_bsize);
1048 if (error) {
1049 dp->i_e2fs_nlink--;
1050 dp->i_flag |= IN_CHANGE;
1051 goto bad;
1052 }
1053 ip->i_flag |= IN_CHANGE;
1054 }
1055
1056 /* Directory set up, now install it's entry in the parent directory. */
1057 error = ext2fs_direnter(ip, dvp, cnp);
1058 if (error != 0) {
1059 dp->i_e2fs_nlink--;
1060 dp->i_flag |= IN_CHANGE;
1061 }
1062 bad:
1063 /*
1064 * No need to do an explicit VOP_TRUNCATE here, vrele will do this
1065 * for us because we set the link count to 0.
1066 */
1067 if (error) {
1068 ip->i_e2fs_nlink = 0;
1069 ip->i_flag |= IN_CHANGE;
1070 vput(tvp);
1071 } else
1072 *ap->a_vpp = tvp;
1073 out:
1074 pool_put(&namei_pool, cnp->cn_pnbuf);
1075 vput(dvp);
1076 return (error);
1077 }
1078
1079 /*
1080 * Rmdir system call.
1081 */
1082 int
ext2fs_rmdir(v)1083 ext2fs_rmdir(v)
1084 void *v;
1085 {
1086 struct vop_rmdir_args /* {
1087 struct vnode *a_dvp;
1088 struct vnode *a_vp;
1089 struct componentname *a_cnp;
1090 } */ *ap = v;
1091 register struct vnode *vp = ap->a_vp;
1092 register struct vnode *dvp = ap->a_dvp;
1093 register struct componentname *cnp = ap->a_cnp;
1094 register struct inode *ip, *dp;
1095 int error;
1096
1097 ip = VTOI(vp);
1098 dp = VTOI(dvp);
1099 /*
1100 * No rmdir "." please.
1101 */
1102 if (dp == ip) {
1103 vrele(dvp);
1104 vput(vp);
1105 return (EINVAL);
1106 }
1107 /*
1108 * Verify the directory is empty (and valid).
1109 * (Rmdir ".." won't be valid since
1110 * ".." will contain a reference to
1111 * the current directory and thus be
1112 * non-empty.)
1113 */
1114 error = 0;
1115 if (ip->i_e2fs_nlink != 2 ||
1116 !ext2fs_dirempty(ip, dp->i_number, cnp->cn_cred)) {
1117 error = ENOTEMPTY;
1118 goto out;
1119 }
1120 if ((dp->i_e2fs_flags & EXT2_APPEND) ||
1121 (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND))) {
1122 error = EPERM;
1123 goto out;
1124 }
1125 /*
1126 * Delete reference to directory before purging
1127 * inode. If we crash in between, the directory
1128 * will be reattached to lost+found,
1129 */
1130 error = ext2fs_dirremove(dvp, cnp);
1131 if (error != 0)
1132 goto out;
1133 dp->i_e2fs_nlink--;
1134 dp->i_flag |= IN_CHANGE;
1135 cache_purge(dvp);
1136 vput(dvp);
1137 dvp = NULL;
1138 /*
1139 * Truncate inode. The only stuff left
1140 * in the directory is "." and "..". The
1141 * "." reference is inconsequential since
1142 * we're quashing it. The ".." reference
1143 * has already been adjusted above. We've
1144 * removed the "." reference and the reference
1145 * in the parent directory, but there may be
1146 * other hard links so decrement by 2 and
1147 * worry about them later.
1148 */
1149 ip->i_e2fs_nlink -= 2;
1150 error = ext2fs_truncate(ip, (off_t)0, IO_SYNC, cnp->cn_cred);
1151 cache_purge(ITOV(ip));
1152 out:
1153 if (dvp)
1154 vput(dvp);
1155 vput(vp);
1156 return (error);
1157 }
1158
1159 /*
1160 * symlink -- make a symbolic link
1161 */
1162 int
ext2fs_symlink(v)1163 ext2fs_symlink(v)
1164 void *v;
1165 {
1166 struct vop_symlink_args /* {
1167 struct vnode *a_dvp;
1168 struct vnode **a_vpp;
1169 struct componentname *a_cnp;
1170 struct vattr *a_vap;
1171 char *a_target;
1172 } */ *ap = v;
1173 register struct vnode *vp, **vpp = ap->a_vpp;
1174 register struct inode *ip;
1175 int len, error;
1176
1177 error = ext2fs_makeinode(IFLNK | ap->a_vap->va_mode, ap->a_dvp,
1178 vpp, ap->a_cnp);
1179 if (error)
1180 return (error);
1181 vp = *vpp;
1182 len = strlen(ap->a_target);
1183 if (len < vp->v_mount->mnt_maxsymlinklen) {
1184 ip = VTOI(vp);
1185 bcopy(ap->a_target, (char *)ip->i_e2din.e2di_shortlink, len);
1186 error = ext2fs_setsize(ip, len);
1187 if (error)
1188 goto bad;
1189 ip->i_flag |= IN_CHANGE | IN_UPDATE;
1190 } else
1191 error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0,
1192 UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, NULL,
1193 (struct proc *)0);
1194 bad:
1195 vput(vp);
1196 return (error);
1197 }
1198
1199 /*
1200 * Return target name of a symbolic link
1201 */
1202 int
ext2fs_readlink(v)1203 ext2fs_readlink(v)
1204 void *v;
1205 {
1206 struct vop_readlink_args /* {
1207 struct vnode *a_vp;
1208 struct uio *a_uio;
1209 struct ucred *a_cred;
1210 } */ *ap = v;
1211 register struct vnode *vp = ap->a_vp;
1212 register struct inode *ip = VTOI(vp);
1213 int isize;
1214
1215 isize = ext2fs_size(ip);
1216 if (isize < vp->v_mount->mnt_maxsymlinklen ||
1217 (vp->v_mount->mnt_maxsymlinklen == 0 && ip->i_e2fs_nblock == 0)) {
1218 uiomove((char *)ip->i_e2din.e2di_shortlink, isize, ap->a_uio);
1219 return (0);
1220 }
1221 return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred));
1222 }
1223
1224 /*
1225 * Advisory record locking support
1226 */
1227 int
ext2fs_advlock(v)1228 ext2fs_advlock(v)
1229 void *v;
1230 {
1231 struct vop_advlock_args /* {
1232 struct vnode *a_vp;
1233 caddr_t a_id;
1234 int a_op;
1235 struct flock *a_fl;
1236 int a_flags;
1237 } */ *ap = v;
1238 register struct inode *ip = VTOI(ap->a_vp);
1239
1240 return (lf_advlock(&ip->i_lockf, ext2fs_size(ip), ap->a_id, ap->a_op,
1241 ap->a_fl, ap->a_flags));
1242 }
1243
1244 /*
1245 * Allocate a new inode.
1246 */
1247 int
ext2fs_makeinode(mode,dvp,vpp,cnp)1248 ext2fs_makeinode(mode, dvp, vpp, cnp)
1249 int mode;
1250 struct vnode *dvp;
1251 struct vnode **vpp;
1252 struct componentname *cnp;
1253 {
1254 register struct inode *ip, *pdir;
1255 struct vnode *tvp;
1256 int error;
1257
1258 pdir = VTOI(dvp);
1259 #ifdef DIAGNOSTIC
1260 if ((cnp->cn_flags & HASBUF) == 0)
1261 panic("ext2fs_makeinode: no name");
1262 #endif
1263 *vpp = NULL;
1264 if ((mode & IFMT) == 0)
1265 mode |= IFREG;
1266
1267 if ((error = ext2fs_inode_alloc(pdir, mode, cnp->cn_cred, &tvp))
1268 != 0) {
1269 pool_put(&namei_pool, cnp->cn_pnbuf);
1270 vput(dvp);
1271 return (error);
1272 }
1273 ip = VTOI(tvp);
1274 ip->i_e2fs_gid = pdir->i_e2fs_gid;
1275 ip->i_e2fs_uid = cnp->cn_cred->cr_uid;
1276 ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
1277 ip->i_e2fs_mode = mode;
1278 tvp->v_type = IFTOVT(mode); /* Rest init'd in getnewvnode(). */
1279 ip->i_e2fs_nlink = 1;
1280 if ((ip->i_e2fs_mode & ISGID) &&
1281 !groupmember(ip->i_e2fs_gid, cnp->cn_cred) &&
1282 suser_ucred(cnp->cn_cred))
1283 ip->i_e2fs_mode &= ~ISGID;
1284
1285 /*
1286 * Make sure inode goes to disk before directory entry.
1287 */
1288 if ((error = ext2fs_update(ip, NULL, NULL, 1)) != 0)
1289 goto bad;
1290 error = ext2fs_direnter(ip, dvp, cnp);
1291 if (error != 0)
1292 goto bad;
1293 if ((cnp->cn_flags & SAVESTART) == 0)
1294 pool_put(&namei_pool, cnp->cn_pnbuf);
1295 vput(dvp);
1296 *vpp = tvp;
1297 return (0);
1298
1299 bad:
1300 /*
1301 * Write error occurred trying to update the inode
1302 * or the directory so must deallocate the inode.
1303 */
1304 pool_put(&namei_pool, cnp->cn_pnbuf);
1305 vput(dvp);
1306 ip->i_e2fs_nlink = 0;
1307 ip->i_flag |= IN_CHANGE;
1308 tvp->v_type = VNON;
1309 vput(tvp);
1310 return (error);
1311 }
1312
1313 /*
1314 * Synch an open file.
1315 */
1316 /* ARGSUSED */
1317 int
ext2fs_fsync(v)1318 ext2fs_fsync(v)
1319 void *v;
1320 {
1321 struct vop_fsync_args /* {
1322 struct vnode *a_vp;
1323 struct ucred *a_cred;
1324 int a_waitfor;
1325 struct proc *a_p;
1326 } */ *ap = v;
1327 register struct vnode *vp = ap->a_vp;
1328
1329 vflushbuf(vp, ap->a_waitfor == MNT_WAIT);
1330 return (ext2fs_update(VTOI(ap->a_vp), NULL, NULL,
1331 ap->a_waitfor == MNT_WAIT));
1332 }
1333
1334 /*
1335 * Reclaim an inode so that it can be used for other purposes.
1336 */
1337 int
ext2fs_reclaim(v)1338 ext2fs_reclaim(v)
1339 void *v;
1340 {
1341 struct vop_reclaim_args /* {
1342 struct vnode *a_vp;
1343 } */ *ap = v;
1344 register struct vnode *vp = ap->a_vp;
1345 struct inode *ip;
1346 extern int prtactive;
1347
1348 if (prtactive && vp->v_usecount != 0)
1349 vprint("ext2fs_reclaim: pushing active", vp);
1350
1351 /*
1352 * Remove the inode from its hash chain.
1353 */
1354 ip = VTOI(vp);
1355 ufs_ihashrem(ip);
1356
1357 /*
1358 * Purge old data structures associated with the inode.
1359 */
1360 cache_purge(vp);
1361 if (ip->i_devvp)
1362 vrele(ip->i_devvp);
1363
1364 FREE(vp->v_data, M_EXT2FSNODE);
1365 vp->v_data = NULL;
1366
1367 return (0);
1368 }
1369
1370 /* Global vfs data structures for ext2fs. */
1371 int (**ext2fs_vnodeop_p)(void *);
1372 struct vnodeopv_entry_desc ext2fs_vnodeop_entries[] = {
1373 { &vop_default_desc, vn_default_error },
1374 { &vop_lookup_desc, ext2fs_lookup }, /* lookup */
1375 { &vop_create_desc, ext2fs_create }, /* create */
1376 { &vop_mknod_desc, ext2fs_mknod }, /* mknod */
1377 { &vop_open_desc, ext2fs_open }, /* open */
1378 { &vop_close_desc, ufs_close }, /* close */
1379 { &vop_access_desc, ext2fs_access }, /* access */
1380 { &vop_getattr_desc, ext2fs_getattr }, /* getattr */
1381 { &vop_setattr_desc, ext2fs_setattr }, /* setattr */
1382 { &vop_read_desc, ext2fs_read }, /* read */
1383 { &vop_write_desc, ext2fs_write }, /* write */
1384 { &vop_lease_desc, ufs_lease_check }, /* lease */
1385 { &vop_ioctl_desc, ufs_ioctl }, /* ioctl */
1386 { &vop_poll_desc, ufs_poll }, /* poll */
1387 { &vop_kqfilter_desc, vop_generic_kqfilter }, /* kqfilter */
1388 { &vop_fsync_desc, ext2fs_fsync }, /* fsync */
1389 { &vop_remove_desc, ext2fs_remove }, /* remove */
1390 { &vop_link_desc, ext2fs_link }, /* link */
1391 { &vop_rename_desc, ext2fs_rename }, /* rename */
1392 { &vop_mkdir_desc, ext2fs_mkdir }, /* mkdir */
1393 { &vop_rmdir_desc, ext2fs_rmdir }, /* rmdir */
1394 { &vop_symlink_desc, ext2fs_symlink }, /* symlink */
1395 { &vop_readdir_desc, ext2fs_readdir }, /* readdir */
1396 { &vop_readlink_desc, ext2fs_readlink },/* readlink */
1397 { &vop_abortop_desc, vop_generic_abortop }, /* abortop */
1398 { &vop_inactive_desc, ext2fs_inactive },/* inactive */
1399 { &vop_reclaim_desc, ext2fs_reclaim }, /* reclaim */
1400 { &vop_lock_desc, ufs_lock }, /* lock */
1401 { &vop_unlock_desc, ufs_unlock }, /* unlock */
1402 { &vop_bmap_desc, ext2fs_bmap }, /* bmap */
1403 { &vop_strategy_desc, ufs_strategy }, /* strategy */
1404 { &vop_print_desc, ufs_print }, /* print */
1405 { &vop_islocked_desc, ufs_islocked }, /* islocked */
1406 { &vop_pathconf_desc, ufs_pathconf }, /* pathconf */
1407 { &vop_advlock_desc, ext2fs_advlock }, /* advlock */
1408 { &vop_bwrite_desc, vop_generic_bwrite }, /* bwrite */
1409 { NULL, NULL }
1410 };
1411 struct vnodeopv_desc ext2fs_vnodeop_opv_desc =
1412 { &ext2fs_vnodeop_p, ext2fs_vnodeop_entries };
1413
1414 int (**ext2fs_specop_p)(void *);
1415 struct vnodeopv_entry_desc ext2fs_specop_entries[] = {
1416 { &vop_default_desc, spec_vnoperate },
1417 { &vop_close_desc, ufsspec_close }, /* close */
1418 { &vop_access_desc, ext2fs_access }, /* access */
1419 { &vop_getattr_desc, ext2fs_getattr }, /* getattr */
1420 { &vop_setattr_desc, ext2fs_setattr }, /* setattr */
1421 { &vop_read_desc, ufsspec_read }, /* read */
1422 { &vop_write_desc, ufsspec_write }, /* write */
1423 { &vop_fsync_desc, ext2fs_fsync }, /* fsync */
1424 { &vop_inactive_desc, ext2fs_inactive },/* inactive */
1425 { &vop_reclaim_desc, ext2fs_reclaim }, /* reclaim */
1426 { &vop_lock_desc, ufs_lock }, /* lock */
1427 { &vop_unlock_desc, ufs_unlock }, /* unlock */
1428 { &vop_print_desc, ufs_print }, /* print */
1429 { &vop_islocked_desc, ufs_islocked }, /* islocked */
1430 { NULL, NULL }
1431 };
1432 struct vnodeopv_desc ext2fs_specop_opv_desc =
1433 { &ext2fs_specop_p, ext2fs_specop_entries };
1434
1435 #ifdef FIFO
1436 int (**ext2fs_fifoop_p)(void *);
1437 struct vnodeopv_entry_desc ext2fs_fifoop_entries[] = {
1438 { &vop_default_desc, fifo_vnoperate },
1439 { &vop_close_desc, ufsfifo_close }, /* close */
1440 { &vop_access_desc, ext2fs_access }, /* access */
1441 { &vop_getattr_desc, ext2fs_getattr }, /* getattr */
1442 { &vop_setattr_desc, ext2fs_setattr }, /* setattr */
1443 { &vop_read_desc, ufsfifo_read }, /* read */
1444 { &vop_write_desc, ufsfifo_write }, /* write */
1445 { &vop_fsync_desc, ext2fs_fsync }, /* fsync */
1446 { &vop_inactive_desc, ext2fs_inactive },/* inactive */
1447 { &vop_reclaim_desc, ext2fsfifo_reclaim }, /* reclaim */
1448 { &vop_lock_desc, ufs_lock }, /* lock */
1449 { &vop_unlock_desc, ufs_unlock }, /* unlock */
1450 { &vop_print_desc, ufs_print }, /* print */
1451 { &vop_islocked_desc, ufs_islocked }, /* islocked */
1452 { &vop_bwrite_desc, vop_generic_bwrite }, /* bwrite */
1453 { NULL, NULL }
1454 };
1455 struct vnodeopv_desc ext2fs_fifoop_opv_desc =
1456 { &ext2fs_fifoop_p, ext2fs_fifoop_entries };
1457
1458 int
ext2fsfifo_reclaim(void * v)1459 ext2fsfifo_reclaim(void *v)
1460 {
1461 fifo_reclaim(v);
1462 return (ext2fs_reclaim(v));
1463 }
1464 #endif /* FIFO */
1465