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
.util
as util
30 import pakfire
.compress
as compress
31 from pakfire
.constants
import *
33 from base
import Package
34 from lexer
import FileLexer
36 # XXX need to add zlib and stuff here.
37 PAYLOAD_COMPRESSION_MAGIC
= {
41 class InnerTarFile(tarfile
.TarFile
):
42 SUPPORTED_XATTRS
= ("security.capability",)
44 def __init__(self
, *args
, **kwargs
):
45 # Force the PAX format.
46 kwargs
["format"] = tarfile
.PAX_FORMAT
48 tarfile
.TarFile
.__init
__(self
, *args
, **kwargs
)
50 def add(self
, name
, arcname
=None, recursive
=None, exclude
=None, filter=None):
52 Emulate the add function with xattrs support.
54 tarinfo
= self
.gettarinfo(name
, arcname
)
59 # Use new modules code...
60 if hasattr(xattr
, "get_all"):
61 attrs
= xattr
.get_all(name
)
63 # ...or use the deprecated API.
65 for attr
in xattr
.listxattr(name
):
66 val
= xattr
.getxattr(name
, attr
)
67 attrs
.append((attr
, val
))
69 for attr
, val
in attrs
:
70 # Skip all attrs that are not supported (e.g. selinux).
71 if not attr
in self
.SUPPORTED_XATTRS
:
74 logging
.debug("Saving xattr %s=%s from %s" % (attr
, val
, name
))
76 tarinfo
.pax_headers
[attr
] = val
78 # Append the tar header and data to the archive.
79 f
= tarfile
.bltn_open(name
, "rb")
80 self
.addfile(tarinfo
, f
)
86 for f
in os
.listdir(name
):
87 self
.add(os
.path
.join(name
, f
), os
.path
.join(arcname
, f
),
88 recursive
, exclude
, filter)
93 def extract(self
, member
, path
=""):
94 # Extract file the normal way...
95 tarfile
.TarFile
.extract(self
, member
, path
)
97 # ...and then apply the extended attributes.
98 if member
.pax_headers
:
99 target
= os
.path
.join(path
, member
.name
)
101 for attr
, val
in member
.pax_headers
.items():
102 # Skip all attrs that are not supported (e.g. selinux).
103 if not attr
in self
.SUPPORTED_XATTRS
:
106 logging
.debug("Restoring xattr %s=%s to %s" % (attr
, val
, target
))
107 if hasattr(xattr
, "set"):
108 xattr
.set(target
, attr
, val
)
111 xattr
.setxattr(target
, attr
, val
)
114 class FilePackage(Package
):
116 This class is a wrapper that reads package data from the (outer)
117 tarball and should never be used solely.
119 def __init__(self
, pakfire
, repo
, filename
):
120 Package
.__init
__(self
, pakfire
, repo
)
121 self
.filename
= os
.path
.abspath(filename
)
123 # Place to cache the metadata
126 # Store the format of this package file.
127 self
.format
= self
.get_format()
129 # XXX need to make this much better.
132 # Read the info file.
134 a
= self
.open_archive()
135 f
= a
.extractfile("info")
137 self
.lexer
= FileLexer(f
.readlines())
142 elif self
.format
== 0:
146 raise PackageFormatUnsupportedError
, _("Filename: %s") % self
.filename
150 Initially check if the given file is of the correct type and
153 if not tarfile
.is_tarfile(self
.filename
):
154 raise FileError
, "Given file is not of correct format: %s" % self
.filename
156 assert self
.format
in PACKAGE_FORMATS_SUPPORTED
158 def get_format(self
):
159 a
= self
.open_archive()
161 f
= a
.extractfile("pakfire-format")
177 return "<%s %s>" % (self
.__class
__.__name
__, self
.filename
)
181 # A file package is always local.
184 def open_archive(self
):
185 return tarfile
.open(self
.filename
, format
=tarfile
.PAX_FORMAT
)
187 def extract(self
, message
=None, prefix
=None):
188 logging
.debug("Extracting package %s" % self
.friendly_name
)
193 # A place to store temporary data.
196 # Open package data for read.
197 archive
= self
.open_archive()
199 # Get the package payload.
200 payload
= archive
.extractfile("data.img")
202 # Decompress the payload if needed.
203 logging
.debug("Compression: %s" % self
.payload_compression
)
205 # Create a temporary file to store the decompressed output.
206 garbage
, tempf
= tempfile
.mkstemp(prefix
="pakfire")
211 # Decompress the package payload.
212 if self
.payload_compression
:
213 compress
.decompressobj(i
, o
, algo
=self
.payload_compression
)
216 buf
= i
.read(BUFFER_SIZE
)
219 buf
= i
.read(BUFFER_SIZE
)
224 payload
= open(tempf
)
226 # Open the tarball in the package.
227 payload_archive
= InnerTarFile
.open(fileobj
=payload
)
229 members
= payload_archive
.getmembers()
234 message
= "%-10s : %s" % (message
, self
.friendly_name
)
235 pb
= util
.make_progress(message
, len(members
), eta
=False)
238 for member
in members
:
244 target
= os
.path
.join(prefix
, member
.name
)
246 # If the member is a directory and if it already exists, we
247 # don't need to create it again.
249 if os
.path
.exists(target
):
254 # Remove file if it has been existant
257 #if self.pakfire.config.get("debug"):
258 # msg = "Creating file (%s:%03d:%03d) " % \
259 # (tarfile.filemode(member.mode), member.uid, member.gid)
261 # msg += "/%s -> %s" % (member.name, member.linkname)
262 # elif member.islnk():
263 # msg += "/%s link to /%s" % (member.name, member.linkname)
265 # msg += "/%s" % member.name
268 payload_archive
.extract(member
, path
=prefix
)
270 # Close all open files.
271 payload_archive
.close()
284 Read-in the metadata from the "info" file and cache it in _metadata.
286 assert self
.format
== 0, self
288 if not self
._metadata
:
289 a
= self
.open_archive()
290 f
= a
.extractfile("info")
292 for line
in f
.readlines():
293 m
= re
.match(r
"^(\w+)=(.*)$", line
)
297 key
, val
= m
.groups()
298 self
._metadata
[key
] = val
.strip("\"")
303 return self
._metadata
308 Return the size of the package file.
310 return os
.path
.getsize(self
.filename
)
312 def __filelist_from_metadata(self
):
313 a
= self
.open_archive()
314 f
= a
.extractfile("filelist")
317 for line
in f
.readlines():
319 if not line
.startswith("/"):
329 def __filelist_from_payload(self
):
330 # XXX expect uncompressed payload for now
331 # this is very simple and very slow
333 a
= self
.open_archive()
334 f
= a
.extractfile("data.img")
335 t
= tarfile
.open(fileobj
=f
)
337 ret
= ["/%s" % n
for n
in t
.getnames()]
348 Return a list of the files that are contained in the package
351 At first, we try to get them from the metadata (which is the
353 If the file is not existant, we will open the payload and
354 read it instead. The latter is a very slow procedure and
355 should not be used anyway.
357 if not hasattr(self
, "__filelist"):
359 self
.__filelist
= self
.__filelist
_from
_metadata
()
361 self
.__filelist
= self
.__filelist
_from
_payload
()
363 return self
.__filelist
366 def configfiles(self
):
367 a
= self
.open_archive()
369 f
= a
.extractfile("configs")
370 for line
in f
.readlines():
371 if not line
.startswith("/"):
378 def payload_compression(self
):
380 Return the (guessed) compression type of the payload.
382 # Get the max. length of the magic values.
383 max_length
= max([len(v
) for v
in PAYLOAD_COMPRESSION_MAGIC
.values()])
385 a
= self
.open_archive()
386 f
= a
.extractfile("data.img")
388 # Read magic bytes from file.
389 magic
= f
.read(max_length
)
394 for algo
, m
in PAYLOAD_COMPRESSION_MAGIC
.items():
395 if not magic
.startswith(m
):
402 # XXX needs to be replaced
404 Read the signature from the archive or return None if no
405 signature does exist.
409 a
= self
.open_archive()
410 f
= a
.extractfile("signature")
418 # signature file could not be found
426 Calculate the hash1 of this package.
428 return util
.calc_hash1(self
.filename
)
433 name
= self
.lexer
.package
.get_var("name")
434 elif self
.format
== 0:
435 name
= self
.metadata
.get("PKG_NAME")
443 epoch
= self
.lexer
.package
.get_var("epoch", 0)
444 elif self
.format
== 0:
445 epoch
= self
.metadata
.get("PKG_EPOCH")
457 version
= self
.lexer
.package
.get_var("version")
458 elif self
.format
== 0:
459 version
= self
.metadata
.get("PKG_VER")
467 release
= self
.lexer
.package
.get_var("release")
468 elif self
.format
== 0:
469 release
= self
.metadata
.get("PKG_REL")
477 arch
= self
.lexer
.package
.get_var("arch")
478 elif self
.format
== 0:
479 arch
= self
.metadata
.get("PKG_ARCH")
487 vendor
= self
.lexer
.distro
.get_var("vendor")
488 elif self
.format
== 0:
489 vendor
= self
.metadata
.get("PKG_VENDOR")
496 summary
= self
.lexer
.package
.get_var("summary")
497 elif self
.format
== 0:
498 summary
= self
.metadata
.get("PKG_SUMMARY")
504 def description(self
):
506 description
= self
.lexer
.package
.get_var("description")
507 elif self
.format
== 0:
508 description
= self
.metadata
.get("PKG_DESC")
515 groups
= self
.lexer
.package
.get_var("groups")
516 elif self
.format
== 0:
517 groups
= self
.metadata
.get("PKG_GROUPS")
520 return groups
.split()
527 license
= self
.lexer
.package
.get_var("license")
528 elif self
.format
== 0:
529 license
= self
.metadata
.get("PKG_LICENSE")
536 url
= self
.lexer
.package
.get_var("url")
537 elif self
.format
== 0:
538 url
= self
.metadata
.get("PKG_URL")
543 def maintainer(self
):
545 maintainer
= self
.lexer
.package
.get_var("maintainer")
546 elif self
.format
== 0:
547 maintainer
= self
.metadata
.get("PKG_MAINTAINER")
554 uuid
= self
.lexer
.package
.get_var("uuid")
555 elif self
.format
== 0:
556 uuid
= self
.metadata
.get("PKG_UUID")
558 #assert uuid, self XXX re-enable this
564 build_id
= self
.lexer
.build
.get_var("id")
565 elif self
.format
== 0:
566 build_id
= self
.metadata
.get("BUILD_ID")
568 assert build_id
, self
572 def build_host(self
):
574 build_host
= self
.lexer
.build
.get_var("host")
575 elif self
.format
== 0:
576 build_host
= self
.metadata
.get("BUILD_HOST")
578 assert build_host
, self
582 def build_time(self
):
584 build_time
= self
.lexer
.build
.get_var("time")
585 elif self
.format
== 0:
586 build_time
= self
.metadata
.get("BUILD_TIME")
588 # XXX re-enable this later
589 #assert build_time, self
592 build_time
= int(build_time
)
601 provides
= self
.lexer
.deps
.get_var("provides")
602 elif self
.format
== 0:
603 provides
= self
.metadata
.get("PKG_PROVIDES")
608 return provides
.split()
613 requires
= self
.lexer
.deps
.get_var("requires")
614 elif self
.format
== 0:
615 requires
= self
.metadata
.get("PKG_REQUIRES")
620 return requires
.split()
623 def prerequires(self
):
625 prerequires
= self
.lexer
.deps
.get_var("prerequires")
626 elif self
.format
== 0:
627 prerequires
= self
.metadata
.get("PKG_PREREQUIRES")
632 return prerequires
.split()
637 obsoletes
= self
.lexer
.deps
.get_var("obsoletes")
638 elif self
.format
== 0:
639 obsoletes
= self
.metadata
.get("PKG_OBSOLETES")
644 return obsoletes
.split()
649 conflicts
= self
.lexer
.deps
.get_var("conflicts")
650 elif self
.format
== 0:
651 conflicts
= self
.metadata
.get("PKG_CONFLICTS")
656 return conflicts
.split()