reorganize to split out interactive shell
[unix-history] / usr / src / sys / ufs / ffs / ufs_vnops.c
CommitLineData
94368568 1/* ufs_vnops.c 6.14 84/08/29 */
6459ebe0 2
94368568
JB
3#include "param.h"
4#include "systm.h"
5#include "dir.h"
6#include "user.h"
7#include "kernel.h"
8#include "file.h"
9#include "stat.h"
10#include "inode.h"
11#include "fs.h"
12#include "buf.h"
13#include "proc.h"
14#include "quota.h"
15#include "uio.h"
16#include "socket.h"
17#include "socketvar.h"
18#include "mount.h"
88a7a62a
SL
19
20extern struct fileops inodeops;
21struct file *getinode();
3e78e260 22
4f083fd7
SL
23/*
24 * Change current working directory (``.'').
25 */
3e78e260
BJ
26chdir()
27{
28
29 chdirec(&u.u_cdir);
30}
31
4f083fd7
SL
32/*
33 * Change notion of root (``/'') directory.
34 */
3e78e260
BJ
35chroot()
36{
37
38 if (suser())
39 chdirec(&u.u_rdir);
40}
41
4f083fd7
SL
42/*
43 * Common routine for chroot and chdir.
44 */
3e78e260 45chdirec(ipp)
528f664c 46 register struct inode **ipp;
3e78e260
BJ
47{
48 register struct inode *ip;
49 struct a {
50 char *fname;
715baff1
KM
51 } *uap = (struct a *)u.u_ap;
52 register struct nameidata *ndp = &u.u_nd;
3e78e260 53
715baff1
KM
54 ndp->ni_nameiop = LOOKUP | FOLLOW;
55 ndp->ni_segflg = UIO_USERSPACE;
56 ndp->ni_dirp = uap->fname;
57 ip = namei(ndp);
4f083fd7 58 if (ip == NULL)
3e78e260 59 return;
4f083fd7 60 if ((ip->i_mode&IFMT) != IFDIR) {
3e78e260
BJ
61 u.u_error = ENOTDIR;
62 goto bad;
63 }
4f083fd7 64 if (access(ip, IEXEC))
3e78e260 65 goto bad;
a388503d 66 IUNLOCK(ip);
8eee8525
KM
67 if (*ipp)
68 irele(*ipp);
3e78e260
BJ
69 *ipp = ip;
70 return;
71
72bad:
73 iput(ip);
74}
75
76/*
77 * Open system call.
78 */
79open()
80{
88a7a62a 81 struct a {
3e78e260 82 char *fname;
528f664c 83 int mode;
88a7a62a
SL
84 int crtmode;
85 } *uap = (struct a *) u.u_ap;
3e78e260 86
715baff1 87 copen(uap->mode-FOPEN, uap->crtmode, uap->fname);
3e78e260
BJ
88}
89
90/*
91 * Creat system call.
92 */
88a7a62a 93creat()
3e78e260 94{
88a7a62a 95 struct a {
3e78e260
BJ
96 char *fname;
97 int fmode;
88a7a62a 98 } *uap = (struct a *)u.u_ap;
3e78e260 99
715baff1 100 copen(FWRITE|FCREAT|FTRUNC, uap->fmode, uap->fname);
3e78e260
BJ
101}
102
103/*
104 * Common code for open and creat.
88a7a62a
SL
105 * Check permissions, allocate an open file structure,
106 * and call the device open routine if any.
3e78e260 107 */
715baff1 108copen(mode, arg, fname)
88a7a62a
SL
109 register int mode;
110 int arg;
715baff1 111 caddr_t fname;
3e78e260 112{
88a7a62a 113 register struct inode *ip;
3e78e260 114 register struct file *fp;
715baff1 115 register struct nameidata *ndp = &u.u_nd;
88a7a62a 116 int i;
3e78e260 117
88a7a62a
SL
118#ifdef notdef
119 if ((mode&(FREAD|FWRITE)) == 0) {
120 u.u_error = EINVAL;
121 return;
122 }
123#endif
715baff1
KM
124 ndp->ni_segflg = UIO_USERSPACE;
125 ndp->ni_dirp = fname;
88a7a62a 126 if (mode&FCREAT) {
715baff1
KM
127 ndp->ni_nameiop = CREATE | FOLLOW;
128 ip = namei(ndp);
88a7a62a
SL
129 if (ip == NULL) {
130 if (u.u_error)
131 return;
715baff1 132 ip = maknode(arg&07777&(~ISVTX), ndp);
88a7a62a
SL
133 if (ip == NULL)
134 return;
135 mode &= ~FTRUNC;
136 } else {
137 if (mode&FEXCL) {
138 u.u_error = EEXIST;
139 iput(ip);
140 return;
141 }
142 mode &= ~FCREAT;
143 }
144 } else {
715baff1
KM
145 ndp->ni_nameiop = LOOKUP | FOLLOW;
146 ip = namei(ndp);
88a7a62a
SL
147 if (ip == NULL)
148 return;
149 }
150 if ((ip->i_mode & IFMT) == IFSOCK) {
151 u.u_error = EOPNOTSUPP;
152 goto bad;
153 }
154 if ((mode&FCREAT) == 0) {
3e78e260 155 if (mode&FREAD)
528f664c
SL
156 if (access(ip, IREAD))
157 goto bad;
09bf91df 158 if (mode&(FWRITE|FTRUNC)) {
528f664c
SL
159 if (access(ip, IWRITE))
160 goto bad;
161 if ((ip->i_mode&IFMT) == IFDIR) {
3e78e260 162 u.u_error = EISDIR;
528f664c
SL
163 goto bad;
164 }
3e78e260
BJ
165 }
166 }
88a7a62a
SL
167 fp = falloc();
168 if (fp == NULL)
169 goto bad;
170 if (mode&FTRUNC)
4f083fd7 171 itrunc(ip, (u_long)0);
a388503d 172 IUNLOCK(ip);
88a7a62a
SL
173 fp->f_flag = mode&FMASK;
174 fp->f_type = DTYPE_INODE;
175 fp->f_ops = &inodeops;
176 fp->f_data = (caddr_t)ip;
3e78e260 177 i = u.u_r.r_val1;
88a7a62a
SL
178 if (setjmp(&u.u_qsave)) {
179 if (u.u_error == 0)
180 u.u_error = EINTR;
181 u.u_ofile[i] = NULL;
182 closef(fp);
3e78e260 183 return;
528f664c 184 }
88a7a62a
SL
185 u.u_error = openi(ip, mode);
186 if (u.u_error == 0)
187 return;
3e78e260
BJ
188 u.u_ofile[i] = NULL;
189 fp->f_count--;
8eee8525 190 irele(ip);
528f664c
SL
191 return;
192bad:
193 iput(ip);
3e78e260
BJ
194}
195
196/*
197 * Mknod system call
198 */
199mknod()
200{
201 register struct inode *ip;
202 register struct a {
203 char *fname;
204 int fmode;
205 int dev;
715baff1
KM
206 } *uap = (struct a *)u.u_ap;
207 register struct nameidata *ndp = &u.u_nd;
3e78e260 208
88a7a62a
SL
209 if (!suser())
210 return;
715baff1
KM
211 ndp->ni_nameiop = CREATE;
212 ndp->ni_segflg = UIO_USERSPACE;
213 ndp->ni_dirp = uap->fname;
214 ip = namei(ndp);
88a7a62a
SL
215 if (ip != NULL) {
216 u.u_error = EEXIST;
217 goto out;
3e78e260
BJ
218 }
219 if (u.u_error)
220 return;
715baff1 221 ip = maknode(uap->fmode, ndp);
3e78e260
BJ
222 if (ip == NULL)
223 return;
88a7a62a
SL
224 switch (ip->i_mode & IFMT) {
225
7bfaea72 226 case IFMT: /* used by badsect to flag bad sectors */
88a7a62a
SL
227 case IFCHR:
228 case IFBLK:
229 if (uap->dev) {
230 /*
231 * Want to be able to use this to make badblock
232 * inodes, so don't truncate the dev number.
233 */
234 ip->i_rdev = uap->dev;
235 ip->i_flag |= IACC|IUPD|ICHG;
236 }
3e78e260
BJ
237 }
238
239out:
240 iput(ip);
241}
242
243/*
244 * link system call
245 */
246link()
247{
248 register struct inode *ip, *xp;
249 register struct a {
250 char *target;
251 char *linkname;
715baff1
KM
252 } *uap = (struct a *)u.u_ap;
253 register struct nameidata *ndp = &u.u_nd;
3e78e260 254
715baff1
KM
255 ndp->ni_nameiop = LOOKUP | FOLLOW;
256 ndp->ni_segflg = UIO_USERSPACE;
257 ndp->ni_dirp = uap->target;
258 ip = namei(ndp); /* well, this routine is doomed anyhow */
3e78e260
BJ
259 if (ip == NULL)
260 return;
4f083fd7 261 if ((ip->i_mode&IFMT) == IFDIR && !suser()) {
f94ceb3b
BJ
262 iput(ip);
263 return;
264 }
3e78e260
BJ
265 ip->i_nlink++;
266 ip->i_flag |= ICHG;
3fd23f5c 267 iupdat(ip, &time, &time, 1);
a388503d 268 IUNLOCK(ip);
715baff1
KM
269 ndp->ni_nameiop = CREATE;
270 ndp->ni_segflg = UIO_USERSPACE;
271 ndp->ni_dirp = (caddr_t)uap->linkname;
272 xp = namei(ndp);
3e78e260
BJ
273 if (xp != NULL) {
274 u.u_error = EEXIST;
275 iput(xp);
276 goto out;
277 }
278 if (u.u_error)
279 goto out;
715baff1
KM
280 if (ndp->ni_pdir->i_dev != ip->i_dev) {
281 iput(ndp->ni_pdir);
3e78e260
BJ
282 u.u_error = EXDEV;
283 goto out;
284 }
715baff1 285 u.u_error = direnter(ip, ndp);
3e78e260
BJ
286out:
287 if (u.u_error) {
288 ip->i_nlink--;
289 ip->i_flag |= ICHG;
290 }
8eee8525 291 irele(ip);
3e78e260
BJ
292}
293
294/*
295 * symlink -- make a symbolic link
296 */
297symlink()
298{
299 register struct a {
300 char *target;
301 char *linkname;
715baff1 302 } *uap = (struct a *)u.u_ap;
3e78e260
BJ
303 register struct inode *ip;
304 register char *tp;
305 register c, nc;
715baff1 306 register struct nameidata *ndp = &u.u_nd;
3e78e260 307
3e78e260
BJ
308 tp = uap->target;
309 nc = 0;
310 while (c = fubyte(tp)) {
311 if (c < 0) {
312 u.u_error = EFAULT;
313 return;
314 }
315 tp++;
316 nc++;
317 }
715baff1
KM
318 ndp->ni_nameiop = CREATE;
319 ndp->ni_segflg = UIO_USERSPACE;
320 ndp->ni_dirp = uap->linkname;
321 ip = namei(ndp);
3e78e260
BJ
322 if (ip) {
323 iput(ip);
324 u.u_error = EEXIST;
325 return;
326 }
327 if (u.u_error)
328 return;
715baff1 329 ip = maknode(IFLNK | 0777, ndp);
3e78e260
BJ
330 if (ip == NULL)
331 return;
31949305 332 u.u_error = rdwri(UIO_WRITE, ip, uap->target, nc, 0, 0, (int *)0);
4f083fd7 333 /* handle u.u_error != 0 */
3e78e260
BJ
334 iput(ip);
335}
336
337/*
338 * Unlink system call.
339 * Hard to avoid races here, especially
340 * in unlinking directories.
341 */
342unlink()
343{
3e78e260
BJ
344 struct a {
345 char *fname;
715baff1 346 } *uap = (struct a *)u.u_ap;
4f083fd7 347 register struct inode *ip, *dp;
715baff1 348 register struct nameidata *ndp = &u.u_nd;
3e78e260 349
715baff1
KM
350 ndp->ni_nameiop = DELETE | LOCKPARENT;
351 ndp->ni_segflg = UIO_USERSPACE;
352 ndp->ni_dirp = uap->fname;
353 ip = namei(ndp);
4f083fd7 354 if (ip == NULL)
3e78e260 355 return;
715baff1 356 dp = ndp->ni_pdir;
4f083fd7 357 if ((ip->i_mode&IFMT) == IFDIR && !suser())
3e78e260
BJ
358 goto out;
359 /*
360 * Don't unlink a mounted file.
361 */
4f083fd7 362 if (ip->i_dev != dp->i_dev) {
3e78e260
BJ
363 u.u_error = EBUSY;
364 goto out;
365 }
366 if (ip->i_flag&ITEXT)
367 xrele(ip); /* try once to free text */
715baff1 368 if (dirremove(ndp)) {
64d3a787
BJ
369 ip->i_nlink--;
370 ip->i_flag |= ICHG;
6459ebe0 371 }
3e78e260 372out:
4f083fd7 373 if (dp == ip)
8eee8525
KM
374 irele(ip);
375 else
376 iput(ip);
4f083fd7 377 iput(dp);
3e78e260
BJ
378}
379
380/*
381 * Seek system call
382 */
35e7c31a 383lseek()
3e78e260
BJ
384{
385 register struct file *fp;
386 register struct a {
528f664c 387 int fd;
3e78e260
BJ
388 off_t off;
389 int sbase;
715baff1 390 } *uap = (struct a *)u.u_ap;
3e78e260 391
8462a185
SL
392 GETF(fp, uap->fd);
393 if (fp->f_type != DTYPE_INODE) {
394 u.u_error = ESPIPE;
3e78e260 395 return;
8462a185 396 }
b4d1aee9
SL
397 switch (uap->sbase) {
398
399 case L_INCR:
400 fp->f_offset += uap->off;
401 break;
402
403 case L_XTND:
404 fp->f_offset = uap->off + ((struct inode *)fp->f_data)->i_size;
405 break;
406
407 case L_SET:
408 fp->f_offset = uap->off;
409 break;
410
411 default:
412 u.u_error = EINVAL;
413 return;
414 }
415 u.u_r.r_off = fp->f_offset;
3e78e260
BJ
416}
417
418/*
419 * Access system call
420 */
421saccess()
422{
423 register svuid, svgid;
424 register struct inode *ip;
425 register struct a {
426 char *fname;
427 int fmode;
715baff1
KM
428 } *uap = (struct a *)u.u_ap;
429 register struct nameidata *ndp = &u.u_nd;
3e78e260 430
3e78e260
BJ
431 svuid = u.u_uid;
432 svgid = u.u_gid;
433 u.u_uid = u.u_ruid;
434 u.u_gid = u.u_rgid;
715baff1
KM
435 ndp->ni_nameiop = LOOKUP | FOLLOW;
436 ndp->ni_segflg = UIO_USERSPACE;
437 ndp->ni_dirp = uap->fname;
438 ip = namei(ndp);
3e78e260 439 if (ip != NULL) {
88a7a62a 440 if ((uap->fmode&R_OK) && access(ip, IREAD))
528f664c 441 goto done;
88a7a62a 442 if ((uap->fmode&W_OK) && access(ip, IWRITE))
528f664c 443 goto done;
88a7a62a 444 if ((uap->fmode&X_OK) && access(ip, IEXEC))
528f664c
SL
445 goto done;
446done:
3e78e260
BJ
447 iput(ip);
448 }
449 u.u_uid = svuid;
450 u.u_gid = svgid;
451}
d67a03eb 452
d67a03eb 453/*
6459ebe0 454 * Stat system call. This version follows links.
d67a03eb
BJ
455 */
456stat()
457{
d67a03eb 458
715baff1 459 stat1(FOLLOW);
d67a03eb
BJ
460}
461
5485e062 462/*
6459ebe0 463 * Lstat system call. This version does not follow links.
5485e062
BJ
464 */
465lstat()
88a7a62a
SL
466{
467
715baff1 468 stat1(NOFOLLOW);
88a7a62a
SL
469}
470
471stat1(follow)
472 int follow;
5485e062
BJ
473{
474 register struct inode *ip;
475 register struct a {
476 char *fname;
88a7a62a 477 struct stat *ub;
715baff1 478 } *uap = (struct a *)u.u_ap;
88a7a62a 479 struct stat sb;
715baff1 480 register struct nameidata *ndp = &u.u_nd;
5485e062 481
715baff1
KM
482 ndp->ni_nameiop = LOOKUP | follow;
483 ndp->ni_segflg = UIO_USERSPACE;
484 ndp->ni_dirp = uap->fname;
485 ip = namei(ndp);
5485e062
BJ
486 if (ip == NULL)
487 return;
0eaf24e3 488 (void) ino_stat(ip, &sb);
5485e062 489 iput(ip);
88a7a62a 490 u.u_error = copyout((caddr_t)&sb, (caddr_t)uap->ub, sizeof (sb));
d67a03eb
BJ
491}
492
493/*
5485e062
BJ
494 * Return target name of a symbolic link
495 */
496readlink()
497{
498 register struct inode *ip;
499 register struct a {
500 char *name;
501 char *buf;
502 int count;
31949305 503 } *uap = (struct a *)u.u_ap;
715baff1 504 register struct nameidata *ndp = &u.u_nd;
31949305 505 int resid;
5485e062 506
715baff1
KM
507 ndp->ni_nameiop = LOOKUP;
508 ndp->ni_segflg = UIO_USERSPACE;
509 ndp->ni_dirp = uap->name;
510 ip = namei(ndp);
5485e062
BJ
511 if (ip == NULL)
512 return;
513 if ((ip->i_mode&IFMT) != IFLNK) {
514 u.u_error = ENXIO;
515 goto out;
516 }
31949305 517 u.u_error = rdwri(UIO_READ, ip, uap->buf, uap->count, 0, 0, &resid);
5485e062
BJ
518out:
519 iput(ip);
31949305 520 u.u_r.r_val1 = uap->count - resid;
5485e062
BJ
521}
522
4f083fd7
SL
523/*
524 * Change mode of a file given path name.
525 */
3e78e260 526chmod()
5485e062 527{
528f664c
SL
528 struct inode *ip;
529 struct a {
3e78e260
BJ
530 char *fname;
531 int fmode;
715baff1 532 } *uap = (struct a *)u.u_ap;
5485e062 533
715baff1 534 if ((ip = owner(uap->fname, FOLLOW)) == NULL)
5485e062 535 return;
528f664c 536 chmod1(ip, uap->fmode);
4f083fd7 537 iput(ip);
528f664c 538}
f94ceb3b 539
4f083fd7
SL
540/*
541 * Change mode of a file given a file descriptor.
542 */
528f664c
SL
543fchmod()
544{
545 struct a {
546 int fd;
547 int fmode;
715baff1 548 } *uap = (struct a *)u.u_ap;
528f664c
SL
549 register struct inode *ip;
550 register struct file *fp;
551
88a7a62a 552 fp = getinode(uap->fd);
528f664c
SL
553 if (fp == NULL)
554 return;
88a7a62a 555 ip = (struct inode *)fp->f_data;
4f083fd7 556 if (u.u_uid != ip->i_uid && !suser())
528f664c 557 return;
a388503d 558 ILOCK(ip);
528f664c 559 chmod1(ip, uap->fmode);
a388503d 560 IUNLOCK(ip);
528f664c
SL
561}
562
4f083fd7
SL
563/*
564 * Change the mode on a file.
565 * Inode must be locked before calling.
566 */
528f664c
SL
567chmod1(ip, mode)
568 register struct inode *ip;
569 register int mode;
570{
197da11b 571
3e78e260 572 ip->i_mode &= ~07777;
f94ceb3b 573 if (u.u_uid) {
528f664c 574 mode &= ~ISVTX;
bb1b75f4
SL
575 if (!groupmember(ip->i_gid))
576 mode &= ~ISGID;
f94ceb3b 577 }
528f664c 578 ip->i_mode |= mode&07777;
3e78e260
BJ
579 ip->i_flag |= ICHG;
580 if (ip->i_flag&ITEXT && (ip->i_mode&ISVTX)==0)
581 xrele(ip);
5485e062
BJ
582}
583
4f083fd7
SL
584/*
585 * Set ownership given a path name.
586 */
3e78e260 587chown()
d67a03eb 588{
528f664c
SL
589 struct inode *ip;
590 struct a {
3e78e260
BJ
591 char *fname;
592 int uid;
593 int gid;
715baff1 594 } *uap = (struct a *)u.u_ap;
d67a03eb 595
715baff1 596 if (!suser() || (ip = owner(uap->fname, NOFOLLOW)) == NULL)
d67a03eb 597 return;
bb1b75f4 598 u.u_error = chown1(ip, uap->uid, uap->gid);
4f083fd7 599 iput(ip);
528f664c 600}
f94ceb3b 601
4f083fd7
SL
602/*
603 * Set ownership given a file descriptor.
604 */
528f664c
SL
605fchown()
606{
607 struct a {
608 int fd;
609 int uid;
610 int gid;
715baff1 611 } *uap = (struct a *)u.u_ap;
528f664c
SL
612 register struct inode *ip;
613 register struct file *fp;
614
88a7a62a 615 fp = getinode(uap->fd);
528f664c
SL
616 if (fp == NULL)
617 return;
88a7a62a 618 ip = (struct inode *)fp->f_data;
aad8ef38 619 if (!suser())
528f664c 620 return;
a388503d 621 ILOCK(ip);
bb1b75f4 622 u.u_error = chown1(ip, uap->uid, uap->gid);
a388503d 623 IUNLOCK(ip);
528f664c
SL
624}
625
626/*
627 * Perform chown operation on inode ip;
628 * inode must be locked prior to call.
629 */
630chown1(ip, uid, gid)
631 register struct inode *ip;
632 int uid, gid;
633{
634#ifdef QUOTA
635 register long change;
bb1b75f4 636#endif
528f664c 637
bb1b75f4
SL
638 if (uid == -1)
639 uid = ip->i_uid;
640 if (gid == -1)
641 gid = ip->i_gid;
bb1b75f4 642#ifdef QUOTA
3809bf69 643 if (ip->i_uid == uid) /* this just speeds things a little */
0a77f278 644 change = 0;
2e073567
SL
645 else
646 change = ip->i_blocks;
647 (void) chkdq(ip, -change, 1);
648 (void) chkiq(ip->i_dev, ip, ip->i_uid, 1);
0a77f278 649 dqrele(ip->i_dquot);
f94ceb3b 650#endif
bb1b75f4
SL
651 ip->i_uid = uid;
652 ip->i_gid = gid;
3e78e260
BJ
653 ip->i_flag |= ICHG;
654 if (u.u_ruid != 0)
655 ip->i_mode &= ~(ISUID|ISGID);
528f664c 656#ifdef QUOTA
0a77f278 657 ip->i_dquot = inoquota(ip);
2e073567
SL
658 (void) chkdq(ip, change, 1);
659 (void) chkiq(ip->i_dev, (struct inode *)NULL, uid, 1);
660 return (u.u_error); /* should == 0 ALWAYS !! */
661#else
bb1b75f4 662 return (0);
2e073567 663#endif
d67a03eb
BJ
664}
665
bb1b75f4
SL
666utimes()
667{
668 register struct a {
669 char *fname;
670 struct timeval *tptr;
671 } *uap = (struct a *)u.u_ap;
672 register struct inode *ip;
673 struct timeval tv[2];
674
715baff1 675 if ((ip = owner(uap->fname, FOLLOW)) == NULL)
bb1b75f4
SL
676 return;
677 u.u_error = copyin((caddr_t)uap->tptr, (caddr_t)tv, sizeof (tv));
678 if (u.u_error == 0) {
679 ip->i_flag |= IACC|IUPD|ICHG;
680 iupdat(ip, &tv[0], &tv[1], 0);
681 }
682 iput(ip);
683}
d67a03eb 684
4f083fd7
SL
685/*
686 * Flush any pending I/O.
687 */
3e78e260 688sync()
d67a03eb 689{
d67a03eb 690
3fd23f5c 691 update();
d67a03eb 692}
64d3a787 693
4f083fd7
SL
694/*
695 * Truncate a file given its path name.
696 */
528f664c
SL
697truncate()
698{
699 struct a {
700 char *fname;
4f083fd7 701 u_long length;
31949305 702 } *uap = (struct a *)u.u_ap;
528f664c 703 struct inode *ip;
715baff1 704 register struct nameidata *ndp = &u.u_nd;
528f664c 705
715baff1
KM
706 ndp->ni_nameiop = LOOKUP | FOLLOW;
707 ndp->ni_segflg = UIO_USERSPACE;
708 ndp->ni_dirp = uap->fname;
709 ip = namei(ndp);
528f664c
SL
710 if (ip == NULL)
711 return;
712 if (access(ip, IWRITE))
713 goto bad;
714 if ((ip->i_mode&IFMT) == IFDIR) {
715 u.u_error = EISDIR;
716 goto bad;
717 }
718 itrunc(ip, uap->length);
528f664c
SL
719bad:
720 iput(ip);
721}
722
4f083fd7
SL
723/*
724 * Truncate a file given a file descriptor.
725 */
528f664c
SL
726ftruncate()
727{
728 struct a {
729 int fd;
4f083fd7 730 u_long length;
31949305 731 } *uap = (struct a *)u.u_ap;
528f664c
SL
732 struct inode *ip;
733 struct file *fp;
734
88a7a62a 735 fp = getinode(uap->fd);
528f664c
SL
736 if (fp == NULL)
737 return;
528f664c
SL
738 if ((fp->f_flag&FWRITE) == 0) {
739 u.u_error = EINVAL;
740 return;
741 }
88a7a62a 742 ip = (struct inode *)fp->f_data;
a388503d 743 ILOCK(ip);
528f664c 744 itrunc(ip, uap->length);
a388503d 745 IUNLOCK(ip);
4f083fd7
SL
746}
747
748/*
749 * Synch an open file.
750 */
751fsync()
752{
753 struct a {
754 int fd;
755 } *uap = (struct a *)u.u_ap;
756 struct inode *ip;
757 struct file *fp;
758
88a7a62a 759 fp = getinode(uap->fd);
4f083fd7
SL
760 if (fp == NULL)
761 return;
88a7a62a 762 ip = (struct inode *)fp->f_data;
a388503d 763 ILOCK(ip);
4f083fd7 764 syncip(ip);
a388503d 765 IUNLOCK(ip);
528f664c
SL
766}
767
4f083fd7
SL
768/*
769 * Rename system call.
770 * rename("foo", "bar");
771 * is essentially
772 * unlink("bar");
773 * link("foo", "bar");
774 * unlink("foo");
775 * but ``atomically''. Can't do full commit without saving state in the
776 * inode on disk which isn't feasible at this time. Best we can do is
777 * always guarantee the target exists.
778 *
779 * Basic algorithm is:
780 *
781 * 1) Bump link count on source while we're linking it to the
782 * target. This also insure the inode won't be deleted out
68f21562
KM
783 * from underneath us while we work (it may be truncated by
784 * a concurrent `trunc' or `open' for creation).
4f083fd7
SL
785 * 2) Link source to destination. If destination already exists,
786 * delete it first.
68f21562
KM
787 * 3) Unlink source reference to inode if still around. If a
788 * directory was moved and the parent of the destination
4f083fd7
SL
789 * is different from the source, patch the ".." entry in the
790 * directory.
791 *
792 * Source and destination must either both be directories, or both
793 * not be directories. If target is a directory, it must be empty.
794 */
528f664c
SL
795rename()
796{
797 struct a {
798 char *from;
799 char *to;
715baff1 800 } *uap = (struct a *)u.u_ap;
4f083fd7 801 register struct inode *ip, *xp, *dp;
68f21562
KM
802 struct dirtemplate dirbuf;
803 int doingdirectory = 0, oldparent = 0, newparent = 0;
715baff1 804 register struct nameidata *ndp = &u.u_nd;
a5390dce 805 int error = 0;
4f083fd7 806
715baff1
KM
807 ndp->ni_nameiop = DELETE | LOCKPARENT;
808 ndp->ni_segflg = UIO_USERSPACE;
809 ndp->ni_dirp = uap->from;
810 ip = namei(ndp);
4f083fd7
SL
811 if (ip == NULL)
812 return;
715baff1 813 dp = ndp->ni_pdir;
4f083fd7
SL
814 if ((ip->i_mode&IFMT) == IFDIR) {
815 register struct direct *d;
816
715baff1 817 d = &ndp->ni_dent;
4f083fd7 818 /*
046f18d1 819 * Avoid ".", "..", and aliases of "." for obvious reasons.
4f083fd7 820 */
046f18d1
SL
821 if ((d->d_namlen == 1 && d->d_name[0] == '.') ||
822 (d->d_namlen == 2 && bcmp(d->d_name, "..", 2) == 0) ||
68f21562 823 (dp == ip) || (ip->i_flag & IRENAME)) {
046f18d1
SL
824 iput(dp);
825 if (dp == ip)
826 irele(ip);
827 else
4f083fd7 828 iput(ip);
046f18d1
SL
829 u.u_error = EINVAL;
830 return;
4f083fd7 831 }
68f21562 832 ip->i_flag |= IRENAME;
4f083fd7
SL
833 oldparent = dp->i_number;
834 doingdirectory++;
835 }
046f18d1 836 iput(dp);
4f083fd7
SL
837
838 /*
839 * 1) Bump link count while we're moving stuff
840 * around. If we crash somewhere before
841 * completing our work, the link count
842 * may be wrong, but correctable.
843 */
844 ip->i_nlink++;
845 ip->i_flag |= ICHG;
846 iupdat(ip, &time, &time, 1);
a388503d 847 IUNLOCK(ip);
4f083fd7
SL
848
849 /*
850 * When the target exists, both the directory
851 * and target inodes are returned locked.
852 */
715baff1
KM
853 ndp->ni_nameiop = CREATE | LOCKPARENT | NOCACHE;
854 ndp->ni_dirp = (caddr_t)uap->to;
855 xp = namei(ndp);
a5390dce
SL
856 if (u.u_error) {
857 error = u.u_error;
4f083fd7 858 goto out;
a5390dce 859 }
715baff1 860 dp = ndp->ni_pdir;
046f18d1
SL
861 /*
862 * If ".." must be changed (ie the directory gets a new
81552f0f
KM
863 * parent) then the source directory must not be in the
864 * directory heirarchy above the target, as this would
865 * orphan everything below the source directory. Also
866 * the user must have write permission in the source so
867 * as to be able to change "..". We must repeat the call
868 * to namei, as the parent directory is unlocked by the
869 * call to checkpath().
046f18d1 870 */
68f21562
KM
871 if (oldparent != dp->i_number)
872 newparent = dp->i_number;
873 if (doingdirectory && newparent) {
81552f0f
KM
874 if (access(ip, IWRITE))
875 goto bad;
876 do {
715baff1 877 dp = ndp->ni_pdir;
81552f0f
KM
878 if (xp != NULL)
879 iput(xp);
880 u.u_error = checkpath(ip, dp);
881 if (u.u_error)
882 goto out;
715baff1 883 xp = namei(ndp);
81552f0f
KM
884 if (u.u_error) {
885 error = u.u_error;
886 goto out;
887 }
715baff1 888 } while (dp != ndp->ni_pdir);
81552f0f 889 }
4f083fd7
SL
890 /*
891 * 2) If target doesn't exist, link the target
892 * to the source and unlink the source.
893 * Otherwise, rewrite the target directory
894 * entry to reference the source inode and
895 * expunge the original entry's existence.
896 */
4f083fd7
SL
897 if (xp == NULL) {
898 if (dp->i_dev != ip->i_dev) {
a5390dce 899 error = EXDEV;
4f083fd7
SL
900 goto bad;
901 }
902 /*
68f21562
KM
903 * Account for ".." in new directory.
904 * When source and destination have the same
905 * parent we don't fool with the link count.
4f083fd7 906 */
68f21562 907 if (doingdirectory && newparent) {
4f083fd7
SL
908 dp->i_nlink++;
909 dp->i_flag |= ICHG;
910 iupdat(dp, &time, &time, 1);
911 }
715baff1 912 error = direnter(ip, ndp);
f2a5ad78 913 if (error)
4f083fd7
SL
914 goto out;
915 } else {
916 if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev) {
a5390dce 917 error = EXDEV;
4f083fd7
SL
918 goto bad;
919 }
e69c3c9c
SL
920 /*
921 * Short circuit rename(foo, foo).
922 */
923 if (xp->i_number == ip->i_number)
924 goto bad;
4f083fd7 925 /*
a5390dce
SL
926 * Target must be empty if a directory
927 * and have no links to it.
4f083fd7
SL
928 * Also, insure source and target are
929 * compatible (both directories, or both
930 * not directories).
931 */
932 if ((xp->i_mode&IFMT) == IFDIR) {
68f21562 933 if (!dirempty(xp, dp->i_number) || xp->i_nlink > 2) {
a5390dce 934 error = ENOTEMPTY;
4f083fd7
SL
935 goto bad;
936 }
937 if (!doingdirectory) {
a5390dce 938 error = ENOTDIR;
4f083fd7
SL
939 goto bad;
940 }
68f21562 941 cacheinval(dp);
4f083fd7 942 } else if (doingdirectory) {
a5390dce 943 error = EISDIR;
4f083fd7
SL
944 goto bad;
945 }
715baff1 946 dirrewrite(dp, ip, ndp);
a5390dce
SL
947 if (u.u_error) {
948 error = u.u_error;
4f083fd7 949 goto bad1;
a5390dce 950 }
4f083fd7 951 /*
a5390dce
SL
952 * Adjust the link count of the target to
953 * reflect the dirrewrite above. If this is
954 * a directory it is empty and there are
955 * no links to it, so we can squash the inode and
956 * any space associated with it. We disallowed
957 * renaming over top of a directory with links to
68f21562
KM
958 * it above, as the remaining link would point to
959 * a directory without "." or ".." entries.
4f083fd7 960 */
a5390dce 961 xp->i_nlink--;
4f083fd7 962 if (doingdirectory) {
a5390dce
SL
963 if (--xp->i_nlink != 0)
964 panic("rename: linked directory");
4f083fd7 965 itrunc(xp, (u_long)0);
a5390dce 966 }
4f083fd7
SL
967 xp->i_flag |= ICHG;
968 iput(xp);
31db12cb 969 xp = NULL;
4f083fd7
SL
970 }
971
972 /*
973 * 3) Unlink the source.
974 */
715baff1
KM
975 ndp->ni_nameiop = DELETE | LOCKPARENT;
976 ndp->ni_segflg = UIO_USERSPACE;
977 ndp->ni_dirp = uap->from;
68f21562 978 xp = namei(ndp);
715baff1 979 dp = ndp->ni_pdir;
4f083fd7 980 /*
68f21562
KM
981 * Insure that the directory entry still exists and has not
982 * changed while the new name has been entered. If the source is
983 * a file then the entry may have been unlinked or renamed. In
984 * either case there is no further work to be done. If the source
985 * is a directory then it cannot have been rmdir'ed; its link
986 * count of three would cause a rmdir to fail with ENOTEMPTY.
987 * The IRENAME flag insures that it cannot be moved by another
988 * rename.
4f083fd7 989 */
68f21562
KM
990 if (dp == NULL || xp != ip) {
991 if (doingdirectory)
992 panic("rename: lost entry");
993 } else {
4f083fd7 994 /*
68f21562
KM
995 * If the source is a directory with a
996 * new parent, the link count of the old
997 * parent directory must be decremented
998 * and ".." set to point to the new parent.
4f083fd7 999 */
68f21562 1000 if (doingdirectory && newparent) {
4f083fd7
SL
1001 dp->i_nlink--;
1002 dp->i_flag |= ICHG;
68f21562
KM
1003 error = rdwri(UIO_READ, xp, (caddr_t)&dirbuf,
1004 sizeof (struct dirtemplate), (off_t)0, 1,
1005 (int *)0);
1006 if (error == 0) {
1007 if (dirbuf.dotdot_namlen != 2 ||
1008 dirbuf.dotdot_name[0] != '.' ||
1009 dirbuf.dotdot_name[1] != '.') {
1010 printf("rename: mangled dir\n");
1011 } else {
1012 dirbuf.dotdot_ino = newparent;
1013 (void) rdwri(UIO_WRITE, xp,
1014 (caddr_t)&dirbuf,
1015 sizeof (struct dirtemplate),
1016 (off_t)0, 1, (int *)0);
1017 cacheinval(dp);
1018 }
1019 }
4f083fd7 1020 }
715baff1 1021 if (dirremove(ndp)) {
68f21562
KM
1022 xp->i_nlink--;
1023 xp->i_flag |= ICHG;
4f083fd7 1024 }
68f21562
KM
1025 xp->i_flag &= ~IRENAME;
1026 if (error == 0) /* XXX conservative */
a5390dce 1027 error = u.u_error;
4f083fd7 1028 }
4f083fd7
SL
1029 if (dp)
1030 iput(dp);
68f21562
KM
1031 if (xp)
1032 iput(xp);
1033 irele(ip);
1034 if (error)
1035 u.u_error = error;
1036 return;
a5390dce 1037
4f083fd7 1038bad:
31db12cb 1039 iput(dp);
4f083fd7
SL
1040bad1:
1041 if (xp)
31db12cb 1042 iput(xp);
4f083fd7
SL
1043out:
1044 ip->i_nlink--;
1045 ip->i_flag |= ICHG;
1046 irele(ip);
a5390dce
SL
1047 if (error)
1048 u.u_error = error;
528f664c
SL
1049}
1050
64d3a787
BJ
1051/*
1052 * Make a new file.
1053 */
1054struct inode *
715baff1 1055maknode(mode, ndp)
64d3a787 1056 int mode;
715baff1 1057 register struct nameidata *ndp;
64d3a787
BJ
1058{
1059 register struct inode *ip;
715baff1 1060 register struct inode *pdir = ndp->ni_pdir;
64d3a787
BJ
1061 ino_t ipref;
1062
1063 if ((mode & IFMT) == IFDIR)
715baff1 1064 ipref = dirpref(pdir->i_fs);
64d3a787 1065 else
715baff1
KM
1066 ipref = pdir->i_number;
1067 ip = ialloc(pdir, ipref, mode);
64d3a787 1068 if (ip == NULL) {
715baff1 1069 iput(pdir);
528f664c 1070 return (NULL);
64d3a787 1071 }
528f664c 1072#ifdef QUOTA
64d3a787
BJ
1073 if (ip->i_dquot != NODQUOT)
1074 panic("maknode: dquot");
1075#endif
1076 ip->i_flag |= IACC|IUPD|ICHG;
1077 if ((mode & IFMT) == 0)
1078 mode |= IFREG;
1079 ip->i_mode = mode & ~u.u_cmask;
1080 ip->i_nlink = 1;
1081 ip->i_uid = u.u_uid;
715baff1 1082 ip->i_gid = pdir->i_gid;
bb1b75f4
SL
1083 if (ip->i_mode & ISGID && !groupmember(ip->i_gid))
1084 ip->i_mode &= ~ISGID;
528f664c 1085#ifdef QUOTA
64d3a787
BJ
1086 ip->i_dquot = inoquota(ip);
1087#endif
1088
1089 /*
1090 * Make sure inode goes to disk before directory entry.
1091 */
3fd23f5c 1092 iupdat(ip, &time, &time, 1);
715baff1 1093 u.u_error = direnter(ip, ndp);
64d3a787
BJ
1094 if (u.u_error) {
1095 /*
f2a5ad78
SL
1096 * Write error occurred trying to update directory
1097 * so must deallocate the inode.
64d3a787
BJ
1098 */
1099 ip->i_nlink = 0;
1100 ip->i_flag |= ICHG;
1101 iput(ip);
528f664c 1102 return (NULL);
64d3a787 1103 }
528f664c 1104 return (ip);
64d3a787 1105}
88a7a62a
SL
1106
1107/*
1108 * A virgin directory (no blushing please).
1109 */
1110struct dirtemplate mastertemplate = {
1111 0, 12, 1, ".",
1112 0, DIRBLKSIZ - 12, 2, ".."
1113};
1114
1115/*
1116 * Mkdir system call
1117 */
1118mkdir()
1119{
1120 struct a {
1121 char *name;
1122 int dmode;
715baff1 1123 } *uap = (struct a *)u.u_ap;
88a7a62a
SL
1124 register struct inode *ip, *dp;
1125 struct dirtemplate dirtemplate;
715baff1 1126 register struct nameidata *ndp = &u.u_nd;
88a7a62a 1127
715baff1
KM
1128 ndp->ni_nameiop = CREATE;
1129 ndp->ni_segflg = UIO_USERSPACE;
1130 ndp->ni_dirp = uap->name;
1131 ip = namei(ndp);
88a7a62a
SL
1132 if (u.u_error)
1133 return;
1134 if (ip != NULL) {
1135 iput(ip);
1136 u.u_error = EEXIST;
1137 return;
1138 }
715baff1 1139 dp = ndp->ni_pdir;
88a7a62a
SL
1140 uap->dmode &= 0777;
1141 uap->dmode |= IFDIR;
1142 /*
1143 * Must simulate part of maknode here
1144 * in order to acquire the inode, but
1145 * not have it entered in the parent
1146 * directory. The entry is made later
1147 * after writing "." and ".." entries out.
1148 */
1149 ip = ialloc(dp, dirpref(dp->i_fs), uap->dmode);
1150 if (ip == NULL) {
1151 iput(dp);
1152 return;
1153 }
1154#ifdef QUOTA
1155 if (ip->i_dquot != NODQUOT)
1156 panic("mkdir: dquot");
1157#endif
1158 ip->i_flag |= IACC|IUPD|ICHG;
1159 ip->i_mode = uap->dmode & ~u.u_cmask;
1160 ip->i_nlink = 2;
1161 ip->i_uid = u.u_uid;
1162 ip->i_gid = dp->i_gid;
1163#ifdef QUOTA
1164 ip->i_dquot = inoquota(ip);
1165#endif
1166 iupdat(ip, &time, &time, 1);
1167
1168 /*
1169 * Bump link count in parent directory
1170 * to reflect work done below. Should
1171 * be done before reference is created
1172 * so reparation is possible if we crash.
1173 */
1174 dp->i_nlink++;
1175 dp->i_flag |= ICHG;
1176 iupdat(dp, &time, &time, 1);
1177
1178 /*
1179 * Initialize directory with "."
1180 * and ".." from static template.
1181 */
1182 dirtemplate = mastertemplate;
1183 dirtemplate.dot_ino = ip->i_number;
1184 dirtemplate.dotdot_ino = dp->i_number;
1185 u.u_error = rdwri(UIO_WRITE, ip, (caddr_t)&dirtemplate,
1186 sizeof (dirtemplate), (off_t)0, 1, (int *)0);
1187 if (u.u_error) {
1188 dp->i_nlink--;
1189 dp->i_flag |= ICHG;
1190 goto bad;
1191 }
1192 /*
1193 * Directory all set up, now
1194 * install the entry for it in
1195 * the parent directory.
1196 */
715baff1 1197 u.u_error = direnter(ip, ndp);
88a7a62a
SL
1198 dp = NULL;
1199 if (u.u_error) {
715baff1
KM
1200 ndp->ni_nameiop = LOOKUP | NOCACHE;
1201 ndp->ni_segflg = UIO_USERSPACE;
1202 ndp->ni_dirp = uap->name;
1203 dp = namei(ndp);
88a7a62a
SL
1204 if (dp) {
1205 dp->i_nlink--;
1206 dp->i_flag |= ICHG;
1207 }
1208 }
1209bad:
1210 /*
1211 * No need to do an explicit itrunc here,
1212 * irele will do this for us because we set
1213 * the link count to 0.
1214 */
1215 if (u.u_error) {
1216 ip->i_nlink = 0;
1217 ip->i_flag |= ICHG;
1218 }
1219 if (dp)
1220 iput(dp);
1221 iput(ip);
1222}
1223
1224/*
1225 * Rmdir system call.
1226 */
1227rmdir()
1228{
1229 struct a {
1230 char *name;
715baff1 1231 } *uap = (struct a *)u.u_ap;
88a7a62a 1232 register struct inode *ip, *dp;
715baff1 1233 register struct nameidata *ndp = &u.u_nd;
88a7a62a 1234
715baff1
KM
1235 ndp->ni_nameiop = DELETE | LOCKPARENT;
1236 ndp->ni_segflg = UIO_USERSPACE;
1237 ndp->ni_dirp = uap->name;
1238 ip = namei(ndp);
88a7a62a
SL
1239 if (ip == NULL)
1240 return;
715baff1 1241 dp = ndp->ni_pdir;
88a7a62a
SL
1242 /*
1243 * No rmdir "." please.
1244 */
1245 if (dp == ip) {
1246 irele(dp);
1247 iput(ip);
1248 u.u_error = EINVAL;
1249 return;
1250 }
1251 if ((ip->i_mode&IFMT) != IFDIR) {
1252 u.u_error = ENOTDIR;
1253 goto out;
1254 }
1255 /*
1256 * Don't remove a mounted on directory.
1257 */
1258 if (ip->i_dev != dp->i_dev) {
1259 u.u_error = EBUSY;
1260 goto out;
1261 }
1262 /*
1263 * Verify the directory is empty (and valid).
1264 * (Rmdir ".." won't be valid since
1265 * ".." will contain a reference to
1266 * the current directory and thus be
1267 * non-empty.)
1268 */
68f21562 1269 if (ip->i_nlink != 2 || !dirempty(ip, dp->i_number)) {
88a7a62a
SL
1270 u.u_error = ENOTEMPTY;
1271 goto out;
1272 }
1273 /*
1274 * Delete reference to directory before purging
1275 * inode. If we crash in between, the directory
1276 * will be reattached to lost+found,
1277 */
715baff1 1278 if (dirremove(ndp) == 0)
88a7a62a
SL
1279 goto out;
1280 dp->i_nlink--;
1281 dp->i_flag |= ICHG;
68f21562 1282 cacheinval(dp);
88a7a62a
SL
1283 iput(dp);
1284 dp = NULL;
1285 /*
1286 * Truncate inode. The only stuff left
1287 * in the directory is "." and "..". The
1288 * "." reference is inconsequential since
1289 * we're quashing it. The ".." reference
1290 * has already been adjusted above. We've
1291 * removed the "." reference and the reference
1292 * in the parent directory, but there may be
1293 * other hard links so decrement by 2 and
1294 * worry about them later.
1295 */
1296 ip->i_nlink -= 2;
1297 itrunc(ip, (u_long)0);
1d00c1cf 1298 cacheinval(ip);
88a7a62a
SL
1299out:
1300 if (dp)
1301 iput(dp);
1302 iput(ip);
1303}
1304
1305struct file *
1306getinode(fdes)
1307 int fdes;
1308{
8462a185 1309 struct file *fp;
88a7a62a 1310
8462a185
SL
1311 if ((unsigned)fdes >= NOFILE || (fp = u.u_ofile[fdes]) == NULL) {
1312 u.u_error = EBADF;
1313 return ((struct file *)0);
1314 }
88a7a62a
SL
1315 if (fp->f_type != DTYPE_INODE) {
1316 u.u_error = EINVAL;
8462a185 1317 return ((struct file *)0);
88a7a62a
SL
1318 }
1319 return (fp);
1320}
1321
1322/*
1323 * mode mask for creation of files
1324 */
1325umask()
1326{
1327 register struct a {
1328 int mask;
715baff1 1329 } *uap = (struct a *)u.u_ap;
88a7a62a 1330
88a7a62a
SL
1331 u.u_r.r_val1 = u.u_cmask;
1332 u.u_cmask = uap->mask & 07777;
1333}