How to mv a folder in Linux retaining its mtime?

12

4

I am using CentOS 5.5 and would like to move a large amount of folders within one volume, retaining their mtime.

The best solution I could find is like this:

cp -p -r source/data target/
rm -rf source/data

With over 1TB of data on a NFS share, the copying takes forever. I do not want to copy. I want instantaneous move.

When I move a folder using mv source/data target/, the mtime of the folder (not the files) gets set to current time. This is because the contents of folder I am moving get modified by this operation (the .. entry is pointing to a different inode).

I came up with a following shell script I called mv_preserve_mtime.sh:

#!/bin/bash
# Moves source folder to target folder. 
# You are responsible for making sure the target does not exist, otherwise this blows up
export timestamp=`stat -c %y $1`
mv "$1" "$2"
touch --date="${timestamp}" $2

Well, that did not work either. The folder's mtime is restored, but all folders within the folder I move (only the ones 1 level deep) get their mtime reset for reasons I do not understand.

Does anyone have a proper, efficient and correct solution?

Roman Zenka

Posted 2010-12-09T16:19:23.417

Reputation: 305

I wonder why your attempt with touch didn't work. Is it the mv step or the touch step that changes the mtime of the subdirectories? What OS is on the NFS server, and (if you know) what filesystem type? – Gilles 'SO- stop being evil' – 2010-12-10T20:22:33.587

@Gilles: I do not know why is it happening. It is the mv step that causes trouble. The NFS server is actually a NetApp storage, I know virtually nothing about its internals. – Roman Zenka – 2010-12-10T22:15:04.507

1Thanks. I suspect it's a NetApp oddity. Otherwise touch should have worked. By the way a more portable way would be touch -r "$1" reference.tmp; mv -- "$1" "$2"; touch -r reference.tmp -- "$2"; rm reference.tmp. – Gilles 'SO- stop being evil' – 2010-12-10T22:29:29.450

@Gilles: Very interesting, did not realize stat was not portable. – Roman Zenka – 2010-12-11T17:09:36.530

Answers

15

POSIX mv doesn't provide any option to ask for atime/mtime preservation, but as the operation is local to a same volume, you can ask cp to use hard-links instead of copying data of the regular files using the -l option:

cp -p -r -l source/date target/
rm -rf source/data

Since only directories and file references will be actually copied, it should go much faster:

For more informations on hard-links, you can consult the corresponding Wikipedia page

As for why subdirectories mtime is being reset with your current solution, it's because you only get and restore the parent directory mtime : touch is not a recursive command.

Eureka

Posted 2010-12-09T16:19:23.417

Reputation: 511

@Eureka Trying to do it on Android (because of this: https://stackoverflow.com/q/50540334/878126 ) I get this error : cp: /data/local/tmp/test.apk: Cross-device link . Any idea why? Is there perhaps a way to store and later restore the timestamp of the file?

– android developer – 2018-05-26T07:51:48.087

The mtime is more complicated than that. Only the parent directory and the directories directly under it have mtime changed. All other directories remain the same. One would expect either every single directory to be changed, or only the parent. – Roman Zenka – 2010-12-09T21:26:14.867

1Actually, it makes sense: 1) The parent directory has the good mtime because it was explicitly set by touch, 2) The directory entries where recreated with the parent directory, but their mtime was not manually restored (Unix directory structure and inode format) 3) The rest of the tree structure wasn't actually changed, as we remained on the same volume : That's why mv has no "recursive" option, descending into subdirectories is only done if actual copy (different volumes, for example) is needed. – Eureka – 2010-12-09T21:51:18.580

@Eureka: Good explanation, but why is it done this way? If I was to implement mv on a directory data, I would simply change the .. in the data's contents and modify the source and target directories to list the moved item properly. No other directories would need to be touched. – Roman Zenka – 2010-12-10T05:51:23.973

1

@Roman Zenka After some search, this behaviour seems to be rather loosely specified between Unices and filesystems and to depend a lot of the underlying rename syscall implementation by the kernel and the used filesystem(s), NFS adding its share to the problem. There are some pointer referencing this kind of inconsistencies: http://patchwork.ozlabs.org/patch/25833/ http://bugs.opensolaris.org/bugdatabase/view_bug.do%3Bjsessionid=cb4ea429e94190f02710a8255560?bug_id=6877802

– Eureka – 2010-12-10T09:56:26.463

@Eureka: I find it extremely hard to believe that something I would consider so basic can be such a mess. It is almost 2011. Thanks for those resources! – Roman Zenka – 2010-12-10T16:35:48.817

@Roman Zenka So do I... If you ever find a definitive explanation, I'll be interested: I couldn't find it yet. – Eureka – 2010-12-10T17:21:24.073

4

Another solution may be:

rsync -a --remove-source-files source/data target/

Genjo

Posted 2010-12-09T16:19:23.417

Reputation: 41

This does not seem to work on macOS. – Lenar Hoyt – 2017-09-04T22:13:36.600