// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2000-2003 Silicon Graphics, Inc.
 * All Rights Reserved.
 */

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

#include <xfs/xfs.h>
#include <xfs/jdm.h>

/* simple test program to try out a bunch of xfsctls:
 *     XFS_IOC_FSCOUNTS
 *     XFS_IOC_GET_RESBLKS
 *     XFS_IOC_SET_RESBLKS
 *     XFS_IOC_PATH_TO_FSHANDLE
 *     XFS_IOC_PATH_TO_HANDLE
 *     XFS_IOC_FD_TO_HANDLE
 *     XFS_IOC_OPEN_BY_HANDLE
 *     XFS_IOC_READLINK_BY_HANDLE
*/

    
void fscounts(char *fname, int fsfd)
{
    xfs_fsop_counts_t   counts;
    int                 ret;
    
    ret=xfsctl(fname, fsfd, XFS_IOC_FSCOUNTS, &counts);
    if (ret) {
        perror("xfsctl(XFS_IOC_FSCOUNTS)");
        exit(1);
    }

    printf("XFS_IOC_FSCOUNTS-\n    freedata: %lld freertx: %lld freeino: %lld allocino: %lld\n",
            (long long)counts.freedata, (long long)counts.freertx,
	   (long long)counts.freeino, (long long)counts.allocino);
}
    
__u64 getresblks(char *fname, int fsfd)
{
    xfs_fsop_resblks_t  res;
    int                 ret;
    
    ret=xfsctl(fname, fsfd, XFS_IOC_GET_RESBLKS, &res);
    if (ret) {
        perror("xfsctl(XFS_IOC_GET_RESBLKS)");
        exit(1);
    }
    
    printf("XFS_IOC_GET_RESBLKS-\n    resblks: %lld blksavail: %lld\n",
            (long long)res.resblks, (long long)res.resblks_avail);
    
    return res.resblks;
}
    
__u64 setresblks(char *fname, int fsfd, __u64 blks)
{
    xfs_fsop_resblks_t  res;
    int                 ret;
    
    res.resblks=blks;
    ret=xfsctl(fname, fsfd, XFS_IOC_SET_RESBLKS, &res);
    if (ret) {
        perror("xfsctl(XFS_IOC_SET_RESBLKS)");
        exit(1);
    }
    
    printf("XFS_IOC_SET_RESBLKS-\n    resblks: %lld blksavail: %lld\n",
            (long long)res.resblks, (long long)res.resblks_avail);
    
    return res.resblks;
}

void handle_print(void *handle, int handlen)
{
    char *p=handle;
    if (!handle||!handlen) {
        printf("%s",handle?"<zero len>":"<NULL>");
        return;
    };
    
    printf("#");
    while (handlen--)
        printf("%02x", *p++);   
}

void stat_print(int fd)
{
    struct stat buf;
    
    if (fstat(fd, &buf)) {
        perror("stat");
        exit(1);
    }
    printf("dev: %llu ino: %llu mode: %o\n",
            (unsigned long long)buf.st_dev, (unsigned long long)buf.st_ino, buf.st_mode);
}
    

void handle(char *fname, int fsfd, char *path)
{
    xfs_fsop_handlereq_t    handle;
    char                    buffer[1024];
    char                    link[1024];
    int                     ret;
    __u32                   len;
    __u32                   linklen;
    int                     fd;
    
    handle.path=path;
    handle.ohandle=buffer;
    handle.ohandlen=&len;
    ret=xfsctl(fname, fsfd, XFS_IOC_PATH_TO_FSHANDLE, &handle);
    if (ret) {
        perror("xfsctl(XFS_IOC_PATH_TO_FSHANDLE)");
        exit(1);
    }
    printf("XFS_IOC_PATH_TO_FSHANDLE-\n    handle: ");
    handle_print(handle.ohandle, *handle.ohandlen);
    printf("\n");
    
    fd=open(path,O_RDONLY);
    if (fd<0) {
        perror("open");
        exit(1);
    }
    handle.path=NULL;
    handle.fd=fd;
    handle.ohandle=buffer;
    handle.ohandlen=&len;
    ret=xfsctl(fname, fsfd, XFS_IOC_FD_TO_HANDLE, &handle);
    if (ret) {
        perror("ioctl(XFS_IOC_FD_TO_HANDLE)");
        exit(1);
    }
    
    printf("XFS_IOC_FD_TO_HANDLE-\n    path: %s\n    handle: ", path);
    handle_print(handle.ohandle, *handle.ohandlen);
    printf("\n");
    
    close(fd);
    
    handle.path=NULL;
    handle.fd=-1;
    handle.ihandle=buffer;
    handle.ihandlen=len;
    handle.ohandle=NULL;
    handle.ohandlen=NULL;
    ret=xfsctl(fname, fsfd, XFS_IOC_OPEN_BY_HANDLE, &handle);
    if (ret<0) {
        perror("xfsctl(XFS_IOC_OPEN_BY_HANDLE)");
        exit(1);
    }
    printf("XFS_IOC_OPEN_BY_HANDLE-\n    handle: ");
    handle_print(handle.ihandle, handle.ihandlen);
    printf("\n    fd: %d\n    stat- ", ret);
    stat_print(ret);
    close(ret);
    
    handle.path=path;
    handle.ohandle=buffer;
    handle.ohandlen=&len;
    ret=xfsctl(fname, fsfd, XFS_IOC_PATH_TO_HANDLE, &handle);
    if (ret) {
        perror("xfsctl(XFS_IOC_PATH_TO_HANDLE)");
        exit(1);
    }
    printf("XFS_IOC_PATH_TO_HANDLE-\n    path: %s\n    handle: ", path);
    handle_print(handle.ohandle, *handle.ohandlen);
    printf("\n");

    handle.path=NULL;
    handle.fd=-1;
    handle.ihandle=buffer;
    handle.ihandlen=len;
    handle.ohandle=link;
    linklen=sizeof(link);
    handle.ohandlen=&linklen;
    ret=xfsctl(fname, fsfd, XFS_IOC_READLINK_BY_HANDLE, &handle);
    if (ret<0) {
        perror("xfsctl(XFS_IOC_READLINK_BY_HANDLE)");
        fprintf(stderr,"ERROR IGNORED\n");
    } else {
        printf("XFS_IOC_READLINK_BY_HANDLE-\n    handle: ");
        handle_print(handle.ihandle, handle.ihandlen);
        printf("\n    link=\"%*.*s\"\n", 
                ret, ret, (char*)handle.ohandle);
    }
}

int
main(int argc, char **argv)
{
    int                 fsfd;
    
    if (argc != 3) {
        fprintf(stderr,"usage: %s <filesystem> <file/link in FS>\n",
                argv[0]);
        exit(0);
    }

    fsfd = open(argv[1], O_RDONLY);
    if (fsfd < 0) {
	perror("open");
	exit(1);
    }
    
    /* XFS_IOC_FSCOUNTS */
    fscounts(argv[0], fsfd);
    /* XFS_IOC_GET_RESBLKS & XFS_IOC_SET_RESBLKS */
    getresblks(argv[0], fsfd);
    setresblks(argv[0], fsfd, 1000);
    getresblks(argv[0], fsfd);
    setresblks(argv[0], fsfd, 0);
    /* TODO - XFS_IOC_FSINUMBERS */

    /* XFS_IOC_PATH_TO_FSHANDLE */
    /* XFS_IOC_PATH_TO_HANDLE */
    /* XFS_IOC_FD_TO_HANDLE */
    /* XFS_IOC_OPEN_BY_HANDLE */
    /* XFS_IOC_READLINK_BY_HANDLE */
    handle(argv[0], fsfd, argv[2]);
    
    return 0;
}
