root/maint/gnulib/lib/mkancesdirs.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mkancesdirs

   1 /* Make a file's ancestor directories.
   2 
   3    Copyright (C) 2006, 2009-2021 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 <https://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 "filename.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, -1 (setting errno) otherwise.  If
  46    COMPONENT is relative, it is relative to the temporary working
  47    directory, which may differ from *WD.
  48 
  49    Ordinarily MAKE_DIR is executed with the working directory changed
  50    to reflect the already-made prefix, and mkancesdirs returns with
  51    the working directory changed a prefix of FILE.  However, if the
  52    initial working directory cannot be saved in a file descriptor,
  53    MAKE_DIR is invoked in a subprocess and this function returns in
  54    both the parent and child process, so the caller should not assume
  55    any changed state survives other than the EXITMAX component of WD,
  56    and the caller should take care that the parent does not attempt to
  57    do the work that the child is doing.
  58 
  59    If successful and if this process can go ahead and create FILE,
  60    return the length of the prefix of FILE that has already been made.
  61    If successful so far but a child process is doing the actual work,
  62    return -2.  If unsuccessful, return -1 and set errno.  */
  63 
  64 ptrdiff_t
  65 mkancesdirs (char *file, struct savewd *wd,
     /* [previous][next][first][last][top][bottom][index][help] */
  66              int (*make_dir) (char const *, char const *, void *),
  67              void *make_dir_arg)
  68 {
  69   /* Address of the previous directory separator that follows an
  70      ordinary byte in a file name in the left-to-right scan, or NULL
  71      if no such separator precedes the current location P.  */
  72   char *sep = NULL;
  73 
  74   /* Address of the leftmost file name component that has not yet
  75      been processed.  */
  76   char *component = file;
  77 
  78   char *p = file + FILE_SYSTEM_PREFIX_LEN (file);
  79   char c;
  80   bool made_dir = false;
  81 
  82   /* Scan forward through FILE, creating and chdiring into directories
  83      along the way.  Try MAKE_DIR before chdir, so that the procedure
  84      works even when two or more processes are executing it in
  85      parallel.  Isolate each file name component by having COMPONENT
  86      point to its start and SEP point just after its end.  */
  87 
  88   while ((c = *p++))
  89     if (ISSLASH (*p))
  90       {
  91         if (! ISSLASH (c))
  92           sep = p;
  93       }
  94     else if (ISSLASH (c) && *p && sep)
  95       {
  96         /* Don't bother to make or test for "." since it does not
  97            affect the algorithm.  */
  98         if (! (sep - component == 1 && component[0] == '.'))
  99           {
 100             int make_dir_errno = 0;
 101             int savewd_chdir_options = 0;
 102             int chdir_result;
 103 
 104             /* Temporarily modify FILE to isolate this file name
 105                component.  */
 106             *sep = '\0';
 107 
 108             /* Invoke MAKE_DIR on this component, except don't bother
 109                with ".." since it must exist if its "parent" does.  */
 110             if (sep - component == 2
 111                 && component[0] == '.' && component[1] == '.')
 112               made_dir = false;
 113             else if (make_dir (file, component, make_dir_arg) < 0)
 114               make_dir_errno = errno;
 115             else
 116               made_dir = true;
 117 
 118             if (made_dir)
 119               savewd_chdir_options |= SAVEWD_CHDIR_NOFOLLOW;
 120 
 121             chdir_result =
 122               savewd_chdir (wd, component, savewd_chdir_options, NULL);
 123 
 124             /* Undo the temporary modification to FILE, unless there
 125                was a failure.  */
 126             if (chdir_result != -1)
 127               *sep = '/';
 128 
 129             if (chdir_result != 0)
 130               {
 131                 if (make_dir_errno != 0 && errno == ENOENT)
 132                   errno = make_dir_errno;
 133                 return chdir_result;
 134               }
 135           }
 136 
 137         component = p;
 138       }
 139 
 140   return component - file;
 141 }

/* [previous][next][first][last][top][bottom][index][help] */