func CanOpen(mode FileMode) bool {
	return mode.IsRegular() || mode.IsDir() || mode.IsNamedPipe() || mode.IsBlockDevice() || mode.IsCharacterDevice()
}
CanOpen returns whether this file open can be opened, read and written to.

This includes everything except symlinks and sockets.
package p9
import (
	"fmt"
	"io"
	"path"
	"strings"
	"sync/atomic"
	"github.com/hugelgupf/p9/internal"
	"github.com/hugelgupf/p9/internal/linux"
)
func newErr(err error) *rlerror {
	return &rlerror{Error: uint32(internal.ExtractErrno(err))}
}
type handler interface {
	handle(cs *connState) message
}
func (t *tversion) handle(cs *connState) message {
	unknown := &rversion{
		MSize:   0,
		Version: "unknown",
	}
	if t.MSize == 0 {
		return unknown
	}
	msize := t.MSize
	if t.MSize > maximumLength {
		msize = maximumLength
	}
	reqBaseVersion, reqVersion, ok := parseVersion(t.Version)
	if !ok {
		return unknown
	}
	var baseVersion baseVersion
	var version uint32
	switch reqBaseVersion {
	case version9P2000, version9P2000U:
		return unknown
	case version9P2000L:
		baseVersion = reqBaseVersion
		if reqVersion > highestSupportedVersion {
			version = highestSupportedVersion
		} else {
			version = reqVersion
		}
	}
	atomic.StoreUint32(&cs.messageSize, msize)
	atomic.StoreUint32(&cs.version, version)
	cs.baseVersion = baseVersion
	return &rversion{
		MSize:   msize,
		Version: versionString(baseVersion, version),
	}
}
func (t *tflush) handle(cs *connState) message {
	cs.WaitTag(t.OldTag)
	return &rflush{}
}
func checkSafeName(name string) error {
	if name != "" && !strings.Contains(name, "/") && name != "." && name != ".." {
		return nil
	}
	return linux.EINVAL
}
func (t *tclunk) handle(cs *connState) message {
	if !cs.DeleteFID(t.fid) {
		return newErr(linux.EBADF)
	}
	return &rclunk{}
}
func (t *tremove) handle(cs *connState) message {
	ref, ok := cs.LookupFID(t.fid)
	if !ok {
		return newErr(linux.EBADF)
	}
	defer ref.DecRef()
	err := ref.safelyGlobal(func() error {
		if ref.isRoot() {
			return linux.EINVAL
		}
		if ref.isDeleted() {
			return linux.EINVAL
		}
		name := ref.parent.pathNode.nameFor(ref)
		if err := ref.parent.file.UnlinkAt(name, 0); err != nil {
			return err
		}
		ref.parent.markChildDeleted(name)
		return nil
	})
	if !cs.DeleteFID(t.fid) {
		return newErr(linux.EBADF)
	}
	if err != nil {
		return newErr(err)
	}
	return &rremove{}
}
func (t *tauth) handle(cs *connState) message {
	return newErr(linux.ENOSYS)
}
func (t *tattach) handle(cs *connState) message {
	if t.Auth.Authenticationfid != noFID {
		return newErr(linux.EINVAL)
	}
	if path.IsAbs(t.Auth.AttachName) {
		t.Auth.AttachName = t.Auth.AttachName[1:]
	}
	sf, err := cs.server.attacher.Attach()
	if err != nil {
		return newErr(err)
	}
	qid, valid, attr, err := sf.GetAttr(AttrMaskAll)
	if err != nil {
		sf.Close()  
		return newErr(err)
	}
	if !valid.Mode {
		sf.Close()  
		return newErr(linux.EINVAL)
	}
	root := &fidRef{
		server:   cs.server,
		parent:   nil,
		file:     sf,
		refs:     1,
		mode:     attr.Mode.FileType(),
		pathNode: cs.server.pathTree,
	}
	defer root.DecRef()
	if len(t.Auth.AttachName) == 0 {
		cs.InsertFID(t.fid, root)
		return &rattach{QID: qid}
	}
	names := strings.Split(t.Auth.AttachName, "/")
	_, newRef, _, _, err := doWalk(cs, root, names, false)
	if err != nil {
		return newErr(err)
	}
	defer newRef.DecRef()
	cs.InsertFID(t.fid, newRef)
	return &rattach{QID: qid}
}