/* ffs_vnops.c 4.62 83/08/06 */
#include "../h/socketvar.h"
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
;
ip
= namei(uchar
, LOOKUP
, 1);
if ((ip
->i_mode
&IFMT
) != IFDIR
) {
} *uap
= (struct a
*) u
.u_ap
;
copen(uap
->mode
-FOPEN
, uap
->crtmode
);
} *uap
= (struct a
*)u
.u_ap
;
copen(FWRITE
|FCREAT
|FTRUNC
, uap
->fmode
);
* 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
;
if ((mode
&(FREAD
|FWRITE
)) == 0) {
ip
= namei(uchar
, CREATE
, 1);
ip
= maknode(arg
&07777&(~ISVTX
));
ip
= namei(uchar
, LOOKUP
, 1);
if ((ip
->i_mode
& IFMT
) == IFSOCK
) {
if ((mode
&FCREAT
) == 0) {
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
;
ip
= namei(uchar
, CREATE
, 0);
ip
= maknode(uap
->fmode
);
switch (ip
->i_mode
& IFMT
) {
* 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
;
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
;
ip
= namei(uchar
, LOOKUP
, 1);
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
;
ip
= namei(uchar
, LOOKUP
, follow
);
(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
;
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
;
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(0)) == NULL
)
u
.u_error
= 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
;
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(1)) == 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
;
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_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.
* 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 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
parentdifferent
= oldparent
!= dp
->i_number
;
if (doingdirectory
&& parentdifferent
) {
u
.u_error
= checkpath(ip
, dp
);
u
.u_dirp
= (caddr_t
)uap
->to
;
xp
= namei(uchar
, CREATE
| LOCKPARENT
, 0);
} while (dp
!= u
.u_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 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
;
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
);
* 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, ".."
register struct inode
*ip
, *dp
;
struct dirtemplate dirtemplate
;
uap
= (struct a
*)u
.u_ap
;
ip
= namei(uchar
, CREATE
, 0);
* 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
);
dp
= namei(uchar
, LOOKUP
, 0);
* No need to do an explicit itrunc here,
* irele will do this for us because we set
register struct inode
*ip
, *dp
;
ip
= namei(uchar
, DELETE
| LOCKPARENT
, 0);
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
)) {
* 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.
register struct file
*fp
;
if (fp
->f_type
!= DTYPE_INODE
) {
* 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;