#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE /* since glibc 2.20 _SVID_SOURCE is deprecated */
+#endif
#include "config.h"
#define O_DIRECT 0
#endif
+#if __GNUC_PREREQ (4, 6)
+#pragma GCC diagnostic push
+#ifndef CONFIG_MMP
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#endif
+#endif
+
errcode_t ext2fs_mmp_read(ext2_filsys fs, blk64_t mmp_blk, void *buf)
{
+#ifdef CONFIG_MMP
struct mmp_struct *mmp_cmp;
errcode_t retval = 0;
* regardless of how the io_manager is doing reads, to avoid caching of
* the MMP block by the io_manager or the VM. It needs to be fresh. */
if (fs->mmp_fd <= 0) {
- fs->mmp_fd = open(fs->device_name, O_RDWR | O_DIRECT);
+ int flags = O_RDWR | O_DIRECT;
+
+retry:
+ fs->mmp_fd = open(fs->device_name, flags);
if (fs->mmp_fd < 0) {
+ struct stat st;
+
+ /* Avoid O_DIRECT for filesystem image files if open
+ * fails, since it breaks when running on tmpfs. */
+ if (errno == EINVAL && (flags & O_DIRECT) &&
+ stat(fs->device_name, &st) == 0 &&
+ S_ISREG(st.st_mode)) {
+ flags &= ~O_DIRECT;
+ goto retry;
+ }
retval = EXT2_ET_MMP_OPEN_DIRECT;
goto out;
}
}
mmp_cmp = fs->mmp_cmp;
+
+ if (!(fs->flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) &&
+ !ext2fs_mmp_csum_verify(fs, mmp_cmp))
+ retval = EXT2_ET_MMP_CSUM_INVALID;
+
#ifdef WORDS_BIGENDIAN
ext2fs_swap_mmp(mmp_cmp);
#endif
out:
return retval;
+#else
+ return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
}
errcode_t ext2fs_mmp_write(ext2_filsys fs, blk64_t mmp_blk, void *buf)
{
+#ifdef CONFIG_MMP
struct mmp_struct *mmp_s = buf;
struct timeval tv;
errcode_t retval = 0;
ext2fs_swap_mmp(mmp_s);
#endif
+ retval = ext2fs_mmp_csum_set(fs, mmp_s);
+ if (retval)
+ return retval;
+
/* I was tempted to make this use O_DIRECT and the mmp_fd, but
* this caused no end of grief, while leaving it as-is works. */
retval = io_channel_write_blk64(fs->io, mmp_blk, -(int)sizeof(struct mmp_struct), buf);
/* Make sure the block gets to disk quickly */
io_channel_flush(fs->io);
return retval;
+#else
+ return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
}
#ifdef HAVE_SRANDOM
#define rand() random()
#endif
-unsigned ext2fs_mmp_new_seq()
+unsigned ext2fs_mmp_new_seq(void)
{
+#ifdef CONFIG_MMP
unsigned new_seq;
struct timeval tv;
} while (new_seq > EXT4_MMP_SEQ_MAX);
return new_seq;
+#else
+ return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
}
+#ifdef CONFIG_MMP
static errcode_t ext2fs_mmp_reset(ext2_filsys fs)
{
struct mmp_struct *mmp_s = NULL;
mmp_s->mmp_magic = EXT4_MMP_MAGIC;
mmp_s->mmp_seq = EXT4_MMP_SEQ_CLEAN;
mmp_s->mmp_time = 0;
-#if _BSD_SOURCE || _XOPEN_SOURCE >= 500
+#ifdef HAVE_GETHOSTNAME
gethostname(mmp_s->mmp_nodename, sizeof(mmp_s->mmp_nodename));
#else
mmp_s->mmp_nodename[0] = '\0';
out:
return retval;
}
+#endif
+
+errcode_t ext2fs_mmp_update(ext2_filsys fs)
+{
+ return ext2fs_mmp_update2(fs, 0);
+}
errcode_t ext2fs_mmp_clear(ext2_filsys fs)
{
+#ifdef CONFIG_MMP
errcode_t retval = 0;
if (!(fs->flags & EXT2_FLAG_RW))
retval = ext2fs_mmp_reset(fs);
return retval;
+#else
+ return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
}
errcode_t ext2fs_mmp_init(ext2_filsys fs)
{
+#ifdef CONFIG_MMP
struct ext2_super_block *sb = fs->super;
blk64_t mmp_block;
errcode_t retval;
out:
return retval;
+#else
+ return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
}
+#ifndef min
+#define min(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
/*
* Make sure that the fs is not mounted or being fsck'ed while opening the fs.
*/
errcode_t ext2fs_mmp_start(ext2_filsys fs)
{
+#ifdef CONFIG_MMP
struct mmp_struct *mmp_s;
unsigned seq;
unsigned int mmp_check_interval;
if (mmp_s->mmp_check_interval > mmp_check_interval)
mmp_check_interval = mmp_s->mmp_check_interval;
- sleep(2 * mmp_check_interval + 1);
+ sleep(min(mmp_check_interval * 2 + 1, mmp_check_interval + 60));
retval = ext2fs_mmp_read(fs, fs->super->s_mmp_block, fs->mmp_buf);
if (retval)
goto mmp_error;
mmp_s->mmp_seq = seq = ext2fs_mmp_new_seq();
-#if _BSD_SOURCE || _XOPEN_SOURCE >= 500
+#ifdef HAVE_GETHOSTNAME
gethostname(mmp_s->mmp_nodename, sizeof(mmp_s->mmp_nodename));
#else
strcpy(mmp_s->mmp_nodename, "unknown host");
if (retval)
goto mmp_error;
- sleep(2 * mmp_check_interval + 1);
+ sleep(min(2 * mmp_check_interval + 1, mmp_check_interval + 60));
retval = ext2fs_mmp_read(fs, fs->super->s_mmp_block, fs->mmp_buf);
if (retval)
mmp_error:
return retval;
+#else
+ return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
}
/*
*/
errcode_t ext2fs_mmp_stop(ext2_filsys fs)
{
+#ifdef CONFIG_MMP
struct mmp_struct *mmp, *mmp_cmp;
errcode_t retval = 0;
- if (!(fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_MMP) ||
+ if (!ext2fs_has_feature_mmp(fs->super) ||
!(fs->flags & EXT2_FLAG_RW) || (fs->flags & EXT2_FLAG_SKIP_MMP))
goto mmp_error;
}
return retval;
+#else
+ if (!ext2fs_has_feature_mmp(fs->super) ||
+ !(fs->flags & EXT2_FLAG_RW) || (fs->flags & EXT2_FLAG_SKIP_MMP))
+ return 0;
+
+ return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
}
#define EXT2_MIN_MMP_UPDATE_INTERVAL 60
/*
* Update the on-disk mmp buffer, after checking that it hasn't been changed.
*/
-errcode_t ext2fs_mmp_update(ext2_filsys fs)
+errcode_t ext2fs_mmp_update2(ext2_filsys fs, int immediately)
{
+#ifdef CONFIG_MMP
struct mmp_struct *mmp, *mmp_cmp;
struct timeval tv;
errcode_t retval = 0;
- if (!(fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_MMP) ||
+ if (!ext2fs_has_feature_mmp(fs->super) ||
!(fs->flags & EXT2_FLAG_RW) || (fs->flags & EXT2_FLAG_SKIP_MMP))
return 0;
gettimeofday(&tv, 0);
- if (tv.tv_sec - fs->mmp_last_written < EXT2_MIN_MMP_UPDATE_INTERVAL)
+ if (!immediately &&
+ tv.tv_sec - fs->mmp_last_written < EXT2_MIN_MMP_UPDATE_INTERVAL)
return 0;
retval = ext2fs_mmp_read(fs, fs->super->s_mmp_block, NULL);
mmp_error:
return retval;
+#else
+ if (!ext2fs_has_feature_mmp(fs->super) ||
+ !(fs->flags & EXT2_FLAG_RW) || (fs->flags & EXT2_FLAG_SKIP_MMP))
+ return 0;
+
+ return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
}
+#if __GNUC_PREREQ (4, 6)
+#pragma GCC diagnostic pop
+#endif