Snapshots
A snapshot is a committed stable view of a Power-Safe filesystem. Each mounted filesystem has one stable snapshot and one working view (in which copy-on-write modifications to the stable snapshot are being made).
Whenever a new snapshot is made, filesystem activity is suspended (to give a stable system), the bitmaps are updated, all dirty blocks are forced to disk, and the alternate filesystem superblock is written (with a higher sequence number). Then filesystem activity is resumed, and another working view is constructed on the old superblock. When a filesystem is remounted after an unclean power failure, it restores the last stable snapshot.
Snapshots are made:
- explicitly, when a global sync() of all filesystems is performed
- explicitly, when fsync() is called for any file in the Power-Safe filesystem
- explicitly, when switching to read-only mode with mount -ur
- periodically, from the timer specified to the snapshot= option to the mount command (the default is 10 seconds).
You can disable snapshots on a filesystem at a global or local level. When disabled, a new superblock isn't written, and an attempt to make a snapshot fails with an errno of EAGAIN (or silently, for the sync() or timer cases). If snapshots are still disabled when the filesystem is unmounted (implicitly or at a power failure), any pending modifications are discarded (lost).
Snapshots are also permanently disabled automatically after an unrecoverable error that would result in an inconsistent filesystem. An example is running out of memory for caching bitmap modifications, or a disk I/O error while writing a snapshot. In this case, the filesystem is forced to be read-only, and the current and all future snapshot processing is omitted; the aim being to ensure that the last stable snapshot remains undisturbed and available for reloading at the next mount/startup (i.e., the filesystem always has a guaranteed stable state, even if slightly stale). This is only for certain serious error situations, and generally shouldn't happen.
Manually disabling snapshots can be used to encapsulate a higher-level sequence of operations that must either all succeed or none occur (e.g., should power be lost during this sequence). Possible applications include software updates or filesystem defragmentation.
struct fs_fileflags flags;
memset( &flags, 0, sizeof(struct fs_fileflags));
flags.mask[FS_FLAGS_GENERIC] = FS_FLAGS_COMMITTING;
flags.bits[FS_FLAGS_GENERIC] = disable ? 0 : FS_FLAGS_COMMITTING;
devctl( fd, DCMD_FSYS_FILE_FLAGS, &flags,
sizeof(struct fs_fileflags), NULL);
# chattr -snapshot /fs/qnx6
/fs/qnx6: -snapshot
...
# chattr +snapshot /fs/qnx6
/fs/qnx6: +snapshot
To disable snapshots at a local level, adjust the QNX6FS_SNAPSHOT_HOLD count on a per-file-descriptor basis, again using the DCMD_FSYS_FILE_FLAGS command to devctl(). Each open file has its own hold count, and the sum of all local hold counts is a global hold count that disables snapshots if nonzero. Thus if any client sets a hold count, snapshots are disabled until all clients clear their hold counts.
struct fs_fileflags flags;
memset( &flags, 0, sizeof(struct fs_fileflags));
flags.mask[FS_FLAGS_FSYS] = QNX6FS_SNAPSHOT_HOLD;
flags.bits[FS_FLAGS_FSYS] = QNX6FS_SNAPSHOT_HOLD;
devctl( fd, DCMD_FSYS_FILE_FLAGS, &flags,
sizeof(struct fs_fileflags), NULL);
...
memset( &flags, 0, sizeof(struct fs_fileflags));
flags.mask[FS_FLAGS_FSYS] = QNX6FS_SNAPSHOT_HOLD;
flags.bits[FS_FLAGS_FSYS] = 0;
devctl( fd, DCMD_FSYS_FILE_FLAGS, &flags,
sizeof(struct fs_fileflags), NULL);
# chattr /fs/qnx6
/fs/qnx6: +snapshot +contiguous +used +hold
If +snapshot
isn't displayed, then snapshots have been disabled
via the global flag.
If +hold
is displayed, then snapshots have been disabled due to
a global nonzero hold count (by an unspecified number of clients).
If +dirty
is permanently displayed (even after a
sync()), then either snapshots have been disabled due to a
potentially fatal error, or the disk hardware doesn't support full data
synchronization (track cache flush).