diff options
Diffstat (limited to 'fs/ocfs2/super.c')
-rw-r--r-- | fs/ocfs2/super.c | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 60f1d29421ad..2eb657c3e7a8 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c | |||
@@ -41,6 +41,7 @@ | |||
41 | #include <linux/debugfs.h> | 41 | #include <linux/debugfs.h> |
42 | #include <linux/mount.h> | 42 | #include <linux/mount.h> |
43 | #include <linux/seq_file.h> | 43 | #include <linux/seq_file.h> |
44 | #include <linux/quotaops.h> | ||
44 | 45 | ||
45 | #define MLOG_MASK_PREFIX ML_SUPER | 46 | #define MLOG_MASK_PREFIX ML_SUPER |
46 | #include <cluster/masklog.h> | 47 | #include <cluster/masklog.h> |
@@ -127,6 +128,9 @@ static int ocfs2_get_sector(struct super_block *sb, | |||
127 | static void ocfs2_write_super(struct super_block *sb); | 128 | static void ocfs2_write_super(struct super_block *sb); |
128 | static struct inode *ocfs2_alloc_inode(struct super_block *sb); | 129 | static struct inode *ocfs2_alloc_inode(struct super_block *sb); |
129 | static void ocfs2_destroy_inode(struct inode *inode); | 130 | static void ocfs2_destroy_inode(struct inode *inode); |
131 | static int ocfs2_susp_quotas(struct ocfs2_super *osb, int unsuspend); | ||
132 | static int ocfs2_enable_quotas(struct ocfs2_super *osb); | ||
133 | static void ocfs2_disable_quotas(struct ocfs2_super *osb); | ||
130 | 134 | ||
131 | static const struct super_operations ocfs2_sops = { | 135 | static const struct super_operations ocfs2_sops = { |
132 | .statfs = ocfs2_statfs, | 136 | .statfs = ocfs2_statfs, |
@@ -165,6 +169,8 @@ enum { | |||
165 | Opt_inode64, | 169 | Opt_inode64, |
166 | Opt_acl, | 170 | Opt_acl, |
167 | Opt_noacl, | 171 | Opt_noacl, |
172 | Opt_usrquota, | ||
173 | Opt_grpquota, | ||
168 | Opt_err, | 174 | Opt_err, |
169 | }; | 175 | }; |
170 | 176 | ||
@@ -189,6 +195,8 @@ static const match_table_t tokens = { | |||
189 | {Opt_inode64, "inode64"}, | 195 | {Opt_inode64, "inode64"}, |
190 | {Opt_acl, "acl"}, | 196 | {Opt_acl, "acl"}, |
191 | {Opt_noacl, "noacl"}, | 197 | {Opt_noacl, "noacl"}, |
198 | {Opt_usrquota, "usrquota"}, | ||
199 | {Opt_grpquota, "grpquota"}, | ||
192 | {Opt_err, NULL} | 200 | {Opt_err, NULL} |
193 | }; | 201 | }; |
194 | 202 | ||
@@ -452,6 +460,12 @@ static int ocfs2_remount(struct super_block *sb, int *flags, char *data) | |||
452 | 460 | ||
453 | /* We're going to/from readonly mode. */ | 461 | /* We're going to/from readonly mode. */ |
454 | if ((*flags & MS_RDONLY) != (sb->s_flags & MS_RDONLY)) { | 462 | if ((*flags & MS_RDONLY) != (sb->s_flags & MS_RDONLY)) { |
463 | /* Disable quota accounting before remounting RO */ | ||
464 | if (*flags & MS_RDONLY) { | ||
465 | ret = ocfs2_susp_quotas(osb, 0); | ||
466 | if (ret < 0) | ||
467 | goto out; | ||
468 | } | ||
455 | /* Lock here so the check of HARD_RO and the potential | 469 | /* Lock here so the check of HARD_RO and the potential |
456 | * setting of SOFT_RO is atomic. */ | 470 | * setting of SOFT_RO is atomic. */ |
457 | spin_lock(&osb->osb_lock); | 471 | spin_lock(&osb->osb_lock); |
@@ -487,6 +501,21 @@ static int ocfs2_remount(struct super_block *sb, int *flags, char *data) | |||
487 | } | 501 | } |
488 | unlock_osb: | 502 | unlock_osb: |
489 | spin_unlock(&osb->osb_lock); | 503 | spin_unlock(&osb->osb_lock); |
504 | /* Enable quota accounting after remounting RW */ | ||
505 | if (!ret && !(*flags & MS_RDONLY)) { | ||
506 | if (sb_any_quota_suspended(sb)) | ||
507 | ret = ocfs2_susp_quotas(osb, 1); | ||
508 | else | ||
509 | ret = ocfs2_enable_quotas(osb); | ||
510 | if (ret < 0) { | ||
511 | /* Return back changes... */ | ||
512 | spin_lock(&osb->osb_lock); | ||
513 | sb->s_flags |= MS_RDONLY; | ||
514 | osb->osb_flags |= OCFS2_OSB_SOFT_RO; | ||
515 | spin_unlock(&osb->osb_lock); | ||
516 | goto out; | ||
517 | } | ||
518 | } | ||
490 | } | 519 | } |
491 | 520 | ||
492 | if (!ret) { | 521 | if (!ret) { |
@@ -647,6 +676,131 @@ static int ocfs2_verify_userspace_stack(struct ocfs2_super *osb, | |||
647 | return 0; | 676 | return 0; |
648 | } | 677 | } |
649 | 678 | ||
679 | static int ocfs2_susp_quotas(struct ocfs2_super *osb, int unsuspend) | ||
680 | { | ||
681 | int type; | ||
682 | struct super_block *sb = osb->sb; | ||
683 | unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA, | ||
684 | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA}; | ||
685 | int status = 0; | ||
686 | |||
687 | for (type = 0; type < MAXQUOTAS; type++) { | ||
688 | if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type])) | ||
689 | continue; | ||
690 | if (unsuspend) | ||
691 | status = vfs_quota_enable( | ||
692 | sb_dqopt(sb)->files[type], | ||
693 | type, QFMT_OCFS2, | ||
694 | DQUOT_SUSPENDED); | ||
695 | else | ||
696 | status = vfs_quota_disable(sb, type, | ||
697 | DQUOT_SUSPENDED); | ||
698 | if (status < 0) | ||
699 | break; | ||
700 | } | ||
701 | if (status < 0) | ||
702 | mlog(ML_ERROR, "Failed to suspend/unsuspend quotas on " | ||
703 | "remount (error = %d).\n", status); | ||
704 | return status; | ||
705 | } | ||
706 | |||
707 | static int ocfs2_enable_quotas(struct ocfs2_super *osb) | ||
708 | { | ||
709 | struct inode *inode[MAXQUOTAS] = { NULL, NULL }; | ||
710 | struct super_block *sb = osb->sb; | ||
711 | unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA, | ||
712 | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA}; | ||
713 | unsigned int ino[MAXQUOTAS] = { LOCAL_USER_QUOTA_SYSTEM_INODE, | ||
714 | LOCAL_GROUP_QUOTA_SYSTEM_INODE }; | ||
715 | int status; | ||
716 | int type; | ||
717 | |||
718 | sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE | DQUOT_NEGATIVE_USAGE; | ||
719 | for (type = 0; type < MAXQUOTAS; type++) { | ||
720 | if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type])) | ||
721 | continue; | ||
722 | inode[type] = ocfs2_get_system_file_inode(osb, ino[type], | ||
723 | osb->slot_num); | ||
724 | if (!inode[type]) { | ||
725 | status = -ENOENT; | ||
726 | goto out_quota_off; | ||
727 | } | ||
728 | status = vfs_quota_enable(inode[type], type, QFMT_OCFS2, | ||
729 | DQUOT_USAGE_ENABLED); | ||
730 | if (status < 0) | ||
731 | goto out_quota_off; | ||
732 | } | ||
733 | |||
734 | for (type = 0; type < MAXQUOTAS; type++) | ||
735 | iput(inode[type]); | ||
736 | return 0; | ||
737 | out_quota_off: | ||
738 | ocfs2_disable_quotas(osb); | ||
739 | for (type = 0; type < MAXQUOTAS; type++) | ||
740 | iput(inode[type]); | ||
741 | mlog_errno(status); | ||
742 | return status; | ||
743 | } | ||
744 | |||
745 | static void ocfs2_disable_quotas(struct ocfs2_super *osb) | ||
746 | { | ||
747 | int type; | ||
748 | struct inode *inode; | ||
749 | struct super_block *sb = osb->sb; | ||
750 | |||
751 | /* We mostly ignore errors in this function because there's not much | ||
752 | * we can do when we see them */ | ||
753 | for (type = 0; type < MAXQUOTAS; type++) { | ||
754 | if (!sb_has_quota_loaded(sb, type)) | ||
755 | continue; | ||
756 | inode = igrab(sb->s_dquot.files[type]); | ||
757 | /* Turn off quotas. This will remove all dquot structures from | ||
758 | * memory and so they will be automatically synced to global | ||
759 | * quota files */ | ||
760 | vfs_quota_disable(sb, type, DQUOT_USAGE_ENABLED | | ||
761 | DQUOT_LIMITS_ENABLED); | ||
762 | if (!inode) | ||
763 | continue; | ||
764 | iput(inode); | ||
765 | } | ||
766 | } | ||
767 | |||
768 | /* Handle quota on quotactl */ | ||
769 | static int ocfs2_quota_on(struct super_block *sb, int type, int format_id, | ||
770 | char *path, int remount) | ||
771 | { | ||
772 | unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA, | ||
773 | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA}; | ||
774 | |||
775 | if (!OCFS2_HAS_RO_COMPAT_FEATURE(sb, feature[type])) | ||
776 | return -EINVAL; | ||
777 | |||
778 | if (remount) | ||
779 | return 0; /* Just ignore it has been handled in | ||
780 | * ocfs2_remount() */ | ||
781 | return vfs_quota_enable(sb_dqopt(sb)->files[type], type, | ||
782 | format_id, DQUOT_LIMITS_ENABLED); | ||
783 | } | ||
784 | |||
785 | /* Handle quota off quotactl */ | ||
786 | static int ocfs2_quota_off(struct super_block *sb, int type, int remount) | ||
787 | { | ||
788 | if (remount) | ||
789 | return 0; /* Ignore now and handle later in | ||
790 | * ocfs2_remount() */ | ||
791 | return vfs_quota_disable(sb, type, DQUOT_LIMITS_ENABLED); | ||
792 | } | ||
793 | |||
794 | static struct quotactl_ops ocfs2_quotactl_ops = { | ||
795 | .quota_on = ocfs2_quota_on, | ||
796 | .quota_off = ocfs2_quota_off, | ||
797 | .quota_sync = vfs_quota_sync, | ||
798 | .get_info = vfs_get_dqinfo, | ||
799 | .set_info = vfs_set_dqinfo, | ||
800 | .get_dqblk = vfs_get_dqblk, | ||
801 | .set_dqblk = vfs_set_dqblk, | ||
802 | }; | ||
803 | |||
650 | static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) | 804 | static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) |
651 | { | 805 | { |
652 | struct dentry *root; | 806 | struct dentry *root; |
@@ -689,6 +843,22 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) | |||
689 | osb->osb_commit_interval = parsed_options.commit_interval; | 843 | osb->osb_commit_interval = parsed_options.commit_interval; |
690 | osb->local_alloc_default_bits = ocfs2_megabytes_to_clusters(sb, parsed_options.localalloc_opt); | 844 | osb->local_alloc_default_bits = ocfs2_megabytes_to_clusters(sb, parsed_options.localalloc_opt); |
691 | osb->local_alloc_bits = osb->local_alloc_default_bits; | 845 | osb->local_alloc_bits = osb->local_alloc_default_bits; |
846 | if (osb->s_mount_opt & OCFS2_MOUNT_USRQUOTA && | ||
847 | !OCFS2_HAS_RO_COMPAT_FEATURE(sb, | ||
848 | OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) { | ||
849 | status = -EINVAL; | ||
850 | mlog(ML_ERROR, "User quotas were requested, but this " | ||
851 | "filesystem does not have the feature enabled.\n"); | ||
852 | goto read_super_error; | ||
853 | } | ||
854 | if (osb->s_mount_opt & OCFS2_MOUNT_GRPQUOTA && | ||
855 | !OCFS2_HAS_RO_COMPAT_FEATURE(sb, | ||
856 | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) { | ||
857 | status = -EINVAL; | ||
858 | mlog(ML_ERROR, "Group quotas were requested, but this " | ||
859 | "filesystem does not have the feature enabled.\n"); | ||
860 | goto read_super_error; | ||
861 | } | ||
692 | 862 | ||
693 | status = ocfs2_verify_userspace_stack(osb, &parsed_options); | 863 | status = ocfs2_verify_userspace_stack(osb, &parsed_options); |
694 | if (status) | 864 | if (status) |
@@ -793,6 +963,28 @@ static int ocfs2_fill_super(struct super_block *sb, void *data, int silent) | |||
793 | atomic_set(&osb->vol_state, VOLUME_MOUNTED); | 963 | atomic_set(&osb->vol_state, VOLUME_MOUNTED); |
794 | wake_up(&osb->osb_mount_event); | 964 | wake_up(&osb->osb_mount_event); |
795 | 965 | ||
966 | /* Now we can initialize quotas because we can afford to wait | ||
967 | * for cluster locks recovery now. That also means that truncation | ||
968 | * log recovery can happen but that waits for proper quota setup */ | ||
969 | if (!(sb->s_flags & MS_RDONLY)) { | ||
970 | status = ocfs2_enable_quotas(osb); | ||
971 | if (status < 0) { | ||
972 | /* We have to err-out specially here because | ||
973 | * s_root is already set */ | ||
974 | mlog_errno(status); | ||
975 | atomic_set(&osb->vol_state, VOLUME_DISABLED); | ||
976 | wake_up(&osb->osb_mount_event); | ||
977 | mlog_exit(status); | ||
978 | return status; | ||
979 | } | ||
980 | } | ||
981 | |||
982 | ocfs2_complete_quota_recovery(osb); | ||
983 | |||
984 | /* Now we wake up again for processes waiting for quotas */ | ||
985 | atomic_set(&osb->vol_state, VOLUME_MOUNTED_QUOTAS); | ||
986 | wake_up(&osb->osb_mount_event); | ||
987 | |||
796 | mlog_exit(status); | 988 | mlog_exit(status); |
797 | return status; | 989 | return status; |
798 | 990 | ||
@@ -980,6 +1172,28 @@ static int ocfs2_parse_options(struct super_block *sb, | |||
980 | case Opt_inode64: | 1172 | case Opt_inode64: |
981 | mopt->mount_opt |= OCFS2_MOUNT_INODE64; | 1173 | mopt->mount_opt |= OCFS2_MOUNT_INODE64; |
982 | break; | 1174 | break; |
1175 | case Opt_usrquota: | ||
1176 | /* We check only on remount, otherwise features | ||
1177 | * aren't yet initialized. */ | ||
1178 | if (is_remount && !OCFS2_HAS_RO_COMPAT_FEATURE(sb, | ||
1179 | OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) { | ||
1180 | mlog(ML_ERROR, "User quota requested but " | ||
1181 | "filesystem feature is not set\n"); | ||
1182 | status = 0; | ||
1183 | goto bail; | ||
1184 | } | ||
1185 | mopt->mount_opt |= OCFS2_MOUNT_USRQUOTA; | ||
1186 | break; | ||
1187 | case Opt_grpquota: | ||
1188 | if (is_remount && !OCFS2_HAS_RO_COMPAT_FEATURE(sb, | ||
1189 | OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) { | ||
1190 | mlog(ML_ERROR, "Group quota requested but " | ||
1191 | "filesystem feature is not set\n"); | ||
1192 | status = 0; | ||
1193 | goto bail; | ||
1194 | } | ||
1195 | mopt->mount_opt |= OCFS2_MOUNT_GRPQUOTA; | ||
1196 | break; | ||
983 | #ifdef CONFIG_OCFS2_FS_POSIX_ACL | 1197 | #ifdef CONFIG_OCFS2_FS_POSIX_ACL |
984 | case Opt_acl: | 1198 | case Opt_acl: |
985 | mopt->mount_opt |= OCFS2_MOUNT_POSIX_ACL; | 1199 | mopt->mount_opt |= OCFS2_MOUNT_POSIX_ACL; |
@@ -1056,6 +1270,10 @@ static int ocfs2_show_options(struct seq_file *s, struct vfsmount *mnt) | |||
1056 | if (osb->osb_cluster_stack[0]) | 1270 | if (osb->osb_cluster_stack[0]) |
1057 | seq_printf(s, ",cluster_stack=%.*s", OCFS2_STACK_LABEL_LEN, | 1271 | seq_printf(s, ",cluster_stack=%.*s", OCFS2_STACK_LABEL_LEN, |
1058 | osb->osb_cluster_stack); | 1272 | osb->osb_cluster_stack); |
1273 | if (opts & OCFS2_MOUNT_USRQUOTA) | ||
1274 | seq_printf(s, ",usrquota"); | ||
1275 | if (opts & OCFS2_MOUNT_GRPQUOTA) | ||
1276 | seq_printf(s, ",grpquota"); | ||
1059 | 1277 | ||
1060 | if (opts & OCFS2_MOUNT_NOUSERXATTR) | 1278 | if (opts & OCFS2_MOUNT_NOUSERXATTR) |
1061 | seq_printf(s, ",nouser_xattr"); | 1279 | seq_printf(s, ",nouser_xattr"); |
@@ -1394,6 +1612,8 @@ static void ocfs2_dismount_volume(struct super_block *sb, int mnt_err) | |||
1394 | osb = OCFS2_SB(sb); | 1612 | osb = OCFS2_SB(sb); |
1395 | BUG_ON(!osb); | 1613 | BUG_ON(!osb); |
1396 | 1614 | ||
1615 | ocfs2_disable_quotas(osb); | ||
1616 | |||
1397 | ocfs2_shutdown_local_alloc(osb); | 1617 | ocfs2_shutdown_local_alloc(osb); |
1398 | 1618 | ||
1399 | ocfs2_truncate_log_shutdown(osb); | 1619 | ocfs2_truncate_log_shutdown(osb); |
@@ -1504,6 +1724,8 @@ static int ocfs2_initialize_super(struct super_block *sb, | |||
1504 | sb->s_fs_info = osb; | 1724 | sb->s_fs_info = osb; |
1505 | sb->s_op = &ocfs2_sops; | 1725 | sb->s_op = &ocfs2_sops; |
1506 | sb->s_export_op = &ocfs2_export_ops; | 1726 | sb->s_export_op = &ocfs2_export_ops; |
1727 | sb->s_qcop = &ocfs2_quotactl_ops; | ||
1728 | sb->dq_op = &ocfs2_quota_operations; | ||
1507 | sb->s_xattr = ocfs2_xattr_handlers; | 1729 | sb->s_xattr = ocfs2_xattr_handlers; |
1508 | sb->s_time_gran = 1; | 1730 | sb->s_time_gran = 1; |
1509 | sb->s_flags |= MS_NOATIME; | 1731 | sb->s_flags |= MS_NOATIME; |