426 lines
13 KiB
C
426 lines
13 KiB
C
|
|
|
|
#include "fs.h"
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <minix/callnr.h>
|
|
#include <minix/com.h>
|
|
#include "buf.h"
|
|
#include "inode.h"
|
|
#include "super.h"
|
|
|
|
#include <minix/vfsif.h>
|
|
|
|
PRIVATE char mode_map[] = {R_BIT, W_BIT, R_BIT|W_BIT, 0};
|
|
FORWARD _PROTOTYPE( struct inode *new_node, (struct inode *ldirp,
|
|
char *string, mode_t bits, zone_t z0, int opaque, char *parsed));
|
|
|
|
|
|
/*===========================================================================*
|
|
* fs_open *
|
|
*===========================================================================*/
|
|
PUBLIC int fs_open()
|
|
{
|
|
int r, b, exist = TRUE;
|
|
struct inode *ldirp;
|
|
struct inode *rip;
|
|
int oflags;
|
|
mode_t omode;
|
|
mode_t bits;
|
|
char lastc[NAME_MAX];
|
|
|
|
/* Read request message */
|
|
oflags = fs_m_in.REQ_FLAGS;
|
|
omode = fs_m_in.REQ_MODE;
|
|
|
|
|
|
caller_uid = fs_m_in.REQ_UID;
|
|
caller_gid = fs_m_in.REQ_GID;
|
|
|
|
/* Device number only for device special files */
|
|
fs_m_out.RES_DEV = NO_DEV;
|
|
|
|
/* Remap the bottom two bits of oflags. */
|
|
bits = (mode_t) mode_map[oflags & O_ACCMODE];
|
|
|
|
|
|
/* If O_CREATE is set, try to make the file. */
|
|
if (oflags & O_CREAT) {
|
|
/* Copy the last component */
|
|
err_code = sys_datacopy(FS_PROC_NR, (vir_bytes) fs_m_in.REQ_PATH,
|
|
SELF, (vir_bytes) lastc, (phys_bytes) fs_m_in.REQ_PATH_LEN);
|
|
|
|
if (err_code != OK) return err_code;
|
|
|
|
/* Get last directory inode */
|
|
if ((ldirp = get_inode(fs_dev, fs_m_in.REQ_INODE_NR)) == NIL_INODE) {
|
|
printf("MFS(%d) get_inode for parent dir by creat() failed\n", SELF_E);
|
|
return ENOENT;
|
|
}
|
|
|
|
/* Create a new inode by calling new_node(). */
|
|
rip = new_node(ldirp, lastc, omode, NO_ZONE, oflags&O_EXCL, NULL);
|
|
r = err_code;
|
|
if (r == OK) exist = FALSE; /* we just created the file */
|
|
else if (r != EEXIST) {
|
|
put_inode(ldirp);
|
|
return(r); /* other error */
|
|
}
|
|
else exist = !(oflags & O_EXCL); /* file exists, if the O_EXCL
|
|
flag is set this is an error */
|
|
}
|
|
else {
|
|
/* Get file inode. */
|
|
if ( (rip = get_inode(fs_dev, fs_m_in.REQ_INODE_NR)) == NIL_INODE) {
|
|
printf("MFS(%d) get_inode by open() failed\n", SELF_E);
|
|
return ENOENT;
|
|
}
|
|
ldirp = NIL_INODE;
|
|
}
|
|
|
|
/* Only do the normal open code if we didn't just create the file. */
|
|
if (exist) {
|
|
/* Check protections. */
|
|
if ((r = forbidden(rip, bits)) == OK) {
|
|
/* Opening reg. files directories and special files differ. */
|
|
switch (rip->i_mode & I_TYPE) {
|
|
case I_REGULAR:
|
|
/* Truncate regular file if O_TRUNC. */
|
|
if (oflags & O_TRUNC) {
|
|
if ((r = forbidden(rip, W_BIT)) !=OK) break;
|
|
truncate_inode(rip, 0);
|
|
wipe_inode(rip);
|
|
/* Send the inode from the inode cache to the
|
|
* block cache, so it gets written on the next
|
|
* cache flush.
|
|
*/
|
|
rw_inode(rip, WRITING);
|
|
}
|
|
break;
|
|
|
|
case I_DIRECTORY:
|
|
/* Directories may be read but not written. */
|
|
r = (bits & W_BIT ? EISDIR : OK);
|
|
break;
|
|
|
|
case I_CHAR_SPECIAL:
|
|
case I_BLOCK_SPECIAL:
|
|
/* Send back the device number */
|
|
fs_m_out.RES_DEV = (Dev_t) rip->i_zone[0];
|
|
break;
|
|
|
|
case I_NAMED_PIPE:
|
|
rip->i_pipe = I_PIPE;
|
|
b = (bits & R_BIT ? R_BIT : W_BIT);
|
|
if (b == R_BIT)
|
|
fs_m_out.RES_POS = rip->i_zone[V2_NR_DZONES+0];
|
|
else
|
|
fs_m_out.RES_POS = rip->i_zone[V2_NR_DZONES+1];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If error, release inode. */
|
|
if (r != OK) {
|
|
put_inode(ldirp);
|
|
put_inode(rip);
|
|
return(r);
|
|
}
|
|
|
|
/* Reply message */
|
|
fs_m_out.m_source = rip->i_dev; /* filled with FS endpoint by the system */
|
|
fs_m_out.RES_INODE_NR = rip->i_num;
|
|
fs_m_out.RES_MODE = rip->i_mode;
|
|
fs_m_out.RES_FILE_SIZE = rip->i_size;
|
|
fs_m_out.RES_INODE_INDEX = (rip - &inode[0]) / sizeof(struct inode);
|
|
|
|
/* This values are needed for the execution */
|
|
fs_m_out.RES_UID = rip->i_uid;
|
|
fs_m_out.RES_GID = rip->i_gid;
|
|
if ((rip->i_mode & I_TYPE) == I_REGULAR) fs_m_out.RES_CTIME = rip->i_ctime;
|
|
|
|
/* Drop parent dir */
|
|
put_inode(ldirp);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* fs_mknod *
|
|
*===========================================================================*/
|
|
PUBLIC int fs_mknod()
|
|
{
|
|
struct inode *ip, *ldirp;
|
|
char lastc[NAME_MAX];
|
|
|
|
/* Copy the last component and set up caller's user and group id */
|
|
err_code = sys_datacopy(FS_PROC_NR, (vir_bytes) fs_m_in.REQ_PATH, SELF,
|
|
(vir_bytes) lastc, (phys_bytes) fs_m_in.REQ_PATH_LEN);
|
|
|
|
if (err_code != OK) return err_code;
|
|
|
|
caller_uid = fs_m_in.REQ_UID;
|
|
caller_gid = fs_m_in.REQ_GID;
|
|
|
|
/* Get last directory inode */
|
|
if ((ldirp = get_inode(fs_dev, fs_m_in.REQ_INODE_NR)) == NIL_INODE) {
|
|
printf("MFS(%d) get_inode for parent dir by mknod() failed\n", SELF_E);
|
|
return ENOENT;
|
|
}
|
|
|
|
/* Try to create the new node */
|
|
ip = new_node(ldirp, lastc, fs_m_in.REQ_MODE, (zone_t) fs_m_in.REQ_DEV,
|
|
TRUE, NULL);
|
|
|
|
put_inode(ip);
|
|
put_inode(ldirp);
|
|
return(err_code);
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* fs_mkdir *
|
|
*===========================================================================*/
|
|
PUBLIC int fs_mkdir()
|
|
{
|
|
int r1, r2; /* status codes */
|
|
ino_t dot, dotdot; /* inode numbers for . and .. */
|
|
struct inode *rip, *ldirp;
|
|
char lastc[NAME_MAX]; /* last component */
|
|
|
|
/* Copy the last component and set up caller's user and group id */
|
|
err_code = sys_datacopy(FS_PROC_NR, (vir_bytes) fs_m_in.REQ_PATH, SELF,
|
|
(vir_bytes) lastc, (phys_bytes)
|
|
MIN(fs_m_in.REQ_PATH_LEN, NAME_MAX));
|
|
|
|
if (err_code != OK) return err_code;
|
|
|
|
caller_uid = fs_m_in.REQ_UID;
|
|
caller_gid = fs_m_in.REQ_GID;
|
|
|
|
/* Get last directory inode */
|
|
if ((ldirp = get_inode(fs_dev, fs_m_in.REQ_INODE_NR)) == NIL_INODE) {
|
|
printf("MFS(%d) get_inode for parent dir by mkdir() failed\n", SELF_E);
|
|
return ENOENT;
|
|
}
|
|
|
|
/* Next make the inode. If that fails, return error code. */
|
|
rip = new_node(ldirp, lastc, fs_m_in.REQ_MODE, (zone_t) 0, TRUE, NULL);
|
|
|
|
if (rip == NIL_INODE || err_code == EEXIST) {
|
|
put_inode(rip); /* can't make dir: it already exists */
|
|
put_inode(ldirp);
|
|
return(err_code);
|
|
}
|
|
|
|
/* Get the inode numbers for . and .. to enter in the directory. */
|
|
dotdot = ldirp->i_num; /* parent's inode number */
|
|
dot = rip->i_num; /* inode number of the new dir itself */
|
|
|
|
/* Now make dir entries for . and .. unless the disk is completely full. */
|
|
/* Use dot1 and dot2, so the mode of the directory isn't important. */
|
|
rip->i_mode = fs_m_in.REQ_MODE; /* set mode */
|
|
r1 = search_dir(rip, dot1, &dot, ENTER); /* enter . in the new dir */
|
|
r2 = search_dir(rip, dot2, &dotdot, ENTER); /* enter .. in the new dir */
|
|
|
|
/* If both . and .. were successfully entered, increment the link counts. */
|
|
if (r1 == OK && r2 == OK) {
|
|
/* Normal case. It was possible to enter . and .. in the new dir. */
|
|
rip->i_nlinks++; /* this accounts for . */
|
|
ldirp->i_nlinks++; /* this accounts for .. */
|
|
ldirp->i_dirt = DIRTY; /* mark parent's inode as dirty */
|
|
} else {
|
|
/* It was not possible to enter . or .. probably disk was full -
|
|
* links counts haven't been touched.
|
|
*/
|
|
if(search_dir(ldirp, lastc, (ino_t *) 0, DELETE) != OK)
|
|
panic(__FILE__, "Dir disappeared ", rip->i_num);
|
|
rip->i_nlinks--; /* undo the increment done in new_node() */
|
|
}
|
|
rip->i_dirt = DIRTY; /* either way, i_nlinks has changed */
|
|
|
|
put_inode(ldirp); /* return the inode of the parent dir */
|
|
put_inode(rip); /* return the inode of the newly made dir */
|
|
return(err_code); /* new_node() always sets 'err_code' */
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* fs_slink *
|
|
*===========================================================================*/
|
|
PUBLIC int fs_slink()
|
|
{
|
|
struct inode *sip; /* inode containing symbolic link */
|
|
struct inode *ldirp; /* directory containing link */
|
|
register int r; /* error code */
|
|
char string[NAME_MAX]; /* last component of the new dir's path name */
|
|
struct buf *bp; /* disk buffer for link */
|
|
|
|
caller_uid = fs_m_in.REQ_UID;
|
|
caller_gid = fs_m_in.REQ_GID;
|
|
|
|
/* Temporarily open the dir. */
|
|
if ( (ldirp = get_inode(fs_dev, fs_m_in.REQ_INODE_NR)) == NIL_INODE) {
|
|
return(EINVAL);
|
|
}
|
|
|
|
/* Copy the link name's last component */
|
|
r = sys_datacopy(FS_PROC_NR, (vir_bytes) fs_m_in.REQ_PATH,
|
|
SELF, (vir_bytes) string,
|
|
(phys_bytes) fs_m_in.REQ_PATH_LEN);
|
|
|
|
if (r != OK) return r;
|
|
|
|
/* Create the inode for the symlink. */
|
|
sip = new_node(ldirp, string, (mode_t) (I_SYMBOLIC_LINK | RWX_MODES),
|
|
(zone_t) 0, TRUE, NULL);
|
|
|
|
/* Allocate a disk block for the contents of the symlink.
|
|
* Copy contents of symlink (the name pointed to) into first disk block.
|
|
*/
|
|
if ((r = err_code) == OK) {
|
|
r = (bp = new_block(sip, (off_t) 0)) == NIL_BUF ? err_code :
|
|
sys_vircopy(fs_m_in.REQ_WHO_E, D, (vir_bytes) fs_m_in.REQ_USER_ADDR,
|
|
SELF, D, (vir_bytes) bp->b_data,
|
|
(vir_bytes) fs_m_in.REQ_SLENGTH);
|
|
|
|
if(r == OK) {
|
|
bp->b_data[_MIN_BLOCK_SIZE-1] = '\0';
|
|
sip->i_size = strlen(bp->b_data);
|
|
/*if(sip->i_size != m_in.name1_length-1) {*/
|
|
if(sip->i_size != fs_m_in.REQ_SLENGTH) {
|
|
/* This can happen if the user provides a buffer
|
|
* with a \0 in it. This can cause a lot of trouble
|
|
* when the symlink is used later. We could just use
|
|
* the strlen() value, but we want to let the user
|
|
* know he did something wrong. ENAMETOOLONG doesn't
|
|
* exactly describe the error, but there is no
|
|
* ENAMETOOWRONG.
|
|
*/
|
|
r = ENAMETOOLONG;
|
|
}
|
|
}
|
|
|
|
put_block(bp, DIRECTORY_BLOCK); /* put_block() accepts NIL_BUF. */
|
|
|
|
if (r != OK) {
|
|
sip->i_nlinks = 0;
|
|
if (search_dir(ldirp, string, (ino_t *) 0, DELETE) != OK)
|
|
panic(__FILE__, "Symbolic link vanished", NO_NUM);
|
|
}
|
|
}
|
|
|
|
/* put_inode() accepts NIL_INODE as a noop, so the below are safe. */
|
|
put_inode(sip);
|
|
put_inode(ldirp);
|
|
|
|
return(r);
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
* new_node *
|
|
*===========================================================================*/
|
|
PRIVATE struct inode *new_node(struct inode *ldirp,
|
|
char *string, mode_t bits, zone_t z0, int opaque, char *parsed)
|
|
{
|
|
/* New_node() is called by fs_open(), fs_mknod(), and fs_mkdir().
|
|
* In all cases it allocates a new inode, makes a directory entry for it in
|
|
* the ldirp directory with string name, and initializes it.
|
|
* It returns a pointer to the inode if it can do this;
|
|
* otherwise it returns NIL_INODE. It always sets 'err_code'
|
|
* to an appropriate value (OK or an error code).
|
|
*
|
|
* The parsed path rest is returned in 'parsed' if parsed is nonzero. It
|
|
* has to hold at least NAME_MAX bytes.
|
|
*/
|
|
|
|
register struct inode *rip;
|
|
register int r;
|
|
|
|
/*
|
|
*ldirp = parse_path(path, string, opaque ? LAST_DIR : LAST_DIR_EATSYM);
|
|
if (*ldirp == NIL_INODE) return(NIL_INODE);
|
|
*/
|
|
|
|
/* Get final component of the path. */
|
|
rip = advance(&ldirp, string);
|
|
|
|
if (S_ISDIR(bits) &&
|
|
(ldirp)->i_nlinks >= ((ldirp)->i_sp->s_version == V1 ?
|
|
CHAR_MAX : SHRT_MAX)) {
|
|
/* New entry is a directory, alas we can't give it a ".." */
|
|
put_inode(rip);
|
|
err_code = EMLINK;
|
|
return(NIL_INODE);
|
|
}
|
|
|
|
if ( rip == NIL_INODE && err_code == ENOENT) {
|
|
/* Last path component does not exist. Make new directory entry. */
|
|
if ( (rip = alloc_inode((ldirp)->i_dev, bits)) == NIL_INODE) {
|
|
/* Can't creat new inode: out of inodes. */
|
|
return(NIL_INODE);
|
|
}
|
|
|
|
/* Force inode to the disk before making directory entry to make
|
|
* the system more robust in the face of a crash: an inode with
|
|
* no directory entry is much better than the opposite.
|
|
*/
|
|
rip->i_nlinks++;
|
|
rip->i_zone[0] = z0; /* major/minor device numbers */
|
|
rw_inode(rip, WRITING); /* force inode to disk now */
|
|
|
|
/* New inode acquired. Try to make directory entry. */
|
|
if ((r = search_dir(ldirp, string, &rip->i_num, ENTER)) != OK) {
|
|
rip->i_nlinks--; /* pity, have to free disk inode */
|
|
rip->i_dirt = DIRTY; /* dirty inodes are written out */
|
|
put_inode(rip); /* this call frees the inode */
|
|
err_code = r;
|
|
return(NIL_INODE);
|
|
}
|
|
|
|
} else {
|
|
/* Either last component exists, or there is some problem. */
|
|
if (rip != NIL_INODE || err_code == EENTERMOUNT ||
|
|
err_code == ELEAVEMOUNT)
|
|
r = EEXIST;
|
|
else
|
|
r = err_code;
|
|
}
|
|
|
|
if(parsed) { /* Give the caller the parsed string if requested. */
|
|
strncpy(parsed, string, NAME_MAX-1);
|
|
parsed[NAME_MAX-1] = '\0';
|
|
}
|
|
|
|
/* The caller has to return the directory inode (*ldirp). */
|
|
err_code = r;
|
|
return(rip);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
* fs_inhibread *
|
|
*===========================================================================*/
|
|
PUBLIC int fs_inhibread()
|
|
{
|
|
struct inode *rip;
|
|
|
|
if ((rip = find_inode(fs_dev, fs_m_in.REQ_INODE_NR))== NIL_INODE){
|
|
printf("FSinhibread: couldn't find inode %d\n", fs_m_in.REQ_INODE_NR);
|
|
return EINVAL;
|
|
}
|
|
|
|
/* inhibit read ahead */
|
|
rip->i_seek = ISEEK;
|
|
|
|
return OK;
|
|
}
|
|
|