1// See the file LICENSE for copyright and licensing information. 2 3// Adapted from Plan 9 from User Space's src/cmd/9pfuse/fuse.c, 4// which carries this notice: 5// 6// The files in this directory are subject to the following license. 7// 8// The author of this software is Russ Cox. 9// 10// Copyright (c) 2006 Russ Cox 11// 12// Permission to use, copy, modify, and distribute this software for any 13// purpose without fee is hereby granted, provided that this entire notice 14// is included in all copies of any software which is or includes a copy 15// or modification of this software and in all copies of the supporting 16// documentation for such software. 17// 18// THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED 19// WARRANTY. IN PARTICULAR, THE AUTHOR MAKES NO REPRESENTATION OR WARRANTY 20// OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS 21// FITNESS FOR ANY PARTICULAR PURPOSE. 22 23// Package fuse enables writing FUSE file systems on Linux, OS X, and FreeBSD. 24// 25// On OS X, it requires OSXFUSE (http://osxfuse.github.com/). 26// 27// There are two approaches to writing a FUSE file system. The first is to speak 28// the low-level message protocol, reading from a Conn using ReadRequest and 29// writing using the various Respond methods. This approach is closest to 30// the actual interaction with the kernel and can be the simplest one in contexts 31// such as protocol translators. 32// 33// Servers of synthesized file systems tend to share common 34// bookkeeping abstracted away by the second approach, which is to 35// call fs.Serve to serve the FUSE protocol using an implementation of 36// the service methods in the interfaces FS* (file system), Node* (file 37// or directory), and Handle* (opened file or directory). 38// There are a daunting number of such methods that can be written, 39// but few are required. 40// The specific methods are described in the documentation for those interfaces. 41// 42// The hellofs subdirectory contains a simple illustration of the fs.Serve approach. 43// 44// Service Methods 45// 46// The required and optional methods for the FS, Node, and Handle interfaces 47// have the general form 48// 49// Op(ctx context.Context, req *OpRequest, resp *OpResponse) error 50// 51// where Op is the name of a FUSE operation. Op reads request 52// parameters from req and writes results to resp. An operation whose 53// only result is the error result omits the resp parameter. 54// 55// Multiple goroutines may call service methods simultaneously; the 56// methods being called are responsible for appropriate 57// synchronization. 58// 59// The operation must not hold on to the request or response, 60// including any []byte fields such as WriteRequest.Data or 61// SetxattrRequest.Xattr. 62// 63// Errors 64// 65// Operations can return errors. The FUSE interface can only 66// communicate POSIX errno error numbers to file system clients, the 67// message is not visible to file system clients. The returned error 68// can implement ErrorNumber to control the errno returned. Without 69// ErrorNumber, a generic errno (EIO) is returned. 70// 71// Error messages will be visible in the debug log as part of the 72// response. 73// 74// Interrupted Operations 75// 76// In some file systems, some operations 77// may take an undetermined amount of time. For example, a Read waiting for 78// a network message or a matching Write might wait indefinitely. If the request 79// is cancelled and no longer needed, the context will be cancelled. 80// Blocking operations should select on a receive from ctx.Done() and attempt to 81// abort the operation early if the receive succeeds (meaning the channel is closed). 82// To indicate that the operation failed because it was aborted, return fuse.EINTR. 83// 84// If an operation does not block for an indefinite amount of time, supporting 85// cancellation is not necessary. 86// 87// Authentication 88// 89// All requests types embed a Header, meaning that the method can 90// inspect req.Pid, req.Uid, and req.Gid as necessary to implement 91// permission checking. The kernel FUSE layer normally prevents other 92// users from accessing the FUSE file system (to change this, see 93// AllowOther, AllowRoot), but does not enforce access modes (to 94// change this, see DefaultPermissions). 95// 96// Mount Options 97// 98// Behavior and metadata of the mounted file system can be changed by 99// passing MountOption values to Mount. 100// 101package fuse // import "bazil.org/fuse" 102 103import ( 104 "bytes" 105 "encoding/json" 106 "errors" 107 "fmt" 108 "io" 109 "os" 110 "sync" 111 "syscall" 112 "time" 113 "unsafe" 114) 115 116// A Conn represents a connection to a mounted FUSE file system. 117type Conn struct { 118 // Ready is closed when the mount is complete or has failed. 119 Ready <-chan struct{} 120 121 // MountError stores any error from the mount process. Only valid 122 // after Ready is closed. 123 MountError error 124 125 // File handle for kernel communication. Only safe to access if 126 // rio or wio is held. 127 dev *os.File 128 wio sync.RWMutex 129 rio sync.RWMutex 130 131 // Protocol version negotiated with InitRequest/InitResponse. 132 proto Protocol 133} 134 135// MountpointDoesNotExistError is an error returned when the 136// mountpoint does not exist. 137type MountpointDoesNotExistError struct { 138 Path string 139} 140 141var _ error = (*MountpointDoesNotExistError)(nil) 142 143func (e *MountpointDoesNotExistError) Error() string { 144 return fmt.Sprintf("mountpoint does not exist: %v", e.Path) 145} 146 147// Mount mounts a new FUSE connection on the named directory 148// and returns a connection for reading and writing FUSE messages. 149// 150// After a successful return, caller must call Close to free 151// resources. 152// 153// Even on successful return, the new mount is not guaranteed to be 154// visible until after Conn.Ready is closed. See Conn.MountError for 155// possible errors. Incoming requests on Conn must be served to make 156// progress. 157func Mount(dir string, options ...MountOption) (*Conn, error) { 158 conf := mountConfig{ 159 options: make(map[string]string), 160 } 161 for _, option := range options { 162 if err := option(&conf); err != nil { 163 return nil, err 164 } 165 } 166 167 ready := make(chan struct{}, 1) 168 c := &Conn{ 169 Ready: ready, 170 } 171 f, err := mount(dir, &conf, ready, &c.MountError) 172 if err != nil { 173 return nil, err 174 } 175 c.dev = f 176 177 if err := initMount(c, &conf); err != nil { 178 c.Close() 179 if err == ErrClosedWithoutInit { 180 // see if we can provide a better error 181 <-c.Ready 182 if err := c.MountError; err != nil { 183 return nil, err 184 } 185 } 186 return nil, err 187 } 188 189 return c, nil 190} 191 192type OldVersionError struct { 193 Kernel Protocol 194 LibraryMin Protocol 195} 196 197func (e *OldVersionError) Error() string { 198 return fmt.Sprintf("kernel FUSE version is too old: %v < %v", e.Kernel, e.LibraryMin) 199} 200 201var ( 202 ErrClosedWithoutInit = errors.New("fuse connection closed without init") 203) 204 205func initMount(c *Conn, conf *mountConfig) error { 206 req, err := c.ReadRequest() 207 if err != nil { 208 if err == io.EOF { 209 return ErrClosedWithoutInit 210 } 211 return err 212 } 213 r, ok := req.(*InitRequest) 214 if !ok { 215 return fmt.Errorf("missing init, got: %T", req) 216 } 217 218 min := Protocol{protoVersionMinMajor, protoVersionMinMinor} 219 if r.Kernel.LT(min) { 220 req.RespondError(Errno(syscall.EPROTO)) 221 c.Close() 222 return &OldVersionError{ 223 Kernel: r.Kernel, 224 LibraryMin: min, 225 } 226 } 227 228 proto := Protocol{protoVersionMaxMajor, protoVersionMaxMinor} 229 if r.Kernel.LT(proto) { 230 // Kernel doesn't support the latest version we have. 231 proto = r.Kernel 232 } 233 c.proto = proto 234 235 s := &InitResponse{ 236 Library: proto, 237 MaxReadahead: conf.maxReadahead, 238 MaxWrite: maxWrite, 239 Flags: InitBigWrites | conf.initFlags, 240 } 241 r.Respond(s) 242 return nil 243} 244 245// A Request represents a single FUSE request received from the kernel. 246// Use a type switch to determine the specific kind. 247// A request of unrecognized type will have concrete type *Header. 248type Request interface { 249 // Hdr returns the Header associated with this request. 250 Hdr() *Header 251 252 // RespondError responds to the request with the given error. 253 RespondError(error) 254 255 String() string 256} 257 258// A RequestID identifies an active FUSE request. 259type RequestID uint64 260 261func (r RequestID) String() string { 262 return fmt.Sprintf("%#x", uint64(r)) 263} 264 265// A NodeID is a number identifying a directory or file. 266// It must be unique among IDs returned in LookupResponses 267// that have not yet been forgotten by ForgetRequests. 268type NodeID uint64 269 270func (n NodeID) String() string { 271 return fmt.Sprintf("%#x", uint64(n)) 272} 273 274// A HandleID is a number identifying an open directory or file. 275// It only needs to be unique while the directory or file is open. 276type HandleID uint64 277 278func (h HandleID) String() string { 279 return fmt.Sprintf("%#x", uint64(h)) 280} 281 282// The RootID identifies the root directory of a FUSE file system. 283const RootID NodeID = rootID 284 285const PollHackName = ".bazil-fuse-epoll-hack" 286const PollHackInode = NodeID(^uint64(0)) 287 288// A Header describes the basic information sent in every request. 289type Header struct { 290 Conn *Conn `json:"-"` // connection this request was received on 291 ID RequestID // unique ID for request 292 Node NodeID // file or directory the request is about 293 Uid uint32 // user ID of process making request 294 Gid uint32 // group ID of process making request 295 Pid uint32 // process ID of process making request 296 297 // for returning to reqPool 298 msg *message 299} 300 301func (h *Header) String() string { 302 return fmt.Sprintf("ID=%v Node=%v Uid=%d Gid=%d Pid=%d", h.ID, h.Node, h.Uid, h.Gid, h.Pid) 303} 304 305func (h *Header) Hdr() *Header { 306 return h 307} 308 309func (h *Header) noResponse() { 310 putMessage(h.msg) 311} 312 313func (h *Header) respond(msg []byte) { 314 out := (*outHeader)(unsafe.Pointer(&msg[0])) 315 out.Unique = uint64(h.ID) 316 h.Conn.respond(msg) 317 putMessage(h.msg) 318} 319 320// An ErrorNumber is an error with a specific error number. 321// 322// Operations may return an error value that implements ErrorNumber to 323// control what specific error number (errno) to return. 324type ErrorNumber interface { 325 // Errno returns the the error number (errno) for this error. 326 Errno() Errno 327} 328 329const ( 330 // ENOSYS indicates that the call is not supported. 331 ENOSYS = Errno(syscall.ENOSYS) 332 333 // ESTALE is used by Serve to respond to violations of the FUSE protocol. 334 ESTALE = Errno(syscall.ESTALE) 335 336 ENOENT = Errno(syscall.ENOENT) 337 EIO = Errno(syscall.EIO) 338 EPERM = Errno(syscall.EPERM) 339 340 // EINTR indicates request was interrupted by an InterruptRequest. 341 // See also fs.Intr. 342 EINTR = Errno(syscall.EINTR) 343 344 ERANGE = Errno(syscall.ERANGE) 345 ENOTSUP = Errno(syscall.ENOTSUP) 346 EEXIST = Errno(syscall.EEXIST) 347) 348 349// DefaultErrno is the errno used when error returned does not 350// implement ErrorNumber. 351const DefaultErrno = EIO 352 353var errnoNames = map[Errno]string{ 354 ENOSYS: "ENOSYS", 355 ESTALE: "ESTALE", 356 ENOENT: "ENOENT", 357 EIO: "EIO", 358 EPERM: "EPERM", 359 EINTR: "EINTR", 360 EEXIST: "EEXIST", 361} 362 363// Errno implements Error and ErrorNumber using a syscall.Errno. 364type Errno syscall.Errno 365 366var _ = ErrorNumber(Errno(0)) 367var _ = error(Errno(0)) 368 369func (e Errno) Errno() Errno { 370 return e 371} 372 373func (e Errno) String() string { 374 return syscall.Errno(e).Error() 375} 376 377func (e Errno) Error() string { 378 return syscall.Errno(e).Error() 379} 380 381// ErrnoName returns the short non-numeric identifier for this errno. 382// For example, "EIO". 383func (e Errno) ErrnoName() string { 384 s := errnoNames[e] 385 if s == "" { 386 s = fmt.Sprint(e.Errno()) 387 } 388 return s 389} 390 391func (e Errno) MarshalText() ([]byte, error) { 392 s := e.ErrnoName() 393 return []byte(s), nil 394} 395 396func (h *Header) RespondError(err error) { 397 errno := DefaultErrno 398 if ferr, ok := err.(ErrorNumber); ok { 399 errno = ferr.Errno() 400 } 401 // FUSE uses negative errors! 402 // TODO: File bug report against OSXFUSE: positive error causes kernel panic. 403 buf := newBuffer(0) 404 hOut := (*outHeader)(unsafe.Pointer(&buf[0])) 405 hOut.Error = -int32(errno) 406 h.respond(buf) 407} 408 409// All requests read from the kernel, without data, are shorter than 410// this. 411var maxRequestSize = syscall.Getpagesize() 412var bufSize = maxRequestSize + maxWrite 413 414// reqPool is a pool of messages. 415// 416// Lifetime of a logical message is from getMessage to putMessage. 417// getMessage is called by ReadRequest. putMessage is called by 418// Conn.ReadRequest, Request.Respond, or Request.RespondError. 419// 420// Messages in the pool are guaranteed to have conn and off zeroed, 421// buf allocated and len==bufSize, and hdr set. 422var reqPool = sync.Pool{ 423 New: allocMessage, 424} 425 426func allocMessage() interface{} { 427 m := &message{buf: make([]byte, bufSize)} 428 m.hdr = (*inHeader)(unsafe.Pointer(&m.buf[0])) 429 return m 430} 431 432func getMessage(c *Conn) *message { 433 m := reqPool.Get().(*message) 434 m.conn = c 435 return m 436} 437 438func putMessage(m *message) { 439 m.buf = m.buf[:bufSize] 440 m.conn = nil 441 m.off = 0 442 reqPool.Put(m) 443} 444 445// a message represents the bytes of a single FUSE message 446type message struct { 447 conn *Conn 448 buf []byte // all bytes 449 hdr *inHeader // header 450 off int // offset for reading additional fields 451} 452 453func (m *message) len() uintptr { 454 return uintptr(len(m.buf) - m.off) 455} 456 457func (m *message) data() unsafe.Pointer { 458 var p unsafe.Pointer 459 if m.off < len(m.buf) { 460 p = unsafe.Pointer(&m.buf[m.off]) 461 } 462 return p 463} 464 465func (m *message) bytes() []byte { 466 return m.buf[m.off:] 467} 468 469func (m *message) Header() Header { 470 h := m.hdr 471 return Header{ 472 Conn: m.conn, 473 ID: RequestID(h.Unique), 474 Node: NodeID(h.Nodeid), 475 Uid: h.Uid, 476 Gid: h.Gid, 477 Pid: h.Pid, 478 479 msg: m, 480 } 481} 482 483// fileMode returns a Go os.FileMode from a Unix mode. 484func fileMode(unixMode uint32) os.FileMode { 485 mode := os.FileMode(unixMode & 0777) 486 switch unixMode & syscall.S_IFMT { 487 case syscall.S_IFREG: 488 // nothing 489 case syscall.S_IFDIR: 490 mode |= os.ModeDir 491 case syscall.S_IFCHR: 492 mode |= os.ModeCharDevice | os.ModeDevice 493 case syscall.S_IFBLK: 494 mode |= os.ModeDevice 495 case syscall.S_IFIFO: 496 mode |= os.ModeNamedPipe 497 case syscall.S_IFLNK: 498 mode |= os.ModeSymlink 499 case syscall.S_IFSOCK: 500 mode |= os.ModeSocket 501 default: 502 // no idea 503 mode |= os.ModeDevice 504 } 505 if unixMode&syscall.S_ISUID != 0 { 506 mode |= os.ModeSetuid 507 } 508 if unixMode&syscall.S_ISGID != 0 { 509 mode |= os.ModeSetgid 510 } 511 return mode 512} 513 514type noOpcode struct { 515 Opcode uint32 516} 517 518func (m noOpcode) String() string { 519 return fmt.Sprintf("No opcode %v", m.Opcode) 520} 521 522type malformedMessage struct { 523} 524 525func (malformedMessage) String() string { 526 return "malformed message" 527} 528 529// Close closes the FUSE connection. 530func (c *Conn) Close() error { 531 c.wio.Lock() 532 defer c.wio.Unlock() 533 c.rio.Lock() 534 defer c.rio.Unlock() 535 return c.dev.Close() 536} 537 538// caller must hold wio or rio 539func (c *Conn) fd() int { 540 return int(c.dev.Fd()) 541} 542 543func (c *Conn) Protocol() Protocol { 544 return c.proto 545} 546 547// ReadRequest returns the next FUSE request from the kernel. 548// 549// Caller must call either Request.Respond or Request.RespondError in 550// a reasonable time. Caller must not retain Request after that call. 551func (c *Conn) ReadRequest() (Request, error) { 552 m := getMessage(c) 553loop: 554 c.rio.RLock() 555 n, err := syscall.Read(c.fd(), m.buf) 556 c.rio.RUnlock() 557 if err == syscall.EINTR { 558 // OSXFUSE sends EINTR to userspace when a request interrupt 559 // completed before it got sent to userspace? 560 goto loop 561 } 562 if err != nil && err != syscall.ENODEV { 563 putMessage(m) 564 return nil, err 565 } 566 if n <= 0 { 567 putMessage(m) 568 return nil, io.EOF 569 } 570 m.buf = m.buf[:n] 571 572 if n < inHeaderSize { 573 putMessage(m) 574 return nil, errors.New("fuse: message too short") 575 } 576 577 // FreeBSD FUSE sends a short length in the header 578 // for FUSE_INIT even though the actual read length is correct. 579 if n == inHeaderSize+initInSize && m.hdr.Opcode == opInit && m.hdr.Len < uint32(n) { 580 m.hdr.Len = uint32(n) 581 } 582 583 // OSXFUSE sometimes sends the wrong m.hdr.Len in a FUSE_WRITE message. 584 if m.hdr.Len < uint32(n) && m.hdr.Len >= uint32(unsafe.Sizeof(writeIn{})) && m.hdr.Opcode == opWrite { 585 m.hdr.Len = uint32(n) 586 } 587 588 if m.hdr.Len != uint32(n) { 589 // prepare error message before returning m to pool 590 err := fmt.Errorf("fuse: read %d opcode %d but expected %d", n, m.hdr.Opcode, m.hdr.Len) 591 putMessage(m) 592 return nil, err 593 } 594 595 m.off = inHeaderSize 596 597 // Convert to data structures. 598 // Do not trust kernel to hand us well-formed data. 599 var req Request 600 switch m.hdr.Opcode { 601 default: 602 Debug(noOpcode{Opcode: m.hdr.Opcode}) 603 goto unrecognized 604 605 case opLookup: 606 buf := m.bytes() 607 n := len(buf) 608 if n == 0 || buf[n-1] != '\x00' { 609 goto corrupt 610 } 611 req = &LookupRequest{ 612 Header: m.Header(), 613 Name: string(buf[:n-1]), 614 } 615 616 case opForget: 617 in := (*forgetIn)(m.data()) 618 if m.len() < unsafe.Sizeof(*in) { 619 goto corrupt 620 } 621 req = &ForgetRequest{ 622 Header: m.Header(), 623 N: in.Nlookup, 624 } 625 626 case opGetattr: 627 switch { 628 case c.proto.LT(Protocol{7, 9}): 629 req = &GetattrRequest{ 630 Header: m.Header(), 631 } 632 633 default: 634 in := (*getattrIn)(m.data()) 635 if m.len() < unsafe.Sizeof(*in) { 636 goto corrupt 637 } 638 req = &GetattrRequest{ 639 Header: m.Header(), 640 Flags: GetattrFlags(in.GetattrFlags), 641 Handle: HandleID(in.Fh), 642 } 643 } 644 645 case opSetattr: 646 in := (*setattrIn)(m.data()) 647 if m.len() < unsafe.Sizeof(*in) { 648 goto corrupt 649 } 650 req = &SetattrRequest{ 651 Header: m.Header(), 652 Valid: SetattrValid(in.Valid), 653 Handle: HandleID(in.Fh), 654 Size: in.Size, 655 Atime: time.Unix(int64(in.Atime), int64(in.AtimeNsec)), 656 Mtime: time.Unix(int64(in.Mtime), int64(in.MtimeNsec)), 657 Mode: fileMode(in.Mode), 658 Uid: in.Uid, 659 Gid: in.Gid, 660 Bkuptime: in.BkupTime(), 661 Chgtime: in.Chgtime(), 662 Flags: in.Flags(), 663 } 664 665 case opReadlink: 666 if len(m.bytes()) > 0 { 667 goto corrupt 668 } 669 req = &ReadlinkRequest{ 670 Header: m.Header(), 671 } 672 673 case opSymlink: 674 // m.bytes() is "newName\0target\0" 675 names := m.bytes() 676 if len(names) == 0 || names[len(names)-1] != 0 { 677 goto corrupt 678 } 679 i := bytes.IndexByte(names, '\x00') 680 if i < 0 { 681 goto corrupt 682 } 683 newName, target := names[0:i], names[i+1:len(names)-1] 684 req = &SymlinkRequest{ 685 Header: m.Header(), 686 NewName: string(newName), 687 Target: string(target), 688 } 689 690 case opLink: 691 in := (*linkIn)(m.data()) 692 if m.len() < unsafe.Sizeof(*in) { 693 goto corrupt 694 } 695 newName := m.bytes()[unsafe.Sizeof(*in):] 696 if len(newName) < 2 || newName[len(newName)-1] != 0 { 697 goto corrupt 698 } 699 newName = newName[:len(newName)-1] 700 req = &LinkRequest{ 701 Header: m.Header(), 702 OldNode: NodeID(in.Oldnodeid), 703 NewName: string(newName), 704 } 705 706 case opMknod: 707 size := mknodInSize(c.proto) 708 if m.len() < size { 709 goto corrupt 710 } 711 in := (*mknodIn)(m.data()) 712 name := m.bytes()[size:] 713 if len(name) < 2 || name[len(name)-1] != '\x00' { 714 goto corrupt 715 } 716 name = name[:len(name)-1] 717 r := &MknodRequest{ 718 Header: m.Header(), 719 Mode: fileMode(in.Mode), 720 Rdev: in.Rdev, 721 Name: string(name), 722 } 723 if c.proto.GE(Protocol{7, 12}) { 724 r.Umask = fileMode(in.Umask) & os.ModePerm 725 } 726 req = r 727 728 case opMkdir: 729 size := mkdirInSize(c.proto) 730 if m.len() < size { 731 goto corrupt 732 } 733 in := (*mkdirIn)(m.data()) 734 name := m.bytes()[size:] 735 i := bytes.IndexByte(name, '\x00') 736 if i < 0 { 737 goto corrupt 738 } 739 r := &MkdirRequest{ 740 Header: m.Header(), 741 Name: string(name[:i]), 742 // observed on Linux: mkdirIn.Mode & syscall.S_IFMT == 0, 743 // and this causes fileMode to go into it's "no idea" 744 // code branch; enforce type to directory 745 Mode: fileMode((in.Mode &^ syscall.S_IFMT) | syscall.S_IFDIR), 746 } 747 if c.proto.GE(Protocol{7, 12}) { 748 r.Umask = fileMode(in.Umask) & os.ModePerm 749 } 750 req = r 751 752 case opUnlink, opRmdir: 753 buf := m.bytes() 754 n := len(buf) 755 if n == 0 || buf[n-1] != '\x00' { 756 goto corrupt 757 } 758 req = &RemoveRequest{ 759 Header: m.Header(), 760 Name: string(buf[:n-1]), 761 Dir: m.hdr.Opcode == opRmdir, 762 } 763 764 case opRename: 765 in := (*renameIn)(m.data()) 766 if m.len() < unsafe.Sizeof(*in) { 767 goto corrupt 768 } 769 newDirNodeID := NodeID(in.Newdir) 770 oldNew := m.bytes()[unsafe.Sizeof(*in):] 771 // oldNew should be "old\x00new\x00" 772 if len(oldNew) < 4 { 773 goto corrupt 774 } 775 if oldNew[len(oldNew)-1] != '\x00' { 776 goto corrupt 777 } 778 i := bytes.IndexByte(oldNew, '\x00') 779 if i < 0 { 780 goto corrupt 781 } 782 oldName, newName := string(oldNew[:i]), string(oldNew[i+1:len(oldNew)-1]) 783 req = &RenameRequest{ 784 Header: m.Header(), 785 NewDir: newDirNodeID, 786 OldName: oldName, 787 NewName: newName, 788 } 789 790 case opOpendir, opOpen: 791 in := (*openIn)(m.data()) 792 if m.len() < unsafe.Sizeof(*in) { 793 goto corrupt 794 } 795 req = &OpenRequest{ 796 Header: m.Header(), 797 Dir: m.hdr.Opcode == opOpendir, 798 Flags: openFlags(in.Flags), 799 } 800 801 case opRead, opReaddir: 802 in := (*readIn)(m.data()) 803 if m.len() < readInSize(c.proto) { 804 goto corrupt 805 } 806 r := &ReadRequest{ 807 Header: m.Header(), 808 Dir: m.hdr.Opcode == opReaddir, 809 Handle: HandleID(in.Fh), 810 Offset: int64(in.Offset), 811 Size: int(in.Size), 812 } 813 if c.proto.GE(Protocol{7, 9}) { 814 r.Flags = ReadFlags(in.ReadFlags) 815 r.LockOwner = in.LockOwner 816 r.FileFlags = openFlags(in.Flags) 817 } 818 req = r 819 820 case opWrite: 821 in := (*writeIn)(m.data()) 822 if m.len() < writeInSize(c.proto) { 823 goto corrupt 824 } 825 r := &WriteRequest{ 826 Header: m.Header(), 827 Handle: HandleID(in.Fh), 828 Offset: int64(in.Offset), 829 Flags: WriteFlags(in.WriteFlags), 830 } 831 if c.proto.GE(Protocol{7, 9}) { 832 r.LockOwner = in.LockOwner 833 r.FileFlags = openFlags(in.Flags) 834 } 835 buf := m.bytes()[writeInSize(c.proto):] 836 if uint32(len(buf)) < in.Size { 837 goto corrupt 838 } 839 r.Data = buf 840 req = r 841 842 case opStatfs: 843 req = &StatfsRequest{ 844 Header: m.Header(), 845 } 846 847 case opRelease, opReleasedir: 848 in := (*releaseIn)(m.data()) 849 if m.len() < unsafe.Sizeof(*in) { 850 goto corrupt 851 } 852 req = &ReleaseRequest{ 853 Header: m.Header(), 854 Dir: m.hdr.Opcode == opReleasedir, 855 Handle: HandleID(in.Fh), 856 Flags: openFlags(in.Flags), 857 ReleaseFlags: ReleaseFlags(in.ReleaseFlags), 858 LockOwner: in.LockOwner, 859 } 860 861 case opFsync, opFsyncdir: 862 in := (*fsyncIn)(m.data()) 863 if m.len() < unsafe.Sizeof(*in) { 864 goto corrupt 865 } 866 req = &FsyncRequest{ 867 Dir: m.hdr.Opcode == opFsyncdir, 868 Header: m.Header(), 869 Handle: HandleID(in.Fh), 870 Flags: in.FsyncFlags, 871 } 872 873 case opSetxattr: 874 in := (*setxattrIn)(m.data()) 875 if m.len() < unsafe.Sizeof(*in) { 876 goto corrupt 877 } 878 m.off += int(unsafe.Sizeof(*in)) 879 name := m.bytes() 880 i := bytes.IndexByte(name, '\x00') 881 if i < 0 { 882 goto corrupt 883 } 884 xattr := name[i+1:] 885 if uint32(len(xattr)) < in.Size { 886 goto corrupt 887 } 888 xattr = xattr[:in.Size] 889 req = &SetxattrRequest{ 890 Header: m.Header(), 891 Flags: in.Flags, 892 Position: in.position(), 893 Name: string(name[:i]), 894 Xattr: xattr, 895 } 896 897 case opGetxattr: 898 in := (*getxattrIn)(m.data()) 899 if m.len() < unsafe.Sizeof(*in) { 900 goto corrupt 901 } 902 name := m.bytes()[unsafe.Sizeof(*in):] 903 i := bytes.IndexByte(name, '\x00') 904 if i < 0 { 905 goto corrupt 906 } 907 req = &GetxattrRequest{ 908 Header: m.Header(), 909 Name: string(name[:i]), 910 Size: in.Size, 911 Position: in.position(), 912 } 913 914 case opListxattr: 915 in := (*getxattrIn)(m.data()) 916 if m.len() < unsafe.Sizeof(*in) { 917 goto corrupt 918 } 919 req = &ListxattrRequest{ 920 Header: m.Header(), 921 Size: in.Size, 922 Position: in.position(), 923 } 924 925 case opRemovexattr: 926 buf := m.bytes() 927 n := len(buf) 928 if n == 0 || buf[n-1] != '\x00' { 929 goto corrupt 930 } 931 req = &RemovexattrRequest{ 932 Header: m.Header(), 933 Name: string(buf[:n-1]), 934 } 935 936 case opFlush: 937 in := (*flushIn)(m.data()) 938 if m.len() < unsafe.Sizeof(*in) { 939 goto corrupt 940 } 941 req = &FlushRequest{ 942 Header: m.Header(), 943 Handle: HandleID(in.Fh), 944 Flags: in.FlushFlags, 945 LockOwner: in.LockOwner, 946 } 947 948 case opInit: 949 in := (*initIn)(m.data()) 950 if m.len() < unsafe.Sizeof(*in) { 951 goto corrupt 952 } 953 req = &InitRequest{ 954 Header: m.Header(), 955 Kernel: Protocol{in.Major, in.Minor}, 956 MaxReadahead: in.MaxReadahead, 957 Flags: InitFlags(in.Flags), 958 } 959 960 case opGetlk: 961 panic("opGetlk") 962 case opSetlk: 963 panic("opSetlk") 964 case opSetlkw: 965 panic("opSetlkw") 966 967 case opAccess: 968 in := (*accessIn)(m.data()) 969 if m.len() < unsafe.Sizeof(*in) { 970 goto corrupt 971 } 972 req = &AccessRequest{ 973 Header: m.Header(), 974 Mask: in.Mask, 975 } 976 977 case opCreate: 978 size := createInSize(c.proto) 979 if m.len() < size { 980 goto corrupt 981 } 982 in := (*createIn)(m.data()) 983 name := m.bytes()[size:] 984 i := bytes.IndexByte(name, '\x00') 985 if i < 0 { 986 goto corrupt 987 } 988 r := &CreateRequest{ 989 Header: m.Header(), 990 Flags: openFlags(in.Flags), 991 Mode: fileMode(in.Mode), 992 Name: string(name[:i]), 993 } 994 if c.proto.GE(Protocol{7, 12}) { 995 r.Umask = fileMode(in.Umask) & os.ModePerm 996 } 997 req = r 998 999 case opInterrupt: 1000 in := (*interruptIn)(m.data()) 1001 if m.len() < unsafe.Sizeof(*in) { 1002 goto corrupt 1003 } 1004 req = &InterruptRequest{ 1005 Header: m.Header(), 1006 IntrID: RequestID(in.Unique), 1007 } 1008 1009 case opBmap: 1010 panic("opBmap") 1011 1012 case opDestroy: 1013 req = &DestroyRequest{ 1014 Header: m.Header(), 1015 } 1016 1017 // OS X 1018 case opSetvolname: 1019 panic("opSetvolname") 1020 case opGetxtimes: 1021 panic("opGetxtimes") 1022 case opExchange: 1023 in := (*exchangeIn)(m.data()) 1024 if m.len() < unsafe.Sizeof(*in) { 1025 goto corrupt 1026 } 1027 oldDirNodeID := NodeID(in.Olddir) 1028 newDirNodeID := NodeID(in.Newdir) 1029 oldNew := m.bytes()[unsafe.Sizeof(*in):] 1030 // oldNew should be "oldname\x00newname\x00" 1031 if len(oldNew) < 4 { 1032 goto corrupt 1033 } 1034 if oldNew[len(oldNew)-1] != '\x00' { 1035 goto corrupt 1036 } 1037 i := bytes.IndexByte(oldNew, '\x00') 1038 if i < 0 { 1039 goto corrupt 1040 } 1041 oldName, newName := string(oldNew[:i]), string(oldNew[i+1:len(oldNew)-1]) 1042 req = &ExchangeDataRequest{ 1043 Header: m.Header(), 1044 OldDir: oldDirNodeID, 1045 NewDir: newDirNodeID, 1046 OldName: oldName, 1047 NewName: newName, 1048 // TODO options 1049 } 1050 } 1051 1052 return req, nil 1053 1054corrupt: 1055 Debug(malformedMessage{}) 1056 putMessage(m) 1057 return nil, fmt.Errorf("fuse: malformed message") 1058 1059unrecognized: 1060 // Unrecognized message. 1061 // Assume higher-level code will send a "no idea what you mean" error. 1062 h := m.Header() 1063 return &h, nil 1064} 1065 1066type bugShortKernelWrite struct { 1067 Written int64 1068 Length int64 1069 Error string 1070 Stack string 1071} 1072 1073func (b bugShortKernelWrite) String() string { 1074 return fmt.Sprintf("short kernel write: written=%d/%d error=%q stack=\n%s", b.Written, b.Length, b.Error, b.Stack) 1075} 1076 1077type bugKernelWriteError struct { 1078 Error string 1079 Stack string 1080} 1081 1082func (b bugKernelWriteError) String() string { 1083 return fmt.Sprintf("kernel write error: error=%q stack=\n%s", b.Error, b.Stack) 1084} 1085 1086// safe to call even with nil error 1087func errorString(err error) string { 1088 if err == nil { 1089 return "" 1090 } 1091 return err.Error() 1092} 1093 1094func (c *Conn) writeToKernel(msg []byte) error { 1095 out := (*outHeader)(unsafe.Pointer(&msg[0])) 1096 out.Len = uint32(len(msg)) 1097 1098 c.wio.RLock() 1099 defer c.wio.RUnlock() 1100 nn, err := syscall.Write(c.fd(), msg) 1101 if err == nil && nn != len(msg) { 1102 Debug(bugShortKernelWrite{ 1103 Written: int64(nn), 1104 Length: int64(len(msg)), 1105 Error: errorString(err), 1106 Stack: stack(), 1107 }) 1108 } 1109 return err 1110} 1111 1112func (c *Conn) respond(msg []byte) { 1113 if err := c.writeToKernel(msg); err != nil { 1114 Debug(bugKernelWriteError{ 1115 Error: errorString(err), 1116 Stack: stack(), 1117 }) 1118 } 1119} 1120 1121type notCachedError struct{} 1122 1123func (notCachedError) Error() string { 1124 return "node not cached" 1125} 1126 1127var _ ErrorNumber = notCachedError{} 1128 1129func (notCachedError) Errno() Errno { 1130 // Behave just like if the original syscall.ENOENT had been passed 1131 // straight through. 1132 return ENOENT 1133} 1134 1135var ( 1136 ErrNotCached = notCachedError{} 1137) 1138 1139// sendInvalidate sends an invalidate notification to kernel. 1140// 1141// A returned ENOENT is translated to a friendlier error. 1142func (c *Conn) sendInvalidate(msg []byte) error { 1143 switch err := c.writeToKernel(msg); err { 1144 case syscall.ENOENT: 1145 return ErrNotCached 1146 default: 1147 return err 1148 } 1149} 1150 1151// InvalidateNode invalidates the kernel cache of the attributes and a 1152// range of the data of a node. 1153// 1154// Giving offset 0 and size -1 means all data. To invalidate just the 1155// attributes, give offset 0 and size 0. 1156// 1157// Returns ErrNotCached if the kernel is not currently caching the 1158// node. 1159func (c *Conn) InvalidateNode(nodeID NodeID, off int64, size int64) error { 1160 buf := newBuffer(unsafe.Sizeof(notifyInvalInodeOut{})) 1161 h := (*outHeader)(unsafe.Pointer(&buf[0])) 1162 // h.Unique is 0 1163 h.Error = notifyCodeInvalInode 1164 out := (*notifyInvalInodeOut)(buf.alloc(unsafe.Sizeof(notifyInvalInodeOut{}))) 1165 out.Ino = uint64(nodeID) 1166 out.Off = off 1167 out.Len = size 1168 return c.sendInvalidate(buf) 1169} 1170 1171// InvalidateEntry invalidates the kernel cache of the directory entry 1172// identified by parent directory node ID and entry basename. 1173// 1174// Kernel may or may not cache directory listings. To invalidate 1175// those, use InvalidateNode to invalidate all of the data for a 1176// directory. (As of 2015-06, Linux FUSE does not cache directory 1177// listings.) 1178// 1179// Returns ErrNotCached if the kernel is not currently caching the 1180// node. 1181func (c *Conn) InvalidateEntry(parent NodeID, name string) error { 1182 const maxUint32 = ^uint32(0) 1183 if uint64(len(name)) > uint64(maxUint32) { 1184 // very unlikely, but we don't want to silently truncate 1185 return syscall.ENAMETOOLONG 1186 } 1187 buf := newBuffer(unsafe.Sizeof(notifyInvalEntryOut{}) + uintptr(len(name)) + 1) 1188 h := (*outHeader)(unsafe.Pointer(&buf[0])) 1189 // h.Unique is 0 1190 h.Error = notifyCodeInvalEntry 1191 out := (*notifyInvalEntryOut)(buf.alloc(unsafe.Sizeof(notifyInvalEntryOut{}))) 1192 out.Parent = uint64(parent) 1193 out.Namelen = uint32(len(name)) 1194 buf = append(buf, name...) 1195 buf = append(buf, '\x00') 1196 return c.sendInvalidate(buf) 1197} 1198 1199// An InitRequest is the first request sent on a FUSE file system. 1200type InitRequest struct { 1201 Header `json:"-"` 1202 Kernel Protocol 1203 // Maximum readahead in bytes that the kernel plans to use. 1204 MaxReadahead uint32 1205 Flags InitFlags 1206} 1207 1208var _ = Request(&InitRequest{}) 1209 1210func (r *InitRequest) String() string { 1211 return fmt.Sprintf("Init [%v] %v ra=%d fl=%v", &r.Header, r.Kernel, r.MaxReadahead, r.Flags) 1212} 1213 1214// An InitResponse is the response to an InitRequest. 1215type InitResponse struct { 1216 Library Protocol 1217 // Maximum readahead in bytes that the kernel can use. Ignored if 1218 // greater than InitRequest.MaxReadahead. 1219 MaxReadahead uint32 1220 Flags InitFlags 1221 // Maximum size of a single write operation. 1222 // Linux enforces a minimum of 4 KiB. 1223 MaxWrite uint32 1224} 1225 1226func (r *InitResponse) String() string { 1227 return fmt.Sprintf("Init %v ra=%d fl=%v w=%d", r.Library, r.MaxReadahead, r.Flags, r.MaxWrite) 1228} 1229 1230// Respond replies to the request with the given response. 1231func (r *InitRequest) Respond(resp *InitResponse) { 1232 buf := newBuffer(unsafe.Sizeof(initOut{})) 1233 out := (*initOut)(buf.alloc(unsafe.Sizeof(initOut{}))) 1234 out.Major = resp.Library.Major 1235 out.Minor = resp.Library.Minor 1236 out.MaxReadahead = resp.MaxReadahead 1237 out.Flags = uint32(resp.Flags) 1238 out.MaxWrite = resp.MaxWrite 1239 1240 // MaxWrite larger than our receive buffer would just lead to 1241 // errors on large writes. 1242 if out.MaxWrite > maxWrite { 1243 out.MaxWrite = maxWrite 1244 } 1245 r.respond(buf) 1246} 1247 1248// A StatfsRequest requests information about the mounted file system. 1249type StatfsRequest struct { 1250 Header `json:"-"` 1251} 1252 1253var _ = Request(&StatfsRequest{}) 1254 1255func (r *StatfsRequest) String() string { 1256 return fmt.Sprintf("Statfs [%s]", &r.Header) 1257} 1258 1259// Respond replies to the request with the given response. 1260func (r *StatfsRequest) Respond(resp *StatfsResponse) { 1261 buf := newBuffer(unsafe.Sizeof(statfsOut{})) 1262 out := (*statfsOut)(buf.alloc(unsafe.Sizeof(statfsOut{}))) 1263 out.St = kstatfs{ 1264 Blocks: resp.Blocks, 1265 Bfree: resp.Bfree, 1266 Bavail: resp.Bavail, 1267 Files: resp.Files, 1268 Bsize: resp.Bsize, 1269 Namelen: resp.Namelen, 1270 Frsize: resp.Frsize, 1271 } 1272 r.respond(buf) 1273} 1274 1275// A StatfsResponse is the response to a StatfsRequest. 1276type StatfsResponse struct { 1277 Blocks uint64 // Total data blocks in file system. 1278 Bfree uint64 // Free blocks in file system. 1279 Bavail uint64 // Free blocks in file system if you're not root. 1280 Files uint64 // Total files in file system. 1281 Ffree uint64 // Free files in file system. 1282 Bsize uint32 // Block size 1283 Namelen uint32 // Maximum file name length? 1284 Frsize uint32 // Fragment size, smallest addressable data size in the file system. 1285} 1286 1287func (r *StatfsResponse) String() string { 1288 return fmt.Sprintf("Statfs blocks=%d/%d/%d files=%d/%d bsize=%d frsize=%d namelen=%d", 1289 r.Bavail, r.Bfree, r.Blocks, 1290 r.Ffree, r.Files, 1291 r.Bsize, 1292 r.Frsize, 1293 r.Namelen, 1294 ) 1295} 1296 1297// An AccessRequest asks whether the file can be accessed 1298// for the purpose specified by the mask. 1299type AccessRequest struct { 1300 Header `json:"-"` 1301 Mask uint32 1302} 1303 1304var _ = Request(&AccessRequest{}) 1305 1306func (r *AccessRequest) String() string { 1307 return fmt.Sprintf("Access [%s] mask=%#x", &r.Header, r.Mask) 1308} 1309 1310// Respond replies to the request indicating that access is allowed. 1311// To deny access, use RespondError. 1312func (r *AccessRequest) Respond() { 1313 buf := newBuffer(0) 1314 r.respond(buf) 1315} 1316 1317// An Attr is the metadata for a single file or directory. 1318type Attr struct { 1319 Valid time.Duration // how long Attr can be cached 1320 1321 Inode uint64 // inode number 1322 Size uint64 // size in bytes 1323 Blocks uint64 // size in 512-byte units 1324 Atime time.Time // time of last access 1325 Mtime time.Time // time of last modification 1326 Ctime time.Time // time of last inode change 1327 Crtime time.Time // time of creation (OS X only) 1328 Mode os.FileMode // file mode 1329 Nlink uint32 // number of links (usually 1) 1330 Uid uint32 // owner uid 1331 Gid uint32 // group gid 1332 Rdev uint32 // device numbers 1333 Flags uint32 // chflags(2) flags (OS X only) 1334 BlockSize uint32 // preferred blocksize for filesystem I/O 1335} 1336 1337func (a Attr) String() string { 1338 return fmt.Sprintf("valid=%v ino=%v size=%d mode=%v", a.Valid, a.Inode, a.Size, a.Mode) 1339} 1340 1341func unix(t time.Time) (sec uint64, nsec uint32) { 1342 nano := t.UnixNano() 1343 sec = uint64(nano / 1e9) 1344 nsec = uint32(nano % 1e9) 1345 return 1346} 1347 1348func (a *Attr) attr(out *attr, proto Protocol) { 1349 out.Ino = a.Inode 1350 out.Size = a.Size 1351 out.Blocks = a.Blocks 1352 out.Atime, out.AtimeNsec = unix(a.Atime) 1353 out.Mtime, out.MtimeNsec = unix(a.Mtime) 1354 out.Ctime, out.CtimeNsec = unix(a.Ctime) 1355 out.SetCrtime(unix(a.Crtime)) 1356 out.Mode = uint32(a.Mode) & 0777 1357 switch { 1358 default: 1359 out.Mode |= syscall.S_IFREG 1360 case a.Mode&os.ModeDir != 0: 1361 out.Mode |= syscall.S_IFDIR 1362 case a.Mode&os.ModeDevice != 0: 1363 if a.Mode&os.ModeCharDevice != 0 { 1364 out.Mode |= syscall.S_IFCHR 1365 } else { 1366 out.Mode |= syscall.S_IFBLK 1367 } 1368 case a.Mode&os.ModeNamedPipe != 0: 1369 out.Mode |= syscall.S_IFIFO 1370 case a.Mode&os.ModeSymlink != 0: 1371 out.Mode |= syscall.S_IFLNK 1372 case a.Mode&os.ModeSocket != 0: 1373 out.Mode |= syscall.S_IFSOCK 1374 } 1375 if a.Mode&os.ModeSetuid != 0 { 1376 out.Mode |= syscall.S_ISUID 1377 } 1378 if a.Mode&os.ModeSetgid != 0 { 1379 out.Mode |= syscall.S_ISGID 1380 } 1381 out.Nlink = a.Nlink 1382 out.Uid = a.Uid 1383 out.Gid = a.Gid 1384 out.Rdev = a.Rdev 1385 out.SetFlags(a.Flags) 1386 if proto.GE(Protocol{7, 9}) { 1387 out.Blksize = a.BlockSize 1388 } 1389 1390 return 1391} 1392 1393// A GetattrRequest asks for the metadata for the file denoted by r.Node. 1394type GetattrRequest struct { 1395 Header `json:"-"` 1396 Flags GetattrFlags 1397 Handle HandleID 1398} 1399 1400var _ = Request(&GetattrRequest{}) 1401 1402func (r *GetattrRequest) String() string { 1403 return fmt.Sprintf("Getattr [%s] %v fl=%v", &r.Header, r.Handle, r.Flags) 1404} 1405 1406// Respond replies to the request with the given response. 1407func (r *GetattrRequest) Respond(resp *GetattrResponse) { 1408 size := attrOutSize(r.Header.Conn.proto) 1409 buf := newBuffer(size) 1410 out := (*attrOut)(buf.alloc(size)) 1411 out.AttrValid = uint64(resp.Attr.Valid / time.Second) 1412 out.AttrValidNsec = uint32(resp.Attr.Valid % time.Second / time.Nanosecond) 1413 resp.Attr.attr(&out.Attr, r.Header.Conn.proto) 1414 r.respond(buf) 1415} 1416 1417// A GetattrResponse is the response to a GetattrRequest. 1418type GetattrResponse struct { 1419 Attr Attr // file attributes 1420} 1421 1422func (r *GetattrResponse) String() string { 1423 return fmt.Sprintf("Getattr %v", r.Attr) 1424} 1425 1426// A GetxattrRequest asks for the extended attributes associated with r.Node. 1427type GetxattrRequest struct { 1428 Header `json:"-"` 1429 1430 // Maximum size to return. 1431 Size uint32 1432 1433 // Name of the attribute requested. 1434 Name string 1435 1436 // Offset within extended attributes. 1437 // 1438 // Only valid for OS X, and then only with the resource fork 1439 // attribute. 1440 Position uint32 1441} 1442 1443var _ = Request(&GetxattrRequest{}) 1444 1445func (r *GetxattrRequest) String() string { 1446 return fmt.Sprintf("Getxattr [%s] %q %d @%d", &r.Header, r.Name, r.Size, r.Position) 1447} 1448 1449// Respond replies to the request with the given response. 1450func (r *GetxattrRequest) Respond(resp *GetxattrResponse) { 1451 if r.Size == 0 { 1452 buf := newBuffer(unsafe.Sizeof(getxattrOut{})) 1453 out := (*getxattrOut)(buf.alloc(unsafe.Sizeof(getxattrOut{}))) 1454 out.Size = uint32(len(resp.Xattr)) 1455 r.respond(buf) 1456 } else { 1457 buf := newBuffer(uintptr(len(resp.Xattr))) 1458 buf = append(buf, resp.Xattr...) 1459 r.respond(buf) 1460 } 1461} 1462 1463// A GetxattrResponse is the response to a GetxattrRequest. 1464type GetxattrResponse struct { 1465 Xattr []byte 1466} 1467 1468func (r *GetxattrResponse) String() string { 1469 return fmt.Sprintf("Getxattr %x", r.Xattr) 1470} 1471 1472// A ListxattrRequest asks to list the extended attributes associated with r.Node. 1473type ListxattrRequest struct { 1474 Header `json:"-"` 1475 Size uint32 // maximum size to return 1476 Position uint32 // offset within attribute list 1477} 1478 1479var _ = Request(&ListxattrRequest{}) 1480 1481func (r *ListxattrRequest) String() string { 1482 return fmt.Sprintf("Listxattr [%s] %d @%d", &r.Header, r.Size, r.Position) 1483} 1484 1485// Respond replies to the request with the given response. 1486func (r *ListxattrRequest) Respond(resp *ListxattrResponse) { 1487 if r.Size == 0 { 1488 buf := newBuffer(unsafe.Sizeof(getxattrOut{})) 1489 out := (*getxattrOut)(buf.alloc(unsafe.Sizeof(getxattrOut{}))) 1490 out.Size = uint32(len(resp.Xattr)) 1491 r.respond(buf) 1492 } else { 1493 buf := newBuffer(uintptr(len(resp.Xattr))) 1494 buf = append(buf, resp.Xattr...) 1495 r.respond(buf) 1496 } 1497} 1498 1499// A ListxattrResponse is the response to a ListxattrRequest. 1500type ListxattrResponse struct { 1501 Xattr []byte 1502} 1503 1504func (r *ListxattrResponse) String() string { 1505 return fmt.Sprintf("Listxattr %x", r.Xattr) 1506} 1507 1508// Append adds an extended attribute name to the response. 1509func (r *ListxattrResponse) Append(names ...string) { 1510 for _, name := range names { 1511 r.Xattr = append(r.Xattr, name...) 1512 r.Xattr = append(r.Xattr, '\x00') 1513 } 1514} 1515 1516// A RemovexattrRequest asks to remove an extended attribute associated with r.Node. 1517type RemovexattrRequest struct { 1518 Header `json:"-"` 1519 Name string // name of extended attribute 1520} 1521 1522var _ = Request(&RemovexattrRequest{}) 1523 1524func (r *RemovexattrRequest) String() string { 1525 return fmt.Sprintf("Removexattr [%s] %q", &r.Header, r.Name) 1526} 1527 1528// Respond replies to the request, indicating that the attribute was removed. 1529func (r *RemovexattrRequest) Respond() { 1530 buf := newBuffer(0) 1531 r.respond(buf) 1532} 1533 1534// A SetxattrRequest asks to set an extended attribute associated with a file. 1535type SetxattrRequest struct { 1536 Header `json:"-"` 1537 1538 // Flags can make the request fail if attribute does/not already 1539 // exist. Unfortunately, the constants are platform-specific and 1540 // not exposed by Go1.2. Look for XATTR_CREATE, XATTR_REPLACE. 1541 // 1542 // TODO improve this later 1543 // 1544 // TODO XATTR_CREATE and exist -> EEXIST 1545 // 1546 // TODO XATTR_REPLACE and not exist -> ENODATA 1547 Flags uint32 1548 1549 // Offset within extended attributes. 1550 // 1551 // Only valid for OS X, and then only with the resource fork 1552 // attribute. 1553 Position uint32 1554 1555 Name string 1556 Xattr []byte 1557} 1558 1559var _ = Request(&SetxattrRequest{}) 1560 1561func trunc(b []byte, max int) ([]byte, string) { 1562 if len(b) > max { 1563 return b[:max], "..." 1564 } 1565 return b, "" 1566} 1567 1568func (r *SetxattrRequest) String() string { 1569 xattr, tail := trunc(r.Xattr, 16) 1570 return fmt.Sprintf("Setxattr [%s] %q %x%s fl=%v @%#x", &r.Header, r.Name, xattr, tail, r.Flags, r.Position) 1571} 1572 1573// Respond replies to the request, indicating that the extended attribute was set. 1574func (r *SetxattrRequest) Respond() { 1575 buf := newBuffer(0) 1576 r.respond(buf) 1577} 1578 1579// A LookupRequest asks to look up the given name in the directory named by r.Node. 1580type LookupRequest struct { 1581 Header `json:"-"` 1582 Name string 1583} 1584 1585var _ = Request(&LookupRequest{}) 1586 1587func (r *LookupRequest) String() string { 1588 return fmt.Sprintf("Lookup [%s] %q", &r.Header, r.Name) 1589} 1590 1591// Respond replies to the request with the given response. 1592func (r *LookupRequest) Respond(resp *LookupResponse) { 1593 size := entryOutSize(r.Header.Conn.proto) 1594 buf := newBuffer(size) 1595 out := (*entryOut)(buf.alloc(size)) 1596 out.Nodeid = uint64(resp.Node) 1597 out.Generation = resp.Generation 1598 out.EntryValid = uint64(resp.EntryValid / time.Second) 1599 out.EntryValidNsec = uint32(resp.EntryValid % time.Second / time.Nanosecond) 1600 out.AttrValid = uint64(resp.Attr.Valid / time.Second) 1601 out.AttrValidNsec = uint32(resp.Attr.Valid % time.Second / time.Nanosecond) 1602 resp.Attr.attr(&out.Attr, r.Header.Conn.proto) 1603 r.respond(buf) 1604} 1605 1606// A LookupResponse is the response to a LookupRequest. 1607type LookupResponse struct { 1608 Node NodeID 1609 Generation uint64 1610 EntryValid time.Duration 1611 Attr Attr 1612} 1613 1614func (r *LookupResponse) string() string { 1615 return fmt.Sprintf("%v gen=%d valid=%v attr={%v}", r.Node, r.Generation, r.EntryValid, r.Attr) 1616} 1617 1618func (r *LookupResponse) String() string { 1619 return fmt.Sprintf("Lookup %s", r.string()) 1620} 1621 1622// An OpenRequest asks to open a file or directory 1623type OpenRequest struct { 1624 Header `json:"-"` 1625 Dir bool // is this Opendir? 1626 Flags OpenFlags 1627} 1628 1629var _ = Request(&OpenRequest{}) 1630 1631func (r *OpenRequest) String() string { 1632 return fmt.Sprintf("Open [%s] dir=%v fl=%v", &r.Header, r.Dir, r.Flags) 1633} 1634 1635// Respond replies to the request with the given response. 1636func (r *OpenRequest) Respond(resp *OpenResponse) { 1637 buf := newBuffer(unsafe.Sizeof(openOut{})) 1638 out := (*openOut)(buf.alloc(unsafe.Sizeof(openOut{}))) 1639 out.Fh = uint64(resp.Handle) 1640 out.OpenFlags = uint32(resp.Flags) 1641 r.respond(buf) 1642} 1643 1644// A OpenResponse is the response to a OpenRequest. 1645type OpenResponse struct { 1646 Handle HandleID 1647 Flags OpenResponseFlags 1648} 1649 1650func (r *OpenResponse) string() string { 1651 return fmt.Sprintf("%v fl=%v", r.Handle, r.Flags) 1652} 1653 1654func (r *OpenResponse) String() string { 1655 return fmt.Sprintf("Open %s", r.string()) 1656} 1657 1658// A CreateRequest asks to create and open a file (not a directory). 1659type CreateRequest struct { 1660 Header `json:"-"` 1661 Name string 1662 Flags OpenFlags 1663 Mode os.FileMode 1664 // Umask of the request. Not supported on OS X. 1665 Umask os.FileMode 1666} 1667 1668var _ = Request(&CreateRequest{}) 1669 1670func (r *CreateRequest) String() string { 1671 return fmt.Sprintf("Create [%s] %q fl=%v mode=%v umask=%v", &r.Header, r.Name, r.Flags, r.Mode, r.Umask) 1672} 1673 1674// Respond replies to the request with the given response. 1675func (r *CreateRequest) Respond(resp *CreateResponse) { 1676 eSize := entryOutSize(r.Header.Conn.proto) 1677 buf := newBuffer(eSize + unsafe.Sizeof(openOut{})) 1678 1679 e := (*entryOut)(buf.alloc(eSize)) 1680 e.Nodeid = uint64(resp.Node) 1681 e.Generation = resp.Generation 1682 e.EntryValid = uint64(resp.EntryValid / time.Second) 1683 e.EntryValidNsec = uint32(resp.EntryValid % time.Second / time.Nanosecond) 1684 e.AttrValid = uint64(resp.Attr.Valid / time.Second) 1685 e.AttrValidNsec = uint32(resp.Attr.Valid % time.Second / time.Nanosecond) 1686 resp.Attr.attr(&e.Attr, r.Header.Conn.proto) 1687 1688 o := (*openOut)(buf.alloc(unsafe.Sizeof(openOut{}))) 1689 o.Fh = uint64(resp.Handle) 1690 o.OpenFlags = uint32(resp.Flags) 1691 1692 r.respond(buf) 1693} 1694 1695// A CreateResponse is the response to a CreateRequest. 1696// It describes the created node and opened handle. 1697type CreateResponse struct { 1698 LookupResponse 1699 OpenResponse 1700} 1701 1702func (r *CreateResponse) String() string { 1703 return fmt.Sprintf("Create {%s} {%s}", r.LookupResponse.string(), r.OpenResponse.string()) 1704} 1705 1706// A MkdirRequest asks to create (but not open) a directory. 1707type MkdirRequest struct { 1708 Header `json:"-"` 1709 Name string 1710 Mode os.FileMode 1711 // Umask of the request. Not supported on OS X. 1712 Umask os.FileMode 1713} 1714 1715var _ = Request(&MkdirRequest{}) 1716 1717func (r *MkdirRequest) String() string { 1718 return fmt.Sprintf("Mkdir [%s] %q mode=%v umask=%v", &r.Header, r.Name, r.Mode, r.Umask) 1719} 1720 1721// Respond replies to the request with the given response. 1722func (r *MkdirRequest) Respond(resp *MkdirResponse) { 1723 size := entryOutSize(r.Header.Conn.proto) 1724 buf := newBuffer(size) 1725 out := (*entryOut)(buf.alloc(size)) 1726 out.Nodeid = uint64(resp.Node) 1727 out.Generation = resp.Generation 1728 out.EntryValid = uint64(resp.EntryValid / time.Second) 1729 out.EntryValidNsec = uint32(resp.EntryValid % time.Second / time.Nanosecond) 1730 out.AttrValid = uint64(resp.Attr.Valid / time.Second) 1731 out.AttrValidNsec = uint32(resp.Attr.Valid % time.Second / time.Nanosecond) 1732 resp.Attr.attr(&out.Attr, r.Header.Conn.proto) 1733 r.respond(buf) 1734} 1735 1736// A MkdirResponse is the response to a MkdirRequest. 1737type MkdirResponse struct { 1738 LookupResponse 1739} 1740 1741func (r *MkdirResponse) String() string { 1742 return fmt.Sprintf("Mkdir %v", r.LookupResponse.string()) 1743} 1744 1745// A ReadRequest asks to read from an open file. 1746type ReadRequest struct { 1747 Header `json:"-"` 1748 Dir bool // is this Readdir? 1749 Handle HandleID 1750 Offset int64 1751 Size int 1752 Flags ReadFlags 1753 LockOwner uint64 1754 FileFlags OpenFlags 1755} 1756 1757var _ = Request(&ReadRequest{}) 1758 1759func (r *ReadRequest) String() string { 1760 return fmt.Sprintf("Read [%s] %v %d @%#x dir=%v fl=%v lock=%d ffl=%v", &r.Header, r.Handle, r.Size, r.Offset, r.Dir, r.Flags, r.LockOwner, r.FileFlags) 1761} 1762 1763// Respond replies to the request with the given response. 1764func (r *ReadRequest) Respond(resp *ReadResponse) { 1765 buf := newBuffer(uintptr(len(resp.Data))) 1766 buf = append(buf, resp.Data...) 1767 r.respond(buf) 1768} 1769 1770// A ReadResponse is the response to a ReadRequest. 1771type ReadResponse struct { 1772 Data []byte 1773} 1774 1775func (r *ReadResponse) String() string { 1776 return fmt.Sprintf("Read %d", len(r.Data)) 1777} 1778 1779type jsonReadResponse struct { 1780 Len uint64 1781} 1782 1783func (r *ReadResponse) MarshalJSON() ([]byte, error) { 1784 j := jsonReadResponse{ 1785 Len: uint64(len(r.Data)), 1786 } 1787 return json.Marshal(j) 1788} 1789 1790// A ReleaseRequest asks to release (close) an open file handle. 1791type ReleaseRequest struct { 1792 Header `json:"-"` 1793 Dir bool // is this Releasedir? 1794 Handle HandleID 1795 Flags OpenFlags // flags from OpenRequest 1796 ReleaseFlags ReleaseFlags 1797 LockOwner uint32 1798} 1799 1800var _ = Request(&ReleaseRequest{}) 1801 1802func (r *ReleaseRequest) String() string { 1803 return fmt.Sprintf("Release [%s] %v fl=%v rfl=%v owner=%#x", &r.Header, r.Handle, r.Flags, r.ReleaseFlags, r.LockOwner) 1804} 1805 1806// Respond replies to the request, indicating that the handle has been released. 1807func (r *ReleaseRequest) Respond() { 1808 buf := newBuffer(0) 1809 r.respond(buf) 1810} 1811 1812// A DestroyRequest is sent by the kernel when unmounting the file system. 1813// No more requests will be received after this one, but it should still be 1814// responded to. 1815type DestroyRequest struct { 1816 Header `json:"-"` 1817} 1818 1819var _ = Request(&DestroyRequest{}) 1820 1821func (r *DestroyRequest) String() string { 1822 return fmt.Sprintf("Destroy [%s]", &r.Header) 1823} 1824 1825// Respond replies to the request. 1826func (r *DestroyRequest) Respond() { 1827 buf := newBuffer(0) 1828 r.respond(buf) 1829} 1830 1831// A ForgetRequest is sent by the kernel when forgetting about r.Node 1832// as returned by r.N lookup requests. 1833type ForgetRequest struct { 1834 Header `json:"-"` 1835 N uint64 1836} 1837 1838var _ = Request(&ForgetRequest{}) 1839 1840func (r *ForgetRequest) String() string { 1841 return fmt.Sprintf("Forget [%s] %d", &r.Header, r.N) 1842} 1843 1844// Respond replies to the request, indicating that the forgetfulness has been recorded. 1845func (r *ForgetRequest) Respond() { 1846 // Don't reply to forget messages. 1847 r.noResponse() 1848} 1849 1850// A Dirent represents a single directory entry. 1851type Dirent struct { 1852 // Inode this entry names. 1853 Inode uint64 1854 1855 // Type of the entry, for example DT_File. 1856 // 1857 // Setting this is optional. The zero value (DT_Unknown) means 1858 // callers will just need to do a Getattr when the type is 1859 // needed. Providing a type can speed up operations 1860 // significantly. 1861 Type DirentType 1862 1863 // Name of the entry 1864 Name string 1865} 1866 1867// Type of an entry in a directory listing. 1868type DirentType uint32 1869 1870const ( 1871 // These don't quite match os.FileMode; especially there's an 1872 // explicit unknown, instead of zero value meaning file. They 1873 // are also not quite syscall.DT_*; nothing says the FUSE 1874 // protocol follows those, and even if they were, we don't 1875 // want each fs to fiddle with syscall. 1876 1877 // The shift by 12 is hardcoded in the FUSE userspace 1878 // low-level C library, so it's safe here. 1879 1880 DT_Unknown DirentType = 0 1881 DT_Socket DirentType = syscall.S_IFSOCK >> 12 1882 DT_Link DirentType = syscall.S_IFLNK >> 12 1883 DT_File DirentType = syscall.S_IFREG >> 12 1884 DT_Block DirentType = syscall.S_IFBLK >> 12 1885 DT_Dir DirentType = syscall.S_IFDIR >> 12 1886 DT_Char DirentType = syscall.S_IFCHR >> 12 1887 DT_FIFO DirentType = syscall.S_IFIFO >> 12 1888) 1889 1890func (t DirentType) String() string { 1891 switch t { 1892 case DT_Unknown: 1893 return "unknown" 1894 case DT_Socket: 1895 return "socket" 1896 case DT_Link: 1897 return "link" 1898 case DT_File: 1899 return "file" 1900 case DT_Block: 1901 return "block" 1902 case DT_Dir: 1903 return "dir" 1904 case DT_Char: 1905 return "char" 1906 case DT_FIFO: 1907 return "fifo" 1908 } 1909 return "invalid" 1910} 1911 1912// AppendDirent appends the encoded form of a directory entry to data 1913// and returns the resulting slice. 1914func AppendDirent(data []byte, dir Dirent) []byte { 1915 de := dirent{ 1916 Ino: dir.Inode, 1917 Namelen: uint32(len(dir.Name)), 1918 Type: uint32(dir.Type), 1919 } 1920 de.Off = uint64(len(data) + direntSize + (len(dir.Name)+7)&^7) 1921 data = append(data, (*[direntSize]byte)(unsafe.Pointer(&de))[:]...) 1922 data = append(data, dir.Name...) 1923 n := direntSize + uintptr(len(dir.Name)) 1924 if n%8 != 0 { 1925 var pad [8]byte 1926 data = append(data, pad[:8-n%8]...) 1927 } 1928 return data 1929} 1930 1931// A WriteRequest asks to write to an open file. 1932type WriteRequest struct { 1933 Header 1934 Handle HandleID 1935 Offset int64 1936 Data []byte 1937 Flags WriteFlags 1938 LockOwner uint64 1939 FileFlags OpenFlags 1940} 1941 1942var _ = Request(&WriteRequest{}) 1943 1944func (r *WriteRequest) String() string { 1945 return fmt.Sprintf("Write [%s] %v %d @%d fl=%v lock=%d ffl=%v", &r.Header, r.Handle, len(r.Data), r.Offset, r.Flags, r.LockOwner, r.FileFlags) 1946} 1947 1948type jsonWriteRequest struct { 1949 Handle HandleID 1950 Offset int64 1951 Len uint64 1952 Flags WriteFlags 1953} 1954 1955func (r *WriteRequest) MarshalJSON() ([]byte, error) { 1956 j := jsonWriteRequest{ 1957 Handle: r.Handle, 1958 Offset: r.Offset, 1959 Len: uint64(len(r.Data)), 1960 Flags: r.Flags, 1961 } 1962 return json.Marshal(j) 1963} 1964 1965// Respond replies to the request with the given response. 1966func (r *WriteRequest) Respond(resp *WriteResponse) { 1967 buf := newBuffer(unsafe.Sizeof(writeOut{})) 1968 out := (*writeOut)(buf.alloc(unsafe.Sizeof(writeOut{}))) 1969 out.Size = uint32(resp.Size) 1970 r.respond(buf) 1971} 1972 1973// A WriteResponse replies to a write indicating how many bytes were written. 1974type WriteResponse struct { 1975 Size int 1976} 1977 1978func (r *WriteResponse) String() string { 1979 return fmt.Sprintf("Write %d", r.Size) 1980} 1981 1982// A SetattrRequest asks to change one or more attributes associated with a file, 1983// as indicated by Valid. 1984type SetattrRequest struct { 1985 Header `json:"-"` 1986 Valid SetattrValid 1987 Handle HandleID 1988 Size uint64 1989 Atime time.Time 1990 Mtime time.Time 1991 Mode os.FileMode 1992 Uid uint32 1993 Gid uint32 1994 1995 // OS X only 1996 Bkuptime time.Time 1997 Chgtime time.Time 1998 Crtime time.Time 1999 Flags uint32 // see chflags(2) 2000} 2001 2002var _ = Request(&SetattrRequest{}) 2003 2004func (r *SetattrRequest) String() string { 2005 var buf bytes.Buffer 2006 fmt.Fprintf(&buf, "Setattr [%s]", &r.Header) 2007 if r.Valid.Mode() { 2008 fmt.Fprintf(&buf, " mode=%v", r.Mode) 2009 } 2010 if r.Valid.Uid() { 2011 fmt.Fprintf(&buf, " uid=%d", r.Uid) 2012 } 2013 if r.Valid.Gid() { 2014 fmt.Fprintf(&buf, " gid=%d", r.Gid) 2015 } 2016 if r.Valid.Size() { 2017 fmt.Fprintf(&buf, " size=%d", r.Size) 2018 } 2019 if r.Valid.Atime() { 2020 fmt.Fprintf(&buf, " atime=%v", r.Atime) 2021 } 2022 if r.Valid.AtimeNow() { 2023 fmt.Fprintf(&buf, " atime=now") 2024 } 2025 if r.Valid.Mtime() { 2026 fmt.Fprintf(&buf, " mtime=%v", r.Mtime) 2027 } 2028 if r.Valid.MtimeNow() { 2029 fmt.Fprintf(&buf, " mtime=now") 2030 } 2031 if r.Valid.Handle() { 2032 fmt.Fprintf(&buf, " handle=%v", r.Handle) 2033 } else { 2034 fmt.Fprintf(&buf, " handle=INVALID-%v", r.Handle) 2035 } 2036 if r.Valid.LockOwner() { 2037 fmt.Fprintf(&buf, " lockowner") 2038 } 2039 if r.Valid.Crtime() { 2040 fmt.Fprintf(&buf, " crtime=%v", r.Crtime) 2041 } 2042 if r.Valid.Chgtime() { 2043 fmt.Fprintf(&buf, " chgtime=%v", r.Chgtime) 2044 } 2045 if r.Valid.Bkuptime() { 2046 fmt.Fprintf(&buf, " bkuptime=%v", r.Bkuptime) 2047 } 2048 if r.Valid.Flags() { 2049 fmt.Fprintf(&buf, " flags=%v", r.Flags) 2050 } 2051 return buf.String() 2052} 2053 2054// Respond replies to the request with the given response, 2055// giving the updated attributes. 2056func (r *SetattrRequest) Respond(resp *SetattrResponse) { 2057 size := attrOutSize(r.Header.Conn.proto) 2058 buf := newBuffer(size) 2059 out := (*attrOut)(buf.alloc(size)) 2060 out.AttrValid = uint64(resp.Attr.Valid / time.Second) 2061 out.AttrValidNsec = uint32(resp.Attr.Valid % time.Second / time.Nanosecond) 2062 resp.Attr.attr(&out.Attr, r.Header.Conn.proto) 2063 r.respond(buf) 2064} 2065 2066// A SetattrResponse is the response to a SetattrRequest. 2067type SetattrResponse struct { 2068 Attr Attr // file attributes 2069} 2070 2071func (r *SetattrResponse) String() string { 2072 return fmt.Sprintf("Setattr %v", r.Attr) 2073} 2074 2075// A FlushRequest asks for the current state of an open file to be flushed 2076// to storage, as when a file descriptor is being closed. A single opened Handle 2077// may receive multiple FlushRequests over its lifetime. 2078type FlushRequest struct { 2079 Header `json:"-"` 2080 Handle HandleID 2081 Flags uint32 2082 LockOwner uint64 2083} 2084 2085var _ = Request(&FlushRequest{}) 2086 2087func (r *FlushRequest) String() string { 2088 return fmt.Sprintf("Flush [%s] %v fl=%#x lk=%#x", &r.Header, r.Handle, r.Flags, r.LockOwner) 2089} 2090 2091// Respond replies to the request, indicating that the flush succeeded. 2092func (r *FlushRequest) Respond() { 2093 buf := newBuffer(0) 2094 r.respond(buf) 2095} 2096 2097// A RemoveRequest asks to remove a file or directory from the 2098// directory r.Node. 2099type RemoveRequest struct { 2100 Header `json:"-"` 2101 Name string // name of the entry to remove 2102 Dir bool // is this rmdir? 2103} 2104 2105var _ = Request(&RemoveRequest{}) 2106 2107func (r *RemoveRequest) String() string { 2108 return fmt.Sprintf("Remove [%s] %q dir=%v", &r.Header, r.Name, r.Dir) 2109} 2110 2111// Respond replies to the request, indicating that the file was removed. 2112func (r *RemoveRequest) Respond() { 2113 buf := newBuffer(0) 2114 r.respond(buf) 2115} 2116 2117// A SymlinkRequest is a request to create a symlink making NewName point to Target. 2118type SymlinkRequest struct { 2119 Header `json:"-"` 2120 NewName, Target string 2121} 2122 2123var _ = Request(&SymlinkRequest{}) 2124 2125func (r *SymlinkRequest) String() string { 2126 return fmt.Sprintf("Symlink [%s] from %q to target %q", &r.Header, r.NewName, r.Target) 2127} 2128 2129// Respond replies to the request, indicating that the symlink was created. 2130func (r *SymlinkRequest) Respond(resp *SymlinkResponse) { 2131 size := entryOutSize(r.Header.Conn.proto) 2132 buf := newBuffer(size) 2133 out := (*entryOut)(buf.alloc(size)) 2134 out.Nodeid = uint64(resp.Node) 2135 out.Generation = resp.Generation 2136 out.EntryValid = uint64(resp.EntryValid / time.Second) 2137 out.EntryValidNsec = uint32(resp.EntryValid % time.Second / time.Nanosecond) 2138 out.AttrValid = uint64(resp.Attr.Valid / time.Second) 2139 out.AttrValidNsec = uint32(resp.Attr.Valid % time.Second / time.Nanosecond) 2140 resp.Attr.attr(&out.Attr, r.Header.Conn.proto) 2141 r.respond(buf) 2142} 2143 2144// A SymlinkResponse is the response to a SymlinkRequest. 2145type SymlinkResponse struct { 2146 LookupResponse 2147} 2148 2149func (r *SymlinkResponse) String() string { 2150 return fmt.Sprintf("Symlink %v", r.LookupResponse.string()) 2151} 2152 2153// A ReadlinkRequest is a request to read a symlink's target. 2154type ReadlinkRequest struct { 2155 Header `json:"-"` 2156} 2157 2158var _ = Request(&ReadlinkRequest{}) 2159 2160func (r *ReadlinkRequest) String() string { 2161 return fmt.Sprintf("Readlink [%s]", &r.Header) 2162} 2163 2164func (r *ReadlinkRequest) Respond(target string) { 2165 buf := newBuffer(uintptr(len(target))) 2166 buf = append(buf, target...) 2167 r.respond(buf) 2168} 2169 2170// A LinkRequest is a request to create a hard link. 2171type LinkRequest struct { 2172 Header `json:"-"` 2173 OldNode NodeID 2174 NewName string 2175} 2176 2177var _ = Request(&LinkRequest{}) 2178 2179func (r *LinkRequest) String() string { 2180 return fmt.Sprintf("Link [%s] node %d to %q", &r.Header, r.OldNode, r.NewName) 2181} 2182 2183func (r *LinkRequest) Respond(resp *LookupResponse) { 2184 size := entryOutSize(r.Header.Conn.proto) 2185 buf := newBuffer(size) 2186 out := (*entryOut)(buf.alloc(size)) 2187 out.Nodeid = uint64(resp.Node) 2188 out.Generation = resp.Generation 2189 out.EntryValid = uint64(resp.EntryValid / time.Second) 2190 out.EntryValidNsec = uint32(resp.EntryValid % time.Second / time.Nanosecond) 2191 out.AttrValid = uint64(resp.Attr.Valid / time.Second) 2192 out.AttrValidNsec = uint32(resp.Attr.Valid % time.Second / time.Nanosecond) 2193 resp.Attr.attr(&out.Attr, r.Header.Conn.proto) 2194 r.respond(buf) 2195} 2196 2197// A RenameRequest is a request to rename a file. 2198type RenameRequest struct { 2199 Header `json:"-"` 2200 NewDir NodeID 2201 OldName, NewName string 2202} 2203 2204var _ = Request(&RenameRequest{}) 2205 2206func (r *RenameRequest) String() string { 2207 return fmt.Sprintf("Rename [%s] from %q to dirnode %v %q", &r.Header, r.OldName, r.NewDir, r.NewName) 2208} 2209 2210func (r *RenameRequest) Respond() { 2211 buf := newBuffer(0) 2212 r.respond(buf) 2213} 2214 2215type MknodRequest struct { 2216 Header `json:"-"` 2217 Name string 2218 Mode os.FileMode 2219 Rdev uint32 2220 // Umask of the request. Not supported on OS X. 2221 Umask os.FileMode 2222} 2223 2224var _ = Request(&MknodRequest{}) 2225 2226func (r *MknodRequest) String() string { 2227 return fmt.Sprintf("Mknod [%s] Name %q mode=%v umask=%v rdev=%d", &r.Header, r.Name, r.Mode, r.Umask, r.Rdev) 2228} 2229 2230func (r *MknodRequest) Respond(resp *LookupResponse) { 2231 size := entryOutSize(r.Header.Conn.proto) 2232 buf := newBuffer(size) 2233 out := (*entryOut)(buf.alloc(size)) 2234 out.Nodeid = uint64(resp.Node) 2235 out.Generation = resp.Generation 2236 out.EntryValid = uint64(resp.EntryValid / time.Second) 2237 out.EntryValidNsec = uint32(resp.EntryValid % time.Second / time.Nanosecond) 2238 out.AttrValid = uint64(resp.Attr.Valid / time.Second) 2239 out.AttrValidNsec = uint32(resp.Attr.Valid % time.Second / time.Nanosecond) 2240 resp.Attr.attr(&out.Attr, r.Header.Conn.proto) 2241 r.respond(buf) 2242} 2243 2244type FsyncRequest struct { 2245 Header `json:"-"` 2246 Handle HandleID 2247 // TODO bit 1 is datasync, not well documented upstream 2248 Flags uint32 2249 Dir bool 2250} 2251 2252var _ = Request(&FsyncRequest{}) 2253 2254func (r *FsyncRequest) String() string { 2255 return fmt.Sprintf("Fsync [%s] Handle %v Flags %v", &r.Header, r.Handle, r.Flags) 2256} 2257 2258func (r *FsyncRequest) Respond() { 2259 buf := newBuffer(0) 2260 r.respond(buf) 2261} 2262 2263// An InterruptRequest is a request to interrupt another pending request. The 2264// response to that request should return an error status of EINTR. 2265type InterruptRequest struct { 2266 Header `json:"-"` 2267 IntrID RequestID // ID of the request to be interrupt. 2268} 2269 2270var _ = Request(&InterruptRequest{}) 2271 2272func (r *InterruptRequest) Respond() { 2273 // nothing to do here 2274 r.noResponse() 2275} 2276 2277func (r *InterruptRequest) String() string { 2278 return fmt.Sprintf("Interrupt [%s] ID %v", &r.Header, r.IntrID) 2279} 2280 2281// An ExchangeDataRequest is a request to exchange the contents of two 2282// files, while leaving most metadata untouched. 2283// 2284// This request comes from OS X exchangedata(2) and represents its 2285// specific semantics. Crucially, it is very different from Linux 2286// renameat(2) RENAME_EXCHANGE. 2287// 2288// https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/exchangedata.2.html 2289type ExchangeDataRequest struct { 2290 Header `json:"-"` 2291 OldDir, NewDir NodeID 2292 OldName, NewName string 2293 // TODO options 2294} 2295 2296var _ = Request(&ExchangeDataRequest{}) 2297 2298func (r *ExchangeDataRequest) String() string { 2299 // TODO options 2300 return fmt.Sprintf("ExchangeData [%s] %v %q and %v %q", &r.Header, r.OldDir, r.OldName, r.NewDir, r.NewName) 2301} 2302 2303func (r *ExchangeDataRequest) Respond() { 2304 buf := newBuffer(0) 2305 r.respond(buf) 2306} 2307