void dir_crawl(GTree *t, GHashTable *linkhash, GHashTable *userhash, GHashTable *grouphash, char *path) { DIR *dir; struct dirent *dent; struct rdup *directory; struct chown_pack *cp; char *curpath; gchar *lnk; struct stat s; struct rdup pop; struct remove_path rp; dev_t current_dev; size_t curpath_len; /* dir stack */ gint32 d = 0; gint32 dstack_cnt = 1; struct rdup **dirstack = g_malloc(dstack_cnt * D_STACKSIZE * sizeof(struct rdup *)); if (!(dir = opendir(path))) { /* non-dirs are also allowed, check for this, if it isn't give the error */ if (access(path, R_OK) == 0) { g_free(dirstack); return; } msg(_("Cannot enter directory `%s\': %s"), path, strerror(errno)); g_free(dirstack); return; } /* get device */ #ifdef HAVE_DIRFD if (fstat(dirfd(dir), &s) != 0) { #else if (fstat(rdup_dirfd(dir), &s) != 0) { #endif msg(_("Cannot determine holding device of the directory `%s\': %s"), path, strerror(errno)); closedir(dir); g_free(dirstack); return; } current_dev = s.st_dev; while((dent = readdir(dir))) { if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) continue; if (opt_chown) { if ( !strncmp(dent->d_name, USRGRPINFO, LEN_USRGRPINFO) ) { continue; } } if (strcmp(path, "/") == 0) { curpath = g_strdup_printf("/%s", dent->d_name); curpath_len = strlen(curpath); } else { curpath = g_strdup_printf("%s/%s", path, dent->d_name); curpath_len = strlen(curpath); } if (lstat(curpath, &s) != 0) { msg(_("Could not stat path `%s\': %s"), curpath, strerror(errno)); g_free(curpath); continue; } if (strchr(curpath, '\n')) { msg(_("Newline (\\n) found in path `%s\', skipping"), curpath); g_free(curpath); continue; } if (S_ISREG(s.st_mode) || S_ISLNK(s.st_mode) || S_ISBLK(s.st_mode) || S_ISCHR(s.st_mode) || S_ISFIFO(s.st_mode) || S_ISSOCK(s.st_mode) ) { pop.f_name = curpath; pop.f_target = NULL; pop.f_name_size = curpath_len; pop.f_uid = s.st_uid; pop.f_user = lookup_user(userhash, pop.f_uid); pop.f_gid = s.st_gid; pop.f_group = lookup_group(grouphash, pop.f_gid); pop.f_ctime = s.st_ctime; pop.f_mtime = s.st_mtime; pop.f_atime = s.st_atime; pop.f_mode = s.st_mode; pop.f_size = s.st_size; pop.f_dev = s.st_dev; pop.f_rdev = s.st_rdev; pop.f_ino = s.st_ino; pop.f_lnk = 0; if (gfunc_regexp(pregex_list, curpath, curpath_len)) { g_free(curpath); continue; } if (opt_nobackup && !strcmp(dent->d_name, NOBACKUP)) { /* return after seeing .nobackup */ if (opt_verbose > 0) { msg(_("%s found in '%s\'"), NOBACKUP, path); } /* remove all files found in this path */ rp.tree = t; rp.len = strlen(path); rp.path = path; g_tree_foreach(t, gfunc_remove_path, (gpointer)&rp); /* add .nobackup back in */ g_tree_insert(t, (gpointer) entry_dup(&pop), VALUE); g_free(dirstack); closedir(dir); return; } /* hardlinks */ if (s.st_nlink > 1) { if ((lnk = hlink(linkhash, &pop))) { pop.f_target = lnk; pop.f_lnk = 1; } } if (S_ISLNK(s.st_mode)) pop.f_target = slink(&pop); if (S_ISLNK(s.st_mode) || pop.f_lnk) { /* fix the name and the sizes */ pop.f_size = pop.f_name_size; pop.f_name_size += 4 + strlen(pop.f_target); } /* check for USRGRPINFO file */ if ( opt_chown && (cp = chown_parse(path, dent->d_name)) != NULL ) { pop.f_uid = cp->u; pop.f_gid = cp->g; pop.f_user = cp->user; pop.f_group = cp->group; } g_tree_insert(t, (gpointer) entry_dup(&pop), VALUE); if (pop.f_target != NULL) g_free(pop.f_target); g_free(curpath); continue; } else if(S_ISDIR(s.st_mode)) { /* one filesystem */ if (opt_onefilesystem && s.st_dev != current_dev) { msg(_("Not walking into different filesystem `%s\'"), curpath); g_free(curpath); continue; } /* Exclude list */ if (gfunc_regexp(pregex_list, curpath, curpath_len)) { g_free(curpath); continue; } dirstack[d] = g_malloc(sizeof(struct rdup)); dirstack[d]->f_name = g_strdup(curpath); dirstack[d]->f_target = NULL; dirstack[d]->f_name_size = curpath_len; dirstack[d]->f_uid = s.st_uid; dirstack[d]->f_user = lookup_user(userhash, s.st_uid); dirstack[d]->f_gid = s.st_gid; dirstack[d]->f_group = lookup_group(grouphash, s.st_gid); dirstack[d]->f_ctime = s.st_ctime; dirstack[d]->f_mtime = s.st_mtime; dirstack[d]->f_atime = s.st_atime; dirstack[d]->f_mode = s.st_mode; dirstack[d]->f_size = s.st_size; dirstack[d]->f_dev = s.st_dev; dirstack[d]->f_rdev = s.st_rdev; dirstack[d]->f_ino = s.st_ino; dirstack[d]->f_lnk = 0; /* check for USRGRPINFO file */ if ( opt_chown && (cp = chown_parse(curpath, NULL)) != NULL ) { dirstack[d]->f_uid = cp->u; dirstack[d]->f_gid = cp->g; dirstack[d]->f_user = cp->user; dirstack[d]->f_group = cp->group; } if (d++ % D_STACKSIZE == 0) { dirstack = g_realloc(dirstack, ++dstack_cnt * D_STACKSIZE * sizeof(struct rdup *)); } g_free(curpath); continue; } else { if (opt_verbose > 0) { msg(_("Neither file nor directory `%s\'"), curpath); } g_free(curpath); } } closedir(dir); if (opt_atime) { /* reset dirs atime */ if (d > 0 && opt_atime) { struct utimbuf ut; ut.actime = dirstack[d - 1]->f_atime; ut.modtime = dirstack[d - 1]->f_mtime; if (utime(dirstack[d - 1]->f_name, &ut) == -1) msg(_("Failed to reset atime: '%s\': %s"), dirstack[d - 1]->f_name, strerror(errno)); } } while (d > 0) { directory = dirstack[--d]; g_tree_insert(t, (gpointer) entry_dup(directory), VALUE); /* recurse */ /* potentially expensive operation. Better would be to when we hit * .nobackup to go up the tree and delete some nodes.... or not */ dir_crawl(t, linkhash, userhash, grouphash, directory->f_name); entry_free(directory); } g_free(dirstack); return; }
/* * or parse a new style rdup -c output entry * +- 0775 1000 1000 18 2947\n * /home/miekg/bin/tt * <contents> * * where contents is block based * 1BLOCK8192 * 8192 bytes of data * 1BLOCK15 * 15 bytes of data * 1BLOCK0 * the-end */ struct rdup *parse_entry(char *buf, size_t l) { struct rdup *e; struct stat s; gint i; char *n, *pos; e = g_malloc(sizeof(struct rdup)); e->f_ctime = 0; /* not used in rdup-* */ switch (opt_input) { case I_LIST: if (lstat(buf, &s) == -1) { msg(_("Could not stat path `%s\': %s"), buf, strerror(errno)); g_free(e); return NULL; } e->plusmin = PLUS; e->f_name = g_strdup(buf); e->f_name_size = strlen(buf); e->f_target = NULL; e->f_mode = s.st_mode; e->f_uid = s.st_uid; e->f_gid = s.st_gid; e->f_size = s.st_size; e->f_dev = s.st_dev; e->f_ino = s.st_ino; e->f_rdev = s.st_rdev; e->f_lnk = 0; e->f_ctime = s.st_ctime; e->f_mtime = s.st_mtime; e->f_atime = s.st_atime; e->f_hash = NULL; /* you will loose hardlink information here * as 'stat' cannot check this */ if (S_ISLNK(e->f_mode)) e->f_target = slink(e); break; case I_RDUP: if (strlen(buf) < LIST_MINSIZE) { msg(_("Corrupt entry `%s\' in input at line: %zd"), buf, l); g_free(e); return NULL; } /* defaults */ e->f_dev = 0; e->f_rdev = 0; e->f_ino = 0; e->f_lnk = 0; e->f_target = NULL; e->f_name = NULL; e->f_hash = NULL; /* 1st char should + or - */ if (buf[0] != '-' && buf[0] != '+') { msg(_ ("First character should \'-\' or \'+\', `%s\' at line: %zd"), buf, l); g_free(e); return NULL; } if (buf[0] == '+') e->plusmin = PLUS; if (buf[0] == '-') e->plusmin = MINUS; if (opt_output != O_RDUP && e->plusmin == MINUS) { msg(_ ("Removing files is not supported for any output except rdup")); g_free(e); return NULL; } /* type */ switch (buf[1]) { case '-': e->f_mode = S_IFREG; break; case 'd': e->f_mode = S_IFDIR; break; case 'l': e->f_mode = S_IFLNK; break; case 'h': e->f_mode = S_IFREG; e->f_lnk = 1; break; case 'c': e->f_mode = S_IFCHR; break; case 'b': e->f_mode = S_IFBLK; break; case 'p': e->f_mode = S_IFIFO; break; case 's': e->f_mode = S_IFSOCK; break; default: msg(_("Type must be one of d, l, h, -, c, b, p or s")); g_free(e); return NULL; } /* perm */ i = (buf[3] - 48) * 512 + (buf[4] - 48) * 64 + /* oct -> dec */ (buf[5] - 48) * 8 + (buf[6] - 48); if (i < 0 || i > 07777) { msg(_("Invalid permissions at line: %zd"), l); g_free(e); return NULL; } e->f_mode |= i; /* m_time */ n = strchr(buf + 8, ' '); if (!n) { msg(_("Malformed input for m_time at line: %zd"), l); g_free(e); return NULL; } e->f_mtime = (time_t) atol(buf + 8); pos = n + 1; /* uid */ n = strchr(pos, ' '); if (!n) { msg(_("Malformed input for uid at line: %zd"), l); g_free(e); return NULL; } else { *n = '\0'; } e->f_uid = atoi(pos); pos = n + 1; /* username */ n = strchr(pos, ' '); if (!n) { msg(_("Malformed input for user at line: %zd"), l); g_free(e); return NULL; } else { *n = '\0'; } e->f_user = g_strdup(pos); pos = n + 1; /* gid */ n = strchr(pos, ' '); if (!n) { msg(_("Malformed input for gid at line: %zd"), l); g_free(e); return NULL; } else { *n = '\0'; } e->f_gid = atoi(pos); pos = n + 1; /* groupname */ n = strchr(pos, ' '); if (!n) { msg(_("Malformed input for group at line: %zd"), l); g_free(e); return NULL; } else { *n = '\0'; } e->f_group = g_strdup(pos); pos = n + 1; /* pathname length */ n = strchr(pos, ' '); if (!n) { msg(_("Malformed input for path length at line: %zd"), l); g_free(e); return NULL; } e->f_name_size = atoi(pos); /* checks */ pos = n + 1; /* dev file? */ if (S_ISCHR(e->f_mode) || S_ISBLK(e->f_mode)) { int major, minor; n = strchr(pos, ','); if (!n) { msg("No major,minor found for device at line: %zd", l); g_free(e); return NULL; } *n = '\0'; major = atoi(pos); minor = atoi(n + 1); e->f_size = 0; e->f_rdev = makedev(major, minor); } else e->f_size = atoi(pos); break; } return e; }
/** * prepend path leading up to backup directory to the tree */ gboolean dir_prepend(GTree *t, char *path, GHashTable *u, GHashTable *g) { char *c; char *p; char *path2; size_t len; struct stat s; struct rdup e; path2 = g_strdup(path); len = strlen(path); /* add closing / */ if (path2[len - 1] != '/') { path2 = g_realloc(path2, len + 2); path2[len] = '/'; path2[len + 1] = '\0'; } for (p = path2 + 1; (c = strchr(p, '/')); p++) { *c = '\0'; if (lstat(path2, &s) != 0) { msg(_("Could not stat path `%s\': %s"), path2, strerror(errno)); g_free(path2); return FALSE; } e.f_name = path2; e.f_target = NULL; e.f_name_size = strlen(path2); e.f_uid = s.st_uid; e.f_user = lookup_user(u, e.f_uid); e.f_gid = s.st_gid; e.f_group = lookup_group(g, e.f_gid); e.f_ctime = s.st_ctime; e.f_mtime = s.st_mtime; e.f_atime = s.st_atime; e.f_mode = s.st_mode; e.f_size = s.st_size; e.f_dev = s.st_dev; e.f_rdev = s.st_rdev; e.f_ino = s.st_ino; e.f_lnk = 0; /* symlinks; also set the target */ if (S_ISLNK(s.st_mode)) { e.f_target = slink(&e); e.f_size = e.f_name_size; e.f_name_size += 4 + strlen(e.f_target); /* When we encounter a symlink on this level, it is very hard to make this * backup work, because the target may fall out of the backup. If this * is the case the entire backup fails. Gnu tar only show the symlink * and then stops. We do now the same, heance the return FALSE */ g_tree_insert(t, (gpointer) entry_dup(&e), VALUE); g_free(e.f_target); g_free(path2); return FALSE; } g_tree_insert(t, (gpointer) entry_dup(&e), VALUE); *c = '/'; p = c++; } g_free(path2); return TRUE; }