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 }