Skip to content

Commit

Permalink
added --no-remount-readonly option
Browse files Browse the repository at this point in the history
  • Loading branch information
ianka committed Jul 14, 2020
1 parent fa1d88c commit 525c908
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 81 deletions.
124 changes: 77 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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
====

Expand All @@ -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
Expand Down Expand Up @@ -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*

Expand All @@ -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
========

Expand Down Expand Up @@ -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
=======
Expand Down
105 changes: 76 additions & 29 deletions xfs_undelete
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down
20 changes: 15 additions & 5 deletions xfs_undelete.man
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ xfs_undelete \- an undelete tool for the XFS filesystem
] [
.B \-m
.I magicfiles
] [
.B \--no-remount-readonly
]
.I device
.br
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 <[email protected]>

0 comments on commit 525c908

Please sign in to comment.