/*
* Copyright (C) 2011 Novell Inc.
* Copyright (C) 2016 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/fs.h>
#include <linux/cred.h>
#include <linux/ctype.h>
#include <linux/namei.h>
#include <linux/xattr.h>
#include <linux/ratelimit.h>
#include <linux/mount.h>
#include <linux/exportfs.h>
#include "overlayfs.h"
struct ovl_lookup_data {
struct qstr name;
bool is_dir;
bool opaque;
bool stop;
bool last;
char *redirect;
};
static int ovl_check_redirect(struct dentry *dentry, struct ovl_lookup_data *d,
size_t prelen, const char *post)
{
int res;
char *s, *next, *buf = NULL;
res = vfs_getxattr(dentry, OVL_XATTR_REDIRECT, NULL, 0);
if (res < 0) {
if (res == -ENODATA || res == -EOPNOTSUPP)
return 0;
goto fail;
}
buf = kzalloc(prelen + res + strlen(post) + 1, GFP_KERNEL);
if (!buf)
return -ENOMEM;
if (res == 0)
goto invalid;
res = vfs_getxattr(dentry, OVL_XATTR_REDIRECT, buf, res);
if (res < 0)
goto fail;
if (res == 0)
goto invalid;
if (buf[0] == '/') {
for (s = buf; *s++ == '/'; s = next) {
next = strchrnul(s, '/');
if (s == next)
goto invalid;
}
/*
* One of the ancestor path elements in an absolute path
* lookup in ovl_lookup_layer() could have been opaque and
* that will stop further lookup in lower layers (d->stop=true)
* But we have found an absolute redirect in decendant path
* element and that should force continue lookup in lower
* layers (reset d->stop).
*/
d->stop = false;
} else {
if (strchr(buf, '/') != NULL)
goto invalid;
memmove(buf + prelen, buf, res);
memcpy(buf, d->name.name, prelen);
}
strcat(buf, post);
kfree(d->redirect);
d->redirect = buf;
d->name.name = d->redirect;
d->name.len = strlen(d->redirect);
return 0;
err_free:
kfree(buf);
return 0;
fail:
pr_warn_ratelimited("overlayfs: failed to get redirect (%i)\n", res);
goto err_free;
invalid:
pr_warn_ratelimited("overlayfs: invalid redirect (%s)\n", buf);
goto err_free;
}
static int ovl_acceptable(void *ctx, struct dentry *dentry)
{
/*
* A non-dir origin may be disconnected, which is fine, because
* we only need it for its unique inode number.
*/
if (!d_is_dir(dentry))
return 1;
/* Don't decode a deleted empty directory */
if (d_unhashed(dentry))
return 0;
/* Check if directory belongs to the layer we are decoding from */
return is_subdir(dentry, ((struct vfsmount *)ctx)->mnt_root);
}
/*
* Check validity of an overlay file handle buffer.
*
* Return 0 for a valid file handle.
* Return -ENODATA for "origin unknown".
* Return <0 for an invalid file handle.
*/
int ovl_check_fh_len(struct ovl_fh *fh, int fh_len)
{
if (fh_len < sizeof(struct ovl_fh) || fh_len < fh->len)
return -EINVAL;
if (fh->magic != OVL_FH_MAGIC)
return -