static struct ric_part_info *find_matching_bdev_mountpoint(struct path *path) { struct ric_part_info *part; struct path mnt_path; int i, ret, match = 0; for (i = 0; i < num_partitions; i++) { part = &bdev_info[i]; ret = kern_path(part->mnt_path, LOOKUP_FOLLOW, &mnt_path); if (ret) continue; if ((path->dentry == mnt_path.dentry) || is_subdir(path->dentry, mnt_path.dentry)) { match = 1; path_put(&mnt_path); break; } path_put(&mnt_path); } if (match) return part; return NULL; }
// When recursing into directories, do not want to check the include_ext list. static int file_is_included_no_incext(struct conf *conf, const char *fname) { int ret=0; int longest=0; int matching=0; struct strlist *l=NULL; struct strlist *best=NULL; if(in_exclude_ext(conf->excext, fname) || in_exclude_regex(conf->excreg, fname)) return 0; // Check include/exclude directories. for(l=conf->incexcdir; l; l=l->next) { //logp("try: %d %s\n", i, l->path); matching=is_subdir(l->path, fname); if(matching>longest) { longest=matching; best=l; } } //logp("best: %d\n", best); if(!best) ret=0; else ret=best->flag; //logp("return: %d\n", ret); return ret; }
// When recursing into directories, do not want to check the include_ext list. static int file_is_included_no_incext(struct conf **confs, const char *fname) { int ret=0; int longest=0; int matching=0; struct strlist *l=NULL; struct strlist *best=NULL; if(in_exclude_ext(get_strlist(confs[OPT_EXCEXT]), fname) || in_exclude_regex(get_strlist(confs[OPT_EXCREG]), fname)) return 0; // Check include/exclude directories. for(l=get_strlist(confs[OPT_INCEXCDIR]); l; l=l->next) { matching=is_subdir(l->path, fname); if(matching>longest) { longest=matching; best=l; } } if(!best) ret=0; else ret=best->flag; return ret; }
/* * git grep pathspecs are somewhat different from diff-tree pathspecs; * pathname wildcards are allowed. */ static int pathspec_matches(const char **paths, const char *name, int max_depth) { int namelen, i; if (!paths || !*paths) return accept_subdir(name, max_depth); namelen = strlen(name); for (i = 0; paths[i]; i++) { const char *match = paths[i]; int matchlen = strlen(match); const char *cp, *meta; if (is_subdir(name, namelen, match, matchlen, max_depth)) return 1; if (!fnmatch(match, name, 0)) return 1; if (name[namelen-1] != '/') continue; /* We are being asked if the directory ("name") is worth * descending into. * * Find the longest leading directory name that does * not have metacharacter in the pathspec; the name * we are looking at must overlap with that directory. */ for (cp = match, meta = NULL; cp - match < matchlen; cp++) { char ch = *cp; if (ch == '*' || ch == '[' || ch == '?') { meta = cp; break; } } if (!meta) meta = cp; /* fully literal */ if (namelen <= meta - match) { /* Looking at "Documentation/" and * the pattern says "Documentation/howto/", or * "Documentation/diff*.txt". The name we * have should match prefix. */ if (!memcmp(match, name, namelen)) return 1; continue; } if (meta - match < namelen) { /* Looking at "Documentation/howto/" and * the pattern says "Documentation/h*"; * match up to "Do.../h"; this avoids descending * into "Documentation/technical/". */ if (!memcmp(match, name, meta - match)) return 1; continue; } } return 0; }
/* * Return true if path is reachable from root * * namespace_sem is held, and mnt is attached */ static bool is_path_reachable(struct vfsmount *mnt, struct dentry *dentry, const struct path *root) { while (mnt != root->mnt && mnt->mnt_parent != mnt) { dentry = mnt->mnt_mountpoint; mnt = mnt->mnt_parent; } return mnt == root->mnt && is_subdir(dentry, root->dentry); }
/* * mount 'source_mnt' under the destination 'dest_mnt' at * dentry 'dest_dentry'. And propagate that mount to * all the peer and slave mounts of 'dest_mnt'. * Link all the new mounts into a propagation tree headed at * source_mnt. Also link all the new mounts using ->mnt_list * headed at source_mnt's ->mnt_list * * @dest_mnt: destination mount. * @dest_dentry: destination dentry. * @source_mnt: source mount. * @tree_list : list of heads of trees to be attached. */ int propagate_mnt(struct mount *dest_mnt, struct dentry *dest_dentry, struct mount *source_mnt, struct list_head *tree_list) { struct user_namespace *user_ns = current->nsproxy->mnt_ns->user_ns; struct mount *m, *child; int ret = 0; struct mount *prev_dest_mnt = dest_mnt; struct mount *prev_src_mnt = source_mnt; LIST_HEAD(tmp_list); LIST_HEAD(umount_list); for (m = propagation_next(dest_mnt, dest_mnt); m; m = propagation_next(m, dest_mnt)) { int type; struct mount *source; if (IS_MNT_NEW(m)) continue; source = get_source(m, prev_dest_mnt, prev_src_mnt, &type); /* Notice when we are propagating across user namespaces */ if (m->mnt_ns->user_ns != user_ns) type |= CL_UNPRIVILEGED; child = copy_tree(source, source->mnt.mnt_root, type); if (IS_ERR(child)) { ret = PTR_ERR(child); list_splice(tree_list, tmp_list.prev); goto out; } if (is_subdir(dest_dentry, m->mnt.mnt_root)) { mnt_set_mountpoint(m, dest_dentry, child); list_add_tail(&child->mnt_hash, tree_list); } else { /* * This can happen if the parent mount was bind mounted * on some subdirectory of a shared/slave mount. */ list_add_tail(&child->mnt_hash, &tmp_list); } prev_dest_mnt = m; prev_src_mnt = child; } out: br_write_lock(&vfsmount_lock); while (!list_empty(&tmp_list)) { child = list_first_entry(&tmp_list, struct mount, mnt_hash); umount_tree(child, 0, &umount_list); } br_write_unlock(&vfsmount_lock); release_mounts(&umount_list); return ret; }
/* * mount 'source_mnt' under the destination 'dest_mnt' at * dentry 'dest_dentry'. And propagate that mount to * all the peer and slave mounts of 'dest_mnt'. * Link all the new mounts into a propagation tree headed at * source_mnt. Also link all the new mounts using ->mnt_list * headed at source_mnt's ->mnt_list * * @dest_mnt: destination mount. * @dest_dentry: destination dentry. * @source_mnt: source mount. * @tree_list : list of heads of trees to be attached. */ int propagate_mnt(struct mount *dest_mnt, struct mountpoint *dest_mp, struct mount *source_mnt, struct hlist_head *tree_list) { struct user_namespace *user_ns = current->nsproxy->mnt_ns->user_ns; struct mount *m, *child; int ret = 0; struct mount *prev_dest_mnt = dest_mnt; struct mount *prev_src_mnt = source_mnt; HLIST_HEAD(tmp_list); for (m = propagation_next(dest_mnt, dest_mnt); m; m = propagation_next(m, dest_mnt)) { int type; struct mount *source; if (IS_MNT_NEW(m)) continue; source = get_source(m, prev_dest_mnt, prev_src_mnt, &type); /* Notice when we are propagating across user namespaces */ if (m->mnt_ns->user_ns != user_ns) type |= CL_UNPRIVILEGED; child = copy_tree(source, source->mnt.mnt_root, type); if (IS_ERR(child)) { ret = PTR_ERR(child); tmp_list = *tree_list; tmp_list.first->pprev = &tmp_list.first; INIT_HLIST_HEAD(tree_list); goto out; } if (is_subdir(dest_mp->m_dentry, m->mnt.mnt_root)) { mnt_set_mountpoint(m, dest_mp, child); hlist_add_head(&child->mnt_hash, tree_list); } else { /* * This can happen if the parent mount was bind mounted * on some subdirectory of a shared/slave mount. */ hlist_add_head(&child->mnt_hash, &tmp_list); } prev_dest_mnt = m; prev_src_mnt = child; } out: lock_mount_hash(); while (!hlist_empty(&tmp_list)) { child = hlist_entry(tmp_list.first, struct mount, mnt_hash); umount_tree(child, 0); } unlock_mount_hash(); return ret; }
static int countsubdirs(struct dnode **dn, int nfiles) { int i, subdirs; if (dn == NULL || nfiles < 1) return 0; subdirs = 0; for (i = 0; i < nfiles; i++) if (is_subdir(dn[i])) subdirs++; return subdirs; }
static int propagate_one(struct mount *m) { struct mount *child; int type; /* skip ones added by this propagate_mnt() */ if (IS_MNT_NEW(m)) return 0; /* skip if mountpoint isn't covered by it */ if (!is_subdir(mp->m_dentry, m->mnt.mnt_root)) return 0; if (m->mnt_group_id == last_dest->mnt_group_id) { type = CL_MAKE_SHARED; } else { struct mount *n, *p; for (n = m; ; n = p) { p = n->mnt_master; if (p == dest_master || IS_MNT_MARKED(p)) { while (last_dest->mnt_master != p) { last_source = last_source->mnt_master; last_dest = last_source->mnt_parent; } if (n->mnt_group_id != last_dest->mnt_group_id) { last_source = last_source->mnt_master; last_dest = last_source->mnt_parent; } break; } } type = CL_SLAVE; /* beginning of peer group among the slaves? */ if (IS_MNT_SHARED(m)) type |= CL_MAKE_SHARED; } /* Notice when we are propagating across user namespaces */ if (m->mnt_ns->user_ns != user_ns) type |= CL_UNPRIVILEGED; child = copy_tree(last_source, last_source->mnt.mnt_root, type); if (IS_ERR(child)) return PTR_ERR(child); child->mnt.mnt_flags &= ~MNT_LOCKED; mnt_set_mountpoint(m, mp, child); last_dest = m; last_source = child; if (m->mnt_master != dest_master) { read_seqlock_excl(&mount_lock); SET_MNT_MARK(m->mnt_master); read_sequnlock_excl(&mount_lock); } hlist_add_head(&child->mnt_hash, list); return 0; }
static int restore_ent(const char *client, struct sbuf *sb, struct sbuf ***sblist, int *scount, struct bu *arr, int a, int i, const char *tmppath1, const char *tmppath2, enum action act, char status, struct config *cconf, struct cntr *cntr, struct cntr *p1cntr) { int s=0; int ret=0; // Check if we have any directories waiting to be restored. for(s=(*scount)-1; s>=0; s--) { if(is_subdir((*sblist)[s]->path, sb->path)) { // We are still in a subdir. //printf(" subdir (%s %s)\n", // (*sblist)[s]->path, sb->path); break; } else { // Can now restore sblist[s] because nothing else is // fiddling in a subdirectory. if(restore_sbuf((*sblist)[s], arr, a, i, tmppath1, tmppath2, act, client, status, p1cntr, cntr, cconf)) { ret=-1; break; } else if(del_from_sbuf_arr(sblist, scount)) { ret=-1; break; } } } /* If it is a directory, need to remember it and restore it later, so that the permissions come out right. */ /* Meta data of directories will also have the stat stuff set to be a directory, so will also come out at the end. */ if(!ret && S_ISDIR(sb->statp.st_mode)) { if(add_to_sbuf_arr(sblist, sb, scount)) ret=-1; // Wipe out sb, without freeing up all the strings inside it, // which have been added to sblist. init_sbuf(sb); } else if(!ret && restore_sbuf(sb, arr, a, i, tmppath1, tmppath2, act, client, status, p1cntr, cntr, cconf)) ret=-1; return ret; }
/* * mount 'source_mnt' under the destination 'dest_mnt' at * dentry 'dest_dentry'. And propagate that mount to * all the peer and slave mounts of 'dest_mnt'. * Link all the new mounts into a propagation tree headed at * source_mnt. Also link all the new mounts using ->mnt_list * headed at source_mnt's ->mnt_list * * @dest_mnt: destination mount. * @dest_dentry: destination dentry. * @source_mnt: source mount. * @tree_list : list of heads of trees to be attached. */ int propagate_mnt(struct vfsmount *dest_mnt, struct dentry *dest_dentry, struct vfsmount *source_mnt, struct list_head *tree_list) { struct vfsmount *m, *child; int ret = 0; struct vfsmount *prev_dest_mnt = dest_mnt; struct vfsmount *prev_src_mnt = source_mnt; LIST_HEAD(tmp_list); LIST_HEAD(umount_list); for (m = propagation_next(dest_mnt, dest_mnt); m; m = propagation_next(m, dest_mnt)) { int type; struct vfsmount *source; if (IS_MNT_NEW(m)) continue; source = get_source(m, prev_dest_mnt, prev_src_mnt, &type); if (!(child = copy_tree(source, source->mnt_root, type))) { ret = -ENOMEM; list_splice(tree_list, tmp_list.prev); goto out; } if (is_subdir(dest_dentry, m->mnt_root)) { mnt_set_mountpoint(m, dest_dentry, child); list_add_tail(&child->mnt_hash, tree_list); } else { /* * This can happen if the parent mount was bind mounted * on some subdirectory of a shared/slave mount. */ list_add_tail(&child->mnt_hash, &tmp_list); } prev_dest_mnt = m; prev_src_mnt = child; } out: spin_lock(&vfsmount_lock); while (!list_empty(&tmp_list)) { child = list_entry(tmp_list.next, struct vfsmount, mnt_hash); list_del_init(&child->mnt_hash); umount_tree(child, 0, &umount_list); } spin_unlock(&vfsmount_lock); release_mounts(&umount_list); return ret; }
// This decides which directories to start backing up, and which // are subdirectories which don't need to be started separately. static int finalise_start_dirs(struct conf **c) { struct strlist *s=NULL; struct strlist *last_ie=NULL; struct strlist *last_sd=NULL; // Make sure that the startdir list starts empty, or chaos will ensue. conf_free_content(c[OPT_STARTDIR]); for(s=get_strlist(c[OPT_INCLUDE]); s; s=s->next) { #ifdef HAVE_WIN32 convert_backslashes(&s->path); #endif if(path_checks(s->path, "ERROR: Please use absolute include/exclude paths.\n")) return -1; // Ensure that we do not backup the same directory twice. if(last_ie && !strcmp(s->path, last_ie->path)) { logp("Directory appears twice in conf: %s\n", s->path); return -1; } // If it is not a subdirectory of the most recent start point, // we have found another start point. if(!get_strlist(c[OPT_STARTDIR]) || !last_sd || !is_subdir(last_sd->path, s->path)) { // Do not use strlist_add_sorted, because last_sd is // relying on incexcdir already being sorted. if(add_to_strlist(c[OPT_STARTDIR], s->path, s->flag)) return -1; last_sd=s; } else { // If it is not a starting directory, it should at // least be included as a cross_filesystem entry. if(add_to_cross_filesystem(c, s->path)) return -1; } last_ie=s; } return 0; }
int propagate_mnt(struct mount *dest_mnt, struct dentry *dest_dentry, struct mount *source_mnt, struct list_head *tree_list) { struct mount *m, *child; int ret = 0; struct mount *prev_dest_mnt = dest_mnt; struct mount *prev_src_mnt = source_mnt; LIST_HEAD(tmp_list); LIST_HEAD(umount_list); for (m = propagation_next(dest_mnt, dest_mnt); m; m = propagation_next(m, dest_mnt)) { int type; struct mount *source; if (IS_MNT_NEW(m)) continue; source = get_source(m, prev_dest_mnt, prev_src_mnt, &type); if (!(child = copy_tree(source, source->mnt.mnt_root, type))) { ret = -ENOMEM; list_splice(tree_list, tmp_list.prev); goto out; } if (is_subdir(dest_dentry, m->mnt.mnt_root)) { mnt_set_mountpoint(m, dest_dentry, child); list_add_tail(&child->mnt_hash, tree_list); } else { list_add_tail(&child->mnt_hash, &tmp_list); } prev_dest_mnt = m; prev_src_mnt = child; } out: br_write_lock(vfsmount_lock); while (!list_empty(&tmp_list)) { child = list_first_entry(&tmp_list, struct mount, mnt_hash); umount_tree(child, 0, &umount_list); } br_write_unlock(vfsmount_lock); release_mounts(&umount_list); return ret; }
int au_is_subdir(struct dentry *d1, struct dentry *d2) { int err; #ifndef AuUse_ISSUBDIR int i, j; struct au_dcsub_pages dpages; struct au_dpage *dpage; struct dentry **dentries; #endif LKTRTrace("%.*s, %.*s\n", DLNPair(d1), DLNPair(d2)); #ifdef AuUse_ISSUBDIR spin_lock(&dcache_lock); err = is_subdir(d1, d2); spin_unlock(&dcache_lock); #else err = au_dpages_init(&dpages, GFP_KERNEL); if (unlikely(err)) goto out; err = au_dcsub_pages_rev(&dpages, d1, /*do_include*/1, NULL, NULL); if (unlikely(err)) goto out_dpages; for (i = dpages.ndpage - 1; !err && i >= 0; i--) { dpage = dpages.dpages + i; dentries = dpage->dentries; for (j = dpage->ndentry - 1; !err && j >= 0; j--) { struct dentry *d; d = dentries[j]; //Dbg("d %.*s\n", DLNPair(d)); err = (d == d2); } } out_dpages: au_dpages_free(&dpages); out: #endif TraceErr(err); return err; }
/* * rename uses retrying to avoid race-conditions: at least they should be minimal. * it tries to allocate all the blocks, then sanity-checks, and if the sanity- * checks fail, it tries to restart itself again. Very practical - no changes * are done until we know everything works ok.. and then all the changes can be * done in one fell swoop when we have claimed all the buffers needed. * * Anybody can rename anything with this: the permission checks are left to the * higher-level routines. */ static int do_minix_rename(struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir, struct dentry *new_dentry) { struct inode * old_inode, * new_inode; struct buffer_head * old_bh, * new_bh, * dir_bh; struct minix_dir_entry * old_de, * new_de; struct minix_sb_info * info; int retval; info = &old_dir->i_sb->u.minix_sb; goto start_up; try_again: brelse(old_bh); brelse(new_bh); brelse(dir_bh); current->counter = 0; schedule(); start_up: old_inode = new_inode = NULL; old_bh = new_bh = dir_bh = NULL; old_bh = minix_find_entry(old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de); retval = -ENOENT; if (!old_bh) goto end_rename; old_inode = old_dentry->d_inode; retval = -EPERM; new_inode = new_dentry->d_inode; new_bh = minix_find_entry(new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_de); if (new_bh) { if (!new_inode) { brelse(new_bh); new_bh = NULL; } } if (new_inode == old_inode) { retval = 0; goto end_rename; } if (S_ISDIR(old_inode->i_mode)) { retval = -EINVAL; if (is_subdir(new_dentry, old_dentry)) goto end_rename; if (new_inode) { /* Prune any children before testing for busy */ if (new_dentry->d_count > 1) shrink_dcache_parent(new_dentry); retval = -EBUSY; if (new_dentry->d_count > 1) retval = -ENOTEMPTY; if (!empty_dir(new_inode)) goto end_rename; retval = -EBUSY; } retval = -EIO; dir_bh = minix_bread(old_inode,0,0); if (!dir_bh) goto end_rename; if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino) goto end_rename; retval = -EMLINK; if (!new_inode && new_dir->i_nlink >= info->s_link_max) goto end_rename; } if (!new_bh) { retval = minix_add_entry(new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_bh, &new_de); if (retval) goto end_rename; } /* sanity checking before doing the rename - avoid races */ if (new_inode && (new_de->inode != new_inode->i_ino)) goto try_again; if (new_de->inode && !new_inode) goto try_again; if (old_de->inode != old_inode->i_ino) goto try_again; /* ok, that's it */ old_de->inode = 0; new_de->inode = old_inode->i_ino; old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; mark_inode_dirty(old_dir); old_dir->i_version = ++event; new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME; mark_inode_dirty(new_dir); new_dir->i_version = ++event; if (new_inode) { new_inode->i_nlink--; new_inode->i_ctime = CURRENT_TIME; mark_inode_dirty(new_inode); } mark_buffer_dirty(old_bh, 1); mark_buffer_dirty(new_bh, 1); if (dir_bh) { PARENT_INO(dir_bh->b_data) = new_dir->i_ino; mark_buffer_dirty(dir_bh, 1); old_dir->i_nlink--; mark_inode_dirty(old_dir); if (new_inode) { new_inode->i_nlink--; mark_inode_dirty(new_inode); } else { new_dir->i_nlink++; mark_inode_dirty(new_dir); } } /* Update the dcache */ d_move(old_dentry, new_dentry); retval = 0; end_rename: brelse(dir_bh); brelse(old_bh); brelse(new_bh); return retval; }
int restore_ent(struct asfd *asfd, struct sbuf **sb, struct slist *slist, struct bu *bu, enum action act, struct sdirs *sdirs, enum cntr_status cntr_status, struct conf **cconfs, struct sbuf *need_data, int *last_ent_was_dir, const char *manifest) { int ret=-1; struct sbuf *xb; if(!(*sb)->path.buf) { logp("Got NULL path!\n"); return -1; } // Check if we have any directories waiting to be restored. while((xb=slist->head)) { if(is_subdir(xb->path.buf, (*sb)->path.buf)) { // We are still in a subdir. break; } else { // Can now restore xb because nothing else is fiddling // in a subdirectory. if(restore_sbuf(asfd, xb, bu, act, sdirs, cntr_status, cconfs, need_data, manifest, slist)) goto end; slist->head=xb->next; sbuf_free(&xb); } } /* If it is a directory, need to remember it and restore it later, so that the permissions come out right. */ /* Meta data of directories will also have the stat stuff set to be a directory, so will also come out at the end. */ /* FIX THIS: for Windows, need to read and remember the blocks that go with the directories. Probably have to do the same for metadata that goes with directories. */ if(S_ISDIR((*sb)->statp.st_mode)) { // Add to the head of the list instead of the tail. (*sb)->next=slist->head; slist->head=*sb; *last_ent_was_dir=1; // Allocate a new sb. if(!(*sb=sbuf_alloc(get_protocol(cconfs)))) goto end; } else { *last_ent_was_dir=0; if(restore_sbuf(asfd, *sb, bu, act, sdirs, cntr_status, cconfs, need_data, manifest, slist)) goto end; } ret=0; end: return ret; }
/* * rename uses retrying to avoid race-conditions: at least they should be minimal. * it tries to allocate all the blocks, then sanity-checks, and if the sanity- * checks fail, it tries to restart itself again. Very practical - no changes * are done until we know everything works ok.. and then all the changes can be * done in one fell swoop when we have claimed all the buffers needed. * * Anybody can rename anything with this: the permission checks are left to the * higher-level routines. */ static int do_sysv_rename(struct inode * old_dir, struct dentry * old_dentry, struct inode * new_dir, struct dentry * new_dentry) { struct inode * old_inode, * new_inode; struct buffer_head * old_bh, * new_bh, * dir_bh; struct sysv_dir_entry * old_de, * new_de; int retval; goto start_up; try_again: brelse(old_bh); brelse(new_bh); brelse(dir_bh); current->counter = 0; schedule(); start_up: old_inode = new_inode = NULL; old_bh = new_bh = dir_bh = NULL; old_bh = sysv_find_entry(old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de); retval = -ENOENT; if (!old_bh) goto end_rename; old_inode = old_dentry->d_inode; /* don't cross mnt-points */ retval = -EPERM; new_inode = new_dentry->d_inode; new_bh = sysv_find_entry(new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_de); if (new_bh) { if (!new_inode) { brelse(new_bh); new_bh = NULL; } } if (new_inode == old_inode) { retval = 0; goto end_rename; } if (S_ISDIR(old_inode->i_mode)) { retval = -EINVAL; if (is_subdir(new_dentry, old_dentry)) goto end_rename; if (new_inode) { if (new_dentry->d_count > 1) shrink_dcache_parent(new_dentry); retval = -EBUSY; if (new_dentry->d_count > 1) goto end_rename; retval = -ENOTEMPTY; if (!empty_dir(new_inode)) goto end_rename; } retval = -EIO; dir_bh = sysv_file_bread(old_inode, 0, 0); if (!dir_bh) goto end_rename; if (PARENT_INO(dir_bh->b_data) != old_dir->i_ino) goto end_rename; retval = -EMLINK; if (!new_inode && new_dir->i_nlink >= new_dir->i_sb->sv_link_max) goto end_rename; } if (!new_bh) { retval = sysv_add_entry(new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_bh, &new_de); if (retval) goto end_rename; } /* sanity checking before doing the rename - avoid races */ if (new_inode && (new_de->inode != new_inode->i_ino)) goto try_again; if (new_de->inode && !new_inode) goto try_again; if (old_de->inode != old_inode->i_ino) goto try_again; /* ok, that's it */ old_de->inode = 0; new_de->inode = old_inode->i_ino; old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; mark_inode_dirty(old_dir); new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME; mark_inode_dirty(new_dir); if (new_inode) { new_inode->i_nlink--; new_inode->i_ctime = CURRENT_TIME; mark_inode_dirty(new_inode); } mark_buffer_dirty(old_bh, 1); mark_buffer_dirty(new_bh, 1); if (dir_bh) { PARENT_INO(dir_bh->b_data) = new_dir->i_ino; mark_buffer_dirty(dir_bh, 1); old_dir->i_nlink--; mark_inode_dirty(old_dir); if (new_inode) { new_inode->i_nlink--; mark_inode_dirty(new_inode); } else { new_dir->i_nlink++; mark_inode_dirty(new_dir); } } d_move(old_dentry, new_dentry); retval = 0; end_rename: brelse(dir_bh); brelse(old_bh); brelse(new_bh); return retval; }
/* * process, that is going to call fix_nodes/do_balance must hold only * one path. If it holds 2 or more, it can get into endless waiting in * get_empty_nodes or its clones */ static int do_reiserfs_rename (struct reiserfs_transaction_handle *th, struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir, struct dentry *new_dentry) { int retval; struct path old_entry_path, new_entry_path, dot_dot_entry_path; struct reiserfs_dir_entry old_de, new_de, dot_dot_de; struct inode * old_inode, * new_inode; int new_entry_added = 0; init_path (&old_entry_path); init_path (&new_entry_path); init_path (&dot_dot_entry_path); goto start_up; try_again: current->policy |= SCHED_YIELD; schedule(); start_up: old_inode = new_inode = NULL; dot_dot_de.de_bh = 0; new_de.de_bh = 0; /* * look for the old name in old directory */ retval = -ENOENT; old_de.de_gen_number_bit_string = 0; if (reiserfs_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_entry_path, &old_de) == POSITION_NOT_FOUND) goto end_rename; pathrelse (&old_entry_path); old_inode = old_dentry->d_inode; retval = -EPERM; if ((old_dir->i_mode & S_ISVTX) && current->fsuid != old_inode->i_uid && current->fsuid != old_dir->i_uid && !fsuser()) goto end_rename; new_inode = new_dentry->d_inode; /* look for the new entry in target directory */ new_de.de_gen_number_bit_string = 0; if (reiserfs_find_entry (new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_entry_path, &new_de) == POSITION_FOUND) { if (!new_inode) { printk ("do_reiserfs_rename: new entry found, inode == 0 though\n"); } /* this entry already exists, we can just set key of object */ new_entry_added = 1; } else { #ifdef REISERFS_CHECK if (new_entry_added) { if (new_de.de_namelen != new_dentry->d_name.len || memcmp (new_de.de_name, new_dentry->d_name.name, new_de.de_namelen) || de_visible (new_de.de_deh)) reiserfs_panic (old_dir->i_sb, "vs-7045: reiserfs_rename: suspicious entry found"); } #endif /* REISERFS_CHECK */ } pathrelse (&new_entry_path); if (new_inode == old_inode) { retval = 0; goto end_rename; } if (new_inode && S_ISDIR(new_inode->i_mode)) { /* new name exists and points to directory */ retval = -EISDIR; if (!S_ISDIR(old_inode->i_mode)) goto end_rename; retval = -EINVAL; if (is_subdir (new_dentry, old_dentry)) goto end_rename; retval = -ENOTEMPTY; if (!reiserfs_empty_dir (new_inode)) goto end_rename; retval = -EBUSY; if (new_inode->i_count > 1) goto end_rename; } retval = -EPERM; if (new_inode && (new_dir->i_mode & S_ISVTX) && current->fsuid != new_inode->i_uid && current->fsuid != new_dir->i_uid && !fsuser()) goto end_rename; if (S_ISDIR(old_inode->i_mode)) { /* old name points to directory */ retval = -ENOTDIR; if (new_inode && !S_ISDIR(new_inode->i_mode)) goto end_rename; retval = -EINVAL; if (is_subdir(new_dentry, old_dentry)) goto end_rename; retval = -EIO; /* directory is renamed, its parent directory will be changed, so find ".." entry */ dot_dot_de.de_gen_number_bit_string = 0; if (reiserfs_find_entry (old_inode, "..", 2, &dot_dot_entry_path, &dot_dot_de) == POSITION_NOT_FOUND) goto end_rename; if (dot_dot_de.de_objectid != old_dir->i_ino) goto end_rename; pathrelse (&dot_dot_entry_path); retval = -EMLINK; if (!new_inode && new_dir->i_nlink >= REISERFS_LINK_MAX) goto end_rename; } if (new_entry_added == 0) { /* add new entry if we did not do it, but do not mark it as visible */ retval = reiserfs_add_entry (th, new_dir, new_dentry->d_name.name, new_dentry->d_name.len, INODE_PKEY (old_inode), &new_de, 0); if (retval) goto end_rename; if_in_ram_update_sd (th, new_dir); new_entry_added = 1; goto try_again; } /* * look for old name, new name and ".." when renaming directories again */ if (reiserfs_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_entry_path, &old_de) == POSITION_NOT_FOUND) reiserfs_panic (old_dir->i_sb, "vs-7050: reiserfs_rename: old name not found"); if (reiserfs_find_entry (new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_entry_path, &new_de) == POSITION_NOT_FOUND) reiserfs_panic (old_dir->i_sb, "vs-7055: reiserfs_rename: new name not found"); if (S_ISDIR(old_inode->i_mode) && reiserfs_find_entry (old_inode, "..", 2, &dot_dot_entry_path, &dot_dot_de) == POSITION_NOT_FOUND) reiserfs_panic (old_dir->i_sb, "vs-7060: reiserfs_rename: \"..\" name not found"); /* sanity checking before doing the rename - avoid races */ if (!entry_points_to_object (new_dentry->d_name.name, new_dentry->d_name.len, &new_de, new_inode)) goto try_again; if (!entry_points_to_object (old_dentry->d_name.name, old_dentry->d_name.len, &old_de, old_inode)) /* go to re-looking for old entry */ goto try_again; if (S_ISDIR(old_inode->i_mode) && !entry_points_to_object ("..", 2, &dot_dot_de, old_dir)) /* go to re-looking for ".." entry of renamed directory */ goto try_again; /* ok, all the changes can be done in one fell swoop when we have claimed all the buffers needed.*/ /* make old name hidden */ mark_de_hidden (old_de.de_deh); journal_mark_dirty(th, old_dir->i_sb, old_de.de_bh) ; /* make new name visible and set key of old object (if entry existed, it is already visible, if not, key is correct already) */ mark_de_visible (new_de.de_deh); new_de.de_deh->deh_dir_id = INODE_PKEY (old_inode)->k_dir_id; new_de.de_deh->deh_objectid = INODE_PKEY (old_inode)->k_objectid; journal_mark_dirty(th, old_dir->i_sb, new_de.de_bh) ; old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; if_in_ram_update_sd (th, old_dir); new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME; if_in_ram_update_sd (th, new_dir); if (new_inode) { new_inode->i_nlink--; new_inode->i_ctime = CURRENT_TIME; if_in_ram_update_sd (th, new_inode); } if (dot_dot_de.de_bh) { set_ino_in_dir_entry (&dot_dot_de, INODE_PKEY (new_dir)); journal_mark_dirty(th, old_dir->i_sb, dot_dot_de.de_bh) ; old_dir->i_nlink--; if_in_ram_update_sd (th, old_dir); if (new_inode) { new_inode->i_nlink--; if_in_ram_update_sd (th, new_inode); } else { new_dir->i_nlink++; if_in_ram_update_sd (th, new_dir); } } /* ok, renaming done */ decrement_counters_in_path (&new_entry_path); decrement_counters_in_path (&dot_dot_entry_path); /* remove old name (it is hidden now) */ if (reiserfs_cut_from_item (th, old_dir, old_dir->i_sb, &old_entry_path, &(old_de.de_entry_num), &(old_de.de_entry_key), 0, PRESERVE_RENAMING) == 0) printk ("reiserfs_rename: could not remove old name\n"); else { old_dir->i_size -= DEH_SIZE + old_de.de_entrylen; old_dir->i_blocks = old_dir->i_size / 512 + ((old_dir->i_size % 512) ? 1 : 0); if_in_ram_update_sd (th, old_dir); } /* Update the dcache */ d_move(old_dentry, new_dentry); retval = 0; end_rename: pathrelse (&old_entry_path); return retval; }
static void test_is_subdir(void) { // Sensible exaples are sensible g_assert_true(is_subdir("/dir/subdir", "/dir/")); g_assert_true(is_subdir("/dir/subdir", "/dir")); g_assert_true(is_subdir("/dir/", "/dir")); g_assert_true(is_subdir("/dir", "/dir")); // Also without leading slash g_assert_true(is_subdir("dir/subdir", "dir/")); g_assert_true(is_subdir("dir/subdir", "dir")); g_assert_true(is_subdir("dir/", "dir")); g_assert_true(is_subdir("dir", "dir")); // Some more ideas g_assert_true(is_subdir("//", "/")); g_assert_true(is_subdir("/", "/")); g_assert_true(is_subdir("", "")); // but this is not true g_assert_false(is_subdir("/", "/dir")); g_assert_false(is_subdir("/rid", "/dir")); g_assert_false(is_subdir("/different/dir", "/dir")); g_assert_false(is_subdir("/", "")); }
static int process_files_in_directory(struct asfd *asfd, struct dirent **nl, int count, int *rtn_stat, char **link, size_t len, size_t *link_len, struct conf *conf, FF_PKT *ff_pkt, dev_t our_device) { int m=0; for(m=0; m<count; m++) { size_t i; char *p=NULL; char *q=NULL; p=nl[m]->d_name; if(strlen(p)+len>=*link_len) { *link_len=len+strlen(p)+1; if(!(*link=(char *) realloc_w(*link, (*link_len)+1, __func__))) return -1; } q=(*link)+len; for(i=0; i<strlen(nl[m]->d_name); i++) *q++=*p++; *q=0; ff_pkt->flen=i; if(file_is_included_no_incext(conf, *link)) { *rtn_stat=find_files(asfd, ff_pkt, conf, *link, our_device, false); } else { struct strlist *x; // Excluded, but there might be a subdirectory that is // included. for(x=conf->incexcdir; x; x=x->next) { if(x->flag && is_subdir(*link, x->path)) { struct strlist *y; if((*rtn_stat=find_files(asfd, ff_pkt, conf, x->path, our_device, false))) break; // Now need to skip subdirectories of // the thing that we just stuck in // find_one_file(), or we might get // some things backed up twice. for(y=x->next; y; y=y->next) if(is_subdir(x->path, y->path)) y=y->next; } } } free_v((void **)&(nl[m])); if(*rtn_stat) break; } return 0; }
// a = length of struct bu array // i = position to restore from static int restore_manifest(struct bu *arr, int a, int i, const char *tmppath1, const char *tmppath2, regex_t *regex, enum action act, const char *client, struct cntr *p1cntr, struct cntr *cntr, struct config *cconf, bool all) { int ret=0; gzFile zp=NULL; char *manifest=NULL; char *datadir=NULL; FILE *logfp=NULL; char *logpath=NULL; char *logpathz=NULL; // For sending status information up to the server. char status=STATUS_RESTORING; if(act==ACTION_RESTORE) status=STATUS_RESTORING; else if(act==ACTION_VERIFY) status=STATUS_VERIFYING; if( (act==ACTION_RESTORE && !(logpath=prepend_s(arr[i].path, "restorelog", strlen("restorelog")))) || (act==ACTION_RESTORE && !(logpathz=prepend_s(arr[i].path, "restorelog.gz", strlen("restorelog.gz")))) || (act==ACTION_VERIFY && !(logpath=prepend_s(arr[i].path, "verifylog", strlen("verifylog")))) || (act==ACTION_VERIFY && !(logpathz=prepend_s(arr[i].path, "verifylog.gz", strlen("verifylog.gz")))) || !(manifest=prepend_s(arr[i].path, "manifest.gz", strlen("manifest.gz")))) { log_and_send("out of memory"); ret=-1; } else if(!(logfp=open_file(logpath, "ab")) || set_logfp(logfp)) { char msg[256]=""; snprintf(msg, sizeof(msg), "could not open log file: %s", logpath); log_and_send(msg); ret=-1; } else if(!(zp=gzopen_file(manifest, "rb"))) { log_and_send("could not open manifest"); ret=-1; } else { char cmd; int quit=0; size_t len=0; struct sbuf sb; // For out-of-sequence directory restoring so that the // timestamps come out right: int s=0; int scount=0; struct sbuf **sblist=NULL; init_sbuf(&sb); while(!quit) { int ars=0; char *buf=NULL; if(async_read_quick(&cmd, &buf, &len)) { logp("read quick error\n"); ret=-1; quit++; break; } if(buf) { //logp("got read quick\n"); if(cmd==CMD_WARNING) { logp("WARNING: %s\n", buf); do_filecounter(cntr, cmd, 0); free(buf); buf=NULL; continue; } else if(cmd==CMD_INTERRUPT) { // Client wanted to interrupt the // sending of a file. But if we are // here, we have already moved on. // Ignore. free(buf); buf=NULL; continue; } else { logp("unexpected cmd from client: %c:%s\n", cmd, buf); free(buf); buf=NULL; ret=-1; quit++; break; } } if((ars=sbuf_fill(NULL, zp, &sb, cntr))) { if(ars<0) ret=-1; // ars==1 means end ok quit++; } else { if(check_regex(regex, sb.path)) { // Check if we have any directories waiting // to be restored. for(s=scount-1; s>=0; s--) { if(is_subdir(sblist[s]->path, sb.path)) { // We are still in a subdir. //printf(" subdir (%s %s)\n", sblist[s]->path, sb.path); break; } else { // Can now restore sblist[s] // because nothing else is // fiddling in a subdirectory. if(restore_sbuf(sblist[s], arr, a, i, tmppath1, tmppath2, act, client, status, p1cntr, cntr, cconf)) { ret=-1; quit++; break; } else if(del_from_sbuf_arr( &sblist, &scount)) { ret=-1; quit++; break; } } } /* If it is a directory, need to remember it and restore it later, so that the permissions come out right. */ /* Meta data of directories will also have the stat stuff set to be a directory, so will also come out at the end. */ if(!ret && S_ISDIR(sb.statp.st_mode)) { if(add_to_sbuf_arr(&sblist, &sb, &scount)) { ret=-1; quit++; } // Wipe out sb, without freeing up // all the strings inside it, which // have been added to sblist. init_sbuf(&sb); } else if(!ret && restore_sbuf(&sb, arr, a, i, tmppath1, tmppath2, act, client, status, p1cntr, cntr, cconf)) { ret=-1; quit++; } } } free_sbuf(&sb); } gzclose_fp(&zp); // Restore any directories that are left in the list. if(!ret) for(s=scount-1; s>=0; s--) { if(restore_sbuf(sblist[s], arr, a, i, tmppath1, tmppath2, act, client, status, p1cntr, cntr, cconf)) { ret=-1; break; } } free_sbufs(sblist, scount); if(!ret && !all) ret=do_restore_end(act, cntr); print_endcounter(cntr); print_filecounters(p1cntr, cntr, act, 0); reset_filecounter(p1cntr); reset_filecounter(cntr); } set_logfp(NULL); compress_file(logpath, logpathz, cconf); if(manifest) free(manifest); if(datadir) free(datadir); if(logpath) free(logpath); if(logpathz) free(logpathz); return ret; }
static int restore_ent(struct asfd *asfd, struct sbuf **sb, struct slist *slist, enum action act, enum cstat_status status, struct conf *conf, int *need_data, int *last_ent_was_dir) { int ret=-1; struct sbuf *xb; if(!(*sb)->path.buf) { logp("Got NULL path!\n"); return -1; } //printf("want to restore: %s\n", (*sb)->path.buf); // Check if we have any directories waiting to be restored. while((xb=slist->head)) { if(is_subdir(xb->path.buf, (*sb)->path.buf)) { // We are still in a subdir. break; } else { //printf("do dir: %s\n", xb->path.buf); // Can now restore because nothing else is // fiddling in a subdirectory. if(restore_sbuf(asfd, xb, act, status, conf, need_data)) goto end; slist->head=xb->next; sbuf_free(&xb); } } // If it is a directory, need to remember it and restore it later, so // that the permissions come out right. // Meta data of directories will also have the stat stuff set to be a // directory, so will also come out at the end. // FIX THIS: for Windows, need to read and remember the blocks that // go with the directories. Probably have to do the same for metadata // that goes with directories. if(S_ISDIR((*sb)->statp.st_mode)) { // Add to the head of the list instead of the tail. (*sb)->next=slist->head; slist->head=*sb; *last_ent_was_dir=1; // Allocate a new sb. if(!(*sb=sbuf_alloc(conf))) goto end; } else { *last_ent_was_dir=0; if(restore_sbuf(asfd, *sb, act, status, conf, need_data)) goto end; } ret=0; end: return ret; }