UFS Recover file after power loss

My system lost power and now one important file has a size of zero. fsck run during startup and produced the following output:

Code:
Oct 12 11:17:13 s05 fsck[1117]: /dev/gpt/ssd960encdata: UNREF FILE I=80095655  OWNER=owner MODE=100644
Oct 12 11:17:13 s05 fsck[1117]: /dev/gpt/ssd960encdata: SIZE=16123 MTIME=Oct 11 13:25 2024  (CLEARED)
Oct 12 11:17:13 s05 fsck[1117]: /dev/gpt/ssd960encdata: Reclaimed: 0 directories, 1 files, 0 fragments
Oct 12 11:17:13 s05 fsck[1117]: /dev/gpt/ssd960encdata: 587703 files, 160760410 used, 55890098 free (17482 frags, 6984077 blocks, 0.0% fragmentation)

Code:
# tunefs -p /data
tunefs: POSIX.1e ACLs: (-a)                                disabled
tunefs: NFSv4 ACLs: (-N)                                   disabled
tunefs: MAC multilabel: (-l)                               disabled
tunefs: soft updates: (-n)                                 enabled
tunefs: soft update journaling: (-j)                       disabled
tunefs: gjournal: (-J)                                     disabled
tunefs: trim: (-t)                                         enabled
tunefs: maximum blocks per file in a cylinder group: (-e)  4096
tunefs: average file size: (-f)                            16384
tunefs: average number of files in a directory: (-s)       64
tunefs: minimum percentage of free space: (-m)             8%
tunefs: space to hold for metadata blocks: (-k)            6408
tunefs: optimization preference: (-o)                      time
tunefs: volume label: (-L)                                 ssd960encdata


lost+found directory is empty. Is there a way to recover this file? Or what is the meaning of the "CLEARED" operation?

Thanks a lot in advance!
 
Look at the current version of the file: Does it have inode number 80095655? If yes, fsck has done the best job it can do, and the zero byte file is what you'll get.

If the current version of the file is not that inode number, then try to find the file with that inode number. The easiest way to do that is with find /...mountpoint -inum 80095655, that will give you the file path, if there is one. That might be your file, or it might be something completely unrelated.

Was the file open (meaning the application that was supposed to write the file was still running) at the time of the power outage? If yes, the likely answer is that the application (you said it was gnumeric) had either not yet written the file, or had written and but not closed and/or not fsync'ed. In that case, it is quite plausible that a zero file is the logical result. I don't know how gnumeric performs writing of the file it is changing. The standard technique for writing output files is used by most editors: they read the input file (let's call it foo), then they write a temporary file call foo.temp or something like that, and then they rename the original (unmodified) input file foo to foo.bak or foo~, and rename foo.temp to foo (in a nutshell, but adding and removing links you can do a more elaborate dance out of that). Like that, at any given moment either the old unmodified file is still present, or the new file is present, and the content can never be lost.

If the application had already finished, it is still possible to lose the file content due to a crash, if the application wrote it and closed the file, but didn't call any form of sync, or didn't use the sync option.
 
It's a spreadsheet created with gnumeric.

Seek a (hidden) .gsf-save-* file at the same path.

From a run of truss gnumeric, for a file that I chose to save at /tmp:

Code:
…
readlink("/tmp/Book1.gnumeric",0xdf13001d200,256) ERR#2 'No such file or directory'
fstatat(AT_FDCWD,"/tmp/Book1.gnumeric",0x820fe4420,0x0) ERR#2 'No such file or directory'
getuid()                                         = 1002 (0x3ea)
fstatat(AT_FDCWD,"/tmp",{ mode=drwxrwxrwt ,inode=2,size=896,blksize=4096 },0x0) = 0 (0x0)
getgid()                                         = 1002 (0x3ea)
umask(0x3f)                                      = 18 (0x12)
openat(AT_FDCWD,"/tmp/.gsf-save-RACEV2",O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC,0600) = 20 (0x14)
umask(0x12)                                      = 63 (0x3f)
fcntl(20,F_GETFL,)                               = 2 (0x2)
write(16,"\^A\0\0\0\0\0\0\0",8)                  = 8 (0x8)
poll({ 16/POLLIN },1,-1)                         = 1 (0x1)
read(16,"\^A\0\0\0\0\0\0\0",8)                   = 8 (0x8)
openat(AT_FDCWD,"/usr/local/share/locale/en.UTF-8/LC_MESSAGES/libgsf.mo",O_RDONLY,00) ERR#2 'No such file or directory'
openat(AT_FDCWD,"/usr/local/share/locale/en/LC_MESSAGES/libgsf.mo",O_RDONLY,00) ERR#2 'No such file or directory'
write(8,"\^A\0\0\0\0\0\0\0",8)                   = 8 (0x8)
poll({ 5/POLLIN 8/POLLIN 19/POLLIN 22/POLLIN 27/POLLIN 31/POLLIN 32/POLLIN },7,-1) = 1 (0x1)
read(8,"\^A\0\0\0\0\0\0\0",8)                    = 8 (0x8)
sendmsg(5,{NULL,0,[{"l\^A\0\^A\M^@\0\0\0\M^H\0\0\0y\0"...,272}],1,{},0,0},MSG_NOSIGNAL) = 272 (0x110)
poll({ 5/POLLIN 8/POLLIN 19/POLLIN 22/POLLIN 27/POLLIN 31/POLLIN 32/POLLIN },7,0) = 0 (0x0)
poll({ 5/POLLIN 8/POLLIN 19/POLLIN 22/POLLIN 27/POLLIN 31/POLLIN 32/POLLIN },7,-1) = 1 (0x1)
write(8,"\^A\0\0\0\0\0\0\0",8)                   = 8 (0x8)
recvmsg(5,{NULL,0,[{"l\^B\^A\^A\0\0\0\0j\0\0\0005\0\0"...,16}],1,{},0,MSG_CMSG_CLOEXEC},MSG_CMSG_CLOEXEC) = 16 (0x10)
poll({ 5/POLLIN },1,0)                           = 1 (0x1)
recvmsg(5,{NULL,0,[{"\^F\^As\0\^E\0\0\0:1.94\0\0\0\^E"...,56}],1,{},0,MSG_CMSG_CLOEXEC},MSG_CMSG_CLOEXEC) = 56 (0x38)
write(8,"\^A\0\0\0\0\0\0\0",8)                   = 8 (0x8)
write(8,"\^A\0\0\0\0\0\0\0",8)                   = 8 (0x8)
poll({ 8/POLLIN 19/POLLIN 22/POLLIN 27/POLLIN 31/POLLIN 32/POLLIN },6,0) = 1 (0x1)
read(8,"\^C\0\0\0\0\0\0\0",8)                    = 8 (0x8)
write(16,"\^A\0\0\0\0\0\0\0",8)                  = 8 (0x8)
poll({ 16/POLLIN },1,25000)                      = 1 (0x1)
read(16,"\^A\0\0\0\0\0\0\0",8)                   = 8 (0x8)
poll({ 5/POLLIN },1,0)                           = 0 (0x0)
write(8,"\^A\0\0\0\0\0\0\0",8)                   = 8 (0x8)
poll({ 5/POLLIN 8/POLLIN 19/POLLIN 22/POLLIN 27/POLLIN 31/POLLIN 32/POLLIN },7,-1) = 1 (0x1)
read(8,"\^A\0\0\0\0\0\0\0",8)                    = 8 (0x8)
fstat(20,{ mode=-rw------- ,inode=70,size=0,blksize=4096 }) = 0 (0x0)
write(20,"\^_\M^K\b\0\0\0\0\0\0\^C\M-MWmo"...,1628) = 1628 (0x65c)
close(20)                                        = 0 (0x0)
rename("/tmp/.gsf-save-RACEV2","/tmp/Book1.gnumeric") = 0 (0x0)
chmod("/tmp/Book1.gnumeric",0644)                = 0 (0x0)
chown("/tmp/Book1.gnumeric",1002,1002)           = 0 (0x0)
chmod("/tmp/Book1.gnumeric",0644)                = 0 (0x0)
issetugid()                                      = 0 (0x0)
issetugid()                                      = 0 (0x0)
fstatat(AT_FDCWD,"/tmp/Book1.gnumeric",{ mode=-rw-r--r-- ,inode=70,size=1628,blksize=4096 },AT_SYMLINK_NOFOLLOW) = 0 (0x0)
issetugid()                                      = 0 (0x0)
access("/tmp/Book1.gnumeric",F_OK)               = 0 (0x0)
…
 
Look at the current version of the file: Does it have inode number 80095655?
The zeroed byte file has ha different inode number:

Code:
# ls -ali Gear.gnumeric
80095658 -rw-r--r--  1 owner  owner  0 Oct 11 13:25 Gear.gnumeric

Using find no other files were found with this nor the other inode number. :(

I also tried to find *gsf-save* files, but there were none.

Looks like having no chance. But thank you all for your ideas!
 
if you know what file contents you can bruteforce "grep" the device for it
best is to write a small c program
i saved myself once or twice like this after doing something like ls >all-day-work.c
i does not work perfectly but i could recover at list something
 
Looks like having no chance. But thank you all for your ideas!
Commiserations. It might be very old fashioned, but for local documents that I care about losing I still use devel/rcs to check in a copy every time I update. To get started:
Code:
mkdir RCS
libreoffice MySpreadSheet.cvs
ci MySpreadSheet.cvs
co -l MySpreadSheet.cvs
This simple approach preserves all previous version of the file for recovery. It protects against crashes while updating and finger trouble.

Other, more complicated, revision control schemes exist...
 
I saw this behavior in the few ufs experiments I've done (default installations). I suppose there are some fs settings to prevent this, …

  • … a delay of up to thirty seconds before data is written to disk (the norm for UFS)
  • expert test results suggest that the delay may be much greater.

– if I recall correctly (more than a year ago), the expert's calculation was up to two minutes.
 
Back
Top