From: Kern Sibbald Date: Thu, 8 Mar 2012 08:07:19 +0000 (+0100) Subject: Add test-deltaseq source X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=ebadc0985391cd89bf51144a2bf977082d51d821;p=bacula%2Fbacula Add test-deltaseq source --- diff --git a/bacula/src/plugins/fd/test-deltaseq-fd.c b/bacula/src/plugins/fd/test-deltaseq-fd.c new file mode 100644 index 0000000000..b365711901 --- /dev/null +++ b/bacula/src/plugins/fd/test-deltaseq-fd.c @@ -0,0 +1,499 @@ +/* + Bacula® - The Network Backup Solution + + Copyright (C) 2007-2011 Free Software Foundation Europe e.V. + + The main author of Bacula is Kern Sibbald, with contributions from + many others, a complete list can be found in the file AUTHORS. + This program is Free Software; you can redistribute it and/or + modify it under the terms of version three of the GNU Affero General Public + License as published by the Free Software Foundation, which is + listed in the file LICENSE. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + + Bacula® is a registered trademark of Kern Sibbald. + The licensor of Bacula is the Free Software Foundation Europe + (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, + Switzerland, email:ftf@fsfeurope.org. +*/ +/* + * A simple delta plugin for the Bacula File Daemon + * + * + */ +#include "bacula.h" +#include "fd_plugins.h" +#include "fd_common.h" + +#undef malloc +#undef free +#undef strdup + +#ifdef __cplusplus +extern "C" { +#endif + +static const int dbglvl = 0; + +#define PLUGIN_LICENSE "Bacula AGPLv3" +#define PLUGIN_AUTHOR "Eric Bollengier" +#define PLUGIN_DATE "November 2010" +#define PLUGIN_VERSION "1" +#define PLUGIN_DESCRIPTION "Bacula Delta Test Plugin" + +/* Forward referenced functions */ +static bRC newPlugin(bpContext *ctx); +static bRC freePlugin(bpContext *ctx); +static bRC getPluginValue(bpContext *ctx, pVariable var, void *value); +static bRC setPluginValue(bpContext *ctx, pVariable var, void *value); +static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value); +static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp); +static bRC endBackupFile(bpContext *ctx); +static bRC pluginIO(bpContext *ctx, struct io_pkt *io); +static bRC startRestoreFile(bpContext *ctx, const char *cmd); +static bRC endRestoreFile(bpContext *ctx); +static bRC createFile(bpContext *ctx, struct restore_pkt *rp); +static bRC setFileAttributes(bpContext *ctx, struct restore_pkt *rp); + +/* Pointers to Bacula functions */ +static bFuncs *bfuncs = NULL; +static bInfo *binfo = NULL; + +/* Plugin Information block */ +static pInfo pluginInfo = { + sizeof(pluginInfo), + FD_PLUGIN_INTERFACE_VERSION, + FD_PLUGIN_MAGIC, + PLUGIN_LICENSE, + PLUGIN_AUTHOR, + PLUGIN_DATE, + PLUGIN_VERSION, + PLUGIN_DESCRIPTION, +}; + +/* Plugin entry points for Bacula */ +static pFuncs pluginFuncs = { + sizeof(pluginFuncs), + FD_PLUGIN_INTERFACE_VERSION, + + /* Entry points into plugin */ + newPlugin, /* new plugin instance */ + freePlugin, /* free plugin instance */ + getPluginValue, + setPluginValue, + handlePluginEvent, + startBackupFile, + endBackupFile, + startRestoreFile, + endRestoreFile, + pluginIO, + createFile, + setFileAttributes, + NULL /* no checkFile */ +}; + +#define get_self(x) ((delta_test*)((x)->pContext)) +#define FO_DELTA (1<<28) /* Do delta on file */ +#define FO_OFFSETS (1<<30) /* Keep block offsets */ + +class delta_test +{ +private: + bpContext *ctx; + +public: + POOLMEM *fname; /* Filename to save */ + int32_t delta; + FILE *fd; + bool done; + int level; + + delta_test(bpContext *bpc) { + fd = NULL; + ctx = bpc; + done = false; + level = 0; + delta = 0; + fname = get_pool_memory(PM_FNAME); + } + ~delta_test() { + free_and_null_pool_memory(fname); + } +}; + +/* + * loadPlugin() and unloadPlugin() are entry points that are + * exported, so Bacula can directly call these two entry points + * they are common to all Bacula plugins. + */ +/* + * External entry point called by Bacula to "load the plugin + */ +bRC loadPlugin(bInfo *lbinfo, bFuncs *lbfuncs, pInfo **pinfo, pFuncs **pfuncs) +{ + bfuncs = lbfuncs; /* set Bacula funct pointers */ + binfo = lbinfo; + *pinfo = &pluginInfo; /* return pointer to our info */ + *pfuncs = &pluginFuncs; /* return pointer to our functions */ + + /* Activate this plugin only in developer mode */ +#ifdef DEVELOPER + return bRC_OK; +#else + return bRC_Error; +#endif +} + +/* + * External entry point to unload the plugin + */ +bRC unloadPlugin() +{ +// Dmsg(NULL, dbglvl, "delta-test-fd: Unloaded\n"); + return bRC_OK; +} + +/* + * The following entry points are accessed through the function + * pointers we supplied to Bacula. Each plugin type (dir, fd, sd) + * has its own set of entry points that the plugin must define. + */ +/* + * Create a new instance of the plugin i.e. allocate our private storage + */ +static bRC newPlugin(bpContext *ctx) +{ + delta_test *self = new delta_test(ctx); + if (!self) { + return bRC_Error; + } + ctx->pContext = (void *)self; /* set our context pointer */ + return bRC_OK; +} + +/* + * Free a plugin instance, i.e. release our private storage + */ +static bRC freePlugin(bpContext *ctx) +{ + delta_test *self = get_self(ctx); + if (!self) { + return bRC_Error; + } + delete self; + return bRC_OK; +} + +/* + * Return some plugin value (none defined) + */ +static bRC getPluginValue(bpContext *ctx, pVariable var, void *value) +{ + return bRC_OK; +} + +/* + * Set a plugin value (none defined) + */ +static bRC setPluginValue(bpContext *ctx, pVariable var, void *value) +{ + return bRC_OK; +} + +/* + * Handle an event that was generated in Bacula + */ +static bRC handlePluginEvent(bpContext *ctx, bEvent *event, void *value) +{ + delta_test *self = get_self(ctx); + int accurate=0; + + if (!self) { + return bRC_Error; + } + +// char *name; + + /* + * Most events don't interest us so we ignore them. + * the printfs are so that plugin writers can enable them to see + * what is really going on. + */ + switch (event->eventType) { + case bEventPluginCommand: +// Dmsg(ctx, dbglvl, +// "delta-test-fd: PluginCommand=%s\n", (char *)value); + break; + case bEventJobStart: +// Dmsg(ctx, dbglvl, "delta-test-fd: JobStart=%s\n", (char *)value); + break; + case bEventJobEnd: +// Dmsg(ctx, dbglvl, "delta-test-fd: JobEnd\n"); + break; + case bEventStartBackupJob: +// Dmsg(ctx, dbglvl, "delta-test-fd: StartBackupJob\n"); + break; + case bEventEndBackupJob: +// Dmsg(ctx, dbglvl, "delta-test-fd: EndBackupJob\n"); + break; + case bEventLevel: +// Dmsg(ctx, dbglvl, "delta-test-fd: JobLevel=%c %d\n", (int)value, (int)value); + self->level = (int)(intptr_t)value; + break; + case bEventSince: +// Dmsg(ctx, dbglvl, "delta-test-fd: since=%d\n", (int)value); + break; + + case bEventStartRestoreJob: +// Dmsg(ctx, dbglvl, "delta-test-fd: StartRestoreJob\n"); + break; + + case bEventEndRestoreJob: +// Dmsg(ctx, dbglvl, "delta-test-fd: EndRestoreJob\n"); + break; + + /* Plugin command e.g. plugin = ::read command:write command */ + case bEventRestoreCommand: +// Dmsg(ctx, dbglvl, "delta-test-fd: EventRestoreCommand cmd=%s\n", (char *)value); + /* Fall-through wanted */ + break; + case bEventBackupCommand: + Dmsg(ctx, dbglvl, "delta-test-fd: pluginEvent cmd=%s\n", (char *)value); + if (self->level == 'I' || self->level == 'D') { + bfuncs->getBaculaValue(ctx, bVarAccurate, (void *)&accurate); + if (!accurate) { /* can be changed to FATAL */ + Jmsg(ctx, M_FATAL, + "Accurate mode should be turned on when using the " + "delta-test plugin\n"); + return bRC_Error; + } + } + break; + + default: +// Dmsg(ctx, dbglvl, "delta-test-fd: unknown event=%d\n", event->eventType); + break; + } + return bRC_OK; +} + +static const char *files[] = { + "/etc/passwd", + "/etc/group", + "/etc/hosts", + "/etc/services" +}; +static int nb_files = 4; + +/* + * Start the backup of a specific file + */ +static bRC startBackupFile(bpContext *ctx, struct save_pkt *sp) +{ + delta_test *self = get_self(ctx); + if (!self) { + return bRC_Error; + } + time_t now = time(NULL); + sp->fname = (char *)"/delta.txt"; + sp->type = FT_REG; + sp->statp.st_mode = 0700 | S_IFREG; + sp->statp.st_ctime = now; + sp->statp.st_mtime = now; + sp->statp.st_atime = now; + sp->statp.st_size = -1; + sp->statp.st_blksize = 4096; + sp->statp.st_blocks = 1; + if (self->level == 'I' || self->level == 'D') { + bRC state = bfuncs->checkChanges(ctx, sp); + /* Should always be bRC_OK */ + sp->type = (state == bRC_Seen)? FT_NOCHG : FT_REG; + sp->flags |= (FO_DELTA|FO_OFFSETS); + self->delta = sp->delta_seq + 1; + } + pm_strcpy(self->fname, files[self->delta % nb_files]); + Dmsg(ctx, dbglvl, "delta-test-fd: delta_seq=%i delta=%i fname=%s\n", + sp->delta_seq, self->delta, self->fname); +// Dmsg(ctx, dbglvl, "delta-test-fd: startBackupFile\n"); + return bRC_OK; +} + +/* + * Done with backup of this file + */ +static bRC endBackupFile(bpContext *ctx) +{ + /* + * We would return bRC_More if we wanted startBackupFile to be + * called again to backup another file + */ + return bRC_OK; +} + + +/* + * Bacula is calling us to do the actual I/O + */ +static bRC pluginIO(bpContext *ctx, struct io_pkt *io) +{ + delta_test *self = get_self(ctx); + struct stat statp; + if (!self) { + return bRC_Error; + } + + io->status = 0; + io->io_errno = 0; + switch(io->func) { + case IO_OPEN: + Dmsg(ctx, dbglvl, "delta-test-fd: IO_OPEN\n"); + if (io->flags & (O_CREAT | O_WRONLY)) { + /* TODO: if the file already exists, the result is undefined */ + if (stat(io->fname, &statp) == 0) { /* file exists */ + self->fd = fopen(io->fname, "r+"); + } else { + self->fd = fopen(io->fname, "w"); /* file doesn't exist,create it */ + } + if (!self->fd) { + io->io_errno = errno; + Jmsg(ctx, M_FATAL, + "Open failed: ERR=%s\n", strerror(errno)); + return bRC_Error; + } + + } else { + self->fd = fopen(self->fname, "r"); + if (!self->fd) { + io->io_errno = errno; + Jmsg(ctx, M_FATAL, + "Open failed: ERR=%s\n", strerror(errno)); + return bRC_Error; + } + } + break; + + case IO_READ: + if (!self->fd) { + Jmsg(ctx, M_FATAL, "Logic error: NULL read FD\n"); + return bRC_Error; + } + if (self->done) { + io->status = 0; + } else { + /* first time, read 300, then replace 50-250 by other data */ + if (self->delta == 0) { + io->status = fread(io->buf, 1, 400, self->fd); + } else { + io->offset = self->delta * 100 / 2; /* chunks are melted */ + io->status = fread(io->buf, 1, 100, self->fd); + } + Dmsg(ctx, dbglvl, "delta-test-fd: READ offset=%lld\n", (int64_t)io->offset); + self->done = true; + } + if (io->status == 0 && ferror(self->fd)) { + Jmsg(ctx, M_FATAL, + "Pipe read error: ERR=%s\n", strerror(errno)); + Dmsg(ctx, dbglvl, + "Pipe read error: ERR=%s\n", strerror(errno)); + return bRC_Error; + } + Dmsg(ctx, dbglvl, "offset=%d\n", io->offset); + break; + + case IO_WRITE: + if (!self->fd) { + Jmsg(ctx, M_FATAL, "Logic error: NULL write FD\n"); + return bRC_Error; + } + Dmsg(ctx, dbglvl, "delta-test-fd: WRITE count=%lld\n", (int64_t)io->count); + io->status = fwrite(io->buf, 1, io->count, self->fd); + if (io->status == 0 && ferror(self->fd)) { + Jmsg(ctx, M_FATAL, + "Pipe write error\n"); + Dmsg(ctx, dbglvl, + "Pipe read error: ERR=%s\n", strerror(errno)); + return bRC_Error; + } + break; + + case IO_CLOSE: + if (!self->fd) { + Jmsg(ctx, M_FATAL, "Logic error: NULL FD on delta close\n"); + return bRC_Error; + } + io->status = fclose(self->fd); + break; + + case IO_SEEK: + if (!self->fd) { + Jmsg(ctx, M_FATAL, "Logic error: NULL FD on delta close\n"); + return bRC_Error; + } + Dmsg(ctx, dbglvl, "delta-test-fd: SEEK offset=%lld\n", (int64_t)io->offset); + io->status = fseek(self->fd, io->offset, io->whence); + Dmsg(ctx, dbglvl, "after SEEK=%lld\n", (int64_t)ftell(self->fd)); + break; + } + return bRC_OK; +} + +/* + * Bacula is notifying us that a plugin name string was found, and + * passing us the plugin command, so we can prepare for a restore. + */ +static bRC startRestoreFile(bpContext *ctx, const char *cmd) +{ +// Dmsg(ctx, dbglvl, "delta-test-fd: startRestoreFile cmd=%s\n", cmd); + return bRC_OK; +} + +/* + * Bacula is notifying us that the plugin data has terminated, so + * the restore for this particular file is done. + */ +static bRC endRestoreFile(bpContext *ctx) +{ +// Dmsg(ctx, dbglvl, "delta-test-fd: endRestoreFile\n"); + return bRC_OK; +} + +/* + * This is called during restore to create the file (if necessary) + * We must return in rp->create_status: + * + * CF_ERROR -- error + * CF_SKIP -- skip processing this file + * CF_EXTRACT -- extract the file (i.e.call i/o routines) + * CF_CREATED -- created, but no content to extract (typically directories) + * + */ +static bRC createFile(bpContext *ctx, struct restore_pkt *rp) +{ + delta_test *self = get_self(ctx); + pm_strcpy(self->fname, rp->ofname); + rp->create_status = CF_EXTRACT; + return bRC_OK; +} + +/* + * We will get here if the File is a directory after everything + * is written in the directory. + */ +static bRC setFileAttributes(bpContext *ctx, struct restore_pkt *rp) +{ +// Dmsg(ctx, dbglvl, "delta-test-fd: setFileAttributes\n"); + return bRC_OK; +} + +#ifdef __cplusplus +} +#endif