^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) The Linux Journalling API
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) =========================
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) Overview
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) --------
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) Details
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) ~~~~~~~
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) The journalling layer is easy to use. You need to first of all create a
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) journal_t data structure. There are two calls to do this dependent on
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) how you decide to allocate the physical media on which the journal
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) resides. The jbd2_journal_init_inode() call is for journals stored in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) filesystem inodes, or the jbd2_journal_init_dev() call can be used
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) for journal stored on a raw device (in a continuous range of blocks). A
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) journal_t is a typedef for a struct pointer, so when you are finally
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) finished make sure you call jbd2_journal_destroy() on it to free up
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) any used kernel memory.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) Once you have got your journal_t object you need to 'mount' or load the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) journal file. The journalling layer expects the space for the journal
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) was already allocated and initialized properly by the userspace tools.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) When loading the journal you must call jbd2_journal_load() to process
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) journal contents. If the client file system detects the journal contents
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) does not need to be processed (or even need not have valid contents), it
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) may call jbd2_journal_wipe() to clear the journal contents before
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) calling jbd2_journal_load().
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) Note that jbd2_journal_wipe(..,0) calls
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) jbd2_journal_skip_recovery() for you if it detects any outstanding
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) transactions in the journal and similarly jbd2_journal_load() will
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) call jbd2_journal_recover() if necessary. I would advise reading
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) ext4_load_journal() in fs/ext4/super.c for examples on this stage.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) Now you can go ahead and start modifying the underlying filesystem.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) Almost.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) You still need to actually journal your filesystem changes, this is done
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) by wrapping them into transactions. Additionally you also need to wrap
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) the modification of each of the buffers with calls to the journal layer,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) so it knows what the modifications you are actually making are. To do
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) this use jbd2_journal_start() which returns a transaction handle.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) jbd2_journal_start() and its counterpart jbd2_journal_stop(),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) which indicates the end of a transaction are nestable calls, so you can
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) reenter a transaction if necessary, but remember you must call
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) jbd2_journal_stop() the same number of times as
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) jbd2_journal_start() before the transaction is completed (or more
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) accurately leaves the update phase). Ext4/VFS makes use of this feature to
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) simplify handling of inode dirtying, quota support, etc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) Inside each transaction you need to wrap the modifications to the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) individual buffers (blocks). Before you start to modify a buffer you
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) need to call jbd2_journal_get_create_access() /
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) jbd2_journal_get_write_access() /
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) jbd2_journal_get_undo_access() as appropriate, this allows the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) journalling layer to copy the unmodified
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) data if it needs to. After all the buffer may be part of a previously
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) uncommitted transaction. At this point you are at last ready to modify a
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) buffer, and once you are have done so you need to call
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) jbd2_journal_dirty_metadata(). Or if you've asked for access to a
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) buffer you now know is now longer required to be pushed back on the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) device you can call jbd2_journal_forget() in much the same way as you
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) might have used bforget() in the past.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) A jbd2_journal_flush() may be called at any time to commit and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) checkpoint all your transactions.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) Then at umount time , in your put_super() you can then call
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) jbd2_journal_destroy() to clean up your in-core journal object.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) Unfortunately there a couple of ways the journal layer can cause a
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) deadlock. The first thing to note is that each task can only have a
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) single outstanding transaction at any one time, remember nothing commits
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) until the outermost jbd2_journal_stop(). This means you must complete
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) the transaction at the end of each file/inode/address etc. operation you
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) perform, so that the journalling system isn't re-entered on another
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) journal. Since transactions can't be nested/batched across differing
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) journals, and another filesystem other than yours (say ext4) may be
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) modified in a later syscall.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) The second case to bear in mind is that jbd2_journal_start() can block
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) if there isn't enough space in the journal for your transaction (based
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) on the passed nblocks param) - when it blocks it merely(!) needs to wait
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) for transactions to complete and be committed from other tasks, so
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) essentially we are waiting for jbd2_journal_stop(). So to avoid
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) deadlocks you must treat jbd2_journal_start() /
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) jbd2_journal_stop() as if they were semaphores and include them in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) your semaphore ordering rules to prevent
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) deadlocks. Note that jbd2_journal_extend() has similar blocking
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) behaviour to jbd2_journal_start() so you can deadlock here just as
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) easily as on jbd2_journal_start().
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) Try to reserve the right number of blocks the first time. ;-). This will
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) be the maximum number of blocks you are going to touch in this
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) transaction. I advise having a look at at least ext4_jbd.h to see the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) basis on which ext4 uses to make these decisions.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) Another wriggle to watch out for is your on-disk block allocation
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) strategy. Why? Because, if you do a delete, you need to ensure you
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) haven't reused any of the freed blocks until the transaction freeing
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) these blocks commits. If you reused these blocks and crash happens,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) there is no way to restore the contents of the reallocated blocks at the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) end of the last fully committed transaction. One simple way of doing
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) this is to mark blocks as free in internal in-memory block allocation
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) structures only after the transaction freeing them commits. Ext4 uses
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) journal commit callback for this purpose.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) With journal commit callbacks you can ask the journalling layer to call
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) a callback function when the transaction is finally committed to disk,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) so that you can do some of your own management. You ask the journalling
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) layer for calling the callback by simply setting
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) ``journal->j_commit_callback`` function pointer and that function is
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) called after each transaction commit. You can also use
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) ``transaction->t_private_list`` for attaching entries to a transaction
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) that need processing when the transaction commits.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) JBD2 also provides a way to block all transaction updates via
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) jbd2_journal_lock_updates() /
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) jbd2_journal_unlock_updates(). Ext4 uses this when it wants a
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) window with a clean and stable fs for a moment. E.g.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) ::
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) jbd2_journal_lock_updates() //stop new stuff happening..
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) jbd2_journal_flush() // checkpoint everything.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) ..do stuff on stable fs
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) jbd2_journal_unlock_updates() // carry on with filesystem use.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) The opportunities for abuse and DOS attacks with this should be obvious,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) if you allow unprivileged userspace to trigger codepaths containing
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) these calls.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) Fast commits
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) ~~~~~~~~~~~~
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) JBD2 to also allows you to perform file-system specific delta commits known as
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) fast commits. In order to use fast commits, you will need to set following
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) callbacks that perform correspodning work:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) `journal->j_fc_cleanup_cb`: Cleanup function called after every full commit and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) fast commit.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) `journal->j_fc_replay_cb`: Replay function called for replay of fast commit
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) blocks.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) File system is free to perform fast commits as and when it wants as long as it
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) gets permission from JBD2 to do so by calling the function
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) :c:func:`jbd2_fc_begin_commit()`. Once a fast commit is done, the client
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) file system should tell JBD2 about it by calling
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) :c:func:`jbd2_fc_end_commit()`. If file system wants JBD2 to perform a full
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) commit immediately after stopping the fast commit it can do so by calling
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) :c:func:`jbd2_fc_end_commit_fallback()`. This is useful if fast commit operation
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) fails for some reason and the only way to guarantee consistency is for JBD2 to
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) perform the full traditional commit.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) JBD2 helper functions to manage fast commit buffers. File system can use
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) :c:func:`jbd2_fc_get_buf()` and :c:func:`jbd2_fc_wait_bufs()` to allocate
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) and wait on IO completion of fast commit buffers.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) Currently, only Ext4 implements fast commits. For details of its implementation
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) of fast commits, please refer to the top level comments in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) fs/ext4/fast_commit.c.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) Summary
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) ~~~~~~~
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) Using the journal is a matter of wrapping the different context changes,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) being each mount, each modification (transaction) and each changed
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) buffer to tell the journalling layer about them.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) Data Types
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) ----------
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) The journalling layer uses typedefs to 'hide' the concrete definitions
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) of the structures used. As a client of the JBD2 layer you can just rely
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) on the using the pointer as a magic cookie of some sort. Obviously the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) hiding is not enforced as this is 'C'.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) Structures
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) ~~~~~~~~~~
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) .. kernel-doc:: include/linux/jbd2.h
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) :internal:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) Functions
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) ---------
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) The functions here are split into two groups those that affect a journal
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) as a whole, and those which are used to manage transactions
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) Journal Level
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) ~~~~~~~~~~~~~
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) .. kernel-doc:: fs/jbd2/journal.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) :export:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) .. kernel-doc:: fs/jbd2/recovery.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) :internal:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) Transasction Level
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) ~~~~~~~~~~~~~~~~~~
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) .. kernel-doc:: fs/jbd2/transaction.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) See also
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) --------
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) `Journaling the Linux ext2fs Filesystem, LinuxExpo 98, Stephen
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) Tweedie <http://kernel.org/pub/linux/kernel/people/sct/ext3/journal-design.ps.gz>`__
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) `Ext3 Journalling FileSystem, OLS 2000, Dr. Stephen
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) Tweedie <http://olstrans.sourceforge.net/release/OLS2000-ext3/OLS2000-ext3.html>`__
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215)