diff --git a/README.md b/README.md index 0f2a191..4f81e18 100644 --- a/README.md +++ b/README.md @@ -20,22 +20,22 @@ Then, it tries to make sense of the extents stored in the inode (which XFS does ## Is it safe to use? Given it only ever *reads* from the filesystem it operates on, yes. -It also remounts the filesystem read-only on startup so you don’t accidentally overwrite source data. +It also remounts the filesystem read-only on startup by default so you don’t accidentally overwrite source data. However, I don’t offer any warranty or liability. **Use at your own risk.** ## Prerequisites *xfs_undelete* is a tiny Tcl script so it needs a Tcl interpreter. It makes use of some features of Tcl-8.6, so you need at least that version. The *tcllib* package is used for parsing the command line. -It also needs a version of *dd* which supports the *bs=*, *skip=*, *seek=*, *count=*, *conv=notrunc*, and *status=none* options. -That one from from GNU core utilities will do. +It also needs a version of *dd* which supports the *bs=*, *skip=*, *seek=*, *count=*, *conv=notrunc*, and *status=none* options, as well as a *readlink* which supports the *-e* option, and a version of *stat* which supports the *-L* and *--format=%m* options. +The ones from GNU core utilities will do. If the *file* utility and magic number files with MIME type support are installed (likely), *xfs_undelete* will use that to guess a file extension from the content of the recovered file. In short: - tcl >= 8.6 - tcllib - GNU coreutils -Optional: +Recommended: - file (having magic number files with MIME type support) @@ -55,6 +55,12 @@ terms of the attached GPLv3 license. See the file LICENSE for details. There's a manpage. Here is a copy of it: +--- +date: March 2020 +section: 8 +title: xfs\_undelete +--- + NAME ==== @@ -66,7 +72,7 @@ SYNOPSIS **xfs\_undelete** \[ **-t** *timespec* \] \[ **-r** *filetypes* \] \[ **-i** *filetypes* \] \[ **-z** *filetypes* \] \[ **-o** *output\_directory* \] \[ **-s** *start\_inode* \] \[ **-m** -*magicfiles* \] *device*\ +*magicfiles* \] \[ **\--no-remount-readonly** \] *device*\ **xfs\_undelete -l** \[ **-m** *magicfiles* \] DESCRIPTION @@ -112,42 +118,23 @@ OPTIONS **-r** *filetypes* : Only recover files with a filetype matching a pattern from this - **comma**-separated list of patterns. Patterns of the form \*/\* are - matched against known mimetypes, all others are matched against - known file extensions. (The file extensions are guessed from the - file contents with the help of the **file** utility, so they don\'t - neccessarily are the same the file had before deletion.) See the - **-l** option for a list of valid file types. By default this - pattern is \*; all files are being recovered, but also see the - **-i** option. **Note:** you may want to quote the list to avoid the - shell doing the wildcard expansion. + **comma**-separated list of patterns. See section **FILETYPES** + below. By default this pattern is \* ; all files are being + recovered, but also see the **-i** option. **-i** *filetypes* : Ignore files with a filetype matching a pattern from this - **comma**-separated list of patterns. Patterns of the form \*/\* are - matched against known mimetypes, all others are matched against - known file extensions. (The file extensions are guessed from the - file contents with the help of the **file** utility, so they don\'t - neccessarily are the same the file had before deletion.) See the - **-l** option for a list of valid file types. By default this list - is set to *bin*; all files of unknown type are being ignored, but - also see the **-r** option. **Note:** you may want to quote the list - to avoid the shell doing the wildcard expansion. + **comma**-separated list of patterns. See section **FILETYPES** + below. By default this list is set to *bin* ; all files of unknown + type are being ignored, but also see the **-r** option. **-z** *filetypes* -: Remove trailing zeroes from all files with a filetype matching a - pattern from this **comma**-separated list of patterns. Patterns of - the form \*/\* are matched against known mimetypes, all others are - matched against known file extensions. (The file extensions are - guessed from the file contents with the help of the **file** - utility, so they don\'t neccessarily are the same the file had - before deletion.) See the **-l** option for a list of valid file - types. By default this list is set to *text/\**; all files of - text/\* mimetype have their trailing zeroes removed. **Note:** you - may want to quote the list to avoid the shell doing the wildcard - expansion. +: Remove trailing zeroes from files with a filetype matching a pattern + from this **comma**-separated list of patterns. See section + **FILETYPES** below. By default this list is set to *text/\** ; all + files of text/\* mimetype have their trailing zeroes removed. **-o** *output\_directory* @@ -170,12 +157,50 @@ OPTIONS be used instead. This option is passed to the **file** utility in verbatim if specified. +**\--no-remount-readonly** + +: This is a convenience option meant for the case you need to recover + files from your root filesystem, which you cannot umount or remount + read-only at the time you want to run *xfs\_undelete*. The sane + solution would be moving the harddisk with that particular file + system to another computer where it isn\'t needed for operation. + +If you refuse to be that sane, you have to make sure the filesystem was +umounted or remounted read-only at least in the meantime by another +means, for example by doing a reboot. Otherwise you won\'t be able to +recover recently deleted files. + +**USE THIS OPTION AT YOUR OWN RISK.** As the source filesystem isn\'t +remounted read-only when you specify this option, you may accidentally +overwrite your source filesystem with the recovered files. +*Xfs\_undelete* checks if you accidentally specified your output +directory within the mount hierarchy of your source filesystem and +refuses to do such nonsense. However, automatic checks may fail, so +better check your specification of the output directory by hand. Twice. +It **must** reside on a different filesystem. + **-l** : Shows a list of filetypes suitable for use with the **-r**, **-i**, and **-z** options, along with common name as put by the **file** utility. +FILETYPES +========= + +The *filetypes* as used with the **-r**, **-i**, and **-z** options are +a **comma**-separated list of patterns. Patterns of the form \*/\* are +matched against known mimetypes, all others are matched against known +file extensions. The file extensions are guessed from the file contents +with the help of the **file** utility, so they don\'t neccessarily are +the same the file had before deletion. + +Start *xfs\_undeleted* with the **-l** option to get a list of valid +file types. + +**Note:** you want to quote the list of filetypes to avoid the shell +doing wildcard expansion. + EXAMPLES ======== @@ -217,23 +242,28 @@ state. This remount may fail if the filesystem is busy e.g. because it\'s your */home* or */* filesystem and there are programs having files opened in read-write mode on it. Stop those programs e.g. by running *fuser -m /home* or ultimately, put your computer into single-user mode -to have them stopped by init. - -For the same reason, you need some space on another filesystem to put -the recovered files onto. If your computer only has one huge xfs -filesystem, you need to connect external storage. - -If the recovered files have no file extensions, or if the **-r** and -**-i** options aren\'t functional, check with the **-l** option if the -**file** utility functions as intended. If the returned list is very -short, the **file** utility is most likely not installed or the magic -files for the **file** utility, often shipped extra in a package named -*file-magic* are missing, or they don\'t feature mimetypes. +to have them stopped by init. If you need to recover files from your / +filesystem, you may want to reboot, then use the +**\--no-remount-readonly** option, but the sane option is to boot from a +different root filesystem instead, for example by connecting the +harddisk with the valueable deleted files to another computer. + +You also need some space on another filesystem to put the recovered +files onto as they cannot be recovered in place. If your computer only +has one huge xfs filesystem, you need to connect external storage. + +If the recovered files have no file extensions, or if the **-r**, +**-i**, and **-z** options aren\'t functional, check with the **-l** +option if the **file** utility functions as intended. If the returned +list is very short, the **file** utility is most likely not installed or +the magic files for the **file** utility, often shipped extra in a +package named *file-magic* are missing, or they don\'t feature +mimetypes. SEE ALSO ======== -**xfs**(5), **fuser**(1), **clock**(n) +**xfs**(5), **fuser**(1), **clock**(n), **file**(1), AUTHORS ======= diff --git a/xfs_undelete b/xfs_undelete index b9a0f3c..9d6da85 100755 --- a/xfs_undelete +++ b/xfs_undelete @@ -82,6 +82,23 @@ proc readlink {symlink} { } +## Mountpoint helper. +proc mountpoint {dir} { + ## Get the mountpoint of the output directory. + while {[catch {exec -- stat -L --format=%m $dir} result]} { + ## Try again with the parent directory if the path does not exist. + set dir [file dirname $dir] + } + + if {$result eq "?"} { + puts stderr "Your stat utility does not support the %m format option." + exit 32 + } + + return $result +} + + ## DD helper. proc dd {args} { upvar inode restartinode @@ -319,6 +336,7 @@ if {[catch {set parameters [cmdline::getoptions argv { {s.arg "" "restart at inode number"} {m.arg "" "magic path passed to the 'file' utility"} {l "list file extensions understood"} + {no-remount-readonly "do not remount read-only before recovery"} } {[options] device -- options are:}]} result]} { puts stderr $result exit 127 @@ -436,46 +454,75 @@ foreach line [split $result \n] { ## Skip non-matching entries. if {[readlink [lindex $line 0]] ne $fs} continue + ## Ensure our output directory isn't on the same filesystem as the device node. + if {[mountpoint [dict get $::parameters o]] eq [lindex $line 2]} { + puts stderr "Your output directory is [file normalize [dict get $::parameters o]] +That is within the filesystem [lindex $line 2] you want to recover files +from. This isn't feasible as it would overwrite the deleted files you wanted to +recover. Please specify the option -o /path/to/output_directory on another (rw +mounted) filesystem or run xfs_undelete from within a directory on that +filesystem so the recovered files could be written there. They cannot be +recovered in place." + exit 32 + } + ## Skip non-read-write filesystems. if {"rw" ni [split [string trim [lindex $line 5] "()"] ,]} continue - ## Log. - puts stderr "[lindex $argv 0] ($fs) is currently mounted read-write. Trying to remount read-only." - - ## The rw option has been found. Try to remount read-only. - set lang $env(LANG) - set env(LANG) C - if {[catch {exec -- mount -oremount,ro [lindex $line 2]} err]} { - switch -glob -- $err { - {mount: only root can use "--options" option} { - ## Fail if we cannot remount due to missing permissions. - puts stderr "Remount failed. Root privileges are required to run this command on this filesystem." - exit 32 - } - {mount: * is busy?} { - puts stderr "Remount failed. $err" - exit 32 - } - "* mount point not mounted or bad option." { - ## Ignore this error. - } - default { - ## Error during mount. Fail. - puts stderr $err - exit 32 + ## Check if the remount step should be skipped. + if {[dict get $::parameters no-remount-readonly]} { + ## Confirm. + puts -nonewline stderr "[lindex $argv 0] ($fs) is currently mounted read-write, but you +specified the --no-remount-readonly option. This is a convenience option meant +for the case you need to recover files from your root filesystem. You have to +make sure the filesystem was umounted or remounted read-only by another means, +for example a reboot. Otherwise you won't be able to recover recently deleted +files. + +Type UNDERSTOOD if you have fully understood that: " + if {[gets stdin] ne "UNDERSTOOD"} { + ## Log. + puts stderr "Operation cancelled." + exit 32 + } + } else { + ## Log. + puts stderr "[lindex $argv 0] ($fs) is currently mounted read-write. Trying to remount read-only." + + ## The rw option has been found. Try to remount read-only. + set lang $env(LANG) + set env(LANG) C + if {[catch {exec -- mount -oremount,ro [lindex $line 2]} err]} { + switch -glob -- $err { + {mount: only root can use "--options" option} { + ## Fail if we cannot remount due to missing permissions. + puts stderr "Remount failed. Root privileges are required to run this command on this filesystem." + exit 32 + } + {mount: * is busy?} { + puts stderr "Remount failed. $err" + exit 32 + } + "* mount point not mounted or bad option." { + ## Ignore this error. + } + default { + ## Error during mount. Fail. + puts stderr $err + exit 32 + } } } - } - set env(LANG) $lang + set env(LANG) $lang - ## Sucess. Log. - puts stderr "Remount successful." + ## Sucess. Log. + puts stderr "Remount successful." + } ## Ignore multiple mounts of the same filesystem as they all share the same read-only option. break } - ## Open filesystem image. if {[catch {open $fs r} fd]} { puts stderr "Opening of filesystem failed. $fd" diff --git a/xfs_undelete.man b/xfs_undelete.man index fa4e5e6..526f2d7 100644 --- a/xfs_undelete.man +++ b/xfs_undelete.man @@ -25,6 +25,8 @@ xfs_undelete \- an undelete tool for the XFS filesystem ] [ .B \-m .I magicfiles +] [ +.B \--no-remount-readonly ] .I device .br @@ -64,7 +66,15 @@ Specify the inode number the recovery should be started at. This must be an exis \fB\-m\fR \fImagicfiles\fR Specify an alternate list of files and directories containing magic. This can be a single item, or a \fBcolon\fR-separated list. If a compiled magic file is found alongside a file or directory, it will be used instead. This option is passed to the \fBfile\fR utility in verbatim if specified. .TP -\fB\-l\fR\fR +\fB\--no-remount-readonly\fR +This is a convenience option meant for the case you need to recover files from your root filesystem, which you cannot umount or remount read-only at the time you want to run \fIxfs_undelete\fR. The sane solution would be moving the harddisk with that particular file system to another computer where it isn't needed for operation. + +If you refuse to be that sane, you have to make sure the filesystem was umounted or remounted read-only at least in the meantime by another means, for example by doing a reboot. Otherwise you won't be able to recover recently deleted files. + +\fBUSE THIS OPTION AT YOUR OWN RISK.\fR +As the source filesystem isn't remounted read-only when you specify this option, you may accidentally overwrite your source filesystem with the recovered files. \fIXfs_undelete\fR checks if you accidentally specified your output directory within the mount hierarchy of your source filesystem and refuses to do such nonsense. However, automatic checks may fail, so better check your specification of the output directory by hand. Twice. It \fBmust\fR reside on a different filesystem. +.TP +\fB\-l\fR Shows a list of filetypes suitable for use with the \fB-r\fR, \fB-i\fR, and \fB-z\fR options, along with common name as put by the \fBfile\fR utility. .SH FILETYPES The \fIfiletypes\fR as used with the \fB-r\fR, \fB-i\fR, and \fB-z\fR options are a \fBcomma\fR-separated list of patterns. Patterns of the form */* are matched against known mimetypes, all others are matched against known file extensions. The file extensions are guessed from the file contents with the help of the \fBfile\fR utility, so they don't neccessarily are the same the file had before deletion. @@ -99,13 +109,13 @@ This recovers all files deleted not more than two hours ago, including "bin" fil This only recovers files matching any image/ mimetype plus those getting assigned an extension starting with gimp-. .ED .SH TROUBLESHOOTING -When operating on devices, this program must be run as root, as it remounts the source filesystem read-only to put it into a consistent state. This remount may fail if the filesystem is busy e.g. because it's your \fI/home\fR or \fI/\fR filesystem and there are programs having files opened in read-write mode on it. Stop those programs e.g. by running \fIfuser -m /home\fR or ultimately, put your computer into single-user mode to have them stopped by init. +When operating on devices, this program must be run as root, as it remounts the source filesystem read-only to put it into a consistent state. This remount may fail if the filesystem is busy e.g. because it's your \fI/home\fR or \fI/\fR filesystem and there are programs having files opened in read-write mode on it. Stop those programs e.g. by running \fIfuser -m /home\fR or ultimately, put your computer into single-user mode to have them stopped by init. If you need to recover files from your / filesystem, you may want to reboot, then use the \fB\--no-remount-readonly\fR option, but the sane option is to boot from a different root filesystem instead, for example by connecting the harddisk with the valueable deleted files to another computer. -For the same reason, you need some space on another filesystem to put the recovered files onto. If your computer only has one huge xfs filesystem, you need to connect external storage. +You also need some space on another filesystem to put the recovered files onto as they cannot be recovered in place. If your computer only has one huge xfs filesystem, you need to connect external storage. -If the recovered files have no file extensions, or if the \fB\-r\fR and \fB\-i\fR options aren't functional, check with the \fB-l\fR option if the \fBfile\fR utility functions as intended. If the returned list is very short, the \fBfile\fR utility is most likely not installed or the magic files for the \fBfile\fR utility, often shipped extra in a package named \fIfile-magic\fR are missing, or they don't feature mimetypes. +If the recovered files have no file extensions, or if the \fB\-r\fR, \fB\-i\fR, and \fB\-z\fR options aren't functional, check with the \fB-l\fR option if the \fBfile\fR utility functions as intended. If the returned list is very short, the \fBfile\fR utility is most likely not installed or the magic files for the \fBfile\fR utility, often shipped extra in a package named \fIfile-magic\fR are missing, or they don't feature mimetypes. .SH SEE ALSO -\fBxfs\fR(5), \fBfuser\fR(1), \fBclock\fR(n) +\fBxfs\fR(5), \fBfuser\fR(1), \fBclock\fR(n), \fBfile\fR(1), .SH AUTHORS Jan Kandziora