blob: 8c3eeeb472daa6654d9f4a44c561bf77ef0458f7 [file] [log] [blame]
/*
*
* Copyright CEA/DAM/DIF (2008)
* contributeur : Philippe DENIEL philippe.deniel@cea.fr
* Thomas LEIBOVICI thomas.leibovici@cea.fr
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* ---------------------------------------
*/
/**
* @file nfs_filehandle_mgmt.c
* @brief Some tools for managing the file handles
*
*/
#include "config.h"
#include "log.h"
#include "nfs_core.h"
#include "nfs23.h"
#include "nfs4.h"
#include "fsal.h"
#include "nfs_exports.h"
#include "nfs_file_handle.h"
#include "nfs_proto_functions.h"
#include "nfs_proto_tools.h"
#include "nfs_convert.h"
#include "export_mgr.h"
#include "fsal_convert.h"
/**
*
* @brief Allocates a buffer to be used for storing a NFSv4 filehandle.
*
* Allocates a buffer to be used for storing a NFSv3 filehandle.
*
* @param fh [INOUT] the filehandle to manage.
*
* @return NFS3_OK if successful, NFS3ERR_SERVERFAULT, otherwise.
*
*/
int nfs3_AllocateFH(nfs_fh3 *fh)
{
/* Allocating the filehandle in memory */
fh->data.data_len = sizeof(struct alloc_file_handle_v3);
fh->data.data_val = gsh_malloc(fh->data.data_len);
if (fh->data.data_val == NULL) {
LogCrit(COMPONENT_NFSPROTO,
"Could not allocate space for filehandle");
return NFS3ERR_SERVERFAULT;
}
memset((char *)fh->data.data_val, 0, fh->data.data_len);
return NFS3_OK;
} /* nfs4_AllocateFH */
/**
*
* @brief Allocates a buffer to be used for storing a NFSv4 filehandle.
*
* Allocates a buffer to be used for storing a NFSv4 filehandle.
*
* @param fh [INOUT] the filehandle to manage.
*
* @return NFS4_OK if successful, NFS3ERR_SERVERFAULT, NFS4ERR_RESOURCE or
* NFS4ERR_STALE otherwise.
*
*/
int nfs4_AllocateFH(nfs_fh4 *fh)
{
/* Allocating the filehandle in memory */
fh->nfs_fh4_len = sizeof(struct alloc_file_handle_v4);
fh->nfs_fh4_val = gsh_malloc(fh->nfs_fh4_len);
if (fh->nfs_fh4_val == NULL) {
LogCrit(COMPONENT_NFS_V4,
"Could not allocate memory for filehandle");
return NFS4ERR_RESOURCE;
}
memset(fh->nfs_fh4_val, 0, fh->nfs_fh4_len);
LogFullDebugOpaque(COMPONENT_FILEHANDLE, "NFS4 Handle %s", LEN_FH_STR,
fh->nfs_fh4_val, fh->nfs_fh4_len);
return NFS4_OK;
}
/**
*
* nfs3_FhandleToCache: gets the cache entry from the NFSv3 file handle.
*
* Validates and Converts a V3 file handle and then gets the cache entry.
*
* @param fh3 [IN] pointer to the file handle to be converted
* @param exp_list [IN] export fsal to use
* @param status [OUT] protocol status
* @param rc [OUT] operation status
*
* @return cache entry or NULL on failure
*
*/
cache_entry_t *nfs3_FhandleToCache(nfs_fh3 *fh3,
nfsstat3 *status,
int *rc)
{
fsal_status_t fsal_status;
file_handle_v3_t *v3_handle;
struct fsal_export *export;
cache_entry_t *entry = NULL;
cache_inode_fsal_data_t fsal_data;
cache_inode_status_t cache_status;
/* Default behaviour */
*rc = NFS_REQ_OK;
/* validate the filehandle */
*status = nfs3_Is_Fh_Invalid(fh3);
if (*status != NFS3_OK)
goto badhdl;
/* Cast the fh as a non opaque structure */
v3_handle = (file_handle_v3_t *) (fh3->data.data_val);
assert(v3_handle->exportid == op_ctx->export->export_id);
export = op_ctx->fsal_export;
/* Give the export a crack at it */
fsal_data.export = export;
fsal_data.fh_desc.len = v3_handle->fs_len;
fsal_data.fh_desc.addr = &v3_handle->fsopaque;
/* adjust the handle opaque into a cache key */
fsal_status =
export->exp_ops.extract_handle(export, FSAL_DIGEST_NFSV3,
&fsal_data.fh_desc,
v3_handle->fhflags1);
if (FSAL_IS_ERROR(fsal_status))
cache_status = cache_inode_error_convert(fsal_status);
else
cache_status = cache_inode_get(&fsal_data, &entry);
if (cache_status != CACHE_INODE_SUCCESS) {
*status = nfs3_Errno(cache_status);
if (nfs_RetryableError(cache_status))
*rc = NFS_REQ_DROP;
}
badhdl:
return entry;
}
/**
* @brief Converts an FSAL object to an NFSv4 file handle
*
* @param[out] fh4 The extracted file handle
* @param[in] fsalhandle The FSAL handle to be converted
*
* @return true if successful, false otherwise
*/
bool nfs4_FSALToFhandle(nfs_fh4 *fh4,
const struct fsal_obj_handle *fsalhandle,
struct gsh_export *exp)
{
fsal_status_t fsal_status;
file_handle_v4_t *file_handle;
struct gsh_buffdesc fh_desc;
/* reset the buffer to be used as handle */
fh4->nfs_fh4_len = sizeof(struct alloc_file_handle_v4);
memset(fh4->nfs_fh4_val, 0, fh4->nfs_fh4_len);
file_handle = (file_handle_v4_t *) fh4->nfs_fh4_val;
/* Fill in the fs opaque part */
fh_desc.addr = &file_handle->fsopaque;
fh_desc.len = fh4->nfs_fh4_len - offsetof(file_handle_v4_t, fsopaque);
fsal_status =
fsalhandle->obj_ops.handle_digest(fsalhandle, FSAL_DIGEST_NFSV4,
&fh_desc);
if (FSAL_IS_ERROR(fsal_status)) {
LogDebug(COMPONENT_FILEHANDLE,
"handle_digest FSAL_DIGEST_NFSV4 failed");
return false;
}
file_handle->fhversion = GANESHA_FH_VERSION;
#if (BYTE_ORDER == BIG_ENDIAN)
file_handle->fhflags1 = FH_FSAL_BIG_ENDIAN;
#endif
file_handle->fs_len = fh_desc.len; /* set the actual size */
/* keep track of the export id */
file_handle->id.exports = exp->export_id;
/* Set the len */
fh4->nfs_fh4_len = nfs4_sizeof_handle(file_handle);
LogFullDebug(COMPONENT_FILEHANDLE, "NFS4 Handle 0x%X export id %d",
file_handle->fhflags1, file_handle->id.exports);
LogFullDebugOpaque(COMPONENT_FILEHANDLE, "NFS4 Handle %s", LEN_FH_STR,
fh4->nfs_fh4_val, fh4->nfs_fh4_len);
return true;
}
/**
* @brief Converts an FSAL object to an NFSv3 file handle
*
* @param[out] fh3 The extracted file handle
* @param[in] fsalhandle The FSAL handle to be converted
* @param[in] exp The gsh_export that this handle belongs to
*
* @return true if successful, false otherwise
*
* @todo Do we have to worry about buffer alignment and memcpy to
* compensate??
*/
bool nfs3_FSALToFhandle(nfs_fh3 *fh3,
const struct fsal_obj_handle *fsalhandle,
struct gsh_export *exp)
{
fsal_status_t fsal_status;
file_handle_v3_t *file_handle;
struct gsh_buffdesc fh_desc;
/* reset the buffer to be used as handle */
fh3->data.data_len = sizeof(struct alloc_file_handle_v3);
memset(fh3->data.data_val, 0, fh3->data.data_len);
file_handle = (file_handle_v3_t *) fh3->data.data_val;
/* Fill in the fs opaque part */
fh_desc.addr = &file_handle->fsopaque;
fh_desc.len = fh3->data.data_len - offsetof(file_handle_v3_t, fsopaque);
fsal_status =
fsalhandle->obj_ops.handle_digest(fsalhandle, FSAL_DIGEST_NFSV3,
&fh_desc);
if (FSAL_IS_ERROR(fsal_status)) {
LogDebug(COMPONENT_FILEHANDLE,
"handle_digest FSAL_DIGEST_NFSV3 failed");
return false;
}
file_handle->fhversion = GANESHA_FH_VERSION;
#if (BYTE_ORDER == BIG_ENDIAN)
file_handle->fhflags1 = FH_FSAL_BIG_ENDIAN;
#endif
file_handle->fs_len = fh_desc.len; /* set the actual size */
/* keep track of the export id */
file_handle->exportid = exp->export_id;
/* Set the len */
/* re-adjust to as built */
fh3->data.data_len = nfs3_sizeof_handle(file_handle);
LogFullDebugOpaque(COMPONENT_FILEHANDLE, "NFS3 Handle %s", LEN_FH_STR,
fh3->data.data_val, fh3->data.data_len);
return true;
}
/**
*
* nfs4_Is_Fh_DSHandle
*
* This routine is used to test if a fh is a DS fh
*
* @param fh [IN] file handle to test.
*
* @return true if DS fh, false otherwise
*
*/
int nfs4_Is_Fh_DSHandle(nfs_fh4 *fh)
{
file_handle_v4_t *fhandle4;
if (fh == NULL)
return 0;
fhandle4 = (file_handle_v4_t *) (fh->nfs_fh4_val);
return (fhandle4->fhflags1 & FILE_HANDLE_V4_FLAG_DS) != 0;
}
/**
* @brief Test if a filehandle is invalid
*
* @param[in] fh File handle to test.
*
* @return NFS4_OK if successfull.
*/
int nfs4_Is_Fh_Invalid(nfs_fh4 *fh)
{
file_handle_v4_t *pfile_handle;
BUILD_BUG_ON(sizeof(struct alloc_file_handle_v4) != NFS4_FHSIZE);
if (fh == NULL) {
LogMajor(COMPONENT_FILEHANDLE, "INVALID HANDLE: fh==NULL");
return NFS4ERR_BADHANDLE;
}
LogFullDebugOpaque(COMPONENT_FILEHANDLE, "NFS4 Handle %s", LEN_FH_STR,
fh->nfs_fh4_val, fh->nfs_fh4_len);
/* Cast the fh as a non opaque structure */
pfile_handle = (file_handle_v4_t *) (fh->nfs_fh4_val);
LogFullDebug(COMPONENT_FILEHANDLE, "NFS4 Handle 0x%X export id %d",
pfile_handle->fhflags1, pfile_handle->id.exports);
/* validate the filehandle */
if (pfile_handle == NULL || fh->nfs_fh4_len == 0
|| pfile_handle->fhversion != GANESHA_FH_VERSION
|| fh->nfs_fh4_len < offsetof(struct file_handle_v4, fsopaque)
|| fh->nfs_fh4_len > sizeof(struct alloc_file_handle_v4)
|| fh->nfs_fh4_len != nfs4_sizeof_handle(pfile_handle)) {
if (isInfo(COMPONENT_FILEHANDLE)) {
if (pfile_handle == NULL) {
LogInfo(COMPONENT_FILEHANDLE,
"INVALID HANDLE: nfs_fh4_val=NULL");
} else if (fh->nfs_fh4_len == 0) {
LogInfo(COMPONENT_FILEHANDLE,
"INVALID HANDLE: zero length handle");
} else if (pfile_handle->fhversion !=
GANESHA_FH_VERSION) {
LogInfo(COMPONENT_FILEHANDLE,
"INVALID HANDLE: not a Ganesha handle, fhversion=%d",
pfile_handle->fhversion);
} else if (fh->nfs_fh4_len <
offsetof(struct file_handle_v4, fsopaque)) {
LogInfo(COMPONENT_FILEHANDLE,
"INVALID HANDLE: data.data_len=%d is less than %d",
fh->nfs_fh4_len,
(int)offsetof(struct file_handle_v4,
fsopaque));
} else if (fh->nfs_fh4_len >
sizeof(struct alloc_file_handle_v4)) {
LogInfo(COMPONENT_FILEHANDLE,
"INVALID HANDLE: data.data_len=%d is greater than %d",
fh->nfs_fh4_len,
(int)sizeof(struct
alloc_file_handle_v4));
} else if (fh->nfs_fh4_len !=
nfs4_sizeof_handle(pfile_handle)) {
LogInfo(COMPONENT_FILEHANDLE,
"INVALID HANDLE: nfs_fh4_len=%d, should be %d",
fh->nfs_fh4_len,
(int)nfs4_sizeof_handle(pfile_handle));
} else {
LogInfo(COMPONENT_FILEHANDLE,
"INVALID HANDLE: is_pseudofs=%d",
pfile_handle->id.exports == 0);
}
}
return NFS4ERR_BADHANDLE; /* Bad FH */
}
return NFS4_OK;
} /* nfs4_Is_Fh_Invalid */
/**
* @brief Test if a filehandle is invalid.
*
* @param[in] fh3 File handle to test.
*
* @return NFS4_OK if successfull.
*
*/
int nfs3_Is_Fh_Invalid(nfs_fh3 *fh3)
{
file_handle_v3_t *pfile_handle;
BUILD_BUG_ON(sizeof(struct alloc_file_handle_v3) != NFS3_FHSIZE);
if (fh3 == NULL) {
LogMajor(COMPONENT_FILEHANDLE, "INVALID HANDLE: fh3==NULL");
return NFS3ERR_BADHANDLE;
}
LogFullDebugOpaque(COMPONENT_FILEHANDLE, "NFS3 Handle %s", LEN_FH_STR,
fh3->data.data_val, fh3->data.data_len);
/* Cast the fh as a non opaque structure */
pfile_handle = (file_handle_v3_t *) (fh3->data.data_val);
/* validate the filehandle */
if (pfile_handle == NULL || fh3->data.data_len == 0
|| pfile_handle->fhversion != GANESHA_FH_VERSION
|| fh3->data.data_len < sizeof(file_handle_v3_t)
|| fh3->data.data_len > sizeof(struct alloc_file_handle_v3)
|| fh3->data.data_len != nfs3_sizeof_handle(pfile_handle)) {
if (isInfo(COMPONENT_FILEHANDLE)) {
if (pfile_handle == NULL) {
LogInfo(COMPONENT_FILEHANDLE,
"INVALID HANDLE: data.data_val=NULL");
} else if (fh3->data.data_len == 0) {
LogInfo(COMPONENT_FILEHANDLE,
"INVALID HANDLE: zero length handle");
} else if (pfile_handle->fhversion !=
GANESHA_FH_VERSION) {
LogInfo(COMPONENT_FILEHANDLE,
"INVALID HANDLE: not a Ganesha handle, fhversion=%d",
pfile_handle->fhversion);
} else if (fh3->data.data_len <
sizeof(file_handle_v3_t)) {
LogInfo(COMPONENT_FILEHANDLE,
"INVALID HANDLE: data.data_len=%d is less than %d",
fh3->data.data_len,
(int)sizeof(file_handle_v3_t));
} else if (fh3->data.data_len >
sizeof(struct alloc_file_handle_v3)) {
LogInfo(COMPONENT_FILEHANDLE,
"INVALID HANDLE: data.data_len=%d is greater than %d",
fh3->data.data_len,
(int)sizeof(struct
alloc_file_handle_v3));
} else if (fh3->data.data_len !=
nfs3_sizeof_handle(pfile_handle)) {
LogInfo(COMPONENT_FILEHANDLE,
"INVALID HANDLE: data.data_len=%d, should be %d",
fh3->data.data_len,
(int)nfs3_sizeof_handle(pfile_handle));
}
}
return NFS3ERR_BADHANDLE; /* Bad FH */
}
return NFS3_OK;
} /* nfs3_Is_Fh_Invalid */
/**
* @brief Print an NFSv3 file handle
*
* @param[in] component Subsystem component ID
* @param[in] fh File handle to prin
*/
void print_fhandle3(log_components_t component, nfs_fh3 *fh)
{
if (isFullDebug(component)) {
char str[LEN_FH_STR];
sprint_fhandle3(str, fh);
LogFullDebug(component, "%s", str);
}
}
void sprint_fhandle3(char *str, nfs_fh3 *fh)
{
char *tmp =
str + sprintf(str, "File Handle V3: Len=%u ", fh->data.data_len);
sprint_mem(tmp, fh->data.data_val, fh->data.data_len);
}
/**
* @brief Print an NFSv4 file handle
*
* @param[in] component Subsystem component ID
* @param[in] fh File handle to print
*/
void print_fhandle4(log_components_t component, nfs_fh4 *fh)
{
if (isFullDebug(component)) {
char str[LEN_FH_STR];
sprint_fhandle4(str, fh);
LogFullDebug(component, "%s", str);
}
}
void sprint_fhandle4(char *str, nfs_fh4 *fh)
{
char *tmp =
str + sprintf(str, "File Handle V4: Len=%u ", fh->nfs_fh4_len);
sprint_mem(tmp, fh->nfs_fh4_val, fh->nfs_fh4_len);
}
/**
* @brief Print an NLM netobj
*
* @param[in] component Subsystem component ID
* @param[in] fh File handle to print
*/
void print_fhandle_nlm(log_components_t component, netobj *fh)
{
if (isFullDebug(component)) {
char str[LEN_FH_STR];
sprint_fhandle_nlm(str, fh);
LogFullDebug(component, "%s", str);
}
} /* print_fhandle_nlm */
void sprint_fhandle_nlm(char *str, netobj *fh)
{
char *tmp = str + sprintf(str, "File Handle V3: Len=%u ", fh->n_len);
sprint_mem(tmp, fh->n_bytes, fh->n_len);
} /* sprint_fhandle_nlm */
/**
* @brief Print the content of a buffer.
*
* @param[in] component Logging subsystem ID
* @param[in] buff Buffer to print.
* @param[in] len Length of the buffer.
*/
void print_buff(log_components_t component, char *buff, int len)
{
if (isFullDebug(component)) {
char str[1024];
sprint_buff(str, buff, len);
LogFullDebug(component, "%s", str);
}
} /* print_buff */
void sprint_buff(char *str, char *buff, int len)
{
char *tmp = str + sprintf(str, " Len=%u Buff=%p Val: ", len, buff);
sprint_mem(tmp, buff, len);
} /* sprint_buff */
void sprint_mem(char *str, char *buff, int len)
{
int i;
if (buff == NULL)
sprintf(str, "<null>");
else
for (i = 0; i < len; i++)
sprintf(str + i * 2, "%02x", (unsigned char)buff[i]);
}
/**
* @brief Convert a file handle to a string representation
*
* @param rq_vers [IN] version of the NFS protocol to be used
* @param fh3 [IN] NFSv3 file handle or NULL
* @param fh4 [IN] NFSv4 file handle or NULL
* @param str [OUT] string version of handle
*
*/
void nfs_FhandleToStr(u_long rq_vers, nfs_fh3 *fh3, nfs_fh4 *fh4, char *str)
{
switch (rq_vers) {
case NFS_V4:
sprint_fhandle4(str, fh4);
break;
case NFS_V3:
sprint_fhandle3(str, fh3);
break;
}
} /* nfs_FhandleToStr */
/**
*
* print_compound_fh
*
* This routine prints all the file handle within a compoud request's data
* structure.
*
* @param data [IN] compound's data to manage.
*/
void LogCompoundFH(compound_data_t *data)
{
if (isFullDebug(COMPONENT_FILEHANDLE)) {
char str[LEN_FH_STR];
sprint_fhandle4(str, &data->currentFH);
LogFullDebug(COMPONENT_FILEHANDLE, "Current FH %s", str);
sprint_fhandle4(str, &data->savedFH);
LogFullDebug(COMPONENT_FILEHANDLE, "Saved FH %s", str);
}
}
/**
* @brief Do basic checks on the CurrentFH
*
* This function performs basic checks to make sure the supplied
* filehandle is sane for a given operation.
*
* @param data [IN] Compound_data_t for the operation to check
* @param required_type [IN] The file type this operation requires.
* Set to 0 to allow any type.
* @param ds_allowed [IN] true if DS handles are allowed.
*
* @return NFSv4.1 status codes
*/
nfsstat4 nfs4_sanity_check_FH(compound_data_t *data,
object_file_type_t required_type,
bool ds_allowed)
{
int fh_status;
/* If there is no FH */
fh_status = nfs4_Is_Fh_Empty(&data->currentFH);
if (fh_status != NFS4_OK)
return fh_status;
assert(data->current_entry != NULL &&
data->current_filetype != NO_FILE_TYPE);
/* If the filehandle is invalid */
fh_status = nfs4_Is_Fh_Invalid(&data->currentFH);
if (fh_status != NFS4_OK)
return fh_status;
/* Check for the correct file type */
if (required_type != NO_FILE_TYPE) {
if (data->current_filetype != required_type) {
LogDebug(COMPONENT_NFS_V4,
"Wrong file type expected %s actual %s",
object_file_type_to_str(required_type),
object_file_type_to_str(data->
current_filetype));
if (required_type == DIRECTORY) {
if (data->current_filetype == SYMBOLIC_LINK)
return NFS4ERR_SYMLINK;
else
return NFS4ERR_NOTDIR;
} else if (required_type == SYMBOLIC_LINK)
return NFS4ERR_INVAL;
switch (data->current_filetype) {
case DIRECTORY:
return NFS4ERR_ISDIR;
default:
return NFS4ERR_INVAL;
}
}
}
if (nfs4_Is_Fh_DSHandle(&data->currentFH) && !ds_allowed) {
LogDebug(COMPONENT_NFS_V4, "DS Handle");
return NFS4ERR_INVAL;
}
return NFS4_OK;
} /* nfs4_sanity_check_FH */
/**
* @brief Do basic checks on the SavedFH
*
* This function performs basic checks to make sure the supplied
* filehandle is sane for a given operation.
*
* @param data [IN] Compound_data_t for the operation to check
* @param required_type [IN] The file type this operation requires.
* Set to 0 to allow any type. A negative value
* indicates any type BUT that type is allowed.
* @param ds_allowed [IN] true if DS handles are allowed.
*
* @return NFSv4.1 status codes
*/
nfsstat4 nfs4_sanity_check_saved_FH(compound_data_t *data, int required_type,
bool ds_allowed)
{
int fh_status;
/* If there is no FH */
fh_status = nfs4_Is_Fh_Empty(&data->savedFH);
if (fh_status != NFS4_OK)
return fh_status;
/* If the filehandle is invalid */
fh_status = nfs4_Is_Fh_Invalid(&data->savedFH);
if (fh_status != NFS4_OK)
return fh_status;
if (nfs4_Is_Fh_DSHandle(&data->savedFH) && !ds_allowed) {
LogDebug(COMPONENT_NFS_V4, "DS Handle");
return NFS4ERR_INVAL;
}
/* Check for the correct file type */
if (required_type < 0) {
if (-required_type == data->saved_filetype) {
LogDebug(COMPONENT_NFS_V4,
"Wrong file type expected not to be %s was %s",
object_file_type_to_str((object_file_type_t) -
required_type),
object_file_type_to_str(data->
current_filetype));
if (-required_type == DIRECTORY) {
return NFS4ERR_ISDIR;
return NFS4ERR_INVAL;
}
}
} else if (required_type != NO_FILE_TYPE) {
if (data->saved_filetype != required_type) {
LogDebug(COMPONENT_NFS_V4,
"Wrong file type expected %s was %s",
object_file_type_to_str((object_file_type_t)
required_type),
object_file_type_to_str(data->
current_filetype));
if (required_type == DIRECTORY) {
if (data->current_filetype == SYMBOLIC_LINK)
return NFS4ERR_SYMLINK;
else
return NFS4ERR_NOTDIR;
} else if (required_type == SYMBOLIC_LINK)
return NFS4ERR_INVAL;
switch (data->saved_filetype) {
case DIRECTORY:
return NFS4ERR_ISDIR;
default:
return NFS4ERR_INVAL;
}
}
}
return NFS4_OK;
} /* nfs4_sanity_check_saved_FH */