use FSYNC instead of doing iupdat directly
[unix-history] / usr / src / sys / ufs / ffs / ufs_vnops.c
CommitLineData
da7c5cc6 1/*
7188ac27
KM
2 * Copyright (c) 1982, 1986, 1989 Regents of the University of California.
3 * All rights reserved.
da7c5cc6 4 *
7188ac27
KM
5 * Redistribution and use in source and binary forms are permitted
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16 *
f09fe6d3 17 * @(#)ufs_vnops.c 7.20 (Berkeley) %G%
da7c5cc6 18 */
6459ebe0 19
94368568
JB
20#include "param.h"
21#include "systm.h"
94368568
JB
22#include "user.h"
23#include "kernel.h"
24#include "file.h"
25#include "stat.h"
94368568
JB
26#include "buf.h"
27#include "proc.h"
94368568
JB
28#include "uio.h"
29#include "socket.h"
30#include "socketvar.h"
7188ac27 31#include "conf.h"
94368568 32#include "mount.h"
7188ac27
KM
33#include "vnode.h"
34#include "../ufs/inode.h"
35#include "../ufs/fs.h"
36#include "../ufs/quota.h"
3e78e260 37
4f083fd7 38/*
7188ac27 39 * Global vfs data structures for ufs
4f083fd7 40 */
3e78e260 41
7188ac27
KM
42int ufs_lookup(),
43 ufs_create(),
44 ufs_mknod(),
45 ufs_open(),
46 ufs_close(),
47 ufs_access(),
48 ufs_getattr(),
49 ufs_setattr(),
50 ufs_read(),
51 ufs_write(),
52 ufs_ioctl(),
53 ufs_select(),
54 ufs_mmap(),
55 ufs_fsync(),
56 ufs_seek(),
57 ufs_remove(),
58 ufs_link(),
59 ufs_rename(),
60 ufs_mkdir(),
61 ufs_rmdir(),
62 ufs_symlink(),
63 ufs_readdir(),
64 ufs_readlink(),
65 ufs_abortop(),
66 ufs_inactive(),
e8a3816c 67 ufs_reclaim(),
7188ac27
KM
68 ufs_lock(),
69 ufs_unlock(),
70 ufs_bmap(),
71 ufs_strategy();
72
73struct vnodeops ufs_vnodeops = {
74 ufs_lookup,
75 ufs_create,
76 ufs_mknod,
77 ufs_open,
78 ufs_close,
79 ufs_access,
80 ufs_getattr,
81 ufs_setattr,
82 ufs_read,
83 ufs_write,
84 ufs_ioctl,
85 ufs_select,
86 ufs_mmap,
87 ufs_fsync,
88 ufs_seek,
89 ufs_remove,
90 ufs_link,
91 ufs_rename,
92 ufs_mkdir,
93 ufs_rmdir,
94 ufs_symlink,
95 ufs_readdir,
96 ufs_readlink,
97 ufs_abortop,
98 ufs_inactive,
e8a3816c 99 ufs_reclaim,
7188ac27
KM
100 ufs_lock,
101 ufs_unlock,
102 ufs_bmap,
103 ufs_strategy,
104};
105
f09fe6d3
KM
106int spec_lookup(),
107 spec_open(),
108 spec_read(),
109 spec_write(),
110 spec_strategy(),
111 spec_ioctl(),
112 spec_select(),
113 spec_close(),
114 spec_badop(),
115 spec_nullop();
116
117struct vnodeops spec_inodeops = {
118 spec_lookup,
119 spec_badop,
120 spec_badop,
121 spec_open,
122 spec_close,
123 ufs_access,
124 ufs_getattr,
125 ufs_setattr,
126 spec_read,
127 spec_write,
128 spec_ioctl,
129 spec_select,
130 spec_badop,
131 spec_nullop,
132 spec_badop,
133 spec_badop,
134 spec_badop,
135 spec_badop,
136 spec_badop,
137 spec_badop,
138 spec_badop,
139 spec_badop,
140 spec_badop,
141 spec_badop,
142 ufs_inactive,
143 ufs_reclaim,
144 ufs_lock,
145 ufs_unlock,
146 spec_badop,
147 spec_strategy,
148};
149
7188ac27
KM
150enum vtype iftovt_tab[8] = {
151 VNON, VCHR, VDIR, VBLK, VREG, VLNK, VSOCK, VBAD,
152};
153int vttoif_tab[8] = {
154 0, IFREG, IFDIR, IFBLK, IFCHR, IFLNK, IFSOCK, IFMT,
155};
3e78e260 156
4f083fd7 157/*
7188ac27 158 * Create a regular file
4f083fd7 159 */
7188ac27
KM
160ufs_create(ndp, vap)
161 struct nameidata *ndp;
162 struct vattr *vap;
3e78e260 163{
7188ac27
KM
164 struct inode *ip;
165 int error;
3e78e260 166
7188ac27
KM
167 if (error = maknode(MAKEIMODE(vap->va_type, vap->va_mode), ndp, &ip))
168 return (error);
169 ndp->ni_vp = ITOV(ip);
170 return (0);
3e78e260
BJ
171}
172
4f083fd7 173/*
7188ac27 174 * Mknod vnode call
4f083fd7 175 */
7188ac27
KM
176/* ARGSUSED */
177ufs_mknod(ndp, vap, cred)
178 struct nameidata *ndp;
179 struct ucred *cred;
180 struct vattr *vap;
3e78e260 181{
f09fe6d3 182 register struct vnode *vp;
7188ac27
KM
183 struct inode *ip;
184 int error;
3e78e260 185
7188ac27
KM
186 if (error = maknode(MAKEIMODE(vap->va_type, vap->va_mode), ndp, &ip))
187 return (error);
f09fe6d3 188 vp = ITOV(ip);
7188ac27
KM
189 if (vap->va_rdev) {
190 /*
191 * Want to be able to use this to make badblock
192 * inodes, so don't truncate the dev number.
193 */
f09fe6d3 194 vp->v_rdev = ip->i_rdev = vap->va_rdev;
7188ac27
KM
195 ip->i_flag |= IACC|IUPD|ICHG;
196 }
7188ac27
KM
197 /*
198 * Remove inode so that it will be reloaded by iget and
199 * checked to see if it is an alias of an existing entry
200 * in the inode cache.
201 */
9ed9b473 202 iput(ip);
f09fe6d3
KM
203 vp->v_type = VNON;
204 vgone(vp);
7188ac27 205 return (0);
3e78e260
BJ
206}
207
208/*
7188ac27
KM
209 * Open called.
210 *
211 * Nothing to do.
3e78e260 212 */
7188ac27
KM
213/* ARGSUSED */
214ufs_open(vp, mode, cred)
215 struct vnode *vp;
216 int mode;
217 struct ucred *cred;
3e78e260 218{
3e78e260 219
7188ac27 220 return (0);
3e78e260
BJ
221}
222
223/*
7188ac27
KM
224 * Close called
225 *
226 * Update the times on the inode.
3e78e260 227 */
7188ac27
KM
228/* ARGSUSED */
229ufs_close(vp, fflag, cred)
230 struct vnode *vp;
231 int fflag;
232 struct ucred *cred;
3e78e260 233{
7188ac27 234 register struct inode *ip = VTOI(vp);
3e78e260 235
7188ac27
KM
236 if (vp->v_count > 1 && !(ip->i_flag & ILOCKED))
237 ITIMES(ip, &time, &time);
238 return (0);
3e78e260
BJ
239}
240
7188ac27
KM
241ufs_access(vp, mode, cred)
242 struct vnode *vp;
243 int mode;
244 struct ucred *cred;
3e78e260 245{
3e78e260 246
7188ac27 247 return (iaccess(VTOI(vp), mode, cred));
3e78e260
BJ
248}
249
7188ac27
KM
250/* ARGSUSED */
251ufs_getattr(vp, vap, cred)
252 struct vnode *vp;
253 register struct vattr *vap;
254 struct ucred *cred;
3e78e260 255{
7188ac27 256 register struct inode *ip = VTOI(vp);
3e78e260 257
7188ac27 258 ITIMES(ip, &time, &time);
3e78e260 259 /*
7188ac27 260 * Copy from inode table
3e78e260 261 */
7188ac27
KM
262 vap->va_fsid = ip->i_dev;
263 vap->va_fileid = ip->i_number;
264 vap->va_mode = ip->i_mode & ~IFMT;
265 vap->va_nlink = ip->i_nlink;
266 vap->va_uid = ip->i_uid;
267 vap->va_gid = ip->i_gid;
268 vap->va_rdev = (dev_t)ip->i_rdev;
e8a3816c
KM
269 vap->va_size = ip->i_din.di_qsize.val[0];
270 vap->va_size1 = ip->i_din.di_qsize.val[1];
7188ac27 271 vap->va_atime.tv_sec = ip->i_atime;
6ef53d70 272 vap->va_atime.tv_usec = 0;
7188ac27 273 vap->va_mtime.tv_sec = ip->i_mtime;
6ef53d70 274 vap->va_mtime.tv_usec = 0;
7188ac27 275 vap->va_ctime.tv_sec = ip->i_ctime;
6ef53d70 276 vap->va_ctime.tv_usec = 0;
d07fc92a
KM
277 vap->va_flags = ip->i_flags;
278 vap->va_gen = ip->i_gen;
7188ac27
KM
279 /* this doesn't belong here */
280 if (vp->v_type == VBLK)
281 vap->va_blocksize = BLKDEV_IOSIZE;
282 else if (vp->v_type == VCHR)
283 vap->va_blocksize = MAXBSIZE;
8eee8525 284 else
7188ac27 285 vap->va_blocksize = ip->i_fs->fs_bsize;
a61a68d6 286 vap->va_bytes = dbtob(ip->i_blocks);
7188ac27
KM
287 vap->va_bytes1 = -1;
288 vap->va_type = vp->v_type;
289 return (0);
3e78e260
BJ
290}
291
292/*
7188ac27 293 * Set attribute vnode op. called from several syscalls
3e78e260 294 */
7188ac27
KM
295ufs_setattr(vp, vap, cred)
296 register struct vnode *vp;
297 register struct vattr *vap;
298 register struct ucred *cred;
3e78e260 299{
7188ac27
KM
300 register struct inode *ip = VTOI(vp);
301 int error = 0;
b4d1aee9 302
7188ac27
KM
303 /*
304 * Check for unsetable attributes.
305 */
306 if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
307 (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
308 (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
d07fc92a 309 ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
7188ac27 310 return (EINVAL);
b4d1aee9 311 }
7188ac27
KM
312 /*
313 * Go through the fields and update iff not VNOVAL.
314 */
315 if (vap->va_uid != (u_short)VNOVAL || vap->va_gid != (u_short)VNOVAL)
316 if (error = chown1(vp, vap->va_uid, vap->va_gid, cred))
317 return (error);
318 if (vap->va_size != VNOVAL) {
319 if (vp->v_type == VDIR)
320 return (EISDIR);
7188ac27
KM
321 if (error = itrunc(ip, vap->va_size))
322 return (error);
3e78e260 323 }
7188ac27 324 if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
de412887
KM
325 if (cred->cr_uid != ip->i_uid &&
326 (error = suser(cred, &u.u_acflag)))
327 return (error);
7188ac27
KM
328 if (vap->va_atime.tv_sec != VNOVAL)
329 ip->i_flag |= IACC;
330 if (vap->va_mtime.tv_sec != VNOVAL)
331 ip->i_flag |= IUPD;
332 ip->i_flag |= ICHG;
333 if (error = iupdat(ip, &vap->va_atime, &vap->va_mtime, 1))
334 return (error);
5485e062 335 }
7188ac27
KM
336 if (vap->va_mode != (u_short)VNOVAL)
337 error = chmod1(vp, (int)vap->va_mode, cred);
d07fc92a
KM
338 if (vap->va_flags != VNOVAL) {
339 if (cred->cr_uid != ip->i_uid &&
340 (error = suser(cred, &u.u_acflag)))
341 return (error);
342 if (cred->cr_uid == 0) {
343 ip->i_flags = vap->va_flags;
344 } else {
345 ip->i_flags &= 0xffff0000;
346 ip->i_flags |= (vap->va_flags & 0xffff);
347 }
348 ip->i_flag |= ICHG;
349 }
7188ac27 350 return (error);
528f664c
SL
351}
352
4f083fd7
SL
353/*
354 * Change the mode on a file.
355 * Inode must be locked before calling.
356 */
7188ac27
KM
357chmod1(vp, mode, cred)
358 register struct vnode *vp;
528f664c 359 register int mode;
7188ac27 360 struct ucred *cred;
528f664c 361{
7188ac27 362 register struct inode *ip = VTOI(vp);
de412887 363 int error;
197da11b 364
de412887
KM
365 if (cred->cr_uid != ip->i_uid &&
366 (error = suser(cred, &u.u_acflag)))
367 return (error);
3e78e260 368 ip->i_mode &= ~07777;
7188ac27
KM
369 if (cred->cr_uid) {
370 if (vp->v_type != VDIR)
47af7174 371 mode &= ~ISVTX;
7188ac27 372 if (!groupmember(ip->i_gid, cred))
bb1b75f4 373 mode &= ~ISGID;
f94ceb3b 374 }
7188ac27 375 ip->i_mode |= mode & 07777;
3e78e260 376 ip->i_flag |= ICHG;
7188ac27
KM
377 if ((vp->v_flag & VTEXT) && (ip->i_mode & ISVTX) == 0)
378 xrele(vp);
47af7174 379 return (0);
5485e062
BJ
380}
381
528f664c
SL
382/*
383 * Perform chown operation on inode ip;
384 * inode must be locked prior to call.
385 */
7188ac27
KM
386chown1(vp, uid, gid, cred)
387 register struct vnode *vp;
388 uid_t uid;
389 gid_t gid;
390 struct ucred *cred;
528f664c 391{
7188ac27 392 register struct inode *ip = VTOI(vp);
528f664c
SL
393#ifdef QUOTA
394 register long change;
bb1b75f4 395#endif
7188ac27 396 int error;
528f664c 397
7188ac27 398 if (uid == (u_short)VNOVAL)
bb1b75f4 399 uid = ip->i_uid;
7188ac27 400 if (gid == (u_short)VNOVAL)
bb1b75f4 401 gid = ip->i_gid;
18b0bce6
KB
402 /*
403 * If we don't own the file, are trying to change the owner
404 * of the file, or are not a member of the target group,
405 * the caller must be superuser or the call fails.
406 */
7188ac27
KM
407 if ((cred->cr_uid != ip->i_uid || uid != ip->i_uid ||
408 !groupmember((gid_t)gid, cred)) &&
409 (error = suser(cred, &u.u_acflag)))
410 return (error);
bb1b75f4 411#ifdef QUOTA
3809bf69 412 if (ip->i_uid == uid) /* this just speeds things a little */
0a77f278 413 change = 0;
2e073567
SL
414 else
415 change = ip->i_blocks;
416 (void) chkdq(ip, -change, 1);
417 (void) chkiq(ip->i_dev, ip, ip->i_uid, 1);
0a77f278 418 dqrele(ip->i_dquot);
f94ceb3b 419#endif
b440c2b9
KM
420 if (ip->i_uid != uid && cred->cr_uid != 0)
421 ip->i_mode &= ~ISUID;
422 if (ip->i_gid != gid && cred->cr_uid != 0)
423 ip->i_mode &= ~ISGID;
bb1b75f4
SL
424 ip->i_uid = uid;
425 ip->i_gid = gid;
3e78e260 426 ip->i_flag |= ICHG;
528f664c 427#ifdef QUOTA
0a77f278 428 ip->i_dquot = inoquota(ip);
2e073567 429 (void) chkdq(ip, change, 1);
8011f5df 430 (void) chkiq(ip->i_dev, (struct inode *)NULL, (uid_t)uid, 1);
2e073567
SL
431 return (u.u_error); /* should == 0 ALWAYS !! */
432#else
bb1b75f4 433 return (0);
2e073567 434#endif
d67a03eb
BJ
435}
436
7188ac27
KM
437/* ARGSUSED */
438ufs_ioctl(vp, com, data, fflag, cred)
439 struct vnode *vp;
440 int com;
441 caddr_t data;
442 int fflag;
443 struct ucred *cred;
bb1b75f4 444{
bb1b75f4 445
7188ac27
KM
446 printf("ufs_ioctl called with type %d\n", vp->v_type);
447 return (ENOTTY);
448}
449
450/* ARGSUSED */
451ufs_select(vp, which, cred)
452 struct vnode *vp;
453 int which;
454 struct ucred *cred;
455{
456
457 printf("ufs_select called with type %d\n", vp->v_type);
458 return (1); /* XXX */
bb1b75f4 459}
d67a03eb 460
4f083fd7 461/*
7188ac27
KM
462 * Mmap a file
463 *
464 * NB Currently unsupported.
4f083fd7 465 */
7188ac27
KM
466/* ARGSUSED */
467ufs_mmap(vp, fflags, cred)
468 struct vnode *vp;
469 int fflags;
470 struct ucred *cred;
d67a03eb 471{
d67a03eb 472
7188ac27 473 return (EINVAL);
d67a03eb 474}
64d3a787 475
4f083fd7 476/*
7188ac27 477 * Synch an open file.
4f083fd7 478 */
7188ac27
KM
479/* ARGSUSED */
480ufs_fsync(vp, fflags, cred)
481 struct vnode *vp;
482 int fflags;
483 struct ucred *cred;
528f664c 484{
7188ac27
KM
485 register struct inode *ip = VTOI(vp);
486 int error;
487
488 ILOCK(ip);
489 if (fflags&FWRITE)
490 ip->i_flag |= ICHG;
491 error = syncip(ip);
492 IUNLOCK(ip);
493 return (error);
528f664c
SL
494}
495
4f083fd7 496/*
7188ac27
KM
497 * Seek on a file
498 *
499 * Nothing to do, so just return.
4f083fd7 500 */
7188ac27
KM
501/* ARGSUSED */
502ufs_seek(vp, oldoff, newoff, cred)
503 struct vnode *vp;
504 off_t oldoff, newoff;
505 struct ucred *cred;
528f664c 506{
7188ac27
KM
507
508 return (0);
509}
510
511/*
512 * ufs remove
513 * Hard to avoid races here, especially
514 * in unlinking directories.
515 */
516ufs_remove(ndp)
517 struct nameidata *ndp;
518{
519 register struct inode *ip, *dp;
520 int error;
521
522 ip = VTOI(ndp->ni_vp);
523 dp = VTOI(ndp->ni_dvp);
524 error = dirremove(ndp);
525 if (!error) {
526 ip->i_nlink--;
527 ip->i_flag |= ICHG;
528f664c 528 }
7188ac27
KM
529 if (dp == ip)
530 vrele(ITOV(ip));
531 else
532 iput(ip);
533 iput(dp);
534 return (error);
4f083fd7
SL
535}
536
537/*
7188ac27 538 * link vnode call
4f083fd7 539 */
7188ac27
KM
540ufs_link(vp, ndp)
541 register struct vnode *vp;
542 register struct nameidata *ndp;
4f083fd7 543{
7188ac27
KM
544 register struct inode *ip = VTOI(vp);
545 int error;
4f083fd7 546
7188ac27
KM
547 if (ndp->ni_dvp != vp)
548 ILOCK(ip);
549 if (ip->i_nlink == LINK_MAX - 1) {
550 error = EMLINK;
551 goto out;
552 }
553 ip->i_nlink++;
554 ip->i_flag |= ICHG;
555 error = iupdat(ip, &time, &time, 1);
556 if (!error)
557 error = direnter(ip, ndp);
558out:
559 if (ndp->ni_dvp != vp)
560 IUNLOCK(ip);
561 if (error) {
562 ip->i_nlink--;
82252d2b 563 ip->i_flag |= ICHG;
7188ac27
KM
564 }
565 return (error);
528f664c
SL
566}
567
4f083fd7
SL
568/*
569 * Rename system call.
570 * rename("foo", "bar");
571 * is essentially
572 * unlink("bar");
573 * link("foo", "bar");
574 * unlink("foo");
575 * but ``atomically''. Can't do full commit without saving state in the
576 * inode on disk which isn't feasible at this time. Best we can do is
577 * always guarantee the target exists.
578 *
579 * Basic algorithm is:
580 *
581 * 1) Bump link count on source while we're linking it to the
7188ac27 582 * target. This also ensure the inode won't be deleted out
68f21562
KM
583 * from underneath us while we work (it may be truncated by
584 * a concurrent `trunc' or `open' for creation).
4f083fd7
SL
585 * 2) Link source to destination. If destination already exists,
586 * delete it first.
68f21562
KM
587 * 3) Unlink source reference to inode if still around. If a
588 * directory was moved and the parent of the destination
4f083fd7
SL
589 * is different from the source, patch the ".." entry in the
590 * directory.
4f083fd7 591 */
7188ac27
KM
592ufs_rename(fndp, tndp)
593 register struct nameidata *fndp, *tndp;
528f664c 594{
4f083fd7 595 register struct inode *ip, *xp, *dp;
68f21562
KM
596 struct dirtemplate dirbuf;
597 int doingdirectory = 0, oldparent = 0, newparent = 0;
a5390dce 598 int error = 0;
4f083fd7 599
7188ac27
KM
600 dp = VTOI(fndp->ni_dvp);
601 ip = VTOI(fndp->ni_vp);
602 ILOCK(ip);
4f083fd7 603 if ((ip->i_mode&IFMT) == IFDIR) {
7188ac27 604 register struct direct *d = &fndp->ni_dent;
4f083fd7 605
4f083fd7 606 /*
046f18d1 607 * Avoid ".", "..", and aliases of "." for obvious reasons.
4f083fd7 608 */
7188ac27
KM
609 if ((d->d_namlen == 1 && d->d_name[0] == '.') || dp == ip ||
610 fndp->ni_isdotdot || (ip->i_flag & IRENAME)) {
611 IUNLOCK(ip);
612 ufs_abortop(fndp);
613 ufs_abortop(tndp);
614 return (EINVAL);
4f083fd7 615 }
68f21562 616 ip->i_flag |= IRENAME;
4f083fd7
SL
617 oldparent = dp->i_number;
618 doingdirectory++;
619 }
7188ac27 620 vrele(fndp->ni_dvp);
4f083fd7
SL
621
622 /*
623 * 1) Bump link count while we're moving stuff
624 * around. If we crash somewhere before
625 * completing our work, the link count
626 * may be wrong, but correctable.
627 */
628 ip->i_nlink++;
629 ip->i_flag |= ICHG;
7188ac27 630 error = iupdat(ip, &time, &time, 1);
a388503d 631 IUNLOCK(ip);
4f083fd7
SL
632
633 /*
634 * When the target exists, both the directory
7188ac27 635 * and target vnodes are returned locked.
4f083fd7 636 */
7188ac27
KM
637 dp = VTOI(tndp->ni_dvp);
638 xp = NULL;
639 if (tndp->ni_vp)
640 xp = VTOI(tndp->ni_vp);
046f18d1
SL
641 /*
642 * If ".." must be changed (ie the directory gets a new
81552f0f
KM
643 * parent) then the source directory must not be in the
644 * directory heirarchy above the target, as this would
645 * orphan everything below the source directory. Also
646 * the user must have write permission in the source so
647 * as to be able to change "..". We must repeat the call
648 * to namei, as the parent directory is unlocked by the
649 * call to checkpath().
046f18d1 650 */
68f21562
KM
651 if (oldparent != dp->i_number)
652 newparent = dp->i_number;
653 if (doingdirectory && newparent) {
7188ac27 654 if (error = iaccess(ip, IWRITE, tndp->ni_cred))
81552f0f 655 goto bad;
7188ac27 656 tndp->ni_nameiop = RENAME | LOCKPARENT | LOCKLEAF | NOCACHE;
81552f0f 657 do {
7188ac27 658 dp = VTOI(tndp->ni_dvp);
81552f0f 659 if (xp != NULL)
79cf21e2 660 iput(xp);
7188ac27 661 if (error = checkpath(ip, dp, tndp->ni_cred))
81552f0f 662 goto out;
7188ac27 663 if (error = namei(tndp))
81552f0f 664 goto out;
7188ac27
KM
665 xp = NULL;
666 if (tndp->ni_vp)
667 xp = VTOI(tndp->ni_vp);
668 } while (dp != VTOI(tndp->ni_dvp));
81552f0f 669 }
4f083fd7
SL
670 /*
671 * 2) If target doesn't exist, link the target
672 * to the source and unlink the source.
673 * Otherwise, rewrite the target directory
674 * entry to reference the source inode and
675 * expunge the original entry's existence.
676 */
4f083fd7 677 if (xp == NULL) {
7188ac27
KM
678 if (dp->i_dev != ip->i_dev)
679 panic("rename: EXDEV");
4f083fd7 680 /*
68f21562
KM
681 * Account for ".." in new directory.
682 * When source and destination have the same
683 * parent we don't fool with the link count.
4f083fd7 684 */
68f21562 685 if (doingdirectory && newparent) {
4f083fd7
SL
686 dp->i_nlink++;
687 dp->i_flag |= ICHG;
7188ac27 688 error = iupdat(dp, &time, &time, 1);
4f083fd7 689 }
7188ac27 690 if (error = direnter(ip, tndp))
4f083fd7
SL
691 goto out;
692 } else {
7188ac27
KM
693 if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev)
694 panic("rename: EXDEV");
e69c3c9c
SL
695 /*
696 * Short circuit rename(foo, foo).
697 */
698 if (xp->i_number == ip->i_number)
7188ac27 699 panic("rename: same file");
80cee150
JB
700 /*
701 * If the parent directory is "sticky", then the user must
702 * own the parent directory, or the destination of the rename,
703 * otherwise the destination may not be changed (except by
704 * root). This implements append-only directories.
705 */
7188ac27
KM
706 if ((dp->i_mode & ISVTX) && tndp->ni_cred->cr_uid != 0 &&
707 tndp->ni_cred->cr_uid != dp->i_uid &&
708 xp->i_uid != tndp->ni_cred->cr_uid) {
80cee150
JB
709 error = EPERM;
710 goto bad;
711 }
4f083fd7 712 /*
a5390dce
SL
713 * Target must be empty if a directory
714 * and have no links to it.
4f083fd7
SL
715 * Also, insure source and target are
716 * compatible (both directories, or both
717 * not directories).
718 */
719 if ((xp->i_mode&IFMT) == IFDIR) {
7188ac27
KM
720 if (!dirempty(xp, dp->i_number, tndp->ni_cred) ||
721 xp->i_nlink > 2) {
a5390dce 722 error = ENOTEMPTY;
4f083fd7
SL
723 goto bad;
724 }
725 if (!doingdirectory) {
a5390dce 726 error = ENOTDIR;
4f083fd7
SL
727 goto bad;
728 }
7188ac27 729 cache_purge(ITOV(dp));
4f083fd7 730 } else if (doingdirectory) {
a5390dce 731 error = EISDIR;
4f083fd7
SL
732 goto bad;
733 }
7188ac27
KM
734 if (error = dirrewrite(dp, ip, tndp))
735 goto bad;
736 vput(ITOV(dp));
4f083fd7 737 /*
a5390dce
SL
738 * Adjust the link count of the target to
739 * reflect the dirrewrite above. If this is
740 * a directory it is empty and there are
741 * no links to it, so we can squash the inode and
742 * any space associated with it. We disallowed
743 * renaming over top of a directory with links to
68f21562
KM
744 * it above, as the remaining link would point to
745 * a directory without "." or ".." entries.
4f083fd7 746 */
a5390dce 747 xp->i_nlink--;
4f083fd7 748 if (doingdirectory) {
a5390dce
SL
749 if (--xp->i_nlink != 0)
750 panic("rename: linked directory");
7188ac27 751 error = itrunc(xp, (u_long)0);
a5390dce 752 }
4f083fd7 753 xp->i_flag |= ICHG;
88d931ba 754 iput(xp);
31db12cb 755 xp = NULL;
4f083fd7
SL
756 }
757
758 /*
759 * 3) Unlink the source.
760 */
7188ac27
KM
761 fndp->ni_nameiop = DELETE | LOCKPARENT | LOCKLEAF;
762 (void)namei(fndp);
763 if (fndp->ni_vp != NULL) {
764 xp = VTOI(fndp->ni_vp);
765 dp = VTOI(fndp->ni_dvp);
766 } else {
79cf21e2
KM
767 if (fndp->ni_dvp != NULL)
768 vput(fndp->ni_dvp);
7188ac27 769 xp = NULL;
4f1a9037 770 dp = NULL;
7188ac27 771 }
4f083fd7 772 /*
7188ac27 773 * Ensure that the directory entry still exists and has not
68f21562
KM
774 * changed while the new name has been entered. If the source is
775 * a file then the entry may have been unlinked or renamed. In
776 * either case there is no further work to be done. If the source
777 * is a directory then it cannot have been rmdir'ed; its link
778 * count of three would cause a rmdir to fail with ENOTEMPTY.
7188ac27 779 * The IRENAME flag ensures that it cannot be moved by another
68f21562 780 * rename.
4f083fd7 781 */
4f1a9037 782 if (xp != ip) {
68f21562 783 if (doingdirectory)
4f1a9037 784 panic("rename: lost dir entry");
68f21562 785 } else {
4f083fd7 786 /*
68f21562
KM
787 * If the source is a directory with a
788 * new parent, the link count of the old
789 * parent directory must be decremented
790 * and ".." set to point to the new parent.
4f083fd7 791 */
68f21562 792 if (doingdirectory && newparent) {
4f083fd7
SL
793 dp->i_nlink--;
794 dp->i_flag |= ICHG;
68f21562 795 error = rdwri(UIO_READ, xp, (caddr_t)&dirbuf,
7188ac27 796 sizeof (struct dirtemplate), (off_t)0,
93e273b9 797 UIO_SYSSPACE, tndp->ni_cred, (int *)0);
68f21562
KM
798 if (error == 0) {
799 if (dirbuf.dotdot_namlen != 2 ||
800 dirbuf.dotdot_name[0] != '.' ||
801 dirbuf.dotdot_name[1] != '.') {
802 printf("rename: mangled dir\n");
803 } else {
804 dirbuf.dotdot_ino = newparent;
805 (void) rdwri(UIO_WRITE, xp,
806 (caddr_t)&dirbuf,
807 sizeof (struct dirtemplate),
93e273b9 808 (off_t)0, UIO_SYSSPACE,
7188ac27
KM
809 tndp->ni_cred, (int *)0);
810 cache_purge(ITOV(dp));
68f21562
KM
811 }
812 }
4f083fd7 813 }
7188ac27
KM
814 error = dirremove(fndp);
815 if (!error) {
68f21562
KM
816 xp->i_nlink--;
817 xp->i_flag |= ICHG;
4f083fd7 818 }
68f21562 819 xp->i_flag &= ~IRENAME;
4f083fd7 820 }
4f083fd7 821 if (dp)
7188ac27 822 vput(ITOV(dp));
68f21562 823 if (xp)
7188ac27
KM
824 vput(ITOV(xp));
825 vrele(ITOV(ip));
826 return (error);
a5390dce 827
4f083fd7 828bad:
4f083fd7 829 if (xp)
7188ac27
KM
830 vput(ITOV(xp));
831 vput(ITOV(dp));
4f083fd7
SL
832out:
833 ip->i_nlink--;
834 ip->i_flag |= ICHG;
7188ac27
KM
835 vrele(ITOV(ip));
836 return (error);
64d3a787 837}
88a7a62a
SL
838
839/*
840 * A virgin directory (no blushing please).
841 */
842struct dirtemplate mastertemplate = {
843 0, 12, 1, ".",
844 0, DIRBLKSIZ - 12, 2, ".."
845};
846
847/*
848 * Mkdir system call
849 */
7188ac27
KM
850ufs_mkdir(ndp, vap)
851 struct nameidata *ndp;
852 struct vattr *vap;
88a7a62a 853{
88a7a62a 854 register struct inode *ip, *dp;
7188ac27
KM
855 struct inode *tip;
856 struct vnode *dvp;
88a7a62a 857 struct dirtemplate dirtemplate;
7188ac27
KM
858 int error;
859 int dmode;
860
861 dvp = ndp->ni_dvp;
862 dp = VTOI(dvp);
863 dmode = vap->va_mode&0777;
864 dmode |= IFDIR;
88a7a62a
SL
865 /*
866 * Must simulate part of maknode here
867 * in order to acquire the inode, but
868 * not have it entered in the parent
869 * directory. The entry is made later
870 * after writing "." and ".." entries out.
871 */
7188ac27
KM
872 error = ialloc(dp, dirpref(dp->i_fs), dmode, &tip);
873 if (error) {
88a7a62a 874 iput(dp);
7188ac27 875 return (error);
88a7a62a 876 }
7188ac27 877 ip = tip;
88a7a62a
SL
878#ifdef QUOTA
879 if (ip->i_dquot != NODQUOT)
880 panic("mkdir: dquot");
881#endif
882 ip->i_flag |= IACC|IUPD|ICHG;
7188ac27
KM
883 ip->i_mode = dmode;
884 ITOV(ip)->v_type = VDIR; /* Rest init'd in iget() */
88a7a62a 885 ip->i_nlink = 2;
7188ac27 886 ip->i_uid = ndp->ni_cred->cr_uid;
88a7a62a
SL
887 ip->i_gid = dp->i_gid;
888#ifdef QUOTA
889 ip->i_dquot = inoquota(ip);
890#endif
7188ac27 891 error = iupdat(ip, &time, &time, 1);
88a7a62a
SL
892
893 /*
894 * Bump link count in parent directory
895 * to reflect work done below. Should
896 * be done before reference is created
897 * so reparation is possible if we crash.
898 */
899 dp->i_nlink++;
900 dp->i_flag |= ICHG;
7188ac27 901 error = iupdat(dp, &time, &time, 1);
88a7a62a
SL
902
903 /*
904 * Initialize directory with "."
905 * and ".." from static template.
906 */
907 dirtemplate = mastertemplate;
908 dirtemplate.dot_ino = ip->i_number;
909 dirtemplate.dotdot_ino = dp->i_number;
7188ac27
KM
910 error = rdwri(UIO_WRITE, ip, (caddr_t)&dirtemplate,
911 sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE,
912 ndp->ni_cred, (int *)0);
913 if (error) {
88a7a62a
SL
914 dp->i_nlink--;
915 dp->i_flag |= ICHG;
916 goto bad;
917 }
7188ac27
KM
918 if (DIRBLKSIZ > dp->i_fs->fs_fsize)
919 panic("mkdir: blksize"); /* XXX - should grow w/balloc() */
23de9f20
KM
920 else
921 ip->i_size = DIRBLKSIZ;
88a7a62a
SL
922 /*
923 * Directory all set up, now
924 * install the entry for it in
925 * the parent directory.
926 */
7188ac27 927 error = direnter(ip, ndp);
88a7a62a 928 dp = NULL;
7188ac27 929 if (error) {
715baff1 930 ndp->ni_nameiop = LOOKUP | NOCACHE;
7188ac27
KM
931 error = namei(ndp);
932 if (!error) {
933 dp = VTOI(ndp->ni_vp);
88a7a62a
SL
934 dp->i_nlink--;
935 dp->i_flag |= ICHG;
936 }
937 }
938bad:
939 /*
940 * No need to do an explicit itrunc here,
7188ac27 941 * vrele will do this for us because we set
88a7a62a
SL
942 * the link count to 0.
943 */
7188ac27 944 if (error) {
88a7a62a
SL
945 ip->i_nlink = 0;
946 ip->i_flag |= ICHG;
cbcdacd6
KM
947 iput(ip);
948 } else
949 ndp->ni_vp = ITOV(ip);
88a7a62a
SL
950 if (dp)
951 iput(dp);
7188ac27 952 return (error);
88a7a62a
SL
953}
954
955/*
956 * Rmdir system call.
957 */
7188ac27
KM
958ufs_rmdir(ndp)
959 register struct nameidata *ndp;
88a7a62a 960{
88a7a62a 961 register struct inode *ip, *dp;
7188ac27
KM
962 int error = 0;
963
964 ip = VTOI(ndp->ni_vp);
965 dp = VTOI(ndp->ni_dvp);
88a7a62a
SL
966 /*
967 * No rmdir "." please.
968 */
969 if (dp == ip) {
7188ac27 970 vrele(ITOV(dp));
88a7a62a 971 iput(ip);
7188ac27 972 return (EINVAL);
88a7a62a
SL
973 }
974 /*
975 * Verify the directory is empty (and valid).
976 * (Rmdir ".." won't be valid since
977 * ".." will contain a reference to
978 * the current directory and thus be
979 * non-empty.)
980 */
7188ac27
KM
981 if (ip->i_nlink != 2 || !dirempty(ip, dp->i_number, ndp->ni_cred)) {
982 error = ENOTEMPTY;
88a7a62a
SL
983 goto out;
984 }
985 /*
986 * Delete reference to directory before purging
987 * inode. If we crash in between, the directory
988 * will be reattached to lost+found,
989 */
7188ac27 990 if (error = dirremove(ndp))
88a7a62a
SL
991 goto out;
992 dp->i_nlink--;
993 dp->i_flag |= ICHG;
7188ac27 994 cache_purge(ITOV(dp));
88a7a62a 995 iput(dp);
7188ac27 996 ndp->ni_dvp = NULL;
88a7a62a
SL
997 /*
998 * Truncate inode. The only stuff left
999 * in the directory is "." and "..". The
1000 * "." reference is inconsequential since
1001 * we're quashing it. The ".." reference
1002 * has already been adjusted above. We've
1003 * removed the "." reference and the reference
1004 * in the parent directory, but there may be
1005 * other hard links so decrement by 2 and
1006 * worry about them later.
1007 */
1008 ip->i_nlink -= 2;
7188ac27
KM
1009 error = itrunc(ip, (u_long)0);
1010 cache_purge(ITOV(ip));
88a7a62a 1011out:
7188ac27 1012 if (ndp->ni_dvp)
88a7a62a
SL
1013 iput(dp);
1014 iput(ip);
7188ac27 1015 return (error);
88a7a62a
SL
1016}
1017
7188ac27
KM
1018/*
1019 * symlink -- make a symbolic link
1020 */
1021ufs_symlink(ndp, vap, target)
1022 struct nameidata *ndp;
1023 struct vattr *vap;
1024 char *target;
1025{
1026 struct inode *ip;
1027 int error;
1028
1029 error = maknode(IFLNK | vap->va_mode, ndp, &ip);
1030 if (error)
1031 return (error);
1032 error = rdwri(UIO_WRITE, ip, target, strlen(target), (off_t)0,
1033 UIO_SYSSPACE, ndp->ni_cred, (int *)0);
1034 iput(ip);
1035 return (error);
1036}
1037
1038/*
1039 * Vnode op for read and write
1040 */
1041ufs_readdir(vp, uio, offp, cred)
1042 struct vnode *vp;
1043 register struct uio *uio;
1044 off_t *offp;
1045 struct ucred *cred;
88a7a62a 1046{
7188ac27
KM
1047 register struct inode *ip = VTOI(vp);
1048 int count, error;
88a7a62a 1049
7188ac27
KM
1050 ILOCK(ip);
1051 uio->uio_offset = *offp;
1052 count = uio->uio_resid;
1053 count &= ~(DIRBLKSIZ - 1);
1054 if (vp->v_type != VDIR || uio->uio_iovcnt != 1 ||
1055 (count < DIRBLKSIZ) || (uio->uio_offset & (DIRBLKSIZ -1))) {
1056 IUNLOCK(ip);
1057 return (EINVAL);
1058 }
1059 uio->uio_resid = count;
1060 uio->uio_iov->iov_len = count;
1061 error = readip(ip, uio, cred);
1062 *offp += count - uio->uio_resid;
1063 IUNLOCK(ip);
1064 return (error);
1065}
1066
1067/*
1068 * Return target name of a symbolic link
1069 */
1070ufs_readlink(vp, uiop, cred)
1071 struct vnode *vp;
1072 struct uio *uiop;
1073 struct ucred *cred;
1074{
1075
1076 return (readip(VTOI(vp), uiop, cred));
1077}
1078
1079/*
1080 * Ufs abort op, called after namei() when a CREATE/DELETE isn't actually
1081 * done. Iff ni_vp/ni_dvp not null and locked, unlock.
1082 */
1083ufs_abortop(ndp)
1084 register struct nameidata *ndp;
1085{
1086 register struct inode *ip;
1087
1088 if (ndp->ni_vp) {
1089 ip = VTOI(ndp->ni_vp);
1090 if (ip->i_flag & ILOCKED)
1091 IUNLOCK(ip);
1092 vrele(ndp->ni_vp);
8462a185 1093 }
7188ac27
KM
1094 if (ndp->ni_dvp) {
1095 ip = VTOI(ndp->ni_dvp);
1096 if (ip->i_flag & ILOCKED)
1097 IUNLOCK(ip);
1098 vrele(ndp->ni_dvp);
88a7a62a 1099 }
7188ac27
KM
1100 return;
1101}
1102
1103ufs_lock(vp)
1104 struct vnode *vp;
1105{
1106 register struct inode *ip = VTOI(vp);
1107
1108 ILOCK(ip);
1109 return (0);
1110}
1111
1112ufs_unlock(vp)
1113 struct vnode *vp;
1114{
1115 register struct inode *ip = VTOI(vp);
1116
1117 if (!(ip->i_flag & ILOCKED))
1118 panic("ufs_unlock NOT LOCKED");
1119 IUNLOCK(ip);
1120 return (0);
1121}
1122
1123/*
1124 * Get access to bmap
1125 */
1126ufs_bmap(vp, bn, vpp, bnp)
1127 struct vnode *vp;
1128 daddr_t bn;
1129 struct vnode **vpp;
1130 daddr_t *bnp;
1131{
1132 struct inode *ip = VTOI(vp);
1133
1134 if (vpp != NULL)
1135 *vpp = ip->i_devvp;
1136 if (bnp == NULL)
1137 return (0);
1138 return (bmap(ip, bn, bnp, (daddr_t *)0, (int *)0));
88a7a62a
SL
1139}
1140
1141/*
7188ac27 1142 * Just call the device strategy routine
88a7a62a 1143 */
7188ac27
KM
1144ufs_strategy(bp)
1145 register struct buf *bp;
88a7a62a 1146{
7188ac27
KM
1147 (*bdevsw[major(bp->b_dev)].d_strategy)(bp);
1148 return (0);
1149}
88a7a62a 1150
7188ac27
KM
1151/*
1152 * Make a new file.
1153 */
1154maknode(mode, ndp, ipp)
1155 int mode;
1156 register struct nameidata *ndp;
1157 struct inode **ipp;
1158{
1159 register struct inode *ip;
1160 struct inode *tip;
1161 register struct inode *pdir = VTOI(ndp->ni_dvp);
1162 ino_t ipref;
1163 int error;
1164
1165 *ipp = 0;
1166 if ((mode & IFMT) == IFDIR)
1167 ipref = dirpref(pdir->i_fs);
1168 else
1169 ipref = pdir->i_number;
1170 error = ialloc(pdir, ipref, mode, &tip);
1171 if (error) {
1172 iput(pdir);
1173 return (error);
1174 }
1175 ip = tip;
1176#ifdef QUOTA
1177 if (ip->i_dquot != NODQUOT)
1178 panic("maknode: dquot");
1179#endif
1180 ip->i_flag |= IACC|IUPD|ICHG;
1181 if ((mode & IFMT) == 0)
1182 mode |= IFREG;
1183 ip->i_mode = mode;
1184 ITOV(ip)->v_type = IFTOVT(mode); /* Rest init'd in iget() */
1185 ip->i_nlink = 1;
1186 ip->i_uid = ndp->ni_cred->cr_uid;
1187 ip->i_gid = pdir->i_gid;
1188 if ((ip->i_mode & ISGID) && !groupmember(ip->i_gid, ndp->ni_cred) &&
1189 suser(ndp->ni_cred, NULL))
1190 ip->i_mode &= ~ISGID;
1191#ifdef QUOTA
1192 ip->i_dquot = inoquota(ip);
1193#endif
1194
1195 /*
1196 * Make sure inode goes to disk before directory entry.
1197 */
1198 if ((error = iupdat(ip, &time, &time, 1)) ||
1199 (error = direnter(ip, ndp))) {
1200 /*
1201 * Write error occurred trying to update the inode
1202 * or the directory so must deallocate the inode.
1203 */
1204 ip->i_nlink = 0;
1205 ip->i_flag |= ICHG;
1206 iput(ip);
1207 return (error);
1208 }
1209 *ipp = ip;
1210 return (0);
88a7a62a 1211}