How flags are used
All flags are passed to the specific file copy implementation of each operating system.
For example, in the case of UNIX-based systems, the UnixCopyFile
uses the options to determine whether to perform certain additional operations.
Relevant class excerpt UnixCopyFile
:
static Flags fromMoveOptions(CopyOption... options) {
Flags flags = new Flags();
for (CopyOption option: options) {
if (option == StandardCopyOption.ATOMIC_MOVE) {
flags.atomicMove = true;
continue;
}
if (option == StandardCopyOption.REPLACE_EXISTING) {
flags.replaceExisting = true;
continue;
}
if (option == LinkOption.NOFOLLOW_LINKS) {
// ignore
continue;
}
if (option == null)
throw new NullPointerException();
throw new UnsupportedOperationException("Unsupported copy option");
}
// a move requires that all attributes be copied but only fail if
// the basic attributes cannot be copied
flags.copyBasicAttributes = true;
flags.copyPosixAttributes = true;
flags.copyNonPosixAttributes = true;
flags.failIfUnableToCopyBasic = true;
return flags;
}
Copy attributes
Specifically about COPY_ATTRIBUTES
, usually copying a file means creating a new one at the destination location and copying the content.
File attributes such as last access date, modification date, and permissions generally receive the default values of a new file.
Therefore, such flag causes the new file to receive the same values in these attributes. Note that this is not always what we want. Copying rarely has the full sense of replicating in practice.
Relevant class excerpt UnixCopyFile
:
// copy owner/permissions
if (flags.copyPosixAttributes) {
try {
fchown(fo, attrs.uid(), attrs.gid());
fchmod(fo, attrs.mode());
} catch (UnixException x) {
if (flags.failIfUnableToCopyPosix)
x.rethrowAsIOException(target);
}
}
// copy non POSIX attributes (depends on file system)
if (flags.copyNonPosixAttributes) {
source.getFileSystem().copyNonPosixAttributes(fi, fo);
}
// copy time attributes
if (flags.copyBasicAttributes) {
try {
futimes(fo,
attrs.lastAccessTime().to(TimeUnit.MICROSECONDS),
attrs.lastModifiedTime().to(TimeUnit.MICROSECONDS));
} catch (UnixException x) {
if (flags.failIfUnableToCopyBasic)
x.rethrowAsIOException(target);
}
}
Atomic operations
The ATOMIC_MOVE
flag does not actually apply to copy operations, despite the name of enum
, but when moving a file.
For UNIX-based systems, Java uses the command rename
to change the file from the source path to the destination path because this command is atomic.
In addition, the Java code does not perform any additional operations, such as verifying that the destination file exists, as this would make the process subject to non-atomic running conditions.
Relevant class excerpt UnixCopyFile
:
// handle atomic rename case
if (flags.atomicMove) {
try {
rename(source, target);
} catch (UnixException x) {
if (x.errno() == EXDEV) {
throw new AtomicMoveNotSupportedException(
source.getPathForExecptionMessage(),
target.getPathForExecptionMessage(),
x.errorString());
}
x.rethrowAsIOException(source, target);
}
return;
}