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);
38 static void print_ls_output(char *fname, struct stat *statp);
41 static DEVICE *dev = NULL;
49 "Usage: bscan [-d debug_level] <bacula-archive>\n"
50 " -dnn set debug level to nn\n"
51 " -? print this message\n\n");
55 static void my_free_jcr(JCR *jcr)
60 int main (int argc, char *argv[])
64 my_name_is(argc, argv, "bscan");
68 while ((ch = getopt(argc, argv, "b:d:?")) != -1) {
71 bsr = parse_bsr(NULL, optarg);
73 case 'd': /* debug level */
74 debug_level = atoi(optarg);
89 Pmsg0(0, "Wrong number of arguments: \n");
93 jcr = new_jcr(sizeof(JCR), my_free_jcr);
94 jcr->VolSessionId = 1;
95 jcr->VolSessionTime = (uint32_t)time(NULL);
98 strcpy(jcr->Job, "bscan");
99 jcr->dev_name = get_pool_memory(PM_FNAME);
100 strcpy(jcr->dev_name, argv[0]);
102 /* *** FIXME **** need to put in corect db, user, and password */
103 if ((db=db_init_database(NULL, "bacula", "bacula", "")) == NULL) {
104 Emsg0(M_ABORT, 0, "Could not init Bacula database\n");
106 if (!db_open_database(db)) {
107 Emsg0(M_ABORT, 0, db_strerror(db));
109 Dmsg0(200, "Database opened\n");
119 static void do_scan(char *devname)
125 long record_file_index;
128 POOLMEM *fname; /* original file name */
129 POOLMEM *ofile; /* output name with prefix */
130 POOLMEM *lname; /* link name */
135 if (strncmp(devname, "/dev/", 5) != 0) {
136 /* Try stripping file part */
137 p = devname + strlen(devname);
138 while (p >= devname && *p != '/') {
142 strcpy(VolName, p+1);
146 strcpy(jcr->VolumeName, VolName);
148 dev = init_dev(NULL, devname);
149 if (!dev || !open_device(dev)) {
150 Emsg1(M_ABORT, 0, "Cannot open %s\n", devname);
152 Dmsg0(90, "Device opened for read.\n");
154 fname = get_pool_memory(PM_FNAME);
155 ofile = get_pool_memory(PM_FNAME);
156 lname = get_pool_memory(PM_FNAME);
158 block = new_block(dev);
160 create_vol_list(jcr);
162 if (!acquire_device_for_read(jcr, dev, block)) {
163 Emsg1(M_ABORT, 0, "Cannot open %s\n", devname);
167 free_pool_memory(rec->data);
168 rec->data = get_memory(70000);
170 memset(&mr, 0, sizeof(mr));
171 memset(&pr, 0, sizeof(pr));
174 if (!read_block_from_device(dev, block)) {
176 if (dev->state & ST_EOT) {
177 if (!mount_next_read_volume(jcr, dev, block)) {
182 if (dev->state & ST_EOF) {
183 continue; /* try again */
185 if (dev->state & ST_SHORT) {
188 Pmsg0(0, "Read Record got a bad record\n");
189 status_dev(dev, &status);
190 Dmsg1(20, "Device status: %x\n", status);
192 Emsg0(M_ABORT, 0, "Unexpected End of Data\n");
193 else if (status & MT_EOT)
194 Emsg0(M_ABORT, 0, "Unexpected End of Tape\n");
195 else if (status & MT_EOF)
196 Emsg0(M_ABORT, 0, "Unexpected End of File\n");
197 else if (status & MT_DR_OPEN)
198 Emsg0(M_ABORT, 0, "Tape Door is Open\n");
199 else if (!(status & MT_ONLINE))
200 Emsg0(M_ABORT, 0, "Unexpected Tape is Off-line\n");
202 Emsg3(M_ABORT, 0, "Read error %d on Record Header %s: %s\n",
203 status, dev_name(dev), strerror(errno));
206 for (rec->state=0; !is_block_empty(rec); ) {
207 SESSION_LABEL label, elabel;
208 if (!read_record_from_block(block, rec)) {
213 /* This is no longer used */
214 if (rec->VolSessionId == 0 && rec->VolSessionTime == 0) {
215 Emsg0(M_ERROR, 0, "Zero header record. This shouldn't happen.\n");
216 break; /* END OF FILE */
220 * Check for Start or End of Session Record
223 if (rec->FileIndex < 0) {
225 if (debug_level > 1) {
226 dump_label_record(dev, rec, 1);
228 switch (rec->FileIndex) {
230 Pmsg0(000, "Volume is prelabeled. This tape cannot be scanned.\n");
234 unser_volume_label(dev, rec);
235 strcpy(mr.VolumeName, dev->VolHdr.VolName);
236 if (!db_get_media_record(db, &mr)) {
237 Pmsg1(000, "VOL_LABEL: Media record not found for Volume: %s\n",
241 if (strcmp(mr.MediaType, dev->VolHdr.MediaType) != 0) {
242 Pmsg2(000, "VOL_LABEL: MediaType mismatch. DB=%s Vol=%s\n",
243 mr.MediaType, dev->VolHdr.MediaType);
246 strcpy(pr.Name, dev->VolHdr.PoolName);
247 if (!db_get_pool_record(db, &pr)) {
248 Pmsg1(000, "VOL_LABEL: Pool record not found for Pool: %s\n",
252 if (strcmp(pr.PoolType, dev->VolHdr.PoolType) != 0) {
253 Pmsg2(000, "VOL_LABEL: PoolType mismatch. DB=%s Vol=%s\n",
254 pr.PoolType, dev->VolHdr.PoolType);
257 Pmsg1(000, "VOL_LABEL: OK for Volume: %s\n", mr.VolumeName);
260 unser_session_label(&label, rec);
261 memset(&jr, 0, sizeof(jr));
262 jr.JobId = label.JobId;
263 if (!db_get_job_record(db, &jr)) {
264 Pmsg1(000, "SOS_LABEL: Job record not found for JobId: %d\n",
268 if (rec->VolSessionId != jr.VolSessionId) {
269 Pmsg2(000, "SOS_LABEL: VolSessId mismatch. DB=%d Vol=%d\n",
270 jr.VolSessionId, rec->VolSessionId);
273 if (rec->VolSessionTime != jr.VolSessionTime) {
274 Pmsg2(000, "SOS_LABEL: VolSessTime mismatch. DB=%d Vol=%d\n",
275 jr.VolSessionTime, rec->VolSessionTime);
278 if (jr.PoolId != pr.PoolId) {
279 Pmsg2(000, "SOS_LABEL: PoolId mismatch. DB=%d Vol=%d\n",
280 jr.PoolId, pr.PoolId);
285 unser_session_label(&elabel, rec);
286 if (elabel.JobId != label.JobId) {
287 Pmsg2(000, "EOS_LABEL: Start/End JobId mismatch. Start=%d End=%d\n",
288 label.JobId, elabel.JobId);
291 if (elabel.JobFiles != jr.JobFiles) {
292 Pmsg2(000, "EOS_LABEL: JobFiles mismatch. DB=%d EOS=%d\n",
293 jr.JobFiles, elabel.JobFiles);
296 if (elabel.JobBytes != jr.JobBytes) {
297 Pmsg2(000, "EOS_LABEL: JobBytes mismatch. DB=%d EOS=%d\n",
298 jr.JobBytes, elabel.JobBytes);
301 Pmsg1(000, "EOS_LABEL: OK for JobId=%d\n", elabel.JobId);
311 /* Is this the file we want? */
312 if (bsr && !match_bsr(bsr, rec, &dev->VolHdr, &label)) {
316 if (is_partial_record(rec)) {
320 /* File Attributes stream */
321 if (rec->Stream == STREAM_UNIX_ATTRIBUTES) {
324 if (sizeof_pool_memory(fname) < rec->data_len) {
325 fname = realloc_pool_memory(fname, rec->data_len + 1);
327 if (sizeof_pool_memory(lname) < rec->data_len) {
328 lname = realloc_pool_memory(lname, rec->data_len + 1);
334 * An Attributes record consists of:
339 * Link name (if file linked i.e. FT_LNK)
342 sscanf(rec->data, "%ld %d", &record_file_index, &type);
343 if (record_file_index != rec->FileIndex)
344 Emsg2(M_ERROR_TERM, 0, "Record header file index %ld not equal record index %ld\n",
345 rec->FileIndex, record_file_index);
347 while (*ap++ != ' ') /* skip record file index */
349 while (*ap++ != ' ') /* skip type */
351 /* Save filename and position to attributes */
356 *fp = *ap++; /* terminate filename & point to attribs */
358 /* Skip through attributes to link name */
359 if (type == FT_LNK) {
364 strcat(lname, lp); /* "save" link name */
369 decode_stat(ap, &statp);
371 if (debug_level > 1) {
372 print_ls_output(fname, &statp);
375 /* Data stream and extracting */
376 } else if (rec->Stream == STREAM_FILE_DATA) {
378 } else if (rec->Stream != STREAM_MD5_SIGNATURE) {
379 Pmsg2(0, "None of above!!! stream=%d data=%s\n", rec->Stream, rec->data);
384 release_device(jcr, dev, block);
386 free_pool_memory(fname);
387 free_pool_memory(ofile);
388 free_pool_memory(lname);
395 extern char *getuser(uid_t uid);
396 extern char *getgroup(gid_t gid);
398 static void print_ls_output(char *fname, struct stat *statp)
404 p = encode_mode(statp->st_mode, buf);
405 n = sprintf(p, " %2d ", (uint32_t)statp->st_nlink);
407 n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid), getgroup(statp->st_gid));
409 n = sprintf(p, "%8lld ", (uint64_t)statp->st_size);
411 p = encode_time(statp->st_ctime, p);