2012-02-03 19:00:17 +00:00
|
|
|
/*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public
|
|
|
|
* License v2 as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public
|
|
|
|
* License along with this program; if not, write to the
|
|
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
|
|
* Boston, MA 021110-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
2012-11-15 22:24:03 +00:00
|
|
|
#include <stdint.h>
|
2012-02-03 19:00:17 +00:00
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include "kerncompat.h"
|
|
|
|
#include "ioctl.h"
|
2013-01-28 05:22:30 +00:00
|
|
|
#include "utils.h"
|
2013-04-23 10:18:55 +00:00
|
|
|
#include "ctree.h"
|
|
|
|
#include "send-utils.h"
|
2012-02-03 19:00:17 +00:00
|
|
|
|
2012-02-03 19:00:17 +00:00
|
|
|
#include "commands.h"
|
2012-08-08 08:54:48 +00:00
|
|
|
#include "btrfs-list.h"
|
2012-02-03 19:00:17 +00:00
|
|
|
|
2012-02-08 15:45:54 +00:00
|
|
|
static const char * const inspect_cmd_group_usage[] = {
|
|
|
|
"btrfs inspect-internal <command> <args>",
|
|
|
|
NULL
|
|
|
|
};
|
2012-02-03 19:00:17 +00:00
|
|
|
|
2012-02-03 19:00:17 +00:00
|
|
|
static int __ino_to_path_fd(u64 inum, int fd, int verbose, const char *prepend)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int i;
|
|
|
|
struct btrfs_ioctl_ino_path_args ipa;
|
|
|
|
struct btrfs_data_container *fspath;
|
|
|
|
|
|
|
|
fspath = malloc(4096);
|
|
|
|
if (!fspath)
|
2013-09-04 15:22:24 +00:00
|
|
|
return -ENOMEM;
|
2012-02-03 19:00:17 +00:00
|
|
|
|
2013-01-25 00:18:51 +00:00
|
|
|
memset(fspath, 0, sizeof(*fspath));
|
2012-02-03 19:00:17 +00:00
|
|
|
ipa.inum = inum;
|
|
|
|
ipa.size = 4096;
|
2014-07-26 16:49:55 +00:00
|
|
|
ipa.fspath = ptr_to_u64(fspath);
|
2012-02-03 19:00:17 +00:00
|
|
|
|
|
|
|
ret = ioctl(fd, BTRFS_IOC_INO_PATHS, &ipa);
|
|
|
|
if (ret) {
|
|
|
|
printf("ioctl ret=%d, error: %s\n", ret, strerror(errno));
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (verbose)
|
|
|
|
printf("ioctl ret=%d, bytes_left=%lu, bytes_missing=%lu, "
|
|
|
|
"cnt=%d, missed=%d\n", ret,
|
|
|
|
(unsigned long)fspath->bytes_left,
|
|
|
|
(unsigned long)fspath->bytes_missing,
|
|
|
|
fspath->elem_cnt, fspath->elem_missed);
|
|
|
|
|
|
|
|
for (i = 0; i < fspath->elem_cnt; ++i) {
|
2013-02-14 18:29:50 +00:00
|
|
|
u64 ptr;
|
|
|
|
char *str;
|
|
|
|
ptr = (u64)(unsigned long)fspath->val;
|
|
|
|
ptr += fspath->val[i];
|
|
|
|
str = (char *)(unsigned long)ptr;
|
2012-02-03 19:00:17 +00:00
|
|
|
if (prepend)
|
2013-02-14 18:29:50 +00:00
|
|
|
printf("%s/%s\n", prepend, str);
|
2012-02-03 19:00:17 +00:00
|
|
|
else
|
2013-02-14 18:29:50 +00:00
|
|
|
printf("%s\n", str);
|
2012-02-03 19:00:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
free(fspath);
|
2013-09-04 15:22:24 +00:00
|
|
|
return !!ret;
|
2012-02-03 19:00:17 +00:00
|
|
|
}
|
|
|
|
|
2012-02-03 19:00:17 +00:00
|
|
|
static const char * const cmd_inode_resolve_usage[] = {
|
|
|
|
"btrfs inspect-internal inode-resolve [-v] <inode> <path>",
|
|
|
|
"Get file system paths for the given inode",
|
2013-07-23 02:43:21 +00:00
|
|
|
"",
|
|
|
|
"-v verbose mode",
|
2012-02-03 19:00:17 +00:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static int cmd_inode_resolve(int argc, char **argv)
|
2012-02-03 19:00:17 +00:00
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
int verbose = 0;
|
2013-01-24 18:45:00 +00:00
|
|
|
int ret;
|
2013-07-15 11:36:50 +00:00
|
|
|
DIR *dirstream = NULL;
|
2012-02-03 19:00:17 +00:00
|
|
|
|
|
|
|
optind = 1;
|
|
|
|
while (1) {
|
2012-02-03 19:00:17 +00:00
|
|
|
int c = getopt(argc, argv, "v");
|
2012-02-03 19:00:17 +00:00
|
|
|
if (c < 0)
|
|
|
|
break;
|
2012-02-03 19:00:17 +00:00
|
|
|
|
2012-02-03 19:00:17 +00:00
|
|
|
switch (c) {
|
|
|
|
case 'v':
|
|
|
|
verbose = 1;
|
|
|
|
break;
|
|
|
|
default:
|
2012-02-03 19:00:17 +00:00
|
|
|
usage(cmd_inode_resolve_usage);
|
2012-02-03 19:00:17 +00:00
|
|
|
}
|
|
|
|
}
|
2012-02-03 19:00:17 +00:00
|
|
|
|
|
|
|
if (check_argc_exact(argc - optind, 2))
|
|
|
|
usage(cmd_inode_resolve_usage);
|
2012-02-03 19:00:17 +00:00
|
|
|
|
2013-07-15 11:36:50 +00:00
|
|
|
fd = open_file_or_dir(argv[optind+1], &dirstream);
|
2012-02-03 19:00:17 +00:00
|
|
|
if (fd < 0) {
|
|
|
|
fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind+1]);
|
2013-09-04 15:22:24 +00:00
|
|
|
return 1;
|
2012-02-03 19:00:17 +00:00
|
|
|
}
|
|
|
|
|
2014-02-20 01:30:51 +00:00
|
|
|
ret = __ino_to_path_fd(arg_strtou64(argv[optind]), fd, verbose,
|
2013-01-24 18:45:00 +00:00
|
|
|
argv[optind+1]);
|
2013-07-15 11:36:50 +00:00
|
|
|
close_file_or_dir(fd, dirstream);
|
2013-09-04 15:22:24 +00:00
|
|
|
return !!ret;
|
2013-01-24 18:45:00 +00:00
|
|
|
|
2012-02-03 19:00:17 +00:00
|
|
|
}
|
|
|
|
|
2012-02-03 19:00:17 +00:00
|
|
|
static const char * const cmd_logical_resolve_usage[] = {
|
2012-08-31 05:40:44 +00:00
|
|
|
"btrfs inspect-internal logical-resolve [-Pv] [-s bufsize] <logical> <path>",
|
2012-02-03 19:00:17 +00:00
|
|
|
"Get file system paths for the given logical address",
|
2012-08-31 05:40:44 +00:00
|
|
|
"-P skip the path resolving and print the inodes instead",
|
|
|
|
"-v verbose mode",
|
|
|
|
"-s bufsize set inode container's size. This is used to increase inode",
|
|
|
|
" container's size in case it is not enough to read all the ",
|
|
|
|
" resolved results. The max value one can set is 64k",
|
2012-02-03 19:00:17 +00:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static int cmd_logical_resolve(int argc, char **argv)
|
2012-02-03 19:00:17 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int fd;
|
|
|
|
int i;
|
|
|
|
int verbose = 0;
|
|
|
|
int getpath = 1;
|
|
|
|
int bytes_left;
|
|
|
|
struct btrfs_ioctl_logical_ino_args loi;
|
|
|
|
struct btrfs_data_container *inodes;
|
2012-08-31 05:40:44 +00:00
|
|
|
u64 size = 4096;
|
2012-02-03 19:00:17 +00:00
|
|
|
char full_path[4096];
|
|
|
|
char *path_ptr;
|
2013-07-15 11:36:50 +00:00
|
|
|
DIR *dirstream = NULL;
|
2012-02-03 19:00:17 +00:00
|
|
|
|
|
|
|
optind = 1;
|
|
|
|
while (1) {
|
2012-08-31 05:40:44 +00:00
|
|
|
int c = getopt(argc, argv, "Pvs:");
|
2012-02-03 19:00:17 +00:00
|
|
|
if (c < 0)
|
|
|
|
break;
|
2012-02-03 19:00:17 +00:00
|
|
|
|
2012-02-03 19:00:17 +00:00
|
|
|
switch (c) {
|
|
|
|
case 'P':
|
|
|
|
getpath = 0;
|
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
verbose = 1;
|
|
|
|
break;
|
2012-08-31 05:40:44 +00:00
|
|
|
case 's':
|
2014-02-20 01:30:51 +00:00
|
|
|
size = arg_strtou64(optarg);
|
2012-08-31 05:40:44 +00:00
|
|
|
break;
|
2012-02-03 19:00:17 +00:00
|
|
|
default:
|
2012-02-03 19:00:17 +00:00
|
|
|
usage(cmd_logical_resolve_usage);
|
2012-02-03 19:00:17 +00:00
|
|
|
}
|
|
|
|
}
|
2012-02-03 19:00:17 +00:00
|
|
|
|
|
|
|
if (check_argc_exact(argc - optind, 2))
|
|
|
|
usage(cmd_logical_resolve_usage);
|
2012-02-03 19:00:17 +00:00
|
|
|
|
2012-10-05 00:35:31 +00:00
|
|
|
size = min(size, (u64)64 * 1024);
|
2012-08-31 05:40:44 +00:00
|
|
|
inodes = malloc(size);
|
2012-02-03 19:00:17 +00:00
|
|
|
if (!inodes)
|
|
|
|
return 1;
|
|
|
|
|
2013-01-25 00:18:51 +00:00
|
|
|
memset(inodes, 0, sizeof(*inodes));
|
2014-02-20 01:30:51 +00:00
|
|
|
loi.logical = arg_strtou64(argv[optind]);
|
2012-08-31 05:40:44 +00:00
|
|
|
loi.size = size;
|
2014-07-26 16:49:55 +00:00
|
|
|
loi.inodes = ptr_to_u64(inodes);
|
2012-02-03 19:00:17 +00:00
|
|
|
|
2013-07-15 11:36:50 +00:00
|
|
|
fd = open_file_or_dir(argv[optind+1], &dirstream);
|
2012-02-03 19:00:17 +00:00
|
|
|
if (fd < 0) {
|
|
|
|
fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind+1]);
|
|
|
|
ret = 12;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = ioctl(fd, BTRFS_IOC_LOGICAL_INO, &loi);
|
|
|
|
if (ret) {
|
|
|
|
printf("ioctl ret=%d, error: %s\n", ret, strerror(errno));
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (verbose)
|
2012-08-31 05:40:44 +00:00
|
|
|
printf("ioctl ret=%d, total_size=%llu, bytes_left=%lu, "
|
|
|
|
"bytes_missing=%lu, cnt=%d, missed=%d\n",
|
|
|
|
ret, size,
|
2012-02-03 19:00:17 +00:00
|
|
|
(unsigned long)inodes->bytes_left,
|
|
|
|
(unsigned long)inodes->bytes_missing,
|
|
|
|
inodes->elem_cnt, inodes->elem_missed);
|
|
|
|
|
|
|
|
bytes_left = sizeof(full_path);
|
|
|
|
ret = snprintf(full_path, bytes_left, "%s/", argv[optind+1]);
|
|
|
|
path_ptr = full_path + ret;
|
|
|
|
bytes_left -= ret + 1;
|
|
|
|
BUG_ON(bytes_left < 0);
|
|
|
|
|
|
|
|
for (i = 0; i < inodes->elem_cnt; i += 3) {
|
|
|
|
u64 inum = inodes->val[i];
|
|
|
|
u64 offset = inodes->val[i+1];
|
|
|
|
u64 root = inodes->val[i+2];
|
|
|
|
int path_fd;
|
|
|
|
char *name;
|
2013-07-15 11:36:50 +00:00
|
|
|
DIR *dirs = NULL;
|
2012-02-03 19:00:17 +00:00
|
|
|
|
|
|
|
if (getpath) {
|
Btrfs-progs: restructure list_subvolumes
The current code of list_subvols() has very bad scalability, if we want to
add new filter conditions or new sort methods, we have to modify lots of code.
Beside that, the most code of list_snapshots() is similar to list_subvols(),
So I restructure list_subvols(), and split the subvolume filter function,
the subvolume sort function and the output function from list_subvols().
In order to implement it, we defined some importtant structures:
struct btrfs_list_filter {
btrfs_list_filter_func filter_func;
void *data;
};
struct btrfs_list_comparer {
btrfs_list_comp_func comp_func;
int is_descending;
};
struct {
char *name;
char *column_name;
int need_print;
} btrfs_list_columns[];
If we want to add a new filter condition, we can choose a suitable filter
function, or implement a new filter function[1], and add it into a set of
the filters, and then pass the filter set into list_subvols(). We also can
mix several filters (just add those filters into the set, and pass the set
into list_subvols()) if the users specify two or more filter conditions.
The subvolume sort function is similar to the subvolume filter function. The
differentiation is the order of comparers in the array which is passed into
list_subvols() show us the priority of the sort methods.
The output function is different with the above two functions, we define a
array to manage all the columns that can be outputed, and use a member variant
(->need_print) to control the output of the relative column. Some columns are
outputed by default. But we can change it according to the requirement of the
users.
After appling this patch, we needn't implement a independent list_snapshots()
function, just pass a filter function which is used to identify the snapshot
into list_subvols().
[1]: If we implement new filter functions or compare functions, we must add
them into the array all_filter_funcs or the array all_comp_funcs, and modify
the relative enum variants(btrfs_list_filter_enum, btrfs_list_comp_enum).
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-18 06:51:49 +00:00
|
|
|
name = btrfs_list_path_for_root(fd, root);
|
2013-01-24 18:53:49 +00:00
|
|
|
if (IS_ERR(name)) {
|
|
|
|
ret = PTR_ERR(name);
|
|
|
|
goto out;
|
|
|
|
}
|
2012-02-03 19:00:17 +00:00
|
|
|
if (!name) {
|
|
|
|
path_ptr[-1] = '\0';
|
|
|
|
path_fd = fd;
|
|
|
|
} else {
|
|
|
|
path_ptr[-1] = '/';
|
|
|
|
ret = snprintf(path_ptr, bytes_left, "%s",
|
|
|
|
name);
|
|
|
|
BUG_ON(ret >= bytes_left);
|
|
|
|
free(name);
|
2013-07-15 11:36:50 +00:00
|
|
|
path_fd = open_file_or_dir(full_path, &dirs);
|
2012-02-03 19:00:17 +00:00
|
|
|
if (path_fd < 0) {
|
|
|
|
fprintf(stderr, "ERROR: can't access "
|
|
|
|
"'%s'\n", full_path);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
__ino_to_path_fd(inum, path_fd, verbose, full_path);
|
2013-01-24 18:53:49 +00:00
|
|
|
if (path_fd != fd)
|
2013-07-15 11:36:50 +00:00
|
|
|
close_file_or_dir(path_fd, dirs);
|
2012-02-03 19:00:17 +00:00
|
|
|
} else {
|
|
|
|
printf("inode %llu offset %llu root %llu\n", inum,
|
|
|
|
offset, root);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
2013-07-15 11:36:50 +00:00
|
|
|
close_file_or_dir(fd, dirstream);
|
2012-02-03 19:00:17 +00:00
|
|
|
free(inodes);
|
2013-09-04 15:22:24 +00:00
|
|
|
return !!ret;
|
2012-02-03 19:00:17 +00:00
|
|
|
}
|
|
|
|
|
2013-04-23 10:18:55 +00:00
|
|
|
static const char * const cmd_subvolid_resolve_usage[] = {
|
|
|
|
"btrfs inspect-internal subvolid-resolve <subvolid> <path>",
|
|
|
|
"Get file system paths for the given subvolume ID.",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static int cmd_subvolid_resolve(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int fd = -1;
|
|
|
|
u64 subvol_id;
|
|
|
|
char path[BTRFS_PATH_NAME_MAX + 1];
|
2013-07-15 11:36:50 +00:00
|
|
|
DIR *dirstream = NULL;
|
2013-04-23 10:18:55 +00:00
|
|
|
|
|
|
|
if (check_argc_exact(argc, 3))
|
|
|
|
usage(cmd_subvolid_resolve_usage);
|
|
|
|
|
2013-07-15 11:36:50 +00:00
|
|
|
fd = open_file_or_dir(argv[2], &dirstream);
|
2013-04-23 10:18:55 +00:00
|
|
|
if (fd < 0) {
|
|
|
|
fprintf(stderr, "ERROR: can't access '%s'\n", argv[2]);
|
|
|
|
ret = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2014-02-20 01:30:51 +00:00
|
|
|
subvol_id = arg_strtou64(argv[1]);
|
2013-04-23 10:18:55 +00:00
|
|
|
ret = btrfs_subvolid_resolve(fd, path, sizeof(path), subvol_id);
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"%s: btrfs_subvolid_resolve(subvol_id %llu) failed with ret=%d\n",
|
|
|
|
argv[0], (unsigned long long)subvol_id, ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
path[BTRFS_PATH_NAME_MAX] = '\0';
|
|
|
|
printf("%s\n", path);
|
|
|
|
|
|
|
|
out:
|
2013-07-15 11:36:50 +00:00
|
|
|
close_file_or_dir(fd, dirstream);
|
2013-04-23 10:18:55 +00:00
|
|
|
return ret ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
2013-09-18 16:19:30 +00:00
|
|
|
static const char* const cmd_rootid_usage[] = {
|
|
|
|
"btrfs inspect-internal rootid <path>",
|
|
|
|
"Get tree ID of the containing subvolume of path.",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static int cmd_rootid(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int fd = -1;
|
|
|
|
u64 rootid;
|
|
|
|
DIR *dirstream = NULL;
|
|
|
|
|
|
|
|
if (check_argc_exact(argc, 2))
|
|
|
|
usage(cmd_rootid_usage);
|
|
|
|
|
|
|
|
fd = open_file_or_dir(argv[1], &dirstream);
|
|
|
|
if (fd < 0) {
|
|
|
|
fprintf(stderr, "ERROR: can't access '%s'\n", argv[1]);
|
|
|
|
ret = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = lookup_ino_rootid(fd, &rootid);
|
|
|
|
if (ret) {
|
|
|
|
fprintf(stderr, "%s: rootid failed with ret=%d\n",
|
|
|
|
argv[0], ret);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("%llu\n", (unsigned long long)rootid);
|
|
|
|
out:
|
|
|
|
close_file_or_dir(fd, dirstream);
|
|
|
|
|
|
|
|
return !!ret;
|
|
|
|
}
|
|
|
|
|
2012-02-03 19:00:17 +00:00
|
|
|
const struct cmd_group inspect_cmd_group = {
|
|
|
|
inspect_cmd_group_usage, NULL, {
|
|
|
|
{ "inode-resolve", cmd_inode_resolve, cmd_inode_resolve_usage,
|
|
|
|
NULL, 0 },
|
|
|
|
{ "logical-resolve", cmd_logical_resolve,
|
|
|
|
cmd_logical_resolve_usage, NULL, 0 },
|
2013-04-23 10:18:55 +00:00
|
|
|
{ "subvolid-resolve", cmd_subvolid_resolve,
|
|
|
|
cmd_subvolid_resolve_usage, NULL, 0 },
|
2013-09-18 16:19:30 +00:00
|
|
|
{ "rootid", cmd_rootid, cmd_rootid_usage, NULL, 0 },
|
2013-08-14 23:16:45 +00:00
|
|
|
NULL_CMD_STRUCT
|
2012-02-03 19:00:17 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
int cmd_inspect(int argc, char **argv)
|
|
|
|
{
|
|
|
|
return handle_command_group(&inspect_cmd_group, argc, argv);
|
|
|
|
}
|