github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/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/bazil.org/fuse"
    31  	"camlistore.org/third_party/bazil.org/fuse/fs"
    32  )
    33  
    34  // xattrPrefix is the permanode attribute prefix used for record
    35  // extended attributes.
    36  const xattrPrefix = "xattr:"
    37  
    38  // xattr provides common support for extended attributes for various
    39  // file and directory implementations (fuse.Node) within the FUSE services.
    40  type xattr struct {
    41  	typeName  string // for logging
    42  	fs        *CamliFileSystem
    43  	permanode blob.Ref
    44  
    45  	// mu guards xattrs.  Both mu and the xattrs map are provided by the
    46  	// fuse.Node when the struct is created.
    47  	mu *sync.Mutex
    48  
    49  	// This is a pointer to the particular fuse.Node's location of its
    50  	// xattr map so that it can be initialized commonly when the fuse.Node
    51  	// calls xattr.load(*search.DescribedPermanode)
    52  	xattrs *map[string][]byte
    53  }
    54  
    55  // load is invoked after the creation of a fuse.Node that may contain extended
    56  // attributes.  This creates the node's xattr map as well as fills it with any
    57  // extended attributes found in the permanode's claims.
    58  func (x *xattr) load(p *search.DescribedPermanode) {
    59  	x.mu.Lock()
    60  	defer x.mu.Unlock()
    61  
    62  	*x.xattrs = map[string][]byte{}
    63  	for k, v := range p.Attr {
    64  		if strings.HasPrefix(k, xattrPrefix) {
    65  			name := k[len(xattrPrefix):]
    66  			val, err := base64.StdEncoding.DecodeString(v[0])
    67  			if err != nil {
    68  				log.Printf("Base64 decoding error on attribute %v: %v", name, err)
    69  				continue
    70  			}
    71  			(*x.xattrs)[name] = val
    72  		}
    73  	}
    74  }
    75  
    76  func (x *xattr) set(req *fuse.SetxattrRequest) fuse.Error {
    77  	log.Printf("%s.setxattr(%q) -> %q", x.typeName, req.Name, req.Xattr)
    78  
    79  	claim := schema.NewSetAttributeClaim(x.permanode, xattrPrefix+req.Name,
    80  		base64.StdEncoding.EncodeToString(req.Xattr))
    81  	_, err := x.fs.client.UploadAndSignBlob(claim)
    82  	if err != nil {
    83  		log.Printf("Error setting xattr: %v", err)
    84  		return fuse.EIO
    85  	}
    86  
    87  	x.mu.Lock()
    88  	(*x.xattrs)[req.Name] = req.Xattr
    89  	x.mu.Unlock()
    90  
    91  	return nil
    92  }
    93  
    94  func (x *xattr) remove(req *fuse.RemovexattrRequest) fuse.Error {
    95  	log.Printf("%s.Removexattr(%q)", x.typeName, req.Name)
    96  
    97  	claim := schema.NewDelAttributeClaim(x.permanode, xattrPrefix+req.Name, "")
    98  	_, err := x.fs.client.UploadAndSignBlob(claim)
    99  
   100  	if err != nil {
   101  		log.Printf("Error removing xattr: %v", err)
   102  		return fuse.EIO
   103  	}
   104  
   105  	x.mu.Lock()
   106  	delete(*x.xattrs, req.Name)
   107  	x.mu.Unlock()
   108  
   109  	return nil
   110  }
   111  
   112  func (x *xattr) get(req *fuse.GetxattrRequest, res *fuse.GetxattrResponse) fuse.Error {
   113  	x.mu.Lock()
   114  	defer x.mu.Unlock()
   115  
   116  	val, found := (*x.xattrs)[req.Name]
   117  
   118  	if !found {
   119  		return fuse.ENODATA
   120  	}
   121  
   122  	res.Xattr = val
   123  
   124  	return nil
   125  }
   126  
   127  func (x *xattr) list(req *fuse.ListxattrRequest, res *fuse.ListxattrResponse) fuse.Error {
   128  	x.mu.Lock()
   129  	defer x.mu.Unlock()
   130  
   131  	for k := range *x.xattrs {
   132  		res.Xattr = append(res.Xattr, k...)
   133  		res.Xattr = append(res.Xattr, '\x00')
   134  	}
   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, fs.Intr) fuse.Error {
   147  	return fuse.ENODATA
   148  }
   149  
   150  func (n noXattr) Listxattr(*fuse.ListxattrRequest, *fuse.ListxattrResponse, fs.Intr) fuse.Error {
   151  	return nil
   152  }
   153  
   154  func (n noXattr) Setxattr(*fuse.SetxattrRequest, fs.Intr) fuse.Error {
   155  	return fuse.EPERM
   156  }
   157  
   158  func (n noXattr) Removexattr(*fuse.RemovexattrRequest, fs.Intr) fuse.Error {
   159  	return fuse.EPERM
   160  }