| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (c) 2005-2006 Silicon Graphics, Inc. |
| * All Rights Reserved. |
| */ |
| |
| #include "command.h" |
| #include "input.h" |
| #include "libfrog/paths.h" |
| #include "parent.h" |
| #include "handle.h" |
| #include "jdm.h" |
| #include "init.h" |
| #include "io.h" |
| |
| #define PARENTBUF_SZ 16384 |
| #define BSTATBUF_SZ 16384 |
| |
| static cmdinfo_t parent_cmd; |
| static int verbose_flag; |
| static int err_status; |
| static __u64 inodes_checked; |
| static char *mntpt; |
| |
| /* |
| * check out a parent entry to see if the values seem valid |
| */ |
| static void |
| check_parent_entry(struct xfs_bstat *bstatp, parent_t *parent) |
| { |
| int sts; |
| char fullpath[PATH_MAX]; |
| struct stat statbuf; |
| char *str; |
| |
| sprintf(fullpath, _("%s%s"), mntpt, parent->p_name); |
| |
| sts = lstat(fullpath, &statbuf); |
| if (sts != 0) { |
| fprintf(stderr, |
| _("inode-path for inode: %llu is incorrect - path \"%s\" non-existent\n"), |
| (unsigned long long) bstatp->bs_ino, fullpath); |
| if (verbose_flag) { |
| fprintf(stderr, |
| _("path \"%s\" does not stat for inode: %llu; err = %s\n"), |
| fullpath, |
| (unsigned long long) bstatp->bs_ino, |
| strerror(errno)); |
| } |
| err_status++; |
| return; |
| } else { |
| if (verbose_flag > 1) { |
| printf(_("path \"%s\" found\n"), fullpath); |
| } |
| } |
| |
| if (statbuf.st_ino != bstatp->bs_ino) { |
| fprintf(stderr, |
| _("inode-path for inode: %llu is incorrect - wrong inode#\n"), |
| (unsigned long long) bstatp->bs_ino); |
| if (verbose_flag) { |
| fprintf(stderr, |
| _("ino mismatch for path \"%s\" %llu vs %llu\n"), |
| fullpath, |
| (unsigned long long)statbuf.st_ino, |
| (unsigned long long)bstatp->bs_ino); |
| } |
| err_status++; |
| return; |
| } else if (verbose_flag > 1) { |
| printf(_("inode number match: %llu\n"), |
| (unsigned long long)statbuf.st_ino); |
| } |
| |
| /* get parent path */ |
| str = strrchr(fullpath, '/'); |
| *str = '\0'; |
| sts = stat(fullpath, &statbuf); |
| if (sts != 0) { |
| fprintf(stderr, |
| _("parent path \"%s\" does not stat: %s\n"), |
| fullpath, |
| strerror(errno)); |
| err_status++; |
| return; |
| } else { |
| if (parent->p_ino != statbuf.st_ino) { |
| fprintf(stderr, |
| _("inode-path for inode: %llu is incorrect - wrong parent inode#\n"), |
| (unsigned long long) bstatp->bs_ino); |
| if (verbose_flag) { |
| fprintf(stderr, |
| _("ino mismatch for path \"%s\" %llu vs %llu\n"), |
| fullpath, |
| (unsigned long long)parent->p_ino, |
| (unsigned long long)statbuf.st_ino); |
| } |
| err_status++; |
| return; |
| } else { |
| if (verbose_flag > 1) { |
| printf(_("parent ino match for %llu\n"), |
| (unsigned long long) parent->p_ino); |
| } |
| } |
| } |
| } |
| |
| static void |
| check_parents(parent_t *parentbuf, size_t *parentbuf_size, |
| jdm_fshandle_t *fshandlep, struct xfs_bstat *statp) |
| { |
| int error, i; |
| __u32 count; |
| parent_t *entryp; |
| |
| do { |
| error = jdm_parentpaths(fshandlep, statp, parentbuf, *parentbuf_size, &count); |
| |
| if (error == ERANGE) { |
| *parentbuf_size *= 2; |
| parentbuf = (parent_t *)realloc(parentbuf, *parentbuf_size); |
| } else if (error) { |
| fprintf(stderr, _("parentpaths failed for ino %llu: %s\n"), |
| (unsigned long long) statp->bs_ino, |
| strerror(errno)); |
| err_status++; |
| break; |
| } |
| } while (error == ERANGE); |
| |
| |
| if (count == 0) { |
| /* no links for inode - something wrong here */ |
| fprintf(stderr, _("inode-path for inode: %llu is missing\n"), |
| (unsigned long long) statp->bs_ino); |
| err_status++; |
| } |
| |
| entryp = parentbuf; |
| for (i = 0; i < count; i++) { |
| check_parent_entry(statp, entryp); |
| entryp = (parent_t*) (((char*)entryp) + entryp->p_reclen); |
| } |
| } |
| |
| static int |
| do_bulkstat(parent_t *parentbuf, size_t *parentbuf_size, |
| struct xfs_bstat *bstatbuf, int fsfd, jdm_fshandle_t *fshandlep) |
| { |
| __s32 buflenout; |
| __u64 lastino = 0; |
| struct xfs_bstat *p; |
| struct xfs_bstat *endp; |
| struct xfs_fsop_bulkreq bulkreq; |
| struct stat mntstat; |
| |
| if (stat(mntpt, &mntstat)) { |
| fprintf(stderr, _("can't stat mount point \"%s\": %s\n"), |
| mntpt, strerror(errno)); |
| return 1; |
| } |
| |
| bulkreq.lastip = &lastino; |
| bulkreq.icount = BSTATBUF_SZ; |
| bulkreq.ubuffer = (void *)bstatbuf; |
| bulkreq.ocount = &buflenout; |
| |
| while (xfsctl(mntpt, fsfd, XFS_IOC_FSBULKSTAT, &bulkreq) == 0) { |
| if (*(bulkreq.ocount) == 0) { |
| return 0; |
| } |
| for (p = bstatbuf, endp = bstatbuf + *bulkreq.ocount; p < endp; p++) { |
| |
| /* inode being modified, get synced data with iget */ |
| if ( (!p->bs_nlink || !p->bs_mode) && p->bs_ino != 0 ) { |
| |
| if (xfsctl(mntpt, fsfd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq) < 0) { |
| fprintf(stderr, |
| _("failed to get bulkstat information for inode %llu\n"), |
| (unsigned long long) p->bs_ino); |
| continue; |
| } |
| if (!p->bs_nlink || !p->bs_mode || !p->bs_ino) { |
| fprintf(stderr, |
| _("failed to get valid bulkstat information for inode %llu\n"), |
| (unsigned long long) p->bs_ino); |
| continue; |
| } |
| } |
| |
| /* skip root */ |
| if (p->bs_ino == mntstat.st_ino) { |
| continue; |
| } |
| |
| if (verbose_flag > 1) { |
| printf(_("checking inode %llu\n"), |
| (unsigned long long) p->bs_ino); |
| } |
| |
| /* print dotted progress */ |
| if ((inodes_checked % 100) == 0 && verbose_flag == 1) { |
| printf("."); fflush(stdout); |
| } |
| inodes_checked++; |
| |
| check_parents(parentbuf, parentbuf_size, fshandlep, p); |
| } |
| |
| }/*while*/ |
| |
| fprintf(stderr, _("syssgi bulkstat failed: %s\n"), strerror(errno)); |
| return 1; |
| } |
| |
| static int |
| parent_check(void) |
| { |
| int fsfd; |
| jdm_fshandle_t *fshandlep; |
| parent_t *parentbuf; |
| size_t parentbuf_size = PARENTBUF_SZ; |
| struct xfs_bstat *bstatbuf; |
| |
| err_status = 0; |
| inodes_checked = 0; |
| |
| sync(); |
| |
| fsfd = file->fd; |
| |
| fshandlep = jdm_getfshandle(mntpt); |
| if (fshandlep == NULL) { |
| fprintf(stderr, _("unable to open \"%s\" for jdm: %s\n"), |
| mntpt, |
| strerror(errno)); |
| return 1; |
| } |
| |
| /* allocate buffers */ |
| bstatbuf = (struct xfs_bstat *)calloc(BSTATBUF_SZ, sizeof(struct xfs_bstat)); |
| parentbuf = (parent_t *)malloc(parentbuf_size); |
| if (!bstatbuf || !parentbuf) { |
| fprintf(stderr, _("unable to allocate buffers: %s\n"), |
| strerror(errno)); |
| err_status = 1; |
| goto out; |
| } |
| |
| if (do_bulkstat(parentbuf, &parentbuf_size, bstatbuf, fsfd, fshandlep) != 0) |
| err_status++; |
| |
| if (err_status > 0) |
| fprintf(stderr, _("num errors: %d\n"), err_status); |
| else |
| printf(_("succeeded checking %llu inodes\n"), |
| (unsigned long long) inodes_checked); |
| |
| out: |
| free(bstatbuf); |
| free(parentbuf); |
| free(fshandlep); |
| return err_status; |
| } |
| |
| static void |
| print_parent_entry(parent_t *parent, int fullpath) |
| { |
| printf(_("p_ino = %llu\n"), (unsigned long long) parent->p_ino); |
| printf(_("p_gen = %u\n"), parent->p_gen); |
| printf(_("p_reclen = %u\n"), parent->p_reclen); |
| if (fullpath) |
| printf(_("p_name = \"%s%s\"\n"), mntpt, parent->p_name); |
| else |
| printf(_("p_name = \"%s\"\n"), parent->p_name); |
| } |
| |
| static int |
| parent_list(int fullpath) |
| { |
| void *handlep = NULL; |
| size_t handlen; |
| int error, i; |
| int retval = 1; |
| __u32 count; |
| parent_t *entryp; |
| parent_t *parentbuf = NULL; |
| char *path = file->name; |
| int pb_size = PARENTBUF_SZ; |
| |
| /* XXXX for linux libhandle version - to set libhandle fsfd cache */ |
| { |
| void *fshandle; |
| size_t fshlen; |
| |
| if (path_to_fshandle(mntpt, &fshandle, &fshlen) != 0) { |
| fprintf(stderr, _("%s: failed path_to_fshandle \"%s\": %s\n"), |
| progname, path, strerror(errno)); |
| goto error; |
| } |
| free_handle(fshandle, fshlen); |
| } |
| |
| if (path_to_handle(path, &handlep, &handlen) != 0) { |
| fprintf(stderr, _("%s: path_to_handle failed for \"%s\"\n"), progname, path); |
| goto error; |
| } |
| |
| do { |
| parentbuf = (parent_t *)realloc(parentbuf, pb_size); |
| if (!parentbuf) { |
| fprintf(stderr, _("%s: unable to allocate parent buffer: %s\n"), |
| progname, strerror(errno)); |
| goto error; |
| } |
| |
| if (fullpath) { |
| error = parentpaths_by_handle(handlep, |
| handlen, |
| parentbuf, |
| pb_size, |
| &count); |
| } else { |
| error = parents_by_handle(handlep, |
| handlen, |
| parentbuf, |
| pb_size, |
| &count); |
| } |
| if (error == ERANGE) { |
| pb_size *= 2; |
| } else if (error) { |
| fprintf(stderr, _("%s: %s call failed for \"%s\": %s\n"), |
| progname, fullpath ? "parentpaths" : "parents", |
| path, strerror(errno)); |
| goto error; |
| } |
| } while (error == ERANGE); |
| |
| if (count == 0) { |
| /* no links for inode - something wrong here */ |
| fprintf(stderr, _("%s: inode-path is missing\n"), progname); |
| goto error; |
| } |
| |
| entryp = parentbuf; |
| for (i = 0; i < count; i++) { |
| print_parent_entry(entryp, fullpath); |
| entryp = (parent_t*) (((char*)entryp) + entryp->p_reclen); |
| } |
| |
| retval = 0; |
| error: |
| free(handlep); |
| free(parentbuf); |
| return retval; |
| } |
| |
| static int |
| parent_f(int argc, char **argv) |
| { |
| int c; |
| int listpath_flag = 0; |
| int check_flag = 0; |
| fs_path_t *fs; |
| static int tab_init; |
| |
| if (!tab_init) { |
| tab_init = 1; |
| fs_table_initialise(0, NULL, 0, NULL); |
| } |
| fs = fs_table_lookup(file->name, FS_MOUNT_POINT); |
| if (!fs) { |
| fprintf(stderr, _("file argument, \"%s\", is not in a mounted XFS filesystem\n"), |
| file->name); |
| exitcode = 1; |
| return 1; |
| } |
| mntpt = fs->fs_dir; |
| |
| verbose_flag = 0; |
| |
| while ((c = getopt(argc, argv, "cpv")) != EOF) { |
| switch (c) { |
| case 'c': |
| check_flag = 1; |
| break; |
| case 'p': |
| listpath_flag = 1; |
| break; |
| case 'v': |
| verbose_flag++; |
| break; |
| default: |
| return command_usage(&parent_cmd); |
| } |
| } |
| |
| if (!check_flag && !listpath_flag) /* default case */ |
| exitcode = parent_list(listpath_flag); |
| else { |
| if (listpath_flag) |
| exitcode = parent_list(listpath_flag); |
| if (check_flag) |
| exitcode = parent_check(); |
| } |
| |
| return 0; |
| } |
| |
| static void |
| parent_help(void) |
| { |
| printf(_( |
| "\n" |
| " list the current file's parents and their filenames\n" |
| "\n" |
| " -c -- check the current file's file system for parent consistency\n" |
| " -p -- list the current file's parents and their full paths\n" |
| " -v -- verbose mode\n" |
| "\n")); |
| } |
| |
| void |
| parent_init(void) |
| { |
| parent_cmd.name = "parent"; |
| parent_cmd.cfunc = parent_f; |
| parent_cmd.argmin = 0; |
| parent_cmd.argmax = -1; |
| parent_cmd.args = _("[-cpv]"); |
| parent_cmd.flags = CMD_NOMAP_OK; |
| parent_cmd.oneline = _("print or check parent inodes"); |
| parent_cmd.help = parent_help; |
| |
| if (expert) |
| add_command(&parent_cmd); |
| } |