github.com/swiftstack/proxyfs@v0.0.0-20201223034610-5434d919416e/fs/resolve_path_test.go (about)

     1  package fs
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/swiftstack/ProxyFS/inode"
     7  )
     8  
     9  func TestResolvePath(t *testing.T) {
    10  	var (
    11  		canonicalizedPathSplit   []string
    12  		dirA                     inode.InodeNumber
    13  		dirEntryBasename         string
    14  		dirEntryInodeNumber      inode.InodeNumber
    15  		dirEntryInodeType        inode.InodeType
    16  		dirInodeNumber           inode.InodeNumber
    17  		err                      error
    18  		fileA                    inode.InodeNumber
    19  		fileB                    inode.InodeNumber
    20  		heldLocks                *heldLocksStruct
    21  		foundInMap               bool
    22  		reCanonicalizedPathSplit []string
    23  		retryRequired            bool
    24  		symlinkA                 inode.InodeNumber
    25  		testContainer            inode.InodeNumber
    26  	)
    27  
    28  	// Validate canonicalizePath() & reCanonicalizePathForSymlink()
    29  
    30  	canonicalizedPathSplit, err = canonicalizePath("//.//a/b/c/../d")
    31  	if nil != err {
    32  		t.Fatalf("canonicalizePath(\"//.//a/b/c/../d\") failed: %v", err)
    33  	}
    34  	if (3 != len(canonicalizedPathSplit)) ||
    35  		("a" != canonicalizedPathSplit[0]) ||
    36  		("b" != canonicalizedPathSplit[1]) ||
    37  		("d" != canonicalizedPathSplit[2]) {
    38  		t.Fatalf("canonicalizedPathSplit(\"//.//a/b/c/../d\") returned unexpected results: %v", canonicalizedPathSplit)
    39  	}
    40  
    41  	reCanonicalizedPathSplit, err = reCanonicalizePathForSymlink(canonicalizedPathSplit, 1, "e/f")
    42  	if nil != err {
    43  		t.Fatalf("reCanonicalizePathForSymlink(canonicalizedPathSplit, 1, \"e/f\") failed: %v", err)
    44  	}
    45  	if (4 != len(reCanonicalizedPathSplit)) ||
    46  		("a" != reCanonicalizedPathSplit[0]) ||
    47  		("e" != reCanonicalizedPathSplit[1]) ||
    48  		("f" != reCanonicalizedPathSplit[2]) ||
    49  		("d" != reCanonicalizedPathSplit[3]) {
    50  		t.Fatalf("reCanonicalizePathForSymlink(canonicalizedPathSplit, 1, \"e/f\") returned unexpected results: %v", reCanonicalizedPathSplit)
    51  	}
    52  
    53  	// Build out directory hierarchy underneath "/TestResolvePathContainer/":
    54  	//
    55  	//   /
    56  	//     TestResolvePathContainer/
    57  	//                               FileA
    58  	//                               SimlinkA --> FileA
    59  	//                               SimlinkB --> ../DirA
    60  	//
    61  	// During testing, a PUT of /TestResolvePathContainer/SimlinkB/FileB
    62  	//   will implicitly create /TestResolvePathContainer/DirA/FileB:
    63  	//
    64  	//   /
    65  	//     TestResolvePathContainer/
    66  	//                               FileA
    67  	//                               SimlinkA --> FileA
    68  	//                               SimlinkB --> ../DirA
    69  	//                               DirA\
    70  	//                                                    FileB
    71  
    72  	testSetup(t, false)
    73  
    74  	testContainer, err = testVolumeStruct.Mkdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inode.RootDirInodeNumber, "TestResolvePathContainer", inode.PosixModePerm)
    75  	if nil != err {
    76  		t.Fatalf("Mkdir(,,,,\"TestResolvePathContainer\",) failed: %v", err)
    77  	}
    78  	fileA, err = testVolumeStruct.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, testContainer, "FileA", inode.PosixModePerm)
    79  	if nil != err {
    80  		t.Fatalf("Create(,,,,\"FileA\",) failed: %v", err)
    81  	}
    82  	symlinkA, err = testVolumeStruct.Symlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, testContainer, "SymlinkA", "FileA")
    83  	if nil != err {
    84  		t.Fatalf("Symlink(,,,,\"SymlinkA\",) failed: %v", err)
    85  	}
    86  
    87  	// GET or HEAD on "/TestResolvePathContainer/SymlinkA" should find FileA
    88  
    89  	heldLocks = newHeldLocks()
    90  
    91  	dirInodeNumber, dirEntryInodeNumber, dirEntryBasename, dirEntryInodeType, retryRequired, err =
    92  		testVolumeStruct.resolvePath(
    93  			inode.RootDirInodeNumber,
    94  			"/TestResolvePathContainer/SymlinkA",
    95  			heldLocks,
    96  			resolvePathFollowDirEntrySymlinks|
    97  				resolvePathFollowDirSymlinks)
    98  
    99  	if nil != err {
   100  		t.Fatalf("resolvePath() for GET or HEAD failed: %v", err)
   101  	}
   102  	if (testContainer != dirInodeNumber) ||
   103  		(fileA != dirEntryInodeNumber) ||
   104  		("FileA" != dirEntryBasename) ||
   105  		(inode.FileType != dirEntryInodeType) ||
   106  		retryRequired ||
   107  		(0 != len(heldLocks.exclusive)) ||
   108  		(1 != len(heldLocks.shared)) {
   109  		t.Fatalf("resolvePath() for GET or HEAD returned unexpected results")
   110  	}
   111  
   112  	_, foundInMap = heldLocks.shared[fileA]
   113  	if !foundInMap {
   114  		t.Fatalf("resolvePath() for GET or HEAD returned heldLocks.shared missing fileA")
   115  	}
   116  
   117  	heldLocks.free()
   118  
   119  	// POST or COALESCE (destPath) on "/TestResolvePathContainer/SymlinkA" should find FileA
   120  
   121  	heldLocks = newHeldLocks()
   122  
   123  	dirInodeNumber, dirEntryInodeNumber, dirEntryBasename, dirEntryInodeType, retryRequired, err =
   124  		testVolumeStruct.resolvePath(
   125  			inode.RootDirInodeNumber,
   126  			"/TestResolvePathContainer/SymlinkA",
   127  			heldLocks,
   128  			resolvePathFollowDirEntrySymlinks|
   129  				resolvePathFollowDirSymlinks|
   130  				resolvePathCreateMissingPathElements|
   131  				resolvePathRequireExclusiveLockOnDirEntryInode)
   132  
   133  	if nil != err {
   134  		t.Fatalf("resolvePath() for POST or COALESCE (destPath) failed: %v", err)
   135  	}
   136  	if (testContainer != dirInodeNumber) ||
   137  		(fileA != dirEntryInodeNumber) ||
   138  		("FileA" != dirEntryBasename) ||
   139  		(inode.FileType != dirEntryInodeType) ||
   140  		retryRequired ||
   141  		(1 != len(heldLocks.exclusive)) ||
   142  		(0 != len(heldLocks.shared)) {
   143  		t.Fatalf("resolvePath() for POST or COALESCE (destPath) returned unexpected results")
   144  	}
   145  
   146  	_, foundInMap = heldLocks.exclusive[fileA]
   147  	if !foundInMap {
   148  		t.Fatalf("resolvePath() for POST or COALESCE (destPath) returned heldLocks.exclusive missing fileA")
   149  	}
   150  
   151  	heldLocks.free()
   152  
   153  	// DELETE on "/TestResolvePathContainer/SymlinkA" should find SymlinkA
   154  
   155  	heldLocks = newHeldLocks()
   156  
   157  	dirInodeNumber, dirEntryInodeNumber, dirEntryBasename, dirEntryInodeType, retryRequired, err =
   158  		testVolumeStruct.resolvePath(
   159  			inode.RootDirInodeNumber,
   160  			"/TestResolvePathContainer/SymlinkA",
   161  			heldLocks,
   162  			resolvePathFollowDirSymlinks|
   163  				resolvePathRequireExclusiveLockOnDirEntryInode|
   164  				resolvePathRequireExclusiveLockOnDirInode)
   165  
   166  	if nil != err {
   167  		t.Fatalf("resolvePath() for DELETE failed: %v", err)
   168  	}
   169  	if (testContainer != dirInodeNumber) ||
   170  		(symlinkA != dirEntryInodeNumber) ||
   171  		("SymlinkA" != dirEntryBasename) ||
   172  		(inode.SymlinkType != dirEntryInodeType) ||
   173  		retryRequired ||
   174  		(2 != len(heldLocks.exclusive)) ||
   175  		(0 != len(heldLocks.shared)) {
   176  		t.Fatalf("resolvePath() for DELETE returned unexpected results")
   177  	}
   178  
   179  	_, foundInMap = heldLocks.exclusive[symlinkA]
   180  	if !foundInMap {
   181  		t.Fatalf("resolvePath() for DELETE returned heldLocks.shared missing symlinkA")
   182  	}
   183  	_, foundInMap = heldLocks.exclusive[testContainer]
   184  	if !foundInMap {
   185  		t.Fatalf("resolvePath() for DELETE returned heldLocks.shared missing symlinkA")
   186  	}
   187  
   188  	heldLocks.free()
   189  
   190  	// PUT on /TestResolvePathContainer/SimlinkB/FileB should create /TestResolvePathContainer/DirA/FileB
   191  
   192  	heldLocks = newHeldLocks()
   193  
   194  	dirInodeNumber, dirEntryInodeNumber, dirEntryBasename, dirEntryInodeType, retryRequired, err =
   195  		testVolumeStruct.resolvePath(
   196  			inode.RootDirInodeNumber,
   197  			"/TestResolvePathContainer/DirA/FileB",
   198  			heldLocks,
   199  			resolvePathFollowDirEntrySymlinks|
   200  				resolvePathFollowDirSymlinks|
   201  				resolvePathCreateMissingPathElements|
   202  				resolvePathRequireExclusiveLockOnDirEntryInode)
   203  
   204  	if nil != err {
   205  		t.Fatalf("resolvePath() for PUT (thru symlink & missing dir) failed: %v", err)
   206  	}
   207  	if ("FileB" != dirEntryBasename) ||
   208  		(inode.FileType != dirEntryInodeType) ||
   209  		retryRequired ||
   210  		(3 != len(heldLocks.exclusive)) ||
   211  		(0 != len(heldLocks.shared)) {
   212  		t.Fatalf("resolvePath() for PUT (thru symlink & missing dir) returned unexpected results")
   213  	}
   214  
   215  	dirA = dirInodeNumber
   216  	fileB = dirEntryInodeNumber
   217  
   218  	_, foundInMap = heldLocks.exclusive[fileB]
   219  	if !foundInMap {
   220  		t.Fatalf("resolvePath() for PUT (thru symlink & missing dir) returned heldLocks.exclusive missing fileB")
   221  	}
   222  	_, foundInMap = heldLocks.exclusive[dirA]
   223  	if !foundInMap {
   224  		t.Fatalf("resolvePath() for PUT (thru symlink & missing dir) returned heldLocks.exclusive missing dirA")
   225  	}
   226  	_, foundInMap = heldLocks.exclusive[testContainer]
   227  	if !foundInMap {
   228  		t.Fatalf("resolvePath() for PUT (thru symlink & missing dir) returned heldLocks.exclusive missing testContainer")
   229  	}
   230  
   231  	heldLocks.free()
   232  
   233  	dirEntryInodeNumber, err = testVolumeStruct.Lookup(inode.InodeRootUserID, inode.InodeGroupID(0), nil, testContainer, "DirA")
   234  	if nil != err {
   235  		t.Fatalf("Lookup(,,,,\"DirA\") failed: %v", err)
   236  	}
   237  	if dirEntryInodeNumber != dirA {
   238  		t.Fatalf("Lookup(,,,,\"DirA\") returned 0x%016X... expected 0x%016X", dirEntryInodeNumber, dirA)
   239  	}
   240  
   241  	dirEntryInodeNumber, err = testVolumeStruct.Lookup(inode.InodeRootUserID, inode.InodeGroupID(0), nil, dirA, "FileB")
   242  	if nil != err {
   243  		t.Fatalf("Lookup(,,,,\"FileB\") failed: %v", err)
   244  	}
   245  	if dirEntryInodeNumber != fileB {
   246  		t.Fatalf("Lookup(,,,,\"FileB\") returned 0x%016X... expected 0x%016X", dirEntryInodeNumber, fileB)
   247  	}
   248  
   249  	// Destroy directory hierachy underneath "/TestResolvePathContainer/"
   250  
   251  	err = testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, dirA, "FileB")
   252  	if nil != err {
   253  		t.Fatalf("Unlink(,,,,\"FileB\") failed: %v", err)
   254  	}
   255  	err = testVolumeStruct.Rmdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, testContainer, "DirA")
   256  	if nil != err {
   257  		t.Fatalf("Rmdir(,,,,\"DirA\") failed: %v", err)
   258  	}
   259  	err = testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, testContainer, "SymlinkA")
   260  	if nil != err {
   261  		t.Fatalf("Unlink(,,,,\"SymlinkA\") failed: %v", err)
   262  	}
   263  	err = testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, testContainer, "FileA")
   264  	if nil != err {
   265  		t.Fatalf("Unlink(,,,,\"FileA\") failed: %v", err)
   266  	}
   267  	err = testVolumeStruct.Rmdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inode.RootDirInodeNumber, "TestResolvePathContainer")
   268  	if nil != err {
   269  		t.Fatalf("Rmdir(,,,,\"TestResolvePathContainer\") failed: %v", err)
   270  	}
   271  
   272  	testTeardown(t)
   273  }
   274  
   275  type testCanonicalizePathItemStruct struct {
   276  	path                   string
   277  	shouldSucceed          bool
   278  	canonicalizedPathSplit []string
   279  	dirInodeIndex          int
   280  }
   281  
   282  func TestCanonicalizePath(t *testing.T) {
   283  	var (
   284  		canonicalizedPathSplit   []string
   285  		containerInodeNumber     inode.InodeNumber
   286  		dirInodeIndex            int
   287  		directoryInodeNumber     inode.InodeNumber
   288  		err                      error
   289  		pathSplitIndex           int
   290  		testCanonicalizePathItem *testCanonicalizePathItemStruct
   291  		testCanonicalizePathList []*testCanonicalizePathItemStruct
   292  	)
   293  
   294  	testSetup(t, false)
   295  
   296  	_, err = testVolumeStruct.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inode.RootDirInodeNumber, "RootFileName", inode.PosixModePerm)
   297  	if nil != err {
   298  		t.Fatal(err)
   299  	}
   300  	containerInodeNumber, err = testVolumeStruct.Mkdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inode.RootDirInodeNumber, "ContainerName", inode.PosixModePerm)
   301  	if nil != err {
   302  		t.Fatal(err)
   303  	}
   304  	_, err = testVolumeStruct.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, containerInodeNumber, "ContainerFileName", inode.PosixModePerm)
   305  	if nil != err {
   306  		t.Fatal(err)
   307  	}
   308  	directoryInodeNumber, err = testVolumeStruct.Mkdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, containerInodeNumber, "DirectoryName", inode.PosixModePerm)
   309  	if nil != err {
   310  		t.Fatal(err)
   311  	}
   312  	_, err = testVolumeStruct.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, directoryInodeNumber, "DirectoryFileName", inode.PosixModePerm)
   313  	if nil != err {
   314  		t.Fatal(err)
   315  	}
   316  
   317  	testCanonicalizePathList = []*testCanonicalizePathItemStruct{
   318  		&testCanonicalizePathItemStruct{
   319  			path:                   "",
   320  			shouldSucceed:          false,
   321  			canonicalizedPathSplit: []string{}, // Not actually returned
   322  			dirInodeIndex:          0,          // Not actually returned
   323  		},
   324  		&testCanonicalizePathItemStruct{
   325  			path:                   "MissingNameInRoot",
   326  			shouldSucceed:          true,
   327  			canonicalizedPathSplit: []string{"MissingNameInRoot"},
   328  			dirInodeIndex:          -1,
   329  		},
   330  		&testCanonicalizePathItemStruct{
   331  			path:                   "RootFileName",
   332  			shouldSucceed:          true,
   333  			canonicalizedPathSplit: []string{"RootFileName"},
   334  			dirInodeIndex:          -1,
   335  		},
   336  		&testCanonicalizePathItemStruct{
   337  			path:                   "ContainerName",
   338  			shouldSucceed:          true,
   339  			canonicalizedPathSplit: []string{"ContainerName"},
   340  			dirInodeIndex:          0,
   341  		},
   342  		&testCanonicalizePathItemStruct{
   343  			path:                   "ContainerName/MissingNameInContainer",
   344  			shouldSucceed:          true,
   345  			canonicalizedPathSplit: []string{"ContainerName", "MissingNameInContainer"},
   346  			dirInodeIndex:          0,
   347  		},
   348  		&testCanonicalizePathItemStruct{
   349  			path:                   "ContainerName/DirectoryName",
   350  			shouldSucceed:          true,
   351  			canonicalizedPathSplit: []string{"ContainerName", "DirectoryName"},
   352  			dirInodeIndex:          1,
   353  		},
   354  		&testCanonicalizePathItemStruct{
   355  			path:                   "ContainerName/DirectoryName/MissingNameInDirectory",
   356  			shouldSucceed:          true,
   357  			canonicalizedPathSplit: []string{"ContainerName", "DirectoryName", "MissingNameInDirectory"},
   358  			dirInodeIndex:          1,
   359  		},
   360  		&testCanonicalizePathItemStruct{
   361  			path:                   "ContainerName/DirectoryName/DirectoryFileName",
   362  			shouldSucceed:          true,
   363  			canonicalizedPathSplit: []string{"ContainerName", "DirectoryName", "DirectoryFileName"},
   364  			dirInodeIndex:          1,
   365  		},
   366  		&testCanonicalizePathItemStruct{
   367  			path:                   "ContainerName/DirectoryName/MissingNameInDirectory/MissingSubDirectoryName",
   368  			shouldSucceed:          true,
   369  			canonicalizedPathSplit: []string{"ContainerName", "DirectoryName", "MissingNameInDirectory", "MissingSubDirectoryName"},
   370  			dirInodeIndex:          2,
   371  		},
   372  	}
   373  
   374  	for _, testCanonicalizePathItem = range testCanonicalizePathList {
   375  		canonicalizedPathSplit, dirInodeIndex, err = testVolumeStruct.canonicalizePathAndLocateLeafDirInode(testCanonicalizePathItem.path)
   376  		if testCanonicalizePathItem.shouldSucceed {
   377  			if nil == err {
   378  				if len(canonicalizedPathSplit) != len(testCanonicalizePathItem.canonicalizedPathSplit) {
   379  					t.Fatalf("canonicalizePathAndLocateLeafDirInode(\"%s\") received unexpected canonicalizedPathSplit: %v", testCanonicalizePathItem.path, canonicalizedPathSplit)
   380  				}
   381  				for pathSplitIndex = range canonicalizedPathSplit {
   382  					if canonicalizedPathSplit[pathSplitIndex] != testCanonicalizePathItem.canonicalizedPathSplit[pathSplitIndex] {
   383  						t.Fatalf("canonicalizePathAndLocateLeafDirInode(\"%s\") received unexpected canonicalizedPathSplit: %v", testCanonicalizePathItem.path, canonicalizedPathSplit)
   384  					}
   385  				}
   386  				if dirInodeIndex != testCanonicalizePathItem.dirInodeIndex {
   387  					t.Fatalf("canonicalizePathAndLocateLeafDirInode(\"%s\") received unexpected canonicalizedPathSplit: %v", testCanonicalizePathItem.path, dirInodeIndex)
   388  				}
   389  			} else {
   390  				t.Fatalf("canonicalizePathAndLocateLeafDirInode(\"%s\") unexpectadly failed: %v", testCanonicalizePathItem.path, err)
   391  			}
   392  		} else { // !testCanonicalizePathItem.shouldSucceed
   393  			if nil == err {
   394  				t.Fatalf("canonicalizePathAndLocateLeafDirInode(\"%s\") unexpectadly succeeded", testCanonicalizePathItem.path)
   395  			}
   396  		}
   397  	}
   398  
   399  	err = testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, directoryInodeNumber, "DirectoryFileName")
   400  	if nil != err {
   401  		t.Fatal(err)
   402  	}
   403  	err = testVolumeStruct.Rmdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, containerInodeNumber, "DirectoryName")
   404  	if nil != err {
   405  		t.Fatal(err)
   406  	}
   407  	err = testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, containerInodeNumber, "ContainerFileName")
   408  	if nil != err {
   409  		t.Fatal(err)
   410  	}
   411  	err = testVolumeStruct.Rmdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inode.RootDirInodeNumber, "ContainerName")
   412  	if nil != err {
   413  		t.Fatal(err)
   414  	}
   415  	err = testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inode.RootDirInodeNumber, "RootFileName")
   416  	if nil != err {
   417  		t.Fatal(err)
   418  	}
   419  
   420  	testTeardown(t)
   421  }