#!/usr/bin/env python
"""Describes and converts various metadata and metadata representations."""
import os
import sys
import backshift_os_mod
import constants_mod
import escape_mod
import helpers
[docs]def get_int_from_fields(fields):
"""Extract an integer field from a repo line."""
assert fields[1:] and not fields[2:]
return int(fields[1])
[docs]def get_float_from_fields(fields):
"""Extract a floating point field from a repo line."""
assert fields[1:] and not fields[2:]
return float(fields[1])
[docs]def get_string_from_fields(fields):
"""Extract a string field from a repo line."""
assert fields[1:] and not fields[2:]
# it's already a string
return fields[1]
[docs]def get_escaped_string_from_fields(fields):
"""Extract a string field from a repo line."""
assert fields[1:] and not fields[2:]
# it's already a string
return escape_mod.unescape(fields[1])
[docs]def get_bool_from_fields(fields):
"""Extract a boolean from a list of fields."""
if fields[1] == constants_mod.Constants.b_true:
return True
elif fields[1] == constants_mod.Constants.b_false:
return False
else:
raise ValueError('Boolean not True or False: %s' % fields[1])
[docs]def undefined(stat_buf, field):
"""Extract an undefined field from a stat - used for derived fields."""
helpers.make_used([stat_buf, field])
raise NotImplementedError
[docs]def get_field_on_stat(filename, stat_buf, field):
"""Just pull out a field - not much to do with types here, because stat returns things reasonably to begin with."""
helpers.make_used(filename)
value = getattr(stat_buf, helpers.binary_to_string(field))
return value
[docs]def always_true(filename, stat_buf, field):
"""Just always return True, for things that are single-field like directory or regular_file."""
helpers.make_used([filename, stat_buf, field])
return True
# Believe it or not, both Windows and Linux allow blanks in usernames and groupnames. So we escape them.
[docs]def get_group_on_stat(filename, stat_buf, field):
"""Look up the group name (not just the gid) - if none, return #gid."""
helpers.make_used([filename, field])
gid = stat_buf.st_gid
try:
grp_ent = backshift_os_mod.my_getgrgid(gid)
except KeyError:
group = '%d' % gid
else:
group = grp_ent.gr_name
return escape_mod.escape(group)
[docs]def get_owner_on_stat(filename, stat_buf, field):
"""Look up the username (not just the uid) - if none, return #uid."""
helpers.make_used([filename, field])
uid = stat_buf.st_uid
try:
pw_ent = backshift_os_mod.my_getpwuid(uid)
except KeyError:
user = '%d' % uid
else:
user = pw_ent.pw_name
return escape_mod.escape(user)
[docs]def init_hashes_on_stat(filename, stat_buf, field):
"""Just give an initial empty list for the hashes - we'll handle them separately/specially."""
helpers.make_used([filename, stat_buf, field])
return []
[docs]def get_link_target_on_stat(filename, stat_buf, field):
"""Return the link target of the file in question, encoded in base64 so we needn't worry about whitespace issues."""
helpers.make_used([stat_buf, field])
try:
not_yet_escaped = os.readlink(filename)
except OSError:
sys.stderr.write('Symlink disappeared? %s\n' % filename)
not_yet_escaped = constants_mod.Constants.b_readlink_failed
escaped = escape_mod.escape(not_yet_escaped)
return escaped
[docs]def get_hashes_from_fields(fields):
"""Get the hashes from a fields line - actually, we get the hashes and the lengths of the chunks they're hashes of."""
subfields = fields[1].split(constants_mod.Constants.b_minus)
assert subfields[1:] and not subfields[2:]
return (subfields[0], int(subfields[1]))
[docs]def get_st_rdev(stat_buf):
"""Extract st_rdev (st_dev doesn't count) from an os.stat return."""
return stat_buf.st_rdev
[docs]def get_major_on_stat(filename, stat_buf, field):
"""Get the major device number for a device."""
helpers.make_used([filename, field])
return os.major(get_st_rdev(stat_buf))
[docs]def get_minor_on_stat(filename, stat_buf, field):
"""Get the minor device number for a device."""
helpers.make_used([filename, field])
return os.minor(get_st_rdev(stat_buf))
[docs]class File_attributes(object):
# pylint: disable=W0232,R0903
# W0232: We don't need an __init__ method
# R0903: We don't need any public methodes
"""Contains the various metadata attributes a file might have - as class variables."""
cmc = constants_mod.Constants
dict_ = {}
dict_[cmc.b_block_device] = Metadata(cmc.b_block_device, always_true, get_bool_from_fields)
dict_[cmc.b_block_major] = Metadata(cmc.b_block_major, get_major_on_stat, get_int_from_fields)
dict_[cmc.b_block_minor] = Metadata(cmc.b_block_minor, get_minor_on_stat, get_int_from_fields)
dict_[cmc.b_character_device] = Metadata(cmc.b_character_device, always_true, get_bool_from_fields)
dict_[cmc.b_character_major] = Metadata(cmc.b_character_major, get_major_on_stat, get_int_from_fields)
dict_[cmc.b_character_minor] = Metadata(cmc.b_character_minor, get_minor_on_stat, get_int_from_fields)
dict_[cmc.b_directory] = Metadata(cmc.b_directory, always_true, get_bool_from_fields)
dict_[cmc.b_fifo] = Metadata(cmc.b_fifo, always_true, get_bool_from_fields)
dict_[cmc.b_group] = Metadata(cmc.b_group, get_group_on_stat, get_escaped_string_from_fields)
dict_[cmc.b_hash] = Metadata(cmc.b_hash, init_hashes_on_stat, get_hashes_from_fields)
dict_[cmc.b_link_target] = Metadata(cmc.b_link_target, get_link_target_on_stat, get_escaped_string_from_fields)
dict_[cmc.b_owner] = Metadata(cmc.b_owner, get_owner_on_stat, get_escaped_string_from_fields)
dict_[cmc.b_regular_file] = Metadata(cmc.b_regular_file, always_true, get_bool_from_fields)
dict_[cmc.b_symlink] = Metadata(cmc.b_symlink, always_true, get_bool_from_fields)
dict_[cmc.b_st_atime] = Metadata(cmc.b_st_atime, get_field_on_stat, get_float_from_fields)
dict_[cmc.b_st_ctime] = Metadata(cmc.b_st_ctime, get_field_on_stat, get_float_from_fields)
dict_[cmc.b_st_dev] = Metadata(cmc.b_st_dev, get_field_on_stat, get_int_from_fields)
dict_[cmc.b_st_gid] = Metadata(cmc.b_st_gid, get_field_on_stat, get_int_from_fields)
dict_[cmc.b_st_ino] = Metadata(cmc.b_st_ino, get_field_on_stat, get_int_from_fields)
dict_[cmc.b_st_mode] = Metadata(cmc.b_st_mode, get_field_on_stat, get_int_from_fields)
dict_[cmc.b_st_mtime] = Metadata(cmc.b_st_mtime, get_field_on_stat, get_float_from_fields)
dict_[cmc.b_st_size] = Metadata(cmc.b_st_size, get_field_on_stat, get_int_from_fields)
dict_[cmc.b_st_uid] = Metadata(cmc.b_st_uid, get_field_on_stat, get_int_from_fields)
[docs]class File_types(object):
# pylint: disable=W0232,R0903
# W0232: This class doesn't need an __init__
# R0903: We don't require public methods for this class
"""Just hold the different file types, their metadata fields, and how to convert each type of attribute."""
cmc = constants_mod.Constants
dict_ = {}
dict_[cmc.b_directory] = [
File_attributes.dict_[cmc.b_directory],
File_attributes.dict_[cmc.b_group],
File_attributes.dict_[cmc.b_owner],
File_attributes.dict_[cmc.b_st_ctime],
File_attributes.dict_[cmc.b_st_dev],
File_attributes.dict_[cmc.b_st_gid],
File_attributes.dict_[cmc.b_st_ino],
File_attributes.dict_[cmc.b_st_mode],
File_attributes.dict_[cmc.b_st_mtime],
File_attributes.dict_[cmc.b_st_uid],
]
dict_[cmc.b_regular_file] = [
File_attributes.dict_[cmc.b_group],
File_attributes.dict_[cmc.b_hash],
File_attributes.dict_[cmc.b_owner],
File_attributes.dict_[cmc.b_regular_file],
File_attributes.dict_[cmc.b_st_ctime],
File_attributes.dict_[cmc.b_st_dev],
File_attributes.dict_[cmc.b_st_gid],
File_attributes.dict_[cmc.b_st_ino],
File_attributes.dict_[cmc.b_st_mode],
File_attributes.dict_[cmc.b_st_mtime],
File_attributes.dict_[cmc.b_st_size],
File_attributes.dict_[cmc.b_st_uid],
]
dict_[cmc.b_symlink] = [
File_attributes.dict_[cmc.b_group],
File_attributes.dict_[cmc.b_link_target],
File_attributes.dict_[cmc.b_owner],
File_attributes.dict_[cmc.b_st_ctime],
File_attributes.dict_[cmc.b_st_gid],
File_attributes.dict_[cmc.b_st_mode],
File_attributes.dict_[cmc.b_st_mtime],
File_attributes.dict_[cmc.b_st_uid],
File_attributes.dict_[cmc.b_symlink],
]
dict_[cmc.b_block_device] = [
File_attributes.dict_[cmc.b_block_device],
File_attributes.dict_[cmc.b_block_major],
File_attributes.dict_[cmc.b_block_minor],
File_attributes.dict_[cmc.b_group],
File_attributes.dict_[cmc.b_owner],
File_attributes.dict_[cmc.b_st_ctime],
File_attributes.dict_[cmc.b_st_dev],
File_attributes.dict_[cmc.b_st_gid],
File_attributes.dict_[cmc.b_st_ino],
File_attributes.dict_[cmc.b_st_mode],
File_attributes.dict_[cmc.b_st_mtime],
File_attributes.dict_[cmc.b_st_uid],
]
dict_[cmc.b_character_device] = [
File_attributes.dict_[cmc.b_character_device],
File_attributes.dict_[cmc.b_character_major],
File_attributes.dict_[cmc.b_character_minor],
File_attributes.dict_[cmc.b_group],
File_attributes.dict_[cmc.b_owner],
File_attributes.dict_[cmc.b_st_ctime],
File_attributes.dict_[cmc.b_st_dev],
File_attributes.dict_[cmc.b_st_gid],
File_attributes.dict_[cmc.b_st_ino],
File_attributes.dict_[cmc.b_st_mode],
File_attributes.dict_[cmc.b_st_mtime],
File_attributes.dict_[cmc.b_st_uid],
]
dict_[cmc.b_fifo] = [
File_attributes.dict_[cmc.b_fifo],
File_attributes.dict_[cmc.b_group],
File_attributes.dict_[cmc.b_owner],
File_attributes.dict_[cmc.b_st_ctime],
File_attributes.dict_[cmc.b_st_dev],
File_attributes.dict_[cmc.b_st_gid],
File_attributes.dict_[cmc.b_st_ino],
File_attributes.dict_[cmc.b_st_mode],
File_attributes.dict_[cmc.b_st_mtime],
File_attributes.dict_[cmc.b_st_uid],
]