|
5 | 5 | #[macro_use]
|
6 | 6 | mod sys_common;
|
7 | 7 |
|
8 |
| -use cap_std::fs::{DirBuilder, OpenOptions}; |
| 8 | +use cap_std::fs::{Dir, DirBuilder, OpenOptions}; |
9 | 9 | use cap_std::time::SystemClock;
|
10 | 10 | use std::io::{self, Read, Write};
|
11 | 11 | use std::path::Path;
|
12 | 12 | use std::str;
|
13 |
| -use sys_common::io::{tmpdir, TempDir}; |
| 13 | +use sys_common::io::tmpdir; |
14 | 14 | use sys_common::symlink_supported;
|
15 | 15 |
|
16 | 16 | #[cfg(not(windows))]
|
17 |
| -fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(src: P, tmpdir: &TempDir, dst: Q) -> io::Result<()> { |
| 17 | +fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(src: P, tmpdir: &Dir, dst: Q) -> io::Result<()> { |
18 | 18 | tmpdir.symlink(src, dst)
|
19 | 19 | }
|
20 | 20 | #[cfg(not(windows))]
|
21 |
| -fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>( |
22 |
| - src: P, |
23 |
| - tmpdir: &TempDir, |
24 |
| - dst: Q, |
25 |
| -) -> io::Result<()> { |
| 21 | +fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, tmpdir: &Dir, dst: Q) -> io::Result<()> { |
26 | 22 | tmpdir.symlink(src, dst)
|
27 | 23 | }
|
28 | 24 | #[cfg(windows)]
|
29 |
| -fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(src: P, tmpdir: &TempDir, dst: Q) -> io::Result<()> { |
| 25 | +fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(src: P, tmpdir: &Dir, dst: Q) -> io::Result<()> { |
30 | 26 | tmpdir.symlink_dir(src, dst)
|
31 | 27 | }
|
32 | 28 | #[cfg(windows)]
|
33 |
| -fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>( |
34 |
| - src: P, |
35 |
| - tmpdir: &TempDir, |
36 |
| - dst: Q, |
37 |
| -) -> io::Result<()> { |
| 29 | +fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, tmpdir: &Dir, dst: Q) -> io::Result<()> { |
38 | 30 | tmpdir.symlink_file(src, dst)
|
39 | 31 | }
|
40 | 32 |
|
@@ -1046,3 +1038,321 @@ fn check_metadata(std: &std::fs::Metadata, cap: &cap_std::fs::Metadata) {
|
1046 | 1038 | assert_eq!(std.blocks(), cap_std::fs::MetadataExt::blocks(cap));
|
1047 | 1039 | }
|
1048 | 1040 | }
|
| 1041 | + |
| 1042 | +/// Test that a symlink in the middle of a path containing ".." doesn't cause |
| 1043 | +/// the path to be treated as if it ends with "..". |
| 1044 | +#[test] |
| 1045 | +fn dotdot_in_middle_of_symlink() { |
| 1046 | + let tmpdir = tmpdir(); |
| 1047 | + |
| 1048 | + let foo = b"foo"; |
| 1049 | + check!(tmpdir.write("target", foo)); |
| 1050 | + check!(tmpdir.create_dir("b")); |
| 1051 | + let b = check!(tmpdir.open_dir("b")); |
| 1052 | + check!(symlink_dir("..", &b, "up")); |
| 1053 | + |
| 1054 | + let path = "b/up/target"; |
| 1055 | + let mut file = check!(tmpdir.open(path)); |
| 1056 | + let mut data = Vec::new(); |
| 1057 | + check!(file.read_to_end(&mut data)); |
| 1058 | + assert_eq!(data, foo); |
| 1059 | +} |
| 1060 | + |
| 1061 | +/// Like `dotdot_in_middle_of_symlink` but with a `/.` at the end. |
| 1062 | +/// |
| 1063 | +/// Windows doesn't appear to like symlinks that end with `/.`. |
| 1064 | +#[test] |
| 1065 | +#[cfg_attr(windows, ignore)] |
| 1066 | +fn dotdot_slashdot_in_middle_of_symlink() { |
| 1067 | + let tmpdir = tmpdir(); |
| 1068 | + |
| 1069 | + let foo = b"foo"; |
| 1070 | + check!(tmpdir.write("target", foo)); |
| 1071 | + check!(tmpdir.create_dir("b")); |
| 1072 | + let b = check!(tmpdir.open_dir("b")); |
| 1073 | + check!(symlink_dir("../.", &b, "up")); |
| 1074 | + |
| 1075 | + let path = "b/up/target"; |
| 1076 | + let mut file = check!(tmpdir.open(path)); |
| 1077 | + let mut data = Vec::new(); |
| 1078 | + check!(file.read_to_end(&mut data)); |
| 1079 | + assert_eq!(data, foo); |
| 1080 | +} |
| 1081 | + |
| 1082 | +/// Same as `dotdot_in_middle_of_symlink`, but use two levels of `..`. |
| 1083 | +/// |
| 1084 | +/// Windows doesn't appear to like symlinks that end with `/..`. |
| 1085 | +#[test] |
| 1086 | +#[cfg_attr(windows, ignore)] |
| 1087 | +fn dotdot_more_in_middle_of_symlink() { |
| 1088 | + let tmpdir = tmpdir(); |
| 1089 | + |
| 1090 | + let foo = b"foo"; |
| 1091 | + check!(tmpdir.write("target", foo)); |
| 1092 | + check!(tmpdir.create_dir_all("b/c")); |
| 1093 | + let b = check!(tmpdir.open_dir("b")); |
| 1094 | + check!(symlink_dir("c/../..", &b, "up")); |
| 1095 | + |
| 1096 | + let path = "b/up/target"; |
| 1097 | + let mut file = check!(tmpdir.open(path)); |
| 1098 | + let mut data = Vec::new(); |
| 1099 | + check!(file.read_to_end(&mut data)); |
| 1100 | + assert_eq!(data, foo); |
| 1101 | +} |
| 1102 | + |
| 1103 | +/// Like `dotdot_more_in_middle_of_symlink`, but with a `/.` at the end. |
| 1104 | +/// |
| 1105 | +/// Windows doesn't appear to like symlinks that end with `/.`. |
| 1106 | +#[test] |
| 1107 | +#[cfg_attr(windows, ignore)] |
| 1108 | +fn dotdot_slashdot_more_in_middle_of_symlink() { |
| 1109 | + let tmpdir = tmpdir(); |
| 1110 | + |
| 1111 | + let foo = b"foo"; |
| 1112 | + check!(tmpdir.write("target", foo)); |
| 1113 | + check!(tmpdir.create_dir_all("b/c")); |
| 1114 | + let b = check!(tmpdir.open_dir("b")); |
| 1115 | + check!(symlink_dir("c/../../.", &b, "up")); |
| 1116 | + |
| 1117 | + let path = "b/up/target"; |
| 1118 | + let mut file = check!(tmpdir.open(path)); |
| 1119 | + let mut data = Vec::new(); |
| 1120 | + check!(file.read_to_end(&mut data)); |
| 1121 | + assert_eq!(data, foo); |
| 1122 | +} |
| 1123 | + |
| 1124 | +/// Same as `dotdot_more_in_middle_of_symlink`, but the symlink doesn't |
| 1125 | +/// include `c`. |
| 1126 | +/// |
| 1127 | +/// Windows doesn't appear to like symlinks that end with `/..`. |
| 1128 | +#[test] |
| 1129 | +#[cfg_attr(windows, ignore)] |
| 1130 | +fn dotdot_other_in_middle_of_symlink() { |
| 1131 | + let tmpdir = tmpdir(); |
| 1132 | + |
| 1133 | + let foo = b"foo"; |
| 1134 | + check!(tmpdir.write("target", foo)); |
| 1135 | + check!(tmpdir.create_dir_all("b/c")); |
| 1136 | + let c = check!(tmpdir.open_dir("b/c")); |
| 1137 | + check!(symlink_dir("../..", &c, "up")); |
| 1138 | + |
| 1139 | + let path = "b/c/up/target"; |
| 1140 | + let mut file = check!(tmpdir.open(path)); |
| 1141 | + let mut data = Vec::new(); |
| 1142 | + check!(file.read_to_end(&mut data)); |
| 1143 | + assert_eq!(data, foo); |
| 1144 | +} |
| 1145 | + |
| 1146 | +/// Like `dotdot_other_in_middle_of_symlink`, but with `/.` at the end. |
| 1147 | +/// |
| 1148 | +/// Windows doesn't appear to like symlinks that end with `/.`. |
| 1149 | +#[test] |
| 1150 | +#[cfg_attr(windows, ignore)] |
| 1151 | +fn dotdot_slashdot_other_in_middle_of_symlink() { |
| 1152 | + let tmpdir = tmpdir(); |
| 1153 | + |
| 1154 | + let foo = b"foo"; |
| 1155 | + check!(tmpdir.write("target", foo)); |
| 1156 | + check!(tmpdir.create_dir_all("b/c")); |
| 1157 | + let c = check!(tmpdir.open_dir("b/c")); |
| 1158 | + check!(symlink_dir("../../.", &c, "up")); |
| 1159 | + |
| 1160 | + let path = "b/c/up/target"; |
| 1161 | + let mut file = check!(tmpdir.open(path)); |
| 1162 | + let mut data = Vec::new(); |
| 1163 | + check!(file.read_to_end(&mut data)); |
| 1164 | + assert_eq!(data, foo); |
| 1165 | +} |
| 1166 | + |
| 1167 | +/// Same as `dotdot_more_in_middle_of_symlink`, but use a symlink that |
| 1168 | +/// doesn't end with `..`. |
| 1169 | +#[test] |
| 1170 | +fn dotdot_even_more_in_middle_of_symlink() { |
| 1171 | + let tmpdir = tmpdir(); |
| 1172 | + |
| 1173 | + let foo = b"foo"; |
| 1174 | + check!(tmpdir.create_dir_all("b/c")); |
| 1175 | + check!(tmpdir.write("b/target", foo)); |
| 1176 | + let b = check!(tmpdir.open_dir("b")); |
| 1177 | + check!(symlink_dir("c/../../b", &b, "up")); |
| 1178 | + |
| 1179 | + let path = "b/up/target"; |
| 1180 | + let mut file = check!(tmpdir.open(path)); |
| 1181 | + let mut data = Vec::new(); |
| 1182 | + check!(file.read_to_end(&mut data)); |
| 1183 | + assert_eq!(data, foo); |
| 1184 | +} |
| 1185 | + |
| 1186 | +/// Like `dotdot_even_more_in_middle_of_symlink`, but with a `/.` at the end. |
| 1187 | +/// |
| 1188 | +/// Windows doesn't appear to like symlinks that end with `/.`. |
| 1189 | +#[test] |
| 1190 | +#[cfg_attr(windows, ignore)] |
| 1191 | +fn dotdot_slashdot_even_more_in_middle_of_symlink() { |
| 1192 | + let tmpdir = tmpdir(); |
| 1193 | + |
| 1194 | + let foo = b"foo"; |
| 1195 | + check!(tmpdir.create_dir_all("b/c")); |
| 1196 | + check!(tmpdir.write("b/target", foo)); |
| 1197 | + let b = check!(tmpdir.open_dir("b")); |
| 1198 | + check!(symlink_dir("c/../../b/.", &b, "up")); |
| 1199 | + |
| 1200 | + let path = "b/up/target"; |
| 1201 | + let mut file = check!(tmpdir.open(path)); |
| 1202 | + let mut data = Vec::new(); |
| 1203 | + check!(file.read_to_end(&mut data)); |
| 1204 | + assert_eq!(data, foo); |
| 1205 | +} |
| 1206 | + |
| 1207 | +/// Same as `dotdot_even_more_in_middle_of_symlink`, but the symlink doesn't |
| 1208 | +/// include `c`. |
| 1209 | +#[test] |
| 1210 | +fn dotdot_even_other_in_middle_of_symlink() { |
| 1211 | + let tmpdir = tmpdir(); |
| 1212 | + |
| 1213 | + let foo = b"foo"; |
| 1214 | + check!(tmpdir.create_dir_all("b/c")); |
| 1215 | + check!(tmpdir.write("b/target", foo)); |
| 1216 | + let c = check!(tmpdir.open_dir("b/c")); |
| 1217 | + check!(symlink_dir("../../b", &c, "up")); |
| 1218 | + |
| 1219 | + let path = "b/c/up/target"; |
| 1220 | + let mut file = check!(tmpdir.open(path)); |
| 1221 | + let mut data = Vec::new(); |
| 1222 | + check!(file.read_to_end(&mut data)); |
| 1223 | + assert_eq!(data, foo); |
| 1224 | +} |
| 1225 | + |
| 1226 | +/// Like `dotdot_even_other_in_middle_of_symlink`, but with a `/.` at the end. |
| 1227 | +/// |
| 1228 | +/// Windows doesn't appear to like symlinks that end with `/.`. |
| 1229 | +#[test] |
| 1230 | +#[cfg_attr(windows, ignore)] |
| 1231 | +fn dotdot_slashdot_even_other_in_middle_of_symlink() { |
| 1232 | + let tmpdir = tmpdir(); |
| 1233 | + |
| 1234 | + let foo = b"foo"; |
| 1235 | + check!(tmpdir.create_dir_all("b/c")); |
| 1236 | + check!(tmpdir.write("b/target", foo)); |
| 1237 | + let c = check!(tmpdir.open_dir("b/c")); |
| 1238 | + check!(symlink_dir("../../b/.", &c, "up")); |
| 1239 | + |
| 1240 | + let path = "b/c/up/target"; |
| 1241 | + let mut file = check!(tmpdir.open(path)); |
| 1242 | + let mut data = Vec::new(); |
| 1243 | + check!(file.read_to_end(&mut data)); |
| 1244 | + assert_eq!(data, foo); |
| 1245 | +} |
| 1246 | + |
| 1247 | +/// Similar to `dotdot_in_middle_of_symlink`, but this time the symlink to |
| 1248 | +/// `..` does happen to be the end of the path, so we need to make sure |
| 1249 | +/// the implementation doesn't just do a stack pop when it sees the `..` |
| 1250 | +/// leaving us with an `O_PATH` directory handle. |
| 1251 | +#[test] |
| 1252 | +fn dotdot_at_end_of_symlink() { |
| 1253 | + let tmpdir = tmpdir(); |
| 1254 | + |
| 1255 | + let foo = b"foo"; |
| 1256 | + check!(tmpdir.write("target", foo)); |
| 1257 | + check!(tmpdir.create_dir("b")); |
| 1258 | + let b = check!(tmpdir.open_dir("b")); |
| 1259 | + check!(symlink_dir("..", &b, "up")); |
| 1260 | + |
| 1261 | + // Do some things with `path` that might break with an `O_PATH` fd. |
| 1262 | + // On Linux, the `permissions` part doesn't because cap-std uses |
| 1263 | + // /proc/self/fd. But the `read_dir` part does. |
| 1264 | + let path = "b/up"; |
| 1265 | + |
| 1266 | + let perms = check!(tmpdir.metadata(path)).permissions(); |
| 1267 | + check!(tmpdir.set_permissions(path, perms)); |
| 1268 | + |
| 1269 | + let contents = check!(tmpdir.read_dir(path)); |
| 1270 | + for entry in contents { |
| 1271 | + let _entry = check!(entry); |
| 1272 | + } |
| 1273 | +} |
| 1274 | + |
| 1275 | +/// Like `dotdot_at_end_of_symlink`, but with a `/.` at the end. |
| 1276 | +/// |
| 1277 | +/// Windows doesn't appear to like symlinks that end with `/.`. |
| 1278 | +#[test] |
| 1279 | +#[cfg_attr(windows, ignore)] |
| 1280 | +fn dotdot_slashdot_at_end_of_symlink() { |
| 1281 | + let tmpdir = tmpdir(); |
| 1282 | + |
| 1283 | + let foo = b"foo"; |
| 1284 | + check!(tmpdir.write("target", foo)); |
| 1285 | + check!(tmpdir.create_dir("b")); |
| 1286 | + let b = check!(tmpdir.open_dir("b")); |
| 1287 | + check!(symlink_dir("../.", &b, "up")); |
| 1288 | + |
| 1289 | + // Do some things with `path` that might break with an `O_PATH` fd. |
| 1290 | + // On Linux, the `permissions` part doesn't because cap-std uses |
| 1291 | + // /proc/self/fd. But the `read_dir` part does. |
| 1292 | + let path = "b/up"; |
| 1293 | + |
| 1294 | + let perms = check!(tmpdir.metadata(path)).permissions(); |
| 1295 | + check!(tmpdir.set_permissions(path, perms)); |
| 1296 | + |
| 1297 | + let contents = check!(tmpdir.read_dir(path)); |
| 1298 | + for entry in contents { |
| 1299 | + let _entry = check!(entry); |
| 1300 | + } |
| 1301 | +} |
| 1302 | + |
| 1303 | +/// Like `dotdot_at_end_of_symlink`, but do everything inside a new directory, |
| 1304 | +/// so that `MaybeOwnedFile` doesn't reopen `.` which would artificially give |
| 1305 | +/// us a non-`O_PATH` fd. |
| 1306 | +#[test] |
| 1307 | +fn dotdot_at_end_of_symlink_all_inside_dir() { |
| 1308 | + let tmpdir = tmpdir(); |
| 1309 | + |
| 1310 | + let foo = b"foo"; |
| 1311 | + check!(tmpdir.create_dir("dir")); |
| 1312 | + check!(tmpdir.write("dir/target", foo)); |
| 1313 | + check!(tmpdir.create_dir("dir/b")); |
| 1314 | + let b = check!(tmpdir.open_dir("dir/b")); |
| 1315 | + check!(symlink_dir("..", &b, "up")); |
| 1316 | + |
| 1317 | + // Do some things with `path` that might break with an `O_PATH` fd. |
| 1318 | + // On Linux, the `permissions` part doesn't because cap-std uses |
| 1319 | + // /proc/self/fd. But the `read_dir` part does. |
| 1320 | + let path = "dir/b/up"; |
| 1321 | + |
| 1322 | + let perms = check!(tmpdir.metadata(path)).permissions(); |
| 1323 | + check!(tmpdir.set_permissions(path, perms)); |
| 1324 | + |
| 1325 | + let contents = check!(tmpdir.read_dir(path)); |
| 1326 | + for entry in contents { |
| 1327 | + let _entry = check!(entry); |
| 1328 | + } |
| 1329 | +} |
| 1330 | + |
| 1331 | +/// `dotdot_at_end_of_symlink_all_inside_dir`, but with a `/.` at the end. |
| 1332 | +/// |
| 1333 | +/// Windows doesn't appear to like symlinks that end with `/.`. |
| 1334 | +#[test] |
| 1335 | +#[cfg_attr(windows, ignore)] |
| 1336 | +fn dotdot_slashdot_at_end_of_symlink_all_inside_dir() { |
| 1337 | + let tmpdir = tmpdir(); |
| 1338 | + |
| 1339 | + let foo = b"foo"; |
| 1340 | + check!(tmpdir.create_dir("dir")); |
| 1341 | + check!(tmpdir.write("dir/target", foo)); |
| 1342 | + check!(tmpdir.create_dir("dir/b")); |
| 1343 | + let b = check!(tmpdir.open_dir("dir/b")); |
| 1344 | + check!(symlink_dir("../.", &b, "up")); |
| 1345 | + |
| 1346 | + // Do some things with `path` that might break with an `O_PATH` fd. |
| 1347 | + // On Linux, the `permissions` part doesn't because cap-std uses |
| 1348 | + // /proc/self/fd. But the `read_dir` part does. |
| 1349 | + let path = "dir/b/up"; |
| 1350 | + |
| 1351 | + let perms = check!(tmpdir.metadata(path)).permissions(); |
| 1352 | + check!(tmpdir.set_permissions(path, perms)); |
| 1353 | + |
| 1354 | + let contents = check!(tmpdir.read_dir(path)); |
| 1355 | + for entry in contents { |
| 1356 | + let _entry = check!(entry); |
| 1357 | + } |
| 1358 | +} |
0 commit comments