- blocksize recognition code.
For 1.30 release:
+- Fix restore of hard linked file.
+- Implement FileOptions (see end of this document)
+- Implement Bacula plugins -- design API
+- Make bcopy read through bad tape records.
+- Fix read_record to handle multiple sessions.
+- Program files (i.e. execute a program to read/write files).
+ Pass read date of last backup, size of file last time.
- Add Signature type to File DB record.
- CD into subdirectory when open()ing files for backup to
speed up things. Test with testfind().
- Implement finer multiprocessing options.
- Solaris -I on tar for include list
- Enable avoid backing up archive device (findlib/find_one.c:128)
-- Implement FileOptions (see end of this document)
-- Implement Bacula plugins -- design API
-- Make bcopy read through bad tape records.
- Need a verbose mode in restore, perhaps to bsr.
- bscan without -v is too quiet -- perhaps show jobs.
- Add code to reject whole blocks if not wanted on restore.
records -- one per Job, maybe a JCR or some other structure with
a block and a record.
- Figure out how to do a bare metal Windows restore
-- Fix read_record to handle multiple sessions.
-- Program files (i.e. execute a program to read/write files).
- Pass read date of last backup, size of file last time.
- Put system type returned by FD into catalog.
- Possibly add email to Watchdog if drive is unmounted too
long and a job is waiting on the drive.
====== 1.30 =======
- Implement SHA1
- Get correct error status from run_program or open_bpipe().
-
+- Restrict permissions on File Volumes (now 0640).
+- Umasked 022 daemons
FilenameId INTEGER REFERENCES Filename NOT NULL,
MarkId INTEGER UNSIGNED DEFAULT 0,
LStat VARCHAR(255) NOT NULL,
- MD5 VARCHAR(25) NOT NULL,
+ MD5 VARCHAR(255) NOT NULL,
PRIMARY KEY(FileId)
);
* down the tree setting all children if the
* node is a directory.
*/
-static void set_extract(TREE_NODE *node, int value)
+static void set_extract(UAContext *ua, TREE_NODE *node, TREE_CTX *tree, int value)
{
TREE_NODE *n;
+ FILE_DBR fdbr;
+ struct stat statp;
node->extract = value;
+ /* For a non-file (i.e. directory), we see all the children */
if (node->type != TN_FILE) {
for (n=node->child; n; n=n->sibling) {
- set_extract(n, value);
+ set_extract(ua, n, tree, value);
+ }
+ } else if (value) {
+ char cwd[2000];
+ /* Ordinary file, we get the full path, look up the
+ * attributes, decode them, and if we are hard linked to
+ * a file that was saved, we must load that file too.
+ */
+ tree_getpath(node, cwd, sizeof(cwd));
+ fdbr.FileId = 0;
+ fdbr.JobId = node->JobId;
+ if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, &fdbr)) {
+ uint32_t LinkFI;
+ decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
+ /* If we point to a hard linked file, traverse the tree to
+ * find that file, and mark it for restoration as well.
+ */
+ if (LinkFI) {
+ for (n=first_tree_node(tree->root); n; n=next_tree_node(n)) {
+ if (n->FileIndex == LinkFI) {
+ n->extract = 1;
+ break;
+ }
+ }
+ }
}
}
}
}
for (node = tree->node->child; node; node=node->sibling) {
if (fnmatch(ua->argk[1], node->fname, 0) == 0) {
- set_extract(node, 1);
+ set_extract(ua, node, tree, 1);
}
}
return 1;
extern char *getuser(uid_t uid);
extern char *getgroup(gid_t gid);
-static void ls_output(char *buf, char *fname, struct stat *statp)
+/*
+ * This is actually the long form used for "dir"
+ */
+static void ls_output(char *buf, char *fname, int extract, struct stat *statp)
{
char *p, *f;
char ec1[30];
int n;
-// Dmsg2(000, "%s mode=0%o\n", fname, statp->st_mode);
-
p = encode_mode(statp->st_mode, buf);
n = sprintf(p, " %2d ", (uint32_t)statp->st_nlink);
p += n;
p += n;
p = encode_time(statp->st_ctime, p);
*p++ = ' ';
- *p++ = ' ';
+ if (extract) {
+ *p++ = '*';
+ } else {
+ *p++ = ' ';
+ }
for (f=fname; *f; )
*p++ = *f++;
*p = 0;
fdbr.FileId = 0;
fdbr.JobId = node->JobId;
if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, &fdbr)) {
- decode_stat(fdbr.LStat, &statp); /* decode stat pkt */
- ls_output(buf, cwd, &statp);
+ uint32_t LinkFI;
+ decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
+ ls_output(buf, cwd, node->extract, &statp);
bsendmsg(ua, "%s\n", buf);
} else {
/* Something went wrong getting attributes -- print name */
}
for (node = tree->node->child; node; node=node->sibling) {
if (fnmatch(ua->argk[1], node->fname, 0) == 0) {
- set_extract(node, 0);
+ set_extract(ua, node, tree, 0);
}
}
return 1;
* Got attributes stream, decode it
*/
if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_WIN32_ATTRIBUTES) {
+ uint32_t LinkFIf, LinkFIc;
Dmsg2(400, "file_index=%d attr=%s\n", file_index, attr);
jcr->JobFiles++;
jcr->FileIndex = file_index; /* remember attribute file_index */
- decode_stat(attr, &statf); /* decode file stat packet */
+ decode_stat(attr, &statf, &LinkFIf); /* decode file stat packet */
do_SIG = NO_SIG;
jcr->fn_printed = FALSE;
strcpy(jcr->fname, fname); /* move filename into JCR */
Dmsg3(400, "Found %s in catalog. inx=%d Opts=%s\n", jcr->fname,
file_index, Opts_SIG);
- decode_stat(fdbr.LStat, &statc); /* decode catalog stat */
+ decode_stat(fdbr.LStat, &statc, &LinkFIc); /* decode catalog stat */
/*
* Loop over options supplied by user and verify the
* fields he requests.
}
Dmsg1(130, "bfiled: sending %s to stored\n", ff_pkt->fname);
- encode_stat(attribs, &ff_pkt->statp);
+ encode_stat(attribs, &ff_pkt->statp, ff_pkt->LinkFI);
stream = encode_attribsEx(jcr, attribsEx, ff_pkt);
Dmsg3(200, "File %s\nattribs=%s\nattribsEx=%s\n", ff_pkt->fname, attribs, attribsEx);
P(jcr->mutex);
jcr->JobFiles++; /* increment number of files sent */
+ ff_pkt->FileIndex = jcr->JobFiles; /* return FileIndex */
pm_strcpy(&jcr->last_fname, ff_pkt->fname);
V(jcr->mutex);
/* File Attributes stream */
if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_WIN32_ATTRIBUTES) {
char *ap, *lp, *fp, *apex;
+ uint32_t LinkFI;
Dmsg1(30, "Stream=Unix Attributes. extract=%d\n", extract);
/* If extracting, it was from previous stream, so
Dmsg3(200, "File %s\nattrib=%s\nattribsEx=%s\n", fname, ap, attribsEx);
- decode_stat(ap, &statp);
+ decode_stat(ap, &statp, &LinkFI);
/*
* Prepend the where directory so that the
* files are put where the user wants.
fid = -1;
}
- encode_stat(attribs, &ff_pkt->statp);
+ encode_stat(attribs, &ff_pkt->statp, ff_pkt->LinkFI);
P(jcr->mutex);
jcr->JobFiles++; /* increment number of files sent */
/* Encode a stat structure into a base64 character string */
-void encode_stat(char *buf, struct stat *statp)
+void encode_stat(char *buf, struct stat *statp, uint32_t LinkFI)
{
char *p = buf;
/*
p += to_base64((int64_t)statp->st_mtime, p);
*p++ = ' ';
p += to_base64((int64_t)statp->st_ctime, p);
+ *p++ = ' ';
+ p += to_base64((int64_t)LinkFI, p);
*p = 0;
return;
}
/* Decode a stat packet from base64 characters */
void
-decode_stat(char *buf, struct stat *statp)
+decode_stat(char *buf, struct stat *statp, uint32_t *LinkFI)
{
char *p = buf;
int64_t val;
p++;
p += from_base64(&val, p);
statp->st_ctime = val;
+ /* Optional FileIndex of hard linked file data */
+ if (*p == ' ' || (*p != 0 && *(p+1) == ' ')) {
+ p++;
+ p += from_base64(&val, p);
+ *LinkFI = (uint32_t)val;
+ } else {
+ *LinkFI = 0;
+ }
}
/*
char *link; /* link if file linked */
POOLMEM *sys_fname; /* system filename */
struct stat statp; /* stat packet */
+ uint32_t FileIndex; /* FileIndex of this file */
+ uint32_t LinkFI; /* FileIndex of main hard linked file */
+ struct f_link *linked; /* Set if we are hard linked */
int type; /* FT_ type from above */
int fid; /* file id if opened */
int flags; /* control flags */
/*
- * Structure for keeping track of hard linked files
+ * Structure for keeping track of hard linked files, we
+ * keep an entry for each hardlinked file that we save,
+ * which is the first one found. For all the other files that
+ * are linked to this one, we save only the directory
+ * entry so we can link it.
*/
struct f_link {
struct f_link *next;
- dev_t dev;
- ino_t ino;
+ dev_t dev; /* device */
+ ino_t ino; /* inode with device is unique */
short linkcount;
- char name[1];
+ uint32_t FileIndex; /* Bacula FileIndex of this file */
+ char name[1]; /* The name */
};
return handle_file(ff_pkt, pkt);
}
#endif
-
+ ff_pkt->LinkFI = 0;
/*
* Handle hard linked files
*
struct f_link *lp;
- /* Search link list of hard linked files */
- for (lp = ff_pkt->linklist; lp; lp = lp->next)
- if (lp->ino == ff_pkt->statp.st_ino && lp->dev == ff_pkt->statp.st_dev) {
- ff_pkt->link = lp->name;
- ff_pkt->type = FT_LNKSAVED; /* Handle link, file already saved */
- return handle_file(ff_pkt, pkt);
- }
-
- /* File not previously dumped. Chain it into our list. */
- lp = (struct f_link *)bmalloc(sizeof(struct f_link) + strlen(fname) +1);
- lp->ino = ff_pkt->statp.st_ino;
- lp->dev = ff_pkt->statp.st_dev;
- strcpy(lp->name, fname);
- lp->next = ff_pkt->linklist;
- ff_pkt->linklist = lp;
+ /* Search link list of hard linked files */
+ for (lp = ff_pkt->linklist; lp; lp = lp->next)
+ if (lp->ino == ff_pkt->statp.st_ino && lp->dev == ff_pkt->statp.st_dev) {
+ ff_pkt->link = lp->name;
+ ff_pkt->type = FT_LNKSAVED; /* Handle link, file already saved */
+ ff_pkt->LinkFI = lp->FileIndex;
+ return handle_file(ff_pkt, pkt);
+ }
+
+ /* File not previously dumped. Chain it into our list. */
+ lp = (struct f_link *)bmalloc(sizeof(struct f_link) + strlen(fname) +1);
+ lp->ino = ff_pkt->statp.st_ino;
+ lp->dev = ff_pkt->statp.st_dev;
+ strcpy(lp->name, fname);
+ lp->next = ff_pkt->linklist;
+ ff_pkt->linklist = lp;
+ ff_pkt->linked = lp; /* mark saved link */
+ } else {
+ ff_pkt->linked = NULL;
}
/* This is not a link to a previously dumped file, so dump it. */
if (S_ISREG(ff_pkt->statp.st_mode)) {
- off_t sizeleft;
-
- sizeleft = ff_pkt->statp.st_size;
-
- /* Don't bother opening empty, world readable files. Also do not open
- files when archive is meant for /dev/null. */
- if (ff_pkt->null_output_device || (sizeleft == 0
- && MODE_RALL == (MODE_RALL & ff_pkt->statp.st_mode))) {
- ff_pkt->type = FT_REGE;
- } else {
- ff_pkt->type = FT_REG;
- }
- return handle_file(ff_pkt, pkt);
+ off_t sizeleft;
+
+ sizeleft = ff_pkt->statp.st_size;
+
+ /* Don't bother opening empty, world readable files. Also do not open
+ files when archive is meant for /dev/null. */
+ if (ff_pkt->null_output_device || (sizeleft == 0
+ && MODE_RALL == (MODE_RALL & ff_pkt->statp.st_mode))) {
+ ff_pkt->type = FT_REGE;
+ } else {
+ ff_pkt->type = FT_REG;
+ }
+ rtn_stat = handle_file(ff_pkt, pkt);
+ if (ff_pkt->linked) {
+ ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
+ }
+ return rtn_stat;
+
} else if (S_ISLNK(ff_pkt->statp.st_mode)) {
- int size;
- char *buffer = (char *)alloca(path_max + name_max + 2);
-
- size = readlink(fname, buffer, path_max + name_max + 1);
- if (size < 0) {
- /* Could not follow link */
- ff_pkt->type = FT_NOFOLLOW;
- ff_pkt->ff_errno = errno;
- return handle_file(ff_pkt, pkt);
- }
- buffer[size] = 0;
- ff_pkt->link = buffer;
- ff_pkt->type = FT_LNK; /* got a real link */
- return handle_file(ff_pkt, pkt);
+ int size;
+ char *buffer = (char *)alloca(path_max + name_max + 2);
+
+ size = readlink(fname, buffer, path_max + name_max + 1);
+ if (size < 0) {
+ /* Could not follow link */
+ ff_pkt->type = FT_NOFOLLOW;
+ ff_pkt->ff_errno = errno;
+ rtn_stat = handle_file(ff_pkt, pkt);
+ if (ff_pkt->linked) {
+ ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
+ }
+ return rtn_stat;
+ }
+ buffer[size] = 0;
+ ff_pkt->link = buffer;
+ ff_pkt->type = FT_LNK; /* got a real link */
+ rtn_stat = handle_file(ff_pkt, pkt);
+ if (ff_pkt->linked) {
+ ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
+ }
+ return rtn_stat;
} else if (S_ISDIR(ff_pkt->statp.st_mode)) {
- DIR *directory;
- struct dirent *entry, *result;
- char *link;
- int link_len;
- int len;
- int status;
- dev_t our_device = ff_pkt->statp.st_dev;
-
- if (access(fname, R_OK) == -1 && geteuid() != 0) {
- /* Could not access() directory */
- ff_pkt->type = FT_NOACCESS;
- ff_pkt->ff_errno = errno;
- return handle_file(ff_pkt, pkt);
- }
-
- /* Build a canonical directory name with a trailing slash. */
- len = strlen(fname);
- link_len = len + 200;
- link = (char *)bmalloc(link_len + 2);
- bstrncpy(link, fname, link_len);
- /* Strip all trailing slashes */
- while (len >= 1 && link[len - 1] == '/')
- len--;
- link[len++] = '/'; /* add back one */
- link[len] = 0;
-
- ff_pkt->link = link;
- if (ff_pkt->incremental &&
- (ff_pkt->statp.st_mtime < ff_pkt->save_time &&
- ff_pkt->statp.st_ctime < ff_pkt->save_time)) {
- /* Incremental option, directory entry not changed */
- ff_pkt->type = FT_DIRNOCHG;
- } else {
- ff_pkt->type = FT_DIR;
- }
- handle_file(ff_pkt, pkt); /* handle directory entry */
-
- ff_pkt->link = ff_pkt->fname; /* reset "link" */
-
- /*
- * Do not decend into subdirectories (recurse) if the
- * user has turned it off for this directory.
- */
- if (ff_pkt->flags & FO_NO_RECURSION) {
- free(link);
- /* No recursion into this directory */
- ff_pkt->type = FT_NORECURSE;
- return handle_file(ff_pkt, pkt);
- }
-
- /*
- * See if we are crossing file systems, and
- * avoid doing so if the user only wants to dump one file system.
- */
- if (!top_level && !(ff_pkt->flags & FO_MULTIFS) &&
- parent_device != ff_pkt->statp.st_dev) {
- free(link);
- /* returning here means we do not handle this directory */
- ff_pkt->type = FT_NOFSCHG;
- return handle_file(ff_pkt, pkt);
- }
- /*
- * Now process the files in this directory.
- */
- errno = 0;
- if ((directory = opendir(fname)) == NULL) {
- free(link);
- ff_pkt->type = FT_NOOPEN;
- ff_pkt->ff_errno = errno;
- return handle_file(ff_pkt, pkt);
- }
-
- /*
- * This would possibly run faster if we chdir to the directory
- * before traversing it.
- */
- rtn_stat = 1;
- entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 100);
- for ( ; !job_cancelled(jcr); ) {
- char *p, *q;
- int i;
-
- status = readdir_r(directory, entry, &result);
- Dmsg3(200, "readdir stat=%d result=%x name=%s\n", status, result,
- entry->d_name);
- if (status != 0 || result == NULL) {
- break;
- }
- ASSERT(name_max+1 > sizeof(struct dirent) + (int)NAMELEN(entry));
- p = entry->d_name;
- /* Skip `.', `..', and excluded file names. */
- if (p[0] == '\0' || (p[0] == '.' && (p[1] == '\0' ||
- (p[1] == '.' && p[2] == '\0')))) {
- continue;
- }
-
- if ((int)NAMELEN(entry) + len >= link_len) {
- link_len = len + NAMELEN(entry) + 1;
- link = (char *)brealloc(link, link_len + 1);
- }
- q = link + len;
- for (i=0; i < (int)NAMELEN(entry); i++) {
- *q++ = *p++;
- }
- *q = 0;
- if (!file_is_excluded(ff_pkt, link)) {
- rtn_stat = find_one_file(jcr, ff_pkt, handle_file, pkt, link, our_device, 0);
- }
- }
- closedir(directory);
- free(link);
- free(entry);
-
- if (ff_pkt->atime_preserve) {
- utime(fname, &restore_times);
- }
- return rtn_stat;
+ DIR *directory;
+ struct dirent *entry, *result;
+ char *link;
+ int link_len;
+ int len;
+ int status;
+ dev_t our_device = ff_pkt->statp.st_dev;
+
+ if (access(fname, R_OK) == -1 && geteuid() != 0) {
+ /* Could not access() directory */
+ ff_pkt->type = FT_NOACCESS;
+ ff_pkt->ff_errno = errno;
+ rtn_stat = handle_file(ff_pkt, pkt);
+ if (ff_pkt->linked) {
+ ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
+ }
+ return rtn_stat;
+ }
+
+ /* Build a canonical directory name with a trailing slash. */
+ len = strlen(fname);
+ link_len = len + 200;
+ link = (char *)bmalloc(link_len + 2);
+ bstrncpy(link, fname, link_len);
+ /* Strip all trailing slashes */
+ while (len >= 1 && link[len - 1] == '/')
+ len--;
+ link[len++] = '/'; /* add back one */
+ link[len] = 0;
+
+ ff_pkt->link = link;
+ if (ff_pkt->incremental &&
+ (ff_pkt->statp.st_mtime < ff_pkt->save_time &&
+ ff_pkt->statp.st_ctime < ff_pkt->save_time)) {
+ /* Incremental option, directory entry not changed */
+ ff_pkt->type = FT_DIRNOCHG;
+ } else {
+ ff_pkt->type = FT_DIR;
+ }
+ handle_file(ff_pkt, pkt); /* handle directory entry */
+ if (ff_pkt->linked) {
+ ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
+ }
+
+ ff_pkt->link = ff_pkt->fname; /* reset "link" */
+
+ /*
+ * Do not decend into subdirectories (recurse) if the
+ * user has turned it off for this directory.
+ */
+ if (ff_pkt->flags & FO_NO_RECURSION) {
+ free(link);
+ /* No recursion into this directory */
+ ff_pkt->type = FT_NORECURSE;
+ rtn_stat = handle_file(ff_pkt, pkt);
+ if (ff_pkt->linked) {
+ ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
+ }
+ return rtn_stat;
+ }
+
+ /*
+ * See if we are crossing file systems, and
+ * avoid doing so if the user only wants to dump one file system.
+ */
+ if (!top_level && !(ff_pkt->flags & FO_MULTIFS) &&
+ parent_device != ff_pkt->statp.st_dev) {
+ free(link);
+ /* returning here means we do not handle this directory */
+ ff_pkt->type = FT_NOFSCHG;
+ rtn_stat = handle_file(ff_pkt, pkt);
+ if (ff_pkt->linked) {
+ ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
+ }
+ return rtn_stat;
+ }
+ /*
+ * Now process the files in this directory.
+ */
+ errno = 0;
+ if ((directory = opendir(fname)) == NULL) {
+ free(link);
+ ff_pkt->type = FT_NOOPEN;
+ ff_pkt->ff_errno = errno;
+ rtn_stat = handle_file(ff_pkt, pkt);
+ if (ff_pkt->linked) {
+ ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
+ }
+ return rtn_stat;
+ }
+
+ /*
+ * This would possibly run faster if we chdir to the directory
+ * before traversing it.
+ */
+ rtn_stat = 1;
+ entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 100);
+ for ( ; !job_cancelled(jcr); ) {
+ char *p, *q;
+ int i;
+
+ status = readdir_r(directory, entry, &result);
+ Dmsg3(200, "readdir stat=%d result=%x name=%s\n", status, result,
+ entry->d_name);
+ if (status != 0 || result == NULL) {
+ break;
+ }
+ ASSERT(name_max+1 > sizeof(struct dirent) + (int)NAMELEN(entry));
+ p = entry->d_name;
+ /* Skip `.', `..', and excluded file names. */
+ if (p[0] == '\0' || (p[0] == '.' && (p[1] == '\0' ||
+ (p[1] == '.' && p[2] == '\0')))) {
+ continue;
+ }
+
+ if ((int)NAMELEN(entry) + len >= link_len) {
+ link_len = len + NAMELEN(entry) + 1;
+ link = (char *)brealloc(link, link_len + 1);
+ }
+ q = link + len;
+ for (i=0; i < (int)NAMELEN(entry); i++) {
+ *q++ = *p++;
+ }
+ *q = 0;
+ if (!file_is_excluded(ff_pkt, link)) {
+ rtn_stat = find_one_file(jcr, ff_pkt, handle_file, pkt, link, our_device, 0);
+ if (ff_pkt->linked) {
+ ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
+ }
+ }
+ }
+ closedir(directory);
+ free(link);
+ free(entry);
+
+ if (ff_pkt->atime_preserve) {
+ utime(fname, &restore_times);
+ }
+ return rtn_stat;
} /* end check for directory */
/*
/* The only remaining types are special (character, ...) files */
ff_pkt->type = FT_SPEC;
}
- return handle_file(ff_pkt, pkt);
+ rtn_stat = handle_file(ff_pkt, pkt);
+ if (ff_pkt->linked) {
+ ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
+ }
+ return rtn_stat;
}
int term_find_one(FF_PKT *ff)
*/
/* from attribs.c */
-void encode_stat (char *buf, struct stat *statp);
-void decode_stat (char *buf, struct stat *statp);
-int encode_attribsEx (void *jcr, char *attribsEx, FF_PKT *ff_pkt);
+void encode_stat (char *buf, struct stat *statp, uint32_t LinkFI);
+void decode_stat (char *buf, struct stat *statp, uint32_t *LinkFI);
+int encode_attribsEx (void *jcr, char *attribsEx, FF_PKT *ff_pkt);
int set_attributes(void *jcr, char *fname, char *ofile, char *lname,
- int type, int stream, struct stat *statp,
- char *attribsEx, int *ofd);
+ int type, int stream, struct stat *statp,
+ char *attribsEx, int *ofd);
/* from create_file.c */
int create_file(void *jcr, char *fname, char *ofile, char *lname,
- int type, int stream, struct stat *statp,
- char *attribsEx, int *ofd, int replace);
+ int type, int stream, struct stat *statp,
+ char *attribsEx, int *ofd, int replace);
/* From find.c */
FF_PKT *init_find_files();
int file_is_excluded(FF_PKT *ff, char *file);
int file_is_included(FF_PKT *ff, char *file);
struct s_included_file *get_next_included_file(FF_PKT *ff,
- struct s_included_file *inc);
+ struct s_included_file *inc);
/* From find_one.c */
int find_one_file(JCR *jcr, FF_PKT *ff, int handle_file(FF_PKT *ff_pkt, void *hpkt),
- void *pkt, char *p, dev_t parent_device, int top_level);
+ void *pkt, char *p, dev_t parent_device, int top_level);
int term_find_one(FF_PKT *ff);
/* from makepath.c */
int make_path(void *jcr, const char *argpath, int mode,
- int parent_mode, uid_t owner, gid_t group,
- int preserve_existing, char *verbose_fmt_string);
+ int parent_mode, uid_t owner, gid_t group,
+ int preserve_existing, char *verbose_fmt_string);
btraceback.gdb
startit
stopit
+sha1sum
+md5sum
cd $(topdir) \
&& CONFIG_FILES=$(thisdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
-rwlock_test: rwlock.o
+rwlock_test:
rm -f rwlock.o
$(CXX) -DTEST_RWLOCK $(DEFS) $(DEBUG) -c $(CPPFLAGS) -I$(srcdir) -I$(basedir) $(DINCLUDE) $(CFLAGS) rwlock.c
$(CXX) $(LDFLAGS) -L. -o $@ rwlock.o $(LIBS) $(DLIB) -lbac -lm
rm -f rwlock.o
$(CXX) $(DEFS) $(DEBUG) -c $(CPPFLAGS) -I$(srcdir) -I$(basedir) $(DINCLUDE) $(CFLAGS) rwlock.c
+
+md5sum:
+ rm -f md5.o
+ $(CXX) -DMD5_SUM $(DEFS) $(DEBUG) -c $(CPPFLAGS) -I$(srcdir) -I$(basedir) $(DINCLUDE) $(CFLAGS) md5.c
+ $(CXX) $(LDFLAGS) -L. -o $@ md5.o $(LIBS) $(DLIB) -lbac -lm
+ rm -f md5.o
+ $(CXX) $(DEFS) $(DEBUG) -c $(CPPFLAGS) -I$(srcdir) -I$(basedir) $(DINCLUDE) $(CFLAGS) md5.c
+
+
+sha1sum:
+ rm -f sha1.o
+ $(CXX) -DSHA1_SUM $(DEFS) $(DEBUG) -c $(CPPFLAGS) -I$(srcdir) -I$(basedir) $(DINCLUDE) $(CFLAGS) sha1.c
+ $(CXX) $(LDFLAGS) -L. -o $@ sha1.o $(LIBS) $(DLIB) -lbac -lm
+ rm -f sha1.o
+ $(CXX) $(DEFS) $(DEBUG) -c $(CPPFLAGS) -I$(srcdir) -I$(basedir) $(DINCLUDE) $(CFLAGS) md5.c
install:
clean:
$(RMF) *.a core a.out *.o *.bak *.tex *.pdf *~ *.intpro *.extpro 1 2 3
- $(RMF) rwlock_test md5 sha1
+ $(RMF) rwlock_test md5sum sha1sum
realclean: clean
$(RMF) tags
* Version $Id$
*/
/*
- Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
+ Copyright (C) 2002-2003 Kern Sibbald and John Walker
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
* Returns -2 on error
*/
int32_t
-bnet_recv_pkt(BSOCK *bsock, BPKT *pkt)
+bnet_recv_pkt(BSOCK *bsock, BPKT *pkt, int *version)
{
+ unser_declare;
+ short lversion;
+ int type;
+
+ unser_begin(bsock->msg, 0);
+ unser_uint16(lversion);
+ *version = (int)lversion;
+
+
+ for ( ; pkt->type != BP_EOF; pkt++) {
+ if (pkt->id) {
+ ser_int8(BP_ID);
+ ser_string((char *)pkt->id);
+ }
+ ser_int8(pkt->type);
+ switch (pkt->type) {
+ case BP_CHAR:
+ ser_int8(*(int8_t *)pkt->value);
+ break;
+ case BP_INT32:
+ ser_int32(*(int32_t *)pkt->value);
+ break;
+ case BP_UINT32:
+ break;
+ ser_unit32(*(uint32_t *)pkt->value);
+ break;
+ case BP_INT64:
+ ser_int64(*(int64_t *)pkt->value);
+ break;
+ case BP_BTIME:
+ case BP_UTIME:
+ case BP_UINT64:
+ ser_uint64(*(uint64_t *)pkt->value);
+ break;
+ case BP_POOL:
+ case BP_STRING:
+ case BP_NAME:
+ ser_string((char *)pkt->value);
+ break;
+ case BP_BYTES:
+ ser_uint32(*(uint32_t *)pkt->len);
+ ser_bytes((char *)pkt->value, pkt->len);
+ break;
+ default:
+ Emsg1(M_ABORT, 0, _("Unknown BPKT type: %d\n"), pkt->type);
+ }
+ }
+ unser_end(bsock->msg, 0);
+
}
/*
* 1 on success
*/
int
-bnet_send_pkt(BSOCK *bsock, BPKT *pkt)
+bnet_send_pkt(BSOCK *bsock, BPKT *pkt, int version)
{
ser_declare;
ser_begin(bsock->msg, 0);
+ ser_uint16(version);
- while (pkt->type != BP_EOF) {
+ for ( ; pkt->type != BP_EOF; pkt++) {
+ if (pkt->id) {
+ ser_int8(BP_ID);
+ ser_string((char *)pkt->id);
+ }
ser_int8(pkt->type);
switch (pkt->type) {
case BP_CHAR:
- ser_int8(*((int8_t)pkt->value)));
+ ser_int8(*(int8_t *)pkt->value);
break;
case BP_INT32:
- ser_int32(*(int32_t)pkt->value)));
+ ser_int32(*(int32_t *)pkt->value);
break;
case BP_UINT32:
break;
+ ser_unit32(*(uint32_t *)pkt->value);
+ break;
case BP_INT64:
+ ser_int64(*(int64_t *)pkt->value);
break;
- case BP_STRING:
+ case BP_BTIME:
+ case BP_UTIME:
+ case BP_UINT64:
+ ser_uint64(*(uint64_t *)pkt->value);
break;
+ case BP_POOL:
+ case BP_STRING:
case BP_NAME:
+ ser_string((char *)pkt->value);
break;
case BP_BYTES:
- break;
- case BP_FLOAT32:
- break;
- case BP_FLOAT64:
+ ser_uint32(*(uint32_t *)pkt->len);
+ ser_bytes((char *)pkt->value, pkt->len);
break;
default:
Emsg1(M_ABORT, 0, _("Unknown BPKT type: %d\n"), pkt->type);
}
-
+ }
+ ser_end(bsock->msg, 0);
}
#ifndef HAVE_CYGWIN
int i;
int cpid;
+ mode_t oldmask;
/*
* Become a daemon.
*/
chdir("/");
#endif
- /* clear any inherited umask */
- umask(0);
+ /*
+ * Avoid creating files 666 but don't override any
+ * more restrictive mask set by the user.
+ */
+ oldmask = umask(022);
+ oldmask |= 022;
+ umask(oldmask);
Dmsg0(200, "Exit daemon_start\n");
#endif /* HAVE_CYGWIN */
#include "bacula.h" /* pull in global headers */
#include "stored.h" /* pull in Storage Deamon headers */
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
/*********************************************************************
* Acquire device for reading. We permit (for the moment)
* Acquire device for writing. We permit multiple writers.
* If this is the first one, we read the label.
*
- * Returns: 0 if failed for any reason
- * 1 if successful
+ * Returns: NULL if failed for any reason
+ * dev if successful (may change if new dev opened)
*/
-int acquire_device_for_append(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
+DEVICE * acquire_device_for_append(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
{
int release = 0;
int do_mount = 0;
- int stat = 0;
+ DEVICE *rtn_dev = NULL;
lock_device(dev);
block_device(dev, BST_DOING_ACQUIRE);
unlock_device(dev);
Dmsg1(190, "acquire_append device is %s\n", dev_is_tape(dev)?"tape":"disk");
-
+
if (dev->state & ST_APPEND) {
/*
!(dir_find_next_appendable_volume(jcr) &&
strcmp(dev->VolHdr.VolName, jcr->VolumeName) == 0)) { /* wrong tape mounted */
if (dev->num_writers != 0) {
- /*
- * ***FIXME*** add multiple writers here if permitted
- * find end of dev chain
- * dev->next = init_dev(NULL, dev->device);
- * ...
- */
- Jmsg(jcr, M_FATAL, 0, _("Device %s is busy writing on another Volume.\n"), dev_name(dev));
- goto get_out;
+ DEVICE *d = ((DEVRES *)dev->device)->dev;
+ uint32_t open_vols = 0;
+ for ( ; d; d=d->next) {
+ open_vols++;
+ }
+ if (dev->state & ST_FILE && dev->max_open_vols > open_vols) {
+ P(mutex); /* lock all devices */
+ d = init_dev(NULL, (DEVRES *)dev->device); /* init new device */
+ d->prev = dev; /* chain in new device */
+ d->next = dev->next;
+ dev->next = d;
+ /* Release old device */
+ P(dev->mutex);
+ unblock_device(dev);
+ V(dev->mutex);
+ /* Make new device current device and lock it */
+ dev = d;
+ lock_device(dev);
+ block_device(dev, BST_DOING_ACQUIRE);
+ unlock_device(dev);
+ V(mutex);
+ } else {
+ Jmsg(jcr, M_FATAL, 0, _("Device %s is busy writing on another Volume.\n"), dev_name(dev));
+ goto get_out;
+ }
}
/* Wrong tape mounted, release it, then fall through to get correct one */
release = 1;
jcr->NumVolumes = 1;
}
attach_jcr_to_device(dev, jcr); /* attach jcr to device */
- stat = 1; /* good return */
+ rtn_dev = dev; /* return device */
get_out:
P(dev->mutex);
unblock_device(dev);
V(dev->mutex);
- return stat;
+ return rtn_dev;
}
/*
dev_name(dev), NPRT(jcr->VolumeName));
}
detach_jcr_from_device(dev, jcr);
- unlock_device(dev);
+ if (dev->prev && !(dev->state & ST_READ) && dev->num_writers == 0) {
+ P(mutex);
+ unlock_device(dev);
+ dev->prev->next = dev->next; /* dechain */
+ term_dev(dev);
+ V(mutex);
+ } else {
+ unlock_device(dev);
+ }
return 1;
}
* subroutine.
*/
Dmsg0(100, "just before acquire_device\n");
- if (!acquire_device_for_append(jcr, dev, block)) {
+ if (!(dev=acquire_device_for_append(jcr, dev, block))) {
set_jcr_job_status(jcr, JS_ErrorTerminated);
free_block(block);
return 0;
exit(1);
}
unlock_device(out_dev);
- if (!acquire_device_for_append(out_jcr, out_dev, out_block)) {
+ if (!(out_dev=acquire_device_for_append(out_jcr, out_dev, out_block))) {
free_block(out_block);
free_jcr(in_jcr);
exit(1);
if (file_is_included(ff, fname) && !file_is_excluded(ff, fname)) {
+ uint32_t LinkFI;
- decode_stat(ap, &statp);
+ decode_stat(ap, &statp, &LinkFI);
/*
* Prepend the where directory so that the
* files are put where the user wants.
/* File Attributes stream */
if (rec->Stream == STREAM_UNIX_ATTRIBUTES || rec->Stream == STREAM_WIN32_ATTRIBUTES) {
char *ap, *fp;
+ uint32_t LinkFI;
+
sscanf(rec->data, "%ld %d", &record_file_index, &type);
if (record_file_index != rec->FileIndex) {
Emsg2(M_ERROR_TERM, 0, "Record header file index %ld not equal record index %ld\n",
}
*fp = *ap++; /* terminate filename & point to attribs */
- decode_stat(ap, &statp);
+ decode_stat(ap, &statp, &LinkFI);
/* Skip to link name */
while (*ap++ != 0)
;
}
strcat(lname, lp); /* "save" link name */
if (verbose > 1) {
- decode_stat(ap, &statp);
+ uint32_t LinkFI;
+ decode_stat(ap, &statp, &LinkFI);
print_ls_output(fname, lname, type, &statp);
}
mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
* subroutine.
*/
Dmsg0(100, "just before acquire_device\n");
- if (!acquire_device_for_append(jcr, dev, block)) {
+ if (!(dev=acquire_device_for_append(jcr, dev, block))) {
set_jcr_job_status(jcr, JS_ErrorTerminated);
free_block(block);
return;
dev->volume_capacity = device->volume_capacity;
dev->max_rewind_wait = device->max_rewind_wait;
dev->max_open_wait = device->max_open_wait;
+ dev->max_open_vols = device->max_open_vols;
dev->device = device;
if (tape) {
} else {
Emsg0(M_ABORT, 0, _("Illegal mode given to open_dev.\n"));
}
- if ((dev->fd = open(archive_name, dev->mode, MODE_RW)) < 0) {
+ /* If creating file, give 0640 permissions */
+ if ((dev->fd = open(archive_name, dev->mode, 0640)) < 0) {
dev->dev_errno = errno;
Mmsg2(&dev->errmsg, _("Could not open: %s, ERR=%s\n"), archive_name, strerror(dev->dev_errno));
Emsg0(M_FATAL, 0, dev->errmsg);
/* #define NEW_LOCK 1 */
-#define new_lock_device(dev) _new_lock_device(__FILE__, __LINE__, (dev))
+#define new_lock_device(dev) _new_lock_device(__FILE__, __LINE__, (dev))
#define new_lock_device_state(dev,state) _new_lock_device(__FILE__, __LINE__, (dev), (state))
-#define new_unlock_device(dev) _new_unlock_device(__FILE__, __LINE__, (dev))
+#define new_unlock_device(dev) _new_unlock_device(__FILE__, __LINE__, (dev))
#define lock_device(d) _lock_device(__FILE__, __LINE__, (d))
#define unlock_device(d) _unlock_device(__FILE__, __LINE__, (d))
#define READ_WRITE 0
#define READ_ONLY 1
#define OPEN_READ_WRITE 0
-#define OPEN_READ_ONLY 1
+#define OPEN_READ_ONLY 1
#define OPEN_WRITE_ONLY 2
/* Generic status bits returned from status_dev() */
-#define MT_TAPE (1<<0) /* is tape device */
-#define MT_EOF (1<<1) /* just read EOF */
-#define MT_BOT (1<<2) /* at beginning of tape */
-#define MT_EOT (1<<3) /* end of tape reached */
-#define MT_SM (1<<4) /* DDS setmark */
-#define MT_EOD (1<<5) /* DDS at end of data */
-#define MT_WR_PROT (1<<6) /* tape write protected */
-#define MT_ONLINE (1<<7) /* tape online */
-#define MT_DR_OPEN (1<<8) /* tape door open */
-#define MT_IM_REP_EN (1<<9) /* immediate report enabled */
+#define MT_TAPE (1<<0) /* is tape device */
+#define MT_EOF (1<<1) /* just read EOF */
+#define MT_BOT (1<<2) /* at beginning of tape */
+#define MT_EOT (1<<3) /* end of tape reached */
+#define MT_SM (1<<4) /* DDS setmark */
+#define MT_EOD (1<<5) /* DDS at end of data */
+#define MT_WR_PROT (1<<6) /* tape write protected */
+#define MT_ONLINE (1<<7) /* tape online */
+#define MT_DR_OPEN (1<<8) /* tape door open */
+#define MT_IM_REP_EN (1<<9) /* immediate report enabled */
/* Test capabilities */
#define dev_cap(dev, cap) ((dev)->capabilities & (cap))
/* Bits for device capabilities */
-#define CAP_EOF 0x0001 /* has MTWEOF */
-#define CAP_BSR 0x0002 /* has MTBSR */
-#define CAP_BSF 0x0004 /* has MTBSF */
-#define CAP_FSR 0x0008 /* has MTFSR */
-#define CAP_FSF 0x0010 /* has MTFSF */
-#define CAP_EOM 0x0020 /* has MTEOM */
-#define CAP_REM 0x0040 /* is removable media */
-#define CAP_RACCESS 0x0080 /* is random access device */
-#define CAP_AUTOMOUNT 0x0100 /* Read device at start to see what is there */
-#define CAP_LABEL 0x0200 /* Label blank tapes */
-#define CAP_ANONVOLS 0x0400 /* Mount without knowing volume name */
-#define CAP_ALWAYSOPEN 0x0800 /* always keep device open */
+#define CAP_EOF 0x0001 /* has MTWEOF */
+#define CAP_BSR 0x0002 /* has MTBSR */
+#define CAP_BSF 0x0004 /* has MTBSF */
+#define CAP_FSR 0x0008 /* has MTFSR */
+#define CAP_FSF 0x0010 /* has MTFSF */
+#define CAP_EOM 0x0020 /* has MTEOM */
+#define CAP_REM 0x0040 /* is removable media */
+#define CAP_RACCESS 0x0080 /* is random access device */
+#define CAP_AUTOMOUNT 0x0100 /* Read device at start to see what is there */
+#define CAP_LABEL 0x0200 /* Label blank tapes */
+#define CAP_ANONVOLS 0x0400 /* Mount without knowing volume name */
+#define CAP_ALWAYSOPEN 0x0800 /* always keep device open */
#define CAP_AUTOCHANGER 0x1000 /* AutoChanger */
#define CAP_OFFLINEUNMOUNT 0x2000 /* Offline before unmount */
-#define CAP_STREAM 0x4000 /* Stream device */
+#define CAP_STREAM 0x4000 /* Stream device */
/* Test state */
#define dev_state(dev, state) ((dev)->state & (state))
/* Device state bits */
-#define ST_OPENED 0x0001 /* set when device opened */
-#define ST_TAPE 0x0002 /* is a tape device */
-#define ST_FILE 0x0004 /* is a file device */
-#define ST_FIFO 0x0008 /* is a fifo device */
-#define ST_PROG 0x0010 /* is a program device */
-#define ST_LABEL 0x0020 /* label found */
+#define ST_OPENED 0x0001 /* set when device opened */
+#define ST_TAPE 0x0002 /* is a tape device */
+#define ST_FILE 0x0004 /* is a file device */
+#define ST_FIFO 0x0008 /* is a fifo device */
+#define ST_PROG 0x0010 /* is a program device */
+#define ST_LABEL 0x0020 /* label found */
#define ST_MALLOC 0x0040 /* dev packet malloc'ed in init_dev() */
-#define ST_APPEND 0x0080 /* ready for Bacula append */
-#define ST_READ 0x0100 /* ready for Bacula read */
-#define ST_EOT 0x0200 /* at end of tape */
-#define ST_WEOT 0x0400 /* Got EOT on write */
-#define ST_EOF 0x0800 /* Read EOF i.e. zero bytes */
-#define ST_NEXTVOL 0x1000 /* Start writing on next volume */
-#define ST_SHORT 0x2000 /* Short block read */
+#define ST_APPEND 0x0080 /* ready for Bacula append */
+#define ST_READ 0x0100 /* ready for Bacula read */
+#define ST_EOT 0x0200 /* at end of tape */
+#define ST_WEOT 0x0400 /* Got EOT on write */
+#define ST_EOF 0x0800 /* Read EOF i.e. zero bytes */
+#define ST_NEXTVOL 0x1000 /* Start writing on next volume */
+#define ST_SHORT 0x2000 /* Short block read */
/* dev_blocked states (mutually exclusive) */
#define BST_NOT_BLOCKED 0 /* not blocked */
-#define BST_UNMOUNTED 1 /* User unmounted device */
+#define BST_UNMOUNTED 1 /* User unmounted device */
#define BST_WAITING_FOR_SYSOP 2 /* Waiting for operator to mount tape */
#define BST_DOING_ACQUIRE 3 /* Opening/validating/moving tape */
#define BST_WRITING_LABEL 4 /* Labeling a tape */
/* Volume Catalog Information structure definition */
typedef struct s_volume_catalog_info {
/* Media info for the current Volume */
- uint32_t VolCatJobs; /* number of jobs on this Volume */
- uint32_t VolCatFiles; /* Number of files */
- uint32_t VolCatBlocks; /* Number of blocks */
- uint64_t VolCatBytes; /* Number of bytes written */
- uint32_t VolCatMounts; /* Number of mounts this volume */
- uint32_t VolCatErrors; /* Number of errors this volume */
- uint32_t VolCatWrites; /* Number of writes this volume */
- uint32_t VolCatReads; /* Number of reads this volume */
- uint32_t VolCatRecycles; /* Number of recycles this volume */
- int32_t Slot; /* Slot in changer */
- uint32_t VolCatMaxJobs; /* Maximum Jobs to write to volume */
- uint32_t VolCatMaxFiles; /* Maximum files to write to volume */
- uint64_t VolCatMaxBytes; /* Max bytes to write to volume */
+ uint32_t VolCatJobs; /* number of jobs on this Volume */
+ uint32_t VolCatFiles; /* Number of files */
+ uint32_t VolCatBlocks; /* Number of blocks */
+ uint64_t VolCatBytes; /* Number of bytes written */
+ uint32_t VolCatMounts; /* Number of mounts this volume */
+ uint32_t VolCatErrors; /* Number of errors this volume */
+ uint32_t VolCatWrites; /* Number of writes this volume */
+ uint32_t VolCatReads; /* Number of reads this volume */
+ uint32_t VolCatRecycles; /* Number of recycles this volume */
+ int32_t Slot; /* Slot in changer */
+ uint32_t VolCatMaxJobs; /* Maximum Jobs to write to volume */
+ uint32_t VolCatMaxFiles; /* Maximum files to write to volume */
+ uint64_t VolCatMaxBytes; /* Max bytes to write to volume */
uint64_t VolCatCapacityBytes; /* capacity estimate */
- char VolCatStatus[20]; /* Volume status */
+ char VolCatStatus[20]; /* Volume status */
char VolCatName[MAX_NAME_LENGTH]; /* Desired volume to mount */
} VOLUME_CAT_INFO;
typedef struct s_steal_lock {
- pthread_t no_wait_id; /* id of no wait thread */
- int dev_blocked; /* state */
+ pthread_t no_wait_id; /* id of no wait thread */
+ int dev_blocked; /* state */
} bsteal_lock_t;
/* Device structure definition */
typedef struct s_device {
- struct s_device *next; /* pointer to next open device */
- void *attached_jcrs; /* attached JCR list */
- pthread_mutex_t mutex; /* access control */
- pthread_cond_t wait; /* thread wait variable */
+ struct s_device *next; /* pointer to next open device */
+ struct s_device *prev; /* pointer to prev open device */
+ void *attached_jcrs; /* attached JCR list */
+ pthread_mutex_t mutex; /* access control */
+ pthread_cond_t wait; /* thread wait variable */
pthread_cond_t wait_next_vol; /* wait for tape to be mounted */
- pthread_t no_wait_id; /* this thread must not wait */
- int dev_blocked; /* set if we must wait (i.e. change tape) */
- int num_waiting; /* number of threads waiting */
- int num_writers; /* number of writing threads */
- int use_count; /* usage count on this device */
- int fd; /* file descriptor */
- int capabilities; /* capabilities mask */
- int state; /* state mask */
- int dev_errno; /* Our own errno */
- int mode; /* read/write modes */
- char *dev_name; /* device name */
- char *errmsg; /* nicely edited error message */
- uint32_t block_num; /* current block number base 0 */
- uint32_t file; /* current file number base 0 */
- uint64_t file_addr; /* Current file read/write address */
- uint32_t EndBlock; /* last block written */
- uint32_t EndFile; /* last file written */
- uint32_t min_block_size; /* min block size */
- uint32_t max_block_size; /* max block size */
- uint32_t max_volume_jobs; /* max jobs to put on one volume */
- uint64_t max_volume_files; /* max files to put on one volume */
- uint64_t max_volume_size; /* max bytes to put on one volume */
- uint64_t max_file_size; /* max file size to put in one file on volume */
- uint64_t volume_capacity; /* advisory capacity */
- uint32_t max_rewind_wait; /* max secs to allow for rewind */
- uint32_t max_open_wait; /* max secs to allow for open */
- void *device; /* pointer to Device Resource */
- btimer_id tid; /* timer id */
-
- VOLUME_CAT_INFO VolCatInfo; /* Volume Catalog Information */
- VOLUME_LABEL VolHdr; /* Actual volume label */
+ pthread_t no_wait_id; /* this thread must not wait */
+ int dev_blocked; /* set if we must wait (i.e. change tape) */
+ int num_waiting; /* number of threads waiting */
+ int num_writers; /* number of writing threads */
+ int use_count; /* usage count on this device */
+ int fd; /* file descriptor */
+ int capabilities; /* capabilities mask */
+ int state; /* state mask */
+ int dev_errno; /* Our own errno */
+ int mode; /* read/write modes */
+ char *dev_name; /* device name */
+ char *errmsg; /* nicely edited error message */
+ uint32_t block_num; /* current block number base 0 */
+ uint32_t file; /* current file number base 0 */
+ uint64_t file_addr; /* Current file read/write address */
+ uint32_t EndBlock; /* last block written */
+ uint32_t EndFile; /* last file written */
+ uint32_t min_block_size; /* min block size */
+ uint32_t max_block_size; /* max block size */
+ uint32_t max_volume_jobs; /* max jobs to put on one volume */
+ uint64_t max_volume_files; /* max files to put on one volume */
+ uint64_t max_volume_size; /* max bytes to put on one volume */
+ uint64_t max_file_size; /* max file size to put in one file on volume */
+ uint64_t volume_capacity; /* advisory capacity */
+ uint32_t max_rewind_wait; /* max secs to allow for rewind */
+ uint32_t max_open_wait; /* max secs to allow for open */
+ uint32_t max_open_vols; /* max simultaneous open volumes */
+ void *device; /* pointer to Device Resource */
+ btimer_id tid; /* timer id */
+
+ VOLUME_CAT_INFO VolCatInfo; /* Volume Catalog Information */
+ VOLUME_LABEL VolHdr; /* Actual volume label */
} DEVICE;
* dependent. Arrgggg!
*/
#ifndef MTEOM
-#ifdef MTSEOD
+#ifdef MTSEOD
#define MTEOM MTSEOD
#endif
#ifdef MTEOD
uint32_t new_VolSessionId();
/* From acquire.c */
-int acquire_device_for_append(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+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);
{"changercommand", store_strname,ITEM(res_dev.changer_command), 0, 0, 0},
{"maximumchangerwait", store_pint, ITEM(res_dev.max_changer_wait), 0, ITEM_DEFAULT, 2 * 60},
{"maximumopenwait", store_pint, ITEM(res_dev.max_open_wait), 0, ITEM_DEFAULT, 5 * 60},
+ {"maximumopenvolumes", store_pint, ITEM(res_dev.max_open_vols), 0, ITEM_DEFAULT, 1},
{"offlineonunmount", store_yesno, ITEM(res_dev.cap_bits), CAP_OFFLINEUNMOUNT, ITEM_DEFAULT, 0},
{"maximumrewindwait", store_pint, ITEM(res_dev.max_rewind_wait), 0, ITEM_DEFAULT, 5 * 60},
{"minimumblocksize", store_pint, ITEM(res_dev.min_block_size), 0, 0, 0},
uint32_t max_changer_wait; /* Changer timeout */
uint32_t max_rewind_wait; /* maximum secs to wait for rewind */
uint32_t max_open_wait; /* maximum secs to wait for open */
+ uint32_t max_open_vols; /* maximum simultaneous open volumes */
uint32_t min_block_size; /* min block size */
uint32_t max_block_size; /* max block size */
uint32_t max_volume_jobs; /* max jobs to put on one volume */
/* */
#define VERSION "1.30"
#define VSTRING "1"
-#define DATE "26 January 2003"
-#define LSMDATE "26Jan03"
+#define DATE "1 February 2003"
+#define LSMDATE "01Feb03"
/* Debug flags */
#define DEBUG 1