Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Backup files.

This thread is locked; no one can reply to it. rss feed Print
Backup files.
Andrei Ellman
Member #3,434
April 2003

I am currently implementing backup-file functionality in my level-editor, and am wondering on the best way to implment it.

The most obvious solution is to rename the file to the backup filename and then write out the saved level as normal, like so...

1#define CHIO_MAXPATHLENGTH 260
2#define CHIO_FILENAMEEXTENSION_BACKUP "bak"
3 
4char szBackupName[CHIO_MAXPATHLENGTH];
5replace_extension(szBackupName, szPathToUse, CHIO_FILENAMEEXTENSION_BACKUP, CHIO_MAXPATHLENGTH);
6 
7if(exists(szPathToUse))
8{
9 if(delete_file(szBackupName))
10 {
11 return AERVAL_IOFAL; // TODO: More accurate error-code
12 }
13}
14 
15if(rename(szPathToUse, szBackupName))
16{
17 return AERVAL_IOFAL; // TODO: More accurate error-code
18}

Unfortunately, this has the problem of losing any meta-data associated with the file (such as creation-date, file-owner, and I believe that on BeOS, meta-data can become quite involved).

The alternative solution I had would be to copy the file byte by byte. But this has the disadvantage of being slow and inefficient (like when saving on a floppy disk), as it involves one read and two writes instead of one write. Also, the backup-file will not contain the meta-data of the original file.

Are there any other recommended ways of creating backup files, or any libc functions I could use? Perhaps I could read in the meta-data of the backup file and apply it to the saved file (although this would probably vary from OS to OS, and I'm not sure if I can create a file belonging to a different owner).

AE.

--
Don't let the illegitimates turn you into carbon.

Myrdos
Member #1,772
December 2001

Quote:

The most obvious solution is to rename the file to the backup filename and then write out the saved level as normal

That's what I would do. The meta-data will take care of itself - how can your 'normal' saves have the correct meta-data, but your 'backup' saves don't?

The creation time will be whenever the backup occurred, which is what you'd want. The owner and everything else should be the same as a normal save.

__________________________________________________

spunit262
Member #5,545
February 2005
avatar

What you do is
1. Rename the original.
2. Wright the new file.
3. Change the Creation time of the new file that of the old.
I don't know how to do step 3, but I know it possible.
Edit:
I found one way
http://msdn2.microsoft.com/en-us/library/system.io.file.setcreationtime.aspx
Edit 2:
This is werid
http://www.devcity.net/Articles/133/1/article.aspx

Andrei Ellman
Member #3,434
April 2003

David McCallum said:

Quote:

The most obvious solution is to rename the file to the backup filename and then write out the saved level as normal

That's what I would do. The meta-data will take care of itself - how can your 'normal' saves have the correct meta-data, but your 'backup' saves don't?

Ummm... if the file is being renamed, then the renamed file will preserve the meta-data, but the newly saved file won't. The Backup file will have the creation date of the original file and the modification date of the original file's previous save, whereas the actual file being saved will have a creation and modification date equal to the current time.

spunit262 said:

What you do is
1. Rename the original.
2. Wright the new file.
3. Change the Creation time of the new file that of the old.

It's not just the creation date I want to change, it's the entire meta-data (including the file-owner) in an OS-independent way.

Both these methods rely on .NET . I'm writing my program in plain C - no C++ or .NET . Is there a method in libc or any publically available C library that can create backup files?

AE.

--
Don't let the illegitimates turn you into carbon.

spunit262
Member #5,545
February 2005
avatar

I figured that. :(
It was the only thing I could find.
I guess I could trudge through some source code. ::)

Andrei Ellman
Member #3,434
April 2003

I've now written my backup code so that instead of renaming my file to the backup name and writing a new file, it instead makes a copy of the original file and then overwrites the original file with the backup file. This has the advantage that the meta-data is preserved (but not in the backup file), but the main dis-advantage is that it performs an un-necessary read and write of the file, which can be slow if using a slow medium such as a microfloppy. Anyway, here is the code I'm using now...

1#define CHIO_MAXPATHLENGTH 260
2#define CHIO_FILENAMEEXTENSION_BACKUP "bak"
3 
4 
5char szBackupName[CHIO_MAXPATHLENGTH];
6 
7replace_extension(szBackupName, szPathToUse, CHIO_FILENAMEEXTENSION_BACKUP, CHIO_MAXPATHLENGTH);
8 
9 
10if(exists(szPathToUse))
11{
12 if(delete_file(szBackupName))
13 {
14 return AERVAL_IOFAL; // TODO: More accurate error-code
15 }
16}
17 
18 
19{
20 /* Make a copy of the file instead of renaming. This preserves meta-data, but is much slower than just renaming */
21 
22 FILE *fpInFile, *fpOutFile;
23 
24 
25 /* Open the source file */
26 if( !(fpInFile = fopen(szPathToUse,"rb")) )
27 {
28 return AERVAL_IOFAL; // TODO: More accurate error-code
29 }
30 
31 
32 /* Open the dest file */
33 if( !(fpOutFile = fopen(szBackupName,"wb")) )
34 {
35 if(fclose(fpInFile))
36 {
37 // TODO: Additional error
38 }
39 
40 return AERVAL_IOFAL; // TODO: More accurate error-code
41 }
42 
43 
44 /* Do the copy */
45 {
46 int nChar;
47 
48 while ( (nChar = getc(fpInFile)) != EOF)
49 {
50 if( (nChar = putc(nChar,fpOutFile)) == EOF)
51 {
52 // TODO: Close streams and handle write-error
53 }
54 }
55 
56 if(ferror(fpInFile))
57 {
58 // TODO: Close streams and handle read-error
59 return -1;
60 }
61 }
62 
63 
64 /* And close */
65 if(fclose(fpOutFile))
66 {
67 // TODO: Handle error
68 
69 if(fclose(fpInFile))
70 {
71 // TODO: Additional error
72 }
73 
74 }
75 
76 if(fclose(fpInFile))
77 {
78 // TODO: Handle error
79 }
80 
81 
82}
83 
84 
85/* Make sure the backup file is written onto the disk (ie. not stored in a buffer in RAM) before we start to write the saved level. */
86if(fflush(NULL))
87{
88 return AERVAL_IOFAL; // TODO: More accurate error-code
89}

What would be nice is a libc function that would make a clone of a file under a different name, or even better, to create a new file with all the meta-data of an existing file (especially the original creation date).

If anyone knows of a better method than either the one above or the one in my first post of implementing backup file functionality in C, or if there's a freely available platform-independent C library that can do this, then please let me know.

Also, I've heard that on windows, fflush writes to the operating system buffer rather than the disk buffer unless either the program is linked with COMMODE.OBJ or the file-open mode-string includes the 'c' flag. Both these methods are windows only, and am wondering of there's an OS independent way to make sure that the disk-cache is fully flushed to disk.

AE.

--
Don't let the illegitimates turn you into carbon.

Kitty Cat
Member #2,815
October 2002
avatar

fflush is fully standard. It writes to the OS buffer because the program interacts with the OS, then the OS writes to the disk's DMA/IO port which then gets written to the disk. Unless you have a critical need to ensure the data is physically put on the disk before you continue (you usually don't), that's Good Enough (tm). The OS won't do things out of order.

But anyway, here's my copy-file routine, which preserves as much as possible:

1static int copy_file(const char *sf, const char *df)
2{
3 struct stat statbuf;
4 FILE *src, *dst;
5 int ret, i;
6 int fd;
7 
8 if(stat(sf, &statbuf) != 0)
9 return 1;
10 
11#ifdef O_BINARY
12 fd = open(df, O_WRONLY|O_BINARY|O_TRUNC|O_CREAT, statbuf.st_mode);
13#else
14 fd = open(df, O_WRONLY|O_TRUNC|O_CREAT, statbuf.st_mode);
15#endif
16 if(fd < 0)
17 return 1;
18 dst = fdopen(fd, "wb");
19 if(!dst)
20 {
21 close(fd);
22 return 1;
23 }
24 
25 src = fopen(sf, "rb");
26 if(!src)
27 {
28 fclose(dst);
29 return 1;
30 }
31 
32 ret = 0;
33 do {
34 i = fread(buffer, 1, sizeof(buffer), src);
35 if(i > 0)
36 i = fwrite(buffer, 1, i, dst);
37 if(i < 0)
38 ret = 1;
39 } while(i > 0);
40 
41 fclose(src);
42 fclose(dst);
43 return ret;
44}

Could use a bit more error checking in the loop, but it's generally good enough unless you run out of disk-space. buffer is simply a static array.

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

Andrei Ellman
Member #3,434
April 2003

Kitty Cat said:

Unless you have a critical need to ensure the data is physically put on the disk before you continue (you usually don't), that's Good Enough (tm). The OS won't do things out of order.

Is that also true if I'm writing a file and a backup-file and the order of the file-clusteres of the two files is heavily fragmented on the disk (the order of the parts of the two files is mixed up), then both files will be written in order. Because otherwise, if there's a power-failiure when the hard-drive is busy writing the data, then there's the possibility that both files could become corrupted.

Also, in your copy-routine, I notice that you've used open() and fdopen() for the destination file. What advantage does this have over fopen()?

AE.

--
Don't let the illegitimates turn you into carbon.

Kitty Cat
Member #2,815
October 2002
avatar

Quote:

if there's a power-failiure when the hard-drive is busy writing the data, then there's the possibility that both files could become corrupted.

If there's a power failure while writing, you're pretty much fucked regardless. The most you can do is use a proper journaling file system (NTFS, ext3, reiserfs, etc) so that the system can potentially recover the corrupted files, though there's still no gaurantee. That's all something the OS should worry about, not you.

Quote:

Also, in your copy-routine, I notice that you've used open() and fdopen() for the destination file. What advantage does this have over fopen()?

Using open() I can specify the file mode, which I get from the source file (viastat()). That's so it preserves the proper permissions and such from the original file.

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

Andrei Ellman
Member #3,434
April 2003

Thanks for that. However, it still does not preserve the creation date in the destination file (only tried it with a FAT32 filesystem so did not test for the ownership). Also, I'm sure there's a solution out there that preserves meta-data in the original file that just involves a rename instead of a copy.

AE.

--
Don't let the illegitimates turn you into carbon.

Kitty Cat
Member #2,815
October 2002
avatar

Could always try rename, but I don't know if that preserves the original meta data.

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

Andrei Ellman
Member #3,434
April 2003

rename does save me from having to read in and write out the original file, and it preserves the original meta-data in the backup file. However, once the new file is written, it will no longer have an original file to overwrite, and then, the meta-data in the original file will no longer be preserved.

Surely there must be some form of metadata-preserving backup solution in C (using libc or some other freely available C library) that not only preserves the metadata in the original file, but also works without having to copy (read in and write out) the entire file.

AE.

--
Don't let the illegitimates turn you into carbon.

Thomas Fjellstrom
Member #476
June 2000
avatar

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Andrei Ellman
Member #3,434
April 2003

That looks like a UNIX-centric description of file-attributes. It appears that UNIX does not have the concept of a creation-time.

After a bit of googling, I discovered what I think is the solution I've been searching for, but it's Windows-only - ReplaceFile(). Other Windows-only functions that might be of use are SetFileTime and CreateFile.

Now if only there was a cross-platform way to do this.

AE.

--
Don't let the illegitimates turn you into carbon.

Go to: