- Figure out how to use ssh or stunnel to protect Bacula communications.
For 1.32:
+- Add ExhautiveRestoreSearch and use repositioning at the beginning
+ of the tape.
+- Add Machine type (Linux/Windows) to Status report for daemons.
+- Document "status" in the console.
+- Document driving console from shell script.
+- Specify list of files to restore
+- Implement ClientRunBeforeJob and ClientRunAfterJob.
+- Restore file list.
- Write JobMedia records with max file size is reached on tape.
- Look at the possibility of loading only the necessary
data into the restore tree (i.e. do it one directory at a
> woorkstations to be shut down overnight to save power.
>
+- From Terry Manderson <terry@apnic.net>
+ jobset { # new structure
+ name = "monthlyUnixBoxen"
+ type = backup
+ level = full
+ jobs = "wakame;durian;soy;wasabi;miso" #new!
+ schedule = monthly
+ storage = DLT
+ messages = Standard
+ pool = MonthlyPool
+ priority = 10
+ }
+
+ job {
+ name = "wakame"
+ fileset = "genericUnixSet"
+ client = wakame-fd
+ }
+
+ job {
+ name = "durian"
+ fileset = "genericUnixSet"
+ client = durian-fd
+ }
+
+ job {
+ name = "soy"
+ fileset = "UnixDevelBoxSet"
+ client = soy-fd
+ }
+
+
- Autolabel should be specified by DIR instead of SD.
- Storage daemon
- Add media capacity
int count;
db_lock(mdb);
+#ifdef not_used_in_new_code
Mmsg(&mdb->cmd, "SELECT JobId, MediaId FROM JobMedia WHERE \
JobId=%d AND MediaId=%d", jm->JobId, jm->MediaId);
}
sql_free_result(mdb);
}
+#endif
/* Now get count for VolIndex */
Mmsg(&mdb->cmd, "SELECT count(*) from JobMedia");
Mmsg(&mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
"VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
"VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,Recycle,Slot,"
-"FirstWritten,LastWritten "
+"FirstWritten,LastWritten,VolStatus "
"FROM Media WHERE PoolId=%u AND MediaType='%s' AND VolStatus IN ('Full',"
"'Recycle','Purged','Used','Append') "
"ORDER BY LastWritten LIMIT 1", mr->PoolId, mr->MediaType);
Mmsg(&mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks,"
"VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes,"
"VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,Recycle,Slot,"
-"FirstWritten,LastWritten "
+"FirstWritten,LastWritten,VolStatus "
"FROM Media WHERE PoolId=%u AND MediaType='%s' AND VolStatus='%s' "
"ORDER BY MediaId", mr->PoolId, mr->MediaType, mr->VolStatus);
}
mr->FirstWritten = (time_t)str_to_utime(mr->cFirstWritten);
bstrncpy(mr->cLastWritten, row[18]!=NULL?row[18]:"", sizeof(mr->cLastWritten));
mr->LastWritten = (time_t)str_to_utime(mr->cLastWritten);
+ bstrncpy(mr->VolStatus, row[19], sizeof(mr->VolStatus));
sql_free_result(mdb);
db_lock(mdb);
Mmsg(&mdb->cmd,
-"SELECT VolumeName FROM JobMedia,Media WHERE JobMedia.JobId=%u \
-AND JobMedia.MediaId=Media.MediaId", JobId);
+"SELECT VolumeName FROM JobMedia,Media WHERE JobMedia.JobId=%u "
+"AND JobMedia.MediaId=Media.MediaId GROUP BY VolumeName", JobId);
Dmsg1(130, "VolNam=%s\n", mdb->cmd);
*VolumeNames[0] = 0;
<widget>
<class>GtkWindow</class>
<name>app1</name>
+ <width>800</width>
+ <height>500</height>
<signal>
<name>delete_event</name>
<handler>on_app1_delete_event</handler>
<type>GTK_WINDOW_TOPLEVEL</type>
<position>GTK_WIN_POS_CENTER</position>
<modal>False</modal>
- <default_width>800</default_width>
<default_height>500</default_height>
<allow_shrink>True</allow_shrink>
<allow_grow>True</allow_grow>
sum(JobBytes) AS Bytes, Name AS Job FROM Job GROUP BY Name;
SELECT max(JobId) AS Jobs,sum(JobFiles) AS Files,
sum(JobBytes) As Bytes FROM Job;
-#
+# 2
:List where a file is saved:
*Enter path with trailing slash:
*Enter filename:
AND JobMedia.MediaId=Media.MediaId
AND Client.ClientId=Job.ClientId
GROUP BY Job.JobId;
-#
+# 3
:List where the most recent copies of a file are saved:
*Enter path with trailing slash:
*Enter filename:
AND JobMedia.MediaId=Media.MediaId
AND Client.ClientId=Job.ClientId
ORDER BY Job.StartTime DESC LIMIT 5;
-#
+# 4
:List last 20 Full Backups for a Client:
*Enter Client name:
Select Job.JobId,Client.Name as Client,StartTime,JobFiles,JobBytes,
AND Level='F' AND JobStatus='T'
AND JobMedia.JobId=Job.JobId AND JobMedia.MediaId=Media.MediaId
ORDER BY Job.StartTime DESC LIMIT 20;
-#
+# 5
:List all backups for a Client after a specified time
*Enter Client Name:
*Enter time in YYYY-MM-DD HH:MM:SS format:
AND JobMedia.JobId=Job.JobId AND JobMedia.MediaId=Media.MediaId
AND Job.StartTime >= '%2'
ORDER BY Job.StartTime;
-#
+# 6
:List all backups for a Client
*Enter Client Name:
Select Job.JobId,Client.Name as Client,Level,StartTime,JobFiles,JobBytes,VolumeName
AND JobStatus='T'
AND JobMedia.JobId=Job.JobId AND JobMedia.MediaId=Media.MediaId
ORDER BY Job.StartTime;
-#
+# 7
:List Volume Attributes for a selected Volume:
*Enter Volume name:
SELECT Slot,MaxVolBytes,VolCapacityBytes,VolStatus,Recycle,VolRetention,
VolUseDuration,MaxVolJobs,MaxVolFiles
FROM Media
WHERE Volumename='%1';
-#
+# 8
:List Volumes used by selected JobId:
*Enter JobId:
SELECT Job.JobId,VolumeName
WHERE Job.JobId=%1
AND Job.JobId=JobMedia.JobId
AND JobMedia.MediaId=Media.MediaId;
-#
+# 9
:List Volumes to Restore All Files:
*Enter Client Name:
!DROP TABLE temp;
AND JobMedia.JobId=Job.JobId
AND JobMedia.MediaId=Media.MediaId
ORDER BY Job.JobTDate DESC LIMIT 1;
-# Copy into temp 2
-INSERT INTO temp2 SELECT JobId,StartTime,VolumeName,Level,StartFile,
- VolSessionId,VolSessionTime
- FROM temp;
+# Copy into temp 2 getting all volumes of Full save
+INSERT INTO temp2 SELECT Job.JobId,Job.StartTime,Media.VolumeName,Job.Level,
+ JobMedia.StartFile,Job.VolSessionId,Job.VolSessionTime
+ FROM temp,Job,JobMedia,Media WHERE temp.JobId=Job.JobId
+ AND Job.Level='F' AND Job.JobStatus='T'
+ AND JobMedia.JobId=Job.JobId
+ AND JobMedia.MediaId=Media.MediaId;
# Now add subsequent incrementals
INSERT INTO temp2 SELECT Job.JobId,Job.StartTime,Media.VolumeName,
Job.Level,JobMedia.StartFile,Job.VolSessionId,Job.VolSessionTime
FROM Job,temp,JobMedia,Media
WHERE Job.JobTDate>temp.JobTDate
AND Job.ClientId=temp.ClientId
- AND Job.Level='I' AND JobStatus='T'
+ AND Job.Level IN ('I','D') AND JobStatus='T'
AND JobMedia.JobId=Job.JobId
AND JobMedia.MediaId=Media.MediaId
GROUP BY Job.JobId;
SELECT * from temp2;
!DROP TABLE temp;
!DROP TABLE temp2;
-#
+# 10
:List Pool Attributes for a selected Pool:
*Enter Pool name:
SELECT Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,MaxVolBytes
FROM Pool
WHERE Name='%1';
-#
+# 11
:List where a File is saved:
*Enter Filename (no path):
SELECT Job.JobId as JobId, Client.Name as Client,
"AND Job.Level='F' "
"AND Job.JobStatus='T' "
"AND Job.FileSetId=DelCandidates.FileSetId) "
- "GROUP BY JobId";
+ "GROUP BY DelCandidates.JobId";
/* Select Jobs from the DelCandidates table that have a
* more recent InitCatalog -- i.e. are not the only InitCatalog
"AND Job.Level='V' "
"AND Job.JobStatus='T' "
"AND Job.FileSetId=DelCandidates.FileSetId "
- "GROUP BY JobId";
+ "GROUP BY DelCandidates.JobId";
/* Select Jobs from the DelCandidates table.
"WHERE Job.JobTDate>%s "
"AND Job.ClientId=%u "
"AND Job.Type='R' "
- "GROUP BY JobId";
+ "GROUP BY DelCandidates.JobId";
"SELECT JobId,Client.Name as Client,StartTime,Level as "
"JobLevel,JobFiles,JobBytes "
"FROM Client,Job WHERE Client.ClientId=Job.ClientId AND JobStatus='T' "
- "AND Type='B' ORDER BY StartTime LIMIT 20";
+ "AND Type='B' ORDER BY StartTime DESC LIMIT 20";
#ifdef HAVE_MYSQL
/* MYSQL IS NOT STANDARD SQL !!!!! */
utime_t JobTDate;
uint32_t TotalFiles;
char ClientName[MAX_NAME_LENGTH];
+ char last_jobid[10];
char JobIds[200]; /* User entered string of JobIds */
STORE *store;
};
bsendmsg(ua, "%s", db_strerror(ua->db));
}
}
- bsendmsg(ua, "%d item%s inserted into the tree and marked for extraction.\n",
+ bsendmsg(ua, "%d Job%s inserted into the tree and marked for extraction.\n",
items, items==1?"":"s");
free_pool_memory(query);
if (stat == 0) {
break;
}
+ if (jr.JobId == JobId) {
+ continue; /* duplicate of last JobId */
+ }
jr.JobId = JobId;
if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
bsendmsg(ua, _("Unable to get Job record. ERR=%s\n"), db_strerror(ua->db));
/* Get the JobIds from that list */
ji->JobIds[0] = 0;
+ ji->last_jobid[0] = 0;
if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)ji)) {
bsendmsg(ua, "%s\n", db_strerror(ua->db));
}
{
JOBIDS *ji = (JOBIDS *)ctx;
+ if (strcmp(ji->last_jobid, row[0]) == 0) {
+ return 0; /* duplicate id */
+ }
+ bstrncpy(ji->last_jobid, row[0], sizeof(ji->last_jobid));
/* Concatenate a JobId if it does not exceed array size */
if (strlen(ji->JobIds)+strlen(row[0])+2 < sizeof(ji->JobIds)) {
if (ji->JobIds[0] != 0) {
}
parse_ua_args(ua);
if (ua->argc == 0) {
- return;
+ break;
}
len = strlen(ua->argk[0]);
/* Global part of JCR common to all daemons */
JCR *next;
JCR *prev;
- volatile int use_count; /* use count */
- pthread_t my_thread_id; /* id of thread controlling jcr */
- pthread_mutex_t mutex; /* jcr mutex */
- BSOCK *dir_bsock; /* Director bsock or NULL if we are him */
- BSOCK *store_bsock; /* Storage connection socket */
- BSOCK *file_bsock; /* File daemon connection socket */
+ volatile int use_count; /* use count */
+ pthread_t my_thread_id; /* id of thread controlling jcr */
+ pthread_mutex_t mutex; /* jcr mutex */
+ BSOCK *dir_bsock; /* Director bsock or NULL if we are him */
+ BSOCK *store_bsock; /* Storage connection socket */
+ BSOCK *file_bsock; /* File daemon connection socket */
JCR_free_HANDLER *daemon_free_jcr; /* Local free routine */
- POOLMEM *errmsg; /* edited error message */
- char Job[MAX_NAME_LENGTH]; /* Unique name of this Job */
+ POOLMEM *errmsg; /* edited error message */
+ char Job[MAX_NAME_LENGTH]; /* Unique name of this Job */
uint32_t JobId; /* Director's JobId */
uint32_t VolSessionId;
uint32_t VolSessionTime;
- uint32_t JobFiles; /* Number of files written, this job */
- uint32_t JobErrors; /* */
- uint64_t JobBytes; /* Number of bytes processed this job */
- uint64_t ReadBytes; /* Bytes read -- before compression */
- uint32_t Errors; /* Number of non-fatal errors */
- volatile int JobStatus; /* ready, running, blocked, terminated */
- int JobType; /* backup, restore, verify ... */
- int JobLevel; /* Job level */
- int JobPriority; /* Job priority */
- int authenticated; /* set when client authenticated */
- time_t sched_time; /* job schedule time, i.e. when it should start */
- time_t start_time; /* when job actually started */
- time_t run_time; /* used for computing speed */
- time_t end_time; /* job end time */
- POOLMEM *VolumeName; /* Volume name desired -- pool_memory */
- POOLMEM *client_name; /* client name */
- POOLMEM *RestoreBootstrap; /* Bootstrap file to restore */
- char *sd_auth_key; /* SD auth key */
- MSGS *jcr_msgs; /* Copy of message resource -- actually used */
- uint32_t ClientId; /* Client associated with Job */
- char *where; /* prefix to restore files to */
- int prefix_links; /* Prefix links with Where path */
- int cached_pnl; /* cached path length */
- POOLMEM *cached_path; /* cached path */
+ uint32_t JobFiles; /* Number of files written, this job */
+ uint32_t JobErrors; /* */
+ uint64_t JobBytes; /* Number of bytes processed this job */
+ uint64_t ReadBytes; /* Bytes read -- before compression */
+ uint32_t Errors; /* Number of non-fatal errors */
+ volatile int JobStatus; /* ready, running, blocked, terminated */
+ int JobType; /* backup, restore, verify ... */
+ int JobLevel; /* Job level */
+ int JobPriority; /* Job priority */
+ int authenticated; /* set when client authenticated */
+ time_t sched_time; /* job schedule time, i.e. when it should start */
+ time_t start_time; /* when job actually started */
+ time_t run_time; /* used for computing speed */
+ time_t end_time; /* job end time */
+ POOLMEM *VolumeName; /* Volume name desired -- pool_memory */
+ POOLMEM *client_name; /* client name */
+ POOLMEM *RestoreBootstrap; /* Bootstrap file to restore */
+ char *sd_auth_key; /* SD auth key */
+ MSGS *jcr_msgs; /* Copy of message resource -- actually used */
+ uint32_t ClientId; /* Client associated with Job */
+ char *where; /* prefix to restore files to */
+ int prefix_links; /* Prefix links with Where path */
+ int cached_pnl; /* cached path length */
+ POOLMEM *cached_path; /* cached path */
/* Daemon specific part of JCR */
/* This should be empty in the library */
#ifdef DIRECTOR_DAEMON
/* Director Daemon specific part of JCR */
- pthread_t SD_msg_chan; /* Message channel thread id */
- pthread_cond_t term_wait; /* Wait for job termination */
- workq_ele_t *work_item; /* Work queue item if scheduled */
+ pthread_t SD_msg_chan; /* Message channel thread id */
+ pthread_cond_t term_wait; /* Wait for job termination */
+ workq_ele_t *work_item; /* Work queue item if scheduled */
volatile bool sd_msg_thread_done; /* Set when Storage message thread terms */
- BSOCK *ua; /* User agent */
- JOB *job; /* Job resource */
- STORE *store; /* Storage resource */
- CLIENT *client; /* Client resource */
- POOL *pool; /* Pool resource */
- FILESET *fileset; /* FileSet resource */
- CAT *catalog; /* Catalog resource */
- MSGS *messages; /* Default message handler */
- uint32_t SDJobFiles; /* Number of files written, this job */
- uint64_t SDJobBytes; /* Number of bytes processed this job */
- uint32_t SDErrors; /* Number of non-fatal errors */
- volatile int SDJobStatus; /* Storage Job Status */
- volatile int FDJobStatus; /* File daemon Job Status */
- B_DB *db; /* database pointer */
- uint32_t MediaId; /* DB record IDs associated with this job */
- uint32_t PoolId; /* Pool record id */
- FileId_t FileId; /* Last file id inserted */
- uint32_t FileIndex; /* Last FileIndex processed */
- POOLMEM *fname; /* name to put into catalog */
- int fn_printed; /* printed filename */
- POOLMEM *stime; /* start time for incremental/differential */
- JOB_DBR jr; /* Job record in Database */
- uint32_t RestoreJobId; /* Id specified by UA */
- POOLMEM *client_uname; /* client uname */
- int replace; /* Replace option */
+ BSOCK *ua; /* User agent */
+ JOB *job; /* Job resource */
+ STORE *store; /* Storage resource */
+ CLIENT *client; /* Client resource */
+ POOL *pool; /* Pool resource */
+ FILESET *fileset; /* FileSet resource */
+ CAT *catalog; /* Catalog resource */
+ MSGS *messages; /* Default message handler */
+ uint32_t SDJobFiles; /* Number of files written, this job */
+ uint64_t SDJobBytes; /* Number of bytes processed this job */
+ uint32_t SDErrors; /* Number of non-fatal errors */
+ volatile int SDJobStatus; /* Storage Job Status */
+ volatile int FDJobStatus; /* File daemon Job Status */
+ B_DB *db; /* database pointer */
+ uint32_t MediaId; /* DB record IDs associated with this job */
+ uint32_t PoolId; /* Pool record id */
+ FileId_t FileId; /* Last file id inserted */
+ uint32_t FileIndex; /* Last FileIndex processed */
+ POOLMEM *fname; /* name to put into catalog */
+ int fn_printed; /* printed filename */
+ POOLMEM *stime; /* start time for incremental/differential */
+ JOB_DBR jr; /* Job record in Database */
+ uint32_t RestoreJobId; /* Id specified by UA */
+ POOLMEM *client_uname; /* client uname */
+ int replace; /* Replace option */
bool acquired_resource_locks; /* set if resource locks acquired */
- int NumVols; /* Number of Volume used in pool */
- int reschedule_count; /* Number of times rescheduled */
+ int NumVols; /* Number of Volume used in pool */
+ int reschedule_count; /* Number of times rescheduled */
#endif /* DIRECTOR_DAEMON */
#ifdef FILE_DAEMON
/* File Daemon specific part of JCR */
uint32_t num_files_examined; /* files examined this job */
- POOLMEM *last_fname; /* last file saved/verified */
+ POOLMEM *last_fname; /* last file saved/verified */
/*********FIXME********* add missing files and files to be retried */
- int incremental; /* set if incremental for SINCE */
- time_t mtime; /* begin time for SINCE */
- int mtime_only; /* compare only mtime and not ctime as well */
- int listing; /* job listing in estimate */
- long Ticket; /* Ticket */
- int save_level; /* save level */
- char *big_buf; /* I/O buffer */
- POOLMEM *compress_buf; /* Compression buffer */
- int32_t compress_buf_size; /* Length of compression buffer */
- int replace; /* Replace options */
- int buf_size; /* length of buffer */
- void *ff; /* Find Files packet */
+ int incremental; /* set if incremental for SINCE */
+ time_t mtime; /* begin time for SINCE */
+ int mtime_only; /* compare only mtime and not ctime as well */
+ int listing; /* job listing in estimate */
+ long Ticket; /* Ticket */
+ int save_level; /* save level */
+ char *big_buf; /* I/O buffer */
+ POOLMEM *compress_buf; /* Compression buffer */
+ int32_t compress_buf_size; /* Length of compression buffer */
+ int replace; /* Replace options */
+ int buf_size; /* length of buffer */
+ void *ff; /* Find Files packet */
char stored_addr[MAX_NAME_LENGTH]; /* storage daemon address */
uint32_t StartFile;
uint32_t EndFile;
uint32_t StartBlock;
uint32_t EndBlock;
- pthread_t heartbeat_id; /* id of heartbeat thread */
- volatile BSOCK *hb_bsock; /* duped SD socket */
+ pthread_t heartbeat_id; /* id of heartbeat thread */
+ volatile BSOCK *hb_bsock; /* duped SD socket */
#endif /* FILE_DAEMON */
#ifdef STORAGE_DAEMON
/* Storage Daemon specific part of JCR */
- JCR *next_dev; /* next JCR attached to device */
- JCR *prev_dev; /* previous JCR attached to device */
+ JCR *next_dev; /* next JCR attached to device */
+ JCR *prev_dev; /* previous JCR attached to device */
pthread_cond_t job_start_wait; /* Wait for FD to start Job */
int type;
- DEVRES *device; /* device to use */
- VOLUME_CAT_INFO VolCatInfo; /* Catalog info for desired volume */
- POOLMEM *job_name; /* base Job name (not unique) */
- POOLMEM *fileset_name; /* FileSet */
- POOLMEM *fileset_md5; /* MD5 for FileSet */
- POOLMEM *pool_name; /* pool to use */
- POOLMEM *pool_type; /* pool type to use */
- POOLMEM *media_type; /* media type */
- POOLMEM *dev_name; /* device name */
- VOL_LIST *VolList; /* list to read */
- int32_t NumVolumes; /* number of volumes used */
- int32_t CurVolume; /* current volume number */
- int spool_attributes; /* set if spooling attributes */
- int no_attributes; /* set if no attributes wanted */
- int label_status; /* device volume label status */
- int label_errors; /* count of label errors */
+ DEVRES *device; /* device to use */
+ VOLUME_CAT_INFO VolCatInfo; /* Catalog info for desired volume */
+ POOLMEM *job_name; /* base Job name (not unique) */
+ POOLMEM *fileset_name; /* FileSet */
+ POOLMEM *fileset_md5; /* MD5 for FileSet */
+ POOLMEM *pool_name; /* pool to use */
+ POOLMEM *pool_type; /* pool type to use */
+ POOLMEM *media_type; /* media type */
+ POOLMEM *dev_name; /* device name */
+ VOL_LIST *VolList; /* list to read */
+ int32_t NumVolumes; /* number of volumes used */
+ int32_t CurVolume; /* current volume number */
+ int spool_attributes; /* set if spooling attributes */
+ int no_attributes; /* set if no attributes wanted */
+ int label_status; /* device volume label status */
+ int label_errors; /* count of label errors */
int session_opened;
- DEV_RECORD rec; /* Read/Write record */
- long Ticket; /* ticket for this job */
- uint32_t VolFirstIndex; /* First file index this Volume */
- uint32_t VolLastIndex; /* Last file index this Volume */
- uint32_t FileIndex; /* Current File Index */
- uint32_t EndFile; /* End file written */
- uint32_t StartFile; /* Start write file */
- uint32_t StartBlock; /* Start write block */
- uint32_t EndBlock; /* Ending block written */
- bool NewVol; /* set when new Volume mounted */
- bool WroteVol; /* set when Volume written */
- int CurVol; /* Current Volume count */
+ DEV_RECORD rec; /* Read/Write record */
+ long Ticket; /* ticket for this job */
+ uint32_t VolFirstIndex; /* First file index this Volume */
+ uint32_t VolLastIndex; /* Last file index this Volume */
+ uint32_t FileIndex; /* Current File Index */
+ uint32_t EndFile; /* End file written */
+ uint32_t StartFile; /* Start write file */
+ uint32_t StartBlock; /* Start write block */
+ uint32_t EndBlock; /* Ending block written */
+ bool NewVol; /* set when new Volume mounted */
+ bool WroteVol; /* set when Volume written */
+ bool NewFile; /* set when EOF written */
+ int CurVol; /* Current Volume count */
- uint32_t FileId; /* Last file id inserted */
+ uint32_t FileId; /* Last file id inserted */
/* Parmaters for Open Read Session */
- BSR *bsr; /* Bootstrap record -- has everything */
+ BSR *bsr; /* Bootstrap record -- has everything */
uint32_t read_VolSessionId;
uint32_t read_VolSessionTime;
uint32_t read_StartFile;
#endif
/*
- * This subrouting gets a big buffer.
+ * This subroutine gets a big buffer.
*/
static void malloc_buf(TREE_ROOT *root, int size)
{
case TN_NEWDIR:
case TN_DIR:
case TN_DIR_NLS:
- sprintf(buf, "%s/%s", path, tree->fname);
+ bsnprintf(buf, sizeof(buf), "%s/%s", path, tree->fname);
print_tree(buf, tree->child);
break;
case TN_ROOT:
if (node->type == TN_DIR_NLS && buf[0] == '/' && buf[1] == 0) {
buf[0] = 0;
}
- strcat(buf, node->fname);
+ bstrncat(buf, node->fname, buf_size);
if (node->type != TN_FILE) {
- strcat(buf, "/");
+ bstrncat(buf, "/", buf_size);
}
return 1;
}
if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) {
continue;
}
- strcpy(file, dir->d_name);
+ bstrncpy(file, dir->d_name, sizeof(file));
snprintf(pathbuf, MAXPATHLEN-1, "%s/%s", path, file);
if (lstat(pathbuf, &statbuf) < 0) {
printf("lstat() failed. ERR=%s\n", strerror(errno));
attach_jcr_to_device(dev, jcr); /* attach jcr to device */
Jmsg(jcr, M_INFO, 0, _("Ready to read from volume \"%s\" on device %s.\n"),
jcr->VolumeName, dev_name(dev));
- if ((dev->state & ST_TAPE) && vol->start_file > 0) {
- Dmsg1(200, "====== Got start_file = %d\n", vol->start_file);
- Jmsg(jcr, M_INFO, 0, _("Forward spacing to file %d.\n"), vol->start_file);
- fsf_dev(dev, vol->start_file);
+ if (jcr->bsr) {
+ BSR *bsr;
+
+ jcr->bsr->reposition = true;
+ bsr = find_next_bsr(jcr->bsr, dev);
+ if (bsr) {
+ Jmsg(jcr, M_INFO, 0, _("Forward spacing to file:block %u:%u.\n"),
+ bsr->volfile->sfile, bsr->volblock->sblock);
+ reposition_dev(dev, bsr->volfile->sfile, bsr->volblock->sblock);
+ } else if ((dev->state & ST_TAPE) && vol->start_file > 0) {
+ Dmsg1(200, "====== Got start_file = %d\n", vol->start_file);
+ Jmsg(jcr, M_INFO, 0, _("Forward spacing to file %d.\n"), vol->start_file);
+ fsf_dev(dev, vol->start_file);
+ }
}
get_out:
dev->num_writers--;
Dmsg1(100, "There are %d writers in release_device\n", dev->num_writers);
if (dev->num_writers == 0) {
- /* If we have fully acquired the tape */
+ /* If we are the only writer, write EOF after job */
if (dev->state & ST_LABEL) {
Dmsg0(100, "dir_create_jobmedia_record. Release\n");
dir_create_jobmedia_record(jcr);
switch (ch) {
case 'b': /* bootstrap file */
bsr = parse_bsr(NULL, optarg);
-// dump_bsr(bsr);
+// dump_bsr(bsr, true);
break;
case 'c': /* specify config file */
* If a new volume has been mounted since our last write
* Create a JobMedia record for the previous volume written,
* and set new parameters to write this volume
+ * The saem applies for if we are in a new file.
*/
- if (jcr->NewVol) {
+ if (jcr->NewVol || jcr->NewFile) {
/* Create a jobmedia record for this job */
if (!dir_create_jobmedia_record(jcr)) {
Jmsg(jcr, M_ERROR, 0, _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"),
unlock_device(dev);
return 0;
}
- set_new_volume_parameters(jcr, dev);
+ if (jcr->NewVol) {
+ /* Note, setting a new volume also handles any pending new file */
+ set_new_volume_parameters(jcr, dev);
+ jcr->NewFile = false; /* this handled for new file too */
+ } else {
+ set_new_file_parameters(jcr, dev);
+ }
}
if (!write_block_to_dev(jcr, dev, block)) {
Jmsg(jcr, M_INFO, 0, _("User defined maximum volume capacity %s exceeded on device %s.\n"),
edit_uint64(max_cap, ed1), dev->dev_name);
block->write_failed = true;
- weof_dev(dev, 1); /* end the tape */
- weof_dev(dev, 1); /* write second eof */
+ weof_dev(dev, 2); /* end the tape */
dev->state |= (ST_EOF | ST_EOT | ST_WEOT);
- return 0;
+ return 0;
}
/* Limit maximum File size on volume to user specified value */
if (dev->state & ST_TAPE) {
if ((dev->max_file_size > 0) &&
(dev->file_addr+block->binbuf) >= dev->max_file_size) {
+
+ /* Write EOF */
if (weof_dev(dev, 1) != 0) { /* write eof */
Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
- /* Plunge on anyway -- if tape is bad we will die on write */
+ block->write_failed = true;
+ dev->state |= (ST_EOF | ST_EOT | ST_WEOT);
+ return 0;
+ }
+
+ /* Do bookkeeping to handle EOF just written */
+ if (!dir_create_jobmedia_record(jcr)) {
+ Jmsg(jcr, M_ERROR, 0, _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"),
+ jcr->VolCatInfo.VolCatName, jcr->Job);
+ return 0;
+ }
+ /*
+ * Walk through all attached jcrs indicating the file has changed
+ */
+ Dmsg1(100, "Walk attached jcrs. Volume=%s\n", dev->VolCatInfo.VolCatName);
+ for (JCR *mjcr=NULL; (mjcr=next_attached_jcr(dev, mjcr)); ) {
+ if (mjcr->JobId == 0) {
+ continue; /* ignore console */
+ }
+ mjcr->NewFile = true;
}
+ set_new_file_parameters(jcr, dev);
}
}
uint32_t BlockNumber;
int retry = 0;
+ if (dev_state(dev, ST_EOT)) {
+ return 0;
+ }
looping = 0;
Dmsg1(100, "Full read() in read_block_from_device() len=%d\n",
block->buf_len);
static SESSION_LABEL sessrec;
static uint32_t num_files = 0;
static ATTR *attr;
-static int non_support_data = 0;
#define CONFIG_FILE "bacula-sd.conf"
char *configfile;
char *rtype;
memset(sessrec, 0, sizeof(sessrec));
switch (rec->FileIndex) {
- case PRE_LABEL:
- rtype = "Fresh Volume Label";
- break;
- case VOL_LABEL:
- rtype = "Volume Label";
- unser_volume_label(dev, rec);
- break;
- case SOS_LABEL:
- rtype = "Begin Session";
- unser_session_label(sessrec, rec);
- break;
- case EOS_LABEL:
- rtype = "End Session";
- break;
- case EOM_LABEL:
- rtype = "End of Medium";
- break;
- default:
- rtype = "Unknown";
- break;
+ case PRE_LABEL:
+ rtype = "Fresh Volume Label";
+ break;
+ case VOL_LABEL:
+ rtype = "Volume Label";
+ unser_volume_label(dev, rec);
+ break;
+ case SOS_LABEL:
+ rtype = "Begin Session";
+ unser_session_label(sessrec, rec);
+ break;
+ case EOS_LABEL:
+ rtype = "End Session";
+ break;
+ case EOM_LABEL:
+ rtype = "End of Medium";
+ break;
+ default:
+ rtype = "Unknown";
+ break;
}
Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
VOL_LIST *next;
char VolumeName[MAX_NAME_LENGTH];
int Slot;
- int start_file;
+ uint32_t start_file;
};
struct BSR {
BSR *next; /* pointer to next one */
- bool done; /* set when everything found */
+ BSR *root; /* root bsr */
+ bool reposition; /* set when any bsr is marked done */
+ bool mount_next_volume; /* set when next volume should be mounted */
+ bool done; /* set when everything found for this bsr */
bool use_fast_rejection; /* set if fast rejection can be used */
bool use_positioning; /* set if we can position the archive */
BSR_VOLUME *volume;
switch (ch) {
case 'b': /* bootstrap file */
bsr = parse_bsr(NULL, optarg);
-// dump_bsr(bsr);
+// dump_bsr(bsr, true);
break;
case 'c': /* specify config file */
/* Forward space a record */
static void fsrcmd()
{
- int stat;
-
- if ((stat=fsr_dev(dev, 1)) < 0) {
- Pmsg2(0, "Bad status from fsr %d. ERR=%s\n", stat, strerror_dev(dev));
+ if (!fsr_dev(dev, 1)) {
+ Pmsg1(0, "Bad status from fsr. ERR=%s\n", strerror_dev(dev));
return;
}
Pmsg0(0, "Forward spaced one record.\n");
if (dev->fd < 0) {
dev->dev_errno = EBADF;
- Mmsg0(&dev->errmsg, _("Bad call to load_dev. Archive not open\n"));
+ Mmsg0(&dev->errmsg, _("Bad call to offline_dev. Archive not open\n"));
Emsg0(M_FATAL, 0, dev->errmsg);
return 0;
}
/*
* Backward space a file
+ * Returns: 0 on failure
+ * 1 on success
*/
int
bsf_dev(DEVICE *dev, int num)
if (dev->fd < 0) {
dev->dev_errno = EBADF;
- Mmsg0(&dev->errmsg, _("Bad call to fsf_dev. Archive not open\n"));
+ Mmsg0(&dev->errmsg, _("Bad call to bsf_dev. Archive not open\n"));
Emsg0(M_FATAL, 0, dev->errmsg);
- return -1;
+ return 0;
}
- if (!(dev->state & ST_TAPE)) {
+ if (!(dev_state(dev, ST_TAPE))) {
return 0;
}
Dmsg0(29, "bsf_dev\n");
dev->dev_name, strerror(dev->dev_errno));
}
update_pos_dev(dev);
- return stat;
+ return stat == 0 ? 1 : 0;
}
/*
* Foward space a record
+ * Returns: 0 on failure
+ * 1 on success
*/
int
fsr_dev(DEVICE *dev, int num)
if (dev->fd < 0) {
dev->dev_errno = EBADF;
- Mmsg0(&dev->errmsg, _("Bad call to fsf_dev. Archive not open\n"));
+ Mmsg0(&dev->errmsg, _("Bad call to fsr_dev. Archive not open\n"));
Emsg0(M_FATAL, 0, dev->errmsg);
- return -1;
+ return 0;
}
- if (!(dev->state & ST_TAPE)) {
+ if (!(dev_state(dev, ST_TAPE))) {
return 0;
}
Dmsg0(29, "fsr_dev\n");
dev->dev_name, strerror(dev->dev_errno));
}
update_pos_dev(dev);
- return stat;
+ return stat == 0 ? 1 : 0;
}
/*
* Backward space a record
- * Returns: 0 on success
- * -1 on failure
+ * Returns: 0 on failure
+ * 1 on success
*/
int
bsr_dev(DEVICE *dev, int num)
if (dev->fd < 0) {
dev->dev_errno = EBADF;
- Mmsg0(&dev->errmsg, _("Bad call to fsf_dev. Archive not open\n"));
+ Mmsg0(&dev->errmsg, _("Bad call to bsr_dev. Archive not open\n"));
Emsg0(M_FATAL, 0, dev->errmsg);
- return -1;
+ return 0;
}
if (!(dev->state & ST_TAPE)) {
dev->dev_name, strerror(dev->dev_errno));
}
update_pos_dev(dev);
- return stat;
+ return stat == 0 ? 1 : 0;
+}
+
+/*
+ * Reposition the device to file, block
+ * Currently only works for tapes.
+ * Returns: 0 on failure
+ * 1 on success
+ */
+int
+reposition_dev(DEVICE *dev, uint32_t file, uint32_t block)
+{
+
+ if (dev->fd < 0) {
+ dev->dev_errno = EBADF;
+ Mmsg0(&dev->errmsg, _("Bad call to reposition_dev. Archive not open\n"));
+ Emsg0(M_FATAL, 0, dev->errmsg);
+ return 0;
+ }
+
+ if (!(dev_state(dev, ST_TAPE))) {
+ return 0;
+ }
+ Dmsg4(100, "reposition_dev from %u:%u to %u:%u\n",
+ dev->file, dev->block_num, file, block);
+ if (file < dev->file) {
+ Dmsg0(100, "Rewind_dev\n");
+ if (!rewind_dev(dev)) {
+ return 0;
+ }
+ }
+ if (file > dev->file) {
+ Dmsg1(100, "fsf %d\n", file-dev->file);
+ if (!fsf_dev(dev, file-dev->file)) {
+ return 0;
+ }
+ }
+ if (block > dev->block_num) {
+ /* Ignore errors as Bacula can read to the correct block */
+ Dmsg1(100, "fsr %d\n", block-dev->block_num);
+ fsr_dev(dev, block-dev->block_num);
+ }
+ return 1;
}
if (dev->fd < 0) {
dev->dev_errno = EBADF;
- Mmsg0(&dev->errmsg, _("Bad call to fsf_dev. Archive not open\n"));
+ Mmsg0(&dev->errmsg, _("Bad call to weof_dev. Archive not open\n"));
Emsg0(M_FATAL, 0, dev->errmsg);
return -1;
}
mt_com.mt_count = num;
stat = ioctl(dev->fd, MTIOCTOP, (char *)&mt_com);
if (stat == 0) {
- dev->file++;
+ dev->file += num;
dev->file_addr = 0;
} else {
clrerror_dev(dev, MTWEOF);
close_dev(DEVICE *dev)
{
if (!dev) {
- Mmsg0(&dev->errmsg, _("Bad call to fsf_dev. Archive not open\n"));
+ Mmsg0(&dev->errmsg, _("Bad call to close_dev. Archive not open\n"));
Emsg0(M_FATAL, 0, dev->errmsg);
return;
}
void force_close_dev(DEVICE *dev)
{
if (!dev) {
- Mmsg0(&dev->errmsg, _("Bad call to fsf_dev. Archive not open\n"));
+ Mmsg0(&dev->errmsg, _("Bad call to force_close_dev. Archive not open\n"));
Emsg0(M_FATAL, 0, dev->errmsg);
return;
}
{
if (!dev) {
dev->dev_errno = EBADF;
- Mmsg0(&dev->errmsg, _("Bad call to fsf_dev. Archive not open\n"));
+ Mmsg0(&dev->errmsg, _("Bad call to term_dev. Archive not open\n"));
Emsg0(M_FATAL, 0, dev->errmsg);
return;
}
#define CAP_BSFATEOM (1<<15) /* Backspace file at EOM */
/* Test state */
-#define dev_state(dev, state) ((dev)->state & (state))
+#define dev_state(dev, st_state) ((dev)->state & (st_state))
/* Device state bits */
#define ST_OPENED (1<<0) /* set when device opened */
jcr->WroteVol = false;
}
+/*
+ * We are now in a new file, so reset the Volume parameters
+ * concerning this job. The global changes were made earlier
+ * in the dev structure.
+ */
+void set_new_file_parameters(JCR *jcr, DEVICE *dev)
+{
+ /* Set new start/end positions */
+ if (dev->state & ST_TAPE) {
+ jcr->StartBlock = dev->block_num;
+ jcr->StartFile = dev->file;
+ } else {
+ jcr->StartBlock = (uint32_t)dev->file_addr;
+ jcr->StartFile = (uint32_t)(dev->file_addr >> 32);
+ }
+ /* Reset indicies */
+ jcr->VolFirstIndex = 0;
+ jcr->VolLastIndex = 0;
+ jcr->NewFile = false;
+ jcr->WroteVol = false;
+}
+
+
/*
* Open the device. Expect dev to already be initialized.
goto bail_out;
}
if (debug_level > 20) {
- dump_bsr(jcr->bsr);
+ dump_bsr(jcr->bsr, true);
}
return bnet_fsend(fd, OK_bootstrap);
static int match_all(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec, bool done);
static int match_block_sesstime(BSR *bsr, BSR_SESSTIME *sesstime, DEV_BLOCK *block);
static int match_block_sessid(BSR *bsr, BSR_SESSID *sessid, DEV_BLOCK *block);
+static BSR *find_smallest_volfile(BSR *fbsr, BSR *bsr);
+
/*********************************************************************
*
*
* Match Bootstrap records
* returns 1 on match
- * returns 0 no match
+ * returns 0 no match and reposition is set if we should
+ * reposition the tape
* returns -1 no additional matches possible
*/
int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, SESSION_LABEL *sessrec)
{
int stat;
+ /*
+ * The bsr->reposition flag is set any time a bsr is done.
+ * In this case, we can probably reposition the
+ * tape to the next available bsr position.
+ */
if (bsr) {
+ bsr->reposition = false;
stat = match_all(bsr, rec, volrec, sessrec, true);
+ /*
+ * Note, bsr->reposition is set by match_all when
+ * a bsr is done. We turn it off if a match was
+ * found or if we cannot use poistioning
+ */
+ if (stat != 0 || !bsr->use_positioning) {
+ bsr->reposition = false;
+ }
} else {
- stat = 0;
+ stat = 1; /* no bsr => match all */
}
-// Dmsg1(000, "BSR returning %d\n", stat);
return stat;
}
+/*
+ * Find the next bsr that applies to the current tape.
+ * It is the one with the smallest VolFile position.
+ */
+BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev)
+{
+ BSR *bsr;
+ BSR *found_bsr = NULL;
+
+ if (!root_bsr || !root_bsr->use_positioning ||
+ !root_bsr->reposition || !dev_is_tape(dev)) {
+ Dmsg2(000, "use_pos=%d repos=%d\n", root_bsr->use_positioning,
+ root_bsr->reposition);
+ return NULL;
+ }
+ root_bsr->mount_next_volume = false;
+ for (bsr=root_bsr; bsr; bsr=bsr->next) {
+ if (bsr->done || !match_volume(bsr, bsr->volume, &dev->VolHdr, 1)) {
+ continue;
+ }
+ if (found_bsr == NULL) {
+ found_bsr = bsr;
+ } else {
+ found_bsr = find_smallest_volfile(found_bsr, bsr);
+ }
+ }
+ /*
+ * If we get to this point and found no bsr, it means
+ * that any additional bsr's must apply to the next
+ * tape, so set a flag.
+ */
+ if (found_bsr == NULL) {
+ root_bsr->mount_next_volume = true;
+ }
+ return found_bsr;
+}
+
+static BSR *find_smallest_volfile(BSR *found_bsr, BSR *bsr)
+{
+ BSR *return_bsr = found_bsr;
+ BSR_VOLFILE *vf;
+ BSR_VOLBLOCK *vb;
+ uint32_t found_bsr_sfile, bsr_sfile;
+ uint32_t found_bsr_sblock, bsr_sblock;
+
+ vf = found_bsr->volfile;
+ found_bsr_sfile = vf->sfile;
+ while ( (vf=vf->next) ) {
+ if (vf->sfile < found_bsr_sfile) {
+ found_bsr_sfile = vf->sfile;
+ }
+ }
+ vf = bsr->volfile;
+ bsr_sfile = vf->sfile;
+ while ( (vf=vf->next) ) {
+ if (vf->sfile < bsr_sfile) {
+ bsr_sfile = vf->sfile;
+ }
+ }
+ if (found_bsr_sfile > bsr_sfile) {
+ return_bsr = bsr;
+ } else if (found_bsr_sfile == bsr_sfile) {
+ /* Must check block */
+ vb = found_bsr->volblock;
+ found_bsr_sblock = vb->sblock;
+ while ( (vb=vb->next) ) {
+ if (vb->sblock < found_bsr_sblock) {
+ found_bsr_sblock = vb->sblock;
+ }
+ }
+ vb = bsr->volblock;
+ bsr_sblock = vb->sblock;
+ while ( (vb=vb->next) ) {
+ if (vb->sblock < bsr_sblock) {
+ bsr_sblock = vb->sblock;
+ }
+ }
+ if (found_bsr_sblock > bsr_sblock) {
+ return_bsr = bsr;
+ }
+ }
+
+ return return_bsr;
+}
+
/*
* Match all the components of current record
* returns 1 on match
}
if (bsr->count && bsr->count <= bsr->found) {
bsr->done = true;
+ bsr->root->reposition = true;
+ Dmsg0(100, "bsr done from count\n");
goto no_match;
}
if (!match_volume(bsr, bsr->volume, volrec, 1)) {
/* If we are done and all prior matches are done, this bsr is finished */
if (volfile->done && done) {
bsr->done = true;
+ bsr->root->reposition = true;
+ Dmsg0(100, "bsr done from volfile\n");
}
return 0;
}
}
if (sesstime->done && done) {
bsr->done = true;
+ bsr->root->reposition = true;
+ Dmsg0(100, "bsr done from sesstime\n");
}
return 0;
}
}
if (findex->done && done) {
bsr->done = true;
+ bsr->root->reposition = true;
+ Dmsg1(100, "bsr done from findex %d\n", rec->FileIndex);
}
return 0;
}
static BSR *store_stream(LEX *lc, BSR *bsr);
static BSR *store_slot(LEX *lc, BSR *bsr);
static bool is_fast_rejection_ok(BSR *bsr);
+static bool is_positioning_ok(BSR *bsr);
struct kw_items {
char *name;
/*
* Create a BSR record
*/
-static BSR *new_bsr()
+static BSR *new_bsr()
{
BSR *bsr = (BSR *)malloc(sizeof(BSR));
memset(bsr, 0, sizeof(BSR));
* Parse Bootstrap file
*
*/
-BSR *parse_bsr(JCR *jcr, char *cf)
+BSR *parse_bsr(JCR *jcr, char *fname)
{
LEX *lc = NULL;
int token, i;
BSR *root_bsr = new_bsr();
BSR *bsr = root_bsr;
-
- Dmsg1(200, "Enter parse_bsf %s\n", cf);
- lc = lex_open_file(lc, cf, s_err);
+
+ Dmsg1(200, "Enter parse_bsf %s\n", fname);
+ lc = lex_open_file(lc, fname, s_err);
lc->caller_ctx = (void *)jcr;
while ((token=lex_get_token(lc, T_ALL)) != T_EOF) {
Dmsg1(200, "parse got token=%s\n", lex_tok_to_str(token));
}
if (root_bsr) {
root_bsr->use_fast_rejection = is_fast_rejection_ok(root_bsr);
+ root_bsr->use_positioning = is_positioning_ok(root_bsr);
+ }
+ for (bsr=root_bsr; bsr; bsr=bsr->next) {
+ bsr->root = root_bsr;
}
return root_bsr;
}
return true;
}
+static bool is_positioning_ok(BSR *bsr)
+{
+ /*
+ * Every bsr should have a volfile entry and a volblock entry
+ * if we are going to use positioning
+ */
+ if (!bsr->volfile || !bsr->volblock) {
+ return false;
+ }
+ if (bsr->next) {
+ return is_positioning_ok(bsr->next);
+ }
+ return true;
+}
+
+
static BSR *store_vol(LEX *lc, BSR *bsr)
{
int token;
-void dump_bsr(BSR *bsr)
+void dump_bsr(BSR *bsr, bool recurse)
{
+ int save_debug = debug_level;
+ debug_level = 1;
if (!bsr) {
Dmsg0(-1, "BSR is NULL\n");
+ debug_level = save_debug;
return;
}
- Dmsg1(-1,
-"Next : 0x%x\n", bsr->next);
+ Dmsg1(-1, "Next : 0x%x\n", bsr->next);
+ Dmsg1(-1, "Root bsr : 0x%x\n", bsr->root);
dump_volume(bsr->volume);
dump_sessid(bsr->sessid);
dump_sesstime(bsr->sesstime);
if (bsr->count) {
Dmsg1(-1, "count : %u\n", bsr->count);
}
- if (bsr->next) {
+ Dmsg1(-1, "done : %s\n", bsr->done?"yes":"no");
+ Dmsg1(-1, "positioning : %d\n", bsr->use_positioning);
+ if (recurse && bsr->next) {
Dmsg0(-1, "\n");
- dump_bsr(bsr->next);
+ dump_bsr(bsr->next, true);
}
+ debug_level = save_debug;
}
uint32_t new_VolSessionId();
/* From acquire.c */
-DEVICE *acquire_device_for_append(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-int acquire_device_for_read(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-int release_device(JCR *jcr, DEVICE *dev);
+DEVICE *acquire_device_for_append(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+int acquire_device_for_read(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+int release_device(JCR *jcr, DEVICE *dev);
/* From askdir.c */
enum get_vol_info_rw {
GET_VOL_INFO_FOR_WRITE,
GET_VOL_INFO_FOR_READ
};
-int dir_get_volume_info(JCR *jcr, enum get_vol_info_rw);
-int dir_find_next_appendable_volume(JCR *jcr);
-int dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel);
-int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev);
-int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev);
-int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec);
-int dir_send_job_status(JCR *jcr);
-int dir_create_jobmedia_record(JCR *jcr);
+int dir_get_volume_info(JCR *jcr, enum get_vol_info_rw);
+int dir_find_next_appendable_volume(JCR *jcr);
+int dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel);
+int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev);
+int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev);
+int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec);
+int dir_send_job_status(JCR *jcr);
+int dir_create_jobmedia_record(JCR *jcr);
/* authenticate.c */
-int authenticate_director(JCR *jcr);
-int authenticate_filed(JCR *jcr);
+int authenticate_director(JCR *jcr);
+int authenticate_filed(JCR *jcr);
/* From block.c */
-void dump_block(DEV_BLOCK *b, char *msg);
+void dump_block(DEV_BLOCK *b, char *msg);
DEV_BLOCK *new_block(DEVICE *dev);
-void init_block_write(DEV_BLOCK *block);
-void empty_block(DEV_BLOCK *block);
-void free_block(DEV_BLOCK *block);
-int write_block_to_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-int write_block_to_dev(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+void init_block_write(DEV_BLOCK *block);
+void empty_block(DEV_BLOCK *block);
+void free_block(DEV_BLOCK *block);
+int write_block_to_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+int write_block_to_dev(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
#define CHECK_BLOCK_NUMBERS true
#define NO_BLOCK_NUMBER_CHECK false
-int read_block_from_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, bool check_block_numbers);
-int read_block_from_dev(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, bool check_block_numbers);
+int read_block_from_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, bool check_block_numbers);
+int read_block_from_dev(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, bool check_block_numbers);
/* From butil.c -- utilities for SD tool programs */
-void print_ls_output(char *fname, char *link, int type, struct stat *statp);
+void print_ls_output(char *fname, char *link, int type, struct stat *statp);
JCR *setup_jcr(char *name, char *device, BSR *bsr, char *VolumeName);
DEVICE *setup_to_access_device(JCR *jcr, int read_access);
-void display_tape_error_status(JCR *jcr, DEVICE *dev);
+void display_tape_error_status(JCR *jcr, DEVICE *dev);
DEVRES *find_device_res(char *device_name, int read_access);
/* From dev.c */
-DEVICE *init_dev(DEVICE *dev, DEVRES *device);
-int open_dev(DEVICE *dev, char *VolName, int mode);
-void close_dev(DEVICE *dev);
-void force_close_dev(DEVICE *dev);
-int truncate_dev(DEVICE *dev);
-void term_dev(DEVICE *dev);
-char * strerror_dev(DEVICE *dev);
-void clrerror_dev(DEVICE *dev, int func);
-int update_pos_dev(DEVICE *dev);
-int rewind_dev(DEVICE *dev);
-int load_dev(DEVICE *dev);
-int offline_dev(DEVICE *dev);
-int flush_dev(DEVICE *dev);
-int weof_dev(DEVICE *dev, int num);
-int write_block(DEVICE *dev);
-int write_dev(DEVICE *dev, char *buf, size_t len);
-int read_dev(DEVICE *dev, char *buf, size_t len);
-int status_dev(DEVICE *dev, uint32_t *status);
-int eod_dev(DEVICE *dev);
-int fsf_dev(DEVICE *dev, int num);
-int fsr_dev(DEVICE *dev, int num);
-int bsf_dev(DEVICE *dev, int num);
-int bsr_dev(DEVICE *dev, int num);
-void attach_jcr_to_device(DEVICE *dev, JCR *jcr);
-void detach_jcr_from_device(DEVICE *dev, JCR *jcr);
-JCR *next_attached_jcr(DEVICE *dev, JCR *jcr);
-int dev_can_write(DEVICE *dev);
-int offline_or_rewind_dev(DEVICE *dev);
+DEVICE *init_dev(DEVICE *dev, DEVRES *device);
+int open_dev(DEVICE *dev, char *VolName, int mode);
+void close_dev(DEVICE *dev);
+void force_close_dev(DEVICE *dev);
+int truncate_dev(DEVICE *dev);
+void term_dev(DEVICE *dev);
+char * strerror_dev(DEVICE *dev);
+void clrerror_dev(DEVICE *dev, int func);
+int update_pos_dev(DEVICE *dev);
+int rewind_dev(DEVICE *dev);
+int load_dev(DEVICE *dev);
+int offline_dev(DEVICE *dev);
+int flush_dev(DEVICE *dev);
+int weof_dev(DEVICE *dev, int num);
+int write_block(DEVICE *dev);
+int write_dev(DEVICE *dev, char *buf, size_t len);
+int read_dev(DEVICE *dev, char *buf, size_t len);
+int status_dev(DEVICE *dev, uint32_t *status);
+int eod_dev(DEVICE *dev);
+int fsf_dev(DEVICE *dev, int num);
+int fsr_dev(DEVICE *dev, int num);
+int bsf_dev(DEVICE *dev, int num);
+int bsr_dev(DEVICE *dev, int num);
+void attach_jcr_to_device(DEVICE *dev, JCR *jcr);
+void detach_jcr_from_device(DEVICE *dev, JCR *jcr);
+JCR *next_attached_jcr(DEVICE *dev, JCR *jcr);
+int dev_can_write(DEVICE *dev);
+int offline_or_rewind_dev(DEVICE *dev);
+int reposition_dev(DEVICE *dev, uint32_t file, uint32_t block);
/* Get info about device */
-char * dev_name(DEVICE *dev);
-char * dev_vol_name(DEVICE *dev);
+char * dev_name(DEVICE *dev);
+char * dev_vol_name(DEVICE *dev);
uint32_t dev_block(DEVICE *dev);
uint32_t dev_file(DEVICE *dev);
-int dev_is_tape(DEVICE *dev);
+int dev_is_tape(DEVICE *dev);
/* From device.c */
-int open_device(DEVICE *dev);
-int fixup_device_block_write_error(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+int open_device(DEVICE *dev);
+int fixup_device_block_write_error(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
void _lock_device(char *file, int line, DEVICE *dev);
void _unlock_device(char *file, int line, DEVICE *dev);
void _block_device(char *file, int line, DEVICE *dev, int state);
void _steal_device_lock(char *file, int line, DEVICE *dev, bsteal_lock_t *hold, int state);
void _give_back_device_lock(char *file, int line, DEVICE *dev, bsteal_lock_t *hold);
void set_new_volume_parameters(JCR *jcr, DEVICE *dev);
+void set_new_file_parameters(JCR *jcr, DEVICE *dev);
/* From dircmd.c */
-void *connection_request(void *arg);
+void *connection_request(void *arg);
/* From fd_cmds.c */
-void run_job(JCR *jcr);
+void run_job(JCR *jcr);
/* From job.c */
-void stored_free_jcr(JCR *jcr);
-void connection_from_filed(void *arg);
-void handle_filed_connection(BSOCK *fd, char *job_name);
+void stored_free_jcr(JCR *jcr);
+void connection_from_filed(void *arg);
+void handle_filed_connection(BSOCK *fd, char *job_name);
/* From label.c */
-int read_dev_volume_label(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-void create_session_label(JCR *jcr, DEV_RECORD *rec, int label);
-void create_volume_label(DEVICE *dev, char *VolName);
-int write_volume_label_to_dev(JCR *jcr, DEVRES *device, char *VolName, char *PoolName);
-int write_session_label(JCR *jcr, DEV_BLOCK *block, int label);
-int write_volume_label_to_block(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-void dump_volume_label(DEVICE *dev);
-void dump_label_record(DEVICE *dev, DEV_RECORD *rec, int verbose);
-int unser_volume_label(DEVICE *dev, DEV_RECORD *rec);
-int unser_session_label(SESSION_LABEL *label, DEV_RECORD *rec);
+int read_dev_volume_label(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+void create_session_label(JCR *jcr, DEV_RECORD *rec, int label);
+void create_volume_label(DEVICE *dev, char *VolName);
+int write_volume_label_to_dev(JCR *jcr, DEVRES *device, char *VolName, char *PoolName);
+int write_session_label(JCR *jcr, DEV_BLOCK *block, int label);
+int write_volume_label_to_block(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+void dump_volume_label(DEVICE *dev);
+void dump_label_record(DEVICE *dev, DEV_RECORD *rec, int verbose);
+int unser_volume_label(DEVICE *dev, DEV_RECORD *rec);
+int unser_session_label(SESSION_LABEL *label, DEV_RECORD *rec);
/* From match_bsr.c */
int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec,
- SESSION_LABEL *sesrec);
+ SESSION_LABEL *sesrec);
int match_bsr_block(BSR *bsr, DEV_BLOCK *block);
void position_bsr_block(BSR *bsr, DEV_BLOCK *block);
+BSR *find_next_bsr(BSR *root_bsr, DEVICE *dev);
/* From mount.c */
-int mount_next_write_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, int release);
-int mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-void release_volume(JCR *jcr, DEVICE *dev);
+int mount_next_write_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, int release);
+int mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+void release_volume(JCR *jcr, DEVICE *dev);
/* From autochanger.c */
-int autoload_device(JCR *jcr, DEVICE *dev, int writing, BSOCK *dir);
-int autochanger_list(JCR *jcr, DEVICE *dev, BSOCK *dir);
-void invalidate_slot_in_catalog(JCR *jcr);
+int autoload_device(JCR *jcr, DEVICE *dev, int writing, BSOCK *dir);
+int autochanger_list(JCR *jcr, DEVICE *dev, BSOCK *dir);
+void invalidate_slot_in_catalog(JCR *jcr);
/* From parse_bsr.c */
extern BSR *parse_bsr(JCR *jcr, char *lf);
-extern void dump_bsr(BSR *bsr);
+void dump_bsr(BSR *bsr, bool recurse);
extern void free_bsr(BSR *bsr);
extern VOL_LIST *new_vol();
extern int add_vol(JCR *jcr, VOL_LIST *vol);
/* From record.c */
char *FI_to_ascii(int fi);
char *stream_to_ascii(int stream, int fi);
-int write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec);
-int can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec);
-int read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec);
+int write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec);
+int can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec);
+int read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec);
DEV_RECORD *new_record();
-void free_record(DEV_RECORD *rec);
+void free_record(DEV_RECORD *rec);
/* From read_record.c */
int read_records(JCR *jcr, DEVICE *dev,
int ok = TRUE;
bool done = false;
SESSION_LABEL sessrec;
- dlist *recs;
+ dlist *recs; /* linked list of rec packets open */
block = new_block(dev);
recs = new dlist(rec, &rec->link);
}
if (!read_block_from_device(jcr, dev, block, CHECK_BLOCK_NUMBERS)) {
Dmsg0(20, "!read_record()\n");
- if (dev->state & ST_EOT) {
+ if (dev_state(dev, ST_EOT)) {
DEV_RECORD *trec = new_record();
Dmsg3(100, "EOT. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec),
get_session_record(dev, trec, &sessrec);
ok = record_cb(jcr, dev, block, trec);
free_record(trec);
- } else if (dev->state & ST_EOF) {
+ /*
+ * Now find and position to first file and block
+ * on this tape.
+ */
+ BSR *bsr = find_next_bsr(jcr->bsr, dev);
+ if (bsr == NULL && jcr->bsr->mount_next_volume) {
+ Dmsg0(100, "Would mount next volume here\n");
+ }
+ if (bsr) {
+ Dmsg4(100, "Reposition new tape from (file:block) %d:%d to %d:%d\n",
+ dev->file, dev->block_num, bsr->volfile->sfile,
+ bsr->volblock->sblock);
+ reposition_dev(dev, bsr->volfile->sfile, bsr->volblock->sblock);
+ Dmsg2(100, "Now at (file:block) %d:%d\n",
+ dev->file, dev->block_num);
+ }
+ } else if (dev_state(dev, ST_EOF)) {
Jmsg(jcr, M_INFO, 0, "Got EOF at file %u on device %s, Volume \"%s\"\n",
dev->file, dev_name(dev), jcr->VolumeName);
Dmsg0(20, "read_record got eof. try again\n");
continue;
- } else if (dev->state & ST_SHORT) {
+ } else if (dev_state(dev, ST_SHORT)) {
Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
continue;
} else {
break;
}
}
+ Dmsg5(100, "Read block: dev=%d blk=%d VI=%u VT=%u blen=%d\n", dev->block_num, block->BlockNumber,
+ block->VolSessionId, block->VolSessionTime, block->block_len);
if (!match_bsr_block(jcr->bsr, block)) {
- Dmsg5(150, "reject Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
+ Dmsg5(100, "reject Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
block->BlockNumber, block->block_len, block->BlockVer,
block->VolSessionId, block->VolSessionTime);
continue;
Dmsg2(100, "New record for SI=%d ST=%d\n",
block->VolSessionId, block->VolSessionTime);
} else {
- if ((rec->Block+1) != block->BlockNumber) {
+ if (rec->Block != 0 && (rec->Block+1) != block->BlockNumber) {
Jmsg(jcr, M_ERROR, 0, _("Invalid block number. Expected %u, got %u\n"),
rec->Block+1, block->BlockNumber);
}
block->BlockNumber, rec->remainder);
break;
}
- Dmsg3(10, "read-OK. stat=%s blk=%d rem=%d\n", rec_state_to_str(rec),
- block->BlockNumber, rec->remainder);
+ Dmsg5(100, "read-OK. stat=%s blk=%d rem=%d file:block=%d:%d\n",
+ rec_state_to_str(rec), block->BlockNumber, rec->remainder,
+ dev->file, dev->block_num);
/*
* At this point, we have at least a record header.
* Now decide if we want this record or not, but remember
* get all the data.
*/
record++;
- Dmsg6(30, "recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
+ Dmsg6(100, "recno=%d state=%s blk=%d SI=%d ST=%d FI=%d\n", record,
rec_state_to_str(rec), block->BlockNumber,
rec->VolSessionId, rec->VolSessionTime, rec->FileIndex);
Dmsg4(30, "VolSId=%ld FI=%s Strm=%s Size=%ld\n", rec->VolSessionId,
done = true; /* all items found, stop */
break;
} else if (stat == 0) { /* no match */
+ BSR *bsr;
+ bsr = find_next_bsr(jcr->bsr, dev);
+ if (bsr == NULL && jcr->bsr->mount_next_volume) {
+ Dmsg0(100, "Would mount next volume here\n");
+ Dmsg2(100, "Current postion (file:block) %d:%d\n",
+ dev->file, dev->block_num);
+ dev->state |= ST_EOT;
+ rec->Block = 0;
+ break;
+ }
+ if (bsr) {
+ Dmsg4(100, "Reposition from (file:block) %d:%d to %d:%d\n",
+ dev->file, dev->block_num, bsr->volfile->sfile,
+ bsr->volblock->sblock);
+ reposition_dev(dev, bsr->volfile->sfile, bsr->volblock->sblock);
+ rec->Block = 0;
+ Dmsg2(100, "Now at (file:block) %d:%d\n",
+ dev->file, dev->block_num);
+ }
Dmsg5(10, "BSR no match rec=%d block=%d SessId=%d SessTime=%d FI=%d\n",
record, block->BlockNumber, rec->VolSessionId, rec->VolSessionTime,
rec->FileIndex);