/* * ----- check_samfs_fs - verify path is in samfs file system. */ static void check_samfs_fs() { char *check_name = "."; struct sam_stat sb; if (sam_stat(check_name, &sb, sizeof (sb)) < 0 || !SS_ISSAMFS(sb.attr)) { BUMP_STAT(errors); BUMP_STAT(errors_dir); error(1, 0, catgets(catfd, SET, 259, "%s: Not a SAM-FS file."), check_name); } }
void cs_restore( boolean strip_slashes, /* should leading slash be stripped? */ int fcount, /* number of filename patterns in flist */ char **flist) /* filename patterns to select for restore */ { int file_data = 0; int file_data_read; csd_fhdr_t file_hdr; char name[MAXPATHLEN + 1]; /* name to compare for selective rstr */ char *name_compare = &name[0]; char slink[MAXPATHLEN + 1]; char *save_path = (char *)NULL; /* Prev path opened for SAM_fd */ char *last_path = NULL; /* Last compared path */ char *slash_loc; /* Last loc of a '/' in filename */ char unused; /* A safe value for slash_loc */ int namelen; int skipping; struct sam_perm_inode perm_inode; struct sam_stat sb; struct sam_vsn_section *vsnp; void *data; int n_acls; aclent_t *aclp; /* * Read the dump file */ while (csd_read_header(&file_hdr) > 0) { namelen = file_hdr.namelen; csd_read(name, namelen, &perm_inode); data = NULL; vsnp = NULL; aclp = NULL; n_acls = 0; if (file_hdr.flags & CSD_FH_DATA) { BUMP_STAT(data_files); file_data_read = 0; file_data++; } else { file_data_read = file_data = 0; } if (S_ISREQ(perm_inode.di.mode)) { SamMalloc(data, perm_inode.di.psize.rmfile); } csd_read_next(&perm_inode, &vsnp, slink, data, &n_acls, &aclp); if (dont_process_this_entry) { /* if problem reading inode */ BUMP_STAT(errors); goto skip_file; } /* Skip privileged files except root */ if (SAM_PRIVILEGE_INO(perm_inode.di.version, perm_inode.di.id.ino)) { if (perm_inode.di.id.ino != SAM_ROOT_INO) { goto skip_file; } } /* select subset of file names to restore. */ name_compare = &name[0]; if (fcount != 0) { /* Search the file name list */ int i; for (i = 0; i < fcount; i++) { /* * If stripping first slash to make relative * path and dump file name begins with '/' and * select file name does not being with '/', * then move name compare ahead past first '/' */ if (strip_slashes && ('/' == *name) && ('/' != *flist[i])) { name_compare = &name[1]; } /* * filecmp returns: * 0 for no match * 1 exact match, * 2 name prefix of flist[i], * 3 flist[i] prefix of name. */ if (filecmp(name_compare, flist[i]) != 0) { break; } } if (i >= fcount) { if (S_ISSEGI(&perm_inode.di)) { skipping = 1; goto skip_seg_file; } goto skip_file; } } /* * If stripping first slash to make relative path and * dump file name begins with '/' * then move location ahead past first '/' */ if (strip_slashes && ('/' == *name)) { name_compare = &name[1]; } /* * Make sure that we don't restore into a non-SAM-FS * filesystem. * If relative path to be restored, check current directory for * being in a SAM-FS filesystem. If absolute path to be * restored, search backwards through path for a viable * SAM-FS path by calling sam_stat() and checking the * directory attributes. */ if ((strip_slashes && ('/' == *name)) || ('/' != *name)) { char *check_name = "."; char *next_name; if ((save_path != (char *)NULL) && (strcmp(check_name, save_path) != 0)) { check_samfs_fs(); free(save_path); (void) close(SAM_fd); save_path = strdup(check_name); SAM_fd = open_samfs(save_path); } else if (save_path == (char *)NULL) { check_samfs_fs(); save_path = strdup(check_name); SAM_fd = open_samfs(save_path); } /* * Check if last path matches this path. Otherwise, * If subpath does not yet exist, make directories * as needed. */ if (last_path != NULL) { char *name_cmp; int path_len; path_len = strlen(last_path); /* Copy filename */ name_cmp = strdup(name_compare); if ((slash_loc = strrchr(name_cmp, '/')) != NULL) { *slash_loc = '\0'; if ((path_len == strlen(name_cmp)) && (last_path[path_len-1] == name_cmp[path_len-1]) && (bcmp(last_path, name_cmp, path_len) == 0)) { free(name_cmp); goto restore_file; } } free(name_cmp); } if (last_path != NULL) { free(last_path); last_path = NULL; } /* Save path for check above */ last_path = strdup(name_compare); if ((slash_loc = strrchr(last_path, '/')) != NULL) { *slash_loc = '\0'; } else { free(last_path); last_path = NULL; } /* Copy filename */ next_name = check_name = strdup(name_compare); while ((slash_loc = strchr(next_name, '/')) != (char *)NULL) { *slash_loc = '\0'; if (mkdir(check_name, S_IRWXU | S_IRWXG | S_IRWXO) < 0) { if (EEXIST == errno) { *slash_loc = '/'; next_name = slash_loc + 1; continue; } BUMP_STAT(errors); error(1, errno, catgets(catfd, SET, 222, "%s: Cannot mkdir()"), check_name); break; } *slash_loc = '/'; next_name = slash_loc + 1; } free(check_name); } else { /* * Search through absolute path from end for * SAM-FS file system */ /* Copy filename */ char *check_name = strdup(name_compare); slash_loc = &unused; do { *slash_loc = '\0'; /* * Make sure we don't restore into a * non-SAM-FS filesystem * by calling sam_stat() and checking the * directory attributes. */ if (sam_stat(check_name, &sb, sizeof (sb)) == 0 && SS_ISSAMFS(sb.attr)) { if ((save_path != (char *)NULL) && (strcmp(check_name, save_path) != 0)) { free(save_path); (void) close(SAM_fd); save_path = strdup(check_name); SAM_fd = open_samfs(save_path); } else if (save_path == (char *)NULL) { save_path = strdup(check_name); SAM_fd = open_samfs(save_path); } break; } slash_loc = strrchr(check_name, '/'); } while (slash_loc != (char *)NULL); /* * If no SAM-FS file system found in path, issue error */ if ((char *)NULL == slash_loc) { BUMP_STAT(errors); BUMP_STAT(errors_dir); error(1, 0, catgets(catfd, SET, 259, "%s: Not a SAM-FS file."), name_compare); } free(check_name); } restore_file: if (verbose) { sam_ls(name_compare, &perm_inode, NULL); } /* If not already offline */ if (!perm_inode.di.status.b.offline) { /* If archived */ if (perm_inode.di.arch_status) { print_reslog(log_st, name_compare, &perm_inode, "online"); } } else if (perm_inode.di.status.b.pextents) { print_reslog(log_st, name_compare, &perm_inode, "partial"); } /* * if segment index, we have to skip the data segment inodes * before we can get to the dumped data. */ if (!(S_ISSEGI(&perm_inode.di) && file_data)) { sam_restore_a_file(name_compare, &perm_inode, vsnp, slink, data, n_acls, aclp, file_data, &file_data_read); } skipping = 0; skip_seg_file: if (S_ISSEGI(&perm_inode.di)) { struct sam_perm_inode seg_inode; int i; offset_t seg_size; int no_seg; sam_id_t seg_parent_id = perm_inode.di.id; /* * If we are restoring the data, don't restore the * data segment inodes. This means we will lose the * archive copies, if any. */ if (file_data) skipping++; /* * Read each segment inode. If archive copies * overflowed, * read vsn sections directly after each segment inode. */ seg_size = (offset_t)perm_inode.di.rm.info.dk.seg_size * SAM_MIN_SEGMENT_SIZE; no_seg = (perm_inode.di.rm.size + seg_size - 1) / seg_size; for (i = 0; i < no_seg; i++) { struct sam_vsn_section *seg_vsnp; readcheck(&seg_inode, sizeof (seg_inode), 5002); if (swapped) { if (sam_byte_swap( sam_perm_inode_swap_descriptor, &seg_inode, sizeof (seg_inode))) { error(0, 0, catgets(catfd, SET, 13531, "%s: segment inode byte " "swap error - skipping."), name); dont_process_this_entry = 1; } } if (!(SAM_CHECK_INODE_VERSION( seg_inode.di.version))) { if (debugging) { fprintf(stderr, "cs_restore: seg %d " "inode version %d\n", i, seg_inode.di.version); } error(0, 0, catgets(catfd, SET, 739, "%s: inode version incorrect - " "skipping"), name); dont_process_this_entry = 1; } if (skipping || dont_process_this_entry) { continue; } seg_inode.di.parent_id = seg_parent_id; seg_vsnp = NULL; csd_read_mve(&seg_inode, &seg_vsnp); if (verbose) { sam_ls(name_compare, &seg_inode, NULL); } sam_restore_a_file(name_compare, &seg_inode, seg_vsnp, NULL, NULL, 0, NULL, file_data, NULL); if (seg_vsnp) { SamFree(seg_vsnp); } } /* * Now that we have skipped the data segment inodes * we can restore the dumped data if any. */ if (file_data) { sam_restore_a_file(name_compare, &perm_inode, vsnp, slink, data, n_acls, aclp, file_data, &file_data_read); } } skip_file: if (data) { SamFree(data); data = NULL; } if (vsnp) { SamFree(vsnp); vsnp = NULL; } if (aclp) { SamFree(aclp); aclp = NULL; } if (file_data && file_data_read == 0) { skip_embedded_file_data(); } } if (last_path != NULL) { free(last_path); } if (save_path != (char *)NULL) { free(save_path); } if (open_dir_fd > 0) { (void) close(open_dir_fd); } pop_permissions_stack(); pop_times_stack(); }
int main( int argc, /* Number of arguments */ char *argv[]) /* Argument pointer list */ { #if defined(REMOTE) char *rpchost = NULL; #endif /* defined(REMOTE) */ int errors = 0; int c; /* * Process arguments. */ while ((c = getopt(argc, argv, "h:")) != EOF) { switch (c) { #if defined(REMOTE) case 'h': rpchost = strdup(optarg); break; #endif /* defined(REMOTE) */ case '?': default: errors++; break; } } if (optind == argc || errors != 0) { (void)fprintf(stderr, "Usage: %s [-h samhost] filename ...\n", argv[0]); exit(2); } #if defined(REMOTE) if (sam_initrpc(rpchost) < 0) { perror("sam_initrpc"); exit(1); } #endif /* defined(REMOTE) */ /* * Do sam_stat() for all the files. */ while (optind < argc) { struct sam_stat sb; char *fname; int n; fname = argv[optind++]; if (sam_stat(fname, &sb, sizeof(struct sam_stat)) == -1) { perror("sam_stat"); exit(1); } /* * Print out the POSIX stat() information. */ (void)printf("\nsam_stat of file: %s\n", fname); (void)printf("mode=0%lo, ino=%ld, dev=%ld, nlink=%ld\n", sb.st_mode, sb.st_ino, sb.st_dev, sb.st_nlink); (void)printf("uid=%ld, gid=%ld\n", sb.st_uid, sb.st_gid); (void)printf("atime=%ld, mtime=%ld, ctime=%ld\n", sb.st_atime, sb.st_mtime, sb.st_ctime); if (!SS_ISSAMFS(sb.attr)) { (void)printf("\n"); return(0); } /* * Print out the SAM-FS information. */ (void)printf("attr: %05x %s\n", sb.attr, sam_attrtoa(sb.attr, NULL)); (void)printf("\n"); /* * Print out the information for each of the archive copies. */ (void)printf("copy flags vsns posn.offset media vsn\n"); for (n = 0; n < MAX_ARCHIVE; n++) { if (!(sb.copy[n].flags & CF_ARCHIVED)) continue; (void)printf("%d %5x %4d 0x%llx.%lx %-5s %s\n", n + 1, sb.copy[n].flags, sb.copy[n].n_vsns, sb.copy[n].position, sb.copy[n].offset, sb.copy[n].media, sb.copy[n].vsn); } } #if defined(REMOTE) sam_closerpc(); #endif /* defined(REMOTE) */ return(0); }
static int do_restore_inode( char *filename, /* path where file is to be restored */ filedetails_t *details, /* ptr to file info */ dumpspec_t *ds, /* info about samfsdump */ replace_t replace /* if & how file should be replaced */ ) { int rval; int file_data = 0; int file_data_read; char path[MAXPATHLEN + 1]; char name[MAXPATHLEN + 1]; char slink[MAXPATHLEN + 1]; char *slash_loc; int skipping; struct sam_perm_inode perm_inode; struct sam_stat sb; struct sam_vsn_section *vsnp; struct sam_ioctl_idtime idtime; void *data; int n_acls; aclent_t *aclp; timestackvars_t tvars = {0}; permstackvars_t pvars = {0}; int out_dir_fd = -1; int dir_fd = -1; char *dirp; size_t namelen; /* * Read the dump file - seek to the specified file offset * * Offset is the location of the actual inode. To use the * common read functions, we need to back-up the length of the * provided name. */ namelen = strlen(details->file_name); gzseek(ds->fildmp, details->snapOffset - namelen, SEEK_SET); rval = common_csd_read(ds->fildmp, name, namelen, ds->byteswapped, &perm_inode); if (rval != 0) { return (-1); } data = NULL; vsnp = NULL; aclp = NULL; n_acls = 0; if (details->summary.flags & FL_HASDATA) { file_data_read = 0; file_data++; } else { file_data_read = file_data = 0; } if (S_ISREQ(perm_inode.di.mode)) { data = malloc(perm_inode.di.psize.rmfile); } /* read_next needs error handling... --SG */ common_csd_read_next(ds->fildmp, ds->byteswapped, ds->csdversion, &perm_inode, &vsnp, slink, data, &n_acls, &aclp); /* skip system files, shouldn't happen with indexes */ if (perm_inode.di.id.ino == 1 || perm_inode.di.id.ino == 4 || perm_inode.di.id.ino == 5) { goto skip_file; } /* * Make sure that we don't restore into a non-SAM-FS filesystem. * * Search backwards through path for a viable SAM-FS path. * by calling sam_stat() and checking the directory attributes */ rval = sam_stat(filename, &sb, sizeof (sb)); strlcpy(path, filename, sizeof (path)); dirp = dirname(path); if (rval == 0) { if (replace == REPLACE_NEVER) { /* can't do anything, file exists */ rval = -2; goto skip_file; } if (!SS_ISSAMFS(sb.attr)) { rval = -3; goto skip_file; } } else { /* * file doesn't exist * loop through path looking for samfs, create * path if necessary */ rval = sam_stat(path, &sb, sizeof (sb)); if ((rval == 0) && (!SS_ISSAMFS(sb.attr))) { rval = -3; goto skip_file; } /* only mkdir if parent directory does not exist */ if (rval != 0) { while ((slash_loc = strrchr(path, '/')) != NULL) { *slash_loc = '\0'; rval = sam_stat(path, &sb, sizeof (sb)); if (rval == 0) { break; } } if (rval != 0) { /* no part of path exists? */ rval = -1; goto skip_file; } if (!SS_ISSAMFS(sb.attr)) { rval = -3; goto skip_file; } strlcpy(path, filename, sizeof (path)); dirp = dirname(path); rval = mkdirp(dirp, S_IRWXU | S_IRWXG | S_IRWXO); if (rval != 0) { rval = -1; goto skip_file; } } } /* Open the parent directory so times can be properly reset */ idtime.id.ino = sb.st_ino; idtime.id.gen = sb.gen; idtime.atime = sb.st_atime; idtime.mtime = sb.st_mtime; idtime.xtime = sb.creation_time; idtime.ytime = sb.attribute_time; dir_fd = open64(dirp, O_RDONLY); /* ok, ready to start restoring ... */ /* * if segment index, we have to skip the data segment inodes * before we can get to the dumped data. */ if (!(S_ISSEGI(&perm_inode.di) && file_data)) { common_sam_restore_a_file(ds->fildmp, filename, &perm_inode, vsnp, slink, data, n_acls, aclp, file_data, &file_data_read, &out_dir_fd, replace, &tvars, &pvars); } if (S_ISSEGI(&perm_inode.di)) { struct sam_perm_inode seg_inode; struct sam_vsn_section *seg_vsnp; int i; offset_t seg_size; int no_seg; sam_id_t seg_parent_id = perm_inode.di.id; /* * If we are restoring the data, don't restore the data segment * inodes. This means we will lose the archive copies, if any. */ skipping = file_data; /* * Read each segment inode. If archive copies overflowed, * read vsn sections directly after each segment inode. */ seg_size = (offset_t)perm_inode.di.rm.info.dk.seg_size * SAM_MIN_SEGMENT_SIZE; no_seg = (perm_inode.di.rm.size + seg_size - 1) / seg_size; for (i = 0; i < no_seg; i++) { gzread(ds->fildmp, &seg_inode, sizeof (seg_inode)); /* * cs_restore keeps going on the segments even * on error. We'll do that here too, but I'm not * sure it's right. */ if (ds->byteswapped) { if (sam_byte_swap( sam_perm_inode_swap_descriptor, &seg_inode, sizeof (seg_inode))) { continue; } } if (!(SAM_CHECK_INODE_VERSION(seg_inode.di.version))) { continue; } if (skipping) { continue; } seg_inode.di.parent_id = seg_parent_id; seg_vsnp = NULL; common_csd_read_mve(ds->fildmp, ds->csdversion, &seg_inode, &seg_vsnp, ds->byteswapped); common_sam_restore_a_file(ds->fildmp, filename, &seg_inode, seg_vsnp, NULL, NULL, 0, NULL, file_data, NULL, &out_dir_fd, replace, &tvars, &pvars); if (seg_vsnp) { free(seg_vsnp); } } /* * Now that we have skipped the data segment inodes * we can restore the dumped data if any. */ if (file_data) { common_sam_restore_a_file(ds->fildmp, filename, &perm_inode, vsnp, slink, data, n_acls, aclp, file_data, &file_data_read, &out_dir_fd, replace, &tvars, &pvars); } } skip_file: if (data) { free(data); } if (vsnp) { free(vsnp); } if (aclp) { free(aclp); } if (file_data && file_data_read == 0) { common_skip_embedded_file_data(ds->fildmp); } common_pop_permissions_stack(&pvars); if (dir_fd > 0) { common_pop_times_stack(dir_fd, &tvars); ioctl(dir_fd, F_IDTIME, &idtime); close(dir_fd); } if (out_dir_fd > 0) { close(out_dir_fd); } return (rval); }
/* Sanity check restore list */ int restore_check(sqm_lst_t *filepaths, sqm_lst_t *copies, sqm_lst_t *dest, dumpspec_t *dsp) { node_t *destp; node_t *pathp; node_t *copyp; int which; struct sam_stat sb; int rval; char buf[MAXPATHLEN + 1]; char *slash_loc; /* Make sure both lists are the same length */ if ((filepaths->length != dest->length) || (filepaths->length != copies->length)) { return (samrerr(SE_MISMATCHEDARGS, "")); } destp = dest->head; pathp = filepaths->head; copyp = copies->head; /* * Sanity check to make sure all source files exist and destinations * are viable and don't already exist. */ while (destp != NULL) { /* * Count how many nodes this will be. */ if (strcmp(".", pathp->data) != 0) { #ifdef COUNT_REQUESTED_NODES /* include the start point in the count */ restore_max = 1; (void) restore_children(pathp->data, 0, "", "", dsp, REPLACE_NEVER, TRUE); #else /* * counting can be very time consuming and cause * the browser to time out. The 'right' answer for * very large directories is to have the number of * entries stored in the database. This approach, * rather than walking the whole thing, should be * investigated in the post-4.6 timeframe. For now, * the 4.6 GUI will handle the special case of * max = 0 when reporting restore job status. */ restore_max = 0; #endif /* COUNT_REQUESTED_NODES */ } else { /* whole file system, use numnodes from index */ restore_max = dsp->numfiles; } /* Verify copy selected is legal */ which = *(int *)(copyp->data); if ((which != DONT_STAGE) && (which != SAM_CHOOSES_COPY) && (which != STAGE_AS_AT_DUMP)) { if ((which < 0) || (which > 3)) return (samrerr(SE_BADSTAGE, destp->data)); } /* validate that destination directory is on a SAM filesystem */ rval = sam_stat(destp->data, &sb, sizeof (sb)); if ((rval == 0) && (!SS_ISSAMFS(sb.attr))) { return (samrerr(SE_RESTORE_NOT_SAMFS, destp->data)); } /* * Restore makes the full directory path when required, so * keep looking until we find an existing directory */ strlcpy(buf, destp->data, sizeof (buf)); if (rval != 0) { while ((slash_loc = strrchr(buf, '/')) != NULL) { *slash_loc = '\0'; rval = sam_stat(buf, &sb, sizeof (sb)); if (rval == 0) { break; } } /* * rval will be non-zero only if no part of the * path exists. This really shouldn't happen... */ if ((rval != 0) || (!SS_ISSAMFS(sb.attr))) { return (samrerr(SE_RESTORE_NOT_SAMFS, destp->data)); break; } } destp = destp->next; pathp = pathp->next; copyp = copyp->next; } return (0); }
int main(int argc, char *argv[]) { char *load_file = (char *)NULL; /* DB load path/file name */ char *optstring; /* Command option string */ char *logfile = NULL; /* Log file name for samfsrestore -g option */ struct sam_stat sb; int option; /* Current getopt option being processed */ int io_sz, read_size; int len; operator_t operator; /* Operator data */ shm_alloc_t master_shm; /* Master shared memory structure */ program_name = basename(argv[0]); operation = NONE; replace_newer = false; replace = false; swapped = false; verbose = false; quiet = false; noheaders = false; strip_slashes = false; list_by_inode = false; online_data = false; partial_data = false; scan_only = false; unarchived_data = false; use_file_list = false; read_buffer_size = write_buffer_size = CSD_DEFAULT_BUFSZ; block_size = 0; memset(&csd_header, '\0', sizeof (csd_header)); CustmsgInit(0, NULL); if ((Initial_path = getcwd(NULL, (MAXPATHLEN + 1))) == NULL) { error(1, errno, catgets(catfd, SET, 590, "cannot getcwd()")); } /* * If basename is samfsrestore, fake out "r" option; if * samfsdump, fake out "c" option. Allow redundant specification * specifications like "samfsrestore r", "samfsdump c". Allow * "samfsrestore t" to override "r" option so that we look * like "ufsrestore t". If basename is none of the above, then * user must specify one of c,r,t,O. */ if (strcmp(program_name, "samfsrestore") == 0) { operation = RESTORE; optstring = RESTORE_OPT; qfs = false; } else if (strcmp(program_name, "samfsdump") == 0) { operation = DUMP; optstring = DUMP_OPT; qfs = false; } else if (strcmp(program_name, "qfsdump") == 0) { operation = DUMP; optstring = QFSDUMP_OPT; qfs = true; } else if (strcmp(program_name, "qfsrestore") == 0) { operation = RESTORE; optstring = RESTORE_OPT; qfs = true; } if (operation == NONE) { error(1, 0, catgets(catfd, SET, 13508, "Improper invocation of SAM-FS csd " "utility %s"), program_name); } for (nexcluded = CSD_MAX_EXCLUDED - 1; nexcluded >= 0; nexcluded--) { excluded[nexcluded] = NULL; } nexcluded = 0; for (nincluded = CSD_MAX_INCLUDED - 1; nincluded >= 0; nincluded--) { included[nincluded] = NULL; } nincluded = 0; while ((option = getopt(argc, argv, optstring)) != EOF) { switch (option) { case 'B': /* select buffer size */ io_sz = atoi(optarg) * 512; if (io_sz < CSD_MIN_BUFSZ || io_sz > CSD_MAX_BUFSZ) { error(1, 0, catgets(catfd, SET, 13509, "Invalid buffer size, must " "be >= %d and <= %d, " "specified in 512b units"), CSD_MIN_BUFSZ/512, CSD_MAX_BUFSZ/512); } read_buffer_size = write_buffer_size = io_sz; break; case 'b': /* select blocking factor */ io_sz = atoi(optarg) * 512; if (io_sz < 0 || io_sz > CSD_MAX_BUFSZ) { error(1, 0, catgets(catfd, SET, 13510, "Invalid blocking factor, " "must be >= 0 and <= %d, " "specified in 512b units"), CSD_MAX_BUFSZ/512); } block_size = io_sz; break; case 'd': /* enable debug messages */ debugging = 1; break; case 'D': /* enable directio */ Directio++; break; case 'I': /* file of paths to process */ if (nincluded > CSD_MAX_INCLUDED) { error(1, 0, catgets(catfd, SET, 13537, "Maximum -I (include file) entries exceeded, max. is %d."), CSD_MAX_INCLUDED); } included[nincluded] = optarg; len = strlen(optarg); if (len > 0) { *(included[nincluded] + len) = '\0'; nincluded++; } else { error(1, 0, catgets(catfd, SET, 13538, "-I (include file) entry " "<%s> invalid"), optarg); } break; case 'X': /* excluded directory list */ if (nexcluded > CSD_MAX_EXCLUDED) { error(1, 0, catgets(catfd, SET, 13520, "Maximum -X (excluded " "directory) entries " "exceeded, max. is %d."), CSD_MAX_EXCLUDED); } excluded[nexcluded] = optarg; len = strlen(optarg); if (len > 0) { *(excluded[nexcluded] + len) = '\0'; nexcluded++; } else { error(1, 0, catgets(catfd, SET, 13521, "-X (excluded directory) entry <%s> " "invalid"), optarg); } break; case 'f': /* specify dump file */ dump_file = optarg; break; case 'g': /* specify log file */ logfile = optarg; break; case 'H': /* no headers */ noheaders = true; break; case 'i': /* print inode numbers */ ls_options |= LS_INODES; break; case 'l': /* print 1 line ls */ verbose = true; ls_options |= LS_LINE1; break; case 'n': /* Deprecated. Do not break script. */ break; case 'P': /* dump partial online data */ partial_data = true; break; case 'q': /* Do not report damage warnings */ quiet = true; break; case 'r': /* replace file if dump is newer */ replace_newer = true; break; case 'R': /* replace file */ replace = true; break; case 's': /* strip leading /'s */ strip_slashes = true; break; case 'S': /* scan only */ scan_only = true; break; case 't': /* list dump file */ if (operation & DUMP) { error(1, 0, catgets(catfd, SET, 941, "Do not specify 't' option.")); } /* samfsrestore t is LIST, not RESTORE */ operation = LIST; break; case 'T': /* statistics mode */ statistics = true; break; case 'u': /* dump unarchived data */ unarchived_data = true; break; case 'U': /* dump online data */ online_data = true; break; case 'v': /* verbose mode */ verbose = true; break; case 'W': /* warning mode, allowed for historical reasons */ break; case '2': /* print 2 line ls */ verbose = true; ls_options |= LS_LINE2; break; case 'Y': /* file list for dump */ use_file_list = true; break; case 'Z': /* generate database load file */ load_file = optarg; break; case '?': usage(); /* doesn't return */ break; } } if (scan_only) { /* clear dump related flags */ online_data = partial_data = unarchived_data = false; } if (block_size && (block_size > read_buffer_size)) { block_size = read_buffer_size; } if (operation == DUMP && use_file_list) { operation = LISTDUMP; } if (debugging) { int i; fprintf(stderr, "\nCOMMAND ARGUMENT VALUES\n"); fprintf(stderr, "operation = 0x%x\n", operation); fprintf(stderr, "debugging = %d\n", debugging); fprintf(stderr, "dump_file = %s\n", ((dump_file != (char *)NULL) ? dump_file : "UNDEFINED")); fprintf(stderr, "logfile = %s\n", ((logfile != (char *)NULL) ? logfile : "UNDEFINED")); fprintf(stderr, "noheaders = %d\n", noheaders); fprintf(stderr, "verbose = %d\n", verbose); fprintf(stderr, "quiet = %d\n", quiet); fprintf(stderr, "qfs = %d\n", qfs); fprintf(stderr, "replace = %d\n", replace); fprintf(stderr, "replace_newer = %d\n", replace_newer); fprintf(stderr, "strip_slashes = %d\n", strip_slashes); fprintf(stderr, "ls_options = 0x%x\n", ls_options); fprintf(stderr, "load_file = %s\n", ((load_file != (char *)NULL) ? load_file : "UNDEFINED")); fprintf(stderr, "online_data = %d\n", online_data); fprintf(stderr, "partial_data = %d\n", partial_data); fprintf(stderr, "scan_only = %d\n", scan_only); fprintf(stderr, "unarchived_data = %d\n", unarchived_data); fprintf(stderr, "use_file_list = %d\n", use_file_list); fprintf(stderr, "read buffer size = %ld\n", read_buffer_size); fprintf(stderr, "write buffer size = %ld\n", write_buffer_size); fprintf(stderr, "blocking factor = %ld\n", block_size); for (i = optind; i < argc; i++) { fprintf(stderr, "File argument %d = %s\n", (i - optind + 1), argv[i]); } fprintf(stderr, "END COMMAND ARGUMENT VALUES\n\n"); } /* * If dump file has not been specified, exit with error and usage */ if ((!scan_only) && dump_file == (char *)NULL) { error(0, 0, catgets(catfd, SET, 5019, "%s: Dump file name not specified (-f)"), program_name); usage(); } /* * Access master shared memory segment */ if ((master_shm.shared_memory = (shm_ptr_tbl_t *) sam_mastershm_attach(0, SHM_RDONLY)) == (void *)-1) { /* * If shared memory not available, effective ID must be * super-user. */ if (geteuid() != 0) { error(1, 0, catgets(catfd, SET, 100, "%s may be run only by super-user.\n"), program_name); } } else { /* * If operator is not super-user, issue error message and exit */ SET_SAM_OPER_LEVEL(operator); shmdt((char *)master_shm.shared_memory); if (!SAM_ROOT_LEVEL(operator)) { error(1, 0, catgets(catfd, SET, 100, "%s may be run only by super-user.\n"), program_name); } } /* * Open dump file and process header */ switch (operation) { case NONE: error(1, 0, catgets(catfd, SET, 1845, "One of c, r, O or t options must be specified.")); usage(); /* doesn't return */ break; case DUMP: case LISTDUMP: if (!scan_only) { if (strcmp(dump_file, "-") == 0) { CSD_fd = dup(STDOUT); } else { CSD_fd = open(dump_file, O_WRONLY|O_TRUNC|O_CREAT|SAM_O_LARGEFILE, 0666 & ~umask(0)); } close(STDOUT); if (CSD_fd < 0) { error(1, errno, catgets(catfd, SET, 216, "%s: Cannot create dump file"), dump_file); } } break; case RESTORE: case LIST: if (0 == strcmp(dump_file, "-")) { CSD_fd = dup(STDIN); } else { CSD_fd = open(dump_file, O_RDONLY | SAM_O_LARGEFILE); } close(STDIN); if (CSD_fd < 0) { error(1, errno, catgets(catfd, SET, 225, "%s: Cannot open dump file"), dump_file); } if (noheaders) { break; } if (buffered_read(CSD_fd, &csd_header, sizeof (csd_hdr_t)) != sizeof (csd_hdr_t)) { error(1, errno, catgets(catfd, SET, 244, "%s: Header record read error"), dump_file); } if (csd_header.csd_header.magic != CSD_MAGIC) { if (csd_header.csd_header.magic != CSD_MAGIC_RE) { error(1, 0, catgets(catfd, SET, 13529, "%s: Volume is not in dump format."), dump_file); } else { error(0, 0, "Dump file was generated on an " "opposite endian machine"); swapped = true; sam_byte_swap(csd_header_swap_descriptor, &csd_header, sizeof (csd_hdr_t)); } } csd_version = csd_header.csd_header.version; switch (csd_version) { case CSD_VERS_6: case CSD_VERS_5: /* read remainder of extended header */ io_sz = sizeof (csd_hdrx_t) - sizeof (csd_hdr_t); read_size = buffered_read(CSD_fd, (char *)((char *)(&csd_header) + sizeof (csd_hdr_t)), io_sz); if (swapped) { sam_bswap4(&csd_header.csd_header_flags, 1); sam_bswap4(&csd_header.csd_header_magic, 1); sam_bswap8(&csd_header.csd_fs_magic, 1); } if ((read_size != io_sz) || csd_header.csd_header_magic != CSD_MAGIC) { error(1, errno, catgets(catfd, SET, 244, "%s: Header record read error"), dump_file); } break; case CSD_VERS_4: case CSD_VERS_3: case CSD_VERS_2: break; default: error(1, 0, catgets(catfd, SET, 5013, "%s: Header record error: Bad version number (%d)"), dump_file, csd_version); } if (verbose) { printf(catgets(catfd, SET, 972, "Dump created:%s"), ctime((const time_t *)&csd_header.csd_header.time)); } if (debugging) { fprintf(stderr, "Dump created:%s\n", ctime((const time_t *)&csd_header.csd_header.time)); } } if ((logfile != NULL) && (log_st = fopen(logfile, "w")) == NULL) { error(0, errno, "%s", logfile); error(0, 0, catgets(catfd, SET, 1856, "Open failed on (%s)"), logfile); } if ((load_file != NULL)) { if (0 == strcmp(load_file, "-")) { DB_FILE = fdopen(dup(STDOUT), "w"); close(STDOUT); } else if ((DB_FILE = fopen64(load_file, "w")) == NULL) { error(0, errno, "%s", load_file); error(1, 0, catgets(catfd, SET, 1856, "Open failed on (%s)"), load_file); } } switch (operation) { case NONE: break; case DUMP: { char *filename; if (debugging) { fprintf(stderr, "dumping. argc %d, initial " "path '%s' \n", optind, Initial_path); } while ((filename = get_path(argc, argv)) != NULL) { if (debugging) { fprintf(stderr, "dumping path '%s' \n", filename); } /* * Try to make sure we don't dump from a non-SAM-FS * filesystem by calling sam_stat() and checking the * attributes. */ if (sam_stat(filename, &sb, sizeof (sb)) < 0 || ! SS_ISSAMFS(sb.attr)) { BUMP_STAT(errors); BUMP_STAT(errors_dir); error(1, errno, catgets(catfd, SET, 259, "%s: Not a SAM-FS file."), filename); } else { if (SAM_fd != -1) { close(SAM_fd); } SAM_fd = open_samfs(filename); csd_dump_path("initial", filename, (mode_t)sb.st_mode); if (S_ISDIR(sb.st_mode)) { process_saved_dir_list(filename); } } if (chdir(Initial_path) < 0) { error(1, errno, catgets(catfd, SET, 212, "%s: cannot chdir()"), Initial_path); } } } bflush(CSD_fd); break; case LISTDUMP: { char *filename; if (debugging) { fprintf(stderr, "dumping. argc %d, initial " "path '%s' \n", optind, Initial_path); } while ((filename = get_path(argc, argv)) != NULL) { if (debugging) { fprintf(stderr, "dumping path '%s' \n", filename); } if (strcmp(filename, "-") == 0) { DL_FILE = fdopen(dup(STDIN), "r"); close(STDIN); } else if ((DL_FILE = fopen(filename, "r")) == NULL) { error(0, errno, "%s", load_file); error(1, 0, catgets(catfd, SET, 1856, "Open failed on (%s)"), load_file); } if (SAM_fd != -1) { close(SAM_fd); } csd_dump_files(DL_FILE, filename); fclose(DL_FILE); DL_FILE = NULL; } } bflush(CSD_fd); break; case RESTORE: umask(0); cs_restore(strip_slashes, (argc - optind), &argv[optind]); break; case LIST: cs_list((argc - optind), &argv[optind]); } close(CSD_fd); if (statistics) { print_stats(); } return (exit_status); }
int main( int argc, /* Number of arguments */ char *argv[]) /* Argument pointer list */ { extern int optind; int c; #if defined(REMOTE) char *opts = "h:dn"; char *rpchost = NULL; #else /* defined(REMOTE) */ char *opts = "dn"; #endif /* defined(REMOTE) */ /* * Process arguments. */ while ((c = getopt(argc, argv, opts)) != EOF) { switch (c) { case 'd': Default = TRUE; break; case 'n': n_opt = TRUE; break; #if defined(REMOTE) case 'h': rpchost = strdup(optarg); break; #endif /* defined(REMOTE) */ case '?': default: exit_status++; } } if (optind == argc) exit_status++; /* No file name */ if (exit_status != 0) { (void) fprintf(stderr, "Usage: %s [-h samhost] [-d] [-n] filename", argv[0]); exit(2); } #if defined(REMOTE) if (sam_initrpc(rpchost) < 0) { (void) fprintf(stderr, "sam_initrpc failed, errno %d.\n", errno); exit(2); } #endif /* defined(REMOTE) */ /* * Set up action. */ (void) memset(opns, 0, sizeof (opns)); if (Default) *opn++ = 'd'; if (n_opt) *opn++ = 'n'; if (!Default && !n_opt) *opn++ = 'i'; /* * Process all file names. */ while (optind < argc) { char *name = argv[optind++]; (void) strncpy(fullpath, name, sizeof (fullpath) - 2); if (sam_lstat(fullpath, &sb, sizeof (struct sam_stat)) < 0) { perror("sam_lstat"); continue; } if (!SS_ISSAMFS(sb.attr)) { perror("Not a SAM-FS file"); continue; } if (sam_archive(name, opns) < 0) { perror("sam_archive"); continue; } } #if defined(REMOTE) sam_closerpc(); #endif /* defined(REMOTE) */ return (exit_status); }
int main( int argc, /* Number of arguments */ char *argv[]) /* Argument pointer list */ { extern int optind; struct sam_stat sb; char c; char *fname; int errors = 0; int media; int optical_opts = 0; int media_set = 0; int tape_opts = 0; char *argvsn; int n_pos = 0; int pos_set = 0; char *argpos; char *posp; program_name = basename(argv[0]); /* * Process arguments. */ CustmsgInit(0, NULL); while ((c = getopt(argc, argv, "m:v:l:bdp:s:f:n:o:g:i:N")) != EOF) { switch (c) { case 'm': { strcpy(rb.media, optarg); if ((media = sam_atomedia(rb.media)) == 0) { error(2, 0, catgets(catfd, SET, 1416, "Invalid media")); } media_set = 1; } break; case 'v': { argvsn = optarg; if (rb.n_vsns) { error(2, 0, catgets(catfd, SET, 13210, "%s and %s are mutually exclusive."), "v", "l"); } while ((vsnp[rb.n_vsns] = strtok(argvsn, "/")) != NULL) { if (strlen(vsnp[rb.n_vsns]) > sizeof (vsn_t)-1) { error(2, 0, catgets(catfd, SET, 2879, "vsn longer than %d characters"), sizeof (vsn_t)-1); } if (++rb.n_vsns > MAX_VOLUMES) { error(2, 0, catgets(catfd, SET, 1832, "number of vsns greater than %d"), MAX_VOLUMES); } argvsn = NULL; } } break; case 'l': { fname = optarg; if (rb.n_vsns) { error(2, 0, catgets(catfd, SET, 13210, "%s and %s are mutually exclusive."), "l", "v"); } else if (pos_set) { error(2, 0, catgets(catfd, SET, 13210, "%s and %s are mutually exclusive."), "l", "p"); } else { FILE *vsnfd; if ((vsnfd = fopen(fname, "r")) == NULL) { error(1, errno, catgets(catfd, SET, 20010, "unable to open %s"), fname); } for (;;) { char c[80]; char *v; /* * if EOF */ if (fgets(c, 80, vsnfd) == NULL) { break; } if ((v = strtok(c, " \n\t")) != NULL) { vsnp[rb.n_vsns] = strdup(v); position[rb.n_vsns] = 0; if ((v = strtok(NULL, " \n\t")) != NULL) { position[rb.n_vsns] = strtoll(v, NULL, 0); pos_set = 1; } if (++rb.n_vsns > MAX_VOLUMES) { /* N.B. Bad indentation here to meet cstyle requirements */ error(2, 0, catgets(catfd, SET, 1832, "number of vsns greater than %d"), MAX_VOLUMES); } } } if (pos_set) { n_pos = rb.n_vsns; rb.position = position[0]; } } } break; case 'b': if (rb.flags == RI_blockio) { error(0, 0, catgets(catfd, SET, 2975, "b and d options cannot be used together")); errors++; } rb.flags = RI_bufio; break; case 'd': if (rb.flags == RI_bufio) { error(0, 0, catgets(catfd, SET, 2975, "b and d options cannot be used together")); errors++; } rb.flags = RI_blockio; break; case 'p': { char *p; if (n_pos) { error(2, 0, catgets(catfd, SET, 13210, "%s and %s are mutually exclusive."), "p", "l"); } if (getzoneid() != GLOBAL_ZONEID) { error(0, 0, catgets(catfd, SET, 5039, "cannot specify -p in" " the non-global zone")); errors++; } if (getuid() != 0) { error(0, 0, catgets(catfd, SET, 2975, "You must be root to set the position")); errors++; } else { argpos = optarg; while ((posp = strtok(argpos, "/")) != NULL) { position[n_pos] = strtoll(posp, &p, 0); if (*p != '\0') { error(0, 0, catgets(catfd, SET, 1043, "error in position")); errors++; } if (++n_pos > MAX_VOLUMES) { error(2, 0, catgets(catfd, SET, 3042, "number of positions" " greater than %d"), MAX_VOLUMES); } argpos = NULL; } rb.position = position[0]; pos_set = TRUE; } } break; case 's': { char *p; rb.required_size = strtoll(optarg, &p, 0); if (*p != '\0') { error(0, 0, catgets(catfd, SET, 1044, "error in required size")); errors++; } } break; case 'f': if (strlen(optarg) > sizeof (rb.file_id)-1) { error(0, 0, catgets(catfd, SET, 1144, "file identifier longer than %d" " characters"), sizeof (rb.file_id)-1); errors++; } else { strcpy(rb.file_id, optarg); } optical_opts++; break; case 'n': { char *p; rb.version = strtol(optarg, &p, 0); if (*p != '\0') { error(0, 0, catgets(catfd, SET, 1047, "error in version")); errors++; } } break; case 'o': if (strlen(optarg) > sizeof (rb.owner_id)-1) { error(0, 0, catgets(catfd, SET, 1898, "owner identifier longer than %d" " characters"), sizeof (rb.owner_id)-1); errors++; } else { strcpy(rb.owner_id, optarg); } optical_opts++; break; case 'g': if (strlen(optarg) > sizeof (rb.group_id)-1) { error(0, 0, catgets(catfd, SET, 1270, "group identifier longer than %d" " characters"), sizeof (rb.group_id)-1); errors++; } else { strcpy(rb.group_id, optarg); } optical_opts++; break; case 'i': if (strlen(optarg) > sizeof (rb.info)-1) { error(0, 0, catgets(catfd, SET, 2834, "user info longer than %d characters"), sizeof (rb.info)-1); errors++; } else { strcpy(rb.info, optarg); } optical_opts++; break; case 'N': rb.flags |= RI_foreign; tape_opts++; break; case '?': default: errors++; } } if (rb.flags == 0) { rb.flags = RI_blockio; /* Block IO is the default */ } /* * Set media. */ if (!media_set) { if (optind >= argc) { exit(2); } if (strlen(argv[optind]) != 2) { error(2, 0, catgets(catfd, SET, 1416, "Invalid media")); } strcpy(rb.media, argv[optind++]); if ((media = sam_atomedia(rb.media)) == 0) { error(2, 0, catgets(catfd, SET, 1416, "Invalid media")); } } /* * Set vsns. */ if (!rb.n_vsns) { if (optind >= argc) { exit(2); } argvsn = argv[optind]; while ((vsnp[rb.n_vsns] = strtok(argvsn, "/")) != NULL) { if (strlen(vsnp[rb.n_vsns]) > sizeof (vsn_t)-1) { error(2, 0, catgets(catfd, SET, 2879, "vsn longer than %d characters"), sizeof (vsn_t)-1); } if (++rb.n_vsns > MAX_VOLUMES) { error(2, 0, catgets(catfd, SET, 1832, "number of vsns greater than %d"), MAX_VOLUMES); } argvsn = NULL; } optind++; } if (pos_set && (rb.n_vsns != n_pos)) { error(2, 0, catgets(catfd, SET, 3043, "number of vsns %d does not match number of position %d"), rb.n_vsns, n_pos); } /* * Get sam_rminfo with length enough for n_vsns. */ rp = &rb; if (rb.n_vsns > 0) { int v; rp = (struct sam_rminfo *)malloc(SAM_RMINFO_SIZE(rb.n_vsns)); memset(rp, 0, SAM_RMINFO_SIZE(rb.n_vsns)); memcpy(rp, &rb, sizeof (struct sam_rminfo)); for (v = 0; v < rb.n_vsns; v++) { strcpy(rp->section[v].vsn, vsnp[v]); if (pos_set) { rp->section[v].position = position[v]; } } } fname = argv[optind]; if (errors || (argc != (optind + 1))) { fprintf(stderr, "usage:\ntape\n" " %s -m media [-v vsn1[/vsn2/...] [-p pos1[/pos2/...] | -l vsnfile]\n" " [-s n] [-N] file\n" "optical\n" " %s -m media [-v vsn1[/vsn2/...] [-p pos1[/pos2/...] | -l vsnfile]\n" " [-s n]\n" " [-f file_id] [-n version] [-o owner] [-g group] [-i info] file\n", program_name, program_name); exit(2); } /* * Set optical defaults. */ if ((media & DT_CLASS_MASK) == DT_OPTICAL) { if (tape_opts != 0) { error(2, 0, catgets(catfd, SET, 5023, "tape options not valid for this media")); } if (*rb.file_id == '\0') { char *p; p = basename(fname); if (strlen(p) > sizeof (rb.file_id)-1) { error(2, 0, catgets(catfd, SET, 1150, "file name > %d chars, use shorter" " file name or -f option"), sizeof (rb.file_id)-1); } strcpy(rb.file_id, p); } if (*rb.owner_id == '\0') { struct passwd *pw; pw = getpwuid(getuid()); strncpy(rb.owner_id, pw->pw_name, sizeof (rb.owner_id)-1); } if (*rb.group_id == '\0') { struct group *gr; gr = getgrgid(getgid()); strncpy(rb.group_id, gr->gr_name, sizeof (rb.owner_id)-1); } } else { char *p; int v; if (optical_opts != 0) { error(2, 0, catgets(catfd, SET, 1876, "optical options not valid for this media")); } /* * Validate tape vsn. */ for (v = 0; v < rb.n_vsns; v++) { if ((int)strlen(rp->section[v].vsn) > 6) { error(2, 0, catgets(catfd, SET, 2469, "tape vsn longer than 6 characters")); } for (p = rp->section[v].vsn; *p != '\0'; p++) { if (isupper(*p) || isdigit(*p)) { continue; } if (strchr(" !\"%&'()*+,-./:;<=>?_", *p) != NULL) { continue; } error(2, 0, catgets(catfd, SET, 1319, "illegal character in tape vsn")); } } } #if defined(PRrminfo) /* BLOCK for cstyle */ { struct tm *tm; char timestr[32]; int n; printf("file: %s media: %s required_size: 0x%llx\n", fname, rp->media, rp->required_size); printf("file_id: %s version: %d\n", rp->file_id, rp->version); printf("owner: %s group: %s\n", rp->owner_id, rp->group_id); printf("info: %s\n", rp->info); printf("n_vsns: %d c_vsn: %d\n", rp->n_vsns, rp->c_vsn); for (n = 0; n < rp->n_vsns; n++) { printf(" vsn[%d]:%s position:0x%llx\n", n, rp->section[n].vsn, rp->section[n].position); } tm = localtime((time_t *)&rp->creation_time); strftime(timestr, sizeof (timestr)-1, "%Y/%m/%d %H:%M:%S", tm); printf("time: %s\n", timestr); } #endif if (sam_stat(fname, &sb, sizeof (sb)) < 0) { int fd; if ((fd = open(fname, O_CREAT, 0666)) < 0) { error(1, errno, catgets(catfd, SET, 574, "cannot create %s"), fname); } close(fd); if (sam_stat(fname, &sb, sizeof (sb)) < 0) { error(1, errno, catgets(catfd, SET, 633, "cannot sam_stat %s"), fname); } } if (!SS_ISSAMFS(sb.attr)) { error(1, 0, catgets(catfd, SET, 1146, "file is not on a SAM filesystem")); } if (sam_request(fname, rp, SAM_RMINFO_SIZE(rb.n_vsns)) < 0) { switch (errno) { case EINVAL: error(2, 0, catgets(catfd, SET, 1416, "Invalid media")); break; /* statement not reached */ /* * The request operation is not supported on clients of * the mounted FS. We could get here as a client via a * syscall or ioctl. In the event that we do then unlink * the file and tell the user they can't do this. */ case ENOTSUP: (void) unlink(fname); error(2, 0, catgets(catfd, SET, 630, "cannot request: operation not supported" " on shared client")); break; /* statement not reached */ } error(1, errno, catgets(catfd, SET, 629, "cannot request %s"), fname); } #if defined(PRrminfo) /* BLOCK for cstyle */ { struct tm *tm; char timestr[32]; int n; int nvsns = rb.n_vsns; memset(rp, 0, SAM_RMINFO_SIZE(nvsns)); if (sam_readrminfo(fname, rp, SAM_RMINFO_SIZE(nvsns)) < 0) { error(1, 1, catgets(catfd, SET, 627, "cannot read rminfo %s"), fname); } printf(catgets(catfd, SET, 2125, "Returned --\n")); printf("file: %s media: %s required_size: 0x%llx\n", fname, rp->media, rp->required_size); printf("position: 0x%llx file_id: %s version: %d\n", rp->position, rp->file_id, rp->version); printf("owner: %s group: %s\n", rp->owner_id, rp->group_id); printf("info: %s\n", rp->info); printf("n_vsns: %d c_vsn: %d\n", rp->n_vsns, rp->c_vsn); for (n = 0; n < nvsns; n++) { printf(" vsn[%d]:%s position:0x%llx\n", n, rp->section[n].vsn, rp->section[n].position); } tm = localtime((time_t *)&rp->creation_time); strftime(timestr, sizeof (timestr)-1, "%Y/%m/%d %H:%M:%S", tm); printf("time: %s\n", timestr); } #endif return (0); }