[Ocfs2-tools-devel] [PATCH v2 1/1] defragfs.ocfs2: Implement ocfs2 defragmentation tool

Larry Chen lchen at suse.com
Wed Oct 17 00:20:43 PDT 2018


This patch tries to build main framework with necessary *.h and *.c
files.

The defragmentation tool supports 3 types of target, single file,
directory and block device.

The simplest case is defragment a single file, that is actually call
ioctl with OCFS2_IOC_MOVE_EXT.

To defragment a dir means to defrag each file within this dir and its
subdirectory.

To defragment a block device means to defrag root directory only if
there is a ocfs2 file system running on top of it.

1. Implement the core function do_file_defrag in main.c

2. The function defrag_dir and defrag_block_dev in main.c are core
ones used for directory and block device as mentioned above.

3. Implement necessary utility func in libdefrag.c.

4. To support resuming defragmentation works from where it was stopped,
we use a temp file to record necessary info. Once uses run defrag tool
with -g option, the temp file will be loaded, and process resumes from
where it is stopped. This function is mainly implemented in record.c


Signed-off-by: Larry Chen <lchen at suse.com>
---
 Makefile                           |   2 +-
 configure.in                       |   1 +
 defragfs.ocfs2/Makefile            |  40 ++
 defragfs.ocfs2/commit.sh           |   4 +
 defragfs.ocfs2/defragfs.ocfs2.8.in |  76 ++++
 defragfs.ocfs2/include/libdefrag.h |  36 ++
 defragfs.ocfs2/include/o2defrag.h  |  40 ++
 defragfs.ocfs2/include/record.h    |  59 +++
 defragfs.ocfs2/libdefrag.c         | 126 +++++++
 defragfs.ocfs2/main.c              | 735 +++++++++++++++++++++++++++++++++++++
 defragfs.ocfs2/record.c            | 262 +++++++++++++
 11 files changed, 1380 insertions(+), 1 deletion(-)
 create mode 100644 defragfs.ocfs2/Makefile
 create mode 100755 defragfs.ocfs2/commit.sh
 create mode 100644 defragfs.ocfs2/defragfs.ocfs2.8.in
 create mode 100644 defragfs.ocfs2/include/libdefrag.h
 create mode 100644 defragfs.ocfs2/include/o2defrag.h
 create mode 100644 defragfs.ocfs2/include/record.h
 create mode 100644 defragfs.ocfs2/libdefrag.c
 create mode 100644 defragfs.ocfs2/main.c
 create mode 100644 defragfs.ocfs2/record.c

diff --git a/Makefile b/Makefile
index 955fab9c..5b0c258f 100644
--- a/Makefile
+++ b/Makefile
@@ -23,7 +23,7 @@ endif
 SUBDIRS1 = include
 SUBDIRS2 = libtools-internal libo2dlm libo2cb
 SUBDIRS3 = libocfs2
-SUBDIRS4 = fsck.ocfs2 mkfs.ocfs2 mounted.ocfs2 tunefs.ocfs2 debugfs.ocfs2 o2cb_ctl ocfs2_hb_ctl mount.ocfs2 ocfs2_controld o2image o2info o2monitor extras fswreck patches
+SUBDIRS4 = fsck.ocfs2 mkfs.ocfs2 mounted.ocfs2 tunefs.ocfs2 debugfs.ocfs2 o2cb_ctl ocfs2_hb_ctl mount.ocfs2 ocfs2_controld o2image o2info o2monitor extras fswreck patches defragfs.ocfs2
 
 ifdef BUILD_OCFS2CONSOLE
 SUBDIRS4 += ocfs2console
diff --git a/configure.in b/configure.in
index f89f4c21..59f95677 100644
--- a/configure.in
+++ b/configure.in
@@ -476,6 +476,7 @@ o2cb_ctl/ocfs2.cluster.conf.5
 vendor/common/o2cb.sysconfig.5
 libocfs2/ocfs2.7
 vendor/common/ocfs2-tools.spec-generic
+defragfs.ocfs2/defragfs.ocfs2.8
 ])
 
 AC_OUTPUT
diff --git a/defragfs.ocfs2/Makefile b/defragfs.ocfs2/Makefile
new file mode 100644
index 00000000..8740d33f
--- /dev/null
+++ b/defragfs.ocfs2/Makefile
@@ -0,0 +1,40 @@
+TOPDIR = ..
+
+include $(TOPDIR)/Preamble.make
+
+sbindir = $(root_sbindir)
+SBIN_PROGRAMS = defragfs.ocfs2
+
+DEFINES += -DVERSION=\"$(VERSION)\"
+
+
+INCLUDES = -I$(TOPDIR)/include -I./include
+DEFINES = -DVERSION=\"$(VERSION)\"
+
+CFILES = main.c record.c libdefrag.c
+HFILES =	\
+	include/libdefrag.h	\
+	include/o2defrag.h	\
+	include/record.h
+
+OBJS = $(subst .c,.o,$(CFILES))
+
+
+DIST_FILES = $(CFILES) $(HFILES) defragfs.ocfs2.8.in
+
+DIST_RULES = dist-subdircreate
+MANS = defragfs.ocfs2.8
+
+defragfs.ocfs2: $(OBJS)
+	$(LINK)
+
+dist-subdircreate:
+	$(TOPDIR)/mkinstalldirs $(DIST_DIR)/include
+
+CLEAN_RULES = defragfs-clean 
+
+defragfs-clean:
+	rm  defragfs.ocfs2.8
+
+.PHONY: defragfs-clean
+include $(TOPDIR)/Postamble.make
diff --git a/defragfs.ocfs2/commit.sh b/defragfs.ocfs2/commit.sh
new file mode 100755
index 00000000..bf1a3738
--- /dev/null
+++ b/defragfs.ocfs2/commit.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+git commit -am "test"
+git push
diff --git a/defragfs.ocfs2/defragfs.ocfs2.8.in b/defragfs.ocfs2/defragfs.ocfs2.8.in
new file mode 100644
index 00000000..be39639b
--- /dev/null
+++ b/defragfs.ocfs2/defragfs.ocfs2.8.in
@@ -0,0 +1,76 @@
+.TH "defragfs.ocfs2" "8" "January 2012" "Version @VERSION@" "OCFS2 Manual Pages"
+.SH NAME
+defragfs.ocfs2 \- online defragmenter for ocfs2 filesystem
+.SH SYNOPSIS
+defragfs.ocfs2 [\-c] [\-v] [\-l] [\-g] [\-h][target]...
+.SH DESCRIPTION
+.PP
+.B defragfs.ocfs2
+reduces fragmentation of ocfs2 based file. The file targeted by
+.B defragfs.ocfs2
+is created on ocfs2 filesystem The targeted file gets more contiguous blocks and improves the file access
+speed.
+.PP
+.I target
+is a regular file, a directory, or a device that is mounted as ocfs2 filesystem.
+If
+.I target
+is a directory,
+.B defragfs.ocfs2
+reduces fragmentation of all files in it. If
+.I target
+is a device,
+.B defragfs.ocfs2
+gets the mount point of it and reduces fragmentation of all files in this mount
+point.
+.SH OPTIONS
+.TP
+.B \-c
+Print the numbers of files that should be processed 
+.TP
+.B \-v
+verbose mode, the current fragmentation count and the ideal fragmentation count are
+printed for each file.
+.IP
+If this option is specified,
+.I target
+is never defragmented.
+.TP
+.B \-l
+.B defragfs.ocfs2 
+will run in low io mode, which means it will regularly yield cpu to allow other processes to run.
+.TP
+.B \-g
+.B defragfs.ocfs2
+regularly records the current progress. So if it is interrupted, the next time you run 
+.B defragfs.ocfs2
+with this option, it will resume from the recorded progress. 
+Note that if this option is specified, other options are ignored and replaced by those that were recorded.
+.TP
+.B \-h
+Print help info
+
+.SH NOTES
+.B defragfs.ocfs2
+does not support files in lost+found directory. When
+.I target
+is a device or a mount point,
+.B defragfs.ocfs2
+doesn't defragment files in mount point of other device.
+.PP
+It is completely
+.B not
+recommended to run against a file while it is actively in another application.
+Since the read/write of a file involves a dlm lock, this can result in a performance slowdown to both defragfs.ocfs2
+and the application due to contention.
+.PP
+If the file system's free space is fragmented, or if there is
+insufficient free space available, defragfs.ocfs2 may not be able
+to improve the file's fragmentation.
+.PP
+Non-privileged users can execute
+.B defragfs.ocfs2
+to their own file. Therefore, it is desirable to be executed by root user.
+.SH AUTHOR
+Written by Larry Chen <lchen at suse.com>
+
diff --git a/defragfs.ocfs2/include/libdefrag.h b/defragfs.ocfs2/include/libdefrag.h
new file mode 100644
index 00000000..337d1e02
--- /dev/null
+++ b/defragfs.ocfs2/include/libdefrag.h
@@ -0,0 +1,36 @@
+#ifndef __LIB_DEFERAG_H__
+#define __LIB_DEFERAG_H__
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <linux/limits.h>
+#include <linux/types.h>
+
+#define PRINT_ERR(msg)	\
+	fprintf(stderr, "[ERROR]\t%s\n", (msg))
+
+#define PRINT_FILE_MSG(file, msg) \
+	fprintf(stdout, "\"%s\":%s\n", (file), msg)
+
+#define PRINT_FILE_ERRNO(file)	\
+	fprintf(stderr, "[ERROR]\"%s\":%s\n", (file), strerror(errno))
+
+#define PRINT_FILE_MSG_ERRNO(file, msg) \
+	fprintf(stderr, "[ERROR]%s:\"%s\" - %s\n", msg, file, strerror(errno))
+
+#define PRINT_FILE_ERR(file, msg) \
+	fprintf(stderr, "[ERROR]\"%s\":%s\n", (file), msg)
+
+void *do_malloc(size_t size);
+
+int do_read(int fd, void *bytes, size_t count);
+
+int do_write(int fd, const void *bytes, size_t count);
+
+unsigned int do_csum(const unsigned char *buff, int len);
+
+
+#endif
diff --git a/defragfs.ocfs2/include/o2defrag.h b/defragfs.ocfs2/include/o2defrag.h
new file mode 100644
index 00000000..1ae7376d
--- /dev/null
+++ b/defragfs.ocfs2/include/o2defrag.h
@@ -0,0 +1,40 @@
+#ifndef __O2DEFRAG_H__
+#define __O2DEFRAG_H__
+
+#define FS_OCFS2 "ocfs2"
+
+#define DETAIL			0x01
+#define STATISTIC		0x02
+#define GO_ON			0x04
+#define LOW_IO			0x08
+
+#define DEVNAME			0
+#define DIRNAME			1
+#define FILENAME		2
+
+#define FTW_OPEN_FD		2000
+
+#define ROOT_UID		0
+
+#define SHOW_FRAG_FILES	5
+#define SCHEDULE_TIME_LIMIT	5
+
+#define RECORD_EVERY_N_FILES	50
+
+#ifndef OCFS2_IOC_MOVE_EXT
+#define OCFS2_IOC_MOVE_EXT	_IOW('o', 6, struct ocfs2_move_extents)
+#endif
+
+struct o2defrag_opt {
+	int o_num;
+	char *o_str;
+};
+
+#define declare_opt(n, s)  {\
+	.o_num = n,\
+	.o_str = s\
+}
+
+#define PROGRAME_NAME "defragfs.ocfs2"
+
+#endif
diff --git a/defragfs.ocfs2/include/record.h b/defragfs.ocfs2/include/record.h
new file mode 100644
index 00000000..25b8659f
--- /dev/null
+++ b/defragfs.ocfs2/include/record.h
@@ -0,0 +1,59 @@
+#ifndef __RECORD_H__
+#define __RECORD_H__
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <linux/limits.h>
+#include <linux/types.h>
+#include <stdint.h>
+
+#define RECORD_FILE_NAME ".ocfs2.defrag.record" //under lost_found dir
+
+//#define offset_of(type, member)  &(((type *)0)->(member))
+#define offset_of(type, member) (unsigned long)(&((type *)0)->member)
+
+
+#define calc_record_file_size(argc) (RECORD_HEADER_LEN + argc * PATH_MAX)
+
+/*
+ * record format****************
+ * mode_flag(binary)
+ * inode_no_to_resume
+ * how_many_left
+ * argv[0]'\0'argv[1]'\0'.......argv[n-1]'\0'
+ * check_sum
+ */
+
+struct argv_node {
+	char *a_path;
+	struct list_head a_list;
+};
+
+
+struct resume_record {
+	int r_mode_flag;
+	ino_t r_inode_no;
+	int r_argc;
+	struct list_head r_argvs;
+};
+
+#define RECORD_HEADER_LEN offset_of(struct resume_record, r_argvs)
+
+extern void dump_record(char *base_name,
+			struct resume_record *rr,
+			void (*dump_mode_flag)(int mode_flag));
+
+extern void set_record_file_path(char *path);
+extern void free_record(struct resume_record *rr);
+extern void fill_resume_record(struct resume_record *rr,
+		int mode_flag, char **argv, int argc, ino_t inode_no);
+extern void free_argv_node(struct argv_node *n);
+extern int store_record(struct resume_record *rr);
+extern int load_record(struct resume_record *rr);
+extern void mv_record(struct resume_record *dst, struct resume_record *src);
+extern int remove_record(void);
+#endif
diff --git a/defragfs.ocfs2/libdefrag.c b/defragfs.ocfs2/libdefrag.c
new file mode 100644
index 00000000..200370c2
--- /dev/null
+++ b/defragfs.ocfs2/libdefrag.c
@@ -0,0 +1,126 @@
+#include <libdefrag.h>
+#include <unistd.h>
+
+void *do_malloc(size_t size)
+{
+	void *buf;
+
+	buf = calloc(size, 1);
+
+	if (buf == NULL) {
+		fprintf(stderr, "No mem\n");
+		exit(-1);
+	}
+
+	return buf;
+}
+
+int do_read(int fd, void *bytes, size_t count)
+{
+	int total = 0;
+	int ret;
+
+
+	while (total < count) {
+		ret = read(fd, bytes + total, count - total);
+		if (ret < 0) {
+			ret = -errno;
+			if ((ret == -EAGAIN) || (ret == -EINTR))
+				continue;
+			total = ret;
+			break;
+		}
+		if (ret == 0)
+			break;
+		total += ret;
+	}
+
+	return total;
+}
+
+int do_write(int fd, const void *bytes, size_t count)
+{
+	int total = 0;
+	int ret;
+
+	while (total < count) {
+		ret = write(fd, bytes + total, count - total);
+		if (ret < 0) {
+			ret = -errno;
+			if ((ret == -EAGAIN) || (ret == -EINTR))
+				continue;
+			else
+				goto error;
+		}
+
+		total += ret;
+	}
+	return total;
+error:
+	return ret;
+}
+
+static inline unsigned short from32to16(unsigned int x)
+{
+	/* add up 16-bit and 16-bit for 16+c bit */
+	x = (x & 0xffff) + (x >> 16);
+	/* add up carry.. */
+	x = (x & 0xffff) + (x >> 16);
+	return x;
+}
+
+unsigned int do_csum(const unsigned char *buff, int len)
+{
+	int odd;
+	unsigned int result = 0;
+
+	if (len <= 0)
+		goto out;
+	odd = 1 & (unsigned long) buff;
+	if (odd) {
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+		result += (*buff << 8);
+#else
+		result = *buff;
+#endif
+		len--;
+		buff++;
+	}
+	if (len >= 2) {
+		if (2 & (unsigned long) buff) {
+			result += *(unsigned short *) buff;
+			len -= 2;
+			buff += 2;
+		}
+		if (len >= 4) {
+			const unsigned char *end = buff + ((unsigned)len & ~3);
+			unsigned int carry = 0;
+
+			do {
+				unsigned int w = *(unsigned int *) buff;
+
+				buff += 4;
+				result += carry;
+				result += w;
+				carry = (w > result);
+			} while (buff < end);
+			result += carry;
+			result = (result & 0xffff) + (result >> 16);
+		}
+		if (len & 2) {
+			result += *(unsigned short *) buff;
+			buff += 2;
+		}
+	}
+	if (len & 1)
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+		result += *buff;
+#else
+		result += (*buff << 8);
+#endif
+	result = from32to16(result);
+	if (odd)
+		result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
+out:
+	return result;
+}
diff --git a/defragfs.ocfs2/main.c b/defragfs.ocfs2/main.c
new file mode 100644
index 00000000..2a110b6e
--- /dev/null
+++ b/defragfs.ocfs2/main.c
@@ -0,0 +1,735 @@
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <errno.h>
+#include <malloc.h>
+#include <time.h>
+#include <libgen.h>
+#include <netinet/in.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <assert.h>
+#include <sys/ioctl.h>
+#include <mntent.h>
+#include <sys/vfs.h>
+#include <ftw.h>
+#include <sched.h>
+#include <signal.h>
+#include <uuid/uuid.h>
+
+#include "ocfs2/ocfs2.h"
+#include "ocfs2/bitops.h"
+
+#include <record.h>
+#include <libdefrag.h>
+#include <o2defrag.h>
+
+
+
+
+static char	lost_found_dir[PATH_MAX + 1];
+static int	mode_flag;
+static unsigned int	regular_count;
+static unsigned int	succeed_count;
+static unsigned int skipped_count;
+static unsigned int processed_count;
+static uid_t current_uid;
+static int should_stop;
+struct resume_record rr;
+
+
+
+static void usage(char *progname)
+{
+	printf("usage: %s [-c] [-v] [-l] [-g] [-h] [FILE | DIRECTORY | DEVICE]...\n",
+			progname);
+	printf("\t-c\t\tCalculate how many files will be processed\n");
+	printf("\t-v\t\tVerbose mode\n");
+	printf("\t-l\t\tLow io rate mode\n");
+	printf("\t-g\t\tResume last defrag progress\n");
+	printf("\t-h\t\tShow this help\n");
+	exit(0);
+}
+
+/*
+ * get_dev_mount_point() -	Get device's ocfs2 mount point.
+ * @devname:		the device's name.
+ * @mount_point:	the mount point.
+ *
+ * a block device may be mounted on multiple mount points,
+ * this function return on first match
+ */
+static int get_dev_mount_point(const char *devname, char *mount_point)
+{
+	/* Refer to /etc/mtab */
+	const char *mtab = MOUNTED;
+	FILE *fp = NULL;
+	struct mntent *mnt = NULL;
+	struct stat64 sb;
+
+	if (stat64(devname, &sb) < 0) {
+		PRINT_FILE_MSG_ERRNO(devname, "While getting mount point");
+		return -1;
+	}
+
+	fp = setmntent(mtab, "r");
+	if (fp == NULL) {
+		PRINT_ERR("Couldn't access /etc/mtab");
+		return -1;
+	}
+
+	while ((mnt = getmntent(fp)) != NULL) {
+		struct stat64 ms;
+
+		/*
+		 * To handle device symlinks, we see if the
+		 * device number matches, not the name
+		 */
+		if (stat64(mnt->mnt_fsname, &ms) < 0)
+			continue;
+		if (sb.st_rdev != ms.st_rdev)
+			continue;
+
+		endmntent(fp);
+		if (strcmp(mnt->mnt_type, FS_OCFS2) == 0) {
+			strncpy(mount_point, mnt->mnt_dir,
+				PATH_MAX);
+			return 0;
+		}
+		PRINT_FILE_MSG(devname, "Not ocfs2 format");
+		return -1;
+	}
+	endmntent(fp);
+	PRINT_FILE_MSG(devname, "Is not mounted");
+	return -1;
+}
+
+/*
+ * get_file_backend_info() - Get file's mount point and block dev that it's on.
+ * @file:	the device's name.
+ * @dev:	dev path
+ * @mount_point:	the mount point.
+ *
+ * The file mush be on ocfs2 partition
+ */
+
+static int get_file_backend_info(const char *file, char *dev, char *mount_point)
+{
+	int maxlen = 0;
+	int	len, ret;
+	FILE *fp = NULL;
+	/* Refer to /etc/mtab */
+	const char *mtab = MOUNTED;
+	char real_path[PATH_MAX + 1];
+	struct mntent *mnt = NULL;
+	struct statfs64	fsbuf;
+
+
+	/* Get full path */
+	if (realpath(file, real_path) == NULL) {
+		PRINT_FILE_MSG_ERRNO(file, "Getting realpath failed");
+		return -1;
+	}
+
+	if (statfs64(real_path, &fsbuf) < 0) {
+		PRINT_FILE_MSG_ERRNO(file, "Getting fs state failed");
+		return -1;
+	}
+
+	if (fsbuf.f_type != OCFS2_SUPER_MAGIC)
+		return -1;
+
+	fp = setmntent(mtab, "r");
+	if (fp == NULL) {
+		PRINT_ERR("Couldn't access /etc/mtab");
+		return -1;
+	}
+
+	while ((mnt = getmntent(fp)) != NULL) {
+		if (mnt->mnt_fsname[0] != '/')
+			continue;
+		len = strlen(mnt->mnt_dir);
+		ret = memcmp(real_path, mnt->mnt_dir, len);
+		if (ret != 0)
+			continue;
+
+		if (maxlen >= len)
+			continue;
+
+		maxlen = len;
+
+		strncpy(mount_point, mnt->mnt_dir, PATH_MAX);
+		strncpy(dev, mnt->mnt_fsname, strlen(mnt->mnt_fsname) + 1);
+	}
+
+	endmntent(fp);
+	return 0;
+}
+
+/*
+ * is_ocfs2() -	Test whether the file on an ocfs2 filesystem.
+ * @file:		the file's name.
+ */
+static int is_ocfs2(const char *file)
+{
+	struct statfs64	fsbuf;
+	/* Refer to /etc/mtab */
+	char	file_path[PATH_MAX + 1];
+
+	/* Get full path */
+	if (realpath(file, file_path) == NULL)
+		return 0;
+
+	if (statfs64(file_path, &fsbuf) < 0)
+		return 0;
+
+	if (fsbuf.f_type != OCFS2_SUPER_MAGIC)
+		return 0;
+
+	return 1;
+}
+
+/*
+ * calc_entry_counts() - Calculate file counts.
+ * @file:		file name.
+ * @buf:		file info.
+ * @flag:		file type.
+ * @ftwbuf:		the pointer of a struct FTW.
+ */
+static int calc_entry_counts(const char *file,
+		const struct stat64 *buf, int flag, struct FTW *ftwbuf)
+{
+	if (lost_found_dir[0] != '\0' &&
+	    !memcmp(file, lost_found_dir, strnlen(lost_found_dir, PATH_MAX))) {
+		return 0;
+	}
+
+	if (S_ISREG(buf->st_mode))
+		regular_count++;
+	return 0;
+}
+
+/*
+ * print_progress -	Print defrag progress
+ * @file:		file name.
+ * @start:		logical offset for defrag target file
+ * @file_size:		defrag target filesize
+ */
+static void print_progress(const char *file, int result)
+{
+	char *str_res = "Success";
+
+	if (result)
+		str_res = "Failed";
+	if (mode_flag & DETAIL)
+		printf("[%u/%u]%s:%s\n",
+			processed_count, regular_count, file, str_res);
+	else
+		printf("\033[79;0H\033[K[%u/%u]%s:%s\t",
+			processed_count, regular_count, file, str_res);
+	fflush(stdout);
+}
+
+static int do_file_defrag(const char *file, const struct stat64 *buf,
+			int flag, struct FTW *ftwbuf)
+{
+	/* Defrag the file */
+	int ret;
+	int fd;
+	struct ocfs2_move_extents me = {
+		.me_start = 0,
+		.me_len = buf->st_size,
+		.me_flags = OCFS2_MOVE_EXT_FL_AUTO_DEFRAG,
+	};
+
+	fd = open64(file, O_RDWR, 0400);
+	if (fd < 0) {
+		PRINT_FILE_MSG_ERRNO(file, "Open file failed");
+		return -1;
+	}
+
+	ret = ioctl(fd, OCFS2_IOC_MOVE_EXT, &me);
+	if (ret < 0)
+		PRINT_FILE_MSG_ERRNO(file,"Move extent failed");
+
+	close(fd);
+	return ret;
+}
+
+
+/*
+ * check_file() - Check file attributes.
+ * @file:		the file's name.
+ * @file_stat:		the pointer of the struct stat64.
+ */
+static int check_file(const char *file, const struct stat64 *file_stat)
+{
+	const struct stat64 *buf;
+	struct stat64 stat_tmp;
+	int ret;
+
+	buf = file_stat;
+
+	if (!buf) {
+		ret = stat64(file, &stat_tmp);
+		if (ret < 0) {
+			PRINT_FILE_MSG_ERRNO(file, "Get file stat failed");
+			return 0;
+		}
+		buf = &stat_tmp;
+	}
+
+	if (buf->st_size == 0) {
+		if (mode_flag & DETAIL)
+			PRINT_FILE_MSG(file, "File size is 0... skip");
+		return 0;
+	}
+
+	/* Has no blocks */
+	if (buf->st_blocks == 0) {
+		if (mode_flag & DETAIL)
+			PRINT_FILE_MSG(file, "File has no blocks");
+		return 0;
+	}
+
+	if (current_uid != ROOT_UID &&
+		buf->st_uid != current_uid) {
+		if (mode_flag & DETAIL) {
+			PRINT_FILE_MSG(file,
+				"File is not current user's file or current user is not root");
+		}
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * defrag_file_ftw() - Check file attributes and call ioctl to defrag.
+ * @file:		the file's name.
+ * @buf:		the pointer of the struct stat64.
+ * @flag:		file type.
+ * @ftwbuf:		the pointer of a struct FTW.
+ */
+static int defrag_file_ftw(const char *file, const struct stat64 *buf,
+			int flag, struct FTW *ftwbuf)
+{
+	int ret;
+	time_t run_limit = SCHEDULE_TIME_LIMIT;
+	static time_t sched_clock;
+	time_t now, diff;
+	static int count;
+
+
+	if (rr.r_inode_no) {
+		if (buf->st_ino != rr.r_inode_no)
+			goto skip;
+		else
+			rr.r_inode_no = 0;
+	}
+
+	if (should_stop || count >= RECORD_EVERY_N_FILES) {
+		rr.r_inode_no = buf->st_ino;
+
+		if (should_stop) {
+			PRINT_FILE_MSG(file, "Interrupted");
+			printf("%s ---- interrupted\n", file);
+		} else {
+			rr.r_inode_no = 0;
+			count = 0;
+		}
+
+		if (mode_flag & DETAIL)
+			printf("\nRecording...\n");
+		ret = store_record(&rr);
+
+		if (ret)
+			PRINT_ERR("Record failed");
+		else if (mode_flag & DETAIL)
+				printf("Record successfully\n"
+					"Use -g option to resume progress\n");
+
+		if (should_stop)
+			exit(0);
+	} else
+		count++;
+
+	if (mode_flag & LOW_IO) {
+		if(sched_clock == 0)
+			sched_clock = time(NULL);
+
+		now = time(NULL);
+		diff = now - sched_clock;
+		if (diff > run_limit) {
+			printf("===========\n");
+			sched_yield();
+			sched_clock = now;
+		}
+	}
+
+	if (lost_found_dir[0] != '\0' &&
+	    !memcmp(file, lost_found_dir, strnlen(lost_found_dir, PATH_MAX))) {
+		if (mode_flag & DETAIL)
+			PRINT_FILE_MSG(file,
+				"In lost+found dir... ignore");
+		return 0;
+	}
+
+	if (!S_ISREG(buf->st_mode)) {
+		if (mode_flag & DETAIL)
+			PRINT_FILE_MSG(file, "Not regular file... ignore");
+		return 0;
+	}
+
+	processed_count++;
+
+	ret = check_file(file, buf);
+	if (!ret) {
+		skipped_count++;
+		goto out;
+	}
+
+	ret = do_file_defrag(file, buf, flag, ftwbuf);
+	if (ret == 0)
+		succeed_count++;
+out:
+	print_progress(file, ret);
+	return 0;
+skip:
+	if (mode_flag & DETAIL)
+		PRINT_FILE_MSG(file, "already done... skip\n");
+	processed_count++;
+	skipped_count++;
+	return 0;
+}
+
+static int defrag_dir(const char *dir_path)
+{
+	//lost+found is skipped!
+	char *real_dir_path = do_malloc(PATH_MAX);
+	char *dev_path = do_malloc(PATH_MAX);
+	char *mount_point = do_malloc(PATH_MAX);
+	char *lost_found = lost_found_dir;
+	int mount_point_len;
+	int flags = FTW_PHYS | FTW_MOUNT;
+
+	int ret;
+
+	if (!is_ocfs2(dir_path)) {
+		PRINT_FILE_MSG(dir_path, "Not within ocfs2 fs");
+		return 1;
+	}
+
+	if (realpath(dir_path, real_dir_path) == NULL) {
+		PRINT_FILE_MSG(dir_path, "Couldn't get full path");
+		return 1;
+	}
+	ret = get_file_backend_info(dir_path, dev_path, mount_point);
+	if (ret < 0) {
+		PRINT_FILE_MSG(dir_path, "can not get file back info");
+		return ret;
+	}
+	if (access(dir_path, R_OK) < 0) {
+		perror(dir_path);
+		return 1;
+	}
+	mount_point_len = strnlen(mount_point, PATH_MAX);
+	snprintf(lost_found, PATH_MAX, "%s%s", mount_point, "/lost+found");
+
+	/* Not the case("defragfs.ocfs2  mount_point_dir") */
+	if (dir_path[mount_point_len] != '\0') {
+		/*
+		 * "defragfs.ocfs2 mount_point_dir/lost+found"
+		 * or "defragfs.ocfs2 mount_point_dir/lost+found/"
+		 */
+		int lf_len = strnlen(lost_found, PATH_MAX);
+
+		if (strncmp(lost_found, dir_path, lf_len) == 0
+			&& (dir_path[lf_len] == '\0'
+				|| dir_path[lf_len] == '/')) {
+			PRINT_FILE_MSG(dir_path, 
+					"defrag won't work within lost+found\n");
+		}
+	}
+
+	nftw64(real_dir_path, calc_entry_counts, FTW_OPEN_FD, flags);
+
+	if (mode_flag & STATISTIC) {
+		printf("%8d files should be defraged in [%s]\n",
+				regular_count, real_dir_path);
+		return 1;
+	}
+
+	nftw64(dir_path, defrag_file_ftw, FTW_OPEN_FD, flags);
+	return 0;
+}
+
+static int defrag_block_dev(char *dev_path)
+{
+	char *mnt_point;
+	int ret;
+
+	mnt_point = do_malloc(PATH_MAX);
+	ret = get_dev_mount_point(dev_path, mnt_point);
+	if (ret < 0) {
+		PRINT_FILE_MSG(dev_path, "Could not find mount point");
+		return ret;
+	}
+
+	if (mode_flag & DETAIL)
+		printf("ocfs2 defragmentation for device(%s)\n",
+			dev_path);
+
+	return defrag_dir(mnt_point);
+}
+
+static int defrag_file(char *file_path)
+{
+	struct stat64 file_stat;
+	int ret;
+
+	if (!is_ocfs2(file_path)) {
+		PRINT_FILE_MSG(file_path, "Not on ocfs2 fs\n");
+		return -1;
+	}
+
+	ret = stat64(file_path, &file_stat);
+	if (ret < 0) {
+		PRINT_FILE_MSG(file_path, "get file stat error");
+		return -1;
+	}
+
+	if (!S_ISREG(file_stat.st_mode)) {
+		if (mode_flag & DETAIL)
+			PRINT_FILE_MSG(file_path,
+				"Not regular file... ignore");
+		return -1;
+	}
+	regular_count++;
+
+	/* Empty file */
+	if (file_stat.st_size == 0) {
+		if (mode_flag & DETAIL)
+			PRINT_FILE_MSG(file_path,
+				"File size is 0... skip");
+		skipped_count++;
+		return -1;
+	}
+
+	/* Has no blocks */
+	if (file_stat.st_blocks == 0) {
+		if (mode_flag & DETAIL)
+			PRINT_FILE_MSG(file_path, "File has no blocks");
+		skipped_count++;
+		return -1;
+	}
+
+
+	if (current_uid != ROOT_UID &&
+		file_stat.st_uid != current_uid) {
+		if (mode_flag & DETAIL) {
+			PRINT_FILE_MSG(file_path,
+				"File is not current user's file or current user is not root");
+		}
+		return -1;
+	}
+
+	ret = do_file_defrag(file_path, &file_stat, 0, 0);
+	if (ret < 0) {
+		PRINT_FILE_ERRNO(file_path);
+		return -1;
+	}
+
+	printf("%s: Succeeded\n", file_path);
+	return 0;
+}
+
+static void handle_signal(int sig)
+{
+	switch (sig) {
+	case SIGTERM:
+	case SIGINT:
+		printf("\nProcess Interrupted. signale = %d\n", sig);
+		should_stop = 1;
+	}
+}
+
+static struct o2defrag_opt opt_table[] = {
+	declare_opt(DETAIL, "-v"),
+	declare_opt(STATISTIC, "-c"),
+	declare_opt(GO_ON, "-g"),
+	declare_opt(LOW_IO, "-o"),
+};
+
+static void dump_mode_flag(int mode_flag)
+{
+	int i = 0;
+
+	for (i = 0; i < sizeof(opt_table)/sizeof(opt_table[0]); i++) {
+		if (mode_flag & opt_table[i].o_num)
+			printf(" %s ", opt_table[i].o_str);
+	}
+}
+
+static int parse_opt(int argc, char **argv, int *_mode_flag)
+{
+	char opt;
+
+	if (argc == 1)
+		return 0;
+
+	while ((opt = getopt(argc, argv, "gvclh")) != EOF) {
+		switch (opt) {
+		case 'v':
+			*_mode_flag |= DETAIL;
+			break;
+		case 'c':
+			*_mode_flag |= STATISTIC;
+			break;
+		case 'g':
+			*_mode_flag |= GO_ON;
+			break;
+		case 'l':
+			*_mode_flag |= LOW_IO;
+			break;
+		case 'h':
+			usage(PROGRAME_NAME);
+			exit(0);
+
+		default:
+			break;
+		}
+	}
+	return optind;
+}
+
+
+static void init_signal_handler(void)
+{
+	if (signal(SIGTERM, handle_signal) == SIG_ERR) {
+		PRINT_ERR("Could not set SIGTERM");
+		exit(1);
+	}
+
+	if (signal(SIGINT, handle_signal) == SIG_ERR) {
+		PRINT_ERR("Could not set SIGINT");
+		exit(1);
+	}
+}
+
+
+static void print_version(char *progname)
+{
+	printf("%s %s\n", progname, VERSION);
+}
+
+
+
+/*
+ * main() -		ocfs2 online defrag.
+ *
+ * @argc:		the number of parameter.
+ * @argv[]:		the pointer array of parameter.
+ */
+int main(int argc, char *argv[])
+{
+	int ret;
+	char	dir_name[PATH_MAX + 1];
+	char	dev_name[PATH_MAX + 1];
+	struct stat64	buf;
+	int _mode_flag = 0;
+	int index;
+	struct resume_record rr_tmp;
+	struct list_head *pos;
+
+	init_signal_handler();
+
+	print_version(PROGRAME_NAME);
+
+	index = parse_opt(argc, argv, &_mode_flag);
+
+	if (_mode_flag & GO_ON) {
+		ret = load_record(&rr_tmp);
+		if (ret) {
+			PRINT_ERR("Load record failed...exit");
+			exit(0);
+		}
+		mv_record(&rr, &rr_tmp);
+		printf("Record detected...\n Start as:\n");
+		dump_record(PROGRAME_NAME, &rr, dump_mode_flag);
+	} else {
+		if (index == argc || index == 0) {
+			usage(PROGRAME_NAME);
+			exit(0);
+		}
+
+		fill_resume_record(&rr, _mode_flag, &argv[index], argc - index, 0);
+	}
+
+	mode_flag = rr.r_mode_flag & ~GO_ON;
+	current_uid = getuid();
+
+	/* Main process */
+	list_for_each(pos, &rr.r_argvs) {
+		struct argv_node *n = list_entry(pos, struct argv_node, a_list);
+		char *path = n->a_path;
+
+		succeed_count = 0;
+		regular_count = 0;
+		skipped_count = 0;
+
+		memset(dir_name, 0, PATH_MAX + 1);
+		memset(dev_name, 0, PATH_MAX + 1);
+		memset(lost_found_dir, 0, PATH_MAX + 1);
+
+		if (lstat64(path, &buf) < 0) {
+			perror("Failed to get file info:");
+			printf("%s\n", path);
+			continue;
+		}
+
+		/* Handle i.e. lvm device symlinks */
+		if (S_ISLNK(buf.st_mode)) {
+			struct stat64	buf2;
+
+			if (stat64(path, &buf2) == 0 &&
+			    S_ISBLK(buf2.st_mode))
+				buf = buf2;
+		}
+
+		if (S_ISBLK(buf.st_mode)) {
+			/* Block device */
+			ret = defrag_block_dev(path);
+		} else if (S_ISDIR(buf.st_mode)) {
+			/* Directory */
+			ret = defrag_dir(path);
+		} else if (S_ISREG(buf.st_mode)) {
+			/* Regular file */
+			ret = defrag_file(path);
+			continue;
+		} else {
+			/* Irregular file */
+			printf("irregular file\n");
+			continue;
+		}
+
+		if (!ret) {
+			printf("\n\tSuccess:\t\t\t[ %u/%u ]\n",
+					succeed_count, regular_count);
+			printf("\n\tSkipped:\t\t\t[ %u/%u ]\n",
+					skipped_count, regular_count);
+			printf("\n\tFailure:\t\t\t[ %u/%u ]\n",
+				regular_count - succeed_count - skipped_count,
+				regular_count);
+		}
+
+		free_argv_node(n);
+	}
+
+	remove_record();
+	return 0;
+}
diff --git a/defragfs.ocfs2/record.c b/defragfs.ocfs2/record.c
new file mode 100644
index 00000000..bcf1e395
--- /dev/null
+++ b/defragfs.ocfs2/record.c
@@ -0,0 +1,262 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <linux/limits.h>
+#include <linux/types.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <ocfs2-kernel/kernel-list.h>
+#include <record.h>
+#include <libdefrag.h>
+
+
+#define MAX_RECORD_FILE_SIZE (2<<20)
+
+static char record_path[PATH_MAX] = "/tmp/"RECORD_FILE_NAME;
+
+void mv_record(struct resume_record *dst, struct resume_record *src)
+{
+	dst->r_argc = src->r_argc;
+	dst->r_inode_no = src->r_inode_no;
+	dst->r_mode_flag = src->r_mode_flag;
+	INIT_LIST_HEAD(&dst->r_argvs);
+	list_splice(&src->r_argvs, &dst->r_argvs);
+}
+
+void dump_record(char *base_name, struct resume_record *rr,
+		void (*dump_mode_flag)(int mode_flag))
+{
+		struct list_head *pos;
+		struct argv_node *n;
+		int mode_flag = rr->r_mode_flag;
+
+
+		printf("%s", base_name);
+		dump_mode_flag(mode_flag);
+
+		list_for_each(pos, &rr->r_argvs) {
+			n = list_entry(pos, struct argv_node, a_list);
+			printf(" %s ", n->a_path);
+		}
+		puts("");
+}
+
+
+void free_argv_node(struct argv_node *n)
+{
+	list_del(&n->a_list);
+	if (n->a_path)
+		free(n->a_path);
+	free(n);
+}
+
+static inline
+struct argv_node *allocate_argv_node(int len)
+{
+	struct argv_node *n;
+
+	n = do_malloc(sizeof(struct argv_node));
+	n->a_path = do_malloc(len);
+	return n;
+}
+
+void free_record(struct resume_record *rr)
+{
+	struct list_head *pos;
+	struct argv_node *n;
+
+	list_for_each(pos, &rr->r_argvs) {
+		n = list_entry(pos, struct argv_node, a_list);
+		free_argv_node(n);
+	}
+}
+
+static int read_record(int fd, struct resume_record **p, int *len)
+{
+	struct stat s;
+	void *content_buf = NULL;
+
+	if (fstat(fd, &s))
+		goto error;
+
+	content_buf = do_malloc(s.st_size);
+
+	if (do_read(fd, content_buf, s.st_size) != s.st_size)
+		goto error;
+
+	if (p)
+		*p = content_buf;
+	if (len)
+		*len = s.st_size;
+	return 0;
+
+error:
+	if (content_buf)
+		free(content_buf);
+	if (p)
+		*p = NULL;
+	if (len)
+		*len = 0;
+	return -errno;
+
+}
+
+static int is_record_file_valid(void *buf, int len)
+{
+	int data_len = len - sizeof(unsigned int);
+	unsigned int check_sum = *(int *)(buf + data_len);
+
+	if (check_sum == do_csum(buf, data_len))
+		return 1;
+	return 0;
+}
+
+static int __store_record(int fd, struct resume_record *rr)
+{
+	struct list_head *pos;
+	struct argv_node *n;
+	int index = 0;
+	unsigned char *buf = do_malloc(MAX_RECORD_FILE_SIZE);
+	int len;
+	int ret;
+
+	memcpy(buf, rr, RECORD_HEADER_LEN);
+	index += RECORD_HEADER_LEN;
+
+	list_for_each(pos, &rr->r_argvs) {
+		n = list_entry(pos, struct argv_node, a_list);
+		len = strlen(n->a_path) + 1;
+		if (index + len > MAX_RECORD_FILE_SIZE) {
+			PRINT_ERR("Arg too long");
+			ret = -1;
+			goto out;
+		}
+		strcpy((char *)&buf[index], n->a_path);
+		index += len;
+	}
+
+	*(int *)&buf[index] = do_csum(buf, index);
+	index += sizeof(int);
+	ret = do_write(fd, buf, index);
+
+out:
+	free(buf);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+
+void fill_resume_record(struct resume_record *rr,
+		int mode_flag, char **argv, int argc, ino_t inode_no)
+{
+	int i;
+	int len;
+	struct argv_node *node;
+
+	rr->r_mode_flag = mode_flag;
+	rr->r_argc = argc;
+	rr->r_inode_no = inode_no;
+	INIT_LIST_HEAD(&rr->r_argvs);
+	for (i = 0; i < argc; i++) {
+		len = strlen(argv[i]) + 1;
+		node = do_malloc(sizeof(struct argv_node));
+		node->a_path = do_malloc(len);
+		strcpy(node->a_path, argv[i]);
+		list_add_tail(&node->a_list, &rr->r_argvs);
+	}
+}
+
+int remove_record(void)
+{
+	int ret;
+
+	ret = unlink(record_path);
+	if (ret && errno != ENOENT) {
+		perror("while deleting record file");
+		return -errno;
+	}
+	return 0;
+}
+
+int store_record(struct resume_record *rr)
+{
+	int fd;
+
+	fd = open(record_path,
+			O_TRUNC | O_CREAT | O_WRONLY, 0600);
+	if (fd < 0) {
+		perror("while opening record file");
+		return -errno;
+	}
+	if (__store_record(fd, rr))
+		goto out;
+
+	if (fsync(fd))
+		goto out;
+
+	close(fd);
+	return 0;
+out:
+	return -1;
+}
+
+static int __load_record(int fd, struct resume_record *rr)
+{
+	int ret = -1, i;
+	struct resume_record *rr_tmp = NULL;
+	char **argv = NULL;
+	int argc;
+	char *p;
+	int len = 0;
+
+	if (read_record(fd, &rr_tmp, &len))
+		goto out;
+
+	if (!is_record_file_valid(rr_tmp, len))
+		goto out;
+
+
+	argc = rr_tmp->r_argc;
+
+	argv = do_malloc(sizeof(char *) * argc);
+
+	p = (void *)rr_tmp + RECORD_HEADER_LEN;
+	for (i = 0; i < argc; i++, p++) {
+		argv[i] = (char *)p;
+		p += strlen(p) + 1;
+	}
+
+	fill_resume_record(rr, rr_tmp->r_mode_flag,
+			argv, argc, rr_tmp->r_inode_no);
+
+	ret = 0;
+out:
+	if (rr_tmp)
+		free(rr_tmp);
+	if (argv)
+		free(argv);
+	return ret;
+
+}
+
+int load_record(struct resume_record *rr)
+{
+	int ret;
+	int record_fd = open(record_path, O_RDONLY, 0700);
+
+	if (record_fd < 0) {
+		perror("while opening record file");
+		return -errno;
+	}
+	ret = __load_record(record_fd, rr);
+	close(record_fd);
+	return ret;
+}
+
-- 
2.13.7




More information about the Ocfs2-tools-devel mailing list