staticint ocfs2_init_global_system_inodes(struct ocfs2_super *osb)
{ struct inode *new = NULL; int status = 0; int i;
new = ocfs2_iget(osb, osb->root_blkno, OCFS2_FI_FLAG_SYSFILE, 0); if (IS_ERR(new)) {
status = PTR_ERR(new);
mlog_errno(status); goto bail;
}
osb->root_inode = new;
new = ocfs2_iget(osb, osb->system_dir_blkno, OCFS2_FI_FLAG_SYSFILE, 0); if (IS_ERR(new)) {
status = PTR_ERR(new);
mlog_errno(status); goto bail;
}
osb->sys_root_inode = new;
for (i = OCFS2_FIRST_ONLINE_SYSTEM_INODE;
i <= OCFS2_LAST_GLOBAL_SYSTEM_INODE; i++) { if (!ocfs2_need_system_inode(osb, i)) continue; new = ocfs2_get_system_file_inode(osb, i, osb->slot_num); if (!new) {
ocfs2_release_system_inodes(osb);
status = ocfs2_is_soft_readonly(osb) ? -EROFS : -EINVAL;
mlog_errno(status);
mlog(ML_ERROR, "Unable to load system inode %d, " "possibly corrupt fs?", i); goto bail;
} // the array now has one ref, so drop this one
iput(new);
}
bail: if (status)
mlog_errno(status); return status;
}
staticint ocfs2_init_local_system_inodes(struct ocfs2_super *osb)
{ struct inode *new = NULL; int status = 0; int i;
for (i = OCFS2_LAST_GLOBAL_SYSTEM_INODE + 1;
i < NUM_SYSTEM_INODES;
i++) { if (!ocfs2_need_system_inode(osb, i)) continue; new = ocfs2_get_system_file_inode(osb, i, osb->slot_num); if (!new) {
ocfs2_release_system_inodes(osb);
status = ocfs2_is_soft_readonly(osb) ? -EROFS : -EINVAL;
mlog(ML_ERROR, "status=%d, sysfile=%d, slot=%d\n",
status, i, osb->slot_num); goto bail;
} /* the array now has one ref, so drop this one */
iput(new);
}
bail: if (status)
mlog_errno(status); return status;
}
staticvoid ocfs2_release_system_inodes(struct ocfs2_super *osb)
{ int i; struct inode *inode;
for (i = 0; i < NUM_GLOBAL_SYSTEM_INODES; i++) {
inode = osb->global_system_inodes[i]; if (inode) {
iput(inode);
osb->global_system_inodes[i] = NULL;
}
}
for (i = 0; i < NUM_LOCAL_SYSTEM_INODES * osb->max_slots; i++) { if (osb->local_system_inodes[i]) {
iput(osb->local_system_inodes[i]);
osb->local_system_inodes[i] = NULL;
}
}
/* * i_size and all block offsets in ocfs2 are always 64 bits * wide. i_clusters is 32 bits, in cluster-sized units. So on * 64 bit platforms, cluster size will be the limiting factor.
*/
#if BITS_PER_LONG == 32
BUILD_BUG_ON(sizeof(sector_t) != 8); /* * We might be limited by page cache size.
*/ if (bytes > PAGE_SIZE) {
bytes = PAGE_SIZE;
trim = 1; /* * Shift by 31 here so that we don't get larger than * MAX_LFS_FILESIZE
*/
bitshift = 31;
} #endif
/* * Trim by a whole cluster when we can actually approach the * on-disk limits. Otherwise we can overflow i_clusters when * an extent start is at the max offset.
*/ return (((unsignedlonglong)bytes) << bitshift) - trim;
}
staticint ocfs2_reconfigure(struct fs_context *fc)
{ int incompat_features; int ret = 0; struct mount_options *parsed_options = fc->fs_private; struct super_block *sb = fc->root->d_sb; struct ocfs2_super *osb = OCFS2_SB(sb);
u32 tmp;
sync_filesystem(sb);
if (!ocfs2_check_set_options(sb, parsed_options)) {
ret = -EINVAL; goto out;
}
tmp = OCFS2_MOUNT_HB_LOCAL | OCFS2_MOUNT_HB_GLOBAL |
OCFS2_MOUNT_HB_NONE; if ((osb->s_mount_opt & tmp) != (parsed_options->mount_opt & tmp)) {
ret = -EINVAL;
mlog(ML_ERROR, "Cannot change heartbeat mode on remount\n"); goto out;
}
if ((osb->s_mount_opt & OCFS2_MOUNT_DATA_WRITEBACK) !=
(parsed_options->mount_opt & OCFS2_MOUNT_DATA_WRITEBACK)) {
ret = -EINVAL;
mlog(ML_ERROR, "Cannot change data mode on remount\n"); goto out;
}
/* Probably don't want this on remount; it might
* mess with other nodes */ if (!(osb->s_mount_opt & OCFS2_MOUNT_INODE64) &&
(parsed_options->mount_opt & OCFS2_MOUNT_INODE64)) {
ret = -EINVAL;
mlog(ML_ERROR, "Cannot enable inode64 on remount\n"); goto out;
}
/* We're going to/from readonly mode. */ if ((bool)(fc->sb_flags & SB_RDONLY) != sb_rdonly(sb)) { /* Disable quota accounting before remounting RO */ if (fc->sb_flags & SB_RDONLY) {
ret = ocfs2_susp_quotas(osb, 0); if (ret < 0) goto out;
} /* Lock here so the check of HARD_RO and the potential
* setting of SOFT_RO is atomic. */
spin_lock(&osb->osb_lock); if (osb->osb_flags & OCFS2_OSB_HARD_RO) {
mlog(ML_ERROR, "Remount on readonly device is forbidden.\n");
ret = -EROFS; goto unlock_osb;
}
if (fc->sb_flags & SB_RDONLY) {
sb->s_flags |= SB_RDONLY;
osb->osb_flags |= OCFS2_OSB_SOFT_RO;
} else { if (osb->osb_flags & OCFS2_OSB_ERROR_FS) {
mlog(ML_ERROR, "Cannot remount RDWR " "filesystem due to previous errors.\n");
ret = -EROFS; goto unlock_osb;
}
incompat_features = OCFS2_HAS_RO_COMPAT_FEATURE(sb, ~OCFS2_FEATURE_RO_COMPAT_SUPP); if (incompat_features) {
mlog(ML_ERROR, "Cannot remount RDWR because " "of unsupported optional features " "(%x).\n", incompat_features);
ret = -EINVAL; goto unlock_osb;
}
sb->s_flags &= ~SB_RDONLY;
osb->osb_flags &= ~OCFS2_OSB_SOFT_RO;
}
trace_ocfs2_remount(sb->s_flags, osb->osb_flags, fc->sb_flags);
unlock_osb:
spin_unlock(&osb->osb_lock); /* Enable quota accounting after remounting RW */ if (!ret && !(fc->sb_flags & SB_RDONLY)) { if (sb_any_quota_suspended(sb))
ret = ocfs2_susp_quotas(osb, 1); else
ret = ocfs2_enable_quotas(osb); if (ret < 0) { /* Return back changes... */
spin_lock(&osb->osb_lock);
sb->s_flags |= SB_RDONLY;
osb->osb_flags |= OCFS2_OSB_SOFT_RO;
spin_unlock(&osb->osb_lock); goto out;
}
}
}
if (!ret) { /* Only save off the new mount options in case of a successful
* remount. */
osb->s_mount_opt = parsed_options->mount_opt;
osb->s_atime_quantum = parsed_options->atime_quantum;
osb->preferred_slot = parsed_options->slot; if (parsed_options->commit_interval)
osb->osb_commit_interval = parsed_options->commit_interval;
if (!ocfs2_is_hard_readonly(osb))
ocfs2_set_journal_params(osb);
staticint ocfs2_sb_probe(struct super_block *sb, struct buffer_head **bh, int *sector_size, struct ocfs2_blockcheck_stats *stats)
{ int status, tmpstat; struct ocfs1_vol_disk_hdr *hdr; struct ocfs2_dinode *di; int blksize;
*bh = NULL;
/* may be > 512 */
*sector_size = bdev_logical_block_size(sb->s_bdev); if (*sector_size > OCFS2_MAX_BLOCKSIZE) {
mlog(ML_ERROR, "Hardware sector size too large: %d (max=%d)\n",
*sector_size, OCFS2_MAX_BLOCKSIZE);
status = -EINVAL; goto bail;
}
/* Can this really happen? */ if (*sector_size < OCFS2_MIN_BLOCKSIZE)
*sector_size = OCFS2_MIN_BLOCKSIZE;
/* check block zero for old format */
status = ocfs2_get_sector(sb, bh, 0, *sector_size); if (status < 0) {
mlog_errno(status); goto bail;
}
hdr = (struct ocfs1_vol_disk_hdr *) (*bh)->b_data; if (hdr->major_version == OCFS1_MAJOR_VERSION) {
mlog(ML_ERROR, "incompatible version: %u.%u\n",
hdr->major_version, hdr->minor_version);
status = -EINVAL;
} if (memcmp(hdr->signature, OCFS1_VOLUME_SIGNATURE,
strlen(OCFS1_VOLUME_SIGNATURE)) == 0) {
mlog(ML_ERROR, "incompatible volume signature: %8s\n",
hdr->signature);
status = -EINVAL;
}
brelse(*bh);
*bh = NULL; if (status < 0) {
mlog(ML_ERROR, "This is an ocfs v1 filesystem which must be " "upgraded before mounting with ocfs v2\n"); goto bail;
}
/* * Now check at magic offset for 512, 1024, 2048, 4096 * blocksizes. 4096 is the maximum blocksize because it is * the minimum clustersize.
*/
status = -EINVAL; for (blksize = *sector_size;
blksize <= OCFS2_MAX_BLOCKSIZE;
blksize <<= 1) {
tmpstat = ocfs2_get_sector(sb, bh,
OCFS2_SUPER_BLOCK_BLKNO,
blksize); if (tmpstat < 0) {
status = tmpstat;
mlog_errno(status); break;
}
di = (struct ocfs2_dinode *) (*bh)->b_data;
memset(stats, 0, sizeof(struct ocfs2_blockcheck_stats));
spin_lock_init(&stats->b_lock);
tmpstat = ocfs2_verify_volume(di, *bh, blksize, stats); if (tmpstat < 0) {
brelse(*bh);
*bh = NULL;
} if (tmpstat != -EAGAIN) {
status = tmpstat; break;
}
}
if (osb->s_mount_opt & hb_enabled) { if (ocfs2_mount_local(osb)) {
mlog(ML_ERROR, "Cannot heartbeat on a locally " "mounted device.\n"); return -EINVAL;
} if (ocfs2_userspace_stack(osb)) {
mlog(ML_ERROR, "Userspace stack expected, but " "o2cb heartbeat arguments passed to mount\n"); return -EINVAL;
} if (((osb->s_mount_opt & OCFS2_MOUNT_HB_GLOBAL) &&
!ocfs2_cluster_o2cb_global_heartbeat(osb)) ||
((osb->s_mount_opt & OCFS2_MOUNT_HB_LOCAL) &&
ocfs2_cluster_o2cb_global_heartbeat(osb))) {
mlog(ML_ERROR, "Mismatching o2cb heartbeat modes\n"); return -EINVAL;
}
}
if (!(osb->s_mount_opt & hb_enabled)) { if (!ocfs2_mount_local(osb) && !ocfs2_is_hard_readonly(osb) &&
!ocfs2_userspace_stack(osb)) {
mlog(ML_ERROR, "Heartbeat has to be started to mount " "a read-write clustered device.\n"); return -EINVAL;
}
}
return 0;
}
/* * If we're using a userspace stack, mount should have passed * a name that matches the disk. If not, mount should not * have passed a stack.
*/ staticint ocfs2_verify_userspace_stack(struct ocfs2_super *osb, struct mount_options *mopt)
{ if (!ocfs2_userspace_stack(osb) && mopt->cluster_stack[0]) {
mlog(ML_ERROR, "cluster stack passed to mount, but this filesystem " "does not support it\n"); return -EINVAL;
}
if (ocfs2_userspace_stack(osb) &&
strncmp(osb->osb_cluster_stack, mopt->cluster_stack,
OCFS2_STACK_LABEL_LEN)) {
mlog(ML_ERROR, "cluster stack passed to mount (\"%s\") does not " "match the filesystem (\"%s\")\n",
mopt->cluster_stack,
osb->osb_cluster_stack); return -EINVAL;
}
return 0;
}
staticint ocfs2_susp_quotas(struct ocfs2_super *osb, int unsuspend)
{ int type; struct super_block *sb = osb->sb; unsignedint feature[OCFS2_MAXQUOTAS] = {
OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
OCFS2_FEATURE_RO_COMPAT_GRPQUOTA}; int status = 0;
for (type = 0; type < OCFS2_MAXQUOTAS; type++) { if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type])) continue; if (unsuspend)
status = dquot_resume(sb, type); else { struct ocfs2_mem_dqinfo *oinfo;
/* Cancel periodic syncing before suspending */
oinfo = sb_dqinfo(sb, type)->dqi_priv;
cancel_delayed_work_sync(&oinfo->dqi_sync_work);
status = dquot_suspend(sb, type);
} if (status < 0) break;
} if (status < 0)
mlog(ML_ERROR, "Failed to suspend/unsuspend quotas on " "remount (error = %d).\n", status); return status;
}
/* We mostly ignore errors in this function because there's not much
* we can do when we see them */ for (type = 0; type < OCFS2_MAXQUOTAS; type++) { if (!sb_has_quota_loaded(sb, type)) continue; if (!sb_has_quota_suspended(sb, type)) {
oinfo = sb_dqinfo(sb, type)->dqi_priv;
cancel_delayed_work_sync(&oinfo->dqi_sync_work);
}
inode = igrab(sb->s_dquot.files[type]); /* Turn off quotas. This will remove all dquot structures from * memory and so they will be automatically synced to global
* quota files */
dquot_disable(sb, type, DQUOT_USAGE_ENABLED |
DQUOT_LIMITS_ENABLED);
iput(inode);
}
}
/* Hard readonly mode only if: bdev_read_only, SB_RDONLY,
* heartbeat=none */ if (bdev_read_only(sb->s_bdev)) { if (!sb_rdonly(sb)) {
status = -EACCES;
mlog(ML_ERROR, "Readonly device detected but readonly " "mount was not specified.\n"); goto out_super;
}
/* You should not be able to start a local heartbeat
* on a readonly device. */ if (osb->s_mount_opt & OCFS2_MOUNT_HB_LOCAL) {
status = -EROFS;
mlog(ML_ERROR, "Local heartbeat specified on readonly " "device.\n"); goto out_super;
}
status = ocfs2_check_journals_nolocks(osb); if (status < 0) { if (status == -EROFS)
mlog(ML_ERROR, "Recovery required on readonly " "file system, but write access is " "unavailable.\n"); goto out_super;
}
ocfs2_set_ro_flag(osb, 1);
printk(KERN_NOTICE "ocfs2: Readonly device (%s) detected. " "Cluster services will not be used for this mount. " "Recovery will be skipped.\n", osb->dev_str);
}
if (!ocfs2_is_hard_readonly(osb)) { if (sb_rdonly(sb))
ocfs2_set_ro_flag(osb, 0);
}
status = ocfs2_verify_heartbeat(osb); if (status < 0) goto out_super;
if (ocfs2_meta_ecc(osb)) {
ocfs2_initialize_journal_triggers(sb, osb->s_journal_triggers);
ocfs2_blockcheck_stats_debugfs_install( &osb->osb_ecc_stats,
osb->osb_debug_root);
}
status = ocfs2_mount_volume(sb); if (status < 0) goto out_debugfs;
if (osb->root_inode)
inode = igrab(osb->root_inode);
if (!inode) {
status = -EIO; goto out_dismount;
}
osb->osb_dev_kset = kset_create_and_add(sb->s_id, NULL,
&ocfs2_kset->kobj); if (!osb->osb_dev_kset) {
status = -ENOMEM;
mlog(ML_ERROR, "Unable to create device kset %s.\n", sb->s_id); goto out_dismount;
}
/* Create filecheck sysfs related directories/files at
* /sys/fs/ocfs2/<devname>/filecheck */ if (ocfs2_filecheck_create_sysfs(osb)) {
status = -ENOMEM;
mlog(ML_ERROR, "Unable to create filecheck sysfs directory at " "/sys/fs/ocfs2/%s/filecheck.\n", sb->s_id); goto out_dismount;
}
root = d_make_root(inode); if (!root) {
status = -ENOMEM; goto out_dismount;
}
sb->s_root = root;
ocfs2_complete_mount_recovery(osb);
if (ocfs2_mount_local(osb))
snprintf(nodestr, sizeof(nodestr), "local"); else
snprintf(nodestr, sizeof(nodestr), "%u", osb->node_num);
/* Now we can initialize quotas because we can afford to wait * for cluster locks recovery now. That also means that truncation
* log recovery can happen but that waits for proper quota setup */ if (!sb_rdonly(sb)) {
status = ocfs2_enable_quotas(osb); if (status < 0) { /* We have to err-out specially here because
* s_root is already set */
mlog_errno(status);
atomic_set(&osb->vol_state, VOLUME_DISABLED);
wake_up(&osb->osb_mount_event); return status;
}
}
ocfs2_complete_quota_recovery(osb);
/* Now we wake up again for processes waiting for quotas */
atomic_set(&osb->vol_state, VOLUME_MOUNTED_QUOTAS);
wake_up(&osb->osb_mount_event);
/* Start this when the mount is almost sure of being successful */
ocfs2_orphan_scan_start(osb);
/* Ensure only one heartbeat mode */
tmp = options->mount_opt & (OCFS2_MOUNT_HB_LOCAL |
OCFS2_MOUNT_HB_GLOBAL |
OCFS2_MOUNT_HB_NONE); if (hweight32(tmp) != 1) {
mlog(ML_ERROR, "Invalid heartbeat mount options\n"); return 0;
}
} if (options->mount_opt & OCFS2_MOUNT_USRQUOTA &&
!OCFS2_HAS_RO_COMPAT_FEATURE(sb,
OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) {
mlog(ML_ERROR, "User quotas were requested, but this " "filesystem does not have the feature enabled.\n"); return 0;
} if (options->mount_opt & OCFS2_MOUNT_GRPQUOTA &&
!OCFS2_HAS_RO_COMPAT_FEATURE(sb,
OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) {
mlog(ML_ERROR, "Group quotas were requested, but this " "filesystem does not have the feature enabled.\n"); return 0;
} if (options->mount_opt & OCFS2_MOUNT_POSIX_ACL &&
!OCFS2_HAS_INCOMPAT_FEATURE(sb, OCFS2_FEATURE_INCOMPAT_XATTR)) {
mlog(ML_ERROR, "ACL support requested but extended attributes " "feature is not enabled\n"); return 0;
} /* No ACL setting specified? Use XATTR feature... */ if (!(options->mount_opt & (OCFS2_MOUNT_POSIX_ACL |
OCFS2_MOUNT_NO_POSIX_ACL))) { if (OCFS2_HAS_INCOMPAT_FEATURE(sb, OCFS2_FEATURE_INCOMPAT_XATTR))
options->mount_opt |= OCFS2_MOUNT_POSIX_ACL; else
options->mount_opt |= OCFS2_MOUNT_NO_POSIX_ACL;
} return 1;
}
switch (opt) { case Opt_heartbeat:
mopt->mount_opt |= result.uint_32; break; case Opt_barrier: if (result.uint_32)
mopt->mount_opt |= OCFS2_MOUNT_BARRIER; else
mopt->mount_opt &= ~OCFS2_MOUNT_BARRIER; break; case Opt_intr: if (result.negated)
mopt->mount_opt |= OCFS2_MOUNT_NOINTR; else
mopt->mount_opt &= ~OCFS2_MOUNT_NOINTR; break; case Opt_errors:
mopt->mount_opt &= ~(OCFS2_MOUNT_ERRORS_CONT |
OCFS2_MOUNT_ERRORS_ROFS |
OCFS2_MOUNT_ERRORS_PANIC);
mopt->mount_opt |= result.uint_32; break; case Opt_data:
mopt->mount_opt &= ~OCFS2_MOUNT_DATA_WRITEBACK;
mopt->mount_opt |= result.uint_32; break; case Opt_user_xattr: if (result.negated)
mopt->mount_opt |= OCFS2_MOUNT_NOUSERXATTR; else
mopt->mount_opt &= ~OCFS2_MOUNT_NOUSERXATTR; break; case Opt_atime_quantum:
mopt->atime_quantum = result.uint_32; break; case Opt_slot: if (result.uint_32)
mopt->slot = (u16)result.uint_32; break; case Opt_commit: if (result.uint_32 == 0)
mopt->commit_interval = HZ * JBD2_DEFAULT_MAX_COMMIT_AGE; else
mopt->commit_interval = HZ * result.uint_32; break; case Opt_localalloc: if (result.int_32 >= 0)
mopt->localalloc_opt = result.int_32; break; case Opt_localflocks: /* * Changing this during remount could race flock() requests, or * "unbalance" existing ones (e.g., a lock is taken in one mode * but dropped in the other). If users care enough to flip * locking modes during remount, we could add a "local" flag to * individual flock structures for proper tracking of state.
*/ if (!is_remount)
mopt->mount_opt |= OCFS2_MOUNT_LOCALFLOCKS; break; case Opt_stack: /* Check both that the option we were passed is of the right * length and that it is a proper string of the right length.
*/ if (strlen(param->string) != OCFS2_STACK_LABEL_LEN) {
mlog(ML_ERROR, "Invalid cluster_stack option\n"); return -EINVAL;
}
memcpy(mopt->cluster_stack, param->string, OCFS2_STACK_LABEL_LEN);
mopt->cluster_stack[OCFS2_STACK_LABEL_LEN] = '\0'; /* * Open code the memcmp here as we don't have an osb to pass * to ocfs2_userspace_stack().
*/ if (memcmp(mopt->cluster_stack,
OCFS2_CLASSIC_CLUSTER_STACK,
OCFS2_STACK_LABEL_LEN))
mopt->user_stack = 1; break; case Opt_inode64:
mopt->mount_opt |= OCFS2_MOUNT_INODE64; break; case Opt_usrquota:
mopt->mount_opt |= OCFS2_MOUNT_USRQUOTA; break; case Opt_grpquota:
mopt->mount_opt |= OCFS2_MOUNT_GRPQUOTA; break; case Opt_coherency:
mopt->mount_opt &= ~OCFS2_MOUNT_COHERENCY_BUFFERED;
mopt->mount_opt |= result.uint_32; break; case Opt_acl: if (result.negated) {
mopt->mount_opt |= OCFS2_MOUNT_NO_POSIX_ACL;
mopt->mount_opt &= ~OCFS2_MOUNT_POSIX_ACL;
} else {
mopt->mount_opt |= OCFS2_MOUNT_POSIX_ACL;
mopt->mount_opt &= ~OCFS2_MOUNT_NO_POSIX_ACL;
} break; case Opt_resv_level: if (is_remount) break; if (result.uint_32 >= OCFS2_MIN_RESV_LEVEL &&
result.uint_32 < OCFS2_MAX_RESV_LEVEL)
mopt->resv_level = result.uint_32; break; case Opt_dir_resv_level: if (is_remount) break; if (result.uint_32 >= OCFS2_MIN_RESV_LEVEL &&
result.uint_32 < OCFS2_MAX_RESV_LEVEL)
mopt->dir_resv_level = result.uint_32; break; case Opt_journal_async_commit:
mopt->mount_opt |= OCFS2_MOUNT_JOURNAL_ASYNC_COMMIT; break; default: return -EINVAL;
}
inode = ocfs2_get_system_file_inode(osb,
GLOBAL_BITMAP_SYSTEM_INODE,
OCFS2_INVALID_SLOT); if (!inode) {
mlog(ML_ERROR, "failed to get bitmap inode\n");
status = -EIO; goto bail;
}
status = ocfs2_inode_lock(inode, &bh, 0); if (status < 0) {
mlog_errno(status); goto bail;
}
staticvoid ocfs2_free_mem_caches(void)
{ /* * Make sure all delayed rcu free inodes are flushed before we * destroy cache.
*/
rcu_barrier();
kmem_cache_destroy(ocfs2_inode_cachep);
ocfs2_inode_cachep = NULL;
staticint ocfs2_get_sector(struct super_block *sb, struct buffer_head **bh, int block, int sect_size)
{ if (!sb_set_blocksize(sb, sect_size)) {
mlog(ML_ERROR, "unable to set blocksize\n"); return -EIO;
}
*bh = sb_getblk(sb, block); if (!*bh) {
mlog_errno(-ENOMEM); return -ENOMEM;
}
lock_buffer(*bh); if (!buffer_dirty(*bh))
clear_buffer_uptodate(*bh);
unlock_buffer(*bh); if (bh_read(*bh, 0) < 0) {
mlog_errno(-EIO);
brelse(*bh);
*bh = NULL; return -EIO;
}
return 0;
}
staticint ocfs2_mount_volume(struct super_block *sb)
{ int status = 0; struct ocfs2_super *osb = OCFS2_SB(sb);
if (ocfs2_is_hard_readonly(osb)) goto out;
mutex_init(&osb->obs_trim_fs_mutex);
status = ocfs2_dlm_init(osb); if (status < 0) {
mlog_errno(status); if (status == -EBADR && ocfs2_userspace_stack(osb))
mlog(ML_ERROR, "couldn't mount because cluster name on" " disk does not match the running cluster name.\n"); goto out;
}
status = ocfs2_super_lock(osb, 1); if (status < 0) {
mlog_errno(status); goto out_dlm;
}
/* This will load up the node map and add ourselves to it. */
status = ocfs2_find_slot(osb); if (status < 0) {
mlog_errno(status); goto out_super_lock;
}
/* load all node-local system inodes */
status = ocfs2_init_local_system_inodes(osb); if (status < 0) {
mlog_errno(status); goto out_super_lock;
}
status = ocfs2_check_volume(osb); if (status < 0) {
mlog_errno(status); goto out_system_inodes;
}
status = ocfs2_truncate_log_init(osb); if (status < 0) {
mlog_errno(status); goto out_check_volume;
}
ocfs2_super_unlock(osb, 1); return 0;
out_check_volume:
ocfs2_free_replay_slots(osb);
out_system_inodes: if (osb->local_alloc_state == OCFS2_LA_ENABLED)
ocfs2_shutdown_local_alloc(osb);
ocfs2_release_system_inodes(osb); /* before journal shutdown, we should release slot_info */
ocfs2_free_slot_info(osb);
ocfs2_journal_shutdown(osb);
out_super_lock:
ocfs2_super_unlock(osb, 1);
out_dlm:
ocfs2_dlm_shutdown(osb, 0);
out: return status;
}
staticvoid ocfs2_dismount_volume(struct super_block *sb, int mnt_err)
{ int tmp, hangup_needed = 0; struct ocfs2_super *osb = NULL; char nodestr[12];
trace_ocfs2_dismount_volume(sb);
BUG_ON(!sb);
osb = OCFS2_SB(sb);
BUG_ON(!osb);
/* Remove file check sysfs related directories/files,
* and wait for the pending file check operations */
ocfs2_filecheck_remove_sysfs(osb);
kset_unregister(osb->osb_dev_kset);
/* Orphan scan should be stopped as early as possible */
ocfs2_orphan_scan_stop(osb);
/* Stop quota recovery so that we can disable quotas */
ocfs2_recovery_disable_quota(osb);
ocfs2_disable_quotas(osb);
/* All dquots should be freed by now */
WARN_ON(!llist_empty(&osb->dquot_drop_list)); /* Wait for worker to be done with the work structure in osb */
cancel_work_sync(&osb->dquot_drop_work);
ocfs2_shutdown_local_alloc(osb);
ocfs2_truncate_log_shutdown(osb);
/* This will disable recovery and flush any recovery work. */
ocfs2_recovery_exit(osb);
ocfs2_sync_blockdev(sb);
ocfs2_purge_refcount_trees(osb);
/* No cluster connection means we've failed during mount, so skip
* all the steps which depended on that to complete. */ if (osb->cconn) {
tmp = ocfs2_super_lock(osb, 1); if (tmp < 0) {
mlog_errno(tmp); return;
}
}
if (osb->slot_num != OCFS2_INVALID_SLOT)
ocfs2_put_slot(osb);
if (osb->cconn)
ocfs2_super_unlock(osb, 1);
ocfs2_release_system_inodes(osb);
ocfs2_journal_shutdown(osb);
/* * If we're dismounting due to mount error, mount.ocfs2 will clean * up heartbeat. If we're a local mount, there is no heartbeat. * If we failed before we got a uuid_str yet, we can't stop * heartbeat. Otherwise, do it.
*/ if (!mnt_err && !ocfs2_mount_local(osb) && osb->uuid_str &&
!ocfs2_is_hard_readonly(osb))
hangup_needed = 1;
for (i = 0, ptr = osb->uuid_str; i < OCFS2_VOL_UUID_LEN; i++) { /* print with null */
ret = snprintf(ptr, 3, "%02X", uuid[i]); if (ret != 2) /* drop super cleans up */ return -EINVAL; /* then only advance past the last char */
ptr += 2;
}
return 0;
}
/* Make sure entire volume is addressable by our journal. Requires osb_clusters_at_boot to be valid and for the journal to have been
initialized by ocfs2_journal_init(). */ staticint ocfs2_journal_addressable(struct ocfs2_super *osb)
{ int status = 0;
u64 max_block =
ocfs2_clusters_to_blocks(osb->sb,
osb->osb_clusters_at_boot) - 1;
/* 32-bit block number is always OK. */ if (max_block <= (u32)~0ULL) goto out;
/* Volume is "huge", so see if our journal is new enough to
support it. */ if (!(OCFS2_HAS_COMPAT_FEATURE(osb->sb,
OCFS2_FEATURE_COMPAT_JBD2_SB) &&
jbd2_journal_check_used_features(osb->journal->j_journal, 0, 0,
JBD2_FEATURE_INCOMPAT_64BIT))) {
mlog(ML_ERROR, "The journal cannot address the entire volume. " "Enable the 'block64' journal option with tunefs.ocfs2");
status = -EFBIG; goto out;
}
out: return status;
}
staticint ocfs2_initialize_super(struct super_block *sb, struct buffer_head *bh, int sector_size, struct ocfs2_blockcheck_stats *stats)
{ int status; int i, cbits, bbits; struct ocfs2_dinode *di = (struct ocfs2_dinode *)bh->b_data; struct inode *inode = NULL; struct ocfs2_super *osb;
u64 total_blocks;
osb = kzalloc(sizeof(struct ocfs2_super), GFP_KERNEL); if (!osb) {
status = -ENOMEM;
mlog_errno(status); goto out;
}
for (i = 0; i < 3; i++)
osb->osb_dx_seed[i] = le32_to_cpu(di->id2.i_super.s_dx_seed[i]);
osb->osb_dx_seed[3] = le32_to_cpu(di->id2.i_super.s_uuid_hash);
if ((i = OCFS2_HAS_INCOMPAT_FEATURE(osb->sb, ~OCFS2_FEATURE_INCOMPAT_SUPP))) {
mlog(ML_ERROR, "couldn't mount because of unsupported " "optional features (%x).\n", i);
status = -EINVAL; goto out_orphan_wipes;
} if (!sb_rdonly(osb->sb) && (i = OCFS2_HAS_RO_COMPAT_FEATURE(osb->sb, ~OCFS2_FEATURE_RO_COMPAT_SUPP))) {
mlog(ML_ERROR, "couldn't mount RDWR because of " "unsupported optional features (%x).\n", i);
status = -EINVAL; goto out_orphan_wipes;
}
if (ocfs2_clusterinfo_valid(osb)) { /* * ci_stack and ci_cluster in ocfs2_cluster_info may not be null * terminated, so make sure no overflow happens here by using * memcpy. Destination strings will always be null terminated * because osb is allocated using kzalloc.
*/
osb->osb_stackflags =
OCFS2_RAW_SB(di)->s_cluster_info.ci_stackflags;
memcpy(osb->osb_cluster_stack,
OCFS2_RAW_SB(di)->s_cluster_info.ci_stack,
OCFS2_STACK_LABEL_LEN); if (strlen(osb->osb_cluster_stack) != OCFS2_STACK_LABEL_LEN) {
mlog(ML_ERROR, "couldn't mount because of an invalid " "cluster stack label (%s) \n",
osb->osb_cluster_stack);
status = -EINVAL; goto out_orphan_wipes;
}
memcpy(osb->osb_cluster_name,
OCFS2_RAW_SB(di)->s_cluster_info.ci_cluster,
OCFS2_CLUSTER_NAME_LEN);
} else { /* The empty string is identical with classic tools that
* don't know about s_cluster_info. */
osb->osb_cluster_stack[0] = '\0';
}
/* * FIXME * This should be done in ocfs2_journal_init(), but any inode * writes back operation will cause the filesystem to crash.
*/
status = ocfs2_journal_alloc(osb); if (status < 0) goto out_orphan_wipes;
status = generic_check_addressable(osb->sb->s_blocksize_bits,
total_blocks); if (status) {
mlog(ML_ERROR, "Volume too large " "to mount safely on this system");
status = -EFBIG; goto out_journal;
}
if (ocfs2_setup_osb_uuid(osb, di->id2.i_super.s_uuid, sizeof(di->id2.i_super.s_uuid))) {
mlog(ML_ERROR, "Out of memory trying to setup our uuid.\n");
status = -ENOMEM; goto out_journal;
}
osb->osb_dlm_debug = ocfs2_new_dlm_debug(); if (!osb->osb_dlm_debug) {
status = -ENOMEM;
mlog_errno(status); goto out_uuid_str;
}
atomic_set(&osb->vol_state, VOLUME_INIT);
/* load root, system_dir, and all global system inodes */
status = ocfs2_init_global_system_inodes(osb); if (status < 0) {
mlog_errno(status); goto out_dlm_out;
}
/* * global bitmap
*/
inode = ocfs2_get_system_file_inode(osb, GLOBAL_BITMAP_SYSTEM_INODE,
OCFS2_INVALID_SLOT); if (!inode) {
status = -EINVAL;
mlog_errno(status); goto out_system_inodes;
}
/* * will return: -EAGAIN if it is ok to keep searching for superblocks * -EINVAL if there is a bad superblock * 0 on success
*/ staticint ocfs2_verify_volume(struct ocfs2_dinode *di, struct buffer_head *bh,
u32 blksz, struct ocfs2_blockcheck_stats *stats)
{ int status = -EAGAIN;
u32 blksz_bits;
if (memcmp(di->i_signature, OCFS2_SUPER_BLOCK_SIGNATURE,
strlen(OCFS2_SUPER_BLOCK_SIGNATURE)) == 0) { /* We have to do a raw check of the feature here */ if (le32_to_cpu(di->id2.i_super.s_feature_incompat) &
OCFS2_FEATURE_INCOMPAT_META_ECC) {
status = ocfs2_block_check_validate(bh->b_data,
bh->b_size,
&di->i_check,
stats); if (status) goto out;
}
status = -EINVAL; /* Acceptable block sizes are 512 bytes, 1K, 2K and 4K. */
blksz_bits = le32_to_cpu(di->id2.i_super.s_blocksize_bits); if (blksz_bits < 9 || blksz_bits > 12) {
mlog(ML_ERROR, "found superblock with incorrect block " "size bits: found %u, should be 9, 10, 11, or 12\n",
blksz_bits);
} elseif ((1 << blksz_bits) != blksz) {
mlog(ML_ERROR, "found superblock with incorrect block " "size: found %u, should be %u\n", 1 << blksz_bits, blksz);
} elseif (le16_to_cpu(di->id2.i_super.s_major_rev_level) !=
OCFS2_MAJOR_REV_LEVEL ||
le16_to_cpu(di->id2.i_super.s_minor_rev_level) !=
OCFS2_MINOR_REV_LEVEL) {
mlog(ML_ERROR, "found superblock with bad version: " "found %u.%u, should be %u.%u\n",
le16_to_cpu(di->id2.i_super.s_major_rev_level),
le16_to_cpu(di->id2.i_super.s_minor_rev_level),
OCFS2_MAJOR_REV_LEVEL,
OCFS2_MINOR_REV_LEVEL);
} elseif (bh->b_blocknr != le64_to_cpu(di->i_blkno)) {
mlog(ML_ERROR, "bad block number on superblock: " "found %llu, should be %llu\n",
(unsignedlonglong)le64_to_cpu(di->i_blkno),
(unsignedlonglong)bh->b_blocknr);
} elseif (le32_to_cpu(di->id2.i_super.s_clustersize_bits) < 12 ||
le32_to_cpu(di->id2.i_super.s_clustersize_bits) > 20) {
mlog(ML_ERROR, "bad cluster size bit found: %u\n",
le32_to_cpu(di->id2.i_super.s_clustersize_bits));
} elseif (!le64_to_cpu(di->id2.i_super.s_root_blkno)) {
mlog(ML_ERROR, "bad root_blkno: 0\n");
} elseif (!le64_to_cpu(di->id2.i_super.s_system_dir_blkno)) {
mlog(ML_ERROR, "bad system_dir_blkno: 0\n");
} elseif (le16_to_cpu(di->id2.i_super.s_max_slots) > OCFS2_MAX_SLOTS) {
mlog(ML_ERROR, "Superblock slots found greater than file system " "maximum: found %u, max %u\n",
le16_to_cpu(di->id2.i_super.s_max_slots),
OCFS2_MAX_SLOTS);
} else { /* found it! */
status = 0;
}
}
out: if (status && status != -EAGAIN)
mlog_errno(status); return status;
}
staticint ocfs2_check_volume(struct ocfs2_super *osb)
{ int status; int dirty; int local; struct ocfs2_dinode *local_alloc = NULL; /* only used if we * recover
* ourselves. */
/* Init our journal object. */
status = ocfs2_journal_init(osb, &dirty); if (status < 0) {
mlog(ML_ERROR, "Could not initialize journal!\n"); goto finally;
}
/* Now that journal has been initialized, check to make sure
entire volume is addressable. */
status = ocfs2_journal_addressable(osb); if (status) goto finally;
/* If the journal was unmounted cleanly then we don't want to * recover anything. Otherwise, journal_load will do that
* dirty work for us :) */ if (!dirty) {
status = ocfs2_journal_wipe(osb->journal, 0); if (status < 0) {
mlog_errno(status); goto finally;
}
} else {
printk(KERN_NOTICE "ocfs2: File system on device (%s) was not " "unmounted cleanly, recovering it.\n", osb->dev_str);
}
local = ocfs2_mount_local(osb);
/* will play back anything left in the journal. */
status = ocfs2_journal_load(osb->journal, local, dirty); if (status < 0) {
mlog(ML_ERROR, "ocfs2 journal load failed! %d\n", status); goto finally;
}
if (dirty) { /* recover my local alloc if we didn't unmount cleanly. */
status = ocfs2_begin_local_alloc_recovery(osb,
osb->slot_num,
&local_alloc); if (status < 0) {
mlog_errno(status); goto finally;
} /* we complete the recovery process after we've marked
* ourselves as mounted. */
}
status = ocfs2_load_local_alloc(osb); if (status < 0) {
mlog_errno(status); goto finally;
}
if (dirty) { /* Recovery will be completed after we've mounted the
* rest of the volume. */
osb->local_alloc_copy = local_alloc;
local_alloc = NULL;
}
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.