Viewing: liblustreapi_swap.c
// SPDX-License-Identifier: LGPL-2.1+
/*
* (C) Copyright 2012 Commissariat a l'energie atomique et aux energies
* alternatives
*
* Copyright (c) 2017, 2021, DDN Storage Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
*
* library for file layout exchange used by HSM, FLR, migration to
* transparently migrate data from one layout to another.
*/
#include <stdlib.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <lustre/lustreapi.h>
/**
* llapi_get_data_version() - Get file data pointed by @fd
* @fd: file discriptor
* @data_version: data version [out]
* @flags: 0: no flush pages, usually used when process has already taken locks;
* LL_DV_RD_FLUSH: OSTs will take LCK_PR to flush dirty pages from
* clients;
* LL_DV_WR_FLUSH: OSTs will take LCK_PW to flush all caching pages from
* clients.
*
* Get a 64-bit value representing the version of file data pointed by fd.
*
* Each write or truncate, flushed on OST, will change this value. You can use
* this value to verify if file data was modified. This only checks the file
* data, not metadata.
*
* Return:
* * %0 on success.
* * %-errno on error.
*/
int llapi_get_data_version(int fd, __u64 *data_version, __u64 flags)
{
struct ioc_data_version idv;
int rc;
if (!data_version)
return -EFAULT;
idv.idv_flags = (__u32)flags;
rc = ioctl(fd, LL_IOC_DATA_VERSION, &idv);
if (rc)
rc = -errno;
else
*data_version = idv.idv_version;
return rc;
}
/*
* Fetch layout version from OST objects. Layout version on OST objects are
* only set when the file is a mirrored file AND after the file has been
* written at least once.
*
* It actually fetches the least layout version from the objects.
*/
int llapi_get_ost_layout_version(int fd, __u32 *layout_version)
{
int rc;
struct ioc_data_version idv = { 0 };
rc = ioctl(fd, LL_IOC_DATA_VERSION, &idv);
if (rc)
rc = -errno;
else
*layout_version = idv.idv_layout_version;
return rc;
}
/**
* llapi_hsm_data_version_set() - Set data version in the HSM xattr
* @fd: file discriptor
* @data_version: data version to set
*
* Set the data version in the HSM xattr on the MDT inode of a file to a
* specific value.
*
* Return:
* * %0 on success.
* * %-errno on error.
*/
int llapi_hsm_data_version_set(int fd, __u64 data_version)
{
int rc;
rc = ioctl(fd, LL_IOC_HSM_DATA_VERSION, &data_version);
if (rc)
rc = -errno;
return rc;
}
/*
* Create a file without any name and open it for read/write
*
* - file is created as if it were a standard file in the given \a directory
* - file does not appear in \a directory and mtime does not change because
* the filename is handled specially by the Lustre MDS.
* - file is destroyed at final close
*
* \param[in] directory directory from which to inherit layout/MDT idx
* \param[in] mdt_idx MDT index on which the file is created,
* \a idx == -1 means no specific MDT is requested
* \param[in] mode standard open(2) mode
* \param[in] stripe_param stripe parameters. May be NULL.
*
* \retval a file descriptor on success.
* \retval -errno on error.
*/
int llapi_create_volatile_param(const char *directory, int mdt_idx,
int open_flags, mode_t mode,
const struct llapi_stripe_param *stripe_param)
{
char file_path[PATH_MAX];
int saved_errno = errno;
int fd;
unsigned int rnumber;
int rc;
do {
rnumber = random();
if (mdt_idx == -1)
rc = snprintf(file_path, sizeof(file_path),
"%s/" LUSTRE_VOLATILE_HDR "::%.4X",
directory, rnumber);
else
rc = snprintf(file_path, sizeof(file_path),
"%s/" LUSTRE_VOLATILE_HDR ":%.4X:%.4X",
directory, mdt_idx, rnumber);
if (rc < 0 || rc >= sizeof(file_path))
return -ENAMETOOLONG;
/*
* Either open O_WRONLY or O_RDWR, creating RDONLY
* is non-sensical here
*/
if ((open_flags & O_ACCMODE) == O_RDONLY)
open_flags = O_RDWR | (open_flags & ~O_ACCMODE);
open_flags |= O_CREAT | O_EXCL | O_NOFOLLOW;
if (stripe_param != NULL) {
fd = llapi_file_open_param(file_path, open_flags,
mode, stripe_param);
if (fd < 0)
rc = fd;
} else {
fd = open(file_path, open_flags, mode);
if (fd < 0)
rc = -errno;
}
} while (fd < 0 && rc == -EEXIST);
if (fd < 0) {
llapi_error(LLAPI_MSG_ERROR, rc,
"Cannot create volatile file '%s' in '%s'",
file_path + strlen(directory) + 1 +
LUSTRE_VOLATILE_HDR_LEN,
directory);
return rc;
}
/*
* Unlink file in case this wasn't a Lustre filesystem and the magic
* volatile filename wasn't handled as intended. The effect is the
* same. If volatile open was supported then we expect unlink() to
* return -ENOENT.
*/
(void)unlink(file_path);
/*
* Since we are returning successfully we restore errno (and
* mask out possible EEXIST from open() and ENOENT from unlink().
*/
errno = saved_errno;
return fd;
}
/*
* Create a file without any name open it for read/write
*
* - file is created as if it were a standard file in the given \a directory
* - file does not appear in \a directory and mtime does not change because
* the filename is handled specially by the Lustre MDS.
* - file is removed at final close
* - file modes are rw------- since it doesn't make sense to have a read-only
* or write-only file that cannot be opened again.
* - if user wants another mode it must use fchmod() on the open file, no
* security problems arise because it cannot be opened by another process.
*
* \param[in] directory directory from which to inherit layout/MDT idx
* \param[in] idx MDT index on which the file is created,
* \a idx == -1 means no specific MDT is requested
* \param[in] open_flags standard open(2) flags
*
* \retval a file descriptor on success.
* \retval -errno on error.
*/
int llapi_create_volatile_idx(const char *directory, int mdt_idx,
int open_flags)
{
return llapi_create_volatile_param(directory, mdt_idx, open_flags,
S_IRUSR | S_IWUSR, NULL);
}
/**
* llapi_fswap_layouts_grouplock() - Swap the layouts between 2 files
* @fd1: path of file1(src)
* @fd2: path of file2(dst). (After swap, file1 will point to file2 data)
* @dv1: Data version of file pointed by @path1
* @dv2: Data version of file pointed by @path2
* @gid: Random generated group id
* @flags: Used for timestamps during swapped
*
* Note1: Both the 2 files must be open for writing
* Note2: First fd received the ioctl, second fd is passed as arg
* this is assymetric but avoid use of root path for ioctl
*
* Return:
* * %0 on success.
* * %-errno on failure.
*/
int llapi_fswap_layouts_grouplock(int fd1, int fd2, __u64 dv1, __u64 dv2,
int gid, __u64 flags)
{
struct lustre_swap_layouts lsl;
struct stat st1;
struct stat st2;
int rc;
if (flags & (SWAP_LAYOUTS_KEEP_ATIME | SWAP_LAYOUTS_KEEP_MTIME)) {
rc = fstat(fd1, &st1);
if (rc < 0)
return -errno;
rc = fstat(fd2, &st2);
if (rc < 0)
return -errno;
}
lsl.sl_fd = fd2;
lsl.sl_flags = flags;
lsl.sl_gid = gid;
lsl.sl_dv1 = dv1;
lsl.sl_dv2 = dv2;
rc = ioctl(fd1, LL_IOC_LOV_SWAP_LAYOUTS, &lsl);
if (rc < 0)
return -errno;
if (flags & (SWAP_LAYOUTS_KEEP_ATIME | SWAP_LAYOUTS_KEEP_MTIME)) {
struct timeval tv1[2];
struct timeval tv2[2];
memset(tv1, 0, sizeof(tv1));
memset(tv2, 0, sizeof(tv2));
if (flags & SWAP_LAYOUTS_KEEP_ATIME) {
tv1[0].tv_sec = st1.st_atime;
tv2[0].tv_sec = st2.st_atime;
} else {
tv1[0].tv_sec = st2.st_atime;
tv2[0].tv_sec = st1.st_atime;
}
if (flags & SWAP_LAYOUTS_KEEP_MTIME) {
tv1[1].tv_sec = st1.st_mtime;
tv2[1].tv_sec = st2.st_mtime;
} else {
tv1[1].tv_sec = st2.st_mtime;
tv2[1].tv_sec = st1.st_mtime;
}
rc = futimes(fd1, tv1);
if (rc < 0)
return -errno;
rc = futimes(fd2, tv2);
if (rc < 0)
return -errno;
}
return 0;
}
int llapi_fswap_layouts(int fd1, int fd2, __u64 dv1, __u64 dv2, __u64 flags)
{
int rc;
int grp_id;
do
grp_id = random();
while (grp_id == 0);
rc = llapi_fswap_layouts_grouplock(fd1, fd2, dv1, dv2, grp_id, flags);
if (rc < 0)
return rc;
return 0;
}
/**
* llapi_swap_layouts() - Swap the layouts between 2 files
* @path1: path of file1(src)
* @path2: path of file2(dst). (After swap, file1 will point to file2 data)
* @dv1: Data version of file pointed by @path1
* @dv2: Data version of file pointed by @path2
* @flags: Used for timestamps during swapped
*
* Note: Both the 2 files must be open for writing
*
* Return:
* * %0 on success.
* * %-errno on failure.
*/
int llapi_swap_layouts(const char *path1, const char *path2,
__u64 dv1, __u64 dv2, __u64 flags)
{
int fd1, fd2, rc;
fd1 = open(path1, O_WRONLY | O_LOV_DELAY_CREATE);
if (fd1 < 0) {
rc = -errno;
llapi_error(LLAPI_MSG_ERROR, rc,
"error: cannot open '%s' for write", path1);
goto out;
}
fd2 = open(path2, O_WRONLY | O_LOV_DELAY_CREATE);
if (fd2 < 0) {
rc = -errno;
llapi_error(LLAPI_MSG_ERROR, rc,
"error: cannot open '%s' for write", path2);
goto out_close;
}
rc = llapi_fswap_layouts(fd1, fd2, dv1, dv2, flags);
if (rc < 0)
llapi_error(LLAPI_MSG_ERROR, rc,
"error: cannot swap layout between '%s' and '%s'",
path1, path2);
close(fd2);
out_close:
close(fd1);
out:
return rc;
}
/**
* llapi_group_lock() - Take group lock.
* @fd: File to lock.
* @gid: Group Identifier.
*
* Return:
* * %0 on success.
* * %-errno on failure.
*/
int llapi_group_lock(int fd, int gid)
{
int rc;
rc = ioctl(fd, LL_IOC_GROUP_LOCK, gid);
if (rc < 0) {
rc = -errno;
llapi_error(LLAPI_MSG_ERROR, rc, "cannot get group lock");
}
return rc;
}
int llapi_group_lock64(int fd, __u64 gid)
{
int rc;
/* If this is ever compiled on a 32-bit system then a new
* LL_IOC_GROUP_LOCK64 will need to be defined that takes
* __u64 as an argument. That may never happen again.
*/
BUILD_BUG_ON(sizeof(long) != sizeof(__u64));
rc = ioctl(fd, LL_IOC_GROUP_LOCK, gid);
if (rc < 0) {
rc = -errno;
llapi_error(LLAPI_MSG_ERROR, rc, "cannot get group lock");
}
return rc;
}
/**
* llapi_group_unlock() - Put group lock.
* @fd: File to unlock.
* @gid: Group Identifier.
*
* Return:
* * %0 on success.
* * %-errno on failure.
*/
int llapi_group_unlock(int fd, int gid)
{
int rc;
rc = ioctl(fd, LL_IOC_GROUP_UNLOCK, gid);
if (rc < 0) {
rc = -errno;
llapi_error(LLAPI_MSG_ERROR, rc, "cannot put group lock");
}
return rc;
}
int llapi_group_unlock64(int fd, __u64 gid)
{
int rc;
rc = ioctl(fd, LL_IOC_GROUP_UNLOCK, gid);
if (rc < 0) {
rc = -errno;
llapi_error(LLAPI_MSG_ERROR, rc, "cannot put group lock");
}
return rc;
}