diff options
author | Jan Kara <jack@suse.cz> | 2007-10-17 02:29:31 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-17 11:42:56 -0400 |
commit | 8e8934695dfd1d5013555a74a9da706a2e301cb0 (patch) | |
tree | edef65302982cbd3e18cf4ef3c88040939886e3a /fs/dquot.c | |
parent | fac8b209b1084bc85748bd54e13d00c1262b220f (diff) |
quota: send messages via netlink
Implement sending of quota messages via netlink interface. The advantage
is that in userspace we can better decide what to do with the message - for
example display a dialogue in your X session or just write the message to
the console. As a bonus, we can get rid of problems with console locking
deep inside filesystem code once we remove the old printing mechanism.
Signed-off-by: Jan Kara <jack@suse.cz>
Cc: Randy Dunlap <randy.dunlap@oracle.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/dquot.c')
-rw-r--r-- | fs/dquot.c | 144 |
1 files changed, 114 insertions, 30 deletions
diff --git a/fs/dquot.c b/fs/dquot.c index de9a29f64ff3..2809768d9c41 100644 --- a/fs/dquot.c +++ b/fs/dquot.c | |||
@@ -79,6 +79,10 @@ | |||
79 | #include <linux/capability.h> | 79 | #include <linux/capability.h> |
80 | #include <linux/quotaops.h> | 80 | #include <linux/quotaops.h> |
81 | #include <linux/writeback.h> /* for inode_lock, oddly enough.. */ | 81 | #include <linux/writeback.h> /* for inode_lock, oddly enough.. */ |
82 | #ifdef CONFIG_QUOTA_NETLINK_INTERFACE | ||
83 | #include <net/netlink.h> | ||
84 | #include <net/genetlink.h> | ||
85 | #endif | ||
82 | 86 | ||
83 | #include <asm/uaccess.h> | 87 | #include <asm/uaccess.h> |
84 | 88 | ||
@@ -823,6 +827,7 @@ static inline void dquot_decr_space(struct dquot *dquot, qsize_t number) | |||
823 | clear_bit(DQ_BLKS_B, &dquot->dq_flags); | 827 | clear_bit(DQ_BLKS_B, &dquot->dq_flags); |
824 | } | 828 | } |
825 | 829 | ||
830 | #ifdef CONFIG_PRINT_QUOTA_WARNING | ||
826 | static int flag_print_warnings = 1; | 831 | static int flag_print_warnings = 1; |
827 | 832 | ||
828 | static inline int need_print_warning(struct dquot *dquot) | 833 | static inline int need_print_warning(struct dquot *dquot) |
@@ -839,22 +844,15 @@ static inline int need_print_warning(struct dquot *dquot) | |||
839 | return 0; | 844 | return 0; |
840 | } | 845 | } |
841 | 846 | ||
842 | /* Values of warnings */ | ||
843 | #define NOWARN 0 | ||
844 | #define IHARDWARN 1 | ||
845 | #define ISOFTLONGWARN 2 | ||
846 | #define ISOFTWARN 3 | ||
847 | #define BHARDWARN 4 | ||
848 | #define BSOFTLONGWARN 5 | ||
849 | #define BSOFTWARN 6 | ||
850 | |||
851 | /* Print warning to user which exceeded quota */ | 847 | /* Print warning to user which exceeded quota */ |
852 | static void print_warning(struct dquot *dquot, const char warntype) | 848 | static void print_warning(struct dquot *dquot, const char warntype) |
853 | { | 849 | { |
854 | char *msg = NULL; | 850 | char *msg = NULL; |
855 | struct tty_struct *tty; | 851 | struct tty_struct *tty; |
856 | int flag = (warntype == BHARDWARN || warntype == BSOFTLONGWARN) ? DQ_BLKS_B : | 852 | int flag = (warntype == QUOTA_NL_BHARDWARN || |
857 | ((warntype == IHARDWARN || warntype == ISOFTLONGWARN) ? DQ_INODES_B : 0); | 853 | warntype == QUOTA_NL_BSOFTLONGWARN) ? DQ_BLKS_B : |
854 | ((warntype == QUOTA_NL_IHARDWARN || | ||
855 | warntype == QUOTA_NL_ISOFTLONGWARN) ? DQ_INODES_B : 0); | ||
858 | 856 | ||
859 | if (!need_print_warning(dquot) || (flag && test_and_set_bit(flag, &dquot->dq_flags))) | 857 | if (!need_print_warning(dquot) || (flag && test_and_set_bit(flag, &dquot->dq_flags))) |
860 | return; | 858 | return; |
@@ -864,28 +862,28 @@ static void print_warning(struct dquot *dquot, const char warntype) | |||
864 | if (!tty) | 862 | if (!tty) |
865 | goto out_lock; | 863 | goto out_lock; |
866 | tty_write_message(tty, dquot->dq_sb->s_id); | 864 | tty_write_message(tty, dquot->dq_sb->s_id); |
867 | if (warntype == ISOFTWARN || warntype == BSOFTWARN) | 865 | if (warntype == QUOTA_NL_ISOFTWARN || warntype == QUOTA_NL_BSOFTWARN) |
868 | tty_write_message(tty, ": warning, "); | 866 | tty_write_message(tty, ": warning, "); |
869 | else | 867 | else |
870 | tty_write_message(tty, ": write failed, "); | 868 | tty_write_message(tty, ": write failed, "); |
871 | tty_write_message(tty, quotatypes[dquot->dq_type]); | 869 | tty_write_message(tty, quotatypes[dquot->dq_type]); |
872 | switch (warntype) { | 870 | switch (warntype) { |
873 | case IHARDWARN: | 871 | case QUOTA_NL_IHARDWARN: |
874 | msg = " file limit reached.\r\n"; | 872 | msg = " file limit reached.\r\n"; |
875 | break; | 873 | break; |
876 | case ISOFTLONGWARN: | 874 | case QUOTA_NL_ISOFTLONGWARN: |
877 | msg = " file quota exceeded too long.\r\n"; | 875 | msg = " file quota exceeded too long.\r\n"; |
878 | break; | 876 | break; |
879 | case ISOFTWARN: | 877 | case QUOTA_NL_ISOFTWARN: |
880 | msg = " file quota exceeded.\r\n"; | 878 | msg = " file quota exceeded.\r\n"; |
881 | break; | 879 | break; |
882 | case BHARDWARN: | 880 | case QUOTA_NL_BHARDWARN: |
883 | msg = " block limit reached.\r\n"; | 881 | msg = " block limit reached.\r\n"; |
884 | break; | 882 | break; |
885 | case BSOFTLONGWARN: | 883 | case QUOTA_NL_BSOFTLONGWARN: |
886 | msg = " block quota exceeded too long.\r\n"; | 884 | msg = " block quota exceeded too long.\r\n"; |
887 | break; | 885 | break; |
888 | case BSOFTWARN: | 886 | case QUOTA_NL_BSOFTWARN: |
889 | msg = " block quota exceeded.\r\n"; | 887 | msg = " block quota exceeded.\r\n"; |
890 | break; | 888 | break; |
891 | } | 889 | } |
@@ -893,14 +891,93 @@ static void print_warning(struct dquot *dquot, const char warntype) | |||
893 | out_lock: | 891 | out_lock: |
894 | mutex_unlock(&tty_mutex); | 892 | mutex_unlock(&tty_mutex); |
895 | } | 893 | } |
894 | #endif | ||
895 | |||
896 | #ifdef CONFIG_QUOTA_NETLINK_INTERFACE | ||
897 | |||
898 | /* Size of quota netlink message - actually an upperbound for buffer size */ | ||
899 | #define QUOTA_NL_MSG_SIZE 32 | ||
900 | |||
901 | /* Netlink family structure for quota */ | ||
902 | static struct genl_family quota_genl_family = { | ||
903 | .id = GENL_ID_GENERATE, | ||
904 | .hdrsize = 0, | ||
905 | .name = "VFS_DQUOT", | ||
906 | .version = 1, | ||
907 | .maxattr = QUOTA_NL_A_MAX, | ||
908 | }; | ||
909 | |||
910 | /* Send warning to userspace about user which exceeded quota */ | ||
911 | static void send_warning(const struct dquot *dquot, const char warntype) | ||
912 | { | ||
913 | static atomic_t seq; | ||
914 | struct sk_buff *skb; | ||
915 | void *msg_head; | ||
916 | int ret; | ||
917 | |||
918 | /* We have to allocate using GFP_NOFS as we are called from a | ||
919 | * filesystem performing write and thus further recursion into | ||
920 | * the fs to free some data could cause deadlocks. */ | ||
921 | skb = genlmsg_new(QUOTA_NL_MSG_SIZE, GFP_NOFS); | ||
922 | if (!skb) { | ||
923 | printk(KERN_ERR | ||
924 | "VFS: Not enough memory to send quota warning.\n"); | ||
925 | return; | ||
926 | } | ||
927 | msg_head = genlmsg_put(skb, 0, atomic_add_return(1, &seq), | ||
928 | "a_genl_family, 0, QUOTA_NL_C_WARNING); | ||
929 | if (!msg_head) { | ||
930 | printk(KERN_ERR | ||
931 | "VFS: Cannot store netlink header in quota warning.\n"); | ||
932 | goto err_out; | ||
933 | } | ||
934 | ret = nla_put_u32(skb, QUOTA_NL_A_QTYPE, dquot->dq_type); | ||
935 | if (ret) | ||
936 | goto attr_err_out; | ||
937 | ret = nla_put_u64(skb, QUOTA_NL_A_EXCESS_ID, dquot->dq_id); | ||
938 | if (ret) | ||
939 | goto attr_err_out; | ||
940 | ret = nla_put_u32(skb, QUOTA_NL_A_WARNING, warntype); | ||
941 | if (ret) | ||
942 | goto attr_err_out; | ||
943 | ret = nla_put_u32(skb, QUOTA_NL_A_DEV_MAJOR, | ||
944 | MAJOR(dquot->dq_sb->s_dev)); | ||
945 | if (ret) | ||
946 | goto attr_err_out; | ||
947 | ret = nla_put_u32(skb, QUOTA_NL_A_DEV_MINOR, | ||
948 | MINOR(dquot->dq_sb->s_dev)); | ||
949 | if (ret) | ||
950 | goto attr_err_out; | ||
951 | ret = nla_put_u64(skb, QUOTA_NL_A_CAUSED_ID, current->user->uid); | ||
952 | if (ret) | ||
953 | goto attr_err_out; | ||
954 | genlmsg_end(skb, msg_head); | ||
955 | |||
956 | ret = genlmsg_multicast(skb, 0, quota_genl_family.id, GFP_NOFS); | ||
957 | if (ret < 0 && ret != -ESRCH) | ||
958 | printk(KERN_ERR | ||
959 | "VFS: Failed to send notification message: %d\n", ret); | ||
960 | return; | ||
961 | attr_err_out: | ||
962 | printk(KERN_ERR "VFS: Failed to compose quota message: %d\n", ret); | ||
963 | err_out: | ||
964 | kfree_skb(skb); | ||
965 | } | ||
966 | #endif | ||
896 | 967 | ||
897 | static inline void flush_warnings(struct dquot **dquots, char *warntype) | 968 | static inline void flush_warnings(struct dquot **dquots, char *warntype) |
898 | { | 969 | { |
899 | int i; | 970 | int i; |
900 | 971 | ||
901 | for (i = 0; i < MAXQUOTAS; i++) | 972 | for (i = 0; i < MAXQUOTAS; i++) |
902 | if (dquots[i] != NODQUOT && warntype[i] != NOWARN) | 973 | if (dquots[i] != NODQUOT && warntype[i] != QUOTA_NL_NOWARN) { |
974 | #ifdef CONFIG_PRINT_QUOTA_WARNING | ||
903 | print_warning(dquots[i], warntype[i]); | 975 | print_warning(dquots[i], warntype[i]); |
976 | #endif | ||
977 | #ifdef CONFIG_QUOTA_NETLINK_INTERFACE | ||
978 | send_warning(dquots[i], warntype[i]); | ||
979 | #endif | ||
980 | } | ||
904 | } | 981 | } |
905 | 982 | ||
906 | static inline char ignore_hardlimit(struct dquot *dquot) | 983 | static inline char ignore_hardlimit(struct dquot *dquot) |
@@ -914,14 +991,14 @@ static inline char ignore_hardlimit(struct dquot *dquot) | |||
914 | /* needs dq_data_lock */ | 991 | /* needs dq_data_lock */ |
915 | static int check_idq(struct dquot *dquot, ulong inodes, char *warntype) | 992 | static int check_idq(struct dquot *dquot, ulong inodes, char *warntype) |
916 | { | 993 | { |
917 | *warntype = NOWARN; | 994 | *warntype = QUOTA_NL_NOWARN; |
918 | if (inodes <= 0 || test_bit(DQ_FAKE_B, &dquot->dq_flags)) | 995 | if (inodes <= 0 || test_bit(DQ_FAKE_B, &dquot->dq_flags)) |
919 | return QUOTA_OK; | 996 | return QUOTA_OK; |
920 | 997 | ||
921 | if (dquot->dq_dqb.dqb_ihardlimit && | 998 | if (dquot->dq_dqb.dqb_ihardlimit && |
922 | (dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_ihardlimit && | 999 | (dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_ihardlimit && |
923 | !ignore_hardlimit(dquot)) { | 1000 | !ignore_hardlimit(dquot)) { |
924 | *warntype = IHARDWARN; | 1001 | *warntype = QUOTA_NL_IHARDWARN; |
925 | return NO_QUOTA; | 1002 | return NO_QUOTA; |
926 | } | 1003 | } |
927 | 1004 | ||
@@ -929,14 +1006,14 @@ static int check_idq(struct dquot *dquot, ulong inodes, char *warntype) | |||
929 | (dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_isoftlimit && | 1006 | (dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_isoftlimit && |
930 | dquot->dq_dqb.dqb_itime && get_seconds() >= dquot->dq_dqb.dqb_itime && | 1007 | dquot->dq_dqb.dqb_itime && get_seconds() >= dquot->dq_dqb.dqb_itime && |
931 | !ignore_hardlimit(dquot)) { | 1008 | !ignore_hardlimit(dquot)) { |
932 | *warntype = ISOFTLONGWARN; | 1009 | *warntype = QUOTA_NL_ISOFTLONGWARN; |
933 | return NO_QUOTA; | 1010 | return NO_QUOTA; |
934 | } | 1011 | } |
935 | 1012 | ||
936 | if (dquot->dq_dqb.dqb_isoftlimit && | 1013 | if (dquot->dq_dqb.dqb_isoftlimit && |
937 | (dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_isoftlimit && | 1014 | (dquot->dq_dqb.dqb_curinodes + inodes) > dquot->dq_dqb.dqb_isoftlimit && |
938 | dquot->dq_dqb.dqb_itime == 0) { | 1015 | dquot->dq_dqb.dqb_itime == 0) { |
939 | *warntype = ISOFTWARN; | 1016 | *warntype = QUOTA_NL_ISOFTWARN; |
940 | dquot->dq_dqb.dqb_itime = get_seconds() + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_igrace; | 1017 | dquot->dq_dqb.dqb_itime = get_seconds() + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_igrace; |
941 | } | 1018 | } |
942 | 1019 | ||
@@ -946,7 +1023,7 @@ static int check_idq(struct dquot *dquot, ulong inodes, char *warntype) | |||
946 | /* needs dq_data_lock */ | 1023 | /* needs dq_data_lock */ |
947 | static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *warntype) | 1024 | static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *warntype) |
948 | { | 1025 | { |
949 | *warntype = 0; | 1026 | *warntype = QUOTA_NL_NOWARN; |
950 | if (space <= 0 || test_bit(DQ_FAKE_B, &dquot->dq_flags)) | 1027 | if (space <= 0 || test_bit(DQ_FAKE_B, &dquot->dq_flags)) |
951 | return QUOTA_OK; | 1028 | return QUOTA_OK; |
952 | 1029 | ||
@@ -954,7 +1031,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war | |||
954 | toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bhardlimit && | 1031 | toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bhardlimit && |
955 | !ignore_hardlimit(dquot)) { | 1032 | !ignore_hardlimit(dquot)) { |
956 | if (!prealloc) | 1033 | if (!prealloc) |
957 | *warntype = BHARDWARN; | 1034 | *warntype = QUOTA_NL_BHARDWARN; |
958 | return NO_QUOTA; | 1035 | return NO_QUOTA; |
959 | } | 1036 | } |
960 | 1037 | ||
@@ -963,7 +1040,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war | |||
963 | dquot->dq_dqb.dqb_btime && get_seconds() >= dquot->dq_dqb.dqb_btime && | 1040 | dquot->dq_dqb.dqb_btime && get_seconds() >= dquot->dq_dqb.dqb_btime && |
964 | !ignore_hardlimit(dquot)) { | 1041 | !ignore_hardlimit(dquot)) { |
965 | if (!prealloc) | 1042 | if (!prealloc) |
966 | *warntype = BSOFTLONGWARN; | 1043 | *warntype = QUOTA_NL_BSOFTLONGWARN; |
967 | return NO_QUOTA; | 1044 | return NO_QUOTA; |
968 | } | 1045 | } |
969 | 1046 | ||
@@ -971,7 +1048,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc, char *war | |||
971 | toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bsoftlimit && | 1048 | toqb(dquot->dq_dqb.dqb_curspace + space) > dquot->dq_dqb.dqb_bsoftlimit && |
972 | dquot->dq_dqb.dqb_btime == 0) { | 1049 | dquot->dq_dqb.dqb_btime == 0) { |
973 | if (!prealloc) { | 1050 | if (!prealloc) { |
974 | *warntype = BSOFTWARN; | 1051 | *warntype = QUOTA_NL_BSOFTWARN; |
975 | dquot->dq_dqb.dqb_btime = get_seconds() + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_bgrace; | 1052 | dquot->dq_dqb.dqb_btime = get_seconds() + sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_bgrace; |
976 | } | 1053 | } |
977 | else | 1054 | else |
@@ -1066,7 +1143,7 @@ out_add: | |||
1066 | return QUOTA_OK; | 1143 | return QUOTA_OK; |
1067 | } | 1144 | } |
1068 | for (cnt = 0; cnt < MAXQUOTAS; cnt++) | 1145 | for (cnt = 0; cnt < MAXQUOTAS; cnt++) |
1069 | warntype[cnt] = NOWARN; | 1146 | warntype[cnt] = QUOTA_NL_NOWARN; |
1070 | 1147 | ||
1071 | down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); | 1148 | down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); |
1072 | if (IS_NOQUOTA(inode)) { /* Now we can do reliable test... */ | 1149 | if (IS_NOQUOTA(inode)) { /* Now we can do reliable test... */ |
@@ -1112,7 +1189,7 @@ int dquot_alloc_inode(const struct inode *inode, unsigned long number) | |||
1112 | if (IS_NOQUOTA(inode)) | 1189 | if (IS_NOQUOTA(inode)) |
1113 | return QUOTA_OK; | 1190 | return QUOTA_OK; |
1114 | for (cnt = 0; cnt < MAXQUOTAS; cnt++) | 1191 | for (cnt = 0; cnt < MAXQUOTAS; cnt++) |
1115 | warntype[cnt] = NOWARN; | 1192 | warntype[cnt] = QUOTA_NL_NOWARN; |
1116 | down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); | 1193 | down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); |
1117 | if (IS_NOQUOTA(inode)) { | 1194 | if (IS_NOQUOTA(inode)) { |
1118 | up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); | 1195 | up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); |
@@ -1234,7 +1311,7 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr) | |||
1234 | /* Clear the arrays */ | 1311 | /* Clear the arrays */ |
1235 | for (cnt = 0; cnt < MAXQUOTAS; cnt++) { | 1312 | for (cnt = 0; cnt < MAXQUOTAS; cnt++) { |
1236 | transfer_to[cnt] = transfer_from[cnt] = NODQUOT; | 1313 | transfer_to[cnt] = transfer_from[cnt] = NODQUOT; |
1237 | warntype[cnt] = NOWARN; | 1314 | warntype[cnt] = QUOTA_NL_NOWARN; |
1238 | } | 1315 | } |
1239 | down_write(&sb_dqopt(inode->i_sb)->dqptr_sem); | 1316 | down_write(&sb_dqopt(inode->i_sb)->dqptr_sem); |
1240 | /* Now recheck reliably when holding dqptr_sem */ | 1317 | /* Now recheck reliably when holding dqptr_sem */ |
@@ -1808,6 +1885,7 @@ static ctl_table fs_dqstats_table[] = { | |||
1808 | .mode = 0444, | 1885 | .mode = 0444, |
1809 | .proc_handler = &proc_dointvec, | 1886 | .proc_handler = &proc_dointvec, |
1810 | }, | 1887 | }, |
1888 | #ifdef CONFIG_PRINT_QUOTA_WARNING | ||
1811 | { | 1889 | { |
1812 | .ctl_name = FS_DQ_WARNINGS, | 1890 | .ctl_name = FS_DQ_WARNINGS, |
1813 | .procname = "warnings", | 1891 | .procname = "warnings", |
@@ -1816,6 +1894,7 @@ static ctl_table fs_dqstats_table[] = { | |||
1816 | .mode = 0644, | 1894 | .mode = 0644, |
1817 | .proc_handler = &proc_dointvec, | 1895 | .proc_handler = &proc_dointvec, |
1818 | }, | 1896 | }, |
1897 | #endif | ||
1819 | { .ctl_name = 0 }, | 1898 | { .ctl_name = 0 }, |
1820 | }; | 1899 | }; |
1821 | 1900 | ||
@@ -1877,6 +1956,11 @@ static int __init dquot_init(void) | |||
1877 | 1956 | ||
1878 | register_shrinker(&dqcache_shrinker); | 1957 | register_shrinker(&dqcache_shrinker); |
1879 | 1958 | ||
1959 | #ifdef CONFIG_QUOTA_NETLINK_INTERFACE | ||
1960 | if (genl_register_family("a_genl_family) != 0) | ||
1961 | printk(KERN_ERR "VFS: Failed to create quota netlink interface.\n"); | ||
1962 | #endif | ||
1963 | |||
1880 | return 0; | 1964 | return 0; |
1881 | } | 1965 | } |
1882 | module_init(dquot_init); | 1966 | module_init(dquot_init); |