diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 06:03:02 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 06:03:02 +0000 |
commit | 4897093455a2bf08f3db3a1132cc2f6f5484d77c (patch) | |
tree | 9e6373544263f003139431fb4b08f9766e1ed530 /support/junction/junction.c | |
parent | Initial commit. (diff) | |
download | nfs-utils-upstream.tar.xz nfs-utils-upstream.zip |
Adding upstream version 1:2.6.4.upstream/1%2.6.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'support/junction/junction.c')
-rw-r--r-- | support/junction/junction.c | 498 |
1 files changed, 498 insertions, 0 deletions
diff --git a/support/junction/junction.c b/support/junction/junction.c new file mode 100644 index 0000000..c1ec8ff --- /dev/null +++ b/support/junction/junction.c @@ -0,0 +1,498 @@ +/** + * @file support/junction/junction.c + * @brief Common utilities for managing junctions on the local file system + */ + +/* + * Copyright 2010, 2018 Oracle. All rights reserved. + * + * This file is part of nfs-utils. + * + * nfs-utils is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2.0 as + * published by the Free Software Foundation. + * + * nfs-utils is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License version 2.0 for more details. + * + * You should have received a copy of the GNU General Public License + * version 2.0 along with nfs-utils. If not, see: + * + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <sys/stat.h> + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <wchar.h> +#include <memory.h> +#include <signal.h> +#include <errno.h> +#include <dirent.h> + +#include <sys/xattr.h> + +#include "junction.h" +#include "junction-internal.h" +#include "xlog.h" + +/** + * Open a file system object + * + * @param pathname NUL-terminated C string containing pathname of an object + * @param fd OUT: a file descriptor number is filled in + * @return a FedFsStatus code + */ +FedFsStatus +junction_open_path(const char *pathname, int *fd) +{ + int tmp; + + if (pathname == NULL || fd == NULL) + return FEDFS_ERR_INVAL; + + tmp = open(pathname, O_DIRECTORY); + if (tmp == -1) { + switch (errno) { + case EPERM: + return FEDFS_ERR_ACCESS; + case EACCES: + return FEDFS_ERR_PERM; + default: + xlog(D_GENERAL, "%s: Failed to open path %s: %m", + __func__, pathname); + return FEDFS_ERR_INVAL; + } + } + + *fd = tmp; + return FEDFS_OK; +} + +/** + * Predicate: is object a directory? + * + * @param fd an open file descriptor + * @param path NUL-terminated C string containing pathname of a directory + * @return a FedFsStatus code + */ +FedFsStatus +junction_is_directory(int fd, const char *path) +{ + struct stat stb; + + if (fstatat(fd, "", &stb, AT_NO_AUTOMOUNT|AT_EMPTY_PATH) == -1) { + xlog(D_GENERAL, "%s: failed to stat %s: %m", + __func__, path); + return FEDFS_ERR_ACCESS; + } + + if (!S_ISDIR(stb.st_mode)) { + xlog(D_CALL, "%s: %s is not a directory", + __func__, path); + return FEDFS_ERR_INVAL; + } + + xlog(D_CALL, "%s: %s is a directory", __func__, path); + return FEDFS_OK; +} + +/** + * Predicate: is a directory's sticky bit set? + * + * @param fd an open file descriptor + * @param path NUL-terminated C string containing pathname of a directory + * @return a FedFsStatus code + */ +FedFsStatus +junction_is_sticky_bit_set(int fd, const char *path) +{ + struct stat stb; + + if (fstatat(fd, "", &stb, AT_NO_AUTOMOUNT|AT_EMPTY_PATH) == -1) { + xlog(D_GENERAL, "%s: failed to stat %s: %m", + __func__, path); + return FEDFS_ERR_ACCESS; + } + + if (stb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) { + xlog(D_CALL, "%s: execute bit set on %s", + __func__, path); + return FEDFS_ERR_NOTJUNCT; + } + + if (!(stb.st_mode & S_ISVTX)) { + xlog(D_CALL, "%s: sticky bit not set on %s", + __func__, path); + return FEDFS_ERR_NOTJUNCT; + } + + xlog(D_CALL, "%s: sticky bit is set on %s", __func__, path); + return FEDFS_OK; +} + +/** + * Set just a directory's sticky bit + * + * @param fd an open file descriptor + * @param path NUL-terminated C string containing pathname of a directory + * @return a FedFsStatus code + */ +FedFsStatus +junction_set_sticky_bit(int fd, const char *path) +{ + struct stat stb; + + if (fstatat(fd, "", &stb, AT_NO_AUTOMOUNT|AT_EMPTY_PATH) == -1) { + xlog(D_GENERAL, "%s: failed to stat %s: %m", + __func__, path); + return FEDFS_ERR_ACCESS; + } + + stb.st_mode &= (unsigned int)~ALLPERMS; + stb.st_mode |= S_ISVTX; + + if (fchmod(fd, stb.st_mode) == -1) { + xlog(D_GENERAL, "%s: failed to set sticky bit on %s: %m", + __func__, path); + return FEDFS_ERR_ROFS; + } + + xlog(D_CALL, "%s: set sticky bit on %s", __func__, path); + return FEDFS_OK; +} + +/** + * Predicate: does a directory have an xattr named "name"? + * + * @param fd an open file descriptor + * @param path NUL-terminated C string containing pathname of a directory + * @param name NUL-terminated C string containing name of xattr to check + * @return a FedFsStatus code + * + * @note Access to trusted attributes requires CAP_SYS_ADMIN. + */ +FedFsStatus +junction_is_xattr_present(int fd, const char *path, const char *name) +{ + ssize_t rc; + + /* + * Do not assume the total number of extended attributes + * this object may have. + */ + rc = fgetxattr(fd, name, NULL, 0); + if (rc == -1) { + switch (errno) { + case EPERM: + xlog(D_CALL, "%s: no access to xattr %s on %s", + __func__, name, path); + return FEDFS_ERR_PERM; + case ENODATA: + xlog(D_CALL, "%s: no xattr %s present on %s", + __func__, name, path); + return FEDFS_ERR_NOTJUNCT; + default: + xlog(D_CALL, "%s: xattr %s not found on %s: %m", + __func__, name, path); + return FEDFS_ERR_IO; + } + } + + xlog(D_CALL, "%s: xattr %s found on %s", + __func__, name, path); + return FEDFS_OK; +} + +/** + * Read the contents of xattr "name" + * + * @param fd an open file descriptor + * @param path NUL-terminated C string containing pathname of a directory + * @param name NUL-terminated C string containing name of xattr to retrieve + * @param contents OUT: NUL-terminated C string containing contents of xattr + * @return a FedFsStatus code + * + * If junction_read_xattr() returns FEDFS_OK, the caller must free "*contents" + * with free(3). + * + * @note Access to trusted attributes requires CAP_SYS_ADMIN. + */ +FedFsStatus +junction_read_xattr(int fd, const char *path, const char *name, char **contents) +{ + char *xattrbuf = NULL; + ssize_t len; + + len = fgetxattr(fd, name, xattrbuf, 0); + if (len < 0) { + xlog(D_GENERAL, "%s: failed to get size of xattr %s on %s: %m", + __func__, name, path); + return FEDFS_ERR_ACCESS; + } + + xattrbuf = malloc((size_t)len + 1); + if (xattrbuf == NULL) { + xlog(D_GENERAL, "%s: failed to get buffer for xattr %s on %s", + __func__, name, path); + return FEDFS_ERR_SVRFAULT; + } + + if (fgetxattr(fd, name, xattrbuf, (size_t)len) == -1) { + xlog(D_GENERAL, "%s: failed to get xattr %s on %s: %m", + __func__, name, path); + free(xattrbuf); + return FEDFS_ERR_ACCESS; + } + xattrbuf[len] = '\0'; + + xlog(D_CALL, "%s: read xattr %s from path %s", + __func__, name, path); + *contents = xattrbuf; + return FEDFS_OK; +} + +/** + * Retrieve the contents of xattr "name" + * + * @param fd an open file descriptor + * @param path NUL-terminated C string containing pathname of a directory + * @param name NUL-terminated C string containing name of xattr to retrieve + * @param contents OUT: opaque byte array containing contents of xattr + * @param contentlen OUT: size of "contents" + * @return a FedFsStatus code + * + * If junction_get_xattr() returns FEDFS_OK, the caller must free "*contents" + * with free(3). + * + * @note Access to trusted attributes requires CAP_SYS_ADMIN. + */ +FedFsStatus +junction_get_xattr(int fd, const char *path, const char *name, void **contents, + size_t *contentlen) +{ + void *xattrbuf = NULL; + ssize_t len; + + len = fgetxattr(fd, name, xattrbuf, 0); + if (len < 0) { + xlog(D_GENERAL, "%s: failed to get size of xattr %s on %s: %m", + __func__, name, path); + return FEDFS_ERR_ACCESS; + } + + xattrbuf = malloc((size_t)len); + if (xattrbuf == NULL) { + xlog(D_GENERAL, "%s: failed to get buffer for xattr %s on %s", + __func__, name, path); + return FEDFS_ERR_SVRFAULT; + } + + if (fgetxattr(fd, name, xattrbuf, (size_t)len) == -1) { + xlog(D_GENERAL, "%s: failed to get xattr %s on %s: %m", + __func__, name, path); + free(xattrbuf); + return FEDFS_ERR_ACCESS; + } + + xlog(D_CALL, "%s: read xattr %s from path %s", + __func__, name, path); + *contents = xattrbuf; + *contentlen = (size_t)len; + return FEDFS_OK; +} + +/** + * Update the contents of an xattr + * + * @param fd an open file descriptor + * @param path NUL-terminated C string containing pathname of a directory + * @param name NUL-terminated C string containing name of xattr to set + * @param contents opaque byte array containing contents of xattr + * @param contentlen size of "contents" + * @return a FedFsStatus code + * + * The extended attribute is created if it does not exist. + * Its contents are replaced if it does. + * + * @note Access to trusted attributes requires CAP_SYS_ADMIN. + */ +FedFsStatus +junction_set_xattr(int fd, const char *path, const char *name, + const void *contents, const size_t contentlen) +{ + /* + * XXX: Eventually should distinguish among several errors: + * object isn't there, no root access, some other issue + */ + if (fsetxattr(fd, name, contents, contentlen, 0) == -1) { + xlog(D_GENERAL, "%s: Failed to set xattr %s on %s: %m", + __func__, name, path); + return FEDFS_ERR_IO; + } + + xlog(D_CALL, "%s: Wrote xattr %s from path %s", + __func__, name, path); + return FEDFS_OK; +} + +/** + * Remove one xattr + * + * @param fd an open file descriptor + * @param pathname NUL-terminated C string containing pathname of a directory + * @param name NUL-terminated C string containing name of xattr to set + * @return a FedFsStatus code + * + * @note Access to trusted attributes requires CAP_SYS_ADMIN. + */ +FedFsStatus +junction_remove_xattr(int fd, const char *pathname, const char *name) +{ + /* + * XXX: Eventually should distinguish among several errors: + * object isn't there, no root access, some other issue + */ + if (fremovexattr(fd, name) == -1) { + xlog(D_GENERAL, "%s: failed to remove xattr %s from %s: %m", + __func__, name, pathname); + return FEDFS_ERR_ACCESS; + } + xlog(D_CALL, "%s: removed xattr %s from path %s", + __func__, name, pathname); + return FEDFS_OK; +} + +/** + * Retrieve object's mode bits. + * + * @param pathname NUL-terminated C string containing pathname of a directory + * @param mode OUT: mode bits + * @return a FedFsStatus code + */ +FedFsStatus +junction_get_mode(const char *pathname, mode_t *mode) +{ + FedFsStatus retval; + struct stat stb; + int fd; + + retval = junction_open_path(pathname, &fd); + if (retval != FEDFS_OK) + return retval; + + if (fstatat(fd, "", &stb, AT_NO_AUTOMOUNT|AT_EMPTY_PATH) == -1) { + xlog(D_GENERAL, "%s: failed to stat %s: %m", + __func__, pathname); + (void)close(fd); + return FEDFS_ERR_ACCESS; + } + (void)close(fd); + + xlog(D_CALL, "%s: pathname %s has mode %o", + __func__, pathname, stb.st_mode); + *mode = stb.st_mode; + return FEDFS_OK; + +} + +/** + * Save the object's mode in an xattr. Saved mode is human-readable. + * + * @param pathname NUL-terminated C string containing pathname of a directory + * @return a FedFsStatus code + */ +FedFsStatus +junction_save_mode(const char *pathname) +{ + FedFsStatus retval; + mode_t mode; + char buf[8]; + int fd; + + retval = junction_get_mode(pathname, &mode); + if (retval != FEDFS_OK) + return retval; + (void)snprintf(buf, sizeof(buf), "%o", ALLPERMS & mode); + + retval = junction_open_path(pathname, &fd); + if (retval != FEDFS_OK) + return retval; + + retval = junction_set_xattr(fd, pathname, JUNCTION_XATTR_NAME_MODE, + buf, strlen(buf)); + if (retval != FEDFS_OK) + goto out; + + retval = junction_set_sticky_bit(fd, pathname); + if (retval != FEDFS_OK) { + (void)junction_remove_xattr(fd, pathname, + JUNCTION_XATTR_NAME_MODE); + goto out; + } + + xlog(D_CALL, "%s: saved mode %o to %s", __func__, mode, pathname); + retval = FEDFS_OK; + +out: + (void)close(fd); + return retval; + +} + +/** + * Restore an object's mode bits + * + * @param pathname NUL-terminated C string containing pathname of a directory + * @return a FedFsStatus code + */ +FedFsStatus +junction_restore_mode(const char *pathname) +{ + FedFsStatus retval; + char *buf = NULL; + mode_t mode; + int fd; + + retval = junction_open_path(pathname, &fd); + if (retval != FEDFS_OK) + return retval; + + retval = junction_read_xattr(fd, pathname, JUNCTION_XATTR_NAME_MODE, &buf); + if (retval != FEDFS_OK) + goto out; + + retval = FEDFS_ERR_SVRFAULT; + if (sscanf((char *)buf, "%o", &mode) != 1) { + xlog(D_GENERAL, "%s: failed to parse saved mode on %s", + __func__, pathname); + goto out; + } + + retval = FEDFS_ERR_ROFS; + if (fchmod(fd, mode) == -1) { + xlog(D_GENERAL, "%s: failed to set mode of %s to %o: %m", + __func__, pathname, mode); + goto out; + } + + xlog(D_CALL, "%s: restored mode %o to %s", __func__, mode, pathname); + retval = FEDFS_OK; + +out: + free(buf); + (void)close(fd); + return retval; +} |