Example #1
0
/*
 * ----- 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);
	}
}
Example #2
0
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();
}
Example #3
0
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);
}
Example #4
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);
}
Example #5
0
/* 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);
}
Example #6
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);
}
Example #7
0
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);
}
Example #8
0
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);
}