github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/pkg/fs/ro.go (about) 1 // +build linux darwin 2 3 /* 4 Copyright 2013 Google Inc. 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 "errors" 23 "log" 24 "os" 25 "path/filepath" 26 "strings" 27 "sync" 28 "time" 29 30 "camlistore.org/pkg/blob" 31 "camlistore.org/pkg/schema" 32 "camlistore.org/pkg/search" 33 "camlistore.org/pkg/types" 34 35 "camlistore.org/third_party/bazil.org/fuse" 36 "camlistore.org/third_party/bazil.org/fuse/fs" 37 ) 38 39 // roDir is a read-only directory. 40 // Its permanode is the permanode with camliPath:entname attributes. 41 type roDir struct { 42 fs *CamliFileSystem 43 permanode blob.Ref 44 parent *roDir // or nil, if the root within its roots.go root. 45 name string // ent name (base name within parent) 46 at time.Time 47 48 mu sync.Mutex 49 children map[string]roFileOrDir 50 xattrs map[string][]byte 51 } 52 53 func newRODir(fs *CamliFileSystem, permanode blob.Ref, name string, at time.Time) *roDir { 54 return &roDir{ 55 fs: fs, 56 permanode: permanode, 57 name: name, 58 at: at, 59 } 60 } 61 62 // for debugging 63 func (n *roDir) fullPath() string { 64 if n == nil { 65 return "" 66 } 67 return filepath.Join(n.parent.fullPath(), n.name) 68 } 69 70 func (n *roDir) Attr() fuse.Attr { 71 return fuse.Attr{ 72 Inode: n.permanode.Sum64(), 73 Mode: os.ModeDir | 0500, 74 Uid: uint32(os.Getuid()), 75 Gid: uint32(os.Getgid()), 76 } 77 } 78 79 // populate hits the blobstore to populate map of child nodes. 80 func (n *roDir) populate() error { 81 n.mu.Lock() 82 defer n.mu.Unlock() 83 84 // Things never change here, so if we've ever populated, we're 85 // populated. 86 if n.children != nil { 87 return nil 88 } 89 90 log.Printf("roDir.populate(%q) - Sending request At %v", n.fullPath(), n.at) 91 92 res, err := n.fs.client.Describe(&search.DescribeRequest{ 93 BlobRef: n.permanode, 94 Depth: 3, 95 At: types.Time3339(n.at), 96 }) 97 if err != nil { 98 log.Println("roDir.paths:", err) 99 return nil 100 } 101 db := res.Meta[n.permanode.String()] 102 if db == nil { 103 return errors.New("dir blobref not described") 104 } 105 106 // Find all child permanodes and stick them in n.children 107 n.children = make(map[string]roFileOrDir) 108 for k, v := range db.Permanode.Attr { 109 const p = "camliPath:" 110 if !strings.HasPrefix(k, p) || len(v) < 1 { 111 continue 112 } 113 name := k[len(p):] 114 childRef := v[0] 115 child := res.Meta[childRef] 116 if child == nil { 117 log.Printf("child not described: %v", childRef) 118 continue 119 } 120 if target := child.Permanode.Attr.Get("camliSymlinkTarget"); target != "" { 121 // This is a symlink. 122 n.children[name] = &roFile{ 123 fs: n.fs, 124 permanode: blob.ParseOrZero(childRef), 125 parent: n, 126 name: name, 127 symLink: true, 128 target: target, 129 } 130 } else if isDir(child.Permanode) { 131 // This is a directory. 132 n.children[name] = &roDir{ 133 fs: n.fs, 134 permanode: blob.ParseOrZero(childRef), 135 parent: n, 136 name: name, 137 } 138 } else if contentRef := child.Permanode.Attr.Get("camliContent"); contentRef != "" { 139 // This is a file. 140 content := res.Meta[contentRef] 141 if content == nil { 142 log.Printf("child content not described: %v", childRef) 143 continue 144 } 145 if content.CamliType != "file" { 146 log.Printf("child not a file: %v", childRef) 147 continue 148 } 149 n.children[name] = &roFile{ 150 fs: n.fs, 151 permanode: blob.ParseOrZero(childRef), 152 parent: n, 153 name: name, 154 content: blob.ParseOrZero(contentRef), 155 size: content.File.Size, 156 } 157 } else { 158 // unknown type 159 continue 160 } 161 n.children[name].xattr().load(child.Permanode) 162 } 163 return nil 164 } 165 166 func (n *roDir) ReadDir(intr fs.Intr) ([]fuse.Dirent, fuse.Error) { 167 if err := n.populate(); err != nil { 168 log.Println("populate:", err) 169 return nil, fuse.EIO 170 } 171 n.mu.Lock() 172 defer n.mu.Unlock() 173 var ents []fuse.Dirent 174 for name, childNode := range n.children { 175 var ino uint64 176 switch v := childNode.(type) { 177 case *roDir: 178 ino = v.permanode.Sum64() 179 case *roFile: 180 ino = v.permanode.Sum64() 181 default: 182 log.Printf("roDir.ReadDir: unknown child type %T", childNode) 183 } 184 185 // TODO: figure out what Dirent.Type means. 186 // fuse.go says "Type uint32 // ?" 187 dirent := fuse.Dirent{ 188 Name: name, 189 Inode: ino, 190 } 191 log.Printf("roDir(%q) appending inode %x, %+v", n.fullPath(), dirent.Inode, dirent) 192 ents = append(ents, dirent) 193 } 194 return ents, nil 195 } 196 197 func (n *roDir) Lookup(name string, intr fs.Intr) (ret fs.Node, err fuse.Error) { 198 defer func() { 199 log.Printf("roDir(%q).Lookup(%q) = %#v, %v", n.fullPath(), name, ret, err) 200 }() 201 if err := n.populate(); err != nil { 202 log.Println("populate:", err) 203 return nil, fuse.EIO 204 } 205 n.mu.Lock() 206 defer n.mu.Unlock() 207 if n2 := n.children[name]; n2 != nil { 208 return n2, nil 209 } 210 return nil, fuse.ENOENT 211 } 212 213 // roFile is a read-only file, or symlink. 214 type roFile struct { 215 fs *CamliFileSystem 216 permanode blob.Ref 217 parent *roDir 218 name string // ent name (base name within parent) 219 220 mu sync.Mutex // protects all following fields 221 symLink bool // if true, is a symlink 222 target string // if a symlink 223 content blob.Ref // if a regular file 224 size int64 225 mtime, atime time.Time // if zero, use serverStart 226 xattrs map[string][]byte 227 } 228 229 func (n *roDir) Getxattr(req *fuse.GetxattrRequest, res *fuse.GetxattrResponse, intr fs.Intr) fuse.Error { 230 return n.xattr().get(req, res) 231 } 232 233 func (n *roDir) Listxattr(req *fuse.ListxattrRequest, res *fuse.ListxattrResponse, intr fs.Intr) fuse.Error { 234 return n.xattr().list(req, res) 235 } 236 237 func (n *roFile) Getxattr(req *fuse.GetxattrRequest, res *fuse.GetxattrResponse, intr fs.Intr) fuse.Error { 238 return n.xattr().get(req, res) 239 } 240 241 func (n *roFile) Listxattr(req *fuse.ListxattrRequest, res *fuse.ListxattrResponse, intr fs.Intr) fuse.Error { 242 return n.xattr().list(req, res) 243 } 244 245 func (n *roFile) Removexattr(req *fuse.RemovexattrRequest, intr fs.Intr) fuse.Error { 246 return fuse.EPERM 247 } 248 249 func (n *roFile) Setxattr(req *fuse.SetxattrRequest, intr fs.Intr) fuse.Error { 250 return fuse.EPERM 251 } 252 253 // for debugging 254 func (n *roFile) fullPath() string { 255 if n == nil { 256 return "" 257 } 258 return filepath.Join(n.parent.fullPath(), n.name) 259 } 260 261 func (n *roFile) Attr() fuse.Attr { 262 // TODO: don't grab n.mu three+ times in here. 263 var mode os.FileMode = 0400 // read-only 264 265 n.mu.Lock() 266 size := n.size 267 var blocks uint64 268 if size > 0 { 269 blocks = uint64(size)/512 + 1 270 } 271 inode := n.permanode.Sum64() 272 if n.symLink { 273 mode |= os.ModeSymlink 274 } 275 n.mu.Unlock() 276 277 return fuse.Attr{ 278 Inode: inode, 279 Mode: mode, 280 Uid: uint32(os.Getuid()), 281 Gid: uint32(os.Getgid()), 282 Size: uint64(size), 283 Blocks: blocks, 284 Mtime: n.modTime(), 285 Atime: n.accessTime(), 286 Ctime: serverStart, 287 Crtime: serverStart, 288 } 289 } 290 291 func (n *roFile) accessTime() time.Time { 292 n.mu.Lock() 293 if !n.atime.IsZero() { 294 defer n.mu.Unlock() 295 return n.atime 296 } 297 n.mu.Unlock() 298 return n.modTime() 299 } 300 301 func (n *roFile) modTime() time.Time { 302 n.mu.Lock() 303 defer n.mu.Unlock() 304 if !n.mtime.IsZero() { 305 return n.mtime 306 } 307 return serverStart 308 } 309 310 // Empirically: 311 // open for read: req.Flags == 0 312 // open for append: req.Flags == 1 313 // open for write: req.Flags == 1 314 // open for read/write (+<) == 2 (bitmask? of?) 315 // 316 // open flags are O_WRONLY (1), O_RDONLY (0), or O_RDWR (2). and also 317 // bitmaks of O_SYMLINK (0x200000) maybe. (from 318 // fuse_filehandle_xlate_to_oflags in macosx/kext/fuse_file.h) 319 func (n *roFile) Open(req *fuse.OpenRequest, res *fuse.OpenResponse, intr fs.Intr) (fs.Handle, fuse.Error) { 320 roFileOpen.Incr() 321 322 if isWriteFlags(req.Flags) { 323 return nil, fuse.EPERM 324 } 325 326 log.Printf("roFile.Open: %v: content: %v dir=%v flags=%v", n.permanode, n.content, req.Dir, req.Flags) 327 r, err := schema.NewFileReader(n.fs.fetcher, n.content) 328 if err != nil { 329 roFileOpenError.Incr() 330 log.Printf("roFile.Open: %v", err) 331 return nil, fuse.EIO 332 } 333 334 // Turn off the OpenDirectIO bit (on by default in rsc fuse server.go), 335 // else append operations don't work for some reason. 336 res.Flags &= ^fuse.OpenDirectIO 337 338 // Read-only. 339 nod := &node{ 340 fs: n.fs, 341 blobref: n.content, 342 } 343 return &nodeReader{n: nod, fr: r}, nil 344 } 345 346 func (n *roFile) Fsync(r *fuse.FsyncRequest, intr fs.Intr) fuse.Error { 347 // noop 348 return nil 349 } 350 351 func (n *roFile) Readlink(req *fuse.ReadlinkRequest, intr fs.Intr) (string, fuse.Error) { 352 log.Printf("roFile.Readlink(%q)", n.fullPath()) 353 n.mu.Lock() 354 defer n.mu.Unlock() 355 if !n.symLink { 356 log.Printf("roFile.Readlink on node that's not a symlink?") 357 return "", fuse.EIO 358 } 359 return n.target, nil 360 } 361 362 // roFileOrDir is a *roFile or *roDir 363 type roFileOrDir interface { 364 fs.Node 365 permanodeString() string 366 xattr() *xattr 367 } 368 369 func (n *roFile) permanodeString() string { 370 return n.permanode.String() 371 } 372 373 func (n *roDir) permanodeString() string { 374 return n.permanode.String() 375 } 376 377 func (n *roFile) xattr() *xattr { 378 return &xattr{"roFile", n.fs, n.permanode, &n.mu, &n.xattrs} 379 } 380 381 func (n *roDir) xattr() *xattr { 382 return &xattr{"roDir", n.fs, n.permanode, &n.mu, &n.xattrs} 383 }