minix/servers/iso9660fs/path.c
2010-06-08 13:58:01 +00:00

373 lines
9.8 KiB
C

#include "inc.h"
#include <string.h>
#include <minix/com.h>
#include <minix/vfsif.h>
#include "buf.h"
FORWARD _PROTOTYPE( char *get_name, (char *name, char string[NAME_MAX+1]) );
FORWARD _PROTOTYPE( int parse_path, (ino_t dir_ino, ino_t root_ino, int flags,
struct dir_record **res_inop,
size_t *offsetp) );
/*===========================================================================*
* fs_lookup *
*===========================================================================*/
PUBLIC int fs_lookup() {
cp_grant_id_t grant;
int r, len, flags;
size_t offset;
ino_t dir_ino, root_ino;
struct dir_record *dir;
grant = fs_m_in.REQ_GRANT;
len = fs_m_in.REQ_PATH_LEN; /* including terminating nul */
dir_ino = fs_m_in.REQ_DIR_INO;
root_ino = fs_m_in.REQ_ROOT_INO;
flags = fs_m_in.REQ_FLAGS;
caller_uid = fs_m_in.REQ_UID;
caller_gid = fs_m_in.REQ_GID;
/* Check length. */
if(len > sizeof(user_path)) return(E2BIG); /* too big for buffer */
if(len < 1) return(EINVAL); /* too small */
/* Copy the pathname and set up caller's user and group id */
r = sys_safecopyfrom(VFS_PROC_NR, grant, 0, (vir_bytes) user_path,
(phys_bytes) len, D);
if (r != OK) {
printf("ISOFS %s:%d sys_safecopyfrom failed: %d\n",
__FILE__, __LINE__, r);
return(r);
}
/* Verify this is a null-terminated path. */
if(user_path[len-1] != '\0') return(EINVAL);
/* Lookup inode */
dir = NULL;
offset = 0;
r = parse_path(dir_ino, root_ino, flags, &dir, &offset);
if (r == ELEAVEMOUNT) {
/* Report offset and the error */
fs_m_out.RES_OFFSET = offset;
fs_m_out.RES_SYMLOOP = 0;
return(r);
}
if (r != OK && r != EENTERMOUNT) return(r);
fs_m_out.RES_INODE_NR = ID_DIR_RECORD(dir);
fs_m_out.RES_MODE = dir->d_mode;
fs_m_out.RES_FILE_SIZE_LO = dir->d_file_size;
fs_m_out.RES_SYMLOOP = 0;
fs_m_out.RES_UID = SYS_UID; /* root */
fs_m_out.RES_GID = SYS_GID; /* operator */
if (r == EENTERMOUNT) {
fs_m_out.RES_OFFSET = offset;
release_dir_record(dir);
}
return(r);
}
/* The search dir actually performs the operation of searching for the
* compoent ``string" in ldir_ptr. It returns the response and the number of
* the inode in numb. */
/*===========================================================================*
* search_dir *
*===========================================================================*/
PUBLIC int search_dir(ldir_ptr,string,numb)
register struct dir_record *ldir_ptr; /* dir record parent */
char string[NAME_MAX]; /* component to search for */
ino_t *numb; /* pointer to new dir record */
{
struct dir_record *dir_tmp;
register struct buf *bp;
int pos;
char* comma_pos = NULL;
char tmp_string[NAME_MAX];
/* This function search a particular element (in string) in a inode and
* return its number */
/* Initialize the tmp array */
memset(tmp_string,'\0',NAME_MAX);
if ((ldir_ptr->d_mode & I_TYPE) != I_DIRECTORY) {
return(ENOTDIR);
}
if (strcmp(string,".") == 0) {
*numb = ID_DIR_RECORD(ldir_ptr);
return OK;
}
if (strcmp(string,"..") == 0 && ldir_ptr->loc_extent_l == v_pri.dir_rec_root->loc_extent_l) {
*numb = ROOT_INO_NR;
/* *numb = ID_DIR_RECORD(ldir_ptr); */
return OK;
}
/* Read the dir's content */
pos = ldir_ptr->ext_attr_rec_length;
bp = get_block(ldir_ptr->loc_extent_l);
if (bp == NULL)
return EINVAL;
while (pos < v_pri.logical_block_size_l) {
if ((dir_tmp = get_free_dir_record()) == NULL) {
put_block(bp);
return EINVAL;
}
if (create_dir_record(dir_tmp,bp->b_data + pos,
ldir_ptr->loc_extent_l*v_pri.logical_block_size_l + pos) == EINVAL)
return EINVAL;
if (dir_tmp->length == 0) {
release_dir_record(dir_tmp);
put_block(bp);
return EINVAL;
}
memcpy(tmp_string,dir_tmp->file_id,dir_tmp->length_file_id);
comma_pos = strchr(tmp_string,';');
if (comma_pos != NULL)
*comma_pos = 0;
else
tmp_string[dir_tmp->length_file_id] = 0;
if (tmp_string[strlen(tmp_string) - 1] == '.')
tmp_string[strlen(tmp_string) - 1] = '\0';
if (strcmp(tmp_string,string) == 0 ||
(dir_tmp->file_id[0] == 1 && strcmp(string,"..") == 0)) {
/* If the element is found or we are searchig for... */
if (dir_tmp->loc_extent_l == dir_records->loc_extent_l) {
/* In this case the inode is a root because the parent
* points to the same location than the inode. */
*numb = 1;
release_dir_record(dir_tmp);
put_block(bp);
return OK;
}
if (dir_tmp->ext_attr_rec_length != 0) {
dir_tmp->ext_attr = get_free_ext_attr();
create_ext_attr(dir_tmp->ext_attr,bp->b_data);
}
*numb = ID_DIR_RECORD(dir_tmp);
release_dir_record(dir_tmp);
put_block(bp);
return OK;
}
pos += dir_tmp->length;
release_dir_record(dir_tmp);
}
put_block(bp);
return EINVAL;
}
/*===========================================================================*
* parse_path *
*===========================================================================*/
PRIVATE int parse_path(dir_ino, root_ino, flags, res_inop, offsetp)
ino_t dir_ino;
ino_t root_ino;
int flags;
struct dir_record **res_inop;
size_t *offsetp;
{
int r;
char string[NAME_MAX+1];
char *cp, *ncp;
struct dir_record *start_dir, *old_dir;
/* Find starting inode inode according to the request message */
if ((start_dir = get_dir_record(dir_ino)) == NULL) {
printf("ISOFS: couldn't find starting inode %d\n", dir_ino);
return(ENOENT);
}
cp = user_path;
/* Scan the path component by component. */
while (TRUE) {
if (cp[0] == '\0') {
/* Empty path */
*res_inop= start_dir;
*offsetp += cp-user_path;
/* Return EENTERMOUNT if we are at a mount point */
if (start_dir->d_mountpoint)
return EENTERMOUNT;
return OK;
}
if (cp[0] == '/') {
/* Special case code. If the remaining path consists of just
* slashes, we need to look up '.'
*/
while(cp[0] == '/')
cp++;
if (cp[0] == '\0') {
strcpy(string, ".");
ncp = cp;
}
else
ncp = get_name(cp, string);
} else
/* Just get the first component */
ncp = get_name(cp, string);
/* Special code for '..'. A process is not allowed to leave a chrooted
* environment. A lookup of '..' at the root of a mounted filesystem
* has to return ELEAVEMOUNT.
*/
if (strcmp(string, "..") == 0) {
/* This condition is not necessary since it will never be the root filesystem */
/* if (start_dir == dir_records) { */
/* cp = ncp; */
/* continue; /\* Just ignore the '..' at a process' */
/* * root. */
/* *\/ */
/* } */
if (start_dir == dir_records) {
/* Climbing up mountpoint */
release_dir_record(start_dir);
*res_inop = NULL;
*offsetp += cp-user_path;
return ELEAVEMOUNT;
}
} else {
/* Only check for a mount point if we are not looking for '..'. */
if (start_dir->d_mountpoint) {
*res_inop= start_dir;
*offsetp += cp-user_path;
return EENTERMOUNT;
}
}
/* There is more path. Keep parsing. */
old_dir = start_dir;
r = advance(old_dir, string, &start_dir);
if (r != OK) {
release_dir_record(old_dir);
return r;
}
release_dir_record(old_dir);
cp = ncp;
}
}
/*===========================================================================*
* advance *
*===========================================================================*/
PUBLIC int advance(dirp, string, resp)
struct dir_record *dirp; /* inode for directory to be searched */
char string[NAME_MAX]; /* component name to look for */
struct dir_record **resp; /* resulting inode */
{
/* Given a directory and a component of a path, look up the component in
* the directory, find the inode, open it, and return a pointer to its inode
* slot.
*/
register struct dir_record *rip = NULL;
int r;
ino_t numb;
/* If 'string' is empty, yield same inode straight away. */
if (string[0] == '\0') {
return ENOENT;
}
/* Check for NULL. */
if (dirp == NULL) {
return EINVAL;
}
/* If 'string' is not present in the directory, signal error. */
if ( (r = search_dir(dirp, string, &numb)) != OK) {
return r;
}
/* The component has been found in the directory. Get inode. */
if ( (rip = get_dir_record((int) numb)) == NULL) {
return(err_code);
}
*resp= rip;
return OK;
}
/*===========================================================================*
* get_name *
*===========================================================================*/
PRIVATE char *get_name(path_name, string)
char *path_name; /* path name to parse */
char string[NAME_MAX+1]; /* component extracted from 'old_name' */
{
/* Given a pointer to a path name in fs space, 'path_name', copy the first
* component to 'string' (truncated if necessary, always nul terminated).
* A pointer to the string after the first component of the name as yet
* unparsed is returned. Roughly speaking,
* 'get_name' = 'path_name' - 'string'.
*
* This routine follows the standard convention that /usr/ast, /usr//ast,
* //usr///ast and /usr/ast/ are all equivalent.
*/
size_t len;
char *cp, *ep;
cp= path_name;
/* Skip leading slashes */
while (cp[0] == '/')
cp++;
/* Find the end of the first component */
ep= cp;
while(ep[0] != '\0' && ep[0] != '/')
ep++;
len= ep-cp;
/* Truncate the amount to be copied if it exceeds NAME_MAX */
if (len > NAME_MAX)
len= NAME_MAX;
/* Special case of the string at cp is empty */
if (len == 0)
{
/* Return "." */
strcpy(string, ".");
}
else
{
memcpy(string, cp, len);
string[len]= '\0';
}
return ep;
}