Beispiel #1
0
static int
read_mtree_spec(FILE *fp)
{
	char pathspec[PATH_MAX];
	char *cp;
	int error;

	error = read_word(fp, pathspec, sizeof(pathspec));
	if (error)
		goto out;

	cp = strchr(pathspec, '/');
	if (cp != NULL) {
		/* Absolute pathname */
		mtree_current = mtree_root;

		do {
			*cp++ = '\0';

			/* Disallow '..' as a component. */
			if (IS_DOTDOT(pathspec)) {
				mtree_error("absolute path cannot contain "
				    ".. component");
				goto out;
			}

			/* Ignore multiple adjacent slashes and '.'. */
			if (pathspec[0] != '\0' && !IS_DOT(pathspec))
				error = read_mtree_spec1(fp, false, pathspec);
			memmove(pathspec, cp, strlen(cp) + 1);
			cp = strchr(pathspec, '/');
		} while (!error && cp != NULL);

		/* Disallow '.' and '..' as the last component. */
		if (!error && (IS_DOT(pathspec) || IS_DOTDOT(pathspec))) {
			mtree_error("absolute path cannot contain . or .. "
			    "components");
			goto out;
		}
	}

	/* Ignore absolute specfications that end with a slash. */
	if (!error && pathspec[0] != '\0')
		error = read_mtree_spec1(fp, true, pathspec);

 out:
	skip_to(fp, "\n");
	(void)getc(fp);
	return (error);
}
Beispiel #2
0
/*
 * ndmpd_fhdir_v3_cb
 *
 * Callback function for file history dir information
 */
int
ndmpd_fhdir_v3_cb(lbr_fhlog_call_backs_t *cbp, char *dir, struct stat64 *stp)
{
	char nm[PATH_MAX+1];
	int nml;
	int err;
	ulong_t ino, pino;
	ulong_t pos;
	ndmp_lbr_params_t *nlp;
	ndmpd_module_params_t *params;
	DIR *dirp;
	char dirpath[PATH_MAX];

	if (!cbp) {
		err = -1;
		syslog(LOG_DEBUG, "cbp is NULL");
	} else if (!cbp->fh_cookie) {
		err = -1;
		syslog(LOG_DEBUG, "cookie is NULL");
	} else if (!dir) {
		err = -1;
		syslog(LOG_DEBUG, "dir is NULL");
	} else if (!(nlp = ndmp_get_nlp(cbp->fh_cookie))) {
		err = -1;
		syslog(LOG_DEBUG, "nlp is NULL");
	} else
		err = 0;

	if (err != 0)
		return (0);

	if (!NLP_ISSET(nlp, NLPF_FH))
		return (0);

	/*
	 * Veritas net_backup accepts only 2 as the inode number of the backup
	 * root directory.  The other way compares the path against the
	 * backup path which is slower.
	 */
	if (stp->st_ino == nlp->nlp_bkdirino)
		pino = ROOT_INODE;
	else
		pino = stp->st_ino;

	/*
	 * There is nothing below this directory to be backed up.
	 * If there was, the bit for this directory would have
	 * been set.  Backup root directory is exception.  We
	 * always send the dir file history records of it.
	 */
	if (pino != ROOT_INODE &&
	    !dbm_getone(nlp->nlp_bkmap, (u_longlong_t)stp->st_ino)) {
		syslog(LOG_DEBUG, "nothing below here");
		return (0);
	}

	params = nlp->nlp_params;
	if (!params || !params->mp_file_history_dir_func)
		return (-1);

	pos = 0;
	err = 0;

	dirp = opendir(dir);
	if (dirp == NULL)
		return (0);

	do {
		nml = PATH_MAX;
		err = dp_readdir(dirp, &pos, nm, &nml, &ino);
		if (err != 0) {
			syslog(LOG_DEBUG,
			    "%d reading pos %u dir \"%s\"", err, pos, dir);
			break;
		}
		if (nml == 0)
			break;
		nm[nml] = '\0';

		if (pino == ROOT_INODE) {
			if (rootfs_dot_or_dotdot(nm))
				ino = ROOT_INODE;
		} else if (ino == nlp->nlp_bkdirino && IS_DOTDOT(nm)) {
			ino = ROOT_INODE;
		}

		if (!dbm_getone(nlp->nlp_bkmap, (u_longlong_t)ino))
			continue;

		/*
		 * If the entry is on exclusion list dont send the info
		 */
		if (tlm_is_excluded(dir, nm, ndmp_excl_list)) {
			syslog(LOG_DEBUG,
			    "name \"%s\" skipped", nm == 0 ? "nil" : nm);
			continue;
		}

		err = (*params->mp_file_history_dir_func)(cbp->fh_cookie, nm,
		    ino, pino);
		if (err < 0) {
			syslog(LOG_ERR, "\"%s\": %d", dir, err);
			break;
		}

		/*
		 * This is a requirement by some DMA's (net_vault) that during
		 * the incremental backup, the node info should also be sent
		 * along with the dir info for all directories leading to a
		 * backed up file.
		 */
		if (ndmp_fhinode) {
			struct stat64 ret_attr;

			(void) strlcpy(dirpath, dir, PATH_MAX);
			(void) strlcat(dirpath, "/", PATH_MAX);
			(void) strlcat(dirpath, nm, PATH_MAX);
			err = stat64(dirpath, &ret_attr);
			if (err != 0) {
				syslog(LOG_ERR,
				    "Error looking up %s", nm);
				break;
			}

			if (S_ISDIR(ret_attr.st_mode)) {
				err = (*params->mp_file_history_node_func)(cbp->
				    fh_cookie, ino, &ret_attr, 0);
				if (err < 0) {
					syslog(LOG_ERR, "\"%s/\": %d",
					    dir, err);
					break;
				}
			}
		}
	} while (err == 0);

	(void) closedir(dirp);
	return (err);
}
Beispiel #3
0
static int
read_mtree_spec1(FILE *fp, bool def, const char *name)
{
	fsnode *last, *node, *parent;
	u_int type;
	int error;

	assert(name[0] != '\0');

	/*
	 * Treat '..' specially, because it only changes our current
	 * directory. We don't create a node for it. We simply ignore
	 * any keywords that may appear on the line as well.
	 * Going up a directory is a little non-obvious. A directory
	 * node has a corresponding '.' child. The parent of '.' is
	 * not the '.' node of the parent directory, but the directory
	 * node within the parent to which the child relates. However,
	 * going up a directory means we need to find the '.' node to
	 * which the directoy node is linked.  This we can do via the
	 * first * pointer, because '.' is always the first entry in a
	 * directory.
	 */
	if (IS_DOTDOT(name)) {
		/* This deals with NULL pointers as well. */
		if (mtree_current == mtree_root) {
			mtree_warning("ignoring .. in root directory");
			return (0);
		}

		node = mtree_current;

		assert(node != NULL);
		assert(IS_DOT(node->name));
		assert(node->first == node);

		/* Get the corresponding directory node in the parent. */
		node = mtree_current->parent;

		assert(node != NULL);
		assert(!IS_DOT(node->name));

		node = node->first;

		assert(node != NULL);
		assert(IS_DOT(node->name));
		assert(node->first == node);

		mtree_current = node;
		return (0);
	}

	/*
	 * If we don't have a current directory and the first specification
	 * (either implicit or defined) is not '.', then we need to create
	 * a '.' node first (using a recursive call).
	 */
	if (!IS_DOT(name) && mtree_current == NULL) {
		error = read_mtree_spec1(fp, false, ".");
		if (error)
			return (error);
	}

	/*
	 * Lookup the name in the current directory (if we have a current
	 * directory) to make sure we do not create multiple nodes for the
	 * same component. For non-definitions, if we find a node with the
	 * same name, simply change the current directory. For definitions
	 * more happens.
	 */
	last = NULL;
	node = mtree_current;
	while (node != NULL) {
		assert(node->first == mtree_current);

		if (strcmp(name, node->name) == 0) {
			if (def == true) {
				if (!dupsok)
					mtree_error(
					    "duplicate definition of %s",
					    name);
				else
					mtree_warning(
					    "duplicate definition of %s",
					    name);
				return (0);
			}

			if (node->type != S_IFDIR) {
				mtree_error("%s is not a directory", name);
				return (0);
			}

			assert(!IS_DOT(name));

			node = node->child;

			assert(node != NULL);
			assert(IS_DOT(node->name));

			mtree_current = node;
			return (0);
		}

		last = node;
		node = last->next;
	}

	parent = (mtree_current != NULL) ? mtree_current->parent : NULL;
	type = (def == false || IS_DOT(name)) ? S_IFDIR : 0;
	node = create_node(name, type, parent, &mtree_global);
	if (node == NULL)
		return (ENOMEM);

	if (def == true) {
		error = read_mtree_keywords(fp, node);
		if (error) {
			destroy_node(node);
			return (error);
		}
	}

	node->first = (mtree_current != NULL) ? mtree_current : node;

	if (last != NULL)
		last->next = node;

	if (node->type != S_IFDIR)
		return (0);

	if (!IS_DOT(node->name)) {
		parent = node;
		node = create_node(".", S_IFDIR, parent, parent);
		if (node == NULL) {
			last->next = NULL;
			destroy_node(parent);
			return (ENOMEM);
		}
		parent->child = node;
		node->first = node;
	}

	assert(node != NULL);
	assert(IS_DOT(node->name));
	assert(node->first == node);

	mtree_current = node;
	if (mtree_root == NULL)
		mtree_root = node;

	return (0);
}
Beispiel #4
0
/* dnormalize():
 *	The path will be normalized if it
 *	1) is "..",
 *	2) or starts with "../",
 *	3) or ends with "/..",
 *	4) or contains the string "/../",
 *	then it will be normalized, unless those strings are quoted. 
 *	Otherwise, a copy is made and sent back.
 */
Char   *
dnormalize(const Char *cp, int expnd)
{

/* return true if dp is of the form "../xxx" or "/../xxx" */
#define IS_DOTDOT(sp, p) (ISDOTDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))
#define IS_DOT(sp, p) (ISDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))

#ifdef S_IFLNK
    if (expnd) {
	struct Strbuf buf = Strbuf_INIT;
 	int     dotdot = 0;
	Char   *dp, *cwd;
	const Char *start = cp;
# ifdef HAVE_SLASHSLASH
	int slashslash;
# endif /* HAVE_SLASHSLASH */

	/*
	 * count the number of "../xxx" or "xxx/../xxx" in the path
	 */
	for ( ; *cp && *(cp + 1); cp++)
	    if (IS_DOTDOT(start, cp))
	        dotdot++;

	/*
	 * if none, we are done.
	 */
        if (dotdot == 0)
	    return (Strsave(start));
	
# ifdef notdef
	struct stat sb;
	/*
	 * We disable this test because:
	 * cd /tmp; mkdir dir1 dir2; cd dir2; ln -s /tmp/dir1; cd dir1;
	 * echo ../../dir1 does not expand. We had enabled this before
	 * because it was bothering people with expansions in compilation
	 * lines like -I../../foo. Maybe we need some kind of finer grain
	 * control?
	 *
	 * If the path doesn't exist, we are done too.
	 */
	if (lstat(short2str(start), &sb) != 0 && errno == ENOENT)
	    return (Strsave(start));
# endif

	cwd = xmalloc((Strlen(dcwd->di_name) + 3) * sizeof(Char));
	(void) Strcpy(cwd, dcwd->di_name);

	/*
	 * If the path starts with a slash, we are not relative to
	 * the current working directory.
	 */
	if (ABSOLUTEP(start))
	    *cwd = '\0';
# ifdef HAVE_SLASHSLASH
	slashslash = cwd[0] == '/' && cwd[1] == '/';
# endif /* HAVE_SLASHSLASH */

	/*
	 * Ignore . and count ..'s
	 */
	cp = start;
	do {
	    dotdot = 0;
	    buf.len = 0;
	    while (*cp) 
	        if (IS_DOT(start, cp)) {
	            if (*++cp)
	                cp++;
	        }
	        else if (IS_DOTDOT(start, cp)) {
		    if (buf.len != 0)
		        break; /* finish analyzing .././../xxx/[..] */
		    dotdot++;
		    cp += 2;
		    if (*cp)
		        cp++;
	        }
	        else 
		    Strbuf_append1(&buf, *cp++);

	    Strbuf_terminate(&buf);
	    while (dotdot > 0) 
	        if ((dp = Strrchr(cwd, '/')) != NULL) {
# ifdef HAVE_SLASHSLASH
		    if (dp == &cwd[1]) 
		        slashslash = 1;
# endif /* HAVE_SLASHSLASH */
		        *dp = '\0';
		        dotdot--;
	        }
	        else
		    break;

	    if (!*cwd) {	/* too many ..'s, starts with "/" */
	        cwd[0] = '/';
# ifdef HAVE_SLASHSLASH
		/*
		 * Only append another slash, if already the former cwd
		 * was in a double-slash path.
		 */
		cwd[1] = slashslash ? '/' : '\0';
		cwd[2] = '\0';
# else /* !HAVE_SLASHSLASH */
		cwd[1] = '\0';
# endif /* HAVE_SLASHSLASH */
	    }
# ifdef HAVE_SLASHSLASH
	    else if (slashslash && cwd[1] == '\0') {
		cwd[1] = '/';
		cwd[2] = '\0';
	    }
# endif /* HAVE_SLASHSLASH */

	    if (buf.len != 0) {
		size_t i;

		i = Strlen(cwd);
		if (TRM(cwd[i - 1]) != '/') {
		    cwd[i++] = '/';
		    cwd[i] = '\0';
		}
	        dp = Strspl(cwd, TRM(buf.s[0]) == '/' ? &buf.s[1] : buf.s);
	        xfree(cwd);
	        cwd = dp;
		i = Strlen(cwd) - 1;
	        if (TRM(cwd[i]) == '/')
		    cwd[i] = '\0';
	    }
	    /* Reduction of ".." following the stuff we collected in buf
	     * only makes sense if the directory item in buf really exists.
	     * Avoid reduction of "-I../.." (typical compiler call) to ""
	     * or "/usr/nonexistant/../bin" to "/usr/bin":
	     */
	    if (cwd[0]) {
	        struct stat exists;
		if (0 != stat(short2str(cwd), &exists)) {
		    xfree(buf.s);
		    xfree(cwd);
		    return Strsave(start);
		}
	    }
	} while (*cp != '\0');
	xfree(buf.s);
	return cwd;
    }
#endif /* S_IFLNK */
    return Strsave(cp);
}
/*
 * Read directory structure and store entries in `entries', which must initially
 * point to an empty list.
 */
static int
read_path(struct mtree_reader *r, const char *path, struct mtree_entry **entries,
    struct mtree_entry *parent)
{
	DIR			*dirp;
	struct dirent		*dp;
	struct mtree_entry	*entry;
	struct mtree_entry	*dirs = NULL;
	struct mtree_entry	*dot = NULL;
	char			*wd = NULL;
	int			 ret;
	int			 skip;
	int			 skip_children;

	if (parent == NULL) {
		entry = mtree_entry_create(path);
		if (entry == NULL)
			return (-1);
		ret = read_path_file(r, entry, &skip, &skip_children);
		if (ret == -1) {
			mtree_entry_free(entry);
			return (-1);
		}
		/*
		 * If the path doesn't point to a directory, simply store
		 * the single entry.
		 */
		if (entry->data.type != MTREE_ENTRY_DIR) {
			*entries = entry;
			return (0);
		}
		mtree_entry_free(entry);

		if ((r->options & MTREE_READ_PATH_DONT_CROSS_MOUNT) != 0) {
			struct stat st;

			if ((r->options & MTREE_READ_PATH_FOLLOW_SYMLINKS) != 0)
				ret = stat(path, &st);
			else
				ret = lstat(path, &st);
			if (ret == -1) {
				mtree_reader_set_error(r, errno, "`%s'", path);
				return (-1);
			}
			r->base_dev = st.st_dev;
		}

		/*
		 * Change to the directory to be able to read the files
		 * with the mtree's "./" prefix.
		 */
		wd = mtree_getcwd();
		if (wd == NULL) {
			mtree_reader_set_error(r, errno,
			    "Could not determine the current working directory");
			return (-1);
		}
		if (chdir(path) == -1) {
			mtree_reader_set_error(r, errno,
			    "Could not change the working directory to `%s'",
			    path);
			return (-1);
		}
	}

	/*
	 * Read the directory structure.
	 */
	ret = 0;
	if ((dirp = opendir((parent != NULL) ? path : ".")) == NULL) {
		if ((r->options & MTREE_READ_PATH_SKIP_ON_ERROR) == 0) {
			ret = -1;
			goto end;
		}
		return (0);
	}
	while ((dp = readdir(dirp)) != NULL) {
		if (IS_DOTDOT(dp->d_name))
			continue;
		/*
		 * Dot is read only in the initial directory.
		 */
		if (parent != NULL && IS_DOT(dp->d_name))
			continue;

		entry = mtree_entry_create_empty();
		if (entry == NULL) {
			ret = -1;
			break;
		}
		entry->name = strdup(dp->d_name);
		if (entry->name == NULL) {
			ret = -1;
			break;
		}
		entry->parent = parent;
		entry->path = create_v1_path(entry);
		if (entry->path == NULL) {
			ret = -1;
			break;
		}

		ret = read_path_file(r, entry, &skip, &skip_children);
		if (ret == -1)
			break;
		if (IS_DOT(dp->d_name)) {
			if (skip)
				mtree_entry_free(entry);
			else
				dot = entry;
			if (skip_children) {
				/*
				 * This is a bit artificial, but when the user
				 * asks to skip children below ".", remove
				 * all the entries, except for the dot itself
				 * (unless the dot is skipped as well).
				 */
				mtree_entry_free_all(*entries);
				mtree_entry_free_all(dirs);
				*entries = NULL;
				dirs = NULL;
				break;
			}
			continue;
		}
		if (skip && skip_children) {
			mtree_entry_free(entry);
			continue;
		}
		if (entry->data.type == MTREE_ENTRY_DIR) {
			if (skip)
				entry->flags |= __MTREE_ENTRY_SKIP;
			if (skip_children)
				entry->flags |= __MTREE_ENTRY_SKIP_CHILDREN;
			dirs = mtree_entry_prepend(dirs, entry);
		} else if (!skip)
			*entries = mtree_entry_prepend(*entries, entry);
	}
end:
	if (ret == 0) {
		if (dot != NULL) {
			/* Put the initial dot at the (reversed) start. */
			*entries = mtree_entry_append(*entries, dot);
		}
		closedir(dirp);

		/* Directories are processed after files. */
		entry = dirs;
		while (entry != NULL) {
			struct mtree_entry *next = entry->next;

			if ((entry->flags & __MTREE_ENTRY_SKIP) == 0) {
				dirs = mtree_entry_unlink(dirs, entry);
				*entries = mtree_entry_prepend(*entries, entry);
			}
			if ((entry->flags & __MTREE_ENTRY_SKIP_CHILDREN) == 0)
				ret = read_path(r, entry->path, entries, entry);

			if (ret == -1)
				break;

			entry = next;
		}
	} else {
		/* Fatal error, clean up and make our way back to the caller. */
		mtree_entry_free_all(*entries);
		*entries = NULL;
	}
	mtree_entry_free_all(dirs);

	if (parent == NULL && chdir(wd) == -1)
		WARN("Could not change the working directory back to `%s'", wd);

	return (ret);
}
static int
read_spec(struct mtree_reader *r, char *s)
{
	struct mtree_entry	*entry;
	char			 name[MAXPATHLEN];
	char			*slash, *file, *word, *next;
	int			 ret;
	int			 skip = 0;

	/* Try to detect the format if it isn't known yet. */
	if (r->path_last == -1 && detect_format(r, s) == -1)
		return (-1);
	if (r->path_last != 1) {
		/*
		 * Path is surely in the first field, either because we know
		 * the format or there is just one single field.
		 */
		read_word(s, &file, &next);
		assert(file != NULL);

		if (IS_DOTDOT(file)) {
			/* Only change the parent, keywords are ignored. */
			if (r->parent == NULL) {
				mtree_reader_set_error(r, EINVAL,
				    "`..' not allowed, no parent directory");
				return (-1);
			}
			r->parent = r->parent->parent;
			return (0);
		}
	}
	entry = mtree_entry_create_empty();
	if (entry == NULL) {
		mtree_reader_set_error(r, errno, NULL);
		return (-1);
	}

	if (r->path_last != 1) {
		if (next != NULL) {
			/* Read keyword that follow the path. */
			ret = read_keywords(r, next, &entry->data, 1);
			if (ret == -1) {
				mtree_entry_free(entry);
				return (-1);
			}
		}
	} else {
		/* Path is at the end, read the keywords first. */
		for (file = NULL; file == NULL;) {
			read_word(s, &word, &next);
			assert(word != NULL);

			if (next != NULL) {
				ret = read_keyword(r, word, &entry->data, 1);
				if (ret == -1) {
					mtree_entry_free(entry);
					return (-1);
				}
				s = next;
			} else
				file = word;
		}
		assert(file != NULL);

		if (IS_DOTDOT(file)) {
			mtree_reader_set_error(r, EINVAL, "`..' not allowed in "
			    "this format");
			mtree_entry_free(entry);
			return (-1);
		}
	}
	/* Copy /set values to the entry. */
	mtree_entry_data_copy_keywords(&entry->data, &r->defaults,
	    r->defaults.keywords, 0);

	/*
	 * See if we should skip this file.
	 */
	if (SKIP_TYPE(r->options, entry->data.type)) {
		/*
		 * With directories in version 1.0 format, we'll have to worry
		 * about potentially setting the directory as the current
		 * parent, other types can be skipped immediately.
		 */
		if (entry->data.type != MTREE_ENTRY_DIR || r->path_last == 1)
			goto skip;
		else
			skip = 1;
	}

	/* Save the file path and name. */
	if (strnunvis(name, sizeof(name), file) == -1) {
		mtree_reader_set_error(r, ENAMETOOLONG, "File name too long: `%s'",
		    file);
		mtree_entry_free(entry);
		return (-1);
	}
	if ((slash = strchr(name, '/')) != NULL) {
		/*
		 * If the name contains a slash, it is a relative path.
		 * These do not change the working directory.
		 */
		if (skip)
			goto skip;
		ret = mtree_cleanup_path(name, &entry->path, &entry->name);
		if (ret == -1) {
			mtree_reader_set_error(r, errno, NULL);
			mtree_entry_free(entry);
			return (-1);
		}
	} else {
		entry->name = strdup(name);
		if (entry->name == NULL) {
			mtree_reader_set_error(r, errno, NULL);
			mtree_entry_free(entry);
			return (-1);
		}
		entry->parent = r->parent;
		entry->path = create_v1_path(entry);
		if (entry->path == NULL) {
			mtree_reader_set_error(r, errno, NULL);
			mtree_entry_free(entry);
			return (-1);
		}
	}

	/*
	 * We may want to skip this entry if a filter told us to skip
	 * children of a directory, which is a parent of this entry.
	 *
	 * There is also no need to worry about changing the parent if
	 * this happens.
	 */
	if (!skip && r->skip_trie != NULL) {
		char *tok;
		char *endptr;

		for (tok = strtok_r(name, "/", &endptr); tok != NULL;
		     tok = strtok_r(NULL, "/", &endptr)) {
			if (mtree_trie_find(r->skip_trie, tok) != NULL) {
				mtree_entry_free(entry);
				return (0);
			}
		}
	}

	/*
	 * Mark the current entry as the current directory. This only applies
	 * to classic (1.0) entries and must be done after keywords are read.
	 */
	if (slash == NULL && entry->data.type == MTREE_ENTRY_DIR)
		r->parent = entry;
	if (skip)
		goto skip;

	if (r->filter != NULL) {
		int result;

		/* Apply filter. */
		result = r->filter(entry, r->filter_data);
		if (result & MTREE_ENTRY_SKIP_CHILDREN) {
			if (entry->data.type == MTREE_ENTRY_DIR) {
				struct mtree_entry *found, *start;

				if (r->skip_trie == NULL) {
					r->skip_trie = mtree_trie_create(NULL);
					if (r->skip_trie == NULL) {
						mtree_reader_set_error(r, errno,
						    NULL);
						return (-1);
					}
				}
				if (mtree_trie_insert(r->skip_trie, entry->path,
				    TRIE_ITEM) == -1) {
					mtree_reader_set_error(r, errno,
					    NULL);
					return (-1);
				}

				strcpy(name, entry->path);
				strcat(name, "/");
				/*
				 * Remove children that are already in the
				 * list.
				 */
				start = r->entries;
				for (;;) {
					found = mtree_entry_find_prefix(start,
					    name);
					if (found == NULL)
						break;

					start = found->next;
					r->entries = mtree_entry_unlink(
					    r->entries, found);
					mtree_entry_free(found);
				}
			}
		}
		if (result & MTREE_ENTRY_SKIP)
			goto skip;
	}

	r->entries = mtree_entry_prepend(r->entries, entry);
	return (0);
skip:
	/*
	 * Skipping directory which is the current parent would make its files
	 * end up in a wrong directory.
	 *
	 * Keep such entries in a list of `loose' entries instead, these will
	 * be deleted when all the work is done.
	 */
	if (entry == r->parent)
		r->loose = mtree_entry_prepend(r->loose, entry);
	else
		mtree_entry_free(entry);
	return (0);
}