2 * Simple utility to migrate a group of specified files.
3 * The unsorted input is from migfind, and is of the form:
4 * filehandle length filehandle file size
6 * The data for each file will be stored in another file located
7 * in a different directory. This 'staging directory' should be on
8 * another filesystem. The staging file will be named the same as
9 * the file handle. This simple-minded scheme suffices, since we're
10 * not interested in showing any media management in this example.
13 * Persistent managed regions are supported
14 * Persistent DM attributes are supported
17 * This code was written by Peter Lawthers, and placed in the public
18 * domain for use by DMAPI implementors and app writers.
20 * Standard disclaimer:
21 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include <sys/types.h>
35 #include <sys/param.h>
46 extern int optind, optopt, opterr;
50 int mig_files(dm_sessid_t, char *);
51 int mk_nonrez(dm_sessid_t, void *, size_t, dm_token_t, dm_off_t);
52 int set_mrgns(dm_sessid_t, void *, size_t, dm_token_t, dm_off_t,
54 void clear_mrgns(dm_sessid_t, void *, dm_size_t, dm_token_t);
55 int lock_file(dm_sessid_t, void *, size_t, dm_right_t, dm_token_t *);
56 void unlock_file(dm_sessid_t, dm_token_t);
57 int get_dmchange(dm_sessid_t, void *, size_t, dm_token_t, u_int *);
58 int create_stgfile(char *, void *, size_t, char *, int *);
59 int setup_dmapi(dm_sessid_t *);
60 int save_filedata(dm_sessid_t, void *, size_t, int, dm_size_t);
61 int extract_fields(char *, char *, size_t *, dm_size_t *);
62 int save_dataloc(dm_sessid_t, void *, size_t, dm_token_t, char *);
70 fprintf(stderr, "Usage: %s ", prog);
71 fprintf(stderr, " <-v verbose> ");
72 fprintf(stderr, "<staging directory>\n");
92 while ((c = getopt(argc, argv, "v")) != EOF) {
104 if (optind >= argc) {
108 stage_dir = argv[optind];
109 if (stage_dir == NULL) {
115 * Init the dmapi, and get a session.
117 error = setup_dmapi(&sid);
122 * Migrate all the files given to us via stdin
124 error = mig_files(sid, stage_dir);
127 if (dm_destroy_session(sid))
128 errno_msg("Can't shut down session, line=%d, errno=%d", __LINE__, errno);
134 * Migrate all the files given in stdin
146 u_int change_start, change_end;
147 int stg_fd; /* staging file descriptor */
148 dm_off_t off; /* starting offset */
149 dm_token_t token; /* file token */
151 char handle_buf[HANDLE_LEN];
152 char stgpath[MAXPATHLEN];
155 * Read all the lines in std input and migrate each file.
156 * This simple-minded migout does no batching, sorting, or
157 * anything else that a real HSM might do.
159 while (fgets(ibuf, IBUFSIZE, stdin) != NULL) {
160 error = extract_fields(ibuf, handle_buf, &hlen, &fsize);
162 err_msg("%s/%d: mangled input line, '%s' ", __FILE__, __LINE__, ibuf);
165 hanp = (void *)handle_buf;
167 print_handle(hanp, hlen);
171 * Create and open the file in the staging directory.
173 error = create_stgfile(stage_dir, hanp, hlen, stgpath, &stg_fd);
178 * Get the file's DMAPI change indicator so that we
179 * can tell if it changed (either via a data mod, or
180 * a DM attribute update) while we are staging it out
182 error = get_dmchange(sid, hanp, hlen, DM_NO_TOKEN,
190 * Write all the file's data to our file in the
191 * staging directory. In a real HSM, the data would
192 * be copied off to tertiary storage somewhere.
194 * The staging file descriptor will be closed for us
197 error = save_filedata(sid, hanp, hlen, stg_fd, fsize);
203 * Get exclusive access to the file so we can blow
206 error = lock_file(sid, hanp, hlen, DM_RIGHT_EXCL, &token);
208 err_msg("Can't get exclusive access to file, ignoring");
213 * Make sure the file did not change
215 error = get_dmchange(sid, hanp, hlen, token, &change_end);
217 unlock_file(sid, token);
220 if (change_start != change_end) {
221 unlock_file(sid, token);
222 err_msg("File changed during stageout, ignoring");
227 * Save the location of the data (the staging file)
228 * in a private DM attribute so that we can get the
229 * file back in the future
231 error = save_dataloc(sid, hanp, hlen, token, stgpath);
233 err_msg("Can't save location of file data");
234 unlock_file(sid, token);
240 * Set up the managed regions on the file so that
241 * a foray past the fencepost will cause an event to
244 error = set_mrgns(sid, hanp, hlen, token, fsize, &off);
246 unlock_file(sid, token);
247 err_msg("Can't set managed regions");
252 * Now we can safely blow away the data.
254 error = mk_nonrez(sid, hanp, hlen, token, off);
256 clear_mrgns(sid, hanp, hlen, token);
260 * Unlock the file, which releases the token
262 unlock_file(sid, token);
271 * Remove the data for a file
283 error = dm_punch_hole(sid, hanp, hlen, token, off, 0);
285 errno_msg("Can't punch hole in file, line=%d, errno=%d", __LINE__, errno);
293 * Set up the managed regions on a file. We try to leave some of the
294 * file resident; the actual amount left on-disk is dependent
295 * on the rounding enforced by the DMAPI implementation.
312 if (fsize > FENCEPOST_SIZE) {
313 error = dm_probe_hole(sid, hanp, hlen, token, FENCEPOST_SIZE, 0,
316 errno_msg("Can't probe hole in file, line=%d, errno=%d", __LINE__, errno);
325 * Now we know what the DMAPI and filesystem will support with
326 * respect to rounding of holes. We try to set our managed region
327 * to begin at this offset and continue to the end of the file.
328 * We set the event mask so that we'll trap on all events that
329 * occur in the managed region.
331 * Note that some implementations may not be able to support
332 * a managed region that starts someplace other than the beginning
333 * of the file. If we really cared, we could check the exact_flag.
335 rgn.rg_offset = rroff;
337 rgn.rg_flags = DM_REGION_READ | DM_REGION_WRITE | DM_REGION_TRUNCATE;
338 error = dm_set_region(sid, hanp, hlen, token, 1, &rgn, &exact_flag);
340 errno_msg("Can't set managed region, line=%d, errno=%d", __LINE__, errno);
348 * Clear a managed region on a file
363 rgn.rg_flags = DM_REGION_NOEVENT;
364 error = dm_set_region(sid, hanp, hlen, token, 1, &rgn, &exact_flag);
366 errno_msg("Can't clear managed regions from file, line=%d, errno=%d", __LINE__, errno);
373 * File rights are accessed via a token. The token must be associated
374 * with a synchronous event message. So, to obtain either shared or
375 * exclusive rights to a file, we first associate a token with a message,
376 * and then request our desired right
388 error = dm_create_userevent(sid, (size_t)0, (void *)0, token);
390 errno_msg("Can't create user event for token context, line=%d, errno=%d", __LINE__, errno);
393 error = dm_request_right(sid, hanp, hlen, *token, DM_RR_WAIT, right);
395 errno_msg("Can't get requested right for token, line=%d, errno=%d", __LINE__, errno);
403 * Release the lock on a file
412 error = dm_respond_event(sid, token, DM_RESP_CONTINUE, 0, 0, 0);
414 errno_msg("Can't respond to event and release token, line=%d, errno=%d", __LINE__, errno);
428 char handle_str[HANDLE_STR];
430 if (hlen > HANDLE_LEN) {
431 err_msg("Handle length (%d) too long for file", hlen);
435 strcpy(path, stage_dir);
437 hantoa(hanp, hlen, handle_str);
440 * Concat the ascii representation of the file handle
441 * (which is two times longer than the binary version)
442 * onto the staging path name
444 strncat(path, (char *)handle_str, hlen*2);
446 if ( (*stg_fd = open(path, O_RDWR | O_CREAT, 0644)) < 0) {
447 errno_msg("Can't open file %s, line=%d, errno=%d\n", path, __LINE__, errno);
456 * Extract the three fields from the input line. THe input is of
458 * filehandle length filehandle file size
460 * The length of the file handle is expected to be less than 64 bytes.
475 * Skip any leading white space, and check the length
479 while (!isalnum(*cp))
487 len = strtol(start, 0, 0);
488 if (len > HANDLE_LEN) {
489 err_msg("%s/%d: Handle length %d too long in input line", __FILE__, __LINE__, len);
496 * Skip over white space, and extract the file handle
498 while (!isalnum(*cp))
503 * Skip over the ascii length of the file handle, and
504 * then extract the file's length
509 atohan( hanp, (void**)&hanpp, &len );
510 memcpy( handle_buf, hanpp, len);
513 /* skip over white space */
514 while (!isalnum(*cp))
517 /* read file length */
523 *fsize = strtol(start, 0, 0);
531 * Save the location of the file's data so that we can find
532 * it again when we're staging the file back in. Rather than store
533 * the full pathname of the staging file, we just store the handle.
534 * This means the staging dir must be on a filesystem that supports
548 dm_attrname_t hanp_attrname;
549 dm_attrname_t hlen_attrname;
551 if (dm_path_to_handle(stgpath, &stg_hanp, &stg_hlen) == -1) {
552 errno_msg("Can't get handle for path %s, line=%d, errno=%d", stgpath, __LINE__, errno);
557 * Since handles are in two parts, they become two attributes.
558 * This can be useful, since we can extract the length
559 * separately when we stage the file back in
561 memcpy((void *)&hanp_attrname.an_chars[0], DLOC_HAN, DM_ATTR_NAME_SIZE);
562 error = dm_set_dmattr(sid, hanp, hlen, token, &hanp_attrname,
563 0, stg_hlen, stg_hanp);
565 errno_msg("Can't set DM attr of staging filehandle, line=%d, errno=%d",__LINE__, errno);
569 memcpy((void *)&hlen_attrname.an_chars[0], DLOC_HANLEN, DM_ATTR_NAME_SIZE);
570 error = dm_set_dmattr(sid, hanp, hlen, token, &hlen_attrname,
571 0, sizeof(stg_hlen), (void *)&stg_hlen);
573 errno_msg("Can't set DM attr of staging filehandle length, line=%d, errno=%d", __LINE__, errno);