3 # FUSE interface to Cumulus, allowing snapshots to be mounted as a virtual
6 # Copyright (C) 2006-2008 The Regents of the University of California
7 # Written by Michael Vrable <mvrable@cs.ucsd.edu>
9 # This program can be distributed under the terms of the GNU GPL, either
10 # version 2 of the License, or (at your option) any later version. See the
13 import itertools, os, stat, errno
17 import cumulus.metadata
19 fuse.fuse_python_api = (0, 2)
21 # TODO: Figure out FUSE option parsing
22 lowlevel = cumulus.LowlevelDataStore('/backups/lbs/turin')
23 store = cumulus.ObjectStore(lowlevel)
26 if ptr is None: return None
27 return tuple(x[1] for x in ptr)
30 """Strip leading slashe from path, and split apart into components."""
31 if not path.startswith('/'):
36 return path[1:].split('/')
38 def load_metadata(path):
39 if type(path) != type([]):
40 path = parse_path(path)
42 if path is None or len(path) < 2:
45 snapshot = cumulus.parse_full(store.load_snapshot(path[0]))
46 metadata = cumulus.metadata.Metadata(store, snapshot['Root'])
47 ptr = metadata.search(lambda x: cmp(x, path[1:]))
48 item = metadata._read(ptr)
49 if metadata._get_path(item) != path[1:]:
51 return cumulus.MetadataItem(item, store)
53 class MyStat(fuse.Stat):
66 class CumulusFS(Fuse):
67 def getattr(self, path):
69 path = parse_path(path)
71 if path is None: return -errno.ENOENT
74 st.st_mode = stat.S_IFDIR | 0755
78 snapshot = cumulus.parse_full(store.load_snapshot(path[0]))
81 st.st_mode = stat.S_IFDIR | 0755
84 # File contained within a snapshot
85 m = load_metadata(path)
90 st.st_uid = m.items.user[0]
91 st.st_gid = m.items.group[0]
92 st.st_mtime = m.items.mtime
93 st.st_ctime = m.items.ctime
94 st.st_atime = m.items.mtime
95 if m.items.type == 'd':
96 st.st_mode = stat.S_IFDIR | m.items.mode
98 elif m.items.type == 'l':
99 st.st_mode = stat.S_IFLNK | m.items.mode
101 st.st_mode = stat.S_IFREG | m.items.mode
102 st.st_size = m.items.size
106 def _cumulus_readdir(self, metadata, path):
107 # Find pointer to base directory in metadata
108 ptr1 = metadata.search(lambda x: cmp(x, path))
110 # Find pointer to end of directory contents
113 if len(p2) > len(p1): p2 = p2[0:len(p1)]
119 ptr2 = metadata.search(endcmp(path), ptr1)
121 # Scan through looking for top-level files and directories. Skip over
122 # data for files in subdirectories.
123 while metadata._cmp(ptr1, ptr2) < 0:
124 item = metadata._read(ptr1)
125 m = cumulus.MetadataItem(item, store)
126 if m.items.name == '.':
129 itempath = m.items.name.split('/')
130 assert itempath[0:len(path)] == path
132 if len(itempath) == len(path):
133 ptr1 = metadata._advance(ptr1)
136 if len(itempath) > len(path) + 1:
137 ptr1 = metadata.search(endcmp(itempath[0:len(path)+1]),
141 yield itempath[len(path)]
142 ptr1 = metadata._advance(ptr1)
144 def readdir(self, path, offset):
146 for r in itertools.chain(('.', '..'), lowlevel.list_snapshots()):
147 yield fuse.Direntry(r)
149 path = parse_path(path)
152 snapshot = cumulus.parse_full(store.load_snapshot(path[0]))
153 metadata = cumulus.metadata.Metadata(store, snapshot['Root'])
154 for r in itertools.chain(('.', '..'),
155 self._cumulus_readdir(metadata, path[1:])):
156 yield fuse.Direntry(r)
158 def readlink(self, path):
159 m = load_metadata(path)
163 return m.items.target
165 def open(self, path, flags):
166 m = load_metadata(path)
169 accmode = os.O_RDONLY | os.O_WRONLY | os.O_RDWR
170 if (flags & accmode) != os.O_RDONLY:
173 def read(self, path, size, offset):
174 m = load_metadata(path)
182 cumulus-fuse: Mount cumulus snapshots as a filesystem
185 server = CumulusFS(version="%prog " + fuse.__version__,
187 dash_s_do='setsingle')
189 server.parser.add_option(mountopt="root", metavar="PATH", default='/',
190 help="read snapshots from PATH [default: %default]")
192 server.parse(errex=1)
193 print server.fuse_args
194 print server.fuse_args.assemble()
198 if __name__ == '__main__':