...
If the file referenced by new
exists prior to calling rename()
, the behavior is implementation-defined. On POSIX systems, the file is removed. On Windows systems, the rename()
fails, returns a nonzero value, and sets errno
to EACCES
.
This creates issues when trying to write portable code, or when trying to get the other behaviorto implement alternative behavior.
If the file named by new exists at the time of the call to rename(), the result is implementation-defined.
- What to do in an application targeted at only POSIX or only Windows if the "other" behavior is desired (i.e. non-clobbering on POSIX, clobbering on Windows). For non-clobbering on POSIX you use access() and only call rename() if the file does not exist. For clobbering on Windows you call remove() before rename(). In both cases there is a TOCTOU so the part about needing a secure directory applies.
- What to do in applications intended to be portable to any C99 implementation. This is a combination of the above two cases. I.e. if you want non-clobbering you do an existence check even though it wouldn't be needed on Windows and other non-clobbering implementations; if you want clobbering you do an explicit remove() even though it wouldn't be needed on POSIX and other clobbering implementations.
...
New File Removed
Non-Compliant Code Example (Windows)
If the intent of the programmer is to remove the file referenced by new
if it exists prior to calling rename()
, this code example is non-compliant on Windows platforms because it will fail, returns a nonzero value, and sets errno
to EACCES
.
Code Block | ||
---|---|---|
| ||
char const *old = /* ... */;
char const *new = /* ... */;
if (rename(old, new) != 0) {
/* Handle Error */
}
|
Compliant Solution (Windows)
On Windows systems, it is necessary to remove the file referenced by new
before calling rename()
, if you want the file to be overwritten and the rename()
operation to succeed.
Code Block | ||
---|---|---|
| ||
char const *old = /* ... */;
char const *new = /* ... */;
if (remove(new) != 0) {
/* Handle error condition */
}
if (rename(old, new) != 0) {
/* Handle error condition */
}
|
This code contains an unavoidable race condition between the call to remove()
and the call to rename()
and can consequently only be safely executed within a secure directory (see FIO17-A. Ensure that file operations are performed in a secure directory).
Compliant Solution (POSIX)
On POSIX systems, if the file referenced by new
exists prior to calling rename()
, the file is removed.
Code Block | ||
---|---|---|
| ||
char const *old = /* ... */;
char const *new = /* ... */;
if (rename(old, new) != 0) {
/* Handle error condition */
}
|
New File Not Removed
Non-Compliant Code Example (POSIX)
If the intent of the programmer is to not remove the file referenced by new
if it exists prior to calling rename()
, this code example is non-compliant because on POSIX systems the file is removed.
Code Block | ||
---|---|---|
| ||
char const *old = /* ... */;
char const *new = /* ... */;
if (rename(old, new) != 0) {
/* Handle Error */
}
|
Compliant Solution (POSIX)
Wiki Markup |
---|
TheIf the intent of the programmer is to not remove the file referenced by {{new}} if it exists prior to calling {{rename()}}, the POSIX {{access()}} function which can check for the existence of a file explicitly \[[Open Group 04|AA. C References#Open Group 04]\]. As with the {{fopen()}} solution, this code contains an unavoidable race condition and consequently can only be safely executed within a secure directory. In fact the existence check is not really needed on POSIX systems, because POSIX specifies the behavior of {{rename()}} whenThis compliant solution only renames the file referenced by {{old}} if the file referenced by {{new}} existsdoes not exist. |
Code Block | ||
---|---|---|
| ||
char const *old = /* ... */; char const *new = /* ... */; if (access(new, F_OK) != 0) { if (rename(old, new) != 0) { /* Handle error Errorcondition */ } } else { /* Handle error Errorcondition */ } |
This code contains an unavoidable race condition between the call to access()
and the call to rename()
and can consequently only be safely executed within a secure directory (see FIO17-A. Ensure that file operations are performed in a secure directory).
While the likelihood of access()
returning a false negative is lower than that of fopen()
, on file systems where the program does not have sufficient permissions in the directory to view the file, access()
may return -1
even when the file exists. In such cases, rename()
will would also fail since because the program does not have lacks adequate permissions inside to perform the directoryoperation.
Compliant Solution (Windows)
Wiki Markup |
---|
On Windows, the [{{rename()}}|http://msdn.microsoft.com/en-us/library/zw5t957f(VS.80).aspx] is already defined to return nonzero function fails if: \[[MSDN|AA. C References#MSDN]\] |
File or directory specified by
newname
already exists or could not be created (invalid path)
So Consequently, it is not necessary unnecessary to explicitly check for the existence of the file referenced by new
before calling rename()
.
Code Block | ||
---|---|---|
| ||
char const *old = /* ... */;
char const *new = /* ... */;
if (rename(old, new) != 0) {
/* Handle Error */
}
|
Portable Behavior
A programmer that wants to an application to behave the same on any C99 implementation must first determine what behavior to implement.
Compliant Solution (New File Removed)
This compliant solution ensures that any file referenced by new
is portably removed.
Code Block | ||
---|---|---|
| ||
char const *old = /* ... */;
char const *new = /* ... */;
if (remove(new) != 0) {
/* Handle error condition */
}
if (rename(old, new) != 0) {
/* Handle error condition */
}
|
This code contains an unavoidable race condition between the call to remove()
and the call to rename()
and can consequently only be safely executed within a secure directory (see FIO17-A. Ensure that file operations are performed in a secure directory).
Compliant Solution (New File Not Removed)
This compliant solution only renames the file referenced by old
if the file referenced by new
does not exist.
Code Block | ||
---|---|---|
| ||
char const *old = /* ... */;
char const *new = /* ... */;
if (access(new, F_OK) != 0) {
if (rename(old, new) != 0) {
/* Handle error condition */
}
}
else {
/* Handle error condition */
}
|
This code contains an unavoidable race condition between the call to remove()
and the call to rename()
and can consequently only be safely executed within a secure directory (see FIO17-A. Ensure that file operations are performed in a secure directory).
Risk Assessment
Calling rename()
has implementation-defined behavior when the new file name refers to an existing file. Incorrect use of rename()
can result in a file being unexpectedly overwritten or other unexpected behavior.
...