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;
48 "Usage: bscan [-d debug_level] <bacula-archive>\n"
49 " -dnn set debug level to nn\n"
50 " -? print this message\n\n");
54 static void my_free_jcr(JCR *jcr)
59 int main (int argc, char *argv[])
63 my_name_is(argc, argv, "bscan");
67 while ((ch = getopt(argc, argv, "d:?")) != -1) {
69 case 'd': /* debug level */
70 debug_level = atoi(optarg);
85 Pmsg0(0, "Wrong number of arguments: \n");
89 jcr = new_jcr(sizeof(JCR), my_free_jcr);
90 jcr->VolSessionId = 1;
91 jcr->VolSessionTime = (uint32_t)time(NULL);
94 /* *** FIXME **** need to put in corect db, user, and password */
95 if ((db=db_init_database("bacula", "bacula", "")) == NULL) {
96 Emsg0(M_ABORT, 0, "Could not init Bacula database\n");
98 if (!db_open_database(db)) {
99 Emsg0(M_ABORT, 0, db_strerror(db));
101 Dmsg0(200, "Database opened\n");
111 static void do_scan(char *devname)
118 long record_file_index;
121 char *fname; /* original file name */
122 char *ofile; /* output name with prefix */
123 char *lname; /* link name */
128 if (strncmp(devname, "/dev/", 5) != 0) {
129 /* Try stripping file part */
130 p = devname + strlen(devname);
131 while (p >= devname && *p != '/') {
135 strcpy(VolName, p+1);
140 dev = init_dev(NULL, devname);
141 if (!dev || !open_device(dev)) {
142 Emsg1(M_ABORT, 0, "Cannot open %s\n", devname);
144 Dmsg0(90, "Device opened for read.\n");
146 fname = (char *)get_pool_memory(PM_FNAME);
147 ofile = (char *)get_pool_memory(PM_FNAME);
148 lname = (char *)get_pool_memory(PM_FNAME);
150 block = new_block(dev);
152 strcpy(jcr->VolumeName, VolName);
154 if (!acquire_device_for_read(jcr, dev, block)) {
155 Emsg1(M_ABORT, 0, "Cannot open %s\n", devname);
158 memset(&rec, 0, sizeof(rec));
159 rec.data = (char *)get_memory(70000);
161 memset(&mr, 0, sizeof(mr));
162 memset(&pr, 0, sizeof(pr));
165 if (!read_record(dev, block, &rec)) {
167 if (dev->state & ST_EOT) {
170 if (dev->state & ST_EOF) {
171 continue; /* try again */
173 Pmsg0(0, "Read Record got a bad record\n");
174 status_dev(dev, &status);
175 Dmsg1(20, "Device status: %x\n", status);
177 Emsg0(M_ABORT, 0, "Unexpected End of Data\n");
178 else if (status & MT_EOT)
179 Emsg0(M_ABORT, 0, "Unexpected End of Tape\n");
180 else if (status & MT_EOF)
181 Emsg0(M_ABORT, 0, "Unexpected End of File\n");
182 else if (status & MT_DR_OPEN)
183 Emsg0(M_ABORT, 0, "Tape Door is Open\n");
184 else if (!(status & MT_ONLINE))
185 Emsg0(M_ABORT, 0, "Unexpected Tape is Off-line\n");
187 Emsg3(M_ABORT, 0, "Read error %d on Record Header %s: %s\n", n, dev_name(dev), strerror(errno));
191 /* This is no longer used */
192 if (rec.VolSessionId == 0 && rec.VolSessionTime == 0) {
193 Emsg0(M_ERROR, 0, "Zero header record. This shouldn't happen.\n");
194 break; /* END OF FILE */
198 * Check for Start or End of Session Record
201 if (rec.FileIndex < 0) {
202 SESSION_LABEL label, elabel;
204 if (debug_level > 1) {
205 dump_label_record(dev, &rec, 1);
207 switch (rec.FileIndex) {
209 Pmsg0(000, "Volume is prelabeled. This tape cannot be scanned.\n");
213 unser_volume_label(dev, &rec);
214 strcpy(mr.VolumeName, dev->VolHdr.VolName);
215 if (!db_get_media_record(db, &mr)) {
216 Pmsg1(000, "VOL_LABEL: Media record not found for Volume: %s\n",
220 if (strcmp(mr.MediaType, dev->VolHdr.MediaType) != 0) {
221 Pmsg2(000, "VOL_LABEL: MediaType mismatch. DB=%s Vol=%s\n",
222 mr.MediaType, dev->VolHdr.MediaType);
225 strcpy(pr.Name, dev->VolHdr.PoolName);
226 if (!db_get_pool_record(db, &pr)) {
227 Pmsg1(000, "VOL_LABEL: Pool record not found for Pool: %s\n",
231 if (strcmp(pr.PoolType, dev->VolHdr.PoolType) != 0) {
232 Pmsg2(000, "VOL_LABEL: PoolType mismatch. DB=%s Vol=%s\n",
233 pr.PoolType, dev->VolHdr.PoolType);
236 Pmsg1(000, "VOL_LABEL: OK for Volume: %s\n", mr.VolumeName);
239 unser_session_label(&label, &rec);
240 memset(&jr, 0, sizeof(jr));
241 jr.JobId = label.JobId;
242 if (!db_get_job_record(db, &jr)) {
243 Pmsg1(000, "SOS_LABEL: Job record not found for JobId: %d\n",
247 if (rec.VolSessionId != jr.VolSessionId) {
248 Pmsg2(000, "SOS_LABEL: VolSessId mismatch. DB=%d Vol=%d\n",
249 jr.VolSessionId, rec.VolSessionId);
252 if (rec.VolSessionTime != jr.VolSessionTime) {
253 Pmsg2(000, "SOS_LABEL: VolSessTime mismatch. DB=%d Vol=%d\n",
254 jr.VolSessionTime, rec.VolSessionTime);
257 if (jr.PoolId != pr.PoolId) {
258 Pmsg2(000, "SOS_LABEL: PoolId mismatch. DB=%d Vol=%d\n",
259 jr.PoolId, pr.PoolId);
264 unser_session_label(&elabel, &rec);
265 if (elabel.JobId != label.JobId) {
266 Pmsg2(000, "EOS_LABEL: Start/End JobId mismatch. Start=%d End=%d\n",
267 label.JobId, elabel.JobId);
270 if (elabel.JobFiles != jr.JobFiles) {
271 Pmsg2(000, "EOS_LABEL: JobFiles mismatch. DB=%d EOS=%d\n",
272 jr.JobFiles, elabel.JobFiles);
275 if (elabel.JobBytes != jr.JobBytes) {
276 Pmsg2(000, "EOS_LABEL: JobBytes mismatch. DB=%d EOS=%d\n",
277 jr.JobBytes, elabel.JobBytes);
280 Pmsg1(000, "EOS_LABEL: OK for JobId=%d\n", elabel.JobId);
290 /* File Attributes stream */
291 if (rec.Stream == STREAM_UNIX_ATTRIBUTES) {
294 if (sizeof_pool_memory(fname) < rec.data_len) {
295 fname = (char *)realloc_pool_memory(fname, rec.data_len + 1);
297 if (sizeof_pool_memory(lname) < rec.data_len) {
298 ofile = (char *)realloc_pool_memory(ofile, rec.data_len + 1);
304 * An Attributes record consists of:
309 * Link name (if file linked i.e. FT_LNK)
312 sscanf(rec.data, "%ld %d %s", &record_file_index, &type, fname);
313 if (record_file_index != rec.FileIndex)
314 Emsg2(M_ABORT, 0, "Record header file index %ld not equal record index %ld\n",
315 rec.FileIndex, record_file_index);
317 /* Skip to attributes */
320 /* Skip to Link name */
321 if (type == FT_LNK) {
326 strcat(lname, lp); /* "save" link name */
332 decode_stat(ap, &statp);
333 /* Dmsg1(000, "Restoring: %s\n", ofile); */
335 if (debug_level > 1) {
336 print_ls_output(fname, &statp);
339 /* Data stream and extracting */
340 } else if (rec.Stream == STREAM_FILE_DATA) {
342 } else if (rec.Stream != STREAM_MD5_SIGNATURE) {
343 Pmsg2(0, "None of above!!! stream=%d data=%s\n", rec.Stream, rec.data);
347 release_device(jcr, dev, block);
349 free_pool_memory(fname);
350 free_pool_memory(ofile);
351 free_pool_memory(lname);
357 extern char *getuser(uid_t uid);
358 extern char *getgroup(gid_t gid);
360 static void print_ls_output(char *fname, struct stat *statp)
366 p = encode_mode(statp->st_mode, buf);
367 n = sprintf(p, " %2d ", (uint32_t)statp->st_nlink);
369 n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid), getgroup(statp->st_gid));
371 n = sprintf(p, "%8lld ", (uint64_t)statp->st_size);
373 p = encode_time(statp->st_ctime, p);