check: allow '-e testid' to exclude a single test
[xfstests-dev.git] / dmapi / src / sample_hsm / wbee.c
1 /*
2  * Worker bees.
3  *
4  * The bees perform the grunt work of handling a file event
5  *
6  * This code was written by Peter Lawthers, and placed in the public
7  * domain for use by DMAPI implementors and app writers.
8  *
9  * Standard disclaimer:
10  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
11  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
12  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
13  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
14  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
15  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
16  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
17  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
18  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
19  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
20  * SUCH DAMAGE.
21  */
22
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <sys/time.h>
26 #include <sys/resource.h>
27
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <fcntl.h>
31 #include <string.h>
32
33 #include <lib/hsm.h>
34
35 extern char     *optarg;
36 extern int       optind, optopt, opterr;
37 extern int       errno;
38 char            *Progname;
39 int              Verbose;
40
41 extern int       restore_filedata(dm_sessid_t, void *, size_t, dm_token_t, 
42                                 void *, size_t, dm_off_t);
43 extern int       get_dmchange(dm_sessid_t, void *, size_t, dm_token_t, u_int *);
44 extern int       setup_dmapi(dm_sessid_t *);
45 extern void      err_msg(char *, ...);
46 extern void      errno_msg(char *, ...);
47
48 int     stagein_file(dm_sessid_t, dm_token_t, dm_eventmsg_t *);
49 int     inval_file(dm_sessid_t, dm_token_t, dm_eventmsg_t *);
50 int     check_lockstate(dm_sessid_t, void *, size_t, dm_token_t);
51 int     clear_mrgns(dm_sessid_t, void *, size_t, dm_token_t);
52 int     find_msg(dm_sessid_t, dm_token_t, dm_eventmsg_t **);
53 int     get_stghandle(dm_sessid_t, void *, size_t, dm_token_t, void **, 
54                         size_t *);
55 void    usage(char *);
56
57
58
59 void
60 usage(
61         char *prog)
62 {
63         fprintf(stderr, "Usage: %s ", prog);
64         fprintf(stderr, " <-i invalidate file> ");
65         fprintf(stderr, " <-r restore file> ");
66         fprintf(stderr, "[-s sid] [-t token] \n");
67 }
68
69
70 int
71 main(
72         int     argc, 
73         char    *argv[])
74 {
75         
76         dm_eventmsg_t   *msgheader;
77         char            *sid_str, *token_str;
78         dm_sessid_t      sid;
79         dm_token_t       token;
80         int              c;
81         int              error;
82         int              restore_flag, inval_flag;
83         char            *cp;
84
85         Progname  = argv[0];
86         sid_str   = NULL;
87         token_str = NULL;
88         restore_flag = 0;
89         inval_flag   = 0;
90
91         while ((c = getopt(argc, argv, "s:t:ri")) != EOF) {
92                 switch (c) {
93                 case 'r':
94                         restore_flag++;
95                         break;
96
97                 case 'i':
98                         inval_flag++;
99                         break;
100
101                 case 's':
102                         sid_str = optarg;
103                         break;
104
105                 case 't':
106                         token_str = optarg;
107                         break;
108
109                 case '?':
110                 default:
111                         usage(Progname);
112                         exit(1);
113                 }
114         }
115         if (optind < argc) {
116                 usage(Progname);
117                 exit(1);
118         }
119         if (sid_str == NULL || token_str == NULL) {
120                 usage(Progname);
121                 exit(1);
122         }
123         if ((restore_flag > 0) && (inval_flag > 0)) {
124                 usage(Progname);
125                 exit(1);
126         }
127
128         if (sscanf(sid_str, "%d", &sid) <= 0) {
129                 err_msg("Can't convert sid");
130                 exit(1);
131         }
132         if (sscanf(token_str, "%d", &token) <= 0) {
133                 err_msg("Can't convert token");
134                 exit(1);
135         }
136
137         /*
138          * Now we have our session and token. We just need to
139          * let the DMAPI know we exist so we can use the interface.
140          * We don't need to create a session since we'll be using
141          * the session of our parent.
142          */
143         error = dm_init_service(&cp);
144         if (error == -1) {
145                 errno_msg("Can't init DMAPI");
146                 exit(1);
147         }
148         if (strcmp(cp, DM_VER_STR_CONTENTS)) {
149                 err_msg("Compiled for a different version");
150                 exit(1);
151         }
152         
153         /*
154          * Find the message our caller wants us to handle
155          */
156         error = find_msg(sid, token, &msgheader);
157         if (error) 
158                 exit(1);
159
160         /*
161          * Now service the particular event type
162          */
163         if (restore_flag) 
164                 error = stagein_file(sid, token, msgheader);
165         else
166                 error = inval_file(sid, token, msgheader);
167
168         return(error);
169 }
170
171
172 /*
173  * Find the data event message that correponds to the token.
174  *
175  * RETURNS:
176  *      A pointer to malloc'd memory that contains the message
177  *      we're supposed to handle in the 'msgheader' param.
178  */
179 int
180 find_msg(
181         dm_sessid_t       sid,
182         dm_token_t        token,
183         dm_eventmsg_t   **msgheader)
184 {
185         void    *buf;
186         size_t   buflen, rlen;
187         int      error;
188
189         /*
190          * Malloc a buffer that we think is large enough for
191          * the common message header and the event specific part. 
192          * If it's not large enough, we can always resize it.
193          */
194         buflen = sizeof(dm_eventmsg_t) + sizeof(dm_data_event_t) + HANDLE_LEN;
195         buf = (void *)malloc(buflen);
196         if (buf == NULL) {
197                 err_msg("Can't alloc memory for event buffer");
198                 return(1);
199         }
200
201         error = dm_find_eventmsg(sid, token, buflen, buf, &rlen);
202         if (error == -1) {
203                 if (errno != E2BIG) {
204                         free(buf);
205                         errno_msg("Can't obtain message from token");
206                         return(1);
207                 }
208                 free(buf);
209                 buflen = rlen;
210                 buf = (void *)malloc(buflen);
211                 if (buf == NULL) {
212                         err_msg("Can't resize event buffer");
213                         return(1);
214                 }
215                 error = dm_find_eventmsg(sid, token, buflen, buf, &rlen);
216                 if (error == -1) {
217                         errno_msg("Can't get message with resized buffer");
218                         return(1);
219                 }
220         }
221         
222         *msgheader = (dm_eventmsg_t *)buf;
223         return(0);
224 }
225
226
227 /*
228  * Check the lock state associated with the file. If the token
229  * does not reference exclusive access, try to upgrade our lock.
230  * If we can't upgrade, drop the lock and start over
231  */
232 int
233 check_lockstate(
234         dm_sessid_t      sid, 
235         void            *hanp, 
236         size_t           hlen, 
237         dm_token_t       token)
238 {
239         int             error;
240         dm_right_t      right;
241         u_int           change_start, change_end;
242
243         error = dm_query_right(sid, hanp, hlen, token, &right);
244         if (error == -1) {
245                 errno_msg("Can't query file access rights");
246                 return(1);
247         }
248 #if defined(linux)
249         /*
250          * There are no access rights on the SGI. 1 means it's
251          * there.
252          */
253         if (right == DM_RIGHT_SHARED)
254                 return 0;
255 #endif
256
257         if (right != DM_RIGHT_EXCL) {
258                 error = dm_request_right(sid, hanp, hlen, token, 0,
259                                          DM_RIGHT_EXCL);
260                 if (error == -1) {
261                         if (errno != EAGAIN) {
262                                 errno_msg("Can't upgrade lock");
263                                 return(1);
264                         }
265                         error = get_dmchange(sid, hanp, hlen, token, 
266                                                 &change_start);
267                         if (error) 
268                                 return(1);
269                                 
270                         
271                         error = dm_release_right(sid, hanp, hlen, token);
272                         if (error == -1) {
273                                 errno_msg("Can't release file access rights");
274                                 return(1);
275                         }
276                         error = dm_request_right(sid, hanp, hlen, token, 
277                                                 DM_RR_WAIT, DM_RIGHT_EXCL);
278                         if (error == -1)  {
279                                 errno_msg("Can't get exclusive right to file");
280                                 return(1);
281                         }
282
283                         /*
284                          * If the file changed while we slept, then someone
285                          * must have modified the file
286                          */
287                         error = get_dmchange(sid, hanp, hlen, token, 
288                                                 &change_end);
289                         if (error == -1) 
290                                 return(1);
291                         
292                         if (change_start != change_end) {
293                                 err_msg("File changed while waiting for lock");
294                                 return(1);
295                         }
296                 }
297         }
298         return(0);
299 }
300
301
302 /*
303  * Stage in the data for a file
304  */
305 int
306 stagein_file(
307         dm_sessid_t      sid, 
308         dm_token_t       token,
309         dm_eventmsg_t   *msgheader)
310 {
311
312         void            *stg_hanp, *hanp;
313         size_t           stg_hlen, hlen;
314         int              error, ret_errno;
315         dm_response_t    reply;
316         dm_data_event_t *msg;
317
318         /*
319          * Extract the event-specific info from the message header,
320          * then get the file handle.
321          */
322         msg  = DM_GET_VALUE(msgheader, ev_data, dm_data_event_t *);
323         hanp = DM_GET_VALUE(msg, de_handle, void *);
324         hlen = DM_GET_LEN(msg, de_handle);
325
326         /*
327          * Check our permissions. We need exclusive access to the 
328          * file to stage it back in.
329          */
330         error = check_lockstate(sid, hanp, hlen, token);
331         if (error) 
332                 goto out;
333
334         /*
335          * get the staging file handle from it's DM attributes
336          */
337         stg_hanp = NULL;
338         error = get_stghandle(sid, hanp, hlen, token, &stg_hanp, &stg_hlen);
339         if (error) 
340                 goto out;
341
342         /*
343          * We keep the exclusive lock held for the *entire* duration
344          * of the stagein. This is not required, but just quick and
345          * [sl]easy. For a large file, it is typically better to release
346          * the lock, and have a sliding window of managed regions to allow
347          * people to consume the data as it is being read in.
348          */
349         error = restore_filedata(sid, hanp, hlen, token, stg_hanp, stg_hlen,
350                            msg->de_offset);
351         if (error) 
352                 goto out;
353
354         /*
355          * Now that the data is restored, and while we still have exclusive
356          * access to the file, clear the managed regions.
357          */
358         error = clear_mrgns(sid, hanp, hlen, token);
359         if (error) 
360                 goto out;
361
362 out:
363         if (stg_hanp)
364                 free((char *)stg_hanp);
365
366         /*
367          * Figure out what our response to the event will be. Once
368          * we've responded to the event, the token is no longer valid.
369          * On error, we pick the (less than helpful) errno EIO to signal
370          * to the user that something went wrong.
371          */
372         if (error) {
373                 reply = DM_RESP_ABORT;
374                 ret_errno = EIO;
375         } else {
376                 reply = DM_RESP_CONTINUE;
377                 ret_errno = 0;
378         }
379         (void)dm_respond_event(sid, token, reply, ret_errno, 0, 0);
380
381         return(ret_errno);
382
383 }
384
385 /*
386  * Turn off event monitoring for a file. In a real HSM, we would
387  * probably want to either invalidate the file's data on 
388  * tertiary storage, or start some aging process so that it will
389  * eventually go away.
390  *
391  * The assumption is that for write and truncate events, the file
392  * data is about to be invalidated.
393  */
394 int
395 inval_file(
396         dm_sessid_t      sid, 
397         dm_token_t       token,
398         dm_eventmsg_t   *msgheader)
399 {
400         dm_data_event_t *msg;
401         void            *hanp;
402         size_t           hlen;
403         int              error, ret_errno;
404         dm_response_t    reply;
405
406         /*
407          * Extract the event-specific info from the message header,
408          * then get the file handle.
409          */
410         msg  = DM_GET_VALUE(msgheader, ev_data, dm_data_event_t *);
411         hanp = DM_GET_VALUE(msg, de_handle, void *);
412         hlen = DM_GET_LEN(msg, de_handle);
413
414         /*
415          * Check our permissions. We need exclusive access to the 
416          * file to clear our managed regions.
417          */
418         error = check_lockstate(sid, hanp, hlen, token);
419         if (error) 
420                 goto out;
421
422         /*
423          * Clear all the managed regions for the file.
424          */
425         error = clear_mrgns(sid, hanp, hlen, token);
426
427 out:
428         /*
429          * Figure out what our response to the event will be. Once
430          * we've responded to the event, the token is no longer valid.
431          * On error, we pick the (less than helpful) errno EIO to signal
432          * to the user that something went wrong.
433          */
434         if (error) {
435                 reply = DM_RESP_ABORT;
436                 ret_errno = EIO;
437         } else {
438                 reply = DM_RESP_CONTINUE;
439                 ret_errno = 0;
440         }
441         (void)dm_respond_event(sid, token, reply, ret_errno, 0, 0);
442         
443         return(ret_errno);
444 }
445
446 /*
447  * Clear all of the managed regions for a file. 
448  */
449 int
450 clear_mrgns(
451         dm_sessid_t      sid, 
452         void            *hanp, 
453         size_t           hlen, 
454         dm_token_t       token)
455 {
456         dm_region_t     *rgn, *sv_rgn;
457         u_int            nregions, nret;
458         u_int            exact_flag;
459         int              i;
460         int              error, retval;
461
462         /*
463          * We take a guess first and assume there is only one managed
464          * region per file. There should'nt be more than this, but
465          * it never hurts to check, since we want to make sure that
466          * all regions are turned off.
467          *
468          * The main purpose of this is to demonstrate the use of the 
469          * E2BIG paradigm.
470          */
471         retval = 1;
472         nregions = 1;
473         rgn = (dm_region_t *)malloc(nregions * sizeof(dm_region_t));
474         if (rgn == NULL) {
475                 err_msg("Can't allocate memory for region buffers");
476                 goto out;
477         }
478
479         error = dm_get_region(sid, hanp, hlen, token, nregions, rgn, &nret);
480         if (error == -1) {
481                 if (errno != E2BIG) {
482                         errno_msg("Can't get list of managed regions for file");
483                         goto out;
484                 }
485
486                 /*
487                  * Now we know how many managed regions there are, so we can
488                  * resize our buffer
489                  */
490                 nregions = nret;
491                 free(rgn);
492                 rgn = (dm_region_t *)malloc(nregions * sizeof(dm_region_t));
493                 if (rgn == NULL) {
494                         err_msg("Can't resize region buffers");
495                         goto out;
496                 }
497                 error = dm_get_region(sid, hanp, hlen, token, nregions, rgn, 
498                                         &nret);
499                 if (error == -1) {
500                         errno_msg("Can't get list of managed regions for file");
501                         goto out;
502                 }
503         }
504
505         sv_rgn = rgn;
506
507         /*
508          * Clear all the managed regions
509          */
510         for (i=0; i<nregions; i++) {
511                 rgn->rg_offset = 0;
512                 rgn->rg_size   = 0;
513                 rgn->rg_flags  = DM_REGION_NOEVENT;
514                 rgn++;
515         }
516         rgn = sv_rgn;
517
518         error = dm_set_region(sid, hanp, hlen, token, nregions, rgn, 
519                                 &exact_flag);
520         if (error == -1) {
521                 errno_msg("Can't clear list of managed regions for file");
522         }
523         retval = 0;
524
525 out:
526         if (rgn != NULL) 
527                 free(rgn);
528
529         return(retval);
530 }
531
532
533 /*
534  * Extract the staging file handle from a file's DM attributes
535  */
536 int
537 get_stghandle(
538         dm_sessid_t       sid, 
539         void             *hanp, 
540         size_t            hlen, 
541         dm_token_t        token,
542         void            **stg_hanp,
543         size_t           *stg_hlen)
544 {
545         void            *han_buf;
546         size_t           han_len;
547         int              error;
548         size_t           rlen;
549         dm_attrname_t    hanp_attrname;
550         dm_attrname_t    hlen_attrname;
551
552         /*
553          * First get the length of the file handle, so we
554          * can size our buffer correctly
555          */
556         memcpy((void *)&hlen_attrname.an_chars[0], DLOC_HANLEN, DM_ATTR_NAME_SIZE);
557         error = dm_get_dmattr(sid, hanp, hlen, token, &hlen_attrname,
558                                 sizeof(size_t), &han_len, &rlen);
559         if (error == -1) {
560                 /*
561                  * On any error, even E2BIG, we bail since the size of
562                  * the file handle should be a constant
563                  */
564                 errno_msg("Can't get size of staging file handle");
565                 return(1);
566         }
567         if (rlen != sizeof(size_t)) {
568                 err_msg("File handle length component incorrect");
569                 return(1);
570         }
571
572         /*
573          * Malloc space for our staging file handle, and 
574          * extract it from our DM attributes
575          */
576         han_buf = (void *)malloc(han_len);
577         if (han_buf == NULL) {
578                 err_msg("Can't alloc memory for file handle");
579                 return(1);
580         }
581
582         memcpy((void *)&hanp_attrname.an_chars[0], DLOC_HAN, DM_ATTR_NAME_SIZE);
583         error = dm_get_dmattr(sid, hanp, hlen, token, &hanp_attrname, 
584                                 han_len, han_buf, &rlen);
585         if (error == -1) {
586                 errno_msg("Can't get staging file handle");
587                 free(han_buf);
588                 return(1);
589         }
590         if (rlen != han_len) {
591                 err_msg("File handle is incorrect length");
592                 free(han_buf);
593                 return(1);
594         }
595         *stg_hanp = han_buf;
596         *stg_hlen = han_len;
597         return(0);
598 }
599