[Ocfs2-tools-devel] [PATCH v3 1/1] defragfs.ocfs2 Build main frame work for defrag.ocfs2
Larry Chen
lchen at suse.com
Mon Oct 22 01:25:47 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, of
course, recursively 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. The main frame works has been implemented in main.c, like following,
for_each_target(t) {
if (S_ISBLK(t))
defrag_block_dev(t);
else if (S_ISDIR(t))
defrag_dir(t);
else if (S_ISREG(t))
defrag_file(t);
}
The targets are paths which are formated as a list.
2. The functions defrag_block_dev, defrag_dir and defrag_file in main.c
are core ones respectfully used for block device, directory and single
regular file as mentioned above.
Actually defrag_block_dev calls defrag_dir if there is an ocfs2 on the
bllock device. And defrag_dir recursively defrag against each file
within the dir.
3. Implement necessary utility functions in libdefrag.c.
4. To support resuming defragmentation works from where it was stopped,
we use a temp file to record necessary info. Once users run defrag tool
with -g option, the temp file will be loaded, and process resumes from
where it is stopped.
This function has been implemented in record.c
Signed-off-by: Larry Chen <lchen at suse.com>
---
CREDITS | 3 +
Makefile | 2 +-
configure.in | 1 +
defragfs.ocfs2/Makefile | 40 ++
defragfs.ocfs2/defragfs.ocfs2.8.in | 76 ++++
defragfs.ocfs2/include/libdefrag.h | 36 ++
defragfs.ocfs2/include/o2defrag.h | 40 ++
defragfs.ocfs2/include/record.h | 53 +++
defragfs.ocfs2/libdefrag.c | 126 +++++++
defragfs.ocfs2/main.c | 735 +++++++++++++++++++++++++++++++++++++
defragfs.ocfs2/record.c | 262 +++++++++++++
11 files changed, 1373 insertions(+), 1 deletion(-)
create mode 100644 defragfs.ocfs2/Makefile
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/CREDITS b/CREDITS
index 042b8e12..c7703433 100644
--- a/CREDITS
+++ b/CREDITS
@@ -32,6 +32,9 @@ ocfs2console:
o2info:
Written by Tristan Ye.
+defrag.ocfs2:
+ Written by Larry Chen.
+
ocfs2console/blkid:
From e2fsprogs 1.37, by Theodore Ts'o and Andreas Dilger.
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/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..97bcd9ed
--- /dev/null
+++ b/defragfs.ocfs2/include/record.h
@@ -0,0 +1,53 @@
+#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 /tmp dir
+
+#define offset_of(type, member) (unsigned long)(&((type *)0)->member)
+
+#define calc_record_file_size(argc) (RECORD_HEADER_LEN + argc * PATH_MAX)
+
+
+/*
+ * Because the main way to use this tool is like
+ * defrag -a -b -c path1 path2 path3
+ * This struct is used to record every path
+ */
+struct argv_node {
+ char *a_path;
+ struct list_head a_list;
+};
+
+struct resume_record {
+ int r_mode_flag; /* mode flag, the binary of the combination of options */
+ ino_t r_inode_no; /* start from the file as a inode number */
+ int r_argc; /* how many argv_node in the r_argvs list */
+ struct list_head r_argvs; /* the list of argv_node */
+};
+
+#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