github.com/swiftstack/ProxyFS@v0.0.0-20210203235616-4017c267d62f/inode/coalesce_test.go (about)

     1  // Copyright (c) 2015-2021, NVIDIA CORPORATION.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package inode
     5  
     6  import (
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/swiftstack/ProxyFS/blunder"
    12  )
    13  
    14  // NB: test setup and such is in api_test.go (look for TestMain function)
    15  
    16  func TestCoalesce(t *testing.T) {
    17  	testSetup(t, false)
    18  
    19  	// We're going to take some files:
    20  	//
    21  	// d1/file1a   (contents "abcd")
    22  	// d1/file1b   (contents "efgh")
    23  	// d2/file2a   (contents "ijkl")
    24  	// d2/file2b   (contents "mnop")
    25  	// d2/file2c   (contents "\0\0st\0\0")
    26  	//
    27  	// and coalesce them into a single file:
    28  	//
    29  	// d1/combined (contents "abcdefghijklmnop\0\0st\0\0")
    30  	//
    31  	// This will also unlink the constituent files from their directories.
    32  
    33  	assert := assert.New(t)
    34  	vh, err := FetchVolumeHandle("TestVolume")
    35  	if !assert.Nil(err) {
    36  		return
    37  	}
    38  
    39  	d1InodeNumber, err := vh.CreateDir(PosixModePerm, 0, 0)
    40  	if !assert.Nil(err) {
    41  		return
    42  	}
    43  	err = vh.Link(RootDirInodeNumber, "d1", d1InodeNumber, false)
    44  	if !assert.Nil(err) {
    45  		return
    46  	}
    47  
    48  	d2InodeNumber, err := vh.CreateDir(PosixModePerm, 0, 0)
    49  	if !assert.Nil(err) {
    50  		return
    51  	}
    52  	err = vh.Link(RootDirInodeNumber, "d2", d2InodeNumber, false)
    53  	if !assert.Nil(err) {
    54  		return
    55  	}
    56  
    57  	file1aInodeNumber, err := vh.CreateFile(PosixModePerm, 0, 0)
    58  	if !assert.Nil(err) {
    59  		return
    60  	}
    61  	err = vh.Write(file1aInodeNumber, 0, []byte("abcd"), nil)
    62  	if !assert.Nil(err) {
    63  		return
    64  	}
    65  	err = vh.Link(d1InodeNumber, "file1a", file1aInodeNumber, false)
    66  	if !assert.Nil(err) {
    67  		return
    68  	}
    69  
    70  	file1bInodeNumber, err := vh.CreateFile(PosixModePerm, 0, 0)
    71  	if !assert.Nil(err) {
    72  		return
    73  	}
    74  	err = vh.Write(file1bInodeNumber, 0, []byte("efgh"), nil)
    75  	if !assert.Nil(err) {
    76  		return
    77  	}
    78  	err = vh.Link(d1InodeNumber, "file1b", file1bInodeNumber, false)
    79  	if !assert.Nil(err) {
    80  		return
    81  	}
    82  
    83  	file2aInodeNumber, err := vh.CreateFile(PosixModePerm, 0, 0)
    84  	if !assert.Nil(err) {
    85  		return
    86  	}
    87  	err = vh.Write(file2aInodeNumber, 0, []byte("ijkl"), nil)
    88  	if !assert.Nil(err) {
    89  		return
    90  	}
    91  	err = vh.Link(d2InodeNumber, "file2a", file2aInodeNumber, false)
    92  	if !assert.Nil(err) {
    93  		return
    94  	}
    95  
    96  	file2bInodeNumber, err := vh.CreateFile(PosixModePerm, 0, 0)
    97  	if !assert.Nil(err) {
    98  		return
    99  	}
   100  	err = vh.Write(file2bInodeNumber, 0, []byte("mnop"), nil)
   101  	if !assert.Nil(err) {
   102  		return
   103  	}
   104  	err = vh.Link(d2InodeNumber, "file2b", file2bInodeNumber, false)
   105  	if !assert.Nil(err) {
   106  		return
   107  	}
   108  
   109  	// Note that this one is sparse: the first 2 bytes are 0, then we have "st", then 2 more 0s
   110  	file2cInodeNumber, err := vh.CreateFile(PosixModePerm, 0, 0)
   111  	if !assert.Nil(err) {
   112  		return
   113  	}
   114  	err = vh.Write(file2cInodeNumber, 2, []byte("st"), nil)
   115  	if !assert.Nil(err) {
   116  		return
   117  	}
   118  	err = vh.Link(d2InodeNumber, "file2c", file2cInodeNumber, false)
   119  	if !assert.Nil(err) {
   120  		return
   121  	}
   122  	err = vh.SetSize(file2cInodeNumber, 6)
   123  	if !assert.Nil(err) {
   124  		return
   125  	}
   126  
   127  	// Now create destination file
   128  	combinedInodeNumber, err := vh.CreateFile(PosixModePerm, 0, 0)
   129  	if !assert.Nil(err) {
   130  		return
   131  	}
   132  	err = vh.Link(d1InodeNumber, "combined", combinedInodeNumber, false)
   133  	if !assert.Nil(err) {
   134  		return
   135  	}
   136  
   137  	// test setup's done; now we can coalesce things
   138  	elements := make([]*CoalesceElement, 0, 5)
   139  	elements = append(elements, &CoalesceElement{
   140  		ContainingDirectoryInodeNumber: d1InodeNumber,
   141  		ElementInodeNumber:             file1aInodeNumber,
   142  		ElementName:                    "file1a"})
   143  	elements = append(elements, &CoalesceElement{
   144  		ContainingDirectoryInodeNumber: d1InodeNumber,
   145  		ElementInodeNumber:             file1bInodeNumber,
   146  		ElementName:                    "file1b"})
   147  	elements = append(elements, &CoalesceElement{
   148  		ContainingDirectoryInodeNumber: d2InodeNumber,
   149  		ElementInodeNumber:             file2aInodeNumber,
   150  		ElementName:                    "file2a"})
   151  	elements = append(elements, &CoalesceElement{
   152  		ContainingDirectoryInodeNumber: d2InodeNumber,
   153  		ElementInodeNumber:             file2bInodeNumber,
   154  		ElementName:                    "file2b"})
   155  	elements = append(elements, &CoalesceElement{
   156  		ContainingDirectoryInodeNumber: d2InodeNumber,
   157  		ElementInodeNumber:             file2cInodeNumber,
   158  		ElementName:                    "file2c"})
   159  
   160  	newMetaData := []byte("The quick brown fox jumped over the lazy dog.")
   161  
   162  	// Coalesce the above 5 files and metadata into d1/combined
   163  	startTime := time.Now()
   164  	attrChangeTime, modificationTime, numWrites, fileSize, err := vh.Coalesce(
   165  		combinedInodeNumber, "MetaDataStream", newMetaData, elements)
   166  	if !assert.Nil(err) {
   167  		return
   168  	}
   169  	assert.Equal(uint64(22), fileSize)
   170  	assert.Equal(uint64(5), numWrites)
   171  	assert.Equal(attrChangeTime, modificationTime)
   172  	assert.True(attrChangeTime.After(startTime))
   173  
   174  	// The new file has the contents of the old files combined
   175  	contents, err := vh.Read(combinedInodeNumber, 0, 22, nil)
   176  	if !assert.Nil(err) {
   177  		return
   178  	}
   179  	assert.Equal([]byte("abcdefghijklmnop\x00\x00st\x00\x00"), contents)
   180  
   181  	// The old files have ceased to be
   182  	_, err = vh.Lookup(d1InodeNumber, "file1a")
   183  	assert.True(blunder.Is(err, blunder.NotFoundError))
   184  	_, err = vh.Lookup(d1InodeNumber, "file1b")
   185  	assert.True(blunder.Is(err, blunder.NotFoundError))
   186  	_, err = vh.Lookup(d2InodeNumber, "file2a")
   187  	assert.True(blunder.Is(err, blunder.NotFoundError))
   188  	_, err = vh.Lookup(d2InodeNumber, "file2b")
   189  	assert.True(blunder.Is(err, blunder.NotFoundError))
   190  	_, err = vh.Lookup(d2InodeNumber, "file2c")
   191  	assert.True(blunder.Is(err, blunder.NotFoundError))
   192  
   193  	// The new file is linked in at the right spot
   194  	foundInodeNumber, err := vh.Lookup(d1InodeNumber, "combined")
   195  	if !assert.Nil(err) {
   196  		return
   197  	}
   198  	assert.Equal(combinedInodeNumber, foundInodeNumber)
   199  
   200  	// Verify the new file has the new metadata
   201  	buf, err := vh.GetStream(combinedInodeNumber, "MetaDataStream")
   202  	if assert.Nil(err) {
   203  		assert.Equal(buf, newMetaData)
   204  	}
   205  
   206  	testTeardown(t)
   207  }
   208  
   209  func TestCoalesceDir(t *testing.T) {
   210  	testSetup(t, false)
   211  
   212  	// We're going to take some files:
   213  	//
   214  	// d1/file1a   (contents "abcd")
   215  	// d1/file1b   (contents "efgh")
   216  	//
   217  	// and attempt to coalesce them into a directory:
   218  	//
   219  	// d1
   220  
   221  	assert := assert.New(t)
   222  	vh, err := FetchVolumeHandle("TestVolume")
   223  	if !assert.Nil(err) {
   224  		return
   225  	}
   226  
   227  	d1InodeNumber, err := vh.CreateDir(PosixModePerm, 0, 0)
   228  	if !assert.Nil(err) {
   229  		return
   230  	}
   231  	err = vh.Link(RootDirInodeNumber, "d1", d1InodeNumber, false)
   232  	if !assert.Nil(err) {
   233  		return
   234  	}
   235  
   236  	file1aInodeNumber, err := vh.CreateFile(PosixModePerm, 0, 0)
   237  	if !assert.Nil(err) {
   238  		return
   239  	}
   240  	err = vh.Write(file1aInodeNumber, 0, []byte("abcd"), nil)
   241  	if !assert.Nil(err) {
   242  		return
   243  	}
   244  	err = vh.Link(d1InodeNumber, "file1a", file1aInodeNumber, false)
   245  	if !assert.Nil(err) {
   246  		return
   247  	}
   248  
   249  	file1bInodeNumber, err := vh.CreateFile(PosixModePerm, 0, 0)
   250  	if !assert.Nil(err) {
   251  		return
   252  	}
   253  	err = vh.Write(file1bInodeNumber, 0, []byte("efgh"), nil)
   254  	if !assert.Nil(err) {
   255  		return
   256  	}
   257  	err = vh.Link(d1InodeNumber, "file1b", file1bInodeNumber, false)
   258  	if !assert.Nil(err) {
   259  		return
   260  	}
   261  
   262  	// test setup's done; now we can coalesce things
   263  	elements := make([]*CoalesceElement, 0, 2)
   264  	elements = append(elements, &CoalesceElement{
   265  		ContainingDirectoryInodeNumber: d1InodeNumber,
   266  		ElementInodeNumber:             file1aInodeNumber,
   267  		ElementName:                    "file1a"})
   268  	elements = append(elements, &CoalesceElement{
   269  		ContainingDirectoryInodeNumber: d1InodeNumber,
   270  		ElementInodeNumber:             file1bInodeNumber,
   271  		ElementName:                    "file1b"})
   272  
   273  	// Coalesce the above 2 files into d1
   274  	_, _, _, _, err = vh.Coalesce(d1InodeNumber, "MetaDataStream", nil, elements)
   275  	assert.NotNil(err)
   276  	assert.True(blunder.Is(err, blunder.PermDeniedError))
   277  
   278  	testTeardown(t)
   279  }
   280  
   281  func TestCoalesceMultipleLinks(t *testing.T) {
   282  	testSetup(t, false)
   283  
   284  	// We're going to take hard-linked files:
   285  	//
   286  	// d1/file1a   (contents "abcd")
   287  	// d1/file1b   (hard-linked to d1/file1a)
   288  	//
   289  	// and attempt to coalesce them into a single file:
   290  	//
   291  	// d1/combined
   292  
   293  	assert := assert.New(t)
   294  	vh, err := FetchVolumeHandle("TestVolume")
   295  	if !assert.Nil(err) {
   296  		return
   297  	}
   298  
   299  	d1InodeNumber, err := vh.CreateDir(PosixModePerm, 0, 0)
   300  	if !assert.Nil(err) {
   301  		return
   302  	}
   303  	err = vh.Link(RootDirInodeNumber, "d1", d1InodeNumber, false)
   304  	if !assert.Nil(err) {
   305  		return
   306  	}
   307  
   308  	file1aInodeNumber, err := vh.CreateFile(PosixModePerm, 0, 0)
   309  	if !assert.Nil(err) {
   310  		return
   311  	}
   312  	err = vh.Write(file1aInodeNumber, 0, []byte("abcd"), nil)
   313  	if !assert.Nil(err) {
   314  		return
   315  	}
   316  	err = vh.Link(d1InodeNumber, "file1a", file1aInodeNumber, false)
   317  	if !assert.Nil(err) {
   318  		return
   319  	}
   320  
   321  	file1bInodeNumber := file1aInodeNumber
   322  	err = vh.Link(d1InodeNumber, "file1b", file1bInodeNumber, false)
   323  	if !assert.Nil(err) {
   324  		return
   325  	}
   326  
   327  	// Now create destination file
   328  	combinedInodeNumber, err := vh.CreateFile(PosixModePerm, 0, 0)
   329  	if !assert.Nil(err) {
   330  		return
   331  	}
   332  	err = vh.Link(d1InodeNumber, "combined", combinedInodeNumber, false)
   333  	if !assert.Nil(err) {
   334  		return
   335  	}
   336  
   337  	// test setup's done; now we can coalesce things
   338  	elements := make([]*CoalesceElement, 0, 2)
   339  	elements = append(elements, &CoalesceElement{
   340  		ContainingDirectoryInodeNumber: d1InodeNumber,
   341  		ElementInodeNumber:             file1aInodeNumber,
   342  		ElementName:                    "file1a"})
   343  	elements = append(elements, &CoalesceElement{
   344  		ContainingDirectoryInodeNumber: d1InodeNumber,
   345  		ElementInodeNumber:             file1bInodeNumber,
   346  		ElementName:                    "file1b"})
   347  
   348  	// Coalesce the above 2 files into d1/combined
   349  	_, _, _, _, err = vh.Coalesce(combinedInodeNumber, "MetaDataStream", nil, elements)
   350  	assert.NotNil(err)
   351  	assert.True(blunder.Is(err, blunder.TooManyLinksError))
   352  
   353  	testTeardown(t)
   354  }
   355  
   356  func TestCoalesceDuplicates(t *testing.T) {
   357  	testSetup(t, false)
   358  
   359  	// We're going to take hard-linked files:
   360  	//
   361  	// d1/file1a   (contents "abcd")
   362  	// d1/file1b   (contents "efgh")
   363  	// d1/file1a   (again)
   364  	//
   365  	// and attempt to coalesce them into a single file:
   366  	//
   367  	// d1/combined
   368  
   369  	assert := assert.New(t)
   370  	vh, err := FetchVolumeHandle("TestVolume")
   371  	if !assert.Nil(err) {
   372  		return
   373  	}
   374  
   375  	d1InodeNumber, err := vh.CreateDir(PosixModePerm, 0, 0)
   376  	if !assert.Nil(err) {
   377  		return
   378  	}
   379  	err = vh.Link(RootDirInodeNumber, "d1", d1InodeNumber, false)
   380  	if !assert.Nil(err) {
   381  		return
   382  	}
   383  
   384  	file1aInodeNumber, err := vh.CreateFile(PosixModePerm, 0, 0)
   385  	if !assert.Nil(err) {
   386  		return
   387  	}
   388  	err = vh.Write(file1aInodeNumber, 0, []byte("abcd"), nil)
   389  	if !assert.Nil(err) {
   390  		return
   391  	}
   392  	err = vh.Link(d1InodeNumber, "file1a", file1aInodeNumber, false)
   393  	if !assert.Nil(err) {
   394  		return
   395  	}
   396  
   397  	file1bInodeNumber, err := vh.CreateFile(PosixModePerm, 0, 0)
   398  	if !assert.Nil(err) {
   399  		return
   400  	}
   401  	err = vh.Write(file1bInodeNumber, 0, []byte("efgh"), nil)
   402  	if !assert.Nil(err) {
   403  		return
   404  	}
   405  	err = vh.Link(d1InodeNumber, "file1b", file1bInodeNumber, false)
   406  	if !assert.Nil(err) {
   407  		return
   408  	}
   409  
   410  	// Now create destination file
   411  	combinedInodeNumber, err := vh.CreateFile(PosixModePerm, 0, 0)
   412  	if !assert.Nil(err) {
   413  		return
   414  	}
   415  	err = vh.Link(d1InodeNumber, "combined", combinedInodeNumber, false)
   416  	if !assert.Nil(err) {
   417  		return
   418  	}
   419  
   420  	// test setup's done; now we can coalesce things
   421  	elements := make([]*CoalesceElement, 0, 3)
   422  	elements = append(elements, &CoalesceElement{
   423  		ContainingDirectoryInodeNumber: d1InodeNumber,
   424  		ElementInodeNumber:             file1aInodeNumber,
   425  		ElementName:                    "file1a"})
   426  	elements = append(elements, &CoalesceElement{
   427  		ContainingDirectoryInodeNumber: d1InodeNumber,
   428  		ElementInodeNumber:             file1bInodeNumber,
   429  		ElementName:                    "file1b"})
   430  	elements = append(elements, &CoalesceElement{
   431  		ContainingDirectoryInodeNumber: d1InodeNumber,
   432  		ElementInodeNumber:             file1aInodeNumber,
   433  		ElementName:                    "file1a"})
   434  
   435  	// Coalesce the above 3 files into d1/combined
   436  	_, _, _, _, err = vh.Coalesce(combinedInodeNumber, "MetaDataStream", nil, elements)
   437  	assert.NotNil(err)
   438  	assert.True(blunder.Is(err, blunder.InvalidArgError))
   439  
   440  	testTeardown(t)
   441  }