LCOV - code coverage report
Current view: top level - snapwebsites - mkdir_p.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 33 81 40.7 %
Date: 2019-12-15 17:13:15 Functions: 4 5 80.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Snap Websites Server -- like shell `mkdir -p ...`
       2             : // Copyright (c) 2013-2019  Made to Order Software Corp.  All Rights Reserved
       3             : //
       4             : // This program is free software; you can redistribute it and/or modify
       5             : // it under the terms of the GNU General Public License as published by
       6             : // the Free Software Foundation; either version 2 of the License, or
       7             : // (at your option) any later version.
       8             : //
       9             : // This program is distributed in the hope that it will be useful,
      10             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             : // GNU General Public License for more details.
      13             : //
      14             : // You should have received a copy of the GNU General Public License
      15             : // along with this program; if not, write to the Free Software
      16             : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      17             : 
      18             : 
      19             : // self
      20             : //
      21             : #include "snapwebsites/mkdir_p.h"
      22             : 
      23             : 
      24             : // snapwebsites lib
      25             : //
      26             : #include "snapwebsites/chownnm.h"
      27             : #include "snapwebsites/log.h"
      28             : #include "snapwebsites/snap_exception.h"
      29             : #include "snapwebsites/snap_string_list.h"
      30             : 
      31             : 
      32             : // snapdev lib
      33             : //
      34             : #include <snapdev/not_reached.h>
      35             : #include <snapdev/not_used.h>
      36             : 
      37             : 
      38             : // C++ lib
      39             : //
      40             : #include <sstream>
      41             : 
      42             : 
      43             : // C lib
      44             : //
      45             : #include <sys/stat.h>
      46             : #include <sys/types.h>
      47             : 
      48             : 
      49             : // last include
      50             : //
      51             : #include <snapdev/poison.h>
      52             : 
      53             : 
      54             : 
      55             : namespace snap
      56             : {
      57             : 
      58             : 
      59             : /** \brief Create directory as with `mkdir -p ...`.
      60             :  *
      61             :  * This function creates all the directories so one can create a file
      62             :  * under the deepest directory specified in \p path.
      63             :  *
      64             :  * If \p path includes the filename, then make sure to set
      65             :  * \p include_filename parameter to true. That way this function
      66             :  * ignores that name.
      67             :  *
      68             :  * You make also pass the mode and owner/group details for the new
      69             :  * or existing directory. The function ignores these parameters if
      70             :  * set to their default (0 for mode and an empty string for owner
      71             :  * and group.)
      72             :  *
      73             :  * The default mode of 0755 is used when creating a directory if you
      74             :  * used 0 as the mode. In most cases, acceptable modes are:
      75             :  *
      76             :  * \li 0700
      77             :  * \li 0770
      78             :  * \li 0775
      79             :  * \li 0750
      80             :  * \li 0755
      81             :  *
      82             :  * Other modes are likely to not be useful for a directory.
      83             :  *
      84             :  * The owner and group parameters can be set to specific user and group
      85             :  * names. These names must existing in /etc/passwd and /etc/group. When
      86             :  * both are empty strings, chown() is not called.
      87             :  *
      88             :  * The function accepts paths with double slashes as if there was
      89             :  * just one (i.e. "/etc//snapwebsites" is viewed as "/etc/snapwebsites".)
      90             :  * This is the standard Unix behavior.
      91             :  *
      92             :  * The function returns -1 if one or more of the directories cannot
      93             :  * be created or adjusted according to the parameters. It also logs
      94             :  * a message to your log file specifying which directory could not
      95             :  * be created.
      96             :  *
      97             :  * If the function returns -1, then errno is set to the error returned
      98             :  * by the last mkdir(), chmod(), or chown() that failed.
      99             :  *
     100             :  * \note
     101             :  * The two main errors when this function fails are: (1) the directory
     102             :  * cannot be created because you do not have enough permissions; and
     103             :  * (2) the named directory exists in the form of a file.
     104             :  *
     105             :  * \bug
     106             :  * Many of the default directories that we need to have to run our
     107             :  * servers are to be created in directories that are owned by root.
     108             :  * This causes problems when attempting to run Snap! executables
     109             :  * as a developer.
     110             :  *
     111             :  * \param[in] path  The path to create.
     112             :  * \param[in] include_filename  If true, the \p path parameter also
     113             :  *            includes a filename.
     114             :  * \param[in] mode  The new directories mode if not zero.
     115             :  * \param[in] owner  The new directories owner.
     116             :  * \param[in] group  The new directories group.
     117             :  *
     118             :  * \return 0 if the directory exists at the time the function returns,
     119             :  *         -1 if an error occurs (i.e. permissions denied)
     120             :  */
     121           1 : int mkdir_p(QString const & path
     122             :           , bool include_filename
     123             :           , int mode
     124             :           , QString const & owner
     125             :           , QString const & group)
     126             : {
     127             :     // we skip empty parts since "//" is the same as "/" in a Unix path.
     128             :     //
     129           2 :     snap::snap_string_list segments(path.split('/', QString::SkipEmptyParts));
     130           1 :     if(segments.empty())
     131             :     {
     132           0 :         return 0;
     133             :     }
     134             : 
     135           1 :     if(include_filename)
     136             :     {
     137           0 :         segments.pop_back();
     138             :     }
     139             : 
     140           2 :     QString p;
     141           1 :     int const max_segments(segments.size());
     142           5 :     for(int idx(0); idx < max_segments; ++idx)
     143             :     {
     144             :         // compute path
     145           4 :         p += "/";
     146           4 :         p += segments[idx];
     147             : 
     148             :         // already exists?
     149             :         struct stat s;
     150           4 :         if(stat(p.toUtf8().data(), &s) == 0)
     151             :         {
     152             :             // the file exists, it is a directory?
     153             :             //
     154           4 :             if(S_ISDIR(s.st_mode))
     155             :             {
     156             :                 // make sure the last segment (directory we are really
     157             :                 // expected to create) has the correct mode and ownership
     158             :                 //
     159           4 :                 if(idx + 1 == max_segments)
     160             :                 {
     161           1 :                     if(mode != 0)
     162             :                     {
     163           1 :                         if(chmod(p.toUtf8().data(), mode) != 0)
     164             :                         {
     165           0 :                             int const e(errno);
     166           0 :                             std::stringstream m;
     167           0 :                             m << "0" << std::oct << mode;
     168           0 :                             SNAP_LOG_DEBUG("could not change directory \"")
     169           0 :                                           (p)
     170           0 :                                           ("\" permissions to \"")
     171           0 :                                           (m.str())
     172           0 :                                           ("\". (errno: ")
     173           0 :                                           (e)
     174           0 :                                           (" -- ")
     175           0 :                                           (strerror(e));
     176             :                         }
     177             :                     }
     178           1 :                     if(chownnm(p, owner, group) != 0)
     179             :                     {
     180           1 :                         int const e(errno);
     181           2 :                         SNAP_LOG_DEBUG("could not change directory \"")
     182           1 :                                       (p)
     183           1 :                                       ("\" ownership to \"")
     184           1 :                                       (owner)
     185           1 :                                       (":")
     186           1 :                                       (group)
     187           1 :                                       ("\". (errno: ")
     188           1 :                                       (e)
     189           1 :                                       (" -- ")
     190           1 :                                       (strerror(e));
     191             :                     }
     192             :                 }
     193           8 :                 continue;
     194             :             }
     195             : 
     196             :             // not a directory, that is an error
     197             :             //
     198           0 :             int const e(EEXIST);
     199           0 :             SNAP_LOG_ERROR("could not create directory \"")
     200           0 :                           (p)
     201           0 :                           ("\" since a file, which is not a directory, of the same name exists. (errno: ")
     202           0 :                           (e)
     203           0 :                           (" -- ")
     204           0 :                           (strerror(e));
     205           0 :             errno = e;
     206           0 :             return -1;
     207             :         }
     208             : 
     209             :         // attempt creating
     210             :         //
     211           0 :         if(mkdir(p.toUtf8().data(), mode == 0 ? 0755 : mode) != 0)
     212             :         {
     213           0 :             int const e(errno);
     214           0 :             SNAP_LOG_ERROR("could not create directory \"")
     215           0 :                           (p)
     216           0 :                           ("\". (errno: ")
     217           0 :                           (e)
     218           0 :                           (" -- ")
     219           0 :                           (strerror(e));
     220           0 :             errno = e;
     221           0 :             return -1;
     222             :         }
     223             : 
     224             :         // directories we create are also assigned owner/group
     225             :         //
     226           0 :         if(chownnm(p, owner, group) != 0)
     227             :         {
     228           0 :             int const e(errno);
     229           0 :             SNAP_LOG_DEBUG("could not change directory \"")
     230           0 :                           (p)
     231           0 :                           ("\" ownership to \"")
     232           0 :                           (owner)
     233           0 :                           (":")
     234           0 :                           (group)
     235           0 :                           ("\". (errno: ")
     236           0 :                           (e)
     237           0 :                           (" -- ")
     238           0 :                           (strerror(e));
     239             :         }
     240             :     }
     241             : 
     242           1 :     return 0;
     243             : }
     244             : 
     245             : 
     246           1 : int mkdir_p(std::string const & path
     247             :           , bool include_filename
     248             :           , int mode
     249             :           , std::string const & owner
     250             :           , std::string const & group)
     251             : {
     252           2 :     return mkdir_p(QString::fromUtf8(path.c_str())
     253             :                  , include_filename
     254             :                  , mode
     255           2 :                  , QString::fromUtf8(owner.c_str())
     256           3 :                  , QString::fromUtf8(group.c_str()));
     257             : }
     258             : 
     259             : 
     260           0 : int mkdir_p(char const * path
     261             :           , bool include_filename
     262             :           , int mode
     263             :           , char const * owner
     264             :           , char const * group)
     265             : {
     266           0 :     return mkdir_p(QString::fromUtf8(path)
     267             :                  , include_filename
     268             :                  , mode
     269           0 :                  , QString::fromUtf8(owner == nullptr ? "" : owner)
     270           0 :                  , QString::fromUtf8(group == nullptr ? "" : group));
     271             : }
     272             : 
     273             : 
     274           6 : } // snap namespace
     275             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13