+++ /dev/null
-/*
- * Simple utility to migrate a group of specified files.
- * The unsorted input is from migfind, and is of the form:
- * filehandle length filehandle file size
- *
- * The data for each file will be stored in another file located
- * in a different directory. This 'staging directory' should be on
- * another filesystem. The staging file will be named the same as
- * the file handle. This simple-minded scheme suffices, since we're
- * not interested in showing any media management in this example.
- *
- * ASSUMPTIONS:
- * Persistent managed regions are supported
- * Persistent DM attributes are supported
- *
- *
- * This code was written by Peter Lawthers, and placed in the public
- * domain for use by DMAPI implementors and app writers.
- *
- * Standard disclaimer:
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/types.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-
-#include <unistd.h>
-#include <ctype.h>
-#include <string.h>
-#include <fcntl.h>
-
-#include <lib/hsm.h>
-
-extern char *optarg;
-extern int optind, optopt, opterr;
-char *Progname;
-int Verbose;
-
-int mig_files(dm_sessid_t, char *);
-int mk_nonrez(dm_sessid_t, void *, size_t, dm_token_t, dm_off_t);
-int set_mrgns(dm_sessid_t, void *, size_t, dm_token_t, dm_off_t,
- dm_off_t *);
-void clear_mrgns(dm_sessid_t, void *, dm_size_t, dm_token_t);
-int lock_file(dm_sessid_t, void *, size_t, dm_right_t, dm_token_t *);
-void unlock_file(dm_sessid_t, dm_token_t);
-int get_dmchange(dm_sessid_t, void *, size_t, dm_token_t, u_int *);
-int create_stgfile(char *, void *, size_t, char *, int *);
-int setup_dmapi(dm_sessid_t *);
-int save_filedata(dm_sessid_t, void *, size_t, int, dm_size_t);
-int extract_fields(char *, char *, size_t *, dm_size_t *);
-int save_dataloc(dm_sessid_t, void *, size_t, dm_token_t, char *);
-
-void usage(char *);
-
-void
-usage(
- char *prog)
-{
- fprintf(stderr, "Usage: %s ", prog);
- fprintf(stderr, " <-v verbose> ");
- fprintf(stderr, "<staging directory>\n");
-}
-
-
-int
-main(
- int argc,
- char *argv[])
-{
-
- int c;
- int error;
- char *stage_dir;
- dm_sessid_t sid;
-
-
- error = 0;
- Progname = argv[0];
- stage_dir = NULL;
-
- while ((c = getopt(argc, argv, "v")) != EOF) {
- switch (c) {
- case 'v':
- Verbose++;
- break;
-
- case '?':
- default:
- usage(Progname);
- exit(1);
- }
- }
- if (optind >= argc) {
- usage(Progname);
- exit(1);
- }
- stage_dir = argv[optind];
- if (stage_dir == NULL) {
- usage(Progname);
- exit(1);
- }
-
- /*
- * Init the dmapi, and get a session.
- */
- error = setup_dmapi(&sid);
- if (error)
- exit(1);
-
- /*
- * Migrate all the files given to us via stdin
- */
- error = mig_files(sid, stage_dir);
-
-
- if (dm_destroy_session(sid))
- errno_msg("Can't shut down session, line=%d, errno=%d", __LINE__, errno);
-
- return(error);
-}
-
-/*
- * Migrate all the files given in stdin
- */
-
-int
-mig_files(
- dm_sessid_t sid,
- char *stage_dir)
-{
- void *hanp;
- size_t hlen;
- dm_size_t fsize;
- int error;
- u_int change_start, change_end;
- int stg_fd; /* staging file descriptor */
- dm_off_t off; /* starting offset */
- dm_token_t token; /* file token */
- char ibuf[IBUFSIZE];
- char handle_buf[HANDLE_LEN];
- char stgpath[MAXPATHLEN];
-
- /*
- * Read all the lines in std input and migrate each file.
- * This simple-minded migout does no batching, sorting, or
- * anything else that a real HSM might do.
- */
- while (fgets(ibuf, IBUFSIZE, stdin) != NULL) {
- error = extract_fields(ibuf, handle_buf, &hlen, &fsize);
- if (error) {
- err_msg("%s/%d: mangled input line, '%s' ", __FILE__, __LINE__, ibuf);
- continue;
- }
- hanp = (void *)handle_buf;
- if (Verbose) {
- print_handle(hanp, hlen);
- }
-
- /*
- * Create and open the file in the staging directory.
- */
- error = create_stgfile(stage_dir, hanp, hlen, stgpath, &stg_fd);
- if (error)
- continue;
-
- /*
- * Get the file's DMAPI change indicator so that we
- * can tell if it changed (either via a data mod, or
- * a DM attribute update) while we are staging it out
- */
- error = get_dmchange(sid, hanp, hlen, DM_NO_TOKEN,
- &change_start);
- if (error) {
- close(stg_fd);
- continue;
- }
-
- /*
- * Write all the file's data to our file in the
- * staging directory. In a real HSM, the data would
- * be copied off to tertiary storage somewhere.
- *
- * The staging file descriptor will be closed for us
- * in all cases.
- */
- error = save_filedata(sid, hanp, hlen, stg_fd, fsize);
- if (error)
- continue;
-
-
- /*
- * Get exclusive access to the file so we can blow
- * away its data
- */
- error = lock_file(sid, hanp, hlen, DM_RIGHT_EXCL, &token);
- if (error) {
- err_msg("Can't get exclusive access to file, ignoring");
- continue;
- }
-
- /*
- * Make sure the file did not change
- */
- error = get_dmchange(sid, hanp, hlen, token, &change_end);
- if (error) {
- unlock_file(sid, token);
- continue;
- }
- if (change_start != change_end) {
- unlock_file(sid, token);
- err_msg("File changed during stageout, ignoring");
- continue;
- }
-
- /*
- * Save the location of the data (the staging file)
- * in a private DM attribute so that we can get the
- * file back in the future
- */
- error = save_dataloc(sid, hanp, hlen, token, stgpath);
- if (error) {
- err_msg("Can't save location of file data");
- unlock_file(sid, token);
- continue;
- }
-
-
- /*
- * Set up the managed regions on the file so that
- * a foray past the fencepost will cause an event to
- * be generated.
- */
- error = set_mrgns(sid, hanp, hlen, token, fsize, &off);
- if (error) {
- unlock_file(sid, token);
- err_msg("Can't set managed regions");
- continue;
- }
-
- /*
- * Now we can safely blow away the data.
- */
- error = mk_nonrez(sid, hanp, hlen, token, off);
- if (error) {
- clear_mrgns(sid, hanp, hlen, token);
- }
-
- /*
- * Unlock the file, which releases the token
- */
- unlock_file(sid, token);
-
- }
-
- return(0);
-}
-
-
-/*
- * Remove the data for a file
- */
-int
-mk_nonrez(
- dm_sessid_t sid,
- void *hanp,
- size_t hlen,
- dm_token_t token,
- dm_off_t off)
-{
- int error;
-
- error = dm_punch_hole(sid, hanp, hlen, token, off, 0);
- if (error == -1) {
- errno_msg("Can't punch hole in file, line=%d, errno=%d", __LINE__, errno);
- return(1);
- }
- return(0);
-}
-
-
-/*
- * Set up the managed regions on a file. We try to leave some of the
- * file resident; the actual amount left on-disk is dependent
- * on the rounding enforced by the DMAPI implementation.
- */
-int
-set_mrgns(
- dm_sessid_t sid,
- void *hanp,
- size_t hlen,
- dm_token_t token,
- dm_off_t fsize,
- dm_off_t *start_off)
-{
- dm_off_t rroff;
- dm_size_t rlenp;
- dm_region_t rgn;
- u_int exact_flag;
- int error;
-
- if (fsize > FENCEPOST_SIZE) {
- error = dm_probe_hole(sid, hanp, hlen, token, FENCEPOST_SIZE, 0,
- &rroff, &rlenp);
- if (error == -1) {
- errno_msg("Can't probe hole in file, line=%d, errno=%d", __LINE__, errno);
- return(1);
- }
- } else {
- rroff = 0;
- }
- *start_off = rroff;
-
- /*
- * Now we know what the DMAPI and filesystem will support with
- * respect to rounding of holes. We try to set our managed region
- * to begin at this offset and continue to the end of the file.
- * We set the event mask so that we'll trap on all events that
- * occur in the managed region.
- *
- * Note that some implementations may not be able to support
- * a managed region that starts someplace other than the beginning
- * of the file. If we really cared, we could check the exact_flag.
- */
- rgn.rg_offset = rroff;
- rgn.rg_size = 0;
- rgn.rg_flags = DM_REGION_READ | DM_REGION_WRITE | DM_REGION_TRUNCATE;
- error = dm_set_region(sid, hanp, hlen, token, 1, &rgn, &exact_flag);
- if (error == -1) {
- errno_msg("Can't set managed region, line=%d, errno=%d", __LINE__, errno);
- return(1);
- }
- return(0);
-}
-
-
-/*
- * Clear a managed region on a file
- */
-void
-clear_mrgns(
- dm_sessid_t sid,
- void *hanp,
- dm_size_t hlen,
- dm_token_t token)
-{
- dm_region_t rgn;
- u_int exact_flag;
- int error;
-
- rgn.rg_offset = 0;
- rgn.rg_size = 0;
- rgn.rg_flags = DM_REGION_NOEVENT;
- error = dm_set_region(sid, hanp, hlen, token, 1, &rgn, &exact_flag);
- if (error)
- errno_msg("Can't clear managed regions from file, line=%d, errno=%d", __LINE__, errno);
-
- return;
-}
-
-
-/*
- * File rights are accessed via a token. The token must be associated
- * with a synchronous event message. So, to obtain either shared or
- * exclusive rights to a file, we first associate a token with a message,
- * and then request our desired right
- */
-int
-lock_file(
- dm_sessid_t sid,
- void *hanp,
- size_t hlen,
- dm_right_t right,
- dm_token_t *token)
-{
- int error;
-
- error = dm_create_userevent(sid, (size_t)0, (void *)0, token);
- if (error == -1) {
- errno_msg("Can't create user event for token context, line=%d, errno=%d", __LINE__, errno);
- return(1);
- }
- error = dm_request_right(sid, hanp, hlen, *token, DM_RR_WAIT, right);
- if (error == -1) {
- errno_msg("Can't get requested right for token, line=%d, errno=%d", __LINE__, errno);
- return(1);
- }
- return(0);
-}
-
-
-/*
- * Release the lock on a file
- */
-void
-unlock_file(
- dm_sessid_t sid,
- dm_token_t token)
-{
- int error;
-
- error = dm_respond_event(sid, token, DM_RESP_CONTINUE, 0, 0, 0);
- if (error == -1)
- errno_msg("Can't respond to event and release token, line=%d, errno=%d", __LINE__, errno);
-
- return;
-}
-
-
-int
-create_stgfile(
- char *stage_dir,
- void *hanp,
- size_t hlen,
- char *path,
- int *stg_fd)
-{
- char handle_str[HANDLE_STR];
-
- if (hlen > HANDLE_LEN) {
- err_msg("Handle length (%d) too long for file", hlen);
- return(1);
- }
-
- strcpy(path, stage_dir);
- strcat(path, "/");
- hantoa(hanp, hlen, handle_str);
-
- /*
- * Concat the ascii representation of the file handle
- * (which is two times longer than the binary version)
- * onto the staging path name
- */
- strncat(path, (char *)handle_str, hlen*2);
-
- if ( (*stg_fd = open(path, O_RDWR | O_CREAT, 0644)) < 0) {
- errno_msg("Can't open file %s, line=%d, errno=%d\n", path, __LINE__, errno);
- return(1);
- }
-
- return(0);
-}
-
-
-/*
- * Extract the three fields from the input line. THe input is of
- * the form
- * filehandle length filehandle file size
- *
- * The length of the file handle is expected to be less than 64 bytes.
- */
-int
-extract_fields(
- char *ibuf,
- char *handle_buf,
- size_t *hlen,
- dm_size_t *fsize)
-{
- char *cp, *start;
- size_t len;
- char *hanp;
- char *hanpp=NULL;
-
- /*
- * Skip any leading white space, and check the length
- * of the file handle
- */
- cp = ibuf;
- while (!isalnum(*cp))
- cp++;
-
- start = cp;
- while (isalnum(*cp))
- cp++;
- *cp = '\0';
-
- len = strtol(start, 0, 0);
- if (len > HANDLE_LEN) {
- err_msg("%s/%d: Handle length %d too long in input line", __FILE__, __LINE__, len);
- return(1);
- }
-
- *hlen = len;
-
- /*
- * Skip over white space, and extract the file handle
- */
- while (!isalnum(*cp))
- cp++;
- hanp = cp;
-
- /*
- * Skip over the ascii length of the file handle, and
- * then extract the file's length
- */
- cp += len*2;
- *cp = '\0';
-
- atohan( hanp, (void**)&hanpp, &len );
- memcpy( handle_buf, hanpp, len);
- free( hanpp );
-
- /* skip over white space */
- while (!isalnum(*cp))
- cp++;
-
- /* read file length */
- start = cp;
- while (isalnum(*cp))
- cp++;
- *cp = '\0';
-
- *fsize = strtol(start, 0, 0);
-
- return(0);
-
-}
-
-
-/*
- * Save the location of the file's data so that we can find
- * it again when we're staging the file back in. Rather than store
- * the full pathname of the staging file, we just store the handle.
- * This means the staging dir must be on a filesystem that supports
- * the DMAPI.
- */
-int
-save_dataloc(
- dm_sessid_t sid,
- void *hanp,
- size_t hlen,
- dm_token_t token,
- char *stgpath)
-{
- void *stg_hanp;
- size_t stg_hlen;
- int error;
- dm_attrname_t hanp_attrname;
- dm_attrname_t hlen_attrname;
-
- if (dm_path_to_handle(stgpath, &stg_hanp, &stg_hlen) == -1) {
- errno_msg("Can't get handle for path %s, line=%d, errno=%d", stgpath, __LINE__, errno);
- return(1);
- }
-
- /*
- * Since handles are in two parts, they become two attributes.
- * This can be useful, since we can extract the length
- * separately when we stage the file back in
- */
- memcpy((void *)&hanp_attrname.an_chars[0], DLOC_HAN, DM_ATTR_NAME_SIZE);
- error = dm_set_dmattr(sid, hanp, hlen, token, &hanp_attrname,
- 0, stg_hlen, stg_hanp);
- if (error == -1) {
- errno_msg("Can't set DM attr of staging filehandle, line=%d, errno=%d",__LINE__, errno);
- return(1);
- }
-
- memcpy((void *)&hlen_attrname.an_chars[0], DLOC_HANLEN, DM_ATTR_NAME_SIZE);
- error = dm_set_dmattr(sid, hanp, hlen, token, &hlen_attrname,
- 0, sizeof(stg_hlen), (void *)&stg_hlen);
- if (error == -1) {
- errno_msg("Can't set DM attr of staging filehandle length, line=%d, errno=%d", __LINE__, errno);
- return(1);
- }
- return(0);
-}