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 }