2 ###############################################################################
4 # Pakfire - The IPFire package management system #
5 # Copyright (C) 2011 Pakfire development team #
7 # This program is free software: you can redistribute it and/or modify #
8 # it under the terms of the GNU General Public License as published by #
9 # the Free Software Foundation, either version 3 of the License, or #
10 # (at your option) any later version. #
12 # This program is distributed in the hope that it will be useful, #
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15 # GNU General Public License for more details. #
17 # You should have received a copy of the GNU General Public License #
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
20 ###############################################################################
29 import pakfire
.filelist
30 import pakfire
.util
as util
31 import pakfire
.compress
as compress
32 from pakfire
.constants
import *
33 from pakfire
.i18n
import _
35 from base
import Package
36 from lexer
import FileLexer
38 # XXX need to add zlib and stuff here.
39 PAYLOAD_COMPRESSION_MAGIC
= {
43 class InnerTarFile(tarfile
.TarFile
):
44 SUPPORTED_XATTRS
= ("security.capability",)
46 def __init__(self
, *args
, **kwargs
):
47 # Force the PAX format.
48 kwargs
["format"] = tarfile
.PAX_FORMAT
50 tarfile
.TarFile
.__init
__(self
, *args
, **kwargs
)
52 def add(self
, name
, arcname
=None, recursive
=None, exclude
=None, filter=None):
54 Emulate the add function with xattrs support.
56 tarinfo
= self
.gettarinfo(name
, arcname
)
61 # Use new modules code...
62 if hasattr(xattr
, "get_all"):
63 attrs
= xattr
.get_all(name
)
65 # ...or use the deprecated API.
67 for attr
in xattr
.listxattr(name
):
68 val
= xattr
.getxattr(name
, attr
)
69 attrs
.append((attr
, val
))
71 for attr
, val
in attrs
:
72 # Skip all attrs that are not supported (e.g. selinux).
73 if not attr
in self
.SUPPORTED_XATTRS
:
76 logging
.debug("Saving xattr %s=%s from %s" % (attr
, val
, name
))
78 tarinfo
.pax_headers
[attr
] = val
80 # Append the tar header and data to the archive.
81 f
= tarfile
.bltn_open(name
, "rb")
82 self
.addfile(tarinfo
, f
)
88 for f
in os
.listdir(name
):
89 self
.add(os
.path
.join(name
, f
), os
.path
.join(arcname
, f
),
90 recursive
, exclude
, filter)
95 def extract(self
, member
, path
=""):
96 target
= os
.path
.join(path
, member
.name
)
98 # Remove symlink targets, because tarfile cannot replace them.
99 if member
.issym() and os
.path
.exists(target
):
100 print "unlinking", target
103 # Extract file the normal way...
105 tarfile
.TarFile
.extract(self
, member
, path
)
107 logging
.warning(_("Could not extract file: /%(src)s - %(dst)s") \
108 % { "src" : member
.name
, "dst" : e
, })
110 # ...and then apply the extended attributes.
111 if member
.pax_headers
:
112 for attr
, val
in member
.pax_headers
.items():
113 # Skip all attrs that are not supported (e.g. selinux).
114 if not attr
in self
.SUPPORTED_XATTRS
:
117 logging
.debug("Restoring xattr %s=%s to %s" % (attr
, val
, target
))
118 if hasattr(xattr
, "set"):
119 xattr
.set(target
, attr
, val
)
122 xattr
.setxattr(target
, attr
, val
)
125 class FilePackage(Package
):
127 This class is a wrapper that reads package data from the (outer)
128 tarball and should never be used solely.
130 def __init__(self
, pakfire
, repo
, filename
):
131 Package
.__init
__(self
, pakfire
, repo
)
132 self
.filename
= os
.path
.abspath(filename
)
134 # Place to cache the metadata
137 # Place to cache the filelist
138 self
._filelist
= None
140 # Store the format of this package file.
141 self
.format
= self
.get_format()
143 # XXX need to make this much better.
146 # Read the info file.
148 a
= self
.open_archive()
149 f
= a
.extractfile("info")
151 self
.lexer
= FileLexer(f
.readlines())
156 elif self
.format
== 0:
160 raise PackageFormatUnsupportedError
, _("Filename: %s") % self
.filename
164 Initially check if the given file is of the correct type and
167 if not tarfile
.is_tarfile(self
.filename
):
168 raise FileError
, "Given file is not of correct format: %s" % self
.filename
170 assert self
.format
in PACKAGE_FORMATS_SUPPORTED
172 def get_format(self
):
173 a
= self
.open_archive()
175 f
= a
.extractfile("pakfire-format")
191 return "<%s %s>" % (self
.__class
__.__name
__, self
.filename
)
195 # A file package is always local.
198 def open_archive(self
):
199 return tarfile
.open(self
.filename
, format
=tarfile
.PAX_FORMAT
)
201 def extract(self
, message
=None, prefix
=None):
202 logging
.debug("Extracting package %s" % self
.friendly_name
)
207 # A place to store temporary data.
210 # Open package data for read.
211 archive
= self
.open_archive()
213 # Get the package payload.
214 payload
= archive
.extractfile("data.img")
216 # Decompress the payload if needed.
217 logging
.debug("Compression: %s" % self
.payload_compression
)
219 # Create a temporary file to store the decompressed output.
220 garbage
, tempf
= tempfile
.mkstemp(prefix
="pakfire")
225 # Decompress the package payload.
226 if self
.payload_compression
:
227 compress
.decompressobj(i
, o
, algo
=self
.payload_compression
)
230 buf
= i
.read(BUFFER_SIZE
)
233 buf
= i
.read(BUFFER_SIZE
)
238 payload
= open(tempf
)
240 # Open the tarball in the package.
241 payload_archive
= InnerTarFile
.open(fileobj
=payload
)
243 members
= payload_archive
.getmembers()
248 message
= "%-10s : %s" % (message
, self
.friendly_name
)
249 pb
= util
.make_progress(message
, len(members
), eta
=False)
252 for member
in members
:
258 target
= os
.path
.join(prefix
, member
.name
)
260 # If the member is a directory and if it already exists, we
261 # don't need to create it again.
263 if os
.path
.exists(target
):
268 # Remove file if it has been existant
271 #if self.pakfire.config.get("debug"):
272 # msg = "Creating file (%s:%03d:%03d) " % \
273 # (tarfile.filemode(member.mode), member.uid, member.gid)
275 # msg += "/%s -> %s" % (member.name, member.linkname)
276 # elif member.islnk():
277 # msg += "/%s link to /%s" % (member.name, member.linkname)
279 # msg += "/%s" % member.name
282 payload_archive
.extract(member
, path
=prefix
)
284 # Close all open files.
285 payload_archive
.close()
298 Read-in the metadata from the "info" file and cache it in _metadata.
300 assert self
.format
== 0, self
302 if not self
._metadata
:
303 a
= self
.open_archive()
304 f
= a
.extractfile("info")
306 for line
in f
.readlines():
307 m
= re
.match(r
"^(\w+)=(.*)$", line
)
311 key
, val
= m
.groups()
312 self
._metadata
[key
] = val
.strip("\"")
317 return self
._metadata
322 Return the size of the package file.
324 return os
.path
.getsize(self
.filename
)
331 inst_size
= self
.lexer
.package
.get_var("size")
333 inst_size
= int(inst_size
)
339 def get_filelist(self
):
341 Return a list of the files that are contained in the package
346 a
= self
.open_archive()
347 f
= a
.extractfile("filelist")
349 for line
in f
.readlines():
352 file = pakfire
.filelist
.File(self
.pakfire
)
358 # XXX need to parse the rest of the information from the
364 if not name
.startswith("/"):
379 if self
._filelist
is None:
380 self
._filelist
= self
.get_filelist()
382 return self
._filelist
385 def configfiles(self
):
386 a
= self
.open_archive()
388 f
= a
.extractfile("configs")
389 for line
in f
.readlines():
390 if not line
.startswith("/"):
397 def payload_compression(self
):
399 Return the (guessed) compression type of the payload.
401 # Get the max. length of the magic values.
402 max_length
= max([len(v
) for v
in PAYLOAD_COMPRESSION_MAGIC
.values()])
404 a
= self
.open_archive()
405 f
= a
.extractfile("data.img")
407 # Read magic bytes from file.
408 magic
= f
.read(max_length
)
413 for algo
, m
in PAYLOAD_COMPRESSION_MAGIC
.items():
414 if not magic
.startswith(m
):
421 # XXX needs to be replaced
423 Read the signature from the archive or return None if no
424 signature does exist.
428 a
= self
.open_archive()
429 f
= a
.extractfile("signature")
437 # signature file could not be found
445 Calculate the hash1 of this package.
447 return util
.calc_hash1(self
.filename
)
452 name
= self
.lexer
.package
.get_var("name")
453 elif self
.format
== 0:
454 name
= self
.metadata
.get("PKG_NAME")
462 epoch
= self
.lexer
.package
.get_var("epoch", 0)
463 elif self
.format
== 0:
464 epoch
= self
.metadata
.get("PKG_EPOCH")
476 version
= self
.lexer
.package
.get_var("version")
477 elif self
.format
== 0:
478 version
= self
.metadata
.get("PKG_VER")
486 release
= self
.lexer
.package
.get_var("release")
487 elif self
.format
== 0:
488 release
= self
.metadata
.get("PKG_REL")
496 arch
= self
.lexer
.package
.get_var("arch")
497 elif self
.format
== 0:
498 arch
= self
.metadata
.get("PKG_ARCH")
506 vendor
= self
.lexer
.distro
.get_var("vendor")
507 elif self
.format
== 0:
508 vendor
= self
.metadata
.get("PKG_VENDOR")
515 summary
= self
.lexer
.package
.get_var("summary")
516 elif self
.format
== 0:
517 summary
= self
.metadata
.get("PKG_SUMMARY")
523 def description(self
):
525 description
= self
.lexer
.package
.get_var("description")
526 elif self
.format
== 0:
527 description
= self
.metadata
.get("PKG_DESC")
534 groups
= self
.lexer
.package
.get_var("groups")
535 elif self
.format
== 0:
536 groups
= self
.metadata
.get("PKG_GROUPS")
539 return groups
.split()
546 license
= self
.lexer
.package
.get_var("license")
547 elif self
.format
== 0:
548 license
= self
.metadata
.get("PKG_LICENSE")
555 url
= self
.lexer
.package
.get_var("url")
556 elif self
.format
== 0:
557 url
= self
.metadata
.get("PKG_URL")
562 def maintainer(self
):
564 maintainer
= self
.lexer
.package
.get_var("maintainer")
565 elif self
.format
== 0:
566 maintainer
= self
.metadata
.get("PKG_MAINTAINER")
573 uuid
= self
.lexer
.package
.get_var("uuid")
574 elif self
.format
== 0:
575 uuid
= self
.metadata
.get("PKG_UUID")
577 #assert uuid, self XXX re-enable this
583 build_id
= self
.lexer
.build
.get_var("id")
584 elif self
.format
== 0:
585 build_id
= self
.metadata
.get("BUILD_ID")
587 assert build_id
, self
591 def build_host(self
):
593 build_host
= self
.lexer
.build
.get_var("host")
594 elif self
.format
== 0:
595 build_host
= self
.metadata
.get("BUILD_HOST")
597 assert build_host
, self
601 def build_time(self
):
603 build_time
= self
.lexer
.build
.get_var("time")
604 elif self
.format
== 0:
605 build_time
= self
.metadata
.get("BUILD_TIME")
607 # XXX re-enable this later
608 #assert build_time, self
611 build_time
= int(build_time
)
620 provides
= self
.lexer
.deps
.get_var("provides")
621 elif self
.format
== 0:
622 provides
= self
.metadata
.get("PKG_PROVIDES")
627 return provides
.split()
632 requires
= self
.lexer
.deps
.get_var("requires")
633 elif self
.format
== 0:
634 requires
= self
.metadata
.get("PKG_REQUIRES")
639 return requires
.split()
642 def prerequires(self
):
644 prerequires
= self
.lexer
.deps
.get_var("prerequires")
645 elif self
.format
== 0:
646 prerequires
= self
.metadata
.get("PKG_PREREQUIRES")
651 return prerequires
.split()
656 obsoletes
= self
.lexer
.deps
.get_var("obsoletes")
657 elif self
.format
== 0:
658 obsoletes
= self
.metadata
.get("PKG_OBSOLETES")
663 return obsoletes
.split()
668 conflicts
= self
.lexer
.deps
.get_var("conflicts")
669 elif self
.format
== 0:
670 conflicts
= self
.metadata
.get("PKG_CONFLICTS")
675 return conflicts
.split()
678 class SourcePackage(FilePackage
):
682 class BinaryPackage(FilePackage
):
683 def get_scriptlet(self
, type):
684 a
= self
.open_archive()
686 # Path of the scriptlet in the tarball.
687 path
= "scriptlets/%s" % type
690 f
= a
.extractfile(path
)
692 # If the scriptlet is not available, we just return.