fsal_status_t MFSL_link(mfsl_object_t * target_handle, /* IN */ mfsl_object_t * dir_handle, /* IN */ fsal_name_t * p_link_name, /* IN */ fsal_op_context_t * p_context, /* IN */ mfsl_context_t * p_mfsl_context, /* IN */ fsal_attrib_list_t * attributes, /* [ IN/OUT ] */ void * pextra ) { return FSAL_link(&target_handle->handle, &dir_handle->handle, p_link_name, p_context, attributes); } /* MFSL_link */
fsal_status_t MFSL_link(mfsl_object_t * target_handle, /* IN */ mfsl_object_t * dir_handle, /* IN */ fsal_name_t * p_link_name, /* IN */ fsal_op_context_t * p_context, /* IN */ mfsl_context_t * p_mfsl_context, /* IN */ fsal_attrib_list_t * attributes, /* [ IN/OUT ] */ void * pextra ) { struct timeval start, stop, delta ; fsal_status_t fsal_status = { ERR_FSAL_NO_ERROR, 0 } ; gettimeofday( &start, 0 ) ; fsal_status = FSAL_link(&target_handle->handle, &dir_handle->handle, p_link_name, p_context, attributes); gettimeofday( &stop, 0 ) ; delta = mfsl_timer_diff( &stop, &start ) ; LogFullDebug( COMPONENT_MFSL, "%s: duration=%ld.%06ld", __FUNCTION__, delta.tv_sec, delta.tv_usec ) ; return fsal_status ; } /* MFSL_link */
/** * * cache_inode_link: hardlinks a pentry to another. * * Hard links a pentry to another. This is basically a equivalent of FSAL_link in the cache inode layer. * * @param pentry_src [IN] entry pointer the entry to be linked. This can't be a directory. * @param pentry_dir_dest [INOUT] entry pointer for the destination directory in which the link will be created. * @param plink_name [IN] pointer to the name of the object in the destination directory. * @param pattr [OUT] attributes for the linked attributes after the operation. * @param ht [INOUT] hash table used for the cache. * @param pclient [INOUT] ressource allocated by the client for the nfs management. * @param pcontext [IN] FSAL credentials * @param pstatus [OUT] returned status. * * @return CACHE_INODE_SUCCESS if operation is a success \n * @return CACHE_INODE_LRU_ERROR if allocation error occured when validating the entry\n * @return CACHE_INODE_BAD_TYPE either source or destination have incorrect type\n * @return CACHE_INODE_ENTRY_EXISTS entry of that name already exists in destination. * */ cache_inode_status_t cache_inode_link(cache_entry_t * pentry_src, cache_entry_t * pentry_dir_dest, fsal_name_t * plink_name, fsal_attrib_list_t * pattr, hash_table_t * ht, cache_inode_client_t * pclient, fsal_op_context_t * pcontext, cache_inode_status_t * pstatus) { fsal_status_t fsal_status; fsal_handle_t handle_src; fsal_handle_t handle_dest; fsal_attrib_list_t link_attributes; #ifdef _USE_MFSL fsal_attrib_list_t dirdest_attributes; #endif cache_inode_status_t status; cache_entry_t *pentry_lookup = NULL; fsal_attrib_list_t lookup_attributes; fsal_size_t save_size = 0; fsal_size_t save_spaceused = 0; fsal_time_t save_mtime = { .seconds = 0, .nseconds = 0 }; fsal_accessflags_t access_mask = 0; /* Set the return default to CACHE_INODE_SUCCESS */ *pstatus = CACHE_INODE_SUCCESS; /* stats */ pclient->stat.nb_call_total += 1; pclient->stat.func_stats.nb_call[CACHE_INODE_LINK] += 1; /* Is the destination a directory ? */ if(pentry_dir_dest->internal_md.type != DIR_BEGINNING && pentry_dir_dest->internal_md.type != DIR_CONTINUE) { /* Bad type .... */ *pstatus = CACHE_INODE_BAD_TYPE; pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LINK] += 1; return *pstatus; } /* Check if caller is allowed to perform the operation */ access_mask = FSAL_MODE_MASK_SET(FSAL_W_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_ADD_FILE); if((status = cache_inode_access(pentry_dir_dest, access_mask, ht, pclient, pcontext, &status)) != CACHE_INODE_SUCCESS) { *pstatus = status; /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LINK] += 1; /* pentry is a directory */ return *pstatus; } /* Check if an entry of the same name doesn't exist in the destination directory */ if((pentry_lookup = cache_inode_lookup(pentry_dir_dest, plink_name, &lookup_attributes, ht, pclient, pcontext, pstatus)) != NULL) { /* There exists such an entry... */ *pstatus = CACHE_INODE_ENTRY_EXISTS; pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LINK] += 1; return *pstatus; } /* The pentry to be hardlinked can't be a DIR_BEGINNING or a DIR_CONTINUE */ if(pentry_src->internal_md.type == DIR_BEGINNING || pentry_src->internal_md.type == DIR_CONTINUE) { /* Bad type .... */ *pstatus = CACHE_INODE_BAD_TYPE; pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LINK] += 1; return *pstatus; } #if 0 if( pentry_src->internal_md.type == REGULAR_FILE ) printf( "=== link === %p | inode=%llu\n", pentry_src, pentry_src->object.file.attributes.fileid ) ; #endif /* At this point, we know that the entry does not exist in destination directory, we know that the * destination is actually a directory and that the source is no directory */ /* Lock the source */ P_w(&pentry_src->lock); /* Lock the target dir */ P_w(&pentry_dir_dest->lock); /* Get the handles */ switch (pentry_src->internal_md.type) { case REGULAR_FILE: handle_src = pentry_src->object.file.handle; break; case SYMBOLIC_LINK: handle_src = pentry_src->object.symlink.handle; break; case FS_JUNCTION: case DIR_BEGINNING: handle_src = pentry_src->object.dir_begin.handle; break; case DIR_CONTINUE: /* lock the related dir_begin (dir begin are garbagge collected AFTER their related dir_cont) * this means that if a DIR_CONTINUE exists, its pdir pointer is not endless */ P_r(&pentry_src->object.dir_cont.pdir_begin->lock); handle_src = pentry_src->object.dir_cont.pdir_begin->object.dir_begin.handle; V_r(&pentry_src->object.dir_cont.pdir_begin->lock); break; case CHARACTER_FILE: case BLOCK_FILE: case SOCKET_FILE: case FIFO_FILE: handle_src = pentry_src->object.special_obj.handle; break; case UNASSIGNED: case RECYCLED: LogCrit(COMPONENT_CACHE_INODE, "WARNING: unknown source pentry type: internal_md.type=%d, line %d in file %s", pentry_src->internal_md.type, __LINE__, __FILE__); *pstatus = CACHE_INODE_BAD_TYPE; pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LINK] += 1; return *pstatus; } switch (pentry_dir_dest->internal_md.type) { case FS_JUNCTION: case DIR_BEGINNING: handle_dest = pentry_dir_dest->object.dir_begin.handle; break; case DIR_CONTINUE: /* lock the related dir_begin (dir begin are garbagge collected AFTER their related dir_cont) * this means that if a DIR_CONTINUE exists, its pdir pointer is not endless */ P_r(&pentry_dir_dest->object.dir_cont.pdir_begin->lock); handle_dest = pentry_src->object.dir_cont.pdir_begin->object.dir_begin.handle; V_r(&pentry_dir_dest->object.dir_cont.pdir_begin->lock); break; default: LogCrit(COMPONENT_CACHE_INODE, "WARNING: unknown source pentry type: internal_md.type=%d, line %d in file %s", pentry_src->internal_md.type, __LINE__, __FILE__); *pstatus = CACHE_INODE_BAD_TYPE; pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LINK] += 1; return *pstatus; } /* If object is a data cached regular file, keeps it mtime and size, STEP 1 */ if((pentry_src->internal_md.type == REGULAR_FILE) && (pentry_src->object.file.pentry_content != NULL)) { save_mtime = pentry_src->object.file.attributes.mtime; save_size = pentry_src->object.file.attributes.filesize; save_spaceused = pentry_src->object.file.attributes.spaceused; } /* Do the link at FSAL level */ link_attributes.asked_attributes = pclient->attrmask; #ifdef _USE_MFSL cache_inode_get_attributes(pentry_src, &link_attributes); cache_inode_get_attributes(pentry_dir_dest, &dirdest_attributes); fsal_status = MFSL_link(&pentry_src->mobject, &pentry_dir_dest->mobject, plink_name, pcontext, &pclient->mfsl_context, &link_attributes, NULL); #else fsal_status = FSAL_link(&handle_src, &handle_dest, plink_name, pcontext, &link_attributes); #endif if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); V_w(&pentry_dir_dest->lock); V_w(&pentry_src->lock); if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; fsal_status_t getattr_status; LogEvent(COMPONENT_CACHE_INODE, "cache_inode_link: Stale FSAL File Handle detected for at least one in pentry = %p and pentry = %p", pentry_src, pentry_dir_dest); /* Use FSAL_getattrs to find which entry is staled */ getattr_status = FSAL_getattrs(&handle_src, pcontext, &link_attributes); if(getattr_status.major == ERR_FSAL_ACCESS) { LogEvent(COMPONENT_CACHE_INODE, "cache_inode_link: Stale FSAL File Handle detected for pentry = %p", pentry_src); if(cache_inode_kill_entry(pentry_src, ht, pclient, &kill_status) != CACHE_INODE_SUCCESS) LogCrit(COMPONENT_CACHE_INODE, "cache_inode_link: Could not kill entry %p, status = %u", pentry_src, kill_status); } getattr_status = FSAL_getattrs(&handle_dest, pcontext, &link_attributes); if(getattr_status.major == ERR_FSAL_ACCESS) { LogEvent(COMPONENT_CACHE_INODE, "cache_inode_link: Stale FSAL File Handle detected for pentry = %p", pentry_dir_dest); if(cache_inode_kill_entry(pentry_dir_dest, ht, pclient, &kill_status) != CACHE_INODE_SUCCESS) LogCrit(COMPONENT_CACHE_INODE, "cache_inode_link: Could not kill entry %p, status = %u", pentry_dir_dest, kill_status); } } *pstatus = CACHE_INODE_FSAL_ESTALE; return *pstatus; } /* If object is a data cached regular file, keeps it mtime and size, STEP 2 */ if((pentry_src->internal_md.type == REGULAR_FILE) && (pentry_src->object.file.pentry_content != NULL)) { link_attributes.mtime = save_mtime; link_attributes.filesize = save_size; link_attributes.spaceused = save_spaceused; } /* Update cached attributes */ cache_inode_set_attributes(pentry_src, &link_attributes); /* Add the new entry in the destination directory */ if(cache_inode_add_cached_dirent(pentry_dir_dest, plink_name, pentry_src, NULL, ht, pclient, pcontext, &status) != CACHE_INODE_SUCCESS) { V_w(&pentry_dir_dest->lock); V_w(&pentry_src->lock); return *pstatus; } /* Regular exit */ /* return the attributes */ *pattr = link_attributes; /* Validate the entries */ *pstatus = cache_inode_valid(pentry_src, CACHE_INODE_OP_SET, pclient); /* Release the target dir */ V_w(&pentry_dir_dest->lock); /* Release the source */ V_w(&pentry_src->lock); /* stats */ if(*pstatus != CACHE_INODE_SUCCESS) pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_LINK] += 1; else pclient->stat.func_stats.nb_success[CACHE_INODE_LINK] += 1; return *pstatus; }