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
11 Copyright (C) 2001, 2002 Kern Sibbald and John Walker
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation; either version 2 of
16 the License, or (at your option) any later version.
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 General Public License for more details.
23 You should have received a copy of the GNU General Public
24 License along with this program; if not, write to the Free
25 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
32 #include "findlib/find.h"
33 #include "cats/cats.h"
35 static void do_scan(char *fname);
36 static void print_ls_output(char *fname, struct stat *statp);
39 static DEVICE *dev = NULL;
46 "Usage: bscan [-d debug_level] <bacula-archive>\n"
47 " -dnn set debug level to nn\n"
48 " -? print this message\n\n");
52 static void my_free_jcr(JCR *jcr)
57 int main (int argc, char *argv[])
61 my_name_is(argc, argv, "bscan");
64 while ((ch = getopt(argc, argv, "d:?")) != -1) {
66 case 'd': /* debug level */
67 debug_level = atoi(optarg);
82 Dmsg0(0, "Wrong number of arguments: \n");
87 * Ensure that every message is always printed
89 for (i=1; i<=M_MAX; i++) {
90 add_msg_dest(NULL, MD_STDOUT, i, NULL, NULL);
93 jcr = new_jcr(sizeof(JCR), my_free_jcr);
94 jcr->VolSessionId = 1;
95 jcr->VolSessionTime = (uint32_t)time(NULL);
98 /* *** FIXME **** need to put in corect db, user, and password */
99 if ((db=db_init_database("bacula", "bacula", "")) == NULL) {
100 Emsg0(M_ABORT, 0, "Could not init Bacula database\n");
102 if (!db_open_database(db)) {
103 Emsg0(M_ABORT, 0, db_strerror(db));
105 Dmsg0(200, "Database opened\n");
115 static void do_scan(char *devname)
122 long record_file_index;
125 char *fname; /* original file name */
126 char *ofile; /* output name with prefix */
127 char *lname; /* link name */
132 if (strncmp(devname, "/dev/", 5) != 0) {
133 /* Try stripping file part */
134 p = devname + strlen(devname);
135 while (p >= devname && *p != '/') {
139 strcpy(VolName, p+1);
144 dev = init_dev(NULL, devname);
145 if (!dev || !open_device(dev)) {
146 Emsg1(M_ABORT, 0, "Cannot open %s\n", devname);
148 Dmsg0(90, "Device opened for read.\n");
150 fname = (char *)get_pool_memory(PM_FNAME);
151 ofile = (char *)get_pool_memory(PM_FNAME);
152 lname = (char *)get_pool_memory(PM_FNAME);
154 block = new_block(dev);
156 strcpy(jcr->VolumeName, VolName);
158 if (!acquire_device_for_read(jcr, dev, block)) {
159 Emsg1(M_ABORT, 0, "Cannot open %s\n", devname);
162 memset(&rec, 0, sizeof(rec));
163 rec.data = (char *)get_memory(70000);
165 memset(&mr, 0, sizeof(mr));
166 memset(&pr, 0, sizeof(pr));
169 if (!read_record(dev, block, &rec)) {
171 if (dev->state & ST_EOT) {
174 if (dev->state & ST_EOF) {
175 continue; /* try again */
177 Dmsg0(0, "Read Record got a bad record\n");
178 status_dev(dev, &status);
179 Dmsg1(20, "Device status: %x\n", status);
181 Emsg0(M_ABORT, 0, "Unexpected End of Data\n");
182 else if (status & MT_EOT)
183 Emsg0(M_ABORT, 0, "Unexpected End of Tape\n");
184 else if (status & MT_EOF)
185 Emsg0(M_ABORT, 0, "Unexpected End of File\n");
186 else if (status & MT_DR_OPEN)
187 Emsg0(M_ABORT, 0, "Tape Door is Open\n");
188 else if (!(status & MT_ONLINE))
189 Emsg0(M_ABORT, 0, "Unexpected Tape is Off-line\n");
191 Emsg3(M_ABORT, 0, "Read error %d on Record Header %s: %s\n", n, dev_name(dev), strerror(errno));
195 /* This is no longer used */
196 if (rec.VolSessionId == 0 && rec.VolSessionTime == 0) {
197 Emsg0(M_ERROR, 0, "Zero header record. This shouldn't happen.\n");
198 break; /* END OF FILE */
202 * Check for Start or End of Session Record
205 if (rec.FileIndex < 0) {
206 SESSION_LABEL label, elabel;
208 if (debug_level > 1) {
209 dump_label_record(dev, &rec, 1);
211 switch (rec.FileIndex) {
213 Dmsg0(000, "Volume is prelabeled. This tape cannot be scanned.\n");
217 unser_volume_label(dev, &rec);
218 strcpy(mr.VolumeName, dev->VolHdr.VolName);
219 if (!db_get_media_record(db, &mr)) {
220 Dmsg1(000, "VOL_LABEL: Media record not found for Volume: %s\n",
224 if (strcmp(mr.MediaType, dev->VolHdr.MediaType) != 0) {
225 Dmsg2(000, "VOL_LABEL: MediaType mismatch. DB=%s Vol=%s\n",
226 mr.MediaType, dev->VolHdr.MediaType);
229 strcpy(pr.Name, dev->VolHdr.PoolName);
230 if (!db_get_pool_record(db, &pr)) {
231 Dmsg1(000, "VOL_LABEL: Pool record not found for Pool: %s\n",
235 if (strcmp(pr.PoolType, dev->VolHdr.PoolType) != 0) {
236 Dmsg2(000, "VOL_LABEL: PoolType mismatch. DB=%s Vol=%s\n",
237 pr.PoolType, dev->VolHdr.PoolType);
240 Dmsg1(000, "VOL_LABEL: OK for Volume: %s\n", mr.VolumeName);
243 unser_session_label(&label, &rec);
244 memset(&jr, 0, sizeof(jr));
245 jr.JobId = label.JobId;
246 if (!db_get_job_record(db, &jr)) {
247 Dmsg1(000, "SOS_LABEL: Job record not found for JobId: %d\n",
251 if (rec.VolSessionId != jr.VolSessionId) {
252 Dmsg2(000, "SOS_LABEL: VolSessId mismatch. DB=%d Vol=%d\n",
253 jr.VolSessionId, rec.VolSessionId);
256 if (rec.VolSessionTime != jr.VolSessionTime) {
257 Dmsg2(000, "SOS_LABEL: VolSessTime mismatch. DB=%d Vol=%d\n",
258 jr.VolSessionTime, rec.VolSessionTime);
261 if (jr.PoolId != pr.PoolId) {
262 Dmsg2(000, "SOS_LABEL: PoolId mismatch. DB=%d Vol=%d\n",
263 jr.PoolId, pr.PoolId);
268 unser_session_label(&elabel, &rec);
269 if (elabel.JobId != label.JobId) {
270 Dmsg2(000, "EOS_LABEL: Start/End JobId mismatch. Start=%d End=%d\n",
271 label.JobId, elabel.JobId);
274 if (elabel.JobFiles != jr.JobFiles) {
275 Dmsg2(000, "EOS_LABEL: JobFiles mismatch. DB=%d EOS=%d\n",
276 jr.JobFiles, elabel.JobFiles);
279 if (elabel.JobBytes != jr.JobBytes) {
280 Dmsg2(000, "EOS_LABEL: JobBytes mismatch. DB=%d EOS=%d\n",
281 jr.JobBytes, elabel.JobBytes);
284 Dmsg1(000, "EOS_LABEL: OK for JobId=%d\n", elabel.JobId);
294 /* File Attributes stream */
295 if (rec.Stream == STREAM_UNIX_ATTRIBUTES) {
298 if (sizeof_pool_memory(fname) < rec.data_len) {
299 fname = (char *)realloc_pool_memory(fname, rec.data_len + 1);
301 if (sizeof_pool_memory(lname) < rec.data_len) {
302 ofile = (char *)realloc_pool_memory(ofile, rec.data_len + 1);
308 * An Attributes record consists of:
313 * Link name (if file linked i.e. FT_LNK)
316 sscanf(rec.data, "%ld %d %s", &record_file_index, &type, fname);
317 if (record_file_index != rec.FileIndex)
318 Emsg2(M_ABORT, 0, "Record header file index %ld not equal record index %ld\n",
319 rec.FileIndex, record_file_index);
321 /* Skip to attributes */
324 /* Skip to Link name */
325 if (type == FT_LNK) {
330 strcat(lname, lp); /* "save" link name */
336 decode_stat(ap, &statp);
337 /* Dmsg1(000, "Restoring: %s\n", ofile); */
339 if (debug_level > 1) {
340 print_ls_output(fname, &statp);
343 /* Data stream and extracting */
344 } else if (rec.Stream == STREAM_FILE_DATA) {
346 } else if (rec.Stream != STREAM_MD5_SIGNATURE) {
347 Dmsg2(0, "None of above!!! stream=%d data=%s\n", rec.Stream, rec.data);
351 release_device(jcr, dev, block);
353 free_pool_memory(fname);
354 free_pool_memory(ofile);
355 free_pool_memory(lname);
361 extern char *getuser(uid_t uid);
362 extern char *getgroup(gid_t gid);
364 static void print_ls_output(char *fname, struct stat *statp)
370 p = encode_mode(statp->st_mode, buf);
371 n = sprintf(p, " %2d ", (uint32_t)statp->st_nlink);
373 n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid), getgroup(statp->st_gid));
375 n = sprintf(p, "%8lld ", (uint64_t)statp->st_size);
377 p = encode_time(statp->st_ctime, p);