views:

109

answers:

3

For a particular piece of homework, I'm implementing a basic data storage system using sequential files under standard C, which cannot load more than 1 record at a time. So, the basic part is creating a new file where the results of whatever we do with the original records are stored. The previous file's renamed, and a new one under the working name is created. The code's compiled with MinGW 5.1.6 on Windows 7.

Problem is, this particular version of the code (I've got nearly-identical versions of this floating around my functions) doesn't always remove the old file, so the rename fails and hence the stored data gets wiped by the fopen().

FILE *archivo, *antiguo;

remove("IndiceNecesidades.old");  // This randomly fails to work in time.
rename("IndiceNecesidades.dat", "IndiceNecesidades.old"); // So rename() fails.

antiguo = fopen("IndiceNecesidades.old", "rb");
// But apparently it still gets deleted, since this turns out null (and I never find the .old in my working folder after the program's done).
archivo = fopen("IndiceNecesidades.dat", "wb"); // And here the data gets wiped.

Basically, anytime the .old previously exists, there's a chance it's not removed in time for the rename() to take effect successfully. No possible name conflicts both internally and externally.

The weird thing's that it's only with this particular file. Identical snippets except with the name changed to Necesidades.dat (which happen in 3 different functions) work perfectly fine.

// I'm yet to see this snippet fail.
FILE *antiguo, *archivo;

remove("Necesidades.old");
rename("Necesidades.dat", "Necesidades.old");

antiguo = fopen("Necesidades.old", "rb");
archivo = fopen("Necesidades.dat", "wb");

Any ideas on why would this happen, and/or how can I ensure the remove() command has taken effect by the time rename() is executed? (I thought of just using a while loop to force call remove() again so long as fopen() returns a non-null pointer, but that sounds like begging for a crash due to overflowing the OS with delete requests or something.)

A: 

It would probably be a good idea to check the remove() function for errors. man remove says that the function returns 0 on success and -1 on failure, setting errno to record the error. Try replacing the call with

if (remove("IndiceNecesidades.old") != 0){
   perror("remove(\"IndiceNecesidades.old\") failed");
}

which should give an error message saying what failed.

Further, it doesn't appear that the remove is neccessary

man rename()

The rename() system call causes the link named old to be renamed as new. If new exists, it is first removed. Both old and new must be of the same type (that is, both must be either directories or non-directories) and must reside on the same file system.

The rename() system call guarantees that an instance of new will always exist, even if the system should crash in the middle of the operation.

If the final component of old is a symbolic link, the symbolic link is renamed, not the file or directory to which it points.

EPERM will be returned if:

[EPERM] The directory containing old is marked sticky, and neither the containing directory nor old are owned by the effective user ID.

[EPERM] The new file exists, the directory containing new is marked sticky, and neither the containing directory nor new are owned by the effec- tive user ID.

so the next step would be to check you have permissions on the containing directory

Scott Wales
I perror'd both remove and rename, only rename throws errors. (Plus, the deletion still goes through.)btw: You've got your condition backwards there. 0 is false. ;)
Kyte
@Kyte: No, he's got the condition correct. ISO C specifies that remove() returns 0 on success and -1 on failure, just like he says.
janneb
@janneb, There was a typo but I caught it just after I submitted the answer, Kyte must have seen it in the gap.
Scott Wales
A: 

That sounds quite strange, and even more so when you say that the same code works OK with a different filename - I would strongly suspect a bug elsewhere in your code. However, you should be able to work around it by renaming the file you want to remove:

rename("IndiceNecesidades.old", "IndiceNecesidades.older");
remove("IndiceNecesidades.older");
rename("IndiceNecesidades.dat", "IndiceNecesidades.old");
caf
+2  A: 

So suddenly, after reading Scott's mention of permissions, I thought about "Permission Denied" and applied some Google. Turned out it's a pretty common, if obscure, error. caf was right, it was in another piece of code. Namely, I had forgotten to fclose that same file in the function meant to show the contents. Since I wasn't tracking that particular detail, it appeared to be random.

Disclaimer: Weekly math assigments make for very little sleeptime. ¬¬

Kyte
Just an interesting fact: if you were using POSIX system and not Windows that problem would not happen. In POSIX system you can always remove an open file and it will be removed from the directory, although, a the same time, it will exist, without a name, until the last process closes it. Just one of the differences between Windows family and POSIX.
Jacek Konieczny