Viewing: check_fhandle_syscalls.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2013, DataDirect Networks, Inc.
*
* Copyright (c) 2014, Intel Corporation.
*
* Author: Swapnil Pimpale <spimpale@ddn.com>
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <mntent.h>
#include <linux/unistd.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <linux/lustre/lustre_user.h>
#define MAX_HANDLE_SZ 128
static void usage(char *prog)
{
fprintf(stderr, "usage: %s <filepath> <mount2>\n",
prog);
fprintf(stderr, "the absolute path of a test file on a "
"lustre file system is needed.\n");
exit(1);
}
#ifndef HAVE_FHANDLE_GLIBC_SUPPORT
/* Because the kernel supports this functions doesn't mean that glibc does.
* Just in case we define what we need */
struct file_handle {
__u32 handle_bytes;
int handle_type;
/* file identifier */
unsigned char f_handle[];
};
#if defined(_ASM_X86_UNISTD_64_H)
#ifndef __NR_name_to_handle_at
#define __NR_name_to_handle_at 303
#endif
#ifndef __NR_open_by_handle_at
#define __NR_open_by_handle_at 304
#endif
#elif defined(_ASM_X86_UNISTD_32_H)
#ifndef __NR_name_to_handle_at
#define __NR_name_to_handle_at 341
#endif
#ifndef __NR_open_by_handle_at
#define __NR_open_by_handle_at 342
#endif
#else
#ifndef __NR_name_to_handle_at
#define __NR_name_to_handle_at 264
#endif
#ifndef __NR_open_by_handle_at
#define __NR_open_by_handle_at 265
#endif
#endif
static inline int
name_to_handle_at(int mnt_fd, const char *filename, struct file_handle *fh,
int *mnt_id, int flags)
{
return syscall(__NR_name_to_handle_at, mnt_fd, filename, fh,
&mnt_id, flags);
}
static inline int
open_by_handle_at(int mnt_fd, struct file_handle *fh, int mode)
{
return syscall(__NR_open_by_handle_at, mnt_fd, fh, mode);
}
#endif
static int debug_mark(const char *msg)
{
char cmd[4096] = "";
snprintf(cmd, sizeof(cmd), "../utils/lctl mark %s 2>/dev/null", msg);
return system(cmd);
}
/* verify a file contents */
static int check_access(const char *filename,
int mnt_fd, struct file_handle *fh, struct stat *st_orig)
{
int fd2, rc, len, offset;
struct stat st;
char *readbuf = NULL;
debug_mark("before open by handle");
/* Open the file handle */
fd2 = open_by_handle_at(mnt_fd, fh, O_RDONLY |
(S_ISDIR(st_orig->st_mode) ? O_DIRECTORY : 0));
debug_mark("after open by handle");
if (fd2 < 0) {
fprintf(stderr, "open_by_handle_at(%s) error: %s\n", filename,
strerror(errno));
if (errno == ESTALE)
fprintf(stderr, "second mountpoint not mounted?\n");
rc = errno;
goto out_f_handle;
}
/* Get file size */
bzero(&st, sizeof(struct stat));
debug_mark("before stat");
rc = fstat(fd2, &st);
debug_mark("after stat");
if (rc < 0) {
fprintf(stderr, "fstat(%s) error: %s\n", filename,
strerror(errno));
rc = errno;
goto out_fd2;
}
/* we can't check a ctime due unlink update */
if (st_orig->st_size != st.st_size ||
st_orig->st_ino != st.st_ino ||
st_orig->st_mode != st.st_mode ||
st_orig->st_mtime != st.st_mtime) {
fprintf(stderr,
"stat data mismatch between fopen and fhandle case\n");
rc = EINVAL;
goto out_fd2;
}
if (st.st_size && S_ISREG(st.st_mode)) {
len = st.st_blksize;
readbuf = malloc(len);
if (readbuf == NULL) {
fprintf(stderr, "malloc(%d) error: %s\n", len,
strerror(errno));
rc = errno;
goto out_fd2;
}
for (offset = 0; offset < st.st_size; offset += len) {
/* read from the file */
rc = read(fd2, readbuf, len);
if (rc < 0) {
fprintf(stderr, "read(%s) error: %s\n",
filename, strerror(errno));
rc = errno;
goto out_readbuf;
}
}
}
rc = 0;
out_readbuf:
free(readbuf);
out_fd2:
close(fd2);
out_f_handle:
return rc;
}
int main(int argc, char **argv)
{
char *filename, *file;
int ret, rc = -EINVAL, mnt_fd, mnt_id, fd1, i;
struct file_handle *fh = NULL;
struct lu_fid *parent, *fid;
struct stat st;
if (argc != 3)
usage(argv[0]);
file = argv[1];
if (file[0] != '/') {
fprintf(stderr, "Need the absolete path of the file\n");
goto out;
}
if (*argv[2] != '/') {
fprintf(stderr, "Need the absolete path of the mount point\n");
goto out;
}
filename = rindex(file, '/') + 1;
debug_mark("before first open");
fd1 = open(file, O_RDONLY);
debug_mark("after first open");
if (fd1 < 0) {
fprintf(stderr, "open file %s error: %s\n",
file, strerror(errno));
rc = errno;
goto out;
}
/* Get file stats using fd1 from traditional open */
bzero(&st, sizeof(struct stat));
debug_mark("before first stat");
rc = fstat(fd1, &st);
debug_mark("after first stat");
if (rc < 0) {
fprintf(stderr, "fstat(%s) error: %s\n", file,
strerror(errno));
rc = errno;
goto out_fd1;
}
/* Open mount point directory */
debug_mark("before directory open");
mnt_fd = open(argv[2], O_DIRECTORY);
debug_mark("after directory open");
if (mnt_fd < 0) {
fprintf(stderr, "open(%s) error: %s\n)", argv[2],
strerror(errno));
rc = errno;
goto out_fd1;
}
/* Allocate memory for file handle */
fh = malloc(sizeof(struct file_handle) + MAX_HANDLE_SZ);
if (!fh) {
fprintf(stderr, "malloc(%d) error: %s\n", MAX_HANDLE_SZ,
strerror(errno));
rc = errno;
goto out_mnt_fd;
}
fh->handle_bytes = MAX_HANDLE_SZ;
/* Convert name to handle */
debug_mark("before get handle");
ret = name_to_handle_at(AT_FDCWD, file, fh, &mnt_id,
AT_SYMLINK_FOLLOW);
debug_mark("after get handle");
if (ret) {
fprintf(stderr, "name_by_handle_at(%s) error: %s\n", filename,
strerror(errno));
rc = errno;
goto out_f_handle;
}
/* Print out the contents of the file handle */
fprintf(stdout, "file: %s\nfh_bytes: %u\nfh_type: %d\nfh_data: ",
file, fh->handle_bytes, fh->handle_type);
for (i = 0; i < fh->handle_bytes; i++)
fprintf(stdout, "%02x ", fh->f_handle[i]);
fprintf(stdout, "\n");
/* Lustre stores both the parents FID and the file FID
* in the f_handle. */
parent = (struct lu_fid *)(fh->f_handle + 16);
fid = (struct lu_fid *)fh->f_handle;
fprintf(stdout, "file's parent FID is "DFID"\n", PFID(parent));
fprintf(stdout, "file FID is "DFID"\n", PFID(fid));
fprintf(stdout, "access via mount point '%s' - ", argv[2]);
fflush(stdout);
rc = check_access(filename, mnt_fd, fh, &st);
if (rc != 0)
goto out_f_handle;
fprintf(stdout, "OK \n");
fflush(stdout);
if (S_ISREG(st.st_mode)) {
fprintf(stdout, "access after unlink - ");
fflush(stdout);
ret = unlink(file);
if (ret < 0) {
fprintf(stderr,
"can't unlink '%s'. check permissions?\n",
file);
goto out_f_handle;
}
rc = check_access(filename, mnt_fd, fh, &st);
if (rc != 0)
goto out_f_handle;
fprintf(stdout, "OK\n");
fflush(stdout);
}
rc = 0;
fprintf(stdout, "check_fhandle_syscalls test Passed!\n");
out_f_handle:
free(fh);
out_mnt_fd:
close(mnt_fd);
out_fd1:
close(fd1);
out:
return rc;
}