1*433d6423SLionel Sambuc /* This file contains the procedures that look up path names in the directory 2*433d6423SLionel Sambuc * system and determine the inode number that goes with a given path name. 3*433d6423SLionel Sambuc * 4*433d6423SLionel Sambuc * The entry points into this file are 5*433d6423SLionel Sambuc * eat_path: the 'main' routine of the path-to-inode conversion mechanism 6*433d6423SLionel Sambuc * last_dir: find the final directory on a given path 7*433d6423SLionel Sambuc * advance: parse one component of a path name 8*433d6423SLionel Sambuc * search_dir: search a directory for a string and return its inode number 9*433d6423SLionel Sambuc * 10*433d6423SLionel Sambuc */ 11*433d6423SLionel Sambuc 12*433d6423SLionel Sambuc #include "fs.h" 13*433d6423SLionel Sambuc #include "assert.h" 14*433d6423SLionel Sambuc #include <string.h> 15*433d6423SLionel Sambuc #include <minix/endpoint.h> 16*433d6423SLionel Sambuc #include <sys/stat.h> 17*433d6423SLionel Sambuc #include <sys/types.h> 18*433d6423SLionel Sambuc #include "buf.h" 19*433d6423SLionel Sambuc #include "inode.h" 20*433d6423SLionel Sambuc #include "super.h" 21*433d6423SLionel Sambuc #include <minix/vfsif.h> 22*433d6423SLionel Sambuc #include <minix/libminixfs.h> 23*433d6423SLionel Sambuc 24*433d6423SLionel Sambuc 25*433d6423SLionel Sambuc char dot1[2] = "."; /* used for search_dir to bypass the access */ 26*433d6423SLionel Sambuc char dot2[3] = ".."; /* permissions for . and .. */ 27*433d6423SLionel Sambuc 28*433d6423SLionel Sambuc static char *get_name(char *name, char string[MFS_NAME_MAX+1]); 29*433d6423SLionel Sambuc static int ltraverse(struct inode *rip, char *suffix); 30*433d6423SLionel Sambuc static int parse_path(ino_t dir_ino, ino_t root_ino, int flags, struct 31*433d6423SLionel Sambuc inode **res_inop, size_t *offsetp, int *symlinkp); 32*433d6423SLionel Sambuc 33*433d6423SLionel Sambuc 34*433d6423SLionel Sambuc /*===========================================================================* 35*433d6423SLionel Sambuc * fs_lookup * 36*433d6423SLionel Sambuc *===========================================================================*/ 37*433d6423SLionel Sambuc int fs_lookup() 38*433d6423SLionel Sambuc { 39*433d6423SLionel Sambuc cp_grant_id_t grant; 40*433d6423SLionel Sambuc int r, r1, flags, symlinks; 41*433d6423SLionel Sambuc unsigned int len; 42*433d6423SLionel Sambuc size_t offset = 0, path_size; 43*433d6423SLionel Sambuc ino_t dir_ino, root_ino; 44*433d6423SLionel Sambuc struct inode *rip; 45*433d6423SLionel Sambuc 46*433d6423SLionel Sambuc grant = fs_m_in.m_vfs_fs_lookup.grant_path; 47*433d6423SLionel Sambuc path_size = fs_m_in.m_vfs_fs_lookup.path_size; /* Size of the buffer */ 48*433d6423SLionel Sambuc len = fs_m_in.m_vfs_fs_lookup.path_len; /* including terminating nul */ 49*433d6423SLionel Sambuc dir_ino = fs_m_in.m_vfs_fs_lookup.dir_ino; 50*433d6423SLionel Sambuc root_ino = fs_m_in.m_vfs_fs_lookup.root_ino; 51*433d6423SLionel Sambuc flags = fs_m_in.m_vfs_fs_lookup.flags; 52*433d6423SLionel Sambuc 53*433d6423SLionel Sambuc /* Check length. */ 54*433d6423SLionel Sambuc if(len > sizeof(user_path)) return(E2BIG); /* too big for buffer */ 55*433d6423SLionel Sambuc if(len == 0) return(EINVAL); /* too small */ 56*433d6423SLionel Sambuc 57*433d6423SLionel Sambuc /* Copy the pathname and set up caller's user and group id */ 58*433d6423SLionel Sambuc r = sys_safecopyfrom(VFS_PROC_NR, grant, /*offset*/ (vir_bytes) 0, 59*433d6423SLionel Sambuc (vir_bytes) user_path, (size_t) len); 60*433d6423SLionel Sambuc if(r != OK) return(r); 61*433d6423SLionel Sambuc 62*433d6423SLionel Sambuc /* Verify this is a null-terminated path. */ 63*433d6423SLionel Sambuc if(user_path[len - 1] != '\0') return(EINVAL); 64*433d6423SLionel Sambuc 65*433d6423SLionel Sambuc memset(&credentials, 0, sizeof(credentials)); 66*433d6423SLionel Sambuc if(!(flags & PATH_GET_UCRED)) { /* Do we have to copy uid/gid credentials? */ 67*433d6423SLionel Sambuc caller_uid = fs_m_in.m_vfs_fs_lookup.uid; 68*433d6423SLionel Sambuc caller_gid = fs_m_in.m_vfs_fs_lookup.gid; 69*433d6423SLionel Sambuc } else { 70*433d6423SLionel Sambuc if((r=fs_lookup_credentials(&credentials, 71*433d6423SLionel Sambuc &caller_uid, &caller_gid, 72*433d6423SLionel Sambuc fs_m_in.m_vfs_fs_lookup.grant_ucred, 73*433d6423SLionel Sambuc fs_m_in.m_vfs_fs_lookup.ucred_size)) != OK) 74*433d6423SLionel Sambuc return r; 75*433d6423SLionel Sambuc } 76*433d6423SLionel Sambuc 77*433d6423SLionel Sambuc /* Lookup inode */ 78*433d6423SLionel Sambuc rip = NULL; 79*433d6423SLionel Sambuc r = parse_path(dir_ino, root_ino, flags, &rip, &offset, &symlinks); 80*433d6423SLionel Sambuc 81*433d6423SLionel Sambuc if(symlinks != 0 && (r == ELEAVEMOUNT || r == EENTERMOUNT || r == ESYMLINK)){ 82*433d6423SLionel Sambuc len = strlen(user_path)+1; 83*433d6423SLionel Sambuc if(len > path_size) return(ENAMETOOLONG); 84*433d6423SLionel Sambuc 85*433d6423SLionel Sambuc r1 = sys_safecopyto(VFS_PROC_NR, grant, (vir_bytes) 0, 86*433d6423SLionel Sambuc (vir_bytes) user_path, (size_t) len); 87*433d6423SLionel Sambuc if(r1 != OK) return(r1); 88*433d6423SLionel Sambuc } 89*433d6423SLionel Sambuc 90*433d6423SLionel Sambuc if(r == ELEAVEMOUNT || r == ESYMLINK) { 91*433d6423SLionel Sambuc /* Report offset and the error */ 92*433d6423SLionel Sambuc fs_m_out.m_fs_vfs_lookup.offset = offset; 93*433d6423SLionel Sambuc fs_m_out.m_fs_vfs_lookup.symloop = symlinks; 94*433d6423SLionel Sambuc 95*433d6423SLionel Sambuc return(r); 96*433d6423SLionel Sambuc } 97*433d6423SLionel Sambuc 98*433d6423SLionel Sambuc if (r != OK && r != EENTERMOUNT) return(r); 99*433d6423SLionel Sambuc 100*433d6423SLionel Sambuc fs_m_out.m_fs_vfs_lookup.inode = rip->i_num; 101*433d6423SLionel Sambuc fs_m_out.m_fs_vfs_lookup.mode = rip->i_mode; 102*433d6423SLionel Sambuc fs_m_out.m_fs_vfs_lookup.file_size = rip->i_size; 103*433d6423SLionel Sambuc fs_m_out.m_fs_vfs_lookup.symloop = symlinks; 104*433d6423SLionel Sambuc fs_m_out.m_fs_vfs_lookup.uid = rip->i_uid; 105*433d6423SLionel Sambuc fs_m_out.m_fs_vfs_lookup.gid = rip->i_gid; 106*433d6423SLionel Sambuc 107*433d6423SLionel Sambuc /* This is only valid for block and character specials. But it doesn't 108*433d6423SLionel Sambuc * cause any harm to always set the device field. */ 109*433d6423SLionel Sambuc fs_m_out.m_fs_vfs_lookup.device = (dev_t) rip->i_zone[0]; 110*433d6423SLionel Sambuc 111*433d6423SLionel Sambuc if(r == EENTERMOUNT) { 112*433d6423SLionel Sambuc fs_m_out.m_fs_vfs_lookup.offset = offset; 113*433d6423SLionel Sambuc put_inode(rip); /* Only return a reference to the final object */ 114*433d6423SLionel Sambuc } 115*433d6423SLionel Sambuc 116*433d6423SLionel Sambuc return(r); 117*433d6423SLionel Sambuc } 118*433d6423SLionel Sambuc 119*433d6423SLionel Sambuc 120*433d6423SLionel Sambuc /*===========================================================================* 121*433d6423SLionel Sambuc * parse_path * 122*433d6423SLionel Sambuc *===========================================================================*/ 123*433d6423SLionel Sambuc static int parse_path( 124*433d6423SLionel Sambuc ino_t dir_ino, 125*433d6423SLionel Sambuc ino_t root_ino, 126*433d6423SLionel Sambuc int flags, 127*433d6423SLionel Sambuc struct inode **res_inop, 128*433d6423SLionel Sambuc size_t *offsetp, 129*433d6423SLionel Sambuc int *symlinkp 130*433d6423SLionel Sambuc ) { 131*433d6423SLionel Sambuc /* Parse the path in user_path, starting at dir_ino. If the path is the empty 132*433d6423SLionel Sambuc * string, just return dir_ino. It is upto the caller to treat an empty 133*433d6423SLionel Sambuc * path in a special way. Otherwise, if the path consists of just one or 134*433d6423SLionel Sambuc * more slash ('/') characters, the path is replaced with ".". Otherwise, 135*433d6423SLionel Sambuc * just look up the first (or only) component in path after skipping any 136*433d6423SLionel Sambuc * leading slashes. 137*433d6423SLionel Sambuc */ 138*433d6423SLionel Sambuc int r, leaving_mount; 139*433d6423SLionel Sambuc struct inode *rip, *dir_ip; 140*433d6423SLionel Sambuc char *cp, *next_cp; /* component and next component */ 141*433d6423SLionel Sambuc char component[MFS_NAME_MAX+1]; 142*433d6423SLionel Sambuc 143*433d6423SLionel Sambuc /* Start parsing path at the first component in user_path */ 144*433d6423SLionel Sambuc cp = user_path; 145*433d6423SLionel Sambuc 146*433d6423SLionel Sambuc /* No symlinks encountered yet */ 147*433d6423SLionel Sambuc *symlinkp = 0; 148*433d6423SLionel Sambuc 149*433d6423SLionel Sambuc /* Find starting inode inode according to the request message */ 150*433d6423SLionel Sambuc if((rip = find_inode(fs_dev, dir_ino)) == NULL) 151*433d6423SLionel Sambuc return(ENOENT); 152*433d6423SLionel Sambuc 153*433d6423SLionel Sambuc /* If dir has been removed return ENOENT. */ 154*433d6423SLionel Sambuc if (rip->i_nlinks == NO_LINK) return(ENOENT); 155*433d6423SLionel Sambuc 156*433d6423SLionel Sambuc dup_inode(rip); 157*433d6423SLionel Sambuc 158*433d6423SLionel Sambuc /* If the given start inode is a mountpoint, we must be here because the file 159*433d6423SLionel Sambuc * system mounted on top returned an ELEAVEMOUNT error. In this case, we must 160*433d6423SLionel Sambuc * only accept ".." as the first path component. 161*433d6423SLionel Sambuc */ 162*433d6423SLionel Sambuc leaving_mount = rip->i_mountpoint; /* True iff rip is a mountpoint */ 163*433d6423SLionel Sambuc 164*433d6423SLionel Sambuc /* Scan the path component by component. */ 165*433d6423SLionel Sambuc while (TRUE) { 166*433d6423SLionel Sambuc if(cp[0] == '\0') { 167*433d6423SLionel Sambuc /* We're done; either the path was empty or we've parsed all 168*433d6423SLionel Sambuc components of the path */ 169*433d6423SLionel Sambuc 170*433d6423SLionel Sambuc *res_inop = rip; 171*433d6423SLionel Sambuc *offsetp += cp - user_path; 172*433d6423SLionel Sambuc 173*433d6423SLionel Sambuc /* Return EENTERMOUNT if we are at a mount point */ 174*433d6423SLionel Sambuc if (rip->i_mountpoint) return(EENTERMOUNT); 175*433d6423SLionel Sambuc 176*433d6423SLionel Sambuc return(OK); 177*433d6423SLionel Sambuc } 178*433d6423SLionel Sambuc 179*433d6423SLionel Sambuc while(cp[0] == '/') cp++; 180*433d6423SLionel Sambuc next_cp = get_name(cp, component); 181*433d6423SLionel Sambuc 182*433d6423SLionel Sambuc /* Special code for '..'. A process is not allowed to leave a chrooted 183*433d6423SLionel Sambuc * environment. A lookup of '..' at the root of a mounted filesystem 184*433d6423SLionel Sambuc * has to return ELEAVEMOUNT. In both cases, the caller needs search 185*433d6423SLionel Sambuc * permission for the current inode, as it is used as directory. 186*433d6423SLionel Sambuc */ 187*433d6423SLionel Sambuc if(strcmp(component, "..") == 0) { 188*433d6423SLionel Sambuc /* 'rip' is now accessed as directory */ 189*433d6423SLionel Sambuc if ((r = forbidden(rip, X_BIT)) != OK) { 190*433d6423SLionel Sambuc put_inode(rip); 191*433d6423SLionel Sambuc return(r); 192*433d6423SLionel Sambuc } 193*433d6423SLionel Sambuc 194*433d6423SLionel Sambuc if (rip->i_num == root_ino) { 195*433d6423SLionel Sambuc cp = next_cp; 196*433d6423SLionel Sambuc continue; /* Ignore the '..' at a process' root 197*433d6423SLionel Sambuc and move on to the next component */ 198*433d6423SLionel Sambuc } 199*433d6423SLionel Sambuc 200*433d6423SLionel Sambuc if (rip->i_num == ROOT_INODE && !rip->i_sp->s_is_root) { 201*433d6423SLionel Sambuc /* Climbing up to parent FS */ 202*433d6423SLionel Sambuc 203*433d6423SLionel Sambuc put_inode(rip); 204*433d6423SLionel Sambuc *offsetp += cp - user_path; 205*433d6423SLionel Sambuc return(ELEAVEMOUNT); 206*433d6423SLionel Sambuc } 207*433d6423SLionel Sambuc } 208*433d6423SLionel Sambuc 209*433d6423SLionel Sambuc /* Only check for a mount point if we are not coming from one. */ 210*433d6423SLionel Sambuc if (!leaving_mount && rip->i_mountpoint) { 211*433d6423SLionel Sambuc /* Going to enter a child FS */ 212*433d6423SLionel Sambuc 213*433d6423SLionel Sambuc *res_inop = rip; 214*433d6423SLionel Sambuc *offsetp += cp - user_path; 215*433d6423SLionel Sambuc return(EENTERMOUNT); 216*433d6423SLionel Sambuc } 217*433d6423SLionel Sambuc 218*433d6423SLionel Sambuc /* There is more path. Keep parsing. 219*433d6423SLionel Sambuc * If we're leaving a mountpoint, skip directory permission checks. 220*433d6423SLionel Sambuc */ 221*433d6423SLionel Sambuc dir_ip = rip; 222*433d6423SLionel Sambuc rip = advance(dir_ip, leaving_mount ? dot2 : component, CHK_PERM); 223*433d6423SLionel Sambuc if(err_code == ELEAVEMOUNT || err_code == EENTERMOUNT) 224*433d6423SLionel Sambuc err_code = OK; 225*433d6423SLionel Sambuc 226*433d6423SLionel Sambuc if (err_code != OK) { 227*433d6423SLionel Sambuc put_inode(dir_ip); 228*433d6423SLionel Sambuc return(err_code); 229*433d6423SLionel Sambuc } 230*433d6423SLionel Sambuc 231*433d6423SLionel Sambuc leaving_mount = 0; 232*433d6423SLionel Sambuc 233*433d6423SLionel Sambuc /* The call to advance() succeeded. Fetch next component. */ 234*433d6423SLionel Sambuc if (S_ISLNK(rip->i_mode)) { 235*433d6423SLionel Sambuc 236*433d6423SLionel Sambuc if (next_cp[0] == '\0' && (flags & PATH_RET_SYMLINK)) { 237*433d6423SLionel Sambuc put_inode(dir_ip); 238*433d6423SLionel Sambuc *res_inop = rip; 239*433d6423SLionel Sambuc *offsetp += next_cp - user_path; 240*433d6423SLionel Sambuc return(OK); 241*433d6423SLionel Sambuc } 242*433d6423SLionel Sambuc 243*433d6423SLionel Sambuc /* Extract path name from the symlink file */ 244*433d6423SLionel Sambuc r = ltraverse(rip, next_cp); 245*433d6423SLionel Sambuc next_cp = user_path; 246*433d6423SLionel Sambuc *offsetp = 0; 247*433d6423SLionel Sambuc 248*433d6423SLionel Sambuc /* Symloop limit reached? */ 249*433d6423SLionel Sambuc if (++(*symlinkp) > _POSIX_SYMLOOP_MAX) 250*433d6423SLionel Sambuc r = ELOOP; 251*433d6423SLionel Sambuc 252*433d6423SLionel Sambuc if (r != OK) { 253*433d6423SLionel Sambuc put_inode(dir_ip); 254*433d6423SLionel Sambuc put_inode(rip); 255*433d6423SLionel Sambuc return(r); 256*433d6423SLionel Sambuc } 257*433d6423SLionel Sambuc 258*433d6423SLionel Sambuc if (next_cp[0] == '/') { 259*433d6423SLionel Sambuc put_inode(dir_ip); 260*433d6423SLionel Sambuc put_inode(rip); 261*433d6423SLionel Sambuc return(ESYMLINK); 262*433d6423SLionel Sambuc } 263*433d6423SLionel Sambuc 264*433d6423SLionel Sambuc put_inode(rip); 265*433d6423SLionel Sambuc dup_inode(dir_ip); 266*433d6423SLionel Sambuc rip = dir_ip; 267*433d6423SLionel Sambuc } 268*433d6423SLionel Sambuc 269*433d6423SLionel Sambuc put_inode(dir_ip); 270*433d6423SLionel Sambuc cp = next_cp; /* Process subsequent component in next round */ 271*433d6423SLionel Sambuc } 272*433d6423SLionel Sambuc } 273*433d6423SLionel Sambuc 274*433d6423SLionel Sambuc 275*433d6423SLionel Sambuc /*===========================================================================* 276*433d6423SLionel Sambuc * ltraverse * 277*433d6423SLionel Sambuc *===========================================================================*/ 278*433d6423SLionel Sambuc static int ltraverse(rip, suffix) 279*433d6423SLionel Sambuc register struct inode *rip; /* symbolic link */ 280*433d6423SLionel Sambuc char *suffix; /* current remaining path. Has to point in the 281*433d6423SLionel Sambuc * user_path buffer 282*433d6423SLionel Sambuc */ 283*433d6423SLionel Sambuc { 284*433d6423SLionel Sambuc /* Traverse a symbolic link. Copy the link text from the inode and insert 285*433d6423SLionel Sambuc * the text into the path. Return error code or report success. Base 286*433d6423SLionel Sambuc * directory has to be determined according to the first character of the 287*433d6423SLionel Sambuc * new pathname. 288*433d6423SLionel Sambuc */ 289*433d6423SLionel Sambuc 290*433d6423SLionel Sambuc size_t llen; /* length of link */ 291*433d6423SLionel Sambuc size_t slen; /* length of suffix */ 292*433d6423SLionel Sambuc struct buf *bp; /* buffer containing link text */ 293*433d6423SLionel Sambuc char *sp; /* start of link text */ 294*433d6423SLionel Sambuc 295*433d6423SLionel Sambuc if(!(bp = get_block_map(rip, 0))) 296*433d6423SLionel Sambuc return(EIO); 297*433d6423SLionel Sambuc llen = (size_t) rip->i_size; 298*433d6423SLionel Sambuc sp = b_data(bp); 299*433d6423SLionel Sambuc slen = strlen(suffix); 300*433d6423SLionel Sambuc 301*433d6423SLionel Sambuc /* The path we're parsing looks like this: 302*433d6423SLionel Sambuc * /already/processed/path/<link> or 303*433d6423SLionel Sambuc * /already/processed/path/<link>/not/yet/processed/path 304*433d6423SLionel Sambuc * After expanding the <link>, the path will look like 305*433d6423SLionel Sambuc * <expandedlink> or 306*433d6423SLionel Sambuc * <expandedlink>/not/yet/processed 307*433d6423SLionel Sambuc * In both cases user_path must have enough room to hold <expandedlink>. 308*433d6423SLionel Sambuc * However, in the latter case we have to move /not/yet/processed to the 309*433d6423SLionel Sambuc * right place first, before we expand <link>. When strlen(<expandedlink>) is 310*433d6423SLionel Sambuc * smaller than strlen(/already/processes/path), we move the suffix to the 311*433d6423SLionel Sambuc * left. Is strlen(<expandedlink>) greater then we move it to the right. Else 312*433d6423SLionel Sambuc * we do nothing. 313*433d6423SLionel Sambuc */ 314*433d6423SLionel Sambuc 315*433d6423SLionel Sambuc if (slen > 0) { /* Do we have path after the link? */ 316*433d6423SLionel Sambuc /* For simplicity we require that suffix starts with a slash */ 317*433d6423SLionel Sambuc if (suffix[0] != '/') { 318*433d6423SLionel Sambuc panic("ltraverse: suffix does not start with a slash"); 319*433d6423SLionel Sambuc } 320*433d6423SLionel Sambuc 321*433d6423SLionel Sambuc /* To be able to expand the <link>, we have to move the 'suffix' 322*433d6423SLionel Sambuc * to the right place. 323*433d6423SLionel Sambuc */ 324*433d6423SLionel Sambuc if (slen + llen + 1 > sizeof(user_path)) 325*433d6423SLionel Sambuc return(ENAMETOOLONG);/* <expandedlink>+suffix+\0 does not fit*/ 326*433d6423SLionel Sambuc if ((unsigned) (suffix-user_path) != llen) { 327*433d6423SLionel Sambuc /* Move suffix left or right */ 328*433d6423SLionel Sambuc memmove(&user_path[llen], suffix, slen+1); 329*433d6423SLionel Sambuc } 330*433d6423SLionel Sambuc } else { 331*433d6423SLionel Sambuc if (llen + 1 > sizeof(user_path)) 332*433d6423SLionel Sambuc return(ENAMETOOLONG); /* <expandedlink> + \0 does not fix */ 333*433d6423SLionel Sambuc 334*433d6423SLionel Sambuc /* Set terminating nul */ 335*433d6423SLionel Sambuc user_path[llen]= '\0'; 336*433d6423SLionel Sambuc } 337*433d6423SLionel Sambuc 338*433d6423SLionel Sambuc /* Everything is set, now copy the expanded link to user_path */ 339*433d6423SLionel Sambuc memmove(user_path, sp, llen); 340*433d6423SLionel Sambuc 341*433d6423SLionel Sambuc put_block(bp, DIRECTORY_BLOCK); 342*433d6423SLionel Sambuc return(OK); 343*433d6423SLionel Sambuc } 344*433d6423SLionel Sambuc 345*433d6423SLionel Sambuc 346*433d6423SLionel Sambuc /*===========================================================================* 347*433d6423SLionel Sambuc * advance * 348*433d6423SLionel Sambuc *===========================================================================*/ 349*433d6423SLionel Sambuc struct inode *advance(dirp, string, chk_perm) 350*433d6423SLionel Sambuc struct inode *dirp; /* inode for directory to be searched */ 351*433d6423SLionel Sambuc char string[MFS_NAME_MAX]; /* component name to look for */ 352*433d6423SLionel Sambuc int chk_perm; /* check permissions when string is looked up*/ 353*433d6423SLionel Sambuc { 354*433d6423SLionel Sambuc /* Given a directory and a component of a path, look up the component in 355*433d6423SLionel Sambuc * the directory, find the inode, open it, and return a pointer to its inode 356*433d6423SLionel Sambuc * slot. 357*433d6423SLionel Sambuc */ 358*433d6423SLionel Sambuc ino_t numb; 359*433d6423SLionel Sambuc struct inode *rip; 360*433d6423SLionel Sambuc 361*433d6423SLionel Sambuc /* If 'string' is empty, return an error. */ 362*433d6423SLionel Sambuc if (string[0] == '\0') { 363*433d6423SLionel Sambuc err_code = ENOENT; 364*433d6423SLionel Sambuc return(NULL); 365*433d6423SLionel Sambuc } 366*433d6423SLionel Sambuc 367*433d6423SLionel Sambuc /* Check for NULL. */ 368*433d6423SLionel Sambuc if (dirp == NULL) return(NULL); 369*433d6423SLionel Sambuc 370*433d6423SLionel Sambuc /* If 'string' is not present in the directory, signal error. */ 371*433d6423SLionel Sambuc if ( (err_code = search_dir(dirp, string, &numb, LOOK_UP, chk_perm)) != OK) { 372*433d6423SLionel Sambuc return(NULL); 373*433d6423SLionel Sambuc } 374*433d6423SLionel Sambuc 375*433d6423SLionel Sambuc /* The component has been found in the directory. Get inode. */ 376*433d6423SLionel Sambuc if ( (rip = get_inode(dirp->i_dev, (int) numb)) == NULL) { 377*433d6423SLionel Sambuc return(NULL); 378*433d6423SLionel Sambuc } 379*433d6423SLionel Sambuc 380*433d6423SLionel Sambuc /* The following test is for "mountpoint/.." where mountpoint is a 381*433d6423SLionel Sambuc * mountpoint. ".." will refer to the root of the mounted filesystem, 382*433d6423SLionel Sambuc * but has to become a reference to the parent of the 'mountpoint' 383*433d6423SLionel Sambuc * directory. 384*433d6423SLionel Sambuc * 385*433d6423SLionel Sambuc * This case is recognized by the looked up name pointing to a 386*433d6423SLionel Sambuc * root inode, and the directory in which it is held being a 387*433d6423SLionel Sambuc * root inode, _and_ the name[1] being '.'. (This is a test for '..' 388*433d6423SLionel Sambuc * and excludes '.'.) 389*433d6423SLionel Sambuc */ 390*433d6423SLionel Sambuc if (rip->i_num == ROOT_INODE) { 391*433d6423SLionel Sambuc if (dirp->i_num == ROOT_INODE) { 392*433d6423SLionel Sambuc if (string[1] == '.') { 393*433d6423SLionel Sambuc if (!rip->i_sp->s_is_root) { 394*433d6423SLionel Sambuc /* Climbing up mountpoint */ 395*433d6423SLionel Sambuc err_code = ELEAVEMOUNT; 396*433d6423SLionel Sambuc } 397*433d6423SLionel Sambuc } 398*433d6423SLionel Sambuc } 399*433d6423SLionel Sambuc } 400*433d6423SLionel Sambuc 401*433d6423SLionel Sambuc /* See if the inode is mounted on. If so, switch to root directory of the 402*433d6423SLionel Sambuc * mounted file system. The super_block provides the linkage between the 403*433d6423SLionel Sambuc * inode mounted on and the root directory of the mounted file system. 404*433d6423SLionel Sambuc */ 405*433d6423SLionel Sambuc if (rip->i_mountpoint) { 406*433d6423SLionel Sambuc /* Mountpoint encountered, report it */ 407*433d6423SLionel Sambuc err_code = EENTERMOUNT; 408*433d6423SLionel Sambuc } 409*433d6423SLionel Sambuc 410*433d6423SLionel Sambuc return(rip); 411*433d6423SLionel Sambuc } 412*433d6423SLionel Sambuc 413*433d6423SLionel Sambuc 414*433d6423SLionel Sambuc /*===========================================================================* 415*433d6423SLionel Sambuc * get_name * 416*433d6423SLionel Sambuc *===========================================================================*/ 417*433d6423SLionel Sambuc static char *get_name(path_name, string) 418*433d6423SLionel Sambuc char *path_name; /* path name to parse */ 419*433d6423SLionel Sambuc char string[MFS_NAME_MAX+1]; /* component extracted from 'old_name' */ 420*433d6423SLionel Sambuc { 421*433d6423SLionel Sambuc /* Given a pointer to a path name in fs space, 'path_name', copy the first 422*433d6423SLionel Sambuc * component to 'string' (truncated if necessary, always nul terminated). 423*433d6423SLionel Sambuc * A pointer to the string after the first component of the name as yet 424*433d6423SLionel Sambuc * unparsed is returned. Roughly speaking, 425*433d6423SLionel Sambuc * 'get_name' = 'path_name' - 'string'. 426*433d6423SLionel Sambuc * 427*433d6423SLionel Sambuc * This routine follows the standard convention that /usr/ast, /usr//ast, 428*433d6423SLionel Sambuc * //usr///ast and /usr/ast/ are all equivalent. 429*433d6423SLionel Sambuc */ 430*433d6423SLionel Sambuc size_t len; 431*433d6423SLionel Sambuc char *cp, *ep; 432*433d6423SLionel Sambuc 433*433d6423SLionel Sambuc cp = path_name; 434*433d6423SLionel Sambuc 435*433d6423SLionel Sambuc /* Skip leading slashes */ 436*433d6423SLionel Sambuc while (cp[0] == '/') cp++; 437*433d6423SLionel Sambuc 438*433d6423SLionel Sambuc /* Find the end of the first component */ 439*433d6423SLionel Sambuc ep = cp; 440*433d6423SLionel Sambuc while(ep[0] != '\0' && ep[0] != '/') 441*433d6423SLionel Sambuc ep++; 442*433d6423SLionel Sambuc 443*433d6423SLionel Sambuc len = (size_t) (ep - cp); 444*433d6423SLionel Sambuc 445*433d6423SLionel Sambuc /* Truncate the amount to be copied if it exceeds MFS_NAME_MAX */ 446*433d6423SLionel Sambuc if (len > MFS_NAME_MAX) len = MFS_NAME_MAX; 447*433d6423SLionel Sambuc 448*433d6423SLionel Sambuc /* Special case of the string at cp is empty */ 449*433d6423SLionel Sambuc if (len == 0) 450*433d6423SLionel Sambuc strlcpy(string, ".", MFS_NAME_MAX + 1); /* Return "." */ 451*433d6423SLionel Sambuc else { 452*433d6423SLionel Sambuc memcpy(string, cp, len); 453*433d6423SLionel Sambuc string[len]= '\0'; 454*433d6423SLionel Sambuc } 455*433d6423SLionel Sambuc 456*433d6423SLionel Sambuc return(ep); 457*433d6423SLionel Sambuc } 458*433d6423SLionel Sambuc 459*433d6423SLionel Sambuc 460*433d6423SLionel Sambuc /*===========================================================================* 461*433d6423SLionel Sambuc * search_dir * 462*433d6423SLionel Sambuc *===========================================================================*/ 463*433d6423SLionel Sambuc int search_dir(ldir_ptr, string, numb, flag, check_permissions) 464*433d6423SLionel Sambuc register struct inode *ldir_ptr; /* ptr to inode for dir to search */ 465*433d6423SLionel Sambuc char string[MFS_NAME_MAX]; /* component to search for */ 466*433d6423SLionel Sambuc ino_t *numb; /* pointer to inode number */ 467*433d6423SLionel Sambuc int flag; /* LOOK_UP, ENTER, DELETE or IS_EMPTY */ 468*433d6423SLionel Sambuc int check_permissions; /* check permissions when flag is !IS_EMPTY */ 469*433d6423SLionel Sambuc { 470*433d6423SLionel Sambuc /* This function searches the directory whose inode is pointed to by 'ldip': 471*433d6423SLionel Sambuc * if (flag == ENTER) enter 'string' in the directory with inode # '*numb'; 472*433d6423SLionel Sambuc * if (flag == DELETE) delete 'string' from the directory; 473*433d6423SLionel Sambuc * if (flag == LOOK_UP) search for 'string' and return inode # in 'numb'; 474*433d6423SLionel Sambuc * if (flag == IS_EMPTY) return OK if only . and .. in dir else ENOTEMPTY; 475*433d6423SLionel Sambuc * 476*433d6423SLionel Sambuc * if 'string' is dot1 or dot2, no access permissions are checked. 477*433d6423SLionel Sambuc */ 478*433d6423SLionel Sambuc 479*433d6423SLionel Sambuc register struct direct *dp = NULL; 480*433d6423SLionel Sambuc register struct buf *bp = NULL; 481*433d6423SLionel Sambuc int i, r, e_hit, t, match; 482*433d6423SLionel Sambuc mode_t bits; 483*433d6423SLionel Sambuc off_t pos; 484*433d6423SLionel Sambuc unsigned new_slots, old_slots; 485*433d6423SLionel Sambuc struct super_block *sp; 486*433d6423SLionel Sambuc int extended = 0; 487*433d6423SLionel Sambuc 488*433d6423SLionel Sambuc /* If 'ldir_ptr' is not a pointer to a dir inode, error. */ 489*433d6423SLionel Sambuc if ( (ldir_ptr->i_mode & I_TYPE) != I_DIRECTORY) { 490*433d6423SLionel Sambuc return(ENOTDIR); 491*433d6423SLionel Sambuc } 492*433d6423SLionel Sambuc 493*433d6423SLionel Sambuc if((flag == DELETE || flag == ENTER) && ldir_ptr->i_sp->s_rd_only) 494*433d6423SLionel Sambuc return EROFS; 495*433d6423SLionel Sambuc 496*433d6423SLionel Sambuc r = OK; 497*433d6423SLionel Sambuc 498*433d6423SLionel Sambuc if (flag != IS_EMPTY) { 499*433d6423SLionel Sambuc bits = (flag == LOOK_UP ? X_BIT : W_BIT | X_BIT); 500*433d6423SLionel Sambuc 501*433d6423SLionel Sambuc if (string == dot1 || string == dot2) { 502*433d6423SLionel Sambuc if (flag != LOOK_UP) r = read_only(ldir_ptr); 503*433d6423SLionel Sambuc /* only a writable device is required. */ 504*433d6423SLionel Sambuc } else if(check_permissions) { 505*433d6423SLionel Sambuc r = forbidden(ldir_ptr, bits); /* check access permissions */ 506*433d6423SLionel Sambuc } 507*433d6423SLionel Sambuc } 508*433d6423SLionel Sambuc if (r != OK) return(r); 509*433d6423SLionel Sambuc 510*433d6423SLionel Sambuc /* Step through the directory one block at a time. */ 511*433d6423SLionel Sambuc old_slots = (unsigned) (ldir_ptr->i_size/DIR_ENTRY_SIZE); 512*433d6423SLionel Sambuc new_slots = 0; 513*433d6423SLionel Sambuc e_hit = FALSE; 514*433d6423SLionel Sambuc match = 0; /* set when a string match occurs */ 515*433d6423SLionel Sambuc 516*433d6423SLionel Sambuc pos = 0; 517*433d6423SLionel Sambuc if (flag == ENTER && ldir_ptr->i_last_dpos < ldir_ptr->i_size) { 518*433d6423SLionel Sambuc pos = ldir_ptr->i_last_dpos; 519*433d6423SLionel Sambuc new_slots = (unsigned) (pos/DIR_ENTRY_SIZE); 520*433d6423SLionel Sambuc } 521*433d6423SLionel Sambuc 522*433d6423SLionel Sambuc for (; pos < ldir_ptr->i_size; pos += ldir_ptr->i_sp->s_block_size) { 523*433d6423SLionel Sambuc assert(ldir_ptr->i_dev != NO_DEV); 524*433d6423SLionel Sambuc 525*433d6423SLionel Sambuc /* Since directories don't have holes, 'b' cannot be NO_BLOCK. */ 526*433d6423SLionel Sambuc bp = get_block_map(ldir_ptr, pos); 527*433d6423SLionel Sambuc 528*433d6423SLionel Sambuc assert(ldir_ptr->i_dev != NO_DEV); 529*433d6423SLionel Sambuc assert(bp != NULL); 530*433d6423SLionel Sambuc assert(lmfs_dev(bp) != NO_DEV); 531*433d6423SLionel Sambuc 532*433d6423SLionel Sambuc /* Search a directory block. */ 533*433d6423SLionel Sambuc for (dp = &b_dir(bp)[0]; 534*433d6423SLionel Sambuc dp < &b_dir(bp)[NR_DIR_ENTRIES(ldir_ptr->i_sp->s_block_size)]; 535*433d6423SLionel Sambuc dp++) { 536*433d6423SLionel Sambuc if (++new_slots > old_slots) { /* not found, but room left */ 537*433d6423SLionel Sambuc if (flag == ENTER) e_hit = TRUE; 538*433d6423SLionel Sambuc break; 539*433d6423SLionel Sambuc } 540*433d6423SLionel Sambuc 541*433d6423SLionel Sambuc /* Match occurs if string found. */ 542*433d6423SLionel Sambuc if (flag != ENTER && dp->mfs_d_ino != NO_ENTRY) { 543*433d6423SLionel Sambuc if (flag == IS_EMPTY) { 544*433d6423SLionel Sambuc /* If this test succeeds, dir is not empty. */ 545*433d6423SLionel Sambuc if (strcmp(dp->mfs_d_name, "." ) != 0 && 546*433d6423SLionel Sambuc strcmp(dp->mfs_d_name, "..") != 0) match = 1; 547*433d6423SLionel Sambuc } else { 548*433d6423SLionel Sambuc if (strncmp(dp->mfs_d_name, string, 549*433d6423SLionel Sambuc sizeof(dp->mfs_d_name)) == 0){ 550*433d6423SLionel Sambuc match = 1; 551*433d6423SLionel Sambuc } 552*433d6423SLionel Sambuc } 553*433d6423SLionel Sambuc } 554*433d6423SLionel Sambuc 555*433d6423SLionel Sambuc if (match) { 556*433d6423SLionel Sambuc /* LOOK_UP or DELETE found what it wanted. */ 557*433d6423SLionel Sambuc r = OK; 558*433d6423SLionel Sambuc if (flag == IS_EMPTY) r = ENOTEMPTY; 559*433d6423SLionel Sambuc else if (flag == DELETE) { 560*433d6423SLionel Sambuc /* Save d_ino for recovery. */ 561*433d6423SLionel Sambuc t = MFS_NAME_MAX - sizeof(ino_t); 562*433d6423SLionel Sambuc *((ino_t *) &dp->mfs_d_name[t]) = dp->mfs_d_ino; 563*433d6423SLionel Sambuc dp->mfs_d_ino = NO_ENTRY; /* erase entry */ 564*433d6423SLionel Sambuc MARKDIRTY(bp); 565*433d6423SLionel Sambuc ldir_ptr->i_update |= CTIME | MTIME; 566*433d6423SLionel Sambuc IN_MARKDIRTY(ldir_ptr); 567*433d6423SLionel Sambuc if (pos < ldir_ptr->i_last_dpos) 568*433d6423SLionel Sambuc ldir_ptr->i_last_dpos = pos; 569*433d6423SLionel Sambuc } else { 570*433d6423SLionel Sambuc sp = ldir_ptr->i_sp; /* 'flag' is LOOK_UP */ 571*433d6423SLionel Sambuc *numb = (ino_t) conv4(sp->s_native, 572*433d6423SLionel Sambuc (int) dp->mfs_d_ino); 573*433d6423SLionel Sambuc } 574*433d6423SLionel Sambuc assert(lmfs_dev(bp) != NO_DEV); 575*433d6423SLionel Sambuc put_block(bp, DIRECTORY_BLOCK); 576*433d6423SLionel Sambuc return(r); 577*433d6423SLionel Sambuc } 578*433d6423SLionel Sambuc 579*433d6423SLionel Sambuc /* Check for free slot for the benefit of ENTER. */ 580*433d6423SLionel Sambuc if (flag == ENTER && dp->mfs_d_ino == 0) { 581*433d6423SLionel Sambuc e_hit = TRUE; /* we found a free slot */ 582*433d6423SLionel Sambuc break; 583*433d6423SLionel Sambuc } 584*433d6423SLionel Sambuc } 585*433d6423SLionel Sambuc 586*433d6423SLionel Sambuc /* The whole block has been searched or ENTER has a free slot. */ 587*433d6423SLionel Sambuc if (e_hit) break; /* e_hit set if ENTER can be performed now */ 588*433d6423SLionel Sambuc assert(lmfs_dev(bp) != NO_DEV); 589*433d6423SLionel Sambuc put_block(bp, DIRECTORY_BLOCK); /* otherwise, continue searching dir */ 590*433d6423SLionel Sambuc } 591*433d6423SLionel Sambuc 592*433d6423SLionel Sambuc /* The whole directory has now been searched. */ 593*433d6423SLionel Sambuc if (flag != ENTER) { 594*433d6423SLionel Sambuc return(flag == IS_EMPTY ? OK : ENOENT); 595*433d6423SLionel Sambuc } 596*433d6423SLionel Sambuc 597*433d6423SLionel Sambuc /* When ENTER next time, start searching for free slot from 598*433d6423SLionel Sambuc * i_last_dpos. It gives some performance improvement (3-5%). 599*433d6423SLionel Sambuc */ 600*433d6423SLionel Sambuc ldir_ptr->i_last_dpos = pos; 601*433d6423SLionel Sambuc 602*433d6423SLionel Sambuc /* This call is for ENTER. If no free slot has been found so far, try to 603*433d6423SLionel Sambuc * extend directory. 604*433d6423SLionel Sambuc */ 605*433d6423SLionel Sambuc if (e_hit == FALSE) { /* directory is full and no room left in last block */ 606*433d6423SLionel Sambuc new_slots++; /* increase directory size by 1 entry */ 607*433d6423SLionel Sambuc if (new_slots == 0) return(EFBIG); /* dir size limited by slot count */ 608*433d6423SLionel Sambuc if ( (bp = new_block(ldir_ptr, ldir_ptr->i_size)) == NULL) 609*433d6423SLionel Sambuc return(err_code); 610*433d6423SLionel Sambuc dp = &b_dir(bp)[0]; 611*433d6423SLionel Sambuc extended = 1; 612*433d6423SLionel Sambuc } 613*433d6423SLionel Sambuc 614*433d6423SLionel Sambuc /* 'bp' now points to a directory block with space. 'dp' points to slot. */ 615*433d6423SLionel Sambuc (void) memset(dp->mfs_d_name, 0, (size_t) MFS_NAME_MAX); /* clear entry */ 616*433d6423SLionel Sambuc for (i = 0; i < MFS_NAME_MAX && string[i]; i++) dp->mfs_d_name[i] = string[i]; 617*433d6423SLionel Sambuc sp = ldir_ptr->i_sp; 618*433d6423SLionel Sambuc dp->mfs_d_ino = conv4(sp->s_native, (int) *numb); 619*433d6423SLionel Sambuc MARKDIRTY(bp); 620*433d6423SLionel Sambuc put_block(bp, DIRECTORY_BLOCK); 621*433d6423SLionel Sambuc ldir_ptr->i_update |= CTIME | MTIME; /* mark mtime for update later */ 622*433d6423SLionel Sambuc IN_MARKDIRTY(ldir_ptr); 623*433d6423SLionel Sambuc if (new_slots > old_slots) { 624*433d6423SLionel Sambuc ldir_ptr->i_size = (off_t) new_slots * DIR_ENTRY_SIZE; 625*433d6423SLionel Sambuc /* Send the change to disk if the directory is extended. */ 626*433d6423SLionel Sambuc if (extended) rw_inode(ldir_ptr, WRITING); 627*433d6423SLionel Sambuc } 628*433d6423SLionel Sambuc return(OK); 629*433d6423SLionel Sambuc } 630*433d6423SLionel Sambuc 631