| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384 | 
							- // Copyright 2015 Google Inc. All Rights Reserved.
 
- //
 
- // Licensed under the Apache License, Version 2.0 (the "License");
 
- // you may not use this file except in compliance with the License.
 
- // You may obtain a copy of the License at
 
- //
 
- //     http://www.apache.org/licenses/LICENSE-2.0
 
- //
 
- // Unless required by applicable law or agreed to in writing, software
 
- // distributed under the License is distributed on an "AS IS" BASIS,
 
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
- // See the License for the specific language governing permissions and
 
- // limitations under the License.
 
- package memfs
 
- import (
 
- 	"fmt"
 
- 	"io"
 
- 	"os"
 
- 	"time"
 
- 	"github.com/jacobsa/fuse/fuseops"
 
- 	"github.com/jacobsa/fuse/fuseutil"
 
- )
 
- // Common attributes for files and directories.
 
- //
 
- // External synchronization is required.
 
- type inode struct {
 
- 	/////////////////////////
 
- 	// Mutable state
 
- 	/////////////////////////
 
- 	// The current attributes of this inode.
 
- 	//
 
- 	// INVARIANT: attrs.Mode &^ (os.ModePerm|os.ModeDir|os.ModeSymlink) == 0
 
- 	// INVARIANT: !(isDir() && isSymlink())
 
- 	// INVARIANT: attrs.Size == len(contents)
 
- 	attrs fuseops.InodeAttributes
 
- 	// For directories, entries describing the children of the directory. Unused
 
- 	// entries are of type DT_Unknown.
 
- 	//
 
- 	// This array can never be shortened, nor can its elements be moved, because
 
- 	// we use its indices for Dirent.Offset, which is exposed to the user who
 
- 	// might be calling readdir in a loop while concurrently modifying the
 
- 	// directory. Unused entries can, however, be reused.
 
- 	//
 
- 	// INVARIANT: If !isDir(), len(entries) == 0
 
- 	// INVARIANT: For each i, entries[i].Offset == i+1
 
- 	// INVARIANT: Contains no duplicate names in used entries.
 
- 	entries []fuseutil.Dirent
 
- 	// For files, the current contents of the file.
 
- 	//
 
- 	// INVARIANT: If !isFile(), len(contents) == 0
 
- 	contents []byte
 
- 	// For symlinks, the target of the symlink.
 
- 	//
 
- 	// INVARIANT: If !isSymlink(), len(target) == 0
 
- 	target string
 
- 	// extended attributes and values
 
- 	xattrs map[string][]byte
 
- }
 
- ////////////////////////////////////////////////////////////////////////
 
- // Helpers
 
- ////////////////////////////////////////////////////////////////////////
 
- // Create a new inode with the supplied attributes, which need not contain
 
- // time-related information (the inode object will take care of that).
 
- func newInode(
 
- 	attrs fuseops.InodeAttributes) (in *inode) {
 
- 	// Update time info.
 
- 	now := time.Now()
 
- 	attrs.Mtime = now
 
- 	attrs.Crtime = now
 
- 	// Create the object.
 
- 	in = &inode{
 
- 		attrs:  attrs,
 
- 		xattrs: make(map[string][]byte),
 
- 	}
 
- 	return
 
- }
 
- func (in *inode) CheckInvariants() {
 
- 	// INVARIANT: attrs.Mode &^ (os.ModePerm|os.ModeDir|os.ModeSymlink) == 0
 
- 	if !(in.attrs.Mode&^(os.ModePerm|os.ModeDir|os.ModeSymlink) == 0) {
 
- 		panic(fmt.Sprintf("Unexpected mode: %v", in.attrs.Mode))
 
- 	}
 
- 	// INVARIANT: !(isDir() && isSymlink())
 
- 	if in.isDir() && in.isSymlink() {
 
- 		panic(fmt.Sprintf("Unexpected mode: %v", in.attrs.Mode))
 
- 	}
 
- 	// INVARIANT: attrs.Size == len(contents)
 
- 	if in.attrs.Size != uint64(len(in.contents)) {
 
- 		panic(fmt.Sprintf(
 
- 			"Size mismatch: %d vs. %d",
 
- 			in.attrs.Size,
 
- 			len(in.contents)))
 
- 	}
 
- 	// INVARIANT: If !isDir(), len(entries) == 0
 
- 	if !in.isDir() && len(in.entries) != 0 {
 
- 		panic(fmt.Sprintf("Unexpected entries length: %d", len(in.entries)))
 
- 	}
 
- 	// INVARIANT: For each i, entries[i].Offset == i+1
 
- 	for i, e := range in.entries {
 
- 		if !(e.Offset == fuseops.DirOffset(i+1)) {
 
- 			panic(fmt.Sprintf("Unexpected offset for index %d: %d", i, e.Offset))
 
- 		}
 
- 	}
 
- 	// INVARIANT: Contains no duplicate names in used entries.
 
- 	childNames := make(map[string]struct{})
 
- 	for _, e := range in.entries {
 
- 		if e.Type != fuseutil.DT_Unknown {
 
- 			if _, ok := childNames[e.Name]; ok {
 
- 				panic(fmt.Sprintf("Duplicate name: %s", e.Name))
 
- 			}
 
- 			childNames[e.Name] = struct{}{}
 
- 		}
 
- 	}
 
- 	// INVARIANT: If !isFile(), len(contents) == 0
 
- 	if !in.isFile() && len(in.contents) != 0 {
 
- 		panic(fmt.Sprintf("Unexpected length: %d", len(in.contents)))
 
- 	}
 
- 	// INVARIANT: If !isSymlink(), len(target) == 0
 
- 	if !in.isSymlink() && len(in.target) != 0 {
 
- 		panic(fmt.Sprintf("Unexpected target length: %d", len(in.target)))
 
- 	}
 
- 	return
 
- }
 
- func (in *inode) isDir() bool {
 
- 	return in.attrs.Mode&os.ModeDir != 0
 
- }
 
- func (in *inode) isSymlink() bool {
 
- 	return in.attrs.Mode&os.ModeSymlink != 0
 
- }
 
- func (in *inode) isFile() bool {
 
- 	return !(in.isDir() || in.isSymlink())
 
- }
 
- // Return the index of the child within in.entries, if it exists.
 
- //
 
- // REQUIRES: in.isDir()
 
- func (in *inode) findChild(name string) (i int, ok bool) {
 
- 	if !in.isDir() {
 
- 		panic("findChild called on non-directory.")
 
- 	}
 
- 	var e fuseutil.Dirent
 
- 	for i, e = range in.entries {
 
- 		if e.Name == name {
 
- 			ok = true
 
- 			return
 
- 		}
 
- 	}
 
- 	return
 
- }
 
- ////////////////////////////////////////////////////////////////////////
 
- // Public methods
 
- ////////////////////////////////////////////////////////////////////////
 
- // Return the number of children of the directory.
 
- //
 
- // REQUIRES: in.isDir()
 
- func (in *inode) Len() (n int) {
 
- 	for _, e := range in.entries {
 
- 		if e.Type != fuseutil.DT_Unknown {
 
- 			n++
 
- 		}
 
- 	}
 
- 	return
 
- }
 
- // Find an entry for the given child name and return its inode ID.
 
- //
 
- // REQUIRES: in.isDir()
 
- func (in *inode) LookUpChild(name string) (
 
- 	id fuseops.InodeID,
 
- 	typ fuseutil.DirentType,
 
- 	ok bool) {
 
- 	index, ok := in.findChild(name)
 
- 	if ok {
 
- 		id = in.entries[index].Inode
 
- 		typ = in.entries[index].Type
 
- 	}
 
- 	return
 
- }
 
- // Add an entry for a child.
 
- //
 
- // REQUIRES: in.isDir()
 
- // REQUIRES: dt != fuseutil.DT_Unknown
 
- func (in *inode) AddChild(
 
- 	id fuseops.InodeID,
 
- 	name string,
 
- 	dt fuseutil.DirentType) {
 
- 	var index int
 
- 	// Update the modification time.
 
- 	in.attrs.Mtime = time.Now()
 
- 	// No matter where we place the entry, make sure it has the correct Offset
 
- 	// field.
 
- 	defer func() {
 
- 		in.entries[index].Offset = fuseops.DirOffset(index + 1)
 
- 	}()
 
- 	// Set up the entry.
 
- 	e := fuseutil.Dirent{
 
- 		Inode: id,
 
- 		Name:  name,
 
- 		Type:  dt,
 
- 	}
 
- 	// Look for a gap in which we can insert it.
 
- 	for index = range in.entries {
 
- 		if in.entries[index].Type == fuseutil.DT_Unknown {
 
- 			in.entries[index] = e
 
- 			return
 
- 		}
 
- 	}
 
- 	// Append it to the end.
 
- 	index = len(in.entries)
 
- 	in.entries = append(in.entries, e)
 
- }
 
- // Remove an entry for a child.
 
- //
 
- // REQUIRES: in.isDir()
 
- // REQUIRES: An entry for the given name exists.
 
- func (in *inode) RemoveChild(name string) {
 
- 	// Update the modification time.
 
- 	in.attrs.Mtime = time.Now()
 
- 	// Find the entry.
 
- 	i, ok := in.findChild(name)
 
- 	if !ok {
 
- 		panic(fmt.Sprintf("Unknown child: %s", name))
 
- 	}
 
- 	// Mark it as unused.
 
- 	in.entries[i] = fuseutil.Dirent{
 
- 		Type:   fuseutil.DT_Unknown,
 
- 		Offset: fuseops.DirOffset(i + 1),
 
- 	}
 
- }
 
- // Serve a ReadDir request.
 
- //
 
- // REQUIRES: in.isDir()
 
- func (in *inode) ReadDir(p []byte, offset int) (n int) {
 
- 	if !in.isDir() {
 
- 		panic("ReadDir called on non-directory.")
 
- 	}
 
- 	for i := offset; i < len(in.entries); i++ {
 
- 		e := in.entries[i]
 
- 		// Skip unused entries.
 
- 		if e.Type == fuseutil.DT_Unknown {
 
- 			continue
 
- 		}
 
- 		tmp := fuseutil.WriteDirent(p[n:], in.entries[i])
 
- 		if tmp == 0 {
 
- 			break
 
- 		}
 
- 		n += tmp
 
- 	}
 
- 	return
 
- }
 
- // Read from the file's contents. See documentation for ioutil.ReaderAt.
 
- //
 
- // REQUIRES: in.isFile()
 
- func (in *inode) ReadAt(p []byte, off int64) (n int, err error) {
 
- 	if !in.isFile() {
 
- 		panic("ReadAt called on non-file.")
 
- 	}
 
- 	// Ensure the offset is in range.
 
- 	if off > int64(len(in.contents)) {
 
- 		err = io.EOF
 
- 		return
 
- 	}
 
- 	// Read what we can.
 
- 	n = copy(p, in.contents[off:])
 
- 	if n < len(p) {
 
- 		err = io.EOF
 
- 	}
 
- 	return
 
- }
 
- // Write to the file's contents. See documentation for ioutil.WriterAt.
 
- //
 
- // REQUIRES: in.isFile()
 
- func (in *inode) WriteAt(p []byte, off int64) (n int, err error) {
 
- 	if !in.isFile() {
 
- 		panic("WriteAt called on non-file.")
 
- 	}
 
- 	// Update the modification time.
 
- 	in.attrs.Mtime = time.Now()
 
- 	// Ensure that the contents slice is long enough.
 
- 	newLen := int(off) + len(p)
 
- 	if len(in.contents) < newLen {
 
- 		padding := make([]byte, newLen-len(in.contents))
 
- 		in.contents = append(in.contents, padding...)
 
- 		in.attrs.Size = uint64(newLen)
 
- 	}
 
- 	// Copy in the data.
 
- 	n = copy(in.contents[off:], p)
 
- 	// Sanity check.
 
- 	if n != len(p) {
 
- 		panic(fmt.Sprintf("Unexpected short copy: %v", n))
 
- 	}
 
- 	return
 
- }
 
- // Update attributes from non-nil parameters.
 
- func (in *inode) SetAttributes(
 
- 	size *uint64,
 
- 	mode *os.FileMode,
 
- 	mtime *time.Time) {
 
- 	// Update the modification time.
 
- 	in.attrs.Mtime = time.Now()
 
- 	// Truncate?
 
- 	if size != nil {
 
- 		intSize := int(*size)
 
- 		// Update contents.
 
- 		if intSize <= len(in.contents) {
 
- 			in.contents = in.contents[:intSize]
 
- 		} else {
 
- 			padding := make([]byte, intSize-len(in.contents))
 
- 			in.contents = append(in.contents, padding...)
 
- 		}
 
- 		// Update attributes.
 
- 		in.attrs.Size = *size
 
- 	}
 
- 	// Change mode?
 
- 	if mode != nil {
 
- 		in.attrs.Mode = *mode
 
- 	}
 
- 	// Change mtime?
 
- 	if mtime != nil {
 
- 		in.attrs.Mtime = *mtime
 
- 	}
 
- }
 
 
  |