1// +build linux darwin 2 3/* 4Copyright 2013 The Perkeep Authors 5 6Licensed under the Apache License, Version 2.0 (the "License"); 7you may not use this file except in compliance with the License. 8You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12Unless required by applicable law or agreed to in writing, software 13distributed under the License is distributed on an "AS IS" BASIS, 14WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15See the License for the specific language governing permissions and 16limitations under the License. 17*/ 18 19package fs 20 21import ( 22 "context" 23 "errors" 24 "fmt" 25 "io" 26 "io/ioutil" 27 "os" 28 "path/filepath" 29 "strings" 30 "sync" 31 "time" 32 33 "perkeep.org/pkg/blob" 34 "perkeep.org/pkg/schema" 35 "perkeep.org/pkg/search" 36 37 "bazil.org/fuse" 38 "bazil.org/fuse/fs" 39 "go4.org/readerutil" 40 "go4.org/syncutil" 41) 42 43// How often to refresh directory nodes by reading from the blobstore. 44const populateInterval = 30 * time.Second 45 46// How long an item that was created locally will be present 47// regardless of its presence in the indexing server. 48const deletionRefreshWindow = time.Minute 49 50type nodeType int 51 52const ( 53 fileType nodeType = iota 54 dirType 55 symlinkType 56) 57 58// mutDir is a mutable directory. 59// Its br is the permanode with camliPath:entname attributes. 60type mutDir struct { 61 fs *CamliFileSystem 62 permanode blob.Ref 63 parent *mutDir // or nil, if the root within its roots.go root. 64 name string // ent name (base name within parent) 65 66 localCreateTime time.Time // time this node was created locally (iff it was) 67 68 mu sync.Mutex 69 lastPop time.Time 70 children map[string]mutFileOrDir 71 xattrs map[string][]byte 72 deleted bool 73} 74 75var _ fs.Node = (*mutDir)(nil) 76var _ fs.NodeAccesser = (*mutDir)(nil) 77var _ fs.HandleReadDirAller = (*mutDir)(nil) 78var _ fs.NodeStringLookuper = (*mutDir)(nil) 79var _ fs.NodeGetxattrer = (*mutDir)(nil) 80var _ fs.NodeListxattrer = (*mutDir)(nil) 81var _ fs.NodeSetxattrer = (*mutDir)(nil) 82var _ fs.NodeCreater = (*mutDir)(nil) 83var _ fs.NodeMkdirer = (*mutDir)(nil) 84var _ fs.NodeSymlinker = (*mutDir)(nil) 85var _ fs.NodeRemover = (*mutDir)(nil) 86var _ fs.NodeRenamer = (*mutDir)(nil) 87 88func (n *mutDir) String() string { 89 return fmt.Sprintf("&mutDir{%p name=%q perm:%v}", n, n.fullPath(), n.permanode) 90} 91 92// for debugging 93func (n *mutDir) fullPath() string { 94 if n == nil { 95 return "" 96 } 97 return filepath.Join(n.parent.fullPath(), n.name) 98} 99 100func (n *mutDir) Attr(ctx context.Context, a *fuse.Attr) error { 101 a.Inode = n.permanode.Sum64() 102 a.Mode = os.ModeDir | 0700 103 a.Uid = uint32(os.Getuid()) 104 a.Gid = uint32(os.Getgid()) 105 return nil 106} 107 108func (n *mutDir) Access(ctx context.Context, req *fuse.AccessRequest) error { 109 n.mu.Lock() 110 defer n.mu.Unlock() 111 if n.deleted { 112 return fuse.ENOENT 113 } 114 return nil 115} 116 117func (n *mutFile) Access(ctx context.Context, req *fuse.AccessRequest) error { 118 n.mu.Lock() 119 defer n.mu.Unlock() 120 if n.deleted { 121 return fuse.ENOENT 122 } 123 return nil 124} 125 126// populate hits the blobstore to populate map of child nodes. 127func (n *mutDir) populate() error { 128 n.mu.Lock() 129 defer n.mu.Unlock() 130 ctx := context.TODO() 131 132 // Only re-populate if we haven't done so recently. 133 now := time.Now() 134 if n.lastPop.Add(populateInterval).After(now) { 135 return nil 136 } 137 n.lastPop = now 138 139 res, err := n.fs.client.Describe(ctx, &search.DescribeRequest{ 140 BlobRef: n.permanode, 141 Depth: 3, 142 }) 143 if err != nil { 144 Logger.Println("mutDir.paths:", err) 145 return nil 146 } 147 db := res.Meta[n.permanode.String()] 148 if db == nil { 149 return errors.New("dir blobref not described") 150 } 151 152 // Find all child permanodes and stick them in n.children 153 if n.children == nil { 154 n.children = make(map[string]mutFileOrDir) 155 } 156 currentChildren := map[string]bool{} 157 for k, v := range db.Permanode.Attr { 158 const p = "camliPath:" 159 if !strings.HasPrefix(k, p) || len(v) < 1 { 160 continue 161 } 162 name := k[len(p):] 163 childRef := v[0] 164 child := res.Meta[childRef] 165 if child == nil { 166 Logger.Printf("child not described: %v", childRef) 167 continue 168 } 169 if child.Permanode == nil { 170 Logger.Printf("invalid child, not a permanode: %v", childRef) 171 continue 172 } 173 if target := child.Permanode.Attr.Get("camliSymlinkTarget"); target != "" { 174 // This is a symlink. 175 n.maybeAddChild(name, child.Permanode, &mutFile{ 176 fs: n.fs, 177 permanode: blob.ParseOrZero(childRef), 178 parent: n, 179 name: name, 180 symLink: true, 181 target: target, 182 }) 183 } else if isDir(child.Permanode) { 184 // This is a directory. 185 n.maybeAddChild(name, child.Permanode, &mutDir{ 186 fs: n.fs, 187 permanode: blob.ParseOrZero(childRef), 188 parent: n, 189 name: name, 190 }) 191 } else if contentRef := child.Permanode.Attr.Get("camliContent"); contentRef != "" { 192 // This is a file. 193 content := res.Meta[contentRef] 194 if content == nil { 195 Logger.Printf("child content not described: %v", childRef) 196 continue 197 } 198 if content.CamliType != "file" { 199 Logger.Printf("child not a file: %v", childRef) 200 continue 201 } 202 if content.File == nil { 203 Logger.Printf("camlitype \"file\" child %v has no described File member", childRef) 204 continue 205 } 206 n.maybeAddChild(name, child.Permanode, &mutFile{ 207 fs: n.fs, 208 permanode: blob.ParseOrZero(childRef), 209 parent: n, 210 name: name, 211 content: blob.ParseOrZero(contentRef), 212 size: content.File.Size, 213 }) 214 } else { 215 // unhandled type... 216 continue 217 } 218 currentChildren[name] = true 219 } 220 // Remove unreferenced children 221 for name, oldchild := range n.children { 222 if _, ok := currentChildren[name]; !ok { 223 if oldchild.eligibleToDelete() { 224 delete(n.children, name) 225 } 226 } 227 } 228 return nil 229} 230 231// maybeAddChild adds a child directory to this mutable directory 232// unless it already has one with this name and permanode. 233func (n *mutDir) maybeAddChild(name string, permanode *search.DescribedPermanode, 234 child mutFileOrDir) { 235 if current, ok := n.children[name]; !ok || 236 current.permanodeString() != child.permanodeString() { 237 238 child.xattr().load(permanode) 239 n.children[name] = child 240 } 241} 242 243func isDir(d *search.DescribedPermanode) bool { 244 // Explicit 245 if d.Attr.Get("camliNodeType") == "directory" { 246 return true 247 } 248 // Implied 249 for k := range d.Attr { 250 if strings.HasPrefix(k, "camliPath:") { 251 return true 252 } 253 } 254 return false 255} 256 257func (n *mutDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { 258 if err := n.populate(); err != nil { 259 Logger.Println("populate:", err) 260 return nil, fuse.EIO 261 } 262 n.mu.Lock() 263 defer n.mu.Unlock() 264 var ents []fuse.Dirent 265 for name, childNode := range n.children { 266 var ino uint64 267 switch v := childNode.(type) { 268 case *mutDir: 269 ino = v.permanode.Sum64() 270 case *mutFile: 271 ino = v.permanode.Sum64() 272 default: 273 Logger.Printf("mutDir.ReadDirAll: unknown child type %T", childNode) 274 } 275 276 // TODO: figure out what Dirent.Type means. 277 // fuse.go says "Type uint32 // ?" 278 dirent := fuse.Dirent{ 279 Name: name, 280 Inode: ino, 281 } 282 Logger.Printf("mutDir(%q) appending inode %x, %+v", n.fullPath(), dirent.Inode, dirent) 283 ents = append(ents, dirent) 284 } 285 return ents, nil 286} 287 288func (n *mutDir) Lookup(ctx context.Context, name string) (ret fs.Node, err error) { 289 defer func() { 290 Logger.Printf("mutDir(%q).Lookup(%q) = %v, %v", n.fullPath(), name, ret, err) 291 }() 292 if err := n.populate(); err != nil { 293 Logger.Println("populate:", err) 294 return nil, fuse.EIO 295 } 296 n.mu.Lock() 297 defer n.mu.Unlock() 298 if n2 := n.children[name]; n2 != nil { 299 return n2, nil 300 } 301 return nil, fuse.ENOENT 302} 303 304// Create of regular file. (not a dir) 305// 306// Flags are always 514: O_CREAT is 0x200 | O_RDWR is 0x2. 307// From fuse_vnops.c: 308// /* XXX: We /always/ creat() like this. Wish we were on Linux. */ 309// foi->flags = O_CREAT | O_RDWR; 310// 311// 2013/07/21 05:26:35 <- &{Create [ID=0x3 Node=0x8 Uid=61652 Gid=5000 Pid=13115] "x" fl=514 mode=-rw-r--r-- fuse.Intr} 312// 2013/07/21 05:26:36 -> 0x3 Create {LookupResponse:{Node:23 Generation:0 EntryValid:1m0s AttrValid:1m0s Attr:{Inode:15976986887557313215 Size:0 Blocks:0 Atime:2013-07-21 05:23:51.537251251 +1200 NZST Mtime:2013-07-21 05:23:51.537251251 +1200 NZST Ctime:2013-07-21 05:23:51.537251251 +1200 NZST Crtime:2013-07-21 05:23:51.537251251 +1200 NZST Mode:-rw------- Nlink:1 Uid:61652 Gid:5000 Rdev:0 Flags:0}} OpenResponse:{Handle:1 Flags:0}} 313func (n *mutDir) Create(ctx context.Context, req *fuse.CreateRequest, res *fuse.CreateResponse) (fs.Node, fs.Handle, error) { 314 child, err := n.creat(ctx, req.Name, fileType) 315 if err != nil { 316 Logger.Printf("mutDir.Create(%q): %v", req.Name, err) 317 return nil, nil, fuse.EIO 318 } 319 320 // Create and return a file handle. 321 h, ferr := child.(*mutFile).newHandle(nil) 322 if ferr != nil { 323 return nil, nil, ferr 324 } 325 326 return child, h, nil 327} 328 329func (n *mutDir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) { 330 child, err := n.creat(ctx, req.Name, dirType) 331 if err != nil { 332 Logger.Printf("mutDir.Mkdir(%q): %v", req.Name, err) 333 return nil, fuse.EIO 334 } 335 return child, nil 336} 337 338// &fuse.SymlinkRequest{Header:fuse.Header{Conn:(*fuse.Conn)(0xc210047180), ID:0x4, Node:0x8, Uid:0xf0d4, Gid:0x1388, Pid:0x7e88}, NewName:"some-link", Target:"../../some-target"} 339func (n *mutDir) Symlink(ctx context.Context, req *fuse.SymlinkRequest) (fs.Node, error) { 340 node, err := n.creat(ctx, req.NewName, symlinkType) 341 if err != nil { 342 Logger.Printf("mutDir.Symlink(%q): %v", req.NewName, err) 343 return nil, fuse.EIO 344 } 345 mf := node.(*mutFile) 346 mf.symLink = true 347 mf.target = req.Target 348 349 claim := schema.NewSetAttributeClaim(mf.permanode, "camliSymlinkTarget", req.Target) 350 _, err = n.fs.client.UploadAndSignBlob(ctx, claim) 351 if err != nil { 352 Logger.Printf("mutDir.Symlink(%q) upload error: %v", req.NewName, err) 353 return nil, fuse.EIO 354 } 355 356 return node, nil 357} 358 359func (n *mutDir) creat(ctx context.Context, name string, typ nodeType) (fs.Node, error) { 360 // Create a Permanode for the file/directory. 361 pr, err := n.fs.client.UploadNewPermanode(ctx) 362 if err != nil { 363 return nil, err 364 } 365 366 var grp syncutil.Group 367 grp.Go(func() (err error) { 368 // Add a camliPath:name attribute to the directory permanode. 369 claim := schema.NewSetAttributeClaim(n.permanode, "camliPath:"+name, pr.BlobRef.String()) 370 _, err = n.fs.client.UploadAndSignBlob(ctx, claim) 371 return 372 }) 373 374 // Hide OS X Finder .DS_Store junk. This is distinct from 375 // extended attributes. 376 if name == ".DS_Store" { 377 grp.Go(func() (err error) { 378 claim := schema.NewSetAttributeClaim(pr.BlobRef, "camliDefVis", "hide") 379 _, err = n.fs.client.UploadAndSignBlob(ctx, claim) 380 return 381 }) 382 } 383 384 if typ == dirType { 385 grp.Go(func() (err error) { 386 // Set a directory type on the permanode 387 claim := schema.NewSetAttributeClaim(pr.BlobRef, "camliNodeType", "directory") 388 _, err = n.fs.client.UploadAndSignBlob(ctx, claim) 389 return 390 }) 391 grp.Go(func() (err error) { 392 // Set the permanode title to the directory name 393 claim := schema.NewSetAttributeClaim(pr.BlobRef, "title", name) 394 _, err = n.fs.client.UploadAndSignBlob(ctx, claim) 395 return 396 }) 397 } 398 if err := grp.Err(); err != nil { 399 return nil, err 400 } 401 402 // Add a child node to this node. 403 var child mutFileOrDir 404 switch typ { 405 case dirType: 406 child = &mutDir{ 407 fs: n.fs, 408 permanode: pr.BlobRef, 409 parent: n, 410 name: name, 411 xattrs: map[string][]byte{}, 412 localCreateTime: time.Now(), 413 } 414 case fileType, symlinkType: 415 child = &mutFile{ 416 fs: n.fs, 417 permanode: pr.BlobRef, 418 parent: n, 419 name: name, 420 xattrs: map[string][]byte{}, 421 localCreateTime: time.Now(), 422 } 423 default: 424 panic("bogus creat type") 425 } 426 n.mu.Lock() 427 if n.children == nil { 428 n.children = make(map[string]mutFileOrDir) 429 } 430 n.children[name] = child 431 n.mu.Unlock() 432 433 Logger.Printf("Created %v in %p", child, n) 434 435 return child, nil 436} 437 438func (n *mutDir) Remove(ctx context.Context, req *fuse.RemoveRequest) error { 439 // Remove the camliPath:name attribute from the directory permanode. 440 claim := schema.NewDelAttributeClaim(n.permanode, "camliPath:"+req.Name, "") 441 _, err := n.fs.client.UploadAndSignBlob(ctx, claim) 442 if err != nil { 443 Logger.Println("mutDir.Remove:", err) 444 return fuse.EIO 445 } 446 // Remove child from map. 447 n.mu.Lock() 448 if n.children != nil { 449 if removed, ok := n.children[req.Name]; ok { 450 removed.invalidate() 451 delete(n.children, req.Name) 452 Logger.Printf("Removed %v from %p", removed, n) 453 } 454 } 455 n.mu.Unlock() 456 return nil 457} 458 459// &RenameRequest{Header:fuse.Header{Conn:(*fuse.Conn)(0xc210048180), ID:0x2, Node:0x8, Uid:0xf0d4, Gid:0x1388, Pid:0x5edb}, NewDir:0x8, OldName:"1", NewName:"2"} 460func (n *mutDir) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.Node) error { 461 n2, ok := newDir.(*mutDir) 462 if !ok { 463 Logger.Printf("*mutDir newDir node isn't a *mutDir; is a %T; can't handle. returning EIO.", newDir) 464 return fuse.EIO 465 } 466 467 var wg syncutil.Group 468 wg.Go(n.populate) 469 wg.Go(n2.populate) 470 if err := wg.Err(); err != nil { 471 Logger.Printf("*mutDir.Rename src dir populate = %v", err) 472 return fuse.EIO 473 } 474 475 n.mu.Lock() 476 target, ok := n.children[req.OldName] 477 n.mu.Unlock() 478 if !ok { 479 Logger.Printf("*mutDir.Rename src name %q isn't known", req.OldName) 480 return fuse.ENOENT 481 } 482 483 now := time.Now() 484 485 // Add a camliPath:name attribute to the dest permanode before unlinking it from 486 // the source. 487 claim := schema.NewSetAttributeClaim(n2.permanode, "camliPath:"+req.NewName, target.permanodeString()) 488 claim.SetClaimDate(now) 489 _, err := n.fs.client.UploadAndSignBlob(ctx, claim) 490 if err != nil { 491 Logger.Printf("Upload rename link error: %v", err) 492 return fuse.EIO 493 } 494 495 var grp syncutil.Group 496 // Unlink the dest permanode from the source. 497 grp.Go(func() (err error) { 498 delClaim := schema.NewDelAttributeClaim(n.permanode, "camliPath:"+req.OldName, "") 499 delClaim.SetClaimDate(now) 500 _, err = n.fs.client.UploadAndSignBlob(ctx, delClaim) 501 return 502 }) 503 // If target is a directory then update its title. 504 if dir, ok := target.(*mutDir); ok { 505 grp.Go(func() (err error) { 506 claim := schema.NewSetAttributeClaim(dir.permanode, "title", req.NewName) 507 _, err = n.fs.client.UploadAndSignBlob(ctx, claim) 508 return 509 }) 510 } 511 if err := grp.Err(); err != nil { 512 Logger.Printf("Upload rename unlink/title error: %v", err) 513 return fuse.EIO 514 } 515 516 // TODO(bradfitz): this locking would be racy, if the kernel 517 // doesn't do it properly. (It should) Let's just trust the 518 // kernel for now. Later we can verify and remove this 519 // comment. 520 n.mu.Lock() 521 if n.children[req.OldName] != target { 522 panic("Race.") 523 } 524 delete(n.children, req.OldName) 525 n.mu.Unlock() 526 n2.mu.Lock() 527 n2.children[req.NewName] = target 528 n2.mu.Unlock() 529 530 return nil 531} 532 533// mutFile is a mutable file, or symlink. 534type mutFile struct { 535 fs *CamliFileSystem 536 permanode blob.Ref 537 parent *mutDir 538 name string // ent name (base name within parent) 539 540 localCreateTime time.Time // time this node was created locally (iff it was) 541 542 mu sync.Mutex // protects all following fields 543 symLink bool // if true, is a symlink 544 target string // if a symlink 545 content blob.Ref // if a regular file 546 size int64 547 mtime, atime time.Time // if zero, use serverStart 548 xattrs map[string][]byte 549 deleted bool 550} 551 552var ( 553 _ fs.Node = (*mutFile)(nil) 554 _ fs.NodeAccesser = (*mutFile)(nil) 555 _ fs.NodeGetxattrer = (*mutFile)(nil) 556 _ fs.NodeListxattrer = (*mutFile)(nil) 557 _ fs.NodeSetxattrer = (*mutFile)(nil) 558 _ fs.NodeOpener = (*mutFile)(nil) 559 _ fs.NodeFsyncer = (*mutFile)(nil) 560 _ fs.NodeReadlinker = (*mutFile)(nil) 561 _ fs.NodeSetattrer = (*mutFile)(nil) 562) 563 564func (n *mutFile) String() string { 565 return fmt.Sprintf("&mutFile{%p name=%q perm:%v}", n, n.fullPath(), n.permanode) 566} 567 568// for debugging 569func (n *mutFile) fullPath() string { 570 if n == nil { 571 return "" 572 } 573 return filepath.Join(n.parent.fullPath(), n.name) 574} 575 576func (n *mutFile) xattr() *xattr { 577 return &xattr{"mutFile", n.fs, n.permanode, &n.mu, &n.xattrs} 578} 579 580func (n *mutDir) xattr() *xattr { 581 return &xattr{"mutDir", n.fs, n.permanode, &n.mu, &n.xattrs} 582} 583 584func (n *mutDir) Removexattr(ctx context.Context, req *fuse.RemovexattrRequest) error { 585 return n.xattr().remove(ctx, req) 586} 587 588func (n *mutDir) Setxattr(ctx context.Context, req *fuse.SetxattrRequest) error { 589 return n.xattr().set(ctx, req) 590} 591 592func (n *mutDir) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, res *fuse.GetxattrResponse) error { 593 return n.xattr().get(req, res) 594} 595 596func (n *mutDir) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, res *fuse.ListxattrResponse) error { 597 return n.xattr().list(req, res) 598} 599 600func (n *mutFile) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, res *fuse.GetxattrResponse) error { 601 return n.xattr().get(req, res) 602} 603 604func (n *mutFile) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, res *fuse.ListxattrResponse) error { 605 return n.xattr().list(req, res) 606} 607 608func (n *mutFile) Removexattr(ctx context.Context, req *fuse.RemovexattrRequest) error { 609 return n.xattr().remove(ctx, req) 610} 611 612func (n *mutFile) Setxattr(ctx context.Context, req *fuse.SetxattrRequest) error { 613 return n.xattr().set(ctx, req) 614} 615 616func (n *mutFile) Attr(ctx context.Context, a *fuse.Attr) error { 617 // TODO: don't grab n.mu three+ times in here. 618 var mode os.FileMode = 0600 // writable 619 620 n.mu.Lock() 621 size := n.size 622 var blocks uint64 623 if size > 0 { 624 blocks = uint64(size)/512 + 1 625 } 626 inode := n.permanode.Sum64() 627 if n.symLink { 628 mode |= os.ModeSymlink 629 } 630 n.mu.Unlock() 631 632 a.Inode = inode 633 a.Mode = mode 634 a.Uid = uint32(os.Getuid()) 635 a.Gid = uint32(os.Getgid()) 636 a.Size = uint64(size) 637 a.Blocks = blocks 638 a.Mtime = n.modTime() 639 a.Atime = n.accessTime() 640 a.Ctime = serverStart 641 a.Crtime = serverStart 642 return nil 643} 644 645func (n *mutFile) accessTime() time.Time { 646 n.mu.Lock() 647 if !n.atime.IsZero() { 648 defer n.mu.Unlock() 649 return n.atime 650 } 651 n.mu.Unlock() 652 return n.modTime() 653} 654 655func (n *mutFile) modTime() time.Time { 656 n.mu.Lock() 657 defer n.mu.Unlock() 658 if !n.mtime.IsZero() { 659 return n.mtime 660 } 661 return serverStart 662} 663 664func (n *mutFile) setContent(ctx context.Context, br blob.Ref, size int64) error { 665 n.mu.Lock() 666 defer n.mu.Unlock() 667 n.content = br 668 n.size = size 669 claim := schema.NewSetAttributeClaim(n.permanode, "camliContent", br.String()) 670 _, err := n.fs.client.UploadAndSignBlob(ctx, claim) 671 return err 672} 673 674func (n *mutFile) setSizeAtLeast(size int64) { 675 n.mu.Lock() 676 defer n.mu.Unlock() 677 Logger.Printf("mutFile.setSizeAtLeast(%d). old size = %d", size, n.size) 678 if size > n.size { 679 n.size = size 680 } 681} 682 683// Empirically: 684// open for read: req.Flags == 0 685// open for append: req.Flags == 1 686// open for write: req.Flags == 1 687// open for read/write (+<) == 2 (bitmask? of?) 688// 689// open flags are O_WRONLY (1), O_RDONLY (0), or O_RDWR (2). and also 690// bitmaks of O_SYMLINK (0x200000) maybe. (from 691// fuse_filehandle_xlate_to_oflags in macosx/kext/fuse_file.h) 692func (n *mutFile) Open(ctx context.Context, req *fuse.OpenRequest, res *fuse.OpenResponse) (fs.Handle, error) { 693 mutFileOpen.Incr() 694 695 Logger.Printf("mutFile.Open: %v: content: %v dir=%v flags=%v", n.permanode, n.content, req.Dir, req.Flags) 696 r, err := schema.NewFileReader(ctx, n.fs.fetcher, n.content) 697 if err != nil { 698 mutFileOpenError.Incr() 699 Logger.Printf("mutFile.Open: %v", err) 700 return nil, fuse.EIO 701 } 702 703 // Read-only. 704 if !isWriteFlags(req.Flags) { 705 mutFileOpenRO.Incr() 706 Logger.Printf("mutFile.Open returning read-only file") 707 n := &node{ 708 fs: n.fs, 709 blobref: n.content, 710 } 711 return &nodeReader{n: n, fr: r}, nil 712 } 713 714 mutFileOpenRW.Incr() 715 Logger.Printf("mutFile.Open returning read-write filehandle") 716 717 defer r.Close() 718 return n.newHandle(r) 719} 720 721func (n *mutFile) Fsync(ctx context.Context, r *fuse.FsyncRequest) error { 722 // TODO(adg): in the fuse package, plumb through fsync to mutFileHandle 723 // in the same way we did Truncate. 724 Logger.Printf("mutFile.Fsync: TODO") 725 return nil 726} 727 728func (n *mutFile) Readlink(ctx context.Context, req *fuse.ReadlinkRequest) (string, error) { 729 n.mu.Lock() 730 defer n.mu.Unlock() 731 if !n.symLink { 732 Logger.Printf("mutFile.Readlink on node that's not a symlink?") 733 return "", fuse.EIO 734 } 735 return n.target, nil 736} 737 738func (n *mutFile) Setattr(ctx context.Context, req *fuse.SetattrRequest, res *fuse.SetattrResponse) error { 739 Logger.Printf("mutFile.Setattr on %q: %#v", n.fullPath(), req) 740 // 2013/07/17 19:43:41 mutFile.Setattr on "foo": &fuse.SetattrRequest{Header:fuse.Header{Conn:(*fuse.Conn)(0xc210047180), ID:0x3, Node:0x3d, Uid:0xf0d4, Gid:0x1388, Pid:0x75e8}, Valid:0x30, Handle:0x0, Size:0x0, Atime:time.Time{sec:63509651021, nsec:0x4aec6b8, loc:(*time.Location)(0x47f7600)}, Mtime:time.Time{sec:63509651021, nsec:0x4aec6b8, loc:(*time.Location)(0x47f7600)}, Mode:0x4000000, Uid:0x0, Gid:0x0, Bkuptime:time.Time{sec:62135596800, nsec:0x0, loc:(*time.Location)(0x47f7600)}, Chgtime:time.Time{sec:62135596800, nsec:0x0, loc:(*time.Location)(0x47f7600)}, Crtime:time.Time{sec:0, nsec:0x0, loc:(*time.Location)(nil)}, Flags:0x0} 741 742 n.mu.Lock() 743 if req.Valid&fuse.SetattrMtime != 0 { 744 n.mtime = req.Mtime 745 } 746 if req.Valid&fuse.SetattrAtime != 0 { 747 n.atime = req.Atime 748 } 749 if req.Valid&fuse.SetattrSize != 0 { 750 // TODO(bradfitz): truncate? 751 n.size = int64(req.Size) 752 } 753 n.mu.Unlock() 754 755 n.Attr(ctx, &res.Attr) 756 return nil 757} 758 759func (n *mutFile) newHandle(body io.Reader) (fs.Handle, error) { 760 tmp, err := ioutil.TempFile("", "camli-") 761 if err == nil && body != nil { 762 _, err = io.Copy(tmp, body) 763 } 764 if err != nil { 765 Logger.Printf("mutFile.newHandle: %v", err) 766 if tmp != nil { 767 tmp.Close() 768 os.Remove(tmp.Name()) 769 } 770 return nil, fuse.EIO 771 } 772 return &mutFileHandle{f: n, tmp: tmp}, nil 773} 774 775// mutFileHandle represents an open mutable file. 776// It stores the file contents in a temporary file, and 777// delegates reads and writes directly to the temporary file. 778// When the handle is released, it writes the contents of the 779// temporary file to the blobstore, and instructs the parent 780// mutFile to update the file permanode. 781type mutFileHandle struct { 782 f *mutFile 783 tmp *os.File 784} 785 786var ( 787 _ fs.HandleReader = (*mutFileHandle)(nil) 788 _ fs.HandleWriter = (*mutFileHandle)(nil) 789 _ fs.HandleFlusher = (*mutFileHandle)(nil) 790 _ fs.HandleReleaser = (*mutFileHandle)(nil) 791) 792 793func (h *mutFileHandle) Read(ctx context.Context, req *fuse.ReadRequest, res *fuse.ReadResponse) error { 794 if h.tmp == nil { 795 Logger.Printf("Read called on camli mutFileHandle without a tempfile set") 796 return fuse.EIO 797 } 798 799 buf := make([]byte, req.Size) 800 n, err := h.tmp.ReadAt(buf, req.Offset) 801 if err == io.EOF { 802 err = nil 803 } 804 if err != nil { 805 Logger.Printf("mutFileHandle.Read: %v", err) 806 return fuse.EIO 807 } 808 res.Data = buf[:n] 809 return nil 810} 811 812func (h *mutFileHandle) Write(ctx context.Context, req *fuse.WriteRequest, res *fuse.WriteResponse) error { 813 if h.tmp == nil { 814 Logger.Printf("Write called on camli mutFileHandle without a tempfile set") 815 return fuse.EIO 816 } 817 818 n, err := h.tmp.WriteAt(req.Data, req.Offset) 819 Logger.Printf("mutFileHandle.Write(%q, %d bytes at %d, flags %v) = %d, %v", 820 h.f.fullPath(), len(req.Data), req.Offset, req.Flags, n, err) 821 if err != nil { 822 Logger.Println("mutFileHandle.Write:", err) 823 return fuse.EIO 824 } 825 res.Size = n 826 h.f.setSizeAtLeast(req.Offset + int64(n)) 827 return nil 828} 829 830// Flush is called to let the file system clean up any data buffers 831// and to pass any errors in the process of closing a file to the user 832// application. 833// 834// Flush *may* be called more than once in the case where a file is 835// opened more than once, but it's not possible to detect from the 836// call itself whether this is a final flush. 837// 838// This is generally the last opportunity to finalize data and the 839// return value sets the return value of the Close that led to the 840// calling of Flush. 841// 842// Note that this is distinct from Fsync -- which is a user-requested 843// flush (fsync, etc...) 844func (h *mutFileHandle) Flush(ctx context.Context, req *fuse.FlushRequest) error { 845 if h.tmp == nil { 846 Logger.Printf("Flush called on camli mutFileHandle without a tempfile set") 847 return fuse.EIO 848 } 849 _, err := h.tmp.Seek(0, 0) 850 if err != nil { 851 Logger.Println("mutFileHandle.Flush:", err) 852 return fuse.EIO 853 } 854 var n int64 855 br, err := schema.WriteFileFromReader(ctx, h.f.fs.client, h.f.name, readerutil.CountingReader{Reader: h.tmp, N: &n}) 856 if err != nil { 857 Logger.Println("mutFileHandle.Flush:", err) 858 return fuse.EIO 859 } 860 err = h.f.setContent(ctx, br, n) 861 if err != nil { 862 Logger.Printf("mutFileHandle.Flush: %v", err) 863 return fuse.EIO 864 } 865 866 return nil 867} 868 869// Release is called when a file handle is no longer needed. This is 870// called asynchronously after the last handle to a file is closed. 871func (h *mutFileHandle) Release(ctx context.Context, req *fuse.ReleaseRequest) error { 872 h.tmp.Close() 873 os.Remove(h.tmp.Name()) 874 h.tmp = nil 875 876 return nil 877} 878 879// mutFileOrDir is a *mutFile or *mutDir 880type mutFileOrDir interface { 881 fs.Node 882 invalidate() 883 permanodeString() string 884 xattr() *xattr 885 eligibleToDelete() bool 886} 887 888func (n *mutFile) permanodeString() string { 889 return n.permanode.String() 890} 891 892func (n *mutDir) permanodeString() string { 893 return n.permanode.String() 894} 895 896func (n *mutFile) invalidate() { 897 n.mu.Lock() 898 n.deleted = true 899 n.mu.Unlock() 900} 901 902func (n *mutDir) invalidate() { 903 n.mu.Lock() 904 n.deleted = true 905 n.mu.Unlock() 906} 907 908func (n *mutFile) eligibleToDelete() bool { 909 return n.localCreateTime.Before(time.Now().Add(-deletionRefreshWindow)) 910} 911 912func (n *mutDir) eligibleToDelete() bool { 913 return n.localCreateTime.Before(time.Now().Add(-deletionRefreshWindow)) 914} 915