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/link.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 handles the LINK and UNLINK system calls.  It also deals with
    2  * deallocating the storage used by a file when the last UNLINK is done to a
    3  * file and the blocks must be returned to the free block pool.
    4  *
    5  * The entry points into this file are
    6  *   do_link:   perform the LINK system call
    7  *   do_unlink: perform the UNLINK and RMDIR system calls
    8  *   do_rename: perform the RENAME system call
    9  *   truncate:  release all the blocks associated with an inode
   10  */
   11 
   12 #include "fs.h"
   13 #include <sys/stat.h>
   14 #include <string.h>
   15 #include <minix/com.h>
   16 #include <minix/callnr.h>
   17 #include "buf.h"
   18 #include "file.h"
   19 #include "fproc.h"
   20 #include "inode.h"
   21 #include "param.h"
   22 #include "super.h"
   23 
   24 #define SAME 1000
   25 
   26 FORWARD _PROTOTYPE( int remove_dir, (struct inode *rldirp, struct inode *rip,
   27                         char dir_name[NAME_MAX])                        );
   28 
   29 FORWARD _PROTOTYPE( int unlink_file, (struct inode *dirp, struct inode *rip,
   30                         char file_name[NAME_MAX])                       );
   31 
   32 /*===========================================================================*
   33  *                              do_link                                      *
   34  *===========================================================================*/
   35 PUBLIC int do_link()
   36 {
   37 /* Perform the link(name1, name2) system call. */
   38 
   39   register struct inode *ip, *rip;
   40   register int r;
   41   char string[NAME_MAX];
   42   struct inode *new_ip;
   43 
   44   /* See if 'name' (file to be linked) exists. */
   45   if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) return(err_code);
   46   if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code);
   47 
   48   /* Check to see if the file has maximum number of links already. */
   49   r = OK;
   50   if (rip->i_nlinks >= (rip->i_sp->s_version == V1 ? CHAR_MAX : SHRT_MAX))
   51         r = EMLINK;
   52 
   53   /* Only super_user may link to directories. */
   54   if (r == OK)
   55         if ( (rip->i_mode & I_TYPE) == I_DIRECTORY && !super_user) r = EPERM;
   56 
   57   /* If error with 'name', return the inode. */
   58   if (r != OK) {
   59         put_inode(rip);
   60         return(r);
   61   }
   62 
   63   /* Does the final directory of 'name2' exist? */
   64   if (fetch_name(m_in.name2, m_in.name2_length, M1) != OK) {
   65         put_inode(rip);
   66         return(err_code);
   67   }
   68   if ( (ip = last_dir(user_path, string)) == NIL_INODE) r = err_code;
   69 
   70   /* If 'name2' exists in full (even if no space) set 'r' to error. */
   71   if (r == OK) {
   72         if ( (new_ip = advance(ip, string)) == NIL_INODE) {
   73                 r = err_code;
   74                 if (r == ENOENT) r = OK;
   75         } else {
   76                 put_inode(new_ip);
   77                 r = EEXIST;
   78         }
   79   }
   80 
   81   /* Check for links across devices. */
   82   if (r == OK)
   83         if (rip->i_dev != ip->i_dev) r = EXDEV;
   84 
   85   /* Try to link. */
   86   if (r == OK)
   87         r = search_dir(ip, string, &rip->i_num, ENTER);
   88 
   89   /* If success, register the linking. */
   90   if (r == OK) {
   91         rip->i_nlinks++;
   92         rip->i_update |= CTIME;
   93         rip->i_dirt = DIRTY;
   94   }
   95 
   96   /* Done.  Release both inodes. */
   97   put_inode(rip);
   98   put_inode(ip);
   99   return(r);
  100 }
  101 
  102 /*===========================================================================*
  103  *                              do_unlink                                    *
  104  *===========================================================================*/
  105 PUBLIC int do_unlink()
  106 {
  107 /* Perform the unlink(name) or rmdir(name) system call. The code for these two
  108  * is almost the same.  They differ only in some condition testing.  Unlink()
  109  * may be used by the superuser to do dangerous things; rmdir() may not.
  110  */
  111 
  112   register struct inode *rip;
  113   struct inode *rldirp;
  114   int r;
  115   char string[NAME_MAX];
  116 
  117   /* Get the last directory in the path. */
  118   if (fetch_name(m_in.name, m_in.name_length, M3) != OK) return(err_code);
  119   if ( (rldirp = last_dir(user_path, string)) == NIL_INODE)
  120         return(err_code);
  121 
  122   /* The last directory exists.  Does the file also exist? */
  123   r = OK;
  124   if ( (rip = advance(rldirp, string)) == NIL_INODE) r = err_code;
  125 
  126   /* If error, return inode. */
  127   if (r != OK) {
  128         put_inode(rldirp);
  129         return(r);
  130   }
  131 
  132   /* Do not remove a mount point. */
  133   if (rip->i_num == ROOT_INODE) {
  134         put_inode(rldirp);
  135         put_inode(rip);
  136         return(EBUSY);
  137   }
  138 
  139   /* Now test if the call is allowed, separately for unlink() and rmdir(). */
  140   if (call_nr == UNLINK) {
  141         /* Only the su may unlink directories, but the su can unlink any dir.*/
  142         if ( (rip->i_mode & I_TYPE) == I_DIRECTORY && !super_user) r = EPERM;
  143 
  144         /* Don't unlink a file if it is the root of a mounted file system. */
  145         if (rip->i_num == ROOT_INODE) r = EBUSY;
  146 
  147         /* Actually try to unlink the file; fails if parent is mode 0 etc. */
  148         if (r == OK) r = unlink_file(rldirp, rip, string);
  149 
  150   } else {
  151         r = remove_dir(rldirp, rip, string); /* call is RMDIR */
  152   }
  153 
  154   /* If unlink was possible, it has been done, otherwise it has not. */
  155   put_inode(rip);
  156   put_inode(rldirp);
  157   return(r);
  158 }
  159 
  160 /*===========================================================================*
  161  *                              do_rename                                    *
  162  *===========================================================================*/
  163 PUBLIC int do_rename()
  164 {
  165 /* Perform the rename(name1, name2) system call. */
  166 
  167   struct inode *old_dirp, *old_ip;      /* ptrs to old dir, file inodes */
  168   struct inode *new_dirp, *new_ip;      /* ptrs to new dir, file inodes */
  169   struct inode *new_superdirp, *next_new_superdirp;
  170   int r = OK;                           /* error flag; initially no error */
  171   int odir, ndir;                       /* TRUE iff {old|new} file is dir */
  172   int same_pdir;                        /* TRUE iff parent dirs are the same */
  173   char old_name[NAME_MAX], new_name[NAME_MAX];
  174   ino_t numb;
  175   int r1;
  176   
  177   /* See if 'name1' (existing file) exists.  Get dir and file inodes. */
  178   if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) return(err_code);
  179   if ( (old_dirp = last_dir(user_path, old_name))==NIL_INODE) return(err_code);
  180 
  181   if ( (old_ip = advance(old_dirp, old_name)) == NIL_INODE) r = err_code;
  182 
  183   /* See if 'name2' (new name) exists.  Get dir and file inodes. */
  184   if (fetch_name(m_in.name2, m_in.name2_length, M1) != OK) r = err_code;
  185   if ( (new_dirp = last_dir(user_path, new_name)) == NIL_INODE) r = err_code;
  186   new_ip = advance(new_dirp, new_name); /* not required to exist */
  187 
  188   if (old_ip != NIL_INODE)
  189         odir = ((old_ip->i_mode & I_TYPE) == I_DIRECTORY);  /* TRUE iff dir */
  190 
  191   /* If it is ok, check for a variety of possible errors. */
  192   if (r == OK) {
  193         same_pdir = (old_dirp == new_dirp);
  194 
  195         /* The old inode must not be a superdirectory of the new last dir. */
  196         if (odir && !same_pdir) {
  197                 dup_inode(new_superdirp = new_dirp);
  198                 while (TRUE) {          /* may hang in a file system loop */
  199                         if (new_superdirp == old_ip) {
  200                                 r = EINVAL;
  201                                 break;
  202                         }
  203                         next_new_superdirp = advance(new_superdirp, dot2);
  204                         put_inode(new_superdirp);
  205                         if (next_new_superdirp == new_superdirp)
  206                                 break;  /* back at system root directory */
  207                         new_superdirp = next_new_superdirp;
  208                         if (new_superdirp == NIL_INODE) {
  209                                 /* Missing ".." entry.  Assume the worst. */
  210                                 r = EINVAL;
  211                                 break;
  212                         }
  213                 }       
  214                 put_inode(new_superdirp);
  215         }       
  216 
  217         /* The old or new name must not be . or .. */
  218         if (strcmp(old_name, ".")==0 || strcmp(old_name, "..")==0 ||
  219             strcmp(new_name, ".")==0 || strcmp(new_name, "..")==0) r = EINVAL;
  220 
  221         /* Both parent directories must be on the same device. */
  222         if (old_dirp->i_dev != new_dirp->i_dev) r = EXDEV;
  223 
  224         /* Parent dirs must be writable, searchable and on a writable device */
  225         if ((r1 = forbidden(old_dirp, W_BIT | X_BIT)) != OK ||
  226             (r1 = forbidden(new_dirp, W_BIT | X_BIT)) != OK) r = r1;
  227 
  228         /* Some tests apply only if the new path exists. */
  229         if (new_ip == NIL_INODE) {
  230                 /* don't rename a file with a file system mounted on it. */
  231                 if (old_ip->i_dev != old_dirp->i_dev) r = EXDEV;
  232                 if (odir && new_dirp->i_nlinks >=
  233                     (new_dirp->i_sp->s_version == V1 ? CHAR_MAX : SHRT_MAX) &&
  234                     !same_pdir && r == OK) r = EMLINK;
  235         } else {
  236                 if (old_ip == new_ip) r = SAME; /* old=new */
  237 
  238                 /* has the old file or new file a file system mounted on it? */
  239                 if (old_ip->i_dev != new_ip->i_dev) r = EXDEV;
  240 
  241                 ndir = ((new_ip->i_mode & I_TYPE) == I_DIRECTORY); /* dir ? */
  242                 if (odir == TRUE && ndir == FALSE) r = ENOTDIR;
  243                 if (odir == FALSE && ndir == TRUE) r = EISDIR;
  244         }
  245   }
  246 
  247   /* If a process has another root directory than the system root, we might
  248    * "accidently" be moving it's working directory to a place where it's
  249    * root directory isn't a super directory of it anymore. This can make
  250    * the function chroot useless. If chroot will be used often we should
  251    * probably check for it here.
  252    */
  253 
  254   /* The rename will probably work. Only two things can go wrong now:
  255    * 1. being unable to remove the new file. (when new file already exists)
  256    * 2. being unable to make the new directory entry. (new file doesn't exists)
  257    *     [directory has to grow by one block and cannot because the disk
  258    *      is completely full].
  259    */
  260   if (r == OK) {
  261         if (new_ip != NIL_INODE) {
  262                   /* There is already an entry for 'new'. Try to remove it. */
  263                 if (odir) 
  264                         r = remove_dir(new_dirp, new_ip, new_name);
  265                 else 
  266                         r = unlink_file(new_dirp, new_ip, new_name);
  267         }
  268         /* if r is OK, the rename will succeed, while there is now an
  269          * unused entry in the new parent directory.
  270          */
  271   }
  272 
  273   if (r == OK) {
  274         /* If the new name will be in the same parent directory as the old one,
  275          * first remove the old name to free an entry for the new name,
  276          * otherwise first try to create the new name entry to make sure
  277          * the rename will succeed.
  278          */
  279         numb = old_ip->i_num;           /* inode number of old file */
  280 
  281         if (same_pdir) {
  282                 r = search_dir(old_dirp, old_name, (ino_t *) 0, DELETE);
  283                                                 /* shouldn't go wrong. */
  284                 if (r==OK) (void) search_dir(old_dirp, new_name, &numb, ENTER);
  285         } else {
  286                 r = search_dir(new_dirp, new_name, &numb, ENTER);
  287                 if (r == OK)
  288                     (void) search_dir(old_dirp, old_name, (ino_t *) 0, DELETE);
  289         }
  290   }
  291   /* If r is OK, the ctime and mtime of old_dirp and new_dirp have been marked
  292    * for update in search_dir.
  293    */
  294 
  295   if (r == OK && odir && !same_pdir) {
  296         /* Update the .. entry in the directory (still points to old_dirp). */
  297         numb = new_dirp->i_num;
  298         (void) unlink_file(old_ip, NIL_INODE, dot2);
  299         if (search_dir(old_ip, dot2, &numb, ENTER) == OK) {
  300                 /* New link created. */
  301                 new_dirp->i_nlinks++;
  302                 new_dirp->i_dirt = DIRTY;
  303         }
  304   }
  305         
  306   /* Release the inodes. */
  307   put_inode(old_dirp);
  308   put_inode(old_ip);
  309   put_inode(new_dirp);
  310   put_inode(new_ip);
  311   return(r == SAME ? OK : r);
  312 }
  313 
  314 /*===========================================================================*
  315  *                              truncate                                     *
  316  *===========================================================================*/
  317 PUBLIC void truncate(rip)
  318 register struct inode *rip;     /* pointer to inode to be truncated */
  319 {
  320 /* Remove all the zones from the inode 'rip' and mark it dirty. */
  321 
  322   register block_t b;
  323   zone_t z, zone_size, z1;
  324   off_t position;
  325   int i, scale, file_type, waspipe, single, nr_indirects;
  326   struct buf *bp;
  327   dev_t dev;
  328 
  329   file_type = rip->i_mode & I_TYPE;     /* check to see if file is special */
  330   if (file_type == I_CHAR_SPECIAL || file_type == I_BLOCK_SPECIAL) return;
  331   dev = rip->i_dev;             /* device on which inode resides */
  332   scale = rip->i_sp->s_log_zone_size;
  333   zone_size = (zone_t) rip->i_sp->s_block_size << scale;
  334   nr_indirects = rip->i_nindirs;
  335 
  336   /* Pipes can shrink, so adjust size to make sure all zones are removed. */
  337   waspipe = rip->i_pipe == I_PIPE;      /* TRUE is this was a pipe */
  338   if (waspipe) rip->i_size = PIPE_SIZE(rip->i_sp->s_block_size);
  339 
  340   /* Step through the file a zone at a time, finding and freeing the zones. */
  341   for (position = 0; position < rip->i_size; position += zone_size) {
  342         if ( (b = read_map(rip, position)) != NO_BLOCK) {
  343                 z = (zone_t) b >> scale;
  344                 free_zone(dev, z);
  345         }
  346   }
  347 
  348   /* All the data zones have been freed.  Now free the indirect zones. */
  349   rip->i_dirt = DIRTY;
  350   if (waspipe) {
  351         wipe_inode(rip);        /* clear out inode for pipes */
  352         return;                 /* indirect slots contain file positions */
  353   }
  354   single = rip->i_ndzones;
  355   free_zone(dev, rip->i_zone[single]);  /* single indirect zone */
  356   if ( (z = rip->i_zone[single+1]) != NO_ZONE) {
  357         /* Free all the single indirect zones pointed to by the double. */
  358         b = (block_t) z << scale;
  359         bp = get_block(dev, b, NORMAL); /* get double indirect zone */
  360         for (i = 0; i < nr_indirects; i++) {
  361                 z1 = rd_indir(bp, i);
  362                 free_zone(dev, z1);
  363         }
  364 
  365         /* Now free the double indirect zone itself. */
  366         put_block(bp, INDIRECT_BLOCK);
  367         free_zone(dev, z);
  368   }
  369 
  370   /* Leave zone numbers for de(1) to recover file after an unlink(2).  */
  371 }
  372 
  373 /*===========================================================================*
  374  *                              remove_dir                                   *
  375  *===========================================================================*/
  376 PRIVATE int remove_dir(rldirp, rip, dir_name)
  377 struct inode *rldirp;                   /* parent directory */
  378 struct inode *rip;                      /* directory to be removed */
  379 char dir_name[NAME_MAX];                /* name of directory to be removed */
  380 {
  381   /* A directory file has to be removed. Five conditions have to met:
  382    *    - The file must be a directory
  383    *    - The directory must be empty (except for . and ..)
  384    *    - The final component of the path must not be . or ..
  385    *    - The directory must not be the root of a mounted file system
  386    *    - The directory must not be anybody's root/working directory
  387    */
  388 
  389   int r;
  390   register struct fproc *rfp;
  391 
  392   /* search_dir checks that rip is a directory too. */
  393   if ((r = search_dir(rip, "", (ino_t *) 0, IS_EMPTY)) != OK) return r;
  394 
  395   if (strcmp(dir_name, ".") == 0 || strcmp(dir_name, "..") == 0)return(EINVAL);
  396   if (rip->i_num == ROOT_INODE) return(EBUSY); /* can't remove 'root' */
  397   
  398   for (rfp = &fproc[INIT_PROC_NR + 1]; rfp < &fproc[NR_PROCS]; rfp++)
  399         if (rfp->fp_workdir == rip || rfp->fp_rootdir == rip) return(EBUSY);
  400                                 /* can't remove anybody's working dir */
  401 
  402   /* Actually try to unlink the file; fails if parent is mode 0 etc. */
  403   if ((r = unlink_file(rldirp, rip, dir_name)) != OK) return r;
  404 
  405   /* Unlink . and .. from the dir. The super user can link and unlink any dir,
  406    * so don't make too many assumptions about them.
  407    */
  408   (void) unlink_file(rip, NIL_INODE, dot1);
  409   (void) unlink_file(rip, NIL_INODE, dot2);
  410   return(OK);
  411 }
  412 
  413 /*===========================================================================*
  414  *                              unlink_file                                  *
  415  *===========================================================================*/
  416 PRIVATE int unlink_file(dirp, rip, file_name)
  417 struct inode *dirp;             /* parent directory of file */
  418 struct inode *rip;              /* inode of file, may be NIL_INODE too. */
  419 char file_name[NAME_MAX];       /* name of file to be removed */
  420 {
  421 /* Unlink 'file_name'; rip must be the inode of 'file_name' or NIL_INODE. */
  422 
  423   ino_t numb;                   /* inode number */
  424   int   r;
  425 
  426   /* If rip is not NIL_INODE, it is used to get faster access to the inode. */
  427   if (rip == NIL_INODE) {
  428         /* Search for file in directory and try to get its inode. */
  429         err_code = search_dir(dirp, file_name, &numb, LOOK_UP);
  430         if (err_code == OK) rip = get_inode(dirp->i_dev, (int) numb);
  431         if (err_code != OK || rip == NIL_INODE) return(err_code);
  432   } else {
  433         dup_inode(rip);         /* inode will be returned with put_inode */
  434   }
  435 
  436   r = search_dir(dirp, file_name, (ino_t *) 0, DELETE);
  437 
  438   if (r == OK) {
  439         rip->i_nlinks--;        /* entry deleted from parent's dir */
  440         rip->i_update |= CTIME;
  441         rip->i_dirt = DIRTY;
  442   }
  443 
  444   put_inode(rip);
  445   return(r);
  446 }

Cache object: b8918085f67e10d54b44a63ec67bf90f


[ 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.