We want to write to "foo.txt" in a given directory. If "foo.txt" already exists, we want to write to "foo-1.txt", and so on.
There are a few code snippets around that try and answer this question, but none are quite satisfactory. E.g. this solution at CocoaDev uses NSFileManager to test if a path exists to create a safe path. However, this leads to obvious race conditions between obtaining a path and writing to it. It would be safer to attempt atomic writes, and loop the numeric suffix on failure.
Go at it!
-
Use the
open
system call with theO_EXCL
andO_CREAT
options. If the file doesn't already exist,open
will create it, open it, and return the file descriptor to you; if it does exist,open
will fail and seterrno
toEEXIST
.From there, it should be obvious how to construct the loop that tries incrementing filenames until it returns a file descriptor or constructs a filename too long. On the latter point, make sure you check
errno
whenopen
fails—EEXIST
andENAMETOOLONG
are just two of the errors you could encounter. -
int fd; uint32_t counter; char filename[1024]; // obviously unsafe sprintf(filename, "foo.txt"); if( (fd = open(filename, O_CREAT | O_EXCL | O_EXLOCK, 0644)) == -1 && errno == EEXIST ) { for( counter = 1; counter < UINT32_MAX; counter++ ) { sprintf(filename, "foo-%u.txt", counter); if( (fd = open(filename, O_CREAT | O_EXCL | O_EXLOCK, 0644)) == -1 && errno == EEXIST ) continue; else break; } } if( fd == -1 && counter == UINT32_MAX ) { fprintf(stderr, "too many foo-files\n"); } else if( fd == -1 ) { fprintf(stderr, "could not open file: %s\n", strerror(errno)); } // otherwise fd is an open file with an atomically unique name and an // exclusive lock.
-
How about:
- Write the file to a temporary directory where you know there's no risk of collision
- Use NSFileManager to move the file to the preferred destination
- If step 3 fails due to a file already existing, add/increment a numeric suffix and repeat step 2
You'd be basically re-creating Cocoa's atomic file write handling, but adding in the feature of ensuring a unique filename. A big advantage of this approach is that if the power goes out or your app crashes mid-write, the half-finished file will be tucked away in a tmp folder and deleted by the system; not left for the user to try and work with.
0 comments:
Post a Comment