aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs
diff options
context:
space:
mode:
authorChuck Lever <chuck.lever@oracle.com>2008-06-24 19:28:02 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2008-07-09 12:09:44 -0400
commitf45663ce5fb30f76a3414ab3ac69f4dd320e760a (patch)
treec05fc92328c7a27aad026b1579eb087bf9f17402 /fs/nfs
parent6738b2512bdf93ffbefe108b38a9165d5a718c3f (diff)
NFS: Allow either strict or sloppy mount option parsing
The kernel's NFS client mount option parser currently doesn't allow unrecognized or incorrect mount options. This prevents misspellings or incorrectly specified mount options from possibly causing silent data corruption. However, NFS mount options are not standardized, so different operating systems can use differently spelled mount options to support similar features, or can support mount options which no other operating system supports. "Sloppy" mount option parsing, which allows the parser to ignore any option it doesn't recognize, is needed to support automounters that often use maps that are shared between heterogenous operating systems. The legacy mount command ignores the validity of the values of mount options entirely, except for the "sec=" and "proto=" options. If an incorrect value is specified, the out-of-range value is passed to the kernel; if a value is specified that contains non-numeric characters, it appears as though the legacy mount command sets that option to zero (probably incorrect behavior in general). In any case, this sets a precedent which we will partially follow for the kernel mount option parser: + if "sloppy" is not set, the parser will be strict about both unrecognized options (same as legacy) and invalid option values (stricter than legacy) + if "sloppy" is set, the parser will ignore unrecognized options and invalid option values (same as legacy) An "invalid" option value in this case means that either the type (integer, short, or string) or sign (for integer values) of the specified value is incorrect. This patch does two things: it changes the NFS client's mount option parsing loop so that it parses the whole string instead of failing at the first unrecognized option or invalid option value. An unrecognized option or an invalid option value cause the option to be skipped. Then, the patch adds a "sloppy" mount option that allows the parsing to succeed anyway if there were any problems during parsing. When parsing a set of options is complete, if there are errors and "sloppy" was specified, return success anyway. Otherwise, only return success if there are no errors. Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/super.c203
1 files changed, 128 insertions, 75 deletions
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 4bbdbf6de417..47cf83e917be 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -92,8 +92,8 @@ enum {
92 Opt_sec, Opt_proto, Opt_mountproto, Opt_mounthost, 92 Opt_sec, Opt_proto, Opt_mountproto, Opt_mounthost,
93 Opt_addr, Opt_mountaddr, Opt_clientaddr, 93 Opt_addr, Opt_mountaddr, Opt_clientaddr,
94 94
95 /* Mount options that are ignored */ 95 /* Special mount options */
96 Opt_userspace, Opt_deprecated, 96 Opt_userspace, Opt_deprecated, Opt_sloppy,
97 97
98 Opt_err 98 Opt_err
99}; 99};
@@ -103,6 +103,8 @@ static match_table_t nfs_mount_option_tokens = {
103 { Opt_userspace, "fg" }, 103 { Opt_userspace, "fg" },
104 { Opt_userspace, "retry=%s" }, 104 { Opt_userspace, "retry=%s" },
105 105
106 { Opt_sloppy, "sloppy" },
107
106 { Opt_soft, "soft" }, 108 { Opt_soft, "soft" },
107 { Opt_hard, "hard" }, 109 { Opt_hard, "hard" },
108 { Opt_deprecated, "intr" }, 110 { Opt_deprecated, "intr" },
@@ -917,15 +919,22 @@ static int nfs_parse_security_flavors(char *value,
917 return 1; 919 return 1;
918} 920}
919 921
922static void nfs_parse_invalid_value(const char *option)
923{
924 dfprintk(MOUNT, "NFS: bad value specified for %s option\n", option);
925}
926
920/* 927/*
921 * Error-check and convert a string of mount options from user space into 928 * Error-check and convert a string of mount options from user space into
922 * a data structure 929 * a data structure. The whole mount string is processed; bad options are
930 * skipped as they are encountered. If there were no errors, return 1;
931 * otherwise return 0 (zero).
923 */ 932 */
924static int nfs_parse_mount_options(char *raw, 933static int nfs_parse_mount_options(char *raw,
925 struct nfs_parsed_mount_data *mnt) 934 struct nfs_parsed_mount_data *mnt)
926{ 935{
927 char *p, *string, *secdata; 936 char *p, *string, *secdata;
928 int rc; 937 int rc, sloppy = 0, errors = 0;
929 938
930 if (!raw) { 939 if (!raw) {
931 dfprintk(MOUNT, "NFS: mount options string was NULL.\n"); 940 dfprintk(MOUNT, "NFS: mount options string was NULL.\n");
@@ -958,6 +967,10 @@ static int nfs_parse_mount_options(char *raw,
958 967
959 token = match_token(p, nfs_mount_option_tokens, args); 968 token = match_token(p, nfs_mount_option_tokens, args);
960 switch (token) { 969 switch (token) {
970
971 /*
972 * boolean options: foo/nofoo
973 */
961 case Opt_soft: 974 case Opt_soft:
962 mnt->flags |= NFS_MOUNT_SOFT; 975 mnt->flags |= NFS_MOUNT_SOFT;
963 break; 976 break;
@@ -1025,103 +1038,145 @@ static int nfs_parse_mount_options(char *raw,
1025 mnt->flags |= NFS_MOUNT_UNSHARED; 1038 mnt->flags |= NFS_MOUNT_UNSHARED;
1026 break; 1039 break;
1027 1040
1041 /*
1042 * options that take numeric values
1043 */
1028 case Opt_port: 1044 case Opt_port:
1029 if (match_int(args, &option)) 1045 if (match_int(args, &option) ||
1030 return 0; 1046 option < 0 || option > USHORT_MAX) {
1031 if (option < 0 || option > 65535) 1047 errors++;
1032 return 0; 1048 nfs_parse_invalid_value("port");
1033 mnt->nfs_server.port = option; 1049 } else
1050 mnt->nfs_server.port = option;
1034 break; 1051 break;
1035 case Opt_rsize: 1052 case Opt_rsize:
1036 if (match_int(args, &mnt->rsize)) 1053 if (match_int(args, &option) || option < 0) {
1037 return 0; 1054 errors++;
1055 nfs_parse_invalid_value("rsize");
1056 } else
1057 mnt->rsize = option;
1038 break; 1058 break;
1039 case Opt_wsize: 1059 case Opt_wsize:
1040 if (match_int(args, &mnt->wsize)) 1060 if (match_int(args, &option) || option < 0) {
1041 return 0; 1061 errors++;
1062 nfs_parse_invalid_value("wsize");
1063 } else
1064 mnt->wsize = option;
1042 break; 1065 break;
1043 case Opt_bsize: 1066 case Opt_bsize:
1044 if (match_int(args, &option)) 1067 if (match_int(args, &option) || option < 0) {
1045 return 0; 1068 errors++;
1046 if (option < 0) 1069 nfs_parse_invalid_value("bsize");
1047 return 0; 1070 } else
1048 mnt->bsize = option; 1071 mnt->bsize = option;
1049 break; 1072 break;
1050 case Opt_timeo: 1073 case Opt_timeo:
1051 if (match_int(args, &mnt->timeo)) 1074 if (match_int(args, &option) || option <= 0) {
1052 return 0; 1075 errors++;
1076 nfs_parse_invalid_value("timeo");
1077 } else
1078 mnt->timeo = option;
1053 break; 1079 break;
1054 case Opt_retrans: 1080 case Opt_retrans:
1055 if (match_int(args, &mnt->retrans)) 1081 if (match_int(args, &option) || option <= 0) {
1056 return 0; 1082 errors++;
1083 nfs_parse_invalid_value("retrans");
1084 } else
1085 mnt->retrans = option;
1057 break; 1086 break;
1058 case Opt_acregmin: 1087 case Opt_acregmin:
1059 if (match_int(args, &mnt->acregmin)) 1088 if (match_int(args, &option) || option < 0) {
1060 return 0; 1089 errors++;
1090 nfs_parse_invalid_value("acregmin");
1091 } else
1092 mnt->acregmin = option;
1061 break; 1093 break;
1062 case Opt_acregmax: 1094 case Opt_acregmax:
1063 if (match_int(args, &mnt->acregmax)) 1095 if (match_int(args, &option) || option < 0) {
1064 return 0; 1096 errors++;
1097 nfs_parse_invalid_value("acregmax");
1098 } else
1099 mnt->acregmax = option;
1065 break; 1100 break;
1066 case Opt_acdirmin: 1101 case Opt_acdirmin:
1067 if (match_int(args, &mnt->acdirmin)) 1102 if (match_int(args, &option) || option < 0) {
1068 return 0; 1103 errors++;
1104 nfs_parse_invalid_value("acdirmin");
1105 } else
1106 mnt->acdirmin = option;
1069 break; 1107 break;
1070 case Opt_acdirmax: 1108 case Opt_acdirmax:
1071 if (match_int(args, &mnt->acdirmax)) 1109 if (match_int(args, &option) || option < 0) {
1072 return 0; 1110 errors++;
1111 nfs_parse_invalid_value("acdirmax");
1112 } else
1113 mnt->acdirmax = option;
1073 break; 1114 break;
1074 case Opt_actimeo: 1115 case Opt_actimeo:
1075 if (match_int(args, &option)) 1116 if (match_int(args, &option) || option < 0) {
1076 return 0; 1117 errors++;
1077 if (option < 0) 1118 nfs_parse_invalid_value("actimeo");
1078 return 0; 1119 } else
1079 mnt->acregmin = 1120 mnt->acregmin = mnt->acregmax =
1080 mnt->acregmax = 1121 mnt->acdirmin = mnt->acdirmax = option;
1081 mnt->acdirmin =
1082 mnt->acdirmax = option;
1083 break; 1122 break;
1084 case Opt_namelen: 1123 case Opt_namelen:
1085 if (match_int(args, &mnt->namlen)) 1124 if (match_int(args, &option) || option < 0) {
1086 return 0; 1125 errors++;
1126 nfs_parse_invalid_value("namlen");
1127 } else
1128 mnt->namlen = option;
1087 break; 1129 break;
1088 case Opt_mountport: 1130 case Opt_mountport:
1089 if (match_int(args, &option)) 1131 if (match_int(args, &option) ||
1090 return 0; 1132 option < 0 || option > USHORT_MAX) {
1091 if (option < 0 || option > 65535) 1133 errors++;
1092 return 0; 1134 nfs_parse_invalid_value("mountport");
1093 mnt->mount_server.port = option; 1135 } else
1136 mnt->mount_server.port = option;
1094 break; 1137 break;
1095 case Opt_mountvers: 1138 case Opt_mountvers:
1096 if (match_int(args, &option)) 1139 if (match_int(args, &option) ||
1097 return 0; 1140 option < NFS_MNT_VERSION ||
1098 if (option < 0) 1141 option > NFS_MNT3_VERSION) {
1099 return 0; 1142 errors++;
1100 mnt->mount_server.version = option; 1143 nfs_parse_invalid_value("mountvers");
1144 } else
1145 mnt->mount_server.version = option;
1101 break; 1146 break;
1102 case Opt_nfsvers: 1147 case Opt_nfsvers:
1103 if (match_int(args, &option)) 1148 if (match_int(args, &option)) {
1104 return 0; 1149 errors++;
1150 nfs_parse_invalid_value("nfsvers");
1151 break;
1152 }
1105 switch (option) { 1153 switch (option) {
1106 case 2: 1154 case NFS2_VERSION:
1107 mnt->flags &= ~NFS_MOUNT_VER3; 1155 mnt->flags &= ~NFS_MOUNT_VER3;
1108 break; 1156 break;
1109 case 3: 1157 case NFS3_VERSION:
1110 mnt->flags |= NFS_MOUNT_VER3; 1158 mnt->flags |= NFS_MOUNT_VER3;
1111 break; 1159 break;
1112 default: 1160 default:
1113 goto out_unrec_vers; 1161 errors++;
1162 nfs_parse_invalid_value("nfsvers");
1114 } 1163 }
1115 break; 1164 break;
1116 1165
1166 /*
1167 * options that take text values
1168 */
1117 case Opt_sec: 1169 case Opt_sec:
1118 string = match_strdup(args); 1170 string = match_strdup(args);
1119 if (string == NULL) 1171 if (string == NULL)
1120 goto out_nomem; 1172 goto out_nomem;
1121 rc = nfs_parse_security_flavors(string, mnt); 1173 rc = nfs_parse_security_flavors(string, mnt);
1122 kfree(string); 1174 kfree(string);
1123 if (!rc) 1175 if (!rc) {
1124 goto out_unrec_sec; 1176 errors++;
1177 dfprintk(MOUNT, "NFS: unrecognized "
1178 "security flavor\n");
1179 }
1125 break; 1180 break;
1126 case Opt_proto: 1181 case Opt_proto:
1127 string = match_strdup(args); 1182 string = match_strdup(args);
@@ -1146,7 +1201,9 @@ static int nfs_parse_mount_options(char *raw,
1146 mnt->nfs_server.protocol = XPRT_TRANSPORT_RDMA; 1201 mnt->nfs_server.protocol = XPRT_TRANSPORT_RDMA;
1147 break; 1202 break;
1148 default: 1203 default:
1149 goto out_unrec_xprt; 1204 errors++;
1205 dfprintk(MOUNT, "NFS: unrecognized "
1206 "transport protocol\n");
1150 } 1207 }
1151 break; 1208 break;
1152 case Opt_mountproto: 1209 case Opt_mountproto:
@@ -1166,7 +1223,9 @@ static int nfs_parse_mount_options(char *raw,
1166 break; 1223 break;
1167 case Opt_xprt_rdma: /* not used for side protocols */ 1224 case Opt_xprt_rdma: /* not used for side protocols */
1168 default: 1225 default:
1169 goto out_unrec_xprt; 1226 errors++;
1227 dfprintk(MOUNT, "NFS: unrecognized "
1228 "transport protocol\n");
1170 } 1229 }
1171 break; 1230 break;
1172 case Opt_addr: 1231 case Opt_addr:
@@ -1204,6 +1263,13 @@ static int nfs_parse_mount_options(char *raw,
1204 kfree(string); 1263 kfree(string);
1205 break; 1264 break;
1206 1265
1266 /*
1267 * Special options
1268 */
1269 case Opt_sloppy:
1270 sloppy = 1;
1271 dfprintk(MOUNT, "NFS: relaxing parsing rules\n");
1272 break;
1207 case Opt_userspace: 1273 case Opt_userspace:
1208 case Opt_deprecated: 1274 case Opt_deprecated:
1209 dfprintk(MOUNT, "NFS: ignoring mount option " 1275 dfprintk(MOUNT, "NFS: ignoring mount option "
@@ -1211,7 +1277,9 @@ static int nfs_parse_mount_options(char *raw,
1211 break; 1277 break;
1212 1278
1213 default: 1279 default:
1214 goto out_unknown; 1280 errors++;
1281 dfprintk(MOUNT, "NFS: unrecognized mount option "
1282 "'%s'\n", p);
1215 } 1283 }
1216 } 1284 }
1217 1285
@@ -1224,21 +1292,6 @@ out_security_failure:
1224 free_secdata(secdata); 1292 free_secdata(secdata);
1225 printk(KERN_INFO "NFS: security options invalid: %d\n", rc); 1293 printk(KERN_INFO "NFS: security options invalid: %d\n", rc);
1226 return 0; 1294 return 0;
1227out_unrec_vers:
1228 printk(KERN_INFO "NFS: unrecognized NFS version number\n");
1229 return 0;
1230
1231out_unrec_xprt:
1232 printk(KERN_INFO "NFS: unrecognized transport protocol\n");
1233 return 0;
1234
1235out_unrec_sec:
1236 printk(KERN_INFO "NFS: unrecognized security flavor\n");
1237 return 0;
1238
1239out_unknown:
1240 printk(KERN_INFO "NFS: unknown mount option: %s\n", p);
1241 return 0;
1242} 1295}
1243 1296
1244/* 1297/*