github.com/hanwen/go-fuse@v1.0.0/fuse/server.go (about) 1 // Copyright 2016 the Go-FUSE Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package fuse 6 7 import ( 8 "fmt" 9 "log" 10 "math" 11 "os" 12 "path/filepath" 13 "runtime" 14 "strings" 15 "sync" 16 "syscall" 17 "time" 18 ) 19 20 const ( 21 // The kernel caps writes at 128k. 22 MAX_KERNEL_WRITE = 128 * 1024 23 ) 24 25 // Server contains the logic for reading from the FUSE device and 26 // translating it to RawFileSystem interface calls. 27 type Server struct { 28 // Empty if unmounted. 29 mountPoint string 30 fileSystem RawFileSystem 31 32 // writeMu serializes close and notify writes 33 writeMu sync.Mutex 34 35 // I/O with kernel and daemon. 36 mountFd int 37 38 latencies LatencyMap 39 40 opts *MountOptions 41 42 // Pool for request structs. 43 reqPool sync.Pool 44 45 // Pool for raw requests data 46 readPool sync.Pool 47 reqMu sync.Mutex 48 reqReaders int 49 kernelSettings InitIn 50 51 // in-flight notify-retrieve queries 52 retrieveMu sync.Mutex 53 retrieveNext uint64 54 retrieveTab map[uint64]*retrieveCacheRequest // notifyUnique -> retrieve request 55 56 singleReader bool 57 canSplice bool 58 loops sync.WaitGroup 59 60 ready chan error 61 } 62 63 // SetDebug is deprecated. Use MountOptions.Debug instead. 64 func (ms *Server) SetDebug(dbg bool) { 65 // This will typically trigger the race detector. 66 ms.opts.Debug = dbg 67 } 68 69 // KernelSettings returns the Init message from the kernel, so 70 // filesystems can adapt to availability of features of the kernel 71 // driver. The message should not be altered. 72 func (ms *Server) KernelSettings() *InitIn { 73 ms.reqMu.Lock() 74 s := ms.kernelSettings 75 ms.reqMu.Unlock() 76 77 return &s 78 } 79 80 const _MAX_NAME_LEN = 20 81 82 // This type may be provided for recording latencies of each FUSE 83 // operation. 84 type LatencyMap interface { 85 Add(name string, dt time.Duration) 86 } 87 88 // RecordLatencies switches on collection of timing for each request 89 // coming from the kernel.P assing a nil argument switches off the 90 func (ms *Server) RecordLatencies(l LatencyMap) { 91 ms.latencies = l 92 } 93 94 // Unmount calls fusermount -u on the mount. This has the effect of 95 // shutting down the filesystem. After the Server is unmounted, it 96 // should be discarded. 97 func (ms *Server) Unmount() (err error) { 98 if ms.mountPoint == "" { 99 return nil 100 } 101 delay := time.Duration(0) 102 for try := 0; try < 5; try++ { 103 err = unmount(ms.mountPoint) 104 if err == nil { 105 break 106 } 107 108 // Sleep for a bit. This is not pretty, but there is 109 // no way we can be certain that the kernel thinks all 110 // open files have already been closed. 111 delay = 2*delay + 5*time.Millisecond 112 time.Sleep(delay) 113 } 114 if err != nil { 115 return 116 } 117 // Wait for event loops to exit. 118 ms.loops.Wait() 119 ms.mountPoint = "" 120 return err 121 } 122 123 // NewServer creates a server and attaches it to the given directory. 124 func NewServer(fs RawFileSystem, mountPoint string, opts *MountOptions) (*Server, error) { 125 if opts == nil { 126 opts = &MountOptions{ 127 MaxBackground: _DEFAULT_BACKGROUND_TASKS, 128 } 129 } 130 o := *opts 131 if o.SingleThreaded { 132 fs = NewLockingRawFileSystem(fs) 133 } 134 135 if o.Buffers == nil { 136 o.Buffers = defaultBufferPool 137 } 138 if o.MaxWrite < 0 { 139 o.MaxWrite = 0 140 } 141 if o.MaxWrite == 0 { 142 o.MaxWrite = 1 << 16 143 } 144 if o.MaxWrite > MAX_KERNEL_WRITE { 145 o.MaxWrite = MAX_KERNEL_WRITE 146 } 147 if o.Name == "" { 148 name := fs.String() 149 l := len(name) 150 if l > _MAX_NAME_LEN { 151 l = _MAX_NAME_LEN 152 } 153 o.Name = strings.Replace(name[:l], ",", ";", -1) 154 } 155 156 for _, s := range o.optionsStrings() { 157 if strings.Contains(s, ",") { 158 return nil, fmt.Errorf("found ',' in option string %q", s) 159 } 160 } 161 162 ms := &Server{ 163 fileSystem: fs, 164 opts: &o, 165 retrieveTab: make(map[uint64]*retrieveCacheRequest), 166 // OSX has races when multiple routines read from the 167 // FUSE device: on unmount, sometime some reads do not 168 // error-out, meaning that unmount will hang. 169 singleReader: runtime.GOOS == "darwin", 170 ready: make(chan error, 1), 171 } 172 ms.reqPool.New = func() interface{} { return new(request) } 173 ms.readPool.New = func() interface{} { return make([]byte, o.MaxWrite+pageSize) } 174 175 mountPoint = filepath.Clean(mountPoint) 176 if !filepath.IsAbs(mountPoint) { 177 cwd, err := os.Getwd() 178 if err != nil { 179 return nil, err 180 } 181 mountPoint = filepath.Clean(filepath.Join(cwd, mountPoint)) 182 } 183 fd, err := mount(mountPoint, &o, ms.ready) 184 if err != nil { 185 return nil, err 186 } 187 188 ms.mountPoint = mountPoint 189 ms.mountFd = fd 190 191 if code := ms.handleInit(); !code.Ok() { 192 syscall.Close(fd) 193 // TODO - unmount as well? 194 return nil, fmt.Errorf("init: %s", code) 195 } 196 return ms, nil 197 } 198 199 func (o *MountOptions) optionsStrings() []string { 200 var r []string 201 r = append(r, o.Options...) 202 203 if o.AllowOther { 204 r = append(r, "allow_other") 205 } 206 207 if o.FsName != "" { 208 r = append(r, "fsname="+o.FsName) 209 } 210 if o.Name != "" { 211 r = append(r, "subtype="+o.Name) 212 } 213 214 return r 215 } 216 217 // DebugData returns internal status information for debugging 218 // purposes. 219 func (ms *Server) DebugData() string { 220 var r int 221 ms.reqMu.Lock() 222 r = ms.reqReaders 223 ms.reqMu.Unlock() 224 225 return fmt.Sprintf("readers: %d", r) 226 } 227 228 // What is a good number? Maybe the number of CPUs? 229 const _MAX_READERS = 2 230 231 // handleEINTR retries the given function until it doesn't return syscall.EINTR. 232 // This is similar to the HANDLE_EINTR() macro from Chromium ( see 233 // https://code.google.com/p/chromium/codesearch#chromium/src/base/posix/eintr_wrapper.h 234 // ) and the TEMP_FAILURE_RETRY() from glibc (see 235 // https://www.gnu.org/software/libc/manual/html_node/Interrupted-Primitives.html 236 // ). 237 // 238 // Don't use handleEINTR() with syscall.Close(); see 239 // https://code.google.com/p/chromium/issues/detail?id=269623 . 240 func handleEINTR(fn func() error) (err error) { 241 for { 242 err = fn() 243 if err != syscall.EINTR { 244 break 245 } 246 } 247 return 248 } 249 250 // Returns a new request, or error. In case exitIdle is given, returns 251 // nil, OK if we have too many readers already. 252 func (ms *Server) readRequest(exitIdle bool) (req *request, code Status) { 253 ms.reqMu.Lock() 254 if ms.reqReaders > _MAX_READERS { 255 ms.reqMu.Unlock() 256 return nil, OK 257 } 258 req = ms.reqPool.Get().(*request) 259 dest := ms.readPool.Get().([]byte) 260 ms.reqReaders++ 261 ms.reqMu.Unlock() 262 263 var n int 264 err := handleEINTR(func() error { 265 var err error 266 n, err = syscall.Read(ms.mountFd, dest) 267 return err 268 }) 269 if err != nil { 270 code = ToStatus(err) 271 ms.reqPool.Put(req) 272 ms.reqMu.Lock() 273 ms.reqReaders-- 274 ms.reqMu.Unlock() 275 return nil, code 276 } 277 278 if ms.latencies != nil { 279 req.startTime = time.Now() 280 } 281 gobbled := req.setInput(dest[:n]) 282 283 ms.reqMu.Lock() 284 if !gobbled { 285 ms.readPool.Put(dest) 286 dest = nil 287 } 288 ms.reqReaders-- 289 if !ms.singleReader && ms.reqReaders <= 0 { 290 ms.loops.Add(1) 291 go ms.loop(true) 292 } 293 ms.reqMu.Unlock() 294 295 return req, OK 296 } 297 298 // returnRequest returns a request to the pool of unused requests. 299 func (ms *Server) returnRequest(req *request) { 300 ms.recordStats(req) 301 302 if req.bufferPoolOutputBuf != nil { 303 ms.opts.Buffers.FreeBuffer(req.bufferPoolOutputBuf) 304 req.bufferPoolOutputBuf = nil 305 } 306 307 req.clear() 308 309 if p := req.bufferPoolInputBuf; p != nil { 310 req.bufferPoolInputBuf = nil 311 ms.readPool.Put(p) 312 } 313 ms.reqPool.Put(req) 314 } 315 316 func (ms *Server) recordStats(req *request) { 317 if ms.latencies != nil { 318 dt := time.Now().Sub(req.startTime) 319 opname := operationName(req.inHeader.Opcode) 320 ms.latencies.Add(opname, dt) 321 } 322 } 323 324 // Serve initiates the FUSE loop. Normally, callers should run Serve() 325 // and wait for it to exit, but tests will want to run this in a 326 // goroutine. 327 // 328 // Each filesystem operation executes in a separate goroutine. 329 func (ms *Server) Serve() { 330 ms.loops.Add(1) 331 ms.loop(false) 332 ms.loops.Wait() 333 334 ms.writeMu.Lock() 335 syscall.Close(ms.mountFd) 336 ms.writeMu.Unlock() 337 338 // shutdown in-flight cache retrieves. 339 // 340 // It is possible that umount comes in the middle - after retrieve 341 // request was sent to kernel, but corresponding kernel reply has not 342 // yet been read. We unblock all such readers and wake them up with ENODEV. 343 ms.retrieveMu.Lock() 344 rtab := ms.retrieveTab 345 // retrieve attempts might be erroneously tried even after close 346 // we have to keep retrieveTab !nil not to panic. 347 ms.retrieveTab = make(map[uint64]*retrieveCacheRequest) 348 ms.retrieveMu.Unlock() 349 for _, reading := range rtab { 350 reading.n = 0 351 reading.st = ENODEV 352 close(reading.ready) 353 } 354 } 355 356 func (ms *Server) handleInit() Status { 357 // The first request should be INIT; read it synchronously, 358 // and don't spawn new readers. 359 orig := ms.singleReader 360 ms.singleReader = true 361 req, errNo := ms.readRequest(false) 362 ms.singleReader = orig 363 364 if errNo != OK || req == nil { 365 return errNo 366 } 367 if code := ms.handleRequest(req); !code.Ok() { 368 return code 369 } 370 371 // INIT is handled. Init the file system, but don't accept 372 // incoming requests, so the file system can setup itself. 373 ms.fileSystem.Init(ms) 374 return OK 375 } 376 377 func (ms *Server) loop(exitIdle bool) { 378 defer ms.loops.Done() 379 exit: 380 for { 381 req, errNo := ms.readRequest(exitIdle) 382 switch errNo { 383 case OK: 384 if req == nil { 385 break exit 386 } 387 case ENOENT: 388 continue 389 case ENODEV: 390 // unmount 391 if ms.opts.Debug { 392 log.Printf("received ENODEV (unmount request), thread exiting") 393 } 394 break exit 395 default: // some other error? 396 log.Printf("Failed to read from fuse conn: %v", errNo) 397 break exit 398 } 399 400 if ms.singleReader { 401 go ms.handleRequest(req) 402 } else { 403 ms.handleRequest(req) 404 } 405 } 406 } 407 408 func (ms *Server) handleRequest(req *request) Status { 409 req.parse() 410 if req.handler == nil { 411 req.status = ENOSYS 412 } 413 414 if req.status.Ok() && ms.opts.Debug { 415 log.Println(req.InputDebug()) 416 } 417 418 if req.inHeader.NodeId == pollHackInode { 419 // We want to avoid switching off features through our 420 // poll hack, so don't use ENOSYS 421 req.status = EIO 422 if req.inHeader.Opcode == _OP_POLL { 423 req.status = ENOSYS 424 } 425 } else if req.inHeader.NodeId == FUSE_ROOT_ID && len(req.filenames) > 0 && req.filenames[0] == pollHackName { 426 doPollHackLookup(ms, req) 427 } else if req.status.Ok() && req.handler.Func == nil { 428 log.Printf("Unimplemented opcode %v", operationName(req.inHeader.Opcode)) 429 req.status = ENOSYS 430 } else if req.status.Ok() { 431 req.handler.Func(ms, req) 432 } 433 434 errNo := ms.write(req) 435 if errNo != 0 { 436 log.Printf("writer: Write/Writev failed, err: %v. opcode: %v", 437 errNo, operationName(req.inHeader.Opcode)) 438 } 439 ms.returnRequest(req) 440 return Status(errNo) 441 } 442 443 func (ms *Server) allocOut(req *request, size uint32) []byte { 444 if cap(req.bufferPoolOutputBuf) >= int(size) { 445 req.bufferPoolOutputBuf = req.bufferPoolOutputBuf[:size] 446 return req.bufferPoolOutputBuf 447 } 448 if req.bufferPoolOutputBuf != nil { 449 ms.opts.Buffers.FreeBuffer(req.bufferPoolOutputBuf) 450 } 451 req.bufferPoolOutputBuf = ms.opts.Buffers.AllocBuffer(size) 452 return req.bufferPoolOutputBuf 453 } 454 455 func (ms *Server) write(req *request) Status { 456 // Forget/NotifyReply do not wait for reply from filesystem server. 457 switch req.inHeader.Opcode { 458 case _OP_FORGET, _OP_BATCH_FORGET, _OP_NOTIFY_REPLY: 459 return OK 460 } 461 462 header := req.serializeHeader(req.flatDataSize()) 463 if ms.opts.Debug { 464 log.Println(req.OutputDebug()) 465 } 466 467 if header == nil { 468 return OK 469 } 470 471 s := ms.systemWrite(req, header) 472 return s 473 } 474 475 // InodeNotify invalidates the information associated with the inode 476 // (ie. data cache, attributes, etc.) 477 func (ms *Server) InodeNotify(node uint64, off int64, length int64) Status { 478 if !ms.kernelSettings.SupportsNotify(NOTIFY_INVAL_INODE) { 479 return ENOSYS 480 } 481 482 req := request{ 483 inHeader: &InHeader{ 484 Opcode: _OP_NOTIFY_INVAL_INODE, 485 }, 486 handler: operationHandlers[_OP_NOTIFY_INVAL_INODE], 487 status: NOTIFY_INVAL_INODE, 488 } 489 490 entry := (*NotifyInvalInodeOut)(req.outData()) 491 entry.Ino = node 492 entry.Off = off 493 entry.Length = length 494 495 // Protect against concurrent close. 496 ms.writeMu.Lock() 497 result := ms.write(&req) 498 ms.writeMu.Unlock() 499 500 if ms.opts.Debug { 501 log.Println("Response: INODE_NOTIFY", result) 502 } 503 return result 504 } 505 506 // InodeNotifyStoreCache tells kernel to store data into inode's cache. 507 // 508 // This call is similar to InodeNotify, but instead of only invalidating a data 509 // region, it gives updated data directly to the kernel. 510 func (ms *Server) InodeNotifyStoreCache(node uint64, offset int64, data []byte) Status { 511 if !ms.kernelSettings.SupportsNotify(NOTIFY_STORE_CACHE) { 512 return ENOSYS 513 } 514 515 for len(data) > 0 { 516 size := len(data) 517 if size > math.MaxInt32 { 518 // NotifyStoreOut has only uint32 for size. 519 // we check for max(int32), not max(uint32), because on 32-bit 520 // platforms int has only 31-bit for positive range. 521 size = math.MaxInt32 522 } 523 524 st := ms.inodeNotifyStoreCache32(node, offset, data[:size]) 525 if st != OK { 526 return st 527 } 528 529 data = data[size:] 530 offset += int64(size) 531 } 532 533 return OK 534 } 535 536 // inodeNotifyStoreCache32 is internal worker for InodeNotifyStoreCache which 537 // handles data chunks not larger than 2GB. 538 func (ms *Server) inodeNotifyStoreCache32(node uint64, offset int64, data []byte) Status { 539 req := request{ 540 inHeader: &InHeader{ 541 Opcode: _OP_NOTIFY_STORE_CACHE, 542 }, 543 handler: operationHandlers[_OP_NOTIFY_STORE_CACHE], 544 status: NOTIFY_STORE_CACHE, 545 } 546 547 store := (*NotifyStoreOut)(req.outData()) 548 store.Nodeid = node 549 store.Offset = uint64(offset) // NOTE not int64, as it is e.g. in NotifyInvalInodeOut 550 store.Size = uint32(len(data)) 551 552 req.flatData = data 553 554 // Protect against concurrent close. 555 ms.writeMu.Lock() 556 result := ms.write(&req) 557 ms.writeMu.Unlock() 558 559 if ms.opts.Debug { 560 log.Printf("Response: INODE_NOTIFY_STORE_CACHE: %v", result) 561 } 562 return result 563 } 564 565 // InodeRetrieveCache retrieves data from kernel's inode cache. 566 // 567 // InodeRetrieveCache asks kernel to return data from its cache for inode at 568 // [offset:offset+len(dest)) and waits for corresponding reply. If kernel cache 569 // has fewer consecutive data starting at offset, that fewer amount is returned. 570 // In particular if inode data at offset is not cached (0, OK) is returned. 571 // 572 // The kernel returns ENOENT if it does not currently have entry for this inode 573 // in its dentry cache. 574 func (ms *Server) InodeRetrieveCache(node uint64, offset int64, dest []byte) (n int, st Status) { 575 // the kernel won't send us in one go more then what we negotiated as MaxWrite. 576 // retrieve the data in chunks. 577 // TODO spawn some number of readahead retrievers in parallel. 578 ntotal := 0 579 for { 580 chunkSize := len(dest) 581 if chunkSize > ms.opts.MaxWrite { 582 chunkSize = ms.opts.MaxWrite 583 } 584 n, st = ms.inodeRetrieveCache1(node, offset, dest[:chunkSize]) 585 if st != OK || n == 0 { 586 break 587 } 588 589 ntotal += n 590 offset += int64(n) 591 dest = dest[n:] 592 } 593 594 // if we could retrieve at least something - it is ok. 595 // if ntotal=0 - st will be st returned from first inodeRetrieveCache1. 596 if ntotal > 0 { 597 st = OK 598 } 599 return ntotal, st 600 } 601 602 // inodeRetrieveCache1 is internal worker for InodeRetrieveCache which 603 // actually talks to kernel and retrieves chunks not larger than ms.opts.MaxWrite. 604 func (ms *Server) inodeRetrieveCache1(node uint64, offset int64, dest []byte) (n int, st Status) { 605 if !ms.kernelSettings.SupportsNotify(NOTIFY_RETRIEVE_CACHE) { 606 return 0, ENOSYS 607 } 608 609 req := request{ 610 inHeader: &InHeader{ 611 Opcode: _OP_NOTIFY_RETRIEVE_CACHE, 612 }, 613 handler: operationHandlers[_OP_NOTIFY_RETRIEVE_CACHE], 614 status: NOTIFY_RETRIEVE_CACHE, 615 } 616 617 // retrieve up to 2GB not to overflow uint32 size in NotifyRetrieveOut. 618 // see InodeNotifyStoreCache in similar place for why it is only 2GB, not 4GB. 619 // 620 // ( InodeRetrieveCache calls us with chunks not larger than 621 // ms.opts.MaxWrite, but MaxWrite is int, so let's be extra cautious ) 622 size := len(dest) 623 if size > math.MaxInt32 { 624 size = math.MaxInt32 625 } 626 dest = dest[:size] 627 628 q := (*NotifyRetrieveOut)(req.outData()) 629 q.Nodeid = node 630 q.Offset = uint64(offset) // not int64, as it is e.g. in NotifyInvalInodeOut 631 q.Size = uint32(len(dest)) 632 633 reading := &retrieveCacheRequest{ 634 nodeid: q.Nodeid, 635 offset: q.Offset, 636 dest: dest, 637 ready: make(chan struct{}), 638 } 639 640 ms.retrieveMu.Lock() 641 q.NotifyUnique = ms.retrieveNext 642 ms.retrieveNext++ 643 ms.retrieveTab[q.NotifyUnique] = reading 644 ms.retrieveMu.Unlock() 645 646 // Protect against concurrent close. 647 ms.writeMu.Lock() 648 result := ms.write(&req) 649 ms.writeMu.Unlock() 650 651 if ms.opts.Debug { 652 log.Printf("Response: NOTIFY_RETRIEVE_CACHE: %v", result) 653 } 654 if result != OK { 655 ms.retrieveMu.Lock() 656 r := ms.retrieveTab[q.NotifyUnique] 657 if r == reading { 658 delete(ms.retrieveTab, q.NotifyUnique) 659 } else if r == nil { 660 // ok - might be dequeued by umount 661 } else { 662 // although very unlikely, it is possible that kernel sends 663 // unexpected NotifyReply with our notifyUnique, then 664 // retrieveNext wraps, makes full cycle, and another 665 // retrieve request is made with the same notifyUnique. 666 log.Printf("W: INODE_RETRIEVE_CACHE: request with notifyUnique=%d mutated", q.NotifyUnique) 667 } 668 ms.retrieveMu.Unlock() 669 return 0, result 670 } 671 672 // NotifyRetrieveOut sent to the kernel successfully. Now the kernel 673 // have to return data in a separate write-style NotifyReply request. 674 // Wait for the result. 675 <-reading.ready 676 return reading.n, reading.st 677 } 678 679 // retrieveCacheRequest represents in-flight cache retrieve request. 680 type retrieveCacheRequest struct { 681 nodeid uint64 682 offset uint64 683 dest []byte 684 685 // reply status 686 n int 687 st Status 688 ready chan struct{} 689 } 690 691 // DeleteNotify notifies the kernel that an entry is removed from a 692 // directory. In many cases, this is equivalent to EntryNotify, 693 // except when the directory is in use, eg. as working directory of 694 // some process. You should not hold any FUSE filesystem locks, as that 695 // can lead to deadlock. 696 func (ms *Server) DeleteNotify(parent uint64, child uint64, name string) Status { 697 if ms.kernelSettings.Minor < 18 { 698 return ms.EntryNotify(parent, name) 699 } 700 701 req := request{ 702 inHeader: &InHeader{ 703 Opcode: _OP_NOTIFY_DELETE, 704 }, 705 handler: operationHandlers[_OP_NOTIFY_DELETE], 706 status: NOTIFY_DELETE, 707 } 708 709 entry := (*NotifyInvalDeleteOut)(req.outData()) 710 entry.Parent = parent 711 entry.Child = child 712 entry.NameLen = uint32(len(name)) 713 714 // Many versions of FUSE generate stacktraces if the 715 // terminating null byte is missing. 716 nameBytes := make([]byte, len(name)+1) 717 copy(nameBytes, name) 718 nameBytes[len(nameBytes)-1] = '\000' 719 req.flatData = nameBytes 720 721 // Protect against concurrent close. 722 ms.writeMu.Lock() 723 result := ms.write(&req) 724 ms.writeMu.Unlock() 725 726 if ms.opts.Debug { 727 log.Printf("Response: DELETE_NOTIFY: %v", result) 728 } 729 return result 730 } 731 732 // EntryNotify should be used if the existence status of an entry 733 // within a directory changes. You should not hold any FUSE filesystem 734 // locks, as that can lead to deadlock. 735 func (ms *Server) EntryNotify(parent uint64, name string) Status { 736 if !ms.kernelSettings.SupportsNotify(NOTIFY_INVAL_ENTRY) { 737 return ENOSYS 738 } 739 req := request{ 740 inHeader: &InHeader{ 741 Opcode: _OP_NOTIFY_INVAL_ENTRY, 742 }, 743 handler: operationHandlers[_OP_NOTIFY_INVAL_ENTRY], 744 status: NOTIFY_INVAL_ENTRY, 745 } 746 entry := (*NotifyInvalEntryOut)(req.outData()) 747 entry.Parent = parent 748 entry.NameLen = uint32(len(name)) 749 750 // Many versions of FUSE generate stacktraces if the 751 // terminating null byte is missing. 752 nameBytes := make([]byte, len(name)+1) 753 copy(nameBytes, name) 754 nameBytes[len(nameBytes)-1] = '\000' 755 req.flatData = nameBytes 756 757 // Protect against concurrent close. 758 ms.writeMu.Lock() 759 result := ms.write(&req) 760 ms.writeMu.Unlock() 761 762 if ms.opts.Debug { 763 log.Printf("Response: ENTRY_NOTIFY: %v", result) 764 } 765 return result 766 } 767 768 // SupportsVersion returns true if the kernel supports the given 769 // protocol version or newer. 770 func (in *InitIn) SupportsVersion(maj, min uint32) bool { 771 return in.Major >= maj || (in.Major == maj && in.Minor >= min) 772 } 773 774 // SupportsNotify returns whether a certain notification type is 775 // supported. Pass any of the NOTIFY_* types as argument. 776 func (in *InitIn) SupportsNotify(notifyType int) bool { 777 switch notifyType { 778 case NOTIFY_INVAL_ENTRY: 779 return in.SupportsVersion(7, 12) 780 case NOTIFY_INVAL_INODE: 781 return in.SupportsVersion(7, 12) 782 case NOTIFY_STORE_CACHE, NOTIFY_RETRIEVE_CACHE: 783 return in.SupportsVersion(7, 15) 784 case NOTIFY_DELETE: 785 return in.SupportsVersion(7, 18) 786 } 787 return false 788 } 789 790 var defaultBufferPool BufferPool 791 792 func init() { 793 defaultBufferPool = NewBufferPool() 794 } 795 796 // WaitMount waits for the first request to be served. Use this to 797 // avoid racing between accessing the (empty or not yet mounted) 798 // mountpoint, and the OS trying to setup the user-space mount. 799 func (ms *Server) WaitMount() error { 800 err := <-ms.ready 801 if err != nil { 802 return err 803 } 804 return pollHack(ms.mountPoint) 805 }