2 * Copyright (c) 2008-2012 Zmanda, Inc. All Rights Reserved.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
19 * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
22 %module "Amanda::Archive"
23 %include "amglue/amglue.swg"
24 %include "exception.i"
27 %include "Amanda/Archive.pod"
34 /* Support code (not directly available from perl) */
36 #define AMANDA_ARCHIVE_ERROR_DOMAIN "Amanda archive"
38 /* A C object to contain all of the relevant callbacks and other state during a
39 * read operation; this becomes the user_data during the read */
40 typedef struct perl_read_data_s {
45 amar_attr_handling_t *handling_array;
58 perl_read_data_t *dat = user_data;
65 g_assert(dat->file_start_sub != NULL);
71 XPUSHs(dat->user_data);
72 XPUSHs(sv_2mortal(newSViv(filenum)));
73 XPUSHs(sv_2mortal(newSVpvn(filename, filename_len)));
76 count = call_sv(dat->file_start_sub, G_EVAL|G_SCALAR);
81 croak("file_start_sub returned nothing");
85 /* if it's the string "IGNORE", then ignore it */
87 static const char *ign = "IGNORE";
88 char *rvstr = SvPV(rv, len);
89 if (strlen(ign) == len && 0 == strncmp(ign, rvstr, len))
93 /* otherwise, keep the value */
95 *(SV **)(file_data) = SvREFCNT_inc(rv);
114 perl_read_data_t *dat = user_data;
116 g_assert(dat->file_finish_sub != NULL);
121 PUSHMARK(SP); XPUSHs(dat->user_data); XPUSHs(*(SV **)file_data);
122 XPUSHs(sv_2mortal(newSViv(filenum)));
123 XPUSHs(sv_2mortal(newSViv(truncated))); PUTBACK;
125 call_sv(dat->file_finish_sub, G_EVAL|G_DISCARD);
127 /* we're done with this file's file_data */
128 SvREFCNT_dec(*(SV **)file_data);
144 gpointer attrid_data,
152 perl_read_data_t *dat = user_data;
163 XPUSHs(dat->user_data);
164 XPUSHs(sv_2mortal(newSViv(filenum)));
165 XPUSHs((SV *)file_data);
166 XPUSHs(sv_2mortal(newSViv(attrid)));
168 XPUSHs((SV *)(*attr_data));
170 XPUSHs(&PL_sv_undef);
171 XPUSHs(sv_2mortal(newSVpvn(data, size)));
172 XPUSHs(sv_2mortal(newSViv(eoa)));
173 XPUSHs(sv_2mortal(newSViv(truncated)));
176 count = call_sv(attrid_data, G_EVAL|G_SCALAR);
181 croak("fragment callback returned nothing");
186 SvREFCNT_dec(*attr_data);
188 /* increment before decrement here, in case they're the same object */
190 SvREFCNT_dec(*attr_data);
202 /* generic function to recognize when a string+len represents a number and
203 * incidentally return the resulting value. Note that this does not handle
204 * negative numbers. */
206 is_number(char *str, int len, int *result)
212 if (!g_ascii_isdigit(*str)) return FALSE;
213 r = r * 10 + (int)(*str - '0');
227 /* Rename all of the below wrapper functions (suffixed with '_') for
228 * consumption by perl */
229 %rename(amar_new) amar_new_;
230 %rename(amar_close) amar_close_;
231 %rename(amar_new_file) amar_new_file_;
232 %rename(amar_file_close) amar_file_close_;
233 %rename(amar_new_attr) amar_new_attr_;
234 %rename(amar_attr_close) amar_attr_close_;
235 %rename(amar_attr_add_data_buffer) amar_attr_add_data_buffer_;
236 %rename(amar_attr_add_data_fd) amar_attr_add_data_fd_;
237 %rename(amar_read) amar_read_;
239 /* typemaps for the below */
240 %apply (char *STRING, int LENGTH) { (char *filename, gsize filename_len) };
241 %apply (char *STRING, int LENGTH) { (char *buffer, gsize size) };
242 %typemap(in) SV * "$1 = $input;"
244 %typemap(in) off_t *want_position (off_t position) {
245 if (SvTRUE($input)) {
252 %typemap(argout) off_t *want_position {
254 SP += argvi; PUTBACK;
255 $result = sv_2mortal(amglue_newSVi64(*$1));
256 SPAGAIN; SP -= argvi; argvi++;
262 /* Wrapper functions, mostly dealing with error handling */
264 amar_t *amar_new_(int fd, char *modestr) {
265 GError *error = NULL;
269 if (strcmp(modestr, ">") == 0)
271 else if (strcmp(modestr, "<") == 0)
274 croak("mode must be '<' or '>'");
276 if ((rv = amar_new(fd, mode, &error))) {
280 croak_gerror(AMANDA_ARCHIVE_ERROR_DOMAIN, &error);
284 void amar_close_(amar_t *arch) {
285 GError *error = NULL;
286 if (!amar_close(arch, &error))
287 croak_gerror(AMANDA_ARCHIVE_ERROR_DOMAIN, &error);
291 amar_new_file_(amar_t *arch, char *filename, gsize filename_len, off_t *want_position) {
292 GError *error = NULL;
294 g_assert(arch != NULL);
296 file = amar_new_file(arch, filename, filename_len, want_position, &error);
300 croak_gerror(AMANDA_ARCHIVE_ERROR_DOMAIN, &error);
304 void amar_file_close_(amar_file_t *file) {
305 GError *error = NULL;
306 if (!amar_file_close(file, &error))
307 croak_gerror(AMANDA_ARCHIVE_ERROR_DOMAIN, &error);
311 amar_new_attr_(amar_file_t *file, guint16 attrid) {
312 GError *error = NULL;
315 g_assert(file != NULL);
317 attr = amar_new_attr(file, attrid, &error);
321 croak_gerror(AMANDA_ARCHIVE_ERROR_DOMAIN, &error);
325 void amar_attr_close_(amar_attr_t *attr) {
326 GError *error = NULL;
327 if (!amar_attr_close(attr, &error))
328 croak_gerror(AMANDA_ARCHIVE_ERROR_DOMAIN, &error);
331 void amar_attr_add_data_buffer_(amar_attr_t *attr, char *buffer, gsize size, gboolean eoa) {
332 GError *error = NULL;
333 if (!amar_attr_add_data_buffer(attr, buffer, size, eoa, &error))
334 croak_gerror(AMANDA_ARCHIVE_ERROR_DOMAIN, &error);
338 amar_attr_add_data_fd_(amar_attr_t *attr, int fd, gboolean eoa) {
339 GError *error = NULL;
340 size_t rv = amar_attr_add_data_fd(attr, fd, eoa, &error);
342 croak_gerror(AMANDA_ARCHIVE_ERROR_DOMAIN, &error);
348 void amar_read_(amar_t *archive, SV *params_hashref) {
349 perl_read_data_t *dat = g_new0(perl_read_data_t, 1);
350 GError *error = NULL;
358 /* make sure we got a hashref */
359 if (!SvROK(params_hashref) || SvTYPE(SvRV(params_hashref)) != SVt_PVHV)
360 croak("read() expects a single hashref");
361 params = (HV *)SvRV(params_hashref);
362 len = hv_iterinit(params);
364 maxhandlers = hdl_idx = len;
365 dat->handling_array = g_new0(amar_attr_handling_t, len+1);
367 /* loop through the parameters */
368 while ((param = hv_iternext(params))) {
370 char *key = hv_iterkey(param, &keylen);
373 /* if it's a number, it's handling information for an attrid */
374 if (is_number(key, keylen, &attrid)) {
375 SV *val = hv_iterval(params, param);
380 if (!SvROK(val)) goto croak_hdl;
382 switch (SvTYPE(SvRV(val))) {
388 AV *arr = (AV *)SvRV(val);
391 if (av_len(arr) != 1) /* av_len == largest index, not length */
394 /* get the bufsize */
395 svp = av_fetch(arr, 0, 0);
398 bufsize = SvUV(*svp);
400 /* and the coderef */
401 svp = av_fetch(arr, 1, 0);
402 if (!SvROK(*svp) || SvTYPE(SvRV(*svp)) != SVt_PVCV)
412 /* fill in the handling array, putting attrid 0 at the end, and
413 * filling in entries backward from there */
414 i = (attrid == 0)? maxhandlers : --hdl_idx;
415 dat->handling_array[i].attrid = attrid;
416 dat->handling_array[i].min_size = bufsize;
417 dat->handling_array[i].callback = read_frag_cb;
418 dat->handling_array[i].attrid_data = coderef;
419 SvREFCNT_inc(coderef);
423 croak("Expected CODEREF or [ MIN_SIZE, CODEREF ] for attrid %d", attrid);
426 #define key_compare(key, val, keylen) \
427 (keylen == sizeof(val)-1) && (0 == strncmp(key, val, keylen))
429 if (key_compare(key, "file_start", keylen)) {
430 SV *val = hv_iterval(params, param);
431 if (!SvROK(val) || SvTYPE(SvRV(val)) != SVt_PVCV)
432 croak("Expected a CODEREF for file_start");
433 dat->file_start_sub = val;
438 if (key_compare(key, "file_finish", keylen)) {
439 SV *val = hv_iterval(params, param);
440 if (!SvROK(val) || SvTYPE(SvRV(val)) != SVt_PVCV)
441 croak("Expected a CODEREF for file_finish");
442 dat->file_finish_sub = val;
447 if (key_compare(key, "user_data", keylen)) {
448 SV *val = hv_iterval(params, param);
449 dat->user_data = val;
454 croak("Invalid parameter named '%*s'", (int)keylen, key);
458 dat->user_data = &PL_sv_undef;
460 success = amar_read(archive, dat, dat->handling_array + hdl_idx,
461 dat->file_start_sub? read_start_file_cb : NULL,
462 dat->file_finish_sub? read_finish_file_cb : NULL,
465 /* now unreference and free everything we referenced earlier */
466 if (dat->file_start_sub)
467 SvREFCNT_dec(dat->file_start_sub);
468 if (dat->file_finish_sub)
469 SvREFCNT_dec(dat->file_finish_sub);
470 if (dat->user_data && dat->user_data != &PL_sv_undef)
471 SvREFCNT_dec(dat->user_data);
473 for (hdl_idx = 0; hdl_idx <= maxhandlers; hdl_idx++) {
474 if (dat->handling_array[hdl_idx].attrid_data)
475 SvREFCNT_dec(dat->handling_array[hdl_idx].attrid_data);
478 g_free(dat->handling_array);
481 /* if amar_read returned FALSE, then either we hit an internal
482 * error, or one of the perl callbacks raised an exception, and $@
486 croak_gerror(AMANDA_ARCHIVE_ERROR_DOMAIN, &error);
494 /* now wrap those flat functions in Perl classes, depending on the perl
495 * refcounting to close objects in the right order */
498 package Amanda::Archive;
500 # Expose the Archive constructor at Amanda::Archive->new
503 Amanda::Archive::Archive->new(@_);
506 package Amanda::Archive::Archive;
509 my ($class, $fd, $mode) = @_;
510 my $arch = Amanda::Archive::amar_new($fd, $mode);
511 return bless (\$arch, $class);
517 Amanda::Archive::amar_close($$self);
528 my ($self, $filename, $want_offset) = @_;
529 return Amanda::Archive::File->new($self, $filename, $want_offset);
532 sub Amanda::Archive::Archive::read {
534 die "Archive is not open" unless ($$self);
535 # pass a hashref to the C code
537 Amanda::Archive::amar_read($$self, \%h);
540 package Amanda::Archive::File;
543 my ($class, $arch, $filename, $want_offset) = @_;
544 die "Archive is not open" unless ($$arch);
546 # note that posn is returned first by the SWIG wrapper
547 my ($file, $posn) = Amanda::Archive::amar_new_file($$arch, $filename, $want_offset);
548 return (bless([ $file, $arch ], $class), $posn);
550 my $file = Amanda::Archive::amar_new_file($$arch, $filename, $want_offset);
551 return bless([ $file, $arch ], $class);
558 Amanda::Archive::amar_file_close($self->[0]);
569 my ($self, $attrid) = @_;
570 return Amanda::Archive::Attr->new($self, $attrid);
573 package Amanda::Archive::Attr;
576 my ($class, $file, $attrid) = @_;
577 die "File is not open" unless ($file->[0]);
578 my $attr = Amanda::Archive::amar_new_attr($file->[0], $attrid);
579 return bless ([$attr, $file], $class);
585 Amanda::Archive::amar_attr_close($self->[0]);
596 my ($self, $data, $eoa) = @_;
597 die "Attr is not open" unless ($self->[0]);
598 Amanda::Archive::amar_attr_add_data_buffer($self->[0], $data, $eoa);
602 my ($self, $fd, $eoa) = @_;
603 die "Attr is not open" unless ($self->[0]);
604 return Amanda::Archive::amar_attr_add_data_fd($self->[0], $fd, $eoa);