1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: Apache-2.0
23 "git.curoverse.com/arvados.git/sdk/go/arvadostest"
24 check "gopkg.in/check.v1"
27 var _ = check.Suite(&CollectionFSSuite{})
29 type keepClientStub struct {
30 blocks map[string][]byte
34 var errStub404 = errors.New("404 block not found")
36 func (kcs *keepClientStub) ReadAt(locator string, p []byte, off int) (int, error) {
39 buf := kcs.blocks[locator[:32]]
43 return copy(p, buf[off:]), nil
46 func (kcs *keepClientStub) PutB(p []byte) (string, int, error) {
47 locator := fmt.Sprintf("%x+%d+A12345@abcde", md5.Sum(p), len(p))
48 buf := make([]byte, len(p))
52 kcs.blocks[locator[:32]] = buf
53 return locator, 1, nil
56 type CollectionFSSuite struct {
59 fs CollectionFileSystem
63 func (s *CollectionFSSuite) SetUpTest(c *check.C) {
64 s.client = NewClientFromEnv()
65 err := s.client.RequestAndDecode(&s.coll, "GET", "arvados/v1/collections/"+arvadostest.FooAndBarFilesInDirUUID, nil, nil)
66 c.Assert(err, check.IsNil)
67 s.kc = &keepClientStub{
68 blocks: map[string][]byte{
69 "3858f62230ac3c915f300c664312c63f": []byte("foobar"),
71 s.fs, err = s.coll.FileSystem(s.client, s.kc)
72 c.Assert(err, check.IsNil)
75 func (s *CollectionFSSuite) TestHttpFileSystemInterface(c *check.C) {
76 _, ok := s.fs.(http.FileSystem)
77 c.Check(ok, check.Equals, true)
80 func (s *CollectionFSSuite) TestColonInFilename(c *check.C) {
81 fs, err := (&Collection{
82 ManifestText: "./foo:foo 3858f62230ac3c915f300c664312c63f+3 0:3:bar:bar\n",
83 }).FileSystem(s.client, s.kc)
84 c.Assert(err, check.IsNil)
86 f, err := fs.Open("/foo:foo")
87 c.Assert(err, check.IsNil)
89 fis, err := f.Readdir(0)
90 c.Check(err, check.IsNil)
91 c.Check(len(fis), check.Equals, 1)
92 c.Check(fis[0].Name(), check.Equals, "bar:bar")
95 func (s *CollectionFSSuite) TestReaddirFull(c *check.C) {
96 f, err := s.fs.Open("/dir1")
97 c.Assert(err, check.IsNil)
100 c.Assert(err, check.IsNil)
101 c.Check(st.Size(), check.Equals, int64(2))
102 c.Check(st.IsDir(), check.Equals, true)
104 fis, err := f.Readdir(0)
105 c.Check(err, check.IsNil)
106 c.Check(len(fis), check.Equals, 2)
108 c.Check(fis[0].Size(), check.Equals, int64(3))
112 func (s *CollectionFSSuite) TestReaddirLimited(c *check.C) {
113 f, err := s.fs.Open("./dir1")
114 c.Assert(err, check.IsNil)
116 fis, err := f.Readdir(1)
117 c.Check(err, check.IsNil)
118 c.Check(len(fis), check.Equals, 1)
120 c.Check(fis[0].Size(), check.Equals, int64(3))
123 fis, err = f.Readdir(1)
124 c.Check(err, check.IsNil)
125 c.Check(len(fis), check.Equals, 1)
127 c.Check(fis[0].Size(), check.Equals, int64(3))
130 fis, err = f.Readdir(1)
131 c.Check(len(fis), check.Equals, 0)
132 c.Check(err, check.NotNil)
133 c.Check(err, check.Equals, io.EOF)
135 f, err = s.fs.Open("dir1")
136 c.Assert(err, check.IsNil)
137 fis, err = f.Readdir(1)
138 c.Check(len(fis), check.Equals, 1)
139 c.Assert(err, check.IsNil)
140 fis, err = f.Readdir(2)
141 c.Check(len(fis), check.Equals, 1)
142 c.Assert(err, check.IsNil)
143 fis, err = f.Readdir(2)
144 c.Check(len(fis), check.Equals, 0)
145 c.Assert(err, check.Equals, io.EOF)
148 func (s *CollectionFSSuite) TestPathMunge(c *check.C) {
149 for _, path := range []string{".", "/", "./", "///", "/../", "/./.."} {
150 f, err := s.fs.Open(path)
151 c.Assert(err, check.IsNil)
154 c.Assert(err, check.IsNil)
155 c.Check(st.Size(), check.Equals, int64(1))
156 c.Check(st.IsDir(), check.Equals, true)
158 for _, path := range []string{"/dir1", "dir1", "./dir1", "///dir1//.//", "../dir1/../dir1/"} {
160 f, err := s.fs.Open(path)
161 c.Assert(err, check.IsNil)
164 c.Assert(err, check.IsNil)
165 c.Check(st.Size(), check.Equals, int64(2))
166 c.Check(st.IsDir(), check.Equals, true)
170 func (s *CollectionFSSuite) TestNotExist(c *check.C) {
171 for _, path := range []string{"/no", "no", "./no", "n/o", "/n/o"} {
172 f, err := s.fs.Open(path)
173 c.Assert(f, check.IsNil)
174 c.Assert(err, check.NotNil)
175 c.Assert(os.IsNotExist(err), check.Equals, true)
179 func (s *CollectionFSSuite) TestReadOnlyFile(c *check.C) {
180 f, err := s.fs.OpenFile("/dir1/foo", os.O_RDONLY, 0)
181 c.Assert(err, check.IsNil)
183 c.Assert(err, check.IsNil)
184 c.Check(st.Size(), check.Equals, int64(3))
185 n, err := f.Write([]byte("bar"))
186 c.Check(n, check.Equals, 0)
187 c.Check(err, check.Equals, ErrReadOnlyFile)
190 func (s *CollectionFSSuite) TestCreateFile(c *check.C) {
191 f, err := s.fs.OpenFile("/new-file 1", os.O_RDWR|os.O_CREATE, 0)
192 c.Assert(err, check.IsNil)
194 c.Assert(err, check.IsNil)
195 c.Check(st.Size(), check.Equals, int64(0))
197 n, err := f.Write([]byte("bar"))
198 c.Check(n, check.Equals, 3)
199 c.Check(err, check.IsNil)
201 c.Check(f.Close(), check.IsNil)
203 f, err = s.fs.OpenFile("/new-file 1", os.O_RDWR|os.O_CREATE|os.O_EXCL, 0)
204 c.Check(f, check.IsNil)
205 c.Assert(err, check.NotNil)
207 f, err = s.fs.OpenFile("/new-file 1", os.O_RDWR, 0)
208 c.Assert(err, check.IsNil)
210 c.Assert(err, check.IsNil)
211 c.Check(st.Size(), check.Equals, int64(3))
213 c.Check(f.Close(), check.IsNil)
215 m, err := s.fs.MarshalManifest(".")
216 c.Assert(err, check.IsNil)
217 c.Check(m, check.Matches, `. 37b51d194a7513e45b56f6524f2d51f2\+3\+\S+ 0:3:new-file\\0401\n./dir1 .* 3:3:bar 0:3:foo\n`)
220 func (s *CollectionFSSuite) TestReadWriteFile(c *check.C) {
222 defer func() { maxBlockSize = 2 << 26 }()
224 f, err := s.fs.OpenFile("/dir1/foo", os.O_RDWR, 0)
225 c.Assert(err, check.IsNil)
228 c.Assert(err, check.IsNil)
229 c.Check(st.Size(), check.Equals, int64(3))
231 f2, err := s.fs.OpenFile("/dir1/foo", os.O_RDWR, 0)
232 c.Assert(err, check.IsNil)
235 buf := make([]byte, 64)
236 n, err := f.Read(buf)
237 c.Check(n, check.Equals, 3)
238 c.Check(err, check.Equals, io.EOF)
239 c.Check(string(buf[:3]), check.DeepEquals, "foo")
241 pos, err := f.Seek(-2, io.SeekCurrent)
242 c.Check(pos, check.Equals, int64(1))
243 c.Check(err, check.IsNil)
245 // Split a storedExtent in two, and insert a memExtent
246 n, err = f.Write([]byte("*"))
247 c.Check(n, check.Equals, 1)
248 c.Check(err, check.IsNil)
250 pos, err = f.Seek(0, io.SeekCurrent)
251 c.Check(pos, check.Equals, int64(2))
252 c.Check(err, check.IsNil)
254 pos, err = f.Seek(0, io.SeekStart)
255 c.Check(pos, check.Equals, int64(0))
256 c.Check(err, check.IsNil)
258 rbuf, err := ioutil.ReadAll(f)
259 c.Check(len(rbuf), check.Equals, 3)
260 c.Check(err, check.IsNil)
261 c.Check(string(rbuf), check.Equals, "f*o")
263 // Write multiple blocks in one call
264 f.Seek(1, io.SeekStart)
265 n, err = f.Write([]byte("0123456789abcdefg"))
266 c.Check(n, check.Equals, 17)
267 c.Check(err, check.IsNil)
268 pos, err = f.Seek(0, io.SeekCurrent)
269 c.Check(pos, check.Equals, int64(18))
270 c.Check(err, check.IsNil)
271 pos, err = f.Seek(-18, io.SeekCurrent)
272 c.Check(pos, check.Equals, int64(0))
273 c.Check(err, check.IsNil)
274 n, err = io.ReadFull(f, buf)
275 c.Check(n, check.Equals, 18)
276 c.Check(err, check.Equals, io.ErrUnexpectedEOF)
277 c.Check(string(buf[:n]), check.Equals, "f0123456789abcdefg")
279 buf2, err := ioutil.ReadAll(f2)
280 c.Check(err, check.IsNil)
281 c.Check(string(buf2), check.Equals, "f0123456789abcdefg")
283 // truncate to current size
285 c.Check(err, check.IsNil)
286 f2.Seek(0, io.SeekStart)
287 buf2, err = ioutil.ReadAll(f2)
288 c.Check(err, check.IsNil)
289 c.Check(string(buf2), check.Equals, "f0123456789abcdefg")
291 // shrink to zero some data
293 f2.Seek(0, io.SeekStart)
294 buf2, err = ioutil.ReadAll(f2)
295 c.Check(err, check.IsNil)
296 c.Check(string(buf2), check.Equals, "f0123456789abcd")
298 // grow to partial block/extent
300 f2.Seek(0, io.SeekStart)
301 buf2, err = ioutil.ReadAll(f2)
302 c.Check(err, check.IsNil)
303 c.Check(string(buf2), check.Equals, "f0123456789abcd\x00\x00\x00\x00\x00")
306 f2.Seek(0, io.SeekStart)
307 f2.Write([]byte("12345678abcdefghijkl"))
309 // grow to block/extent boundary
311 f2.Seek(0, io.SeekStart)
312 buf2, err = ioutil.ReadAll(f2)
313 c.Check(err, check.IsNil)
314 c.Check(len(buf2), check.Equals, 64)
315 c.Check(len(f.(*filehandle).inode.(*filenode).segments), check.Equals, 8)
317 // shrink to block/extent boundary
319 c.Check(err, check.IsNil)
320 f2.Seek(0, io.SeekStart)
321 buf2, err = ioutil.ReadAll(f2)
322 c.Check(err, check.IsNil)
323 c.Check(len(buf2), check.Equals, 32)
324 c.Check(len(f.(*filehandle).inode.(*filenode).segments), check.Equals, 4)
326 // shrink to partial block/extent
328 c.Check(err, check.IsNil)
329 f2.Seek(0, io.SeekStart)
330 buf2, err = ioutil.ReadAll(f2)
331 c.Check(err, check.IsNil)
332 c.Check(string(buf2), check.Equals, "12345678abcdefg")
333 c.Check(len(f.(*filehandle).inode.(*filenode).segments), check.Equals, 2)
335 // Force flush to ensure the block "12345678" gets stored, so
336 // we know what to expect in the final manifest below.
337 _, err = s.fs.MarshalManifest(".")
338 c.Check(err, check.IsNil)
340 // Truncate to size=3 while f2's ptr is at 15
342 c.Check(err, check.IsNil)
343 buf2, err = ioutil.ReadAll(f2)
344 c.Check(err, check.IsNil)
345 c.Check(string(buf2), check.Equals, "")
346 f2.Seek(0, io.SeekStart)
347 buf2, err = ioutil.ReadAll(f2)
348 c.Check(err, check.IsNil)
349 c.Check(string(buf2), check.Equals, "123")
350 c.Check(len(f.(*filehandle).inode.(*filenode).segments), check.Equals, 1)
352 m, err := s.fs.MarshalManifest(".")
353 c.Check(err, check.IsNil)
354 m = regexp.MustCompile(`\+A[^\+ ]+`).ReplaceAllLiteralString(m, "")
355 c.Check(m, check.Equals, "./dir1 3858f62230ac3c915f300c664312c63f+6 25d55ad283aa400af464c76d713c07ad+8 3:3:bar 6:3:foo\n")
356 c.Check(s.fs.Size(), check.Equals, int64(6))
359 func (s *CollectionFSSuite) TestSeekSparse(c *check.C) {
360 fs, err := (&Collection{}).FileSystem(s.client, s.kc)
361 c.Assert(err, check.IsNil)
362 f, err := fs.OpenFile("test", os.O_CREATE|os.O_RDWR, 0755)
363 c.Assert(err, check.IsNil)
366 checkSize := func(size int64) {
368 c.Assert(err, check.IsNil)
369 c.Check(fi.Size(), check.Equals, size)
371 f, err := fs.OpenFile("test", os.O_CREATE|os.O_RDWR, 0755)
372 c.Assert(err, check.IsNil)
375 c.Check(err, check.IsNil)
376 c.Check(fi.Size(), check.Equals, size)
377 pos, err := f.Seek(0, io.SeekEnd)
378 c.Check(err, check.IsNil)
379 c.Check(pos, check.Equals, size)
382 f.Seek(2, io.SeekEnd)
387 f.Seek(2, io.SeekCurrent)
392 f.Seek(8, io.SeekStart)
394 n, err := f.Read(make([]byte, 1))
395 c.Check(n, check.Equals, 0)
396 c.Check(err, check.Equals, io.EOF)
398 f.Write([]byte{1, 2, 3})
402 func (s *CollectionFSSuite) TestMarshalSmallBlocks(c *check.C) {
404 defer func() { maxBlockSize = 2 << 26 }()
407 s.fs, err = (&Collection{}).FileSystem(s.client, s.kc)
408 c.Assert(err, check.IsNil)
409 for _, name := range []string{"foo", "bar", "baz"} {
410 f, err := s.fs.OpenFile(name, os.O_WRONLY|os.O_CREATE, 0)
411 c.Assert(err, check.IsNil)
412 f.Write([]byte(name))
416 m, err := s.fs.MarshalManifest(".")
417 c.Check(err, check.IsNil)
418 m = regexp.MustCompile(`\+A[^\+ ]+`).ReplaceAllLiteralString(m, "")
419 c.Check(m, check.Equals, ". c3c23db5285662ef7172373df0003206+6 acbd18db4cc2f85cedef654fccc4a4d8+3 0:3:bar 3:3:baz 6:3:foo\n")
422 func (s *CollectionFSSuite) TestMkdir(c *check.C) {
423 err := s.fs.Mkdir("foo/bar", 0755)
424 c.Check(err, check.Equals, os.ErrNotExist)
426 f, err := s.fs.OpenFile("foo/bar", os.O_CREATE, 0)
427 c.Check(err, check.Equals, os.ErrNotExist)
429 err = s.fs.Mkdir("foo", 0755)
430 c.Check(err, check.IsNil)
432 f, err = s.fs.OpenFile("foo/bar", os.O_CREATE|os.O_WRONLY, 0)
433 c.Check(err, check.IsNil)
436 f.Write([]byte("foo"))
439 // mkdir fails if a file already exists with that name
440 err = s.fs.Mkdir("foo/bar", 0755)
441 c.Check(err, check.NotNil)
443 err = s.fs.Remove("foo/bar")
444 c.Check(err, check.IsNil)
446 // mkdir succeeds after the file is deleted
447 err = s.fs.Mkdir("foo/bar", 0755)
448 c.Check(err, check.IsNil)
450 // creating a file in a nonexistent subdir should still fail
451 f, err = s.fs.OpenFile("foo/bar/baz/foo.txt", os.O_CREATE|os.O_WRONLY, 0)
452 c.Check(err, check.Equals, os.ErrNotExist)
454 f, err = s.fs.OpenFile("foo/bar/foo.txt", os.O_CREATE|os.O_WRONLY, 0)
455 c.Check(err, check.IsNil)
458 f.Write([]byte("foo"))
461 // creating foo/bar as a regular file should fail
462 f, err = s.fs.OpenFile("foo/bar", os.O_CREATE|os.O_EXCL, 0)
463 c.Check(err, check.NotNil)
465 // creating foo/bar as a directory should fail
466 f, err = s.fs.OpenFile("foo/bar", os.O_CREATE|os.O_EXCL, os.ModeDir)
467 c.Check(err, check.NotNil)
468 err = s.fs.Mkdir("foo/bar", 0755)
469 c.Check(err, check.NotNil)
471 m, err := s.fs.MarshalManifest(".")
472 c.Check(err, check.IsNil)
473 m = regexp.MustCompile(`\+A[^\+ ]+`).ReplaceAllLiteralString(m, "")
474 c.Check(m, check.Equals, "./dir1 3858f62230ac3c915f300c664312c63f+6 3:3:bar 0:3:foo\n./foo/bar acbd18db4cc2f85cedef654fccc4a4d8+3 0:3:foo.txt\n")
477 func (s *CollectionFSSuite) TestConcurrentWriters(c *check.C) {
483 defer func() { maxBlockSize = 2 << 26 }()
485 var wg sync.WaitGroup
486 for n := 0; n < 128; n++ {
490 f, err := s.fs.OpenFile("/dir1/foo", os.O_RDWR, 0)
491 c.Assert(err, check.IsNil)
493 for i := 0; i < 6502; i++ {
497 _, err := s.fs.MarshalManifest(".")
498 c.Check(err, check.IsNil)
500 f.Truncate(int64(rand.Intn(64)))
502 f.Seek(int64(rand.Intn(64)), io.SeekStart)
504 _, err := f.Write([]byte("beep boop"))
505 c.Check(err, check.IsNil)
507 _, err := ioutil.ReadAll(f)
508 c.Check(err, check.IsNil)
515 f, err := s.fs.OpenFile("/dir1/foo", os.O_RDWR, 0)
516 c.Assert(err, check.IsNil)
518 buf, err := ioutil.ReadAll(f)
519 c.Check(err, check.IsNil)
520 c.Logf("after lots of random r/w/seek/trunc, buf is %q", buf)
523 func (s *CollectionFSSuite) TestRandomWrites(c *check.C) {
525 defer func() { maxBlockSize = 2 << 26 }()
528 s.fs, err = (&Collection{}).FileSystem(s.client, s.kc)
529 c.Assert(err, check.IsNil)
532 const ngoroutines = 256
534 var wg sync.WaitGroup
535 for n := 0; n < nfiles; n++ {
539 expect := make([]byte, 0, 64)
540 wbytes := []byte("there's no simple explanation for anything important that any of us do")
541 f, err := s.fs.OpenFile(fmt.Sprintf("random-%d", n), os.O_RDWR|os.O_CREATE|os.O_EXCL, 0)
542 c.Assert(err, check.IsNil)
544 for i := 0; i < ngoroutines; i++ {
545 trunc := rand.Intn(65)
546 woff := rand.Intn(trunc + 1)
547 wbytes = wbytes[:rand.Intn(64-woff+1)]
548 for buf, i := expect[:cap(expect)], len(expect); i < trunc; i++ {
551 expect = expect[:trunc]
552 if trunc < woff+len(wbytes) {
553 expect = expect[:woff+len(wbytes)]
555 copy(expect[woff:], wbytes)
556 f.Truncate(int64(trunc))
557 pos, err := f.Seek(int64(woff), io.SeekStart)
558 c.Check(pos, check.Equals, int64(woff))
559 c.Check(err, check.IsNil)
560 n, err := f.Write(wbytes)
561 c.Check(n, check.Equals, len(wbytes))
562 c.Check(err, check.IsNil)
563 pos, err = f.Seek(0, io.SeekStart)
564 c.Check(pos, check.Equals, int64(0))
565 c.Check(err, check.IsNil)
566 buf, err := ioutil.ReadAll(f)
567 c.Check(string(buf), check.Equals, string(expect))
568 c.Check(err, check.IsNil)
575 root, err := s.fs.Open("/")
576 c.Assert(err, check.IsNil)
578 fi, err := root.Readdir(-1)
579 c.Check(err, check.IsNil)
580 c.Check(len(fi), check.Equals, nfiles)
582 _, err = s.fs.MarshalManifest(".")
583 c.Check(err, check.IsNil)
584 // TODO: check manifest content
587 func (s *CollectionFSSuite) TestRemove(c *check.C) {
588 fs, err := (&Collection{}).FileSystem(s.client, s.kc)
589 c.Assert(err, check.IsNil)
590 err = fs.Mkdir("dir0", 0755)
591 c.Assert(err, check.IsNil)
592 err = fs.Mkdir("dir1", 0755)
593 c.Assert(err, check.IsNil)
594 err = fs.Mkdir("dir1/dir2", 0755)
595 c.Assert(err, check.IsNil)
596 err = fs.Mkdir("dir1/dir3", 0755)
597 c.Assert(err, check.IsNil)
599 err = fs.Remove("dir0")
600 c.Check(err, check.IsNil)
601 err = fs.Remove("dir0")
602 c.Check(err, check.Equals, os.ErrNotExist)
604 err = fs.Remove("dir1/dir2/.")
605 c.Check(err, check.Equals, ErrInvalidArgument)
606 err = fs.Remove("dir1/dir2/..")
607 c.Check(err, check.Equals, ErrInvalidArgument)
608 err = fs.Remove("dir1")
609 c.Check(err, check.Equals, ErrDirectoryNotEmpty)
610 err = fs.Remove("dir1/dir2/../../../dir1")
611 c.Check(err, check.Equals, ErrDirectoryNotEmpty)
612 err = fs.Remove("dir1/dir3/")
613 c.Check(err, check.IsNil)
614 err = fs.RemoveAll("dir1")
615 c.Check(err, check.IsNil)
616 err = fs.RemoveAll("dir1")
617 c.Check(err, check.IsNil)
620 func (s *CollectionFSSuite) TestRenameError(c *check.C) {
621 fs, err := (&Collection{}).FileSystem(s.client, s.kc)
622 c.Assert(err, check.IsNil)
623 err = fs.Mkdir("first", 0755)
624 c.Assert(err, check.IsNil)
625 err = fs.Mkdir("first/second", 0755)
626 c.Assert(err, check.IsNil)
627 f, err := fs.OpenFile("first/second/file", os.O_CREATE|os.O_WRONLY, 0755)
628 c.Assert(err, check.IsNil)
629 f.Write([]byte{1, 2, 3, 4, 5})
631 err = fs.Rename("first", "first/second/third")
632 c.Check(err, check.Equals, ErrInvalidArgument)
633 err = fs.Rename("first", "first/third")
634 c.Check(err, check.Equals, ErrInvalidArgument)
635 err = fs.Rename("first/second", "second")
636 c.Check(err, check.IsNil)
637 f, err = fs.OpenFile("second/file", 0, 0)
638 c.Assert(err, check.IsNil)
639 data, err := ioutil.ReadAll(f)
640 c.Check(err, check.IsNil)
641 c.Check(data, check.DeepEquals, []byte{1, 2, 3, 4, 5})
644 func (s *CollectionFSSuite) TestRename(c *check.C) {
645 fs, err := (&Collection{}).FileSystem(s.client, s.kc)
646 c.Assert(err, check.IsNil)
651 for i := 0; i < outer; i++ {
652 err = fs.Mkdir(fmt.Sprintf("dir%d", i), 0755)
653 c.Assert(err, check.IsNil)
654 for j := 0; j < inner; j++ {
655 err = fs.Mkdir(fmt.Sprintf("dir%d/dir%d", i, j), 0755)
656 c.Assert(err, check.IsNil)
657 for _, fnm := range []string{
658 fmt.Sprintf("dir%d/file%d", i, j),
659 fmt.Sprintf("dir%d/dir%d/file%d", i, j, j),
661 f, err := fs.OpenFile(fnm, os.O_CREATE|os.O_WRONLY, 0755)
662 c.Assert(err, check.IsNil)
663 _, err = f.Write([]byte("beep"))
664 c.Assert(err, check.IsNil)
669 var wg sync.WaitGroup
670 for i := 0; i < outer; i++ {
671 for j := 0; j < inner; j++ {
675 oldname := fmt.Sprintf("dir%d/dir%d/file%d", i, j, j)
676 newname := fmt.Sprintf("dir%d/newfile%d", i, inner-j-1)
677 _, err := fs.Open(newname)
678 c.Check(err, check.Equals, os.ErrNotExist)
679 err = fs.Rename(oldname, newname)
680 c.Check(err, check.IsNil)
681 f, err := fs.Open(newname)
682 c.Check(err, check.IsNil)
689 // oldname does not exist
691 fmt.Sprintf("dir%d/dir%d/missing", i, j),
692 fmt.Sprintf("dir%d/dir%d/file%d", outer-i-1, j, j))
693 c.Check(err, check.ErrorMatches, `.*does not exist`)
695 // newname parent dir does not exist
697 fmt.Sprintf("dir%d/dir%d", i, j),
698 fmt.Sprintf("dir%d/missing/irrelevant", outer-i-1))
699 c.Check(err, check.ErrorMatches, `.*does not exist`)
701 // oldname parent dir is a file
703 fmt.Sprintf("dir%d/file%d/patherror", i, j),
704 fmt.Sprintf("dir%d/irrelevant", i))
705 c.Check(err, check.ErrorMatches, `.*not a directory`)
707 // newname parent dir is a file
709 fmt.Sprintf("dir%d/dir%d/file%d", i, j, j),
710 fmt.Sprintf("dir%d/file%d/patherror", i, inner-j-1))
711 c.Check(err, check.ErrorMatches, `.*not a directory`)
717 f, err := fs.OpenFile("dir1/newfile3", 0, 0)
718 c.Assert(err, check.IsNil)
719 c.Check(f.Size(), check.Equals, int64(4))
720 buf, err := ioutil.ReadAll(f)
721 c.Check(buf, check.DeepEquals, []byte("beep"))
722 c.Check(err, check.IsNil)
723 _, err = fs.Open("dir1/dir1/file1")
724 c.Check(err, check.Equals, os.ErrNotExist)
727 func (s *CollectionFSSuite) TestPersist(c *check.C) {
729 defer func() { maxBlockSize = 2 << 26 }()
732 s.fs, err = (&Collection{}).FileSystem(s.client, s.kc)
733 c.Assert(err, check.IsNil)
734 err = s.fs.Mkdir("d:r", 0755)
735 c.Assert(err, check.IsNil)
737 expect := map[string][]byte{}
739 var wg sync.WaitGroup
740 for _, name := range []string{"random 1", "random:2", "random\\3", "d:r/random4"} {
741 buf := make([]byte, 500)
745 f, err := s.fs.OpenFile(name, os.O_WRONLY|os.O_CREATE, 0)
746 c.Assert(err, check.IsNil)
747 // Note: we don't close the file until after the test
748 // is done. Writes to unclosed files should persist.
754 for i := 0; i < len(buf); i += 5 {
755 _, err := f.Write(buf[i : i+5])
756 c.Assert(err, check.IsNil)
762 m, err := s.fs.MarshalManifest(".")
763 c.Check(err, check.IsNil)
766 root, err := s.fs.Open("/")
767 c.Assert(err, check.IsNil)
769 fi, err := root.Readdir(-1)
770 c.Check(err, check.IsNil)
771 c.Check(len(fi), check.Equals, 4)
773 persisted, err := (&Collection{ManifestText: m}).FileSystem(s.client, s.kc)
774 c.Assert(err, check.IsNil)
776 root, err = persisted.Open("/")
777 c.Assert(err, check.IsNil)
779 fi, err = root.Readdir(-1)
780 c.Check(err, check.IsNil)
781 c.Check(len(fi), check.Equals, 4)
783 for name, content := range expect {
784 c.Logf("read %q", name)
785 f, err := persisted.Open(name)
786 c.Assert(err, check.IsNil)
788 buf, err := ioutil.ReadAll(f)
789 c.Check(err, check.IsNil)
790 c.Check(buf, check.DeepEquals, content)
794 func (s *CollectionFSSuite) TestPersistEmptyFiles(c *check.C) {
796 s.fs, err = (&Collection{}).FileSystem(s.client, s.kc)
797 c.Assert(err, check.IsNil)
798 for _, name := range []string{"dir", "dir/zerodir", "zero", "zero/zero"} {
799 err = s.fs.Mkdir(name, 0755)
800 c.Assert(err, check.IsNil)
803 expect := map[string][]byte{
810 "dir/zerodir/zero": nil,
811 "zero/zero/zero": nil,
813 for name, data := range expect {
814 f, err := s.fs.OpenFile(name, os.O_WRONLY|os.O_CREATE, 0)
815 c.Assert(err, check.IsNil)
817 _, err := f.Write(data)
818 c.Assert(err, check.IsNil)
823 m, err := s.fs.MarshalManifest(".")
824 c.Check(err, check.IsNil)
827 persisted, err := (&Collection{ManifestText: m}).FileSystem(s.client, s.kc)
828 c.Assert(err, check.IsNil)
830 for name, data := range expect {
831 _, err = persisted.Open("bogus-" + name)
832 c.Check(err, check.NotNil)
834 f, err := persisted.Open(name)
835 c.Assert(err, check.IsNil)
840 buf, err := ioutil.ReadAll(f)
841 c.Check(err, check.IsNil)
842 c.Check(buf, check.DeepEquals, data)
846 func (s *CollectionFSSuite) TestOpenFileFlags(c *check.C) {
847 fs, err := (&Collection{}).FileSystem(s.client, s.kc)
848 c.Assert(err, check.IsNil)
850 f, err := fs.OpenFile("missing", os.O_WRONLY, 0)
851 c.Check(f, check.IsNil)
852 c.Check(err, check.ErrorMatches, `file does not exist`)
854 f, err = fs.OpenFile("new", os.O_CREATE|os.O_RDONLY, 0)
855 c.Assert(err, check.IsNil)
857 n, err := f.Write([]byte{1, 2, 3})
858 c.Check(n, check.Equals, 0)
859 c.Check(err, check.ErrorMatches, `read-only file`)
860 n, err = f.Read(make([]byte, 1))
861 c.Check(n, check.Equals, 0)
862 c.Check(err, check.Equals, io.EOF)
863 f, err = fs.OpenFile("new", os.O_RDWR, 0)
864 c.Assert(err, check.IsNil)
866 _, err = f.Write([]byte{4, 5, 6})
867 c.Check(err, check.IsNil)
869 c.Assert(err, check.IsNil)
870 c.Check(fi.Size(), check.Equals, int64(3))
872 f, err = fs.OpenFile("new", os.O_TRUNC|os.O_RDWR, 0)
873 c.Assert(err, check.IsNil)
875 pos, err := f.Seek(0, io.SeekEnd)
876 c.Check(pos, check.Equals, int64(0))
877 c.Check(err, check.IsNil)
879 c.Assert(err, check.IsNil)
880 c.Check(fi.Size(), check.Equals, int64(0))
883 buf := make([]byte, 64)
884 f, err = fs.OpenFile("append", os.O_EXCL|os.O_CREATE|os.O_RDWR|os.O_APPEND, 0)
885 c.Assert(err, check.IsNil)
886 f.Write([]byte{1, 2, 3})
887 f.Seek(0, io.SeekStart)
888 n, _ = f.Read(buf[:1])
889 c.Check(n, check.Equals, 1)
890 c.Check(buf[:1], check.DeepEquals, []byte{1})
891 pos, err = f.Seek(0, io.SeekCurrent)
892 c.Assert(err, check.IsNil)
893 c.Check(pos, check.Equals, int64(1))
894 f.Write([]byte{4, 5, 6})
895 pos, err = f.Seek(0, io.SeekCurrent)
896 c.Assert(err, check.IsNil)
897 c.Check(pos, check.Equals, int64(6))
898 f.Seek(0, io.SeekStart)
900 c.Check(buf[:n], check.DeepEquals, []byte{1, 2, 3, 4, 5, 6})
901 c.Check(err, check.Equals, io.EOF)
904 f, err = fs.OpenFile("append", os.O_RDWR|os.O_APPEND, 0)
905 c.Assert(err, check.IsNil)
906 pos, err = f.Seek(0, io.SeekCurrent)
907 c.Check(pos, check.Equals, int64(0))
908 c.Check(err, check.IsNil)
910 pos, _ = f.Seek(0, io.SeekCurrent)
911 c.Check(pos, check.Equals, int64(3))
912 f.Write([]byte{7, 8, 9})
913 pos, err = f.Seek(0, io.SeekCurrent)
914 c.Check(err, check.IsNil)
915 c.Check(pos, check.Equals, int64(9))
918 f, err = fs.OpenFile("wronly", os.O_CREATE|os.O_WRONLY, 0)
919 c.Assert(err, check.IsNil)
920 n, err = f.Write([]byte{3, 2, 1})
921 c.Check(n, check.Equals, 3)
922 c.Check(err, check.IsNil)
923 pos, _ = f.Seek(0, io.SeekCurrent)
924 c.Check(pos, check.Equals, int64(3))
925 pos, _ = f.Seek(0, io.SeekStart)
926 c.Check(pos, check.Equals, int64(0))
928 c.Check(n, check.Equals, 0)
929 c.Check(err, check.ErrorMatches, `.*O_WRONLY.*`)
930 f, err = fs.OpenFile("wronly", os.O_RDONLY, 0)
931 c.Assert(err, check.IsNil)
933 c.Check(buf[:n], check.DeepEquals, []byte{3, 2, 1})
935 f, err = fs.OpenFile("unsupported", os.O_CREATE|os.O_SYNC, 0)
936 c.Check(f, check.IsNil)
937 c.Check(err, check.NotNil)
939 f, err = fs.OpenFile("append", os.O_RDWR|os.O_WRONLY, 0)
940 c.Check(f, check.IsNil)
941 c.Check(err, check.ErrorMatches, `invalid flag.*`)
944 func (s *CollectionFSSuite) TestFlushFullBlocks(c *check.C) {
946 defer func() { maxBlockSize = 2 << 26 }()
948 fs, err := (&Collection{}).FileSystem(s.client, s.kc)
949 c.Assert(err, check.IsNil)
950 f, err := fs.OpenFile("50K", os.O_WRONLY|os.O_CREATE, 0)
951 c.Assert(err, check.IsNil)
954 data := make([]byte, 500)
957 for i := 0; i < 100; i++ {
958 n, err := f.Write(data)
959 c.Assert(n, check.Equals, len(data))
960 c.Assert(err, check.IsNil)
963 currentMemExtents := func() (memExtents []int) {
964 for idx, e := range f.(*filehandle).inode.(*filenode).segments {
967 memExtents = append(memExtents, idx)
972 c.Check(currentMemExtents(), check.HasLen, 1)
974 m, err := fs.MarshalManifest(".")
975 c.Check(m, check.Matches, `[^:]* 0:50000:50K\n`)
976 c.Check(err, check.IsNil)
977 c.Check(currentMemExtents(), check.HasLen, 0)
980 func (s *CollectionFSSuite) TestBrokenManifests(c *check.C) {
981 for _, txt := range []string{
985 ". d41d8cd98f00b204e9800998ecf8427e+0\n",
986 ". d41d8cd98f00b204e9800998ecf8427e+0 \n",
989 ". 0:0:foo 0:0:bar\n",
990 ". d41d8cd98f00b204e9800998ecf8427e 0:0:foo\n",
991 ". d41d8cd98f00b204e9800998ecf8427e+0 :0:0:foo\n",
992 ". d41d8cd98f00b204e9800998ecf8427e+0 foo:0:foo\n",
993 ". d41d8cd98f00b204e9800998ecf8427e+0 0:foo:foo\n",
994 ". d41d8cd98f00b204e9800998ecf8427e+1 0:1:foo 1:1:bar\n",
995 ". d41d8cd98f00b204e9800998ecf8427e+1 0:0:foo\n./foo d41d8cd98f00b204e9800998ecf8427e+1 0:0:bar\n",
996 "./foo d41d8cd98f00b204e9800998ecf8427e+1 0:0:bar\n. d41d8cd98f00b204e9800998ecf8427e+1 0:0:foo\n",
999 fs, err := (&Collection{ManifestText: txt}).FileSystem(s.client, s.kc)
1000 c.Check(fs, check.IsNil)
1001 c.Logf("-> %s", err)
1002 c.Check(err, check.NotNil)
1006 func (s *CollectionFSSuite) TestEdgeCaseManifests(c *check.C) {
1007 for _, txt := range []string{
1009 ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:foo\n",
1010 ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:foo 0:0:foo 0:0:bar\n",
1011 ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:foo 0:0:foo 0:0:bar\n",
1012 ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:foo/bar\n./foo d41d8cd98f00b204e9800998ecf8427e+0 0:0:bar\n",
1015 fs, err := (&Collection{ManifestText: txt}).FileSystem(s.client, s.kc)
1016 c.Check(err, check.IsNil)
1017 c.Check(fs, check.NotNil)
1021 func (s *CollectionFSSuite) checkMemSize(c *check.C, f File) {
1022 fn := f.(*filehandle).inode.(*filenode)
1024 for _, seg := range fn.segments {
1025 if e, ok := seg.(*memSegment); ok {
1026 memsize += int64(len(e.buf))
1029 c.Check(fn.memsize, check.Equals, memsize)
1032 type CollectionFSUnitSuite struct{}
1034 var _ = check.Suite(&CollectionFSUnitSuite{})
1036 // expect ~2 seconds to load a manifest with 256K files
1037 func (s *CollectionFSUnitSuite) TestLargeManifest(c *check.C) {
1038 if testing.Short() {
1047 mb := bytes.NewBuffer(make([]byte, 0, 40000000))
1048 for i := 0; i < dirCount; i++ {
1049 fmt.Fprintf(mb, "./dir%d", i)
1050 for j := 0; j <= fileCount; j++ {
1051 fmt.Fprintf(mb, " %032x+42+A%040x@%08x", j, j, j)
1053 for j := 0; j < fileCount; j++ {
1054 fmt.Fprintf(mb, " %d:%d:dir%d/file%d", j*42+21, 42, j, j)
1056 mb.Write([]byte{'\n'})
1058 coll := Collection{ManifestText: mb.String()}
1059 c.Logf("%s built", time.Now())
1061 var memstats runtime.MemStats
1062 runtime.ReadMemStats(&memstats)
1063 c.Logf("%s Alloc=%d Sys=%d", time.Now(), memstats.Alloc, memstats.Sys)
1065 f, err := coll.FileSystem(nil, nil)
1066 c.Check(err, check.IsNil)
1067 c.Logf("%s loaded", time.Now())
1068 c.Check(f.Size(), check.Equals, int64(42*dirCount*fileCount))
1070 for i := 0; i < dirCount; i++ {
1071 for j := 0; j < fileCount; j++ {
1072 f.Stat(fmt.Sprintf("./dir%d/dir%d/file%d", i, j, j))
1075 c.Logf("%s Stat() x %d", time.Now(), dirCount*fileCount)
1077 runtime.ReadMemStats(&memstats)
1078 c.Logf("%s Alloc=%d Sys=%d", time.Now(), memstats.Alloc, memstats.Sys)
1081 // Gocheck boilerplate
1082 func Test(t *testing.T) {