Skip to content

Commit

Permalink
OS-3925 lxbrand in-kernel link(2) and linkat(2)
Browse files Browse the repository at this point in the history
OS-5094 lxbrand link to symlink failure
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
  • Loading branch information
pfmooney committed Jan 19, 2016
1 parent 8572d6e commit 8843c35
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 84 deletions.
74 changes: 1 addition & 73 deletions usr/src/lib/brand/lx/lx_brand/common/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
* Copyright 2015 Joyent, Inc. All rights reserved.
* Copyright 2016 Joyent, Inc.
*/

#include <sys/fstyp.h>
Expand Down Expand Up @@ -119,23 +119,6 @@ ltos_at_flag(int lflag, int allow, boolean_t enforce)
* Miscellaneous file-related system calls.
*/

/*
* On Linux, even root cannot create a link to a directory, so we have to
* add an explicit check.
*/
long
lx_link(uintptr_t p1, uintptr_t p2)
{
char *from = (char *)p1;
char *to = (char *)p2;
struct stat64 statbuf;

if ((stat64(from, &statbuf) == 0) && S_ISDIR(statbuf.st_mode))
return (-EPERM);

return (link(from, to) ? -errno : 0);
}

/*
* On Linux, an unlink of a directory returns EISDIR, not EPERM.
*/
Expand Down Expand Up @@ -632,61 +615,6 @@ lx_symlinkat(uintptr_t p1, uintptr_t ext1, uintptr_t p2)
return (symlink((char *)p1, pathbuf) ? -errno : 0);
}

long
lx_linkat(uintptr_t ext1, uintptr_t p1, uintptr_t ext2, uintptr_t p2,
uintptr_t p3)
{
int atfd1 = (int)ext1;
int atfd2 = (int)ext2;
char pathbuf1[MAXPATHLEN];
char pathbuf2[MAXPATHLEN];
int ret;

/*
* The flag specifies whether the hardlink will point to a symlink or
* not, on solaris the default behaviour of link() is to dereference a
* symlink and there is no obvious way to trigger the other behaviour.
* So for now we just ignore this flag and act like link().
*/
int flag = p3;

/* return proper error for invalid flags */
if ((flag & ~(LX_AT_SYMLINK_FOLLOW | LX_AT_EMPTY_PATH)) != 0)
return (-EINVAL);

ret = getpathat(atfd1, p1, pathbuf1, sizeof (pathbuf1));
if (ret < 0) {
if (ret == -EBADF) {
/*
* Try to figure out correct Linux errno. We know path
* is relative. Check if we have a fd for a dir which
* has been removed.
*/
if (atfd1 != -1 && lx_fd_to_path(atfd1, pathbuf1,
sizeof (pathbuf1)) == NULL)
ret = -ENOENT;
}
return (ret);
}

ret = getpathat(atfd2, p2, pathbuf2, sizeof (pathbuf2));
if (ret < 0) {
if (ret == -EBADF) {
/*
* Try to figure out correct Linux errno. We know path
* is relative. Check if we have a fd for a dir which
* has been removed.
*/
if (atfd2 != -1 && lx_fd_to_path(atfd2, pathbuf2,
sizeof (pathbuf2)) == NULL)
ret = -ENOENT;
}
return (ret);
}

return (lx_link((uintptr_t)pathbuf1, (uintptr_t)pathbuf2));
}

long
lx_readlink(uintptr_t p1, uintptr_t p2, uintptr_t p3)
{
Expand Down
8 changes: 4 additions & 4 deletions usr/src/lib/brand/lx/lx_brand/common/lx_brand.c
Original file line number Diff line number Diff line change
Expand Up @@ -1045,7 +1045,7 @@ static lx_syscall_handler_t lx_handlers[] = {
NULL, /* 83: mkdir */
lx_rmdir, /* 84: rmdir */
lx_creat, /* 85: creat */
lx_link, /* 86: link */
NULL, /* 86: link */
lx_unlink, /* 87: unlink */
lx_symlink, /* 88: symlink */
lx_readlink, /* 89: readlink */
Expand Down Expand Up @@ -1224,7 +1224,7 @@ static lx_syscall_handler_t lx_handlers[] = {
NULL, /* 262: fstatat64 */
lx_unlinkat, /* 263: unlinkat */
lx_renameat, /* 264: renameat */
lx_linkat, /* 265: linkat */
NULL, /* 265: linkat */
lx_symlinkat, /* 266: symlinkat */
lx_readlinkat, /* 267: readlinkat */
NULL, /* 268: fchmodat */
Expand Down Expand Up @@ -1299,7 +1299,7 @@ static lx_syscall_handler_t lx_handlers[] = {
lx_close, /* 6: close */
NULL, /* 7: waitpid */
lx_creat, /* 8: creat */
lx_link, /* 9: link */
NULL, /* 9: link */
lx_unlink, /* 10: unlink */
lx_execve, /* 11: execve */
lx_chdir, /* 12: chdir */
Expand Down Expand Up @@ -1593,7 +1593,7 @@ static lx_syscall_handler_t lx_handlers[] = {
NULL, /* 300: fstatat64 */
lx_unlinkat, /* 301: unlinkat */
lx_renameat, /* 302: renameat */
lx_linkat, /* 303: linkat */
NULL, /* 303: linkat */
lx_symlinkat, /* 304: symlinkat */
lx_readlinkat, /* 305: readlinkat */
NULL, /* 306: fchmodat */
Expand Down
4 changes: 1 addition & 3 deletions usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
*/

/*
* Copyright 2015 Joyent, Inc. All rights reserved.
* Copyright 2016 Joyent, Inc.
*/

#ifndef _SYS_LX_SYSCALL_H
Expand All @@ -50,7 +50,6 @@ extern long lx_utimensat(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
extern long lx_fstatat64(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
extern long lx_unlinkat(uintptr_t, uintptr_t, uintptr_t);
extern long lx_renameat(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
extern long lx_linkat(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
extern long lx_symlinkat(uintptr_t, uintptr_t, uintptr_t);
extern long lx_readlinkat(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
extern long lx_access(uintptr_t, uintptr_t);
Expand Down Expand Up @@ -144,7 +143,6 @@ extern long lx_ftruncate64(uintptr_t, uintptr_t, uintptr_t);
extern long lx_sysctl(uintptr_t);
extern long lx_fsync(uintptr_t);
extern long lx_fdatasync(uintptr_t);
extern long lx_link(uintptr_t, uintptr_t);
extern long lx_unlink(uintptr_t);
extern long lx_rmdir(uintptr_t);
extern long lx_rename(uintptr_t, uintptr_t);
Expand Down
8 changes: 4 additions & 4 deletions usr/src/uts/common/brand/lx/os/lx_syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ lx_sysent_t lx_sysent32[] = {
{"close", lx_close, 0, 1}, /* 6 */
{"waitpid", lx_waitpid, 0, 3}, /* 7 */
{"creat", NULL, 0, 2}, /* 8 */
{"link", NULL, 0, 2}, /* 9 */
{"link", lx_link, 0, 2}, /* 9 */
{"unlink", NULL, 0, 1}, /* 10 */
{"execve", NULL, 0, 3}, /* 11 */
{"chdir", NULL, 0, 1}, /* 12 */
Expand Down Expand Up @@ -923,7 +923,7 @@ lx_sysent_t lx_sysent32[] = {
{"fstatat64", lx_fstatat64, 0, 4}, /* 300 */
{"unlinkat", NULL, 0, 3}, /* 301 */
{"renameat", NULL, 0, 4}, /* 302 */
{"linkat", NULL, 0, 5}, /* 303 */
{"linkat", lx_linkat, 0, 5}, /* 303 */
{"symlinkat", NULL, 0, 3}, /* 304 */
{"readlinkat", NULL, 0, 4}, /* 305 */
{"fchmodat", lx_fchmodat, 0, 3}, /* 306 */
Expand Down Expand Up @@ -1073,7 +1073,7 @@ lx_sysent_t lx_sysent64[] = {
{"mkdir", lx_mkdir, 0, 2}, /* 83 */
{"rmdir", NULL, 0, 1}, /* 84 */
{"creat", NULL, 0, 2}, /* 85 */
{"link", NULL, 0, 2}, /* 86 */
{"link", lx_link, 0, 2}, /* 86 */
{"unlink", NULL, 0, 1}, /* 87 */
{"symlink", NULL, 0, 2}, /* 88 */
{"readlink", NULL, 0, 3}, /* 89 */
Expand Down Expand Up @@ -1252,7 +1252,7 @@ lx_sysent_t lx_sysent64[] = {
{"fstatat64", lx_fstatat64, 0, 4}, /* 262 */
{"unlinkat", NULL, 0, 3}, /* 263 */
{"renameat", NULL, 0, 4}, /* 264 */
{"linkat", NULL, 0, 5}, /* 265 */
{"linkat", lx_linkat, 0, 5}, /* 265 */
{"symlinkat", NULL, 0, 3}, /* 266 */
{"readlinkat", NULL, 0, 4}, /* 267 */
{"fchmodat", lx_fchmodat, 0, 3}, /* 268 */
Expand Down
2 changes: 2 additions & 0 deletions usr/src/uts/common/brand/lx/sys/lx_syscalls.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ extern long lx_ioprio_set();
extern long lx_kill();
extern long lx_lchown();
extern long lx_lchown16();
extern long lx_link();
extern long lx_linkat();
extern long lx_lstat32();
extern long lx_lstat64();
extern long lx_mkdir();
Expand Down
97 changes: 97 additions & 0 deletions usr/src/uts/common/brand/lx/syscall/lx_link.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*/

/*
* Copyright 2016 Joyent, Inc.
*/

#include <sys/fcntl.h>
#include <sys/errno.h>
#include <sys/file.h>
#include <sys/vnode.h>
#include <sys/systm.h>
#include <sys/lx_fcntl.h>

#define LX_LINK_ALLOWED (LX_AT_SYMLINK_FOLLOW | LX_AT_EMPTY_PATH)

static long
lx_link_common(int ffd, char *from, int tfd, char *to, int flags)
{
int error;
vnode_t *fsvp = NULL, *tsvp = NULL;
enum symfollow follow = NO_FOLLOW;

if ((flags & ~LX_LINK_ALLOWED) != 0) {
return (set_errno(EINVAL));
}
if ((flags & LX_AT_EMPTY_PATH) == 0) {
char c;

/*
* Check that both 'from' and 'to' names are non-empty if
* AT_EMPTY_PATH is not set.
*/
if (copyin(from, &c, sizeof (c)) != 0) {
return (set_errno(EFAULT));
} else if (c == '\0') {
return (set_errno(ENOENT));
}
if (copyin(to, &c, sizeof (c)) != 0) {
return (set_errno(EFAULT));
} else if (c == '\0') {
return (set_errno(ENOENT));
}

/*
* XXX: When our support for LX capabilities improves, ENOENT
* should be thrown when a process lacking CAP_DAC_READ_SEARCH
* attempts to use the AT_EMPTY_PATH flag.
*/
}
if ((flags & LX_AT_SYMLINK_FOLLOW) != 0) {
follow = FOLLOW;
}

if ((error = fgetstartvp(ffd, from, &fsvp)) != 0) {
goto out;
}
if ((error = fgetstartvp(tfd, to, &tsvp)) != 0) {
goto out;
}
error = vn_linkat(fsvp, from, follow, tsvp, to, UIO_USERSPACE);

out:
if (fsvp != NULL) {
VN_RELE(fsvp);
}
if (tsvp != NULL) {
VN_RELE(tsvp);
}
if (error) {
return (set_errno(error));
}
return (0);
}

long
lx_link(char *from, char *to)
{
return (lx_link_common(AT_FDCWD, from, AT_FDCWD, to, 0));
}

long
lx_linkat(int ffd, char *from, int tfd, char *to, int flags)
{
ffd = (ffd == LX_AT_FDCWD) ? AT_FDCWD : ffd;
tfd = (tfd == LX_AT_FDCWD) ? AT_FDCWD : tfd;

return (lx_link_common(ffd, from, tfd, to, flags));
}
1 change: 1 addition & 0 deletions usr/src/uts/intel/Makefile.files
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ LX_BRAND_OBJS = \
lx_ioctl.o \
lx_ioprio.o \
lx_kill.o \
lx_link.o \
lx_misc.o \
lx_mkdir.o \
lx_modify_ldt.o \
Expand Down

0 comments on commit 8843c35

Please sign in to comment.