Skip to content

Commit 442be08

Browse files
author
Xing Lin
committed
HADOOP-18144: getTrashRoot in ViewFileSystem should return a path in ViewFS.
To get the new behavior, define fs.viewfs.trash.force-inside-mount-point to be true. If the trash root for path p is in the same mount point as path p, and one of: * The mount point isn't at the top of the target fs. * The resolved path of path is root (eg it is the fallback FS). * The trash root isn't in user's target fs home directory. get the corresponding viewFS path for the trash root and return it. Otherwise, use <mnt>/.Trash/<user>. Signed-off-by: Owen O'Malley <[email protected]> (cherry picked from commit 8b8158f)
1 parent 712d9be commit 442be08

File tree

7 files changed

+340
-7
lines changed

7 files changed

+340
-7
lines changed

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/TrashPolicyDefault.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,8 @@ public boolean moveToTrash(Path path) throws IOException {
191191
cause = e;
192192
}
193193
}
194-
throw (IOException)
195-
new IOException("Failed to move to trash: " + path).initCause(cause);
194+
throw new IOException("Failed to move " + path + " to trash " + trashPath,
195+
cause);
196196
}
197197

198198
@SuppressWarnings("deprecation")

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/Constants.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,4 +125,11 @@ public interface Constants {
125125
"fs.viewfs.ignore.port.in.mount.table.name";
126126

127127
boolean CONFIG_VIEWFS_IGNORE_PORT_IN_MOUNT_TABLE_NAME_DEFAULT = false;
128+
129+
/**
130+
* Force ViewFileSystem to return a trashRoot that is inside a mount point.
131+
*/
132+
String CONFIG_VIEWFS_TRASH_FORCE_INSIDE_MOUNT_POINT =
133+
"fs.viewfs.trash.force-inside-mount-point";
134+
boolean CONFIG_VIEWFS_TRASH_FORCE_INSIDE_MOUNT_POINT_DEFAULT = false;
128135
}

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/ViewFileSystem.java

Lines changed: 132 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import static org.apache.hadoop.fs.viewfs.Constants.CONFIG_VIEWFS_MOUNT_LINKS_AS_SYMLINKS;
2626
import static org.apache.hadoop.fs.viewfs.Constants.CONFIG_VIEWFS_MOUNT_LINKS_AS_SYMLINKS_DEFAULT;
2727
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;
2830

2931
import java.util.function.Function;
3032
import java.io.FileNotFoundException;
@@ -1130,33 +1132,158 @@ public Collection<? extends BlockStoragePolicySpi> getAllStoragePolicies()
11301132
* Get the trash root directory for current user when the path
11311133
* specified is deleted.
11321134
*
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+
*
11331167
* @param path the trash root of the path to be determined.
11341168
* @return the trash root path.
11351169
*/
11361170
@Override
11371171
public Path getTrashRoot(Path path) {
1172+
11381173
try {
11391174
InodeTree.ResolveResult<FileSystem> res =
11401175
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) {
11431208
throw new NotInMountpointException(path, "getTrashRoot");
11441209
}
11451210
}
11461211

11471212
/**
11481213
* Get all the trash roots for current user or all users.
11491214
*
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+
*
11501218
* @param allUsers return trash roots for all users if true.
11511219
* @return all Trash root directories.
11521220
*/
11531221
@Override
11541222
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<>();
11561226
for (FileSystem fs : getChildFileSystems()) {
1157-
trashRoots.addAll(fs.getTrashRoots(allUsers));
1227+
for (FileStatus trash : fs.getTrashRoots(allUsers)) {
1228+
trashRoots.put(trash.getPath(), trash);
1229+
}
11581230
}
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();
11601287
}
11611288

11621289
@Override

hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/viewfs/TestViewFileSystemLocalFileSystem.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
import org.apache.hadoop.fs.FileStatus;
3434
import org.apache.hadoop.fs.FileSystem;
3535
import org.apache.hadoop.fs.Path;
36+
import static org.apache.hadoop.fs.FileSystem.TRASH_PREFIX;
37+
import org.apache.hadoop.security.UserGroupInformation;
3638

3739
import org.junit.After;
3840
import org.junit.Before;
@@ -61,6 +63,13 @@ public void setUp() throws Exception {
6163

6264
}
6365

66+
@Override
67+
Path getTrashRootInFallBackFS() throws IOException {
68+
return new Path(
69+
"/" + TRASH_PREFIX + "/" + UserGroupInformation.getCurrentUser()
70+
.getShortUserName());
71+
}
72+
6473
@Test
6574
public void testNflyWriteSimple() throws IOException {
6675
LOG.info("Starting testNflyWriteSimple");

hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/viewfs/TestViewFileSystemWithAuthorityLocalFileSystem.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
import org.apache.hadoop.fs.FileSystemTestHelper;
2626
import org.apache.hadoop.fs.FsConstants;
2727
import org.apache.hadoop.fs.Path;
28+
import static org.apache.hadoop.fs.FileSystem.TRASH_PREFIX;
29+
import org.apache.hadoop.security.UserGroupInformation;
30+
import java.io.IOException;
2831

2932
import org.junit.After;
3033
import org.junit.Assert;
@@ -63,6 +66,13 @@ public void tearDown() throws Exception {
6366
super.tearDown();
6467
}
6568

69+
@Override
70+
Path getTrashRootInFallBackFS() throws IOException {
71+
return new Path(
72+
"/" + TRASH_PREFIX + "/" + UserGroupInformation.getCurrentUser()
73+
.getShortUserName());
74+
}
75+
6676
@Override
6777
@Test
6878
public void testBasicPaths() {

0 commit comments

Comments
 (0)