github.com/scaleoutsean/fusego@v0.0.0-20220224074057-4a6429e46bb8/conversions.go (about) 1 // Copyright 2015 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package fuse 16 17 import ( 18 "bytes" 19 "errors" 20 "fmt" 21 "os" 22 "reflect" 23 "syscall" 24 "time" 25 "unsafe" 26 27 "github.com/scaleoutsean/fusego/fuseops" 28 "github.com/scaleoutsean/fusego/internal/buffer" 29 "github.com/scaleoutsean/fusego/internal/fusekernel" 30 ) 31 32 //////////////////////////////////////////////////////////////////////// 33 // Incoming messages 34 //////////////////////////////////////////////////////////////////////// 35 36 // Convert a kernel message to an appropriate op. If the op is unknown, a 37 // special unexported type will be used. 38 // 39 // The caller is responsible for arranging for the message to be destroyed. 40 func convertInMessage( 41 inMsg *buffer.InMessage, 42 outMsg *buffer.OutMessage, 43 protocol fusekernel.Protocol) (o interface{}, err error) { 44 switch inMsg.Header().Opcode { 45 case fusekernel.OpLookup: 46 buf := inMsg.ConsumeBytes(inMsg.Len()) 47 n := len(buf) 48 if n == 0 || buf[n-1] != '\x00' { 49 return nil, errors.New("Corrupt OpLookup") 50 } 51 52 o = &fuseops.LookUpInodeOp{ 53 Parent: fuseops.InodeID(inMsg.Header().Nodeid), 54 Name: string(buf[:n-1]), 55 } 56 57 case fusekernel.OpGetattr: 58 o = &fuseops.GetInodeAttributesOp{ 59 Inode: fuseops.InodeID(inMsg.Header().Nodeid), 60 } 61 62 case fusekernel.OpSetattr: 63 type input fusekernel.SetattrIn 64 in := (*input)(inMsg.Consume(unsafe.Sizeof(input{}))) 65 if in == nil { 66 return nil, errors.New("Corrupt OpSetattr") 67 } 68 69 to := &fuseops.SetInodeAttributesOp{ 70 Inode: fuseops.InodeID(inMsg.Header().Nodeid), 71 } 72 o = to 73 74 valid := fusekernel.SetattrValid(in.Valid) 75 if valid&fusekernel.SetattrSize != 0 { 76 to.Size = &in.Size 77 } 78 79 if valid&fusekernel.SetattrMode != 0 { 80 mode := convertFileMode(in.Mode) 81 to.Mode = &mode 82 } 83 84 if valid&fusekernel.SetattrAtime != 0 { 85 t := time.Unix(int64(in.Atime), int64(in.AtimeNsec)) 86 to.Atime = &t 87 } 88 89 if valid&fusekernel.SetattrMtime != 0 { 90 t := time.Unix(int64(in.Mtime), int64(in.MtimeNsec)) 91 to.Mtime = &t 92 } 93 94 if valid.Handle() { 95 t := fuseops.HandleID(in.Fh) 96 to.Handle = &t 97 } 98 99 case fusekernel.OpForget: 100 type input fusekernel.ForgetIn 101 in := (*input)(inMsg.Consume(unsafe.Sizeof(input{}))) 102 if in == nil { 103 return nil, errors.New("Corrupt OpForget") 104 } 105 106 o = &fuseops.ForgetInodeOp{ 107 Inode: fuseops.InodeID(inMsg.Header().Nodeid), 108 N: in.Nlookup, 109 } 110 111 case fusekernel.OpMkdir: 112 in := (*fusekernel.MkdirIn)(inMsg.Consume(fusekernel.MkdirInSize(protocol))) 113 if in == nil { 114 return nil, errors.New("Corrupt OpMkdir") 115 } 116 117 name := inMsg.ConsumeBytes(inMsg.Len()) 118 i := bytes.IndexByte(name, '\x00') 119 if i < 0 { 120 return nil, errors.New("Corrupt OpMkdir") 121 } 122 name = name[:i] 123 124 o = &fuseops.MkDirOp{ 125 Parent: fuseops.InodeID(inMsg.Header().Nodeid), 126 Name: string(name), 127 128 // On Linux, vfs_mkdir calls through to the inode with at most 129 // permissions and sticky bits set (cf. https://goo.gl/WxgQXk), and fuse 130 // passes that on directly (cf. https://goo.gl/f31aMo). In other words, 131 // the fact that this is a directory is implicit in the fact that the 132 // opcode is mkdir. But we want the correct mode to go through, so ensure 133 // that os.ModeDir is set. 134 Mode: convertFileMode(in.Mode) | os.ModeDir, 135 } 136 137 case fusekernel.OpMknod: 138 in := (*fusekernel.MknodIn)(inMsg.Consume(fusekernel.MknodInSize(protocol))) 139 if in == nil { 140 return nil, errors.New("Corrupt OpMknod") 141 } 142 143 name := inMsg.ConsumeBytes(inMsg.Len()) 144 i := bytes.IndexByte(name, '\x00') 145 if i < 0 { 146 return nil, errors.New("Corrupt OpMknod") 147 } 148 name = name[:i] 149 150 o = &fuseops.MkNodeOp{ 151 Parent: fuseops.InodeID(inMsg.Header().Nodeid), 152 Name: string(name), 153 Mode: convertFileMode(in.Mode), 154 } 155 156 case fusekernel.OpCreate: 157 in := (*fusekernel.CreateIn)(inMsg.Consume(fusekernel.CreateInSize(protocol))) 158 if in == nil { 159 return nil, errors.New("Corrupt OpCreate") 160 } 161 162 name := inMsg.ConsumeBytes(inMsg.Len()) 163 i := bytes.IndexByte(name, '\x00') 164 if i < 0 { 165 return nil, errors.New("Corrupt OpCreate") 166 } 167 name = name[:i] 168 169 o = &fuseops.CreateFileOp{ 170 Parent: fuseops.InodeID(inMsg.Header().Nodeid), 171 Name: string(name), 172 Mode: convertFileMode(in.Mode), 173 Metadata: fuseops.OpMetadata{Pid: inMsg.Header().Pid}, 174 } 175 176 case fusekernel.OpSymlink: 177 // The message is "newName\0target\0". 178 names := inMsg.ConsumeBytes(inMsg.Len()) 179 if len(names) == 0 || names[len(names)-1] != 0 { 180 return nil, errors.New("Corrupt OpSymlink") 181 } 182 i := bytes.IndexByte(names, '\x00') 183 if i < 0 { 184 return nil, errors.New("Corrupt OpSymlink") 185 } 186 newName, target := names[0:i], names[i+1:len(names)-1] 187 188 o = &fuseops.CreateSymlinkOp{ 189 Parent: fuseops.InodeID(inMsg.Header().Nodeid), 190 Name: string(newName), 191 Target: string(target), 192 } 193 194 case fusekernel.OpRename: 195 type input fusekernel.RenameIn 196 in := (*input)(inMsg.Consume(unsafe.Sizeof(input{}))) 197 if in == nil { 198 return nil, errors.New("Corrupt OpRename") 199 } 200 201 names := inMsg.ConsumeBytes(inMsg.Len()) 202 // names should be "old\x00new\x00" 203 if len(names) < 4 { 204 return nil, errors.New("Corrupt OpRename") 205 } 206 if names[len(names)-1] != '\x00' { 207 return nil, errors.New("Corrupt OpRename") 208 } 209 i := bytes.IndexByte(names, '\x00') 210 if i < 0 { 211 return nil, errors.New("Corrupt OpRename") 212 } 213 oldName, newName := names[:i], names[i+1:len(names)-1] 214 215 o = &fuseops.RenameOp{ 216 OldParent: fuseops.InodeID(inMsg.Header().Nodeid), 217 OldName: string(oldName), 218 NewParent: fuseops.InodeID(in.Newdir), 219 NewName: string(newName), 220 } 221 222 case fusekernel.OpUnlink: 223 buf := inMsg.ConsumeBytes(inMsg.Len()) 224 n := len(buf) 225 if n == 0 || buf[n-1] != '\x00' { 226 return nil, errors.New("Corrupt OpUnlink") 227 } 228 229 o = &fuseops.UnlinkOp{ 230 Parent: fuseops.InodeID(inMsg.Header().Nodeid), 231 Name: string(buf[:n-1]), 232 } 233 234 case fusekernel.OpRmdir: 235 buf := inMsg.ConsumeBytes(inMsg.Len()) 236 n := len(buf) 237 if n == 0 || buf[n-1] != '\x00' { 238 return nil, errors.New("Corrupt OpRmdir") 239 } 240 241 o = &fuseops.RmDirOp{ 242 Parent: fuseops.InodeID(inMsg.Header().Nodeid), 243 Name: string(buf[:n-1]), 244 } 245 246 case fusekernel.OpOpen: 247 o = &fuseops.OpenFileOp{ 248 Inode: fuseops.InodeID(inMsg.Header().Nodeid), 249 Metadata: fuseops.OpMetadata{Pid: inMsg.Header().Pid}, 250 } 251 252 case fusekernel.OpOpendir: 253 o = &fuseops.OpenDirOp{ 254 Inode: fuseops.InodeID(inMsg.Header().Nodeid), 255 } 256 257 case fusekernel.OpRead: 258 in := (*fusekernel.ReadIn)(inMsg.Consume(fusekernel.ReadInSize(protocol))) 259 if in == nil { 260 return nil, errors.New("Corrupt OpRead") 261 } 262 263 to := &fuseops.ReadFileOp{ 264 Inode: fuseops.InodeID(inMsg.Header().Nodeid), 265 Handle: fuseops.HandleID(in.Fh), 266 Offset: int64(in.Offset), 267 } 268 o = to 269 270 readSize := int(in.Size) 271 p := outMsg.GrowNoZero(readSize) 272 if p == nil { 273 return nil, fmt.Errorf("Can't grow for %d-byte read", readSize) 274 } 275 276 sh := (*reflect.SliceHeader)(unsafe.Pointer(&to.Dst)) 277 sh.Data = uintptr(p) 278 sh.Len = readSize 279 sh.Cap = readSize 280 281 case fusekernel.OpReaddir: 282 in := (*fusekernel.ReadIn)(inMsg.Consume(fusekernel.ReadInSize(protocol))) 283 if in == nil { 284 return nil, errors.New("Corrupt OpReaddir") 285 } 286 287 to := &fuseops.ReadDirOp{ 288 Inode: fuseops.InodeID(inMsg.Header().Nodeid), 289 Handle: fuseops.HandleID(in.Fh), 290 Offset: fuseops.DirOffset(in.Offset), 291 } 292 o = to 293 294 readSize := int(in.Size) 295 p := outMsg.GrowNoZero(readSize) 296 if p == nil { 297 return nil, fmt.Errorf("Can't grow for %d-byte read", readSize) 298 } 299 300 sh := (*reflect.SliceHeader)(unsafe.Pointer(&to.Dst)) 301 sh.Data = uintptr(p) 302 sh.Len = readSize 303 sh.Cap = readSize 304 305 case fusekernel.OpRelease: 306 type input fusekernel.ReleaseIn 307 in := (*input)(inMsg.Consume(unsafe.Sizeof(input{}))) 308 if in == nil { 309 return nil, errors.New("Corrupt OpRelease") 310 } 311 312 o = &fuseops.ReleaseFileHandleOp{ 313 Handle: fuseops.HandleID(in.Fh), 314 } 315 316 case fusekernel.OpReleasedir: 317 type input fusekernel.ReleaseIn 318 in := (*input)(inMsg.Consume(unsafe.Sizeof(input{}))) 319 if in == nil { 320 return nil, errors.New("Corrupt OpReleasedir") 321 } 322 323 o = &fuseops.ReleaseDirHandleOp{ 324 Handle: fuseops.HandleID(in.Fh), 325 } 326 327 case fusekernel.OpWrite: 328 in := (*fusekernel.WriteIn)(inMsg.Consume(fusekernel.WriteInSize(protocol))) 329 if in == nil { 330 return nil, errors.New("Corrupt OpWrite") 331 } 332 333 buf := inMsg.ConsumeBytes(inMsg.Len()) 334 if len(buf) < int(in.Size) { 335 return nil, errors.New("Corrupt OpWrite") 336 } 337 338 o = &fuseops.WriteFileOp{ 339 Inode: fuseops.InodeID(inMsg.Header().Nodeid), 340 Handle: fuseops.HandleID(in.Fh), 341 Data: buf, 342 Offset: int64(in.Offset), 343 } 344 345 case fusekernel.OpFsync: 346 type input fusekernel.FsyncIn 347 in := (*input)(inMsg.Consume(unsafe.Sizeof(input{}))) 348 if in == nil { 349 return nil, errors.New("Corrupt OpFsync") 350 } 351 352 o = &fuseops.SyncFileOp{ 353 Inode: fuseops.InodeID(inMsg.Header().Nodeid), 354 Handle: fuseops.HandleID(in.Fh), 355 } 356 357 case fusekernel.OpFlush: 358 type input fusekernel.FlushIn 359 in := (*input)(inMsg.Consume(unsafe.Sizeof(input{}))) 360 if in == nil { 361 return nil, errors.New("Corrupt OpFlush") 362 } 363 364 o = &fuseops.FlushFileOp{ 365 Inode: fuseops.InodeID(inMsg.Header().Nodeid), 366 Handle: fuseops.HandleID(in.Fh), 367 Metadata: fuseops.OpMetadata{Pid: inMsg.Header().Pid}, 368 } 369 370 case fusekernel.OpReadlink: 371 o = &fuseops.ReadSymlinkOp{ 372 Inode: fuseops.InodeID(inMsg.Header().Nodeid), 373 } 374 375 case fusekernel.OpStatfs: 376 o = &fuseops.StatFSOp{} 377 378 case fusekernel.OpInterrupt: 379 type input fusekernel.InterruptIn 380 in := (*input)(inMsg.Consume(unsafe.Sizeof(input{}))) 381 if in == nil { 382 return nil, errors.New("Corrupt OpInterrupt") 383 } 384 385 o = &interruptOp{ 386 FuseID: in.Unique, 387 } 388 389 case fusekernel.OpInit: 390 type input fusekernel.InitIn 391 in := (*input)(inMsg.Consume(unsafe.Sizeof(input{}))) 392 if in == nil { 393 return nil, errors.New("Corrupt OpInit") 394 } 395 396 o = &initOp{ 397 Kernel: fusekernel.Protocol{in.Major, in.Minor}, 398 MaxReadahead: in.MaxReadahead, 399 Flags: fusekernel.InitFlags(in.Flags), 400 } 401 402 case fusekernel.OpLink: 403 type input fusekernel.LinkIn 404 in := (*input)(inMsg.Consume(unsafe.Sizeof(input{}))) 405 if in == nil { 406 return nil, errors.New("Corrupt OpLink") 407 } 408 409 name := inMsg.ConsumeBytes(inMsg.Len()) 410 i := bytes.IndexByte(name, '\x00') 411 if i < 0 { 412 return nil, errors.New("Corrupt OpLink") 413 } 414 name = name[:i] 415 if len(name) == 0 { 416 return nil, errors.New("Corrupt OpLink (Name not read)") 417 } 418 419 o = &fuseops.CreateLinkOp{ 420 Parent: fuseops.InodeID(inMsg.Header().Nodeid), 421 Name: string(name), 422 Target: fuseops.InodeID(in.Oldnodeid), 423 } 424 425 case fusekernel.OpRemovexattr: 426 buf := inMsg.ConsumeBytes(inMsg.Len()) 427 n := len(buf) 428 if n == 0 || buf[n-1] != '\x00' { 429 return nil, errors.New("Corrupt OpRemovexattr") 430 } 431 432 o = &fuseops.RemoveXattrOp{ 433 Inode: fuseops.InodeID(inMsg.Header().Nodeid), 434 Name: string(buf[:n-1]), 435 } 436 437 case fusekernel.OpGetxattr: 438 type input fusekernel.GetxattrIn 439 in := (*input)(inMsg.Consume(unsafe.Sizeof(input{}))) 440 if in == nil { 441 return nil, errors.New("Corrupt OpGetxattr") 442 } 443 444 name := inMsg.ConsumeBytes(inMsg.Len()) 445 i := bytes.IndexByte(name, '\x00') 446 if i < 0 { 447 return nil, errors.New("Corrupt OpGetxattr") 448 } 449 name = name[:i] 450 451 to := &fuseops.GetXattrOp{ 452 Inode: fuseops.InodeID(inMsg.Header().Nodeid), 453 Name: string(name), 454 } 455 o = to 456 457 readSize := int(in.Size) 458 p := outMsg.GrowNoZero(readSize) 459 if p == nil { 460 return nil, fmt.Errorf("Can't grow for %d-byte read", readSize) 461 } 462 463 sh := (*reflect.SliceHeader)(unsafe.Pointer(&to.Dst)) 464 sh.Data = uintptr(p) 465 sh.Len = readSize 466 sh.Cap = readSize 467 468 case fusekernel.OpListxattr: 469 type input fusekernel.ListxattrIn 470 in := (*input)(inMsg.Consume(unsafe.Sizeof(input{}))) 471 if in == nil { 472 return nil, errors.New("Corrupt OpListxattr") 473 } 474 475 to := &fuseops.ListXattrOp{ 476 Inode: fuseops.InodeID(inMsg.Header().Nodeid), 477 } 478 o = to 479 480 readSize := int(in.Size) 481 if readSize != 0 { 482 p := outMsg.GrowNoZero(readSize) 483 if p == nil { 484 return nil, fmt.Errorf("Can't grow for %d-byte read", readSize) 485 } 486 sh := (*reflect.SliceHeader)(unsafe.Pointer(&to.Dst)) 487 sh.Data = uintptr(p) 488 sh.Len = readSize 489 sh.Cap = readSize 490 } 491 case fusekernel.OpSetxattr: 492 type input fusekernel.SetxattrIn 493 in := (*input)(inMsg.Consume(unsafe.Sizeof(input{}))) 494 if in == nil { 495 return nil, errors.New("Corrupt OpSetxattr") 496 } 497 498 payload := inMsg.ConsumeBytes(inMsg.Len()) 499 // payload should be "name\x00value" 500 if len(payload) < 3 { 501 return nil, errors.New("Corrupt OpSetxattr") 502 } 503 i := bytes.IndexByte(payload, '\x00') 504 if i < 0 { 505 return nil, errors.New("Corrupt OpSetxattr") 506 } 507 508 name, value := payload[:i], payload[i+1:len(payload)] 509 510 o = &fuseops.SetXattrOp{ 511 Inode: fuseops.InodeID(inMsg.Header().Nodeid), 512 Name: string(name), 513 Value: value, 514 Flags: in.Flags, 515 } 516 case fusekernel.OpFallocate: 517 type input fusekernel.FallocateIn 518 in := (*input)(inMsg.Consume(unsafe.Sizeof(input{}))) 519 if in == nil { 520 return nil, errors.New("Corrupt OpFallocate") 521 } 522 523 o = &fuseops.FallocateOp{ 524 Inode: fuseops.InodeID(inMsg.Header().Nodeid), 525 Handle: fuseops.HandleID(in.Fh), 526 Offset: in.Offset, 527 Length: in.Length, 528 Mode: in.Mode, 529 } 530 531 default: 532 o = &unknownOp{ 533 OpCode: inMsg.Header().Opcode, 534 Inode: fuseops.InodeID(inMsg.Header().Nodeid), 535 } 536 } 537 538 return o, nil 539 } 540 541 //////////////////////////////////////////////////////////////////////// 542 // Outgoing messages 543 //////////////////////////////////////////////////////////////////////// 544 545 // Fill in the response that should be sent to the kernel, or set noResponse if 546 // the op requires no response. 547 func (c *Connection) kernelResponse( 548 m *buffer.OutMessage, 549 fuseID uint64, 550 op interface{}, 551 opErr error) (noResponse bool) { 552 h := m.OutHeader() 553 h.Unique = fuseID 554 555 // Special case: handle the ops for which the kernel expects no response. 556 // interruptOp . 557 switch op.(type) { 558 case *fuseops.ForgetInodeOp: 559 return true 560 561 case *interruptOp: 562 return true 563 } 564 565 // If the user returned the error, fill in the error field of the outgoing 566 // message header. 567 if opErr != nil { 568 handled := false 569 570 if !handled { 571 m.OutHeader().Error = -int32(syscall.EIO) 572 if errno, ok := opErr.(syscall.Errno); ok { 573 m.OutHeader().Error = -int32(errno) 574 } 575 576 // Special case: for some types, convertInMessage grew the message in order 577 // to obtain a destination buffer. Make sure that we shrink back to just 578 // the header, because on OS X the kernel otherwise returns EINVAL when we 579 // attempt to write an error response with a length that extends beyond the 580 // header. 581 m.ShrinkTo(buffer.OutMessageHeaderSize) 582 } 583 } 584 585 // Otherwise, fill in the rest of the response. 586 if opErr == nil { 587 c.kernelResponseForOp(m, op) 588 } 589 590 h.Len = uint32(m.Len()) 591 return false 592 } 593 594 // Like kernelResponse, but assumes the user replied with a nil error to the 595 // op. 596 func (c *Connection) kernelResponseForOp( 597 m *buffer.OutMessage, 598 op interface{}) { 599 // Create the appropriate output message 600 switch o := op.(type) { 601 case *fuseops.LookUpInodeOp: 602 size := int(fusekernel.EntryOutSize(c.protocol)) 603 out := (*fusekernel.EntryOut)(m.Grow(size)) 604 convertChildInodeEntry(&o.Entry, out) 605 606 case *fuseops.GetInodeAttributesOp: 607 size := int(fusekernel.AttrOutSize(c.protocol)) 608 out := (*fusekernel.AttrOut)(m.Grow(size)) 609 out.AttrValid, out.AttrValidNsec = convertExpirationTime( 610 o.AttributesExpiration) 611 convertAttributes(o.Inode, &o.Attributes, &out.Attr) 612 613 case *fuseops.SetInodeAttributesOp: 614 size := int(fusekernel.AttrOutSize(c.protocol)) 615 out := (*fusekernel.AttrOut)(m.Grow(size)) 616 out.AttrValid, out.AttrValidNsec = convertExpirationTime( 617 o.AttributesExpiration) 618 convertAttributes(o.Inode, &o.Attributes, &out.Attr) 619 620 case *fuseops.MkDirOp: 621 size := int(fusekernel.EntryOutSize(c.protocol)) 622 out := (*fusekernel.EntryOut)(m.Grow(size)) 623 convertChildInodeEntry(&o.Entry, out) 624 625 case *fuseops.MkNodeOp: 626 size := int(fusekernel.EntryOutSize(c.protocol)) 627 out := (*fusekernel.EntryOut)(m.Grow(size)) 628 convertChildInodeEntry(&o.Entry, out) 629 630 case *fuseops.CreateFileOp: 631 eSize := int(fusekernel.EntryOutSize(c.protocol)) 632 633 e := (*fusekernel.EntryOut)(m.Grow(eSize)) 634 convertChildInodeEntry(&o.Entry, e) 635 636 oo := (*fusekernel.OpenOut)(m.Grow(int(unsafe.Sizeof(fusekernel.OpenOut{})))) 637 oo.Fh = uint64(o.Handle) 638 639 case *fuseops.CreateSymlinkOp: 640 size := int(fusekernel.EntryOutSize(c.protocol)) 641 out := (*fusekernel.EntryOut)(m.Grow(size)) 642 convertChildInodeEntry(&o.Entry, out) 643 644 case *fuseops.CreateLinkOp: 645 size := int(fusekernel.EntryOutSize(c.protocol)) 646 out := (*fusekernel.EntryOut)(m.Grow(size)) 647 convertChildInodeEntry(&o.Entry, out) 648 649 case *fuseops.RenameOp: 650 // Empty response 651 652 case *fuseops.RmDirOp: 653 // Empty response 654 655 case *fuseops.UnlinkOp: 656 // Empty response 657 658 case *fuseops.OpenDirOp: 659 out := (*fusekernel.OpenOut)(m.Grow(int(unsafe.Sizeof(fusekernel.OpenOut{})))) 660 out.Fh = uint64(o.Handle) 661 662 case *fuseops.ReadDirOp: 663 // convertInMessage already set up the destination buffer to be at the end 664 // of the out message. We need only shrink to the right size based on how 665 // much the user read. 666 m.ShrinkTo(buffer.OutMessageHeaderSize + o.BytesRead) 667 668 case *fuseops.ReleaseDirHandleOp: 669 // Empty response 670 671 case *fuseops.OpenFileOp: 672 out := (*fusekernel.OpenOut)(m.Grow(int(unsafe.Sizeof(fusekernel.OpenOut{})))) 673 out.Fh = uint64(o.Handle) 674 675 if o.KeepPageCache { 676 out.OpenFlags |= uint32(fusekernel.OpenKeepCache) 677 } 678 679 if o.UseDirectIO { 680 out.OpenFlags |= uint32(fusekernel.OpenDirectIO) 681 } 682 683 case *fuseops.ReadFileOp: 684 // convertInMessage already set up the destination buffer to be at the end 685 // of the out message. We need only shrink to the right size based on how 686 // much the user read. 687 m.ShrinkTo(buffer.OutMessageHeaderSize + o.BytesRead) 688 689 case *fuseops.WriteFileOp: 690 out := (*fusekernel.WriteOut)(m.Grow(int(unsafe.Sizeof(fusekernel.WriteOut{})))) 691 out.Size = uint32(len(o.Data)) 692 693 case *fuseops.SyncFileOp: 694 // Empty response 695 696 case *fuseops.FlushFileOp: 697 // Empty response 698 699 case *fuseops.ReleaseFileHandleOp: 700 // Empty response 701 702 case *fuseops.ReadSymlinkOp: 703 m.AppendString(o.Target) 704 705 case *fuseops.StatFSOp: 706 out := (*fusekernel.StatfsOut)(m.Grow(int(unsafe.Sizeof(fusekernel.StatfsOut{})))) 707 out.St.Blocks = o.Blocks 708 out.St.Bfree = o.BlocksFree 709 out.St.Bavail = o.BlocksAvailable 710 out.St.Files = o.Inodes 711 out.St.Ffree = o.InodesFree 712 713 // The posix spec for sys/statvfs.h (http://goo.gl/LktgrF) defines the 714 // following fields of statvfs, among others: 715 // 716 // f_bsize File system block size. 717 // f_frsize Fundamental file system block size. 718 // f_blocks Total number of blocks on file system in units of f_frsize. 719 // 720 // It appears as though f_bsize was the only thing supported by most unixes 721 // originally, but then f_frsize was added when new sorts of file systems 722 // came about. Quoth The Linux Programming Interface by Michael Kerrisk 723 // (https://goo.gl/5LZMxQ): 724 // 725 // For most Linux file systems, the values of f_bsize and f_frsize are 726 // the same. However, some file systems support the notion of block 727 // fragments, which can be used to allocate a smaller unit of storage 728 // at the end of the file if if a full block is not required. This 729 // avoids the waste of space that would otherwise occur if a full block 730 // was allocated. On such file systems, f_frsize is the size of a 731 // fragment, and f_bsize is the size of a whole block. (The notion of 732 // fragments in UNIX file systems first appeared in the early 1980s 733 // with the 4.2BSD Fast File System.) 734 // 735 // Confusingly, it appears as though osxfuse surfaces fuse_kstatfs::bsize 736 // as statfs::f_iosize (of advisory use only), and fuse_kstatfs::frsize as 737 // statfs::f_bsize (which affects free space display in the Finder). 738 out.St.Bsize = o.IoSize 739 out.St.Frsize = o.BlockSize 740 741 case *fuseops.RemoveXattrOp: 742 // Empty response 743 744 case *fuseops.GetXattrOp: 745 // convertInMessage already set up the destination buffer to be at the end 746 // of the out message. We need only shrink to the right size based on how 747 // much the user read. 748 if len(o.Dst) == 0 { 749 writeXattrSize(m, uint32(o.BytesRead)) 750 } else { 751 m.ShrinkTo(buffer.OutMessageHeaderSize + o.BytesRead) 752 } 753 754 case *fuseops.ListXattrOp: 755 if len(o.Dst) == 0 { 756 writeXattrSize(m, uint32(o.BytesRead)) 757 } else { 758 m.ShrinkTo(buffer.OutMessageHeaderSize + o.BytesRead) 759 } 760 761 case *fuseops.SetXattrOp: 762 // Empty response 763 764 case *fuseops.FallocateOp: 765 // Empty response 766 767 case *initOp: 768 out := (*fusekernel.InitOut)(m.Grow(int(unsafe.Sizeof(fusekernel.InitOut{})))) 769 770 out.Major = o.Library.Major 771 out.Minor = o.Library.Minor 772 out.MaxReadahead = o.MaxReadahead 773 out.Flags = uint32(o.Flags) 774 // Default values 775 out.MaxBackground = 12 776 out.CongestionThreshold = 9 777 out.MaxWrite = o.MaxWrite 778 out.TimeGran = 1 779 out.MaxPages = o.MaxPages 780 781 default: 782 panic(fmt.Sprintf("Unexpected op: %#v", op)) 783 } 784 785 return 786 } 787 788 //////////////////////////////////////////////////////////////////////// 789 // General conversions 790 //////////////////////////////////////////////////////////////////////// 791 792 func convertTime(t time.Time) (secs uint64, nsec uint32) { 793 totalNano := t.UnixNano() 794 secs = uint64(totalNano / 1e9) 795 nsec = uint32(totalNano % 1e9) 796 return secs, nsec 797 } 798 799 func convertAttributes( 800 inodeID fuseops.InodeID, 801 in *fuseops.InodeAttributes, 802 out *fusekernel.Attr) { 803 out.Ino = uint64(inodeID) 804 out.Size = in.Size 805 out.Atime, out.AtimeNsec = convertTime(in.Atime) 806 out.Mtime, out.MtimeNsec = convertTime(in.Mtime) 807 out.Ctime, out.CtimeNsec = convertTime(in.Ctime) 808 out.SetCrtime(convertTime(in.Crtime)) 809 out.Nlink = in.Nlink 810 out.Uid = in.Uid 811 out.Gid = in.Gid 812 // round up to the nearest 512 boundary 813 out.Blocks = (in.Size + 512 - 1) / 512 814 815 // Set the mode. 816 out.Mode = uint32(in.Mode) & 0777 817 switch { 818 default: 819 out.Mode |= syscall.S_IFREG 820 case in.Mode&os.ModeDir != 0: 821 out.Mode |= syscall.S_IFDIR 822 case in.Mode&os.ModeDevice != 0: 823 if in.Mode&os.ModeCharDevice != 0 { 824 out.Mode |= syscall.S_IFCHR 825 } else { 826 out.Mode |= syscall.S_IFBLK 827 } 828 case in.Mode&os.ModeNamedPipe != 0: 829 out.Mode |= syscall.S_IFIFO 830 case in.Mode&os.ModeSymlink != 0: 831 out.Mode |= syscall.S_IFLNK 832 case in.Mode&os.ModeSocket != 0: 833 out.Mode |= syscall.S_IFSOCK 834 } 835 if in.Mode&os.ModeSetuid != 0 { 836 out.Mode |= syscall.S_ISUID 837 } 838 } 839 840 // Convert an absolute cache expiration time to a relative time from now for 841 // consumption by the fuse kernel module. 842 func convertExpirationTime(t time.Time) (secs uint64, nsecs uint32) { 843 // Fuse represents durations as unsigned 64-bit counts of seconds and 32-bit 844 // counts of nanoseconds (cf. http://goo.gl/EJupJV). So negative durations 845 // are right out. There is no need to cap the positive magnitude, because 846 // 2^64 seconds is well longer than the 2^63 ns range of time.Duration. 847 d := t.Sub(time.Now()) 848 if d > 0 { 849 secs = uint64(d / time.Second) 850 nsecs = uint32((d % time.Second) / time.Nanosecond) 851 } 852 853 return secs, nsecs 854 } 855 856 func convertChildInodeEntry( 857 in *fuseops.ChildInodeEntry, 858 out *fusekernel.EntryOut) { 859 out.Nodeid = uint64(in.Child) 860 out.Generation = uint64(in.Generation) 861 out.EntryValid, out.EntryValidNsec = convertExpirationTime(in.EntryExpiration) 862 out.AttrValid, out.AttrValidNsec = convertExpirationTime(in.AttributesExpiration) 863 864 convertAttributes(in.Child, &in.Attributes, &out.Attr) 865 } 866 867 func convertFileMode(unixMode uint32) os.FileMode { 868 mode := os.FileMode(unixMode & 0777) 869 switch unixMode & syscall.S_IFMT { 870 case syscall.S_IFREG: 871 // nothing 872 case syscall.S_IFDIR: 873 mode |= os.ModeDir 874 case syscall.S_IFCHR: 875 mode |= os.ModeCharDevice | os.ModeDevice 876 case syscall.S_IFBLK: 877 mode |= os.ModeDevice 878 case syscall.S_IFIFO: 879 mode |= os.ModeNamedPipe 880 case syscall.S_IFLNK: 881 mode |= os.ModeSymlink 882 case syscall.S_IFSOCK: 883 mode |= os.ModeSocket 884 default: 885 // no idea 886 mode |= os.ModeDevice 887 } 888 if unixMode&syscall.S_ISUID != 0 { 889 mode |= os.ModeSetuid 890 } 891 if unixMode&syscall.S_ISGID != 0 { 892 mode |= os.ModeSetgid 893 } 894 return mode 895 } 896 897 func writeXattrSize(m *buffer.OutMessage, size uint32) { 898 out := (*fusekernel.GetxattrOut)(m.Grow(int(unsafe.Sizeof(fusekernel.GetxattrOut{})))) 899 out.Size = size 900 }