/*
- 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.
+ Bacula(R) - The Network Backup Solution
+
+ Copyright (C) 2000-2015 Kern Sibbald
+
+ The original author of Bacula is Kern Sibbald, with contributions
+ from many others, a complete list can be found in the file AUTHORS.
+
+ You may use this file and others of this release according to the
+ license defined in the LICENSE file, which includes the Affero General
+ Public License, v3.0 ("AGPLv3") and some additional permissions and
+ terms pursuant to its AGPLv3 Section 7.
+
+ This notice must be preserved when any source code is
+ conveyed and/or propagated.
+
+ Bacula(R) is a registered trademark of Kern Sibbald.
*/
/*
* 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$
+ * ***FIXME*** the mkpath code could be significantly optimized by
+ * walking up the path chain from the bottom until it either gets
+ * to the top or finds an existing directory then walk back down
+ * creating the path components. Currently, it always starts at
+ * the top, which can be rather inefficient for long path names.
+ *
*/
#include "bacula.h"
#include "jcr.h"
+#define dbglvl 50
+
+/*
+ * For old systems that don't have lchown() or lchmod()
+ */
+#ifndef HAVE_LCHOWN
+#define lchown chown
+#endif
+#ifndef HAVE_LCHMOD
+#define lchmod chmod
+#endif
+
+
+typedef struct PrivateCurDir {
+ hlink link;
+ char fname[1];
+} CurDir;
+
+/* Initialize the path hash table */
+static bool path_list_init(JCR *jcr)
+{
+ CurDir *elt = NULL;
+ jcr->path_list = (htable *)malloc(sizeof(htable));
+
+ /* Hard to know in advance how many directories will
+ * be stored in this hash
+ */
+ jcr->path_list->init(elt, &elt->link, 10000);
+ return true;
+}
+
+/* Add a path to the hash when we create a directory
+ * with the replace=NEVER option
+ */
+bool path_list_add(JCR *jcr, uint32_t len, char *fname)
+{
+ bool ret = true;
+ CurDir *item;
+
+ if (!jcr->path_list) {
+ path_list_init(jcr);
+ }
+
+ /* we store CurDir, fname in the same chunk */
+ item = (CurDir *)jcr->path_list->hash_malloc(sizeof(CurDir)+len+1);
+
+ memset(item, 0, sizeof(CurDir));
+ memcpy(item->fname, fname, len+1);
+
+ jcr->path_list->insert(item->fname, item);
+
+ Dmsg1(dbglvl, "add fname=<%s>\n", fname);
+ return ret;
+}
+
+void free_path_list(JCR *jcr)
+{
+ if (jcr->path_list) {
+ jcr->path_list->destroy();
+ free(jcr->path_list);
+ jcr->path_list = NULL;
+ }
+}
+
+bool path_list_lookup(JCR *jcr, char *fname)
+{
+ bool found=false;
+ char bkp;
+
+ if (!jcr->path_list) {
+ return false;
+ }
+
+ /* Strip trailing / */
+ int len = strlen(fname);
+ if (len == 0) {
+ return false;
+ }
+ len--;
+ bkp = fname[len];
+ if (fname[len] == '/') { /* strip any trailing slash */
+ fname[len] = 0;
+ }
+
+ CurDir *temp = (CurDir *)jcr->path_list->lookup(fname);
+ if (temp) {
+ found=true;
+ }
+
+ Dmsg2(dbglvl, "lookup <%s> %s\n", fname, found?"ok":"not ok");
+
+ fname[len] = bkp; /* restore last / */
+ return found;
+}
+
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) {
+ if (lstat(path, &statp) != 0) {
Jmsg2(jcr, M_ERROR, 0, _("Cannot create directory %s: ERR=%s\n"),
path, be.bstrerror());
return false;
}
return true; /* directory exists */
}
+#if 0
+ /* TODO: This code rely on statp that is not initialized, we need to do a stat() */
+ if (S_ISLNK(statp.st_mode)) {
+ /*
+ * Note, we created a directory, not a link, so if we find a
+ * link, there is a security problem here.
+ */
+ Jmsg1(jcr, M_FATAL, 0, _("Security problem!! We created directory %s, but it is a link.\n"),
+ path);
+ return false;
+ }
+#endif
+ if (jcr->keep_path_list) {
+ /* When replace=NEVER, we keep track of all directories newly created */
+ path_list_add(jcr, strlen(path), path);
+ }
+
*created = true;
return true;
}
+/*
+ * Restore the owner and permissions (mode) of a Directory.
+ * See attribs.c for the equivalent for files.
+ */
static void set_own_mod(ATTR *attr, char *path, uid_t owner, gid_t group, mode_t mode)
{
- if (chown(path, owner, group) != 0 && attr->uid == 0
+ if (lchown(path, owner, group) != 0 && attr->uid == 0
#ifdef AFS
&& errno != EPERM
#endif
Jmsg2(attr->jcr, M_WARNING, 0, _("Cannot change owner and/or group of %s: ERR=%s\n"),
path, be.bstrerror());
}
- if (chmod(path, mode) != 0 && attr->uid == 0) {
+ if (lchmod(path, mode) != 0 && attr->uid == 0) {
berrno be;
Jmsg2(attr->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
+ * parent_mode are the parent's modes if we need to create parent
* directories.
*
* owner and group are to set on any created dirs
*/
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) {
- Jmsg1(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)) {
}
/* 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++;