Example #1
0
// 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
}
Example #2
0
// 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
}