Abstract: E2fsck found no error with the -n option but with -p (preen). It corrected the error but did not give any error message. The error is only reflected via exit code. How to interpret this?

I am using an USB hard drive with an Ext2 filesystem to store backups of several machines. Recently I had a huge data throughput on that drive why I decided to do an extra filesystem check. In total, I did four e2fsck runs with different options. Here are the commands I used (as root) together with their outputs, which also contain the exit status of e2fsck. Unfortunately some phrases are localized to German but the (presumably) important ones are in English:

1st run, read-only:

# e2fsck -nv /dev/sdb1; echo $?
e2fsck 1.41.1 (01-Sep-2008)
WD-Elements: sauber, 709312/61046784 Dateien, 96258851/244182016 Blöcke

2nd run, read-only forced:

# e2fsck -nfv /dev/sdb1; echo $?
e2fsck 1.41.1 (01-Sep-2008)
Durchgang 1: Prüfe Inodes, Blocks, und Größen
Durchgang 2: Prüfe Verzeichnis Struktur
Durchgang 3: Prüfe Verzeichnis Verknüpfungen
Durchgang 4: Überprüfe die Referenzzähler
Durchgang 5: Überprüfe Gruppe Zusammenfassung

  709312 inodes used (1.16%)
   95492 non-contiguous inodes (13.5%)
         # von Inodes mit ind/dind/tind Blöcken: 109958/2429/7
96258851 blocks used (39.42%)
       0 bad blocks
       8 large files

  564029 regular files
  121351 directories
       0 character device files
       0 block device files
      11 fifos
  506224 links
   23073 symbolic links (19397 fast symbolic links)
     839 sockets
 1215527 files

3rd run, preening:

# e2fsck -pv /dev/sdb1; echo $?
WD-Elements: sauber, 709312/61046784 Dateien, 96258851/244182016 Blöcke

4th run, preening forced:

# e2fsck -pfv /dev/sdb1; echo $?

  709312 inodes used (1.16%)
   95492 non-contiguous inodes (13.5%)
         # von Inodes mit ind/dind/tind Blöcken: 109958/2429/7
96258853 blocks used (39.42%)
       0 bad blocks
       8 large files

  564029 regular files
  121351 directories
       0 character device files
       0 block device files
      11 fifos
  506224 links
   23073 symbolic links (19397 fast symbolic links)
     839 sockets
 1215527 files

The commands were issued directly one after the other without touching anything else in between.

Please note the differences:

  • In the first two runs, the filesystem was opened read-only (-n option), while the last two were preening runs (-p option).

  • The first and the third run were not forced, the second and the last run were (-f).

  • All runs reported coinciding filesystem data with one exception: The last run (-pfv) reported a different number of "blocks used".

  • All but the last run exited with status 0, the last one (-pfv) with status 1.

Obviously the last, forced preening run (-pfv) has found (and corrected) a filesystem error that the other runs were not able to find. Unfortunately it does not give any hint about that error in its output.

Now for my questions:

  • What error was found and corrected there? Was it as simple as an incorrect count of used blocks?

  • What could have caused that error? The filesystem was always cleanly unmounted.

  • The filesystem error was finally corrected by e2fsck. But can I trust the data stored therein? Couldn't it be that whatever caused that filesystem error in the first place, also corrupted the data on the disk? This would render all data on the disk worthless. Or is this to paranoid? Why?

The last question distinguishes between filesystem and data. In this respect, Mikel's answer to "Do journaling filesystems guarantee against corruption after a power failure?" is of high relevance. Unfortunately it focuses on journaling filesystems, so it does not apply to Ext2.

Also Gilles' answer to "How to test file system correction done by fsck" is a good read: According to that, fsck only guarantees a consistent state of the filesystem, not necessarily the latest one.

Update 1

In his comment, Luciano Andress Martini pointed out that the observed and apparently puzzling behavior of e2fsck could have been caused by RAM errors in the executing machine. While being a highly relevant aspect in comparable situations, it does not seem to apply here: I checked the RAM with "memtest86+" over night and it completed 16 passes without errors. In addition, I performed e2fsck -nfv, e2fsck -pfv, and e2fsck -fv runs on the drive under test using another machine (different hardware, kernel, and version of e2fsck). These did not find any filesystem errors and confirmed the filesystem data that was reported by the last e2fsck command shown above, in particular the number of used blocks. Also the total number of blocks (244182016) that was reported by the unforced checks was confirmed.

Update 2

telcoM's answer suggests, that the observed behavior of e2fsck might be explained with changes of the filesystem feature settings that e2fsck does when dealing with very old filesystems. Unfortunately this very consistent explanation does not apply here: The filesystem was actually created with a newer version of mke2fs (1.42.8) which enabled the features ext_attr, resize_inode, dir_index, filetype, sparse_super, large_file. This was not changed by the e2fsck runs described above.

Update 3

Meanwhile the USB drive successfully passed a non-destructive read-write badblocks test (took 3 days, and yes: the specified block size (-b) and number of blocks (-c) matter lot) and several offline S.M.A.R.T. tests.

  • Why are you using ext2 and not ext4 with a journal for your backup partition?
    – sudodus
    Jan 13, 2019 at 17:09
  • 1
    Fsck can do bad things specially if your machine has memory RAM errors. It can see errors when they are not, and do bad modifications. Jan 13, 2019 at 18:32
  • @sudodus: You are totally right. But in this case, Ext2 is used for compatibility reasons with very old machines, which even do not know Ext3. (They are used air gapped, of course.)
    – Jürgen
    Jan 13, 2019 at 19:24
  • @Luciano: So you mean that in case of a true filesystem error, e2fsck should definitively have been printing an error message to stdout, even when preening (-p), and the absence of such a message is a strong indication of RAM errors? Good point, I didn't have thought of that.
    – Jürgen
    Jan 13, 2019 at 19:30
  • 1
    The errors will appear and dissappear randomly in that case. And if you fix the errors, in the next check it is possible that new errors appear. I am not very sure about your problem. Jan 15, 2019 at 10:27

2 Answers 2


To supplement the helpful contributions to my question, I did some research on my own. Because parts of the results might be of also of some general interest, I summarize them in this self-answer.

Please note: Defined by the question, all of the following refers to e2fsck version 1.41.1 and focuses to Ext2 filesystems without journal. But the general terms do also apply to some extend to contemporary versions of both, program and filesystem.

Lessons learned

Let's start with the headlines—rationales below:

  • Run e2fsck in the C locale, for example like so:

    LC_ALL=C e2fsck ...

    This way you get English messages which make it easier to find specific help on the net.

  • Exercise caution with the -y option: It will automatically answer "yes" to all prompts that e2fsck comes up with. And these will not always refer to questions like "Fix this error?", there are also questions with the gist "Delete the affected file?".

  • That e2fsck did some changes to the filesystem and exited with status 1 (or 3) does not mean that there were filesystem errors (corruptions).

  • e2fsck handles SIGINT (Ctrl-C). But I would avoid to resort to it. (Personal opinion.)

The following points focus on the information that you get from e2fsck:

  • If you want to know what errors your filesystem probably contains and what e2fsck does about it, do not use the -p option (preen).

  • An interactive run of e2fsck (i.e. one without the options -n, -p, -a, -y) outputs more messages on the errors it finds than read-only (-n) or preening (-p) runs do. -a is just an alias to -p. From "yes" runs (-y) you get basically the same information as from an interactive run.

  • Albeit being pretty close, the option -n does not yield an exact dry run of an interactive one.

  • If you do not use the -f option, chances are that e2fsck forces the check by its own. Doing so, it gives additional information to reason its decision. Example:

    ... primary superblock features different from backup, check forced.

    If you do not want to miss this, start without the -f option, using it only in a second attempt if e2fsck refuses checking because the filesystem appears to be clean.

  • Do not forget to look at the exit code of e2fsck to get the whole picture: echo $?.

Types of checks

Interactive: Without using the options -n, -p, -a and -y, e2fsck performs an interactive filesystem check. This means that it will ask you on every step what to do. This gives you maximum control over the process.

Caveat: Depending on size and health of the filesystem, this can get tedious rather fast: Imagine that you have to confirm fixing inode by inode. Such sessions may last for hours or even worse.

In addition, things might get really scary if the questions evolve in a direction that you are not familiar with.

Interrupting: If an interactive check gets out of hand in this way, it might be nice to know that e2fsck handles SIGINT (Ctrl-C).

In fact, there are encouraging reports, for example by MadHatter and by Chris. But as already mentioned, I would try to avoid such interrupts.

The reason is simple: Checking a filesystem is a complicated process, repairing corruptions has to be done in a coherent and consistent way, and handling interrupts boost the complexity even more. Like any complex software, signal handlers might be buggy. See for example this post by Andreas Dilger. So why to take the risk? There might be good reasons of course, but weight by yourself.

Read-only: If you know little about the health status of the filesystem to check, it is a good idea to use e2fsck with the -n option first. As we will see below, this does not yield an exact dry run, but it gives a good impression of what you might expect from an interactive run.

Preening: e2fsck preens the filesystem if the -p option is used. The man page e2fsck(8) sounds promising:

This option will cause e2fsck to automatically fix any filesystem problems that can be safely fixed without human intervention.

But this means also, that it will only correct some filesystem errors. It can be seen from the sources of e2fsck, that a -p run stops as soon as it detects an error that it can not handle safely, leaving the rest to subsequent runs that do more than just preening.

In addition, as said above, -p will give less information on the errors and its corrections.

Yes: Starting e2fsck with the -y option yields the same result as an interactive run whose questions were all answered with "yes". The pitfalls of this approach were already mentioned above.

Expect: As I learned from this section of the "Ext2fs Undeletion of Directory Structures mini-HOWTO", that one can automatically answer the questions of e2fsck with a finer granularity by using the program Expect. There, the following wrapper script for e2fsck is used:

#!/usr/bin/expect -f
set timeout -1
spawn /sbin/e2fsck -f $argv
expect {
    "Clear<y>? " { send "n" ; exp_continue }
    "<y>? "      { send "y" ; exp_continue }

This will automatically answer all questions that use the prompt "Clear? " with "n", and all the others with "y". Consult the documentation of Expect for details. See this question by Wrothgarr for another example of using expect for e2fsck.

To clarify: I do not recommend to blindly use those scripts. They are just referenced here for "educational purposes".

For those that want to take up this idea and tailor it to their own needs: Near the beginning of the e2fsck source file e2fsck/problem.c, the array prompt is defined to hold all of the in total 20 prompts that e2fsck uses. Some of them are used only internally. More on the interrelation between prompts and filesystem errors follows below.

Learned from the sources

Dialog: For the majority of errors that it find in the filesystem, e2fsck consults the function fix_problem that is defined in file e2fsck/problem.c. This function performs the dialog with the user as appropriate for the individual error depending on the e2fsck options given.

For that, fix_problem looks up the current error code in the array problem_table which is defined earlier in the same file e2fsck/problem.c. This array attributes each error code with a template for an error message, a prompt to ask the user about the problem, and a bit mask that controls details of the error handling. (For some errors there is also a reference to a further error code, called "after code", whose dialog has be performed subsequently. But this does not matter for us.)

There are two flags occasionally used in this bit mask that are important for our question: PR_PREEN_NOMSG and PR_NO_NOMSG. When set, they suppress the error message for -p runs and -n runs, respectively. So these define the errors for which you get more information in an interactive run or a -y run.

The definition of problem_table specifies 292 error codes of which 23 are flagged PR_PREEN_NOMSG and only 1 is flagged PR_NO_NOMSG. None of them carries both flags, PR_PREEN_NOMSG and PR_NO_NOMSG.

Another interesting flag is PR_PREEN_OK: Errors that carry this flag can be safely handled by preen (-p runs). There are other errors that preen takes care of, see "special cases" below, but these are the majority of them. 82 errors in the array problem_table are flagged PR_PREEN_OK.

Start: For a Linux build of e2fsck version 1.41.1, execution starts with the function main in file e2fsck/unix.c.

Pass 0: After initialization and checking the journal, which is not relevant for this question, some fundamental checks and cleanups are performed on the filesystem. This is also addressed as pass 0. A major part of this is done by the function check_super_block in source file e2fsck/super.c.

Albeit the name, this function does not only take care of the superblock but also checks the block group descriptors. Doing so it sums up the per block group counts of free blocks and free inodes and compares the result with the global values in the superblock.

What happens if these values do not coincide depends on the e2fsck command line options: For an -n run, the filesystem is considered invalid and a full check is forced later. In all other cases (-p, -y, interactive run), the total counts in the superblock are silently updated, not forcing a full check. In fact, if the latter runs do not find additional errors, they report the filesystem clean in spite of this silent correction.

The function check_super_block does also other things, like checking the resize inode, cleaning up orphan inodes, doing some housekeeping around the journal, but this does not seem to be important for our question.

Skip: If a full filesystem check is not already forced by the -f option, e2fsck my decide to force a full check by itself. Widely known criteria for this are the number of mounts since last check, the absence of a clean unmount, already known files errors, etc.

But there is another criterion that it applied, yet only if the -n option was not used: Differences between the superblock and its backups with respect to the following quantities:

  • Enabled filesystem features, apart from large_file, dir_nlink, extent,

  • total blocks count,

  • total inodes count,

  • filesystem UUID.

The reason to except some of the filesystem features from this criterion is that the kernel may set such features on the fly as needed, and does this only in the superblock but not in the backups. For the excepted features, such differences are not considered important enough to force a full check. In contrast, the ext_attr feature may also be dynamically set by the kernel, but in this case an update in the backups is crucial, why this feature is not excepted.

If e2fsck decides to force a full check by ts own, it prints a message to reason the inconvenience. If this is because of one of the named differences between the superblock and its backups, the message reads:

... primary superblock features different from backup, check forced.

Please note that the term "features" in the message has a wider meaning than pure "filesystem features": It also covers the total block count for example. See also this post by Eric Sandeen and this one by Theodore Tso in this respect.

Anyhow, you will never see this message in an -n run, because, as said above, the superblock backups are not taken into account in this case.

If a full check is not forced, neither by -f nor by e2fsck, the check is skipped (e2fsck diction). In this case, e2fsck reports the filesystem as clean end exits with status 0. This is also true, if there were some repairs in pass 0, like a correction of the total free blocks count in the superblock for example.

Passes 1 to 6: In a full check, e2fsck does at least five complete passes through the filesystem, each one with a different focus. These are carried out by the functions e2fsck_pass1 to e2fsck_pass5 which are defined in the source files e2fsck/pass1.c to e2fsck/pass5.c, respectively.

There might be additional passes that supplement pass 1 if needed to handle special filesystem corruptions. These are labeled pass 1B to pass 1D, and the corresponding functions pass1b to pass1d are all defined in e2fsck/pass1b.c.

The rehashing of directories, which is part of pass 3 and which is carried out by function e2fsck_rehash_directories in file e2fsck/rehash.c is considered as pass 3A.

In addition there is an error code PR_6_RECREATE_JOURNAL which is used when the journal has to be recreated. Obviously this is considered to constitute a separate pass: pass 6. It is performed in function main.

Most of the errors defined in the array problem_table are checked for in these passes. For each error one can see the number of the pass that it is subject to from the name of its error code: The number follows the first underscore in the code name. So, for example the error PR_1_TOO_MANY_BAD_BLOCKS is handled in pass 1, and PR_3A_OPTIMIZE_DIR_ERR is taken care of in pass 3A.

It is of particular interest for this question, that the total counts of free blocks and free inodes are checked again at the beginning of pass 5: Other than in the quick check in pass 0, where only the corresponding values from the block group descriptors were summed up, this time the counts are computed based on the data that e2fsck gathered thoroughly in the course of its passes through the complete filesystem where the roles of each block and each inode were analyzed individually. This is done by the functions check_block_bitmaps and check_inode_bitmaps which are defined in file e2fsck/pass5.c.

Differences of the resulting values compared to those in the superblock are treated as errors PR_5_FREE_BLOCK_COUNT and PR_5_FREE_INODE_COUNT. By the way, these errors are flagged PR_PREEN_NOMSG, so they are not reported explicitly when preening (-p).

Special cases: There are corrections that e2fsck might perform on the filesystem without calling fix_problem or consulting the error catalog in problem_table. These corrections are performed only in the absence of the -n option and without notice in the output, yet maybe in the exit status. I found three of those in the sources:

  • The correction of the total free blocks and free inodes counts in the superblock during pass 0 (without -n). This was already discussed above.

  • During pass 1, the last orphan field in the superblock is silently cleared if set (without -n).

  • If the link count value that is stored in the inode of an indexed directory indicates that it previously exceeded its upper limit while the current true count is found to be lower than this limit, the value in the inode is silently corrected in pass 4 (without -n).

Exit status: For a full (forced) check, the exit code is determined by function main after completion of the check passes in the course of the analysis of the latter's outcome: If the check was not canceled midway, the exit status will be zero if and only if there were no changes to the filesystem up to now.

Final touch: If the check was not canceled midway, the function main resets the mount count in the superblock and updates the timestamp there, so that e2fsck can tell in future runs when the next full check has to be forced. This is done during cleanup after the exit status has been determined, so this change has no influence on the status.

Signal handler: In the function PRS which is called by main, both defined in e2fsck/unix.c, e2fsck spawns signal handlers for SIGINT, SIGTERM, SIGUSR1, and SIGUSR2. The latter two can be used to switch progress information as explained in the man page e2fsck(8).

The former are obviously handled to allow safe interrupts and termination of e2fsck.

Learned from tests

Trying to reproduce the behavior of e2fsck that is shown in the question, I set up a test Ext2 filesystem, populated it with dummy files up to 10% of its capacity, and used a hex editor to introduce some artificial errors. Then that filesystem was checked with the same commands as in the question to compare outputs and exit statuses of e2fsck.

In the last run in the question, the count of used blocks has changed. e2fsck calculates this value in an obvious way from the free block count and the total blocks count. That is why I chose these quantities as the subject of the artificial errors.

Free blocks count in superblock: The data structure of the superblock is described in great detail in this document. (A contemporary version of this document, which describes the Ext4 filesystem, can be found here.) Based on that, I used the hex editor to reduce the free blocks count in the superblock by 2.

This artificial error was detected by e2fsck -nv (without -f) which complained loudly, forced a full check and exited with exit status 4.

Also a forced read-only run (-nfv) reported that error and exited with status 4.

A subsequent -pv run (without -f) found the filesystem clean and did not give any notice about an error. However, it corrected the error and output the number of used blocks based on the corrected values, yet exited with status 0.

After introducing the same error again, a forced preen run (-pfv) was also not reporting the error, but corrected it exiting with status 1.

This behavior of e2fsck can be well understood from what was learned from the sources, above.

This means, that it must have been a different error that led to the check results described in the question: Otherwise, it would have been reported by the read-only runs and corrected by the first (unforced) preen, so that the last run would have found a clean filesystem.

Total blocks count in superblock: Using a hex editor, I reduced the total blocks count in the superblock by 2.

This was not detected by an -nv run (without -f), which reported the filesystem as clean and exited with status 0.

Forcing this check (-nfv), several errors were found—wrong ones in a sense: e2fsck took the manipulated total block count seriously, and found in consequence wrong free blocks counts in the last block group and in the superblock. In addition, it found that the padding at the end of the block bitmap was not set. The exit status was 4.

A subsequent preening (-pv, without -f) forced a full check because of differences between the superblock and it backups. In the course, it corrected all the "wrong" errors that were previously found by the forced read-only run. However, it only reported the ("wrong") error in the bitmap padding, not giving any notice on the free blocks counts. It finally exited with status 1.

After introducing the same error again, a forced preening (-pfv) did essentially the same, except that is did not inform about the difference between the superblock and it backups that was previously given as the reason for the forced check.

Also this behavior of e2fsck can be understood from the discussion of the sources above. It was, however, different from what is described in the question. So it must have been another error there.

Free blocks count in backup: The block numbers of the superblock backups can be found with

LC_ALL=C dumpe2fs <device> | grep -i superblock

However, the free blocks count in the first superblock backup is totally ignored by e2fsck. In fact, it seems that this value differs from that in the primary superblock even in a truly clean filesystem. And indeed, if one thinks about it, it would be a huge overhead to keep this value constantly synchronized throughout all the backups. So I assume, that there it has no meaning at all.

Total blocks count in backup: Using the hex editor again, I reduced the total blocks count in the first superblock backup by 2.

This artificial error was totally ignored by e2fsck in read-only mode: -nv and -nfv.

A preen run (-pv, without -f) forced a full check giving the message

... primary superblock features different from backup, check forced.

In the course, it corrected the error without further related messages and exited with status 1.

After introducing the same error again, a forced preening (-pfv) did the same, yet without any notice on the error.

Again, this behavior can be well understood from the above discussion of the sources; and it is different from what was observed in the question.

In addition, the unforced e2fsck runs described in the question and the subsequently performed checks described in update 1 reported the same total blocks count. So this value was not changed in any of these runs and hence could not be the subject of the wanted error.

Does this yield an answer to the question?

In short: No.

For each individual run that is described in the question, I found possible errors that lead to the observed behavior of e2fsck. But I did not find a single error that causes that behavior of all runs in a sequence.

All errors in problem_table are ruled out, because they would have been reported either by the -nfv run or by the -pfv run or by both.

Considering the "special cases" above, a wrong free blocks or free inodes count would have been reported by the read-only runs. This was not the case.

The other "special cases" would not have led to a change of the used blocks count that was observed in the last run.

But after all, e2fsck is a complex piece of software, so it is most likely that I have overseen something.


Facing these findings, it seems that the following workflow can be used to safely check an unmounted Ext2 filesystem with unknown health status while avoiding unpleasant surprises in the interactive part and getting a maximum of information from e2fsck.

This assumes a healthy hardware! If in particular the drive is not trustworthy in this respect, unconditionally begin with step 3 (back up the filesystem), proceeding with the remaining steps in the stated order:

  1. Do an -nv run:

    LC_ALL=C e2fsck -nv <device>; echo $?
  2. If e2fsck skips the full check reporting the filesystem as clean, force the check repeating step 1 with -f.

  3. Depending on the corruptions found, back up the filesystem with dd. This allows to restore the current state if things get screwed up in the next steps.

  4. If it seems feasible according to the results of the read-only runs, do an interactive check with

    LC_ALL=C e2fsck -v <device>; echo $?

    forcing it with -f if needed to get a full check.

  5. What to do if an interactive run is not feasible depends on the findings so far.

Appendix: Inspecting filesystem features

dumpe2fs: The program dumpe2fs can be used to find out which features are enabled in a filesystem.

This does also work for unknown features. In this case, dumpe2fs uses generic feature names that uniquely identify the corresponding bits in the features fields of the superblock. For example FEATURE_R16 corresponds to bit 16 (counting from 0) in the read-only compatible features field of the superblock. Similarly, FEATURE_I31 corresponds to the most significant bit of the incompatible features field.

If the feature compression is set, dumpe2fs has to be started with the -f option.

However, version 1.41.1 of this program seems to be a little bit buggy, as it crashes with a floating point exception on some combinations of enabled and disabled features, e.g. for enabled 64bit and disabled journal_dev.

debugfs: The command show_super_stats of debugfs yields a similar output to that of dumpe2fs with regard to the enabled filesystem features. Also this program informs about unknown features.

The version 1.41.1 also of this program seems to be somehow buggy: The command show_super_stats crashes with a segmentation fault if compression or journal_dev is enabled. Like dumpe2fs, the whole program debugfs crushes with a floating point exception if the feature 64bit is enabled while journal_dev is disabled.

tune2fs: If only known filesystem features are enabled, they can be listed as part of the output of tune2fs -l. However, this program refuses to start if any unknown filesystem feature is enabled, even if the -f option is used.


You mentioned that this filesystem is used with very old machines. If the filesystem was originally created with a very old mke2fs tool that did not support the resize_inode filesystem feature to reserve some metadata space for on-line extension of the filesystem, it might be possible that your second run with e2fsck version 1.14.1 did just automatically add it.

If I recall correctly, the allocation is completely benign for old systems that don't understand it, but it ensures that some critical metadata structure can extend without a major re-organization, if the filesystem is ever extended.

You can confirm this by running tune2fs -l on the filesystem of your USB drive and on one of the ext2 filesystems of your old machines, and comparing the results. You can do that even if the filesystems are mounted. If the output for your USB drive includes the keyword resize_inode on the Filesystem features: line, and the local ext2 filesystems on your old machines don't have that keyword, then the most likely explanation is that your e2fsck -pfv just took the opportunity to make that tiny allocation in the hope that it might help avoid downtime in the future.

  • Actually the filesystem was created with a newer version of mke2fs namely 1.42.8 (20-Jun-2013). That one enables the features ext_attr, resize_inode, dir_index, filetype, sparse_super, large_file by default and did so also in this case. The enabled features are still the same.
    – Jürgen
    Jan 15, 2019 at 14:15

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .