43 #
44 # Dictionary used to map action names to output format. Each entry is
45 # indexed by action name, and consists of a list of tuples that map
46 # FileInfo class members to output labels.
47 #
48 OUTPUTMAP = {
49 "dir": [
50 ("group", "group="),
51 ("mode", "mode="),
52 ("owner", "owner="),
53 ("path", "path=")
54 ],
55 "file": [
56 ("hash", ""),
57 ("group", "group="),
58 ("mode", "mode="),
59 ("owner", "owner="),
60 ("path", "path=")
61 ],
62 "link": [
63 ("path", "path="),
64 ("target", "target=")
65 ],
66 "hardlink": [
67 ("path", "path="),
68 ("hardkey", "target=")
69 ],
70 }
71
72 # Mode checks used to validate safe file and directory permissions
73 ALLMODECHECKS = frozenset(("m", "w", "s", "o"))
74 DEFAULTMODECHECKS = frozenset(("m", "w", "o"))
75
76 class FileInfo(object):
77 """Base class to represent a file.
78
79 Subclassed according to whether the file represents an actual filesystem
80 object (RealFileInfo) or an IPS manifest action (ActionInfo).
81 """
82
179 #
180 # Because the manifest may legitimately translate a relative
181 # path from the proto area into a different path on the installed
182 # system, we don't compare paths here. We only expect this comparison
183 # to be invoked on items with identical relative paths in
184 # first place.
185 #
186
187 #
188 # All comparisons depend on type. For symlink and directory, they
189 # must be the same. For file and hardlink, see below.
190 #
191 typelhs = lhs.name()
192 typerhs = rhs.name()
193 if typelhs in ("link", "dir"):
194 if typelhs != typerhs:
195 return True
196
197 #
198 # For symlinks, all that's left is the link target.
199 #
200 if typelhs == "link":
201 return lhs.target != rhs.target
202
203 #
204 # For a directory, it's important that both be directories,
205 # the modes be identical, and the paths are identical. We already
206 # checked all but the modes above.
207 #
208 # If both objects are files, then we're in the same boat.
209 #
210 if typelhs == "dir" or (typelhs == "file" and typerhs == "file"):
211 return lhs.mode != rhs.mode
212
213 #
214 # For files or hardlinks:
215 #
216 # Since the key space is different (inodes for real files and
217 # actual link targets for hard links), and since the proto area will
218 # identify all N occurrences as hardlinks, but the manifests as one
219 # file and N-1 hardlinks, we have to compare files to hardlinks.
220 #
221
289
290 def __init__(self, action):
291 FileInfo.__init__(self)
292 #
293 # Currently, all actions that we support have a "path"
294 # attribute. If that changes, then we'll need to
295 # catch a KeyError from this assignment.
296 #
297 self.path = action.attrs["path"]
298
299 if action.name == "file":
300 self.owner = action.attrs["owner"]
301 self.group = action.attrs["group"]
302 self.mode = action.attrs["mode"]
303 self.hash = action.hash
304 if "preserve" in action.attrs:
305 self.editable = True
306 elif action.name == "link":
307 target = action.attrs["target"]
308 self.target = os.path.normpath(target)
309 elif action.name == "dir":
310 self.owner = action.attrs["owner"]
311 self.group = action.attrs["group"]
312 self.mode = action.attrs["mode"]
313 self.isdir = True
314 elif action.name == "hardlink":
315 target = os.path.normpath(action.get_target_path())
316 self.hardkey = target
317 self.hardpaths.add(target)
318
319 @staticmethod
320 def supported(action):
321 """Indicates whether the specified IPS action time is
322 correctly handled by the ActionInfo constructor.
323 """
324 return action in frozenset(("file", "dir", "link", "hardlink"))
325
326
327 class UnsupportedFileFormatError(Exception):
328 """This means that the stat.S_IFMT returned something we don't
349 is no way to determine which of the hard links should be
350 delivered as a file, and which as hardlinks.
351 """
352
353 def __init__(self, root=None, path=None):
354 FileInfo.__init__(self)
355 self.path = path
356 path = os.path.join(root, path)
357 lstat = os.lstat(path)
358 mode = lstat.st_mode
359
360 #
361 # Per stat.py, these cases are mutually exclusive.
362 #
363 if stat.S_ISREG(mode):
364 self.hash = self.path
365 elif stat.S_ISDIR(mode):
366 self.isdir = True
367 elif stat.S_ISLNK(mode):
368 self.target = os.path.normpath(os.readlink(path))
369 else:
370 raise UnsupportedFileFormatError(path, mode)
371
372 if not stat.S_ISLNK(mode):
373 self.mode = "%04o" % stat.S_IMODE(mode)
374 #
375 # Instead of reading the group and owner from the proto area after
376 # a non-root build, just drop in dummy values. Since we don't
377 # compare them anywhere, this should allow at least marginally
378 # useful comparisons of protolist-style output.
379 #
380 self.owner = "owner"
381 self.group = "group"
382
383 #
384 # refcount > 1 indicates a hard link
385 #
386 if lstat.st_nlink > 1:
387 #
388 # This could get ugly if multiple proto areas reside
|
43 #
44 # Dictionary used to map action names to output format. Each entry is
45 # indexed by action name, and consists of a list of tuples that map
46 # FileInfo class members to output labels.
47 #
48 OUTPUTMAP = {
49 "dir": [
50 ("group", "group="),
51 ("mode", "mode="),
52 ("owner", "owner="),
53 ("path", "path=")
54 ],
55 "file": [
56 ("hash", ""),
57 ("group", "group="),
58 ("mode", "mode="),
59 ("owner", "owner="),
60 ("path", "path=")
61 ],
62 "link": [
63 ("mediator", "mediator="),
64 ("path", "path="),
65 ("target", "target=")
66 ],
67 "hardlink": [
68 ("path", "path="),
69 ("hardkey", "target=")
70 ],
71 }
72
73 # Mode checks used to validate safe file and directory permissions
74 ALLMODECHECKS = frozenset(("m", "w", "s", "o"))
75 DEFAULTMODECHECKS = frozenset(("m", "w", "o"))
76
77 class FileInfo(object):
78 """Base class to represent a file.
79
80 Subclassed according to whether the file represents an actual filesystem
81 object (RealFileInfo) or an IPS manifest action (ActionInfo).
82 """
83
180 #
181 # Because the manifest may legitimately translate a relative
182 # path from the proto area into a different path on the installed
183 # system, we don't compare paths here. We only expect this comparison
184 # to be invoked on items with identical relative paths in
185 # first place.
186 #
187
188 #
189 # All comparisons depend on type. For symlink and directory, they
190 # must be the same. For file and hardlink, see below.
191 #
192 typelhs = lhs.name()
193 typerhs = rhs.name()
194 if typelhs in ("link", "dir"):
195 if typelhs != typerhs:
196 return True
197
198 #
199 # For symlinks, all that's left is the link target.
200 # For mediated symlinks targets can differ.
201 #
202 if typelhs == "link":
203 return (lhs.mediator is None) and (lhs.target != rhs.target)
204
205 #
206 # For a directory, it's important that both be directories,
207 # the modes be identical, and the paths are identical. We already
208 # checked all but the modes above.
209 #
210 # If both objects are files, then we're in the same boat.
211 #
212 if typelhs == "dir" or (typelhs == "file" and typerhs == "file"):
213 return lhs.mode != rhs.mode
214
215 #
216 # For files or hardlinks:
217 #
218 # Since the key space is different (inodes for real files and
219 # actual link targets for hard links), and since the proto area will
220 # identify all N occurrences as hardlinks, but the manifests as one
221 # file and N-1 hardlinks, we have to compare files to hardlinks.
222 #
223
291
292 def __init__(self, action):
293 FileInfo.__init__(self)
294 #
295 # Currently, all actions that we support have a "path"
296 # attribute. If that changes, then we'll need to
297 # catch a KeyError from this assignment.
298 #
299 self.path = action.attrs["path"]
300
301 if action.name == "file":
302 self.owner = action.attrs["owner"]
303 self.group = action.attrs["group"]
304 self.mode = action.attrs["mode"]
305 self.hash = action.hash
306 if "preserve" in action.attrs:
307 self.editable = True
308 elif action.name == "link":
309 target = action.attrs["target"]
310 self.target = os.path.normpath(target)
311 if "mediator" in action.attrs:
312 self.mediator = action.attrs["mediator"]
313 else:
314 self.mediator = None
315 elif action.name == "dir":
316 self.owner = action.attrs["owner"]
317 self.group = action.attrs["group"]
318 self.mode = action.attrs["mode"]
319 self.isdir = True
320 elif action.name == "hardlink":
321 target = os.path.normpath(action.get_target_path())
322 self.hardkey = target
323 self.hardpaths.add(target)
324
325 @staticmethod
326 def supported(action):
327 """Indicates whether the specified IPS action time is
328 correctly handled by the ActionInfo constructor.
329 """
330 return action in frozenset(("file", "dir", "link", "hardlink"))
331
332
333 class UnsupportedFileFormatError(Exception):
334 """This means that the stat.S_IFMT returned something we don't
355 is no way to determine which of the hard links should be
356 delivered as a file, and which as hardlinks.
357 """
358
359 def __init__(self, root=None, path=None):
360 FileInfo.__init__(self)
361 self.path = path
362 path = os.path.join(root, path)
363 lstat = os.lstat(path)
364 mode = lstat.st_mode
365
366 #
367 # Per stat.py, these cases are mutually exclusive.
368 #
369 if stat.S_ISREG(mode):
370 self.hash = self.path
371 elif stat.S_ISDIR(mode):
372 self.isdir = True
373 elif stat.S_ISLNK(mode):
374 self.target = os.path.normpath(os.readlink(path))
375 self.mediator = None
376 else:
377 raise UnsupportedFileFormatError(path, mode)
378
379 if not stat.S_ISLNK(mode):
380 self.mode = "%04o" % stat.S_IMODE(mode)
381 #
382 # Instead of reading the group and owner from the proto area after
383 # a non-root build, just drop in dummy values. Since we don't
384 # compare them anywhere, this should allow at least marginally
385 # useful comparisons of protolist-style output.
386 #
387 self.owner = "owner"
388 self.group = "group"
389
390 #
391 # refcount > 1 indicates a hard link
392 #
393 if lstat.st_nlink > 1:
394 #
395 # This could get ugly if multiple proto areas reside
|