/* ufs_syscalls.c 4.53 83/03/22 */
#include "../h/descrip.h"
#include "../h/socketvar.h"
* Change current working directory (``.'').
* Change notion of root (``/'') directory.
* Common routine for chroot and chdir.
register struct inode
**ipp
;
register struct inode
*ip
;
ip
= namei(uchar
, LOOKUP
, 1);
if ((ip
->i_mode
&IFMT
) != IFDIR
) {
register struct inode
*ip
;
int checkpermissions
= 1, flags
;
uap
= (struct a
*)u
.u_ap
;
if ((flags
&FTRUNCATE
) && (flags
&FWRITE
) == 0) {
ip
= namei(uchar
, CREATE
, 1);
ip
= maknode(uap
->mode
&07777&(~ISVTX
));
ip
= namei(uchar
, LOOKUP
, 1);
open1(ip
, flags
, checkpermissions
);
register struct inode
*ip
;
uap
= (struct a
*)u
.u_ap
;
ip
= namei(uchar
, CREATE
, 1);
ip
= maknode(uap
->fmode
&07777&(~ISVTX
));
open1(ip
, FWRITE
|FTRUNCATE
, 1);
* Common code for open and creat.
* Check permissions (if we haven't done so already),
* allocate an open file structure, and call
* the device open routine, if any.
open1(ip
, mode
, checkpermissions
)
register struct inode
*ip
;
register struct file
*fp
;
if ((ip
->i_mode
&IFMT
) == IFDIR
) {
* Check locking on inode. Release "inode lock"
* while doing so in case we block inside flocki.
if (mode
&(FSHLOCK
|FEXLOCK
)) {
flags
= flocki(ip
, 0, mode
);
if ((fp
= falloc()) == NULL
)
fp
->f_flag
= mode
& FMODES
;
u
.u_error
= openi(ip
, mode
);
register struct inode
*ip
;
uap
= (struct a
*)u
.u_ap
;
ip
= namei(uchar
, CREATE
, 0);
ip
= maknode(uap
->fmode
);
* 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
;
ip
= namei(uchar
, LOOKUP
, 1); /* well, this routine is doomed anyhow */
if ((ip
->i_mode
&IFMT
) == IFDIR
&& !suser()) {
iupdat(ip
, &time
, &time
, 1);
u
.u_dirp
= (caddr_t
)uap
->linkname
;
xp
= namei(uchar
, CREATE
, 0);
if (u
.u_pdir
->i_dev
!= ip
->i_dev
) {
u
.u_error
= direnter(ip
);
* symlink -- make a symbolic link
register struct inode
*ip
;
uap
= (struct a
*)u
.u_ap
;
u
.u_dirp
= uap
->linkname
;
ip
= namei(uchar
, CREATE
, 0);
ip
= maknode(IFLNK
| 0777);
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.
register struct inode
*ip
, *dp
;
ip
= namei(uchar
, DELETE
| LOCKPARENT
, 0);
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_SOCKET
) {
if (uap
->sbase
== FSEEK_RELATIVE
)
uap
->off
+= fp
->f_offset
;
else if (uap
->sbase
== FSEEK_EOF
)
uap
->off
+= fp
->f_inode
->i_size
;
register struct inode
*ip
;
uap
= (struct a
*)u
.u_ap
;
ip
= namei(uchar
, LOOKUP
, 1);
if ((uap
->fmode
&FACCESS_READ
) && access(ip
, IREAD
))
if ((uap
->fmode
&FACCESS_WRITE
) && access(ip
, IWRITE
))
if ((uap
->fmode
&FACCESS_EXECUTE
) && access(ip
, IEXEC
))
register struct file
*fp
;
uap
= (struct a
*)u
.u_ap
;
if (fp
->f_type
== DTYPE_SOCKET
)
u
.u_error
= sostat(fp
->f_socket
, uap
->sb
);
stat1(fp
->f_inode
, uap
->sb
);
* Stat system call. This version follows links.
register struct inode
*ip
;
uap
= (struct a
*)u
.u_ap
;
ip
= namei(uchar
, LOOKUP
, 1);
* Lstat system call. This version does not follow links.
register struct inode
*ip
;
uap
= (struct a
*)u
.u_ap
;
ip
= namei(uchar
, LOOKUP
, 0);
* The basic routine for fstat and stat:
* get the inode and pass appropriate parts back.
register struct inode
*ip
;
IUPDAT(ip
, &time
, &time
, 0);
ds
.st_ino
= ip
->i_number
;
ds
.st_nlink
= ip
->i_nlink
;
ds
.st_rdev
= (dev_t
)ip
->i_rdev
;
ds
.st_atime
= ip
->i_atime
;
ds
.st_mtime
= ip
->i_mtime
;
ds
.st_ctime
= ip
->i_ctime
;
/* this doesn't belong here */
if ((ip
->i_mode
&IFMT
) == IFBLK
)
ds
.st_blksize
= BLKDEV_IOSIZE
;
else if ((ip
->i_mode
&IFMT
) == IFCHR
)
ds
.st_blksize
= MAXBSIZE
;
ds
.st_blksize
= ip
->i_fs
->fs_bsize
;
ds
.st_spare4
[0] = ds
.st_spare4
[1] = ds
.st_spare4
[2] = 0;
u
.u_error
= copyout((caddr_t
)&ds
, (caddr_t
)ub
, sizeof(ds
));
* Return target name of a symbolic link
register struct inode
*ip
;
} *uap
= (struct a
*)u
.u_ap
;
ip
= namei(uchar
, LOOKUP
, 0);
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(1)) == NULL
)
* Change mode of a file given a file descriptor.
register struct inode
*ip
;
register struct file
*fp
;
uap
= (struct a
*)u
.u_ap
;
if (fp
->f_type
== DTYPE_SOCKET
) {
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 (u
.u_gid
== ip
->i_gid
)
for (gp
= u
.u_groups
; gp
< &u
.u_groups
[NGROUPS
]; gp
++)
if (u
.u_quota
->q_syflags
& QF_UMASK
&& u
.u_uid
!= 0 &&
(ip
->i_mode
& IFMT
) != IFCHR
)
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(0)) == NULL
)
chown1(ip
, uap
->uid
, uap
->gid
);
* Set ownership given a file descriptor.
register struct inode
*ip
;
register struct file
*fp
;
uap
= (struct a
*)u
.u_ap
;
if (fp
->f_type
== DTYPE_SOCKET
) {
chown1(ip
, uap
->uid
, uap
->gid
);
* Perform chown operation on inode ip;
* inode must be locked prior to call.
register struct inode
*ip
;
* This doesn't allow for holes in files (which hopefully don't
* happen often in files that we chown), and is not accurate anyway
* (eg: it totally ignores 3 level indir blk files - but hopefully
* noone who can make a file that big will have a quota)
register struct fs
*fs
= ip
->i_fs
;
if (ip
->i_size
> (change
= NDADDR
* fs
->fs_bsize
)) {
size
= blkroundup(fs
, ip
->i_size
) - change
;
/* this assumes NIADDR <= 2 */
if (size
> NINDIR(fs
) * fs
->fs_bsize
)
change
= fragroundup(fs
, ip
->i_size
);
(void)chkdq(ip
, -change
, 1);
(void)chkiq(ip
->i_dev
, ip
, ip
->i_uid
, 1);
* keep uid/gid's in sane range -- no err,
* so chown(file, uid, -1) will do something useful
if (uid
>= 0 && uid
<= 32767) /* should have a constant */
if (gid
>= 0 && gid
<= 32767) /* same here */
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);
* Set IUPD and IACC times on file.
register struct inode
*ip
;
uap
= (struct a
*)u
.u_ap
;
if ((ip
= owner(1)) == NULL
)
u
.u_error
= copyin((caddr_t
)uap
->tptr
, (caddr_t
)tv
, sizeof(tv
));
ip
->i_flag
|= IACC
|IUPD
|ICHG
;
tv0
.tv_sec
= tv
[0]; tv0
.tv_usec
= 0;
tv1
.tv_sec
= tv
[1]; tv1
.tv_usec
= 0;
iupdat(ip
, &tv0
, &tv1
, 0);
* Apply an advisory lock on a file descriptor.
register struct file
*fp
;
uap
= (struct a
*)u
.u_ap
;
if (fp
->f_type
== DTYPE_SOCKET
) { /* XXX */
flags
= u
.u_pofile
[uap
->fd
] & (UF_SHLOCK
|UF_EXLOCK
);
funlocki(fp
->f_inode
, flags
);
u
.u_pofile
[uap
->fd
] &= ~(UF_SHLOCK
|UF_EXLOCK
);
* No reason to write lock a file we've already
* write locked, similarly with a read lock.
if ((flags
&UF_EXLOCK
) && (cmd
&FEXLOCK
) ||
(flags
&UF_SHLOCK
) && (cmd
&FSHLOCK
))
u
.u_pofile
[uap
->fd
] = flocki(fp
->f_inode
, u
.u_pofile
[uap
->fd
], cmd
);
* Truncate a file given its path name.
} *uap
= (struct a
*)u
.u_ap
;
ip
= namei(uchar
, LOOKUP
, 1);
if ((ip
->i_mode
&IFMT
) == IFDIR
) {
* Truncate a file given a file descriptor.
} *uap
= (struct a
*)u
.u_ap
;
if (fp
->f_type
== DTYPE_SOCKET
) {
if ((fp
->f_flag
&FWRITE
) == 0) {
} *uap
= (struct a
*)u
.u_ap
;
if (fp
->f_type
== DTYPE_SOCKET
) {
* 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.
* 2) Link source to destination. If destination already exists,
* 3) Unlink source reference to inode if still around.
* 4) 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.
register struct inode
*ip
, *xp
, *dp
;
int oldparent
, parentdifferent
, doingdirectory
;
uap
= (struct a
*)u
.u_ap
;
ip
= namei(uchar
, DELETE
| LOCKPARENT
, 0);
oldparent
= 0, doingdirectory
= 0;
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) ||
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.
u
.u_dirp
= (caddr_t
)uap
->to
;
xp
= namei(uchar
, CREATE
| LOCKPARENT
, 0);
* If ".." must be changed (ie the directory gets a new
* parent) then the user must have write permission.
parentdifferent
= oldparent
!= dp
->i_number
;
if (doingdirectory
&& parentdifferent
&& access(ip
, IWRITE
))
* 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
) {
* Disallow rename(foo, foo/bar).
if (dp
->i_number
== ip
->i_number
) {
* Account for ".." in directory.
* When source and destination have the
* same parent we don't fool with the
* link count -- this isn't required
* because we do a similar check below.
if (doingdirectory
&& parentdifferent
) {
iupdat(dp
, &time
, &time
, 1);
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
) || 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 we've no way to determine if
* we've got a link or the directory itself, and
* if we get a link, then ".." will be screwed up.
panic("rename: linked directory");
dp
= namei(uchar
, DELETE
, 0);
* Insure directory entry still exists and
* has not changed since the start of all
* this. If either has occured, forget about
* about deleting the original entry and just
* adjust the link count in the inode.
if (dp
== NULL
|| u
.u_dent
.d_ino
!= ip
->i_number
) {
* If source is a directory, must adjust
* link count of parent directory also.
* If target didn't exist and source and
* target have the same parent, then we
* needn't touch the link count, it all
* balances out in the end. Otherwise, we
* must do so to reflect deletion of ".."
if (doingdirectory
&& (xp
!= NULL
|| parentdifferent
)) {
if (error
== 0) /* conservative */
* 4) Renaming a directory with the parent
* different requires ".." to be rewritten.
* The window is still there for ".." to
* be inconsistent, but this is unavoidable,
* and a lot shorter than when it was done
if (doingdirectory
&& parentdifferent
&& error
== 0) {
struct dirtemplate dirbuf
;
ip
= namei(uchar
, LOOKUP
| LOCKPARENT
, 0);
printf("rename: .. went away\n");
if ((ip
->i_mode
&IFMT
) != IFDIR
) {
printf("rename: .. not a directory\n");
error
= rdwri(UIO_READ
, ip
, (caddr_t
)&dirbuf
,
sizeof (struct dirtemplate
), (off_t
)0, 1, (int *)0);
dirbuf
.dotdot_ino
= dp
->i_number
;
(void) rdwri(UIO_WRITE
, ip
, (caddr_t
)&dirbuf
,
sizeof (struct dirtemplate
), (off_t
)0, 1, (int *)0);
register struct inode
*ip
;
if ((mode
& IFMT
) == IFDIR
)
ipref
= dirpref(u
.u_pdir
->i_fs
);
ipref
= u
.u_pdir
->i_number
;
ip
= ialloc(u
.u_pdir
, ipref
, mode
);
if (ip
->i_dquot
!= NODQUOT
)
ip
->i_flag
|= IACC
|IUPD
|ICHG
;
ip
->i_mode
= mode
& ~u
.u_cmask
;
ip
->i_gid
= u
.u_pdir
->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
);
* Write error occurred trying to update directory
* so must deallocate the inode.