@@ -40,6 +40,7 @@
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/vfs.h>
+#include <libgen.h>
/* A relatively new ioctl interface ... */
#ifndef EXT4_IOC_MOVE_EXT
@@ -1408,6 +1409,8 @@ static int file_defrag(const char *file, const struct stat64 *buf,
int file_frags_start, file_frags_end;
int orig_physical_cnt, donor_physical_cnt = 0;
char tmp_inode_name[PATH_MAX + 8];
+ char *parent_name = NULL;
+ struct stat parent_stat;
ext4_fsblk_t blk_count = 0;
struct fiemap_extent_list *orig_list_physical = NULL;
struct fiemap_extent_list *orig_list_logical = NULL;
@@ -1526,29 +1529,51 @@ static int file_defrag(const char *file, const struct stat64 *buf,
/* Create donor inode */
memset(tmp_inode_name, 0, PATH_MAX + 8);
- sprintf(tmp_inode_name, "%.*s.defrag",
- (int)strnlen(file, PATH_MAX), file);
- donor_fd = open64(tmp_inode_name, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR);
+ /* Try O_TMPFILE first, to avoid changing directory mtime */
+ sprintf(tmp_inode_name, "%.*s", (int)strnlen(file, PATH_MAX), file);
+ parent_name = dirname(tmp_inode_name);
+ donor_fd = open64(parent_name, O_WRONLY | O_TMPFILE | O_EXCL, S_IWUSR);
if (donor_fd < 0) {
- if (mode_flag & DETAIL) {
- PRINT_FILE_NAME(file);
- if (errno == EEXIST)
+ /* Save parent timestamps for reset */
+ ret = stat(parent_name, &parent_stat);
+ if (ret < 0) {
+ if (mode_flag & DETAIL) {
+ PRINT_FILE_NAME(file);
PRINT_ERR_MSG_WITH_ERRNO(
- "File is being defraged by other program");
- else
- PRINT_ERR_MSG_WITH_ERRNO(NGMSG_FILE_OPEN);
+ "Failed to stat() parent directory");
+ }
+ goto out;
}
- goto out;
- }
- /* Unlink donor inode */
- ret = unlink(tmp_inode_name);
- if (ret < 0) {
- if (mode_flag & DETAIL) {
- PRINT_FILE_NAME(file);
- PRINT_ERR_MSG_WITH_ERRNO("Failed to unlink");
+ sprintf(tmp_inode_name, "%.*s.defrag",
+ (int)strnlen(file, PATH_MAX), file);
+ donor_fd = open64(tmp_inode_name, O_WRONLY | O_CREAT | O_EXCL,
+ S_IWUSR);
+ if (donor_fd < 0) {
+ if (mode_flag & DETAIL) {
+ PRINT_FILE_NAME(file);
+ if (errno == EEXIST)
+ PRINT_ERR_MSG_WITH_ERRNO(
+ "File is being defraged by other program");
+ else
+ PRINT_ERR_MSG_WITH_ERRNO(
+ NGMSG_FILE_OPEN);
+ }
+ goto out;
}
- goto out;
+
+ /* Unlink donor inode */
+ ret = unlink(tmp_inode_name);
+ if (ret < 0) {
+ if (mode_flag & DETAIL) {
+ PRINT_FILE_NAME(file);
+ PRINT_ERR_MSG_WITH_ERRNO("Failed to unlink");
+ }
+ goto out;
+ }
+
+ /* Reset parent times */
+ utimensat(0, parent_name, &parent_stat.st_atim, 0);
}
/* Allocate space for donor inode */