// createHeader creates a new random header and writes it to disk. // Returns the new file ID. // The caller must hold fileIDLock.Lock(). func (f *file) createHeader() (fileID []byte, err error) { h := contentenc.RandomHeader() buf := h.Pack() // Prevent partially written (=corrupt) header by preallocating the space beforehand if !f.fs.args.NoPrealloc { err = syscallcompat.EnospcPrealloc(int(f.fd.Fd()), 0, contentenc.HeaderLen) if err != nil { tlog.Warn.Printf("ino%d: createHeader: prealloc failed: %s\n", f.devIno.ino, err.Error()) return nil, err } } // Actually write header _, err = f.fd.WriteAt(buf, 0) if err != nil { return nil, err } return h.ID, err }
// doWrite - encrypt "data" and write it to plaintext offset "off" // // Arguments do not have to be block-aligned, read-modify-write is // performed internally as necessary // // Called by Write() for normal writing, // and by Truncate() to rewrite the last file block. // // Empty writes do nothing and are allowed. func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) { // Read header from disk, create a new one if the file is empty f.fileTableEntry.IDLock.RLock() if f.fileTableEntry.ID == nil { f.fileTableEntry.IDLock.RUnlock() // Somebody else may write the header here, but this would do no harm. f.fileTableEntry.IDLock.Lock() tmpID, err := f.readFileID() if err == io.EOF { tmpID, err = f.createHeader() } if err != nil { f.fileTableEntry.IDLock.Unlock() return 0, fuse.ToStatus(err) } f.fileTableEntry.ID = tmpID f.fileTableEntry.IDLock.Unlock() // The file ID may change in here. This does no harm because we // re-read it after the RLock(). f.fileTableEntry.IDLock.RLock() } fileID := f.fileTableEntry.ID defer f.fileTableEntry.IDLock.RUnlock() // Handle payload data status := fuse.OK dataBuf := bytes.NewBuffer(data) blocks := f.contentEnc.ExplodePlainRange(uint64(off), uint64(len(data))) writeChain := make([][]byte, len(blocks)) var numOutBytes int for i, b := range blocks { blockData := dataBuf.Next(int(b.Length)) // Incomplete block -> Read-Modify-Write if b.IsPartial() { // Read o := b.BlockPlainOff() var oldData []byte oldData, status = f.doRead(o, f.contentEnc.PlainBS()) if status != fuse.OK { tlog.Warn.Printf("ino%d fh%d: RMW read failed: %s", f.devIno.ino, f.intFd(), status.String()) return 0, status } // Modify blockData = f.contentEnc.MergeBlocks(oldData, blockData, int(b.Skip)) tlog.Debug.Printf("len(oldData)=%d len(blockData)=%d", len(oldData), len(blockData)) } // Encrypt blockData = f.contentEnc.EncryptBlock(blockData, b.BlockNo, fileID) tlog.Debug.Printf("ino%d: Writing %d bytes to block #%d", f.devIno.ino, uint64(len(blockData))-f.contentEnc.BlockOverhead(), b.BlockNo) // Store output data in the writeChain writeChain[i] = blockData numOutBytes += len(blockData) } // Concatenenate all elements in the writeChain into one contiguous buffer tmp := make([]byte, numOutBytes) writeBuf := bytes.NewBuffer(tmp[:0]) for _, w := range writeChain { writeBuf.Write(w) } // Preallocate so we cannot run out of space in the middle of the write. // This prevents partially written (=corrupt) blocks. var err error cOff := blocks[0].BlockCipherOff() if !f.fs.args.NoPrealloc { err = syscallcompat.EnospcPrealloc(int(f.fd.Fd()), int64(cOff), int64(writeBuf.Len())) if err != nil { tlog.Warn.Printf("ino%d fh%d: doWrite: prealloc failed: %s", f.devIno.ino, f.intFd(), err.Error()) return 0, fuse.ToStatus(err) } } // Write _, err = f.fd.WriteAt(writeBuf.Bytes(), int64(cOff)) if err != nil { tlog.Warn.Printf("doWrite: Write failed: %s", err.Error()) return 0, fuse.ToStatus(err) } return uint32(len(data)), fuse.OK }