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