gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/renter/files_test.go (about)

     1  package renter
     2  
     3  import (
     4  	"encoding/hex"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  	"testing"
    11  
    12  	"gitlab.com/NebulousLabs/errors"
    13  	"gitlab.com/NebulousLabs/fastrand"
    14  
    15  	"gitlab.com/SkynetLabs/skyd/skymodules"
    16  	"gitlab.com/SkynetLabs/skyd/skymodules/renter/filesystem"
    17  	"go.sia.tech/siad/crypto"
    18  	"go.sia.tech/siad/persist"
    19  )
    20  
    21  // createRenterTestFile creates a test file when the test has a renter so that the
    22  // file is properly added to the renter. It returns the SiaFileSetEntry that the
    23  // SiaFile is stored in
    24  func (r *Renter) createRenterTestFile(siaPath skymodules.SiaPath) (*filesystem.FileNode, error) {
    25  	// Generate erasure coder
    26  	_, rsc := testingFileParams()
    27  	return r.createRenterTestFileWithParams(siaPath, rsc, crypto.RandomCipherType())
    28  }
    29  
    30  // createRenterTestFileWithParams creates a test file when the test has a renter
    31  // so that the file is properly added to the renter.
    32  func (r *Renter) createRenterTestFileWithParams(siaPath skymodules.SiaPath, rsc skymodules.ErasureCoder, ct crypto.CipherType) (*filesystem.FileNode, error) {
    33  	return r.createRenterTestFileWithParamsAndSize(siaPath, rsc, ct, 1000)
    34  }
    35  
    36  // createRenterTestFileWithParamsAndSize creates a test file when the test has
    37  // a renter so that the file is properly added to the renter.
    38  func (r *Renter) createRenterTestFileWithParamsAndSize(siaPath skymodules.SiaPath, rsc skymodules.ErasureCoder, ct crypto.CipherType, size uint64) (*filesystem.FileNode, error) {
    39  	// create the renter/files dir if it doesn't exist
    40  	siaFilePath := r.staticFileSystem.FilePath(siaPath)
    41  	dir, _ := filepath.Split(siaFilePath)
    42  	if err := os.MkdirAll(dir, 0700); err != nil {
    43  		return nil, err
    44  	}
    45  	// Create File
    46  	up := skymodules.FileUploadParams{
    47  		Source:      "",
    48  		SiaPath:     siaPath,
    49  		ErasureCode: rsc,
    50  	}
    51  	err := r.staticFileSystem.NewSiaFile(up.SiaPath, up.Source, up.ErasureCode, crypto.GenerateSiaKey(ct), size, persist.DefaultDiskPermissionsTest)
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  	return r.staticFileSystem.OpenSiaFile(up.SiaPath)
    56  }
    57  
    58  // newRenterTestFile creates a test file when the test has a renter so that the
    59  // file is properly added to the renter. It returns the SiaFileSetEntry that the
    60  // SiaFile is stored in
    61  func (r *Renter) newRenterTestFile() (*filesystem.FileNode, error) {
    62  	// Generate name and erasure coding
    63  	siaPath, rsc := testingFileParams()
    64  	f, err := r.createRenterTestFileWithParams(siaPath, rsc, crypto.RandomCipherType())
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  	// Mark as finished for compatibility in testing
    69  	return f, f.SetFinished(0)
    70  }
    71  
    72  // TestRenterFileListLocalPath verifies that FileList() returns the correct
    73  // local path information for an uploaded file.
    74  func TestRenterFileListLocalPath(t *testing.T) {
    75  	if testing.Short() {
    76  		t.SkipNow()
    77  	}
    78  	rt, err := newRenterTester(t.Name())
    79  	if err != nil {
    80  		t.Fatal(err)
    81  	}
    82  	defer func() {
    83  		if err := rt.Close(); err != nil {
    84  			t.Fatal(err)
    85  		}
    86  	}()
    87  	id := rt.renter.mu.Lock()
    88  	entry, _ := rt.renter.newRenterTestFile()
    89  	if err := entry.SetLocalPath("TestPath"); err != nil {
    90  		t.Fatal(err)
    91  	}
    92  	rt.renter.mu.Unlock(id)
    93  	files, err := rt.renter.FileListCollect(skymodules.RootSiaPath(), true, false)
    94  	if err != nil {
    95  		t.Fatal(err)
    96  	}
    97  	if len(files) != 1 {
    98  		t.Fatal("wrong number of files, got", len(files), "wanted one")
    99  	}
   100  	if files[0].LocalPath != "TestPath" {
   101  		t.Fatal("file had wrong LocalPath: got", files[0].LocalPath, "wanted TestPath")
   102  	}
   103  }
   104  
   105  // TestRenterDeleteFile probes the DeleteFile method of the renter type.
   106  func TestRenterDeleteFile(t *testing.T) {
   107  	if testing.Short() {
   108  		t.SkipNow()
   109  	}
   110  	rt, err := newRenterTester(t.Name())
   111  	if err != nil {
   112  		t.Fatal(err)
   113  	}
   114  	defer func() {
   115  		if err := rt.Close(); err != nil {
   116  			t.Fatal(err)
   117  		}
   118  	}()
   119  
   120  	// Delete a file from an empty renter.
   121  	siaPath, err := skymodules.NewSiaPath("dne")
   122  	if err != nil {
   123  		t.Fatal(err)
   124  	}
   125  	err = rt.renter.DeleteFile(siaPath)
   126  	// NOTE: using strings.Contains because errors.Contains does not recognize
   127  	// errors when errors.Extend is used
   128  	if !strings.Contains(err.Error(), filesystem.ErrNotExist.Error()) {
   129  		t.Errorf("Expected error to contain %v but got '%v'", filesystem.ErrNotExist, err)
   130  	}
   131  
   132  	// Put a file in the renter.
   133  	entry, err := rt.renter.newRenterTestFile()
   134  	if err != nil {
   135  		t.Fatal(err)
   136  	}
   137  	// Delete a different file.
   138  	siaPathOne, err := skymodules.NewSiaPath("one")
   139  	if err != nil {
   140  		t.Fatal(err)
   141  	}
   142  	err = rt.renter.DeleteFile(siaPathOne)
   143  	// NOTE: using strings.Contains because errors.Contains does not recognize
   144  	// errors when errors.Extend is used
   145  	if !strings.Contains(err.Error(), filesystem.ErrNotExist.Error()) {
   146  		t.Errorf("Expected error to contain %v but got '%v'", filesystem.ErrNotExist, err)
   147  	}
   148  	// Delete the file.
   149  	siapath := rt.renter.staticFileSystem.FileSiaPath(entry)
   150  
   151  	if err := entry.Close(); err != nil {
   152  		t.Fatal(err)
   153  	}
   154  	err = rt.renter.DeleteFile(siapath)
   155  	if err != nil {
   156  		t.Fatal(err)
   157  	}
   158  	files, err := rt.renter.FileListCollect(skymodules.RootSiaPath(), true, false)
   159  	if err != nil {
   160  		t.Fatal(err)
   161  	}
   162  	if len(files) != 0 {
   163  		t.Error("file was deleted, but is still reported in FileList")
   164  	}
   165  	// Confirm that file was removed from SiaFileSet
   166  	_, err = rt.renter.staticFileSystem.OpenSiaFile(siapath)
   167  	if err == nil {
   168  		t.Fatal("Deleted file still found in staticFileSet")
   169  	}
   170  
   171  	// Put a file in the renter, then rename it.
   172  	entry2, err := rt.renter.newRenterTestFile()
   173  	if err != nil {
   174  		t.Fatal(err)
   175  	}
   176  	siaPath1, err := skymodules.NewSiaPath("1")
   177  	if err != nil {
   178  		t.Fatal(err)
   179  	}
   180  	err = rt.renter.RenameFile(rt.renter.staticFileSystem.FileSiaPath(entry2), siaPath1) // set name to "1"
   181  	if err != nil {
   182  		t.Fatal(err)
   183  	}
   184  	siapath2 := rt.renter.staticFileSystem.FileSiaPath(entry2)
   185  	entry2.Close()
   186  	siapath2 = rt.renter.staticFileSystem.FileSiaPath(entry2)
   187  	err = rt.renter.RenameFile(siapath2, siaPathOne)
   188  	if err != nil {
   189  		t.Fatal(err)
   190  	}
   191  	// Call delete on the previous name.
   192  	err = rt.renter.DeleteFile(siaPath1)
   193  	// NOTE: using strings.Contains because errors.Contains does not recognize
   194  	// errors when errors.Extend is used
   195  	if !strings.Contains(err.Error(), filesystem.ErrNotExist.Error()) {
   196  		t.Errorf("Expected error to contain %v but got '%v'", filesystem.ErrNotExist, err)
   197  	}
   198  	// Call delete on the new name.
   199  	err = rt.renter.DeleteFile(siaPathOne)
   200  	if err != nil {
   201  		t.Error(err)
   202  	}
   203  
   204  	// Check that all .sia files have been deleted.
   205  	var walkStr string
   206  	rt.renter.staticFileSystem.Walk(skymodules.RootSiaPath(), func(path string, _ os.FileInfo, _ error) error {
   207  		// capture only .sia files
   208  		if filepath.Ext(path) == ".sia" {
   209  			rel, _ := filepath.Rel(rt.renter.staticFileSystem.Root(), path) // strip testdir prefix
   210  			walkStr += rel
   211  		}
   212  		return nil
   213  	})
   214  	expWalkStr := ""
   215  	if walkStr != expWalkStr {
   216  		t.Fatalf("Bad walk string: expected %q, got %q", expWalkStr, walkStr)
   217  	}
   218  }
   219  
   220  // TestRenterDeleteFileMissingParent tries to delete a file for which the parent
   221  // has been deleted before.
   222  func TestRenterDeleteFileMissingParent(t *testing.T) {
   223  	if testing.Short() {
   224  		t.SkipNow()
   225  	}
   226  	rt, err := newRenterTester(t.Name())
   227  	if err != nil {
   228  		t.Fatal(err)
   229  	}
   230  	defer func() {
   231  		if err := rt.Close(); err != nil {
   232  			t.Fatal(err)
   233  		}
   234  	}()
   235  
   236  	// Put a file in the renter.
   237  	siaPath, err := skymodules.NewSiaPath("parent/file")
   238  	if err != nil {
   239  		t.Fatal(err)
   240  	}
   241  	dirSiaPath, err := siaPath.Dir()
   242  	if err != nil {
   243  		t.Fatal(err)
   244  	}
   245  	siaPath, rsc := testingFileParams()
   246  	up := skymodules.FileUploadParams{
   247  		Source:      "",
   248  		SiaPath:     siaPath,
   249  		ErasureCode: rsc,
   250  	}
   251  	err = rt.renter.staticFileSystem.NewSiaFile(up.SiaPath, up.Source, up.ErasureCode, crypto.GenerateSiaKey(crypto.RandomCipherType()), 1000, persist.DefaultDiskPermissionsTest)
   252  	if err != nil {
   253  		t.Fatal(err)
   254  	}
   255  	// Delete the parent.
   256  	err = rt.renter.staticFileSystem.DeleteFile(dirSiaPath)
   257  	// NOTE: using strings.Contains because errors.Contains does not recognize
   258  	// errors when errors.Extend is used
   259  	if !strings.Contains(err.Error(), filesystem.ErrNotExist.Error()) {
   260  		t.Errorf("Expected error to contain %v but got '%v'", filesystem.ErrNotExist, err)
   261  	}
   262  	// Delete the file. This should not return an error since it's already
   263  	// deleted implicitly.
   264  	if err := rt.renter.staticFileSystem.DeleteFile(up.SiaPath); err != nil {
   265  		t.Fatal(err)
   266  	}
   267  }
   268  
   269  // TestRenterFileList probes the FileList method of the renter type.
   270  func TestRenterFileList(t *testing.T) {
   271  	if testing.Short() {
   272  		t.SkipNow()
   273  	}
   274  	rt, err := newRenterTester(t.Name())
   275  	if err != nil {
   276  		t.Fatal(err)
   277  	}
   278  	defer func() {
   279  		if err := rt.Close(); err != nil {
   280  			t.Fatal(err)
   281  		}
   282  	}()
   283  
   284  	// Get the file list of an empty renter.
   285  	files, err := rt.renter.FileListCollect(skymodules.RootSiaPath(), true, false)
   286  	if err != nil {
   287  		t.Fatal(err)
   288  	}
   289  	if len(files) != 0 {
   290  		t.Fatal("FileList has non-zero length for empty renter?")
   291  	}
   292  
   293  	// Put a file in the renter.
   294  	entry1, _ := rt.renter.newRenterTestFile()
   295  	files, err = rt.renter.FileListCollect(skymodules.RootSiaPath(), true, false)
   296  	if err != nil {
   297  		t.Fatal(err)
   298  	}
   299  	if len(files) != 1 {
   300  		t.Fatal("FileList is not returning the only file in the renter")
   301  	}
   302  	entry1SP := rt.renter.staticFileSystem.FileSiaPath(entry1)
   303  	if !files[0].SiaPath.Equals(entry1SP) {
   304  		t.Error("FileList is not returning the correct filename for the only file")
   305  	}
   306  
   307  	// Put multiple files in the renter.
   308  	entry2, _ := rt.renter.newRenterTestFile()
   309  	entry2SP := rt.renter.staticFileSystem.FileSiaPath(entry2)
   310  	files, err = rt.renter.FileListCollect(skymodules.RootSiaPath(), true, false)
   311  	if err != nil {
   312  		t.Fatal(err)
   313  	}
   314  	if len(files) != 2 {
   315  		t.Fatalf("Expected %v files, got %v", 2, len(files))
   316  	}
   317  	files, err = rt.renter.FileListCollect(skymodules.RootSiaPath(), true, false)
   318  	if err != nil {
   319  		t.Fatal(err)
   320  	}
   321  	if !((files[0].SiaPath.Equals(entry1SP) || files[0].SiaPath.Equals(entry2SP)) &&
   322  		(files[1].SiaPath.Equals(entry1SP) || files[1].SiaPath.Equals(entry2SP)) &&
   323  		(files[0].SiaPath != files[1].SiaPath)) {
   324  		t.Log("files[0].SiaPath", files[0].SiaPath)
   325  		t.Log("files[1].SiaPath", files[1].SiaPath)
   326  		t.Log("file1.SiaPath()", rt.renter.staticFileSystem.FileSiaPath(entry1).String())
   327  		t.Log("file2.SiaPath()", rt.renter.staticFileSystem.FileSiaPath(entry2).String())
   328  		t.Error("FileList is returning wrong names for the files")
   329  	}
   330  }
   331  
   332  // TestRenterRenameFile probes the rename method of the renter.
   333  func TestRenterRenameFile(t *testing.T) {
   334  	if testing.Short() {
   335  		t.SkipNow()
   336  	}
   337  	rt, err := newRenterTester(t.Name())
   338  	if err != nil {
   339  		t.Fatal(err)
   340  	}
   341  	defer func() {
   342  		if err := rt.Close(); err != nil {
   343  			t.Fatal(err)
   344  		}
   345  	}()
   346  
   347  	// Rename a file that doesn't exist.
   348  	siaPath1, err := skymodules.NewSiaPath("1")
   349  	if err != nil {
   350  		t.Fatal(err)
   351  	}
   352  	siaPath1a, err := skymodules.NewSiaPath("1a")
   353  	if err != nil {
   354  		t.Fatal(err)
   355  	}
   356  	err = rt.renter.RenameFile(siaPath1, siaPath1a)
   357  	if err.Error() != filesystem.ErrNotExist.Error() {
   358  		t.Errorf("Expected '%v' got '%v'", filesystem.ErrNotExist, err)
   359  	}
   360  
   361  	// Get the filesystem.
   362  	sfs := rt.renter.staticFileSystem
   363  
   364  	// Rename a file that does exist.
   365  	entry, _ := rt.renter.newRenterTestFile()
   366  	var sp skymodules.SiaPath
   367  	if err := sp.FromSysPath(entry.SiaFilePath(), sfs.DirPath(skymodules.RootSiaPath())); err != nil {
   368  		t.Fatal(err)
   369  	}
   370  	err = rt.renter.RenameFile(sp, siaPath1)
   371  	if err != nil {
   372  		t.Fatal(err)
   373  	}
   374  	err = rt.renter.RenameFile(siaPath1, siaPath1a)
   375  	if err != nil {
   376  		t.Fatal(err)
   377  	}
   378  	files, err := rt.renter.FileListCollect(skymodules.RootSiaPath(), true, false)
   379  	if err != nil {
   380  		t.Fatal(err)
   381  	}
   382  	if len(files) != 1 {
   383  		t.Fatal("FileList has unexpected number of files:", len(files))
   384  	}
   385  	if !files[0].SiaPath.Equals(siaPath1a) {
   386  		t.Errorf("RenameFile failed: expected %v, got %v", siaPath1a.String(), files[0].SiaPath)
   387  	}
   388  	// Confirm SiaFileSet was updated
   389  	_, err = rt.renter.staticFileSystem.OpenSiaFile(siaPath1a)
   390  	if err != nil {
   391  		t.Fatal("renter staticFileSet not updated to new file name:", err)
   392  	}
   393  	_, err = rt.renter.staticFileSystem.OpenSiaFile(siaPath1)
   394  	if err == nil {
   395  		t.Fatal("old name not removed from renter staticFileSet")
   396  	}
   397  	// Rename a file to an existing name.
   398  	entry2, err := rt.renter.newRenterTestFile()
   399  	if err != nil {
   400  		t.Fatal(err)
   401  	}
   402  	var sp2 skymodules.SiaPath
   403  	if err := sp2.FromSysPath(entry2.SiaFilePath(), sfs.DirPath(skymodules.RootSiaPath())); err != nil {
   404  		t.Fatal(err)
   405  	}
   406  	err = rt.renter.RenameFile(sp2, siaPath1) // Rename to "1"
   407  	if err != nil {
   408  		t.Fatal(err)
   409  	}
   410  	entry2.Close()
   411  	err = rt.renter.RenameFile(siaPath1, siaPath1a)
   412  	if !errors.Contains(err, filesystem.ErrExists) {
   413  		t.Fatal("Expecting ErrExists, got", err)
   414  	}
   415  	// Rename a file to the same name.
   416  	err = rt.renter.RenameFile(siaPath1, siaPath1)
   417  	if !errors.Contains(err, filesystem.ErrExists) {
   418  		t.Fatal("Expecting ErrExists, got", err)
   419  	}
   420  
   421  	// Confirm ability to rename file
   422  	siaPath1b, err := skymodules.NewSiaPath("1b")
   423  	if err != nil {
   424  		t.Fatal(err)
   425  	}
   426  	err = rt.renter.RenameFile(siaPath1, siaPath1b)
   427  	if err != nil {
   428  		t.Fatal(err)
   429  	}
   430  	// Rename file that would create a directory
   431  	siaPathWithDir, err := skymodules.NewSiaPath("new/name/with/dir/test")
   432  	if err != nil {
   433  		t.Fatal(err)
   434  	}
   435  	err = rt.renter.RenameFile(siaPath1b, siaPathWithDir)
   436  	if err != nil {
   437  		t.Fatal(err)
   438  	}
   439  
   440  	// Confirm directory metadatas exist
   441  	for !siaPathWithDir.Equals(skymodules.RootSiaPath()) {
   442  		siaPathWithDir, err = siaPathWithDir.Dir()
   443  		if err != nil {
   444  			t.Fatal(err)
   445  		}
   446  		_, err = rt.renter.staticFileSystem.OpenSiaDir(siaPathWithDir)
   447  		if err != nil {
   448  			t.Fatal(err)
   449  		}
   450  	}
   451  }
   452  
   453  // TestRenterFileDir tests that the renter files are uploaded to the files
   454  // directory and not the root directory of the renter.
   455  func TestRenterFileDir(t *testing.T) {
   456  	if testing.Short() {
   457  		t.SkipNow()
   458  	}
   459  	rt, err := newRenterTester(t.Name())
   460  	if err != nil {
   461  		t.Fatal(err)
   462  	}
   463  	defer func() {
   464  		if err := rt.Close(); err != nil {
   465  			t.Fatal(err)
   466  		}
   467  	}()
   468  
   469  	// Create local file to upload
   470  	localDir := filepath.Join(rt.dir, "files")
   471  	if err := os.MkdirAll(localDir, 0700); err != nil {
   472  		t.Fatal(err)
   473  	}
   474  	size := 100
   475  	fileName := fmt.Sprintf("%dbytes %s", size, hex.EncodeToString(fastrand.Bytes(4)))
   476  	source := filepath.Join(localDir, fileName)
   477  	bytes := fastrand.Bytes(size)
   478  	if err := ioutil.WriteFile(source, bytes, 0600); err != nil {
   479  		t.Fatal(err)
   480  	}
   481  
   482  	// Upload local file
   483  	ec := skymodules.NewRSCodeDefault()
   484  	siaPath, err := skymodules.NewSiaPath(fileName)
   485  	if err != nil {
   486  		t.Fatal(err)
   487  	}
   488  	params := skymodules.FileUploadParams{
   489  		Source:      source,
   490  		SiaPath:     siaPath,
   491  		ErasureCode: ec,
   492  	}
   493  	err = rt.renter.Upload(params)
   494  	if err != nil {
   495  		t.Fatal("failed to upload file:", err)
   496  	}
   497  
   498  	// Get file and check siapath
   499  	f, err := rt.renter.File(siaPath)
   500  	if err != nil {
   501  		t.Fatal(err)
   502  	}
   503  	if !f.SiaPath.Equals(siaPath) {
   504  		t.Fatalf("siapath not set as expected: got %v expected %v", f.SiaPath, fileName)
   505  	}
   506  
   507  	// Confirm .sia file exists on disk in the SiapathRoot directory
   508  	renterDir := filepath.Join(rt.dir, skymodules.RenterDir)
   509  	siapathRootDir := filepath.Join(renterDir, skymodules.FileSystemRoot)
   510  	fullPath := siaPath.SiaFileSysPath(siapathRootDir)
   511  	if _, err := os.Stat(fullPath); os.IsNotExist(err) {
   512  		t.Fatal("No .sia file found on disk")
   513  	}
   514  }