2 from __future__ import absolute_import
3 import errno, os, tempfile
10 import cPickle as pickle
13 class Error(Exception):
17 def __init__(self, filename):
18 # Map a "dev:ino" node to a list of paths associated with that node.
20 # Map a path to a "dev:ino" node.
22 self._filename = filename
23 self._save_prepared = None
27 f = open(filename, 'rb')
29 if e.errno == errno.ENOENT:
35 self._node_paths = pickle.load(f)
39 # Set up the reverse hard link index.
40 for node, paths in compat.items(self._node_paths):
42 self._path_node[path] = node
44 def prepare_save(self):
45 """ Commit all of the relevant data to disk. Do as much work
46 as possible without actually making the changes visible."""
47 if self._save_prepared:
48 raise Error('save of %r already in progress' % self._filename)
50 (dir, name) = os.path.split(self._filename)
51 (ffd, self._tmpname) = tempfile.mkstemp(b'.tmp', name, dir)
54 f = os.fdopen(ffd, 'wb', 65536)
59 pickle.dump(self._node_paths, f, 2)
64 tmpname = self._tmpname
68 self._save_prepared = True
70 def commit_save(self):
71 if not self._save_prepared:
72 raise Error('cannot commit save of %r; no save prepared'
75 os.rename(self._tmpname, self._filename)
77 else: # No data -- delete _filename if it exists.
79 os.unlink(self._filename)
81 if e.errno == errno.ENOENT:
85 self._save_prepared = None
89 os.unlink(self._tmpname)
95 def add_path(self, path, dev, ino):
97 node = '%s:%s' % (dev, ino)
98 self._path_node[path] = node
99 link_paths = self._node_paths.get(node)
100 if link_paths and path not in link_paths:
101 link_paths.append(path)
103 self._node_paths[node] = [path]
105 def _del_node_path(self, node, path):
106 link_paths = self._node_paths[node]
107 link_paths.remove(path)
109 del self._node_paths[node]
111 def change_path(self, path, new_dev, new_ino):
112 prev_node = self._path_node.get(path)
114 self._del_node_path(prev_node, path)
115 self.add_path(new_dev, new_ino, path)
117 def del_path(self, path):
118 # Path may not be in db (if updating a pre-hardlink support index).
119 node = self._path_node.get(path)
121 self._del_node_path(node, path)
122 del self._path_node[path]
124 def node_paths(self, dev, ino):
125 node = '%s:%s' % (dev, ino)
126 return self._node_paths[node]