diff --git a/dir.c b/dir.c index 0c82c6e..bf022d2 100644 --- a/dir.c +++ b/dir.c @@ -69,3 +69,41 @@ gchar *dir_parent(gchar * p) } return NULL; } + +/** + * Make sure a path exists + */ + +void dir_mkpath(gchar *p) +{ + gchar *parent; + + parent = dir_parent(p); + + if (parent && (parent[0] == 0)) { + msgd(__func__, __LINE__, _("Reached / while trying to create path, bailing out.")); + g_free(parent); + return; + } + +#if DEBUG + msgd(__func__, __LINE__, _("Creating skeleton directory `%s\'"), parent); +#endif + + struct stat *st = g_malloc(sizeof(struct stat)); + + if (stat(parent, st) != 0) { + /* Directory does not exist. EEXIST for race condition */ + if (mkdir(parent, 0700) != 0 && errno != EEXIST) { + dir_mkpath(parent); + if (mkdir(parent, 0700) != 0 && errno != EEXIST) + msgd(__func__, __LINE__, _("Failed to create `%s\'"), parent); + } + } + else if (!S_ISDIR(st->st_mode)) { + msgd(__func__, __LINE__, _("%s\': already exists and not a directory"), parent); + } + + g_free(st); + g_free(parent); +} diff --git a/fs-up.c b/fs-up.c index 9a6517f..2b28f03 100644 --- a/fs-up.c +++ b/fs-up.c @@ -97,7 +97,20 @@ mk_dev(struct rdup *e, GHashTable * uidhash, GHashTable * gidhash) return FALSE; if (mknod(e->f_name, e->f_mode, e->f_rdev) == -1) { - if (errno == EACCES) { + if ( errno == ENOENT ) { + // A path element does not exist, this is probably a -R dump + // Create path with default perms, dir entry itself will appear later + // and set things right regarding perms/ownership + dir_mkpath(e->f_name); + + // Retry mknod + if (mknod(e->f_name, e->f_mode, e->f_rdev) == -1) { + msgd(__func__, __LINE__, + _("Failed to make device `%s\': %s"), + e->f_name, strerror(errno)); + return FALSE; + } + } else if (errno == EACCES) { parent = dir_parent(e->f_name); st = dir_write(parent); if (mknod(e->f_name, e->f_mode, e->f_rdev) == -1) { @@ -105,10 +118,12 @@ mk_dev(struct rdup *e, GHashTable * uidhash, GHashTable * gidhash) _("Failed to make device `%s\': %s"), e->f_name, strerror(errno)); dir_restore(parent, st); + g_free(st); g_free(parent); return FALSE; } dir_restore(parent, st); + g_free(st); g_free(parent); } else { msgd(__func__, __LINE__, @@ -134,7 +149,20 @@ mk_sock(struct rdup *e, GHashTable * uidhash, GHashTable * gidhash) return FALSE; if (mkfifo(e->f_name, e->f_mode) == -1) { - if (errno == EACCES) { + if ( errno == ENOENT ) { + // A path element does not exist, this is probably a -R dump + // Create path with default perms, dir entry itself will appear later + // and set things right regarding perms/ownership + dir_mkpath(e->f_name); + + // Retry fifio + if (mkfifo(e->f_name, e->f_mode) == -1) { + msgd(__func__, __LINE__, + _("Failed to make socket `%s\': %s"), + e->f_name, strerror(errno)); + return FALSE; + } + } else if (errno == EACCES) { parent = dir_parent(e->f_name); st = dir_write(parent); if (mkfifo(e->f_name, e->f_mode) == -1) { @@ -142,10 +170,12 @@ mk_sock(struct rdup *e, GHashTable * uidhash, GHashTable * gidhash) _("Failed to make socket `%s\': %s"), e->f_name, strerror(errno)); dir_restore(parent, st); + g_free(st); g_free(parent); return FALSE; } dir_restore(parent, st); + g_free(st); g_free(parent); } else { msgd(__func__, __LINE__, @@ -174,7 +204,20 @@ mk_link(struct rdup *e, char *p, GHashTable * uidhash, GHashTable * gidhash) /* symlink */ if (S_ISLNK(e->f_mode)) { if (symlink(e->f_target, e->f_name) == -1) { - if (errno == EACCES) { + if ( errno == ENOENT ) { + // A path element does not exist, this is probably a -R dump + // Create path with default perms, dir entry itself will appear later + // and set things right regarding perms/ownership + dir_mkpath(e->f_name); + + // Retry symlink + if (symlink(e->f_target, e->f_name) == -1) { + msgd(__func__, __LINE__, + _("Failed to make symlink `%s -> %s\': %s"), + e->f_name, e->f_target, strerror(errno)); + return FALSE; + } + } else if (errno == EACCES) { parent = dir_parent(e->f_name); st = dir_write(parent); if (symlink(e->f_target, e->f_name) == -1) { @@ -184,10 +227,12 @@ mk_link(struct rdup *e, char *p, GHashTable * uidhash, GHashTable * gidhash) e->f_name, e->f_target, strerror(errno)); dir_restore(parent, st); + g_free(st); g_free(parent); return FALSE; } dir_restore(parent, st); + g_free(st); g_free(parent); } else { msgd(__func__, __LINE__, @@ -236,17 +281,32 @@ mk_reg(FILE * in, struct rdup *e, GHashTable * uidhash, GHashTable * gidhash) } } if (!opt_dry && !(out = fopen(e->f_name, "w"))) { - if (errno == EACCES) { + if ( errno == ENOENT ) { + // A path element does not exist, this is probably a -R dump + // Create path with default perms, dir entry itself will appear later + // and set things right regarding perms/ownership + dir_mkpath(e->f_name); + + // Reopen file now + if (!(out = fopen(e->f_name, "w"))) { + msgd(__func__, __LINE__, + _("ENOENT file `%s\': %s"), + e->f_name, strerror(errno)); + ok = FALSE; + } + } else if (errno == EACCES) { parent = dir_parent(e->f_name); st = dir_write(parent); if (!(out = fopen(e->f_name, "w"))) { msgd(__func__, __LINE__, _("Failed to open file `%s\': %s"), e->f_name, strerror(errno)); + g_free(st); g_free(parent); ok = FALSE; } dir_restore(parent, st); + g_free(st); g_free(parent); } else { msgd(__func__, __LINE__, @@ -320,7 +380,20 @@ mk_dir(struct rdup *e, GHashTable * uidhash, GHashTable * gidhash) /* nothing there */ if (mkdir(e->f_name, e->f_mode) == -1) { - if (errno == EACCES) { + if ( errno == ENOENT ) { + // A path element does not exist, this is probably a -R dump + // Create path with default perms, dir entry itself will appear later + // and set things right regarding perms/ownership + dir_mkpath(e->f_name); + + // Retry mkdir + if (mkdir(e->f_name, e->f_mode) == -1) { + msgd(__func__, __LINE__, + _("Failed to create directory `%s\': %s"), + e->f_name, strerror(errno)); + return FALSE; + } + } else if (errno == EACCES) { /* make parent dir writable, and try again */ parent = dir_parent(e->f_name); #ifdef DEBUG @@ -336,10 +409,12 @@ mk_dir(struct rdup *e, GHashTable * uidhash, GHashTable * gidhash) _("Failed to create directory `%s\': %s"), e->f_name, strerror(errno)); dir_restore(parent, s); + g_free(s); g_free(parent); return FALSE; } dir_restore(parent, s); + g_free(s); g_free(parent); } else { msgd(__func__, __LINE__, diff --git a/rdup-up.h.in b/rdup-up.h.in index cdd789f..661db0c 100644 --- a/rdup-up.h.in +++ b/rdup-up.h.in @@ -92,6 +92,8 @@ int mkpath(const char *, mode_t); struct stat * dir_write(gchar *); void dir_restore(gchar *, struct stat *); gchar *dir_parent(gchar *); +void dir_mkpath(gchar *); + /* chown.c */ void chown_write(gchar *dir, gchar *base, uid_t u, gchar *user, gid_t g, gchar *group);