libmount: use chdir() and NOFOLLOW umount flag for umount operation

Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
Karel Zak 2011-03-30 13:00:03 +02:00
parent a2c90d998e
commit 66bb826710
4 changed files with 114 additions and 3 deletions

View File

@ -392,7 +392,7 @@ static int do_mount(struct libmnt_context *cxt, const char *try_type)
DBG(CXT, mnt_debug_h(cxt, "%smount(2) "
"[source=%s, target=%s, type=%s, "
" mountflags=%08lx, mountdata=%s]",
" mountflags=0x%08lx, mountdata=%s]",
(cxt->flags & MNT_FL_FAKE) ? "(FAKE) " : "",
src, target, type,
flags, cxt->mountdata ? "yes" : "<none>"));

View File

@ -424,10 +424,25 @@ int mnt_context_umount_setopt(struct libmnt_context *cxt, int c, char *arg)
return rc;
}
/* Check whether the kernel supports UMOUNT_NOFOLLOW flag */
static int umount_nofollow_support(void)
{
int res = umount2("", UMOUNT_UNUSED);
if (res != -1 || errno != EINVAL)
return 0;
res = umount2("", UMOUNT_NOFOLLOW);
if (res != -1 || errno != ENOENT)
return 0;
return 1;
}
static int do_umount(struct libmnt_context *cxt)
{
int rc = 0, flags = 0;
const char *src, *target;
char *tgtbuf = NULL;
assert(cxt);
assert(cxt->fs);
@ -446,16 +461,38 @@ static int do_umount(struct libmnt_context *cxt)
if (cxt->flags & MNT_FL_FAKE)
return 0;
DBG(CXT, mnt_debug_h(cxt, "do umount"));
if (cxt->restricted) {
/*
* extra paranoa for non-root users
* -- chdir to the parent of the mountpoint and use NOFOLLOW
* flag to avoid races and symlink attacks.
*/
if (umount_nofollow_support())
flags |= UMOUNT_NOFOLLOW;
rc = mnt_chdir_to_parent(target, &tgtbuf);
if (rc)
return rc;
target = tgtbuf;
}
if (cxt->flags & MNT_FL_LAZY)
flags |= MNT_DETACH;
else if (cxt->flags & MNT_FL_FORCE)
flags |= MNT_FORCE;
rc = flasg ? umount2(target, flags) : umount(target);
DBG(CXT, mnt_debug_h(cxt, "umount(2) [target='%s', flags=0x%08x]",
target, flags));
rc = flags ? umount2(target, flags) : umount(target);
if (rc < 0)
cxt->syscall_status = -errno;
free(tgtbuf);
/*
* try remount read-only
*/
@ -468,7 +505,7 @@ static int do_umount(struct libmnt_context *cxt)
"umount(2) failed [errno=%d] -- tring remount read-only",
-cxt->syscall_status));
rc = mount(src, target, NULL,
rc = mount(src, mnt_fs_get_target(cxt->fs), NULL,
MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL);
if (rc < 0) {
cxt->syscall_status = -errno;

View File

@ -111,6 +111,7 @@ extern int mnt_run_test(struct libmnt_test *tests, int argc, char *argv[]);
extern int endswith(const char *s, const char *sx);
extern int startswith(const char *s, const char *sx);
extern int mnt_chdir_to_parent(const char *target, char **filename);
extern char *mnt_get_username(const uid_t uid);
extern int mnt_get_uid(const char *username, uid_t *uid);
extern int mnt_get_gid(const char *groupname, gid_t *gid);

View File

@ -71,6 +71,58 @@ static char *stripoff_last_component(char *path)
return ++p;
}
/* Note that the @target has to be absolute path (so at least "/")
*/
int mnt_chdir_to_parent(const char *target, char **filename)
{
char *path, *last = NULL;
char cwd[PATH_MAX];
int rc = -EINVAL;
if (!target || *target != '/')
return -EINVAL;
path = strdup(target);
if (!path)
return -ENOMEM;
if (*(path + 1) != '\0') {
last = stripoff_last_component(path);
if (!last)
goto err;
}
if (!*path)
*path = '/'; /* root */
if (chdir(path) == -1) {
DBG(UTILS, mnt_debug("failed to chdir to %s: %m", path));
rc = -errno;
goto err;
}
if (!getcwd(cwd, sizeof(cwd))) {
DBG(UTILS, mnt_debug("failed to obtain current directory: %m"));
rc = -errno;
goto err;
}
if (strcmp(cwd, path) != 0) {
DBG(UTILS, mnt_debug("path moved (%s -> %s)", path, cwd));
goto err;
}
DBG(CXT, mnt_debug("current directory moved to %s", path));
*filename = path;
if (!last || !*last)
memcpy(*filename, ".", 2);
else
memcpy(*filename, last, strlen(last) + 1);
return 0;
err:
free(path);
return rc;
}
/**
* mnt_mangle:
* @str: string
@ -800,6 +852,26 @@ int test_filesystems(struct libmnt_test *ts, int argc, char *argv[])
return rc;
}
int test_chdir(struct libmnt_test *ts, int argc, char *argv[])
{
int rc;
char *path = canonicalize_path(argv[1]),
*last = NULL;
if (!path)
return -errno;
rc = mnt_chdir_to_parent(path, &last);
if (!rc) {
printf("path='%s', abs='%s', last='%s'\n",
argv[1], path, last);
}
free(path);
free(last);
return rc;
}
int main(int argc, char *argv[])
{
struct libmnt_test tss[] = {
@ -810,6 +882,7 @@ int main(int argc, char *argv[])
{ "--ends-with", test_endswith, "<string> <prefix>" },
{ "--mountpoint", test_mountpoint, "<path>" },
{ "--fs-root", test_fsroot, "<path>" },
{ "--cd-parent", test_chdir, "<path>" },
{ NULL }
};