/** * * nlm4_send_grant_msg: Send NLMPROC4_GRANTED_MSG * * This runs in the nlm_asyn_thread context. */ static void nlm4_send_grant_msg(state_async_queue_t *arg) { int retval; char buffer[1024]; state_status_t state_status = STATE_SUCCESS; state_cookie_entry_t * cookie_entry; fsal_op_context_t context, * pcontext = &context; state_nlm_async_data_t * nlm_arg = &arg->state_async_data.state_nlm_async_data; if(isDebug(COMPONENT_NLM)) { netobj_to_string(&nlm_arg->nlm_async_args.nlm_async_grant.cookie, buffer, sizeof(buffer)); LogDebug(COMPONENT_NLM, "Sending GRANTED for arg=%p svid=%d start=%llx len=%llx cookie=%s", arg, nlm_arg->nlm_async_args.nlm_async_grant.alock.svid, (unsigned long long) nlm_arg->nlm_async_args.nlm_async_grant.alock.l_offset, (unsigned long long) nlm_arg->nlm_async_args.nlm_async_grant.alock.l_len, buffer); } retval = nlm_send_async(NLMPROC4_GRANTED_MSG, nlm_arg->nlm_async_host, &(nlm_arg->nlm_async_args.nlm_async_grant), nlm_arg->nlm_async_key); dec_nlm_client_ref(nlm_arg->nlm_async_host); free_grant_arg(arg); /* If success, we are done. */ if(retval == RPC_SUCCESS) return; /* * We are not able call granted callback. Some client may retry * the lock again. So remove the existing blocked nlm entry */ LogMajor(COMPONENT_NLM, "GRANTED_MSG RPC call failed with return code %d. Removing the blocking lock", retval); if(state_find_grant(nlm_arg->nlm_async_args.nlm_async_grant.cookie.n_bytes, nlm_arg->nlm_async_args.nlm_async_grant.cookie.n_len, &cookie_entry, &state_status) != STATE_SUCCESS) { /* This must be an old NLM_GRANTED_RES */ LogFullDebug(COMPONENT_NLM, "Could not find cookie=%s status=%s", buffer, state_err_str(state_status)); return; } PTHREAD_RWLOCK_WRLOCK(&cookie_entry->sce_pentry->state_lock); if(cookie_entry->sce_lock_entry->sle_block_data == NULL || !nlm_block_data_to_fsal_context(cookie_entry->sce_lock_entry->sle_block_data, pcontext)) { /* Wow, we're not doing well... */ PTHREAD_RWLOCK_UNLOCK(&cookie_entry->sce_pentry->state_lock); LogFullDebug(COMPONENT_NLM, "Could not find block data for cookie=%s (must be an old NLM_GRANTED_RES)", buffer); return; } PTHREAD_RWLOCK_UNLOCK(&cookie_entry->sce_pentry->state_lock); if(state_release_grant(pcontext, cookie_entry, &state_status) != STATE_SUCCESS) { /* Huh? */ LogFullDebug(COMPONENT_NLM, "Could not release cookie=%s status=%s", buffer, state_err_str(state_status)); } }
/* XXX : ACL */ fsal_status_t fsal_internal_testAccess(fsal_op_context_t * p_context, /* IN */ fsal_accessflags_t access_type, /* IN */ struct stat * p_buffstat, /* IN */ fsal_attrib_list_t * p_object_attributes /* IN */ ) { fsal_accessflags_t missing_access; unsigned int is_grp, i; fsal_uid_t uid; fsal_gid_t gid; fsal_accessmode_t mode; fsal_uid_t userid = ((vfsfsal_op_context_t *)p_context)->credential.user; fsal_uid_t groupid = ((vfsfsal_op_context_t *)p_context)->credential.group; /* sanity checks. */ if((!p_object_attributes && !p_buffstat) || !p_context) ReturnCode(ERR_FSAL_FAULT, 0); /* If the FSAL_F_OK flag is set, returns ERR INVAL */ if(access_type & FSAL_F_OK) ReturnCode(ERR_FSAL_INVAL, 0); /* test root access */ if(userid == 0) ReturnCode(ERR_FSAL_NO_ERROR, 0); /* unsatisfied flags */ missing_access = FSAL_MODE_MASK(access_type); /* only modes, no ACLs here */ if(p_object_attributes) { uid = p_object_attributes->owner; gid = p_object_attributes->group; mode = p_object_attributes->mode; } else { uid = p_buffstat->st_uid; gid = p_buffstat->st_gid; mode = unix2fsal_mode(p_buffstat->st_mode); } /* Test if file belongs to user. */ if(userid == uid) { LogFullDebug(COMPONENT_FSAL, "File belongs to user %d", uid); if(mode & FSAL_MODE_RUSR) missing_access &= ~FSAL_R_OK; if(mode & FSAL_MODE_WUSR) missing_access &= ~FSAL_W_OK; if(mode & FSAL_MODE_XUSR) missing_access &= ~FSAL_X_OK; /* handle the creation of a new 500 file correctly */ if((missing_access & FSAL_OWNER_OK) != 0) missing_access = 0; if(missing_access == 0) ReturnCode(ERR_FSAL_NO_ERROR, 0); else { LogFullDebug(COMPONENT_FSAL, "Mode=%#o, Access=%#o, Rights missing: %#o", mode, access_type, missing_access); ReturnCode(ERR_FSAL_ACCESS, 0); } } /* missing_access will be nonzero triggering a failure * even though FSAL_OWNER_OK is not even a real posix file * permission */ missing_access &= ~FSAL_OWNER_OK; /* Test if the file belongs to user's group. */ is_grp = (groupid == gid); if(is_grp) LogFullDebug(COMPONENT_FSAL, "File belongs to user's group %d", groupid); /* Test if file belongs to alt user's groups */ if(!is_grp) { for(i = 0; i < ((vfsfsal_op_context_t *)p_context)->credential.nbgroups; i++) { is_grp = (((vfsfsal_op_context_t *)p_context)->credential.alt_groups[i] == gid); if(is_grp) LogFullDebug(COMPONENT_FSAL, "File belongs to user's alt group %d", ((vfsfsal_op_context_t *)p_context)->credential.alt_groups[i]); // exits loop if found if(is_grp) break; } } /* finally apply group rights */ if(is_grp) { if(mode & FSAL_MODE_RGRP) missing_access &= ~FSAL_R_OK; if(mode & FSAL_MODE_WGRP) missing_access &= ~FSAL_W_OK; if(mode & FSAL_MODE_XGRP) missing_access &= ~FSAL_X_OK; if(missing_access == 0) ReturnCode(ERR_FSAL_NO_ERROR, 0); else ReturnCode(ERR_FSAL_ACCESS, 0); } /* test other perms */ if(mode & FSAL_MODE_ROTH) missing_access &= ~FSAL_R_OK; if(mode & FSAL_MODE_WOTH) missing_access &= ~FSAL_W_OK; if(mode & FSAL_MODE_XOTH) missing_access &= ~FSAL_X_OK; /* XXX ACLs. */ if(missing_access == 0) ReturnCode(ERR_FSAL_NO_ERROR, 0); else ReturnCode(ERR_FSAL_ACCESS, 0); }
/** * build the export entry */ fsal_status_t XFSFSAL_BuildExportContext(xfsfsal_export_context_t * p_export_context, /* OUT */ fsal_path_t * p_export_path, /* IN */ char *fs_specific_options /* IN */ ) { /* Get the mount point for this lustre FS, * so it can be used for building .lustre/fid paths. */ FILE *fp; struct mntent *p_mnt; struct stat pathstat; char rpath[MAXPATHLEN]; char mntdir[MAXPATHLEN]; char fs_spec[MAXPATHLEN]; char *first_xfs_dir = NULL; char type[256]; size_t pathlen, outlen; int rc; char *handle; size_t handle_len = 0; /* sanity check */ if(p_export_context == NULL) { LogCrit(COMPONENT_FSAL, "NULL mandatory argument passed to %s()", __FUNCTION__); Return(ERR_FSAL_FAULT, 0, INDEX_FSAL_BuildExportContext); } outlen = 0; if(p_export_path != NULL) strncpy(rpath, p_export_path->path, MAXPATHLEN); /* open mnt file */ fp = setmntent(MOUNTED, "r"); if(fp == NULL) { rc = errno; LogCrit(COMPONENT_FSAL, "Error %d in setmntent(%s): %s", rc, MOUNTED, strerror(rc)); Return(posix2fsal_error(rc), rc, INDEX_FSAL_BuildExportContext); } while((p_mnt = getmntent(fp)) != NULL) { /* get the longer path xfs related export that matches export path */ if(p_mnt->mnt_dir != NULL) { pathlen = strlen(p_mnt->mnt_dir); if(strncmp(p_mnt->mnt_type, "xfs", 256)) continue; if(first_xfs_dir == NULL) first_xfs_dir = p_mnt->mnt_dir; if((pathlen > outlen) && !strcmp(p_mnt->mnt_dir, "/")) { LogDebug(COMPONENT_FSAL, "Root mountpoint is allowed for matching %s, type=%s, fs=%s", rpath, p_mnt->mnt_type, p_mnt->mnt_fsname); outlen = pathlen; strncpy(mntdir, p_mnt->mnt_dir, MAXPATHLEN); strncpy(type, p_mnt->mnt_type, 256); strncpy(fs_spec, p_mnt->mnt_fsname, MAXPATHLEN); } /* in other cases, the filesystem must be <mountpoint>/<smthg> or <mountpoint>\0 */ else if((pathlen > outlen) && !strncmp(rpath, p_mnt->mnt_dir, pathlen) && ((rpath[pathlen] == '/') || (rpath[pathlen] == '\0'))) { LogFullDebug(COMPONENT_FSAL, "%s is under mountpoint %s, type=%s, fs=%s", rpath, p_mnt->mnt_dir, p_mnt->mnt_type, p_mnt->mnt_fsname); outlen = pathlen; strncpy(mntdir, p_mnt->mnt_dir, MAXPATHLEN); strncpy(type, p_mnt->mnt_type, 256); strncpy(fs_spec, p_mnt->mnt_fsname, MAXPATHLEN); } } } if(outlen <= 0) { if(p_export_path == NULL) strncpy(mntdir, first_xfs_dir, MAXPATHLEN); else { LogCrit(COMPONENT_FSAL, "No mount entry matches '%s' in %s", rpath, MOUNTED); endmntent(fp); Return(ERR_FSAL_NOENT, 0, INDEX_FSAL_BuildExportContext); } } /* Do the path_to_fshandle call to init the xfs's libhandle */ strncpy(p_export_context->mount_point, mntdir, MAXPATHLEN); if((rc = path_to_fshandle(mntdir, (void **)(&handle), &handle_len)) < 0) Return(ERR_FSAL_FAULT, errno, INDEX_FSAL_BuildExportContext); memcpy(p_export_context->mnt_fshandle_val, handle, handle_len); p_export_context->mnt_fshandle_len = handle_len; if((rc = path_to_handle(mntdir, (void **)(&handle), &handle_len)) < 0) Return(ERR_FSAL_FAULT, errno, INDEX_FSAL_BuildExportContext); memcpy(p_export_context->mnt_handle_val, handle, handle_len); p_export_context->mnt_handle_len = handle_len; p_export_context->dev_id = 1; /** @todo BUGAZOMEU : put something smarter here, using setmntent */ Return(ERR_FSAL_NO_ERROR, 0, INDEX_FSAL_BuildExportContext); }
int nfs_Fsstat(nfs_arg_t *parg, exportlist_t *pexport, fsal_op_context_t *pcontext, nfs_worker_data_t *pworker, struct svc_req *preq, nfs_res_t * pres) { fsal_dynamicfsinfo_t dynamicinfo; cache_inode_status_t cache_status; cache_entry_t *pentry = NULL; fsal_attrib_list_t attr; int rc = NFS_REQ_OK; if(isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; nfs_FhandleToStr(preq->rq_vers, &(parg->arg_statfs2), &(parg->arg_fsstat3.fsroot), NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs_Fsstat handle: %s", str); } if(preq->rq_vers == NFS_V3) { /* to avoid setting it on each error case */ pres->res_fsstat3.FSSTAT3res_u.resfail.obj_attributes.attributes_follow = FALSE; } /* convert file handle to vnode */ if((pentry = nfs_FhandleToCache(preq->rq_vers, &(parg->arg_statfs2), &(parg->arg_fsstat3.fsroot), NULL, &(pres->res_statfs2.status), &(pres->res_fsstat3.status), NULL, NULL, pcontext, &rc)) == NULL) { /* Stale NFS FH ? */ /* return NFS_REQ_DROP ; */ goto out; } /* Get statistics and convert from cache */ if((cache_status = cache_inode_statfs(pentry, &dynamicinfo, pcontext, &cache_status)) == CACHE_INODE_SUCCESS) { /* This call is costless, the pentry was cached during call to nfs_FhandleToCache */ if((cache_status = cache_inode_getattr(pentry, &attr, pcontext, &cache_status)) == CACHE_INODE_SUCCESS) { LogFullDebug(COMPONENT_NFSPROTO, "nfs_Fsstat --> dynamicinfo.total_bytes = %zu dynamicinfo.free_bytes = %zu dynamicinfo.avail_bytes = %zu", dynamicinfo.total_bytes, dynamicinfo.free_bytes, dynamicinfo.avail_bytes); LogFullDebug(COMPONENT_NFSPROTO, "nfs_Fsstat --> dynamicinfo.total_files = %llu dynamicinfo.free_files = %llu dynamicinfo.avail_files = %llu", dynamicinfo.total_files, dynamicinfo.free_files, dynamicinfo.avail_files); switch (preq->rq_vers) { case NFS_V2: pres->res_statfs2.STATFS2res_u.info.tsize = NFS2_MAXDATA; pres->res_statfs2.STATFS2res_u.info.bsize = DEV_BSIZE; pres->res_statfs2.STATFS2res_u.info.blocks = dynamicinfo.total_bytes / DEV_BSIZE; pres->res_statfs2.STATFS2res_u.info.bfree = dynamicinfo.free_bytes / DEV_BSIZE; pres->res_statfs2.STATFS2res_u.info.bavail = dynamicinfo.avail_bytes / DEV_BSIZE; pres->res_statfs2.status = NFS_OK; break; case NFS_V3: nfs_SetPostOpAttr(pexport, &attr, &(pres->res_fsstat3.FSSTAT3res_u .resok.obj_attributes)); pres->res_fsstat3.FSSTAT3res_u.resok.tbytes = dynamicinfo.total_bytes; pres->res_fsstat3.FSSTAT3res_u.resok.fbytes = dynamicinfo.free_bytes; pres->res_fsstat3.FSSTAT3res_u.resok.abytes = dynamicinfo.avail_bytes; pres->res_fsstat3.FSSTAT3res_u.resok.tfiles = dynamicinfo.total_files; pres->res_fsstat3.FSSTAT3res_u.resok.ffiles = dynamicinfo.free_files; pres->res_fsstat3.FSSTAT3res_u.resok.afiles = dynamicinfo.avail_files; pres->res_fsstat3.FSSTAT3res_u.resok.invarsec = 0; /* volatile FS */ pres->res_fsstat3.status = NFS3_OK; LogFullDebug(COMPONENT_NFSPROTO, "nfs_Fsstat --> tbytes=%llu fbytes=%llu abytes=%llu", pres->res_fsstat3.FSSTAT3res_u.resok.tbytes, pres->res_fsstat3.FSSTAT3res_u.resok.fbytes, pres->res_fsstat3.FSSTAT3res_u.resok.abytes); LogFullDebug(COMPONENT_NFSPROTO, "nfs_Fsstat --> tfiles=%llu fffiles=%llu afiles=%llu", pres->res_fsstat3.FSSTAT3res_u.resok.tfiles, pres->res_fsstat3.FSSTAT3res_u.resok.ffiles, pres->res_fsstat3.FSSTAT3res_u.resok.afiles); break; } rc = NFS_REQ_OK; goto out; } } /* At this point we met an error */ if(nfs_RetryableError(cache_status)) { rc = NFS_REQ_DROP; goto out; } nfs_SetFailedStatus(pcontext, pexport, preq->rq_vers, cache_status, &pres->res_statfs2.status, &pres->res_fsstat3.status, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); rc = NFS_REQ_OK; out: /* return references */ if (pentry) cache_inode_put(pentry); return (rc); } /* nfs_Fsstat */
fsal_posixdb_status_t fsal_posixdb_getInfoFromHandle(fsal_posixdb_conn * p_conn, /* IN */ posixfsal_handle_t * p_object_handle, /* IN/OUT */ fsal_path_t * p_paths, /* OUT */ int paths_size, /* IN */ int *p_count /* OUT */ ) { fsal_posixdb_status_t st; result_handle_t res; MYSQL_ROW row; posixfsal_handle_t parent_directory_handle; int i_path; int toomanypaths = 0; char query[2048]; /* sanity check */ if(!p_conn || !p_object_handle || ((!p_paths || !p_count) && paths_size > 0)) { ReturnCodeDB(ERR_FSAL_POSIXDB_FAULT, 0); } LogFullDebug(COMPONENT_FSAL, "OBJECT_ID=%lli\n", p_object_handle->data.id); BeginTransaction(p_conn); /* lookup for the handle of the file */ if(!fsal_posixdb_GetInodeCache(p_object_handle)) { snprintf(query, 2048, "SELECT Handle.deviceid, Handle.inode, Handle.nlink, Handle.ctime, Handle.ftype " "FROM Handle WHERE handleid=%llu AND handlets=%u", p_object_handle->data.id, p_object_handle->data.ts); st = db_exec_sql(p_conn, query, &res); if(FSAL_POSIXDB_IS_ERROR(st)) goto rollback; /* p_res contains : Handle.deviceId, Handle.inode, Handle.nlink, Handle.ctime, Handle.ftype */ LogDebug(COMPONENT_FSAL, "lookupHandle(%llu,%u)", p_object_handle->data.id, (unsigned int)p_object_handle->data.ts); if((mysql_num_rows(res) != 1) || ((row = mysql_fetch_row(res)) == NULL)) { LogDebug(COMPONENT_FSAL, "lookupHandle=%d entries", mysql_num_rows(res)); mysql_free_result(res); RollbackTransaction(p_conn); ReturnCodeDB(ERR_FSAL_POSIXDB_NOENT, 0); } posixdb_internal_fillFileinfoFromStrValues(&(p_object_handle->data.info), row[0], row[1], row[2], row[3], row[4]); mysql_free_result(res); /* update the inode */ fsal_posixdb_UpdateInodeCache(p_object_handle); } /* Build the paths of the object */ if(p_paths) { /* find all the paths to the object */ snprintf(query, 2048, "SELECT name, handleidparent, handletsparent " "FROM Parent WHERE handleid=%llu AND handlets=%u", p_object_handle->data.id, p_object_handle->data.ts); st = db_exec_sql(p_conn, query, &res); if(FSAL_POSIXDB_IS_ERROR(st)) goto rollback; /* res contains name, handleidparent, handletsparent */ *p_count = mysql_num_rows(res); if(*p_count == 0) { mysql_free_result(res); RollbackTransaction(p_conn); ReturnCodeDB(ERR_FSAL_POSIXDB_NOPATH, 0); } else if(*p_count > paths_size) { toomanypaths = 1; LogCrit(COMPONENT_FSAL, "Too many paths found for object %llu.%u: found=%u, max=%d", p_object_handle->data.id, p_object_handle->data.ts, *p_count, paths_size); *p_count = paths_size; } for(i_path = 0; i_path < *p_count; i_path++) { unsigned int tmp_len; row = mysql_fetch_row(res); if(row == NULL) { mysql_free_result(res); RollbackTransaction(p_conn); ReturnCodeDB(ERR_FSAL_POSIXDB_FAULT, 0); } /* build the path of the parent directory */ parent_directory_handle.data.id = atoll(row[1]); parent_directory_handle.data.ts = atoi(row[2]); st = fsal_posixdb_buildOnePath(p_conn, &parent_directory_handle, &p_paths[i_path]); if(FSAL_POSIXDB_IS_ERROR(st)) goto free_res; tmp_len = p_paths[i_path].len; if((tmp_len > 0) && (p_paths[i_path].path[tmp_len - 1] == '/')) { /* then concatenate the name of the file */ /* but not concatenate '/' */ if((tmp_len + strlen(row[0]) >= FSAL_MAX_PATH_LEN)) { mysql_free_result(res); RollbackTransaction(p_conn); ReturnCodeDB(ERR_FSAL_POSIXDB_PATHTOOLONG, 0); } strcpy(&p_paths[i_path].path[tmp_len], row[0]); p_paths[i_path].len += strlen(row[0]); } else { /* then concatenate the name of the file */ if((tmp_len + 1 + strlen(row[0]) >= FSAL_MAX_PATH_LEN)) { mysql_free_result(res); RollbackTransaction(p_conn); ReturnCodeDB(ERR_FSAL_POSIXDB_PATHTOOLONG, 0); } p_paths[i_path].path[tmp_len] = '/'; strcpy(&p_paths[i_path].path[tmp_len + 1], row[0]); p_paths[i_path].len += 1 + strlen(row[0]); } /* insert the object into cache */ fsal_posixdb_CachePath(p_object_handle, &p_paths[i_path]); } mysql_free_result(res); } st = EndTransaction(p_conn); if(toomanypaths) ReturnCodeDB(ERR_FSAL_POSIXDB_TOOMANYPATHS, 0); else return st; free_res: mysql_free_result(res); rollback: RollbackTransaction(p_conn); return st; }
fsal_status_t vfs_reopen2(struct fsal_obj_handle *obj_hdl, struct state_t *state, fsal_openflags_t openflags) { struct vfs_fd fd, *my_fd = &fd, *my_share_fd; struct vfs_fsal_obj_handle *myself; fsal_status_t status = {0, 0}; int posix_flags = 0; fsal_openflags_t old_openflags; my_share_fd = (struct vfs_fd *)(state + 1); fsal2posix_openflags(openflags, &posix_flags); LogFullDebug(COMPONENT_FSAL, posix_flags & O_TRUNC ? "Truncate" : "No truncate"); memset(my_fd, 0, sizeof(*my_fd)); fd.fd = -1; myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); if (obj_hdl->fsal != obj_hdl->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", obj_hdl->fsal->name, obj_hdl->fs->fsal->name); return fsalstat(posix2fsal_error(EXDEV), EXDEV); } /* This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->lock); old_openflags = my_share_fd->openflags; /* We can conflict with old share, so go ahead and check now. */ status = check_share_conflict(&myself->u.file.share, openflags, false); if (FSAL_IS_ERROR(status)) { PTHREAD_RWLOCK_unlock(&obj_hdl->lock); return status; } /* Set up the new share so we can drop the lock and not have a * conflicting share be asserted, updating the share counters. */ update_share_counters(&myself->u.file.share, old_openflags, openflags); PTHREAD_RWLOCK_unlock(&obj_hdl->lock); status = vfs_open_my_fd(myself, openflags, posix_flags, my_fd); if (!FSAL_IS_ERROR(status)) { /* Close the existing file descriptor and copy the new * one over. */ vfs_close_my_fd(my_share_fd); *my_share_fd = fd; } else { /* We had a failure on open - we need to revert the share. * This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->lock); update_share_counters(&myself->u.file.share, openflags, old_openflags); PTHREAD_RWLOCK_unlock(&obj_hdl->lock); } return status; }
/** * * proxy_Fattr_To_FSAL_dynamic_fsinfo: Converts NFSv4 attributes buffer to a FSAL dynamic fsinfo structure. * * Converts NFSv4 attributes buffer to a FSAL dynamic fsinfo structure. * * @param pdynamicinfo [OUT] pointer to FSAL attributes. * @param Fattr [IN] pointer to NFSv4 attributes. * * @return 1 if successful, 0 if not supported, -1 if argument is badly formed * */ int proxy_Fattr_To_FSAL_dynamic_fsinfo(fsal_dynamicfsinfo_t * pdynamicinfo, fattr4 * Fattr) { u_int LastOffset = 0; unsigned int i = 0; char __attribute__ ((__unused__)) funcname[] = "proxy_Fattr_To_FSAL_dynamic_fsinfo"; uint32_t attrmasklist[FATTR4_MOUNTED_ON_FILEID]; /* List cannot be longer than FATTR4_MOUNTED_ON_FILEID */ uint32_t attrmasklen = 0; uint32_t attribute_to_set = 0; uint64_t tmp_uint64 = 0LL; if(pdynamicinfo == NULL || Fattr == NULL) return -1; /* Check attributes data */ if(Fattr->attr_vals.attrlist4_val == NULL) return -1; /* Convert the attribute bitmap to an attribute list */ nfs4_bitmap4_to_list(&(Fattr->attrmask), &attrmasklen, attrmasklist); LogFullDebug(COMPONENT_NFS_V4, " nfs4_bitmap4_to_list ====> attrmasklen = %d\n", attrmasklen); /* Init */ memset((char *)pdynamicinfo, 0, sizeof(fsal_dynamicfsinfo_t)); for(i = 0; i < attrmasklen; i++) { attribute_to_set = attrmasklist[i]; if(attrmasklist[i] > FATTR4_MOUNTED_ON_FILEID) { /* Erroneous value... skip */ continue; } LogFullDebug(COMPONENT_NFS_V4, "=================> nfs4_Fattr_To_FSAL_attr: i=%u attr=%u\n", i, attrmasklist[i]); LogFullDebug(COMPONENT_NFS_V4, "Flag for Operation = %d|%d is ON, name = %s reply_size = %d\n", attrmasklist[i], fattr4tab[attribute_to_set].val, fattr4tab[attribute_to_set].name, fattr4tab[attribute_to_set].size_fattr4); switch (attribute_to_set) { case FATTR4_FILES_AVAIL: memcpy((char *)&tmp_uint64, (char *)(Fattr->attr_vals.attrlist4_val + LastOffset), sizeof(fattr4_files_avail)); pdynamicinfo->avail_files = nfs_ntohl64(tmp_uint64); LastOffset += fattr4tab[attribute_to_set].size_fattr4; break; case FATTR4_FILES_FREE: memcpy((char *)&tmp_uint64, (char *)(Fattr->attr_vals.attrlist4_val + LastOffset), sizeof(fattr4_files_free)); pdynamicinfo->free_files = nfs_ntohl64(tmp_uint64); LastOffset += fattr4tab[attribute_to_set].size_fattr4; break; case FATTR4_FILES_TOTAL: memcpy((char *)&tmp_uint64, (char *)(Fattr->attr_vals.attrlist4_val + LastOffset), sizeof(fattr4_files_total)); pdynamicinfo->total_files = nfs_ntohl64(tmp_uint64); LastOffset += fattr4tab[attribute_to_set].size_fattr4; break; case FATTR4_SPACE_AVAIL: memcpy((char *)&tmp_uint64, (char *)(Fattr->attr_vals.attrlist4_val + LastOffset), sizeof(fattr4_space_avail)); pdynamicinfo->avail_bytes = nfs_ntohl64(tmp_uint64); LastOffset += fattr4tab[attribute_to_set].size_fattr4; break; case FATTR4_SPACE_FREE: memcpy((char *)&tmp_uint64, (char *)(Fattr->attr_vals.attrlist4_val + LastOffset), sizeof(fattr4_space_free)); pdynamicinfo->free_bytes = nfs_ntohl64(tmp_uint64); LastOffset += fattr4tab[attribute_to_set].size_fattr4; break; case FATTR4_SPACE_TOTAL: memcpy((char *)&tmp_uint64, (char *)(Fattr->attr_vals.attrlist4_val + LastOffset), sizeof(fattr4_space_total)); pdynamicinfo->total_bytes = nfs_ntohl64(tmp_uint64); LastOffset += fattr4tab[attribute_to_set].size_fattr4; break; default: LogFullDebug(COMPONENT_NFS_V4, "SATTR: Attribut no supporte %d name=%s\n", attribute_to_set, fattr4tab[attribute_to_set].name); LastOffset += fattr4tab[attribute_to_set].size_fattr4; break; } /* switch( attribute_to_set ) */ } return 1; } /* proxy_Fattr_To_FSAL_dynamic_fsinfo */
fsal_posixdb_status_t fsal_posixdb_add(fsal_posixdb_conn * p_conn, /* IN */ fsal_posixdb_fileinfo_t * p_object_info, /* IN */ posixfsal_handle_t * p_parent_directory_handle, /* IN */ fsal_name_t * p_filename, /* IN */ posixfsal_handle_t * p_object_handle /* OUT */ ) { PGresult *p_res; char handleid_str[MAX_HANDLEIDSTR_SIZE]; char handlets_str[MAX_HANDLETSSTR_SIZE]; char handleidparent_str[MAX_HANDLEIDSTR_SIZE]; char handletsparent_str[MAX_HANDLETSSTR_SIZE]; char devid_str[MAX_DEVICEIDSTR_SIZE]; char inode_str[MAX_INODESTR_SIZE]; int found; const char *paramValues[6]; fsal_posixdb_status_t st; /******************* * 1/ sanity check * *******************/ /* parent_directory and filename are NULL only if it is the root directory */ if(!p_conn || !p_object_info || !p_object_handle || (p_filename && !p_parent_directory_handle) || (!p_filename && p_parent_directory_handle)) ReturnCodeDB(ERR_FSAL_POSIXDB_FAULT, 0); CheckConn(p_conn); LogFullDebug(COMPONENT_FSAL, "adding entry with parentid=%llu, id=%"PRIu64", name=%s\n", p_parent_directory_handle ? p_parent_directory_handle->data.id : 0, p_object_info ? p_object_info->inode : 0, p_filename ? p_filename->name : "NULL"); BeginTransaction(p_conn, p_res); /********************************* * 2/ we check the parent handle * *********************************/ if(p_parent_directory_handle) { /* the root has no parent */ snprintf(handleidparent_str, MAX_HANDLEIDSTR_SIZE, "%llu", p_parent_directory_handle->data.id); snprintf(handletsparent_str, MAX_HANDLETSSTR_SIZE, "%i", p_parent_directory_handle->data.ts); paramValues[0] = handleidparent_str; paramValues[1] = handletsparent_str; p_res = PQexecPrepared(p_conn, "lookupHandle", 2, paramValues, NULL, NULL, 0); CheckResult(p_res); if(PQntuples(p_res) != 1) { /* parent entry not found */ RollbackTransaction(p_conn, p_res); ReturnCodeDB(ERR_FSAL_POSIXDB_NOENT, 0); } PQclear(p_res); } /********************************************************** * 3/ Check if there is an existing Handle for the object * **********************************************************/ snprintf(devid_str, MAX_DEVICEIDSTR_SIZE, "%llu", (unsigned long long int)p_object_info->devid); snprintf(inode_str, MAX_INODESTR_SIZE, "%llu", (unsigned long long int)p_object_info->inode); paramValues[0] = devid_str; paramValues[1] = inode_str; p_res = PQexecPrepared(p_conn, "lookupHandleByInodeFU", 2, paramValues, NULL, NULL, 0); CheckResult(p_res); found = (PQntuples(p_res) == 1); if(found) { /* a Handle (that matches devid & inode) already exists */ /* fill 'info' with information about the handle in the database */ posixdb_internal_fillFileinfoFromStrValues(&(p_object_handle->data.info), NULL, NULL, PQgetvalue(p_res, 0, 2), /* nlink */ PQgetvalue(p_res, 0, 3), /* ctime */ PQgetvalue(p_res, 0, 4) /* ftype */ ); p_object_handle->data.info.inode = p_object_info->inode; p_object_handle->data.info.devid = p_object_info->devid; strncpy(handleid_str, PQgetvalue(p_res, 0, 0), MAX_HANDLEIDSTR_SIZE); strncpy(handlets_str, PQgetvalue(p_res, 0, 1), MAX_HANDLETSSTR_SIZE); PQclear(p_res); p_object_handle->data.id = atoll(handleid_str); p_object_handle->data.ts = atoi(handlets_str); /* check the consistency of the handle */ if(fsal_posixdb_consistency_check(&(p_object_handle->data.info), p_object_info)) { /* consistency check failed */ /* p_object_handle has been filled in order to be able to fix the consistency later */ RollbackTransaction(p_conn, p_res); ReturnCodeDB(ERR_FSAL_POSIXDB_CONSISTENCY, 0); } /* update nlink & ctime if needed */ if(p_object_info->nlink != p_object_handle->data.info.nlink || p_object_info->ctime != p_object_handle->data.info.ctime) { char nlink_str[MAX_NLINKSTR_SIZE]; char ctime_str[MAX_CTIMESTR_SIZE]; snprintf(nlink_str, MAX_NLINKSTR_SIZE, "%i", p_object_info->nlink); snprintf(ctime_str, MAX_CTIMESTR_SIZE, "%i", (int)p_object_info->ctime); paramValues[0] = handleid_str; paramValues[1] = handlets_str; paramValues[2] = nlink_str; paramValues[3] = ctime_str; p_object_handle->data.info = *p_object_info; p_res = PQexecPrepared(p_conn, "updateHandle", 4, paramValues, NULL, NULL, 0); CheckCommand(p_res); } fsal_posixdb_UpdateInodeCache(p_object_handle); } else { /* no handle found */ /* Handle does not exist, add a new Handle entry */ char nlink_str[MAX_NLINKSTR_SIZE]; char ctime_str[MAX_CTIMESTR_SIZE]; char ftype_str[MAX_FTYPESTR_SIZE]; PQclear(p_res); p_object_handle->data.ts = (int)time(NULL); p_object_handle->data.info = *p_object_info; snprintf(handlets_str, MAX_HANDLETSSTR_SIZE, "%i", p_object_handle->data.ts); snprintf(nlink_str, MAX_NLINKSTR_SIZE, "%i", p_object_info->nlink); snprintf(ctime_str, MAX_CTIMESTR_SIZE, "%i", (int)p_object_info->ctime); snprintf(ftype_str, MAX_CTIMESTR_SIZE, "%i", (int)p_object_info->ftype); paramValues[0] = devid_str; paramValues[1] = inode_str; paramValues[2] = handlets_str; paramValues[3] = nlink_str; paramValues[4] = ctime_str; paramValues[5] = ftype_str; p_res = PQexecPrepared(p_conn, "insertHandle", 6, paramValues, NULL, NULL, 0); CheckCommand(p_res); PQclear(p_res); p_res = PQexecPrepared(p_conn, "lookupHandleByInodeFU", 2, paramValues, NULL, NULL, 0); CheckResult(p_res); strncpy(handleid_str, PQgetvalue(p_res, 0, 0), MAX_HANDLEIDSTR_SIZE); strncpy(handlets_str, PQgetvalue(p_res, 0, 1), MAX_HANDLETSSTR_SIZE); p_object_handle->data.id = atoll(PQgetvalue(p_res, 0, 0)); PQclear(p_res); /* now, we have the handle id */ fsal_posixdb_UpdateInodeCache(p_object_handle); } /************************************************ * add (or update) an entry in the Parent table * ************************************************/ paramValues[0] = p_parent_directory_handle ? handleidparent_str : handleid_str; paramValues[1] = p_parent_directory_handle ? handletsparent_str : handlets_str; paramValues[2] = p_filename ? p_filename->name : ""; p_res = PQexecPrepared(p_conn, "lookupParent", 3, paramValues, NULL, NULL, 0); CheckResult(p_res); /* p-res contains handleid & handlets */ found = (PQntuples(p_res) == 1); paramValues[3] = handleid_str; paramValues[4] = handlets_str; if(found) { /* update the Parent entry if necessary (there entry exists with another handle) */ if((fsal_u64_t) atoll(PQgetvalue(p_res, 0, 0)) != p_object_handle->data.id || atoi(PQgetvalue(p_res, 0, 1)) != p_object_handle->data.ts) { /* steps : - check the nlink value of the Parent entry to be overwritten - if nlink = 1, then we can delete the handle. else we have to update it (nlink--) : that is done by fsal_posixdb_deleteParent - update the handle of the entry */ char bad_handleid_str[MAX_HANDLEIDSTR_SIZE]; char bad_handlets_str[MAX_HANDLETSSTR_SIZE]; int nlink; strncpy(bad_handleid_str, PQgetvalue(p_res, 0, 0), MAX_HANDLEIDSTR_SIZE); strncpy(bad_handlets_str, PQgetvalue(p_res, 0, 1), MAX_HANDLETSSTR_SIZE); PQclear(p_res); /* clear old res before a new query */ /* check the nlink value of the entry to be updated */ paramValues[0] = handleidparent_str; paramValues[1] = handletsparent_str; p_res = PQexecPrepared(p_conn, "lookupHandleFU", 2, paramValues, NULL, NULL, 0); CheckResult(p_res); found = (PQntuples(p_res) == 1); if(found) { /* we have retrieved the handle information of the bad entry */ nlink = atoi(PQgetvalue(p_res, 0, 4)); PQclear(p_res); /* clear old res before a new query */ /* a Parent entry already exists, we delete it */ st = fsal_posixdb_deleteParent(p_conn, bad_handleid_str, bad_handlets_str, p_parent_directory_handle ? handleidparent_str : handleid_str, p_parent_directory_handle ? handletsparent_str : handlets_str, p_filename ? p_filename->name : "", nlink); if(FSAL_POSIXDB_IS_ERROR(st)) { RollbackTransaction(p_conn, p_res); return st; } } else { /* the Handle line has been deleted */ PQclear(p_res); /* clear old res before a new query */ } /* the bad entry has been deleted. Now we had a new Parent entry */ goto add_new_parent_entry; } else { /* a Parent entry exists with our handle, nothing to do */ PQclear(p_res); } } else { /* add a Parent entry */ PQclear(p_res); add_new_parent_entry: paramValues[0] = p_parent_directory_handle ? handleidparent_str : handleid_str; paramValues[1] = p_parent_directory_handle ? handletsparent_str : handlets_str; paramValues[2] = p_filename ? p_filename->name : ""; paramValues[3] = handleid_str; paramValues[4] = handlets_str; p_res = PQexecPrepared(p_conn, "insertParent", 5, paramValues, NULL, NULL, 0); CheckCommand(p_res); PQclear(p_res); /* XXX : is it possible to have unique key violation ? */ } EndTransaction(p_conn, p_res); ReturnCodeDB(ERR_FSAL_POSIXDB_NOERR, 0); }
int nfs4_op_setclientid_confirm(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { SETCLIENTID_CONFIRM4args * const arg_SETCLIENTID_CONFIRM4 = &op->nfs_argop4_u.opsetclientid_confirm; SETCLIENTID_CONFIRM4res * const res_SETCLIENTID_CONFIRM4 = &resp->nfs_resop4_u.opsetclientid_confirm; nfs_client_id_t *conf = NULL; nfs_client_id_t *unconf = NULL; nfs_client_record_t *client_record; clientid4 clientid = 0; char str_verifier[NFS4_VERIFIER_SIZE * 2 + 1]; const char *str_client_addr = "(unknown)"; /* The client name, for gratuitous logging */ char str_client[CLIENTNAME_BUFSIZE]; /* Display buffer for client name */ struct display_buffer dspbuf_client = { sizeof(str_client), str_client, str_client}; /* The clientid4 broken down into fields */ char str_clientid4[DISPLAY_CLIENTID_SIZE]; /* Display buffer for clientid4 */ struct display_buffer dspbuf_clientid4 = { sizeof(str_clientid4), str_clientid4, str_clientid4}; int rc; /* Make sure str_client is always printable even * if log level changes midstream. */ display_printf(&dspbuf_client, "(unknown)"); display_reset_buffer(&dspbuf_client); resp->resop = NFS4_OP_SETCLIENTID_CONFIRM; res_SETCLIENTID_CONFIRM4->status = NFS4_OK; clientid = arg_SETCLIENTID_CONFIRM4->clientid; display_clientid(&dspbuf_clientid4, clientid); if (data->minorversion > 0) { res_SETCLIENTID_CONFIRM4->status = NFS4ERR_NOTSUPP; return res_SETCLIENTID_CONFIRM4->status; } if (op_ctx->client != NULL) str_client_addr = op_ctx->client->hostaddr_str; if (isDebug(COMPONENT_CLIENTID)) { sprint_mem(str_verifier, arg_SETCLIENTID_CONFIRM4->setclientid_confirm, NFS4_VERIFIER_SIZE); } else { str_verifier[0] = '\0'; } LogDebug(COMPONENT_CLIENTID, "SETCLIENTID_CONFIRM client addr=%s clientid=%s setclientid_confirm=%s", str_client_addr, str_clientid4, str_verifier); /* First try to look up unconfirmed record */ rc = nfs_client_id_get_unconfirmed(clientid, &unconf); if (rc == CLIENT_ID_SUCCESS) { client_record = unconf->cid_client_record; if (isFullDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, unconf); LogFullDebug(COMPONENT_CLIENTID, "Found %s", str); } } else { rc = nfs_client_id_get_confirmed(clientid, &conf); if (rc != CLIENT_ID_SUCCESS) { /* No record whatsoever of this clientid */ LogDebug(COMPONENT_CLIENTID, "%s clientid = %s", clientid_error_to_str(rc), str_clientid4); res_SETCLIENTID_CONFIRM4->status = clientid_error_to_nfsstat_no_expire(rc); return res_SETCLIENTID_CONFIRM4->status; } client_record = conf->cid_client_record; if (isFullDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, conf); LogFullDebug(COMPONENT_CLIENTID, "Found %s", str); } } PTHREAD_MUTEX_lock(&client_record->cr_mutex); inc_client_record_ref(client_record); if (isFullDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_record(&dspbuf, client_record); LogFullDebug(COMPONENT_CLIENTID, "Client Record %s cr_confirmed_rec=%p cr_unconfirmed_rec=%p", str, client_record->cr_confirmed_rec, client_record->cr_unconfirmed_rec); } /* At this point one and only one of pconf and punconf is non-NULL */ if (unconf != NULL) { /* First must match principal */ if (!nfs_compare_clientcred(&unconf->cid_credential, &data->credential) || op_ctx->client == NULL || unconf->gsh_client == NULL || op_ctx->client != unconf->gsh_client) { if (isDebug(COMPONENT_CLIENTID)) { char *unconfirmed_addr = "(unknown)"; if (unconf->gsh_client != NULL) unconfirmed_addr = unconf->gsh_client->hostaddr_str; LogDebug(COMPONENT_CLIENTID, "Unconfirmed ClientId %s->'%s': Principals do not match... unconfirmed addr=%s Return NFS4ERR_CLID_INUSE", str_clientid4, str_client_addr, unconfirmed_addr); } res_SETCLIENTID_CONFIRM4->status = NFS4ERR_CLID_INUSE; dec_client_id_ref(unconf); goto out; } else if (unconf->cid_confirmed == CONFIRMED_CLIENT_ID && memcmp(unconf->cid_verifier, arg_SETCLIENTID_CONFIRM4->setclientid_confirm, NFS4_VERIFIER_SIZE) == 0) { /* We must have raced with another SETCLIENTID_CONFIRM */ if (isDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = { sizeof(str), str, str}; display_client_id_rec(&dspbuf, unconf); LogDebug(COMPONENT_CLIENTID, "Race against confirm for %s", str); } res_SETCLIENTID_CONFIRM4->status = NFS4_OK; dec_client_id_ref(unconf); goto out; } else if (unconf->cid_confirmed != UNCONFIRMED_CLIENT_ID) { /* We raced with another thread that dealt * with this unconfirmed record. Release our * reference, and pretend we didn't find a * record. */ if (isDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = { sizeof(str), str, str}; display_client_id_rec(&dspbuf, unconf); LogDebug(COMPONENT_CLIENTID, "Race against expire for %s", str); } res_SETCLIENTID_CONFIRM4->status = NFS4ERR_STALE_CLIENTID; dec_client_id_ref(unconf); goto out; } } if (conf != NULL) { if (isDebug(COMPONENT_CLIENTID) && conf != NULL) display_clientid_name(&dspbuf_client, conf); /* First must match principal */ if (!nfs_compare_clientcred(&conf->cid_credential, &data->credential) || op_ctx->client == NULL || conf->gsh_client == NULL || op_ctx->client != conf->gsh_client) { if (isDebug(COMPONENT_CLIENTID)) { char *confirmed_addr = "(unknown)"; if (conf->gsh_client != NULL) confirmed_addr = conf->gsh_client->hostaddr_str; LogDebug(COMPONENT_CLIENTID, "Confirmed ClientId %s->%s addr=%s: Principals do not match... confirmed addr=%s Return NFS4ERR_CLID_INUSE", str_clientid4, str_client, str_client_addr, confirmed_addr); } res_SETCLIENTID_CONFIRM4->status = NFS4ERR_CLID_INUSE; } else if (memcmp( conf->cid_verifier, arg_SETCLIENTID_CONFIRM4->setclientid_confirm, NFS4_VERIFIER_SIZE) == 0) { /* In this case, the record was confirmed and * we have received a retry */ if (isDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = { sizeof(str), str, str}; display_client_id_rec(&dspbuf, conf); LogDebug(COMPONENT_CLIENTID, "Retry confirm for %s", str); } res_SETCLIENTID_CONFIRM4->status = NFS4_OK; } else { /* This is a case not covered... Return * NFS4ERR_CLID_INUSE */ if (isDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = { sizeof(str), str, str}; char str_conf_verifier[NFS4_VERIFIER_SIZE * 2 + 1]; sprint_mem(str_conf_verifier, conf->cid_verifier, NFS4_VERIFIER_SIZE); display_client_id_rec(&dspbuf, conf); LogDebug(COMPONENT_CLIENTID, "Confirm verifier=%s doesn't match verifier=%s for %s", str_conf_verifier, str_verifier, str); } res_SETCLIENTID_CONFIRM4->status = NFS4ERR_CLID_INUSE; } /* Release our reference to the confirmed clientid. */ dec_client_id_ref(conf); goto out; } /* We don't need to do any further principal checks, we can't * have a confirmed clientid record with a different principal * than the unconfirmed record. Also, at this point, we have * a matching unconfirmed clientid (punconf != NULL and pconf * == NULL). */ /* Make sure we have a reference to the confirmed clientid * record if any */ if (conf == NULL) { conf = client_record->cr_confirmed_rec; if (isDebug(COMPONENT_CLIENTID) && conf != NULL) display_clientid_name(&dspbuf_client, conf); /* Need a reference to the confirmed record for below */ if (conf != NULL) inc_client_id_ref(conf); } if (conf != NULL && conf->cid_clientid != clientid) { /* Old confirmed record - need to expire it */ if (isDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, conf); LogDebug(COMPONENT_CLIENTID, "Expiring %s", str); } /* Expire clientid and release our reference. */ nfs_client_id_expire(conf, false); dec_client_id_ref(conf); conf = NULL; } if (conf != NULL) { /* At this point we are updating the confirmed * clientid. Update the confirmed record from the * unconfirmed record. */ if (isFullDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, unconf); LogFullDebug(COMPONENT_CLIENTID, "Updating from %s", str); } /* Copy callback information into confirmed clientid record */ memcpy(conf->cid_cb.v40.cb_client_r_addr, unconf->cid_cb.v40.cb_client_r_addr, sizeof(conf->cid_cb.v40.cb_client_r_addr)); conf->cid_cb.v40.cb_addr = unconf->cid_cb.v40.cb_addr; conf->cid_cb.v40.cb_program = unconf->cid_cb.v40.cb_program; conf->cid_cb.v40.cb_callback_ident = unconf->cid_cb.v40.cb_callback_ident; nfs_rpc_destroy_chan(&conf->cid_cb.v40.cb_chan); memcpy(conf->cid_verifier, unconf->cid_verifier, NFS4_VERIFIER_SIZE); /* unhash the unconfirmed clientid record */ remove_unconfirmed_client_id(unconf); /* Release our reference to the unconfirmed entry */ dec_client_id_ref(unconf); if (isDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, conf); LogDebug(COMPONENT_CLIENTID, "Updated %s", str); } /* Check and update call back channel state */ if (nfs_param.nfsv4_param.allow_delegations && nfs_test_cb_chan(conf) != RPC_SUCCESS) { set_cb_chan_down(conf, true); LogCrit(COMPONENT_CLIENTID, "setclid confirm: Callback channel is down"); } else { set_cb_chan_down(conf, false); LogDebug(COMPONENT_CLIENTID, "setclid confirm: Callback channel is UP"); } /* Release our reference to the confirmed clientid. */ dec_client_id_ref(conf); } else { /* This is a new clientid */ if (isFullDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, unconf); LogFullDebug(COMPONENT_CLIENTID, "Confirming new %s", str); } rc = nfs_client_id_confirm(unconf, COMPONENT_CLIENTID); if (rc != CLIENT_ID_SUCCESS) { res_SETCLIENTID_CONFIRM4->status = clientid_error_to_nfsstat_no_expire(rc); LogEvent(COMPONENT_CLIENTID, "FAILED to confirm client"); /* Release our reference to the unconfirmed record */ dec_client_id_ref(unconf); goto out; } /* check if the client can perform reclaims */ nfs4_chk_clid(unconf); if (isDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, unconf); LogDebug(COMPONENT_CLIENTID, "Confirmed %s", str); } /* Check and update call back channel state */ if (nfs_param.nfsv4_param.allow_delegations && nfs_test_cb_chan(unconf) != RPC_SUCCESS) { set_cb_chan_down(unconf, true); LogCrit(COMPONENT_CLIENTID, "setclid confirm: Callback channel is down"); } else { set_cb_chan_down(unconf, false); LogDebug(COMPONENT_CLIENTID, "setclid confirm: Callback channel is UP"); } /* Release our reference to the now confirmed record */ dec_client_id_ref(unconf); } if (isFullDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_record(&dspbuf, client_record); LogFullDebug(COMPONENT_CLIENTID, "Client Record %s cr_confirmed_rec=%p cr_unconfirmed_rec=%p", str, client_record->cr_confirmed_rec, client_record->cr_unconfirmed_rec); } /* Successful exit */ res_SETCLIENTID_CONFIRM4->status = NFS4_OK; out: PTHREAD_MUTEX_unlock(&client_record->cr_mutex); /* Release our reference to the client record and return */ dec_client_record_ref(client_record); return res_SETCLIENTID_CONFIRM4->status; }
fsal_posixdb_status_t fsal_posixdb_getInfoFromName(fsal_posixdb_conn * p_conn, /* IN */ posixfsal_handle_t * p_parent_directory_handle, /* IN/OUT */ fsal_name_t * p_objectname, /* IN */ fsal_path_t * p_path, /* OUT */ posixfsal_handle_t * p_handle /* OUT */ ) { PGresult *p_res; fsal_posixdb_status_t st; char handleid_str[MAX_HANDLEIDSTR_SIZE]; char handlets_str[MAX_HANDLETSSTR_SIZE]; const char *paramValues[3] = { handleid_str, handlets_str, p_objectname->name }; /* sanity check */ if(!p_conn || !p_handle) { ReturnCodeDB(ERR_FSAL_POSIXDB_FAULT, 0); } CheckConn(p_conn); LogFullDebug(COMPONENT_FSAL, "object_name='%s'\n", p_objectname->name); BeginTransaction(p_conn, p_res); /* lookup for the handle of the file */ if(p_parent_directory_handle && p_parent_directory_handle->data.id) { snprintf(handleid_str, MAX_HANDLEIDSTR_SIZE, "%lli", p_parent_directory_handle->data.id); snprintf(handlets_str, MAX_HANDLETSSTR_SIZE, "%i", p_parent_directory_handle->data.ts); p_res = PQexecPrepared(p_conn, "lookupHandleByName", 3, paramValues, NULL, NULL, 0); CheckResult(p_res); } else { // get root handle : p_res = PQexecPrepared(p_conn, "lookupRootHandle", 0, NULL, NULL, NULL, 0); CheckResult(p_res); } /* p_res contains : Parent.handleid, Parent.handlets, Handle.deviceId, Handle.inode, Handle.nlink, Handle.ctime, Handle.ftype */ /* entry not found */ if(PQntuples(p_res) != 1) { PQclear(p_res); RollbackTransaction(p_conn, p_res); ReturnCodeDB(ERR_FSAL_POSIXDB_NOENT, 0); } p_handle->data.id = atoll(PQgetvalue(p_res, 0, 0)); p_handle->data.ts = atoi(PQgetvalue(p_res, 0, 1)); posixdb_internal_fillFileinfoFromStrValues(&(p_handle->data.info), PQgetvalue(p_res, 0, 2), PQgetvalue(p_res, 0, 3), PQgetvalue(p_res, 0, 4), /* nlink */ PQgetvalue(p_res, 0, 5), /* ctime */ PQgetvalue(p_res, 0, 6) /* ftype */ ); PQclear(p_res); /* Build the path of the object */ if(p_path && p_objectname) { /* build the path of the Parent */ st = fsal_posixdb_buildOnePath(p_conn, p_parent_directory_handle, p_path); if(st.major != ERR_FSAL_POSIXDB_NOERR) { RollbackTransaction(p_conn, p_res); return st; } /* then concatenate the filename */ if(!(p_path->len + 1 + p_objectname->len < FSAL_MAX_PATH_LEN)) { RollbackTransaction(p_conn, p_res); ReturnCodeDB(ERR_FSAL_POSIXDB_PATHTOOLONG, 0); } p_path->path[p_path->len] = '/'; strcpy(&p_path->path[p_path->len + 1], p_objectname->name); p_path->len += 1 + p_objectname->len; /* add the the path to cache */ fsal_posixdb_CachePath(p_handle, p_path); } else { /* update handle if it was in cache */ fsal_posixdb_UpdateInodeCache(p_handle); } EndTransaction(p_conn, p_res); ReturnCodeDB(ERR_FSAL_POSIXDB_NOERR, 0); }
fsal_posixdb_status_t fsal_posixdb_getInfoFromHandle(fsal_posixdb_conn * p_conn, /* IN */ posixfsal_handle_t * p_object_handle, /* IN/OUT */ fsal_path_t * p_paths, /* OUT */ int paths_size, /* IN */ int *p_count /* OUT */ ) { PGresult *p_res; fsal_posixdb_status_t st; char handleid_str[MAX_HANDLEIDSTR_SIZE]; char handlets_str[MAX_HANDLETSSTR_SIZE]; posixfsal_handle_t parent_directory_handle; int i_path; int toomanypaths = 0; const char *paramValues[2] = { handleid_str, handlets_str }; /* sanity check */ if(!p_conn || !p_object_handle || ((!p_paths || !p_count) && paths_size > 0)) { ReturnCodeDB(ERR_FSAL_POSIXDB_FAULT, 0); } CheckConn(p_conn); LogFullDebug(COMPONENT_FSAL, "OBJECT_ID=%lli\n", p_object_handle->data.id); BeginTransaction(p_conn, p_res); /* lookup for the handle of the file */ snprintf(handleid_str, MAX_HANDLEIDSTR_SIZE, "%lli", p_object_handle->data.id); snprintf(handlets_str, MAX_HANDLETSSTR_SIZE, "%i", p_object_handle->data.ts); if(!fsal_posixdb_GetInodeCache(p_object_handle)) { p_res = PQexecPrepared(p_conn, "lookupHandle", 2, paramValues, NULL, NULL, 0); CheckResult(p_res); /* p_res contains : Handle.deviceId, Handle.inode, Handle.nlink, Handle.ctime, Handle.ftype */ LogDebug(COMPONENT_FSAL, "lookupHandle(%u,%u)", (unsigned int)p_object_handle->data.id, (unsigned int)p_object_handle->data.ts); /* entry not found */ if(PQntuples(p_res) != 1) { LogDebug(COMPONENT_FSAL, "lookupHandle=%d entries", PQntuples(p_res)); RollbackTransaction(p_conn, p_res); ReturnCodeDB(ERR_FSAL_POSIXDB_NOENT, 0); } posixdb_internal_fillFileinfoFromStrValues(&(p_object_handle->data.info), PQgetvalue(p_res, 0, 0), PQgetvalue(p_res, 0, 1), PQgetvalue(p_res, 0, 2), /* nlink */ PQgetvalue(p_res, 0, 3), /* ctime */ PQgetvalue(p_res, 0, 4) /* ftype */ ); PQclear(p_res); /* update the inode */ fsal_posixdb_UpdateInodeCache(p_object_handle); } /* Build the paths of the object */ if(p_paths) { /* find all the paths to the object */ p_res = PQexecPrepared(p_conn, "lookupPaths", 2, paramValues, NULL, NULL, 0); CheckResult(p_res); /* p_res contains name, handleidparent, handletsparent */ *p_count = PQntuples(p_res); if(*p_count == 0) { RollbackTransaction(p_conn, p_res); ReturnCodeDB(ERR_FSAL_POSIXDB_NOPATH, 0); } if(*p_count > paths_size) { toomanypaths = 1; LogCrit(COMPONENT_FSAL, "Too many paths found for object %s.%s: found=%u, max=%d", handleid_str, handlets_str, *p_count, paths_size); *p_count = paths_size; } for(i_path = 0; i_path < *p_count; i_path++) { unsigned int tmp_len; /* build the path of the parent directory */ parent_directory_handle.data.id = atoll(PQgetvalue(p_res, i_path, 1)); parent_directory_handle.data.ts = atoi(PQgetvalue(p_res, i_path, 2)); st = fsal_posixdb_buildOnePath(p_conn, &parent_directory_handle, &p_paths[i_path]); if(st.major != ERR_FSAL_POSIXDB_NOERR) { RollbackTransaction(p_conn, p_res); return st; } tmp_len = p_paths[i_path].len; if((tmp_len > 0) && (p_paths[i_path].path[tmp_len - 1] == '/')) { /* then concatenate the name of the file */ /* but not concatenate '/' */ if((tmp_len + strlen(PQgetvalue(p_res, i_path, 0)) >= FSAL_MAX_PATH_LEN)) { RollbackTransaction(p_conn, p_res); ReturnCodeDB(ERR_FSAL_POSIXDB_PATHTOOLONG, 0); } strcpy(&p_paths[i_path].path[tmp_len], PQgetvalue(p_res, i_path, 0)); p_paths[i_path].len += strlen(PQgetvalue(p_res, i_path, 0)); } else { /* then concatenate the name of the file */ if((tmp_len + 1 + strlen(PQgetvalue(p_res, i_path, 0)) >= FSAL_MAX_PATH_LEN)) { RollbackTransaction(p_conn, p_res); ReturnCodeDB(ERR_FSAL_POSIXDB_PATHTOOLONG, 0); } p_paths[i_path].path[tmp_len] = '/'; strcpy(&p_paths[i_path].path[tmp_len + 1], PQgetvalue(p_res, i_path, 0)); p_paths[i_path].len += 1 + strlen(PQgetvalue(p_res, i_path, 0)); } /* insert the object into cache */ fsal_posixdb_CachePath(p_object_handle, &p_paths[i_path]); } PQclear(p_res); } EndTransaction(p_conn, p_res); ReturnCodeDB(toomanypaths ? ERR_FSAL_POSIXDB_TOOMANYPATHS : ERR_FSAL_POSIXDB_NOERR, 0); }
bool_t nlm_block_data_to_fsal_context(state_block_data_t * block_data, fsal_op_context_t * fsal_context) { exportlist_t * pexport = NULL; short exportid; fsal_status_t fsal_status; state_nlm_block_data_t * nlm_block_data = &block_data->sbd_block_data.sbd_nlm_block_data; /* Get export ID from handle */ exportid = nlm4_FhandleToExportId(&nlm_block_data->sbd_nlm_fh); /* Get export matching export ID */ if(exportid < 0 || (pexport = nfs_Get_export_by_id(nfs_param.pexportlist, exportid)) == NULL || (pexport->export_perms.options & EXPORT_OPTION_NFSV3) == 0) { /* Reject the request for authentication reason (incompatible file handle) */ if(isInfo(COMPONENT_NLM)) { char dumpfh[1024]; char *reason; char addrbuf[SOCK_NAME_MAX]; sprint_sockaddr(&nlm_block_data->sbd_nlm_hostaddr, addrbuf, sizeof(addrbuf)); if(exportid < 0) reason = "has badly formed handle"; else if(pexport == NULL) reason = "has invalid export"; else reason = "V3 not allowed on this export"; sprint_fhandle_nlm(dumpfh, &nlm_block_data->sbd_nlm_fh); LogMajor(COMPONENT_NLM, "NLM4 granted lock from host %s %s, FH=%s", addrbuf, reason, dumpfh); } return FALSE; } LogFullDebug(COMPONENT_NLM, "Found export entry for path=%s as exportid=%d", pexport->fullpath, pexport->id); /* Build the credentials */ fsal_status = FSAL_GetClientContext(fsal_context, &pexport->FS_export_context, block_data->sbd_credential.user, block_data->sbd_credential.group, block_data->sbd_credential.alt_groups, block_data->sbd_credential.nbgroups); if(FSAL_IS_ERROR(fsal_status)) { LogEvent(COMPONENT_NLM, "Could not get credentials for (uid=%d,gid=%d), fsal error=(%d,%d)", block_data->sbd_credential.user, block_data->sbd_credential.group, fsal_status.major, fsal_status.minor); return FALSE; } else LogDebug(COMPONENT_NLM, "FSAL Cred acquired for (uid=%d,gid=%d)", block_data->sbd_credential.user, block_data->sbd_credential.group); return TRUE; }
int nlm_process_share_parms(struct svc_req * preq, nlm4_share * share, cache_entry_t ** ppentry, fsal_op_context_t * pcontext, care_t care, state_nsm_client_t ** ppnsm_client, state_nlm_client_t ** ppnlm_client, state_owner_t ** ppowner) { cache_inode_fsal_data_t fsal_data; fsal_attrib_list_t attr; cache_inode_status_t cache_status; SVCXPRT *ptr_svc = preq->rq_xprt; int rc; *ppnsm_client = NULL; *ppnlm_client = NULL; *ppowner = NULL; /* Convert file handle into a cache entry */ if(share->fh.n_len > MAX_NETOBJ_SZ || !nfs3_FhandleToFSAL((nfs_fh3 *) &share->fh, &fsal_data.fh_desc, pcontext)) { /* handle is not valid */ return NLM4_STALE_FH; } /* Now get the cached inode attributes */ *ppentry = cache_inode_get(&fsal_data, &attr, pcontext, NULL, &cache_status); if(*ppentry == NULL) { /* handle is not valid */ return NLM4_STALE_FH; } *ppnsm_client = get_nsm_client(care, ptr_svc, share->caller_name); if(*ppnsm_client == NULL) { /* If NSM Client is not found, and we don't care (for unshare), * just return GRANTED (the unshare must succeed, there can't be * any shares). */ if(care != CARE_NOT) rc = NLM4_DENIED_NOLOCKS; else rc = NLM4_GRANTED; goto out_put; } *ppnlm_client = get_nlm_client(care, ptr_svc, *ppnsm_client, share->caller_name); if(*ppnlm_client == NULL) { /* If NLM Client is not found, and we don't care (such as unlock), * just return GRANTED (the unlock must succeed, there can't be * any locks). */ dec_nsm_client_ref(*ppnsm_client); if(care != CARE_NOT) rc = NLM4_DENIED_NOLOCKS; else rc = NLM4_GRANTED; goto out_put; } *ppowner = get_nlm_owner(care, *ppnlm_client, &share->oh, 0); if(*ppowner == NULL) { LogDebug(COMPONENT_NLM, "Could not get NLM Owner"); dec_nsm_client_ref(*ppnsm_client); dec_nlm_client_ref(*ppnlm_client); *ppnlm_client = NULL; /* If owner is not found, and we don't care (such as unlock), * just return GRANTED (the unlock must succeed, there can't be * any locks). */ if(care != CARE_NOT) rc = NLM4_DENIED_NOLOCKS; else rc = NLM4_GRANTED; goto out_put; } LogFullDebug(COMPONENT_NLM, "Parameters Processed"); return -1; out_put: cache_inode_put(*ppentry); *ppentry = NULL; return rc; }
int nlm_process_parameters(struct svc_req * preq, bool_t exclusive, nlm4_lock * alock, fsal_lock_param_t * plock, cache_entry_t ** ppentry, fsal_op_context_t * pcontext, care_t care, state_nsm_client_t ** ppnsm_client, state_nlm_client_t ** ppnlm_client, state_owner_t ** ppowner, state_block_data_t ** ppblock_data) { cache_inode_fsal_data_t fsal_data; fsal_attrib_list_t attr; cache_inode_status_t cache_status; SVCXPRT *ptr_svc = preq->rq_xprt; int rc; *ppnsm_client = NULL; *ppnlm_client = NULL; *ppowner = NULL; /* Convert file handle into a cache entry */ if(alock->fh.n_len > MAX_NETOBJ_SZ || !nfs3_FhandleToFSAL((nfs_fh3 *) &alock->fh, &fsal_data.fh_desc, pcontext)) { /* handle is not valid */ return NLM4_STALE_FH; } /* Now get the cached inode attributes */ *ppentry = cache_inode_get(&fsal_data, &attr, pcontext, NULL, &cache_status); if(*ppentry == NULL) { /* handle is not valid */ return NLM4_STALE_FH; } *ppnsm_client = get_nsm_client(care, ptr_svc, alock->caller_name); if(*ppnsm_client == NULL) { /* If NSM Client is not found, and we don't care (such as unlock), * just return GRANTED (the unlock must succeed, there can't be * any locks). */ if(care != CARE_NOT) rc = NLM4_DENIED_NOLOCKS; else rc = NLM4_GRANTED; goto out_put; } *ppnlm_client = get_nlm_client(care, ptr_svc, *ppnsm_client, alock->caller_name); if(*ppnlm_client == NULL) { /* If NLM Client is not found, and we don't care (such as unlock), * just return GRANTED (the unlock must succeed, there can't be * any locks). */ dec_nsm_client_ref(*ppnsm_client); if(care != CARE_NOT) rc = NLM4_DENIED_NOLOCKS; else rc = NLM4_GRANTED; goto out_put; } *ppowner = get_nlm_owner(care, *ppnlm_client, &alock->oh, alock->svid); if(*ppowner == NULL) { LogDebug(COMPONENT_NLM, "Could not get NLM Owner"); dec_nsm_client_ref(*ppnsm_client); dec_nlm_client_ref(*ppnlm_client); *ppnlm_client = NULL; /* If owner is not found, and we don't care (such as unlock), * just return GRANTED (the unlock must succeed, there can't be * any locks). */ if(care != CARE_NOT) rc = NLM4_DENIED_NOLOCKS; else rc = NLM4_GRANTED; goto out_put; } if(ppblock_data != NULL) { *ppblock_data = gsh_calloc(1, sizeof(**ppblock_data)); /* Fill in the block data, if we don't get one, we will just proceed * without (which will mean the lock doesn't block. */ if(*ppblock_data != NULL) { if(copy_xprt_addr(&(*ppblock_data)->sbd_block_data.sbd_nlm_block_data.sbd_nlm_hostaddr, ptr_svc) == 0) { LogFullDebug(COMPONENT_NLM, "copy_xprt_addr failed for Program %d, Version %d, Function %d", (int)preq->rq_prog, (int)preq->rq_vers, (int)preq->rq_proc); gsh_free(*ppblock_data); *ppblock_data = NULL; rc = NLM4_FAILED; goto out_put; } (*ppblock_data)->sbd_granted_callback = nlm_granted_callback; (*ppblock_data)->sbd_block_data.sbd_nlm_block_data.sbd_nlm_fh.n_bytes = (*ppblock_data)->sbd_block_data.sbd_nlm_block_data.sbd_nlm_fh_buf; (*ppblock_data)->sbd_block_data.sbd_nlm_block_data.sbd_nlm_fh.n_len = alock->fh.n_len; memcpy((*ppblock_data)->sbd_block_data.sbd_nlm_block_data.sbd_nlm_fh_buf, alock->fh.n_bytes, alock->fh.n_len); /* FSF TODO: Ultimately I think the following will go away, we won't need the context, just the export */ /* Copy credentials from pcontext */ #ifdef _USE_HPSS /** @todo : PhD: Think about removing hpsscred_t from FSAL */ (*ppblock_data)->sbd_credential.user = pcontext->credential.hpss_usercred.Uid ; (*ppblock_data)->sbd_credential.group = pcontext->credential.hpss_usercred.Gid ; #else (*ppblock_data)->sbd_credential = pcontext->credential; /* Copy the alt groups list */ if(pcontext->credential.nbgroups != 0) { (*ppblock_data)->sbd_credential.alt_groups = gsh_malloc(sizeof(gid_t) * pcontext->credential.nbgroups); if((*ppblock_data)->sbd_credential.alt_groups == NULL) { gsh_free(*ppblock_data); *ppblock_data = NULL; rc = NLM4_FAILED; goto out_put; } memcpy((*ppblock_data)->sbd_credential.alt_groups, pcontext->credential.alt_groups, pcontext->credential.nbgroups); } #endif } } /* Fill in plock */ plock->lock_type = exclusive ? FSAL_LOCK_W : FSAL_LOCK_R; plock->lock_start = alock->l_offset; plock->lock_length = alock->l_len; LogFullDebug(COMPONENT_NLM, "Parameters Processed"); return -1; out_put: cache_inode_put(*ppentry); *ppentry = NULL; return rc; }
/** * @brief Set attributes on an object * * This function sets attributes on an object. Which attributes are * set is determined by attrib_set->mask. The FSAL must manage bypass * or not of share reservations, and a state may be passed. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * @param[in] attrib_set Attributes to set * * @return FSAL status. */ fsal_status_t vfs_setattr2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, struct attrlist *attrib_set) { struct vfs_fsal_obj_handle *myself; fsal_status_t status = {0, 0}; int retval = 0; fsal_openflags_t openflags = FSAL_O_ANY; bool has_lock = false; bool need_fsync = false; bool closefd = false; int my_fd; const char *func; /* apply umask, if mode attribute is to be changed */ if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MODE)) attrib_set->mode &= ~op_ctx->fsal_export->exp_ops.fs_umask(op_ctx->fsal_export); myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); if (obj_hdl->fsal != obj_hdl->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", obj_hdl->fsal->name, obj_hdl->fs->fsal != NULL ? obj_hdl->fs->fsal->name : "(none)"); return fsalstat(posix2fsal_error(EXDEV), EXDEV); } #ifdef ENABLE_VFS_DEBUG_ACL #ifdef ENABLE_RFC_ACL if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MODE) && !FSAL_TEST_MASK(attrib_set->mask, ATTR_ACL)) { /* Set ACL from MODE */ struct attrlist attrs; fsal_prepare_attrs(&attrs, ATTR_ACL); status = obj_hdl->obj_ops.getattrs(obj_hdl, &attrs); if (FSAL_IS_ERROR(status)) return status; status = fsal_mode_to_acl(attrib_set, attrs.acl); /* Done with the attrs */ fsal_release_attrs(&attrs); } else { /* If ATTR_ACL is set, mode needs to be adjusted no matter what. * See 7530 s 6.4.1.3 */ if (!FSAL_TEST_MASK(attrib_set->mask, ATTR_MODE)) attrib_set->mode = myself->mode; status = fsal_acl_to_mode(attrib_set); } if (FSAL_IS_ERROR(status)) return status; #endif /* ENABLE_RFC_ACL */ #endif /* This is yet another "you can't get there from here". If this object * is a socket (AF_UNIX), an fd on the socket s useless _period_. * If it is for a symlink, without O_PATH, you will get an ELOOP error * and (f)chmod doesn't work for a symlink anyway - not that it matters * because access checking is not done on the symlink but the final * target. * AF_UNIX sockets are also ozone material. If the socket is already * active listeners et al, you can manipulate the mode etc. If it is * just sitting there as in you made it with a mknod. * (one of those leaky abstractions...) * or the listener forgot to unlink it, it is lame duck. */ /* Test if size is being set, make sure file is regular and if so, * require a read/write file descriptor. */ if (FSAL_TEST_MASK(attrib_set->mask, ATTR_SIZE)) { if (obj_hdl->type != REGULAR_FILE) { LogFullDebug(COMPONENT_FSAL, "Setting size on non-regular file"); return fsalstat(ERR_FSAL_INVAL, EINVAL); } openflags = FSAL_O_RDWR; } /* Get a usable file descriptor. Share conflict is only possible if * size is being set. */ status = find_fd(&my_fd, obj_hdl, bypass, state, openflags, &has_lock, &need_fsync, &closefd, false); if (FSAL_IS_ERROR(status)) { if (obj_hdl->type == SYMBOLIC_LINK && status.major == ERR_FSAL_PERM) { /* You cannot open_by_handle (XFS) a symlink and it * throws an EPERM error for it. open_by_handle_at * does not throw that error for symlinks so we play a * game here. Since there is not much we can do with * symlinks anyway, say that we did it * but don't actually do anything. * If you *really* want to tweek things * like owners, get a modern linux kernel... */ status = fsalstat(ERR_FSAL_NO_ERROR, 0); } LogFullDebug(COMPONENT_FSAL, "find_fd status=%s", fsal_err_txt(status)); goto out; } /** TRUNCATE **/ if (FSAL_TEST_MASK(attrib_set->mask, ATTR_SIZE)) { retval = ftruncate(my_fd, attrib_set->filesize); if (retval != 0) { /** @todo FSF: is this still necessary? * * XXX ESXi volume creation pattern reliably * reached this point in the past, however now that we * only use the already open file descriptor if it is * open read/write, this may no longer fail. * If there is some other error from ftruncate, then * we will needlessly retry, but without more detail * of the original failure, we can't be sure. * Fortunately permission checking is done by * Ganesha before calling here, so we won't get an * EACCES since this call is done as root. We could * get EFBIG, EPERM, or EINVAL. */ /** @todo FSF: re-open if we really still need this */ retval = ftruncate(my_fd, attrib_set->filesize); if (retval != 0) { func = "truncate"; goto fileerr; } } } /** CHMOD **/ if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MODE)) { /* The POSIX chmod call doesn't affect the symlink object, but * the entry it points to. So we must ignore it. */ if (obj_hdl->type != SYMBOLIC_LINK) { if (vfs_unopenable_type(obj_hdl->type)) retval = fchmodat( my_fd, myself->u.unopenable.name, fsal2unix_mode(attrib_set->mode), 0); else retval = fchmod( my_fd, fsal2unix_mode(attrib_set->mode)); if (retval != 0) { func = "chmod"; goto fileerr; } } } /** CHOWN **/ if (FSAL_TEST_MASK(attrib_set->mask, ATTR_OWNER | ATTR_GROUP)) { uid_t user = FSAL_TEST_MASK(attrib_set->mask, ATTR_OWNER) ? (int)attrib_set->owner : -1; gid_t group = FSAL_TEST_MASK(attrib_set->mask, ATTR_GROUP) ? (int)attrib_set->group : -1; if (vfs_unopenable_type(obj_hdl->type)) retval = fchownat(my_fd, myself->u.unopenable.name, user, group, AT_SYMLINK_NOFOLLOW); else if (obj_hdl->type == SYMBOLIC_LINK) retval = fchownat(my_fd, "", user, group, AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH); else retval = fchown(my_fd, user, group); if (retval) { func = "chown"; goto fileerr; } } /** UTIME **/ if (FSAL_TEST_MASK(attrib_set->mask, ATTRS_SET_TIME)) { struct timespec timebuf[2]; if (obj_hdl->type == SYMBOLIC_LINK) goto out; /* Setting time on symlinks is illegal */ /* Atime */ if (FSAL_TEST_MASK(attrib_set->mask, ATTR_ATIME_SERVER)) { timebuf[0].tv_sec = 0; timebuf[0].tv_nsec = UTIME_NOW; } else if (FSAL_TEST_MASK(attrib_set->mask, ATTR_ATIME)) { timebuf[0] = attrib_set->atime; } else { timebuf[0].tv_sec = 0; timebuf[0].tv_nsec = UTIME_OMIT; } /* Mtime */ if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MTIME_SERVER)) { timebuf[1].tv_sec = 0; timebuf[1].tv_nsec = UTIME_NOW; } else if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MTIME)) { timebuf[1] = attrib_set->mtime; } else { timebuf[1].tv_sec = 0; timebuf[1].tv_nsec = UTIME_OMIT; } if (vfs_unopenable_type(obj_hdl->type)) retval = vfs_utimesat(my_fd, myself->u.unopenable.name, timebuf, AT_SYMLINK_NOFOLLOW); else retval = vfs_utimes(my_fd, timebuf); if (retval != 0) { func = "utimes"; goto fileerr; } } /** SUBFSAL **/ if (myself->sub_ops && myself->sub_ops->setattrs) { status = myself->sub_ops->setattrs( myself, my_fd, attrib_set->mask, attrib_set); if (FSAL_IS_ERROR(status)) goto out; } errno = 0; fileerr: retval = errno; if (retval != 0) { LogDebug(COMPONENT_FSAL, "%s returned %s", func, strerror(retval)); } status = fsalstat(posix2fsal_error(retval), retval); out: if (closefd) close(my_fd); if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->lock); return status; }
/** * * get_req_uid_gid: * * * * @param ptr_req [IN] incoming request. * @param pexport_client [IN] related export client * @param pexport [IN] related export entry * @param user_credentials [OUT] Filled in structure with uid and gids * * @return TRUE if successful, FALSE otherwise * */ int get_req_uid_gid(struct svc_req *ptr_req, exportlist_client_entry_t * pexport_client, exportlist_t * pexport, struct user_cred *user_credentials) { struct authunix_parms *punix_creds = NULL; unsigned int rpcxid = 0; #ifdef _HAVE_GSSAPI struct svc_rpc_gss_data *gd = NULL; char principal[MAXNAMLEN]; #endif if (user_credentials == NULL) return FALSE; rpcxid = get_rpc_xid(ptr_req); switch (ptr_req->rq_cred.oa_flavor) { case AUTH_NONE: /* Nothing to be done here... */ LogFullDebug(COMPONENT_DISPATCH, "Request xid=%u has authentication AUTH_NONE", rpcxid); break; case AUTH_UNIX: LogFullDebug(COMPONENT_DISPATCH, "Request xid=%u has authentication AUTH_UNIX", rpcxid); /* We map the rq_cred to Authunix_parms */ punix_creds = (struct authunix_parms *)ptr_req->rq_clntcred; /* Get the uid/gid couple */ user_credentials->caller_uid = punix_creds->aup_uid; user_credentials->caller_gid = punix_creds->aup_gid; user_credentials->caller_glen = punix_creds->aup_len; user_credentials->caller_garray = punix_creds->aup_gids; LogFullDebug(COMPONENT_DISPATCH, "----> Uid=%u Gid=%u", (unsigned int)user_credentials->caller_uid, (unsigned int)user_credentials->caller_gid); break; #ifdef _HAVE_GSSAPI case RPCSEC_GSS: LogFullDebug(COMPONENT_DISPATCH, "Request xid=%u has authentication RPCSEC_GSS", rpcxid); /* Get the gss data to process them */ gd = SVCAUTH_PRIVATE(ptr_req->rq_xprt->xp_auth); if(isFullDebug(COMPONENT_RPCSEC_GSS)) { OM_uint32 maj_stat = 0; OM_uint32 min_stat = 0; char ptr[256]; gss_buffer_desc oidbuff; LogFullDebug(COMPONENT_RPCSEC_GSS, "----> RPCSEC_GSS svc=%u RPCSEC_GSS_SVC_NONE=%u RPCSEC_GSS_SVC_INTEGRITY=%u RPCSEC_GSS_SVC_PRIVACY=%u", gd->sec.svc, RPCSEC_GSS_SVC_NONE, RPCSEC_GSS_SVC_INTEGRITY, RPCSEC_GSS_SVC_PRIVACY); memcpy(&ptr, (void *)gd->ctx + 4, 4); LogFullDebug(COMPONENT_RPCSEC_GSS, "----> Client=%s length=%lu Qop=%u established=%u gss_ctx_id=%p|%p", (char *)gd->cname.value, gd->cname.length, gd->established, gd->sec.qop, gd->ctx, ptr); if((maj_stat = gss_oid_to_str(&min_stat, gd->sec.mech, &oidbuff)) != GSS_S_COMPLETE) { LogFullDebug(COMPONENT_DISPATCH, "Error in gss_oid_to_str: %u|%u", maj_stat, min_stat); } else { LogFullDebug(COMPONENT_RPCSEC_GSS, "----> Client mech=%s len=%lu", (char *)oidbuff.value, oidbuff.length); // Release the string (void)gss_release_buffer(&min_stat, &oidbuff); } } LogFullDebug(COMPONENT_RPCSEC_GSS, "Mapping principal %s to uid/gid", (char *)gd->cname.value); memcpy(principal, gd->cname.value, gd->cname.length); principal[gd->cname.length] = 0; /* Convert to uid */ if(!principal2uid(principal, &user_credentials->caller_uid)) { LogWarn(COMPONENT_IDMAPPER, "WARNING: Could not map principal to uid; mapping principal " "to anonymous uid/gid"); /* For compatibility with Linux knfsd, we set the uid/gid * to anonymous when a name->uid mapping can't be found. */ user_credentials->caller_uid = pexport->anonymous_uid; user_credentials->caller_gid = pexport->anonymous_gid; /* No alternate groups for "nobody" */ user_credentials->caller_glen = 0 ; user_credentials->caller_garray = NULL ; return TRUE; } if(uidgidmap_get(user_credentials->caller_uid, &user_credentials->caller_gid) != ID_MAPPER_SUCCESS) { LogMajor(COMPONENT_DISPATCH, "FAILURE: Could not resolve uidgid map for %u", user_credentials->caller_uid); user_credentials->caller_gid = -1; } LogFullDebug(COMPONENT_DISPATCH, "----> Uid=%u Gid=%u", (unsigned int)user_credentials->caller_uid, (unsigned int)user_credentials->caller_gid); user_credentials->caller_glen = 0; user_credentials->caller_garray = 0; break; #endif /* _USE_GSSRPC */ default: LogFullDebug(COMPONENT_DISPATCH, "FAILURE: Request xid=%u, has unsupported authentication %d", rpcxid, ptr_req->rq_cred.oa_flavor); /* Reject the request for weak authentication and return to worker */ return FALSE; break; } /* switch( ptr_req->rq_cred.oa_flavor ) */ return TRUE; }
fsal_status_t vfs_open2(struct fsal_obj_handle *obj_hdl, struct state_t *state, fsal_openflags_t openflags, enum fsal_create_mode createmode, const char *name, struct attrlist *attrib_set, fsal_verifier_t verifier, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out, bool *caller_perm_check) { int posix_flags = 0; int fd, dir_fd; int retval = 0; mode_t unix_mode; fsal_status_t status = {0, 0}; struct vfs_fd *my_fd = NULL; struct vfs_fsal_obj_handle *myself, *hdl = NULL; struct stat stat; vfs_file_handle_t *fh = NULL; bool truncated; bool created = false; if (state != NULL) my_fd = (struct vfs_fd *)(state + 1); myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); LogAttrlist(COMPONENT_FSAL, NIV_FULL_DEBUG, "attrib_set ", attrib_set, false); fsal2posix_openflags(openflags, &posix_flags); truncated = (posix_flags & O_TRUNC) != 0; LogFullDebug(COMPONENT_FSAL, truncated ? "Truncate" : "No truncate"); if (createmode >= FSAL_EXCLUSIVE) { /* Now fixup attrs for verifier if exclusive create */ set_common_verifier(attrib_set, verifier); } if (name == NULL) { /* This is an open by handle */ struct vfs_fsal_obj_handle *myself; myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); if (obj_hdl->fsal != obj_hdl->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", obj_hdl->fsal->name, obj_hdl->fs->fsal->name); return fsalstat(posix2fsal_error(EXDEV), EXDEV); } if (state != NULL) { /* Prepare to take the share reservation, but only if we * are called with a valid state (if state is NULL the * caller is a stateless create such as NFS v3 CREATE). */ /* This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->lock); /* Check share reservation conflicts. */ status = check_share_conflict(&myself->u.file.share, openflags, false); if (FSAL_IS_ERROR(status)) { PTHREAD_RWLOCK_unlock(&obj_hdl->lock); return status; } /* Take the share reservation now by updating the * counters. */ update_share_counters(&myself->u.file.share, FSAL_O_CLOSED, openflags); PTHREAD_RWLOCK_unlock(&obj_hdl->lock); } else { /* We need to use the global fd to continue, and take * the lock to protect it. */ my_fd = &hdl->u.file.fd; PTHREAD_RWLOCK_wrlock(&obj_hdl->lock); } status = vfs_open_my_fd(myself, openflags, posix_flags, my_fd); if (FSAL_IS_ERROR(status)) { if (state == NULL) { /* Release the lock taken above, and return * since there is nothing to undo. */ PTHREAD_RWLOCK_unlock(&obj_hdl->lock); return status; } else { /* Error - need to release the share */ goto undo_share; } } if (createmode >= FSAL_EXCLUSIVE || truncated) { /* Refresh the attributes */ struct stat stat; retval = fstat(my_fd->fd, &stat); if (retval == 0) { LogFullDebug(COMPONENT_FSAL, "New size = %" PRIx64, stat.st_size); } else { if (errno == EBADF) errno = ESTALE; status = fsalstat(posix2fsal_error(errno), errno); } /* Now check verifier for exclusive, but not for * FSAL_EXCLUSIVE_9P. */ if (!FSAL_IS_ERROR(status) && createmode >= FSAL_EXCLUSIVE && createmode != FSAL_EXCLUSIVE_9P && !check_verifier_stat(&stat, verifier)) { /* Verifier didn't match, return EEXIST */ status = fsalstat(posix2fsal_error(EEXIST), EEXIST); } } if (state == NULL) { /* If no state, release the lock taken above and return * status. */ PTHREAD_RWLOCK_unlock(&obj_hdl->lock); return status; } if (!FSAL_IS_ERROR(status)) { /* Return success. */ return status; } (void) vfs_close_my_fd(my_fd); undo_share: /* Can only get here with state not NULL and an error */ /* On error we need to release our share reservation * and undo the update of the share counters. * This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->lock); update_share_counters(&myself->u.file.share, openflags, FSAL_O_CLOSED); PTHREAD_RWLOCK_unlock(&obj_hdl->lock); return status; } /* In this path where we are opening by name, we can't check share * reservation yet since we don't have an object_handle yet. If we * indeed create the object handle (there is no race with another * open by name), then there CAN NOT be a share conflict, otherwise * the share conflict will be resolved when the object handles are * merged. */ #ifdef ENABLE_VFS_DEBUG_ACL if (createmode != FSAL_NO_CREATE) { /* Need to ammend attributes for inherited ACL, these will be * set later. We also need to test for permission to create * since there might be an ACL. */ struct attrlist attrs; fsal_accessflags_t access_type; access_type = FSAL_MODE_MASK_SET(FSAL_W_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_ADD_FILE); status = obj_hdl->obj_ops.test_access(obj_hdl, access_type, NULL, NULL, false); if (FSAL_IS_ERROR(status)) return status; fsal_prepare_attrs(&attrs, ATTR_ACL); status = obj_hdl->obj_ops.getattrs(obj_hdl, &attrs); if (FSAL_IS_ERROR(status)) return status; status.major = fsal_inherit_acls(attrib_set, attrs.acl, FSAL_ACE_FLAG_FILE_INHERIT); /* Done with the attrs */ fsal_release_attrs(&attrs); if (FSAL_IS_ERROR(status)) return status; } #endif /* ENABLE_VFS_DEBUG_ACL */ if (createmode != FSAL_NO_CREATE) { /* Now add in O_CREAT and O_EXCL. */ posix_flags |= O_CREAT; /* And if we are at least FSAL_GUARDED, do an O_EXCL create. */ if (createmode >= FSAL_GUARDED) posix_flags |= O_EXCL; /* Fetch the mode attribute to use in the openat system call. */ unix_mode = fsal2unix_mode(attrib_set->mode) & ~op_ctx->fsal_export->exp_ops.fs_umask(op_ctx->fsal_export); /* Don't set the mode if we later set the attributes */ FSAL_UNSET_MASK(attrib_set->mask, ATTR_MODE); } if (createmode == FSAL_UNCHECKED && (attrib_set->mask != 0)) { /* If we have FSAL_UNCHECKED and want to set more attributes * than the mode, we attempt an O_EXCL create first, if that * succeeds, then we will be allowed to set the additional * attributes, otherwise, we don't know we created the file * and this can NOT set the attributes. */ posix_flags |= O_EXCL; } dir_fd = vfs_fsal_open(myself, O_PATH | O_NOACCESS, &status.major); if (dir_fd < 0) return fsalstat(status.major, -dir_fd); /** @todo: not sure what this accomplishes... */ retval = vfs_stat_by_handle(dir_fd, &stat); if (retval < 0) { retval = errno; status = fsalstat(posix2fsal_error(retval), retval); goto direrr; } /* Become the user because we are creating an object in this dir. */ if (createmode != FSAL_NO_CREATE) fsal_set_credentials(op_ctx->creds); if ((posix_flags & O_CREAT) != 0) fd = openat(dir_fd, name, posix_flags, unix_mode); else fd = openat(dir_fd, name, posix_flags); if (fd == -1 && errno == EEXIST && createmode == FSAL_UNCHECKED) { /* We tried to create O_EXCL to set attributes and failed. * Remove O_EXCL and retry. We still try O_CREAT again just in * case file disappears out from under us. * * Note that because we have dropped O_EXCL, later on we will * not assume we created the file, and thus will not set * additional attributes. We don't need to separately track * the condition of not wanting to set attributes. */ posix_flags &= ~O_EXCL; fd = openat(dir_fd, name, posix_flags, unix_mode); } /* Preserve errno */ retval = errno; /* If we were creating, restore credentials now. */ if (createmode != FSAL_NO_CREATE) fsal_restore_ganesha_credentials(); if (fd < 0) { status = fsalstat(posix2fsal_error(retval), retval); goto direrr; } /* Remember if we were responsible for creating the file. * Note that in an UNCHECKED retry we MIGHT have re-created the * file and won't remember that. Oh well, so in that rare case we * leak a partially created file if we have a subsequent error in here. * Also notify caller to do permission check if we DID NOT create the * file. Note it IS possible in the case of a race between an UNCHECKED * open and an external unlink, we did create the file, but we will * still force a permission check. Of course that permission check * SHOULD succeed since we also won't set the mode the caller requested * and the default file create permissions SHOULD allow the owner * read/write. */ created = (posix_flags & O_EXCL) != 0; *caller_perm_check = !created; vfs_alloc_handle(fh); retval = vfs_name_to_handle(dir_fd, obj_hdl->fs, name, fh); if (retval < 0) { retval = errno; status = fsalstat(posix2fsal_error(retval), retval); goto fileerr; } retval = fstat(fd, &stat); if (retval < 0) { retval = errno; status = fsalstat(posix2fsal_error(retval), retval); goto fileerr; } /* allocate an obj_handle and fill it up */ hdl = alloc_handle(dir_fd, fh, obj_hdl->fs, &stat, myself->handle, name, op_ctx->fsal_export); if (hdl == NULL) { status = fsalstat(posix2fsal_error(ENOMEM), ENOMEM); goto fileerr; } /* If we didn't have a state above, use the global fd. At this point, * since we just created the global fd, no one else can have a * reference to it, and thus we can mamnipulate unlocked which is * handy since we can then call setattr2 which WILL take the lock * without a double locking deadlock. */ if (my_fd == NULL) my_fd = &hdl->u.file.fd; my_fd->fd = fd; my_fd->openflags = openflags; *new_obj = &hdl->obj_handle; if (created && attrib_set->mask != 0) { /* Set attributes using our newly opened file descriptor as the * share_fd if there are any left to set (mode and truncate * have already been handled). * * Note that we only set the attributes if we were responsible * for creating the file and we have attributes to set. * * Note if we have ENABLE_VFS_DEBUG_ACL an inherited ACL might * be part of the attributes we are setting here. */ status = (*new_obj)->obj_ops.setattr2(*new_obj, false, state, attrib_set); if (FSAL_IS_ERROR(status)) { /* Release the handle we just allocated. */ (*new_obj)->obj_ops.release(*new_obj); *new_obj = NULL; goto fileerr; } if (attrs_out != NULL) { status = (*new_obj)->obj_ops.getattrs(*new_obj, attrs_out); if (FSAL_IS_ERROR(status) && (attrs_out->mask & ATTR_RDATTR_ERR) == 0) { /* Get attributes failed and caller expected * to get the attributes. Otherwise continue * with attrs_out indicating ATTR_RDATTR_ERR. */ goto fileerr; } } } else if (attrs_out != NULL) { /* Since we haven't set any attributes other than what was set * on create (if we even created), just use the stat results * we used to create the fsal_obj_handle. */ posix2fsal_attributes(&stat, attrs_out); /* Make sure ATTR_RDATTR_ERR is cleared on success. */ attrs_out->mask &= ~ATTR_RDATTR_ERR; } close(dir_fd); if (state != NULL) { /* Prepare to take the share reservation, but only if we are * called with a valid state (if state is NULL the caller is * a stateless create such as NFS v3 CREATE). */ /* This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&(*new_obj)->lock); /* Take the share reservation now by updating the counters. */ update_share_counters(&hdl->u.file.share, FSAL_O_CLOSED, openflags); PTHREAD_RWLOCK_unlock(&(*new_obj)->lock); } return fsalstat(ERR_FSAL_NO_ERROR, 0); fileerr: close(fd); /* Delete the file if we actually created it. */ if (created) unlinkat(dir_fd, name, 0); direrr: close(dir_fd); return fsalstat(posix2fsal_error(retval), retval); }
/** * FSAL_write: * Perform a write operation on an opened file. * * \param file_descriptor (input): * The file descriptor returned by FSAL_open. * \param p_context (input): * Authentication context for the operation (user,...). * \param seek_descriptor (optional input): * Specifies the position where data is to be written. * If not specified, data will be written at the current position. * \param buffer_size (input): * Amount (in bytes) of data to be written. * \param buffer (input): * Address in memory of the data to write to file. * \param write_amount (output): * Pointer to the amount of data (in bytes) that have been written * during this call. * * \return Major error codes: * - ERR_FSAL_NO_ERROR (no error) * - ERR_FSAL_INVAL (invalid parameter) * - ERR_FSAL_NOT_OPENED (tried to write in a non-opened fusefsal_file_t) * - ERR_FSAL_FAULT (a NULL pointer was passed as mandatory argument) * - Other error codes can be returned : * ERR_FSAL_IO, ERR_FSAL_NOSPC, ERR_FSAL_DQUOT... */ fsal_status_t FUSEFSAL_write(fsal_file_t * file_desc, /* IN */ fsal_op_context_t * p_context, /* IN */ fsal_seek_t * seek_descriptor, /* IN */ fsal_size_t buffer_size, /* IN */ caddr_t buffer, /* IN */ fsal_size_t * write_amount /* OUT */ ) { size_t req_size; int rc; off_t seekoffset = 0; struct stat stbuf; char object_path[FSAL_MAX_PATH_LEN]; fusefsal_file_t * file_descriptor = (fusefsal_file_t *)file_desc; /* sanity checks. */ if(!file_descriptor || !buffer || !write_amount) Return(ERR_FSAL_FAULT, 0, INDEX_FSAL_write); if(!p_fs_ops->write) Return(ERR_FSAL_NOTSUPP, 0, INDEX_FSAL_write); /* initialize returned values */ *write_amount = 0; req_size = (size_t) buffer_size; /* set context so it can be retrieved by FS */ fsal_set_thread_context((fsal_op_context_t *) &file_descriptor->context); LogFullDebug(COMPONENT_FSAL, "FSAL_write: FH=%"PRId64, file_descriptor->file_info.fh); /* get file's full path */ rc = NamespacePath(file_descriptor->file_handle.data.inode, file_descriptor->file_handle.data.device, file_descriptor->file_handle.data.validator, object_path); if(rc) Return(ERR_FSAL_STALE, rc, INDEX_FSAL_write); if(seek_descriptor) { switch (seek_descriptor->whence) { case FSAL_SEEK_SET: /* set absolute position to offset */ seekoffset = seek_descriptor->offset; break; case FSAL_SEEK_CUR: /* current position + offset */ seekoffset = file_descriptor->current_offset + seek_descriptor->offset; break; case FSAL_SEEK_END: /* set end of file + offset */ /* in this case, the only solution is to get entry attributes */ if(p_fs_ops->fgetattr) { rc = p_fs_ops->fgetattr(object_path, &stbuf, &(file_descriptor->file_info)); } else { rc = p_fs_ops->getattr(object_path, &stbuf); } if(rc) Return(fuse2fsal_error(rc, TRUE), rc, INDEX_FSAL_write); seekoffset = (off_t) stbuf.st_size + seek_descriptor->offset; break; default: LogCrit(COMPONENT_FSAL, "FSAL_write: Invalid seek parameter: whence=%d", seek_descriptor->whence); Return(ERR_FSAL_INVAL, 0, INDEX_FSAL_write); } } else { seekoffset = file_descriptor->current_offset; } TakeTokenFSCall(); rc = p_fs_ops->write(object_path, buffer, req_size, seekoffset, &(file_descriptor->file_info)); ReleaseTokenFSCall(); if(rc < 0) Return(fuse2fsal_error(rc, TRUE), rc, INDEX_FSAL_write); file_descriptor->current_offset = seekoffset + (off_t) rc; *write_amount = (fsal_size_t) rc; Return(ERR_FSAL_NO_ERROR, 0, INDEX_FSAL_write); }
fsal_status_t find_fd(int *fd, struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, fsal_openflags_t openflags, bool *has_lock, bool *need_fsync, bool *closefd, bool open_for_locks) { struct vfs_fsal_obj_handle *myself; struct vfs_filesystem *vfs_fs; struct vfs_fd temp_fd = {0, -1}, *out_fd = &temp_fd; fsal_status_t status = {ERR_FSAL_NO_ERROR, 0}; int rc, posix_flags; myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); vfs_fs = myself->obj_handle.fs->private_data; fsal2posix_openflags(openflags, &posix_flags); /* Handle nom-regular files */ switch (obj_hdl->type) { case SOCKET_FILE: case CHARACTER_FILE: case BLOCK_FILE: rc = vfs_open_by_handle(vfs_fs, myself->u.unopenable.dir, O_PATH | O_NOACCESS, &status.major); if (rc < 0) { LogDebug(COMPONENT_FSAL, "Failed with %s openflags 0x%08x", strerror(-rc), O_PATH | O_NOACCESS); return fsalstat(posix2fsal_error(-rc), -rc); } *fd = rc; *closefd = true; return status; case REGULAR_FILE: status = fsal_find_fd((struct fsal_fd **)&out_fd, obj_hdl, (struct fsal_fd *)&myself->u.file.fd, &myself->u.file.share, bypass, state, openflags, vfs_open_func, vfs_close_func, has_lock, need_fsync, closefd, open_for_locks); *fd = out_fd->fd; return status; case SYMBOLIC_LINK: posix_flags |= (O_PATH | O_RDWR | O_NOFOLLOW); break; case FIFO_FILE: posix_flags |= O_NONBLOCK; break; case DIRECTORY: break; case NO_FILE_TYPE: case EXTENDED_ATTR: return fsalstat(posix2fsal_error(EINVAL), EINVAL); } /* Open file descriptor for non-regular files. */ rc = vfs_fsal_open(myself, posix_flags, &status.major); if (rc < 0) { LogDebug(COMPONENT_FSAL, "Failed with %s openflags 0x%08x", strerror(-rc), openflags); return fsalstat(posix2fsal_error(-rc), -rc); } LogFullDebug(COMPONENT_FSAL, "Opened fd=%d for file of type %s", rc, object_file_type_to_str(obj_hdl->type)); *fd = rc; *closefd = true; return status; }
/** * FSAL_open: * Open a regular file for reading/writing its data content. * * \param filehandle (input): * Handle of the file to be read/modified. * \param cred (input): * Authentication context for the operation (user,...). * \param openflags (input): * Flags that indicates behavior for file opening and access. * This is an inclusive OR of the following values * ( such of them are not compatible) : * - FSAL_O_RDONLY: opening file for reading only. * - FSAL_O_RDWR: opening file for reading and writing. * - FSAL_O_WRONLY: opening file for writting only. * - FSAL_O_APPEND: always write at the end of the file. * - FSAL_O_TRUNC: truncate the file to 0 on opening. * \param file_descriptor (output): * The file descriptor to be used for FSAL_read/write operations. * \param file_attributes (optionnal input/output): * Post operation attributes. * As input, it defines the attributes that the caller * wants to retrieve (by positioning flags into this structure) * and the output is built considering this input * (it fills the structure according to the flags it contains). * * \return Major error codes: * - ERR_FSAL_NO_ERROR (no error) * - ERR_FSAL_ACCESS (user doesn't have the permissions for opening the file) * - ERR_FSAL_STALE (filehandle does not address an existing object) * - ERR_FSAL_INVAL (filehandle does not address a regular file, * or open flags are conflicting) * - ERR_FSAL_FAULT (a NULL pointer was passed as mandatory argument) * - Other error codes can be returned : * ERR_FSAL_IO, ... */ fsal_status_t FUSEFSAL_open(fsal_handle_t * file_hdl, /* IN */ fsal_op_context_t * p_context, /* IN */ fsal_openflags_t openflags, /* IN */ fsal_file_t * file_desc, /* OUT */ fsal_attrib_list_t * file_attributes /* [ IN/OUT ] */ ) { int rc = 0; char object_path[FSAL_MAX_PATH_LEN]; int file_info_provided = FALSE; fusefsal_handle_t * filehandle = (fusefsal_handle_t *)file_hdl; fusefsal_file_t * file_descriptor = (fusefsal_file_t *)file_desc; /* sanity checks. * note : file_attributes is optional. */ if(!filehandle || !p_context || !file_descriptor) Return(ERR_FSAL_FAULT, 0, INDEX_FSAL_open); /* get the full path for this file */ rc = NamespacePath(filehandle->data.inode, filehandle->data.device, filehandle->data.validator, object_path); if(rc) Return(ERR_FSAL_STALE, rc, INDEX_FSAL_open); memset(file_descriptor, 0, sizeof(fusefsal_file_t)); /* set access mode flags */ file_descriptor->file_info.flags = 0; if(openflags & FSAL_O_RDONLY) file_descriptor->file_info.flags |= O_RDONLY; if(openflags & FSAL_O_WRONLY) file_descriptor->file_info.flags |= O_WRONLY; if(openflags & FSAL_O_RDWR) file_descriptor->file_info.flags |= O_RDWR; /* set context for the next operation, so it can be retrieved by FS thread */ fsal_set_thread_context(p_context); /* check open call */ if(p_fs_ops->open) { LogFullDebug(COMPONENT_FSAL, "Call to open( %s, %#X )", object_path, file_descriptor->file_info.flags); TakeTokenFSCall(); rc = p_fs_ops->open(object_path, &(file_descriptor->file_info)); ReleaseTokenFSCall(); if(rc) Return(fuse2fsal_error(rc, TRUE), rc, INDEX_FSAL_open); file_info_provided = TRUE; } else { LogFullDebug(COMPONENT_FSAL, "no open command provided"); /* ignoring open */ memset(&(file_descriptor->file_info), 0, sizeof(struct ganefuse_file_info)); } /* check open flags (only FSAL_O_TRUNC and FSAL_O_APPEND are used for FUSE filesystems) */ if(openflags & FSAL_O_TRUNC) { if(file_info_provided && p_fs_ops->ftruncate) { LogFullDebug(COMPONENT_FSAL, "call to ftruncate on file since FSAL_O_TRUNC was set"); /* ftruncate the file */ TakeTokenFSCall(); rc = p_fs_ops->ftruncate(object_path, 0, &(file_descriptor->file_info)); ReleaseTokenFSCall(); if(rc) Return(fuse2fsal_error(rc, TRUE), rc, INDEX_FSAL_open); } else if(p_fs_ops->truncate) { LogFullDebug(COMPONENT_FSAL, "call to truncate on file since FSAL_O_TRUNC was set"); /* truncate the file */ TakeTokenFSCall(); rc = p_fs_ops->truncate(object_path, 0); ReleaseTokenFSCall(); if(rc) Return(fuse2fsal_error(rc, TRUE), rc, INDEX_FSAL_open); } /* else: ignoring flag */ } if(openflags & FSAL_O_APPEND) { struct stat stbuf; /* In this case, this only solution is to get file attributes */ if(file_info_provided && p_fs_ops->fgetattr) { rc = p_fs_ops->fgetattr(object_path, &stbuf, &(file_descriptor->file_info)); } else { rc = p_fs_ops->getattr(object_path, &stbuf); } if(rc) Return(fuse2fsal_error(rc, TRUE), rc, INDEX_FSAL_open); file_descriptor->current_offset = stbuf.st_size; } else { file_descriptor->current_offset = 0; } /* fill the file descriptor structure */ file_descriptor->file_handle = *filehandle; /* backup context */ file_descriptor->context = *(fusefsal_op_context_t *)p_context; if(file_info_provided) LogFullDebug(COMPONENT_FSAL, "FSAL_open: FH=%"PRId64, file_descriptor->file_info.fh); if(file_attributes) { fsal_status_t status; status = FUSEFSAL_getattrs((fsal_handle_t *)filehandle, p_context, file_attributes); /* on error, we set a special bit in the mask. */ if(FSAL_IS_ERROR(status)) { FSAL_CLEAR_MASK(file_attributes->asked_attributes); FSAL_SET_MASK(file_attributes->asked_attributes, FSAL_ATTR_RDATTR_ERR); } } Return(ERR_FSAL_NO_ERROR, 0, INDEX_FSAL_open); }
/** * GPFSFSAL_setattrs: * Set attributes for the object specified by its filehandle. * * \param filehandle (input): * The handle of the object to get parameters. * \param cred (input): * Authentication context for the operation (user,...). * \param attrib_set (mandatory input): * The attributes to be set for the object. * It defines the attributes that the caller * wants to set and their values. * \param object_attributes (optionnal input/output): * The post operation attributes for the object. * As input, it defines the attributes that the caller * wants to retrieve (by positioning flags into this structure) * and the output is built considering this input * (it fills the structure according to the flags it contains). * May be NULL. * * \return Major error codes : * - ERR_FSAL_NO_ERROR (no error) * - Another error code if an error occured. */ fsal_status_t GPFSFSAL_setattrs(fsal_handle_t * p_filehandle, /* IN */ fsal_op_context_t * p_context, /* IN */ fsal_attrib_list_t * p_attrib_set, /* IN */ fsal_attrib_list_t * p_object_attributes /* [ IN/OUT ] */ ) { unsigned int i; fsal_status_t status; /* Buffer that will be passed to gpfs_ganesha API. */ gpfsfsal_xstat_t buffxstat; /* Indicate if stat or acl or both should be changed. */ int attr_valid = 0; /* Indiate which attribute in stat should be changed. */ int attr_changed = 0; fsal_accessflags_t access_mask = 0; fsal_attrib_list_t wanted_attrs, current_attrs; /* sanity checks. * note : object_attributes is optional. */ if(!p_filehandle || !p_context || !p_attrib_set) Return(ERR_FSAL_FAULT, 0, INDEX_FSAL_setattrs); /* local copy of attributes */ wanted_attrs = *p_attrib_set; /* It does not make sense to setattr on a symlink */ /* if(p_filehandle->type == DT_LNK) return fsal_internal_setattrs_symlink(p_filehandle, p_context, p_attrib_set, p_object_attributes); */ /* First, check that FSAL attributes changes are allowed. */ /* Is it allowed to change times ? */ if(!global_fs_info.cansettime) { if(wanted_attrs.asked_attributes & (FSAL_ATTR_ATIME | FSAL_ATTR_CREATION | FSAL_ATTR_CTIME | FSAL_ATTR_MTIME)) { /* handled as an unsettable attribute. */ Return(ERR_FSAL_INVAL, 0, INDEX_FSAL_setattrs); } } /* apply umask, if mode attribute is to be changed */ if(FSAL_TEST_MASK(wanted_attrs.asked_attributes, FSAL_ATTR_MODE)) { wanted_attrs.mode &= (~global_fs_info.umask); } /* get current attributes */ current_attrs.asked_attributes = GPFS_SUPPORTED_ATTRIBUTES; status = GPFSFSAL_getattrs(p_filehandle, p_context, ¤t_attrs); if(FSAL_IS_ERROR(status)) { FSAL_CLEAR_MASK(p_object_attributes->asked_attributes); FSAL_SET_MASK(p_object_attributes->asked_attributes, FSAL_ATTR_RDATTR_ERR); ReturnStatus(status, INDEX_FSAL_setattrs); } /*********** * CHMOD * ***********/ if(FSAL_TEST_MASK(wanted_attrs.asked_attributes, FSAL_ATTR_MODE)) { /* The POSIX chmod call don't affect the symlink object, but * the entry it points to. So we must ignore it. */ if(current_attrs.type != FSAL_TYPE_LNK) { #ifdef _USE_NFS4_ACL if(current_attrs.acl) { /* Check permission using ACL. */ access_mask = FSAL_MODE_MASK_SET(0) | /* Dummy. */ FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_WRITE_ATTR); status = fsal_internal_testAccess(p_context, access_mask, NULL, ¤t_attrs); if(FSAL_IS_ERROR(status)) ReturnStatus(status, INDEX_FSAL_setattrs); } else { #endif /* For modifying mode, user must be root or the owner */ if((p_context->credential.user != 0) && (p_context->credential.user != current_attrs.owner)) { LogFullDebug(COMPONENT_FSAL, "Permission denied for CHMOD opeartion: current owner=%d, credential=%d", current_attrs.owner, p_context->credential.user); Return(ERR_FSAL_PERM, 0, INDEX_FSAL_setattrs); } #ifdef _USE_NFS4_ACL } #endif attr_valid |= XATTR_STAT; attr_changed |= XATTR_MODE; /* Fill wanted mode. */ buffxstat.buffstat.st_mode = fsal2unix_mode(wanted_attrs.mode); LogDebug(COMPONENT_FSAL, "current mode = %o, new mode = %o", fsal2unix_mode(current_attrs.mode), buffxstat.buffstat.st_mode); } } /*********** * CHOWN * ***********/ /* Only root can change uid and A normal user must be in the group he wants to set */ if(FSAL_TEST_MASK(wanted_attrs.asked_attributes, FSAL_ATTR_OWNER)) { #ifdef _USE_NFS4_ACL if(current_attrs.acl) { /* Check permission using ACL. */ access_mask = FSAL_MODE_MASK_SET(0) | /* Dummy. */ FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_WRITE_OWNER); status = fsal_internal_testAccess(p_context, access_mask, NULL, ¤t_attrs); if(FSAL_IS_ERROR(status)) ReturnStatus(status, INDEX_FSAL_setattrs); } else { #endif /* For modifying owner, user must be root or current owner==wanted==client */ if((p_context->credential.user != 0) && ((p_context->credential.user != current_attrs.owner) || (p_context->credential.user != wanted_attrs.owner))) { LogFullDebug(COMPONENT_FSAL, "Permission denied for CHOWN opeartion: current owner=%d, credential=%d, new owner=%d", current_attrs.owner, p_context->credential.user, wanted_attrs.owner); Return(ERR_FSAL_PERM, 0, INDEX_FSAL_setattrs); } #ifdef _USE_NFS4_ACL } #endif } if(FSAL_TEST_MASK(wanted_attrs.asked_attributes, FSAL_ATTR_GROUP)) { #ifdef _USE_NFS4_ACL if(current_attrs.acl) { /* Check permission using ACL. */ access_mask = FSAL_MODE_MASK_SET(0) | /* Dummy. */ FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_WRITE_OWNER); status = fsal_internal_testAccess(p_context, access_mask, NULL, ¤t_attrs); if(FSAL_IS_ERROR(status)) ReturnStatus(status, INDEX_FSAL_setattrs); } else { #endif /* For modifying group, user must be root or current owner */ if((p_context->credential.user != 0) && (p_context->credential.user != current_attrs.owner)) { Return(ERR_FSAL_PERM, 0, INDEX_FSAL_setattrs); } int in_grp = 0; /* set in_grp */ if(p_context->credential.group == wanted_attrs.group) in_grp = 1; else for(i = 0; i < p_context->credential.nbgroups; i++) { if((in_grp = (wanted_attrs.group == p_context->credential.alt_groups[i]))) break; } /* it must also be in target group */ if(p_context->credential.user != 0 && !in_grp) { LogFullDebug(COMPONENT_FSAL, "Permission denied for CHOWN operation: current group=%d, credential=%d, new group=%d", current_attrs.group, p_context->credential.group, wanted_attrs.group); Return(ERR_FSAL_PERM, 0, INDEX_FSAL_setattrs); } #ifdef _USE_NFS4_ACL } #endif } if(FSAL_TEST_MASK(wanted_attrs.asked_attributes, FSAL_ATTR_OWNER | FSAL_ATTR_GROUP)) { /* LogFullDebug(COMPONENT_FSAL, "Performing chown(%s, %d,%d)", fsalpath.path, FSAL_TEST_MASK(wanted_attrs.asked_attributes, FSAL_ATTR_OWNER) ? (int)wanted_attrs.owner : -1, FSAL_TEST_MASK(wanted_attrs.asked_attributes, FSAL_ATTR_GROUP) ? (int)wanted_attrs.group : -1);*/ attr_valid |= XATTR_STAT; attr_changed |= FSAL_TEST_MASK(wanted_attrs.asked_attributes, FSAL_ATTR_OWNER) ? XATTR_UID : XATTR_GID; /* Fill wanted owner. */ if(FSAL_TEST_MASK(wanted_attrs.asked_attributes, FSAL_ATTR_OWNER)) { buffxstat.buffstat.st_uid = (int)wanted_attrs.owner; LogDebug(COMPONENT_FSAL, "current uid = %d, new uid = %d", current_attrs.owner, buffxstat.buffstat.st_uid); } /* Fill wanted group. */ if(FSAL_TEST_MASK(wanted_attrs.asked_attributes, FSAL_ATTR_GROUP)) { buffxstat.buffstat.st_gid = (int)wanted_attrs.group; LogDebug(COMPONENT_FSAL, "current gid = %d, new gid = %d", current_attrs.group, buffxstat.buffstat.st_gid); } } /*********** * UTIME * ***********/ /* user must be the owner or have read access to modify 'atime' */ access_mask = FSAL_MODE_MASK_SET(FSAL_R_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_WRITE_ATTR); if(FSAL_TEST_MASK(wanted_attrs.asked_attributes, FSAL_ATTR_ATIME) && (p_context->credential.user != 0) && (p_context->credential.user != current_attrs.owner) && ((status = fsal_internal_testAccess(p_context, access_mask, NULL, ¤t_attrs)).major != ERR_FSAL_NO_ERROR)) { ReturnStatus(status, INDEX_FSAL_setattrs); } /* user must be the owner or have write access to modify 'mtime' */ access_mask = FSAL_MODE_MASK_SET(FSAL_W_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_WRITE_ATTR); if(FSAL_TEST_MASK(wanted_attrs.asked_attributes, FSAL_ATTR_MTIME) && (p_context->credential.user != 0) && (p_context->credential.user != current_attrs.owner) && ((status = fsal_internal_testAccess(p_context, access_mask, NULL, ¤t_attrs)).major != ERR_FSAL_NO_ERROR)) { ReturnStatus(status, INDEX_FSAL_setattrs); } if(FSAL_TEST_MASK(wanted_attrs.asked_attributes, FSAL_ATTR_ATIME | FSAL_ATTR_MTIME)) { attr_valid |= XATTR_STAT; /* Fill wanted atime. */ if(FSAL_TEST_MASK(wanted_attrs.asked_attributes, FSAL_ATTR_ATIME)) { attr_changed |= XATTR_ATIME; buffxstat.buffstat.st_atime = (time_t) wanted_attrs.atime.seconds; LogDebug(COMPONENT_FSAL, "current atime = %lu, new atime = %lu", (unsigned long)current_attrs.atime.seconds, (unsigned long)buffxstat.buffstat.st_atime); } /* Fill wanted mtime. */ if(FSAL_TEST_MASK(wanted_attrs.asked_attributes, FSAL_ATTR_MTIME)) { attr_changed |= XATTR_CTIME; buffxstat.buffstat.st_mtime = (time_t) wanted_attrs.mtime.seconds; LogDebug(COMPONENT_FSAL, "current mtime = %lu, new mtime = %lu", (unsigned long)current_attrs.mtime.seconds, (unsigned long)buffxstat.buffstat.st_mtime); } } #ifdef _USE_NFS4_ACL /*********** * ACL * ***********/ if(FSAL_TEST_MASK(wanted_attrs.asked_attributes, FSAL_ATTR_ACL)) { /* Check permission to set ACL. */ access_mask = FSAL_MODE_MASK_SET(0) | /* Dummy */ FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_WRITE_ACL); status = fsal_internal_testAccess(p_context, access_mask, NULL, ¤t_attrs); if(FSAL_IS_ERROR(status)) ReturnStatus(status, INDEX_FSAL_setattrs); if(wanted_attrs.acl) { attr_valid |= XATTR_ACL; LogDebug(COMPONENT_FSAL, "setattr acl = %p", wanted_attrs.acl); /* Convert FSAL ACL to GPFS NFS4 ACL and fill the buffer. */ status = fsal_acl_2_gpfs_acl(wanted_attrs.acl, &buffxstat); if(FSAL_IS_ERROR(status)) ReturnStatus(status, INDEX_FSAL_setattrs); } else { LogCrit(COMPONENT_FSAL, "setattr acl is NULL"); Return(ERR_FSAL_FAULT, 0, INDEX_FSAL_setattrs); } } #endif /* _USE_NFS4_ACL */ /* If there is any change in stat or acl or both, send it down to file system. */ if((attr_valid == XATTR_STAT && attr_changed !=0) || attr_valid == XATTR_ACL) { status = fsal_set_xstat_by_handle(p_context, p_filehandle, attr_valid, attr_changed, &buffxstat); if(FSAL_IS_ERROR(status)) ReturnStatus(status, INDEX_FSAL_setattrs); } /* Optionaly fills output attributes. */ if(p_object_attributes) { status = GPFSFSAL_getattrs(p_filehandle, p_context, p_object_attributes); /* on error, we set a special bit in the mask. */ if(FSAL_IS_ERROR(status)) { FSAL_CLEAR_MASK(p_object_attributes->asked_attributes); FSAL_SET_MASK(p_object_attributes->asked_attributes, FSAL_ATTR_RDATTR_ERR); } } Return(ERR_FSAL_NO_ERROR, 0, INDEX_FSAL_setattrs); }
/** * Tests about Log streams and special printf functions. */ void Test1(char *str, char *file) { char tempstr[2048]; struct display_buffer buffer = { sizeof(tempstr), tmpstr, tmpstr }; int i; SetComponentLogFile(COMPONENT_INIT, "STDOUT"); LogAlways(COMPONENT_INIT, "%s", "Starting Log Tests"); LogTest("My PID = %d", getpid()); LogTest("------------------------------------------------------"); LogTest("Test conversion of log levels between string and integer"); for (i = NIV_NULL; i < NB_LOG_LEVEL; i++) { int j; if (strcmp(tabLogLevel[i].str, ReturnLevelInt(i)) != 0) { LogTest( "FAILURE: Log level %d did not convert to %s, it converted to %s", i, tabLogLevel[i].str, ReturnLevelInt(i)); exit(1); } j = ReturnLevelAscii(tabLogLevel[i].str); if (j != i) { LogTest( "FAILURE: Log level %s did not convert to %d, it converted to %d", tabLogLevel[i].str, i, j); exit(1); } } LogTest("------------------------------------------------------"); LogTest("\nTesting possible environment variable"); LogTest("COMPONENT_MEMCORRUPT debug level is %s", ReturnLevelInt(LogComponents[COMPONENT_MEMCORRUPT]. comp_log_level)); LogFullDebug(COMPONENT_MEMCORRUPT, "This should appear if environment is set properly"); LogTest("------------------------------------------------------"); LogTest("Send some messages to various files"); SetComponentLogFile(COMPONENT_DISPATCH, "STDERR"); LogEvent(COMPONENT_DISPATCH, "This should go to stderr"); SetComponentLogFile(COMPONENT_DISPATCH, "STDOUT"); LogEvent(COMPONENT_DISPATCH, "This should go to stdout"); SetComponentLogFile(COMPONENT_DISPATCH, "SYSLOG"); LogEvent(COMPONENT_DISPATCH, "This should go to syslog (verf = %s)", str); LogTest("About to set %s", file); SetComponentLogFile(COMPONENT_DISPATCH, file); LogTest("Got it set"); LogEvent(COMPONENT_DISPATCH, "This should go to %s", file); /* Set up for tests that will verify what was actually produced by log * messages. This is used to test log levels and to test the * log_vnsprintf function. */ /** @todo FSF: this can be done by setting the right header flags and * peeking at the context buffer. */ SetComponentLogBuffer(COMPONENT_MAIN, &buffer); SetComponentLogBuffer(COMPONENT_INIT, &buffer); LogTest("------------------------------------------------------"); LogTest("Test all levels of log filtering"); SetComponentLogLevel(COMPONENT_MAIN, NIV_NULL); TestAlways(true, tempstr, COMPONENT_MAIN, "LogAlways (should print)"); TestMajor(false, tempstr, COMPONENT_MAIN, "LogMajor (shouldn't print)"); TestCrit(false, tempstr, COMPONENT_MAIN, "LogCrit (shouldn't print)"); TestEvent(false, tempstr, COMPONENT_MAIN, "LogEvent (shouldn't print)"); TestDebug(false, tempstr, COMPONENT_MAIN, "LogDebug (shouldn't print)"); TestFullDebug(false, tempstr, COMPONENT_MAIN, "LogFullDebug (shouldn't print)"); SetComponentLogLevel(COMPONENT_MAIN, NIV_MAJOR); TestAlways(true, tempstr, COMPONENT_MAIN, "LogAlways (should print)"); TestMajor(true, tempstr, COMPONENT_MAIN, "LogMajor (should print)"); TestCrit(false, tempstr, COMPONENT_MAIN, "LogCrit (shouldn't print)"); TestEvent(false, tempstr, COMPONENT_MAIN, "LogEvent (shouldn't print)"); TestDebug(false, tempstr, COMPONENT_MAIN, "LogDebug (shouldn't print)"); TestFullDebug(false, tempstr, COMPONENT_MAIN, "LogFullDebug (shouldn't print)"); SetComponentLogLevel(COMPONENT_MAIN, NIV_CRIT); TestAlways(true, tempstr, COMPONENT_MAIN, "LogAlways (should print)"); TestMajor(true, tempstr, COMPONENT_MAIN, "LogMajor (should print)"); TestCrit(true, tempstr, COMPONENT_MAIN, "LogCrit (should print)"); TestEvent(false, tempstr, COMPONENT_MAIN, "LogEvent (shouldn't print)"); TestDebug(false, tempstr, COMPONENT_MAIN, "LogDebug (shouldn't print)"); TestFullDebug(false, tempstr, COMPONENT_MAIN, "LogFullDebug (shouldn't print)"); SetComponentLogLevel(COMPONENT_MAIN, NIV_EVENT); TestAlways(true, tempstr, COMPONENT_MAIN, "LogAlways (should print)"); TestMajor(true, tempstr, COMPONENT_MAIN, "LogMajor (should print)"); TestCrit(true, tempstr, COMPONENT_MAIN, "LogCrit (should print)"); TestEvent(true, tempstr, COMPONENT_MAIN, "LogEvent (should print)"); TestDebug(false, tempstr, COMPONENT_MAIN, "LogDebug (shouldn't print)"); TestFullDebug(false, tempstr, COMPONENT_MAIN, "LogFullDebug (shouldn't print)"); SetComponentLogLevel(COMPONENT_MAIN, NIV_DEBUG); TestAlways(true, tempstr, COMPONENT_MAIN, "LogAlways (should print)"); TestMajor(true, tempstr, COMPONENT_MAIN, "LogMajor (should print)"); TestCrit(true, tempstr, COMPONENT_MAIN, "LogCrit (should print)"); TestEvent(true, tempstr, COMPONENT_MAIN, "LogEvent (should print)"); TestDebug(true, tempstr, COMPONENT_MAIN, "LogDebug (should print)"); TestFullDebug(false, tempstr, COMPONENT_MAIN, "LogFullDebug (shouldn't print)"); SetComponentLogLevel(COMPONENT_MAIN, NIV_FULL_DEBUG); TestAlways(true, tempstr, COMPONENT_MAIN, "LogAlways (should print)"); TestMajor(true, tempstr, COMPONENT_MAIN, "LogMajor (should print)"); TestCrit(true, tempstr, COMPONENT_MAIN, "LogCrit (should print)"); TestEvent(true, tempstr, COMPONENT_MAIN, "LogEvent (should print)"); TestDebug(true, tempstr, COMPONENT_MAIN, "LogDebug (should print)"); TestFullDebug(true, tempstr, COMPONENT_MAIN, "LogFullDebug (should print)"); }
fsal_posixdb_status_t fsal_posixdb_getInfoFromName(fsal_posixdb_conn * p_conn, /* IN */ posixfsal_handle_t * p_parent_directory_handle, /* IN/OUT */ fsal_name_t * p_objectname, /* IN */ fsal_path_t * p_path, /* OUT */ posixfsal_handle_t * p_handle /* OUT */ ) { fsal_posixdb_status_t st; char query[2048]; result_handle_t res; MYSQL_ROW row; /* sanity check */ if(!p_conn || !p_handle) { ReturnCodeDB(ERR_FSAL_POSIXDB_FAULT, 0); } LogFullDebug(COMPONENT_FSAL, "object_name='%s'\n", p_objectname->name ? p_objectname->name : "/"); BeginTransaction(p_conn); /* lookup for the handle of the file */ if(p_parent_directory_handle && p_parent_directory_handle->data.id) { snprintf(query, 2048, "SELECT Parent.handleid, Parent.handlets, Handle.deviceid, " "Handle.inode, Handle.nlink, Handle.ctime, Handle.ftype " "FROM Parent INNER JOIN Handle ON Parent.handleid = Handle.handleid AND Parent.handlets=Handle.handlets " "WHERE handleidparent=%llu AND handletsparent=%u AND name='%s'", p_parent_directory_handle->data.id, p_parent_directory_handle->data.ts, p_objectname->name); st = db_exec_sql(p_conn, query, &res); if(FSAL_POSIXDB_IS_ERROR(st)) goto rollback; } else { /* get root handle: */ st = db_exec_sql(p_conn, "SELECT Parent.handleid, Parent.handlets, Handle.deviceid, Handle.inode, Handle.nlink, Handle.ctime, Handle.ftype " "FROM Parent INNER JOIN Handle ON Parent.handleid = Handle.handleid AND Parent.handlets=Handle.handlets " "WHERE Parent.handleidparent=Parent.handleid AND Parent.handletsparent=Parent.handlets", &res); if(FSAL_POSIXDB_IS_ERROR(st)) goto rollback; } /* res contains : Parent.handleid, Parent.handlets, Handle.deviceid, Handle.inode, Handle.nlink, Handle.ctime, Handle.ftype */ /* entry not found */ if((mysql_num_rows(res) != 1) || ((row = mysql_fetch_row(res)) == NULL)) { mysql_free_result(res); RollbackTransaction(p_conn); ReturnCodeDB(ERR_FSAL_POSIXDB_NOENT, 0); } p_handle->data.id = atoll(row[0]); p_handle->data.ts = atoi(row[1]); posixdb_internal_fillFileinfoFromStrValues(&(p_handle->data.info), row[2], row[3], /* devid, inode */ row[4], /* nlink */ row[5], /* ctime */ row[6]); /* ftype */ mysql_free_result(res); /* Build the path of the object */ if(p_path && p_objectname) { /* build the path of the Parent */ st = fsal_posixdb_buildOnePath(p_conn, p_parent_directory_handle, p_path); if(FSAL_POSIXDB_IS_ERROR(st)) goto rollback; /* then concatenate the filename */ if(!(p_path->len + 1 + p_objectname->len < FSAL_MAX_PATH_LEN)) { RollbackTransaction(p_conn); ReturnCodeDB(ERR_FSAL_POSIXDB_PATHTOOLONG, 0); } p_path->path[p_path->len] = '/'; strcpy(&p_path->path[p_path->len + 1], p_objectname->name); p_path->len += 1 + p_objectname->len; /* add the the path to cache */ fsal_posixdb_CachePath(p_handle, p_path); } else { /* update handle if it was in cache */ fsal_posixdb_UpdateInodeCache(p_handle); } return EndTransaction(p_conn); rollback: RollbackTransaction(p_conn); return st; }
/** * @brief Open a file descriptor for read or write and possibly create * * This function opens a file for read or write, possibly creating it. * If the caller is passing a state, it must hold the state_lock * exclusive. * * state can be NULL which indicates a stateless open (such as via the * NFS v3 CREATE operation), in which case the FSAL must assure protection * of any resources. If the file is being created, such protection is * simple since no one else will have access to the object yet, however, * in the case of an exclusive create, the common resources may still need * protection. * * If Name is NULL, obj_hdl is the file itself, otherwise obj_hdl is the * parent directory. * * On an exclusive create, the upper layer may know the object handle * already, so it MAY call with name == NULL. In this case, the caller * expects just to check the verifier. * * On a call with an existing object handle for an UNCHECKED create, * we can set the size to 0. * * At least the mode attribute must be set if createmode is not FSAL_NO_CREATE. * Some FSALs may still have to pass a mode on a create call for exclusive, * and even with FSAL_NO_CREATE, and empty set of attributes MUST be passed. * * If an open by name succeeds and did not result in Ganesha creating a file, * the caller will need to do a subsequent permission check to confirm the * open. This is because the permission attributes were not available * beforehand. * * @param[in] obj_hdl File to open or parent directory * @param[in,out] state state_t to use for this operation * @param[in] openflags Mode for open * @param[in] createmode Mode for create * @param[in] name Name for file if being created or opened * @param[in] attrib_set Attributes to set on created file * @param[in] verifier Verifier to use for exclusive create * @param[in,out] new_obj Newly created object * @param[in,out] attrs_out Newly created object attributes * @param[in,out] caller_perm_check The caller must do a permission check * * @return FSAL status. */ fsal_status_t gpfs_open2(struct fsal_obj_handle *obj_hdl, struct state_t *state, fsal_openflags_t openflags, enum fsal_create_mode createmode, const char *name, struct attrlist *attrib_set, fsal_verifier_t verifier, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out, bool *caller_perm_check) { int posix_flags = 0; mode_t unix_mode; fsal_status_t status = {0, 0}; struct gpfs_fd *my_fd = NULL; struct gpfs_fsal_obj_handle *myself; struct gpfs_fsal_obj_handle *hdl = NULL; struct gpfs_file_handle fh; bool truncated; bool created = false; struct fsal_export *export = op_ctx->fsal_export; struct gpfs_filesystem *gpfs_fs = obj_hdl->fs->private_data; int *fd = NULL; myself = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); if (name) LogFullDebug(COMPONENT_FSAL, "got name %s", name); else LogFullDebug(COMPONENT_FSAL, "no name"); LogAttrlist(COMPONENT_FSAL, NIV_FULL_DEBUG, "attrs ", attrib_set, false); if (state != NULL) my_fd = (struct gpfs_fd *)(state + 1); fsal2posix_openflags(openflags, &posix_flags); truncated = (posix_flags & O_TRUNC) != 0; if (createmode >= FSAL_EXCLUSIVE) { /* Now fixup attrs for verifier if exclusive create */ set_common_verifier(attrib_set, verifier); } if (name == NULL) { /* This is an open by handle */ if (state != NULL) { /* Prepare to take the share reservation, but only if we * are called with a valid state (if state is NULL the * caller is a stateless create such as NFS v3 CREATE). */ /* This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->lock); /* Check share reservation conflicts. */ status = check_share_conflict(&myself->u.file.share, openflags, false); if (FSAL_IS_ERROR(status)) { PTHREAD_RWLOCK_unlock(&obj_hdl->lock); return status; } /* Take the share reservation now by updating the * counters. */ update_share_counters(&myself->u.file.share, FSAL_O_CLOSED, openflags); PTHREAD_RWLOCK_unlock(&obj_hdl->lock); fd = &my_fd->fd; my_fd->openflags = openflags; } else { /* We need to use the global fd to continue, and take * the lock to protect it. */ fd = &myself->u.file.fd.fd; myself->u.file.fd.openflags = openflags; PTHREAD_RWLOCK_wrlock(&obj_hdl->lock); } status = GPFSFSAL_open(obj_hdl, op_ctx, posix_flags, fd, false); if (FSAL_IS_ERROR(status)) { if (state == NULL) { /* Release the lock taken above, and return * since there is nothing to undo. */ PTHREAD_RWLOCK_unlock(&obj_hdl->lock); return status; } /* Error - need to release the share */ goto undo_share; } if (attrs_out && (createmode >= FSAL_EXCLUSIVE || truncated)) { /* Refresh the attributes */ status = GPFSFSAL_getattrs(export, gpfs_fs, op_ctx, myself->handle, attrs_out); if (!FSAL_IS_ERROR(status)) { LogFullDebug(COMPONENT_FSAL, "New size = %"PRIx64, attrs_out->filesize); } /* Now check verifier for exclusive, but not for * FSAL_EXCLUSIVE_9P. */ if (!FSAL_IS_ERROR(status) && createmode >= FSAL_EXCLUSIVE && createmode != FSAL_EXCLUSIVE_9P && !check_verifier_attrlist(attrs_out, verifier)) { /* Verifier didn't match, return EEXIST */ status = fsalstat(posix2fsal_error(EEXIST), EEXIST); } } if (state == NULL) { /* If no state, release the lock taken above and return * status. */ PTHREAD_RWLOCK_unlock(&obj_hdl->lock); return status; } if (!FSAL_IS_ERROR(status)) { /* Return success. */ return status; } (void) fsal_internal_close(*fd, state->state_owner, 0); undo_share: /* Can only get here with state not NULL and an error */ /* On error we need to release our share reservation * and undo the update of the share counters. * This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->lock); update_share_counters(&myself->u.file.share, openflags, FSAL_O_CLOSED); PTHREAD_RWLOCK_unlock(&obj_hdl->lock); return status; }
/* * This function initializes shared variables of the fsal. */ fsal_status_t fsal_internal_init_global(fsal_init_info_t * fsal_info, fs_common_initinfo_t * fs_common_info, fs_specific_initinfo_t * fs_specific_info) { /* sanity check */ if(!fsal_info || !fs_common_info || !fs_specific_info) ReturnCode(ERR_FSAL_FAULT, 0); /* inits FS call semaphore */ if(fsal_info->max_fs_calls > 0) { int rc; limit_calls = TRUE; rc = semaphore_init(&sem_fs_calls, fsal_info->max_fs_calls); if(rc != 0) ReturnCode(ERR_FSAL_SERVERFAULT, rc); LogDebug(COMPONENT_FSAL, "FSAL INIT: Max simultaneous calls to filesystem is limited to %u.", fsal_info->max_fs_calls); } else { LogDebug(COMPONENT_FSAL, "FSAL INIT: Max simultaneous calls to filesystem is unlimited."); } /* setting default values. */ global_fs_info = default_posix_info; LogDebug(COMPONENT_FSAL, "{"); LogDebug(COMPONENT_FSAL, " maxfilesize = %llX ", default_posix_info.maxfilesize); LogDebug(COMPONENT_FSAL, " maxlink = %lu ", default_posix_info.maxlink); LogDebug(COMPONENT_FSAL, " maxnamelen = %lu ", default_posix_info.maxnamelen); LogDebug(COMPONENT_FSAL, " maxpathlen = %lu ", default_posix_info.maxpathlen); LogDebug(COMPONENT_FSAL, " no_trunc = %d ", default_posix_info.no_trunc); LogDebug(COMPONENT_FSAL, " chown_restricted = %d ", default_posix_info.chown_restricted); LogDebug(COMPONENT_FSAL, " case_insensitive = %d ", default_posix_info.case_insensitive); LogDebug(COMPONENT_FSAL, " case_preserving = %d ", default_posix_info.case_preserving); LogDebug(COMPONENT_FSAL, " fh_expire_type = %hu ", default_posix_info.fh_expire_type); LogDebug(COMPONENT_FSAL, " link_support = %d ", default_posix_info.link_support); LogDebug(COMPONENT_FSAL, " symlink_support = %d ", default_posix_info.symlink_support); LogDebug(COMPONENT_FSAL, " lock_support = %d ", default_posix_info.lock_support); LogDebug(COMPONENT_FSAL, " lock_support_owner = %d ", global_fs_info.lock_support_owner); LogDebug(COMPONENT_FSAL, " lock_support_async_block = %d ", global_fs_info.lock_support_async_block); LogDebug(COMPONENT_FSAL, " named_attr = %d ", default_posix_info.named_attr); LogDebug(COMPONENT_FSAL, " unique_handles = %d ", default_posix_info.unique_handles); LogDebug(COMPONENT_FSAL, " acl_support = %hu ", default_posix_info.acl_support); LogDebug(COMPONENT_FSAL, " cansettime = %d ", default_posix_info.cansettime); LogDebug(COMPONENT_FSAL, " homogenous = %d ", default_posix_info.homogenous); LogDebug(COMPONENT_FSAL, " supported_attrs = %llX ", default_posix_info.supported_attrs); LogDebug(COMPONENT_FSAL, " maxread = %llX ", default_posix_info.maxread); LogDebug(COMPONENT_FSAL, " maxwrite = %llX ", default_posix_info.maxwrite); LogDebug(COMPONENT_FSAL, " umask = %X ", default_posix_info.umask); LogDebug(COMPONENT_FSAL, "}"); /* Analyzing fs_common_info struct */ if((fs_common_info->behaviors.maxfilesize != FSAL_INIT_FS_DEFAULT) || (fs_common_info->behaviors.maxlink != FSAL_INIT_FS_DEFAULT) || (fs_common_info->behaviors.maxnamelen != FSAL_INIT_FS_DEFAULT) || (fs_common_info->behaviors.maxpathlen != FSAL_INIT_FS_DEFAULT) || (fs_common_info->behaviors.no_trunc != FSAL_INIT_FS_DEFAULT) || (fs_common_info->behaviors.case_insensitive != FSAL_INIT_FS_DEFAULT) || (fs_common_info->behaviors.case_preserving != FSAL_INIT_FS_DEFAULT) || (fs_common_info->behaviors.named_attr != FSAL_INIT_FS_DEFAULT) || (fs_common_info->behaviors.lease_time != FSAL_INIT_FS_DEFAULT) || (fs_common_info->behaviors.supported_attrs != FSAL_INIT_FS_DEFAULT) || (fs_common_info->behaviors.homogenous != FSAL_INIT_FS_DEFAULT)) ReturnCode(ERR_FSAL_NOTSUPP, 0); VFS_SET_BOOLEAN_PARAM(global_fs_info, fs_common_info, symlink_support); VFS_SET_BOOLEAN_PARAM(global_fs_info, fs_common_info, link_support); VFS_SET_BOOLEAN_PARAM(global_fs_info, fs_common_info, lock_support); VFS_SET_BOOLEAN_PARAM(global_fs_info, fs_common_info, lock_support_owner); VFS_SET_BOOLEAN_PARAM(global_fs_info, fs_common_info, lock_support_async_block); VFS_SET_BOOLEAN_PARAM(global_fs_info, fs_common_info, cansettime); VFS_SET_INTEGER_PARAM(global_fs_info, fs_common_info, maxread); VFS_SET_INTEGER_PARAM(global_fs_info, fs_common_info, maxwrite); VFS_SET_BITMAP_PARAM(global_fs_info, fs_common_info, umask); VFS_SET_BOOLEAN_PARAM(global_fs_info, fs_common_info, auth_exportpath_xdev); VFS_SET_BITMAP_PARAM(global_fs_info, fs_common_info, xattr_access_rights); LogFullDebug(COMPONENT_FSAL, "Supported attributes constant = 0x%llX.", VFS_SUPPORTED_ATTRIBUTES); LogFullDebug(COMPONENT_FSAL, "Supported attributes default = 0x%llX.", default_posix_info.supported_attrs); LogDebug(COMPONENT_FSAL, "FSAL INIT: Supported attributes mask = 0x%llX.", global_fs_info.supported_attrs); ReturnCode(ERR_FSAL_NO_ERROR, 0); }
/** * * @brief The NFSPROC3_LINK * * The NFSPROC3_LINK. * * @param[in] arg NFS argument union * @param[in] req SVC request related to this call * @param[out] res Structure to contain the result of the call * * @retval NFS_REQ_OK if successful * @retval NFS_REQ_DROP if failed but retryable * @retval NFS_REQ_FAILED if failed and not retryable * */ int nfs3_link(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct LINK3args *l3_arg = &arg->arg_link3; struct LINK3res *l3_res = &res->res_link3; const char *link_name = l3_arg->link.name; struct fsal_obj_handle *target_obj = NULL; struct fsal_obj_handle *parent_obj = NULL; pre_op_attr pre_parent = {0}; fsal_status_t fsal_status = {0, 0}; int rc = NFS_REQ_OK; if (isDebug(COMPONENT_NFSPROTO)) { char strto[LEN_FH_STR], strfrom[LEN_FH_STR]; nfs_FhandleToStr(req->rq_msg.cb_vers, &l3_arg->file, NULL, strfrom); nfs_FhandleToStr(req->rq_msg.cb_vers, &l3_arg->link.dir, NULL, strto); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs3_link handle: %s to handle: %s name: %s", strfrom, strto, link_name); } /* to avoid setting it on each error case */ l3_res->LINK3res_u.resfail.file_attributes.attributes_follow = FALSE; l3_res->LINK3res_u.resfail.linkdir_wcc.before.attributes_follow = FALSE; l3_res->LINK3res_u.resfail.linkdir_wcc.after.attributes_follow = FALSE; l3_res->status = nfs3_verify_exportid(l3_arg, req); if (l3_res->status != NFS3_OK) return rc; parent_obj = nfs3_FhandleToCache(&l3_arg->link.dir, &l3_res->status, &rc); if (parent_obj == NULL) return rc; /* Status and rc are set by nfs3_FhandleToCache */ nfs_SetPreOpAttr(parent_obj, &pre_parent); target_obj = nfs3_FhandleToCache(&l3_arg->file, &l3_res->status, &rc); if (target_obj == NULL) { parent_obj->obj_ops.put_ref(parent_obj); return rc; /* Status and rc are set by nfs3_FhandleToCache */ } if (parent_obj->type != DIRECTORY) { l3_res->status = NFS3ERR_NOTDIR; goto out; } if (link_name == NULL || *link_name == '\0') { l3_res->status = NFS3ERR_INVAL; goto out; } fsal_status = fsal_link(target_obj, parent_obj, link_name); if (FSAL_IS_ERROR(fsal_status)) { /* If we are here, there was an error */ LogFullDebug(COMPONENT_NFSPROTO, "failed link: fsal_status=%s", fsal_err_txt(fsal_status)); if (nfs_RetryableError(fsal_status.major)) { rc = NFS_REQ_DROP; goto out; } l3_res->status = nfs3_Errno_status(fsal_status); nfs_SetPostOpAttr(target_obj, &l3_res->LINK3res_u.resfail.file_attributes, NULL); nfs_SetWccData(&pre_parent, parent_obj, &l3_res->LINK3res_u.resfail.linkdir_wcc); } else { nfs_SetPostOpAttr(target_obj, &l3_res->LINK3res_u.resok.file_attributes, NULL); nfs_SetWccData(&pre_parent, parent_obj, &l3_res->LINK3res_u.resok.linkdir_wcc); l3_res->status = NFS3_OK; } out: /* return references */ target_obj->obj_ops.put_ref(target_obj); parent_obj->obj_ops.put_ref(parent_obj); return rc; } /* nfs3_link */
int nfs41_op_exchange_id(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { char str_verifier[NFS4_VERIFIER_SIZE * 2 + 1]; char str_client[NFS4_OPAQUE_LIMIT * 2 + 1]; nfs_client_record_t * pclient_record; nfs_client_id_t * pconf; nfs_client_id_t * punconf; int rc; int len; char * temp; bool_t update; const char * update_str; log_components_t component = COMPONENT_CLIENTID; #if 0 /** @todo: plante le client sous windows. Ai-je réellement besoin de cela ???? */ /* Check flags value (test EID4) */ if(arg_EXCHANGE_ID4.eia_flags & all_eia_flags != arg_EXCHANGE_ID4.eia_flags) { res_EXCHANGE_ID4.eir_status = NFS4ERR_INVAL; return res_EXCHANGE_ID4.eir_status; } #endif if(isDebug(COMPONENT_SESSIONS)) component = COMPONENT_SESSIONS; #define arg_EXCHANGE_ID4 op->nfs_argop4_u.opexchange_id #define res_EXCHANGE_ID4 resp->nfs_resop4_u.opexchange_id #define res_EXCHANGE_ID4_ok resp->nfs_resop4_u.opexchange_id.EXCHANGE_ID4res_u.eir_resok4 resp->resop = NFS4_OP_EXCHANGE_ID; update = (arg_EXCHANGE_ID4.eia_flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) != 0; if(isDebug(component)) { DisplayOpaqueValue(arg_EXCHANGE_ID4.eia_clientowner.co_ownerid.co_ownerid_val, arg_EXCHANGE_ID4.eia_clientowner.co_ownerid.co_ownerid_len, str_client); sprint_mem(str_verifier, arg_EXCHANGE_ID4.eia_clientowner.co_verifier, NFS4_VERIFIER_SIZE); update_str = update ? "UPDATE" : "NO UPDATE"; } LogDebug(component, "EXCHANGE_ID Client addr=%s id=%s verf=%s %s --------------------", data->pworker->hostaddr_str, str_client, str_verifier, update_str); /* Do we already have one or more records for client id (x)? */ pclient_record = get_client_record(arg_EXCHANGE_ID4.eia_clientowner.co_ownerid.co_ownerid_val, arg_EXCHANGE_ID4.eia_clientowner.co_ownerid.co_ownerid_len); if(pclient_record == NULL) { /* Some major failure */ LogCrit(component, "EXCHANGE_ID failed"); res_EXCHANGE_ID4.eir_status = NFS4ERR_SERVERFAULT; return res_EXCHANGE_ID4.eir_status; } /* * The following checks are based on RFC5661 * * This attempts to implement the logic described in 18.35.4. IMPLEMENTATION */ P(pclient_record->cr_mutex); if(isFullDebug(COMPONENT_CLIENTID)) { char str[HASHTABLE_DISPLAY_STRLEN]; display_client_record(pclient_record, str); LogFullDebug(COMPONENT_CLIENTID, "Client Record %s cr_pconfirmed_id=%p cr_punconfirmed_id=%p", str, pclient_record->cr_pconfirmed_id, pclient_record->cr_punconfirmed_id); } pconf = pclient_record->cr_pconfirmed_id; if(pconf != NULL) { /* Need a reference to the confirmed record for below */ inc_client_id_ref(pconf); } if(pconf != NULL && !update) { /* EXCHGID4_FLAG_UPD_CONFIRMED_REC_A not set */ /** @todo FSF: old code ifdefed out nfs_compare_clientcred with _NFSV4_COMPARE_CRED_IN_EXCHANGE_ID */ if(!nfs_compare_clientcred(&pconf->cid_credential, &data->credential) || !cmp_sockaddr(&pconf->cid_client_addr, &data->pworker->hostaddr, IGNORE_PORT)) { /** @todo FSF: should also check if there is no state */ P(pconf->cid_mutex); if(valid_lease(pconf)) { V(pconf->cid_mutex); /* CASE 3, client collisions, old clientid is expired */ if(isDebug(COMPONENT_CLIENTID)) { char str[HASHTABLE_DISPLAY_STRLEN]; display_client_id_rec(pconf, str); LogDebug(COMPONENT_CLIENTID, "Expiring %s", str); } /* Expire clientid and release our reference. */ nfs_client_id_expire(pconf); dec_client_id_ref(pconf); pconf = NULL; } else { V(pconf->cid_mutex); /* CASE 3, client collisions, old clientid is not expired */ if(isDebug(component)) { char confirmed_addr[SOCK_NAME_MAX]; sprint_sockip(&pconf->cid_client_addr, confirmed_addr, sizeof(confirmed_addr)); LogDebug(component, "Confirmed ClientId %"PRIx64"->'%s': Principals do not match... confirmed addr=%s Return NFS4ERR_CLID_INUSE", pconf->cid_clientid, str_client, confirmed_addr); } res_EXCHANGE_ID4.eir_status = NFS4ERR_CLID_INUSE; /* Release our reference to the confirmed clientid. */ dec_client_id_ref(pconf); goto out; } } else if(memcmp(arg_EXCHANGE_ID4.eia_clientowner.co_verifier, pconf->cid_incoming_verifier, NFS4_VERIFIER_SIZE) == 0) { /* CASE 2, Non-Update on Existing Client ID */ /* Return what was last returned without changing any refcounts */ LogDebug(COMPONENT_CLIENTID, "Non-update of confirmed ClientId %"PRIx64"->%s", pconf->cid_clientid, str_client); punconf = pconf; goto return_ok; } else { /* CASE 5, client restart */ /** @todo FSF: expire old clientid? */ LogDebug(component, "Restarted ClientId %"PRIx64"->%s", pconf->cid_clientid, str_client); } } else if(pconf != NULL) { /* EXCHGID4_FLAG_UPD_CONFIRMED_REC_A set */ if(memcmp(arg_EXCHANGE_ID4.eia_clientowner.co_verifier, pconf->cid_incoming_verifier, NFS4_VERIFIER_SIZE) == 0) { /** @todo FSF: old code ifdefed out nfs_compare_clientcred with _NFSV4_COMPARE_CRED_IN_EXCHANGE_ID */ if(!nfs_compare_clientcred(&pconf->cid_credential, &data->credential) || !cmp_sockaddr(&pconf->cid_client_addr, &data->pworker->hostaddr, IGNORE_PORT)) { /* CASE 9, Update but wrong principal */ if(isDebug(component)) { char confirmed_addr[SOCK_NAME_MAX]; sprint_sockip(&pconf->cid_client_addr, confirmed_addr, sizeof(confirmed_addr)); LogDebug(component, "Confirmed ClientId %"PRIx64"->'%s': Principals do not match... confirmed addr=%s Return NFS4ERR_PERM", pconf->cid_clientid, str_client, confirmed_addr); } res_EXCHANGE_ID4.eir_status = NFS4ERR_PERM; } else { /* CASE 6, Update */ /** @todo: this is not implemented, the things it updates aren't even tracked */ LogMajor(component, "EXCHANGE_ID Update not supported"); res_EXCHANGE_ID4.eir_status = NFS4ERR_NOTSUPP; } } else { /* CASE 8, Update but wrong verifier */ if(isDebug(component)) { char str_old_verifier[NFS4_VERIFIER_SIZE * 2 + 1]; sprint_mem(str_old_verifier, pconf->cid_incoming_verifier, NFS4_VERIFIER_SIZE); LogDebug(component, "Confirmed clientid %"PRIx64"->'%s': Verifiers do not match... confirmed verifier=%s", pconf->cid_clientid, str_client, str_old_verifier); } res_EXCHANGE_ID4.eir_status = NFS4ERR_NOT_SAME; } /* Release our reference to the confirmed clientid. */ dec_client_id_ref(pconf); goto out; } else if(pconf == NULL && update) { LogDebug(component, "No confirmed clientid to update for %s", str_client); res_EXCHANGE_ID4.eir_status = NFS4ERR_NOENT; goto out; } /* At this point, no matter what the case was above, we should remove any * pre-existing unconfirmed record. */ punconf = pclient_record->cr_punconfirmed_id; if(punconf != NULL) { /* CASE 4, replacement of unconfirmed record */ /* Delete the unconfirmed clientid record */ if(isDebug(COMPONENT_CLIENTID)) { char str[HASHTABLE_DISPLAY_STRLEN]; display_client_id_rec(punconf, str); LogDebug(COMPONENT_CLIENTID, "Replacing %s", str); } /* unhash the clientid record */ remove_unconfirmed_client_id(punconf); } /* Now we can proceed to build the new unconfirmed record. We have determined * the clientid and setclientid_confirm values above. */ punconf = create_client_id(0, pclient_record, &data->pworker->hostaddr, &data->credential); if(punconf == NULL) { /* Error already logged, return */ res_EXCHANGE_ID4.eir_status = NFS4ERR_RESOURCE; goto out; } memcpy(punconf->cid_incoming_verifier, arg_EXCHANGE_ID4.eia_clientowner.co_verifier, NFS4_VERIFIER_SIZE); if(gethostname(punconf->cid_server_owner, sizeof(punconf->cid_server_owner)) == -1) { /* Free the clientid record and return */ free_client_id(punconf); res_EXCHANGE_ID4.eir_status = NFS4ERR_SERVERFAULT; goto out; } snprintf(punconf->cid_server_scope, sizeof(punconf->cid_server_scope), "%s_NFS-Ganesha", punconf->cid_server_owner); rc = nfs_client_id_insert(punconf); if(rc != CLIENT_ID_SUCCESS) { /* Record is already freed, return. */ res_EXCHANGE_ID4.eir_status = clientid_error_to_nfsstat(rc); goto out; } return_ok: /* Build the reply */ res_EXCHANGE_ID4_ok.eir_clientid = punconf->cid_clientid; res_EXCHANGE_ID4_ok.eir_sequenceid = punconf->cid_create_session_sequence; #if defined(_USE_FSALMDS) && defined(_USE_FSALDS) res_EXCHANGE_ID4_ok.eir_flags = EXCHGID4_FLAG_USE_PNFS_MDS | EXCHGID4_FLAG_USE_PNFS_DS | EXCHGID4_FLAG_SUPP_MOVED_REFER; #elif defined(_USE_FSALMDS) res_EXCHANGE_ID4_ok.eir_flags = EXCHGID4_FLAG_USE_PNFS_MDS | EXCHGID4_FLAG_SUPP_MOVED_REFER; #elif defined(_USE_FSALDS) res_EXCHANGE_ID4_ok.eir_flags = EXCHGID4_FLAG_USE_PNFS_DS | EXCHGID4_FLAG_SUPP_MOVED_REFER; #elif defined(_USE_DS) res_EXCHANGE_ID4_ok.eir_flags = EXCHGID4_FLAG_USE_PNFS_MDS | EXCHGID4_FLAG_USE_PNFS_DS | EXCHGID4_FLAG_SUPP_MOVED_REFER; #else res_EXCHANGE_ID4_ok.eir_flags = EXCHGID4_FLAG_USE_NON_PNFS | EXCHGID4_FLAG_SUPP_MOVED_REFER; #endif res_EXCHANGE_ID4_ok.eir_state_protect.spr_how = SP4_NONE; len = strlen(punconf->cid_server_owner); temp = gsh_malloc(len); if(temp == NULL) { LogDebug(component, "Could not allocate memory for so_major_id in response"); /** @todo FSF: not the best way to handle this but keeps from crashing */ len = 0; } else memcpy(temp, punconf->cid_server_owner, len); res_EXCHANGE_ID4_ok.eir_server_owner.so_major_id.so_major_id_len = len; res_EXCHANGE_ID4_ok.eir_server_owner.so_major_id.so_major_id_val = temp; res_EXCHANGE_ID4_ok.eir_server_owner.so_minor_id = 0; len = strlen(punconf->cid_server_scope); temp = gsh_malloc(len); if(temp == NULL) { LogDebug(component, "Could not allocate memory for eir_server_scope in response"); /** @todo FSF: not the best way to handle this but keeps from crashing */ len = 0; } else memcpy(temp, punconf->cid_server_scope, len); res_EXCHANGE_ID4_ok.eir_server_scope.eir_server_scope_len = len; res_EXCHANGE_ID4_ok.eir_server_scope.eir_server_scope_val = temp; res_EXCHANGE_ID4_ok.eir_server_impl_id.eir_server_impl_id_len = 0; res_EXCHANGE_ID4_ok.eir_server_impl_id.eir_server_impl_id_val = NULL; if(isDebug(COMPONENT_CLIENTID)) { char str[HASHTABLE_DISPLAY_STRLEN]; sprint_mem(str_verifier, arg_EXCHANGE_ID4.eia_clientowner.co_verifier, NFS4_VERIFIER_SIZE); display_client_id_rec(punconf, str); LogDebug(COMPONENT_CLIENTID, "EXCHANGE_ID reply Verifier=%s %s", str_verifier, str); } res_EXCHANGE_ID4.eir_status = NFS4_OK; out: V(pclient_record->cr_mutex); /* Release our reference to the client record */ dec_client_record_ref(pclient_record); return res_EXCHANGE_ID4.eir_status; } /* nfs41_op_exchange_id */
/** * @brief Perform a lock operation * * This function performs a lock operation (lock, unlock, test) on a * file. This method assumes the FSAL is able to support lock owners, * though it need not support asynchronous blocking locks. Passing the * lock state allows the FSAL to associate information with a specific * lock owner for each file (which may include use of a "file descriptor". * * For FSAL_VFS etc. we ignore owner, implicitly we have a lock_fd per * lock owner (i.e. per state). * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * @param[in] owner Lock owner * @param[in] lock_op Operation to perform * @param[in] request_lock Lock to take/release/test * @param[out] conflicting_lock Conflicting lock * * @return FSAL status. */ fsal_status_t vfs_lock_op2(struct fsal_obj_handle *obj_hdl, struct state_t *state, void *owner, fsal_lock_op_t lock_op, fsal_lock_param_t *request_lock, fsal_lock_param_t *conflicting_lock) { struct flock lock_args; int fcntl_comm; fsal_status_t status = {0, 0}; int retval = 0; int my_fd = -1; bool has_lock = false; bool need_fsync = false; bool closefd = false; bool bypass = false; fsal_openflags_t openflags = FSAL_O_RDWR; if (obj_hdl->fsal != obj_hdl->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", obj_hdl->fsal->name, obj_hdl->fs->fsal->name); return fsalstat(posix2fsal_error(EXDEV), EXDEV); } LogFullDebug(COMPONENT_FSAL, "Locking: op:%d type:%d start:%" PRIu64 " length:%" PRIu64 " ", lock_op, request_lock->lock_type, request_lock->lock_start, request_lock->lock_length); if (lock_op == FSAL_OP_LOCKT) { fcntl_comm = F_OFD_GETLK; /* We may end up using global fd, don't fail on a deny mode */ bypass = true; openflags = FSAL_O_ANY; } else if (lock_op == FSAL_OP_LOCK) { fcntl_comm = F_OFD_SETLK; if (request_lock->lock_type == FSAL_LOCK_R) openflags = FSAL_O_READ; else if (request_lock->lock_type == FSAL_LOCK_W) openflags = FSAL_O_WRITE; } else if (lock_op == FSAL_OP_UNLOCK) { fcntl_comm = F_OFD_SETLK; openflags = FSAL_O_ANY; } else { LogDebug(COMPONENT_FSAL, "ERROR: Lock operation requested was not TEST, READ, or WRITE."); return fsalstat(ERR_FSAL_NOTSUPP, 0); } if (lock_op != FSAL_OP_LOCKT && state == NULL) { LogCrit(COMPONENT_FSAL, "Non TEST operation with NULL state"); return fsalstat(posix2fsal_error(EINVAL), EINVAL); } if (request_lock->lock_type == FSAL_LOCK_R) { lock_args.l_type = F_RDLCK; } else if (request_lock->lock_type == FSAL_LOCK_W) { lock_args.l_type = F_WRLCK; } else { LogDebug(COMPONENT_FSAL, "ERROR: The requested lock type was not read or write."); return fsalstat(ERR_FSAL_NOTSUPP, 0); } if (lock_op == FSAL_OP_UNLOCK) lock_args.l_type = F_UNLCK; lock_args.l_pid = 0; lock_args.l_len = request_lock->lock_length; lock_args.l_start = request_lock->lock_start; lock_args.l_whence = SEEK_SET; /* flock.l_len being signed long integer, larger lock ranges may * get mapped to negative values. As per 'man 3 fcntl', posix * locks can accept negative l_len values which may lead to * unlocking an unintended range. Better bail out to prevent that. */ if (lock_args.l_len < 0) { LogCrit(COMPONENT_FSAL, "The requested lock length is out of range- lock_args.l_len(%" PRId64 "), request_lock_length(%" PRIu64 ")", lock_args.l_len, request_lock->lock_length); return fsalstat(ERR_FSAL_BAD_RANGE, 0); } /* Get a usable file descriptor */ status = find_fd(&my_fd, obj_hdl, bypass, state, openflags, &has_lock, &need_fsync, &closefd, true); if (FSAL_IS_ERROR(status)) { LogCrit(COMPONENT_FSAL, "Unable to find fd for lock operation"); return status; } errno = 0; retval = fcntl(my_fd, fcntl_comm, &lock_args); if (retval /* && lock_op == FSAL_OP_LOCK */) { retval = errno; LogDebug(COMPONENT_FSAL, "fcntl returned %d %s", retval, strerror(retval)); if (conflicting_lock != NULL) { /* Get the conflicting lock */ int rc = fcntl(my_fd, F_GETLK, &lock_args); if (rc) { retval = errno; /* we lose the initial error */ LogCrit(COMPONENT_FSAL, "After failing a lock request, I couldn't even get the details of who owns the lock."); goto err; } if (conflicting_lock != NULL) { conflicting_lock->lock_length = lock_args.l_len; conflicting_lock->lock_start = lock_args.l_start; conflicting_lock->lock_type = lock_args.l_type; } } goto err; } /* F_UNLCK is returned then the tested operation would be possible. */ if (conflicting_lock != NULL) { if (lock_op == FSAL_OP_LOCKT && lock_args.l_type != F_UNLCK) { conflicting_lock->lock_length = lock_args.l_len; conflicting_lock->lock_start = lock_args.l_start; conflicting_lock->lock_type = lock_args.l_type; } else { conflicting_lock->lock_length = 0; conflicting_lock->lock_start = 0; conflicting_lock->lock_type = FSAL_NO_LOCK; } } /* Fall through (retval == 0) */ err: if (closefd) close(my_fd); if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->lock); return fsalstat(posix2fsal_error(retval), retval); }
fsal_status_t hpss_getextattr_id_by_name(struct fsal_obj_handle *fsal_obj_hdl, const char *xattr_name, unsigned int *pxattr_id) { struct hpss_fsal_obj_handle *obj_hdl; unsigned int index; int found = FALSE; if (!fsal_obj_hdl || !xattr_name || !pxattr_id) return fsalstat(ERR_FSAL_FAULT, 0); obj_hdl = container_of(fsal_obj_hdl, struct hpss_fsal_obj_handle, obj_handle); for (index = 0; index < XATTR_COUNT; index++) { if (do_match_type(xattr_list[index].flags, fsal_obj_hdl->type) && !strcmp(xattr_list[index].xattr_name, xattr_name)) { found = TRUE; break; } } if (!found) { /* search for name in UDAs */ hpss_userattr_list_t attr_list; unsigned int i; int rc; char attrpath[MAXNAMLEN]; sec_cred_t ucreds; HPSSFSAL_ucreds_from_opctx(op_ctx, &ucreds); /* convert FSAL xattr name to HPSS attr path. * returns error if it is not a UDA name. */ if (fsal_xattr_name_2_uda(xattr_name, attrpath) == 0) { memset(&attr_list, 0, sizeof(hpss_userattr_list_t)); LogFullDebug(COMPONENT_FSAL, "looking for xattr '%s' in UDAs", xattr_name); /* get list of UDAs, and return the good index*/ rc = hpss_UserAttrListAttrHandle(&(obj_hdl->handle->ns_handle), NULL, &ucreds, &attr_list, XML_ATTR); if (rc == 0) for (i = 0; i < attr_list.len; i++) if (!strcmp(attr_list.Pair[i].Key, attrpath)) { /* xattr index is XATTR_COUNT + * UDA index */ index = XATTR_COUNT + i; found = TRUE; break; } /* Allocated by hpss - use free */ for (i = 0; i < attr_list.len; i++) { free(attr_list.Pair[i].Key); free(attr_list.Pair[i].Value); } free(attr_list.Pair); } } if (found) { *pxattr_id = index; return fsalstat(ERR_FSAL_NO_ERROR, 0); } else return fsalstat(ERR_FSAL_NOENT, ENOENT); }
/** * nfs4_op_readdir: The NFS4_OP_READDIR. * * Implements the NFS4_OP_READDIR. If fh is a pseudo FH, then call is routed to routine nfs4_op_readdir_pseudo * * @param op [IN] pointer to nfs4_op arguments * @param data [INOUT] Pointer to the compound request's data * @param resp [IN] Pointer to nfs4_op results * * @return NFS4_OK if ok, any other value show an error. * */ int nfs4_op_readdir(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { cache_entry_t *dir_pentry = NULL; cache_entry_t *pentry = NULL; cache_inode_endofdir_t eod_met; fsal_attrib_list_t attrlookup; cache_inode_status_t cache_status; cache_inode_status_t cache_status_attr; char __attribute__ ((__unused__)) funcname[] = "nfs4_op_readdir"; unsigned long dircount; unsigned long maxcount; entry4 *entry_nfs_array; cache_inode_dir_entry_t **dirent_array = NULL; verifier4 cookie_verifier; uint64_t cookie = 0; uint64_t end_cookie = 0; fsal_handle_t *entry_FSALhandle; nfs_fh4 entryFH; char val_fh[NFS4_FHSIZE]; entry_name_array_item_t *entry_name_array = NULL; unsigned int estimated_num_entries; unsigned int num_entries; int dir_pentry_unlock = FALSE; unsigned int i = 0; unsigned int outbuffsize = 0 ; unsigned int entrysize = 0 ; bitmap4 RdAttrErrorBitmap = { 1, (uint32_t *) "\0\0\0\b" }; /* 0xB = 11 = FATTR4_RDATTR_ERROR */ attrlist4 RdAttrErrorVals = { 0, NULL }; /* Nothing to be seen here */ resp->resop = NFS4_OP_READDIR; res_READDIR4.status = NFS4_OK; entryFH.nfs_fh4_len = 0; entryFH.nfs_fh4_val = val_fh; /* If there is no FH */ if(nfs4_Is_Fh_Empty(&(data->currentFH))) { res_READDIR4.status = NFS4ERR_NOFILEHANDLE; return res_READDIR4.status; } /* If the filehandle is invalid */ if(nfs4_Is_Fh_Invalid(&(data->currentFH))) { res_READDIR4.status = NFS4ERR_BADHANDLE; return res_READDIR4.status; } /* Tests if the Filehandle is expired (for volatile filehandle) */ if(nfs4_Is_Fh_Expired(&(data->currentFH))) { res_READDIR4.status = NFS4ERR_FHEXPIRED; return res_READDIR4.status; } /* Pseudo Fs management */ if(nfs4_Is_Fh_Pseudo(&(data->currentFH))) return nfs4_op_readdir_pseudo(op, data, resp); /* Xattrs management */ if(nfs4_Is_Fh_Xattr(&(data->currentFH))) return nfs4_op_readdir_xattr(op, data, resp); /* You can readdir only within a directory */ dir_pentry = data->current_entry; if(data->current_filetype != DIRECTORY) { res_READDIR4.status = NFS4ERR_NOTDIR; return res_READDIR4.status; } /* get the characteristic value for readdir operation */ dircount = arg_READDIR4.dircount; maxcount = arg_READDIR4.maxcount*0.9; cookie = (unsigned int)arg_READDIR4.cookie; /* dircount is considered meaningless by many nfsv4 client (like the CITI * one). we use maxcount instead. */ /* the Linux 3.0, 3.1.0 clients vs. TCP Ganesha comes out 10x slower * with 500 max entries */ #if 0 /* takes 2s to return 2999 entries */ estimated_num_entries = maxcount / sizeof(entry4); #else /* takes 20s to return 2999 entries */ estimated_num_entries = 50; #endif LogFullDebug(COMPONENT_NFS_V4, "--- nfs4_op_readdir ---> dircount=%lu maxcount=%lu arg_cookie=%" PRIu64" cookie=%"PRIu64" estimated_num_entries=%u", dircount, maxcount, arg_READDIR4.cookie, cookie, estimated_num_entries); /* Do not use a cookie of 1 or 2 (reserved values) */ if(cookie == 1 || cookie == 2) { res_READDIR4.status = NFS4ERR_BAD_COOKIE; return res_READDIR4.status; } /* Get only attributes that are allowed to be read */ if(!nfs4_Fattr_Check_Access_Bitmap(&arg_READDIR4.attr_request, FATTR4_ATTR_READ)) { res_READDIR4.status = NFS4ERR_INVAL; return res_READDIR4.status; } /* If maxcount is too short, return NFS4ERR_TOOSMALL */ if(maxcount < sizeof(entry4) || estimated_num_entries == 0) { res_READDIR4.status = NFS4ERR_TOOSMALL; return res_READDIR4.status; } /* * If cookie verifier is used, then an non-trivial value is * returned to the client This value is the mtime of * the pentry. If verifier is unused (as in many NFS * Servers) then only a set of zeros is returned (trivial * value) */ memset(cookie_verifier, 0, NFS4_VERIFIER_SIZE); if(data->pexport->UseCookieVerifier == 1) memcpy(cookie_verifier, &dir_pentry->internal_md.mod_time, sizeof(time_t)); /* Cookie delivered by the server and used by the client SHOULD not ne 0, 1 or 2 (cf RFC3530, page192) * because theses value are reserved for special use. * 0 - cookie for first READDIR * 1 - reserved for . on client handside * 2 - reserved for .. on client handside * Entries '.' and '..' are not returned also * For these reason, there will be an offset of 3 between NFS4 cookie and * HPSS cookie */ if((cookie != 0) && (data->pexport->UseCookieVerifier == 1)) { if(memcmp(cookie_verifier, arg_READDIR4.cookieverf, NFS4_VERIFIER_SIZE) != 0) { res_READDIR4.status = NFS4ERR_BAD_COOKIE; return res_READDIR4.status; } } /* The default behaviour is to consider that eof is not reached, the * returned values by cache_inode_readdir will let us know if eod was * reached or not */ res_READDIR4.READDIR4res_u.resok4.reply.eof = FALSE; /* Get prepared for readdir */ if((dirent_array = (cache_inode_dir_entry_t **) Mem_Alloc( estimated_num_entries * sizeof(cache_inode_dir_entry_t*))) == NULL) { res_READDIR4.status = NFS4ERR_SERVERFAULT; goto out; } /* Perform the readdir operation */ if(cache_inode_readdir(dir_pentry, data->pexport->cache_inode_policy, cookie, estimated_num_entries, &num_entries, &end_cookie, &eod_met, dirent_array, data->ht, &dir_pentry_unlock, data->pclient, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { res_READDIR4.status = nfs4_Errno(cache_status); goto out; } /* For an empty directory, we will find only . and .., so reply as if the * end is reached */ if(num_entries == 0) { /* only . and .. */ res_READDIR4.READDIR4res_u.resok4.reply.entries = NULL; res_READDIR4.READDIR4res_u.resok4.reply.eof = TRUE; memcpy(res_READDIR4.READDIR4res_u.resok4.cookieverf, cookie_verifier, NFS4_VERIFIER_SIZE); } else { /* Start computing the outbuffsize */ outbuffsize = sizeof( bool_t) /* eof */ + sizeof( nfsstat4 ) /* READDIR4res::status */ + NFS4_VERIFIER_SIZE /* cookie verifier */ ; /* Allocation of reply structures */ if((entry_name_array = (entry_name_array_item_t *) Mem_Alloc(num_entries * (FSAL_MAX_NAME_LEN + 1))) == NULL) { LogError(COMPONENT_NFS_V4, ERR_SYS, ERR_MALLOC, errno); res_READDIR4.status = NFS4ERR_SERVERFAULT; return res_READDIR4.status; } memset((char *)entry_name_array, 0, num_entries * (FSAL_MAX_NAME_LEN + 1)); if((entry_nfs_array = (entry4 *) Mem_Alloc(num_entries * sizeof(entry4))) == NULL) { LogError(COMPONENT_NFS_V4, ERR_SYS, ERR_MALLOC, errno); res_READDIR4.status = NFS4ERR_SERVERFAULT; return res_READDIR4.status; } memset((char *)entry_nfs_array, 0, num_entries * sizeof(entry4)); for(i = 0; i < num_entries; i++) { entry_nfs_array[i].name.utf8string_val = entry_name_array[i]; if(str2utf8(dirent_array[i]->name.name, &entry_nfs_array[i].name) == -1) { res_READDIR4.status = NFS4ERR_SERVERFAULT; goto out; } /* Set the cookie value */ entry_nfs_array[i].cookie = dirent_array[i]->cookie; /* Get the pentry for the object's attributes and filehandle */ if( ( pentry = cache_inode_lookup_no_mutex( dir_pentry, &dirent_array[i]->name, data->pexport->cache_inode_policy, &attrlookup, data->ht, data->pclient, data->pcontext, &cache_status ) ) == NULL ) { Mem_Free((char *)entry_nfs_array); /* Return the fattr4_rdattr_error , cf RFC3530, page 192 */ entry_nfs_array[i].attrs.attrmask = RdAttrErrorBitmap; entry_nfs_array[i].attrs.attr_vals = RdAttrErrorVals; res_READDIR4.status = NFS4ERR_SERVERFAULT; goto out; } /* If file handle is asked in the attributes, provide it */ if(arg_READDIR4.attr_request.bitmap4_val != NULL && (arg_READDIR4.attr_request.bitmap4_val[0] & FATTR4_FILEHANDLE)) { if((entry_FSALhandle = cache_inode_get_fsal_handle(pentry, &cache_status_attr)) == NULL) { /* Faulty Handle or pentry */ Mem_Free((char *)entry_nfs_array); res_READDIR4.status = NFS4ERR_SERVERFAULT; goto out; } if(!nfs4_FSALToFhandle(&entryFH, entry_FSALhandle, data)) { /* Faulty type */ Mem_Free((char *)entry_nfs_array); res_READDIR4.status = NFS4ERR_SERVERFAULT; goto out; } } if(nfs4_FSALattr_To_Fattr(data->pexport, &attrlookup, &(entry_nfs_array[i].attrs), data, &entryFH, &(arg_READDIR4.attr_request)) != 0) { /* Return the fattr4_rdattr_error , cf RFC3530, page 192 */ entry_nfs_array[i].attrs.attrmask = RdAttrErrorBitmap; entry_nfs_array[i].attrs.attr_vals = RdAttrErrorVals; } /* Update the size of the output buffer */ entrysize = sizeof( nfs_cookie4 ) ; /* nfs_cookie4 */ entrysize += sizeof( u_int ) ; /* pathname4::utf8strings_len */ entrysize += entry_nfs_array[i].name.utf8string_len ; entrysize += sizeof( u_int ) ; /* bitmap4_len */ entrysize += entry_nfs_array[i].attrs.attrmask.bitmap4_len ; entrysize += sizeof( u_int ) ; /* attrlist4_len */ entrysize += entry_nfs_array[i].attrs.attr_vals.attrlist4_len ; entrysize += sizeof( caddr_t ) ; outbuffsize += entrysize; LogFullDebug(COMPONENT_NFS_V4, " === nfs4_op_readdir ===> i=%u name=%s cookie=%"PRIu64" " "entrysize=%u buffsize=%u", i, dirent_array[i]->name.name, entry_nfs_array[i].cookie, entrysize, outbuffsize); /* Chain the entries together */ entry_nfs_array[i].nextentry = NULL; if(i != 0) { if( outbuffsize < maxcount ) entry_nfs_array[i - 1].nextentry = &(entry_nfs_array[i]); else { LogFullDebug(COMPONENT_NFS_V4, "=== nfs4_op_readdir ===> " "maxcount reached at %u entries name=%s " "cookie=%llu " "buffsize=%u (return early)", i+1, dirent_array[i]->name.name, (unsigned long long)entry_nfs_array[i].cookie, outbuffsize); entry_nfs_array[i - 1].nextentry = NULL ; break ; } } } /* for i */ if((i == num_entries) && (eod_met == END_OF_DIR)) { LogFullDebug(COMPONENT_NFS_V4, "End of directory reached: num_entries=%d i=%d", num_entries, i); /* This is the end of the directory */ res_READDIR4.READDIR4res_u.resok4.reply.eof = TRUE; memcpy(res_READDIR4.READDIR4res_u.resok4.cookieverf, cookie_verifier, NFS4_VERIFIER_SIZE); } /* Put the entry's list in the READDIR reply */ res_READDIR4.READDIR4res_u.resok4.reply.entries = entry_nfs_array; } /* Do not forget to set the verifier */ memcpy((char *)res_READDIR4.READDIR4res_u.resok4.cookieverf, cookie_verifier, NFS4_VERIFIER_SIZE); res_READDIR4.status = NFS4_OK; out: /* release read lock on dir_pentry, if requested */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if (dirent_array) { if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, data->pclient ) ; Mem_Free((char *)dirent_array); } return res_READDIR4.status; } /* nfs4_op_readdir */