/* vfs_syscalls.c 6.14 84/08/29 */
extern struct fileops inodeops
;
* Change current working directory (``.'').
* Change notion of root (``/'') directory.
* Common routine for chroot and chdir.
register struct inode
**ipp
;
register struct inode
*ip
;
} *uap
= (struct a
*)u
.u_ap
;
register struct nameidata
*ndp
= &u
.u_nd
;
ndp
->ni_nameiop
= LOOKUP
| FOLLOW
;
ndp
->ni_segflg
= UIO_USERSPACE
;
ndp
->ni_dirp
= uap
->fname
;
if ((ip
->i_mode
&IFMT
) != IFDIR
) {
} *uap
= (struct a
*) u
.u_ap
;
copen(uap
->mode
-FOPEN
, uap
->crtmode
, uap
->fname
);
} *uap
= (struct a
*)u
.u_ap
;
copen(FWRITE
|FCREAT
|FTRUNC
, uap
->fmode
, uap
->fname
);
* Common code for open and creat.
* Check permissions, allocate an open file structure,
* and call the device open routine if any.
register struct inode
*ip
;
register struct file
*fp
;
register struct nameidata
*ndp
= &u
.u_nd
;
if ((mode
&(FREAD
|FWRITE
)) == 0) {
ndp
->ni_segflg
= UIO_USERSPACE
;
ndp
->ni_nameiop
= CREATE
| FOLLOW
;
ip
= maknode(arg
&07777&(~ISVTX
), ndp
);
ndp
->ni_nameiop
= LOOKUP
| FOLLOW
;
if ((ip
->i_mode
& IFMT
) == IFSOCK
) {
if ((mode
&FCREAT
) == 0) {
if (mode
&(FWRITE
|FTRUNC
)) {
if ((ip
->i_mode
&IFMT
) == IFDIR
) {
fp
->f_type
= DTYPE_INODE
;
fp
->f_data
= (caddr_t
)ip
;
if (setjmp(&u
.u_qsave
)) {
u
.u_error
= openi(ip
, mode
);
register struct inode
*ip
;
} *uap
= (struct a
*)u
.u_ap
;
register struct nameidata
*ndp
= &u
.u_nd
;
ndp
->ni_nameiop
= CREATE
;
ndp
->ni_segflg
= UIO_USERSPACE
;
ndp
->ni_dirp
= uap
->fname
;
ip
= maknode(uap
->fmode
, ndp
);
switch (ip
->i_mode
& IFMT
) {
case IFMT
: /* used by badsect to flag bad sectors */
* Want to be able to use this to make badblock
* inodes, so don't truncate the dev number.
ip
->i_flag
|= IACC
|IUPD
|ICHG
;
register struct inode
*ip
, *xp
;
} *uap
= (struct a
*)u
.u_ap
;
register struct nameidata
*ndp
= &u
.u_nd
;
ndp
->ni_nameiop
= LOOKUP
| FOLLOW
;
ndp
->ni_segflg
= UIO_USERSPACE
;
ndp
->ni_dirp
= uap
->target
;
ip
= namei(ndp
); /* well, this routine is doomed anyhow */
if ((ip
->i_mode
&IFMT
) == IFDIR
&& !suser()) {
iupdat(ip
, &time
, &time
, 1);
ndp
->ni_nameiop
= CREATE
;
ndp
->ni_segflg
= UIO_USERSPACE
;
ndp
->ni_dirp
= (caddr_t
)uap
->linkname
;
if (ndp
->ni_pdir
->i_dev
!= ip
->i_dev
) {
u
.u_error
= direnter(ip
, ndp
);
* symlink -- make a symbolic link
} *uap
= (struct a
*)u
.u_ap
;
register struct inode
*ip
;
register struct nameidata
*ndp
= &u
.u_nd
;
ndp
->ni_nameiop
= CREATE
;
ndp
->ni_segflg
= UIO_USERSPACE
;
ndp
->ni_dirp
= uap
->linkname
;
ip
= maknode(IFLNK
| 0777, ndp
);
u
.u_error
= rdwri(UIO_WRITE
, ip
, uap
->target
, nc
, 0, 0, (int *)0);
/* handle u.u_error != 0 */
* Hard to avoid races here, especially
* in unlinking directories.
} *uap
= (struct a
*)u
.u_ap
;
register struct inode
*ip
, *dp
;
register struct nameidata
*ndp
= &u
.u_nd
;
ndp
->ni_nameiop
= DELETE
| LOCKPARENT
;
ndp
->ni_segflg
= UIO_USERSPACE
;
ndp
->ni_dirp
= uap
->fname
;
if ((ip
->i_mode
&IFMT
) == IFDIR
&& !suser())
* Don't unlink a mounted file.
if (ip
->i_dev
!= dp
->i_dev
) {
xrele(ip
); /* try once to free text */
register struct file
*fp
;
} *uap
= (struct a
*)u
.u_ap
;
if (fp
->f_type
!= DTYPE_INODE
) {
fp
->f_offset
+= uap
->off
;
fp
->f_offset
= uap
->off
+ ((struct inode
*)fp
->f_data
)->i_size
;
u
.u_r
.r_off
= fp
->f_offset
;
register struct inode
*ip
;
} *uap
= (struct a
*)u
.u_ap
;
register struct nameidata
*ndp
= &u
.u_nd
;
ndp
->ni_nameiop
= LOOKUP
| FOLLOW
;
ndp
->ni_segflg
= UIO_USERSPACE
;
ndp
->ni_dirp
= uap
->fname
;
if ((uap
->fmode
&R_OK
) && access(ip
, IREAD
))
if ((uap
->fmode
&W_OK
) && access(ip
, IWRITE
))
if ((uap
->fmode
&X_OK
) && access(ip
, IEXEC
))
* Stat system call. This version follows links.
* Lstat system call. This version does not follow links.
register struct inode
*ip
;
} *uap
= (struct a
*)u
.u_ap
;
register struct nameidata
*ndp
= &u
.u_nd
;
ndp
->ni_nameiop
= LOOKUP
| follow
;
ndp
->ni_segflg
= UIO_USERSPACE
;
ndp
->ni_dirp
= uap
->fname
;
(void) ino_stat(ip
, &sb
);
u
.u_error
= copyout((caddr_t
)&sb
, (caddr_t
)uap
->ub
, sizeof (sb
));
* Return target name of a symbolic link
register struct inode
*ip
;
} *uap
= (struct a
*)u
.u_ap
;
register struct nameidata
*ndp
= &u
.u_nd
;
ndp
->ni_nameiop
= LOOKUP
;
ndp
->ni_segflg
= UIO_USERSPACE
;
ndp
->ni_dirp
= uap
->name
;
if ((ip
->i_mode
&IFMT
) != IFLNK
) {
u
.u_error
= rdwri(UIO_READ
, ip
, uap
->buf
, uap
->count
, 0, 0, &resid
);
u
.u_r
.r_val1
= uap
->count
- resid
;
* Change mode of a file given path name.
} *uap
= (struct a
*)u
.u_ap
;
if ((ip
= owner(uap
->fname
, FOLLOW
)) == NULL
)
* Change mode of a file given a file descriptor.
} *uap
= (struct a
*)u
.u_ap
;
register struct inode
*ip
;
register struct file
*fp
;
ip
= (struct inode
*)fp
->f_data
;
if (u
.u_uid
!= ip
->i_uid
&& !suser())
* Change the mode on a file.
* Inode must be locked before calling.
register struct inode
*ip
;
if (!groupmember(ip
->i_gid
))
ip
->i_mode
|= mode
&07777;
if (ip
->i_flag
&ITEXT
&& (ip
->i_mode
&ISVTX
)==0)
* Set ownership given a path name.
} *uap
= (struct a
*)u
.u_ap
;
if (!suser() || (ip
= owner(uap
->fname
, NOFOLLOW
)) == NULL
)
u
.u_error
= chown1(ip
, uap
->uid
, uap
->gid
);
* Set ownership given a file descriptor.
} *uap
= (struct a
*)u
.u_ap
;
register struct inode
*ip
;
register struct file
*fp
;
ip
= (struct inode
*)fp
->f_data
;
u
.u_error
= chown1(ip
, uap
->uid
, uap
->gid
);
* Perform chown operation on inode ip;
* inode must be locked prior to call.
register struct inode
*ip
;
if (ip
->i_uid
== uid
) /* this just speeds things a little */
(void) chkdq(ip
, -change
, 1);
(void) chkiq(ip
->i_dev
, ip
, ip
->i_uid
, 1);
ip
->i_mode
&= ~(ISUID
|ISGID
);
ip
->i_dquot
= inoquota(ip
);
(void) chkdq(ip
, change
, 1);
(void) chkiq(ip
->i_dev
, (struct inode
*)NULL
, uid
, 1);
return (u
.u_error
); /* should == 0 ALWAYS !! */
} *uap
= (struct a
*)u
.u_ap
;
register struct inode
*ip
;
if ((ip
= owner(uap
->fname
, FOLLOW
)) == NULL
)
u
.u_error
= copyin((caddr_t
)uap
->tptr
, (caddr_t
)tv
, sizeof (tv
));
ip
->i_flag
|= IACC
|IUPD
|ICHG
;
iupdat(ip
, &tv
[0], &tv
[1], 0);
* Truncate a file given its path name.
} *uap
= (struct a
*)u
.u_ap
;
register struct nameidata
*ndp
= &u
.u_nd
;
ndp
->ni_nameiop
= LOOKUP
| FOLLOW
;
ndp
->ni_segflg
= UIO_USERSPACE
;
ndp
->ni_dirp
= uap
->fname
;
if ((ip
->i_mode
&IFMT
) == IFDIR
) {
* Truncate a file given a file descriptor.
} *uap
= (struct a
*)u
.u_ap
;
if ((fp
->f_flag
&FWRITE
) == 0) {
ip
= (struct inode
*)fp
->f_data
;
} *uap
= (struct a
*)u
.u_ap
;
ip
= (struct inode
*)fp
->f_data
;
* but ``atomically''. Can't do full commit without saving state in the
* inode on disk which isn't feasible at this time. Best we can do is
* always guarantee the target exists.
* 1) Bump link count on source while we're linking it to the
* target. This also insure the inode won't be deleted out
* from underneath us while we work (it may be truncated by
* a concurrent `trunc' or `open' for creation).
* 2) Link source to destination. If destination already exists,
* 3) Unlink source reference to inode if still around. If a
* directory was moved and the parent of the destination
* is different from the source, patch the ".." entry in the
* Source and destination must either both be directories, or both
* not be directories. If target is a directory, it must be empty.
} *uap
= (struct a
*)u
.u_ap
;
register struct inode
*ip
, *xp
, *dp
;
struct dirtemplate dirbuf
;
int doingdirectory
= 0, oldparent
= 0, newparent
= 0;
register struct nameidata
*ndp
= &u
.u_nd
;
ndp
->ni_nameiop
= DELETE
| LOCKPARENT
;
ndp
->ni_segflg
= UIO_USERSPACE
;
ndp
->ni_dirp
= uap
->from
;
if ((ip
->i_mode
&IFMT
) == IFDIR
) {
register struct direct
*d
;
* Avoid ".", "..", and aliases of "." for obvious reasons.
if ((d
->d_namlen
== 1 && d
->d_name
[0] == '.') ||
(d
->d_namlen
== 2 && bcmp(d
->d_name
, "..", 2) == 0) ||
(dp
== ip
) || (ip
->i_flag
& IRENAME
)) {
oldparent
= dp
->i_number
;
* 1) Bump link count while we're moving stuff
* around. If we crash somewhere before
* completing our work, the link count
* may be wrong, but correctable.
iupdat(ip
, &time
, &time
, 1);
* When the target exists, both the directory
* and target inodes are returned locked.
ndp
->ni_nameiop
= CREATE
| LOCKPARENT
| NOCACHE
;
ndp
->ni_dirp
= (caddr_t
)uap
->to
;
* If ".." must be changed (ie the directory gets a new
* parent) then the source directory must not be in the
* directory heirarchy above the target, as this would
* orphan everything below the source directory. Also
* the user must have write permission in the source so
* as to be able to change "..". We must repeat the call
* to namei, as the parent directory is unlocked by the
if (oldparent
!= dp
->i_number
)
newparent
= dp
->i_number
;
if (doingdirectory
&& newparent
) {
u
.u_error
= checkpath(ip
, dp
);
} while (dp
!= ndp
->ni_pdir
);
* 2) If target doesn't exist, link the target
* to the source and unlink the source.
* Otherwise, rewrite the target directory
* entry to reference the source inode and
* expunge the original entry's existence.
if (dp
->i_dev
!= ip
->i_dev
) {
* Account for ".." in new directory.
* When source and destination have the same
* parent we don't fool with the link count.
if (doingdirectory
&& newparent
) {
iupdat(dp
, &time
, &time
, 1);
error
= direnter(ip
, ndp
);
if (xp
->i_dev
!= dp
->i_dev
|| xp
->i_dev
!= ip
->i_dev
) {
* Short circuit rename(foo, foo).
if (xp
->i_number
== ip
->i_number
)
* Target must be empty if a directory
* and have no links to it.
* Also, insure source and target are
* compatible (both directories, or both
if ((xp
->i_mode
&IFMT
) == IFDIR
) {
if (!dirempty(xp
, dp
->i_number
) || xp
->i_nlink
> 2) {
} else if (doingdirectory
) {
* Adjust the link count of the target to
* reflect the dirrewrite above. If this is
* a directory it is empty and there are
* no links to it, so we can squash the inode and
* any space associated with it. We disallowed
* renaming over top of a directory with links to
* it above, as the remaining link would point to
* a directory without "." or ".." entries.
panic("rename: linked directory");
ndp
->ni_nameiop
= DELETE
| LOCKPARENT
;
ndp
->ni_segflg
= UIO_USERSPACE
;
ndp
->ni_dirp
= uap
->from
;
* Insure that the directory entry still exists and has not
* changed while the new name has been entered. If the source is
* a file then the entry may have been unlinked or renamed. In
* either case there is no further work to be done. If the source
* is a directory then it cannot have been rmdir'ed; its link
* count of three would cause a rmdir to fail with ENOTEMPTY.
* The IRENAME flag insures that it cannot be moved by another
if (dp
== NULL
|| xp
!= ip
) {
panic("rename: lost entry");
* If the source is a directory with a
* new parent, the link count of the old
* parent directory must be decremented
* and ".." set to point to the new parent.
if (doingdirectory
&& newparent
) {
error
= rdwri(UIO_READ
, xp
, (caddr_t
)&dirbuf
,
sizeof (struct dirtemplate
), (off_t
)0, 1,
if (dirbuf
.dotdot_namlen
!= 2 ||
dirbuf
.dotdot_name
[0] != '.' ||
dirbuf
.dotdot_name
[1] != '.') {
printf("rename: mangled dir\n");
dirbuf
.dotdot_ino
= newparent
;
(void) rdwri(UIO_WRITE
, xp
,
sizeof (struct dirtemplate
),
if (error
== 0) /* XXX conservative */
register struct nameidata
*ndp
;
register struct inode
*ip
;
register struct inode
*pdir
= ndp
->ni_pdir
;
if ((mode
& IFMT
) == IFDIR
)
ipref
= dirpref(pdir
->i_fs
);
ip
= ialloc(pdir
, ipref
, mode
);
if (ip
->i_dquot
!= NODQUOT
)
ip
->i_flag
|= IACC
|IUPD
|ICHG
;
ip
->i_mode
= mode
& ~u
.u_cmask
;
if (ip
->i_mode
& ISGID
&& !groupmember(ip
->i_gid
))
ip
->i_dquot
= inoquota(ip
);
* Make sure inode goes to disk before directory entry.
iupdat(ip
, &time
, &time
, 1);
u
.u_error
= direnter(ip
, ndp
);
* Write error occurred trying to update directory
* so must deallocate the inode.
* A virgin directory (no blushing please).
struct dirtemplate mastertemplate
= {
0, DIRBLKSIZ
- 12, 2, ".."
} *uap
= (struct a
*)u
.u_ap
;
register struct inode
*ip
, *dp
;
struct dirtemplate dirtemplate
;
register struct nameidata
*ndp
= &u
.u_nd
;
ndp
->ni_nameiop
= CREATE
;
ndp
->ni_segflg
= UIO_USERSPACE
;
ndp
->ni_dirp
= uap
->name
;
* Must simulate part of maknode here
* in order to acquire the inode, but
* not have it entered in the parent
* directory. The entry is made later
* after writing "." and ".." entries out.
ip
= ialloc(dp
, dirpref(dp
->i_fs
), uap
->dmode
);
if (ip
->i_dquot
!= NODQUOT
)
ip
->i_flag
|= IACC
|IUPD
|ICHG
;
ip
->i_mode
= uap
->dmode
& ~u
.u_cmask
;
ip
->i_dquot
= inoquota(ip
);
iupdat(ip
, &time
, &time
, 1);
* Bump link count in parent directory
* to reflect work done below. Should
* be done before reference is created
* so reparation is possible if we crash.
iupdat(dp
, &time
, &time
, 1);
* Initialize directory with "."
* and ".." from static template.
dirtemplate
= mastertemplate
;
dirtemplate
.dot_ino
= ip
->i_number
;
dirtemplate
.dotdot_ino
= dp
->i_number
;
u
.u_error
= rdwri(UIO_WRITE
, ip
, (caddr_t
)&dirtemplate
,
sizeof (dirtemplate
), (off_t
)0, 1, (int *)0);
* Directory all set up, now
* install the entry for it in
u
.u_error
= direnter(ip
, ndp
);
ndp
->ni_nameiop
= LOOKUP
| NOCACHE
;
ndp
->ni_segflg
= UIO_USERSPACE
;
ndp
->ni_dirp
= uap
->name
;
* No need to do an explicit itrunc here,
* irele will do this for us because we set
} *uap
= (struct a
*)u
.u_ap
;
register struct inode
*ip
, *dp
;
register struct nameidata
*ndp
= &u
.u_nd
;
ndp
->ni_nameiop
= DELETE
| LOCKPARENT
;
ndp
->ni_segflg
= UIO_USERSPACE
;
ndp
->ni_dirp
= uap
->name
;
if ((ip
->i_mode
&IFMT
) != IFDIR
) {
* Don't remove a mounted on directory.
if (ip
->i_dev
!= dp
->i_dev
) {
* Verify the directory is empty (and valid).
* (Rmdir ".." won't be valid since
* ".." will contain a reference to
* the current directory and thus be
if (ip
->i_nlink
!= 2 || !dirempty(ip
, dp
->i_number
)) {
* Delete reference to directory before purging
* inode. If we crash in between, the directory
* will be reattached to lost+found,
* Truncate inode. The only stuff left
* in the directory is "." and "..". The
* "." reference is inconsequential since
* we're quashing it. The ".." reference
* has already been adjusted above. We've
* removed the "." reference and the reference
* in the parent directory, but there may be
* other hard links so decrement by 2 and
* worry about them later.
if ((unsigned)fdes
>= NOFILE
|| (fp
= u
.u_ofile
[fdes
]) == NULL
) {
return ((struct file
*)0);
if (fp
->f_type
!= DTYPE_INODE
) {
return ((struct file
*)0);
* mode mask for creation of files
} *uap
= (struct a
*)u
.u_ap
;
u
.u_r
.r_val1
= u
.u_cmask
;
u
.u_cmask
= uap
->mask
& 07777;