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")
358 func (s *CollectionFSSuite) TestSeekSparse(c *check.C) {
359 fs, err := (&Collection{}).FileSystem(s.client, s.kc)
360 c.Assert(err, check.IsNil)
361 f, err := fs.OpenFile("test", os.O_CREATE|os.O_RDWR, 0755)
362 c.Assert(err, check.IsNil)
365 checkSize := func(size int64) {
367 c.Assert(err, check.IsNil)
368 c.Check(fi.Size(), check.Equals, size)
370 f, err := fs.OpenFile("test", os.O_CREATE|os.O_RDWR, 0755)
371 c.Assert(err, check.IsNil)
374 c.Check(err, check.IsNil)
375 c.Check(fi.Size(), check.Equals, size)
376 pos, err := f.Seek(0, io.SeekEnd)
377 c.Check(err, check.IsNil)
378 c.Check(pos, check.Equals, size)
381 f.Seek(2, io.SeekEnd)
386 f.Seek(2, io.SeekCurrent)
391 f.Seek(8, io.SeekStart)
393 n, err := f.Read(make([]byte, 1))
394 c.Check(n, check.Equals, 0)
395 c.Check(err, check.Equals, io.EOF)
397 f.Write([]byte{1, 2, 3})
401 func (s *CollectionFSSuite) TestMarshalSmallBlocks(c *check.C) {
403 defer func() { maxBlockSize = 2 << 26 }()
406 s.fs, err = (&Collection{}).FileSystem(s.client, s.kc)
407 c.Assert(err, check.IsNil)
408 for _, name := range []string{"foo", "bar", "baz"} {
409 f, err := s.fs.OpenFile(name, os.O_WRONLY|os.O_CREATE, 0)
410 c.Assert(err, check.IsNil)
411 f.Write([]byte(name))
415 m, err := s.fs.MarshalManifest(".")
416 c.Check(err, check.IsNil)
417 m = regexp.MustCompile(`\+A[^\+ ]+`).ReplaceAllLiteralString(m, "")
418 c.Check(m, check.Equals, ". c3c23db5285662ef7172373df0003206+6 acbd18db4cc2f85cedef654fccc4a4d8+3 0:3:bar 3:3:baz 6:3:foo\n")
421 func (s *CollectionFSSuite) TestMkdir(c *check.C) {
422 err := s.fs.Mkdir("foo/bar", 0755)
423 c.Check(err, check.Equals, os.ErrNotExist)
425 f, err := s.fs.OpenFile("foo/bar", os.O_CREATE, 0)
426 c.Check(err, check.Equals, os.ErrNotExist)
428 err = s.fs.Mkdir("foo", 0755)
429 c.Check(err, check.IsNil)
431 f, err = s.fs.OpenFile("foo/bar", os.O_CREATE|os.O_WRONLY, 0)
432 c.Check(err, check.IsNil)
435 f.Write([]byte("foo"))
438 // mkdir fails if a file already exists with that name
439 err = s.fs.Mkdir("foo/bar", 0755)
440 c.Check(err, check.NotNil)
442 err = s.fs.Remove("foo/bar")
443 c.Check(err, check.IsNil)
445 // mkdir succeeds after the file is deleted
446 err = s.fs.Mkdir("foo/bar", 0755)
447 c.Check(err, check.IsNil)
449 // creating a file in a nonexistent subdir should still fail
450 f, err = s.fs.OpenFile("foo/bar/baz/foo.txt", os.O_CREATE|os.O_WRONLY, 0)
451 c.Check(err, check.Equals, os.ErrNotExist)
453 f, err = s.fs.OpenFile("foo/bar/foo.txt", os.O_CREATE|os.O_WRONLY, 0)
454 c.Check(err, check.IsNil)
457 f.Write([]byte("foo"))
460 // creating foo/bar as a regular file should fail
461 f, err = s.fs.OpenFile("foo/bar", os.O_CREATE|os.O_EXCL, 0)
462 c.Check(err, check.NotNil)
464 // creating foo/bar as a directory should fail
465 f, err = s.fs.OpenFile("foo/bar", os.O_CREATE|os.O_EXCL, os.ModeDir)
466 c.Check(err, check.NotNil)
467 err = s.fs.Mkdir("foo/bar", 0755)
468 c.Check(err, check.NotNil)
470 m, err := s.fs.MarshalManifest(".")
471 c.Check(err, check.IsNil)
472 m = regexp.MustCompile(`\+A[^\+ ]+`).ReplaceAllLiteralString(m, "")
473 c.Check(m, check.Equals, "./dir1 3858f62230ac3c915f300c664312c63f+6 3:3:bar 0:3:foo\n./foo/bar acbd18db4cc2f85cedef654fccc4a4d8+3 0:3:foo.txt\n")
476 func (s *CollectionFSSuite) TestConcurrentWriters(c *check.C) {
482 defer func() { maxBlockSize = 2 << 26 }()
484 var wg sync.WaitGroup
485 for n := 0; n < 128; n++ {
489 f, err := s.fs.OpenFile("/dir1/foo", os.O_RDWR, 0)
490 c.Assert(err, check.IsNil)
492 for i := 0; i < 6502; i++ {
493 switch rand.Int() & 3 {
495 f.Truncate(int64(rand.Intn(64)))
497 f.Seek(int64(rand.Intn(64)), io.SeekStart)
499 _, err := f.Write([]byte("beep boop"))
500 c.Check(err, check.IsNil)
502 _, err := ioutil.ReadAll(f)
503 c.Check(err, check.IsNil)
510 f, err := s.fs.OpenFile("/dir1/foo", os.O_RDWR, 0)
511 c.Assert(err, check.IsNil)
513 buf, err := ioutil.ReadAll(f)
514 c.Check(err, check.IsNil)
515 c.Logf("after lots of random r/w/seek/trunc, buf is %q", buf)
518 func (s *CollectionFSSuite) TestRandomWrites(c *check.C) {
520 defer func() { maxBlockSize = 2 << 26 }()
523 s.fs, err = (&Collection{}).FileSystem(s.client, s.kc)
524 c.Assert(err, check.IsNil)
527 const ngoroutines = 256
529 var wg sync.WaitGroup
530 for n := 0; n < nfiles; n++ {
534 expect := make([]byte, 0, 64)
535 wbytes := []byte("there's no simple explanation for anything important that any of us do")
536 f, err := s.fs.OpenFile(fmt.Sprintf("random-%d", n), os.O_RDWR|os.O_CREATE|os.O_EXCL, 0)
537 c.Assert(err, check.IsNil)
539 for i := 0; i < ngoroutines; i++ {
540 trunc := rand.Intn(65)
541 woff := rand.Intn(trunc + 1)
542 wbytes = wbytes[:rand.Intn(64-woff+1)]
543 for buf, i := expect[:cap(expect)], len(expect); i < trunc; i++ {
546 expect = expect[:trunc]
547 if trunc < woff+len(wbytes) {
548 expect = expect[:woff+len(wbytes)]
550 copy(expect[woff:], wbytes)
551 f.Truncate(int64(trunc))
552 pos, err := f.Seek(int64(woff), io.SeekStart)
553 c.Check(pos, check.Equals, int64(woff))
554 c.Check(err, check.IsNil)
555 n, err := f.Write(wbytes)
556 c.Check(n, check.Equals, len(wbytes))
557 c.Check(err, check.IsNil)
558 pos, err = f.Seek(0, io.SeekStart)
559 c.Check(pos, check.Equals, int64(0))
560 c.Check(err, check.IsNil)
561 buf, err := ioutil.ReadAll(f)
562 c.Check(string(buf), check.Equals, string(expect))
563 c.Check(err, check.IsNil)
570 root, err := s.fs.Open("/")
571 c.Assert(err, check.IsNil)
573 fi, err := root.Readdir(-1)
574 c.Check(err, check.IsNil)
575 c.Check(len(fi), check.Equals, nfiles)
577 _, err = s.fs.MarshalManifest(".")
578 c.Check(err, check.IsNil)
579 // TODO: check manifest content
582 func (s *CollectionFSSuite) TestRemove(c *check.C) {
583 fs, err := (&Collection{}).FileSystem(s.client, s.kc)
584 c.Assert(err, check.IsNil)
585 err = fs.Mkdir("dir0", 0755)
586 c.Assert(err, check.IsNil)
587 err = fs.Mkdir("dir1", 0755)
588 c.Assert(err, check.IsNil)
589 err = fs.Mkdir("dir1/dir2", 0755)
590 c.Assert(err, check.IsNil)
591 err = fs.Mkdir("dir1/dir3", 0755)
592 c.Assert(err, check.IsNil)
594 err = fs.Remove("dir0")
595 c.Check(err, check.IsNil)
596 err = fs.Remove("dir0")
597 c.Check(err, check.Equals, os.ErrNotExist)
599 err = fs.Remove("dir1/dir2/.")
600 c.Check(err, check.Equals, ErrInvalidArgument)
601 err = fs.Remove("dir1/dir2/..")
602 c.Check(err, check.Equals, ErrInvalidArgument)
603 err = fs.Remove("dir1")
604 c.Check(err, check.Equals, ErrDirectoryNotEmpty)
605 err = fs.Remove("dir1/dir2/../../../dir1")
606 c.Check(err, check.Equals, ErrDirectoryNotEmpty)
607 err = fs.Remove("dir1/dir3/")
608 c.Check(err, check.IsNil)
609 err = fs.RemoveAll("dir1")
610 c.Check(err, check.IsNil)
611 err = fs.RemoveAll("dir1")
612 c.Check(err, check.IsNil)
615 func (s *CollectionFSSuite) TestRenameError(c *check.C) {
616 fs, err := (&Collection{}).FileSystem(s.client, s.kc)
617 c.Assert(err, check.IsNil)
618 err = fs.Mkdir("first", 0755)
619 c.Assert(err, check.IsNil)
620 err = fs.Mkdir("first/second", 0755)
621 c.Assert(err, check.IsNil)
622 f, err := fs.OpenFile("first/second/file", os.O_CREATE|os.O_WRONLY, 0755)
623 c.Assert(err, check.IsNil)
624 f.Write([]byte{1, 2, 3, 4, 5})
626 err = fs.Rename("first", "first/second/third")
627 c.Check(err, check.Equals, ErrInvalidArgument)
628 err = fs.Rename("first", "first/third")
629 c.Check(err, check.Equals, ErrInvalidArgument)
630 err = fs.Rename("first/second", "second")
631 c.Check(err, check.IsNil)
632 f, err = fs.OpenFile("second/file", 0, 0)
633 c.Assert(err, check.IsNil)
634 data, err := ioutil.ReadAll(f)
635 c.Check(err, check.IsNil)
636 c.Check(data, check.DeepEquals, []byte{1, 2, 3, 4, 5})
639 func (s *CollectionFSSuite) TestRename(c *check.C) {
640 fs, err := (&Collection{}).FileSystem(s.client, s.kc)
641 c.Assert(err, check.IsNil)
646 for i := 0; i < outer; i++ {
647 err = fs.Mkdir(fmt.Sprintf("dir%d", i), 0755)
648 c.Assert(err, check.IsNil)
649 for j := 0; j < inner; j++ {
650 err = fs.Mkdir(fmt.Sprintf("dir%d/dir%d", i, j), 0755)
651 c.Assert(err, check.IsNil)
652 for _, fnm := range []string{
653 fmt.Sprintf("dir%d/file%d", i, j),
654 fmt.Sprintf("dir%d/dir%d/file%d", i, j, j),
656 f, err := fs.OpenFile(fnm, os.O_CREATE|os.O_WRONLY, 0755)
657 c.Assert(err, check.IsNil)
658 _, err = f.Write([]byte("beep"))
659 c.Assert(err, check.IsNil)
664 var wg sync.WaitGroup
665 for i := 0; i < outer; i++ {
666 for j := 0; j < inner; j++ {
670 oldname := fmt.Sprintf("dir%d/dir%d/file%d", i, j, j)
671 newname := fmt.Sprintf("dir%d/newfile%d", i, inner-j-1)
672 _, err := fs.Open(newname)
673 c.Check(err, check.Equals, os.ErrNotExist)
674 err = fs.Rename(oldname, newname)
675 c.Check(err, check.IsNil)
676 f, err := fs.Open(newname)
677 c.Check(err, check.IsNil)
684 // oldname does not exist
686 fmt.Sprintf("dir%d/dir%d/missing", i, j),
687 fmt.Sprintf("dir%d/dir%d/file%d", outer-i-1, j, j))
688 c.Check(err, check.ErrorMatches, `.*does not exist`)
690 // newname parent dir does not exist
692 fmt.Sprintf("dir%d/dir%d", i, j),
693 fmt.Sprintf("dir%d/missing/irrelevant", outer-i-1))
694 c.Check(err, check.ErrorMatches, `.*does not exist`)
696 // oldname parent dir is a file
698 fmt.Sprintf("dir%d/file%d/patherror", i, j),
699 fmt.Sprintf("dir%d/irrelevant", i))
700 c.Check(err, check.ErrorMatches, `.*not a directory`)
702 // newname parent dir is a file
704 fmt.Sprintf("dir%d/dir%d/file%d", i, j, j),
705 fmt.Sprintf("dir%d/file%d/patherror", i, inner-j-1))
706 c.Check(err, check.ErrorMatches, `.*not a directory`)
712 f, err := fs.OpenFile("dir1/newfile3", 0, 0)
713 c.Assert(err, check.IsNil)
714 c.Check(f.Size(), check.Equals, int64(4))
715 buf, err := ioutil.ReadAll(f)
716 c.Check(buf, check.DeepEquals, []byte("beep"))
717 c.Check(err, check.IsNil)
718 _, err = fs.Open("dir1/dir1/file1")
719 c.Check(err, check.Equals, os.ErrNotExist)
722 func (s *CollectionFSSuite) TestPersist(c *check.C) {
724 defer func() { maxBlockSize = 2 << 26 }()
727 s.fs, err = (&Collection{}).FileSystem(s.client, s.kc)
728 c.Assert(err, check.IsNil)
729 err = s.fs.Mkdir("d:r", 0755)
730 c.Assert(err, check.IsNil)
732 expect := map[string][]byte{}
734 var wg sync.WaitGroup
735 for _, name := range []string{"random 1", "random:2", "random\\3", "d:r/random4"} {
736 buf := make([]byte, 500)
740 f, err := s.fs.OpenFile(name, os.O_WRONLY|os.O_CREATE, 0)
741 c.Assert(err, check.IsNil)
742 // Note: we don't close the file until after the test
743 // is done. Writes to unclosed files should persist.
749 for i := 0; i < len(buf); i += 5 {
750 _, err := f.Write(buf[i : i+5])
751 c.Assert(err, check.IsNil)
757 m, err := s.fs.MarshalManifest(".")
758 c.Check(err, check.IsNil)
761 root, err := s.fs.Open("/")
762 c.Assert(err, check.IsNil)
764 fi, err := root.Readdir(-1)
765 c.Check(err, check.IsNil)
766 c.Check(len(fi), check.Equals, 4)
768 persisted, err := (&Collection{ManifestText: m}).FileSystem(s.client, s.kc)
769 c.Assert(err, check.IsNil)
771 root, err = persisted.Open("/")
772 c.Assert(err, check.IsNil)
774 fi, err = root.Readdir(-1)
775 c.Check(err, check.IsNil)
776 c.Check(len(fi), check.Equals, 4)
778 for name, content := range expect {
779 c.Logf("read %q", name)
780 f, err := persisted.Open(name)
781 c.Assert(err, check.IsNil)
783 buf, err := ioutil.ReadAll(f)
784 c.Check(err, check.IsNil)
785 c.Check(buf, check.DeepEquals, content)
789 func (s *CollectionFSSuite) TestPersistEmptyFiles(c *check.C) {
791 s.fs, err = (&Collection{}).FileSystem(s.client, s.kc)
792 c.Assert(err, check.IsNil)
793 for _, name := range []string{"dir", "dir/zerodir", "zero", "zero/zero"} {
794 err = s.fs.Mkdir(name, 0755)
795 c.Assert(err, check.IsNil)
798 expect := map[string][]byte{
805 "dir/zerodir/zero": nil,
806 "zero/zero/zero": nil,
808 for name, data := range expect {
809 f, err := s.fs.OpenFile(name, os.O_WRONLY|os.O_CREATE, 0)
810 c.Assert(err, check.IsNil)
812 _, err := f.Write(data)
813 c.Assert(err, check.IsNil)
818 m, err := s.fs.MarshalManifest(".")
819 c.Check(err, check.IsNil)
822 persisted, err := (&Collection{ManifestText: m}).FileSystem(s.client, s.kc)
823 c.Assert(err, check.IsNil)
825 for name, data := range expect {
826 _, err = persisted.Open("bogus-" + name)
827 c.Check(err, check.NotNil)
829 f, err := persisted.Open(name)
830 c.Assert(err, check.IsNil)
835 buf, err := ioutil.ReadAll(f)
836 c.Check(err, check.IsNil)
837 c.Check(buf, check.DeepEquals, data)
841 func (s *CollectionFSSuite) TestOpenFileFlags(c *check.C) {
842 fs, err := (&Collection{}).FileSystem(s.client, s.kc)
843 c.Assert(err, check.IsNil)
845 f, err := fs.OpenFile("missing", os.O_WRONLY, 0)
846 c.Check(f, check.IsNil)
847 c.Check(err, check.ErrorMatches, `file does not exist`)
849 f, err = fs.OpenFile("new", os.O_CREATE|os.O_RDONLY, 0)
850 c.Assert(err, check.IsNil)
852 n, err := f.Write([]byte{1, 2, 3})
853 c.Check(n, check.Equals, 0)
854 c.Check(err, check.ErrorMatches, `read-only file`)
855 n, err = f.Read(make([]byte, 1))
856 c.Check(n, check.Equals, 0)
857 c.Check(err, check.Equals, io.EOF)
858 f, err = fs.OpenFile("new", os.O_RDWR, 0)
859 c.Assert(err, check.IsNil)
861 _, err = f.Write([]byte{4, 5, 6})
862 c.Check(err, check.IsNil)
864 c.Assert(err, check.IsNil)
865 c.Check(fi.Size(), check.Equals, int64(3))
867 f, err = fs.OpenFile("new", os.O_TRUNC|os.O_RDWR, 0)
868 c.Assert(err, check.IsNil)
870 pos, err := f.Seek(0, io.SeekEnd)
871 c.Check(pos, check.Equals, int64(0))
872 c.Check(err, check.IsNil)
874 c.Assert(err, check.IsNil)
875 c.Check(fi.Size(), check.Equals, int64(0))
878 buf := make([]byte, 64)
879 f, err = fs.OpenFile("append", os.O_EXCL|os.O_CREATE|os.O_RDWR|os.O_APPEND, 0)
880 c.Assert(err, check.IsNil)
881 f.Write([]byte{1, 2, 3})
882 f.Seek(0, io.SeekStart)
883 n, _ = f.Read(buf[:1])
884 c.Check(n, check.Equals, 1)
885 c.Check(buf[:1], check.DeepEquals, []byte{1})
886 pos, err = f.Seek(0, io.SeekCurrent)
887 c.Assert(err, check.IsNil)
888 c.Check(pos, check.Equals, int64(1))
889 f.Write([]byte{4, 5, 6})
890 pos, err = f.Seek(0, io.SeekCurrent)
891 c.Assert(err, check.IsNil)
892 c.Check(pos, check.Equals, int64(6))
893 f.Seek(0, io.SeekStart)
895 c.Check(buf[:n], check.DeepEquals, []byte{1, 2, 3, 4, 5, 6})
896 c.Check(err, check.Equals, io.EOF)
899 f, err = fs.OpenFile("append", os.O_RDWR|os.O_APPEND, 0)
900 c.Assert(err, check.IsNil)
901 pos, err = f.Seek(0, io.SeekCurrent)
902 c.Check(pos, check.Equals, int64(0))
903 c.Check(err, check.IsNil)
905 pos, _ = f.Seek(0, io.SeekCurrent)
906 c.Check(pos, check.Equals, int64(3))
907 f.Write([]byte{7, 8, 9})
908 pos, err = f.Seek(0, io.SeekCurrent)
909 c.Check(err, check.IsNil)
910 c.Check(pos, check.Equals, int64(9))
913 f, err = fs.OpenFile("wronly", os.O_CREATE|os.O_WRONLY, 0)
914 c.Assert(err, check.IsNil)
915 n, err = f.Write([]byte{3, 2, 1})
916 c.Check(n, check.Equals, 3)
917 c.Check(err, check.IsNil)
918 pos, _ = f.Seek(0, io.SeekCurrent)
919 c.Check(pos, check.Equals, int64(3))
920 pos, _ = f.Seek(0, io.SeekStart)
921 c.Check(pos, check.Equals, int64(0))
923 c.Check(n, check.Equals, 0)
924 c.Check(err, check.ErrorMatches, `.*O_WRONLY.*`)
925 f, err = fs.OpenFile("wronly", os.O_RDONLY, 0)
926 c.Assert(err, check.IsNil)
928 c.Check(buf[:n], check.DeepEquals, []byte{3, 2, 1})
930 f, err = fs.OpenFile("unsupported", os.O_CREATE|os.O_SYNC, 0)
931 c.Check(f, check.IsNil)
932 c.Check(err, check.NotNil)
934 f, err = fs.OpenFile("append", os.O_RDWR|os.O_WRONLY, 0)
935 c.Check(f, check.IsNil)
936 c.Check(err, check.ErrorMatches, `invalid flag.*`)
939 func (s *CollectionFSSuite) TestFlushFullBlocks(c *check.C) {
941 defer func() { maxBlockSize = 2 << 26 }()
943 fs, err := (&Collection{}).FileSystem(s.client, s.kc)
944 c.Assert(err, check.IsNil)
945 f, err := fs.OpenFile("50K", os.O_WRONLY|os.O_CREATE, 0)
946 c.Assert(err, check.IsNil)
949 data := make([]byte, 500)
952 for i := 0; i < 100; i++ {
953 n, err := f.Write(data)
954 c.Assert(n, check.Equals, len(data))
955 c.Assert(err, check.IsNil)
958 currentMemExtents := func() (memExtents []int) {
959 for idx, e := range f.(*filehandle).inode.(*filenode).segments {
962 memExtents = append(memExtents, idx)
967 c.Check(currentMemExtents(), check.HasLen, 1)
969 m, err := fs.MarshalManifest(".")
970 c.Check(m, check.Matches, `[^:]* 0:50000:50K\n`)
971 c.Check(err, check.IsNil)
972 c.Check(currentMemExtents(), check.HasLen, 0)
975 func (s *CollectionFSSuite) TestBrokenManifests(c *check.C) {
976 for _, txt := range []string{
980 ". d41d8cd98f00b204e9800998ecf8427e+0\n",
981 ". d41d8cd98f00b204e9800998ecf8427e+0 \n",
984 ". 0:0:foo 0:0:bar\n",
985 ". d41d8cd98f00b204e9800998ecf8427e 0:0:foo\n",
986 ". d41d8cd98f00b204e9800998ecf8427e+0 :0:0:foo\n",
987 ". d41d8cd98f00b204e9800998ecf8427e+0 foo:0:foo\n",
988 ". d41d8cd98f00b204e9800998ecf8427e+0 0:foo:foo\n",
989 ". d41d8cd98f00b204e9800998ecf8427e+1 0:1:foo 1:1:bar\n",
990 ". d41d8cd98f00b204e9800998ecf8427e+1 0:0:foo\n./foo d41d8cd98f00b204e9800998ecf8427e+1 0:0:bar\n",
991 "./foo d41d8cd98f00b204e9800998ecf8427e+1 0:0:bar\n. d41d8cd98f00b204e9800998ecf8427e+1 0:0:foo\n",
994 fs, err := (&Collection{ManifestText: txt}).FileSystem(s.client, s.kc)
995 c.Check(fs, check.IsNil)
997 c.Check(err, check.NotNil)
1001 func (s *CollectionFSSuite) TestEdgeCaseManifests(c *check.C) {
1002 for _, txt := range []string{
1004 ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:foo\n",
1005 ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:foo 0:0:foo 0:0:bar\n",
1006 ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:foo 0:0:foo 0:0:bar\n",
1007 ". d41d8cd98f00b204e9800998ecf8427e+0 0:0:foo/bar\n./foo d41d8cd98f00b204e9800998ecf8427e+0 0:0:bar\n",
1010 fs, err := (&Collection{ManifestText: txt}).FileSystem(s.client, s.kc)
1011 c.Check(err, check.IsNil)
1012 c.Check(fs, check.NotNil)
1016 func (s *CollectionFSSuite) checkMemSize(c *check.C, f File) {
1017 fn := f.(*filehandle).inode.(*filenode)
1019 for _, seg := range fn.segments {
1020 if e, ok := seg.(*memSegment); ok {
1021 memsize += int64(len(e.buf))
1024 c.Check(fn.memsize, check.Equals, memsize)
1027 type CollectionFSUnitSuite struct{}
1029 var _ = check.Suite(&CollectionFSUnitSuite{})
1031 // expect ~2 seconds to load a manifest with 256K files
1032 func (s *CollectionFSUnitSuite) TestLargeManifest(c *check.C) {
1033 if testing.Short() {
1042 mb := bytes.NewBuffer(make([]byte, 0, 40000000))
1043 for i := 0; i < dirCount; i++ {
1044 fmt.Fprintf(mb, "./dir%d", i)
1045 for j := 0; j <= fileCount; j++ {
1046 fmt.Fprintf(mb, " %032x+42+A%040x@%08x", j, j, j)
1048 for j := 0; j < fileCount; j++ {
1049 fmt.Fprintf(mb, " %d:%d:dir%d/file%d", j*42+21, 42, j, j)
1051 mb.Write([]byte{'\n'})
1053 coll := Collection{ManifestText: mb.String()}
1054 c.Logf("%s built", time.Now())
1056 var memstats runtime.MemStats
1057 runtime.ReadMemStats(&memstats)
1058 c.Logf("%s Alloc=%d Sys=%d", time.Now(), memstats.Alloc, memstats.Sys)
1060 f, err := coll.FileSystem(nil, nil)
1061 c.Check(err, check.IsNil)
1062 c.Logf("%s loaded", time.Now())
1064 for i := 0; i < dirCount; i++ {
1065 for j := 0; j < fileCount; j++ {
1066 f.Stat(fmt.Sprintf("./dir%d/dir%d/file%d", i, j, j))
1069 c.Logf("%s Stat() x %d", time.Now(), dirCount*fileCount)
1071 runtime.ReadMemStats(&memstats)
1072 c.Logf("%s Alloc=%d Sys=%d", time.Now(), memstats.Alloc, memstats.Sys)
1075 // Gocheck boilerplate
1076 func Test(t *testing.T) {