nfs-ganesha 1.4

fsal_xattrs.c

Go to the documentation of this file.
00001 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; -*-
00002  * vim:expandtab:shiftwidth=4:tabstop=4:
00003  */
00011 #ifdef HAVE_CONFIG_H
00012 #include "config.h"
00013 #endif
00014 
00015 #include "fsal.h"
00016 #include "fsal_internal.h"
00017 #include "fsal_convert.h"
00018 
00019 #include <string.h>
00020 #include <time.h>
00021 #include <sys/types.h>
00022 #include <attr/xattr.h>
00023 #include "abstract_mem.h"
00024 
00025 /* generic definitions for extended attributes */
00026 
00027 #define XATTR_FOR_FILE     0x00000001
00028 #define XATTR_FOR_DIR      0x00000002
00029 #define XATTR_FOR_SYMLINK  0x00000004
00030 #define XATTR_FOR_ALL      0x0000000F
00031 #define XATTR_RO           0x00000100
00032 #define XATTR_RW           0x00000200
00033 
00034 /* function for getting an attribute value */
00035 
00036 typedef int (*xattr_getfunc_t) (fsal_handle_t *,  /* object handle */
00037                                 fsal_op_context_t *,      /* context */
00038                                 caddr_t,        /* output buff */
00039                                 size_t, /* output buff size */
00040                                 size_t *,       /* output size */
00041                                 void *arg);     /* optionnal argument */
00042 
00043 typedef int (*xattr_setfunc_t) (fsal_handle_t *,  /* object handle */
00044                                 fsal_op_context_t *,      /* context */
00045                                 caddr_t,        /* input buff */
00046                                 size_t, /* input size */
00047                                 int,    /* creation flag */
00048                                 void *arg);     /* optionnal argument */
00049 
00050 typedef struct fsal_xattr_def__
00051 {
00052   char xattr_name[FSAL_MAX_NAME_LEN];
00053   xattr_getfunc_t get_func;
00054   xattr_setfunc_t set_func;
00055   int flags;
00056   void *arg;
00057 } fsal_xattr_def_t;
00058 
00059 /*
00060  * DEFINE GET/SET FUNCTIONS
00061  */
00062 
00063 int print_fid(fsal_handle_t * p_objecthandle,     /* object handle */
00064               fsal_op_context_t * p_context,      /* IN */
00065               caddr_t buffer_addr,      /* IN/OUT */
00066               size_t buffer_size,       /* IN */
00067               size_t * p_output_size,   /* OUT */
00068               void *arg)
00069 {
00070   *p_output_size = snprintf(buffer_addr, buffer_size, DFID_NOBRACE "\n",
00071                             PFID(&((lustrefsal_handle_t *)p_objecthandle)->data.fid));
00072   return 0;
00073 }                               /* print_fid */
00074 
00075 #define ARG_STRIPE_SIZE  ((long)0)
00076 #define ARG_STRIPE_COUNT ((long)1)
00077 #define ARG_STORAGE_TGT  ((long)2)
00078 #define ARG_POOL         ((long)3)
00079 
00080 #ifdef _LUSTRE_HSM
00081 #define ARG_HSM_STATE    ((long)0)
00082 #define ARG_HSM_ACTION   ((long)1)
00083 #define ARG_HSM_ARCH_NUM ((long)2)
00084 #endif
00085 
00086 int print_stripe(fsal_handle_t * p_objecthandle,  /* object handle */
00087                  fsal_op_context_t * p_context,   /* IN */
00088                  caddr_t buffer_addr,   /* IN/OUT */
00089                  size_t buffer_size,    /* IN */
00090                  size_t * p_output_size,        /* OUT */
00091                  void *arg)
00092 {
00093   /* buffer userd for llapi_get_stripe.
00094    * oversize it to 4kB because there can be many stripe entries
00095    * in the case of join'ed files.
00096    */
00097   int rc;
00098   char lum_buffer[4096];
00099   fsal_path_t entry_path;
00100   fsal_status_t st;
00101   unsigned int i;
00102   char *curr;
00103   struct lov_user_md *p_lum = (struct lov_user_md *)lum_buffer;
00104 
00105   st = fsal_internal_Handle2FidPath(p_context, p_objecthandle, &entry_path);
00106   if(FSAL_IS_ERROR(st))
00107     return st.major;
00108 
00109   memset(lum_buffer, 0, sizeof(lum_buffer));
00110   rc = llapi_file_get_stripe(entry_path.path, p_lum);
00111 
00112   if(rc != 0)
00113     {
00114       if(abs(rc) == ENODATA)
00115         {
00116           LogDebug(COMPONENT_FSAL, "%s has no stripe information", entry_path.path);
00117           *p_output_size = sprintf(buffer_addr, "none\n");
00118           return 0;
00119         }
00120       else
00121           LogCrit(COMPONENT_FSAL, "Error %d getting stripe info for %s", rc, entry_path.path);
00122       return posix2fsal_error(abs(rc));
00123     }
00124 
00125   /* Check for protocol version number */
00126   if(p_lum->lmm_magic == LOV_USER_MAGIC_V1)
00127     {
00128 
00129       switch ((long)arg)
00130         {
00131         case ARG_STRIPE_SIZE:
00132           *p_output_size =
00133               snprintf(buffer_addr, buffer_size, "%u\n", p_lum->lmm_stripe_size);
00134           break;
00135 
00136         case ARG_STRIPE_COUNT:
00137           *p_output_size =
00138               snprintf(buffer_addr, buffer_size, "%u\n", p_lum->lmm_stripe_count);
00139           break;
00140 
00141         case ARG_POOL: /* no pool if the structure is LOV V1 */
00142             ((char*)buffer_addr)[0]='\0';
00143             *p_output_size = 0;
00144             break;
00145 
00146         case ARG_STORAGE_TGT:
00147           *p_output_size = 0;
00148           curr = buffer_addr;
00149           curr[0] = '\0';
00150 
00151           for(i = 0; i < p_lum->lmm_stripe_count; i++)
00152             {
00153               int sz;
00154               if(i != p_lum->lmm_stripe_count - 1)
00155                 sz = snprintf(curr, buffer_size - *p_output_size, "%u,",
00156                               p_lum->lmm_objects[i].l_ost_idx);
00157               else
00158                 sz = snprintf(curr, buffer_size - *p_output_size, "%u\n",
00159                               p_lum->lmm_objects[i].l_ost_idx);
00160               curr += sz;
00161               *p_output_size += sz;
00162             }
00163 
00164           break;
00165         }
00166     }
00167 #ifdef LOV_USER_MAGIC_V3 /* pool support */
00168   else if(p_lum->lmm_magic == LOV_USER_MAGIC_V3)
00169     {
00170         struct lov_user_md_v3 *p_lum3 = ( struct lov_user_md_v3 * ) p_lum;
00171 
00172         switch ((long)arg)
00173         {
00174             case ARG_STRIPE_SIZE:
00175               *p_output_size =
00176                   snprintf(buffer_addr, buffer_size, "%u\n", p_lum3->lmm_stripe_size);
00177               break;
00178 
00179             case ARG_STRIPE_COUNT:
00180                 *p_output_size =
00181                     snprintf(buffer_addr, buffer_size, "%u\n", p_lum3->lmm_stripe_count);
00182                 break;
00183 
00184             case ARG_POOL: /* pool structure in LOV V3 */
00185                 *p_output_size = snprintf( buffer_addr, buffer_size, "%s\n",
00186                                            p_lum3->lmm_pool_name );
00187                 break;
00188 
00189             case ARG_STORAGE_TGT:
00190               *p_output_size = 0;
00191               curr = buffer_addr;
00192               curr[0] = '\0';
00193 
00194               for(i = 0; i < p_lum3->lmm_stripe_count; i++)
00195                 {
00196                   int sz;
00197                   if(i != p_lum3->lmm_stripe_count - 1)
00198                     sz = snprintf(curr, buffer_size - *p_output_size, "%u,",
00199                                   p_lum3->lmm_objects[i].l_ost_idx);
00200                   else
00201                     sz = snprintf(curr, buffer_size - *p_output_size, "%u\n",
00202                                   p_lum3->lmm_objects[i].l_ost_idx);
00203                   curr += sz;
00204                   *p_output_size += sz;
00205                 }
00206 
00207               break;
00208         }
00209 
00210     }
00211 #endif
00212   else
00213     {
00214         LogCrit(COMPONENT_FSAL, "Wrong Luster magic number for %s: %#X <> %#X",
00215                  entry_path.path, p_lum->lmm_magic, LOV_USER_MAGIC_V1);
00216       return ERR_FSAL_INVAL;
00217     }
00218 
00219   return 0;
00220 }
00221 
00222 /* ------------ Lustre-HSM specific attributes ------------ */
00223 #ifdef _LUSTRE_HSM
00224 
00225 #define TEST_FLAG_APPEND( _mask, _flg_val, _flg_name, _p_sz )               \
00226         do { if ((_mask) & (_flg_val)) {                                   \
00227                 size_t sz = 0;                                             \
00228                 if ( (*_p_sz) > 0 )                                         \
00229                     sz = snprintf(curr, buffer_size-(*_p_sz), " %s", _flg_name); \
00230                 else                                                       \
00231                     sz = snprintf(curr, buffer_size-(*_p_sz), "%s", _flg_name);  \
00232                 curr += sz;                                                \
00233                 (*_p_sz) += sz;                                             \
00234          }} while(0)
00235 
00236 
00237 int print_hsm_info(lustrefsal_handle_t * p_objecthandle,  /* object handle */
00238                    lustrefsal_op_context_t * p_context,   /* IN */
00239                    caddr_t buffer_addr,   /* IN/OUT */
00240                    size_t buffer_size,    /* IN */
00241                    size_t * p_output_size,        /* OUT */
00242                    void *arg)
00243 {
00244     int rc;
00245     fsal_path_t entry_path;
00246     fsal_status_t st;
00247     unsigned int i;
00248     struct hsm_user_state hus = {0};
00249 
00250     /* set empty output, by default */
00251     ((char*)buffer_addr)[0]='\0';
00252     *p_output_size = 0;
00253 
00254     st = fsal_internal_Handle2FidPath(p_context, p_objecthandle, &entry_path);
00255     if(FSAL_IS_ERROR(st))
00256         return st.major;
00257 
00258     rc = llapi_hsm_state_get(entry_path.path, &hus);
00259 
00260     if(rc != 0)
00261         return posix2fsal_error(-rc);
00262 
00263     switch ((long)arg)
00264     {
00265         case ARG_HSM_STATE:
00266             if ( hus.hus_states == 0 )
00267             {
00268                 *p_output_size =
00269                     snprintf(buffer_addr, buffer_size, "new\n" );
00270             }
00271             else
00272             {
00273                 char * curr = (char*)buffer_addr;
00274 
00275                 TEST_FLAG_APPEND( hus.hus_states, HS_RELEASED, "released",
00276                                   p_output_size );
00277                 TEST_FLAG_APPEND( hus.hus_states, HS_EXISTS, "exists",
00278                                   p_output_size );
00279                 TEST_FLAG_APPEND( hus.hus_states, HS_DIRTY, "dirty",
00280                                   p_output_size );
00281                 TEST_FLAG_APPEND( hus.hus_states, HS_ARCHIVED, "archived",
00282                                   p_output_size );
00283                 TEST_FLAG_APPEND( hus.hus_states, HS_NORELEASE, "never_release",
00284                                   p_output_size );
00285                 TEST_FLAG_APPEND( hus.hus_states, HS_NOARCHIVE, "never_archive",
00286                                   p_output_size );
00287                 TEST_FLAG_APPEND( hus.hus_states, HS_LOST, "lost_from_hsm",
00288                                   p_output_size );
00289                 if ( (*p_output_size) > 0 )
00290                     (*p_output_size) += snprintf( curr,
00291                                                   buffer_size-(*p_output_size),
00292                                                   "\n" );
00293             }
00294           break;
00295 
00296         case ARG_HSM_ACTION:
00297             if ( hus.hus_in_progress_action != HUA_NONE )
00298             {
00299                 *p_output_size =
00300                     snprintf(buffer_addr, buffer_size, "%s (%s)\n",
00301                              hsm_user_action2name(hus.hus_in_progress_action),
00302                              hsm_progress_state2name(hus.hus_in_progress_state));
00303             }
00304             else
00305                 *p_output_size =
00306                     snprintf(buffer_addr, buffer_size, "%s\n",
00307                              hsm_user_action2name(hus.hus_in_progress_action));
00308             break;
00309 
00310         case ARG_HSM_ARCH_NUM:
00311             if (hus.hus_archive_num != 0)
00312             {
00313                 *p_output_size =
00314                     snprintf(buffer_addr, buffer_size, "%u\n",
00315                              hus.hus_archive_num);
00316             }
00317           break;
00318     }
00319 
00320     return 0;
00321 }
00322 #endif
00323 
00324 /* DEFINE HERE YOUR ATTRIBUTES LIST */
00325 
00326 static fsal_xattr_def_t xattr_list[] = {
00327     {"fid", print_fid, NULL, XATTR_FOR_ALL | XATTR_RO, NULL},
00328     {"stripe_size", print_stripe, NULL, XATTR_FOR_FILE | XATTR_FOR_DIR | XATTR_RO,
00329         (void *)ARG_STRIPE_SIZE},
00330     {"stripe_count", print_stripe, NULL, XATTR_FOR_FILE | XATTR_FOR_DIR | XATTR_RO,
00331         (void *)ARG_STRIPE_COUNT},
00332     {"pool", print_stripe, NULL, XATTR_FOR_FILE | XATTR_FOR_DIR | XATTR_RO,
00333         (void *)ARG_POOL},
00334     {"OSTs", print_stripe, NULL, XATTR_FOR_FILE | XATTR_RO,
00335         (void *)ARG_STORAGE_TGT},
00336 #ifdef _LUSTRE_HSM
00337     /* additionnal attributes from HSM state */
00338     {"hsm_state", print_hsm_info, NULL, XATTR_FOR_FILE | XATTR_RO,
00339         (void *)ARG_HSM_STATE },
00340     {"hsm_action", print_hsm_info, NULL, XATTR_FOR_FILE | XATTR_RO,
00341         (void *)ARG_HSM_ACTION },
00342 #endif
00343 };
00344 
00345 #ifdef _LUSTRE_HSM
00346 #define XATTR_COUNT 7
00347 #else
00348 #define XATTR_COUNT 5
00349 #endif
00350 
00351 /* we assume that this number is < 254 */
00352 #if ( XATTR_COUNT > 254 )
00353 #error "ERROR: xattr count > 254"
00354 #endif
00355 
00356 /* YOUR SHOULD NOT HAVE TO MODIFY THE FOLLOWING FUNCTIONS */
00357 
00358 /* test if an object has a given attribute */
00359 static int do_match_type(int xattr_flag, fsal_nodetype_t obj_type)
00360 {
00361   switch (obj_type)
00362     {
00363     case FSAL_TYPE_FILE:
00364       return ((xattr_flag & XATTR_FOR_FILE) == XATTR_FOR_FILE);
00365 
00366     case FSAL_TYPE_DIR:
00367       return ((xattr_flag & XATTR_FOR_DIR) == XATTR_FOR_DIR);
00368 
00369     case FSAL_TYPE_LNK:
00370       return ((xattr_flag & XATTR_FOR_SYMLINK) == XATTR_FOR_SYMLINK);
00371 
00372     default:
00373       return ((xattr_flag & XATTR_FOR_ALL) == XATTR_FOR_ALL);
00374     }
00375 }
00376 
00377 static int attr_is_read_only(unsigned int attr_index)
00378 {
00379   if(attr_index < XATTR_COUNT)
00380     {
00381       if(xattr_list[attr_index].flags & XATTR_RO)
00382         return TRUE;
00383     }
00384   /* else : standard xattr */
00385   return FALSE;
00386 }
00387 
00388 static int file_attributes_to_xattr_attrs(fsal_attrib_list_t * file_attrs,
00389                                           fsal_attrib_list_t * p_xattr_attrs,
00390                                           unsigned int attr_index)
00391 {
00392   /* supported attributes are:
00393    * - owner (same as the objet)
00394    * - group (same as the objet)
00395    * - type FSAL_TYPE_XATTR
00396    * - fileid (attr index ? or (fileid^((index+1)<<24)) )
00397    * - mode (config & file)
00398    * - atime, mtime, ctime = these of the object ?
00399    * - size=1block, used=1block
00400    * - rdev=0
00401    * - nlink=1
00402    */
00403   fsal_attrib_mask_t supported = FSAL_ATTR_SUPPATTR | FSAL_ATTR_MODE | FSAL_ATTR_FILEID
00404       | FSAL_ATTR_TYPE | FSAL_ATTR_OWNER | FSAL_ATTR_GROUP
00405       | FSAL_ATTR_ATIME | FSAL_ATTR_MTIME | FSAL_ATTR_CTIME
00406       | FSAL_ATTR_CREATION | FSAL_ATTR_CHGTIME | FSAL_ATTR_SIZE
00407       | FSAL_ATTR_SPACEUSED | FSAL_ATTR_NUMLINKS | FSAL_ATTR_RAWDEV | FSAL_ATTR_FSID;
00408   fsal_attrib_mask_t unsupp;
00409 
00410   /* only those supported by filesystem */
00411   supported &= global_fs_info.supported_attrs;
00412 
00413   if(p_xattr_attrs->asked_attributes == 0)
00414     {
00415       p_xattr_attrs->asked_attributes = supported;
00416 
00417       LogCrit(COMPONENT_FSAL, 
00418                         "Error: p_xattr_attrs->asked_attributes was 0 in %s() line %d, file %s",
00419                         __FUNCTION__, __LINE__, __FILE__);
00420     }
00421 
00422   unsupp = p_xattr_attrs->asked_attributes & (~supported);
00423 
00424   if(unsupp)
00425     {
00426       LogDebug(COMPONENT_FSAL,
00427                         "Asking for unsupported attributes in %s(): %#llX removing it from asked attributes",
00428                         __FUNCTION__, unsupp);
00429 
00430       p_xattr_attrs->asked_attributes &= (~unsupp);
00431     }
00432 
00433   if(p_xattr_attrs->asked_attributes & FSAL_ATTR_SUPPATTR)
00434     p_xattr_attrs->supported_attributes = supported;
00435 
00436   if(p_xattr_attrs->asked_attributes & FSAL_ATTR_MODE)
00437     {
00438       p_xattr_attrs->mode = file_attrs->mode & global_fs_info.xattr_access_rights;
00439 
00440       if(attr_is_read_only(attr_index))
00441         p_xattr_attrs->mode &= ~(0222);
00442     }
00443 
00444   if(p_xattr_attrs->asked_attributes & FSAL_ATTR_FILEID)
00445     {
00446       unsigned int i;
00447       unsigned long hash = attr_index + 1;
00448       char *str = (char *)&file_attrs->fileid;
00449 
00450       for(i = 0; i < sizeof(p_xattr_attrs->fileid); i++, str++)
00451         {
00452           hash = (hash << 5) - hash + (unsigned long)(*str);
00453         }
00454       p_xattr_attrs->fileid = hash;
00455     }
00456 
00457   if(p_xattr_attrs->asked_attributes & FSAL_ATTR_TYPE)
00458     p_xattr_attrs->type = FSAL_TYPE_XATTR;
00459 
00460   if(p_xattr_attrs->asked_attributes & FSAL_ATTR_OWNER)
00461     p_xattr_attrs->owner = file_attrs->owner;
00462 
00463   if(p_xattr_attrs->asked_attributes & FSAL_ATTR_GROUP)
00464     p_xattr_attrs->group = file_attrs->group;
00465 
00466   if(p_xattr_attrs->asked_attributes & FSAL_ATTR_ATIME)
00467     p_xattr_attrs->atime = file_attrs->atime;
00468 
00469   if(p_xattr_attrs->asked_attributes & FSAL_ATTR_MTIME)
00470     p_xattr_attrs->mtime = file_attrs->mtime;
00471 
00472   if(p_xattr_attrs->asked_attributes & FSAL_ATTR_CTIME)
00473     p_xattr_attrs->ctime = file_attrs->ctime;
00474 
00475   if(p_xattr_attrs->asked_attributes & FSAL_ATTR_CREATION)
00476     p_xattr_attrs->creation = file_attrs->creation;
00477 
00478   if(p_xattr_attrs->asked_attributes & FSAL_ATTR_CHGTIME)
00479     {
00480       p_xattr_attrs->chgtime = file_attrs->chgtime;
00481       p_xattr_attrs->change = (uint64_t) p_xattr_attrs->chgtime.seconds;
00482     }
00483 
00484   if(p_xattr_attrs->asked_attributes & FSAL_ATTR_SIZE)
00485     p_xattr_attrs->filesize = DEV_BSIZE;
00486 
00487   if(p_xattr_attrs->asked_attributes & FSAL_ATTR_SPACEUSED)
00488     p_xattr_attrs->spaceused = DEV_BSIZE;
00489 
00490   if(p_xattr_attrs->asked_attributes & FSAL_ATTR_NUMLINKS)
00491     p_xattr_attrs->numlinks = 1;
00492 
00493   if(p_xattr_attrs->asked_attributes & FSAL_ATTR_RAWDEV)
00494     {
00495       p_xattr_attrs->rawdev.major = 0;
00496       p_xattr_attrs->rawdev.minor = 0;
00497     }
00498 
00499   if(p_xattr_attrs->asked_attributes & FSAL_ATTR_FSID)
00500     {
00501       p_xattr_attrs->fsid = file_attrs->fsid;
00502     }
00503 
00504   /* if mode==0, then owner is set to root and mode is set to 0600 */
00505   if((p_xattr_attrs->asked_attributes & FSAL_ATTR_OWNER)
00506      && (p_xattr_attrs->asked_attributes & FSAL_ATTR_MODE) && (p_xattr_attrs->mode == 0))
00507     {
00508       p_xattr_attrs->owner = 0;
00509       p_xattr_attrs->mode = 0600;
00510       if(attr_is_read_only(attr_index))
00511         p_xattr_attrs->mode &= ~(0200);
00512     }
00513 
00514   return 0;
00515 
00516 }
00517 
00526 fsal_status_t LUSTREFSAL_GetXAttrAttrs(fsal_handle_t * p_objecthandle,    /* IN */
00527                                        fsal_op_context_t * p_context,     /* IN */
00528                                        unsigned int xattr_id,   /* IN */
00529                                        fsal_attrib_list_t * p_attrs
00531     )
00532 {
00533   int rc;
00534   fsal_status_t st;
00535   fsal_attrib_list_t file_attrs;
00536 
00537   /* sanity checks */
00538   if(!p_objecthandle || !p_context || !p_attrs)
00539     Return(ERR_FSAL_FAULT, 0, INDEX_FSAL_GetXAttrAttrs);
00540 
00541   /* object attributes we want to retrieve from parent */
00542   file_attrs.asked_attributes = FSAL_ATTR_MODE | FSAL_ATTR_FILEID | FSAL_ATTR_OWNER
00543       | FSAL_ATTR_GROUP | FSAL_ATTR_ATIME | FSAL_ATTR_MTIME | FSAL_ATTR_TYPE
00544       | FSAL_ATTR_CTIME | FSAL_ATTR_CREATION | FSAL_ATTR_CHGTIME | FSAL_ATTR_FSID;
00545 
00546   /* don't retrieve attributes not asked */
00547   file_attrs.asked_attributes &= p_attrs->asked_attributes;
00548 
00549   st = LUSTREFSAL_getattrs(p_objecthandle, p_context, &file_attrs);
00550 
00551   if(FSAL_IS_ERROR(st))
00552     Return(st.major, st.minor, INDEX_FSAL_GetXAttrAttrs);
00553 
00554   /* check that this index match the type of entry */
00555   if(xattr_id < XATTR_COUNT
00556      && !do_match_type(xattr_list[xattr_id].flags, file_attrs.type))
00557     {
00558       Return(ERR_FSAL_INVAL, 0, INDEX_FSAL_GetXAttrAttrs);
00559     }
00560   else if(xattr_id >= XATTR_COUNT)
00561     {
00562       /* This is user defined xattr */
00563       LogFullDebug(COMPONENT_FSAL,
00564                         "Getting attributes for xattr #%u", xattr_id - XATTR_COUNT);
00565     }
00566 
00567   if((rc = file_attributes_to_xattr_attrs(&file_attrs, p_attrs, xattr_id)))
00568     {
00569       Return(ERR_FSAL_INVAL, rc, INDEX_FSAL_GetXAttrAttrs);
00570     }
00571 
00572   Return(ERR_FSAL_NO_ERROR, 0, INDEX_FSAL_GetXAttrAttrs);
00573 
00574 }                               /* FSAL_GetXAttrAttrs */
00575 
00588 fsal_status_t LUSTREFSAL_ListXAttrs(fsal_handle_t * p_objecthandle,       /* IN */
00589                                     unsigned int argcookie,        /* IN */
00590                                     fsal_op_context_t * p_context,        /* IN */
00591                                     fsal_xattrent_t * xattrs_tab,       /* IN/OUT */
00592                                     unsigned int xattrs_tabsize,        /* IN */
00593                                     unsigned int *p_nb_returned,        /* OUT */
00594                                     int *end_of_list    /* OUT */
00595     )
00596 {
00597   unsigned int index;
00598   unsigned int out_index;
00599   fsal_status_t st;
00600   fsal_attrib_list_t file_attrs;
00601   fsal_path_t lustre_path;
00602   unsigned int cookie = argcookie ;
00603 
00604   char names[MAXPATHLEN], *ptr;
00605   size_t namesize;
00606   int xattr_idx;
00607 
00608   /* sanity checks */
00609   if(!p_objecthandle || !p_context || !xattrs_tab || !p_nb_returned || !end_of_list)
00610     Return(ERR_FSAL_FAULT, 0, INDEX_FSAL_ListXAttrs);
00611 
00612   /* Deal with special cookie */
00613   if( argcookie == FSAL_XATTR_RW_COOKIE ) cookie = XATTR_COUNT ;
00614 
00615   /* object attributes we want to retrieve from parent */
00616   file_attrs.asked_attributes = FSAL_ATTR_MODE | FSAL_ATTR_FILEID | FSAL_ATTR_OWNER
00617       | FSAL_ATTR_GROUP | FSAL_ATTR_ATIME | FSAL_ATTR_MTIME | FSAL_ATTR_TYPE
00618       | FSAL_ATTR_CTIME | FSAL_ATTR_CREATION | FSAL_ATTR_CHGTIME | FSAL_ATTR_FSID;
00619 
00620   /* don't retrieve unsuipported attributes */
00621   file_attrs.asked_attributes &= global_fs_info.supported_attrs;
00622 
00623   st = LUSTREFSAL_getattrs(p_objecthandle, p_context, &file_attrs);
00624 
00625   if(FSAL_IS_ERROR(st))
00626     Return(st.major, st.minor, INDEX_FSAL_ListXAttrs);
00627 
00628   for(index = cookie, out_index = 0;
00629       index < XATTR_COUNT && out_index < xattrs_tabsize; index++)
00630     {
00631       if(do_match_type(xattr_list[index].flags, file_attrs.type))
00632         {
00633           /* fills an xattr entry */
00634           xattrs_tab[out_index].xattr_id = index;
00635           FSAL_str2name(xattr_list[index].xattr_name, FSAL_MAX_NAME_LEN,
00636                         &xattrs_tab[out_index].xattr_name);
00637           xattrs_tab[out_index].xattr_cookie = index + 1;
00638 
00639           /* set asked attributes (all supported) */
00640           xattrs_tab[out_index].attributes.asked_attributes =
00641               global_fs_info.supported_attrs;
00642 
00643           if(file_attributes_to_xattr_attrs
00644              (&file_attrs, &xattrs_tab[out_index].attributes, index))
00645             {
00646               /* set error flag */
00647               xattrs_tab[out_index].attributes.asked_attributes = FSAL_ATTR_RDATTR_ERR;
00648             }
00649 
00650           /* next output slot */
00651           out_index++;
00652         }
00653     }
00654 
00655   /* save a call if output array is full */
00656   if(out_index == xattrs_tabsize)
00657     {
00658       *end_of_list = FALSE;
00659       *p_nb_returned = out_index;
00660       Return(ERR_FSAL_NO_ERROR, 0, INDEX_FSAL_ListXAttrs);
00661     }
00662 
00663   /* get the path of the file in Lustre */
00664   st = fsal_internal_Handle2FidPath(p_context, p_objecthandle, &lustre_path);
00665   if(FSAL_IS_ERROR(st))
00666     ReturnStatus(st, INDEX_FSAL_ListXAttrs);
00667 
00668   /* get xattrs */
00669 
00670   TakeTokenFSCall();
00671   namesize = llistxattr(lustre_path.path, names, sizeof(names));
00672   ReleaseTokenFSCall();
00673 
00674   if(namesize >= 0)
00675     {
00676       size_t len = 0;
00677 
00678       errno = 0;
00679 
00680       for(ptr = names, xattr_idx = 0;
00681           (ptr < names + namesize) && (out_index < xattrs_tabsize);
00682           xattr_idx++, ptr += len + 1)
00683         {
00684           len = strlen(ptr);
00685           index = XATTR_COUNT + xattr_idx;
00686 
00687           /* skip if index is before cookie */
00688           if(index < cookie)
00689             continue;
00690 
00691           /* fills an xattr entry */
00692           xattrs_tab[out_index].xattr_id = index;
00693           FSAL_str2name(ptr, len + 1, &xattrs_tab[out_index].xattr_name);
00694           xattrs_tab[out_index].xattr_cookie = index + 1;
00695 
00696           /* set asked attributes (all supported) */
00697           xattrs_tab[out_index].attributes.asked_attributes =
00698               global_fs_info.supported_attrs;
00699 
00700           if(file_attributes_to_xattr_attrs(&file_attrs,
00701                                             &xattrs_tab[out_index].attributes, index))
00702             {
00703               /* set error flag */
00704               xattrs_tab[out_index].attributes.asked_attributes = FSAL_ATTR_RDATTR_ERR;
00705             }
00706 
00707           /* next output slot */
00708           out_index++;
00709         }
00710       /* all xattrs are in the output array */
00711       if(ptr >= names + namesize)
00712         *end_of_list = TRUE;
00713       else
00714         *end_of_list = FALSE;
00715     }
00716   else                          /* no xattrs */
00717     *end_of_list = TRUE;
00718 
00719   *p_nb_returned = out_index;
00720 
00721   Return(ERR_FSAL_NO_ERROR, 0, INDEX_FSAL_ListXAttrs);
00722 
00723 }
00724 
00725 static int xattr_id_to_name(char *lustre_path, unsigned int xattr_id, char *name)
00726 {
00727   unsigned int index;
00728   unsigned int curr_idx;
00729   char names[MAXPATHLEN], *ptr;
00730   size_t namesize;
00731   size_t len = 0;
00732 
00733   if(xattr_id < XATTR_COUNT)
00734     return ERR_FSAL_INVAL;
00735 
00736   index = xattr_id - XATTR_COUNT;
00737 
00738   /* get xattrs */
00739 
00740   TakeTokenFSCall();
00741   namesize = llistxattr(lustre_path, names, sizeof(names));
00742   ReleaseTokenFSCall();
00743 
00744   if(namesize < 0)
00745     return ERR_FSAL_NOENT;
00746 
00747   errno = 0;
00748 
00749   for(ptr = names, curr_idx = 0; ptr < names + namesize; curr_idx++, ptr += len + 1)
00750     {
00751       len = strlen(ptr);
00752       if(curr_idx == index)
00753         {
00754           strcpy(name, ptr);
00755           return ERR_FSAL_NO_ERROR;
00756         }
00757     }
00758   return ERR_FSAL_NOENT;
00759 }
00760 
00765 static int xattr_name_to_id(char *lustre_path, const char *name)
00766 {
00767   unsigned int i;
00768   char names[MAXPATHLEN], *ptr;
00769   size_t namesize;
00770 
00771   /* get xattrs */
00772 
00773   TakeTokenFSCall();
00774   namesize = llistxattr(lustre_path, names, sizeof(names));
00775   ReleaseTokenFSCall();
00776 
00777   if(namesize < 0)
00778     return -ERR_FSAL_NOENT;
00779 
00780   for(ptr = names, i = 0; ptr < names + namesize; i++, ptr += strlen(ptr) + 1)
00781     {
00782       if(!strcmp(name, ptr))
00783         return i + XATTR_COUNT;
00784     }
00785   return -ERR_FSAL_NOENT;
00786 }
00787 
00788 static int xattr_format_value(caddr_t buffer, size_t * datalen, size_t maxlen)
00789 {
00790   size_t size_in = *datalen;
00791   size_t len = strnlen((char *)buffer, size_in);
00792   int i;
00793 
00794   if(len == size_in - 1 || len == size_in)
00795     {
00796       int ascii = TRUE;
00797       char *str = buffer;
00798       int i;
00799 
00800       for(i = 0; i < len; i++)
00801         {
00802           if(!isprint(str[i]) && !isspace(str[i]))
00803             {
00804               ascii = FALSE;
00805               break;
00806             }
00807         }
00808 
00809       if(ascii)
00810         {
00811           *datalen = size_in;
00812           /* add additional '\n', if missing */
00813           if((size_in + 1 < maxlen) && (str[len - 1] != '\n'))
00814             {
00815               str[len] = '\n';
00816               str[len + 1] = '\0';
00817               (*datalen) += 2;
00818             }
00819           return ERR_FSAL_NO_ERROR;
00820         }
00821     }
00822 
00823   /* byte, word, 32 or 64 bits */
00824   if(size_in == 1)
00825     {
00826       unsigned char val = *((unsigned char *)buffer);
00827       *datalen = 1 + snprintf((char *)buffer, maxlen, "%hhu\n", val);
00828       return ERR_FSAL_NO_ERROR;
00829     }
00830   else if(size_in == 2)
00831     {
00832       unsigned short val = *((unsigned short *)buffer);
00833       *datalen = 1 + snprintf((char *)buffer, maxlen, "%hu\n", val);
00834       return ERR_FSAL_NO_ERROR;
00835     }
00836   else if(size_in == 4)
00837     {
00838       unsigned int val = *((unsigned int *)buffer);
00839       *datalen = 1 + snprintf((char *)buffer, maxlen, "%u\n", val);
00840       return ERR_FSAL_NO_ERROR;
00841     }
00842   else if(size_in == 8)
00843     {
00844       unsigned long long val = *((unsigned long long *)buffer);
00845       *datalen = 1 + snprintf((char *)buffer, maxlen, "%llu\n", val);
00846       return ERR_FSAL_NO_ERROR;
00847     }
00848   else
00849     {
00850       /* 2 bytes per initial byte +'0x' +\n +\0 */
00851       char *curr_out;
00852       char *tmp_buf = gsh_malloc(3 * size_in + 4);
00853       if(!tmp_buf)
00854         return ERR_FSAL_NOMEM;
00855       curr_out = tmp_buf;
00856       curr_out += sprintf(curr_out, "0x");
00857       /* hexa representation */
00858       for(i = 0; i < size_in; i++)
00859         {
00860           unsigned char *p8 = (unsigned char *)(buffer + i);
00861           if((i % 4 == 3) && (i != size_in - 1))
00862             curr_out += sprintf(curr_out, "%02hhX.", *p8);
00863           else
00864             curr_out += sprintf(curr_out, "%02hhX", *p8);
00865         }
00866       *curr_out = '\n';
00867       curr_out++;
00868       *curr_out = '\0';
00869       curr_out++;
00870       strncpy((char *)buffer, tmp_buf, maxlen);
00871       *datalen = strlen(tmp_buf) + 1;
00872       if(*datalen > maxlen)
00873         *datalen = maxlen;
00874       gsh_free(tmp_buf);
00875       return ERR_FSAL_NO_ERROR;
00876     }
00877 }
00878 
00889 fsal_status_t LUSTREFSAL_GetXAttrValueById(fsal_handle_t * p_objecthandle,        /* IN */
00890                                            unsigned int xattr_id,       /* IN */
00891                                            fsal_op_context_t * p_context, /* IN */
00892                                            caddr_t buffer_addr, /* IN/OUT */
00893                                            size_t buffer_size,  /* IN */
00894                                            size_t * p_output_size       /* OUT */
00895     )
00896 {
00897   int rc;
00898   fsal_attrib_list_t file_attrs;
00899   fsal_status_t st;
00900 
00901   /* sanity checks */
00902   if(!p_objecthandle || !p_context || !p_output_size || !buffer_addr)
00903     Return(ERR_FSAL_FAULT, 0, INDEX_FSAL_GetXAttrValue);
00904 
00905   /* get type for checking it */
00906   file_attrs.asked_attributes = FSAL_ATTR_TYPE;
00907 
00908   st = LUSTREFSAL_getattrs(p_objecthandle, p_context, &file_attrs);
00909 
00910   if(FSAL_IS_ERROR(st))
00911     ReturnStatus(st, INDEX_FSAL_GetXAttrValue);
00912 
00913   /* check that this index match the type of entry */
00914   if((xattr_id < XATTR_COUNT)
00915      && !do_match_type(xattr_list[xattr_id].flags, file_attrs.type))
00916     {
00917       Return(ERR_FSAL_INVAL, 0, INDEX_FSAL_GetXAttrValue);
00918     }
00919   else if(xattr_id >= XATTR_COUNT)
00920     {
00921       fsal_path_t lustre_path;
00922       char attr_name[MAXPATHLEN];
00923 
00924       st = fsal_internal_Handle2FidPath(p_context, p_objecthandle, &lustre_path);
00925       if(FSAL_IS_ERROR(st))
00926         ReturnStatus(st, INDEX_FSAL_GetXAttrValue);
00927 
00928       /* get the name for this attr */
00929       rc = xattr_id_to_name(lustre_path.path, xattr_id, attr_name);
00930       if(rc)
00931         Return(rc, errno, INDEX_FSAL_GetXAttrValue);
00932 
00933       rc = lgetxattr(lustre_path.path, attr_name, buffer_addr, buffer_size);
00934       if(rc < 0)
00935         Return(posix2fsal_error(errno), errno, INDEX_FSAL_GetXAttrValue);
00936 
00937       /* the xattr value can be a binary, or a string.
00938        * trying to determine its type...
00939        */
00940       *p_output_size = rc;
00941       xattr_format_value(buffer_addr, p_output_size, buffer_size);
00942 
00943       Return(ERR_FSAL_NO_ERROR, 0, INDEX_FSAL_GetXAttrValue);
00944     }
00945   else                          /* built-in attr */
00946     {
00947       /* get the value */
00948         rc = xattr_list[xattr_id].get_func(p_objecthandle, p_context,
00949                                          buffer_addr, buffer_size,
00950                                          p_output_size, xattr_list[xattr_id].arg);
00951       Return(rc, 0, INDEX_FSAL_GetXAttrValue);
00952     }
00953 
00954 }
00955 
00966 fsal_status_t LUSTREFSAL_GetXAttrIdByName(fsal_handle_t * p_objecthandle, /* IN */
00967                                           const fsal_name_t * xattr_name,       /* IN */
00968                                           fsal_op_context_t * p_context,  /* IN */
00969                                           unsigned int *pxattr_id       /* OUT */
00970     )
00971 {
00972   fsal_status_t st;
00973   unsigned int index;
00974   int rc;
00975   int found = FALSE;
00976 
00977   /* sanity checks */
00978   if(!p_objecthandle || !xattr_name)
00979     Return(ERR_FSAL_FAULT, 0, INDEX_FSAL_GetXAttrValue);
00980 
00981   for(index = 0; index < XATTR_COUNT; index++)
00982     {
00983       if(!strcmp(xattr_list[index].xattr_name, xattr_name->name))
00984         {
00985           found = TRUE;
00986           break;
00987         }
00988     }
00989 
00990   /* search in xattrs */
00991   if(!found)
00992     {
00993       fsal_path_t lustre_path;
00994 
00995       st = fsal_internal_Handle2FidPath(p_context, p_objecthandle, &lustre_path);
00996       if(FSAL_IS_ERROR(st))
00997         ReturnStatus(st, INDEX_FSAL_GetXAttrValue);
00998 
00999       errno = 0;
01000       rc = xattr_name_to_id(lustre_path.path, xattr_name->name);
01001       if(rc < 0)
01002         Return(-rc, errno, INDEX_FSAL_GetXAttrValue);
01003       else
01004         {
01005           index = rc;
01006           found = TRUE;
01007         }
01008     }
01009 
01010   if(found)
01011     {
01012       *pxattr_id = index;
01013       Return(ERR_FSAL_NO_ERROR, 0, INDEX_FSAL_GetXAttrValue);
01014     }
01015   else
01016     Return(ERR_FSAL_NOENT, ENOENT, INDEX_FSAL_GetXAttrValue);
01017 }                               /* FSAL_GetXAttrIdByName */
01018 
01029 fsal_status_t LUSTREFSAL_GetXAttrValueByName(fsal_handle_t * p_objecthandle,      /* IN */
01030                                              const fsal_name_t * xattr_name,    /* IN */
01031                                              fsal_op_context_t * p_context,       /* IN */
01032                                              caddr_t buffer_addr,       /* IN/OUT */
01033                                              size_t buffer_size,        /* IN */
01034                                              size_t * p_output_size     /* OUT */
01035     )
01036 {
01037   unsigned int index;
01038   fsal_attrib_list_t file_attrs;
01039   fsal_status_t st;
01040   fsal_path_t lustre_path;
01041   int rc;
01042 
01043   /* sanity checks */
01044   if(!p_objecthandle || !p_context || !p_output_size || !buffer_addr || !xattr_name)
01045     Return(ERR_FSAL_FAULT, 0, INDEX_FSAL_GetXAttrValue);
01046 
01047   /* get type for checking it */
01048   file_attrs.asked_attributes = FSAL_ATTR_TYPE;
01049 
01050   st = LUSTREFSAL_getattrs(p_objecthandle, p_context, &file_attrs);
01051 
01052   if(FSAL_IS_ERROR(st))
01053     ReturnStatus(st, INDEX_FSAL_GetXAttrValue);
01054 
01055   /* look for this name */
01056 
01057   for(index = 0; index < XATTR_COUNT; index++)
01058     {
01059       if(do_match_type(xattr_list[index].flags, file_attrs.type)
01060          && !strcmp(xattr_list[index].xattr_name, xattr_name->name))
01061         {
01062 
01063           return LUSTREFSAL_GetXAttrValueById(p_objecthandle, index, p_context,
01064                                               buffer_addr, buffer_size, p_output_size);
01065         }
01066     }
01067 
01068   st = fsal_internal_Handle2FidPath(p_context, p_objecthandle, &lustre_path);
01069   if(FSAL_IS_ERROR(st))
01070     ReturnStatus(st, INDEX_FSAL_GetXAttrValue);
01071 
01072   /* is it an xattr? */
01073   rc = lgetxattr(lustre_path.path, xattr_name->name, buffer_addr, buffer_size);
01074   if(rc < 0)
01075     Return(posix2fsal_error(errno), errno, INDEX_FSAL_GetXAttrValue);
01076 
01077   /* the xattr value can be a binary, or a string.
01078    * trying to determine its type...
01079    */
01080   *p_output_size = rc;
01081   xattr_format_value(buffer_addr, p_output_size, buffer_size);
01082 
01083   Return(ERR_FSAL_NO_ERROR, 0, INDEX_FSAL_GetXAttrValue);
01084 }
01085 
01086 static void chomp_attr_value(char *str, size_t size)
01087 {
01088   int len;
01089 
01090   if(str == NULL)
01091     return;
01092 
01093   /* security: set last char to '\0' */
01094   str[size - 1] = '\0';
01095 
01096   len = strnlen(str, size);
01097   if((len > 0) && (str[len - 1] == '\n'))
01098     str[len - 1] = '\0';
01099 }
01100 
01101 fsal_status_t LUSTREFSAL_SetXAttrValue(fsal_handle_t * p_objecthandle,    /* IN */
01102                                        const fsal_name_t * xattr_name,  /* IN */
01103                                        fsal_op_context_t * p_context,     /* IN */
01104                                        caddr_t buffer_addr,     /* IN */
01105                                        size_t buffer_size,      /* IN */
01106                                        int create       /* IN */
01107     )
01108 {
01109   int rc;
01110   fsal_status_t st;
01111   fsal_path_t lustre_path;
01112   size_t len;
01113 
01114   /* remove final '\n', if any */
01115   chomp_attr_value((char *)buffer_addr, buffer_size);
01116 
01117   /* build fid path in lustre */
01118   st = fsal_internal_Handle2FidPath(p_context, p_objecthandle, &lustre_path);
01119   if(FSAL_IS_ERROR(st))
01120     ReturnStatus(st, INDEX_FSAL_SetXAttrValue);
01121 
01122   len = strnlen((char *)buffer_addr, buffer_size);
01123   TakeTokenFSCall();
01124   if(len == 0)
01125     rc = lsetxattr(lustre_path.path, xattr_name->name, "", 1,
01126                    create ? XATTR_CREATE : XATTR_REPLACE);
01127   else
01128     rc = lsetxattr(lustre_path.path, xattr_name->name, (char *)buffer_addr,
01129                    len, create ? XATTR_CREATE : XATTR_REPLACE);
01130 
01131   ReleaseTokenFSCall();
01132   if(rc != 0)
01133     Return(posix2fsal_error(errno), errno, INDEX_FSAL_SetXAttrValue);
01134   else
01135     Return(ERR_FSAL_NO_ERROR, 0, INDEX_FSAL_SetXAttrValue);
01136 }
01137 
01138 fsal_status_t LUSTREFSAL_SetXAttrValueById(fsal_handle_t * p_objecthandle,        /* IN */
01139                                            unsigned int xattr_id,       /* IN */
01140                                            fsal_op_context_t * p_context, /* IN */
01141                                            caddr_t buffer_addr, /* IN */
01142                                            size_t buffer_size   /* IN */
01143     )
01144 {
01145   int rc;
01146   fsal_status_t st;
01147   fsal_path_t lustre_path;
01148   fsal_name_t attr_name;
01149   char name[FSAL_MAX_NAME_LEN];
01150 
01151   if(attr_is_read_only(xattr_id))
01152     Return(ERR_FSAL_PERM, 0, INDEX_FSAL_SetXAttrValue);
01153   else if(xattr_id < XATTR_COUNT)
01154     /* this is not a UDA (setattr not supported) */
01155     Return(ERR_FSAL_PERM, 0, INDEX_FSAL_SetXAttrValue);
01156 
01157   /* build fid path in lustre */
01158   st = fsal_internal_Handle2FidPath(p_context, p_objecthandle, &lustre_path);
01159   if(FSAL_IS_ERROR(st))
01160     ReturnStatus(st, INDEX_FSAL_SetXAttrValue);
01161 
01162   rc = xattr_id_to_name(lustre_path.path, xattr_id, name);
01163   if(rc)
01164     Return(rc, errno, INDEX_FSAL_SetXAttrValue);
01165 
01166   FSAL_str2name(name, FSAL_MAX_NAME_LEN, &attr_name);
01167 
01168   return LUSTREFSAL_SetXAttrValue(p_objecthandle, &attr_name,
01169                                   p_context, buffer_addr, buffer_size, FALSE);
01170 }
01171 
01179 fsal_status_t LUSTREFSAL_RemoveXAttrById(fsal_handle_t * p_objecthandle,  /* IN */
01180                                          fsal_op_context_t * p_context,   /* IN */
01181                                          unsigned int xattr_id) /* IN */
01182 {
01183   int rc;
01184   fsal_status_t st;
01185   fsal_path_t lustre_path;
01186   char name[FSAL_MAX_NAME_LEN];
01187 
01188   st = fsal_internal_Handle2FidPath(p_context, p_objecthandle, &lustre_path);
01189   if(FSAL_IS_ERROR(st))
01190     ReturnStatus(st, INDEX_FSAL_SetXAttrValue);
01191 
01192   rc = xattr_id_to_name(lustre_path.path, xattr_id, name);
01193   if(rc)
01194     Return(rc, errno, INDEX_FSAL_SetXAttrValue);
01195 
01196   TakeTokenFSCall();
01197   rc = lremovexattr(lustre_path.path, name);
01198   ReleaseTokenFSCall();
01199 
01200   if(rc != 0)
01201     ReturnCode(posix2fsal_error(errno), errno);
01202 
01203   ReturnCode(ERR_FSAL_NO_ERROR, 0);
01204 }                               /* FSAL_RemoveXAttrById */
01205 
01213 fsal_status_t LUSTREFSAL_RemoveXAttrByName(fsal_handle_t * p_objecthandle,        /* IN */
01214                                            fsal_op_context_t * p_context, /* IN */
01215                                            const fsal_name_t * xattr_name)      /* IN */
01216 {
01217   int rc;
01218   fsal_status_t st;
01219   fsal_path_t lustre_path;
01220 
01221   st = fsal_internal_Handle2FidPath(p_context, p_objecthandle, &lustre_path);
01222   if(FSAL_IS_ERROR(st))
01223     ReturnStatus(st, INDEX_FSAL_SetXAttrValue);
01224 
01225   TakeTokenFSCall();
01226   rc = lremovexattr(lustre_path.path, xattr_name->name);
01227   ReleaseTokenFSCall();
01228 
01229   if(rc != 0)
01230     ReturnCode(posix2fsal_error(errno), errno);
01231 
01232   ReturnCode(ERR_FSAL_NO_ERROR, 0);
01233 }                               /* FSAL_RemoveXAttrById */
01234 
01235 int LUSTREFSAL_GetXattrOffsetSetable( void )
01236 {
01237   return XATTR_COUNT ;
01238 }