Viewing: lfs_project.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017, DataDirect Networks Storage.
* Copyright (c) 2017, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
*
* lustre/utils/lfs_project.c
*
* Author: Wang Shilong <wshilong@ddn.com>
* Author: Fan Yong <fan.yong@intel.com>
*/
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <stddef.h>
#include <libintl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <libcfs/util/list.h>
#include <libcfs/util/ioctl.h>
#include <sys/ioctl.h>
#include <libgen.h>
#include "lfs_project.h"
#include <lustre/lustreapi.h>
const char *progname;
struct lfs_project_item {
struct list_head lpi_list;
char *lpi_pathname;
};
static int
lfs_project_item_alloc(struct list_head *head, const char *pathname)
{
struct lfs_project_item *lpi;
lpi = malloc(sizeof(struct lfs_project_item));
if (lpi == NULL) {
fprintf(stderr,
"%s: cannot allocate project item for '%s': %s\n",
progname, pathname, strerror(ENOMEM));
return -ENOMEM;
}
lpi->lpi_pathname = strdup(pathname);
if (!lpi->lpi_pathname) {
free(lpi);
return -ENOMEM;
} else
list_add_tail(&lpi->lpi_list, head);
return 0;
}
static int project_get_fsxattr(const char *pathname, struct fsxattr *fsx,
struct stat *st, char *ret_bname)
{
int ret = 0, fd = -1;
char dname_path[PATH_MAX + 1] = { 0 };
char bname_path[PATH_MAX + 1] = { 0 };
char *dname, *bname;
struct lu_project lu_project = { 0 };
ret = lstat(pathname, st);
if (ret) {
fprintf(stderr, "%s: failed to lstat '%s': %s\n",
progname, pathname, strerror(errno));
ret = -errno;
goto out;
}
/* currently, only file and dir supported */
if (!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode))
goto new_api;
fd = open(pathname, O_RDONLY | O_NOCTTY | O_NDELAY);
if (fd < 0) {
fprintf(stderr, "%s: failed to open '%s': %s\n",
progname, pathname, strerror(errno));
ret = -errno;
goto out;
}
ret = ioctl(fd, FS_IOC_FSGETXATTR, fsx);
if (ret) {
fprintf(stderr, "%s: failed to get xattr for '%s': %s\n",
progname, pathname, strerror(errno));
ret = -errno;
}
goto out;
new_api:
strncpy(dname_path, pathname, PATH_MAX);
strncpy(bname_path, pathname, PATH_MAX);
dname = dirname(dname_path);
bname = basename(bname_path);
fd = open(dname, O_RDONLY | O_NOCTTY | O_NDELAY | O_NOFOLLOW);
if (fd < 0) {
ret = -errno;
goto out;
}
lu_project.project_type = LU_PROJECT_GET;
if (bname) {
strncpy(lu_project.project_name, bname, NAME_MAX);
if (ret_bname)
strncpy(ret_bname, bname, NAME_MAX);
}
ret = ioctl(fd, LL_IOC_PROJECT, &lu_project);
if (ret) {
fprintf(stderr, "%s: failed to get xattr for '%s': %s\n",
progname, pathname, strerror(errno));
ret = -errno;
} else {
fsx->fsx_xflags = lu_project.project_xflags;
fsx->fsx_projid = lu_project.project_id;
}
out:
if (ret && fd >= 0)
close(fd);
return ret ? ret : fd;
}
static int
project_check_one(const char *pathname, struct project_handle_control *phc)
{
struct fsxattr fsx;
int ret;
struct stat st;
ret = project_get_fsxattr(pathname, &fsx, &st, NULL);
if (ret < 0)
return ret;
/* use top directory's project ID if not specified */
if (!phc->assign_projid) {
phc->assign_projid = true;
phc->projid = fsx.fsx_projid;
}
if (!(fsx.fsx_xflags & FS_XFLAG_PROJINHERIT) &&
S_ISDIR(st.st_mode)) {
if (!phc->newline) {
printf("%s%c", pathname, '\0');
goto out;
}
printf("%s - project inheritance flag is not set\n",
pathname);
}
if (fsx.fsx_projid != phc->projid) {
if (!phc->newline) {
printf("%s%c", pathname, '\0');
goto out;
}
printf("%s - project identifier is not set (inode=%u, tree=%u)\n",
pathname, fsx.fsx_projid, phc->projid);
}
out:
close(ret);
return 0;
}
static int
project_list_one(const char *pathname, struct project_handle_control *phc)
{
struct fsxattr fsx;
struct stat st;
int ret;
ret = project_get_fsxattr(pathname, &fsx, &st, NULL);
if (ret < 0)
return ret;
printf("%5u %c %s\n", fsx.fsx_projid,
(fsx.fsx_xflags & FS_XFLAG_PROJINHERIT) ?
'P' : '-', pathname);
close(ret);
return 0;
}
static int
project_set_one(const char *pathname, struct project_handle_control *phc)
{
struct fsxattr fsx;
struct stat st;
int fd, ret = 0;
char bname[NAME_MAX + 1] = { 0 };
struct lu_project lp = { 0 };
fd = project_get_fsxattr(pathname, &fsx, &st, bname);
if (fd < 0)
return fd;
if ((!phc->set_projid || fsx.fsx_projid == phc->projid) &&
(!phc->set_inherit || (fsx.fsx_xflags & FS_XFLAG_PROJINHERIT)))
goto out;
if (phc->set_inherit)
fsx.fsx_xflags |= FS_XFLAG_PROJINHERIT;
if (phc->set_projid)
fsx.fsx_projid = phc->projid;
if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) {
ret = ioctl(fd, FS_IOC_FSSETXATTR, &fsx);
} else {
lp.project_xflags = fsx.fsx_xflags;
lp.project_id = fsx.fsx_projid;
lp.project_type = LU_PROJECT_SET;
strncpy(lp.project_name, bname, sizeof(lp.project_name));
ret = ioctl(fd, LL_IOC_PROJECT, &lp);
}
out:
if (ret)
fprintf(stderr, "%s: failed to set xattr for '%s': %s\n",
progname, pathname, strerror(errno));
close(fd);
return ret;
}
static int
project_clear_one(const char *pathname, struct project_handle_control *phc)
{
struct fsxattr fsx;
struct stat st;
int ret = 0, fd;
char bname[NAME_MAX + 1] = { 0 };
struct lu_project lp = { 0 };
fd = project_get_fsxattr(pathname, &fsx, &st, bname);
if (fd < 0)
return fd;
if ((!(fsx.fsx_xflags & FS_XFLAG_PROJINHERIT)) &&
(fsx.fsx_projid == 0 || phc->keep_projid))
goto out;
fsx.fsx_xflags &= ~FS_XFLAG_PROJINHERIT;
if (!phc->keep_projid)
fsx.fsx_projid = 0;
if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) {
ret = ioctl(fd, FS_IOC_FSSETXATTR, &fsx);
} else {
lp.project_xflags = fsx.fsx_xflags;
lp.project_id = fsx.fsx_projid;
lp.project_type = LU_PROJECT_SET;
strncpy(lp.project_name, bname, sizeof(lp.project_name));
ret = ioctl(fd, LL_IOC_PROJECT, &lp);
}
if (ret)
fprintf(stderr, "%s: failed to set xattr for '%s': %s\n",
progname, pathname, strerror(errno));
out:
close(fd);
return ret;
}
static int
lfs_project_handle_dir(struct list_head *head, const char *pathname,
struct project_handle_control *phc,
int (*func)(const char *,
struct project_handle_control *))
{
char fullname[PATH_MAX];
size_t size = sizeof(fullname);
int namelen;
struct dirent *ent;
DIR *dir;
int ret = 0;
int rc;
dir = opendir(pathname);
if (dir == NULL) {
ret = -errno;
fprintf(stderr, "%s: failed to opendir '%s': %s\n",
progname, pathname, strerror(-ret));
return ret;
}
while ((ent = readdir(dir)) != NULL) {
/* skip "." and ".." */
if (strcmp(ent->d_name, ".") == 0 ||
strcmp(ent->d_name, "..") == 0)
continue;
if (strlen(ent->d_name) + strlen(pathname) + 1 >=
sizeof(fullname)) {
ret = -ENAMETOOLONG;
errno = ENAMETOOLONG;
fprintf(stderr, "%s: ignored too long path: %s/%s\n",
progname, pathname, ent->d_name);
continue;
}
namelen = snprintf(fullname, size, "%s/%s",
pathname, ent->d_name);
if (namelen >= size)
fullname[size - 1] = '\0';
rc = func(fullname, phc);
if (rc && !ret)
ret = rc;
if (phc->recursive && ent->d_type == DT_DIR) {
rc = lfs_project_item_alloc(head, fullname);
if (rc && !ret)
ret = rc;
}
}
closedir(dir);
return ret;
}
static int lfs_project_iterate(const char *pathname,
struct project_handle_control *phc,
int (*func)(const char *,
struct project_handle_control *))
{
struct lfs_project_item *lpi;
struct list_head head;
struct stat st;
int ret = 0;
int rc = 0;
ret = lstat(pathname, &st);
if (ret) {
fprintf(stderr, "%s: failed to lstat '%s': %s\n",
progname, pathname, strerror(errno));
return ret;
}
/* list opeation will skip top directory in default */
if (!S_ISDIR(st.st_mode) || phc->dironly ||
project_list_one != func)
ret = func(pathname, phc);
/* dironly first, recursive will be ignored */
if (!S_ISDIR(st.st_mode) || phc->dironly || ret)
return ret;
INIT_LIST_HEAD(&head);
ret = lfs_project_item_alloc(&head, pathname);
if (ret)
return ret;
while (!list_empty(&head)) {
lpi = list_first_entry(&head, struct lfs_project_item,
lpi_list);
list_del(&lpi->lpi_list);
rc = lfs_project_handle_dir(&head, lpi->lpi_pathname,
phc, func);
if (!ret && rc)
ret = rc;
free(lpi->lpi_pathname);
free(lpi);
}
return ret;
}
int lfs_project_check(const char *pathname,
struct project_handle_control *phc)
{
return lfs_project_iterate(pathname, phc, project_check_one);
}
int lfs_project_clear(const char *pathname,
struct project_handle_control *phc)
{
return lfs_project_iterate(pathname, phc, project_clear_one);
}
int lfs_project_set(const char *pathname,
struct project_handle_control *phc)
{
return lfs_project_iterate(pathname, phc, project_set_one);
}
int lfs_project_list(const char *pathname,
struct project_handle_control *phc)
{
return lfs_project_iterate(pathname, phc, project_list_one);
}