github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/pkg/fs/xattr.go (about)

     1  // +build linux darwin
     2  
     3  /*
     4  Copyright 2013 The Camlistore Authors
     5  
     6  Licensed under the Apache License, Version 2.0 (the "License");
     7  you may not use this file except in compliance with the License.
     8  You may obtain a copy of the License at
     9  
    10       http://www.apache.org/licenses/LICENSE-2.0
    11  
    12  Unless required by applicable law or agreed to in writing, software
    13  distributed under the License is distributed on an "AS IS" BASIS,
    14  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15  See the License for the specific language governing permissions and
    16  limitations under the License.
    17  */
    18  
    19  package fs
    20  
    21  import (
    22  	"encoding/base64"
    23  	"log"
    24  	"strings"
    25  	"sync"
    26  
    27  	"camlistore.org/pkg/blob"
    28  	"camlistore.org/pkg/schema"
    29  	"camlistore.org/pkg/search"
    30  	"camlistore.org/third_party/code.google.com/p/rsc/fuse"
    31  )
    32  
    33  // xattrPrefix is the permanode attribute prefix used for record
    34  // extended attributes.
    35  const xattrPrefix = "xattr:"
    36  
    37  // xattr provides common support for extended attributes for various
    38  // file and directory implementations (fuse.Node) within the FUSE services.
    39  type xattr struct {
    40  	typeName  string // for logging
    41  	fs        *CamliFileSystem
    42  	permanode blob.Ref
    43  
    44  	// mu guards xattrs.  Both mu and the xattrs map are provided by the
    45  	// fuse.Node when the struct is created.
    46  	mu *sync.Mutex
    47  
    48  	// This is a pointer to the particular fuse.Node's location of its
    49  	// xattr map so that it can be initialized commonly when the fuse.Node
    50  	// calls xattr.load(*search.DescribedPermanode)
    51  	xattrs *map[string][]byte
    52  }
    53  
    54  // load is invoked after the creation of a fuse.Node that may contain extended
    55  // attributes.  This creates the node's xattr map as well as fills it with any
    56  // extended attributes found in the permanode's claims.
    57  func (x *xattr) load(p *search.DescribedPermanode) {
    58  	x.mu.Lock()
    59  	defer x.mu.Unlock()
    60  
    61  	*x.xattrs = map[string][]byte{}
    62  	for k, v := range p.Attr {
    63  		if strings.HasPrefix(k, xattrPrefix) {
    64  			name := k[len(xattrPrefix):]
    65  			val, err := base64.StdEncoding.DecodeString(v[0])
    66  			if err != nil {
    67  				log.Printf("Base64 decoding error on attribute %v: %v", name, err)
    68  				continue
    69  			}
    70  			(*x.xattrs)[name] = val
    71  		}
    72  	}
    73  }
    74  
    75  func (x *xattr) set(req *fuse.SetxattrRequest) fuse.Error {
    76  	log.Printf("%s.setxattr(%q) -> %q", x.typeName, req.Name, req.Xattr)
    77  
    78  	claim := schema.NewSetAttributeClaim(x.permanode, xattrPrefix+req.Name,
    79  		base64.StdEncoding.EncodeToString(req.Xattr))
    80  	_, err := x.fs.client.UploadAndSignBlob(claim)
    81  	if err != nil {
    82  		log.Printf("Error setting xattr: %v", err)
    83  		return fuse.EIO
    84  	}
    85  
    86  	x.mu.Lock()
    87  	(*x.xattrs)[req.Name] = req.Xattr
    88  	x.mu.Unlock()
    89  
    90  	return nil
    91  }
    92  
    93  func (x *xattr) remove(req *fuse.RemovexattrRequest) fuse.Error {
    94  	log.Printf("%s.Removexattr(%q)", x.typeName, req.Name)
    95  
    96  	claim := schema.NewDelAttributeClaim(x.permanode, xattrPrefix+req.Name, "")
    97  	_, err := x.fs.client.UploadAndSignBlob(claim)
    98  
    99  	if err != nil {
   100  		log.Printf("Error removing xattr: %v", err)
   101  		return fuse.EIO
   102  	}
   103  
   104  	x.mu.Lock()
   105  	delete(*x.xattrs, req.Name)
   106  	x.mu.Unlock()
   107  
   108  	return nil
   109  }
   110  
   111  func (x *xattr) get(req *fuse.GetxattrRequest, res *fuse.GetxattrResponse) fuse.Error {
   112  	x.mu.Lock()
   113  	defer x.mu.Unlock()
   114  
   115  	val, found := (*x.xattrs)[req.Name]
   116  
   117  	if !found {
   118  		return fuse.ENOATTR
   119  	}
   120  
   121  	res.Xattr = val
   122  
   123  	return nil
   124  }
   125  
   126  func (x *xattr) list(req *fuse.ListxattrRequest, res *fuse.ListxattrResponse) fuse.Error {
   127  	x.mu.Lock()
   128  	defer x.mu.Unlock()
   129  
   130  	rv := make([]string, 0, len(*x.xattrs))
   131  	for k := range *x.xattrs {
   132  		rv = append(rv, k)
   133  	}
   134  	res.SetAttrNames(req, rv)
   135  	return nil
   136  }
   137  
   138  // noXattr provides default xattr methods for fuse nodes.  The fuse
   139  // package itself defaults to ENOSYS which causes some systems (read:
   140  // MacOSX) to assume that no extended attribute support is available
   141  // anywhere in the filesystem.  This different set of defaults just
   142  // returns no values for read requests and permission denied for write
   143  // requests.
   144  type noXattr struct{}
   145  
   146  func (n noXattr) Getxattr(*fuse.GetxattrRequest, *fuse.GetxattrResponse, fuse.Intr) fuse.Error {
   147  	return fuse.ENOATTR
   148  }
   149  
   150  func (n noXattr) Listxattr(*fuse.ListxattrRequest, *fuse.ListxattrResponse, fuse.Intr) fuse.Error {
   151  	return nil
   152  }
   153  
   154  func (n noXattr) Setxattr(*fuse.SetxattrRequest, fuse.Intr) fuse.Error {
   155  	return fuse.EPERM
   156  }
   157  
   158  func (n noXattr) Removexattr(*fuse.RemovexattrRequest, fuse.Intr) fuse.Error {
   159  	return fuse.EPERM
   160  }