|
25 | 25 | import static org.apache.hadoop.fs.viewfs.Constants.CONFIG_VIEWFS_MOUNT_LINKS_AS_SYMLINKS; |
26 | 26 | import static org.apache.hadoop.fs.viewfs.Constants.CONFIG_VIEWFS_MOUNT_LINKS_AS_SYMLINKS_DEFAULT; |
27 | 27 | import static org.apache.hadoop.fs.viewfs.Constants.PERMISSION_555; |
| 28 | +import static org.apache.hadoop.fs.viewfs.Constants.CONFIG_VIEWFS_TRASH_FORCE_INSIDE_MOUNT_POINT; |
| 29 | +import static org.apache.hadoop.fs.viewfs.Constants.CONFIG_VIEWFS_TRASH_FORCE_INSIDE_MOUNT_POINT_DEFAULT; |
28 | 30 |
|
29 | 31 | import java.util.function.Function; |
30 | 32 | import java.io.FileNotFoundException; |
@@ -1130,33 +1132,158 @@ public Collection<? extends BlockStoragePolicySpi> getAllStoragePolicies() |
1130 | 1132 | * Get the trash root directory for current user when the path |
1131 | 1133 | * specified is deleted. |
1132 | 1134 | * |
| 1135 | + * If FORCE_INSIDE_MOUNT_POINT flag is not set, return the default trash root |
| 1136 | + * from targetFS. |
| 1137 | + * |
| 1138 | + * When FORCE_INSIDE_MOUNT_POINT is set to true, |
| 1139 | + * <ol> |
| 1140 | + * <li> |
| 1141 | + * If the trash root for path p is in the same mount point as path p, |
| 1142 | + * and one of: |
| 1143 | + * <ol> |
| 1144 | + * <li>The mount point isn't at the top of the target fs.</li> |
| 1145 | + * <li>The resolved path of path is root (in fallback FS).</li> |
| 1146 | + * <li>The trash isn't in user's target fs home directory |
| 1147 | + * get the corresponding viewFS path for the trash root and return |
| 1148 | + * it. |
| 1149 | + * </li> |
| 1150 | + * </ol> |
| 1151 | + * </li> |
| 1152 | + * <li> |
| 1153 | + * else, return the trash root under the root of the mount point |
| 1154 | + * (/{mntpoint}/.Trash/{user}). |
| 1155 | + * </li> |
| 1156 | + * </ol> |
| 1157 | + * |
| 1158 | + * These conditions handle several different important cases: |
| 1159 | + * <ul> |
| 1160 | + * <li>File systems may need to have more local trash roots, such as |
| 1161 | + * encryption zones or snapshot roots.</li> |
| 1162 | + * <li>The fallback mount should use the user's home directory.</li> |
| 1163 | + * <li>Cloud storage systems should not use trash in an implicity defined |
| 1164 | + * home directory, per a container, unless it is the fallback fs.</li> |
| 1165 | + * </ul> |
| 1166 | + * |
1133 | 1167 | * @param path the trash root of the path to be determined. |
1134 | 1168 | * @return the trash root path. |
1135 | 1169 | */ |
1136 | 1170 | @Override |
1137 | 1171 | public Path getTrashRoot(Path path) { |
| 1172 | + |
1138 | 1173 | try { |
1139 | 1174 | InodeTree.ResolveResult<FileSystem> res = |
1140 | 1175 | fsState.resolve(getUriPath(path), true); |
1141 | | - return res.targetFileSystem.getTrashRoot(res.remainingPath); |
1142 | | - } catch (Exception e) { |
| 1176 | + Path targetFSTrashRoot = |
| 1177 | + res.targetFileSystem.getTrashRoot(res.remainingPath); |
| 1178 | + |
| 1179 | + // Allow clients to use old behavior of delegating to target fs. |
| 1180 | + if (!config.getBoolean(CONFIG_VIEWFS_TRASH_FORCE_INSIDE_MOUNT_POINT, |
| 1181 | + CONFIG_VIEWFS_TRASH_FORCE_INSIDE_MOUNT_POINT_DEFAULT)) { |
| 1182 | + return targetFSTrashRoot; |
| 1183 | + } |
| 1184 | + |
| 1185 | + // The trash root path from the target fs |
| 1186 | + String targetFSTrashRootPath = targetFSTrashRoot.toUri().getPath(); |
| 1187 | + // The mount point path in the target fs |
| 1188 | + String mountTargetPath = res.targetFileSystem.getUri().getPath(); |
| 1189 | + if (!mountTargetPath.endsWith("/")) { |
| 1190 | + mountTargetPath = mountTargetPath + "/"; |
| 1191 | + } |
| 1192 | + |
| 1193 | + Path targetFsUserHome = res.targetFileSystem.getHomeDirectory(); |
| 1194 | + if (targetFSTrashRootPath.startsWith(mountTargetPath) && |
| 1195 | + !(mountTargetPath.equals(ROOT_PATH.toString()) && |
| 1196 | + !res.resolvedPath.equals(ROOT_PATH.toString()) && |
| 1197 | + (targetFsUserHome != null && targetFSTrashRootPath.startsWith( |
| 1198 | + targetFsUserHome.toUri().getPath())))) { |
| 1199 | + String relativeTrashRoot = |
| 1200 | + targetFSTrashRootPath.substring(mountTargetPath.length()); |
| 1201 | + return makeQualified(new Path(res.resolvedPath, relativeTrashRoot)); |
| 1202 | + } else { |
| 1203 | + // Return the trash root for the mount point. |
| 1204 | + return makeQualified(new Path(res.resolvedPath, |
| 1205 | + TRASH_PREFIX + "/" + ugi.getShortUserName())); |
| 1206 | + } |
| 1207 | + } catch (IOException | IllegalArgumentException e) { |
1143 | 1208 | throw new NotInMountpointException(path, "getTrashRoot"); |
1144 | 1209 | } |
1145 | 1210 | } |
1146 | 1211 |
|
1147 | 1212 | /** |
1148 | 1213 | * Get all the trash roots for current user or all users. |
1149 | 1214 | * |
| 1215 | + * When FORCE_INSIDE_MOUNT_POINT is set to true, we also return trash roots |
| 1216 | + * under the root of each mount point, with their viewFS paths. |
| 1217 | + * |
1150 | 1218 | * @param allUsers return trash roots for all users if true. |
1151 | 1219 | * @return all Trash root directories. |
1152 | 1220 | */ |
1153 | 1221 | @Override |
1154 | 1222 | public Collection<FileStatus> getTrashRoots(boolean allUsers) { |
1155 | | - List<FileStatus> trashRoots = new ArrayList<>(); |
| 1223 | + // A map from targetFSPath -> FileStatus. |
| 1224 | + // FileStatus can be from targetFS or viewFS. |
| 1225 | + HashMap<Path, FileStatus> trashRoots = new HashMap<>(); |
1156 | 1226 | for (FileSystem fs : getChildFileSystems()) { |
1157 | | - trashRoots.addAll(fs.getTrashRoots(allUsers)); |
| 1227 | + for (FileStatus trash : fs.getTrashRoots(allUsers)) { |
| 1228 | + trashRoots.put(trash.getPath(), trash); |
| 1229 | + } |
1158 | 1230 | } |
1159 | | - return trashRoots; |
| 1231 | + |
| 1232 | + // Return trashRoots if FORCE_INSIDE_MOUNT_POINT is disabled. |
| 1233 | + if (!config.getBoolean(CONFIG_VIEWFS_TRASH_FORCE_INSIDE_MOUNT_POINT, |
| 1234 | + CONFIG_VIEWFS_TRASH_FORCE_INSIDE_MOUNT_POINT_DEFAULT)) { |
| 1235 | + return trashRoots.values(); |
| 1236 | + } |
| 1237 | + |
| 1238 | + // Get trash roots in TRASH_PREFIX dir inside mount points and fallback FS. |
| 1239 | + List<InodeTree.MountPoint<FileSystem>> mountPoints = |
| 1240 | + fsState.getMountPoints(); |
| 1241 | + // If we have a fallback FS, add a mount point for it as <"", fallback FS>. |
| 1242 | + // The source path of a mount point shall not end with '/', thus for |
| 1243 | + // fallback fs, we set its mount point src as "". |
| 1244 | + if (fsState.getRootFallbackLink() != null) { |
| 1245 | + mountPoints.add(new InodeTree.MountPoint<>("", |
| 1246 | + fsState.getRootFallbackLink())); |
| 1247 | + } |
| 1248 | + |
| 1249 | + try { |
| 1250 | + for (InodeTree.MountPoint<FileSystem> mountPoint : mountPoints) { |
| 1251 | + |
| 1252 | + Path trashRoot = |
| 1253 | + makeQualified(new Path(mountPoint.src + "/" + TRASH_PREFIX)); |
| 1254 | + |
| 1255 | + // Continue if trashRoot does not exist for this mount point |
| 1256 | + if (!exists(trashRoot)) { |
| 1257 | + continue; |
| 1258 | + } |
| 1259 | + |
| 1260 | + FileSystem targetFS = mountPoint.target.getTargetFileSystem(); |
| 1261 | + if (!allUsers) { |
| 1262 | + Path userTrashRoot = new Path(trashRoot, ugi.getShortUserName()); |
| 1263 | + if (exists(userTrashRoot)) { |
| 1264 | + Path targetFSUserTrashRoot = targetFS.makeQualified( |
| 1265 | + new Path(targetFS.getUri().getPath(), |
| 1266 | + TRASH_PREFIX + "/" + ugi.getShortUserName())); |
| 1267 | + trashRoots.put(targetFSUserTrashRoot, getFileStatus(userTrashRoot)); |
| 1268 | + } |
| 1269 | + } else { |
| 1270 | + FileStatus[] mountPointTrashRoots = listStatus(trashRoot); |
| 1271 | + for (FileStatus trash : mountPointTrashRoots) { |
| 1272 | + // Remove the mountPoint and the leading '/' to get the |
| 1273 | + // relative targetFsTrash path |
| 1274 | + String targetFsTrash = trash.getPath().toUri().getPath() |
| 1275 | + .substring(mountPoint.src.length() + 1); |
| 1276 | + Path targetFsTrashPath = targetFS.makeQualified( |
| 1277 | + new Path(targetFS.getUri().getPath(), targetFsTrash)); |
| 1278 | + trashRoots.put(targetFsTrashPath, trash); |
| 1279 | + } |
| 1280 | + } |
| 1281 | + } |
| 1282 | + } catch (IOException e) { |
| 1283 | + LOG.warn("Exception in get all trash roots for mount points", e); |
| 1284 | + } |
| 1285 | + |
| 1286 | + return trashRoots.values(); |
1160 | 1287 | } |
1161 | 1288 |
|
1162 | 1289 | @Override |
|
0 commit comments