static int dir_is_mount_point(DIR *d, const char *subdir) { struct file_handle *h; int mount_id_parent, mount_id; int r_p, r; h = alloca(MAX_HANDLE_SZ); h->handle_bytes = MAX_HANDLE_SZ; r_p = name_to_handle_at(dirfd(d), ".", h, &mount_id_parent, 0); if (r_p < 0) r_p = -errno; h->handle_bytes = MAX_HANDLE_SZ; r = name_to_handle_at(dirfd(d), subdir, h, &mount_id, 0); if (r < 0) r = -errno; /* got no handle; make no assumptions, return error */ if (r_p < 0 && r < 0) return r_p; /* got both handles; if they differ, it is a mount point */ if (r_p >= 0 && r >= 0) return mount_id_parent != mount_id; /* got only one handle; assume different mount points if one * of both queries was not supported by the filesystem */ if (r_p == -ENOSYS || r_p == -ENOTSUP || r == -ENOSYS || r == -ENOTSUP) return true; /* return error */ if (r_p < 0) return r_p; return r; }
int main(void) { struct file_handle *handle = alloca(sizeof(struct file_handle) + MAX_HANDLE_SZ); const int dirfd = AT_FDCWD; const int flags = AT_SYMLINK_FOLLOW; int mount_id; unsigned int i; handle->handle_bytes = 0; assert(name_to_handle_at(dirfd, ".", handle, &mount_id, flags | 1) == -1); if (EINVAL != errno) perror_msg_and_skip("name_to_handle_at"); printf("name_to_handle_at(AT_FDCWD, \".\", {handle_bytes=0}, %p" ", AT_SYMLINK_FOLLOW|0x1) = -1 EINVAL (%m)\n", &mount_id); assert(name_to_handle_at(dirfd, ".", handle, &mount_id, flags) == -1); if (EOVERFLOW != errno) perror_msg_and_skip("name_to_handle_at"); printf("name_to_handle_at(AT_FDCWD, \".\", {handle_bytes=0 => %u}" ", %p, AT_SYMLINK_FOLLOW) = -1 EOVERFLOW (%m)\n", handle->handle_bytes, &mount_id); assert(name_to_handle_at(dirfd, ".", handle, &mount_id, flags) == 0); printf("name_to_handle_at(AT_FDCWD, \".\", {handle_bytes=%u" ", handle_type=%d, f_handle=0x", handle->handle_bytes, handle->handle_type); for (i = 0; i < handle->handle_bytes; ++i) printf("%02x", handle->f_handle[i]); printf("}, [%d], AT_SYMLINK_FOLLOW) = 0\n", mount_id); printf("open_by_handle_at(-1, {handle_bytes=%u, handle_type=%d" ", f_handle=0x", handle->handle_bytes, handle->handle_type); for (i = 0; i < handle->handle_bytes; ++i) printf("%02x", handle->f_handle[i]); printf("}, O_RDONLY|O_DIRECTORY) = -1 "); assert(open_by_handle_at(-1, handle, O_RDONLY | O_DIRECTORY) == -1); const char *errno_text; switch (errno) { case EPERM: errno_text = "EPERM"; break; case EINVAL: errno_text = "EINVAL"; break; default: errno_text = "EBADF"; } printf("%s (%m)\n", errno_text); puts("+++ exited with 0 +++"); return 0; }
int main(int argc, char **argv) { #ifdef HAVE_FHANDLE_SYSCALLS char *filename, *file, *mount_point = NULL, *readbuf = NULL; int ret, rc = -EINVAL, mnt_id, mnt_fd, fd1, fd2, i, len, offset; struct file_handle *fh = NULL; int file_size, mtime, ctime; struct lu_fid *parent, *fid; struct mntent *ent; struct stat st; __ino_t inode; FILE *mntpt; if (argc != 2) usage(argv[0]); file = argv[1]; if (file[0] != '/') { fprintf(stderr, "Need the absolete path of the file\n"); goto out; } fd1 = open(file, O_RDONLY); 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)); rc = fstat(fd1, &st); if (rc < 0) { fprintf(stderr, "fstat(%s) error: %s\n", file, strerror(errno)); rc = errno; goto out_fd1; } inode = st.st_ino; mtime = st.st_mtime; ctime = st.st_ctime; file_size = st.st_size; /* Now for the setup to use fhandles */ mntpt = setmntent("/etc/mtab", "r"); if (mntpt == NULL) { fprintf(stderr, "setmntent error: %s\n", strerror(errno)); rc = errno; goto out_fd1; } while (NULL != (ent = getmntent(mntpt))) { if ((strncmp(file, ent->mnt_dir, strlen(ent->mnt_dir)) == 0) && (strcmp(ent->mnt_type, "lustre") == 0)) { mount_point = ent->mnt_dir; break; } } endmntent(mntpt); if (mount_point == NULL) { fprintf(stderr, "file is not located on a lustre file " "system?\n"); goto out_fd1; } filename = rindex(file, '/') + 1; /* Open mount point directory */ mnt_fd = open(mount_point, O_DIRECTORY); if (mnt_fd < 0) { fprintf(stderr, "open(%s) error: %s\n)", mount_point, 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 */ ret = name_to_handle_at(mnt_fd, filename, fh, &mnt_id, AT_SYMLINK_FOLLOW); 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, "fh_bytes: %u\nfh_type: %d\nfh_data: ", 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)); /* Open the file handle */ fd2 = open_by_handle_at(mnt_fd, fh, O_RDONLY); if (fd2 < 0) { fprintf(stderr, "open_by_handle_at(%s) error: %s\n", filename, strerror(errno)); rc = errno; goto out_f_handle; } /* Get file size */ bzero(&st, sizeof(struct stat)); rc = fstat(fd2, &st); if (rc < 0) { fprintf(stderr, "fstat(%s) error: %s\n", filename, strerror(errno)); rc = errno; goto out_fd2; } if (ctime != st.st_ctime || file_size != st.st_size || inode != st.st_ino || mtime != st.st_mtime) { fprintf(stderr, "stat data does not match between fopen " "and fhandle case\n"); goto out_fd2; } if (st.st_size) { 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; fprintf(stdout, "check_fhandle_syscalls test Passed!\n"); out_readbuf: if (readbuf != NULL) free(readbuf); out_fd2: close(fd2); out_f_handle: free(fh); out_mnt_fd: close(mnt_fd); out_fd1: close(fd1); out: return rc; #else /* !HAVE_FHANDLE_SYSCALLS */ if (argc != 2) usage(argv[0]); fprintf(stderr, "HAVE_FHANDLE_SYSCALLS not defined\n"); return 0; #endif /* HAVE_FHANDLE_SYSCALLS */ }
int fs_on_ssd(const char *p) { struct stat st; _cleanup_udev_unref_ struct udev *udev = NULL; _cleanup_udev_device_unref_ struct udev_device *udev_device = NULL; struct udev_device *look_at = NULL; const char *devtype, *rotational, *model, *id; int r; assert(p); if (stat(p, &st) < 0) return -errno; if (major(st.st_dev) == 0) { _cleanup_fclose_ FILE *f = NULL; int mount_id; struct file_handle *h; /* Might be btrfs, which exposes "ssd" as mount flag if it is on ssd. * * We first determine the mount ID here, if we can, * and then lookup the mount ID in mountinfo to find * the mount options. */ h = alloca(MAX_HANDLE_SZ); h->handle_bytes = MAX_HANDLE_SZ; r = name_to_handle_at(AT_FDCWD, p, h, &mount_id, AT_SYMLINK_FOLLOW); if (r < 0) return false; f = fopen("/proc/self/mountinfo", "re"); if (!f) return false; for (;;) { char line[LINE_MAX], *e; _cleanup_free_ char *opts = NULL; int mid; if (!fgets(line, sizeof(line), f)) return false; truncate_nl(line); if (sscanf(line, "%i", &mid) != 1) continue; if (mid != mount_id) continue; e = strstr(line, " - "); if (!e) continue; if (sscanf(e+3, "%*s %*s %ms", &opts) != 1) continue; if (streq(opts, "ssd") || startswith(opts, "ssd,") || endswith(opts, ",ssd") || strstr(opts, ",ssd,")) return true; } return false; } udev = udev_new(); if (!udev) return -ENOMEM; udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev); if (!udev_device) return false; devtype = udev_device_get_property_value(udev_device, "DEVTYPE"); if (devtype && streq(devtype, "partition")) look_at = udev_device_get_parent(udev_device); else look_at = udev_device; if (!look_at) return false; /* First, try high-level property */ id = udev_device_get_property_value(look_at, "ID_SSD"); if (id) return streq(id, "1"); /* Second, try kernel attribute */ rotational = udev_device_get_sysattr_value(look_at, "queue/rotational"); if (rotational) return streq(rotational, "0"); /* Finally, fallback to heuristics */ look_at = udev_device_get_parent(look_at); if (!look_at) return false; model = udev_device_get_sysattr_value(look_at, "model"); if (model) return !!strstr(model, "SSD"); return false; }
int main(int argc, char *argv[]) { struct file_handle *fhp; int mount_id, fhsize, flags, dirfd, j; char *pathname; if (argc != 2) { fprintf(stderr, "Usage: %s pathname\n", argv[0]); exit(EXIT_FAILURE); } pathname = argv[1]; /* Allocate file_handle structure */ fhsize = sizeof(*fhp); fhp = malloc(fhsize); if (fhp == NULL) errExit("malloc"); /* Make an initial call to name_to_handle_at() to discover the size required for file handle */ dirfd = AT_FDCWD; /* For name_to_handle_at() calls */ flags = 0; /* For name_to_handle_at() calls */ fhp->handle_bytes = 0; if (name_to_handle_at(dirfd, pathname, fhp, &mount_id, flags) != -1 || errno != EOVERFLOW) { fprintf(stderr, "Unexpected result from name_to_handle_at()\n"); exit(EXIT_FAILURE); } /* Reallocate file_handle structure with correct size */ fhsize = sizeof(struct file_handle) + fhp->handle_bytes; fhp = realloc(fhp, fhsize); /* Copies fhp->handle_bytes */ if (fhp == NULL) errExit("realloc"); /* Get file handle from pathname supplied on command line */ if (name_to_handle_at(dirfd, pathname, fhp, &mount_id, flags) == -1) errExit("name_to_handle_at"); /* Write mount ID, file handle size, and file handle to stdout, for later reuse by t_open_by_handle_at.c */ printf("%d\n", mount_id); printf("%d %d ", fhp->handle_bytes, fhp->handle_type); for (j = 0; j < fhp->handle_bytes; j++) printf(" %02x", fhp->f_handle[j]); printf("\n"); // Open file int fd = open_by_handle_at(AT_FDCWD, fhp, O_RDONLY); char a[1024]; int r = read(fd, a, 1000); printf("%d > %s [%d]\n", fd, a, r); for(int i = 0; i < r; i++) { printf("%02x", a[i] & 0xff); } printf("\n"); for(int i = 0; i < r; i++) { printf("%02x ", a[i] & 0xff); } printf("\n"); printf("END\n"); exit(EXIT_SUCCESS); }
/* * stress_handle() * stress system by rapid open/close calls via * name_to_handle_at and open_by_handle_at */ int stress_handle( uint64_t *const counter, const uint32_t instance, const uint64_t max_ops, const char *name) { int mounts; (void)instance; if ((mounts = get_mount_info(name)) < 0) { pr_fail(stderr, "%s: failed to parse /proc/self/mountinfo\n", name); return EXIT_FAILURE; } do { struct file_handle *fhp, *tmp; int mount_id, mount_fd, fd, i; if ((fhp = malloc(sizeof(*fhp))) == NULL) continue; fhp->handle_bytes = 0; if ((name_to_handle_at(AT_FDCWD, FILENAME, fhp, &mount_id, 0) != -1) && (errno != EOVERFLOW)) { pr_fail(stderr, "%s: name_to_handle_at: failed to get file handle size: errno=%d (%s)\n", name, errno, strerror(errno)); free(fhp); break; } tmp = realloc(fhp, sizeof(struct file_handle) + fhp->handle_bytes); if (tmp == NULL) { free(fhp); continue; } fhp = tmp; if (name_to_handle_at(AT_FDCWD, FILENAME, fhp, &mount_id, 0) < 0) { pr_fail(stderr, "%s: name_to_handle_at: failed to get file handle: errno=%d (%s)\n", name, errno, strerror(errno)); free(fhp); break; } mount_fd = -2; for (i = 0; i < mounts; i++) { if (mount_info[i].mount_id == mount_id) { mount_fd = open(mount_info[i].mount_path, O_RDONLY); break; } } if (mount_fd == -2) { pr_fail(stderr, "%s: cannot find mount id %d\n", name, mount_id); free(fhp); break; } if (mount_fd < 0) { pr_fail(stderr, "%s: failed to open mount path '%s': errno=%d (%s)\n", name, mount_info[i].mount_path, errno, strerror(errno)); free(fhp); break; } if ((fd = open_by_handle_at(mount_fd, fhp, O_RDONLY)) < 0) { /* We don't abort if EPERM occurs, that's not a test failure */ if (errno != EPERM) { pr_fail(stderr, "%s: open_by_handle_at: failed to open: errno=%d (%s)\n", name, errno, strerror(errno)); (void)close(mount_fd); free(fhp); break; } } else { (void)close(fd); } (void)close(mount_fd); free(fhp); } while (opt_do_run && (!max_ops || *counter < max_ops)); free_mount_info(mounts); return EXIT_SUCCESS; }