/** * @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 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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_PATH|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; }