Skip to content

Commit

Permalink
Issue #1780: Implement a new FSIO handler akin to the realpath(3) f…
Browse files Browse the repository at this point in the history
…unction, for resolving paths. (#1790)

Most of the time, this will be a pass-through.  However, modules like mod_vroot
play games with filesystem paths, and other modules need a way to play by those
same rules.
  • Loading branch information
Castaglia committed Mar 30, 2024
1 parent 4aad05f commit 61fce5b
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 13 deletions.
5 changes: 5 additions & 0 deletions include/fsio.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
#define FSIO_FILE_LREMOVEXATTR 0x1000000
#define FSIO_FILE_SETXATTR 0x2000000
#define FSIO_FILE_LSETXATTR 0x4000000
#define FSIO_FILE_REALPATH 0x8000000

/* Macro that defines the most common file ops */
#define FSIO_FILE_COMMON (FSIO_FILE_OPEN|FSIO_FILE_READ|FSIO_FILE_WRITE|\
Expand Down Expand Up @@ -158,6 +159,8 @@ struct fs_rec {
int (*futimes)(pr_fh_t *, int, struct timeval *);
int (*fsync)(pr_fh_t *, int);

const char *(*realpath)(pr_fs_t *, pool *p, const char *);

/* Extended attribute support */
ssize_t (*getxattr)(pool *, pr_fs_t *, const char *, const char *, void *,
size_t);
Expand Down Expand Up @@ -287,6 +290,8 @@ int pr_fsio_futimes(pr_fh_t *, struct timeval *);
int pr_fsio_fsync(pr_fh_t *fh);
off_t pr_fsio_lseek(pr_fh_t *, off_t, int);

const char *pr_fsio_realpath(pool *p, const char *path);

/* Extended attribute support */
ssize_t pr_fsio_getxattr(pool *p, const char *, const char *, void *, size_t);
ssize_t pr_fsio_lgetxattr(pool *, const char *, const char *, void *, size_t);
Expand Down
34 changes: 21 additions & 13 deletions modules/mod_facl.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* ProFTPD - FTP server daemon
* Copyright (c) 2004-2022 The ProFTPD Project team
* Copyright (c) 2004-2024 The ProFTPD Project team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -974,6 +974,7 @@ static int check_facl(pool *p, const char *path, int mode, void *acl, int nents,

static int facl_fsio_access(pr_fs_t *fs, const char *path, int mode,
uid_t uid, gid_t gid, array_header *suppl_gids) {
const char *real_path = NULL;
int nents = 0, res, xerrno;
struct stat st;
void *acls;
Expand All @@ -984,6 +985,14 @@ static int facl_fsio_access(pr_fs_t *fs, const char *path, int mode,
return -1;
}

tmp_pool = make_sub_pool(fs->fs_pool);
pr_pool_tag(tmp_pool, "mod_facl access(2) pool");

real_path = pr_fsio_realpath(tmp_pool, path);
if (real_path != NULL) {
path = real_path;
}

/* Look up the acl for this path. */
# if defined(HAVE_BSD_POSIX_ACL) || \
defined(HAVE_LINUX_POSIX_ACL) || \
Expand All @@ -992,6 +1001,8 @@ static int facl_fsio_access(pr_fs_t *fs, const char *path, int mode,
if (acls == NULL) {
xerrno = errno;

destroy_pool(tmp_pool);

pr_trace_msg(trace_channel, 5, "unable to retrieve ACL for '%s': [%d] %s",
path, xerrno, strerror(xerrno));

Expand Down Expand Up @@ -1021,6 +1032,8 @@ static int facl_fsio_access(pr_fs_t *fs, const char *path, int mode,
if (nents < 0) {
xerrno = errno;

destroy_pool(tmp_pool);

pr_trace_msg(trace_channel, 5,
"unable to retrieve ACL count for '%s': [%d] %s", path, xerrno,
strerror(xerrno));
Expand Down Expand Up @@ -1048,19 +1061,13 @@ static int facl_fsio_access(pr_fs_t *fs, const char *path, int mode,
pr_trace_msg(trace_channel, 10,
"acl(2) returned %d ACL entries for path '%s'", nents, path);

if (tmp_pool == NULL) {
tmp_pool = make_sub_pool(fs->fs_pool);
pr_pool_tag(tmp_pool, "mod_facl access(2) pool");
}

acls = pcalloc(tmp_pool, nents * sizeof(aclent_t));

nents = acl(path, GETACL, nents, acls);
if (nents < 0) {
xerrno = errno;

destroy_pool(tmp_pool);
tmp_pool = NULL;

pr_trace_msg(trace_channel, 5,
"unable to retrieve ACL for '%s': [%d] %s", path, xerrno,
Expand All @@ -1087,11 +1094,6 @@ static int facl_fsio_access(pr_fs_t *fs, const char *path, int mode,
}
# endif

if (tmp_pool == NULL) {
tmp_pool = make_sub_pool(fs->fs_pool);
pr_pool_tag(tmp_pool, "mod_facl access(2) pool");
}

res = check_facl(tmp_pool, path, mode, acls, nents, &st, uid, gid,
suppl_gids);
xerrno = errno;
Expand All @@ -1109,6 +1111,7 @@ static int facl_fsio_access(pr_fs_t *fs, const char *path, int mode,

static int facl_fsio_faccess(pr_fh_t *fh, int mode, uid_t uid, gid_t gid,
array_header *suppl_gids) {
const char *real_path = NULL;
int nents = 0, res, xerrno;
struct stat st;
void *acls;
Expand Down Expand Up @@ -1224,7 +1227,12 @@ static int facl_fsio_faccess(pr_fh_t *fh, int mode, uid_t uid, gid_t gid,
pr_pool_tag(tmp_pool, "mod_facl faccess(2) pool");
}

res = check_facl(tmp_pool, fh->fh_path, mode, acls, nents, &st, uid, gid,
real_path = pr_fsio_realpath(tmp_pool, fh->fh_path);
if (real_path == NULL) {
real_path = fh->fh_path;
}

res = check_facl(tmp_pool, real_path, mode, acls, nents, &st, uid, gid,
suppl_gids);
xerrno = errno;

Expand Down
39 changes: 39 additions & 0 deletions src/fsio.c
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,15 @@ static int sys_fsync(pr_fh_t *fh, int fd) {
return res;
}

static const char *sys_realpath(pr_fs_t *fs, pool *p, const char *path) {
(void) fs;

/* The default implementation does NOT use the realpath(3) function, and
* instead is a pass-through.
*/
return pstrdup(p, path);
}

static ssize_t sys_getxattr(pool *p, pr_fs_t *fs, const char *path,
const char *name, void *val, size_t valsz) {
ssize_t res = -1;
Expand Down Expand Up @@ -6080,6 +6089,35 @@ int pr_fsio_fsync(pr_fh_t *fh) {
return res;
}

const char *pr_fsio_realpath(pool *p, const char *path) {
const char *res;
pr_fs_t *fs;

if (p == NULL ||
path == NULL) {
errno = EINVAL;
return NULL;
}

fs = lookup_file_fs(path, NULL, FSIO_FILE_REALPATH);
if (fs == NULL) {
return NULL;
}

/* Find the first non-NULL custom realpath handler. If there are none,
* use the system realpath
*/
while (fs && fs->fs_next && !fs->realpath) {
fs = fs->fs_next;
}

pr_trace_msg(trace_channel, 8, "using %s realpath() for path '%s'",
fs->fs_name, path);
res = (fs->realpath)(fs, p, path);

return res;
}

ssize_t pr_fsio_getxattr(pool *p, const char *path, const char *name, void *val,
size_t valsz) {
ssize_t res;
Expand Down Expand Up @@ -7501,6 +7539,7 @@ int init_fs(void) {
root_fs->utimes = sys_utimes;
root_fs->futimes = sys_futimes;
root_fs->fsync = sys_fsync;
root_fs->realpath = sys_realpath;

root_fs->getxattr = sys_getxattr;
root_fs->lgetxattr = sys_lgetxattr;
Expand Down
24 changes: 24 additions & 0 deletions tests/api/fsio.c
Original file line number Diff line number Diff line change
Expand Up @@ -1457,6 +1457,28 @@ START_TEST (fsio_sys_fsync_test) {
}
END_TEST

START_TEST (fsio_sys_realpath_test) {
const char *res;

mark_point();
res = pr_fsio_realpath(NULL, NULL);
ck_assert_msg(res == NULL, "Failed to handle null pool");
ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
strerror(errno), errno);

mark_point();
res = pr_fsio_realpath(p, NULL);
ck_assert_msg(res == NULL, "Failed to handle null path");
ck_assert_msg(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL,
strerror(errno), errno);

mark_point();
res = pr_fsio_realpath(p, "/tmp");
ck_assert_msg(res != NULL, "Failed to resolve path '/tmp': %s",
strerror(errno));
}
END_TEST

START_TEST (fsio_sys_getxattr_test) {
ssize_t res;
const char *path, *name;
Expand Down Expand Up @@ -5237,6 +5259,8 @@ Suite *tests_get_fsio_suite(void) {
tcase_add_test(testcase, fsio_sys_futimes_test);
tcase_add_test(testcase, fsio_sys_fsync_test);

tcase_add_test(testcase, fsio_sys_realpath_test);

/* Extended attribute tests */
tcase_add_test(testcase, fsio_sys_getxattr_test);
tcase_add_test(testcase, fsio_sys_lgetxattr_test);
Expand Down

0 comments on commit 61fce5b

Please sign in to comment.