root/trunk/src/server/unstuff.sm @ 8506

Revision 8506, 13.0 KB (checked in by bligon, 3 years ago)

Changed the ".perms" value for the unstuff state machine from PINT_SERVER_CHECK_WRITE to
PINT_SERVER_CHECK_ATTR. The CHECK_WRITE perms value caused the unstuff machine to check
file permissions for the user, even when the user was the owner. This security check
caused a problem with the "tar" command when it tried to untar a file having only
444 permissions on it. The "tar" command used the system call
open(<filename>,O_CREAT|O_WRONLY|O_EXCL,444). According to the Linux man page, this
combination of open parameters should be allowable. ext3, ext4, and qfs all
allowed this combination. NOTE: PINT_SERVER_CHECK_ATTR allows the owner of the
file to perform an unstuff, no file permissions are checked. If the user is not
the owner, then file permissions are checked.

Line 
1/*
2 * (C) 2001 Clemson University and The University of Chicago
3 *
4 * See COPYING in top-level directory.
5 *
6 * Changes by Acxiom Corporation to add dirent_count field to attributes
7 * Copyright © Acxiom Corporation, 2005.
8 */
9
10#include <string.h>
11#include <assert.h>
12
13#include "server-config.h"
14#include "pvfs2-server.h"
15#include "pvfs2-attr.h"
16#include "pvfs2-types.h"
17#include "pvfs2-types-debug.h"
18#include "pvfs2-util.h"
19#include "pint-util.h"
20#include "pvfs2-internal.h"
21#include "pint-cached-config.h"
22
23#define STATE_UNSTUFF 33
24
25%%
26
27machine pvfs2_unstuff_sm
28{
29    state prelude
30    {
31        jump pvfs2_prelude_sm;
32        success => getattr_setup;
33        default => final_response;
34    }
35
36    state getattr_setup
37    {
38        run getattr_setup;
39        success => getattr_do_work;
40        default => final_response;
41    }
42
43    state getattr_do_work
44    {
45        jump pvfs2_get_attr_work_sm;
46        default => getattr_interpret;
47    }
48
49    state getattr_interpret
50    {
51        run getattr_interpret;
52        STATE_UNSTUFF => get_keyvals;
53        default => final_response;
54    }
55
56    state get_keyvals
57    {
58        run get_keyvals;
59        success => inspect_keyvals;
60        default => final_response;
61    }
62
63    state inspect_keyvals
64    {
65        run inspect_keyvals;
66        success => get_handles;
67        default => final_response;
68    }
69
70    state get_handles
71    {
72        run get_handles;
73        default => set_handles_on_object;
74    }
75
76    state set_handles_on_object
77    {
78        run set_handles_on_object;
79        success => update_dfile_count;
80        default => final_response;
81    }
82
83    state update_dfile_count
84    {
85        run update_dfile_count;
86        success => remove_layout;
87        default => final_response;
88    }
89
90    state remove_layout
91    {
92        run remove_layout;
93        default => final_response;
94    }
95
96    state final_response
97    {
98        jump pvfs2_final_response_sm;
99        default => cleanup;
100    }
101
102    state cleanup
103    {
104        run cleanup;
105        default => terminate;
106    }
107}
108
109%%
110
111static PINT_sm_action get_keyvals(
112        struct PINT_smcb *smcb, job_status_s *js_p)
113{
114    struct PINT_server_op *s_op = PINT_sm_frame(smcb, PINT_FRAME_CURRENT);
115    int kind = 0;
116    int ret;
117    job_id_t job_id;
118
119    s_op->keyval_count = 2;
120    s_op->key_a = malloc(sizeof(*s_op->key_a) * s_op->keyval_count);
121    if(!s_op->key_a)
122    {
123        js_p->error_code = -PVFS_ENOMEM;
124        goto error_exit;
125    }
126
127    s_op->val_a = malloc(sizeof(*s_op->val_a) * s_op->keyval_count);
128    if(!s_op->val_a)
129    {
130        js_p->error_code = -PVFS_ENOMEM;
131        goto free_key_array;
132    }
133
134    s_op->error_a = malloc(sizeof(*s_op->error_a) * s_op->keyval_count);
135    if(!s_op->error_a)
136    {
137        js_p->error_code = -PVFS_ENOMEM;
138        goto free_val_array;
139    }
140    memset(s_op->error_a, 0, sizeof(*s_op->error_a) * s_op->keyval_count);
141
142    s_op->u.unstuff.encoded_layout = malloc(PVFS_REQ_LIMIT_LAYOUT);
143    if(!s_op->u.unstuff.encoded_layout)
144    {
145        js_p->error_code = -PVFS_ENOMEM;
146        goto free_encoded_layout;
147    }
148    memset(s_op->u.unstuff.encoded_layout, 0, PVFS_REQ_LIMIT_LAYOUT);
149
150    /* kind = 0 */
151    s_op->key_a[kind].buffer = Trove_Common_Keys[METAFILE_LAYOUT_KEY].key;
152    s_op->key_a[kind].buffer_sz = Trove_Common_Keys[METAFILE_LAYOUT_KEY].size;
153
154    s_op->val_a[kind].buffer = s_op->u.unstuff.encoded_layout;
155    s_op->val_a[kind].buffer_sz = PVFS_REQ_LIMIT_LAYOUT;
156
157    ++kind;
158    /* kind = 1 */
159    s_op->key_a[kind].buffer = Trove_Common_Keys[NUM_DFILES_REQ_KEY].key;
160    s_op->key_a[kind].buffer_sz = Trove_Common_Keys[NUM_DFILES_REQ_KEY].size;
161
162    s_op->val_a[kind].buffer = &s_op->u.unstuff.num_dfiles_req;
163    s_op->val_a[kind].buffer_sz = sizeof(s_op->u.unstuff.num_dfiles_req);
164
165    ret = job_trove_keyval_read_list(
166        s_op->req->u.unstuff.fs_id,
167        s_op->req->u.unstuff.handle,
168        s_op->key_a, s_op->val_a, s_op->error_a, s_op->keyval_count,
169        0, NULL, smcb, 0, js_p, &job_id, server_job_context,
170        s_op->req->hints);
171    return ret;
172
173free_encoded_layout:
174    free(s_op->u.unstuff.encoded_layout);
175    s_op->u.unstuff.encoded_layout = NULL;
176free_val_array:
177    free(s_op->val_a);
178    s_op->val_a = NULL;
179free_key_array:
180    free(s_op->key_a);
181    s_op->key_a = NULL;
182error_exit:
183    return SM_ACTION_COMPLETE;
184}
185
186static PINT_sm_action inspect_keyvals(
187        struct PINT_smcb *smcb, job_status_s *js_p)
188{
189    struct PINT_server_op *s_op = PINT_sm_frame(smcb, PINT_FRAME_CURRENT);
190    char* tmpbuf;
191
192    if(js_p->error_code == 0)
193    {
194        /* check keys; we have a big problem if one of them is missing */
195        if(s_op->error_a[0])
196        {
197            js_p->error_code = s_op->error_a[0];
198        }
199        else if(s_op->error_a[1])
200        {
201            js_p->error_code = s_op->error_a[1];
202        }
203
204        if(js_p->error_code == 0)
205        {
206       
207            /* sanity check num dfiles */
208            if(s_op->u.unstuff.num_dfiles_req < 1)
209            {
210                js_p->error_code = -PVFS_EINVAL;
211            }
212
213            /* decode layout information */
214            tmpbuf = s_op->u.unstuff.encoded_layout;
215            decode_PVFS_sys_layout(&tmpbuf, &s_op->u.unstuff.layout);
216        }
217    }
218
219    /* pass along error code for next state to handle */
220    return SM_ACTION_COMPLETE;
221}
222
223static PINT_sm_action get_handles(
224        struct PINT_smcb *smcb, job_status_s *js_p)
225{
226    int ret;
227    job_id_t j_id;
228    struct PINT_server_op *s_op = PINT_sm_frame(smcb, PINT_FRAME_CURRENT);
229
230    if(s_op->u.unstuff.layout.algorithm != PVFS_SYS_LAYOUT_ROUND_ROBIN)
231    {
232        /* see create.sm; for now the only layout that we use stuffing on is
233         * ROUND_ROBIN.  The storage format supports other layouts if we
234         * want to add support for others later
235         */
236        gossip_err("Error: unstuff doesn't support layout algorithm: %d\n",
237            s_op->u.unstuff.layout.algorithm);
238        js_p->error_code = -PVFS_ENOSYS;
239        return SM_ACTION_COMPLETE;
240    }
241
242    /* allocate room for final number of handles we want */
243    s_op->u.unstuff.dfile_array =
244        malloc(s_op->u.unstuff.num_dfiles_req * sizeof(PVFS_handle));
245    if(!s_op->u.unstuff.dfile_array)
246    {
247        js_p->error_code = -PVFS_ENOMEM;
248        return SM_ACTION_COMPLETE;
249    }
250
251    /* the very first handle should be our current stuffed handle */
252    s_op->u.unstuff.dfile_array[0]
253        = s_op->resp.u.unstuff.attr.u.meta.dfile_array[0];
254
255    if(s_op->u.unstuff.num_dfiles_req == 1)
256    {
257        /* special case; we are unstuffing to 1 datafile.  There is no need
258         * to retrieve any additional handles
259         */
260        js_p->error_code = 0;
261        return SM_ACTION_COMPLETE;
262    }
263
264    /* NOTE: we are leaving the existing resp attr structure alone until we
265     * get a successful answer from get_handles() and commit to disk
266     */
267    ret = job_precreate_pool_get_handles(
268        s_op->req->u.unstuff.fs_id,
269        (s_op->u.unstuff.num_dfiles_req-1),
270        NULL,
271        &s_op->u.unstuff.dfile_array[1],
272        0,
273        smcb,
274        0,
275        js_p,
276        &j_id,
277        server_job_context,
278        s_op->req->hints);
279    return ret;
280}
281
282static PINT_sm_action set_handles_on_object(
283        struct PINT_smcb *smcb, job_status_s *js_p)
284{
285    struct PINT_server_op *s_op = PINT_sm_frame(smcb, PINT_FRAME_CURRENT);
286    job_id_t j_id;
287
288    gossip_debug(GOSSIP_SERVER_DEBUG, "job_precreate_pool_get_handles() returned %d\n", js_p->error_code);
289
290    if(js_p->error_code < 0)
291    {
292        /* we failed to retrieve any handles */
293        if(s_op->u.unstuff.dfile_array)
294        {
295            free(s_op->u.unstuff.dfile_array);
296            /* preserve error code */
297            return SM_ACTION_COMPLETE;
298        }
299    }
300
301    /* replace dfile information in attr structure */
302    free(s_op->resp.u.unstuff.attr.u.meta.dfile_array);
303    s_op->resp.u.unstuff.attr.u.meta.dfile_array
304        = s_op->u.unstuff.dfile_array;
305    s_op->resp.u.unstuff.attr.u.meta.dfile_count
306        = s_op->u.unstuff.num_dfiles_req;
307
308    /* write new datafile handles to disk */
309    s_op->key.buffer = Trove_Common_Keys[METAFILE_HANDLES_KEY].key;
310    s_op->key.buffer_sz = Trove_Common_Keys[METAFILE_HANDLES_KEY].size;
311
312    s_op->val.buffer =
313        s_op->resp.u.unstuff.attr.u.meta.dfile_array;
314    s_op->val.buffer_sz =
315        s_op->resp.u.unstuff.attr.u.meta.dfile_count * sizeof(PVFS_handle);
316
317    return job_trove_keyval_write(
318        s_op->req->u.unstuff.fs_id,
319        s_op->req->u.unstuff.handle,
320        &s_op->key,
321        &s_op->val,
322        0, NULL, smcb, 0, js_p, &j_id, server_job_context,
323        s_op->req->hints);
324}
325
326static PINT_sm_action update_dfile_count(
327        struct PINT_smcb *smcb, job_status_s *js_p)
328{
329    struct PINT_server_op *s_op = PINT_sm_frame(smcb, PINT_FRAME_CURRENT);
330    job_id_t j_id;
331
332    /* take the current on-disk attribute in object_attr form
333     * (acquired from prelude) with the modified datafile array,
334     * and convert it to the dspace form for writing.
335     */
336    PVFS_object_attr_to_ds_attr(&s_op->resp.u.unstuff.attr, &s_op->ds_attr);
337
338    return job_trove_dspace_setattr(
339        s_op->req->u.unstuff.fs_id, s_op->req->u.unstuff.handle,
340        &s_op->ds_attr,
341        TROVE_SYNC,
342        smcb, 0, js_p, &j_id, server_job_context,
343        s_op->req->hints);
344}
345
346static PINT_sm_action remove_layout(
347        struct PINT_smcb *smcb, job_status_s *js_p)
348{
349    struct PINT_server_op *s_op = PINT_sm_frame(smcb, PINT_FRAME_CURRENT);
350    job_id_t j_id;
351
352    /* remove the layout and num_dfiles_req keyvals as the layout has
353     * now been chosen.
354     */
355    return job_trove_keyval_remove_list(
356        s_op->req->u.unstuff.fs_id, s_op->req->u.unstuff.handle,
357        s_op->key_a,
358        s_op->val_a,
359        s_op->error_a,
360        2,
361        TROVE_SYNC, NULL,
362        smcb, 0, js_p, &j_id, server_job_context,
363        s_op->req->hints);
364}
365
366static PINT_sm_action cleanup(
367        struct PINT_smcb *smcb, job_status_s *js_p)
368{
369    struct PINT_server_op *s_op = PINT_sm_frame(smcb, PINT_FRAME_CURRENT);
370
371    if(s_op->u.unstuff.layout.server_list.servers)
372    {
373        free(s_op->u.unstuff.layout.server_list.servers);
374    }
375    if(s_op->u.unstuff.encoded_layout)
376    {
377        free(s_op->u.unstuff.encoded_layout);
378    }
379    if(s_op->val_a)
380    {
381        free(s_op->val_a);
382    }
383    if(s_op->key_a)
384    {
385        free(s_op->key_a);
386    }
387    if(s_op->error_a)
388    {
389        free(s_op->error_a);
390    }
391
392    PINT_free_object_attr(&s_op->resp.u.getattr.attr);
393    return (server_state_machine_complete(smcb));
394}
395
396static PINT_sm_action getattr_setup(
397    struct PINT_smcb *smcb, job_status_s *js_p)
398{
399    struct PINT_server_op *s_op = PINT_sm_frame(smcb, PINT_FRAME_CURRENT);
400    struct PINT_server_op *getattr_op;
401    int ret;
402
403    js_p->error_code = 0;
404
405    getattr_op = malloc(sizeof(*getattr_op));
406    if(!getattr_op)
407    {
408        js_p->error_code = -PVFS_ENOMEM;
409        return SM_ACTION_COMPLETE;
410    }
411    memset(getattr_op, 0, sizeof(*getattr_op));
412
413    /* TODO: can we come up with a way to clean up and nail down what has
414     * to be set in order to run this nested machine?  This seems fragile.
415     */
416
417    /* need attrs that the prelude read already */
418    getattr_op->attr = s_op->attr;
419    /* need a valid request structure for some generic features like access
420     * logging
421     */
422    getattr_op->req = s_op->req;
423    /* need to fill in the input parameters to the getattr nested machine */
424    getattr_op->u.getattr.fs_id = s_op->req->u.unstuff.fs_id;
425    getattr_op->u.getattr.handle = s_op->req->u.unstuff.handle;
426    getattr_op->u.getattr.attrmask = s_op->req->u.unstuff.attrmask;
427
428    ret = PINT_sm_push_frame(smcb, 0, getattr_op);
429    if(ret < 0)
430    {
431        js_p->error_code = ret;
432    }
433
434    return SM_ACTION_COMPLETE;
435}
436
437static PINT_sm_action getattr_interpret(
438    struct PINT_smcb *smcb, job_status_s *js_p)
439{
440    struct PINT_server_op *getattr_op;
441    struct PINT_server_op *s_op;
442    int task_id;
443    int remaining;
444
445    getattr_op = PINT_sm_pop_frame(smcb, &task_id, &js_p->error_code,
446        &remaining);
447    s_op = PINT_sm_frame(smcb, PINT_FRAME_CURRENT);
448
449    s_op->resp.u.unstuff.attr = getattr_op->resp.u.getattr.attr;
450
451    free(getattr_op);
452
453    if(js_p->error_code)
454    {
455        gossip_debug(GOSSIP_SERVER_DEBUG,
456            "unstuff failed to retrieve existing attrs.\n");
457        return(SM_ACTION_COMPLETE);
458    }
459
460    if(s_op->resp.u.unstuff.attr.mask & PVFS_ATTR_META_UNSTUFFED)
461    {
462        gossip_debug(GOSSIP_SERVER_DEBUG,
463            "unstuff found file already unstuffed; return existing attrs.\n");
464        js_p->error_code = 0;
465        return(SM_ACTION_COMPLETE);
466    }
467
468    gossip_debug(GOSSIP_SERVER_DEBUG,
469        "unstuff found stuffed file.\n");
470    js_p->error_code = STATE_UNSTUFF;
471    return SM_ACTION_COMPLETE;
472}
473
474
475PINT_GET_OBJECT_REF_DEFINE(unstuff);
476
477struct PINT_server_req_params pvfs2_unstuff_params =
478{
479    .string_name = "unstuff",
480    .perm = PINT_SERVER_CHECK_ATTR,
481    .access_type = PINT_server_req_modify,
482    .sched_policy = PINT_SERVER_REQ_SCHEDULE,
483    .get_object_ref = PINT_get_object_ref_unstuff,
484    .state_machine = &pvfs2_unstuff_sm
485};
486
487/*
488 * Local variables:
489 *  mode: c
490 *  c-indent-level: 4
491 *  c-basic-offset: 4
492 * End:
493 *
494 * vim: ft=c ts=8 sts=4 sw=4 expandtab
495 */
496
Note: See TracBrowser for help on using the browser.