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 * 4. Neither the name of the University nor the names of its contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
39 *
40 * @(#)ufs_vnops.c 8.7 (Berkeley) 2/3/94
41 * @(#)ufs_vnops.c 8.27 (Berkeley) 5/27/95
42 * $FreeBSD: releng/9.0/sys/fs/ext2fs/ext2_vnops.c 221166 2011-04-28 14:27:17Z jhb $
43 */
44
45 #include "opt_suiddir.h"
46
47 #include <sys/param.h>
48 #include <sys/systm.h>
49 #include <sys/kernel.h>
50 #include <sys/fcntl.h>
51 #include <sys/stat.h>
52 #include <sys/bio.h>
53 #include <sys/buf.h>
54 #include <sys/endian.h>
55 #include <sys/priv.h>
56 #include <sys/mount.h>
57 #include <sys/unistd.h>
58 #include <sys/time.h>
59 #include <sys/vnode.h>
60 #include <sys/namei.h>
61 #include <sys/lockf.h>
62 #include <sys/event.h>
63 #include <sys/conf.h>
64 #include <sys/file.h>
65
66 #include <vm/vm.h>
67 #include <vm/vm_extern.h>
68 #include <vm/vnode_pager.h>
69
70 #include <fs/fifofs/fifo.h>
71
72 #include <ufs/ufs/dir.h>
73
74 #include <fs/ext2fs/fs.h>
75 #include <fs/ext2fs/inode.h>
76 #include <fs/ext2fs/ext2_extern.h>
77 #include <fs/ext2fs/ext2fs.h>
78 #include <fs/ext2fs/ext2_dinode.h>
79 #include <fs/ext2fs/ext2_dir.h>
80 #include <fs/ext2fs/ext2_mount.h>
81
82 static int ext2_makeinode(int mode, struct vnode *, struct vnode **, struct componentname *);
83 static void ext2_itimes_locked(struct vnode *);
84
85 static vop_access_t ext2_access;
86 static int ext2_chmod(struct vnode *, int, struct ucred *, struct thread *);
87 static int ext2_chown(struct vnode *, uid_t, gid_t, struct ucred *,
88 struct thread *);
89 static vop_close_t ext2_close;
90 static vop_create_t ext2_create;
91 static vop_fsync_t ext2_fsync;
92 static vop_getattr_t ext2_getattr;
93 static vop_link_t ext2_link;
94 static vop_mkdir_t ext2_mkdir;
95 static vop_mknod_t ext2_mknod;
96 static vop_open_t ext2_open;
97 static vop_pathconf_t ext2_pathconf;
98 static vop_print_t ext2_print;
99 static vop_read_t ext2_read;
100 static vop_readlink_t ext2_readlink;
101 static vop_remove_t ext2_remove;
102 static vop_rename_t ext2_rename;
103 static vop_rmdir_t ext2_rmdir;
104 static vop_setattr_t ext2_setattr;
105 static vop_strategy_t ext2_strategy;
106 static vop_symlink_t ext2_symlink;
107 static vop_write_t ext2_write;
108 static vop_vptofh_t ext2_vptofh;
109 static vop_close_t ext2fifo_close;
110 static vop_kqfilter_t ext2fifo_kqfilter;
111
112 /* Global vfs data structures for ext2. */
113 struct vop_vector ext2_vnodeops = {
114 .vop_default = &default_vnodeops,
115 .vop_access = ext2_access,
116 .vop_bmap = ext2_bmap,
117 .vop_cachedlookup = ext2_lookup,
118 .vop_close = ext2_close,
119 .vop_create = ext2_create,
120 .vop_fsync = ext2_fsync,
121 .vop_getattr = ext2_getattr,
122 .vop_inactive = ext2_inactive,
123 .vop_link = ext2_link,
124 .vop_lookup = vfs_cache_lookup,
125 .vop_mkdir = ext2_mkdir,
126 .vop_mknod = ext2_mknod,
127 .vop_open = ext2_open,
128 .vop_pathconf = ext2_pathconf,
129 .vop_poll = vop_stdpoll,
130 .vop_print = ext2_print,
131 .vop_read = ext2_read,
132 .vop_readdir = ext2_readdir,
133 .vop_readlink = ext2_readlink,
134 .vop_reallocblks = ext2_reallocblks,
135 .vop_reclaim = ext2_reclaim,
136 .vop_remove = ext2_remove,
137 .vop_rename = ext2_rename,
138 .vop_rmdir = ext2_rmdir,
139 .vop_setattr = ext2_setattr,
140 .vop_strategy = ext2_strategy,
141 .vop_symlink = ext2_symlink,
142 .vop_write = ext2_write,
143 .vop_vptofh = ext2_vptofh,
144 };
145
146 struct vop_vector ext2_fifoops = {
147 .vop_default = &fifo_specops,
148 .vop_access = ext2_access,
149 .vop_close = ext2fifo_close,
150 .vop_fsync = ext2_fsync,
151 .vop_getattr = ext2_getattr,
152 .vop_inactive = ext2_inactive,
153 .vop_kqfilter = ext2fifo_kqfilter,
154 .vop_print = ext2_print,
155 .vop_read = VOP_PANIC,
156 .vop_reclaim = ext2_reclaim,
157 .vop_setattr = ext2_setattr,
158 .vop_write = VOP_PANIC,
159 .vop_vptofh = ext2_vptofh,
160 };
161
162 #include <fs/ext2fs/ext2_readwrite.c>
163
164 /*
165 * A virgin directory (no blushing please).
166 * Note that the type and namlen fields are reversed relative to ext2.
167 * Also, we don't use `struct odirtemplate', since it would just cause
168 * endianness problems.
169 */
170 static struct dirtemplate mastertemplate = {
171 0, 12, 1, EXT2_FT_DIR, ".",
172 0, DIRBLKSIZ - 12, 2, EXT2_FT_DIR, ".."
173 };
174 static struct dirtemplate omastertemplate = {
175 0, 12, 1, EXT2_FT_UNKNOWN, ".",
176 0, DIRBLKSIZ - 12, 2, EXT2_FT_UNKNOWN, ".."
177 };
178
179 static void
180 ext2_itimes_locked(struct vnode *vp)
181 {
182 struct inode *ip;
183 struct timespec ts;
184
185 ASSERT_VI_LOCKED(vp, __func__);
186
187 ip = VTOI(vp);
188 if ((ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_UPDATE)) == 0)
189 return;
190 if ((vp->v_type == VBLK || vp->v_type == VCHR))
191 ip->i_flag |= IN_LAZYMOD;
192 else
193 ip->i_flag |= IN_MODIFIED;
194 if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
195 vfs_timestamp(&ts);
196 if (ip->i_flag & IN_ACCESS) {
197 ip->i_atime = ts.tv_sec;
198 ip->i_atimensec = ts.tv_nsec;
199 }
200 if (ip->i_flag & IN_UPDATE) {
201 ip->i_mtime = ts.tv_sec;
202 ip->i_mtimensec = ts.tv_nsec;
203 ip->i_modrev++;
204 }
205 if (ip->i_flag & IN_CHANGE) {
206 ip->i_ctime = ts.tv_sec;
207 ip->i_ctimensec = ts.tv_nsec;
208 }
209 }
210 ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE);
211 }
212
213 void
214 ext2_itimes(struct vnode *vp)
215 {
216
217 VI_LOCK(vp);
218 ext2_itimes_locked(vp);
219 VI_UNLOCK(vp);
220 }
221
222 /*
223 * Create a regular file
224 */
225 static int
226 ext2_create(ap)
227 struct vop_create_args /* {
228 struct vnode *a_dvp;
229 struct vnode **a_vpp;
230 struct componentname *a_cnp;
231 struct vattr *a_vap;
232 } */ *ap;
233 {
234 int error;
235
236 error =
237 ext2_makeinode(MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode),
238 ap->a_dvp, ap->a_vpp, ap->a_cnp);
239 if (error)
240 return (error);
241 return (0);
242 }
243
244 static int
245 ext2_open(ap)
246 struct vop_open_args /* {
247 struct vnode *a_vp;
248 int a_mode;
249 struct ucred *a_cred;
250 struct thread *a_td;
251 } */ *ap;
252 {
253
254 if (ap->a_vp->v_type == VBLK || ap->a_vp->v_type == VCHR)
255 return (EOPNOTSUPP);
256
257 /*
258 * Files marked append-only must be opened for appending.
259 */
260 if ((VTOI(ap->a_vp)->i_flags & APPEND) &&
261 (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
262 return (EPERM);
263
264 vnode_create_vobject(ap->a_vp, VTOI(ap->a_vp)->i_size, ap->a_td);
265
266 return (0);
267 }
268
269 /*
270 * Close called.
271 *
272 * Update the times on the inode.
273 */
274 static int
275 ext2_close(ap)
276 struct vop_close_args /* {
277 struct vnode *a_vp;
278 int a_fflag;
279 struct ucred *a_cred;
280 struct thread *a_td;
281 } */ *ap;
282 {
283 struct vnode *vp = ap->a_vp;
284
285 VI_LOCK(vp);
286 if (vp->v_usecount > 1)
287 ext2_itimes_locked(vp);
288 VI_UNLOCK(vp);
289 return (0);
290 }
291
292 static int
293 ext2_access(ap)
294 struct vop_access_args /* {
295 struct vnode *a_vp;
296 accmode_t a_accmode;
297 struct ucred *a_cred;
298 struct thread *a_td;
299 } */ *ap;
300 {
301 struct vnode *vp = ap->a_vp;
302 struct inode *ip = VTOI(vp);
303 accmode_t accmode = ap->a_accmode;
304 int error;
305
306 if (vp->v_type == VBLK || vp->v_type == VCHR)
307 return (EOPNOTSUPP);
308
309 /*
310 * Disallow write attempts on read-only file systems;
311 * unless the file is a socket, fifo, or a block or
312 * character device resident on the file system.
313 */
314 if (accmode & VWRITE) {
315 switch (vp->v_type) {
316 case VDIR:
317 case VLNK:
318 case VREG:
319 if (vp->v_mount->mnt_flag & MNT_RDONLY)
320 return (EROFS);
321 break;
322 default:
323 break;
324 }
325 }
326
327 /* If immutable bit set, nobody gets to write it. */
328 if ((accmode & VWRITE) && (ip->i_flags & (SF_IMMUTABLE | SF_SNAPSHOT)))
329 return (EPERM);
330
331 error = vaccess(vp->v_type, ip->i_mode, ip->i_uid, ip->i_gid,
332 ap->a_accmode, ap->a_cred, NULL);
333 return (error);
334 }
335
336 static int
337 ext2_getattr(ap)
338 struct vop_getattr_args /* {
339 struct vnode *a_vp;
340 struct vattr *a_vap;
341 struct ucred *a_cred;
342 } */ *ap;
343 {
344 struct vnode *vp = ap->a_vp;
345 struct inode *ip = VTOI(vp);
346 struct vattr *vap = ap->a_vap;
347
348 ext2_itimes(vp);
349 /*
350 * Copy from inode table
351 */
352 vap->va_fsid = dev2udev(ip->i_devvp->v_rdev);
353 vap->va_fileid = ip->i_number;
354 vap->va_mode = ip->i_mode & ~IFMT;
355 vap->va_nlink = ip->i_nlink;
356 vap->va_uid = ip->i_uid;
357 vap->va_gid = ip->i_gid;
358 vap->va_rdev = ip->i_rdev;
359 vap->va_size = ip->i_size;
360 vap->va_atime.tv_sec = ip->i_atime;
361 vap->va_atime.tv_nsec = ip->i_atimensec;
362 vap->va_mtime.tv_sec = ip->i_mtime;
363 vap->va_mtime.tv_nsec = ip->i_mtimensec;
364 vap->va_ctime.tv_sec = ip->i_ctime;
365 vap->va_ctime.tv_nsec = ip->i_ctimensec;
366 vap->va_flags = ip->i_flags;
367 vap->va_gen = ip->i_gen;
368 vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize;
369 vap->va_bytes = dbtob((u_quad_t)ip->i_blocks);
370 vap->va_type = IFTOVT(ip->i_mode);
371 vap->va_filerev = ip->i_modrev;
372 return (0);
373 }
374
375 /*
376 * Set attribute vnode op. called from several syscalls
377 */
378 static int
379 ext2_setattr(ap)
380 struct vop_setattr_args /* {
381 struct vnode *a_vp;
382 struct vattr *a_vap;
383 struct ucred *a_cred;
384 } */ *ap;
385 {
386 struct vattr *vap = ap->a_vap;
387 struct vnode *vp = ap->a_vp;
388 struct inode *ip = VTOI(vp);
389 struct ucred *cred = ap->a_cred;
390 struct thread *td = curthread;
391 int error;
392
393 /*
394 * Check for unsettable attributes.
395 */
396 if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
397 (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
398 (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
399 ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
400 return (EINVAL);
401 }
402 if (vap->va_flags != VNOVAL) {
403 /* Disallow flags not supported by ext2fs. */
404 if(vap->va_flags & ~(SF_APPEND | SF_IMMUTABLE | UF_NODUMP))
405 return (EOPNOTSUPP);
406
407 if (vp->v_mount->mnt_flag & MNT_RDONLY)
408 return (EROFS);
409 /*
410 * Callers may only modify the file flags on objects they
411 * have VADMIN rights for.
412 */
413 if ((error = VOP_ACCESS(vp, VADMIN, cred, td)))
414 return (error);
415 /*
416 * Unprivileged processes and privileged processes in
417 * jail() are not permitted to unset system flags, or
418 * modify flags if any system flags are set.
419 * Privileged non-jail processes may not modify system flags
420 * if securelevel > 0 and any existing system flags are set.
421 */
422 if (!priv_check_cred(cred, PRIV_VFS_SYSFLAGS, 0)) {
423 if (ip->i_flags
424 & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) {
425 error = securelevel_gt(cred, 0);
426 if (error)
427 return (error);
428 }
429 ip->i_flags = vap->va_flags;
430 } else {
431 if (ip->i_flags
432 & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND) ||
433 (vap->va_flags & UF_SETTABLE) != vap->va_flags)
434 return (EPERM);
435 ip->i_flags &= SF_SETTABLE;
436 ip->i_flags |= (vap->va_flags & UF_SETTABLE);
437 }
438 ip->i_flag |= IN_CHANGE;
439 if (vap->va_flags & (IMMUTABLE | APPEND))
440 return (0);
441 }
442 if (ip->i_flags & (IMMUTABLE | APPEND))
443 return (EPERM);
444 /*
445 * Go through the fields and update iff not VNOVAL.
446 */
447 if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
448 if (vp->v_mount->mnt_flag & MNT_RDONLY)
449 return (EROFS);
450 if ((error = ext2_chown(vp, vap->va_uid, vap->va_gid, cred,
451 td)) != 0)
452 return (error);
453 }
454 if (vap->va_size != VNOVAL) {
455 /*
456 * Disallow write attempts on read-only file systems;
457 * unless the file is a socket, fifo, or a block or
458 * character device resident on the file system.
459 */
460 switch (vp->v_type) {
461 case VDIR:
462 return (EISDIR);
463 case VLNK:
464 case VREG:
465 if (vp->v_mount->mnt_flag & MNT_RDONLY)
466 return (EROFS);
467 break;
468 default:
469 break;
470 }
471 if ((error = ext2_truncate(vp, vap->va_size, 0, cred, td)) != 0)
472 return (error);
473 }
474 if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
475 if (vp->v_mount->mnt_flag & MNT_RDONLY)
476 return (EROFS);
477 /*
478 * From utimes(2):
479 * If times is NULL, ... The caller must be the owner of
480 * the file, have permission to write the file, or be the
481 * super-user.
482 * If times is non-NULL, ... The caller must be the owner of
483 * the file or be the super-user.
484 */
485 if ((error = VOP_ACCESS(vp, VADMIN, cred, td)) &&
486 ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
487 (error = VOP_ACCESS(vp, VWRITE, cred, td))))
488 return (error);
489 if (vap->va_atime.tv_sec != VNOVAL)
490 ip->i_flag |= IN_ACCESS;
491 if (vap->va_mtime.tv_sec != VNOVAL)
492 ip->i_flag |= IN_CHANGE | IN_UPDATE;
493 ext2_itimes(vp);
494 if (vap->va_atime.tv_sec != VNOVAL) {
495 ip->i_atime = vap->va_atime.tv_sec;
496 ip->i_atimensec = vap->va_atime.tv_nsec;
497 }
498 if (vap->va_mtime.tv_sec != VNOVAL) {
499 ip->i_mtime = vap->va_mtime.tv_sec;
500 ip->i_mtimensec = vap->va_mtime.tv_nsec;
501 }
502 error = ext2_update(vp, 0);
503 if (error)
504 return (error);
505 }
506 error = 0;
507 if (vap->va_mode != (mode_t)VNOVAL) {
508 if (vp->v_mount->mnt_flag & MNT_RDONLY)
509 return (EROFS);
510 error = ext2_chmod(vp, (int)vap->va_mode, cred, td);
511 }
512 return (error);
513 }
514
515 /*
516 * Change the mode on a file.
517 * Inode must be locked before calling.
518 */
519 static int
520 ext2_chmod(vp, mode, cred, td)
521 struct vnode *vp;
522 int mode;
523 struct ucred *cred;
524 struct thread *td;
525 {
526 struct inode *ip = VTOI(vp);
527 int error;
528
529 /*
530 * To modify the permissions on a file, must possess VADMIN
531 * for that file.
532 */
533 if ((error = VOP_ACCESS(vp, VADMIN, cred, td)))
534 return (error);
535 /*
536 * Privileged processes may set the sticky bit on non-directories,
537 * as well as set the setgid bit on a file with a group that the
538 * process is not a member of.
539 */
540 if (vp->v_type != VDIR && (mode & S_ISTXT)) {
541 error = priv_check_cred(cred, PRIV_VFS_STICKYFILE, 0);
542 if (error)
543 return (EFTYPE);
544 }
545 if (!groupmember(ip->i_gid, cred) && (mode & ISGID)) {
546 error = priv_check_cred(cred, PRIV_VFS_SETGID, 0);
547 if (error)
548 return (error);
549 }
550 ip->i_mode &= ~ALLPERMS;
551 ip->i_mode |= (mode & ALLPERMS);
552 ip->i_flag |= IN_CHANGE;
553 return (0);
554 }
555
556 /*
557 * Perform chown operation on inode ip;
558 * inode must be locked prior to call.
559 */
560 static int
561 ext2_chown(vp, uid, gid, cred, td)
562 struct vnode *vp;
563 uid_t uid;
564 gid_t gid;
565 struct ucred *cred;
566 struct thread *td;
567 {
568 struct inode *ip = VTOI(vp);
569 uid_t ouid;
570 gid_t ogid;
571 int error = 0;
572
573 if (uid == (uid_t)VNOVAL)
574 uid = ip->i_uid;
575 if (gid == (gid_t)VNOVAL)
576 gid = ip->i_gid;
577 /*
578 * To modify the ownership of a file, must possess VADMIN
579 * for that file.
580 */
581 if ((error = VOP_ACCESS(vp, VADMIN, cred, td)))
582 return (error);
583 /*
584 * To change the owner of a file, or change the group of a file
585 * to a group of which we are not a member, the caller must
586 * have privilege.
587 */
588 if (uid != ip->i_uid || (gid != ip->i_gid &&
589 !groupmember(gid, cred))) {
590 error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0);
591 if (error)
592 return (error);
593 }
594 ogid = ip->i_gid;
595 ouid = ip->i_uid;
596 ip->i_gid = gid;
597 ip->i_uid = uid;
598 ip->i_flag |= IN_CHANGE;
599 if ((ip->i_mode & (ISUID | ISGID)) && (ouid != uid || ogid != gid)) {
600 if (priv_check_cred(cred, PRIV_VFS_RETAINSUGID, 0) != 0)
601 ip->i_mode &= ~(ISUID | ISGID);
602 }
603 return (0);
604 }
605
606 /*
607 * Synch an open file.
608 */
609 /* ARGSUSED */
610 static int
611 ext2_fsync(ap)
612 struct vop_fsync_args /* {
613 struct vnode *a_vp;
614 struct ucred *a_cred;
615 int a_waitfor;
616 struct thread *a_td;
617 } */ *ap;
618 {
619 /*
620 * Flush all dirty buffers associated with a vnode.
621 */
622
623 vop_stdfsync(ap);
624
625 return (ext2_update(ap->a_vp, ap->a_waitfor == MNT_WAIT));
626 }
627
628 /*
629 * Mknod vnode call
630 */
631 /* ARGSUSED */
632 static int
633 ext2_mknod(ap)
634 struct vop_mknod_args /* {
635 struct vnode *a_dvp;
636 struct vnode **a_vpp;
637 struct componentname *a_cnp;
638 struct vattr *a_vap;
639 } */ *ap;
640 {
641 struct vattr *vap = ap->a_vap;
642 struct vnode **vpp = ap->a_vpp;
643 struct inode *ip;
644 ino_t ino;
645 int error;
646
647 error = ext2_makeinode(MAKEIMODE(vap->va_type, vap->va_mode),
648 ap->a_dvp, vpp, ap->a_cnp);
649 if (error)
650 return (error);
651 ip = VTOI(*vpp);
652 ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
653 if (vap->va_rdev != VNOVAL) {
654 /*
655 * Want to be able to use this to make badblock
656 * inodes, so don't truncate the dev number.
657 */
658 ip->i_rdev = vap->va_rdev;
659 }
660 /*
661 * Remove inode, then reload it through VFS_VGET so it is
662 * checked to see if it is an alias of an existing entry in
663 * the inode cache. XXX I don't believe this is necessary now.
664 */
665 (*vpp)->v_type = VNON;
666 ino = ip->i_number; /* Save this before vgone() invalidates ip. */
667 vgone(*vpp);
668 vput(*vpp);
669 error = VFS_VGET(ap->a_dvp->v_mount, ino, LK_EXCLUSIVE, vpp);
670 if (error) {
671 *vpp = NULL;
672 return (error);
673 }
674 return (0);
675 }
676
677 static int
678 ext2_remove(ap)
679 struct vop_remove_args /* {
680 struct vnode *a_dvp;
681 struct vnode *a_vp;
682 struct componentname *a_cnp;
683 } */ *ap;
684 {
685 struct inode *ip;
686 struct vnode *vp = ap->a_vp;
687 struct vnode *dvp = ap->a_dvp;
688 int error;
689
690 ip = VTOI(vp);
691 if ((ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
692 (VTOI(dvp)->i_flags & APPEND)) {
693 error = EPERM;
694 goto out;
695 }
696 error = ext2_dirremove(dvp, ap->a_cnp);
697 if (error == 0) {
698 ip->i_nlink--;
699 ip->i_flag |= IN_CHANGE;
700 }
701 out:
702 return (error);
703 }
704
705 /*
706 * link vnode call
707 */
708 static int
709 ext2_link(ap)
710 struct vop_link_args /* {
711 struct vnode *a_tdvp;
712 struct vnode *a_vp;
713 struct componentname *a_cnp;
714 } */ *ap;
715 {
716 struct vnode *vp = ap->a_vp;
717 struct vnode *tdvp = ap->a_tdvp;
718 struct componentname *cnp = ap->a_cnp;
719 struct inode *ip;
720 int error;
721
722 #ifdef DIAGNOSTIC
723 if ((cnp->cn_flags & HASBUF) == 0)
724 panic("ext2_link: no name");
725 #endif
726 if (tdvp->v_mount != vp->v_mount) {
727 error = EXDEV;
728 goto out;
729 }
730 ip = VTOI(vp);
731 if ((nlink_t)ip->i_nlink >= LINK_MAX) {
732 error = EMLINK;
733 goto out;
734 }
735 if (ip->i_flags & (IMMUTABLE | APPEND)) {
736 error = EPERM;
737 goto out;
738 }
739 ip->i_nlink++;
740 ip->i_flag |= IN_CHANGE;
741 error = ext2_update(vp, !DOINGASYNC(vp));
742 if (!error)
743 error = ext2_direnter(ip, tdvp, cnp);
744 if (error) {
745 ip->i_nlink--;
746 ip->i_flag |= IN_CHANGE;
747 }
748 out:
749 return (error);
750 }
751
752 /*
753 * Rename system call.
754 * rename("foo", "bar");
755 * is essentially
756 * unlink("bar");
757 * link("foo", "bar");
758 * unlink("foo");
759 * but ``atomically''. Can't do full commit without saving state in the
760 * inode on disk which isn't feasible at this time. Best we can do is
761 * always guarantee the target exists.
762 *
763 * Basic algorithm is:
764 *
765 * 1) Bump link count on source while we're linking it to the
766 * target. This also ensure the inode won't be deleted out
767 * from underneath us while we work (it may be truncated by
768 * a concurrent `trunc' or `open' for creation).
769 * 2) Link source to destination. If destination already exists,
770 * delete it first.
771 * 3) Unlink source reference to inode if still around. If a
772 * directory was moved and the parent of the destination
773 * is different from the source, patch the ".." entry in the
774 * directory.
775 */
776 static int
777 ext2_rename(ap)
778 struct vop_rename_args /* {
779 struct vnode *a_fdvp;
780 struct vnode *a_fvp;
781 struct componentname *a_fcnp;
782 struct vnode *a_tdvp;
783 struct vnode *a_tvp;
784 struct componentname *a_tcnp;
785 } */ *ap;
786 {
787 struct vnode *tvp = ap->a_tvp;
788 struct vnode *tdvp = ap->a_tdvp;
789 struct vnode *fvp = ap->a_fvp;
790 struct vnode *fdvp = ap->a_fdvp;
791 struct componentname *tcnp = ap->a_tcnp;
792 struct componentname *fcnp = ap->a_fcnp;
793 struct inode *ip, *xp, *dp;
794 struct dirtemplate dirbuf;
795 int doingdirectory = 0, oldparent = 0, newparent = 0;
796 int error = 0;
797 u_char namlen;
798
799 #ifdef DIAGNOSTIC
800 if ((tcnp->cn_flags & HASBUF) == 0 ||
801 (fcnp->cn_flags & HASBUF) == 0)
802 panic("ext2_rename: no name");
803 #endif
804 /*
805 * Check for cross-device rename.
806 */
807 if ((fvp->v_mount != tdvp->v_mount) ||
808 (tvp && (fvp->v_mount != tvp->v_mount))) {
809 error = EXDEV;
810 abortit:
811 if (tdvp == tvp)
812 vrele(tdvp);
813 else
814 vput(tdvp);
815 if (tvp)
816 vput(tvp);
817 vrele(fdvp);
818 vrele(fvp);
819 return (error);
820 }
821
822 if (tvp && ((VTOI(tvp)->i_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
823 (VTOI(tdvp)->i_flags & APPEND))) {
824 error = EPERM;
825 goto abortit;
826 }
827
828 /*
829 * Renaming a file to itself has no effect. The upper layers should
830 * not call us in that case. Temporarily just warn if they do.
831 */
832 if (fvp == tvp) {
833 printf("ext2_rename: fvp == tvp (can't happen)\n");
834 error = 0;
835 goto abortit;
836 }
837
838 if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
839 goto abortit;
840 dp = VTOI(fdvp);
841 ip = VTOI(fvp);
842 if (ip->i_nlink >= LINK_MAX) {
843 VOP_UNLOCK(fvp, 0);
844 error = EMLINK;
845 goto abortit;
846 }
847 if ((ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND))
848 || (dp->i_flags & APPEND)) {
849 VOP_UNLOCK(fvp, 0);
850 error = EPERM;
851 goto abortit;
852 }
853 if ((ip->i_mode & IFMT) == IFDIR) {
854 /*
855 * Avoid ".", "..", and aliases of "." for obvious reasons.
856 */
857 if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
858 dp == ip || (fcnp->cn_flags | tcnp->cn_flags) & ISDOTDOT ||
859 (ip->i_flag & IN_RENAME)) {
860 VOP_UNLOCK(fvp, 0);
861 error = EINVAL;
862 goto abortit;
863 }
864 ip->i_flag |= IN_RENAME;
865 oldparent = dp->i_number;
866 doingdirectory++;
867 }
868 vrele(fdvp);
869
870 /*
871 * When the target exists, both the directory
872 * and target vnodes are returned locked.
873 */
874 dp = VTOI(tdvp);
875 xp = NULL;
876 if (tvp)
877 xp = VTOI(tvp);
878
879 /*
880 * 1) Bump link count while we're moving stuff
881 * around. If we crash somewhere before
882 * completing our work, the link count
883 * may be wrong, but correctable.
884 */
885 ip->i_nlink++;
886 ip->i_flag |= IN_CHANGE;
887 if ((error = ext2_update(fvp, !DOINGASYNC(fvp))) != 0) {
888 VOP_UNLOCK(fvp, 0);
889 goto bad;
890 }
891
892 /*
893 * If ".." must be changed (ie the directory gets a new
894 * parent) then the source directory must not be in the
895 * directory hierarchy above the target, as this would
896 * orphan everything below the source directory. Also
897 * the user must have write permission in the source so
898 * as to be able to change "..". We must repeat the call
899 * to namei, as the parent directory is unlocked by the
900 * call to checkpath().
901 */
902 error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_thread);
903 VOP_UNLOCK(fvp, 0);
904 if (oldparent != dp->i_number)
905 newparent = dp->i_number;
906 if (doingdirectory && newparent) {
907 if (error) /* write access check above */
908 goto bad;
909 if (xp != NULL)
910 vput(tvp);
911 error = ext2_checkpath(ip, dp, tcnp->cn_cred);
912 if (error)
913 goto out;
914 VREF(tdvp);
915 error = relookup(tdvp, &tvp, tcnp);
916 if (error)
917 goto out;
918 vrele(tdvp);
919 dp = VTOI(tdvp);
920 xp = NULL;
921 if (tvp)
922 xp = VTOI(tvp);
923 }
924 /*
925 * 2) If target doesn't exist, link the target
926 * to the source and unlink the source.
927 * Otherwise, rewrite the target directory
928 * entry to reference the source inode and
929 * expunge the original entry's existence.
930 */
931 if (xp == NULL) {
932 if (dp->i_devvp != ip->i_devvp)
933 panic("ext2_rename: EXDEV");
934 /*
935 * Account for ".." in new directory.
936 * When source and destination have the same
937 * parent we don't fool with the link count.
938 */
939 if (doingdirectory && newparent) {
940 if ((nlink_t)dp->i_nlink >= LINK_MAX) {
941 error = EMLINK;
942 goto bad;
943 }
944 dp->i_nlink++;
945 dp->i_flag |= IN_CHANGE;
946 error = ext2_update(tdvp, !DOINGASYNC(tdvp));
947 if (error)
948 goto bad;
949 }
950 error = ext2_direnter(ip, tdvp, tcnp);
951 if (error) {
952 if (doingdirectory && newparent) {
953 dp->i_nlink--;
954 dp->i_flag |= IN_CHANGE;
955 (void)ext2_update(tdvp, 1);
956 }
957 goto bad;
958 }
959 vput(tdvp);
960 } else {
961 if (xp->i_devvp != dp->i_devvp || xp->i_devvp != ip->i_devvp)
962 panic("ext2_rename: EXDEV");
963 /*
964 * Short circuit rename(foo, foo).
965 */
966 if (xp->i_number == ip->i_number)
967 panic("ext2_rename: same file");
968 /*
969 * If the parent directory is "sticky", then the user must
970 * own the parent directory, or the destination of the rename,
971 * otherwise the destination may not be changed (except by
972 * root). This implements append-only directories.
973 */
974 if ((dp->i_mode & S_ISTXT) && tcnp->cn_cred->cr_uid != 0 &&
975 tcnp->cn_cred->cr_uid != dp->i_uid &&
976 xp->i_uid != tcnp->cn_cred->cr_uid) {
977 error = EPERM;
978 goto bad;
979 }
980 /*
981 * Target must be empty if a directory and have no links
982 * to it. Also, ensure source and target are compatible
983 * (both directories, or both not directories).
984 */
985 if ((xp->i_mode&IFMT) == IFDIR) {
986 if (! ext2_dirempty(xp, dp->i_number, tcnp->cn_cred) ||
987 xp->i_nlink > 2) {
988 error = ENOTEMPTY;
989 goto bad;
990 }
991 if (!doingdirectory) {
992 error = ENOTDIR;
993 goto bad;
994 }
995 cache_purge(tdvp);
996 } else if (doingdirectory) {
997 error = EISDIR;
998 goto bad;
999 }
1000 error = ext2_dirrewrite(dp, ip, tcnp);
1001 if (error)
1002 goto bad;
1003 /*
1004 * If the target directory is in the same
1005 * directory as the source directory,
1006 * decrement the link count on the parent
1007 * of the target directory.
1008 */
1009 if (doingdirectory && !newparent) {
1010 dp->i_nlink--;
1011 dp->i_flag |= IN_CHANGE;
1012 }
1013 vput(tdvp);
1014 /*
1015 * Adjust the link count of the target to
1016 * reflect the dirrewrite above. If this is
1017 * a directory it is empty and there are
1018 * no links to it, so we can squash the inode and
1019 * any space associated with it. We disallowed
1020 * renaming over top of a directory with links to
1021 * it above, as the remaining link would point to
1022 * a directory without "." or ".." entries.
1023 */
1024 xp->i_nlink--;
1025 if (doingdirectory) {
1026 if (--xp->i_nlink != 0)
1027 panic("ext2_rename: linked directory");
1028 error = ext2_truncate(tvp, (off_t)0, IO_SYNC,
1029 tcnp->cn_cred, tcnp->cn_thread);
1030 }
1031 xp->i_flag |= IN_CHANGE;
1032 vput(tvp);
1033 xp = NULL;
1034 }
1035
1036 /*
1037 * 3) Unlink the source.
1038 */
1039 fcnp->cn_flags &= ~MODMASK;
1040 fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
1041 VREF(fdvp);
1042 error = relookup(fdvp, &fvp, fcnp);
1043 if (error == 0)
1044 vrele(fdvp);
1045 if (fvp != NULL) {
1046 xp = VTOI(fvp);
1047 dp = VTOI(fdvp);
1048 } else {
1049 /*
1050 * From name has disappeared.
1051 */
1052 if (doingdirectory)
1053 panic("ext2_rename: lost dir entry");
1054 vrele(ap->a_fvp);
1055 return (0);
1056 }
1057 /*
1058 * Ensure that the directory entry still exists and has not
1059 * changed while the new name has been entered. If the source is
1060 * a file then the entry may have been unlinked or renamed. In
1061 * either case there is no further work to be done. If the source
1062 * is a directory then it cannot have been rmdir'ed; its link
1063 * count of three would cause a rmdir to fail with ENOTEMPTY.
1064 * The IN_RENAME flag ensures that it cannot be moved by another
1065 * rename.
1066 */
1067 if (xp != ip) {
1068 if (doingdirectory)
1069 panic("ext2_rename: lost dir entry");
1070 } else {
1071 /*
1072 * If the source is a directory with a
1073 * new parent, the link count of the old
1074 * parent directory must be decremented
1075 * and ".." set to point to the new parent.
1076 */
1077 if (doingdirectory && newparent) {
1078 dp->i_nlink--;
1079 dp->i_flag |= IN_CHANGE;
1080 error = vn_rdwr(UIO_READ, fvp, (caddr_t)&dirbuf,
1081 sizeof (struct dirtemplate), (off_t)0,
1082 UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK,
1083 tcnp->cn_cred, NOCRED, NULL, NULL);
1084 if (error == 0) {
1085 /* Like ufs little-endian: */
1086 namlen = dirbuf.dotdot_type;
1087 if (namlen != 2 ||
1088 dirbuf.dotdot_name[0] != '.' ||
1089 dirbuf.dotdot_name[1] != '.') {
1090 ext2_dirbad(xp, (doff_t)12,
1091 "rename: mangled dir");
1092 } else {
1093 dirbuf.dotdot_ino = newparent;
1094 (void) vn_rdwr(UIO_WRITE, fvp,
1095 (caddr_t)&dirbuf,
1096 sizeof (struct dirtemplate),
1097 (off_t)0, UIO_SYSSPACE,
1098 IO_NODELOCKED | IO_SYNC |
1099 IO_NOMACCHECK, tcnp->cn_cred,
1100 NOCRED, NULL, NULL);
1101 cache_purge(fdvp);
1102 }
1103 }
1104 }
1105 error = ext2_dirremove(fdvp, fcnp);
1106 if (!error) {
1107 xp->i_nlink--;
1108 xp->i_flag |= IN_CHANGE;
1109 }
1110 xp->i_flag &= ~IN_RENAME;
1111 }
1112 if (dp)
1113 vput(fdvp);
1114 if (xp)
1115 vput(fvp);
1116 vrele(ap->a_fvp);
1117 return (error);
1118
1119 bad:
1120 if (xp)
1121 vput(ITOV(xp));
1122 vput(ITOV(dp));
1123 out:
1124 if (doingdirectory)
1125 ip->i_flag &= ~IN_RENAME;
1126 if (vn_lock(fvp, LK_EXCLUSIVE) == 0) {
1127 ip->i_nlink--;
1128 ip->i_flag |= IN_CHANGE;
1129 ip->i_flag &= ~IN_RENAME;
1130 vput(fvp);
1131 } else
1132 vrele(fvp);
1133 return (error);
1134 }
1135
1136 /*
1137 * Mkdir system call
1138 */
1139 static int
1140 ext2_mkdir(ap)
1141 struct vop_mkdir_args /* {
1142 struct vnode *a_dvp;
1143 struct vnode **a_vpp;
1144 struct componentname *a_cnp;
1145 struct vattr *a_vap;
1146 } */ *ap;
1147 {
1148 struct vnode *dvp = ap->a_dvp;
1149 struct vattr *vap = ap->a_vap;
1150 struct componentname *cnp = ap->a_cnp;
1151 struct inode *ip, *dp;
1152 struct vnode *tvp;
1153 struct dirtemplate dirtemplate, *dtp;
1154 int error, dmode;
1155
1156 #ifdef DIAGNOSTIC
1157 if ((cnp->cn_flags & HASBUF) == 0)
1158 panic("ext2_mkdir: no name");
1159 #endif
1160 dp = VTOI(dvp);
1161 if ((nlink_t)dp->i_nlink >= LINK_MAX) {
1162 error = EMLINK;
1163 goto out;
1164 }
1165 dmode = vap->va_mode & 0777;
1166 dmode |= IFDIR;
1167 /*
1168 * Must simulate part of ext2_makeinode here to acquire the inode,
1169 * but not have it entered in the parent directory. The entry is
1170 * made later after writing "." and ".." entries.
1171 */
1172 error = ext2_valloc(dvp, dmode, cnp->cn_cred, &tvp);
1173 if (error)
1174 goto out;
1175 ip = VTOI(tvp);
1176 ip->i_gid = dp->i_gid;
1177 #ifdef SUIDDIR
1178 {
1179 /*
1180 * if we are hacking owners here, (only do this where told to)
1181 * and we are not giving it TOO root, (would subvert quotas)
1182 * then go ahead and give it to the other user.
1183 * The new directory also inherits the SUID bit.
1184 * If user's UID and dir UID are the same,
1185 * 'give it away' so that the SUID is still forced on.
1186 */
1187 if ( (dvp->v_mount->mnt_flag & MNT_SUIDDIR) &&
1188 (dp->i_mode & ISUID) && dp->i_uid) {
1189 dmode |= ISUID;
1190 ip->i_uid = dp->i_uid;
1191 } else {
1192 ip->i_uid = cnp->cn_cred->cr_uid;
1193 }
1194 }
1195 #else
1196 ip->i_uid = cnp->cn_cred->cr_uid;
1197 #endif
1198 ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
1199 ip->i_mode = dmode;
1200 tvp->v_type = VDIR; /* Rest init'd in getnewvnode(). */
1201 ip->i_nlink = 2;
1202 if (cnp->cn_flags & ISWHITEOUT)
1203 ip->i_flags |= UF_OPAQUE;
1204 error = ext2_update(tvp, 1);
1205
1206 /*
1207 * Bump link count in parent directory
1208 * to reflect work done below. Should
1209 * be done before reference is created
1210 * so reparation is possible if we crash.
1211 */
1212 dp->i_nlink++;
1213 dp->i_flag |= IN_CHANGE;
1214 error = ext2_update(dvp, !DOINGASYNC(dvp));
1215 if (error)
1216 goto bad;
1217
1218 /* Initialize directory with "." and ".." from static template. */
1219 if (EXT2_HAS_INCOMPAT_FEATURE(ip->i_e2fs,
1220 EXT2F_INCOMPAT_FTYPE))
1221 dtp = &mastertemplate;
1222 else
1223 dtp = &omastertemplate;
1224 dirtemplate = *dtp;
1225 dirtemplate.dot_ino = ip->i_number;
1226 dirtemplate.dotdot_ino = dp->i_number;
1227 /* note that in ext2 DIRBLKSIZ == blocksize, not DEV_BSIZE
1228 * so let's just redefine it - for this function only
1229 */
1230 #undef DIRBLKSIZ
1231 #define DIRBLKSIZ VTOI(dvp)->i_e2fs->e2fs_bsize
1232 dirtemplate.dotdot_reclen = DIRBLKSIZ - 12;
1233 error = vn_rdwr(UIO_WRITE, tvp, (caddr_t)&dirtemplate,
1234 sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE,
1235 IO_NODELOCKED | IO_SYNC | IO_NOMACCHECK, cnp->cn_cred, NOCRED,
1236 NULL, NULL);
1237 if (error) {
1238 dp->i_nlink--;
1239 dp->i_flag |= IN_CHANGE;
1240 goto bad;
1241 }
1242 if (DIRBLKSIZ > VFSTOEXT2(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
1243 /* XXX should grow with balloc() */
1244 panic("ext2_mkdir: blksize");
1245 else {
1246 ip->i_size = DIRBLKSIZ;
1247 ip->i_flag |= IN_CHANGE;
1248 }
1249
1250 /* Directory set up, now install its entry in the parent directory. */
1251 error = ext2_direnter(ip, dvp, cnp);
1252 if (error) {
1253 dp->i_nlink--;
1254 dp->i_flag |= IN_CHANGE;
1255 }
1256 bad:
1257 /*
1258 * No need to do an explicit VOP_TRUNCATE here, vrele will do this
1259 * for us because we set the link count to 0.
1260 */
1261 if (error) {
1262 ip->i_nlink = 0;
1263 ip->i_flag |= IN_CHANGE;
1264 vput(tvp);
1265 } else
1266 *ap->a_vpp = tvp;
1267 out:
1268 return (error);
1269 #undef DIRBLKSIZ
1270 #define DIRBLKSIZ DEV_BSIZE
1271 }
1272
1273 /*
1274 * Rmdir system call.
1275 */
1276 static int
1277 ext2_rmdir(ap)
1278 struct vop_rmdir_args /* {
1279 struct vnode *a_dvp;
1280 struct vnode *a_vp;
1281 struct componentname *a_cnp;
1282 } */ *ap;
1283 {
1284 struct vnode *vp = ap->a_vp;
1285 struct vnode *dvp = ap->a_dvp;
1286 struct componentname *cnp = ap->a_cnp;
1287 struct inode *ip, *dp;
1288 int error;
1289
1290 ip = VTOI(vp);
1291 dp = VTOI(dvp);
1292
1293 /*
1294 * Verify the directory is empty (and valid).
1295 * (Rmdir ".." won't be valid since
1296 * ".." will contain a reference to
1297 * the current directory and thus be
1298 * non-empty.)
1299 */
1300 error = 0;
1301 if (ip->i_nlink != 2 || !ext2_dirempty(ip, dp->i_number, cnp->cn_cred)) {
1302 error = ENOTEMPTY;
1303 goto out;
1304 }
1305 if ((dp->i_flags & APPEND)
1306 || (ip->i_flags & (NOUNLINK | IMMUTABLE | APPEND))) {
1307 error = EPERM;
1308 goto out;
1309 }
1310 /*
1311 * Delete reference to directory before purging
1312 * inode. If we crash in between, the directory
1313 * will be reattached to lost+found,
1314 */
1315 error = ext2_dirremove(dvp, cnp);
1316 if (error)
1317 goto out;
1318 dp->i_nlink--;
1319 dp->i_flag |= IN_CHANGE;
1320 cache_purge(dvp);
1321 VOP_UNLOCK(dvp, 0);
1322 /*
1323 * Truncate inode. The only stuff left
1324 * in the directory is "." and "..". The
1325 * "." reference is inconsequential since
1326 * we're quashing it. The ".." reference
1327 * has already been adjusted above. We've
1328 * removed the "." reference and the reference
1329 * in the parent directory, but there may be
1330 * other hard links so decrement by 2 and
1331 * worry about them later.
1332 */
1333 ip->i_nlink -= 2;
1334 error = ext2_truncate(vp, (off_t)0, IO_SYNC, cnp->cn_cred,
1335 cnp->cn_thread);
1336 cache_purge(ITOV(ip));
1337 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
1338 out:
1339 return (error);
1340 }
1341
1342 /*
1343 * symlink -- make a symbolic link
1344 */
1345 static int
1346 ext2_symlink(ap)
1347 struct vop_symlink_args /* {
1348 struct vnode *a_dvp;
1349 struct vnode **a_vpp;
1350 struct componentname *a_cnp;
1351 struct vattr *a_vap;
1352 char *a_target;
1353 } */ *ap;
1354 {
1355 struct vnode *vp, **vpp = ap->a_vpp;
1356 struct inode *ip;
1357 int len, error;
1358
1359 error = ext2_makeinode(IFLNK | ap->a_vap->va_mode, ap->a_dvp,
1360 vpp, ap->a_cnp);
1361 if (error)
1362 return (error);
1363 vp = *vpp;
1364 len = strlen(ap->a_target);
1365 if (len < vp->v_mount->mnt_maxsymlinklen) {
1366 ip = VTOI(vp);
1367 bcopy(ap->a_target, (char *)ip->i_shortlink, len);
1368 ip->i_size = len;
1369 ip->i_flag |= IN_CHANGE | IN_UPDATE;
1370 } else
1371 error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0,
1372 UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK,
1373 ap->a_cnp->cn_cred, NOCRED, NULL, NULL);
1374 if (error)
1375 vput(vp);
1376 return (error);
1377 }
1378
1379 /*
1380 * Return target name of a symbolic link
1381 */
1382 static int
1383 ext2_readlink(ap)
1384 struct vop_readlink_args /* {
1385 struct vnode *a_vp;
1386 struct uio *a_uio;
1387 struct ucred *a_cred;
1388 } */ *ap;
1389 {
1390 struct vnode *vp = ap->a_vp;
1391 struct inode *ip = VTOI(vp);
1392 int isize;
1393
1394 isize = ip->i_size;
1395 if (isize < vp->v_mount->mnt_maxsymlinklen) {
1396 uiomove((char *)ip->i_shortlink, isize, ap->a_uio);
1397 return (0);
1398 }
1399 return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred));
1400 }
1401
1402 /*
1403 * Calculate the logical to physical mapping if not done already,
1404 * then call the device strategy routine.
1405 *
1406 * In order to be able to swap to a file, the ext2_bmaparray() operation may not
1407 * deadlock on memory. See ext2_bmap() for details.
1408 */
1409 static int
1410 ext2_strategy(ap)
1411 struct vop_strategy_args /* {
1412 struct vnode *a_vp;
1413 struct buf *a_bp;
1414 } */ *ap;
1415 {
1416 struct buf *bp = ap->a_bp;
1417 struct vnode *vp = ap->a_vp;
1418 struct inode *ip;
1419 struct bufobj *bo;
1420 int32_t blkno;
1421 int error;
1422
1423 ip = VTOI(vp);
1424 if (vp->v_type == VBLK || vp->v_type == VCHR)
1425 panic("ext2_strategy: spec");
1426 if (bp->b_blkno == bp->b_lblkno) {
1427 error = ext2_bmaparray(vp, bp->b_lblkno, &blkno, NULL, NULL);
1428 bp->b_blkno = blkno;
1429 if (error) {
1430 bp->b_error = error;
1431 bp->b_ioflags |= BIO_ERROR;
1432 bufdone(bp);
1433 return (0);
1434 }
1435 if ((long)bp->b_blkno == -1)
1436 vfs_bio_clrbuf(bp);
1437 }
1438 if ((long)bp->b_blkno == -1) {
1439 bufdone(bp);
1440 return (0);
1441 }
1442 bp->b_iooffset = dbtob(bp->b_blkno);
1443 bo = VFSTOEXT2(vp->v_mount)->um_bo;
1444 BO_STRATEGY(bo, bp);
1445 return (0);
1446 }
1447
1448 /*
1449 * Print out the contents of an inode.
1450 */
1451 static int
1452 ext2_print(ap)
1453 struct vop_print_args /* {
1454 struct vnode *a_vp;
1455 } */ *ap;
1456 {
1457 struct vnode *vp = ap->a_vp;
1458 struct inode *ip = VTOI(vp);
1459
1460 vn_printf(ip->i_devvp, "\tino %lu", (u_long)ip->i_number);
1461 if (vp->v_type == VFIFO)
1462 fifo_printinfo(vp);
1463 printf("\n");
1464 return (0);
1465 }
1466
1467 /*
1468 * Close wrapper for fifos.
1469 *
1470 * Update the times on the inode then do device close.
1471 */
1472 static int
1473 ext2fifo_close(ap)
1474 struct vop_close_args /* {
1475 struct vnode *a_vp;
1476 int a_fflag;
1477 struct ucred *a_cred;
1478 struct thread *a_td;
1479 } */ *ap;
1480 {
1481 struct vnode *vp = ap->a_vp;
1482
1483 VI_LOCK(vp);
1484 if (vp->v_usecount > 1)
1485 ext2_itimes_locked(vp);
1486 VI_UNLOCK(vp);
1487 return (fifo_specops.vop_close(ap));
1488 }
1489
1490 /*
1491 * Kqfilter wrapper for fifos.
1492 *
1493 * Fall through to ext2 kqfilter routines if needed
1494 */
1495 static int
1496 ext2fifo_kqfilter(ap)
1497 struct vop_kqfilter_args *ap;
1498 {
1499 int error;
1500
1501 error = fifo_specops.vop_kqfilter(ap);
1502 if (error)
1503 error = vfs_kqfilter(ap);
1504 return (error);
1505 }
1506
1507 /*
1508 * Return POSIX pathconf information applicable to ext2 filesystems.
1509 */
1510 static int
1511 ext2_pathconf(ap)
1512 struct vop_pathconf_args /* {
1513 struct vnode *a_vp;
1514 int a_name;
1515 int *a_retval;
1516 } */ *ap;
1517 {
1518
1519 switch (ap->a_name) {
1520 case _PC_LINK_MAX:
1521 *ap->a_retval = LINK_MAX;
1522 return (0);
1523 case _PC_NAME_MAX:
1524 *ap->a_retval = NAME_MAX;
1525 return (0);
1526 case _PC_PATH_MAX:
1527 *ap->a_retval = PATH_MAX;
1528 return (0);
1529 case _PC_PIPE_BUF:
1530 *ap->a_retval = PIPE_BUF;
1531 return (0);
1532 case _PC_CHOWN_RESTRICTED:
1533 *ap->a_retval = 1;
1534 return (0);
1535 case _PC_NO_TRUNC:
1536 *ap->a_retval = 1;
1537 return (0);
1538 default:
1539 return (EINVAL);
1540 }
1541 /* NOTREACHED */
1542 }
1543
1544 /*
1545 * Vnode pointer to File handle
1546 */
1547 /* ARGSUSED */
1548 static int
1549 ext2_vptofh(ap)
1550 struct vop_vptofh_args /* {
1551 struct vnode *a_vp;
1552 struct fid *a_fhp;
1553 } */ *ap;
1554 {
1555 struct inode *ip;
1556 struct ufid *ufhp;
1557
1558 ip = VTOI(ap->a_vp);
1559 ufhp = (struct ufid *)ap->a_fhp;
1560 ufhp->ufid_len = sizeof(struct ufid);
1561 ufhp->ufid_ino = ip->i_number;
1562 ufhp->ufid_gen = ip->i_gen;
1563 return (0);
1564 }
1565
1566 /*
1567 * Initialize the vnode associated with a new inode, handle aliased
1568 * vnodes.
1569 */
1570 int
1571 ext2_vinit(mntp, fifoops, vpp)
1572 struct mount *mntp;
1573 struct vop_vector *fifoops;
1574 struct vnode **vpp;
1575 {
1576 struct inode *ip;
1577 struct vnode *vp;
1578
1579 vp = *vpp;
1580 ip = VTOI(vp);
1581 vp->v_type = IFTOVT(ip->i_mode);
1582 if (vp->v_type == VFIFO)
1583 vp->v_op = fifoops;
1584
1585 if (ip->i_number == EXT2_ROOTINO)
1586 vp->v_vflag |= VV_ROOT;
1587 ip->i_modrev = init_va_filerev();
1588 *vpp = vp;
1589 return (0);
1590 }
1591
1592 /*
1593 * Allocate a new inode.
1594 */
1595 static int
1596 ext2_makeinode(mode, dvp, vpp, cnp)
1597 int mode;
1598 struct vnode *dvp;
1599 struct vnode **vpp;
1600 struct componentname *cnp;
1601 {
1602 struct inode *ip, *pdir;
1603 struct vnode *tvp;
1604 int error;
1605
1606 pdir = VTOI(dvp);
1607 #ifdef DIAGNOSTIC
1608 if ((cnp->cn_flags & HASBUF) == 0)
1609 panic("ext2_makeinode: no name");
1610 #endif
1611 *vpp = NULL;
1612 if ((mode & IFMT) == 0)
1613 mode |= IFREG;
1614
1615 error = ext2_valloc(dvp, mode, cnp->cn_cred, &tvp);
1616 if (error) {
1617 return (error);
1618 }
1619 ip = VTOI(tvp);
1620 ip->i_gid = pdir->i_gid;
1621 #ifdef SUIDDIR
1622 {
1623 /*
1624 * if we are
1625 * not the owner of the directory,
1626 * and we are hacking owners here, (only do this where told to)
1627 * and we are not giving it TOO root, (would subvert quotas)
1628 * then go ahead and give it to the other user.
1629 * Note that this drops off the execute bits for security.
1630 */
1631 if ( (dvp->v_mount->mnt_flag & MNT_SUIDDIR) &&
1632 (pdir->i_mode & ISUID) &&
1633 (pdir->i_uid != cnp->cn_cred->cr_uid) && pdir->i_uid) {
1634 ip->i_uid = pdir->i_uid;
1635 mode &= ~07111;
1636 } else {
1637 ip->i_uid = cnp->cn_cred->cr_uid;
1638 }
1639 }
1640 #else
1641 ip->i_uid = cnp->cn_cred->cr_uid;
1642 #endif
1643 ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
1644 ip->i_mode = mode;
1645 tvp->v_type = IFTOVT(mode); /* Rest init'd in getnewvnode(). */
1646 ip->i_nlink = 1;
1647 if ((ip->i_mode & ISGID) && !groupmember(ip->i_gid, cnp->cn_cred)) {
1648 if (priv_check_cred(cnp->cn_cred, PRIV_VFS_RETAINSUGID, 0))
1649 ip->i_mode &= ~ISGID;
1650 }
1651
1652 if (cnp->cn_flags & ISWHITEOUT)
1653 ip->i_flags |= UF_OPAQUE;
1654
1655 /*
1656 * Make sure inode goes to disk before directory entry.
1657 */
1658 error = ext2_update(tvp, !DOINGASYNC(tvp));
1659 if (error)
1660 goto bad;
1661 error = ext2_direnter(ip, dvp, cnp);
1662 if (error)
1663 goto bad;
1664
1665 *vpp = tvp;
1666 return (0);
1667
1668 bad:
1669 /*
1670 * Write error occurred trying to update the inode
1671 * or the directory so must deallocate the inode.
1672 */
1673 ip->i_nlink = 0;
1674 ip->i_flag |= IN_CHANGE;
1675 vput(tvp);
1676 return (error);
1677 }
Cache object: cdfd26ff403c8db125e41df8257e7bae
|