[Ocfs2-tools-devel] [PATCH 5/5] O2info: Add new running codes for '--filestat'.

Tristan Ye tristan.ye at oracle.com
Sat Jan 29 22:31:28 PST 2011


This patch not only tries to teach o2info to dump file's standard stat
information, but also report some extended info such as extents, frag,
holes, unwritten and shared space in clusters, which also was gained
mainly by the help of new 'FS_IOC_FIEMAP' ioctl.

Signed-off-by: Tristan Ye <tristan.ye at oracle.com>
---
 o2info/o2info.1.in  |    6 +-
 o2info/o2info.c     |   15 +++
 o2info/operations.c |  136 +++++++++++++++++++++++
 o2info/utils.c      |  302 +++++++++++++++++++++++++++++++++++++++++++++++++++
 o2info/utils.h      |    9 ++
 5 files changed, 467 insertions(+), 1 deletions(-)

diff --git a/o2info/o2info.1.in b/o2info/o2info.1.in
index 3b6ee5c..fd84967 100644
--- a/o2info/o2info.1.in
+++ b/o2info/o2info.1.in
@@ -2,7 +2,7 @@
 .SH "NAME"
 o2info \- Dump \fIOCFS2\fR file system information on disk.
 .SH "SYNOPSIS"
-\fBo2info\fR [\fB\-C|\-\-cluster\-coherent\fR] [\fB\-\-fs\-features\fR] [\fB\-\-volinfo\fR] [\fB\-\-mkfs\fR] [\fB\-\-freeinode\fR] [\fB\-\-freefrag\fR \fIchunksize\fR] [\fB\-\-space\-usage\fR] <\fBdevice or file\fR>
+\fBo2info\fR [\fB\-C|\-\-cluster\-coherent\fR] [\fB\-\-fs\-features\fR] [\fB\-\-volinfo\fR] [\fB\-\-mkfs\fR] [\fB\-\-freeinode\fR] [\fB\-\-freefrag\fR \fIchunksize\fR] [\fB\-\-space\-usage\fR] [\fB\-\-filestat\fR] <\fBdevice or file\fR>
 
 .SH "DESCRIPTION"
 .PP
@@ -39,6 +39,10 @@ Report free space fragmentation on \fIOCFS2\fR filesystems, it prints how many c
 Display file's usage information on disk in terms of how many physical blocks it consumed, how many reserved space and holes in it.
 
 .TP
+\fB\-\-filestat\fR
+Display inode's standard stat information, other than this, it also dumps inode's extended info such as extents, fragmentation, holes, unwritten and shared space in clusters.
+
+.TP
 \fB\-V, \-\-version\fR
 Show version and exit.
 
diff --git a/o2info/o2info.c b/o2info/o2info.c
index c19e954..ef9468c 100644
--- a/o2info/o2info.c
+++ b/o2info/o2info.c
@@ -40,6 +40,7 @@ extern struct o2info_operation mkfs_op;
 extern struct o2info_operation freeinode_op;
 extern struct o2info_operation freefrag_op;
 extern struct o2info_operation space_usage_op;
+extern struct o2info_operation filestat_op;
 
 static LIST_HEAD(o2info_op_task_list);
 static int o2info_op_task_count;
@@ -183,6 +184,19 @@ static struct o2info_option space_usage_option = {
 	.opt_private	= NULL,
 };
 
+static struct o2info_option filestat_option = {
+	.opt_option	= {
+		.name		= "filestat",
+		.val		= CHAR_MAX,
+		.has_arg	= 0,
+		.flag		= NULL,
+	},
+	.opt_help	= "   --filestat",
+	.opt_handler	= NULL,
+	.opt_op		= &filestat_op,
+	.opt_private	= NULL,
+};
+
 static struct o2info_option *options[] = {
 	&help_option,
 	&version_option,
@@ -193,6 +207,7 @@ static struct o2info_option *options[] = {
 	&freeinode_option,
 	&freefrag_option,
 	&space_usage_option,
+	&filestat_option,
 	NULL,
 };
 
diff --git a/o2info/operations.c b/o2info/operations.c
index ee08667..abaca15 100644
--- a/o2info/operations.c
+++ b/o2info/operations.c
@@ -885,3 +885,139 @@ out:
 DEFINE_O2INFO_OP(space_usage,
 		 space_usage_run,
 		 NULL);
+
+static int o2info_report_filestat(struct o2info_method *om,
+				  struct stat *st,
+				  struct o2info_fiemap *ofp)
+{
+	int ret = 0;
+	uint16_t perm;
+
+	char *path = NULL;
+	char *filetype = NULL, *h_perm = NULL;
+	char *uname = NULL, *gname = NULL;
+	char *ah_time = NULL, *ch_time = NULL, *mh_time = NULL;
+
+	ret = o2info_get_human_path(st->st_mode, om->om_path, &path);
+	if (ret)
+		goto out;
+
+	ret = o2info_get_filetype(*st, &filetype);
+	if (ret)
+		goto out;
+
+	ret = o2info_uid2name(st->st_uid, &uname);
+	if (ret)
+		goto out;
+
+	ret = o2info_gid2name(st->st_gid, &gname);
+	if (ret)
+		goto out;
+
+	ret = o2info_get_human_permission(st->st_mode, &perm, &h_perm);
+	if (ret)
+		goto out;
+
+	if (!ofp->blocksize)
+		ofp->blocksize = st->st_blksize;
+
+	fprintf(stdout, "  File: %s\n", path);
+	fprintf(stdout, "  Size: %-10lu\tBlocks: %-10u IO Block: %-6u %s\n",
+		st->st_size, st->st_blocks, ofp->blocksize, filetype);
+	 if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode))
+		fprintf(stdout, "Device: %xh/%dd\tInode: %-10i  Links: %-5u"
+			" Device type: %u,%u\n", st->st_dev, st->st_dev,
+			st->st_ino, st->st_nlink,
+			st->st_dev >> 16UL, st->st_dev & 0x0000FFFF);
+	else
+		fprintf(stdout, "Device: %xh/%dd\tInode: %-10i  Links: %u\n",
+			st->st_dev, st->st_dev, st->st_ino, st->st_nlink);
+	fprintf(stdout, " Frag%: %-10.2f\tClusters: %-8u Extents: "
+		"%-6lu Score: %.0f\n", ofp->frag, ofp->clusters,
+		ofp->num_extents, ofp->score);
+	fprintf(stdout, "Shared: %-10u\tUnwritten: %-7u Holes: %-8u "
+		"Xattr: %u\n", ofp->shared, ofp->unwrittens,
+		ofp->holes, ofp->xattr);
+	fprintf(stdout, "Access: (%04o/%10s)  Uid: (%5u/%8s)   "
+		"Gid: (%5u/%8s)\n", perm, h_perm, st->st_uid,
+		uname, st->st_gid, gname);
+
+	ret = o2info_get_human_time(&ah_time, o2info_get_stat_atime(st));
+	if (ret)
+		goto out;
+	ret = o2info_get_human_time(&mh_time, o2info_get_stat_mtime(st));
+	if (ret)
+		goto out;
+	ret = o2info_get_human_time(&ch_time, o2info_get_stat_ctime(st));
+	if (ret)
+		goto out;
+
+	fprintf(stdout, "Access: %s\n", ah_time);
+	fprintf(stdout, "Modify: %s\n", mh_time);
+	fprintf(stdout, "Change: %s\n", ch_time);
+
+out:
+	if (path)
+		ocfs2_free(&path);
+	if (filetype)
+		ocfs2_free(&filetype);
+	if (uname)
+		ocfs2_free(&uname);
+	if (gname)
+		ocfs2_free(&gname);
+	if (h_perm)
+		ocfs2_free(&h_perm);
+	if (ah_time)
+		ocfs2_free(&ah_time);
+	if (mh_time)
+		ocfs2_free(&mh_time);
+	if (ch_time)
+		ocfs2_free(&ch_time);
+
+	return ret;
+}
+
+static int filestat_run(struct o2info_operation *op,
+			struct o2info_method *om,
+			void *arg)
+{
+	int ret = 0, flags = 0;
+	struct stat st;
+	struct o2info_volinfo ovf;
+	struct o2info_fiemap ofp;
+
+	if (om->om_method == O2INFO_USE_LIBOCFS2) {
+		o2i_error(op, "specify a none-device file to stat\n");
+		ret = -1;
+		goto out;
+	}
+
+	ret = lstat(om->om_path, &st);
+	if (ret < 0) {
+		ret = errno;
+		o2i_error(op, "lstat error: %s\n", strerror(ret));
+		ret = -1;
+		goto out;
+	}
+
+	memset(&ofp, 0, sizeof(ofp));
+
+	ret = get_volinfo_ioctl(op, om->om_fd, &ovf);
+	if (ret)
+		return -1;
+
+	ofp.blocksize = ovf.blocksize;
+	ofp.clustersize = ovf.clustersize;
+
+	ret = o2info_get_fiemap(om->om_fd, flags, &ofp);
+	if (ret)
+		goto out;
+
+	ret = o2info_report_filestat(om, &st, &ofp);
+out:
+	return ret;
+}
+
+DEFINE_O2INFO_OP(filestat,
+		 filestat_run,
+		 NULL);
diff --git a/o2info/utils.c b/o2info/utils.c
index ed980ed..90c7567 100644
--- a/o2info/utils.c
+++ b/o2info/utils.c
@@ -182,3 +182,305 @@ int o2info_method(const char *path)
 out:
 	return rc;
 }
+
+int o2info_get_filetype(struct stat st, char **filetype)
+{
+	int rc = 0;
+
+	if (S_ISREG(st.st_mode))
+		if (st.st_size != 0)
+			*filetype = strdup("regular file");
+		else
+			*filetype = strdup("regular empty file");
+	else if (S_ISDIR(st.st_mode))
+		*filetype = strdup("directory");
+	else if (S_ISCHR(st.st_mode))
+		*filetype = strdup("character special file");
+	else if (S_ISBLK(st.st_mode))
+		*filetype = strdup("block special file");
+	else if (S_ISFIFO(st.st_mode))
+		*filetype = strdup("FIFO");
+	else if (S_ISLNK(st.st_mode))
+		if (st.st_blocks == 0)
+			*filetype = strdup("fast symbolic link");
+		else
+			*filetype = strdup("symbolic link");
+	else if (S_ISSOCK(st.st_mode))
+		*filetype = strdup("socket");
+	else {
+		*filetype = strdup("unknown file type");
+		rc = -1;
+	}
+
+	if (!*filetype) {
+		errorf("No memory for allocation\n");
+		rc = -1;
+	}
+
+	return rc;
+}
+
+int o2info_get_human_permission(mode_t st_mode, uint16_t *perm, char **h_perm)
+{
+	int rc = 0;
+	char tmp[11] = "----------";
+
+	*perm = (uint16_t)(st_mode & 0x00000FFF);
+
+	tmp[10] = '\0';
+	tmp[9] = (*perm & 0x0001) ? 'x' : '-';
+	tmp[8] = (*perm & 0x0002) ? 'w' : '-';
+	tmp[7] = (*perm & 0x0004) ? 'r' : '-';
+	tmp[6] = (*perm & 0x0008) ? 'x' : '-';
+	tmp[5] = (*perm & 0x0010) ? 'w' : '-';
+	tmp[4] = (*perm & 0x0020) ? 'r' : '-';
+	tmp[3] = (*perm & 0x0040) ? 'x' : '-';
+	tmp[2] = (*perm & 0x0080) ? 'w' : '-';
+	tmp[1] = (*perm & 0x0100) ? 'r' : '-';
+
+	/*
+	 * Handling the setuid/setgid/sticky bits,
+	 * by following the convention stat obeys.
+	 */
+	if (*perm & 0x0200) {
+		if (*perm & 0x0001)
+			tmp[9] = 't';
+		else
+			tmp[9] = 'T';
+	}
+
+	if (*perm & 0x0400) {
+		if (*perm & 0x0008)
+			tmp[6] = 's';
+		else
+			tmp[6] = 'S';
+	}
+
+	if (*perm & 0x0800) {
+		if (*perm & 0x0040)
+			tmp[3] = 's';
+		else
+			tmp[3] = 'S';
+	}
+
+	if (S_ISCHR(st_mode))
+		tmp[0] = 'c';
+	else if (S_ISBLK(st_mode))
+		tmp[0] = 'b';
+	else if (S_ISFIFO(st_mode))
+		tmp[0] = 'p';
+	else if (S_ISLNK(st_mode))
+		tmp[0] = 'l';
+	else if (S_ISSOCK(st_mode))
+		tmp[0] = 's';
+	else if (S_ISDIR(st_mode))
+		tmp[0] = 'd';
+
+	*h_perm = strdup(tmp);
+	if (!*h_perm) {
+		errorf("No memory for allocation\n");
+		rc = -1;
+	}
+
+	return rc;
+}
+
+int o2info_uid2name(uid_t uid, char **uname)
+{
+	struct passwd *entry;
+	int ret = 0;
+
+	entry = getpwuid(uid);
+
+	if (!entry) {
+		errorf("user %d does not exist!\n", uid);
+		ret = -1;
+	} else {
+		*uname = strdup(entry->pw_name);
+		if (*uname == NULL) {
+			errorf("No memory for allocation\n");
+			ret = -1;
+		}
+	}
+
+	return ret;
+}
+
+int o2info_gid2name(gid_t gid, char **gname)
+{
+	struct group *group;
+	int ret = 0;
+
+	group = getgrgid(gid);
+
+	if (!group) {
+		errorf("group %d does not exist!\n", gid);
+		ret = -1;
+	} else {
+		*gname = strdup(group->gr_name);
+		if (*gname == NULL) {
+			errorf("No memory for allocation\n");
+			ret = -1;
+		}
+	}
+
+	return ret;
+}
+
+struct timespec o2info_get_stat_atime(struct stat *st)
+{
+#ifdef __USE_MISC
+	return st->st_atim;
+#else
+	struct timespec t;
+	t.tv_sec = st->st_atime;
+	t.tv_nsec = st->st_atimensec;
+	return t;
+#endif
+}
+
+struct timespec o2info_get_stat_mtime(struct stat *st)
+{
+#ifdef __USE_MISC
+	return st->st_mtim;
+#else
+	struct timespec t;
+	t.tv_sec = st->st_mtime;
+	t.tv_nsec = st->st_mtimensec;
+	return t;
+#endif
+}
+
+struct timespec o2info_get_stat_ctime(struct stat *st)
+{
+#ifdef __USE_MISC
+	return st->st_ctim;
+#else
+	struct timespec t;
+	t.tv_sec = st->st_ctime;
+	t.tv_nsec = st->st_ctimensec;
+	return t;
+#endif
+}
+
+static int *get_prefix(char *str_pattern, int pattern_len)
+{
+	int i = 1, j = 0;
+	int *prefix = (int *)malloc(pattern_len * sizeof(int));
+
+	prefix[0] = 0;
+
+	while (i < pattern_len) {
+
+		if (str_pattern[i] == str_pattern[j])
+			prefix[i] = ++j;
+		else {
+			j = 0;
+			prefix[i] = j;
+		}
+
+		i++;
+	}
+
+	return prefix;
+}
+
+static int kmp(const char *str_pattern, int pattern_len,
+	       const char *str_target, int target_len,
+	       int *prefix)
+{
+	int i = 0;
+	int j = 0;
+
+	if (!prefix)
+		return -1;
+
+	while ((i < pattern_len) && (j < target_len)) {
+
+		if ((j == 0) || (str_pattern[i] == str_target[j])) {
+			i++;
+			j++;
+		} else
+			j = prefix[j];
+	}
+
+	if (prefix) {
+		free(prefix);
+		prefix = NULL;
+	}
+
+	if (j == target_len)
+		return i - j + 1;
+	else
+		return -1;
+}
+
+static int print_nsec_to_htime(char *htime, unsigned long nsec,
+			       const char *stuff)
+{
+	char *s_nsec;
+	int index = 0, ret = 0;
+	int *prefix = get_prefix(htime, strlen(htime));
+
+	s_nsec = (char *)malloc(strlen(stuff) + 1);
+	snprintf(s_nsec, strlen(stuff) + 1, "%lu", nsec);
+
+	index = kmp(htime, strlen(htime), stuff, strlen(stuff), prefix);
+	if (index < 0) {
+		ret = -1;
+		goto bail;
+	}
+
+	strncpy(&htime[index], s_nsec, strlen(stuff));
+
+bail:
+	if (s_nsec)
+		free(s_nsec);
+
+	return 0;
+}
+
+int o2info_get_human_time(char **htime, struct timespec t)
+{
+	struct tm const *tm = localtime(&t.tv_sec);
+	int ret, size;
+
+	size = strlen("YYYY-MM-DD HH:MM:SS.NNNNNNNNN +ZZZZ") + 1;
+	*htime = (char *)malloc(size);
+
+	strftime(*htime, size, "%Y-%m-%d %H:%M:%S.NNNNNNNNN %z", tm);
+	ret = print_nsec_to_htime(*htime, t.tv_nsec, "NNNNNNNNN");
+	if (ret < 0) {
+		errorf("print n_seconds failed.");
+		return -1;
+	}
+
+	return 0;
+}
+
+int o2info_get_human_path(mode_t st_mode, const char *path, char **h_path)
+{
+	int rc;
+	char link[PATH_MAX];
+	char tmp_path[PATH_MAX * 2 + 4];
+
+	if (!S_ISLNK(st_mode))
+		*h_path = strdup(path);
+	else {
+		rc = readlink(path, link, PATH_MAX);
+		if (rc < 0) {
+			rc = errno;
+			tcom_err(rc, "while readlink %s", path);
+			return -1;
+		} else
+			link[rc] = '\0';
+
+		strncpy(tmp_path, path, PATH_MAX);
+		strcat(tmp_path, " -> ");
+		strcat(tmp_path, link);
+
+		*h_path = strdup(tmp_path);
+	}
+
+	return 0;
+}
diff --git a/o2info/utils.h b/o2info/utils.h
index 69446c2..fdb13ff 100644
--- a/o2info/utils.h
+++ b/o2info/utils.h
@@ -25,6 +25,15 @@
 int o2info_get_compat_flag(uint32_t flag, char **compat);
 int o2info_get_incompat_flag(uint32_t flag, char **incompat);
 int o2info_get_rocompat_flag(uint32_t flag, char **rocompat);
+int o2info_get_filetype(struct stat st, char **filetype);
+int o2info_get_human_permission(mode_t st_mode, uint16_t *perm, char **h_perm);
+int o2info_uid2name(uid_t uid, char **uname);
+int o2info_gid2name(gid_t gid, char **name);
+struct timespec o2info_get_stat_atime(struct stat *st);
+struct timespec o2info_get_stat_ctime(struct stat *st);
+struct timespec o2info_get_stat_mtime(struct stat *st);
+int o2info_get_human_time(char **htime, struct timespec t);
+int o2info_get_human_path(mode_t st_mode, const char *path, char **h_path);
 
 int o2info_method(const char *path);
 
-- 
1.5.5




More information about the Ocfs2-tools-devel mailing list