minix/servers/vfs/link.c

377 lines
9.2 KiB
C
Raw Normal View History

/* This file handles the LINK and UNLINK system calls. It also deals with
* deallocating the storage used by a file when the last UNLINK is done to a
* file and the blocks must be returned to the free block pool.
*
* The entry points into this file are
* do_link: perform the LINK system call
* do_unlink: perform the UNLINK and RMDIR system calls
* do_rename: perform the RENAME system call
* do_truncate: perform the TRUNCATE system call
* do_ftruncate: perform the FTRUNCATE system call
* do_rdlink: perform the RDLNK system call
*
* Changes for VFS:
* Jul 2006 (Balazs Gerofi)
*/
#include "fs.h"
#include <sys/stat.h>
#include <string.h>
#include <minix/com.h>
#include <minix/callnr.h>
#include <dirent.h>
#include "file.h"
#include "fproc.h"
#include "param.h"
#include <minix/vfsif.h>
#include "vnode.h"
/*===========================================================================*
* do_link *
*===========================================================================*/
PUBLIC int do_link()
{
/* Perform the link(name1, name2) system call. */
int r;
2007-08-07 14:52:47 +02:00
endpoint_t linked_fs_e, link_lastdir_fs_e;
struct vnode *vp_o, *vp_d;
if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK)
return(err_code);
/* Request lookup */
2007-08-07 14:52:47 +02:00
if ((r = lookup_vp(0 /*flags*/, 0 /*!use_realuid*/, &vp_o)) != OK) return r;
2007-08-07 14:52:47 +02:00
linked_fs_e = vp_o->v_fs_e;
/* Does the final directory of 'name2' exist? */
if (fetch_name(m_in.name2, m_in.name2_length, M1) != OK) {
2007-08-07 14:52:47 +02:00
put_vnode(vp_o);
return(err_code);
}
/* Request lookup */
2007-08-07 14:52:47 +02:00
if ((r = lookup_lastdir(0 /*!use_realuid*/, &vp_d)) != OK)
{
put_vnode(vp_o);
return r;
}
link_lastdir_fs_e = vp_d->v_fs_e;
/* Check for links across devices. */
if (linked_fs_e != link_lastdir_fs_e)
2007-08-07 14:52:47 +02:00
{
put_vnode(vp_o);
put_vnode(vp_d);
return EXDEV;
2007-08-07 14:52:47 +02:00
}
2007-08-08 17:26:47 +02:00
/* Make sure that the object is a directory */
if ((vp_d->v_mode & I_TYPE) != I_DIRECTORY)
{
put_vnode(vp_o);
put_vnode(vp_d);
return ENOTDIR;
}
2007-08-07 14:52:47 +02:00
r= forbidden(vp_d, W_BIT|X_BIT, 0 /*!use_realuid*/);
if (r != OK)
{
put_vnode(vp_o);
put_vnode(vp_d);
return r;
}
/* Issue request */
2007-08-07 14:52:47 +02:00
r= req_link(linked_fs_e, vp_d->v_inode_nr, user_fullpath, vp_o->v_inode_nr);
put_vnode(vp_o);
put_vnode(vp_d);
return r;
}
/*===========================================================================*
* do_unlink *
*===========================================================================*/
PUBLIC int do_unlink()
{
/* Perform the unlink(name) or rmdir(name) system call. The code for these two
* is almost the same. They differ only in some condition testing. Unlink()
* may be used by the superuser to do dangerous things; rmdir() may not.
*/
register struct fproc *rfp;
struct vnode *vp;
int r;
if (fetch_name(m_in.name, m_in.name_length, M3) != OK) return(err_code);
2007-08-07 14:52:47 +02:00
r= lookup_lastdir(0 /*!use_realuid*/, &vp);
if (r != OK)
return r;
/* Make sure that the object is a directory */
if ((vp->v_mode & I_TYPE) != I_DIRECTORY)
{
put_vnode(vp);
return ENOTDIR;
}
2007-08-07 14:52:47 +02:00
/* The caller must have both search and execute permission */
r= forbidden(vp, X_BIT|W_BIT, 0 /*!use_realuid*/);
if (r != OK)
{
put_vnode(vp);
return r;
}
/* If a directory file has to be removed the following conditions have to met:
* - The directory must not be the root of a mounted file system
* - The directory must not be anybody's root/working directory
*/
/* Issue request */
2007-08-07 14:52:47 +02:00
r= ((call_nr == UNLINK) ? req_unlink : req_rmdir)(vp->v_fs_e,
vp->v_inode_nr, user_fullpath);
put_vnode(vp);
return r;
}
/*===========================================================================*
* do_rename *
*===========================================================================*/
PUBLIC int do_rename()
{
/* Perform the rename(name1, name2) system call. */
2007-08-07 14:52:47 +02:00
int r;
int old_dir_inode;
int old_fs_e;
int new_dir_inode;
int new_fs_e;
2007-08-07 14:52:47 +02:00
size_t len;
struct vnode *vp_od, *vp_nd;
char old_name[PATH_MAX+1];
/* See if 'name1' (existing file) exists. Get dir and file inodes. */
if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) return(err_code);
/* Request lookup */
2007-08-07 14:52:47 +02:00
if ((r = lookup_lastdir(0 /*!use_realuid*/, &vp_od)) != OK) return r;
r= forbidden(vp_od, W_BIT|X_BIT, 0 /*!use_realuid*/);
if (r != OK)
{
put_vnode(vp_od);
return r;
}
2007-08-07 14:52:47 +02:00
/* Remeber FS endpoint */
old_fs_e = vp_od->v_fs_e;
/* Save the last component of the old name */
len= strlen(user_fullpath);
if (len >= sizeof(old_name))
{
put_vnode(vp_od);
return ENAMETOOLONG;
}
memcpy(old_name, user_fullpath, len+1);
/* See if 'name2' (new name) exists. Get dir inode */
2007-08-07 14:52:47 +02:00
if (fetch_name(m_in.name2, m_in.name2_length, M1) != OK)
{
put_vnode(vp_od);
return err_code;
}
/* Request lookup */
2007-08-07 14:52:47 +02:00
r = lookup_lastdir(0 /*!use_realuid*/, &vp_nd);
if (r != OK)
{
put_vnode(vp_od);
return r;
}
r= forbidden(vp_nd, W_BIT|X_BIT, 0 /*!use_realuid*/);
if (r != OK)
{
put_vnode(vp_od);
put_vnode(vp_nd);
return r;
}
2007-08-07 14:52:47 +02:00
/* Remeber FS endpoint */
new_fs_e = vp_nd->v_fs_e;
/* Both parent directories must be on the same device. */
2007-08-07 14:52:47 +02:00
if (old_fs_e != new_fs_e)
{
put_vnode(vp_od);
put_vnode(vp_nd);
return EXDEV;
}
/* Issue request */
2007-08-07 14:52:47 +02:00
r= req_rename(old_fs_e, vp_od->v_inode_nr, old_name, vp_nd->v_inode_nr,
user_fullpath);
put_vnode(vp_od);
put_vnode(vp_nd);
return r;
}
/*===========================================================================*
* do_truncate *
*===========================================================================*/
PUBLIC int do_truncate()
{
/* truncate_inode() does the actual work of do_truncate() and do_ftruncate().
* do_truncate() and do_ftruncate() have to get hold of the inode, either
* by name or fd, do checks on it, and call truncate_inode() to do the
* work.
*/
struct vnode *vp;
int r;
2007-01-05 17:36:55 +01:00
printf("in do_truncate\n");
if (fetch_name(m_in.m2_p1, m_in.m2_i1, M1) != OK) return err_code;
/* Request lookup */
2007-08-07 14:52:47 +02:00
if ((r = lookup_vp(0 /*flags*/, 0 /*!use_realuid*/, &vp)) != OK) return r;
Various VFS and MFS fixes to improve correctness, consistency and POSIX compliance. VFS changes: * truncate() on a file system mounted read-only no longer panics MFS. * ftruncate() and fcntl(F_FREESP) now check for write permission on the file descriptor instead of the file, write(). * utime(), chown() and fchown() now check for file system read-only status. MFS changes: * link() and rename() no longer return the internal EENTERMOUNT and ELEAVEMOUNT errors to the application as part of a check on the source path. * rename() now treats EENTERMOUNT from the destination path check as an error, preventing file system corruption from renaming a normal directory to an existing mountpoint directory. * mountpoints (mounted-on dirs) are hidden better during lookups: - if a lookup starts from a mountpoint, the first component has to be ".." (anything else being a VFS-FS protocol violation). - in that case, the permissions of the mountpoint are not checked. - in all other cases, visiting a mountpoint always results in EENTERMOUNT. * a lookup on ".." from a mount root or chroot(2) root no longer succeeds if the caller does not have search permission on that directory. * POSIX: getdents() now updates directory access times. * POSIX: readlink() now returns partial results instead of ERANGE. Miscellaneous changes: * semaphore file handling bug (leading to hangs) fixed in test 32. The VFS changes should now put the burden of checking for read-only status of file systems entirely on VFS, and limit the access permission checks that file systems have to perform, to checking search permission on directories during lookups. From this point on, any deviation from that spceification should be considered a bug. Note that for legacy reasons, the root partition is assumed to be mounted read-write.
2009-05-18 13:27:12 +02:00
if ((r = forbidden(vp, W_BIT, 0 /*!use_realuid*/)) == OK)
r = truncate_vn(vp, m_in.m2_l1);
2007-08-07 14:52:47 +02:00
put_vnode(vp);
return r;
}
/*===========================================================================*
* do_ftruncate *
*===========================================================================*/
PUBLIC int do_ftruncate()
{
/* As with do_truncate(), truncate_inode() does the actual work. */
int r;
struct filp *rfilp;
if ( (rfilp = get_filp(m_in.m2_i1)) == NIL_FILP)
return err_code;
Various VFS and MFS fixes to improve correctness, consistency and POSIX compliance. VFS changes: * truncate() on a file system mounted read-only no longer panics MFS. * ftruncate() and fcntl(F_FREESP) now check for write permission on the file descriptor instead of the file, write(). * utime(), chown() and fchown() now check for file system read-only status. MFS changes: * link() and rename() no longer return the internal EENTERMOUNT and ELEAVEMOUNT errors to the application as part of a check on the source path. * rename() now treats EENTERMOUNT from the destination path check as an error, preventing file system corruption from renaming a normal directory to an existing mountpoint directory. * mountpoints (mounted-on dirs) are hidden better during lookups: - if a lookup starts from a mountpoint, the first component has to be ".." (anything else being a VFS-FS protocol violation). - in that case, the permissions of the mountpoint are not checked. - in all other cases, visiting a mountpoint always results in EENTERMOUNT. * a lookup on ".." from a mount root or chroot(2) root no longer succeeds if the caller does not have search permission on that directory. * POSIX: getdents() now updates directory access times. * POSIX: readlink() now returns partial results instead of ERANGE. Miscellaneous changes: * semaphore file handling bug (leading to hangs) fixed in test 32. The VFS changes should now put the burden of checking for read-only status of file systems entirely on VFS, and limit the access permission checks that file systems have to perform, to checking search permission on directories during lookups. From this point on, any deviation from that spceification should be considered a bug. Note that for legacy reasons, the root partition is assumed to be mounted read-write.
2009-05-18 13:27:12 +02:00
if (!(rfilp->filp_mode & W_BIT))
return EBADF;
2007-01-05 17:36:55 +01:00
return truncate_vn(rfilp->filp_vno, m_in.m2_l1);
}
/*===========================================================================*
* truncate_vn *
*===========================================================================*/
PUBLIC int truncate_vn(vp, newsize)
struct vnode *vp;
off_t newsize;
{
int r;
if ( (vp->v_mode & I_TYPE) != I_REGULAR &&
(vp->v_mode & I_TYPE) != I_NAMED_PIPE) {
return EINVAL;
2007-01-05 17:36:55 +01:00
}
/* Issue request */
2007-08-07 14:52:47 +02:00
if ((r = req_ftrunc(vp->v_fs_e, vp->v_inode_nr, newsize, 0)) != OK) return r;
2007-01-05 17:36:55 +01:00
vp->v_size = newsize;
return OK;
}
/*===========================================================================*
* do_slink *
*===========================================================================*/
PUBLIC int do_slink()
{
/* Perform the symlink(name1, name2) system call. */
int r;
2007-08-07 14:52:47 +02:00
struct vnode *vp;
char string[NAME_MAX]; /* last component of the new dir's path name */
if (fetch_name(m_in.name2, m_in.name2_length, M1) != OK)
return(err_code);
if (m_in.name1_length <= 1 || m_in.name1_length >= _MIN_BLOCK_SIZE)
return(ENAMETOOLONG);
2007-08-07 14:52:47 +02:00
/* Request lookup */
2007-08-07 14:52:47 +02:00
if ((r = lookup_lastdir(0 /*!use_realuid*/, &vp)) != OK)
{
printf("vfs:do_slink: lookup_lastdir failed with %d\n", r);
return r;
}
2008-12-11 15:46:46 +01:00
#if 0
2007-08-07 14:52:47 +02:00
printf("vfs:do_slink: got dir inode %d on dev 0x%x, fs %d\n",
vp->v_inode_nr, vp->v_dev, vp->v_fs_e);
2008-12-11 15:46:46 +01:00
#endif
2007-08-07 14:52:47 +02:00
r= forbidden(vp, W_BIT|X_BIT, 0 /*!use_realuid*/);
if (r != OK)
{
put_vnode(vp);
return r;
}
/* Issue request */
2007-08-07 14:52:47 +02:00
r= req_slink(vp->v_fs_e, vp->v_inode_nr, user_fullpath, who_e, m_in.name1,
m_in.name1_length - 1, fp->fp_effuid, fp->fp_effgid);
put_vnode(vp);
return r;
}
/*===========================================================================*
* do_rdlink *
*===========================================================================*/
PUBLIC int do_rdlink()
{
/* Perform the readlink(name, buf) system call. */
2007-08-07 14:52:47 +02:00
int r, copylen;
struct vnode *vp;
copylen = m_in.m1_i2;
if(copylen < 0) return EINVAL;
if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) return(err_code);
/* Request lookup */
2007-08-07 14:52:47 +02:00
r = lookup_vp(PATH_RET_SYMLINK, 0 /*!use_realuid*/, &vp);
if (r != OK) return r;
/* Make sure this is a symbolic link */
if ((vp->v_mode & I_TYPE) != I_SYMBOLIC_LINK) {
put_vnode(vp);
return EINVAL;
}
/* Issue request */
2007-08-07 14:52:47 +02:00
r= req_rdlink(vp->v_fs_e, vp->v_inode_nr, who_e, (vir_bytes)m_in.name2,
copylen);
put_vnode(vp);
return r;
}