2 * Helper functions for multiple mount protection (MMP).
4 * Copyright (C) 2011 Whamcloud, Inc.
7 * This file may be redistributed under the terms of the GNU Library
8 * General Public License, version 2.
15 #ifndef _DEFAULT_SOURCE
16 #define _DEFAULT_SOURCE /* since glibc 2.20 _SVID_SOURCE is deprecated */
26 #include <sys/types.h>
30 #include "ext2fs/ext2_fs.h"
31 #include "ext2fs/ext2fs.h"
37 #pragma GCC diagnostic push
39 #pragma GCC diagnostic ignored "-Wunused-parameter"
42 errcode_t ext2fs_mmp_read(ext2_filsys fs, blk64_t mmp_blk, void *buf)
45 struct mmp_struct *mmp_cmp;
48 if ((mmp_blk <= fs->super->s_first_data_block) ||
49 (mmp_blk >= ext2fs_blocks_count(fs->super)))
50 return EXT2_ET_MMP_BAD_BLOCK;
52 /* ext2fs_open() reserves fd0,1,2 to avoid stdio collision, so checking
53 * mmp_fd <= 0 is OK to validate that the fd is valid. This opens its
54 * own fd to read the MMP block to ensure that it is using O_DIRECT,
55 * regardless of how the io_manager is doing reads, to avoid caching of
56 * the MMP block by the io_manager or the VM. It needs to be fresh. */
57 if (fs->mmp_fd <= 0) {
58 fs->mmp_fd = open(fs->device_name, O_RDWR | O_DIRECT);
60 retval = EXT2_ET_MMP_OPEN_DIRECT;
65 if (fs->mmp_cmp == NULL) {
66 int align = ext2fs_get_dio_alignment(fs->mmp_fd);
68 retval = ext2fs_get_memalign(fs->blocksize, align,
74 if ((blk64_t) ext2fs_llseek(fs->mmp_fd, mmp_blk * fs->blocksize,
76 mmp_blk * fs->blocksize) {
77 retval = EXT2_ET_LLSEEK_FAILED;
81 if (read(fs->mmp_fd, fs->mmp_cmp, fs->blocksize) != fs->blocksize) {
82 retval = EXT2_ET_SHORT_READ;
86 mmp_cmp = fs->mmp_cmp;
88 if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
89 !ext2fs_mmp_csum_verify(fs, mmp_cmp))
90 retval = EXT2_ET_MMP_CSUM_INVALID;
92 #ifdef WORDS_BIGENDIAN
93 ext2fs_swap_mmp(mmp_cmp);
96 if (buf != NULL && buf != fs->mmp_cmp)
97 memcpy(buf, fs->mmp_cmp, fs->blocksize);
99 if (mmp_cmp->mmp_magic != EXT4_MMP_MAGIC) {
100 retval = EXT2_ET_MMP_MAGIC_INVALID;
107 return EXT2_ET_OP_NOT_SUPPORTED;
111 errcode_t ext2fs_mmp_write(ext2_filsys fs, blk64_t mmp_blk, void *buf)
114 struct mmp_struct *mmp_s = buf;
116 errcode_t retval = 0;
118 gettimeofday(&tv, 0);
119 mmp_s->mmp_time = tv.tv_sec;
120 fs->mmp_last_written = tv.tv_sec;
122 if (fs->super->s_mmp_block < fs->super->s_first_data_block ||
123 fs->super->s_mmp_block > ext2fs_blocks_count(fs->super))
124 return EXT2_ET_MMP_BAD_BLOCK;
126 #ifdef WORDS_BIGENDIAN
127 ext2fs_swap_mmp(mmp_s);
130 retval = ext2fs_mmp_csum_set(fs, mmp_s);
134 /* I was tempted to make this use O_DIRECT and the mmp_fd, but
135 * this caused no end of grief, while leaving it as-is works. */
136 retval = io_channel_write_blk64(fs->io, mmp_blk, -(int)sizeof(struct mmp_struct), buf);
138 #ifdef WORDS_BIGENDIAN
139 ext2fs_swap_mmp(mmp_s);
142 /* Make sure the block gets to disk quickly */
143 io_channel_flush(fs->io);
146 return EXT2_ET_OP_NOT_SUPPORTED;
151 #define srand(x) srandom(x)
152 #define rand() random()
155 unsigned ext2fs_mmp_new_seq(void)
161 gettimeofday(&tv, 0);
162 srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
164 gettimeofday(&tv, 0);
165 /* Crank the random number generator a few times */
166 for (new_seq = (tv.tv_sec ^ tv.tv_usec) & 0x1F; new_seq > 0; new_seq--)
171 } while (new_seq > EXT4_MMP_SEQ_MAX);
175 return EXT2_ET_OP_NOT_SUPPORTED;
180 static errcode_t ext2fs_mmp_reset(ext2_filsys fs)
182 struct mmp_struct *mmp_s = NULL;
183 errcode_t retval = 0;
185 if (fs->mmp_buf == NULL) {
186 retval = ext2fs_get_mem(fs->blocksize, &fs->mmp_buf);
191 memset(fs->mmp_buf, 0, fs->blocksize);
194 mmp_s->mmp_magic = EXT4_MMP_MAGIC;
195 mmp_s->mmp_seq = EXT4_MMP_SEQ_CLEAN;
197 #if _BSD_SOURCE || _XOPEN_SOURCE >= 500
198 gethostname(mmp_s->mmp_nodename, sizeof(mmp_s->mmp_nodename));
200 mmp_s->mmp_nodename[0] = '\0';
202 strncpy(mmp_s->mmp_bdevname, fs->device_name,
203 sizeof(mmp_s->mmp_bdevname));
205 mmp_s->mmp_check_interval = fs->super->s_mmp_update_interval;
206 if (mmp_s->mmp_check_interval < EXT4_MMP_MIN_CHECK_INTERVAL)
207 mmp_s->mmp_check_interval = EXT4_MMP_MIN_CHECK_INTERVAL;
209 retval = ext2fs_mmp_write(fs, fs->super->s_mmp_block, fs->mmp_buf);
215 errcode_t ext2fs_mmp_update(ext2_filsys fs)
217 return ext2fs_mmp_update2(fs, 0);
220 errcode_t ext2fs_mmp_clear(ext2_filsys fs)
223 errcode_t retval = 0;
225 if (!(fs->flags & EXT2_FLAG_RW))
226 return EXT2_ET_RO_FILSYS;
228 retval = ext2fs_mmp_reset(fs);
232 return EXT2_ET_OP_NOT_SUPPORTED;
236 errcode_t ext2fs_mmp_init(ext2_filsys fs)
239 struct ext2_super_block *sb = fs->super;
243 if (sb->s_mmp_update_interval == 0)
244 sb->s_mmp_update_interval = EXT4_MMP_UPDATE_INTERVAL;
245 /* This is probably excessively large, but who knows? */
246 else if (sb->s_mmp_update_interval > EXT4_MMP_MAX_UPDATE_INTERVAL)
247 return EXT2_ET_INVALID_ARGUMENT;
249 if (fs->mmp_buf == NULL) {
250 retval = ext2fs_get_mem(fs->blocksize, &fs->mmp_buf);
255 retval = ext2fs_alloc_block2(fs, 0, fs->mmp_buf, &mmp_block);
259 sb->s_mmp_block = mmp_block;
261 retval = ext2fs_mmp_reset(fs);
268 return EXT2_ET_OP_NOT_SUPPORTED;
273 * Make sure that the fs is not mounted or being fsck'ed while opening the fs.
275 errcode_t ext2fs_mmp_start(ext2_filsys fs)
278 struct mmp_struct *mmp_s;
280 unsigned int mmp_check_interval;
281 errcode_t retval = 0;
283 if (fs->mmp_buf == NULL) {
284 retval = ext2fs_get_mem(fs->blocksize, &fs->mmp_buf);
289 retval = ext2fs_mmp_read(fs, fs->super->s_mmp_block, fs->mmp_buf);
295 mmp_check_interval = fs->super->s_mmp_update_interval;
296 if (mmp_check_interval < EXT4_MMP_MIN_CHECK_INTERVAL)
297 mmp_check_interval = EXT4_MMP_MIN_CHECK_INTERVAL;
299 seq = mmp_s->mmp_seq;
300 if (seq == EXT4_MMP_SEQ_CLEAN)
302 if (seq == EXT4_MMP_SEQ_FSCK) {
303 retval = EXT2_ET_MMP_FSCK_ON;
307 if (seq > EXT4_MMP_SEQ_FSCK) {
308 retval = EXT2_ET_MMP_UNKNOWN_SEQ;
313 * If check_interval in MMP block is larger, use that instead of
314 * check_interval from the superblock.
316 if (mmp_s->mmp_check_interval > mmp_check_interval)
317 mmp_check_interval = mmp_s->mmp_check_interval;
319 sleep(2 * mmp_check_interval + 1);
321 retval = ext2fs_mmp_read(fs, fs->super->s_mmp_block, fs->mmp_buf);
325 if (seq != mmp_s->mmp_seq) {
326 retval = EXT2_ET_MMP_FAILED;
331 if (!(fs->flags & EXT2_FLAG_RW))
334 mmp_s->mmp_seq = seq = ext2fs_mmp_new_seq();
335 #if _BSD_SOURCE || _XOPEN_SOURCE >= 500
336 gethostname(mmp_s->mmp_nodename, sizeof(mmp_s->mmp_nodename));
338 strcpy(mmp_s->mmp_nodename, "unknown host");
340 strncpy(mmp_s->mmp_bdevname, fs->device_name,
341 sizeof(mmp_s->mmp_bdevname));
343 retval = ext2fs_mmp_write(fs, fs->super->s_mmp_block, fs->mmp_buf);
347 sleep(2 * mmp_check_interval + 1);
349 retval = ext2fs_mmp_read(fs, fs->super->s_mmp_block, fs->mmp_buf);
353 if (seq != mmp_s->mmp_seq) {
354 retval = EXT2_ET_MMP_FAILED;
358 mmp_s->mmp_seq = EXT4_MMP_SEQ_FSCK;
359 retval = ext2fs_mmp_write(fs, fs->super->s_mmp_block, fs->mmp_buf);
368 return EXT2_ET_OP_NOT_SUPPORTED;
373 * Clear the MMP usage in the filesystem. If this function returns an
374 * error EXT2_ET_MMP_CHANGE_ABORT it means the filesystem was modified
375 * by some other process while in use, and changes should be dropped, or
376 * risk filesystem corruption.
378 errcode_t ext2fs_mmp_stop(ext2_filsys fs)
381 struct mmp_struct *mmp, *mmp_cmp;
382 errcode_t retval = 0;
384 if (!ext2fs_has_feature_mmp(fs->super) ||
385 !(fs->flags & EXT2_FLAG_RW) || (fs->flags & EXT2_FLAG_SKIP_MMP))
388 retval = ext2fs_mmp_read(fs, fs->super->s_mmp_block, fs->mmp_buf);
392 /* Check if the MMP block is not changed. */
394 mmp_cmp = fs->mmp_cmp;
395 if (memcmp(mmp, mmp_cmp, sizeof(*mmp_cmp))) {
396 retval = EXT2_ET_MMP_CHANGE_ABORT;
400 mmp_cmp->mmp_seq = EXT4_MMP_SEQ_CLEAN;
401 retval = ext2fs_mmp_write(fs, fs->super->s_mmp_block, fs->mmp_cmp);
404 if (fs->mmp_fd > 0) {
411 if (!ext2fs_has_feature_mmp(fs->super) ||
412 !(fs->flags & EXT2_FLAG_RW) || (fs->flags & EXT2_FLAG_SKIP_MMP))
415 return EXT2_ET_OP_NOT_SUPPORTED;
419 #define EXT2_MIN_MMP_UPDATE_INTERVAL 60
422 * Update the on-disk mmp buffer, after checking that it hasn't been changed.
424 errcode_t ext2fs_mmp_update2(ext2_filsys fs, int immediately)
427 struct mmp_struct *mmp, *mmp_cmp;
429 errcode_t retval = 0;
431 if (!ext2fs_has_feature_mmp(fs->super) ||
432 !(fs->flags & EXT2_FLAG_RW) || (fs->flags & EXT2_FLAG_SKIP_MMP))
435 gettimeofday(&tv, 0);
437 tv.tv_sec - fs->mmp_last_written < EXT2_MIN_MMP_UPDATE_INTERVAL)
440 retval = ext2fs_mmp_read(fs, fs->super->s_mmp_block, NULL);
445 mmp_cmp = fs->mmp_cmp;
447 if (memcmp(mmp, mmp_cmp, sizeof(*mmp_cmp)))
448 return EXT2_ET_MMP_CHANGE_ABORT;
450 mmp->mmp_time = tv.tv_sec;
451 mmp->mmp_seq = EXT4_MMP_SEQ_FSCK;
452 retval = ext2fs_mmp_write(fs, fs->super->s_mmp_block, fs->mmp_buf);
457 if (!ext2fs_has_feature_mmp(fs->super) ||
458 !(fs->flags & EXT2_FLAG_RW) || (fs->flags & EXT2_FLAG_SKIP_MMP))
461 return EXT2_ET_OP_NOT_SUPPORTED;
464 #pragma GCC diagnostic pop