LCOV - code coverage report
Current view: top level - lib - mkancesdirs.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 37 37 100.0 %
Date: 2018-01-30 Functions: 1 1 100.0 %

          Line data    Source code
       1             : /* Make a file's ancestor directories.
       2             : 
       3             :    Copyright (C) 2006 Free Software Foundation, Inc.
       4             : 
       5             :    This program is free software: you can redistribute it and/or modify
       6             :    it under the terms of the GNU General Public License as published by
       7             :    the Free Software Foundation; either version 3 of the License, or
       8             :    (at your option) any later version.
       9             : 
      10             :    This program is distributed in the hope that it will be useful,
      11             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :    GNU General Public License for more details.
      14             : 
      15             :    You should have received a copy of the GNU General Public License
      16             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
      17             : 
      18             : /* Written by Paul Eggert.  */
      19             : 
      20             : #include <config.h>
      21             : 
      22             : #include "mkancesdirs.h"
      23             : 
      24             : #include <sys/types.h>
      25             : #include <sys/stat.h>
      26             : #include <fcntl.h>
      27             : 
      28             : #include <errno.h>
      29             : #include <unistd.h>
      30             : 
      31             : #include "dirname.h"
      32             : #include "savewd.h"
      33             : 
      34             : /* Ensure that the ancestor directories of FILE exist, using an
      35             :    algorithm that should work even if two processes execute this
      36             :    function in parallel.  Modify FILE as necessary to access the
      37             :    ancestor directories, but restore FILE to an equivalent value
      38             :    if successful.
      39             : 
      40             :    WD points to the working directory, using the conventions of
      41             :    savewd.
      42             : 
      43             :    Create any ancestor directories that don't already exist, by
      44             :    invoking MAKE_DIR (FILE, COMPONENT, MAKE_DIR_ARG).  This function
      45             :    should return 0 if successful and the resulting directory is
      46             :    readable, 1 if successful but the resulting directory might not be
      47             :    readable, -1 (setting errno) otherwise.  If COMPONENT is relative,
      48             :    it is relative to the temporary working directory, which may differ
      49             :    from *WD.
      50             : 
      51             :    Ordinarily MAKE_DIR is executed with the working directory changed
      52             :    to reflect the already-made prefix, and mkancesdirs returns with
      53             :    the working directory changed a prefix of FILE.  However, if the
      54             :    initial working directory cannot be saved in a file descriptor,
      55             :    MAKE_DIR is invoked in a subprocess and this function returns in
      56             :    both the parent and child process, so the caller should not assume
      57             :    any changed state survives other than the EXITMAX component of WD,
      58             :    and the caller should take care that the parent does not attempt to
      59             :    do the work that the child is doing.
      60             : 
      61             :    If successful and if this process can go ahead and create FILE,
      62             :    return the length of the prefix of FILE that has already been made.
      63             :    If successful so far but a child process is doing the actual work,
      64             :    return -2.  If unsuccessful, return -1 and set errno.  */
      65             : 
      66             : ptrdiff_t
      67          77 : mkancesdirs (char *file, struct savewd *wd,
      68             :              int (*make_dir) (char const *, char const *, void *),
      69             :              void *make_dir_arg)
      70             : {
      71             :   /* Address of the previous directory separator that follows an
      72             :      ordinary byte in a file name in the left-to-right scan, or NULL
      73             :      if no such separator precedes the current location P.  */
      74          77 :   char *sep = NULL;
      75             : 
      76             :   /* Address of the leftmost file name component that has not yet
      77             :      been processed.  */
      78          77 :   char *component = file;
      79             : 
      80          77 :   char *p = file + FILE_SYSTEM_PREFIX_LEN (file);
      81             :   char c;
      82          77 :   bool made_dir = false;
      83             : 
      84             :   /* Scan forward through FILE, creating and chdiring into directories
      85             :      along the way.  Try MAKE_DIR before chdir, so that the procedure
      86             :      works even when two or more processes are executing it in
      87             :      parallel.  Isolate each file name component by having COMPONENT
      88             :      point to its start and SEP point just after its end.  */
      89             : 
      90         432 :   while ((c = *p++))
      91         291 :     if (ISSLASH (*p))
      92             :       {
      93         181 :         if (! ISSLASH (c))
      94          39 :           sep = p;
      95             :       }
      96         110 :     else if (ISSLASH (c) && *p && sep)
      97             :       {
      98             :         /* Don't bother to make or test for "." since it does not
      99             :            affect the algorithm.  */
     100          27 :         if (! (sep - component == 1 && component[0] == '.'))
     101             :           {
     102          18 :             int make_dir_errno = 0;
     103          18 :             int savewd_chdir_options = 0;
     104             :             int chdir_result;
     105             : 
     106             :             /* Temporarily modify FILE to isolate this file name
     107             :                component.  */
     108          18 :             *sep = '\0';
     109             : 
     110             :             /* Invoke MAKE_DIR on this component, except don't bother
     111             :                with ".." since it must exist if its "parent" does.  */
     112          18 :             if (sep - component == 2
     113           7 :                 && component[0] == '.' && component[1] == '.')
     114           3 :               made_dir = false;
     115             :             else
     116          15 :               switch (make_dir (file, component, make_dir_arg))
     117             :                 {
     118          14 :                 case -1:
     119          14 :                   make_dir_errno = errno;
     120          14 :                   break;
     121             : 
     122           1 :                 case 0:
     123           1 :                   savewd_chdir_options |= SAVEWD_CHDIR_READABLE;
     124             :                   /* Fall through.  */
     125           1 :                 case 1:
     126           1 :                   made_dir = true;
     127           1 :                   break;
     128             :                 }
     129             : 
     130          18 :             if (made_dir)
     131           1 :               savewd_chdir_options |= SAVEWD_CHDIR_NOFOLLOW;
     132             : 
     133          18 :             chdir_result =
     134             :               savewd_chdir (wd, component, savewd_chdir_options, NULL);
     135             : 
     136             :             /* Undo the temporary modification to FILE, unless there
     137             :                was a failure.  */
     138          18 :             if (chdir_result != -1)
     139           5 :               *sep = '/';
     140             : 
     141          18 :             if (chdir_result != 0)
     142             :               {
     143          13 :                 if (make_dir_errno != 0 && errno == ENOENT)
     144           8 :                   errno = make_dir_errno;
     145          13 :                 return chdir_result;
     146             :               }
     147             :           }
     148             : 
     149          14 :         component = p;
     150             :       }
     151             : 
     152          64 :   return component - file;
     153             : }

Generated by: LCOV version 1.10