/**
 *
 * MFSL_unlink : posts an asynchronous unlink and sets the cached attributes in return.
 *
 * Posts an asynchronous unlink and sets the cached attributes in return.
 * If an object is not asynchronous, then the content of object attributes structure for result will be used to populate it.
 *
 * @param parentdir_handle  [IN]    mfsl object to be operated on (source directory for the unlink)
 * @param p_object_name     [IN]    name of the object to be destroyed
 * @param p_context         [IN]    associated fsal context
 * @param p_mfsl_context    [INOUT] associated mfsl context
 * @param parentdir_attributes    [INOUT] resulting attributes for directory 
 *
 * @return the same as FSAL_unlink
 */
fsal_status_t MFSL_unlink(mfsl_object_t * dir_handle,   /* IN */
                          fsal_name_t * p_object_name,  /* IN */
                          mfsl_object_t * object_handle,        /* INOUT */
                          fsal_op_context_t * p_context,        /* IN */
                          mfsl_context_t * p_mfsl_context,      /* IN */
                          fsal_attrib_list_t * dir_attributes /* [ IN/OUT ] */ )
{
  fsal_status_t fsal_status;
  mfsl_async_op_desc_t *pasyncopdesc = NULL;
  mfsl_object_specific_data_t *dir_pasyncdata = NULL;
  mfsl_object_specific_data_t *obj_pasyncdata = NULL;

  GetFromPool(pasyncopdesc, &p_mfsl_context->pool_async_op, mfsl_async_op_desc_t);

  if(pasyncopdesc == NULL)
    MFSL_return(ERR_FSAL_INVAL, 0);

  if(gettimeofday(&pasyncopdesc->op_time, NULL) != 0)
    {
      /* Could'not get time of day... Stopping, this may need a major failure */
      LogMajor(COMPONENT_MFSL, "MFSL_link: cannot get time of day... exiting");
      exit(1);
    }

  if(!mfsl_async_get_specdata(dir_handle, &dir_pasyncdata))
    {
      /* Target is not yet asynchronous */

      GetFromPool(dir_pasyncdata, &p_mfsl_context->pool_spec_data, mfsl_object_specific_data_t);

      /* In this case use object_attributes parameter to initiate asynchronous object */
      dir_pasyncdata->async_attr = *dir_attributes;
    }

  fsal_status = MFSAL_unlink_check_perms(dir_handle,
                                         dir_pasyncdata,
                                         p_object_name,
                                         dir_attributes, p_context, p_mfsl_context);

  if(FSAL_IS_ERROR(fsal_status))
    return fsal_status;

  LogDebug(COMPONENT_MFSL,  "Creating asyncop %p",
                    pasyncopdesc);

  pasyncopdesc->op_type = MFSL_ASYNC_OP_REMOVE;

  pasyncopdesc->op_args.remove.pmobject = dir_handle;
  pasyncopdesc->op_args.remove.name = *p_object_name;
  pasyncopdesc->op_res.remove.attr = *dir_attributes;

  pasyncopdesc->op_func = MFSL_unlink_async_op;
  pasyncopdesc->fsal_op_context = *p_context;

  pasyncopdesc->ptr_mfsl_context = (caddr_t) p_mfsl_context;

  fsal_status = MFSL_async_post(pasyncopdesc);
  if(FSAL_IS_ERROR(fsal_status))
    return fsal_status;

  /* Update the asynchronous metadata */
  dir_pasyncdata->async_attr.ctime.seconds = pasyncopdesc->op_time.tv_sec;
  dir_pasyncdata->async_attr.ctime.nseconds = pasyncopdesc->op_time.tv_usec;  /** @todo: there may be a coefficient to be applied here */
  dir_handle->health = MFSL_ASYNC_ASYNCHRONOUS;

  if(!mfsl_async_set_specdata(dir_handle, dir_pasyncdata))
    MFSL_return(ERR_FSAL_SERVERFAULT, 0);

  if(!mfsl_async_get_specdata(object_handle, &obj_pasyncdata))
    {
      /* The object to be deleted is not asynchronous, but it has
       * has to become asynchronous to be correctly managed until the FSAL deletes it */
      GetFromPool(obj_pasyncdata, &p_mfsl_context->pool_spec_data, mfsl_object_specific_data_t);

      /* Possible bug here with getattr because it has not data */
    }

  /* Depending on the value of numlinks, the object should be deleted or not */
  if((obj_pasyncdata->async_attr.numlinks > 1)
     && (obj_pasyncdata->async_attr.type == FSAL_TYPE_FILE))
    obj_pasyncdata->async_attr.numlinks -= 1;
  else
    obj_pasyncdata->deleted = TRUE;

  if(!mfsl_async_set_specdata(object_handle, obj_pasyncdata))
    MFSL_return(ERR_FSAL_SERVERFAULT, 0);

  /* Return the correct attributes */
  *dir_attributes = dir_pasyncdata->async_attr;

  MFSL_return(ERR_FSAL_NO_ERROR, 0);
}                               /* MFSL_unlink */
/**
 *
 * MFSL_setattrs : posts an asynchronous setattr and sets the cached attributes in return.
 *
 * Posts an asynchronous setattr and sets the cached attributes in return.
 * If the object is not asynchronous, then the content of object attributes will be used to populate it.
 *
 * @param filehandle        [IN]    mfsl object to be operated on.
 * @param p_context         [IN]    associated fsal context
 * @param p_mfsl_context    [INOUT] associated mfsl context
 * @param attrib_set        [IN]    attributes to be set 
 * @param object_attributes [INOUT] resulting attributes
 *
 * @return the same as FSAL_setattrs
 */
fsal_status_t MFSL_setattrs(mfsl_object_t * filehandle, /* IN */
                            fsal_op_context_t * p_context,      /* IN */
                            mfsl_context_t * p_mfsl_context,    /* IN */
                            fsal_attrib_list_t * attrib_set,    /* IN */
                            fsal_attrib_list_t * object_attributes,      /* [ IN/OUT ] */
			    void * pextra
    )
{
  fsal_status_t fsal_status;
  mfsl_async_op_desc_t *pasyncopdesc = NULL;
  mfsl_object_specific_data_t *pasyncdata = NULL;

  P(p_mfsl_context->lock);

  GetFromPool(pasyncopdesc, &p_mfsl_context->pool_async_op, mfsl_async_op_desc_t);

  V(p_mfsl_context->lock);

  if(pasyncopdesc == NULL)
    MFSL_return(ERR_FSAL_INVAL, 0);

  if(gettimeofday(&pasyncopdesc->op_time, NULL) != 0)
    {
      /* Could'not get time of day... Stopping, this may need a major failure */
      LogMajor(COMPONENT_MFSL, "MFSL_setattrs: cannot get time of day... exiting");
      exit(1);
    }

  /* Is the object asynchronous ? */
  if(!mfsl_async_get_specdata(filehandle, &pasyncdata))
    {
      /* Not yet asynchronous object */
      P(p_mfsl_context->lock);

      GetFromPool(pasyncdata, &p_mfsl_context->pool_spec_data, mfsl_object_specific_data_t);

      V(p_mfsl_context->lock);

      /* In this case use object_attributes parameter to initiate asynchronous object */
      pasyncdata->async_attr = *object_attributes;
    }

  fsal_status =
      MFSL_setattrs_check_perms(filehandle, pasyncdata, p_context, p_mfsl_context,
                                attrib_set);

  if(FSAL_IS_ERROR(fsal_status))
    return fsal_status;

  LogDebug(COMPONENT_MFSL,  "Creating asyncop %p",
                    pasyncopdesc);

  pasyncopdesc->op_type = MFSL_ASYNC_OP_SETATTR;
  pasyncopdesc->op_mobject = filehandle;
  pasyncopdesc->op_args.setattr.pmobject = filehandle;
  pasyncopdesc->op_args.setattr.attr = *attrib_set;
  pasyncopdesc->op_res.setattr.attr = *attrib_set;

  pasyncopdesc->op_func = MFSL_setattr_async_op;
  pasyncopdesc->fsal_op_context = *p_context;

  pasyncopdesc->ptr_mfsl_context = (caddr_t) p_mfsl_context;

  fsal_status = MFSL_async_post(pasyncopdesc);
  if(FSAL_IS_ERROR(fsal_status))
    return fsal_status;

  /* Update the associated times for this object */
  pasyncdata->async_attr.ctime.seconds = pasyncopdesc->op_time.tv_sec;
  pasyncdata->async_attr.ctime.nseconds = pasyncopdesc->op_time.tv_usec;  /** @todo: there may be a coefficient to be applied here */
  filehandle->health = MFSL_ASYNC_ASYNCHRONOUS;

  /* merge the attributes to the asynchronous attributes */
  if((attrib_set->asked_attributes & FSAL_ATTR_SIZE) ||
     (attrib_set->asked_attributes & FSAL_ATTR_SPACEUSED))
    {
      /* Operation on a non data cached file */
      pasyncdata->async_attr.filesize = attrib_set->filesize;
      pasyncdata->async_attr.spaceused = attrib_set->spaceused;
    }

  if(attrib_set->asked_attributes & (FSAL_ATTR_MODE | FSAL_ATTR_OWNER | FSAL_ATTR_GROUP))
    {
      if(attrib_set->asked_attributes & FSAL_ATTR_MODE)
        pasyncdata->async_attr.mode = attrib_set->mode;

      if(attrib_set->asked_attributes & FSAL_ATTR_OWNER)
        pasyncdata->async_attr.owner = attrib_set->owner;

      if(attrib_set->asked_attributes & FSAL_ATTR_GROUP)
        pasyncdata->async_attr.group = attrib_set->group;
    }

  if(attrib_set->asked_attributes & (FSAL_ATTR_ATIME | FSAL_ATTR_MTIME))
    {
      if(attrib_set->asked_attributes & FSAL_ATTR_ATIME)
        pasyncdata->async_attr.atime = attrib_set->atime;

      if(attrib_set->asked_attributes & FSAL_ATTR_MTIME)
        pasyncdata->async_attr.mtime = attrib_set->mtime;
    }

  /* Set output attributes */
  *object_attributes = pasyncdata->async_attr;

  if(!mfsl_async_set_specdata(filehandle, pasyncdata))
    MFSL_return(ERR_FSAL_SERVERFAULT, 0);

  MFSL_return(ERR_FSAL_NO_ERROR, 0);
}                               /* MFSL_setattr */
/**
 *
 * MFSL_mkdir : posts an asynchronous mkdir and sets the cached attributes in return.
 *
 * Posts an asynchronous setattr and sets the cached attributes in return.
 * If an object is not asynchronous, then the content of object attributes structure for result will be used to populate it.
 *
 * @param target_handle     [IN]    mfsl object to be operated on (object to be hard linked).
 * @param dir_handle        [IN]    mfsl object to be operated on (destination directory for the link).
 * @param p_context         [IN]    associated fsal context
 * @param p_mfsl_context    [INOUT] associated mfsl context
 * @param attrib_set        [IN]    attributes to be set 
 * @param tgt_attributes    [INOUT] resulting attributes for target
 * @param dir_attributes    [INOUT] resulting attributes for directory 
 *
 * @return the same as FSAL_link
 */
fsal_status_t MFSL_mkdir(mfsl_object_t * parent_directory_handle,       /* IN */
                         fsal_name_t * p_dirname,       /* IN */
                         fsal_op_context_t * p_context, /* IN */
                         mfsl_context_t * p_mfsl_context,       /* IN */
                         fsal_accessmode_t accessmode,  /* IN */
                         mfsl_object_t * object_handle, /* OUT */
                         fsal_attrib_list_t * object_attributes,        /* [ IN/OUT ] */
                         fsal_attrib_list_t * parent_attributes /* IN */ )
{
  fsal_status_t fsal_status;
  mfsl_async_op_desc_t *pasyncopdesc = NULL;
  mfsl_object_specific_data_t *newdir_pasyncdata = NULL;
  mfsl_object_t *pnewdir_handle = NULL;
  mfsl_precreated_object_t *pprecreated = NULL;

  fsal_status = MFSAL_mkdir_check_perms(parent_directory_handle,
                                        p_dirname,
                                        p_context, p_mfsl_context, parent_attributes);

  if(FSAL_IS_ERROR(fsal_status))
    return fsal_status;

  P(p_mfsl_context->lock);

  GetFromPool(pasyncopdesc, &p_mfsl_context->pool_async_op, mfsl_async_op_desc_t);

  GetFromPool(newdir_pasyncdata, &p_mfsl_context->pool_spec_data, mfsl_object_specific_data_t);

  V(p_mfsl_context->lock);

  if(pasyncopdesc == NULL)
    MFSL_return(ERR_FSAL_INVAL, 0);

  if(gettimeofday(&pasyncopdesc->op_time, NULL) != 0)
    {
      /* Could'not get time of day... Stopping, this may need a major failure */
      LogMajor(COMPONENT_MFSL, "MFSL_link: cannot get time of day... exiting");
      exit(1);
    }

  /* Now get a pre-allocated directory from the synclet data */
  P(p_mfsl_context->lock);
  GetFromPool(pprecreated, &p_mfsl_context->pool_dirs, mfsl_precreated_object_t);
  V(p_mfsl_context->lock);

  pnewdir_handle = &(pprecreated->mobject);

  LogDebug(COMPONENT_MFSL,  "Creating asyncop %p",
                    pasyncopdesc);

  pasyncopdesc->op_type = MFSL_ASYNC_OP_MKDIR;
  pasyncopdesc->op_args.mkdir.pmfsl_obj_dirdest = parent_directory_handle;
  pasyncopdesc->op_args.mkdir.precreate_name = pprecreated->name;
  pasyncopdesc->op_args.mkdir.dirname = *p_dirname;
  pasyncopdesc->op_args.mkdir.mode = accessmode;
  pasyncopdesc->op_args.mkdir.owner = FSAL_OP_CONTEXT_TO_UID(p_context);
  pasyncopdesc->op_args.mkdir.group = FSAL_OP_CONTEXT_TO_GID(p_context);
  pasyncopdesc->op_res.mkdir.attr.asked_attributes = object_attributes->asked_attributes;
  pasyncopdesc->op_res.mkdir.attr.supported_attributes =
      object_attributes->supported_attributes;

  if(FSAL_IS_ERROR(fsal_status))
    return fsal_status;

  pasyncopdesc->op_func = MFSL_mkdir_async_op;
  //pasyncopdesc->fsal_op_context = p_context ;
  pasyncopdesc->fsal_op_context =
      synclet_data[pasyncopdesc->related_synclet_index].root_fsal_context;

  pasyncopdesc->ptr_mfsl_context = (caddr_t) p_mfsl_context;

  fsal_status = MFSL_async_post(pasyncopdesc);
  if(FSAL_IS_ERROR(fsal_status))
    return fsal_status;

  /* Update the asynchronous metadata */
  newdir_pasyncdata->async_attr = pprecreated->attr;

  newdir_pasyncdata->async_attr.type = FSAL_TYPE_DIR;
  newdir_pasyncdata->async_attr.filesize = DEV_BSIZE;
  newdir_pasyncdata->async_attr.spaceused = DEV_BSIZE;
  newdir_pasyncdata->async_attr.numlinks = 2;

  newdir_pasyncdata->async_attr.owner = pasyncopdesc->op_args.mkdir.owner;
  newdir_pasyncdata->async_attr.group = pasyncopdesc->op_args.mkdir.group;

  newdir_pasyncdata->async_attr.ctime.seconds = pasyncopdesc->op_time.tv_sec;
  newdir_pasyncdata->async_attr.ctime.nseconds = pasyncopdesc->op_time.tv_usec;  /** @todo: there may be a coefficient to be applied here */

  newdir_pasyncdata->deleted = FALSE;

  if(!mfsl_async_set_specdata(pnewdir_handle, newdir_pasyncdata))
    MFSL_return(ERR_FSAL_SERVERFAULT, 0);

  /* Return the correct attributes */
  *object_attributes = newdir_pasyncdata->async_attr;
  *object_handle = pprecreated->mobject;
  object_handle->health = MFSL_ASYNC_NEVER_SYNCED;

  /* Do not forget that the parent directory becomes asynchronous too */
  parent_directory_handle->health = MFSL_ASYNC_ASYNCHRONOUS;

  MFSL_return(ERR_FSAL_NO_ERROR, 0);
}                               /* MFSL_mkdir */
/**
 *
 * MFSL_truncate : posts an asynchronous truncate and sets the cached attributes in return.
 *
 * Posts an asynchronous truncate and sets the cached attributes in return.
 * If the object is not asynchronous, then the content of object attributes will be used to populate it.
 *
 * @param filehandle        [IN]    mfsl object to be operated on.
 * @param p_context         [IN]    associated fsal context
 * @param p_mfsl_context    [INOUT] associated mfsl context
 * @param size              [IN]    new size
 * @param file_descriptor   [UNUSED] should be removed as stateful FSAL_truncate is removed from FSAL_PROXY
 * @param object_attributes [INOUT] resulting attributes
 *
 * @return the same as FSAL_truncate
 */
fsal_status_t MFSL_truncate(mfsl_object_t * filehandle, /* IN */
                            fsal_op_context_t * p_context,      /* IN */
                            mfsl_context_t * p_mfsl_context,    /* IN */
                            fsal_size_t length, fsal_file_t * file_descriptor,  /* INOUT */
                            fsal_attrib_list_t * object_attributes      /* [ IN/OUT ] */
    )
{
  fsal_status_t fsal_status;
  mfsl_async_op_desc_t *pasyncopdesc = NULL;
  mfsl_object_specific_data_t *pasyncdata = NULL;

  P(p_mfsl_context->lock);

  GetFromPool(pasyncopdesc, &p_mfsl_context->pool_async_op, mfsl_async_op_desc_t);

  V(p_mfsl_context->lock);

  if(pasyncopdesc == NULL)
    MFSL_return(ERR_FSAL_INVAL, 0);

  if(gettimeofday(&pasyncopdesc->op_time, NULL) != 0)
    {
      /* Could'not get time of day... Stopping, this may need a major failure */
      LogMajor(COMPONENT_MFSL, "MFSL_truncate: cannot get time of day... exiting");
      exit(1);
    }

  /* Is the object asynchronous ? */
  if(!mfsl_async_get_specdata(filehandle, &pasyncdata))
    {
      /* Not yet asynchronous object */
      P(p_mfsl_context->lock);

      GetFromPool(pasyncdata, &p_mfsl_context->pool_spec_data, mfsl_object_specific_data_t);

      V(p_mfsl_context->lock);

      /* In this case use object_attributes parameter to initiate asynchronous object */
      pasyncdata->async_attr = *object_attributes;
    }

  fsal_status =
      MFSAL_truncate_check_perms(filehandle, pasyncdata, p_context, p_mfsl_context);

  if(FSAL_IS_ERROR(fsal_status))
    return fsal_status;

  LogDebug(COMPONENT_MFSL,  "Creating asyncop %p",
                    pasyncopdesc);

  pasyncopdesc->op_type = MFSL_ASYNC_OP_TRUNCATE;
  pasyncopdesc->op_mobject = filehandle;
  pasyncopdesc->op_args.truncate.pmobject = filehandle;
  pasyncopdesc->op_args.truncate.size = length;
  pasyncopdesc->op_res.truncate.attr = *object_attributes;

  pasyncopdesc->op_func = MFSL_truncate_async_op;
  pasyncopdesc->fsal_op_context = *p_context;

  pasyncopdesc->ptr_mfsl_context = (caddr_t) p_mfsl_context;

  fsal_status = MFSL_async_post(pasyncopdesc);
  if(FSAL_IS_ERROR(fsal_status))
    return fsal_status;

  /* Update the associated times for this object */
  pasyncdata->async_attr = *object_attributes;
  pasyncdata->async_attr.ctime.seconds = pasyncopdesc->op_time.tv_sec;
  pasyncdata->async_attr.ctime.nseconds = pasyncopdesc->op_time.tv_usec;  /** @todo: there may be a coefficient to be applied here */
  filehandle->health = MFSL_ASYNC_ASYNCHRONOUS;

  /* Set output attributes */
  *object_attributes = pasyncdata->async_attr;

  if(!mfsl_async_set_specdata(filehandle, pasyncdata))
    MFSL_return(ERR_FSAL_SERVERFAULT, 0);

  MFSL_return(ERR_FSAL_NO_ERROR, 0);
}                               /* MFSL_truncate */
fsal_status_t MFSL_symlink(mfsl_object_t * parent_directory_handle,     /* IN */
                           fsal_name_t * p_linkname,    /* IN */
                           fsal_path_t * p_linkcontent, /* IN */
                           fsal_op_context_t * p_context,       /* IN */
                           mfsl_context_t * p_mfsl_context,     /* IN */
                           fsal_accessmode_t accessmode,        /* IN (ignored); */
                           mfsl_object_t * link_handle, /* OUT */
                           fsal_attrib_list_t * link_attributes /* [ IN/OUT ] */ )
{
  fsal_status_t fsal_status;
  mfsl_async_op_desc_t *pasyncopdesc = NULL;
  mfsl_object_specific_data_t *symlink_pasyncdata = NULL;
  mfsl_object_t *psymlink_handle = NULL;
  fsal_name_t tmp_fsal_name;
  char tmp_name[MAXNAMLEN];
  static unsigned int counter = 0;

  snprintf(tmp_name, MAXNAMLEN, "%s.%u", p_linkname->name, counter);
  counter += 1;

  if(FSAL_IS_ERROR(FSAL_str2name(tmp_name, MAXNAMLEN, &tmp_fsal_name)))
    return fsal_status;

  fsal_status = MFSAL_symlink_check_perms(parent_directory_handle,
                                          p_linkname,
                                          p_context, p_mfsl_context, link_attributes);

  if(FSAL_IS_ERROR(fsal_status))
    return fsal_status;

  P(parent_directory_handle->lock);
  fsal_status = FSAL_symlink(&tmp_symlink_dirhandle,
                             &tmp_fsal_name,
                             p_linkcontent,
                             p_context,
                             accessmode, &link_handle->handle, link_attributes);
  V(parent_directory_handle->lock);

  P(p_mfsl_context->lock);

  GET_PREALLOC(pasyncopdesc,
               p_mfsl_context->pool_async_op,
               mfsl_param.nb_pre_async_op_desc, mfsl_async_op_desc_t, next_alloc);

  GET_PREALLOC(symlink_pasyncdata,
               p_mfsl_context->pool_spec_data,
               mfsl_param.nb_pre_async_op_desc, mfsl_object_specific_data_t, next_alloc);

  V(p_mfsl_context->lock);

  if(pasyncopdesc == NULL)
    MFSL_return(ERR_FSAL_INVAL, 0);

  if(gettimeofday(&pasyncopdesc->op_time, NULL) != 0)
    {
      /* Could'not get time of day... Stopping, this may need a major failure */
      LogMajor(COMPONENT_MFSL, "MFSL_synlink: cannot get time of day... exiting");
      exit(1);
    }

  LogDebug(COMPONENT_MFSL,  "Creating asyncop %p",
                    pasyncopdesc);

  pasyncopdesc->op_type = MFSL_ASYNC_OP_SYMLINK;

  pasyncopdesc->op_args.symlink.pmobject_dirdest = parent_directory_handle;
  pasyncopdesc->op_args.symlink.precreate_name = tmp_fsal_name;
  pasyncopdesc->op_args.symlink.linkname = *p_linkname;

  pasyncopdesc->op_res.symlink.attr.asked_attributes = link_attributes->asked_attributes;
  pasyncopdesc->op_res.symlink.attr.supported_attributes =
      link_attributes->supported_attributes;

  pasyncopdesc->ptr_mfsl_context = (caddr_t) p_mfsl_context;

  if(FSAL_IS_ERROR(fsal_status))
    return fsal_status;

  pasyncopdesc->op_func = MFSL_symlink_async_op;
  //pasyncopdesc->fsal_op_context = p_context ;
  pasyncopdesc->fsal_op_context =
      synclet_data[pasyncopdesc->related_synclet_index].root_fsal_context;

  fsal_status = MFSL_async_post(pasyncopdesc);
  if(FSAL_IS_ERROR(fsal_status))
    return fsal_status;

  /* Update the asynchronous metadata */
  symlink_pasyncdata->async_attr = *link_attributes;
  symlink_pasyncdata->deleted = FALSE;

  if(!mfsl_async_set_specdata(link_handle, symlink_pasyncdata))
    MFSL_return(ERR_FSAL_SERVERFAULT, 0);

  /* Return the correct attributes */
  link_handle->health = MFSL_ASYNC_NEVER_SYNCED;

  /* Do not forget that the parent directory becomes asynchronous too */
  parent_directory_handle->health = MFSL_ASYNC_ASYNCHRONOUS;

  MFSL_return(ERR_FSAL_NO_ERROR, 0);
}                               /* MFSL_symlink */