62
64
return p[0], p[2], p[3]
63
65
return p[0], p[2], None
68
def relative_symlink(src_path, dst_path):
69
"""os.symlink replacement that creates relative symbolic links."""
70
path_sep = os.path.sep
71
src_path = os.path.normpath(src_path)
72
dst_path = os.path.normpath(dst_path)
73
src_path_elems = src_path.split(path_sep)
74
dst_path_elems = dst_path.split(path_sep)
75
if os.path.isabs(src_path):
76
if not os.path.isabs(dst_path):
77
dst_path = os.path.abspath(dst_path)
78
# XXX: dsilvers: 20060315: Note that os.path.commonprefix does not
79
# require that the common prefix be full path elements. As a result
80
# the common prefix of /foo/bar/baz and /foo/barbaz is /foo/bar.
81
# This isn't an issue here in the pool code but it could be a
82
# problem if this code is transplanted elsewhere.
83
common_prefix = os.path.commonprefix([src_path_elems, dst_path_elems])
84
backward_elems = ['..'] * (len(dst_path_elems)-len(common_prefix)-1)
85
forward_elems = src_path_elems[len(common_prefix):]
86
src_path = path_sep.join(backward_elems + forward_elems)
87
os.symlink(src_path, dst_path)
89
class _diskpool_atomicfile:
90
"""Simple file-like object used by the pool to atomically move into place
91
a file after downloading from the librarian.
93
This class is designed to solve a very specific problem encountered in
94
the publisher. Namely that should the publisher crash during the process
95
of publishing a file to the pool, an empty or incomplete file would be
96
present in the pool. Its mere presence would fool the publisher into
97
believing it had already downloaded that file to the pool, resulting
98
in failures in the apt-ftparchive stage.
100
By performing a rename() when the file is guaranteed to have been
101
fully written to disk (after the fd.close()) we can be sure that if
102
the filename is present in the pool, it is definitely complete.
105
def __init__(self, targetfilename, mode, rootpath="/tmp"):
109
self.targetfilename = targetfilename
110
fd, name = tempfile.mkstemp(prefix=".temp-download.", dir=rootpath)
111
self.fd = os.fdopen(fd, mode)
113
self.write = self.fd.write
116
"""Make the atomic move into place having closed the temp file."""
118
os.chmod(self.tempname, 0644)
119
# Note that this will fail if the target and the temp dirs are on
120
# different filesystems.
121
os.rename(self.tempname, self.targetfilename)
65
124
class AlreadyInPool:
66
125
"""Raised when an attempt is made to add a file already in the pool."""
69
129
"""Raised when an attempt is made to remove a non-existent file."""
71
132
class DiskPoolEntry:
72
133
def __init__(self, source=''):
74
135
self.comps = Set()
75
136
self.source = source
78
140
"""Scan a pool on the filesystem and record information about it."""
162
224
self.components[component][leafname] = False
163
225
self.files[leafname] = DiskPoolEntry(sourcename)
164
226
self.files[leafname].defcomp = component
165
return file(targetpath,"w")
227
return _diskpool_atomicfile(targetpath, "wb", rootpath=self.rootpath)
167
229
def removeFile(self, component, sourcename, leafname):
168
230
"""Remove a file from a given component.