1
# canonical.ubuntuone.storage.u1sync.scan
5
# Author: Tim Cole <tim.cole@canonical.com>
7
# Copyright 2009 Canonical Ltd.
9
# This program is free software: you can redistribute it and/or modify it
10
# under the terms of the GNU General Public License version 3, as published
11
# by the Free Software Foundation.
13
# This program is distributed in the hope that it will be useful, but
14
# WITHOUT ANY WARRANTY; without even the implied warranties of
15
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
16
# PURPOSE. See the GNU General Public License for more details.
18
# You should have received a copy of the GNU General Public License along
19
# with this program. If not, see <http://www.gnu.org/licenses/>.
20
"""Code for scanning local directory state."""
22
from __future__ import with_statement
27
from errno import ENOTDIR, EINVAL
29
EMPTY_HASH = "sha1:%s" % hashlib.sha1().hexdigest()
31
from canonical.ubuntuone.storage.protocol.dircontent_pb2 import \
32
DIRECTORY, FILE, SYMLINK
33
from canonical.ubuntuone.storage.u1sync.genericmerge import MergeNode
34
from canonical.ubuntuone.storage.u1sync.utils import should_sync
36
def scan_directory(path, display_path="", quiet=False):
37
"""Scans a local directory and builds an in-memory tree from it."""
38
if display_path != "" and not quiet:
44
link_target = os.readlink(path)
49
child_names = os.listdir(path)
51
if e.errno != ENOTDIR:
54
if link_target is not None:
57
sum.update(link_target)
58
content_hash = "sha1:%s" % sum.hexdigest()
59
return MergeNode(node_type=SYMLINK, content_hash=content_hash)
60
elif child_names is not None:
62
child_names = [n for n in child_names if should_sync(n.decode("utf-8"))]
63
child_paths = [(os.path.join(path, child_name),
64
os.path.join(display_path, child_name)) \
65
for child_name in child_names]
66
children = [scan_directory(child_path, child_display_path, quiet) \
67
for (child_path, child_display_path) in child_paths]
68
unicode_child_names = [n.decode("utf-8") for n in child_names]
69
children = dict(zip(unicode_child_names, children))
70
return MergeNode(node_type=DIRECTORY, children=children)
76
class HashStream(object):
77
"""Stream that computes hashes."""
78
def write(self, bytes):
79
"""Accumulate bytes."""
83
with open(path, "r") as stream:
84
shutil.copyfileobj(stream, HashStream())
85
content_hash = "sha1:%s" % sum.hexdigest()
86
return MergeNode(node_type=FILE, content_hash=content_hash)