3 * Program to scan a Bacula tape and compare it with
4 * the catalog and optionally synchronize the catalog
7 * Kern E. Sibbald, December 2001
13 Copyright (C) 2001, 2002 Kern Sibbald and John Walker
15 This program is free software; you can redistribute it and/or
16 modify it under the terms of the GNU General Public License as
17 published by the Free Software Foundation; either version 2 of
18 the License, or (at your option) any later version.
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 General Public License for more details.
25 You should have received a copy of the GNU General Public
26 License along with this program; if not, write to the Free
27 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
34 #include "findlib/find.h"
35 #include "cats/cats.h"
37 static void do_scan(char *fname);
40 static DEVICE *dev = NULL;
48 "\nVersion: " VERSION " (" DATE ")\n\n"
49 "Usage: bscan [-d debug_level] <bacula-archive>\n"
50 " -dnn set debug level to nn\n"
51 " -? print this message\n\n");
55 int main (int argc, char *argv[])
59 my_name_is(argc, argv, "bscan");
63 while ((ch = getopt(argc, argv, "b:d:?")) != -1) {
66 bsr = parse_bsr(NULL, optarg);
68 case 'd': /* debug level */
69 debug_level = atoi(optarg);
84 Pmsg0(0, "Wrong number of arguments: \n");
88 jcr = setup_jcr("bscan", argv[0], bsr);
90 /* *** FIXME **** need to put in corect db, user, and password */
91 if ((db=db_init_database(NULL, "bacula", "bacula", "")) == NULL) {
92 Emsg0(M_ABORT, 0, "Could not init Bacula database\n");
94 if (!db_open_database(db)) {
95 Emsg0(M_ABORT, 0, db_strerror(db));
97 Dmsg0(200, "Database opened\n");
107 static void do_scan(char *devname)
111 long record_file_index;
114 POOLMEM *fname; /* original file name */
115 POOLMEM *ofile; /* output name with prefix */
116 POOLMEM *lname; /* link name */
121 dev = setup_to_read_device(jcr);
126 fname = get_pool_memory(PM_FNAME);
127 ofile = get_pool_memory(PM_FNAME);
128 lname = get_pool_memory(PM_FNAME);
130 block = new_block(dev);
133 free_pool_memory(rec->data);
134 rec->data = get_memory(70000);
136 memset(&mr, 0, sizeof(mr));
137 memset(&pr, 0, sizeof(pr));
140 if (!read_block_from_device(dev, block)) {
142 if (dev->state & ST_EOT) {
143 if (!mount_next_read_volume(jcr, dev, block)) {
148 if (dev->state & ST_EOF) {
149 continue; /* try again */
151 if (dev->state & ST_SHORT) {
154 Pmsg0(0, "Read Record got a bad record\n");
155 status_dev(dev, &status);
156 Dmsg1(20, "Device status: %x\n", status);
158 Emsg0(M_ABORT, 0, "Unexpected End of Data\n");
159 else if (status & MT_EOT)
160 Emsg0(M_ABORT, 0, "Unexpected End of Tape\n");
161 else if (status & MT_EOF)
162 Emsg0(M_ABORT, 0, "Unexpected End of File\n");
163 else if (status & MT_DR_OPEN)
164 Emsg0(M_ABORT, 0, "Tape Door is Open\n");
165 else if (!(status & MT_ONLINE))
166 Emsg0(M_ABORT, 0, "Unexpected Tape is Off-line\n");
168 Emsg3(M_ABORT, 0, "Read error %d on Record Header %s: %s\n",
169 status, dev_name(dev), strerror(errno));
172 for (rec->state=0; !is_block_empty(rec); ) {
173 SESSION_LABEL label, elabel;
174 if (!read_record_from_block(block, rec)) {
179 /* This is no longer used */
180 if (rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
181 Emsg0(M_ERROR, 0, "Zero header record. This shouldn't happen.\n");
182 break; /* END OF FILE */
186 * Check for Start or End of Session Record
189 if (rec->FileIndex < 0) {
191 if (debug_level > 1) {
192 dump_label_record(dev, rec, 1);
194 switch (rec->FileIndex) {
196 Pmsg0(000, "Volume is prelabeled. This tape cannot be scanned.\n");
200 unser_volume_label(dev, rec);
201 strcpy(mr.VolumeName, dev->VolHdr.VolName);
202 if (!db_get_media_record(db, &mr)) {
203 Pmsg1(000, "VOL_LABEL: Media record not found for Volume: %s\n",
207 if (strcmp(mr.MediaType, dev->VolHdr.MediaType) != 0) {
208 Pmsg2(000, "VOL_LABEL: MediaType mismatch. DB=%s Vol=%s\n",
209 mr.MediaType, dev->VolHdr.MediaType);
212 strcpy(pr.Name, dev->VolHdr.PoolName);
213 if (!db_get_pool_record(db, &pr)) {
214 Pmsg1(000, "VOL_LABEL: Pool record not found for Pool: %s\n",
218 if (strcmp(pr.PoolType, dev->VolHdr.PoolType) != 0) {
219 Pmsg2(000, "VOL_LABEL: PoolType mismatch. DB=%s Vol=%s\n",
220 pr.PoolType, dev->VolHdr.PoolType);
223 Pmsg1(000, "VOL_LABEL: OK for Volume: %s\n", mr.VolumeName);
226 unser_session_label(&label, rec);
227 memset(&jr, 0, sizeof(jr));
228 jr.JobId = label.JobId;
229 if (!db_get_job_record(db, &jr)) {
230 Pmsg1(000, "SOS_LABEL: Job record not found for JobId: %d\n",
234 if (rec->VolSessionId != jr.VolSessionId) {
235 Pmsg2(000, "SOS_LABEL: VolSessId mismatch. DB=%d Vol=%d\n",
236 jr.VolSessionId, rec->VolSessionId);
239 if (rec->VolSessionTime != jr.VolSessionTime) {
240 Pmsg2(000, "SOS_LABEL: VolSessTime mismatch. DB=%d Vol=%d\n",
241 jr.VolSessionTime, rec->VolSessionTime);
244 if (jr.PoolId != pr.PoolId) {
245 Pmsg2(000, "SOS_LABEL: PoolId mismatch. DB=%d Vol=%d\n",
246 jr.PoolId, pr.PoolId);
251 unser_session_label(&elabel, rec);
252 if (elabel.JobId != label.JobId) {
253 Pmsg2(000, "EOS_LABEL: Start/End JobId mismatch. Start=%d End=%d\n",
254 label.JobId, elabel.JobId);
257 if (elabel.JobFiles != jr.JobFiles) {
258 Pmsg2(000, "EOS_LABEL: JobFiles mismatch. DB=%d EOS=%d\n",
259 jr.JobFiles, elabel.JobFiles);
262 if (elabel.JobBytes != jr.JobBytes) {
263 Pmsg2(000, "EOS_LABEL: JobBytes mismatch. DB=%d EOS=%d\n",
264 jr.JobBytes, elabel.JobBytes);
267 Pmsg1(000, "EOS_LABEL: OK for JobId=%d\n", elabel.JobId);
277 /* Is this the file we want? */
278 if (bsr && !match_bsr(bsr, rec, &dev->VolHdr, &label)) {
282 if (is_partial_record(rec)) {
286 /* File Attributes stream */
287 if (rec->Stream == STREAM_UNIX_ATTRIBUTES) {
290 if (sizeof_pool_memory(fname) < rec->data_len) {
291 fname = realloc_pool_memory(fname, rec->data_len + 1);
293 if (sizeof_pool_memory(lname) < rec->data_len) {
294 lname = realloc_pool_memory(lname, rec->data_len + 1);
300 * An Attributes record consists of:
305 * Link name (if file linked i.e. FT_LNK)
308 sscanf(rec->data, "%ld %d", &record_file_index, &type);
309 if (record_file_index != rec->FileIndex)
310 Emsg2(M_ERROR_TERM, 0, "Record header file index %ld not equal record index %ld\n",
311 rec->FileIndex, record_file_index);
313 while (*ap++ != ' ') /* skip record file index */
315 while (*ap++ != ' ') /* skip type */
317 /* Save filename and position to attributes */
322 *fp = *ap++; /* terminate filename & point to attribs */
324 /* Skip through attributes to link name */
325 if (type == FT_LNK) {
330 strcat(lname, lp); /* "save" link name */
335 decode_stat(ap, &statp);
337 if (debug_level > 1) {
338 print_ls_output(fname, lname, type, &statp);
341 /* Data stream and extracting */
342 } else if (rec->Stream == STREAM_FILE_DATA) {
344 } else if (rec->Stream != STREAM_MD5_SIGNATURE) {
345 Pmsg2(0, "None of above!!! stream=%d data=%s\n", rec->Stream, rec->data);
350 release_device(jcr, dev, block);
352 free_pool_memory(fname);
353 free_pool_memory(ofile);
354 free_pool_memory(lname);
362 /* Dummies to replace askdir.c */
363 int dir_get_volume_info(JCR *jcr) { return 1;}
364 int dir_find_next_appendable_volume(JCR *jcr) { return 1;}
365 int dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; }
366 int dir_create_jobmedia_record(JCR *jcr) { return 1; }
367 int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
368 int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
369 int dir_send_job_status(JCR *jcr) {return 1;}
372 int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev)
374 fprintf(stderr, "Mount Volume %s on device %s and press return when ready: ",
375 jcr->VolumeName, dev_name(dev));