+++ /dev/null
-/* makepath.c -- Ensure that a directory path exists.
-
- Copyright (C) 1990, 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software Foundation,
- Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
-
-/* Written by David MacKenzie <djm@gnu.ai.mit.edu> and Jim Meyering. */
-/*
- * Modified by Kern Sibbald for use in Bacula, December 2000
- *
- * Version $Id$
- */
-
-#include "bacula.h"
-#include "jcr.h"
-#include "savecwd.h"
-
-
-#if !defined(S_ISDIR) && defined(S_IFDIR)
-# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
-#endif
-
-#ifndef S_IRWXUGO
-# define S_IRWXUGO (S_IRWXU | S_IRWXG | S_IRWXO)
-#endif
-
-
-#ifndef S_ISUID
-# define S_ISUID 04000
-#endif
-#ifndef S_ISGID
-# define S_ISGID 02000
-#endif
-#ifndef S_ISVTX
-# define S_ISVTX 01000
-#endif
-#ifndef S_IRUSR
-# define S_IRUSR 0200
-#endif
-#ifndef S_IWUSR
-# define S_IWUSR 0200
-#endif
-#ifndef S_IXUSR
-# define S_IXUSR 0100
-#endif
-
-#ifndef S_IRWXU
-# define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR)
-#endif
-
-#define WX_USR (S_IWUSR | S_IXUSR)
-
-#define quote(path) path
-
-extern void strip_trailing_slashes();
-
-/* Attempt to create directory DIR (aka DIRPATH) with the specified MODE.
- If CREATED_DIR_P is non-NULL, set *CREATED_DIR_P to non-zero if this
- function creates DIR and to zero otherwise. Give a diagnostic and
- return non-zero if DIR cannot be created or cannot be determined to
- exist already. Use DIRPATH in any diagnostic, not DIR.
- Note that if DIR already exists, this function will return zero
- (indicating success) and will set *CREATED_DIR_P to zero. */
-
-static int
-make_dir(JCR *jcr, const char *dir, const char *dirpath, mode_t mode, int *created_dir_p)
-{
- int fail = 0;
- int created_dir;
- int save_errno;
-
- Dmsg2(300, "make_dir mode=%o dir=%s\n", mode, dir);
- created_dir = (mkdir(dir, mode) == 0);
- save_errno = errno;
-
- if (!created_dir) {
- struct stat stats;
-
- /* The mkdir and stat calls below may appear to be reversed.
- They are not. It is important to call mkdir first and then to
- call stat (to distinguish the three cases) only if mkdir fails.
- The alternative to this approach is to `stat' each directory,
- then to call mkdir if it doesn't exist. But if some other process
- were to create the directory between the stat & mkdir, the mkdir
- would fail with EEXIST. */
-
- if (stat(dir, &stats)) {
- berrno be;
- be.set_errno(save_errno);
- Jmsg(jcr, M_ERROR, 0, _("Cannot create directory %s: ERR=%s\n"),
- dirpath, be.bstrerror());
- fail = 1;
- } else if (!S_ISDIR(stats.st_mode)) {
- Jmsg(jcr, M_ERROR, 0, _("%s exists but is not a directory\n"), quote(dirpath));
- fail = 1;
- } else {
- /* DIR (aka DIRPATH) already exists and is a directory. */
- }
- }
-
- if (created_dir_p) {
- *created_dir_p = created_dir;
- }
-
- return fail;
-}
-
-/* return non-zero if path is absolute or zero if relative. */
-int
-isAbsolute(const char *path)
-{
-#if defined(HAVE_WIN32)
- return path[1] == ':' || IsPathSeparator(*path); /* drivespec:/blah is absolute */
-#else
- return IsPathSeparator(*path);
-#endif
-}
-
-/* Ensure that the directory ARGPATH exists.
- Remove any trailing slashes from ARGPATH before calling this function.
-
- Create any leading directories that don't already exist, with
- permissions PARENT_MODE.
-
- If the last element of ARGPATH does not exist, create it as
- a new directory with permissions MODE.
-
- If OWNER and GROUP are non-negative, use them to set the UID and GID of
- any created directories.
-
- If VERBOSE_FMT_STRING is nonzero, use it as a printf format
- string for printing a message after successfully making a directory,
- with the name of the directory that was just made as an argument.
-
- If PRESERVE_EXISTING is non-zero and ARGPATH is an existing directory,
- then do not attempt to set its permissions and ownership.
-
- Return 0 if ARGPATH exists as a directory with the proper
- ownership and permissions when done, otherwise 1. */
-
-int
-make_path(
- JCR *jcr,
- const char *argpath,
- int mode,
- int parent_mode,
- uid_t owner,
- gid_t group,
- int preserve_existing,
- char *verbose_fmt_string)
-{
- struct stat stats;
- int retval = 0;
-
- if (stat(argpath, &stats)) {
- char *slash;
- int tmp_mode; /* Initial perms for leading dirs. */
- int re_protect; /* Should leading dirs be unwritable? */
- struct ptr_list {
- char *dirname_end;
- struct ptr_list *next;
- };
- struct ptr_list *p, *leading_dirs = NULL;
- saveCWD cwd;
- char *basename_dir;
- char *dirpath;
-
- /* Temporarily relax umask in case it's overly restrictive. */
- mode_t oldmask = umask(0);
-
- /* Make a copy of ARGPATH that we can scribble NULs on. */
- dirpath = (char *)alloca(strlen(argpath) + 1);
- strcpy (dirpath, argpath);
- strip_trailing_slashes(dirpath);
-
- /* If leading directories shouldn't be writable or executable,
- or should have set[ug]id or sticky bits set and we are setting
- their owners, we need to fix their permissions after making them. */
- if (((parent_mode & WX_USR) != WX_USR)
- || ((owner != (uid_t)-1 || group != (gid_t)-1)
- && (parent_mode & (S_ISUID | S_ISGID | S_ISVTX)) != 0)) {
- tmp_mode = S_IRWXU;
- re_protect = 1;
- } else {
- tmp_mode = parent_mode;
- re_protect = 0;
- }
-
-#if defined(HAVE_WIN32)
- /* chdir can fail if permissions are sufficiently restricted since I don't think
- backup/restore security rights affect ChangeWorkingDirectory */
-
- /* Validate drive letter */
- if (dirpath[1] == ':') {
- char drive[4] = "X:\\";
-
- drive[0] = dirpath[0];
-
- UINT drive_type = GetDriveType(drive);
-
- if (drive_type == DRIVE_UNKNOWN || drive_type == DRIVE_NO_ROOT_DIR) {
- Jmsg(jcr, M_ERROR, 0, _("%c: is not a valid drive\n"), dirpath[0]);
- return 1;
- }
-
- if (dirpath[2] == '\0') {
- return 0;
- }
-
- slash = &dirpath[3];
- } else {
- slash = dirpath;
- }
-#else
- /* If we can record the current working directory, we may be able
- to do the chdir optimization. */
- cwd.save(jcr);
-
- slash = dirpath;
-#endif
-
- /* If we've saved the cwd and DIRPATH is an absolute pathname,
- we must chdir to `/' in order to enable the chdir optimization.
- So if chdir ("/") fails, turn off the optimization. */
- if (cwd.is_saved() && isAbsolute(dirpath) && (chdir("/") < 0)) {
- cwd.release();
- }
-
- /* Skip over leading slashes. */
- while (IsPathSeparator(*slash))
- slash++;
-
- for ( ; ; ) {
- int newly_created_dir;
- int fail;
-
- /* slash points to the leftmost unprocessed component of dirpath. */
- basename_dir = slash;
- slash = first_path_separator(slash);
- if (slash == NULL) {
- break;
- }
-
- /* If we're *not* doing chdir before each mkdir, then we have to refer
- to the target using the full (multi-component) directory name. */
- if (!cwd.is_saved()) {
- basename_dir = dirpath;
- }
-
- *slash = '\0';
- fail = make_dir(jcr, basename_dir, dirpath, tmp_mode, &newly_created_dir);
- if (fail) {
- umask(oldmask);
- cwd.restore(jcr);
- return 1;
- }
-
- if (newly_created_dir) {
- Dmsg0(300, "newly_created_dir\n");
-
- if ((owner != (uid_t)-1 || group != (gid_t)-1)
- && chown(basename_dir, owner, group)
-#if defined(AFS) && defined (EPERM)
- && errno != EPERM
-#endif
- ) {
- /* Note, if we are restoring as NON-root, this may not be fatal */
- berrno be;
- Jmsg(jcr, M_ERROR, 0, _("Cannot change owner and/or group of %s: ERR=%s\n"),
- quote(dirpath), be.bstrerror());
- }
- Dmsg0(300, "Chown done.\n");
-
- if (re_protect) {
- struct ptr_list *pnew = (struct ptr_list *)
- alloca(sizeof (struct ptr_list));
- pnew->dirname_end = slash;
- pnew->next = leading_dirs;
- leading_dirs = pnew;
- Dmsg0(300, "re_protect\n");
- }
- }
-
- /* If we were able to save the initial working directory,
- then we can use chdir to change into each directory before
- creating an entry in that directory. This avoids making
- stat and mkdir process O(n^2) file name components. */
- if (cwd.is_saved() && chdir(basename_dir) < 0) {
- berrno be;
- Jmsg(jcr, M_ERROR, 0, _("Cannot chdir to directory, %s: ERR=%s\n"),
- quote(dirpath), be.bstrerror());
- umask(oldmask);
-
- cwd.restore(jcr);
- return 1;
- }
-
- *slash++ = '/';
-
- /* Avoid unnecessary calls to `stat' when given
- pathnames containing multiple adjacent slashes. */
- while (IsPathSeparator(*slash))
- slash++;
- } /* end while (1) */
-
- if (!cwd.is_saved()) {
- basename_dir = dirpath;
- }
-
- /* We're done making leading directories.
- Create the final component of the path. */
-
- Dmsg1(300, "Create final component. mode=%o\n", mode);
- if (make_dir(jcr, basename_dir, dirpath, mode, NULL)) {
- umask(oldmask);
- cwd.restore(jcr);
- return 1;
- }
-
- /* Done creating directories. Restore original umask. */
- umask(oldmask);
-
- if (owner != (uid_t)-1 || group != (gid_t)-1) {
- if (chown(basename_dir, owner, group)
-#ifdef AFS
- && errno != EPERM
-#endif
- )
- {
- berrno be;
- Jmsg(jcr, M_WARNING, 0, _("Cannot change owner and/or group of %s: ERR=%s\n"),
- quote(dirpath), be.bstrerror());
- }
- }
-
- /* The above chown may have turned off some permission bits in MODE.
- Another reason we may have to use chmod here is that mkdir(2) is
- required to honor only the file permission bits. In particular,
- it need not honor the `special' bits, so if MODE includes any
- special bits, set them here. */
- if (mode & ~S_IRWXUGO) {
- Dmsg1(300, "Final chmod mode=%o\n", mode);
- }
- if ((mode & ~S_IRWXUGO) && chmod(basename_dir, mode)) {
- berrno be;
- Jmsg(jcr, M_WARNING, 0, _("Cannot change permissions of %s: ERR=%s\n"),
- quote(dirpath), be.bstrerror());
- }
-
- if (!cwd.restore(jcr)) {
- return 1;
- }
-
- /* If the mode for leading directories didn't include owner "wx"
- privileges, we have to reset their protections to the correct
- value. */
- for (p = leading_dirs; p != NULL; p = p->next) {
- *(p->dirname_end) = '\0';
- Dmsg2(300, "Reset parent mode=%o dir=%s\n", parent_mode, dirpath);
- if (chmod(dirpath, parent_mode)) {
- berrno be;
- Jmsg(jcr, M_WARNING, 0, _("Cannot change permissions of %s: ERR=%s\n"),
- quote(dirpath), be.bstrerror());
- }
- }
- } else {
- /* We get here if the entire path already exists. */
-
- const char *dirpath = argpath;
-
- if (!S_ISDIR(stats.st_mode)) {
- Jmsg(jcr, M_ERROR, 0, _("%s exists but is not a directory\n"), quote(dirpath));
- return 1;
- }
-
- if (!preserve_existing) {
- Dmsg0(300, "Do not preserve existing.\n");
- /* chown must precede chmod because on some systems,
- chown clears the set[ug]id bits for non-superusers,
- resulting in incorrect permissions.
- On System V, users can give away files with chown and then not
- be able to chmod them. So don't give files away. */
-
- if ((owner != (uid_t)-1 || group != (gid_t)-1)
- && chown(dirpath, owner, group)
-#ifdef AFS
- && errno != EPERM
-#endif
- ) {
- berrno be;
- Jmsg(jcr, M_WARNING, 0, _("Cannot change owner and/or group of %s: ERR=%s\n"),
- quote(dirpath), be.bstrerror());
- }
- if (chmod(dirpath, mode)) {
- berrno be;
- Jmsg(jcr, M_WARNING, 0, _("Cannot change permissions of %s: ERR=%s\n"),
- quote(dirpath), be.bstrerror());
- }
- Dmsg2(300, "pathexists chmod mode=%o dir=%s\n", mode, dirpath);
- }
- }
- return retval;
-}
--- /dev/null
+/*
+ Bacula® - The Network Backup Solution
+
+ Copyright (C) 2007-2007 Free Software Foundation Europe e.V.
+
+ The main author of Bacula is Kern Sibbald, with contributions from
+ many others, a complete list can be found in the file AUTHORS.
+ This program is Free Software; you can redistribute it and/or
+ modify it under the terms of version two of the GNU General Public
+ License as published by the Free Software Foundation and included
+ in the file LICENSE.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+ Bacula® is a registered trademark of John Walker.
+ The licensor of Bacula is the Free Software Foundation Europe
+ (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
+ Switzerland, email:ftf@fsfeurope.org.
+*/
+
+/*
+ * Kern Sibbald, September MMVII
+ *
+ * This is tricky code, especially when writing from scratch. Fortunately,
+ * a non-copyrighted version of mkdir was available to consult.
+ *
+ * Version $Id$
+ */
+#include "bacula.h"
+#include "jcr.h"
+
+static bool makedir(JCR *jcr, char *path, mode_t mode, int *created)
+{
+ struct stat statp;
+
+ if (mkdir(path, mode) != 0) {
+ berrno be;
+ *created = false;
+ if (stat(path, &statp) != 0) {
+ Jmsg(jcr, M_ERROR, 0, _("Cannot create directory %s: ERR=%s\n"),
+ path, be.bstrerror());
+ return false;
+ } else if (!S_ISDIR(statp.st_mode)) {
+ Jmsg(jcr, M_ERROR, 0, _("%s exists but is not a directory.\n"), path);
+ return false;
+ }
+ return true; /* directory exists */
+ }
+ *created = true;
+ return true;
+}
+
+static void set_own_mod(JCR *jcr, char *path, uid_t owner, gid_t group, mode_t mode)
+{
+ if (chown(path, owner, group) != 0
+#ifdef AFS
+ && errno != EPERM
+#endif
+ ) {
+ berrno be;
+ Jmsg(jcr, M_WARNING, 0, _("Cannot change owner and/or group of %s: ERR=%s\n"),
+ path, be.bstrerror());
+ }
+ if (chmod(path, mode) != 0) {
+ berrno be;
+ Jmsg(jcr, M_WARNING, 0, _("Cannot change permissions of %s: ERR=%s\n"),
+ path, be.bstrerror());
+ }
+}
+
+/*
+ * mode is the mode bits to use in creating a new directory
+ *
+ * parent_mode are the parent's modes if we need to create parent
+ * directories.
+ *
+ * owner and group are to set on any created dirs
+ *
+ * keep_dir_modes if set means don't change mode bits if dir exists
+ */
+bool makepath(JCR *jcr, const char *apath, mode_t mode, mode_t parent_mode,
+ uid_t owner, gid_t group, int keep_dir_modes)
+{
+ struct stat statp;
+ mode_t omask, tmode;
+ char *path = (char *)apath;
+ char *p;
+ int len;
+ bool ok = false;
+ int created;
+ char new_dir[5000];
+ int ndir = 0;
+ int i = 0;
+ int max_dirs = (int)sizeof(new_dir);
+
+ if (stat(path, &statp) == 0) { /* Does dir exist? */
+ if (!S_ISDIR(statp.st_mode)) {
+ Jmsg(jcr, M_ERROR, 0, _("%s exists but is not a directory.\n"), path);
+ return false;
+ }
+ /* Full path exists */
+ if (keep_dir_modes) {
+ return true;
+ }
+ set_own_mod(jcr, path, owner, group, mode);
+ return true;
+ }
+ omask = umask(0);
+ umask(omask);
+ len = strlen(apath);
+ path = (char *)alloca(len+1);
+ bstrncpy(path, apath, len+1);
+ strip_trailing_slashes(path);
+ /*
+ * Now for one of the complexities. If we are not running as root,
+ * then if the parent_mode does not have wx user perms, or we are
+ * setting the userid or group, and the parent_mode has setuid, setgid,
+ * or sticky bits, we must create the dir with open permissions, then
+ * go back and patch all the dirs up with the correct perms.
+ * Solution, set everything to 0777, then go back and reset them at the
+ * end.
+ */
+ tmode = 0777;
+
+#if defined(HAVE_WIN32)
+ /* Validate drive letter */
+ if (path[1] == ':') {
+ char drive[4] = "X:\\";
+
+ drive[0] = path[0];
+
+ UINT drive_type = GetDriveType(drive);
+
+ if (drive_type == DRIVE_UNKNOWN || drive_type == DRIVE_NO_ROOT_DIR) {
+ Jmsg(jcr, M_ERROR, 0, _("%c: is not a valid drive.\n"), path[0]);
+ goto bail_out;
+ }
+
+ if (path[2] == '\0') { /* attempt to create a drive */
+ ok = true;
+ goto bail_out; /* OK, it is already there */
+ }
+
+ p = &path[3];
+ } else {
+ p = path;
+ }
+#else
+ p = path;
+#endif
+
+ /* Skip leading slash(es) */
+ while (IsPathSeparator(*p)) {
+ p++;
+ }
+ while ((p = first_path_separator(p))) {
+ char save_p;
+ save_p = *p;
+ *p = 0;
+ if (!makedir(jcr, path, tmode, &created)) {
+ goto bail_out;
+ }
+ if (ndir < max_dirs) {
+ new_dir[ndir++] = created;
+ }
+ *p = save_p;
+ while (IsPathSeparator(*p)) {
+ p++;
+ }
+ }
+ /* Create final component */
+ if (!makedir(jcr, path, tmode, &created)) {
+ goto bail_out;
+ }
+ if (ndir < max_dirs) {
+ new_dir[ndir++] = created;
+ }
+ if (ndir >= max_dirs) {
+ Jmsg(jcr, M_WARNING, 0, _("Too many subdirectories. Some permissions not reset.\n"));
+ }
+
+ /* Now set the proper owner and modes */
+#if defined(HAVE_WIN32)
+ if (path[1] == ':') {
+ p = &path[3];
+ } else {
+ p = path;
+ }
+#else
+ p = path;
+#endif
+ /* Skip leading slash(es) */
+ while (IsPathSeparator(*p)) {
+ p++;
+ }
+ while ((p = first_path_separator(p))) {
+ char save_p;
+ save_p = *p;
+ *p = 0;
+ if (i < ndir && new_dir[i++] && !keep_dir_modes) {
+ set_own_mod(jcr, path, owner, group, parent_mode);
+ }
+ *p = save_p;
+ while (IsPathSeparator(*p)) {
+ p++;
+ }
+ }
+
+ /* Set for final component */
+ if (i < ndir && new_dir[i++]) {
+ set_own_mod(jcr, path, owner, group, mode);
+ }
+
+ ok = true;
+bail_out:
+ umask(omask);
+ return ok;
+}