The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/servers/fs/path.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /* This file contains the procedures that look up path names in the directory
    2  * system and determine the inode number that goes with a given path name.
    3  *
    4  *  The entry points into this file are
    5  *   eat_path:   the 'main' routine of the path-to-inode conversion mechanism
    6  *   last_dir:   find the final directory on a given path
    7  *   advance:    parse one component of a path name
    8  *   search_dir: search a directory for a string and return its inode number
    9  */
   10 
   11 #include "fs.h"
   12 #include <string.h>
   13 #include <minix/callnr.h>
   14 #include "buf.h"
   15 #include "file.h"
   16 #include "fproc.h"
   17 #include "inode.h"
   18 #include "super.h"
   19 
   20 PUBLIC char dot1[2] = ".";      /* used for search_dir to bypass the access */
   21 PUBLIC char dot2[3] = "..";     /* permissions for . and ..                 */
   22 
   23 FORWARD _PROTOTYPE( char *get_name, (char *old_name, char string [NAME_MAX]) );
   24 
   25 /*===========================================================================*
   26  *                              eat_path                                     *
   27  *===========================================================================*/
   28 PUBLIC struct inode *eat_path(path)
   29 char *path;                     /* the path name to be parsed */
   30 {
   31 /* Parse the path 'path' and put its inode in the inode table. If not possible,
   32  * return NIL_INODE as function value and an error code in 'err_code'.
   33  */
   34 
   35   register struct inode *ldip, *rip;
   36   char string[NAME_MAX];        /* hold 1 path component name here */
   37 
   38   /* First open the path down to the final directory. */
   39   if ( (ldip = last_dir(path, string)) == NIL_INODE) {
   40         return(NIL_INODE);      /* we couldn't open final directory */
   41         }
   42 
   43   /* The path consisting only of "/" is a special case, check for it. */
   44   if (string[0] == '\0') return(ldip);
   45 
   46   /* Get final component of the path. */
   47   rip = advance(ldip, string);
   48   put_inode(ldip);
   49   return(rip);
   50 }
   51 
   52 /*===========================================================================*
   53  *                              last_dir                                     *
   54  *===========================================================================*/
   55 PUBLIC struct inode *last_dir(path, string)
   56 char *path;                     /* the path name to be parsed */
   57 char string[NAME_MAX];          /* the final component is returned here */
   58 {
   59 /* Given a path, 'path', located in the fs address space, parse it as
   60  * far as the last directory, fetch the inode for the last directory into
   61  * the inode table, and return a pointer to the inode.  In
   62  * addition, return the final component of the path in 'string'.
   63  * If the last directory can't be opened, return NIL_INODE and
   64  * the reason for failure in 'err_code'.
   65  */
   66 
   67   register struct inode *rip;
   68   register char *new_name;
   69   register struct inode *new_ip;
   70 
   71   /* Is the path absolute or relative?  Initialize 'rip' accordingly. */
   72   rip = (*path == '/' ? fp->fp_rootdir : fp->fp_workdir);
   73 
   74   /* If dir has been removed or path is empty, return ENOENT. */
   75   if (rip->i_nlinks == 0 || *path == '\0') {
   76         err_code = ENOENT;
   77         return(NIL_INODE);
   78   }
   79 
   80   dup_inode(rip);               /* inode will be returned with put_inode */
   81 
   82   /* Scan the path component by component. */
   83   while (TRUE) {
   84         /* Extract one component. */
   85         if ( (new_name = get_name(path, string)) == (char*) 0) {
   86                 put_inode(rip); /* bad path in user space */
   87                 return(NIL_INODE);
   88         }
   89         if (*new_name == '\0') {
   90                 if ( (rip->i_mode & I_TYPE) == I_DIRECTORY) {
   91                         return(rip);    /* normal exit */
   92                 } else {
   93                         /* last file of path prefix is not a directory */
   94                         put_inode(rip);
   95                         err_code = ENOTDIR;                     
   96                         return(NIL_INODE);
   97                 }
   98         }
   99 
  100         /* There is more path.  Keep parsing. */
  101         new_ip = advance(rip, string);
  102         put_inode(rip);         /* rip either obsolete or irrelevant */
  103         if (new_ip == NIL_INODE) return(NIL_INODE);
  104 
  105         /* The call to advance() succeeded.  Fetch next component. */
  106         path = new_name;
  107         rip = new_ip;
  108   }
  109 }
  110 
  111 /*===========================================================================*
  112  *                              get_name                                     *
  113  *===========================================================================*/
  114 PRIVATE char *get_name(old_name, string)
  115 char *old_name;                 /* path name to parse */
  116 char string[NAME_MAX];          /* component extracted from 'old_name' */
  117 {
  118 /* Given a pointer to a path name in fs space, 'old_name', copy the next
  119  * component to 'string' and pad with zeros.  A pointer to that part of
  120  * the name as yet unparsed is returned.  Roughly speaking,
  121  * 'get_name' = 'old_name' - 'string'.
  122  *
  123  * This routine follows the standard convention that /usr/ast, /usr//ast,
  124  * //usr///ast and /usr/ast/ are all equivalent.
  125  */
  126 
  127   register int c;
  128   register char *np, *rnp;
  129 
  130   np = string;                  /* 'np' points to current position */
  131   rnp = old_name;               /* 'rnp' points to unparsed string */
  132   while ( (c = *rnp) == '/') rnp++;     /* skip leading slashes */
  133 
  134   /* Copy the unparsed path, 'old_name', to the array, 'string'. */
  135   while ( rnp < &old_name[PATH_MAX]  &&  c != '/'   &&  c != '\0') {
  136         if (np < &string[NAME_MAX]) *np++ = c;
  137         c = *++rnp;             /* advance to next character */
  138   }
  139 
  140   /* To make /usr/ast/ equivalent to /usr/ast, skip trailing slashes. */
  141   while (c == '/' && rnp < &old_name[PATH_MAX]) c = *++rnp;
  142 
  143   if (np < &string[NAME_MAX]) *np = '\0';       /* Terminate string */
  144 
  145   if (rnp >= &old_name[PATH_MAX]) {
  146         err_code = ENAMETOOLONG;
  147         return((char *) 0);
  148   }
  149   return(rnp);
  150 }
  151 
  152 /*===========================================================================*
  153  *                              advance                                      *
  154  *===========================================================================*/
  155 PUBLIC struct inode *advance(dirp, string)
  156 struct inode *dirp;             /* inode for directory to be searched */
  157 char string[NAME_MAX];          /* component name to look for */
  158 {
  159 /* Given a directory and a component of a path, look up the component in
  160  * the directory, find the inode, open it, and return a pointer to its inode
  161  * slot.  If it can't be done, return NIL_INODE.
  162  */
  163 
  164   register struct inode *rip;
  165   struct inode *rip2;
  166   register struct super_block *sp;
  167   int r, inumb;
  168   dev_t mnt_dev;
  169   ino_t numb;
  170 
  171   /* If 'string' is empty, yield same inode straight away. */
  172   if (string[0] == '\0') { return(get_inode(dirp->i_dev, (int) dirp->i_num)); }
  173 
  174   /* Check for NIL_INODE. */
  175   if (dirp == NIL_INODE) { return(NIL_INODE); }
  176 
  177   /* If 'string' is not present in the directory, signal error. */
  178   if ( (r = search_dir(dirp, string, &numb, LOOK_UP)) != OK) {
  179         err_code = r;
  180         return(NIL_INODE);
  181   }
  182 
  183   /* Don't go beyond the current root directory, unless the string is dot2. */
  184   if (dirp == fp->fp_rootdir && strcmp(string, "..") == 0 && string != dot2)
  185                 return(get_inode(dirp->i_dev, (int) dirp->i_num));
  186 
  187   /* The component has been found in the directory.  Get inode. */
  188   if ( (rip = get_inode(dirp->i_dev, (int) numb)) == NIL_INODE)  {
  189         return(NIL_INODE);
  190         }
  191 
  192   if (rip->i_num == ROOT_INODE)
  193         if (dirp->i_num == ROOT_INODE) {
  194             if (string[1] == '.') {
  195                 for (sp = &super_block[1]; sp < &super_block[NR_SUPERS]; sp++){
  196                         if (sp->s_dev == rip->i_dev) {
  197                                 /* Release the root inode.  Replace by the
  198                                  * inode mounted on.
  199                                  */
  200                                 put_inode(rip);
  201                                 mnt_dev = sp->s_imount->i_dev;
  202                                 inumb = (int) sp->s_imount->i_num;
  203                                 rip2 = get_inode(mnt_dev, inumb);
  204                                 rip = advance(rip2, string);
  205                                 put_inode(rip2);
  206                                 break;
  207                         }
  208                 }
  209             }
  210         }
  211   if (rip == NIL_INODE) return(NIL_INODE);
  212 
  213   /* See if the inode is mounted on.  If so, switch to root directory of the
  214    * mounted file system.  The super_block provides the linkage between the
  215    * inode mounted on and the root directory of the mounted file system.
  216    */
  217   while (rip != NIL_INODE && rip->i_mount == I_MOUNT) {
  218         /* The inode is indeed mounted on. */
  219         for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++) {
  220                 if (sp->s_imount == rip) {
  221                         /* Release the inode mounted on.  Replace by the
  222                          * inode of the root inode of the mounted device.
  223                          */
  224                         put_inode(rip);
  225                         rip = get_inode(sp->s_dev, ROOT_INODE);
  226                         break;
  227                 }
  228         }
  229   }
  230   return(rip);          /* return pointer to inode's component */
  231 }
  232 
  233 /*===========================================================================*
  234  *                              search_dir                                   *
  235  *===========================================================================*/
  236 PUBLIC int search_dir(ldir_ptr, string, numb, flag)
  237 register struct inode *ldir_ptr; /* ptr to inode for dir to search */
  238 char string[NAME_MAX];           /* component to search for */
  239 ino_t *numb;                     /* pointer to inode number */
  240 int flag;                        /* LOOK_UP, ENTER, DELETE or IS_EMPTY */
  241 {
  242 /* This function searches the directory whose inode is pointed to by 'ldip':
  243  * if (flag == ENTER)  enter 'string' in the directory with inode # '*numb';
  244  * if (flag == DELETE) delete 'string' from the directory;
  245  * if (flag == LOOK_UP) search for 'string' and return inode # in 'numb';
  246  * if (flag == IS_EMPTY) return OK if only . and .. in dir else ENOTEMPTY;
  247  *
  248  *    if 'string' is dot1 or dot2, no access permissions are checked.
  249  */
  250 
  251   register struct direct *dp = NULL;
  252   register struct buf *bp = NULL;
  253   int i, r, e_hit, t, match;
  254   mode_t bits;
  255   off_t pos;
  256   unsigned new_slots, old_slots;
  257   block_t b;
  258   struct super_block *sp;
  259   int extended = 0;
  260 
  261   /* If 'ldir_ptr' is not a pointer to a dir inode, error. */
  262   if ( (ldir_ptr->i_mode & I_TYPE) != I_DIRECTORY) return(ENOTDIR);
  263 
  264   r = OK;
  265 
  266   if (flag != IS_EMPTY) {
  267         bits = (flag == LOOK_UP ? X_BIT : W_BIT | X_BIT);
  268 
  269         if (string == dot1 || string == dot2) {
  270                 if (flag != LOOK_UP) r = read_only(ldir_ptr);
  271                                      /* only a writable device is required. */
  272         }
  273         else r = forbidden(ldir_ptr, bits); /* check access permissions */
  274   }
  275   if (r != OK) return(r);
  276   
  277   /* Step through the directory one block at a time. */
  278   old_slots = (unsigned) (ldir_ptr->i_size/DIR_ENTRY_SIZE);
  279   new_slots = 0;
  280   e_hit = FALSE;
  281   match = 0;                    /* set when a string match occurs */
  282 
  283   for (pos = 0; pos < ldir_ptr->i_size; pos += ldir_ptr->i_sp->s_block_size) {
  284         b = read_map(ldir_ptr, pos);    /* get block number */
  285 
  286         /* Since directories don't have holes, 'b' cannot be NO_BLOCK. */
  287         bp = get_block(ldir_ptr->i_dev, b, NORMAL);     /* get a dir block */
  288 
  289         if (bp == NO_BLOCK)
  290                 panic(__FILE__,"get_block returned NO_BLOCK", NO_NUM);
  291 
  292         /* Search a directory block. */
  293         for (dp = &bp->b_dir[0];
  294                 dp < &bp->b_dir[NR_DIR_ENTRIES(ldir_ptr->i_sp->s_block_size)];
  295                 dp++) {
  296                 if (++new_slots > old_slots) { /* not found, but room left */
  297                         if (flag == ENTER) e_hit = TRUE;
  298                         break;
  299                 }
  300 
  301                 /* Match occurs if string found. */
  302                 if (flag != ENTER && dp->d_ino != 0) {
  303                         if (flag == IS_EMPTY) {
  304                                 /* If this test succeeds, dir is not empty. */
  305                                 if (strcmp(dp->d_name, "." ) != 0 &&
  306                                     strcmp(dp->d_name, "..") != 0) match = 1;
  307                         } else {
  308                                 if (strncmp(dp->d_name, string, NAME_MAX) == 0) {
  309                                         match = 1;
  310                                 }
  311                         }
  312                 }
  313 
  314                 if (match) {
  315                         /* LOOK_UP or DELETE found what it wanted. */
  316                         r = OK;
  317                         if (flag == IS_EMPTY) r = ENOTEMPTY;
  318                         else if (flag == DELETE) {
  319                                 /* Save d_ino for recovery. */
  320                                 t = NAME_MAX - sizeof(ino_t);
  321                                 *((ino_t *) &dp->d_name[t]) = dp->d_ino;
  322                                 dp->d_ino = 0;  /* erase entry */
  323                                 bp->b_dirt = DIRTY;
  324                                 ldir_ptr->i_update |= CTIME | MTIME;
  325                                 ldir_ptr->i_dirt = DIRTY;
  326                         } else {
  327                                 sp = ldir_ptr->i_sp;    /* 'flag' is LOOK_UP */
  328                                 *numb = conv4(sp->s_native, (int) dp->d_ino);
  329                         }
  330                         put_block(bp, DIRECTORY_BLOCK);
  331                         return(r);
  332                 }
  333 
  334                 /* Check for free slot for the benefit of ENTER. */
  335                 if (flag == ENTER && dp->d_ino == 0) {
  336                         e_hit = TRUE;   /* we found a free slot */
  337                         break;
  338                 }
  339         }
  340 
  341         /* The whole block has been searched or ENTER has a free slot. */
  342         if (e_hit) break;       /* e_hit set if ENTER can be performed now */
  343         put_block(bp, DIRECTORY_BLOCK); /* otherwise, continue searching dir */
  344   }
  345 
  346   /* The whole directory has now been searched. */
  347   if (flag != ENTER) {
  348         return(flag == IS_EMPTY ? OK : ENOENT);
  349   }
  350 
  351   /* This call is for ENTER.  If no free slot has been found so far, try to
  352    * extend directory.
  353    */
  354   if (e_hit == FALSE) { /* directory is full and no room left in last block */
  355         new_slots++;            /* increase directory size by 1 entry */
  356         if (new_slots == 0) return(EFBIG); /* dir size limited by slot count */
  357         if ( (bp = new_block(ldir_ptr, ldir_ptr->i_size)) == NIL_BUF)
  358                 return(err_code);
  359         dp = &bp->b_dir[0];
  360         extended = 1;
  361   }
  362 
  363   /* 'bp' now points to a directory block with space. 'dp' points to slot. */
  364   (void) memset(dp->d_name, 0, (size_t) NAME_MAX); /* clear entry */
  365   for (i = 0; string[i] && i < NAME_MAX; i++) dp->d_name[i] = string[i];
  366   sp = ldir_ptr->i_sp; 
  367   dp->d_ino = conv4(sp->s_native, (int) *numb);
  368   bp->b_dirt = DIRTY;
  369   put_block(bp, DIRECTORY_BLOCK);
  370   ldir_ptr->i_update |= CTIME | MTIME;  /* mark mtime for update later */
  371   ldir_ptr->i_dirt = DIRTY;
  372   if (new_slots > old_slots) {
  373         ldir_ptr->i_size = (off_t) new_slots * DIR_ENTRY_SIZE;
  374         /* Send the change to disk if the directory is extended. */
  375         if (extended) rw_inode(ldir_ptr, WRITING);
  376   }
  377   return(OK);
  378 }

Cache object: c6485e7e052aa420b9ae816153e4da9b


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.