|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "generic.h" |
|
#include "apt_instmodule.h" |
|
#include <apt-pkg/extracttar.h> |
|
#include <apt-pkg/error.h> |
|
#include <apt-pkg/dirstream.h> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PyDirStream : public pkgDirStream |
|
{ |
|
|
|
public: |
|
PyObject *callback; |
|
PyObject *py_data; |
|
|
|
const char *member; |
|
|
|
|
|
bool error; |
|
|
|
char *copy; |
|
|
|
size_t copy_size; |
|
|
|
virtual bool DoItem(Item &Itm,int &Fd); |
|
virtual bool FinishedFile(Item &Itm,int Fd); |
|
#if (APT_PKG_MAJOR >= 5) |
|
virtual bool Process(Item &Itm,const unsigned char *Data, |
|
unsigned long long Size,unsigned long long Pos); |
|
#else |
|
virtual bool Process(Item &Itm,const unsigned char *Data, |
|
unsigned long Size,unsigned long Pos); |
|
#endif |
|
PyDirStream(PyObject *callback, const char *member=0) : callback(callback), |
|
py_data(0), member(member), error(false), copy(0), copy_size(0) |
|
{ |
|
Py_XINCREF(callback); |
|
} |
|
|
|
virtual ~PyDirStream() { |
|
Py_XDECREF(callback); |
|
Py_XDECREF(py_data); |
|
delete[] copy; |
|
} |
|
}; |
|
|
|
bool PyDirStream::DoItem(Item &Itm, int &Fd) |
|
{ |
|
if (!member || strcmp(Itm.Name, member) == 0) { |
|
|
|
if (Itm.Size > SIZE_MAX) |
|
goto to_large; |
|
if (copy == NULL || copy_size < Itm.Size) { |
|
delete[] copy; |
|
copy = new (std::nothrow) char[Itm.Size]; |
|
if (copy == NULL) |
|
goto to_large; |
|
copy_size = Itm.Size; |
|
} |
|
Fd = -2; |
|
} else { |
|
Fd = -1; |
|
} |
|
return true; |
|
to_large: |
|
delete[] copy; |
|
copy = NULL; |
|
copy_size = 0; |
|
|
|
if (member) { |
|
error = true; |
|
PyErr_Format(PyExc_MemoryError, |
|
"The member %s was too large to read into memory", |
|
Itm.Name); |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
#if (APT_PKG_MAJOR >= 5) |
|
bool PyDirStream::Process(Item &Itm,const unsigned char *Data, |
|
unsigned long long Size,unsigned long long Pos) |
|
#else |
|
bool PyDirStream::Process(Item &Itm,const unsigned char *Data, |
|
unsigned long Size,unsigned long Pos) |
|
#endif |
|
{ |
|
if (copy != NULL) |
|
memcpy(copy + Pos, Data,Size); |
|
return true; |
|
} |
|
|
|
bool PyDirStream::FinishedFile(Item &Itm,int Fd) |
|
{ |
|
if (member && strcmp(Itm.Name, member) != 0) |
|
|
|
return true; |
|
|
|
Py_XDECREF(py_data); |
|
if (copy == NULL) { |
|
Py_INCREF(Py_None); |
|
py_data = Py_None; |
|
} else { |
|
py_data = PyBytes_FromStringAndSize(copy, Itm.Size); |
|
} |
|
|
|
if (!callback) |
|
return true; |
|
|
|
|
|
CppPyObject<Item> *py_member; |
|
py_member = CppPyObject_NEW<Item>(0, &PyTarMember_Type); |
|
|
|
py_member->Object = Itm; |
|
py_member->Object.Name = new char[strlen(Itm.Name)+1]; |
|
py_member->Object.LinkTarget = new char[strlen(Itm.LinkTarget)+1]; |
|
strcpy(py_member->Object.Name, Itm.Name); |
|
strcpy(py_member->Object.LinkTarget,Itm.LinkTarget); |
|
py_member->NoDelete = true; |
|
error = PyObject_CallFunctionObjArgs(callback, py_member, py_data, 0) == 0; |
|
|
|
Py_XDECREF(py_member); |
|
return (!error); |
|
} |
|
|
|
void tarmember_dealloc(PyObject *self) { |
|
|
|
delete[] GetCpp<pkgDirStream::Item>(self).Name; |
|
delete[] GetCpp<pkgDirStream::Item>(self).LinkTarget; |
|
CppDealloc<pkgDirStream::Item>(self); |
|
} |
|
|
|
|
|
static PyObject *tarmember_isblk(PyObject *self, PyObject *args) |
|
{ |
|
return PyBool_FromLong(GetCpp<pkgDirStream::Item>(self).Type == |
|
pkgDirStream::Item::BlockDevice); |
|
} |
|
static PyObject *tarmember_ischr(PyObject *self, PyObject *args) |
|
{ |
|
return PyBool_FromLong(GetCpp<pkgDirStream::Item>(self).Type == |
|
pkgDirStream::Item::CharDevice); |
|
} |
|
static PyObject *tarmember_isdev(PyObject *self, PyObject *args) |
|
{ |
|
pkgDirStream::Item::Type_t type = GetCpp<pkgDirStream::Item>(self).Type; |
|
return PyBool_FromLong(type == pkgDirStream::Item::CharDevice || |
|
type == pkgDirStream::Item::BlockDevice || |
|
type == pkgDirStream::Item::FIFO); |
|
} |
|
|
|
static PyObject *tarmember_isdir(PyObject *self, PyObject *args) |
|
{ |
|
return PyBool_FromLong(GetCpp<pkgDirStream::Item>(self).Type == |
|
pkgDirStream::Item::Directory); |
|
} |
|
|
|
static PyObject *tarmember_isfifo(PyObject *self, PyObject *args) |
|
{ |
|
return PyBool_FromLong(GetCpp<pkgDirStream::Item>(self).Type == |
|
pkgDirStream::Item::FIFO); |
|
} |
|
|
|
static PyObject *tarmember_isfile(PyObject *self, PyObject *args) |
|
{ |
|
return PyBool_FromLong(GetCpp<pkgDirStream::Item>(self).Type == |
|
pkgDirStream::Item::File); |
|
} |
|
static PyObject *tarmember_islnk(PyObject *self, PyObject *args) |
|
{ |
|
return PyBool_FromLong(GetCpp<pkgDirStream::Item>(self).Type == |
|
pkgDirStream::Item::HardLink); |
|
} |
|
static PyObject *tarmember_isreg(PyObject *self, PyObject *args) |
|
{ |
|
return tarmember_isfile(self, NULL); |
|
} |
|
static PyObject *tarmember_issym(PyObject *self, PyObject *args) |
|
{ |
|
return PyBool_FromLong(GetCpp<pkgDirStream::Item>(self).Type == |
|
pkgDirStream::Item::SymbolicLink); |
|
} |
|
|
|
static PyObject *tarmember_get_name(PyObject *self, void *closure) |
|
{ |
|
return CppPyPath(GetCpp<pkgDirStream::Item>(self).Name); |
|
} |
|
|
|
static PyObject *tarmember_get_linkname(PyObject *self, void *closure) |
|
{ |
|
return CppPyPath(GetCpp<pkgDirStream::Item>(self).LinkTarget); |
|
} |
|
|
|
static PyObject *tarmember_get_mode(PyObject *self, void *closure) |
|
{ |
|
return MkPyNumber(GetCpp<pkgDirStream::Item>(self).Mode); |
|
} |
|
|
|
static PyObject *tarmember_get_uid(PyObject *self, void *closure) |
|
{ |
|
return MkPyNumber(GetCpp<pkgDirStream::Item>(self).UID); |
|
} |
|
static PyObject *tarmember_get_gid(PyObject *self, void *closure) |
|
{ |
|
return MkPyNumber(GetCpp<pkgDirStream::Item>(self).GID); |
|
} |
|
static PyObject *tarmember_get_size(PyObject *self, void *closure) |
|
{ |
|
return MkPyNumber(GetCpp<pkgDirStream::Item>(self).Size); |
|
} |
|
|
|
static PyObject *tarmember_get_mtime(PyObject *self, void *closure) |
|
{ |
|
return MkPyNumber(GetCpp<pkgDirStream::Item>(self).MTime); |
|
} |
|
|
|
static PyObject *tarmember_get_major(PyObject *self, void *closure) |
|
{ |
|
return MkPyNumber(GetCpp<pkgDirStream::Item>(self).Major); |
|
} |
|
|
|
static PyObject *tarmember_get_minor(PyObject *self, void *closure) |
|
{ |
|
return MkPyNumber(GetCpp<pkgDirStream::Item>(self).Minor); |
|
} |
|
|
|
static PyObject *tarmember_repr(PyObject *self) |
|
{ |
|
return PyString_FromFormat("<%s object: name:'%s'>", |
|
self->ob_type->tp_name, |
|
GetCpp<pkgDirStream::Item>(self).Name); |
|
|
|
} |
|
|
|
|
|
static PyMethodDef tarmember_methods[] = { |
|
{"isblk",tarmember_isblk,METH_NOARGS, |
|
"Determine whether the member is a block device."}, |
|
{"ischr",tarmember_ischr,METH_NOARGS, |
|
"Determine whether the member is a character device."}, |
|
{"isdev",tarmember_isdev,METH_NOARGS, |
|
"Determine whether the member is a device (block, character or FIFO)."}, |
|
{"isdir",tarmember_isdir,METH_NOARGS, |
|
"Determine whether the member is a directory."}, |
|
{"isfifo",tarmember_isfifo,METH_NOARGS, |
|
"Determine whether the member is a FIFO."}, |
|
{"isfile",tarmember_isfile,METH_NOARGS, |
|
"Determine whether the member is a regular file."}, |
|
{"islnk",tarmember_islnk,METH_NOARGS, |
|
"Determine whether the member is a hardlink."}, |
|
{"isreg",tarmember_isreg,METH_NOARGS, |
|
"Determine whether the member is a regular file, same as isfile()."}, |
|
{"issym",tarmember_issym,METH_NOARGS, |
|
"Determine whether the member is a symbolic link."}, |
|
{NULL} |
|
}; |
|
|
|
static PyGetSetDef tarmember_getset[] = { |
|
{"gid",tarmember_get_gid,0,"The owner's group ID."}, |
|
{"linkname",tarmember_get_linkname,0,"The target of the link."}, |
|
{"major",tarmember_get_major,0,"The major ID of the device."}, |
|
{"minor",tarmember_get_minor,0,"The minor ID of the device."}, |
|
{"mode",tarmember_get_mode,0,"The mode (permissions)."}, |
|
{"mtime",tarmember_get_mtime,0,"Last time of modification."}, |
|
{"name",tarmember_get_name,0,"The name of the file."}, |
|
{"size",tarmember_get_size,0,"The size of the file."}, |
|
{"uid",tarmember_get_uid,0,"The owner's user ID."}, |
|
{NULL} |
|
}; |
|
|
|
static const char *tarmember_doc = |
|
"Represent a single member of a 'tar' archive.\n\n" |
|
"This class, which has been modelled after 'tarfile.TarInfo', represents\n" |
|
"information about a given member in an archive."; |
|
PyTypeObject PyTarMember_Type = { |
|
PyVarObject_HEAD_INIT(&PyType_Type, 0) |
|
"apt_inst.TarMember", |
|
sizeof(CppPyObject<pkgDirStream::Item>), |
|
0, |
|
|
|
tarmember_dealloc, |
|
0, |
|
0, |
|
0, |
|
0, |
|
tarmember_repr, |
|
0, |
|
0, |
|
0, |
|
0, |
|
0, |
|
0, |
|
0, |
|
0, |
|
0, |
|
Py_TPFLAGS_DEFAULT | |
|
Py_TPFLAGS_HAVE_GC, |
|
tarmember_doc, |
|
CppTraverse<pkgDirStream::Item>, |
|
CppClear<pkgDirStream::Item>, |
|
0, |
|
0, |
|
0, |
|
0, |
|
tarmember_methods, |
|
0, |
|
tarmember_getset |
|
}; |
|
|
|
|
|
|
|
static PyObject *tarfile_new(PyTypeObject *type,PyObject *args,PyObject *kwds) |
|
{ |
|
PyObject *file; |
|
PyApt_Filename filename; |
|
int fileno; |
|
int min = 0; |
|
int max = 0xFFFFFFFF; |
|
char *comp = "gzip"; |
|
|
|
static char *kwlist[] = {"file","min","max","comp",NULL}; |
|
if (PyArg_ParseTupleAndKeywords(args, kwds, "O|iis", kwlist, &file, &min, |
|
&max,&comp) == 0) |
|
return 0; |
|
|
|
PyApt_UniqueObject<PyTarFileObject> self((PyTarFileObject*)CppPyObject_NEW<ExtractTar*>(file,type)); |
|
|
|
|
|
if (filename.init(file)) |
|
new (&self->Fd) FileFd((const char *) filename,FileFd::ReadOnly); |
|
else if ((fileno = PyObject_AsFileDescriptor(file)) != -1) { |
|
|
|
PyErr_Clear(); |
|
new (&self->Fd) FileFd(fileno,false); |
|
} |
|
else { |
|
return 0; |
|
} |
|
|
|
self->min = min; |
|
self->Object = new ExtractTar(self->Fd,max,comp); |
|
if (_error->PendingError() == true) |
|
return HandleErrors(self.release()); |
|
return self.release(); |
|
} |
|
|
|
static const char *tarfile_extractall_doc = |
|
"extractall([rootdir: str]) -> True\n\n" |
|
"Extract the archive in the current directory. The argument 'rootdir'\n" |
|
"can be used to change the target directory."; |
|
static PyObject *tarfile_extractall(PyObject *self, PyObject *args) |
|
{ |
|
std::string cwd = SafeGetCWD(); |
|
PyApt_Filename rootdir; |
|
if (PyArg_ParseTuple(args,"|O&:extractall", PyApt_Filename::Converter, &rootdir) == 0) |
|
return 0; |
|
|
|
if (rootdir) { |
|
if (chdir(rootdir) == -1) |
|
return PyErr_SetFromErrnoWithFilename(PyExc_OSError, rootdir); |
|
} |
|
|
|
pkgDirStream Extract; |
|
|
|
((PyTarFileObject*)self)->Fd.Seek(((PyTarFileObject*)self)->min); |
|
bool res = GetCpp<ExtractTar*>(self)->Go(Extract); |
|
|
|
|
|
|
|
if (rootdir) { |
|
if (chdir(cwd.c_str()) == -1) |
|
return PyErr_SetFromErrnoWithFilename(PyExc_OSError, |
|
(char*)cwd.c_str()); |
|
} |
|
return HandleErrors(PyBool_FromLong(res)); |
|
} |
|
|
|
static const char *tarfile_go_doc = |
|
"go(callback: callable[, member: str]) -> True\n\n" |
|
"Go through the archive and call the callable 'callback' for each\n" |
|
"member with 2 arguments. The first argument is the TarMember and\n" |
|
"the second one is the data, as bytes.\n\n" |
|
"The optional parameter 'member' can be used to specify the member for\n" |
|
"which to call the callback. If not specified, it will be called for all\n" |
|
"members. If specified and not found, LookupError will be raised."; |
|
static PyObject *tarfile_go(PyObject *self, PyObject *args) |
|
{ |
|
PyObject *callback; |
|
PyApt_Filename member; |
|
if (PyArg_ParseTuple(args,"O|O&",&callback, PyApt_Filename::Converter, &member) == 0) |
|
return 0; |
|
if (member && strcmp(member, "") == 0) |
|
member = 0; |
|
pkgDirStream Extract; |
|
PyDirStream stream(callback, member); |
|
((PyTarFileObject*)self)->Fd.Seek(((PyTarFileObject*)self)->min); |
|
bool res = GetCpp<ExtractTar*>(self)->Go(stream); |
|
if (stream.error) |
|
return 0; |
|
if (member && !stream.py_data) |
|
return PyErr_Format(PyExc_LookupError, "There is no member named '%s'", |
|
member.path); |
|
return HandleErrors(PyBool_FromLong(res)); |
|
} |
|
|
|
static const char *tarfile_extractdata_doc = |
|
"extractdata(member: str) -> bytes\n\n" |
|
"Return the contents of the member, as a bytes object. Raise\n" |
|
"LookupError if there is no member with the given name."; |
|
static PyObject *tarfile_extractdata(PyObject *self, PyObject *args) |
|
{ |
|
PyApt_Filename member; |
|
if (PyArg_ParseTuple(args,"O&", PyApt_Filename::Converter, &member) == 0) |
|
return 0; |
|
PyDirStream stream(NULL, member); |
|
((PyTarFileObject*)self)->Fd.Seek(((PyTarFileObject*)self)->min); |
|
|
|
GetCpp<ExtractTar*>(self)->Go(stream); |
|
|
|
if (stream.error) |
|
return 0; |
|
|
|
if (!stream.py_data) |
|
return PyErr_Format(PyExc_LookupError, "There is no member named '%s'", |
|
member.path); |
|
return Py_INCREF(stream.py_data), stream.py_data; |
|
} |
|
|
|
static PyMethodDef tarfile_methods[] = { |
|
{"extractdata",tarfile_extractdata,METH_VARARGS,tarfile_extractdata_doc}, |
|
{"extractall",tarfile_extractall,METH_VARARGS,tarfile_extractall_doc}, |
|
{"go",tarfile_go,METH_VARARGS,tarfile_go_doc}, |
|
{NULL} |
|
}; |
|
|
|
static PyObject *tarfile_repr(PyObject *self) |
|
{ |
|
return PyString_FromFormat("<%s object: %s>", self->ob_type->tp_name, |
|
PyString_AsString(PyObject_Repr(GetOwner<ExtractTar*>(self)))); |
|
} |
|
|
|
static const char *tarfile_doc = |
|
"TarFile(file: str/int/file[, min: int, max: int, comp: str])\n\n" |
|
"The parameter 'file' may be a string specifying the path of a file, or\n" |
|
"a file-like object providing the fileno() method. It may also be an int\n" |
|
"specifying a file descriptor (returned by e.g. os.open()).\n\n" |
|
"The parameter 'min' describes the offset in the file where the archive\n" |
|
"begins and the parameter 'max' is the size of the archive.\n\n" |
|
"The compression of the archive is set by the parameter 'comp'. It can\n" |
|
"be set to any program supporting the -d switch, the default being gzip."; |
|
PyTypeObject PyTarFile_Type = { |
|
PyVarObject_HEAD_INIT(&PyType_Type, 0) |
|
"apt_inst.TarFile", |
|
sizeof(PyTarFileObject), |
|
0, |
|
|
|
CppDealloc<ExtractTar*>, |
|
0, |
|
0, |
|
0, |
|
0, |
|
tarfile_repr, |
|
0, |
|
0, |
|
0, |
|
0, |
|
0, |
|
0, |
|
0, |
|
0, |
|
0, |
|
Py_TPFLAGS_DEFAULT | |
|
Py_TPFLAGS_HAVE_GC, |
|
tarfile_doc, |
|
CppTraverse<ExtractTar*>, |
|
CppClear<ExtractTar*>, |
|
0, |
|
0, |
|
0, |
|
0, |
|
tarfile_methods, |
|
0, |
|
0, |
|
0, |
|
0, |
|
0, |
|
0, |
|
0, |
|
0, |
|
0, |
|
tarfile_new |
|
}; |
|
|