github.com/YousefHaggyHeroku/pack@v1.5.5/internal/archive/archive_test.go (about)

     1  package archive_test
     2  
     3  import (
     4  	"archive/tar"
     5  	"io/ioutil"
     6  	"math/rand"
     7  	"net"
     8  	"os"
     9  	"path/filepath"
    10  	"runtime"
    11  	"strings"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/pkg/errors"
    16  
    17  	"github.com/heroku/color"
    18  	"github.com/sclevine/spec"
    19  	"github.com/sclevine/spec/report"
    20  
    21  	"github.com/YousefHaggyHeroku/pack/internal/archive"
    22  	h "github.com/YousefHaggyHeroku/pack/testhelpers"
    23  )
    24  
    25  func TestArchive(t *testing.T) {
    26  	color.Disable(true)
    27  	defer color.Disable(false)
    28  	rand.Seed(time.Now().UTC().UnixNano())
    29  	spec.Run(t, "Archive", testArchive, spec.Sequential(), spec.Report(report.Terminal{}))
    30  }
    31  
    32  func testArchive(t *testing.T, when spec.G, it spec.S) {
    33  	var (
    34  		tmpDir string
    35  	)
    36  
    37  	it.Before(func() {
    38  		var err error
    39  		tmpDir, err = ioutil.TempDir("", "create-tar-test")
    40  		if err != nil {
    41  			t.Fatalf("failed to create tmp dir %s: %s", tmpDir, err)
    42  		}
    43  	})
    44  
    45  	it.After(func() {
    46  		if err := os.RemoveAll(tmpDir); err != nil {
    47  			t.Fatalf("failed to clean up tmp dir %s: %s", tmpDir, err)
    48  		}
    49  	})
    50  
    51  	when("#ReadDirAsTar", func() {
    52  		var src string
    53  		it.Before(func() {
    54  			src = filepath.Join("testdata", "dir-to-tar")
    55  		})
    56  
    57  		it("returns a TarReader of the dir", func() {
    58  			rc := archive.ReadDirAsTar(src, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, nil)
    59  
    60  			tr := tar.NewReader(rc)
    61  			verify := h.NewTarVerifier(t, tr, 1234, 2345)
    62  			verify.NextFile("/nested/dir/dir-in-archive/some-file.txt", "some-content", int64(os.ModePerm))
    63  			verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", int64(os.ModePerm))
    64  			if runtime.GOOS != "windows" {
    65  				verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt")
    66  				verify.NoMoreFilesExist()
    67  				h.AssertNil(t, rc.Close())
    68  			}
    69  		})
    70  
    71  		it("returns error if closed multiple times", func() {
    72  			rc := archive.ReadDirAsTar(src, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, func(s string) bool { return false })
    73  			tr := tar.NewReader(rc)
    74  			verify := h.NewTarVerifier(t, tr, 1234, 2345)
    75  			verify.NoMoreFilesExist()
    76  			h.AssertNil(t, rc.Close())
    77  			h.AssertError(t, rc.Close(), "reader already closed")
    78  		})
    79  	})
    80  
    81  	when("#ReadZipAsTar", func() {
    82  		var src string
    83  		it.Before(func() {
    84  			src = filepath.Join("testdata", "zip-to-tar.zip")
    85  		})
    86  
    87  		it("returns a TarReader of the dir", func() {
    88  			rc := archive.ReadZipAsTar(src, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, nil)
    89  
    90  			tr := tar.NewReader(rc)
    91  			verify := h.NewTarVerifier(t, tr, 1234, 2345)
    92  			verify.NextFile("/nested/dir/dir-in-archive/some-file.txt", "some-content", int64(os.ModePerm))
    93  			verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", int64(os.ModePerm))
    94  			verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt")
    95  
    96  			verify.NoMoreFilesExist()
    97  			h.AssertNil(t, rc.Close())
    98  		})
    99  	})
   100  
   101  	when("#ReadTarEntry", func() {
   102  		var (
   103  			err     error
   104  			tarFile *os.File
   105  		)
   106  		it.Before(func() {
   107  			tarFile, err = ioutil.TempFile(tmpDir, "file.tgz")
   108  			h.AssertNil(t, err)
   109  		})
   110  
   111  		it.After(func() {
   112  			_ = tarFile.Close()
   113  		})
   114  
   115  		when("tgz has the path", func() {
   116  			it.Before(func() {
   117  				err = archive.CreateSingleFileTar(tarFile.Name(), "file1", "file-1 content")
   118  				h.AssertNil(t, err)
   119  			})
   120  
   121  			it("returns the file contents", func() {
   122  				_, contents, err := archive.ReadTarEntry(tarFile, "file1")
   123  				h.AssertNil(t, err)
   124  				h.AssertEq(t, string(contents), "file-1 content")
   125  			})
   126  		})
   127  
   128  		when("tgz has ./path", func() {
   129  			it.Before(func() {
   130  				err = archive.CreateSingleFileTar(tarFile.Name(), "./file1", "file-1 content")
   131  				h.AssertNil(t, err)
   132  			})
   133  
   134  			it("returns the file contents", func() {
   135  				_, contents, err := archive.ReadTarEntry(tarFile, "file1")
   136  				h.AssertNil(t, err)
   137  				h.AssertEq(t, string(contents), "file-1 content")
   138  			})
   139  		})
   140  
   141  		when("path doesn't exist", func() {
   142  			it.Before(func() {
   143  				err = archive.CreateSingleFileTar(tarFile.Name(), "file1", "file-1 content")
   144  				h.AssertNil(t, err)
   145  			})
   146  
   147  			it("returns the file contents", func() {
   148  				_, _, err := archive.ReadTarEntry(tarFile, "file2")
   149  				h.AssertError(t, err, "could not find entry path")
   150  				h.AssertTrue(t, archive.IsEntryNotExist(err))
   151  			})
   152  		})
   153  
   154  		when("reader isn't tar", func() {
   155  			it("returns the file contents", func() {
   156  				reader := strings.NewReader("abcde")
   157  				_, _, err := archive.ReadTarEntry(reader, "file1")
   158  				h.AssertError(t, err, "get next tar entry")
   159  			})
   160  		})
   161  	})
   162  
   163  	when("#CreateSingleFileTarReader", func() {
   164  		it("returns the file contents", func() {
   165  			rc := archive.CreateSingleFileTarReader("file1", "file-1 content")
   166  			_, contents, err := archive.ReadTarEntry(rc, "file1")
   167  			h.AssertNil(t, err)
   168  			h.AssertEq(t, string(contents), "file-1 content")
   169  		})
   170  	})
   171  
   172  	when("#IsEntryNotExist", func() {
   173  		it("works", func() {
   174  			h.AssertTrue(t, archive.IsEntryNotExist(errors.Wrap(archive.ErrEntryNotExist, "something")))
   175  			h.AssertFalse(t, archive.IsEntryNotExist(errors.New("something not err not exist")))
   176  		})
   177  	})
   178  
   179  	when("#WriteDirToTar", func() {
   180  		var src string
   181  		it.Before(func() {
   182  			src = filepath.Join("testdata", "dir-to-tar")
   183  		})
   184  
   185  		when("mode is set to 0777", func() {
   186  			it("writes a tar to the dest dir with 0777", func() {
   187  				fh, err := os.Create(filepath.Join(tmpDir, "some.tar"))
   188  				h.AssertNil(t, err)
   189  
   190  				tw := tar.NewWriter(fh)
   191  
   192  				err = archive.WriteDirToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, nil)
   193  				h.AssertNil(t, err)
   194  				h.AssertNil(t, tw.Close())
   195  				h.AssertNil(t, fh.Close())
   196  
   197  				file, err := os.Open(filepath.Join(tmpDir, "some.tar"))
   198  				h.AssertNil(t, err)
   199  				defer file.Close()
   200  
   201  				tr := tar.NewReader(file)
   202  
   203  				verify := h.NewTarVerifier(t, tr, 1234, 2345)
   204  				verify.NextFile("/nested/dir/dir-in-archive/some-file.txt", "some-content", int64(os.ModePerm))
   205  				verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", int64(os.ModePerm))
   206  				if runtime.GOOS != "windows" {
   207  					verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt")
   208  				}
   209  			})
   210  		})
   211  
   212  		when("mode is set to -1", func() {
   213  			it("writes a tar to the dest dir with preexisting file mode", func() {
   214  				fh, err := os.Create(filepath.Join(tmpDir, "some.tar"))
   215  				h.AssertNil(t, err)
   216  
   217  				tw := tar.NewWriter(fh)
   218  
   219  				err = archive.WriteDirToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, -1, true, nil)
   220  				h.AssertNil(t, err)
   221  				h.AssertNil(t, tw.Close())
   222  				h.AssertNil(t, fh.Close())
   223  
   224  				file, err := os.Open(filepath.Join(tmpDir, "some.tar"))
   225  				h.AssertNil(t, err)
   226  				defer file.Close()
   227  
   228  				tr := tar.NewReader(file)
   229  
   230  				verify := h.NewTarVerifier(t, tr, 1234, 2345)
   231  				verify.NextFile("/nested/dir/dir-in-archive/some-file.txt", "some-content", fileMode(t, filepath.Join(src, "some-file.txt")))
   232  				verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", fileMode(t, filepath.Join(src, "sub-dir")))
   233  				if runtime.GOOS != "windows" {
   234  					verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt")
   235  				}
   236  			})
   237  		})
   238  
   239  		when("has file filter", func() {
   240  			it("does not add files against the file filter", func() {
   241  				tarFile := filepath.Join(tmpDir, "some.tar")
   242  				fh, err := os.Create(tarFile)
   243  				h.AssertNil(t, err)
   244  
   245  				tw := tar.NewWriter(fh)
   246  
   247  				err = archive.WriteDirToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, func(path string) bool {
   248  					return !strings.Contains(path, "some-file.txt")
   249  				})
   250  				h.AssertNil(t, err)
   251  				h.AssertNil(t, tw.Close())
   252  				h.AssertNil(t, fh.Close())
   253  
   254  				file, err := os.Open(filepath.Join(tmpDir, "some.tar"))
   255  				h.AssertNil(t, err)
   256  				defer file.Close()
   257  
   258  				tr := tar.NewReader(file)
   259  
   260  				verify := h.NewTarVerifier(t, tr, 1234, 2345)
   261  				verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", int64(os.ModePerm))
   262  				if runtime.GOOS != "windows" {
   263  					verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt")
   264  				}
   265  			})
   266  		})
   267  
   268  		when("normalize mod time is false", func() {
   269  			it("does not normalize mod times", func() {
   270  				tarFile := filepath.Join(tmpDir, "some.tar")
   271  				fh, err := os.Create(tarFile)
   272  				h.AssertNil(t, err)
   273  
   274  				tw := tar.NewWriter(fh)
   275  
   276  				err = archive.WriteDirToTar(tw, src, "/foo", 1234, 2345, 0777, false, nil)
   277  				h.AssertNil(t, err)
   278  				h.AssertNil(t, tw.Close())
   279  				h.AssertNil(t, fh.Close())
   280  
   281  				h.AssertOnTarEntry(t, tarFile, "/foo/some-file.txt",
   282  					h.DoesNotHaveModTime(archive.NormalizedDateTime),
   283  				)
   284  			})
   285  		})
   286  
   287  		when("normalize mod time is true", func() {
   288  			it("normalizes mod times", func() {
   289  				tarFile := filepath.Join(tmpDir, "some.tar")
   290  				fh, err := os.Create(tarFile)
   291  				h.AssertNil(t, err)
   292  
   293  				tw := tar.NewWriter(fh)
   294  
   295  				err = archive.WriteDirToTar(tw, src, "/foo", 1234, 2345, 0777, true, nil)
   296  				h.AssertNil(t, err)
   297  				h.AssertNil(t, tw.Close())
   298  				h.AssertNil(t, fh.Close())
   299  
   300  				h.AssertOnTarEntry(t, tarFile, "/foo/some-file.txt",
   301  					h.HasModTime(archive.NormalizedDateTime),
   302  				)
   303  			})
   304  		})
   305  
   306  		when("is posix", func() {
   307  			it.Before(func() {
   308  				h.SkipIf(t, runtime.GOOS == "windows", "Skipping on windows")
   309  			})
   310  
   311  			when("socket is present", func() {
   312  				var (
   313  					err        error
   314  					tmpSrcDir  string
   315  					fakeSocket net.Listener
   316  				)
   317  
   318  				it.Before(func() {
   319  					tmpSrcDir, err = ioutil.TempDir("", "socket-test")
   320  					h.AssertNil(t, err)
   321  
   322  					fakeSocket, err = net.Listen(
   323  						"unix",
   324  						filepath.Join(tmpSrcDir, "fake-socket"),
   325  					)
   326  
   327  					err = ioutil.WriteFile(filepath.Join(tmpSrcDir, "fake-file"), []byte("some-content"), 0777)
   328  					h.AssertNil(t, err)
   329  				})
   330  
   331  				it.After(func() {
   332  					os.RemoveAll(tmpSrcDir)
   333  					fakeSocket.Close()
   334  				})
   335  
   336  				it("silently ignore socket", func() {
   337  					fh, err := os.Create(filepath.Join(tmpDir, "some.tar"))
   338  					h.AssertNil(t, err)
   339  
   340  					tw := tar.NewWriter(fh)
   341  
   342  					err = archive.WriteDirToTar(tw, tmpSrcDir, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, nil)
   343  					h.AssertNil(t, err)
   344  					h.AssertNil(t, tw.Close())
   345  					h.AssertNil(t, fh.Close())
   346  
   347  					file, err := os.Open(filepath.Join(tmpDir, "some.tar"))
   348  					h.AssertNil(t, err)
   349  					defer file.Close()
   350  
   351  					tr := tar.NewReader(file)
   352  
   353  					verify := h.NewTarVerifier(t, tr, 1234, 2345)
   354  					verify.NextFile(
   355  						"/nested/dir/dir-in-archive/fake-file",
   356  						"some-content",
   357  						0777,
   358  					)
   359  					verify.NoMoreFilesExist()
   360  				})
   361  			})
   362  		})
   363  	})
   364  
   365  	when("#WriteZipToTar", func() {
   366  		var src string
   367  		it.Before(func() {
   368  			src = filepath.Join("testdata", "zip-to-tar.zip")
   369  		})
   370  
   371  		when("mode is set to 0777", func() {
   372  			it("writes a tar to the dest dir with 0777", func() {
   373  				fh, err := os.Create(filepath.Join(tmpDir, "some.tar"))
   374  				h.AssertNil(t, err)
   375  
   376  				tw := tar.NewWriter(fh)
   377  
   378  				err = archive.WriteZipToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, nil)
   379  				h.AssertNil(t, err)
   380  				h.AssertNil(t, tw.Close())
   381  				h.AssertNil(t, fh.Close())
   382  
   383  				file, err := os.Open(filepath.Join(tmpDir, "some.tar"))
   384  				h.AssertNil(t, err)
   385  				defer file.Close()
   386  
   387  				tr := tar.NewReader(file)
   388  
   389  				verify := h.NewTarVerifier(t, tr, 1234, 2345)
   390  				verify.NextFile("/nested/dir/dir-in-archive/some-file.txt", "some-content", 0777)
   391  				verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", 0777)
   392  				verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt")
   393  			})
   394  		})
   395  
   396  		when("mode is set to -1", func() {
   397  			it("writes a tar to the dest dir with preexisting file mode", func() {
   398  				fh, err := os.Create(filepath.Join(tmpDir, "some.tar"))
   399  				h.AssertNil(t, err)
   400  
   401  				tw := tar.NewWriter(fh)
   402  
   403  				err = archive.WriteZipToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, -1, true, nil)
   404  				h.AssertNil(t, err)
   405  				h.AssertNil(t, tw.Close())
   406  				h.AssertNil(t, fh.Close())
   407  
   408  				file, err := os.Open(filepath.Join(tmpDir, "some.tar"))
   409  				h.AssertNil(t, err)
   410  				defer file.Close()
   411  
   412  				tr := tar.NewReader(file)
   413  
   414  				verify := h.NewTarVerifier(t, tr, 1234, 2345)
   415  				verify.NextFile("/nested/dir/dir-in-archive/some-file.txt", "some-content", 0644)
   416  				verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", 0755)
   417  				verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt")
   418  			})
   419  
   420  			when("files are compressed in fat (MSDOS) format", func() {
   421  				it.Before(func() {
   422  					src = filepath.Join("testdata", "fat-zip-to-tar.zip")
   423  				})
   424  
   425  				it("writes a tar to the dest dir with 0777", func() {
   426  					fh, err := os.Create(filepath.Join(tmpDir, "some.tar"))
   427  					h.AssertNil(t, err)
   428  
   429  					tw := tar.NewWriter(fh)
   430  
   431  					err = archive.WriteZipToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, -1, true, nil)
   432  					h.AssertNil(t, err)
   433  					h.AssertNil(t, tw.Close())
   434  					h.AssertNil(t, fh.Close())
   435  
   436  					file, err := os.Open(filepath.Join(tmpDir, "some.tar"))
   437  					h.AssertNil(t, err)
   438  					defer file.Close()
   439  
   440  					tr := tar.NewReader(file)
   441  
   442  					verify := h.NewTarVerifier(t, tr, 1234, 2345)
   443  					verify.NextFile("/nested/dir/dir-in-archive/some-file.txt", "some-content", 0777)
   444  					verify.NoMoreFilesExist()
   445  				})
   446  			})
   447  		})
   448  
   449  		when("has file filter", func() {
   450  			it("follows it when adding files", func() {
   451  				fh, err := os.Create(filepath.Join(tmpDir, "some.tar"))
   452  				h.AssertNil(t, err)
   453  
   454  				tw := tar.NewWriter(fh)
   455  
   456  				err = archive.WriteZipToTar(tw, src, "/nested/dir/dir-in-archive", 1234, 2345, 0777, true, func(path string) bool {
   457  					return !strings.Contains(path, "some-file.txt")
   458  				})
   459  				h.AssertNil(t, err)
   460  				h.AssertNil(t, tw.Close())
   461  				h.AssertNil(t, fh.Close())
   462  
   463  				file, err := os.Open(filepath.Join(tmpDir, "some.tar"))
   464  				h.AssertNil(t, err)
   465  				defer file.Close()
   466  
   467  				tr := tar.NewReader(file)
   468  
   469  				verify := h.NewTarVerifier(t, tr, 1234, 2345)
   470  				verify.NextDirectory("/nested/dir/dir-in-archive/sub-dir", 0777)
   471  				verify.NextSymLink("/nested/dir/dir-in-archive/sub-dir/link-file", "../some-file.txt")
   472  			})
   473  		})
   474  
   475  		when("normalize mod time is false", func() {
   476  			it("does not normalize mod times", func() {
   477  				tarFile := filepath.Join(tmpDir, "some.tar")
   478  				fh, err := os.Create(tarFile)
   479  				h.AssertNil(t, err)
   480  
   481  				tw := tar.NewWriter(fh)
   482  
   483  				err = archive.WriteZipToTar(tw, src, "/foo", 1234, 2345, 0777, false, nil)
   484  				h.AssertNil(t, err)
   485  				h.AssertNil(t, tw.Close())
   486  				h.AssertNil(t, fh.Close())
   487  
   488  				h.AssertOnTarEntry(t, tarFile, "/foo/some-file.txt",
   489  					h.DoesNotHaveModTime(archive.NormalizedDateTime),
   490  				)
   491  			})
   492  		})
   493  
   494  		when("normalize mod time is true", func() {
   495  			it("normalizes mod times", func() {
   496  				tarFile := filepath.Join(tmpDir, "some.tar")
   497  				fh, err := os.Create(tarFile)
   498  				h.AssertNil(t, err)
   499  
   500  				tw := tar.NewWriter(fh)
   501  
   502  				err = archive.WriteZipToTar(tw, src, "/foo", 1234, 2345, 0777, true, nil)
   503  				h.AssertNil(t, err)
   504  				h.AssertNil(t, tw.Close())
   505  				h.AssertNil(t, fh.Close())
   506  
   507  				h.AssertOnTarEntry(t, tarFile, "/foo/some-file.txt",
   508  					h.HasModTime(archive.NormalizedDateTime),
   509  				)
   510  			})
   511  		})
   512  	})
   513  
   514  	when("#IsZip", func() {
   515  		when("file is a zip file", func() {
   516  			it("returns true", func() {
   517  				path := filepath.Join("testdata", "zip-to-tar.zip")
   518  
   519  				file, err := os.Open(path)
   520  				h.AssertNil(t, err)
   521  				defer file.Close()
   522  
   523  				isZip, err := archive.IsZip(file)
   524  				h.AssertNil(t, err)
   525  				h.AssertTrue(t, isZip)
   526  			})
   527  		})
   528  
   529  		when("file is a jar file", func() {
   530  			it("returns true", func() {
   531  				path := filepath.Join("testdata", "jar-file.jar")
   532  
   533  				file, err := os.Open(path)
   534  				h.AssertNil(t, err)
   535  				defer file.Close()
   536  
   537  				isZip, err := archive.IsZip(file)
   538  				h.AssertNil(t, err)
   539  				h.AssertTrue(t, isZip)
   540  			})
   541  		})
   542  
   543  		when("file is not a zip file", func() {
   544  			when("file has some content", func() {
   545  				it("returns false", func() {
   546  					file, err := ioutil.TempFile(tmpDir, "file.txt")
   547  					h.AssertNil(t, err)
   548  					defer file.Close()
   549  
   550  					err = ioutil.WriteFile(file.Name(), []byte("content"), os.ModePerm)
   551  					h.AssertNil(t, err)
   552  
   553  					isZip, err := archive.IsZip(file)
   554  					h.AssertNil(t, err)
   555  					h.AssertFalse(t, isZip)
   556  				})
   557  			})
   558  
   559  			when("file doesn't have content", func() {
   560  				it("returns false", func() {
   561  					file, err := ioutil.TempFile(tmpDir, "file.txt")
   562  					h.AssertNil(t, err)
   563  					defer file.Close()
   564  
   565  					isZip, err := archive.IsZip(file)
   566  					h.AssertNil(t, err)
   567  					h.AssertFalse(t, isZip)
   568  				})
   569  			})
   570  		})
   571  
   572  		when("reader is closed", func() {
   573  			it("returns error", func() {
   574  				file, err := ioutil.TempFile(tmpDir, "file.txt")
   575  				h.AssertNil(t, err)
   576  				err = file.Close()
   577  				h.AssertNil(t, err)
   578  
   579  				isZip, err := archive.IsZip(file)
   580  				h.AssertError(t, err, os.ErrClosed.Error())
   581  				h.AssertFalse(t, isZip)
   582  			})
   583  		})
   584  	})
   585  }
   586  
   587  func fileMode(t *testing.T, path string) int64 {
   588  	t.Helper()
   589  	info, err := os.Stat(path)
   590  	if err != nil {
   591  		t.Fatalf("failed to stat %s", path)
   592  	}
   593  	mode := int64(info.Mode() & os.ModePerm)
   594  	return mode
   595  }