pax_global_header00006660000000000000000000000064132427241020014507gustar00rootroot0000000000000052 comment=97867975b2ee69d475876e222c439b1bc9764a78 nfs-ganesha-2.6.0/000077500000000000000000000000001324272410200137065ustar00rootroot00000000000000nfs-ganesha-2.6.0/.gitignore000066400000000000000000000011271324272410200156770ustar00rootroot00000000000000# dirs oldtars/ linuxbox-ceph/ src/Docs/html/ src/Docs/latex/ src/autom4te.cache/ src/build-aux/ src/rpm/ src/pkg-deb/ .deps/ .libs/ CMakeFiles/ build/ # files *.kdevelop* *.kdevses TAGS tags Makefile.in Makefile libtool configure depcomp missing ltmain.sh install-sh config.* aclocal.m4 *.o *.a libganeshaNFS.pc nfs-ganesha.spec cppcheck.* cscope.* ylwrap nfs-ganesha*.tar.gz nfs-ganesha*.tar.bz2 patch-for-HPSS-nfs-ganesha-* *.diff *.patch !debian/patches/*.patch core.* *cmake_install.cmake libfsal*.so* Doxyfile *~ *.swp .project .checkpatch.conf src/scripts/systemd/nfs-ganesha-config.service nfs-ganesha-2.6.0/.gitmodules000066400000000000000000000001431324272410200160610ustar00rootroot00000000000000[submodule "src/libntirpc"] path = src/libntirpc url = https://github.com/nfs-ganesha/ntirpc.git nfs-ganesha-2.6.0/.gitreview000066400000000000000000000001321324272410200157100ustar00rootroot00000000000000[gerrit] host=review.gerrithub.io port=29418 project=ffilz/nfs-ganesha defaultbranch=next nfs-ganesha-2.6.0/.mailmap000066400000000000000000000053721324272410200153360ustar00rootroot00000000000000# .mailmap, see 'git shortlog --help' for details # # Listing of contributors that filed patches with different email addresses. # Format: # Allison Henderson Daniel Gryniewicz Daniel Gryniewicz Daniel Gryniewicz Dominique Martinet Frank S. Filz Frank S. Filz Frank S. Filz Jeremy Bongio Jeremy Bongio Jeremy Bongio Jim Lieb Manfred Haubrich Marc Eshel Matt Benjamin Matt Benjamin Matt Benjamin matt Meghana Madhusudhan Philippe DENIEL Philippe DENIEL Philippe DENIEL Philippe DENIEL Philippe DENIEL Philippe DENIEL Philippe DENIEL Philippe DENIEL Philippe DENIEL Philippe DENIEL Rong Zeng Rong Zeng Rong Zeng Rong Zeng Suhrud Patankar Thomas LEIBOVICI Thomas LEIBOVICI Thomas LEIBOVICI Thomas LEIBOVICI Thomas LEIBOVICI Thomas LEIBOVICI Venkateswararao Jujjuri (JV) William Allen Simpson William Allen Simpson nfs-ganesha-2.6.0/.travis.yml000066400000000000000000000055511324272410200160250ustar00rootroot00000000000000language: c compiler: - gcc - clang env: - BUILD_TYPE=Maintainer - BUILD_TYPE=Debug - BUILD_TYPE=Release branches: only: - next matrix: allow_failures: - compiler: clang env: BUILD_TYPE=Maintainer - compiler: clang env: BUILD_TYPE=Debug before_install: - git submodule update --init --recursive - sudo add-apt-repository -y ppa:gluster/glusterfs-3.6 - sudo apt-add-repository -y ppa:lttng/ppa - sudo apt-get update -q - sudo apt-get install -y libnfsidmap2 - sudo apt-get install -y libnfsidmap-dev - sudo apt-get install -y libkrb5-3 - sudo apt-get install -y libkrb5-dev - sudo apt-get install -y libk5crypto3 - sudo apt-get install -y libgssapi-krb5-2 - sudo apt-get install -y libgssglue1 - sudo apt-get install -y libdbus-1-3 - sudo apt-get install -y libattr1-dev - sudo apt-get install -y libacl1-dev - sudo apt-get install -y dbus - sudo apt-get install -y libdbus-1-dev - sudo apt-get install -y libcap-dev - sudo apt-get install -y libjemalloc-dev - sudo apt-get install -y glusterfs-common - sudo apt-get install -y uuid-dev - sudo apt-get install -y libblkid-dev - sudo apt-get install -y xfslibs-dev # - sudo apt-get install -y libcephfs-dev - sudo apt-get install -y libwbclient-dev - sudo apt-get install -y lttng-tools - sudo apt-get install -y liblttng-ust-dev - sudo apt-get install -y lttng-modules-dkms - sudo apt-get install -y pyqt4-dev-tools - sudo apt-get install -y rpm2cpio - sudo apt-get install -y libaio-dev - sudo apt-get install -y libibverbs-dev - sudo apt-get install -y librdmacm-dev install: - wget https://downloads.hpdd.intel.com/public/lustre/latest-maintenance-release/el6/server/RPMS/x86_64/lustre-2.5.3-2.6.32_431.23.3.el6_lustre.x86_64.x86_64.rpm -O /tmp/lustre.rpm - mkdir /tmp/lustre && pushd /tmp/lustre ; rpm2cpio /tmp/lustre.rpm | cpio -id ./usr/include/\* ./usr/lib64/liblustreapi.a ; popd - wget https://github.com/tfb-bull/mooshika/archive/0.7.1.tar.gz -O /tmp/mooshika.tar.gz - mkdir /tmp/mooshika && tar xf /tmp/mooshika.tar.gz -C /tmp && cd /tmp/mooshika-0.7.1 && sh autogen.sh && ./configure --prefix=/tmp/mooshika && make && make install && cd $TRAVIS_BUILD_DIR - if [[ ${CC} == 'gcc' ]]; then cd contrib/libzfswrap && aclocal -I m4 && libtoolize --force --copy && autoconf && autoheader && automake -a --add-missing -Wall && ./configure --prefix=/tmp/libzfswrap && make && make install && cd $TRAVIS_BUILD_DIR ; fi script: - mkdir ../build && cd ../build && cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_CONFIG=everything -DUSE_FSAL_PT=ON -DUSE_FSAL_CEPH=OFF -DUSE_ADMIN_TOOLS=ON -DUSE_LTTNG=ON -DUSE_TIRPC_IPV6=ON -DUSE_9P_RDMA=ON -D_USE_9P_RDMA=ON -DLUSTRE_PREFIX=/tmp/lustre/usr -DZFS_PREFIX=/tmp/libzfswrap -DMOOSHIKA_PREFIX=/tmp/mooshika ../nfs-ganesha/src/ && make #notifications: # email: # recepients: # - thomas.favre-bulle@bull.net # # on_success: always # on_failure: always nfs-ganesha-2.6.0/README.md000066400000000000000000000006001324272410200151610ustar00rootroot00000000000000[![Coverity Scan Build Status](https://scan.coverity.com/projects/2187/badge.svg)](https://scan.coverity.com/projects/2187) nfs-ganesha =========== NFS-Ganesha is an NFSv3,v4,v4.1 fileserver that runs in user mode on most UNIX/Linux systems. It also supports the 9p.2000L protocol. For more information, consult the [project wiki](https://github.com/nfs-ganesha/nfs-ganesha/wiki). nfs-ganesha-2.6.0/coverity/000077500000000000000000000000001324272410200155525ustar00rootroot00000000000000nfs-ganesha-2.6.0/coverity/ganesha_model.c000066400000000000000000000001221324272410200204770ustar00rootroot00000000000000/* coverity [+free] */ int dlclose(void *handle) { __coverity_free__(handle); } nfs-ganesha-2.6.0/jenkins/000077500000000000000000000000001324272410200153475ustar00rootroot00000000000000nfs-ganesha-2.6.0/jenkins/ganesha.test.conf000066400000000000000000000042471324272410200206110ustar00rootroot00000000000000################################################### # Export entries ################################################### EXPORT { # Export Id (mandatory) Export_Id = 77 ; # Exported path (mandatory) Path = "/tmp" ; Root_Access = "*" ; Access = "*"; # Pseudo path for NFSv4 export (mandatory) Pseudo = "/tmp"; SecType = "sys"; # The uid for root when its host doesn't have a root_access. (default: -2) Anonymous_root_uid = -2 ; NFS_Protocols = "3,4" ; SecType = "sys"; # Maximum size for a read operation. MaxRead = 32768; # Maximum size for a write operation. MaxWrite = 32768; # Prefered size for a read operation. PrefRead = 32768; # Prefered size for a write operation. PrefWrite = 32768; # Prefered size for a readdir operation. PrefReaddir = 32768; # Filesystem ID (default 666.666) # This sets the filesystem id for the entries of this export. Filesystem_id = 192.168 ; # Should the client to this export entry come from a privileged port ? PrivilegedPort = FALSE ; # Export entry "tag" name # Can be used as an alternative way of addressing the # export entry at mount time ( alternate to the 'Path') Tag = "vfs" ; FSAL = "VFS" ; } FSAL { VFS { FSAL_Shared_Library = /tmp/libfsalvfs.so.4.2.0 ; } } FileSystem { Umask = 0000 ; Link_support = TRUE; # hardlink support Symlink_support = TRUE; # symlinks support CanSetTime = TRUE; # Is it possible to change file times } NFS_Core_Param { # Number of worker threads to be used Nb_Worker = 60 ; # NFS Port to be used # Default value is 2049 NFS_Port = 2049 ; # Mount port to be used # Default is 0 (let the system use an available ephemeral port) #MNT_Port = 0 ; # NFS RPC Program number # Default value is 100003 #NFS_Program = 100003 ; # Mount protocol RPC Program Number # Default value is 100005 #MNT_Program = 100005 ; NFS_Protocols = "3,4" ; # Size to be used for the core dump file (if the daemon crashes) ##Core_Dump_Size = 0 ; } _9P { _9P_TCP_Port = 564 ; _9P_RDMA_Port = 5640 ; DebugLevel = NIV_DEBUG ; # } nfs-ganesha-2.6.0/jenkins/sigmund_as_root.rc000066400000000000000000000013531324272410200210730ustar00rootroot00000000000000# Configuration file for the run_test.sh framework # # Use this template to build your own test_variables.rc # ##### Repo to get to build test GIT_PYNFS_URL=git://git.linux-nfs.org/projects/iisaman/newpynfs ##### Root of test ##### TEST_DIR=/mnt/sigmund BUILD_TEST_DIR=/tmp/sigmund ##### Variables to be used by module allfs ##### # Path to cthon04 test's suite CTHON04_DIR=/opt/cthon04 GIT_CLONE_URL=/opt/GANESHA/.git # Non-root user that will run part of the test TEST_USER=root # Primary group for the TEST_USER GROUP1=adm # One of the alternate group for TEST_USER GROUP2=sys ##### Variables to be used by module nfsv41 ##### # path to pynfs repo PYNFS_DIR=/opt/pynfs # Remote URL to be used by PYNFS PYNFS_URL="aury62:/vfs/pynfs40" nfs-ganesha-2.6.0/src/000077500000000000000000000000001324272410200144755ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/CMakeLists.txt000066400000000000000000001327241324272410200172460ustar00rootroot00000000000000# NFS Ganesha Cmake # Current version as of Fedora 16. Not tested with earlier. cmake_minimum_required(VERSION 2.6.3) message( STATUS "cmake version ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" ) if( "${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER "2.6" ) if(COMMAND cmake_policy) cmake_policy(SET CMP0017 NEW) endif(COMMAND cmake_policy) endif( "${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER "2.6" ) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules/") project(nfs-ganesha C CXX) # Project versioning set(GANESHA_MAJOR_VERSION 2) set(GANESHA_MINOR_VERSION 6) IF(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") set(GANESHA_BUILD_RELEASE 1) ELSE() set(GANESHA_BUILD_RELEASE 0) ENDIF(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") # needs to come after project() IF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT OR CMAKE_INSTALL_PREFIX STREQUAL "/usr") SET(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "Install prefix for common files" FORCE) message(STATUS "override default CMAKE_INSTALL_PREFIX = ${CMAKE_INSTALL_PREFIX}") SET(SYSCONFDIR "/etc" CACHE PATH "Install prefix for common files") SET(SYSSTATEDIR "/var" CACHE PATH "Install prefix for common files") ELSE() message(STATUS "was set CMAKE_INSTALL_PREFIX = ${CMAKE_INSTALL_PREFIX}") SET(SYSCONFDIR "${CMAKE_INSTALL_PREFIX}/etc" CACHE PATH "Install prefix for common files") SET(SYSSTATEDIR "${CMAKE_INSTALL_PREFIX}/var" CACHE PATH "Install prefix for common files") ENDIF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT OR CMAKE_INSTALL_PREFIX STREQUAL "/usr") # Patch level is always ".0" for mainline (master). It is blank for development. # When starting a stable maintenance branch, this becomes ".N" # where N is monotonically increasing starting at 1. Remember to include the "." !! set(GANESHA_PATCH_LEVEL .0) # Extra version is for naming development/RC. It is blank in master/stable branches # so it can be available to end-users to name local variants/versions # If used, it is always of the form "-whateveryouwant" set(GANESHA_EXTRA_VERSION ) set(GANESHA_VERSION ${GANESHA_MAJOR_VERSION}.${GANESHA_MINOR_VERSION}${GANESHA_PATCH_LEVEL}${GANESHA_EXTRA_VERSION}) set(GANESHA_BASE_VERSION ${GANESHA_MAJOR_VERSION}.${GANESHA_MINOR_VERSION}${GANESHA_PATCH_LEVEL}) set(VERSION_COMMENT "GANESHA file server is 64 bits compliant and supports NFS v3,4.0,4.1 (pNFS) and 9P" ) # find out which platform we are building on if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") set(LINUX ON) set(UNIX ON) # Now detects the Linux's distro set(DISTRO "UNKNOWN") set(LIBEXECDIR "/usr/libexec") EXECUTE_PROCESS( COMMAND awk -F= "/^NAME=/ { print $2 }" /etc/os-release OUTPUT_VARIABLE SYS_RELEASE ERROR_QUIET ) # Red Hat Enterprise Linux versions before 7.0 will be detected as UNKNOWN if( ${SYS_RELEASE} MATCHES "Red Hat" ) message( STATUS "Detected a Linux Red Hat machine" ) set(DISTRO "RED_HAT") elseif( ${SYS_RELEASE} MATCHES "Fedora" ) message( STATUS "Detected a Linux Fedora machine" ) set(DISTRO "FEDORA") elseif( ${SYS_RELEASE} MATCHES "SLES" ) message( STATUS "Detected a Linux SLES machine" ) set(DISTRO "SLES") set(LIBEXECDIR "/usr/lib") elseif( ${SYS_RELEASE} MATCHES "openSUSE Leap" ) message( STATUS "Detected a Linux openSUSE Leap machine" ) set(DISTRO "SLES") set(LIBEXECDIR "/usr/lib") elseif( ${SYS_RELEASE} MATCHES "openSUSE Tumbleweed" ) message( STATUS "Detected a Linux openSUSE Tumbleweed machine" ) set(DISTRO "SLES") set(LIBEXECDIR "/usr/lib") elseif( (${SYS_RELEASE} MATCHES "Debian GNU/Linux") OR (${SYS_RELEASE} MATCHES "Ubuntu") ) message( STATUS "Detected a Linux Debian base machine" ) set(DISTRO "DEBIAN") set(LIBEXECDIR "/usr/lib") else( ${SYS_RELEASE} MATCHES "Red Hat" ) message( STATUS "Detected an UNKNOWN Linux machine" ) set(DISTRO "UNKNOWN") endif( ${SYS_RELEASE} MATCHES "Red Hat" ) endif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") set(FREEBSD ON) set(UNIX ON) # On FreeBSD libc doesn't directly provide libexecinfo, so we have to find it set(USE_EXECINFO ON) endif(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") set(WINDOWS ON) endif(${CMAKE_SYSTEM_NAME} MATCHES "Windows") # Identify the host we are building on EXECUTE_PROCESS( COMMAND hostname OUTPUT_VARIABLE BUILD_HOST_NAME OUTPUT_STRIP_TRAILING_WHITESPACE ) find_package(Toolchain REQUIRED) find_package(Sanitizers) # Add maintainer mode for (mainly) strict builds include(${CMAKE_SOURCE_DIR}/cmake/maintainer_mode.cmake) # For libraries that provide pkg-config files include(FindPkgConfig) # If we are in a git tree, then this CMakeLists.txt is in "src/" and go .git is in "src/.." IF( EXISTS ${CMAKE_SOURCE_DIR}/../.git/HEAD ) message( STATUS "Compilation from within a git repository. Using git rev-parse HEAD") EXECUTE_PROCESS( COMMAND git rev-parse HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET OUTPUT_VARIABLE _GIT_HEAD_COMMIT) EXECUTE_PROCESS( COMMAND git describe --long WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET OUTPUT_VARIABLE _GIT_DESCRIBE) ELSE( EXISTS ${CMAKE_SOURCE_DIR}/../.git/HEAD ) message( STATUS "Outside a git repository, use saved data" ) EXEC_PROGRAM(${CMAKE_SOURCE_DIR}/cmake/githead_from_path.sh ARGS ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE _GIT_HEAD_COMMIT) EXEC_PROGRAM(${CMAKE_SOURCE_DIR}/cmake/gitdesc_from_path.sh ARGS ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE _GIT_DESCRIBE) ENDIF( EXISTS ${CMAKE_SOURCE_DIR}/../.git/HEAD ) STRING(SUBSTRING ${_GIT_HEAD_COMMIT} 0 7 _GIT_HEAD_COMMIT_ABBREV ) if (FREEBSD) #default gcc doesn't like using -Wuninitialized without -O on FreeBSD set(PLATFORM "FREEBSD") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2 -ggdb") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-optimize-sibling-calls") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--export-dynamic") set(OS_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/include/os/freebsd") find_library(LIBDL c) # libc suffices on freebsd endif(FREEBSD) if (LINUX) set(PLATFORM "LINUX") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64") set(OS_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/include/os/linux") find_library(LIBDL dl) # module loader endif(LINUX) if (MSVC) add_definitions(-D_CRT_SECURE_NO_WARNINGS) endif(MSVC) # Library path name get_property(USE_LIB64 GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS) if (USE_LIB64) set(LIB_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/lib64 CACHE PATH "Specify name of libdir inside install path") else (USE_LIB64) set(LIB_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/lib CACHE PATH "Specify name of libdir inside install path") endif (USE_LIB64) IF(FSAL_DESTINATION) set( FSAL_DESTINATION ${FSAL_DESTINATION} ) ELSE() set( FSAL_DESTINATION "${LIB_INSTALL_DIR}/ganesha") ENDIF() if (CMAKE_SYSTEM_PROCESSOR MATCHES "unknown") # uname -p is broken on this system. Try uname -m EXECUTE_PROCESS( COMMAND uname -m OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET OUTPUT_VARIABLE ARCH) else (CMAKE_SYSTEM_PROCESSOR MATCHES "unknown") set(ARCH ${CMAKE_SYSTEM_PROCESSOR}) endif (CMAKE_SYSTEM_PROCESSOR MATCHES "unknown") if (ARCH MATCHES "x86_64") # Nothing special to do for x86_64 elseif (ARCH MATCHES "i386") # Nothing special to do for i386 elseif (ARCH MATCHES "mips") set(SYSTEM_LIBRARIES "-latomic" ${SYSTEM_LIBRARIES}) else() message(WARNING "Unhandled architecture ${ARCH}") endif () # FSAL selection # FSALs which are enabled by default but could be disabled # during the build option(USE_FSAL_PROXY "build PROXY FSAL shared library" ON) option(USE_FSAL_VFS "build VFS FSAL shared library" ON) option(USE_FSAL_CEPH "build CEPH FSAL shared library" ON) option(USE_FSAL_GPFS "build GPFS FSAL" ON) option(USE_FSAL_XFS "build XFS support in VFS FSAL" ON) option(USE_FSAL_PANFS "build PanFS support in VFS FSAL" OFF) option(USE_FSAL_GLUSTER "build GLUSTER FSAL shared library" ON) option(USE_FSAL_NULL "build NULL FSAL shared library" ON) option(USE_FSAL_RGW "build RGW FSAL shared library" OFF) option(USE_FSAL_MEM "build Memory FSAL shared library" ON) option(USE_TOOL_MULTILOCK "build multilock tool" OFF) # nTIRPC option(USE_SYSTEM_NTIRPC "Use the system nTIRPC, rather than the submodule" OFF) option (USE_GSS "enable RPCSEC_GSS support" ON) option(TIRPC_EPOLL "platform supports EPOLL or emulation" ON) # Build configure options option(USE_DBUS "enable DBUS protocol support" OFF) # Various DBUS enabled features option(USE_CB_SIMULATOR "enable callback simulator thread" OFF) option(USE_NFSIDMAP "Use of libnfsidmap for name resolution" ON) option(ENABLE_ERROR_INJECTION "enable error injection" OFF) option(ENABLE_VFS_DEBUG_ACL "Enable debug ACL store for VFS" OFF) option(ENABLE_RFC_ACL "Use all RFC ACL checks" OFF) # Electric Fence (-lefence) link flag option(USE_EFENCE "link with efence memory debug library" OFF) # These are -D_FOO options, why ??? should be flags?? option(_NO_TCP_REGISTER "disable registration of tcp services on portmapper" OFF) option(_NO_PORTMAPPER "disable registration on portmapper" OFF) option(_NO_XATTRD "disable ghost xattr directory and files support" ON) option(DEBUG_SAL "enable debugging of SAL by keeping list of all locks, stateids, and state owners" OFF) option(_VALGRIND_MEMCHECK "Initialize buffers passed to GPFS ioctl that valgrind doesn't understand" OFF) option(ENABLE_LOCKTRACE "Enable lock trace" OFF) option(PROXY_HANDLE_MAPPING "enable NFSv3 handle mapping for PROXY FSAL" OFF) option(DEBUG_MDCACHE "Add various asserts to mdcache" OFF) # Debug symbols (-g) build flag option(DEBUG_SYMS "include debug symbols to binaries (-g option)" OFF) # Add coverage information to build tree option(COVERAGE "add flag to generate coverage data at runtime" OFF) # Add coverage information to build tree option(ENFORCE_GCC "enforce gcc as a the C compiler used for the project" OFF) # enable code profiling [-g -pg] option(PROFILING "turn on code profiling (-g and -pg)" OFF) # Define CPACK component (to deal with sub packages) set(CPACK_COMPONENTS_ALL daemon fsal headers ) set(CPACK_COMPONENT_DAEMON_DISPLAY_NAME "NFS-Ganesha daemon") if (USE_SYSTEM_NTIRPC) # Don't include libntirpc in the tarball set(CPACK_SOURCE_IGNORE_FILES "libntirpc") else(USE_SYSTEM_NTIRPC) # Don't include libntirpc's spec file; this can confuse rpmbuild set(CPACK_SOURCE_IGNORE_FILES "libntirpc.spec$") endif(USE_SYSTEM_NTIRPC) # Include custom config and cpack module include(${CMAKE_SOURCE_DIR}/cmake/cpack_config.cmake) include(CPack) # MSPAC support -lwbclient link flag if( "${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER "2.6" ) option(_MSPAC_SUPPORT "enable mspac Winbind support" ON) else( "${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER "2.6" ) option(_MSPAC_SUPPORT "enable mspac Winbind support" OFF) endif( "${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER "2.6" ) # CUnit option(USE_CUNIT "Use Cunit test framework" OFF) # Blkin (Zipkin) Tracing option(USE_BLKIN "Use Blkin/Zipkin trace framework" OFF) option(BLKIN_PREFIX "Blkin installation prefix" "/opt/blkin") if(USE_BLKIN) find_package(LTTng) if(LTTNG_FOUND) else(LTTNG_FOUND) message(WARNING "LTTng libraries not found. Disabling USE_BLKIN") set(USE_BLKIN OFF) endif(LTTNG_FOUND) if (NOT BLKIN_PREFIX) set(BLKIN_PREFIX "/opt/blkin") endif(NOT BLKIN_PREFIX) set(BLKIN_PREFIX ${BLKIN_PREFIX} CACHE PATH "Blkin path") find_library(BLKIN NAMES blkin PATHS "${BLKIN_PREFIX}/lib" REQUIRED) find_library(BLKIN_INIT NAMES blkin_init PATHS "${BLKIN_REFIX}/lib" REQUIRED) find_library(LTTNG NAMES lttng-ust REQUIRED) # build flags set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DHAVE_BLKIN -I${BLKIN_PREFIX}/include") set(SYSTEM_LIBRARIES ${BLKIN} ${SYSTEM_LIBRARIES}) set(SYSTEM_LIBRARIES ${LTTNG} ${SYSTEM_LIBRARIES}) endif(USE_BLKIN) # GTest option(USE_GTEST "Use Google Test test framework" OFF) option(GTEST_PREFIX "Google Test installation prefix" "/opt/gmock/gtest") if(USE_GTEST) find_package(LTTng) find_package(Gperftools) if(LTTNG_CTL_FOUND AND GPERFTOOLS_FOUND) if (NOT GTEST_PREFIX) set(GTEST_PREFIX "/opt/gmock/gtest") endif(NOT GTEST_PREFIX) set(GTEST_PREFIX ${GTEST_PREFIX} CACHE PATH "Google Test path") find_library(GTEST NAMES gtest PATHS "${GTEST_PREFIX}") find_library(GTEST_MAIN NAMES gtest_main PATHS "${GTEST_PREFIX}") else(LTTNG_CTL_FOUND AND GPERFTOOLS_FOUND) message(WARNING "Couldn't find eithe LTTng ctl libraries or gperftools. Disabling USE_GTEST") set(USE_GTEST OFF) endif(LTTNG_CTL_FOUND AND GPERFTOOLS_FOUND) endif(USE_GTEST) # NFS RDMA option(USE_NFS_RDMA "enable NFS/RDMA support" OFF) # Enable 9P Support option(USE_9P "enable 9P support" ON) option(USE_9P_RDMA "enable 9P_RDMA support" OFF) # Enable NFSv3 Support option(USE_NFS3 "enable NFSv3 support" ON) # Enable NLM Support option(USE_NLM "enable NLM support" ON) # AF_VSOCK host support (NFS) option(USE_VSOCK "enable AF_VSOCK listener" OFF) if(USE_VSOCK) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DRPC_VSOCK") endif(USE_VSOCK) # This option will stop cmake compilation if a requested FSAL could not be built option(STRICT_PACKAGE "Enable strict packaging behavior" OFF ) # This option will trigger "long distro name" aka name that contains git information option(DISTNAME_HAS_GIT_DATA "Distribution package's name carries git data" OFF ) # Build and package Python admin scripts for managing via DBus option(USE_ADMIN_TOOLS "Package Admin scripts" OFF) # Build and package Python gui admin scripts for managing via DBus option(USE_GUI_ADMIN_TOOLS "Package GUI Admin scripts" ON) # Enable GCC Thread-sanitizer option(USE_TSAN "Enable GCC Thread-Sanitizer" OFF) # Enable LTTng tracing option(USE_LTTNG "Enable LTTng tracing" OFF) # Build man page. option(USE_MAN_PAGE "Build MAN page" OFF) # Enable Rados KV store for recovery option(USE_RADOS_RECOV "Enable Rados KV Recovery" ON) # Enable RADOS URL config file sections option(RADOS_URLS "Enable config file inclusion from RADOS objects" OFF) # # End build options # # Choose a shortcut build config IF(BUILD_CONFIG) INCLUDE( ${CMAKE_SOURCE_DIR}/cmake/build_configurations/${BUILD_CONFIG}.cmake) ENDIF() IF(DEBUG_SYMS) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -g") ENDIF(DEBUG_SYMS) IF(COVERAGE) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage") ENDIF(COVERAGE) # Set what's needed is GCC is enforced IF(ENFORCE_GCC) set(CMAKE_COMPILER_IS_GNUCXX TRUE) set(CMAKE_C_COMPILER gcc) ENDIF(ENFORCE_GCC) IF(USE_FSAL_GLUSTER) IF(GLUSTER_PREFIX) set(GLUSTER_PREFIX ${GLUSTER_PREFIX} CACHE PATH "Path to Gluster installation") LIST(APPEND CMAKE_PREFIX_PATH "${GLUSTER_PREFIX}") LIST(APPEND CMAKE_LIBRARY_PATH "${GLUSTER_PREFIX}/lib") LIST(APPEND CMAKE_LIBRARY_PATH "${GLUSTER_PREFIX}/local/lib") LIST(APPEND CMAKE_LIBRARY_PATH "${GLUSTER_PREFIX}/local/lib64") LIST(APPEND CMAKE_REQUIRED_INCLUDES "${GLUSTER_PREFIX}/include") ELSE() set(GLUSTER_PREFIX "/usr" CACHE PATH "Path to Gluster installation") ENDIF() ENDIF() IF(KRB5_PREFIX) set(KRB5_PREFIX ${KRB5_PREFIX} CACHE PATH "Path to Krb5 installation") LIST(APPEND CMAKE_PREFIX_PATH "${KRB5_PREFIX}") LIST(APPEND CMAKE_LIBRARY_PATH "${KRB5_PREFIX}/lib") ENDIF() if(SAMBA4_PREFIX) set(SAMBA4_PREFIX ${SAMBA4_PREFIX} CACHE PATH "Path to Samba4 installation") LIST(APPEND CMAKE_PREFIX_PATH "${SAMBA4_PREFIX}") LIST(APPEND CMAKE_LIBRARY_PATH "${SAMBA4_PREFIX}/lib") endif() IF(MOOSHIKA_PREFIX) set(MOOSHIKA_PREFIX ${MOOSHIKA_PREFIX} CACHE PATH "Path to Mooshika installation") set(ENV{PKG_CONFIG_PATH} "${PKG_CONFIG_PATH}:${MOOSHIKA_PREFIX}/lib/pkgconfig") ENDIF() if(USE_NFS_RDMA OR USE_9P_RDMA) find_package(RDMA REQUIRED) include_directories(${RDMA_INCLUDE_DIR}) set(SYSTEM_LIBRARIES ${SYSTEM_LIBRARIES} ${RDMA_LIBRARY}) endif(USE_NFS_RDMA OR USE_9P_RDMA) if(USE_CB_SIMULATOR AND NOT USE_DBUS) message(WARNING "The callback simulator needs DBUS. Enabling DBUS") set(USE_DBUS ON) endif(USE_CB_SIMULATOR AND NOT USE_DBUS) if(USE_9P_RDMA AND NOT USE_9P) message(WARNING "The support of 9P/RDMA needs 9P protocol support. Enabling 9P") set(USE_9P ON) endif(USE_9P_RDMA AND NOT USE_9P) IF(ALLOCATOR) set(ALLOCATOR ${ALLOCATOR} CACHE STRING "memory allocator: jemalloc|tcmalloc|libc") ELSE() if( "${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER "2.6" ) set(ALLOCATOR "jemalloc" CACHE STRING "specify the memory allocator to use: jemalloc|tcmalloc|libc") else("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER "2.6" ) set(ALLOCATOR "libc" CACHE STRING "specify the memory allocator to use: jemalloc|tcmalloc|libc") endif( "${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER "2.6" ) ENDIF() # Find packages and libs we need for building include(CheckIncludeFiles) include(CheckLibraryExists) include(CheckCSourceCompiles) include(TestBigEndian) check_include_files(stdbool.h HAVE_STDBOOL_H) check_include_files(strings.h HAVE_STRINGS_H) check_include_files(string.h HAVE_STRING_H) if(HAVE_STRING_H AND HAVE_STRINGS_H) # we have all the libraries and include files to use string.h set(HAVE_STRNLEN ON) endif(HAVE_STRING_H AND HAVE_STRINGS_H) # PROXY handle mapping needs sqlite3 IF(PROXY_HANDLE_MAPPING) check_include_files(sqlite3.h HAVE_SQLITE3_H) check_library_exists( sqlite3 sqlite3_open "" HAVE_SQLITE3 ) if(NOT HAVE_SQLITE3 OR NOT HAVE_SQLITE3_H) message(WARNING "Cannot find sqlite3.h or the library. Disabling proxy handle mapping") set(PROXY_HANDLE_MAPPING OFF) endif(NOT HAVE_SQLITE3 OR NOT HAVE_SQLITE3_H) ENDIF(PROXY_HANDLE_MAPPING) IF(_VALGRIND_MEMCHECK) check_include_files(valgrind/memcheck.h HAVE_MEMCHECK_H) if(NOT HAVE_MEMCHECK_H) message(FATAL_ERROR "Cannot find valgrind/memcheck.h, install valgrind-devel package to enable _VALGRIND_MEMCHECK") ENDIF(NOT HAVE_MEMCHECK_H) ENDIF(_VALGRIND_MEMCHECK) # X_ATTRD requires the kernel to have xattrs...DBUS_STATS if(NOT _NO_XATTRD) check_include_files("unistd.h;sys/xattr.h" HAVE_XATTR_H) if(NOT HAVE_XATTR_H) message(WARNING "Cannot find xattr.h. Disabling XATTRD support: ${HAVE_XATTR_H}") set(_NO_XATTRD ON) endif(NOT HAVE_XATTR_H) endif(NOT _NO_XATTRD) TEST_BIG_ENDIAN(BIGENDIAN) if(${BIGENDIAN}) set(BIGEND ON) else() set(LITTLEEND ON) endif(${BIGENDIAN}) if( "${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER "2.6" ) find_package(Threads REQUIRED) endif( "${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_GREATER "2.6" ) if (USE_GSS) find_package(Krb5 REQUIRED gssapi) check_include_files(gssapi.h HAVE_GSSAPI_H) if (NOT HAVE_GSSAPI_H) # Debian/Ubuntu 12 magic set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I/usr/include/mit-krb5/") check_include_files(gssapi.h HAVE_GSSAPI_H) endif(NOT HAVE_GSSAPI_H) if(KRB5_FOUND AND HAVE_GSSAPI_H) set(HAVE_KRB5 ON) set(KRB5_VERSION 194) # hand code until we do krb5-config --version magic set(_HAVE_GSSAPI ON) else(KRB5_FOUND AND HAVE_GSSAPI_H) if (NOT KRB5_FOUND) message(FATAL_ERROR "Cannot find kerberos libraries") endif(NOT KRB5_FOUND) if (NOT HAVE_GSSAPI_H) message(FATAL_ERROR "Cannot find GSS libraries") endif (NOT HAVE_GSSAPI_H) endif(KRB5_FOUND AND HAVE_GSSAPI_H) endif(USE_GSS) if (USE_ADMIN_TOOLS) find_package(PythonInterp) if (NOT PYTHONINTERP_FOUND) message(FATAL_ERROR "Cannot find python for enabling admin tools") endif (NOT PYTHONINTERP_FOUND) if (USE_GUI_ADMIN_TOOLS) find_program(PYUIC NAMES pyuic4 DOC "PyQT UI-compiler executable") if (NOT PYUIC) message(STATUS "Cannot find PyQt4. Disabling GUI admin tools") set(USE_GUI_ADMIN_TOOLS OFF) endif (NOT PYUIC) endif (USE_GUI_ADMIN_TOOLS) endif (USE_ADMIN_TOOLS) if (USE_MAN_PAGE) find_program(SPHINX_BUILD sphinx-build) if(NOT SPHINX_BUILD) message(FATAL_ERROR "Can't find sphinx-build.") endif(NOT SPHINX_BUILD) endif(USE_MAN_PAGE) # Validate fsal dependencies if(USE_FSAL_GLUSTER) find_package(PkgConfig) IF(GLUSTER_PREFIX) set(ENV{PKG_CONFIG_PATH} "${PKG_CONFIG_PATH}:${GLUSTER_PREFIX}/lib/pkgconfig") ENDIF(GLUSTER_PREFIX) pkg_check_modules(GFAPI glusterfs-api>=7.3.10.7) if(NOT GFAPI_FOUND) if(STRICT_PACKAGE) message(FATAL_ERROR "STRICT PACKAGE: Cannot find GLUSTER GFAPI runtime. Disabling GLUSTER fsal build") else(STRICT_PACKAGE) message(WARNING "Cannot find GLUSTER GFAPI runtime. Disabling GLUSTER fsal build") set(USE_FSAL_GLUSTER OFF) endif(STRICT_PACKAGE) else(NOT GFAPI_FOUND) message(STATUS "GFAPI_INCLUDE_DIRS=${GFAPI_INCLUDE_DIRS}") message(STATUS "GFAPI_LIBRARY_DIRS=${GFAPI_LIBRARY_DIRS}") message(STATUS "GFAPI_LIBDIR=${GFAPI_LIBDIR}") include_directories(${GFAPI_INCLUDE_DIRS}) # missing directory not provided by current version of GlusterFS include_directories(${GFAPI_PREFIX}/include) link_directories (${GFAPI_LIBRARY_DIRS}) endif(NOT GFAPI_FOUND) if(USE_FSAL_GLUSTER) check_include_files("unistd.h;attr/xattr.h" HAVE_XATTR_H) if(NOT HAVE_XATTR_H) if(STRICT_PACKAGE) message(FATAL_ERROR "STRICT PACKAGE: Can not find attr/xattr.h, disabling GLUSTER fsal build") else(STRICT_PACKAGE) message(WARNING "Can not find attr/xattr.h, disabling GLUSTER fsal build") set(USE_FSAL_GLUSTER OFF) endif(STRICT_PACKAGE) endif(NOT HAVE_XATTR_H) check_include_files("acl/libacl.h" HAVE_ACL_H) if(HAVE_ACL_H) set(USE_POSIX_ACLS ON) else() set(USE_POSIX_ACLS OFF) set(USE_FSAL_GLUSTER OFF) message(STATUS "Could not find libacl, disabling GLUSTER fsal build") endif(HAVE_ACL_H) check_library_exists(gfapi glfs_xreaddirplus_r ${GFAPI_LIBDIR} HAVE_XREADDIRPLUS) if(HAVE_XREADDIRPLUS) set(USE_GLUSTER_XREADDIRPLUS ON) else() set(USE_GLUSTER_XREADDIRPLUS OFF) message(STATUS "Could not find glfs_xreaddirplus, switching to glfs_readdir_r") endif(HAVE_XREADDIRPLUS) check_library_exists(gfapi glfs_fd_set_lkowner ${GFAPI_LIBDIR} HAVE_LKOWNER) if(HAVE_LKOWNER) set(USE_LKOWNER ON) else() set(USE_LKOWNER OFF) set(USE_FSAL_GLUSTER OFF) message(STATUS "lkowner support is needed to enable GLUSTER build") endif(HAVE_LKOWNER) check_library_exists(gfapi glfs_upcall_register ${GFAPI_LIBDIR} HAVE_REGISTER_UPCALL) if(HAVE_REGISTER_UPCALL) set(USE_GLUSTER_UPCALL_REGISTER ON) else() set(USE_GLUSTER_UPCALL_REGISTER OFF) message(STATUS "Could not find glfs_upcall_register, switching to glfs_h_poll_upcall") endif(HAVE_REGISTER_UPCALL) endif(USE_FSAL_GLUSTER) endif(USE_FSAL_GLUSTER) if(USE_FSAL_CEPH) find_package(CephFS) if(NOT CEPHFS_FOUND) if(STRICT_PACKAGE) message(FATAL_ERROR "STRICT_PACKAGE : Cannot find CEPH runtime. Disabling CEPH fsal build") else(STRICT_PACKAGE) message(WARNING "Cannot find CEPH runtime. Disabling CEPH fsal build") set(USE_FSAL_CEPH OFF) endif(STRICT_PACKAGE) endif(NOT CEPHFS_FOUND) endif(USE_FSAL_CEPH) if(USE_FSAL_RGW) # require RGW w/API version 1.1.x find_package(RGW 1.1.6) if(NOT RGW_FOUND) if(STRICT_PACKAGE) message(FATAL_ERROR "STRICT_PACKAGE : Cannot find RGW runtime. Disabling RGW fsal build") else(STRICT_PACKAGE) message(WARNING "Cannot find supported RGW runtime. Disabling RGW fsal build") set(USE_FSAL_RGW OFF) endif(STRICT_PACKAGE) endif(NOT RGW_FOUND) endif(USE_FSAL_RGW) if(USE_FSAL_XFS) if(EXISTS /lib/libhandle.so) check_library_exists(handle "open_by_handle" "/./lib" HAVE_XFS_LIB) if(HAVE_XFS_LIB) set(PATH_LIBHANDLE "/lib/libhandle.so" CACHE INTERNAL "debian stretch and ubuntu xenial hack") endif(HAVE_XFS_LIB) else(EXISTS /lib/libhandle.so) check_library_exists(handle "open_by_handle" "" HAVE_XFS_LIB) endif(EXISTS /lib/libhandle.so) check_include_files("xfs/xfs.h" HAVE_XFS_H) if((NOT HAVE_XFS_LIB) OR (NOT HAVE_XFS_H)) if(STRICT_PACKAGE) message(FATAL_ERROR "STRICT_PACKAGE: Cannot find XFS runtime. Disabling XFS build") else(STRICT_PACKAGE) message(WARNING "Cannot find XFS runtime. Disabling XFS build") set(USE_FSAL_XFS OFF) endif(STRICT_PACKAGE) endif((NOT HAVE_XFS_LIB) OR (NOT HAVE_XFS_H)) endif(USE_FSAL_XFS) # sort out which allocator to use if(${ALLOCATOR} STREQUAL "jemalloc") find_package(JeMalloc) if(JEMALLOC_FOUND) set(SYSTEM_LIBRARIES ${JEMALLOC_LIBRARIES} ${SYSTEM_LIBRARIES}) else(JEMALLOC_FOUND) message(WARNING "jemalloc not found, falling back to libc") set(ALLOCATOR "libc") endif(JEMALLOC_FOUND) elseif(${ALLOCATOR} STREQUAL "tcmalloc") find_package(TcMalloc) if(TCMALLOC_FOUND) set(SYSTEM_LIBRARIES ${TCMALLOC_LIBRARIES} ${SYSTEM_LIBRARIES}) else(TCMALLOC_FOUND) message(WARNING "tcmalloc not found, falling back to libc") set(ALLOCATOR "libc") endif(TCMALLOC_FOUND) else() if(NOT ${ALLOCATOR} STREQUAL "libc") message(SEND_ERROR "${ALLOCATOR} is not a valid option. Valid allocators are: jemalloc|tcmalloc|libc") endif() endif() # Find optional libraries/packages if(USE_EFENCE) find_package(efence REQUIRED) set(SYSTEM_LIBRARIES ${efence_LIBRARIES} ${SYSTEM_LIBRARIES}) endif(USE_EFENCE) if(USE_DBUS) find_package(PkgConfig) pkg_check_modules(DBUS REQUIRED dbus-1) set(SYSTEM_LIBRARIES ${DBUS_LIBRARIES} ${SYSTEM_LIBRARIES}) LIST(APPEND CMAKE_LIBRARY_PATH ${DBUS_LIBRARY_DIRS}) link_directories (${DBUS_LIBRARY_DIRS}) endif(USE_DBUS) if(USE_NFSIDMAP) find_package(NfsIdmap) if(NFSIDMAP_FOUND) set(SYSTEM_LIBRARIES ${NFSIDMAP_LIBRARY} ${SYSTEM_LIBRARIES}) else(NFSIDMAP_FOUND) message(WARNING "libnfsidmap not found, disabling USE_NFSIDMAP") set(USE_NFSIDMAP OFF) endif(NFSIDMAP_FOUND) endif(USE_NFSIDMAP) if(USE_EXECINFO) find_package(ExecInfo REQUIRED) set(SYSTEM_LIBRARIES ${EXECINFO_LIBRARY} ${SYSTEM_LIBRARIES}) endif(USE_EXECINFO) if(USE_CUNIT) find_package(cunit REQUIRED) set(SYSTEM_LIBRARIES ${cunit_LIBRARIES} ${SYSTEM_LIBRARIES}) endif(USE_CUNIT) if(_MSPAC_SUPPORT) find_package(WBclient REQUIRED) if(WBCLIENT_FOUND AND WBCLIENT4_H) set(SYSTEM_LIBRARIES ${WBCLIENT_LIBRARIES} ${SYSTEM_LIBRARIES}) else(WBCLIENT_FOUND AND WBCLIENT4_H) message(WARNING "Samba 4 wbclient not found. Disabling MSPAC_SUPPORT") set(_MSPAC_SUPPORT OFF) endif(WBCLIENT_FOUND AND WBCLIENT4_H) endif(_MSPAC_SUPPORT) if(USE_LTTNG) # Set LTTNG_PATH_HINT on the command line # if your LTTng is not in a standard place find_package(LTTng) if(LTTNG_FOUND) include_directories(${LTTNG_INCLUDE_DIR}) else(LTTNG_FOUND) message(WARNING "LTTng libraries not found. Disabling USE_LTTNG") set(USE_LTTNG OFF) endif(LTTNG_FOUND) endif(USE_LTTNG) if(USE_RADOS_RECOV OR RADOS_URLS) find_package(RADOS) if(NOT RADOS_FOUND) if(STRICT_PACKAGE) message(FATAL_ERROR "STRICT_PACKAGE : Rados libraries not found.") else(STRICT_PACKAGE) message(WARNING "Rados libraries not found.") set(USE_RADOS_RECOV OFF) set(RADOS_URLS OFF) endif(STRICT_PACKAGE) endif(NOT RADOS_FOUND) endif(USE_RADOS_RECOV OR RADOS_URLS) # Cmake 2.6 has issue in managing BISON and FLEX if( "${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_LESS "2.8" ) message( status "CMake 2.6 detected, using portability hooks" ) set(CMAKE_CURRENT_LIST_DIR /usr/share/cmake/Modules ) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/portability_cmake_2.8 /usr/share/cmake/Modules ${CMAKE_MODULE_PATH}) endif( "${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" VERSION_LESS "2.8" ) include_directories( "${PROJECT_BINARY_DIR}/include" "${PROJECT_SOURCE_DIR}/include" "${OS_INCLUDE_DIR}" ) if (HAVE_KRB5) include_directories( "${KRB5_INCLUDE_DIRS}" ) endif (HAVE_KRB5) # Fixup loose bits of autotools legacy set(_USE_9P ${USE_9P}) set(_USE_9P_RDMA ${USE_9P_RDMA}) set(_USE_NFS3 ${USE_NFS3}) set(_USE_NLM ${USE_NLM}) if(USE_CB_SIMULATOR) set(_USE_CB_SIMULATOR ON) endif(USE_CB_SIMULATOR) ########### add a "make dist" and a "make rpm" ############### set( PKG_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}.tar.gz") add_custom_target(dist COMMAND ${CMAKE_MAKE_PROGRAM} package_source) # Find misc system libs find_library(LIBRT rt) # extended Pthreads functions # We need to have libcap installed find_library(LIBCAP cap) # Management of Capablilities check_library_exists( cap cap_set_proc "" HAVE_LIBCAP ) if(HAVE_LIBCAP) set(SYSTEM_LIBRARIES ${SYSTEM_LIBRARIES} ${LIBCAP}) set(USE_CAPS ON) else(HAVE_LIBCAP) set(USE_CAPS OFF) message(STATUS "Could not find capabilities library, disabling USE_CAPS") endif(HAVE_LIBCAP) # Check if we have libblkid and libuuid, will just be reported under one # flag USE_BLKID check_include_files("blkid/blkid.h" HAVE_LIBBLKID_H) find_library(LIBBLKID blkid) # Management of Capablilities check_library_exists( blkid blkid_devno_to_devname "" HAVE_LIBBLKID ) check_include_files("uuid/uuid.h" HAVE_LIBUUID_H) find_library(LIBUUID uuid) # Management of Capablilities check_library_exists( uuid uuid_parse "" HAVE_LIBUUID ) if(HAVE_LIBBLKID AND HAVE_LIBUUID AND HAVE_LIBBLKID_H AND HAVE_LIBUUID_H) # we have all the libraries and include files to use libblkid and libuuid set(SYSTEM_LIBRARIES ${SYSTEM_LIBRARIES} ${LIBBLKID} ${LIBUUID}) set(USE_BLKID ON) else(HAVE_LIBBLKID AND HAVE_LIBUUID AND HAVE_LIBBLKID_H AND HAVE_LIBUUID_H) # we are missing something and can't use libblkid and libuuid set(USE_BLKID OFF) if(NOT HAVE_LIBBLKID) message(STATUS "Could not find blkid library, disabling USE_BLKID") elseif(NOT HAVE_LIBUUID) message(STATUS "Could not find uuid library, disabling USE_BLKID") elseif(NOT HAVE_LIBBLKID_H) message(STATUS "Could not find blkid header files, disabling USE_BLKID") else(NOT HAVE_LIBBLKID) message(STATUS "Could not find uuid header files, disabling USE_BLKID") endif(NOT HAVE_LIBBLKID) endif(HAVE_LIBBLKID AND HAVE_LIBUUID AND HAVE_LIBBLKID_H AND HAVE_LIBUUID_H) # check is daemon exists # I use check_library_exists there to be portab;e check_library_exists( c daemon "" HAVE_DAEMON ) # Roll up required libraries #Protocols we support set(PROTOCOLS nfsproto rquota ) if(USE_NLM) set(PROTOCOLS ${PROTOCOLS} nlm) endif(USE_NLM) if(USE_9P) set(PROTOCOLS ${PROTOCOLS} 9p) endif(USE_9P) set(PROTOCOLS ${PROTOCOLS} nfs_mnt_xdr ) # Core subsystems set(GANESHA_CORE sal idmap avltree hashtable rpcal support nfs4callbacks cidr string_utils hash log uid2grp netgroup_cache fsalpseudo fsalmdcache ) if(USE_CACHE_INODE) set(GANESHA_CORE ${GANESHA_CORE} cache_inode) endif(USE_CACHE_INODE) if(USE_DBUS) set(GANESHA_CORE ${GANESHA_CORE} gshdbus) endif(USE_DBUS) if(USE_9P_RDMA) find_package(PkgConfig) IF(MOOSHIKA_PREFIX) set(ENV{PKG_CONFIG_PATH} "${PKG_CONFIG_PATH}:${MOOSHIKA_PREFIX}/lib/pkgconfig") ENDIF() pkg_check_modules(MOOSHIKA REQUIRED libmooshika>=0.6) # Remove rdma and ibverbs libraries that have already been found by FindRDMA # # TODO: this temporary workaround will no longer be required # with future mooshika versions (>1.0) # Original code is: set(GANESHA_CORE ${GANESHA_CORE} ${MOOSHIKA_LIBRARIES}) # set(MOOSHIKA_LIBS_EXCEPT_RDMA ${MOOSHIKA_LIBRARIES}) list(REMOVE_ITEM MOOSHIKA_LIBS_EXCEPT_RDMA rdmacm ibverbs) set(GANESHA_CORE ${GANESHA_CORE} ${MOOSHIKA_LIBS_EXCEPT_RDMA}) include_directories(${MOOSHIKA_INCLUDE_DIRS}) link_directories (${MOOSHIKA_LIBRARY_DIRS}) endif(USE_9P_RDMA) set(NTIRPC_MIN_VERSION 1.6.1) if (USE_SYSTEM_NTIRPC) find_package(NTIRPC ${NTIRPC_MIN_VERSION} REQUIRED) else (USE_SYSTEM_NTIRPC) # Set options for submodule set(USE_RPC_RDMA ${USE_NFS_RDMA} CACHE BOOL "Use RDMA") set(TIRPC_EPOLL ${TIRPC_EPOLL} CACHE BOOL "Use EPOLL") set(USE_GSS ${USE_GSS} CACHE BOOL "Use GSS") add_subdirectory(libntirpc) set(NTIRPC_LIBRARY ntirpc) set(NTIRPC_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/libntirpc/ntirpc/") message(STATUS "Using ntirpc submodule") endif (USE_SYSTEM_NTIRPC) message(${NTIRPC_INCLUDE_DIR}) include_directories(${NTIRPC_INCLUDE_DIR}) # All the plumbing in the basement set(SYSTEM_LIBRARIES ${SYSTEM_LIBRARIES} gos ${LIBDL} ${KRB5_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${LIBRT} ${NTIRPC_LIBRARY} ) # Config file; make sure it doesn't clobber an existing one include(${CMAKE_SOURCE_DIR}/cmake/modules/InstallPackageConfigFile.cmake) InstallPackageConfigFile(${CMAKE_SOURCE_DIR}/config_samples/ganesha.conf.example ${SYSCONFDIR}/ganesha ganesha.conf) # Sample config files if( ${DISTRO} MATCHES "SLES" ) install(DIRECTORY config_samples DESTINATION share/doc/packages/ganesha) else( ${DISTRO} MATCHES "SLES" ) install(DIRECTORY config_samples DESTINATION share/doc/ganesha) endif( ${DISTRO} MATCHES "SLES" ) # pre-create PREFIX/var/run/ganesha install(DIRECTORY DESTINATION ${SYSSTATEDIR}/run/ganesha) add_subdirectory(log) add_subdirectory(config_parsing) add_subdirectory(cidr) add_subdirectory(test) add_subdirectory(avl) add_subdirectory(hashtable) if(USE_CACHE_INODE) add_subdirectory(cache_inode) endif(USE_CACHE_INODE) add_subdirectory(SAL) add_subdirectory(RPCAL) add_subdirectory(Protocols) add_subdirectory(support) add_subdirectory(os) add_subdirectory(FSAL) add_subdirectory(idmapper) add_subdirectory(MainNFSD) add_subdirectory(tools) if(USE_GTEST) add_subdirectory(gtest) endif(USE_GTEST) if(USE_DBUS) add_subdirectory(dbus) endif(USE_DBUS) if(USE_LTTNG) add_subdirectory(tracing) endif(USE_LTTNG) add_subdirectory(scripts) add_subdirectory(doc) # display configuration vars message(STATUS) message(STATUS "-------------------------------------------------------") message(STATUS "PLATFORM = ${PLATFORM}") message(STATUS "ARCH = ${ARCH}") message(STATUS "VERSION = ${GANESHA_VERSION}") message(STATUS "BUILD HOST = ${BUILD_HOST_NAME}") message(STATUS "-------------------------------------------------------") message(STATUS "USE_FSAL_PROXY = ${USE_FSAL_PROXY}") message(STATUS "USE_FSAL_VFS = ${USE_FSAL_VFS}") message(STATUS "USE_FSAL_CEPH = ${USE_FSAL_CEPH}") message(STATUS "USE_FSAL_CEPH_MKNOD = ${USE_FSAL_CEPH_MKNOD}") message(STATUS "USE_FSAL_CEPH_SETLK = ${USE_FSAL_CEPH_SETLK}") message(STATUS "USE_FSAL_CEPH_LL_LOOKUP_ROOT = ${USE_FSAL_CEPH_LL_LOOKUP_ROOT}") message(STATUS "USE_FSAL_CEPH_STATX = ${USE_FSAL_CEPH_STATX}") message(STATUS "USE_FSAL_CEPH_LL_DELEGATION = ${USE_FSAL_CEPH_LL_DELEGATION}") message(STATUS "USE_FSAL_RGW = ${USE_FSAL_RGW}") message(STATUS "USE_FSAL_XFS = ${USE_FSAL_XFS}") message(STATUS "USE_FSAL_PANFS = ${USE_FSAL_PANFS}") message(STATUS "USE_FSAL_GPFS = ${USE_FSAL_GPFS}") message(STATUS "USE_FSAL_GLUSTER = ${USE_FSAL_GLUSTER}") message(STATUS "USE_FSAL_NULL = ${USE_FSAL_NULL}") message(STATUS "USE_FSAL_MEM = ${USE_FSAL_MEM}") message(STATUS "USE_SYSTEM_NTIRPC = ${USE_SYSTEM_NTIRPC}") message(STATUS "USE_DBUS = ${USE_DBUS}") message(STATUS "USE_CB_SIMULATOR = ${USE_CB_SIMULATOR}") message(STATUS "USE_NFSIDMAP = ${USE_NFSIDMAP}") message(STATUS "ENABLE_ERROR_INJECTION = ${ENABLE_ERROR_INJECTION}") message(STATUS "ENABLE_VFS_DEBUG_ACL = ${ENABLE_VFS_DEBUG_ACL}") message(STATUS "ENABLE_RFC_ACL = ${ENABLE_RFC_ACL}") message(STATUS "USE_CAPS = ${USE_CAPS}") message(STATUS "USE_BLKID = ${USE_BLKID}") message(STATUS "STRICT_PACKAGE = ${STRICT_PACKAGE}") message(STATUS "DISTNAME_HAS_GIT_DATA = ${DISTNAME_HAS_GIT_DATA}" ) message(STATUS "_MSPAC_SUPPORT = ${_MSPAC_SUPPORT}") message(STATUS "USE_EFENCE = ${USE_EFENCE}") message(STATUS "_NO_TCP_REGISTER = ${_NO_TCP_REGISTER}") message(STATUS "_NO_PORTMAPPER = ${_NO_PORTMAPPER}") message(STATUS "_NO_XATTRD = ${_NO_XATTRD}") message(STATUS "DEBUG_SAL = ${DEBUG_SAL}") message(STATUS "_VALGRIND_MEMCHECK = ${_VALGRIND_MEMCHECK}") message(STATUS "PROXY_HANDLE_MAPPING = ${PROXY_HANDLE_MAPPING}") message(STATUS "DEBUG_SYMS = ${DEBUG_SYMS}") message(STATUS "COVERAGE = ${COVERAGE}") message(STATUS "ENFORCE_GCC = ${ENFORCE_GCC}") message(STATUS "USE_GTEST = ${USE_GTEST}") message(STATUS "GTEST_PREFIX = ${GTEST_PREFIX}") message(STATUS "GTEST_MAIN = ${GTEST_MAIN}") message(STATUS "PROFILING = ${PROFILING}") message(STATUS "USE_GSS = ${USE_GSS}") message(STATUS "TIRPC_EPOLL = ${TIRPC_EPOLL}") message(STATUS "USE_9P = ${USE_9P}") message(STATUS "_USE_9P = ${_USE_9P}") message(STATUS "_USE_9P_RDMA = ${_USE_9P_RDMA}") message(STATUS "USE_NFS_RDMA = ${USE_NFS_RDMA}") message(STATUS "USE_NFS3 = ${USE_NFS3}") message(STATUS "USE_NLM = ${USE_NLM}") message(STATUS "KRB5_PREFIX = ${KRB5_PREFIX}") message(STATUS "CEPH_PREFIX = ${CEPH_PREFIX}") message(STATUS "RGW_PREFIX = ${RGW_PREFIX}") message(STATUS "GLUSTER_PREFIX = ${GLUSTER_PREFIX}") message(STATUS "CMAKE_PREFIX_PATH = ${CMAKE_PREFIX_PATH}") message(STATUS "_GIT_HEAD_COMMIT = ${_GIT_HEAD_COMMIT}") message(STATUS "_GIT_HEAD_COMMIT_ABBREV = ${_GIT_HEAD_COMMIT_ABBREV}") message(STATUS "_GIT_DESCRIBE = ${_GIT_DESCRIBE}") message(STATUS "ALLOCATOR = ${ALLOCATOR}") message(STATUS "GOLD_LINKER = ${GOLD_LINKER}") message(STATUS "CMAKE_INSTALL_PREFIX = ${CMAKE_INSTALL_PREFIX}") message(STATUS "FSAL_DESTINATION = ${FSAL_DESTINATION}") message(STATUS "USE_ADMIN_TOOLS = ${USE_ADMIN_TOOLS}") message(STATUS "USE_GUI_ADMIN_TOOLS = ${USE_GUI_ADMIN_TOOLS}") message(STATUS "MODULES_PATH = ${MODULES_PATH}") message(STATUS "USE_TSAN = ${USE_TSAN}") message(STATUS "USE_LTTNG = ${USE_LTTNG}") message(STATUS "USE_BLKIN = ${USE_BLKIN}") message(STATUS "USE_VSOCK = ${USE_VSOCK}") message(STATUS "USE_TOOL_MULTILOCK = ${USE_TOOL_MULTILOCK}") message(STATUS "USE_MAN_PAGE = ${USE_MAN_PAGE}") message(STATUS "USE_RADOS_RECOV = ${USE_RADOS_RECOV}") message(STATUS "RADOS_URLS = ${RADOS_URLS}") #force command line options to be stored in cache set(USE_FSAL_VFS ${USE_FSAL_VFS} CACHE BOOL "build VFS FSAL shared library" FORCE) set(USE_FSAL_PROXY ${USE_FSAL_PROXY} CACHE BOOL "build PROXY FSAL shared library" FORCE) set(USE_FSAL_CEPH ${USE_FSAL_CEPH} CACHE BOOL "build CEPH FSAL shared library" FORCE) set(USE_FSAL_RGW ${USE_FSAL_RGW} CACHE BOOL "build RGW FSAL shared library" FORCE) set(USE_FSAL_XFS ${USE_FSAL_XFS} CACHE BOOL "build XFS FSAL" FORCE) set(USE_FSAL_PANFS ${USE_FSAL_PANFS} CACHE BOOL "build PanFS FSAL" FORCE) set(USE_FSAL_GPFS ${USE_FSAL_GPFS} CACHE BOOL "build GPFS FSAL" FORCE) set(USE_FSAL_GLUSTER ${USE_FSAL_GLUSTER} CACHE BOOL "build GLUSTER FSAL" FORCE) set(USE_FSAL_MEM ${USE_FSAL_MEM} CACHE BOOL "build GLUSTER FSAL" FORCE) set(USE_DBUS ${USE_DBUS} CACHE BOOL "enable DBUS protocol support" FORCE) set(USE_CB_SIMULATOR ${USE_CB_SIMULATOR} CACHE BOOL "enable callback simulator thread" FORCE) set(USE_NFSIDMAP ${USE_NFSIDMAP} CACHE BOOL "Use of libnfsidmap for name resolution" FORCE) set(DEBUG_SAL ${DEBUG_SAL} CACHE BOOL "enable debug SAL" FORCE) set(_VALGRIND_MEMCHECK ${_VALGRIND_MEMCHECK} CACHE BOOL "Initialize buffers passed to GPFS ioctl" FORCE) set(ENABLE_ERROR_INJECTION ${ENABLE_ERROR_INJECTION} CACHE BOOL "enable error injection" FORCE) set(ENABLE_VFS_DEBUG_ACL ${ENABLE_VFS_DEBUG_ACL} CACHE BOOL "Enable debug ACL store for VFS" FORCE) set(ENABLE_RFC_ACL ${ENABLE_RFC_ACL} CACHE BOOL "Enable debug ACL store for VFS" FORCE) set(_MSPAC_SUPPORT ${_MSPAC_SUPPORT} CACHE BOOL "enable mspac winbind support" FORCE) set(STRICT_PACKAGE ${STRICT_PACKAGE} CACHE BOOL "enable strict packaging behavior" FORCE) set( DISTNAME_HAS_GIT_DATA ${DISTNAME_HAS_GIT_DATA} CACHE BOOL "Distribution package's name carries git data" FORCE) set(USE_9P ${USE_9P} CACHE BOOL "enable 9P support" FORCE) set(_USE_9P ${_USE_9P} CACHE BOOL "enable 9P support in config" FORCE) set(_USE_9P_RDMA ${_USE_9P_RDMA} CACHE BOOL "enable 9P_RDMA support" FORCE) set(USE_NFS3 ${USE_NFS3} CACHE BOOL "enable NFSv3 support" FORCE) set(USE_NLM ${USE_NLM} CACHE BOOL "enable NLM support" FORCE) set(USE_ADMIN_TOOLS ${USE_ADMIN_TOOLS} CACHE BOOL "Package Admin Scripts" FORCE) set(USE_GUI_ADMIN_TOOLS ${USE_GUI_ADMIN_TOOLS} CACHE BOOL "Package GUI Admin Scripts" FORCE) set(USE_TSAN ${USE_TSAN} CACHE BOOL "Enable GCC Thread-Sanitizer" FORCE) set(USE_LTTNG ${USE_LTTNG} CACHE BOOL "Enable LTTng tracing" FORCE) set(USE_NFS_RDMA ${USE_NFS_RDMA} CACHE BOOL "enable nfs RDMA" FORCE) set(_USE_NFS_RDMA ${USE_NFS_RDMA} CACHE BOOL "enable nfs RDMA in config" FORCE) set(USE_RADOS_RECOV ${USE_RADOS_RECOV} CACHE BOOL "enable rados recovery" FORCE) set(RADOS_URLS ${RADOS_URLS} CACHE BOOL "enable rados url provider" FORCE) # Now create a useable config.h configure_file( "${PROJECT_SOURCE_DIR}/include/config-h.in.cmake" "${PROJECT_BINARY_DIR}/include/config.h" ) # Tweak the "%bcond_ in the specfile for every # optional feature. Take care on the logic of this syntax # %bcond_with means you add a "--with" option, default is "without this feature" # %bcond_without adds a"--without" so the feature is enabled by default # This has to be coherent with chosen FSALs if(USE_FSAL_XFS) set(BCOND_XFS "%bcond_without") else(USE_FSAL_XFS) set(BCOND_XFS "%bcond_with") endif(USE_FSAL_XFS) if(USE_FSAL_PANFS) set(BCOND_PANFS "%bcond_without") else(USE_FSAL_PANFS) set(BCOND_PANFS "%bcond_with") endif(USE_FSAL_PANFS) if(USE_FSAL_GPFS) set(BCOND_GPFS "%bcond_without") else(USE_FSAL_GPFS) set(BCOND_GPFS "%bcond_with") endif(USE_FSAL_GPFS) if(USE_FSAL_CEPH) set(BCOND_CEPH "%bcond_without") else(USE_FSAL_CEPH) set(BCOND_CEPH "%bcond_with") endif(USE_FSAL_CEPH) if(USE_FSAL_RGW) set(BCOND_RGW "%bcond_without") else(USE_FSAL_RGW) set(BCOND_RGW "%bcond_with") endif(USE_FSAL_RGW) if(USE_FSAL_GLUSTER) set(BCOND_GLUSTER "%bcond_without") else(USE_FSAL_GLUSTER) set(BCOND_GLUSTER "%bcond_with") endif(USE_FSAL_GLUSTER) if(USE_FSAL_NULL) set(BCOND_NULLFS "%bcond_without") else(USE_FSAL_NULL) set(BCOND_NULLFS "%bcond_with") endif(USE_FSAL_NULL) if(USE_FSAL_MEM) set(BCOND_MEM "%bcond_without") else(USE_FSAL_MEM) set(BCOND_MEM "%bcond_with") endif(USE_FSAL_MEM) if(USE_9P_RDMA) set(BCOND_RDMA "%bcond_without") else(USE_9P_RDMA) set(BCOND_RDMA "%bcond_with") endif(USE_9P_RDMA) if(USE_LTTNG) set(BCOND_LTTNG "%bcond_without") else(USE_LTTNG) set(BCOND_LTTNG "%bcond_with") endif(USE_LTTNG) if(${ALLOCATOR} STREQUAL "jemalloc") set(BCOND_JEMALLOC "%bcond_without") else(${ALLOCATOR} STREQUAL "jemalloc") set(BCOND_JEMALLOC "%bcond_with") endif(${ALLOCATOR} STREQUAL "jemalloc") if(USE_ADMIN_TOOLS) set(BCOND_UTILS "%bcond_without") else(USE_ADMIN_TOOLS) set(BCOND_UTILS "%bcond_with") endif(USE_ADMIN_TOOLS) if(USE_GUI_ADMIN_TOOLS) set(BCOND_GUI_UTILS "%bcond_without") else(USE_GUI_ADMIN_TOOLS) set(BCOND_GUI_UTILS "%bcond_with") endif(USE_GUI_ADMIN_TOOLS) if (USE_SYSTEM_NTIRPC) set(BCOND_NTIRPC "%bcond_without") else(USE_SYSTEM_NTIRPC) set(BCOND_NTIRPC "%bcond_with") endif(USE_SYSTEM_NTIRPC) if (USE_MAN_PAGE) set(BCOND_MAN_PAGE "%bcond_without") else(USE_MAN_PAGE) set(BCOND_MAN_PAGE "%bcond_with") endif(USE_MAN_PAGE) if(USE_RADOS_RECOV) set(BCOND_RADOS_RECOV "%bcond_without") else(USE_RADOS_RECOV) set(BCOND_RADOS_RECOV "%bcond_with") endif(USE_RADOS_RECOV) if(RADOS_URLS) set(BCOND_RADOS_URLS "%bcond_without") else(RADOS_URLS) set(BCOND_RADOS_URLS "%bcond_with") endif(RADOS_URLS) # Now create a useable specfile configure_file( "${PROJECT_SOURCE_DIR}/nfs-ganesha.spec-in.cmake" "${PROJECT_SOURCE_DIR}/nfs-ganesha.spec" ) configure_file( "${PROJECT_SOURCE_DIR}/scripts/systemd/nfs-ganesha-config.service-in.cmake" "${PROJECT_SOURCE_DIR}/scripts/systemd/nfs-ganesha-config.service" ) add_custom_target( rpm DEPENDS dist) add_custom_command(TARGET rpm COMMAND sh -c "rpmbuild -ta ${PKG_NAME}" VERBATIM DEPENDS dist) set(RPMDEST "--define '_srcrpmdir ${CMAKE_CURRENT_BINARY_DIR}'") add_custom_target( srpm DEPENDS dist) add_custom_command(TARGET srpm COMMAND sh -c "rpmbuild ${RPMDEST} -ts ${PKG_NAME}" VERBATIM DEPENDS dist) # Make a docker image set(DOCKER_TMP_INSTALL_PATH "${PROJECT_BINARY_DIR}/docker/root") find_package(LSB) if(LSB_RELEASE_EXECUTABLE) string(TOLOWER ${LSB_RELEASE_ID_SHORT} DOCKER_DISTRO) set(DOCKER_DISTRO_VERSION "${LSB_RELEASE_RELEASE_SHORT}") configure_file( "${PROJECT_SOURCE_DIR}/scripts/docker/entrypoint.sh-in.cmake" "${PROJECT_BINARY_DIR}/docker/entrypoint.sh" @ONLY ) configure_file( "${PROJECT_SOURCE_DIR}/scripts/docker/Dockerfile-in.cmake" "${PROJECT_BINARY_DIR}/docker/Dockerfile" @ONLY ) add_custom_target(docker COMMAND sh -c "make DESTDIR=${PROJECT_BINARY_DIR}/docker/root install" COMMAND sh -c "docker build -t ganesha/dev ${PROJECT_BINARY_DIR}/docker" VERBATIM ) add_dependencies(docker ganesha.nfsd ) endif(LSB_RELEASE_EXECUTABLE) if(COVERAGE) find_program(LCOV_EXEC lcov) find_program(GENHTML_EXEC genhtml) if(LCOV_EXEC AND GENHTML_EXEC) add_custom_target(lcov) add_custom_command(TARGET lcov COMMAND ${LCOV_EXEC} --capture --directory . --output-file coverage.info COMMAND ${GENHTML_EXEC} coverage.info --output-directory ./coverage_html/ VERBATIM WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) endif(LCOV_EXEC AND GENHTML_EXEC) endif(COVERAGE) ########### add a "make doc" target to call Doxygen find_package(Doxygen) if(DOXYGEN_FOUND) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) add_custom_target(doc ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) endif(DOXYGEN_FOUND) # Include thread-sanitizer module include(${CMAKE_SOURCE_DIR}/cmake/tsan.cmake) nfs-ganesha-2.6.0/src/COMPILING_HOWTO.txt000066400000000000000000000135761324272410200175530ustar00rootroot00000000000000 Building nfs-ganesha From Source ================================ This HOWTO covers what is necessary to download, configure, and build the nfs-ganesha NFS server software. There are two major components to the software, nfs-ganesha itself, and the libntirpc library which is used for the transport layer. The server and the libntirpc library are in separate git repositories. For the build, the libntirpc source is a 'git submodule' of nfs-ganesha. The first step in building is to download (clone) the nfs-ganesha repository from its official location on github.com into the place in my home directory where I keep my git trees. $ cd ~/git $ git clone --recursive git://github.com/nfs-ganesha.git Note the '--recursive' option which clones and initializes the submodule for libntirpc. If you do not use this option, you will have to use the git submodule command below before building. If you forget, the 'cmake' step below will terminate with an error message reminding you that you forgot. The git submodule ----------------- If you are building from a release source archive rather than from git, skip to the next section. However, if you are building from git, you must initialize the submodule after clone and after pulling a new update by going to the root of your repository and typing git submodule update --init Also, please note that the current HEAD of the ntirpc directory is a piece of tracked state. Please do not commit a change to the state unintentionally. NOTE: The submodule can get out of step with the main project. This can occur if you make a change to the nfs-ganesha working directory such as doing a checkout of another branch or a rebase of your work from an upstream update. In these cases you must do a 'git update' to make sure you are building nfs-ganesha with the correct version of libntirpc. CMAKE Build Instructions ------------------------ Cmake can and does prefer to build out- of-source. In other words, your build tree is over here and your git source tree is over there. The Makefiles are created by Cmake in the build tree, the objects and targets are in the build tree but the source is referenced "over there". For example, in a Ganesha build, we would do: $ cd some-build-sandbox $ rm -rf build_dir; mkdir build_dir $ cd build_dir $ cmake ~/git/nfs-ganesha/src $ make $ make install This gets the build completely away from where the git repo is. Note that I have thoroughly scrubbed the area before doing the build. You can also build in-tree but this litters the git repo with extra files just like autotools. See the Cmake manual for the details and restrictions. Building is a two step process. You first run Cmake to configure the build and then you do a conventional Make. You can do iterative development by editing files, including Cmake files (CMakeLists.txt) in the source tree and go back to the build directory and do a "make". The makefile will do the right thing and re-run Cmake if you messed with configuration files. Your configuration and build parameters are preserved in the build tree so you only have to do the full configuration step once. Unlike autotools where the build and source are in the same tree, having a separate build area allows you to do a couple of thing safely. * You can delete the whole build tree at any time. Simply repeat the configuration step and you get it all back. Your source is safely somewhere else. Be aware of which window/terminal you are in before doing an "rm -rf" however. Yes, I did that once so now I have the windows on separate monitors... * You can easily build multiple configurations. Simply create one build directory, enter it, and run Cmake with one set of parameters. Repeat in another build directory with a different set of parameters. Nice. System-wide NTIRPC ------------------ libntirpc may be installed separately on the system, and ganesha build with the -DUSE_SYSTEM_NTIRPC option. This does not require the submodule, and will not build ntirpc. Configuration Tweaking ---------------------- Cmake allows the setting of configuration parameters from the command line. You would use this in a similar way to how autotools works. You can discover what you can tweak by doing the following: $ mkdir tweaktest; cd tweaktest $ cmake -i ~/git/nfs-ganesha/src This will enter you into a "wizard" configuration (no fancy GUI stuff). Simply step through the configuration and note what knobs and switches are available and what their defaults are. After this, you can explicitly change parameters from the command line. For example: $ mkdir mybuild; cd mybuild $ cmake -D_USE_9P=OFF -D_HANDLE_MAPPING=ON -DALLOCATOR=tcmalloc \ ~/git/nfs-ganesha/src This will disable a 9P build, use handle mapping in the PROXY fsal and pick the tcmalloc allocator. There are two other variables of interest: CMAKE_BUILD_TYPE This is a global setting for the type of build. See the Cmake documentation for what this means. I have added a "Maintainer" type which forces strict compiles. It is what I intend to use on builds. BUILD_CONFIG This setting triggers the loading of a file in src/cmake/build_configurations. This is useful for having a "canned" configuration. There is only one file currently in use which will turn on every option available. Put these together and you get the build I use for merge testing: $ cmake -DCMAKE_BUILD_TYPE=Maintainer -DBUILD_CONFIG=everything \ ~/git/nfs-ganesha/src Look at src/cmake/build_configuration/everything.cmake to see what this turns on. If you want a custom, for say just a 9P server or only some features, create a file on the model of everything.cmake in that directory and then reference it on the command line. This eliminates the various shell scripts we have laying around... I stole this from the Mysql build where they use this trick to have things like 'redhat.cmake' and 'debian.cmake'. nfs-ganesha-2.6.0/src/CONTRIBUTING_HOWTO.txt000066400000000000000000000103171324272410200201270ustar00rootroot00000000000000Contributing and Using Git Paul Sheer - 18 October 2012 Dominique Martinet - 2015-02-26 Frank Filz - 2015-08-12 If you would like to contribute to nfs-ganesha development: There is a process to dumping the source (using git), modifying the source, and pushing your changes back. This process is quite simple and requires only a few commands. These are as follows: Establish from the other developers which branch of whose repository is the best to work in. Create an account on github.com, login and view that developers repository. Click "Fork" on the top right to create a copy of that repository. Let's say your name is "Paul Sheer" (replace "paul", "p_sheer" and "paulsheer" with your own name below), and say the the developer who owns the repository is named Code Leader and his github.com login is "codeleader". Now let's say the code branch is "new-unstable-dev-project". First you need to fetch the code you wish to work on: git clone git://github.com/codeleader/nfs-ganesha.git --branch new-unstable-dev-project cd nfs-ganesha The current (as of 2015-08-12) branch for development is: git clone git://github.com/nfs-ganesha/nfs-ganesha.git --branch next Now check what you have: cd nfs-ganesha git status Now visit COMPILING_HOWTO.txt for instructions on building and running. Now fetch the latest updates: git remote update You will also need to pull in the libntirpc submodule: git submodule update --init We also have several commit hooks, please install them: src/scripts/git_hooks/install_git_hooks.sh Install the Gerrithub change id commit hook scp -p -P 29418 USERNAMEHERE@review.gerrithub.io:hooks/commit-msg .git/hooks/ You may also want to do the following so you don't have to always provide your identity: git config user.name "Your Real Name" git config user.email "you@some.place" You can use the --global option to set these for all your git repos. Now do your development work. When you are developing, testing etc.: git commit --signoff -a There may be updates from other developers. Update to their new branch "more-stable-dev-project" to include other work other people may have done: git rebase origin/more-stable-dev-project Then push your work up to your github as follows: ssh-keygen cat ~/.ssh/id_rsa.pub Copy the output into your ssh keys in your github.com account. Pushing to your github is optional, but does provide some backup for your work. To push to the project, we now use Gerrithub for code submission and review. First, when creating a gerrithub account (first time sign-in), do NOT copy your ganesha repo unless you plan to receive changes from someone else. It makes NO sense to push changes to your own repo (except for the gatekeeper) Now you have an account, you want to push some patch for us to review to ffilz/nfs-ganesha - you need to start by adding a new remote. You have a list of targets (anonymous http, ssh, http) to add on the project page: https://review.gerrithub.io/#/admin/projects/ffilz/nfs-ganesha If your network allows it, ssh is easier. http needs you to setup a generated password (settings page, "HTTP Password" menu on the left) Also make sure you use https (not http) so taking an example: git remote add gerrit ssh://USERNAMEHERE@review.gerrithub.io:29418/ffilz/nfs-ganesha git fetch gerrit git log gerrit/next..HEAD This should ONLY list the commits you want to push! Also, all should have a Change-Id. Finally push your patches to Gerrithub: git push gerrit HEAD:refs/for/next That's it. If you edit a commit, just push it again, gerrit will notice it has the same Change-Id and update your change with a new patch set. Please make sure you don't change the Change-Id if you update the patch otherwise this creates disconnected review. If you want specific people to review your code, please go to Gerrithub and add them. Please note that you can only push changes to another repo that you wrote, gerrithub will check the mail address you're using and ask you to add any different mail address to your profile (settings -> Contact information). Well, just read the text if you get an error, it's usually clear enough :) nfs-ganesha-2.6.0/src/ChangeLog000066400000000000000000000763661324272410200162710ustar00rootroot000000000000000.99.39 - Mon 21 Jul 2008 - Initial version submitted to sourceforge 0.99.40 - Mon 28 Jul 2008 - Fixed several memory issues in NFSv4 implementation - Fixed several memory issues in FSAL_PROXY - Fixed a badly placed Mem_Free in support/nfs_state_id.c (function nfs4_State_Del) - Added Handle Mapping feature in FSAL_PROXY which makes it possible to export back in NFSv2 and NFSv3 from a proxyfied server accessed via NFSv4 0.99.41 - Mon 18 Aug 2008 - Fixed nfs4_op_access bug due to bad interpretation of the RFC for OP_ACCESS4 - Added extended features in BuddyMalloc module to enable extended memory leak tracking (run the ./configure script with the following options : "--enable-debug-memleaks --disable-block-prealloc") - Fixed a bug in FSAL_PROXY that made every user have root permissions in a few situations - Bug fixed: bad offset management in FSAL_read/FSAL_write for FSAL_PROXY. This made the datacache behaves weirdly. - Use All-0 stateid for r/w operations made for maintaining the data cache coherent with the "proxyfied" server with FSAL_PROXY. Due to this the parameter NFSv4_Proxy::Open_by_FH_Working_Dir is no more required, making the configuration of nfs-ganesha as a proxy much easier - FSAL_PROXY now supports RPCSEC_GSS authentication 0.99.42 - Mon 29 Sep 2008 - Added xattr support in NFSv4. For object "foo" a ghost directory name ".xattr.d.foo" is used to access extended attributes - Added xattr ghost directory and ghost objects for NFSv3. These "extended attributes" are read-only for the moment. These two new features may be disable if "--disable-xattr-directory" is used at "./configure" time - Enhancement in FSAL_GetXattrs function (file FSAL/FSAL_*/fsal_xattrs.c) for making integration to xattr.d easier 0.99.43 - Fri 10 Oct 2008 - Fixed RPCSEC_GSS support. It is now possible to mount with krb5 authentication. Security tuple krb5, krb5i and krb5p are supported - Add RPCSEC_GSS specifc feature for NFSv4 - Add RPCSEC_GSS specifc feature for NFSv3 - Configuration file supports "nodesets" syntax( the set "node1,node3,node4,node5,node6" can be summarized as "node[1,3,4-6]). This feature is very useful if your client is a cluster. - New RPM packaging. - The libganeshaNFS that provide NFS exports for FUSE ready product is now available as both static and shared libraries. 0.99.44 - Mon 20 Oct 2008 - NFS4_OP_OPEN default behaviour is now to ask the client to confirm the open using NFS5_OP_OPEN_CONFIRM. The default behaviour was formerly the opposite. - A bug in NFS4_OP_OPEN : same open_owner could have multiple states on the same file in certain cases - Many bug fixed in NFS4_OP_LOCK - libganeshaNFS (produced when FSAL_FUSELIKE is chosen) can now be managed by pkgconfig (a ".pc" file is added to the install target) - Improved compatibility for old FUSE filesystems that implement getdir instead of readdir. - FUSE-like binding now support filesystems with no inode numbers. - TIRPC can be use as alternate RPC layer. In later releases, this feature will be used to support IPv6 with NFS-GANESHA. - Added a 'init' scripts to be place in /etc/init.d - Added stuff to specfile so that it installs the .vim files 0.99.45 - Wed 5 Nov 2008 - Add IPv6 support via TIRPC support - Add features to configuration file parsing for IPv6 address - Bug fix in handle mapping module for FSAL_PROXY - Bug Fix: Bad verifier management in FSAL_PROXY's cleintid negociation - Bug fix: bad management of uid=0 is uidgidmap cache when using RPCSEC_GSS authentication. - Bug fix: several bug fixed in .xattr.d management for NFSv3 0.99.46 - Wed 18 Nov 2008 - Bug fix: possible deadlock may occur in worker thread when using tcp clients - Minor feature: MD cache now uses home made RW_Lock (the same that Hashtable module already uses). Call rw_lock_downgrade was added to RW_Lock module - DataCache GC is done by an external call to the ganesha service started in "syncers" of "flushers" mode. 0.99.47 - Thu 4 Dec 2008 - Running the Spec benchmark on nfs-ganesha showed a bug in attributes asked at creation time in NFSPROC3_CREATE, this has been fixed - Fix non-compliancy (thanks again to Spec NFS) in NFSPROC3_SYMLINK (attributes provided as arguments were never used). - Code cleanup: removed structure related to deprecated way of performing datacache's garbagge collection. - Major feature added : skeleton and "basements" added for MFSL, a new module that goes between md cache and FSAL - Removed direct Cache_Inode_Async, md writeback will be provided in a future module MFSL_ASYNC in MFSL directory 0.99.48 - Mon 15 Dec 2008 - Renamed yyparse and yylex functions so that GANESHA/FUSE would no more interfer with FUSE modules that uses lex/yacc parsing - FSAL_PROXY was ported to MacOS X (Darwin 9.5.0) - FSAL_FUSE was ported to MacOS X (Darwin 9.5.0). This allow user space libs with fuse binding to be used from MacOS through NFS-GANESHA - FSAL_SNMP has been ported to MacOS X (Darwin 9.5.0) - FSAL_POSIX has been ported to MacOS X (Darwin 9.5.0) 0.99.49 - Fri 9 Jan 2009 - FSAL_PROXY has been ported to FreeBSD 7.0 - FSAL_FUSE has been ported to FreeBSD 7.0 (FUSE ready product can now export in NFS via NFS-GANESHA) - FSAL_POSIX has been ported to FreeBSD 7.0 - FSAL_SNMP has been ported to FreeBSD 7.0 - Code has been ported and validated to ia64 architecture for all FSALs 0.99.50 - Fri 23 Jan 2009 - Code has been validated on PPC/Linux architecture - Fixed a Makefile.am that prevent from compiling with FSAL_FUSE and gssrpc activated - Added a 'debian' directory in order to produce debian packages - FSAL_POSIX has been ported to OpenSolaris (alpha) - FSAL_FUSE has been ported to OpenSolaris (beta) - FSAL_PROXY has been ported to OpenSolaris (alpha) - FSAL_SNMP has been ported to OpenSolaris (alpha) - Bug fix: client's address was badly printed by the worker when nfs_export_check_access denied access - Bug fix: syntax error in posix.ganesha.nfsd.conf configuration file example 0.99.51 - Fri 20 Mar 2009 - MySQL can be used instead of PGSQL in FSAL_POSIX. It becomes the default database for this FSAL. - Bug Fix: Massive and parralel mount requests (e.g. : all nodes of a compute cluster starting at the same time after a maintenance) could lead to errors on client. This "denial of service" like trouble has been fixed. - Bug Fix: bad computation in FH checksum when compiled on x86_64 architecture - MFSL_ASYNC: setattr has been made asynchronous - MFSL_ASYNC: link has been made asynchronous - MFSL_ASYNC: unlink has been made asynchronous - MFSL_ASYNC: rename has been made asynchronous - MFSL_ASYNC: truncate has been made asynchronous - MFSL_ASYNC: mkdir has been made asynchronous - MFSL_ASYNC: create has been made asynchronous - New feature: very early alpha version of asynchronous metadata management is available via MFSL_ASYNC This is to be activated by using --with-mfsl=ASYNC at configure time. 0.99.52 - Wed 8 Apr 2009 - Bug Fix: possible race condition in cache_inode_readdir (badly placed rw_lock_downgrade) that could lead in having cache_inode_readdir_populate called twice at the same time. - MFSL_ASYNC: now, preallocated entries are owned by each - Bug Fix: bad MFSL_Context management in ganeshell - Bug Fix: deleted entry can no more be lookuped and accessed before their real deletion. - Lustre v2 FSAL Beta - Fixed bug in RW_lock_downgrade function - Fixed weak locking in cache_inode_remove - I/O optimization and fixes - Improved fd cache management - Tweak fixes in logs - A lot of debug messages now only appear for DEBUG and FULL_DEBUG log levels 0.99.53 - Wed 20 May 2009 - Bug Fix in the non-regression test suite (badly managed configuration file) - Bug Fix: in MFSL_ASYNC, an entry is not looked up by cache_inode layer if its parent is asynchronous, this will prevent from an encountered incoherency (this issue was discovered with cthon's basic test3) - Bug Fix: in MFSL_ASYNC, numlinks was badly managed in MFSL_unlink, files with numlinks >1 were deleted by MFSL_unlink - Bug Fix: in MFSL_ASYNC, symbolic links can't be proceeded asynchronously. Mechanism were added to bypass the asynchronous management for this specific case - MFSL_ASYNC: MFSL_create and MFSL_mkdir now use correctly the credentials provided by the caller - FSAL_POSIX uses pread/pwrite instead of fseek+fwrite/fseek+fread - NFSv4 implementation now supports NFSv4 referrals 0.99.54 - Mon 22 Jun 2009 - Bug Fixed in NFSV4 lock management : NFS4ERR_OPENMODE was not returned in case a WRITE lock was requested on a read-only file - Bug fixed in NFSv4 lock management : previously know lockowner where badly managed. - Bug fixed in NFSv4 lock management : NFS4ERR_OLD_STATEID was returned instead of NFS4ERR_BAD_STATEID in severals cases - Update nfs4_op_open's behaviour related to NFS4ERR_SHARE_DENIED - Update nfs4_op_write and nfs4_op_read behavior regardings locks and share reservations. - OP4_LOCK/OP4_LOCKU bad behavior fixed - add a missing relation between lock-stateids and related open-stateids for managing seqid at state transition - better management of OPEN4 with EXCLIVE4 repeated with same verifier - Return NFS4ERR_LOCKS_HELD when closing a file with held locks - all-one stateid is correctly managed in OP4_WRITE and OP4_READ - Minor bug fixed in nfs4_op_open_confirm (errors when bad arguments are used) - bad seqid management in nfs4_op_close - Fix bug in the use of ACCES4resok::supported flag - MFSL_ASYNC now has startup routines to clean preallocated stuff left by older instance of the server - Bug Fix in OP_OPEN4 : if the file already existed as an OPEN_CREATE operation was made with the UNCHECKED mode, the parent directory remain as the cfh as the operation returned NFS4_OK, leading to lots of mess about the file. 0.99.55 -Tue 30 Jun 2009 - Stupid bug fix in nfs4_op_open.c (allocating a zero length buffer...) - Bug fix in OP_LOCKU4 implementation : no stateid was returned - cache_inode_setattr was modified so that cache_inode_setattr can set the size of a file (by calling FSAL_setattr). This is required for a correct implementation of OP_OPEN4 - Lots of problems were met when implementing NFSv4 state mechanism and work is still in progress. To provide users with usable NFSv4 exports, a "rather stateless" model has been implemented. More complex implementation of NFSv4 state modelscan be activated by using "--enable-nfs4-stateid" at ./configure time. In this last case, the targeted client is 2.6.29-4 (Fedora11). 0.99.56 - Thu 9 Jul 2009 - Change 2 log messages in MFSL_ASYNC - Removed a debug messages in fusexmp_fh - Bug fix in RW_Lock (may lead to deadlock when used in parralel with several clients - Prevent FSAL_PROXY to use udp as a transport layer, the "NFSv4_Proxy::NFS_Proto" field from the configuration file as been removed as well - MFSL_ASYNC: now, only root can chmod or chgrp on a file/dir/symlink - MFSL_ASYNC: the way mfsl_async_symlink was fully reviewd 0.99.57 - Thu 30 Jul 2009 - Add NFSv3 commit/write logic - newpynfs helped locating bug in nfs4_op_commit (bad types and write verifier not set) - Add NFSv4 commit/write logic - Brand new management of the NFSv4 open_owner - Bug fix in clientid generation (very bad hash function) - File nfs4_op_lock.c was fully rewritten to fix bugs - open_owner's seqid is to be incremented even if OPEN fails - lock overlapping check was reviewed - no seqid checking when making read/write 0.99.58 - Mon 14 Sep 2009 - Use SOMAXCON in listen - XDR modules now support NFSv4.1 protocol, the rest is to be implemented - Added nfs41_op_exchange_id, nfs41_op_create_session, nfs41_op_sequence, nfs41_op_lock, nfs41_op_locku, nfs41_op_lockt - Added early support for nfs41_op_create_session, nfs41_op_exchange_id, nfs41_op_destroy_session - command like 'mount -t nfs4 -o minorversion=1 ..' is now possible but basic connectathon is not fully ok 0.99.59 - Wed 28 Oct 2009 - Implementation of SEQUENCE's DRC - Add new "NFSv4.1 specific errors" management in OP_SEQUENCE - Bug Fix: test SEQ9c showed that the request replayed from the session's DRC always had status NFS4_OK - Bug Fix : Management of NFS4ERR_RETRY_UNCACHED_REP and non cached sequence - Bug Fix : correct use of NFS4ERR_TOO_MANY_OPS - Bug Fix : initial seqid value is 1 in NFSv4.1 - Bug Fix : stateid's sequence increment was badly manage in case the file was opened without being created at the same time. - Minor change in rpcxid dupreq management - Fix bad default parameters in config_samples/proxy.ganesha.nfsd.conf - Add config file parameter NFSv4::DomainName (formerly domain was alway "localdomain" which was a big lack of flexibility) - Add macro %tmp_install_dir to spec file to avoid using dirty variable TMP_INSTALL_DIR - Add optional use of nfsidmap library 0.99.60 - Mon 30 Nov 2009 - The uid/gid mapping functions did a bad use of their related idmapper_cache functions (the cache was not used in several cases) - Bug Fix : with kernel newer than 2.6.29, Connectathon's test6 failed on NFSv4 and NFSv4.1. This is now fixed : eod is returned only when the array of requested entries is empty (no more entry to be read in the directory). - Lock supports goes successfully through BULL's locktest when used on top of NFSv4.1 (problems remains with NFSv4.0) - Bug Fix: NFSv4 rsize/wsize had always value 1024 that killed performances. - Bug Fix : in nfsv4, the same open_owner opening a previously opened fileid did not get the same stateid. - Bug Fix : most of the time, files opened/created via NFSv4 were never closed 0.99.61 - Fri 22 Jan 2010 - A patch from Eric Sesterhenn about memleaks has been integrated. - Bug Fix : now check value of csa_flags for OP4_CREATE_SESSION - Bug Fix : OP4_LOOKUPP should return NFS4ERR_SYMLINK instead of NFS4ERR_NOTDIR when cfh is related to a symbolic link. - Bug Fix : error NFS4ERR_NOT_ONLY_OP managed for OP4_EXCHANGE_ID - Bug Fix : OP4_LOOKUPP should return NFS4ERR_NOENT when called from the rootfh - Bug Fix : management of NFS4ERR_NOT_ONLY_OP introduced a bug when compiling without NFSv4.1 support. This is now fixed. - Changed bad #define in Log/log_functions.c (former situation could lead to possible buffer overflow) - A patch by Erik Levinson about the use of libnfsidmap with gssrpc has been integrated - Bug Fix : it was impossible to compile with both support for gssrpc and support for NFSv4.1 (mismatch in nfsv41.h and xdr_nfsv41.c) 0.99.62 - Fri 5 Mar 2010 - Security fix : badly managed caller_gid in nfs_exports.c - Fixed a typo in nfs-ganesha.spec.in - RPM packaging : fixed bad dependences for db engine to be used with FSAL_POSIX (postgresql was always referenced, even when compiled with mysql) - Debian Packaging : fixed same dep problems as above with rpm files - Bug Fix : in idmapper.c, functions utf82uid and utf82gid were badly managing parameters passed to name2uid/name2gid when compiled with the support of libnfsidmap - pNFS implementation : now support attribute FATTR4_FS_LAYOUT_TYPE - Fixed a bug in a Makefile.am that prevent target 'check' to compile - RPM packaging : when compiling rpm files, only those related to the FSAL chose at ./configure time are build - fixed two typos and one potential memleaks (thanks to IBM guys how located this in the code) 0.99.63 - Thu 25 Mar 2010 - A patch from Sean Dague (japh@us.ibm.com) fixes a memleak in FSAL_POSIX - A big patch provided by Aneesh Kumar (aneesh.kumar@linux.vnet.ibm.com) implements the NLMv4 protocol (NFS lock managemment for NFSv3) - Two small patches from Frank Filz were applied - A "indent" target has been add to the src/Makefile.am . The code was fully re-indented with this target (that uses the "indent" utility). - C-format style template for emacs provided by Sean Dague (japh@us.ibm.com) - Bug fix (Frank Filz) : readdir had an extraneous empty request with eod=TRUE - Option "-nce" added to 'make indent' - Bug fix : It was impossible to mount an exported entry's sub directory 0.99.64 - Thu 29 Apr 2010 - pNFS implementation for LAYOUT4_FILE starts with this version - Function to initiates connection/NFsv4.1 sessions with the DS are done - RPM Packaging : add chkconfig --add in %post - Export Access Type "MDONLY" was not managed when using NFSv4 - Add safety check to cache_inode_remove/cache_inode_create and cache_inode_link to prevent from non allowed access. - Statistics for NFSv4.0 and NFSv4.1 operations have been added - Bug Fix: default value for FSINFO3::dtpref was 0. Value 16384 is now used. - Bug Fix: OPEN4 returns NFS4ERR_ROFS when used from the pseudofs - Early (unstable) implementation of pNFS provided. Will continue and be stabilized in later releases. - Project is now released under LGPLv3 0.99.65 - Tue 8 Jun 2010 - New FSAL_XFS designed for natively exporting XFS filesystems - Integration of a patch from Aneesh Kumar that implements Async NLM and NSM support - A patch from Frank Filz related to POSIX behavior when opening file - Bug Fix : missing AM_FLAGS prevented from compiling with pNFS and POSIX based FSAL using MySQL. - add '--enable-ds' in configure to configure nfs-ganesha as a NFSv4.1 server usable as a pNFS Data Server - FSAL_LUSTRE : add lockdesc support - Bug Fix: nfs4_op_access was not managing secondary groups properly. It now relyes on the FSAL for this. - Default value for FSAL_MODE_SUID and FSAL_MODE_SGID have changed. - Add log trace when a operation is refused due to a setuid/setgid bit violation - Bug Fix : in NFSv4, file were created with mode = 0000 . This was a trouble with certain FSAL - Bug fix: Double close in log_functions.c - checksum were removed from NFS file handles (more space for storing FSAL handles) - (Patch from Frank Filz) : new and optionnal core parameter Bind_Addr 0.99.66 - Fri 25 Jun 2010 - FSAL_XFS now has lock support - Brand new FSAL_GPFS added (patch from IBM) to natively support GPFS - FSAL_POSIX and FSAL_XFS now have quota support (via rquota v1/v2 protocol and the use of the quotactl function) - Typos fixed in doxygen.conf files - FSAL_TEMPLATE updated (had new functions for quota and lock management) - pNFS/LAYOUT_FILES works with multiple Data Server 1.0.1 - Fri 17 Sep 2010 - New FSAL model: FSAL modules can now be compile as shared object and can be loaded at runtime - New FSAL to support ZFS filesystem - Add the capability to build a "FSAL-less" daemon that loads FSAL shared object by using the dlopen function. - FSAL_PROXY: the embedded client now can allocate a tcp socket on a privileged port to contact the "proxyfied" server - FSAL shared objects are provided as precompiled rpms - Log layer now uses syslog as a possible log stream - Add new call FSAL_getextattrs to get info such a generation number or creation time - New Log management from IBM - Several potential buffer overflow in fscanf fixed 1.0.2 - Mon 18 Oct 2010 - New TCP connection management to avoid DOS-like attack - Tag _NO_BLOCK_PREALLOC was removed - New debian packaging - Bug Fix : OP4_REMOVE did not operate if the destination was an existing file - Bug Fix : FATTR4_ACL is now an unsupported attributes (it is in fact) - Bug Fix : OP4_GETATTR on unsupported attributes should ignore them instead of returning NFS4ERR_ATTR_NOTSUPP - Bug Fix : locks_held counter was badly managed in OP4_LOCKU - Bug Fix : OP4_DESTROY_SESSION returned bad session when session was successfully destroyed. - Bug Fix : FSAL_XFS:fsal_create always made files owned by root. - Code Cleaning : all Log* functions's format were reviewed and fixed - Memory manager improvement : stuff_alloc macros and BuddyMalloc are now closely tied. - Bug Fix : erroneous EACCESS in FSAL_XFS and FSAL_LUSTRE when creating file as a regulat user - New feature : FSAL_ZFS provides access to ZFS snapshot directory .zfs based on ZFS's COW feature. 1.0.3 - Tue 21 Dec 2010 - A regression in FSAL_PROXY was fixed (size of NFSv4 handles) - HashTable support hash functions that compute hashval and rbtval in one pass - Cache_inode uses a "dual value compute function" in its hashtable - Reverse clientid mapper uses a "dual value compute function" in its hashtable - pNFS/LAYOUT4_NFSV4_1_FILES : several bug fixed (see git log) - DupReq indexation has been reviewed: now (src_ipaddr,port,xid,additional cksum) tuple is used as key - workers's selection has been reviewed to reduce CPU comsumption in the case a very large number of workers is used. - security fixed : when 'root' is mapped as nobody, its 'altgroups' is cleared - Configuration can now reduce the version of NFS advertsised to rpcbind (to avoid NFSv2 for example). - XML output has been added to non-regression tests for easier integration in HudsonCI - A few changes in NLM implementation - FSAL_HPSS has been ported to HPSSv7.3.2 - Better management of NFSv4.x attribute FATTR4_CHANGE 1.0.4 - Tue 25 Jan 2011 - init.d scripts are now closer to LSB requirements - Bug fix: race condition when inserting the same new entry several time and concurrently in metadata cache - Bug fix : it was impossible to set "/" as an export's entry's pseudopath - Bug fix : badly pack fsal_handle in FSAL_PROXY could create 2 entries in cache_inode for a single file. - Bug fix : memory violation in FSAL_PROXY for fattr4's type based on nfs4time 1.0.5 - Wed 6 Apr 2011 - Using function daemon() to start the daemon in a cleaner way - Using gethostbyaddr_r to avoid contention when resolving address in nfs_ip_name.c - pNFS/file support has been added for LUSTRE - pNFS related sources were reorganized with a design closer to what is done with FSAL/MFSL - "stable_how" flag management has been remade in deep, including a new FSAL_sync call to every FSAL - FSAL_PROXY: the FSAL is no able to deal correctly when the remote server restarts - Data Cache : a set of flushers threads has been added to the daemon, making it possible to run the flush operations internally and no more as an external command. - Bug Fix: FSAL error was not converted into a Cache_inode error, and thus was dropped, causing client to infinite retry - Add a new tool to find all the instances of LogXXX - New LogInfo and LogWarn log level 1.1.0 - Wed 7 Apr 2011 - Bug Fix: Bad memset in FSAL_HPSS::HPSSFSAL_lookup. Result was a segfault at init. - Code reorganization : subdirectory RPCAL has been added - Bug fixe : bad 'fd2handle' options in FSAL_XFS::FSAL_rename - Log: new log levels LogFatal, LogCleanup added - TIRPC now supports the RPCSEC_GSS feature - pNFS : API for implementing pNFS specific feature has been fully refurbished. - FSAL_VFS: new FSAL using "open_by_handle" feature added in the kernel higher than 2.6.39. This FSAL makes it possible to export any filesystem managed by the VFS. - NFSv4: the product is now compiled with NFSv4.1 support by default - Log module: now using __FUNCTION__ macro to get the function's name - Bug Fix: the cache_inode_commit function badly managed the Data Cache. - Multiple FSAL support: it is now possible to 'dlopen' several fsal shared objects. Attention: there are changes to be done in the configuration file - Bug Fix: issue occured in 'Tbl' pass of cthon04 test for FSAL_XFS and FSAL_VFS because of a badly managed opened fd. This is now fixed. - NFSv4 ACLs support - GPFS ACLs support (to be used with NFSv4 ACL support) 1.1.1 - Fri 11 Nov 2011 - Bug Fix: padded size for FSAL-less daemon were too short and not aligned on 64 bits - Bug Fix: Badly formed fsal_op_context_t in FSAL_ZFS (issue when using as shared object) - Bug Fix: bad modes/ACLs management in several FSALs (produced EPERM errors) - Bug Fix: badly managed access_type in nfs3_Access 1.2.0 - Mon 12 Dec 2011 - Bug Fix: Call cache_inode_close after deleting share state - Bug Fix: Bad management of PID_FILE is several init.d scripts - Added support for ERR_FSAL_DELAY in upper layer - FSAL code has been reorganized a lot - New SAL (State Abstraction Layer) to manage locks (NFSv4/NLM) and share reservations - Symbolic Links are managed via a pool to save memory in Cache_Inode - Early 9P support 1.3.0 - Fri 23 Dec 2011 - Lustre FSAL: exporting several Lustre filesystem with the same server instance - A bug was found (during Bake-A-thon) and fix in OP4_READDIRPLUS (leading to missing directory's entries) - Directory content cached is now managed as a tree. Formerly used dir_chain were removed. - Lock support for FSAL_LUSTRE - FSAL_LUSTRE/FSAL_XFS/FSAL_FUSELIKE/FSAL_ZFS: regression in readdir detected and fixed - export list now supports IP ranges using the CIDR format 1.4.0 - Thu 15 Mar 2012 - Bug Fix: Memory leak fixed in RPC's DRC - Bug Fix: Bad computation of rbt_value in RPC's DRC's hashtable - New FSAL_CEPH (dedicated to the ceph filesystem) - pNFS code refurbished with CEPH specific code and layout file support - All RPCs are now based on TIRPC - Bug Fix: in nfs_Write, for clean handle of umask, the server should allow the owner of a file to write to it, even if the file is read-only (has mode r---r--r-- for example) - Bug Fix: bad memory padding in fsal_handle_t and fsal_cookie_t 1.5.0 - Mon 16 Jul 2012 - TIRPC library was significantly refurbished with bi-directional RPC support (required for NFSv4.1 backchannel support). - Multiple RPC dispatch is now supported - Cache inode has been reworked on its handling of directory entries - Cache inode readdir logic is now based on callbacks to take advantage of AVL tree work - State Management has been unified and improved. Locking is consistent across both NFSv3 and NFSv4.x - Client id management support has been added - 9p.2000L support has been added. Dev is currently under alpha version - Improved file handle support. Handles's versions and variable length handles are better supported. - Memory allocation has been refactored. The configure step can detect the memory allocators available for the build and select the best allocator (jemalloc preferred if available) with a fallback to the standard C library malloc/free. The buddy allocator has been completely removed. - Improved Kerberos support. Autoconf now probes it correctly. Compatibility with Microsoft AD Kerberos is improved. - Numerous bits of experimental and partially implemented feature code have been removed, significantly reducing the code line count and eliminating unused build time configuration options. These include: - FSAL_SNMP is now officially deprecated 2.0.0 - Fri 6 Dec 2013 - FSALs (filesystem backends) are now loadable shared objects. The server can support multiple backends at runtime. Most of the 1.5.0 FSALs have been rewritten to support this new API. - NFSv4.1 pNFS is supported. Most of the FSALs for clustered filesystems also support pNFS. The server can be both an MDS and DS. - DBus is now the administration tool. The SNMP MIB and stats thread are deprecated. Reference implementations of DBus client(s) for all of the DBus interfaces are supplied. These clients use the PyQt4 class libraries and Python 2.7. There are both command line scripts and a Qt based GUI admin tool. - All the significant bugfixes from the 1.5.x branch have been forward ported or re-implemented in the new codebase. - The server passes all of the cthonv4 and pynfs 4.0 tests. All of the significant (non-delegation) pynfs 4.1 tests also pass. This is significant protocol correctness improvement over the previous version. - Most subsystems/modules have had refactor/rewrite work to improve stability, scalability, and correctness. Memory and system resource usage has also been improved. - NFSv2 support has been deprecated. NFSv3 still supports the older version of the MNT protocol for compatibility with some clients. - The build process has been converted to Cmake and autotools has been removed. - The codebase has been reformatted to conform to Linux kernel coding style. Changes are checked and validated with the kernel's checkpatch.pl script. The script and our configuration file for it are supplied in the source. 2.1.0 - Thurs 26 Jun 2014 - Exports are now dynamic. They can be added or removed via DBus commands. The manage_exports python script has been updated to support the feature. - The Pseudo filesystem has been re-written as a FSAL. This enables dynamic exports. Submounted filesystems also work base on this change. - The configuration file processing has been rewritten to improve error checking and logging. All parameters are consistently checked for range. The validation of the whole configuration blocks are also checked. - GIDs can now be managed to use external authentication sources. This fixes the protocol limitation of AUTH_SYS which restricted the number of alternate gids to 16. - RPM packaging has been restructured and updated. The DBus tools are now packaged. 2.2.0 - Tue 21 Apr 2015 - Ganesha supports granting delegations - There have been numerous config changes - Ganesha now includes systemd scripts - Improved packaging for RPM and Debian - Major stability improvements - non-QT based python tools - Support for Ganesha to be a pNFS DS only, no MDS - SECINFO in preferred order - LTTng support - NFS v4.2 support - Major improvements in 9p support - Code cleanup (checkpatch and Coverity) - ntirpc improvements - FSAL_GLUSTER updated with pNFS and ACL support and more 2.3.0 - Tue 27 Oct 2015 - Numerous bug fixes - this release is fundamentally a stability improvement - Update Checkpatch - FSAL_GLUSTER ACL support using POSIX ACLs underneath - FSAL_GLUSTER Upcall Interface - FSAL_GLUSTER Performance Improvements (including pNFS performance) - Send SECINFO_NO_NAME in preferred order. - Build and Packaging Improvements - In Memory NFS v4 ACLs for FSAL_VFS for experimentation/testing - libntirpc Performance Improvements - Allow Multiple DSes - Cache Inode Tuning - Licensing Cleanup - Config improvements in FSAL_GLUSTER and FSAL_GPFS (including ability to disable ACL support in config) - Build properly on 32 bit and Big Endian platforms - FSAL_GPFS fix regression for short handles to work with VMWare clients - FSAL_GPFS fix regression for short handles to work with some NFS v3 clients that don't conform to RFC and only allow 56 byte handles nfs-ganesha-2.6.0/src/Docs/000077500000000000000000000000001324272410200153655ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/Docs/Resources.txt000066400000000000000000000033501324272410200201010ustar00rootroot00000000000000Here is a list of things that may be useful with Ganesha. * bonnie++ A filesystem benchmarking tool. It can be retrieved from http://www.coker.com.au/bonnie++/ * libgssapi A gssapi interface, available from CITI at http://www.citi.umich.edu/projects/nfsv4/linux/libgssapi/ * libgssglue A gssapi interface, available from CITI at http://www.citi.umich.edu/projects/nfsv4/linux/libgssglue/ * libnfsidmap A library from CITI for mapping NFSv4 user/group IDs. http://www.citi.umich.edu/projects/nfsv4/linux/libnfsidmap/ * librpcsecgss An implementation of RPCSEC_GSS from CITI. http://www.citi.umich.edu/projects/nfsv4/linux/librpcsecgss/ * locktests-net An fcntl lock stress tester from Bull, targetting NFSv4 locks http://nfsv4.bullopensource.org/tools/tests/page39.php * PyNFS Client and server libraries for NFSv4.0 and NFSv4.1 with a collection of tests. By CITI, available from git://git.linux-nfs.org/projects/iisaman/newpynfs.git * NFS Shell A user-mode NFSv3 client and call exerciser, available from ftp://ftp.cs.vu.nl/pub/leendert/nfsshell.tar.gz * NFS Connectathon 04 Test NFS servers through POSIX calls. Available from git://fedorapeople.org/home/fedora/steved/public_git/cthon04.git * NFS Utils Linux NFS user space support utilities, available from git://git.linux-nfs.org/projects/steved/nfs-utils.git * NFSWatch Monitor traffic to and from an NFS server. Available from http://nfswatch.sourceforge.net/ * RPCBind Implements the RPC port mapping service, required for running an NFSv3. Available from http://rpcbind.sourceforge.net/ * SGI NFS Test Tools An NFS test suite available from SGI at http://oss.sgi.com/projects/nfs/testtools/ nfs-ganesha-2.6.0/src/Docs/USEFUL-RFCs.txt000066400000000000000000000035551324272410200177340ustar00rootroot00000000000000The following RFCs are useful references for Ganesha RPC Version 2 https://datatracker.ietf.org/doc/rfc5531/ RPC Bind https://datatracker.ietf.org/doc/rfc1833/ NFS v2 We no longer implement V2 but for reference https://www.rfc-editor.org/info/rfc1094 NFS v3 https://www.rfc-editor.org/info/rfc1813 NFS v4.0 https://www.rfc-editor.org/info/rfc7530 obsolete but we coded to it originally https://www.rfc-editor.org/info/rfc3530 NFS v4.0 XDR https://www.rfc-editor.org/info/rfc7531 NFS v4.0 Migration https://datatracker.ietf.org/doc/draft-ietf-nfsv4-rfc3530-migration-update/ https://datatracker.ietf.org/doc/draft-ietf-nfsv4-migration-issues/ NFS v4.1 https://www.rfc-editor.org/info/rfc5661 NFS v4.1 XDR https://www.rfc-editor.org/info/rfc5662 NFS v4.1 PNFS Block Layout https://www.rfc-editor.org/info/rfc5663 NFS v4.1 PNFS Object Layout https://www.rfc-editor.org/info/rfc5664 NFS v4.1 PNFS Flex Files Layout http://datatracker.ietf.org/doc/draft-ietf-nfsv4-flex-files/ NFS v4.2 https://datatracker.ietf.org/doc/draft-ietf-nfsv4-minorversion2/ NFS v4.2 XDR https://datatracker.ietf.org/doc/draft-ietf-nfsv4-minorversion2-dot-x/ NFS v4.x Versioning https://datatracker.ietf.org/doc/draft-ietf-nfsv4-versioning/ NFS V4 XATTRS https://datatracker.ietf.org/doc/draft-ietf-nfsv4-xattrs/ IANA Considerations for Remote Procedure Call (RPC) Network Identifiers and Universal Address Formats https://www.rfc-editor.org/info/rfc5665 Kerberos/GSS https://www.rfc-editor.org/info/rfc1964 https://www.rfc-editor.org/info/rfc2025 https://www.rfc-editor.org/info/rfc2203 https://www.rfc-editor.org/info/rfc2623 https://www.rfc-editor.org/info/rfc4120 https://www.rfc-editor.org/info/rfc4121 https://www.rfc-editor.org/info/rfc5403 https://www.rfc-editor.org/info/rfc6112 https://www.rfc-editor.org/info/rfc6542 https://www.rfc-editor.org/info/rfc6649 and more...nfs-ganesha-2.6.0/src/Docs/coding_style/000077500000000000000000000000001324272410200200505ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/Docs/coding_style/doxygen.txt000066400000000000000000000136151324272410200222740ustar00rootroot00000000000000A quick guide to Doxygen in Ganesha Adam C. Emerson Here are my suggestions for how to make better use of doxygen. This is not a complete manual on doxygen, just a short guide for hacking on ganesha. First and foremost, whenever you edit a function, please check the function comment to make sure that it accurately describes the function, that the parameters it lists are actually the parameters the function takes, and that the documented return matches the values the function may return. Please do likewise for data structures, enumerations, and other types. Every file should have a file comment. At minimum this should be of the form: /** * @file filename.c * @brief Do this, that, and the other thing */ Please do not prefix anything with the filename. If you add a long description, just separate it with a blank line, like so. /** * @file filename.c * @brief Functions for for this, that, and the other thing. * * This file contains functions that perform many important * operations, among them processing arguments and returning values. * They also allocate stack frames. */ There is no reason to copy the @brief description into the long description. Doxygen (assuming I've configured it properly) already handles that. If you don't have anything longer to say, don't include a long description at all. If you add an @author tag, please include your full name and email address, like so. /** * @file filename.c * @author Adam C. Emerson * @brief Functions for for this, that, and the other thing. */ The old tags with the $cvs stuff$ in them are really not very useful and should be removed. My opinion is that the @author tag is best used to indicate the person to contact if one has questions about a file, rather than the first person to create a file with that name. Therefore, if you have made extensive changes to a file, you could add yourself as an @author. (You may have more than one @author tag per file.) Every function should have a function comment. At a minimum, this should contain @brief information, a description of the parameters, and a description of the return value. If the function is void, do not include a @return tag. As with files, if the @brief description conveys all information worth conveying about the function, do not include a long comment and skip directly from the @brief description to the paremters. Here is an example: /** * @brief Do some stuff * * This function does a large number of interesting things with bits. * It ANDs them together. it ORs them. It shifts them left and * shifts them right. It even returns some. * * @param[in] param1 Some bits to work on * @param[out] param2 Some bits that we output * @param[in,out] param3 Some bits we read and output * * @return The result of doing things to the bits */ uint64_t do_some_stuff(uint64_t param1, uint64_t *param2, uint64_t *param3) If you wish to list possible return values and their meanings, please do so as follows: /** * @brief Check a value * * @param[in] v The value to check * * @retval VALUE_OKAY if the value is passable. * @retval VALUE_PU the value is not very nice. * @retval VALUE_HORRIBLE the value is really quite dreadful. * @retval VALUE_SUPERLATIVE the value is really quite good. */ Please do not prefix anything in the function comment with the function's name. Doxygen gets the function's name from the function itself. Every data structure should also have a comment consisting of, at least, a @brief. Members should be commented individually. This may be done one of two ways. /** * @brief A structure holding some state. */ struct state { char *name; /*< The name of the state */ char abbrev[2]; /*< The two-letter abbreviation for the state */ struct person *governor; /*< The state's executive */ }; Please note that an inline comment following a data member should be prefixed with /*<. Many structures have /**< which is incorrect. For readability, if the types or member names start to get long, you can also precede the data members with their comments, like so. struct city { /** The name of the city */ char *name; /** A linked list of officers responsible for city government */ struct glist_head city_council; /** The head of city government */ struct person *lord_mayor; }; Doxygen is happy with either one. I recommend the second if you're starting to get seriously word-wrapped member comments. Modules and layers can be indicated with the group command. We aren't doing this yet but should start. Pick a name and description for the module, wrap every file that implements it like so: /** * @defgroup Foolayer The Foo Layer * @{ */ /** * @file foo.c * @brief Functions for the foo layer */ /** * @brief Foo Function */ void foo(void) { } /** @} */ /** * @defgroup Foolayer The Foo Layer * @{ */ /** * @file foo.h * @brief Structures for the foo layer */ /** * @brief Foo structure */ struct foodata { uint64_t blah; } /** @} */ You may also define information pages. These are ideal for describing the high level design or detailing behaviors of a module that are not bound to a specific function or structure. Use the @page command like so: /** * @page LockDisc Locking Discipline * * When using this layer, one should always take the A lock before the * B lock, and the B lock before the C lock. One must have both the B * and C lock before modifying the Q area... */ The first token after @page is an arbitrary name for the page. Be unique. Following the unique page name is the page title. If you are describing the behavior of a subsystem, please make sure the @page comment is included within the grouping command for that subsystem. You can use markdown in any long description. For more information please see the doxygen manual, but this should be enough to get started. nfs-ganesha-2.6.0/src/Doxyfile.in000066400000000000000000002254021324272410200166150ustar00rootroot00000000000000# Doxyfile 1.8.1.2 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or sequence of words) that should # identify the project. Note that if you do not use Doxywizard you need # to put quotes around the project name if it contains spaces. PROJECT_NAME = "NFS-Ganesha" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = pre-2.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "An NFS server in userspace" # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = Docs # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding # "class=itcl::class" will allow you to use the command class in the # itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = h=C # If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all # comments according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you # can mix doxygen, HTML, and XML commands with Markdown formatting. # Disable only in case of backward compatibilities issues. MARKDOWN_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and # unions with only public data fields will be shown inline in the documentation # of the scope in which they are defined (i.e. file, namespace, or group # documentation), provided this scope is documented. If set to NO (the default), # structs, classes, and unions are shown on a separate page (for HTML and Man # pages) or section (for LaTeX and RTF). INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = YES # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. SYMBOL_CACHE_SIZE = 0 # Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be # set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given # their name and scope. Since this can be an expensive process and often the # same symbol appear multiple times in the code, doxygen keeps a cache of # pre-resolved symbols. If the cache is too small doxygen will become slower. # If the cache is too large, memory is wasted. The cache size is given by this # formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal scope will be included in the documentation. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files # containing the references data. This must be a list of .bib files. The # .bib extension is automatically appended if omitted. Using this command # requires the bibtex tool to be installed. See also # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this # feature you need bibtex and perl available in the search path. CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = NO # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = YES # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = @CMAKE_CURRENT_SOURCE_DIR@ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = @CMAKE_CURRENT_SOURCE_DIR@/tools @CMAKE_CURRENT_SOURCE_DIR@/testres-xml @CMAKE_CURRENT_SOURCE_DIR@/test @CMAKE_CURRENT_SOURCE_DIR@/scripts @CMAKE_CURRENT_SOURCE_DIR@/rpm @CMAKE_CURRENT_SOURCE_DIR@/HudsonCI @CMAKE_CURRENT_SOURCE_DIR@/example-fuse @CMAKE_CURRENT_SOURCE_DIR@/debian @CMAKE_CURRENT_SOURCE_DIR@/config_samples @CMAKE_CURRENT_SOURCE_DIR@/FSAL/FSAL_GPFS @CMAKE_CURRENT_SOURCE_DIR@/FSAL/FSAL_GLUSTER @CMAKE_CURRENT_SOURCE_DIR@/libntirpc # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C, C++ and Fortran comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is advised to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when # changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # style sheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the style sheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of # entries shown in the various tree structured indices initially; the user # can expand and collapse entries dynamically later on. Doxygen will expand # the tree to such a level that at most the specified number of entries are # visible (unless a fully collapsed tree already exceeds this amount). # So setting the number of entries 1 will produce a full collapsed tree by # default. 0 is a special value representing an infinite number of entries # and will result in a full expanded tree by default. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) # at top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. Since the tabs have the same information as the # navigation tree you can set this option to NO if you already set # GENERATE_TREEVIEW to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. # Since the tree basically has the same information as the tab index you # could consider to set DISABLE_INDEX to NO when enabling this option. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you may also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to # the MathJax Content Delivery Network so you can quickly see the result without # installing MathJax. # However, it is strongly recommended to install a local # copy of MathJax from http://www.mathjax.org before deployment. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension # names that should be enabled during MathJax rendering. MATHJAX_EXTENSIONS = # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvantages are that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = letter # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See # http://en.wikipedia.org/wiki/BibTeX for more info. LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load style sheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. For each # tag file the location of the external documentation should be added. The # format of a tag file without this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths # or URLs. Note that each tag file must have a unique name (where the name does # NOT include the path). If a tag file is not located in the directory in which # doxygen is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = NO # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will use the Helvetica font for all dot files that # doxygen generates. When you want a differently looking font you can specify # the font name using DOT_FONTNAME. You need to make sure dot is able to find # the font, which can be done by putting it in a standard location or by setting # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the # directory containing the font. DOT_FONTNAME = Helvetica # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the Helvetica font. # If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to # set the path where dot can find it. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If the UML_LOOK tag is enabled, the fields and methods are shown inside # the class node. If there are many fields or methods and many nodes the # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS # threshold limits the number of items for each type to make the size more # managable. Set this to 0 for no limit. Note that the threshold may be # exceeded by 50% before the limit is enforced. UML_LIMIT_NUM_FIELDS = 10 # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. If you choose svg you need to set # HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible in IE 9+ (other browsers do not have this requirement). DOT_IMAGE_FORMAT = png # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. # Note that this requires a modern browser other than Internet Explorer. # Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you # need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files # visible. Older versions of IE do not have SVG support. INTERACTIVE_SVG = NO # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = YES # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES nfs-ganesha-2.6.0/src/FSAL/000077500000000000000000000000001324272410200152225ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/FSAL/CMakeLists.txt000066400000000000000000000012731324272410200177650ustar00rootroot00000000000000# Add the directory for stackable FSALs add_subdirectory(Stackable_FSALs) # Add the directory for Pseudo FSAL add_subdirectory(FSAL_PSEUDO) # All we need to do here is control the # build of chosen fsals if(USE_FSAL_PROXY) add_subdirectory(FSAL_PROXY) endif(USE_FSAL_PROXY) if(USE_FSAL_CEPH) add_subdirectory(FSAL_CEPH) endif(USE_FSAL_CEPH) if(USE_FSAL_RGW) add_subdirectory(FSAL_RGW) endif(USE_FSAL_RGW) if(USE_FSAL_GPFS) add_subdirectory(FSAL_GPFS) endif(USE_FSAL_GPFS) if(USE_FSAL_VFS) add_subdirectory(FSAL_VFS) endif(USE_FSAL_VFS) if(USE_FSAL_GLUSTER) add_subdirectory(FSAL_GLUSTER) endif(USE_FSAL_GLUSTER) if(USE_FSAL_MEM) add_subdirectory(FSAL_MEM) endif(USE_FSAL_MEM) nfs-ganesha-2.6.0/src/FSAL/FSAL_CEPH/000077500000000000000000000000001324272410200165465ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/FSAL/FSAL_CEPH/CMakeLists.txt000066400000000000000000000011671324272410200213130ustar00rootroot00000000000000add_definitions( -D_FILE_OFFSET_BITS=64 ) SET(fsalceph_LIB_SRCS main.c export.c handle.c mds.c ds.c internal.c internal.h statx_compat.h ) if (NOT CEPH_FS_CEPH_STATX) SET(fsalceph_LIB_SRCS ${fsalceph_LIB_SRCS} statx_compat.c ) endif(NOT CEPH_FS_CEPH_STATX) include_directories(${CEPHFS_INCLUDE_DIR}) add_library(fsalceph MODULE ${fsalceph_LIB_SRCS}) add_sanitizers(fsalceph) target_link_libraries(fsalceph ${CEPHFS_LIBRARIES} ${SYSTEM_LIBRARIES}) set_target_properties(fsalceph PROPERTIES VERSION 4.2.0 SOVERSION 4) install(TARGETS fsalceph COMPONENT fsal DESTINATION ${FSAL_DESTINATION} ) nfs-ganesha-2.6.0/src/FSAL/FSAL_CEPH/ds.c000066400000000000000000000310711324272410200173220ustar00rootroot00000000000000/* * Copyright © 2012 CohortFS, LLC. * Author: Adam C. Emerson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @file ds.c * @author Adam C. Emerson * @date Mon Jul 30 12:29:22 2012 * * @brief pNFS DS operations for Ceph * * This file implements the read, write, commit, and dispose * operations for Ceph data-server handles. * * Also, creating a data server handle -- now called via the DS itself. */ #ifdef CEPH_PNFS #include "config.h" #include #include #include "fsal_api.h" #include "FSAL/fsal_commonlib.h" #include "../fsal_private.h" #include "fsal_up.h" #include "internal.h" #include "pnfs_utils.h" #define min(a, b) ({ \ typeof(a) _a = (a); \ typeof(b) _b = (b); \ _a < _b ? _a : _b; }) /** * @brief Local invalidate * * A shortcut method for invalidating inode attributes. It is * not sufficient to invalidate locally, but is immediate and * correct when the MDS and DS are colocated. */ static inline void local_invalidate(struct ds *ds, struct fsal_export *export) { struct gsh_buffdesc key = { .addr = &ds->wire.wire.vi, .len = sizeof(ds->wire.wire.vi) }; up_async_invalidate(general_fridge, export->up_ops, export->fsal, &key, CACHE_INODE_INVALIDATE_ATTRS, NULL, NULL); } /** * @brief Release a DS handle * * @param[in] ds_pub The object to release */ static void ds_release(struct fsal_ds_handle *const ds_pub) { /* The private 'full' DS handle */ struct ds *ds = container_of(ds_pub, struct ds, ds); fsal_ds_handle_fini(&ds->ds); gsh_free(ds); } /** * @brief Read from a data-server handle. * * NFSv4.1 data server handles are disjount from normal * filehandles (in Ganesha, there is a ds_flag in the filehandle_v4_t * structure) and do not get loaded into cache_inode or processed the * normal way. * * @param[in] ds_pub FSAL DS handle * @param[in] req_ctx Credentials * @param[in] stateid The stateid supplied with the READ operation, * for validation * @param[in] offset The offset at which to read * @param[in] requested_length Length of read requested (and size of buffer) * @param[out] buffer The buffer to which to store read data * @param[out] supplied_length Length of data read * @param[out] eof True on end of file * * @return An NFSv4.1 status code. */ static nfsstat4 ds_read(struct fsal_ds_handle *const ds_pub, struct req_op_context *const req_ctx, const stateid4 *stateid, const offset4 offset, const count4 requested_length, void *const buffer, count4 * const supplied_length, bool * const end_of_file) { /* The private 'full' export */ struct export *export = container_of(req_ctx->fsal_export, struct export, export); /* The private 'full' DS handle */ struct ds *ds = container_of(ds_pub, struct ds, ds); /* The OSD number for this machine */ int local_OSD = 0; /* Width of a stripe in the file */ uint32_t stripe_width = 0; /* Beginning of a block */ uint64_t block_start = 0; /* Number of the stripe being read */ uint32_t stripe = 0; /* Internal offset within the stripe */ uint32_t internal_offset = 0; /* The amount actually read */ int amount_read = 0; /* Find out what my OSD ID is, so we can avoid talking to other OSDs. */ local_OSD = ceph_get_local_osd(export->cmount); if (local_OSD < 0) return posix2nfs4_error(-local_OSD); /* Find out what stripe we're writing to and where within the stripe. */ stripe_width = ds->wire.layout.fl_stripe_unit; stripe = offset / stripe_width; block_start = stripe * stripe_width; internal_offset = offset - block_start; if (local_OSD != ceph_ll_get_stripe_osd(export->cmount, ds->wire.wire.vi, stripe, &(ds->wire.layout))) { return NFS4ERR_PNFS_IO_HOLE; } amount_read = ceph_ll_read_block(export->cmount, ds->wire.wire.vi, stripe, buffer, internal_offset, min((stripe_width - internal_offset), requested_length), &(ds->wire.layout)); if (amount_read < 0) return posix2nfs4_error(-amount_read); *supplied_length = amount_read; *end_of_file = false; return NFS4_OK; } /** * * @brief Write to a data-server handle. * * This performs a DS write not going through the data server unless * FILE_SYNC4 is specified, in which case it connects the filehandle * and performs an MDS write. * * @param[in] ds_pub FSAL DS handle * @param[in] req_ctx Credentials * @param[in] stateid The stateid supplied with the READ operation, * for validation * @param[in] offset The offset at which to read * @param[in] write_length Length of write requested (and size of buffer) * @param[out] buffer The buffer to which to store read data * @param[in] stability wanted Stability of write * @param[out] written_length Length of data written * @param[out] writeverf Write verifier * @param[out] stability_got Stability used for write (must be as * or more stable than request) * * @return An NFSv4.1 status code. */ static nfsstat4 ds_write(struct fsal_ds_handle *const ds_pub, struct req_op_context *const req_ctx, const stateid4 *stateid, const offset4 offset, const count4 write_length, const void *buffer, const stable_how4 stability_wanted, count4 * const written_length, verifier4 * const writeverf, stable_how4 * const stability_got) { /* The private 'full' export */ struct export *export = container_of(req_ctx->fsal_export, struct export, export); /* The private 'full' DS handle */ struct ds *ds = container_of(ds_pub, struct ds, ds); /* The OSD number for this host */ int local_OSD = 0; /* Width of a stripe in the file */ uint32_t stripe_width = 0; /* Beginning of a block */ uint64_t block_start = 0; /* Number of the stripe being written */ uint32_t stripe = 0; /* Internal offset within the stripe */ uint32_t internal_offset = 0; /* The amount actually written */ int32_t amount_written = 0; /* The adjusted write length, confined to one object */ uint32_t adjusted_write = 0; /* Return code from ceph calls */ int ceph_status = 0; memset(*writeverf, 0, NFS4_VERIFIER_SIZE); /* Find out what my OSD ID is, so we can avoid talking to other OSDs. */ local_OSD = ceph_get_local_osd(export->cmount); /* Find out what stripe we're writing to and where within the stripe. */ stripe_width = ds->wire.layout.fl_stripe_unit; stripe = offset / stripe_width; block_start = stripe * stripe_width; internal_offset = offset - block_start; if (local_OSD != ceph_ll_get_stripe_osd(export->cmount, ds->wire.wire.vi, stripe, &(ds->wire.layout))) { return NFS4ERR_PNFS_IO_HOLE; } adjusted_write = min((stripe_width - internal_offset), write_length); /* If the client specifies FILE_SYNC4, then we have to connect the filehandle and use the MDS to update size and access time. */ if (stability_wanted == FILE_SYNC4) { Fh *descriptor = NULL; if (!ds->connected) { ceph_status = ceph_ll_connectable_m( export->cmount, &ds->wire.wire.vi, ds->wire.wire.parent_ino, ds->wire.wire.parent_hash); if (ceph_status != 0) { LogMajor(COMPONENT_PNFS, "Filehandle connection failed with: %d\n", ceph_status); return posix2nfs4_error(-ceph_status); } ds->connected = true; } ceph_status = fsal_ceph_ll_open( export->cmount, ds->wire.wire.vi, O_WRONLY, &descriptor, op_ctx->creds); if (ceph_status != 0) { LogMajor(COMPONENT_FSAL, "Open failed with: %d", ceph_status); return posix2nfs4_error(-ceph_status); } amount_written = ceph_ll_write(export->cmount, descriptor, offset, adjusted_write, buffer); if (amount_written < 0) { LogMajor(COMPONENT_FSAL, "Write failed with: %d", amount_written); ceph_ll_close(export->cmount, descriptor); return posix2nfs4_error(-amount_written); } ceph_status = ceph_ll_fsync(export->cmount, descriptor, 0); if (ceph_status < 0) { LogMajor(COMPONENT_FSAL "fsync failed with: %d", ceph_status); ceph_ll_close(export->cmount, descriptor); return posix2nfs4_error(-ceph_status); } ceph_status = ceph_ll_close(export->cmount, descriptor); if (ceph_status < 0) { LogMajor(COMPONENT_FSAL, "close failed with: %d", ceph_status); return posix2nfs4_error(-ceph_status); } /* invalidate client caches */ local_invalidate(ds, &export->export); *written_length = amount_written; *stability_got = FILE_SYNC4; } else { /* FILE_SYNC4 wasn't specified, so we don't have to bother with the MDS. */ amount_written = ceph_ll_write_block(export->cmount, ds->wire.wire.vi, stripe, (char *)buffer, internal_offset, adjusted_write, &(ds->wire.layout), ds->wire.snapseq, (stability_wanted == DATA_SYNC4)); if (amount_written < 0) return posix2nfs4_error(-amount_written); *written_length = amount_written; *stability_got = stability_wanted; } return NFS4_OK; } /** * @brief Commit a byte range to a DS handle. * * NFSv4.1 data server filehandles are disjount from normal * filehandles (in Ganesha, there is a ds_flag in the filehandle_v4_t * structure) and do not get loaded into cache_inode or processed the * normal way. * * @param[in] ds_pub FSAL DS handle * @param[in] req_ctx Credentials * @param[in] offset Start of commit window * @param[in] count Length of commit window * @param[out] writeverf Write verifier * * @return An NFSv4.1 status code. */ static nfsstat4 ds_commit(struct fsal_ds_handle *const ds_pub, struct req_op_context *const req_ctx, const offset4 offset, const count4 count, verifier4 * const writeverf) { #ifdef COMMIT_FIX /* The private 'full' export */ struct export *export = container_of(req_ctx->fsal_export, struct export, export); /* The private 'full' DS handle */ struct ds *ds = container_of(ds_pub, struct ds, ds); /* Error return from Ceph */ int rc = 0; /* Find out what stripe we're writing to and where within the stripe. */ rc = ceph_ll_commit_blocks(export->cmount, ds->wire.wire.vi, offset, (count == 0) ? UINT64_MAX : count); if (rc < 0) return posix2nfs4_error(rc); #endif /* COMMIT_FIX */ memset(*writeverf, 0, NFS4_VERIFIER_SIZE); LogCrit(COMPONENT_PNFS, "Commits should go to MDS\n"); return NFS4_OK; } static void dsh_ops_init(struct fsal_dsh_ops *ops) { memcpy(ops, &def_dsh_ops, sizeof(struct fsal_dsh_ops)); ops->release = ds_release; ops->read = ds_read; ops->write = ds_write; ops->commit = ds_commit; } /** * @brief Try to create a FSAL data server handle from a wire handle * * This function creates a FSAL data server handle from a client * supplied "wire" handle. This is also where validation gets done, * since PUTFH is the only operation that can return * NFS4ERR_BADHANDLE. * * @param[in] pds FSAL pNFS DS * @param[in] desc Buffer from which to create the file * @param[out] handle FSAL DS handle * * @return NFSv4.1 error codes. */ static nfsstat4 make_ds_handle(struct fsal_pnfs_ds *const pds, const struct gsh_buffdesc *const desc, struct fsal_ds_handle **const handle, int flags) { struct ds_wire *dsw = (struct ds_wire *)desc->addr; struct ds *ds; /* Handle to be created */ *handle = NULL; if (desc->len != sizeof(struct ds_wire)) return NFS4ERR_BADHANDLE; if (dsw->layout.fl_stripe_unit == 0) return NFS4ERR_BADHANDLE; ds = gsh_calloc(1, sizeof(struct ds)); *handle = &ds->ds; fsal_ds_handle_init(*handle, pds); /* Connect lazily when a FILE_SYNC4 write forces us to, not here. */ ds->connected = false; memcpy(&ds->wire, desc->addr, desc->len); return NFS4_OK; } void pnfs_ds_ops_init(struct fsal_pnfs_ds_ops *ops) { memcpy(ops, &def_pnfs_ds_ops, sizeof(struct fsal_pnfs_ds_ops)); ops->make_ds_handle = make_ds_handle; ops->fsal_dsh_ops = dsh_ops_init; } #endif /* CEPH_PNFS */ nfs-ganesha-2.6.0/src/FSAL/FSAL_CEPH/export.c000066400000000000000000000323451324272410200202420ustar00rootroot00000000000000/* * Copyright © 2012-2014, CohortFS, LLC. * Author: Adam C. Emerson * * contributeur : William Allen Simpson * Marcus Watts * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @file FSAL_CEPH/export.c * @author Adam C. Emerson * @author William Allen Simpson * @date Wed Oct 22 13:24:33 2014 * * @brief Implementation of FSAL export functions for Ceph * * This file implements the Ceph specific functionality for the FSAL * export handle. */ #include #include #include #include #include "abstract_mem.h" #include "fsal.h" #include "fsal_types.h" #include "fsal_api.h" #include "FSAL/fsal_commonlib.h" #include "FSAL/fsal_config.h" #include "internal.h" #include "statx_compat.h" /** * @brief Clean up an export * * This function cleans up an export after the last reference is * released. * * @param[in,out] export The export to be released * * @retval ERR_FSAL_NO_ERROR on success. * @retval ERR_FSAL_BUSY if the export is in use. */ static void release(struct fsal_export *export_pub) { /* The private, expanded export */ struct export *export = container_of(export_pub, struct export, export); deconstruct_handle(export->root); export->root = 0; fsal_detach_export(export->export.fsal, &export->export.exports); free_export_ops(&export->export); ceph_shutdown(export->cmount); export->cmount = NULL; gsh_free(export); export = NULL; } /** * @brief Return a handle corresponding to a path * * This function looks up the given path and supplies an FSAL object * handle. Because the root path specified for the export is a Ceph * style root as supplied to mount -t ceph of ceph-fuse (of the form * host:/path), we check to see if the path begins with / and, if not, * skip until we find one. * * @param[in] export_pub The export in which to look up the file * @param[in] path The path to look up * @param[out] pub_handle The created public FSAL handle * * @return FSAL status. */ static fsal_status_t lookup_path(struct fsal_export *export_pub, const char *path, struct fsal_obj_handle **pub_handle, struct attrlist *attrs_out) { /* The 'private' full export handle */ struct export *export = container_of(export_pub, struct export, export); /* The 'private' full object handle */ struct handle *handle = NULL; /* Inode pointer */ struct Inode *i = NULL; /* FSAL status structure */ fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; /* The buffer in which to store stat info */ struct ceph_statx stx; /* Return code from Ceph */ int rc; /* Find the actual path in the supplied path */ const char *realpath; if (*path != '/') { realpath = strchr(path, ':'); if (realpath == NULL) { status.major = ERR_FSAL_INVAL; return status; } if (*(++realpath) != '/') { status.major = ERR_FSAL_INVAL; return status; } } else { realpath = path; } *pub_handle = NULL; /* * sanity check: ensure that this is the right export. realpath * must be a superset of the export fullpath, or the string * handling will be broken. */ if (strstr(realpath, op_ctx->ctx_export->fullpath) != realpath) { status.major = ERR_FSAL_SERVERFAULT; return status; } /* Advance past the export's fullpath */ realpath += strlen(op_ctx->ctx_export->fullpath); /* special case the root */ if (strcmp(realpath, "/") == 0) { assert(export->root); *pub_handle = &export->root->handle; return status; } rc = fsal_ceph_ll_walk(export->cmount, realpath, &i, &stx, !!attrs_out, op_ctx->creds); if (rc < 0) return ceph2fsal_error(rc); construct_handle(&stx, i, export, &handle); if (attrs_out != NULL) ceph2fsal_attributes(&stx, attrs_out); *pub_handle = &handle->handle; return status; } /** * @brief Decode a digested handle * * This function decodes a previously digested handle. * * @param[in] exp_handle Handle of the relevant fs export * @param[in] in_type The type of digest being decoded * @param[out] fh_desc Address and length of key */ static fsal_status_t wire_to_host(struct fsal_export *exp_hdl, fsal_digesttype_t in_type, struct gsh_buffdesc *fh_desc, int flags) { switch (in_type) { /* Digested Handles */ case FSAL_DIGEST_NFSV3: case FSAL_DIGEST_NFSV4: /* wire handles */ fh_desc->len = sizeof(vinodeno_t); break; default: return fsalstat(ERR_FSAL_SERVERFAULT, 0); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Create a handle object from a wire handle * * The wire handle is given in a buffer outlined by desc, which it * looks like we shouldn't modify. * * @param[in] export_pub Public export * @param[in] desc Handle buffer descriptor * @param[out] pub_handle The created handle * * @return FSAL status. */ static fsal_status_t create_handle(struct fsal_export *export_pub, struct gsh_buffdesc *desc, struct fsal_obj_handle **pub_handle, struct attrlist *attrs_out) { /* Full 'private' export structure */ struct export *export = container_of(export_pub, struct export, export); /* FSAL status to return */ fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; /* The FSAL specific portion of the handle received by the client */ vinodeno_t *vi = desc->addr; /* Ceph return code */ int rc = 0; /* Stat buffer */ struct ceph_statx stx; /* Handle to be created */ struct handle *handle = NULL; /* Inode pointer */ struct Inode *i; *pub_handle = NULL; if (desc->len != sizeof(vinodeno_t)) { status.major = ERR_FSAL_INVAL; return status; } /* Check our local cache first */ i = ceph_ll_get_inode(export->cmount, *vi); if (!i) { /* * Try the slow way, may not be in cache now. * * Currently, there is no interface for looking up a snapped * inode, so we just bail here in that case. */ if (vi->snapid.val != CEPH_NOSNAP) return ceph2fsal_error(-ESTALE); rc = ceph_ll_lookup_inode(export->cmount, vi->ino, &i); if (rc) return ceph2fsal_error(rc); } rc = fsal_ceph_ll_getattr(export->cmount, i, &stx, attrs_out ? CEPH_STATX_ATTR_MASK : CEPH_STATX_HANDLE_MASK, op_ctx->creds); if (rc < 0) return ceph2fsal_error(rc); construct_handle(&stx, i, export, &handle); if (attrs_out != NULL) ceph2fsal_attributes(&stx, attrs_out); *pub_handle = &handle->handle; return status; } /** * @brief Get dynamic filesystem info * * This function returns dynamic filesystem information for the given * export. * * @param[in] export_pub The public export handle * @param[out] info The dynamic FS information * * @return FSAL status. */ static fsal_status_t get_fs_dynamic_info(struct fsal_export *export_pub, struct fsal_obj_handle *obj_hdl, fsal_dynamicfsinfo_t *info) { /* Full 'private' export */ struct export *export = container_of(export_pub, struct export, export); /* Return value from Ceph calls */ int rc = 0; /* Filesystem stat */ struct statvfs vfs_st; rc = ceph_ll_statfs(export->cmount, export->root->i, &vfs_st); if (rc < 0) return ceph2fsal_error(rc); memset(info, 0, sizeof(fsal_dynamicfsinfo_t)); info->total_bytes = vfs_st.f_frsize * vfs_st.f_blocks; info->free_bytes = vfs_st.f_frsize * vfs_st.f_bfree; info->avail_bytes = vfs_st.f_frsize * vfs_st.f_bavail; info->total_files = vfs_st.f_files; info->free_files = vfs_st.f_ffree; info->avail_files = vfs_st.f_favail; info->time_delta.tv_sec = 1; info->time_delta.tv_nsec = 0; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Query the FSAL's capabilities * * This function queries the capabilities of an FSAL export. * * @param[in] export_pub The public export handle * @param[in] option The option to check * * @retval true if the option is supported. * @retval false if the option is unsupported (or unknown). */ static bool fs_supports(struct fsal_export *export_pub, fsal_fsinfo_options_t option) { struct fsal_staticfsinfo_t *info = ceph_staticinfo(export_pub->fsal); return fsal_supports(info, option); } /** * @brief Return the longest file supported * * This function returns the length of the longest file supported. * * @param[in] export_pub The public export * * @return UINT64_MAX. */ static uint64_t fs_maxfilesize(struct fsal_export *export_pub) { return UINT64_MAX; } /** * @brief Return the longest read supported * * This function returns the length of the longest read supported. * * @param[in] export_pub The public export * * @return 4 mebibytes. */ static uint32_t fs_maxread(struct fsal_export *export_pub) { return 0x400000; } /** * @brief Return the longest write supported * * This function returns the length of the longest write supported. * * @param[in] export_pub The public export * * @return 4 mebibytes. */ static uint32_t fs_maxwrite(struct fsal_export *export_pub) { return 0x400000; } /** * @brief Return the maximum number of hard links to a file * * This function returns the maximum number of hard links supported to * any file. * * @param[in] export_pub The public export * * @return 1024. */ static uint32_t fs_maxlink(struct fsal_export *export_pub) { /* Ceph does not like hard links. See the anchor table design. We should fix this, but have to do it in the Ceph core. */ return 1024; } /** * @brief Return the maximum size of a Ceph filename * * This function returns the maximum filename length. * * @param[in] export_pub The public export * * @return UINT32_MAX. */ static uint32_t fs_maxnamelen(struct fsal_export *export_pub) { /* Ceph actually supports filenames of unlimited length, at least according to the protocol docs. We may wish to constrain this later. */ return UINT32_MAX; } /** * @brief Return the maximum length of a Ceph path * * This function returns the maximum path length. * * @param[in] export_pub The public export * * @return UINT32_MAX. */ static uint32_t fs_maxpathlen(struct fsal_export *export_pub) { /* Similarly unlimited int he protocol */ return UINT32_MAX; } /** * @brief Return the lease time * * This function returns the lease time. * * @param[in] export_pub The public export * * @return five minutes. */ static struct timespec fs_lease_time(struct fsal_export *export_pub) { struct timespec lease = { 300, 0 }; return lease; } /** * @brief Return ACL support * * This function returns the export's ACL support. * * @param[in] export_pub The public export * * @return FSAL_ACLSUPPORT_DENY. */ static fsal_aclsupp_t fs_acl_support(struct fsal_export *export_pub) { return FSAL_ACLSUPPORT_DENY; } /** * @brief Return the attributes supported by this FSAL * * This function returns the mask of attributes this FSAL can support. * * @param[in] export_pub The public export * * @return supported_attributes as defined in internal.c. */ static attrmask_t fs_supported_attrs(struct fsal_export *export_pub) { return CEPH_SUPPORTED_ATTRS; } /** * @brief Return the mode under which the FSAL will create files * * This function modifies the default mode on any new file created. * * @param[in] export_pub The public export * * @return 0 (usually). Bits set here turn off bits in created files. */ static uint32_t fs_umask(struct fsal_export *export_pub) { return fsal_umask(ceph_staticinfo(export_pub->fsal)); } /** * @brief Return the mode for extended attributes * * This function returns the access mode applied to extended * attributes. Dubious. * * @param[in] export_pub The public export * * @return 0644. */ static uint32_t fs_xattr_access_rights(struct fsal_export *export_pub) { return fsal_xattr_access_rights(ceph_staticinfo(export_pub->fsal)); } /** * @brief Set operations for exports * * This function overrides operations that we've implemented, leaving * the rest for the default. * * @param[in,out] ops Operations vector */ void export_ops_init(struct export_ops *ops) { ops->release = release; ops->lookup_path = lookup_path; ops->wire_to_host = wire_to_host; ops->create_handle = create_handle; ops->get_fs_dynamic_info = get_fs_dynamic_info; ops->fs_supports = fs_supports; ops->fs_maxfilesize = fs_maxfilesize; ops->fs_maxread = fs_maxread; ops->fs_maxwrite = fs_maxwrite; ops->fs_maxlink = fs_maxlink; ops->fs_maxnamelen = fs_maxnamelen; ops->fs_maxpathlen = fs_maxpathlen; ops->fs_lease_time = fs_lease_time; ops->fs_acl_support = fs_acl_support; ops->fs_supported_attrs = fs_supported_attrs; ops->fs_umask = fs_umask; ops->fs_xattr_access_rights = fs_xattr_access_rights; ops->alloc_state = ceph_alloc_state; ops->free_state = ceph_free_state; #ifdef CEPH_PNFS export_ops_pnfs(ops); #endif /* CEPH_PNFS */ } nfs-ganesha-2.6.0/src/FSAL/FSAL_CEPH/handle.c000066400000000000000000002053311324272410200201510ustar00rootroot00000000000000/* * Copyright © 2012, CohortFS, LLC. * Author: Adam C. Emerson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @file FSAL_CEPH/handle.c * @author Adam C. Emerson * @date Mon Jul 9 15:18:47 2012 * * @brief Interface to handle functionality * * This function implements the interfaces on the struct * fsal_obj_handle type. */ #include "config.h" #ifdef LINUX #include /* for makedev(3) */ #endif #include #include #include "fsal.h" #include "fsal_types.h" #include "fsal_convert.h" #include "fsal_api.h" #include "internal.h" #include "nfs_exports.h" #include "sal_data.h" #include "statx_compat.h" /** * @brief Release an object * * This function looks up an object by name in a directory. * * @param[in] obj_pub The object to release * * @return FSAL status codes. */ static void release(struct fsal_obj_handle *obj_pub) { /* The private 'full' handle */ struct handle *obj = container_of(obj_pub, struct handle, handle); if (obj != obj->export->root) deconstruct_handle(obj); } /** * @brief Look up an object by name * * This function looks up an object by name in a directory. * * @param[in] dir_pub The directory in which to look up the object. * @param[in] path The name to look up. * @param[out] obj_pub The looked up object. * * @return FSAL status codes. */ static fsal_status_t lookup(struct fsal_obj_handle *dir_pub, const char *path, struct fsal_obj_handle **obj_pub, struct attrlist *attrs_out) { /* Generic status return */ int rc = 0; /* Stat output */ struct ceph_statx stx; /* The private 'full' export */ struct export *export = container_of(op_ctx->fsal_export, struct export, export); struct handle *dir = container_of(dir_pub, struct handle, handle); struct handle *obj = NULL; struct Inode *i = NULL; LogFullDebug(COMPONENT_FSAL, "Lookup %s", path); rc = fsal_ceph_ll_lookup(export->cmount, dir->i, path, &i, &stx, !!attrs_out, op_ctx->creds); if (rc < 0) return ceph2fsal_error(rc); construct_handle(&stx, i, export, &obj); if (attrs_out != NULL) ceph2fsal_attributes(&stx, attrs_out); *obj_pub = &obj->handle; return fsalstat(0, 0); } /** * @brief Read a directory * * This function reads the contents of a directory (excluding . and * .., which is ironic since the Ceph readdir call synthesizes them * out of nothing) and passes dirent information to the supplied * callback. * * @param[in] dir_pub The directory to read * @param[in] whence The cookie indicating resumption, NULL to start * @param[in] dir_state Opaque, passed to cb * @param[in] cb Callback that receives directory entries * @param[out] eof True if there are no more entries * * @return FSAL status. */ static fsal_status_t ceph_fsal_readdir(struct fsal_obj_handle *dir_pub, fsal_cookie_t *whence, void *dir_state, fsal_readdir_cb cb, attrmask_t attrmask, bool *eof) { /* Generic status return */ int rc = 0; /* The private 'full' export */ struct export *export = container_of(op_ctx->fsal_export, struct export, export); /* The private 'full' directory handle */ struct handle *dir = container_of(dir_pub, struct handle, handle); /* The director descriptor */ struct ceph_dir_result *dir_desc = NULL; /* Cookie marking the start of the readdir */ uint64_t start = 0; /* ceph_statx want mask */ unsigned int want = attrmask2ceph_want(attrmask); /* Return status */ fsal_status_t fsal_status = { ERR_FSAL_NO_ERROR, 0 }; rc = fsal_ceph_ll_opendir(export->cmount, dir->i, &dir_desc, op_ctx->creds); if (rc < 0) return ceph2fsal_error(rc); if (whence != NULL) start = *whence; ceph_seekdir(export->cmount, dir_desc, start); while (!(*eof)) { struct ceph_statx stx; struct dirent de; struct Inode *i = NULL; rc = fsal_ceph_readdirplus(export->cmount, dir_desc, dir->i, &de, &stx, want, 0, &i, op_ctx->creds); if (rc < 0) { fsal_status = ceph2fsal_error(rc); goto closedir; } else if (rc == 1) { struct handle *obj; struct attrlist attrs; enum fsal_dir_result cb_rc; /* skip . and .. */ if ((strcmp(de.d_name, ".") == 0) || (strcmp(de.d_name, "..") == 0)) { continue; } construct_handle(&stx, i, export, &obj); fsal_prepare_attrs(&attrs, attrmask); ceph2fsal_attributes(&stx, &attrs); cb_rc = cb(de.d_name, &obj->handle, &attrs, dir_state, de.d_off); fsal_release_attrs(&attrs); /* Read ahead not supported by this FSAL. */ if (cb_rc >= DIR_READAHEAD) goto closedir; } else if (rc == 0) { *eof = true; } else { /* Can't happen */ abort(); } } closedir: rc = ceph_ll_releasedir(export->cmount, dir_desc); if (rc < 0) fsal_status = ceph2fsal_error(rc); return fsal_status; } /** * @brief Create a directory * * This function creates a new directory. * * For support_ex, this method will handle attribute setting. The caller * MUST include the mode attribute and SHOULD NOT include the owner or * group attributes if they are the same as the op_ctx->cred. * * @param[in] dir_hdl Directory in which to create the directory * @param[in] name Name of directory to create * @param[in] attrib Attributes to set on newly created object * @param[out] new_obj Newly created object * * @note On success, @a new_obj has been ref'd * * @return FSAL status. */ static fsal_status_t ceph_fsal_mkdir(struct fsal_obj_handle *dir_hdl, const char *name, struct attrlist *attrib, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out) { /* Generic status return */ int rc = 0; /* The private 'full' export */ struct export *export = container_of(op_ctx->fsal_export, struct export, export); /* The private 'full' directory handle */ struct handle *dir = container_of(dir_hdl, struct handle, handle); /* Stat result */ struct ceph_statx stx; mode_t unix_mode; /* Newly created object */ struct handle *obj = NULL; struct Inode *i = NULL; fsal_status_t status; LogFullDebug(COMPONENT_FSAL, "mode = %o uid=%d gid=%d", attrib->mode, (int) op_ctx->creds->caller_uid, (int) op_ctx->creds->caller_gid); unix_mode = fsal2unix_mode(attrib->mode) & ~op_ctx->fsal_export->exp_ops.fs_umask(op_ctx->fsal_export); rc = fsal_ceph_ll_mkdir(export->cmount, dir->i, name, unix_mode, &i, &stx, !!attrs_out, op_ctx->creds); if (rc < 0) return ceph2fsal_error(rc); construct_handle(&stx, i, export, &obj); *new_obj = &obj->handle; /* We handled the mode above. */ FSAL_UNSET_MASK(attrib->valid_mask, ATTR_MODE); if (attrib->valid_mask) { /* Now per support_ex API, if there are any other attributes * set, go ahead and get them set now. */ status = (*new_obj)->obj_ops.setattr2(*new_obj, false, NULL, attrib); if (FSAL_IS_ERROR(status)) { /* Release the handle we just allocated. */ LogFullDebug(COMPONENT_FSAL, "setattr2 status=%s", fsal_err_txt(status)); (*new_obj)->obj_ops.release(*new_obj); *new_obj = NULL; } } else { status = fsalstat(ERR_FSAL_NO_ERROR, 0); if (attrs_out != NULL) { /* Since we haven't set any attributes other than what * was set on create, just use the stat results we used * to create the fsal_obj_handle. */ ceph2fsal_attributes(&stx, attrs_out); } } FSAL_SET_MASK(attrib->valid_mask, ATTR_MODE); return status; } /** * @brief Create a special file * * This function creates a new special file. * * For support_ex, this method will handle attribute setting. The caller * MUST include the mode attribute and SHOULD NOT include the owner or * group attributes if they are the same as the op_ctx->cred. * * @param[in] dir_hdl Directory in which to create the object * @param[in] name Name of object to create * @param[in] nodetype Type of special file to create * @param[in] dev Major and minor device numbers for block or * character special * @param[in] attrib Attributes to set on newly created object * @param[out] new_obj Newly created object * * @note On success, @a new_obj has been ref'd * * @return FSAL status. */ static fsal_status_t ceph_fsal_mknode(struct fsal_obj_handle *dir_hdl, const char *name, object_file_type_t nodetype, struct attrlist *attrib, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out) { #ifdef USE_FSAL_CEPH_MKNOD /* Generic status return */ int rc = 0; /* The private 'full' export */ struct export *export = container_of(op_ctx->fsal_export, struct export, export); /* The private 'full' directory handle */ struct handle *dir = container_of(dir_hdl, struct handle, handle); /* Newly opened file descriptor */ struct Inode *i = NULL; /* Status after create */ struct ceph_statx stx; mode_t unix_mode; dev_t unix_dev = 0; /* Newly created object */ struct handle *obj; fsal_status_t status; unix_mode = fsal2unix_mode(attrib->mode) & ~op_ctx->fsal_export->exp_ops.fs_umask(op_ctx->fsal_export); switch (nodetype) { case BLOCK_FILE: unix_mode |= S_IFBLK; unix_dev = makedev(attrib->rawdev.major, attrib->rawdev.minor); break; case CHARACTER_FILE: unix_mode |= S_IFCHR; unix_dev = makedev(attrib->rawdev.major, attrib->rawdev.minor); break; case FIFO_FILE: unix_mode |= S_IFIFO; break; case SOCKET_FILE: unix_mode |= S_IFSOCK; break; default: LogMajor(COMPONENT_FSAL, "Invalid node type in FSAL_mknode: %d", nodetype); return fsalstat(ERR_FSAL_INVAL, EINVAL); } rc = fsal_ceph_ll_mknod(export->cmount, dir->i, name, unix_mode, unix_dev, &i, &stx, !!attrs_out, op_ctx->creds); if (rc < 0) return ceph2fsal_error(rc); construct_handle(&stx, i, export, &obj); *new_obj = &obj->handle; /* We handled the mode and rawdev above. */ FSAL_UNSET_MASK(attrib->valid_mask, ATTR_MODE | ATTR_RAWDEV); if (attrib->valid_mask) { /* Now per support_ex API, if there are any other attributes * set, go ahead and get them set now. */ status = (*new_obj)->obj_ops.setattr2(*new_obj, false, NULL, attrib); if (FSAL_IS_ERROR(status)) { /* Release the handle we just allocated. */ LogFullDebug(COMPONENT_FSAL, "setattr2 status=%s", fsal_err_txt(status)); (*new_obj)->obj_ops.release(*new_obj); *new_obj = NULL; } } else { status = fsalstat(ERR_FSAL_NO_ERROR, 0); if (attrs_out != NULL) { /* Since we haven't set any attributes other than what * was set on create, just use the stat results we used * to create the fsal_obj_handle. */ ceph2fsal_attributes(&stx, attrs_out); } } FSAL_SET_MASK(attrib->valid_mask, ATTR_MODE); return status; #else return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); #endif } /** * @brief Create a symbolic link * * This function creates a new symbolic link. * * For support_ex, this method will handle attribute setting. The caller * MUST include the mode attribute and SHOULD NOT include the owner or * group attributes if they are the same as the op_ctx->cred. * * @param[in] dir_hdl Directory in which to create the object * @param[in] name Name of object to create * @param[in] link_path Content of symbolic link * @param[in] attrib Attributes to set on newly created object * @param[out] new_obj Newly created object * * @note On success, @a new_obj has been ref'd * * @return FSAL status. */ static fsal_status_t ceph_fsal_symlink(struct fsal_obj_handle *dir_hdl, const char *name, const char *link_path, struct attrlist *attrib, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out) { /* Generic status return */ int rc = 0; /* The private 'full' export */ struct export *export = container_of(op_ctx->fsal_export, struct export, export); /* The private 'full' directory handle */ struct handle *dir = container_of(dir_hdl, struct handle, handle); /* Stat result */ struct ceph_statx stx; struct Inode *i = NULL; /* Newly created object */ struct handle *obj = NULL; fsal_status_t status; rc = fsal_ceph_ll_symlink(export->cmount, dir->i, name, link_path, &i, &stx, !!attrs_out, op_ctx->creds); if (rc < 0) return ceph2fsal_error(rc); construct_handle(&stx, i, export, &obj); *new_obj = &obj->handle; /* We handled the mode above. */ FSAL_UNSET_MASK(attrib->valid_mask, ATTR_MODE); if (attrib->valid_mask) { /* Now per support_ex API, if there are any other attributes * set, go ahead and get them set now. */ status = (*new_obj)->obj_ops.setattr2(*new_obj, false, NULL, attrib); if (FSAL_IS_ERROR(status)) { /* Release the handle we just allocated. */ LogFullDebug(COMPONENT_FSAL, "setattr2 status=%s", fsal_err_txt(status)); (*new_obj)->obj_ops.release(*new_obj); *new_obj = NULL; } } else { status = fsalstat(ERR_FSAL_NO_ERROR, 0); if (attrs_out != NULL) { /* Since we haven't set any attributes other than what * was set on create, just use the stat results we used * to create the fsal_obj_handle. */ ceph2fsal_attributes(&stx, attrs_out); } } FSAL_SET_MASK(attrib->valid_mask, ATTR_MODE); return status; } /** * @brief Retrieve the content of a symlink * * This function allocates a buffer, copying the symlink content into * it. * * @param[in] link_pub The handle for the link * @param[out] content_buf Buffdesc for symbolic link * @param[in] refresh true if the underlying content should be * refreshed. * * @return FSAL status. */ static fsal_status_t ceph_fsal_readlink(struct fsal_obj_handle *link_pub, struct gsh_buffdesc *content_buf, bool refresh) { /* Generic status return */ int rc = 0; /* The private 'full' export */ struct export *export = container_of(op_ctx->fsal_export, struct export, export); /* The private 'full' directory handle */ struct handle *link = container_of(link_pub, struct handle, handle); /* Pointer to the Ceph link content */ char content[PATH_MAX]; rc = fsal_ceph_ll_readlink(export->cmount, link->i, content, PATH_MAX, op_ctx->creds); if (rc < 0) return ceph2fsal_error(rc); /* XXX in Ceph through 1/2016, ceph_ll_readlink returns the * length of the path copied (truncated to 32 bits) in rc, * and it cannot exceed the passed buffer size */ content_buf->addr = gsh_strldup(content, MIN(rc, (PATH_MAX-1)), &content_buf->len); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Freshen and return attributes * * This function freshens and returns the attributes of the given * file. * * @param[in] handle_pub Object to interrogate * * @return FSAL status. */ static fsal_status_t getattrs(struct fsal_obj_handle *handle_pub, struct attrlist *attrs) { /* Generic status return */ int rc = 0; /* The private 'full' export */ struct export *export = container_of(op_ctx->fsal_export, struct export, export); /* The private 'full' directory handle */ struct handle *handle = container_of(handle_pub, struct handle, handle); /* Stat buffer */ struct ceph_statx stx; rc = fsal_ceph_ll_getattr(export->cmount, handle->i, &stx, CEPH_STATX_ATTR_MASK, op_ctx->creds); LogDebug(COMPONENT_FSAL, "getattr returned %d", rc); if (rc < 0) { if (attrs->request_mask & ATTR_RDATTR_ERR) { /* Caller asked for error to be visible. */ attrs->valid_mask = ATTR_RDATTR_ERR; } return ceph2fsal_error(rc); } ceph2fsal_attributes(&stx, attrs); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Create a hard link * * This function creates a link from the supplied file to a new name * in a new directory. * * @param[in] handle_pub File to link * @param[in] destdir_pub Directory in which to create link * @param[in] name Name of link * * @return FSAL status. */ static fsal_status_t ceph_fsal_link(struct fsal_obj_handle *handle_pub, struct fsal_obj_handle *destdir_pub, const char *name) { /* Generic status return */ int rc = 0; /* The private 'full' export */ struct export *export = container_of(op_ctx->fsal_export, struct export, export); /* The private 'full' object handle */ struct handle *handle = container_of(handle_pub, struct handle, handle); /* The private 'full' destination directory handle */ struct handle *destdir = container_of(destdir_pub, struct handle, handle); rc = fsal_ceph_ll_link(export->cmount, handle->i, destdir->i, name, op_ctx->creds); if (rc < 0) return ceph2fsal_error(rc); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Rename a file * * This function renames a file, possibly moving it into another * directory. We assume most checks are done by the caller. * * @param[in] olddir_pub Source directory * @param[in] old_name Original name * @param[in] newdir_pub Destination directory * @param[in] new_name New name * * @return FSAL status. */ static fsal_status_t ceph_fsal_rename(struct fsal_obj_handle *obj_hdl, struct fsal_obj_handle *olddir_pub, const char *old_name, struct fsal_obj_handle *newdir_pub, const char *new_name) { /* Generic status return */ int rc = 0; /* The private 'full' export */ struct export *export = container_of(op_ctx->fsal_export, struct export, export); /* The private 'full' object handle */ struct handle *olddir = container_of(olddir_pub, struct handle, handle); /* The private 'full' destination directory handle */ struct handle *newdir = container_of(newdir_pub, struct handle, handle); rc = fsal_ceph_ll_rename(export->cmount, olddir->i, old_name, newdir->i, new_name, op_ctx->creds); if (rc < 0) return ceph2fsal_error(rc); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Remove a name * * This function removes a name from the filesystem and possibly * deletes the associated file. Directories must be empty to be * removed. * * @param[in] dir_pub Parent directory * @param[in] name Name to remove * * @return FSAL status. */ static fsal_status_t ceph_fsal_unlink(struct fsal_obj_handle *dir_pub, struct fsal_obj_handle *obj_pub, const char *name) { /* Generic status return */ int rc = 0; /* The private 'full' export */ struct export *export = container_of(op_ctx->fsal_export, struct export, export); /* The private 'full' object handle */ struct handle *dir = container_of(dir_pub, struct handle, handle); LogFullDebug(COMPONENT_FSAL, "Unlink %s, I think it's a %s", name, object_file_type_to_str(obj_pub->type)); if (obj_pub->type != DIRECTORY) { rc = fsal_ceph_ll_unlink(export->cmount, dir->i, name, op_ctx->creds); } else { rc = fsal_ceph_ll_rmdir(export->cmount, dir->i, name, op_ctx->creds); } if (rc < 0) { LogDebug(COMPONENT_FSAL, "Unlink %s returned %s (%d)", name, strerror(-rc), -rc); return ceph2fsal_error(rc); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Open a ceph_fd. * * @param[in] myself The ceph internal object handle * @param[in] openflags Mode for open * @param[in] posix_flags POSIX open flags for open * @param[in] my_fd The ceph_fd to open * * @return FSAL status. */ fsal_status_t ceph_open_my_fd(struct handle *myself, fsal_openflags_t openflags, int posix_flags, struct ceph_fd *my_fd) { int rc; struct export *export = container_of(op_ctx->fsal_export, struct export, export); LogFullDebug(COMPONENT_FSAL, "my_fd = %p my_fd->fd = %p openflags = %x, posix_flags = %x", my_fd, my_fd->fd, openflags, posix_flags); assert(my_fd->fd == NULL && my_fd->openflags == FSAL_O_CLOSED && openflags != 0); LogFullDebug(COMPONENT_FSAL, "openflags = %x, posix_flags = %x", openflags, posix_flags); rc = fsal_ceph_ll_open(export->cmount, myself->i, posix_flags, &my_fd->fd, op_ctx->creds); if (rc < 0) { my_fd->fd = NULL; LogFullDebug(COMPONENT_FSAL, "open failed with %s", strerror(-rc)); return ceph2fsal_error(rc); } /* Save the file descriptor, make sure we only save the * open modes that actually represent the open file. */ LogFullDebug(COMPONENT_FSAL, "fd = %p, new openflags = %x", my_fd->fd, openflags); my_fd->openflags = openflags; return fsalstat(ERR_FSAL_NO_ERROR, 0); } fsal_status_t ceph_close_my_fd(struct handle *handle, struct ceph_fd *my_fd) { int rc = 0; fsal_status_t status = fsalstat(ERR_FSAL_NO_ERROR, 0); if (my_fd->fd != NULL && my_fd->openflags != FSAL_O_CLOSED) { rc = ceph_ll_close(handle->export->cmount, my_fd->fd); if (rc < 0) status = ceph2fsal_error(rc); my_fd->fd = NULL; my_fd->openflags = FSAL_O_CLOSED; } return status; } /** * @brief Function to open an fsal_obj_handle's global file descriptor. * * @param[in] obj_hdl File on which to operate * @param[in] openflags Mode for open * @param[out] fd File descriptor that is to be used * * @return FSAL status. */ static fsal_status_t ceph_open_func(struct fsal_obj_handle *obj_hdl, fsal_openflags_t openflags, struct fsal_fd *fd) { int posix_flags = 0; fsal2posix_openflags(openflags, &posix_flags); return ceph_open_my_fd(container_of(obj_hdl, struct handle, handle), openflags, posix_flags, (struct ceph_fd *)fd); } /** * @brief Function to close an fsal_obj_handle's global file descriptor. * * @param[in] obj_hdl File on which to operate * @param[in] fd File handle to close * * @return FSAL status. */ static fsal_status_t ceph_close_func(struct fsal_obj_handle *obj_hdl, struct fsal_fd *fd) { return ceph_close_my_fd(container_of(obj_hdl, struct handle, handle), (struct ceph_fd *)fd); } /** * @brief Close a file * * This function closes a file, freeing resources used for read/write * access and releasing capabilities. * * @param[in] obj_hdl File to close * * @return FSAL status. */ static fsal_status_t ceph_fsal_close(struct fsal_obj_handle *obj_hdl) { fsal_status_t status; /* The private 'full' object handle */ struct handle *handle = container_of(obj_hdl, struct handle, handle); if (handle->fd.openflags == FSAL_O_CLOSED) return fsalstat(ERR_FSAL_NOT_OPENED, 0); /* Take write lock on object to protect file descriptor. * This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->obj_lock); status = ceph_close_my_fd(handle, &handle->fd); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /** * @brief Allocate a state_t structure * * Note that this is not expected to fail since memory allocation is * expected to abort on failure. * * @param[in] exp_hdl Export state_t will be associated with * @param[in] state_type Type of state to allocate * @param[in] related_state Related state if appropriate * * @returns a state structure. */ struct state_t *ceph_alloc_state(struct fsal_export *exp_hdl, enum state_type state_type, struct state_t *related_state) { struct state_t *state; struct ceph_fd *my_fd; state = init_state(gsh_calloc(1, sizeof(struct ceph_state_fd)), exp_hdl, state_type, related_state); my_fd = &container_of(state, struct ceph_state_fd, state)->ceph_fd; my_fd->fd = NULL; my_fd->openflags = FSAL_O_CLOSED; PTHREAD_RWLOCK_init(&my_fd->fdlock, NULL); return state; } /** * @brief free a ceph_state_fd structure * * @param[in] exp_hdl Export state_t will be associated with * @param[in] state Related state if appropriate * */ void ceph_free_state(struct fsal_export *exp_hdl, struct state_t *state) { struct ceph_state_fd *state_fd = container_of(state, struct ceph_state_fd, state); struct ceph_fd *my_fd = &state_fd->ceph_fd; PTHREAD_RWLOCK_destroy(&my_fd->fdlock); gsh_free(state_fd); } /** * @brief Merge a duplicate handle with an original handle * * This function is used if an upper layer detects that a duplicate * object handle has been created. It allows the FSAL to merge anything * from the duplicate back into the original. * * The caller must release the object (the caller may have to close * files if the merge is unsuccessful). * * @param[in] orig_hdl Original handle * @param[in] dupe_hdl Handle to merge into original * * @return FSAL status. * */ fsal_status_t ceph_merge(struct fsal_obj_handle *orig_hdl, struct fsal_obj_handle *dupe_hdl) { fsal_status_t status = {ERR_FSAL_NO_ERROR, 0}; if (orig_hdl->type == REGULAR_FILE && dupe_hdl->type == REGULAR_FILE) { /* We need to merge the share reservations on this file. * This could result in ERR_FSAL_SHARE_DENIED. */ struct handle *orig, *dupe; orig = container_of(orig_hdl, struct handle, handle); dupe = container_of(dupe_hdl, struct handle, handle); /* This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&orig_hdl->obj_lock); status = merge_share(&orig->share, &dupe->share); PTHREAD_RWLOCK_unlock(&orig_hdl->obj_lock); } return status; } static bool ceph_check_verifier_stat(struct ceph_statx *stx, fsal_verifier_t verifier) { uint32_t verf_hi, verf_lo; memcpy(&verf_hi, verifier, sizeof(uint32_t)); memcpy(&verf_lo, verifier + sizeof(uint32_t), sizeof(uint32_t)); LogFullDebug(COMPONENT_FSAL, "Passed verifier %"PRIx32" %"PRIx32 " file verifier %"PRIx32" %"PRIx32, verf_hi, verf_lo, (uint32_t)stx->stx_atime.tv_sec, (uint32_t)stx->stx_mtime.tv_sec); return stx->stx_atime.tv_sec == verf_hi && stx->stx_mtime.tv_sec == verf_lo; } /** * @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. * * If attributes are not set on create, the FSAL will set some minimal * attributes (for example, mode might be set to 0600). * * 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] caller_perm_check The caller must do a permission check * * @return FSAL status. */ fsal_status_t ceph_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 retval = 0; mode_t unix_mode = 0; fsal_status_t status = {0, 0}; struct ceph_fd *my_fd = NULL; struct handle *myself, *hdl = NULL; struct ceph_statx stx; bool truncated; bool created = false; struct export *export = container_of(op_ctx->fsal_export, struct export, export); struct Inode *i = NULL; Fh *fd; LogAttrlist(COMPONENT_FSAL, NIV_FULL_DEBUG, "attrs ", attrib_set, false); if (state != NULL) my_fd = &container_of(state, struct ceph_state_fd, state)->ceph_fd; myself = container_of(obj_hdl, struct handle, handle); 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->obj_lock); /* Check share reservation conflicts. */ status = check_share_conflict(&myself->share, openflags, false); if (FSAL_IS_ERROR(status)) { PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /* Take the share reservation now by updating the * counters. */ update_share_counters(&myself->share, FSAL_O_CLOSED, openflags); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); } else { /* We need to use the global fd to continue, and take * the lock to protect it. */ my_fd = &myself->fd; PTHREAD_RWLOCK_wrlock(&obj_hdl->obj_lock); } if (my_fd->openflags != FSAL_O_CLOSED) { ceph_close_my_fd(myself, my_fd); } status = ceph_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->obj_lock); return status; } else { /* Error - need to release the share */ goto undo_share; } } if (createmode >= FSAL_EXCLUSIVE || truncated) { /* Refresh the attributes */ retval = fsal_ceph_ll_getattr(export->cmount, myself->i, &stx, !!attrs_out, op_ctx->creds); if (retval == 0) { LogFullDebug(COMPONENT_FSAL, "New size = %"PRIx64, stx.stx_size); } else { /* Because we have an inode ref, we never * get EBADF like other FSALs might see. */ status = ceph2fsal_error(retval); } /* Now check verifier for exclusive, but not for * FSAL_EXCLUSIVE_9P. */ if (!FSAL_IS_ERROR(status) && createmode >= FSAL_EXCLUSIVE && createmode != FSAL_EXCLUSIVE_9P && !ceph_check_verifier_stat(&stx, verifier)) { /* Verifier didn't match, return EEXIST */ status = fsalstat(posix2fsal_error(EEXIST), EEXIST); } if (attrs_out) { /* Save out new attributes */ ceph2fsal_attributes(&stx, attrs_out); } } else if (attrs_out && attrs_out->request_mask & ATTR_RDATTR_ERR) { attrs_out->valid_mask &= ATTR_RDATTR_ERR; } if (state == NULL) { /* If no state, release the lock taken above and return * status. If success, we haven't done any permission * check so ask the caller to do so. */ PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); *caller_perm_check = !FSAL_IS_ERROR(status); return status; } if (!FSAL_IS_ERROR(status)) { /* Return success. We haven't done any permission * check so ask the caller to do so. */ *caller_perm_check = true; return status; } (void) ceph_close_my_fd(myself, 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->obj_lock); update_share_counters(&myself->share, openflags, FSAL_O_CLOSED); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_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. */ if (createmode == FSAL_NO_CREATE) { /* Non creation case, libcephfs doesn't have open by name so we * have to do a lookup and then handle as an open by handle. */ struct fsal_obj_handle *temp = NULL; /* We don't have open by name... */ status = obj_hdl->obj_ops.lookup(obj_hdl, name, &temp, NULL); if (FSAL_IS_ERROR(status)) { LogFullDebug(COMPONENT_FSAL, "lookup returned %s", fsal_err_txt(status)); return status; } /* Now call ourselves without name and attributes to open. */ status = obj_hdl->obj_ops.open2(temp, state, openflags, FSAL_NO_CREATE, NULL, NULL, verifier, new_obj, attrs_out, caller_perm_check); if (FSAL_IS_ERROR(status)) { /* Release the object we found by lookup. */ temp->obj_ops.release(temp); LogFullDebug(COMPONENT_FSAL, "open returned %s", fsal_err_txt(status)); } return status; } /* Now add in O_CREAT and O_EXCL. * Even with FSAL_UNGUARDED we try exclusive create first so * we can safely set attributes. */ 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->valid_mask, ATTR_MODE); } if (createmode == FSAL_UNCHECKED && (attrib_set->valid_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; } retval = fsal_ceph_ll_create(export->cmount, myself->i, name, unix_mode, posix_flags, &i, &fd, &stx, !!attrs_out, op_ctx->creds); if (retval < 0) { LogFullDebug(COMPONENT_FSAL, "Create %s failed with %s", name, strerror(-retval)); } if (retval == -EEXIST && createmode == FSAL_UNCHECKED) { /* We tried to create O_EXCL to set attributes and failed. * Remove O_EXCL and retry, also remember not to set attributes. * 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; retval = fsal_ceph_ll_create(export->cmount, myself->i, name, unix_mode, posix_flags, &i, &fd, &stx, !!attrs_out, op_ctx->creds); if (retval < 0) { LogFullDebug(COMPONENT_FSAL, "Non-exclusive Create %s failed with %s", name, strerror(-retval)); } } if (retval < 0) { return ceph2fsal_error(retval); } /* 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. */ created = (posix_flags & O_EXCL) != 0; /** @todo FSF: Note that the current implementation of ceph_ll_create * does not accept an alt groups list, so it is possible * a create (including an UNCHECKED create on an already * existing file) would fail because the directory or * file was owned by a group other than the primary group. * Conversely, it could also succeed when it should have * failed if other is granted more permission than * one of the alt groups). */ /* Since we did the ceph_ll_create using the caller's credentials, * we don't need to do an additional permission check. */ *caller_perm_check = false; construct_handle(&stx, i, export, &hdl); /* 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->fd; my_fd->fd = fd; my_fd->openflags = openflags; *new_obj = &hdl->handle; if (created && attrib_set->valid_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. */ status = (*new_obj)->obj_ops.setattr2(*new_obj, false, state, attrib_set); if (FSAL_IS_ERROR(status)) goto fileerr; if (attrs_out != NULL) { status = (*new_obj)->obj_ops.getattrs(*new_obj, attrs_out); if (FSAL_IS_ERROR(status) && (attrs_out->request_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. */ ceph2fsal_attributes(&stx, attrs_out); } 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)->obj_lock); /* Take the share reservation now by updating the counters. */ update_share_counters(&hdl->share, FSAL_O_CLOSED, openflags); PTHREAD_RWLOCK_unlock(&(*new_obj)->obj_lock); } return fsalstat(ERR_FSAL_NO_ERROR, 0); fileerr: /* Close the file we just opened. */ (void) ceph_close_my_fd(container_of(*new_obj, struct handle, handle), my_fd); /* Release the handle we just allocated. */ (*new_obj)->obj_ops.release(*new_obj); *new_obj = NULL; if (created) { /* Remove the file we just created */ fsal_ceph_ll_unlink(export->cmount, myself->i, name, op_ctx->creds); } return status; } /** * @brief Return open status of a state. * * This function returns open flags representing the current open * status for a state. The state_lock must be held. * * @param[in] obj_hdl File on which to operate * @param[in] state File state to interrogate * * @retval Flags representing current open status */ fsal_openflags_t ceph_status2(struct fsal_obj_handle *obj_hdl, struct state_t *state) { struct ceph_fd *my_fd = (struct ceph_fd *)(state + 1); return my_fd->openflags; } /** * @brief Re-open a file that may be already opened * * This function supports changing the access mode of a share reservation and * thus should only be called with a share state. The state_lock must be held. * * This MAY be used to open a file the first time if there is no need for * open by name or create semantics. One example would be 9P lopen. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * @param[in] openflags Mode for re-open * * @return FSAL status. */ fsal_status_t ceph_reopen2(struct fsal_obj_handle *obj_hdl, struct state_t *state, fsal_openflags_t openflags) { struct ceph_fd temp_fd = { FSAL_O_CLOSED, PTHREAD_RWLOCK_INITIALIZER, NULL }; struct ceph_fd *my_fd = &temp_fd, *my_share_fd; struct handle *myself = container_of(obj_hdl, struct handle, handle); fsal_status_t status = {0, 0}; int posix_flags = 0; fsal_openflags_t old_openflags; my_share_fd = &container_of(state, struct ceph_state_fd, state)->ceph_fd; fsal2posix_openflags(openflags, &posix_flags); /* This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->obj_lock); old_openflags = my_share_fd->openflags; /* We can conflict with old share, so go ahead and check now. */ status = check_share_conflict(&myself->share, openflags, false); if (FSAL_IS_ERROR(status)) { PTHREAD_RWLOCK_unlock(&obj_hdl->obj_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->share, old_openflags, openflags); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); status = ceph_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. Make sure no one is using the fd that we are * about to close! */ PTHREAD_RWLOCK_wrlock(&my_share_fd->fdlock); ceph_close_my_fd(myself, my_share_fd); my_share_fd->fd = my_fd->fd; my_share_fd->openflags = my_fd->openflags; PTHREAD_RWLOCK_unlock(&my_share_fd->fdlock); } 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->obj_lock); update_share_counters(&myself->share, openflags, old_openflags); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); } return status; } /** * @brief Find a file descriptor for a read or write operation. * * We do not need file descriptors for non-regular files, so this never has to * handle them. */ fsal_status_t ceph_find_fd(Fh **fd, struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, fsal_openflags_t openflags, bool *has_lock, bool *closefd, bool open_for_locks) { struct handle *myself = container_of(obj_hdl, struct handle, handle); struct ceph_fd temp_fd = { FSAL_O_CLOSED, PTHREAD_RWLOCK_INITIALIZER, NULL }; struct ceph_fd *out_fd = &temp_fd; fsal_status_t status = {ERR_FSAL_NO_ERROR, 0}; bool reusing_open_state_fd = false; status = fsal_find_fd((struct fsal_fd **)&out_fd, obj_hdl, (struct fsal_fd *)&myself->fd, &myself->share, bypass, state, openflags, ceph_open_func, ceph_close_func, has_lock, closefd, open_for_locks, &reusing_open_state_fd); LogFullDebug(COMPONENT_FSAL, "fd = %p", out_fd->fd); *fd = out_fd->fd; return status; } /** * @brief Read data from a file * * This function reads data from the given file. The FSAL must be able to * perform the read whether a state is presented or not. This function also * is expected to handle properly bypassing or not share reservations. * * @param[in] obj_hdl File on which to operate * @param[in] bypass If state doesn't indicate a share reservation, * bypass any deny read * @param[in] state state_t to use for this operation * @param[in] offset Position from which to read * @param[in] buffer_size Amount of data to read * @param[out] buffer Buffer to which data are to be copied * @param[out] read_amount Amount of data read * @param[out] end_of_file true if the end of file has been reached * @param[in,out] info more information about the data * * @return FSAL status. */ fsal_status_t ceph_read2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buffer_size, void *buffer, size_t *read_amount, bool *end_of_file, struct io_info *info) { struct handle *myself = container_of(obj_hdl, struct handle, handle); Fh *my_fd = NULL; ssize_t nb_read; fsal_status_t status; bool has_lock = false; bool closefd = false; struct export *export = container_of(op_ctx->fsal_export, struct export, export); struct ceph_fd *ceph_fd = NULL; if (info != NULL) { /* Currently we don't support READ_PLUS */ return fsalstat(ERR_FSAL_NOTSUPP, 0); } /* Acquire state's fdlock to prevent OPEN upgrade closing the * file descriptor while we use it. */ if (state) { ceph_fd = &container_of(state, struct ceph_state_fd, state)->ceph_fd; PTHREAD_RWLOCK_rdlock(&ceph_fd->fdlock); } /* Get a usable file descriptor */ status = ceph_find_fd(&my_fd, obj_hdl, bypass, state, FSAL_O_READ, &has_lock, &closefd, false); if (FSAL_IS_ERROR(status)) goto out; nb_read = ceph_ll_read(export->cmount, my_fd, offset, buffer_size, buffer); if (offset == -1 || nb_read < 0) { status = ceph2fsal_error(nb_read); goto out; } *read_amount = nb_read; *end_of_file = nb_read == 0; #if 0 /** @todo * * Is this all we really need to do to support READ_PLUS? Will anyone * ever get upset that we don't return holes, even for blocks of all * zeroes? * */ if (info != NULL) { info->io_content.what = NFS4_CONTENT_DATA; info->io_content.data.d_offset = offset + nb_read; info->io_content.data.d_data.data_len = nb_read; info->io_content.data.d_data.data_val = buffer; } #endif out: if (ceph_fd) PTHREAD_RWLOCK_unlock(&ceph_fd->fdlock); if (closefd) (void) ceph_ll_close(myself->export->cmount, my_fd); if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /** * @brief Write data to a file * * This function writes data to a file. The FSAL must be able to * perform the write whether a state is presented or not. This function also * is expected to handle properly bypassing or not share reservations. Even * with bypass == true, it will enforce a mandatory (NFSv4) deny_write if * an appropriate state is not passed). * * The FSAL is expected to enforce sync if necessary. * * @param[in] obj_hdl File on which to operate * @param[in] bypass If state doesn't indicate a share reservation, * bypass any non-mandatory deny write * @param[in] state state_t to use for this operation * @param[in] offset Position at which to write * @param[in] buffer Data to be written * @param[in,out] fsal_stable In, if on, the fsal is requested to write data * to stable store. Out, the fsal reports what * it did. * @param[in,out] info more information about the data * * @return FSAL status. */ fsal_status_t ceph_write2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buffer_size, void *buffer, size_t *wrote_amount, bool *fsal_stable, struct io_info *info) { struct handle *myself = container_of(obj_hdl, struct handle, handle); ssize_t nb_written; fsal_status_t status; int retval = 0; Fh *my_fd = NULL; bool has_lock = false; bool closefd = false; fsal_openflags_t openflags = FSAL_O_WRITE; struct export *export = container_of(op_ctx->fsal_export, struct export, export); struct ceph_fd *ceph_fd = NULL; if (info != NULL) { /* Currently we don't support WRITE_PLUS */ return fsalstat(ERR_FSAL_NOTSUPP, 0); } /* Acquire state's fdlock to prevent OPEN upgrade closing the * file descriptor while we use it. */ if (state) { ceph_fd = &container_of(state, struct ceph_state_fd, state)->ceph_fd; PTHREAD_RWLOCK_rdlock(&ceph_fd->fdlock); } /* Get a usable file descriptor */ status = ceph_find_fd(&my_fd, obj_hdl, bypass, state, openflags, &has_lock, &closefd, false); if (FSAL_IS_ERROR(status)) { LogDebug(COMPONENT_FSAL, "find_fd failed %s", msg_fsal_err(status.major)); goto out; } nb_written = ceph_ll_write(export->cmount, my_fd, offset, buffer_size, buffer); if (nb_written < 0) { status = ceph2fsal_error(nb_written); goto out; } *wrote_amount = nb_written; if (*fsal_stable) { retval = ceph_ll_fsync(export->cmount, my_fd, false); if (retval < 0) status = ceph2fsal_error(retval); } out: if (ceph_fd) PTHREAD_RWLOCK_unlock(&ceph_fd->fdlock); if (closefd) (void) ceph_ll_close(myself->export->cmount, my_fd); if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /** * @brief Commit written data * * This function flushes possibly buffered data to a file. This method * differs from commit due to the need to interact with share reservations * and the fact that the FSAL manages the state of "file descriptors". The * FSAL must be able to perform this operation without being passed a specific * state. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * @param[in] offset Start of range to commit * @param[in] len Length of range to commit * * @return FSAL status. */ fsal_status_t ceph_commit2(struct fsal_obj_handle *obj_hdl, off_t offset, size_t len) { struct handle *myself = container_of(obj_hdl, struct handle, handle); fsal_status_t status; int retval; struct ceph_fd temp_fd = { FSAL_O_CLOSED, PTHREAD_RWLOCK_INITIALIZER, NULL }; struct ceph_fd *out_fd = &temp_fd; bool has_lock = false; bool closefd = false; struct export *export = container_of(op_ctx->fsal_export, struct export, export); /* Make sure file is open in appropriate mode. * Do not check share reservation. */ status = fsal_reopen_obj(obj_hdl, false, false, FSAL_O_WRITE, (struct fsal_fd *)&myself->fd, &myself->share, ceph_open_func, ceph_close_func, (struct fsal_fd **)&out_fd, &has_lock, &closefd); if (!FSAL_IS_ERROR(status)) { retval = ceph_ll_fsync(export->cmount, out_fd->fd, false); if (retval < 0) status = ceph2fsal_error(retval); } if (closefd) (void) ceph_ll_close(myself->export->cmount, out_fd->fd); if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } #ifdef USE_FSAL_CEPH_SETLK /** * @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 ceph_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 handle *myself = container_of(obj_hdl, struct handle, handle); struct flock lock_args; fsal_status_t status = {0, 0}; int retval = 0; Fh *my_fd = NULL; bool has_lock = false; bool closefd = false; bool bypass = false; fsal_openflags_t openflags = FSAL_O_RDWR; struct export *export = container_of(op_ctx->fsal_export, struct export, export); struct ceph_fd *ceph_fd = NULL; 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) { /* 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) { 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) { 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(%ld), request_lock_length(%" PRIu64 ")", lock_args.l_len, request_lock->lock_length); return fsalstat(ERR_FSAL_BAD_RANGE, 0); } /* Acquire state's fdlock to prevent OPEN upgrade closing the * file descriptor while we use it. */ if (state) { ceph_fd = &container_of(state, struct ceph_state_fd, state)->ceph_fd; PTHREAD_RWLOCK_rdlock(&ceph_fd->fdlock); } /* Get a usable file descriptor */ status = ceph_find_fd(&my_fd, obj_hdl, bypass, state, openflags, &has_lock, &closefd, true); if (FSAL_IS_ERROR(status)) { LogCrit(COMPONENT_FSAL, "Unable to find fd for lock operation"); return status; } if (lock_op == FSAL_OP_LOCKT) { retval = ceph_ll_getlk(export->cmount, my_fd, &lock_args, (uint64_t) owner); } else { retval = ceph_ll_setlk(export->cmount, my_fd, &lock_args, (uint64_t) owner, false); } if (retval < 0) { LogDebug(COMPONENT_FSAL, "%s returned %d %s", lock_op == FSAL_OP_LOCKT ? "ceph_ll_getlk" : "ceph_ll_setlk", -retval, strerror(-retval)); if (conflicting_lock != NULL) { int retval2; /* Get the conflicting lock */ retval2 = ceph_ll_getlk(export->cmount, my_fd, &lock_args, (uint64_t) owner); if (retval2 < 0) { LogCrit(COMPONENT_FSAL, "After failing a lock request, I couldn't even get the details of who owns the lock, error %d %s", -retval2, strerror(-retval2)); goto err; } 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 (ceph_fd) PTHREAD_RWLOCK_unlock(&ceph_fd->fdlock); if (closefd) (void) ceph_ll_close(myself->export->cmount, my_fd); if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return ceph2fsal_error(retval); } #endif #ifdef USE_FSAL_CEPH_LL_DELEGATION static void ceph_deleg_cb(Fh *fh, void *vhdl) { fsal_status_t fsal_status; struct fsal_obj_handle *obj_hdl = vhdl; struct handle *hdl = container_of(obj_hdl, struct handle, handle); struct gsh_buffdesc key = { .addr = &hdl->vi, .len = sizeof(vinodeno_t) }; LogDebug(COMPONENT_FSAL, "Recalling delegations on %p", hdl); fsal_status = up_async_delegrecall(general_fridge, hdl->up_ops, &key, NULL, NULL); if (FSAL_IS_ERROR(fsal_status)) LogCrit(COMPONENT_FSAL, "Unable to queue delegrecall for 0x%p: %s", hdl, fsal_err_txt(fsal_status)); } static fsal_status_t ceph_lease_op2(struct fsal_obj_handle *obj_hdl, state_t *state, void *owner, fsal_deleg_t deleg) { struct handle *myself = container_of(obj_hdl, struct handle, handle); fsal_status_t status = {0, 0}; int retval = 0; Fh *my_fd = NULL; unsigned int cmd; bool has_lock = false; bool closefd = false; bool bypass = false; fsal_openflags_t openflags = FSAL_O_READ; struct ceph_fd *ceph_fd = NULL; switch (deleg) { case FSAL_DELEG_NONE: cmd = CEPH_DELEGATION_NONE; break; case FSAL_DELEG_RD: cmd = CEPH_DELEGATION_RD; break; case FSAL_DELEG_WR: /* No write delegations (yet!) */ return ceph2fsal_error(-ENOTSUP); default: LogCrit(COMPONENT_FSAL, "Unknown requested lease state"); return ceph2fsal_error(-EINVAL); }; /* Acquire state's fdlock to prevent OPEN upgrade closing the * file descriptor while we use it. */ if (state) { ceph_fd = &container_of(state, struct ceph_state_fd, state)->ceph_fd; PTHREAD_RWLOCK_rdlock(&ceph_fd->fdlock); } /* Get a usable file descriptor */ status = ceph_find_fd(&my_fd, obj_hdl, bypass, state, openflags, &has_lock, &closefd, false); if (FSAL_IS_ERROR(status)) { LogCrit(COMPONENT_FSAL, "Unable to find fd for lease op"); if (ceph_fd) PTHREAD_RWLOCK_unlock(&ceph_fd->fdlock); return status; } retval = ceph_ll_delegation(myself->export->cmount, my_fd, cmd, ceph_deleg_cb, obj_hdl); if (ceph_fd) PTHREAD_RWLOCK_unlock(&ceph_fd->fdlock); if (closefd) (void) ceph_ll_close(myself->export->cmount, my_fd); if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return ceph2fsal_error(retval); } #endif /** * @brief Set attributes on an object * * This function sets attributes on an object. Which attributes are * set is determined by attrib_set->valid_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 ceph_setattr2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, struct attrlist *attrib_set) { struct handle *myself = container_of(obj_hdl, struct handle, handle); fsal_status_t status = {0, 0}; int rc = 0; bool has_lock = false; bool closefd = false; struct export *export = container_of(op_ctx->fsal_export, struct export, export); /* Stat buffer */ struct ceph_statx stx; /* Mask of attributes to set */ uint32_t mask = 0; bool reusing_open_state_fd = false; if (attrib_set->valid_mask & ~CEPH_SETTABLE_ATTRIBUTES) { LogDebug(COMPONENT_FSAL, "bad mask %"PRIx64" not settable %"PRIx64, attrib_set->valid_mask, attrib_set->valid_mask & ~CEPH_SETTABLE_ATTRIBUTES); return fsalstat(ERR_FSAL_INVAL, 0); } LogAttrlist(COMPONENT_FSAL, NIV_FULL_DEBUG, "attrs ", attrib_set, false); /* apply umask, if mode attribute is to be changed */ if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_MODE)) attrib_set->mode &= ~op_ctx->fsal_export->exp_ops.fs_umask(op_ctx->fsal_export); /* 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->valid_mask, ATTR_SIZE)) { if (obj_hdl->type != REGULAR_FILE) { LogFullDebug(COMPONENT_FSAL, "Setting size on non-regular file"); return fsalstat(ERR_FSAL_INVAL, EINVAL); } /* We don't actually need an open fd, we are just doing the * share reservation checking, thus the NULL parameters. */ status = fsal_find_fd(NULL, obj_hdl, NULL, &myself->share, bypass, state, FSAL_O_RDWR, NULL, NULL, &has_lock, &closefd, false, &reusing_open_state_fd); if (FSAL_IS_ERROR(status)) { LogFullDebug(COMPONENT_FSAL, "fsal_find_fd status=%s", fsal_err_txt(status)); goto out; } } memset(&stx, 0, sizeof(stx)); if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_SIZE)) { mask |= CEPH_SETATTR_SIZE; stx.stx_size = attrib_set->filesize; LogDebug(COMPONENT_FSAL, "setting size to %lu", stx.stx_size); } if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_MODE)) { mask |= CEPH_SETATTR_MODE; stx.stx_mode = fsal2unix_mode(attrib_set->mode); } if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_OWNER)) { mask |= CEPH_SETATTR_UID; stx.stx_uid = attrib_set->owner; } if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_GROUP)) { mask |= CEPH_SETATTR_GID; stx.stx_gid = attrib_set->group; } if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_ATIME)) { mask |= CEPH_SETATTR_ATIME; stx.stx_atime = attrib_set->atime; } if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_ATIME_SERVER)) { struct timespec timestamp; mask |= CEPH_SETATTR_ATIME; rc = clock_gettime(CLOCK_REALTIME, ×tamp); if (rc != 0) { LogDebug(COMPONENT_FSAL, "clock_gettime returned %s (%d)", strerror(errno), errno); status = fsalstat(posix2fsal_error(errno), errno); goto out; } stx.stx_atime = timestamp; } if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_MTIME)) { mask |= CEPH_SETATTR_MTIME; stx.stx_mtime = attrib_set->mtime; } if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_MTIME_SERVER)) { struct timespec timestamp; mask |= CEPH_SETATTR_MTIME; rc = clock_gettime(CLOCK_REALTIME, ×tamp); if (rc != 0) { LogDebug(COMPONENT_FSAL, "clock_gettime returned %s (%d)", strerror(errno), errno); status = fsalstat(posix2fsal_error(errno), errno); goto out; } stx.stx_mtime = timestamp; } if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_CTIME)) { mask |= CEPH_SETATTR_CTIME; stx.stx_ctime = attrib_set->ctime; } #ifdef CEPH_SETATTR_BTIME if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_CREATION)) { mask |= CEPH_SETATTR_BTIME; stx.stx_btime = attrib_set->creation; } #endif rc = fsal_ceph_ll_setattr(export->cmount, myself->i, &stx, mask, op_ctx->creds); if (rc < 0) { LogDebug(COMPONENT_FSAL, "setattrx returned %s (%d)", strerror(-rc), -rc); status = ceph2fsal_error(rc); } else { /* Success */ status = fsalstat(ERR_FSAL_NO_ERROR, 0); } out: if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /** * @brief Manage closing a file when a state is no longer needed. * * When the upper layers are ready to dispense with a state, this method is * called to allow the FSAL to close any file descriptors or release any other * resources associated with the state. A call to free_state should be assumed * to follow soon. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * * @return FSAL status. */ fsal_status_t ceph_close2(struct fsal_obj_handle *obj_hdl, struct state_t *state) { struct handle *myself = container_of(obj_hdl, struct handle, handle); struct ceph_fd *my_fd = &container_of(state, struct ceph_state_fd, state)->ceph_fd; if (state->state_type == STATE_TYPE_SHARE || state->state_type == STATE_TYPE_NLM_SHARE || state->state_type == STATE_TYPE_9P_FID) { /* This is a share state, we must update the share counters */ /* This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->obj_lock); update_share_counters(&myself->share, my_fd->openflags, FSAL_O_CLOSED); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); } return ceph_close_my_fd(myself, my_fd); } /** * @brief Write wire handle * * This function writes a 'wire' handle to be sent to clients and * received from the. * * @param[in] handle_pub Handle to digest * @param[in] output_type Type of digest requested * @param[in,out] fh_desc Location/size of buffer for * digest/Length modified to digest length * * @return FSAL status. */ static fsal_status_t handle_to_wire(const struct fsal_obj_handle *handle_pub, uint32_t output_type, struct gsh_buffdesc *fh_desc) { /* The private 'full' object handle */ const struct handle *handle = container_of(handle_pub, const struct handle, handle); switch (output_type) { /* Digested Handles */ case FSAL_DIGEST_NFSV3: case FSAL_DIGEST_NFSV4: if (fh_desc->len < sizeof(handle->vi)) { LogMajor(COMPONENT_FSAL, "digest_handle: space too small for handle. Need %zu, have %zu", sizeof(vinodeno_t), fh_desc->len); return fsalstat(ERR_FSAL_TOOSMALL, 0); } else { memcpy(fh_desc->addr, &handle->vi, sizeof(vinodeno_t)); fh_desc->len = sizeof(handle->vi); } break; default: return fsalstat(ERR_FSAL_SERVERFAULT, 0); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Give a hash key for file handle * * This function locates a unique hash key for a given file. * * @param[in] handle_pub The file whose key is to be found * @param[out] fh_desc Address and length of key */ static void handle_to_key(struct fsal_obj_handle *handle_pub, struct gsh_buffdesc *fh_desc) { /* The private 'full' object handle */ struct handle *handle = container_of(handle_pub, struct handle, handle); fh_desc->addr = &handle->vi; fh_desc->len = sizeof(vinodeno_t); } /** * @brief Override functions in ops vector * * This function overrides implemented functions in the ops vector * with versions for this FSAL. * * @param[in] ops Handle operations vector */ void handle_ops_init(struct fsal_obj_ops *ops) { ops->release = release; ops->merge = ceph_merge; ops->lookup = lookup; ops->mkdir = ceph_fsal_mkdir; ops->mknode = ceph_fsal_mknode; ops->readdir = ceph_fsal_readdir; ops->symlink = ceph_fsal_symlink; ops->readlink = ceph_fsal_readlink; ops->getattrs = getattrs; ops->link = ceph_fsal_link; ops->rename = ceph_fsal_rename; ops->unlink = ceph_fsal_unlink; ops->close = ceph_fsal_close; ops->handle_to_wire = handle_to_wire; ops->handle_to_key = handle_to_key; ops->open2 = ceph_open2; ops->status2 = ceph_status2; ops->reopen2 = ceph_reopen2; ops->read2 = ceph_read2; ops->write2 = ceph_write2; ops->commit2 = ceph_commit2; #ifdef USE_FSAL_CEPH_SETLK ops->lock_op2 = ceph_lock_op2; #endif #ifdef USE_FSAL_CEPH_LL_DELEGATION ops->lease_op2 = ceph_lease_op2; #endif ops->setattr2 = ceph_setattr2; ops->close2 = ceph_close2; #ifdef CEPH_PNFS handle_ops_pnfs(ops); #endif /* CEPH_PNFS */ } nfs-ganesha-2.6.0/src/FSAL/FSAL_CEPH/internal.c000066400000000000000000000133651324272410200205360ustar00rootroot00000000000000/* * Copyright © 2012-2014, CohortFS, LLC. * Author: Adam C. Emerson * * contributeur : William Allen Simpson * Marcus Watts * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @file internal.c * @author Adam C. Emerson * @author William Allen Simpson * @date Wed Oct 22 13:24:33 2014 * * @brief Internal definitions for the Ceph FSAL * * This file includes internal function definitions, constants, and * variable declarations used to impelment the Ceph FSAL, but not * exposed as part of the API. */ #include #include #include "fsal_types.h" #include "fsal.h" #include "fsal_convert.h" #include "FSAL/fsal_commonlib.h" #include "statx_compat.h" #include "internal.h" /** * @brief Construct a new filehandle * * This function constructs a new Ceph FSAL object handle and attaches * it to the export. After this call the attributes have been filled * in and the handdle is up-to-date and usable. * * @param[in] stx ceph_statx data for the file * @param[in] export Export on which the object lives * @param[out] obj Object created * * @return 0 on success, negative error codes on failure. */ void construct_handle(const struct ceph_statx *stx, struct Inode *i, struct export *export, struct handle **obj) { /* Pointer to the handle under construction */ struct handle *constructing = NULL; assert(i); constructing = gsh_calloc(1, sizeof(struct handle)); constructing->vi.ino.val = stx->stx_ino; #ifdef CEPH_NOSNAP constructing->vi.snapid.val = stx->stx_dev; #endif /* CEPH_NOSNAP */ constructing->i = i; constructing->up_ops = export->export.up_ops; fsal_obj_handle_init(&constructing->handle, &export->export, posix2fsal_type(stx->stx_mode)); handle_ops_init(&constructing->handle.obj_ops); constructing->handle.fsid = posix2fsal_fsid(stx->stx_dev); constructing->handle.fileid = stx->stx_ino; constructing->export = export; *obj = constructing; } /** * @brief Release all resrouces for a handle * * @param[in] obj Handle to release */ void deconstruct_handle(struct handle *obj) { ceph_ll_put(obj->export->cmount, obj->i); fsal_obj_handle_fini(&obj->handle); gsh_free(obj); } unsigned int attrmask2ceph_want(attrmask_t mask) { unsigned int want = 0; if (mask & ATTR_MODE) want |= CEPH_STATX_MODE; if (mask & ATTR_OWNER) want |= CEPH_STATX_UID; if (mask & ATTR_GROUP) want |= CEPH_STATX_GID; if (mask & ATTR_SIZE) want |= CEPH_STATX_SIZE; if (mask & ATTR_NUMLINKS) want |= CEPH_STATX_NLINK; if (mask & ATTR_SPACEUSED) want |= CEPH_STATX_BLOCKS; if (mask & ATTR_ATIME) want |= CEPH_STATX_ATIME; if (mask & ATTR_CTIME) want |= CEPH_STATX_CTIME; if (mask & ATTR_MTIME) want |= CEPH_STATX_MTIME; if (mask & ATTR_CREATION) want |= CEPH_STATX_BTIME; if (mask & ATTR_CHANGE) want |= CEPH_STATX_VERSION; if (mask & ATTR_CHGTIME) want |= (CEPH_STATX_CTIME|CEPH_STATX_MTIME); return want; } void ceph2fsal_attributes(const struct ceph_statx *stx, struct attrlist *fsalattr) { /* These are always considered to be available */ fsalattr->valid_mask |= ATTR_TYPE|ATTR_FSID|ATTR_RAWDEV|ATTR_FILEID; fsalattr->supported = CEPH_SUPPORTED_ATTRS; fsalattr->type = posix2fsal_type(stx->stx_mode); fsalattr->rawdev = posix2fsal_devt(stx->stx_rdev); fsalattr->fsid = posix2fsal_fsid(stx->stx_dev); fsalattr->fileid = stx->stx_ino; if (stx->stx_mask & CEPH_STATX_MODE) { fsalattr->valid_mask |= ATTR_MODE; fsalattr->mode = unix2fsal_mode(stx->stx_mode); } if (stx->stx_mask & CEPH_STATX_UID) { fsalattr->valid_mask |= ATTR_OWNER; fsalattr->owner = stx->stx_uid; } if (stx->stx_mask & CEPH_STATX_GID) { fsalattr->valid_mask |= ATTR_GROUP; fsalattr->group = stx->stx_gid; } if (stx->stx_mask & CEPH_STATX_SIZE) { fsalattr->valid_mask |= ATTR_SIZE; fsalattr->filesize = stx->stx_size; } if (stx->stx_mask & CEPH_STATX_NLINK) { fsalattr->valid_mask |= ATTR_NUMLINKS; fsalattr->numlinks = stx->stx_nlink; } if (stx->stx_mask & CEPH_STATX_BLOCKS) { fsalattr->valid_mask |= ATTR_SPACEUSED; fsalattr->spaceused = stx->stx_blocks * S_BLKSIZE; } /* Use full timer resolution */ if (stx->stx_mask & CEPH_STATX_ATIME) { fsalattr->valid_mask |= ATTR_ATIME; fsalattr->atime = stx->stx_atime; } if (stx->stx_mask & CEPH_STATX_CTIME) { fsalattr->valid_mask |= ATTR_CTIME; fsalattr->ctime = stx->stx_ctime; } if (stx->stx_mask & CEPH_STATX_MTIME) { fsalattr->valid_mask |= ATTR_MTIME; fsalattr->mtime = stx->stx_mtime; } if (stx->stx_mask & CEPH_STATX_BTIME) { fsalattr->valid_mask |= ATTR_CREATION; fsalattr->creation = stx->stx_btime; } if (stx->stx_mask & CEPH_STATX_VERSION) { fsalattr->valid_mask |= ATTR_CHANGE; fsalattr->change = stx->stx_version; } if ((stx->stx_mask & (CEPH_STATX_CTIME|CEPH_STATX_MTIME)) == (CEPH_STATX_CTIME|CEPH_STATX_MTIME)) { fsalattr->valid_mask |= ATTR_CHGTIME; fsalattr->chgtime = (gsh_time_cmp(&fsalattr->mtime, &fsalattr->ctime) > 0) ? fsalattr->mtime : fsalattr->ctime; } } nfs-ganesha-2.6.0/src/FSAL/FSAL_CEPH/internal.h000066400000000000000000000126441324272410200205420ustar00rootroot00000000000000/* * Copyright © 2012-2014, CohortFS, LLC. * Author: Adam C. Emerson * * contributeur : William Allen Simpson * Marcus Watts * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @file internal.h * @author Adam C. Emerson * @author William Allen Simpson * @date Wed Oct 22 13:24:33 2014 * * @brief Internal declarations for the Ceph FSAL * * This file includes declarations of data types, functions, * variables, and constants for the Ceph FSAL. */ #ifndef FSAL_CEPH_INTERNAL_INTERNAL__ #define FSAL_CEPH_INTERNAL_INTERNAL__ #include #include "fsal.h" #include "fsal_types.h" #include "fsal_api.h" #include "fsal_convert.h" #include #include #include "statx_compat.h" #include "FSAL/fsal_commonlib.h" /* Max length of a user_id string that we pass to ceph_mount */ #define MAXUIDLEN (64) /* Max length of a secret key for this user */ #define MAXSECRETLEN (88) /** * Ceph Main (global) module object */ struct ceph_fsal_module { struct fsal_module fsal; fsal_staticfsinfo_t fs_info; char *conf_path; }; extern struct ceph_fsal_module CephFSM; /** * Ceph private export object */ struct export { struct fsal_export export; /*< The public export object */ struct ceph_mount_info *cmount; /*< The mount object used to access all Ceph methods on this export. */ struct handle *root; /*< The root handle */ char *user_id; /* cephx user_id for this mount */ char *secret_key; }; struct ceph_fd { /** The open and share mode etc. */ fsal_openflags_t openflags; /* rw lock to protect the file descriptor */ pthread_rwlock_t fdlock; /** The cephfs file descriptor. */ Fh *fd; }; struct ceph_state_fd { struct state_t state; struct ceph_fd ceph_fd; }; /** * The 'private' Ceph FSAL handle */ struct handle { struct fsal_obj_handle handle; /*< The public handle */ struct ceph_fd fd; struct Inode *i; /*< The Ceph inode */ const struct fsal_up_vector *up_ops; /*< Upcall operations */ struct export *export; /*< The first export this handle belongs to */ vinodeno_t vi; /*< The object identifier */ struct fsal_share share; #ifdef CEPH_PNFS uint64_t rd_issued; uint64_t rd_serial; uint64_t rw_issued; uint64_t rw_serial; uint64_t rw_max_len; #endif /* CEPH_PNFS */ }; #ifdef CEPH_PNFS /** * The wire content of a DS (data server) handle */ struct ds_wire { struct wire_handle wire; /*< All the information of a regualr handle */ struct ceph_file_layout layout; /*< Layout information */ uint64_t snapseq; /*< And a single entry giving a degernate snaprealm. */ }; /** * The full, 'private' DS (data server) handle */ struct ds { struct fsal_ds_handle ds; /*< Public DS handle */ struct ds_wire wire; /*< Wire data */ bool connected; /*< True if the handle has been connected (in Ceph) */ }; #endif /* CEPH_PNFS */ #define CEPH_SUPPORTED_ATTRS ((const attrmask_t) (ATTRS_POSIX)) #define CEPH_SETTABLE_ATTRIBUTES ((const attrmask_t) ( \ ATTR_MODE | ATTR_OWNER | ATTR_GROUP | ATTR_ATIME | \ ATTR_CTIME | ATTR_MTIME | ATTR_SIZE | ATTR_MTIME_SERVER | \ ATTR_ATIME_SERVER)) /* private helper for export object */ static inline fsal_staticfsinfo_t *ceph_staticinfo(struct fsal_module *hdl) { struct ceph_fsal_module *myself = container_of(hdl, struct ceph_fsal_module, fsal); return &myself->fs_info; } /* Prototypes */ void construct_handle(const struct ceph_statx *stx, struct Inode *i, struct export *export, struct handle **obj); void deconstruct_handle(struct handle *obj); /** * @brief FSAL status from Ceph error * * This function returns a fsal_status_t with the FSAL error as the * major, and the posix error as minor. (Ceph's error codes are just * negative signed versions of POSIX error codes.) * * @param[in] ceph_errorcode Ceph error (negative Posix) * * @return FSAL status. */ static inline fsal_status_t ceph2fsal_error(const int ceph_errorcode) { return fsalstat(posix2fsal_error(-ceph_errorcode), -ceph_errorcode); } unsigned int attrmask2ceph_want(attrmask_t mask); void ceph2fsal_attributes(const struct ceph_statx *stx, struct attrlist *fsalattr); void export_ops_init(struct export_ops *ops); void handle_ops_init(struct fsal_obj_ops *ops); #ifdef CEPH_PNFS void pnfs_ds_ops_init(struct fsal_pnfs_ds_ops *ops); void export_ops_pnfs(struct export_ops *ops); void handle_ops_pnfs(struct fsal_obj_ops *ops); #endif /* CEPH_PNFS */ struct state_t *ceph_alloc_state(struct fsal_export *exp_hdl, enum state_type state_type, struct state_t *related_state); void ceph_free_state(struct fsal_export *exp_hdl, struct state_t *state); #endif /* !FSAL_CEPH_INTERNAL_INTERNAL__ */ nfs-ganesha-2.6.0/src/FSAL/FSAL_CEPH/main.c000066400000000000000000000267351324272410200176530ustar00rootroot00000000000000/* * Copyright © 2012-2014, CohortFS, LLC. * Author: Adam C. Emerson * * contributeur : William Allen Simpson * Marcus Watts * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @file FSAL_CEPH/main.c * @author Adam C. Emerson * @author William Allen Simpson * @date Wed Oct 22 13:24:33 2014 * * @brief Implementation of FSAL module founctions for Ceph * * This file implements the module functions for the Ceph FSAL, for * initialization, teardown, configuration, and creation of exports. */ #include #include #include "fsal.h" #include "fsal_types.h" #include "FSAL/fsal_init.h" #include "FSAL/fsal_commonlib.h" #include "fsal_api.h" #include "internal.h" #include "abstract_mem.h" #include "nfs_exports.h" #include "export_mgr.h" #include "statx_compat.h" #include "nfs_core.h" /** * Ceph global module object. */ struct ceph_fsal_module CephFSM; /** * The name of this module. */ static const char *module_name = "Ceph"; static fsal_staticfsinfo_t default_ceph_info = { /* settable */ #if 0 .umask = 0, .xattr_access_rights = 0, #endif /* fixed */ .symlink_support = true, .link_support = true, .cansettime = true, .no_trunc = true, .chown_restricted = true, .case_preserving = true, #ifdef USE_FSAL_CEPH_SETLK .lock_support = true, .lock_support_async_block = false, #endif .unique_handles = true, .homogenous = true, #ifdef USE_FSAL_CEPH_LL_DELEGATION .delegations = FSAL_OPTION_FILE_READ_DELEG, #endif }; static struct config_item ceph_items[] = { CONF_ITEM_PATH("ceph_conf", 1, MAXPATHLEN, NULL, ceph_fsal_module, conf_path), CONF_ITEM_MODE("umask", 0, ceph_fsal_module, fs_info.umask), CONF_ITEM_MODE("xattr_access_rights", 0, ceph_fsal_module, fs_info.xattr_access_rights), CONFIG_EOL }; static struct config_block ceph_block = { .dbus_interface_name = "org.ganesha.nfsd.config.fsal.ceph", .blk_desc.name = "Ceph", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = noop_conf_init, .blk_desc.u.blk.params = ceph_items, .blk_desc.u.blk.commit = noop_conf_commit }; /* Module methods */ /* init_config * must be called with a reference taken (via lookup_fsal) */ static fsal_status_t init_config(struct fsal_module *module_in, config_file_t config_struct, struct config_error_type *err_type) { struct ceph_fsal_module *myself = container_of(module_in, struct ceph_fsal_module, fsal); LogDebug(COMPONENT_FSAL, "Ceph module setup."); myself->fs_info = default_ceph_info; (void) load_config_from_parse(config_struct, &ceph_block, myself, true, err_type); if (!config_error_is_harmless(err_type)) return fsalstat(ERR_FSAL_INVAL, 0); return fsalstat(ERR_FSAL_NO_ERROR, 0); } #ifdef USE_FSAL_CEPH_LL_LOOKUP_ROOT static fsal_status_t find_cephfs_root(struct ceph_mount_info *cmount, Inode **pi) { return ceph2fsal_error(ceph_ll_lookup_root(cmount, pi)); } #else /* USE_FSAL_CEPH_LL_LOOKUP_ROOT */ static fsal_status_t find_cephfs_root(struct ceph_mount_info *cmount, Inode **pi) { struct stat st; return ceph2fsal_error(ceph_ll_walk(cmount, "/", pi, &st)); } #endif /* USE_FSAL_CEPH_LL_LOOKUP_ROOT */ static struct config_item export_params[] = { CONF_ITEM_NOOP("name"), CONF_ITEM_STR("user_id", 0, MAXUIDLEN, NULL, export, user_id), CONF_ITEM_STR("secret_access_key", 0, MAXSECRETLEN, NULL, export, secret_key), CONFIG_EOL }; static struct config_block export_param_block = { .dbus_interface_name = "org.ganesha.nfsd.config.fsal.ceph-export%d", .blk_desc.name = "FSAL", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = noop_conf_init, .blk_desc.u.blk.params = export_params, .blk_desc.u.blk.commit = noop_conf_commit }; #ifdef USE_FSAL_CEPH_LL_DELEGATION static void enable_delegations(struct export *export) { struct export_perms *export_perms = &op_ctx->ctx_export->export_perms; if (export_perms->options & EXPORT_OPTION_DELEGATIONS) { /* * Ganesha will time out delegations when the recall fails * for two lease periods. We add just a little bit above that * as a scheduling fudge-factor. * * The idea here is to make this long enough to give ganesha * a chance to kick out a misbehaving client, but shorter * than ceph cluster-wide MDS session timeout. * * Exceeding the MDS session timeout may result in the client * (ganesha) being blacklisted in the cluster. Fixing that can * require a long wait and/or administrative intervention. */ unsigned int dt = nfs_param.nfsv4_param.lease_lifetime * 2 + 5; int ceph_status; LogDebug(COMPONENT_FSAL, "Setting deleg timeout to %u", dt); ceph_status = ceph_set_deleg_timeout(export->cmount, dt); if (ceph_status != 0) { export_perms->options &= ~EXPORT_OPTION_DELEGATIONS; LogWarn(COMPONENT_FSAL, "Unable to set delegation timeout for %s. Disabling delegation support: %d", op_ctx->ctx_export->fullpath, ceph_status); } } } #else /* !USE_FSAL_CEPH_LL_DELEGATION */ static inline void enable_delegations(struct export *export) { } #endif /* USE_FSAL_CEPH_LL_DELEGATION */ /** * @brief Create a new export under this FSAL * * This function creates a new export object for the Ceph FSAL. * * @todo ACE: We do not handle re-exports of the same cluster in a * sane way. Currently we create multiple handles and cache objects * pointing to the same one. This is not necessarily wrong, but it is * inefficient. It may also not be something we expect to use enough * to care about. * * @param[in] module_in The supplied module handle * @param[in] path The path to export * @param[in] options Export specific options for the FSAL * @param[in,out] list_entry Our entry in the export list * @param[in] next_fsal Next stacked FSAL * @param[out] pub_export Newly created FSAL export object * * @return FSAL status. */ static fsal_status_t create_export(struct fsal_module *module_in, void *parse_node, struct config_error_type *err_type, const struct fsal_up_vector *up_ops) { /* The status code to return */ fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; /* The internal export object */ struct export *export = gsh_calloc(1, sizeof(struct export)); /* The 'private' root handle */ struct handle *handle = NULL; /* Root inode */ struct Inode *i = NULL; /* Stat for root */ struct ceph_statx stx; /* Return code */ int rc; /* Return code from Ceph calls */ int ceph_status; /* True if we have called fsal_export_init */ bool initialized = false; fsal_export_init(&export->export); export_ops_init(&export->export.exp_ops); /* get params for this export, if any */ if (parse_node) { rc = load_config_from_node(parse_node, &export_param_block, export, true, err_type); if (rc != 0) { gsh_free(export); return fsalstat(ERR_FSAL_INVAL, 0); } } initialized = true; /* allocates ceph_mount_info */ ceph_status = ceph_create(&export->cmount, export->user_id); if (ceph_status != 0) { status.major = ERR_FSAL_SERVERFAULT; LogCrit(COMPONENT_FSAL, "Unable to create Ceph handle for %s.", op_ctx->ctx_export->fullpath); goto error; } ceph_status = ceph_conf_read_file(export->cmount, CephFSM.conf_path); if (ceph_status != 0) { status.major = ERR_FSAL_SERVERFAULT; LogCrit(COMPONENT_FSAL, "Unable to read Ceph configuration for %s.", op_ctx->ctx_export->fullpath); goto error; } if (export->secret_key) { ceph_status = ceph_conf_set(export->cmount, "key", export->secret_key); if (ceph_status) { status.major = ERR_FSAL_INVAL; LogCrit(COMPONENT_FSAL, "Unable to set Ceph secret key for %s: %d", op_ctx->ctx_export->fullpath, ceph_status); goto error; } } /* * Workaround for broken libcephfs that doesn't handle the path * given in ceph_mount properly. Should be harmless for fixed * libcephfs as well (see http://tracker.ceph.com/issues/18254). */ ceph_status = ceph_conf_set(export->cmount, "client_mountpoint", op_ctx->ctx_export->fullpath); if (ceph_status) { status.major = ERR_FSAL_INVAL; LogCrit(COMPONENT_FSAL, "Unable to set Ceph client_mountpoint for %s: %d", op_ctx->ctx_export->fullpath, ceph_status); goto error; } ceph_status = ceph_mount(export->cmount, op_ctx->ctx_export->fullpath); if (ceph_status != 0) { status.major = ERR_FSAL_SERVERFAULT; LogCrit(COMPONENT_FSAL, "Unable to mount Ceph cluster for %s.", op_ctx->ctx_export->fullpath); goto error; } enable_delegations(export); if (fsal_attach_export(module_in, &export->export.exports) != 0) { status.major = ERR_FSAL_SERVERFAULT; LogCrit(COMPONENT_FSAL, "Unable to attach export for %s.", op_ctx->ctx_export->fullpath); goto error; } export->export.fsal = module_in; export->export.up_ops = up_ops; LogDebug(COMPONENT_FSAL, "Ceph module export %s.", op_ctx->ctx_export->fullpath); status = find_cephfs_root(export->cmount, &i); if (FSAL_IS_ERROR(status)) goto error; rc = fsal_ceph_ll_getattr(export->cmount, i, &stx, CEPH_STATX_HANDLE_MASK, op_ctx->creds); if (rc < 0) { status = ceph2fsal_error(rc); goto error; } construct_handle(&stx, i, export, &handle); export->root = handle; op_ctx->fsal_export = &export->export; return status; error: if (i) ceph_ll_put(export->cmount, i); if (export) { if (export->cmount) ceph_shutdown(export->cmount); gsh_free(export); } if (initialized) initialized = false; return status; } /** * @brief Initialize and register the FSAL * * This function initializes the FSAL module handle, being called * before any configuration or even mounting of a Ceph cluster. It * exists solely to produce a properly constructed FSAL module * handle. */ MODULE_INIT void init(void) { struct fsal_module *myself = &CephFSM.fsal; LogDebug(COMPONENT_FSAL, "Ceph module registering."); /* register_fsal seems to expect zeroed memory. */ memset(myself, 0, sizeof(*myself)); if (register_fsal(myself, module_name, FSAL_MAJOR_VERSION, FSAL_MINOR_VERSION, FSAL_ID_CEPH) != 0) { /* The register_fsal function prints its own log message if it fails */ LogCrit(COMPONENT_FSAL, "Ceph module failed to register."); } /* Set up module operations */ #ifdef CEPH_PNFS myself->m_ops.fsal_pnfs_ds_ops = pnfs_ds_ops_init; #endif /* CEPH_PNFS */ myself->m_ops.create_export = create_export; myself->m_ops.init_config = init_config; } /** * @brief Release FSAL resources * * This function unregisters the FSAL and frees its module handle. * The Ceph FSAL has no other resources to release on the per-FSAL * level. */ MODULE_FINI void finish(void) { LogDebug(COMPONENT_FSAL, "Ceph module finishing."); if (unregister_fsal(&CephFSM.fsal) != 0) { LogCrit(COMPONENT_FSAL, "Unable to unload Ceph FSAL. Dying with extreme prejudice."); abort(); } } nfs-ganesha-2.6.0/src/FSAL/FSAL_CEPH/mds.c000066400000000000000000000505101324272410200174760ustar00rootroot00000000000000/* * Copyright © 2012-2014, CohortFS, LLC. * Author: Adam C. Emerson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ #include "gsh_rpc.h" #include #include "fsal.h" #include "fsal_types.h" #include "fsal_api.h" #include "fsal_up.h" #include "pnfs_utils.h" #include "internal.h" #include "nfs_exports.h" #include "FSAL/fsal_commonlib.h" #include "statx_compat.h" #ifdef CEPH_PNFS /** * Linux supports a stripe pattern with no more than 4096 stripes, but * for now we stick to 1024 to keep them da_addrs from being too * gigantic. */ static const size_t BIGGEST_PATTERN = 1024; /** * @file FSAL_CEPH/mds.c * @author Adam C. Emerson * @date Wed Oct 22 13:24:33 2014 * * @brief pNFS Metadata Server Operations for the Ceph FSAL * * This file implements the layoutget, layoutreturn, layoutcommit, * getdeviceinfo, and getdevicelist operations and export query * support for the Ceph FSAL. */ static bool initiate_recall(vinodeno_t vi, bool write, void *opaque) { /* The private 'full' object handle */ struct handle *handle = (struct handle *)opaque; /* Return code from upcall operation */ state_status_t status = STATE_SUCCESS; struct gsh_buffdesc key = { .addr = &handle->vi, .len = sizeof(vinodeno_t) }; struct pnfs_segment segment = { .offset = 0, .length = UINT64_MAX, .io_mode = (write ? LAYOUTIOMODE4_RW : LAYOUTIOMODE4_READ) }; status = handle->up_ops->layoutrecall(&key, LAYOUT4_NFSV4_1_FILES, false, &segment, NULL, NULL); if (status != STATE_SUCCESS) return false; return true; } /** * @brief Describe a Ceph striping pattern * * At present, we support a files based layout only. The CRUSH * striping pattern is a-periodic * * @param[in] export_pub Public export handle * @param[out] da_addr_body Stream we write the result to * @param[in] type Type of layout that gave the device * @param[in] deviceid The device to look up * * @return Valid error codes in RFC 5661, p. 365. */ static nfsstat4 getdeviceinfo(struct fsal_export *export_pub, XDR *da_addr_body, const layouttype4 type, const struct pnfs_deviceid *deviceid) { /* Full 'private' export */ struct export *export = container_of(export_pub, struct export, export); /* The number of Ceph OSDs in the cluster */ unsigned int num_osds = ceph_ll_num_osds(export->cmount); /* Minimal information needed to get layout info */ vinodeno_t vinode; /* Structure containing the storage parameters of the file within the Ceph cluster. */ struct ceph_file_layout file_layout; /* Currently, all layouts have the same number of stripes */ uint32_t stripes = BIGGEST_PATTERN; /* Index for iterating over stripes */ size_t stripe = 0; /* Index for iterating over OSDs */ size_t osd = 0; /* NFSv4 status code */ nfsstat4 nfs_status = 0; vinode.ino.val = deviceid->devid; vinode.snapid.val = CEPH_NOSNAP; /* Sanity check on type */ if (type != LAYOUT4_NFSV4_1_FILES) { LogCrit(COMPONENT_PNFS, "Unsupported layout type: %x", type); return NFS4ERR_UNKNOWN_LAYOUTTYPE; } /* Retrieve and calculate storage parameters of layout */ memset(&file_layout, 0, sizeof(struct ceph_file_layout)); if (ceph_ll_file_layout(export->cmount, vinode, &file_layout) != 0) { LogCrit(COMPONENT_PNFS, "Failed to get Ceph layout for inode"); return NFS4ERR_SERVERFAULT; } /* As this is large, we encode as we go rather than building a structure and encoding it all at once. */ /* The first entry in the nfsv4_1_file_ds_addr4 is the array of stripe indices. First we encode the count of stripes. Since our pattern doesn't repeat, we have as many indices as we do stripes. */ if (!inline_xdr_u_int32_t(da_addr_body, &stripes)) { LogCrit(COMPONENT_PNFS, "Failed to encode length of stripe_indices array: %" PRIu32 ".", stripes); return NFS4ERR_SERVERFAULT; } for (stripe = 0; stripe < stripes; stripe++) { uint32_t stripe_osd = stripe_osd = ceph_ll_get_stripe_osd(export->cmount, vinode, stripe, &file_layout); if (stripe_osd < 0) { LogCrit(COMPONENT_PNFS, "Failed to retrieve OSD for stripe %lu of file %" PRIu64 ". Error: %u", stripe, deviceid->devid, -stripe_osd); return NFS4ERR_SERVERFAULT; } if (!inline_xdr_u_int32_t(da_addr_body, &stripe_osd)) { LogCrit(COMPONENT_PNFS, "Failed to encode OSD for stripe %lu.", stripe); return NFS4ERR_SERVERFAULT; } } /* The number of OSDs in our cluster is the length of our array of multipath_lists */ if (!inline_xdr_u_int32_t(da_addr_body, &num_osds)) { LogCrit(COMPONENT_PNFS, "Failed to encode length of multipath_ds_list array: %u", num_osds); return NFS4ERR_SERVERFAULT; } /* Since our index is the OSD number itself, we have only one host per multipath_list. */ for (osd = 0; osd < num_osds; osd++) { fsal_multipath_member_t host; memset(&host, 0, sizeof(fsal_multipath_member_t)); host.proto = 6; if (ceph_ll_osdaddr(export->cmount, osd, &host.addr) < 0) { LogCrit(COMPONENT_PNFS, "Unable to get IP address for OSD %lu.", osd); return NFS4ERR_SERVERFAULT; } host.port = 2049; nfs_status = FSAL_encode_v4_multipath(da_addr_body, 1, &host); if (nfs_status != NFS4_OK) return nfs_status; } return NFS4_OK; } /** * @brief Get list of available devices * * We do not support listing devices and just set EOF without doing * anything. * * @param[in] export_pub Export handle * @param[in] type Type of layout to get devices for * @param[in] cb Function taking device ID halves * @param[in,out] res In/out and output arguments of the function * * @return Valid error codes in RFC 5661, pp. 365-6. */ static nfsstat4 getdevicelist(struct fsal_export *export_pub, layouttype4 type, void *opaque, bool(*cb) (void *opaque, const uint64_t id), struct fsal_getdevicelist_res *res) { res->eof = true; return NFS4_OK; } /** * @brief Get layout types supported by export * * We just return a pointer to the single type and set the count to 1. * * @param[in] export_pub Public export handle * @param[out] count Number of layout types in array * @param[out] types Static array of layout types that must not be * freed or modified and must not be dereferenced * after export reference is relinquished */ static void fs_layouttypes(struct fsal_export *export_pub, int32_t *count, const layouttype4 **types) { static const layouttype4 supported_layout_type = LAYOUT4_NFSV4_1_FILES; *types = &supported_layout_type; *count = 1; } /** * @brief Get layout block size for export * * This function just returns the Ceph default. * * @param[in] export_pub Public export handle * * @return 4 MB. */ static uint32_t fs_layout_blocksize(struct fsal_export *export_pub) { return 0x400000; } /** * @brief Maximum number of segments we will use * * Since current clients only support 1, that's what we'll use. * * @param[in] export_pub Public export handle * * @return 1 */ static uint32_t fs_maximum_segments(struct fsal_export *export_pub) { return 1; } /** * @brief Size of the buffer needed for a loc_body * * Just a handle plus a bit. * * @param[in] export_pub Public export handle * * @return Size of the buffer needed for a loc_body */ static size_t fs_loc_body_size(struct fsal_export *export_pub) { return 0x100; } /** * @brief Size of the buffer needed for a ds_addr * * This one is huge, due to the striping pattern. * * @param[in] export_pub Public export handle * * @return Size of the buffer needed for a ds_addr */ static size_t fs_da_addr_size(struct fsal_export *export_pub) { return 0x1400; } void export_ops_pnfs(struct export_ops *ops) { ops->getdeviceinfo = getdeviceinfo; ops->getdevicelist = getdevicelist; ops->fs_layouttypes = fs_layouttypes; ops->fs_layout_blocksize = fs_layout_blocksize; ops->fs_maximum_segments = fs_maximum_segments; ops->fs_loc_body_size = fs_loc_body_size; ops->fs_da_addr_size = fs_da_addr_size; } /** * @brief Grant a layout segment. * * Grant a layout on a subset of a file requested. As a special case, * lie and grant a whole-file layout if requested, because Linux will * ignore it otherwise. * * @param[in] obj_pub Public object handle * @param[in] req_ctx Request context * @param[out] loc_body An XDR stream to which the FSAL must encode * the layout specific portion of the granted * layout segment. * @param[in] arg Input arguments of the function * @param[in,out] res In/out and output arguments of the function * * @return Valid error codes in RFC 5661, pp. 366-7. */ static nfsstat4 layoutget(struct fsal_obj_handle *obj_pub, struct req_op_context *req_ctx, XDR *loc_body, const struct fsal_layoutget_arg *arg, struct fsal_layoutget_res *res) { /* The private 'full' export */ struct export *export = container_of(req_ctx->fsal_export, struct export, export); /* The private 'full' object handle */ struct handle *handle = container_of(obj_pub, struct handle, handle); /* Structure containing the storage parameters of the file within the Ceph cluster. */ struct ceph_file_layout file_layout; /* Width of each stripe on the file */ uint32_t stripe_width = 0; /* Utility parameter */ nfl_util4 util = 0; /* The last byte that can be accessed through pNFS */ uint64_t last_possible_byte = 0; /* The deviceid for this layout */ struct pnfs_deviceid deviceid = DEVICE_ID_INIT_ZERO(FSAL_ID_CEPH); /* NFS Status */ nfsstat4 nfs_status = 0; /* DS wire handle */ struct ds_wire ds_wire; /* Descriptor for DS handle */ struct gsh_buffdesc ds_desc = {.addr = &ds_wire, .len = sizeof(struct ds_wire) }; /* The smallest layout the client will accept */ struct pnfs_segment smallest_acceptable = { .io_mode = res->segment.io_mode, .offset = res->segment.offset, .length = arg->minlength }; struct pnfs_segment forbidden_area = { .io_mode = res->segment.io_mode, .length = NFS4_UINT64_MAX }; /* We support only LAYOUT4_NFSV4_1_FILES layouts */ if (arg->type != LAYOUT4_NFSV4_1_FILES) { LogCrit(COMPONENT_PNFS, "Unsupported layout type: %x", arg->type); return NFS4ERR_UNKNOWN_LAYOUTTYPE; } /* Get basic information on the file and calculate the dimensions of the layout we can support. */ memset(&file_layout, 0, sizeof(struct ceph_file_layout)); ceph_ll_file_layout(export->cmount, handle->wire.vi, &file_layout); stripe_width = file_layout.fl_stripe_unit; last_possible_byte = (BIGGEST_PATTERN * stripe_width) - 1; forbidden_area.offset = last_possible_byte + 1; /* Since the Linux kernel refuses to work with any layout that doesn't cover the whole file, if a whole file layout is requested, lie. Otherwise, make sure the required layout doesn't go beyond what can be accessed through pNFS. This is a preliminary check before even talking to Ceph. */ if (! ((res->segment.offset == 0) && (res->segment.length == NFS4_UINT64_MAX))) { if (pnfs_segments_overlap (&smallest_acceptable, &forbidden_area)) { LogCrit(COMPONENT_PNFS, "Required layout extends beyond allowed region. offset: %" PRIu64 ", minlength: %" PRIu64 ".", res->segment.offset, arg->minlength); return NFS4ERR_BADLAYOUT; } res->segment.offset = 0; res->segment.length = stripe_width * BIGGEST_PATTERN; } LogFullDebug(COMPONENT_PNFS, "will issue layout offset: %" PRIu64 " length: %" PRIu64, res->segment.offset, res->segment.length); /* We are using sparse layouts with commit-through-DS, so our utility word contains only the stripe width, our first stripe is always at the beginning of the layout, and there is no pattern offset. */ if ((stripe_width & ~NFL4_UFLG_STRIPE_UNIT_SIZE_MASK) != 0) { LogCrit(COMPONENT_PNFS, "Ceph returned stripe width that is disallowed by NFS: %" PRIu32 ".", stripe_width); return NFS4ERR_SERVERFAULT; } util = stripe_width; /* If we have a cached capbility, use that. Otherwise, call in to Ceph. */ PTHREAD_RWLOCK_wrlock(&handle->handle.obj_lock); if (res->segment.io_mode == LAYOUTIOMODE4_READ) { int32_t r = 0; if (handle->rd_issued == 0) { #if 0 /* (ceph part might be here: thunderbeast mbarrier1) */ r = ceph_ll_hold_rw(export->cmount, handle->wire.vi, false, initiate_recall, handle, &handle->rd_serial, NULL); #endif if (r < 0) { PTHREAD_RWLOCK_unlock(&handle->handle.obj_lock); return posix2nfs4_error(-r); } } ++handle->rd_issued; } else { int32_t r = 0; if (handle->rw_issued == 0) { #if 0 r = ceph_ll_hold_rw(export->cmount, handle->wire.vi, true, initiate_recall, handle, &handle->rw_serial, &handle->rw_max_len); #endif if (r < 0) { PTHREAD_RWLOCK_unlock(&handle->handle.obj_lock); return posix2nfs4_error(-r); } } forbidden_area.offset = handle->rw_max_len; if (pnfs_segments_overlap (&smallest_acceptable, &forbidden_area)) { PTHREAD_RWLOCK_unlock(&handle->handle.obj_lock); return NFS4ERR_BADLAYOUT; } #if CLIENTS_WILL_ACCEPT_SEGMENTED_LAYOUTS /* sigh */ res->segment.length = (handle->rw_max_len - res->segment.offset); #endif ++handle->rw_issued; } PTHREAD_RWLOCK_unlock(&handle->handle.obj_lock); /* For now, just make the low quad of the deviceid be the inode number. With the span of the layouts constrained above, this lets us generate the device address on the fly from the deviceid rather than storing it. */ deviceid.devid = handle->wire.vi.ino.val; /* We return exactly one filehandle, filling in the necessary information for the DS server to speak to the Ceph OSD directly. */ ds_wire.wire = handle->wire; ds_wire.layout = file_layout; ds_wire.snapseq = ceph_ll_snap_seq(export->cmount, handle->wire.vi); nfs_status = FSAL_encode_file_layout(loc_body, &deviceid, util, 0, 0, &req_ctx->export->export_id, 1, &ds_desc); if (nfs_status != NFS4_OK) { LogCrit(COMPONENT_PNFS, "Failed to encode nfsv4_1_file_layout."); goto relinquish; } /* We grant only one segment, and we want it back when the file is closed. */ res->return_on_close = true; res->last_segment = true; return NFS4_OK; relinquish: /* If we failed in encoding the lo_content, relinquish what we reserved for it. */ PTHREAD_RWLOCK_wrlock(&handle->handle.obj_lock); if (res->segment.io_mode == LAYOUTIOMODE4_READ) { if (--handle->rd_issued != 0) { PTHREAD_RWLOCK_unlock(&handle->handle.obj_lock); return nfs_status; } } else { if (--handle->rd_issued != 0) { PTHREAD_RWLOCK_unlock(&handle->handle.obj_lock); return nfs_status; } } #if 0 ceph_ll_return_rw(export->cmount, handle->wire.vi, res->segment.io_mode == LAYOUTIOMODE4_READ ? handle->rd_serial : handle->rw_serial); #endif PTHREAD_RWLOCK_unlock(&handle->handle.obj_lock); return nfs_status; } /** * @brief Potentially return one layout segment * * Since we don't make any reservations, in this version, or get any * pins to release, always succeed * * @param[in] obj_pub Public object handle * @param[in] req_ctx Request context * @param[in] lrf_body Nothing for us * @param[in] arg Input arguments of the function * * @return Valid error codes in RFC 5661, p. 367. */ static nfsstat4 layoutreturn(struct fsal_obj_handle *obj_pub, struct req_op_context *req_ctx, XDR *lrf_body, const struct fsal_layoutreturn_arg *arg) { /* The private 'full' export */ struct export *export = container_of(req_ctx->fsal_export, struct export, export); /* The private 'full' object handle */ struct handle *handle = container_of(obj_pub, struct handle, handle); /* Sanity check on type */ if (arg->lo_type != LAYOUT4_NFSV4_1_FILES) { LogCrit(COMPONENT_PNFS, "Unsupported layout type: %x", arg->lo_type); return NFS4ERR_UNKNOWN_LAYOUTTYPE; } if (arg->dispose) { PTHREAD_RWLOCK_wrlock(&handle->handle.obj_lock); if (arg->cur_segment.io_mode == LAYOUTIOMODE4_READ) { if (--handle->rd_issued != 0) { PTHREAD_RWLOCK_unlock(&handle->handle.obj_lock); return NFS4_OK; } } else { if (--handle->rd_issued != 0) { PTHREAD_RWLOCK_unlock(&handle->handle.obj_lock); return NFS4_OK; } } #if 0 ceph_ll_return_rw(export->cmount, handle->wire.vi, arg->cur_segment.io_mode == LAYOUTIOMODE4_READ ? handle->rd_serial : handle->rw_serial); #endif PTHREAD_RWLOCK_unlock(&handle->handle.obj_lock); } return NFS4_OK; } /** * @brief Commit a segment of a layout * * Update the size and time for a file accessed through a layout. * * @param[in] obj_pub Public object handle * @param[in] req_ctx Request context * @param[in] lou_body An XDR stream containing the layout * type-specific portion of the LAYOUTCOMMIT * arguments. * @param[in] arg Input arguments of the function * @param[in,out] res In/out and output arguments of the function * * @return Valid error codes in RFC 5661, p. 366. */ static nfsstat4 layoutcommit(struct fsal_obj_handle *obj_pub, struct req_op_context *req_ctx, XDR *lou_body, const struct fsal_layoutcommit_arg *arg, struct fsal_layoutcommit_res *res) { /* The private 'full' export */ struct export *export = container_of(req_ctx->fsal_export, struct export, export); /* The private 'full' object handle */ struct handle *handle = container_of(obj_pub, struct handle, handle); /* Old stat, so we don't truncate file or reverse time */ struct ceph_statx stxold; /* new stat to set time and size */ struct ceph_statx stxnew; /* Mask to determine exactly what gets set */ int attrmask = 0; /* Error returns from Ceph */ int ceph_status = 0; /* Sanity check on type */ if (arg->type != LAYOUT4_NFSV4_1_FILES) { LogCrit(COMPONENT_PNFS, "Unsupported layout type: %x", arg->type); return NFS4ERR_UNKNOWN_LAYOUTTYPE; } /* A more proper and robust implementation of this would use Ceph caps, but we need to hack at the client to expose those before it can work. */ ceph_status = fsal_ceph_ll_getattr(export->cmount, handle->wire.vi, &stxold, CEPH_STATX_SIZE|CEPH_STATX_MTIME, op_ctx->creds); if (ceph_status < 0) { LogCrit(COMPONENT_PNFS, "Error %d in attempt to get attributes of file %" PRIu64 ".", -ceph_status, handle->wire.vi.ino.val); return posix2nfs4_error(-ceph_status); } stxnew->stx_mask = 0; if (arg->new_offset) { if (stxold.stx_size < arg->last_write + 1) { attrmask |= CEPH_SETATTR_SIZE; stxnew.stx_size = arg->last_write + 1; res->size_supplied = true; res->new_size = arg->last_write + 1; } } if (arg->time_changed && (arg->new_time.seconds > stxold.stx_mtime || (arg->new_time.seconds == stxold.stx_mtime && arg->new_time.nseconds > stxold.stx_mtime_ns))) { stxnew.stx_mtime = arg->new_time.seconds; stxnew.stx_mtime_ns = arg->new_time.nseconds; } else { struct timespec now; ceph_status = clock_gettime(CLOCK_REALTIME, &now); if (ceph_status != 0) return posix2nfs4_error(errno); stxnew.stx_mtime = now.tv_sec; stxnew.stx_mtime_ns = now.tv_nsec; } attrmask |= CEPH_SETATTR_MTIME; ceph_status = fsal_ceph_ll_setattr(export->cmount, handle->wire.vi, &stxnew, attrmask, op_ctx->creds); if (ceph_status < 0) { LogCrit(COMPONENT_PNFS, "Error %d in attempt to get attributes of file %" PRIu64 ".", -ceph_status, handle->wire.vi.ino.val); return posix2nfs4_error(-ceph_status); } /* This is likely universal for files. */ res->commit_done = true; return NFS4_OK; } void handle_ops_pnfs(struct fsal_obj_ops *ops) { ops->layoutget = layoutget; ops->layoutreturn = layoutreturn; ops->layoutcommit = layoutcommit; } #endif /* CEPH_PNFS */ nfs-ganesha-2.6.0/src/FSAL/FSAL_CEPH/statx_compat.c000066400000000000000000000127261324272410200214300ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright 2016 Red Hat, Inc. and/or its affiliates. * Author: Jeff Layton * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include #include #include #include #include #include "common_utils.h" #include "fsal_types.h" #include "statx_compat.h" static void posix2ceph_statx(struct stat *st, struct ceph_statx *stx) { memset(stx, 0, sizeof(*stx)); stx->stx_mask = CEPH_STATX_BASIC_STATS | CEPH_STATX_VERSION; stx->stx_blksize = st->st_blksize; stx->stx_nlink = st->st_nlink; stx->stx_uid = st->st_uid; stx->stx_gid = st->st_gid; stx->stx_mode = st->st_mode; stx->stx_ino = st->st_ino; stx->stx_size = st->st_size; stx->stx_blocks = st->st_blocks; stx->stx_dev = st->st_dev; stx->stx_rdev = st->st_rdev; stx->stx_atime = st->st_atim; stx->stx_ctime = st->st_ctim; stx->stx_mtime = st->st_mtim; stx->stx_version = timespec_to_nsecs(&st->st_ctim); } int fsal_ceph_ll_walk(struct ceph_mount_info *cmount, const char *name, Inode **i, struct ceph_statx *stx, bool full, const struct user_cred *cred) { int rc; struct stat st; rc = ceph_ll_walk(cmount, name, i, &st); if (rc == 0) posix2ceph_statx(&st, stx); return rc; } int fsal_ceph_ll_getattr(struct ceph_mount_info *cmount, struct Inode *in, struct ceph_statx *stx, unsigned int want, const struct user_cred *cred) { int rc; struct stat st; rc = ceph_ll_getattr(cmount, in, &st, cred->caller_uid, cred->caller_gid); if (rc == 0) posix2ceph_statx(&st, stx); return rc; } int fsal_ceph_ll_lookup(struct ceph_mount_info *cmount, Inode *parent, const char *name, Inode **out, struct ceph_statx *stx, bool full, const struct user_cred *cred) { int rc; struct stat st; rc = ceph_ll_lookup(cmount, parent, name, &st, out, cred->caller_uid, cred->caller_gid); if (rc == 0) posix2ceph_statx(&st, stx); return rc; } int fsal_ceph_ll_mkdir(struct ceph_mount_info *cmount, Inode *parent, const char *name, mode_t mode, Inode **out, struct ceph_statx *stx, bool full, const struct user_cred *cred) { int rc; struct stat st; rc = ceph_ll_mkdir(cmount, parent, name, mode, &st, out, cred->caller_uid, cred->caller_gid); if (rc == 0) posix2ceph_statx(&st, stx); return rc; } #ifdef USE_FSAL_CEPH_MKNOD int fsal_ceph_ll_mknod(struct ceph_mount_info *cmount, Inode *parent, const char *name, mode_t mode, dev_t rdev, Inode **out, struct ceph_statx *stx, bool full, const struct user_cred *cred) { int rc; struct stat st; rc = ceph_ll_mknod(cmount, parent, name, mode, rdev, &st, out, cred->caller_uid, cred->caller_gid); if (rc == 0) posix2ceph_statx(&st, stx); return rc; } #endif /* USE_FSAL_CEPH_MKNOD */ int fsal_ceph_ll_symlink(struct ceph_mount_info *cmount, Inode *parent, const char *name, const char *link_path, Inode **out, struct ceph_statx *stx, bool full, const struct user_cred *cred) { int rc; struct stat st; rc = ceph_ll_symlink(cmount, parent, name, link_path, &st, out, cred->caller_uid, cred->caller_gid); if (rc == 0) posix2ceph_statx(&st, stx); return rc; } int fsal_ceph_ll_create(struct ceph_mount_info *cmount, Inode *parent, const char *name, mode_t mode, int oflags, Inode **outp, Fh **fhp, struct ceph_statx *stx, bool full, const struct user_cred *cred) { int rc; struct stat st; rc = ceph_ll_create(cmount, parent, name, mode, oflags, &st, outp, fhp, cred->caller_uid, cred->caller_gid); if (rc == 0) posix2ceph_statx(&st, stx); return rc; } int fsal_ceph_ll_setattr(struct ceph_mount_info *cmount, Inode *i, struct ceph_statx *stx, unsigned int mask, const struct user_cred *cred) { struct stat st; memset(&st, 0, sizeof(st)); if (mask & CEPH_SETATTR_MODE) st.st_mode = stx->stx_mode; if (mask & CEPH_SETATTR_UID) st.st_uid = stx->stx_uid; if (mask & CEPH_SETATTR_GID) st.st_gid = stx->stx_gid; if (mask & CEPH_SETATTR_ATIME) st.st_atim = stx->stx_atime; if (mask & CEPH_SETATTR_MTIME) st.st_mtim = stx->stx_mtime; if (mask & CEPH_SETATTR_CTIME) st.st_ctim = stx->stx_ctime; if (mask & CEPH_SETATTR_SIZE) st.st_size = stx->stx_size; return ceph_ll_setattr(cmount, i, &st, mask, cred->caller_uid, cred->caller_gid); } int fsal_ceph_readdirplus(struct ceph_mount_info *cmount, struct ceph_dir_result *dirp, Inode *dir, struct dirent *de, struct ceph_statx *stx, unsigned int want, unsigned int flags, Inode **out, struct user_cred *cred) { int stmask, rc; struct stat st; rc = ceph_readdirplus_r(cmount, dirp, de, &st, &stmask); if (rc <= 0) return rc; if (flags & AT_NO_ATTR_SYNC) { posix2ceph_statx(&st, stx); } else { rc = fsal_ceph_ll_lookup(cmount, dir, de->d_name, out, stx, true, cred); if (rc >= 0) rc = 1; } return rc; } nfs-ganesha-2.6.0/src/FSAL/FSAL_CEPH/statx_compat.h000066400000000000000000000313631324272410200214330ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright 2016 Red Hat, Inc. and/or its affiliates. * Author: Jeff Layton * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef _FSAL_CEPH_STATX_COMPAT_H #define _FSAL_CEPH_STATX_COMPAT_H #include "fsal_types.h" /* * Depending on what we'll be doing with the resulting statx structure, we * either set the mask for the minimum that construct_handle requires, or a * full set of attributes. * * Note that even though construct_handle accesses the stx_mode field, we * don't need to request CEPH_STATX_MODE here, as the type bits are always * accessible. */ #define CEPH_STATX_HANDLE_MASK (CEPH_STATX_INO) #define CEPH_STATX_ATTR_MASK (CEPH_STATX_BASIC_STATS | \ CEPH_STATX_BTIME | \ CEPH_STATX_VERSION) #ifdef USE_FSAL_CEPH_STATX static inline UserPerm * user_cred2ceph(const struct user_cred *cred) { return ceph_userperm_new(cred->caller_uid, cred->caller_gid, cred->caller_glen, cred->caller_garray); } static inline int fsal_ceph_ll_walk(struct ceph_mount_info *cmount, const char *name, Inode **i, struct ceph_statx *stx, bool full, const struct user_cred *creds) { int ret; UserPerm *perms = user_cred2ceph(creds); if (!perms) return -ENOMEM; ret = ceph_ll_walk(cmount, name, i, stx, full ? CEPH_STATX_ATTR_MASK : CEPH_STATX_HANDLE_MASK, 0, perms); ceph_userperm_destroy(perms); return ret; } static inline int fsal_ceph_ll_getattr(struct ceph_mount_info *cmount, struct Inode *in, struct ceph_statx *stx, unsigned int want, const struct user_cred *creds) { int ret; UserPerm *perms = user_cred2ceph(creds); if (!perms) return -ENOMEM; ret = ceph_ll_getattr(cmount, in, stx, want, 0, perms); ceph_userperm_destroy(perms); return ret; } static inline int fsal_ceph_ll_lookup(struct ceph_mount_info *cmount, Inode *parent, const char *name, Inode **out, struct ceph_statx *stx, bool full, const struct user_cred *creds) { int ret; UserPerm *perms = user_cred2ceph(creds); if (!perms) return -ENOMEM; ret = ceph_ll_lookup(cmount, parent, name, out, stx, full ? CEPH_STATX_ATTR_MASK : CEPH_STATX_HANDLE_MASK, 0, perms); ceph_userperm_destroy(perms); return ret; } static inline int fsal_ceph_ll_mkdir(struct ceph_mount_info *cmount, Inode *parent, const char *name, mode_t mode, Inode **out, struct ceph_statx *stx, bool full, const struct user_cred *creds) { int ret; UserPerm *perms = user_cred2ceph(creds); if (!perms) return -ENOMEM; ret = ceph_ll_mkdir(cmount, parent, name, mode, out, stx, full ? CEPH_STATX_ATTR_MASK : CEPH_STATX_HANDLE_MASK, 0, perms); ceph_userperm_destroy(perms); return ret; } static inline int fsal_ceph_ll_mknod(struct ceph_mount_info *cmount, Inode *parent, const char *name, mode_t mode, dev_t rdev, Inode **out, struct ceph_statx *stx, bool full, const struct user_cred *creds) { int ret; UserPerm *perms = user_cred2ceph(creds); if (!perms) return -ENOMEM; ret = ceph_ll_mknod(cmount, parent, name, mode, rdev, out, stx, full ? CEPH_STATX_ATTR_MASK : CEPH_STATX_HANDLE_MASK, 0, perms); ceph_userperm_destroy(perms); return ret; } static inline int fsal_ceph_ll_symlink(struct ceph_mount_info *cmount, Inode *parent, const char *name, const char *link_path, Inode **out, struct ceph_statx *stx, bool full, const struct user_cred *creds) { int ret; UserPerm *perms = user_cred2ceph(creds); if (!perms) return -ENOMEM; ret = ceph_ll_symlink(cmount, parent, name, link_path, out, stx, full ? CEPH_STATX_ATTR_MASK : CEPH_STATX_HANDLE_MASK, 0, perms); ceph_userperm_destroy(perms); return ret; } static inline int fsal_ceph_ll_readlink(struct ceph_mount_info *cmount, Inode *in, char *buf, size_t bufsize, const struct user_cred *creds) { int ret; UserPerm *perms = user_cred2ceph(creds); if (!perms) return -ENOMEM; ret = ceph_ll_readlink(cmount, in, buf, bufsize, perms); ceph_userperm_destroy(perms); return ret; } static inline int fsal_ceph_ll_create(struct ceph_mount_info *cmount, Inode *parent, const char *name, mode_t mode, int oflags, Inode **outp, Fh **fhp, struct ceph_statx *stx, bool full, const struct user_cred *creds) { int ret; UserPerm *perms = user_cred2ceph(creds); if (!perms) return -ENOMEM; ret = ceph_ll_create(cmount, parent, name, mode, oflags, outp, fhp, stx, full ? CEPH_STATX_ATTR_MASK : CEPH_STATX_HANDLE_MASK, 0, perms); ceph_userperm_destroy(perms); return ret; } static inline int fsal_ceph_ll_setattr(struct ceph_mount_info *cmount, Inode *i, struct ceph_statx *stx, unsigned int mask, const struct user_cred *creds) { int ret; UserPerm *perms = user_cred2ceph(creds); if (!perms) return -ENOMEM; ret = ceph_ll_setattr(cmount, i, stx, mask, perms); ceph_userperm_destroy(perms); return ret; } static inline int fsal_ceph_ll_open(struct ceph_mount_info *cmount, Inode *i, int flags, Fh **fh, const struct user_cred *creds) { int ret; UserPerm *perms = user_cred2ceph(creds); if (!perms) return -ENOMEM; ret = ceph_ll_open(cmount, i, flags, fh, perms); ceph_userperm_destroy(perms); return ret; } static inline int fsal_ceph_ll_opendir(struct ceph_mount_info *cmount, struct Inode *in, struct ceph_dir_result **dirpp, const struct user_cred *creds) { int ret; UserPerm *perms = user_cred2ceph(creds); if (!perms) return -ENOMEM; ret = ceph_ll_opendir(cmount, in, dirpp, perms); ceph_userperm_destroy(perms); return ret; } static inline int fsal_ceph_ll_link(struct ceph_mount_info *cmount, Inode *i, Inode *newparent, const char *name, const struct user_cred *creds) { int ret; UserPerm *perms = user_cred2ceph(creds); if (!perms) return -ENOMEM; ret = ceph_ll_link(cmount, i, newparent, name, perms); ceph_userperm_destroy(perms); return ret; } static inline int fsal_ceph_ll_unlink(struct ceph_mount_info *cmount, struct Inode *in, const char *name, const struct user_cred *creds) { int ret; UserPerm *perms = user_cred2ceph(creds); if (!perms) return -ENOMEM; ret = ceph_ll_unlink(cmount, in, name, perms); ceph_userperm_destroy(perms); return ret; } static inline int fsal_ceph_ll_rename(struct ceph_mount_info *cmount, struct Inode *parent, const char *name, struct Inode *newparent, const char *newname, const struct user_cred *creds) { int ret; UserPerm *perms = user_cred2ceph(creds); if (!perms) return -ENOMEM; ret = ceph_ll_rename(cmount, parent, name, newparent, newname, perms); ceph_userperm_destroy(perms); return ret; } static inline int fsal_ceph_ll_rmdir(struct ceph_mount_info *cmount, struct Inode *in, const char *name, const struct user_cred *creds) { int ret; UserPerm *perms = user_cred2ceph(creds); if (!perms) return -ENOMEM; ret = ceph_ll_rmdir(cmount, in, name, perms); ceph_userperm_destroy(perms); return ret; } static inline int fsal_ceph_readdirplus(struct ceph_mount_info *cmount, struct ceph_dir_result *dirp, Inode *dir, struct dirent *de, struct ceph_statx *stx, unsigned int want, unsigned int flags, Inode **out, struct user_cred *cred) { return ceph_readdirplus_r(cmount, dirp, de, stx, want, flags, out); } #else /* USE_FSAL_CEPH_STATX */ #ifndef AT_NO_ATTR_SYNC #define AT_NO_ATTR_SYNC 0x4000 #endif /* AT_NO_ATTR_SYNC */ struct ceph_statx { uint32_t stx_mask; uint32_t stx_blksize; uint32_t stx_nlink; uint32_t stx_uid; uint32_t stx_gid; uint16_t stx_mode; uint64_t stx_ino; uint64_t stx_size; uint64_t stx_blocks; dev_t stx_dev; dev_t stx_rdev; struct timespec stx_atime; struct timespec stx_ctime; struct timespec stx_mtime; struct timespec stx_btime; uint64_t stx_version; }; #define CEPH_STATX_MODE 0x00000001U /* Want/got stx_mode */ #define CEPH_STATX_NLINK 0x00000002U /* Want/got stx_nlink */ #define CEPH_STATX_UID 0x00000004U /* Want/got stx_uid */ #define CEPH_STATX_GID 0x00000008U /* Want/got stx_gid */ #define CEPH_STATX_RDEV 0x00000010U /* Want/got stx_rdev */ #define CEPH_STATX_ATIME 0x00000020U /* Want/got stx_atime */ #define CEPH_STATX_MTIME 0x00000040U /* Want/got stx_mtime */ #define CEPH_STATX_CTIME 0x00000080U /* Want/got stx_ctime */ #define CEPH_STATX_INO 0x00000100U /* Want/got stx_ino */ #define CEPH_STATX_SIZE 0x00000200U /* Want/got stx_size */ #define CEPH_STATX_BLOCKS 0x00000400U /* Want/got stx_blocks */ #define CEPH_STATX_BASIC_STATS 0x000007ffU /* posix stat struct fields */ #define CEPH_STATX_BTIME 0x00000800U /* Want/got stx_btime */ #define CEPH_STATX_VERSION 0x00001000U /* Want/got stx_version */ #define CEPH_STATX_ALL_STATS 0x00001fffU /* All supported stats */ int fsal_ceph_ll_walk(struct ceph_mount_info *cmount, const char *name, Inode **i, struct ceph_statx *stx, bool full, const struct user_cred *cred); int fsal_ceph_ll_getattr(struct ceph_mount_info *cmount, struct Inode *in, struct ceph_statx *stx, unsigned int want, const struct user_cred *cred); int fsal_ceph_ll_lookup(struct ceph_mount_info *cmount, Inode *parent, const char *name, Inode **out, struct ceph_statx *stx, bool full, const struct user_cred *cred); int fsal_ceph_ll_mkdir(struct ceph_mount_info *cmount, Inode *parent, const char *name, mode_t mode, Inode **out, struct ceph_statx *stx, bool full, const struct user_cred *cred); #ifdef USE_FSAL_CEPH_MKNOD int fsal_ceph_ll_mknod(struct ceph_mount_info *cmount, Inode *parent, const char *name, mode_t mode, dev_t rdev, Inode **out, struct ceph_statx *stx, bool full, const struct user_cred *cred); #endif /* USE_FSAL_CEPH_MKNOD */ int fsal_ceph_ll_symlink(struct ceph_mount_info *cmount, Inode *parent, const char *name, const char *link_path, Inode **out, struct ceph_statx *stx, bool full, const struct user_cred *cred); int fsal_ceph_ll_create(struct ceph_mount_info *cmount, Inode *parent, const char *name, mode_t mode, int oflags, Inode **outp, Fh **fhp, struct ceph_statx *stx, bool full, const struct user_cred *cred); int fsal_ceph_ll_setattr(struct ceph_mount_info *cmount, Inode *i, struct ceph_statx *stx, unsigned int mask, const struct user_cred *cred); int fsal_ceph_readdirplus(struct ceph_mount_info *cmount, struct ceph_dir_result *dirp, Inode *dir, struct dirent *de, struct ceph_statx *stx, unsigned int want, unsigned int flags, Inode **out, struct user_cred *cred); static inline int fsal_ceph_ll_readlink(struct ceph_mount_info *cmount, Inode *in, char *buf, size_t bufsize, const struct user_cred *creds) { return ceph_ll_readlink(cmount, in, buf, bufsize, creds->caller_uid, creds->caller_gid); } static inline int fsal_ceph_ll_open(struct ceph_mount_info *cmount, Inode *i, int flags, Fh **fh, const struct user_cred *cred) { return ceph_ll_open(cmount, i, flags, fh, cred->caller_uid, cred->caller_gid); } static inline int fsal_ceph_ll_opendir(struct ceph_mount_info *cmount, struct Inode *in, struct ceph_dir_result **dirpp, const struct user_cred *cred) { return ceph_ll_opendir(cmount, in, dirpp, cred->caller_uid, cred->caller_gid); } static inline int fsal_ceph_ll_link(struct ceph_mount_info *cmount, Inode *i, Inode *newparent, const char *name, const struct user_cred *cred) { struct stat st; return ceph_ll_link(cmount, i, newparent, name, &st, cred->caller_uid, cred->caller_gid); } static inline int fsal_ceph_ll_unlink(struct ceph_mount_info *cmount, struct Inode *in, const char *name, const struct user_cred *cred) { return ceph_ll_unlink(cmount, in, name, cred->caller_uid, cred->caller_gid); } static inline int fsal_ceph_ll_rename(struct ceph_mount_info *cmount, struct Inode *parent, const char *name, struct Inode *newparent, const char *newname, const struct user_cred *cred) { return ceph_ll_rename(cmount, parent, name, newparent, newname, cred->caller_uid, cred->caller_gid); } static inline int fsal_ceph_ll_rmdir(struct ceph_mount_info *cmount, struct Inode *in, const char *name, const struct user_cred *cred) { return ceph_ll_rmdir(cmount, in, name, cred->caller_uid, cred->caller_gid); } #endif /* USE_FSAL_CEPH_STATX */ #endif /* _FSAL_CEPH_STATX_COMPAT_H */ nfs-ganesha-2.6.0/src/FSAL/FSAL_GLUSTER/000077500000000000000000000000001324272410200171545ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/FSAL/FSAL_GLUSTER/CMakeLists.txt000066400000000000000000000013621324272410200217160ustar00rootroot00000000000000add_definitions( -D__USE_GNU -D_GNU_SOURCE ${GFAPI_CFLAGS} ) set( LIB_PREFIX 64) #add_subdirectory(pnfs_panfs) #if(USE_FSAL_XFS) # add_subdirectory(xfs) #endif(USE_FSAL_XFS) ########### next target ############### SET(fsalgluster_LIB_SRCS main.c export.c handle.c fsal_up.c gluster_internal.h gluster_internal.c mds.c ds.c posix_acls.h posix_acls.c ) add_library(fsalgluster MODULE ${fsalgluster_LIB_SRCS}) add_sanitizers(fsalgluster) target_link_libraries(fsalgluster ${SYSTEM_LIBRARIES} ${GFAPI_LIBRARIES} ) set_target_properties(fsalgluster PROPERTIES VERSION 4.2.0 SOVERSION 4) install(TARGETS fsalgluster COMPONENT fsal DESTINATION ${FSAL_DESTINATION} ) ########### install files ############### nfs-ganesha-2.6.0/src/FSAL/FSAL_GLUSTER/README000066400000000000000000000017541324272410200200430ustar00rootroot00000000000000################################################### # Compiling nfs-ganesha with FSAL_GLUSTER support: #----------------------------------------------- # #1. You need the libgfapi.so installed. #2. You also need glfs.h, glfs-handles.h files # which are exported and available for use on # installing a standard glusterfs src/bin rpms. # #----------------------------------------------- # Exporting GLUSTERFS volume: #----------------------------------------------- # # To export a GLUSTERFS volume, make sure # * the volume is online and healthy. # * gluster-nfs and kernel-nfs are disbaled. # * Create a sample .conf file with the Gluster # volume related info. Sample config file is # located at 'src/config_samples/gluster.conf' # # For extensive list of NFS options available, # please refer to 'src/config_samples/config.txt' # * Execute the command - # 'ganesha.nfsd -f /path/to/config_file -L /path/to/log/file -N debug_level -d' ################################################### nfs-ganesha-2.6.0/src/FSAL/FSAL_GLUSTER/ds.c000066400000000000000000000226701324272410200177350ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Red Hat Inc., 2014 * Author: Jiffin Tony Thottan jthottan@redhat.com * : Anand Subramanian anands@redhat.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ #include #include "fsal.h" #include "fsal_types.h" #include "fsal_api.h" #include "fsal_up.h" #include "gluster_internal.h" #include "FSAL/fsal_commonlib.h" #include "fsal_convert.h" #include "pnfs_utils.h" #include "nfs_exports.h" #include #include "../fsal_private.h" /** * @brief Release a DS object * * @param[in] obj_pub The object to release * * @return NFS Status codes. */ static void release(struct fsal_ds_handle *const ds_pub) { int rc = 0; struct glfs_ds_handle *ds = container_of(ds_pub, struct glfs_ds_handle, ds); fsal_ds_handle_fini(&ds->ds); if (ds->glhandle) { rc = glfs_h_close(ds->glhandle); if (rc) { LogMajor(COMPONENT_PNFS, "glfs_h_close returned error %s(%d)", strerror(errno), errno); } } gsh_free(ds); } /** * @brief Read from a data-server handle. * * NFSv4.1 data server handles are disjount from normal * filehandles (in Ganesha, there is a ds_flag in the filehandle_v4_t * structure) and do not get loaded into cache_inode or processed the * normal way. * * @param[in] ds_pub FSAL DS handle * @param[in] req_ctx Credentials * @param[in] stateid The stateid supplied with the READ operation, * for validation * @param[in] offset The offset at which to read * @param[in] requested_length Length of read requested (and size of buffer) * @param[out] buffer The buffer to which to store read data * @param[out] supplied_length Length of data read * @param[out] eof True on end of file * * @return An NFSv4.1 status code. */ static nfsstat4 ds_read(struct fsal_ds_handle *const ds_pub, struct req_op_context *const req_ctx, const stateid4 *stateid, const offset4 offset, const count4 requested_length, void *const buffer, count4 * const supplied_length, bool * const end_of_file) { /* The private DS handle */ struct glfs_ds_handle *ds = container_of(ds_pub, struct glfs_ds_handle, ds); int rc = 0; struct glusterfs_export *glfs_export = container_of(ds_pub->pds->mds_fsal_export, struct glusterfs_export, export); if (ds->glhandle == NULL) LogDebug(COMPONENT_PNFS, "glhandle NULL"); rc = glfs_h_anonymous_read(glfs_export->gl_fs->fs, ds->glhandle, buffer, requested_length, offset); if (rc < 0) { rc = errno; LogMajor(COMPONENT_PNFS, "Read failed on DS"); return posix2nfs4_error(rc); } *supplied_length = rc; if (rc == 0 || rc < requested_length) *end_of_file = true; return NFS4_OK; } /** * * @brief Write to a data-server handle. * * @param[in] ds_pub FSAL DS handle * @param[in] req_ctx Credentials * @param[in] stateid The stateid supplied with the READ operation, * for validation * @param[in] offset The offset at which to read * @param[in] write_length Length of write requested (and size of buffer) * @param[out] buffer The buffer to which to store read data * @param[in] stability wanted Stability of write * @param[out] written_length Length of data written * @param[out] writeverf Write verifier * @param[out] stability_got Stability used for write (must be as * or more stable than request) * * @return An NFSv4.1 status code. */ static nfsstat4 ds_write(struct fsal_ds_handle *const ds_pub, struct req_op_context *const req_ctx, const stateid4 *stateid, const offset4 offset, const count4 write_length, const void *buffer, const stable_how4 stability_wanted, count4 * const written_length, verifier4 * const writeverf, stable_how4 * const stability_got) { struct glfs_ds_handle *ds = container_of(ds_pub, struct glfs_ds_handle, ds); struct glusterfs_export *glfs_export = container_of(ds_pub->pds->mds_fsal_export, struct glusterfs_export, export); int rc = 0; memset(writeverf, 0, NFS4_VERIFIER_SIZE); if (ds->glhandle == NULL) LogDebug(COMPONENT_PNFS, "glhandle NULL"); rc = glfs_h_anonymous_write(glfs_export->gl_fs->fs, ds->glhandle, buffer, write_length, offset); if (rc < 0) { rc = errno; LogMajor(COMPONENT_PNFS, "status after write %d", rc); return posix2nfs4_error(rc); } /** @todo:Here DS is performing the write operation, so the MDS is not * aware of the change.We should inform MDS through upcalls about * change in file attributes such as size and time. */ *written_length = rc; *stability_got = stability_wanted; ds->stability_got = stability_wanted; /* Incase of MDS being DS, there shall not be upcalls sent from * backend. Hence invalidate the entry here */ (void)upcall_inode_invalidate(glfs_export->gl_fs, ds->glhandle); return NFS4_OK; } /** * @brief Commit a byte range to a DS handle. * * NFSv4.1 data server filehandles are disjount from normal * filehandles (in Ganesha, there is a ds_flag in the filehandle_v4_t * structure) and do not get loaded into cache_inode or processed the * normal way. * * @param[in] ds_pub FSAL DS handle * @param[in] req_ctx Credentials * @param[in] offset Start of commit window * @param[in] count Length of commit window * @param[out] writeverf Write verifier * * @return An NFSv4.1 status code. */ static nfsstat4 ds_commit(struct fsal_ds_handle *const ds_pub, struct req_op_context *const req_ctx, const offset4 offset, const count4 count, verifier4 * const writeverf) { memset(writeverf, 0, NFS4_VERIFIER_SIZE); struct glfs_ds_handle *ds = container_of(ds_pub, struct glfs_ds_handle, ds); int rc = 0; fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; if (ds->stability_got == FILE_SYNC4) { struct glusterfs_export *glfs_export = container_of(ds_pub->pds->mds_fsal_export, struct glusterfs_export, export); struct glfs_fd *glfd = NULL; SET_GLUSTER_CREDS(glfs_export, &op_ctx->creds->caller_uid, &op_ctx->creds->caller_gid, op_ctx->creds->caller_glen, op_ctx->creds->caller_garray); glfd = glfs_h_open(glfs_export->gl_fs->fs, ds->glhandle, O_RDWR); if (glfd == NULL) { LogDebug(COMPONENT_PNFS, "glfd in ds_handle is NULL"); SET_GLUSTER_CREDS(glfs_export, NULL, NULL, 0, NULL); return NFS4ERR_SERVERFAULT; } rc = glfs_fsync(glfd); if (rc != 0) LogMajor(COMPONENT_PNFS, "glfs_fsync failed %d", errno); rc = glfs_close(glfd); if (rc != 0) LogDebug(COMPONENT_PNFS, "status after close %d", errno); SET_GLUSTER_CREDS(glfs_export, NULL, NULL, 0, NULL); } if ((rc != 0) || (status.major != ERR_FSAL_NO_ERROR)) return NFS4ERR_INVAL; return NFS4_OK; } /* Initialise DS operations */ void dsh_ops_init(struct fsal_dsh_ops *ops) { ops->release = release; ops->read = ds_read; ops->write = ds_write; ops->commit = ds_commit; }; /** * @brief Create a FSAL data server handle from a wire handle * * This function creates a FSAL data server handle from a client * supplied "wire" handle. This is also where validation gets done, * since PUTFH is the only operation that can return * NFS4ERR_BADHANDLE. * * @param[in] export_pub The export in which to create the handle * @param[in] desc Buffer from which to create the file * @param[out] ds_pub FSAL data server handle * * @return NFSv4.1 error codes. */ static nfsstat4 make_ds_handle(struct fsal_pnfs_ds *const pds, const struct gsh_buffdesc *const hdl_desc, struct fsal_ds_handle **const handle, int flags) { /* Handle to be created for DS */ struct glfs_ds_handle *ds = NULL; unsigned char globjhdl[GFAPI_HANDLE_LENGTH] = {'\0'}; struct stat sb; struct glusterfs_export *glfs_export = container_of(pds->mds_fsal_export, struct glusterfs_export, export); *handle = NULL; if (hdl_desc->len != sizeof(struct glfs_ds_wire)) return NFS4ERR_BADHANDLE; ds = gsh_calloc(1, sizeof(struct glfs_ds_handle)); *handle = &ds->ds; fsal_ds_handle_init(*handle, pds); memcpy(globjhdl, hdl_desc->addr, GFAPI_HANDLE_LENGTH); /* Create glfs_object for the DS handle */ ds->glhandle = glfs_h_create_from_handle(glfs_export->gl_fs->fs, globjhdl, GFAPI_HANDLE_LENGTH, &sb); if (ds->glhandle == NULL) { LogDebug(COMPONENT_PNFS, "glhandle in ds_handle is NULL"); return NFS4ERR_SERVERFAULT; } /* Connect lazily when a FILE_SYNC4 write forces us to, not here. */ ds->connected = false; return NFS4_OK; } void pnfs_ds_ops_init(struct fsal_pnfs_ds_ops *ops) { memcpy(ops, &def_pnfs_ds_ops, sizeof(struct fsal_pnfs_ds_ops)); ops->make_ds_handle = make_ds_handle; ops->fsal_dsh_ops = dsh_ops_init; } nfs-ganesha-2.6.0/src/FSAL/FSAL_GLUSTER/export.c000066400000000000000000000603741324272410200206530ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Red Hat Inc., 2013 * Author: Anand Subramanian anands@redhat.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ /** * @file export.c * @author Shyamsundar R * @author Anand Subramanian * * @brief GLUSTERFS FSAL export object */ #include #include #include #include #include "fsal.h" #include "FSAL/fsal_config.h" #include "fsal_convert.h" #include "config_parsing.h" #include "gluster_internal.h" #include "nfs_exports.h" #include "export_mgr.h" #include "pnfs_utils.h" #include "sal_data.h" /* The default location of gfapi log * if glfs_log param is not defined in * the export file */ #define GFAPI_LOG_LOCATION "/var/log/ganesha/ganesha-gfapi.log" /** * @brief Implements GLUSTER FSAL exportoperation release */ static void export_release(struct fsal_export *exp_hdl) { struct glusterfs_export *glfs_export = container_of(exp_hdl, struct glusterfs_export, export); /* check activity on the export */ /* detach the export */ fsal_detach_export(glfs_export->export.fsal, &glfs_export->export.exports); free_export_ops(&glfs_export->export); glusterfs_free_fs(glfs_export->gl_fs); glfs_export->gl_fs = NULL; gsh_free(glfs_export->export_path); glfs_export->export_path = NULL; gsh_free(glfs_export); glfs_export = NULL; } /** * @brief Implements GLUSTER FSAL exportoperation lookup_path */ static fsal_status_t lookup_path(struct fsal_export *export_pub, const char *path, struct fsal_obj_handle **pub_handle, struct attrlist *attrs_out) { int rc = 0; fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; char *realpath = NULL; struct stat sb; struct glfs_object *glhandle = NULL; unsigned char globjhdl[GFAPI_HANDLE_LENGTH] = {'\0'}; struct glusterfs_handle *objhandle = NULL; struct glusterfs_export *glfs_export = container_of(export_pub, struct glusterfs_export, export); char vol_uuid[GLAPI_UUID_LENGTH] = {'\0'}; LogFullDebug(COMPONENT_FSAL, "In args: path = %s", path); *pub_handle = NULL; if (strcmp(path, glfs_export->mount_path) == 0) { realpath = gsh_strdup(glfs_export->export_path); } else { /* * mount path is not same as the exported one. Should be subdir * then. */ /** @todo: How do we handle symlinks if present in the path. */ realpath = gsh_malloc(strlen(glfs_export->export_path) + strlen(path) + 1); /* * Handle the case wherein glfs_export->export_path * is root i.e, '/' separately. */ if (strlen(glfs_export->export_path) != 1) { strcpy(realpath, glfs_export->export_path); strcpy((realpath + strlen(glfs_export->export_path)), &path[strlen(glfs_export->mount_path)]); } else { strcpy(realpath, &path[strlen(glfs_export->mount_path)]); } } glhandle = glfs_h_lookupat(glfs_export->gl_fs->fs, NULL, realpath, &sb, 1); if (glhandle == NULL) { status = gluster2fsal_error(errno); goto out; } rc = glfs_h_extract_handle(glhandle, globjhdl, GFAPI_HANDLE_LENGTH); if (rc < 0) { status = gluster2fsal_error(errno); goto out; } rc = glfs_get_volumeid(glfs_export->gl_fs->fs, vol_uuid, GLAPI_UUID_LENGTH); if (rc < 0) { status = gluster2fsal_error(errno); goto out; } construct_handle(glfs_export, &sb, glhandle, globjhdl, GLAPI_HANDLE_LENGTH, &objhandle, vol_uuid); if (attrs_out != NULL) { posix2fsal_attributes_all(&sb, attrs_out); } *pub_handle = &objhandle->handle; gsh_free(realpath); return status; out: gluster_cleanup_vars(glhandle); gsh_free(realpath); return status; } /** * @brief Implements GLUSTER FSAL exportoperation wire_to_host */ static fsal_status_t wire_to_host(struct fsal_export *exp_hdl, fsal_digesttype_t in_type, struct gsh_buffdesc *fh_desc, int flags) { size_t fh_size; #ifdef GLTIMING struct timespec s_time, e_time; now(&s_time); #endif /* sanity checks */ if (!fh_desc || !fh_desc->addr) return fsalstat(ERR_FSAL_FAULT, 0); fh_size = GLAPI_HANDLE_LENGTH; if (fh_desc->len != fh_size) { LogMajor(COMPONENT_FSAL, "Size mismatch for handle. should be %zu, got %zu", fh_size, fh_desc->len); return fsalstat(ERR_FSAL_SERVERFAULT, 0); } fh_desc->len = fh_size; /* pass back the actual size */ #ifdef GLTIMING now(&e_time); latency_update(&s_time, &e_time, lat_wire_to_host); #endif return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Implements GLUSTER FSAL exportoperation create_handle */ static fsal_status_t create_handle(struct fsal_export *export_pub, struct gsh_buffdesc *fh_desc, struct fsal_obj_handle **pub_handle, struct attrlist *attrs_out) { int rc = 0; fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; struct stat sb; struct glfs_object *glhandle = NULL; unsigned char globjhdl[GFAPI_HANDLE_LENGTH] = {'\0'}; struct glusterfs_handle *objhandle = NULL; struct glusterfs_export *glfs_export = container_of(export_pub, struct glusterfs_export, export); char vol_uuid[GLAPI_UUID_LENGTH] = {'\0'}; #ifdef GLTIMING struct timespec s_time, e_time; now(&s_time); #endif *pub_handle = NULL; if (fh_desc->len != GLAPI_HANDLE_LENGTH) { status.major = ERR_FSAL_INVAL; goto out; } /* First 16bytes contain volume UUID. globjhdl is in the second */ /* half(16bytes) of the fs_desc->addr. */ memcpy(globjhdl, fh_desc->addr+GLAPI_UUID_LENGTH, GFAPI_HANDLE_LENGTH); glhandle = glfs_h_create_from_handle(glfs_export->gl_fs->fs, globjhdl, GFAPI_HANDLE_LENGTH, &sb); if (glhandle == NULL) { status = gluster2fsal_error(errno); goto out; } rc = glfs_get_volumeid(glfs_export->gl_fs->fs, vol_uuid, GLAPI_UUID_LENGTH); if (rc < 0) { status = gluster2fsal_error(errno); goto out; } construct_handle(glfs_export, &sb, glhandle, globjhdl, GLAPI_HANDLE_LENGTH, &objhandle, vol_uuid); if (attrs_out != NULL) { posix2fsal_attributes_all(&sb, attrs_out); } *pub_handle = &objhandle->handle; out: if (status.major != ERR_FSAL_NO_ERROR) gluster_cleanup_vars(glhandle); #ifdef GLTIMING now(&e_time); latency_update(&s_time, &e_time, lat_create_handle); #endif return status; } /** * @brief Given a glfs_object handle, construct handle for * FSAL to use. */ fsal_status_t glfs2fsal_handle(struct glusterfs_export *glfs_export, struct glfs_object *glhandle, struct fsal_obj_handle **pub_handle, struct stat *sb, struct attrlist *attrs_out) { int rc = 0; fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; unsigned char globjhdl[GFAPI_HANDLE_LENGTH] = {'\0'}; struct glusterfs_handle *objhandle = NULL; char vol_uuid[GLAPI_UUID_LENGTH] = {'\0'}; #ifdef GLTIMING struct timespec s_time, e_time; now(&s_time); #endif *pub_handle = NULL; if (!glfs_export || !glhandle) { status.major = ERR_FSAL_INVAL; goto out; } rc = glfs_h_extract_handle(glhandle, globjhdl, GFAPI_HANDLE_LENGTH); if (rc < 0) { status = gluster2fsal_error(errno); goto out; } rc = glfs_get_volumeid(glfs_export->gl_fs->fs, vol_uuid, GLAPI_UUID_LENGTH); if (rc < 0) { status = gluster2fsal_error(errno); goto out; } construct_handle(glfs_export, sb, glhandle, globjhdl, GLAPI_HANDLE_LENGTH, &objhandle, vol_uuid); if (attrs_out != NULL) { posix2fsal_attributes_all(sb, attrs_out); } *pub_handle = &objhandle->handle; out: #ifdef GLTIMING now(&e_time); latency_update(&s_time, &e_time, lat_create_handle); #endif return status; } /** * @brief Implements GLUSTER FSAL exportoperation get_fs_dynamic_info */ static fsal_status_t get_dynamic_info(struct fsal_export *exp_hdl, struct fsal_obj_handle *obj_hdl, fsal_dynamicfsinfo_t *infop) { int rc = 0; fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; struct statvfs vfssb; struct glusterfs_export *glfs_export = container_of(exp_hdl, struct glusterfs_export, export); rc = glfs_statvfs(glfs_export->gl_fs->fs, glfs_export->export_path, &vfssb); if (rc != 0) return gluster2fsal_error(errno); memset(infop, 0, sizeof(fsal_dynamicfsinfo_t)); infop->total_bytes = vfssb.f_frsize * vfssb.f_blocks; infop->free_bytes = vfssb.f_frsize * vfssb.f_bfree; infop->avail_bytes = vfssb.f_frsize * vfssb.f_bavail; infop->total_files = vfssb.f_files; infop->free_files = vfssb.f_ffree; infop->avail_files = vfssb.f_favail; infop->time_delta.tv_sec = 1; infop->time_delta.tv_nsec = 0; return status; } /** * @brief Allocate a state_t structure * * Note that this is not expected to fail since memory allocation is * expected to abort on failure. * * @param[in] exp_hdl Export state_t will be associated with * @param[in] state_type Type of state to allocate * @param[in] related_state Related state if appropriate * * @returns a state structure. */ struct state_t *glusterfs_alloc_state(struct fsal_export *exp_hdl, enum state_type state_type, struct state_t *related_state) { struct state_t *state; struct glusterfs_fd *my_fd; state = init_state(gsh_calloc(1, sizeof(struct glusterfs_state_fd)), exp_hdl, state_type, related_state); my_fd = &container_of(state, struct glusterfs_state_fd, state)->glusterfs_fd; my_fd->glfd = NULL; my_fd->openflags = FSAL_O_CLOSED; PTHREAD_RWLOCK_init(&my_fd->fdlock, NULL); return state; } /** * @brief free a gluster_state_fd structure * * @param[in] exp_hdl Export state_t will be associated with * @param[in] state Related state if appropriate * */ void glusterfs_free_state(struct fsal_export *exp_hdl, struct state_t *state) { struct glusterfs_state_fd *state_fd = container_of(state, struct glusterfs_state_fd, state); struct glusterfs_fd *my_fd = &state_fd->glusterfs_fd; PTHREAD_RWLOCK_destroy(&my_fd->fdlock); gsh_free(state_fd); } /** @todo: We have gone POSIX way for the APIs below, can consider the CEPH way * in case all are constants across all volumes etc. */ /** * @brief Implements GLUSTER FSAL exportoperation fs_supports */ static bool fs_supports(struct fsal_export *exp_hdl, fsal_fsinfo_options_t option) { struct fsal_staticfsinfo_t *info; info = gluster_staticinfo(exp_hdl->fsal); return fsal_supports(info, option); } /** * @brief Implements GLUSTER FSAL exportoperation fs_maxfilesize */ static uint64_t fs_maxfilesize(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = gluster_staticinfo(exp_hdl->fsal); return fsal_maxfilesize(info); } /** * @brief Implements GLUSTER FSAL exportoperation fs_maxread */ static uint32_t fs_maxread(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = gluster_staticinfo(exp_hdl->fsal); return fsal_maxread(info); } /** * @brief Implements GLUSTER FSAL exportoperation fs_maxwrite */ static uint32_t fs_maxwrite(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = gluster_staticinfo(exp_hdl->fsal); return fsal_maxwrite(info); } /** * @brief Implements GLUSTER FSAL exportoperation fs_maxlink */ static uint32_t fs_maxlink(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = gluster_staticinfo(exp_hdl->fsal); return fsal_maxlink(info); } /** * @brief Implements GLUSTER FSAL exportoperation fs_maxnamelen */ static uint32_t fs_maxnamelen(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = gluster_staticinfo(exp_hdl->fsal); return fsal_maxnamelen(info); } /** * @brief Implements GLUSTER FSAL exportoperation fs_maxpathlen */ static uint32_t fs_maxpathlen(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = gluster_staticinfo(exp_hdl->fsal); return fsal_maxpathlen(info); } /** * @brief Implements GLUSTER FSAL exportoperation fs_lease_time */ static struct timespec fs_lease_time(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = gluster_staticinfo(exp_hdl->fsal); return fsal_lease_time(info); } /** * @brief Implements GLUSTER FSAL exportoperation fs_acl_support */ static fsal_aclsupp_t fs_acl_support(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = gluster_staticinfo(exp_hdl->fsal); return fsal_acl_support(info); } /** * @brief Implements GLUSTER FSAL exportoperation fs_supported_attrs */ static attrmask_t fs_supported_attrs(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; attrmask_t supported_mask; info = gluster_staticinfo(exp_hdl->fsal); supported_mask = fsal_supported_attrs(info); if (!NFSv4_ACL_SUPPORT) supported_mask &= ~ATTR_ACL; return supported_mask; } /** * @brief Implements GLUSTER FSAL exportoperation fs_umask */ static uint32_t fs_umask(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = gluster_staticinfo(exp_hdl->fsal); return fsal_umask(info); } /** * @brief Implements GLUSTER FSAL exportoperation fs_xattr_access_rights */ static uint32_t fs_xattr_access_rights(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = gluster_staticinfo(exp_hdl->fsal); return fsal_xattr_access_rights(info); } /** * @brief Implements GLUSTER FSAL exportoperation check_quota */ /* static fsal_status_t check_quota(struct fsal_export *exp_hdl, const char * filepath, int quota_type, struct req_op_context *req_ctx) { return fsalstat(ERR_FSAL_NO_ERROR, 0) ; } */ /** * @brief Implements GLUSTER FSAL exportoperation get_quota */ /* static fsal_status_t get_quota(struct fsal_export *exp_hdl, const char * filepath, int quota_type, struct req_op_context *req_ctx, fsal_quota_t *pquota) { return fsalstat(ERR_FSAL_NOTSUPP, 0) ; } */ /** * @brief Implements GLUSTER FSAL exportoperation set_quota */ /* static fsal_status_t set_quota(struct fsal_export *exp_hdl, const char *filepath, int quota_type, struct req_op_context *req_ctx, fsal_quota_t * pquota, fsal_quota_t * presquota) { return fsalstat(ERR_FSAL_NOTSUPP, 0) ; } */ /** * @brief Registers GLUSTER FSAL exportoperation vector * * This function overrides operations that we've implemented, leaving * the rest for the default. * * @param[in,out] ops Operations vector */ void export_ops_init(struct export_ops *ops) { ops->release = export_release; ops->lookup_path = lookup_path; ops->wire_to_host = wire_to_host; ops->create_handle = create_handle; ops->get_fs_dynamic_info = get_dynamic_info; ops->fs_supports = fs_supports; ops->fs_maxfilesize = fs_maxfilesize; ops->fs_maxread = fs_maxread; ops->fs_maxwrite = fs_maxwrite; ops->fs_maxlink = fs_maxlink; ops->fs_maxnamelen = fs_maxnamelen; ops->fs_maxpathlen = fs_maxpathlen; ops->fs_lease_time = fs_lease_time; ops->fs_acl_support = fs_acl_support; ops->fs_supported_attrs = fs_supported_attrs; ops->fs_umask = fs_umask; ops->fs_xattr_access_rights = fs_xattr_access_rights; ops->alloc_state = glusterfs_alloc_state; ops->free_state = glusterfs_free_state; } enum transport { GLUSTER_TCP_VOL, GLUSTER_RDMA_VOL }; struct glexport_params { char *glvolname; char *glhostname; char *glvolpath; char *glfs_log; uint64_t up_poll_usec; bool enable_upcall; enum transport gltransport; }; static struct config_item_list transportformats[] = { CONFIG_LIST_TOK("tcp", GLUSTER_TCP_VOL), CONFIG_LIST_TOK("rdma", GLUSTER_RDMA_VOL), CONFIG_LIST_EOL }; static struct config_item export_params[] = { CONF_ITEM_NOOP("name"), CONF_MAND_STR("volume", 1, MAXPATHLEN, NULL, glexport_params, glvolname), CONF_MAND_STR("hostname", 1, MAXPATHLEN, NULL, glexport_params, glhostname), CONF_ITEM_PATH("volpath", 1, MAXPATHLEN, "/", glexport_params, glvolpath), CONF_ITEM_PATH("glfs_log", 1, MAXPATHLEN, GFAPI_LOG_LOCATION, glexport_params, glfs_log), CONF_ITEM_UI64("up_poll_usec", 1, 60*1000*1000, 10, glexport_params, up_poll_usec), CONF_ITEM_BOOL("enable_upcall", true, glexport_params, enable_upcall), CONF_ITEM_TOKEN("transport", GLUSTER_TCP_VOL, transportformats, glexport_params, gltransport), CONFIG_EOL }; static struct config_block export_param = { .dbus_interface_name = "org.ganesha.nfsd.config.fsal.gluster-export%d", .blk_desc.name = "FSAL", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = noop_conf_init, .blk_desc.u.blk.params = export_params, .blk_desc.u.blk.commit = noop_conf_commit }; /* * Given glusterfs_fs object, decrement the refcount. In case if it * becomes zero, free the resources. */ void glusterfs_free_fs(struct glusterfs_fs *gl_fs) { int64_t refcnt; int err = 0; PTHREAD_MUTEX_lock(&GlusterFS.lock); refcnt = --(gl_fs->refcnt); assert(refcnt >= 0); if (refcnt) { LogDebug(COMPONENT_FSAL, "There are still (%ld)active shares for volume(%s)", gl_fs->refcnt, gl_fs->volname); PTHREAD_MUTEX_unlock(&GlusterFS.lock); return; } glist_del(&gl_fs->fs_obj); PTHREAD_MUTEX_unlock(&GlusterFS.lock); atomic_inc_int8_t(&gl_fs->destroy_mode); if (!gl_fs->enable_upcall) goto skip_upcall; /* Cancel upcall readiness if not yet done */ up_ready_cancel((struct fsal_up_vector *)gl_fs->up_ops); #ifndef USE_GLUSTER_UPCALL_REGISTER int *retval = NULL; /* Wait for up_thread to exit */ err = pthread_join(gl_fs->up_thread, (void **)&retval); if (retval && *retval) { LogDebug(COMPONENT_FSAL, "Up_thread join returned value %d", *retval); } if (err) { LogWarn(COMPONENT_FSAL, "Up_thread join failed (%s)", strerror(err)); } #else err = glfs_upcall_unregister(gl_fs->fs, GLFS_EVENT_ANY); if ((err < 0) || (!(err & GLFS_EVENT_INODE_INVALIDATE))) { /* Or can we ignore the error like in case of single * node ganesha server. */ LogWarn(COMPONENT_FSAL, "Unable to unregister for upcalls. Volume: %s", gl_fs->volname); } #endif skip_upcall: /* Gluster and memory cleanup */ glfs_fini(gl_fs->fs); gsh_free(gl_fs->volname); gsh_free(gl_fs); } /** * @brief Given Gluster export params, find and return if there is * already existing export entry. If not create one. */ struct glusterfs_fs* glusterfs_get_fs(struct glexport_params params, const struct fsal_up_vector *up_ops) { int rc = 0; struct glusterfs_fs *gl_fs = NULL; glfs_t *fs = NULL; struct glist_head *glist, *glistn; PTHREAD_MUTEX_lock(&GlusterFS.lock); glist_for_each_safe(glist, glistn, &GlusterFS.fs_obj) { gl_fs = glist_entry(glist, struct glusterfs_fs, fs_obj); if (!strcmp(params.glvolname, gl_fs->volname)) { goto found; } } gl_fs = gsh_calloc(1, sizeof(struct glusterfs_fs)); glist_init(&gl_fs->fs_obj); fs = glfs_new(params.glvolname); if (!fs) { LogCrit(COMPONENT_FSAL, "Unable to create new glfs. Volume: %s", params.glvolname); goto out; } switch (params.gltransport) { case GLUSTER_RDMA_VOL: rc = glfs_set_volfile_server(fs, "rdma", params.glhostname, 24007); break; default: rc = glfs_set_volfile_server(fs, "tcp", params.glhostname, 24007); break; } if (rc != 0) { LogCrit(COMPONENT_FSAL, "Unable to set volume file. Volume: %s", params.glvolname); goto out; } rc = glfs_set_logging(fs, params.glfs_log, 7); if (rc != 0) { LogCrit(COMPONENT_FSAL, "Unable to set logging. Volume: %s", params.glvolname); goto out; } rc = glfs_init(fs); if (rc != 0) { LogCrit(COMPONENT_FSAL, "Unable to initialize volume. Volume: %s", params.glvolname); goto out; } gl_fs->fs = fs; gl_fs->volname = strdup(params.glvolname); gl_fs->destroy_mode = 0; gl_fs->up_poll_usec = params.up_poll_usec; gl_fs->up_ops = up_ops; gl_fs->enable_upcall = params.enable_upcall; if (!gl_fs->enable_upcall) goto skip_upcall; #ifndef USE_GLUSTER_UPCALL_REGISTER rc = initiate_up_thread(gl_fs); if (rc != 0) { LogCrit(COMPONENT_FSAL, "Unable to create GLUSTERFSAL_UP_Thread. Volume: %s", params.glvolname); goto out; } #else /* We are mainly interested in INODE_INVALIDATE for now. Still * register for all the events */ rc = glfs_upcall_register(fs, GLFS_EVENT_ANY, gluster_process_upcall, gl_fs); if ((rc < 0) || (!(rc & GLFS_EVENT_INODE_INVALIDATE))) { /* Or can we ignore the error like in case of single * node ganesha server. */ LogCrit(COMPONENT_FSAL, "Unable to register for upcalls. Volume: %s", params.glvolname); goto out; } #endif skip_upcall: glist_add(&GlusterFS.fs_obj, &gl_fs->fs_obj); found: ++(gl_fs->refcnt); PTHREAD_MUTEX_unlock(&GlusterFS.lock); return gl_fs; out: PTHREAD_MUTEX_unlock(&GlusterFS.lock); if (fs) glfs_fini(fs); if (gl_fs) { glist_del(&gl_fs->fs_obj); /* not needed atm */ gsh_free(gl_fs); } return NULL; } /** * @brief Implements GLUSTER FSAL moduleoperation create_export */ fsal_status_t glusterfs_create_export(struct fsal_module *fsal_hdl, void *parse_node, struct config_error_type *err_type, const struct fsal_up_vector *up_ops) { int rc = 0; fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; struct glusterfs_export *glfsexport = NULL; bool fsal_attached = false; struct glexport_params params = { .glvolname = NULL, .glhostname = NULL, .glvolpath = NULL, .glfs_log = NULL}; LogDebug(COMPONENT_FSAL, "In args: export path = %s", op_ctx->ctx_export->fullpath); glfsexport = gsh_calloc(1, sizeof(struct glusterfs_export)); rc = load_config_from_node(parse_node, &export_param, ¶ms, true, err_type); if (rc != 0) { LogCrit(COMPONENT_FSAL, "Incorrect or missing parameters for export %s", op_ctx->ctx_export->fullpath); status.major = ERR_FSAL_INVAL; goto out; } LogEvent(COMPONENT_FSAL, "Volume %s exported at : '%s'", params.glvolname, params.glvolpath); fsal_export_init(&glfsexport->export); export_ops_init(&glfsexport->export.exp_ops); glfsexport->gl_fs = glusterfs_get_fs(params, up_ops); if (!glfsexport->gl_fs) { status.major = ERR_FSAL_SERVERFAULT; goto out; } rc = fsal_attach_export(fsal_hdl, &glfsexport->export.exports); if (rc != 0) { status.major = ERR_FSAL_SERVERFAULT; LogCrit(COMPONENT_FSAL, "Unable to attach export. Export: %s", op_ctx->ctx_export->fullpath); goto out; } fsal_attached = true; glfsexport->mount_path = op_ctx->ctx_export->fullpath; glfsexport->export_path = params.glvolpath; glfsexport->saveduid = geteuid(); glfsexport->savedgid = getegid(); glfsexport->export.fsal = fsal_hdl; op_ctx->fsal_export = &glfsexport->export; glfsexport->pnfs_ds_enabled = glfsexport->export.exp_ops.fs_supports(&glfsexport->export, fso_pnfs_ds_supported); if (glfsexport->pnfs_ds_enabled) { struct fsal_pnfs_ds *pds = NULL; status = fsal_hdl->m_ops.fsal_pnfs_ds(fsal_hdl, parse_node, &pds); if (status.major != ERR_FSAL_NO_ERROR) goto out; /* special case: server_id matches export_id */ pds->id_servers = op_ctx->ctx_export->export_id; pds->mds_export = op_ctx->ctx_export; pds->mds_fsal_export = op_ctx->fsal_export; if (!pnfs_ds_insert(pds)) { LogCrit(COMPONENT_CONFIG, "Server id %d already in use.", pds->id_servers); status.major = ERR_FSAL_EXIST; goto out; } LogDebug(COMPONENT_PNFS, "glusterfs_fsal_create: pnfs ds was enabled for [%s]", op_ctx->ctx_export->fullpath); } glfsexport->pnfs_mds_enabled = glfsexport->export.exp_ops.fs_supports(&glfsexport->export, fso_pnfs_mds_supported); if (glfsexport->pnfs_mds_enabled) { LogDebug(COMPONENT_PNFS, "glusterfs_fsal_create: pnfs mds was enabled for [%s]", op_ctx->ctx_export->fullpath); export_ops_pnfs(&glfsexport->export.exp_ops); fsal_ops_pnfs(&glfsexport->export.fsal->m_ops); } glfsexport->export.up_ops = up_ops; out: gsh_free(params.glvolname); gsh_free(params.glhostname); gsh_free(params.glfs_log); if (status.major != ERR_FSAL_NO_ERROR) { gsh_free(params.glvolpath); if (fsal_attached) fsal_detach_export(fsal_hdl, &glfsexport->export.exports); if (glfsexport->gl_fs) glusterfs_free_fs(glfsexport->gl_fs); gsh_free(glfsexport); } return status; } nfs-ganesha-2.6.0/src/FSAL/FSAL_GLUSTER/fsal_up.c000066400000000000000000000172251324272410200207600ustar00rootroot00000000000000/* * Copyright (C) Red Hat Inc., 2015 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. * * --------------------------------------- */ /** * @file fsal_up.c * * @author Soumya Koduri * * @brief Upcall Interface for FSAL_GLUSTER * */ #include "config.h" #include "fsal.h" #include "fsal_up.h" #include "gluster_internal.h" #include "fsal_convert.h" #include #include #include #include int upcall_inode_invalidate(struct glusterfs_fs *gl_fs, struct glfs_object *object) { int rc = -1; glfs_t *fs = NULL; char vol_uuid[GLAPI_UUID_LENGTH] = {'\0'}; unsigned char globjhdl[GLAPI_HANDLE_LENGTH]; struct gsh_buffdesc key; const struct fsal_up_vector *event_func; fsal_status_t fsal_status = {0, 0}; fs = gl_fs->fs; if (!fs) { LogCrit(COMPONENT_FSAL_UP, "Invalid fs object of the glusterfs_fs(%p)", gl_fs); goto out; } rc = glfs_h_extract_handle(object, globjhdl+GLAPI_UUID_LENGTH, GFAPI_HANDLE_LENGTH); if (rc < 0) { LogDebug(COMPONENT_FSAL_UP, "glfs_h_extract_handle failed %p", fs); goto out; } rc = glfs_get_volumeid(fs, vol_uuid, GLAPI_UUID_LENGTH); if (rc < 0) { LogDebug(COMPONENT_FSAL_UP, "glfs_get_volumeid failed %p", fs); goto out; } memcpy(globjhdl, vol_uuid, GLAPI_UUID_LENGTH); key.addr = &globjhdl; key.len = GLAPI_HANDLE_LENGTH; LogDebug(COMPONENT_FSAL_UP, "Received event to process for %p", fs); event_func = gl_fs->up_ops; fsal_status = event_func->invalidate_close( event_func, &key, FSAL_UP_INVALIDATE_CACHE); rc = fsal_status.major; if (FSAL_IS_ERROR(fsal_status) && fsal_status.major != ERR_FSAL_NOENT) { LogWarn(COMPONENT_FSAL_UP, "Inode_Invalidate event could not be processed for fd %p, rc %d", gl_fs->fs, rc); } out: return rc; } void *GLUSTERFSAL_UP_Thread(void *Arg) { struct glusterfs_fs *gl_fs = Arg; struct fsal_up_vector *event_func; char thr_name[16]; int rc = 0; struct glfs_upcall *cbk = NULL; struct glfs_upcall_inode *in_arg = NULL; enum glfs_upcall_reason reason = 0; int retry = 0; int errsv = 0; struct glfs_object *object = NULL; struct glfs_object *p_object = NULL; struct glfs_object *oldp_object = NULL; snprintf(thr_name, sizeof(thr_name), "fsal_up_%p", gl_fs->fs); SetNameFunction(thr_name); /* Set the FSAL UP functions that will be used to process events. */ event_func = (struct fsal_up_vector *)gl_fs->up_ops; if (event_func == NULL) { LogFatal(COMPONENT_FSAL_UP, "FSAL up vector does not exist. Can not continue."); gsh_free(Arg); return NULL; } LogFullDebug(COMPONENT_FSAL_UP, "Initializing FSAL Callback context for %p.", gl_fs->fs); if (!gl_fs->fs) { LogCrit(COMPONENT_FSAL_UP, "FSAL Callback interface - Null glfs context."); goto out; } /* wait for upcall readiness */ up_ready_wait(event_func); /* Start querying for events and processing. */ /** @todo : Do batch processing instead */ while (!atomic_fetch_int8_t(&gl_fs->destroy_mode)) { LogFullDebug(COMPONENT_FSAL_UP, "Requesting event from FSAL Callback interface for %p.", gl_fs->fs); reason = 0; rc = glfs_h_poll_upcall(gl_fs->fs, &cbk); errsv = errno; if (rc != 0) { /* if ENOMEM retry for couple of times * and then exit */ if ((errsv == ENOMEM) && (retry < 10)) { sleep(1); retry++; continue; } else { switch (errsv) { case ENOMEM: LogMajor(COMPONENT_FSAL_UP, "Memory allocation failed during poll_upcall for (%p).", gl_fs->fs); abort(); case ENOTSUP: LogEvent(COMPONENT_FSAL_UP, "Upcall feature is not supported for (%p).", gl_fs->fs); break; default: LogCrit(COMPONENT_FSAL_UP, "Poll upcall failed for %p. rc %d errno %d (%s) reason %d", gl_fs->fs, rc, errsv, strerror(errsv), reason); } return NULL; } } retry = 0; LogFullDebug(COMPONENT_FSAL_UP, "Received upcall event: reason(%d)", reason); if (!cbk) { usleep(gl_fs->up_poll_usec); continue; } reason = glfs_upcall_get_reason(cbk); /* Decide what type of event this is * inode update / invalidate? */ switch (reason) { case GLFS_UPCALL_EVENT_NULL: usleep(gl_fs->up_poll_usec); continue; case GLFS_UPCALL_INODE_INVALIDATE: in_arg = glfs_upcall_get_event(cbk); if (!in_arg) { /* Could be ENOMEM issues. continue */ LogWarn(COMPONENT_FSAL_UP, "Received NULL upcall event arg"); break; } object = glfs_upcall_inode_get_object(in_arg); if (object) upcall_inode_invalidate(gl_fs, object); p_object = glfs_upcall_inode_get_pobject(in_arg); if (p_object) upcall_inode_invalidate(gl_fs, p_object); oldp_object = glfs_upcall_inode_get_oldpobject(in_arg); if (oldp_object) upcall_inode_invalidate(gl_fs, oldp_object); break; default: LogWarn(COMPONENT_FSAL_UP, "Unknown event: %d", reason); continue; } if (cbk) { glfs_free(cbk); cbk = NULL; } } out: return NULL; } /* GLUSTERFSFSAL_UP_Thread */ void gluster_process_upcall(struct glfs_upcall *cbk, void *data) { struct glusterfs_fs *gl_fs = data; struct fsal_up_vector *event_func; struct glfs_upcall_inode *in_arg = NULL; enum glfs_upcall_reason reason = 0; struct glfs_object *object = NULL; struct glfs_object *p_object = NULL; struct glfs_object *oldp_object = NULL; if (!cbk) { LogFatal(COMPONENT_FSAL_UP, "Upcall received with no data"); return; } /* Set the FSAL UP functions that will be used to process events. */ event_func = (struct fsal_up_vector *)gl_fs->up_ops; if (!event_func) { LogFatal(COMPONENT_FSAL_UP, "FSAL up vector does not exist. Can not continue."); goto out; } if (!gl_fs->fs) { LogCrit(COMPONENT_FSAL_UP, "FSAL Callback interface - Null glfs context."); goto out; } /* wait for upcall readiness */ up_ready_wait(event_func); reason = glfs_upcall_get_reason(cbk); /* Decide what type of event this is * inode update / invalidate? */ switch (reason) { case GLFS_UPCALL_INODE_INVALIDATE: in_arg = glfs_upcall_get_event(cbk); if (!in_arg) { /* Could be ENOMEM issues. continue */ LogWarn(COMPONENT_FSAL_UP, "Received NULL upcall event arg"); break; } object = glfs_upcall_inode_get_object(in_arg); if (object) upcall_inode_invalidate(gl_fs, object); p_object = glfs_upcall_inode_get_pobject(in_arg); if (p_object) upcall_inode_invalidate(gl_fs, p_object); oldp_object = glfs_upcall_inode_get_oldpobject(in_arg); if (oldp_object) upcall_inode_invalidate(gl_fs, oldp_object); break; default: LogWarn(COMPONENT_FSAL_UP, "Unknown event: %d", reason); } out: glfs_free(cbk); } nfs-ganesha-2.6.0/src/FSAL/FSAL_GLUSTER/gluster_internal.c000066400000000000000000000324241324272410200227060ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Red Hat Inc., 2011 * Author: Anand Subramanian anands@redhat.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ /* main.c * Module core functions */ #include #include /* ENOATTR */ #include "gluster_internal.h" #include "fsal_api.h" #include "fsal_convert.h" #include "nfs4_acls.h" #include "FSAL/fsal_commonlib.h" #include "posix_acls.h" /** * @brief FSAL status mapping from GlusterFS errors * * This function returns a fsal_status_t with the FSAL error as the * major, and the posix error as minor. Please note that this routine * needs to be used only in case of failures. * * @param[in] gluster_errorcode Gluster error * * @return FSAL status. */ fsal_status_t gluster2fsal_error(const int err) { fsal_status_t status; int g_err = err; if (!g_err) { LogWarn(COMPONENT_FSAL, "appropriate errno not set"); g_err = EINVAL; } status.minor = g_err; status.major = posix2fsal_error(g_err); return status; } /** * @brief Convert a struct stat from Gluster to a struct attrlist * * This function writes the content of the supplied struct stat to the * struct fsalsattr. * * @param[in] buffstat Stat structure * @param[out] fsalattr FSAL attributes */ void stat2fsal_attributes(const struct stat *buffstat, struct attrlist *fsalattr) { /* Indicate which atrributes we have set without affecting the * other bits in the mask. */ fsalattr->valid_mask |= ATTRS_POSIX; fsalattr->supported = op_ctx->fsal_export->exp_ops.fs_supported_attrs( op_ctx->fsal_export); /* Fills the output struct */ fsalattr->type = posix2fsal_type(buffstat->st_mode); fsalattr->filesize = buffstat->st_size; fsalattr->fsid = posix2fsal_fsid(buffstat->st_dev); fsalattr->fileid = buffstat->st_ino; fsalattr->mode = unix2fsal_mode(buffstat->st_mode); fsalattr->numlinks = buffstat->st_nlink; fsalattr->owner = buffstat->st_uid; fsalattr->group = buffstat->st_gid; /** @todo: gfapi currently only fills in the legacy time_t fields * when it supports the timespec fields calls to this * function should be replaced with calls to * posix2fsal_attributes rather than changing this code. */ fsalattr->atime = posix2fsal_time(buffstat->st_atime, 0); fsalattr->ctime = posix2fsal_time(buffstat->st_ctime, 0); fsalattr->mtime = posix2fsal_time(buffstat->st_mtime, 0); fsalattr->chgtime = posix2fsal_time(MAX(buffstat->st_mtime, buffstat->st_ctime), 0); fsalattr->change = fsalattr->chgtime.tv_sec; fsalattr->spaceused = buffstat->st_blocks * S_BLKSIZE; fsalattr->rawdev = posix2fsal_devt(buffstat->st_rdev); } struct fsal_staticfsinfo_t *gluster_staticinfo(struct fsal_module *hdl) { struct glusterfs_fsal_module *glfsal_module; glfsal_module = container_of(hdl, struct glusterfs_fsal_module, fsal); return &glfsal_module->fs_info; } /** * @brief Construct a new filehandle * * This function constructs a new Gluster FSAL object handle and attaches * it to the export. After this call the attributes have been filled * in and the handdle is up-to-date and usable. * * @param[in] st Stat data for the file * @param[in] export Export on which the object lives * @param[out] obj Object created * * @return 0 on success, negative error codes on failure. */ void construct_handle(struct glusterfs_export *glexport, const struct stat *st, struct glfs_object *glhandle, unsigned char *globjhdl, int len, struct glusterfs_handle **obj, const char *vol_uuid) { struct glusterfs_handle *constructing = NULL; glusterfs_fsal_xstat_t buffxstat; memset(&buffxstat, 0, sizeof(glusterfs_fsal_xstat_t)); constructing = gsh_calloc(1, sizeof(struct glusterfs_handle)); constructing->glhandle = glhandle; memcpy(constructing->globjhdl, vol_uuid, GLAPI_UUID_LENGTH); memcpy(constructing->globjhdl+GLAPI_UUID_LENGTH, globjhdl, GFAPI_HANDLE_LENGTH); constructing->globalfd.glfd = NULL; fsal_obj_handle_init(&constructing->handle, &glexport->export, posix2fsal_type(st->st_mode)); constructing->handle.fsid = posix2fsal_fsid(st->st_dev); constructing->handle.fileid = st->st_ino; handle_ops_init(&constructing->handle.obj_ops); *obj = constructing; } void gluster_cleanup_vars(struct glfs_object *glhandle) { if (glhandle) { /* Error ignored, this is a cleanup operation, can't do much. */ /** @todo: Useful point for logging? */ glfs_h_close(glhandle); } } /* fs_specific_has() parses the fs_specific string for a particular key, * returns true if found, and optionally returns a val if the string is * of the form key=val. * * The fs_specific string is a comma (,) separated options where each option * can be of the form key=value or just key. Example: * FS_specific = "foo=baz,enable_A"; */ bool fs_specific_has(const char *fs_specific, const char *key, char *val, int *max_val_bytes) { char *next_comma, *option; bool ret; char *fso_dup = NULL; if (!fs_specific || !fs_specific[0]) return false; fso_dup = gsh_strdup(fs_specific); for (option = strtok_r(fso_dup, ",", &next_comma); option; option = strtok_r(NULL, ",", &next_comma)) { char *k = option; char *v = k; strsep(&v, "="); if (strcmp(k, key) == 0) { if (val) strncpy(val, v, *max_val_bytes); if (max_val_bytes) *max_val_bytes = strlen(v) + 1; ret = true; goto cleanup; } } ret = false; cleanup: gsh_free(fso_dup); return ret; } void setglustercreds(struct glusterfs_export *glfs_export, uid_t *uid, gid_t *gid, unsigned int ngrps, gid_t *groups, char *file, int line, char *function) { int rc = 0; if (uid) { if (*uid != glfs_export->saveduid) rc = glfs_setfsuid(*uid); } else { rc = glfs_setfsuid(glfs_export->saveduid); } if (rc) goto out; if (gid) { if (*gid != glfs_export->savedgid) rc = glfs_setfsgid(*gid); } else { rc = glfs_setfsgid(glfs_export->savedgid); } if (rc) goto out; if (ngrps != 0 && groups) rc = glfs_setfsgroups(ngrps, groups); else rc = glfs_setfsgroups(0, NULL); out: if (rc != 0) { DisplayLogComponentLevel(COMPONENT_FSAL, file, line, function, NIV_FATAL, "Could not set Gluster credentials - uid(%d), gid(%d)", *uid, *gid); } } /* * Read the ACL in GlusterFS format and convert it into fsal ACL before * storing it in fsalattr */ fsal_status_t glusterfs_get_acl(struct glusterfs_export *glfs_export, struct glfs_object *glhandle, glusterfs_fsal_xstat_t *buffxstat, struct attrlist *fsalattr) { fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; fsal_acl_data_t acldata; fsal_acl_status_t aclstatus; fsal_ace_t *pace = NULL; int e_count = 0, i_count = 0, new_count = 0, new_i_count = 0; if (fsalattr->acl != NULL) { /* We should never be passed attributes that have an * ACL attached, but just in case some future code * path changes that assumption, let's release the * old ACL properly. */ int acl_status; acl_status = nfs4_acl_release_entry(fsalattr->acl); if (acl_status != NFS_V4_ACL_SUCCESS) LogCrit(COMPONENT_FSAL, "Failed to release old acl, status=%d", acl_status); fsalattr->acl = NULL; } if (NFSv4_ACL_SUPPORT) { buffxstat->e_acl = glfs_h_acl_get(glfs_export->gl_fs->fs, glhandle, ACL_TYPE_ACCESS); if (!buffxstat->e_acl) { status = gluster2fsal_error(errno); return status; } e_count = ace_count(buffxstat->e_acl); if (buffxstat->is_dir) { buffxstat->i_acl = glfs_h_acl_get(glfs_export->gl_fs->fs, glhandle, ACL_TYPE_DEFAULT); i_count = ace_count(buffxstat->i_acl); } /* Allocating memory for both ALLOW and DENY entries */ acldata.naces = 2 * (e_count + i_count); LogDebug(COMPONENT_FSAL, "No of aces present in fsal_acl_t = %d" , acldata.naces); if (!acldata.naces) return status; FSAL_SET_MASK(buffxstat->attr_valid, XATTR_ACL); acldata.aces = (fsal_ace_t *) nfs4_ace_alloc(acldata.naces); pace = acldata.aces; new_count = posix_acl_2_fsal_acl(buffxstat->e_acl, buffxstat->is_dir, false, &pace); if (new_count < 0) return fsalstat(ERR_FSAL_NO_ACE, -1); if (i_count > 0) { new_i_count = posix_acl_2_fsal_acl(buffxstat->i_acl, true, true, &pace); if (new_i_count > 0) new_count += new_i_count; else LogDebug(COMPONENT_FSAL, "Inherit acl is not set for this directory"); } /* Reallocating acldata into the required size */ acldata.aces = (fsal_ace_t *) gsh_realloc(acldata.aces, new_count*sizeof(fsal_ace_t)); acldata.naces = new_count; fsalattr->acl = nfs4_acl_new_entry(&acldata, &aclstatus); LogDebug(COMPONENT_FSAL, "fsal acl = %p, fsal_acl_status = %u", fsalattr->acl, aclstatus); if (fsalattr->acl == NULL) { LogCrit(COMPONENT_FSAL, "failed to create a new acl entry"); return fsalstat(ERR_FSAL_NOMEM, -1); } fsalattr->valid_mask |= ATTR_ACL; } else { /* We were asked for ACL but do not support. */ status = fsalstat(ERR_FSAL_NOTSUPP, 0); } return status; } /* * Store the Glusterfs ACL using setxattr call. */ fsal_status_t glusterfs_set_acl(struct glusterfs_export *glfs_export, struct glusterfs_handle *objhandle, glusterfs_fsal_xstat_t *buffxstat) { int rc = 0; rc = glfs_h_acl_set(glfs_export->gl_fs->fs, objhandle->glhandle, ACL_TYPE_ACCESS, buffxstat->e_acl); if (rc < 0) { /** @todo: check if error is appropriate.*/ LogMajor(COMPONENT_FSAL, "failed to set access type posix acl"); return fsalstat(ERR_FSAL_INVAL, 0); } /* For directories consider inherited acl too */ if (buffxstat->is_dir && buffxstat->i_acl) { rc = glfs_h_acl_set(glfs_export->gl_fs->fs, objhandle->glhandle, ACL_TYPE_DEFAULT, buffxstat->i_acl); if (rc < 0) { LogMajor(COMPONENT_FSAL, "failed to set default type posix acl"); return fsalstat(ERR_FSAL_INVAL, 0); } } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* * Process NFSv4 ACLs passed in setattr call */ fsal_status_t glusterfs_process_acl(struct glfs *fs, struct glfs_object *object, struct attrlist *attrs, glusterfs_fsal_xstat_t *buffxstat) { if (attrs->acl) { LogDebug(COMPONENT_FSAL, "setattr acl = %p", attrs->acl); /* Convert FSAL ACL to POSIX ACL */ buffxstat->e_acl = fsal_acl_2_posix_acl(attrs->acl, ACL_TYPE_ACCESS); if (!buffxstat->e_acl) { LogMajor(COMPONENT_FSAL, "failed to set access type posix acl"); return fsalstat(ERR_FSAL_FAULT, 0); } /* For directories consider inherited acl too */ if (buffxstat->is_dir) { buffxstat->i_acl = fsal_acl_2_posix_acl(attrs->acl, ACL_TYPE_DEFAULT); if (!buffxstat->i_acl) LogDebug(COMPONENT_FSAL, "inherited acl is not defined for directory"); } } else { LogCrit(COMPONENT_FSAL, "setattr acl is NULL"); return fsalstat(ERR_FSAL_FAULT, 0); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } int initiate_up_thread(struct glusterfs_fs *gl_fs) { pthread_attr_t up_thr_attr; int retval = -1; int err = 0; int retries = 10; memset(&up_thr_attr, 0, sizeof(up_thr_attr)); /* Initialization of thread attributes from nfs_init.c */ err = pthread_attr_init(&up_thr_attr); if (err) { LogCrit(COMPONENT_THREAD, "can't init pthread's attributes (%s)", strerror(err)); goto out; } err = pthread_attr_setscope(&up_thr_attr, PTHREAD_SCOPE_SYSTEM); if (err) { LogCrit(COMPONENT_THREAD, "can't set pthread's scope (%s)", strerror(err)); goto out; } err = pthread_attr_setdetachstate(&up_thr_attr, PTHREAD_CREATE_JOINABLE); if (err) { LogCrit(COMPONENT_THREAD, "can't set pthread's join state (%s)", strerror(err)); goto out; } err = pthread_attr_setstacksize(&up_thr_attr, 2116488); if (err) { LogCrit(COMPONENT_THREAD, "can't set pthread's stack size (%s)", strerror(err)); goto out; } do { err = pthread_create(&gl_fs->up_thread, &up_thr_attr, GLUSTERFSAL_UP_Thread, gl_fs); sleep(1); } while (err && (err == EAGAIN) && (retries-- > 0)); if (err) { LogCrit(COMPONENT_THREAD, "can't create upcall pthread (%s)", strerror(err)); goto out; } retval = 0; out: err = pthread_attr_destroy(&up_thr_attr); if (err) { LogCrit(COMPONENT_THREAD, "can't destroy pthread's attributes (%s)", strerror(err)); } return retval; } #ifdef GLTIMING void latency_update(struct timespec *s_time, struct timespec *e_time, int opnum) { atomic_add_uint64_t(&glfsal_latencies[opnum].overall_time, timespec_diff(s_time, e_time)); atomic_add_uint64_t(&glfsal_latencies[opnum].count, 1); } void latency_dump(void) { int i = 0; for (; i < LATENCY_SLOTS; i++) { LogCrit(COMPONENT_FSAL, "Op:%d:Count:%"PRIu64":nsecs:%"PRIu64, i, glfsal_latencies[i].count, glfsal_latencies[i].overall_time); } } #endif nfs-ganesha-2.6.0/src/FSAL/FSAL_GLUSTER/gluster_internal.h000066400000000000000000000216201324272410200227070ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Red Hat Inc., 2011 * Author: Anand Subramanian anands@redhat.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ #ifndef GLUSTER_INTERNAL #define GLUSTER_INTERNAL #include "fsal.h" #include "fsal_types.h" #include "fsal_api.h" #include "posix_acls.h" #include "FSAL/fsal_commonlib.h" #include #include #include "nfs_exports.h" #define GLUSTER_VOLNAME_KEY "volume" #define GLUSTER_HOSTNAME_KEY "hostname" #define GLUSTER_VOLPATH_KEY "volpath" /* defined the set of attributes supported with POSIX */ #define GLUSTERFS_SUPPORTED_ATTRIBUTES (ATTRS_POSIX | ATTR_ACL) /** * The attributes this FSAL can set. */ #define GLUSTERFS_SETTABLE_ATTRIBUTES ( \ ATTR_MODE | ATTR_OWNER | ATTR_GROUP | \ ATTR_ATIME | ATTR_CTIME | ATTR_MTIME | \ ATTR_SIZE | ATTR_MTIME_SERVER | ATTR_ATIME_SERVER | \ ATTR_ACL) /** * Override internal Gluster defines for the time being. */ /* Values for valid falgs to be used when using XXXsetattr, to set multiple a ttribute values passed via t*he related stat structure. */ #define GLAPI_SET_ATTR_MODE GFAPI_SET_ATTR_MODE #define GLAPI_SET_ATTR_UID GFAPI_SET_ATTR_UID #define GLAPI_SET_ATTR_GID GFAPI_SET_ATTR_GID #define GLAPI_SET_ATTR_SIZE GFAPI_SET_ATTR_SIZE #define GLAPI_SET_ATTR_ATIME GFAPI_SET_ATTR_ATIME #define GLAPI_SET_ATTR_MTIME GFAPI_SET_ATTR_MTIME /* UUID length for the object returned from glfs_get_volume_id */ #define GLAPI_UUID_LENGTH 16 /* * GFAPI_HANDLE_LENGTH is the handle length for object handles * returned from glfs_h_extract_handle or glfs_h_create_from_handle * * GLAPI_HANDLE_LENGTH is the total length of the handle descriptor * to be used in the wire handle. */ #define GLAPI_HANDLE_LENGTH ( \ GFAPI_HANDLE_LENGTH + \ GLAPI_UUID_LENGTH) /* Flags to determine if ACLs are supported */ #define NFSv4_ACL_SUPPORT (!op_ctx_export_has_option(EXPORT_OPTION_DISABLE_ACL)) /* define flags for attr_valid */ #define XATTR_STAT (1 << 0) /* 01 */ #define XATTR_ACL (1 << 1) /* 02 */ /* END Override */ #ifdef GLTIMING typedef enum { lat_handle_release = 0, lat_lookup, lat_create, lat_getattrs, lat_handle_to_wire, lat_handle_to_key, lat_extract_handle, lat_create_handle, lat_read_dirents, lat_makedir, lat_makenode, lat_setattrs, lat_file_unlink, lat_file_open, lat_file_read, lat_file_write, lat_commit, lat_file_close, lat_makesymlink, lat_readsymlink, lat_linkfile, lat_renamefile, lat_lock_op, lat_end_slots } latency_slots_t; #define LATENCY_SLOTS 24 struct latency_data { uint64_t count; nsecs_elapsed_t overall_time; }; #endif struct glusterfs_fsal_module { struct fsal_staticfsinfo_t fs_info; struct fsal_module fsal; struct glist_head fs_obj; /* list of glusterfs_fs filesystem objects */ pthread_mutex_t lock; /* lock to protect above list */ }; struct glusterfs_fsal_module GlusterFS; struct glusterfs_fs { struct glist_head fs_obj; /* link to glusterfs_fs filesystem objects */ char *volname; glfs_t *fs; const struct fsal_up_vector *up_ops; /*< Upcall operations */ int64_t refcnt; pthread_t up_thread; /* upcall thread */ int8_t destroy_mode; uint64_t up_poll_usec; bool enable_upcall; }; struct glusterfs_export { struct glusterfs_fs *gl_fs; char *mount_path; char *export_path; uid_t saveduid; gid_t savedgid; struct fsal_export export; bool pnfs_ds_enabled; bool pnfs_mds_enabled; }; struct glusterfs_fd { /** The open and share mode etc. This MUST be first in every * file descriptor structure. */ fsal_openflags_t openflags; /* rw lock to protect the file descriptor */ pthread_rwlock_t fdlock; /** Gluster file descriptor. */ struct glfs_fd *glfd; struct user_cred creds; /* user creds opening fd*/ }; struct glusterfs_handle { struct glfs_object *glhandle; unsigned char globjhdl[GLAPI_HANDLE_LENGTH]; /* handle descriptor, for wire handle */ struct glusterfs_fd globalfd; struct fsal_obj_handle handle; /* public FSAL handle */ struct fsal_share share; /* share_reservations */ /* following added for pNFS support */ uint64_t rd_issued; uint64_t rd_serial; uint64_t rw_issued; uint64_t rw_serial; uint64_t rw_max_len; }; /* Structures defined for PNFS */ struct glfs_ds_handle { struct fsal_ds_handle ds; struct glfs_object *glhandle; stable_how4 stability_got; bool connected; }; struct glfs_file_layout { uint32_t stripe_length; uint64_t stripe_type; uint32_t devid; }; struct glfs_ds_wire { unsigned char gfid[16]; struct glfs_file_layout layout; /*< Layout information */ }; /* Define the buffer size for GLUSTERFS NFS4 ACL. */ #define GLFS_ACL_BUF_SIZE 0x1000 /* A set of buffers to retrieve multiple attributes at the same time. */ typedef struct fsal_xstat__ { int attr_valid; struct stat buffstat; char buffacl[GLFS_ACL_BUF_SIZE]; acl_t e_acl; /* stores effective acl */ acl_t i_acl; /* stores inherited acl */ bool is_dir; } glusterfs_fsal_xstat_t; struct glusterfs_state_fd { struct state_t state; struct glusterfs_fd glusterfs_fd; }; void setglustercreds(struct glusterfs_export *glfs_export, uid_t *uid, gid_t *gid, unsigned int ngrps, gid_t *groups, char *file, int line, char *function); #define SET_GLUSTER_CREDS(glfs_export, uid, gid, glen, garray) do { \ int old_errno = errno; \ ((void) setglustercreds(glfs_export, uid, gid, glen, \ garray, (char *) __FILE__, \ __LINE__, (char *) __func__)); \ errno = old_errno; \ } while (0) #ifdef GLTIMING struct latency_data glfsal_latencies[LATENCY_SLOTS]; void latency_update(struct timespec *s_time, struct timespec *e_time, int opnum); void latency_dump(void); #endif void handle_ops_init(struct fsal_obj_ops *ops); fsal_status_t gluster2fsal_error(const int gluster_errorcode); void stat2fsal_attributes(const struct stat *buffstat, struct attrlist *fsalattr); struct fsal_staticfsinfo_t *gluster_staticinfo(struct fsal_module *hdl); void construct_handle(struct glusterfs_export *glexport, const struct stat *st, struct glfs_object *glhandle, unsigned char *globjhdl, int len, struct glusterfs_handle **obj, const char *vol_uuid); fsal_status_t glfs2fsal_handle(struct glusterfs_export *glfs_export, struct glfs_object *glhandle, struct fsal_obj_handle **pub_handle, struct stat *sb, struct attrlist *attrs_out); fsal_status_t glusterfs_create_export(struct fsal_module *fsal_hdl, void *parse_node, struct config_error_type *err_type, const struct fsal_up_vector *up_ops); void gluster_cleanup_vars(struct glfs_object *glhandle); bool fs_specific_has(const char *fs_specific, const char *key, char *val, int *max_val_bytes); fsal_status_t glusterfs_get_acl(struct glusterfs_export *glfs_export, struct glfs_object *objhandle, glusterfs_fsal_xstat_t *buffxstat, struct attrlist *fsalattr); fsal_status_t glusterfs_set_acl(struct glusterfs_export *glfs_export, struct glusterfs_handle *objhandle, glusterfs_fsal_xstat_t *buffxstat); fsal_status_t glusterfs_process_acl(struct glfs *fs, struct glfs_object *object, struct attrlist *attrs, glusterfs_fsal_xstat_t *buffxstat); void glusterfs_free_fs(struct glusterfs_fs *gl_fs); /* * Following have been introduced for pNFS support */ /* Need to call this to initialize export_ops for pnfs */ void export_ops_pnfs(struct export_ops *ops); /* Need to call this to initialize obj_ops for pnfs */ void handle_ops_pnfs(struct fsal_obj_ops *ops); /* Need to call this to initialize ops for pnfs */ void fsal_ops_pnfs(struct fsal_ops *ops); void dsh_ops_init(struct fsal_dsh_ops *ops); void pnfs_ds_ops_init(struct fsal_pnfs_ds_ops *ops); nfsstat4 getdeviceinfo(struct fsal_module *fsal_hdl, XDR *da_addr_body, const layouttype4 type, const struct pnfs_deviceid *deviceid); /* UP thread routines */ void *GLUSTERFSAL_UP_Thread(void *Arg); int initiate_up_thread(struct glusterfs_fs *gl_fs); int upcall_inode_invalidate(struct glusterfs_fs *gl_fs, struct glfs_object *object); void gluster_process_upcall(struct glfs_upcall *cbk, void *data); fsal_status_t glusterfs_close_my_fd(struct glusterfs_fd *my_fd); #endif /* GLUSTER_INTERNAL */ nfs-ganesha-2.6.0/src/FSAL/FSAL_GLUSTER/handle.c000066400000000000000000002212101324272410200205510ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Red Hat Inc., 2013 * Author: Anand Subramanian anands@redhat.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ #include "config.h" #ifdef LINUX #include /* for makedev(3) */ #endif #include #include "fsal.h" #include "gluster_internal.h" #include "FSAL/fsal_commonlib.h" #include "fsal_convert.h" #include "pnfs_utils.h" #include "nfs_exports.h" #include "sal_data.h" /* fsal_obj_handle common methods */ /** * @brief Implements GLUSTER FSAL objectoperation handle_release * * Free up the GLUSTER handle and associated data if any * Typically free up any members of the struct glusterfs_handle */ static void handle_release(struct fsal_obj_handle *obj_hdl) { int rc = 0; struct glusterfs_handle *objhandle = container_of(obj_hdl, struct glusterfs_handle, handle); struct glusterfs_fd *my_fd = &objhandle->globalfd; fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; #ifdef GLTIMING struct timespec s_time, e_time; now(&s_time); #endif fsal_obj_handle_fini(&objhandle->handle); if (my_fd->glfd) { /* During shutdown, the op_ctx is NULL */ if (op_ctx && op_ctx->fsal_export) { /* Since handle gets released as part of internal * operation, we may not need to set credentials */ status = glusterfs_close_my_fd(my_fd); if (FSAL_IS_ERROR(status)) { LogCrit(COMPONENT_FSAL, "glusterfs_close_my_fd returned %s", fsal_err_txt(status)); /* cleanup as much as possible */ } } else if (my_fd->openflags != FSAL_O_CLOSED) { rc = glfs_close(my_fd->glfd); if (rc) { LogCrit(COMPONENT_FSAL, "glfs_close returned %s(%d)", strerror(errno), errno); } } } if (my_fd->creds.caller_garray) { gsh_free(my_fd->creds.caller_garray); my_fd->creds.caller_garray = NULL; } if (objhandle->glhandle) { rc = glfs_h_close(objhandle->glhandle); if (rc) { LogCrit(COMPONENT_FSAL, "glfs_h_close returned error %s(%d)", strerror(errno), errno); } objhandle->glhandle = NULL; } gsh_free(objhandle); #ifdef GLTIMING now(&e_time); latency_update(&s_time, &e_time, lat_handle_release); #endif } /** * @brief Implements GLUSTER FSAL objectoperation lookup */ static fsal_status_t lookup(struct fsal_obj_handle *parent, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { int rc = 0; fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; struct stat sb; struct glfs_object *glhandle = NULL; unsigned char globjhdl[GFAPI_HANDLE_LENGTH] = {'\0'}; char vol_uuid[GLAPI_UUID_LENGTH] = {'\0'}; struct glusterfs_handle *objhandle = NULL; struct glusterfs_export *glfs_export = container_of(op_ctx->fsal_export, struct glusterfs_export, export); struct glusterfs_handle *parenthandle = container_of(parent, struct glusterfs_handle, handle); #ifdef GLTIMING struct timespec s_time, e_time; now(&s_time); #endif glhandle = glfs_h_lookupat(glfs_export->gl_fs->fs, parenthandle->glhandle, path, &sb, 0); if (glhandle == NULL) { status = gluster2fsal_error(errno); goto out; } rc = glfs_h_extract_handle(glhandle, globjhdl, GFAPI_HANDLE_LENGTH); if (rc < 0) { status = gluster2fsal_error(errno); goto out; } rc = glfs_get_volumeid(glfs_export->gl_fs->fs, vol_uuid, GLAPI_UUID_LENGTH); if (rc < 0) { status = gluster2fsal_error(errno); goto out; } construct_handle(glfs_export, &sb, glhandle, globjhdl, GLAPI_HANDLE_LENGTH, &objhandle, vol_uuid); if (attrs_out != NULL) { posix2fsal_attributes_all(&sb, attrs_out); } *handle = &objhandle->handle; out: if (status.major != ERR_FSAL_NO_ERROR) gluster_cleanup_vars(glhandle); #ifdef GLTIMING now(&e_time); latency_update(&s_time, &e_time, lat_lookup); #endif return status; } /** * @brief Implements GLUSTER FSAL objectoperation readdir */ static fsal_status_t read_dirents(struct fsal_obj_handle *dir_hdl, fsal_cookie_t *whence, void *dir_state, fsal_readdir_cb cb, attrmask_t attrmask, bool *eof) { int rc = 0; fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; struct glfs_fd *glfd = NULL; long offset = 0; struct dirent *pde = NULL; struct glusterfs_export *glfs_export = container_of(op_ctx->fsal_export, struct glusterfs_export, export); struct glusterfs_handle *objhandle = container_of(dir_hdl, struct glusterfs_handle, handle); #ifdef USE_GLUSTER_XREADDIRPLUS struct glfs_object *glhandle = NULL; struct glfs_xreaddirp_stat *xstat = NULL; uint32_t flags = (GFAPI_XREADDIRP_STAT | GFAPI_XREADDIRP_HANDLE); #endif #ifdef GLTIMING struct timespec s_time, e_time; now(&s_time); #endif SET_GLUSTER_CREDS(glfs_export, &op_ctx->creds->caller_uid, &op_ctx->creds->caller_gid, op_ctx->creds->caller_glen, op_ctx->creds->caller_garray); /** @todo : Can we use globalfd instead */ glfd = glfs_h_opendir(glfs_export->gl_fs->fs, objhandle->glhandle); SET_GLUSTER_CREDS(glfs_export, NULL, NULL, 0, NULL); if (glfd == NULL) return gluster2fsal_error(errno); if (whence != NULL) offset = *whence; glfs_seekdir(glfd, offset); while (!(*eof)) { struct dirent de; struct fsal_obj_handle *obj; SET_GLUSTER_CREDS(glfs_export, &op_ctx->creds->caller_uid, &op_ctx->creds->caller_gid, op_ctx->creds->caller_glen, op_ctx->creds->caller_garray); #ifndef USE_GLUSTER_XREADDIRPLUS rc = glfs_readdir_r(glfd, &de, &pde); #else rc = glfs_xreaddirplus_r(glfd, flags, &xstat, &de, &pde); #endif SET_GLUSTER_CREDS(glfs_export, NULL, NULL, 0, NULL); if (rc < 0) { status = gluster2fsal_error(errno); goto out; } if (rc == 0 && pde == NULL) { *eof = true; goto out; } struct attrlist attrs; enum fsal_dir_result cb_rc; /* skip . and .. */ if ((strcmp(de.d_name, ".") == 0) || (strcmp(de.d_name, "..") == 0)) { #ifdef USE_GLUSTER_XREADDIRPLUS if (xstat) { glfs_free(xstat); xstat = NULL; } #endif continue; } fsal_prepare_attrs(&attrs, attrmask); #ifndef USE_GLUSTER_XREADDIRPLUS status = lookup(dir_hdl, de.d_name, &obj, &attrs); if (FSAL_IS_ERROR(status)) goto out; #else struct glfs_object *tmp = NULL; struct stat *sb; if (!xstat || !(rc & GFAPI_XREADDIRP_HANDLE)) { status = gluster2fsal_error(errno); goto out; } sb = glfs_xreaddirplus_get_stat(xstat); tmp = glfs_xreaddirplus_get_object(xstat); if (!sb || !tmp) { status = gluster2fsal_error(errno); goto out; } glhandle = glfs_object_copy(tmp); if (!glhandle) { status = gluster2fsal_error(errno); goto out; } status = glfs2fsal_handle(glfs_export, glhandle, &obj, sb, &attrs); glfs_free(xstat); xstat = NULL; if (FSAL_IS_ERROR(status)) { gluster_cleanup_vars(glhandle); goto out; } #endif cb_rc = cb(de.d_name, obj, &attrs, dir_state, glfs_telldir(glfd)); fsal_release_attrs(&attrs); /* Read ahead not supported by this FSAL. */ if (cb_rc >= DIR_READAHEAD) goto out; } out: #ifdef USE_GLUSTER_XREADDIRPLUS if (xstat) glfs_free(xstat); #endif SET_GLUSTER_CREDS(glfs_export, &op_ctx->creds->caller_uid, &op_ctx->creds->caller_gid, op_ctx->creds->caller_glen, op_ctx->creds->caller_garray); rc = glfs_closedir(glfd); SET_GLUSTER_CREDS(glfs_export, NULL, NULL, 0, NULL); if (rc < 0) status = gluster2fsal_error(errno); #ifdef GLTIMING now(&e_time); latency_update(&s_time, &e_time, lat_read_dirents); #endif return status; } /** * @brief Implements GLUSTER FSAL objectoperation mkdir */ static fsal_status_t makedir(struct fsal_obj_handle *dir_hdl, const char *name, struct attrlist *attrib, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { int rc = 0; fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; struct stat sb; struct glfs_object *glhandle = NULL; unsigned char globjhdl[GFAPI_HANDLE_LENGTH] = {'\0'}; char vol_uuid[GLAPI_UUID_LENGTH] = {'\0'}; struct glusterfs_handle *objhandle = NULL; struct glusterfs_export *glfs_export = container_of(op_ctx->fsal_export, struct glusterfs_export, export); struct glusterfs_handle *parenthandle = container_of(dir_hdl, struct glusterfs_handle, handle); #ifdef GLTIMING struct timespec s_time, e_time; now(&s_time); #endif SET_GLUSTER_CREDS(glfs_export, &op_ctx->creds->caller_uid, &op_ctx->creds->caller_gid, op_ctx->creds->caller_glen, op_ctx->creds->caller_garray); glhandle = glfs_h_mkdir(glfs_export->gl_fs->fs, parenthandle->glhandle, name, fsal2unix_mode(attrib->mode), &sb); SET_GLUSTER_CREDS(glfs_export, NULL, NULL, 0, NULL); if (glhandle == NULL) { status = gluster2fsal_error(errno); goto out; } rc = glfs_h_extract_handle(glhandle, globjhdl, GFAPI_HANDLE_LENGTH); if (rc < 0) { status = gluster2fsal_error(errno); goto out; } rc = glfs_get_volumeid(glfs_export->gl_fs->fs, vol_uuid, GLAPI_UUID_LENGTH); if (rc < 0) { status = gluster2fsal_error(errno); goto out; } construct_handle(glfs_export, &sb, glhandle, globjhdl, GLAPI_HANDLE_LENGTH, &objhandle, vol_uuid); if (attrs_out != NULL) { posix2fsal_attributes_all(&sb, attrs_out); } *handle = &objhandle->handle; /* We handled the mode above. */ FSAL_UNSET_MASK(attrib->valid_mask, ATTR_MODE); if (attrib->valid_mask) { /* Now per support_ex API, if there are any other attributes * set, go ahead and get them set now. */ status = (*handle)->obj_ops.setattr2(*handle, false, NULL, attrib); if (FSAL_IS_ERROR(status)) { /* Release the handle we just allocated. */ LogFullDebug(COMPONENT_FSAL, "setattr2 status=%s", fsal_err_txt(status)); (*handle)->obj_ops.release(*handle); /* We released handle at this point */ glhandle = NULL; *handle = NULL; } } else { status.major = ERR_FSAL_NO_ERROR; status.minor = 0; } FSAL_SET_MASK(attrib->valid_mask, ATTR_MODE); out: if (status.major != ERR_FSAL_NO_ERROR) gluster_cleanup_vars(glhandle); #ifdef GLTIMING now(&e_time); latency_update(&s_time, &e_time, lat_makedir); #endif return status; } /** * @brief Implements GLUSTER FSAL objectoperation mknode */ static fsal_status_t makenode(struct fsal_obj_handle *dir_hdl, const char *name, object_file_type_t nodetype, struct attrlist *attrib, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { int rc = 0; fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; struct stat sb; struct glfs_object *glhandle = NULL; unsigned char globjhdl[GFAPI_HANDLE_LENGTH] = {'\0'}; char vol_uuid[GLAPI_UUID_LENGTH] = {'\0'}; struct glusterfs_handle *objhandle = NULL; dev_t ndev = { 0, }; struct glusterfs_export *glfs_export = container_of(op_ctx->fsal_export, struct glusterfs_export, export); struct glusterfs_handle *parenthandle = container_of(dir_hdl, struct glusterfs_handle, handle); mode_t create_mode; #ifdef GLTIMING struct timespec s_time, e_time; now(&s_time); #endif switch (nodetype) { case BLOCK_FILE: /* FIXME: This needs a feature flag test? */ ndev = makedev(attrib->rawdev.major, attrib->rawdev.minor); create_mode = S_IFBLK; break; case CHARACTER_FILE: ndev = makedev(attrib->rawdev.major, attrib->rawdev.minor); create_mode = S_IFCHR; break; case FIFO_FILE: create_mode = S_IFIFO; break; case SOCKET_FILE: create_mode = S_IFSOCK; break; default: LogMajor(COMPONENT_FSAL, "Invalid node type in FSAL_mknode: %d", nodetype); return fsalstat(ERR_FSAL_INVAL, 0); } SET_GLUSTER_CREDS(glfs_export, &op_ctx->creds->caller_uid, &op_ctx->creds->caller_gid, op_ctx->creds->caller_glen, op_ctx->creds->caller_garray); glhandle = glfs_h_mknod(glfs_export->gl_fs->fs, parenthandle->glhandle, name, create_mode | fsal2unix_mode(attrib->mode), ndev, &sb); SET_GLUSTER_CREDS(glfs_export, NULL, NULL, 0, NULL); if (glhandle == NULL) { status = gluster2fsal_error(errno); goto out; } rc = glfs_h_extract_handle(glhandle, globjhdl, GFAPI_HANDLE_LENGTH); if (rc < 0) { status = gluster2fsal_error(errno); goto out; } rc = glfs_get_volumeid(glfs_export->gl_fs->fs, vol_uuid, GLAPI_UUID_LENGTH); if (rc < 0) { status = gluster2fsal_error(errno); goto out; } construct_handle(glfs_export, &sb, glhandle, globjhdl, GLAPI_HANDLE_LENGTH, &objhandle, vol_uuid); if (attrs_out != NULL) { posix2fsal_attributes_all(&sb, attrs_out); } *handle = &objhandle->handle; /* We handled the mode above. */ FSAL_UNSET_MASK(attrib->valid_mask, ATTR_MODE); if (attrib->valid_mask) { /* Now per support_ex API, if there are any other attributes * set, go ahead and get them set now. */ status = (*handle)->obj_ops.setattr2(*handle, false, NULL, attrib); if (FSAL_IS_ERROR(status)) { /* Release the handle we just allocated. */ LogFullDebug(COMPONENT_FSAL, "setattr2 status=%s", fsal_err_txt(status)); (*handle)->obj_ops.release(*handle); /* We released handle at this point */ glhandle = NULL; *handle = NULL; } } else { status.major = ERR_FSAL_NO_ERROR; status.minor = 0; } FSAL_SET_MASK(attrib->valid_mask, ATTR_MODE); out: if (status.major != ERR_FSAL_NO_ERROR) gluster_cleanup_vars(glhandle); #ifdef GLTIMING now(&e_time); latency_update(&s_time, &e_time, lat_makenode); #endif return status; } /** * @brief Implements GLUSTER FSAL objectoperation symlink */ static fsal_status_t makesymlink(struct fsal_obj_handle *dir_hdl, const char *name, const char *link_path, struct attrlist *attrib, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { int rc = 0; fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; struct stat sb; struct glfs_object *glhandle = NULL; unsigned char globjhdl[GFAPI_HANDLE_LENGTH] = {'\0'}; char vol_uuid[GLAPI_UUID_LENGTH] = {'\0'}; struct glusterfs_handle *objhandle = NULL; struct glusterfs_export *glfs_export = container_of(op_ctx->fsal_export, struct glusterfs_export, export); struct glusterfs_handle *parenthandle = container_of(dir_hdl, struct glusterfs_handle, handle); #ifdef GLTIMING struct timespec s_time, e_time; now(&s_time); #endif SET_GLUSTER_CREDS(glfs_export, &op_ctx->creds->caller_uid, &op_ctx->creds->caller_gid, op_ctx->creds->caller_glen, op_ctx->creds->caller_garray); glhandle = glfs_h_symlink(glfs_export->gl_fs->fs, parenthandle->glhandle, name, link_path, &sb); SET_GLUSTER_CREDS(glfs_export, NULL, NULL, 0, NULL); if (glhandle == NULL) { status = gluster2fsal_error(errno); goto out; } rc = glfs_h_extract_handle(glhandle, globjhdl, GFAPI_HANDLE_LENGTH); if (rc < 0) { status = gluster2fsal_error(errno); goto out; } rc = glfs_get_volumeid(glfs_export->gl_fs->fs, vol_uuid, GLAPI_UUID_LENGTH); if (rc < 0) { status = gluster2fsal_error(errno); goto out; } construct_handle(glfs_export, &sb, glhandle, globjhdl, GLAPI_HANDLE_LENGTH, &objhandle, vol_uuid); if (attrs_out != NULL) { posix2fsal_attributes_all(&sb, attrs_out); } *handle = &objhandle->handle; if (attrib->valid_mask) { /* Now per support_ex API, if there are any other attributes * set, go ahead and get them set now. */ status = (*handle)->obj_ops.setattr2(*handle, false, NULL, attrib); if (FSAL_IS_ERROR(status)) { /* Release the handle we just allocated. */ LogFullDebug(COMPONENT_FSAL, "setattr2 status=%s", fsal_err_txt(status)); (*handle)->obj_ops.release(*handle); /* We released handle at this point */ glhandle = NULL; *handle = NULL; } } else { status.major = ERR_FSAL_NO_ERROR; status.minor = 0; } out: if (status.major != ERR_FSAL_NO_ERROR) gluster_cleanup_vars(glhandle); #ifdef GLTIMING now(&e_time); latency_update(&s_time, &e_time, lat_makesymlink); #endif return status; } /** * @brief Implements GLUSTER FSAL objectoperation readlink */ static fsal_status_t readsymlink(struct fsal_obj_handle *obj_hdl, struct gsh_buffdesc *link_content, bool refresh) { int rc = 0; fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; struct glusterfs_export *glfs_export = container_of(op_ctx->fsal_export, struct glusterfs_export, export); struct glusterfs_handle *objhandle = container_of(obj_hdl, struct glusterfs_handle, handle); #ifdef GLTIMING struct timespec s_time, e_time; now(&s_time); #endif link_content->len = MAXPATHLEN; /* Max link path */ link_content->addr = gsh_malloc(link_content->len); SET_GLUSTER_CREDS(glfs_export, &op_ctx->creds->caller_uid, &op_ctx->creds->caller_gid, op_ctx->creds->caller_glen, op_ctx->creds->caller_garray); rc = glfs_h_readlink(glfs_export->gl_fs->fs, objhandle->glhandle, link_content->addr, link_content->len); SET_GLUSTER_CREDS(glfs_export, NULL, NULL, 0, NULL); if (rc < 0) { status = gluster2fsal_error(errno); goto out; } if (rc >= MAXPATHLEN) { status = gluster2fsal_error(EINVAL); goto out; } /* rc is the number of bytes copied into link_content->addr * without including '\0' character. */ *(char *)(link_content->addr + rc) = '\0'; link_content->len = rc + 1; out: if (status.major != ERR_FSAL_NO_ERROR) { gsh_free(link_content->addr); link_content->addr = NULL; link_content->len = 0; } #ifdef GLTIMING now(&e_time); latency_update(&s_time, &e_time, lat_readsymlink); #endif return status; } /** * @brief Implements GLUSTER FSAL objectoperation getattrs */ static fsal_status_t getattrs(struct fsal_obj_handle *obj_hdl, struct attrlist *attrs) { int rc = 0; fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; glusterfs_fsal_xstat_t buffxstat = { 0 }; struct glusterfs_export *glfs_export = container_of(op_ctx->fsal_export, struct glusterfs_export, export); struct glusterfs_handle *objhandle = container_of(obj_hdl, struct glusterfs_handle, handle); #ifdef GLTIMING struct timespec s_time, e_time; now(&s_time); #endif /* * There is a kind of race here when the glfd part of the * FSAL GLUSTER object handle is destroyed during a close * coming in from another NFSv3 WRITE thread which does * cache_inode_open(). Since the context/fd is destroyed * we cannot depend on glfs_fstat assuming glfd is valid. * Fixing the issue by removing the glfs_fstat call here. * So default to glfs_h_stat and re-optimize if a better * way is found - that may involve introducing locks in * the gfapi's for close and getattrs etc. */ /** @todo: With support_ex() above may no longer be valid. * This needs to be revisited */ /* @todo: with POSIX ACLs every user shall have permissions to * read stat & ACLs. But that may not be the case with RichACLs. * If the ganesha service is started by non-root user, that user * may get restricted from reading ACL. */ rc = glfs_h_stat(glfs_export->gl_fs->fs, objhandle->glhandle, &buffxstat.buffstat); if (rc != 0) { if (errno == ENOENT) status = gluster2fsal_error(ESTALE); else status = gluster2fsal_error(errno); if (attrs->request_mask & ATTR_RDATTR_ERR) { /* Caller asked for error to be visible. */ attrs->valid_mask = ATTR_RDATTR_ERR; } goto out; } stat2fsal_attributes(&buffxstat.buffstat, attrs); if (obj_hdl->type == DIRECTORY) buffxstat.is_dir = true; else buffxstat.is_dir = false; if (attrs->request_mask & ATTR_ACL) { /* Fetch the ACL */ status = glusterfs_get_acl(glfs_export, objhandle->glhandle, &buffxstat, attrs); if (!FSAL_IS_ERROR(status)) { /* Success, so mark ACL as valid. */ attrs->valid_mask |= ATTR_ACL; } } /* * * The error ENOENT is not an expected error for GETATTRS * Due to this, operations such as RENAME will fail when * it calls GETATTRS on removed file. But for dead links * we should not return error * */ if (status.major == ERR_FSAL_NOENT) { if (obj_hdl->type == SYMBOLIC_LINK) status = fsalstat(ERR_FSAL_NO_ERROR, 0); else status = gluster2fsal_error(ESTALE); } if (FSAL_IS_ERROR(status)) { if (attrs->request_mask & ATTR_RDATTR_ERR) { /* Caller asked for error to be visible. */ attrs->valid_mask = ATTR_RDATTR_ERR; } } out: #ifdef GLTIMING now(&e_time); latency_update(&s_time, &e_time, lat_getattrs); #endif return status; } /** * @brief Implements GLUSTER FSAL objectoperation link */ static fsal_status_t linkfile(struct fsal_obj_handle *obj_hdl, struct fsal_obj_handle *destdir_hdl, const char *name) { int rc = 0; fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; struct glusterfs_export *glfs_export = container_of(op_ctx->fsal_export, struct glusterfs_export, export); struct glusterfs_handle *objhandle = container_of(obj_hdl, struct glusterfs_handle, handle); struct glusterfs_handle *dstparenthandle = container_of(destdir_hdl, struct glusterfs_handle, handle); #ifdef GLTIMING struct timespec s_time, e_time; now(&s_time); #endif SET_GLUSTER_CREDS(glfs_export, &op_ctx->creds->caller_uid, &op_ctx->creds->caller_gid, op_ctx->creds->caller_glen, op_ctx->creds->caller_garray); rc = glfs_h_link(glfs_export->gl_fs->fs, objhandle->glhandle, dstparenthandle->glhandle, name); SET_GLUSTER_CREDS(glfs_export, NULL, NULL, 0, NULL); if (rc != 0) { status = gluster2fsal_error(errno); goto out; } out: #ifdef GLTIMING now(&e_time); latency_update(&s_time, &e_time, lat_linkfile); #endif return status; } /** * @brief Implements GLUSTER FSAL objectoperation rename */ static fsal_status_t renamefile(struct fsal_obj_handle *obj_hdl, struct fsal_obj_handle *olddir_hdl, const char *old_name, struct fsal_obj_handle *newdir_hdl, const char *new_name) { int rc = 0; fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; struct glusterfs_export *glfs_export = container_of(op_ctx->fsal_export, struct glusterfs_export, export); struct glusterfs_handle *srcparenthandle = container_of(olddir_hdl, struct glusterfs_handle, handle); struct glusterfs_handle *dstparenthandle = container_of(newdir_hdl, struct glusterfs_handle, handle); #ifdef GLTIMING struct timespec s_time, e_time; now(&s_time); #endif SET_GLUSTER_CREDS(glfs_export, &op_ctx->creds->caller_uid, &op_ctx->creds->caller_gid, op_ctx->creds->caller_glen, op_ctx->creds->caller_garray); rc = glfs_h_rename(glfs_export->gl_fs->fs, srcparenthandle->glhandle, old_name, dstparenthandle->glhandle, new_name); SET_GLUSTER_CREDS(glfs_export, NULL, NULL, 0, NULL); if (rc != 0) { status = gluster2fsal_error(errno); goto out; } out: #ifdef GLTIMING now(&e_time); latency_update(&s_time, &e_time, lat_renamefile); #endif return status; } /** * @brief Implements GLUSTER FSAL objectoperation unlink */ static fsal_status_t file_unlink(struct fsal_obj_handle *dir_hdl, struct fsal_obj_handle *obj_hdl, const char *name) { int rc = 0; fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; struct glusterfs_export *glfs_export = container_of(op_ctx->fsal_export, struct glusterfs_export, export); struct glusterfs_handle *parenthandle = container_of(dir_hdl, struct glusterfs_handle, handle); #ifdef GLTIMING struct timespec s_time, e_time; now(&s_time); #endif SET_GLUSTER_CREDS(glfs_export, &op_ctx->creds->caller_uid, &op_ctx->creds->caller_gid, op_ctx->creds->caller_glen, op_ctx->creds->caller_garray); rc = glfs_h_unlink(glfs_export->gl_fs->fs, parenthandle->glhandle, name); SET_GLUSTER_CREDS(glfs_export, NULL, NULL, 0, NULL); if (rc != 0) status = gluster2fsal_error(errno); #ifdef GLTIMING now(&e_time); latency_update(&s_time, &e_time, lat_file_unlink); #endif return status; } /** * @brief Implements GLUSTER FSAL objectoperation share_op */ /* static fsal_status_t share_op(struct fsal_obj_handle *obj_hdl, void *p_owner, fsal_share_param_t request_share) { return fsalstat(ERR_FSAL_NOTSUPP, 0); } */ fsal_status_t glusterfs_open_my_fd(struct glusterfs_handle *objhandle, fsal_openflags_t openflags, int posix_flags, struct glusterfs_fd *my_fd) { fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; struct glfs_fd *glfd = NULL; struct glusterfs_export *glfs_export = container_of(op_ctx->fsal_export, struct glusterfs_export, export); gid_t **garray_copy = NULL; #ifdef GLTIMING struct timespec s_time, e_time; now(&s_time); #endif LogFullDebug(COMPONENT_FSAL, "my_fd->fd = %p openflags = %x, posix_flags = %x", my_fd->glfd, openflags, posix_flags); assert(my_fd->glfd == NULL && my_fd->openflags == FSAL_O_CLOSED && openflags != 0); LogFullDebug(COMPONENT_FSAL, "openflags = %x, posix_flags = %x", openflags, posix_flags); SET_GLUSTER_CREDS(glfs_export, &op_ctx->creds->caller_uid, &op_ctx->creds->caller_gid, op_ctx->creds->caller_glen, op_ctx->creds->caller_garray); glfd = glfs_h_open(glfs_export->gl_fs->fs, objhandle->glhandle, posix_flags); /* restore credentials */ SET_GLUSTER_CREDS(glfs_export, NULL, NULL, 0, NULL); if (glfd == NULL) { status = gluster2fsal_error(errno); goto out; } my_fd->glfd = glfd; my_fd->openflags = openflags; my_fd->creds.caller_uid = op_ctx->creds->caller_uid; my_fd->creds.caller_gid = op_ctx->creds->caller_gid; my_fd->creds.caller_glen = op_ctx->creds->caller_glen; garray_copy = &my_fd->creds.caller_garray; if ((*garray_copy) != NULL) { /* Replace old creds */ gsh_free(*garray_copy); *garray_copy = NULL; } if (op_ctx->creds->caller_glen) { (*garray_copy) = gsh_malloc(op_ctx->creds->caller_glen * sizeof(gid_t)); memcpy((*garray_copy), op_ctx->creds->caller_garray, op_ctx->creds->caller_glen * sizeof(gid_t)); } out: #ifdef GLTIMING now(&e_time); latency_update(&s_time, &e_time, lat_file_open); #endif return status; } fsal_status_t glusterfs_close_my_fd(struct glusterfs_fd *my_fd) { int rc = 0; fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; struct glusterfs_export *glfs_export = container_of(op_ctx->fsal_export, struct glusterfs_export, export); #ifdef GLTIMING struct timespec s_time, e_time; now(&s_time); #endif if (my_fd->glfd && my_fd->openflags != FSAL_O_CLOSED) { /* Use the same credentials which opened up the fd */ SET_GLUSTER_CREDS(glfs_export, &my_fd->creds.caller_uid, &my_fd->creds.caller_gid, my_fd->creds.caller_glen, my_fd->creds.caller_garray); rc = glfs_close(my_fd->glfd); /* restore credentials */ SET_GLUSTER_CREDS(glfs_export, NULL, NULL, 0, NULL); if (rc != 0) { status = gluster2fsal_error(errno); LogCrit(COMPONENT_FSAL, "Error : close returns with %s", strerror(errno)); } } my_fd->glfd = NULL; my_fd->openflags = FSAL_O_CLOSED; my_fd->creds.caller_uid = 0; my_fd->creds.caller_gid = 0; my_fd->creds.caller_glen = 0; if (my_fd->creds.caller_garray) { gsh_free(my_fd->creds.caller_garray); my_fd->creds.caller_garray = NULL; } #ifdef GLTIMING now(&e_time); latency_update(&s_time, &e_time, lat_file_close); #endif return status; } /** * @brief Implements GLUSTER FSAL objectoperation close @todo: close2() could be used to close globalfd as well. */ static fsal_status_t file_close(struct fsal_obj_handle *obj_hdl) { fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; struct glusterfs_handle *objhandle = container_of(obj_hdl, struct glusterfs_handle, handle); #ifdef GLTIMING struct timespec s_time, e_time; now(&s_time); #endif assert(obj_hdl->type == REGULAR_FILE); if (objhandle->globalfd.openflags == FSAL_O_CLOSED) return fsalstat(ERR_FSAL_NOT_OPENED, 0); /* Take write lock on object to protect file descriptor. * This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->obj_lock); status = glusterfs_close_my_fd(&objhandle->globalfd); objhandle->globalfd.openflags = FSAL_O_CLOSED; PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); #ifdef GLTIMING now(&e_time); latency_update(&s_time, &e_time, lat_file_close); #endif return status; } /** * @brief Function to open an fsal_obj_handle's global file descriptor. * * @param[in] obj_hdl File on which to operate * @param[in] openflags Mode for open * @param[out] fd File descriptor that is to be used * * @return FSAL status. */ fsal_status_t glusterfs_open_func(struct fsal_obj_handle *obj_hdl, fsal_openflags_t openflags, struct fsal_fd *fd) { struct glusterfs_handle *myself; int posix_flags = 0; myself = container_of(obj_hdl, struct glusterfs_handle, handle); fsal2posix_openflags(openflags, &posix_flags); return glusterfs_open_my_fd(myself, openflags, posix_flags, (struct glusterfs_fd *)fd); } /** * @brief Function to close an fsal_obj_handle's global file descriptor. * * @param[in] obj_hdl File on which to operate * @param[in] fd File handle to close * * @return FSAL status. */ fsal_status_t glusterfs_close_func(struct fsal_obj_handle *obj_hdl, struct fsal_fd *fd) { return glusterfs_close_my_fd((struct glusterfs_fd *)fd); } /** * @brief Find a file descriptor for a read, write, setattr2 operation. * * We do not need file descriptors for non-regular files, so this never has to * handle them. */ fsal_status_t find_fd(struct glusterfs_fd *my_fd, struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, fsal_openflags_t openflags, bool *has_lock, bool *closefd, bool open_for_locks) { struct glusterfs_handle *myself; fsal_status_t status = {ERR_FSAL_NO_ERROR, 0}; struct glusterfs_fd tmp_fd = {0}, *tmp2_fd = &tmp_fd; bool reusing_open_state_fd = false; myself = container_of(obj_hdl, struct glusterfs_handle, handle); /* Handle only regular files */ if (obj_hdl->type != REGULAR_FILE) return fsalstat(posix2fsal_error(EINVAL), EINVAL); status = fsal_find_fd((struct fsal_fd **)&tmp2_fd, obj_hdl, (struct fsal_fd *)&myself->globalfd, &myself->share, bypass, state, openflags, glusterfs_open_func, glusterfs_close_func, has_lock, closefd, open_for_locks, &reusing_open_state_fd); /* since tmp2_fd is not accessed/closed outside * this routine, its safe to copy its variables into my_fd * without taking extra reference or allocating extra * memory. */ if (reusing_open_state_fd) { my_fd->glfd = glfs_dup(tmp2_fd->glfd); my_fd->creds.caller_garray = gsh_malloc(my_fd->creds.caller_glen * sizeof(gid_t)); memcpy(my_fd->creds.caller_garray, op_ctx->creds->caller_garray, op_ctx->creds->caller_glen * sizeof(gid_t)); } else { my_fd->glfd = tmp2_fd->glfd; my_fd->creds.caller_garray = tmp2_fd->creds.caller_garray; } my_fd->openflags = tmp2_fd->openflags; my_fd->creds.caller_uid = tmp2_fd->creds.caller_uid; my_fd->creds.caller_gid = tmp2_fd->creds.caller_gid; my_fd->creds.caller_glen = tmp2_fd->creds.caller_glen; return status; } /** * @brief Merge a duplicate handle with an original handle * * This function is used if an upper layer detects that a duplicate * object handle has been created. It allows the FSAL to merge anything * from the duplicate back into the original. * * The caller must release the object (the caller may have to close * files if the merge is unsuccessful). * * @param[in] orig_hdl Original handle * @param[in] dupe_hdl Handle to merge into original * * @return FSAL status. * */ fsal_status_t glusterfs_merge(struct fsal_obj_handle *orig_hdl, struct fsal_obj_handle *dupe_hdl) { fsal_status_t status = {ERR_FSAL_NO_ERROR, 0}; if (orig_hdl->type == REGULAR_FILE && dupe_hdl->type == REGULAR_FILE) { /* We need to merge the share reservations on this file. * This could result in ERR_FSAL_SHARE_DENIED. */ struct glusterfs_handle *orig, *dupe; orig = container_of(orig_hdl, struct glusterfs_handle, handle); dupe = container_of(dupe_hdl, struct glusterfs_handle, handle); /* This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&orig_hdl->obj_lock); status = merge_share(&orig->share, &dupe->share); PTHREAD_RWLOCK_unlock(&orig_hdl->obj_lock); } return status; } /* open2 */ static fsal_status_t glusterfs_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) { fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; int p_flags = 0; struct glusterfs_export *glfs_export = container_of(op_ctx->fsal_export, struct glusterfs_export, export); struct glusterfs_handle *myself, *parenthandle = NULL; struct glusterfs_fd *my_fd = NULL; struct stat sb = {0}; struct glfs_object *glhandle = NULL; unsigned char globjhdl[GFAPI_HANDLE_LENGTH] = {'\0'}; char vol_uuid[GLAPI_UUID_LENGTH] = {'\0'}; bool truncated; bool created = false; int retval = 0; mode_t unix_mode; #ifdef GLTIMING struct timespec s_time, e_time; now(&s_time); #endif if (state != NULL) my_fd = &container_of(state, struct glusterfs_state_fd, state)->glusterfs_fd; fsal2posix_openflags(openflags, &p_flags); truncated = (p_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 */ struct glusterfs_handle *myself; myself = container_of(obj_hdl, struct glusterfs_handle, handle); #if 0 /** @todo: fsid work */ 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); } #endif 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->obj_lock); /* Check share reservation conflicts. */ status = check_share_conflict(&myself->share, openflags, false); if (FSAL_IS_ERROR(status)) { PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /* Take the share reservation now by updating the * counters. */ update_share_counters(&myself->share, FSAL_O_CLOSED, openflags); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); } else { /* We need to use the global fd to continue, and take * the lock to protect it. */ my_fd = &myself->globalfd; PTHREAD_RWLOCK_wrlock(&obj_hdl->obj_lock); } if (my_fd->openflags != FSAL_O_CLOSED) { glusterfs_close_my_fd(my_fd); } /* truncate is set in p_flags */ status = glusterfs_open_my_fd(myself, openflags, p_flags, my_fd); if (FSAL_IS_ERROR(status)) { status = gluster2fsal_error(errno); if (state == NULL) { /* Release the lock taken above, and return * since there is nothing to undo. */ PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); goto out; } else { /* Error - need to release the share */ goto undo_share; } } if (createmode >= FSAL_EXCLUSIVE || truncated) { /* Fetch the attributes to check against the * verifier in case of exclusive open/create. */ struct stat stat; /* set proper credentials */ /* @todo: with POSIX ACLs every user shall have * permissions to read stat & ACLs. But that may not be * the case with RichACLs. If the ganesha service is * started by non-root user, that user may get * restricted from reading ACL. */ retval = glfs_fstat(my_fd->glfd, &stat); if (retval == 0) { LogFullDebug(COMPONENT_FSAL, "New size = %" PRIx64, stat.st_size); if (attrs_out) { posix2fsal_attributes_all(&stat, attrs_out); } } 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); } } else if (attrs_out && attrs_out->request_mask & ATTR_RDATTR_ERR) { attrs_out->valid_mask &= ATTR_RDATTR_ERR; } if (state == NULL) { /* If no state, release the lock taken above and return * status. If success, we haven't done any permission * check so ask the caller to do so. */ PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); *caller_perm_check = !FSAL_IS_ERROR(status); return status; } if (!FSAL_IS_ERROR(status)) { /* Return success. We haven't done any permission * check so ask the caller to do so. */ *caller_perm_check = true; return status; } (void) glusterfs_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->obj_lock); update_share_counters(&myself->share, openflags, FSAL_O_CLOSED); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /* case name_not_null */ /* 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. */ if (createmode != FSAL_NO_CREATE) { /* Now add in O_CREAT and O_EXCL. */ p_flags |= O_CREAT; /* And if we are at least FSAL_GUARDED, do an O_EXCL create. */ if (createmode >= FSAL_GUARDED) p_flags |= O_EXCL; /* Fetch the mode attribute to use. */ 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->valid_mask, ATTR_MODE); } if (createmode == FSAL_UNCHECKED && (attrib_set->valid_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. */ p_flags |= O_EXCL; } /** @todo: we do not have openat implemented yet..meanwhile * use 'glfs_h_creat' */ /* obtain parent directory handle */ parenthandle = container_of(obj_hdl, struct glusterfs_handle, handle); if (createmode == FSAL_NO_CREATE) { /* lookup if the object exists */ status = (obj_hdl)->obj_ops.lookup(obj_hdl, name, new_obj, attrs_out); if (FSAL_IS_ERROR(status)) { *new_obj = NULL; goto direrr; } myself = container_of(*new_obj, struct glusterfs_handle, handle); /* The open is not done with the caller's credentials so ask * the caller to perform a permission check. */ *caller_perm_check = true; goto open; } /* Become the user because we are creating an object in this dir. */ /* set proper credentials */ SET_GLUSTER_CREDS(glfs_export, &op_ctx->creds->caller_uid, &op_ctx->creds->caller_gid, op_ctx->creds->caller_glen, op_ctx->creds->caller_garray); /** @todo: glfs_h_creat doesn't honour NO_CREATE mode. Instead use * glfs_h_open to verify if the file already exists. */ glhandle = glfs_h_creat(glfs_export->gl_fs->fs, parenthandle->glhandle, name, p_flags, unix_mode, &sb); if (glhandle == NULL && errno == EEXIST && createmode == FSAL_UNCHECKED) { /* We tried to create O_EXCL to set attributes and failed. * Remove O_EXCL and retry, also remember not to set attributes. * 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. */ p_flags &= ~O_EXCL; glhandle = glfs_h_creat(glfs_export->gl_fs->fs, parenthandle->glhandle, name, p_flags, unix_mode, &sb); } else if (!errno) { created = true; } /* restore credentials */ SET_GLUSTER_CREDS(glfs_export, NULL, NULL, 0, NULL); if (glhandle == NULL) { status = gluster2fsal_error(errno); goto out; } /* 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. That permission check might fail * if the file created after the unlink has a mode that doesn't allow * the caller/creator to open the file (on the other hand, one hopes * a non-exclusive open doesn't set a mode that doesn't allow read/write * since the application clearly expects that another process may have * created the file). This failure case really isn't too awful since * it would just look to the caller like someone else had created the * file with a mode that prevented the open this caller was attempting. */ /* Do a permission check if we were not attempting to create. If we * were attempting any sort of create, then the openat call was made * with the caller's credentials active and as such was permission * checked. */ *caller_perm_check = !created; /* Since the file is created, remove O_CREAT/O_EXCL flags */ p_flags &= ~(O_EXCL | O_CREAT); retval = glfs_h_extract_handle(glhandle, globjhdl, GFAPI_HANDLE_LENGTH); if (retval < 0) { status = gluster2fsal_error(errno); goto direrr; } retval = glfs_get_volumeid(glfs_export->gl_fs->fs, vol_uuid, GLAPI_UUID_LENGTH); if (retval < 0) { status = gluster2fsal_error(errno); goto direrr; } construct_handle(glfs_export, &sb, glhandle, globjhdl, GLAPI_HANDLE_LENGTH, &myself, vol_uuid); *new_obj = &myself->handle; /* 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 = &myself->globalfd; open: /* now open it */ status = glusterfs_open_my_fd(myself, openflags, p_flags, my_fd); if (FSAL_IS_ERROR(status)) goto direrr; if (created && attrib_set->valid_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. */ status = (*new_obj)->obj_ops.setattr2(*new_obj, false, state, attrib_set); if (FSAL_IS_ERROR(status)) goto fileerr; if (attrs_out != NULL) { status = (*new_obj)->obj_ops.getattrs(*new_obj, attrs_out); if (FSAL_IS_ERROR(status) && (attrs_out->request_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_all(&sb, attrs_out); } 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)->obj_lock); /* Take the share reservation now by updating the counters. */ update_share_counters(&myself->share, FSAL_O_CLOSED, openflags); PTHREAD_RWLOCK_unlock(&(*new_obj)->obj_lock); } return fsalstat(ERR_FSAL_NO_ERROR, 0); fileerr: /* Avoiding use after freed, make sure close my_fd before * obj_ops.release(), glfs_close is called depends on * FSAL_O_CLOSED flags, it's harmless of closing my_fd twice * in the floowing obj_ops.release(). */ glusterfs_close_my_fd(my_fd); direrr: /* Release the handle we just allocated. */ if (*new_obj) { (*new_obj)->obj_ops.release(*new_obj); /* We released handle at this point */ glhandle = NULL; *new_obj = NULL; } /* Delete the file if we actually created it. */ if (created) { SET_GLUSTER_CREDS(glfs_export, &op_ctx->creds->caller_uid, &op_ctx->creds->caller_gid, op_ctx->creds->caller_glen, op_ctx->creds->caller_garray); glfs_h_unlink(glfs_export->gl_fs->fs, parenthandle->glhandle, name); SET_GLUSTER_CREDS(glfs_export, NULL, NULL, 0, NULL); } if (status.major != ERR_FSAL_NO_ERROR) gluster_cleanup_vars(glhandle); out: #ifdef GLTIMING now(&e_time); latency_update(&s_time, &e_time, lat_file_open); #endif return status; } /* reopen2 */ static fsal_status_t glusterfs_reopen2(struct fsal_obj_handle *obj_hdl, struct state_t *state, fsal_openflags_t openflags) { struct glusterfs_fd fd = {0}, *my_fd = &fd, *my_share_fd = NULL; struct glusterfs_handle *myself; fsal_status_t status = {0, 0}; int posix_flags = 0; fsal_openflags_t old_openflags; my_share_fd = &container_of(state, struct glusterfs_state_fd, state)->glusterfs_fd; fsal2posix_openflags(openflags, &posix_flags); memset(my_fd, 0, sizeof(*my_fd)); myself = container_of(obj_hdl, struct glusterfs_handle, handle); #if 0 /** @todo: fsid work */ 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); } #endif /* This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->obj_lock); old_openflags = my_share_fd->openflags; /* We can conflict with old share, so go ahead and check now. */ status = check_share_conflict(&myself->share, openflags, false); if (FSAL_IS_ERROR(status)) { PTHREAD_RWLOCK_unlock(&obj_hdl->obj_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->share, old_openflags, openflags); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); status = glusterfs_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. Make sure no one is using the fd that we are * about to close! */ PTHREAD_RWLOCK_wrlock(&my_share_fd->fdlock); glusterfs_close_my_fd(my_share_fd); my_share_fd->glfd = my_fd->glfd; my_share_fd->openflags = my_fd->openflags; PTHREAD_RWLOCK_unlock(&my_share_fd->fdlock); } 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->obj_lock); update_share_counters(&myself->share, openflags, old_openflags); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); } return status; } /* read2 */ static fsal_status_t glusterfs_read2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t seek_descriptor, size_t buffer_size, void *buffer, size_t *read_amount, bool *end_of_file, struct io_info *info) { struct glusterfs_fd my_fd = {0}; ssize_t nb_read; fsal_status_t status; int retval = 0; bool has_lock = false; bool closefd = false; struct glusterfs_export *glfs_export = container_of(op_ctx->fsal_export, struct glusterfs_export, export); struct glusterfs_fd *glusterfs_fd = NULL; if (info != NULL) { /* Currently we don't support READ_PLUS */ return fsalstat(ERR_FSAL_NOTSUPP, 0); } #if 0 /** @todo: fsid work */ 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); } #endif /* Acquire state's fdlock to prevent OPEN upgrade closing the * file descriptor while we use it. */ if (state) { glusterfs_fd = &container_of(state, struct glusterfs_state_fd, state)->glusterfs_fd; PTHREAD_RWLOCK_rdlock(&glusterfs_fd->fdlock); } /* Get a usable file descriptor */ status = find_fd(&my_fd, obj_hdl, bypass, state, FSAL_O_READ, &has_lock, &closefd, false); if (FSAL_IS_ERROR(status)) goto out; SET_GLUSTER_CREDS(glfs_export, &op_ctx->creds->caller_uid, &op_ctx->creds->caller_gid, op_ctx->creds->caller_glen, op_ctx->creds->caller_garray); nb_read = glfs_pread(my_fd.glfd, buffer, buffer_size, seek_descriptor, 0); /* restore credentials */ SET_GLUSTER_CREDS(glfs_export, NULL, NULL, 0, NULL); if (seek_descriptor == -1 || nb_read == -1) { retval = errno; status = fsalstat(posix2fsal_error(retval), retval); goto out; } *read_amount = nb_read; if (nb_read < buffer_size) *end_of_file = true; #if 0 /** @todo * * Is this all we really need to do to support READ_PLUS? Will anyone * ever get upset that we don't return holes, even for blocks of all * zeroes? * */ if (info != NULL) { info->io_content.what = NFS4_CONTENT_DATA; info->io_content.data.d_offset = offset + nb_read; info->io_content.data.d_data.data_len = nb_read; info->io_content.data.d_data.data_val = buffer; } #endif out: if (glusterfs_fd) PTHREAD_RWLOCK_unlock(&glusterfs_fd->fdlock); if (closefd) glusterfs_close_my_fd(&my_fd); if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /* write2 */ static fsal_status_t glusterfs_write2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t seek_descriptor, size_t buffer_size, void *buffer, size_t *write_amount, bool *fsal_stable, struct io_info *info) { ssize_t nb_written; fsal_status_t status; int retval = 0; struct glusterfs_fd my_fd = {0}; bool has_lock = false; bool closefd = false; fsal_openflags_t openflags = FSAL_O_WRITE; struct glusterfs_export *glfs_export = container_of(op_ctx->fsal_export, struct glusterfs_export, export); struct glusterfs_fd *glusterfs_fd = NULL; if (info != NULL) { /* Currently we don't support WRITE_PLUS */ return fsalstat(ERR_FSAL_NOTSUPP, 0); } #if 0 /** @todo: fsid work */ 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); } #endif /* Acquire state's fdlock to prevent OPEN upgrade closing the * file descriptor while we use it. */ if (state) { glusterfs_fd = &container_of(state, struct glusterfs_state_fd, state)->glusterfs_fd; PTHREAD_RWLOCK_rdlock(&glusterfs_fd->fdlock); } /* Get a usable file descriptor */ status = find_fd(&my_fd, obj_hdl, bypass, state, openflags, &has_lock, &closefd, false); if (FSAL_IS_ERROR(status)) goto out; SET_GLUSTER_CREDS(glfs_export, &op_ctx->creds->caller_uid, &op_ctx->creds->caller_gid, op_ctx->creds->caller_glen, op_ctx->creds->caller_garray); nb_written = glfs_pwrite(my_fd.glfd, buffer, buffer_size, seek_descriptor, ((*fsal_stable) ? O_SYNC : 0)); /* restore credentials */ SET_GLUSTER_CREDS(glfs_export, NULL, NULL, 0, NULL); if (nb_written == -1) { retval = errno; status = fsalstat(posix2fsal_error(retval), retval); goto out; } *write_amount = nb_written; out: if (glusterfs_fd) PTHREAD_RWLOCK_unlock(&glusterfs_fd->fdlock); if (closefd) glusterfs_close_my_fd(&my_fd); if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /* commit2 */ static fsal_status_t glusterfs_commit2(struct fsal_obj_handle *obj_hdl, off_t offset, size_t len) { fsal_status_t status; int retval; struct glusterfs_fd tmp_fd = { FSAL_O_CLOSED, PTHREAD_RWLOCK_INITIALIZER, NULL }; struct glusterfs_fd *out_fd = &tmp_fd; struct glusterfs_handle *myself = NULL; bool has_lock = false; bool closefd = false; struct glusterfs_export *glfs_export = container_of(op_ctx->fsal_export, struct glusterfs_export, export); myself = container_of(obj_hdl, struct glusterfs_handle, handle); /* Make sure file is open in appropriate mode. * Do not check share reservation. */ status = fsal_reopen_obj(obj_hdl, false, false, FSAL_O_WRITE, (struct fsal_fd *)&myself->globalfd, &myself->share, glusterfs_open_func, glusterfs_close_func, (struct fsal_fd **)&out_fd, &has_lock, &closefd); if (!FSAL_IS_ERROR(status)) { SET_GLUSTER_CREDS(glfs_export, &op_ctx->creds->caller_uid, &op_ctx->creds->caller_gid, op_ctx->creds->caller_glen, op_ctx->creds->caller_garray); retval = glfs_fsync(out_fd->glfd); if (retval == -1) { retval = errno; status = fsalstat(posix2fsal_error(retval), retval); } /* restore credentials */ SET_GLUSTER_CREDS(glfs_export, NULL, NULL, 0, NULL); } if (closefd) glusterfs_close_my_fd(out_fd); if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /* lock_op2 */ static fsal_status_t glusterfs_lock_op2(struct fsal_obj_handle *obj_hdl, struct state_t *state, void *p_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; struct glusterfs_fd my_fd = {0}; bool has_lock = false; bool closefd = false; bool bypass = false; fsal_openflags_t openflags = FSAL_O_RDWR; struct glusterfs_export *glfs_export = container_of(op_ctx->fsal_export, struct glusterfs_export, export); struct glusterfs_fd *glusterfs_fd = NULL; #if 0 /** @todo: fsid work */ 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); } #endif 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) { /* We may end up using global fd, don't fail on a deny mode */ bypass = true; fcntl_comm = F_GETLK; openflags = FSAL_O_ANY; } else if (lock_op == FSAL_OP_LOCK) { fcntl_comm = F_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_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); } /* Acquire state's fdlock to prevent OPEN upgrade closing the * file descriptor while we use it. */ if (state) { glusterfs_fd = &container_of(state, struct glusterfs_state_fd, state)->glusterfs_fd; PTHREAD_RWLOCK_rdlock(&glusterfs_fd->fdlock); } /* Get a usable file descriptor */ status = find_fd(&my_fd, obj_hdl, bypass, state, openflags, &has_lock, &closefd, (state && ((state->state_type == STATE_TYPE_NLM_LOCK) || (state->state_type == STATE_TYPE_9P_FID)))); if (FSAL_IS_ERROR(status)) { LogCrit(COMPONENT_FSAL, "Unable to find fd for lock operation"); return status; } errno = 0; SET_GLUSTER_CREDS(glfs_export, &op_ctx->creds->caller_uid, &op_ctx->creds->caller_gid, op_ctx->creds->caller_glen, op_ctx->creds->caller_garray); /* Convert lkowner ptr address to opaque string */ retval = glfs_fd_set_lkowner(my_fd.glfd, p_owner, sizeof(p_owner)); if (retval) { LogCrit(COMPONENT_FSAL, "Setting lkowner failed"); goto err; } retval = glfs_posix_lock(my_fd.glfd, fcntl_comm, &lock_args); if (retval /* && lock_op == FSAL_OP_LOCK */) { retval = errno; int rc = 0; LogDebug(COMPONENT_FSAL, "fcntl returned %d %s", retval, strerror(retval)); if (conflicting_lock != NULL) { /* Get the conflicting lock */ rc = glfs_fd_set_lkowner(my_fd.glfd, p_owner, sizeof(p_owner)); if (rc) { retval = errno; /* we losethe initial error */ LogCrit(COMPONENT_FSAL, "Setting lkowner while trying to get conflicting lock failed"); goto err; } rc = glfs_posix_lock(my_fd.glfd, 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; } 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: SET_GLUSTER_CREDS(glfs_export, NULL, NULL, 0, NULL); if (glusterfs_fd) PTHREAD_RWLOCK_unlock(&glusterfs_fd->fdlock); if (closefd) glusterfs_close_my_fd(&my_fd); if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return fsalstat(posix2fsal_error(retval), retval); } /** * @brief Set attributes on an object * * This function sets attributes on an object. Which attributes are * set is determined by attrib_set->valid_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. */ static fsal_status_t glusterfs_setattr2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, struct attrlist *attrib_set) { struct glusterfs_handle *myself; fsal_status_t status = {0, 0}; int retval = 0; fsal_openflags_t openflags = FSAL_O_ANY; bool has_lock = false; bool closefd = false; struct glusterfs_fd my_fd = {0}; struct glusterfs_export *glfs_export = container_of(op_ctx->fsal_export, struct glusterfs_export, export); glusterfs_fsal_xstat_t buffxstat; int attr_valid = 0; int mask = 0; struct glusterfs_fd *glusterfs_fd = NULL; /** @todo: Handle special file symblic links etc */ /* apply umask, if mode attribute is to be changed */ if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_MODE)) attrib_set->mode &= ~op_ctx->fsal_export->exp_ops.fs_umask(op_ctx->fsal_export); myself = container_of(obj_hdl, struct glusterfs_handle, handle); #if 0 /** @todo: fsid work */ 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); } #endif /* 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->valid_mask, ATTR_SIZE)) { if (obj_hdl->type != REGULAR_FILE) return fsalstat(ERR_FSAL_INVAL, EINVAL); openflags = FSAL_O_RDWR; } /** TRUNCATE **/ if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_SIZE) && (obj_hdl->type == REGULAR_FILE)) { /* Acquire state's fdlock to prevent OPEN upgrade closing the * file descriptor while we use it. */ if (state) { glusterfs_fd = &container_of(state, struct glusterfs_state_fd, state)->glusterfs_fd; PTHREAD_RWLOCK_rdlock(&glusterfs_fd->fdlock); } /* Get a usable file descriptor. Share conflict is only * possible if size is being set. For special files, * handle via handle. */ status = find_fd(&my_fd, obj_hdl, bypass, state, openflags, &has_lock, &closefd, false); if (FSAL_IS_ERROR(status)) goto out; SET_GLUSTER_CREDS(glfs_export, &op_ctx->creds->caller_uid, &op_ctx->creds->caller_gid, op_ctx->creds->caller_glen, op_ctx->creds->caller_garray); retval = glfs_ftruncate(my_fd.glfd, attrib_set->filesize); SET_GLUSTER_CREDS(glfs_export, NULL, NULL, 0, NULL); if (retval != 0) { if (retval != 0) { status = gluster2fsal_error(errno); goto out; } } } if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_MODE)) { FSAL_SET_MASK(mask, GLAPI_SET_ATTR_MODE); buffxstat.buffstat.st_mode = fsal2unix_mode(attrib_set->mode); } if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_OWNER)) { FSAL_SET_MASK(mask, GLAPI_SET_ATTR_UID); buffxstat.buffstat.st_uid = attrib_set->owner; } if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_GROUP)) { FSAL_SET_MASK(mask, GLAPI_SET_ATTR_GID); buffxstat.buffstat.st_gid = attrib_set->group; } if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_ATIME)) { FSAL_SET_MASK(mask, GLAPI_SET_ATTR_ATIME); buffxstat.buffstat.st_atim = attrib_set->atime; } if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_ATIME_SERVER)) { FSAL_SET_MASK(mask, GLAPI_SET_ATTR_ATIME); struct timespec timestamp; retval = clock_gettime(CLOCK_REALTIME, ×tamp); if (retval != 0) { status = gluster2fsal_error(errno); goto out; } buffxstat.buffstat.st_atim = timestamp; } /* try to look at glfs_futimens() instead as done in vfs */ if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_MTIME)) { FSAL_SET_MASK(mask, GLAPI_SET_ATTR_MTIME); buffxstat.buffstat.st_mtim = attrib_set->mtime; } if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_MTIME_SERVER)) { FSAL_SET_MASK(mask, GLAPI_SET_ATTR_MTIME); struct timespec timestamp; retval = clock_gettime(CLOCK_REALTIME, ×tamp); if (retval != 0) { status = gluster2fsal_error(retval); goto out; } buffxstat.buffstat.st_mtim = timestamp; } /** @todo: Check for attributes not supported and return */ /* EATTRNOTSUPP error. */ if (NFSv4_ACL_SUPPORT) { if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_ACL)) { if (obj_hdl->type == DIRECTORY) buffxstat.is_dir = true; else buffxstat.is_dir = false; FSAL_SET_MASK(attr_valid, XATTR_ACL); status = glusterfs_process_acl(glfs_export->gl_fs->fs, myself->glhandle, attrib_set, &buffxstat); if (FSAL_IS_ERROR(status)) goto out; /* setting the ACL will set the */ /* mode-bits too if not already passed */ FSAL_SET_MASK(mask, GLAPI_SET_ATTR_MODE); } } else if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_ACL)) { status = fsalstat(ERR_FSAL_ATTRNOTSUPP, 0); goto out; } SET_GLUSTER_CREDS(glfs_export, &op_ctx->creds->caller_uid, &op_ctx->creds->caller_gid, op_ctx->creds->caller_glen, op_ctx->creds->caller_garray); /* If any stat changed, indicate that */ if (mask != 0) FSAL_SET_MASK(attr_valid, XATTR_STAT); if (FSAL_TEST_MASK(attr_valid, XATTR_STAT)) { /* Only if there is any change in attrs send them down to fs */ /** @todo: instead use glfs_fsetattr().... looks like there is * fix needed in there..it doesn't convert the mask flags * to corresponding gluster flags. */ retval = glfs_h_setattrs(glfs_export->gl_fs->fs, myself->glhandle, &buffxstat.buffstat, mask); if (retval != 0) { status = gluster2fsal_error(errno); goto creds; } } if (FSAL_TEST_MASK(attr_valid, XATTR_ACL)) status = glusterfs_set_acl(glfs_export, myself, &buffxstat); if (FSAL_IS_ERROR(status)) { LogDebug(COMPONENT_FSAL, "setting ACL failed"); } creds: SET_GLUSTER_CREDS(glfs_export, NULL, NULL, 0, NULL); out: if (FSAL_IS_ERROR(status)) { LogCrit(COMPONENT_FSAL, "setattrs failed with error %s", strerror(status.minor)); } if (glusterfs_fd) PTHREAD_RWLOCK_unlock(&glusterfs_fd->fdlock); if (closefd) glusterfs_close_my_fd(&my_fd); if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /* close2 */ static fsal_status_t glusterfs_close2(struct fsal_obj_handle *obj_hdl, struct state_t *state) { struct glusterfs_handle *myself = NULL; struct glusterfs_fd *my_fd = &container_of(state, struct glusterfs_state_fd, state)->glusterfs_fd; myself = container_of(obj_hdl, struct glusterfs_handle, handle); if (state->state_type == STATE_TYPE_SHARE || state->state_type == STATE_TYPE_NLM_SHARE || state->state_type == STATE_TYPE_9P_FID) { /* This is a share state, we must update the share counters */ /* This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->obj_lock); update_share_counters(&myself->share, my_fd->openflags, FSAL_O_CLOSED); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); } return glusterfs_close_my_fd(my_fd); } /** * @brief Implements GLUSTER FSAL objectoperation list_ext_attrs */ /* static fsal_status_t list_ext_attrs(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, unsigned int cookie, fsal_xattrent_t * xattrs_tab, unsigned int xattrs_tabsize, unsigned int *p_nb_returned, int *end_of_list) { return fsalstat(ERR_FSAL_NOTSUPP, 0); } */ /** * @brief Implements GLUSTER FSAL objectoperation getextattr_id_by_name */ /* static fsal_status_t getextattr_id_by_name(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, const char *xattr_name, unsigned int *pxattr_id) { return fsalstat(ERR_FSAL_NOTSUPP, 0); } */ /** * @brief Implements GLUSTER FSAL objectoperation getextattr_value_by_name */ /* static fsal_status_t getextattr_value_by_name(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, const char *xattr_name, caddr_t buffer_addr, size_t buffer_size, size_t * p_output_size) { return fsalstat(ERR_FSAL_NOTSUPP, 0); } */ /** * @brief Implements GLUSTER FSAL objectoperation getextattr_value_by_id */ /* static fsal_status_t getextattr_value_by_id(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, unsigned int xattr_id, caddr_t buffer_addr, size_t buffer_size, size_t *p_output_size) { return fsalstat(ERR_FSAL_NOTSUPP, 0); } */ /** * @brief Implements GLUSTER FSAL objectoperation setextattr_value */ /* static fsal_status_t setextattr_value(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, const char *xattr_name, caddr_t buffer_addr, size_t buffer_size, int create) { return fsalstat(ERR_FSAL_NOTSUPP, 0); } */ /** * @brief Implements GLUSTER FSAL objectoperation setextattr_value_by_id */ /* static fsal_status_t setextattr_value_by_id(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, unsigned int xattr_id, caddr_t buffer_addr, size_t buffer_size) { return fsalstat(ERR_FSAL_NOTSUPP, 0); } */ /** * @brief Implements GLUSTER FSAL objectoperation getextattr_attrs */ /* static fsal_status_t getextattr_attrs(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, unsigned int xattr_id, struct attrlist* p_attrs) { return fsalstat(ERR_FSAL_NOTSUPP, 0); } */ /** * @brief Implements GLUSTER FSAL objectoperation remove_extattr_by_id */ /* static fsal_status_t remove_extattr_by_id(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, unsigned int xattr_id) { return fsalstat(ERR_FSAL_NOTSUPP, 0); } */ /** * @brief Implements GLUSTER FSAL objectoperation remove_extattr_by_name */ /* static fsal_status_t remove_extattr_by_name(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, const char *xattr_name) { return fsalstat(ERR_FSAL_NOTSUPP, 0); } */ /** * @brief Implements GLUSTER FSAL objectoperation handle_to_wire */ static fsal_status_t handle_to_wire(const struct fsal_obj_handle *obj_hdl, fsal_digesttype_t output_type, struct gsh_buffdesc *fh_desc) { fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; size_t fh_size; struct glusterfs_handle *objhandle; #ifdef GLTIMING struct timespec s_time, e_time; now(&s_time); #endif if (!fh_desc) return fsalstat(ERR_FSAL_FAULT, 0); objhandle = container_of(obj_hdl, struct glusterfs_handle, handle); switch (output_type) { case FSAL_DIGEST_NFSV3: case FSAL_DIGEST_NFSV4: fh_size = GLAPI_HANDLE_LENGTH; if (fh_desc->len < fh_size) { LogMajor(COMPONENT_FSAL, "Space too small for handle. need %zu, have %zu", fh_size, fh_desc->len); status.major = ERR_FSAL_TOOSMALL; goto out; } memcpy(fh_desc->addr, objhandle->globjhdl, fh_size); break; default: status.major = ERR_FSAL_SERVERFAULT; goto out; } fh_desc->len = fh_size; out: #ifdef GLTIMING now(&e_time); latency_update(&s_time, &e_time, lat_handle_to_wire); #endif return status; } /** * @brief Implements GLUSTER FSAL objectoperation handle_to_key */ static void handle_to_key(struct fsal_obj_handle *obj_hdl, struct gsh_buffdesc *fh_desc) { struct glusterfs_handle *objhandle; #ifdef GLTIMING struct timespec s_time, e_time; now(&s_time); #endif objhandle = container_of(obj_hdl, struct glusterfs_handle, handle); fh_desc->addr = objhandle->globjhdl; fh_desc->len = GLAPI_HANDLE_LENGTH; #ifdef GLTIMING now(&e_time); latency_update(&s_time, &e_time, lat_handle_to_key); #endif } /** * @brief Registers GLUSTER FSAL objectoperation vector */ void handle_ops_init(struct fsal_obj_ops *ops) { ops->release = handle_release; ops->merge = glusterfs_merge; ops->lookup = lookup; ops->mkdir = makedir; ops->mknode = makenode; ops->readdir = read_dirents; ops->symlink = makesymlink; ops->readlink = readsymlink; ops->getattrs = getattrs; ops->link = linkfile; ops->rename = renamefile; ops->unlink = file_unlink; ops->handle_to_wire = handle_to_wire; ops->handle_to_key = handle_to_key; ops->close = file_close; /* fops with OpenTracking (multi-fd) enabled */ ops->open2 = glusterfs_open2; ops->reopen2 = glusterfs_reopen2; ops->read2 = glusterfs_read2; ops->write2 = glusterfs_write2; ops->commit2 = glusterfs_commit2; ops->lock_op2 = glusterfs_lock_op2; ops->setattr2 = glusterfs_setattr2; ops->close2 = glusterfs_close2; /* pNFS related ops */ handle_ops_pnfs(ops); } nfs-ganesha-2.6.0/src/FSAL/FSAL_GLUSTER/main.c000066400000000000000000000112041324272410200202420ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Red Hat Inc., 2013 * Author: Anand Subramanian anands@redhat.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ /** * @file main.c * @author Anand Subramanian * * @author Shyamsundar R * * @brief Module core functions for FSAL_GLUSTER functionality, init etc. * */ #include "fsal.h" #include "FSAL/fsal_init.h" #include "gluster_internal.h" #include "FSAL/fsal_commonlib.h" /* GLUSTERFS FSAL module private storage */ const char glfsal_name[] = "GLUSTER"; /* filesystem info for GLUSTERFS */ static struct fsal_staticfsinfo_t default_gluster_info = { .maxfilesize = UINT64_MAX, .maxlink = _POSIX_LINK_MAX, .maxnamelen = 1024, .maxpathlen = 1024, .no_trunc = true, .chown_restricted = true, .case_insensitive = false, .case_preserving = true, .link_support = true, .symlink_support = true, .lock_support = true, .lock_support_async_block = false, .named_attr = true, .unique_handles = true, .lease_time = {10, 0}, .acl_support = FSAL_ACLSUPPORT_ALLOW | FSAL_ACLSUPPORT_DENY, .cansettime = true, .homogenous = true, .supported_attrs = GLUSTERFS_SUPPORTED_ATTRIBUTES, .maxread = 0, .maxwrite = 0, .umask = 0, .auth_exportpath_xdev = false, .xattr_access_rights = 0400, /* root=RW, owner=R */ .pnfs_mds = false, .pnfs_ds = true, .link_supports_permission_checks = true, }; static struct config_item glfs_params[] = { CONF_ITEM_BOOL("pnfs_mds", false, fsal_staticfsinfo_t, pnfs_mds), CONF_ITEM_BOOL("pnfs_ds", true, fsal_staticfsinfo_t, pnfs_ds), CONFIG_EOL }; struct config_block glfs_param = { .dbus_interface_name = "org.ganesha.nfsd.config.fsal.gluster", .blk_desc.name = "GLUSTER", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = noop_conf_init, .blk_desc.u.blk.params = glfs_params, .blk_desc.u.blk.commit = noop_conf_commit }; static fsal_status_t init_config(struct fsal_module *fsal_hdl, config_file_t config_struct, struct config_error_type *err_type) { struct glusterfs_fsal_module *glfsal_module = container_of(fsal_hdl, struct glusterfs_fsal_module, fsal); glfsal_module->fs_info = default_gluster_info; (void) load_config_from_parse(config_struct, &glfs_param, &glfsal_module->fs_info, true, err_type); /* * Global block is not mandatory, so evenif * it is not parsed correctly, don't consider * that as an error */ if (!config_error_is_harmless(err_type)) LogDebug(COMPONENT_FSAL, "Parsing Export Block failed"); display_fsinfo(&glfsal_module->fs_info); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* Module methods */ MODULE_INIT void glusterfs_init(void) { struct fsal_module *myself = &GlusterFS.fsal; /* register_fsal seems to expect zeroed memory. */ memset(myself, 0, sizeof(*myself)); if (register_fsal(myself, glfsal_name, FSAL_MAJOR_VERSION, FSAL_MINOR_VERSION, FSAL_ID_GLUSTER) != 0) { LogCrit(COMPONENT_FSAL, "Gluster FSAL module failed to register."); return; } /* set up module operations */ myself->m_ops.create_export = glusterfs_create_export; /* setup global handle internals */ myself->m_ops.init_config = init_config; /* * Following inits needed for pNFS support * get device info will used by pnfs meta data server */ myself->m_ops.getdeviceinfo = getdeviceinfo; myself->m_ops.fsal_pnfs_ds_ops = pnfs_ds_ops_init; PTHREAD_MUTEX_init(&GlusterFS.lock, NULL); glist_init(&GlusterFS.fs_obj); LogDebug(COMPONENT_FSAL, "FSAL Gluster initialized"); } MODULE_FINI void glusterfs_unload(void) { if (unregister_fsal(&GlusterFS.fsal) != 0) { LogCrit(COMPONENT_FSAL, "FSAL Gluster unable to unload. Dying ..."); return; } /* All the shares should have been unexported */ if (!glist_empty(&GlusterFS.fs_obj)) { LogWarn(COMPONENT_FSAL, "FSAL Gluster still contains active shares."); } PTHREAD_MUTEX_destroy(&GlusterFS.lock); LogDebug(COMPONENT_FSAL, "FSAL Gluster unloaded"); } nfs-ganesha-2.6.0/src/FSAL/FSAL_GLUSTER/mds.c000066400000000000000000000444341324272410200201140ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Red Hat Inc., 2014 * Author: Jiffin Tony Thottan jthottan@redhat.com * Anand Subramanian anands@redhat.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ #include #include "fsal.h" #include "fsal_types.h" #include "fsal_api.h" #include "fsal_up.h" #include "gsh_rpc.h" #include "gluster_internal.h" #include "FSAL/fsal_commonlib.h" #include "FSAL/fsal_config.h" #include "fsal_convert.h" #include "pnfs_utils.h" #include "nfs_exports.h" #include #include #include #define get16bits(d) (*((const uint16_t *) (d))) #define MAX_DS_COUNT 100 /** * @brief Get layout types supported by export * * We just return a pointer to the single type and set the count to 1. * * @param[in] export_pub Public export handle * @param[out] count Number of layout types in array * @param[out] types Static array of layout types that must not be * freed or modified and must not be dereferenced * after export reference is relinquished */ static void fs_layouttypes(struct fsal_export *export_pub, int32_t *count, const layouttype4 **types) { /* Only supported layout type is file */ static const layouttype4 supported_layout_type = LAYOUT4_NFSV4_1_FILES; *types = &supported_layout_type; *count = 1; } /** * @brief Get layout block size for export * * This function just returns the Gluster default. * * @param[in] export_pub Public export handle * * @return 4 MB. */ static uint32_t fs_layout_blocksize(struct fsal_export *export_pub) { return 0x400000; } /** * @brief Maximum number of segments we will use * * Since current clients only support 1, that's what we'll use. * * @param[in] export_pub Public export handle * * @return 1 */ static uint32_t fs_maximum_segments(struct fsal_export *export_pub) { return 1; } /** * @brief Size of the buffer needed for a loc_body * * Just a handle plus a bit. * * @param[in] export_pub Public export handle * * @return Size of the buffer needed for a loc_body */ static size_t fs_loc_body_size(struct fsal_export *export_pub) { return 0x100; } /** * @brief Size of the buffer needed for a ds_addr * * This one is huge, due to the striping pattern. * * @param[in] export_pub Public export handle * * @return Size of the buffer needed for a ds_addr */ size_t fs_da_addr_size(struct fsal_module *fsal_hdl) { return 0x1400; } int glfs_get_ds_addr(struct glfs *fs, struct glfs_object *object, uint32_t *ds_addr); /** * @brief Grant a layout segment. * * Grants whole layout of the file requested. * * @param[in] obj_pub Public object handle * @param[in] req_ctx Request context * @param[out] loc_body An XDR stream to which the FSAL must encode * the layout specific portion of the granted * layout segment. * @param[in] arg Input arguments of the function * @param[in,out] res In/out and output arguments of the function * * @return Valid error codes in RFC 5661, pp. 366-7. */ static nfsstat4 pnfs_layout_get(struct fsal_obj_handle *obj_pub, struct req_op_context *req_ctx, XDR *loc_body, const struct fsal_layoutget_arg *arg, struct fsal_layoutget_res *res) { struct glusterfs_export *export = container_of(req_ctx->fsal_export, struct glusterfs_export, export); struct glusterfs_handle *handle = container_of(obj_pub, struct glusterfs_handle, handle); int rc = 0; /* Structure containing the storage parameters of the file within glusterfs. */ struct glfs_file_layout file_layout; /* Utility parameter */ nfl_util4 util = 0; /* Stores Data server address */ struct pnfs_deviceid deviceid = DEVICE_ID_INIT_ZERO(FSAL_ID_GLUSTER); nfsstat4 nfs_status = NFS4_OK; /* Descriptor for DS handle */ struct gsh_buffdesc ds_desc; /* DS wire handle send to client */ struct glfs_ds_wire ds_wire; /* Supports only LAYOUT4_NFSV4_1_FILES layouts */ if (arg->type != LAYOUT4_NFSV4_1_FILES) { LogMajor(COMPONENT_PNFS, "Unsupported layout type: %x", arg->type); return NFS4ERR_UNKNOWN_LAYOUTTYPE; } memset(&file_layout, 0, sizeof(struct glfs_file_layout)); /** * Currently whole file is given as file layout, * * Stripe type is dense which is supported right now. * Stripe length is max possible length of file that * can be accessed by the client to perform a read or * write. */ file_layout.stripe_type = NFL4_UFLG_DENSE; file_layout.stripe_length = 0x100000; util |= file_layout.stripe_type | file_layout.stripe_length; /* @todo need to handle IPv6 here */ rc = glfs_get_ds_addr(export->gl_fs->fs, handle->glhandle, &deviceid.device_id4); if (rc) { LogMajor(COMPONENT_PNFS, "Invalid hostname for DS"); return NFS4ERR_INVAL; } /** @todo: When more than one client tries access the same layout * for the write operation, then last write will overwrite * for the write operation, then last write will overwrite * the previous ones, the MDS should intelligently deal * those scenarios */ /* We return exactly one wirehandle, filling in the necessary * information for the DS server to speak to the gluster bricks * For this, wire handle stores gfid and file layout */ rc = glfs_h_extract_handle(handle->glhandle, ds_wire.gfid, GFAPI_HANDLE_LENGTH); if (rc < 0) { rc = errno; LogMajor(COMPONENT_PNFS, "Invalid glfs_object"); return posix2nfs4_error(rc); } ds_wire.layout = file_layout; ds_desc.addr = &ds_wire; ds_desc.len = sizeof(struct glfs_ds_wire); nfs_status = FSAL_encode_file_layout(loc_body, &deviceid, util, 0, 0, &req_ctx->ctx_export->export_id, 1, &ds_desc); if (nfs_status) { LogMajor(COMPONENT_PNFS, "Failed to encode nfsv4_1_file_layout."); goto out; } /* We grant only one segment, and we want it back * when the file is closed. */ res->return_on_close = true; res->last_segment = true; out: return nfs_status; } /** * @brief Potentially return one layout segment * * Since we don't make any reservations, in this version, or get any * pins to release, always succeed * * @param[in] obj_pub Public object handle * @param[in] req_ctx Request context * @param[in] lrf_body Nothing for us * @param[in] arg Input arguments of the function * * @return Valid error codes in RFC 5661, p. 367. */ static nfsstat4 pnfs_layout_return(struct fsal_obj_handle *obj_pub, struct req_op_context *req_ctx, XDR *lrf_body, const struct fsal_layoutreturn_arg *arg) { if (arg->lo_type != LAYOUT4_NFSV4_1_FILES) { LogDebug(COMPONENT_PNFS, "Unsupported layout type: %x", arg->lo_type); return NFS4ERR_UNKNOWN_LAYOUTTYPE; } return NFS4_OK; } /** * @brief Commit a segment of a layout * * Update the size and time for a file accessed through a layout. * * @param[in] obj_pub Public object handle * @param[in] req_ctx Request context * @param[in] lou_body An XDR stream containing the layout * type-specific portion of the LAYOUTCOMMIT * arguments. * @param[in] arg Input arguments of the function * @param[in,out] res In/out and output arguments of the function * * @return Valid error codes in RFC 5661, p. 366. */ static nfsstat4 pnfs_layout_commit(struct fsal_obj_handle *obj_pub, struct req_op_context *req_ctx, XDR *lou_body, const struct fsal_layoutcommit_arg *arg, struct fsal_layoutcommit_res *res) { /* Old stat, so we don't truncate file or reverse time */ struct stat old_stat; /* new stat to set time and size */ struct stat new_stat; struct glusterfs_export *glfs_export = container_of(op_ctx->fsal_export, struct glusterfs_export, export); struct glusterfs_handle *objhandle = container_of(obj_pub, struct glusterfs_handle, handle); /* Mask to determine exactly what gets set */ int mask = 0; int rc = 0; fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; if (arg->type != LAYOUT4_NFSV4_1_FILES) { LogMajor(COMPONENT_PNFS, "Unsupported layout type: %x", arg->type); return NFS4ERR_UNKNOWN_LAYOUTTYPE; } /* Gets previous status of file in the MDS */ rc = glfs_h_stat(glfs_export->gl_fs->fs, objhandle->glhandle, &old_stat); if (rc != 0) { LogMajor(COMPONENT_PNFS, "Commit layout, stat unsucessfully completed"); return NFS4ERR_INVAL; } memset(&new_stat, 0, sizeof(struct stat)); /* Set the new attributes for the file if it is changed */ if (arg->new_offset) { if (old_stat.st_size < arg->last_write + 1) { new_stat.st_size = arg->last_write + 1; res->size_supplied = true; res->new_size = arg->last_write + 1; rc = glfs_h_truncate(glfs_export->gl_fs->fs, objhandle->glhandle, res->new_size); if (rc != 0) { LogMajor(COMPONENT_PNFS, "Commit layout, size changed unsucessfully completed"); return NFS4ERR_INVAL; } } } if ((arg->time_changed) && (arg->new_time.seconds > old_stat.st_mtime)) new_stat.st_mtime = arg->new_time.seconds; else new_stat.st_mtime = time(NULL); mask |= GLAPI_SET_ATTR_MTIME; SET_GLUSTER_CREDS(glfs_export, &op_ctx->creds->caller_uid, &op_ctx->creds->caller_gid, op_ctx->creds->caller_glen, op_ctx->creds->caller_garray); rc = glfs_h_setattrs(glfs_export->gl_fs->fs, objhandle->glhandle, &new_stat, mask); SET_GLUSTER_CREDS(glfs_export, NULL, NULL, 0, NULL); if ((rc != 0) || (status.major != ERR_FSAL_NO_ERROR)) { LogMajor(COMPONENT_PNFS, "commit layout, setattr unsucessflly completed"); return NFS4ERR_INVAL; } res->commit_done = true; return NFS4_OK; } /** * @brief Describes the DS information for the client * * @param[in] export_pub Public export handle * @param[out] da_addr_body Stream we write the result to * @param[in] type Type of layout that gave the device * @param[in] deviceid The device to look up * * @return Valid error codes in RFC 5661, p. 365. */ nfsstat4 getdeviceinfo(struct fsal_module *fsal_hdl, XDR *da_addr_body, const layouttype4 type, const struct pnfs_deviceid *deviceid) { nfsstat4 nfs_status = 0; /* Stores IP address of DS */ fsal_multipath_member_t host; /* Entire file layout will be situated inside ONE DS * And whole file is provided to the DS, so the starting * index for that file is zero */ unsigned int num_ds = 1; uint32_t stripes = 1; uint32_t stripe_ind = 0; if (type != LAYOUT4_NFSV4_1_FILES) { LogMajor(COMPONENT_PNFS, "Unsupported layout type: %x", type); return NFS4ERR_UNKNOWN_LAYOUTTYPE; } if (!inline_xdr_u_int32_t(da_addr_body, &stripes)) { LogMajor(COMPONENT_PNFS, "Failed to encode length of stripe_indices array: %" PRIu32 ".", stripes); return NFS4ERR_SERVERFAULT; } if (!inline_xdr_u_int32_t(da_addr_body, &stripe_ind)) { LogMajor(COMPONENT_PNFS, "Failed to encode ds for the stripe: %" PRIu32 ".", stripe_ind); return NFS4ERR_SERVERFAULT; } if (!inline_xdr_u_int32_t(da_addr_body, &num_ds)) { LogMajor(COMPONENT_PNFS, "Failed to encode length of multipath_ds_list array: %u", num_ds); return NFS4ERR_SERVERFAULT; } memset(&host, 0, sizeof(fsal_multipath_member_t)); host.addr = ntohl(deviceid->device_id4); host.port = 2049; host.proto = 6; nfs_status = FSAL_encode_v4_multipath(da_addr_body, 1, &host); if (nfs_status != NFS4_OK) { LogMajor(COMPONENT_PNFS, "Failed to encode data server address"); return nfs_status; } /** @todo: Here information about Data-Server where file resides * is only send from MDS.If that Data-Server is down then * read or write will performed through MDS. * Instead should we send the information about all * the available data-servers, so that these fops will * always performed through Data-Servers. * (Like in replicated volume contains more than ONE DS) */ return NFS4_OK; } /** * @brief Get list of available devices * * We do not support listing devices and just set EOF without doing * anything. * * @param[in] export_pub Export handle * @param[in] type Type of layout to get devices for * @param[in] cb Function taking device ID halves * @param[in,out] res In/out and output arguments of the function * * @return Valid error codes in RFC 5661, pp. 365-6. */ static nfsstat4 getdevicelist(struct fsal_export *export_pub, layouttype4 type, void *opaque, bool (*cb)(void *opaque, const uint64_t id), struct fsal_getdevicelist_res *res) { res->eof = true; return NFS4_OK; } void handle_ops_pnfs(struct fsal_obj_ops *ops) { ops->layoutget = pnfs_layout_get; ops->layoutreturn = pnfs_layout_return; ops->layoutcommit = pnfs_layout_commit; } void fsal_ops_pnfs(struct fsal_ops *ops) { ops->getdeviceinfo = getdeviceinfo; ops->fs_da_addr_size = fs_da_addr_size; } void export_ops_pnfs(struct export_ops *ops) { ops->getdevicelist = getdevicelist; ops->fs_layouttypes = fs_layouttypes; ops->fs_layout_blocksize = fs_layout_blocksize; ops->fs_maximum_segments = fs_maximum_segments; ops->fs_loc_body_size = fs_loc_body_size; } /* * * Calculates a hash value for a given string buffer */ uint32_t superfasthash(const unsigned char *data, uint32_t len) { uint32_t hash = len, tmp; int32_t rem; rem = len & 3; len >>= 2; /* Main loop */ for (; len > 0; len--) { hash += get16bits(data); tmp = (get16bits(data+2) << 11) ^ hash; hash = (hash << 16) ^ tmp; data += 2*sizeof(uint16_t); hash += hash >> 11; } /* Handle end cases */ switch (rem) { case 3: hash += get16bits(data); hash ^= hash << 16; hash ^= data[sizeof(uint16_t)] << 18; hash += hash >> 11; break; case 2: hash += get16bits(data); hash ^= hash << 11; hash += hash >> 17; break; case 1: hash += *data; hash ^= hash << 10; hash += hash >> 1; } /* Force "avalanching" of final 127 bits */ hash ^= hash << 3; hash += hash >> 5; hash ^= hash << 4; hash += hash >> 17; hash ^= hash << 25; hash += hash >> 6; return hash; } /** * It will extract hostname from pathinfo.PATH_INFO_KEYS gives * details about all the servers and path in that server where * file resides. * First it selects the DS based on distributed hashing, then * with the help of some basic string manipulations, the hostname * can be fetched from the pathinfo * * Returns zero and valid hostname on success */ int select_ds(struct glfs_object *object, char *pathinfo, char *hostname, size_t size) { /* Represents starting of each server in the list*/ const char posix[10] = "POSIX"; /* Array of pathinfo of available dses */ char *ds_path_info[MAX_DS_COUNT]; /* Key for hashing */ unsigned char key[16]; /* Starting of first brick path in the pathinfo */ char *tmp = NULL; /* Stores starting of hostname */ char *start = NULL; /* Stores ending of hostname */ char *end = NULL; int ret = -1; int i = 0; /* counts no of available ds */ int no_of_ds = 0; if (!pathinfo || !size) goto out; tmp = pathinfo; while ((tmp = strstr(tmp, posix))) { ds_path_info[no_of_ds] = tmp; tmp++; no_of_ds++; /* * * If no of dses reaches maxmium count, then * perform load balance on current list */ if (no_of_ds == MAX_DS_COUNT) break; } if (no_of_ds == 0) { LogCrit(COMPONENT_PNFS, "Invalid pathinfo(%s) attribute found while selecting DS.", pathinfo); goto out; } ret = glfs_h_extract_handle(object, key, GFAPI_HANDLE_LENGTH); if (ret < 0) goto out; /* Pick DS from the list */ if (no_of_ds == 1) ret = 0; else ret = superfasthash(key, 16) % no_of_ds; start = strchr(ds_path_info[ret], ':'); if (!start) goto out; end = start + 1; end = strchr(end, ':'); if (start == end) goto out; memset(hostname, 0, size); while (++start != end) hostname[i++] = *start; ret = 0; LogDebug(COMPONENT_PNFS, "hostname %s", hostname); out: return ret; } /* * The data server address will be send from here * * The information about the first server present * in the PATH_INFO_KEY will be returned, since * entire file is consistent over the servers * (Striped volumes are not considered right now) * * On success, returns zero with ip address of * the server will be send */ int glfs_get_ds_addr(struct glfs *fs, struct glfs_object *object, uint32_t *ds_addr) { int ret = 0; char pathinfo[1024] = {0, }; char hostname[256] = {0, }; char scratch[SOCK_NAME_MAX] = {0, }; struct addrinfo hints = {0, }; struct addrinfo *res = NULL; const char *pathinfokey = "trusted.glusterfs.pathinfo"; ret = glfs_h_getxattrs(fs, object, pathinfokey, pathinfo, sizeof(pathinfo)); LogDebug(COMPONENT_PNFS, "pathinfo %s", pathinfo); ret = select_ds(object, pathinfo, hostname, sizeof(hostname)); if (ret) { LogMajor(COMPONENT_PNFS, "No DS found"); goto out; } hints.ai_socktype = SOCK_STREAM; hints.ai_family = AF_INET; ret = getaddrinfo(hostname, NULL, &hints, &res); /* we trust getaddrinfo() never returns EAI_AGAIN! */ if (ret != 0) { *ds_addr = 0; LogMajor(COMPONENT_PNFS, "error %s\n", gai_strerror(ret)); goto out; } sprint_sockip((sockaddr_t *)res->ai_addr, scratch, sizeof(scratch)); LogDebug(COMPONENT_PNFS, "ip address : %s", scratch); *ds_addr = ((struct sockaddr_in *)(res->ai_addr))->sin_addr.s_addr; out: freeaddrinfo(res); return ret; } nfs-ganesha-2.6.0/src/FSAL/FSAL_GLUSTER/posix_acls.c000066400000000000000000000446351324272410200215000ustar00rootroot00000000000000/* * posix_acls.c * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Red Hat Inc., 2015 * Author: Niels de Vos * Jiffin Tony Thottan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * Conversion routines for fsal_acl <-> POSIX ACl * * Routines based on the description from an Internet Draft that has also been * used for the implementation of the conversion in the Linux kernel * NFS-server. * * Title: Mapping Between NFSv4 and Posix Draft ACLs * Authors: Marius Aamodt Eriksen & J. Bruce Fields * URL: http://tools.ietf.org/html/draft-ietf-nfsv4-acl-mapping-05 */ #include "posix_acls.h" /* Checks whether ACE belongs to effective acl (ACCESS TYPE) */ bool is_ace_valid_for_effective_acl_entry(fsal_ace_t *ace) { bool ret; if (IS_FSAL_ACE_HAS_INHERITANCE_FLAGS(*ace)) { if (IS_FSAL_ACE_APPLICABLE_FOR_BOTH_ACL(*ace)) ret = true; else ret = false; } else ret = true; return ret; } /* Checks whether ACE belongs to inherited acl (DEFAULT TYPE) */ bool is_ace_valid_for_inherited_acl_entry(fsal_ace_t *ace) { if (IS_FSAL_ACE_APPLICABLE_FOR_BOTH_ACL(*ace) || IS_FSAL_ACE_APPLICABLE_ONLY_FOR_INHERITED_ACL(*ace)) return true; else return false; } /* * Check whether given perm(ACL_READ, ACL_WRITE or ACL_EXECUTE) is allowed for * a permset, it depends on given ace and permset of @EVERYONE entry. */ bool isallow(fsal_ace_t *ace, acl_permset_t everyone, acl_perm_t perm) { bool ret = acl_get_perm(everyone, perm); switch (perm) { case ACL_READ: ret |= IS_FSAL_ACE_READ_DATA(*ace); break; case ACL_WRITE: ret |= IS_FSAL_ACE_WRITE_DATA(*ace); break; case ACL_EXECUTE: ret |= IS_FSAL_ACE_EXECUTE(*ace); break; } return ret; } /* * Check whether given perm(ACL_READ, ACL_WRITE or ACL_EXECUTE) is denied for a * permset, it depends on permsets of deny entry of the acl and @EVERYONE entry. */ bool isdeny(acl_permset_t deny, acl_permset_t everyone, acl_perm_t perm) { return acl_get_perm(deny, perm) || acl_get_perm(everyone, perm); } /* Returns no of possible fsal_ace entries from a given posix_acl */ int ace_count(acl_t acl) { int ret; ret = acl_entries(acl); if (ret < 0) return 0; /* Mask is not converted to an ace entry */ if (find_entry(acl, ACL_MASK, 0)) ret--; return ret; } /* * It traverse entire list of entries for a posix acl and finds ACL entry which * corresponds to a given tag and id * * On success , it returns acl entry otherwise it returns NULL */ acl_entry_t find_entry(acl_t acl, acl_tag_t tag, unsigned int id) { acl_entry_t entry; acl_tag_t entryTag; int ent, ret; if (!acl) return NULL; for (ent = ACL_FIRST_ENTRY; ; ent = ACL_NEXT_ENTRY) { ret = acl_get_entry(acl, ent, &entry); if (ret == -1) { LogWarn(COMPONENT_FSAL, "acl_get entry failed errno %d", errno); } if (ret == 0 || ret == -1) return NULL; if (acl_get_tag_type(entry, &entryTag) == -1) { LogWarn(COMPONENT_FSAL, "No entry tag for ACL Entry"); continue; } if (tag == entryTag) { if (tag == ACL_USER || tag == ACL_GROUP) if (id != *(unsigned int *) acl_get_qualifier(entry)) continue; break; } } return entry; } /* * It tries to find out whether an entry is present in posix acl for the given * (tag, id) tuple and returns it. If not , it will create a new entry for * given (tag, id). * * On success , it returns acl entry otherwise it returns NULL */ acl_entry_t get_entry(acl_t acl, acl_tag_t tag, unsigned int id) { acl_entry_t entry; int ret; if (!acl) return NULL; entry = find_entry(acl, tag, id); if (!entry) { ret = acl_create_entry(&acl, &entry); if (ret) { LogMajor(COMPONENT_FSAL, "Cannot create entry"); return NULL; } ret = acl_set_tag_type(entry, tag); if (ret) LogWarn(COMPONENT_FSAL, "Cannot set tag for Entry"); ret = acl_set_qualifier(entry, &id); } return entry; } /* * @brief convert POSIX ACL into an equivalent FSAL ACL * * @param[in] p_posixacl POSIX ACL * @param[in] is_dir Represents file/directory * @param[in] is_inherit Represents type of ace entry * @param[in] ace Stores the starting of fsal_acl_t * @param[out] ace Stores last ace entry in fsal_acl_t * * @returns no of entries on success and -1 on failure */ int posix_acl_2_fsal_acl(acl_t p_posixacl, bool is_dir, bool is_inherit, fsal_ace_t **ace) { int ret = 0, ent, d_ent, total = 0; fsal_ace_t *pace_deny = NULL, *pace_allow = NULL; acl_t dup_acl; acl_entry_t entry, mask, other, d_entry; acl_tag_t tag; acl_permset_t p_permset; bool readmask = true, readother = false, readcurrent = true; bool writemask = true, writeother = false, writecurrent = true; bool executemask = true, executeother = false, executecurrent = true; if (!p_posixacl) return -1; pace_deny = *ace; pace_allow = (pace_deny + 1); /* Store the mask entry values */ mask = find_entry(p_posixacl, ACL_MASK, 0); if (mask) { ret = acl_get_permset(mask, &p_permset); if (ret) LogWarn(COMPONENT_FSAL, "Cannot retrieve permission set for the Mask Entry"); if (acl_get_perm(p_permset, ACL_READ) == 0) readmask = false; if (acl_get_perm(p_permset, ACL_WRITE) == 0) writemask = false; if (acl_get_perm(p_permset, ACL_EXECUTE) == 0) executemask = false; } other = find_entry(p_posixacl, ACL_OTHER, 0); if (other) { ret = acl_get_permset(other, &p_permset); if (ret) LogWarn(COMPONENT_FSAL, "Cannot retrieve permission set for the Mask Entry"); if (acl_get_perm(p_permset, ACL_READ) == 1) readother = true; if (acl_get_perm(p_permset, ACL_WRITE) == 1) writeother = true; if (acl_get_perm(p_permset, ACL_EXECUTE) == 1) executeother = true; } /* * * Converts each entry in posix acl into fsal_ace by filling type, flag, * perm, iflag, flag and who(uid, gid) appropiately * * Corresponding to each posix acl entry, there is a possiblity of two * fsal_aces, it can either be ALLOW or DENY. The DENY added to list * depending on the permission set set of other entries. * * Here both entries are created for a posix acl entry and filled up * correspondingly. Then at the end unnecessary DENY entries are removed * from the list. */ for (ent = ACL_FIRST_ENTRY; ; ent = ACL_NEXT_ENTRY) { ret = acl_get_entry(p_posixacl, ent, &entry); if (ret == 0 || ret == -1) { LogDebug(COMPONENT_FSAL, "No more ACL entires remaining"); break; } if (acl_get_tag_type(entry, &tag) == -1) { LogWarn(COMPONENT_FSAL, "No entry tag for ACL Entry"); continue; } /* Mask is not converted to a fsal_acl entry , skipping */ if (tag == ACL_MASK) continue; pace_deny->type = FSAL_ACE_TYPE_DENY; pace_allow->type = FSAL_ACE_TYPE_ALLOW; if (is_inherit) pace_allow->flag = pace_deny->flag = FSAL_ACE_FLAG_INHERIT; else pace_allow->flag = pace_deny->flag = 0; /* Finding uid for the fsal_acl entry */ switch (tag) { case ACL_USER_OBJ: pace_allow->who.uid = pace_deny->who.uid = FSAL_ACE_SPECIAL_OWNER; pace_allow->iflag = pace_deny->iflag = FSAL_ACE_IFLAG_SPECIAL_ID; break; case ACL_GROUP_OBJ: pace_allow->who.uid = pace_deny->who.uid = FSAL_ACE_SPECIAL_GROUP; pace_allow->iflag = pace_deny->iflag = FSAL_ACE_IFLAG_SPECIAL_ID; break; case ACL_OTHER: pace_allow->who.uid = pace_deny->who.uid = FSAL_ACE_SPECIAL_EVERYONE; pace_allow->iflag = pace_deny->iflag = FSAL_ACE_IFLAG_SPECIAL_ID; break; case ACL_USER: pace_allow->who.uid = pace_deny->who.uid = *(uid_t *)acl_get_qualifier(entry); break; case ACL_GROUP: pace_allow->who.gid = pace_deny->who.gid = *(gid_t *)acl_get_qualifier(entry); pace_allow->flag = pace_deny->flag |= FSAL_ACE_FLAG_GROUP_ID; break; default: LogWarn(COMPONENT_FSAL, "Invalid tag for the acl"); } /* * * Finding permission set for the fsal_acl ALLOW entry. * Conversion purely is based on * http://tools.ietf.org/html/draft-ietf-nfsv4-acl-mapping-05 * */ /* * * Unconditionally all ALLOW ACL Entry should have these * permissions * */ pace_allow->perm = FSAL_ACE_PERM_SET_DEFAULT; pace_deny->perm = 0; ret = acl_get_permset(entry, &p_permset); if (ret) { LogWarn(COMPONENT_FSAL, "Cannot retrieve permission set for the ACL Entry"); continue; } /* * * Consider Mask bits only for ACL_USER, ACL_GROUP, * ACL_GROUP_OBJ entries * */ if (acl_get_perm(p_permset, ACL_READ)) { if (tag == ACL_USER_OBJ || tag == ACL_OTHER || readmask) pace_allow->perm |= FSAL_ACE_PERM_READ_DATA; } else readcurrent = false; if (acl_get_perm(p_permset, ACL_WRITE)) { if (tag == ACL_USER_OBJ || tag == ACL_OTHER || writemask) pace_allow->perm |= FSAL_ACE_PERM_SET_DEFAULT_WRITE; if (tag == ACL_USER_OBJ) pace_allow->perm |= FSAL_ACE_PERM_SET_OWNER_WRITE; if (is_dir) pace_allow->perm |= FSAL_ACE_PERM_DELETE_CHILD; } else writecurrent = false; if (acl_get_perm(p_permset, ACL_EXECUTE)) { if (tag == ACL_USER_OBJ || tag == ACL_OTHER || executemask) pace_allow->perm |= FSAL_ACE_PERM_EXECUTE; } else executecurrent = false; /* * * Filling up permission set for DENY entries based on ALLOW * entries , if it is applicable. * If the tag is ACL_USER_OBJ or ACL_USER then all later posix * acl entries should be considered. * If the tag is either ACL_GROUP_OBJ or ACL_GROUP then consider * only ACL_OTHER. */ if (tag == ACL_USER_OBJ || tag == ACL_USER) { dup_acl = acl_dup(p_posixacl); /* * Do not consider ACL_MASK entry in the following loop */ acl_delete_entry(dup_acl, mask); if (tag == ACL_USER_OBJ) { d_entry = find_entry(dup_acl, ACL_USER_OBJ, 0); ret = acl_get_entry(dup_acl, ACL_NEXT_ENTRY, &d_entry); } else d_entry = find_entry(dup_acl, ACL_GROUP_OBJ, 0); for (d_ent = ACL_NEXT_ENTRY; ; d_ent = ACL_NEXT_ENTRY) { ret = acl_get_permset(d_entry, &p_permset); if (ret) { LogWarn(COMPONENT_FSAL, "Cannot retrieve permission set"); continue; } if (!readcurrent && acl_get_perm(p_permset, ACL_READ)) pace_deny->perm |= FSAL_ACE_PERM_READ_DATA; if (!writecurrent && acl_get_perm(p_permset, ACL_WRITE)) { pace_deny->perm |= FSAL_ACE_PERM_SET_DEFAULT_WRITE; if (tag == ACL_USER_OBJ) pace_deny->perm |= FSAL_ACE_PERM_SET_OWNER_WRITE; if (is_dir) pace_deny->perm |= FSAL_ACE_PERM_DELETE_CHILD; } if (!executecurrent && acl_get_perm(p_permset, ACL_EXECUTE)) pace_deny->perm |= FSAL_ACE_PERM_EXECUTE; ret = acl_get_entry(dup_acl, d_ent, &d_entry); if (ret == 0 || ret == -1) { LogDebug(COMPONENT_FSAL, "No more ACL entires remaining"); break; } } acl_free(dup_acl); } else if (tag == ACL_GROUP_OBJ || tag == ACL_GROUP) { if (!readcurrent && readother) pace_deny->perm |= FSAL_ACE_PERM_READ_DATA; if (!writecurrent && writeother) { pace_deny->perm |= FSAL_ACE_PERM_SET_DEFAULT_WRITE; if (is_dir) pace_deny->perm |= FSAL_ACE_PERM_DELETE_CHILD; } if (!executecurrent && executeother) pace_deny->perm |= FSAL_ACE_PERM_EXECUTE; } readcurrent = writecurrent = executecurrent = true; /* Removing DENY entries if it is not present */ if (pace_deny->perm == 0) { *pace_deny = *pace_allow; memset(pace_allow, 0, sizeof(fsal_ace_t)); total += 1; pace_deny += 1; pace_allow += 1; } else { total += 2; pace_deny += 2; pace_allow += 2; } } *ace = pace_allow - 1;/* Returns last entry in the list */ return total; /* Returning no of entries in the list */ } /* * @brief convert FSAL ACL into an equivalent POSIX ACL * * @param[in] p_fsalacl FSAL ACL * @param[in] type Represents type of posix acl( ACCESS/DEFAULT ) * * @return acl_t structure */ acl_t fsal_acl_2_posix_acl(fsal_acl_t *p_fsalacl, acl_type_t type) { int ret = 0, i; fsal_ace_t *f_ace; acl_t allow_acl, deny_acl; acl_entry_t a_entry, d_entry; acl_permset_t a_permset, e_a_permset, d_permset, e_d_permset; acl_tag_t tag = -1; char *acl_str; unsigned int id; bool mask = false; bool deny_e_r = false, deny_e_w = false, deny_e_x = false; if (p_fsalacl == NULL) return NULL; /* * * Check whether ace list contains any inherited entries, if not then * returns NULL. * */ if (type == ACL_TYPE_DEFAULT) { for (f_ace = p_fsalacl->aces; f_ace < p_fsalacl->aces + p_fsalacl->naces; f_ace++) { if (is_ace_valid_for_inherited_acl_entry(f_ace)) ret++; } if (ret == 0) return NULL; } /* * FIXME: Always allocating with maximum possible value of acl entries, * there is a possibility of memory leak */ allow_acl = acl_init(p_fsalacl->naces + 1); deny_acl = acl_init(p_fsalacl->naces + 1); /* first convert ACE EVERYONE@ to ACL_OTHER */ ret = acl_create_entry(&allow_acl, &a_entry); if (ret) { LogMajor(COMPONENT_FSAL, "Cannot create entry for other"); return NULL; } ret = acl_set_tag_type(a_entry, ACL_OTHER); if (ret) LogWarn(COMPONENT_FSAL, "Cannot set tag for ACL Entry"); ret = acl_get_permset(a_entry, &e_a_permset); /* * Deny entry for @EVERYONE created only because it will ease the * manipulation of other acl entries. It will be updated only when deny * entry for @EVERYONE is encountered */ ret = acl_create_entry(&deny_acl, &d_entry); if (ret) LogMajor(COMPONENT_FSAL, "Cannot create entry for other"); ret = acl_set_tag_type(d_entry, ACL_OTHER); if (ret) LogWarn(COMPONENT_FSAL, "Cannot set tag for ACL Entry"); ret = acl_get_permset(d_entry, &e_d_permset); for (f_ace = p_fsalacl->aces; f_ace < p_fsalacl->aces + p_fsalacl->naces; f_ace++) { if (IS_FSAL_ACE_SPECIAL_EVERYONE(*f_ace)) { if ((type == ACL_TYPE_ACCESS && !is_ace_valid_for_effective_acl_entry(f_ace)) || (type == ACL_TYPE_DEFAULT && !is_ace_valid_for_inherited_acl_entry(f_ace))) continue; if (IS_FSAL_ACE_DENY(*f_ace)) { if (IS_FSAL_ACE_READ_DATA(*f_ace)) deny_e_r = true; if (IS_FSAL_ACE_WRITE_DATA(*f_ace)) deny_e_w = true; if (IS_FSAL_ACE_EXECUTE(*f_ace)) deny_e_x = true; } else if (IS_FSAL_ACE_ALLOW(*f_ace)) { if (IS_FSAL_ACE_READ_DATA(*f_ace) && !deny_e_r) acl_add_perm(e_a_permset, ACL_READ); if (IS_FSAL_ACE_WRITE_DATA(*f_ace) && !deny_e_w) acl_add_perm(e_a_permset, ACL_WRITE); if (IS_FSAL_ACE_EXECUTE(*f_ace) && !deny_e_x) acl_add_perm(e_a_permset, ACL_EXECUTE); } } } /* * It is mandatory to have acl entries for ACL_USER_OBJ and * ACL_GROUP_OBJ */ ret = acl_create_entry(&allow_acl, &a_entry); if (ret) { LogMajor(COMPONENT_FSAL, "Cannot create entry for other"); return NULL; } ret = acl_set_tag_type(a_entry, ACL_USER_OBJ); if (ret) LogWarn(COMPONENT_FSAL, "Cannot set tag for ACL Entry"); ret = acl_create_entry(&allow_acl, &a_entry); if (ret) { LogMajor(COMPONENT_FSAL, "Cannot create entry for other"); return NULL; } ret = acl_set_tag_type(a_entry, ACL_GROUP_OBJ); if (ret) LogWarn(COMPONENT_FSAL, "Cannot set tag for ACL Entry"); /** @todo: Annoymous users/groups (id = -1) should handle properly */ /* * It uses two posix acl - allow_acl and deny_acl which represents ALLOW * and DENY aces respectively. They are filled according to the order in * fsal_acl list. The allow acl is build based on ALLOW ace, @EVERYONE * ace and deny acl. The permset for allow acl entry is constructed in * such a way that it will contain all permissions(READ,WRITE,EXECUTE) * of ALLOW aces plus EVERYONE which is not denied by the corresponding * deny acl entry * * At last allow_acl is returned and deny_acl is ignored. */ for (f_ace = p_fsalacl->aces; f_ace < p_fsalacl->aces + p_fsalacl->naces; f_ace++) { if ((type == ACL_TYPE_ACCESS && !is_ace_valid_for_effective_acl_entry(f_ace)) || (type == ACL_TYPE_DEFAULT && !is_ace_valid_for_inherited_acl_entry(f_ace))) continue; if (IS_FSAL_ACE_SPECIAL_ID(*f_ace)) { id = 0; if (IS_FSAL_ACE_SPECIAL_OWNER(*f_ace)) tag = ACL_USER_OBJ; if (IS_FSAL_ACE_SPECIAL_GROUP(*f_ace)) tag = ACL_GROUP_OBJ; } else { id = GET_FSAL_ACE_WHO(*f_ace); if (IS_FSAL_ACE_GROUP_ID(*f_ace)) tag = ACL_GROUP; else tag = ACL_USER; /* * Mask entry will be created only if it * contains user or group entry */ mask = true; } if (IS_FSAL_ACE_SPECIAL_EVERYONE(*f_ace)) { if (IS_FSAL_ACE_DENY(*f_ace)) { if (deny_e_r) acl_add_perm(e_d_permset, ACL_READ); if (deny_e_w) acl_add_perm(e_d_permset, ACL_WRITE); if (deny_e_x) acl_add_perm(e_d_permset, ACL_EXECUTE); } continue; } a_entry = get_entry(allow_acl, tag, id); d_entry = get_entry(deny_acl, tag, id); ret = acl_get_permset(d_entry, &d_permset); if (IS_FSAL_ACE_DENY(*f_ace)) { if (IS_FSAL_ACE_READ_DATA(*f_ace)) acl_add_perm(d_permset, ACL_READ); if (IS_FSAL_ACE_WRITE_DATA(*f_ace)) acl_add_perm(d_permset, ACL_WRITE); if (IS_FSAL_ACE_EXECUTE(*f_ace)) acl_add_perm(d_permset, ACL_EXECUTE); } ret = acl_get_permset(a_entry, &a_permset); if (isallow(f_ace, e_a_permset, ACL_READ) && !isdeny(d_permset, e_d_permset, ACL_READ)) acl_add_perm(a_permset, ACL_READ); if (isallow(f_ace, e_a_permset, ACL_WRITE) && !isdeny(d_permset, e_d_permset, ACL_WRITE)) acl_add_perm(a_permset, ACL_WRITE); if (isallow(f_ace, e_a_permset, ACL_EXECUTE) && !isdeny(d_permset, e_d_permset, ACL_EXECUTE)) acl_add_perm(a_permset, ACL_EXECUTE); } if (mask) { ret = acl_calc_mask(&allow_acl); if (ret) LogWarn(COMPONENT_FSAL, "Cannot calculate mask for posix"); } /* A valid acl_t should have only one entry for ACL_USER_OBJ, * ACL_GROUP_OBJ, ACL_OTHER and ACL_MASK is required only if * ACL_USER or ACL_GROUP exists */ ret = acl_check(allow_acl, &i); if (ret) { if (ret > 0) { LogWarn(COMPONENT_FSAL, "Error converting ACL: %s at entry no %d", acl_error(ret), i); } } acl_str = acl_to_any_text(allow_acl, NULL, ',', TEXT_ABBREVIATE | TEXT_NUMERIC_IDS); LogDebug(COMPONENT_FSAL, "posix acl = %s ", acl_str); acl_free(acl_str); if (deny_acl) acl_free(deny_acl); return allow_acl; } nfs-ganesha-2.6.0/src/FSAL/FSAL_GLUSTER/posix_acls.h000066400000000000000000000024451324272410200214760ustar00rootroot00000000000000#include #include "nfs4_acls.h" #include #include "fsal_types.h" /* inheritance flags checks */ #define IS_FSAL_ACE_HAS_INHERITANCE_FLAGS(ACE) \ (IS_FSAL_ACE_FILE_INHERIT(ACE) | IS_FSAL_ACE_DIR_INHERIT(ACE) | \ IS_FSAL_ACE_NO_PROPAGATE(ACE) | IS_FSAL_ACE_INHERIT_ONLY(ACE)) #define IS_FSAL_ACE_APPLICABLE_FOR_BOTH_ACL(ACE) \ ((IS_FSAL_ACE_FILE_INHERIT(ACE) | IS_FSAL_ACE_DIR_INHERIT(ACE)) & \ !IS_FSAL_ACE_APPLICABLE_ONLY_FOR_INHERITED_ACL(ACE)) #define IS_FSAL_ACE_APPLICABLE_ONLY_FOR_INHERITED_ACL(ACE) \ ((IS_FSAL_ACE_FILE_INHERIT(ACE) | IS_FSAL_ACE_DIR_INHERIT(ACE)) & \ IS_FSAL_ACE_INHERIT_ONLY(ACE)) /* permission set for ACE's */ #define FSAL_ACE_PERM_SET_DEFAULT \ (FSAL_ACE_PERM_READ_ACL | FSAL_ACE_PERM_READ_ATTR \ | FSAL_ACE_PERM_SYNCHRONIZE) #define FSAL_ACE_PERM_SET_DEFAULT_WRITE \ (FSAL_ACE_PERM_WRITE_DATA | FSAL_ACE_PERM_APPEND_DATA) #define FSAL_ACE_PERM_SET_OWNER_WRITE \ (FSAL_ACE_PERM_WRITE_ACL | FSAL_ACE_PERM_WRITE_ATTR) int posix_acl_2_fsal_acl(acl_t p_posixacl, bool is_dir, bool is_inherit, fsal_ace_t **p_falacl); acl_t fsal_acl_2_posix_acl(fsal_acl_t *p_fsalacl, acl_type_t type); acl_entry_t find_entry(acl_t acl, acl_tag_t tag, unsigned int id); acl_entry_t get_entry(acl_t acl, acl_tag_t tag, unsigned int id); int ace_count(acl_t acl); nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/000077500000000000000000000000001324272410200165665ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/CMakeLists.txt000066400000000000000000000014401324272410200213250ustar00rootroot00000000000000if(USE_DBUS) include_directories( ${DBUS_INCLUDE_DIRS} ) endif(USE_DBUS) include_directories("${PROJECT_SOURCE_DIR}/FSAL/FSAL_GPFS") ########### next target ############### SET(fsalgpfs_LIB_SRCS main.c export.c handle.c file.c fsal_up.c fsal_ds.c fsal_mds.c fsal_unlink.c fsal_symlinks.c fsal_rename.c fsal_create.c fsal_fileop.c fsal_attrs.c fsal_lock.c fsal_lookup.c fsal_convert.c fsal_internal.c gpfsext.c fsal_stats_gpfs.c ) add_library(fsalgpfs MODULE ${fsalgpfs_LIB_SRCS}) add_sanitizers(fsalgpfs) target_link_libraries(fsalgpfs ${SYSTEM_LIBRARIES}) set_target_properties(fsalgpfs PROPERTIES VERSION 4.2.0 SOVERSION 4) ########### install files ############### install(TARGETS fsalgpfs COMPONENT fsal DESTINATION ${FSAL_DESTINATION} ) nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/README000066400000000000000000000013101324272410200174410ustar00rootroot00000000000000= FSAL_GPFS = The following is an experimental FSAL for use with IBM's GPFS filesystem. Eventually we hope to use the open-by-handle mechanism that's being developed for upstream Linux. There is currently nothing GPFS specific in the code base, though we expect that as more advanced nfsv4 permissions are supported there will be some calls directly into GPFS due to the handling of RichACLs in the Linux kernel. In the short term we're using a kernel module that provides a character driver to the same effect to prove out this approach. This kernel is in the "kernel" subdirectory, though has only been tested on a 2.6.18 kernel. Use with other versions is not recommended, and will probably not work. nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/export.c000066400000000000000000000606671324272410200202720ustar00rootroot00000000000000/** @file export.c * @brief GPFS FSAL module export functions. * * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ #include "config.h" #include #include /* used for 'dirname' */ #include #include #include #include #include #include #include "fsal.h" #include "fsal_internal.h" #include "fsal_convert.h" #include "FSAL/fsal_commonlib.h" #include "FSAL/fsal_config.h" #include "gpfs_methods.h" #include "nfs_exports.h" #include "export_mgr.h" #include "pnfs_utils.h" #include "include/gpfs.h" /* export object methods */ static void release(struct fsal_export *exp_hdl) { struct gpfs_fsal_export *myself = container_of(exp_hdl, struct gpfs_fsal_export, export); gpfs_unexport_filesystems(myself); fsal_detach_export(exp_hdl->fsal, &exp_hdl->exports); free_export_ops(exp_hdl); close(myself->export_fd); gsh_free(myself); /* elvis has left the building */ } static fsal_status_t get_dynamic_info(struct fsal_export *exp_hdl, struct fsal_obj_handle *obj_hdl, fsal_dynamicfsinfo_t *infop) { fsal_status_t status; struct statfs buffstatgpfs; fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; if (!infop) { fsal_error = ERR_FSAL_FAULT; goto out; } status = GPFSFSAL_statfs(export_fd, obj_hdl, &buffstatgpfs); if (FSAL_IS_ERROR(status)) return status; infop->total_bytes = buffstatgpfs.f_frsize * buffstatgpfs.f_blocks; infop->free_bytes = buffstatgpfs.f_frsize * buffstatgpfs.f_bfree; infop->avail_bytes = buffstatgpfs.f_frsize * buffstatgpfs.f_bavail; infop->total_files = buffstatgpfs.f_files; infop->free_files = buffstatgpfs.f_ffree; infop->avail_files = buffstatgpfs.f_ffree; infop->maxread = buffstatgpfs.f_bsize; infop->maxwrite = buffstatgpfs.f_bsize; infop->time_delta.tv_sec = 1; infop->time_delta.tv_nsec = 0; out: return fsalstat(fsal_error, 0); } static bool fs_supports(struct fsal_export *exp_hdl, fsal_fsinfo_options_t option) { struct fsal_staticfsinfo_t *info; info = gpfs_staticinfo(exp_hdl->fsal); return fsal_supports(info, option); } static uint64_t fs_maxfilesize(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = gpfs_staticinfo(exp_hdl->fsal); return fsal_maxfilesize(info); } static uint32_t fs_maxread(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = gpfs_staticinfo(exp_hdl->fsal); return fsal_maxread(info); } static uint32_t fs_maxwrite(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = gpfs_staticinfo(exp_hdl->fsal); return fsal_maxwrite(info); } static uint32_t fs_maxlink(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = gpfs_staticinfo(exp_hdl->fsal); return fsal_maxlink(info); } static uint32_t fs_maxnamelen(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = gpfs_staticinfo(exp_hdl->fsal); return fsal_maxnamelen(info); } static uint32_t fs_maxpathlen(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = gpfs_staticinfo(exp_hdl->fsal); return fsal_maxpathlen(info); } static struct timespec fs_lease_time(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = gpfs_staticinfo(exp_hdl->fsal); return fsal_lease_time(info); } static fsal_aclsupp_t fs_acl_support(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = gpfs_staticinfo(exp_hdl->fsal); return fsal_acl_support(info); } static attrmask_t fs_supported_attrs(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; attrmask_t supported_mask; struct gpfs_fsal_export *gpfs_export; gpfs_export = container_of(exp_hdl, struct gpfs_fsal_export, export); info = gpfs_staticinfo(exp_hdl->fsal); supported_mask = fsal_supported_attrs(info); /* Fixup supported_mask to indicate if ACL is actually supported for * this export. */ if (gpfs_export->use_acl) supported_mask |= ATTR_ACL; else supported_mask &= ~ATTR_ACL; return supported_mask; } static uint32_t fs_umask(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = gpfs_staticinfo(exp_hdl->fsal); return fsal_umask(info); } static uint32_t fs_xattr_access_rights(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = gpfs_staticinfo(exp_hdl->fsal); return fsal_xattr_access_rights(info); } /* get_quota * return quotas for this export. * path could cross a lower mount boundary which could * mask lower mount values with those of the export root * if this is a real issue, we can scan each time with setmntent() * better yet, compare st_dev of the file with st_dev of root_fd. * on linux, can map st_dev -> /proc/partitions name -> /dev/ */ static fsal_status_t get_quota(struct fsal_export *exp_hdl, const char *filepath, int quota_type, int quota_id, fsal_quota_t *fsal_quota) { gpfs_quotaInfo_t gpfs_quota = {0}; struct stat path_stat; int retval = 0; struct quotactl_arg args; struct fsal_filesystem *fs = container_of(exp_hdl, struct gpfs_fsal_export, export)->root_fs; if (stat(filepath, &path_stat) < 0) { retval = errno; LogMajor(COMPONENT_FSAL, "GPFS get quota, stat: root_path: %s, errno=(%d) %s", fs->path, retval, strerror(retval)); return fsalstat(posix2fsal_error(retval), retval); } if ((major(path_stat.st_dev) != fs->dev.major) || (minor(path_stat.st_dev) != fs->dev.minor)) { LogMajor(COMPONENT_FSAL, "GPFS get quota: crossed mount boundary! root_path: %s, quota path: %s", fs->path, filepath); return fsalstat(ERR_FSAL_FAULT, 0); /* maybe a better error? */ } args.pathname = filepath; args.cmd = GPFS_QCMD(Q_GETQUOTA, quota_type); args.qid = quota_id; args.bufferP = &gpfs_quota; fsal_set_credentials(op_ctx->creds); if (gpfs_ganesha(OPENHANDLE_QUOTA, &args) < 0) retval = errno; fsal_restore_ganesha_credentials(); if (retval) return fsalstat(posix2fsal_error(retval), retval); fsal_quota->bhardlimit = gpfs_quota.blockHardLimit; fsal_quota->bsoftlimit = gpfs_quota.blockSoftLimit; fsal_quota->curblocks = gpfs_quota.blockUsage; fsal_quota->fhardlimit = gpfs_quota.inodeHardLimit; fsal_quota->fsoftlimit = gpfs_quota.inodeSoftLimit; fsal_quota->curfiles = gpfs_quota.inodeUsage; fsal_quota->btimeleft = gpfs_quota.blockGraceTime; fsal_quota->ftimeleft = gpfs_quota.inodeGraceTime; fsal_quota->bsize = 1024; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* set_quota * same lower mount restriction applies */ static fsal_status_t set_quota(struct fsal_export *exp_hdl, const char *filepath, int quota_type, int quota_id, fsal_quota_t *fsal_quota, fsal_quota_t *res_quota) { gpfs_quotaInfo_t gpfs_quota = {0}; struct stat path_stat; int retval = 0; struct quotactl_arg args; struct fsal_filesystem *fs = container_of(exp_hdl, struct gpfs_fsal_export, export)->root_fs; if (stat(filepath, &path_stat) < 0) { retval = errno; LogMajor(COMPONENT_FSAL, "GPFS set quota, stat: root_path: %s, errno=(%d) %s", fs->path, retval, strerror(retval)); return fsalstat(posix2fsal_error(retval), retval); } if ((major(path_stat.st_dev) != fs->dev.major) || (minor(path_stat.st_dev) != fs->dev.minor)) { LogMajor(COMPONENT_FSAL, "GPFS set quota: crossed mount boundary! root_path: %s, quota path: %s", fs->path, filepath); return fsalstat(ERR_FSAL_FAULT, 0); /* maybe a better error? */ } gpfs_quota.blockHardLimit = fsal_quota->bhardlimit; gpfs_quota.blockSoftLimit = fsal_quota->bsoftlimit; gpfs_quota.inodeHardLimit = fsal_quota->fhardlimit; gpfs_quota.inodeSoftLimit = fsal_quota->fsoftlimit; gpfs_quota.blockGraceTime = fsal_quota->btimeleft; gpfs_quota.inodeGraceTime = fsal_quota->ftimeleft; args.pathname = filepath; args.cmd = GPFS_QCMD(Q_SETQUOTA, quota_type); args.qid = quota_id; args.bufferP = &gpfs_quota; fsal_set_credentials(op_ctx->creds); if (gpfs_ganesha(OPENHANDLE_QUOTA, &args) < 0) retval = errno; fsal_restore_ganesha_credentials(); if (retval) return fsalstat(posix2fsal_error(retval), retval); if (res_quota != NULL) return get_quota(exp_hdl, filepath, quota_type, quota_id, res_quota); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* extract a file handle from a buffer. * do verification checks and flag any and all suspicious bits. * Return an updated fh_desc into whatever was passed. The most * common behavior, done here is to just reset the length. */ static fsal_status_t gpfs_wire_to_host(struct fsal_export *exp_hdl, fsal_digesttype_t in_type, struct gsh_buffdesc *fh_desc, int flags) { struct gpfs_file_handle *hdl; size_t fh_size; /* sanity checks */ if (!fh_desc || !fh_desc->addr) return fsalstat(ERR_FSAL_FAULT, 0); hdl = (struct gpfs_file_handle *)fh_desc->addr; if (flags & FH_FSAL_BIG_ENDIAN) { #if (BYTE_ORDER != BIG_ENDIAN) hdl->handle_size = bswap_16(hdl->handle_size); hdl->handle_type = bswap_16(hdl->handle_type); hdl->handle_version = bswap_16(hdl->handle_version); hdl->handle_key_size = bswap_16(hdl->handle_key_size); #endif } else { #if (BYTE_ORDER == BIG_ENDIAN) hdl->handle_size = bswap_16(hdl->handle_size); hdl->handle_type = bswap_16(hdl->handle_type); hdl->handle_version = bswap_16(hdl->handle_version); hdl->handle_key_size = bswap_16(hdl->handle_key_size); #endif } fh_size = gpfs_sizeof_handle(hdl); LogFullDebug(COMPONENT_FSAL, "flags 0x%X size %d type %d ver %d key_size %d FSID 0x%X:%X fh_size %zu", flags, hdl->handle_size, hdl->handle_type, hdl->handle_version, hdl->handle_key_size, hdl->handle_fsid[0], hdl->handle_fsid[1], fh_size); /* Some older file handles include additional 16 bytes in fh_desc->len. * Honor those as well. */ if (fh_desc->len != fh_size && fh_desc->len != fh_size + 16) { LogMajor(COMPONENT_FSAL, "Size mismatch for handle. should be %zu, got %zu", fh_size, fh_desc->len); return fsalstat(ERR_FSAL_SERVERFAULT, 0); } fh_desc->len = hdl->handle_size; /* pass back the size */ return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* Produce handle-key from a host-handle */ static fsal_status_t gpfs_host_to_key(struct fsal_export *exp_hdl, struct gsh_buffdesc *fh_desc) { struct gpfs_file_handle *hdl; if (fh_desc->len < offsetof(struct gpfs_file_handle, f_handle)) return fsalstat(ERR_FSAL_INVAL, 0); hdl = (struct gpfs_file_handle *)fh_desc->addr; fh_desc->len = hdl->handle_key_size; /* pass back the key size */ LogFullDebug(COMPONENT_FSAL, "size %d type %d ver %d key_size %d FSID 0x%X:%X", hdl->handle_size, hdl->handle_type, hdl->handle_version, hdl->handle_key_size, hdl->handle_fsid[0], hdl->handle_fsid[1]); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Allocate a state_t structure * * Note that this is not expected to fail since memory allocation is * expected to abort on failure. * * @param[in] exp_hdl Export state_t will be associated with * @param[in] state_type Type of state to allocate * @param[in] related_state Related state if appropriate * * @returns a state structure. */ struct state_t * gpfs_alloc_state(struct fsal_export *exp_hdl, enum state_type state_type, struct state_t *related_state) { struct state_t *state; struct gpfs_fd *my_fd; state = init_state(gsh_calloc(1, sizeof(struct gpfs_state_fd)), exp_hdl, state_type, related_state); my_fd = &container_of(state, struct gpfs_state_fd, state)->gpfs_fd; my_fd->fd = -1; my_fd->openflags = FSAL_O_CLOSED; PTHREAD_RWLOCK_init(&my_fd->fdlock, NULL); return state; } /** * @brief free a gpfs_state_fd structure * * @param[in] exp_hdl Export state_t will be associated with * @param[in] state Related state if appropriate * */ void gpfs_free_state(struct fsal_export *exp_hdl, struct state_t *state) { struct gpfs_state_fd *state_fd = container_of(state, struct gpfs_state_fd, state); struct gpfs_fd *my_fd = &state_fd->gpfs_fd; PTHREAD_RWLOCK_destroy(&my_fd->fdlock); gsh_free(state_fd); } /** * @brief overwrite vector entries with the methods that we support * @param ops tpye of struct export_ops */ void gpfs_export_ops_init(struct export_ops *ops) { ops->release = release; ops->lookup_path = gpfs_lookup_path; ops->wire_to_host = gpfs_wire_to_host; ops->host_to_key = gpfs_host_to_key; ops->create_handle = gpfs_create_handle; ops->get_fs_dynamic_info = get_dynamic_info; ops->fs_supports = fs_supports; ops->fs_maxfilesize = fs_maxfilesize; ops->fs_maxread = fs_maxread; ops->fs_maxwrite = fs_maxwrite; ops->fs_maxlink = fs_maxlink; ops->fs_maxnamelen = fs_maxnamelen; ops->fs_maxpathlen = fs_maxpathlen; ops->fs_lease_time = fs_lease_time; ops->fs_acl_support = fs_acl_support; ops->fs_supported_attrs = fs_supported_attrs; ops->fs_umask = fs_umask; ops->fs_xattr_access_rights = fs_xattr_access_rights; ops->get_quota = get_quota; ops->set_quota = set_quota; ops->alloc_state = gpfs_alloc_state; ops->free_state = gpfs_free_state; } static void free_gpfs_filesystem(struct gpfs_filesystem *gpfs_fs) { if (gpfs_fs->root_fd >= 0) close(gpfs_fs->root_fd); PTHREAD_MUTEX_destroy(&gpfs_fs->upvector_mutex); gsh_free(gpfs_fs); } /** * @brief Extract major from from fsid * @param fh GPFS file handle * @param fsid FSAL ID */ void gpfs_extract_fsid(struct gpfs_file_handle *fh, struct fsal_fsid__ *fsid) { memcpy(&fsid->major, fh->handle_fsid, sizeof(fsid->major)); fsid->minor = 0; } /** * @brief Open root fd * @param gpfs_fs GPFS filesystem * @return 0(zero) on success, otherwise error. */ int open_root_fd(struct gpfs_filesystem *gpfs_fs) { struct fsal_fsid__ fsid; int retval; fsal_status_t status; struct gpfs_file_handle fh = {0}; gpfs_fs->root_fd = open(gpfs_fs->fs->path, O_RDONLY | O_DIRECTORY); if (gpfs_fs->root_fd < 0) { retval = errno; LogMajor(COMPONENT_FSAL, "Could not open GPFS mount point %s: rc = %s (%d)", gpfs_fs->fs->path, strerror(retval), retval); return retval; } LogFullDebug(COMPONENT_FSAL, "root export_fd %d path %s", gpfs_fs->root_fd, gpfs_fs->fs->path); status = fsal_internal_get_handle_at(gpfs_fs->root_fd, gpfs_fs->fs->path, &fh, gpfs_fs->root_fd); if (FSAL_IS_ERROR(status)) { retval = status.minor; LogMajor(COMPONENT_FSAL, "Get root handle for %s failed with %s (%d)", gpfs_fs->fs->path, strerror(retval), retval); goto errout; } gpfs_extract_fsid(&fh, &fsid); retval = re_index_fs_fsid(gpfs_fs->fs, GPFS_FSID_TYPE, &fsid); if (retval == 0) return 0; LogCrit(COMPONENT_FSAL, "Could not re-index GPFS file system fsid for %s, error:%d", gpfs_fs->fs->path, retval); assert(retval < 0); retval = -retval; errout: close(gpfs_fs->root_fd); gpfs_fs->root_fd = -1; return retval; } /** * @brief Claim GPFS filesystem * @param fs FSAL filesystem * @param exp FSAL export * @return 0(zero) on success, otherwise error. */ int gpfs_claim_filesystem(struct fsal_filesystem *fs, struct fsal_export *exp) { struct gpfs_filesystem *gpfs_fs; int retval; struct gpfs_filesystem_export_map *map; pthread_attr_t attr_thr; if (strcmp(fs->type, "gpfs") != 0) { LogEvent(COMPONENT_FSAL, "Attempt to claim non-GPFS filesystem %s", fs->path); return ENXIO; } if (fs->fsal != NULL && fs->private_data == NULL) LogFatal(COMPONENT_FSAL, "Something wrong with export, fs %s appears already claimed but doesn't have private data", fs->path); if (fs->fsal == NULL && fs->private_data != NULL) LogFatal(COMPONENT_FSAL, "Something wrong with export, fs %s was not claimed but had non-NULL private", fs->path); gpfs_fs = fs->private_data; if (!gpfs_fs) { /* first export */ gpfs_fs = gsh_calloc(1, sizeof(*gpfs_fs)); glist_init(&gpfs_fs->exports); gpfs_fs->root_fd = -1; gpfs_fs->fs = fs; PTHREAD_MUTEX_init(&gpfs_fs->upvector_mutex, NULL); } /* Now map the file system and export */ map = gsh_calloc(1, sizeof(*map)); map->fs = gpfs_fs; map->exp = container_of(exp, struct gpfs_fsal_export, export); PTHREAD_MUTEX_lock(&gpfs_fs->upvector_mutex); glist_add_tail(&gpfs_fs->exports, &map->on_exports); glist_add_tail(&map->exp->filesystems, &map->on_filesystems); PTHREAD_MUTEX_unlock(&gpfs_fs->upvector_mutex); map->exp->export_fd = open(op_ctx->ctx_export->fullpath, O_RDONLY | O_DIRECTORY); if (map->exp->export_fd < 0) { retval = errno; LogMajor(COMPONENT_FSAL, "Could not open GPFS export point %s: rc = %s (%d)", op_ctx->ctx_export->fullpath, strerror(retval), retval); goto errout; } LogFullDebug(COMPONENT_FSAL, "export_fd %d path %s", map->exp->export_fd, op_ctx->ctx_export->fullpath); /* We have set up the export. If the file system is already claimed, * we are done. */ if (fs->private_data) /* file system is already claimed */ return 0; /* Get an fd for the root and create an upcall thread */ retval = open_root_fd(gpfs_fs); if (retval != 0) { if (retval == ENOTTY) { LogInfo(COMPONENT_FSAL, "file system %s is not exportable with %s", fs->path, exp->fsal->name); retval = ENXIO; } goto errout; } if (pthread_attr_init(&attr_thr) != 0) LogCrit(COMPONENT_THREAD, "can't init pthread's attributes"); if (pthread_attr_setscope(&attr_thr, PTHREAD_SCOPE_SYSTEM) != 0) LogCrit(COMPONENT_THREAD, "can't set pthread's scope"); if (pthread_attr_setdetachstate(&attr_thr, PTHREAD_CREATE_JOINABLE) != 0) LogCrit(COMPONENT_THREAD, "can't set pthread's join state"); if (pthread_attr_setstacksize(&attr_thr, 2116488) != 0) LogCrit(COMPONENT_THREAD, "can't set pthread's stack size"); if (pthread_create(&gpfs_fs->up_thread, &attr_thr, GPFSFSAL_UP_Thread, gpfs_fs)) { retval = errno; LogCrit(COMPONENT_THREAD, "Could not create GPFSFSAL_UP_Thread, error = %d (%s)", retval, strerror(retval)); goto errout; } fs->private_data = gpfs_fs; return 0; errout: if (map->exp->export_fd >= 0) { close(map->exp->export_fd); map->exp->export_fd = -1; } PTHREAD_MUTEX_lock(&gpfs_fs->upvector_mutex); glist_del(&map->on_filesystems); glist_del(&map->on_exports); PTHREAD_MUTEX_unlock(&gpfs_fs->upvector_mutex); gsh_free(map); if (!fs->private_data) free_gpfs_filesystem(gpfs_fs); return retval; } /** * @brief Unclaim filesystem * @param fs FSAL filesystem */ void gpfs_unclaim_filesystem(struct fsal_filesystem *fs) { struct gpfs_filesystem *gpfs_fs = fs->private_data; struct glist_head *glist, *glistn; struct gpfs_filesystem_export_map *map; struct callback_arg callback = {0}; int reason = THREAD_STOP; if (gpfs_fs == NULL) goto out; glist_for_each_safe(glist, glistn, &gpfs_fs->exports) { map = glist_entry(glist, struct gpfs_filesystem_export_map, on_exports); /* Remove this file system from mapping */ PTHREAD_MUTEX_lock(&map->fs->upvector_mutex); glist_del(&map->on_filesystems); glist_del(&map->on_exports); PTHREAD_MUTEX_unlock(&map->fs->upvector_mutex); if (map->exp->root_fs == fs) LogInfo(COMPONENT_FSAL, "Removing root_fs %s from GPFS export", fs->path); /* And free it */ gsh_free(map); } /* Terminate GPFS upcall thread */ callback.mountdirfd = gpfs_fs->root_fd; callback.reason = &reason; if (gpfs_ganesha(OPENHANDLE_THREAD_UPDATE, &callback)) LogCrit(COMPONENT_FSAL, "Unable to stop upcall thread for %s, fd=%d, errno=%d", fs->path, gpfs_fs->root_fd, errno); else LogFullDebug(COMPONENT_FSAL, "Thread STOP successful"); pthread_join(gpfs_fs->up_thread, NULL); free_gpfs_filesystem(gpfs_fs); fs->private_data = NULL; out: LogInfo(COMPONENT_FSAL, "GPFS Unclaiming %s", fs->path); } /** * @brief Unexport filesystem * @param exp FSAL export */ void gpfs_unexport_filesystems(struct gpfs_fsal_export *exp) { struct glist_head *glist, *glistn; struct gpfs_filesystem_export_map *map; PTHREAD_RWLOCK_wrlock(&fs_lock); glist_for_each_safe(glist, glistn, &exp->filesystems) { map = glist_entry(glist, struct gpfs_filesystem_export_map, on_filesystems); /* Remove this export from mapping */ PTHREAD_MUTEX_lock(&map->fs->upvector_mutex); glist_del(&map->on_filesystems); glist_del(&map->on_exports); PTHREAD_MUTEX_unlock(&map->fs->upvector_mutex); if (glist_empty(&map->fs->exports)) { LogInfo(COMPONENT_FSAL, "GPFS is no longer exporting filesystem %s", map->fs->fs->path); unclaim_fs(map->fs->fs); } /* And free it */ gsh_free(map); } PTHREAD_RWLOCK_unlock(&fs_lock); } /** * @brief create_export * * Create an export point and return a handle to it to be kept * in the export list. * First lookup the fsal, then create the export and then put the fsal back. * returns the export with one reference taken. * * @return FSAL status */ fsal_status_t gpfs_create_export(struct fsal_module *fsal_hdl, void *parse_node, struct config_error_type *err_type, const struct fsal_up_vector *up_ops) { /* The status code to return */ fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; struct gpfs_fsal_export *gpfs_exp; struct fsal_export *exp; gpfs_exp = gsh_calloc(1, sizeof(struct gpfs_fsal_export)); exp = &gpfs_exp->export; glist_init(&gpfs_exp->filesystems); status.minor = fsal_internal_version(); LogInfo(COMPONENT_FSAL, "GPFS get version is %d options 0x%X id %d", status.minor, op_ctx->export_perms ? op_ctx->export_perms->options : 0, op_ctx->ctx_export->export_id); fsal_export_init(exp); gpfs_export_ops_init(&exp->exp_ops); status.minor = fsal_attach_export(fsal_hdl, &exp->exports); if (status.minor != 0) { status.major = posix2fsal_error(status.minor); goto free; /* seriously bad */ } exp->fsal = fsal_hdl; exp->up_ops = up_ops; op_ctx->fsal_export = exp; status.minor = resolve_posix_filesystem(op_ctx->ctx_export->fullpath, fsal_hdl, exp, gpfs_claim_filesystem, gpfs_unclaim_filesystem, &gpfs_exp->root_fs); if (status.minor != 0) { LogCrit(COMPONENT_FSAL, "resolve_posix_filesystem(%s) returned %s (%d)", op_ctx->ctx_export->fullpath, strerror(status.minor), status.minor); status.major = posix2fsal_error(status.minor); goto detach; } /* if the nodeid has not been obtained, get it now */ if (!g_nodeid) { struct gpfs_filesystem *gpfs_fs = gpfs_exp->root_fs->private_data; struct grace_period_arg gpa; int nodeid; gpa.mountdirfd = gpfs_fs->root_fd; nodeid = gpfs_ganesha(OPENHANDLE_GET_NODEID, &gpa); if (nodeid > 0) { g_nodeid = nodeid; LogFullDebug(COMPONENT_FSAL, "nodeid %d", g_nodeid); } else LogCrit(COMPONENT_FSAL, "OPENHANDLE_GET_NODEID failed rc %d", nodeid); } gpfs_exp->pnfs_ds_enabled = exp->exp_ops.fs_supports(exp, fso_pnfs_ds_supported); gpfs_exp->pnfs_mds_enabled = exp->exp_ops.fs_supports(exp, fso_pnfs_mds_supported); if (gpfs_exp->pnfs_ds_enabled) { struct fsal_pnfs_ds *pds = NULL; status = fsal_hdl->m_ops.fsal_pnfs_ds(fsal_hdl, parse_node, &pds); if (status.major != ERR_FSAL_NO_ERROR) goto unexport; /* special case: server_id matches export_id */ pds->id_servers = op_ctx->ctx_export->export_id; pds->mds_export = op_ctx->ctx_export; pds->mds_fsal_export = op_ctx->fsal_export; if (!pnfs_ds_insert(pds)) { LogCrit(COMPONENT_CONFIG, "Server id %d already in use.", pds->id_servers); status.major = ERR_FSAL_EXIST; fsal_pnfs_ds_fini(pds); gsh_free(pds); goto unexport; } LogInfo(COMPONENT_FSAL, "gpfs_fsal_create: pnfs ds was enabled for [%s]", op_ctx->ctx_export->fullpath); export_ops_pnfs(&exp->exp_ops); } gpfs_exp->use_acl = !op_ctx_export_has_option(EXPORT_OPTION_DISABLE_ACL); return status; unexport: gpfs_unexport_filesystems(gpfs_exp); detach: fsal_detach_export(fsal_hdl, &exp->exports); free: free_export_ops(exp); gsh_free(gpfs_exp); return status; } nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/file.c000066400000000000000000001173111324272410200176550ustar00rootroot00000000000000/** @file file.c * @brief GPFS FSAL module file I/O functions * * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ /* _FILE_OFFSET_BITS macro causes F_GETLK/SETLK/SETLKW to be defined to * F_GETLK64/SETLK64/SETLKW64. Currently GPFS kernel module doesn't work * with these 64 bit macro values through ganesha interface. Undefine it * here to use plain F_GETLK/SETLK/SETLKW values. */ #undef _FILE_OFFSET_BITS #include #include "fsal.h" #include "FSAL/access_check.h" #include "fsal_convert.h" #include "fsal_internal.h" #include #include #include "gpfs_methods.h" #define STATE2FD(s) (&container_of(s, struct gpfs_state_fd, state)->gpfs_fd) static fsal_status_t gpfs_open_func(struct fsal_obj_handle *obj_hdl, fsal_openflags_t openflags, struct fsal_fd *fd) { fsal_status_t status; struct gpfs_fd *my_fd = (struct gpfs_fd *)fd; int posix_flags = 0; fsal2posix_openflags(openflags, &posix_flags); status = GPFSFSAL_open(obj_hdl, op_ctx, posix_flags, &my_fd->fd); if (FSAL_IS_ERROR(status)) return status; my_fd->openflags = openflags; LogFullDebug(COMPONENT_FSAL, "new fd %d", my_fd->fd); return status; } static fsal_status_t gpfs_close_func(struct fsal_obj_handle *obj_hdl, struct fsal_fd *fd) { fsal_status_t status; struct gpfs_fd *my_fd = (struct gpfs_fd *)fd; status = fsal_internal_close(my_fd->fd, NULL, 0); my_fd->fd = -1; my_fd->openflags = FSAL_O_CLOSED; return status; } /** * @brief Merge a duplicate handle with an original handle * * This function is used if an upper layer detects that a duplicate * object handle has been created. It allows the FSAL to merge anything * from the duplicate back into the original. * * The caller must release the object (the caller may have to close * files if the merge is unsuccessful). * * @param[in] orig_hdl Original handle * @param[in] dupe_hdl Handle to merge into original * * @return FSAL status. * */ fsal_status_t gpfs_merge(struct fsal_obj_handle *orig_hdl, struct fsal_obj_handle *dupe_hdl) { fsal_status_t status = {ERR_FSAL_NO_ERROR, 0}; if (orig_hdl->type == REGULAR_FILE && dupe_hdl->type == REGULAR_FILE) { /* We need to merge the share reservations on this file. * This could result in ERR_FSAL_SHARE_DENIED. */ struct gpfs_fsal_obj_handle *orig, *dupe; orig = container_of(orig_hdl, struct gpfs_fsal_obj_handle, obj_handle); dupe = container_of(dupe_hdl, struct gpfs_fsal_obj_handle, obj_handle); /* This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&orig_hdl->obj_lock); status = merge_share(&orig->u.file.share, &dupe->u.file.share); PTHREAD_RWLOCK_unlock(&orig_hdl->obj_lock); } return status; } static fsal_status_t open_by_handle(struct fsal_obj_handle *obj_hdl, struct state_t *state, fsal_openflags_t openflags, int posix_flags, fsal_verifier_t verifier, struct attrlist *attrs_out, enum fsal_create_mode createmode, bool *cpm_check) { struct fsal_export *export = op_ctx->fsal_export; struct gpfs_fsal_obj_handle *gpfs_hdl; struct gpfs_filesystem *gpfs_fs = obj_hdl->fs->private_data; fsal_status_t status; const bool truncated = (posix_flags & O_TRUNC) != 0; struct gpfs_fd *my_fd; int fd; /* This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->obj_lock); gpfs_hdl = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); if (state != NULL) { my_fd = &container_of(state, struct gpfs_state_fd, state)->gpfs_fd; /* 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). */ /* Check share reservation conflicts. */ status = check_share_conflict(&gpfs_hdl->u.file.share, openflags, false); if (FSAL_IS_ERROR(status)) goto out; /* Take the share reservation now by updating the counters. */ update_share_counters(&gpfs_hdl->u.file.share, FSAL_O_CLOSED, openflags); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); } else { /* We need to use the global fd to continue. */ my_fd = &gpfs_hdl->u.file.fd; } status = GPFSFSAL_open(obj_hdl, op_ctx, posix_flags, &fd); if (FSAL_IS_ERROR(status)) { if (state == NULL) goto out; else goto undo_share; } /* Close any old open file descriptor and update with the new * one. There shouldn't be any old open for state based call. */ if (my_fd->openflags != FSAL_O_CLOSED) { assert(my_fd->fd >= 0); /* assert(state == NULL); */ (void)fsal_internal_close(my_fd->fd, NULL, 0); } my_fd->fd = fd; my_fd->openflags = openflags; if (attrs_out && (createmode >= FSAL_EXCLUSIVE || truncated)) { /* Refresh the attributes */ status = GPFSFSAL_getattrs(export, gpfs_fs, op_ctx, gpfs_hdl->handle, attrs_out); if (!FSAL_IS_ERROR(status)) { LogFullDebug(COMPONENT_FSAL, "New size = %"PRIx64, attrs_out->filesize); /* Now check verifier for exclusive */ if (createmode >= FSAL_EXCLUSIVE && !check_verifier_attrlist(attrs_out, verifier)) /* Verifier didn't match, return EEXIST */ status = fsalstat(posix2fsal_error(EEXIST), EEXIST); } } else if (attrs_out && attrs_out->request_mask & ATTR_RDATTR_ERR) { attrs_out->valid_mask &= ATTR_RDATTR_ERR; } if (state == NULL) { /* If no state, return status. If success, we haven't done * any permission check so ask the caller to do so. */ *cpm_check = !FSAL_IS_ERROR(status); goto out; } if (!FSAL_IS_ERROR(status)) { /* Return success. We haven't done any permission * check so ask the caller to do so. */ *cpm_check = true; return status; } (void) fsal_internal_close(my_fd->fd, state->state_owner, 0); my_fd->fd = -1; my_fd->openflags = FSAL_O_CLOSED; undo_share: /* 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->obj_lock); update_share_counters(&gpfs_hdl->u.file.share, openflags, FSAL_O_CLOSED); out: PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } static fsal_status_t open_by_name(struct fsal_obj_handle *obj_hdl, struct state_t *state, const char *name, fsal_openflags_t openflags, int posix_flags, fsal_verifier_t verifier, struct attrlist *attrs_out, bool *cpm_check) { struct fsal_obj_handle *temp = NULL; fsal_status_t status; /* We don't have open by name... */ status = obj_hdl->obj_ops.lookup(obj_hdl, name, &temp, NULL); if (FSAL_IS_ERROR(status)) { LogFullDebug(COMPONENT_FSAL, "lookup returned %s", fsal_err_txt(status)); return status; } status = open_by_handle(temp, state, openflags, posix_flags, verifier, attrs_out, FSAL_NO_CREATE, cpm_check); if (FSAL_IS_ERROR(status)) { /* Release the object we found by lookup. */ temp->obj_ops.release(temp); LogFullDebug(COMPONENT_FSAL, "open returned %s", fsal_err_txt(status)); } return status; } /** * @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] attr_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 *attr_set, fsal_verifier_t verifier, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out, bool *caller_perm_check) { struct gpfs_fsal_obj_handle *hdl = NULL; struct fsal_export *export = op_ctx->fsal_export; struct gpfs_file_handle fh; int posix_flags = 0; bool created = false; fsal_status_t status; mode_t unix_mode; LogAttrlist(COMPONENT_FSAL, NIV_FULL_DEBUG, "attrs ", attr_set, false); fsal2posix_openflags(openflags, &posix_flags); if (createmode >= FSAL_EXCLUSIVE) /* Now fixup attrs for verifier if exclusive create */ set_common_verifier(attr_set, verifier); if (name == NULL) return open_by_handle(obj_hdl, state, openflags, posix_flags, verifier, attrs_out, createmode, caller_perm_check); /* 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. */ /* Non creation case, libgpfs doesn't have open by name so we * have to do a lookup and then handle as an open by handle. */ if (createmode == FSAL_NO_CREATE) return open_by_name(obj_hdl, state, name, openflags, posix_flags, verifier, attrs_out, caller_perm_check); 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(attr_set->mode) & ~export->exp_ops.fs_umask(export); /* Don't set the mode if we later set the attributes */ FSAL_UNSET_MASK(attr_set->valid_mask, ATTR_MODE); if (createmode == FSAL_UNCHECKED && (attr_set->valid_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; } status = GPFSFSAL_create2(obj_hdl, name, op_ctx, unix_mode, &fh, posix_flags, attrs_out); if (status.major == ERR_FSAL_EXIST && createmode == FSAL_UNCHECKED && (posix_flags & O_EXCL) != 0) { /* If we tried to create O_EXCL to set attributes and * failed. Remove O_EXCL and retry, also remember not * to set attributes. 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; status = GPFSFSAL_create2(obj_hdl, name, op_ctx, unix_mode, &fh, posix_flags, attrs_out); } if (FSAL_IS_ERROR(status)) return status; /* 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. * Since we were able to do the permission check even if we were not * creating the file, let the caller know the permission check has * already been done. Note it IS possible in the case of a race between * an UNCHECKED open and an external unlink, we did create the file. */ created = (posix_flags & O_EXCL) != 0; *caller_perm_check = false; /* allocate an obj_handle and fill it up */ hdl = alloc_handle(&fh, obj_hdl->fs, attrs_out, NULL, export); if (hdl == NULL) { status = fsalstat(posix2fsal_error(ENOMEM), ENOMEM); goto fileerr; } *new_obj = &hdl->obj_handle; if (created && attr_set->valid_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. */ status = (*new_obj)->obj_ops.setattr2(*new_obj, false, state, attr_set); if (FSAL_IS_ERROR(status)) goto fileerr; if (attrs_out != NULL) { status = (*new_obj)->obj_ops.getattrs(*new_obj, attrs_out); if (FSAL_IS_ERROR(status) && (attrs_out->request_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; } } /* Restore posix_flags as it was modified for create above */ fsal2posix_openflags(openflags, &posix_flags); return open_by_handle(&hdl->obj_handle, state, openflags, posix_flags, verifier, attrs_out, createmode, caller_perm_check); fileerr: if (hdl != NULL) { /* Release the handle we just allocated. */ (*new_obj)->obj_ops.release(*new_obj); *new_obj = NULL; } if (created) { /* Remove the file we just created */ status = GPFSFSAL_unlink(obj_hdl, name, op_ctx); } return status; } /** * @brief GPFS read plus * * @param obj_hdl FSAL object handle / or fd * @param offset Offset * @param buffer_size Size of buffer * @param buffer void reference to buffer * @param read_amount size_t reference to amount of data read * @param end_of_file boolean indiocating the end of file * @param info I/O information * @return FSAL status */ fsal_status_t gpfs_read_plus_fd(int my_fd, uint64_t offset, size_t buffer_size, void *buffer, size_t *read_amount, bool *end_of_file, struct io_info *info, int expfd) { const fsal_status_t status = {ERR_FSAL_NO_ERROR, 0}; struct read_arg rarg = {0}; ssize_t nb_read; int errsv; if (!buffer || !read_amount || !end_of_file || !info) return fsalstat(ERR_FSAL_FAULT, 0); assert(my_fd >= 0); rarg.mountdirfd = expfd; rarg.fd = my_fd; rarg.bufP = buffer; rarg.offset = offset; rarg.length = buffer_size; rarg.options = IO_SKIP_HOLE; nb_read = gpfs_ganesha(OPENHANDLE_READ_BY_FD, &rarg); errsv = errno; if (nb_read < 0) { if (errsv == EUNATCH) LogFatal(COMPONENT_FSAL, "GPFS Returned EUNATCH"); if (errsv != ENODATA) return fsalstat(posix2fsal_error(errsv), errsv); /* errsv == ENODATA */ #if 0 /** @todo FSF: figure out how to fix this... */ if ((buffer_size + offset) > myself->attributes.filesize) { if (offset >= myself->attributes.filesize) *read_amount = 0; else *read_amount = myself->attributes.filesize - offset; info->io_content.hole.di_length = *read_amount; } else { *read_amount = buffer_size; info->io_content.hole.di_length = buffer_size; } #endif info->io_content.what = NFS4_CONTENT_HOLE; info->io_content.hole.di_offset = offset; } else { info->io_content.what = NFS4_CONTENT_DATA; info->io_content.data.d_offset = offset + nb_read; info->io_content.data.d_data.data_len = nb_read; info->io_content.data.d_data.data_val = buffer; *read_amount = nb_read; } if (nb_read != -1 && (nb_read == 0 || nb_read < buffer_size)) *end_of_file = true; else *end_of_file = false; return status; } /** * @brief Re-open a file that may be already opened * * This function supports changing the access mode of a share reservation and * thus should only be called with a share state. The state_lock must be held. * * This MAY be used to open a file the first time if there is no need for * open by name or create semantics. One example would be 9P lopen. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * @param[in] openflags Mode for re-open * * @return FSAL status. */ fsal_status_t gpfs_reopen2(struct fsal_obj_handle *obj_hdl, struct state_t *state, fsal_openflags_t openflags) { struct gpfs_fd *my_share_fd = &container_of(state, struct gpfs_state_fd, state)->gpfs_fd; fsal_status_t status; int posix_flags = 0; int my_fd = -1; struct fsal_share *share = &container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle)->u.file.share; 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->obj_lock); /* We can conflict with old share, so go ahead and check now. */ status = check_share_conflict(share, openflags, false); if (FSAL_IS_ERROR(status)) { PTHREAD_RWLOCK_unlock(&obj_hdl->obj_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(share, my_share_fd->openflags, openflags); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); fsal2posix_openflags(openflags, &posix_flags); status = GPFSFSAL_open(obj_hdl, op_ctx, posix_flags, &my_fd); if (!FSAL_IS_ERROR(status)) { /* Close the existing file descriptor and copy the new * one over. Make sure no one is using the fd that we are * about to close! */ PTHREAD_RWLOCK_wrlock(&my_share_fd->fdlock); fsal_internal_close(my_share_fd->fd, NULL, 0); my_share_fd->fd = my_fd; my_share_fd->openflags = openflags; PTHREAD_RWLOCK_unlock(&my_share_fd->fdlock); } 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->obj_lock); update_share_counters(share, openflags, my_share_fd->openflags); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); } return status; } 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 *closefd, bool open_for_locks) { fsal_status_t status = {ERR_FSAL_NO_ERROR, 0}; struct gpfs_fsal_obj_handle *myself; struct gpfs_fd temp_fd = { FSAL_O_CLOSED, PTHREAD_RWLOCK_INITIALIZER, -1 }; struct gpfs_fd *out_fd = &temp_fd; int posix_flags; bool reusing_open_state_fd = false; myself = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); fsal2posix_openflags(openflags, &posix_flags); LogFullDebug(COMPONENT_FSAL, "openflags 0x%X posix_flags 0x%X", openflags, posix_flags); switch (obj_hdl->type) { 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, gpfs_open_func, gpfs_close_func, has_lock, closefd, open_for_locks, &reusing_open_state_fd); *fd = out_fd->fd; return status; case SOCKET_FILE: case CHARACTER_FILE: case BLOCK_FILE: case SYMBOLIC_LINK: case FIFO_FILE: case DIRECTORY: break; case NO_FILE_TYPE: case EXTENDED_ATTR: return fsalstat(posix2fsal_error(EINVAL), EINVAL); } /* Open file descriptor for non-regular files. */ status = gpfs_open_func(obj_hdl, openflags, (struct fsal_fd *)out_fd); if (FSAL_IS_ERROR(status)) { LogDebug(COMPONENT_FSAL, "Failed with openflags 0x%08x", openflags); return status; } LogFullDebug(COMPONENT_FSAL, "Opened fd=%d for file of type %s", out_fd->fd, object_file_type_to_str(obj_hdl->type)); *fd = out_fd->fd; *closefd = true; return status; } static fsal_status_t gpfs_write_plus_fd(int my_fd, uint64_t offset, size_t buffer_size, void *buffer, size_t *write_amount, bool *fsal_stable, struct io_info *info, int expfd) { switch (info->io_content.what) { case NFS4_CONTENT_DATA: return GPFSFSAL_write(my_fd, offset, buffer_size, buffer, write_amount, fsal_stable, op_ctx, expfd); case NFS4_CONTENT_DEALLOCATE: return GPFSFSAL_alloc(my_fd, offset, buffer_size, false); case NFS4_CONTENT_ALLOCATE: return GPFSFSAL_alloc(my_fd, offset, buffer_size, true); default: return fsalstat(ERR_FSAL_UNION_NOTSUPP, 0); } } /** * @brief Read data from a file * * This function reads data from the given file. The FSAL must be able to * perform the read whether a state is presented or not. This function also * is expected to handle properly bypassing or not share reservations. * * @param[in] obj_hdl File on which to operate * @param[in] bypass If state doesn't indicate a share reservation, * bypass any deny read * @param[in] state state_t to use for this operation * @param[in] offset Position from which to read * @param[in] buffer_size Amount of data to read * @param[out] buffer Buffer to which data are to be copied * @param[out] read_amount Amount of data read * @param[out] end_of_file true if the end of file has been reached * @param[in,out] info more information about the data * * @return FSAL status. */ fsal_status_t gpfs_read2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buffer_size, void *buffer, size_t *read_amount, bool *end_of_file, struct io_info *info) { int my_fd = -1; fsal_status_t status; bool has_lock = false; bool closefd = false; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; struct gpfs_fd *gpfs_fd = NULL; 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); } /* Acquire state's fdlock to prevent OPEN upgrade closing the * file descriptor while we use it. */ if (state) { gpfs_fd = STATE2FD(state); PTHREAD_RWLOCK_rdlock(&gpfs_fd->fdlock); } /* Get a usable file descriptor */ status = find_fd(&my_fd, obj_hdl, bypass, state, FSAL_O_READ, &has_lock, &closefd, false); if (FSAL_IS_ERROR(status)) { LogDebug(COMPONENT_FSAL, "find_fd failed %s", msg_fsal_err(status.major)); if (gpfs_fd) PTHREAD_RWLOCK_unlock(&gpfs_fd->fdlock); return status; } if (info) status = gpfs_read_plus_fd(my_fd, offset, buffer_size, buffer, read_amount, end_of_file, info, export_fd); else status = GPFSFSAL_read(my_fd, offset, buffer_size, buffer, read_amount, end_of_file, export_fd); if (gpfs_fd) PTHREAD_RWLOCK_unlock(&gpfs_fd->fdlock); if (closefd) { fsal_status_t status2; status2 = fsal_internal_close(my_fd, NULL, 0); if (FSAL_IS_ERROR(status2)) { LogEvent(COMPONENT_FSAL, "fsal close failed, fd:%d, error: %s", my_fd, msg_fsal_err(status2.major)); } } if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /** * @brief Write data to a file * * This function writes data to a file. The FSAL must be able to * perform the write whether a state is presented or not. This function also * is expected to handle properly bypassing or not share reservations. Even * with bypass == true, it will enforce a mandatory (NFSv4) deny_write if * an appropriate state is not passed). * * The FSAL is expected to enforce sync if necessary. * * @param[in] obj_hdl File on which to operate * @param[in] bypass If state doesn't indicate a share reservation, * bypass any non-mandatory deny write * @param[in] state state_t to use for this operation * @param[in] offset Position at which to write * @param[in] buffer Data to be written * @param[in,out] fsal_stable In, if on, the fsal is requested to write data * to stable store. Out, the fsal reports what * it did. * @param[in,out] info more information about the data * * @return FSAL status. */ fsal_status_t gpfs_write2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buffer_size, void *buffer, size_t *wrote_amount, bool *fsal_stable, struct io_info *info) { fsal_status_t status; int my_fd = -1; bool has_lock = false; bool closefd = false; fsal_openflags_t openflags = FSAL_O_WRITE; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; struct gpfs_fd *gpfs_fd = NULL; 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); } /* Acquire state's fdlock to prevent OPEN upgrade closing the * file descriptor while we use it. */ if (state) { gpfs_fd = STATE2FD(state); PTHREAD_RWLOCK_rdlock(&gpfs_fd->fdlock); } /* Get a usable file descriptor */ status = find_fd(&my_fd, obj_hdl, bypass, state, openflags, &has_lock, &closefd, false); if (FSAL_IS_ERROR(status)) { LogDebug(COMPONENT_FSAL, "find_fd failed %s", msg_fsal_err(status.major)); if (gpfs_fd) PTHREAD_RWLOCK_unlock(&gpfs_fd->fdlock); return status; } if (info) status = gpfs_write_plus_fd(my_fd, offset, buffer_size, buffer, wrote_amount, fsal_stable, info, export_fd); else status = GPFSFSAL_write(my_fd, offset, buffer_size, buffer, wrote_amount, fsal_stable, op_ctx, export_fd); if (gpfs_fd) PTHREAD_RWLOCK_unlock(&gpfs_fd->fdlock); if (closefd) { fsal_status_t status2; status2 = fsal_internal_close(my_fd, NULL, 0); if (FSAL_IS_ERROR(status2)) { LogEvent(COMPONENT_FSAL, "fsal close failed, fd:%d, error: %s", my_fd, msg_fsal_err(status2.major)); } } if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } static fsal_status_t gpfs_commit_fd(int my_fd, struct fsal_obj_handle *obj_hdl, off_t offset, size_t len) { struct gpfs_fsal_obj_handle *myself = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); struct fsync_arg arg = {0}; verifier4 writeverf = {0}; int retval; assert(my_fd >= 0); arg.mountdirfd = my_fd; arg.handle = myself->handle; arg.offset = offset; arg.length = len; arg.verifier4 = (int32_t *) &writeverf; if (gpfs_ganesha(OPENHANDLE_FSYNC, &arg) == -1) { retval = errno; if (retval == EUNATCH) LogFatal(COMPONENT_FSAL, "GPFS Returned EUNATCH"); return fsalstat(posix2fsal_error(retval), retval); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Commit written data * * This function flushes possibly buffered data to a file. This method * differs from commit due to the need to interact with share reservations * and the fact that the FSAL manages the state of "file descriptors". The * FSAL must be able to perform this operation without being passed a specific * state. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * @param[in] offset Start of range to commit * @param[in] len Length of range to commit * * @return FSAL status. */ fsal_status_t gpfs_commit2(struct fsal_obj_handle *obj_hdl, off_t offset, size_t len) { fsal_status_t status; struct gpfs_fsal_obj_handle *myself; struct gpfs_fd temp_fd = { FSAL_O_CLOSED, PTHREAD_RWLOCK_INITIALIZER, -1 }; struct gpfs_fd *out_fd = &temp_fd; bool has_lock = false; bool closefd = false; myself = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); /* Make sure file is open in appropriate mode. * Do not check share reservation. */ status = fsal_reopen_obj(obj_hdl, false, false, FSAL_O_WRITE, (struct fsal_fd *)&myself->u.file.fd, &myself->u.file.share, gpfs_open_func, gpfs_close_func, (struct fsal_fd **)&out_fd, &has_lock, &closefd); if (!FSAL_IS_ERROR(status)) { fsal_set_credentials(op_ctx->creds); status = gpfs_commit_fd(out_fd->fd, obj_hdl, offset, len); fsal_restore_ganesha_credentials(); } if (closefd) fsal_internal_close(out_fd->fd, NULL, 0); if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /** * @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] req_lock Lock to take/release/test * @param[out] conflicting_lock Conflicting lock * * @return FSAL status. */ fsal_status_t gpfs_lock_op2(struct fsal_obj_handle *obj_hdl, struct state_t *state, void *owner, fsal_lock_op_t lock_op, fsal_lock_param_t *req_lock, fsal_lock_param_t *conflicting_lock) { struct fsal_export *export = op_ctx->fsal_export; struct glock glock_args; struct set_get_lock_arg gpfs_sg_arg; fsal_openflags_t openflags; fsal_status_t status; bool has_lock = false; bool closefd = false; bool bypass = false; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; struct gpfs_fd *gpfs_fd = NULL; LogFullDebug(COMPONENT_FSAL, "Locking: op:%d sle_type:%d type:%d start:%llu length:%llu owner:%p", lock_op, req_lock->lock_sle_type, req_lock->lock_type, (unsigned long long)req_lock->lock_start, (unsigned long long)req_lock->lock_length, owner); if (obj_hdl == NULL) { LogCrit(COMPONENT_FSAL, "obj_hdl arg is NULL."); return fsalstat(ERR_FSAL_FAULT, 0); } if (owner == NULL) { LogCrit(COMPONENT_FSAL, "owner arg is NULL."); return fsalstat(ERR_FSAL_FAULT, 0); } if (conflicting_lock == NULL && lock_op == FSAL_OP_LOCKT) { LogDebug(COMPONENT_FSAL, "Conflicting_lock argument can't be NULL with lock_op = LOCKT"); return fsalstat(ERR_FSAL_FAULT, 0); } if (lock_op != FSAL_OP_LOCKT && state == NULL) { LogCrit(COMPONENT_FSAL, "Non TEST operation with NULL state"); return fsalstat(posix2fsal_error(EINVAL), EINVAL); } /* 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 (req_lock->lock_length > LONG_MAX) { LogCrit(COMPONENT_FSAL, "Requested lock length is out of range- MAX(%"PRIu64 "), req_lock_length(%" PRIu64 ")", LONG_MAX, req_lock->lock_length); return fsalstat(ERR_FSAL_BAD_RANGE, 0); } switch (req_lock->lock_type) { case FSAL_LOCK_R: glock_args.flock.l_type = F_RDLCK; openflags = FSAL_O_READ; break; case FSAL_LOCK_W: glock_args.flock.l_type = F_WRLCK; openflags = FSAL_O_WRITE; break; default: LogDebug(COMPONENT_FSAL, "ERROR: The requested lock type was not read or write."); return fsalstat(ERR_FSAL_NOTSUPP, 0); } switch (lock_op) { case FSAL_OP_LOCKT: /* We may end up using global fd, don't fail on a deny mode */ bypass = true; glock_args.cmd = F_GETLK; openflags = FSAL_O_ANY; break; case FSAL_OP_UNLOCK: glock_args.flock.l_type = F_UNLCK; glock_args.cmd = F_SETLK; openflags = FSAL_O_ANY; break; case FSAL_OP_LOCK: glock_args.cmd = F_SETLK; break; case FSAL_OP_LOCKB: glock_args.cmd = F_SETLKW; break; case FSAL_OP_CANCEL: glock_args.cmd = GPFS_F_CANCELLK; openflags = FSAL_O_ANY; break; default: LogDebug(COMPONENT_FSAL, "ERROR: Lock operation requested was not TEST, GET, or SET."); return fsalstat(ERR_FSAL_NOTSUPP, 0); } /* Acquire state's fdlock to prevent OPEN upgrade closing the * file descriptor while we use it. */ if (state) { gpfs_fd = STATE2FD(state); PTHREAD_RWLOCK_rdlock(&gpfs_fd->fdlock); } /* Get a usable file descriptor */ status = find_fd(&glock_args.lfd, obj_hdl, bypass, state, openflags, &has_lock, &closefd, true); if (FSAL_IS_ERROR(status)) { LogDebug(COMPONENT_FSAL, "find_fd failed %s", msg_fsal_err(status.major)); if (gpfs_fd) PTHREAD_RWLOCK_unlock(&gpfs_fd->fdlock); return status; } glock_args.flock.l_len = req_lock->lock_length; glock_args.flock.l_start = req_lock->lock_start; glock_args.flock.l_whence = SEEK_SET; glock_args.lock_owner = owner; gpfs_sg_arg.lock = &glock_args; gpfs_sg_arg.reclaim = req_lock->lock_reclaim; gpfs_sg_arg.mountdirfd = export_fd; status = GPFSFSAL_lock_op(export, lock_op, req_lock, conflicting_lock, &gpfs_sg_arg); if (gpfs_fd) PTHREAD_RWLOCK_unlock(&gpfs_fd->fdlock); if (closefd) { fsal_status_t status2; status2 = fsal_internal_close(glock_args.lfd, NULL, 0); if (FSAL_IS_ERROR(status2)) { LogEvent(COMPONENT_FSAL, "fsal close failed, fd:%d, error: %s", glock_args.lfd, msg_fsal_err(status2.major)); } } if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /** * @brief GPFS seek command * * @param obj_hdl FSAL object handle * @param io_info I/O information * @return FSAL status * * default case not supported */ fsal_status_t gpfs_seek(struct fsal_obj_handle *obj_hdl, struct io_info *info) { struct gpfs_fsal_obj_handle *myself = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); struct gpfs_io_info io_info = {0}; struct fseek_arg arg = {0}; assert(myself->u.file.fd.fd >= 0 && myself->u.file.fd.openflags != FSAL_O_CLOSED); arg.mountdirfd = myself->u.file.fd.fd; arg.openfd = myself->u.file.fd.fd; arg.info = &io_info; io_info.io_offset = info->io_content.hole.di_offset; switch (info->io_content.what) { case NFS4_CONTENT_DATA: io_info.io_what = SEEK_DATA; break; case NFS4_CONTENT_HOLE: io_info.io_what = SEEK_HOLE; break; default: return fsalstat(ERR_FSAL_UNION_NOTSUPP, 0); } if (gpfs_ganesha(OPENHANDLE_SEEK_BY_FD, &arg) == -1) { if (errno == EUNATCH) LogFatal(COMPONENT_FSAL, "GPFS Returned EUNATCH"); return fsalstat(posix2fsal_error(errno), 0); } info->io_eof = io_info.io_eof; info->io_content.hole.di_offset = io_info.io_offset; info->io_content.hole.di_length = io_info.io_len; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief GPFS IO advise * * @param obj_hdl FSAL object handle * @param io_hints I/O information * @return FSAL status * */ fsal_status_t gpfs_io_advise(struct fsal_obj_handle *obj_hdl, struct io_hints *hints) { struct gpfs_fsal_obj_handle *myself = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); struct fadvise_arg arg = {0}; assert(myself->u.file.fd.fd >= 0 && myself->u.file.fd.openflags != FSAL_O_CLOSED); arg.mountdirfd = myself->u.file.fd.fd; arg.openfd = myself->u.file.fd.fd; arg.offset = hints->offset; arg.length = hints->count; arg.hints = &hints->hints; if (gpfs_ganesha(OPENHANDLE_FADVISE_BY_FD, &arg) == -1) { if (errno == EUNATCH) LogFatal(COMPONENT_FSAL, "GPFS Returned EUNATCH"); hints->hints = 0; return fsalstat(posix2fsal_error(errno), 0); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Close the file if it is still open. * * @param obj_hdl FSAL object handle * @return FSAL status * * Yes, we ignor lock status. Closing a file in POSIX * releases all locks but that is state and cache inode's problem. */ fsal_status_t gpfs_close(struct fsal_obj_handle *obj_hdl) { struct gpfs_fsal_obj_handle *myself = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); fsal_status_t status = {ERR_FSAL_NO_ERROR, 0}; assert(obj_hdl->type == REGULAR_FILE); /* Take write lock on object to protect file descriptor. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->obj_lock); if (myself->u.file.fd.fd >= 0 && myself->u.file.fd.openflags != FSAL_O_CLOSED) { status = fsal_internal_close(myself->u.file.fd.fd, NULL, 0); myself->u.file.fd.fd = -1; myself->u.file.fd.openflags = FSAL_O_CLOSED; } else { status = fsalstat(ERR_FSAL_NOT_OPENED, 0); } PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /** * @brief Manage closing a file when a state is no longer needed. * * When the upper layers are ready to dispense with a state, this method is * called to allow the FSAL to close any file descriptors or release any other * resources associated with the state. A call to free_state should be assumed * to follow soon. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * * @return FSAL status. */ fsal_status_t gpfs_close2(struct fsal_obj_handle *obj_hdl, struct state_t *state) { struct gpfs_fsal_obj_handle *myself; state_owner_t *state_owner = NULL; fsal_status_t status = {ERR_FSAL_NO_ERROR, 0}; struct gpfs_fd *my_fd = &container_of(state, struct gpfs_state_fd, state)->gpfs_fd; LogFullDebug(COMPONENT_FSAL, "state %p", state); myself = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); if (state->state_type == STATE_TYPE_SHARE || state->state_type == STATE_TYPE_NLM_SHARE || state->state_type == STATE_TYPE_9P_FID) { /* This is a share state, we must update the share counters */ /* This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->obj_lock); update_share_counters(&myself->u.file.share, my_fd->openflags, FSAL_O_CLOSED); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); } if (my_fd->fd > 0) { LogFullDebug(COMPONENT_FSAL, "state %p fd %d", state, my_fd->fd); state_owner = state->state_owner; status = fsal_internal_close(my_fd->fd, state_owner, 0); my_fd->fd = -1; my_fd->openflags = FSAL_O_CLOSED; } return status; } nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/fsal_attrs.c000066400000000000000000000322421324272410200210770ustar00rootroot00000000000000/** @file fsal_attrs.c * @brief GPFS FSAL attribute functions * * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ #include "config.h" #include "fsal.h" #include "fsal_internal.h" #include "fsal_convert.h" #include "gpfs_methods.h" #include #include #include #include #include "export_mgr.h" extern fsal_status_t fsal_acl_2_gpfs_acl(struct fsal_obj_handle *, fsal_acl_t *, gpfsfsal_xstat_t *, gpfs_acl_t *acl_buf, unsigned int acl_buflen); /** * @brief Get fs_locations attribute for the object specified by its * filehandle. * * @param export FSAL export * @param gpfs_fs GPFS filesystem * @param op_ctx Request op context * @param gpfs_fh GPFS file handle * @param obj_attr Object attributes * @param fs_locs FS locations * @return FSAL status * */ fsal_status_t GPFSFSAL_fs_loc(struct fsal_export *export, struct gpfs_filesystem *gpfs_fs, const struct req_op_context *op_ctx, struct gpfs_file_handle *gpfs_fh, struct fs_locations4 *fs_locs) { int errsv, rc; struct fs_loc_arg fs_loc; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; struct fs_location4 *loc_val = fs_locs->locations.locations_val; fs_loc.fs_path_len = fs_locs->fs_root.pathname4_val->utf8string_len; fs_loc.fs_path = fs_locs->fs_root.pathname4_val->utf8string_val; fs_loc.fs_server_len = loc_val->server.server_val->utf8string_len; fs_loc.fs_server = loc_val->server.server_val->utf8string_val; fs_loc.fs_root_len = loc_val->rootpath.pathname4_val->utf8string_len; fs_loc.fs_root = loc_val->rootpath.pathname4_val->utf8string_val; fs_loc.mountdirfd = export_fd; fs_loc.handle = gpfs_fh; rc = gpfs_ganesha(OPENHANDLE_FS_LOCATIONS, &fs_loc); errsv = errno; LogDebug(COMPONENT_FSAL, "gpfs_ganesha: FS_LOCATIONS returned, rc %d errsv %d", rc, errsv); if (rc) return fsalstat(ERR_FSAL_ATTRNOTSUPP, 0); fs_locs->fs_root.pathname4_val->utf8string_len = fs_loc.fs_path_len; loc_val->server.server_val->utf8string_len = fs_loc.fs_server_len; loc_val->rootpath.pathname4_val->utf8string_len = fs_loc.fs_root_len; LogDebug(COMPONENT_FSAL, "gpfs_ganesha: FS_LOCATIONS root=%s path=%s server=%s", fs_loc.fs_root, fs_loc.fs_path, fs_loc.fs_server); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Get attributes for the object specified by its filehandle. * * @param export FSAL export * @param gpfs_fs GPFS filesystem * @param op_ctx Request op context * @param gpfs_fh GPFS file handle * @param obj_attr Object attributes * @return FSAL status */ fsal_status_t GPFSFSAL_getattrs(struct fsal_export *export, struct gpfs_filesystem *gpfs_fs, const struct req_op_context *op_ctx, struct gpfs_file_handle *gpfs_fh, struct attrlist *obj_attr) { fsal_status_t st; gpfsfsal_xstat_t buffxstat; bool expire; uint32_t expire_time_attr = 0; /*< Expiration time for attributes. */ struct gpfs_fsal_export *gpfs_export; gpfs_acl_t *acl_buf; unsigned int acl_buflen; bool use_acl; int retry; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; /* Initialize fsal_fsid to 0.0 in case older GPFS */ buffxstat.fsal_fsid.major = 0; buffxstat.fsal_fsid.minor = 0; expire = atomic_fetch_uint32_t( &op_ctx->ctx_export->expire_time_attr) > 0; gpfs_export = container_of(export, struct gpfs_fsal_export, export); /* Let us make the first request using the acl buffer that * is part of buffxstat itself. If that is not sufficient, * we allocate from heap and retry. */ use_acl = obj_attr->request_mask & ATTR_ACL; for (retry = 0; retry < GPFS_ACL_MAX_RETRY; retry++) { switch (retry) { case 0: /* first attempt */ acl_buf = (gpfs_acl_t *)buffxstat.buffacl; acl_buflen = GPFS_ACL_BUF_SIZE; break; case 1: /* first retry, don't free the old stack buffer */ acl_buflen = acl_buf->acl_len; acl_buf = gsh_malloc(acl_buflen); break; default: /* second or later retry, free the old heap buffer */ acl_buflen = acl_buf->acl_len; gsh_free(acl_buf); acl_buf = gsh_malloc(acl_buflen); break; } st = fsal_get_xstat_by_handle(export_fd, gpfs_fh, &buffxstat, acl_buf, acl_buflen, &expire_time_attr, expire, use_acl); if (FSAL_IS_ERROR(st) || !use_acl || acl_buflen >= acl_buf->acl_len) break; } if (retry == GPFS_ACL_MAX_RETRY) { /* make up an error */ LogCrit(COMPONENT_FSAL, "unable to get ACLs"); st = fsalstat(ERR_FSAL_SERVERFAULT, 0); } if (FSAL_IS_ERROR(st)) goto error; /* convert attributes */ if (expire_time_attr != 0) obj_attr->expire_time_attr = expire_time_attr; /* Assume if fsid = 0.0, then old GPFS didn't fill it in, in that * case, fill in from the object's filesystem. */ if (buffxstat.fsal_fsid.major == 0 && buffxstat.fsal_fsid.minor == 0) buffxstat.fsal_fsid = gpfs_fs->fs->fsid; st = gpfsfsal_xstat_2_fsal_attributes(&buffxstat, obj_attr, acl_buf, gpfs_export->use_acl); error: if (FSAL_IS_ERROR(st)) { if (obj_attr->request_mask & ATTR_RDATTR_ERR) { /* Caller asked for error to be visible. */ obj_attr->valid_mask = ATTR_RDATTR_ERR; } } /* Free acl buffer if we allocated on heap */ if (acl_buflen != GPFS_ACL_BUF_SIZE) { assert(acl_buf != (gpfs_acl_t *)buffxstat.buffacl); gsh_free(acl_buf); } return st; } /** * @brief Get fs attributes for the object specified by its filehandle. * * @param mountdirfd Mounted filesystem * @param obj_hdl Object handle * @param buf reference to statfs structure * @return FSAL status * */ fsal_status_t GPFSFSAL_statfs(int mountdirfd, struct fsal_obj_handle *obj_hdl, struct statfs *buf) { int rc; struct statfs_arg sarg; struct gpfs_fsal_obj_handle *myself; int errsv; myself = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); sarg.handle = myself->handle; sarg.mountdirfd = mountdirfd; sarg.buf = buf; rc = gpfs_ganesha(OPENHANDLE_STATFS_BY_FH, &sarg); errsv = errno; LogFullDebug(COMPONENT_FSAL, "OPENHANDLE_STATFS_BY_FH returned: rc %d", rc); if (rc < 0) { if (errsv == EUNATCH) LogFatal(COMPONENT_FSAL, "GPFS Returned EUNATCH"); return fsalstat(posix2fsal_error(errsv), errsv); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Set attributes for the object specified by its filehandle. * * @param dir_hdl The handle of the object to get parameters. * @param ro_ctx Authentication context for the operation (user,...). * @param obj_attr 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 FSAL status */ fsal_status_t GPFSFSAL_setattrs(struct fsal_obj_handle *dir_hdl, const struct req_op_context *ro_ctx, struct attrlist *obj_attr) { fsal_status_t status; struct gpfs_fsal_obj_handle *myself; gpfsfsal_xstat_t buffxstat; struct gpfs_fsal_export *gpfs_export; gpfs_acl_t *acl_buf = NULL; unsigned int acl_buflen = 0; bool use_acl; struct gpfs_fsal_export *exp = container_of(ro_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; /* Indicate if stat or acl or both should be changed. */ int attr_valid = 0; /* Indicate which attribute in stat should be changed. */ int attr_changed = 0; myself = container_of(dir_hdl, struct gpfs_fsal_obj_handle, obj_handle); gpfs_export = container_of(ro_ctx->fsal_export, struct gpfs_fsal_export, export); use_acl = gpfs_export->use_acl; /* First, check that FSAL attributes changes are allowed. */ /* Is it allowed to change times ? */ if (!ro_ctx->fsal_export->exp_ops.fs_supports(ro_ctx->fsal_export, fso_cansettime)) { if (obj_attr->valid_mask & (ATTR_ATIME | ATTR_CREATION | ATTR_CTIME | ATTR_MTIME | ATTR_MTIME_SERVER | ATTR_ATIME_SERVER)) { /* handled as an unsettable attribute. */ return fsalstat(ERR_FSAL_INVAL, 0); } } /* apply umask, if mode attribute is to be changed */ if (FSAL_TEST_MASK(obj_attr->valid_mask, ATTR_MODE)) { obj_attr->mode &= ~ro_ctx->fsal_export->exp_ops.fs_umask(ro_ctx->fsal_export); } /************** * TRUNCATE * **************/ if (FSAL_TEST_MASK(obj_attr->valid_mask, ATTR_SIZE)) { attr_changed |= XATTR_SIZE; /* Fill wanted size. */ buffxstat.buffstat.st_size = obj_attr->filesize; LogDebug(COMPONENT_FSAL, "new size = %llu", (unsigned long long)buffxstat.buffstat.st_size); } /******************* * SPACE RESERVED * ************)******/ if (FSAL_TEST_MASK(obj_attr->valid_mask, ATTR4_SPACE_RESERVED)) { attr_changed |= XATTR_SPACE_RESERVED; /* Fill wanted space. */ buffxstat.buffstat.st_size = obj_attr->filesize; LogDebug(COMPONENT_FSAL, "new size = %llu", (unsigned long long)buffxstat.buffstat.st_size); } /*********** * CHMOD * ***********/ if (FSAL_TEST_MASK(obj_attr->valid_mask, ATTR_MODE)) { /* The POSIX chmod call don't affect the symlink object, but * the entry it points to. So we must ignore it. */ if (dir_hdl->type != SYMBOLIC_LINK) { attr_changed |= XATTR_MODE; /* Fill wanted mode. */ buffxstat.buffstat.st_mode = fsal2unix_mode(obj_attr->mode); LogDebug(COMPONENT_FSAL, "new mode = %o", buffxstat.buffstat.st_mode); } } /*********** * CHOWN * ***********/ /* Fill wanted owner. */ if (FSAL_TEST_MASK(obj_attr->valid_mask, ATTR_OWNER)) { attr_changed |= XATTR_UID; buffxstat.buffstat.st_uid = (int)obj_attr->owner; LogDebug(COMPONENT_FSAL, "new uid = %d", buffxstat.buffstat.st_uid); } /* Fill wanted group. */ if (FSAL_TEST_MASK(obj_attr->valid_mask, ATTR_GROUP)) { attr_changed |= XATTR_GID; buffxstat.buffstat.st_gid = (int)obj_attr->group; LogDebug(COMPONENT_FSAL, "new gid = %d", buffxstat.buffstat.st_gid); } /*********** * UTIME * ***********/ /* Fill wanted atime. */ if (FSAL_TEST_MASK(obj_attr->valid_mask, ATTR_ATIME)) { attr_changed |= XATTR_ATIME; buffxstat.buffstat.st_atime = (time_t) obj_attr->atime.tv_sec; buffxstat.buffstat.st_atim.tv_nsec = obj_attr->atime.tv_nsec; LogDebug(COMPONENT_FSAL, "new atime = %lu", (unsigned long)buffxstat.buffstat.st_atime); } /* Fill wanted mtime. */ if (FSAL_TEST_MASK(obj_attr->valid_mask, ATTR_MTIME)) { attr_changed |= XATTR_MTIME; buffxstat.buffstat.st_mtime = (time_t) obj_attr->mtime.tv_sec; buffxstat.buffstat.st_mtim.tv_nsec = obj_attr->mtime.tv_nsec; LogDebug(COMPONENT_FSAL, "new mtime = %lu", (unsigned long)buffxstat.buffstat.st_mtime); } /* Asking to set atime to NOW */ if (FSAL_TEST_MASK(obj_attr->valid_mask, ATTR_ATIME_SERVER)) { attr_changed |= XATTR_ATIME | XATTR_ATIME_NOW; LogDebug(COMPONENT_FSAL, "new atime = NOW"); } /* Asking to set atime to NOW */ if (FSAL_TEST_MASK(obj_attr->valid_mask, ATTR_MTIME_SERVER)) { attr_changed |= XATTR_MTIME | XATTR_MTIME_NOW; LogDebug(COMPONENT_FSAL, "new mtime = NOW"); } /* If any stat changed, indicate that */ if (attr_changed != 0) attr_valid |= XATTR_STAT; if (use_acl && FSAL_TEST_MASK(obj_attr->valid_mask, ATTR_ACL)) { if (!obj_attr->acl) { LogCrit(COMPONENT_FSAL, "setattr acl is NULL"); return fsalstat(ERR_FSAL_FAULT, 0); } attr_valid |= XATTR_ACL; LogDebug(COMPONENT_FSAL, "setattr acl = %p", obj_attr->acl); /* Convert FSAL ACL to GPFS NFS4 ACL and fill buffer. */ acl_buflen = offsetof(gpfs_acl_t, ace_v1) + obj_attr->acl->naces * sizeof(gpfs_ace_v4_t); if (acl_buflen > GPFS_ACL_BUF_SIZE) acl_buf = gsh_malloc(acl_buflen); else acl_buf = (gpfs_acl_t *)buffxstat.buffacl; status = fsal_acl_2_gpfs_acl(dir_hdl, obj_attr->acl, &buffxstat, acl_buf, acl_buflen); if (FSAL_IS_ERROR(status)) goto acl_free; } /* If there is any change in stat or acl or both, send it down to fs. */ if (attr_valid != 0) { status = fsal_set_xstat_by_handle(export_fd, ro_ctx, myself->handle, attr_valid, attr_changed, &buffxstat, acl_buf); if (FSAL_IS_ERROR(status)) goto acl_free; } status = fsalstat(ERR_FSAL_NO_ERROR, 0); acl_free: if (acl_buflen > GPFS_ACL_BUF_SIZE) gsh_free(acl_buf); return status; } nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/fsal_convert.c000066400000000000000000000311031324272410200214150ustar00rootroot00000000000000/** @file fsal_convert.c * @brief GPFS FSAL module convert functions * * vim:noexpandtab:shiftwidth=8:tabstop=8: * * GPFS-FSAL type translation functions. */ #include "config.h" #include "fsal_convert.h" #include "fsal_internal.h" #include "nfs4_acls.h" #include "include/gpfs.h" #include #include #include #include #include static fsal_status_t gpfs_acl_2_fsal_acl(struct attrlist *p_object_attributes, gpfs_acl_t *p_gpfsacl); /** * @brief convert GPFS xstat to FSAl attributes * * @param gpfs_buf Reference to GPFS stat buffer * @param fsal_attr Reference to attribute list * @param use_acl Bool whether ACL are used * @return FSAL status * * Same function as posixstat64_2_fsal_attributes. When NFS4 ACL support * is enabled, this will replace posixstat64_2_fsal_attributes. */ fsal_status_t gpfsfsal_xstat_2_fsal_attributes(gpfsfsal_xstat_t *gpfs_buf, struct attrlist *fsal_attr, gpfs_acl_t *acl_buf, bool use_acl) { struct stat *p_buffstat; /* sanity checks */ if (!gpfs_buf || !fsal_attr) return fsalstat(ERR_FSAL_FAULT, 0); fsal_attr->supported = GPFS_SUPPORTED_ATTRIBUTES; p_buffstat = &gpfs_buf->buffstat; LogDebug(COMPONENT_FSAL, "inode %" PRId64, p_buffstat->st_ino); /* Fills the output struct */ if (FSAL_TEST_MASK(fsal_attr->request_mask, ATTR_TYPE)) { fsal_attr->type = posix2fsal_type(p_buffstat->st_mode); fsal_attr->valid_mask |= ATTR_TYPE; LogFullDebug(COMPONENT_FSAL, "type = 0x%x", fsal_attr->type); } if (FSAL_TEST_MASK(fsal_attr->request_mask, ATTR_SIZE)) { fsal_attr->filesize = p_buffstat->st_size; fsal_attr->valid_mask |= ATTR_SIZE; LogFullDebug(COMPONENT_FSAL, "filesize = %llu", (unsigned long long)fsal_attr->filesize); } if (FSAL_TEST_MASK(fsal_attr->request_mask, ATTR_FSID)) { fsal_attr->fsid = gpfs_buf->fsal_fsid; fsal_attr->valid_mask |= ATTR_FSID; LogFullDebug(COMPONENT_FSAL, "fsid=0x%016"PRIx64".0x%016"PRIx64, fsal_attr->fsid.major, fsal_attr->fsid.minor); } if (FSAL_TEST_MASK(fsal_attr->request_mask, ATTR_ACL)) { if (fsal_attr->acl != NULL) { /* We should never be passed attributes that have an * ACL attached, but just in case some future code * path changes that assumption, let's not release the * old ACL properly. */ int acl_status; acl_status = nfs4_acl_release_entry(fsal_attr->acl); if (acl_status != NFS_V4_ACL_SUCCESS) LogCrit(COMPONENT_FSAL, "Failed to release old acl, status=%d", acl_status); fsal_attr->acl = NULL; } if (use_acl && gpfs_buf->attr_valid & XATTR_ACL) { /* ACL is valid, so try to convert fsal acl. */ fsal_status_t status = gpfs_acl_2_fsal_acl(fsal_attr, acl_buf); if (!FSAL_IS_ERROR(status)) { /* Only mark ACL valid if we actually provide * one in fsal_attr. */ fsal_attr->valid_mask |= ATTR_ACL; } else { /* Otherwise, we were asked for ACL and could * not provide one, so we must fail. */ return status; } } LogFullDebug(COMPONENT_FSAL, "acl = %p", fsal_attr->acl); } if (FSAL_TEST_MASK(fsal_attr->request_mask, ATTR_FILEID)) { fsal_attr->fileid = (uint64_t) (p_buffstat->st_ino); fsal_attr->valid_mask |= ATTR_FILEID; LogFullDebug(COMPONENT_FSAL, "fileid = %" PRIu64, fsal_attr->fileid); } if (FSAL_TEST_MASK(fsal_attr->request_mask, ATTR_MODE)) { fsal_attr->mode = unix2fsal_mode(p_buffstat->st_mode); fsal_attr->valid_mask |= ATTR_MODE; LogFullDebug(COMPONENT_FSAL, "mode = %"PRIu32, fsal_attr->mode); } if (FSAL_TEST_MASK(fsal_attr->request_mask, ATTR_NUMLINKS)) { fsal_attr->numlinks = p_buffstat->st_nlink; fsal_attr->valid_mask |= ATTR_NUMLINKS; LogFullDebug(COMPONENT_FSAL, "numlinks = %u", fsal_attr->numlinks); } if (FSAL_TEST_MASK(fsal_attr->request_mask, ATTR_OWNER)) { fsal_attr->owner = p_buffstat->st_uid; fsal_attr->valid_mask |= ATTR_OWNER; LogFullDebug(COMPONENT_FSAL, "owner = %" PRIu64, fsal_attr->owner); } if (FSAL_TEST_MASK(fsal_attr->request_mask, ATTR_GROUP)) { fsal_attr->group = p_buffstat->st_gid; fsal_attr->valid_mask |= ATTR_GROUP; LogFullDebug(COMPONENT_FSAL, "group = %" PRIu64, fsal_attr->group); } if (FSAL_TEST_MASK(fsal_attr->request_mask, ATTR_ATIME)) { fsal_attr->atime = posix2fsal_time(p_buffstat->st_atime, p_buffstat->st_atim.tv_nsec); fsal_attr->valid_mask |= ATTR_ATIME; LogFullDebug(COMPONENT_FSAL, "atime = %lu", fsal_attr->atime.tv_sec); } if (FSAL_TEST_MASK(fsal_attr->request_mask, ATTR_CTIME)) { fsal_attr->ctime = posix2fsal_time(p_buffstat->st_ctime, p_buffstat->st_ctim.tv_nsec); fsal_attr->valid_mask |= ATTR_CTIME; LogFullDebug(COMPONENT_FSAL, "ctime = %lu", fsal_attr->ctime.tv_sec); } if (FSAL_TEST_MASK(fsal_attr->request_mask, ATTR_MTIME)) { fsal_attr->mtime = posix2fsal_time(p_buffstat->st_mtime, p_buffstat->st_mtim.tv_nsec); fsal_attr->valid_mask |= ATTR_MTIME; LogFullDebug(COMPONENT_FSAL, "mtime = %lu", fsal_attr->mtime.tv_sec); } if (FSAL_TEST_MASK(fsal_attr->request_mask, ATTR_CHGTIME)) { if (p_buffstat->st_mtime == p_buffstat->st_ctime) { if (p_buffstat->st_mtim.tv_nsec > p_buffstat->st_ctim.tv_nsec) fsal_attr->chgtime = posix2fsal_time( p_buffstat->st_mtime, p_buffstat->st_mtim.tv_nsec); else fsal_attr->chgtime = posix2fsal_time( p_buffstat->st_ctime, p_buffstat->st_ctim.tv_nsec); } else if (p_buffstat->st_mtime > p_buffstat->st_ctime) { fsal_attr->chgtime = posix2fsal_time(p_buffstat->st_mtime, p_buffstat->st_mtim.tv_nsec); } else { fsal_attr->chgtime = posix2fsal_time(p_buffstat->st_ctime, p_buffstat->st_ctim.tv_nsec); } fsal_attr->change = (uint64_t) fsal_attr->chgtime.tv_sec + (uint64_t) fsal_attr->chgtime.tv_nsec; fsal_attr->valid_mask |= ATTR_CHGTIME; LogFullDebug(COMPONENT_FSAL, "chgtime = %lu", fsal_attr->chgtime.tv_sec); } if (FSAL_TEST_MASK(fsal_attr->request_mask, ATTR_SPACEUSED)) { fsal_attr->spaceused = p_buffstat->st_blocks * S_BLKSIZE; fsal_attr->valid_mask |= ATTR_SPACEUSED; LogFullDebug(COMPONENT_FSAL, "spaceused = %llu", (unsigned long long)fsal_attr->spaceused); } if (FSAL_TEST_MASK(fsal_attr->request_mask, ATTR_RAWDEV)) { fsal_attr->rawdev = posix2fsal_devt(p_buffstat->st_rdev); fsal_attr->valid_mask |= ATTR_RAWDEV; LogFullDebug(COMPONENT_FSAL, "rawdev major = %u, minor = %u", (unsigned int)fsal_attr->rawdev.major, (unsigned int)fsal_attr->rawdev.minor); } /* everything has been copied ! */ return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* Covert GPFS NFS4 ACLs to FSAL ACLs, and set the ACL * pointer of attribute. */ static fsal_status_t gpfs_acl_2_fsal_acl(struct attrlist *p_object_attributes, gpfs_acl_t *p_gpfsacl) { fsal_acl_status_t status; fsal_acl_data_t acldata; fsal_ace_t *pace; fsal_acl_t *pacl; gpfs_ace_v4_t *pace_gpfs; /* sanity checks */ if (!p_object_attributes || !p_gpfsacl) return fsalstat(ERR_FSAL_FAULT, 0); /* Create fsal acl data. */ acldata.naces = p_gpfsacl->acl_nace; acldata.aces = (fsal_ace_t *) nfs4_ace_alloc(acldata.naces); /* Fill fsal acl data from gpfs acl. */ for (pace = acldata.aces, pace_gpfs = p_gpfsacl->ace_v4; pace < acldata.aces + acldata.naces; pace++, pace_gpfs++) { pace->type = pace_gpfs->aceType; pace->flag = pace_gpfs->aceFlags; pace->iflag = pace_gpfs->aceIFlags; pace->perm = pace_gpfs->aceMask; if (IS_FSAL_ACE_SPECIAL_ID(*pace)) { /* Record special user. */ pace->who.uid = pace_gpfs->aceWho; } else { if (IS_FSAL_ACE_GROUP_ID(*pace)) pace->who.gid = pace_gpfs->aceWho; else /* Record user. */ pace->who.uid = pace_gpfs->aceWho; } LogMidDebug(COMPONENT_FSAL, "fsal ace: type = 0x%x, flag = 0x%x, perm = 0x%x, special = %d, %s = 0x%x", pace->type, pace->flag, pace->perm, IS_FSAL_ACE_SPECIAL_ID(*pace), GET_FSAL_ACE_WHO_TYPE(*pace), GET_FSAL_ACE_WHO(*pace)); } /* Create a new hash table entry for fsal acl. */ pacl = nfs4_acl_new_entry(&acldata, &status); LogMidDebug(COMPONENT_FSAL, "fsal acl = %p, fsal_acl_status = %u", pacl, status); if (pacl == NULL) { LogCrit(COMPONENT_FSAL, "failed to create a new acl entry"); return fsalstat(ERR_FSAL_FAULT, 0); } /* Add fsal acl to attribute. */ p_object_attributes->acl = pacl; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** @fn fsal_status_t * fsal_acl_2_gpfs_acl(struct fsal_obj_handle *dir_hdl, * fsal_acl_t *fsal_acl, * gpfsfsal_xstat_t *gpfs_buf) * @param dir_hdl Object handle * @param fsal_acl GPFS access control list * @param gpfs_buf Reference to GPFS stat buffer * @return FSAL status * * @brief Covert FSAL ACLs to GPFS NFS4 ACLs. */ fsal_status_t fsal_acl_2_gpfs_acl(struct fsal_obj_handle *dir_hdl, fsal_acl_t *fsal_acl, gpfsfsal_xstat_t *gpfs_buf, gpfs_acl_t *acl_buf, unsigned int acl_buflen) { int i; fsal_ace_t *pace; acl_buf->acl_level = 0; acl_buf->acl_version = GPFS_ACL_VERSION_NFS4; acl_buf->acl_type = GPFS_ACL_TYPE_NFS4; acl_buf->acl_nace = fsal_acl->naces; acl_buf->acl_len = acl_buflen; for (pace = fsal_acl->aces, i = 0; pace < fsal_acl->aces + fsal_acl->naces; pace++, i++) { acl_buf->ace_v4[i].aceType = pace->type; acl_buf->ace_v4[i].aceFlags = pace->flag; acl_buf->ace_v4[i].aceIFlags = pace->iflag; acl_buf->ace_v4[i].aceMask = pace->perm; if (IS_FSAL_ACE_SPECIAL_ID(*pace)) acl_buf->ace_v4[i].aceWho = pace->who.uid; else { if (IS_FSAL_ACE_GROUP_ID(*pace)) acl_buf->ace_v4[i].aceWho = pace->who.gid; else acl_buf->ace_v4[i].aceWho = pace->who.uid; } LogMidDebug(COMPONENT_FSAL, "gpfs ace: type = 0x%x, flag = 0x%x, perm = 0x%x, special = %d, %s = 0x%x", acl_buf->ace_v4[i].aceType, acl_buf->ace_v4[i].aceFlags, acl_buf->ace_v4[i].aceMask, (acl_buf->ace_v4[i].aceIFlags & FSAL_ACE_IFLAG_SPECIAL_ID) ? 1 : 0, (acl_buf->ace_v4[i].aceFlags & FSAL_ACE_FLAG_GROUP_ID) ? "gid" : "uid", acl_buf->ace_v4[i].aceWho); /* It is invalid to set inherit flags on non dir objects */ if (dir_hdl->type != DIRECTORY && (acl_buf->ace_v4[i].aceFlags & FSAL_ACE_FLAG_INHERIT) != 0) { LogMidDebug(COMPONENT_FSAL, "attempt to set inherit flag to non dir object"); return fsalstat(ERR_FSAL_INVAL, 0); } /* It is invalid to set inherit only with * out an actual inherit flag */ if ((acl_buf->ace_v4[i].aceFlags & FSAL_ACE_FLAG_INHERIT) == FSAL_ACE_FLAG_INHERIT_ONLY) { LogMidDebug(COMPONENT_FSAL, "attempt to set inherit only without an inherit flag"); return fsalstat(ERR_FSAL_INVAL, 0); } } return fsalstat(ERR_FSAL_NO_ERROR, 0); } fsal_status_t fsal_cred_2_gpfs_cred(struct user_cred *p_fsalcred, struct xstat_cred_t *p_gpfscred) { int i; if (!p_fsalcred || !p_gpfscred) return fsalstat(ERR_FSAL_FAULT, 0); p_gpfscred->principal = p_fsalcred->caller_uid; p_gpfscred->group = p_fsalcred->caller_gid; p_gpfscred->num_groups = p_fsalcred->caller_glen; for (i = 0; i < p_fsalcred->caller_glen; i++) p_gpfscred->eGroups[i] = p_fsalcred->caller_garray[i]; return fsalstat(ERR_FSAL_NO_ERROR, 0); } fsal_status_t fsal_mode_2_gpfs_mode(mode_t fsal_mode, fsal_accessflags_t v4mask, unsigned int *p_gpfsmode, bool is_dir) { int gpfs_mode = 0; if (!p_gpfsmode) return fsalstat(ERR_FSAL_FAULT, 0); /* If mode is zero, translate v4mask to posix mode. */ if (fsal_mode == 0) { if (!is_dir) { if (v4mask & FSAL_ACE_PERM_READ_DATA) gpfs_mode |= FSAL_R_OK; } else { if (v4mask & FSAL_ACE_PERM_LIST_DIR) { gpfs_mode |= FSAL_R_OK; gpfs_mode |= FSAL_X_OK; } } if (!is_dir) { if (v4mask & FSAL_ACE_PERM_WRITE_DATA) gpfs_mode |= FSAL_W_OK; } else { if (v4mask & FSAL_ACE_PERM_ADD_FILE) { gpfs_mode |= FSAL_W_OK; gpfs_mode |= FSAL_X_OK; } } if (!is_dir) { if (v4mask & FSAL_ACE_PERM_APPEND_DATA) gpfs_mode |= FSAL_W_OK; } else { if (v4mask & FSAL_ACE_PERM_ADD_SUBDIRECTORY) { gpfs_mode |= FSAL_W_OK; gpfs_mode |= FSAL_X_OK; } } if (!is_dir) { if (v4mask & FSAL_ACE_PERM_EXECUTE) gpfs_mode |= FSAL_X_OK; } else { if (v4mask & FSAL_ACE_PERM_DELETE_CHILD) { gpfs_mode |= FSAL_W_OK; gpfs_mode |= FSAL_X_OK; } } if (v4mask & FSAL_ACE_PERM_DELETE) gpfs_mode |= FSAL_W_OK; gpfs_mode = gpfs_mode >> 24; } else { gpfs_mode = fsal_mode >> 24; } LogMidDebug(COMPONENT_FSAL, "fsal_mode 0x%x, v4mask 0x%x, is_dir %d converted to gpfs_mode 0x%x", fsal_mode, v4mask, is_dir, gpfs_mode); *p_gpfsmode = gpfs_mode; return fsalstat(ERR_FSAL_NO_ERROR, 0); } nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/fsal_create.c000066400000000000000000000167171324272410200212160ustar00rootroot00000000000000/** @file fsal_create * @brief GPFS FSAL Filesystem objects creation functions * * vim:noexpandtab:shiftwidth=8:tabstop=8: * */ #include "config.h" #include "fsal.h" #include "fsal_internal.h" #include "fsal_convert.h" #include "gpfs_methods.h" #include #include #include #include "FSAL/access_check.h" /** * @brief Create a regular file. * * @param dir_hdl Handle of parent directory where the file is to be created. * @param filename Pointer to the name of the file to be created. * @param op_ctx Authentication context for the operation (user,...). * @param accessmode Mode for the file to be created. * @param gpfs_fh Pointer to the handle of the created file. * @param fsal_attr Attributes of the created file. * @return ERR_FSAL_NO_ERROR on success, otherwise error * */ fsal_status_t GPFSFSAL_create(struct fsal_obj_handle *dir_hdl, const char *filename, const struct req_op_context *op_ctx, uint32_t accessmode, struct gpfs_file_handle *gpfs_fh, struct attrlist *fsal_attr) { fsal_status_t status; mode_t unix_mode; /* note : fsal_attr is optional. */ if (!dir_hdl || !op_ctx || !gpfs_fh || !filename) return fsalstat(ERR_FSAL_FAULT, 0); /* convert fsal mode to unix mode. */ unix_mode = fsal2unix_mode(accessmode); /* Apply umask */ unix_mode = unix_mode & ~op_ctx->fsal_export->exp_ops.fs_umask(op_ctx->fsal_export); LogFullDebug(COMPONENT_FSAL, "Creation mode: 0%o", accessmode); /* call to filesystem */ fsal_set_credentials(op_ctx->creds); status = fsal_internal_create(dir_hdl, filename, unix_mode | S_IFREG, 0, gpfs_fh, NULL); fsal_restore_ganesha_credentials(); if (FSAL_IS_ERROR(status)) return status; /* retrieve file attributes */ return GPFSFSAL_getattrs(op_ctx->fsal_export, dir_hdl->fs->private_data, op_ctx, gpfs_fh, fsal_attr); } fsal_status_t GPFSFSAL_create2(struct fsal_obj_handle *dir_hdl, const char *filename, const struct req_op_context *op_ctx, mode_t unix_mode, struct gpfs_file_handle *gpfs_fh, int posix_flags, struct attrlist *fsal_attr) { fsal_status_t status; /* note : fsal_attr is optional. */ if (!dir_hdl || !op_ctx || !gpfs_fh || !filename) return fsalstat(ERR_FSAL_FAULT, 0); LogFullDebug(COMPONENT_FSAL, "Creation mode: 0%o", unix_mode); /* call to filesystem */ fsal_set_credentials(op_ctx->creds); status = fsal_internal_create(dir_hdl, filename, unix_mode | S_IFREG, posix_flags, gpfs_fh, NULL); fsal_restore_ganesha_credentials(); if (!FSAL_IS_ERROR(status) && fsal_attr != NULL) { /* retrieve file attributes */ status = GPFSFSAL_getattrs(op_ctx->fsal_export, dir_hdl->fs->private_data, op_ctx, gpfs_fh, fsal_attr); } return status; } /** * @brief Create a directory. * * @param dir_hdl Handle of the parent directory * @param dir_name Pointer to the name of the directory to be created. * @param op_ctx Authentication context for the operation (user,...). * @param accessmode Mode for the directory to be created. * @param gpfs_fh Pointer to the handle of the created directory. * @param fsal_attr Attributes of the created directory. * @return ERR_FSAL_NO_ERROR on success, error otherwise * */ fsal_status_t GPFSFSAL_mkdir(struct fsal_obj_handle *dir_hdl, const char *dir_name, const struct req_op_context *op_ctx, uint32_t accessmode, struct gpfs_file_handle *gpfs_fh, struct attrlist *obj_attr) { mode_t unix_mode; fsal_status_t status; /* note : obj_attr is optional. */ if (!dir_hdl || !op_ctx || !gpfs_fh || !dir_name) return fsalstat(ERR_FSAL_FAULT, 0); /* convert FSAL mode to unix mode. */ unix_mode = fsal2unix_mode(accessmode); /* Apply umask */ unix_mode = unix_mode & ~op_ctx->fsal_export->exp_ops.fs_umask(op_ctx->fsal_export); /* build new entry path */ /* creates the directory and get its handle */ fsal_set_credentials(op_ctx->creds); status = fsal_internal_create(dir_hdl, dir_name, unix_mode | S_IFDIR, 0, gpfs_fh, NULL); fsal_restore_ganesha_credentials(); if (FSAL_IS_ERROR(status)) return status; /* retrieve file attributes */ return GPFSFSAL_getattrs(op_ctx->fsal_export, dir_hdl->fs->private_data, op_ctx, gpfs_fh, obj_attr); } /** * @brief Create a hardlink. * * @param dir_hdl Handle of the target object. * @param gpfs_fh Pointer to the dire handle where hardlink is to be created. * @param linkname Pointer to the name of the hardlink to be created. * @param op_ctx Authentication context for the operation (user,...). * @return ERR_FSAL_NO_ERROR on success, error otherwise * */ fsal_status_t GPFSFSAL_link(struct fsal_obj_handle *dir_hdl, struct gpfs_file_handle *gpfs_fh, const char *linkname, const struct req_op_context *op_ctx) { fsal_status_t status; struct gpfs_fsal_obj_handle *dest_dir; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; dest_dir = container_of(dir_hdl, struct gpfs_fsal_obj_handle, obj_handle); /* Tests if hardlinking is allowed by configuration. */ if (!op_ctx->fsal_export->exp_ops.fs_supports(op_ctx->fsal_export, fso_link_support)) return fsalstat(ERR_FSAL_NOTSUPP, 0); /* Create the link on the filesystem */ fsal_set_credentials(op_ctx->creds); status = fsal_internal_link_fh(export_fd, gpfs_fh, dest_dir->handle, linkname); fsal_restore_ganesha_credentials(); return status; } /** * @brief Create a special object in the filesystem. * * @param dir_hdl Handle of the parent dir where the file is to be created. * @param node_name Pointer to the name of the file to be created. * @param op_ctx Authentication context for the operation (user,...). * @param accessmode Mode for the file to be created. * @param node_type Type of file to create. * @param dev Device id of file to create. * @param gpfs_fh Pointer to the handle of the created file. * @param fsal_attr Attributes of the created file. * @return ERR_FSAL_NO_ERROR on success, error otherwise * */ fsal_status_t GPFSFSAL_mknode(struct fsal_obj_handle *dir_hdl, const char *node_name, const struct req_op_context *op_ctx, uint32_t accessmode, mode_t nodetype, fsal_dev_t *dev, struct gpfs_file_handle *gpfs_fh, struct attrlist *fsal_attr) { fsal_status_t status; mode_t unix_mode = 0; dev_t unix_dev = 0; /* note : fsal_attr is optional. */ if (!dir_hdl || !op_ctx || !node_name) return fsalstat(ERR_FSAL_FAULT, 0); unix_mode = fsal2unix_mode(accessmode); /* Apply umask */ unix_mode = unix_mode & ~op_ctx->fsal_export->exp_ops.fs_umask(op_ctx->fsal_export); switch (nodetype) { case BLOCK_FILE: if (!dev) return fsalstat(ERR_FSAL_FAULT, 0); unix_mode |= S_IFBLK; unix_dev = (dev->major << 20) | (dev->minor & 0xFFFFF); break; case CHARACTER_FILE: if (!dev) return fsalstat(ERR_FSAL_FAULT, 0); unix_mode |= S_IFCHR; unix_dev = (dev->major << 20) | (dev->minor & 0xFFFFF); break; case SOCKET_FILE: unix_mode |= S_IFSOCK; break; case FIFO_FILE: unix_mode |= S_IFIFO; break; default: LogMajor(COMPONENT_FSAL, "Invalid node type in FSAL_mknode: %d", nodetype); return fsalstat(ERR_FSAL_INVAL, 0); } fsal_set_credentials(op_ctx->creds); status = fsal_internal_mknode(dir_hdl, node_name, unix_mode, unix_dev, gpfs_fh, NULL); fsal_restore_ganesha_credentials(); if (FSAL_IS_ERROR(status)) return status; /* Fills the attributes */ return GPFSFSAL_getattrs(op_ctx->fsal_export, dir_hdl->fs->private_data, op_ctx, gpfs_fh, fsal_attr); } nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/fsal_ds.c000066400000000000000000000421201324272410200203440ustar00rootroot00000000000000/* * Copyright © 2012 CohortFS, LLC. * Author: Adam C. Emerson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @file ds.c * @author Adam C. Emerson * @date Mon Jul 30 12:29:22 2012 * * @brief pNFS DS operations for GPFS * * This file implements the read, write, commit, and dispose * operations for GPFS data-server handles. * * Also, creating a data server handle -- now called via the DS itself. */ #include "config.h" #include #include "fsal_api.h" #include "FSAL/fsal_commonlib.h" #include "../fsal_private.h" #include "fsal_convert.h" #include "fsal_internal.h" #include "gpfs_methods.h" #include "pnfs_utils.h" #include "nfs_creds.h" #define min(a, b) \ ({ typeof(a) _a = (a); \ typeof(b) _b = (b); \ _a < _b ? _a : _b; }) /** * @brief Release a DS handle * * @param[in] ds_pub The object to release */ static void ds_release(struct fsal_ds_handle *const ds_pub) { /* The private 'full' DS handle */ struct gpfs_ds *ds = container_of(ds_pub, struct gpfs_ds, ds); fsal_ds_handle_fini(&ds->ds); gsh_free(ds); } /** * @brief Read from a data-server handle. * * NFSv4.1 data server handles are disjount from normal * filehandles (in Ganesha, there is a ds_flag in the filehandle_v4_t * structure) and do not get loaded into cache_inode or processed the * normal way. * * @param[in] ds_pub FSAL DS handle * @param[in] req_ctx Credentials * @param[in] stateid The stateid supplied with the READ operation, * for validation * @param[in] offset The offset at which to read * @param[in] requested_length Length of read requested (and size of buffer) * @param[out] buffer The buffer to which to store read data * @param[out] supplied_length Length of data read * @param[out] eof True on end of file * * @return An NFSv4.1 status code. */ static nfsstat4 ds_read(struct fsal_ds_handle *const ds_pub, struct req_op_context *const req_ctx, const stateid4 *stateid, const offset4 offset, const count4 requested_length, void *const buffer, count4 * const supplied_length, bool * const end_of_file) { /* The private 'full' DS handle */ struct gpfs_ds *ds = container_of(ds_pub, struct gpfs_ds, ds); struct gpfs_file_handle *gpfs_handle = &ds->wire; /* The amount actually read */ int amount_read = 0; struct dsread_arg rarg; unsigned int *fh; int errsv = 0; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; fh = (int *)&(gpfs_handle->f_handle); rarg.mountdirfd = export_fd; rarg.handle = gpfs_handle; rarg.bufP = buffer; rarg.offset = offset; rarg.length = requested_length; rarg.options = 0; LogDebug(COMPONENT_PNFS, "fh len %d type %d key %d: %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", gpfs_handle->handle_size, gpfs_handle->handle_type, gpfs_handle->handle_key_size, fh[0], fh[1], fh[2], fh[3], fh[4], fh[5], fh[6], fh[7], fh[8], fh[9]); amount_read = gpfs_ganesha(OPENHANDLE_DS_READ, &rarg); errsv = errno; if (amount_read < 0) { if (errsv == EUNATCH) LogFatal(COMPONENT_PNFS, "GPFS Returned EUNATCH"); return posix2nfs4_error(errsv); } *supplied_length = amount_read; if (amount_read == 0 || amount_read < requested_length) *end_of_file = true; return NFS4_OK; } /** * @brief Read plus from a data-server handle. * * NFSv4.2 data server handles are disjount from normal * filehandles (in Ganesha, there is a ds_flag in the filehandle_v4_t * structure) and do not get loaded into cache_inode or processed the * normal way. * * @param[in] ds_pub FSAL DS handle * @param[in] req_ctx Credentials * @param[in] stateid The stateid supplied with the READ operation, * for validation * @param[in] offset The offset at which to read * @param[in] requested_length Length of read requested (and size of buffer) * @param[out] buffer The buffer to which to store read data * @param[out] supplied_length Length of data read * @param[out] eof True on end of file * @param[out] info IO info * * @return An NFSv4.2 status code. */ static nfsstat4 ds_read_plus(struct fsal_ds_handle *const ds_pub, struct req_op_context *const req_ctx, const stateid4 *stateid, const offset4 offset, const count4 requested_length, void *const buffer, const count4 supplied_length, bool * const end_of_file, struct io_info *info) { /* The private 'full' DS handle */ struct gpfs_ds *ds = container_of(ds_pub, struct gpfs_ds, ds); struct gpfs_file_handle *gpfs_handle = &ds->wire; /* The amount actually read */ int amount_read = 0; struct dsread_arg rarg; unsigned int *fh; uint64_t filesize; int errsv = 0; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; fh = (int *)&(gpfs_handle->f_handle); rarg.mountdirfd = export_fd; rarg.handle = gpfs_handle; rarg.bufP = buffer; rarg.offset = offset; rarg.length = requested_length; rarg.filesize = &filesize; rarg.options = IO_SKIP_HOLE; LogDebug(COMPONENT_PNFS, "fh len %d type %d key %d: %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", gpfs_handle->handle_size, gpfs_handle->handle_type, gpfs_handle->handle_key_size, fh[0], fh[1], fh[2], fh[3], fh[4], fh[5], fh[6], fh[7], fh[8], fh[9]); amount_read = gpfs_ganesha(OPENHANDLE_DS_READ, &rarg); errsv = errno; if (amount_read < 0) { if (errsv == EUNATCH) LogFatal(COMPONENT_PNFS, "GPFS Returned EUNATCH"); if (errsv != ENODATA) return posix2nfs4_error(errsv); /* errsv == ENODATA */ info->io_content.what = NFS4_CONTENT_HOLE; info->io_content.hole.di_offset = offset; /*offset of hole*/ info->io_content.hole.di_length = requested_length;/*hole len*/ if ((requested_length + offset) > filesize) { amount_read = filesize - offset; if (amount_read < 0) { amount_read = 0; *end_of_file = true; } else if (amount_read < requested_length) *end_of_file = true; info->io_content.hole.di_length = amount_read; } } else { info->io_content.what = NFS4_CONTENT_DATA; info->io_content.data.d_offset = offset + amount_read; info->io_content.data.d_data.data_len = amount_read; info->io_content.data.d_data.data_val = buffer; if (amount_read == 0 || amount_read < requested_length) *end_of_file = true; } return NFS4_OK; } /** * * @brief Write to a data-server handle. * * This performs a DS write not going through the data server unless * FILE_SYNC4 is specified, in which case it connects the filehandle * and performs an MDS write. * * @param[in] ds_pub FSAL DS handle * @param[in] req_ctx Credentials * @param[in] stateid The stateid supplied with the READ operation, * for validation * @param[in] offset The offset at which to read * @param[in] write_length Length of write requested (and size of buffer) * @param[out] buffer The buffer to which to store read data * @param[in] stability wanted Stability of write * @param[out] written_length Length of data written * @param[out] writeverf Write verifier * @param[out] stability_got Stability used for write (must be as * or more stable than request) * * @return An NFSv4.1 status code. */ static nfsstat4 ds_write(struct fsal_ds_handle *const ds_pub, struct req_op_context *const req_ctx, const stateid4 *stateid, const offset4 offset, const count4 write_length, const void *buffer, const stable_how4 stability_wanted, count4 * const written_length, verifier4 * const writeverf, stable_how4 * const stability_got) { /* The private 'full' DS handle */ struct gpfs_ds *ds = container_of(ds_pub, struct gpfs_ds, ds); struct gpfs_file_handle *gpfs_handle = &ds->wire; /* The amount actually read */ int32_t amount_written = 0; struct dswrite_arg warg; unsigned int *fh; struct gsh_buffdesc key; int errsv = 0; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; fh = (int *)&(gpfs_handle->f_handle); memset(writeverf, 0, NFS4_VERIFIER_SIZE); warg.mountdirfd = export_fd; warg.handle = gpfs_handle; warg.bufP = (char *)buffer; warg.offset = offset; warg.length = write_length; warg.stability_wanted = stability_wanted; warg.stability_got = stability_got; warg.verifier4 = (int32_t *) writeverf; warg.options = 0; LogDebug(COMPONENT_PNFS, "fh len %d type %d key %d: %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", gpfs_handle->handle_size, gpfs_handle->handle_type, gpfs_handle->handle_key_size, fh[0], fh[1], fh[2], fh[3], fh[4], fh[5], fh[6], fh[7], fh[8], fh[9]); amount_written = gpfs_ganesha(OPENHANDLE_DS_WRITE, &warg); errsv = errno; if (amount_written < 0) { if (errsv == EUNATCH) LogFatal(COMPONENT_PNFS, "GPFS Returned EUNATCH"); return posix2nfs4_error(errsv); } LogDebug(COMPONENT_PNFS, "write verifier %d-%d\n", warg.verifier4[0], warg.verifier4[1]); key.addr = gpfs_handle; key.len = gpfs_handle->handle_key_size; req_ctx->fsal_export->up_ops->invalidate( req_ctx->fsal_export->up_ops, &key, FSAL_UP_INVALIDATE_CACHE); *written_length = amount_written; return NFS4_OK; } /** * * @brief Write plus to a data-server handle. * * This performs a DS write not going through the data server unless * FILE_SYNC4 is specified, in which case it connects the filehandle * and performs an MDS write. * * @param[in] ds_pub FSAL DS handle * @param[in] req_ctx Credentials * @param[in] stateid The stateid supplied with the READ operation, * for validation * @param[in] offset The offset at which to read * @param[in] write_length Length of write requested (and size of buffer) * @param[out] buffer The buffer to which to store read data * @param[in] stability wanted Stability of write * @param[out] written_length Length of data written * @param[out] writeverf Write verifier * @param[out] stability_got Stability used for write (must be as * or more stable than request) * @param[in/out] info IO info * * @return An NFSv4.2 status code. */ static nfsstat4 ds_write_plus(struct fsal_ds_handle *const ds_pub, struct req_op_context *const req_ctx, const stateid4 *stateid, const offset4 offset, const count4 write_length, const void *buffer, const stable_how4 stability_wanted, count4 * const written_length, verifier4 * const writeverf, stable_how4 * const stability_got, struct io_info *info) { /* The private 'full' DS handle */ struct gpfs_ds *ds = container_of(ds_pub, struct gpfs_ds, ds); struct gpfs_file_handle *gpfs_handle = &ds->wire; /* The amount actually read */ int32_t amount_written = 0; struct dswrite_arg warg; unsigned int *fh; struct gsh_buffdesc key; int errsv = 0; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; fh = (int *)&(gpfs_handle->f_handle); memset(writeverf, 0, NFS4_VERIFIER_SIZE); warg.mountdirfd = export_fd; warg.handle = gpfs_handle; warg.bufP = (char *)buffer; warg.offset = offset; warg.length = write_length; warg.stability_wanted = stability_wanted; warg.stability_got = stability_got; warg.verifier4 = (int32_t *) writeverf; warg.options = 0; if (info->io_content.what == NFS4_CONTENT_HOLE) warg.options = IO_SKIP_HOLE; LogDebug(COMPONENT_PNFS, "fh len %d type %d key %d: %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", gpfs_handle->handle_size, gpfs_handle->handle_type, gpfs_handle->handle_key_size, fh[0], fh[1], fh[2], fh[3], fh[4], fh[5], fh[6], fh[7], fh[8], fh[9]); amount_written = gpfs_ganesha(OPENHANDLE_DS_WRITE, &warg); errsv = errno; if (amount_written < 0) { if (errsv == EUNATCH) LogFatal(COMPONENT_PNFS, "GPFS Returned EUNATCH"); return posix2nfs4_error(errsv); } LogDebug(COMPONENT_PNFS, "write verifier %d-%d\n", warg.verifier4[0], warg.verifier4[1]); key.addr = gpfs_handle; key.len = gpfs_handle->handle_key_size; req_ctx->fsal_export->up_ops->invalidate( req_ctx->fsal_export->up_ops, &key, FSAL_UP_INVALIDATE_CACHE); *written_length = amount_written; return NFS4_OK; } /** * @brief Commit a byte range to a DS handle. * * NFSv4.1 data server filehandles are disjount from normal * filehandles (in Ganesha, there is a ds_flag in the filehandle_v4_t * structure) and do not get loaded into cache_inode or processed the * normal way. * * @param[in] ds_pub FSAL DS handle * @param[in] req_ctx Credentials * @param[in] offset Start of commit window * @param[in] count Length of commit window * @param[out] writeverf Write verifier * * @return An NFSv4.1 status code. */ static nfsstat4 ds_commit(struct fsal_ds_handle *const ds_pub, struct req_op_context *const req_ctx, const offset4 offset, const count4 count, verifier4 * const writeverf) { memset(writeverf, 0, NFS4_VERIFIER_SIZE); LogCrit(COMPONENT_PNFS, "Commits should go to MDS\n"); /* GPFS asked for COMMIT to go to the MDS */ return NFS4ERR_INVAL; } static void dsh_ops_init(struct fsal_dsh_ops *ops) { /* redundant copy, but you never know about the future... */ memcpy(ops, &def_dsh_ops, sizeof(struct fsal_dsh_ops)); ops->release = ds_release; ops->read = ds_read; ops->read_plus = ds_read_plus; ops->write = ds_write; ops->write_plus = ds_write_plus; ops->commit = ds_commit; } /** * @brief Try to create a FSAL data server handle from a wire handle * * This function creates a FSAL data server handle from a client * supplied "wire" handle. This is also where validation gets done, * since PUTFH is the only operation that can return * NFS4ERR_BADHANDLE. * * @param[in] pds FSAL pNFS DS * @param[in] desc Buffer from which to create the file * @param[out] handle FSAL DS handle * * @return NFSv4.1 error codes. */ static nfsstat4 make_ds_handle(struct fsal_pnfs_ds *const pds, const struct gsh_buffdesc *const desc, struct fsal_ds_handle **const handle, int flags) { struct gpfs_file_handle *fh = (struct gpfs_file_handle *)desc->addr; struct gpfs_ds *ds; /* Handle to be created */ struct fsal_filesystem *fs; struct fsal_fsid__ fsid; *handle = NULL; if (desc->len != sizeof(struct gpfs_file_handle)) return NFS4ERR_BADHANDLE; if (flags & FH_FSAL_BIG_ENDIAN) { #if (BYTE_ORDER != BIG_ENDIAN) fh->handle_size = bswap_16(fh->handle_size); fh->handle_type = bswap_16(fh->handle_type); fh->handle_version = bswap_16(fh->handle_version); fh->handle_key_size = bswap_16(fh->handle_key_size); #endif } else { #if (BYTE_ORDER == BIG_ENDIAN) fh->handle_size = bswap_16(fh->handle_size); fh->handle_type = bswap_16(fh->handle_type); fh->handle_version = bswap_16(fh->handle_version); fh->handle_key_size = bswap_16(fh->handle_key_size); #endif } LogFullDebug(COMPONENT_FSAL, "flags 0x%X size %d type %d ver %d key_size %d FSID 0x%X:%X", flags, fh->handle_size, fh->handle_type, fh->handle_version, fh->handle_key_size, fh->handle_fsid[0], fh->handle_fsid[1]); gpfs_extract_fsid(fh, &fsid); fs = lookup_fsid(&fsid, GPFS_FSID_TYPE); if (fs == NULL) { LogInfo(COMPONENT_FSAL, "Could not find filesystem for fsid=0x%016"PRIx64 ".0x%016"PRIx64" from handle", fsid.major, fsid.minor); return NFS4ERR_STALE; } if (fs->fsal != pds->fsal) { LogInfo(COMPONENT_FSAL, "Non GPFS filesystem fsid=0x%016"PRIx64".0x%016"PRIx64 " from handle", fsid.major, fsid.minor); return NFS4ERR_STALE; } ds = gsh_calloc(1, sizeof(struct gpfs_ds)); *handle = &ds->ds; fsal_ds_handle_init(*handle, pds); /* Connect lazily when a FILE_SYNC4 write forces us to, not here. */ ds->connected = false; ds->gpfs_fs = fs->private_data; memcpy(&ds->wire, desc->addr, desc->len); return NFS4_OK; } static nfsstat4 pds_permissions(struct fsal_pnfs_ds *const pds, struct svc_req *req) { /* special case: related export has been set */ return nfs4_export_check_access(req); } /** * @param ops FSAL pNFS ds ops */ void pnfs_ds_ops_init(struct fsal_pnfs_ds_ops *ops) { memcpy(ops, &def_pnfs_ds_ops, sizeof(struct fsal_pnfs_ds_ops)); ops->permissions = pds_permissions; ops->make_ds_handle = make_ds_handle; ops->fsal_dsh_ops = dsh_ops_init; } nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/fsal_fileop.c000066400000000000000000000172011324272410200212160ustar00rootroot00000000000000/* * @file fsal_fileop.c * @date $Date: 2006/01/17 14:20:07 $ * @brief Files operations. * * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ #include "fsal.h" #include "fsal_internal.h" #include "fsal_convert.h" #include "gpfs_methods.h" #include "FSAL/access_check.h" /** @fn fsal_status_t * GPFSFSAL_open(struct fsal_obj_handle *obj_hdl, * const struct req_op_context *op_ctx, * fsal_openflags_t openflags, int *file_desc, * struct attrlist *fsal_attr) * * @brief Open a regular file for reading/writing its data content. * * @param obj_hdl Handle of the file to be read/modified. * @param op_ctx Authentication context for the operation (user,...). * @param openflags 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_desc The file descriptor to be used for FSAL_read/write ops. * * @return ERR_FSAL_NO_ERROR on success, error otherwise */ fsal_status_t GPFSFSAL_open(struct fsal_obj_handle *obj_hdl, const struct req_op_context *op_ctx, int posix_flags, int *file_desc) { struct gpfs_fsal_obj_handle *myself; fsal_status_t status; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; /* sanity checks. */ if (!obj_hdl || !file_desc) return fsalstat(ERR_FSAL_FAULT, 0); myself = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); LogFullDebug(COMPONENT_FSAL, "posix_flags 0x%X export_fd %d", posix_flags, export_fd); fsal_set_credentials(op_ctx->creds); status = fsal_internal_handle2fd(export_fd, myself->handle, file_desc, posix_flags); fsal_restore_ganesha_credentials(); if (FSAL_IS_ERROR(status)) { /** Try open as root access if the above call fails, * permission will be checked somewhere else in the code. */ status = fsal_internal_handle2fd(export_fd, myself->handle, file_desc, posix_flags); } return status; } /** @fn fsal_status_t * GPFSFSAL_read(int fd, uint64_t offset, size_t buffer_size, * caddr_t buffer, size_t *p_read_amount, * bool *p_end_of_file) * @brief Perform a read operation on an opened file. * @param fd The file descriptor returned by FSAL_open. * @offset Offset * @param buf_size Amount (in bytes) of data to be read. * @param buf Address where the read data is to be stored in memory. * @param read_amount Pointer to the amount of data (in bytes) that have been * read during this call. * @param end_of_file Pointer to a boolean that indicates whether the end of * file has been reached during this call. * * @return ERR_FSAL_NO_ERROR on success, error otherwise. */ fsal_status_t GPFSFSAL_read(int fd, uint64_t offset, size_t buf_size, caddr_t buf, size_t *read_amount, bool *end_of_file, int expfd) { struct read_arg rarg = {0}; ssize_t nb_read; int errsv; /* sanity checks. */ if (!buf || !read_amount || !end_of_file) return fsalstat(ERR_FSAL_FAULT, 0); rarg.mountdirfd = expfd; rarg.fd = fd; rarg.bufP = buf; rarg.offset = offset; rarg.length = buf_size; rarg.options = 0; fsal_set_credentials(op_ctx->creds); nb_read = gpfs_ganesha(OPENHANDLE_READ_BY_FD, &rarg); errsv = errno; fsal_restore_ganesha_credentials(); /* negative values mean error */ if (nb_read < 0) { /* if nb_read is not -1, the split rc/errno didn't work */ if (nb_read != -1) { errsv = labs(nb_read); LogWarn(COMPONENT_FSAL, "Received negative value (%d) from ioctl().", (int) nb_read); } if (errsv == EUNATCH) LogFatal(COMPONENT_FSAL, "GPFS Returned EUNATCH"); return fsalstat(posix2fsal_error(errsv), errsv); } if (nb_read == 0 || nb_read < buf_size) *end_of_file = true; *read_amount = nb_read; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** @fn fsal_status_t * GPFSFSAL_write(int fd, uint64_t offset, size_t buf_size, caddr_t buf, * size_t *write_amount, bool *fsal_stable, * const struct req_op_context *op_ctx) * @brief Perform a write operation on an opened file. * * @param fd The file descriptor returned by FSAL_open. * @param buf_size Amount (in bytes) of data to be written. * @param buf Address where the data is in memory. * @param write_amount Pointer to the amount of data (in bytes) that have been * written during this call. * * @return ERR_FSAL_NO_ERROR on success, error otherwise */ fsal_status_t GPFSFSAL_write(int fd, uint64_t offset, size_t buf_size, caddr_t buf, size_t *write_amount, bool *fsal_stable, const struct req_op_context *op_ctx, int expfd) { struct write_arg warg = {0}; uint32_t stability_got = 0; ssize_t nb_write; int errsv; /* sanity checks. */ if (!buf || !write_amount) return fsalstat(ERR_FSAL_FAULT, 0); warg.mountdirfd = expfd; warg.fd = fd; warg.bufP = buf; warg.offset = offset; warg.length = buf_size; warg.stability_wanted = *fsal_stable; warg.stability_got = &stability_got; warg.options = 0; fsal_set_credentials(op_ctx->creds); nb_write = gpfs_ganesha(OPENHANDLE_WRITE_BY_FD, &warg); errsv = errno; fsal_restore_ganesha_credentials(); if (nb_write == -1) { if (errsv == EUNATCH) LogFatal(COMPONENT_FSAL, "GPFS Returned EUNATCH"); return fsalstat(posix2fsal_error(errsv), errsv); } *write_amount = nb_write; *fsal_stable = (stability_got) ? true : false; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** @fn fsal_status_t * GPFSFSAL_alloc(int fd, uint64_t offset, uint64_t length, bool allocate) * @brief Perform a de/allocc operation on an opened file. * @param fd The file descriptor returned by FSAL_open. * @param offset Offset * @param length Length * @param allocate Allocate * * @return ERR_FSAL_NO_ERROR on success, error otherwise */ fsal_status_t GPFSFSAL_alloc(int fd, uint64_t offset, uint64_t length, bool allocate) { struct alloc_arg aarg = {0}; int errsv; int rc; aarg.fd = fd; aarg.offset = offset; aarg.length = length; aarg.options = (allocate) ? IO_ALLOCATE : IO_DEALLOCATE; fsal_set_credentials(op_ctx->creds); rc = gpfs_ganesha(OPENHANDLE_ALLOCATE_BY_FD, &aarg); errsv = errno; fsal_restore_ganesha_credentials(); if (rc == -1) { if (errsv == EUNATCH) LogFatal(COMPONENT_FSAL, "GPFS Returned EUNATCH"); return fsalstat(posix2fsal_error(errsv), errsv); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/fsal_internal.c000066400000000000000000000456671324272410200215750ustar00rootroot00000000000000/* * @file fsal_internal.c * @date $Date: 2006/01/17 14:20:07 $ * @brief Defines the datas that are to be accessed as extern by the fsal * modules * * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ #include "config.h" #include #include "fsal.h" #include "fsal_internal.h" #include "gpfs_methods.h" #include "fsal_convert.h" #include /* used for 'dirname' */ #include "abstract_mem.h" #include #include #include #include "include/gpfs.h" #define FSAL_INTERNAL_ERROR(__error, __msg) \ ({ \ if ((__error) == EUNATCH) \ LogFatal(COMPONENT_FSAL, "GPFS Returned EUNATCH"); \ \ LogFullDebug(COMPONENT_FSAL, "%s returned errno=%d", __msg, __error); \ \ fsalstat(posix2fsal_error(__error), __error); \ }) /********************************************************************* * * GPFS FSAL char device driver interaces * ********************************************************************/ /** * @brief Close by fd * * @param fd Open file descriptor * * @return status of operation */ fsal_status_t fsal_internal_close(int fd, void *owner, int cflags) { struct close_file_arg carg; carg.mountdirfd = fd; carg.close_fd = fd; carg.close_flags = cflags; carg.close_owner = owner; if (unlikely(gpfs_ganesha(OPENHANDLE_CLOSE_FILE, &carg) < 0)) return FSAL_INTERNAL_ERROR(errno, "OPENHANDLE_CLOSE_FILE"); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Open a file by handle from in an open directory * * @param dirfd Open file descriptor of parent directory * @param gpfs_fh Opaque filehandle * @param fd File descriptor openned by the function * @param oflags Flags to open the file with * * @return status of operation */ fsal_status_t fsal_internal_handle2fd(int dirfd, struct gpfs_file_handle *gpfs_fh, int *fd, int oflags) { struct open_arg oarg = {0}; int rc; if (op_ctx && op_ctx->client && op_ctx->client->hostaddr_str) oarg.cli_ip = op_ctx->client->hostaddr_str; oarg.mountdirfd = dirfd; oarg.handle = gpfs_fh; oarg.flags = oflags; rc = gpfs_ganesha(OPENHANDLE_OPEN_BY_HANDLE, &oarg); if (unlikely(rc < 0)) return FSAL_INTERNAL_ERROR(errno, "OPENHANDLE_OPEN_BY_HANDLE"); *fd = rc; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Create a handle from a directory pointer and filename * * @param dfd Open directory handle * @param fs_name Name of the file * @param gpfs_fh The handle that is found and returned * * @return status of operation */ fsal_status_t fsal_internal_get_handle_at(int dfd, const char *fs_name, struct gpfs_file_handle *gpfs_fh, int expfd) { struct name_handle_arg harg; if (!gpfs_fh) return fsalstat(ERR_FSAL_FAULT, 0); harg.handle = gpfs_fh; harg.handle->handle_size = GPFS_MAX_FH_SIZE; harg.handle->handle_version = OPENHANDLE_VERSION; harg.handle->handle_key_size = OPENHANDLE_KEY_LEN; harg.name = fs_name; harg.dfd = dfd; harg.expfd = expfd; harg.flag = 0; LogFullDebug(COMPONENT_FSAL, "Lookup handle at %d for %s", dfd, fs_name); if (unlikely(gpfs_ganesha(OPENHANDLE_NAME_TO_HANDLE, &harg) < 0)) return FSAL_INTERNAL_ERROR(errno, "OPENHANDLE_NAME_TO_HANDLE"); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Create a handle from a directory handle and filename * * @param dirfd Open file descriptor of parent directory * @param gpfs_fh The handle for the parent directory * @param fs_name Name of the file * @param gpfs_fh_out The handle that is found and returned * * @return status of operation */ fsal_status_t fsal_internal_get_fh(int dirfd, struct gpfs_file_handle *gpfs_fh, const char *fs_name, struct gpfs_file_handle *gpfs_fh_out) { struct get_handle_arg harg; if (!gpfs_fh_out || !gpfs_fh || !fs_name) return fsalstat(ERR_FSAL_FAULT, 0); harg.mountdirfd = dirfd; harg.dir_fh = gpfs_fh; harg.out_fh = gpfs_fh_out; harg.out_fh->handle_size = GPFS_MAX_FH_SIZE; harg.out_fh->handle_version = OPENHANDLE_VERSION; harg.out_fh->handle_key_size = OPENHANDLE_KEY_LEN; harg.len = strlen(fs_name); harg.name = fs_name; LogFullDebug(COMPONENT_FSAL, "Lookup handle for %s", fs_name); if (unlikely(gpfs_ganesha(OPENHANDLE_GET_HANDLE, &harg) < 0)) return FSAL_INTERNAL_ERROR(errno, "OPENHANDLE_GET_HANDLE"); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief convert an fd to a handle * * @param fd Open file descriptor for target file * @param gpfs_fh The handle that is found and returned * * @return status of operation */ fsal_status_t fsal_internal_fd2handle(int fd, struct gpfs_file_handle *gpfs_fh) { struct name_handle_arg harg = {0}; if (!gpfs_fh) return fsalstat(ERR_FSAL_FAULT, 0); harg.handle = gpfs_fh; harg.handle->handle_size = GPFS_MAX_FH_SIZE; harg.handle->handle_key_size = OPENHANDLE_KEY_LEN; harg.handle->handle_version = OPENHANDLE_VERSION; harg.dfd = fd; LogFullDebug(COMPONENT_FSAL, "Lookup handle by fd for %d", fd); if (unlikely(gpfs_ganesha(OPENHANDLE_NAME_TO_HANDLE, &harg) < 0)) return FSAL_INTERNAL_ERROR(errno, "OPENHANDLE_NAME_TO_HANDLE"); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Create a link based on a file fh, dir fh, and new name * * @param dir_fd Open file descriptor of parent directory * @param gpfs_fh_tgt file handle of target file * @param gpfs_fh file handle of source directory * @param link_name name for the new file * * @return status of operation */ fsal_status_t fsal_internal_link_fh(int dirfd, struct gpfs_file_handle *gpfs_fh_tgt, struct gpfs_file_handle *gpfs_fh, const char *link_name) { struct link_fh_arg linkarg; if (!link_name) return fsalstat(ERR_FSAL_FAULT, 0); linkarg.mountdirfd = dirfd; linkarg.len = strlen(link_name); linkarg.name = link_name; linkarg.dir_fh = gpfs_fh; linkarg.dst_fh = gpfs_fh_tgt; if (unlikely(gpfs_ganesha(OPENHANDLE_LINK_BY_FH, &linkarg) < 0)) return FSAL_INTERNAL_ERROR(errno, "OPENHANDLE_LINK_BY_FH"); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Stat a file by name * * @param dirfd Open file descriptor of parent directory * @param gpfs_fh file handle of directory * @param stat_name name to stat * @param buf buffer reference to buffer * * @return status of operation */ fsal_status_t fsal_internal_stat_name(int dirfd, struct gpfs_file_handle *gpfs_fh, const char *stat_name, struct stat *buf) { struct stat_name_arg statarg; if (!stat_name) return fsalstat(ERR_FSAL_FAULT, 0); statarg.mountdirfd = dirfd; statarg.len = strlen(stat_name); statarg.name = stat_name; statarg.handle = gpfs_fh; statarg.buf = buf; if (unlikely(gpfs_ganesha(OPENHANDLE_STAT_BY_NAME, &statarg) < 0)) return FSAL_INTERNAL_ERROR(errno, "OPENHANDLE_STAT_BY_NAME"); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Unlink a file/directory by name * * @param dirfd Open file descriptor of parent directory * @param gpfs_fh file handle of directory * @param stat_name name to unlink * @param buf reference to buffer * * @return status of operation */ fsal_status_t fsal_internal_unlink(int dirfd, struct gpfs_file_handle *gpfs_fh, const char *stat_name, struct stat *buf) { struct stat_name_arg statarg; if (!stat_name) return fsalstat(ERR_FSAL_FAULT, 0); statarg.mountdirfd = dirfd; statarg.len = strlen(stat_name); statarg.name = stat_name; statarg.handle = gpfs_fh; statarg.buf = buf; if (unlikely(gpfs_ganesha(OPENHANDLE_UNLINK_BY_NAME, &statarg) < 0)) return FSAL_INTERNAL_ERROR(errno, "OPENHANDLE_UNLINK_BY_NAME"); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Create a file/directory by name * * @param dir_hdl file handle of directory * @param stat_name name to create * @param mode file type for mknode * @param dev file dev for mknode * @param fh file handle of new file * @param buf file attributes of new file * * @return status of operation */ fsal_status_t fsal_internal_create(struct fsal_obj_handle *dir_hdl, const char *stat_name, mode_t mode, int posix_flags, struct gpfs_file_handle *fh, struct stat *buf) { struct create_name_arg crarg = {0}; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; if (!stat_name) return fsalstat(ERR_FSAL_FAULT, 0); crarg.mountdirfd = export_fd; crarg.mode = mode; crarg.dev = posix_flags; crarg.len = strlen(stat_name); crarg.name = stat_name; crarg.new_fh = fh; crarg.new_fh->handle_size = GPFS_MAX_FH_SIZE; crarg.new_fh->handle_key_size = OPENHANDLE_KEY_LEN; crarg.new_fh->handle_version = OPENHANDLE_VERSION; crarg.buf = buf; crarg.dir_fh = container_of(dir_hdl, struct gpfs_fsal_obj_handle, obj_handle)->handle; if (unlikely(gpfs_ganesha(OPENHANDLE_CREATE_BY_NAME, &crarg) < 0)) return FSAL_INTERNAL_ERROR(errno, "OPENHANDLE_CREATE_BY_NAME"); return fsalstat(ERR_FSAL_NO_ERROR, 0); } fsal_status_t fsal_internal_mknode(struct fsal_obj_handle *dir_hdl, const char *stat_name, mode_t mode, dev_t dev, struct gpfs_file_handle *fh, struct stat *buf) { struct create_name_arg crarg = {0}; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; if (!stat_name) return fsalstat(ERR_FSAL_FAULT, 0); crarg.mountdirfd = export_fd; crarg.mode = mode; crarg.dev = dev; crarg.len = strlen(stat_name); crarg.name = stat_name; crarg.new_fh = fh; crarg.new_fh->handle_size = GPFS_MAX_FH_SIZE; crarg.new_fh->handle_key_size = OPENHANDLE_KEY_LEN; crarg.new_fh->handle_version = OPENHANDLE_VERSION; crarg.buf = buf; crarg.dir_fh = container_of(dir_hdl, struct gpfs_fsal_obj_handle, obj_handle)->handle; if (unlikely(gpfs_ganesha(OPENHANDLE_MKNODE_BY_NAME, &crarg) < 0)) return FSAL_INTERNAL_ERROR(errno, "OPENHANDLE_MKNODE_BY_NAME"); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Rename old file name to new name * * @param dirfd Open file descriptor of parent directory * @param gpfs_fh_old file handle of old file * @param gpfs_fh_new file handle of new directory * @param old_name name for the old file * @param new_name name for the new file * * @return status of operation */ fsal_status_t fsal_internal_rename_fh(int dirfd, struct gpfs_file_handle *gpfs_fh_old, struct gpfs_file_handle *gpfs_fh_new, const char *old_name, const char *new_name) { struct rename_fh_arg renamearg; if (!old_name || !new_name) return fsalstat(ERR_FSAL_FAULT, 0); renamearg.mountdirfd = dirfd; renamearg.old_len = strlen(old_name); renamearg.old_name = old_name; renamearg.new_len = strlen(new_name); renamearg.new_name = new_name; renamearg.old_fh = gpfs_fh_old; renamearg.new_fh = gpfs_fh_new; if (unlikely(gpfs_ganesha(OPENHANDLE_RENAME_BY_FH, &renamearg) < 0)) return FSAL_INTERNAL_ERROR(errno, "OPENHANDLE_RENAME_BY_FH"); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Reads the contents of the link * * @param dirfd Open file descriptor of parent directory * @param gpfs_fh file handle of file * @param buf Buffer * @param maxlen Max length of buffer * * @return status of operation */ fsal_status_t fsal_readlink_by_handle(int dirfd, struct gpfs_file_handle *gpfs_fh, char *buf, size_t maxlen) { struct readlink_fh_arg readlinkarg; int rc; readlinkarg.mountdirfd = dirfd; readlinkarg.handle = gpfs_fh; readlinkarg.buffer = buf; readlinkarg.size = maxlen - 1; /* reserve 1 for terminating 0-byte */ rc = gpfs_ganesha(OPENHANDLE_READLINK_BY_FH, &readlinkarg); if (unlikely(rc < 0)) return FSAL_INTERNAL_ERROR(errno, "OPENHANDLE_READLINK_BY_FH"); buf[rc] = '\0'; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief fsal_internal_version; * * @return GPFS version */ int fsal_internal_version(void) { int rc; if (unlikely(gpfs_ganesha(OPENHANDLE_GET_VERSION2, &rc) < 0)) FSAL_INTERNAL_ERROR(errno, "OPENHANDLE_GET_VERSION2"); else LogDebug(COMPONENT_FSAL, "GPFS get version %d", rc); return rc; } /** * @brief Get NFS4 ACL as well as stat. * * @param dirfd Open file descriptor of parent directory * @param gpfs_fh file handle of file * @param buffxstat Buffer * @param expire_time_attr Expire time attributes * @param expire Bool expire * @param use_acl Bool whether to ACL is to be used * @return status of operation */ fsal_status_t fsal_get_xstat_by_handle(int dirfd, struct gpfs_file_handle *gpfs_fh, gpfsfsal_xstat_t *buffxstat, gpfs_acl_t *acl_buf, unsigned int acl_buflen, uint32_t *expire_time_attr, bool expire, bool use_acl) { struct xstat_arg xstatarg = {0}; int errsv; int rc; if (!gpfs_fh || !buffxstat) return fsalstat(ERR_FSAL_FAULT, 0); /* Initialize acl header so that GPFS knows what we want. */ if (use_acl) { acl_buf->acl_level = 0; acl_buf->acl_version = GPFS_ACL_VERSION_NFS4; acl_buf->acl_type = GPFS_ACL_TYPE_NFS4; acl_buf->acl_len = acl_buflen; xstatarg.acl = acl_buf; xstatarg.attr_valid = XATTR_STAT | XATTR_ACL; } else { xstatarg.acl = NULL; xstatarg.attr_valid = XATTR_STAT; } if (expire) xstatarg.attr_valid |= XATTR_EXPIRE; xstatarg.mountdirfd = dirfd; xstatarg.handle = gpfs_fh; xstatarg.attr_changed = 0; xstatarg.buf = &buffxstat->buffstat; xstatarg.fsid = (struct fsal_fsid *)&buffxstat->fsal_fsid; xstatarg.attr_valid |= XATTR_FSID; xstatarg.expire_attr = expire_time_attr; rc = gpfs_ganesha(OPENHANDLE_GET_XSTAT, &xstatarg); errsv = errno; LogDebug(COMPONENT_FSAL, "GET_XSTAT returned, fd %d rc %d fh_size %d", dirfd, rc, gpfs_fh->handle_size); if (rc < 0) { switch (errsv) { case ENODATA: /* For the special file that do not have ACL, GPFS returns ENODATA. In this case, return okay with stat. */ buffxstat->attr_valid = XATTR_STAT; LogFullDebug(COMPONENT_FSAL, "GET_XSTAT retrieved only stat, not acl"); return fsalstat(ERR_FSAL_NO_ERROR, 0); case ENOSPC: /* If the supplied acl buffer is too small, we * get this errno! acl_len will be updated to * the required length. * * Return success and let the caller check the length */ if (use_acl && acl_buf->acl_len > acl_buflen) { LogFullDebug(COMPONENT_FSAL, "GET_XSTAT returned buffer too small, passed len: %u, required len: %u, ", acl_buflen, acl_buf->acl_len); errno = 0; break; } LogWarn(COMPONENT_FSAL, "GET_XSTAT returned bogus ENOSPC, passed len: %u, required len: %u", acl_buflen, acl_buf->acl_len); return fsalstat(ERR_FSAL_SERVERFAULT, errsv); default: /* Handle other errors. */ LogFullDebug(COMPONENT_FSAL, "GET_XSTAT returned errno:%d -- %s", errsv, strerror(errsv)); if (errsv == EUNATCH) LogFatal(COMPONENT_FSAL, "GPFS Returned EUNATCH"); return fsalstat(posix2fsal_error(errsv), errsv); } } if (use_acl) buffxstat->attr_valid = XATTR_FSID | XATTR_STAT | XATTR_ACL; else buffxstat->attr_valid = XATTR_FSID | XATTR_STAT; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Set NFS4 ACL as well as stat. * * @param dirfd Open file descriptor of parent directory * @param op_ctx Context * @param gpfs_fh file handle of file * @param attr_valid Attributes valid * @param attr_changed Attributes changed * @param buffxstat Buffer * * @return status of operation */ fsal_status_t fsal_set_xstat_by_handle(int dirfd, const struct req_op_context *op_ctx, struct gpfs_file_handle *gpfs_fh, int attr_valid, int attr_changed, gpfsfsal_xstat_t *buffxstat, gpfs_acl_t *acl_buf) { struct xstat_arg xstatarg = {0}; int errsv; int rc; if (!gpfs_fh || !buffxstat) return fsalstat(ERR_FSAL_FAULT, 0); xstatarg.attr_valid = attr_valid; xstatarg.mountdirfd = dirfd; xstatarg.handle = gpfs_fh; xstatarg.acl = acl_buf; xstatarg.attr_changed = attr_changed; xstatarg.buf = &buffxstat->buffstat; /* We explicitly do NOT do setfsuid/setfsgid here because truncate, even to enlarge a file, doesn't actually allocate blocks. GPFS implements sparse files, so blocks of all 0 will not actually be allocated. */ fsal_set_credentials(op_ctx->creds); rc = gpfs_ganesha(OPENHANDLE_SET_XSTAT, &xstatarg); errsv = errno; fsal_restore_ganesha_credentials(); LogDebug(COMPONENT_FSAL, "gpfs_ganesha: SET_XSTAT returned, rc = %d", rc); if (rc < 0) { if (errsv == EUNATCH) LogFatal(COMPONENT_FSAL, "GPFS Returned EUNATCH"); return fsalstat(posix2fsal_error(errsv), errsv); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @param dirfd Open file descriptor of parent directory * @param op_ctx Context * @param gpfs_fh file handle of file * @param size Size * * @return status of operation */ fsal_status_t fsal_trucate_by_handle(int dirfd, const struct req_op_context *op_ctx, struct gpfs_file_handle *gpfs_fh, u_int64_t size) { gpfsfsal_xstat_t buffxstat = {0}; if (!gpfs_fh || !op_ctx) return fsalstat(ERR_FSAL_FAULT, 0); buffxstat.buffstat.st_size = size; return fsal_set_xstat_by_handle(dirfd, op_ctx, gpfs_fh, XATTR_STAT, XATTR_SIZE, &buffxstat, NULL); } /** * @brief Indicates if an FSAL error should be posted as an event * * @param status(input): The fsal status whom event is to be tested. * @return - true if the error event is to be posted. * - false if the error event is NOT to be posted. */ bool fsal_error_is_event(fsal_status_t status) { switch (status.major) { case ERR_FSAL_IO: case ERR_FSAL_STALE: return true; default: return false; } } /** * @brief Indicates if an FSAL error should be posted as an INFO level debug * message. * * @param status(input): The fsal status whom event is to be tested. * @return - true if the error event is to be posted. * - false if the error event is NOT to be posted. */ bool fsal_error_is_info(fsal_status_t status) { switch (status.major) { case ERR_FSAL_NOTDIR: case ERR_FSAL_NOMEM: case ERR_FSAL_FAULT: case ERR_FSAL_EXIST: case ERR_FSAL_XDEV: case ERR_FSAL_ISDIR: case ERR_FSAL_INVAL: case ERR_FSAL_FBIG: case ERR_FSAL_NOSPC: case ERR_FSAL_MLINK: case ERR_FSAL_NAMETOOLONG: case ERR_FSAL_STALE: case ERR_FSAL_NOTSUPP: case ERR_FSAL_OVERFLOW: case ERR_FSAL_DEADLOCK: case ERR_FSAL_INTERRUPT: case ERR_FSAL_SERVERFAULT: return true; default: return false; } } nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/fsal_internal.h000066400000000000000000000251471324272410200215710ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * * @file fsal_internal.h * @brief Extern definitions for variables that are * defined in fsal_internal.c. */ #ifndef FSAL_INTERNAL_H #define FSAL_INTERNAL_H #include #include "fsal.h" #include "gsh_list.h" #include "fsal_types.h" #include "fcntl.h" #include "include/gpfs_nfs.h" #include "include/gpfs.h" #include "fsal_up.h" #include "gsh_config.h" #include "FSAL/fsal_commonlib.h" struct gpfs_filesystem; void gpfs_handle_ops_init(struct fsal_obj_ops *ops); bool fsal_error_is_event(fsal_status_t status); /* * Tests whether an error code should be raised as an info debug. */ bool fsal_error_is_info(fsal_status_t status); /** * The full, 'private' DS (data server) handle */ struct gpfs_ds { struct fsal_ds_handle ds; /*< Public DS handle */ struct gpfs_file_handle wire; /*< Wire data */ struct gpfs_filesystem *gpfs_fs; /*< filesystem handle belongs to */ bool connected; /*< True if the handle has been connected */ }; struct gpfs_fd { /** The open and share mode etc. */ fsal_openflags_t openflags; /* rw lock to protect the file descriptor */ pthread_rwlock_t fdlock; /** The gpfsfs file descriptor. */ int fd; }; struct gpfs_state_fd { struct state_t state; struct gpfs_fd gpfs_fd; }; /* defined the set of attributes supported with POSIX */ #define GPFS_SUPPORTED_ATTRIBUTES ((const attrmask_t) ( \ ATTRS_POSIX | ATTR_ACL | ATTR4_SPACE_RESERVED | \ ATTR4_FS_LOCATIONS | ATTR4_XATTR)) #define GPFS_MAX_FH_SIZE OPENHANDLE_HANDLE_LEN /* Define the buffer size for GPFS NFS4 ACL. */ #define GPFS_ACL_BUF_SIZE 0x1000 #define GPFS_ACL_MAX_RETRY 10 /* Define the standard fsid_type for GPFS*/ #define GPFS_FSID_TYPE FSID_MAJOR_64 /* A set of buffers to retrieve multiple attributes at the same time. */ typedef struct fsal_xstat__ { int attr_valid; struct stat buffstat; fsal_fsid_t fsal_fsid; char buffacl[GPFS_ACL_BUF_SIZE]; } gpfsfsal_xstat_t; static inline size_t gpfs_sizeof_handle(const struct gpfs_file_handle *hdl) { return hdl->handle_size; } void export_ops_init(struct export_ops *ops); void handle_ops_init(struct fsal_obj_ops *ops); void pnfs_ds_ops_init(struct fsal_pnfs_ds_ops *ops); void export_ops_pnfs(struct export_ops *ops); void handle_ops_pnfs(struct fsal_obj_ops *ops); fsal_status_t fsal_internal_close(int fd, void *owner, int cflags); int fsal_internal_version(void); fsal_status_t fsal_internal_get_handle_at(int dfd, const char *p_fsalname, struct gpfs_file_handle *p_handle, int expfd); fsal_status_t gpfsfsal_xstat_2_fsal_attributes(gpfsfsal_xstat_t *gpfs_buf, struct attrlist *fsal_attr, gpfs_acl_t *acl_buf, bool use_acl); fsal_status_t fsal_internal_handle2fd(int dirfd, struct gpfs_file_handle *phandle, int *pfd, int oflags); /** * Gets a file handle from a parent handle and name */ fsal_status_t fsal_internal_get_fh(int dirfd, struct gpfs_file_handle *p_dir_handle, const char *p_fsalname, struct gpfs_file_handle *p_handle); /** * Access a link by a file handle. */ fsal_status_t fsal_readlink_by_handle(int dirfd, struct gpfs_file_handle *p_handle, char *__buf, size_t maxlen); /** * Get the handle for a path (posix or fid path) */ fsal_status_t fsal_internal_fd2handle(int fd, struct gpfs_file_handle *p_handle); fsal_status_t fsal_internal_link_at(int srcfd, int dfd, char *name); fsal_status_t fsal_internal_link_fh(int dirfd, struct gpfs_file_handle *p_target_handle, struct gpfs_file_handle *p_dir_handle, const char *p_link_name); fsal_status_t fsal_internal_stat_name(int dirfd, struct gpfs_file_handle *p_dir_handle, const char *p_stat_name, struct stat *buf); fsal_status_t fsal_internal_unlink(int dirfd, struct gpfs_file_handle *p_dir_handle, const char *p_stat_name, struct stat *buf); fsal_status_t fsal_internal_create(struct fsal_obj_handle *dir_hdl, const char *p_stat_name, mode_t mode, int posix_flags, struct gpfs_file_handle *p_new_handle, struct stat *buf); fsal_status_t fsal_internal_mknode(struct fsal_obj_handle *dir_hdl, const char *p_stat_name, mode_t mode, dev_t dev, struct gpfs_file_handle *p_new_handle, struct stat *buf); fsal_status_t fsal_internal_rename_fh(int dirfd, struct gpfs_file_handle *p_old_handle, struct gpfs_file_handle *p_new_handle, const char *p_old_name, const char *p_new_name); fsal_status_t fsal_get_xstat_by_handle(int dirfd, struct gpfs_file_handle *p_handle, gpfsfsal_xstat_t *p_buffxstat, gpfs_acl_t *acl_buf, unsigned int acl_buflen, uint32_t *expire_time_attr, bool expire, bool use_acl); fsal_status_t fsal_set_xstat_by_handle(int dirfd, const struct req_op_context *p_context, struct gpfs_file_handle *p_handle, int attr_valid, int attr_changed, gpfsfsal_xstat_t *p_buffxstat, gpfs_acl_t *acl_buf); fsal_status_t fsal_trucate_by_handle(int dirfd, const struct req_op_context *p_context, struct gpfs_file_handle *p_handle, u_int64_t size); /* All the call to FSAL to be wrapped */ fsal_status_t GPFSFSAL_getattrs(struct fsal_export *export, struct gpfs_filesystem *gpfs_fs, const struct req_op_context *p_context, struct gpfs_file_handle *p_filehandle, struct attrlist *p_object_attributes); fsal_status_t GPFSFSAL_fs_loc(struct fsal_export *export, struct gpfs_filesystem *gpfs_fs, const struct req_op_context *p_context, struct gpfs_file_handle *p_filehandle, struct fs_locations4 *fs_loc); fsal_status_t GPFSFSAL_statfs(int fd, struct fsal_obj_handle *obj_hdl, struct statfs *buf); fsal_status_t GPFSFSAL_setattrs(struct fsal_obj_handle *dir_hdl, const struct req_op_context *p_context, struct attrlist *p_object_attributes); fsal_status_t GPFSFSAL_create(struct fsal_obj_handle *dir_hdl, const char *p_filename, const struct req_op_context *p_context, uint32_t accessmode, struct gpfs_file_handle *p_object_handle, struct attrlist *p_object_attributes); fsal_status_t GPFSFSAL_create2(struct fsal_obj_handle *dir_hdl, const char *p_filename, const struct req_op_context *p_context, mode_t unix_mode, struct gpfs_file_handle *p_object_handle, int posix_flags, struct attrlist *p_object_attributes); fsal_status_t GPFSFSAL_mkdir(struct fsal_obj_handle *dir_hdl, const char *p_dirname, const struct req_op_context *p_context, uint32_t accessmode, struct gpfs_file_handle *p_object_handle, struct attrlist *p_object_attributes); fsal_status_t GPFSFSAL_link(struct fsal_obj_handle *dir_hdl, struct gpfs_file_handle *p_target_handle, const char *p_link_name, const struct req_op_context *p_context); fsal_status_t GPFSFSAL_mknode(struct fsal_obj_handle *dir_hdl, const char *p_node_name, const struct req_op_context *p_context, uint32_t accessmode, mode_t nodetype, fsal_dev_t *dev, struct gpfs_file_handle *p_object_handle, struct attrlist *node_attributes); fsal_status_t GPFSFSAL_open(struct fsal_obj_handle *obj_hdl, const struct req_op_context *p_context, int posix_flags, int *p_file_descriptor); fsal_status_t GPFSFSAL_read(int fd, uint64_t offset, size_t buffer_size, caddr_t buffer, size_t *p_read_amount, bool *p_end_of_file, int expfs); fsal_status_t GPFSFSAL_write(int fd, uint64_t offset, size_t buffer_size, caddr_t buffer, size_t *p_write_amount, bool *fsal_stable, const struct req_op_context *p_context, int expfs); fsal_status_t GPFSFSAL_alloc(int fd, uint64_t offset, uint64_t length, bool options); fsal_status_t GPFSFSAL_lookup(const struct req_op_context *p_context, struct fsal_obj_handle *parent, const char *p_filename, struct attrlist *p_object_attr, struct gpfs_file_handle *fh, struct fsal_filesystem **new_fs); fsal_status_t GPFSFSAL_lock_op(struct fsal_export *export, fsal_lock_op_t lock_op, fsal_lock_param_t *req_lock, fsal_lock_param_t *confl_lock, struct set_get_lock_arg *sg_lock_arg); fsal_status_t GPFSFSAL_rename(struct fsal_obj_handle *old_hdl, const char *p_old_name, struct fsal_obj_handle *new_hdl, const char *p_new_name, const struct req_op_context *p_context); fsal_status_t GPFSFSAL_readlink(struct fsal_obj_handle *dir_hdl, const struct req_op_context *p_context, char *p_link_content, size_t link_len); fsal_status_t GPFSFSAL_symlink(struct fsal_obj_handle *dir_hdl, const char *p_linkname, const char *p_linkcontent, const struct req_op_context *p_context, uint32_t accessmode, /* IN (ignored) */ struct gpfs_file_handle *p_link_handle, struct attrlist *p_link_attributes); fsal_status_t GPFSFSAL_unlink(struct fsal_obj_handle *dir_hdl, const char *p_object_name, const struct req_op_context *p_context); void *GPFSFSAL_UP_Thread(void *Arg); size_t fs_da_addr_size(struct fsal_module *fsal_hdl); nfsstat4 getdeviceinfo(struct fsal_module *fsal_hdl, XDR *da_addr_body, const layouttype4 type, const struct pnfs_deviceid *deviceid); void fsal_gpfs_extract_stats(struct fsal_module *fsal_hdl, void *iter); void fsal_gpfs_reset_stats(struct fsal_module *fsal_hdl); void prepare_for_stats(struct fsal_module *fsal_hdl); int gpfs_op2index(int op); #endif nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/fsal_lock.c000066400000000000000000000066531324272410200207010ustar00rootroot00000000000000/** @file fsal_lock.s * * Copyright IBM Corporation, 2010 * Contributor: Aneesh Kumar K.v * * * This software is a server that implements the NFS protocol. * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /* _FILE_OFFSET_BITS macro causes F_GETLK/SETLK/SETLKW to be defined to * F_GETLK64/SETLK64/SETLKW64. Currently GPFS kernel module doesn't work * with these 64 bit macro values through ganesha interface. Undefine it * here to use plain F_GETLK/SETLK/SETLKW values. */ #undef _FILE_OFFSET_BITS #include "config.h" #include "fsal.h" #include "fsal_internal.h" #include "fsal_convert.h" #include "gpfs_methods.h" fsal_status_t GPFSFSAL_lock_op(struct fsal_export *export, fsal_lock_op_t lock_op, fsal_lock_param_t *req_lock, fsal_lock_param_t *confl_lock, struct set_get_lock_arg *sg_lock_arg) { struct glock *glock = sg_lock_arg->lock; int retval; int errsv; if (req_lock->lock_sle_type == FSAL_LEASE_LOCK) retval = gpfs_ganesha(OPENHANDLE_SET_DELEGATION, sg_lock_arg); else if (lock_op == FSAL_OP_LOCKT) retval = gpfs_ganesha(OPENHANDLE_GET_LOCK, sg_lock_arg); else retval = gpfs_ganesha(OPENHANDLE_SET_LOCK, sg_lock_arg); if (retval) { errsv = errno; goto err_out; } /* F_UNLCK is returned then the tested operation would be possible. */ if (confl_lock != NULL) { if (lock_op == FSAL_OP_LOCKT && glock->flock.l_type != F_UNLCK) { confl_lock->lock_length = glock->flock.l_len; confl_lock->lock_start = glock->flock.l_start; confl_lock->lock_type = glock->flock.l_type; } else { confl_lock->lock_length = 0; confl_lock->lock_start = 0; confl_lock->lock_type = FSAL_NO_LOCK; } } return fsalstat(ERR_FSAL_NO_ERROR, 0); err_out: /* error only section from here on */ if ((confl_lock != NULL) && (lock_op == FSAL_OP_LOCK || lock_op == FSAL_OP_LOCKB)) { glock->cmd = F_GETLK; if (gpfs_ganesha(OPENHANDLE_GET_LOCK, sg_lock_arg)) { int errsv2 = errno; LogCrit(COMPONENT_FSAL, "After failing a set lock request, an attempt to get the current owner details also failed."); if (errsv2 == EUNATCH) LogFatal(COMPONENT_FSAL, "GPFS Returned EUNATCH"); } else { confl_lock->lock_length = glock->flock.l_len; confl_lock->lock_start = glock->flock.l_start; confl_lock->lock_type = glock->flock.l_type; } } if (retval == 1) { LogFullDebug(COMPONENT_FSAL, "GPFS queued blocked lock"); return fsalstat(ERR_FSAL_BLOCKED, 0); } LogFullDebug(COMPONENT_FSAL, "GPFS lock operation failed error %d %d (%s)", retval, errsv, strerror(errsv)); if (errsv == EUNATCH) LogFatal(COMPONENT_FSAL, "GPFS Returned EUNATCH"); if (errsv == EGRACE) return fsalstat(ERR_FSAL_IN_GRACE, 0); return fsalstat(posix2fsal_error(errsv), errsv); } nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/fsal_lookup.c000066400000000000000000000106251324272410200212540ustar00rootroot00000000000000/** * @file fsal_lookup.c * @date $Date: 2006/01/24 13:45:37 $ * @brief Lookup operations. * * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ #include "config.h" #include #include "fsal.h" #include "FSAL/fsal_commonlib.h" #include "fsal_internal.h" #include "fsal_convert.h" #include "gpfs_methods.h" /** * @brief Looks up for an object into a directory. * * if parent handle and filename are NULL, * this retrieves root's handle. * * @param op_ctx Authentication context for the operation (user,...). * @param parent Handle of the parent directory to search the object in. * @param filename The name of the object to find. * @param fsal_attr Pointer to the attributes of the object we found. * @param fh The handle of the object corresponding to filename. * @param new_fs New FS * * @return - ERR_FSAL_NO_ERROR, if no error. * - Another error code else. */ fsal_status_t GPFSFSAL_lookup(const struct req_op_context *op_ctx, struct fsal_obj_handle *parent, const char *filename, struct attrlist *fsal_attr, struct gpfs_file_handle *fh, struct fsal_filesystem **new_fs) { fsal_status_t status; int parent_fd; struct gpfs_fsal_obj_handle *parent_hdl; struct gpfs_filesystem *gpfs_fs; struct fsal_fsid__ fsid; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; if (!parent || !filename) return fsalstat(ERR_FSAL_FAULT, 0); assert(*new_fs == parent->fs); parent_hdl = container_of(parent, struct gpfs_fsal_obj_handle, obj_handle); gpfs_fs = parent->fs->private_data; status = fsal_internal_handle2fd(export_fd, parent_hdl->handle, &parent_fd, O_RDONLY); if (FSAL_IS_ERROR(status)) return status; /* Be careful about junction crossing, symlinks, hardlinks,... */ switch (parent->type) { case DIRECTORY: /* OK */ break; case REGULAR_FILE: case SYMBOLIC_LINK: /* not a directory */ close(parent_fd); return fsalstat(ERR_FSAL_NOTDIR, 0); default: close(parent_fd); return fsalstat(ERR_FSAL_SERVERFAULT, 0); } status = fsal_internal_get_handle_at(parent_fd, filename, fh, export_fd); if (FSAL_IS_ERROR(status)) { close(parent_fd); return status; } /* In order to check XDEV, we need to get the fsid from the handle. * We need to do this before getting attributes in order to have the * correct gpfs_fs to pass to GPFSFSAL_getattrs. We also return * the correct fs to the caller. */ gpfs_extract_fsid(fh, &fsid); if (fsid.major != parent_hdl->obj_handle.fsid.major) { /* XDEV */ *new_fs = lookup_fsid(&fsid, GPFS_FSID_TYPE); if (*new_fs == NULL) { LogDebug(COMPONENT_FSAL, "Lookup of %s crosses filesystem boundary to unknown file system fsid=0x%016" PRIx64".0x%016"PRIx64, filename, fsid.major, fsid.minor); return fsalstat(ERR_FSAL_XDEV, EXDEV); } if ((*new_fs)->fsal != parent->fsal) { LogDebug(COMPONENT_FSAL, "Lookup of %s crosses filesystem boundary to file system %s into FSAL %s", filename, (*new_fs)->path, (*new_fs)->fsal != NULL ? (*new_fs)->fsal->name : "(none)"); return fsalstat(ERR_FSAL_XDEV, EXDEV); } else { LogDebug(COMPONENT_FSAL, "Lookup of %s crosses filesystem boundary to file system %s", filename, (*new_fs)->path); } gpfs_fs = (*new_fs)->private_data; } /* get object attributes */ status = GPFSFSAL_getattrs(op_ctx->fsal_export, gpfs_fs, op_ctx, fh, fsal_attr); close(parent_fd); /* lookup complete ! */ return status; } nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/fsal_mds.c000066400000000000000000000403371324272410200205310ustar00rootroot00000000000000/* * Copyright © 2012 CohortFS, LLC. * Author: Adam C. Emerson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ #include "fsal.h" #include "fsal_types.h" #include "fsal_api.h" #include "fsal_up.h" #include "pnfs_utils.h" #include "fsal_internal.h" #include "gpfs_methods.h" #include "nfs_exports.h" #include "FSAL/fsal_commonlib.h" #include "export_mgr.h" /** * @brief Get layout types supported by export * * We just return a pointer to the single type and set the count to 1. * * @param[in] export_pub Public export handle * @param[out] count Number of layout types in array * @param[out] types Static array of layout types that must not be * freed or modified and must not be dereferenced * after export reference is relinquished */ static void fs_layouttypes(struct fsal_export *export_hdl, int32_t *count, const layouttype4 **types) { int rc; struct open_arg arg; static const layouttype4 supported_layout_type = LAYOUT4_NFSV4_1_FILES; int errsv = 0; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; /** @todo FSF: needs real getdeviceinfo that gets to the correct * filesystem, this will not work for sub-mounted filesystems. */ arg.mountdirfd = export_fd; rc = gpfs_ganesha(OPENHANDLE_LAYOUT_TYPE, &arg); errsv = errno; if (rc < 0 || (rc != LAYOUT4_NFSV4_1_FILES)) { LogDebug(COMPONENT_PNFS, "rc %d", rc); if (errsv == EUNATCH) LogFatal(COMPONENT_PNFS, "GPFS Returned EUNATCH"); *count = 0; return; } *types = &supported_layout_type; *count = 1; } /** * @brief Get layout block size for export * * This function just returns the GPFS default. * * @param[in] export_pub Public export handle * * @return 4 MB. */ static uint32_t fs_layout_blocksize(struct fsal_export *export_pub) { return 0x400000; } /** * @brief Maximum number of segments we will use * * Since current clients only support 1, that's what we'll use. * * @param[in] export_pub Public export handle * * @return 1 */ static uint32_t fs_maximum_segments(struct fsal_export *export_pub) { return 1; } /** * @brief Size of the buffer needed for a loc_body * * Just a handle plus a bit. * * @param[in] export_pub Public export handle * * @return Size of the buffer needed for a loc_body */ static size_t fs_loc_body_size(struct fsal_export *export_pub) { return 0x100; } /** * @brief Size of the buffer needed for a ds_addr * * This one is huge, due to the striping pattern. * * @param[in] export_pub Public export handle * * @return Size of the buffer needed for a ds_addr */ size_t fs_da_addr_size(struct fsal_module *fsal_hdl) { return 0x20000; } /** * @brief Describe a GPFS striping pattern * * At present, we support a files based layout only. The CRUSH * striping pattern is a-periodic * * @param[in] export_pub Public export handle * @param[out] da_addr_body Stream we write the result to * @param[in] type Type of layout that gave the device * @param[in] deviceid The device to look up * * @return Valid error codes in RFC 5661, p. 365. */ nfsstat4 getdeviceinfo(struct fsal_module *fsal_hdl, XDR *da_addr_body, const layouttype4 type, const struct pnfs_deviceid *deviceid) { struct deviceinfo_arg darg; /* The position before any bytes are sent to the stream */ size_t da_beginning; size_t ds_buffer; /* The total length of the XDR-encoded da_addr_body */ size_t da_length; int rc; int errsv; darg.mountdirfd = deviceid->device_id4; darg.type = LAYOUT4_NFSV4_1_FILES; darg.devid.devid = deviceid->devid; darg.devid.device_id1 = deviceid->device_id1; darg.devid.device_id2 = deviceid->device_id2; darg.devid.device_id4 = deviceid->device_id4; darg.devid.devid = deviceid->devid; da_beginning = xdr_getpos(da_addr_body); darg.xdr.p = xdr_inline_encode(da_addr_body, 0); ds_buffer = xdr_size_inline(da_addr_body); darg.xdr.end = (int *)(darg.xdr.p + ((ds_buffer - da_beginning) / BYTES_PER_XDR_UNIT)); LogDebug(COMPONENT_PNFS, "p %p end %p da_length %zu ds_buffer %zu seq %d fd %d fsid 0x%" PRIx64, darg.xdr.p, darg.xdr.end, da_beginning, ds_buffer, deviceid->device_id2, deviceid->device_id4, deviceid->devid); rc = gpfs_ganesha(OPENHANDLE_GET_DEVICEINFO, &darg); errsv = errno; if (rc < 0) { LogDebug(COMPONENT_PNFS, "rc %d", rc); if (errsv == EUNATCH) LogFatal(COMPONENT_PNFS, "GPFS Returned EUNATCH"); return NFS4ERR_RESOURCE; } (void)xdr_inline_encode(da_addr_body, rc); da_length = xdr_getpos(da_addr_body) - da_beginning; LogDebug(COMPONENT_PNFS, "rc %d da_length %zd", rc, da_length); return NFS4_OK; } /** * @brief Get list of available devices * * We do not support listing devices and just set EOF without doing * anything. * * @param[in] export_pub Export handle * @param[in] type Type of layout to get devices for * @param[in] cb Function taking device ID halves * @param[in,out] res In/out and output arguments of the function * * @return Valid error codes in RFC 5661, pp. 365-6. */ static nfsstat4 getdevicelist(struct fsal_export *export_pub, layouttype4 type, void *opaque, bool(*cb) (void *opaque, const uint64_t id), struct fsal_getdevicelist_res *res) { res->eof = true; return NFS4_OK; } /** * @brief set export options * @param ops reference to struct export_ops */ void export_ops_pnfs(struct export_ops *ops) { ops->getdevicelist = getdevicelist; ops->fs_layouttypes = fs_layouttypes; ops->fs_layout_blocksize = fs_layout_blocksize; ops->fs_maximum_segments = fs_maximum_segments; ops->fs_loc_body_size = fs_loc_body_size; } /** * @brief Grant a layout segment. * * Grant a layout on a subset of a file requested. As a special case, * lie and grant a whole-file layout if requested, because Linux will * ignore it otherwise. * * @param[in] obj_pub Public object handle * @param[in] req_ctx Request context * @param[out] loc_body An XDR stream to which the FSAL must encode * the layout specific portion of the granted * layout segment. * @param[in] arg Input arguments of the function * @param[in,out] res In/out and output arguments of the function * * @return Valid error codes in RFC 5661, pp. 366-7. */ static nfsstat4 layoutget(struct fsal_obj_handle *obj_hdl, struct req_op_context *req_ctx, XDR *loc_body, const struct fsal_layoutget_arg *arg, struct fsal_layoutget_res *res) { struct gpfs_fsal_obj_handle *myself; struct gpfs_file_handle gpfs_ds_handle; struct layoutget_arg larg; struct layoutreturn_arg lrarg; unsigned int rc, *fh; /* Structure containing the storage parameters of the file within the GPFS cluster. */ struct pnfs_filelayout_layout file_layout; /* Width of each stripe on the file */ uint32_t stripe_width = 0; /* Utility parameter */ nfl_util4 util = 0; /* The last byte that can be accessed through pNFS */ /* uint64_t last_possible_byte = 0; strict. set but unused */ /* The deviceid for this layout */ struct pnfs_deviceid deviceid = DEVICE_ID_INIT_ZERO(FSAL_ID_GPFS); /* NFS Status */ nfsstat4 nfs_status = 0; /* Descriptor for DS handle */ struct gsh_buffdesc ds_desc; int errsv = 0; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; myself = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); /* We support only LAYOUT4_NFSV4_1_FILES layouts */ if (arg->type != LAYOUT4_NFSV4_1_FILES) { LogCrit(COMPONENT_PNFS, "Unsupported layout type: %x", arg->type); return NFS4ERR_UNKNOWN_LAYOUTTYPE; } /* Get basic information on the file and calculate the dimensions of the layout we can support. */ memset(&file_layout, 0, sizeof(struct pnfs_filelayout_layout)); memcpy(&gpfs_ds_handle, myself->handle, sizeof(struct gpfs_file_handle)); larg.fd = export_fd; larg.args.lg_minlength = arg->minlength; larg.args.lg_sbid = arg->export_id; larg.args.lg_fh = &gpfs_ds_handle; larg.args.lg_iomode = res->segment.io_mode; larg.handle = &gpfs_ds_handle; larg.file_layout = &file_layout; larg.xdr = NULL; fh = (int *)&(gpfs_ds_handle.f_handle); LogDebug(COMPONENT_PNFS, "fh in len %d type %d key %d: %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x", gpfs_ds_handle.handle_size, gpfs_ds_handle.handle_type, gpfs_ds_handle.handle_key_size, fh[0], fh[1], fh[2], fh[3], fh[4], fh[5], fh[6], fh[7], fh[8], fh[9]); rc = gpfs_ganesha(OPENHANDLE_LAYOUT_GET, &larg); errsv = errno; if (rc != 0) { LogDebug(COMPONENT_PNFS, "GPFSFSAL_layoutget rc %d", rc); if (errsv == EUNATCH) LogFatal(COMPONENT_PNFS, "GPFS Returned EUNATCH"); return NFS4ERR_LAYOUTUNAVAILABLE; } fh = (int *)&(gpfs_ds_handle.f_handle); LogDebug(COMPONENT_PNFS, "fh out len %d type %d key %d: %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x", gpfs_ds_handle.handle_size, gpfs_ds_handle.handle_type, gpfs_ds_handle.handle_key_size, fh[0], fh[1], fh[2], fh[3], fh[4], fh[5], fh[6], fh[7], fh[8], fh[9]); /* We grant only one segment, and we want it back when file is closed.*/ res->return_on_close = true; res->last_segment = true; res->segment.offset = 0; res->segment.length = NFS4_UINT64_MAX; stripe_width = file_layout.lg_stripe_unit; util |= stripe_width | NFL4_UFLG_COMMIT_THRU_MDS; deviceid.fsal_id = file_layout.device_id.fsal_id; deviceid.device_id2 = file_layout.device_id.device_id2; deviceid.device_id4 = file_layout.device_id.device_id4; deviceid.devid = file_layout.device_id.devid; /* last_possible_byte = NFS4_UINT64_MAX; strict. set but unused */ LogDebug(COMPONENT_PNFS, "fsal_id %d seq %d fd %d fsid 0x%" PRIx64 " index %d", deviceid.fsal_id, deviceid.device_id2, deviceid.device_id4, deviceid.devid, file_layout.lg_first_stripe_index); ds_desc.addr = &gpfs_ds_handle; ds_desc.len = sizeof(struct gpfs_file_handle); nfs_status = FSAL_encode_file_layout(loc_body, &deviceid, util, file_layout.lg_first_stripe_index, 0, &req_ctx->ctx_export->export_id, 1, &ds_desc); if (nfs_status) { if (arg->maxcount <= op_ctx->fsal_export->exp_ops.fs_loc_body_size( op_ctx->fsal_export)) { nfs_status = NFS4ERR_TOOSMALL; LogDebug(COMPONENT_PNFS, "Failed to encode nfsv4_1_file_layout."); } else LogCrit(COMPONENT_PNFS, "Failed to encode nfsv4_1_file_layout."); goto relinquish; } return NFS4_OK; relinquish: /* If we failed in encoding the lo_content, relinquish what we reserved for it. */ lrarg.mountdirfd = export_fd; lrarg.handle = &gpfs_ds_handle; lrarg.args.lr_return_type = arg->type; lrarg.args.lr_reclaim = false; lrarg.args.lr_seg.clientid = 0; lrarg.args.lr_seg.layout_type = arg->type; lrarg.args.lr_seg.iomode = res->segment.io_mode; lrarg.args.lr_seg.offset = 0; lrarg.args.lr_seg.length = NFS4_UINT64_MAX; rc = gpfs_ganesha(OPENHANDLE_LAYOUT_RETURN, &lrarg); errsv = errno; LogDebug(COMPONENT_PNFS, "GPFSFSAL_layoutreturn rc %d", rc); if (rc != 0) { LogDebug(COMPONENT_PNFS, "GPFSFSAL_layoutget rc %d", rc); if (errsv == EUNATCH) LogFatal(COMPONENT_PNFS, "GPFS Returned EUNATCH"); } return nfs_status; } /** * @brief Potentially return one layout segment * * Since we don't make any reservations, in this version, or get any * pins to release, always succeed * * @param[in] obj_pub Public object handle * @param[in] req_ctx Request context * @param[in] lrf_body Nothing for us * @param[in] arg Input arguments of the function * * @return Valid error codes in RFC 5661, p. 367. */ static nfsstat4 layoutreturn(struct fsal_obj_handle *obj_hdl, struct req_op_context *req_ctx, XDR *lrf_body, const struct fsal_layoutreturn_arg *arg) { struct layoutreturn_arg larg; struct gpfs_fsal_obj_handle *myself; /* The private 'full' object handle */ struct gpfs_file_handle *gpfs_handle; int errsv = 0; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; int rc = 0; /* Sanity check on type */ if (arg->lo_type != LAYOUT4_NFSV4_1_FILES) { LogCrit(COMPONENT_PNFS, "Unsupported layout type: %x", arg->lo_type); return NFS4ERR_UNKNOWN_LAYOUTTYPE; } myself = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); gpfs_handle = myself->handle; if (arg->dispose) { larg.mountdirfd = export_fd; larg.handle = gpfs_handle; larg.args.lr_return_type = arg->lo_type; larg.args.lr_reclaim = (arg->circumstance == circumstance_reclaim); larg.args.lr_seg.clientid = 0; larg.args.lr_seg.layout_type = arg->lo_type; larg.args.lr_seg.iomode = arg->spec_segment.io_mode; larg.args.lr_seg.offset = arg->spec_segment.offset; larg.args.lr_seg.length = arg->spec_segment.length; rc = gpfs_ganesha(OPENHANDLE_LAYOUT_RETURN, &larg); errsv = errno; if (rc != 0) { LogDebug(COMPONENT_PNFS, "GPFSFSAL_layoutreturn rc %d", rc); if (errsv == EUNATCH) LogFatal(COMPONENT_PNFS, "GPFS Returned EUNATCH"); return NFS4ERR_NOMATCHING_LAYOUT; } } return NFS4_OK; } /** * @brief Commit a segment of a layout * * Update the size and time for a file accessed through a layout. * * @param[in] obj_pub Public object handle * @param[in] req_ctx Request context * @param[in] lou_body An XDR stream containing the layout * type-specific portion of the LAYOUTCOMMIT * arguments. * @param[in] arg Input arguments of the function * @param[in,out] res In/out and output arguments of the function * * @return Valid error codes in RFC 5661, p. 366. */ static nfsstat4 layoutcommit(struct fsal_obj_handle *obj_hdl, struct req_op_context *req_ctx, XDR *lou_body, const struct fsal_layoutcommit_arg *arg, struct fsal_layoutcommit_res *res) { struct gpfs_fsal_obj_handle *myself; /* The private 'full' object handle */ struct gpfs_file_handle *gpfs_handle; int rc = 0; struct layoutcommit_arg targ; int errsv = 0; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; /* Sanity check on type */ if (arg->type != LAYOUT4_NFSV4_1_FILES) { LogCrit(COMPONENT_PNFS, "Unsupported layout type: %x", arg->type); return NFS4ERR_UNKNOWN_LAYOUTTYPE; } myself = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); gpfs_handle = myself->handle; targ.mountdirfd = export_fd; targ.handle = gpfs_handle; targ.xdr = NULL; targ.offset = arg->segment.offset; targ.length = arg->segment.length; targ.reclaim = arg->reclaim; /* True if this is a reclaim commit */ targ.new_offset = arg->new_offset; /* True if the client has suggested a new offset */ if (arg->new_offset) targ.last_write = arg->last_write; /* The offset of the last byte written */ targ.time_changed = arg->time_changed; /*True if provided a new mtime*/ if (arg->time_changed) { targ.new_time.t_sec = arg->new_time.seconds; targ.new_time.t_nsec = arg->new_time.nseconds; } rc = gpfs_ganesha(OPENHANDLE_LAYOUT_COMMIT, &targ); errsv = errno; if (rc != 0) { LogDebug(COMPONENT_PNFS, "GPFSFSAL_layoutcommit rc %d", rc); if (errsv == EUNATCH) LogFatal(COMPONENT_PNFS, "GPFS Returned EUNATCH"); return posix2nfs4_error(-rc); } res->size_supplied = false; res->commit_done = true; return NFS4_OK; } /** * @brief set ops layout * * @param ops reference to object */ void handle_ops_pnfs(struct fsal_obj_ops *ops) { ops->layoutget = layoutget; ops->layoutreturn = layoutreturn; ops->layoutcommit = layoutcommit; } nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/fsal_rename.c000066400000000000000000000055401324272410200212120ustar00rootroot00000000000000/** * @file fsal_rename.c * @date $Date: 2006/01/24 13:45:37 $ * @brief object renaming/moving function. * * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ #include "config.h" #include "fsal.h" #include "fsal_internal.h" #include "fsal_convert.h" #include "gpfs_methods.h" /** * @brief Change name and/or parent dir of a filesystem object. * * @param old_hdl Source parent directory of the object is to be moved/renamed. * @param old_name Current name of the object to be moved/renamed. * @param new_hdl Target parent directory for the object. * @param new_name New name for the object. * @param op_ctx Authentication context for the operation (user,...). * * @return Major error codes : * - ERR_FSAL_NO_ERROR (no error) * - Another error code if an error occured. */ fsal_status_t GPFSFSAL_rename(struct fsal_obj_handle *old_hdl, const char *old_name, struct fsal_obj_handle *new_hdl, const char *new_name, const struct req_op_context *op_ctx) { fsal_status_t status; struct stat buffstat; struct gpfs_fsal_obj_handle *old_gpfs_hdl, *new_gpfs_hdl; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; old_gpfs_hdl = container_of(old_hdl, struct gpfs_fsal_obj_handle, obj_handle); new_gpfs_hdl = container_of(new_hdl, struct gpfs_fsal_obj_handle, obj_handle); /* build file paths */ status = fsal_internal_stat_name(export_fd, old_gpfs_hdl->handle, old_name, &buffstat); if (FSAL_IS_ERROR(status)) return status; /************************************* * Rename the file on the filesystem * *************************************/ fsal_set_credentials(op_ctx->creds); status = fsal_internal_rename_fh(export_fd, old_gpfs_hdl->handle, new_gpfs_hdl->handle, old_name, new_name); fsal_restore_ganesha_credentials(); if (FSAL_IS_ERROR(status)) return status; return fsalstat(ERR_FSAL_NO_ERROR, 0); } nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/fsal_stats_gpfs.c000066400000000000000000000205401324272410200221150ustar00rootroot00000000000000/* */ /* Copyright (C) 2017 International Business Machines */ /* All rights reserved. */ /* */ /* Redistribution and use in source and binary forms, with or without */ /* modification, are permitted provided that the following conditions */ /* are met: */ /* */ /* 1. Redistributions of source code must retain the above copyright notice, */ /* this list of conditions and the following disclaimer. */ /* 2. Redistributions in binary form must reproduce the above copyright */ /* notice, this list of conditions and the following disclaimer in the */ /* documentation and/or other materials provided with the distribution. */ /* 3. The name of the author may not be used to endorse or promote products */ /* derived from this software without specific prior written */ /* permission. */ /* */ /* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR */ /* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES */ /* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. */ /* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL*/ /* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO*/ /* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;*/ /* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, */ /* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR */ /* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF */ /* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* */ /* %Z%%M% %I% %W% %G% %U% */ #include "config.h" #include "gsh_list.h" #ifdef USE_DBUS #include "gsh_dbus.h" #endif #include #include "fsal.h" #include "fsal_internal.h" #include "nfs_proto_functions.h" #include "include/gpfs_nfs.h" struct fsal_op_stats gpfs_op_stats[GPFS_STAT_MAX_OPS]; struct fsal_stats gpfs_stats; #ifdef USE_DBUS /** * * @brief Return string for gpfs opcode * * * * @param[in] gpfs opcode * */ static char *gpfs_opcode_to_name(int opcode) { switch (opcode) { case OPENHANDLE_GET_VERSION: return "GET_VERSION"; case OPENHANDLE_NAME_TO_HANDLE: return "NAME_TO_HANDLE"; case OPENHANDLE_OPEN_BY_HANDLE: return "OPEN_BY_HANDLE"; case OPENHANDLE_LAYOUT_TYPE: return "LAYOUT_TYPE"; case OPENHANDLE_GET_DEVICEINFO: return "GET_DEVICEINFO"; case OPENHANDLE_GET_DEVICELIST: return "GET_DEVICELIST"; case OPENHANDLE_LAYOUT_GET: return "LAYOUT_GET"; case OPENHANDLE_LAYOUT_RETURN: return "LAYOUT_RETURN"; case OPENHANDLE_INODE_UPDATE: return "INODE_UPDATE"; case OPENHANDLE_GET_XSTAT: return "GET_XSTAT"; case OPENHANDLE_SET_XSTAT: return "SET_XSTAT"; case OPENHANDLE_CHECK_ACCESS: return "CHECK_ACCESS"; case OPENHANDLE_OPEN_SHARE_BY_HANDLE: return "OPEN_SHARE_BY_HANDLE"; case OPENHANDLE_GET_LOCK: return "GET_LOCK"; case OPENHANDLE_SET_LOCK: return "SET_LOCK"; case OPENHANDLE_THREAD_UPDATE: return "THREAD_UPDATE"; case OPENHANDLE_LAYOUT_COMMIT: return "LAYOUT_COMMIT"; case OPENHANDLE_DS_READ: return "DS_READ"; case OPENHANDLE_DS_WRITE: return "DS_WRITE"; case OPENHANDLE_GET_VERIFIER: return "GET_VERIFIER"; case OPENHANDLE_FSYNC: return "FSYNC"; case OPENHANDLE_SHARE_RESERVE: return "SHARE_RESERVE"; case OPENHANDLE_GET_NODEID: return "GET_NODEID"; case OPENHANDLE_SET_DELEGATION: return "SET_DELEGATION"; case OPENHANDLE_CLOSE_FILE: return "CLOSE_FILE"; case OPENHANDLE_LINK_BY_FH: return "LINK_BY_FH"; case OPENHANDLE_RENAME_BY_FH: return "RENAME_BY_FH"; case OPENHANDLE_STAT_BY_NAME: return "STAT_BY_NAME"; case OPENHANDLE_GET_HANDLE: return "GET_HANDLE"; case OPENHANDLE_READLINK_BY_FH: return "READLINK_BY_FH"; case OPENHANDLE_UNLINK_BY_NAME: return "UNLINK_BY_NAME"; case OPENHANDLE_CREATE_BY_NAME: return "CREATE_BY_NAME"; case OPENHANDLE_READ_BY_FD: return "READ_BY_FD"; case OPENHANDLE_WRITE_BY_FD: return "WRITE_BY_FD"; case OPENHANDLE_CREATE_BY_NAME_ATTR: return "CREATE_BY_NAME_ATTR"; case OPENHANDLE_GRACE_PERIOD: return "GRACE_PERIOD"; case OPENHANDLE_ALLOCATE_BY_FD: return "ALLOCATE_BY_FD"; case OPENHANDLE_REOPEN_BY_FD: return "REOPEN_BY_FD"; case OPENHANDLE_FADVISE_BY_FD: return "FADVISE_BY_FD"; case OPENHANDLE_SEEK_BY_FD: return "SEEK_BY_FD"; case OPENHANDLE_STATFS_BY_FH: return "STATFS_BY_FH"; case OPENHANDLE_GETXATTRS: return "GETXATTRS"; case OPENHANDLE_SETXATTRS: return "SETXATTRS"; case OPENHANDLE_REMOVEXATTRS: return "REMOVEXATTRS"; case OPENHANDLE_LISTXATTRS: return "LISTXATTRS"; case OPENHANDLE_MKNODE_BY_NAME: return "MKNODE_BY_NAME"; case OPENHANDLE_reserved: return "reserved"; case OPENHANDLE_TRACE_ME: return "TRACE_ME"; case OPENHANDLE_QUOTA: return "QUOTA"; case OPENHANDLE_FS_LOCATIONS: return "FS_LOCATIONS"; default: return "UNMONITORED"; } } #endif /* USE_DBUS */ /** @fn prepare_for_stats(struct fsal_module *fsal_hdl) * * @brief prepare the structure which will hold the stats * */ void prepare_for_stats(struct fsal_module *fsal_hdl) { int idx, op; gpfs_stats.total_ops = GPFS_TOTAL_OPS; gpfs_stats.op_stats = gpfs_op_stats; fsal_hdl->stats = &gpfs_stats; for (op = GPFS_MIN_OP; op <= GPFS_MAX_OP; op++) { idx = gpfs_op2index(op); fsal_hdl->stats->op_stats[idx].op_code = op; } } #ifdef USE_DBUS /** @fn fsal_gpfs_extract_stats(struct fsal_module *fsal_hdl, void *iter) * * @brief Extract the FSAL specific performance counters * */ void fsal_gpfs_extract_stats(struct fsal_module *fsal_hdl, void *iter) { struct timespec timestamp; DBusMessageIter struct_iter; DBusMessageIter *iter1 = (DBusMessageIter *)iter; char *message; uint64_t total_ops, total_resp, min_resp, max_resp; double avg_resp; int i; uint16_t val; struct fsal_stats *gpfs_stats; now(×tamp); dbus_append_timestamp(iter, ×tamp); gpfs_stats = fsal_hdl->stats; dbus_message_iter_open_container(iter1, DBUS_TYPE_STRUCT, NULL, &struct_iter); val = atomic_fetch_uint16_t(&gpfs_stats->total_ops); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT16, &val); for (i = 0; i < GPFS_STAT_PH_INDEX; i++) { if (i == GPFS_STAT_NO_OP_1 || i == GPFS_STAT_NO_OP_2 || i == GPFS_STAT_NO_OP_3) continue; /* Get all the data */ total_ops = atomic_fetch_uint64_t( &gpfs_stats->op_stats[i].num_ops); total_resp = atomic_fetch_uint64_t( &gpfs_stats->op_stats[i].resp_time); min_resp = atomic_fetch_uint64_t( &gpfs_stats->op_stats[i].resp_time_min); max_resp = atomic_fetch_uint64_t( &gpfs_stats->op_stats[i].resp_time_max); message = gpfs_opcode_to_name(gpfs_stats->op_stats[i].op_code); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &message); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT16, &gpfs_stats->op_stats[i].op_code); if (total_ops == 0) continue; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &total_ops); avg_resp = (double) total_resp / total_ops; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_DOUBLE, &avg_resp); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &min_resp); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &max_resp); } dbus_message_iter_close_container(iter1, &struct_iter); } #endif /* USE_DBUS */ void fsal_gpfs_reset_stats(struct fsal_module *fsal_hdl) { int i; struct fsal_stats *gpfs_stats; gpfs_stats = fsal_hdl->stats; /* reset all the counters */ for (i = 0; i < GPFS_STAT_PH_INDEX; i++) { atomic_store_uint64_t( &gpfs_stats->op_stats[i].num_ops, 0); atomic_store_uint64_t( &gpfs_stats->op_stats[i].resp_time, 0); atomic_store_uint64_t( &gpfs_stats->op_stats[i].resp_time_min, 0); atomic_store_uint64_t( &gpfs_stats->op_stats[i].resp_time_max, 0); } } nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/fsal_symlinks.c000066400000000000000000000122031324272410200216060ustar00rootroot00000000000000/** * @file fsal_symlinks.c * @date $Date: 2005/07/29 09:39:04 $ * @brief symlinks operations. * * * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ #include "config.h" #include "fsal.h" #include "fsal_internal.h" #include "fsal_convert.h" #include "gpfs_methods.h" #include #include /** * @brief Read the content of a symbolic link. * * @param dir_hdl Handle of the link to be read. * @param op_ctx Authentication context for the operation (user,...). * @param link_content Fsal path struct where the link content is to be stored * @param link_len Len of content buff. Out actual len of content. * * @return Major error codes : * - ERR_FSAL_NO_ERROR (no error) * - Another error code if an error occured. */ fsal_status_t GPFSFSAL_readlink(struct fsal_obj_handle *dir_hdl, const struct req_op_context *op_ctx, char *link_content, size_t link_len) { struct gpfs_fsal_obj_handle *gpfs_hdl; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; gpfs_hdl = container_of(dir_hdl, struct gpfs_fsal_obj_handle, obj_handle); /* Read the link on the filesystem */ return fsal_readlink_by_handle(export_fd, gpfs_hdl->handle, link_content, link_len); } /** * @brief Create a symbolic link. * * @param dir_hdl Handle of the parent dir where the link is to be created. * @param linkname Name of the link to be created. * @param linkcontent Content of the link to be created. * @param op_ctx Authentication context for the operation (user,...). * @param accessmode Mode of the link to be created. * It has no sense in POSIX filesystems. * @param gpfs_fh Pointer to the handle of the created symlink. * @param link_attr Attributes of the newly created symlink. * 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_symlink(struct fsal_obj_handle *dir_hdl, const char *linkname, const char *linkcontent, const struct req_op_context *op_ctx, uint32_t accessmode, struct gpfs_file_handle *gpfs_fh, struct attrlist *link_attr) { int rc, errsv; fsal_status_t status; int fd; struct gpfs_fsal_obj_handle *gpfs_hdl; struct gpfs_filesystem *gpfs_fs; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; gpfs_hdl = container_of(dir_hdl, struct gpfs_fsal_obj_handle, obj_handle); gpfs_fs = dir_hdl->fs->private_data; /* Tests if symlinking is allowed by configuration. */ if (!op_ctx->fsal_export->exp_ops.fs_supports(op_ctx->fsal_export, fso_symlink_support)) return fsalstat(ERR_FSAL_NOTSUPP, 0); status = fsal_internal_handle2fd(export_fd, gpfs_hdl->handle, &fd, O_RDONLY | O_DIRECTORY); if (FSAL_IS_ERROR(status)) return status; /* build symlink path */ /* create the symlink on the filesystem using the credentials * for proper ownership assignment. */ fsal_set_credentials(op_ctx->creds); rc = symlinkat(linkcontent, fd, linkname); errsv = errno; fsal_restore_ganesha_credentials(); if (rc) { close(fd); return fsalstat(posix2fsal_error(errsv), errsv); } /* now get the associated handle, while there is a race, there is also a race lower down */ status = fsal_internal_get_handle_at(fd, linkname, gpfs_fh, export_fd); if (FSAL_IS_ERROR(status)) { close(fd); return status; } /* get attributes */ status = GPFSFSAL_getattrs(op_ctx->fsal_export, gpfs_fs, op_ctx, gpfs_fh, link_attr); if (!FSAL_IS_ERROR(status) && link_attr->type != SYMBOLIC_LINK) { /* We could wind up not failing the creation of the symlink * and the only way we know is that the object type isn't * a symlink. */ fsal_release_attrs(link_attr); status = fsalstat(ERR_FSAL_EXIST, 0); } close(fd); return status; } nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/fsal_unlink.c000066400000000000000000000050501324272410200212370ustar00rootroot00000000000000/** * @file fsal_unlink.c * @date $Date: 2006/01/24 13:45:37 $ * @brief object removing function. * * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ #include "config.h" #include "fsal.h" #include "fsal_internal.h" #include "fsal_convert.h" #include "gpfs_methods.h" #include /** * @brief Remove a filesystem object . * * @param dir_hdl Handle of the parent directory of the object to be deleted. * @param object_name Name of the object to be removed. * @param op_ctx Authentication context for the operation (user,...). * * @return Major error codes : * - ERR_FSAL_NO_ERROR (no error) * - Another error code if an error occured. */ fsal_status_t GPFSFSAL_unlink(struct fsal_obj_handle *dir_hdl, const char *object_name, const struct req_op_context *op_ctx) { fsal_status_t status; gpfsfsal_xstat_t buffxstat; struct gpfs_fsal_obj_handle *gpfs_hdl; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; gpfs_hdl = container_of(dir_hdl, struct gpfs_fsal_obj_handle, obj_handle); /* get file metadata */ status = fsal_internal_stat_name(export_fd, gpfs_hdl->handle, object_name, &buffxstat.buffstat); if (FSAL_IS_ERROR(status)) return status; /****************************** * DELETE FROM THE FILESYSTEM * ******************************/ fsal_set_credentials(op_ctx->creds); status = fsal_internal_unlink(export_fd, gpfs_hdl->handle, object_name, &buffxstat.buffstat); fsal_restore_ganesha_credentials(); if (FSAL_IS_ERROR(status)) return status; return fsalstat(ERR_FSAL_NO_ERROR, 0); } nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/fsal_up.c000066400000000000000000000323201324272410200203630ustar00rootroot00000000000000/** * @file fsal_up.c * @brief FSAL Upcall Interface * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ #include "config.h" #include "fsal.h" #include "fsal_up.h" #include "fsal_internal.h" #include "fsal_convert.h" #include "gpfs_methods.h" #include "nfs_init.h" #include #include #include #include /* Setup up_vector. File system's upvector_mutex must be held */ static bool setup_up_vector(struct gpfs_filesystem *gpfs_fs) { struct gpfs_filesystem_export_map *map; map = glist_first_entry(&gpfs_fs->exports, struct gpfs_filesystem_export_map, on_exports); if (!map) return false; gpfs_fs->up_vector = (struct fsal_up_vector *)map->exp->export.up_ops; /* wait for upcall readiness */ up_ready_wait(gpfs_fs->up_vector); /* Set up op_ctx for the thread */ op_ctx = &gpfs_fs->req_ctx; op_ctx->fsal_export = gpfs_fs->up_vector->up_fsal_export; op_ctx->ctx_export = gpfs_fs->up_vector->up_gsh_export; return true; } /** * @brief Up Thread * * @param Arg reference to void * */ void *GPFSFSAL_UP_Thread(void *Arg) { struct gpfs_filesystem *gpfs_fs = Arg; struct fsal_up_vector *event_func; char thr_name[16]; int rc = 0; struct pnfs_deviceid devid; struct stat buf; struct glock fl; struct callback_arg callback; struct gpfs_file_handle handle; int reason = 0; int flags = 0; unsigned int *fhP; int retry = 0; struct gsh_buffdesc key; uint32_t expire_time_attr = 0; uint32_t upflags; int errsv = 0; fsal_status_t fsal_status = {0,}; #ifdef _VALGRIND_MEMCHECK memset(&handle, 0, sizeof(handle)); memset(&buf, 0, sizeof(buf)); memset(&fl, 0, sizeof(fl)); memset(&devid, 0, sizeof(devid)); #endif snprintf(thr_name, sizeof(thr_name), "fsal_up_%"PRIu64".%"PRIu64, gpfs_fs->fs->dev.major, gpfs_fs->fs->dev.minor); SetNameFunction(thr_name); LogFullDebug(COMPONENT_FSAL_UP, "Initializing FSAL Callback context for %d.", gpfs_fs->root_fd); /* wait for nfs init completion to get general_fridge * initialized which is needed for processing some upcall events */ nfs_init_wait(); /* Start querying for events and processing. */ while (1) { LogFullDebug(COMPONENT_FSAL_UP, "Requesting event from FSAL Callback interface for %d.", gpfs_fs->root_fd); handle.handle_size = GPFS_MAX_FH_SIZE; handle.handle_key_size = OPENHANDLE_KEY_LEN; handle.handle_version = OPENHANDLE_VERSION; callback.interface_version = GPFS_INTERFACE_VERSION + GPFS_INTERFACE_SUB_VER; callback.mountdirfd = gpfs_fs->root_fd; callback.handle = &handle; callback.reason = &reason; callback.flags = &flags; callback.buf = &buf; callback.fl = &fl; callback.dev_id = &devid; callback.expire_attr = &expire_time_attr; rc = gpfs_ganesha(OPENHANDLE_INODE_UPDATE, &callback); errsv = errno; if (rc != 0) { rc = -(rc); if (rc > GPFS_INTERFACE_VERSION) { LogFatal(COMPONENT_FSAL_UP, "Ganesha version %d mismatch GPFS version %d.", callback.interface_version, rc); return NULL; } if (errsv == EINTR) continue; LogCrit(COMPONENT_FSAL_UP, "OPENHANDLE_INODE_UPDATE failed for %d. rc %d, errno %d (%s) reason %d", gpfs_fs->root_fd, rc, errsv, strerror(errsv), reason); /* @todo 1000 retry logic will go away once the * OPENHANDLE_INODE_UPDATE ioctl separates EINTR * and EUNATCH. */ if (errsv == EUNATCH && ++retry > 1000) LogFatal(COMPONENT_FSAL_UP, "GPFS file system %d has gone away.", gpfs_fs->root_fd); continue; } retry = 0; /* flags is int, but only the least significant 2 bytes * are valid. We are getting random bits into the upper * 2 bytes! Workaround this until the kernel module * gets fixed. */ flags = flags & 0xffff; LogDebug(COMPONENT_FSAL_UP, "inode update: rc %d reason %d update ino %" PRId64 " flags:%x", rc, reason, callback.buf->st_ino, flags); LogFullDebug(COMPONENT_FSAL_UP, "inode update: flags:%x callback.handle:%p handle size = %u handle_type:%d handle_version:%d key_size = %u handle_fsid=%X.%X f_handle:%p expire: %d", *callback.flags, callback.handle, callback.handle->handle_size, callback.handle->handle_type, callback.handle->handle_version, callback.handle->handle_key_size, callback.handle->handle_fsid[0], callback.handle->handle_fsid[1], callback.handle->f_handle, expire_time_attr); callback.handle->handle_version = OPENHANDLE_VERSION; fhP = (int *)&(callback.handle->f_handle[0]); LogFullDebug(COMPONENT_FSAL_UP, " inode update: handle %08x %08x %08x %08x %08x %08x %08x", fhP[0], fhP[1], fhP[2], fhP[3], fhP[4], fhP[5], fhP[6]); /* Here is where we decide what type of event this is * ... open,close,read,...,invalidate? */ key.addr = &handle; key.len = handle.handle_key_size; LogDebug(COMPONENT_FSAL_UP, "Received event to process for %d", gpfs_fs->root_fd); /* We need valid up_vector while processing some of the * events below. Setup up vector and hold the mutex while * processing the event for the entire duration. */ PTHREAD_MUTEX_lock(&gpfs_fs->upvector_mutex); if (!setup_up_vector(gpfs_fs)) { PTHREAD_MUTEX_unlock(&gpfs_fs->upvector_mutex); return NULL; } event_func = gpfs_fs->up_vector; switch (reason) { case INODE_LOCK_GRANTED: /* Lock Event */ case INODE_LOCK_AGAIN: /* Lock Event */ { LogMidDebug(COMPONENT_FSAL_UP, "%s: owner %p pid %d type %d start %lld len %lld", reason == INODE_LOCK_GRANTED ? "inode lock granted" : "inode lock again", fl.lock_owner, fl.flock.l_pid, fl.flock.l_type, (long long)fl.flock.l_start, (long long)fl.flock.l_len); fsal_lock_param_t lockdesc = { .lock_sle_type = FSAL_POSIX_LOCK, .lock_type = fl.flock.l_type, .lock_start = fl.flock.l_start, .lock_length = fl.flock.l_len }; if (reason == INODE_LOCK_AGAIN) fsal_status = up_async_lock_avail( general_fridge, event_func, &key, fl.lock_owner, &lockdesc, NULL, NULL); else fsal_status = up_async_lock_grant( general_fridge, event_func, &key, fl.lock_owner, &lockdesc, NULL, NULL); } break; case BREAK_DELEGATION: /* Delegation Event */ LogDebug(COMPONENT_FSAL_UP, "delegation recall: flags:%x ino %" PRId64, flags, callback.buf->st_ino); fsal_status = up_async_delegrecall(general_fridge, event_func, &key, NULL, NULL); break; case LAYOUT_FILE_RECALL: /* Layout file recall Event */ { struct pnfs_segment segment = { .offset = 0, .length = UINT64_MAX, .io_mode = LAYOUTIOMODE4_ANY }; LogDebug(COMPONENT_FSAL_UP, "layout file recall: flags:%x ino %" PRId64, flags, callback.buf->st_ino); fsal_status = up_async_layoutrecall( general_fridge, event_func, &key, LAYOUT4_NFSV4_1_FILES, false, &segment, NULL, NULL, NULL, NULL); } break; case LAYOUT_RECALL_ANY: /* Recall all layouts Event */ LogDebug(COMPONENT_FSAL_UP, "layout recall any: flags:%x ino %" PRId64, flags, callback.buf->st_ino); /** * @todo This functionality needs to be implemented as a * bulk FSID CB_LAYOUTRECALL. RECALL_ANY isn't suitable * since it can't be restricted to just one FSAL. Also * an FSID LAYOUTRECALL lets you have multiplke * filesystems exported from one FSAL and not yank layouts * on all of them when you only need to recall them for one. */ break; case LAYOUT_NOTIFY_DEVICEID: /* Device update Event */ LogDebug(COMPONENT_FSAL_UP, "layout dev update: flags:%x ino %" PRId64 " seq %d fd %d fsid 0x%" PRIx64, flags, callback.buf->st_ino, devid.device_id2, devid.device_id4, devid.devid); memset(&devid, 0, sizeof(devid)); devid.fsal_id = FSAL_ID_GPFS; fsal_status = up_async_notify_device(general_fridge, event_func, NOTIFY_DEVICEID4_DELETE_MASK, LAYOUT4_NFSV4_1_FILES, &devid, true, NULL, NULL); break; case INODE_UPDATE: /* Update Event */ { struct attrlist attr; LogMidDebug(COMPONENT_FSAL_UP, "inode update: flags:%x update ino %" PRId64 " n_link:%d", flags, callback.buf->st_ino, (int)callback.buf->st_nlink); /** @todo: This notification is completely * asynchronous. If we happen to change some * of the attributes later, we end up over * writing those with these possibly stale * values as we don't know when we get to * update with these up call values. We should * probably use time stamp or let the up call * always provide UP_TIMES flag in which case * we can compare the current ctime vs up call * provided ctime before updating the * attributes. * * For now, we think size attribute is more * important than others, so invalidate the * attributes and let ganesha fetch attributes * as needed if this update includes a size * change. We are careless for other attribute * changes, and we may end up with stale values * until this gets fixed! */ if (flags & (UP_SIZE | UP_SIZE_BIG)) { fsal_status = event_func->invalidate( event_func, &key, FSAL_UP_INVALIDATE_CACHE); break; } /* Check for accepted flags, any other changes just invalidate. */ if (flags & ~(UP_SIZE | UP_NLINK | UP_MODE | UP_OWN | UP_TIMES | UP_ATIME | UP_SIZE_BIG)) { fsal_status = event_func->invalidate( event_func, &key, FSAL_UP_INVALIDATE_CACHE); } else { /* buf may not have all attributes set. * Set the mask to what is changed */ attr.valid_mask = 0; attr.acl = NULL; upflags = 0; if (flags & UP_SIZE) attr.valid_mask |= ATTR_CHGTIME | ATTR_CHANGE | ATTR_SIZE | ATTR_SPACEUSED; if (flags & UP_SIZE_BIG) { attr.valid_mask |= ATTR_CHGTIME | ATTR_CHANGE | ATTR_SIZE | ATTR_SPACEUSED; upflags |= fsal_up_update_filesize_inc | fsal_up_update_spaceused_inc; } if (flags & UP_MODE) attr.valid_mask |= ATTR_CHGTIME | ATTR_CHANGE | ATTR_MODE; if (flags & UP_OWN) attr.valid_mask |= ATTR_CHGTIME | ATTR_CHANGE | ATTR_OWNER | ATTR_GROUP | ATTR_MODE; if (flags & UP_TIMES) attr.valid_mask |= ATTR_CHGTIME | ATTR_CHANGE | ATTR_ATIME | ATTR_CTIME | ATTR_MTIME; if (flags & UP_ATIME) attr.valid_mask |= ATTR_CHGTIME | ATTR_CHANGE | ATTR_ATIME; if (flags & UP_NLINK) attr.valid_mask |= ATTR_NUMLINKS; attr.request_mask = attr.valid_mask; attr.expire_time_attr = expire_time_attr; posix2fsal_attributes(&buf, &attr); fsal_status = event_func->update( event_func, &key, &attr, upflags); if ((flags & UP_NLINK) && (attr.numlinks == 0)) { upflags = fsal_up_nlink; attr.valid_mask = 0; attr.request_mask = 0; fsal_status = up_async_update (general_fridge, event_func, &key, &attr, upflags, NULL, NULL); } } } break; case THREAD_STOP: /* We wanted to terminate this thread */ LogDebug(COMPONENT_FSAL_UP, "Terminating the GPFS up call thread for %d", gpfs_fs->root_fd); PTHREAD_MUTEX_unlock(&gpfs_fs->upvector_mutex); return NULL; case INODE_INVALIDATE: LogMidDebug(COMPONENT_FSAL_UP, "inode invalidate: flags:%x update ino %" PRId64, flags, callback.buf->st_ino); upflags = FSAL_UP_INVALIDATE_CACHE; fsal_status = event_func->invalidate_close( event_func, &key, upflags); break; case THREAD_PAUSE: /* File system image is probably going away, but * we don't need to do anything here as we * eventually get other errors that stop this * thread. */ PTHREAD_MUTEX_unlock(&gpfs_fs->upvector_mutex); continue; /* get next event */ default: PTHREAD_MUTEX_unlock(&gpfs_fs->upvector_mutex); LogWarn(COMPONENT_FSAL_UP, "Unknown event: %d", reason); continue; } PTHREAD_MUTEX_unlock(&gpfs_fs->upvector_mutex); if (FSAL_IS_ERROR(fsal_status) && fsal_status.major != ERR_FSAL_NOENT) { LogWarn(COMPONENT_FSAL_UP, "Event %d could not be processed for fd %d rc %s", reason, gpfs_fs->root_fd, fsal_err_txt(fsal_status)); } } return NULL; } /* GPFSFSAL_UP_Thread */ nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/gpfs_methods.h000066400000000000000000000124471324272410200214310ustar00rootroot00000000000000/* GPFS methods for handles */ #ifndef GPFS_METHODS_H #define GPFS_METHODS_H #include #include "include/gpfs_nfs.h" /* private helpers from export */ struct fsal_staticfsinfo_t *gpfs_staticinfo(struct fsal_module *hdl); /* method proto linkage to handle.c for export */ fsal_status_t gpfs_lookup_path(struct fsal_export *exp_hdl, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out); fsal_status_t gpfs_create_handle(struct fsal_export *exp_hdl, struct gsh_buffdesc *hdl_desc, struct fsal_obj_handle **handle, struct attrlist *attrs_out); /* * GPFS internal export */ struct gpfs_fsal_export { struct fsal_export export; struct fsal_filesystem *root_fs; struct glist_head filesystems; int export_fd; bool pnfs_ds_enabled; bool pnfs_mds_enabled; bool use_acl; }; /* * GPFS internal filesystem */ struct gpfs_filesystem { struct fsal_filesystem *fs; int root_fd; struct glist_head exports; bool up_thread_started; pthread_t up_thread; /* upcall thread */ /* we have an upcall thread for each file system. The upcall * thread needs a valid export/op_ctx for processing some of the * upcall requests. We use upvector_mutex to get an export from * the list of exports in a file system. The following * up_vector points to up_ops in such an export. */ pthread_mutex_t upvector_mutex; struct fsal_up_vector *up_vector; struct req_op_context req_ctx; }; /* * Link GPFS file systems and exports * Supports a many-to-many relationship */ struct gpfs_filesystem_export_map { struct gpfs_fsal_export *exp; struct gpfs_filesystem *fs; struct glist_head on_exports; struct glist_head on_filesystems; }; void gpfs_extract_fsid(struct gpfs_file_handle *fh, struct fsal_fsid__ *fsid); void gpfs_unexport_filesystems(struct gpfs_fsal_export *exp); fsal_status_t gpfs_merge(struct fsal_obj_handle *orig_hdl, struct fsal_obj_handle *dupe_hdl); /* * GPFS internal object handle * handle is a pointer because * a) the last element of file_handle is a char[] meaning variable len... * b) we cannot depend on it *always* being last or being the only * variable sized struct here... a pointer is safer. * wrt locks, should this be a lock counter?? * AF_UNIX sockets are strange ducks. I personally cannot see why they * are here except for the ability of a client to see such an animal with * an 'ls' or get rid of one with an 'rm'. You can't open them in the * usual file way so open_by_handle_at leads to a deadend. To work around * this, we save the args that were used to mknod or lookup the socket. */ struct gpfs_fsal_obj_handle { struct fsal_obj_handle obj_handle; struct gpfs_file_handle *handle; union { struct { struct fsal_share share; struct gpfs_fd fd; } file; struct { unsigned char *link_content; int link_size; } symlink; } u; }; /* I/O management */ struct gpfs_fsal_obj_handle *alloc_handle(struct gpfs_file_handle *fh, struct fsal_filesystem *fs, struct attrlist *attributes, const char *link_content, struct fsal_export *exp_hdl); 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); fsal_status_t gpfs_reopen2(struct fsal_obj_handle *obj_hdl, struct state_t *state, fsal_openflags_t openflags); fsal_status_t gpfs_read2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buffer_size, void *buffer, size_t *read_amount, bool *end_of_file, struct io_info *info); fsal_status_t gpfs_write2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buffer_size, void *buffer, size_t *wrote_amount, bool *fsal_stable, struct io_info *info); fsal_status_t gpfs_commit2(struct fsal_obj_handle *obj_hdl, off_t offset, size_t len); fsal_status_t gpfs_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); fsal_status_t gpfs_close2(struct fsal_obj_handle *obj_hdl, struct state_t *state); fsal_status_t gpfs_setattr2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, struct attrlist *attrib_set); fsal_status_t gpfs_read_plus_fd(int my_fs, uint64_t offset, size_t buffer_size, void *buffer, size_t *read_amount, bool *end_of_file, struct io_info *info, int expfd); fsal_status_t gpfs_seek(struct fsal_obj_handle *obj_hdl, struct io_info *info); fsal_status_t gpfs_io_advise(struct fsal_obj_handle *obj_hdl, struct io_hints *hints); fsal_status_t gpfs_share_op(struct fsal_obj_handle *obj_hdl, void *p_owner, fsal_share_param_t request_share); fsal_status_t gpfs_close(struct fsal_obj_handle *obj_hdl); /* Internal GPFS method linkage to export object */ fsal_status_t gpfs_create_export(struct fsal_module *fsal_hdl, void *parse_node, struct config_error_type *err_type, const struct fsal_up_vector *up_ops); #endif nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/gpfsext.c000066400000000000000000000120161324272410200204120ustar00rootroot00000000000000/**------------------------------------------------------------------------ * @file gpfsext.c * @brief Use ioctl to call into the GPFS kernel module. * * NAME: gpfs_ganesha() * * FUNCTION: Use ioctl to call into the GPFS kernel module. * If GPFS isn't loaded they receive ENOSYS. * * Returns: 0 Successful * -1 Failure * * Errno: ENOSYS No quality of service function available * ENOENT File not found * EINVAL Not a GPFS file * ESTALE cached fs information was invalid *-------------------------------------------------------------------------*/ #include "config.h" #include #include #include #include #include #ifdef _VALGRIND_MEMCHECK #include #include #include #include "include/gpfs.h" #endif #include "fsal.h" #include "include/gpfs_nfs.h" #include "gsh_config.h" struct kxArgs { signed long arg1; signed long arg2; }; #ifdef _VALGRIND_MEMCHECK static void valgrind_kganesha(struct kxArgs *args) { int op = (int)args->arg1; switch (op) { case OPENHANDLE_STATFS_BY_FH: { struct statfs_arg *arg = (void *)args->arg2; VALGRIND_MAKE_MEM_DEFINED(arg->buf, sizeof(*arg->buf)); break; } case OPENHANDLE_READ_BY_FD: { struct read_arg *arg = (void *)args->arg2; VALGRIND_MAKE_MEM_DEFINED(arg->bufP, arg->length); break; } case OPENHANDLE_NAME_TO_HANDLE: { struct name_handle_arg *arg = (void *)args->arg2; VALGRIND_MAKE_MEM_DEFINED(arg->handle, sizeof(struct gpfs_file_handle)); break; } case OPENHANDLE_GET_HANDLE: { struct get_handle_arg *arg = (void *)args->arg2; VALGRIND_MAKE_MEM_DEFINED(arg->out_fh, sizeof(struct gpfs_file_handle)); break; } case OPENHANDLE_STAT_BY_NAME: { struct stat_name_arg *arg = (void *)args->arg2; VALGRIND_MAKE_MEM_DEFINED(arg->buf, sizeof(*arg->buf)); break; } case OPENHANDLE_CREATE_BY_NAME: { struct create_name_arg *arg = (void *)args->arg2; VALGRIND_MAKE_MEM_DEFINED(arg->new_fh, sizeof(struct gpfs_file_handle)); break; } case OPENHANDLE_READLINK_BY_FH: { struct readlink_fh_arg *arg = (void *)args->arg2; VALGRIND_MAKE_MEM_DEFINED(arg->buffer, arg->size); break; } case OPENHANDLE_GET_XSTAT: { struct xstat_arg *arg = (void *)args->arg2; VALGRIND_MAKE_MEM_DEFINED(arg->buf, sizeof(*arg->buf)); VALGRIND_MAKE_MEM_DEFINED(arg->fsid, sizeof(*arg->fsid)); if (arg->acl) { struct gpfs_acl *gacl; size_t outlen; /* * arg->acl points to an IN/OUT buffer. First * few fields are initialized by the caller and * the rest are filled in by the ioctl call. */ gacl = arg->acl; outlen = gacl->acl_len - offsetof(struct gpfs_acl, acl_nace); VALGRIND_MAKE_MEM_DEFINED(&gacl->acl_nace, outlen); } break; } case OPENHANDLE_WRITE_BY_FD: { struct write_arg *arg = (void *)args->arg2; VALGRIND_MAKE_MEM_DEFINED(arg->stability_got, sizeof(*arg->stability_got)); break; } default: break; } } #endif int gpfs_op2index(int op) { if ((op < GPFS_MIN_OP) || (op > GPFS_MAX_OP) || (op == 103 || op == 104 || op == 105)) return GPFS_STAT_PH_INDEX; else return (op - GPFS_MIN_OP); } /** * @param op Operation * @param *oarg Arguments * * @return Result */ int gpfs_ganesha(int op, void *oarg) { int rc, idx; static int gpfs_fd = -2; struct kxArgs args; struct timespec start_time; struct timespec stop_time; nsecs_elapsed_t resp_time; if (gpfs_fd < 0) { /* If we enable fsal_trace in the config, the following * LogFatal would call us here again for fsal tracing! * Since we can't log as we are unable to open the device, * just exit. * * Also, exit handler _dl_fini() will call gpfs_unload * which will call release_log_facility that tries to * acquire log_rwlock a second time! So do an immediate * exit. */ if (gpfs_fd == -1) /* failed in a prior invocation */ _exit(1); assert(gpfs_fd == -2); gpfs_fd = open(GPFS_DEVNAMEX, O_RDONLY); if (gpfs_fd == -1) LogFatal(COMPONENT_FSAL, "open of %s failed with errno %d", GPFS_DEVNAMEX, errno); (void)fcntl(gpfs_fd, F_SETFD, FD_CLOEXEC); } args.arg1 = op; args.arg2 = (long)oarg; #ifdef _VALGRIND_MEMCHECK valgrind_kganesha(&args); #endif if (nfs_param.core_param.enable_FSALSTATS) { /* Collect FSAL stats */ now(&start_time); rc = ioctl(gpfs_fd, kGanesha, &args); now(&stop_time); resp_time = timespec_diff(&start_time, &stop_time); /* record FSAL stats */ idx = gpfs_op2index(op); (void)atomic_inc_uint64_t(&gpfs_stats.op_stats[idx].num_ops); (void)atomic_add_uint64_t(&gpfs_stats.op_stats[idx].resp_time, resp_time); if (gpfs_stats.op_stats[idx].resp_time_max < resp_time) gpfs_stats.op_stats[idx].resp_time_max = resp_time; if (gpfs_stats.op_stats[idx].resp_time_min == 0 || gpfs_stats.op_stats[idx].resp_time_min > resp_time) gpfs_stats.op_stats[idx].resp_time_min = resp_time; } else { rc = ioctl(gpfs_fd, kGanesha, &args); } return rc; } nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/handle.c000066400000000000000000001017541324272410200201750ustar00rootroot00000000000000/** * @file handle.c * @brief GPFS object (file|dir) handle object * * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ #include "config.h" #include /* used for 'dirname' */ #include #include #include #include #include #include #include "fsal.h" #include "fsal_internal.h" #include "fsal_convert.h" #include "FSAL/fsal_commonlib.h" #include "gpfs_methods.h" /* alloc_handle * allocate and fill in a handle * this uses malloc/free for the time being. */ #define ATTR_GPFS_ALLOC_HANDLE (ATTR_TYPE | ATTR_FILEID | ATTR_FSID) struct gpfs_fsal_obj_handle *alloc_handle(struct gpfs_file_handle *fh, struct fsal_filesystem *fs, struct attrlist *attributes, const char *link_content, struct fsal_export *exp_hdl) { struct gpfs_fsal_export *myself = container_of(exp_hdl, struct gpfs_fsal_export, export); struct gpfs_fsal_obj_handle *hdl = gsh_calloc(1, sizeof(struct gpfs_fsal_obj_handle) + sizeof(struct gpfs_file_handle)); hdl->handle = (struct gpfs_file_handle *)&hdl[1]; hdl->obj_handle.fs = fs; memcpy(hdl->handle, fh, sizeof(struct gpfs_file_handle)); hdl->obj_handle.type = attributes->type; if (hdl->obj_handle.type == REGULAR_FILE) { hdl->u.file.fd.fd = -1; /* no open on this yet */ hdl->u.file.fd.openflags = FSAL_O_CLOSED; } else if (hdl->obj_handle.type == SYMBOLIC_LINK && link_content != NULL) { size_t len = strlen(link_content) + 1; hdl->u.symlink.link_content = gsh_malloc(len); memcpy(hdl->u.symlink.link_content, link_content, len); hdl->u.symlink.link_size = len; } fsal_obj_handle_init(&hdl->obj_handle, exp_hdl, attributes->type); hdl->obj_handle.fsid = attributes->fsid; hdl->obj_handle.fileid = attributes->fileid; gpfs_handle_ops_init(&hdl->obj_handle.obj_ops); if (myself->pnfs_mds_enabled) handle_ops_pnfs(&hdl->obj_handle.obj_ops); return hdl; } /* lookup * deprecated NULL parent && NULL path implies root handle */ static fsal_status_t lookup(struct fsal_obj_handle *parent, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; int retval = 0; fsal_status_t status; struct gpfs_fsal_obj_handle *hdl; struct attrlist attrib; struct gpfs_file_handle *fh = alloca(sizeof(struct gpfs_file_handle)); struct fsal_filesystem *fs; *handle = NULL; /* poison it first */ fs = parent->fs; if (!path) return fsalstat(ERR_FSAL_FAULT, 0); memset(fh, 0, sizeof(struct gpfs_file_handle)); fh->handle_size = GPFS_MAX_FH_SIZE; if (!parent->obj_ops.handle_is(parent, DIRECTORY)) { LogCrit(COMPONENT_FSAL, "Parent handle is not a directory. hdl = 0x%p", parent); return fsalstat(ERR_FSAL_NOTDIR, 0); } if (parent->fsal != parent->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", parent->fsal->name, parent->fs->fsal->name); retval = EXDEV; goto hdlerr; } fsal_prepare_attrs(&attrib, ATTR_GPFS_ALLOC_HANDLE); if (attrs_out != NULL) attrib.request_mask |= attrs_out->request_mask; status = GPFSFSAL_lookup(op_ctx, parent, path, &attrib, fh, &fs); if (FSAL_IS_ERROR(status)) return status; /* allocate an obj_handle and fill it up */ hdl = alloc_handle(fh, fs, &attrib, NULL, op_ctx->fsal_export); if (attrs_out != NULL) { /* Copy the attributes to caller, passing ACL ref. */ fsal_copy_attrs(attrs_out, &attrib, true); } else { /* Done with the attrs */ fsal_release_attrs(&attrib); } *handle = &hdl->obj_handle; return fsalstat(ERR_FSAL_NO_ERROR, 0); hdlerr: fsal_error = posix2fsal_error(retval); return fsalstat(fsal_error, retval); } static fsal_status_t makedir(struct fsal_obj_handle *dir_hdl, const char *name, struct attrlist *attr_in, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { struct gpfs_fsal_obj_handle *hdl; fsal_status_t status; struct gpfs_file_handle *fh = alloca(sizeof(struct gpfs_file_handle)); /* Use a separate attrlist to getch the actual attributes into */ struct attrlist attrib; *handle = NULL; /* poison it */ if (!dir_hdl->obj_ops.handle_is(dir_hdl, DIRECTORY)) { LogCrit(COMPONENT_FSAL, "Parent handle is not a directory. hdl = 0x%p", dir_hdl); return fsalstat(ERR_FSAL_NOTDIR, 0); } memset(fh, 0, sizeof(struct gpfs_file_handle)); fh->handle_size = GPFS_MAX_FH_SIZE; fsal_prepare_attrs(&attrib, ATTR_GPFS_ALLOC_HANDLE); if (attrs_out != NULL) attrib.request_mask |= attrs_out->request_mask; status = GPFSFSAL_mkdir(dir_hdl, name, op_ctx, attr_in->mode, fh, &attrib); if (FSAL_IS_ERROR(status)) return status; /* allocate an obj_handle and fill it up */ hdl = alloc_handle(fh, dir_hdl->fs, &attrib, NULL, op_ctx->fsal_export); if (attrs_out != NULL) { /* Copy the attributes to caller, passing ACL ref. */ fsal_copy_attrs(attrs_out, &attrib, true); } else { /* Done with the attrs */ fsal_release_attrs(&attrib); } *handle = &hdl->obj_handle; /* We handled the mode above. */ FSAL_UNSET_MASK(attr_in->valid_mask, ATTR_MODE); if (attr_in->valid_mask) { /* Now per support_ex API, if there are any other attributes * set, go ahead and get them set now. */ status = (*handle)->obj_ops.setattr2(*handle, false, NULL, attr_in); if (FSAL_IS_ERROR(status)) { /* Release the handle we just allocated. */ LogFullDebug(COMPONENT_FSAL, "setattr2 status=%s", fsal_err_txt(status)); (*handle)->obj_ops.release(*handle); *handle = NULL; } } else { status = fsalstat(ERR_FSAL_NO_ERROR, 0); } FSAL_SET_MASK(attr_in->valid_mask, ATTR_MODE); return status; } static fsal_status_t makenode(struct fsal_obj_handle *dir_hdl, const char *name, object_file_type_t nodetype, struct attrlist *attr_in, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { fsal_status_t status; struct gpfs_fsal_obj_handle *hdl; struct gpfs_file_handle *fh = alloca(sizeof(struct gpfs_file_handle)); /* Use a separate attrlist to getch the actual attributes into */ struct attrlist attrib; *handle = NULL; /* poison it */ if (!dir_hdl->obj_ops.handle_is(dir_hdl, DIRECTORY)) { LogCrit(COMPONENT_FSAL, "Parent handle is not a directory. hdl = 0x%p", dir_hdl); return fsalstat(ERR_FSAL_NOTDIR, 0); } memset(fh, 0, sizeof(struct gpfs_file_handle)); fh->handle_size = GPFS_MAX_FH_SIZE; fsal_prepare_attrs(&attrib, ATTR_GPFS_ALLOC_HANDLE); if (attrs_out != NULL) attrib.request_mask |= attrs_out->request_mask; status = GPFSFSAL_mknode(dir_hdl, name, op_ctx, attr_in->mode, nodetype, &attr_in->rawdev, fh, &attrib); if (FSAL_IS_ERROR(status)) return status; /* allocate an obj_handle and fill it up */ hdl = alloc_handle(fh, dir_hdl->fs, &attrib, NULL, op_ctx->fsal_export); if (attrs_out != NULL) { /* Copy the attributes to caller, passing ACL ref. */ fsal_copy_attrs(attrs_out, &attrib, true); } else { /* Done with the attrs */ fsal_release_attrs(&attrib); } *handle = &hdl->obj_handle; /* We handled the mode above. */ FSAL_UNSET_MASK(attr_in->valid_mask, ATTR_MODE); if (attr_in->valid_mask) { /* Now per support_ex API, if there are any other attributes * set, go ahead and get them set now. */ status = (*handle)->obj_ops.setattr2(*handle, false, NULL, attr_in); if (FSAL_IS_ERROR(status)) { /* Release the handle we just allocated. */ LogFullDebug(COMPONENT_FSAL, "setattr2 status=%s", fsal_err_txt(status)); (*handle)->obj_ops.release(*handle); *handle = NULL; } } else { status = fsalstat(ERR_FSAL_NO_ERROR, 0); } FSAL_SET_MASK(attr_in->valid_mask, ATTR_MODE); return status; } /** makesymlink * Note that we do not set mode bits on symlinks for Linux/POSIX * They are not really settable in the kernel and are not checked * anyway (default is 0777) because open uses that target's mode */ static fsal_status_t makesymlink(struct fsal_obj_handle *dir_hdl, const char *name, const char *link_path, struct attrlist *attr_in, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { fsal_status_t status; struct gpfs_fsal_obj_handle *hdl; struct gpfs_file_handle *fh = alloca(sizeof(struct gpfs_file_handle)); /* Use a separate attrlist to getch the actual attributes into */ struct attrlist attrib; *handle = NULL; /* poison it first */ if (!dir_hdl->obj_ops.handle_is(dir_hdl, DIRECTORY)) { LogCrit(COMPONENT_FSAL, "Parent handle is not a directory. hdl = 0x%p", dir_hdl); return fsalstat(ERR_FSAL_NOTDIR, 0); } memset(fh, 0, sizeof(struct gpfs_file_handle)); fh->handle_size = GPFS_MAX_FH_SIZE; fsal_prepare_attrs(&attrib, ATTR_GPFS_ALLOC_HANDLE); if (attrs_out != NULL) attrib.request_mask |= attrs_out->request_mask; status = GPFSFSAL_symlink(dir_hdl, name, link_path, op_ctx, attr_in->mode, fh, &attrib); if (FSAL_IS_ERROR(status)) return status; /* allocate an obj_handle and fill it up */ hdl = alloc_handle(fh, dir_hdl->fs, &attrib, link_path, op_ctx->fsal_export); if (attrs_out != NULL) { /* Copy the attributes to caller, passing ACL ref. */ fsal_copy_attrs(attrs_out, &attrib, true); } else { /* Done with the attrs */ fsal_release_attrs(&attrib); } *handle = &hdl->obj_handle; /* We handled the mode above. */ FSAL_UNSET_MASK(attr_in->valid_mask, ATTR_MODE); if (attr_in->valid_mask) { /* Now per support_ex API, if there are any other attributes * set, go ahead and get them set now. */ status = (*handle)->obj_ops.setattr2(*handle, false, NULL, attr_in); if (FSAL_IS_ERROR(status)) { /* Release the handle we just allocated. */ LogFullDebug(COMPONENT_FSAL, "setattr2 status=%s", fsal_err_txt(status)); (*handle)->obj_ops.release(*handle); *handle = NULL; } } else { status = fsalstat(ERR_FSAL_NO_ERROR, 0); } FSAL_SET_MASK(attr_in->valid_mask, ATTR_MODE); return status; } static fsal_status_t readsymlink(struct fsal_obj_handle *obj_hdl, struct gsh_buffdesc *link_content, bool refresh) { struct gpfs_fsal_obj_handle *myself = NULL; fsal_status_t status; if (obj_hdl->type != SYMBOLIC_LINK) return fsalstat(ERR_FSAL_FAULT, 0); myself = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); if (refresh) { /* lazy load or LRU'd storage */ char link_buff[PATH_MAX]; if (myself->u.symlink.link_content != NULL) { gsh_free(myself->u.symlink.link_content); myself->u.symlink.link_content = NULL; myself->u.symlink.link_size = 0; } status = GPFSFSAL_readlink(obj_hdl, op_ctx, link_buff, sizeof(link_buff)); if (FSAL_IS_ERROR(status)) return status; myself->u.symlink.link_content = gsh_strdup(link_buff); myself->u.symlink.link_size = strlen(link_buff) + 1; } if (myself->u.symlink.link_content == NULL) return fsalstat(ERR_FSAL_FAULT, 0); link_content->len = myself->u.symlink.link_size; link_content->addr = gsh_strdup(myself->u.symlink.link_content); return fsalstat(ERR_FSAL_NO_ERROR, 0); } static fsal_status_t linkfile(struct fsal_obj_handle *obj_hdl, struct fsal_obj_handle *destdir_hdl, const char *name) { fsal_status_t status; struct gpfs_fsal_obj_handle *myself; myself = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); status = GPFSFSAL_link(destdir_hdl, myself->handle, name, op_ctx); return status; } #define BUF_SIZE 1024 /** * read_dirents * read the directory and call through the callback function for * each entry. * @param dir_hdl [IN] the directory to read * @param whence [IN] where to start (next) * @param dir_state [IN] pass thru of state to callback * @param cb [IN] callback function * @param eof [OUT] eof marker true == end of dir */ static fsal_status_t read_dirents(struct fsal_obj_handle *dir_hdl, fsal_cookie_t *whence, void *dir_state, fsal_readdir_cb cb, attrmask_t attrmask, bool *eof) { fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; int retval = 0; struct gpfs_fsal_obj_handle *myself; int dirfd; fsal_status_t status; off_t seekloc = 0; int bpos, cnt, nread; struct dirent64 *dentry; char buf[BUF_SIZE]; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; if (whence != NULL) seekloc = (off_t) *whence; myself = container_of(dir_hdl, struct gpfs_fsal_obj_handle, obj_handle); status = fsal_internal_handle2fd(export_fd, myself->handle, &dirfd, O_RDONLY | O_DIRECTORY); if (FSAL_IS_ERROR(status)) return status; seekloc = lseek(dirfd, seekloc, SEEK_SET); if (seekloc < 0) { retval = errno; fsal_error = posix2fsal_error(retval); goto done; } cnt = 0; do { nread = syscall(SYS_getdents64, dirfd, buf, BUF_SIZE); if (nread < 0) { retval = errno; fsal_error = posix2fsal_error(retval); goto done; } if (nread == 0) break; for (bpos = 0; bpos < nread;) { struct fsal_obj_handle *hdl; struct attrlist attrs; enum fsal_dir_result cb_rc; dentry = (struct dirent64 *)(buf + bpos); if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0) goto skip; /* must skip '.' and '..' */ fsal_prepare_attrs(&attrs, attrmask); status = lookup(dir_hdl, dentry->d_name, &hdl, &attrs); if (FSAL_IS_ERROR(status)) { fsal_error = status.major; goto done; } /* callback to cache inode */ cb_rc = cb(dentry->d_name, hdl, &attrs, dir_state, (fsal_cookie_t) dentry->d_off); fsal_release_attrs(&attrs); /* Read ahead not supported by this FSAL. */ if (cb_rc >= DIR_READAHEAD) goto done; skip: bpos += dentry->d_reclen; cnt++; } } while (nread > 0); *eof = true; done: close(dirfd); return fsalstat(fsal_error, retval); } static fsal_status_t renamefile(struct fsal_obj_handle *obj_hdl, struct fsal_obj_handle *olddir_hdl, const char *old_name, struct fsal_obj_handle *newdir_hdl, const char *new_name) { fsal_status_t status; status = GPFSFSAL_rename(olddir_hdl, old_name, newdir_hdl, new_name, op_ctx); return status; } /* FIXME: attributes are now merged into fsal_obj_handle. This * spreads everywhere these methods are used. eventually deprecate * everywhere except where we explicitly want to to refresh them. * NOTE: this is done under protection of the attributes rwlock in the * cache entry. */ static fsal_status_t getattrs(struct fsal_obj_handle *obj_hdl, struct attrlist *attrs) { struct gpfs_fsal_obj_handle *myself; myself = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); return GPFSFSAL_getattrs(op_ctx->fsal_export, obj_hdl->fs->private_data, op_ctx, myself->handle, attrs); } static fsal_status_t getxattrs(struct fsal_obj_handle *obj_hdl, xattrname4 *xa_name, xattrvalue4 *xa_value) { int rc; int errsv; struct getxattr_arg gxarg; struct gpfs_fsal_obj_handle *myself; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; myself = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); gxarg.mountdirfd = export_fd; gxarg.handle = myself->handle; gxarg.name_len = xa_name->utf8string_len; gxarg.name = xa_name->utf8string_val; gxarg.value_len = xa_value->utf8string_len; gxarg.value = xa_value->utf8string_val; rc = gpfs_ganesha(OPENHANDLE_GETXATTRS, &gxarg); if (rc < 0) { errsv = errno; LogDebug(COMPONENT_FSAL, "GETXATTRS returned rc %d errsv %d", rc, errsv); if (errsv == ERANGE) return fsalstat(ERR_FSAL_TOOSMALL, 0); if (errsv == ENODATA) return fsalstat(ERR_FSAL_NOENT, 0); return fsalstat(posix2fsal_error(errsv), errsv); } LogDebug(COMPONENT_FSAL, "GETXATTRS returned value %.*s len %d rc %d", gxarg.value_len, (char *)gxarg.value, gxarg.value_len, rc); xa_value->utf8string_len = gxarg.value_len; return fsalstat(ERR_FSAL_NO_ERROR, 0); } static fsal_status_t setxattrs(struct fsal_obj_handle *obj_hdl, setxattr_type4 sa_type, xattrname4 *xa_name, xattrvalue4 *xa_value) { int rc; int errsv; struct setxattr_arg sxarg; struct gpfs_fsal_obj_handle *myself; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; myself = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); sxarg.mountdirfd = export_fd; sxarg.handle = myself->handle; sxarg.name_len = xa_name->utf8string_len; sxarg.name = xa_name->utf8string_val; sxarg.value_len = xa_value->utf8string_len; sxarg.value = xa_value->utf8string_val; rc = gpfs_ganesha(OPENHANDLE_SETXATTRS, &sxarg); if (rc < 0) { errsv = errno; LogDebug(COMPONENT_FSAL, "SETXATTRS returned rc %d errsv %d", rc, errsv); return fsalstat(posix2fsal_error(errsv), errsv); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } static fsal_status_t removexattrs(struct fsal_obj_handle *obj_hdl, xattrname4 *xa_name) { int rc; int errsv; struct removexattr_arg rxarg; struct gpfs_fsal_obj_handle *myself; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; myself = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); rxarg.mountdirfd = export_fd; rxarg.handle = myself->handle; rxarg.name_len = xa_name->utf8string_len; rxarg.name = xa_name->utf8string_val; rc = gpfs_ganesha(OPENHANDLE_REMOVEXATTRS, &rxarg); if (rc < 0) { errsv = errno; LogDebug(COMPONENT_FSAL, "REMOVEXATTRS returned rc %d errsv %d", rc, errsv); return fsalstat(posix2fsal_error(errsv), errsv); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } static fsal_status_t listxattrs(struct fsal_obj_handle *obj_hdl, count4 la_maxcount, nfs_cookie4 *la_cookie, verifier4 *la_cookieverf, bool_t *lr_eof, xattrlist4 *lr_names) { int rc; int errsv; char *name, *next, *end, *val, *valstart; int entryCount = 0; char *buf = NULL; struct listxattr_arg lxarg; struct gpfs_fsal_obj_handle *myself; component4 *entry = lr_names->entries; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; val = (char *)entry + la_maxcount; valstart = val; myself = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); #define MAXCOUNT (1024*64) buf = gsh_malloc(MAXCOUNT); lxarg.mountdirfd = export_fd; lxarg.handle = myself->handle; lxarg.cookie = 0; /* For now gpfs doesn't support cookie */ lxarg.verifier = *((uint64_t *)la_cookieverf); lxarg.eof = false; lxarg.name_len = MAXCOUNT; lxarg.names = buf; LogFullDebug(COMPONENT_FSAL, "in cookie %llu len %d cookieverf %llx", (unsigned long long)lxarg.cookie, la_maxcount, (unsigned long long)lxarg.verifier); rc = gpfs_ganesha(OPENHANDLE_LISTXATTRS, &lxarg); if (rc < 0) { errsv = errno; LogDebug(COMPONENT_FSAL, "LISTXATTRS returned rc %d errsv %d", rc, errsv); gsh_free(buf); if (errsv == ERANGE) return fsalstat(ERR_FSAL_TOOSMALL, 0); return fsalstat(posix2fsal_error(errsv), errsv); } if (!lxarg.eof) { errsv = ERR_FSAL_SERVERFAULT; LogCrit(COMPONENT_FSAL, "Unable to get xattr."); return fsalstat(posix2fsal_error(errsv), errsv); } /* Only return names that the caller can read via getxattr */ name = buf; end = buf + rc; entry->utf8string_len = 0; entry->utf8string_val = NULL; while (name < end) { next = strchr(name, '\0'); next += 1; LogDebug(COMPONENT_FSAL, "nameP %s at offset %td", name, (next - name)); if (entryCount >= *la_cookie) { if ((((char *)entry - (char *)lr_names->entries) + sizeof(component4) > la_maxcount) || ((val - valstart)+(next - name) > la_maxcount)) { gsh_free(buf); *lr_eof = false; lr_names->entryCount = entryCount - *la_cookie; *la_cookie += entryCount; LogFullDebug(COMPONENT_FSAL, "out1 cookie %llu off %td eof %d cookieverf %llx", (unsigned long long)*la_cookie, (next - name), *lr_eof, (unsigned long long)* ((uint64_t *)la_cookieverf)); if (lr_names->entryCount == 0) return fsalstat(ERR_FSAL_TOOSMALL, 0); return fsalstat(ERR_FSAL_NO_ERROR, 0); } entry->utf8string_len = next - name; entry->utf8string_val = val; memcpy(entry->utf8string_val, name, entry->utf8string_len); LogFullDebug(COMPONENT_FSAL, "entry %d val %p at %p len %d at %p name %s", entryCount, val, entry, entry->utf8string_len, entry->utf8string_val, entry->utf8string_val); val += entry->utf8string_len; entry += 1; } /* Advance to next name in original buffer */ name = next; entryCount += 1; } lr_names->entryCount = entryCount - *la_cookie; *la_cookie = 0; *lr_eof = true; gsh_free(buf); LogFullDebug(COMPONENT_FSAL, "out2 cookie %llu eof %d cookieverf %llx", (unsigned long long)*la_cookie, *lr_eof, (unsigned long long)*((uint64_t *)la_cookieverf)); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* * NOTE: this is done under protection of the attributes rwlock in cache entry. */ fsal_status_t gpfs_setattr2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, struct attrlist *attrs) { fsal_status_t status; status = GPFSFSAL_setattrs(obj_hdl, op_ctx, attrs); return status; } /* file_unlink * unlink the named file in the directory */ static fsal_status_t file_unlink(struct fsal_obj_handle *dir_hdl, struct fsal_obj_handle *obj_hdl, const char *name) { fsal_status_t status; status = GPFSFSAL_unlink(dir_hdl, name, op_ctx); return status; } /* handle_to_wire * fill in the opaque f/s file handle part. * we zero the buffer to length first. This MAY already be done above * at which point, remove memset here because the caller is zeroing * the whole struct. */ static fsal_status_t handle_to_wire(const struct fsal_obj_handle *obj_hdl, fsal_digesttype_t output_type, struct gsh_buffdesc *fh_desc) { const struct gpfs_fsal_obj_handle *myself; struct gpfs_file_handle *fh; size_t fh_size; /* sanity checks */ if (!fh_desc) return fsalstat(ERR_FSAL_FAULT, 0); myself = container_of(obj_hdl, const struct gpfs_fsal_obj_handle, obj_handle); fh = myself->handle; switch (output_type) { case FSAL_DIGEST_NFSV3: case FSAL_DIGEST_NFSV4: fh_size = gpfs_sizeof_handle(fh); if (fh_desc->len < fh_size) goto errout; memcpy(fh_desc->addr, fh, fh_size); break; default: return fsalstat(ERR_FSAL_SERVERFAULT, 0); } fh_desc->len = fh_size; LogFullDebug(COMPONENT_FSAL, "FSAL fh_size %zu type %d", fh_size, output_type); return fsalstat(ERR_FSAL_NO_ERROR, 0); errout: LogMajor(COMPONENT_FSAL, "Space too small for handle. need %zu, have %zu", fh_size, fh_desc->len); return fsalstat(ERR_FSAL_TOOSMALL, 0); } /** * handle_to_key * return a handle descriptor into the handle in this object handle * @TODO reminder. make sure things like hash keys don't point here * after the handle is released. */ static void handle_to_key(struct fsal_obj_handle *obj_hdl, struct gsh_buffdesc *fh_desc) { struct gpfs_fsal_obj_handle *myself; myself = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); fh_desc->addr = myself->handle; fh_desc->len = myself->handle->handle_key_size; } /* * release * release our export first so they know we are gone */ static void release(struct fsal_obj_handle *obj_hdl) { struct gpfs_fsal_obj_handle *myself; const object_file_type_t type = obj_hdl->type; myself = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); LogFullDebug(COMPONENT_FSAL, "type %d", type); if (type == REGULAR_FILE) { PTHREAD_RWLOCK_wrlock(&obj_hdl->obj_lock); if (myself->u.file.fd.fd >= 0 && myself->u.file.fd.openflags != FSAL_O_CLOSED) { fsal_internal_close(myself->u.file.fd.fd, NULL, 0); myself->u.file.fd.fd = -1; myself->u.file.fd.openflags = FSAL_O_CLOSED; } PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); } fsal_obj_handle_fini(obj_hdl); if (type == SYMBOLIC_LINK) { gsh_free(myself->u.symlink.link_content); } gsh_free(myself); } /* gpfs_fs_locations */ static fsal_status_t gpfs_fs_locations(struct fsal_obj_handle *obj_hdl, struct fs_locations4 *fs_locs) { struct gpfs_fsal_obj_handle *myself; fsal_status_t status; myself = container_of(obj_hdl, struct gpfs_fsal_obj_handle, obj_handle); status = GPFSFSAL_fs_loc(op_ctx->fsal_export, obj_hdl->fs->private_data, op_ctx, myself->handle, fs_locs); return status; } /** * * @param ops Object operations */ void gpfs_handle_ops_init(struct fsal_obj_ops *ops) { ops->release = release; ops->lookup = lookup; ops->readdir = read_dirents; ops->mkdir = makedir; ops->mknode = makenode; ops->symlink = makesymlink; ops->readlink = readsymlink; ops->getattrs = getattrs; ops->link = linkfile; ops->rename = renamefile; ops->unlink = file_unlink; ops->fs_locations = gpfs_fs_locations; ops->seek = gpfs_seek; ops->io_advise = gpfs_io_advise; ops->close = gpfs_close; ops->handle_to_wire = handle_to_wire; ops->handle_to_key = handle_to_key; handle_ops_pnfs(ops); ops->getxattrs = getxattrs; ops->setxattrs = setxattrs; ops->removexattrs = removexattrs; ops->listxattrs = listxattrs; ops->open2 = gpfs_open2; ops->reopen2 = gpfs_reopen2; ops->read2 = gpfs_read2; ops->write2 = gpfs_write2; ops->commit2 = gpfs_commit2; ops->setattr2 = gpfs_setattr2; ops->close2 = gpfs_close2; ops->lock_op2 = gpfs_lock_op2; ops->merge = gpfs_merge; } /** * @param exp_hdl Handle * @param path Path * @param handle Reference to handle * * modelled on old api except we don't stuff attributes. * @return Status of operation */ fsal_status_t gpfs_lookup_path(struct fsal_export *exp_hdl, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { fsal_status_t fsal_status; int retval = 0; int dir_fd; struct fsal_filesystem *fs; struct gpfs_fsal_obj_handle *hdl; struct attrlist attributes; gpfsfsal_xstat_t buffxstat; struct gpfs_file_handle *fh = alloca(sizeof(struct gpfs_file_handle)); struct fsal_fsid__ fsid; struct gpfs_fsal_export *gpfs_export; gpfs_acl_t *acl_buf; unsigned int acl_buflen; bool use_acl; int retry; memset(fh, 0, sizeof(struct gpfs_file_handle)); fh->handle_size = GPFS_MAX_FH_SIZE; *handle = NULL; /* poison it */ dir_fd = open_dir_by_path_walk(-1, path, &buffxstat.buffstat); fsal_prepare_attrs(&attributes, ATTR_GPFS_ALLOC_HANDLE); if (attrs_out != NULL) attributes.request_mask |= attrs_out->request_mask; if (dir_fd < 0) { LogCrit(COMPONENT_FSAL, "Could not open directory for path %s", path); fsal_status = fsalstat(posix2fsal_error(-dir_fd), retval); goto errout; } fsal_status = fsal_internal_fd2handle(dir_fd, fh); if (FSAL_IS_ERROR(fsal_status)) goto fileerr; gpfs_export = container_of(exp_hdl, struct gpfs_fsal_export, export); /* Let us make the first request using the acl buffer that is * part of buffxstat itself. If that is not sufficient, we * allocate from heap and retry. */ use_acl = attributes.request_mask & ATTR_ACL; for (retry = 0; retry < GPFS_ACL_MAX_RETRY; retry++) { switch (retry) { case 0: /* first attempt */ acl_buf = (gpfs_acl_t *)buffxstat.buffacl; acl_buflen = GPFS_ACL_BUF_SIZE; break; case 1: /* first retry, don't free the old stack buffer */ acl_buflen = acl_buf->acl_len; acl_buf = gsh_malloc(acl_buflen); break; default: /* second or later retry, free the old heap buffer */ acl_buflen = acl_buf->acl_len; gsh_free(acl_buf); acl_buf = gsh_malloc(acl_buflen); break; } fsal_status = fsal_get_xstat_by_handle(dir_fd, fh, &buffxstat, acl_buf, acl_buflen, NULL, false, use_acl); if (FSAL_IS_ERROR(fsal_status) || !use_acl || acl_buflen >= acl_buf->acl_len) break; } if (retry == GPFS_ACL_MAX_RETRY) { /* make up an error */ LogCrit(COMPONENT_FSAL, "unable to get ACLs"); fsal_status = fsalstat(ERR_FSAL_SERVERFAULT, 0); } if (FSAL_IS_ERROR(fsal_status)) goto xstat_err; fsal_status = gpfsfsal_xstat_2_fsal_attributes(&buffxstat, &attributes, acl_buf, gpfs_export->use_acl); LogFullDebug(COMPONENT_FSAL, "fsid=0x%016"PRIx64".0x%016"PRIx64, attributes.fsid.major, attributes.fsid.minor); if (FSAL_IS_ERROR(fsal_status)) goto xstat_err; if (acl_buflen != GPFS_ACL_BUF_SIZE) { assert(acl_buf != (gpfs_acl_t *)buffxstat.buffacl); gsh_free(acl_buf); } close(dir_fd); gpfs_extract_fsid(fh, &fsid); fs = lookup_fsid(&fsid, GPFS_FSID_TYPE); if (fs == NULL) { LogInfo(COMPONENT_FSAL, "Could not find file system for path %s", path); fsal_status = fsalstat(posix2fsal_error(ENOENT), ENOENT); goto errout; } if (fs->fsal != exp_hdl->fsal) { LogInfo(COMPONENT_FSAL, "File system for path %s did not belong to FSAL %s", path, exp_hdl->fsal->name); fsal_status = fsalstat(posix2fsal_error(EACCES), EACCES); goto errout; } LogDebug(COMPONENT_FSAL, "filesystem %s for path %s", fs->path, path); /* allocate an obj_handle and fill it up */ hdl = alloc_handle(fh, fs, &attributes, NULL, exp_hdl); if (attrs_out != NULL) { /* Copy the attributes to caller, passing ACL ref. */ fsal_copy_attrs(attrs_out, &attributes, true); } else { /* Done with the attrs */ fsal_release_attrs(&attributes); } *handle = &hdl->obj_handle; return fsalstat(ERR_FSAL_NO_ERROR, 0); xstat_err: /* free if the acl buffer is from the heap */ if (acl_buflen != GPFS_ACL_BUF_SIZE) { assert(acl_buf != (gpfs_acl_t *)buffxstat.buffacl); gsh_free(acl_buf); } fileerr: close(dir_fd); errout: /* Done with attributes */ fsal_release_attrs(&attributes); return fsal_status; } /** * @brief create GPFS handle * * @param exp_hdl export handle * @param hdl_desc handle description * @param handle object handle * @return status * * Does what original FSAL_ExpandHandle did (sort of) * returns a ref counted handle to be later used in cache_inode etc. * NOTE! you must release this thing when done with it! * BEWARE! Thanks to some holes in the *AT syscalls implementation, * we cannot get an fd on an AF_UNIX socket, nor reliably on block or * character special devices. Sorry, it just doesn't... * we could if we had the handle of the dir it is in, but this method * is for getting handles off the wire for cache entries that have LRU'd. * Ideas and/or clever hacks are welcome... */ fsal_status_t gpfs_create_handle(struct fsal_export *exp_hdl, struct gsh_buffdesc *hdl_desc, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { fsal_status_t status; struct gpfs_fsal_obj_handle *hdl; struct gpfs_file_handle *fh; struct attrlist attrib; char link_buff[PATH_MAX]; struct fsal_fsid__ fsid; struct fsal_filesystem *fs; struct gpfs_filesystem *gpfs_fs; struct gpfs_fsal_export *exp = container_of(op_ctx->fsal_export, struct gpfs_fsal_export, export); int export_fd = exp->export_fd; *handle = NULL; /* poison it first */ if ((hdl_desc->len > (sizeof(struct gpfs_file_handle)))) return fsalstat(ERR_FSAL_FAULT, 0); fh = alloca(hdl_desc->len); memcpy(fh, hdl_desc->addr, hdl_desc->len); /* struct aligned copy */ gpfs_extract_fsid(fh, &fsid); fs = lookup_fsid(&fsid, GPFS_FSID_TYPE); if (fs == NULL) { LogInfo(COMPONENT_FSAL, "Could not find filesystem for fsid=0x%016"PRIx64 ".0x%016"PRIx64" from handle", fsid.major, fsid.minor); return fsalstat(ERR_FSAL_STALE, ESTALE); } if (fs->fsal != exp_hdl->fsal) { LogInfo(COMPONENT_FSAL, "Non GPFS filesystem fsid=0x%016"PRIx64 ".0x%016"PRIx64" from handle", fsid.major, fsid.minor); return fsalstat(ERR_FSAL_STALE, ESTALE); } gpfs_fs = fs->private_data; fsal_prepare_attrs(&attrib, ATTR_GPFS_ALLOC_HANDLE); if (attrs_out != NULL) attrib.request_mask |= attrs_out->request_mask; status = GPFSFSAL_getattrs(exp_hdl, gpfs_fs, op_ctx, fh, &attrib); if (FSAL_IS_ERROR(status)) return status; if (attrib.type == SYMBOLIC_LINK) { /* I could lazy eval this... */ status = fsal_readlink_by_handle(export_fd, fh, link_buff, sizeof(link_buff)); if (FSAL_IS_ERROR(status)) return status; } hdl = alloc_handle(fh, fs, &attrib, link_buff, exp_hdl); if (attrs_out != NULL) { /* Copy the attributes to caller, passing ACL ref. */ fsal_copy_attrs(attrs_out, &attrib, true); } else { /* Done with the attrs */ fsal_release_attrs(&attrib); } *handle = &hdl->obj_handle; return status; } nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/include/000077500000000000000000000000001324272410200202115ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/include/gpfs.h000066400000000000000000005060671324272410200213370ustar00rootroot00000000000000/* */ /* Copyright (C) 2001 International Business Machines */ /* All rights reserved. */ /* */ /* This file is part of the GPFS user library. */ /* */ /* Redistribution and use in source and binary forms, with or without */ /* modification, are permitted provided that the following conditions */ /* are met: */ /* */ /* 1. Redistributions of source code must retain the above copyright notice, */ /* this list of conditions and the following disclaimer. */ /* 2. Redistributions in binary form must reproduce the above copyright */ /* notice, this list of conditions and the following disclaimer in the */ /* documentation and/or other materials provided with the distribution. */ /* 3. The name of the author may not be used to endorse or promote products */ /* derived from this software without specific prior written */ /* permission. */ /* */ /* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR */ /* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES */ /* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. */ /* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ /* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, */ /* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; */ /* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, */ /* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR */ /* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF */ /* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* */ /* %Z%%M% %I% %W% %G% %U% */ /* * Library calls for GPFS interfaces */ #ifndef H_GPFS #define H_GPFS #include /* Define GPFS_64BIT_INODES to map the default interface definitions to 64-bit interfaces. Without this define, the 32-bit interface is the default. Both interfaces are always present, but the define sets the default. The actual mapping can be found near the end of this header. */ /* #define GPFS_64BIT_INODES 1 */ #define NFS_IP_SIZE 32 #ifdef __cplusplus extern "C" { #endif #if defined(WIN32) && defined(GPFSDLL) /* The following errno values either are missing from Windows errno.h or have a conflicting value. Other errno values (e.g. EPERM) are okay. */ #define GPFS_EALREADY 37 /* Operation already in progress */ #define GPFS_EOPNOTSUPP 45 /* Operation not supported */ #define GPFS_EDQUOT 69 /* Disk quota exceeded */ #define GPFS_ESTALE 9 /* No file system (mapped to EBADF) */ #define GPFS_EFORMAT 19 /* Unformatted media (mapped to ENODEV) */ /* specify the library calling convention */ #define GPFS_API __stdcall /* On Windows, this is a HANDLE as returned by CreateFile() */ typedef void* gpfs_file_t; #else /* not gpfs.dll on Windows */ #define GPFS_API /* On UNIX systems, this is a file descriptor as returned by open() */ typedef int gpfs_file_t; #endif typedef unsigned int gpfs_uid_t; typedef long long gpfs_off64_t; typedef unsigned long long gpfs_uid64_t; typedef struct gpfs_timestruc { unsigned int tv_sec; unsigned int tv_nsec; } gpfs_timestruc_t; typedef struct gpfs_timestruc64 { long long tv_sec; unsigned int tv_nsec; } gpfs_timestruc64_t; #define GPFS_SLITE_SIZE_BIT 0x00000001 #define GPFS_SLITE_BLKSIZE_BIT 0x00000002 #define GPFS_SLITE_BLOCKS_BIT 0x00000004 #define GPFS_SLITE_ATIME_BIT 0x00000010 #define GPFS_SLITE_MTIME_BIT 0x00000020 #define GPFS_SLITE_CTIME_BIT 0x00000040 #define GPFS_SLITE_EXACT_BITS 0x00000077 /* Returns "1" if the attribute is requested to be accurate. (On output, indicates the value returned in statbufP is accurate). */ #define GPFS_SLITE(m) (0 == (m)) #define GPFS_SLITE_SIZET(m) (0 != ((m) & GPFS_SLITE_SIZE_BIT)) #define GPFS_SLITE_BLKSIZE(m) (0 != ((m) & GPFS_SLITE_BLKSIZE_BIT)) #define GPFS_SLITE_BLOCKS(m) (0 != ((m) & GPFS_SLITE_BLOCKS_BIT)) #define GPFS_SLITE_ATIME(m) (0 != ((m) & GPFS_SLITE_ATIME_BIT)) #define GPFS_SLITE_MTIME(m) (0 != ((m) & GPFS_SLITE_MTIME_BIT)) #define GPFS_SLITE_CTIME(m) (0 != ((m) & GPFS_SLITE_CTIME_BIT)) #define GPFS_SLITE_EXACT(m) (GPFS_SLITE_EXACT_BITS == (m)) /* Sets the litemask bit indicating that the attribute should be accurate */ #define GPFS_S_SLITE(m) (m) = 0 #define GPFS_S_SLITE_SIZET(m) (m) |= GPFS_SLITE_SIZE_BIT #define GPFS_S_SLITE_BLKSIZE(m) (m) |= GPFS_SLITE_BLKSIZE_BIT #define GPFS_S_SLITE_BLOCKS(m) (m) |= GPFS_SLITE_BLOCKS_BIT #define GPFS_S_SLITE_ATIME(m) (m) |= GPFS_SLITE_ATIME_BIT #define GPFS_S_SLITE_MTIME(m) (m) |= GPFS_SLITE_MTIME_BIT #define GPFS_S_SLITE_CTIME(m) (m) |= GPFS_SLITE_CTIME_BIT #define GPFS_S_SLITE_EXACT(m) (m) |= GPFS_SLITE_EXACT_BITS #define GPFS_STATLITE 0 #define GPFS_NOFOLLOW 1 /* Mapping of buffer for gpfs_getacl, gpfs_putacl. */ typedef struct gpfs_opaque_acl { int acl_buffer_len; /* INPUT: Total size of buffer (including this field). OUTPUT: Actual size of the ACL information. */ unsigned short acl_version; /* INPUT: Set to zero. OUTPUT: Current version of the returned ACL. */ unsigned char acl_type; /* INPUT: Type of ACL: access (1) or default (2). */ char acl_var_data[1]; /* OUTPUT: Remainder of the ACL information. */ } gpfs_opaque_acl_t; /* ACL types (acl_type field in gpfs_opaque_acl_t or gpfs_acl_t) */ #define GPFS_ACL_TYPE_ACCESS 1 #define GPFS_ACL_TYPE_DEFAULT 2 #define GPFS_ACL_TYPE_NFS4 3 /* gpfs_getacl, gpfs_putacl flag indicating structures instead of the opaque style data normally used. */ #define GPFS_GETACL_STRUCT 0x00000020 #define GPFS_PUTACL_STRUCT 0x00000020 /* gpfs_getacl, gpfs_putacl flag indicating smbd is the caller */ #define GPFS_ACL_SAMBA 0x00000040 /* Defined values for gpfs_aclVersion_t */ #define GPFS_ACL_VERSION_POSIX 1 #define GPFS_ACL_VERSION_NFS4F 3 /* GPFS_ACL_VERSION_NFS4 plus V4FLAGS */ #define GPFS_ACL_VERSION_NFS4 4 /* Values for gpfs_aclLevel_t */ #define GPFS_ACL_LEVEL_BASE 0 /* compatible with all acl_version values */ #define GPFS_ACL_LEVEL_V4FLAGS 1 /* requires GPFS_ACL_VERSION_NFS4 */ /* Values for gpfs_aceType_t (ACL_VERSION_POSIX) */ #define GPFS_ACL_USER_OBJ 1 #define GPFS_ACL_GROUP_OBJ 2 #define GPFS_ACL_OTHER 3 #define GPFS_ACL_MASK 4 #define GPFS_ACL_USER 5 #define GPFS_ACL_GROUP 6 /* Values for gpfs_acePerm_t (ACL_VERSION_POSIX) */ #define ACL_PERM_EXECUTE 001 #define ACL_PERM_WRITE 002 #define ACL_PERM_READ 004 #define ACL_PERM_CONTROL 010 /* Values for gpfs_aceType_t (ACL_VERSION_NFS4) */ #define ACE4_TYPE_ALLOW 0 #define ACE4_TYPE_DENY 1 #define ACE4_TYPE_AUDIT 2 #define ACE4_TYPE_ALARM 3 /* Values for gpfs_aceFlags_t (ACL_VERSION_NFS4) */ #define ACE4_FLAG_FILE_INHERIT 0x00000001 #define ACE4_FLAG_DIR_INHERIT 0x00000002 #define ACE4_FLAG_NO_PROPAGATE 0x00000004 #define ACE4_FLAG_INHERIT_ONLY 0x00000008 #define ACE4_FLAG_SUCCESSFUL 0x00000010 #define ACE4_FLAG_FAILED 0x00000020 #define ACE4_FLAG_GROUP_ID 0x00000040 #define ACE4_FLAG_INHERITED 0x00000080 /* GPFS-defined flags. Placed in a separate ACL field to avoid ever running into newly defined NFSv4 flags. */ #define ACE4_IFLAG_SPECIAL_ID 0x80000000 /* Values for gpfs_aceMask_t (ACL_VERSION_NFS4) */ #define ACE4_MASK_READ 0x00000001 #define ACE4_MASK_LIST_DIR 0x00000001 #define ACE4_MASK_WRITE 0x00000002 #define ACE4_MASK_ADD_FILE 0x00000002 #define ACE4_MASK_APPEND 0x00000004 #define ACE4_MASK_ADD_SUBDIR 0x00000004 #define ACE4_MASK_READ_NAMED 0x00000008 #define ACE4_MASK_WRITE_NAMED 0x00000010 #define ACE4_MASK_EXECUTE 0x00000020 /* The rfc doesn't provide a mask equivalent to "search" ("x" on a * directory in posix), but it also doesn't say that its EXECUTE * is to have this dual use (even though it does so for other dual * use permissions such as read/list. Going to make the assumption * here that the EXECUTE bit has this dual meaning... otherwise * we're left with no control over search. */ #define ACE4_MASK_SEARCH 0x00000020 #define ACE4_MASK_DELETE_CHILD 0x00000040 #define ACE4_MASK_READ_ATTR 0x00000080 #define ACE4_MASK_WRITE_ATTR 0x00000100 #define ACE4_MASK_DELETE 0x00010000 #define ACE4_MASK_READ_ACL 0x00020000 #define ACE4_MASK_WRITE_ACL 0x00040000 #define ACE4_MASK_WRITE_OWNER 0x00080000 #define ACE4_MASK_SYNCHRONIZE 0x00100000 #define ACE4_MASK_ALL 0x001f01ff /* Values for gpfs_uid_t (ACL_VERSION_NFS4) */ #define ACE4_SPECIAL_OWNER 1 #define ACE4_SPECIAL_GROUP 2 #define ACE4_SPECIAL_EVERYONE 3 /* per-ACL flags imported from a Windows security descriptor object */ #define ACL4_FLAG_OWNER_DEFAULTED 0x00000100 #define ACL4_FLAG_GROUP_DEFAULTED 0x00000200 #define ACL4_FLAG_DACL_PRESENT 0x00000400 #define ACL4_FLAG_DACL_DEFAULTED 0x00000800 #define ACL4_FLAG_SACL_PRESENT 0x00001000 #define ACL4_FLAG_SACL_DEFAULTED 0x00002000 #define ACL4_FLAG_DACL_UNTRUSTED 0x00004000 #define ACL4_FLAG_SERVER_SECURITY 0x00008000 #define ACL4_FLAG_DACL_AUTO_INHERIT_REQ 0x00010000 #define ACL4_FLAG_SACL_AUTO_INHERIT_REQ 0x00020000 #define ACL4_FLAG_DACL_AUTO_INHERITED 0x00040000 #define ACL4_FLAG_SACL_AUTO_INHERITED 0x00080000 #define ACL4_FLAG_DACL_PROTECTED 0x00100000 #define ACL4_FLAG_SACL_PROTECTED 0x00200000 #define ACL4_FLAG_RM_CONTROL_VALID 0x00400000 #define ACL4_FLAG_NULL_DACL 0x00800000 #define ACL4_FLAG_NULL_SACL 0x01000000 #define ACL4_FLAG_VALID_FLAGS 0x01ffff00 /* Externalized ACL defintions */ typedef unsigned int gpfs_aclType_t; typedef unsigned int gpfs_aclLen_t; typedef unsigned int gpfs_aclLevel_t; typedef unsigned int gpfs_aclVersion_t; typedef unsigned int gpfs_aclCount_t; typedef unsigned int gpfs_aclFlag_t; typedef unsigned int gpfs_aceType_t; typedef unsigned int gpfs_aceFlags_t; typedef unsigned int gpfs_acePerm_t; typedef unsigned int gpfs_aceMask_t; /* A POSIX ACL Entry */ typedef struct gpfs_ace_v1 { gpfs_aceType_t ace_type; /* POSIX ACE type */ gpfs_uid_t ace_who; /* uid/gid */ gpfs_acePerm_t ace_perm; /* POSIX permissions */ } gpfs_ace_v1_t; /* An NFSv4 ACL Entry */ typedef struct gpfs_ace_v4 { gpfs_aceType_t aceType; /* Allow or Deny */ gpfs_aceFlags_t aceFlags; /* Inherit specifications, etc. */ gpfs_aceFlags_t aceIFlags; /* GPFS Internal flags */ gpfs_aceMask_t aceMask; /* NFSv4 mask specification */ gpfs_uid_t aceWho; /* User/Group identification */ } gpfs_ace_v4_t; /* when GPFS_ACL_VERSION_NFS4, and GPFS_ACL_LEVEL_V4FLAGS */ typedef struct v4Level1_ext /* ACL extension */ { gpfs_aclFlag_t acl_flags; /* per-ACL flags */ gpfs_ace_v4_t ace_v4[1]; } v4Level1_t; /* The GPFS ACL */ typedef struct gpfs_acl { gpfs_aclLen_t acl_len; /* Total length of this ACL in bytes */ gpfs_aclLevel_t acl_level; /* Reserved (must be zero) */ gpfs_aclVersion_t acl_version; /* POSIX or NFS4 ACL */ gpfs_aclType_t acl_type; /* Access, Default, or NFS4 */ gpfs_aclCount_t acl_nace; /* Number of Entries that follow */ union { gpfs_ace_v1_t ace_v1[1]; /* when GPFS_ACL_VERSION_POSIX */ gpfs_ace_v4_t ace_v4[1]; /* when GPFS_ACL_VERSION_NFS4 */ v4Level1_t v4Level1; /* when GPFS_ACL_LEVEL_V4FLAGS */ }; } gpfs_acl_t; /* NAME: gpfs_getacl() * * FUNCTION: Retrieves the ACL information for a file. * * The aclP parameter must point to a buffer mapped by either: * - gpfs_opaque_acl_t (when flags are zero). In this case, * the opaque data that is intended to be used by a backup * program (restoreed by passing this data back on a subsequent * call to gpfs_putacl). * - gpfs_acl_t (when GPFS_GETACL_STRUCT is specified). In this * case, the data can be interpreted by the calling application * (and may be modified and applied to the file by passing it * to gpfs_putacl...along with the GPFS_PUTACL_STRUCT flag). * * On input, the first four bytes of the buffer must contain its * total size. * * Returns: 0 Successful * -1 Failure * * Errno: ENOSYS function not available * ENOSPC buffer too small to return the entire ACL. * Needed size is returned in the first four * bytes of the buffer pointed to by aclP. * EINVAL Invalid arguments * ENOTDIR Not on directory * ENOMEM Out of memory */ int GPFS_API gpfs_getacl(const char *pathname, int flags, void *acl); /* NAME: gpfs_putacl() * * FUNCTION: Sets the ACL information for a file. * The buffer passed in should contain the ACL data * that was obtained by a previous call to gpfs_getacl. * * Returns: 0 Successful * -1 Failure * * Errno: ENOSYS function not available * EINVAL Invalid arguments * ENOTDIR Not on directory * ENOMEM Out of memory * EPERM Caller does not hold appropriate privilege */ int GPFS_API gpfs_putacl(const char *pathname, int flags, void *acl); /* NAME: gpfs_prealloc() * * FUNCTION: Preallocate disk storage for a file or directory, starting * at the specified startOffset and covering at least the number * of bytes requested by bytesToPrealloc. Allocations are rounded * to block boundaries (block size can be found in st_blksize * returned by fstat()), or possibly larger sizes. For files, the * file descriptor must be open for write, but any existing data * already present will not be modified. Reading the preallocated * blocks will return zeros. For directories, the file descriptor * may be open for read but the caller must have write permission, * and existing entries are unaffected; startOffset must be zero. * * This function implements the behavior of mmchattr when invoked * with --compact[=minimumEntries]. The minimumEntries value * specifies both the lower bound on automatic compaction and the * desired size for pre-allocation. It defaults to zero, meaning * no pre-allocation and compact the directory as much as * possible. The mapping between minimumEntries and the * bytesToPrealloc is given by GPFS_PREALLOC_DIR_SLOT_SIZE, see * below. * * Directory compaction (zero bytesToPrealloc) requires a file * system supporting V2 directories (format version 1400, v4.1). * Directories created before upgrading the file system to version * 4.1, are upgraded from V1 to V2 by this operation even if no * other change is made. Since v4.2.2, bytesToPrealloc may be * nonzero effecting pre-allocation by setting a minimum * compaction size. Prior to v4.2.2 the minimum size of any * directory is zero. * * Returns: 0 Success * -1 Failure * * Errno: ENOSYS No prealloc service available * EBADF Bad file descriptor * EINVAL Not a GPFS file * EINVAL Not a regular file or directory * EINVAL Directory pre-allocation not supported * EINVAL startOffset or bytesToPrealloc < 0 * EACCES File not opened for writing * EACCES Caller does not have write access to directory. * EDQUOT Quota exceeded * ENOSPC Not enough space on disk * EPERM File is in a snapshot */ int GPFS_API gpfs_prealloc(gpfs_file_t fileDesc, gpfs_off64_t startOffset, gpfs_off64_t bytesToPrealloc); /* Directory entries are nominally (assuming compact names of 19 bytes or less) 32 bytes in size. This conversion factor is used in mapping between a number of entries (for mmchattr) and a size when calling gpfs_prealloc. */ #define GPFS_PREALLOC_DIR_SLOT_SIZE 32 /* for size => bytes per entry */ typedef struct gpfs_winattr { gpfs_timestruc_t creationTime; unsigned int winAttrs; /* values as defined below */ } gpfs_winattr_t; /* winAttrs values */ #define GPFS_WINATTR_ARCHIVE 0x0001 #define GPFS_WINATTR_COMPRESSED 0x0002 #define GPFS_WINATTR_DEVICE 0x0004 #define GPFS_WINATTR_DIRECTORY 0x0008 #define GPFS_WINATTR_ENCRYPTED 0x0010 #define GPFS_WINATTR_HIDDEN 0x0020 #define GPFS_WINATTR_NORMAL 0x0040 #define GPFS_WINATTR_NOT_CONTENT_INDEXED 0x0080 #define GPFS_WINATTR_OFFLINE 0x0100 #define GPFS_WINATTR_READONLY 0x0200 #define GPFS_WINATTR_REPARSE_POINT 0x0400 #define GPFS_WINATTR_SPARSE_FILE 0x0800 #define GPFS_WINATTR_SYSTEM 0x1000 #define GPFS_WINATTR_TEMPORARY 0x2000 #define GPFS_WINATTR_HAS_STREAMS 0x4000 /* NAME: gpfs_get_winattrs() * gpfs_get_winattrs_path() * * FUNCTION: Returns gpfs_winattr_t attributes * * Returns: 0 Success * -1 Failure * * Errno: ENOENT file not found * EBADF Bad file handle, not a GPFS file * ENOMEM Memory allocation failed * EACCESS Permission denied * EFAULT Bad address provided * EINVAL Not a regular file * ENOSYS function not available */ int GPFS_API gpfs_get_winattrs(gpfs_file_t fileDesc, gpfs_winattr_t *attrP); int GPFS_API gpfs_get_winattrs_path(const char *pathname, gpfs_winattr_t *attrP); /* NAME: gpfs_set_winattrs() * gpfs_set_winattrs_path() * * FUNCTION: Sets gpfs_winattr_t attributes (as specified by * the flags). * * Returns: 0 Success * -1 Failure * * Errno: ENOENT file not found * EBADF Bad file handle, not a GPFS file * ENOMEM Memory allocation failed * EACCESS Permission denied * EFAULT Bad address provided * EINVAL Not a regular file * ENOSYS function not available */ int GPFS_API gpfs_set_winattrs(gpfs_file_t fileDesc, int flags, gpfs_winattr_t *attrP); int GPFS_API gpfs_set_winattrs_path(const char *pathname, int flags, gpfs_winattr_t *attrP); /* gpfs_set_winattr flag values */ #define GPFS_WINATTR_SET_CREATION_TIME 0x08 #define GPFS_WINATTR_SET_ATTRS 0x10 /* * NAME: gpfs_set_times(), gpfs_set_times_path() * * FUNCTION: Sets file access time, modified time, change time, * and/or creation time (as specified by the flags). * * Input: flagsfileDesc : file descriptor of the object to set * pathname : path to a file or directory * flag : define time value to set * GPFS_SET_ATIME - set access time * GPFS_SET_MTIME - set mod. time * GPFS_SET_CTIME - set change time * GPFS_SET_CREATION_TIME - set creation time * GPFS_SET_TIME_NO_FOLLOW - don't follow links * times : array to times * * Returns: 0 Successful * -1 Failure * * Errno: ENOSYS function not available * EBADF Not a GPFS File * EINVAL invalid argument * EACCES Permission denied * EROFS Filesystem is read only * ENOENT No such file or directory */ typedef gpfs_timestruc_t gpfs_times_vector_t[4]; int GPFS_API gpfs_set_times(gpfs_file_t fileDesc, int flags, gpfs_times_vector_t times); int GPFS_API gpfs_set_times_path(char *pathname, int flags, gpfs_times_vector_t times); /* gpfs_set_times flag values */ #define GPFS_SET_ATIME 0x01 #define GPFS_SET_MTIME 0x02 #define GPFS_SET_CTIME 0x04 #define GPFS_SET_CREATION_TIME 0x08 #define GPFS_SET_TIME_NO_FOLLOW 0x10 /* NAME: gpfs_set_share() * * FUNCTION: Acquire shares * * Input: fileDesc : file descriptor * allow : share type being requested * GPFS_SHARE_NONE, GPFS_SHARE_READ, * GPFS_SHARE_WRITE, GPFS_SHARE_BOTH * deny : share type to deny to others * GPFS_DENY_NONE, GPFS_DENY_READ, * GPFS_DENY_WRITE, GPFS_DENY_BOTH * * Returns: 0 Success * -1 Failure * * Errno: EBADF Bad file handle * EINVAL Bad argument given * EFAULT Bad address provided * ENOMEM Memory allocation failed * EACCES share mode not available * ENOSYS function not available */ /* allow/deny specifications */ #define GPFS_SHARE_NONE 0 #define GPFS_SHARE_READ 1 #define GPFS_SHARE_WRITE 2 #define GPFS_SHARE_BOTH 3 #define GPFS_SHARE_ALL 3 #define GPFS_DENY_NONE 0 #define GPFS_DENY_READ 1 #define GPFS_DENY_WRITE 2 #define GPFS_DENY_BOTH 3 #define GPFS_DENY_DELETE 4 #define GPFS_DENY_ALL 7 int GPFS_API gpfs_set_share(gpfs_file_t fileDesc, unsigned int share, unsigned int deny); /* NAME: gpfs_set_lease() * * FUNCTION: Acquire leases for Samba * * Input: fileDesc : file descriptor * leaseType : lease type being requested * GPFS_LEASE_NONE GPFS_LEASE_READ, * GPFS_LEASE_WRITE * * Returns: 0 Success * -1 Failure * * Errno: EBADF Bad file handle * EINVAL Bad argument given * EFAULT Bad address provided * ENOMEM Memory allocation failed * EAGAIN lease not available * EACCES permission denied * EOPNOTSUPP unsupported leaseType * ESTALE unmounted file system * ENOSYS function not available */ /* leaseType specifications */ #define GPFS_LEASE_NONE 0 #define GPFS_LEASE_READ 1 #define GPFS_LEASE_WRITE 2 int GPFS_API gpfs_set_lease(gpfs_file_t fileDesc, unsigned int leaseType); /* NAME: gpfs_get_lease() * * FUNCTION: Returns the type of lease currently held * * Returns: GPFS_LEASE_READ * GPFS_LEASE_WRITE * GPFS_LEASE_NONE * * Returns: >= 0 Success * -1 Failure * * Errno: EINVAL */ int GPFS_API gpfs_get_lease(gpfs_file_t fileDesc); /* NAME: gpfs_get_realfilename(), gpfs_get_realfilename_path() * * FUNCTION: Interface to get real name of a file. * * INPUT: File descriptor, pathname, buffer, bufferlength * OUTPUT: Real file name stored in file system * * Returns: 0 Success * -1 Failure * * Errno: EBADF Bad file handle * EINVAL Not a regular file * EFAULT Bad address provided * ENOSPC buffer too small to return the real file name. * Needed size is returned in buflen parameter. * ENOENT File does not exist * ENOMEM Memory allocation failed * EACCESS Permission denied * ENOSYS function not available */ int GPFS_API gpfs_get_realfilename(gpfs_file_t fileDesc, char *fileNameP, int *buflen); int GPFS_API gpfs_get_realfilename_path(const char *pathname, char *fileNameP, int *buflen); /* NAME: gpfs_ftruncate() * * FUNCTION: Interface to truncate a file. * * INPUT: File descriptor * length * Returns: 0 Successful * -1 Failure * * Errno: ENOSYS function not available * EBADF Bad file handle * EBADF Not a GPFS file * EINVAL Not a regular file * ENOENT File does not exist * ENOMEM Memory allocation failed * EINVAL length < 0 * EACCESS Permission denied */ int GPFS_API gpfs_ftruncate(gpfs_file_t fileDesc, gpfs_off64_t length); #define GPFS_WIN_CIFS_REGISTERED 0x02000000 typedef struct cifsThreadData_t { unsigned int dataLength; /* Total buffer length */ unsigned int share; /* gpfs_set_share declaration */ unsigned int deny; /* gpfs_set_share specification */ unsigned int lease; /* gpfs_set_lease lease type */ unsigned int secInfoFlags; /* Future use. Must be zero */ gpfs_uid_t sdUID; /* Owning user */ gpfs_uid_t sdGID; /* Owning group */ int shareLocked_fd; /* file descriptor with share locks */ unsigned int aclLength ; /* Length of the following ACL */ gpfs_acl_t acl; /* The initial ACL for create/mkdir */ } cifsThreadData_t; /* NAME: gpfs_register_cifs_export() * * FUNCTION: Register a CIFS export process. * * INPUT: implicit use of the process ids * * Returns: 0 Successful * ENOSYS function not available * EACCES cannot establish credentials * ENOMEM temporary shortage of memory * EINVAL prior process/thread registrations exist * EBADF unable to allocate a file descriptor */ int GPFS_API gpfs_register_cifs_export(void); /* NAME: gpfs_unregister_cifs_export() * * FUNCTION: remove a registration for a CIFS export * * INPUT: implicit use of the process ids * * Returns: 0 Successful * ENOSYS function not available * EACCES cannot establish credentials * ENOMEM temporary shortage of memory */ int GPFS_API gpfs_unregister_cifs_export(void); /* NAME: gpfs_register_cifs_buffer() * * FUNCTION: Register a CIFS thread/buffer combination * * INPUT: implicit use of the process and thread ids * Address of a cifsThreadData_t structure that will include * a GPFS ACL (GPFS_ACL_VERSION_NFS4/GPFS_ACL_LEVEL_V4FLAGS) * that can be applied at file/dir creation. * * Returns: 0 Successful * ENOSYS function not available * EACCES cannot establish credentials * ENOMEM unable to allocate required memory * EINVAL no associated process registrion exists * bad dataLength in buffer. */ int GPFS_API gpfs_register_cifs_buffer(cifsThreadData_t *bufP); /* NAME: gpfs_unregister_cifs_buffer() * * FUNCTION: remove a CIFS thread/buffer registration * * INPUT: implicit use of the process and thread ids * * Returns: 0 Successful * ENOSYS function not available * EACCES cannot establish credentials * ENOMEM unable to allocate required memory * EINVAL no associated process registrion exists */ int GPFS_API gpfs_unregister_cifs_buffer(void); /* NAME: gpfs_lib_init() * * FUNCTION: Open GPFS main module device file * * INPUT: Flags * Returns: 0 Successful * -1 Failure * * Errno: ENOSYS Function not available */ int GPFS_API gpfs_lib_init(int flags); /* NAME: gpfs_lib_term() * * FUNCTION: Close GPFS main module device file * * INPUT: Flags * Returns: 0 Successful * -1 Failure * * Errno: ENOSYS Function not available */ int GPFS_API gpfs_lib_term(int flags); /* Define maximum length of the name for a GPFS named object, such as a snapshot, storage pool or fileset. The name is a null-terminated character string, which is not include in the max length */ #define GPFS_MAXNAMLEN 255 /* Define maximum length of the path to a GPFS named object such as a snapshot or fileset. If the absolute path name exceeds this limit, then use a relative path name. The path is a null-terminated character string, which is not included in the max length */ #define GPFS_MAXPATHLEN 1023 /* ASCII code for "GPFS" in the struct statfs f_type field */ #define GPFS_SUPER_MAGIC 0x47504653 /* GPFS inode attributes gpfs_uid_t - defined above gpfs_uid64_t - defined above gpfs_off64_t - defined above gpfs_mode_t may include gpfs specific values including 0x02000000 To have a gpfs_mode_t be equivalent to a mode_t mask that value out. */ typedef unsigned int gpfs_mode_t; typedef unsigned int gpfs_gid_t; typedef unsigned long long gpfs_gid64_t; typedef unsigned int gpfs_ino_t; typedef unsigned long long gpfs_ino64_t; typedef unsigned int gpfs_gen_t; typedef unsigned long long gpfs_gen64_t; typedef unsigned int gpfs_dev_t; typedef unsigned int gpfs_mask_t; typedef unsigned int gpfs_pool_t; typedef unsigned int gpfs_snapid_t; typedef unsigned long long gpfs_snapid64_t; typedef unsigned long long gpfs_fsid64_t[2]; typedef short gpfs_nlink_t; typedef long long gpfs_nlink64_t; #if defined(WIN32) || defined(_MS_SUA_) typedef struct gpfs_stat64 { gpfs_dev_t st_dev; /* id of device containing file */ gpfs_ino64_t st_ino; /* file inode number */ gpfs_mode_t st_mode; /* access mode */ gpfs_nlink64_t st_nlink; /* number of links */ unsigned int st_flags; /* flag word */ gpfs_uid64_t st_uid; /* owner uid */ gpfs_gid64_t st_gid; /* owner gid */ gpfs_dev_t st_rdev; /* device id (if special file) */ gpfs_off64_t st_size; /* file size in bytes */ gpfs_timestruc64_t st_atime; /* time of last access */ gpfs_timestruc64_t st_mtime; /* time of last data modification */ gpfs_timestruc64_t st_ctime; /* time of last status change */ int st_blksize; /* preferred block size for io */ gpfs_off64_t st_blocks; /* 512 byte blocks of disk held by file */ long long st_fsid; /* file system id */ unsigned int st_type; /* file type */ gpfs_gen64_t st_gen; /* inode generation number */ gpfs_timestruc64_t st_createtime; /* time of creation */ unsigned int st_attrs; /* Windows flags */ } gpfs_stat64_t; #else typedef struct stat64 gpfs_stat64_t; #endif #if defined(WIN32) || defined(_MS_SUA_) typedef struct gpfs_statfs64 { gpfs_off64_t f_blocks; /* total data blocks in file system */ gpfs_off64_t f_bfree; /* free block in fs */ gpfs_off64_t f_bavail; /* free blocks avail to non-superuser */ int f_bsize; /* optimal file system block size */ gpfs_ino64_t f_files; /* total file nodes in file system */ gpfs_ino64_t f_ffree; /* free file nodes in fs */ gpfs_fsid64_t f_fsid; /* file system id */ int f_fsize; /* fundamental file system block size */ int f_sector_size; /* logical disk sector size */ char f_fname[32]; /* file system name (usually mount pt.) */ char f_fpack[32]; /* file system pack name */ int f_name_max; /* maximum component name length for posix */ } gpfs_statfs64_t; #else typedef struct statfs64 gpfs_statfs64_t; #endif /* Declarations for backwards compatibility. */ typedef gpfs_stat64_t stat64_t; typedef gpfs_statfs64_t statfs64_t; /* Define a version number for the directory entry data to allow future changes in this structure. Careful callers should also use the d_reclen field for the size of the structure rather than sizeof, to allow some degree of forward compatibility */ #define GPFS_D_VERSION 1 typedef struct gpfs_direntx { int d_version; /* this struct's version */ unsigned short d_reclen; /* actual size of this struct including null terminated variable length d_name */ unsigned short d_type; /* Types are defined below */ gpfs_ino_t d_ino; /* File inode number */ gpfs_gen_t d_gen; /* Generation number for the inode */ char d_name[256]; /* null terminated variable length name */ } gpfs_direntx_t; #define GPFS_D64_VERSION 2 typedef struct gpfs_direntx64 { int d_version; /* this struct's version */ unsigned short d_reclen; /* actual size of this struct including null terminated variable length d_name */ unsigned short d_type; /* Types are defined below */ gpfs_ino64_t d_ino; /* File inode number */ gpfs_gen64_t d_gen; /* Generation number for the inode */ unsigned int d_flags; /* Flags are defined below */ char d_name[1028]; /* null terminated variable length name */ /* (1020+null+7 byte pad to double word) */ /* to handle up to 255 UTF-8 chars */ } gpfs_direntx64_t; /* File types for d_type field in gpfs_direntx_t */ #define GPFS_DE_OTHER 0 #define GPFS_DE_FIFO 1 #define GPFS_DE_CHR 2 #define GPFS_DE_DIR 4 #define GPFS_DE_BLK 6 #define GPFS_DE_REG 8 #define GPFS_DE_LNK 10 #define GPFS_DE_SOCK 12 #define GPFS_DE_DEL 16 /* Define flags for gpfs_direntx64_t */ #define GPFS_DEFLAG_NONE 0x0000 /* Default value, no flags set */ #define GPFS_DEFLAG_JUNCTION 0x0001 /* DirEnt is a fileset junction */ #define GPFS_DEFLAG_IJUNCTION 0x0002 /* DirEnt is a inode space junction */ #define GPFS_DEFLAG_ORPHAN 0x0004 /* DirEnt is an orphan (pcache) */ #define GPFS_DEFLAG_CLONE 0x0008 /* DirEnt is a clone child */ /* Define a version number for the iattr data to allow future changes in this structure. Careful callers should also use the ia_reclen field for the size of the structure rather than sizeof, to allow some degree of forward compatibility */ #define GPFS_IA_VERSION 1 #define GPFS_IA64_VERSION 3 /* ver 3 adds ia_repl_xxxx bytes instead of ia_pad2 */ #define GPFS_IA64_RESERVED 4 #define GPFS_IA64_UNUSED 8 typedef struct gpfs_iattr { int ia_version; /* this struct version */ int ia_reclen; /* sizeof this structure */ int ia_checksum; /* validity check on iattr struct */ gpfs_mode_t ia_mode; /* access mode; see gpfs_mode_t comment */ gpfs_uid_t ia_uid; /* owner uid */ gpfs_gid_t ia_gid; /* owner gid */ gpfs_ino_t ia_inode; /* file inode number */ gpfs_gen_t ia_gen; /* inode generation number */ gpfs_nlink_t ia_nlink; /* number of links */ short ia_flags; /* Flags (defined below) */ int ia_blocksize; /* preferred block size for io */ gpfs_mask_t ia_mask; /* Initial attribute mask (not used) */ unsigned int ia_pad1; /* reserved space */ gpfs_off64_t ia_size; /* file size in bytes */ gpfs_off64_t ia_blocks; /* 512 byte blocks of disk held by file */ gpfs_timestruc_t ia_atime; /* time of last access */ gpfs_timestruc_t ia_mtime; /* time of last data modification */ gpfs_timestruc_t ia_ctime; /* time of last status change */ gpfs_dev_t ia_rdev; /* id of device */ unsigned int ia_xperm; /* extended attributes (defined below) */ unsigned int ia_modsnapid; /* snapshot id of last modification */ unsigned int ia_filesetid; /* fileset ID */ unsigned int ia_datapoolid; /* storage pool ID for data */ unsigned int ia_pad2; /* reserved space */ } gpfs_iattr_t; typedef struct gpfs_iattr64 { int ia_version; /* this struct version */ int ia_reclen; /* sizeof this structure */ int ia_checksum; /* validity check on iattr struct */ gpfs_mode_t ia_mode; /* access mode; see gpfs_mode_t comment */ gpfs_uid64_t ia_uid; /* owner uid */ gpfs_gid64_t ia_gid; /* owner gid */ gpfs_ino64_t ia_inode; /* file inode number */ gpfs_gen64_t ia_gen; /* inode generation number */ gpfs_nlink64_t ia_nlink; /* number of links */ gpfs_off64_t ia_size; /* file size in bytes */ gpfs_off64_t ia_blocks; /* 512 byte blocks of disk held by file */ gpfs_timestruc64_t ia_atime; /* time of last access */ unsigned int ia_winflags; /* windows flags (defined below) */ unsigned int ia_pad1; /* reserved space */ gpfs_timestruc64_t ia_mtime; /* time of last data modification */ unsigned int ia_flags; /* flags (defined below) */ /* next four bytes were ia_pad2 */ unsigned char ia_repl_data; /* data replication factor */ unsigned char ia_repl_data_max; /* data replication max factor */ unsigned char ia_repl_meta; /* meta data replication factor */ unsigned char ia_repl_meta_max; /* meta data replication max factor */ gpfs_timestruc64_t ia_ctime; /* time of last status change */ int ia_blocksize; /* preferred block size for io */ unsigned int ia_pad3; /* reserved space */ gpfs_timestruc64_t ia_createtime; /* creation time */ gpfs_mask_t ia_mask; /* initial attribute mask (not used) */ int ia_pad4; /* reserved space */ unsigned int ia_reserved[GPFS_IA64_RESERVED]; /* reserved space */ unsigned int ia_xperm; /* extended attributes (defined below) */ gpfs_dev_t ia_dev; /* id of device containing file */ gpfs_dev_t ia_rdev; /* device id (if special file) */ unsigned int ia_pcacheflags; /* pcache inode bits */ gpfs_snapid64_t ia_modsnapid; /* snapshot id of last modification */ unsigned int ia_filesetid; /* fileset ID */ unsigned int ia_datapoolid; /* storage pool ID for data */ gpfs_ino64_t ia_inode_space_mask; /* inode space mask of this file system */ /* This value is saved in the iattr structure during backup and used during restore */ gpfs_off64_t ia_dirminsize; /* dir pre-allocation size in bytes */ unsigned int ia_unused[GPFS_IA64_UNUSED]; /* reserved space */ } gpfs_iattr64_t; /* Define flags for inode attributes */ #define GPFS_IAFLAG_SNAPDIR 0x0001 /* (obsolete) */ #define GPFS_IAFLAG_USRQUOTA 0x0002 /* inode is a user quota file */ #define GPFS_IAFLAG_GRPQUOTA 0x0004 /* inode is a group quota file */ #define GPFS_IAFLAG_ERROR 0x0008 /* error reading inode */ /* Define flags for inode replication attributes */ #define GPFS_IAFLAG_FILESET_ROOT 0x0010 /* root dir of a fileset */ #define GPFS_IAFLAG_NO_SNAP_RESTORE 0x0020 /* don't restore from snapshots */ #define GPFS_IAFLAG_FILESETQUOTA 0x0040 /* inode is a fileset quota file */ #define GPFS_IAFLAG_COMANAGED 0x0080 /* file data is co-managed */ #define GPFS_IAFLAG_ILLPLACED 0x0100 /* may not be properly placed */ #define GPFS_IAFLAG_REPLMETA 0x0200 /* metadata replication set */ #define GPFS_IAFLAG_REPLDATA 0x0400 /* data replication set */ #define GPFS_IAFLAG_EXPOSED 0x0800 /* may have data on suspended disks */ #define GPFS_IAFLAG_ILLREPLICATED 0x1000 /* may not be properly replicated */ #define GPFS_IAFLAG_UNBALANCED 0x2000 /* may not be properly balanced */ #define GPFS_IAFLAG_DATAUPDATEMISS 0x4000 /* has stale data blocks on unavailable disk */ #define GPFS_IAFLAG_METAUPDATEMISS 0x8000 /* has stale metadata on unavailable disk */ #define GPFS_IAFLAG_IMMUTABLE 0x00010000 /* Immutability */ #define GPFS_IAFLAG_INDEFRETENT 0x00020000 /* Indefinite retention */ #define GPFS_IAFLAG_SECUREDELETE 0x00040000 /* Secure deletion */ #define GPFS_IAFLAG_TRUNCMANAGED 0x00080000 /* dmapi truncate event enabled */ #define GPFS_IAFLAG_READMANAGED 0x00100000 /* dmapi read event enabled */ #define GPFS_IAFLAG_WRITEMANAGED 0x00200000 /* dmapi write event enabled */ #define GPFS_IAFLAG_APPENDONLY 0x00400000 /* AppendOnly only */ #define GPFS_IAFLAG_DELETED 0x00800000 /* inode has been deleted */ #ifdef ZIP #define GPFS_IAFLAG_ILLCOMPRESSED 0x01000000 /* may not be properly compressed */ #endif #define GPFS_IAFLAG_FPOILLPLACED 0x02000000 /* may not be properly placed per FPO attributes (bgf, wad, wadfg) */ /* Define flags for window's attributes */ #define GPFS_IWINFLAG_ARCHIVE 0x0001 /* Archive */ #define GPFS_IWINFLAG_HIDDEN 0x0002 /* Hidden */ #define GPFS_IWINFLAG_NOTINDEXED 0x0004 /* Not content indexed */ #define GPFS_IWINFLAG_OFFLINE 0x0008 /* Off-line */ #define GPFS_IWINFLAG_READONLY 0x0010 /* Read-only */ #define GPFS_IWINFLAG_REPARSE 0x0020 /* Reparse point */ #define GPFS_IWINFLAG_SYSTEM 0x0040 /* System */ #define GPFS_IWINFLAG_TEMPORARY 0x0080 /* Temporary */ #define GPFS_IWINFLAG_COMPRESSED 0x0100 /* Compressed */ #define GPFS_IWINFLAG_ENCRYPTED 0x0200 /* Encrypted */ #define GPFS_IWINFLAG_SPARSE 0x0400 /* Sparse file */ #define GPFS_IWINFLAG_HASSTREAMS 0x0800 /* Has streams */ /* Define flags for extended attributes */ #define GPFS_IAXPERM_ACL 0x0001 /* file has acls */ #define GPFS_IAXPERM_XATTR 0x0002 /* file has extended attributes */ #define GPFS_IAXPERM_DMATTR 0x0004 /* file has dm attributes */ #define GPFS_IAXPERM_DOSATTR 0x0008 /* file has non-default dos attrs */ #define GPFS_IAXPERM_RPATTR 0x0010 /* file has restore policy attrs */ /* Define flags for pcache bits defined in the inode */ #define GPFS_ICAFLAG_CACHED 0x0001 /* "cached complete" */ #define GPFS_ICAFLAG_CREATE 0x0002 /* "created" */ #define GPFS_ICAFLAG_DIRTY 0x0004 /* "data dirty" */ #define GPFS_ICAFLAG_LINK 0x0008 /* "hard linked" */ #define GPFS_ICAFLAG_SETATTR 0x0010 /* "attr changed" */ #define GPFS_ICAFLAG_LOCAL 0x0020 /* "local" */ #define GPFS_ICAFLAG_APPEND 0x0040 /* "append" */ #define GPFS_ICAFLAG_STATE 0x0080 /* "has remote state" */ /* Define pointers to interface types */ typedef struct gpfs_fssnap_handle gpfs_fssnap_handle_t; typedef struct gpfs_iscan gpfs_iscan_t; typedef struct gpfs_ifile gpfs_ifile_t; typedef struct gpfs_restore gpfs_restore_t; typedef struct gpfs_fssnap_id { char opaque[48]; } gpfs_fssnap_id_t; /* Define extended return codes for gpfs backup & restore calls without an explicit return code will return the value in errno */ #define GPFS_NEW_ERRNO_BASE 185 #define GPFS_E_INVAL_INUM (GPFS_NEW_ERRNO_BASE+0) /* invalid inode number */ #define GPFS_ERRNO_BASE 190 #define GPFS_E_INVAL_FSSNAPID (GPFS_ERRNO_BASE+0) /* invalid fssnap id */ #define GPFS_E_INVAL_ISCAN (GPFS_ERRNO_BASE+1) /* invalid iscan pointer */ #define GPFS_E_INVAL_IFILE (GPFS_ERRNO_BASE+2) /* invalid ifile pointer */ #define GPFS_E_INVAL_IATTR (GPFS_ERRNO_BASE+3) /* invalid iattr structure */ #define GPFS_E_INVAL_RESTORE (GPFS_ERRNO_BASE+4) /* invalid restore pointer */ #define GPFS_E_INVAL_FSSNAPHANDLE (GPFS_ERRNO_BASE+5) /* invalid fssnap handle */ #define GPFS_E_INVAL_SNAPNAME (GPFS_ERRNO_BASE+6) /* invalid snapshot name */ #define GPFS_E_FS_NOT_RESTORABLE (GPFS_ERRNO_BASE+7) /* FS is not clean */ #define GPFS_E_RESTORE_NOT_ENABLED (GPFS_ERRNO_BASE+8) /* Restore was not enabled */ #define GPFS_E_RESTORE_STARTED (GPFS_ERRNO_BASE+9) /* Restore is running */ #define GPFS_E_INVAL_XATTR (GPFS_ERRNO_BASE+10) /* invalid extended attribute pointer */ /* Define flags parameter for get/put file attributes. Used by gpfs_fgetattr, gpfs_fputattr, gpfs_fputattrwithpath gpfs_igetattrsx, gpfs_iputattrsx and gpfs_lwe_getattrs, gpfs_lwe_putattrs */ #define GPFS_ATTRFLAG_DEFAULT 0x0000 /* default behavior */ #define GPFS_ATTRFLAG_NO_PLACEMENT 0x0001 /* exclude file placement attributes */ #define GPFS_ATTRFLAG_IGNORE_POOL 0x0002 /* saved poolid is not valid */ #define GPFS_ATTRFLAG_USE_POLICY 0x0004 /* use restore policy rules to determine poolid */ #define GPFS_ATTRFLAG_INCL_DMAPI 0x0008 /* Include dmapi attributes */ #define GPFS_ATTRFLAG_FINALIZE_ATTRS 0x0010 /* Finalize immutability attributes */ #define GPFS_ATTRFLAG_SKIP_IMMUTABLE 0x0020 /* Skip immutable attributes */ #define GPFS_ATTRFLAG_INCL_ENCR 0x0040 /* Include encryption attributes */ #define GPFS_ATTRFLAG_SKIP_CLONE 0x0080 /* Skip clone attributes */ #define GPFS_ATTRFLAG_MODIFY_CLONEPARENT 0x0100 /* Allow modification on clone parent */ #ifdef ZIP #define GPFS_ATTRFLAG_NO_COMPRESSED 0x0200 /* exclude "compressed" attribute */ #endif /* Define structure used by gpfs_statfspool */ typedef struct gpfs_statfspool_s { gpfs_off64_t f_blocks; /* total data blocks in pool */ gpfs_off64_t f_bfree; /* free blocks in pool */ gpfs_off64_t f_bavail; /* free blocks avail to non-superuser */ gpfs_off64_t f_mblocks; /* total metadata blocks in pool */ gpfs_off64_t f_mfree; /* free blocks avail for system metadata */ int f_bsize; /* optimal storage pool block size */ int f_files; /* total file nodes assigned to pool */ gpfs_pool_t f_poolid; /* storage pool id */ int f_fsize; /* fundamental file system block size */ unsigned int f_usage; /* data and/or metadata stored in pool */ int f_replica; /* replica */ int f_bgf; /* block group factor */ int f_wad; /* write affinity depth */ int f_allowWriteAffinity; /* allow write affinity depth. 1 means yes */ int f_reserved[3];/* Current unused and set to zero */ } gpfs_statfspool_t; #define STATFSPOOL_USAGE_DATA 0x0001 /* Pool stores user data */ #define STATFSPOOL_USAGE_METADATA 0x0002 /* Pool stores system metadata */ /* NAME: gpfs_fstat(), gpfs_stat() * * FUNCTION: Get exact stat information for a file descriptor (or filename). * Forces all other nodes to flush dirty data and metadata to disk. * Returns: 0 Successful * -1 Failure * * Errno: ENOSYS function not available * EBADF Bad file desc * EINVAL Not a GPFS file * ESTALE cached fs information was invalid */ int GPFS_API gpfs_fstat(gpfs_file_t fileDesc, gpfs_stat64_t *buffer); int GPFS_API gpfs_stat(const char *pathname, /* File pathname */ gpfs_stat64_t *buffer); /* NAME: gpfs_fstat_x(), gpfs_stat_x() * * FUNCTION: Returns extended stat() information with specified accuracy * for a file descriptor (or filename) * * Input: fileDesc : file descriptor or handle * pathname : path to a file or directory * iattrBufLen : length of iattr buffer * * In/Out: st_litemaskP: bitmask specification of required accuracy * iattr : buffer for returned stat information * * Returns: 0 Successful * -1 Failure * * Errno: ENOSYS function not available * ENOENT invalid pathname * EBADF Bad file desc * EINVAL Not a GPFS file * ESTALE cached fs information was invalid */ int GPFS_API gpfs_fstat_x(gpfs_file_t fileDesc, unsigned int *st_litemaskP, gpfs_iattr64_t *iattr, size_t iattrBufLen); int GPFS_API gpfs_stat_x(const char *pathname, /* File pathname */ unsigned int *st_litemaskP, gpfs_iattr64_t *iattr, size_t iattrBufLen); /* NAME: gpfs_statfs64() * * FUNCTION: Get information about the file system. * * Returns: 0 Successful * -1 Failure * * Errno: ENOSYS function not available * EBADF Bad file desc * EINVAL Not a GPFS file * ESTALE cached fs information was invalid */ int GPFS_API gpfs_statfs64(const char *pathname, /* File pathname */ gpfs_statfs64_t *buffer); /* NAME: gpfs_statlite() * gpfs_lstatlite() - do not follow a symlink at the end of the path * * FUNCTION: Returns stat() information with specified accuracy * * Input: pathname : path to a file or directory * * In/Out: st_litemaskP: bitmask specification of required accuracy * statbufP : buffer for returned stat information * * Returns: 0 Successful * -1 Failure * * Errno: Specific error indication * EINVAL * */ int GPFS_API gpfs_statlite(const char *pathname, unsigned int *st_litemaskP, gpfs_stat64_t *statbufP); int GPFS_API gpfs_lstatlite(const char *pathname, unsigned int *st_litemaskP, gpfs_stat64_t *statbufP); /* NAME: gpfs_fgetattrs() * * FUNCTION: Retrieves all extended file attributes in opaque format. * This function together with gpfs_fputattrs is intended for * use by a backup program to save (gpfs_fgetattrs) and * restore (gpfs_fputattrs) all extended file attributes * (ACLs, user attributes, ...) in one call. * * NOTE: This call does not return extended attributes used for * the Data Storage Management (XDSM) API (aka DMAPI). * * Input: flags Define behavior of get attributes * GPFS_ATTRFLAG_NO_PLACEMENT - file attributes for placement * are not saved, neither is the current storage pool. * GPFS_ATTRFLAG_IGNORE_POOL - file attributes for placement * are saved, but the current storage pool is not. * * Returns: 0 Successful * -1 Failure * * Errno: ENOSYS function not available * EINVAL Not a GPFS file * EINVAL invalid flags provided * ENOSPC buffer too small to return all attributes * *attrSizeP will be set to the size necessary */ int GPFS_API gpfs_fgetattrs(gpfs_file_t fileDesc, int flags, void *bufferP, int bufferSize, int *attrSizeP); /* NAME: gpfs_fputattrs() * * FUNCTION: Sets all extended file attributes of a file * and sets the file's storage pool and data replication * to the values saved in the extended attributes. * * If the saved storage pool is not valid or if the IGNORE_POOL * flag is set, then it will select the storage pool by matching * a PLACEMENT rule using the saved file attributes. * If it fails to match a placement rule or if there are * no placement rules installed it will assign the file * to the "system" storage pool. * * The buffer passed in should contain extended attribute data * that was obtained by a previous call to gpfs_fgetattrs. * * Input: flags Define behavior of put attributes * GPFS_ATTRFLAG_NO_PLACEMENT - file attributes are restored * but the storage pool and data replication are unchanged * GPFS_ATTRFLAG_IGNORE_POOL - file attributes are restored * but the storage pool and data replication are selected * by matching the saved attributes to a placement rule * instead of restoring the saved storage pool. * * Returns: 0 Successful * -1 Failure * * Errno: ENOSYS function not available * EINVAL Not a GPFS file * EINVAL the buffer does not contain valid attribute data * EINVAL invalid flags provided */ int GPFS_API gpfs_fputattrs(gpfs_file_t fileDesc, int flags, void *bufferP); /* NAME: gpfs_fputattrswithpathname() * * FUNCTION: Sets all extended file attributes of a file and invokes * the policy engine to match a RESTORE rule using the file's * attributes saved in the extended attributes to set the * file's storage pool and data replication. The caller should * include the full path to the file, including the file name, * to allow rule selection based on file name or path. * * If the file fails to match a RESTORE rule, or if there are * no RESTORE rules installed, then the storage pool and data * replication are selected as when calling gpfs_fputattrs(). * * The buffer passed in should contain extended attribute data * that was obtained by a previous call to gpfs_fgetattrs. * * pathName is a UTF-8 encoded string. On Windows, applications * can convert UTF-16 ("Unicode") to UTF-8 using the platforms * WideCharToMultiByte function. * * * Input: flags Define behavior of put attributes * GPFS_ATTRFLAG_NO_PLACEMENT - file attributes are restored * but the storage pool and data replication are unchanged * GPFS_ATTRFLAG_IGNORE_POOL - file attributes are restored * but if the file fails to match a RESTORE rule, it * ignore the saved storage pool and select a pool * by matching the saved attributes to a PLACEMENT rule. * GPFS_ATTRFLAG_SKIP_IMMUTABLE - Skip immutable/appendOnly flags * before restoring file data. Then use GPFS_ATTRFLAG_FINALIZE_ATTRS * to restore immutable/appendOnly flags after data is restored. * GPFS_ATTRFLAG_FINALIZE_ATTRS - file attributes that are restored * after data is retored. If file is immutable/appendOnly * call without this flag before restoring data * then call with this flag after restoring data * * Returns: 0 Successful * -1 Failure * * Errno: ENOSYS function not available * EINVAL Not a GPFS file * EINVAL the buffer does not contain valid attribute data * ENOENT invalid pathname * EINVAL invalid flags provided */ int GPFS_API gpfs_fputattrswithpathname(gpfs_file_t fileDesc, int flags, void *bufferP, const char *pathName); /* NAME: gpfs_get_fssnaphandle_by_path() * * FUNCTION: Get a volatile handle to uniquely identify a file system * and snapshot by the path to the file system and snapshot * * Input: pathName: path to a file or directory in a gpfs file system * or to one of its snapshots * * Returns: pointer to gpfs_fssnap_handle_t (Successful) * NULL and errno is set (Failure) * * Errno: ENOSYS function not available * EINVAL Not a GPFS file * ENOENT invalid pathname * see system calls open(), fstatfs(), and malloc() ERRORS */ gpfs_fssnap_handle_t * GPFS_API gpfs_get_fssnaphandle_by_path(const char *pathName); /* NAME: gpfs_get_fssnaphandle_by_name() * * FUNCTION: Get a volatile handle to uniquely identify a file system * and snapshot by the file system name and snapshot name. * * Input: fsName: unique name for gpfs file system (may be specified * as fsName or /dev/fsName) * snapName: name for snapshot within that file system * or NULL to access the active file system rather * than a snapshot within the file system. * * Returns: pointer to gpfs_fssnap_handle_t (Successful) * NULL and errno is set (Failure) * * Errno: ENOSYS function not available * ENOENT invalid file system name * GPFS_E_INVAL_SNAPNAME invalid snapshot name * see system calls open(), fstatfs(), and malloc() ERRORS */ gpfs_fssnap_handle_t * GPFS_API gpfs_get_fssnaphandle_by_name(const char *fsName, const char *snapName); /* NAME: gpfs_get_fssnaphandle_by_fssnapid() * * FUNCTION: Get a volatile handle to uniquely identify a file system * and snapshot by a fssnapId created from a previous handle. * * Input: fssnapId: unique id for a file system and snapshot * * Returns: pointer to gpfs_fssnap_handle_t (Successful) * NULL and errno is set (Failure) * * Errno: ENOSYS function not available * GPFS_E_INVAL_FSSNAPID invalid snapshot id * see system calls open(), fstatfs(), and malloc() ERRORS */ gpfs_fssnap_handle_t * GPFS_API gpfs_get_fssnaphandle_by_fssnapid(const gpfs_fssnap_id_t *fssnapId); /* NAME: gpfs_get_fset_snaphandle_by_path() * * FUNCTION: Get a volatile handle to uniquely identify an inode space within a * filesyetsm and snapshot by the path to the file system and snapshot. * * Input: pathName: path to a file or directory in a gpfs file system * or to one of its snapshots * * Returns: pointer to gpfs_fssnap_handle_t (Successful) * NULL and errno is set (Failure) * * Errno: ENOSYS function not available * EINVAL Not a GPFS file * ENOENT invalid pathname * see system calls open(), fstatfs(), and malloc() ERRORS */ gpfs_fssnap_handle_t * GPFS_API gpfs_get_fset_snaphandle_by_path(const char *pathName); /* NAME: gpfs_get_fset_snaphandle_by_name() * * FUNCTION: Get a volatile handle to uniquely identify an inode space within a * file system and snapshot by the independent fileset name, file system * name and snapshot name. * * Input: fsName: unique name for gpfs file system (may be specified * as fsName or /dev/fsName) * fsetName name of the independent fileset that owns the inode space * snapName: name for snapshot within that file system * or NULL to access the active file system rather * than a snapshot within the file system. * * Returns: pointer to gpfs_fssnap_handle_t (Successful) * NULL and errno is set (Failure) * * Errno: ENOSYS function not available * ENOENT invalid file system name * GPFS_E_INVAL_FSETNAME invalid fset nsmae * GPFS_E_INVAL_SNAPNAME invalid snapshot name * see system calls open(), fstatfs(), and malloc() ERRORS */ gpfs_fssnap_handle_t * GPFS_API gpfs_get_fset_snaphandle_by_name(const char *fsName, const char *fsetName, const char *snapName); /* NAME: gpfs_get_fset_snaphandle_by_fset_snapid() * * FUNCTION: Get a volatile handle to uniquely identify a file system * and snapshot by a fssnapId created from a previous handle. * * Input: fssnapId: unique id for a file system and snapshot * * Returns: pointer to gpfs_fssnap_handle_t (Successful) * NULL and errno is set (Failure) * * Errno: ENOSYS function not available * GPFS_E_INVAL_FSSNAPID invalid snapshot id * see system calls open(), fstatfs(), and malloc() ERRORS */ gpfs_fssnap_handle_t * GPFS_API gpfs_get_fset_snaphandle_by_fset_snapid(const gpfs_fssnap_id_t *fsetsnapId); /* NAME: gpfs_get_pathname_from_fssnaphandle() * * FUNCTION: Get the mountpoint and path to a file system * and snapshot identified by a fssnapHandle * * Input: fssnapHandle: ptr to file system & snapshot handle * * Returns: ptr to path name to the file system (Successful) * NULL and errno is set (Failure) * * Errno: ENOSYS function not available * GPFS_E_INVAL_FSSNAPHANDLE invalid fssnapHandle */ const char * GPFS_API gpfs_get_pathname_from_fssnaphandle(gpfs_fssnap_handle_t *fssnapHandle); /* NAME: gpfs_get_fsname_from_fssnaphandle() * * FUNCTION: Get the unique name for the file system * identified by a fssnapHandle * * Input: fssnapHandle: ptr to file system & snapshot handle * * Returns: ptr to name of the file system (Successful) * NULL and errno is set (Failure) * * Errno: ENOSYS function not available * GPFS_E_INVAL_FSSNAPHANDLE invalid fssnapHandle */ const char * GPFS_API gpfs_get_fsname_from_fssnaphandle(gpfs_fssnap_handle_t *fssnapHandle); /* NAME: gpfs_get_snapname_from_fssnaphandle() * * FUNCTION: Get the name for the snapshot * uniquely identified by a fssnapHandle * * Input: fssnapHandle: ptr to file system & snapshot handle * * Returns: ptr to name assigned to the snapshot (Successful) * NULL and errno is set (Failure) * * Errno: ENOSYS function not available * GPFS_E_INVAL_FSSNAPHANDLE invalid fssnaphandle * GPFS_E_INVAL_SNAPNAME snapshot has been deleted * * Notes: If the snapshot has been deleted from the file system * the snapId may still be valid, but the call will fail * with errno set to GPFS_E_INVAL_SNAPNAME. */ const char * GPFS_API gpfs_get_snapname_from_fssnaphandle(gpfs_fssnap_handle_t *fssnapHandle); /* NAME: gpfs_get_snapid_from_fssnaphandle() * * FUNCTION: Get the numeric id for the snapshot identified * by a fssnapHandle. The snapshots define an ordered * sequence of changes to each file. The file's iattr * structure defines the snapshot id in which the file * was last modified (ia_modsnapid). This numeric value * can be compared to the numeric snapid from a fssnaphandle * to determine if the file changed before or after the * snapshot identified by the fssnaphandle. * * Input: fssnapHandle: ptr to file system & snapshot handle * * Returns: Numeric id for the snapshot referred to by the fssnaphandle * 0 if the fssnaphandle does not refer to a snapshot * -1 and errno is set (Failure) * * Errno: ENOSYS function not available * GPFS_E_INVAL_FSSNAPHANDLE invalid fssnaphandle * * Notes: The snapshot need not be on-line to determine the * snapshot's numeric id. */ gpfs_snapid_t GPFS_API gpfs_get_snapid_from_fssnaphandle(gpfs_fssnap_handle_t *fssnapHandle); gpfs_snapid64_t GPFS_API gpfs_get_snapid_from_fssnaphandle64(gpfs_fssnap_handle_t *fssnapHandle); /* NAME: gpfs_get_fssnapid_from_fssnaphandle() * * FUNCTION: Get a unique, non-volatile file system and snapshot id * for the file system and snapshot identified by a * volatile fssnap handle. * * Input: fssnapHandle: ptr to file system & snapshot handle * fssnapId: returned fssnapId uniquely identifying the * file system and snapshot being scanned * * Returns: 0 and fssnapId is set with id (Successful) * -1 and errno is set (Failure) * * Errno: GPFS_E_INVAL_FSSNAPHANDLE invalid fssnaphandle * EINVAL null ptr given for returned fssnapId * EFAULT size mismatch for fssnapId */ int GPFS_API gpfs_get_fssnapid_from_fssnaphandle(gpfs_fssnap_handle_t *fssnapHandle, gpfs_fssnap_id_t *fssnapId); /* NAME: gpfs_get_restore_fssnapid_from_fssnaphandle() * * FUNCTION: Get the unique, non-volatile file system and snapshot id * used for the last complete restore of a mirrored file * system. The file system must been a previous restore * target and ready for additional incremental restore. * * Input: fssnapHandle: ptr to file system & snapshot handle * fssnapId: returned fssnapId uniquely identifying the * last complete restored file system. * * Returns: 0 and fssnapId is set with id (Successful) * -1 and errno is set (Failure) * * Errno: GPFS_E_INVAL_FSSNAPHANDLE invalid fssnaphandle * EINVAL null ptr given for returned fssnapId * EFAULT size mismatch for fssnapId * EPERM caller must have superuser privilege * ENOMEM unable to allocate memory for request * GPFS_E_FS_NOT_RESTORABLE fs is not clean for restore */ int GPFS_API gpfs_get_restore_fssnapid_from_fssnaphandle(gpfs_fssnap_handle_t *fssnapHandle, gpfs_fssnap_id_t *fssnapId); /* NAME: gpfs_free_fssnaphandle() * * FUNCTION: Free a fssnapHandle * * Input: fssnapHandle: ptr to file system & snapshot handle * * Returns: void * * Errno: None */ void GPFS_API gpfs_free_fssnaphandle(gpfs_fssnap_handle_t *fssnapHandle); /* NAME: gpfs_get_snapdirname() * * FUNCTION: Get the name of the directory containing snapshots. * * Input: fssnapHandle: handle for the file system * snapdirName: buffer into which the name of the snapshot * directory will be copied * bufLen: the size of the provided buffer * * Returns: 0 (Successful) * -1 and errno is set (Failure) * * Errno: ENOSYS function not available * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * ENOMEM unable to allocate memory for request * GPFS_E_INVAL_FSSNAPHANDLE fssnapHandle is invalid * E2BIG buffer too small to return the snapshot directory name */ int GPFS_API gpfs_get_snapdirname(gpfs_fssnap_handle_t *fssnapHandle, char *snapdirName, int bufLen); /* NAME: gpfs_open_inodescan() * * FUNCTION: Open inode file for inode scan. * * Input: fssnapHandle: handle for file system and snapshot * to be scanned * prev_fssnapId: * if NULL, all inodes of existing file will be returned; * if non-null, only returns inodes of files that have changed * since the specified previous snapshot; * if specifies the same snapshot as the one referred by * fssnapHandle, only the snapshot inodes that have been * copied into this snap inode file are returned; * maxIno: if non-null, returns the maximum inode number * available in the inode file being scanned. * * Returns: pointer to gpfs_iscan_t (Successful) * NULL and errno is set (Failure) * * Errno: ENOSYS function not available * EINVAL bad parameters * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * ENOMEM unable to allocate memory for request * GPFS_E_INVAL_FSSNAPHANDLE fssnapHandle is invalid * GPFS_E_INVAL_FSSNAPID prev_fssnapId is invalid * EDOM prev_fssnapId is from a different fs * ERANGE prev_fssnapId is more recent than snapId * being scanned * see system calls dup() and malloc() ERRORS */ gpfs_iscan_t * GPFS_API gpfs_open_inodescan(gpfs_fssnap_handle_t *fssnapHandle, const gpfs_fssnap_id_t *prev_fssnapId, gpfs_ino_t *maxIno); gpfs_iscan_t * GPFS_API gpfs_open_inodescan64(gpfs_fssnap_handle_t *fssnapHandle, const gpfs_fssnap_id_t *prev_fssnapId, gpfs_ino64_t *maxIno); /* NAME: gpfs_open_inodescan_with_xattrs() * * FUNCTION: Open inode file and extended attributes for an inode scan * * Input: fssnapHandle: handle for file system and snapshot * to be scanned * prev_fssnapId: if NULL, all inodes of existing file will * be returned; if non-null, only returns inodes of files * that have changed since the specified previous snapshot; * if specifies the same snapshot as the one referred by * fssnapHandle, only the snapshot inodes that have been * copied into this snap inode file are returned; * nxAttrs: count of extended attributes to be returned. * if nxAttrs is set to 0, call returns no extended * attributes, like gpfs_open_inodescan. * if nxAttrs is set to -1, call returns all extended attributes * xAttrList: pointer to array of pointers to names of extended * attribute to be returned. nxAttrList may be null if nxAttrs * is set to 0 or -1. * maxIno: if non-null, returns the maximum inode number * available in the inode file being scanned. * * Returns: pointer to gpfs_iscan_t (Successful) * NULL and errno is set (Failure) * * Errno: ENOSYS function not available * EINVAL bad parameters * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * ENOMEM unable to allocate memory for request * GPFS_E_INVAL_FSSNAPHANDLE fssnapHandle is invalid * GPFS_E_INVAL_FSSNAPID prev_fssnapId is invalid * EDOM prev_fssnapId is from a different fs * ERANGE prev_fssnapId is more recent than snapId * being scanned * see system calls dup() and malloc() ERRORS */ gpfs_iscan_t * GPFS_API gpfs_open_inodescan_with_xattrs(gpfs_fssnap_handle_t *fssnapHandle, const gpfs_fssnap_id_t *prev_fssnapId, int nxAttrs, const char *xattrsList[], gpfs_ino_t *maxIno); gpfs_iscan_t * GPFS_API gpfs_open_inodescan_with_xattrs64(gpfs_fssnap_handle_t *fssnapHandle, const gpfs_fssnap_id_t *prev_fssnapId, int nxAttrs, const char *xattrList[], gpfs_ino64_t *maxIno); /* NAME: gpfs_next_inode() * * FUNCTION: Get next inode from inode scan. Scan terminates before * the last inode specified or the last inode in the * inode file being scanned. * * If the inode scan was opened to expressly look for inodes * in a snapshot, and not dittos, gets the next inode skipping * holes, if any. * * Input: iscan: ptr to inode scan descriptor * termIno: scan terminates before this inode number * caller may specify maxIno from gpfs_open_inodescan() * or 0 to scan the entire inode file. * iattr: pointer to returned pointer to file's iattr. * * Returns: 0 and *iattr set to point to gpfs_iattr_t (Successful) * 0 and *iattr set to NULL for no more inodes before termIno * -1 and errno is set (Failure) * * Errno: ENOSYS function not available * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * ENOMEM buffer too small * GPFS_E_INVAL_ISCAN bad parameters * GPFS_E_INVAL_FSSNAPID the snapshot id provided in the * gpfs iscan is not valid * * Notes: The data returned by gpfs_next_inode() is overwritten by * subsequent calls to gpfs_next_inode() or gpfs_seek_inode(). * * The termIno parameter provides a means to partition an * inode scan such that it may be executed on more than one node. */ int GPFS_API gpfs_next_inode(gpfs_iscan_t *iscan, gpfs_ino_t termIno, const gpfs_iattr_t **iattr); int GPFS_API gpfs_next_inode64(gpfs_iscan_t *iscan, gpfs_ino64_t termIno, const gpfs_iattr64_t **iattr); /* NAME: gpfs_next_inode_with_xattrs() * * FUNCTION: Get next inode and its extended attributes from the inode scan. * The set of extended attributes returned were defined when * the inode scan was opened. The scan terminates before the last * inode specified or the last inode in the inode file being * scanned. * * If the inode scan was opened to expressly look for inodes * in a snapshot, and not dittos, gets the next inode skipping * holes, if any. * * Input: iscan: ptr to inode scan descriptor * termIno: scan terminates before this inode number * caller may specify maxIno from gpfs_open_inodescan() * or 0 to scan the entire inode file. * iattr: pointer to returned pointer to file's iattr. * xattrBuf: pointer to returned pointer to xattr buffer * xattrBufLen: returned length of xattr buffer * * * Returns: 0 and *iattr set to point to gpfs_iattr_t (Successful) * 0 and *iattr set to NULL for no more inodes before termIno * -1 and errno is set (Failure) * * Errno: ENOSYS function not available * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * EFAULT buffer data was overwritten * ENOMEM buffer too small * GPFS_E_INVAL_ISCAN bad parameters * GPFS_E_INVAL_XATTR bad parameters * * Notes: The data returned by gpfs_next_inode() is overwritten by * subsequent calls to gpfs_next_inode(), gpfs_seek_inode() * or gpfs_stat_inode(). * * The termIno parameter provides a means to partition an * inode scan such that it may be executed on more than one node. * * The returned values for xattrBuf and xattrBufLen must be * provided to gpfs_next_xattr() to obtain the extended attribute * names and values. The buffer used for the extended attributes * is overwritten by subsequent calls to gpfs_next_inode(), * gpfs_seek_inode() or gpfs_stat_inode(); * * The returned pointers to the extended attribute name and value * will be aligned to a double-word boundary. */ int GPFS_API gpfs_next_inode_with_xattrs(gpfs_iscan_t *iscan, gpfs_ino_t termIno, const gpfs_iattr_t **iattr, const char **xattrBuf, unsigned int *xattrBufLen); int GPFS_API gpfs_next_inode_with_xattrs64(gpfs_iscan_t *iscan, gpfs_ino64_t termIno, const gpfs_iattr64_t **iattr, const char **xattrBuf, unsigned int *xattrBufLen); /* NAME: gpfs_next_xattr() * * FUNCTION: Iterate over the extended attributes buffer returned * by get_next_inode_with_xattrs to return the individual * attributes and their values. Note that the attribute names * are null-terminated strings, whereas the atttribute value * contains binary data. * * Input: iscan: ptr to inode scan descriptor * xattrBufLen: ptr to attribute buffer length * xattrBuf: ptr to the ptr to the attribute buffer * * Returns: 0 and *name set to point attribue name (Successful) * also sets: *valueLen to length of attribute value * *value to point to attribute value * *xattrBufLen to remaining length of buffer * **xattrBuf to index next attribute in buffer * 0 and *name set to NULL for no more attributes in buffer * also sets: *valueLen to 0 * *value to NULL * *xattrBufLen to 0 * **xattrBuf to NULL * -1 and errno is set (Failure) * * Errno: ENOSYS function not available * GPFS_E_INVAL_ISCAN invalid iscan parameter * GPFS_E_INVAL_XATTR invalid xattr parameters * * Notes: The caller is not allowed to modify the returned attribute * names or values. The data returned by gpfs_next_attribute() * may be overwritten by subsequent calls to gpfs_next_attribute() * or other gpfs library calls. */ int GPFS_API gpfs_next_xattr(gpfs_iscan_t *iscan, const char **xattrBuf, unsigned int *xattrBufLen, const char **name, unsigned int *valueLen, const char **value); /* NAME: gpfs_seek_inode() * * FUNCTION: Seek to a given inode number. * * Input: iscan: ptr to inode scan descriptor * ino: next inode number to be scanned * * Returns: 0 Successful * -1 Failure and errno is set * * Errno: ENOSYS function not available * GPFS_E_INVAL_ISCAN bad parameters */ int GPFS_API gpfs_seek_inode(gpfs_iscan_t *iscan, gpfs_ino_t ino); int GPFS_API gpfs_seek_inode64(gpfs_iscan_t *iscan, gpfs_ino64_t ino); #ifdef SNAPSHOT_ILM /* define GPFS generated errno */ #define GPFS_E_HOLE_IN_IFILE 238 /* hole in inode file */ #endif /* NAME: gpfs_stat_inode() * NAME: gpfs_stat_inode_with_xattrs() * * FUNCTION: Seek to the specified inode and get that inode and * its extended attributes from the inode scan. This is * simply a combination of gpfs_seek_inode and get_next_inode * but will only return the specified inode. * * Input: iscan: ptr to inode scan descriptor * ino: inode number to be returned * termIno: prefetch inodes up to this inode * caller may specify maxIno from gpfs_open_inodescan() * or 0 to allow prefetching over the entire inode file. * iattr: pointer to returned pointer to file's iattr. * xattrBuf: pointer to returned pointer to xattr buffer * xattrBufLen: returned length of xattr buffer * * Returns: 0 and *iattr set to point to gpfs_iattr_t (Successful) * 0 and *iattr set to NULL for no more inodes before termIno * or if requested inode does not exist. * -1 and errno is set (Failure) * * Errno: ENOSYS function not available * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * ENOMEM buffer too small * GPFS_E_INVAL_ISCAN bad parameters * GPFS_E_HOLE_IN_IFILE if we are expressly looking for inodes in * the snapshot file and this one has yet not * been copied into snapshot. * * Notes: The data returned by gpfs_next_inode() is overwritten by * subsequent calls to gpfs_next_inode(), gpfs_seek_inode() * or gpfs_stat_inode(). * * The termIno parameter provides a means to partition an * inode scan such that it may be executed on more than one node. * It is only used by this call to control prefetching. * * The returned values for xattrBuf and xattrBufLen must be * provided to gpfs_next_xattr() to obtain the extended attribute * names and values. The buffer used for the extended attributes * is overwritten by subsequent calls to gpfs_next_inode(), * gpfs_seek_inode() or gpfs_stat_inode(); */ int GPFS_API gpfs_stat_inode(gpfs_iscan_t *iscan, gpfs_ino_t ino, gpfs_ino_t termIno, const gpfs_iattr_t **iattr); int GPFS_API gpfs_stat_inode64(gpfs_iscan_t *iscan, gpfs_ino64_t ino, gpfs_ino64_t termIno, const gpfs_iattr64_t **iattr); int GPFS_API gpfs_stat_inode_with_xattrs(gpfs_iscan_t *iscan, gpfs_ino_t ino, gpfs_ino_t termIno, const gpfs_iattr_t **iattr, const char **xattrBuf, unsigned int *xattrBufLen); int GPFS_API gpfs_stat_inode_with_xattrs64(gpfs_iscan_t *iscan, gpfs_ino64_t ino, gpfs_ino64_t termIno, const gpfs_iattr64_t **iattr, const char **xattrBuf, unsigned int *xattrBufLen); /* NAME: gpfs_close_inodescan() * * FUNCTION: Close inode file. * * Input: iscan: ptr to inode scan descriptor * * Returns: void * * Errno: None */ void GPFS_API gpfs_close_inodescan(gpfs_iscan_t *iscan); /* NAME: gpfs_cmp_fssnapid() * * FUNCTION: Compare two fssnapIds for the same file system to * determine the order in which the two snapshots were taken. * The 'result' variable will be set as follows: * *result < 0: snapshot 1 was taken before snapshot 2 * *result == 0: snapshot 1 and 2 are the same * *result > 0: snapshot 1 was taken after snapshot 2 * * Input: fssnapId1: ptr to fssnapId 1 * fssnapId2: ptr to fssnapId id 2 * result: ptr to returned results * * Returns: 0 and *result is set as described above (Successful) * -1 and errno is set (Failure) * * Errno: ENOSYS function not available * GPFS_E_INVAL_FSSNAPID fssnapid1 or fssnapid2 is not a * valid snapshot id * EDOM the two snapshots cannot be compared because * they were taken from two different file systems. */ int GPFS_API gpfs_cmp_fssnapid(const gpfs_fssnap_id_t *fssnapId1, const gpfs_fssnap_id_t *fssnapId2, int *result); /* NAME: gpfs_iopen() * * FUNCTION: Open a file or directory by inode number. * * Input: fssnapHandle: handle for file system and snapshot * being scanned * ino: inode number * open_flags: O_RDONLY for gpfs_iread() * O_WRONLY for gpfs_iwrite() * O_CREAT create the file if it doesn't exist * O_TRUNC if the inode already exists delete it * caller may use GPFS_O_BACKUP to read files for backup * and GPFS_O_RESTORE to write files for restore * statxbuf: used only with O_CREAT/GPFS_O_BACKUP * all other cases set to NULL * symLink: used only with O_CREAT/GPFS_O_BACKUP for a symbolic link * all other cases set to NULL * * Returns: pointer to gpfs_ifile_t (Successful) * NULL and errno is set (Failure) * * Errno: ENOSYS function not available * ENOENT file not existed * EINVAL missing or bad parameter * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * ENOMEM unable to allocate memory for request * EFORMAT invalid fs version number * EIO error reading original inode * ERANGE error ino is out of range, should use gpfs_iopen64 * GPFS_E_INVAL_INUM reserved inode is not allowed to open * GPFS_E_INVAL_IATTR iattr structure was corrupted * see dup() and malloc() ERRORS */ gpfs_ifile_t * GPFS_API gpfs_iopen(gpfs_fssnap_handle_t *fssnapHandle, gpfs_ino_t ino, int open_flags, const gpfs_iattr_t *statxbuf, const char *symLink); gpfs_ifile_t * GPFS_API gpfs_iopen64(gpfs_fssnap_handle_t *fssnapHandle, gpfs_ino64_t ino, int open_flags, const gpfs_iattr64_t *statxbuf, const char *symLink); /* Define gpfs_iopen flags as used by the backup & restore by inode. The backup code will only read the source files. The restore code writes the target files & creates them if they don't already exist. The file length is set by the inode attributes. Consequently, to restore a user file it is unnecessary to include the O_TRUNC flag. */ #define GPFS_O_BACKUP (O_RDONLY) #define GPFS_O_RESTORE (O_WRONLY | O_CREAT) /* NAME: gpfs_iread() * * FUNCTION: Read file opened by gpfs_iopen. * * Input: ifile: pointer to gpfs_ifile_t from gpfs_iopen * buffer: buffer for data to be read * bufferSize: size of buffer (ie amount of data to be read) * In/Out offset: offset of where within the file to read * if successful, offset will be updated to the * next byte after the last one that was read * * Returns: number of bytes read (Successful) * -1 and errno is set (Failure) * * Errno: ENOSYS function not available * EISDIR file is a directory * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * GPFS_E_INVAL_IFILE bad ifile parameters * see system call read() ERRORS */ int GPFS_API gpfs_iread(gpfs_ifile_t *ifile, void *buffer, int bufferSize, gpfs_off64_t *offset); /* NAME: gpfs_iwrite() * * FUNCTION: Write file opened by gpfs_iopen. * * Input: ifile: pointer to gpfs_ifile_t from gpfs_iopen * buffer: the data to be written * writeLen: how much to write * In/Out offset: offset of where within the file to write * if successful, offset will be updated to the * next byte after the last one that was written * * Returns: number of bytes written (Successful) * -1 and errno is set (Failure) * * Errno: ENOSYS function not available * EISDIR file is a directory * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * GPFS_E_INVAL_IFILE bad ifile parameters * see system call write() ERRORS */ int GPFS_API gpfs_iwrite(gpfs_ifile_t *ifile, void *buffer, int writeLen, gpfs_off64_t *offset); /* NAME: gpfs_ireaddir() * * FUNCTION: Get next directory entry. * * Input: idir: pointer to gpfs_ifile_t from gpfs_iopen * dirent: pointer to returned pointer to directory entry * * Returns: 0 and pointer to gpfs_direntx set (Successful) * 0 and pointer to gpfs_direntx set to NULL (End of directory) * -1 and errno is set (Failure) * * Errno: ENOSYS function not available * ENOTDIR file is not a directory * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * GPFS_E_INVAL_IFILE bad ifile parameter * ENOMEM unable to allocate memory for request * * Notes: The data returned by gpfs_ireaddir() is overwritten by * subsequent calls to gpfs_ireaddir(). */ int GPFS_API gpfs_ireaddir(gpfs_ifile_t *idir, const gpfs_direntx_t **dirent); int GPFS_API gpfs_ireaddir64(gpfs_ifile_t *idir, const gpfs_direntx64_t **dirent); int GPFS_API gpfs_ireaddirx(gpfs_ifile_t *idir, gpfs_iscan_t *iscan, /* in only */ const gpfs_direntx_t **dirent); int GPFS_API gpfs_ireaddirx64(gpfs_ifile_t *idir, gpfs_iscan_t *iscan, /* in only */ const gpfs_direntx64_t **dirent); /* NAME: gpfs_iwritedir() * * FUNCTION: Create a directory entry in a directory opened by gpfs_iopen. * * Input: idir: pointer to gpfs_ifile_t from gpfs_iopen * dirent: directory entry to be written * * Returns: 0 (Successful) * -1 and errno is set (Failure) * * Errno: ENOSYS function not available * GPFS_E_INVAL_IFILE bad file pointer * ENOTDIR file is not a directory * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * ENOMEM unable to allocate memory for request * EFORMAT invalid dirent version number * see system call write() ERRORS */ int GPFS_API gpfs_iwritedir(gpfs_ifile_t *idir, const gpfs_direntx_t *dirent); int GPFS_API gpfs_iwritedir64(gpfs_ifile_t *idir, const gpfs_direntx64_t *dirent); /* NAME: gpfs_igetattrs() * * FUNCTION: Retrieves all extended file attributes in opaque format. * This function together with gpfs_iputattrs is intended for * use by a backup program to save (gpfs_igetattrs) and * restore (gpfs_iputattrs) all extended file attributes * (ACLs, user attributes, ...) in one call. * * NOTE: This call does not return extended attributes used for * the Data Storage Management (XDSM) API (aka DMAPI). * * Input: ifile: pointer to gpfs_ifile_t from gpfs_iopen * buffer: pointer to buffer for returned attributes * bufferSize: size of buffer * attrSize: ptr to returned size of attributes * * Returns: 0 Successful * -1 Failure and errno is set * * Errno: ENOSYS function not available * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * ENOSPC buffer too small to return all attributes * *attrSizeP will be set to the size necessary * GPFS_E_INVAL_IFILE bad ifile parameters */ int GPFS_API gpfs_igetattrs(gpfs_ifile_t *ifile, void *buffer, int bufferSize, int *attrSize); /* NAME: gpfs_igetattrsx() * * FUNCTION: Retrieves all extended file attributes in opaque format. * This function together with gpfs_iputattrsx is intended for * use by a backup program to save (gpfs_igetattrsx) and * restore (gpfs_iputattrsx) all extended file attributes * (ACLs, user attributes, ...) in one call. * * NOTE: This call can optionally return extended attributes * used for the Data Storage Management (XDSM) API * (aka DMAPI). * * Input: ifile: pointer to gpfs_ifile_t from gpfs_iopen * flags Define behavior of get attributes * GPFS_ATTRFLAG_NO_PLACEMENT - file attributes for placement * are not saved, neither is the current storage pool. * GPFS_ATTRFLAG_IGNORE_POOL - file attributes for placement * are saved, but the current storage pool is not. * GPFS_ATTRFLAG_INCL_DMAPI - file attributes for dmapi are * included in the returned buffer * GPFS_ATTRFLAG_INCL_ENCR - file attributes for encryption * are included in the returned buffer * * buffer: pointer to buffer for returned attributes * bufferSize: size of buffer * attrSize: ptr to returned size of attributes * * Returns: 0 Successful * -1 Failure * * Errno: ENOSYS function not available * EINVAL Not a GPFS file * EINVAL invalid flags provided * ENOSPC buffer too small to return all attributes * *attrSizeP will be set to the size necessary */ int GPFS_API gpfs_igetattrsx(gpfs_ifile_t *ifile, int flags, void *buffer, int bufferSize, int *attrSize); /* NAME: gpfs_igetxattr() * * FUNCTION: Retrieves an extended file attributes from ifile which has been open * by gpfs_iopen(). * * NOTE: This call does not return extended attributes used for * the Data Storage Management (XDSM) API (aka DMAPI). * * Input: ifile: pointer to gpfs_ifile_t from gpfs_iopen * buffer: pointer to buffer for key and returned extended * attribute value * bufferSize: size of buffer, should be enough to save attribute value * attrSize: ptr to key length as input and ptr to the returned * size of attributes as putput. * * Returns: 0 Successful * -1 Failure and errno is set * * Errno: ENOSYS function not available * EPERM caller must have superuser priviledges * ESTALE cached fs information was invalid * ENOSPC buffer too small to return all attributes * *attrSize will be set to the size necessary * GPFS_E_INVAL_IFILE bad ifile parameters */ int GPFS_API gpfs_igetxattr(gpfs_ifile_t *ifile, void *buffer, int bufferSize, int *attrSize); /* NAME: gpfs_iputattrs() * * FUNCTION: Sets all extended file attributes of a file. * The buffer passed in should contain extended attribute data * that was obtained by a previous call to gpfs_igetattrs. * * NOTE: This call will not restore extended attributes * used for the Data Storage Management (XDSM) API * (aka DMAPI). They will be silently ignored. * * Input: ifile: pointer to gpfs_ifile_t from gpfs_iopen * buffer: pointer to buffer for returned attributes * * Returns: 0 Successful * -1 Failure and errno is set * * Errno: ENOSYS function not available * EINVAL the buffer does not contain valid attribute data * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * GPFS_E_INVAL_IFILE bad ifile parameters */ int GPFS_API gpfs_iputattrs(gpfs_ifile_t *ifile, void *buffer); /* NAME: gpfs_iputattrsx() * * FUNCTION: Sets all extended file attributes of a file. * * This routine can optionally invoke the policy engine * to match a RESTORE rule using the file's attributes saved * in the extended attributes to set the file's storage pool and * data replication as when calling gpfs_fputattrswithpathname. * When used with the policy the caller should include the * full path to the file, including the file name, to allow * rule selection based on file name or path. * * By default, the routine will not use RESTORE policy rules * for data placement. The pathName parameter will be ignored * and may be set to NULL. * * If the call does not use RESTORE policy rules, or if the * file fails to match a RESTORE rule, or if there are no * RESTORE rules installed, then the storage pool and data * replication are selected as when calling gpfs_fputattrs(). * * The buffer passed in should contain extended attribute data * that was obtained by a previous call to gpfs_fgetattrs. * * pathName is a UTF-8 encoded string. On Windows, applications * can convert UTF-16 ("Unicode") to UTF-8 using the platforms * WideCharToMultiByte function. * * NOTE: This call will restore extended attributes * used for the Data Storage Management (XDSM) API * (aka DMAPI) if they are present in the buffer. * * Input: ifile: pointer to gpfs_ifile_t from gpfs_iopen * flags Define behavior of put attributes * GPFS_ATTRFLAG_NO_PLACEMENT - file attributes are restored * but the storage pool and data replication are unchanged * GPFS_ATTRFLAG_IGNORE_POOL - file attributes are restored * but the storage pool and data replication are selected * by matching the saved attributes to a placement rule * instead of restoring the saved storage pool. * GPFS_ATTRFLAG_USE_POLICY - file attributes are restored * but the storage pool and data replication are selected * by matching the saved attributes to a RESTORE rule * instead of restoring the saved storage pool. * GPFS_ATTRFLAG_FINALIZE_ATTRS - file attributes that are restored * after data is retored. If file is immutable/appendOnly * call without this flag before restoring data * then call with this flag after restoring data * GPFS_ATTRFLAG_INCL_ENCR - file attributes for encryption * are restored. Note that this may result in the file's * File Encryption Key (FEK) being changed, and in this * case any prior content in the file is effectively lost. * This option should only be used when the entire file * content is restored after the attributes are restored. * * buffer: pointer to buffer for returned attributes * pathName: pointer to file path and file name for file * May be set to NULL. * * Returns: 0 Successful * -1 Failure and errno is set * * Errno: ENOSYS function not available * EINVAL the buffer does not contain valid attribute data * EINVAL invalid flags provided * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * GPFS_E_INVAL_IFILE bad ifile parameters */ int GPFS_API gpfs_iputattrsx(gpfs_ifile_t *ifile, int flags, void *buffer, const char *pathName); /* NAME: gpfs_igetfilesetname() * * FUNCTION: Retrieves the name of the fileset which contains this file. * The fileset name is a null-terminated string, with a * a maximum length of GPFS_MAXNAMLEN. * * Input: iscan: ptr to gpfs_iscan_t from gpfs_open_inodescan() * filesetId: ia_filesetId returned in an iattr from the iscan * buffer: pointer to buffer for returned fileset name * bufferSize: size of buffer * * Returns: 0 Successful * -1 Failure and errno is set * * Errno: ENOSYS function not available * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * ENOSPC buffer too small to return fileset name * GPFS_E_INVAL_ISCAN bad iscan parameter */ int GPFS_API gpfs_igetfilesetname(gpfs_iscan_t *iscan, unsigned int filesetId, void *buffer, int bufferSize); /* NAME: gpfs_igetstoragepool() * * FUNCTION: Retrieves the name of the storage pool assigned for * this file's data. The storage pool name is a null-terminated * string, with a maximum length of GPFS_MAXNAMLEN. * * Input: iscan: ptr to gpfs_iscan_t from gpfs_open_inodescan() * dataPoolId: ia_dataPoolId returned in an iattr from the iscan * buffer: pointer to buffer for returned attributes * bufferSize: size of buffer * * Returns: 0 Successful * -1 Failure and errno is set * * Errno: ENOSYS function not available * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * ENOSPC buffer too small to return all storage pool name * GPFS_E_INVAL_ISCAN bad iscan parameters */ int GPFS_API gpfs_igetstoragepool(gpfs_iscan_t *iscan, unsigned int dataPoolId, void *buffer, int bufferSize); /* NAME: gpfs_iclose() * * FUNCTION: Close file opened by inode and update dates. * * Input: ifile: pointer to gpfs_ifile_t from gpfs_iopen * * Returns: void */ void GPFS_API gpfs_iclose(gpfs_ifile_t *ifile); /* NAME: gpfs_ireadlink() * * FUNCTION: Read symbolic link by inode number. * * Input: fssnapHandle: handle for file system & snapshot being scanned * ino: inode number of link file to read * buffer: pointer to buffer for returned link data * bufferSize: size of the buffer * * Returns: number of bytes read (Successful) * -1 and errno is set (Failure) * * Errno: ENOSYS function not available * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * GPFS_E_INVAL_FSSNAPHANDLE invalid fssnap handle * see system call readlink() ERRORS */ int GPFS_API gpfs_ireadlink(gpfs_fssnap_handle_t *fssnapHandle, gpfs_ino_t ino, char *buffer, int bufferSize); int GPFS_API gpfs_ireadlink64(gpfs_fssnap_handle_t *fssnapHandle, gpfs_ino64_t ino, char *buffer, int bufferSize); /* NAME: gpfs_sync_fs() * * FUNCTION: sync file system. * * Input: fssnapHandle: handle for file system being restored * * Returns: 0 all data flushed to disk (Successful) * -1 and errno is set (Failure) * * Errno: ENOSYS function not available * ENOMEM unable to allocate memory for request * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * GPFS_E_INVAL_FSSNAPHANDLE invalid fssnapHandle */ int GPFS_API gpfs_sync_fs(gpfs_fssnap_handle_t *fssnapHandle); /* NAME: gpfs_enable_restore() * * FUNCTION: Mark file system as enabled for restore on/off * * Input: fssnapHandle: handle for file system to be enabled * or disabled for restore * on_off: flag set to 1 to enable restore * 0 to disable restore * * Returns: 0 (Successful) * -1 and errno is set (Failure) * * Errno: ENOSYS function not available * EINVAL bad parameters * GPFS_E_INVAL_FSSNAPHANDLE invalid fssnapHandle * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * ENOMEM unable to allocate memory for request * E_FS_NOT_RESTORABLE fs is not clean * EALREADY fs already marked as requested * E_RESTORE_STARTED restore in progress * * Notes: EALREADY indicates enable/disable restore was already called * for this fs. The caller must decide if EALREADY represents an * error condition. */ int GPFS_API gpfs_enable_restore(gpfs_fssnap_handle_t *fssnapHandle, int on_off); /* NAME: gpfs_start_restore() * * FUNCTION: Start a restore session. * * Input: fssnapHandle: handle for file system to be restored * restore_flags: Flag to indicate the restore should be started * even if a prior restore has not completed. * old_fssnapId: fssnapId of last restored snapshot * new_fssnapId: fssnapId of snapshot being restored * * Returns: pointer to gpfs_restore_t (Successful) * NULL and errno is set (Failure) * * Errno: ENOSYS function not available * ENOMEM unable to allocate memory for request * EINVAL missing parameter * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * EDOM restore fs does not match existing fs * ERANGE restore is missing updates * EFORMAT invalid fs version number * GPFS_E_INVAL_FSSNAPHANDLE invalid fssnaphandle * GPFS_E_INVAL_FSSNAPID bad fssnapId parameter * E_FS_NOT_RESTORABLE fs is not clean for restore * E_RESTORE_NOT_ENABLED fs is not enabled for restore * EALREADY Restore already in progress * * Note: EALREADY indicates start restore was already called for * this fs. This could be due to a prior restore process that failed * or it could be due to a concurrent restore process still running. * The caller must decide if EALREADY represents an error condition. */ gpfs_restore_t * GPFS_API gpfs_start_restore(gpfs_fssnap_handle_t *fssnapHandle, int restore_flags, const gpfs_fssnap_id_t *old_fssnapId, const gpfs_fssnap_id_t *new_fssnapId); #define GPFS_RESTORE_NORMAL 0 /* Restore not started if prior restore has not completed. */ #define GPFS_RESTORE_FORCED 1 /* Restore starts even if prior restore has not completed. */ /* NAME: gpfs_end_restore() * * FUNCTION: End a restore session. * * Input: restoreId: ptr to gpfs_restore_t * * Returns: 0 (Successful) * -1 and errno is set (Failure) * * Errno: ENOSYS function not available * EINVAL bad parameters * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * GPFS_E_INVAL_RESTORE bad restoreId parameter * GPFS_E_FS_NOT_RESTORABLE fs is not clean for restore * GPFS_E_RESTORE_NOT_ENABLED fs is not enabled for restore * EALREADY Restore already ended * * Note: EALREADY indicates end restore was already called for * this fs. This could be due to a concurrent restore process that * already completed. The caller must decide if EALREADY represents * an error condition. */ int GPFS_API gpfs_end_restore(gpfs_restore_t *restoreId); /* NAME: gpfs_ireadx() * * FUNCTION: Block level incremental read on a file opened by gpfs_iopen * with a given incremental scan opened via gpfs_open_inodescan. * * Input: ifile: ptr to gpfs_ifile_t returned from gpfs_iopen() * iscan: ptr to gpfs_iscan_t from gpfs_open_inodescan() * buffer: ptr to buffer for returned data * bufferSize: size of buffer for returned data * offset: ptr to offset value * termOffset: read terminates before reading this offset * caller may specify ia_size for the file's * gpfs_iattr_t or 0 to scan the entire file. * hole: ptr to returned flag to indicate a hole in the file * * Returns: number of bytes read and returned in buffer * or size of hole encountered in the file. (Success) * -1 and errno is set (Failure) * * On input, *offset contains the offset in the file * at which to begin reading to find a difference same file * in a previous snapshot specified when the inodescan was opened. * On return, *offset contains the offset of the first * difference. * * On return, *hole indicates if the change in the file * was data (*hole == 0) and the data is returned in the * buffer provided. The function's value is the amount of data * returned. If the change is a hole in the file, * *hole != 0 and the size of the changed hole is returned * as the function value. * * A call with a NULL buffer pointer will query the next increment * to be read from the current offset. The *offset, *hole and * returned length will be set for the next increment to be read, * but no data will be returned. The bufferSize parameter is * ignored, but the termOffset parameter will limit the * increment returned. * * Errno: ENOSYS function not available * EINVAL missing or bad parameter * EISDIR file is a directory * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * ENOMEM unable to allocate memory for request * EDOM fs snapId does match local fs * ERANGE previous snapId is more recent than scanned snapId * GPFS_E_INVAL_IFILE bad ifile parameter * GPFS_E_INVAL_ISCAN bad iscan parameter * see system call read() ERRORS * * Notes: The termOffset parameter provides a means to partition a * file's data such that it may be read on more than one node. */ gpfs_off64_t GPFS_API gpfs_ireadx(gpfs_ifile_t *ifile, /* in only */ gpfs_iscan_t *iscan, /* in only */ void *buffer, /* in only */ int bufferSize, /* in only */ gpfs_off64_t *offset, /* in/out */ gpfs_off64_t termOffset, /* in only */ int *hole); /* out only */ /* NAME: gpfs_ireadx_ext * * FUNCTION: gpfs_ireadx_ext is used to find different blocks between clone * child and parent files. Input and output are the same as * gpfs_ireadx. * * Returns: See gpfs_ireadx() */ gpfs_off64_t GPFS_API gpfs_ireadx_ext(gpfs_ifile_t *ifile, /* in only */ gpfs_iscan_t *iscan, /* in only */ void *buffer, /* in only */ int bufferSize, /* in only */ gpfs_off64_t *offset, /* in/out */ gpfs_off64_t termOffset, /* in only */ int *hole); /* NAME: gpfs_iwritex() * * FUNCTION: Write file opened by gpfs_iopen. * If parameter hole == 0, then write data * addressed by buffer to the given offset for the * given length. If hole != 0, then write * a hole at the given offset for the given length. * * Input: ifile : ptr to gpfs_ifile_t returned from gpfs_iopen() * buffer: ptr to data buffer * writeLen: length of data to write * offset: offset in file to write data * hole: flag =1 to write a "hole" * =0 to write data * * Returns: number of bytes/size of hole written (Success) * -1 and errno is set (Failure) * * Errno: ENOSYS function not available * EINVAL missing or bad parameter * EISDIR file is a directory * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * GPFS_E_INVAL_IFILE bad ifile parameter * see system call write() ERRORS */ gpfs_off64_t GPFS_API gpfs_iwritex(gpfs_ifile_t *ifile, /* in only */ void *buffer, /* in only */ gpfs_off64_t writeLen, /* in only */ gpfs_off64_t offset, /* in only */ int hole); /* in only */ /* NAME: gpfs_statfspool() * * FUNCTION: Obtain status information about the storage pools * * Input: pathname : path to any file in the file system * poolId : id of first pool to return * on return set to next poolId or -1 * to indicate there are no more pools. * options : option flags (currently not used) * nPools : number of stat structs requested or 0 * on return number of stat structs in buffer * or if nPools was 0 its value is the max number * of storage pools currently defined * buffer : ptr to return stat structures * bufferSize : sizeof stat buffer * * The user is expected to issue two or more calls. On the first * call the user should pass nPools set to 0 and gpfs will * return in nPools the total number of storage pools currently * defined for the file system indicated by the pathname * and it returns in poolId the id of the first storage pool. * The buffer parameter may be set to NULL for this call. * * The user may then allocate a buffer large enough to contain * a gpfs_statfspool_t structure for each of the pools and issue * a second call to obtain stat information about each pool. * Parameter nPools should be set the number of pools requested. * On return, nPools will be set to the number of stat structs * contained in the buffer, and poolId will be set to the id * of the next storage pool or -1 to indicate there are no * additional storage pools defined. * * Alternatively, if the user has a valid poolId from a previous * call, the user may provide that poolId and a buffer large * enough for a single gpfs_statfspool_t structure, and the call * will return the status for a single storage pool. * * * Returns: 0 Successful * -1 Failure * * Errno: Specific error indication * EINVAL */ int GPFS_API gpfs_statfspool(const char *pathname, /* in only: path to file system*/ gpfs_pool_t *poolId, /* in out: id of first pool to return on return set to next poolId or -1 when there are no more pools */ unsigned int options, /* in only: option flags */ int *nPools, /* in out: number of pool stats requested on return number of stat structs returned in buffer or if nPools was set to 0, the return value is the number of pools currently defined */ void *buffer, /* ptr to return stat structures */ int bufferSize); /* sizeof stat buffer or 0 */ /* NAME: gpfs_getpoolname() * * FUNCTION: Retrieves the name of the storage pool assigned for * this file's data. The storage pool name is a null-terminated * string, with a maximum length of GPFS_MAXNAMLEN. * * Input: pathname: path to any file in the file system * poolId: f_poolid returned in gpfs_statfspool_t * buffer: pointer to buffer for returned name * bufferSize: size of buffer * * Returns: 0 Successful * -1 Failure and errno is set * * Errno: ENOSYS function not available * ESTALE file system was unmounted * E_FORMAT_INCOMPAT file system does not support pools * E2BIG buffer too small to return storage pool name */ int GPFS_API gpfs_getpoolname(const char *pathname, gpfs_pool_t poolId, void *buffer, int bufferSize); /* /usr/src/linux/include/linux/fs.h includes /usr/src/linux/include/linux/quota.h which has conflicting definitions. */ #ifdef _LINUX_QUOTA_ #undef Q_SYNC #undef Q_GETQUOTA #undef Q_SETQUOTA #undef Q_QUOTAON #undef Q_QUOTAOFF #endif /* GPFS QUOTACTL */ /* * Command definitions for the 'gpfs_quotactl' system call. * The commands are broken into a main command defined below * and a subcommand that is used to convey the type of * quota that is being manipulated (see above). */ #define SUBCMDMASK 0x00ff #define SUBCMDSHIFT 8 #define GPFS_QCMD(cmd, type) (((cmd) << SUBCMDSHIFT) | ((type) & SUBCMDMASK)) #define Q_QUOTAON 0x0100 /* enable quotas */ #define Q_QUOTAOFF 0x0200 /* disable quotas */ #define Q_GETQUOTA 0x0300 /* get limits and usage */ #ifndef _LINUX_SOURCE_COMPAT /* Standard AIX definitions of quota commands */ #define Q_SETQUOTA 0x0400 /* set limits */ #define Q_SETQLIM Q_SETQUOTA #else /* Alternate definitions, for Linux Affinity */ #define Q_SETQLIM 0x0400 /* set limits */ #define Q_SETQUOTA 0x0700 /* set limits and usage */ #endif #define Q_SETUSE 0x0500 /* set usage */ #define Q_SYNC 0x0600 /* sync disk copy of a file systems quotas */ #define Q_SETGRACETIME 0x0900 /* set grace time */ #define Q_SETGRACETIME_ENHANCE 0x0800 /* set grace time and update all * quota entries */ #define Q_GETDQPFSET 0x0A00 /* get default quota per fileset */ #define Q_SETDQPFSET 0x0B00 /* set default quota per fileset */ #define Q_SETQUOTA_UPDATE_ET 0x0C00 /* this SETQUOTA needs to update entryType */ #define Q_GETDQPFSYS 0x0D00 /* get default quota per file system */ #define Q_SETDQPFSYS 0x0E00 /* set default quota per file system */ /* gpfs quota types */ #define GPFS_USRQUOTA 0 #define GPFS_GRPQUOTA 1 #define GPFS_FILESETQUOTA 2 /* define GPFS generated errno */ #define GPFS_E_NO_QUOTA_INST 237 /* file system does not support quotas */ typedef struct gpfs_quotaInfo { gpfs_off64_t blockUsage; /* current block count in 1 KB units*/ gpfs_off64_t blockHardLimit; /* absolute limit on disk blks alloc */ gpfs_off64_t blockSoftLimit; /* preferred limit on disk blks */ gpfs_off64_t blockInDoubt; /* distributed shares + "lost" usage for blks */ int inodeUsage; /* current # allocated inodes */ int inodeHardLimit; /* absolute limit on allocated inodes */ int inodeSoftLimit; /* preferred inode limit */ int inodeInDoubt; /* distributed shares + "lost" usage for inodes */ gpfs_uid_t quoId; /* uid, gid or fileset id */ int entryType; /* entry type, not used */ unsigned int blockGraceTime; /* time limit for excessive disk use */ unsigned int inodeGraceTime; /* time limit for excessive inode use */ } gpfs_quotaInfo_t; /* NAME: gpfs_quotactl() * * FUNCTION: Manipulate disk quotas * INPUT: pathname: specifies the pathname of any file within the * mounted file system to which the command is to * be applied * cmd: specifies a quota control command to be applied * to UID/GID/FILESETID id. The cmd parameter can be * constructed using GPFS_QCMD(cmd, type) macro defined * in gpfs.h * id: UID or GID or FILESETID that command applied to. * bufferP: points to the address of an optional, command * specific, data structure that is copied in or out of * the system. * * OUTPUT: bufferP, if applicable. * * Returns: 0 success * -1 failure * * Errno: EACCESS * EFAULT An invalid bufferP parameter is supplied; * the associated structure could not be copied * in or out of the kernel * EINVAL * ENOENT No such file or directory * EPERM The quota control command is privileged and * the caller did not have root user authority * EOPNOTSUPP * GPFS_E_NO_QUOTA_INST The file system does not support quotas */ int GPFS_API gpfs_quotactl(const char *pathname, int cmd, int id, void *bufferP); /* NAME: gpfs_getfilesetid() * * FUNCTION: Translate FilesetName to FilesetID * * INPUT: pathname: specifies the pathname of any file within the * mounted file system to which the command is to * be applied * name: name of the fileset * * OUTPUT: idP: points to the address of an integer that receives the ID * * Returns: 0 success * -1 failure * * Errno: EACCESS * EFAULT An invalid pointer is supplied; the associated * data could not be copied in or out of the kernel * EINVAL * ENOENT No such file, directory or fileset */ int GPFS_API gpfs_getfilesetid(const char *pathname, const char *name, int *idP); /* NAME: gpfs_clone_snap() * * FUNCTION: Create an immutable clone parent from a source file * * Input: sourcePathP: path to source file, which will be cloned * destPathP: path to destination file, to be created * * If destPathP is NULL, then the source file will be changed * in place into an immutable clone parent. * * Returns: 0 Successful * -1 Failure * * Errno: ENOSYS Function not available * ENOENT File does not exist * EACCESS Write access to target or source search permission denied * EINVAL Not a regular file or not a GPFS file system * EFAULT Input argument points outside accessible address space * ENAMETOOLONG Source or destination path name too long * ENOSPC Not enough space on disk * EISDIR Destination is a directory * EXDEV Source and destination aren't in the same file system * EROFS Destination is read-only * EPERM Invalid source file * EEXIST Destination file already exists * EBUSY Source file is open * EFORMAT File system does not support clones * EMEDIUMTYPE File system does not support clones */ int GPFS_API gpfs_clone_snap(const char *sourcePathP, const char *destPathP); /* NAME: gpfs_clone_copy() * * FUNCTION: Create a clone copy of an immutable clone parent file * * Input: sourcePathP: path to immutable source file, to be cloned * destPathP: path to destination file, to be created * * Returns: 0 Successful * -1 Failure * * Errno: ENOSYS Function not available * ENOENT File does not exist * EACCESS Write access to target or source search permission denied * EINVAL Not a regular file or not a GPFS file system * EFAULT Input argument points outside accessible address space * ENAMETOOLONG Source or destination path name too long * ENOSPC Not enough space on disk * EISDIR Destination is a directory * EXDEV Source and destination aren't in the same file system * EROFS Destination is read-only * EPERM Invalid source or destination file * EEXIST Destination file already exists * EFORMAT File system does not support clones * EMEDIUMTYPE File system does not support clones */ int GPFS_API gpfs_clone_copy(const char *sourcePathP, const char *destPathP); /* NAME: gpfs_declone() * * FUNCTION: Copy blocks from clone parent(s) to child so that the * parent blocks are no longer referenced by the child. * * Input: fileDesc: File descriptor for file to be de-cloned * ancLimit: Ancestor limit (immediate parent only, or all) * nBlocks: Maximum number of GPFS blocks to copy * In/Out: offsetP: Pointer to starting offset within file (will be * updated to offset of next block to process or * -1 if no more blocks) * * Returns: 0 Successful * -1 Failure * * Errno: ENOSYS Function not available * EINVAL Invalid argument to function * EBADF Bad file descriptor or not a GPFS file * EPERM Not a regular file * EACCESS Write access to target file not permitted * EFAULT Input argument points outside accessible address space * ENOSPC Not enough space on disk */ /* Values for ancLimit */ #define GPFS_CLONE_ALL 0 #define GPFS_CLONE_PARENT_ONLY 1 int GPFS_API gpfs_declone(gpfs_file_t fileDesc, int ancLimit, gpfs_off64_t nBlocks, gpfs_off64_t *offsetP); /* NAME: gpfs_clone_split() * * FUNCTION: Split a clone child file from its parent. Must call * gpfs_declone first, to remove all references. * * Input: fileDesc: File descriptor for file to be split * ancLimit: Ancestor limit (immediate parent only, or all) * * Returns: 0 Successful * -1 Failure * * Errno: ENOSYS Function not available * EINVAL Invalid argument to function * EBADF Bad file descriptor or not a GPFS file * EPERM Not a regular file or not a clone child * EACCESS Write access to target file not permitted */ int GPFS_API gpfs_clone_split(gpfs_file_t fileDesc, int ancLimit); /* NAME: gpfs_clone_unsnap() * * FUNCTION: Change a clone parent with no children back into a * normal file. * * Input: fileDesc: File descriptor for file to be un-snapped * * Returns: 0 Successful * -1 Failure * * Errno: ENOSYS Function not available * EINVAL Invalid argument to function * EBADF Bad file descriptor or not a GPFS file * EPERM Not a regular file or not a clone parent * EACCESS Write access to target file not permitted */ int GPFS_API gpfs_clone_unsnap(gpfs_file_t fileDesc); /* NAME: gpfs_get_fset_masks() * * FUNCTION: return bit masks governing "external" inode and inode-space numbering * * Input: fset_snaphandle: ptr to an fset snaphandle * Output: the bit masks and inodes per block factor. * * Returns: 0 Success * -1 Failure * * Errno: ENOSYS function not available * GPFS_E_INVAL_FSSNAPHANDLE invalid fssnapHandle */ int GPFS_API gpfs_get_fset_masks(gpfs_fssnap_handle_t* fset_snapHandle, gpfs_ino64_t* inodeSpaceMask, gpfs_ino64_t* inodeBlockMask, int* inodesPerInodeBlock); /* * API functions for Light Weight Event */ /* * Define light weight event types */ typedef enum { GPFS_LWE_EVENT_UNKNOWN = 0, /* "Uknown event" */ GPFS_LWE_EVENT_FILEOPEN = 1, /* 'OPEN' - look at getInfo('OPEN_FLAGS') if you care */ GPFS_LWE_EVENT_FILECLOSE = 2, /* "File Close Event" 'CLOSE' */ GPFS_LWE_EVENT_FILEREAD = 3, /* "File Read Event" 'READ' */ GPFS_LWE_EVENT_FILEWRITE = 4, /* "File Write Event" 'WRITE' */ GPFS_LWE_EVENT_FILEDESTROY = 5, /* File is being destroyed 'DESTROY' */ GPFS_LWE_EVENT_FILEEVICT = 6, /* OpenFile object is being evicted from memory 'FILE_EVICT' */ GPFS_LWE_EVENT_BUFFERFLUSH = 7, /* Data buffer is being written to disk 'BUFFER_FLUSH' */ GPFS_LWE_EVENT_POOLTHRESHOLD = 8, /* Storage pool exceeded defined utilization 'POOL_THRESHOLD' */ GPFS_LWE_EVENT_FILEDATA = 9, /* "Read/Write/Trunc" event on open file */ GPFS_LWE_EVENT_FILERENAME = 10, /* Rename event on open file */ GPFS_LWE_EVENT_FILEUNLINK = 11, /* Unlink file event */ GPFS_LWE_EVENT_FILERMDIR = 12, /* Remove directory event */ GPFS_LWE_EVENT_EVALUATE = 13, /* Evaluate And Set Events */ GPFS_LWE_EVENT_FILEOPEN_READ = 14, /* Open for Read Only - EVENT 'OPEN_READ' - deprecated, use 'OPEN' */ GPFS_LWE_EVENT_FILEOPEN_WRITE = 15, /* Open with Writing privileges - EVENT 'OPEN_WRITE' - deprecated, use 'OPEN' */ GPFS_LWE_EVENT_FILEPOOL_CHANGE = 16, /* Open with Writing privileges - EVENT 'OPEN_WRITE' - deprecated, use 'OPEN' */ GPFS_LWE_EVENT_MAX = 17, /* 1 greater than any of the above */ } gpfs_lwe_eventtype_t; /* Define light weight event response types */ typedef enum { GPFS_LWE_RESP_INVALID = 0, /* "Response Invalid/Unknown" */ GPFS_LWE_RESP_CONTINUE = 1, /* "Response Continue" */ GPFS_LWE_RESP_ABORT = 2, /* "Response Abort" */ GPFS_LWE_RESP_DONTCARE = 3 /* "Response DontCare" */ } gpfs_lwe_resp_t; /* * Define light weight event inofrmation */ #define LWE_DATA_FS_NAME 0x00000001 /* "fsName" */ #define LWE_DATA_PATH_NAME 0x00000002 /* "pathName" */ #define LWE_DATA_PATH_NEW_NAME 0x00000004 /* "pathNewName" for reanem */ #define LWE_DATA_URL 0x00000008 /* "URL" */ #define LWE_DATA_INODE 0x00000010 /* "inode" */ #define LWE_DATA_OPEN_FLAGS 0x00000020 /* "openFlags" */ #define LWE_DATA_POOL_NAME 0x00000040 /* "poolName" */ #define LWE_DATA_FILE_SIZE 0x00000080 /* "fileSize" */ #define LWE_DATA_OWNER_UID 0x00000100 /* "ownerUserId" */ #define LWE_DATA_OWNER_GID 0x00000200 /* "ownerGroupId" */ #define LWE_DATA_ATIME 0x00000400 /* "atime" */ #define LWE_DATA_MTIME 0x00000800 /* "mtime" */ #define LWE_DATA_NOW_TIME 0x00001000 /* "nowTime" */ #define LWE_DATA_ELAPSED_TIME 0x00002000 /* "elapsedTime" */ #define LWE_DATA_CLIENT_UID 0x00004000 /* "clientUserId" */ #define LWE_DATA_CLIENT_GID 0x00008000 /* "clientGroupId" */ #define LWE_DATA_NFS_IP 0x00010000 /* "clientIp" */ #define LWE_DATA_PROCESS_ID 0x00020000 /* "processId" */ #define LWE_DATA_TARGET_POOL_NAME 0x00040000 /* "targetPoolName" */ #define LWE_DATA_BYTES_READ 0x00080000 /* "bytesRead" */ #define LWE_DATA_BYTES_WRITTEN 0x00100000 /* "bytesWritten" */ #define LWE_DATA_CLUSTER_NAME 0x00200000 /* "clusterName" */ #define LWE_DATA_NODE_NAME 0x00400000 /* "nodeName" */ /* * Define light weight events */ #define LWE_EVENT_EVALUATED 0x00000001 /* policy was evaluated */ #define LWE_EVENT_FILEOPEN 0x00000002 /* "op_open" */ #define LWE_EVENT_FILECLOSE 0x00000004 /* "op_close" */ #define LWE_EVENT_FILEREAD 0x00000008 /* "op_read" */ #define LWE_EVENT_FILEWRITE 0x00000010 /* "op_write" */ #define LWE_EVENT_FILEDESTROY 0x00000020 /* "op_destroy" */ #define LWE_EVENT_FILEEVICT 0x00000040 /* "op_evict" OpenFile object is being evicted from memory 'FILE_EVICT' */ #define LWE_EVENT_BUFFERFLUSH 0x00000080 /* "op_buffer_flush" Data buffer is being written to disk 'BUFFER_FLUSH' */ #define LWE_EVENT_POOLTHRESHOLD 0x00000100 /* "op_pool_threshhold" Storage pool exceeded defined utilization 'POOL_THRESHOLD' */ #define LWE_EVENT_FILEDATA 0x00000200 /* "op_data" "Read/Write/Trunc" event on open file */ #define LWE_EVENT_FILERENAME 0x00000400 /* "op_rename" Rename event on open file */ #define LWE_EVENT_FILEUNLINK 0x00000800 /* "op_unlink" Unlink file event */ #define LWE_EVENT_FILERMDIR 0x00001000 /* "op_rmdir" Remove directory event */ #define LWE_EVENT_FILEOPEN_READ 0x00002000 /* "op_open_read" Open for Read Only - EVENT 'OPEN_READ' - deprecated, use 'OPEN' */ #define LWE_EVENT_FILEOPEN_WRITE 0x00004000 /* "op_open_write" Open with Writing privileges - EVENT 'OPEN_WRITE' - deprecated, use 'OPEN' */ #define LWE_EVENT_FILEPOOL_CHANGE 0x00008000 /* "op_pool_change" Open with Writing privileges - EVENT 'OPEN_WRITE' - deprecated, use 'OPEN' */ /* * Defines for light weight sessions */ typedef unsigned long long gpfs_lwe_sessid_t; #define GPFS_LWE_NO_SESSION ((gpfs_lwe_sessid_t) 0) #define GPFS_LWE_SESSION_INFO_LEN 256 /* * Define light weight token to identify access right */ typedef struct gpfs_lwe_token { unsigned long long high; unsigned long long low; #ifdef __cplusplus bool operator == (const struct gpfs_lwe_token& rhs) const { return high == rhs.high && low == rhs.low; }; bool operator != (const struct gpfs_lwe_token& rhs) const { return high != rhs.high || low != rhs.low; }; #endif /* __cplusplus */ } gpfs_lwe_token_t; /* Define special tokens */ static const gpfs_lwe_token_t _gpfs_lwe_no_token = { 0, 0 }; #define GPFS_LWE_NO_TOKEN _gpfs_lwe_no_token static const gpfs_lwe_token_t _gpfs_lwe_invalid_token = { 0, 1 }; #define GPFS_LWE_INVALID_TOKEN _gpfs_lwe_invalid_token /* * Note: LWE data managers can set a file's off-line bit * or any of the managed bits visible to the policy language * by calling dm_set_region or dm_set_region_nosync * with a LWE session and LWE exclusive token. To set the bits * there must be * exactly one managed region with offset = -1 * and size = 0. Any other values will return EINVAL. */ /* LWE also provides light weight regions * that are set via policy rules. */ #define GPFS_LWE_MAX_REGIONS 2 /* LWE data events are generated from user access * to a LWE managed region. */ #define GPFS_LWE_DATAEVENT_NONE (0x0) #define GPFS_LWE_DATAEVENT_READ (0x1) #define GPFS_LWE_DATAEVENT_WRITE (0x2) #define GPFS_LWE_DATAEVENT_TRUNCATE (0x4) /* * Define light weight event structure */ typedef struct gpfs_lwe_event { int eventLen; /* offset 0 */ gpfs_lwe_eventtype_t eventType; /* offset 4 */ gpfs_lwe_token_t eventToken; /* offset 8 <--- Must on DWORD */ int isSync; /* offset 16 */ int parmLen; /* offset 20 */ char* parmP; /* offset 24 <-- Must on DWORD */ } gpfs_lwe_event_t; /* * Define light weight access rights */ #define GPFS_LWE_RIGHT_NULL 0 #define GPFS_LWE_RIGHT_SHARED 1 #define GPFS_LWE_RIGHT_EXCL 2 /* Flag indicating whether to wait * when requesting a right or an event */ #define GPFS_LWE_FLAG_NONE 0 #define GPFS_LWE_FLAG_WAIT 1 /* NAME: gpfs_lwe_create_session() * * FUNCTION: create a light weight event session * * Input: oldsid: existing session id, * Set to GPFS_LWE_NO_SESSION to start new session * - If a session with the same name and id already exists * it is not terminated, nor will outstanding events * be redelivered. This is typically used if a session * is shared between multiple processes. * Set to an existing session's id to resume that session * - If a session with the same name exists, that session * will be terminated. All pending/outstanding events * for the old session will be redelivered on the new one. * This is typically used to take over a session from a * failed/hung process. * sessinfop: session string, unique for each session * * Output: newsidp: session id for new session * * Returns: 0 Success * -1 Failure * * Errno: ENOSYS Function not available * EINVAL invalid parameters * ENFILE maximum number of sessions have already been created * ENOMEM insufficient memory to create new session * ENOENT session to resume does not exist * EEXIST session to resume exists with different id * EPERM Caller does not hold appropriate privilege */ int GPFS_API gpfs_lwe_create_session(gpfs_lwe_sessid_t oldsid, /* IN */ char *sessinfop, /* IN */ gpfs_lwe_sessid_t *newsidp); /* OUT */ #define GPFS_MAX_LWE_SESSION_INFO_LEN 100 /* NAME: gpfs_lwe_destroy_session() * * FUNCTION: destroy a light weight event session * * Input: sid: id of the session to be destroyed * * Returns: 0 Success * -1 Failure * * Errno: ENOSYS Function not available * EINVAL sid invalid * EBUSY session is busy * EPERM Caller does not hold appropriate privilege */ int GPFS_API gpfs_lwe_destroy_session(gpfs_lwe_sessid_t sid); /* IN */ /* NAME: gpfs_lwe_getall_sessions() * * FUNCTION: fetch all lwe sessions * * Input: nelem: max number of elements * sidbufp: array of session id * nelemp: number of session returned in sidbufp * * Returns: 0 Success * -1 Failure * * Errno: ENOSYS Function not available * EINVAL pass in args invalid * E2BIG information is too large * EPERM Caller does not hold appropriate privilege */ int GPFS_API gpfs_lwe_getall_sessions(unsigned int nelem, /* IN */ gpfs_lwe_sessid_t *sidbufp, /* OUT */ unsigned int *nelemp); /* OUT */ /* NAME: gpfs_lw_query_session() * * FUNCTION: query session string by id * * Input: sid: id of session to be queryed * buflen: length of buffer * bufp: buffer to store sessions string * rlenp: returned length of bufp * * Returns: 0 Success * -1 Failure * * Errno: ENOSYS Function not available * EINVAL pass in args invalid * E2BIG information is too large * EPERM Caller does not hold appropriate privilege */ int GPFS_API gpfs_lwe_query_session(gpfs_lwe_sessid_t sid, /* IN */ size_t buflen, /* IN */ void *bufp, /* OUT */ size_t *rlenP); /* OUT */ /* NAME: gpfs_lwe_get_events() * * FUNCTION: get events from a light weight session * * Input: sid: id of the session * maxmsgs: max number of event to fetch, * 0 to fetch all possible * flags: GPFS_LWE_EV_WAIT: waiting for new events if event * queue is empty * buflen: length of the buffer * bufp: buffer to hold events * rlenp: returned length of bufp * * Returns: 0 Success * E2BIG information is too large * EINVAL pass in args invalid */ int GPFS_API gpfs_lwe_get_events(gpfs_lwe_sessid_t sid, /* IN */ unsigned int maxmsgs, /* IN */ unsigned int flags, /* IN */ size_t buflen, /* IN */ void *bufp, /* OUT */ size_t *rlenp); /* OUT */ /* NAME: gpfs_lwe_respond_event() * * FUNCTION: response to a light weight event * * Input: sid: id of the session * token: token of the event * response: response to the event * reterror: return error to event callers * * Returns: 0 Success * EINVAL pass in args invalid * */ int GPFS_API gpfs_lwe_respond_event(gpfs_lwe_sessid_t sid, /* IN */ gpfs_lwe_token_t token, /* IN */ gpfs_lwe_resp_t response, /* IN */ int reterror); /* IN */ /* NAME: gpfs_lwe_request_right * * FUNCTION: Request an access right to a file using a dmapi handle * * Input: sid Id of lw session * hanp Pointer to dmapi handle * hlen Length of dmapi handle * right Shared or exclusive access requested * flags Caller will wait to acquire access if necessary * * Output: token Unique identifier for access right * * Returns: 0 Success * -1 Failure * * Errno: ENOSYS Function not available * ESTALE GPFS not available * EINVAL Invalid arguments * EFAULT Invalid pointer provided * EBADF Bad file * ENOMEM Uable to allocate memory for request * EPERM Caller does not hold appropriate privilege * EAGAIN flags parameter did not include WAIT * and process would be blocked * */ int GPFS_API gpfs_lwe_request_right(gpfs_lwe_sessid_t sid, /* IN */ void *hanp, /* IN */ size_t hlen, /* IN */ unsigned int right, /* IN */ unsigned int flags, /* IN */ gpfs_lwe_token_t *token); /* OUT */ /* NAME: gpfs_lwe_upgrade_right * * FUNCTION: Upgrade an access right from shared to exclusive * * This is a non-blocking call to upgrade an access right * from shared to exclusive. If the token already conveys * exclusive access this call returns imediately with sucess. * If another process also holds a shared access right * this call fails with EBUSY to avoid deadlocks. * * Input: sid Id of lw session * hanp Pointer to dmapi handle * hlen Length of dmapi handle * token Unique identifier for access right * * Output: None * * Returns: 0 Success * -1 Failure * * Errno: ENOSYS Function not available * ESTALE GPFS not available * EINVAL Invalid arguments * EINVAL The token is invalid * EFAULT Invalid pointer provided * EPERM Caller does not hold appropriate privilege * EPERM Token's right is not shared or exclusive * EBUSY Process would be blocked * */ int GPFS_API gpfs_lwe_upgrade_right(gpfs_lwe_sessid_t sid, /* IN */ void *hanp, /* IN */ size_t hlen, /* IN */ gpfs_lwe_token_t token); /* IN */ /* NAME: gpfs_lwe_downgrade_right * * FUNCTION: Downgrade an access right from exclusive to shared * * This reduces an access right from exclusive to shared * without dropping the exclusive right to acquire the shared. * The token must convey exclusive right before the call. * * Input: sid Id of lw session * hanp Pointer to dmapi handle * hlen Length of dmapi handle * token Unique identifier for access right * * Output: None * * Returns: 0 Success * -1 Failure * * Errno: ENOSYS Function not available * ESTALE GPFS not available * EINVAL Invalid arguments * EINVAL The token is invalid * EFAULT Invalid pointer provided * EPERM Caller does not hold appropriate privilege * EPERM Token's right is not exclusive * */ int GPFS_API gpfs_lwe_downgrade_right(gpfs_lwe_sessid_t sid, /* IN */ void *hanp, /* IN */ size_t hlen, /* IN */ gpfs_lwe_token_t token); /* IN */ /* NAME: gpfs_lwe_release_right * * FUNCTION: Release an access right conveyed by a token * * This releases the access right held by a token * and invalidates the token. Once the access right * is released the token cannot be reused. * * Input: sid Id of lw session * hanp Pointer to dmapi handle * hlen Length of dmapi handle * token Unique identifier for access right * * Output: None * * Returns: 0 Success * -1 Failure * * Errno: ENOSYS Function not available * ESTALE GPFS not available * EINVAL Invalid arguments * EINVAL The token is invalid * EFAULT Invalid pointer provided * EPERM Caller does not hold appropriate privilege */ int GPFS_API gpfs_lwe_release_right(gpfs_lwe_sessid_t sid, /* IN */ void *hanp, /* IN */ size_t hlen, /* IN */ gpfs_lwe_token_t token); /* IN */ /* NAME: gpfs_lwe_getattrs() * * FUNCTION: Retrieves all extended file attributes in opaque format. * This function together with gpfs_lwe_putattrs is intended for * use by a backup program to save (gpfs_lwe_getattrs) and * restore (gpfs_lwe_putattrs) all extended file attributes * (ACLs, user attributes, ...) in one call. * * NOTE: This call is the lwe equivalent of gpfs_igetattrsx * but uses a file handle to identify the file * and an existing LWE token for locking it. * * * Input: sid Id of lw session * hanp Pointer to dmapi handle * hlen Length of dmapi handle * token Unique identifier for access right * flags Define behavior of get attributes * GPFS_ATTRFLAG_NO_PLACEMENT - file attributes for placement * are not saved, neither is the current storage pool. * GPFS_ATTRFLAG_IGNORE_POOL - file attributes for placement * are saved, but the current storage pool is not. * GPFS_ATTRFLAG_INCL_DMAPI - file attributes for dmapi are * included in the returned buffer * GPFS_ATTRFLAG_INCL_ENCR - file attributes for encryption * are included in the returned buffer * * buffer: pointer to buffer for returned attributes * bufferSize: size of buffer * attrSize: ptr to returned size of attributes * * Returns: 0 Successful * -1 Failure * * Errno: ENOSYS function not available * EINVAL Not a GPFS file * EINVAL invalid flags provided * ENOSPC buffer too small to return all attributes * *attrSizeP will be set to the size necessary */ int GPFS_API gpfs_lwe_getattrs(gpfs_lwe_sessid_t sid, void *hanp, size_t hlen, gpfs_lwe_token_t token, int flags, void *buffer, int bufferSize, int *attrSize); /* NAME: gpfs_lwe_putattrs() * * FUNCTION: Sets all extended file attributes of a file. * * This routine can optionally invoke the policy engine * to match a RESTORE rule using the file's attributes saved * in the extended attributes to set the file's storage pool and * data replication as when calling gpfs_fputattrswithpathname. * When used with the policy the caller should include the * full path to the file, including the file name, to allow * rule selection based on file name or path. * * By default, the routine will not use RESTORE policy rules * for data placement. The pathName parameter will be ignored * and may be set to NULL. * * If the call does not use RESTORE policy rules, or if the * file fails to match a RESTORE rule, or if there are no * RESTORE rules installed, then the storage pool and data * replication are selected as when calling gpfs_fputattrs(). * * The buffer passed in should contain extended attribute data * that was obtained by a previous call to gpfs_fgetattrs. * * pathName is a UTF-8 encoded string. On Windows, applications * can convert UTF-16 ("Unicode") to UTF-8 using the platforms * WideCharToMultiByte function. * * NOTE: This call is the lwe equivalent of gpfs_iputaattrsx * but uses a file handle to identify the file * and an existing LWE token for locking it. * * * Input: sid Id of lw session * hanp Pointer to dmapi handle * hlen Length of dmapi handle * token Unique identifier for access right * flags Define behavior of put attributes * GPFS_ATTRFLAG_NO_PLACEMENT - file attributes are restored * but the storage pool and data replication are unchanged * GPFS_ATTRFLAG_IGNORE_POOL - file attributes are restored * but the storage pool and data replication are selected * by matching the saved attributes to a placement rule * instead of restoring the saved storage pool. * GPFS_ATTRFLAG_USE_POLICY - file attributes are restored * but the storage pool and data replication are selected * by matching the saved attributes to a RESTORE rule * instead of restoring the saved storage pool. * GPFS_ATTRFLAG_FINALIZE_ATTRS - file attributes that are restored * after data is retored. If file is immutable/appendOnly * call without this flag before restoring data * then call with this flag after restoring data * GPFS_ATTRFLAG_INCL_ENCR - file attributes for encryption * are restored. Note that this may result in the file's * File Encryption Key (FEK) being changed, and in this * case any prior content in the file is effectively lost. * This option should only be used when the entire file * content is restored after the attributes are restored. * * buffer: pointer to buffer for returned attributes * pathName: pointer to file path and file name for file * May be set to NULL. * * Returns: 0 Successful * -1 Failure and errno is set * * Errno: ENOSYS function not available * EINVAL the buffer does not contain valid attribute data * EINVAL invalid flags provided * EPERM caller must have superuser privilege * ESTALE cached fs information was invalid * GPFS_E_INVAL_IFILE bad ifile parameters */ int GPFS_API gpfs_lwe_putattrs(gpfs_lwe_sessid_t sid, void *hanp, size_t hlen, gpfs_lwe_token_t token, int flags, void *buffer, const char *pathName); const char* GPFS_API gpfs_get_fspathname_from_fsname(const char* fsname_or_path); /* Check that fsname_or_path refers to a GPFS file system and find the path to its root Return a strdup()ed copy of the path -OR- NULL w/errno */ int GPFS_API /* experimental */ gpfs_qos_getstats( const char *fspathname, /* in only: path to file system*/ unsigned int options, /* in only: option flags: 0=begin at specified qip, 1=begin after qip */ unsigned int qosid, /* in only: 0 or a specific qosid at which to start or continue */ gpfs_pool_t poolid, /* in only: -1 or a specific poolid at which to start or continue */ unsigned int mqips, /* in only: max number of qip=(qosid,poolid) histories to retrieve */ unsigned int nslots, /* in only: max number of time slots of history to retrieve */ void *bufferP, /* ptr to return stat structures */ unsigned int bufferSize); /* sizeof stat buffer or 0 */ int GPFS_API /* experimental */ gpfs_qos_control( const char *fspathname, /* in only: path to file system*/ void *bufferP, /* in/out control/get/set structs */ unsigned int bufferSize); int GPFS_API gpfs_qos_set( const char *fspathname, const char *classname, /* "gold", "silver", or .. "1" or "2" .. */ int id, /* process id or pgrp or userid */ int which, /* process, pgrp or user */ double* qshareP); /* return the share, percentage or when negative IOP limit */ /* if id==0 then getpid() or getpgrp() or getuid() if which==0 or 1 then process, if 2 process then group, if 3 then userid Return -1 on error, with errno= ENOSYS if QOS is not available in the currently installed GPFS software. ENOENT if classname is not recognized. ENXIO if QOS throttling is not active (but classname is recognized and *qshareP has configured value) */ /* For the given process get QOS info */ int GPFS_API gpfs_qos_get( const char *fspathname, int *classnumP, char classname[18], /* "gold", "silver", or .. "1" or "2" .. */ int id, /* process id or pgrp or userid */ int which, /* process, pgrp or user */ double* qshareP); /* return the share, percentage or when negative IOP limit */ /* given classname, set *classnumP and set *qshareP Return -1 on error, with errno= ENOSYS if QOS is not available in the currently installed GPFS software. ENOENT if classname is not recognized. ENXIO if QOS throttling is not active (but classname is recognized, *classnumP and *qshareP have configured values) */ int GPFS_API gpfs_qos_lkupName( const char *fspathname, int *classnumP, const char *classname, double* qshareP); /* given classnumber, find name and share (similar to above), but start with number instead of name */ int GPFS_API gpfs_qos_lkupVal( const char *fspathname, int val, char classname[18], double* qshareP); int GPFS_API gpfs_ioprio_set(int,int,int); /* do not call directly */ int GPFS_API gpfs_ioprio_get(int,int); /* do not call directly */ /* NAME: gpfs_enc_file_rewrap_key() * * FUNCTION: Re-wrap the File Encryption Key (FEK) for the file, * replacing the usage of the original (second parameter) * Master Encryption Key (MEK) with the new key provided as * the third parameter. The content of the file remains intact. * * If the FEK is not currently being wrapped with the MEK * identified by the second parameter then no action is taken. * * This function is normally invoked before the original MEK is * removed. * * The file may be opened in read-only mode for this function * to perform the key rewrap. * * Superuser privilege is required to invoke this API. * * INPUT: fileDesc: File descriptor for file whose key is to be rewrapped * orig_key_p: Key ID for the key (MEK) to be replaced * new_key_p: Key ID for the new key (MEK) to be used * * OUTPUT: N/A * * Returns: 0 success * -1 failure * * Errno: * EACCESS Existing or new key cannot be retrieved * The new key is already being used to wrap the * file's FEK * EBADF Bad file descriptor * EINVAL Arguments are invalid: key format is incorrect * EFAULT An invalid pointer is supplied; the associated * data could not be copied in or out of the kernel * E2BIG Key IDs provided are too long * ENOSYS Function not available (cluster or file system not * enabled for encryption) * EPERM File is in a snapshot * Caller must have superuser privilege */ /* The Key ID is a string comprised of the key ID and the remote key server RKM ID, separated by ':' */ typedef const char *gpfs_enc_key_id_t; /* " : " */ int GPFS_API gpfs_enc_file_rewrap_key(gpfs_file_t fileDesc, gpfs_enc_key_id_t orig_key_p, gpfs_enc_key_id_t new_key_p); /* NAME: gpfs_enc_get_algo() * * FUNCTION: Retrieve a string describing the encryption algorithm, key * length, Master Encryption Key(s) ID, and wrapping and combining * mechanisms used for the file. * * INPUT: fileDesc: File descriptor for file whose encryption * algorithm is being retrieved * encryption_xattrP: content of the gpfs.Encryption * extended attribute, retrieved by a call to * gpfs_fcntl (with structure type GPFS_FCNTL_GET_XATTR) * xattr_len: length of the data in encryption_xattrP * algo_txt_size: space reserved by the caller for algo_txtP * * OUTPUT: algo_txtP: NULL-terminated string describing the * encryption for the file * * Returns: 0 success * -1 failure * * Errno: * ENOENT File not found * EBADF Bad file handle, not a GPFS file * EACCESS Permission denied * EFAULT Bad address provided * EINVAL Not a regular file * EINVAL Invalid values for xattr_len or algo_txt_size * EINVAL Invalid content of encryption extended attribute * ENOSYS Function not available * E2BIG Output string does not fit in algo_txtP */ int GPFS_API gpfs_enc_get_algo(gpfs_file_t fileDesc, const char *encryption_xattrP, int xattr_len, char *algo_txtP, int algo_txt_size); /* NAME: gpfs_init_trace() * * FUNCTION: Initialize the GPFS trace facility and start to use it. * Must be called before calling gpfs_add_trace(). * * Returns: 0 Success * -1 Failure * * Errno: ENOENT file not found * ENOMEM Memory allocation failed * EACCESS Permission denied * ENFILE Too many open files * ENOSYS Function not available */ int GPFS_API gpfs_init_trace(void); /* NAME: gpfs_query_trace() * * FUNCTION: Query and cache the latest settings of GPFS trace facility. * Generally this should be called by the notification handler * for the "traceConfigChanged" event, which is invoked when * something changes in the configuration of the trace facility. * * Returns: 0 Success * -1 Failure * * Errno: ENOENT file not found * ENOMEM Memory allocation failed * EACCESS Permission denied * ENFILE Too many open files * ENOSYS Function not available */ int GPFS_API gpfs_query_trace(void); /* NAME: gpfs_add_trace() * * FUNCTION: write the logs into GPFS trace driver. When the user specified * parameter "level" is less than or equal to the GPFS trace level, * the log message pointed to by parameter "msg" would be written to * GPFS trace buffer, and user can use mmtracectl command to cut * the GPFS trace buffer into a file to observe. Must be called after * the call to gpfs_init_trace(). Also ensure the gpfs_query_trace() * is called properly to update the gpfs trace level cached in * application, otherwise, the trace may miss to write down to * GPFS trace driver. * * Input: level: the level for this trace generation. When the level * is less than or equal to the GPFS trace level, this * trace record would be written to GPFS trace buffer. * msg: the message string that would be put into GPFS trace buffer. * * Returns: None. */ void GPFS_API gpfs_add_trace(int level, const char *msg); /* NAME: gpfs_fini_trace() * * FUNCTION: Stop using GPFS trace facility. This should be paired with * gpfs_init_trace(), and must be called after the last * gpfs_add_trace(). * * Returns: None. */ void gpfs_fini_trace(void); /* * When GPFS_64BIT_INODES is defined, use the 64-bit interface definitions as * the default. */ #ifdef GPFS_64BIT_INODES #undef GPFS_D_VERSION #define GPFS_D_VERSION GPFS_D64_VERSION #undef GPFS_IA_VERSION #define GPFS_IA_VERSION GPFS_IA64_VERSION #define gpfs_ino_t gpfs_ino64_t #define gpfs_gen_t gpfs_gen64_t #define gpfs_uid_t gpfs_uid64_t #define gpfs_gid_t gpfs_gid64_t #define gpfs_snapid_t gpfs_snapid64_t #define gpfs_nlink_t gpfs_nlink64_t #define gpfs_timestruc_t gpfs_timestruc64_t #define gpfs_direntx_t gpfs_direntx64_t #define gpfs_direntx gpfs_direntx64 #define gpfs_iattr_t gpfs_iattr64_t #define gpfs_get_snapid_from_fssnaphandle gpfs_get_snapid_from_fssnaphandle64 #define gpfs_open_inodescan gpfs_open_inodescan64 #define gpfs_open_inodescan_with_xattrs gpfs_open_inodescan_with_xattrs64 #define gpfs_next_inode gpfs_next_inode64 #define gpfs_next_inode_with_xattrs gpfs_next_inode_with_xattrs64 #define gpfs_seek_inode gpfs_seek_inode64 #define gpfs_stat_inode gpfs_stat_inode64 #define gpfs_stat_inode_with_xattrs gpfs_stat_inode_with_xattrs64 #define gpfs_iopen gpfs_iopen64 #define gpfs_ireaddir gpfs_ireaddir64 #define gpfs_ireaddirx gpfs_ireaddirx64 #define gpfs_iwritedir gpfs_iwritedir64 #define gpfs_ireadlink gpfs_ireadlink64 #endif #define gpfs_icreate gpfs_icreate64 #ifdef __cplusplus } #endif #endif /* H_GPFS */ nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/include/gpfs_nfs.h000066400000000000000000000453021324272410200221730ustar00rootroot00000000000000/* */ /* Copyright (C) 2001 International Business Machines */ /* All rights reserved. */ /* */ /* This file is part of the GPFS user library. */ /* */ /* Redistribution and use in source and binary forms, with or without */ /* modification, are permitted provided that the following conditions */ /* are met: */ /* */ /* 1. Redistributions of source code must retain the above copyright notice, */ /* this list of conditions and the following disclaimer. */ /* 2. Redistributions in binary form must reproduce the above copyright */ /* notice, this list of conditions and the following disclaimer in the */ /* documentation and/or other materials provided with the distribution. */ /* 3. The name of the author may not be used to endorse or promote products */ /* derived from this software without specific prior written */ /* permission. */ /* */ /* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR */ /* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES */ /* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. */ /* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ /* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, */ /* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; */ /* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, */ /* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR */ /* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF */ /* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* */ /* %Z%%M% %I% %W% %G% %U% */ /* * Library calls for GPFS interfaces */ #ifndef H_GPFS_NFS #define H_GPFS_NFS #ifdef __cplusplus extern "C" { #endif #ifdef WIN32 struct flock {}; #endif /* GANESHA common information */ #define GPFS_DEVNAMEX "/dev/ss0" /* Must be the same as GPFS_DEVNAME */ #define kGanesha 140 /* Must be the same as Ganesha in enum kxOps */ #define OPENHANDLE_GET_VERSION 100 #define OPENHANDLE_GET_VERSION2 1002 #define OPENHANDLE_NAME_TO_HANDLE 101 #define OPENHANDLE_OPEN_BY_HANDLE 102 #define OPENHANDLE_LAYOUT_TYPE 106 #define OPENHANDLE_GET_DEVICEINFO 107 #define OPENHANDLE_GET_DEVICELIST 108 #define OPENHANDLE_LAYOUT_GET 109 #define OPENHANDLE_LAYOUT_RETURN 110 #define OPENHANDLE_INODE_UPDATE 111 #define OPENHANDLE_GET_XSTAT 112 #define OPENHANDLE_SET_XSTAT 113 #define OPENHANDLE_CHECK_ACCESS 114 #define OPENHANDLE_OPEN_SHARE_BY_HANDLE 115 #define OPENHANDLE_GET_LOCK 116 #define OPENHANDLE_SET_LOCK 117 #define OPENHANDLE_THREAD_UPDATE 118 #define OPENHANDLE_LAYOUT_COMMIT 119 #define OPENHANDLE_DS_READ 120 #define OPENHANDLE_DS_WRITE 121 #define OPENHANDLE_GET_VERIFIER 122 #define OPENHANDLE_FSYNC 123 #define OPENHANDLE_SHARE_RESERVE 124 #define OPENHANDLE_GET_NODEID 125 #define OPENHANDLE_SET_DELEGATION 126 #define OPENHANDLE_CLOSE_FILE 127 #define OPENHANDLE_LINK_BY_FH 128 #define OPENHANDLE_RENAME_BY_FH 129 #define OPENHANDLE_STAT_BY_NAME 130 #define OPENHANDLE_GET_HANDLE 131 #define OPENHANDLE_READLINK_BY_FH 132 #define OPENHANDLE_UNLINK_BY_NAME 133 #define OPENHANDLE_CREATE_BY_NAME 134 #define OPENHANDLE_READ_BY_FD 135 #define OPENHANDLE_WRITE_BY_FD 136 #define OPENHANDLE_CREATE_BY_NAME_ATTR 137 #define OPENHANDLE_GRACE_PERIOD 138 #define OPENHANDLE_ALLOCATE_BY_FD 139 #define OPENHANDLE_REOPEN_BY_FD 140 #define OPENHANDLE_FADVISE_BY_FD 141 #define OPENHANDLE_SEEK_BY_FD 142 #define OPENHANDLE_STATFS_BY_FH 143 #define OPENHANDLE_GETXATTRS 144 #define OPENHANDLE_SETXATTRS 145 #define OPENHANDLE_REMOVEXATTRS 146 #define OPENHANDLE_LISTXATTRS 147 #define OPENHANDLE_MKNODE_BY_NAME 148 #define OPENHANDLE_reserved 149 #define OPENHANDLE_TRACE_ME 150 #define OPENHANDLE_QUOTA 151 #define OPENHANDLE_FS_LOCATIONS 152 /* If there is any change in above constants, then update below values. * Currently ignoring opcode 1002 */ #define GPFS_MIN_OP OPENHANDLE_GET_VERSION #define GPFS_MAX_OP OPENHANDLE_FS_LOCATIONS #define GPFS_STAT_NO_OP_1 3 #define GPFS_STAT_NO_OP_2 4 #define GPFS_STAT_NO_OP_3 5 /* max stat ops including placeholder for phantom ops */ #define GPFS_STAT_MAX_OPS (GPFS_MAX_OP-GPFS_MIN_OP+2) /* placeholder index is the last index in the array */ #define GPFS_STAT_PH_INDEX (GPFS_STAT_MAX_OPS-1) /* total ops excluding the missing ops 103, 104 and 105 and the placeholder * for phantom ops */ #define GPFS_TOTAL_OPS (GPFS_STAT_MAX_OPS-4) struct trace_arg { uint32_t level; uint32_t len; char *str; }; #define ganesha_v1 1 #define ganesha_v2 2 int gpfs_ganesha(int op, void *oarg); #define OPENHANDLE_HANDLE_LEN 40 #define OPENHANDLE_KEY_LEN 28 #define OPENHANDLE_VERSION 2 struct xstat_cred_t { uint32_t principal; /* user id */ uint32_t group; /* primary group id */ uint16_t num_groups; /* number secondary groups for this user */ #define XSTAT_CRED_NGROUPS 32 uint32_t eGroups[XSTAT_CRED_NGROUPS];/* array of secondary groups */ }; struct gpfs_time_t { uint32_t t_sec; uint32_t t_nsec; }; struct gpfs_file_handle { uint16_t handle_size; uint16_t handle_type; uint16_t handle_version; uint16_t handle_key_size; uint32_t handle_fsid[2]; /* file identifier */ unsigned char f_handle[OPENHANDLE_HANDLE_LEN]; }; struct name_handle_arg { int dfd; int flag; const char *name; struct gpfs_file_handle *handle; int expfd; }; struct get_handle_arg { int mountdirfd; int len; const char *name; struct gpfs_file_handle *dir_fh; struct gpfs_file_handle *out_fh; }; struct open_arg { int mountdirfd; int flags; int openfd; struct gpfs_file_handle *handle; const char *cli_ip; }; struct link_fh_arg { int mountdirfd; int len; const char *name; struct gpfs_file_handle *dir_fh; struct gpfs_file_handle *dst_fh; }; struct rename_fh_arg { int mountdirfd; int old_len; const char *old_name; int new_len; const char *new_name; struct gpfs_file_handle *old_fh; struct gpfs_file_handle *new_fh; }; struct glock { int cmd; int lfd; void *lock_owner; struct flock flock; }; #define GPFS_F_CANCELLK (1024 + 5) /* Maps to Linux F_CANCELLK */ #define FL_RECLAIM 4 #define EGRACE 140 struct set_get_lock_arg { int mountdirfd; struct glock *lock; int reclaim; }; struct open_share_arg { int mountdirfd; int flags; int openfd; struct gpfs_file_handle *handle; int share_access; int share_deny; int reclaim; }; struct share_reserve_arg { int mountdirfd; int openfd; int share_access; int share_deny; }; struct fadvise_arg { int mountdirfd; int openfd; uint64_t offset; uint64_t length; uint32_t *hints; }; struct gpfs_io_info { uint32_t io_what; uint64_t io_offset; uint64_t io_len; uint32_t io_eof; uint32_t io_alloc; }; struct fseek_arg { int mountdirfd; int openfd; struct gpfs_io_info *info; }; struct close_file_arg { int mountdirfd; int close_fd; int close_flags; void *close_owner; }; struct link_arg { int file_fd; int dir_fd; const char *name; }; struct readlink_arg { int fd; char *buffer; int size; }; struct readlink_fh_arg { int mountdirfd; struct gpfs_file_handle *handle; char *buffer; int size; }; struct nfsd4_pnfs_deviceid { /** FSAL_ID - to dispatch getdeviceinfo based on */ uint8_t fsal_id; /** Break up the remainder into useful chunks */ uint8_t device_id1; uint16_t device_id2; uint32_t device_id4; uint64_t devid; }; struct gpfs_exp_xdr_stream { int *p; int *end; }; enum x_nfsd_fsid { x_FSID_DEV = 0, x_FSID_NUM, x_FSID_MAJOR_MINOR, x_FSID_ENCODE_DEV, x_FSID_UUID4_INUM, x_FSID_UUID8, x_FSID_UUID16, x_FSID_UUID16_INUM, x_FSID_MAX }; enum x_pnfs_layouttype { x_LAYOUT_NFSV4_1_FILES = 1, x_LAYOUT_OSD2_OBJECTS = 2, x_LAYOUT_BLOCK_VOLUME = 3, x_NFS4_PNFS_PRIVATE_LAYOUT = 0x80000000 }; /* used for both layout return and recall */ enum x_pnfs_layoutreturn_type { x_RETURN_FILE = 1, x_RETURN_FSID = 2, x_RETURN_ALL = 3 }; enum x_pnfs_iomode { x_IOMODE_READ = 1, x_IOMODE_RW = 2, x_IOMODE_ANY = 3, }; enum stable_nfs { x_UNSTABLE4 = 0, x_DATA_SYNC4 = 1, x_FILE_SYNC4 = 2 }; struct pnfstime4 { uint64_t seconds; uint32_t nseconds; }; struct nfsd4_pnfs_dev_iter_res { uint64_t gd_cookie; /* request/repsonse */ uint64_t gd_verf; /* request/repsonse */ uint64_t gd_devid; /* response */ uint32_t gd_eof; /* response */ }; /* Arguments for set_device_notify */ struct pnfs_devnotify_arg { struct nfsd4_pnfs_deviceid dn_devid; /* request */ uint32_t dn_layout_type; /* request */ uint32_t dn_notify_types; /* request/response */ }; struct nfsd4_layout_seg { uint64_t clientid; uint32_t layout_type; uint32_t iomode; uint64_t offset; uint64_t length; }; struct nfsd4_pnfs_layoutget_arg { uint64_t lg_minlength; uint64_t lg_sbid; struct gpfs_file_handle *lg_fh; uint32_t lg_iomode; }; struct nfsd4_pnfs_layoutget_res { struct nfsd4_layout_seg lg_seg; /* request/resopnse */ uint32_t lg_return_on_close; }; struct nfsd4_pnfs_layoutcommit_arg { struct nfsd4_layout_seg lc_seg; /* request */ uint32_t lc_reclaim; /* request */ uint32_t lc_newoffset; /* request */ uint64_t lc_last_wr; /* request */ struct pnfstime4 lc_mtime; /* request */ uint32_t lc_up_len; /* layout length */ void *lc_up_layout; /* decoded by callback */ }; struct nfsd4_pnfs_layoutcommit_res { uint32_t lc_size_chg; /* boolean for response */ uint64_t lc_newsize; /* response */ }; struct nfsd4_pnfs_layoutreturn_arg { uint32_t lr_return_type; /* request */ struct nfsd4_layout_seg lr_seg; /* request */ uint32_t lr_reclaim; /* request */ uint32_t lrf_body_len; /* request */ void *lrf_body; /* request */ void *lr_cookie; /* fs private */ }; struct x_xdr_netobj { unsigned int len; unsigned char *data; }; struct pnfs_filelayout_devaddr { struct x_xdr_netobj r_netid; struct x_xdr_netobj r_addr; }; /* list of multipath servers */ struct pnfs_filelayout_multipath { uint32_t fl_multipath_length; struct pnfs_filelayout_devaddr *fl_multipath_list; }; struct pnfs_filelayout_device { uint32_t fl_stripeindices_length; uint32_t *fl_stripeindices_list; uint32_t fl_device_length; struct pnfs_filelayout_multipath *fl_device_list; }; struct pnfs_filelayout_layout { uint32_t lg_layout_type; /* response */ uint32_t lg_stripe_type; /* response */ uint32_t lg_commit_through_mds; /* response */ uint64_t lg_stripe_unit; /* response */ uint64_t lg_pattern_offset; /* response */ uint32_t lg_first_stripe_index; /* response */ struct nfsd4_pnfs_deviceid device_id; /* response */ uint32_t lg_fh_length; /* response */ struct gpfs_file_handle *lg_fh_list; /* response */ }; enum stripetype4 { STRIPE_SPARSE = 1, STRIPE_DENSE = 2 }; struct deviceinfo_arg { int mountdirfd; int type; struct nfsd4_pnfs_deviceid devid; struct gpfs_exp_xdr_stream xdr; }; struct layoutget_arg { int fd; struct gpfs_file_handle *handle; struct nfsd4_pnfs_layoutget_arg args; struct pnfs_filelayout_layout *file_layout; struct gpfs_exp_xdr_stream *xdr; }; struct layoutreturn_arg { int mountdirfd; struct gpfs_file_handle *handle; struct nfsd4_pnfs_layoutreturn_arg args; }; struct dsread_arg { int mountdirfd; struct gpfs_file_handle *handle; char *bufP; uint64_t offset; uint64_t length; uint64_t *filesize; int options; }; /* define flags for options */ #define IO_SKIP_HOLE (1 << 0) /* 01 */ #define IO_SKIP_DATA (1 << 1) /* 02 */ #define IO_ALLOCATE (1 << 2) /* 04 */ #define IO_DEALLOCATE (1 << 3) /* 08 */ struct dswrite_arg { int mountdirfd; struct gpfs_file_handle *handle; char *bufP; uint64_t offset; uint64_t length; uint32_t stability_wanted; uint32_t *stability_got; uint32_t *verifier4; int options; }; struct read_arg { int mountdirfd; int fd; char *bufP; uint64_t offset; uint64_t length; uint32_t stability_wanted; uint32_t *stability_got; uint32_t *verifier4; int options; }; struct write_arg { int mountdirfd; int fd; char *bufP; uint64_t offset; uint64_t length; uint32_t stability_wanted; uint32_t *stability_got; uint32_t *verifier4; int options; }; struct alloc_arg { int fd; uint64_t offset; uint64_t length; int options; }; struct layoutcommit_arg { int mountdirfd; struct gpfs_file_handle *handle; uint64_t offset; uint64_t length; uint32_t reclaim; /* True if this is a reclaim commit */ uint32_t new_offset; /* True if the client has suggested a new offset */ uint64_t last_write; /* The offset of the last byte written, if new_offset if set, otherwise undefined. */ uint32_t time_changed; /* True if the client provided a new value for mtime */ struct gpfs_time_t new_time; /* If time_changed is true, the client-supplied modification tiem for the file. otherwise, undefined. */ struct gpfs_exp_xdr_stream *xdr; }; struct fsync_arg { int mountdirfd; struct gpfs_file_handle *handle; uint64_t offset; uint64_t length; uint32_t *verifier4; }; struct statfs_arg { int mountdirfd; struct gpfs_file_handle *handle; struct statfs *buf; }; struct stat_arg { int mountdirfd; struct gpfs_file_handle *handle; struct stat *buf; }; struct grace_period_arg { int mountdirfd; int grace_sec; }; struct create_name_arg { int mountdirfd; /* in */ struct gpfs_file_handle *dir_fh;/* in */ uint32_t dev; /* in dev or posix flags */ int mode; /* in */ int len; /* in */ const char *name; /* in */ struct gpfs_file_handle *new_fh;/* out */ struct stat *buf; /* in/out */ int attr_valid; /* in */ int attr_changed; /* in */ struct gpfs_acl *acl; /* in/out */ }; struct stat_name_arg { int mountdirfd; int len; const char *name; struct gpfs_file_handle *handle; struct stat *buf; }; struct callback_arg { int interface_version; int mountdirfd; int *reason; struct gpfs_file_handle *handle; struct glock *fl; int *flags; struct stat *buf; struct pnfs_deviceid *dev_id; uint32_t *expire_attr; }; #define GPFS_INTERFACE_VERSION 10000 #define GPFS_INTERFACE_SUB_VER 1 /* Defines for the flags in callback_arg, keep up to date with CXIUP_xxx */ #define UP_NLINK 0x00000001 /* update nlink */ #define UP_MODE 0x00000002 /* update mode and ctime */ #define UP_OWN 0x00000004 /* update mode,uid,gid and ctime */ #define UP_SIZE 0x00000008 /* update fsize */ #define UP_SIZE_BIG 0x00000010 /* update fsize if bigger */ #define UP_TIMES 0x00000020 /* update all times */ #define UP_ATIME 0x00000040 /* update atime only */ #define UP_PERM 0x00000080 /* update fields needed for permission checking*/ #define UP_RENAME 0x00000100 /* this is a rename op */ #define UP_DESTROY_FLAG 0x00000200 /* clear destroyIfDelInode flag */ #define UP_GANESHA 0x00000400 /* this is a ganesha op */ /* reason list for reason in callback_arg */ #define INODE_INVALIDATE 1 #define INODE_UPDATE 2 #define INODE_LOCK_GRANTED 3 #define INODE_LOCK_AGAIN 4 #define THREAD_STOP 5 #define THREAD_PAUSE 6 #define BREAK_DELEGATION 7 #define LAYOUT_FILE_RECALL 8 #define LAYOUT_RECALL_ANY 9 #define LAYOUT_NOTIFY_DEVICEID 10 /* define flags for attr_valid */ #define XATTR_STAT (1 << 0) #define XATTR_ACL (1 << 1) #define XATTR_NO_CACHE (1 << 2) #define XATTR_EXPIRE (1 << 3) #define XATTR_FSID (1 << 4) /* define flags for attr_chaged */ #define XATTR_MODE (1 << 0) // 01 #define XATTR_UID (1 << 1) // 02 #define XATTR_GID (1 << 2) // 04 #define XATTR_SIZE (1 << 3) // 08 #define XATTR_ATIME (1 << 4) // 10 #define XATTR_MTIME (1 << 5) // 20 #define XATTR_CTIME (1 << 6) // 40 #define XATTR_ATIME_SET (1 << 7) // 80 #define XATTR_MTIME_SET (1 << 8) // 100 #define XATTR_ATIME_NOW (1 << 9) // 200 #define XATTR_MTIME_NOW (1 << 10)// 400 #define XATTR_SPACE_RESERVED (1 << 11)// 800 struct fsal_fsid { uint64_t major; uint64_t minor; }; struct xstat_arg { int attr_valid; int mountdirfd; struct gpfs_file_handle *handle; struct gpfs_acl *acl; int attr_changed; struct stat *buf; struct fsal_fsid *fsid; uint32_t *expire_attr; }; struct getxattr_arg { int mountdirfd; struct gpfs_file_handle *handle; uint32_t name_len; char *name; uint32_t value_len; void *value; }; struct setxattr_arg { int mountdirfd; struct gpfs_file_handle *handle; int type; uint32_t name_len; char *name; uint32_t value_len; void *value; }; struct removexattr_arg { int mountdirfd; struct gpfs_file_handle *handle; uint32_t name_len; char *name; }; struct listxattr_arg { int mountdirfd; struct gpfs_file_handle *handle; uint64_t cookie; uint64_t verifier; uint32_t eof; uint32_t name_len; void *names; }; struct fs_loc_arg { int mountdirfd; struct gpfs_file_handle *handle; int fs_root_len; char *fs_root; int fs_path_len; char *fs_path; int fs_server_len; char *fs_server; }; struct xstat_access_arg { int mountdirfd; struct gpfs_file_handle *handle; struct gpfs_acl *acl; struct xstat_cred_t *cred; unsigned int posix_mode; unsigned int access; /* v4maske */ unsigned int *supported; }; struct quotactl_arg { const char *pathname; int cmd; int qid; void *bufferP; }; #ifdef __cplusplus } #endif extern struct fsal_stats gpfs_stats; #endif /* H_GPFS_NFS */ nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/main.c000066400000000000000000000174131324272410200176640ustar00rootroot00000000000000/** @file main.c * @brief GPFS FSAL module core functions * * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ #include #include "fsal.h" #include "fsal_internal.h" #include "FSAL/fsal_init.h" #include "gpfs_methods.h" #include "gsh_config.h" static const char myname[] = "GPFS"; /** @struct gpfs_fsal_module * @brief GPFS FSAL module private storage */ struct gpfs_fsal_module { struct fsal_module fsal; struct fsal_staticfsinfo_t fs_info; /** gpfsfs_specific_initinfo_t specific_info; placeholder */ }; /** @struct default_gpfs_info * @brief filesystem info for GPFS */ static struct fsal_staticfsinfo_t default_gpfs_info = { .maxfilesize = UINT64_MAX, .maxlink = _POSIX_LINK_MAX, .maxnamelen = 1024, .maxpathlen = 1024, .no_trunc = true, .chown_restricted = false, .case_insensitive = false, .case_preserving = true, .link_support = true, .symlink_support = true, .lock_support = true, .lock_support_async_block = true, .named_attr = true, .unique_handles = true, .lease_time = {10, 0}, .acl_support = FSAL_ACLSUPPORT_ALLOW | FSAL_ACLSUPPORT_DENY, .cansettime = true, .homogenous = true, .supported_attrs = GPFS_SUPPORTED_ATTRIBUTES, .maxread = FSAL_MAXIOSIZE, .maxwrite = FSAL_MAXIOSIZE, .umask = 0, .auth_exportpath_xdev = true, .xattr_access_rights = 0, /* @todo Update lease handling to use new interfaces */ #if 0 .delegations = FSAL_OPTION_FILE_READ_DELEG, /** not working with pNFS */ #endif .pnfs_mds = true, .pnfs_ds = true, .fsal_trace = true, .fsal_grace = false, .link_supports_permission_checks = true, }; /** @struct gpfs_params * @brief Configuration items */ static struct config_item gpfs_params[] = { CONF_ITEM_BOOL("link_support", true, fsal_staticfsinfo_t, link_support), CONF_ITEM_BOOL("symlink_support", true, fsal_staticfsinfo_t, symlink_support), CONF_ITEM_BOOL("cansettime", true, fsal_staticfsinfo_t, cansettime), CONF_ITEM_MODE("umask", 0, fsal_staticfsinfo_t, umask), CONF_ITEM_BOOL("auth_xdev_export", false, fsal_staticfsinfo_t, auth_exportpath_xdev), CONF_ITEM_MODE("xattr_access_rights", 0400, fsal_staticfsinfo_t, xattr_access_rights), /** At the moment GPFS doesn't support WRITE delegations */ CONF_ITEM_ENUM_BITS("Delegations", FSAL_OPTION_FILE_READ_DELEG, FSAL_OPTION_FILE_DELEGATIONS, deleg_types, fsal_staticfsinfo_t, delegations), CONF_ITEM_BOOL("PNFS_MDS", true, fsal_staticfsinfo_t, pnfs_mds), CONF_ITEM_BOOL("PNFS_DS", true, fsal_staticfsinfo_t, pnfs_ds), CONF_ITEM_BOOL("fsal_trace", true, fsal_staticfsinfo_t, fsal_trace), CONF_ITEM_BOOL("fsal_grace", false, fsal_staticfsinfo_t, fsal_grace), CONFIG_EOL }; /** @struct gpfs_param * @brief Configuration block */ static struct config_block gpfs_param = { .dbus_interface_name = "org.ganesha.nfsd.config.fsal.gpfs", .blk_desc.name = "GPFS", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = noop_conf_init, .blk_desc.u.blk.params = gpfs_params, .blk_desc.u.blk.commit = noop_conf_commit }; /** @struct GPFS * @brief my module private storage */ static struct gpfs_fsal_module GPFS; /** @fn struct fsal_staticfsinfo_t *gpfs_staticinfo(struct fsal_module *hdl) * @brief private helper for export object * @param hdl handle to fsal_module */ struct fsal_staticfsinfo_t *gpfs_staticinfo(struct fsal_module *hdl) { struct gpfs_fsal_module *gpfs_me = container_of(hdl, struct gpfs_fsal_module, fsal); return &gpfs_me->fs_info; } /** @fn static int * log_to_gpfs(log_header_t headers, void *private, log_levels_t level, * struct display_buffer *buffer, char *compstr, char *message) * @brief Log to gpfs */ static int log_to_gpfs(log_header_t headers, void *private, log_levels_t level, struct display_buffer *buffer, char *compstr, char *message) { struct trace_arg targ = {0}; if (level <= 0) return 0; targ.level = level; targ.len = strlen(compstr); targ.str = compstr; return gpfs_ganesha(OPENHANDLE_TRACE_ME, &targ); } /** @fn static fsal_status_t init_config(struct fsal_module *fsal_hdl, * config_file_t config_struct, struct config_error_type *err_type) * @brief must be called with a reference taken (via lookup_fsal) */ static fsal_status_t init_config(struct fsal_module *fsal_hdl, config_file_t config_struct, struct config_error_type *err_type) { struct gpfs_fsal_module *gpfs_me = container_of(fsal_hdl, struct gpfs_fsal_module, fsal); int rc; (void) prepare_for_stats(fsal_hdl); gpfs_me->fs_info = default_gpfs_info; /** get a copy of the defaults */ (void) load_config_from_parse(config_struct, &gpfs_param, &gpfs_me->fs_info, true, err_type); if (!config_error_is_harmless(err_type)) return fsalstat(ERR_FSAL_INVAL, 0); display_fsinfo(&gpfs_me->fs_info); LogFullDebug(COMPONENT_FSAL, "Supported attributes constant = 0x%" PRIx64, GPFS_SUPPORTED_ATTRIBUTES); LogFullDebug(COMPONENT_FSAL, "Supported attributes default = 0x%" PRIx64, default_gpfs_info.supported_attrs); LogDebug(COMPONENT_FSAL, "FSAL INIT: Supported attributes mask = 0x%" PRIx64, gpfs_me->fs_info.supported_attrs); rc = create_log_facility(myname, log_to_gpfs, NIV_FULL_DEBUG, LH_COMPONENT, NULL); if (rc != 0) { LogCrit(COMPONENT_FSAL, "Could not create GPFS logger (%s)", strerror(-rc)); return fsalstat(ERR_FSAL_INVAL, 0); } if (gpfs_me->fs_info.fsal_trace) { rc = enable_log_facility(myname); if (rc == 0) return fsalstat(ERR_FSAL_NO_ERROR, 0); LogCrit(COMPONENT_FSAL, "Could not enable GPFS logger (%s)", strerror(-rc)); return fsalstat(ERR_FSAL_INVAL, 0); } rc = disable_log_facility(myname); if (rc == 0) return fsalstat(ERR_FSAL_NO_ERROR, 0); LogCrit(COMPONENT_FSAL, "Could not disable GPFS logger (%s)", strerror(-rc)); return fsalstat(ERR_FSAL_INVAL, 0); } /** @fn MODULE_INIT void gpfs_init(void) * @brief Module initialization. * * Called by dlopen() to register the module * keep a private pointer to me in myself */ MODULE_INIT void gpfs_init(void) { struct fsal_module *myself = &GPFS.fsal; if (register_fsal(myself, myname, FSAL_MAJOR_VERSION, FSAL_MINOR_VERSION, FSAL_ID_GPFS) != 0) { fprintf(stderr, "GPFS module failed to register"); return; } /** Set up module operations */ myself->m_ops.fsal_pnfs_ds_ops = pnfs_ds_ops_init; myself->m_ops.create_export = gpfs_create_export; myself->m_ops.init_config = init_config; myself->m_ops.getdeviceinfo = getdeviceinfo; myself->m_ops.fs_da_addr_size = fs_da_addr_size; #ifdef USE_DBUS myself->m_ops.fsal_extract_stats = fsal_gpfs_extract_stats; #endif myself->m_ops.fsal_reset_stats = fsal_gpfs_reset_stats; } /** @fn MODULE_FINI void gpfs_unload(void) * @brief unload module */ MODULE_FINI void gpfs_unload(void) { release_log_facility(myname); if (unregister_fsal(&GPFS.fsal) != 0) fprintf(stderr, "GPFS module failed to unregister"); } nfs-ganesha-2.6.0/src/FSAL/FSAL_GPFS/maketest.conf000066400000000000000000000011611324272410200212510ustar00rootroot00000000000000 Test Find { Product = FSAL POSIX. Command = ../scripts/non_reg_fsal_posix/compare_find_posix_posixdb.bash /etc Comment = FSAL posixdb find & unix find comparison. # test if we executed all the commands (10) # and the program ended well. Success TestOk { STDOUT =~ /OK/m AND STATUS == 0 } # error Failure TestBAD { STDOUT =~ /BAD/m AND STATUS != 0 } # fsal_posix_tool failure Failure TestFailure { STDERR =~ /Error/m } } nfs-ganesha-2.6.0/src/FSAL/FSAL_MEM/000077500000000000000000000000001324272410200164455ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/FSAL/FSAL_MEM/CMakeLists.txt000066400000000000000000000010271324272410200212050ustar00rootroot00000000000000add_definitions( -D__USE_GNU -D_GNU_SOURCE ) set( LIB_PREFIX 64) ########### next target ############### SET(fsalmem_LIB_SRCS mem_export.c mem_handle.c mem_int.h mem_main.c mem_up.c ) add_library(fsalmem SHARED ${fsalmem_LIB_SRCS}) add_sanitizers(fsalmem) target_link_libraries(fsalmem ${SYSTEM_LIBRARIES} ${LTTNG_LIBRARIES}) set_target_properties(fsalmem PROPERTIES VERSION 4.2.0 SOVERSION 4) install(TARGETS fsalmem COMPONENT fsal DESTINATION ${FSAL_DESTINATION} ) ########### install files ############### nfs-ganesha-2.6.0/src/FSAL/FSAL_MEM/mem_export.c000066400000000000000000000221731324272410200207750ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright 2017-2018 Red Hat, Inc. * Author: Daniel Gryniewicz dang@redhat.com * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* export.c * MEM FSAL export object */ #include "config.h" #include "fsal.h" #include /* used for 'dirname' */ #include #include #include #include #include #include #include "fsal_convert.h" #include "FSAL/fsal_commonlib.h" #include "FSAL/fsal_config.h" #include "mem_int.h" #include "nfs_exports.h" #include "nfs_core.h" #include "export_mgr.h" #ifdef __FreeBSD__ #include #define bswap_16(x) bswap16((x)) #define bswap_64(x) bswap64((x)) #endif #ifdef USE_LTTNG #include "gsh_lttng/fsal_mem.h" #endif /* helpers to/from other MEM objects */ struct fsal_staticfsinfo_t *mem_staticinfo(struct fsal_module *hdl); /* export object methods */ static void mem_release_export(struct fsal_export *exp_hdl) { struct mem_fsal_export *myself; myself = container_of(exp_hdl, struct mem_fsal_export, export); if (myself->root_handle != NULL) { mem_clean_export(myself->root_handle); fsal_obj_handle_fini(&myself->root_handle->obj_handle); LogDebug(COMPONENT_FSAL, "Releasing hdl=%p, name=%s", myself->root_handle, myself->root_handle->m_name); PTHREAD_RWLOCK_wrlock(&myself->mfe_exp_lock); mem_free_handle(myself->root_handle); PTHREAD_RWLOCK_unlock(&myself->mfe_exp_lock); myself->root_handle = NULL; } fsal_detach_export(exp_hdl->fsal, &exp_hdl->exports); free_export_ops(exp_hdl); glist_del(&myself->export_entry); gsh_free(myself->export_path); gsh_free(myself); } static fsal_status_t mem_get_dynamic_info(struct fsal_export *exp_hdl, struct fsal_obj_handle *obj_hdl, fsal_dynamicfsinfo_t *infop) { infop->total_bytes = 0; infop->free_bytes = 0; infop->avail_bytes = 0; infop->total_files = 0; infop->free_files = 0; infop->avail_files = 0; infop->time_delta.tv_sec = 1; infop->time_delta.tv_nsec = 0; return fsalstat(ERR_FSAL_NO_ERROR, 0); } static bool mem_fs_supports(struct fsal_export *exp_hdl, fsal_fsinfo_options_t option) { struct fsal_staticfsinfo_t *info; info = mem_staticinfo(exp_hdl->fsal); return fsal_supports(info, option); } static uint64_t mem_fs_maxfilesize(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = mem_staticinfo(exp_hdl->fsal); return fsal_maxfilesize(info); } static uint32_t mem_fs_maxread(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = mem_staticinfo(exp_hdl->fsal); return fsal_maxread(info); } static uint32_t mem_fs_maxwrite(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = mem_staticinfo(exp_hdl->fsal); return fsal_maxwrite(info); } static uint32_t mem_fs_maxlink(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = mem_staticinfo(exp_hdl->fsal); return fsal_maxlink(info); } static uint32_t mem_fs_maxnamelen(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = mem_staticinfo(exp_hdl->fsal); return fsal_maxnamelen(info); } static uint32_t mem_fs_maxpathlen(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = mem_staticinfo(exp_hdl->fsal); return fsal_maxpathlen(info); } static struct timespec mem_fs_lease_time(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = mem_staticinfo(exp_hdl->fsal); return fsal_lease_time(info); } static fsal_aclsupp_t mem_fs_acl_support(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = mem_staticinfo(exp_hdl->fsal); return fsal_acl_support(info); } static attrmask_t mem_fs_supported_attrs(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = mem_staticinfo(exp_hdl->fsal); return fsal_supported_attrs(info); } static uint32_t mem_fs_umask(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = mem_staticinfo(exp_hdl->fsal); return fsal_umask(info); } static uint32_t mem_fs_xattr_access_rights(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = mem_staticinfo(exp_hdl->fsal); return fsal_xattr_access_rights(info); } /* extract a file handle from a buffer. * do verification checks and flag any and all suspicious bits. * Return an updated fh_desc into whatever was passed. The most * common behavior, done here is to just reset the length. There * is the option to also adjust the start pointer. */ static fsal_status_t mem_wire_to_host(struct fsal_export *exp_hdl, fsal_digesttype_t in_type, struct gsh_buffdesc *fh_desc, int flags) { size_t fh_min; uint64_t *hashkey; ushort *len; fh_min = 1; if (fh_desc->len < fh_min) { LogMajor(COMPONENT_FSAL, "Size mismatch for handle. should be >= %zu, got %zu", fh_min, fh_desc->len); return fsalstat(ERR_FSAL_SERVERFAULT, 0); } hashkey = (uint64_t *)fh_desc->addr; len = (ushort *)((char *)hashkey + sizeof(uint64_t)); if (flags & FH_FSAL_BIG_ENDIAN) { #if (BYTE_ORDER != BIG_ENDIAN) *len = bswap_16(*len); *hashkey = bswap_64(*hashkey); #endif } else { #if (BYTE_ORDER == BIG_ENDIAN) *len = bswap_16(*len); *hashkey = bswap_64(*hashkey); #endif } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Allocate a state_t structure * * Note that this is not expected to fail since memory allocation is * expected to abort on failure. * * @param[in] exp_hdl Export state_t will be associated with * @param[in] state_type Type of state to allocate * @param[in] related_state Related state if appropriate * * @returns a state structure. */ static struct state_t *mem_alloc_state(struct fsal_export *exp_hdl, enum state_type state_type, struct state_t *related_state) { struct state_t *state; state = init_state(gsh_calloc(1, sizeof(struct state_t) + sizeof(struct fsal_fd)), exp_hdl, state_type, related_state); #ifdef USE_LTTNG tracepoint(fsalmem, mem_alloc_state, __func__, __LINE__, state); #endif return state; } /* mem_export_ops_init * overwrite vector entries with the methods that we support */ void mem_export_ops_init(struct export_ops *ops) { ops->release = mem_release_export; ops->lookup_path = mem_lookup_path; ops->wire_to_host = mem_wire_to_host; ops->create_handle = mem_create_handle; ops->get_fs_dynamic_info = mem_get_dynamic_info; ops->fs_supports = mem_fs_supports; ops->fs_maxfilesize = mem_fs_maxfilesize; ops->fs_maxread = mem_fs_maxread; ops->fs_maxwrite = mem_fs_maxwrite; ops->fs_maxlink = mem_fs_maxlink; ops->fs_maxnamelen = mem_fs_maxnamelen; ops->fs_maxpathlen = mem_fs_maxpathlen; ops->fs_lease_time = mem_fs_lease_time; ops->fs_acl_support = mem_fs_acl_support; ops->fs_supported_attrs = mem_fs_supported_attrs; ops->fs_umask = mem_fs_umask; ops->fs_xattr_access_rights = mem_fs_xattr_access_rights; ops->alloc_state = mem_alloc_state; } /* create_export * Create an export point and return a handle to it to be kept * in the export list. * First lookup the fsal, then create the export and then put the fsal back. * returns the export with one reference taken. */ fsal_status_t mem_create_export(struct fsal_module *fsal_hdl, void *parse_node, struct config_error_type *err_type, const struct fsal_up_vector *up_ops) { struct mem_fsal_export *myself; int retval = 0; pthread_rwlockattr_t attrs; myself = gsh_calloc(1, sizeof(struct mem_fsal_export)); glist_init(&myself->mfe_objs); pthread_rwlockattr_init(&attrs); #ifdef GLIBC pthread_rwlockattr_setkind_np(&attrs, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP); #endif PTHREAD_RWLOCK_init(&myself->mfe_exp_lock, &attrs); fsal_export_init(&myself->export); mem_export_ops_init(&myself->export.exp_ops); retval = fsal_attach_export(fsal_hdl, &myself->export.exports); if (retval != 0) { /* seriously bad */ LogMajor(COMPONENT_FSAL, "Could not attach export"); free_export_ops(&myself->export); gsh_free(myself); /* elvis has left the building */ return fsalstat(posix2fsal_error(retval), retval); } myself->export.fsal = fsal_hdl; myself->export.up_ops = up_ops; /* Save the export path. */ myself->export_path = gsh_strdup(op_ctx->ctx_export->fullpath); op_ctx->fsal_export = &myself->export; /* Insert into exports list */ glist_add_tail(&MEM.mem_exports, &myself->export_entry); LogDebug(COMPONENT_FSAL, "Created exp %p - %s", myself, myself->export_path); return fsalstat(ERR_FSAL_NO_ERROR, 0); } nfs-ganesha-2.6.0/src/FSAL/FSAL_MEM/mem_handle.c000066400000000000000000001636551324272410200207220ustar00rootroot00000000000000/* * vim:shiftwidth=8:tabstop=8: * * Copyright 2017-2018 Red Hat, Inc. * Author: Daniel Gryniewicz dang@redhat.com * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* handle.c */ #include "config.h" #include "fsal.h" #include "fsal_convert.h" #include "FSAL/fsal_commonlib.h" #include "mem_int.h" #include "city.h" #include "nfs_file_handle.h" #include "display.h" #ifdef USE_LTTNG #include "gsh_lttng/fsal_mem.h" #endif static void mem_release(struct fsal_obj_handle *obj_hdl); /* Atomic uint64_t that is used to generate inode numbers in the mem FS */ uint64_t mem_inode_number = 1; /* helpers */ static inline int mem_n_cmpf(const struct avltree_node *lhs, const struct avltree_node *rhs) { struct mem_dirent *lk, *rk; lk = avltree_container_of(lhs, struct mem_dirent, avl_n); rk = avltree_container_of(rhs, struct mem_dirent, avl_n); return strcmp(lk->d_name, rk->d_name); } static inline int mem_i_cmpf(const struct avltree_node *lhs, const struct avltree_node *rhs) { struct mem_dirent *lk, *rk; lk = avltree_container_of(lhs, struct mem_dirent, avl_i); rk = avltree_container_of(rhs, struct mem_dirent, avl_i); if (lk->d_index < rk->d_index) return -1; if (lk->d_index == rk->d_index) return 0; return 1; } /** * @brief Construct the fs opaque part of a mem nfsv4 handle * * Given the components of a mem nfsv4 handle, the nfsv4 handle is * created by concatenating the components. This is the fs opaque piece * of struct file_handle_v4 and what is sent over the wire. * * @param[in] myself Obj to create handle for * * @return The nfsv4 mem file handle as a char * */ static void package_mem_handle(struct mem_fsal_obj_handle *myself) { char buf[MAXPATHLEN]; uint16_t len; uint64_t hashkey; int opaque_bytes_used = 0, pathlen = 0; memset(buf, 0, sizeof(buf)); /* Make hashkey */ len = sizeof(myself->obj_handle.fileid); memcpy(buf, &myself->obj_handle.fileid, len); strncpy(buf + len, myself->m_name, sizeof(buf) - len); hashkey = CityHash64(buf, sizeof(buf)); memcpy(myself->handle, &hashkey, sizeof(hashkey)); opaque_bytes_used += sizeof(hashkey); /* include length of the name in the handle. * MAXPATHLEN=4096 ... max path length can be contained in a short int. */ len = strlen(myself->m_name); memcpy(myself->handle + opaque_bytes_used, &len, sizeof(len)); opaque_bytes_used += sizeof(len); /* Either the nfsv4 fh opaque size or the length of the name. * Ideally we can include entire mem name for guaranteed * uniqueness of mem handles. */ pathlen = MIN(V4_FH_OPAQUE_SIZE - opaque_bytes_used, len); memcpy(myself->handle + opaque_bytes_used, myself->m_name, pathlen); opaque_bytes_used += pathlen; /* If there is more space in the opaque handle due to a short mem * path ... zero it. */ if (opaque_bytes_used < V4_FH_OPAQUE_SIZE) { memset(myself->handle + opaque_bytes_used, 0, V4_FH_OPAQUE_SIZE - opaque_bytes_used); } } /** * @brief Insert an obj into it's parent's tree * * @param[in] parent Parent directory * @param[in] child Child to insert. * @param[in] name Name to use for insertion */ static void mem_insert_obj(struct mem_fsal_obj_handle *parent, struct mem_fsal_obj_handle *child, const char *name) { struct mem_dirent *dirent; uint32_t numkids; dirent = gsh_calloc(1, sizeof(*dirent)); dirent->hdl = child; dirent->dir = parent; dirent->d_name = gsh_strdup(name); /* Link into child */ PTHREAD_RWLOCK_wrlock(&child->obj_handle.obj_lock); glist_add_tail(&child->dirents, &dirent->dlist); PTHREAD_RWLOCK_unlock(&child->obj_handle.obj_lock); /* Link into parent */ PTHREAD_RWLOCK_wrlock(&parent->obj_handle.obj_lock); /* Name tree */ avltree_insert(&dirent->avl_n, &parent->mh_dir.avl_name); /* Index tree (increment under lock) */ dirent->d_index = (parent->mh_dir.next_i)++; avltree_insert(&dirent->avl_i, &parent->mh_dir.avl_index); /* Update numkids */ numkids = atomic_inc_uint32_t(&parent->mh_dir.numkids); LogFullDebug(COMPONENT_FSAL, "%s numkids %"PRIu32, parent->m_name, numkids); PTHREAD_RWLOCK_unlock(&parent->obj_handle.obj_lock); } /** * @brief Find the dirent pointing to a name in a directory * * @param[in] dir Directory to search * @param[in] name Name to look up * @return Dirent on success, NULL on failure */ struct mem_dirent * mem_dirent_lookup(struct mem_fsal_obj_handle *dir, const char *name) { struct mem_dirent key; struct avltree_node *node; key.d_name = name; node = avltree_lookup(&key.avl_n, &dir->mh_dir.avl_name); if (!node) { /* it's not there */ return NULL; } return avltree_container_of(node, struct mem_dirent, avl_n); } /** * @brief Remove an obj from it's parent's tree * * @note Caller must hold the obj_lock on the parent * * @param[in] parent Parent directory * @param[in] dirent Dirent to remove * @param[in] release If true and no more dirents, release child */ static void mem_remove_dirent_locked(struct mem_fsal_obj_handle *parent, struct mem_dirent *dirent, bool release) { struct mem_fsal_obj_handle *child; uint32_t numkids; bool empty; avltree_remove(&dirent->avl_n, &parent->mh_dir.avl_name); avltree_remove(&dirent->avl_i, &parent->mh_dir.avl_index); /* Take the child lock, to remove from the child. This should not race * with @r mem_insert_obj since that takes the locks seqentially */ child = dirent->hdl; PTHREAD_RWLOCK_wrlock(&child->obj_handle.obj_lock); glist_del(&dirent->dlist); empty = glist_empty(&child->dirents); PTHREAD_RWLOCK_unlock(&child->obj_handle.obj_lock); numkids = atomic_dec_uint32_t(&parent->mh_dir.numkids); LogFullDebug(COMPONENT_FSAL, "%s numkids %"PRIu32, parent->m_name, numkids); /* Free dirent */ gsh_free((char *)dirent->d_name); gsh_free(dirent); if (release && empty) mem_release(&child->obj_handle); } /** * @brief Remove a dirent from it's parent's tree * * @param[in] parent Parent directory * @param[in] name Name to remove */ static void mem_remove_dirent(struct mem_fsal_obj_handle *parent, const char *name) { struct mem_dirent *dirent; PTHREAD_RWLOCK_wrlock(&parent->obj_handle.obj_lock); dirent = mem_dirent_lookup(parent, name); if (dirent) mem_remove_dirent_locked(parent, dirent, false); PTHREAD_RWLOCK_unlock(&parent->obj_handle.obj_lock); } /** * @brief Recursively clean all objs/dirents on an export * * @note Caller MUST hold export lock for write * * @param[in] root Root to clean * @return Return description */ void mem_clean_export(struct mem_fsal_obj_handle *root) { struct mem_fsal_obj_handle *child; struct avltree_node *node; struct mem_dirent *dirent; #ifdef USE_LTTNG tracepoint(fsalmem, mem_inuse, __func__, __LINE__, root, root->attrs.numlinks, root->is_export); #endif while ((node = avltree_first(&root->mh_dir.avl_name))) { dirent = avltree_container_of(node, struct mem_dirent, avl_n); child = dirent->hdl; if (child->obj_handle.type == DIRECTORY) { mem_clean_export(child); } PTHREAD_RWLOCK_wrlock(&root->obj_handle.obj_lock); mem_remove_dirent_locked(root, dirent, true); PTHREAD_RWLOCK_unlock(&root->obj_handle.obj_lock); } } /** * @brief Remove all children from a directory's tree * * @param[in] parent Directroy to clean */ void mem_clean_all_dirents(struct mem_fsal_obj_handle *parent) { struct avltree_node *node; struct mem_dirent *dirent; PTHREAD_RWLOCK_wrlock(&parent->obj_handle.obj_lock); while ((node = avltree_first(&parent->mh_dir.avl_name))) { dirent = avltree_container_of(node, struct mem_dirent, avl_n); mem_remove_dirent_locked(parent, dirent, true); } PTHREAD_RWLOCK_unlock(&parent->obj_handle.obj_lock); } static void mem_copy_attrs_mask(struct attrlist *attrs_in, struct attrlist *attrs_out) { /* Use full timer resolution */ now(&attrs_out->ctime); if (FSAL_TEST_MASK(attrs_in->valid_mask, ATTR_SIZE)) { attrs_out->filesize = attrs_in->filesize; } if (FSAL_TEST_MASK(attrs_in->valid_mask, ATTR_MODE)) { attrs_out->mode = attrs_in->mode & (~S_IFMT & 0xFFFF) & ~op_ctx->fsal_export->exp_ops.fs_umask( op_ctx->fsal_export); } if (FSAL_TEST_MASK(attrs_in->valid_mask, ATTR_OWNER)) { attrs_out->owner = attrs_in->owner; } if (FSAL_TEST_MASK(attrs_in->valid_mask, ATTR_GROUP)) { attrs_out->group = attrs_in->group; } if (FSAL_TEST_MASK(attrs_in->valid_mask, ATTRS_SET_TIME)) { if (FSAL_TEST_MASK(attrs_in->valid_mask, ATTR_ATIME_SERVER)) { attrs_out->atime.tv_sec = 0; attrs_out->atime.tv_nsec = UTIME_NOW; } else if (FSAL_TEST_MASK(attrs_in->valid_mask, ATTR_ATIME)) { attrs_out->atime = attrs_in->atime; } else { attrs_out->mtime = attrs_out->ctime; } if (FSAL_TEST_MASK(attrs_in->valid_mask, ATTR_MTIME_SERVER)) { attrs_out->mtime.tv_sec = 0; attrs_out->mtime.tv_nsec = UTIME_NOW; } else if (FSAL_TEST_MASK(attrs_in->valid_mask, ATTR_MTIME)) { attrs_out->mtime = attrs_in->mtime; } else { attrs_out->mtime = attrs_out->ctime; } } if (FSAL_TEST_MASK(attrs_in->valid_mask, ATTR_CREATION)) { attrs_out->creation = attrs_in->creation; } if (FSAL_TEST_MASK(attrs_in->valid_mask, ATTR_SPACEUSED)) { attrs_out->spaceused = attrs_in->spaceused; } else { attrs_out->spaceused = attrs_out->filesize; } /* XXX TODO copy ACL */ attrs_out->chgtime = attrs_out->ctime; attrs_out->change = timespec_to_nsecs(&attrs_out->chgtime); } /** * @brief Open an FD * * @param[in] fd FD to close * @return FSAL status */ static fsal_status_t mem_open_my_fd(struct fsal_fd *fd, fsal_openflags_t openflags) { fd->openflags = openflags; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Close an FD * * @param[in] fd FD to close * @return FSAL status */ static fsal_status_t mem_close_my_fd(struct fsal_fd *fd) { if (fd->openflags == FSAL_O_CLOSED) return fsalstat(ERR_FSAL_NOT_OPENED, 0); fd->openflags = FSAL_O_CLOSED; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Function to open an fsal_obj_handle's global file descriptor. * * @param[in] obj_hdl File on which to operate * @param[in] openflags Mode for open * @param[out] fd File descriptor that is to be used * * @return FSAL status. */ static fsal_status_t mem_open_func(struct fsal_obj_handle *obj_hdl, fsal_openflags_t openflags, struct fsal_fd *fd) { return mem_open_my_fd(fd, openflags); } /** * @brief Close a global FD * * @param[in] obj_hdl Object owning FD to close * @param[in] fd FD to close * @return FSAL status */ static fsal_status_t mem_close_func(struct fsal_obj_handle *obj_hdl, struct fsal_fd *fd) { return mem_close_my_fd(fd); } #define mem_alloc_handle(p, n, t, e, a) \ _mem_alloc_handle(p, n, t, e, a, __func__, __LINE__) /** * @brief Allocate a MEM handle * * @param[in] parent Parent directory handle * @param[in] name Name of handle to allocate * @param[in] type Type of handle to allocate * @param[in] mfe MEM Export owning new handle * @param[in] attrs Attributes of new handle * @return Handle on success, NULL on failure */ static struct mem_fsal_obj_handle * _mem_alloc_handle(struct mem_fsal_obj_handle *parent, const char *name, object_file_type_t type, struct mem_fsal_export *mfe, struct attrlist *attrs, const char *func, int line) { struct mem_fsal_obj_handle *hdl; size_t isize; isize = sizeof(struct mem_fsal_obj_handle); if (type == REGULAR_FILE) { /* Regular files need space to read/write */ isize += MEM.inode_size; } hdl = gsh_calloc(1, isize); /* Establish tree details for this directory */ hdl->m_name = gsh_strdup(name); hdl->obj_handle.fileid = atomic_postinc_uint64_t(&mem_inode_number); hdl->datasize = MEM.inode_size; glist_init(&hdl->dirents); PTHREAD_RWLOCK_wrlock(&mfe->mfe_exp_lock); glist_add_tail(&mfe->mfe_objs, &hdl->mfo_exp_entry); hdl->mfo_exp = mfe; PTHREAD_RWLOCK_unlock(&mfe->mfe_exp_lock); package_mem_handle(hdl); /* Fills the output struct */ hdl->obj_handle.type = type; hdl->attrs.type = hdl->obj_handle.type; /* Need an FSID */ hdl->obj_handle.fsid.major = op_ctx->ctx_export->export_id; hdl->obj_handle.fsid.minor = 0; hdl->attrs.fsid.major = hdl->obj_handle.fsid.major; hdl->attrs.fsid.minor = hdl->obj_handle.fsid.minor; hdl->attrs.fileid = hdl->obj_handle.fileid; if ((attrs && attrs->valid_mask & ATTR_MODE) != 0) hdl->attrs.mode = attrs->mode & (~S_IFMT & 0xFFFF) & ~op_ctx->fsal_export->exp_ops.fs_umask( op_ctx->fsal_export); else hdl->attrs.mode = 0600; if ((attrs && attrs->valid_mask & ATTR_OWNER) != 0) hdl->attrs.owner = attrs->owner; else hdl->attrs.owner = op_ctx->creds->caller_uid; if ((attrs && attrs->valid_mask & ATTR_GROUP) != 0) hdl->attrs.group = attrs->group; else hdl->attrs.group = op_ctx->creds->caller_gid; /* Use full timer resolution */ now(&hdl->attrs.ctime); hdl->attrs.chgtime = hdl->attrs.ctime; if ((attrs && attrs->valid_mask & ATTR_ATIME) != 0) hdl->attrs.atime = attrs->atime; else hdl->attrs.atime = hdl->attrs.ctime; if ((attrs && attrs->valid_mask & ATTR_MTIME) != 0) hdl->attrs.mtime = attrs->mtime; else hdl->attrs.mtime = hdl->attrs.ctime; hdl->attrs.change = timespec_to_nsecs(&hdl->attrs.chgtime); switch (type) { case REGULAR_FILE: if ((attrs && attrs->valid_mask & ATTR_SIZE) != 0) { hdl->attrs.filesize = attrs->filesize; hdl->attrs.spaceused = attrs->filesize; } else { hdl->attrs.filesize = 0; hdl->attrs.spaceused = 0; } hdl->attrs.numlinks = 1; break; case BLOCK_FILE: case CHARACTER_FILE: if ((attrs && attrs->valid_mask & ATTR_RAWDEV) != 0) { hdl->attrs.rawdev.major = attrs->rawdev.major; hdl->attrs.rawdev.minor = attrs->rawdev.minor; } else { hdl->attrs.rawdev.major = 0; hdl->attrs.rawdev.minor = 0; } hdl->attrs.numlinks = 1; break; case DIRECTORY: avltree_init(&hdl->mh_dir.avl_name, mem_n_cmpf, 0); avltree_init(&hdl->mh_dir.avl_index, mem_i_cmpf, 0); hdl->mh_dir.next_i = 2; hdl->attrs.numlinks = 2; hdl->mh_dir.numkids = 2; break; default: hdl->attrs.numlinks = 1; break; } /* Set the mask at the end. */ hdl->attrs.valid_mask = ATTRS_POSIX; hdl->attrs.supported = ATTRS_POSIX; fsal_obj_handle_init(&hdl->obj_handle, &mfe->export, type); mem_handle_ops_init(&hdl->obj_handle.obj_ops); if (parent != NULL) { /* Attach myself to my parent */ mem_insert_obj(parent, hdl, name); } else { /* This is an export */ hdl->is_export = true; } #ifdef USE_LTTNG tracepoint(fsalmem, mem_alloc, func, line, hdl, name); #endif return hdl; } #define mem_int_lookup(d, p, e) _mem_int_lookup(d, p, e, __func__, __LINE__) static fsal_status_t _mem_int_lookup(struct mem_fsal_obj_handle *dir, const char *path, struct mem_fsal_obj_handle **entry, const char *func, int line) { struct mem_dirent *dirent; *entry = NULL; LogFullDebug(COMPONENT_FSAL, "Lookup %s in %p", path, dir); #ifdef USE_LTTNG tracepoint(fsalmem, mem_lookup, func, line, dir, path); #endif if (strcmp(path, "..") == 0) { /* lookup parent - lookupp */ if (dir->mh_dir.parent == NULL) { return fsalstat(ERR_FSAL_NOENT, 0); } *entry = dir->mh_dir.parent; LogFullDebug(COMPONENT_FSAL, "Found %s/%s hdl=%p", dir->m_name, path, *entry); return fsalstat(ERR_FSAL_NO_ERROR, 0); } else if (strcmp(path, ".") == 0) { *entry = dir; return fsalstat(ERR_FSAL_NO_ERROR, 0); } dirent = mem_dirent_lookup(dir, path); if (!dirent) { return fsalstat(ERR_FSAL_NOENT, 0); } *entry = dirent->hdl; #ifdef USE_LTTNG tracepoint(fsalmem, mem_lookup, func, line, *entry, (*entry)->m_name); #endif return fsalstat(ERR_FSAL_NO_ERROR, 0); } static fsal_status_t mem_create_obj(struct mem_fsal_obj_handle *parent, object_file_type_t type, const char *name, struct attrlist *attrs_in, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out) { struct mem_fsal_export *mfe = container_of(op_ctx->fsal_export, struct mem_fsal_export, export); struct mem_fsal_obj_handle *hdl; fsal_status_t status; *new_obj = NULL; /* poison it */ if (parent->obj_handle.type != DIRECTORY) { LogCrit(COMPONENT_FSAL, "Parent handle is not a directory. hdl = 0x%p", parent); return fsalstat(ERR_FSAL_NOTDIR, 0); } status = mem_int_lookup(parent, name, &hdl); if (!FSAL_IS_ERROR(status)) { /* It already exists */ return fsalstat(ERR_FSAL_EXIST, 0); } else if (status.major != ERR_FSAL_NOENT) { /* Some other error */ return status; } /* allocate an obj_handle and fill it up */ hdl = mem_alloc_handle(parent, name, type, mfe, attrs_in); if (!hdl) return fsalstat(ERR_FSAL_NOMEM, 0); *new_obj = &hdl->obj_handle; if (attrs_out != NULL) fsal_copy_attrs(attrs_out, &hdl->attrs, false); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* handle methods */ /** * @brief Lookup a file * * @param[in] parent Parent directory * @param[in] path Path to lookup * @param[out] handle Found handle, on success * @param[out] attrs_out Attributes of found handle * @return FSAL status */ static fsal_status_t mem_lookup(struct fsal_obj_handle *parent, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { struct mem_fsal_obj_handle *myself, *hdl = NULL; fsal_status_t status; myself = container_of(parent, struct mem_fsal_obj_handle, obj_handle); /* Check if this context already holds the lock on * this directory. */ if (op_ctx->fsal_private != parent) PTHREAD_RWLOCK_rdlock(&parent->obj_lock); else LogFullDebug(COMPONENT_FSAL, "Skipping lock for %s", myself->m_name); status = mem_int_lookup(myself, path, &hdl); if (FSAL_IS_ERROR(status)) { goto out; } *handle = &hdl->obj_handle; out: if (op_ctx->fsal_private != parent) PTHREAD_RWLOCK_unlock(&parent->obj_lock); if (!FSAL_IS_ERROR(status) && attrs_out != NULL) { /* This is unlocked, however, for the most part, attributes * are read-only. Come back later and do some lock protection. */ fsal_copy_attrs(attrs_out, &hdl->attrs, false); } return status; } /** * @brief Read a directory * * @param[in] dir_hdl the directory to read * @param[in] whence where to start (next) * @param[in] dir_state pass thru of state to callback * @param[in] cb callback function * @param[out] eof eof marker true == end of dir */ static fsal_status_t mem_readdir(struct fsal_obj_handle *dir_hdl, fsal_cookie_t *whence, void *dir_state, fsal_readdir_cb cb, attrmask_t attrmask, bool *eof) { struct mem_fsal_obj_handle *myself; struct avltree_node *node; fsal_cookie_t seekloc = 0; struct attrlist attrs; enum fsal_dir_result cb_rc; myself = container_of(dir_hdl, struct mem_fsal_obj_handle, obj_handle); if (whence != NULL) seekloc = *whence; else seekloc = 2; *eof = true; LogFullDebug(COMPONENT_FSAL, "hdl=%p, name=%s", myself, myself->m_name); PTHREAD_RWLOCK_rdlock(&dir_hdl->obj_lock); /* Use fsal_private to signal to lookup that we hold * the lock. */ op_ctx->fsal_private = dir_hdl; for (node = avltree_first(&myself->mh_dir.avl_index); node != NULL; node = avltree_next(node)) { struct mem_dirent *dirent; dirent = avltree_container_of(node, struct mem_dirent, avl_i); /* skip entries before seekloc */ if (dirent->d_index < seekloc) continue; fsal_prepare_attrs(&attrs, attrmask); fsal_copy_attrs(&attrs, &dirent->hdl->attrs, false); cb_rc = cb(dirent->d_name, &dirent->hdl->obj_handle, &attrs, dir_state, dirent->d_index + 1); fsal_release_attrs(&attrs); if (cb_rc >= DIR_TERMINATE) { *eof = false; break; } } op_ctx->fsal_private = NULL; PTHREAD_RWLOCK_unlock(&dir_hdl->obj_lock); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Create a directory * * This function creates a new directory. * * While FSAL_MEM is a support_ex FSAL, it doesn't actually support * setting attributes, so only the mode attribute is relevant. Any other * attributes set on creation will be ignored. The owner and group will be * set from the active credentials. * * @param[in] dir_hdl Directory in which to create the directory * @param[in] name Name of directory to create * @param[in] attrs_in Attributes to set on newly created object * @param[out] new_obj Newly created object * @param[in,out] attrs_out Optional attributes for newly created object * * @note On success, @a new_obj has been ref'd * * @return FSAL status. */ static fsal_status_t mem_mkdir(struct fsal_obj_handle *dir_hdl, const char *name, struct attrlist *attrs_in, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out) { struct mem_fsal_obj_handle *parent = container_of(dir_hdl, struct mem_fsal_obj_handle, obj_handle); LogDebug(COMPONENT_FSAL, "mkdir %s", name); return mem_create_obj(parent, DIRECTORY, name, attrs_in, new_obj, attrs_out); } /** * @brief Make a device node * * @param[in] dir_hdl Parent directory handle * @param[in] name Name of new node * @param[in] nodetype Type of new node * @param[in] attrs_in Attributes for new node * @param[out] new_obj New object handle on success * * @note This returns an INITIAL ref'd entry on success * @return FSAL status */ static fsal_status_t mem_mknode(struct fsal_obj_handle *dir_hdl, const char *name, object_file_type_t nodetype, struct attrlist *attrs_in, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out) { struct mem_fsal_obj_handle *hdl, *parent = container_of(dir_hdl, struct mem_fsal_obj_handle, obj_handle); fsal_status_t status; LogDebug(COMPONENT_FSAL, "mknode %s", name); status = mem_create_obj(parent, nodetype, name, attrs_in, new_obj, attrs_out); if (unlikely(FSAL_IS_ERROR(status))) return status; hdl = container_of(*new_obj, struct mem_fsal_obj_handle, obj_handle); hdl->mh_node.nodetype = nodetype; hdl->mh_node.dev = attrs_in->rawdev; /* struct copy */ return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Make a symlink * * @param[in] dir_hdl Parent directory handle * @param[in] name Name of new node * @param[in] link_path Contents of symlink * @param[in] attrs_in Attributes for new simlink * @param[out] new_obj New object handle on success * * @note This returns an INITIAL ref'd entry on success * @return FSAL status */ static fsal_status_t mem_symlink(struct fsal_obj_handle *dir_hdl, const char *name, const char *link_path, struct attrlist *attrs_in, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out) { struct mem_fsal_obj_handle *hdl, *parent = container_of(dir_hdl, struct mem_fsal_obj_handle, obj_handle); fsal_status_t status; LogDebug(COMPONENT_FSAL, "symlink %s", name); status = mem_create_obj(parent, SYMBOLIC_LINK, name, attrs_in, new_obj, attrs_out); if (unlikely(FSAL_IS_ERROR(status))) return status; hdl = container_of(*new_obj, struct mem_fsal_obj_handle, obj_handle); hdl->mh_symlink.link_contents = gsh_strdup(link_path); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Read a symlink * * @param[in] obj_hdl Handle for symlink * @param[out] link_content Buffer to fill with link contents * @param[in] refresh If true, refresh attributes on symlink * @return FSAL status */ static fsal_status_t mem_readlink(struct fsal_obj_handle *obj_hdl, struct gsh_buffdesc *link_content, bool refresh) { struct mem_fsal_obj_handle *myself = container_of(obj_hdl, struct mem_fsal_obj_handle, obj_handle); if (obj_hdl->type != SYMBOLIC_LINK) { LogCrit(COMPONENT_FSAL, "Handle is not a symlink. hdl = 0x%p", obj_hdl); return fsalstat(ERR_FSAL_INVAL, 0); } link_content->len = strlen(myself->mh_symlink.link_contents) + 1; link_content->addr = gsh_strdup(myself->mh_symlink.link_contents); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Get attributes for a file * * @param[in] obj_hdl File to get * @param[out] outattrs Attributes for file * @return FSAL status */ static fsal_status_t mem_getattrs(struct fsal_obj_handle *obj_hdl, struct attrlist *outattrs) { struct mem_fsal_obj_handle *myself = container_of(obj_hdl, struct mem_fsal_obj_handle, obj_handle); if (!myself->is_export && glist_empty(&myself->dirents)) { /* Removed entry - stale */ LogDebug(COMPONENT_FSAL, "Requesting attributes for removed entry %p, name=%s", myself, myself->m_name); return fsalstat(ERR_FSAL_STALE, ESTALE); } if (obj_hdl->type == DIRECTORY) { /* We need to update the numlinks */ myself->attrs.numlinks = atomic_fetch_uint32_t(&myself->mh_dir.numkids); } #ifdef USE_LTTNG tracepoint(fsalmem, mem_getattrs, __func__, __LINE__, myself, myself->m_name, myself->attrs.filesize, myself->attrs.numlinks, myself->attrs.change); #endif LogFullDebug(COMPONENT_FSAL, "hdl=%p, name=%s numlinks %"PRIu32, myself, myself->m_name, myself->attrs.numlinks); fsal_copy_attrs(outattrs, &myself->attrs, false); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Set attributes on an object * * This function sets attributes on an object. Which attributes are * set is determined by attrs_set->valid_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] attrs_set Attributes to set * * @return FSAL status. */ fsal_status_t mem_setattr2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, struct attrlist *attrs_set) { struct mem_fsal_obj_handle *myself = container_of(obj_hdl, struct mem_fsal_obj_handle, obj_handle); /* apply umask, if mode attribute is to be changed */ if (FSAL_TEST_MASK(attrs_set->valid_mask, ATTR_MODE)) attrs_set->mode &= ~op_ctx->fsal_export->exp_ops.fs_umask(op_ctx->fsal_export); /* Test if size is being set, make sure file is regular and if so, * require a read/write file descriptor. */ if (FSAL_TEST_MASK(attrs_set->valid_mask, ATTR_SIZE) && obj_hdl->type != REGULAR_FILE) { LogFullDebug(COMPONENT_FSAL, "Setting size on non-regular file"); return fsalstat(ERR_FSAL_INVAL, EINVAL); } mem_copy_attrs_mask(attrs_set, &myself->attrs); #ifdef USE_LTTNG tracepoint(fsalmem, mem_setattrs, __func__, __LINE__, myself, myself->m_name, myself->attrs.filesize, myself->attrs.numlinks, myself->attrs.change); #endif return fsalstat(ERR_FSAL_NO_ERROR, EINVAL); } /** * @brief Hard link an obj * * @param[in] obj_hdl File to link * @param[in] dir_hdl Directory to link into * @param[in] name Name to use for link * * @return FSAL status. */ fsal_status_t mem_link(struct fsal_obj_handle *obj_hdl, struct fsal_obj_handle *dir_hdl, const char *name) { struct mem_fsal_obj_handle *myself = container_of(obj_hdl, struct mem_fsal_obj_handle, obj_handle); struct mem_fsal_obj_handle *dir = container_of(dir_hdl, struct mem_fsal_obj_handle, obj_handle); struct mem_fsal_obj_handle *hdl; fsal_status_t status = {0, 0}; status = mem_int_lookup(dir, name, &hdl); if (!FSAL_IS_ERROR(status)) { /* It already exists */ return fsalstat(ERR_FSAL_EXIST, 0); } else if (status.major != ERR_FSAL_NOENT) { /* Some other error */ return status; } mem_insert_obj(dir, myself, name); myself->attrs.numlinks++; #ifdef USE_LTTNG tracepoint(fsalmem, mem_link, __func__, __LINE__, dir, dir->m_name, myself, myself->m_name, name, myself->attrs.numlinks); #endif return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Unlink a file * * @param[in] dir_hdl Parent directory handle * @param[in] obj_hdl Object being removed * @param[in] name Name of object to remove * @return FSAL status */ static fsal_status_t mem_unlink(struct fsal_obj_handle *dir_hdl, struct fsal_obj_handle *obj_hdl, const char *name) { struct mem_fsal_obj_handle *parent, *myself; fsal_status_t status = {0, 0}; uint32_t numkids; struct mem_dirent *dirent; parent = container_of(dir_hdl, struct mem_fsal_obj_handle, obj_handle); myself = container_of(obj_hdl, struct mem_fsal_obj_handle, obj_handle); PTHREAD_RWLOCK_wrlock(&dir_hdl->obj_lock); switch (obj_hdl->type) { case DIRECTORY: /* Check if directory is empty */ numkids = atomic_fetch_uint32_t(&myself->mh_dir.numkids); if (numkids > 2) { LogFullDebug(COMPONENT_FSAL, "%s numkids %"PRIu32, myself->m_name, numkids); status = fsalstat(ERR_FSAL_NOTEMPTY, 0); goto unlock; } break; case REGULAR_FILE: /* Openable. Make sure it's closed */ if (myself->mh_file.fd.openflags != FSAL_O_CLOSED) { status = fsalstat(ERR_FSAL_FILE_OPEN, 0); goto unlock; } /* FALLTHROUGH */ case SYMBOLIC_LINK: case SOCKET_FILE: case CHARACTER_FILE: case BLOCK_FILE: case FIFO_FILE: /* Unopenable. Just clean up */ myself->attrs.numlinks--; break; default: break; } /* Remove the dirent from the parent*/ dirent = mem_dirent_lookup(parent, name); if (dirent) mem_remove_dirent_locked(parent, dirent, false); unlock: PTHREAD_RWLOCK_unlock(&dir_hdl->obj_lock); #ifdef USE_LTTNG tracepoint(fsalmem, mem_unlink, __func__, __LINE__, parent, parent->m_name, myself, myself->m_name, myself->attrs.numlinks); #endif return status; } /** * @brief Close a file's global descriptor * * @param[in] obj_hdl File on which to operate * * @return FSAL status. */ fsal_status_t mem_close(struct fsal_obj_handle *obj_hdl) { struct mem_fsal_obj_handle *myself = container_of(obj_hdl, struct mem_fsal_obj_handle, obj_handle); fsal_status_t status; assert(obj_hdl->type == REGULAR_FILE); /* Take write lock on object to protect file descriptor. * This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->obj_lock); status = mem_close_my_fd(&myself->mh_file.fd); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /** * @brief Rename an object * * Rename the given object from @a old_name in @a olddir_hdl to @a new_name in * @a newdir_hdl. The old and new directories may be the same. * * @param[in] obj_hdl Object to rename * @param[in] olddir_hdl Directory containing @a obj_hdl * @param[in] old_name Current name of @a obj_hdl * @param[in] newdir_hdl Directory to move @a obj_hdl to * @param[in] new_name Name to rename @a obj_hdl to * @return FSAL status */ static fsal_status_t mem_rename(struct fsal_obj_handle *obj_hdl, struct fsal_obj_handle *olddir_hdl, const char *old_name, struct fsal_obj_handle *newdir_hdl, const char *new_name) { struct mem_fsal_obj_handle *mem_olddir = container_of(olddir_hdl, struct mem_fsal_obj_handle, obj_handle); struct mem_fsal_obj_handle *mem_newdir = container_of(newdir_hdl, struct mem_fsal_obj_handle, obj_handle); struct mem_fsal_obj_handle *mem_obj = container_of(obj_hdl, struct mem_fsal_obj_handle, obj_handle); struct mem_fsal_obj_handle *mem_lookup_dst = NULL; fsal_status_t status; status = mem_int_lookup(mem_newdir, new_name, &mem_lookup_dst); if (!FSAL_IS_ERROR(status)) { uint32_t numkids; if (mem_obj == mem_lookup_dst) { /* Same source and destination */ return status; } if ((obj_hdl->type == DIRECTORY && mem_lookup_dst->obj_handle.type != DIRECTORY) || (obj_hdl->type != DIRECTORY && mem_lookup_dst->obj_handle.type == DIRECTORY)) { /* Types must be "compatible" */ return fsalstat(ERR_FSAL_EXIST, 0); } numkids = atomic_fetch_uint32_t( &mem_lookup_dst->mh_dir.numkids); if (mem_lookup_dst->obj_handle.type == DIRECTORY && numkids > 2) { /* Target dir must be empty */ return fsalstat(ERR_FSAL_EXIST, 0); } /* Unlink destination */ status = mem_unlink(newdir_hdl, &mem_lookup_dst->obj_handle, new_name); if (FSAL_IS_ERROR(status)) { return status; } } #ifdef USE_LTTNG tracepoint(fsalmem, mem_rename, __func__, __LINE__, mem_obj, mem_olddir->m_name, old_name, mem_newdir->m_name, new_name); #endif /* Remove from old dir */ mem_remove_dirent(mem_olddir, old_name); if (!strcmp(old_name, mem_obj->m_name)) { /* Change base name */ gsh_free(mem_obj->m_name); mem_obj->m_name = gsh_strdup(new_name); } /* Insert into new directory */ mem_insert_obj(mem_newdir, mem_obj, new_name); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Open a file descriptor for read or write and possibly create * * @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] attrs_in 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 Optional attributes for newly created object * @param[in,out] caller_perm_check The caller must do a permission check * * @return FSAL status. */ fsal_status_t mem_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 *attrs_set, fsal_verifier_t verifier, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out, bool *caller_perm_check) { fsal_status_t status = {0, 0}; struct fsal_fd *my_fd = NULL; struct mem_fsal_obj_handle *myself, *hdl = NULL; bool truncated; bool setattrs = attrs_set != NULL; bool created = false; struct attrlist verifier_attr; if (state != NULL) my_fd = (struct fsal_fd *)(state + 1); myself = container_of(obj_hdl, struct mem_fsal_obj_handle, obj_handle); if (setattrs) LogAttrlist(COMPONENT_FSAL, NIV_FULL_DEBUG, "attrs_set ", attrs_set, false); truncated = (openflags & FSAL_O_TRUNC) != 0; LogFullDebug(COMPONENT_FSAL, truncated ? "Truncate" : "No truncate"); /* Now fixup attrs for verifier if exclusive create */ if (createmode >= FSAL_EXCLUSIVE) { if (!setattrs) { /* We need to use verifier_attr */ attrs_set = &verifier_attr; memset(&verifier_attr, 0, sizeof(verifier_attr)); } set_common_verifier(attrs_set, verifier); } if (name == NULL) { /* This is an open by handle */ #ifdef USE_LTTNG tracepoint(fsalmem, mem_open, __func__, __LINE__, myself, myself->m_name, state, truncated, setattrs); #endif /* Need a lock to protect the FD */ PTHREAD_RWLOCK_wrlock(&obj_hdl->obj_lock); 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). */ /* Check share reservation conflicts. */ status = check_share_conflict(&myself->mh_file.share, openflags, false); if (FSAL_IS_ERROR(status)) { PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /* Take the share reservation now by updating the * counters. */ update_share_counters(&myself->mh_file.share, FSAL_O_CLOSED, openflags); } else { /* We need to use the global fd to continue, and take * the lock to protect it. */ my_fd = &hdl->mh_file.fd; } if (openflags & FSAL_O_WRITE) openflags |= FSAL_O_READ; mem_open_my_fd(my_fd, openflags); if (truncated) myself->attrs.filesize = myself->attrs.spaceused = 0; /* Now check verifier for exclusive, but not for * FSAL_EXCLUSIVE_9P. */ if (createmode >= FSAL_EXCLUSIVE && createmode != FSAL_EXCLUSIVE_9P && !check_verifier_attrlist(&myself->attrs, verifier)) { /* Verifier didn't match, return EEXIST */ status = fsalstat(posix2fsal_error(EEXIST), EEXIST); } if (!FSAL_IS_ERROR(status)) { /* Return success. */ PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); if (attrs_out != NULL) /* Note, myself->attrs is usually protected by a * the attr_lock in MDCACHE. It's not in this * case. Since MEM is not a production FSAL, * this is deemed to be okay for the moment. */ fsal_copy_attrs(attrs_out, &myself->attrs, false); return status; } (void) mem_close_my_fd(my_fd); if (state == NULL) { /* If no state, release the lock taken above and return * status. */ PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /* 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. */ update_share_counters(&myself->mh_file.share, openflags, FSAL_O_CLOSED); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_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. */ status = mem_int_lookup(myself, name, &hdl); if (FSAL_IS_ERROR(status)) { struct fsal_obj_handle *new_obj; if (status.major != ERR_FSAL_NOENT) { /* Actual error from lookup */ return status; } /* Doesn't exist, create it */ status = mem_create_obj(myself, REGULAR_FILE, name, attrs_set, &new_obj, attrs_out); if (FSAL_IS_ERROR(status)) { return status; } hdl = container_of(new_obj, struct mem_fsal_obj_handle, obj_handle); created = true; } #ifdef USE_LTTNG tracepoint(fsalmem, mem_open, __func__, __LINE__, hdl, hdl->m_name, state, truncated, setattrs); #endif *caller_perm_check = !created; /* 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->mh_file.fd; if (openflags & FSAL_O_WRITE) openflags |= FSAL_O_READ; mem_open_my_fd(my_fd, openflags); *new_obj = &hdl->obj_handle; if (!created) { /* Create sets and gets attributes, so only do this if not * creating */ if (setattrs && attrs_set->valid_mask != 0) { mem_copy_attrs_mask(attrs_set, &hdl->attrs); } if (attrs_out != NULL) { status = (*new_obj)->obj_ops.getattrs(*new_obj, attrs_out); if (FSAL_IS_ERROR(status) && (attrs_out->request_mask & ATTR_RDATTR_ERR) == 0) { /* Get attributes failed and caller expected * to get the attributes. Otherwise continue * with attrs_out indicating ATTR_RDATTR_ERR. */ return status; } } } 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)->obj_lock); /* Take the share reservation now by updating the counters. */ update_share_counters(&hdl->mh_file.share, FSAL_O_CLOSED, openflags); PTHREAD_RWLOCK_unlock(&(*new_obj)->obj_lock); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Re-open a file that may be already opened * * This function supports changing the access mode of a share reservation and * thus should only be called with a share state. The state_lock must be held. * * This MAY be used to open a file the first time if there is no need for * open by name or create semantics. One example would be 9P lopen. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * @param[in] openflags Mode for re-open * * @return FSAL status. */ fsal_status_t mem_reopen2(struct fsal_obj_handle *obj_hdl, struct state_t *state, fsal_openflags_t openflags) { struct mem_fsal_obj_handle *myself = container_of(obj_hdl, struct mem_fsal_obj_handle, obj_handle); fsal_status_t status = {0, 0}; struct fsal_fd *my_fd = (struct fsal_fd *)(state + 1); fsal_openflags_t old_openflags; #ifdef USE_LTTNG tracepoint(fsalmem, mem_open, __func__, __LINE__, myself, myself->m_name, state, openflags & FSAL_O_TRUNC, false); #endif old_openflags = my_fd->openflags; /* This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->obj_lock); /* We can conflict with old share, so go ahead and check now. */ status = check_share_conflict(&myself->mh_file.share, openflags, false); if (FSAL_IS_ERROR(status)) { PTHREAD_RWLOCK_unlock(&obj_hdl->obj_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->mh_file.share, old_openflags, openflags); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); mem_open_my_fd(my_fd, openflags); if (openflags & FSAL_O_TRUNC) myself->attrs.filesize = myself->attrs.spaceused = 0; return status; } /** * @brief Read data from a file * * This function reads data from the given file. The FSAL must be able to * perform the read whether a state is presented or not. This function also * is expected to handle properly bypassing or not share reservations. * * @param[in] obj_hdl File on which to operate * @param[in] bypass If state doesn't indicate a share reservation, * bypass any deny read * @param[in] state state_t to use for this operation * @param[in] offset Position from which to read * @param[in] buffer_size Amount of data to read * @param[out] buffer Buffer to which data are to be copied * @param[out] read_amount Amount of data read * @param[out] end_of_file true if the end of file has been reached * @param[in,out] info more information about the data * * @return FSAL status. */ fsal_status_t mem_read2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buffer_size, void *buffer, size_t *read_amount, bool *end_of_file, struct io_info *info) { struct mem_fsal_obj_handle *myself = container_of(obj_hdl, struct mem_fsal_obj_handle, obj_handle); struct fsal_fd *fsal_fd; bool has_lock, closefd = false; fsal_status_t status = {ERR_FSAL_NO_ERROR, 0}; bool reusing_open_state_fd = false; if (info != NULL) { /* Currently we don't support READ_PLUS */ return fsalstat(ERR_FSAL_NOTSUPP, 0); } /* Find an FD */ status = fsal_find_fd(&fsal_fd, obj_hdl, &myself->mh_file.fd, &myself->mh_file.share, bypass, state, FSAL_O_READ, mem_open_func, mem_close_func, &has_lock, &closefd, false, &reusing_open_state_fd); if (FSAL_IS_ERROR(status)) { return status; } if (offset > myself->attrs.filesize) { buffer_size = 0; } else if (offset + buffer_size > myself->attrs.filesize) { buffer_size = myself->attrs.filesize - offset; } if (offset < myself->datasize) { size_t readsize; /* Data to read */ readsize = MIN(buffer_size, myself->datasize - offset); memcpy(buffer, myself->data + offset, readsize); if (readsize < buffer_size) memset(buffer + readsize, 'a', buffer_size - readsize); } else { memset(buffer, 'a', buffer_size); } #ifdef USE_LTTNG tracepoint(fsalmem, mem_read, __func__, __LINE__, myself, myself->m_name, state, myself->attrs.filesize, myself->attrs.spaceused); #endif *read_amount = buffer_size; *end_of_file = (buffer_size == 0); now(&myself->attrs.atime); if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Write data to a file * * This function writes data to a file. The FSAL must be able to * perform the write whether a state is presented or not. This function also * is expected to handle properly bypassing or not share reservations. Even * with bypass == true, it will enforce a mandatory (NFSv4) deny_write if * an appropriate state is not passed). * * The FSAL is expected to enforce sync if necessary. * * @param[in] obj_hdl File on which to operate * @param[in] bypass If state doesn't indicate a share reservation, * bypass any non-mandatory deny write * @param[in] state state_t to use for this operation * @param[in] offset Position at which to write * @param[in] buffer Data to be written * @param[in,out] fsal_stable In, if on, the fsal is requested to write data * to stable store. Out, the fsal reports what * it did. * @param[in,out] info more information about the data * * @return FSAL status. */ fsal_status_t mem_write2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buffer_size, void *buffer, size_t *wrote_amount, bool *fsal_stable, struct io_info *info) { struct mem_fsal_obj_handle *myself = container_of(obj_hdl, struct mem_fsal_obj_handle, obj_handle); struct fsal_fd *fsal_fd; bool has_lock, closefd = false; fsal_status_t status = {ERR_FSAL_NO_ERROR, 0}; bool reusing_open_state_fd = false; if (info != NULL) { /* Currently we don't support WRITE_PLUS */ return fsalstat(ERR_FSAL_NOTSUPP, 0); } if (obj_hdl->type != REGULAR_FILE) { /* Currently can only write to a file */ return fsalstat(ERR_FSAL_INVAL, 0); } /* Find an FD */ status = fsal_find_fd(&fsal_fd, obj_hdl, &myself->mh_file.fd, &myself->mh_file.share, bypass, state, FSAL_O_WRITE, mem_open_func, mem_close_func, &has_lock, &closefd, false, &reusing_open_state_fd); if (FSAL_IS_ERROR(status)) { return status; } if (offset + buffer_size > myself->attrs.filesize) { myself->attrs.filesize = myself->attrs.spaceused = offset + buffer_size; } if (offset < myself->datasize) { size_t writesize; /* Space to write */ writesize = MIN(buffer_size, myself->datasize - offset); memcpy(myself->data + offset, buffer, writesize); } #ifdef USE_LTTNG tracepoint(fsalmem, mem_write, __func__, __LINE__, myself, myself->m_name, state, myself->attrs.filesize, myself->attrs.spaceused); #endif /* Update change stats */ now(&myself->attrs.mtime); myself->attrs.chgtime = myself->attrs.mtime; myself->attrs.change = timespec_to_nsecs(&myself->attrs.chgtime); *wrote_amount = buffer_size; if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Commit written data * * This function flushes possibly buffered data to a file. This method * differs from commit due to the need to interact with share reservations * and the fact that the FSAL manages the state of "file descriptors". The * FSAL must be able to perform this operation without being passed a specific * state. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * @param[in] offset Start of range to commit * @param[in] len Length of range to commit * * @return FSAL status. */ fsal_status_t mem_commit2(struct fsal_obj_handle *obj_hdl, off_t offset, size_t len) { return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @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". * * @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 mem_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 mem_fsal_obj_handle *myself = container_of(obj_hdl, struct mem_fsal_obj_handle, obj_handle); struct fsal_fd *fsal_fd; bool has_lock, closefd = false; bool bypass = false; fsal_openflags_t openflags; fsal_status_t status = {ERR_FSAL_NO_ERROR, 0}; bool reusing_open_state_fd = false; if (obj_hdl->type != REGULAR_FILE) { /* Currently can only lock a file */ return fsalstat(ERR_FSAL_INVAL, 0); } switch (lock_op) { case FSAL_OP_LOCKT: /* We may end up using global fd, don't fail on a deny mode */ bypass = true; openflags = FSAL_O_ANY; break; case FSAL_OP_LOCK: 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 openflags = FSAL_O_RDWR; break; case FSAL_OP_UNLOCK: openflags = FSAL_O_ANY; break; default: LogDebug(COMPONENT_FSAL, "ERROR: The requested lock type was not read or write."); return fsalstat(ERR_FSAL_NOTSUPP, 0); } status = fsal_find_fd(&fsal_fd, obj_hdl, &myself->mh_file.fd, &myself->mh_file.share, bypass, state, openflags, mem_open_func, mem_close_func, &has_lock, &closefd, true, &reusing_open_state_fd); if (FSAL_IS_ERROR(status)) { return status; } if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /** * @brief Manage closing a file when a state is no longer needed. * * When the upper layers are ready to dispense with a state, this method is * called to allow the FSAL to close any file descriptors or release any other * resources associated with the state. A call to free_state should be assumed * to follow soon. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * * @return FSAL status. */ fsal_status_t mem_close2(struct fsal_obj_handle *obj_hdl, struct state_t *state) { struct fsal_fd *my_fd = (struct fsal_fd *)(state + 1); struct mem_fsal_obj_handle *myself = container_of(obj_hdl, struct mem_fsal_obj_handle, obj_handle); fsal_status_t status; #ifdef USE_LTTNG tracepoint(fsalmem, mem_close, __func__, __LINE__, myself, myself->m_name, state); #endif if (state->state_type == STATE_TYPE_SHARE || state->state_type == STATE_TYPE_NLM_SHARE || state->state_type == STATE_TYPE_9P_FID) { /* This is a share state, we must update the share counters */ /* This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->obj_lock); update_share_counters(&myself->mh_file.share, my_fd->openflags, FSAL_O_CLOSED); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); } status = mem_close_my_fd(&myself->mh_file.fd); return status; } /** * @brief Get the wire version of a handle * * fill in the opaque f/s file handle part. * we zero the buffer to length first. This MAY already be done above * at which point, remove memset here because the caller is zeroing * the whole struct. * * @param[in] obj_hdl Handle to digest * @param[in] out_type Type of digest to get * @param[out] fh_desc Buffer to write digest into * @return FSAL status */ static fsal_status_t mem_handle_to_wire(const struct fsal_obj_handle *obj_hdl, fsal_digesttype_t output_type, struct gsh_buffdesc *fh_desc) { const struct mem_fsal_obj_handle *myself; myself = container_of(obj_hdl, const struct mem_fsal_obj_handle, obj_handle); switch (output_type) { case FSAL_DIGEST_NFSV3: case FSAL_DIGEST_NFSV4: if (fh_desc->len < V4_FH_OPAQUE_SIZE) { LogMajor(COMPONENT_FSAL, "Space too small for handle. need %lu, have %zu", ((unsigned long) V4_FH_OPAQUE_SIZE), fh_desc->len); return fsalstat(ERR_FSAL_TOOSMALL, 0); } memcpy(fh_desc->addr, myself->handle, V4_FH_OPAQUE_SIZE); fh_desc->len = V4_FH_OPAQUE_SIZE; break; default: return fsalstat(ERR_FSAL_SERVERFAULT, 0); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Get the unique key for a handle * * return a handle descriptor into the handle in this object handle * @TODO reminder. make sure things like hash keys don't point here * after the handle is released. * * @param[in] obj_hdl Handle to digest * @param[out] fh_desc Buffer to write key into */ static void mem_handle_to_key(struct fsal_obj_handle *obj_hdl, struct gsh_buffdesc *fh_desc) { struct mem_fsal_obj_handle *myself; myself = container_of(obj_hdl, struct mem_fsal_obj_handle, obj_handle); fh_desc->addr = myself->handle; fh_desc->len = V4_FH_OPAQUE_SIZE; } /** * @brief Release an object handle * * @param[in] obj_hdl Handle to release */ static void mem_release(struct fsal_obj_handle *obj_hdl) { struct mem_fsal_export *mfe; struct mem_fsal_obj_handle *myself; myself = container_of(obj_hdl, struct mem_fsal_obj_handle, obj_handle); mfe = myself->mfo_exp; if (myself->is_export || !glist_empty(&myself->dirents)) { /* Entry is still live: it's either an export, or in a dir */ #ifdef USE_LTTNG tracepoint(fsalmem, mem_inuse, __func__, __LINE__, myself, myself->attrs.numlinks, myself->is_export); #endif LogDebug(COMPONENT_FSAL, "Releasing live hdl=%p, name=%s, don't deconstruct it", myself, myself->m_name); return; } fsal_obj_handle_fini(obj_hdl); LogDebug(COMPONENT_FSAL, "Releasing obj_hdl=%p, myself=%p, name=%s", obj_hdl, myself, myself->m_name); switch (obj_hdl->type) { case DIRECTORY: /* Empty directory */ mem_clean_all_dirents(myself); break; case REGULAR_FILE: break; case SYMBOLIC_LINK: gsh_free(myself->mh_symlink.link_contents); break; case SOCKET_FILE: case CHARACTER_FILE: case BLOCK_FILE: case FIFO_FILE: break; default: break; } PTHREAD_RWLOCK_wrlock(&mfe->mfe_exp_lock); mem_free_handle(myself); PTHREAD_RWLOCK_unlock(&mfe->mfe_exp_lock); } void mem_handle_ops_init(struct fsal_obj_ops *ops) { ops->release = mem_release; ops->lookup = mem_lookup; ops->readdir = mem_readdir; ops->mkdir = mem_mkdir; ops->mknode = mem_mknode; ops->symlink = mem_symlink; ops->readlink = mem_readlink; ops->getattrs = mem_getattrs; ops->setattr2 = mem_setattr2; ops->link = mem_link; ops->rename = mem_rename; ops->unlink = mem_unlink; ops->close = mem_close; ops->open2 = mem_open2; ops->reopen2 = mem_reopen2; ops->read2 = mem_read2; ops->write2 = mem_write2; ops->commit2 = mem_commit2; ops->lock_op2 = mem_lock_op2; ops->close2 = mem_close2; ops->handle_to_wire = mem_handle_to_wire; ops->handle_to_key = mem_handle_to_key; } /* export methods that create object handles */ /* lookup_path * modelled on old api except we don't stuff attributes. * KISS */ fsal_status_t mem_lookup_path(struct fsal_export *exp_hdl, const char *path, struct fsal_obj_handle **obj_hdl, struct attrlist *attrs_out) { struct mem_fsal_export *mfe; struct attrlist attrs; mfe = container_of(exp_hdl, struct mem_fsal_export, export); if (strcmp(path, mfe->export_path) != 0) { /* Lookup of a path other than the export's root. */ LogCrit(COMPONENT_FSAL, "Attempt to lookup non-root path %s", path); return fsalstat(ERR_FSAL_NOENT, ENOENT); } attrs.valid_mask = ATTR_MODE; attrs.mode = 0755; if (mfe->root_handle == NULL) { mfe->root_handle = mem_alloc_handle(NULL, mfe->export_path, DIRECTORY, mfe, &attrs); } *obj_hdl = &mfe->root_handle->obj_handle; if (attrs_out != NULL) fsal_copy_attrs(attrs_out, &mfe->root_handle->attrs, false); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* create_handle * Does what original FSAL_ExpandHandle did (sort of) * returns a ref counted handle to be later used in cache_inode etc. * NOTE! you must release this thing when done with it! * BEWARE! Thanks to some holes in the *AT syscalls implementation, * we cannot get an fd on an AF_UNIX socket, nor reliably on block or * character special devices. Sorry, it just doesn't... * we could if we had the handle of the dir it is in, but this method * is for getting handles off the wire for cache entries that have LRU'd. * Ideas and/or clever hacks are welcome... */ fsal_status_t mem_create_handle(struct fsal_export *exp_hdl, struct gsh_buffdesc *hdl_desc, struct fsal_obj_handle **obj_hdl, struct attrlist *attrs_out) { struct glist_head *glist; struct fsal_obj_handle *hdl; struct mem_fsal_obj_handle *my_hdl; *obj_hdl = NULL; if (hdl_desc->len != V4_FH_OPAQUE_SIZE) { LogCrit(COMPONENT_FSAL, "Invalid handle size %zu expected %lu", hdl_desc->len, ((unsigned long) V4_FH_OPAQUE_SIZE)); return fsalstat(ERR_FSAL_BADHANDLE, 0); } PTHREAD_RWLOCK_rdlock(&exp_hdl->fsal->lock); glist_for_each(glist, &exp_hdl->fsal->handles) { hdl = glist_entry(glist, struct fsal_obj_handle, handles); my_hdl = container_of(hdl, struct mem_fsal_obj_handle, obj_handle); if (memcmp(my_hdl->handle, hdl_desc->addr, V4_FH_OPAQUE_SIZE) == 0) { LogDebug(COMPONENT_FSAL, "Found hdl=%p name=%s", my_hdl, my_hdl->m_name); #ifdef USE_LTTNG tracepoint(fsalmem, mem_create_handle, __func__, __LINE__, hdl, my_hdl->m_name); #endif *obj_hdl = hdl; PTHREAD_RWLOCK_unlock(&exp_hdl->fsal->lock); if (attrs_out != NULL) { fsal_copy_attrs(attrs_out, &my_hdl->attrs, false); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } } LogDebug(COMPONENT_FSAL, "Could not find handle"); PTHREAD_RWLOCK_unlock(&exp_hdl->fsal->lock); return fsalstat(ERR_FSAL_STALE, ESTALE); } nfs-ganesha-2.6.0/src/FSAL/FSAL_MEM/mem_int.h000066400000000000000000000115531324272410200202530ustar00rootroot00000000000000/* * vim:shiftwidth=8:tabstop=8: * * Copyright 2017-2018 Red Hat, Inc. * Author: Daniel Gryniewicz dang@redhat.com * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* MEM methods for handles */ #include "avltree.h" #include "gsh_list.h" #ifdef USE_LTTNG #include "gsh_lttng/fsal_mem.h" #endif struct mem_fsal_obj_handle; /** * MEM internal export */ struct mem_fsal_export { /** Export this wraps */ struct fsal_export export; /** The path for this export */ char *export_path; /** Root object for this export */ struct mem_fsal_obj_handle *root_handle; /** Entry into list of exports */ struct glist_head export_entry; /** Lock protecting mfe_objs */ pthread_rwlock_t mfe_exp_lock; /** List of all the objects in this export */ struct glist_head mfe_objs; }; fsal_status_t mem_lookup_path(struct fsal_export *exp_hdl, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out); fsal_status_t mem_create_handle(struct fsal_export *exp_hdl, struct gsh_buffdesc *hdl_desc, struct fsal_obj_handle **handle, struct attrlist *attrs_out); /* * MEM internal object handle */ #define V4_FH_OPAQUE_SIZE 58 /* Size of state_obj digest */ struct mem_fsal_obj_handle { struct fsal_obj_handle obj_handle; struct attrlist attrs; uint64_t inode; char handle[V4_FH_OPAQUE_SIZE]; union { struct { struct mem_fsal_obj_handle *parent; struct avltree avl_name; struct avltree avl_index; uint32_t numkids; uint32_t next_i; /* next child index */ } mh_dir; struct { struct fsal_share share; struct fsal_fd fd; } mh_file; struct { object_file_type_t nodetype; fsal_dev_t dev; } mh_node; struct { char *link_contents; } mh_symlink; }; struct glist_head dirents; /* List of dirents pointing to obj */ struct glist_head mfo_exp_entry; /**< Link into mfs_objs */ struct mem_fsal_export *mfo_exp; /**< Export owning object */ char *m_name; /**< Base name of obj, for debugging */ uint32_t datasize; bool is_export; char data[0]; /* Allocated data */ }; /** * @brief Dirent for FSAL_MEM */ struct mem_dirent { struct mem_fsal_obj_handle *hdl; /**< Handle dirent points to */ struct mem_fsal_obj_handle *dir; /**< Dir containing dirent */ const char *d_name; /**< Name of dirent */ uint32_t d_index; /**< index in dir */ struct avltree_node avl_n; /**< Entry in dir's avl_name tree */ struct avltree_node avl_i; /**< Entry in dir's avl_index tree */ struct glist_head dlist; /**< Entry in hdl's dirents list */ }; static inline bool mem_unopenable_type(object_file_type_t type) { if ((type == SOCKET_FILE) || (type == CHARACTER_FILE) || (type == BLOCK_FILE)) { return true; } else { return false; } } void mem_handle_ops_init(struct fsal_obj_ops *ops); /* Internal MEM method linkage to export object */ fsal_status_t mem_create_export(struct fsal_module *fsal_hdl, void *parse_node, struct config_error_type *err_type, const struct fsal_up_vector *up_ops); #define mem_free_handle(h) _mem_free_handle(h, __func__, __LINE__) /** * @brief Free a MEM handle * * @note mfe_exp_lock MUST be held for write * @param[in] hdl Handle to free */ static inline void _mem_free_handle(struct mem_fsal_obj_handle *hdl, const char *func, int line) { #ifdef USE_LTTNG tracepoint(fsalmem, mem_free, func, line, hdl, hdl->m_name); #endif glist_del(&hdl->mfo_exp_entry); hdl->mfo_exp = NULL; if (hdl->m_name != NULL) { gsh_free(hdl->m_name); hdl->m_name = NULL; } gsh_free(hdl); } void mem_clean_export(struct mem_fsal_obj_handle *root); void mem_clean_all_dirents(struct mem_fsal_obj_handle *parent); /** * @brief FSAL Module wrapper for MEM */ struct mem_fsal_module { /** Module we're wrapping */ struct fsal_module fsal; /** Our FS INFO */ struct fsal_staticfsinfo_t fs_info; /** List of MEM exports. TODO Locking when we care */ struct glist_head mem_exports; /** Config - size of data in inode */ uint32_t inode_size; /** Config - Interval for UP call thread */ uint32_t up_interval; /** Next unused inode */ uint64_t next_inode; }; /* UP testing */ fsal_status_t mem_up_pkginit(void); fsal_status_t mem_up_pkgshutdown(void); extern struct mem_fsal_module MEM; nfs-ganesha-2.6.0/src/FSAL/FSAL_MEM/mem_main.c000066400000000000000000000126611324272410200204010ustar00rootroot00000000000000/* * vim:shiftwidth=8:tabstop=8: * * Copyright 2017-2018 Red Hat, Inc. * Author: Daniel Gryniewicz dang@redhat.com * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ /* main.c * Module core functions */ #include "config.h" #include "fsal.h" #include /* used for 'dirname' */ #include #include #include #include #include "FSAL/fsal_init.h" #include "mem_int.h" #include "../fsal_private.h" /* MEM FSAL module private storage */ /* defined the set of attributes supported with POSIX */ #define MEM_SUPPORTED_ATTRIBUTES (ATTRS_POSIX) /* my module private storage */ struct mem_fsal_module MEM; const char memname[] = "MEM"; /* filesystem info for MEM */ static struct fsal_staticfsinfo_t default_mem_info = { .maxfilesize = UINT64_MAX, .maxlink = 0, .maxnamelen = MAXNAMLEN, .maxpathlen = MAXPATHLEN, .no_trunc = true, .chown_restricted = true, .case_insensitive = false, .case_preserving = true, .link_support = true, .symlink_support = true, .lock_support = true, .lock_support_async_block = false, .named_attr = false, .unique_handles = true, .lease_time = {10, 0}, .acl_support = 0, .cansettime = true, .homogenous = true, .supported_attrs = MEM_SUPPORTED_ATTRIBUTES, .maxread = FSAL_MAXIOSIZE, .maxwrite = FSAL_MAXIOSIZE, .umask = 0, .auth_exportpath_xdev = false, .xattr_access_rights = 0400, /* root=RW, owner=R */ .link_supports_permission_checks = false, }; static struct config_item mem_items[] = { CONF_ITEM_UI32("Inode_Size", 0, 0x200000, 0, mem_fsal_module, inode_size), CONF_ITEM_UI32("Up_Test_Interval", 0, UINT32_MAX, 0, mem_fsal_module, up_interval), CONFIG_EOL }; static struct config_block mem_block = { .dbus_interface_name = "org.ganesha.nfsd.config.fsal.mem", .blk_desc.name = "MEM", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = noop_conf_init, .blk_desc.u.blk.params = mem_items, .blk_desc.u.blk.commit = noop_conf_commit }; /* private helper for export object */ struct fsal_staticfsinfo_t *mem_staticinfo(struct fsal_module *hdl) { struct mem_fsal_module *myself; myself = container_of(hdl, struct mem_fsal_module, fsal); return &myself->fs_info; } /* Initialize mem fs info */ static fsal_status_t mem_init_config(struct fsal_module *fsal_hdl, config_file_t config_struct, struct config_error_type *err_type) { struct mem_fsal_module *mem_me = container_of(fsal_hdl, struct mem_fsal_module, fsal); fsal_status_t status = {0, 0}; LogDebug(COMPONENT_FSAL, "MEM module setup."); /* get a copy of the defaults */ mem_me->fs_info = default_mem_info; /* if we have fsal specific params, do them here * fsal_hdl->name is used to find the block containing the * params. */ (void) load_config_from_parse(config_struct, &mem_block, mem_me, true, err_type); if (!config_error_is_harmless(err_type)) return fsalstat(ERR_FSAL_INVAL, 0); /* Initialize UP calls */ status = mem_up_pkginit(); if (FSAL_IS_ERROR(status)) { LogMajor(COMPONENT_FSAL, "Failed to initialize FSAL_MEM UP package %s", fsal_err_txt(status)); return status; } display_fsinfo(&mem_me->fs_info); LogFullDebug(COMPONENT_FSAL, "Supported attributes constant = 0x%" PRIx64, (uint64_t) MEM_SUPPORTED_ATTRIBUTES); LogFullDebug(COMPONENT_FSAL, "Supported attributes default = 0x%" PRIx64, default_mem_info.supported_attrs); LogDebug(COMPONENT_FSAL, "FSAL INIT: Supported attributes mask = 0x%" PRIx64, mem_me->fs_info.supported_attrs); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* Module initialization. * Called by dlopen() to register the module * keep a private pointer to me in myself */ /* linkage to the exports and handle ops initializers */ /** * @brief Initialize and register the FSAL * * This function initializes the FSAL module handle. It exists solely to * produce a properly constructed FSAL module handle. */ MODULE_INIT void init(void) { int retval; struct fsal_module *myself = &MEM.fsal; /* register_fsal seems to expect zeroed memory. */ memset(myself, 0, sizeof(*myself)); retval = register_fsal(myself, memname, FSAL_MAJOR_VERSION, FSAL_MINOR_VERSION, FSAL_ID_NO_PNFS); if (retval != 0) { LogCrit(COMPONENT_FSAL, "MEM module failed to register."); } myself->m_ops.create_export = mem_create_export; myself->m_ops.init_config = mem_init_config; glist_init(&MEM.mem_exports); MEM.next_inode = 0xc0ffee; } MODULE_FINI void finish(void) { int retval; LogDebug(COMPONENT_FSAL, "MEM module finishing."); /* Shutdown UP calls */ mem_up_pkgshutdown(); retval = unregister_fsal(&MEM.fsal); if (retval != 0) { LogCrit(COMPONENT_FSAL, "Unable to unload MEM FSAL. Dying with extreme prejudice."); abort(); } } nfs-ganesha-2.6.0/src/FSAL/FSAL_MEM/mem_up.c000066400000000000000000000166321324272410200201030ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright © 2017, Red Hat, Inc. * Author: Daniel Gryniewicz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @file FSAL_MEM/mem_up.c * @author Daniel Gryniewicz * * @brief Upcalls * * Implement upcalls for testing purposes */ #include "config.h" #include #include #include "fsal.h" #include "fsal_convert.h" #include "mem_int.h" static struct fridgethr *mem_up_fridge; /** * @brief Invalidate an object * * This function sends an invalidate for an object. The object itself is not * really deleted, since there's no way to get it back, but it should allow * testing of the invalidate UP call. * * @param[in] mfe MEM export owning handle * @param[in] hdl Handle to invalidate */ static void mem_invalidate(struct mem_fsal_export *mfe, struct mem_fsal_obj_handle *hdl) { const struct fsal_up_vector *up_ops = mfe->export.up_ops; fsal_status_t status; struct gsh_buffdesc fh_desc; LogFullDebug(COMPONENT_FSAL_UP, "invalidating %s", hdl->m_name); hdl->obj_handle.obj_ops.handle_to_key(&hdl->obj_handle, &fh_desc); status = up_ops->invalidate(up_ops, &fh_desc, FSAL_UP_INVALIDATE_CACHE); if (FSAL_IS_ERROR(status)) { LogMajor(COMPONENT_FSAL_UP, "error invalidating %s: %s", hdl->m_name, fsal_err_txt(status)); } } /** * @brief Invalidate and close an object * * This function sends an invalidate_close for an object. The object itself is * not really deleted, since there's no way to get it back, but it should allow * testing of the invalidate_close UP call. * * @param[in] mfe MEM export owning handle * @param[in] hdl Handle to invalidate */ static void mem_invalidate_close(struct mem_fsal_export *mfe, struct mem_fsal_obj_handle *hdl) { const struct fsal_up_vector *up_ops = mfe->export.up_ops; fsal_status_t status; struct gsh_buffdesc fh_desc; LogFullDebug(COMPONENT_FSAL_UP, "invalidate_closing %s", hdl->m_name); hdl->obj_handle.obj_ops.handle_to_key(&hdl->obj_handle, &fh_desc); status = up_ops->invalidate_close(up_ops, &fh_desc, FSAL_UP_INVALIDATE_CACHE); if (FSAL_IS_ERROR(status)) { LogMajor(COMPONENT_FSAL_UP, "error invalidate_closing %s: %s", hdl->m_name, fsal_err_txt(status)); } } /** * @brief Update an object * * This function sends an update for an object. In this case, we update some of * the times, just so something changed. * * @param[in] mfe MEM export owning handle * @param[in] hdl Handle to update */ static void mem_update(struct mem_fsal_export *mfe, struct mem_fsal_obj_handle *hdl) { const struct fsal_up_vector *up_ops = mfe->export.up_ops; fsal_status_t status; struct gsh_buffdesc fh_desc; struct attrlist attrs; LogFullDebug(COMPONENT_FSAL_UP, "updating %s", hdl->m_name); hdl->obj_handle.obj_ops.handle_to_key(&hdl->obj_handle, &fh_desc); fsal_prepare_attrs(&attrs, 0); /* Set CTIME */ now(&hdl->attrs.ctime); attrs.ctime = hdl->attrs.ctime; /* struct copy */ FSAL_SET_MASK(attrs.valid_mask, ATTR_CTIME); /* Set change and chgtime */ hdl->attrs.chgtime = attrs.ctime; /* struct copy */ attrs.chgtime = hdl->attrs.chgtime; /* struct copy */ FSAL_SET_MASK(attrs.valid_mask, ATTR_CHGTIME); hdl->attrs.change++; attrs.change = hdl->attrs.change; FSAL_SET_MASK(attrs.valid_mask, ATTR_CHANGE); status = up_ops->update(up_ops, &fh_desc, &attrs, fsal_up_update_null); if (FSAL_IS_ERROR(status)) { LogMajor(COMPONENT_FSAL_UP, "error updating %s: %s", hdl->m_name, fsal_err_txt(status)); } } /** * @brief Select a random obj from an export * * @param[in] mfe Export to select from * @return Obj on success, NULL on failure */ struct mem_fsal_obj_handle * mem_rand_obj(struct mem_fsal_export *mfe) { struct mem_fsal_obj_handle *res = NULL; struct glist_head *glist, *glistn; uint32_t n = 2; if (glist_empty(&mfe->mfe_objs)) return NULL; PTHREAD_RWLOCK_rdlock(&mfe->mfe_exp_lock); glist_for_each_safe(glist, glistn, &mfe->mfe_objs) { if (res == NULL) { /* Grab first entry */ res = glist_entry(glist, struct mem_fsal_obj_handle, mfo_exp_entry); continue; } if (rand() % n == 0) { /* Replace with current */ res = glist_entry(glist, struct mem_fsal_obj_handle, mfo_exp_entry); break; } n++; } PTHREAD_RWLOCK_unlock(&mfe->mfe_exp_lock); return res; } /** * @brief Run an iteration of the UP call thread * * Each iteration exercises various UP calls. * * - Pick a random obj in each export, and invalidate it * * @param[in] ctx Thread context * @return Return description */ static void mem_up_run(struct fridgethr_context *ctx) { struct glist_head *glist, *glistn; glist_for_each_safe(glist, glistn, &MEM.mem_exports) { struct mem_fsal_export *mfe; struct mem_fsal_obj_handle *hdl; mfe = glist_entry(glist, struct mem_fsal_export, export_entry); /* Update a handle */ hdl = mem_rand_obj(mfe); if (hdl) mem_update(mfe, hdl); /* Invalidate a handle */ hdl = mem_rand_obj(mfe); if (hdl) mem_invalidate(mfe, hdl); /* Invalidate and close a handle */ hdl = mem_rand_obj(mfe); if (hdl) mem_invalidate_close(mfe, hdl); } } /** * Initialize subsystem */ fsal_status_t mem_up_pkginit(void) { /* Return code from system calls */ int code = 0; struct fridgethr_params frp; if (MEM.up_interval == 0) { /* Don't run up-thread */ return fsalstat(ERR_FSAL_NO_ERROR, 0); } if (mem_up_fridge) { /* Already initialized */ return fsalstat(ERR_FSAL_NO_ERROR, 0); } memset(&frp, 0, sizeof(struct fridgethr_params)); frp.thr_max = 1; frp.thr_min = 1; frp.thread_delay = MEM.up_interval; frp.flavor = fridgethr_flavor_looper; /* spawn MEM_UP background thread */ code = fridgethr_init(&mem_up_fridge, "MEM_UP_fridge", &frp); if (code != 0) { LogMajor(COMPONENT_FSAL_UP, "Unable to initialize MEM_UP fridge, error code %d.", code); return posix2fsal_status(code); } code = fridgethr_submit(mem_up_fridge, mem_up_run, NULL); if (code != 0) { LogMajor(COMPONENT_FSAL_UP, "Unable to start MEM_UP thread, error code %d.", code); return fsalstat(posix2fsal_error(code), code); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * Shutdown subsystem * * @return FSAL status */ fsal_status_t mem_up_pkgshutdown(void) { if (!mem_up_fridge) { /* Interval wasn't configured */ return fsalstat(ERR_FSAL_NO_ERROR, 0); } int rc = fridgethr_sync_command(mem_up_fridge, fridgethr_comm_stop, 120); if (rc == ETIMEDOUT) { LogMajor(COMPONENT_FSAL_UP, "Shutdown timed out, cancelling threads."); fridgethr_cancel(mem_up_fridge); } else if (rc != 0) { LogMajor(COMPONENT_FSAL_UP, "Failed shutting down MEM_UP thread: %d", rc); } fridgethr_destroy(mem_up_fridge); mem_up_fridge = NULL; return fsalstat(posix2fsal_error(rc), rc); } nfs-ganesha-2.6.0/src/FSAL/FSAL_PROXY/000077500000000000000000000000001324272410200167505ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/FSAL/FSAL_PROXY/CMakeLists.txt000066400000000000000000000014131324272410200215070ustar00rootroot00000000000000add_definitions( -D__USE_GNU -D_GNU_SOURCE ) ########### next target ############### SET(fsalproxy_LIB_SRCS handle.c main.c export.c xattrs.c ) if(PROXY_HANDLE_MAPPING) SET(fsalproxy_LIB_SRCS ${fsalproxy_LIB_SRCS} handle_mapping/handle_mapping.c handle_mapping/handle_mapping_db.c ) endif(PROXY_HANDLE_MAPPING) add_library(fsalproxy MODULE ${fsalproxy_LIB_SRCS}) add_sanitizers(fsalproxy) target_link_libraries(fsalproxy ${SYSTEM_LIBRARIES} ) if(PROXY_HANDLE_MAPPING) target_link_libraries(fsalproxy sqlite3) endif(PROXY_HANDLE_MAPPING) set_target_properties(fsalproxy PROPERTIES VERSION 4.2.0 SOVERSION 4) install(TARGETS fsalproxy COMPONENT fsal DESTINATION ${FSAL_DESTINATION} ) ########### install files ############### nfs-ganesha-2.6.0/src/FSAL/FSAL_PROXY/export.c000066400000000000000000000124051324272410200204370ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Max Matveev, 2012 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ /* Export-related methods */ #include "config.h" #include "fsal.h" #include #include #include "gsh_list.h" #include "FSAL/fsal_commonlib.h" #include "FSAL/fsal_config.h" #include "pxy_fsal_methods.h" #include "nfs_exports.h" #include "export_mgr.h" static void pxy_release(struct fsal_export *exp_hdl) { struct pxy_export *pxy_exp = container_of(exp_hdl, struct pxy_export, exp); fsal_detach_export(exp_hdl->fsal, &exp_hdl->exports); free_export_ops(exp_hdl); gsh_free(pxy_exp); } static bool pxy_get_supports(struct fsal_export *exp_hdl, fsal_fsinfo_options_t option) { struct pxy_fsal_module *pm = container_of(exp_hdl->fsal, struct pxy_fsal_module, module); return fsal_supports(&pm->fsinfo, option); } static uint64_t pxy_get_maxfilesize(struct fsal_export *exp_hdl) { struct pxy_fsal_module *pm = container_of(exp_hdl->fsal, struct pxy_fsal_module, module); return fsal_maxfilesize(&pm->fsinfo); } static uint32_t pxy_get_maxread(struct fsal_export *exp_hdl) { struct pxy_fsal_module *pm = container_of(exp_hdl->fsal, struct pxy_fsal_module, module); return fsal_maxread(&pm->fsinfo); } static uint32_t pxy_get_maxwrite(struct fsal_export *exp_hdl) { struct pxy_fsal_module *pm = container_of(exp_hdl->fsal, struct pxy_fsal_module, module); return fsal_maxwrite(&pm->fsinfo); } static uint32_t pxy_get_maxlink(struct fsal_export *exp_hdl) { struct pxy_fsal_module *pm = container_of(exp_hdl->fsal, struct pxy_fsal_module, module); return fsal_maxlink(&pm->fsinfo); } static uint32_t pxy_get_maxnamelen(struct fsal_export *exp_hdl) { struct pxy_fsal_module *pm = container_of(exp_hdl->fsal, struct pxy_fsal_module, module); return fsal_maxnamelen(&pm->fsinfo); } static uint32_t pxy_get_maxpathlen(struct fsal_export *exp_hdl) { struct pxy_fsal_module *pm = container_of(exp_hdl->fsal, struct pxy_fsal_module, module); return fsal_maxpathlen(&pm->fsinfo); } static struct timespec pxy_get_lease_time(struct fsal_export *exp_hdl) { struct pxy_fsal_module *pm = container_of(exp_hdl->fsal, struct pxy_fsal_module, module); return fsal_lease_time(&pm->fsinfo); } static fsal_aclsupp_t pxy_get_acl_support(struct fsal_export *exp_hdl) { struct pxy_fsal_module *pm = container_of(exp_hdl->fsal, struct pxy_fsal_module, module); return fsal_acl_support(&pm->fsinfo); } static attrmask_t pxy_get_supported_attrs(struct fsal_export *exp_hdl) { struct pxy_fsal_module *pm = container_of(exp_hdl->fsal, struct pxy_fsal_module, module); return fsal_supported_attrs(&pm->fsinfo); } static uint32_t pxy_get_umask(struct fsal_export *exp_hdl) { struct pxy_fsal_module *pm = container_of(exp_hdl->fsal, struct pxy_fsal_module, module); return fsal_umask(&pm->fsinfo); } static uint32_t pxy_get_xattr_access_rights(struct fsal_export *exp_hdl) { struct pxy_fsal_module *pm = container_of(exp_hdl->fsal, struct pxy_fsal_module, module); return fsal_xattr_access_rights(&pm->fsinfo); } void pxy_export_ops_init(struct export_ops *ops) { ops->release = pxy_release; ops->lookup_path = pxy_lookup_path; ops->wire_to_host = pxy_wire_to_host; ops->create_handle = pxy_create_handle; ops->get_fs_dynamic_info = pxy_get_dynamic_info; ops->fs_supports = pxy_get_supports; ops->fs_maxfilesize = pxy_get_maxfilesize; ops->fs_maxread = pxy_get_maxread; ops->fs_maxwrite = pxy_get_maxwrite; ops->fs_maxlink = pxy_get_maxlink; ops->fs_maxnamelen = pxy_get_maxnamelen; ops->fs_maxpathlen = pxy_get_maxpathlen; ops->fs_lease_time = pxy_get_lease_time; ops->fs_acl_support = pxy_get_acl_support; ops->fs_supported_attrs = pxy_get_supported_attrs; ops->fs_umask = pxy_get_umask; ops->fs_xattr_access_rights = pxy_get_xattr_access_rights; ops->alloc_state = pxy_alloc_state; ops->free_state = pxy_free_state; }; /* Here and not static because proxy.c needs this function * but we also need access to pxy_exp_ops - I'd rather * keep the later static then the former */ fsal_status_t pxy_create_export(struct fsal_module *fsal_hdl, void *parse_node, struct config_error_type *err_type, const struct fsal_up_vector *up_ops) { struct pxy_export *exp = gsh_calloc(1, sizeof(*exp)); struct pxy_fsal_module *pxy = container_of(fsal_hdl, struct pxy_fsal_module, module); fsal_export_init(&exp->exp); pxy_export_ops_init(&exp->exp.exp_ops); exp->info = &pxy->special; exp->exp.fsal = fsal_hdl; exp->exp.up_ops = up_ops; op_ctx->fsal_export = &exp->exp; return fsalstat(ERR_FSAL_NO_ERROR, 0); } nfs-ganesha-2.6.0/src/FSAL/FSAL_PROXY/fsal_internal.h000066400000000000000000000041631324272410200217460ustar00rootroot00000000000000/** * @file FSAL_PROXY/fsal_internal.h * @brief Extern definitions for variables that are * defined in fsal_internal.c. */ #include "fsal.h" #include "FSAL/common_functions.h" #include "nfs4.h" #ifndef FSAL_INTERNAL_H #define FSAL_INTERNAL_H /* the following variables must not be defined in fsal_internal.c */ #ifndef FSAL_INTERNAL_C #define FSAL_PROXY_OWNER_LEN 256 #endif /** * fsal_do_log: * Indicates if an FSAL error has to be traced * into its log file in the NIV_EVENT level. * (in the other cases, return codes are only logged * in the NIV_FULL_DEBUG logging lovel). */ bool fsal_do_log(fsal_status_t status); void fsal_interval_proxy_fsalattr2bitmap4(fsal_attrib_list_t *pfsalattr, bitmap4 *pbitmap); /* * A few functions dedicated in proxy related information * management and conversion */ fsal_status_t fsal_internal_set_auth_gss(proxyfsal_op_context_t * p_thr_context); fsal_status_t fsal_internal_proxy_error_convert(nfsstat4 nfsstatus, int indexfunc); int fsal_internal_proxy_create_fh(nfs_fh4 *pnfs4_handle, fsal_nodetype_t type, fsal_u64_t fileid, fsal_handle_t *pfsal_handle); int fsal_internal_proxy_extract_fh(nfs_fh4 *pnfs4_handle, fsal_handle_t *pfsal_handle); int fsal_internal_proxy_fsal_name_2_utf8(fsal_name_t *pname, utf8string *utf8str); int fsal_internal_proxy_fsal_path_2_utf8(fsal_path_t *ppath, utf8string *utf8str); int fsal_internal_proxy_fsal_utf8_2_name(fsal_name_t *pname, utf8string *utf8str); int fsal_internal_proxy_fsal_utf8_2_path(fsal_path_t *ppath, utf8string *utf8str); int proxy_Fattr_To_FSAL_attr(fsal_attrib_list_t *pFSAL_attr, proxyfsal_handle_t *phandle, fattr4 *Fattr); fsal_status_t FSAL_proxy_setclientid(proxyfsal_op_context_t *p_context); fsal_status_t FSAL_proxy_setclientid_renego(proxyfsal_op_context_t *p_context); fsal_status_t fsal_proxy_create_rpc_clnt(proxyfsal_op_context_t *); int fsal_internal_ClientReconnect(proxyfsal_op_context_t *p_thr_context); fsal_status_t FSAL_proxy_open_confirm(proxyfsal_file_t *pfd); void *FSAL_proxy_change_user(proxyfsal_op_context_t *p_thr_context); #endif nfs-ganesha-2.6.0/src/FSAL/FSAL_PROXY/fsal_nfsv4_macros.h000066400000000000000000000444711324272410200225440ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: */ /** * @file fsal_nfsv4_macros.h * @author Author: deniel * @date 06/05/2007 * @brief Usefull macros to manage NFSv4 call from FSAL_PROXY * * */ #ifndef _FSAL_NFSV4_MACROS_H #define _FSAL_NFSV4_MACROS_H #include "gsh_rpc.h" #include "nfs4.h" #include "fsal.h" #if 0 #include "fsal_internal.h" #include "fsal_convert.h" #include "fsal_common.h" #endif #ifndef ARRAY_SIZE #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) #endif #define TIMEOUTRPC {2, 0} #define PRINT_HANDLE(tag, handle) \ do { \ if (isFullDebug(COMPONENT_FSAL)) { \ char outstr[1024]; \ snprintHandle(outstr, 1024, handle); \ LogFullDebug(COMPONENT_FSAL, \ "============> %s : handle=%s\n", tag, outstr); \ } \ } while (0) /* Free a compound */ #define COMPOUNDV4_ARG_FREE \ do {gsh_free(argcompound.argarray_val); } while (0) /* OP specific macros */ /** * Notice about NFS4_OP_SEQUENCE argop filling : * As rpc_context and slot are mutualized, sa_slotid and related sa_sequenceid * are place holder filled later on pxy_compoundv4_execute function, only when * the free pxy_rpc_io_context is chosen. */ #define COMPOUNDV4_ARG_ADD_OP_SEQUENCE(opcnt, argarray, sessionid, nb_slot) \ do { \ nfs_argop4 *op = argarray + opcnt; opcnt++; \ op->argop = NFS4_OP_SEQUENCE; \ memcpy(op->nfs_argop4_u.opsequence.sa_sessionid, sessionid, \ sizeof(sessionid4)); \ op->nfs_argop4_u.opsequence.sa_highest_slotid = nb_slot - 1; \ op->nfs_argop4_u.opsequence.sa_cachethis = false; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_GLOBAL_RECLAIM_COMPLETE(opcnt, argarray) \ do { \ nfs_argop4 *op = argarray + opcnt; opcnt++; \ op->argop = NFS4_OP_RECLAIM_COMPLETE; \ op->nfs_argop4_u.opreclaim_complete.rca_one_fs = false; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_CREATE_SESSION(opcnt, argarray, cid, seqid, info)\ do { \ callback_sec_parms4 csa_sec_parms_val; \ struct channel_attrs4 *fore_attrs; \ struct channel_attrs4 *back_attrs; \ CREATE_SESSION4args *opcreate_session; \ \ nfs_argop4 *op = argarray + opcnt; opcnt++; \ op->argop = NFS4_OP_CREATE_SESSION; \ opcreate_session = &op->nfs_argop4_u.opcreate_session; \ opcreate_session->csa_clientid = cid; \ opcreate_session->csa_sequence = seqid; \ opcreate_session->csa_flags = CREATE_SESSION4_FLAG_CONN_BACK_CHAN; \ fore_attrs = &opcreate_session->csa_fore_chan_attrs; \ fore_attrs->ca_headerpadsize = 0; \ fore_attrs->ca_maxrequestsize = info->srv_sendsize; \ fore_attrs->ca_maxresponsesize = info->srv_recvsize; \ fore_attrs->ca_maxresponsesize_cached = info->srv_recvsize; \ fore_attrs->ca_maxoperations = NB_MAX_OPERATIONS; \ fore_attrs->ca_maxrequests = NB_RPC_SLOT; \ fore_attrs->ca_rdma_ird.ca_rdma_ird_len = 0; \ fore_attrs->ca_rdma_ird.ca_rdma_ird_val = NULL; \ back_attrs = &opcreate_session->csa_back_chan_attrs; \ back_attrs->ca_headerpadsize = 0; \ back_attrs->ca_maxrequestsize = info->srv_recvsize; \ back_attrs->ca_maxresponsesize = info->srv_sendsize; \ back_attrs->ca_maxresponsesize_cached = info->srv_recvsize; \ back_attrs->ca_maxoperations = NB_MAX_OPERATIONS; \ back_attrs->ca_maxrequests = NB_RPC_SLOT; \ back_attrs->ca_rdma_ird.ca_rdma_ird_len = 0; \ back_attrs->ca_rdma_ird.ca_rdma_ird_val = NULL; \ opcreate_session->csa_cb_program = info->srv_prognum; \ opcreate_session->csa_sec_parms.csa_sec_parms_len = 1; \ csa_sec_parms_val.cb_secflavor = AUTH_NONE; \ opcreate_session->csa_sec_parms.csa_sec_parms_val = &csa_sec_parms_val;\ } while (0) #define COMPOUNDV4_ARG_ADD_OP_PUTROOTFH(opcnt, argarray) \ do { \ argarray[opcnt].argop = NFS4_OP_PUTROOTFH; \ opcnt++; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_OPEN_CONFIRM(argcompound, __openseqid, \ __other, __seqid) \ do { \ argcompound.argarray.argarray_val[\ argcompound.argarray.argarray_len].argop \ = NFS4_OP_OPEN_CONFIRM; \ argcompound.argarray.argarray_val[\ argcompound.argarray.argarray_len].nfs_argop4_u.\ opopen_confirm.seqid = __seqid; \ argcompound.argarray.argarray_val[\ argcompound.argarray.argarray_len].nfs_argop4_u.\ opopen_confirm.open_stateid.seqid = __openseqid; \ memcpy(argcompound.argarray.argarray_val[\ argcompound.argarray.argarray_len].nfs_argop4_u.\ opopen_confirm.open_stateid.other, \ _other, 12); \ argcompound.argarray.argarray_len += 1; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_OPEN_NOCREATE(argcompound, __seqid, inclientid, \ inaccess, inname, __owner_val, \ __owner_len) \ do { \ argcompound.argarray.argarray_val[\ argcompound.argarray.argarray_len].argop = NFS4_OP_OPEN; \ argcompound.argarray.argarray_val[\ argcompound.argarray.argarray_len].nfs_argop4_u.opopen.\ seqid = __seqid; \ argcompound.argarray.argarray_val[\ argcompound.argarray.argarray_len].nfs_argop4_u.opopen.\ share_access = OPEN4_SHARE_ACCESS_BOTH; \ argcompound.argarray.argarray_val[\ argcompound.argarray.argarray_len].nfs_argop4_u.opopen.\ share_deny = OPEN4_SHARE_DENY_NONE; \ argcompound.argarray.argarray_val[\ argcompound.argarray.argarray_len].nfs_argop4_u.opopen.\ owner.clientid = inclientid; \ argcompound.argarray.argarray_val[\ argcompound.argarray.argarray_len].nfs_argop4_u.opopen.\ owner.owner.owner_len = __owner_len; \ argcompound.argarray.argarray_val[\ argcompound.argarray.argarray_len].nfs_argop4_u.opopen.\ owner.owner.owner_val = __owner_val; \ argcompound.argarray.argarray_val[\ argcompound.argarray.argarray_len].nfs_argop4_u.opopen.\ openhow.opentype = OPEN4_NOCREATE; \ argcompound.argarray.argarray_val[\ argcompound.argarray.argarray_len].nfs_argop4_u.opopen.\ claim.claim = CLAIM_NULL; \ argcompound.argarray.argarray_val[\ argcompound.argarray.argarray_len].nfs_argop4_u.opopen.\ claim.open_claim4_u.file = inname; \ argcompound.argarray.argarray_len += 1; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_CLOSE_4_1(opcnt, argarray, __stateid) \ do { \ nfs_argop4 *op = argarray + opcnt; opcnt++; \ op->argop = NFS4_OP_CLOSE; \ op->nfs_argop4_u.opclose.open_stateid.seqid \ = __stateid.seqid; \ memcpy(op->nfs_argop4_u.opclose.open_stateid.other, \ __stateid.other, 12); \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_CLOSE_4_1_STATELESS(opcnt, argarray)\ do { \ nfs_argop4 *op = argarray + opcnt; opcnt++; \ op->argop = NFS4_OP_CLOSE; \ memset(&op->nfs_argop4_u.opclose.open_stateid, 0, \ sizeof(stateid4)); \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_GETATTR(opcnt, argarray, bitmap) \ do { \ nfs_argop4 *op = argarray + opcnt; opcnt++; \ op->argop = NFS4_OP_GETATTR; \ op->nfs_argop4_u.opgetattr.attr_request = bitmap; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_SETATTR(opcnt, argarray, inattr, __other) \ do { \ nfs_argop4 *op = argarray + opcnt; opcnt++; \ op->argop = NFS4_OP_SETATTR; \ op->nfs_argop4_u.opsetattr.stateid.seqid = 0; \ memcpy(op->nfs_argop4_u.opsetattr.stateid.other, __other, \ sizeof(op->nfs_argop4_u.opsetattr.stateid.other)); \ op->nfs_argop4_u.opsetattr.obj_attributes = inattr; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_SETATTR_STATELESS(opcnt, argarray, inattr)\ do { \ nfs_argop4 *op = argarray + opcnt; opcnt++; \ op->argop = NFS4_OP_SETATTR; \ memset(&op->nfs_argop4_u.opsetattr.stateid, 0, sizeof(stateid4)); \ op->nfs_argop4_u.opsetattr.obj_attributes = inattr; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_GETFH(opcnt, argarray) \ do { \ argarray[opcnt].argop = NFS4_OP_GETFH; \ opcnt++; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_PUTFH(opcnt, argarray, nfs4fh) \ do { \ nfs_argop4 *op = argarray + opcnt; opcnt++; \ op->argop = NFS4_OP_PUTFH; \ op->nfs_argop4_u.opputfh.object = nfs4fh; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_LOOKUP(opcnt, argarray, name) \ do { \ nfs_argop4 *op = argarray + opcnt; opcnt++; \ op->argop = NFS4_OP_LOOKUP; \ op->nfs_argop4_u.oplookup.objname.utf8string_val = (char *)name; \ op->nfs_argop4_u.oplookup.objname.utf8string_len = strlen(name); \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_LOOKUPP(opcnt, argarray) \ do { \ argarray[opcnt].argop = NFS4_OP_LOOKUPP; \ opcnt++; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_SETCLIENTID(argcompound, inclient, incallback) \ do { \ argcompound.argarray.argarray_val[\ argcompound.argarray.argarray_len].argop \ = NFS4_OP_SETCLIENTID; \ argcompound.argarray.argarray_val[\ argcompound.argarray.argarray_len].nfs_argop4_u.\ opsetclientid.client = inclient; \ argcompound.argarray.argarray_val[\ argcompound.argarray.argarray_len].nfs_argop4_u.\ opsetclientid.callback = incallback; \ argcompound.argarray.argarray_val[\ argcompound.argarray.argarray_len].nfs_argop4_u.\ opsetclientid.callback_ident = 0; \ argcompound.argarray.argarray_len += 1; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_SETCLIENTID_CONFIRM(argcompound, inclientid, \ inverifier) \ do { \ argcompound.argarray.argarray_val[\ argcompound.argarray.argarray_len].argop \ = NFS4_OP_SETCLIENTID_CONFIRM; \ argcompound.argarray.argarray_val[\ argcompound.argarray.argarray_len].nfs_argop4_u.\ opsetclientid_confirm.clientid = inclientid; \ strncpy(argcompound.argarray.argarray_val[\ argcompound.argarray.argarray_len].nfs_argop4_u.\ opsetclientid_confirm.setclientid_confirm, \ inverifier, NFS4_VERIFIER_SIZE); \ argcompound.argarray.argarray_len += 1; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_ACCESS(argcompound, inaccessflag) \ do { \ argcompound.argarray.argarray_val[argcompound.argarray.argarray_len]\ .argop = NFS4_OP_ACCESS; \ argcompound.argarray.argarray_val[argcompound.argarray.argarray_len]\ .nfs_argop4_u.opaccess.access = inaccessflag; \ argcompound.argarray.argarray_len += 1; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_READDIR(opcnt, args, c4, inbitmap) \ do { \ nfs_argop4 *op = args + opcnt; opcnt++; \ op->argop = NFS4_OP_READDIR; \ op->nfs_argop4_u.opreaddir.cookie = c4; \ memset(&op->nfs_argop4_u.opreaddir.cookieverf, \ 0, NFS4_VERIFIER_SIZE); \ op->nfs_argop4_u.opreaddir.dircount = 2048; \ op->nfs_argop4_u.opreaddir.maxcount = 4096; \ op->nfs_argop4_u.opreaddir.attr_request = inbitmap; \ } while (0) #define COMPOUNDV4_ARGS_ADD_OP_OPEN_4_1(opcnt, args, __share_access, \ __share_deny, __owner_val, \ __owner_len, __openhow, __claim) \ do { \ nfs_argop4 *op = args + opcnt; opcnt++; \ op->argop = NFS4_OP_OPEN; \ op->nfs_argop4_u.opopen.share_access = __share_access; \ op->nfs_argop4_u.opopen.share_deny = __share_deny; \ op->nfs_argop4_u.opopen.owner.owner.owner_len = __owner_len; \ op->nfs_argop4_u.opopen.owner.owner.owner_val = __owner_val; \ op->nfs_argop4_u.opopen.openhow = __openhow; \ op->nfs_argop4_u.opopen.claim = __claim; \ } while (0) #define COMPOUNDV4_ARGS_ADD_OP_OPEN(opcnt, args, oo_seqid, __share_access,\ __share_deny, inclientid, __owner_val,\ __owner_len, __openhow, __claim) \ do { \ nfs_argop4 *op = args + opcnt; opcnt++; \ op->argop = NFS4_OP_OPEN; \ op->nfs_argop4_u.opopen.seqid = oo_seqid; \ op->nfs_argop4_u.opopen.share_access = __share_access; \ op->nfs_argop4_u.opopen.share_deny = __share_deny; \ op->nfs_argop4_u.opopen.owner.clientid = inclientid; \ op->nfs_argop4_u.opopen.owner.owner.owner_len = __owner_len; \ op->nfs_argop4_u.opopen.owner.owner.owner_val = __owner_val; \ op->nfs_argop4_u.opopen.openhow = __openhow; \ op->nfs_argop4_u.opopen.claim = __claim; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_OPEN_CREATE(opcnt, args, inname, inattrs, \ inclientid, __owner_val, \ __owner_len) \ do { \ nfs_argop4 *op = args + opcnt; opcnt++; \ op->argop = NFS4_OP_OPEN; \ op->nfs_argop4_u.opopen.share_access = OPEN4_SHARE_ACCESS_BOTH; \ op->nfs_argop4_u.opopen.share_deny = OPEN4_SHARE_DENY_NONE; \ op->nfs_argop4_u.opopen.owner.clientid = inclientid; \ op->nfs_argop4_u.opopen.owner.owner.owner_len = __owner_len; \ op->nfs_argop4_u.opopen.owner.owner.owner_val = __owner_val; \ op->nfs_argop4_u.opopen.openhow.opentype = OPEN4_CREATE; \ op->nfs_argop4_u.opopen.openhow.openflag4_u.how.mode = GUARDED4; \ op->nfs_argop4_u.opopen.openhow.openflag4_u.how.\ createhow4_u.createattrs = inattrs; \ op->nfs_argop4_u.opopen.claim.claim = CLAIM_NULL; \ op->nfs_argop4_u.opopen.claim.open_claim4_u.file.utf8string_val \ = inname; \ op->nfs_argop4_u.opopen.claim.open_claim4_u.file.utf8string_len = \ strlen(inname); \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_MKDIR(opcnt, argarray, inname, inattrs) \ do { \ nfs_argop4 *op = argarray + opcnt; opcnt++; \ op->argop = NFS4_OP_CREATE; \ op->nfs_argop4_u.opcreate.objtype.type = NF4DIR; \ op->nfs_argop4_u.opcreate.objname.utf8string_val = inname; \ op->nfs_argop4_u.opcreate.objname.utf8string_len = strlen(inname); \ op->nfs_argop4_u.opcreate.createattrs = inattrs; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_CREATE(opcnt, arg, inname, nf4typ, inattrs, \ specd) \ do { \ nfs_argop4 *op = arg + opcnt; opcnt++; \ op->argop = NFS4_OP_CREATE; \ op->nfs_argop4_u.opcreate.objtype.type = nf4typ; \ op->nfs_argop4_u.opcreate.objtype.createtype4_u.devdata = specd; \ op->nfs_argop4_u.opcreate.objname.utf8string_val = inname; \ op->nfs_argop4_u.opcreate.objname.utf8string_len = strlen(inname); \ op->nfs_argop4_u.opcreate.createattrs = inattrs; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_SYMLINK(opcnt, args, inname, incontent, inattrs)\ do { \ nfs_argop4 *op = args + opcnt; opcnt++; \ op->argop = NFS4_OP_CREATE; \ op->nfs_argop4_u.opcreate.objtype.type = NF4LNK; \ op->nfs_argop4_u.opcreate.objtype.createtype4_u.\ linkdata.utf8string_val = incontent; \ op->nfs_argop4_u.opcreate.objtype.createtype4_u.\ linkdata.utf8string_len = strlen(incontent); \ op->nfs_argop4_u.opcreate.objname.utf8string_val = inname; \ op->nfs_argop4_u.opcreate.objname.utf8string_len = strlen(inname); \ op->nfs_argop4_u.opcreate.createattrs = inattrs; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_LINK(opcnt, argarray, inname) \ do { \ nfs_argop4 *op = argarray+opcnt; opcnt++; \ op->argop = NFS4_OP_LINK; \ op->nfs_argop4_u.oplink.newname.utf8string_val = inname; \ op->nfs_argop4_u.oplink.newname.utf8string_len = strlen(inname); \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_REMOVE(opcnt, argarray, inname) \ do { \ nfs_argop4 *op = argarray+opcnt; opcnt++; \ op->argop = NFS4_OP_REMOVE; \ op->nfs_argop4_u.opremove.target.utf8string_val = inname; \ op->nfs_argop4_u.opremove.target.utf8string_len = strlen(inname); \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_RENAME(opcnt, argarray, inoldname, innewname) \ do { \ nfs_argop4 *op = argarray+opcnt; opcnt++; \ op->argop = NFS4_OP_RENAME; \ op->nfs_argop4_u.oprename.oldname.utf8string_val = inoldname; \ op->nfs_argop4_u.oprename.oldname.utf8string_len = strlen(inoldname); \ op->nfs_argop4_u.oprename.newname.utf8string_val = innewname; \ op->nfs_argop4_u.oprename.newname.utf8string_len = strlen(innewname); \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_READLINK(opcnt, argarray) \ do { \ argarray[opcnt].argop = NFS4_OP_READLINK; \ opcnt++; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_SAVEFH(opcnt, argarray) \ do { \ argarray[opcnt].argop = NFS4_OP_SAVEFH; \ opcnt++; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_RESTOREFH(argcompound) \ do { \ argcompound.argarray.argarray_val[\ argcompound.argarray.argarray_len].argop \ = NFS4_OP_RESTOREFH; \ argcompound.argarray.argarray_len += 1; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_READ(opcnt, argarray, inoffset, incount, \ __other) \ do { \ nfs_argop4 *op = argarray+opcnt; opcnt++; \ op->argop = NFS4_OP_READ; \ op->nfs_argop4_u.opread.stateid.seqid = 0; \ memcpy(op->nfs_argop4_u.opread.stateid.other, __other, \ sizeof(op->nfs_argop4_u.opread.stateid.other)); \ op->nfs_argop4_u.opread.offset = inoffset; \ op->nfs_argop4_u.opread.count = incount; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_READ_STATELESS(opcnt, argarray, inoffset, \ incount) \ do { \ nfs_argop4 *op = argarray+opcnt; opcnt++; \ op->argop = NFS4_OP_READ; \ memset(&op->nfs_argop4_u.opread.stateid, 0, sizeof(stateid4)); \ op->nfs_argop4_u.opread.offset = inoffset; \ op->nfs_argop4_u.opread.count = incount; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_READ_BYPASS(opcnt, argarray, inoffset, incount) \ do { \ nfs_argop4 *op = argarray+opcnt; opcnt++; \ op->argop = NFS4_OP_READ; \ memset(&op->nfs_argop4_u.opread.stateid, 0xff, sizeof(stateid4)); \ op->nfs_argop4_u.opread.offset = inoffset; \ op->nfs_argop4_u.opread.count = incount; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_WRITE(opcnt, argarray, inoffset, inbuf, \ inlen, instable, __other) \ do { \ nfs_argop4 *op = argarray+opcnt; opcnt++; \ op->argop = NFS4_OP_WRITE; \ op->nfs_argop4_u.opwrite.stable = instable; \ op->nfs_argop4_u.opread.stateid.seqid = 0; \ memcpy(op->nfs_argop4_u.opwrite.stateid.other, __other, \ sizeof(op->nfs_argop4_u.opwrite.stateid.other)); \ op->nfs_argop4_u.opwrite.offset = inoffset; \ op->nfs_argop4_u.opwrite.data.data_val = inbuf; \ op->nfs_argop4_u.opwrite.data.data_len = inlen; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_WRITE_STATELESS(opcnt, argarray, inoffset,\ inbuf, inlen, instable) \ do { \ nfs_argop4 *op = argarray+opcnt; opcnt++; \ op->argop = NFS4_OP_WRITE; \ op->nfs_argop4_u.opwrite.stable = instable; \ memset(&op->nfs_argop4_u.opwrite.stateid, 0, sizeof(stateid4)); \ op->nfs_argop4_u.opwrite.offset = inoffset; \ op->nfs_argop4_u.opwrite.data.data_val = inbuf; \ op->nfs_argop4_u.opwrite.data.data_len = inlen; \ } while (0) #define COMPOUNDV4_ARG_ADD_OP_COMMIT(opcnt, argoparray, inoffset, inlen) \ do { \ nfs_argop4 *op = argoparray+opcnt; opcnt++; \ op->argop = NFS4_OP_COMMIT; \ op->nfs_argop4_u.opcommit.offset = inoffset; \ op->nfs_argop4_u.opcommit.count = inlen; \ } while (0) #define COMPOUNDV4_EXECUTE_SIMPLE(pcontext, argcompound, rescompound) \ clnt_call(pcontext->rpc_client, NFSPROC4_COMPOUND, \ (xdrproc_t)xdr_COMPOUND4args, (caddr_t)&argcompound, \ (xdrproc_t)xdr_COMPOUND4res, (caddr_t)&rescompound, \ timeout) #endif /* _FSAL_NFSV4_MACROS_H */ nfs-ganesha-2.6.0/src/FSAL/FSAL_PROXY/handle.c000066400000000000000000002460761324272410200203660ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Max Matveev, 2012 * Copyright CEA/DAM/DIF (2008) * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ /* Proxy handle methods */ #include "config.h" #include "fsal.h" #include #include #include #include #include #include "gsh_list.h" #include "abstract_atomic.h" #include "fsal_types.h" #include "FSAL/fsal_commonlib.h" #include "pxy_fsal_methods.h" #include "fsal_nfsv4_macros.h" #include "nfs_core.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "export_mgr.h" #include "common_utils.h" #define FSAL_PROXY_NFS_V4 4 #define FSAL_PROXY_NFS_V4_MINOR 1 #define NB_RPC_SLOT 16 #define NB_MAX_OPERATIONS 10 /** * pxy_clientid_mutex protects pxy_clientid, pxy_client_seqid, * pxy_client_sessionid, no_sessionid and cond_sessionid. */ static clientid4 pxy_clientid; static sequenceid4 pxy_client_seqid; static sessionid4 pxy_client_sessionid; static bool no_sessionid = true; static pthread_cond_t cond_sessionid = PTHREAD_COND_INITIALIZER; static pthread_mutex_t pxy_clientid_mutex = PTHREAD_MUTEX_INITIALIZER; static char pxy_hostname[MAXNAMLEN + 1]; static pthread_t pxy_recv_thread; static pthread_t pxy_renewer_thread; /** * listlock protects rpc_sock and rpc_xid values, sockless condition and * rpc_calls list. */ static struct glist_head rpc_calls; static int rpc_sock = -1; static uint32_t rpc_xid; static pthread_mutex_t listlock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t sockless = PTHREAD_COND_INITIALIZER; static bool close_thread; /* * context_lock protects free_contexts list and need_context condition. */ static struct glist_head free_contexts; static pthread_cond_t need_context = PTHREAD_COND_INITIALIZER; static pthread_mutex_t context_lock = PTHREAD_MUTEX_INITIALIZER; /* NB! nfs_prog is just an easy way to get this info into the call * It should really be fetched via export pointer */ /** * We mutualize rpc_context and slot NFSv4.1. */ struct pxy_rpc_io_context { pthread_mutex_t iolock; pthread_cond_t iowait; struct glist_head calls; uint32_t rpc_xid; bool iodone; int ioresult; unsigned int nfs_prog; unsigned int sendbuf_sz; unsigned int recvbuf_sz; char *sendbuf; char *recvbuf; slotid4 slotid; sequenceid4 seqid; }; /* Use this to estimate storage requirements for fattr4 blob */ struct pxy_fattr_storage { fattr4_type type; fattr4_change change_time; fattr4_size size; fattr4_fsid fsid; fattr4_filehandle filehandle; fattr4_fileid fileid; fattr4_mode mode; fattr4_numlinks numlinks; fattr4_owner owner; fattr4_owner_group owner_group; fattr4_space_used space_used; fattr4_time_access time_access; fattr4_time_metadata time_metadata; fattr4_time_modify time_modify; fattr4_rawdev rawdev; char padowner[MAXNAMLEN + 1]; char padgroup[MAXNAMLEN + 1]; char padfh[NFS4_FHSIZE]; }; #define FATTR_BLOB_SZ sizeof(struct pxy_fattr_storage) /* * This is what becomes an opaque FSAL handle for the upper layers. * * The type is a placeholder for future expansion. */ struct pxy_handle_blob { uint8_t len; uint8_t type; uint8_t bytes[0]; }; struct pxy_obj_handle { struct fsal_obj_handle obj; nfs_fh4 fh4; #ifdef PROXY_HANDLE_MAPPING nfs23_map_handle_t h23; #endif fsal_openflags_t openflags; struct pxy_handle_blob blob; }; static struct pxy_obj_handle *pxy_alloc_handle(struct fsal_export *exp, const nfs_fh4 *fh, fattr4 *obj_attributes, struct attrlist *attrs_out); struct pxy_state { struct state_t state; stateid4 stateid; }; struct state_t *pxy_alloc_state(struct fsal_export *exp_hdl, enum state_type state_type, struct state_t *related_state) { return init_state(gsh_calloc(1, sizeof(struct pxy_state)), exp_hdl, state_type, related_state); } void pxy_free_state(struct fsal_export *exp_hdl, struct state_t *state) { struct pxy_state *pxy_state_id = container_of(state, struct pxy_state, state); gsh_free(pxy_state_id); } #define FSAL_VERIFIER_T_TO_VERIFIER4(verif4, fsal_verif) \ do { \ BUILD_BUG_ON(sizeof(fsal_verifier_t) != sizeof(verifier4)); \ memcpy(verif4, fsal_verif, sizeof(fsal_verifier_t)); \ } while (0) static fsal_status_t nfsstat4_to_fsal(nfsstat4 nfsstatus) { switch (nfsstatus) { case NFS4ERR_SAME: case NFS4ERR_NOT_SAME: case NFS4_OK: return fsalstat(ERR_FSAL_NO_ERROR, (int)nfsstatus); case NFS4ERR_PERM: return fsalstat(ERR_FSAL_PERM, (int)nfsstatus); case NFS4ERR_NOENT: return fsalstat(ERR_FSAL_NOENT, (int)nfsstatus); case NFS4ERR_IO: return fsalstat(ERR_FSAL_IO, (int)nfsstatus); case NFS4ERR_NXIO: return fsalstat(ERR_FSAL_NXIO, (int)nfsstatus); case NFS4ERR_EXPIRED: case NFS4ERR_LOCKED: case NFS4ERR_SHARE_DENIED: case NFS4ERR_LOCK_RANGE: case NFS4ERR_OPENMODE: case NFS4ERR_FILE_OPEN: case NFS4ERR_ACCESS: case NFS4ERR_DENIED: return fsalstat(ERR_FSAL_ACCESS, (int)nfsstatus); case NFS4ERR_EXIST: return fsalstat(ERR_FSAL_EXIST, (int)nfsstatus); case NFS4ERR_XDEV: return fsalstat(ERR_FSAL_XDEV, (int)nfsstatus); case NFS4ERR_NOTDIR: return fsalstat(ERR_FSAL_NOTDIR, (int)nfsstatus); case NFS4ERR_ISDIR: return fsalstat(ERR_FSAL_ISDIR, (int)nfsstatus); case NFS4ERR_FBIG: return fsalstat(ERR_FSAL_FBIG, 0); case NFS4ERR_NOSPC: return fsalstat(ERR_FSAL_NOSPC, (int)nfsstatus); case NFS4ERR_ROFS: return fsalstat(ERR_FSAL_ROFS, (int)nfsstatus); case NFS4ERR_MLINK: return fsalstat(ERR_FSAL_MLINK, (int)nfsstatus); case NFS4ERR_NAMETOOLONG: return fsalstat(ERR_FSAL_NAMETOOLONG, (int)nfsstatus); case NFS4ERR_NOTEMPTY: return fsalstat(ERR_FSAL_NOTEMPTY, (int)nfsstatus); case NFS4ERR_DQUOT: return fsalstat(ERR_FSAL_DQUOT, (int)nfsstatus); case NFS4ERR_STALE: return fsalstat(ERR_FSAL_STALE, (int)nfsstatus); case NFS4ERR_NOFILEHANDLE: case NFS4ERR_BADHANDLE: return fsalstat(ERR_FSAL_BADHANDLE, (int)nfsstatus); case NFS4ERR_BAD_COOKIE: return fsalstat(ERR_FSAL_BADCOOKIE, (int)nfsstatus); case NFS4ERR_NOTSUPP: return fsalstat(ERR_FSAL_NOTSUPP, (int)nfsstatus); case NFS4ERR_TOOSMALL: return fsalstat(ERR_FSAL_TOOSMALL, (int)nfsstatus); case NFS4ERR_SERVERFAULT: return fsalstat(ERR_FSAL_SERVERFAULT, (int)nfsstatus); case NFS4ERR_BADTYPE: return fsalstat(ERR_FSAL_BADTYPE, (int)nfsstatus); case NFS4ERR_GRACE: case NFS4ERR_DELAY: return fsalstat(ERR_FSAL_DELAY, (int)nfsstatus); case NFS4ERR_FHEXPIRED: return fsalstat(ERR_FSAL_FHEXPIRED, (int)nfsstatus); case NFS4ERR_WRONGSEC: return fsalstat(ERR_FSAL_SEC, (int)nfsstatus); case NFS4ERR_SYMLINK: return fsalstat(ERR_FSAL_SYMLINK, (int)nfsstatus); case NFS4ERR_ATTRNOTSUPP: return fsalstat(ERR_FSAL_ATTRNOTSUPP, (int)nfsstatus); case NFS4ERR_BADNAME: return fsalstat(ERR_FSAL_BADNAME, (int)nfsstatus); case NFS4ERR_INVAL: case NFS4ERR_CLID_INUSE: case NFS4ERR_MOVED: case NFS4ERR_RESOURCE: case NFS4ERR_MINOR_VERS_MISMATCH: case NFS4ERR_STALE_CLIENTID: case NFS4ERR_STALE_STATEID: case NFS4ERR_OLD_STATEID: case NFS4ERR_BAD_STATEID: case NFS4ERR_BAD_SEQID: case NFS4ERR_RESTOREFH: case NFS4ERR_LEASE_MOVED: case NFS4ERR_NO_GRACE: case NFS4ERR_RECLAIM_BAD: case NFS4ERR_RECLAIM_CONFLICT: case NFS4ERR_BADXDR: case NFS4ERR_BADCHAR: case NFS4ERR_BAD_RANGE: case NFS4ERR_BADOWNER: case NFS4ERR_OP_ILLEGAL: case NFS4ERR_LOCKS_HELD: case NFS4ERR_LOCK_NOTSUPP: case NFS4ERR_DEADLOCK: case NFS4ERR_ADMIN_REVOKED: case NFS4ERR_CB_PATH_DOWN: default: return fsalstat(ERR_FSAL_INVAL, (int)nfsstatus); } } #define PXY_ATTR_BIT(b) (1U << b) #define PXY_ATTR_BIT2(b) (1U << (b - 32)) static struct bitmap4 pxy_bitmap_getattr = { .map[0] = (PXY_ATTR_BIT(FATTR4_SUPPORTED_ATTRS) | PXY_ATTR_BIT(FATTR4_TYPE) | PXY_ATTR_BIT(FATTR4_CHANGE) | PXY_ATTR_BIT(FATTR4_SIZE) | PXY_ATTR_BIT(FATTR4_FSID) | PXY_ATTR_BIT(FATTR4_FILEID)), .map[1] = (PXY_ATTR_BIT2(FATTR4_MODE) | PXY_ATTR_BIT2(FATTR4_NUMLINKS) | PXY_ATTR_BIT2(FATTR4_OWNER) | PXY_ATTR_BIT2(FATTR4_OWNER_GROUP) | PXY_ATTR_BIT2(FATTR4_SPACE_USED) | PXY_ATTR_BIT2(FATTR4_TIME_ACCESS) | PXY_ATTR_BIT2(FATTR4_TIME_METADATA) | PXY_ATTR_BIT2(FATTR4_TIME_MODIFY) | PXY_ATTR_BIT2(FATTR4_RAWDEV)), .bitmap4_len = 2 }; static struct bitmap4 pxy_bitmap_fsinfo = { .map[0] = (PXY_ATTR_BIT(FATTR4_FILES_AVAIL) | PXY_ATTR_BIT(FATTR4_FILES_FREE) | PXY_ATTR_BIT(FATTR4_FILES_TOTAL)), .map[1] = (PXY_ATTR_BIT2(FATTR4_SPACE_AVAIL) | PXY_ATTR_BIT2(FATTR4_SPACE_FREE) | PXY_ATTR_BIT2(FATTR4_SPACE_TOTAL)), .bitmap4_len = 2 }; static struct bitmap4 lease_bits = { .map[0] = PXY_ATTR_BIT(FATTR4_LEASE_TIME), .bitmap4_len = 1 }; static struct bitmap4 pxy_bitmap_per_file_system_attr = { .map[0] = PXY_ATTR_BIT(FATTR4_MAXREAD) | PXY_ATTR_BIT(FATTR4_MAXWRITE), .bitmap4_len = 1 }; #undef PXY_ATTR_BIT #undef PXY_ATTR_BIT2 static struct { attrmask_t mask; int fattr_bit; } fsal_mask2bit[] = { { ATTR_SIZE, FATTR4_SIZE}, { ATTR_MODE, FATTR4_MODE}, { ATTR_OWNER, FATTR4_OWNER}, { ATTR_GROUP, FATTR4_OWNER_GROUP}, { ATTR_ATIME, FATTR4_TIME_ACCESS_SET}, { ATTR_ATIME_SERVER, FATTR4_TIME_ACCESS_SET}, { ATTR_MTIME, FATTR4_TIME_MODIFY_SET}, { ATTR_MTIME_SERVER, FATTR4_TIME_MODIFY_SET}, { ATTR_CTIME, FATTR4_TIME_METADATA} }; static struct bitmap4 empty_bitmap = { .map[0] = 0, .map[1] = 0, .map[2] = 0, .bitmap4_len = 2 }; static int pxy_fsalattr_to_fattr4(const struct attrlist *attrs, fattr4 *data) { int i; struct bitmap4 bmap = empty_bitmap; struct xdr_attrs_args args; for (i = 0; i < ARRAY_SIZE(fsal_mask2bit); i++) { if (FSAL_TEST_MASK(attrs->valid_mask, fsal_mask2bit[i].mask)) { if (fsal_mask2bit[i].fattr_bit > 31) { bmap.map[1] |= 1U << (fsal_mask2bit[i].fattr_bit - 32); bmap.bitmap4_len = 2; } else { bmap.map[0] |= 1U << fsal_mask2bit[i].fattr_bit; } } } memset(&args, 0, sizeof(args)); args.attrs = (struct attrlist *)attrs; args.data = NULL; return nfs4_FSALattr_To_Fattr(&args, &bmap, data); } static GETATTR4resok *pxy_fill_getattr_reply(nfs_resop4 *resop, char *blob, size_t blob_sz) { GETATTR4resok *a = &resop->nfs_resop4_u.opgetattr.GETATTR4res_u.resok4; a->obj_attributes.attrmask = empty_bitmap; a->obj_attributes.attr_vals.attrlist4_val = blob; a->obj_attributes.attr_vals.attrlist4_len = blob_sz; return a; } static int pxy_got_rpc_reply(struct pxy_rpc_io_context *ctx, int sock, int sz, u_int xid) { char *repbuf = ctx->recvbuf; int size; if (sz > ctx->recvbuf_sz) return -E2BIG; PTHREAD_MUTEX_lock(&ctx->iolock); memcpy(repbuf, &xid, sizeof(xid)); /* * sz includes 4 bytes of xid which have been processed * together with record mark - reduce the read to avoid * gobbing up next record mark. */ repbuf += 4; ctx->ioresult = 4; sz -= 4; while (sz > 0) { /* TODO: handle timeouts - use poll(2) */ int bc = read(sock, repbuf, sz); if (bc <= 0) { ctx->ioresult = -((bc < 0) ? errno : ETIMEDOUT); break; } repbuf += bc; ctx->ioresult += bc; sz -= bc; } ctx->iodone = true; size = ctx->ioresult; pthread_cond_signal(&ctx->iowait); PTHREAD_MUTEX_unlock(&ctx->iolock); return size; } static int pxy_rpc_read_reply(int sock) { struct { uint recmark; uint xid; } h; char *buf = (char *)&h; struct glist_head *c; char sink[256]; int cnt = 0; while (cnt < 8) { int bc = read(sock, buf + cnt, 8 - cnt); if (bc < 0) return -errno; cnt += bc; } h.recmark = ntohl(h.recmark); /* TODO: check for final fragment */ h.xid = ntohl(h.xid); LogDebug(COMPONENT_FSAL, "Recmark %x, xid %u\n", h.recmark, h.xid); h.recmark &= ~(1U << 31); PTHREAD_MUTEX_lock(&listlock); glist_for_each(c, &rpc_calls) { struct pxy_rpc_io_context *ctx = container_of(c, struct pxy_rpc_io_context, calls); if (ctx->rpc_xid == h.xid) { glist_del(c); PTHREAD_MUTEX_unlock(&listlock); return pxy_got_rpc_reply(ctx, sock, h.recmark, h.xid); } } PTHREAD_MUTEX_unlock(&listlock); cnt = h.recmark - 4; LogDebug(COMPONENT_FSAL, "xid %u is not on the list, skip %d bytes\n", h.xid, cnt); while (cnt > 0) { int rb = (cnt > sizeof(sink)) ? sizeof(sink) : cnt; rb = read(sock, sink, rb); if (rb <= 0) return -errno; cnt -= rb; } return 0; } /* called with listlock */ static void pxy_new_socket_ready(void) { struct glist_head *nxt; struct glist_head *c; /* If there are any outstanding calls then tell them to resend */ glist_for_each_safe(c, nxt, &rpc_calls) { struct pxy_rpc_io_context *ctx = container_of(c, struct pxy_rpc_io_context, calls); glist_del(c); PTHREAD_MUTEX_lock(&ctx->iolock); ctx->iodone = true; ctx->ioresult = -EAGAIN; pthread_cond_signal(&ctx->iowait); PTHREAD_MUTEX_unlock(&ctx->iolock); } /* If there is anyone waiting for the socket then tell them * it's ready */ pthread_cond_broadcast(&sockless); } /* called with listlock */ static int pxy_connect(struct pxy_client_params *info, sockaddr_t *dest, uint16_t port) { int sock; int socklen; if (info->use_privileged_client_port) { int priv_port = 0; sock = rresvport_af(&priv_port, dest->ss_family); if (sock < 0) LogCrit(COMPONENT_FSAL, "Cannot create TCP socket on privileged port"); } else { sock = socket(dest->ss_family, SOCK_STREAM, IPPROTO_TCP); if (sock < 0) LogCrit(COMPONENT_FSAL, "Cannot create TCP socket - %d", errno); } switch (dest->ss_family) { case AF_INET: ((struct sockaddr_in *)dest)->sin_port = htons(port); socklen = sizeof(struct sockaddr_in); break; case AF_INET6: ((struct sockaddr_in6 *)dest)->sin6_port = htons(port); socklen = sizeof(struct sockaddr_in6); break; default: LogCrit(COMPONENT_FSAL, "Unknown address family %d", dest->ss_family); return -1; } if (sock >= 0) { if (connect(sock, (struct sockaddr *)dest, socklen) < 0) { close(sock); sock = -1; } else { pxy_new_socket_ready(); } } return sock; } /* * NB! rpc_sock can be closed by the sending thread but it will not be * changing its value. Only this function will change rpc_sock which * means that it can look at the value without holding the lock. */ static void *pxy_rpc_recv(void *arg) { struct pxy_client_params *info = arg; char addr[INET6_ADDRSTRLEN]; struct pollfd pfd; int millisec = info->srv_timeout * 1000; SetNameFunction("pxy_rcv_thread"); while (!close_thread) { int nsleeps = 0; PTHREAD_MUTEX_lock(&listlock); do { rpc_sock = pxy_connect(info, &info->srv_addr, info->srv_port); /* early stop test */ if (close_thread) { PTHREAD_MUTEX_unlock(&listlock); return NULL; } if (rpc_sock < 0) { if (nsleeps == 0) sprint_sockaddr(&info->srv_addr, addr, sizeof(addr)); LogCrit(COMPONENT_FSAL, "Cannot connect to server %s:%u", addr, info->srv_port); PTHREAD_MUTEX_unlock(&listlock); sleep(info->retry_sleeptime); nsleeps++; PTHREAD_MUTEX_lock(&listlock); } else { LogDebug(COMPONENT_FSAL, "Connected after %d sleeps, resending outstanding calls", nsleeps); } } while (rpc_sock < 0 && !close_thread); PTHREAD_MUTEX_unlock(&listlock); /* early stop test */ if (close_thread) return NULL; pfd.fd = rpc_sock; pfd.events = POLLIN | POLLRDHUP; while (rpc_sock >= 0) { switch (poll(&pfd, 1, millisec)) { case 0: LogDebug(COMPONENT_FSAL, "Timeout, wait again..."); continue; case -1: break; default: if (pfd.revents & POLLRDHUP) { LogEvent(COMPONENT_FSAL, "Other end has closed connection, reconnecting..."); } else if (pfd.revents & POLLNVAL) { LogEvent(COMPONENT_FSAL, "Socket is closed"); } else { if (pxy_rpc_read_reply(rpc_sock) >= 0) continue; } break; } PTHREAD_MUTEX_lock(&listlock); close(rpc_sock); rpc_sock = -1; PTHREAD_MUTEX_unlock(&listlock); } } return NULL; } static enum clnt_stat pxy_process_reply(struct pxy_rpc_io_context *ctx, COMPOUND4res *res) { enum clnt_stat rc = RPC_CANTRECV; struct timespec ts; PTHREAD_MUTEX_lock(&ctx->iolock); ts.tv_sec = time(NULL) + 60; ts.tv_nsec = 0; while (!ctx->iodone) { int w = pthread_cond_timedwait(&ctx->iowait, &ctx->iolock, &ts); if (w == ETIMEDOUT) { PTHREAD_MUTEX_unlock(&ctx->iolock); return RPC_TIMEDOUT; } } ctx->iodone = false; PTHREAD_MUTEX_unlock(&ctx->iolock); if (ctx->ioresult > 0) { struct rpc_msg reply; XDR x; memset(&reply, 0, sizeof(reply)); reply.RPCM_ack.ar_results.proc = (xdrproc_t) xdr_COMPOUND4res; reply.RPCM_ack.ar_results.where = (caddr_t) res; memset(&x, 0, sizeof(x)); xdrmem_create(&x, ctx->recvbuf, ctx->ioresult, XDR_DECODE); /* macro is defined, GCC 4.7.2 ignoring */ if (xdr_replymsg(&x, &reply)) { if (reply.rm_reply.rp_stat == MSG_ACCEPTED) { switch (reply.rm_reply.rp_acpt.ar_stat) { case SUCCESS: rc = RPC_SUCCESS; break; case PROG_UNAVAIL: rc = RPC_PROGUNAVAIL; break; case PROG_MISMATCH: rc = RPC_PROGVERSMISMATCH; break; case PROC_UNAVAIL: rc = RPC_PROCUNAVAIL; break; case GARBAGE_ARGS: rc = RPC_CANTDECODEARGS; break; case SYSTEM_ERR: rc = RPC_SYSTEMERROR; break; default: rc = RPC_FAILED; break; } } else { switch (reply.rm_reply.rp_rjct.rj_stat) { case RPC_MISMATCH: rc = RPC_VERSMISMATCH; break; case AUTH_ERROR: rc = RPC_AUTHERROR; break; default: rc = RPC_FAILED; break; } } } else { rc = RPC_CANTDECODERES; } reply.RPCM_ack.ar_results.proc = (xdrproc_t) xdr_void; reply.RPCM_ack.ar_results.where = NULL; xdr_free((xdrproc_t) xdr_replymsg, &reply); } return rc; } static int pxy_rpc_need_sock(void) { PTHREAD_MUTEX_lock(&listlock); while (rpc_sock < 0 && !close_thread) pthread_cond_wait(&sockless, &listlock); PTHREAD_MUTEX_unlock(&listlock); return close_thread; } static int pxy_rpc_renewer_wait(int timeout) { struct timespec ts; int rc; PTHREAD_MUTEX_lock(&listlock); ts.tv_sec = time(NULL) + timeout; ts.tv_nsec = 0; rc = pthread_cond_timedwait(&sockless, &listlock, &ts); PTHREAD_MUTEX_unlock(&listlock); return (rc == ETIMEDOUT); } static int pxy_compoundv4_call(struct pxy_rpc_io_context *pcontext, const struct user_cred *cred, COMPOUND4args *args, COMPOUND4res *res) { XDR x; struct rpc_msg rmsg; AUTH *au; enum clnt_stat rc; PTHREAD_MUTEX_lock(&listlock); rmsg.rm_xid = rpc_xid++; PTHREAD_MUTEX_unlock(&listlock); rmsg.rm_direction = CALL; rmsg.rm_call.cb_rpcvers = RPC_MSG_VERSION; rmsg.cb_prog = pcontext->nfs_prog; rmsg.cb_vers = FSAL_PROXY_NFS_V4; rmsg.cb_proc = NFSPROC4_COMPOUND; if (cred) { au = authunix_ncreate(pxy_hostname, cred->caller_uid, cred->caller_gid, cred->caller_glen, cred->caller_garray); } else { au = authunix_ncreate_default(); } if (AUTH_FAILURE(au)) { char *err = rpc_sperror(&au->ah_error, "failed"); LogDebug(COMPONENT_FSAL, "%s", err); gsh_free(err); AUTH_DESTROY(au); return RPC_AUTHERROR; } rmsg.cb_cred = au->ah_cred; rmsg.cb_verf = au->ah_verf; memset(&x, 0, sizeof(x)); xdrmem_create(&x, pcontext->sendbuf + 4, pcontext->sendbuf_sz, XDR_ENCODE); if (xdr_callmsg(&x, &rmsg) && xdr_COMPOUND4args(&x, args)) { u_int pos = xdr_getpos(&x); u_int recmark = ntohl(pos | (1U << 31)); int first_try = 1; pcontext->rpc_xid = rmsg.rm_xid; memcpy(pcontext->sendbuf, &recmark, sizeof(recmark)); pos += 4; do { int bc = 0; char *buf = pcontext->sendbuf; LogDebug(COMPONENT_FSAL, "%ssend XID %u with %d bytes", (first_try ? "First attempt to " : "Re"), rmsg.rm_xid, pos); PTHREAD_MUTEX_lock(&listlock); while (bc < pos) { int wc = write(rpc_sock, buf, pos - bc); if (wc <= 0) { close(rpc_sock); break; } bc += wc; buf += wc; } if (bc == pos) { if (first_try) { glist_add_tail(&rpc_calls, &pcontext->calls); first_try = 0; } } else { if (!first_try) glist_del(&pcontext->calls); } PTHREAD_MUTEX_unlock(&listlock); if (bc == pos) rc = pxy_process_reply(pcontext, res); else rc = RPC_CANTSEND; } while (rc == RPC_TIMEDOUT); } else { rc = RPC_CANTENCODEARGS; } if (au) auth_destroy(au); return rc; } int pxy_compoundv4_execute(const char *caller, const struct user_cred *creds, uint32_t cnt, nfs_argop4 *argoparray, nfs_resop4 *resoparray) { enum clnt_stat rc; struct pxy_rpc_io_context *ctx; COMPOUND4args arg = { .minorversion = FSAL_PROXY_NFS_V4_MINOR, .argarray.argarray_val = argoparray, .argarray.argarray_len = cnt }; COMPOUND4res res = { .resarray.resarray_val = resoparray, .resarray.resarray_len = cnt }; PTHREAD_MUTEX_lock(&context_lock); while (glist_empty(&free_contexts)) pthread_cond_wait(&need_context, &context_lock); ctx = glist_first_entry(&free_contexts, struct pxy_rpc_io_context, calls); glist_del(&ctx->calls); PTHREAD_MUTEX_unlock(&context_lock); /* fill slotid and sequenceid */ if (argoparray->argop == NFS4_OP_SEQUENCE) { SEQUENCE4args *opsequence = &argoparray->nfs_argop4_u.opsequence; /* set slotid */ opsequence->sa_slotid = ctx->slotid; /* increment and set sequence id */ opsequence->sa_sequenceid = ++ctx->seqid; } do { rc = pxy_compoundv4_call(ctx, creds, &arg, &res); if (rc != RPC_SUCCESS) LogDebug(COMPONENT_FSAL, "%s failed with %d", caller, rc); if (rc == RPC_CANTSEND) if (pxy_rpc_need_sock()) return -1; } while ((rc == RPC_CANTRECV && (ctx->ioresult == -EAGAIN)) || (rc == RPC_CANTSEND)); PTHREAD_MUTEX_lock(&context_lock); pthread_cond_signal(&need_context); glist_add(&free_contexts, &ctx->calls); PTHREAD_MUTEX_unlock(&context_lock); if (rc == RPC_SUCCESS) return res.status; return rc; } #define pxy_nfsv4_call(exp, creds, cnt, args, resp) \ pxy_compoundv4_execute(__func__, creds, cnt, args, resp) static inline void pxy_get_clientid(clientid4 *ret) { PTHREAD_MUTEX_lock(&pxy_clientid_mutex); *ret = pxy_clientid; PTHREAD_MUTEX_unlock(&pxy_clientid_mutex); } static inline void pxy_get_client_sessionid(sessionid4 ret) { PTHREAD_MUTEX_lock(&pxy_clientid_mutex); while (no_sessionid) pthread_cond_wait(&cond_sessionid, &pxy_clientid_mutex); memcpy(ret, pxy_client_sessionid, sizeof(sessionid4)); PTHREAD_MUTEX_unlock(&pxy_clientid_mutex); } static inline void pxy_get_client_seqid(sequenceid4 *ret) { PTHREAD_MUTEX_lock(&pxy_clientid_mutex); *ret = pxy_client_seqid; PTHREAD_MUTEX_unlock(&pxy_clientid_mutex); } /** * Confirm pxy_clientid to set a new session. * * @param[out] new_sessionid The new session id * @param[out] new_lease_time Lease time from the background NFSv4.1 server * * @return 0 on success or NFS error code */ static int pxy_setsessionid(sessionid4 new_sessionid, uint32_t *lease_time, struct pxy_client_params *info) { int rc; int opcnt = 0; /* CREATE_SESSION to set session id */ /* SEQUENCE RECLAIM_COMPLETE PUTROOTFH GETATTR to get lease time */ #define FSAL_SESSIONID_NB_OP_ALLOC 4 nfs_argop4 arg[FSAL_SESSIONID_NB_OP_ALLOC]; nfs_resop4 res[FSAL_SESSIONID_NB_OP_ALLOC]; clientid4 cid; sequenceid4 seqid; CREATE_SESSION4res *s_res; CREATE_SESSION4resok *res_ok; uint32_t fore_ca_rdma_ird_val_sink; uint32_t back_ca_rdma_ird_val_sink; pxy_get_clientid(&cid); pxy_get_client_seqid(&seqid); LogDebug(COMPONENT_FSAL, "Getting new session id for client id %"PRIx64 " with sequence id %"PRIx32, cid, seqid); s_res = &res->nfs_resop4_u.opcreate_session; res_ok = &s_res->CREATE_SESSION4res_u.csr_resok4; res_ok->csr_fore_chan_attrs.ca_rdma_ird.ca_rdma_ird_len = 0; res_ok->csr_fore_chan_attrs.ca_rdma_ird.ca_rdma_ird_val = &fore_ca_rdma_ird_val_sink; res_ok->csr_back_chan_attrs.ca_rdma_ird.ca_rdma_ird_len = 0; res_ok->csr_back_chan_attrs.ca_rdma_ird.ca_rdma_ird_val = &back_ca_rdma_ird_val_sink; COMPOUNDV4_ARG_ADD_OP_CREATE_SESSION(opcnt, arg, cid, seqid, info); rc = pxy_compoundv4_execute(__func__, NULL, opcnt, arg, res); if (rc != NFS4_OK) return -1; /*get session_id in res*/ if (s_res->csr_status != NFS4_OK) return -1; memcpy(new_sessionid, res_ok->csr_sessionid, sizeof(sessionid4)); /* Get the lease time */ opcnt = 0; COMPOUNDV4_ARG_ADD_OP_SEQUENCE(opcnt, arg, new_sessionid, NB_RPC_SLOT); COMPOUNDV4_ARG_ADD_OP_GLOBAL_RECLAIM_COMPLETE(opcnt, arg); COMPOUNDV4_ARG_ADD_OP_PUTROOTFH(opcnt, arg); pxy_fill_getattr_reply(res + opcnt, (char *)lease_time, sizeof(*lease_time)); COMPOUNDV4_ARG_ADD_OP_GETATTR(opcnt, arg, lease_bits); rc = pxy_compoundv4_execute(__func__, NULL, opcnt, arg, res); if (rc != NFS4_OK) { *lease_time = 60; LogDebug(COMPONENT_FSAL, "Setting new lease_time to default %d", *lease_time); } else { *lease_time = ntohl(*lease_time); LogDebug(COMPONENT_FSAL, "Getting new lease %d", *lease_time); } return 0; } static int pxy_setclientid(clientid4 *new_clientid, sequenceid4 *new_seqid) { int rc; #define FSAL_EXCHANGE_ID_NB_OP_ALLOC 1 nfs_argop4 arg[FSAL_EXCHANGE_ID_NB_OP_ALLOC]; nfs_resop4 res[FSAL_EXCHANGE_ID_NB_OP_ALLOC]; client_owner4 clientid; char clientid_name[MAXNAMLEN + 1]; uint64_t temp_verifier; EXCHANGE_ID4args opexchange_id; EXCHANGE_ID4res *ei_res; EXCHANGE_ID4resok *ei_resok; char so_major_id_val[NFS4_OPAQUE_LIMIT]; char eir_server_scope_val[NFS4_OPAQUE_LIMIT]; nfs_impl_id4 eir_server_impl_id_val; struct sockaddr_in sin; socklen_t slen = sizeof(sin); char addrbuf[sizeof("255.255.255.255")]; LogEvent(COMPONENT_FSAL, "Negotiating a new ClientId with the remote server"); /* prepare input */ if (getsockname(rpc_sock, &sin, &slen)) return -errno; snprintf(clientid_name, MAXNAMLEN, "%s(%d) - GANESHA NFSv4 Proxy", inet_ntop(AF_INET, &sin.sin_addr, addrbuf, sizeof(addrbuf)), getpid()); clientid.co_ownerid.co_ownerid_len = strlen(clientid_name); clientid.co_ownerid.co_ownerid_val = clientid_name; /* copy to intermediate uint64_t to 0-fill or truncate as needed */ temp_verifier = (uint64_t)ServerBootTime.tv_sec; BUILD_BUG_ON(sizeof(clientid.co_verifier) != sizeof(uint64_t)); memcpy(&clientid.co_verifier, &temp_verifier, sizeof(uint64_t)); arg[0].argop = NFS4_OP_EXCHANGE_ID; opexchange_id.eia_clientowner = clientid; opexchange_id.eia_flags = 0; opexchange_id.eia_state_protect.spa_how = SP4_NONE; opexchange_id.eia_client_impl_id.eia_client_impl_id_len = 0; opexchange_id.eia_client_impl_id.eia_client_impl_id_val = NULL; arg[0].nfs_argop4_u.opexchange_id = opexchange_id; /* prepare reply */ ei_res = &res->nfs_resop4_u.opexchange_id; ei_resok = &ei_res->EXCHANGE_ID4res_u.eir_resok4; ei_resok->eir_server_owner.so_major_id.so_major_id_val = so_major_id_val; ei_resok->eir_server_scope.eir_server_scope_val = eir_server_scope_val; ei_resok->eir_server_impl_id.eir_server_impl_id_val = &eir_server_impl_id_val; rc = pxy_compoundv4_execute(__func__, NULL, 1, arg, res); if (rc != NFS4_OK) { LogDebug(COMPONENT_FSAL, "Compound setclientid res request returned %d", rc); return -1; } /* Keep the confirmed client id and sequence id*/ if (ei_res->eir_status != NFS4_OK) { LogDebug(COMPONENT_FSAL, "EXCHANGE_ID res status is %d", ei_res->eir_status); return -1; } *new_clientid = ei_resok->eir_clientid; *new_seqid = ei_resok->eir_sequenceid; return 0; } static void *pxy_clientid_renewer(void *arg) { struct pxy_client_params *info = arg; int clientid_needed = 1; int sessionid_needed = 1; uint32_t lease_time = 60; SetNameFunction("pxy_clientid_renewer"); while (!close_thread) { clientid4 newcid = 0; sequenceid4 newseqid = 0; if (!sessionid_needed && pxy_rpc_renewer_wait(lease_time - 5)) { /* Simply renew the session id you've got */ nfs_argop4 seq_arg; nfs_resop4 res; int opcnt = 0; int rc; sessionid4 sid; clientid4 cid; SEQUENCE4res *s_res; SEQUENCE4resok *s_resok; pxy_get_clientid(&cid); pxy_get_client_sessionid(sid); LogDebug(COMPONENT_FSAL, "Try renew session id for client id %"PRIx64, cid); COMPOUNDV4_ARG_ADD_OP_SEQUENCE(opcnt, &seq_arg, sid, NB_RPC_SLOT); s_res = &res.nfs_resop4_u.opsequence; s_resok = &s_res->SEQUENCE4res_u.sr_resok4; s_resok->sr_status_flags = 0; rc = pxy_compoundv4_execute(__func__, NULL, 1, &seq_arg, &res); if (rc == NFS4_OK && !s_resok->sr_status_flags) { LogDebug(COMPONENT_FSAL, "New session id for client id %"PRIu64, cid); continue; } else if (rc == NFS4_OK && s_resok->sr_status_flags) { LogEvent(COMPONENT_FSAL, "sr_status_flags received on renewing session with seqop : %"PRIu32, s_resok->sr_status_flags); continue; } else if (rc != NFS4_OK) { LogEvent(COMPONENT_FSAL, "Failed to renew session"); } } /* early stop test */ if (close_thread) return NULL; /* We've either failed to renew or rpc socket has been * reconnected and we need new clientid or sessionid. */ if (pxy_rpc_need_sock()) /* early stop test */ return NULL; /* We need a new session_id */ if (!clientid_needed) { sessionid4 new_sessionid; LogDebug(COMPONENT_FSAL, "Need %d new session id", sessionid_needed); sessionid_needed = pxy_setsessionid(new_sessionid, &lease_time, info); if (!sessionid_needed) { PTHREAD_MUTEX_lock(&pxy_clientid_mutex); /* Set new session id */ memcpy(pxy_client_sessionid, new_sessionid, sizeof(sessionid4)); no_sessionid = false; pthread_cond_broadcast(&cond_sessionid); /* * We finish our create session request next * one will use the next client sequence id. */ pxy_client_seqid++; PTHREAD_MUTEX_unlock(&pxy_clientid_mutex); continue; } } LogDebug(COMPONENT_FSAL, "Need %d new client id", clientid_needed); clientid_needed = pxy_setclientid(&newcid, &newseqid); if (!clientid_needed) { PTHREAD_MUTEX_lock(&pxy_clientid_mutex); pxy_clientid = newcid; pxy_client_seqid = newseqid; PTHREAD_MUTEX_unlock(&pxy_clientid_mutex); } } return NULL; } static void free_io_contexts(void) { struct glist_head *cur, *n; glist_for_each_safe(cur, n, &free_contexts) { struct pxy_rpc_io_context *c = container_of(cur, struct pxy_rpc_io_context, calls); glist_del(cur); gsh_free(c); } } int pxy_close_thread(void) { int rc; /* setting boolean to stop thread */ close_thread = true; /* waiting threads ends */ /* pxy_clientid_renewer is usually waiting on sockless cond : wake up */ /* pxy_rpc_recv is usually polling rpc_sock : wake up by closing it */ PTHREAD_MUTEX_lock(&listlock); pthread_cond_broadcast(&sockless); close(rpc_sock); PTHREAD_MUTEX_unlock(&listlock); rc = pthread_join(pxy_renewer_thread, NULL); if (rc) { LogWarn(COMPONENT_FSAL, "Error on waiting the pxy_renewer_thread end : %d", rc); return rc; } rc = pthread_join(pxy_recv_thread, NULL); if (rc) { LogWarn(COMPONENT_FSAL, "Error on waiting the pxy_recv_thread end : %d", rc); return rc; } return 0; } int pxy_init_rpc(const struct pxy_fsal_module *pm) { int rc; int i = NB_RPC_SLOT-1; PTHREAD_MUTEX_lock(&listlock); glist_init(&rpc_calls); PTHREAD_MUTEX_unlock(&listlock); PTHREAD_MUTEX_lock(&context_lock); glist_init(&free_contexts); PTHREAD_MUTEX_unlock(&context_lock); /** * @todo this lock is not really necessary so long as we can * only do one export at a time. This is a reminder that * there is work to do to get this fnctn to truely be * per export. */ PTHREAD_MUTEX_lock(&listlock); if (rpc_xid == 0) rpc_xid = getpid() ^ time(NULL); PTHREAD_MUTEX_unlock(&listlock); if (gethostname(pxy_hostname, sizeof(pxy_hostname))) strncpy(pxy_hostname, "NFS-GANESHA/Proxy", sizeof(pxy_hostname)); for (i = NB_RPC_SLOT-1; i >= 0; i--) { struct pxy_rpc_io_context *c = gsh_malloc(sizeof(*c) + pm->special.srv_sendsize + pm->special.srv_recvsize); PTHREAD_MUTEX_init(&c->iolock, NULL); PTHREAD_COND_init(&c->iowait, NULL); c->nfs_prog = pm->special.srv_prognum; c->sendbuf_sz = pm->special.srv_sendsize; c->recvbuf_sz = pm->special.srv_recvsize; c->sendbuf = (char *)(c + 1); c->recvbuf = c->sendbuf + c->sendbuf_sz; c->slotid = i; c->seqid = 0; c->iodone = false; PTHREAD_MUTEX_lock(&context_lock); glist_add(&free_contexts, &c->calls); PTHREAD_MUTEX_unlock(&context_lock); } rc = pthread_create(&pxy_recv_thread, NULL, pxy_rpc_recv, (void *)&pm->special); if (rc) { LogCrit(COMPONENT_FSAL, "Cannot create proxy rpc receiver thread - %s", strerror(rc)); free_io_contexts(); return rc; } rc = pthread_create(&pxy_renewer_thread, NULL, pxy_clientid_renewer, (void *)&pm->special); if (rc) { LogCrit(COMPONENT_FSAL, "Cannot create proxy clientid renewer thread - %s", strerror(rc)); free_io_contexts(); } return rc; } static fsal_status_t pxy_make_object(struct fsal_export *export, fattr4 *obj_attributes, const nfs_fh4 *fh, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { struct pxy_obj_handle *pxy_hdl; pxy_hdl = pxy_alloc_handle(export, fh, obj_attributes, attrs_out); if (pxy_hdl == NULL) return fsalstat(ERR_FSAL_FAULT, 0); *handle = &pxy_hdl->obj; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* * cap maxread and maxwrite config values to background server values */ static void pxy_check_maxread_maxwrite(struct fsal_export *export, fattr4 *f4) { fsal_dynamicfsinfo_t info; int rc; rc = nfs4_Fattr_To_fsinfo(&info, f4); if (rc != NFS4_OK) { LogWarn(COMPONENT_FSAL, "Unable to get maxread and maxwrite from background NFS server : %d", rc); } else { struct pxy_fsal_module *pm = container_of(export->fsal, struct pxy_fsal_module, module); if (info.maxread != 0 && pm->fsinfo.maxread > info.maxread) { LogWarn(COMPONENT_FSAL, "Reduced maxread from %"PRIu64 " to align with remote server %"PRIu64, pm->fsinfo.maxread, info.maxread); pm->fsinfo.maxread = info.maxread; } if (info.maxwrite != 0 && pm->fsinfo.maxwrite > info.maxwrite) { LogWarn(COMPONENT_FSAL, "Reduced maxwrite from %"PRIu64 " to align with remote server %"PRIu64, pm->fsinfo.maxwrite, info.maxwrite); pm->fsinfo.maxwrite = info.maxwrite; } } } /* * NULL parent pointer is only used by lookup_path when it starts * from the root handle and has its own export pointer, everybody * else is supposed to provide a real parent pointer and matching * export */ static fsal_status_t pxy_lookup_impl(struct fsal_obj_handle *parent, struct fsal_export *export, const struct user_cred *cred, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { int rc; uint32_t opcnt = 0; GETATTR4resok *atok; GETATTR4resok *atok_per_file_system_attr; GETFH4resok *fhok; sessionid4 sid; /* SEQUENCE PUTROOTFH/PUTFH LOOKUP GETFH GETATTR (GETATTR) */ #define FSAL_LOOKUP_NB_OP_ALLOC 6 nfs_argop4 argoparray[FSAL_LOOKUP_NB_OP_ALLOC]; nfs_resop4 resoparray[FSAL_LOOKUP_NB_OP_ALLOC]; char fattr_blob[FATTR_BLOB_SZ]; char fattr_blob_per_file_system_attr[FATTR_BLOB_SZ]; char padfilehandle[NFS4_FHSIZE]; if (!handle) return fsalstat(ERR_FSAL_INVAL, 0); /* SEQUENCE */ pxy_get_client_sessionid(sid); COMPOUNDV4_ARG_ADD_OP_SEQUENCE(opcnt, argoparray, sid, NB_RPC_SLOT); if (!parent) { COMPOUNDV4_ARG_ADD_OP_PUTROOTFH(opcnt, argoparray); } else { struct pxy_obj_handle *pxy_obj = container_of(parent, struct pxy_obj_handle, obj); switch (parent->type) { case DIRECTORY: break; default: return fsalstat(ERR_FSAL_NOTDIR, 0); } COMPOUNDV4_ARG_ADD_OP_PUTFH(opcnt, argoparray, pxy_obj->fh4); } if (path) { if (!strcmp(path, ".")) { if (!parent) return fsalstat(ERR_FSAL_FAULT, 0); } else if (!strcmp(path, "..")) { if (!parent) return fsalstat(ERR_FSAL_FAULT, 0); COMPOUNDV4_ARG_ADD_OP_LOOKUPP(opcnt, argoparray); } else { COMPOUNDV4_ARG_ADD_OP_LOOKUP(opcnt, argoparray, path); } } fhok = &resoparray[opcnt].nfs_resop4_u.opgetfh.GETFH4res_u.resok4; COMPOUNDV4_ARG_ADD_OP_GETFH(opcnt, argoparray); atok = pxy_fill_getattr_reply(resoparray + opcnt, fattr_blob, sizeof(fattr_blob)); COMPOUNDV4_ARG_ADD_OP_GETATTR(opcnt, argoparray, pxy_bitmap_getattr); /* Dynamic ask of server per file system attr */ if (!parent) { atok_per_file_system_attr = pxy_fill_getattr_reply(resoparray + opcnt, fattr_blob_per_file_system_attr, sizeof(fattr_blob_per_file_system_attr)); COMPOUNDV4_ARG_ADD_OP_GETATTR(opcnt, argoparray, pxy_bitmap_per_file_system_attr); } fhok->object.nfs_fh4_val = (char *)padfilehandle; fhok->object.nfs_fh4_len = sizeof(padfilehandle); rc = pxy_nfsv4_call(export, cred, opcnt, argoparray, resoparray); if (rc != NFS4_OK) return nfsstat4_to_fsal(rc); /* Dynamic check of server per file system attr */ if (!parent) { /* maxread and maxwrite */ pxy_check_maxread_maxwrite(export, &atok_per_file_system_attr->obj_attributes); } return pxy_make_object(export, &atok->obj_attributes, &fhok->object, handle, attrs_out); } static fsal_status_t pxy_lookup(struct fsal_obj_handle *parent, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { return pxy_lookup_impl(parent, op_ctx->fsal_export, op_ctx->creds, path, handle, attrs_out); } /* TODO: make this per-export */ static uint64_t fcnt; static fsal_status_t pxy_mkdir(struct fsal_obj_handle *dir_hdl, const char *name, struct attrlist *attrib, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { int rc; int opcnt = 0; fattr4 input_attr; char padfilehandle[NFS4_FHSIZE]; struct pxy_obj_handle *ph; char fattr_blob[FATTR_BLOB_SZ]; GETATTR4resok *atok; GETFH4resok *fhok; fsal_status_t st; sessionid4 sid; #define FSAL_MKDIR_NB_OP_ALLOC 5 /* SEQUENCE PUTFH CREATE GETFH GETATTR */ nfs_argop4 argoparray[FSAL_MKDIR_NB_OP_ALLOC]; nfs_resop4 resoparray[FSAL_MKDIR_NB_OP_ALLOC]; /* * The caller gives us partial attributes which include mode and owner * and expects the full attributes back at the end of the call. */ attrib->valid_mask &= ATTR_MODE | ATTR_OWNER | ATTR_GROUP; if (pxy_fsalattr_to_fattr4(attrib, &input_attr) == -1) return fsalstat(ERR_FSAL_INVAL, -1); ph = container_of(dir_hdl, struct pxy_obj_handle, obj); /* SEQUENCE */ pxy_get_client_sessionid(sid); COMPOUNDV4_ARG_ADD_OP_SEQUENCE(opcnt, argoparray, sid, NB_RPC_SLOT); COMPOUNDV4_ARG_ADD_OP_PUTFH(opcnt, argoparray, ph->fh4); resoparray[opcnt].nfs_resop4_u.opcreate.CREATE4res_u.resok4.attrset = empty_bitmap; COMPOUNDV4_ARG_ADD_OP_MKDIR(opcnt, argoparray, (char *)name, input_attr); fhok = &resoparray[opcnt].nfs_resop4_u.opgetfh.GETFH4res_u.resok4; fhok->object.nfs_fh4_val = padfilehandle; fhok->object.nfs_fh4_len = sizeof(padfilehandle); COMPOUNDV4_ARG_ADD_OP_GETFH(opcnt, argoparray); atok = pxy_fill_getattr_reply(resoparray + opcnt, fattr_blob, sizeof(fattr_blob)); COMPOUNDV4_ARG_ADD_OP_GETATTR(opcnt, argoparray, pxy_bitmap_getattr); rc = pxy_nfsv4_call(op_ctx->fsal_export, op_ctx->creds, opcnt, argoparray, resoparray); nfs4_Fattr_Free(&input_attr); if (rc != NFS4_OK) return nfsstat4_to_fsal(rc); st = pxy_make_object(op_ctx->fsal_export, &atok->obj_attributes, &fhok->object, handle, attrs_out); if (FSAL_IS_ERROR(st)) return st; return (*handle)->obj_ops.getattrs(*handle, attrib); } static fsal_status_t pxy_mknod(struct fsal_obj_handle *dir_hdl, const char *name, object_file_type_t nodetype, struct attrlist *attrib, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { int rc; int opcnt = 0; fattr4 input_attr; char padfilehandle[NFS4_FHSIZE]; struct pxy_obj_handle *ph; char fattr_blob[FATTR_BLOB_SZ]; GETATTR4resok *atok; GETFH4resok *fhok; fsal_status_t st; enum nfs_ftype4 nf4type; specdata4 specdata = { 0, 0 }; sessionid4 sid; #define FSAL_MKNOD_NB_OP_ALLOC 5 /* SEQUENCE PUTFH CREATE GETFH GETATTR */ nfs_argop4 argoparray[FSAL_MKNOD_NB_OP_ALLOC]; nfs_resop4 resoparray[FSAL_MKNOD_NB_OP_ALLOC]; switch (nodetype) { case CHARACTER_FILE: specdata.specdata1 = attrib->rawdev.major; specdata.specdata2 = attrib->rawdev.minor; nf4type = NF4CHR; break; case BLOCK_FILE: specdata.specdata1 = attrib->rawdev.major; specdata.specdata2 = attrib->rawdev.minor; nf4type = NF4BLK; break; case SOCKET_FILE: nf4type = NF4SOCK; break; case FIFO_FILE: nf4type = NF4FIFO; break; default: return fsalstat(ERR_FSAL_FAULT, EINVAL); } /* * The caller gives us partial attributes which include mode and owner * and expects the full attributes back at the end of the call. */ attrib->valid_mask &= ATTR_MODE | ATTR_OWNER | ATTR_GROUP; if (pxy_fsalattr_to_fattr4(attrib, &input_attr) == -1) return fsalstat(ERR_FSAL_INVAL, -1); ph = container_of(dir_hdl, struct pxy_obj_handle, obj); /* SEQUENCE */ pxy_get_client_sessionid(sid); COMPOUNDV4_ARG_ADD_OP_SEQUENCE(opcnt, argoparray, sid, NB_RPC_SLOT); COMPOUNDV4_ARG_ADD_OP_PUTFH(opcnt, argoparray, ph->fh4); resoparray[opcnt].nfs_resop4_u.opcreate.CREATE4res_u.resok4.attrset = empty_bitmap; COMPOUNDV4_ARG_ADD_OP_CREATE(opcnt, argoparray, (char *)name, nf4type, input_attr, specdata); fhok = &resoparray[opcnt].nfs_resop4_u.opgetfh.GETFH4res_u.resok4; fhok->object.nfs_fh4_val = padfilehandle; fhok->object.nfs_fh4_len = sizeof(padfilehandle); COMPOUNDV4_ARG_ADD_OP_GETFH(opcnt, argoparray); atok = pxy_fill_getattr_reply(resoparray + opcnt, fattr_blob, sizeof(fattr_blob)); COMPOUNDV4_ARG_ADD_OP_GETATTR(opcnt, argoparray, pxy_bitmap_getattr); rc = pxy_nfsv4_call(op_ctx->fsal_export, op_ctx->creds, opcnt, argoparray, resoparray); nfs4_Fattr_Free(&input_attr); if (rc != NFS4_OK) return nfsstat4_to_fsal(rc); st = pxy_make_object(op_ctx->fsal_export, &atok->obj_attributes, &fhok->object, handle, attrs_out); if (FSAL_IS_ERROR(st)) return st; return (*handle)->obj_ops.getattrs(*handle, attrib); } static fsal_status_t pxy_symlink(struct fsal_obj_handle *dir_hdl, const char *name, const char *link_path, struct attrlist *attrib, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { int rc; int opcnt = 0; fattr4 input_attr; char padfilehandle[NFS4_FHSIZE]; char fattr_blob[FATTR_BLOB_SZ]; sessionid4 sid; #define FSAL_SYMLINK_NB_OP_ALLOC 5 /* SEQUENCE PUTFH CREATE GETFH GETATTR */ nfs_argop4 argoparray[FSAL_SYMLINK_NB_OP_ALLOC]; nfs_resop4 resoparray[FSAL_SYMLINK_NB_OP_ALLOC]; GETATTR4resok *atok; GETFH4resok *fhok; fsal_status_t st; struct pxy_obj_handle *ph; /* Tests if symlinking is allowed by configuration. */ if (!op_ctx->fsal_export->exp_ops.fs_supports(op_ctx->fsal_export, fso_symlink_support)) return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); attrib->valid_mask = ATTR_MODE; if (pxy_fsalattr_to_fattr4(attrib, &input_attr) == -1) return fsalstat(ERR_FSAL_INVAL, -1); ph = container_of(dir_hdl, struct pxy_obj_handle, obj); /* SEQUENCE */ pxy_get_client_sessionid(sid); COMPOUNDV4_ARG_ADD_OP_SEQUENCE(opcnt, argoparray, sid, NB_RPC_SLOT); COMPOUNDV4_ARG_ADD_OP_PUTFH(opcnt, argoparray, ph->fh4); resoparray[opcnt].nfs_resop4_u.opcreate.CREATE4res_u.resok4.attrset = empty_bitmap; COMPOUNDV4_ARG_ADD_OP_SYMLINK(opcnt, argoparray, (char *)name, (char *)link_path, input_attr); fhok = &resoparray[opcnt].nfs_resop4_u.opgetfh.GETFH4res_u.resok4; fhok->object.nfs_fh4_val = padfilehandle; fhok->object.nfs_fh4_len = sizeof(padfilehandle); COMPOUNDV4_ARG_ADD_OP_GETFH(opcnt, argoparray); atok = pxy_fill_getattr_reply(resoparray + opcnt, fattr_blob, sizeof(fattr_blob)); COMPOUNDV4_ARG_ADD_OP_GETATTR(opcnt, argoparray, pxy_bitmap_getattr); rc = pxy_nfsv4_call(op_ctx->fsal_export, op_ctx->creds, opcnt, argoparray, resoparray); nfs4_Fattr_Free(&input_attr); if (rc != NFS4_OK) return nfsstat4_to_fsal(rc); st = pxy_make_object(op_ctx->fsal_export, &atok->obj_attributes, &fhok->object, handle, attrs_out); if (FSAL_IS_ERROR(st)) return st; return (*handle)->obj_ops.getattrs(*handle, attrib); } static fsal_status_t pxy_readlink(struct fsal_obj_handle *obj_hdl, struct gsh_buffdesc *link_content, bool refresh) { int rc; int opcnt = 0; struct pxy_obj_handle *ph; sessionid4 sid; #define FSAL_READLINK_NB_OP_ALLOC 3 /* SEQUENCE PUTFH READLINK */ nfs_argop4 argoparray[FSAL_READLINK_NB_OP_ALLOC]; nfs_resop4 resoparray[FSAL_READLINK_NB_OP_ALLOC]; READLINK4resok *rlok; ph = container_of(obj_hdl, struct pxy_obj_handle, obj); /* SEQUENCE */ pxy_get_client_sessionid(sid); COMPOUNDV4_ARG_ADD_OP_SEQUENCE(opcnt, argoparray, sid, NB_RPC_SLOT); COMPOUNDV4_ARG_ADD_OP_PUTFH(opcnt, argoparray, ph->fh4); /* This saves us from having to do one allocation for the XDR, another allocation for the return, and a copy just to get the \NUL terminator. The link length should be cached in the file handle. */ link_content->len = fsal_default_linksize; link_content->addr = gsh_malloc(link_content->len); rlok = &resoparray[opcnt].nfs_resop4_u.opreadlink.READLINK4res_u.resok4; rlok->link.utf8string_val = link_content->addr; rlok->link.utf8string_len = link_content->len; COMPOUNDV4_ARG_ADD_OP_READLINK(opcnt, argoparray); rc = pxy_nfsv4_call(op_ctx->fsal_export, op_ctx->creds, opcnt, argoparray, resoparray); if (rc != NFS4_OK) { gsh_free(link_content->addr); link_content->addr = NULL; link_content->len = 0; return nfsstat4_to_fsal(rc); } rlok->link.utf8string_val[rlok->link.utf8string_len] = '\0'; link_content->len = rlok->link.utf8string_len + 1; return fsalstat(ERR_FSAL_NO_ERROR, 0); } static fsal_status_t pxy_link(struct fsal_obj_handle *obj_hdl, struct fsal_obj_handle *destdir_hdl, const char *name) { int rc; struct pxy_obj_handle *tgt; struct pxy_obj_handle *dst; sessionid4 sid; #define FSAL_LINK_NB_OP_ALLOC 5 /* SEQUENCE PUTFH SAVEFH PUTFH LINK */ nfs_argop4 argoparray[FSAL_LINK_NB_OP_ALLOC]; nfs_resop4 resoparray[FSAL_LINK_NB_OP_ALLOC]; int opcnt = 0; /* Tests if hardlinking is allowed by configuration. */ if (!op_ctx->fsal_export->exp_ops.fs_supports(op_ctx->fsal_export, fso_link_support)) return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); tgt = container_of(obj_hdl, struct pxy_obj_handle, obj); dst = container_of(destdir_hdl, struct pxy_obj_handle, obj); /* SEQUENCE */ pxy_get_client_sessionid(sid); COMPOUNDV4_ARG_ADD_OP_SEQUENCE(opcnt, argoparray, sid, NB_RPC_SLOT); COMPOUNDV4_ARG_ADD_OP_PUTFH(opcnt, argoparray, tgt->fh4); COMPOUNDV4_ARG_ADD_OP_SAVEFH(opcnt, argoparray); COMPOUNDV4_ARG_ADD_OP_PUTFH(opcnt, argoparray, dst->fh4); COMPOUNDV4_ARG_ADD_OP_LINK(opcnt, argoparray, (char *)name); rc = pxy_nfsv4_call(op_ctx->fsal_export, op_ctx->creds, opcnt, argoparray, resoparray); return nfsstat4_to_fsal(rc); } static bool xdr_readdirres(XDR *x, nfs_resop4 *rdres) { return xdr_nfs_resop4(x, rdres) && xdr_nfs_resop4(x, rdres + 1); } /* * Trying to guess how many entries can fit into a readdir buffer * is complicated and usually results in either gross over-allocation * of the memory for results or under-allocation (on large directories) * and buffer overruns - just pay the price of allocating the memory * inside XDR decoding and free it when done */ static fsal_status_t pxy_do_readdir(struct pxy_obj_handle *ph, nfs_cookie4 *cookie, fsal_readdir_cb cb, void *cbarg, attrmask_t attrmask, bool *eof, bool *again) { uint32_t opcnt = 0; int rc; entry4 *e4; sessionid4 sid; #define FSAL_READDIR_NB_OP_ALLOC 3 /* SEQUENCE PUTFH READDIR */ nfs_argop4 argoparray[FSAL_READDIR_NB_OP_ALLOC]; nfs_resop4 resoparray[FSAL_READDIR_NB_OP_ALLOC]; READDIR4resok *rdok; fsal_status_t st = { ERR_FSAL_NO_ERROR, 0 }; /* SEQUENCE */ pxy_get_client_sessionid(sid); COMPOUNDV4_ARG_ADD_OP_SEQUENCE(opcnt, argoparray, sid, NB_RPC_SLOT); /* PUTFH */ COMPOUNDV4_ARG_ADD_OP_PUTFH(opcnt, argoparray, ph->fh4); rdok = &resoparray[opcnt].nfs_resop4_u.opreaddir.READDIR4res_u.resok4; rdok->reply.entries = NULL; /* READDIR */ COMPOUNDV4_ARG_ADD_OP_READDIR(opcnt, argoparray, *cookie, pxy_bitmap_getattr); rc = pxy_nfsv4_call(ph->obj.export, op_ctx->creds, opcnt, argoparray, resoparray); if (rc != NFS4_OK) return nfsstat4_to_fsal(rc); *eof = rdok->reply.eof; for (e4 = rdok->reply.entries; e4; e4 = e4->nextentry) { struct attrlist attrs; char name[MAXNAMLEN + 1]; struct fsal_obj_handle *handle; enum fsal_dir_result cb_rc; /* UTF8 name does not include trailing 0 */ if (e4->name.utf8string_len > sizeof(name) - 1) return fsalstat(ERR_FSAL_SERVERFAULT, E2BIG); memcpy(name, e4->name.utf8string_val, e4->name.utf8string_len); name[e4->name.utf8string_len] = '\0'; if (nfs4_Fattr_To_FSAL_attr(&attrs, &e4->attrs, NULL)) return fsalstat(ERR_FSAL_FAULT, 0); /* * If *again==false : we are in readahead, * we only call cb for next entries but don't update result * for calling readdir. */ if (*again) { *cookie = e4->cookie; *eof = rdok->reply.eof && !e4->nextentry; } /** @todo FSF: this could be handled by getting handle as part * of readdir attributes. However, if acl is * requested, we might get it separately to * avoid over large READDIR response. */ st = pxy_lookup_impl(&ph->obj, op_ctx->fsal_export, op_ctx->creds, name, &handle, NULL); if (FSAL_IS_ERROR(st)) break; cb_rc = cb(name, handle, &attrs, cbarg, e4->cookie); fsal_release_attrs(&attrs); if (cb_rc >= DIR_TERMINATE) { *again = false; break; } /* * Read ahead is supported by this FSAL * but limited to the current background readdir request. */ if (cb_rc >= DIR_READAHEAD && *again) { *again = false; } } xdr_free((xdrproc_t) xdr_readdirres, resoparray); return st; } /** * @todo We might add a verifier to the cookie provided * if server needs one ... */ static fsal_status_t pxy_readdir(struct fsal_obj_handle *dir_hdl, fsal_cookie_t *whence, void *cbarg, fsal_readdir_cb cb, attrmask_t attrmask, bool *eof) { nfs_cookie4 cookie = 0; struct pxy_obj_handle *ph; bool again = true; if (whence) cookie = (nfs_cookie4) *whence; ph = container_of(dir_hdl, struct pxy_obj_handle, obj); do { fsal_status_t st; st = pxy_do_readdir(ph, &cookie, cb, cbarg, attrmask, eof, &again); if (FSAL_IS_ERROR(st)) return st; } while (*eof == false && again); return fsalstat(ERR_FSAL_NO_ERROR, 0); } static fsal_status_t pxy_rename(struct fsal_obj_handle *obj_hdl, struct fsal_obj_handle *olddir_hdl, const char *old_name, struct fsal_obj_handle *newdir_hdl, const char *new_name) { int rc; int opcnt = 0; sessionid4 sid; #define FSAL_RENAME_NB_OP_ALLOC 5 /* SEQUENCE PUTFH SAVEFH PUTFH RENAME */ nfs_argop4 argoparray[FSAL_RENAME_NB_OP_ALLOC]; nfs_resop4 resoparray[FSAL_RENAME_NB_OP_ALLOC]; struct pxy_obj_handle *src; struct pxy_obj_handle *tgt; src = container_of(olddir_hdl, struct pxy_obj_handle, obj); tgt = container_of(newdir_hdl, struct pxy_obj_handle, obj); /* SEQUENCE */ pxy_get_client_sessionid(sid); COMPOUNDV4_ARG_ADD_OP_SEQUENCE(opcnt, argoparray, sid, NB_RPC_SLOT); COMPOUNDV4_ARG_ADD_OP_PUTFH(opcnt, argoparray, src->fh4); COMPOUNDV4_ARG_ADD_OP_SAVEFH(opcnt, argoparray); COMPOUNDV4_ARG_ADD_OP_PUTFH(opcnt, argoparray, tgt->fh4); COMPOUNDV4_ARG_ADD_OP_RENAME(opcnt, argoparray, (char *)old_name, (char *)new_name); rc = pxy_nfsv4_call(op_ctx->fsal_export, op_ctx->creds, opcnt, argoparray, resoparray); return nfsstat4_to_fsal(rc); } static inline int nfs4_Fattr_To_FSAL_attr_savreqmask(struct attrlist *FSAL_attr, fattr4 *Fattr, compound_data_t *data) { int rc = 0; attrmask_t saved_request_mask = FSAL_attr->request_mask; rc = nfs4_Fattr_To_FSAL_attr(FSAL_attr, Fattr, data); FSAL_attr->request_mask = saved_request_mask; return rc; } static fsal_status_t pxy_getattrs(struct fsal_obj_handle *obj_hdl, struct attrlist *attrs) { struct pxy_obj_handle *ph; int rc; uint32_t opcnt = 0; sessionid4 sid; #define FSAL_GETATTR_NB_OP_ALLOC 3 /* SEQUENCE PUTFH GETATTR */ nfs_argop4 argoparray[FSAL_GETATTR_NB_OP_ALLOC]; nfs_resop4 resoparray[FSAL_GETATTR_NB_OP_ALLOC]; GETATTR4resok *atok; char fattr_blob[FATTR_BLOB_SZ]; ph = container_of(obj_hdl, struct pxy_obj_handle, obj); /* SEQUENCE */ pxy_get_client_sessionid(sid); COMPOUNDV4_ARG_ADD_OP_SEQUENCE(opcnt, argoparray, sid, NB_RPC_SLOT); COMPOUNDV4_ARG_ADD_OP_PUTFH(opcnt, argoparray, ph->fh4); atok = pxy_fill_getattr_reply(resoparray + opcnt, fattr_blob, sizeof(fattr_blob)); COMPOUNDV4_ARG_ADD_OP_GETATTR(opcnt, argoparray, pxy_bitmap_getattr); rc = pxy_nfsv4_call(op_ctx->fsal_export, op_ctx->creds, opcnt, argoparray, resoparray); if (rc != NFS4_OK) { if (attrs->request_mask & ATTR_RDATTR_ERR) { /* Caller asked for error to be visible. */ attrs->valid_mask = ATTR_RDATTR_ERR; } return nfsstat4_to_fsal(rc); } if (nfs4_Fattr_To_FSAL_attr_savreqmask(attrs, &atok->obj_attributes, NULL) != NFS4_OK) return fsalstat(ERR_FSAL_INVAL, 0); return fsalstat(ERR_FSAL_NO_ERROR, 0); } static bool pxy_handle_is(struct fsal_obj_handle *obj_hdl, object_file_type_t type) { return obj_hdl->type == type; } static fsal_status_t pxy_unlink(struct fsal_obj_handle *dir_hdl, struct fsal_obj_handle *obj_hdl, const char *name) { int opcnt = 0; int rc; struct pxy_obj_handle *ph; sessionid4 sid; #define FSAL_UNLINK_NB_OP_ALLOC 4 /* SEQUENCE PUTFH REMOVE GETATTR */ nfs_argop4 argoparray[FSAL_UNLINK_NB_OP_ALLOC]; nfs_resop4 resoparray[FSAL_UNLINK_NB_OP_ALLOC]; #if GETATTR_AFTER GETATTR4resok *atok; char fattr_blob[FATTR_BLOB_SZ]; struct attrlist dirattr; #endif ph = container_of(dir_hdl, struct pxy_obj_handle, obj); /* SEQUENCE */ pxy_get_client_sessionid(sid); COMPOUNDV4_ARG_ADD_OP_SEQUENCE(opcnt, argoparray, sid, NB_RPC_SLOT); COMPOUNDV4_ARG_ADD_OP_PUTFH(opcnt, argoparray, ph->fh4); COMPOUNDV4_ARG_ADD_OP_REMOVE(opcnt, argoparray, (char *)name); #if GETATTR_AFTER atok = pxy_fill_getattr_reply(resoparray + opcnt, fattr_blob, sizeof(fattr_blob)); COMPOUNDV4_ARG_ADD_OP_GETATTR(opcnt, argoparray, pxy_bitmap_getattr); #endif rc = pxy_nfsv4_call(op_ctx->fsal_export, op_ctx->creds, opcnt, argoparray, resoparray); if (rc != NFS4_OK) return nfsstat4_to_fsal(rc); #if GETATTR_AFTER if (nfs4_Fattr_To_FSAL_attr(&dirattr, &atok->obj_attributes, NULL) == NFS4_OK) ph->attributes = dirattr; #endif return fsalstat(ERR_FSAL_NO_ERROR, 0); } static fsal_status_t pxy_handle_to_wire(const struct fsal_obj_handle *obj_hdl, fsal_digesttype_t output_type, struct gsh_buffdesc *fh_desc) { struct pxy_obj_handle *ph = container_of(obj_hdl, struct pxy_obj_handle, obj); size_t fhs; void *data; /* sanity checks */ if (!fh_desc || !fh_desc->addr) return fsalstat(ERR_FSAL_FAULT, 0); switch (output_type) { case FSAL_DIGEST_NFSV3: #ifdef PROXY_HANDLE_MAPPING fhs = sizeof(ph->h23); data = &ph->h23; break; #endif case FSAL_DIGEST_NFSV4: fhs = ph->blob.len; data = &ph->blob; break; default: return fsalstat(ERR_FSAL_SERVERFAULT, 0); } if (fh_desc->len < fhs) return fsalstat(ERR_FSAL_TOOSMALL, 0); memcpy(fh_desc->addr, data, fhs); fh_desc->len = fhs; return fsalstat(ERR_FSAL_NO_ERROR, 0); } static void pxy_handle_to_key(struct fsal_obj_handle *obj_hdl, struct gsh_buffdesc *fh_desc) { struct pxy_obj_handle *ph = container_of(obj_hdl, struct pxy_obj_handle, obj); fh_desc->addr = &ph->blob; fh_desc->len = ph->blob.len; } static void pxy_hdl_release(struct fsal_obj_handle *obj_hdl) { struct pxy_obj_handle *ph = container_of(obj_hdl, struct pxy_obj_handle, obj); fsal_obj_handle_fini(obj_hdl); gsh_free(ph); } /* * In this first FSAL_PROXY support_ex version without state * nothing to do on close. */ static fsal_status_t pxy_close(struct fsal_obj_handle *obj_hdl) { return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* * support_ex methods * * This first dirty version of support_ex in FSAL_PROXY doesn't take care of * any state. * * The goal achieves by this first dirty version is only to be compliant with * support_ex fsal api. */ /** * @brief Fill share_access and share_deny fields of an OPEN4args struct * * This function fills share_access and share_deny fields of an OPEN4args * struct to prepare an OPEN v4.1 call. * * @param[in] openflags fsal open flags * @param[in,out] share_access share_access field to be filled * @param[in,out] share_deny share_deny field to be filled * * @return FSAL status. */ static fsal_status_t fill_share_OPEN4args(uint32_t *share_access, uint32_t *share_deny, fsal_openflags_t openflags) { /* share access */ *share_access = 0; if (openflags & FSAL_O_READ) *share_access |= OPEN4_SHARE_ACCESS_READ; if (openflags & FSAL_O_WRITE) *share_access |= OPEN4_SHARE_ACCESS_WRITE; /* share write */ *share_deny = OPEN4_SHARE_DENY_NONE; if (openflags & FSAL_O_DENY_READ) *share_deny |= OPEN4_SHARE_DENY_READ; if (openflags & FSAL_O_DENY_WRITE || openflags & FSAL_O_DENY_WRITE_MAND) *share_deny |= OPEN4_SHARE_DENY_WRITE; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Fill openhow field of an OPEN4args struct * * This function fills an openflags4 openhow field of an OPEN4args struct * to prepare an OPEN v4.1 call. * * @param[in] attrs_in open atributes * @param[in] create_mode open create mode * @param[in] verifier open verifier * @param[in,out] openhow openhow field to be filled * @param[in,out] inattrs created inattrs (need to be freed by calling * nfs4_Fattr_Free) * @param[out] setattr_needed an additional setattr op is needed * * @return FSAL status. */ static fsal_status_t fill_openhow_OPEN4args(openflag4 *openhow, fattr4 inattrs, enum fsal_create_mode createmode, fsal_verifier_t verifier, bool *setattr_needed) { if (openhow == NULL) return fsalstat(ERR_FSAL_INVAL, -1); /* openhow */ if (createmode != FSAL_NO_CREATE) { createhow4 *how = &(openhow->openflag4_u.how); openhow->opentype = OPEN4_CREATE; switch (createmode) { case FSAL_UNCHECKED: how->mode = UNCHECKED4; how->createhow4_u.createattrs = inattrs; break; case FSAL_GUARDED: case FSAL_EXCLUSIVE_9P: how->mode = GUARDED4; how->createhow4_u.createattrs = inattrs; break; case FSAL_EXCLUSIVE: how->mode = EXCLUSIVE4; FSAL_VERIFIER_T_TO_VERIFIER4( how->createhow4_u.createverf, verifier); /* no way to set attr in same op than old EXCLUSIVE4 */ if (inattrs.attrmask.bitmap4_len > 0) { int i = 0; for (i = 0; i < inattrs.attrmask.bitmap4_len; i++) { if (inattrs.attrmask.map[i]) { *setattr_needed = true; break; } } } break; case FSAL_EXCLUSIVE_41: how->mode = EXCLUSIVE4_1; FSAL_VERIFIER_T_TO_VERIFIER4( how->createhow4_u.ch_createboth.cva_verf, verifier); how->createhow4_u.ch_createboth.cva_attrs = inattrs; /* * We assume verifier is stored in time metadata. * * We had better check suppattr_exclcreat from background * server. */ if (inattrs.attrmask.bitmap4_len >= 2 && inattrs.attrmask.map[1] & (1U << (FATTR4_TIME_METADATA - 32))) { *setattr_needed = true; how->createhow4_u.ch_createboth.cva_attrs.attrmask.map[1] &= ~(1U << (FATTR4_TIME_METADATA - 32)); } break; default: return fsalstat(ERR_FSAL_FAULT, EINVAL); } } else openhow->opentype = OPEN4_NOCREATE; return fsalstat(ERR_FSAL_NO_ERROR, 0); } static fsal_status_t pxy_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 *attrs_in, fsal_verifier_t verifier, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out, bool *caller_perm_check) { struct pxy_obj_handle *ph; int rc; /* return code of nfs call */ int opcnt = 0; /* nfs arg counter */ fsal_status_t st; /* return code of fsal call */ char padfilehandle[NFS4_FHSIZE]; /* gotten FH */ char owner_val[128]; unsigned int owner_len = 0; uint32_t share_access = 0; uint32_t share_deny = 0; openflag4 openhow; fattr4 inattrs; open_claim4 claim; sessionid4 sid; /* SEQUENCE, PUTFH, OPEN, GETFH, GETATTR */ #define FSAL_OPEN_NB_OP 5 nfs_argop4 argoparray[FSAL_OPEN_NB_OP]; nfs_resop4 resoparray[FSAL_OPEN_NB_OP]; /* SEQUENCE, PUTFH, SETATTR, GETATTR */ #define FSAL_OPEN_SETATTR_NB_OP 4 nfs_argop4 setattr_argoparray[FSAL_OPEN_SETATTR_NB_OP]; nfs_resop4 setattr_resoparray[FSAL_OPEN_SETATTR_NB_OP]; OPEN4resok *opok; GETFH4resok *fhok; GETATTR4resok *atok; char fattr_blob[FATTR_BLOB_SZ]; bool setattr_needed = false; /* we have not done yet any check */ *caller_perm_check = true; /* get back proxy handle */ ph = container_of(obj_hdl, struct pxy_obj_handle, obj); /* include TRUNCATE case in attrs_in */ if (openflags & FSAL_O_TRUNC) { attrs_in->valid_mask |= ATTR_SIZE; attrs_in->filesize = 0; } /* fill inattrs */ if (pxy_fsalattr_to_fattr4(attrs_in, &inattrs) == -1) return fsalstat(ERR_FSAL_INVAL, -1); /* Three cases need to do an open : * -open by name to get an handle * -open by handle to get attrs_out * -open by handle to truncate */ if (name || attrs_out || openflags & FSAL_O_TRUNC) { /* * We do the open to get handle, check perm, check share, trunc, * create if needed ... */ /* open call will do the perm check */ *caller_perm_check = false; /* prepare open call */ /* SEQUENCE */ pxy_get_client_sessionid(sid); COMPOUNDV4_ARG_ADD_OP_SEQUENCE(opcnt, argoparray, sid, NB_RPC_SLOT); /* PUTFH */ COMPOUNDV4_ARG_ADD_OP_PUTFH(opcnt, argoparray, ph->fh4); /* OPEN */ /* prepare answer */ opok = &resoparray[opcnt].nfs_resop4_u.opopen.OPEN4res_u.resok4; opok->rflags = 0; /* set to NULL for safety */ opok->attrset = empty_bitmap; /* set to empty for safety */ /* prepare open input args */ /* share_access and share_deny */ st = fill_share_OPEN4args(&share_access, &share_deny, openflags); if (FSAL_IS_ERROR(st)) { nfs4_Fattr_Free(&inattrs); return st; } /* owner */ snprintf(owner_val, sizeof(owner_val), "GANESHA/PROXY: pid=%u %" PRIu64, getpid(), atomic_inc_uint64_t(&fcnt)); owner_len = strnlen(owner_val, sizeof(owner_val)); /* inattrs and openhow */ st = fill_openhow_OPEN4args(&openhow, inattrs, createmode, verifier, &setattr_needed); if (FSAL_IS_ERROR(st)) { nfs4_Fattr_Free(&inattrs); return st; } /* claim : first support_ex version, no state -> no claim */ if (name) { /* open by name */ claim.claim = CLAIM_NULL; claim.open_claim4_u.file.utf8string_val = (char *)name; claim.open_claim4_u.file.utf8string_len = strlen(name); } else { /* open by handle */ claim.claim = CLAIM_FH; } /* add open */ COMPOUNDV4_ARGS_ADD_OP_OPEN_4_1(opcnt, argoparray, share_access, share_deny, owner_val, owner_len, openhow, claim); /* GETFH */ /* prepare answer */ fhok = &resoparray[opcnt].nfs_resop4_u.opgetfh.GETFH4res_u.resok4; fhok->object.nfs_fh4_val = padfilehandle; fhok->object.nfs_fh4_len = sizeof(padfilehandle); COMPOUNDV4_ARG_ADD_OP_GETFH(opcnt, argoparray); if (!setattr_needed && (new_obj || attrs_out)) { /* GETATTR */ atok = pxy_fill_getattr_reply(resoparray + opcnt, fattr_blob, sizeof(fattr_blob)); COMPOUNDV4_ARG_ADD_OP_GETATTR(opcnt, argoparray, pxy_bitmap_getattr); } /* nfs call*/ rc = pxy_nfsv4_call(op_ctx->fsal_export, op_ctx->creds, opcnt, argoparray, resoparray); if (rc != NFS4_OK) { nfs4_Fattr_Free(&inattrs); return nfsstat4_to_fsal(rc); } /* update stateid in current state */ if (state) { struct pxy_state *pxy_state_id = container_of(state, struct pxy_state, state); pxy_state_id->stateid = opok->stateid; } } if (setattr_needed) { opcnt = 0; /* SEQUENCE */ pxy_get_client_sessionid(sid); COMPOUNDV4_ARG_ADD_OP_SEQUENCE(opcnt, setattr_argoparray, sid, NB_RPC_SLOT); /* PUTFH */ COMPOUNDV4_ARG_ADD_OP_PUTFH(opcnt, setattr_argoparray, fhok->object); /* SETATTR for truncate */ setattr_resoparray[opcnt].nfs_resop4_u.opsetattr.attrsset = empty_bitmap; /* We have a stateid */ /* cause we did an open when we set setattr_needed. */ COMPOUNDV4_ARG_ADD_OP_SETATTR(opcnt, setattr_argoparray, inattrs, opok->stateid.other); if (new_obj || attrs_out) { /* GETATTR */ atok = pxy_fill_getattr_reply( setattr_resoparray + opcnt, fattr_blob, sizeof(fattr_blob)); COMPOUNDV4_ARG_ADD_OP_GETATTR(opcnt, setattr_argoparray, pxy_bitmap_getattr); } /* nfs call*/ rc = pxy_nfsv4_call(op_ctx->fsal_export, op_ctx->creds, opcnt, setattr_argoparray, setattr_resoparray); if (rc != NFS4_OK) { nfs4_Fattr_Free(&inattrs); return nfsstat4_to_fsal(rc); } } /* clean inattrs */ nfs4_Fattr_Free(&inattrs); /* create a new object if asked and attrs_out by the way */ if (new_obj) { if (name) { /* create new_obj and set attrs_out*/ st = pxy_make_object(op_ctx->fsal_export, &atok->obj_attributes, &fhok->object, new_obj, attrs_out); if (FSAL_IS_ERROR(st)) return st; } else { *new_obj = obj_hdl; } } /* set attrs_out if needed and not yet done by creating new object */ if (attrs_out && (!new_obj || (new_obj && !name))) { rc = nfs4_Fattr_To_FSAL_attr(attrs_out, &atok->obj_attributes, NULL); if (rc != NFS4_OK) return nfsstat4_to_fsal(rc); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } static fsal_status_t pxy_read2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buffer_size, void *buffer, size_t *read_amount, bool *end_of_file, struct io_info *info) { int maxReadSize; int rc; int opcnt = 0; struct pxy_obj_handle *ph; sessionid4 sid; #define FSAL_READ2_NB_OP_ALLOC 3 /* SEQUENCE + PUTFH + READ */ nfs_argop4 argoparray[FSAL_READ2_NB_OP_ALLOC]; nfs_resop4 resoparray[FSAL_READ2_NB_OP_ALLOC]; READ4resok *rok; ph = container_of(obj_hdl, struct pxy_obj_handle, obj); maxReadSize = op_ctx->fsal_export->exp_ops.fs_maxread( op_ctx->fsal_export); if (buffer_size > maxReadSize) buffer_size = maxReadSize; /* SEQUENCE */ pxy_get_client_sessionid(sid); COMPOUNDV4_ARG_ADD_OP_SEQUENCE(opcnt, argoparray, sid, NB_RPC_SLOT); /* prepare PUTFH */ COMPOUNDV4_ARG_ADD_OP_PUTFH(opcnt, argoparray, ph->fh4); /* prepare READ */ rok = &resoparray[opcnt].nfs_resop4_u.opread.READ4res_u.resok4; rok->data.data_val = buffer; rok->data.data_len = buffer_size; if (bypass) COMPOUNDV4_ARG_ADD_OP_READ_BYPASS(opcnt, argoparray, offset, buffer_size); else { if (state) { struct pxy_state *pxy_state_id = container_of(state, struct pxy_state, state); COMPOUNDV4_ARG_ADD_OP_READ(opcnt, argoparray, offset, buffer_size, pxy_state_id->stateid.other); } else { COMPOUNDV4_ARG_ADD_OP_READ_STATELESS(opcnt, argoparray, offset, buffer_size); } } /* nfs call */ rc = pxy_nfsv4_call(op_ctx->fsal_export, op_ctx->creds, opcnt, argoparray, resoparray); if (rc != NFS4_OK) return nfsstat4_to_fsal(rc); *end_of_file = rok->eof; *read_amount = rok->data.data_len; if (info) { info->io_content.what = NFS4_CONTENT_DATA; info->io_content.data.d_offset = offset + *read_amount; info->io_content.data.d_data.data_len = *read_amount; info->io_content.data.d_data.data_val = buffer; } return fsalstat(ERR_FSAL_NO_ERROR, 0); } static fsal_status_t pxy_write2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buffer_size, void *buffer, size_t *wrote_amount, bool *fsal_stable, struct io_info *info) { int maxWriteSize; int rc; int opcnt = 0; sessionid4 sid; #define FSAL_WRITE_NB_OP_ALLOC 3 /* SEQUENCE + PUTFH + WRITE */ nfs_argop4 argoparray[FSAL_WRITE_NB_OP_ALLOC]; nfs_resop4 resoparray[FSAL_WRITE_NB_OP_ALLOC]; WRITE4resok *wok; struct pxy_obj_handle *ph; stable_how4 stable_how; if (info != NULL) { /* Currently we don't support WRITE_PLUS */ return fsalstat(ERR_FSAL_NOTSUPP, 0); } ph = container_of(obj_hdl, struct pxy_obj_handle, obj); /* check max write size */ maxWriteSize = op_ctx->fsal_export->exp_ops.fs_maxwrite( op_ctx->fsal_export); if (buffer_size > maxWriteSize) buffer_size = maxWriteSize; /* SEQUENCE */ pxy_get_client_sessionid(sid); COMPOUNDV4_ARG_ADD_OP_SEQUENCE(opcnt, argoparray, sid, NB_RPC_SLOT); /* prepare PUTFH */ COMPOUNDV4_ARG_ADD_OP_PUTFH(opcnt, argoparray, ph->fh4); /* prepare write */ wok = &resoparray[opcnt].nfs_resop4_u.opwrite.WRITE4res_u.resok4; if (*fsal_stable) stable_how = DATA_SYNC4; else stable_how = UNSTABLE4; if (state) { struct pxy_state *pxy_state_id = container_of(state, struct pxy_state, state); COMPOUNDV4_ARG_ADD_OP_WRITE(opcnt, argoparray, offset, buffer, buffer_size, stable_how, pxy_state_id->stateid.other); } else { COMPOUNDV4_ARG_ADD_OP_WRITE_STATELESS(opcnt, argoparray, offset, buffer, buffer_size, stable_how); } /* nfs call */ rc = pxy_nfsv4_call(op_ctx->fsal_export, op_ctx->creds, opcnt, argoparray, resoparray); if (rc != NFS4_OK) return nfsstat4_to_fsal(rc); /* get res */ *wrote_amount = wok->count; if (wok->committed == UNSTABLE4) *fsal_stable = false; else *fsal_stable = true; return fsalstat(ERR_FSAL_NO_ERROR, 0); } static fsal_status_t pxy_close2(struct fsal_obj_handle *obj_hdl, struct state_t *state) { struct pxy_obj_handle *ph; int rc; int opcnt = 0; sessionid4 sessionid; /* SEQUENCE, PUTFH, CLOSE */ #define FSAL_CLOSE_NB_OP_ALLOC 3 nfs_argop4 argoparray[FSAL_CLOSE_NB_OP_ALLOC]; nfs_resop4 resoparray[FSAL_CLOSE_NB_OP_ALLOC]; char All_Zero[] = "\0\0\0\0\0\0\0\0\0\0\0\0"; /* 12 times \0 */ struct pxy_state *pxy_state_id = NULL; ph = container_of(obj_hdl, struct pxy_obj_handle, obj); /* Check if this was a "stateless" open, * then nothing is to be done at close */ if (!state) { return fsalstat(ERR_FSAL_NO_ERROR, 0); } else { pxy_state_id = container_of(state, struct pxy_state, state); if (!memcmp(pxy_state_id->stateid.other, All_Zero, 12)) return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* SEQUENCE */ pxy_get_client_sessionid(sessionid); COMPOUNDV4_ARG_ADD_OP_SEQUENCE(opcnt, argoparray, sessionid, NB_RPC_SLOT); /* PUTFH */ COMPOUNDV4_ARG_ADD_OP_PUTFH(opcnt, argoparray, ph->fh4); /* CLOSE */ if (state) COMPOUNDV4_ARG_ADD_OP_CLOSE_4_1(opcnt, argoparray, pxy_state_id->stateid); else COMPOUNDV4_ARG_ADD_OP_CLOSE_4_1_STATELESS(opcnt, argoparray); rc = pxy_nfsv4_call(op_ctx->fsal_export, op_ctx->creds, opcnt, argoparray, resoparray); if (rc != NFS4_OK) { return nfsstat4_to_fsal(rc); } /* We clean local saved stateid. */ if (state) memset(&pxy_state_id->stateid, 0, sizeof(stateid4)); return fsalstat(ERR_FSAL_NO_ERROR, 0); } static fsal_status_t pxy_setattr2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, struct attrlist *attrib_set) { int rc; fattr4 input_attr; uint32_t opcnt = 0; struct pxy_obj_handle *ph; sessionid4 sid; #define FSAL_SETATTR2_NB_OP_ALLOC 3 /* SEQUENCE PUTFH SETATTR */ nfs_argop4 argoparray[FSAL_SETATTR2_NB_OP_ALLOC]; nfs_resop4 resoparray[FSAL_SETATTR2_NB_OP_ALLOC]; /*prepare attributes */ /* * No way to update CTIME using a NFSv4 SETATTR. * Server will return NFS4ERR_INVAL (22). * time_metadata is a readonly attribute in NFSv4 and NFSv4.1. * (section 5.7 in RFC7530 or RFC5651) * Nevermind : this update is useless, we prevent it. */ FSAL_UNSET_MASK(attrib_set->valid_mask, ATTR_CTIME); if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_MODE)) attrib_set->mode &= ~op_ctx->fsal_export->exp_ops.fs_umask( op_ctx->fsal_export); ph = container_of(obj_hdl, struct pxy_obj_handle, obj); if (pxy_fsalattr_to_fattr4(attrib_set, &input_attr) == -1) return fsalstat(ERR_FSAL_INVAL, EINVAL); /* SEQUENCE */ pxy_get_client_sessionid(sid); COMPOUNDV4_ARG_ADD_OP_SEQUENCE(opcnt, argoparray, sid, NB_RPC_SLOT); /* prepare PUTFH */ COMPOUNDV4_ARG_ADD_OP_PUTFH(opcnt, argoparray, ph->fh4); /* prepare SETATTR */ resoparray[opcnt].nfs_resop4_u.opsetattr.attrsset = empty_bitmap; /* We don't use the special "bypass" stateid. */ /* Indeed, bypass state will be treated like anonymous value. */ /* RFC 5661, section 8.2.3 */ if (state) { struct pxy_state *pxy_state_id = container_of(state, struct pxy_state, state); COMPOUNDV4_ARG_ADD_OP_SETATTR(opcnt, argoparray, input_attr, pxy_state_id->stateid.other); } else { COMPOUNDV4_ARG_ADD_OP_SETATTR_STATELESS(opcnt, argoparray, input_attr); } /* nfs call */ rc = pxy_nfsv4_call(op_ctx->fsal_export, op_ctx->creds, opcnt, argoparray, resoparray); nfs4_Fattr_Free(&input_attr); if (rc != NFS4_OK) return nfsstat4_to_fsal(rc); return fsalstat(ERR_FSAL_NO_ERROR, 0); } static fsal_openflags_t pxy_status2(struct fsal_obj_handle *obj_hdl, struct state_t *state) { /* first version of support_ex, no state, no saved openflags */ fsal_openflags_t null_flags = 0; /* closed and deny_none*/ return null_flags; } static fsal_status_t pxy_reopen2(struct fsal_obj_handle *obj_hdl, struct state_t *state, fsal_openflags_t openflags) { /* no way to open by handle in v4 */ /* waiting for v4.1 or solid state to really do the job */ return fsalstat(ERR_FSAL_NO_ERROR, 0); } static fsal_status_t pxy_commit2(struct fsal_obj_handle *obj_hdl, off_t offset, size_t len) { struct pxy_obj_handle *ph; int rc; /* return code of nfs call */ int opcnt = 0; /* nfs arg counter */ sessionid4 sid; #define FSAL_COMMIT2_NB_OP 3 /* SEQUENCE, PUTFH, COMMIT */ nfs_argop4 argoparray[FSAL_COMMIT2_NB_OP]; nfs_resop4 resoparray[FSAL_COMMIT2_NB_OP]; ph = container_of(obj_hdl, struct pxy_obj_handle, obj); /* SEQUENCE */ pxy_get_client_sessionid(sid); COMPOUNDV4_ARG_ADD_OP_SEQUENCE(opcnt, argoparray, sid, NB_RPC_SLOT); /* prepare PUTFH */ COMPOUNDV4_ARG_ADD_OP_PUTFH(opcnt, argoparray, ph->fh4); /* prepare COMMIT */ COMPOUNDV4_ARG_ADD_OP_COMMIT(opcnt, argoparray, offset, len); /* nfs call */ rc = pxy_nfsv4_call(op_ctx->fsal_export, op_ctx->creds, opcnt, argoparray, resoparray); if (rc != NFS4_OK) return nfsstat4_to_fsal(rc); return fsalstat(ERR_FSAL_NO_ERROR, 0); } void pxy_handle_ops_init(struct fsal_obj_ops *ops) { ops->release = pxy_hdl_release; ops->lookup = pxy_lookup; ops->readdir = pxy_readdir; ops->mkdir = pxy_mkdir; ops->mknode = pxy_mknod; ops->symlink = pxy_symlink; ops->readlink = pxy_readlink; ops->getattrs = pxy_getattrs; ops->link = pxy_link; ops->rename = pxy_rename; ops->unlink = pxy_unlink; ops->close = pxy_close; ops->handle_is = pxy_handle_is; ops->handle_to_wire = pxy_handle_to_wire; ops->handle_to_key = pxy_handle_to_key; ops->open2 = pxy_open2; ops->read2 = pxy_read2; ops->write2 = pxy_write2; ops->close2 = pxy_close2; ops->setattr2 = pxy_setattr2; ops->status2 = pxy_status2; ops->reopen2 = pxy_reopen2; ops->commit2 = pxy_commit2; } #ifdef PROXY_HANDLE_MAPPING static unsigned int hash_nfs_fh4(const nfs_fh4 *fh, unsigned int cookie) { const char *cpt; unsigned int sum = cookie; unsigned int extract; unsigned int mod = fh->nfs_fh4_len % sizeof(unsigned int); for (cpt = fh->nfs_fh4_val; cpt - fh->nfs_fh4_val < fh->nfs_fh4_len - mod; cpt += sizeof(unsigned int)) { memcpy(&extract, cpt, sizeof(unsigned int)); sum = (3 * sum + 5 * extract + 1999); } /* * If the handle is not 32 bits-aligned, the last loop will * get uninitialized chars after the end of the handle. We * must avoid this by skipping the last loop and doing a * special processing for the last bytes */ if (mod) { extract = 0; while (cpt - fh->nfs_fh4_val < fh->nfs_fh4_len) { extract <<= 8; extract |= (uint8_t) (*cpt++); } sum = (3 * sum + 5 * extract + 1999); } return sum; } #endif static struct pxy_obj_handle *pxy_alloc_handle(struct fsal_export *exp, const nfs_fh4 *fh, fattr4 *obj_attributes, struct attrlist *attrs_out) { struct pxy_obj_handle *n = gsh_calloc(1, sizeof(*n) + fh->nfs_fh4_len); compound_data_t data; struct attrlist attributes; memset(&attributes, 0, sizeof(attributes)); memset(&data, 0, sizeof(data)); data.current_obj = &n->obj; if (nfs4_Fattr_To_FSAL_attr(&attributes, obj_attributes, &data) != NFS4_OK) { gsh_free(n); return NULL; } n->fh4 = *fh; n->fh4.nfs_fh4_val = n->blob.bytes; memcpy(n->blob.bytes, fh->nfs_fh4_val, fh->nfs_fh4_len); n->blob.len = fh->nfs_fh4_len + sizeof(n->blob); n->blob.type = attributes.type; #ifdef PROXY_HANDLE_MAPPING int rc; memset(&n->h23, 0, sizeof(n->h23)); n->h23.len = sizeof(n->h23); n->h23.type = PXY_HANDLE_MAPPED; n->h23.object_id = n->obj.fileid; n->h23.handle_hash = hash_nfs_fh4(fh, n->obj.fileid); rc = HandleMap_SetFH(&n->h23, &n->blob, n->blob.len); if ((rc != HANDLEMAP_SUCCESS) && (rc != HANDLEMAP_EXISTS)) { gsh_free(n); return NULL; } #endif fsal_obj_handle_init(&n->obj, exp, attributes.type); n->obj.fs = NULL; n->obj.state_hdl = NULL; n->obj.fsid = attributes.fsid; n->obj.fileid = attributes.fileid; pxy_handle_ops_init(&n->obj.obj_ops); if (attrs_out != NULL) { /* We aren't keeping ACL ref ourself, so pass it * to the caller. */ fsal_copy_attrs(attrs_out, &attributes, true); } else { /* Make sure we release the attributes. */ fsal_release_attrs(&attributes); } return n; } /* export methods that create object handles */ fsal_status_t pxy_lookup_path(struct fsal_export *exp_hdl, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { struct fsal_obj_handle *next; struct fsal_obj_handle *parent = NULL; char *saved; char *pcopy; char *p, *pnext; struct user_cred *creds = op_ctx->creds; pcopy = gsh_strdup(path); p = strtok_r(pcopy, "/", &saved); while (p) { if (strcmp(p, "..") == 0) { /* Don't allow lookup of ".." */ LogInfo(COMPONENT_FSAL, "Attempt to use \"..\" element in path %s", path); gsh_free(pcopy); return fsalstat(ERR_FSAL_ACCESS, EACCES); } /* Get the next token now, so we know if we are at the * terminal token or not. */ pnext = strtok_r(NULL, "/", &saved); /* Note that if any element is a symlink, the following will * fail, thus no security exposure. Only pass back the * attributes of the terminal lookup. */ fsal_status_t st = pxy_lookup_impl(parent, exp_hdl, creds, p, &next, pnext == NULL ? attrs_out : NULL); if (FSAL_IS_ERROR(st)) { gsh_free(pcopy); return st; } p = pnext; parent = next; } /* The final element could be a symlink, but either way we are called * will not work with a symlink, so no security exposure there. */ gsh_free(pcopy); *handle = next; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* * Create an FSAL 'object' from the handle - used * to construct objects from a handle which has been * 'extracted' by .wire_to_host. */ fsal_status_t pxy_create_handle(struct fsal_export *exp_hdl, struct gsh_buffdesc *hdl_desc, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { nfs_fh4 fh4; struct pxy_obj_handle *ph; struct pxy_handle_blob *blob; int rc; uint32_t opcnt = 0; sessionid4 sid; #define FSAL_CREATE_HANDLE_NB_OP_ALLOC 3 /* SEQUENCE PUTFH GETATTR */ nfs_argop4 argoparray[FSAL_CREATE_HANDLE_NB_OP_ALLOC]; nfs_resop4 resoparray[FSAL_CREATE_HANDLE_NB_OP_ALLOC]; GETATTR4resok *atok; char fattr_blob[FATTR_BLOB_SZ]; blob = (struct pxy_handle_blob *)hdl_desc->addr; if (blob->len != hdl_desc->len) return fsalstat(ERR_FSAL_INVAL, 0); fh4.nfs_fh4_val = blob->bytes; fh4.nfs_fh4_len = blob->len - sizeof(*blob); /* SEQUENCE */ pxy_get_client_sessionid(sid); COMPOUNDV4_ARG_ADD_OP_SEQUENCE(opcnt, argoparray, sid, NB_RPC_SLOT); COMPOUNDV4_ARG_ADD_OP_PUTFH(opcnt, argoparray, fh4); atok = pxy_fill_getattr_reply(resoparray + opcnt, fattr_blob, sizeof(fattr_blob)); COMPOUNDV4_ARG_ADD_OP_GETATTR(opcnt, argoparray, pxy_bitmap_getattr); rc = pxy_nfsv4_call(exp_hdl, op_ctx->creds, opcnt, argoparray, resoparray); if (rc != NFS4_OK) return nfsstat4_to_fsal(rc); ph = pxy_alloc_handle(exp_hdl, &fh4, &atok->obj_attributes, attrs_out); if (!ph) return fsalstat(ERR_FSAL_FAULT, 0); *handle = &ph->obj; return fsalstat(ERR_FSAL_NO_ERROR, 0); } fsal_status_t pxy_get_dynamic_info(struct fsal_export *exp_hdl, struct fsal_obj_handle *obj_hdl, fsal_dynamicfsinfo_t *infop) { int rc; int opcnt = 0; sessionid4 sid; #define FSAL_FSINFO_NB_OP_ALLOC 3 /* SEQUENCE PUTFH GETATTR */ nfs_argop4 argoparray[FSAL_FSINFO_NB_OP_ALLOC]; nfs_resop4 resoparray[FSAL_FSINFO_NB_OP_ALLOC]; GETATTR4resok *atok; char fattr_blob[48]; /* 6 values, 8 bytes each */ struct pxy_obj_handle *ph; ph = container_of(obj_hdl, struct pxy_obj_handle, obj); /* SEQUENCE */ pxy_get_client_sessionid(sid); COMPOUNDV4_ARG_ADD_OP_SEQUENCE(opcnt, argoparray, sid, NB_RPC_SLOT); COMPOUNDV4_ARG_ADD_OP_PUTFH(opcnt, argoparray, ph->fh4); atok = pxy_fill_getattr_reply(resoparray + opcnt, fattr_blob, sizeof(fattr_blob)); COMPOUNDV4_ARG_ADD_OP_GETATTR(opcnt, argoparray, pxy_bitmap_fsinfo); rc = pxy_nfsv4_call(exp_hdl, op_ctx->creds, opcnt, argoparray, resoparray); if (rc != NFS4_OK) return nfsstat4_to_fsal(rc); if (nfs4_Fattr_To_fsinfo(infop, &atok->obj_attributes) != NFS4_OK) return fsalstat(ERR_FSAL_INVAL, 0); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* Convert of-the-wire digest into unique 'handle' which * can be used to identify the object */ fsal_status_t pxy_wire_to_host(struct fsal_export *exp_hdl, fsal_digesttype_t in_type, struct gsh_buffdesc *fh_desc, int flags) { struct pxy_handle_blob *pxyblob; size_t fh_size; if (!fh_desc || !fh_desc->addr) return fsalstat(ERR_FSAL_FAULT, EINVAL); pxyblob = (struct pxy_handle_blob *)fh_desc->addr; fh_size = pxyblob->len; #ifdef PROXY_HANDLE_MAPPING if (in_type == FSAL_DIGEST_NFSV3) fh_size = sizeof(nfs23_map_handle_t); #endif if (fh_desc->len != fh_size) { LogMajor(COMPONENT_FSAL, "Size mismatch for handle. should be %zu, got %zu", fh_size, fh_desc->len); return fsalstat(ERR_FSAL_SERVERFAULT, 0); } #ifdef PROXY_HANDLE_MAPPING if (in_type == FSAL_DIGEST_NFSV3) { nfs23_map_handle_t *h23 = (nfs23_map_handle_t *) fh_desc->addr; if (h23->type != PXY_HANDLE_MAPPED) return fsalstat(ERR_FSAL_STALE, ESTALE); /* As long as HandleMap_GetFH copies nfs23 handle into * the key before lookup I can get away with using * the same buffer for input and output */ if (HandleMap_GetFH(h23, fh_desc) != HANDLEMAP_SUCCESS) return fsalstat(ERR_FSAL_STALE, 0); fh_size = fh_desc->len; } #endif fh_desc->len = fh_size; return fsalstat(ERR_FSAL_NO_ERROR, 0); } nfs-ganesha-2.6.0/src/FSAL/FSAL_PROXY/handle_mapping/000077500000000000000000000000001324272410200217165ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/FSAL/FSAL_PROXY/handle_mapping/CMakeLists.txt000066400000000000000000000032731324272410200244630ustar00rootroot00000000000000 ########### next target ############### SET(handlemapping_STAT_SRCS handle_mapping.c handle_mapping.h handle_mapping_db.c handle_mapping_db.h handle_mapping_internal.h ) add_library(handlemapping STATIC ${handlemapping_STAT_SRCS}) add_sanitizers(handlemapping) ########### next target ############### SET(test_handle_mapping_db_SRCS test_handle_mapping_db.c ) add_executable(test_handle_mapping_db ${test_handle_mapping_db_SRCS}) target_link_libraries(test_handle_mapping_db handlemapping hashtable log common_utils rwlock sqlite3) ########### next target ############### SET(test_handle_mapping_SRCS test_handle_mapping.c ) add_executable(test_handle_mapping ${test_handle_mapping_SRCS}) target_link_libraries(test_handle_mapping handlemapping hashtable log common_utils rwlock sqlite3) ########### install files ############### #original Makefile.am contents follow: #AM_CFLAGS = $(SQLITE_CFLAGS) # #noinst_LTLIBRARIES = libhandlemapping.la # #libhandlemapping_la_SOURCES = handle_mapping.c handle_mapping.h handle_mapping_db.c handle_mapping_db.h handle_mapping_internal.h # # #check_PROGRAMS = test_handle_mapping_db test_handle_mapping #test_handle_mapping_db_SOURCES = test_handle_mapping_db.c #test_handle_mapping_db_LDADD = libhandlemapping.la $(top_srcdir)/HashTable/libhashtable.la $(top_srcdir)/Log/liblog.la \ # $(top_srcdir)/Common/libcommon_utils.la -lsqlite3 # # #test_handle_mapping_SOURCES = test_handle_mapping.c #test_handle_mapping_LDADD = libhandlemapping.la $(top_srcdir)/HashTable/libhashtable.la $(top_srcdir)/Log/liblog.la \ # $(top_srcdir)/Common/libcommon_utils.la -lsqlite3 # #new: clean all # nfs-ganesha-2.6.0/src/FSAL/FSAL_PROXY/handle_mapping/handle_mapping.c000066400000000000000000000252441324272410200250370ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: */ /* * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file handle_mapping.c * * \brief This module is used for managing a persistent * map between PROXY FSAL handles (including NFSv4 handles from server) * and nfsv3 handles digests (sent to client). */ #include "config.h" #include "fsal.h" #include "nfs4.h" #include "handle_mapping.h" #include "handle_mapping_db.h" #include "handle_mapping_internal.h" static hash_table_t *handle_map_hash; /* memory pool definitions */ typedef struct digest_pool_entry__ { nfs23_map_handle_t nfs23_digest; } digest_pool_entry_t; typedef struct handle_pool_entry__ { uint32_t fh_len; char fh_data[NFS4_FHSIZE]; } handle_pool_entry_t; pool_t *digest_pool; static pthread_mutex_t digest_pool_mutex = PTHREAD_MUTEX_INITIALIZER; pool_t *handle_pool; static pthread_mutex_t handle_pool_mutex = PTHREAD_MUTEX_INITIALIZER; /* helpers for pool allocation */ static digest_pool_entry_t *digest_alloc() { digest_pool_entry_t *p_new; PTHREAD_MUTEX_lock(&digest_pool_mutex); p_new = pool_alloc(digest_pool); PTHREAD_MUTEX_unlock(&digest_pool_mutex); return p_new; } static void digest_free(digest_pool_entry_t *p_digest) { memset(p_digest, 0, sizeof(digest_pool_entry_t)); PTHREAD_MUTEX_lock(&digest_pool_mutex); pool_free(digest_pool, p_digest); PTHREAD_MUTEX_unlock(&digest_pool_mutex); } static handle_pool_entry_t *handle_alloc() { handle_pool_entry_t *p_new; PTHREAD_MUTEX_lock(&handle_pool_mutex); p_new = pool_alloc(handle_pool); PTHREAD_MUTEX_unlock(&handle_pool_mutex); return p_new; } static void handle_free(handle_pool_entry_t *p_handle) { memset(p_handle, 0, sizeof(handle_pool_entry_t)); PTHREAD_MUTEX_lock(&handle_pool_mutex); pool_free(handle_pool, p_handle); PTHREAD_MUTEX_unlock(&handle_pool_mutex); } /* hash table functions */ static uint32_t hash_digest_idx(hash_parameter_t *p_conf, struct gsh_buffdesc *p_key) { uint32_t hash; digest_pool_entry_t *p_digest = (digest_pool_entry_t *) p_key->addr; hash = ((unsigned long)p_digest->nfs23_digest.object_id ^ (unsigned int)p_digest->nfs23_digest.handle_hash); hash = (743 * hash + 1999) % p_conf->index_size; return hash; } static unsigned long hash_digest_rbt(hash_parameter_t *p_conf, struct gsh_buffdesc *p_key) { unsigned long hash; digest_pool_entry_t *p_digest = (digest_pool_entry_t *) p_key->addr; hash = (257 * p_digest->nfs23_digest.object_id + 541); return hash; } static int cmp_digest(struct gsh_buffdesc *p_key1, struct gsh_buffdesc *p_key2) { digest_pool_entry_t *p_digest1 = (digest_pool_entry_t *) p_key1->addr; digest_pool_entry_t *p_digest2 = (digest_pool_entry_t *) p_key2->addr; /* compare object_id and handle_hash */ if (p_digest1->nfs23_digest.object_id != p_digest2->nfs23_digest.object_id) return (int)(p_digest1->nfs23_digest.object_id - p_digest2->nfs23_digest.object_id); else if (p_digest1->nfs23_digest.handle_hash != p_digest2->nfs23_digest.handle_hash) return (int)p_digest1->nfs23_digest.handle_hash - (int)p_digest2->nfs23_digest.handle_hash; else /* same */ return 0; } static int print_digest(struct gsh_buffdesc *p_val, char *outbuff) { digest_pool_entry_t *p_digest = (digest_pool_entry_t *) p_val->addr; return sprintf(outbuff, "%llu, %u", (unsigned long long)p_digest->nfs23_digest.object_id, p_digest->nfs23_digest.handle_hash); } static int print_handle(struct gsh_buffdesc *p_val, char *outbuff) { handle_pool_entry_t *p_handle = (handle_pool_entry_t *) p_val->addr; return snprintmem(outbuff, HASHTABLE_DISPLAY_STRLEN, p_handle->fh_data, p_handle->fh_len); } int handle_mapping_hash_add(hash_table_t *p_hash, uint64_t object_id, unsigned int handle_hash, const void *data, uint32_t datalen) { int rc; struct gsh_buffdesc buffkey; struct gsh_buffdesc buffval; digest_pool_entry_t *digest; handle_pool_entry_t *handle; if (datalen >= sizeof(handle->fh_data)) return HANDLEMAP_INVALID_PARAM; digest = digest_alloc(); if (!digest) return HANDLEMAP_SYSTEM_ERROR; handle = handle_alloc(); if (!handle) { digest_free(digest); return HANDLEMAP_SYSTEM_ERROR; } digest->nfs23_digest.object_id = object_id; digest->nfs23_digest.handle_hash = handle_hash; memset(handle->fh_data, 0, sizeof(handle->fh_data)); memcpy(handle->fh_data, data, datalen); handle->fh_len = datalen; buffkey.addr = (caddr_t) digest; buffkey.len = sizeof(digest_pool_entry_t); buffval.addr = (caddr_t) handle; buffval.len = sizeof(handle_pool_entry_t); rc = hashtable_test_and_set(handle_map_hash, &buffkey, &buffval, HASHTABLE_SET_HOW_SET_NO_OVERWRITE); if (rc != HASHTABLE_SUCCESS) { digest_free(digest); handle_free(handle); if (rc != HASHTABLE_ERROR_KEY_ALREADY_EXISTS) { LogCrit(COMPONENT_FSAL, "ERROR %d inserting entry to handle mapping hash table", rc); return HANDLEMAP_HASHTABLE_ERROR; } else { return HANDLEMAP_EXISTS; } } return HANDLEMAP_SUCCESS; } /* DEFAULT PARAMETERS for hash table */ static hash_parameter_t handle_hash_config = { .index_size = 67, .hash_func_key = hash_digest_idx, .hash_func_rbt = hash_digest_rbt, .compare_key = cmp_digest, .key_to_str = print_digest, .val_to_str = print_handle }; /** * Init handle mapping module. * Reloads the content of the mapping files it they exist, * else it creates them. * \return 0 if OK, a posix error code else. */ int HandleMap_Init(const handle_map_param_t *p_param) { int rc; /* first check database count */ rc = handlemap_db_count(p_param->databases_directory); if ((rc > 0) && (rc != p_param->database_count)) { LogCrit(COMPONENT_FSAL, "ERROR: The number of existing databases (%u) does not match the requested DB thread count (%u)", rc, p_param->database_count); return HANDLEMAP_INVALID_PARAM; } else if (rc < 0) return -rc; /* init database module */ rc = handlemap_db_init(p_param->databases_directory, p_param->temp_directory, p_param->database_count, p_param->synchronous_insert); if (rc) { LogCrit(COMPONENT_FSAL, "ERROR %d initializing database access", rc); return rc; } /* initialize memory pool of digests and handles */ digest_pool = pool_basic_init("digest_pool", sizeof(digest_pool_entry_t)); handle_pool = pool_basic_init("handle_pool", sizeof(handle_pool_entry_t)); /* create hash table */ handle_hash_config.index_size = p_param->hashtable_size; handle_map_hash = hashtable_init(&handle_hash_config); if (!handle_map_hash) { LogCrit(COMPONENT_FSAL, "ERROR creating hash table for handle mapping"); return HANDLEMAP_INTERNAL_ERROR; } /* reload previous data */ rc = handlemap_db_reaload_all(handle_map_hash); if (rc) { LogCrit(COMPONENT_FSAL, "ERROR %d reloading handle mapping from database", rc); return rc; } return HANDLEMAP_SUCCESS; } /** * @brief Retrieves a full fsal_handle from a NFS3 digest. * * @param[in] nfs23_digest The NFS3 handle digest * @param[out] fsal_handle The fsal handle to be retrieved * * @note The caller must provide storage for nfs_fh4_val. * * @retval HANDLEMAP_SUCCESS if the handle is available * @retval HANDLEMAP_STALE if the disgest is unknown or the handle has been * deleted */ int HandleMap_GetFH(const nfs23_map_handle_t *nfs23_digest, struct gsh_buffdesc *fsal_handle) { int rc; struct gsh_buffdesc buffkey; struct gsh_buffdesc buffval; digest_pool_entry_t digest; struct hash_latch hl; digest.nfs23_digest = *nfs23_digest; buffkey.addr = (caddr_t) &digest; buffkey.len = sizeof(digest_pool_entry_t); rc = hashtable_getlatch(handle_map_hash, &buffkey, &buffval, 0, &hl); if (rc == HASHTABLE_SUCCESS) { handle_pool_entry_t *h = (handle_pool_entry_t *) buffval.addr; if (h->fh_len < fsal_handle->len) { fsal_handle->len = h->fh_len; memcpy(fsal_handle->addr, h->fh_data, h->fh_len); rc = HANDLEMAP_SUCCESS; } else { rc = HANDLEMAP_INTERNAL_ERROR; } hashtable_releaselatched(handle_map_hash, &hl); return rc; } if (rc == HASHTABLE_ERROR_NO_SUCH_KEY) hashtable_releaselatched(handle_map_hash, &hl); return HANDLEMAP_STALE; } /* HandleMap_GetFH */ /** * Save the handle association if it was unknown. */ int HandleMap_SetFH(nfs23_map_handle_t *p_in_nfs23_digest, const void *data, uint32_t len) { int rc; /* first, try to insert it to the hash table */ rc = handle_mapping_hash_add(handle_map_hash, p_in_nfs23_digest->object_id, p_in_nfs23_digest->handle_hash, data, len); if ((rc != 0) && (rc != HANDLEMAP_EXISTS)) /* error */ return rc; else if (rc == HANDLEMAP_EXISTS) /* already in database */ return HANDLEMAP_EXISTS; else { /* insert it to DB */ return handlemap_db_insert(p_in_nfs23_digest, data, len); } } /** * Remove a handle from the map * when it was removed from the filesystem * or when it is stale. */ int HandleMap_DelFH(nfs23_map_handle_t *p_in_nfs23_digest) { int rc; struct gsh_buffdesc buffkey, stored_buffkey; struct gsh_buffdesc stored_buffval; digest_pool_entry_t digest; digest_pool_entry_t *p_stored_digest; handle_pool_entry_t *p_stored_handle; /* first, delete it from hash table */ digest.nfs23_digest = *p_in_nfs23_digest; buffkey.addr = (caddr_t) &digest; buffkey.len = sizeof(digest_pool_entry_t); rc = HashTable_Del(handle_map_hash, &buffkey, &stored_buffkey, &stored_buffval); if (rc != HASHTABLE_SUCCESS) return HANDLEMAP_STALE; p_stored_digest = (digest_pool_entry_t *) stored_buffkey.addr; p_stored_handle = (handle_pool_entry_t *) stored_buffval.addr; digest_free(p_stored_digest); handle_free(p_stored_handle); /* then, submit the request to the database */ return handlemap_db_delete(p_in_nfs23_digest); } /** * Flush pending database operations (before stopping the server). */ int HandleMap_Flush(void) { return handlemap_db_flush(); } nfs-ganesha-2.6.0/src/FSAL/FSAL_PROXY/handle_mapping/handle_mapping.h000066400000000000000000000052051324272410200250370ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: */ /* * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-130 USA * * --------------------------------------- */ /** * @file handle_mapping.h * * @brief This module is used for managing a persistent * map between PROXY FSAL handles (including NFSv4 handles from server) * and nfsv3 handles digests (sent to client). */ #ifndef _HANDLE_MAPPING_H #define _HANDLE_MAPPING_H #include "fsal.h" /* parameters for Handle Map module */ typedef struct handle_map_param__ { /* path where database files are located */ char *databases_directory; /* temp dir for database work */ char *temp_directory; /* number of databases */ unsigned int database_count; /* hash table size */ unsigned int hashtable_size; /* synchronous insert mode */ int synchronous_insert; } handle_map_param_t; /* this describes a handle digest for nfsv3 */ #define PXY_HANDLE_MAPPED 0x23 typedef struct nfs23_map_handle__ { uint8_t len; uint8_t type; /* Must be PXY_HANDLE_MAPPED */ /* to avoid reusing handles, when object_id is reused */ unsigned int handle_hash; /* object id */ uint64_t object_id; } nfs23_map_handle_t; /* Error codes */ #define HANDLEMAP_SUCCESS 0 #define HANDLEMAP_STALE 1 #define HANDLEMAP_INCONSISTENCY 2 #define HANDLEMAP_DB_ERROR 3 #define HANDLEMAP_SYSTEM_ERROR 4 #define HANDLEMAP_INTERNAL_ERROR 5 #define HANDLEMAP_INVALID_PARAM 6 #define HANDLEMAP_HASHTABLE_ERROR 7 #define HANDLEMAP_EXISTS 8 int HandleMap_Init(const handle_map_param_t *p_param); int HandleMap_GetFH(const nfs23_map_handle_t *, struct gsh_buffdesc *); int HandleMap_SetFH(nfs23_map_handle_t *p_in_nfs23_digest, const void *p_in_handle, uint32_t len); int HandleMap_DelFH(nfs23_map_handle_t *p_in_nfs23_digest); int HandleMap_Flush(void); #endif nfs-ganesha-2.6.0/src/FSAL/FSAL_PROXY/handle_mapping/handle_mapping_db.c000066400000000000000000000621321324272410200255010ustar00rootroot00000000000000#include "config.h" #include "handle_mapping.h" #include "handle_mapping_db.h" #include "handle_mapping_internal.h" #include #include #include #include #include #include #include #include /* sqlite check macros */ #define CheckTable(_p_conn_, _code_, _msg_str_, _result_) \ do { \ if ((_code_) != SQLITE_OK) { \ LogCrit(COMPONENT_FSAL, \ "SQLite command failed in %s line %i", \ __func__, __LINE__); \ LogCrit(COMPONENT_FSAL, "%s (%d)", \ (_msg_str_ ? _msg_str_ : sqlite3_errmsg(_p_conn_)), \ _code_); \ if (_msg_str_) { \ sqlite3_free(_msg_str_); \ _msg_str_ = NULL; \ } \ if (_result_) { \ sqlite3_free_table(_result_); \ _result_ = NULL; \ } \ return HANDLEMAP_DB_ERROR; \ } \ } while (0) #define CheckCommand(_p_conn_, _code_, _msg_str_) \ do { \ if ((_code_) != SQLITE_OK) { \ LogCrit(COMPONENT_FSAL, \ "SQLite command failed in %s line %i", \ __func__, __LINE__); \ LogCrit(COMPONENT_FSAL, "%s (%d)", \ (_msg_str_ ? _msg_str_ : sqlite3_errmsg(_p_conn_)), \ _code_); \ if (_msg_str_) { \ sqlite3_free(_msg_str_); \ _msg_str_ = NULL; \ } \ return HANDLEMAP_DB_ERROR; \ } \ } while (0) #define CheckPrepare(_p_conn_, _code_) \ do { \ if ((_code_) != SQLITE_OK) { \ LogCrit(COMPONENT_FSAL, \ "SQLite prepare statement failed in %s line %i", \ __func__, __LINE__); \ LogCrit(COMPONENT_FSAL, "%s (%d)", \ sqlite3_errmsg(_p_conn_), _code_); \ return HANDLEMAP_DB_ERROR; \ } \ } while (0) #define CheckBind(_p_conn_, _code_, _stmt_) \ do { \ if ((_code_) != SQLITE_OK) { \ LogCrit(COMPONENT_FSAL, \ "SQLite parameter binding failed in %s line %i", \ __func__, __LINE__); \ LogCrit(COMPONENT_FSAL, "%s (%d)", \ sqlite3_errmsg(_p_conn_), _code_); \ sqlite3_clear_bindings(_stmt_); \ return HANDLEMAP_DB_ERROR; \ } \ } while (0) #define CheckStep(_p_conn_, _code_, _stmt_) \ do { \ if ((_code_) != SQLITE_OK && (_code_) != \ SQLITE_ROW && (_code_) != SQLITE_DONE) { \ LogCrit(COMPONENT_FSAL, \ "SQLite command failed in %s line %i", \ __func__, __LINE__); \ LogCrit(COMPONENT_FSAL, "%s (%d)", \ sqlite3_errmsg(_p_conn_), _code_); \ sqlite3_reset(_stmt_); \ return HANDLEMAP_DB_ERROR; \ } \ } while (0) /* Type of DB operations */ typedef enum { LOAD = 1, INSERT, DELETE } db_op_type; /* DB operation arguments */ typedef struct db_op_item__ { db_op_type op_type; /* operation info */ union { struct hdlmap_tuple { nfs23_map_handle_t nfs23_digest; uint8_t fh4_len; char fh4_data[NFS4_FHSIZE]; } fh_info; hash_table_t *hash; } op_arg; /* for chained list */ struct db_op_item__ *p_next; } db_op_item_t; /* the queue for each DB flusher thread */ typedef struct flusher_queue__ { /* the queue for high priority operations */ db_op_item_t *highprio_first; db_op_item_t *highprio_last; /* the queue for low priority operations */ db_op_item_t *lowprio_first; db_op_item_t *lowprio_last; /* number of operations pending */ unsigned int nb_waiting; pthread_mutex_t queues_mutex; pthread_cond_t work_avail_condition; pthread_cond_t work_done_condition; /* status (used for work_done_condition) */ enum { NOT_READY, IDLE, WORKING, FINISHED } status; } flusher_queue_t; #define LOAD_ALL_STATEMENT 0 #define INSERT_STATEMENT 1 #define DELETE_STATEMENT 2 #define STATEMENT_COUNT 3 /* thread info */ typedef struct db_thread_info__ { pthread_t thr_id; unsigned int thr_index; flusher_queue_t work_queue; /* SQLite database connection */ sqlite3 *db_conn; /* prepared statement table */ sqlite3_stmt * prep_stmt[STATEMENT_COUNT]; /* this pool is accessed by submitter * and by the db thread */ pthread_mutex_t pool_mutex; pool_t *dbop_pool; } db_thread_info_t; static char dbmap_dir[MAXPATHLEN + 1]; static char db_tmpdir[MAXPATHLEN + 1]; static unsigned int nb_db_threads; static int synchronous; /* used for clean shutdown */ static int do_terminate; /* all information and context for threads */ static db_thread_info_t db_thread[MAX_DB]; /** * @brief Print memory to a a hex string * * @param[out] target Buffer where memory is to be printed * @param[in] tgt_size Size of the target buffer * @param[in] source Buffer to be printed * @param[in] mem_size Size of the buffer * * @return The number of bytes written in the target buffer. */ int snprintmem(char *target, size_t tgt_size, const void *source, size_t mem_size) { const unsigned char *c; /* the current char to be printed */ char *str = target; /* the current position in target buffer */ int wrote = 0; for (c = (const unsigned char *)source; c < ((const unsigned char *)source + mem_size); c++) { int tmp_wrote = 0; if (wrote >= tgt_size) { target[tgt_size - 1] = '\0'; break; } tmp_wrote = snprintf(str, tgt_size - wrote, "%.2X", (unsigned char)*c); str += tmp_wrote; wrote += tmp_wrote; } return wrote; } /* test if a letter is hexa */ #define IS_HEXA(c) \ ((((c) >= '0') && ((c) <= '9')) || (((c) >= 'A') && ((c) <= 'F')) \ || (((c) >= 'a') && ((c) <= 'f'))) /* converts an hexa letter */ #define HEXA2BYTE(c) \ ((unsigned char) \ (((c) >= '0') && ((c) <= '9') ? \ ((c) - '0') : (((c) >= 'A') && ((c) <= 'F') ? \ ((c) - 'A' + 10) : (((c) >= 'a') && \ ((c) <= 'f') ? \ ((c) - 'a' + 10) : 0)))) /** * @brief Read a hexadecimal string into memory * * @param[out] target Where memory is to be written * @param[in] tgt_size Size of the target buffer * @param[in] str_source Hexadecimal string * * @retval The number of bytes read in the source string. * @retval -1 on error. */ int sscanmem(void *target, size_t tgt_size, const char *str_source) { unsigned char *mem; /* the current byte to be set */ const char *src; /* pointer to the current char to be read. */ int nb_read = 0; src = str_source; for (mem = (unsigned char *)target; mem < ((unsigned char *)target + tgt_size); mem++) { unsigned char tmp_val; /* we must read 2 bytes (written in hexa) to have 1 target byte value. */ if ((*src == '\0') || (*(src + 1) == '\0')) { /* error, the source string is too small */ return -1; } /* they must be hexa values */ if (!IS_HEXA(*src) || !IS_HEXA(*(src + 1))) return -1; /* we read hexa values. */ tmp_val = (HEXA2BYTE(*src) << 4) + HEXA2BYTE(*(src + 1)); /* we had them to the target buffer */ (*mem) = tmp_val; src += 2; nb_read += 2; } return nb_read; } /* Initialize basic structures for a thread */ static int init_db_thread_info(db_thread_info_t *p_thr_info, unsigned int nb_dbop_prealloc) { unsigned int i; if (!p_thr_info) return HANDLEMAP_INTERNAL_ERROR; memset(p_thr_info, 0, sizeof(db_thread_info_t)); p_thr_info->work_queue.highprio_first = NULL; p_thr_info->work_queue.highprio_last = NULL; p_thr_info->work_queue.lowprio_first = NULL; p_thr_info->work_queue.lowprio_last = NULL; p_thr_info->work_queue.nb_waiting = 0; if (pthread_mutex_init(&p_thr_info->work_queue.queues_mutex, NULL)) return HANDLEMAP_SYSTEM_ERROR; if (pthread_cond_init (&p_thr_info->work_queue.work_avail_condition, NULL)) return HANDLEMAP_SYSTEM_ERROR; if (pthread_cond_init (&p_thr_info->work_queue.work_done_condition, NULL)) return HANDLEMAP_SYSTEM_ERROR; /* init thread status */ p_thr_info->work_queue.status = NOT_READY; p_thr_info->db_conn = NULL; for (i = 0; i < STATEMENT_COUNT; i++) p_thr_info->prep_stmt[i] = NULL; /* init memory pool */ if (pthread_mutex_init(&p_thr_info->pool_mutex, NULL)) return HANDLEMAP_SYSTEM_ERROR; p_thr_info->dbop_pool = pool_basic_init("drop_pool", sizeof(db_op_item_t)); return HANDLEMAP_SUCCESS; } /* Called by a thread to initialize its database access. * After this call: * - database connection is established * - schema is created * - prepared statements are ready to be used */ static int init_database_access(db_thread_info_t *p_thr_info) { char db_file[MAXPATHLEN + 1]; int rc; char **result = NULL; int rows, cols; char *errmsg = NULL; const char *unparsed; /* first open the database file */ snprintf(db_file, MAXPATHLEN, "%s/%s.%u", dbmap_dir, DB_FILE_PREFIX, p_thr_info->thr_index); rc = sqlite3_open(db_file, &p_thr_info->db_conn); if (rc != 0) { if (p_thr_info->db_conn) { LogCrit(COMPONENT_FSAL, "ERROR: could not connect to SQLite3 database (file %s): %s", db_file, sqlite3_errmsg(p_thr_info->db_conn)); sqlite3_close(p_thr_info->db_conn); } else { LogCrit(COMPONENT_FSAL, "ERROR: could not connect to SQLite3 database (file %s): status=%d", db_file, rc); } return HANDLEMAP_DB_ERROR; } /* Now check, that the map table exists */ rc = sqlite3_get_table(p_thr_info->db_conn, "SELECT name FROM sqlite_master WHERE type = 'table' AND name = '" MAP_TABLE "'", &result, &rows, &cols, &errmsg); CheckTable(p_thr_info->db_conn, rc, errmsg, result); /* no need for the result, just the number of rows returned */ sqlite3_free_table(result); if (rows != 1) { /* table must be created */ rc = sqlite3_exec(p_thr_info->db_conn, "CREATE TABLE " MAP_TABLE " ( " OBJID_FIELD " BIGINT NOT NULL, " HASH_FIELD " INT NOT NULL, " HANDLE_FIELD " TEXT, PRIMARY KEY(" OBJID_FIELD ", " HASH_FIELD ") )", NULL, NULL, &errmsg); CheckCommand(p_thr_info->db_conn, rc, errmsg); } /* Now, create prepared statements */ rc = sqlite3_prepare_v2(p_thr_info->db_conn, "SELECT " OBJID_FIELD "," HASH_FIELD "," HANDLE_FIELD " FROM " MAP_TABLE, -1, &(p_thr_info->prep_stmt[LOAD_ALL_STATEMENT]), &unparsed); CheckPrepare(p_thr_info->db_conn, rc); rc = sqlite3_prepare_v2(p_thr_info->db_conn, "INSERT INTO " MAP_TABLE "(" OBJID_FIELD "," HASH_FIELD "," HANDLE_FIELD ") VALUES (?1, ?2, ?3 )", -1, &(p_thr_info->prep_stmt[INSERT_STATEMENT]), &unparsed); CheckPrepare(p_thr_info->db_conn, rc); rc = sqlite3_prepare_v2(p_thr_info->db_conn, "DELETE FROM " MAP_TABLE " WHERE " OBJID_FIELD "=?1 AND " HASH_FIELD "=?2", -1, &(p_thr_info->prep_stmt[DELETE_STATEMENT]), &unparsed); CheckPrepare(p_thr_info->db_conn, rc); /* Everything is OK now ! */ return HANDLEMAP_SUCCESS; } /* init_database_access */ static int db_load_operation(db_thread_info_t *p_info, hash_table_t *p_hash) { /* the object id to be inserted to hash table */ uint64_t object_id; unsigned int handle_hash; const char *fsal_handle_str; char fh4_data[NFS4_FHSIZE]; unsigned int nb_loaded = 0; int rc; struct timeval t1; struct timeval t2; struct timeval tdiff; gettimeofday(&t1, NULL); rc = sqlite3_step(p_info->prep_stmt[LOAD_ALL_STATEMENT]); CheckStep(p_info->db_conn, rc, p_info->prep_stmt[LOAD_ALL_STATEMENT]); /* something to read */ while (rc == SQLITE_ROW) { object_id = sqlite3_column_int64(p_info->prep_stmt[LOAD_ALL_STATEMENT], 0); handle_hash = sqlite3_column_int(p_info->prep_stmt[LOAD_ALL_STATEMENT], 1); fsal_handle_str = sqlite3_column_text(p_info->prep_stmt[LOAD_ALL_STATEMENT], 2); if (fsal_handle_str) { int len = strlen(fsal_handle_str); if ((len & 1) || len > NFS4_FHSIZE * 2) { LogEvent(COMPONENT_FSAL, "Bogus handle '%s' - wrong number of symbols", fsal_handle_str); } else { /* convert hexa string representation * to binary data */ if (sscanmem(fh4_data, len / 2, fsal_handle_str) != len) { LogEvent(COMPONENT_FSAL, "Bogus entry '%s' - cannot convert", fsal_handle_str); } else { /* now insert it to the hash table */ rc = handle_mapping_hash_add( p_hash, object_id, handle_hash, fh4_data, len / 2); if (rc == 0) nb_loaded++; else LogCrit(COMPONENT_FSAL, "ERROR %d adding entry to hash table ", rc, (unsigned long long) object_id, handle_hash, fsal_handle_str); } } } else { LogEvent(COMPONENT_FSAL, "Empty handle in object %lld, hash %d", (unsigned long long)object_id, handle_hash); } rc = sqlite3_step(p_info->prep_stmt[LOAD_ALL_STATEMENT]); CheckStep(p_info->db_conn, rc, p_info->prep_stmt[LOAD_ALL_STATEMENT]); } /* clear results */ sqlite3_reset(p_info->prep_stmt[LOAD_ALL_STATEMENT]); /* print time and item count */ gettimeofday(&t2, NULL); timersub(&t2, &t1, &tdiff); LogEvent(COMPONENT_FSAL, "Reloaded %u items in %d.%06ds", nb_loaded, (int)tdiff.tv_sec, (int)tdiff.tv_usec); return HANDLEMAP_SUCCESS; } /* db_load_operation */ static int db_insert_operation(db_thread_info_t *p_info, struct hdlmap_tuple *data) { int rc; char handle_str[2 * NFS4_FHSIZE + 1]; rc = sqlite3_bind_int64(p_info->prep_stmt[INSERT_STATEMENT], 1, data->nfs23_digest.object_id); CheckBind(p_info->db_conn, rc, p_info->prep_stmt[INSERT_STATEMENT]); rc = sqlite3_bind_int(p_info->prep_stmt[INSERT_STATEMENT], 2, data->nfs23_digest.handle_hash); CheckBind(p_info->db_conn, rc, p_info->prep_stmt[INSERT_STATEMENT]); snprintmem(handle_str, sizeof(handle_str), data->fh4_data, data->fh4_len); rc = sqlite3_bind_text(p_info->prep_stmt[INSERT_STATEMENT], 3, handle_str, -1, SQLITE_STATIC); CheckBind(p_info->db_conn, rc, p_info->prep_stmt[INSERT_STATEMENT]); rc = sqlite3_step(p_info->prep_stmt[INSERT_STATEMENT]); CheckStep(p_info->db_conn, rc, p_info->prep_stmt[INSERT_STATEMENT]); /* clear results */ sqlite3_reset(p_info->prep_stmt[INSERT_STATEMENT]); return HANDLEMAP_SUCCESS; } /* db_insert_operation */ static int db_delete_operation(db_thread_info_t *p_info, nfs23_map_handle_t *p_nfs23_digest) { int rc; rc = sqlite3_bind_int64(p_info->prep_stmt[DELETE_STATEMENT], 1, p_nfs23_digest->object_id); CheckBind(p_info->db_conn, rc, p_info->prep_stmt[DELETE_STATEMENT]); rc = sqlite3_bind_int(p_info->prep_stmt[DELETE_STATEMENT], 2, p_nfs23_digest->handle_hash); CheckBind(p_info->db_conn, rc, p_info->prep_stmt[DELETE_STATEMENT]); rc = sqlite3_step(p_info->prep_stmt[DELETE_STATEMENT]); CheckStep(p_info->db_conn, rc, p_info->prep_stmt[DELETE_STATEMENT]); /* clear results */ sqlite3_reset(p_info->prep_stmt[DELETE_STATEMENT]); return HANDLEMAP_SUCCESS; } /* db_delete_operation */ /* push a task to the queue */ static int dbop_push(flusher_queue_t *p_queue, db_op_item_t *p_op) { PTHREAD_MUTEX_lock(&p_queue->queues_mutex); /* add an item at the end of the queue */ switch (p_op->op_type) { case LOAD: case INSERT: /* high priority operations */ p_op->p_next = NULL; if (p_queue->highprio_last == NULL) { /* first operation */ p_queue->highprio_first = p_op; p_queue->highprio_last = p_op; } else { p_queue->highprio_last->p_next = p_op; p_queue->highprio_last = p_op; } p_queue->nb_waiting++; break; case DELETE: /* low priority operation */ p_op->p_next = NULL; if (p_queue->lowprio_last == NULL) { /* first operation */ p_queue->lowprio_first = p_op; p_queue->lowprio_last = p_op; } else { p_queue->lowprio_last->p_next = p_op; p_queue->lowprio_last = p_op; } p_queue->nb_waiting++; break; default: LogCrit(COMPONENT_FSAL, "ERROR in dbop push: Invalid operation type %d", p_op->op_type); } /* there now some work available */ pthread_cond_signal(&p_queue->work_avail_condition); PTHREAD_MUTEX_unlock(&p_queue->queues_mutex); return HANDLEMAP_SUCCESS; } static void *database_worker_thread(void *arg) { db_thread_info_t *p_info = (db_thread_info_t *) arg; int rc; db_op_item_t *to_be_done = NULL; char thread_name[256]; /* initialize logging */ snprintf(thread_name, 256, "DB thread #%u", p_info->thr_index); SetNameFunction(thread_name); /* initialize memory management */ rc = init_database_access(p_info); if (rc != HANDLEMAP_SUCCESS) { /* Failed init */ LogCrit(COMPONENT_FSAL, "ERROR: Database initialization error %d", rc); exit(rc); } /* main loop */ while (1) { /* Is "work done" or "work available" condition verified ? */ PTHREAD_MUTEX_lock(&p_info->work_queue.queues_mutex); /* nothing to be done ? */ while (p_info->work_queue.highprio_first == NULL && p_info->work_queue.lowprio_first == NULL) { to_be_done = NULL; p_info->work_queue.status = IDLE; pthread_cond_signal( &p_info->work_queue.work_done_condition); /* if termination is requested, exit */ if (do_terminate) { p_info->work_queue.status = FINISHED; PTHREAD_MUTEX_unlock(&p_info->work_queue .queues_mutex); return (void *)p_info; } /* else, wait for something to do */ pthread_cond_wait( &p_info->work_queue.work_avail_condition, &p_info->work_queue.queues_mutex); } /* there is something to do: * first check the highest priority list, * then the lower priority. */ if (p_info->work_queue.highprio_first != NULL) { /* take the next item in the list */ to_be_done = p_info->work_queue.highprio_first; p_info->work_queue.highprio_first = to_be_done->p_next; /* still any entries in the list ? */ if (p_info->work_queue.highprio_first == NULL) p_info->work_queue.highprio_last = NULL; /* it it the last entry ? */ else if (p_info->work_queue.highprio_first->p_next == NULL) p_info->work_queue.highprio_last = p_info->work_queue.highprio_first; /* something to do */ p_info->work_queue.status = WORKING; } else if (p_info->work_queue.lowprio_first != NULL) { /* take the next item in the list */ to_be_done = p_info->work_queue.lowprio_first; p_info->work_queue.lowprio_first = to_be_done->p_next; /* still any entries in the list ? */ if (p_info->work_queue.lowprio_first == NULL) p_info->work_queue.lowprio_last = NULL; /* it it the last entry ? */ else if (p_info->work_queue.lowprio_first->p_next == NULL) p_info->work_queue.lowprio_last = p_info->work_queue.lowprio_first; /* something to do */ p_info->work_queue.status = WORKING; } p_info->work_queue.nb_waiting--; PTHREAD_MUTEX_unlock(&p_info->work_queue.queues_mutex); /* PROCESS THE REQUEST */ switch (to_be_done->op_type) { case LOAD: db_load_operation(p_info, to_be_done->op_arg.hash); break; case INSERT: db_insert_operation(p_info, &to_be_done->op_arg.fh_info); break; case DELETE: db_delete_operation( p_info, &to_be_done->op_arg.fh_info.nfs23_digest); break; default: LogCrit(COMPONENT_FSAL, "ERROR: Invalid operation type %d", to_be_done->op_type); } /* free the db operation item */ PTHREAD_MUTEX_lock(&p_info->pool_mutex); pool_free(p_info->dbop_pool, to_be_done); PTHREAD_MUTEX_unlock(&p_info->pool_mutex); } /* loop forever */ return (void *)p_info; } /** * count the number of database instances in a given directory * (this is used for checking that the number of db * matches the number of threads) */ int handlemap_db_count(const char *dir) { DIR *dir_hdl; struct dirent *direntry; char db_pattern[MAXPATHLEN + 1]; unsigned int count = 0; int end_of_dir = false; snprintf(db_pattern, MAXPATHLEN, "%s.*[0-9]", DB_FILE_PREFIX); dir_hdl = opendir(dir); if (dir_hdl == NULL) { LogCrit(COMPONENT_FSAL, "ERROR: could not access directory %s: %s", dir, strerror(errno)); return -HANDLEMAP_SYSTEM_ERROR; } do { errno = 0; direntry = readdir(dir_hdl); if (direntry != NULL) { /* go to the next loop if the entry is . or .. */ if (!strcmp(".", direntry->d_name) || !strcmp("..", direntry->d_name)) continue; /* does it match the expected db pattern ? */ if (!fnmatch(db_pattern, direntry->d_name, FNM_PATHNAME)) count++; } else if (errno == 0) { /* end of dir */ end_of_dir = true; } else { /* error */ LogCrit(COMPONENT_FSAL, "ERROR: error reading directory %s: %s", dir, strerror(errno)); closedir(dir_hdl); return -HANDLEMAP_SYSTEM_ERROR; } } while (!end_of_dir); closedir(dir_hdl); return count; } /* handlemap_db_count */ unsigned int select_db_queue(const nfs23_map_handle_t *p_nfs23_digest) { unsigned int h = ((p_nfs23_digest->object_id * 1049) ^ p_nfs23_digest->handle_hash) % 2477; h = h % nb_db_threads; return h; } /** * Initialize databases access * - init DB queues * - start threads * - establish DB connections * - create db schema if it was empty */ int handlemap_db_init(const char *db_dir, const char *tmp_dir, unsigned int db_count, int synchronous_insert) { unsigned int i; int rc; /* first, save the parameters */ strncpy(dbmap_dir, db_dir, MAXPATHLEN); strncpy(db_tmpdir, tmp_dir, MAXPATHLEN); if (db_count > MAX_DB) return HANDLEMAP_INVALID_PARAM; nb_db_threads = db_count; synchronous = synchronous_insert; /* set global database engine info */ sqlite3_temp_directory = db_tmpdir; /* initialize structures for each thread and launch it */ for (i = 0; i < nb_db_threads; i++) { rc = init_db_thread_info(&db_thread[i], 100); if (rc) return rc; db_thread[i].thr_index = i; rc = pthread_create(&db_thread[i].thr_id, NULL, database_worker_thread, &db_thread[i]); if (rc) return HANDLEMAP_SYSTEM_ERROR; } /* I'm ready to serve, my Lord ! */ return HANDLEMAP_SUCCESS; } /* wait that a thread has done all its jobs */ static void wait_thread_jobs_finished(db_thread_info_t *p_thr_info) { PTHREAD_MUTEX_lock(&p_thr_info->work_queue.queues_mutex); /* wait until the thread has no more tasks in its queue * and it is no more working */ while (p_thr_info->work_queue.highprio_first != NULL || p_thr_info->work_queue.lowprio_first != NULL || p_thr_info->work_queue.status == WORKING) pthread_cond_wait(&p_thr_info->work_queue.work_done_condition, &p_thr_info->work_queue.queues_mutex); PTHREAD_MUTEX_unlock(&p_thr_info->work_queue.queues_mutex); } /** * Gives the order to each DB thread to reload * the content of its database and insert it * to the hash table. * The function blocks until all threads have loaded their data. */ int handlemap_db_reaload_all(hash_table_t *target_hash) { unsigned int i; db_op_item_t *new_task; int rc; /* give the job to all threads */ for (i = 0; i < nb_db_threads; i++) { /* get a new db operation */ PTHREAD_MUTEX_lock(&db_thread[i].pool_mutex); new_task = pool_alloc(db_thread[i].dbop_pool); PTHREAD_MUTEX_unlock(&db_thread[i].pool_mutex); /* can you fill it ? */ new_task->op_type = LOAD; new_task->op_arg.hash = target_hash; rc = dbop_push(&db_thread[i].work_queue, new_task); if (rc) return rc; } /* wait for all threads to finish their job */ for (i = 0; i < nb_db_threads; i++) wait_thread_jobs_finished(&db_thread[i]); return HANDLEMAP_SUCCESS; } /* handlemap_db_reaload_all */ /** * Submit a db 'insert' request. * The request is inserted in the appropriate db queue. */ int handlemap_db_insert(nfs23_map_handle_t *p_in_nfs23_digest, const void *data, uint32_t len) { unsigned int i; db_op_item_t *new_task; int rc; if (!synchronous) { /* which thread is going to handle this inode ? */ i = select_db_queue(p_in_nfs23_digest); /* get a new db operation */ PTHREAD_MUTEX_lock(&db_thread[i].pool_mutex); new_task = pool_alloc(db_thread[i].dbop_pool); PTHREAD_MUTEX_unlock(&db_thread[i].pool_mutex); /* fill the task info */ new_task->op_type = INSERT; new_task->op_arg.fh_info.nfs23_digest = *p_in_nfs23_digest; memcpy(new_task->op_arg.fh_info.fh4_data, data, len); new_task->op_arg.fh_info.fh4_len = len; rc = dbop_push(&db_thread[i].work_queue, new_task); if (rc) return rc; } /* else: @todo not supported yet */ return HANDLEMAP_SUCCESS; } /** * Submit a db 'delete' request. * The request is inserted in the appropriate db queue. * (always asynchronous) */ int handlemap_db_delete(nfs23_map_handle_t *p_in_nfs23_digest) { unsigned int i; db_op_item_t *new_task; int rc; /* which thread is going to handle this inode ? */ i = select_db_queue(p_in_nfs23_digest); /* get a new db operation */ PTHREAD_MUTEX_lock(&db_thread[i].pool_mutex); new_task = pool_alloc(db_thread[i].dbop_pool); PTHREAD_MUTEX_unlock(&db_thread[i].pool_mutex); /* fill the task info */ new_task->op_type = DELETE; new_task->op_arg.fh_info.nfs23_digest = *p_in_nfs23_digest; rc = dbop_push(&db_thread[i].work_queue, new_task); if (rc) return rc; return HANDLEMAP_SUCCESS; } /** * Wait for all queues to be empty * and all current DB request to be done. */ int handlemap_db_flush(void) { unsigned int i; struct timeval t1; struct timeval t2; struct timeval tdiff; unsigned int to_sync = 0; for (i = 0; i < nb_db_threads; i++) to_sync += db_thread[i].work_queue.nb_waiting; LogEvent(COMPONENT_FSAL, "Waiting for database synchronization (%u operations pending)", to_sync); gettimeofday(&t1, NULL); /* wait for all threads to finish their job */ for (i = 0; i < nb_db_threads; i++) wait_thread_jobs_finished(&db_thread[i]); gettimeofday(&t2, NULL); timersub(&t2, &t1, &tdiff); LogEvent(COMPONENT_FSAL, "Database synchronized in %d.%06ds", (int)tdiff.tv_sec, (int)tdiff.tv_usec); return HANDLEMAP_SUCCESS; } nfs-ganesha-2.6.0/src/FSAL/FSAL_PROXY/handle_mapping/handle_mapping_db.h000066400000000000000000000030231324272410200255000ustar00rootroot00000000000000#ifndef _HANDLE_MAPPING_DB_H #define _HANDLE_MAPPING_DB_H #include "handle_mapping.h" #include "hashtable.h" #define DB_FILE_PREFIX "handlemap.sqlite" /* Database definition */ #define MAP_TABLE "HandleMap" #define OBJID_FIELD "ObjectId" #define HASH_FIELD "HandleHash" #define HANDLE_FIELD "FSALHandle" #define MAX_DB 32 /** * count the number of database instances in a given directory * (this is used for checking that the number of db * matches the number of threads) */ int handlemap_db_count(const char *dir); /** * Initialize databases access * (init DB queues, start threads, establish DB connections, * and create db schema if it was empty). */ int handlemap_db_init(const char *db_dir, const char *tmp_dir, unsigned int db_count, int synchronous_insert); /** * Gives the order to each DB thread to reload * the content of its database and insert it * to the hash table. * The function blocks until all threads have loaded their data. */ int handlemap_db_reaload_all(hash_table_t *target_hash); /** * Submit a db 'insert' request. * The request is inserted in the appropriate db queue. */ int handlemap_db_insert(nfs23_map_handle_t *p_in_nfs23_digest, const void *data, uint32_t len); /** * Submit a db 'delete' request. * The request is inserted in the appropriate db queue. * (always asynchronous) */ int handlemap_db_delete(nfs23_map_handle_t *p_in_nfs23_digest); /** * Wait for all queues to be empty * and all current DB request to be done. */ int handlemap_db_flush(void); #endif nfs-ganesha-2.6.0/src/FSAL/FSAL_PROXY/handle_mapping/handle_mapping_internal.h000066400000000000000000000006341324272410200267340ustar00rootroot00000000000000 #ifndef _HANDLE_MAPPING_INTERNAL_H #define _HANDLE_MAPPING_INTERNAL_H #include "hashtable.h" int handle_mapping_hash_add(hash_table_t *p_hash, uint64_t object_id, unsigned int handle_hash, const void *data, uint32_t datalen); int snprintmem(char *target, size_t tgt_size, const void *source, size_t mem_size); int sscanmem(void *target, size_t tgt_size, const char *str_source); #endif nfs-ganesha-2.6.0/src/FSAL/FSAL_PROXY/handle_mapping/test_handle_mapping.c000066400000000000000000000045201324272410200260700ustar00rootroot00000000000000#include "config.h" #include "handle_mapping_db.h" #include int main(int argc, char **argv) { unsigned int i; struct timeval tv1, tv2, tv3, tvdiff; int count, rc; char *dir; handle_map_param_t param; time_t now; /* Init logging */ SetNamePgm("test_handle_mapping"); SetDefaultLogging("TEST"); SetNameFunction("main"); SetNameHost("localhost"); InitLogging(); if (argc != 3) { LogTest("usage: test_handle_mapping "); exit(1); } count = atoi(argv[2]) if (count == 0) { LogTest("usage: test_handle_mapping "); exit(1); } dir = argv[1]; strcpy(param.databases_directory, dir); strcpy(param.temp_directory, "/tmp"); param.database_count = count; param.hashtable_size = 27; param.nb_handles_prealloc = 1024; param.nb_db_op_prealloc = 1024; param.synchronous_insert = false; rc = HandleMap_Init(¶m); LogTest("HandleMap_Init() = %d", rc); if (rc) exit(rc); gettimeofday(&tv1, NULL); /* Now insert a set of handles */ now = time(NULL); for (i = 0; i < 10000; i++) { nfs23_map_handle_t nfs23_digest; fsal_handle_t handle; memset(&handle, i, sizeof(fsal_handle_t)); nfs23_digest.object_id = 12345 + i; nfs23_digest.handle_hash = (1999 * i + now) % 479001599; rc = HandleMap_SetFH(&nfs23_digest, &handle); if (rc && (rc != HANDLEMAP_EXISTS)) exit(rc); } gettimeofday(&tv2, NULL); timersub(&tv2, &tv1, &tvdiff); LogTest("%u threads inserted 10000 handles in %d.%06ds", count, (int)tvdiff.tv_sec, (int)tvdiff.tv_usec); /* Now get them ! */ for (i = 0; i < 10000; i++) { nfs23_map_handle_t nfs23_digest; fsal_handle_t handle; nfs23_digest.object_id = 12345 + i; nfs23_digest.handle_hash = (1999 * i + now) % 479001599; rc = HandleMap_GetFH(&nfs23_digest, &handle); if (rc) { LogTest("Error %d retrieving handle !", rc); exit(rc); } rc = HandleMap_DelFH(&nfs23_digest); if (rc) { LogTest("Error %d deleting handle !", rc); exit(rc); } } gettimeofday(&tv3, NULL); timersub(&tv3, &tv2, &tvdiff); LogTest("Retrieved and deleted 10000 handles in %d.%06ds", (int)tvdiff.tv_sec, (int)tvdiff.tv_usec); rc = HandleMap_Flush(); gettimeofday(&tv3, NULL); timersub(&tv3, &tv1, &tvdiff); LogTest("Total time with %u threads (including flush): %d.%06ds", count, (int)tvdiff.tv_sec, (int)tvdiff.tv_usec); exit(0); } nfs-ganesha-2.6.0/src/FSAL/FSAL_PROXY/handle_mapping/test_handle_mapping_db.c000066400000000000000000000046621324272410200265440ustar00rootroot00000000000000#include "config.h" #include "handle_mapping_db.h" #include int main(int argc, char **argv) { unsigned int i; struct timeval tv1, tv2, tv3, tvdiff; int count, rc; char *dir; time_t now; if (argc != 3) { LogTest("usage: test_handle_mapping_db "); exit(1); } count = atoi(argv[2]) if (count == 0) { LogTest("usage: test_handle_mapping_db "); exit(1); } dir = argv[1]; /* Init logging */ SetNamePgm("test_handle_mapping"); SetNameFileLog("/dev/tty"); SetNameFunction("main"); SetNameHost("localhost"); /* count databases */ rc = handlemap_db_count(dir); LogTest("handlemap_db_count(%s)=%d", dir, rc); if (rc != 0 && count != rc) { LogTest( "Warning: incompatible thread count %d <> database count %d", count, rc); } rc = handlemap_db_init(dir, "/tmp", count, 1024, false); LogTest("handlemap_db_init() = %d", rc); if (rc) exit(rc); rc = handlemap_db_reaload_all(NULL); LogTest("handlemap_db_reaload_all() = %d", rc); if (rc) exit(rc); gettimeofday(&tv1, NULL); /* Now insert a set of handles */ now = time(NULL); for (i = 0; i < 10000; i++) { nfs23_map_handle_t nfs23_digest; fsal_handle_t handle; memset(&handle, i, sizeof(fsal_handle_t)); nfs23_digest.object_id = 12345 + i; nfs23_digest.handle_hash = (1999 * i + now) % 479001599; rc = handlemap_db_insert(&nfs23_digest, &handle); if (rc) exit(rc); } gettimeofday(&tv2, NULL); timersub(&tv2, &tv1, &tvdiff); LogTest("%u threads inserted 10000 handles in %d.%06ds", count, (int)tvdiff.tv_sec, (int)tvdiff.tv_usec); rc = handlemap_db_flush(); gettimeofday(&tv3, NULL); timersub(&tv3, &tv1, &tvdiff); LogTest("Total time with %u threads (including flush): %d.%06ds", count, (int)tvdiff.tv_sec, (int)tvdiff.tv_usec); LogTest("Now, delete operations"); for (i = 0; i < 10000; i++) { nfs23_map_handle_t nfs23_digest; nfs23_digest.object_id = 12345 + i; nfs23_digest.handle_hash = (1999 * i + now) % 479001599; rc = handlemap_db_delete(&nfs23_digest); if (rc) exit(rc); } gettimeofday(&tv2, NULL); timersub(&tv2, &tv3, &tvdiff); LogTest("%u threads deleted 10000 handles in %d.%06ds", count, (int)tvdiff.tv_sec, (int)tvdiff.tv_usec); rc = handlemap_db_flush(); gettimeofday(&tv1, NULL); timersub(&tv1, &tv3, &tvdiff); LogTest("Delete time with %u threads (including flush): %d.%06ds", count, (int)tvdiff.tv_sec, (int)tvdiff.tv_usec); exit(0); } nfs-ganesha-2.6.0/src/FSAL/FSAL_PROXY/main.c000066400000000000000000000173151324272410200200470ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Max Matveev, 2012 * * Copyright CEA/DAM/DIF (2008) * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #include #include #include "fsal.h" #include "FSAL/fsal_init.h" #include "pxy_fsal_methods.h" #define PROXY_SUPPORTED_ATTRS ((const attrmask_t) (ATTRS_POSIX)) /* filesystem info for PROXY */ static struct fsal_staticfsinfo_t proxy_info = { .maxfilesize = UINT64_MAX, .maxlink = _POSIX_LINK_MAX, .maxnamelen = 1024, .maxpathlen = 1024, .no_trunc = true, .chown_restricted = true, .case_preserving = true, .lock_support = false, .named_attr = true, .unique_handles = true, .lease_time = {10, 0}, .acl_support = FSAL_ACLSUPPORT_ALLOW, .homogenous = true, .supported_attrs = PROXY_SUPPORTED_ATTRS, .link_supports_permission_checks = true, }; #ifdef _USE_GSSRPC static struct config_item_list sec_types[] = { CONFIG_LIST_TOK("krb5", RPCSEC_GSS_SVC_NONE), CONFIG_LIST_TOK("krb5i", RPCSEC_GSS_SVC_INTEGRITY), CONFIG_LIST_TOK("krb5p", RPCSEC_GSS_SVC_PRIVACY), CONFIG_LIST_EOL }; #endif /*512 bytes to store header*/ #define SEND_RECV_HEADER_SPACE 512 /*1MB of default maxsize*/ #define DEFAULT_MAX_WRITE_READ 1048576 static struct config_item proxy_remote_params[] = { CONF_ITEM_UI32("Retry_SleepTime", 0, 60, 10, pxy_client_params, retry_sleeptime), CONF_MAND_IP_ADDR("Srv_Addr", "127.0.0.1", pxy_client_params, srv_addr), CONF_ITEM_UI32("NFS_Service", 0, UINT32_MAX, 100003, pxy_client_params, srv_prognum), CONF_ITEM_UI32("NFS_SendSize", 512 + SEND_RECV_HEADER_SPACE, FSAL_MAXIOSIZE, DEFAULT_MAX_WRITE_READ + SEND_RECV_HEADER_SPACE, pxy_client_params, srv_sendsize), CONF_ITEM_UI32("NFS_RecvSize", 512 + SEND_RECV_HEADER_SPACE, FSAL_MAXIOSIZE, DEFAULT_MAX_WRITE_READ + SEND_RECV_HEADER_SPACE, pxy_client_params, srv_recvsize), CONF_ITEM_UI16("NFS_Port", 0, UINT16_MAX, 2049, pxy_client_params, srv_port), CONF_ITEM_BOOL("Use_Privileged_Client_Port", true, pxy_client_params, use_privileged_client_port), CONF_ITEM_UI32("RPC_Client_Timeout", 1, 60*4, 60, pxy_client_params, srv_timeout), #ifdef _USE_GSSRPC CONF_ITEM_STR("Remote_PrincipalName", 0, MAXNAMLEN, NULL, pxy_client_params, remote_principal), CONF_ITEM_STR("KeytabPath", 0, MAXPATHLEN, "/etc/krb5.keytab" pxy_client_params, keytab), CONF_ITEM_UI32("Credential_LifeTime", 0, 86400*2, 86400, pxy_client_params, cred_lifetime), CONF_ITEM_TOKEN("Sec_Type", RPCSEC_GSS_SVC_NONE, sec_types, pxy_client_params, sec_type), CONF_ITEM_BOOL("Active_krb5", false, pxy_client_params, active_krb5), #endif #ifdef PROXY_HANDLE_MAPPING CONF_ITEM_BOOL("Enable_Handle_Mapping", false, pxy_client_params, enable_handle_mapping), CONF_ITEM_STR("HandleMap_DB_Dir", 0, MAXPATHLEN, "/var/ganesha/handlemap", pxy_client_params, hdlmap.databases_directory), CONF_ITEM_STR("HandleMap_Tmp_Dir", 0, MAXPATHLEN, "/var/ganesha/tmp", pxy_client_params, hdlmap.temp_directory), CONF_ITEM_UI32("HandleMap_DB_Count", 1, 16, 8, pxy_client_params, hdlmap.database_count), CONF_ITEM_UI32("HandleMap_HashTable_Size", 1, 127, 103, pxy_client_params, hdlmap.hashtable_size), #endif CONFIG_EOL }; static int remote_commit(void *node, void *link_mem, void *self_struct, struct config_error_type *err_type) { /* struct pxy_client_params *pcpi = self_struct; */ struct pxy_fsal_module *pxy; pxy = container_of(link_mem, struct pxy_fsal_module, special); if (pxy->fsinfo.maxwrite + SEND_RECV_HEADER_SPACE > pxy->special.srv_sendsize || pxy->fsinfo.maxread + SEND_RECV_HEADER_SPACE > pxy->special.srv_recvsize) { LogCrit(COMPONENT_CONFIG, "FSAL_PROXY CONF : maxwrite/maxread + header > Max_SendSize/Max_RecvSize"); err_type->invalid = true; return 1; } /* Other verifications/parameter checking to be added here */ return 0; } /** * @brief Validate and commit the proxy params * * This is also pretty simple. Just a NOP in both cases. * * @param link_mem - pointer to the link_mem struct memory. * @param self_struct - NULL for init parent, not NULL for attaching */ static struct config_item proxy_params[] = { CONF_ITEM_BOOL("link_support", true, pxy_fsal_module, fsinfo.link_support), CONF_ITEM_BOOL("symlink_support", true, pxy_fsal_module, fsinfo.symlink_support), CONF_ITEM_BOOL("cansettime", true, pxy_fsal_module, fsinfo.cansettime), CONF_ITEM_UI64("maxread", 512, FSAL_MAXIOSIZE - SEND_RECV_HEADER_SPACE, DEFAULT_MAX_WRITE_READ, pxy_fsal_module, fsinfo.maxread), CONF_ITEM_UI64("maxwrite", 512, FSAL_MAXIOSIZE - SEND_RECV_HEADER_SPACE, DEFAULT_MAX_WRITE_READ, pxy_fsal_module, fsinfo.maxwrite), CONF_ITEM_MODE("umask", 0, pxy_fsal_module, fsinfo.umask), CONF_ITEM_BOOL("auth_xdev_export", false, pxy_fsal_module, fsinfo.auth_exportpath_xdev), CONF_ITEM_MODE("xattr_access_rights", 0400, pxy_fsal_module, fsinfo.xattr_access_rights), CONF_ITEM_BLOCK("Remote_Server", proxy_remote_params, noop_conf_init, remote_commit, pxy_fsal_module, special), CONFIG_EOL }; struct config_block proxy_param = { .dbus_interface_name = "org.ganesha.nfsd.config.fsal.proxy", .blk_desc.name = "PROXY", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = noop_conf_init, .blk_desc.u.blk.params = proxy_params, .blk_desc.u.blk.commit = noop_conf_commit }; static fsal_status_t pxy_init_config(struct fsal_module *fsal_hdl, config_file_t config_struct, struct config_error_type *err_type) { int rc; struct pxy_fsal_module *pxy = container_of(fsal_hdl, struct pxy_fsal_module, module); pxy->fsinfo = proxy_info; (void) load_config_from_parse(config_struct, &proxy_param, pxy, true, err_type); if (!config_error_is_harmless(err_type)) return fsalstat(ERR_FSAL_INVAL, 0); #ifdef PROXY_HANDLE_MAPPING rc = HandleMap_Init(&pxy->special.hdlmap); if (rc < 0) return fsalstat(ERR_FSAL_INVAL, -rc); #endif rc = pxy_init_rpc(pxy); if (rc) return fsalstat(ERR_FSAL_FAULT, rc); return fsalstat(ERR_FSAL_NO_ERROR, 0); } static struct pxy_fsal_module PROXY; MODULE_INIT void pxy_init(void) { if (register_fsal(&PROXY.module, "PROXY", FSAL_MAJOR_VERSION, FSAL_MINOR_VERSION, FSAL_ID_NO_PNFS) != 0) return; PROXY.module.m_ops.init_config = pxy_init_config; PROXY.module.m_ops.create_export = pxy_create_export; } MODULE_FINI void pxy_unload(void) { int retval; retval = unregister_fsal(&PROXY.module); if (retval != 0) { fprintf(stderr, "PROXY module failed to unregister : %d", retval); return; } retval = pxy_close_thread(); if (retval != 0) { fprintf(stderr, "PROXY module failed to wait threads : %d", retval); return; } } nfs-ganesha-2.6.0/src/FSAL/FSAL_PROXY/pxy_fsal_methods.h000066400000000000000000000070711324272410200224760ustar00rootroot00000000000000#ifndef _PXY_FSAL_METHODS_H #define _PXY_FSAL_METHODS_H #ifdef PROXY_HANDLE_MAPPING #include "handle_mapping/handle_mapping.h" #endif struct pxy_client_params { unsigned int retry_sleeptime; sockaddr_t srv_addr; unsigned int srv_prognum; unsigned int srv_sendsize; unsigned int srv_recvsize; unsigned int srv_timeout; uint16_t srv_port; unsigned int use_privileged_client_port; char *remote_principal; char *keytab; unsigned int cred_lifetime; unsigned int sec_type; bool active_krb5; /* initialization info for handle mapping */ int enable_handle_mapping; #ifdef PROXY_HANDLE_MAPPING handle_map_param_t hdlmap; #endif } proxyfs_specific_initinfo_t; struct pxy_fsal_module { struct fsal_module module; struct fsal_staticfsinfo_t fsinfo; struct pxy_client_params special; }; struct pxy_export { struct fsal_export exp; struct pxy_client_params *info; }; void pxy_handle_ops_init(struct fsal_obj_ops *ops); int pxy_init_rpc(const struct pxy_fsal_module *); fsal_status_t pxy_list_ext_attrs(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, unsigned int cookie, fsal_xattrent_t *xattrs_tab, unsigned int xattrs_tabsize, unsigned int *p_nb_returned, int *end_of_list); fsal_status_t pxy_getextattr_id_by_name(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, const char *xattr_name, unsigned int *pxattr_id); fsal_status_t pxy_getextattr_value_by_name(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, const char *xattr_name, caddr_t buffer_addr, size_t buffer_size, size_t *len); fsal_status_t pxy_getextattr_value_by_id(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, unsigned int xattr_id, caddr_t buf, size_t sz, size_t *len); fsal_status_t pxy_setextattr_value(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, const char *xattr_name, caddr_t buf, size_t sz, int create); fsal_status_t pxy_setextattr_value_by_id(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, unsigned int xattr_id, caddr_t buf, size_t sz); fsal_status_t pxy_getextattr_attrs(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, unsigned int xattr_id, struct attrlist *attrs); fsal_status_t pxy_remove_extattr_by_id(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, unsigned int xattr_id); fsal_status_t pxy_remove_extattr_by_name(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, const char *xattr_name); fsal_status_t pxy_lookup_path(struct fsal_export *exp_hdl, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out); fsal_status_t pxy_create_handle(struct fsal_export *exp_hdl, struct gsh_buffdesc *hdl_desc, struct fsal_obj_handle **handle, struct attrlist *attrs_out); fsal_status_t pxy_create_export(struct fsal_module *fsal_hdl, void *parse_node, struct config_error_type *err_type, const struct fsal_up_vector *up_ops); fsal_status_t pxy_get_dynamic_info(struct fsal_export *, struct fsal_obj_handle *, fsal_dynamicfsinfo_t *); fsal_status_t pxy_wire_to_host(struct fsal_export *, fsal_digesttype_t, struct gsh_buffdesc *, int); struct state_t *pxy_alloc_state(struct fsal_export *exp_hdl, enum state_type state_type, struct state_t *related_state); void pxy_free_state(struct fsal_export *exp_hdl, struct state_t *state); int pxy_close_thread(void); #endif nfs-ganesha-2.6.0/src/FSAL/FSAL_PROXY/xattrs.c000066400000000000000000000060101324272410200204360ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Max Matveev, 2012 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ #include "config.h" #include "fsal.h" #include "pxy_fsal_methods.h" fsal_status_t pxy_list_ext_attrs(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, unsigned int cookie, fsal_xattrent_t *xattrs_tab, unsigned int xattrs_tabsize, unsigned int *p_nb_returned, int *end_of_list) { return fsalstat(ERR_FSAL_NOTSUPP, 0); } fsal_status_t pxy_getextattr_id_by_name(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, const char *xattr_name, unsigned int *pxattr_id) { return fsalstat(ERR_FSAL_NOTSUPP, 0); } fsal_status_t pxy_getextattr_value_by_name(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, const char *xattr_name, caddr_t buffer_addr, size_t buffer_size, size_t *p_output_size) { return fsalstat(ERR_FSAL_NOTSUPP, 0); } fsal_status_t pxy_getextattr_value_by_id(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, unsigned int xattr_id, caddr_t buffer_addr, size_t buffer_size, size_t *p_output_size) { return fsalstat(ERR_FSAL_NOTSUPP, 0); } fsal_status_t pxy_setextattr_value(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, const char *xattr_name, caddr_t buffer_addr, size_t buffer_size, int create) { return fsalstat(ERR_FSAL_NOTSUPP, 0); } fsal_status_t pxy_setextattr_value_by_id(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, unsigned int xattr_id, caddr_t buffer_addr, size_t buffer_size) { return fsalstat(ERR_FSAL_NOTSUPP, 0); } fsal_status_t pxy_getextattr_attrs(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, unsigned int xattr_id, struct attrlist *p_attrs) { return fsalstat(ERR_FSAL_NOTSUPP, 0); } fsal_status_t pxy_remove_extattr_by_id(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, unsigned int xattr_id) { return fsalstat(ERR_FSAL_NOTSUPP, 0); } fsal_status_t pxy_remove_extattr_by_name(struct fsal_obj_handle *obj_hdl, const struct req_op_context *opctx, const char *xattr_name) { return fsalstat(ERR_FSAL_NOTSUPP, 0); } nfs-ganesha-2.6.0/src/FSAL/FSAL_PSEUDO/000077500000000000000000000000001324272410200170265ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/FSAL/FSAL_PSEUDO/CMakeLists.txt000066400000000000000000000004771324272410200215760ustar00rootroot00000000000000add_definitions( -D__USE_GNU -D_GNU_SOURCE ) set( LIB_PREFIX 64) ########### next target ############### SET(fsalpseudo_LIB_SRCS handle.c pseudofs_methods.h main.c export.c ) add_library(fsalpseudo STATIC ${fsalpseudo_LIB_SRCS}) add_sanitizers(fsalpseudo) ########### install files ############### nfs-ganesha-2.6.0/src/FSAL/FSAL_PSEUDO/export.c000066400000000000000000000220051324272410200205120ustar00rootroot00000000000000/* * vim:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* export.c * PSEUDO FSAL export object */ #include "config.h" #include "fsal.h" #include /* used for 'dirname' */ #include #include #include #include #include #include #include "fsal_convert.h" #include "FSAL/fsal_commonlib.h" #include "FSAL/fsal_config.h" #include "pseudofs_methods.h" #include "nfs_exports.h" #include "export_mgr.h" #include "mdcache.h" #ifdef __FreeBSD__ #include #define bswap_16(x) bswap16((x)) #define bswap_64(x) bswap64((x)) #endif /* helpers to/from other PSEUDO objects */ struct fsal_staticfsinfo_t *pseudofs_staticinfo(struct fsal_module *hdl); /* export object methods */ static void release(struct fsal_export *exp_hdl) { struct pseudofs_fsal_export *myself; myself = container_of(exp_hdl, struct pseudofs_fsal_export, export); if (myself->root_handle != NULL) { fsal_obj_handle_fini(&myself->root_handle->obj_handle); LogDebug(COMPONENT_FSAL, "Releasing hdl=%p, name=%s", myself->root_handle, myself->root_handle->name); if (myself->root_handle->name != NULL) gsh_free(myself->root_handle->name); gsh_free(myself->root_handle); myself->root_handle = NULL; } fsal_detach_export(exp_hdl->fsal, &exp_hdl->exports); free_export_ops(exp_hdl); if (myself->export_path != NULL) gsh_free(myself->export_path); gsh_free(myself); } static fsal_status_t get_dynamic_info(struct fsal_export *exp_hdl, struct fsal_obj_handle *obj_hdl, fsal_dynamicfsinfo_t *infop) { infop->total_bytes = 0; infop->free_bytes = 0; infop->avail_bytes = 0; infop->total_files = 0; infop->free_files = 0; infop->avail_files = 0; infop->time_delta.tv_sec = 1; infop->time_delta.tv_nsec = 0; return fsalstat(ERR_FSAL_NO_ERROR, 0); } static bool fs_supports(struct fsal_export *exp_hdl, fsal_fsinfo_options_t option) { struct fsal_staticfsinfo_t *info; info = pseudofs_staticinfo(exp_hdl->fsal); return fsal_supports(info, option); } static uint64_t fs_maxfilesize(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = pseudofs_staticinfo(exp_hdl->fsal); return fsal_maxfilesize(info); } static uint32_t fs_maxread(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = pseudofs_staticinfo(exp_hdl->fsal); return fsal_maxread(info); } static uint32_t fs_maxwrite(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = pseudofs_staticinfo(exp_hdl->fsal); return fsal_maxwrite(info); } static uint32_t fs_maxlink(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = pseudofs_staticinfo(exp_hdl->fsal); return fsal_maxlink(info); } static uint32_t fs_maxnamelen(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = pseudofs_staticinfo(exp_hdl->fsal); return fsal_maxnamelen(info); } static uint32_t fs_maxpathlen(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = pseudofs_staticinfo(exp_hdl->fsal); return fsal_maxpathlen(info); } static struct timespec fs_lease_time(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = pseudofs_staticinfo(exp_hdl->fsal); return fsal_lease_time(info); } static fsal_aclsupp_t fs_acl_support(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = pseudofs_staticinfo(exp_hdl->fsal); return fsal_acl_support(info); } static attrmask_t fs_supported_attrs(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = pseudofs_staticinfo(exp_hdl->fsal); return fsal_supported_attrs(info); } static uint32_t fs_umask(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = pseudofs_staticinfo(exp_hdl->fsal); return fsal_umask(info); } static uint32_t fs_xattr_access_rights(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = pseudofs_staticinfo(exp_hdl->fsal); return fsal_xattr_access_rights(info); } /* get_quota * return quotas for this export. * path could cross a lower mount boundary which could * mask lower mount values with those of the export root * if this is a real issue, we can scan each time with setmntent() * better yet, compare st_dev of the file with st_dev of root_fd. * on linux, can map st_dev -> /proc/partitions name -> /dev/ */ static fsal_status_t get_quota(struct fsal_export *exp_hdl, const char *filepath, int quota_type, int quota_id, fsal_quota_t *pquota) { /* PSEUDOFS doesn't support quotas */ return fsalstat(ERR_FSAL_NOTSUPP, 0); } /* set_quota * same lower mount restriction applies */ static fsal_status_t set_quota(struct fsal_export *exp_hdl, const char *filepath, int quota_type, int quota_id, fsal_quota_t *pquota, fsal_quota_t *presquota) { /* PSEUDOFS doesn't support quotas */ return fsalstat(ERR_FSAL_NOTSUPP, 0); } /* extract a file handle from a buffer. * do verification checks and flag any and all suspicious bits. * Return an updated fh_desc into whatever was passed. The most * common behavior, done here is to just reset the length. */ static fsal_status_t wire_to_host(struct fsal_export *exp_hdl, fsal_digesttype_t in_type, struct gsh_buffdesc *fh_desc, int flags) { size_t fh_min; uint64_t *hashkey; ushort *len; fh_min = 1; if (fh_desc->len < fh_min) { LogMajor(COMPONENT_FSAL, "Size mismatch for handle. should be >= %zu, got %zu", fh_min, fh_desc->len); return fsalstat(ERR_FSAL_SERVERFAULT, 0); } hashkey = (uint64_t *)fh_desc->addr; len = (ushort *)((char *)hashkey + sizeof(uint64_t)); if (flags & FH_FSAL_BIG_ENDIAN) { #if (BYTE_ORDER != BIG_ENDIAN) *len = bswap_16(*len); *hashkey = bswap_64(*hashkey); #endif } else { #if (BYTE_ORDER == BIG_ENDIAN) *len = bswap_16(*len); *hashkey = bswap_64(*hashkey); #endif } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* pseudofs_export_ops_init * overwrite vector entries with the methods that we support */ void pseudofs_export_ops_init(struct export_ops *ops) { ops->release = release; ops->lookup_path = pseudofs_lookup_path; ops->wire_to_host = wire_to_host; ops->create_handle = pseudofs_create_handle; ops->get_fs_dynamic_info = get_dynamic_info; ops->fs_supports = fs_supports; ops->fs_maxfilesize = fs_maxfilesize; ops->fs_maxread = fs_maxread; ops->fs_maxwrite = fs_maxwrite; ops->fs_maxlink = fs_maxlink; ops->fs_maxnamelen = fs_maxnamelen; ops->fs_maxpathlen = fs_maxpathlen; ops->fs_lease_time = fs_lease_time; ops->fs_acl_support = fs_acl_support; ops->fs_supported_attrs = fs_supported_attrs; ops->fs_umask = fs_umask; ops->fs_xattr_access_rights = fs_xattr_access_rights; ops->get_quota = get_quota; ops->set_quota = set_quota; } /* create_export * Create an export point and return a handle to it to be kept * in the export list. * First lookup the fsal, then create the export and then put the fsal back. * returns the export with one reference taken. */ fsal_status_t pseudofs_create_export(struct fsal_module *fsal_hdl, void *parse_node, struct config_error_type *err_type, const struct fsal_up_vector *up_ops) { struct pseudofs_fsal_export *myself; int retval = 0; myself = gsh_calloc(1, sizeof(struct pseudofs_fsal_export)); if (myself == NULL) { LogMajor(COMPONENT_FSAL, "Could not allocate export"); return fsalstat(posix2fsal_error(errno), errno); } fsal_export_init(&myself->export); pseudofs_export_ops_init(&myself->export.exp_ops); retval = fsal_attach_export(fsal_hdl, &myself->export.exports); if (retval != 0) { /* seriously bad */ LogMajor(COMPONENT_FSAL, "Could not attach export"); gsh_free(myself->export_path); gsh_free(myself->root_handle); free_export_ops(&myself->export); gsh_free(myself); /* elvis has left the building */ return fsalstat(posix2fsal_error(retval), retval); } myself->export.fsal = fsal_hdl; /* Save the export path. */ myself->export_path = gsh_strdup(op_ctx->ctx_export->fullpath); op_ctx->fsal_export = &myself->export; LogDebug(COMPONENT_FSAL, "Created exp %p - %s", myself, myself->export_path); return fsalstat(ERR_FSAL_NO_ERROR, 0); } nfs-ganesha-2.6.0/src/FSAL/FSAL_PSEUDO/handle.c000066400000000000000000000512211324272410200204260ustar00rootroot00000000000000/* * vim:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* handle.c */ #include "config.h" #include "fsal.h" #include "fsal_convert.h" #include "FSAL/fsal_commonlib.h" #include "pseudofs_methods.h" #include "city.h" #include "nfs_file_handle.h" #include "display.h" /* Atomic uint64_t that is used to generate inode numbers in the Pseudo FS */ uint64_t inode_number; #define V4_FH_OPAQUE_SIZE (NFS4_FHSIZE - sizeof(struct file_handle_v4)) /* helpers */ static inline int pseudofs_n_cmpf(const struct avltree_node *lhs, const struct avltree_node *rhs) { struct pseudo_fsal_obj_handle *lk, *rk; lk = avltree_container_of(lhs, struct pseudo_fsal_obj_handle, avl_n); rk = avltree_container_of(rhs, struct pseudo_fsal_obj_handle, avl_n); return strcmp(lk->name, rk->name); } static inline int pseudofs_i_cmpf(const struct avltree_node *lhs, const struct avltree_node *rhs) { struct pseudo_fsal_obj_handle *lk, *rk; lk = avltree_container_of(lhs, struct pseudo_fsal_obj_handle, avl_i); rk = avltree_container_of(rhs, struct pseudo_fsal_obj_handle, avl_i); if (lk->index < rk->index) return -1; if (lk->index == rk->index) return 0; return 1; } static inline struct avltree_node * avltree_inline_name_lookup(const struct avltree_node *key, const struct avltree *tree) { return avltree_inline_lookup(key, tree, pseudofs_n_cmpf); } /** * @brief Construct the fs opaque part of a pseudofs nfsv4 handle * * Given the components of a pseudofs nfsv4 handle, the nfsv4 handle is * created by concatenating the components. This is the fs opaque piece * of struct file_handle_v4 and what is sent over the wire. * * @param[in] pathbuf Full patch of the pseudofs node * @param[in] hashkey a 64 bit hash of the pseudopath parameter * * @return The nfsv4 pseudofs file handle as a char * */ static void package_pseudo_handle(char *buff, struct display_buffer *pathbuf) { ushort len = display_buffer_len(pathbuf); int opaque_bytes_used = 0, pathlen = 0; uint64_t hashkey = CityHash64(pathbuf->b_start, display_buffer_len(pathbuf)); memcpy(buff, &hashkey, sizeof(hashkey)); opaque_bytes_used += sizeof(hashkey); /* include length of the path in the handle. * MAXPATHLEN=4096 ... max path length can be contained in a short int. */ memcpy(buff + opaque_bytes_used, &len, sizeof(len)); opaque_bytes_used += sizeof(len); /* Either the nfsv4 fh opaque size or the length of the pseudopath. * Ideally we can include entire pseudofs pathname for guaranteed * uniqueness of pseudofs handles. */ pathlen = MIN(V4_FH_OPAQUE_SIZE - opaque_bytes_used, len); memcpy(buff + opaque_bytes_used, pathbuf->b_start, pathlen); opaque_bytes_used += pathlen; /* If there is more space in the opaque handle due to a short pseudofs * path ... zero it. */ if (opaque_bytes_used < V4_FH_OPAQUE_SIZE) { memset(buff + opaque_bytes_used, 0, V4_FH_OPAQUE_SIZE - opaque_bytes_used); } } /** * @brief Concatenate a number of pseudofs tokens into a string * * When reading pseudofs paths from export entries, we divide the * path into tokens. This function will recombine a specific number * of those tokens into a string. * * @param[in/out] pathbuf Must be not NULL. Tokens are copied to here. * @param[in] node for which a full pseudopath needs to be formed. * @param[in] maxlen maximum number of chars to copy to pathbuf * * @return void */ static int fullpath(struct display_buffer *pathbuf, struct pseudo_fsal_obj_handle *this_node) { int b_left; if (this_node->parent != NULL) b_left = fullpath(pathbuf, this_node->parent); else b_left = display_start(pathbuf); /* Add slash for all but root node */ if (b_left > 0 && this_node->parent != NULL) b_left = display_cat(pathbuf, "/"); /* Append the node's name. * Note that a Pseudo FS root's name is it's full path. */ if (b_left > 0) b_left = display_cat(pathbuf, this_node->name); return b_left; } /* alloc_handle * allocate and fill in a handle */ static struct pseudo_fsal_obj_handle *alloc_directory_handle(struct pseudo_fsal_obj_handle *parent, const char *name, struct fsal_export *exp_hdl, struct attrlist *attrs) { struct pseudo_fsal_obj_handle *hdl; char path[MAXPATHLEN] = "\0"; struct display_buffer pathbuf = {sizeof(path), path, path}; int rc; hdl = gsh_calloc(1, sizeof(struct pseudo_fsal_obj_handle) + V4_FH_OPAQUE_SIZE); /* Establish tree details for this directory */ hdl->name = gsh_strdup(name); hdl->parent = parent; if (hdl->name == NULL) { LogDebug(COMPONENT_FSAL, "Could not name"); goto spcerr; } /* Create the handle */ hdl->handle = (char *) &hdl[1]; /* Create the full path */ rc = fullpath(&pathbuf, hdl); if (rc < 0) { LogDebug(COMPONENT_FSAL, "Could not create handle"); goto spcerr; } package_pseudo_handle(hdl->handle, &pathbuf); hdl->obj_handle.type = DIRECTORY; /* Fills the output struct */ hdl->attributes.type = DIRECTORY; hdl->attributes.filesize = 0; /* fsid will be supplied later */ hdl->obj_handle.fsid.major = 0; hdl->obj_handle.fsid.minor = 0; hdl->attributes.fsid.major = 0; hdl->attributes.fsid.minor = 0; hdl->obj_handle.fileid = atomic_postinc_uint64_t(&inode_number); hdl->attributes.fileid = hdl->obj_handle.fileid; hdl->attributes.mode = attrs->mode & (~S_IFMT & 0xFFFF) & ~op_ctx->fsal_export->exp_ops.fs_umask(op_ctx->fsal_export); hdl->attributes.numlinks = 2; hdl->numlinks = 2; if ((attrs->valid_mask & ATTR_OWNER) != 0) hdl->attributes.owner = attrs->owner; else hdl->attributes.owner = op_ctx->creds->caller_uid; if ((attrs->valid_mask & ATTR_GROUP) != 0) hdl->attributes.group = attrs->group; else hdl->attributes.group = op_ctx->creds->caller_gid; /* Use full timer resolution */ now(&hdl->attributes.ctime); hdl->attributes.chgtime = hdl->attributes.ctime; if ((attrs->valid_mask & ATTR_ATIME) != 0) hdl->attributes.atime = attrs->atime; else hdl->attributes.atime = hdl->attributes.ctime; if ((attrs->valid_mask & ATTR_MTIME) != 0) hdl->attributes.mtime = attrs->mtime; else hdl->attributes.mtime = hdl->attributes.ctime; hdl->attributes.change = timespec_to_nsecs(&hdl->attributes.chgtime); hdl->attributes.spaceused = 0; hdl->attributes.rawdev.major = 0; hdl->attributes.rawdev.minor = 0; /* Set the mask at the end. */ hdl->attributes.valid_mask = PSEUDO_SUPPORTED_ATTRS; hdl->attributes.supported = PSEUDO_SUPPORTED_ATTRS; fsal_obj_handle_init(&hdl->obj_handle, exp_hdl, DIRECTORY); pseudofs_handle_ops_init(&hdl->obj_handle.obj_ops); avltree_init(&hdl->avl_name, pseudofs_n_cmpf, 0 /* flags */); avltree_init(&hdl->avl_index, pseudofs_i_cmpf, 0 /* flags */); hdl->next_i = 2; if (parent != NULL) { /* Attach myself to my parent */ PTHREAD_RWLOCK_wrlock(&parent->obj_handle.obj_lock); avltree_insert(&hdl->avl_n, &parent->avl_name); hdl->index = (parent->next_i)++; avltree_insert(&hdl->avl_i, &parent->avl_index); hdl->inavl = true; PTHREAD_RWLOCK_unlock(&parent->obj_handle.obj_lock); } return hdl; spcerr: if (hdl->name != NULL) gsh_free(hdl->name); gsh_free(hdl); /* elvis has left the building */ return NULL; } /* handle methods */ /* lookup * deprecated NULL parent && NULL path implies root handle */ static fsal_status_t lookup(struct fsal_obj_handle *parent, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { struct pseudo_fsal_obj_handle *myself, *hdl = NULL; struct pseudo_fsal_obj_handle key[1]; struct avltree_node *node; fsal_errors_t error = ERR_FSAL_NOENT; myself = container_of(parent, struct pseudo_fsal_obj_handle, obj_handle); /* Check if this context already holds the lock on * this directory. */ if (op_ctx->fsal_private != parent) PTHREAD_RWLOCK_rdlock(&parent->obj_lock); else LogFullDebug(COMPONENT_FSAL, "Skipping lock for %s", myself->name); if (strcmp(path, "..") == 0) { /* lookup parent - lookupp */ if (myself->parent != NULL) { hdl = myself->parent; *handle = &hdl->obj_handle; error = ERR_FSAL_NO_ERROR; LogFullDebug(COMPONENT_FSAL, "Found %s/%s hdl=%p", myself->name, path, hdl); } goto out; } key->name = (char *) path; node = avltree_inline_name_lookup(&key->avl_n, &myself->avl_name); if (node) { hdl = avltree_container_of(node, struct pseudo_fsal_obj_handle, avl_n); *handle = &hdl->obj_handle; error = ERR_FSAL_NO_ERROR; LogFullDebug(COMPONENT_FSAL, "Found %s/%s hdl=%p", myself->name, path, hdl); } out: if (op_ctx->fsal_private != parent) PTHREAD_RWLOCK_unlock(&parent->obj_lock); if (error == ERR_FSAL_NO_ERROR && attrs_out != NULL) { /* This is unlocked, however, for the most part, attributes * are read-only. Come back later and do some lock protection. */ fsal_copy_attrs(attrs_out, &hdl->attributes, false); } return fsalstat(error, 0); } /** * @brief Create a directory * * This function creates a new directory. * * While FSAL_PSEUDO is a support_ex FSAL, it doesn't actually support * setting attributes, so only the mode attribute is relevant. Any other * attributes set on creation will be ignored. The owner and group will be * set from the active credentials. * * @param[in] dir_hdl Directory in which to create the directory * @param[in] name Name of directory to create * @param[in] attrs_in Attributes to set on newly created object * @param[out] handle Newly created object * @param[in,out] attrs_out Optional attributes for newly created object * * @note On success, @a new_obj has been ref'd * * @return FSAL status. */ static fsal_status_t makedir(struct fsal_obj_handle *dir_hdl, const char *name, struct attrlist *attrs_in, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { struct pseudo_fsal_obj_handle *myself, *hdl; uint32_t numlinks; LogDebug(COMPONENT_FSAL, "create %s", name); *handle = NULL; /* poison it */ if (!dir_hdl->obj_ops.handle_is(dir_hdl, DIRECTORY)) { LogCrit(COMPONENT_FSAL, "Parent handle is not a directory. hdl = 0x%p", dir_hdl); return fsalstat(ERR_FSAL_NOTDIR, 0); } myself = container_of(dir_hdl, struct pseudo_fsal_obj_handle, obj_handle); /* allocate an obj_handle and fill it up */ hdl = alloc_directory_handle(myself, name, op_ctx->fsal_export, attrs_in); numlinks = atomic_inc_uint32_t(&myself->numlinks); LogFullDebug(COMPONENT_FSAL, "%s numlinks %"PRIu32, myself->name, numlinks); *handle = &hdl->obj_handle; if (attrs_out != NULL) fsal_copy_attrs(attrs_out, &hdl->attributes, false); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * read_dirents * read the directory and call through the callback function for * each entry. * @param dir_hdl [IN] the directory to read * @param whence [IN] where to start (next) * @param dir_state [IN] pass thru of state to callback * @param cb [IN] callback function * @param eof [OUT] eof marker true == end of dir */ static fsal_status_t read_dirents(struct fsal_obj_handle *dir_hdl, fsal_cookie_t *whence, void *dir_state, fsal_readdir_cb cb, attrmask_t attrmask, bool *eof) { struct pseudo_fsal_obj_handle *myself, *hdl; struct avltree_node *node; fsal_cookie_t seekloc; struct attrlist attrs; enum fsal_dir_result cb_rc; if (whence != NULL) seekloc = *whence; else seekloc = 2; /* start from index 2, if no cookie */ *eof = true; myself = container_of(dir_hdl, struct pseudo_fsal_obj_handle, obj_handle); LogDebug(COMPONENT_FSAL, "hdl=%p, name=%s", myself, myself->name); PTHREAD_RWLOCK_rdlock(&dir_hdl->obj_lock); /* Use fsal_private to signal to lookup that we hold * the lock. */ op_ctx->fsal_private = dir_hdl; for (node = avltree_first(&myself->avl_index); node != NULL; node = avltree_next(node)) { hdl = avltree_container_of(node, struct pseudo_fsal_obj_handle, avl_i); /* skip entries before seekloc */ if (hdl->index < seekloc) continue; fsal_prepare_attrs(&attrs, attrmask); fsal_copy_attrs(&attrs, &hdl->attributes, false); cb_rc = cb(hdl->name, &hdl->obj_handle, &attrs, dir_state, hdl->index + 1); fsal_release_attrs(&attrs); /* Read ahead not supported by this FSAL. */ if (cb_rc >= DIR_READAHEAD) { *eof = false; break; } } op_ctx->fsal_private = NULL; PTHREAD_RWLOCK_unlock(&dir_hdl->obj_lock); return fsalstat(ERR_FSAL_NO_ERROR, 0); } static fsal_status_t getattrs(struct fsal_obj_handle *obj_hdl, struct attrlist *outattrs) { struct pseudo_fsal_obj_handle *myself; myself = container_of(obj_hdl, struct pseudo_fsal_obj_handle, obj_handle); if (myself->parent != NULL && !myself->inavl) { /* Removed entry - stale */ LogDebug(COMPONENT_FSAL, "Requesting attributes for removed entry %p, name=%s", myself, myself->name); return fsalstat(ERR_FSAL_STALE, ESTALE); } /* We need to update the numlinks under attr lock. */ myself->attributes.numlinks = atomic_fetch_uint32_t(&myself->numlinks); *outattrs = myself->attributes; LogFullDebug(COMPONENT_FSAL, "hdl=%p, name=%s numlinks %"PRIu32, myself, myself->name, myself->attributes.numlinks); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* file_unlink * unlink the named file in the directory */ static fsal_status_t file_unlink(struct fsal_obj_handle *dir_hdl, struct fsal_obj_handle *obj_hdl, const char *name) { struct pseudo_fsal_obj_handle *myself, *hdl; fsal_errors_t error = ERR_FSAL_NOENT; uint32_t numlinks; myself = container_of(dir_hdl, struct pseudo_fsal_obj_handle, obj_handle); hdl = container_of(obj_hdl, struct pseudo_fsal_obj_handle, obj_handle); PTHREAD_RWLOCK_wrlock(&dir_hdl->obj_lock); /* Check if directory is empty */ numlinks = atomic_fetch_uint32_t(&hdl->numlinks); if (numlinks != 2) { LogFullDebug(COMPONENT_FSAL, "%s numlinks %"PRIu32, hdl->name, numlinks); error = ERR_FSAL_NOTEMPTY; goto unlock; } /* We need to update the numlinks. */ numlinks = atomic_dec_uint32_t(&myself->numlinks); LogFullDebug(COMPONENT_FSAL, "%s numlinks %"PRIu32, myself->name, numlinks); /* Remove from directory's name and index avls */ avltree_remove(&hdl->avl_n, &myself->avl_name); avltree_remove(&hdl->avl_i, &myself->avl_index); hdl->inavl = false; error = ERR_FSAL_NO_ERROR; unlock: PTHREAD_RWLOCK_unlock(&dir_hdl->obj_lock); return fsalstat(error, 0); } /* handle_to_wire * fill in the opaque f/s file handle part. * we zero the buffer to length first. This MAY already be done above * at which point, remove memset here because the caller is zeroing * the whole struct. */ static fsal_status_t handle_to_wire(const struct fsal_obj_handle *obj_hdl, fsal_digesttype_t output_type, struct gsh_buffdesc *fh_desc) { const struct pseudo_fsal_obj_handle *myself; myself = container_of(obj_hdl, const struct pseudo_fsal_obj_handle, obj_handle); switch (output_type) { case FSAL_DIGEST_NFSV3: case FSAL_DIGEST_NFSV4: if (fh_desc->len < V4_FH_OPAQUE_SIZE) { LogMajor(COMPONENT_FSAL, "Space too small for handle. need %lu, have %zu", ((unsigned long) V4_FH_OPAQUE_SIZE), fh_desc->len); return fsalstat(ERR_FSAL_TOOSMALL, 0); } memcpy(fh_desc->addr, myself->handle, V4_FH_OPAQUE_SIZE); fh_desc->len = V4_FH_OPAQUE_SIZE; break; default: return fsalstat(ERR_FSAL_SERVERFAULT, 0); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * handle_to_key * return a handle descriptor into the handle in this object handle * @TODO reminder. make sure things like hash keys don't point here * after the handle is released. */ static void handle_to_key(struct fsal_obj_handle *obj_hdl, struct gsh_buffdesc *fh_desc) { struct pseudo_fsal_obj_handle *myself; myself = container_of(obj_hdl, struct pseudo_fsal_obj_handle, obj_handle); fh_desc->addr = myself->handle; fh_desc->len = V4_FH_OPAQUE_SIZE; } /* * release * release our export first so they know we are gone */ static void release(struct fsal_obj_handle *obj_hdl) { struct pseudo_fsal_obj_handle *myself; myself = container_of(obj_hdl, struct pseudo_fsal_obj_handle, obj_handle); if (myself->parent == NULL || myself->inavl) { /* Entry is still live */ LogDebug(COMPONENT_FSAL, "Releasing live hdl=%p, name=%s, don't deconstruct it", myself, myself->name); return; } fsal_obj_handle_fini(obj_hdl); LogDebug(COMPONENT_FSAL, "Releasing obj_hdl=%p, myself=%p, name=%s", obj_hdl, myself, myself->name); if (myself->name != NULL) gsh_free(myself->name); gsh_free(myself); } void pseudofs_handle_ops_init(struct fsal_obj_ops *ops) { ops->release = release; ops->lookup = lookup; ops->readdir = read_dirents; ops->mkdir = makedir; ops->getattrs = getattrs; ops->unlink = file_unlink; ops->handle_to_wire = handle_to_wire; ops->handle_to_key = handle_to_key; } /* export methods that create object handles */ /* lookup_path * modeled on old api except we don't stuff attributes. * KISS */ fsal_status_t pseudofs_lookup_path(struct fsal_export *exp_hdl, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { struct pseudofs_fsal_export *myself; struct attrlist attrs; myself = container_of(exp_hdl, struct pseudofs_fsal_export, export); if (strcmp(path, myself->export_path) != 0) { /* Lookup of a path other than the export's root. */ LogCrit(COMPONENT_FSAL, "Attempt to lookup non-root path %s", path); return fsalstat(ERR_FSAL_NOENT, ENOENT); } attrs.valid_mask = ATTR_MODE; attrs.mode = 0755; if (myself->root_handle == NULL) { myself->root_handle = alloc_directory_handle(NULL, myself->export_path, exp_hdl, &attrs); } *handle = &myself->root_handle->obj_handle; if (attrs_out != NULL) fsal_copy_attrs(attrs_out, &myself->root_handle->attributes, false); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* create_handle * Does what original FSAL_ExpandHandle did (sort of) * returns a ref counted handle to be later used in cache_inode etc. * NOTE! you must release this thing when done with it! * BEWARE! Thanks to some holes in the *AT syscalls implementation, * we cannot get an fd on an AF_UNIX socket, nor reliably on block or * character special devices. Sorry, it just doesn't... * we could if we had the handle of the dir it is in, but this method * is for getting handles off the wire for cache entries that have LRU'd. * Ideas and/or clever hacks are welcome... */ fsal_status_t pseudofs_create_handle(struct fsal_export *exp_hdl, struct gsh_buffdesc *hdl_desc, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { struct glist_head *glist; struct fsal_obj_handle *hdl; struct pseudo_fsal_obj_handle *my_hdl; *handle = NULL; if (hdl_desc->len != V4_FH_OPAQUE_SIZE) { LogCrit(COMPONENT_FSAL, "Invalid handle size %zu expected %lu", hdl_desc->len, ((unsigned long) V4_FH_OPAQUE_SIZE)); return fsalstat(ERR_FSAL_BADHANDLE, 0); } PTHREAD_RWLOCK_rdlock(&exp_hdl->fsal->lock); glist_for_each(glist, &exp_hdl->fsal->handles) { hdl = glist_entry(glist, struct fsal_obj_handle, handles); my_hdl = container_of(hdl, struct pseudo_fsal_obj_handle, obj_handle); if (memcmp(my_hdl->handle, hdl_desc->addr, V4_FH_OPAQUE_SIZE) == 0) { LogDebug(COMPONENT_FSAL, "Found hdl=%p name=%s", my_hdl, my_hdl->name); *handle = hdl; PTHREAD_RWLOCK_unlock(&exp_hdl->fsal->lock); if (attrs_out != NULL) { fsal_copy_attrs(attrs_out, &my_hdl->attributes, false); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } } LogDebug(COMPONENT_FSAL, "Could not find handle"); PTHREAD_RWLOCK_unlock(&exp_hdl->fsal->lock); return fsalstat(ERR_FSAL_STALE, ESTALE); } nfs-ganesha-2.6.0/src/FSAL/FSAL_PSEUDO/main.c000066400000000000000000000100371324272410200201170ustar00rootroot00000000000000/* * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ /* main.c * Module core functions */ #include "config.h" #include "fsal.h" #include /* used for 'dirname' */ #include #include #include #include #include "FSAL/fsal_init.h" #include "pseudofs_methods.h" #include "../fsal_private.h" /* PSEUDOFS FSAL module private storage */ struct pseudo_fsal_module { struct fsal_module fsal; struct fsal_staticfsinfo_t fs_info; /* pseudofsfs_specific_initinfo_t specific_info; placeholder */ }; const char pseudoname[] = "PSEUDO"; /* filesystem info for PSEUDOFS */ static struct fsal_staticfsinfo_t default_posix_info = { .maxfilesize = UINT64_MAX, .maxlink = 0, .maxnamelen = MAXNAMLEN, .maxpathlen = MAXPATHLEN, .no_trunc = true, .chown_restricted = true, .case_insensitive = false, .case_preserving = true, .link_support = false, .symlink_support = false, .lock_support = false, .lock_support_async_block = false, .named_attr = false, .unique_handles = true, .lease_time = {10, 0}, .acl_support = 0, .cansettime = true, .homogenous = true, .supported_attrs = PSEUDO_SUPPORTED_ATTRS, .maxread = FSAL_MAXIOSIZE, .maxwrite = FSAL_MAXIOSIZE, .umask = 0, .auth_exportpath_xdev = false, .xattr_access_rights = 0400, /* root=RW, owner=R */ .link_supports_permission_checks = false, }; /* private helper for export object */ struct fsal_staticfsinfo_t *pseudofs_staticinfo(struct fsal_module *hdl) { struct pseudo_fsal_module *myself; myself = container_of(hdl, struct pseudo_fsal_module, fsal); return &myself->fs_info; } /* Initialize pseudo fs info */ static void init_config(struct fsal_module *fsal_hdl) { struct pseudo_fsal_module *pseudofs_me = container_of(fsal_hdl, struct pseudo_fsal_module, fsal); /* get a copy of the defaults */ pseudofs_me->fs_info = default_posix_info; /* if we have fsal specific params, do them here * fsal_hdl->name is used to find the block containing the * params. */ display_fsinfo(&pseudofs_me->fs_info); LogFullDebug(COMPONENT_FSAL, "Supported attributes default = 0x%" PRIx64, default_posix_info.supported_attrs); LogDebug(COMPONENT_FSAL, "FSAL INIT: Supported attributes mask = 0x%" PRIx64, pseudofs_me->fs_info.supported_attrs); } /* Module initialization. * Called by dlopen() to register the module * keep a private pointer to me in myself */ /* my module private storage */ static struct pseudo_fsal_module PSEUDOFS; /* linkage to the exports and handle ops initializers */ int unload_pseudo_fsal(struct fsal_module *fsal_hdl) { int retval; retval = unregister_fsal(&PSEUDOFS.fsal); if (retval != 0) fprintf(stderr, "PSEUDO module failed to unregister"); return retval; } void pseudo_fsal_init(void) { int retval; struct fsal_module *myself = &PSEUDOFS.fsal; retval = register_fsal(myself, pseudoname, FSAL_MAJOR_VERSION, FSAL_MINOR_VERSION, FSAL_ID_NO_PNFS); if (retval != 0) { fprintf(stderr, "PSEUDO module failed to register"); return; } myself->m_ops.create_export = pseudofs_create_export; myself->m_ops.unload = unload_pseudo_fsal; /* initialize our config */ init_config(myself); } nfs-ganesha-2.6.0/src/FSAL/FSAL_PSEUDO/pseudofs_methods.h000066400000000000000000000054661324272410200225650ustar00rootroot00000000000000/* * vim:expandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* PSEUDOFS methods for handles */ #include "avltree.h" #include "gsh_list.h" #define PSEUDO_SUPPORTED_ATTRS ((const attrmask_t) (ATTRS_POSIX)) struct pseudo_fsal_obj_handle; /* * PSEUDOFS internal export */ struct pseudofs_fsal_export { struct fsal_export export; char *export_path; struct pseudo_fsal_obj_handle *root_handle; }; fsal_status_t pseudofs_lookup_path(struct fsal_export *exp_hdl, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out); fsal_status_t pseudofs_create_handle(struct fsal_export *exp_hdl, struct gsh_buffdesc *hdl_desc, struct fsal_obj_handle **handle, struct attrlist *attrs_out); /* * PSEUDOFS internal object handle * handle is a pointer because * a) the last element of file_handle is a char[] meaning variable len... * b) we cannot depend on it *always* being last or being the only * variable sized struct here... a pointer is safer. */ struct pseudo_fsal_obj_handle { struct fsal_obj_handle obj_handle; struct attrlist attributes; char *handle; struct pseudo_fsal_obj_handle *parent; struct avltree avl_name; struct avltree avl_index; struct avltree_node avl_n; struct avltree_node avl_i; uint32_t index; /* index in parent */ uint32_t next_i; /* next child index */ uint32_t numlinks; char *name; bool inavl; }; static inline bool pseudofs_unopenable_type(object_file_type_t type) { if ((type == SOCKET_FILE) || (type == CHARACTER_FILE) || (type == BLOCK_FILE)) { return true; } else { return false; } } void pseudofs_handle_ops_init(struct fsal_obj_ops *ops); /* Internal PSEUDOFS method linkage to export object */ fsal_status_t pseudofs_create_export(struct fsal_module *fsal_hdl, void *parse_node, struct config_error_type *err_type, const struct fsal_up_vector *up_ops); nfs-ganesha-2.6.0/src/FSAL/FSAL_RGW/000077500000000000000000000000001324272410200164665ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/FSAL/FSAL_RGW/CMakeLists.txt000066400000000000000000000007671324272410200212400ustar00rootroot00000000000000add_definitions( -D_FILE_OFFSET_BITS=64 ) SET(fsalrgw_LIB_SRCS up.c main.c export.c handle.c internal.c internal.h ) message("RGW_INCLUDE_DIR ${RGW_INCLUDE_DIR}") include_directories(${RGW_INCLUDE_DIR}) add_library(fsalrgw MODULE ${fsalrgw_LIB_SRCS}) add_sanitizers(fsalrgw) target_link_libraries(fsalrgw ${RGW_LIBRARIES} ${SYSTEM_LIBRARIES}) set_target_properties(fsalrgw PROPERTIES VERSION 4.2.0 SOVERSION 4) install(TARGETS fsalrgw COMPONENT fsal DESTINATION ${FSAL_DESTINATION} ) nfs-ganesha-2.6.0/src/FSAL/FSAL_RGW/export.c000066400000000000000000000310771324272410200201630ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Red Hat, 2015 * Author: Orit Wasserman * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* export.c * RGW FSAL export object */ #include #include #include #include "abstract_mem.h" #include "fsal.h" #include "fsal_types.h" #include "fsal_api.h" #include "FSAL/fsal_commonlib.h" #include "FSAL/fsal_config.h" #include "internal.h" /** * @brief Clean up an export * * This function cleans up an export after the last reference is * released. * * @param[in,out] export The export to be released * * @retval ERR_FSAL_NO_ERROR on success. * @retval ERR_FSAL_BUSY if the export is in use. */ static void release(struct fsal_export *export_pub) { /* The private, expanded export */ struct rgw_export *export = container_of(export_pub, struct rgw_export, export); int rc = rgw_umount(export->rgw_fs, RGW_UMOUNT_FLAG_NONE); assert(rc == 0); deconstruct_handle(export->root); export->rgw_fs = NULL; export->root = NULL; fsal_detach_export(export->export.fsal, &export->export.exports); free_export_ops(&export->export); /* XXX we might need/want an rgw_unmount here, but presently, * it wouldn't do anything */ gsh_free(export); export = NULL; } /** * @brief Return a handle corresponding to a path * * This function looks up the given path and supplies an FSAL object * handle. * * @param[in] export_pub The export in which to look up the file * @param[in] path The path to look up * @param[out] pub_handle The created public FSAL handle * * @return FSAL status. */ static fsal_status_t lookup_path(struct fsal_export *export_pub, const char *path, struct fsal_obj_handle **pub_handle, struct attrlist *attrs_out) { /* The 'private' full export handle */ struct rgw_export *export = container_of(export_pub, struct rgw_export, export); /* The 'private' full object handle */ struct rgw_handle *handle = NULL; /* FSAL status structure */ fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; /* The buffer in which to store stat info */ struct stat st; /* Return code from Ceph */ int rc; /* temp filehandle */ struct rgw_file_handle *rgw_fh; *pub_handle = NULL; /* should only be "/" or "bucket_name" */ if (strcmp(path, "/") && strchr(path, '/')) { status.major = ERR_FSAL_INVAL; return status; } #ifndef USE_FSAL_RGW_MOUNT2 /* XXX in FSAL_CEPH, the equivalent code here looks for path == "/" * and returns the root handle with no extra ref. That seems * suspicious, so let RGW figure it out (hopefully, that does not * leak refs) */ rc = rgw_lookup(export->rgw_fs, export->rgw_fs->root_fh, path, &rgw_fh, RGW_LOOKUP_FLAG_NONE); if (rc < 0) return rgw2fsal_error(rc); #else rgw_fh = export->rgw_fs->root_fh; #endif /* get Unix attrs */ rc = rgw_getattr(export->rgw_fs, export->rgw_fs->root_fh, &st, RGW_GETATTR_FLAG_NONE); if (rc < 0) { return rgw2fsal_error(rc); } #ifndef USE_FSAL_RGW_MOUNT2 struct stat st_root; /* fixup export fsid */ rc = rgw_getattr(export->rgw_fs, export->rgw_fs->root_fh, &st_root, RGW_GETATTR_FLAG_NONE); if (rc < 0) { return rgw2fsal_error(rc); } st.st_dev = st_root.st_dev; #endif rc = construct_handle(export, rgw_fh, &st, &handle); if (rc < 0) { return rgw2fsal_error(rc); } *pub_handle = &handle->handle; if (attrs_out != NULL) { posix2fsal_attributes_all(&st, attrs_out); } return status; } /** * @brief Decode a digested handle * * This function decodes a previously digested handle. * * @param[in] exp_handle Handle of the relevant fs export * @param[in] in_type The type of digest being decoded * @param[out] fh_desc Address and length of key */ static fsal_status_t wire_to_host(struct fsal_export *exp_hdl, fsal_digesttype_t in_type, struct gsh_buffdesc *fh_desc, int flags) { switch (in_type) { /* Digested Handles */ case FSAL_DIGEST_NFSV3: case FSAL_DIGEST_NFSV4: /* wire handles */ fh_desc->len = sizeof(struct rgw_fh_hk); break; default: return fsalstat(ERR_FSAL_SERVERFAULT, 0); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Create a handle object from a wire handle * * The wire handle is given in a buffer outlined by desc, which it * looks like we shouldn't modify. * * @param[in] export_pub Public export * @param[in] desc Handle buffer descriptor * @param[out] pub_handle The created handle * * @return FSAL status. */ static fsal_status_t create_handle(struct fsal_export *export_pub, struct gsh_buffdesc *desc, struct fsal_obj_handle **pub_handle, struct attrlist *attrs_out) { /* Full 'private' export structure */ struct rgw_export *export = container_of(export_pub, struct rgw_export, export); /* FSAL status to return */ fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; /* The FSAL specific portion of the handle received by the client */ int rc = 0; /* Stat buffer */ struct stat st; /* Handle to be created */ struct rgw_handle *handle = NULL; /* RGW fh hash key */ struct rgw_fh_hk fh_hk; /* RGW file handle instance */ struct rgw_file_handle *rgw_fh; *pub_handle = NULL; if (desc->len != sizeof(struct rgw_fh_hk)) { status.major = ERR_FSAL_INVAL; return status; } memcpy((char *) &fh_hk, desc->addr, desc->len); rc = rgw_lookup_handle(export->rgw_fs, &fh_hk, &rgw_fh, RGW_LOOKUP_FLAG_NONE); if (rc < 0) return rgw2fsal_error(-ESTALE); rc = rgw_getattr(export->rgw_fs, rgw_fh, &st, RGW_GETATTR_FLAG_NONE); if (rc < 0) return rgw2fsal_error(rc); rc = construct_handle(export, rgw_fh, &st, &handle); if (rc < 0) { return rgw2fsal_error(rc); } *pub_handle = &handle->handle; if (attrs_out != NULL) { posix2fsal_attributes_all(&st, attrs_out); } return status; } /** * @brief Get dynamic filesystem info * * This function returns dynamic filesystem information for the given * export. * * @param[in] export_pub The public export handle * @param[out] info The dynamic FS information * * @return FSAL status. */ static fsal_status_t get_fs_dynamic_info(struct fsal_export *export_pub, struct fsal_obj_handle *obj_hdl, fsal_dynamicfsinfo_t *info) { /* Full 'private' export */ struct rgw_export *export = container_of(export_pub, struct rgw_export, export); int rc = 0; /* Filesystem stat */ struct rgw_statvfs vfs_st; rc = rgw_statfs(export->rgw_fs, export->rgw_fs->root_fh, &vfs_st, RGW_STATFS_FLAG_NONE); if (rc < 0) return rgw2fsal_error(rc); /* TODO: implement in rgw_file */ memset(info, 0, sizeof(fsal_dynamicfsinfo_t)); info->total_bytes = vfs_st.f_frsize * vfs_st.f_blocks; info->free_bytes = vfs_st.f_frsize * vfs_st.f_bfree; info->avail_bytes = vfs_st.f_frsize * vfs_st.f_bavail; info->total_files = vfs_st.f_files; info->free_files = vfs_st.f_ffree; info->avail_files = vfs_st.f_favail; info->time_delta.tv_sec = 1; info->time_delta.tv_nsec = 0; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Query the FSAL's capabilities * * This function queries the capabilities of an FSAL export. * * @param[in] export_pub The public export handle * @param[in] option The option to check * * @retval true if the option is supported. * @retval false if the option is unsupported (or unknown). */ static bool fs_supports(struct fsal_export *export_pub, fsal_fsinfo_options_t option) { struct fsal_staticfsinfo_t *info = rgw_staticinfo(export_pub->fsal); return fsal_supports(info, option); } /** * @brief Return the longest file supported * * This function returns the length of the longest file supported. * * @param[in] export_pub The public export * * @return UINT64_MAX. */ static uint64_t fs_maxfilesize(struct fsal_export *export_pub) { return UINT64_MAX; } /** * @brief Return the longest read supported * * This function returns the length of the longest read supported. * * @param[in] export_pub The public export * * @return 4 mebibytes. */ static uint32_t fs_maxread(struct fsal_export *export_pub) { return 0x400000; } /** * @brief Return the longest write supported * * This function returns the length of the longest write supported. * * @param[in] export_pub The public export * * @return 4 mebibytes. */ static uint32_t fs_maxwrite(struct fsal_export *export_pub) { return 0x400000; } /** * @brief Return the maximum number of hard links to a file * * This function returns the maximum number of hard links supported to * any file. * * @param[in] export_pub The public export * * @return 1024. */ static uint32_t fs_maxlink(struct fsal_export *export_pub) { /* Ceph does not like hard links. See the anchor table design. We should fix this, but have to do it in the Ceph core. */ return 1024; } /** * @brief Return the maximum size of a Ceph filename * * This function returns the maximum filename length. * * @param[in] export_pub The public export * * @return UINT32_MAX. */ static uint32_t fs_maxnamelen(struct fsal_export *export_pub) { /* Ceph actually supports filenames of unlimited length, at least according to the protocol docs. We may wish to constrain this later. */ return UINT32_MAX; } /** * @brief Return the maximum length of a Ceph path * * This function returns the maximum path length. * * @param[in] export_pub The public export * * @return UINT32_MAX. */ static uint32_t fs_maxpathlen(struct fsal_export *export_pub) { /* Similarly unlimited int he protocol */ return UINT32_MAX; } /** * @brief Return the lease time * * This function returns the lease time. * * @param[in] export_pub The public export * * @return five minutes. */ static struct timespec fs_lease_time(struct fsal_export *export_pub) { struct timespec lease = { 300, 0 }; return lease; } /** * @brief Return ACL support * * This function returns the export's ACL support. * * @param[in] export_pub The public export * * @return FSAL_ACLSUPPORT_DENY. */ static fsal_aclsupp_t fs_acl_support(struct fsal_export *export_pub) { return FSAL_ACLSUPPORT_DENY; } /** * @brief Return the attributes supported by this FSAL * * This function returns the mask of attributes this FSAL can support. * * @param[in] export_pub The public export * * @return supported_attributes as defined in internal.c. */ static attrmask_t fs_supported_attrs(struct fsal_export *export_pub) { return RGW_SUPPORTED_ATTRIBUTES; } /** * @brief Return the mode under which the FSAL will create files * * This function modifies the default mode on any new file created. * * @param[in] export_pub The public export * * @return 0 (usually). Bits set here turn off bits in created files. */ static uint32_t fs_umask(struct fsal_export *export_pub) { return fsal_umask(rgw_staticinfo(export_pub->fsal)); } /** * @brief Return the mode for extended attributes * * This function returns the access mode applied to extended * attributes. Dubious. * * @param[in] export_pub The public export * * @return 0644. */ static uint32_t fs_xattr_access_rights(struct fsal_export *export_pub) { return fsal_xattr_access_rights(rgw_staticinfo(export_pub->fsal)); } /** * @brief Set operations for exports * * This function overrides operations that we've implemented, leaving * the rest for the default. * * @param[in,out] ops Operations vector */ void export_ops_init(struct export_ops *ops) { ops->release = release; ops->lookup_path = lookup_path; ops->wire_to_host = wire_to_host; ops->create_handle = create_handle; ops->get_fs_dynamic_info = get_fs_dynamic_info; ops->fs_supports = fs_supports; ops->fs_maxfilesize = fs_maxfilesize; ops->fs_maxread = fs_maxread; ops->fs_maxwrite = fs_maxwrite; ops->fs_maxlink = fs_maxlink; ops->fs_maxnamelen = fs_maxnamelen; ops->fs_maxpathlen = fs_maxpathlen; ops->fs_lease_time = fs_lease_time; ops->fs_acl_support = fs_acl_support; ops->fs_supported_attrs = fs_supported_attrs; ops->fs_umask = fs_umask; ops->fs_xattr_access_rights = fs_xattr_access_rights; ops->alloc_state = rgw_alloc_state; } nfs-ganesha-2.6.0/src/FSAL/FSAL_RGW/handle.c000066400000000000000000001321701324272410200200710ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Red Hat Inc., 2015 * Author: Orit Wasserman * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* handle.c * RGW object (file|dir) handle object */ #include #include "fsal.h" #include "fsal_types.h" #include "fsal_convert.h" #include "fsal_api.h" #include "internal.h" #include "nfs_exports.h" #include "FSAL/fsal_commonlib.h" /** * @brief Release an object * * @param[in] obj_hdl The object to release * * @return FSAL status codes. */ static void release(struct fsal_obj_handle *obj_hdl) { struct rgw_handle *obj = container_of(obj_hdl, struct rgw_handle, handle); struct rgw_export *export = obj->export; if (obj->rgw_fh != export->rgw_fs->root_fh) { /* release RGW ref */ (void) rgw_fh_rele(export->rgw_fs, obj->rgw_fh, 0 /* flags */); } deconstruct_handle(obj); } /** * @brief Look up an object by name * * This function looks up an object by name in a directory. * * @param[in] dir_hdl The directory in which to look up the object. * @param[in] path The name to look up. * @param[in,out] obj_hdl The looked up object. * @param[in,out] attrs_out Optional attributes for newly created object * * @return FSAL status codes. */ static fsal_status_t lookup_int(struct fsal_obj_handle *dir_hdl, const char *path, struct fsal_obj_handle **obj_hdl, struct attrlist *attrs_out, uint32_t flags) { int rc; struct stat st; struct rgw_file_handle *rgw_fh; struct rgw_handle *obj; struct rgw_export *export = container_of(op_ctx->fsal_export, struct rgw_export, export); struct rgw_handle *dir = container_of(dir_hdl, struct rgw_handle, handle); LogFullDebug(COMPONENT_FSAL, "%s enter dir_hdl %p path %s", __func__, dir_hdl, path); /* XXX presently, we can only fake attrs--maybe rgw_lookup should * take struct stat pointer OUT as libcephfs' does */ rc = rgw_lookup(export->rgw_fs, dir->rgw_fh, path, &rgw_fh, flags); if (rc < 0) return rgw2fsal_error(rc); rc = rgw_getattr(export->rgw_fs, rgw_fh, &st, RGW_GETATTR_FLAG_NONE); if (rc < 0) return rgw2fsal_error(rc); rc = construct_handle(export, rgw_fh, &st, &obj); if (rc < 0) { return rgw2fsal_error(rc); } *obj_hdl = &obj->handle; if (attrs_out != NULL) { posix2fsal_attributes_all(&st, attrs_out); } return fsalstat(0, 0); } static fsal_status_t lookup(struct fsal_obj_handle *dir_hdl, const char *path, struct fsal_obj_handle **obj_hdl, struct attrlist *attrs_out) { return lookup_int(dir_hdl, path, obj_hdl, attrs_out, RGW_LOOKUP_FLAG_NONE); } struct rgw_cb_arg { fsal_readdir_cb cb; void *fsal_arg; struct fsal_obj_handle *dir_hdl; attrmask_t attrmask; }; static bool rgw_cb(const char *name, void *arg, uint64_t offset, uint32_t flags) { struct rgw_cb_arg *rgw_cb_arg = arg; struct fsal_obj_handle *obj; fsal_status_t status; struct attrlist attrs; enum fsal_dir_result cb_rc; fsal_prepare_attrs(&attrs, rgw_cb_arg->attrmask); /* rgw_lookup now accepts type hints */ status = lookup_int(rgw_cb_arg->dir_hdl, name, &obj, &attrs, RGW_LOOKUP_FLAG_RCB| (flags & (RGW_LOOKUP_FLAG_DIR|RGW_LOOKUP_FLAG_FILE))); if (FSAL_IS_ERROR(status)) return false; /** @todo FSF - when rgw gains mark capability, need to change this * code... */ cb_rc = rgw_cb_arg->cb(name, obj, &attrs, rgw_cb_arg->fsal_arg, offset); fsal_release_attrs(&attrs); return cb_rc <= DIR_READAHEAD; } /** * @brief Read a directory * * This function reads the contents of a directory (excluding . and * .., which is ironic since the Ceph readdir call synthesizes them * out of nothing) and passes dirent information to the supplied * callback. * * @param[in] dir_hdl The directory to read * @param[in] whence The cookie indicating resumption, NULL to start * @param[in] dir_state Opaque, passed to cb * @param[in] cb Callback that receives directory entries * @param[out] eof True if there are no more entries * * @return FSAL status. */ static fsal_status_t rgw_fsal_readdir(struct fsal_obj_handle *dir_hdl, fsal_cookie_t *whence, void *cb_arg, fsal_readdir_cb cb, attrmask_t attrmask, bool *eof) { int rc; fsal_status_t fsal_status = {ERR_FSAL_NO_ERROR, 0}; struct rgw_cb_arg rgw_cb_arg = {cb, cb_arg, dir_hdl, attrmask}; /* when whence_is_name, whence is a char pointer cast to * fsal_cookie_t */ const char *r_whence = (const char *) whence; struct rgw_export *export = container_of(op_ctx->fsal_export, struct rgw_export, export); struct rgw_handle *dir = container_of(dir_hdl, struct rgw_handle, handle); LogFullDebug(COMPONENT_FSAL, "%s enter dir_hdl %p", __func__, dir_hdl); rc = 0; *eof = false; rc = rgw_readdir2(export->rgw_fs, dir->rgw_fh, r_whence, rgw_cb, &rgw_cb_arg, eof, RGW_READDIR_FLAG_NONE); if (rc < 0) return rgw2fsal_error(rc); return fsal_status; } #if ((LIBRGW_FILE_VER_MAJOR > 1) || \ ((LIBRGW_FILE_VER_MAJOR == 1) && \ ((LIBRGW_FILE_VER_MINOR > 1) || \ ((LIBRGW_FILE_VER_MINOR == 1) && (LIBRGW_FILE_VER_EXTRA >= 4))))) #define HAVE_DIRENT_OFFSETOF 1 #else #define HAVE_DIRENT_OFFSETOF 0 #endif #if HAVE_DIRENT_OFFSETOF /** * @brief Project cookie offset for a dirent name * * This optional API function produces the stable offset which * corresponds to a given dirent name (FSALs for which there is * no stable mapping will not implement). * * @param[in] dir_hdl The containing directory * @param[in] name The dirent name * * @return FSAL status. */ static fsal_cookie_t rgw_fsal_compute_cookie( struct fsal_obj_handle *dir_hdl, const char *name) { uint64_t offset = 0; /* XXX */ struct rgw_export *export = container_of(op_ctx->fsal_export, struct rgw_export, export); struct rgw_handle *dir = container_of(dir_hdl, struct rgw_handle, handle); LogFullDebug(COMPONENT_FSAL, "%s enter dir_hdl %p name %s", __func__, dir_hdl, name); if (unlikely(!strcmp(name, ".."))) { return 1; } if (unlikely(!strcmp(name, "."))) { return 2; } (void) rgw_dirent_offset(export->rgw_fs, dir->rgw_fh, name, &offset, RGW_DIRENT_OFFSET_FLAG_NONE); return offset; } #endif /* HAVE_DIRENT_OFFSETOF */ /** * @brief Help sort dirents. * * For FSALs that are able to compute the cookie for a filename * deterministically from the filename, there must also be a defined order of * entries in a directory based on the name (could be strcmp sort, could be * strict alpha sort, could be deterministic order based on cookie). * * Although the cookies could be computed, the caller will already have them * and thus will provide them to save compute time. * * @param[in] parent Directory entries belong to. * @param[in] name1 File name of first dirent * @param[in] cookie1 Cookie of first dirent * @param[in] name2 File name of second dirent * @param[in] cookie2 Cookie of second dirent * * @retval < 0 if name1 sorts before name2 * @retval == 0 if name1 sorts the same as name2 * @retval >0 if name1 sorts after name2 */ int rgw_fsal_dirent_cmp( struct fsal_obj_handle *parent, const char *name1, fsal_cookie_t cookie1, const char *name2, fsal_cookie_t cookie2) { return strcmp(name1, name2); } /** * @brief Create a directory * * This function creates a new directory. * * For support_ex, this method will handle attribute setting. The caller * MUST include the mode attribute and SHOULD NOT include the owner or * group attributes if they are the same as the op_ctx->cred. * * @param[in] dir_hdl Directory in which to create the directory * @param[in] name Name of directory to create * @param[in] attrib Attributes to set on newly created object * @param[out] new_obj Newly created object * * @note On success, @a new_obj has been ref'd * * @return FSAL status. */ static fsal_status_t rgw_fsal_mkdir(struct fsal_obj_handle *dir_hdl, const char *name, struct attrlist *attrs_in, struct fsal_obj_handle **obj_hdl, struct attrlist *attrs_out) { int rc; struct rgw_file_handle *rgw_fh; struct rgw_handle *obj; struct stat st; struct rgw_export *export = container_of(op_ctx->fsal_export, struct rgw_export, export); struct rgw_handle *dir = container_of(dir_hdl, struct rgw_handle, handle); LogFullDebug(COMPONENT_FSAL, "%s enter dir_hdl %p name %s", __func__, dir_hdl, name); memset(&st, 0, sizeof(struct stat)); st.st_uid = op_ctx->creds->caller_uid; st.st_gid = op_ctx->creds->caller_gid; st.st_mode = fsal2unix_mode(attrs_in->mode) & ~op_ctx->fsal_export->exp_ops.fs_umask(op_ctx->fsal_export); uint32_t create_mask = RGW_SETATTR_UID | RGW_SETATTR_GID | RGW_SETATTR_MODE; rc = rgw_mkdir(export->rgw_fs, dir->rgw_fh, name, &st, create_mask, &rgw_fh, RGW_MKDIR_FLAG_NONE); if (rc < 0) return rgw2fsal_error(rc); rc = construct_handle(export, rgw_fh, &st, &obj); if (rc < 0) { return rgw2fsal_error(rc); } *obj_hdl = &obj->handle; if (attrs_out != NULL) { posix2fsal_attributes_all(&st, attrs_out); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Freshen and return attributes * * This function freshens and returns the attributes of the given * file. * * @param[in] obj_hdl Object to interrogate * * @return FSAL status. */ static fsal_status_t getattrs(struct fsal_obj_handle *obj_hdl, struct attrlist *attrs) { int rc; struct stat st; struct rgw_export *export = container_of(op_ctx->fsal_export, struct rgw_export, export); struct rgw_handle *handle = container_of(obj_hdl, struct rgw_handle, handle); LogFullDebug(COMPONENT_FSAL, "%s enter obj_hdl %p", __func__, obj_hdl); rc = rgw_getattr(export->rgw_fs, handle->rgw_fh, &st, RGW_GETATTR_FLAG_NONE); if (rc < 0) { if (attrs->request_mask & ATTR_RDATTR_ERR) { /* Caller asked for error to be visible. */ attrs->valid_mask = ATTR_RDATTR_ERR; } return rgw2fsal_error(rc); } posix2fsal_attributes_all(&st, attrs); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Set attributes on an object * * This function sets attributes on an object. Which attributes are * set is determined by attrib_set->valid_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 rgw_fsal_setattr2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, struct attrlist *attrib_set) { fsal_status_t status = {0, 0}; int rc = 0; bool has_lock = false; bool closefd = false; struct stat st; /* Mask of attributes to set */ uint32_t mask = 0; bool reusing_open_state_fd = false; struct rgw_export *export = container_of(op_ctx->fsal_export, struct rgw_export, export); struct rgw_handle *handle = container_of(obj_hdl, struct rgw_handle, handle); LogFullDebug(COMPONENT_FSAL, "%s enter obj_hdl %p state %p", __func__, obj_hdl, state); if (attrib_set->valid_mask & ~RGW_SETTABLE_ATTRIBUTES) { LogDebug(COMPONENT_FSAL, "bad mask %"PRIx64" not settable %"PRIx64, attrib_set->valid_mask, attrib_set->valid_mask & ~RGW_SETTABLE_ATTRIBUTES); return fsalstat(ERR_FSAL_INVAL, 0); } LogAttrlist(COMPONENT_FSAL, NIV_FULL_DEBUG, "attrs ", attrib_set, false); /* apply umask, if mode attribute is to be changed */ if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_MODE)) attrib_set->mode &= ~op_ctx->fsal_export->exp_ops.fs_umask( op_ctx->fsal_export); /* 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->valid_mask, ATTR_SIZE)) { if (obj_hdl->type != REGULAR_FILE) { LogFullDebug(COMPONENT_FSAL, "Setting size on non-regular file"); return fsalstat(ERR_FSAL_INVAL, EINVAL); } /* We don't actually need an open fd, we are just doing the * share reservation checking, thus the NULL parameters. */ status = fsal_find_fd(NULL, obj_hdl, NULL, &handle->share, bypass, state, FSAL_O_RDWR, NULL, NULL, &has_lock, &closefd, false, &reusing_open_state_fd); if (FSAL_IS_ERROR(status)) { LogFullDebug(COMPONENT_FSAL, "fsal_find_fd status=%s", fsal_err_txt(status)); goto out; } } memset(&st, 0, sizeof(struct stat)); if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_SIZE)) { rc = rgw_truncate(export->rgw_fs, handle->rgw_fh, attrib_set->filesize, RGW_TRUNCATE_FLAG_NONE); if (rc < 0) { status = rgw2fsal_error(rc); LogDebug(COMPONENT_FSAL, "truncate returned %s (%d)", strerror(-rc), -rc); goto out; } } if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_MODE)) { mask |= RGW_SETATTR_MODE; st.st_mode = fsal2unix_mode(attrib_set->mode); } if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_OWNER)) { mask |= RGW_SETATTR_UID; st.st_uid = attrib_set->owner; } if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_GROUP)) { mask |= RGW_SETATTR_GID; st.st_gid = attrib_set->group; } if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_ATIME)) { mask |= RGW_SETATTR_ATIME; st.st_atim = attrib_set->atime; } if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_ATIME_SERVER)) { mask |= RGW_SETATTR_ATIME; struct timespec timestamp; rc = clock_gettime(CLOCK_REALTIME, ×tamp); if (rc != 0) { LogDebug(COMPONENT_FSAL, "clock_gettime returned %s (%d)", strerror(-rc), -rc); status = rgw2fsal_error(rc); goto out; } st.st_atim = timestamp; } if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_MTIME)) { mask |= RGW_SETATTR_MTIME; st.st_mtim = attrib_set->mtime; } if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_MTIME_SERVER)) { mask |= RGW_SETATTR_MTIME; struct timespec timestamp; rc = clock_gettime(CLOCK_REALTIME, ×tamp); if (rc != 0) { LogDebug(COMPONENT_FSAL, "clock_gettime returned %s (%d)", strerror(-rc), -rc); status = rgw2fsal_error(rc); goto out; } st.st_mtim = timestamp; } if (FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_CTIME)) { mask |= RGW_SETATTR_CTIME; st.st_ctim = attrib_set->ctime; } rc = rgw_setattr(export->rgw_fs, handle->rgw_fh, &st, mask, RGW_SETATTR_FLAG_NONE); if (rc < 0) { LogDebug(COMPONENT_FSAL, "setattr returned %s (%d)", strerror(-rc), -rc); status = rgw2fsal_error(rc); } else { /* Success */ status = fsalstat(ERR_FSAL_NO_ERROR, 0); } out: if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /** * @brief Rename a file * * This function renames a file, possibly moving it into another * directory. We assume most checks are done by the caller. * * @param[in] olddir_hdl Source directory * @param[in] old_name Original name * @param[in] newdir_hdl Destination directory * @param[in] new_name New name * * @return FSAL status. */ static fsal_status_t rgw_fsal_rename(struct fsal_obj_handle *obj_hdl, struct fsal_obj_handle *olddir_hdl, const char *old_name, struct fsal_obj_handle *newdir_hdl, const char *new_name) { int rc; struct rgw_export *export = container_of(op_ctx->fsal_export, struct rgw_export, export); struct rgw_handle *olddir = container_of(olddir_hdl, struct rgw_handle, handle); struct rgw_handle *newdir = container_of(newdir_hdl, struct rgw_handle, handle); LogFullDebug(COMPONENT_FSAL, "%s enter obj_hdl %p olddir_hdl %p oname %s newdir_hdl %p nname %s", __func__, obj_hdl, olddir_hdl, old_name, newdir_hdl, new_name); rc = rgw_rename(export->rgw_fs, olddir->rgw_fh, old_name, newdir->rgw_fh, new_name, RGW_RENAME_FLAG_NONE); if (rc < 0) return rgw2fsal_error(rc); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Remove a name * * This function removes a name from the filesystem and possibly * deletes the associated file. Directories must be empty to be * removed. * * @param[in] dir_hdl The directory from which to remove the name * @param[in] obj_hdl The object being removed * @param[in] name The name to remove * * @return FSAL status. */ static fsal_status_t rgw_fsal_unlink(struct fsal_obj_handle *dir_hdl, struct fsal_obj_handle *obj_hdl, const char *name) { int rc; struct rgw_export *export = container_of(op_ctx->fsal_export, struct rgw_export, export); struct rgw_handle *dir = container_of(dir_hdl, struct rgw_handle, handle); LogFullDebug(COMPONENT_FSAL, "%s enter dir_hdl %p obj_hdl %p name %s", __func__, dir_hdl, obj_hdl, name); rc = rgw_unlink(export->rgw_fs, dir->rgw_fh, name, RGW_UNLINK_FLAG_NONE); if (rc < 0) return rgw2fsal_error(rc); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Merge a duplicate handle with an original handle * * This function is used if an upper layer detects that a duplicate * object handle has been created. It allows the FSAL to merge anything * from the duplicate back into the original. * * The caller must release the object (the caller may have to close * files if the merge is unsuccessful). * * @param[in] orig_hdl Original handle * @param[in] dupe_hdl Handle to merge into original * * @return FSAL status. * */ fsal_status_t rgw_merge(struct fsal_obj_handle *orig_hdl, struct fsal_obj_handle *dupe_hdl) { fsal_status_t status = {ERR_FSAL_NO_ERROR, 0}; if (orig_hdl->type == REGULAR_FILE && dupe_hdl->type == REGULAR_FILE) { /* We need to merge the share reservations on this file. * This could result in ERR_FSAL_SHARE_DENIED. */ struct rgw_handle *orig, *dupe; orig = container_of(orig_hdl, struct rgw_handle, handle); dupe = container_of(dupe_hdl, struct rgw_handle, handle); /* This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&orig_hdl->obj_lock); status = merge_share(&orig->share, &dupe->share); PTHREAD_RWLOCK_unlock(&orig_hdl->obj_lock); } return status; } /** * @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. * * If attributes are not set on create, the FSAL will set some minimal * attributes (for example, mode might be set to 0600). * * 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] caller_perm_check The caller must do a permission check * * @return FSAL status. */ fsal_status_t rgw_fsal_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 rc; mode_t unix_mode; fsal_status_t status = {0, 0}; struct stat st; bool truncated; bool setattrs = attrib_set != NULL; bool created = false; struct attrlist verifier_attr; struct rgw_open_state *open_state = NULL; struct rgw_file_handle *rgw_fh; struct rgw_handle *obj; struct rgw_export *export = container_of(op_ctx->fsal_export, struct rgw_export, export); struct rgw_handle *handle = container_of(obj_hdl, struct rgw_handle, handle); LogFullDebug(COMPONENT_FSAL, "%s enter obj_hdl %p state %p", __func__, obj_hdl, open_state); if (state) { open_state = (struct rgw_open_state *) state; } if (setattrs) LogAttrlist(COMPONENT_FSAL, NIV_FULL_DEBUG, "attrs ", attrib_set, false); fsal2posix_openflags(openflags, &posix_flags); truncated = (posix_flags & O_TRUNC) != 0; /* Now fixup attrs for verifier if exclusive create */ if (createmode >= FSAL_EXCLUSIVE) { if (!setattrs) { /* We need to use verifier_attr */ attrib_set = &verifier_attr; memset(&verifier_attr, 0, sizeof(verifier_attr)); } set_common_verifier(attrib_set, verifier); } if (!name) { /* This is an open by handle */ if (state) { /* 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->obj_lock); /* Check share reservation conflicts. */ status = check_share_conflict(&handle->share, openflags, false); if (FSAL_IS_ERROR(status)) { PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /* Take the share reservation now by updating the * counters. */ update_share_counters(&handle->share, FSAL_O_CLOSED, openflags); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); } else { /* RGW doesn't have a file descriptor/open abstraction, * and actually forbids concurrent opens; This is * where more advanced FSALs would fall back to using * a "global" fd--what we always use; We still need * to take the lock expected by ULP */ #if 0 my_fd = &hdl->fd; #endif PTHREAD_RWLOCK_wrlock(&obj_hdl->obj_lock); } rc = rgw_open(export->rgw_fs, handle->rgw_fh, posix_flags, (!state) ? RGW_OPEN_FLAG_V3 : RGW_OPEN_FLAG_NONE); if (rc < 0) { if (!state) { /* Release the lock taken above, and return * since there is nothing to undo. */ PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return rgw2fsal_error(rc); } else { /* Error - need to release the share */ goto undo_share; } } if (createmode >= FSAL_EXCLUSIVE || truncated) { /* refresh attributes */ rc = rgw_getattr(export->rgw_fs, handle->rgw_fh, &st, RGW_GETATTR_FLAG_NONE); if (rc < 0) { status = rgw2fsal_error(rc); } else { LogFullDebug(COMPONENT_FSAL, "New size = %"PRIx64, st.st_size); /* Now check verifier for exclusive, but not for * FSAL_EXCLUSIVE_9P. */ if (createmode >= FSAL_EXCLUSIVE && createmode != FSAL_EXCLUSIVE_9P && !obj_hdl->obj_ops.check_verifier( obj_hdl, verifier)) { /* Verifier didn't match */ status = fsalstat(posix2fsal_error( EEXIST), EEXIST); } else if (attrs_out) { posix2fsal_attributes_all(&st, attrs_out); } } } else if (attrs_out && attrs_out->request_mask & ATTR_RDATTR_ERR) { attrs_out->valid_mask &= ATTR_RDATTR_ERR; } if (!state) { /* If no state, release the lock taken above and return * status. If success, we haven't done any permission * check so ask the caller to do so. */ PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); *caller_perm_check = !FSAL_IS_ERROR(status); return status; } if (!FSAL_IS_ERROR(status)) { /* Return success. We haven't done any permission * check so ask the caller to do so. */ *caller_perm_check = true; return status; } /* close on error */ (void) rgw_close(export->rgw_fs, handle->rgw_fh, RGW_CLOSE_FLAG_NONE); 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->obj_lock); update_share_counters(&handle->share, openflags, FSAL_O_CLOSED); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /* !name */ /* 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. */ if (createmode == FSAL_NO_CREATE) { /* Non creation case, librgw doesn't have open by name so we * have to do a lookup and then handle as an open by handle. */ struct fsal_obj_handle *temp = NULL; /* We don't have open by name... */ status = obj_hdl->obj_ops.lookup(obj_hdl, name, &temp, NULL); if (FSAL_IS_ERROR(status)) { LogFullDebug(COMPONENT_FSAL, "lookup returned %s", fsal_err_txt(status)); return status; } /* Now call ourselves without name and attributes to open. */ status = obj_hdl->obj_ops.open2(temp, state, openflags, FSAL_NO_CREATE, NULL, NULL, verifier, new_obj, attrs_out, caller_perm_check); if (FSAL_IS_ERROR(status)) { /* Release the object we found by lookup. */ temp->obj_ops.release(temp); LogFullDebug(COMPONENT_FSAL, "open returned %s", fsal_err_txt(status)); } return status; } /* Now add in O_CREAT and O_EXCL. * Even with FSAL_UNGUARDED we try exclusive create first so * we can safely set attributes. */ if (createmode != FSAL_NO_CREATE) { posix_flags |= O_CREAT; if (createmode >= FSAL_GUARDED || setattrs) posix_flags |= O_EXCL; } if (setattrs && FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_MODE)) { 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->valid_mask, ATTR_MODE); } else { /* Default to mode 0600 */ unix_mode = 0600; } memset(&st, 0, sizeof(struct stat)); /* XXX needed? */ st.st_uid = op_ctx->creds->caller_uid; st.st_gid = op_ctx->creds->caller_gid; st.st_mode = unix_mode; uint32_t create_mask = RGW_SETATTR_UID | RGW_SETATTR_GID | RGW_SETATTR_MODE; rc = rgw_create(export->rgw_fs, handle->rgw_fh, name, &st, create_mask, &rgw_fh, posix_flags, RGW_CREATE_FLAG_NONE); if (rc < 0) { LogFullDebug(COMPONENT_FSAL, "Create %s failed with %s", name, strerror(-rc)); } /* XXX won't get here, but maybe someday */ if (rc == -EEXIST && createmode == FSAL_UNCHECKED) { /* We tried to create O_EXCL to set attributes and failed. * Remove O_EXCL and retry, also remember not to set attributes. * We still try O_CREAT again just in case file disappears out * from under us. */ posix_flags &= ~O_EXCL; rc = rgw_create(export->rgw_fs, handle->rgw_fh, name, &st, create_mask, &rgw_fh, posix_flags, RGW_CREATE_FLAG_NONE); if (rc < 0) { LogFullDebug(COMPONENT_FSAL, "Non-exclusive Create %s failed with %s", name, strerror(-rc)); } } if (rc < 0) { return rgw2fsal_error(rc); } /* 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. * Since we were able to do the permission check even if we were not * creating the file, let the caller know the permission check has * already been done. Note it IS possible in the case of a race between * an UNCHECKED open and an external unlink, we did create the file. */ created = (posix_flags & O_EXCL) != 0; *caller_perm_check = false; construct_handle(export, rgw_fh, &st, &obj); /* here FSAL_CEPH operates on its (for RGW non-existent) global * fd */ #if 0 /* 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->fd; my_fd->fd = fd; #endif handle->openflags = openflags; *new_obj = &obj->handle; rc = rgw_open(export->rgw_fs, rgw_fh, posix_flags, (!state) ? RGW_OPEN_FLAG_V3 : RGW_OPEN_FLAG_NONE); if (rc < 0) { goto fileerr; } if (created && setattrs && attrib_set->valid_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. */ status = (*new_obj)->obj_ops.setattr2(*new_obj, false, state, attrib_set); if (FSAL_IS_ERROR(status)) goto fileerr; if (attrs_out != NULL) { status = (*new_obj)->obj_ops.getattrs(*new_obj, attrs_out); if (FSAL_IS_ERROR(status) && (attrs_out->request_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_all(&st, attrs_out); } 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)->obj_lock); /* Take the share reservation now by updating the counters. */ update_share_counters(&obj->share, FSAL_O_CLOSED, openflags); PTHREAD_RWLOCK_unlock(&(*new_obj)->obj_lock); } return fsalstat(ERR_FSAL_NO_ERROR, 0); fileerr: /* Close the file we just opened. */ (void) rgw_close(export->rgw_fs, obj->rgw_fh, RGW_CLOSE_FLAG_NONE); if (created) { /* Remove the file we just created */ (void) rgw_unlink(export->rgw_fs, obj->rgw_fh, name, RGW_UNLINK_FLAG_NONE); } /* Release the handle we just allocated. */ (*new_obj)->obj_ops.release(*new_obj); *new_obj = NULL; return status; } /** * @brief Return open status of a state. * * This function returns open flags representing the current open * status for a state. The state_lock must be held. * * @param[in] obj_hdl File on which to operate * @param[in] state File state to interrogate * * @retval Flags representing current open status */ fsal_openflags_t rgw_fsal_status2(struct fsal_obj_handle *obj_hdl, struct state_t *state) { struct rgw_handle *handle = container_of(obj_hdl, struct rgw_handle, handle); /* normal FSALs recover open state in "state" */ return handle->openflags; } /** * @brief Re-open a file that may be already opened * * This function supports changing the access mode of a share reservation and * thus should only be called with a share state. The state_lock must be held. * * This MAY be used to open a file the first time if there is no need for * open by name or create semantics. One example would be 9P lopen. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * @param[in] openflags Mode for re-open * * @return FSAL status. */ fsal_status_t rgw_fsal_reopen2(struct fsal_obj_handle *obj_hdl, struct state_t *state, fsal_openflags_t openflags) { fsal_status_t status = {0, 0}; int posix_flags = 0; fsal_openflags_t old_openflags; struct rgw_open_state *open_state = NULL; struct rgw_export *export = container_of(op_ctx->fsal_export, struct rgw_export, export); struct rgw_handle *handle = container_of(obj_hdl, struct rgw_handle, handle); LogFullDebug(COMPONENT_FSAL, "%s enter obj_hdl %p state %p", __func__, obj_hdl, open_state); /* RGW fsal does not permit concurrent opens, so openflags * are recovered from handle */ if (state) { /* a conceptual open state exists */ open_state = (struct rgw_open_state *) state; LogFullDebug(COMPONENT_FSAL, "%s called w/open_state %p", __func__, open_state); } fsal2posix_openflags(openflags, &posix_flags); /* This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->obj_lock); old_openflags = handle->openflags; /* We can conflict with old share, so go ahead and check now. */ status = check_share_conflict(&handle->share, openflags, false); if (FSAL_IS_ERROR(status)) { PTHREAD_RWLOCK_unlock(&obj_hdl->obj_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(&handle->share, old_openflags, openflags); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); /* perform a provider open iff not already open */ if (true) { /* XXX also, how do we know the ULP tracks opens? * 9P does, V3 does not */ int rc = rgw_open(export->rgw_fs, handle->rgw_fh, posix_flags, (!state) ? RGW_OPEN_FLAG_V3 : RGW_OPEN_FLAG_NONE); if (rc < 0) { /* 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->obj_lock); update_share_counters(&handle->share, openflags, old_openflags); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); } status = rgw2fsal_error(rc); } return status; } /** * @brief Read data from a file * * This function reads data from the given file. The FSAL must be able to * perform the read whether a state is presented or not. This function also * is expected to handle properly bypassing or not share reservations. * * @param[in] obj_hdl File on which to operate * @param[in] bypass If state doesn't indicate a share reservation, * bypass any deny read * @param[in] state state_t to use for this operation * @param[in] offset Position from which to read * @param[in] buffer_size Amount of data to read * @param[out] buffer Buffer to which data are to be copied * @param[out] read_amount Amount of data read * @param[out] end_of_file true if the end of file has been reached * @param[in,out] info more information about the data * * @return FSAL status. */ fsal_status_t rgw_fsal_read2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buffer_size, void *buffer, size_t *read_amount, bool *end_of_file, struct io_info *info) { struct rgw_export *export = container_of(op_ctx->fsal_export, struct rgw_export, export); struct rgw_handle *handle = container_of(obj_hdl, struct rgw_handle, handle); LogFullDebug(COMPONENT_FSAL, "%s enter obj_hdl %p state %p", __func__, obj_hdl, state); if (info != NULL) { /* Currently we don't support READ_PLUS */ return fsalstat(ERR_FSAL_NOTSUPP, 0); } /* RGW does not support a file descriptor abstraction--so * reads are handle based */ int rc = rgw_read(export->rgw_fs, handle->rgw_fh, offset, buffer_size, read_amount, buffer, RGW_READ_FLAG_NONE); if (rc < 0) return rgw2fsal_error(rc); *end_of_file = (read_amount == 0); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Write data to a file * * This function writes data to a file. The FSAL must be able to * perform the write whether a state is presented or not. This function also * is expected to handle properly bypassing or not share reservations. Even * with bypass == true, it will enforce a mandatory (NFSv4) deny_write if * an appropriate state is not passed). * * The FSAL is expected to enforce sync if necessary. * * @param[in] obj_hdl File on which to operate * @param[in] bypass If state doesn't indicate a share reservation, * bypass any non-mandatory deny write * @param[in] state state_t to use for this operation * @param[in] offset Position at which to write * @param[in] buffer Data to be written * @param[in,out] fsal_stable In, if on, the fsal is requested to write data * to stable store. Out, the fsal reports what * it did. * @param[in,out] info more information about the data * * @return FSAL status. */ fsal_status_t rgw_fsal_write2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buffer_size, void *buffer, size_t *wrote_amount, bool *fsal_stable, struct io_info *info) { struct rgw_export *export = container_of(op_ctx->fsal_export, struct rgw_export, export); struct rgw_handle *handle = container_of(obj_hdl, struct rgw_handle, handle); LogFullDebug(COMPONENT_FSAL, "%s enter obj_hdl %p state %p", __func__, obj_hdl, state); if (info != NULL) { /* Currently we don't support WRITE_PLUS */ return fsalstat(ERR_FSAL_NOTSUPP, 0); } /* XXX note no call to fsal_find_fd (or wrapper) */ int rc = rgw_write(export->rgw_fs, handle->rgw_fh, offset, buffer_size, wrote_amount, buffer, (!state) ? RGW_OPEN_FLAG_V3 : RGW_OPEN_FLAG_NONE); LogFullDebug(COMPONENT_FSAL, "%s post obj_hdl %p state %p returned %d", __func__, obj_hdl, state, rc); if (rc < 0) return rgw2fsal_error(rc); if (*fsal_stable) { rc = rgw_fsync(export->rgw_fs, handle->rgw_fh, RGW_WRITE_FLAG_NONE); if (rc < 0) return rgw2fsal_error(rc); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Commit written data * * This function flushes possibly buffered data to a file. This method * differs from commit due to the need to interact with share reservations * and the fact that the FSAL manages the state of "file descriptors". The * FSAL must be able to perform this operation without being passed a specific * state. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * @param[in] offset Start of range to commit * @param[in] len Length of range to commit * * @return FSAL status. */ fsal_status_t rgw_fsal_commit2(struct fsal_obj_handle *obj_hdl, off_t offset, size_t length) { int rc; struct rgw_export *export = container_of(op_ctx->fsal_export, struct rgw_export, export); struct rgw_handle *handle = container_of(obj_hdl, struct rgw_handle, handle); LogFullDebug(COMPONENT_FSAL, "%s enter obj_hdl %p offset %"PRIx64" length %zx", __func__, obj_hdl, (uint64_t) offset, length); rc = rgw_commit(export->rgw_fs, handle->rgw_fh, offset, length, RGW_FSYNC_FLAG_NONE); if (rc < 0) return rgw2fsal_error(rc); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Allocate a state_t structure * * Note that this is not expected to fail since memory allocation is * expected to abort on failure. * * @param[in] exp_hdl Export state_t will be associated with * @param[in] state_type Type of state to allocate * @param[in] related_state Related state if appropriate * * @returns a state structure. */ struct state_t *rgw_alloc_state(struct fsal_export *exp_hdl, enum state_type state_type, struct state_t *related_state) { return init_state(gsh_calloc(1, sizeof(struct rgw_open_state)), exp_hdl, state_type, related_state); } /** * @brief Manage closing a file when a state is no longer needed. * * When the upper layers are ready to dispense with a state, this method is * called to allow the FSAL to close any file descriptors or release any other * resources associated with the state. A call to free_state should be assumed * to follow soon. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * * @return FSAL status. */ fsal_status_t rgw_fsal_close2(struct fsal_obj_handle *obj_hdl, struct state_t *state) { int rc; struct rgw_open_state *open_state; struct rgw_export *export = container_of(op_ctx->fsal_export, struct rgw_export, export); struct rgw_handle *handle = container_of(obj_hdl, struct rgw_handle, handle); LogFullDebug(COMPONENT_FSAL, "%s enter obj_hdl %p state %p", __func__, obj_hdl, state); if (state) { open_state = (struct rgw_open_state *) state; LogFullDebug(COMPONENT_FSAL, "%s called w/open_state %p", __func__, open_state); if (state->state_type == STATE_TYPE_SHARE || state->state_type == STATE_TYPE_NLM_SHARE || state->state_type == STATE_TYPE_9P_FID) { /* This is a share state, we must update the share * counters. This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->obj_lock); update_share_counters(&handle->share, handle->openflags, FSAL_O_CLOSED); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); } } else if (handle->openflags == FSAL_O_CLOSED) { return fsalstat(ERR_FSAL_NOT_OPENED, 0); } rc = rgw_close(export->rgw_fs, handle->rgw_fh, RGW_CLOSE_FLAG_NONE); if (rc < 0) return rgw2fsal_error(rc); handle->openflags = FSAL_O_CLOSED; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Close the global FD for a file * * This function closes a file, freeing resources used for read/write * access and releasing capabilities. * * @param[in] handle_pub File to close * * @return FSAL status. */ static fsal_status_t rgw_fsal_close(struct fsal_obj_handle *handle_pub) { return rgw_fsal_close2(handle_pub, NULL); } /** * @brief Write wire handle * * This function writes a 'wire' handle to be sent to clients and * received from the. * * @param[in] obj_hdl Handle to digest * @param[in] output_type Type of digest requested * @param[in,out] fh_desc Location/size of buffer for * digest/Length modified to digest length * * @return FSAL status. */ static fsal_status_t handle_to_wire(const struct fsal_obj_handle *obj_hdl, uint32_t output_type, struct gsh_buffdesc *fh_desc) { /* The private 'full' object handle */ const struct rgw_handle *handle = container_of(obj_hdl, const struct rgw_handle, handle); switch (output_type) { /* Digested Handles */ case FSAL_DIGEST_NFSV3: case FSAL_DIGEST_NFSV4: if (fh_desc->len < sizeof(struct rgw_fh_hk)) { LogMajor(COMPONENT_FSAL, "RGW digest_handle: space too small for handle. Need %zu, have %zu", sizeof(handle->rgw_fh), fh_desc->len); return fsalstat(ERR_FSAL_TOOSMALL, 0); } else { memcpy(fh_desc->addr, &(handle->rgw_fh->fh_hk), sizeof(struct rgw_fh_hk)); fh_desc->len = sizeof(struct rgw_fh_hk); } break; default: return fsalstat(ERR_FSAL_SERVERFAULT, 0); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Give a hash key for file handle * * This function locates a unique hash key for a given file. * * @param[in] obj_hdl The file whose key is to be found * @param[out] fh_desc Address and length of key */ static void handle_to_key(struct fsal_obj_handle *obj_hdl, struct gsh_buffdesc *fh_desc) { /* The private 'full' object handle */ struct rgw_handle *handle = container_of(obj_hdl, struct rgw_handle, handle); fh_desc->addr = &(handle->rgw_fh->fh_hk); fh_desc->len = sizeof(struct rgw_fh_hk); } /** * @brief Override functions in ops vector * * This function overrides implemented functions in the ops vector * with versions for this FSAL. * * @param[in] ops Handle operations vector */ void handle_ops_init(struct fsal_obj_ops *ops) { ops->release = release; ops->merge = rgw_merge; ops->lookup = lookup; ops->mkdir = rgw_fsal_mkdir; ops->readdir = rgw_fsal_readdir; #if HAVE_DIRENT_OFFSETOF ops->compute_readdir_cookie = rgw_fsal_compute_cookie; #endif ops->dirent_cmp = rgw_fsal_dirent_cmp; ops->getattrs = getattrs; ops->rename = rgw_fsal_rename; ops->unlink = rgw_fsal_unlink; ops->close = rgw_fsal_close; ops->handle_to_wire = handle_to_wire; ops->handle_to_key = handle_to_key; ops->open2 = rgw_fsal_open2; ops->status2 = rgw_fsal_status2; ops->reopen2 = rgw_fsal_reopen2; ops->read2 = rgw_fsal_read2; ops->write2 = rgw_fsal_write2; ops->commit2 = rgw_fsal_commit2; ops->setattr2 = rgw_fsal_setattr2; ops->close2 = rgw_fsal_close2; } nfs-ganesha-2.6.0/src/FSAL/FSAL_RGW/internal.c000066400000000000000000000113571324272410200204550ustar00rootroot00000000000000/* * Copyright © Red Hat, 2015 * Author: ORit Wasserman * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @file internal.c * @brief Internal definitions for the RGW FSAL * * This file includes internal function definitions, constants, and * variable declarations used to impelment the RGW FSAL, but not * exposed as part of the API. */ #include #include "fsal_types.h" #include "fsal.h" #include "fsal_convert.h" #include "FSAL/fsal_commonlib.h" #define RGW_INTERNAL_C #include "internal.h" struct rgw_fsal_module RGWFSM; /** * @brief FSAL status from RGW error * * This function returns a fsal_status_t with the FSAL error as the * major, and the posix error as minor. (RGW's error codes are just * negative signed versions of POSIX error codes.) * * @param[in] rgw_errorcode RGW error (negative Posix) * * @return FSAL status. */ fsal_status_t rgw2fsal_error(const int rgw_errorcode) { fsal_status_t status; status.minor = -rgw_errorcode; switch (-rgw_errorcode) { case 0: status.major = ERR_FSAL_NO_ERROR; break; case EPERM: status.major = ERR_FSAL_PERM; break; case ENOENT: status.major = ERR_FSAL_NOENT; break; case ECONNREFUSED: case ECONNABORTED: case ECONNRESET: case EIO: case ENFILE: case EMFILE: case EPIPE: status.major = ERR_FSAL_IO; break; case ENODEV: case ENXIO: status.major = ERR_FSAL_NXIO; break; case EBADF: /** * @todo: The EBADF error also happens when file is * opened for reading, and we try writting in * it. In this case, we return * ERR_FSAL_NOT_OPENED, but it doesn't seems to * be a correct error translation. */ status.major = ERR_FSAL_NOT_OPENED; break; case ENOMEM: status.major = ERR_FSAL_NOMEM; break; case EACCES: status.major = ERR_FSAL_ACCESS; break; case EFAULT: status.major = ERR_FSAL_FAULT; break; case EEXIST: status.major = ERR_FSAL_EXIST; break; case EXDEV: status.major = ERR_FSAL_XDEV; break; case ENOTDIR: status.major = ERR_FSAL_NOTDIR; break; case EISDIR: status.major = ERR_FSAL_ISDIR; break; case EINVAL: status.major = ERR_FSAL_INVAL; break; case EFBIG: status.major = ERR_FSAL_FBIG; break; case ENOSPC: status.major = ERR_FSAL_NOSPC; break; case EMLINK: status.major = ERR_FSAL_MLINK; break; case EDQUOT: status.major = ERR_FSAL_DQUOT; break; case ENAMETOOLONG: status.major = ERR_FSAL_NAMETOOLONG; break; case ENOTEMPTY: status.major = ERR_FSAL_NOTEMPTY; break; case ESTALE: status.major = ERR_FSAL_STALE; break; case EAGAIN: case EBUSY: status.major = ERR_FSAL_DELAY; break; default: status.major = ERR_FSAL_SERVERFAULT; break; } return status; } /** * @brief Construct a new filehandle * * This function constructs a new RGW FSAL object handle and attaches * it to the export. After this call the attributes have been filled * in and the handle is up-to-date and usable. * * @param[in] export Export on which the object lives * @param[in] rgw_fh Concise representation of the object name, * in RGW notation * @param[inout] st Object attributes * @param[out] obj Object created * * @return 0 on success, negative error codes on failure. */ int construct_handle(struct rgw_export *export, struct rgw_file_handle *rgw_fh, struct stat *st, struct rgw_handle **obj) { /* Poitner to the handle under construction */ struct rgw_handle *constructing = NULL; *obj = NULL; constructing = gsh_calloc(1, sizeof(struct rgw_handle)); constructing->rgw_fh = rgw_fh; constructing->up_ops = export->export.up_ops; /* XXXX going away */ fsal_obj_handle_init(&constructing->handle, &export->export, posix2fsal_type(st->st_mode)); handle_ops_init(&constructing->handle.obj_ops); constructing->handle.fsid = posix2fsal_fsid(st->st_dev); constructing->handle.fileid = st->st_ino; constructing->export = export; *obj = constructing; return 0; } void deconstruct_handle(struct rgw_handle *obj) { fsal_obj_handle_fini(&obj->handle); gsh_free(obj); } nfs-ganesha-2.6.0/src/FSAL/FSAL_RGW/internal.h000066400000000000000000000101661324272410200204570ustar00rootroot00000000000000/* * Copyright © Red Hat 2015 * Author: Orit Wasserman * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @file internal.h * @brief Internal declarations for the RGW FSAL * * This file includes declarations of data types, functions, * variables, and constants for the RGW FSAL. */ #ifndef FSAL_RGW_INTERNAL_INTERNAL #define FSAL_RGW_INTERNAL_INTERNAL #include #include #include /* NAME_MAX */ #include "fsal.h" #include "fsal_types.h" #include "fsal_api.h" #include "fsal_convert.h" #include "sal_data.h" #include #include #if ((LIBRGW_FILE_VER_MAJOR != 1) || (LIBRGW_FILE_VER_MINOR < 1) || \ (LIBRGW_FILE_VER_EXTRA < 2)) #error rados/rgw_file.h version unsupported (require >= 1.1.1) #endif /** * RGW Main (global) module object */ struct rgw_fsal_module { struct fsal_module fsal; fsal_staticfsinfo_t fs_info; char *conf_path; char *name; char *cluster; char *init_args; librgw_t rgw; }; extern struct rgw_fsal_module RGWFSM; #define MAXUIDLEN 32 #define MAXKEYLEN 20 #define MAXSECRETLEN 40 /** * RGW internal export object */ struct rgw_export { struct fsal_export export; /*< The public export object */ struct rgw_fs *rgw_fs; /*< "Opaque" fs handle */ struct rgw_handle *root; /*< root handle */ char *rgw_name; char *rgw_user_id; char *rgw_access_key_id; char *rgw_secret_access_key; }; /** * The RGW FSAL internal handle */ struct rgw_handle { struct fsal_obj_handle handle; /*< The public handle */ struct rgw_file_handle *rgw_fh; /*< RGW-internal file handle */ /* XXXX remove ptr to up-ops--we can always follow export! */ const struct fsal_up_vector *up_ops; /*< Upcall operations */ struct rgw_export *export; /*< The first export this handle *< belongs to */ struct fsal_share share; fsal_openflags_t openflags; }; /** * RGW "file descriptor" */ struct rgw_open_state { struct state_t gsh_open; uint32_t flags; }; /** * The attributes this FSAL can interpret or supply. * Currently FSAL_RGW uses posix2fsal_attributes, so we should indicate support * for at least those attributes. */ #define RGW_SUPPORTED_ATTRIBUTES ((const attrmask_t) (ATTRS_POSIX)) /** * The attributes this FSAL can set. */ #define RGW_SETTABLE_ATTRIBUTES ((const attrmask_t) ( \ ATTR_MODE | ATTR_OWNER | ATTR_GROUP | ATTR_ATIME |\ ATTR_CTIME | ATTR_MTIME | ATTR_SIZE | ATTR_MTIME_SERVER |\ ATTR_ATIME_SERVER)) /** * Linux supports a stripe pattern with no more than 4096 stripes, but * for now we stick to 1024 to keep them da_addrs from being too * gigantic. */ static const size_t BIGGEST_PATTERN = 1024; static inline fsal_staticfsinfo_t *rgw_staticinfo(struct fsal_module *hdl) { struct rgw_fsal_module *myself = container_of(hdl, struct rgw_fsal_module, fsal); return &myself->fs_info; } /* Prototypes */ int construct_handle(struct rgw_export *export, struct rgw_file_handle *rgw_file_handle, struct stat *st, struct rgw_handle **obj); void deconstruct_handle(struct rgw_handle *obj); fsal_status_t rgw2fsal_error(const int errorcode); void export_ops_init(struct export_ops *ops); void handle_ops_init(struct fsal_obj_ops *ops); struct state_t *rgw_alloc_state(struct fsal_export *exp_hdl, enum state_type state_type, struct state_t *related_state); void rgw_fs_invalidate(void *handle, struct rgw_fh_hk fh_hk); #endif /* !FSAL_RGW_INTERNAL_INTERNAL */ nfs-ganesha-2.6.0/src/FSAL/FSAL_RGW/main.c000066400000000000000000000251101324272410200175550ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Red Hat, 2015 * Author: Orit Wasserman * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* main.c * Module core functions */ #include #include #include "gsh_list.h" #include "fsal.h" #include "fsal_types.h" #include "FSAL/fsal_init.h" #include "FSAL/fsal_commonlib.h" #include "fsal_api.h" #include "internal.h" #include "abstract_mem.h" #include "nfs_exports.h" #include "export_mgr.h" static const char *module_name = "RGW"; #if ((LIBRGW_FILE_VER_MAJOR > 1) || \ ((LIBRGW_FILE_VER_MAJOR == 1) && \ ((LIBRGW_FILE_VER_MINOR > 1) || \ ((LIBRGW_FILE_VER_MINOR == 1) && (LIBRGW_FILE_VER_EXTRA >= 4))))) #define HAVE_DIRENT_OFFSETOF 1 #else #define HAVE_DIRENT_OFFSETOF 0 #endif /* filesystem info for RGW */ static struct fsal_staticfsinfo_t default_rgw_info = { .maxfilesize = UINT64_MAX, .maxlink = _POSIX_LINK_MAX, .maxnamelen = 1024, .maxpathlen = 1024, .no_trunc = true, .chown_restricted = false, .case_insensitive = false, .case_preserving = true, .link_support = false, .symlink_support = false, .lock_support = false, .lock_support_async_block = false, .named_attr = true, /* XXX */ .unique_handles = true, .lease_time = {10, 0}, .acl_support = false, .cansettime = true, .homogenous = true, .supported_attrs = RGW_SUPPORTED_ATTRIBUTES, .maxread = FSAL_MAXIOSIZE, .maxwrite = FSAL_MAXIOSIZE, .umask = 0, .rename_changes_key = true, #if HAVE_DIRENT_OFFSETOF .compute_readdir_cookie = true, #endif .whence_is_name = true, }; static struct config_item rgw_items[] = { CONF_ITEM_PATH("ceph_conf", 1, MAXPATHLEN, NULL, rgw_fsal_module, conf_path), CONF_ITEM_STR("name", 1, MAXPATHLEN, NULL, rgw_fsal_module, name), CONF_ITEM_STR("cluster", 1, MAXPATHLEN, NULL, rgw_fsal_module, cluster), CONF_ITEM_STR("init_args", 1, MAXPATHLEN, NULL, rgw_fsal_module, init_args), CONF_ITEM_MODE("umask", 0, rgw_fsal_module, fs_info.umask), CONF_ITEM_MODE("xattr_access_rights", 0, rgw_fsal_module, fs_info.xattr_access_rights), CONFIG_EOL }; struct config_block rgw_block = { .dbus_interface_name = "org.ganesha.nfsd.config.fsal.rgw", .blk_desc.name = "RGW", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = noop_conf_init, .blk_desc.u.blk.params = rgw_items, .blk_desc.u.blk.commit = noop_conf_commit }; static pthread_mutex_t init_mtx = PTHREAD_MUTEX_INITIALIZER; /* Module methods */ /* init_config * must be called with a reference taken (via lookup_fsal) */ static fsal_status_t init_config(struct fsal_module *module_in, config_file_t config_struct, struct config_error_type *err_type) { struct rgw_fsal_module *myself = container_of(module_in, struct rgw_fsal_module, fsal); LogDebug(COMPONENT_FSAL, "RGW module setup."); myself->fs_info = default_rgw_info; (void) load_config_from_parse(config_struct, &rgw_block, myself, true, err_type); if (!config_error_is_harmless(err_type)) return fsalstat(ERR_FSAL_INVAL, 0); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Create a new export under this FSAL * * This function creates a new export object for the RGW FSAL. * * @todo ACE: We do not handle re-exports of the same cluster in a * sane way. Currently we create multiple handles and cache objects * pointing to the same one. This is not necessarily wrong, but it is * inefficient. It may also not be something we expect to use enough * to care about. * * @param[in] module_in The supplied module handle * @param[in] path The path to export * @param[in] options Export specific options for the FSAL * @param[in,out] list_entry Our entry in the export list * @param[in] next_fsal Next stacked FSAL * @param[out] pub_export Newly created FSAL export object * * @return FSAL status. */ static struct config_item export_params[] = { CONF_ITEM_NOOP("name"), CONF_MAND_STR("user_id", 0, MAXUIDLEN, NULL, rgw_export, rgw_user_id), CONF_MAND_STR("access_key_id", 0, MAXKEYLEN, NULL, rgw_export, rgw_access_key_id), CONF_MAND_STR("secret_access_key", 0, MAXSECRETLEN, NULL, rgw_export, rgw_secret_access_key), CONFIG_EOL }; static struct config_block export_param_block = { .dbus_interface_name = "org.ganesha.nfsd.config.fsal.rgw-export%d", .blk_desc.name = "FSAL", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = noop_conf_init, .blk_desc.u.blk.params = export_params, .blk_desc.u.blk.commit = noop_conf_commit }; static fsal_status_t create_export(struct fsal_module *module_in, void *parse_node, struct config_error_type *err_type, const struct fsal_up_vector *up_ops) { /* The status code to return */ fsal_status_t status = { ERR_FSAL_NO_ERROR, 0 }; /* The internal export object */ struct rgw_export *export = NULL; /* The 'private' root handle */ struct rgw_handle *handle = NULL; /* Stat for root */ struct stat st; /* Return code */ int rc = 0; /* Return code from RGW calls */ int rgw_status; /* True if we have called fsal_export_init */ bool initialized = false; /* once */ if (!RGWFSM.rgw) { PTHREAD_MUTEX_lock(&init_mtx); if (!RGWFSM.rgw) { char *conf_path = NULL; char *inst_name = NULL; char *cluster = NULL; int argc = 1; char *argv[5] = { "nfs-ganesha", NULL, NULL, NULL, NULL }; int clen; if (RGWFSM.conf_path) { clen = strlen(RGWFSM.conf_path) + 8; conf_path = (char *) gsh_malloc(clen); sprintf(conf_path, "--conf=%s", RGWFSM.conf_path); argv[argc] = conf_path; ++argc; } if (RGWFSM.name) { clen = strlen(RGWFSM.name) + 8; inst_name = (char *) gsh_malloc(clen); sprintf(inst_name, "--name=%s", RGWFSM.name); argv[argc] = inst_name; ++argc; } if (RGWFSM.cluster) { clen = strlen(RGWFSM.cluster) + 8; cluster = (char *) gsh_malloc(clen); sprintf(cluster, "--cluster=%s", RGWFSM.cluster); argv[argc] = cluster; ++argc; } if (RGWFSM.init_args) { argv[argc] = RGWFSM.init_args; ++argc; } rc = librgw_create(&RGWFSM.rgw, argc, argv); if (rc != 0) { LogCrit(COMPONENT_FSAL, "RGW module: librgw init failed (%d)", rc); } gsh_free(conf_path); gsh_free(inst_name); gsh_free(cluster); } PTHREAD_MUTEX_unlock(&init_mtx); } if (rc != 0) { status.major = ERR_FSAL_BAD_INIT; goto error; } export = gsh_calloc(1, sizeof(struct rgw_export)); fsal_export_init(&export->export); export_ops_init(&export->export.exp_ops); /* get params for this export, if any */ if (parse_node) { rc = load_config_from_node(parse_node, &export_param_block, export, true, err_type); if (rc != 0) { gsh_free(export); return fsalstat(ERR_FSAL_INVAL, 0); } } initialized = true; #ifndef USE_FSAL_RGW_MOUNT2 rgw_status = rgw_mount(RGWFSM.rgw, export->rgw_user_id, export->rgw_access_key_id, export->rgw_secret_access_key, &export->rgw_fs, RGW_MOUNT_FLAG_NONE); #else rgw_status = rgw_mount2(RGWFSM.rgw, export->rgw_user_id, export->rgw_access_key_id, export->rgw_secret_access_key, op_ctx->ctx_export->fullpath, &export->rgw_fs, RGW_MOUNT_FLAG_NONE); #endif if (rgw_status != 0) { status.major = ERR_FSAL_SERVERFAULT; LogCrit(COMPONENT_FSAL, "Unable to mount RGW cluster for %s.", op_ctx->ctx_export->fullpath); if (rgw_status == -EINVAL) { LogCrit(COMPONENT_FSAL, "Authorization Failed for user %s ", export->rgw_user_id); } goto error; } if (fsal_attach_export(module_in, &export->export.exports) != 0) { status.major = ERR_FSAL_SERVERFAULT; LogCrit(COMPONENT_FSAL, "Unable to attach export for %s.", op_ctx->ctx_export->fullpath); goto error; } if (rgw_register_invalidate(export->rgw_fs, rgw_fs_invalidate, up_ops->up_fsal_export, RGW_REG_INVALIDATE_FLAG_NONE) != 0) { LogCrit(COMPONENT_FSAL, "Unable to register invalidates for %s.", op_ctx->ctx_export->fullpath); goto error; } export->export.fsal = module_in; LogDebug(COMPONENT_FSAL, "RGW module export %s.", op_ctx->ctx_export->fullpath); rc = rgw_getattr(export->rgw_fs, export->rgw_fs->root_fh, &st, RGW_GETATTR_FLAG_NONE); if (rc < 0) return rgw2fsal_error(rc); rc = construct_handle(export, export->rgw_fs->root_fh, &st, &handle); if (rc < 0) { status = rgw2fsal_error(rc); goto error; } op_ctx->fsal_export = &export->export; export->root = handle; export->export.up_ops = up_ops; return status; error: gsh_free(export); if (initialized) initialized = false; return status; } /** * @brief Initialize and register the FSAL * * This function initializes the FSAL module handle, being called * before any configuration or even mounting of a RGW cluster. It * exists solely to produce a properly constructed FSAL module * handle. */ MODULE_INIT void init(void) { struct fsal_module *myself = &RGWFSM.fsal; LogDebug(COMPONENT_FSAL, "RGW module registering."); /* register_fsal seems to expect zeroed memory. */ memset(myself, 0, sizeof(*myself)); if (register_fsal(myself, module_name, FSAL_MAJOR_VERSION, FSAL_MINOR_VERSION, FSAL_ID_RGW) != 0) { /* The register_fsal function prints its own log message if it fails */ LogCrit(COMPONENT_FSAL, "RGW module failed to register."); } /* Set up module operations */ myself->m_ops.create_export = create_export; myself->m_ops.init_config = init_config; } /** * @brief Release FSAL resources * * This function unregisters the FSAL and frees its module handle. The * FSAL also has an open instance of the rgw library, so we also need to * release that. */ MODULE_FINI void finish(void) { int ret; LogDebug(COMPONENT_FSAL, "RGW module finishing."); ret = unregister_fsal(&RGWFSM.fsal); if (ret != 0) { LogCrit(COMPONENT_FSAL, "RGW: unregister_fsal failed (%d)", ret); } /* release the library */ if (RGWFSM.rgw) { librgw_shutdown(RGWFSM.rgw); } } nfs-ganesha-2.6.0/src/FSAL/FSAL_RGW/up.c000066400000000000000000000050351324272410200172610ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright © 2017, Red Hat, Inc. * Author: Matt Benjamin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @file FSAL_RGW/up.c * @author Matt Benjamin * @date Fri Jan 19 18:07:01 2017 * * @brief Upcalls * * Use new generic invalidate hook to drive upcalls. */ #include #include "fsal.h" #include "fsal_types.h" #include "fsal_convert.h" #include "fsal_api.h" #include "internal.h" #include "nfs_exports.h" #include "FSAL/fsal_commonlib.h" /** * @brief Invalidate an inode (dispatch upcall) * * This function terminates an invalidate upcall from librgw. Since * upcalls are asynchronous, no upcall thread is required. * * @param[in] cmount The mount context * @param[in] fh_hk The object being invalidated * @param[in] arg Opaque argument, currently a pointer to export * * @return FSAL status codes. */ void rgw_fs_invalidate(void *handle, struct rgw_fh_hk fh_hk) { struct rgw_export *export = (struct rgw_export *) handle; const struct fsal_up_vector *up_ops; LogFullDebug(COMPONENT_FSAL_UP, "%s: invalidate on fh_hk %" PRIu64 ":%" PRIu64 "\n", __func__, fh_hk.bucket, fh_hk.object); if (!export) { LogMajor(COMPONENT_FSAL_UP, "up/invalidate: called w/nil export"); return; } up_ops = export->export.up_ops; if (!up_ops) { LogMajor(COMPONENT_FSAL_UP, "up/invalidate: nil FSAL_UP ops vector"); return; } fsal_status_t status; struct gsh_buffdesc fh_desc; fh_desc.addr = &fh_hk; fh_desc.len = sizeof(struct rgw_fh_hk); /* invalidate me, my man */ status = up_ops->invalidate(up_ops, &fh_desc, FSAL_UP_INVALIDATE_CACHE); if (FSAL_IS_ERROR(status)) { LogMajor(COMPONENT_FSAL_UP, "up/invalidate: error invalidating fh_hk %" PRIu64 ":%" PRIu64 "\n", fh_hk.bucket, fh_hk.object); } } nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/000077500000000000000000000000001324272410200164655ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/CMakeLists.txt000066400000000000000000000005151324272410200212260ustar00rootroot00000000000000add_definitions( -D__USE_GNU -D_GNU_SOURCE ) set( LIB_PREFIX 64) add_subdirectory(os) add_subdirectory(vfs) if(USE_FSAL_XFS) add_subdirectory(xfs) endif(USE_FSAL_XFS) if(USE_FSAL_PANFS) add_subdirectory(panfs) endif(USE_FSAL_PANFS) ########### next target ############### ########### install files ############### nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/export.c000066400000000000000000000401021324272410200201470ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* export.c * VFS Super-FSAL export object */ #include "config.h" #include "fsal.h" #include /* used for 'dirname' */ #include #include #include #include #include #include #include #include "gsh_list.h" #include "fsal_convert.h" #include "config_parsing.h" #include "FSAL/fsal_commonlib.h" #include "FSAL/fsal_config.h" #include "fsal_handle_syscalls.h" #include "vfs_methods.h" #include "nfs_exports.h" #include "export_mgr.h" #include "subfsal.h" #include "gsh_config.h" /* helpers to/from other VFS objects */ struct fsal_staticfsinfo_t *vfs_staticinfo(struct fsal_module *hdl); int vfs_get_root_fd(struct fsal_export *exp_hdl) { struct vfs_fsal_export *myself; struct vfs_filesystem *my_root_fs; myself = EXPORT_VFS_FROM_FSAL(exp_hdl); my_root_fs = myself->root_fs->private_data; return my_root_fs->root_fd; } /* export object methods */ static void release(struct fsal_export *exp_hdl) { struct vfs_fsal_export *myself; myself = EXPORT_VFS_FROM_FSAL(exp_hdl); if (op_ctx != NULL && op_ctx->ctx_export != NULL) { LogDebug(COMPONENT_FSAL, "Releasing VFS export %"PRIu16 " for %s", exp_hdl->export_id, export_path(op_ctx->ctx_export)); } else { LogDebug(COMPONENT_FSAL, "Releasing VFS export %"PRIu16 " on filesystem %s", exp_hdl->export_id, myself->root_fs->path); } vfs_sub_fini(myself); vfs_unexport_filesystems(myself); fsal_detach_export(exp_hdl->fsal, &exp_hdl->exports); free_export_ops(exp_hdl); gsh_free(myself); /* elvis has left the building */ } static fsal_status_t get_dynamic_info(struct fsal_export *exp_hdl, struct fsal_obj_handle *obj_hdl, fsal_dynamicfsinfo_t *infop) { struct statvfs buffstatvfs; fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; int retval = 0; 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); retval = EXDEV; fsal_error = posix2fsal_error(retval); goto out; } retval = statvfs(obj_hdl->fs->path, &buffstatvfs); if (retval < 0) { fsal_error = posix2fsal_error(errno); retval = errno; goto out; } infop->total_bytes = buffstatvfs.f_frsize * buffstatvfs.f_blocks; infop->free_bytes = buffstatvfs.f_frsize * buffstatvfs.f_bfree; infop->avail_bytes = buffstatvfs.f_frsize * buffstatvfs.f_bavail; infop->total_files = buffstatvfs.f_files; infop->free_files = buffstatvfs.f_ffree; infop->avail_files = buffstatvfs.f_favail; infop->time_delta.tv_sec = 1; infop->time_delta.tv_nsec = 0; out: return fsalstat(fsal_error, retval); } static bool fs_supports(struct fsal_export *exp_hdl, fsal_fsinfo_options_t option) { struct fsal_staticfsinfo_t *info; info = vfs_staticinfo(exp_hdl->fsal); return fsal_supports(info, option); } static uint64_t fs_maxfilesize(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = vfs_staticinfo(exp_hdl->fsal); return fsal_maxfilesize(info); } static uint32_t fs_maxread(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = vfs_staticinfo(exp_hdl->fsal); return fsal_maxread(info); } static uint32_t fs_maxwrite(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = vfs_staticinfo(exp_hdl->fsal); return fsal_maxwrite(info); } static uint32_t fs_maxlink(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = vfs_staticinfo(exp_hdl->fsal); return fsal_maxlink(info); } static uint32_t fs_maxnamelen(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = vfs_staticinfo(exp_hdl->fsal); return fsal_maxnamelen(info); } static uint32_t fs_maxpathlen(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = vfs_staticinfo(exp_hdl->fsal); return fsal_maxpathlen(info); } static struct timespec fs_lease_time(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = vfs_staticinfo(exp_hdl->fsal); return fsal_lease_time(info); } static fsal_aclsupp_t fs_acl_support(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = vfs_staticinfo(exp_hdl->fsal); return fsal_acl_support(info); } static attrmask_t fs_supported_attrs(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = vfs_staticinfo(exp_hdl->fsal); return fsal_supported_attrs(info); } static uint32_t fs_umask(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = vfs_staticinfo(exp_hdl->fsal); return fsal_umask(info); } static uint32_t fs_xattr_access_rights(struct fsal_export *exp_hdl) { struct fsal_staticfsinfo_t *info; info = vfs_staticinfo(exp_hdl->fsal); return fsal_xattr_access_rights(info); } /* get_quota * return quotas for this export. * path could cross a lower mount boundary which could * mask lower mount values with those of the export root * if this is a real issue, we can scan each time with setmntent() * better yet, compare st_dev of the file with st_dev of root_fd. * on linux, can map st_dev -> /proc/partitions name -> /dev/ */ static fsal_status_t get_quota(struct fsal_export *exp_hdl, const char *filepath, int quota_type, int quota_id, fsal_quota_t *pquota) { struct vfs_fsal_export *myself; struct dqblk fs_quota; fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; int retval; int errsv; myself = EXPORT_VFS_FROM_FSAL(exp_hdl); /** @todo if we later have a config to disallow crossmnt, check * that the quota is in the same file system as export. * Otherwise, the fact that the quota path will have * made the longest match means the path MUST be exported * by this export. */ memset((char *)&fs_quota, 0, sizeof(struct dqblk)); fsal_set_credentials(op_ctx->creds); /** @todo need to get the right file system... */ retval = QUOTACTL(QCMD(Q_GETQUOTA, quota_type), myself->root_fs->device, quota_id, (caddr_t) &fs_quota); errsv = errno; fsal_restore_ganesha_credentials(); if (retval < 0) { fsal_error = posix2fsal_error(errsv); retval = errsv; goto out; } pquota->bhardlimit = fs_quota.dqb_bhardlimit; pquota->bsoftlimit = fs_quota.dqb_bsoftlimit; pquota->curblocks = fs_quota.dqb_curspace; pquota->fhardlimit = fs_quota.dqb_ihardlimit; pquota->fsoftlimit = fs_quota.dqb_isoftlimit; pquota->curfiles = fs_quota.dqb_curinodes; pquota->btimeleft = fs_quota.dqb_btime; pquota->ftimeleft = fs_quota.dqb_itime; pquota->bsize = DEV_BSIZE; out: return fsalstat(fsal_error, retval); } /* set_quota * same lower mount restriction applies */ static fsal_status_t set_quota(struct fsal_export *exp_hdl, const char *filepath, int quota_type, int quota_id, fsal_quota_t *pquota, fsal_quota_t *presquota) { struct vfs_fsal_export *myself; struct dqblk fs_quota; fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; int retval; int errsv; myself = EXPORT_VFS_FROM_FSAL(exp_hdl); /** @todo if we later have a config to disallow crossmnt, check * that the quota is in the same file system as export. * Otherwise, the fact that the quota path will have * made the longest match means the path MUST be exported * by this export. */ memset((char *)&fs_quota, 0, sizeof(struct dqblk)); if (pquota->bhardlimit != 0) fs_quota.dqb_bhardlimit = pquota->bhardlimit; if (pquota->bsoftlimit != 0) fs_quota.dqb_bsoftlimit = pquota->bsoftlimit; if (pquota->fhardlimit != 0) fs_quota.dqb_ihardlimit = pquota->fhardlimit; if (pquota->fsoftlimit != 0) fs_quota.dqb_isoftlimit = pquota->fsoftlimit; if (pquota->btimeleft != 0) fs_quota.dqb_btime = pquota->btimeleft; if (pquota->ftimeleft != 0) fs_quota.dqb_itime = pquota->ftimeleft; #ifdef LINUX if (pquota->bhardlimit != 0) fs_quota.dqb_valid |= QIF_BLIMITS; if (pquota->bsoftlimit != 0) fs_quota.dqb_valid |= QIF_BLIMITS; if (pquota->fhardlimit != 0) fs_quota.dqb_valid |= QIF_ILIMITS; if (pquota->btimeleft != 0) fs_quota.dqb_valid |= QIF_BTIME; if (pquota->ftimeleft != 0) fs_quota.dqb_valid |= QIF_ITIME; #endif fsal_set_credentials(op_ctx->creds); /** @todo need to get the right file system... */ retval = QUOTACTL(QCMD(Q_SETQUOTA, quota_type), myself->root_fs->device, quota_id, (caddr_t) &fs_quota); errsv = errno; fsal_restore_ganesha_credentials(); if (retval < 0) { fsal_error = posix2fsal_error(errsv); retval = errsv; goto err; } if (presquota != NULL) return get_quota(exp_hdl, filepath, quota_type, quota_id, presquota); err: return fsalstat(fsal_error, retval); } /* extract a file handle from a buffer. * do verification checks and flag any and all suspicious bits. * Return an updated fh_desc into whatever was passed. The most * common behavior, done here is to just reset the length. * * Setting the length to sizeof(vfs_file_handle_t) coerces all handles * to a value too large for some applications (e.g., ESXi), and much * larger than necessary. (On my Linux system, I'm seeing 12 byte file * handles (EXT4). Since this routine has no idea what the internal * length was, it should not set the value (the length comes from us * anyway, it's up to us to get it right elsewhere). */ static fsal_status_t wire_to_host(struct fsal_export *exp_hdl, fsal_digesttype_t in_type, struct gsh_buffdesc *fh_desc, int flags) { struct fsal_filesystem *fs; bool dummy; vfs_file_handle_t *fh = NULL; vfs_alloc_handle(fh); return vfs_check_handle(exp_hdl, fh_desc, &fs, fh, &dummy); } /* vfs_export_ops_init * overwrite vector entries with the methods that we support */ void vfs_export_ops_init(struct export_ops *ops) { ops->release = release; ops->lookup_path = vfs_lookup_path; ops->wire_to_host = wire_to_host; ops->create_handle = vfs_create_handle; ops->get_fs_dynamic_info = get_dynamic_info; ops->fs_supports = fs_supports; ops->fs_maxfilesize = fs_maxfilesize; ops->fs_maxread = fs_maxread; ops->fs_maxwrite = fs_maxwrite; ops->fs_maxlink = fs_maxlink; ops->fs_maxnamelen = fs_maxnamelen; ops->fs_maxpathlen = fs_maxpathlen; ops->fs_lease_time = fs_lease_time; ops->fs_acl_support = fs_acl_support; ops->fs_supported_attrs = fs_supported_attrs; ops->fs_umask = fs_umask; ops->fs_xattr_access_rights = fs_xattr_access_rights; ops->get_quota = get_quota; ops->set_quota = set_quota; ops->alloc_state = vfs_alloc_state; ops->free_state = vfs_free_state; } void free_vfs_filesystem(struct vfs_filesystem *vfs_fs) { if (vfs_fs->root_fd >= 0) close(vfs_fs->root_fd); gsh_free(vfs_fs); } int vfs_claim_filesystem(struct fsal_filesystem *fs, struct fsal_export *exp) { struct vfs_filesystem *vfs_fs = fs->private_data; int retval; struct vfs_fsal_export *myself; struct vfs_filesystem_export_map *map; myself = EXPORT_VFS_FROM_FSAL(exp); map = gsh_calloc(1, sizeof(*map)); if (fs->fsal != NULL) { vfs_fs = fs->private_data; if (vfs_fs == NULL) { LogCrit(COMPONENT_FSAL, "Something wrong with export, fs %s appears already claimed but doesn't have private data", fs->path); retval = EINVAL; goto errout; } goto already_claimed; } vfs_fs = gsh_calloc(1, sizeof(*vfs_fs)); glist_init(&vfs_fs->exports); vfs_fs->root_fd = -1; vfs_fs->fs = fs; retval = vfs_get_root_handle(vfs_fs, myself); if (retval != 0) { if (retval == ENOTTY) { LogInfo(COMPONENT_FSAL, "file system %s is not exportable with %s", fs->path, exp->fsal->name); retval = ENXIO; } goto errout; } fs->private_data = vfs_fs; already_claimed: /* Now map the file system and export */ map->fs = vfs_fs; map->exp = myself; glist_add_tail(&vfs_fs->exports, &map->on_exports); glist_add_tail(&myself->filesystems, &map->on_filesystems); return 0; errout: gsh_free(map); if (vfs_fs != NULL) free_vfs_filesystem(vfs_fs); return retval; } void vfs_unclaim_filesystem(struct fsal_filesystem *fs) { struct vfs_filesystem *vfs_fs = fs->private_data; struct glist_head *glist, *glistn; struct vfs_filesystem_export_map *map; if (vfs_fs != NULL) { glist_for_each_safe(glist, glistn, &vfs_fs->exports) { map = glist_entry(glist, struct vfs_filesystem_export_map, on_exports); /* Remove this file system from mapping */ glist_del(&map->on_filesystems); glist_del(&map->on_exports); if (map->exp->root_fs == fs) { LogInfo(COMPONENT_FSAL, "Removing root_fs %s from VFS export", fs->path); } /* And free it */ gsh_free(map); } free_vfs_filesystem(vfs_fs); fs->private_data = NULL; } LogInfo(COMPONENT_FSAL, "VFS Unclaiming %s", fs->path); } void vfs_unexport_filesystems(struct vfs_fsal_export *exp) { struct glist_head *glist, *glistn; struct vfs_filesystem_export_map *map; PTHREAD_RWLOCK_wrlock(&fs_lock); glist_for_each_safe(glist, glistn, &exp->filesystems) { map = glist_entry(glist, struct vfs_filesystem_export_map, on_filesystems); /* Remove this export from mapping */ glist_del(&map->on_filesystems); glist_del(&map->on_exports); if (glist_empty(&map->fs->exports)) { LogInfo(COMPONENT_FSAL, "VFS is no longer exporting filesystem %s", map->fs->fs->path); unclaim_fs(map->fs->fs); } /* And free it */ gsh_free(map); } PTHREAD_RWLOCK_unlock(&fs_lock); } /* create_export * Create an export point and return a handle to it to be kept * in the export list. * First lookup the fsal, then create the export and then put the fsal back. * returns the export with one reference taken. */ fsal_status_t vfs_create_export(struct fsal_module *fsal_hdl, void *parse_node, struct config_error_type *err_type, const struct fsal_up_vector *up_ops) { struct vfs_fsal_export *myself; int retval = 0; fsal_status_t fsal_status = {0, 0}; vfs_state_init(); myself = gsh_calloc(1, sizeof(struct vfs_fsal_export)); glist_init(&myself->filesystems); fsal_export_init(&myself->export); vfs_export_ops_init(&myself->export.exp_ops); retval = load_config_from_node(parse_node, vfs_sub_export_param, myself, true, err_type); if (retval != 0) { fsal_status = posix2fsal_status(EINVAL); goto err_free; } myself->export.fsal = fsal_hdl; vfs_sub_init_export_ops(myself, op_ctx->ctx_export->fullpath); retval = fsal_attach_export(fsal_hdl, &myself->export.exports); if (retval != 0) { fsal_status = posix2fsal_status(retval); goto err_free; /* seriously bad */ } retval = resolve_posix_filesystem(op_ctx->ctx_export->fullpath, fsal_hdl, &myself->export, vfs_claim_filesystem, vfs_unclaim_filesystem, &myself->root_fs); if (retval != 0) { LogCrit(COMPONENT_FSAL, "resolve_posix_filesystem(%s) returned %s (%d)", op_ctx->ctx_export->fullpath, strerror(retval), retval); fsal_status = posix2fsal_status(retval); goto err_cleanup; } retval = vfs_sub_init_export(myself); if (retval != 0) { fsal_status = posix2fsal_status(retval); goto err_cleanup; } op_ctx->fsal_export = &myself->export; myself->export.up_ops = up_ops; return fsalstat(ERR_FSAL_NO_ERROR, 0); err_cleanup: vfs_unexport_filesystems(myself); fsal_detach_export(fsal_hdl, &myself->export.exports); err_free: free_export_ops(&myself->export); gsh_free(myself); /* elvis has left the building */ return fsal_status; } nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/file.c000066400000000000000000001601171324272410200175560ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* file.c * File I/O methods for VFS module */ #include "config.h" #include #include "fsal.h" #include "FSAL/access_check.h" #include "fsal_convert.h" #include #include #include "vfs_methods.h" #include "os/subr.h" #include "sal_data.h" fsal_status_t vfs_open_my_fd(struct vfs_fsal_obj_handle *myself, fsal_openflags_t openflags, int posix_flags, struct vfs_fd *my_fd) { int fd; fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; int retval = 0; LogFullDebug(COMPONENT_FSAL, "my_fd->fd = %d openflags = %x, posix_flags = %x", my_fd->fd, openflags, posix_flags); assert(my_fd->fd == -1 && my_fd->openflags == FSAL_O_CLOSED && openflags != 0); LogFullDebug(COMPONENT_FSAL, "openflags = %x, posix_flags = %x", openflags, posix_flags); fd = vfs_fsal_open(myself, posix_flags, &fsal_error); if (fd < 0) { retval = -fd; } else { /* Save the file descriptor, make sure we only save the * open modes that actually represent the open file. */ LogFullDebug(COMPONENT_FSAL, "fd = %d, new openflags = %x", fd, openflags); if (fd == 0) LogCrit(COMPONENT_FSAL, "fd = %d, new openflags = %x", fd, openflags); my_fd->fd = fd; my_fd->openflags = openflags; } return fsalstat(fsal_error, retval); } fsal_status_t vfs_close_my_fd(struct vfs_fd *my_fd) { fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; int retval = 0; if (my_fd->fd >= 0 && my_fd->openflags != FSAL_O_CLOSED) { LogFullDebug(COMPONENT_FSAL, "Closing Opened fd %d", my_fd->fd); retval = close(my_fd->fd); if (retval < 0) { retval = errno; fsal_error = posix2fsal_error(retval); } my_fd->fd = -1; my_fd->openflags = FSAL_O_CLOSED; } return fsalstat(fsal_error, retval); } /** * @brief Function to open an fsal_obj_handle's global file descriptor. * * @param[in] obj_hdl File on which to operate * @param[in] openflags Mode for open * @param[out] fd File descriptor that is to be used * * @return FSAL status. */ static fsal_status_t vfs_open_func(struct fsal_obj_handle *obj_hdl, fsal_openflags_t openflags, struct fsal_fd *fd) { struct vfs_fsal_obj_handle *myself; int posix_flags = 0; myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); fsal2posix_openflags(openflags, &posix_flags); return vfs_open_my_fd(myself, openflags, posix_flags, (struct vfs_fd *)fd); } /** * @brief Function to close an fsal_obj_handle's global file descriptor. * * @param[in] obj_hdl File on which to operate * @param[in] fd File handle to close * * @return FSAL status. */ static fsal_status_t vfs_close_func(struct fsal_obj_handle *obj_hdl, struct fsal_fd *fd) { return vfs_close_my_fd((struct vfs_fd *)fd); } /* vfs_close * Close the file if it is still open. */ fsal_status_t vfs_close(struct fsal_obj_handle *obj_hdl) { struct vfs_fsal_obj_handle *myself; fsal_status_t status; assert(obj_hdl->type == REGULAR_FILE); 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 (myself->u.file.fd.openflags == FSAL_O_CLOSED) return fsalstat(ERR_FSAL_NOT_OPENED, 0); /* Take write lock on object to protect file descriptor. * This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->obj_lock); status = vfs_close_my_fd(&myself->u.file.fd); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /** * @brief Allocate a state_t structure * * Note that this is not expected to fail since memory allocation is * expected to abort on failure. * * @param[in] exp_hdl Export state_t will be associated with * @param[in] state_type Type of state to allocate * @param[in] related_state Related state if appropriate * * @returns a state structure. */ struct state_t *vfs_alloc_state(struct fsal_export *exp_hdl, enum state_type state_type, struct state_t *related_state) { struct state_t *state; struct vfs_fd *my_fd; state = init_state(gsh_calloc(1, sizeof(struct vfs_state_fd)), exp_hdl, state_type, related_state); my_fd = &container_of(state, struct vfs_state_fd, state)->vfs_fd; my_fd->fd = -1; my_fd->openflags = FSAL_O_CLOSED; PTHREAD_RWLOCK_init(&my_fd->fdlock, NULL); return state; } /** * @brief free a vfs_state_fd structure * * @param[in] exp_hdl Export state_t will be associated with * @param[in] state Related state if appropriate * */ void vfs_free_state(struct fsal_export *exp_hdl, struct state_t *state) { struct vfs_state_fd *state_fd = container_of(state, struct vfs_state_fd, state); struct vfs_fd *my_fd = &state_fd->vfs_fd; PTHREAD_RWLOCK_destroy(&my_fd->fdlock); gsh_free(state_fd); } /** * @brief Merge a duplicate handle with an original handle * * This function is used if an upper layer detects that a duplicate * object handle has been created. It allows the FSAL to merge anything * from the duplicate back into the original. * * The caller must release the object (the caller may have to close * files if the merge is unsuccessful). * * @param[in] orig_hdl Original handle * @param[in] dupe_hdl Handle to merge into original * * @return FSAL status. * */ fsal_status_t vfs_merge(struct fsal_obj_handle *orig_hdl, struct fsal_obj_handle *dupe_hdl) { fsal_status_t status = {ERR_FSAL_NO_ERROR, 0}; if (orig_hdl->type == REGULAR_FILE && dupe_hdl->type == REGULAR_FILE) { /* We need to merge the share reservations on this file. * This could result in ERR_FSAL_SHARE_DENIED. */ struct vfs_fsal_obj_handle *orig, *dupe; orig = container_of(orig_hdl, struct vfs_fsal_obj_handle, obj_handle); dupe = container_of(dupe_hdl, struct vfs_fsal_obj_handle, obj_handle); /* This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&orig_hdl->obj_lock); status = merge_share(&orig->u.file.share, &dupe->u.file.share); PTHREAD_RWLOCK_unlock(&orig_hdl->obj_lock); } return status; } static fsal_status_t fetch_attrs(struct vfs_fsal_obj_handle *myself, int my_fd, struct attrlist *attrs) { struct stat stat; int retval = 0; fsal_status_t status = {0, 0}; const char *func = "unknown"; /* Now stat the file as appropriate */ switch (myself->obj_handle.type) { case SOCKET_FILE: case CHARACTER_FILE: case BLOCK_FILE: retval = fstatat(my_fd, myself->u.unopenable.name, &stat, AT_SYMLINK_NOFOLLOW); func = "fstatat"; break; case REGULAR_FILE: retval = fstat(my_fd, &stat); func = "fstat"; break; case SYMBOLIC_LINK: case FIFO_FILE: case DIRECTORY: retval = vfs_stat_by_handle(my_fd, &stat); func = "vfs_stat_by_handle"; break; case NO_FILE_TYPE: case EXTENDED_ATTR: /* Caught during open with EINVAL */ break; } if (retval < 0) { if (errno == ENOENT) retval = ESTALE; else retval = errno; LogDebug(COMPONENT_FSAL, "%s failed with %s", func, strerror(retval)); if (attrs->request_mask & ATTR_RDATTR_ERR) { /* Caller asked for error to be visible. */ attrs->valid_mask = ATTR_RDATTR_ERR; } return fsalstat(posix2fsal_error(retval), retval); } posix2fsal_attributes_all(&stat, attrs); attrs->fsid = myself->obj_handle.fs->fsid; if (myself->sub_ops && myself->sub_ops->getattrs) { status = myself->sub_ops->getattrs(myself, my_fd, attrs->request_mask, attrs); if (FSAL_IS_ERROR(status) && (attrs->request_mask & ATTR_RDATTR_ERR) != 0) { /* Caller asked for error to be visible. */ attrs->valid_mask = ATTR_RDATTR_ERR; } } return status; } static fsal_status_t vfs_open2_by_handle(struct fsal_obj_handle *obj_hdl, struct state_t *state, fsal_openflags_t openflags, enum fsal_create_mode createmode, struct attrlist *attrib_set, fsal_verifier_t verifier, struct attrlist *attrs_out, bool *caller_perm_check) { fsal_status_t status = {0, 0}; struct vfs_fd *my_fd = NULL; int posix_flags = 0; bool truncated; struct vfs_fsal_obj_handle *myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); if (state != NULL) my_fd = &container_of(state, struct vfs_state_fd, state)->vfs_fd; fsal2posix_openflags(openflags, &posix_flags); truncated = (posix_flags & O_TRUNC) != 0; LogFullDebug(COMPONENT_FSAL, truncated ? "Truncate" : "No truncate"); /* This is an open by 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->obj_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->obj_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->obj_lock); } else { /* We need to use the global fd to continue, and take * the lock to protect it. */ my_fd = &myself->u.file.fd; PTHREAD_RWLOCK_wrlock(&obj_hdl->obj_lock); } /* Close if there was any proir fd */ if (my_fd->openflags != FSAL_O_CLOSED) { vfs_close_my_fd(my_fd); } 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->obj_lock); return status; } else { /* Error - need to release the share */ goto undo_share; } } if (createmode >= FSAL_EXCLUSIVE || truncated) { /* Refresh the attributes */ struct attrlist attrs; attrmask_t attrs_mask = ATTR_ATIME | ATTR_MTIME; if (attrs_out) attrs_mask |= attrs_out->request_mask; fsal_prepare_attrs(&attrs, attrs_mask); status = fetch_attrs(myself, my_fd->fd, &attrs); if (FSAL_IS_SUCCESS(status)) { LogFullDebug(COMPONENT_FSAL, "New size = %" PRIx64, attrs.filesize); if (createmode >= FSAL_EXCLUSIVE && createmode != FSAL_EXCLUSIVE_9P && !check_verifier_attrlist(&attrs, verifier)) { /* Verifier didn't match, return EEXIST */ status = fsalstat(posix2fsal_error(EEXIST), EEXIST); } else if (attrs_out) { fsal_copy_attrs(attrs_out, &attrs, true); } } fsal_release_attrs(&attrs); } else if (attrs_out && attrs_out->request_mask & ATTR_RDATTR_ERR) { attrs_out->valid_mask &= ATTR_RDATTR_ERR; } if (state == NULL) { /* If no state, release the lock taken above and return * status. If success, we haven't done any permission * check so ask the caller to do so. */ PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); *caller_perm_check = !FSAL_IS_ERROR(status); return status; } if (!FSAL_IS_ERROR(status)) { /* Return success. We haven't done any permission * check so ask the caller to do so. */ *caller_perm_check = true; 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->obj_lock); update_share_counters(&myself->u.file.share, openflags, FSAL_O_CLOSED); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /** * @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. * * The caller is expected to invoke fsal_release_attrs to release any * resources held by the set attributes. The FSAL layer MAY have added an * inherited ACL. * * The mask should be set in attrs_out indicating which attributes are * desired. Note that since this implies a new object is created, if * the attributes are not fetched, the fsal_obj_handle itself would not * be able to be created and the whole request will fail. * * The attributes will not be returned if this is an open by object as * opposed to an open by name. * * @note If the file was created, @a new_obj has been ref'd * * @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] attrs_in 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 Optional attributes for newly created object * @param[in,out] caller_perm_check The caller must do a permission check * * @return FSAL status. */ 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 = 0000; 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 created = false; if (state != NULL) my_fd = &container_of(state, struct vfs_state_fd, state)->vfs_fd; 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); if (createmode >= FSAL_EXCLUSIVE) { /* Now fixup attrs for verifier if exclusive create */ set_common_verifier(attrib_set, verifier); } if (name == NULL) { return vfs_open2_by_handle(obj_hdl, state, openflags, createmode, attrib_set, verifier, attrs_out, caller_perm_check); } /* 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->valid_mask, ATTR_MODE); } if (createmode == FSAL_UNCHECKED && (attrib_set->valid_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. */ LogFullDebug(COMPONENT_FSAL, "File %s exists, retrying UNCHECKED create with out O_EXCL", name); 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; } LogFullDebug(COMPONENT_FSAL, "Opened fd=%d for file %s", fd, name); /* 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. */ created = (posix_flags & O_EXCL) != 0; /** @todo FSF: If we are running with ENABLE_VFS_DEBUG_ACL or a * VFS sub-FSAL that supports ACLs but doesn't permission * check using those ACLs during openat, then there may be * permission differences here... * * There are three cases at issue: * 1. If the ACL is more permissive for the caller than * the mode, and the ACLs are not evaluated by openat * then a create might fail when the ACL would allow it. * There's nothing to be done there. Ganesha doesn't * evaluate directory permissions for create. * 2. An UNCHECKED create where the file already exists * and the ACL is more permissive then the mode could * fail. This COULD have been permission checked by * Ganesha... * 3. An UNCHECKED create where the file already exists * and the ACL is less permissive then the mode could * succeed. This COULD have been permission checked by * Ganesha... * * These cases are only relevant for create, since if * create is not in effect, we don't do openat using * the caller's credentials and instead force Ganesha to * perform the permission check. */ /* Do a permission check if we were not attempting to create. If we * were attempting any sort of create, then the openat call was made * with the caller's credentials active and as such was permission * checked. */ *caller_perm_check = createmode == FSAL_NO_CREATE; 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) { LogFullDebug(COMPONENT_FSAL, "Using global fd"); my_fd = &hdl->u.file.fd; } my_fd->fd = fd; my_fd->openflags = openflags; *new_obj = &hdl->obj_handle; if (created && attrib_set->valid_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)) goto fileerr; if (attrs_out != NULL) { status = (*new_obj)->obj_ops.getattrs(*new_obj, attrs_out); if (FSAL_IS_ERROR(status) && (attrs_out->request_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_all(&stat, attrs_out); attrs_out->fsid = myself->obj_handle.fs->fsid; } LogFullDebug(COMPONENT_FSAL, "Closing Opened fd %d", dir_fd); 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)->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)->obj_lock); } return fsalstat(ERR_FSAL_NO_ERROR, 0); fileerr: /* hdl->u.file.fd will be close in obj_ops.release */ if (my_fd == &hdl->u.file.fd) { LogFullDebug(COMPONENT_FSAL, "Closing Opened fd %d", fd); close(fd); } if (*new_obj) { /* Release the handle we just allocated. */ (*new_obj)->obj_ops.release(*new_obj); *new_obj = NULL; } /* Delete the file if we actually created it. */ if (created) unlinkat(dir_fd, name, 0); direrr: LogFullDebug(COMPONENT_FSAL, "Closing Opened fd %d", dir_fd); close(dir_fd); return fsalstat(posix2fsal_error(retval), retval); } /** * @brief Re-open a file that may be already opened * * This function supports changing the access mode of a share reservation and * thus should only be called with a share state. The state_lock must be held. * * This MAY be used to open a file the first time if there is no need for * open by name or create semantics. One example would be 9P lopen. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * @param[in] openflags Mode for re-open * * @return FSAL status. */ 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 = &container_of(state, struct vfs_state_fd, state)->vfs_fd; 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->obj_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->obj_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->obj_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. Make sure no one is using the fd that we are * about to close! */ PTHREAD_RWLOCK_wrlock(&my_share_fd->fdlock); vfs_close_my_fd(my_share_fd); my_share_fd->fd = my_fd->fd; my_share_fd->openflags = my_fd->openflags; PTHREAD_RWLOCK_unlock(&my_share_fd->fdlock); } 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->obj_lock); update_share_counters(&myself->u.file.share, openflags, old_openflags); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); } return status; } 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 *closefd, bool open_for_locks) { struct vfs_fsal_obj_handle *myself; struct vfs_filesystem *vfs_fs; struct vfs_fd temp_fd = { FSAL_O_CLOSED, PTHREAD_RWLOCK_INITIALIZER, -1 }; struct vfs_fd *out_fd = &temp_fd; fsal_status_t status = {ERR_FSAL_NO_ERROR, 0}; int rc, posix_flags; bool reusing_open_state_fd = false; 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; LogFullDebug(COMPONENT_FSAL, "Opened fd=%d for file %p of type %s", rc, myself, object_file_type_to_str(obj_hdl->type)); 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, closefd, open_for_locks, &reusing_open_state_fd); *fd = out_fd->fd; LogFullDebug(COMPONENT_FSAL, "Found fd=%d for file %p of type %s", out_fd->fd, myself, object_file_type_to_str(obj_hdl->type)); 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 %p of type %s", rc, myself, object_file_type_to_str(obj_hdl->type)); *fd = rc; *closefd = true; return status; } /** * @brief Read data from a file * * This function reads data from the given file. The FSAL must be able to * perform the read whether a state is presented or not. This function also * is expected to handle properly bypassing or not share reservations. * * @param[in] obj_hdl File on which to operate * @param[in] bypass If state doesn't indicate a share reservation, * bypass any deny read * @param[in] state state_t to use for this operation * @param[in] offset Position from which to read * @param[in] buffer_size Amount of data to read * @param[out] buffer Buffer to which data are to be copied * @param[out] read_amount Amount of data read * @param[out] end_of_file true if the end of file has been reached * @param[in,out] info more information about the data * * @return FSAL status. */ fsal_status_t vfs_read2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buffer_size, void *buffer, size_t *read_amount, bool *end_of_file, struct io_info *info) { int my_fd = -1; ssize_t nb_read; fsal_status_t status; int retval = 0; bool has_lock = false; bool closefd = false; struct vfs_fd *vfs_fd = NULL; if (info != NULL) { /* Currently we don't support READ_PLUS */ return fsalstat(ERR_FSAL_NOTSUPP, 0); } 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); } /* Acquire state's fdlock to prevent OPEN upgrade closing the * file descriptor while we use it. */ if (state) { vfs_fd = &container_of(state, struct vfs_state_fd, state)->vfs_fd; PTHREAD_RWLOCK_rdlock(&vfs_fd->fdlock); } /* Get a usable file descriptor */ LogFullDebug(COMPONENT_FSAL, "Calling find_fd, state = %p", state); status = find_fd(&my_fd, obj_hdl, bypass, state, FSAL_O_READ, &has_lock, &closefd, false); if (FSAL_IS_ERROR(status)) goto out; nb_read = pread(my_fd, buffer, buffer_size, offset); if (offset == -1 || nb_read == -1) { retval = errno; status = fsalstat(posix2fsal_error(retval), retval); goto out; } *read_amount = nb_read; *end_of_file = (nb_read == 0); #if 0 /** @todo * * Is this all we really need to do to support READ_PLUS? Will anyone * ever get upset that we don't return holes, even for blocks of all * zeroes? * */ if (info != NULL) { info->io_content.what = NFS4_CONTENT_DATA; info->io_content.data.d_offset = offset + nb_read; info->io_content.data.d_data.data_len = nb_read; info->io_content.data.d_data.data_val = buffer; } #endif out: if (closefd) { LogFullDebug(COMPONENT_FSAL, "Closing Opened fd %d", my_fd); close(my_fd); } if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /** * @brief Write data to a file * * This function writes data to a file. The FSAL must be able to * perform the write whether a state is presented or not. This function also * is expected to handle properly bypassing or not share reservations. Even * with bypass == true, it will enforce a mandatory (NFSv4) deny_write if * an appropriate state is not passed). * * The FSAL is expected to enforce sync if necessary. * * @param[in] obj_hdl File on which to operate * @param[in] bypass If state doesn't indicate a share reservation, * bypass any non-mandatory deny write * @param[in] state state_t to use for this operation * @param[in] offset Position at which to write * @param[in] buffer Data to be written * @param[in,out] fsal_stable In, if on, the fsal is requested to write data * to stable store. Out, the fsal reports what * it did. * @param[in,out] info more information about the data * * @return FSAL status. */ fsal_status_t vfs_write2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buffer_size, void *buffer, size_t *wrote_amount, bool *fsal_stable, struct io_info *info) { ssize_t nb_written; fsal_status_t status; int retval = 0; int my_fd = -1; bool has_lock = false; bool closefd = false; fsal_openflags_t openflags = FSAL_O_WRITE; struct vfs_fd *vfs_fd = NULL; if (info != NULL) { /* Currently we don't support WRITE_PLUS */ return fsalstat(ERR_FSAL_NOTSUPP, 0); } 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); } /* Acquire state's fdlock to prevent OPEN upgrade closing the * file descriptor while we use it. */ if (state) { vfs_fd = &container_of(state, struct vfs_state_fd, state)->vfs_fd; PTHREAD_RWLOCK_rdlock(&vfs_fd->fdlock); } /* Get a usable file descriptor */ LogFullDebug(COMPONENT_FSAL, "Calling find_fd, state = %p", state); status = find_fd(&my_fd, obj_hdl, bypass, state, openflags, &has_lock, &closefd, false); if (FSAL_IS_ERROR(status)) { LogDebug(COMPONENT_FSAL, "find_fd failed %s", msg_fsal_err(status.major)); goto out; } fsal_set_credentials(op_ctx->creds); nb_written = pwrite(my_fd, buffer, buffer_size, offset); if (nb_written == -1) { retval = errno; status = fsalstat(posix2fsal_error(retval), retval); goto out; } *wrote_amount = nb_written; if (*fsal_stable) { retval = fsync(my_fd); if (retval == -1) { retval = errno; status = fsalstat(posix2fsal_error(retval), retval); } } out: if (vfs_fd) PTHREAD_RWLOCK_unlock(&vfs_fd->fdlock); if (closefd) { LogFullDebug(COMPONENT_FSAL, "Closing Opened fd %d", my_fd); close(my_fd); } if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); fsal_restore_ganesha_credentials(); return status; } /** * @brief Commit written data * * This function flushes possibly buffered data to a file. This method * differs from commit due to the need to interact with share reservations * and the fact that the FSAL manages the state of "file descriptors". The * FSAL must be able to perform this operation without being passed a specific * state. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * @param[in] offset Start of range to commit * @param[in] len Length of range to commit * * @return FSAL status. */ fsal_status_t vfs_commit2(struct fsal_obj_handle *obj_hdl, off_t offset, size_t len) { struct vfs_fsal_obj_handle *myself; fsal_status_t status; int retval; struct vfs_fd temp_fd = { FSAL_O_CLOSED, PTHREAD_RWLOCK_INITIALIZER, -1 }; struct vfs_fd *out_fd = &temp_fd; bool has_lock = false; bool closefd = false; myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); /* Make sure file is open in appropriate mode. * Do not check share reservation. */ status = fsal_reopen_obj(obj_hdl, false, false, FSAL_O_WRITE, (struct fsal_fd *)&myself->u.file.fd, &myself->u.file.share, vfs_open_func, vfs_close_func, (struct fsal_fd **)&out_fd, &has_lock, &closefd); if (!FSAL_IS_ERROR(status)) { fsal_set_credentials(op_ctx->creds); retval = fsync(out_fd->fd); if (retval == -1) { retval = errno; status = fsalstat(posix2fsal_error(retval), retval); } fsal_restore_ganesha_credentials(); } if (closefd) { LogFullDebug(COMPONENT_FSAL, "Closing Opened fd %d", out_fd->fd); close(out_fd->fd); } if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } #ifdef F_OFD_GETLK /** * @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 closefd = false; bool bypass = false; fsal_openflags_t openflags = FSAL_O_RDWR; struct vfs_fd *vfs_fd = NULL; 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); } /* Acquire state's fdlock to prevent OPEN upgrade closing the * file descriptor while we use it. */ if (state) { vfs_fd = &container_of(state, struct vfs_state_fd, state)->vfs_fd; PTHREAD_RWLOCK_rdlock(&vfs_fd->fdlock); } /* Get a usable file descriptor */ LogFullDebug(COMPONENT_FSAL, "Calling find_fd, state = %p", state); status = find_fd(&my_fd, obj_hdl, bypass, state, openflags, &has_lock, &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 (vfs_fd) PTHREAD_RWLOCK_unlock(&vfs_fd->fdlock); if (closefd) { LogFullDebug(COMPONENT_FSAL, "Closing Opened fd %d", my_fd); close(my_fd); } if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return fsalstat(posix2fsal_error(retval), retval); } #endif /** * @brief Get attributes * * This function freshens the cached attributes stored on the handle. * Since the caller can take the attribute lock and read them off the * public filehandle, they are not copied out. * * @param[in] obj_hdl Object to query * * @return FSAL status. */ fsal_status_t vfs_getattr2(struct fsal_obj_handle *obj_hdl, struct attrlist *attrs) { struct vfs_fsal_obj_handle *myself; fsal_status_t status = {0, 0}; bool has_lock = false; bool closefd = false; int my_fd; 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 getattr for handle belonging to FSAL %s, ignoring", obj_hdl->fsal->name, obj_hdl->fs->fsal != NULL ? obj_hdl->fs->fsal->name : "(none)"); goto out; } /* Get a usable file descriptor (don't need to bypass - FSAL_O_ANY * won't conflict with any share reservation). */ LogFullDebug(COMPONENT_FSAL, "Calling find_fd, state = NULL"); status = find_fd(&my_fd, obj_hdl, false, NULL, FSAL_O_ANY, &has_lock, &closefd, false); LogFullDebug(COMPONENT_FSAL, "Got fd %d closefd = %s", my_fd, closefd ? "true" : "false"); if (FSAL_IS_ERROR(status)) { if (obj_hdl->type == SYMBOLIC_LINK && status.major == ERR_FSAL_PERM) { /* You cannot open_by_handle (XFS on linux) 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. In this case, return the stat we got * at lookup time. If you *really* want to tweek things * like owners, get a modern linux kernel... */ status = fsalstat(ERR_FSAL_NO_ERROR, 0); } goto out; } status = fetch_attrs(myself, my_fd, attrs); out: if (closefd) { LogFullDebug(COMPONENT_FSAL, "Closing Opened fd %d", my_fd); close(my_fd); } if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /** * @brief Set attributes on an object * * This function sets attributes on an object. Which attributes are * set is determined by attrib_set->valid_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 closefd = false; int my_fd; const char *func = "none"; struct vfs_fd *vfs_fd = NULL; /* apply umask, if mode attribute is to be changed */ if (FSAL_TEST_MASK(attrib_set->valid_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->valid_mask, ATTR_MODE) && !FSAL_TEST_MASK(attrib_set->valid_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->valid_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->valid_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; } /* Acquire state's fdlock to prevent OPEN upgrade closing the * file descriptor while we use it. */ if (state) { vfs_fd = &container_of(state, struct vfs_state_fd, state)->vfs_fd; PTHREAD_RWLOCK_rdlock(&vfs_fd->fdlock); } /* Get a usable file descriptor. Share conflict is only possible if * size is being set. */ LogFullDebug(COMPONENT_FSAL, "Calling find_fd, state = %p", state); status = find_fd(&my_fd, obj_hdl, bypass, state, openflags, &has_lock, &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->valid_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->valid_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->valid_mask, ATTR_OWNER | ATTR_GROUP)) { uid_t user = FSAL_TEST_MASK(attrib_set->valid_mask, ATTR_OWNER) ? (int)attrib_set->owner : -1; gid_t group = FSAL_TEST_MASK(attrib_set->valid_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->valid_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->valid_mask, ATTR_ATIME_SERVER)) { timebuf[0].tv_sec = 0; timebuf[0].tv_nsec = UTIME_NOW; } else if (FSAL_TEST_MASK(attrib_set->valid_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->valid_mask, ATTR_MTIME_SERVER)) { timebuf[1].tv_sec = 0; timebuf[1].tv_nsec = UTIME_NOW; } else if (FSAL_TEST_MASK(attrib_set->valid_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->valid_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 (vfs_fd) PTHREAD_RWLOCK_unlock(&vfs_fd->fdlock); if (closefd) { LogFullDebug(COMPONENT_FSAL, "Closing Opened fd %d", my_fd); close(my_fd); } if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); return status; } /** * @brief Manage closing a file when a state is no longer needed. * * When the upper layers are ready to dispense with a state, this method is * called to allow the FSAL to close any file descriptors or release any other * resources associated with the state. A call to free_state should be assumed * to follow soon. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * * @return FSAL status. */ fsal_status_t vfs_close2(struct fsal_obj_handle *obj_hdl, struct state_t *state) { struct vfs_fsal_obj_handle *myself = NULL; struct vfs_fd *my_fd = &container_of(state, struct vfs_state_fd, state)->vfs_fd; myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); if (state->state_type == STATE_TYPE_SHARE || state->state_type == STATE_TYPE_NLM_SHARE || state->state_type == STATE_TYPE_9P_FID) { /* This is a share state, we must update the share counters */ /* This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->obj_lock); update_share_counters(&myself->u.file.share, my_fd->openflags, FSAL_O_CLOSED); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); } return vfs_close_my_fd(my_fd); } nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/handle.c000066400000000000000000001463201324272410200200720ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* handle.c * VFS object (file|dir) handle object */ #include "config.h" #ifdef LINUX #include /* for makedev(3) */ #endif #include /* used for 'dirname' */ #include #include #include #include "gsh_list.h" #include "fsal.h" #include "fsal_convert.h" #include "fsal_handle_syscalls.h" #include "FSAL/fsal_commonlib.h" #include "vfs_methods.h" #include #include "subfsal.h" #include "city.h" #include "nfs_core.h" #include "nfs_proto_tools.h" /* helpers */ int vfs_fsal_open(struct vfs_fsal_obj_handle *hdl, int openflags, fsal_errors_t *fsal_error) { struct vfs_filesystem *vfs_fs = hdl->obj_handle.fs->private_data; return vfs_open_by_handle(vfs_fs, hdl->handle, openflags, fsal_error); } /** * @brief Create a VFS OBJ handle * * @param[in] dirfd FD for dir containing new handle * @param[in] fh VFS FH for new handle * @param[in] fs FileSystem containing new handle * @param[in] stat stat(2) resutls for new handle * @param[in] dir_fh VFS FH for dir containing new handle * @param[in] path Path to new handle * @param[in] exp_hdl Export containing new handle * @return VFS OBJ handle on success, NULL on failure */ struct vfs_fsal_obj_handle *alloc_handle(int dirfd, vfs_file_handle_t *fh, struct fsal_filesystem *fs, struct stat *stat, vfs_file_handle_t *dir_fh, const char *path, struct fsal_export *exp_hdl) { struct vfs_fsal_export *myself = container_of(exp_hdl, struct vfs_fsal_export, export); struct vfs_fsal_obj_handle *hdl; hdl = vfs_sub_alloc_handle(); memcpy(hdl->handle, fh, sizeof(vfs_file_handle_t)); hdl->obj_handle.type = posix2fsal_type(stat->st_mode); hdl->dev = posix2fsal_devt(stat->st_dev); hdl->up_ops = exp_hdl->up_ops; hdl->obj_handle.fs = fs; LogFullDebug(COMPONENT_FSAL, "Creating object %p for file %s of type %s", hdl, path, object_file_type_to_str(hdl->obj_handle.type)); if (hdl->obj_handle.type == REGULAR_FILE) { hdl->u.file.fd.fd = -1; /* no open on this yet */ hdl->u.file.fd.openflags = FSAL_O_CLOSED; } else if (hdl->obj_handle.type == DIRECTORY) { hdl->u.directory.path = NULL; hdl->u.directory.fs_location = NULL; } else if (hdl->obj_handle.type == SYMBOLIC_LINK) { ssize_t retlink; size_t len = stat->st_size + 1; char *link_content = gsh_malloc(len); retlink = vfs_readlink_by_handle(fh, dirfd, path, link_content, len); if (retlink < 0 || retlink == len) goto spcerr; link_content[retlink] = '\0'; hdl->u.symlink.link_content = link_content; hdl->u.symlink.link_size = len; } else if (vfs_unopenable_type(hdl->obj_handle.type)) { /* AF_UNIX sockets, character special, and block special files require craziness */ if (dir_fh == NULL) { int retval; vfs_alloc_handle(dir_fh); retval = vfs_fd_to_handle(dirfd, hdl->obj_handle.fs, fh); if (retval < 0) goto spcerr; } hdl->u.unopenable.dir = gsh_malloc(sizeof(vfs_file_handle_t)); memcpy(hdl->u.unopenable.dir, dir_fh, sizeof(vfs_file_handle_t)); hdl->u.unopenable.name = gsh_strdup(path); } fsal_obj_handle_init(&hdl->obj_handle, exp_hdl, posix2fsal_type(stat->st_mode)); hdl->obj_handle.fsid = fs->fsid; hdl->obj_handle.fileid = stat->st_ino; #ifdef VFS_NO_MDCACHE hdl->obj_handle.state_hdl = vfs_state_locate(&hdl->obj_handle); #endif /* VFS_NO_MDCACHE */ vfs_handle_ops_init(&hdl->obj_handle.obj_ops); if (vfs_sub_init_handle(myself, hdl, path) < 0) goto spcerr; return hdl; spcerr: if (hdl->obj_handle.type == SYMBOLIC_LINK) { gsh_free(hdl->u.symlink.link_content); } else if (vfs_unopenable_type(hdl->obj_handle.type)) { gsh_free(hdl->u.unopenable.name); gsh_free(hdl->u.unopenable.dir); } gsh_free(hdl); /* elvis has left the building */ return NULL; } static fsal_status_t lookup_with_fd(struct vfs_fsal_obj_handle *parent_hdl, int dirfd, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { struct vfs_fsal_obj_handle *hdl; int retval, fd; struct stat stat; vfs_file_handle_t *fh = NULL; fsal_dev_t dev; struct fsal_filesystem *fs; bool xfsal = false; fsal_status_t status; fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; vfs_alloc_handle(fh); retval = fstatat(dirfd, path, &stat, AT_SYMLINK_NOFOLLOW); if (retval < 0) { retval = errno; LogDebug(COMPONENT_FSAL, "Failed to open stat %s: %s", path, msg_fsal_err(posix2fsal_error(retval))); status = posix2fsal_status(retval); return status; } dev = posix2fsal_devt(stat.st_dev); fs = parent_hdl->obj_handle.fs; if ((dev.minor != parent_hdl->dev.minor) || (dev.major != parent_hdl->dev.major)) { /* XDEV */ fs = lookup_dev(&dev); if (fs == NULL) { LogDebug(COMPONENT_FSAL, "Lookup of %s crosses filesystem boundary to unknown file system dev=%" PRIu64".%"PRIu64, path, dev.major, dev.minor); status = fsalstat(ERR_FSAL_XDEV, EXDEV); return status; } if (fs->fsal != parent_hdl->obj_handle.fsal) { xfsal = true; LogDebug(COMPONENT_FSAL, "Lookup of %s crosses filesystem boundary to file system %s into FSAL %s", path, fs->path, fs->fsal != NULL ? fs->fsal->name : "(none)"); } else { LogDebug(COMPONENT_FSAL, "Lookup of %s crosses filesystem boundary to file system %s", path, fs->path); } } if (xfsal || vfs_name_to_handle(dirfd, fs, path, fh) < 0) { retval = errno; if (((retval == ENOTTY) || (retval == EOPNOTSUPP) || (retval == ENOTSUP) || xfsal) && (fs != parent_hdl->obj_handle.fs)) { /* Crossed device into territory not handled by * this FSAL (XFS or VFS). Need to invent a handle. * The made up handle will be JUST the fsid, we * do not expect to see the handle on the wire, and * this handle will not be valid for any form of this * FSAL. */ LogDebug(COMPONENT_FSAL, "vfs_name_to_handle %s, inventing FSAL %s handle for FSAL %s filesystem %s", xfsal ? "skipped" : "failed", parent_hdl->obj_handle.fsal->name, fs->fsal != NULL ? fs->fsal->name : "(none)", path); retval = vfs_encode_dummy_handle(fh, fs); if (retval < 0) { retval = errno; status = posix2fsal_status(retval); return status; } retval = 0; } else { /* Some other error */ status = posix2fsal_status(retval); return status; } } /* allocate an obj_handle and fill it up */ hdl = alloc_handle(dirfd, fh, fs, &stat, parent_hdl->handle, path, op_ctx->fsal_export); if (hdl == NULL) { status = fsalstat(ERR_FSAL_NOMEM, ENOMEM); return status; } if (attrs_out != NULL) { posix2fsal_attributes_all(&stat, attrs_out); } /* if it is a directory and the sticky bit is set * let's look for referral information */ if (hdl->obj_handle.type == DIRECTORY && attrs_out != NULL && is_sticky_bit_set(&hdl->obj_handle, attrs_out) && hdl->obj_handle.fs->private_data != NULL) { caddr_t xattr_content; size_t attrsize = 0; char proclnk[MAXPATHLEN]; char readlink_buf[MAXPATHLEN]; char *spath, *fspath; ssize_t r; uint64 hash; fsal_status_t st; struct vfs_filesystem *vfs_fs = hdl->obj_handle.fs->private_data; /* the real path of the referral directory is needed. * it get's stored in u.directory.path */ fd = vfs_fsal_open(hdl, O_DIRECTORY, &fsal_error); if (fd < 0) { return fsalstat(fsal_error, -fd); } sprintf(proclnk, "/proc/self/fd/%d", fd); r = readlink(proclnk, readlink_buf, MAXPATHLEN - 1); if (r < 0) { fsal_error = posix2fsal_error(errno); r = errno; LogEvent(COMPONENT_FSAL, "failed to readlink"); close(fd); return fsalstat(fsal_error, r); } readlink_buf[r] = '\0'; LogDebug(COMPONENT_FSAL, "fd -> path: %d -> %s", fd, readlink_buf); if (hdl->u.directory.path != NULL) { LogFullDebug(COMPONENT_FSAL, "freeing old directory.path: %s", hdl->u.directory.path); gsh_free(hdl->u.directory.path); } fspath = vfs_fs->fs->path; spath = readlink_buf; /* If Path and Pseudo path are not equal replace path with * pseudo path. */ if (strcmp(op_ctx->ctx_export->fullpath, op_ctx->ctx_export->pseudopath) != 0) { int pseudo_length = strlen( op_ctx->ctx_export->pseudopath); int fullpath_length = strlen( op_ctx->ctx_export->fullpath); char *dirpath = spath + fullpath_length; memcpy(proclnk, op_ctx->ctx_export->pseudopath, pseudo_length); memcpy(proclnk + pseudo_length, dirpath, r - fullpath_length); proclnk[pseudo_length + (r - fullpath_length)] = '\0'; spath = proclnk; } else if (strncmp(path, fspath, strlen(fspath)) == 0) { spath += strlen(fspath); } hdl->u.directory.path = gsh_strdup(spath); /* referral configuration is in a xattr "user.fs_location" * on the directory in the form * server:/path/to/referred/directory. * It gets storeded in u.directory.fs_location */ xattr_content = gsh_calloc(XATTR_BUFFERSIZE, sizeof(char)); st = vfs_getextattr_value_by_name((struct fsal_obj_handle *)hdl, "user.fs_location", xattr_content, XATTR_BUFFERSIZE, &attrsize); if (!FSAL_IS_ERROR(st)) { LogDebug(COMPONENT_FSAL, "user.fs_location: %s", xattr_content); if (hdl->u.directory.fs_location != NULL) { LogFullDebug(COMPONENT_FSAL, "freeing old directory.fs_location: %s", hdl->u.directory.fs_location); gsh_free(hdl->u.directory.fs_location); } hdl->u.directory.fs_location = gsh_strdup(xattr_content); /* on a referral the filesystem id has to change * it get's calculated via a hash from the referral * and stored in the fsid object of the fsal_obj_handle */ hash = CityHash64(xattr_content, attrsize); hdl->obj_handle.fsid.major = hash; hdl->obj_handle.fsid.minor = hash; LogDebug(COMPONENT_NFS_V4, "fsid.major = %"PRIu64", fsid.minor = %"PRIu64, hdl->obj_handle.fsid.major, hdl->obj_handle.fsid.minor); } gsh_free(xattr_content); close(fd); } else { /* reset fsid if the sticky bit is not set, * because a referral was removed */ hdl->obj_handle.fsid = hdl->obj_handle.fs->fsid; } *handle = &hdl->obj_handle; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* handle methods */ /* lookup * deprecated NULL parent && NULL path implies root handle */ static fsal_status_t lookup(struct fsal_obj_handle *parent, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { struct vfs_fsal_obj_handle *parent_hdl; fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; int dirfd; fsal_status_t status; *handle = NULL; /* poison it first */ parent_hdl = container_of(parent, struct vfs_fsal_obj_handle, obj_handle); if (!parent->obj_ops.handle_is(parent, DIRECTORY)) { LogCrit(COMPONENT_FSAL, "Parent handle is not a directory. hdl = 0x%p", parent); return fsalstat(ERR_FSAL_NOTDIR, 0); } if (parent->fsal != parent->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", parent->fsal->name, parent->fs->fsal != NULL ? parent->fs->fsal->name : "(none)"); return fsalstat(ERR_FSAL_XDEV, EXDEV); } dirfd = vfs_fsal_open(parent_hdl, O_PATH | O_NOACCESS, &fsal_error); if (dirfd < 0) { LogDebug(COMPONENT_FSAL, "Failed to open parent: %s", msg_fsal_err(fsal_error)); status = fsalstat(fsal_error, -dirfd); return status; } status = lookup_with_fd(parent_hdl, dirfd, path, handle, attrs_out); close(dirfd); return status; } static fsal_status_t makedir(struct fsal_obj_handle *dir_hdl, const char *name, struct attrlist *attrib, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { struct vfs_fsal_obj_handle *myself, *hdl; int dir_fd; struct stat stat; mode_t unix_mode; fsal_status_t status = {0, 0}; int retval = 0; int flags = O_PATH | O_NOACCESS; #ifdef ENABLE_VFS_DEBUG_ACL struct attrlist attrs; fsal_accessflags_t access_type; #endif /* ENABLE_VFS_DEBUG_ACL */ vfs_file_handle_t *fh = NULL; vfs_alloc_handle(fh); LogDebug(COMPONENT_FSAL, "create %s", name); *handle = NULL; /* poison it */ if (!dir_hdl->obj_ops.handle_is(dir_hdl, DIRECTORY)) { LogCrit(COMPONENT_FSAL, "Parent handle is not a directory. hdl = 0x%p", dir_hdl); return fsalstat(ERR_FSAL_NOTDIR, 0); } myself = container_of(dir_hdl, struct vfs_fsal_obj_handle, obj_handle); if (dir_hdl->fsal != dir_hdl->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", dir_hdl->fsal->name, dir_hdl->fs->fsal != NULL ? dir_hdl->fs->fsal->name : "(none)"); retval = EXDEV; goto hdlerr; } #ifdef ENABLE_VFS_DEBUG_ACL access_type = FSAL_MODE_MASK_SET(FSAL_W_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_ADD_SUBDIRECTORY); status = dir_hdl->obj_ops.test_access(dir_hdl, access_type, NULL, NULL, false); if (FSAL_IS_ERROR(status)) return status; fsal_prepare_attrs(&attrs, ATTR_ACL); status = dir_hdl->obj_ops.getattrs(dir_hdl, &attrs); if (FSAL_IS_ERROR(status)) return status; status.major = fsal_inherit_acls(attrib, attrs.acl, FSAL_ACE_FLAG_DIR_INHERIT); /* Done with the attrs */ fsal_release_attrs(&attrs); if (FSAL_IS_ERROR(status)) return status; #endif /* ENABLE_VFS_DEBUG_ACL */ unix_mode = fsal2unix_mode(attrib->mode) & ~op_ctx->fsal_export->exp_ops.fs_umask(op_ctx->fsal_export); dir_fd = vfs_fsal_open(myself, flags, &status.major); if (dir_fd < 0) { LogFullDebug(COMPONENT_FSAL, "vfs_fsal_open returned %s", strerror(-dir_fd)); return fsalstat(status.major, -dir_fd); } retval = vfs_stat_by_handle(dir_fd, &stat); if (retval < 0) { retval = errno; LogFullDebug(COMPONENT_FSAL, "vfs_stat_by_handle returned %s", strerror(retval)); status = posix2fsal_status(retval); goto direrr; } /* Become the user because we are creating an object in this dir. */ fsal_set_credentials(op_ctx->creds); retval = mkdirat(dir_fd, name, unix_mode); if (retval < 0) { retval = errno; fsal_restore_ganesha_credentials(); LogFullDebug(COMPONENT_FSAL, "mkdirat returned %s", strerror(retval)); status = posix2fsal_status(retval); goto direrr; } fsal_restore_ganesha_credentials(); retval = vfs_name_to_handle(dir_fd, dir_hdl->fs, name, fh); if (retval < 0) { retval = errno; status = posix2fsal_status(retval); goto fileerr; } retval = fstatat(dir_fd, name, &stat, AT_SYMLINK_NOFOLLOW); if (retval < 0) { retval = errno; LogFullDebug(COMPONENT_FSAL, "fstatat returned %s", strerror(retval)); status = posix2fsal_status(retval); goto fileerr; } /* allocate an obj_handle and fill it up */ hdl = alloc_handle(dir_fd, fh, dir_hdl->fs, &stat, myself->handle, name, op_ctx->fsal_export); if (hdl == NULL) { LogFullDebug(COMPONENT_FSAL, "alloc_handle returned %s", strerror(retval)); status = fsalstat(ERR_FSAL_NOMEM, ENOMEM); goto fileerr; } *handle = &hdl->obj_handle; /* We handled the mode above. */ FSAL_UNSET_MASK(attrib->valid_mask, ATTR_MODE); if (attrib->valid_mask) { /* Now per support_ex API, if there are any other attributes * set, go ahead and get them set now. */ status = (*handle)->obj_ops.setattr2(*handle, false, NULL, attrib); if (FSAL_IS_ERROR(status)) { /* Release the handle we just allocated. */ LogFullDebug(COMPONENT_FSAL, "setattr2 status=%s", fsal_err_txt(status)); (*handle)->obj_ops.release(*handle); *handle = NULL; } else if (attrs_out != NULL) { status = (*handle)->obj_ops.getattrs(*handle, attrs_out); if (FSAL_IS_ERROR(status) && (attrs_out->request_mask & ATTR_RDATTR_ERR) == 0) { /* Get attributes failed and caller expected * to get the attributes. */ goto fileerr; } } } else { status.major = ERR_FSAL_NO_ERROR; status.minor = 0; if (attrs_out != NULL) { /* Since we haven't set any attributes other than what * was set on create, just use the stat results we used * to create the fsal_obj_handle. */ posix2fsal_attributes_all(&stat, attrs_out); } } close(dir_fd); return status; fileerr: unlinkat(dir_fd, name, 0); direrr: close(dir_fd); hdlerr: status.major = posix2fsal_error(retval); return fsalstat(status.major, retval); } static fsal_status_t makenode(struct fsal_obj_handle *dir_hdl, const char *name, object_file_type_t nodetype, /* IN */ struct attrlist *attrib, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { struct vfs_fsal_obj_handle *myself, *hdl; int dir_fd = -1; struct stat stat; mode_t unix_mode; fsal_status_t status = {0, 0}; int retval = 0; dev_t unix_dev = 0; int flags = O_PATH | O_NOACCESS; #ifdef ENABLE_VFS_DEBUG_ACL struct attrlist attrs; fsal_accessflags_t access_type; #endif /* ENABLE_VFS_DEBUG_ACL */ vfs_file_handle_t *fh = NULL; vfs_alloc_handle(fh); LogDebug(COMPONENT_FSAL, "create %s", name); *handle = NULL; /* poison it */ if (!dir_hdl->obj_ops.handle_is(dir_hdl, DIRECTORY)) { LogCrit(COMPONENT_FSAL, "Parent handle is not a directory. hdl = 0x%p", dir_hdl); return fsalstat(ERR_FSAL_NOTDIR, 0); } myself = container_of(dir_hdl, struct vfs_fsal_obj_handle, obj_handle); #ifdef ENABLE_VFS_DEBUG_ACL fsal_prepare_attrs(&attrs, ATTR_ACL); status = dir_hdl->obj_ops.getattrs(dir_hdl, &attrs); if (FSAL_IS_ERROR(status)) return status; status.major = fsal_inherit_acls(attrib, 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 (dir_hdl->fsal != dir_hdl->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", dir_hdl->fsal->name, dir_hdl->fs->fsal != NULL ? dir_hdl->fs->fsal->name : "(none)"); retval = EXDEV; goto hdlerr; } #ifdef ENABLE_VFS_DEBUG_ACL access_type = FSAL_MODE_MASK_SET(FSAL_W_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_ADD_FILE); status = dir_hdl->obj_ops.test_access(dir_hdl, access_type, NULL, NULL, false); if (FSAL_IS_ERROR(status)) return status; #endif /* ENABLE_VFS_DEBUG_ACL */ unix_mode = fsal2unix_mode(attrib->mode) & ~op_ctx->fsal_export->exp_ops.fs_umask(op_ctx->fsal_export); switch (nodetype) { case BLOCK_FILE: unix_mode |= S_IFBLK; unix_dev = makedev(attrib->rawdev.major, attrib->rawdev.minor); break; case CHARACTER_FILE: unix_mode |= S_IFCHR; unix_dev = makedev(attrib->rawdev.major, attrib->rawdev.minor); break; case FIFO_FILE: unix_mode |= S_IFIFO; break; case SOCKET_FILE: unix_mode |= S_IFSOCK; break; default: LogMajor(COMPONENT_FSAL, "Invalid node type in FSAL_mknode: %d", nodetype); status.major = ERR_FSAL_INVAL; goto errout; } dir_fd = vfs_fsal_open(myself, flags, &status.major); if (dir_fd < 0) goto errout; retval = vfs_stat_by_handle(dir_fd, &stat); if (retval < 0) { retval = errno; status = posix2fsal_status(retval); goto direrr; } fsal_set_credentials(op_ctx->creds); retval = mknodat(dir_fd, name, unix_mode, unix_dev); if (retval < 0) { retval = errno; fsal_restore_ganesha_credentials(); status = posix2fsal_status(retval); goto direrr; } fsal_restore_ganesha_credentials(); vfs_alloc_handle(fh); retval = vfs_name_to_handle(dir_fd, myself->obj_handle.fs, name, fh); if (retval < 0) { retval = errno; status = posix2fsal_status(retval); goto fileerr; } retval = fstatat(dir_fd, name, &stat, AT_SYMLINK_NOFOLLOW); if (retval < 0) { retval = errno; status = posix2fsal_status(retval); goto fileerr; } /* allocate an obj_handle and fill it up */ hdl = alloc_handle(dir_fd, fh, myself->obj_handle.fs, &stat, myself->handle, name, op_ctx->fsal_export); if (hdl == NULL) { status = fsalstat(ERR_FSAL_NOMEM, ENOMEM); goto fileerr; } *handle = &hdl->obj_handle; /* We handled the mode above. */ FSAL_UNSET_MASK(attrib->valid_mask, ATTR_MODE); if (attrib->valid_mask) { /* Now per support_ex API, if there are any other attributes * set, go ahead and get them set now. */ status = (*handle)->obj_ops.setattr2(*handle, false, NULL, attrib); if (FSAL_IS_ERROR(status)) { /* Release the handle we just allocated. */ (*handle)->obj_ops.release(*handle); *handle = NULL; } else if (attrs_out != NULL) { status = (*handle)->obj_ops.getattrs(*handle, attrs_out); if (FSAL_IS_ERROR(status) && (attrs_out->request_mask & ATTR_RDATTR_ERR) == 0) { /* Get attributes failed and caller expected * to get the attributes. */ goto fileerr; } } } else { status.major = ERR_FSAL_NO_ERROR; status.minor = 0; if (attrs_out != NULL) { /* Since we haven't set any attributes other than what * was set on create, just use the stat results we used * to create the fsal_obj_handle. */ posix2fsal_attributes_all(&stat, attrs_out); } } close(dir_fd); return status; fileerr: unlinkat(dir_fd, name, 0); direrr: close(dir_fd); /* done with parent */ hdlerr: status.major = posix2fsal_error(retval); errout: return fsalstat(status.major, retval); } /** makesymlink * Note that we do not set mode bits on symlinks for Linux/POSIX * They are not really settable in the kernel and are not checked * anyway (default is 0777) because open uses that target's mode */ static fsal_status_t makesymlink(struct fsal_obj_handle *dir_hdl, const char *name, const char *link_path, struct attrlist *attrib, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { struct vfs_fsal_obj_handle *myself, *hdl; int dir_fd = -1; struct stat stat; fsal_status_t status = {0, 0}; int retval = 0; int flags = O_PATH | O_NOACCESS; #ifdef ENABLE_VFS_DEBUG_ACL struct attrlist attrs; fsal_accessflags_t access_type; #endif /* ENABLE_VFS_DEBUG_ACL */ vfs_file_handle_t *fh = NULL; vfs_alloc_handle(fh); LogDebug(COMPONENT_FSAL, "create %s", name); *handle = NULL; /* poison it first */ if (!dir_hdl->obj_ops.handle_is(dir_hdl, DIRECTORY)) { LogCrit(COMPONENT_FSAL, "Parent handle is not a directory. hdl = 0x%p", dir_hdl); return fsalstat(ERR_FSAL_NOTDIR, 0); } myself = container_of(dir_hdl, struct vfs_fsal_obj_handle, obj_handle); if (dir_hdl->fsal != dir_hdl->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", dir_hdl->fsal->name, dir_hdl->fs->fsal != NULL ? dir_hdl->fs->fsal->name : "(none)"); retval = EXDEV; goto hdlerr; } #ifdef ENABLE_VFS_DEBUG_ACL access_type = FSAL_MODE_MASK_SET(FSAL_W_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_ADD_FILE); status = dir_hdl->obj_ops.test_access(dir_hdl, access_type, NULL, NULL, false); if (FSAL_IS_ERROR(status)) return status; fsal_prepare_attrs(&attrs, ATTR_ACL); status = dir_hdl->obj_ops.getattrs(dir_hdl, &attrs); if (FSAL_IS_ERROR(status)) return status; status.major = fsal_inherit_acls(attrib, 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 */ dir_fd = vfs_fsal_open(myself, flags, &status.major); if (dir_fd < 0) return fsalstat(status.major, -dir_fd); flags |= O_NOFOLLOW; /* BSD needs O_NOFOLLOW for * fhopen() of symlinks */ retval = vfs_stat_by_handle(dir_fd, &stat); if (retval < 0) { retval = errno; status = posix2fsal_status(retval); goto direrr; } /* Become the user because we are creating an object in this dir. */ fsal_set_credentials(op_ctx->creds); retval = symlinkat(link_path, dir_fd, name); if (retval < 0) { retval = errno; fsal_restore_ganesha_credentials(); status = posix2fsal_status(retval); goto direrr; } fsal_restore_ganesha_credentials(); retval = vfs_name_to_handle(dir_fd, dir_hdl->fs, name, fh); if (retval < 0) { retval = errno; status = posix2fsal_status(retval); goto linkerr; } /* now get attributes info, * being careful to get the link, not the target */ retval = fstatat(dir_fd, name, &stat, AT_SYMLINK_NOFOLLOW); if (retval < 0) { retval = errno; status = posix2fsal_status(retval); goto linkerr; } /* allocate an obj_handle and fill it up */ hdl = alloc_handle(dir_fd, fh, dir_hdl->fs, &stat, NULL, name, op_ctx->fsal_export); if (hdl == NULL) { status = fsalstat(ERR_FSAL_NOMEM, ENOMEM); goto linkerr; } *handle = &hdl->obj_handle; /* We handled the mode above. */ FSAL_UNSET_MASK(attrib->valid_mask, ATTR_MODE); if (attrib->valid_mask) { /* Now per support_ex API, if there are any other attributes * set, go ahead and get them set now. */ status = (*handle)->obj_ops.setattr2(*handle, false, NULL, attrib); if (FSAL_IS_ERROR(status)) { /* Release the handle we just allocated. */ (*handle)->obj_ops.release(*handle); *handle = NULL; } else if (attrs_out != NULL) { status = (*handle)->obj_ops.getattrs(*handle, attrs_out); if (FSAL_IS_ERROR(status) && (attrs_out->request_mask & ATTR_RDATTR_ERR) == 0) { /* Get attributes failed and caller expected * to get the attributes. */ goto linkerr; } } } else { status.major = ERR_FSAL_NO_ERROR; status.minor = 0; if (attrs_out != NULL) { /* Since we haven't set any attributes other than what * was set on create, just use the stat results we used * to create the fsal_obj_handle. */ posix2fsal_attributes_all(&stat, attrs_out); } } close(dir_fd); return status; linkerr: unlinkat(dir_fd, name, 0); direrr: close(dir_fd); hdlerr: if (retval == ENOENT) status.major = ERR_FSAL_STALE; else status.major = posix2fsal_error(retval); return fsalstat(status.major, retval); } static fsal_status_t readsymlink(struct fsal_obj_handle *obj_hdl, struct gsh_buffdesc *link_content, bool refresh) { struct vfs_fsal_obj_handle *myself = NULL; int retval = 0; fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; if (obj_hdl->type != SYMBOLIC_LINK) { fsal_error = ERR_FSAL_INVAL; goto out; } 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)"); retval = EXDEV; goto hdlerr; } if (refresh) { /* lazy load or LRU'd storage */ retval = vfs_readlink(myself, &fsal_error); if (retval < 0) { retval = -retval; goto hdlerr; } } if (myself->u.symlink.link_content == NULL) { fsal_error = ERR_FSAL_FAULT; /* probably a better error?? */ goto out; } link_content->len = myself->u.symlink.link_size; link_content->addr = gsh_malloc(myself->u.symlink.link_size); memcpy(link_content->addr, myself->u.symlink.link_content, link_content->len); hdlerr: fsal_error = posix2fsal_error(retval); out: return fsalstat(fsal_error, retval); } static fsal_status_t linkfile(struct fsal_obj_handle *obj_hdl, struct fsal_obj_handle *destdir_hdl, const char *name) { struct vfs_fsal_obj_handle *myself, *destdir; int srcfd, destdirfd; int retval = 0; int flags = O_PATH | O_NOACCESS | O_NOFOLLOW; fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; LogFullDebug(COMPONENT_FSAL, "link to %s", name); if (!op_ctx->fsal_export->exp_ops.fs_supports( op_ctx->fsal_export, fso_link_support)) { fsal_error = ERR_FSAL_NOTSUPP; goto out; } 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)"); retval = EXDEV; fsal_error = posix2fsal_error(retval); goto out; } /* Take read lock on object to protect file descriptor. * We only take a read lock because we are not changing the state of * the file descriptor. */ PTHREAD_RWLOCK_rdlock(&obj_hdl->obj_lock); if (obj_hdl->type == REGULAR_FILE && myself->u.file.fd.openflags != FSAL_O_CLOSED) { srcfd = myself->u.file.fd.fd; } else { srcfd = vfs_fsal_open(myself, flags, &fsal_error); if (srcfd < 0) { retval = -srcfd; fsal_error = posix2fsal_error(retval); LogDebug(COMPONENT_FSAL, "open myself returned %d", retval); goto out_unlock; } } destdir = container_of(destdir_hdl, struct vfs_fsal_obj_handle, obj_handle); if (destdir_hdl->fsal != destdir_hdl->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", destdir_hdl->fsal->name, destdir_hdl->fs->fsal != NULL ? destdir_hdl->fs->fsal->name : "(none)"); retval = EXDEV; fsal_error = posix2fsal_error(retval); goto fileerr; } destdirfd = vfs_fsal_open(destdir, flags, &fsal_error); if (destdirfd < 0) { retval = destdirfd; fsal_error = posix2fsal_error(retval); LogDebug(COMPONENT_FSAL, "open destdir returned %d", retval); goto fileerr; } retval = vfs_link_by_handle(myself->handle, srcfd, destdirfd, name); if (retval < 0) { retval = errno; LogFullDebug(COMPONENT_FSAL, "link returned %d", retval); fsal_error = posix2fsal_error(retval); } close(destdirfd); fileerr: if (!(obj_hdl->type == REGULAR_FILE && myself->u.file.fd.fd >= 0)) close(srcfd); out_unlock: PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); out: LogFullDebug(COMPONENT_FSAL, "returning %d, %d", fsal_error, retval); return fsalstat(fsal_error, retval); } #define BUF_SIZE 1024 /** * read_dirents * read the directory and call through the callback function for * each entry. * @param dir_hdl [IN] the directory to read * @param whence [IN] where to start (next) * @param dir_state [IN] pass thru of state to callback * @param cb [IN] callback function * @param eof [OUT] eof marker true == end of dir */ static fsal_status_t read_dirents(struct fsal_obj_handle *dir_hdl, fsal_cookie_t *whence, void *dir_state, fsal_readdir_cb cb, attrmask_t attrmask, bool *eof) { struct vfs_fsal_obj_handle *myself; int dirfd; fsal_status_t status = {0, 0}; int retval = 0; off_t seekloc = 0; off_t baseloc = 0; unsigned int bpos; int nread; struct vfs_dirent dentry, *dentryp = &dentry; char buf[BUF_SIZE]; if (whence != NULL) seekloc = (off_t) *whence; myself = container_of(dir_hdl, struct vfs_fsal_obj_handle, obj_handle); if (dir_hdl->fsal != dir_hdl->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", dir_hdl->fsal->name, dir_hdl->fs->fsal != NULL ? dir_hdl->fs->fsal->name : "(none)"); retval = EXDEV; status = posix2fsal_status(retval); goto out; } dirfd = vfs_fsal_open(myself, O_RDONLY | O_DIRECTORY, &status.major); if (dirfd < 0) { retval = -dirfd; status = posix2fsal_status(retval); goto out; } seekloc = lseek(dirfd, seekloc, SEEK_SET); if (seekloc < 0) { retval = errno; status = posix2fsal_status(retval); goto done; } do { baseloc = seekloc; nread = vfs_readents(dirfd, buf, BUF_SIZE, &seekloc); if (nread < 0) { retval = errno; status = posix2fsal_status(retval); goto done; } if (nread == 0) break; for (bpos = 0; bpos < nread;) { struct fsal_obj_handle *hdl; struct attrlist attrs; enum fsal_dir_result cb_rc; if (!to_vfs_dirent(buf, bpos, dentryp, baseloc) || strcmp(dentryp->vd_name, ".") == 0 || strcmp(dentryp->vd_name, "..") == 0) goto skip; /* must skip '.' and '..' */ fsal_prepare_attrs(&attrs, attrmask); status = lookup_with_fd(myself, dirfd, dentryp->vd_name, &hdl, &attrs); if (FSAL_IS_ERROR(status)) { goto done; } /* callback to cache inode */ cb_rc = cb(dentryp->vd_name, hdl, &attrs, dir_state, (fsal_cookie_t) dentryp->vd_offset); fsal_release_attrs(&attrs); /* Read ahead not supported by this FSAL. */ if (cb_rc >= DIR_READAHEAD) goto done; skip: bpos += dentryp->vd_reclen; } } while (nread > 0); *eof = true; done: close(dirfd); out: return status; } static fsal_status_t renamefile(struct fsal_obj_handle *obj_hdl, struct fsal_obj_handle *olddir_hdl, const char *old_name, struct fsal_obj_handle *newdir_hdl, const char *new_name) { struct vfs_fsal_obj_handle *olddir, *newdir, *obj; int oldfd = -1, newfd = -1; fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; int retval = 0; olddir = container_of(olddir_hdl, struct vfs_fsal_obj_handle, obj_handle); if (olddir_hdl->fsal != olddir_hdl->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", olddir_hdl->fsal->name, olddir_hdl->fs->fsal != NULL ? olddir_hdl->fs->fsal->name : "(none)"); retval = EXDEV; fsal_error = posix2fsal_error(retval); goto out; } oldfd = vfs_fsal_open(olddir, O_PATH | O_NOACCESS, &fsal_error); if (oldfd < 0) { retval = -oldfd; goto out; } newdir = container_of(newdir_hdl, struct vfs_fsal_obj_handle, obj_handle); if (newdir_hdl->fsal != newdir_hdl->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", newdir_hdl->fsal->name, newdir_hdl->fs->fsal != NULL ? newdir_hdl->fs->fsal->name : "(none)"); retval = EXDEV; fsal_error = posix2fsal_error(retval); goto out; } obj = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); newfd = vfs_fsal_open(newdir, O_PATH | O_NOACCESS, &fsal_error); if (newfd < 0) { retval = -newfd; goto out; } /* Become the user because we are creating/removing objects * in these dirs which messes with quotas and perms. */ fsal_set_credentials(op_ctx->creds); retval = renameat(oldfd, old_name, newfd, new_name); if (retval < 0) { retval = errno; fsal_error = posix2fsal_error(retval); } else if (vfs_unopenable_type(obj->obj_handle.type)) { /* A block, char, or socket has been renamed. Fixup * our information in the handle so we can still stat it. * Go ahead and discard the old name (we will abort if * gsh_strdup fails to copy the new name). */ gsh_free(obj->u.unopenable.name); memcpy(obj->u.unopenable.dir, newdir->handle, sizeof(vfs_file_handle_t)); obj->u.unopenable.name = gsh_strdup(new_name); } fsal_restore_ganesha_credentials(); out: if (oldfd >= 0) close(oldfd); if (newfd >= 0) close(newfd); return fsalstat(fsal_error, retval); } /** * @brief Open file and get attributes. * * This function opens a file and returns the file descriptor and fetches * the attributes. The obj_hdl->obj_lock MUST be held for this call. * * @param[in] exp The fsal_export the file belongs to * @param[in] myself File to access * @param[in/out] stat The struct stat to fetch attributes into * @param[in] open_flags The mode to open the file in * @param[out] fsal_error Place to return an error * * @return The file descriptor plus indication if it needs to be closed. * */ struct closefd vfs_fsal_open_and_stat(struct fsal_export *exp, struct vfs_fsal_obj_handle *myself, struct stat *stat, fsal_openflags_t flags, fsal_errors_t *fsal_error) { struct fsal_obj_handle *obj_hdl = &myself->obj_handle; struct closefd cfd = { .fd = -1, .close_fd = false }; int retval = 0; const char *func = "unknown"; struct vfs_filesystem *vfs_fs = myself->obj_handle.fs->private_data; int open_flags; fsal2posix_openflags(flags, &open_flags); switch (obj_hdl->type) { case SOCKET_FILE: case CHARACTER_FILE: case BLOCK_FILE: cfd.fd = vfs_open_by_handle(vfs_fs, myself->u.unopenable.dir, O_PATH | O_NOACCESS, fsal_error); if (cfd.fd < 0) { LogDebug(COMPONENT_FSAL, "Failed with %s open_flags 0x%08x", strerror(-cfd.fd), O_PATH | O_NOACCESS); return cfd; } cfd.close_fd = true; retval = fstatat(cfd.fd, myself->u.unopenable.name, stat, AT_SYMLINK_NOFOLLOW); func = "fstatat"; break; case REGULAR_FILE: /* Check if the file descriptor happens to be in a compatible * mode. If not, open a temporary file descriptor. * * Note that FSAL_O_REOPEN will never be set in * myself->u.file.fd.openflags and thus forces a re-open. */ if (((flags & FSAL_O_ANY) != 0 && (myself->u.file.fd.openflags & FSAL_O_RDWR) == 0) || ((myself->u.file.fd.openflags & flags) != flags)) { /* no file open at the moment */ cfd.fd = vfs_fsal_open(myself, open_flags, fsal_error); if (cfd.fd < 0) { LogDebug(COMPONENT_FSAL, "Failed with %s open_flags 0x%08x", strerror(-cfd.fd), open_flags); return cfd; } cfd.close_fd = true; } else { cfd.fd = myself->u.file.fd.fd; } retval = fstat(cfd.fd, stat); func = "fstat"; break; case SYMBOLIC_LINK: open_flags |= (O_PATH | O_RDWR | O_NOFOLLOW); goto vfos_open; case FIFO_FILE: open_flags |= O_NONBLOCK; /* fall through */ case DIRECTORY: default: vfos_open: cfd.fd = vfs_fsal_open(myself, open_flags, fsal_error); if (cfd.fd < 0) { LogDebug(COMPONENT_FSAL, "Failed with %s open_flags 0x%08x", strerror(-cfd.fd), open_flags); return cfd; } cfd.close_fd = true; retval = vfs_stat_by_handle(cfd.fd, stat); func = "vfs_stat_by_handle"; break; } if (retval < 0) { retval = errno; if (cfd.close_fd) { int rc; rc = close(cfd.fd); if (rc < 0) { rc = errno; LogDebug(COMPONENT_FSAL, "close failed with %s", strerror(rc)); } } if (retval == ENOENT) retval = ESTALE; *fsal_error = posix2fsal_error(retval); LogDebug(COMPONENT_FSAL, "%s failed with %s", func, strerror(retval)); cfd.fd = -retval; cfd.close_fd = false; return cfd; } return cfd; } /* file_unlink * unlink the named file in the directory */ static fsal_status_t file_unlink(struct fsal_obj_handle *dir_hdl, struct fsal_obj_handle *obj_hdl, const char *name) { struct vfs_fsal_obj_handle *myself; fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; struct stat stat; int fd; int retval = 0; myself = container_of(dir_hdl, struct vfs_fsal_obj_handle, obj_handle); if (dir_hdl->fsal != dir_hdl->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", dir_hdl->fsal->name, dir_hdl->fs->fsal != NULL ? dir_hdl->fs->fsal->name : "(none)"); retval = EXDEV; fsal_error = posix2fsal_error(retval); goto out; } fd = vfs_fsal_open(myself, O_PATH | O_NOACCESS, &fsal_error); if (fd < 0) { retval = -fd; goto out; } retval = fstatat(fd, name, &stat, AT_SYMLINK_NOFOLLOW); if (retval < 0) { retval = errno; LogDebug(COMPONENT_FSAL, "fstatat %s failed %s", name, strerror(retval)); if (retval == ENOENT) fsal_error = ERR_FSAL_STALE; else fsal_error = posix2fsal_error(retval); goto errout; } fsal_set_credentials(op_ctx->creds); retval = unlinkat(fd, name, (S_ISDIR(stat.st_mode)) ? AT_REMOVEDIR : 0); if (retval < 0) { retval = errno; if (retval == ENOENT) fsal_error = ERR_FSAL_STALE; else fsal_error = posix2fsal_error(retval); } fsal_restore_ganesha_credentials(); errout: close(fd); out: return fsalstat(fsal_error, retval); } /* handle_to_wire * fill in the opaque f/s file handle part. * we zero the buffer to length first. This MAY already be done above * at which point, remove memset here because the caller is zeroing * the whole struct. */ static fsal_status_t handle_to_wire(const struct fsal_obj_handle *obj_hdl, fsal_digesttype_t output_type, struct gsh_buffdesc *fh_desc) { const 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) { /* Log, but allow digest */ LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s", obj_hdl->fsal->name, obj_hdl->fs->fsal != NULL ? obj_hdl->fs->fsal->name : "(none)"); } switch (output_type) { case FSAL_DIGEST_NFSV3: case FSAL_DIGEST_NFSV4: if (fh_desc->len < myself->handle->handle_len) { LogMajor(COMPONENT_FSAL, "Space too small for handle. need %u, have %zu", myself->handle->handle_len, fh_desc->len); return fsalstat(ERR_FSAL_TOOSMALL, 0); } memcpy(fh_desc->addr, myself->handle->handle_data, myself->handle->handle_len); break; default: return fsalstat(ERR_FSAL_SERVERFAULT, 0); } fh_desc->len = myself->handle->handle_len; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * handle_to_key * return a handle descriptor into the handle in this object handle * @TODO reminder. make sure things like hash keys don't point here * after the handle is released. */ static void handle_to_key(struct fsal_obj_handle *obj_hdl, struct gsh_buffdesc *fh_desc) { struct vfs_fsal_obj_handle *myself; myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); fh_desc->addr = myself->handle->handle_data; fh_desc->len = myself->handle->handle_len; } /* * release * release our export first so they know we are gone */ static void release(struct fsal_obj_handle *obj_hdl) { struct vfs_fsal_obj_handle *myself; object_file_type_t type = obj_hdl->type; myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); if (type == REGULAR_FILE) { fsal_status_t st; /* Take write lock on object to protect file descriptor. * This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->obj_lock); st = vfs_close_my_fd(&myself->u.file.fd); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); if (FSAL_IS_ERROR(st)) { LogCrit(COMPONENT_FSAL, "Could not close hdl 0x%p, error %s(%d)", obj_hdl, strerror(st.minor), st.minor); } } fsal_obj_handle_fini(obj_hdl); if (type == SYMBOLIC_LINK) { gsh_free(myself->u.symlink.link_content); } else if (type == REGULAR_FILE) { struct gsh_buffdesc key; handle_to_key(obj_hdl, &key); vfs_state_release(&key); } else if (type == DIRECTORY) { gsh_free(myself->u.directory.path); gsh_free(myself->u.directory.fs_location); } else if (vfs_unopenable_type(type)) { gsh_free(myself->u.unopenable.name); gsh_free(myself->u.unopenable.dir); } LogDebug(COMPONENT_FSAL, "Releasing obj_hdl=%p, myself=%p", obj_hdl, myself); gsh_free(myself); } /* vfs_fs_locations * returns the saved referral information to NFS protocol layer */ static fsal_status_t vfs_fs_locations(struct fsal_obj_handle *obj_hdl, struct fs_locations4 *fs_locs) { struct vfs_fsal_obj_handle *myself; myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); struct vfs_filesystem *vfs_fs = myself->obj_handle.fs->private_data; struct fs_location4 *loc_val = fs_locs->locations.locations_val; LogFullDebug(COMPONENT_FSAL, "vfs_fs = %s root_fd = %d major = %d minor = %d", vfs_fs->fs->path, vfs_fs->root_fd, (int)vfs_fs->fs->fsid.major, (int)vfs_fs->fs->fsid.minor); LogDebug(COMPONENT_FSAL, "fs_location = %p:%s", myself->u.directory.fs_location, myself->u.directory.fs_location); if (myself->u.directory.fs_location != NULL) { char *server; char *path_sav, *path_work; path_sav = gsh_strdup(myself->u.directory.fs_location); path_work = path_sav; server = strsep(&path_work, ":"); LogDebug(COMPONENT_FSAL, "fs_location server %s", server); LogDebug(COMPONENT_FSAL, "fs_location path %s", path_work); nfs4_pathname4_free(&fs_locs->fs_root); nfs4_pathname4_alloc(&fs_locs->fs_root, myself->u.directory.path); strncpy(loc_val->server.server_val->utf8string_val, server, strlen(server)); loc_val->server.server_val->utf8string_len = strlen(server); nfs4_pathname4_free(&loc_val->rootpath); nfs4_pathname4_alloc(&loc_val->rootpath, path_work); gsh_free(path_sav); } else { return fsalstat(ERR_FSAL_NOTSUPP, -1); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } void vfs_handle_ops_init(struct fsal_obj_ops *ops) { ops->release = release; ops->merge = vfs_merge; ops->lookup = lookup; ops->readdir = read_dirents; ops->mkdir = makedir; ops->mknode = makenode; ops->symlink = makesymlink; ops->readlink = readsymlink; ops->getattrs = vfs_getattr2; ops->link = linkfile; ops->rename = renamefile; ops->unlink = file_unlink; ops->fs_locations = vfs_fs_locations; ops->close = vfs_close; ops->handle_to_wire = handle_to_wire; ops->handle_to_key = handle_to_key; ops->open2 = vfs_open2; ops->reopen2 = vfs_reopen2; ops->read2 = vfs_read2; ops->write2 = vfs_write2; ops->commit2 = vfs_commit2; ops->lock_op2 = vfs_lock_op2; ops->setattr2 = vfs_setattr2; ops->close2 = vfs_close2; /* xattr related functions */ ops->list_ext_attrs = vfs_list_ext_attrs; ops->getextattr_id_by_name = vfs_getextattr_id_by_name; ops->getextattr_value_by_name = vfs_getextattr_value_by_name; ops->getextattr_value_by_id = vfs_getextattr_value_by_id; ops->setextattr_value = vfs_setextattr_value; ops->setextattr_value_by_id = vfs_setextattr_value_by_id; ops->remove_extattr_by_id = vfs_remove_extattr_by_id; ops->remove_extattr_by_name = vfs_remove_extattr_by_name; } /* export methods that create object handles */ /* lookup_path * modeled on old api except we don't stuff attributes. * KISS */ fsal_status_t vfs_lookup_path(struct fsal_export *exp_hdl, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { int dir_fd = -1; struct stat stat; struct vfs_fsal_obj_handle *hdl; fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; int retval = 0; struct fsal_filesystem *fs; struct fsal_dev__ dev; vfs_file_handle_t *fh = NULL; vfs_alloc_handle(fh); *handle = NULL; /* poison it */ dir_fd = open_dir_by_path_walk(-1, path, &stat); if (dir_fd < 0) { LogCrit(COMPONENT_FSAL, "Could not open directory for path %s", path); retval = -dir_fd; goto errout; } dev = posix2fsal_devt(stat.st_dev); fs = lookup_dev(&dev); if (fs == NULL) { LogInfo(COMPONENT_FSAL, "Could not find file system for path %s", path); retval = ENOENT; goto errout; } if (fs->fsal != exp_hdl->fsal) { LogInfo(COMPONENT_FSAL, "File system for path %s did not belong to FSAL %s", path, exp_hdl->fsal->name); retval = EACCES; goto errout; } LogDebug(COMPONENT_FSAL, "filesystem %s for path %s", fs->path, path); retval = vfs_fd_to_handle(dir_fd, fs, fh); if (retval < 0) { retval = errno; LogCrit(COMPONENT_FSAL, "Could not get handle for path %s, error %s", path, strerror(retval)); goto errout; } /* allocate an obj_handle and fill it up */ hdl = alloc_handle(-1, fh, fs, &stat, NULL, "", exp_hdl); if (hdl == NULL) { retval = ENOMEM; LogCrit(COMPONENT_FSAL, "Could not allocate handle for path %s", path); goto errout; } close(dir_fd); if (attrs_out != NULL) { posix2fsal_attributes_all(&stat, attrs_out); } *handle = &hdl->obj_handle; return fsalstat(ERR_FSAL_NO_ERROR, 0); errout: if (dir_fd >= 0) close(dir_fd); fsal_error = posix2fsal_error(retval); return fsalstat(fsal_error, retval); } fsal_status_t vfs_check_handle(struct fsal_export *exp_hdl, struct gsh_buffdesc *hdl_desc, struct fsal_filesystem **fs, vfs_file_handle_t *fh, bool *dummy) { fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; int retval = 0; struct fsal_fsid__ fsid; enum fsid_type fsid_type; *fs = NULL; if (!vfs_valid_handle(hdl_desc)) return fsalstat(ERR_FSAL_BADHANDLE, 0); memcpy(fh->handle_data, hdl_desc->addr, hdl_desc->len); fh->handle_len = hdl_desc->len; *dummy = vfs_is_dummy_handle(fh); retval = vfs_extract_fsid(fh, &fsid_type, &fsid); if (retval == 0) { *fs = lookup_fsid(&fsid, fsid_type); if (*fs == NULL) { LogInfo(COMPONENT_FSAL, "Could not map fsid=0x%016"PRIx64 ".0x%016"PRIx64" to filesystem", fsid.major, fsid.minor); retval = ESTALE; fsal_error = posix2fsal_error(retval); goto errout; } if (((*fs)->fsal != exp_hdl->fsal) && !(*dummy)) { LogInfo(COMPONENT_FSAL, "fsid=0x%016"PRIx64".0x%016"PRIx64 " in handle not a %s filesystem", fsid.major, fsid.minor, exp_hdl->fsal->name); retval = ESTALE; fsal_error = posix2fsal_error(retval); goto errout; } LogDebug(COMPONENT_FSAL, "Found filesystem %s for handle for FSAL %s", (*fs)->path, (*fs)->fsal != NULL ? (*fs)->fsal->name : "(none)"); } else { LogDebug(COMPONENT_FSAL, "Could not map handle to fsid"); fsal_error = ERR_FSAL_BADHANDLE; goto errout; } errout: return fsalstat(fsal_error, retval); } /* create_handle * Does what original FSAL_ExpandHandle did (sort of) * returns a ref counted handle to be later used in cache_inode etc. * NOTE! you must release this thing when done with it! * BEWARE! Thanks to some holes in the *AT syscalls implementation, * we cannot get an fd on an AF_UNIX socket, nor reliably on block or * character special devices. Sorry, it just doesn't... * we could if we had the handle of the dir it is in, but this method * is for getting handles off the wire for cache entries that have LRU'd. * Ideas and/or clever hacks are welcome... */ fsal_status_t vfs_create_handle(struct fsal_export *exp_hdl, struct gsh_buffdesc *hdl_desc, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { fsal_status_t status; struct vfs_fsal_obj_handle *hdl; struct stat obj_stat; vfs_file_handle_t *fh = NULL; fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; int retval = 0; int fd; int flags = O_PATH | O_NOACCESS | O_NOFOLLOW; struct fsal_filesystem *fs; bool dummy; vfs_alloc_handle(fh); *handle = NULL; /* poison it first */ status = vfs_check_handle(exp_hdl, hdl_desc, &fs, fh, &dummy); if (FSAL_IS_ERROR(status)) return status; if (dummy) { /* We don't need fd here, just stat the fs->path */ fd = -1; retval = stat(fs->path, &obj_stat); } else { fd = vfs_open_by_handle(fs->private_data, fh, flags, &fsal_error); if (fd < 0) { retval = -fd; goto errout; } retval = vfs_stat_by_handle(fd, &obj_stat); } /* Test the result of stat */ if (retval != 0) { retval = errno; LogDebug(COMPONENT_FSAL, "%s failed with %s", dummy ? "stat" : "vfs_stat_by_handle", strerror(retval)); fsal_error = posix2fsal_error(retval); if (fd >= 0) close(fd); goto errout; } hdl = alloc_handle(fd, fh, fs, &obj_stat, NULL, "", exp_hdl); if (fd >= 0) close(fd); if (hdl == NULL) { LogDebug(COMPONENT_FSAL, "Could not allocate handle"); fsal_error = ERR_FSAL_NOMEM; goto errout; } if (attrs_out != NULL) { posix2fsal_attributes_all(&obj_stat, attrs_out); } *handle = &hdl->obj_handle; errout: return fsalstat(fsal_error, retval); } nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/handle_syscalls.c000066400000000000000000000067151324272410200220120ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* handle.c * VFS object (file|dir) handle object */ #include "config.h" #include "fsal.h" #include "fsal_handle_syscalls.h" #include /* used for 'dirname' */ #include #include #include #include "fsal_convert.h" #include "FSAL/fsal_commonlib.h" #include "vfs_methods.h" #include int vfs_readlink(struct vfs_fsal_obj_handle *myself, fsal_errors_t *fsal_error) { int retval = 0; int fd; ssize_t retlink; struct stat st; int flags = O_PATH | O_NOACCESS | O_NOFOLLOW; if (myself->u.symlink.link_content != NULL) { gsh_free(myself->u.symlink.link_content); myself->u.symlink.link_content = NULL; myself->u.symlink.link_size = 0; } fd = vfs_fsal_open(myself, flags, fsal_error); if (fd < 0) return fd; retval = vfs_stat_by_handle(fd, &st); if (retval < 0) goto error; myself->u.symlink.link_size = st.st_size + 1; myself->u.symlink.link_content = gsh_malloc(myself->u.symlink.link_size); retlink = vfs_readlink_by_handle(myself->handle, fd, "", myself->u.symlink.link_content, myself->u.symlink.link_size); if (retlink < 0) goto error; myself->u.symlink.link_content[retlink] = '\0'; close(fd); return retval; error: retval = -errno; *fsal_error = posix2fsal_error(errno); close(fd); if (myself->u.symlink.link_content != NULL) { gsh_free(myself->u.symlink.link_content); myself->u.symlink.link_content = NULL; myself->u.symlink.link_size = 0; } return retval; } int vfs_get_root_handle(struct vfs_filesystem *vfs_fs, struct vfs_fsal_export *exp) { int retval = 0; vfs_fs->root_fd = open(vfs_fs->fs->path, O_RDONLY | O_DIRECTORY); if (vfs_fs->root_fd < 0) { retval = errno; LogMajor(COMPONENT_FSAL, "Could not open VFS mount point %s: rc = %s (%d)", vfs_fs->fs->path, strerror(retval), retval); return retval; } /* Check if we have to re-index the fsid based on config */ if (exp->fsid_type != FSID_NO_TYPE && exp->fsid_type != vfs_fs->fs->fsid_type) { retval = -change_fsid_type(vfs_fs->fs, exp->fsid_type); if (retval != 0) { LogCrit(COMPONENT_FSAL, "Can not change fsid type of %s to %d, error %s", vfs_fs->fs->path, exp->fsid_type, strerror(retval)); return retval; } LogInfo(COMPONENT_FSAL, "Reindexed filesystem %s to fsid=0x%016" PRIx64".0x%016"PRIx64, vfs_fs->fs->path, vfs_fs->fs->fsid.major, vfs_fs->fs->fsid.minor); } /* May reindex for some platforms */ return vfs_re_index(vfs_fs, exp); } nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/os/000077500000000000000000000000001324272410200171065ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/os/CMakeLists.txt000066400000000000000000000010711324272410200216450ustar00rootroot00000000000000# All we need to do here is control the # build of chosen platform if(FREEBSD) SET(fsal_os_STAT_SRCS freebsd/handle_syscalls.c ) endif(FREEBSD) if(LINUX) SET(fsal_os_STAT_SRCS linux/handle_syscalls.c ) endif(LINUX) add_library(fsal_os STATIC ${fsal_os_STAT_SRCS}) add_sanitizers(fsal_os) ########### install files ############### # This is GCC specific to force PIC compiles. # cmake 2.8.9 has a portable POSITION_INDEPENDENT_CODE property that can be # used when it is available set_target_properties(fsal_os PROPERTIES COMPILE_FLAGS "-fPIC") nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/os/freebsd/000077500000000000000000000000001324272410200205205ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/os/freebsd/handle_syscalls.c000066400000000000000000000175011324272410200240400ustar00rootroot00000000000000/* * Copyright (C) Panasas, Inc. 2011 * Author(s): Brent Welch Sachin Bhamare * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later * version. * * This library can be distributed with a BSD license as well, just * ask. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA */ /** * @file FSAL/FSAL_VFS/os/freebsd/handle_syscalls.c * @brief System calls for the FreeBSD handle calls */ #include /* avoid conflicts with sys/queue.h */ #include #include "fsal_convert.h" #include "fsal_api.h" #include "FSAL/fsal_commonlib.h" #include "../../vfs_methods.h" #include /* * Currently all FreeBSD versions define MAXFIDSZ to be 16 which is not * sufficient for PanFS file handle. Following scheme ensures that on FreeBSD * platform we always use big enough data structure for holding file handle by * using our own file handle data structure instead of struct fhandle from * mount.h * */ #define MAXFIDSIZE 36 struct v_fid { u_short fid_len; /* length of data in bytes */ u_short fid_reserved; /* force longword alignment */ char fid_data[MAXFIDSIZE]; /* data (variable length) */ }; struct v_fhandle { fsid_t fh_fsid; /* Filesystem id of mount point */ struct v_fid fh_fid; /* Filesys specific id */ }; static inline size_t vfs_sizeof_handle(struct v_fhandle *fh) { return sizeof(fsid_t) + sizeof(fh->fh_fid.fid_len) + sizeof(fh->fh_fid.fid_reserved) + fh->fh_fid.fid_len; } /* Verify handle size is large enough. * sizeof(fsid_t) == 8 * sizeof(fid_len) == 2 * sizeof(fid_reserved) == 2 * fid_len == MAXFIDSIZE */ #if VFS_HANDLE_LEN < (8 + 2 + 2 + MAXFIDSIZE) #error "VFS_HANDLE_LEN is too small" #endif void display_vfs_handle(struct display_buffer *dspbuf, struct vfs_file_handle *fh) { struct v_fhandle *hdl = (struct v_fhandle *) fh->handle_data; int b_left; b_left = display_printf(dspbuf, "Handle len %hhu: fsid=0x%016" PRIx32".0x%016"PRIx32 " fid_len=%"PRIu16 " fid_pad=%"PRIu16, fh->handle_len, hdl->fh_fsid.val[0], hdl->fh_fsid.val[1], hdl->fh_fid.fid_len, hdl->fh_fid.fid_reserved); if (b_left <= 0) return; display_opaque_value(dspbuf, hdl->fh_fid.fid_data, hdl->fh_fid.fid_len); } #define LogVFSHandle(fh) \ do { \ if (isMidDebug(COMPONENT_FSAL)) { \ char buf[256] = "\0"; \ struct display_buffer dspbuf = \ {sizeof(buf), buf, buf}; \ \ display_vfs_handle(&dspbuf, fh); \ \ LogMidDebug(COMPONENT_FSAL, "%s", buf); \ } \ } while (0) int vfs_fd_to_handle(int fd, struct fsal_filesystem *fs, vfs_file_handle_t *fh) { int error; struct v_fhandle *handle = (struct v_fhandle *) fh->handle_data; error = getfhat(fd, NULL, (struct fhandle *) handle, AT_SYMLINK_FOLLOW); if (error == 0) fh->handle_len = vfs_sizeof_handle(handle); return error; } int vfs_name_to_handle(int atfd, struct fsal_filesystem *fs, const char *name, vfs_file_handle_t *fh) { int error; struct v_fhandle *handle = (struct v_fhandle *) fh->handle_data; error = getfhat(atfd, (char *)name, (struct fhandle *) handle, AT_SYMLINK_NOFOLLOW); if (error == 0) fh->handle_len = vfs_sizeof_handle(handle); return error; } int vfs_open_by_handle(struct vfs_filesystem *fs, vfs_file_handle_t *fh, int openflags, fsal_errors_t *fsal_error) { int fd; fd = fhopen((struct fhandle *) fh->handle_data, openflags); if (fd < 0) { fd = -errno; if (fd == -ENOENT) fd = -ESTALE; *fsal_error = posix2fsal_error(-fd); LogDebug(COMPONENT_FSAL, "Failed with %s", strerror(-fd)); } return fd; } int vfs_extract_fsid(vfs_file_handle_t *fh, enum fsid_type *fsid_type, struct fsal_fsid__ *fsid) { struct v_fhandle *hdl = (struct v_fhandle *) fh->handle_data; LogVFSHandle(fh); if (hdl->fh_fid.fid_reserved != 0) { int rc; *fsid_type = (enum fsid_type) (hdl->fh_fid.fid_reserved - 1); rc = decode_fsid(fh->handle_data, sizeof(fh->handle_data), fsid, *fsid_type); if (rc < 0) { errno = EINVAL; return rc; } return 0; } *fsid_type = FSID_TWO_UINT32; fsid->major = hdl->fh_fsid.val[0]; fsid->minor = hdl->fh_fsid.val[1]; return 0; } int vfs_encode_dummy_handle(vfs_file_handle_t *fh, struct fsal_filesystem *fs) { struct v_fhandle *hdl = (struct v_fhandle *) fh->handle_data; int rc; hdl->fh_fsid.val[0] = 0; hdl->fh_fsid.val[1] = 0; rc = encode_fsid(hdl->fh_fid.fid_data, sizeof(hdl->fh_fid.fid_data), &fs->fsid, fs->fsid_type); if (rc < 0) { errno = EINVAL; return rc; } hdl->fh_fid.fid_reserved = fs->fsid_type + 1; hdl->fh_fid.fid_len = rc; fh->handle_len = vfs_sizeof_handle(hdl); LogVFSHandle(fh); return 0; } bool vfs_is_dummy_handle(vfs_file_handle_t *fh) { struct v_fhandle *hdl = (struct v_fhandle *) fh->handle_data; return hdl->fh_fid.fid_reserved != 0; } bool vfs_valid_handle(struct gsh_buffdesc *desc) { struct v_fhandle *hdl = (struct v_fhandle *) desc->addr; if ((desc->addr == NULL) || (desc->len < (sizeof(fsid_t) + sizeof(hdl->fh_fid.fid_len) + sizeof(hdl->fh_fid.fid_reserved)))) return false; if (isMidDebug(COMPONENT_FSAL)) { char buf[256] = "\0"; struct display_buffer dspbuf = {sizeof(buf), buf, buf}; int b_left; b_left = display_printf(&dspbuf, "Handle len %d: fsid=0x%016" PRIx32".0x%016"PRIx32 " fid_len=%"PRIu16 " fid_pad=%"PRIu16, (int) desc->len, hdl->fh_fsid.val[0], hdl->fh_fsid.val[1], hdl->fh_fid.fid_len, hdl->fh_fid.fid_reserved); if (b_left > 0) { display_opaque_value(&dspbuf, hdl->fh_fid.fid_data, hdl->fh_fid.fid_len); } LogMidDebug(COMPONENT_FSAL, "%s", buf); } if (hdl->fh_fid.fid_reserved != 0) { bool fsid_type_ok = false; switch ((enum fsid_type) (hdl->fh_fid.fid_reserved - 1)) { case FSID_NO_TYPE: case FSID_ONE_UINT64: case FSID_MAJOR_64: case FSID_TWO_UINT64: case FSID_TWO_UINT32: case FSID_DEVICE: fsid_type_ok = true; break; } if (!fsid_type_ok) { LogDebug(COMPONENT_FSAL, "FSID Type %02"PRIu16" invalid", hdl->fh_fid.fid_reserved - 1); return false; } } return (desc->len >= (sizeof(fsid_t) + sizeof(hdl->fh_fid.fid_len) + sizeof(hdl->fh_fid.fid_reserved))) && (desc->len == vfs_sizeof_handle(hdl)); } int vfs_re_index(struct vfs_filesystem *vfs_fs, struct vfs_fsal_export *exp) { enum fsid_type fsid_type; struct fsal_fsid__ fsid; int retval; vfs_file_handle_t *fh; vfs_alloc_handle(fh); retval = vfs_fd_to_handle(vfs_fs->root_fd, vfs_fs->fs, fh); if (retval != 0) { retval = errno; LogMajor(COMPONENT_FSAL, "Get root handle for %s failed with %s (%d)", vfs_fs->fs->path, strerror(retval), retval); goto errout; } /* Extract fsid from the root handle and re-index the filesystem * using it. This is because the file handle already has an fsid in * it. */ (void) vfs_extract_fsid(fh, &fsid_type, &fsid); retval = re_index_fs_fsid(vfs_fs->fs, fsid_type, &fsid); if (retval < 0) { LogCrit(COMPONENT_FSAL, "Could not re-index VFS file system fsid for %s", vfs_fs->fs->path); retval = -retval; } errout: return retval; } /** @} */ nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/os/linux/000077500000000000000000000000001324272410200202455ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/os/linux/handle_syscalls.c000066400000000000000000000267021324272410200235700ustar00rootroot00000000000000/* * Copyright (C) International Business Machines Corp., 2010 * Author(s): Aneesh Kumar K.V * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * @file FSAL/FSAL_VFS/os/linux/handle_syscalls.c * @brief System calls for the Linux handle calls * */ #include "fsal.h" #include "fsal_api.h" #include "fsal_convert.h" #include "FSAL/fsal_commonlib.h" #include "../../vfs_methods.h" /* We can at most support 40 byte handles, which are the largest known. * We also expect handles to be at least 4 bytes. */ #define VFS_MAX_HANDLE 48 #define VFS_MIN_HANDLE_SIZE 4 /* Visual handle format * * 1 byte handle_len (doesn't go on wire) * 1 byte flags * 00xxxxxx fsid_type * 01000000 handle_type fits in one byte * 10000000 handle_type fits in two bytes * 11000000 handle_type fits in four bytes */ #define HANDLE_TYPE_8 0x40 #define HANDLE_TYPE_16 0x80 #define HANDLE_TYPE_32 0xC0 #define HANDLE_TYPE_MASK 0xC0 #define HANDLE_DUMMY 0x20 #define HANDLE_FSID_MASK (~(HANDLE_TYPE_MASK | HANDLE_DUMMY)) /* * 0, 8, or 16 bytes fsid type * 1,2 or 4 bytes handle type * 12 to 40 bytes opaque kernel handle * * NOTE: if handle type doesn't fit in 2 bytes or less, 40 byte handles * (BTRFS and GPFS are known file systems that use 40 bytes) are * incompatible with 16 byte uuid form fsids. * If VMware clients are concerned, the limit is 8 bytes less... */ int display_vfs_handle(struct display_buffer *dspbuf, struct vfs_file_handle *fh) { int16_t i16; int32_t i32; uint32_t u32[2]; uint64_t u64[2]; uint8_t handle_cursor = 1; int b_left; b_left = display_printf(dspbuf, "Handle len %hhu 0x%02hhx: ", fh->handle_len, fh->handle_data[0]); if (b_left <= 0) return b_left; switch ((enum fsid_type) fh->handle_data[0] & HANDLE_FSID_MASK) { case FSID_NO_TYPE: b_left = display_cat(dspbuf, "no fsid"); break; case FSID_ONE_UINT64: case FSID_MAJOR_64: memcpy(u64, fh->handle_data + handle_cursor, sizeof(u64[0])); handle_cursor += sizeof(u64[0]); b_left = display_printf(dspbuf, "fsid=0x%016"PRIx64 ".0x0000000000000000", u64[0]); break; case FSID_TWO_UINT64: memcpy(u64, fh->handle_data + handle_cursor, sizeof(u64)); handle_cursor += sizeof(u64); b_left = display_printf(dspbuf, "fsid=0x%016"PRIx64".0x%016"PRIx64, u64[0], u64[1]); break; case FSID_TWO_UINT32: case FSID_DEVICE: memcpy(u32, fh->handle_data + handle_cursor, sizeof(u32)); handle_cursor += sizeof(u32); b_left = display_printf(dspbuf, "fsid=0x%016"PRIx64".0x%016"PRIx64, u32[0], u32[1]); break; } if (b_left <= 0) return b_left; if ((fh->handle_data[0] & HANDLE_DUMMY) != 0) return display_cat(dspbuf, ", DUMMY"); switch (fh->handle_data[0] & HANDLE_TYPE_MASK) { case 0: b_left = display_cat(dspbuf, ", invalid type"); break; case HANDLE_TYPE_8: b_left = display_printf(dspbuf, ", type 0x%02hhx", fh->handle_data[handle_cursor]); handle_cursor++; break; case HANDLE_TYPE_16: memcpy(&i16, fh->handle_data + handle_cursor, sizeof(i16)); handle_cursor += sizeof(i16); b_left = display_printf(dspbuf, ", type 0x%04h"PRIx16, i16); break; case HANDLE_TYPE_32: memcpy(&i32, fh->handle_data + handle_cursor, sizeof(i32)); handle_cursor += sizeof(i32); b_left = display_printf(dspbuf, ", type 0x%04"PRIx32, i32); break; } if (b_left <= 0) return b_left; b_left = display_cat(dspbuf, ", opaque: "); if (b_left <= 0) return b_left; return display_opaque_value(dspbuf, fh->handle_data + handle_cursor, fh->handle_len - handle_cursor); } #define LogVFSHandle(fh) \ do { \ if (isMidDebug(COMPONENT_FSAL)) { \ char buf[256] = "\0"; \ struct display_buffer dspbuf = \ {sizeof(buf), buf, buf}; \ \ display_vfs_handle(&dspbuf, fh); \ \ LogMidDebug(COMPONENT_FSAL, "%s", buf); \ } \ } while (0) int vfs_map_name_to_handle_at(int fd, struct fsal_filesystem *fs, const char *path, vfs_file_handle_t *fh, int flags) { struct file_handle *kernel_fh; int32_t i32; int rc; int mnt_id; kernel_fh = alloca(sizeof(struct file_handle) + VFS_MAX_HANDLE); kernel_fh->handle_bytes = VFS_MAX_HANDLE; rc = name_to_handle_at(fd, path, kernel_fh, &mnt_id, flags); if (rc < 0) { int err = errno; LogDebug(COMPONENT_FSAL, "Error %s (%d) bytes = %d", strerror(err), err, (int) kernel_fh->handle_bytes); errno = err; return rc; } /* Init flags with fsid type */ fh->handle_data[0] = fs->fsid_type; fh->handle_len = 1; /* Pack fsid into wire handle */ rc = encode_fsid(fh->handle_data + 1, sizeof_fsid(fs->fsid_type), &fs->fsid, fs->fsid_type); if (rc < 0) { errno = EINVAL; return rc; } fh->handle_len += rc; /* Pack handle type into wire handle */ if (kernel_fh->handle_type <= UINT8_MAX) { /* Copy one byte in and advance cursor */ fh->handle_data[fh->handle_len] = kernel_fh->handle_type; fh->handle_len++; fh->handle_data[0] |= HANDLE_TYPE_8; } else if (kernel_fh->handle_type <= INT16_MAX && kernel_fh->handle_type >= INT16_MIN) { /* Type fits in 16 bits */ int16_t handle_type_16 = kernel_fh->handle_type; memcpy(fh->handle_data + fh->handle_len, &handle_type_16, sizeof(handle_type_16)); fh->handle_len += sizeof(handle_type_16); fh->handle_data[0] |= HANDLE_TYPE_16; } else { /* Type needs whole 32 bits */ i32 = kernel_fh->handle_type; memcpy(fh->handle_data + fh->handle_len, &i32, sizeof(i32)); fh->handle_len += sizeof(i32); fh->handle_data[0] |= HANDLE_TYPE_32; } /* Pack opaque handle into wire handle */ if (fh->handle_len + kernel_fh->handle_bytes > VFS_HANDLE_LEN) { /* We just can't fit this handle... */ errno = EOVERFLOW; return -1; } else { memcpy(fh->handle_data + fh->handle_len, kernel_fh->f_handle, kernel_fh->handle_bytes); fh->handle_len += kernel_fh->handle_bytes; } LogVFSHandle(fh); return 0; } int vfs_open_by_handle(struct vfs_filesystem *vfs_fs, vfs_file_handle_t *fh, int openflags, fsal_errors_t *fsal_error) { struct file_handle *kernel_fh; uint8_t handle_cursor = sizeof_fsid(vfs_fs->fs->fsid_type) + 1; int16_t i16; int32_t i32; int fd; LogFullDebug(COMPONENT_FSAL, "vfs_fs = %s root_fd = %d", vfs_fs->fs->path, vfs_fs->root_fd); LogVFSHandle(fh); kernel_fh = alloca(sizeof(struct file_handle) + VFS_MAX_HANDLE); switch (fh->handle_data[0] & HANDLE_TYPE_MASK) { case 0: LogDebug(COMPONENT_FSAL, "Invaliid handle type = 0"); errno = EINVAL; fd = -1; goto out; case HANDLE_TYPE_8: kernel_fh->handle_type = fh->handle_data[handle_cursor]; handle_cursor++; break; case HANDLE_TYPE_16: memcpy(&i16, fh->handle_data + handle_cursor, sizeof(i16)); handle_cursor += sizeof(i16); kernel_fh->handle_type = i16; break; case HANDLE_TYPE_32: memcpy(&i32, fh->handle_data + handle_cursor, sizeof(i32)); handle_cursor += sizeof(i32); kernel_fh->handle_type = i32; break; } kernel_fh->handle_bytes = fh->handle_len - handle_cursor; memcpy(kernel_fh->f_handle, fh->handle_data + handle_cursor, kernel_fh->handle_bytes); fd = open_by_handle_at(vfs_fs->root_fd, kernel_fh, openflags); out: if (fd < 0) { fd = -errno; if (fd == -ENOENT) fd = -ESTALE; *fsal_error = posix2fsal_error(-fd); LogDebug(COMPONENT_FSAL, "Failed with %s openflags 0x%08x", strerror(-fd), openflags); } else { LogFullDebug(COMPONENT_FSAL, "Opened fd %d", fd); } return fd; } int vfs_fd_to_handle(int fd, struct fsal_filesystem *fs, vfs_file_handle_t *fh) { return vfs_map_name_to_handle_at(fd, fs, "", fh, AT_EMPTY_PATH); } int vfs_name_to_handle(int atfd, struct fsal_filesystem *fs, const char *name, vfs_file_handle_t *fh) { return vfs_map_name_to_handle_at(atfd, fs, name, fh, 0); } int vfs_extract_fsid(vfs_file_handle_t *fh, enum fsid_type *fsid_type, struct fsal_fsid__ *fsid) { LogVFSHandle(fh); *fsid_type = (enum fsid_type) fh->handle_data[0] & HANDLE_FSID_MASK; if (decode_fsid(fh->handle_data + 1, fh->handle_len - 1, fsid, *fsid_type) < 0) return ESTALE; else return 0; } int vfs_encode_dummy_handle(vfs_file_handle_t *fh, struct fsal_filesystem *fs) { int rc; /* Init flags with fsid type */ fh->handle_data[0] = fs->fsid_type | HANDLE_DUMMY; fh->handle_len = 1; /* Pack fsid into wire handle */ rc = encode_fsid(fh->handle_data + 1, sizeof_fsid(fs->fsid_type), &fs->fsid, fs->fsid_type); if (rc < 0) { errno = EINVAL; return rc; } fh->handle_len += rc; LogVFSHandle(fh); return 0; } bool vfs_is_dummy_handle(vfs_file_handle_t *fh) { return (fh->handle_data[0] & HANDLE_DUMMY) != 0; } bool vfs_valid_handle(struct gsh_buffdesc *desc) { uint8_t handle0; int len = 1; /* handle_type */ bool fsid_type_ok = false; bool ok; if (desc->addr == NULL) { LogDebug(COMPONENT_FSAL, "desc->addr == NULL"); return false; } if (desc->len > VFS_HANDLE_LEN) { LogDebug(COMPONENT_FSAL, "desc->len %d > VFS_HANDLE_LEN", (int) desc->len); return false; } handle0 = *((uint8_t *) (desc->addr)); switch ((enum fsid_type) handle0 & HANDLE_FSID_MASK) { case FSID_NO_TYPE: case FSID_ONE_UINT64: case FSID_MAJOR_64: case FSID_TWO_UINT64: case FSID_TWO_UINT32: case FSID_DEVICE: fsid_type_ok = true; len += sizeof_fsid((enum fsid_type) handle0 & HANDLE_FSID_MASK); break; } if (!fsid_type_ok) { LogDebug(COMPONENT_FSAL, "FSID Type %02hhx invalid", (uint8_t) (handle0 & HANDLE_FSID_MASK)); return false; } if ((handle0 & HANDLE_DUMMY) != 0) { ok = len == desc->len; if (!ok) { LogDebug(COMPONENT_FSAL, "Len %d != desc->len %d for DUMMY handle", len, (int) desc->len); } return ok; } /* min kernel handle size */ len += sizeof(uint32_t); switch (handle0 & HANDLE_TYPE_MASK) { case HANDLE_TYPE_8: len += sizeof(uint8_t); break; case HANDLE_TYPE_16: len += sizeof(int16_t); break; case HANDLE_TYPE_32: len += sizeof(int32_t); break; default: LogDebug(COMPONENT_FSAL, "Handle Type %02hhx invalid", (uint8_t) (handle0 & HANDLE_TYPE_MASK)); return false; } ok = (len + VFS_MIN_HANDLE_SIZE) <= desc->len; if (!ok) { LogDebug(COMPONENT_FSAL, "Len %d + VFS_MIN_HANDLE_SIZE %d > desc->len %d", len, len + VFS_MIN_HANDLE_SIZE, (int) desc->len); return false; } ok = (len + VFS_MAX_HANDLE) >= desc->len; if (!ok) { LogDebug(COMPONENT_FSAL, "Len %d + VFS_MAX_HANDLE %d < desc->len %d", len, len + VFS_MAX_HANDLE, (int) desc->len); } return true; } int vfs_re_index(struct vfs_filesystem *vfs_fs, struct vfs_fsal_export *exp) { return 0; } /** @} */ nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/panfs/000077500000000000000000000000001324272410200175745ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/panfs/CMakeLists.txt000066400000000000000000000014241324272410200223350ustar00rootroot00000000000000add_definitions( -D__USE_GNU -D_GNU_SOURCE ) SET(fsalpanfs_LIB_SRCS main.c mds.c panfs_um_pnfs.c ../export.c ../handle.c ../handle_syscalls.c ../file.c ../xattrs.c ../state.c ../vfs_methods.h subfsal_panfs.c attrs.c handle.c ) add_library(fsalpanfs MODULE ${fsalpanfs_LIB_SRCS}) add_sanitizers(fsalpanfs) target_link_libraries(fsalpanfs gos fsal_os ${SYSTEM_LIBRARIES} ) set_target_properties(fsalpanfs PROPERTIES VERSION 4.2.0 SOVERSION 4) install(TARGETS fsalpanfs COMPONENT fsal DESTINATION ${FSAL_DESTINATION} ) # This is GCC specific to force PIC compiles. # cmake 2.8.9 has a portable POSITION_INDEPENDENT_CODE property that can be # used when it is available set_target_properties(fsalpanfs PROPERTIES COMPILE_FLAGS "-fPIC") nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/panfs/attrs.c000066400000000000000000000300071324272410200210750ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) CohortFS LLC, 2014 * Author: Daniel Gryniewicz dang@cohortfs.com * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* attrs.c * PanFS Attribute handling */ #include "fsal_types.h" #include "fsal_convert.h" #include "FSAL/access_check.h" #include "nfs4_acls.h" #include "../vfs_methods.h" #include "attrs.h" #include "panfs_um_pnfs.h" pan_fs_ace_info_t fsal_to_panace_info(fsal_acetype_t type, fsal_aceflag_t flag) { pan_fs_ace_info_t info; switch (type) { case FSAL_ACE_TYPE_ALLOW: info = PAN_FS_ACE_POS; break; case FSAL_ACE_TYPE_DENY: info = PAN_FS_ACE_NEG; break; case FSAL_ACE_TYPE_AUDIT: info = PAN_FS_ACE_AUDIT; break; case FSAL_ACE_TYPE_ALARM: info = PAN_FS_ACE_ALARM; break; default: return (uint32_t)-1; } if (flag & FSAL_ACE_FLAG_FILE_INHERIT) info |= PAN_FS_ACE_OBJECT_INHERIT; if (flag & FSAL_ACE_FLAG_DIR_INHERIT) info |= PAN_FS_ACE_CONTAINER_INHERIT; if (flag & FSAL_ACE_FLAG_NO_PROPAGATE) info |= PAN_FS_ACE_NO_PROPAGATE_INHERIT; if (flag & FSAL_ACE_FLAG_INHERIT_ONLY) info |= PAN_FS_ACE_INHERIT_ONLY; if (flag & FSAL_ACE_FLAG_SUCCESSFUL) info |= PAN_FS_ACE_SUCCESSFUL_ACC_ACE_FLAG; if (flag & FSAL_ACE_FLAG_FAILED) info |= PAN_FS_ACE_FAILED_ACC_ACE_FLAG; if (flag & FSAL_ACE_FLAG_INHERITED) info |= PAN_FS_ACE_INHERITED_ACE; return info; } pan_acl_permission_t fsal_perm_to_panace_perm(fsal_aceperm_t perm) { pan_acl_permission_t ret = 0; if (perm & FSAL_ACE_PERM_LIST_DIR) ret |= PAN_ACL_PERM_LIST_DIR; if (perm & FSAL_ACE_PERM_READ_DATA) ret |= PAN_ACL_PERM_READ; if (perm & FSAL_ACE_PERM_WRITE_DATA) ret |= PAN_ACL_PERM_WRITE; if (perm & FSAL_ACE_PERM_ADD_FILE) ret |= PAN_ACL_PERM_CREATE; if (perm & FSAL_ACE_PERM_APPEND_DATA) ret |= PAN_ACL_PERM_APPEND; if (perm & FSAL_ACE_PERM_ADD_SUBDIRECTORY) ret |= PAN_ACL_PERM_CREATE_DIR; if (perm & FSAL_ACE_PERM_READ_NAMED_ATTR) ret |= PAN_ACL_PERM_READ_NAMED_ATTRS; if (perm & FSAL_ACE_PERM_WRITE_NAMED_ATTR) ret |= PAN_ACL_PERM_WRITE_NAMED_ATTRS; if (perm & FSAL_ACE_PERM_EXECUTE) ret |= PAN_ACL_PERM_EXECUTE; if (perm & FSAL_ACE_PERM_DELETE_CHILD) ret |= PAN_ACL_PERM_DELETE_CHILD; if (perm & FSAL_ACE_PERM_READ_ATTR) ret |= PAN_ACL_PERM_READ_ATTRS; if (perm & FSAL_ACE_PERM_WRITE_ATTR) ret |= PAN_ACL_PERM_WRITE_ATTRS; if (perm & FSAL_ACE_PERM_DELETE) ret |= PAN_ACL_PERM_DELETE; if (perm & FSAL_ACE_PERM_READ_ACL) ret |= PAN_ACL_PERM_READ_ACL; if (perm & FSAL_ACE_PERM_WRITE_ACL) ret |= PAN_ACL_PERM_CHANGE_ACL; if (perm & FSAL_ACE_PERM_WRITE_OWNER) ret |= PAN_ACL_PERM_TAKE_OWNER; if (perm & FSAL_ACE_PERM_SYNCHRONIZE) ret |= PAN_ACL_PERM_SYNCHRONIZE; return ret; } /* XXX dang - This will lose PANFS specific types (PAN_IDENTITY_PAN_* and * PAN_IDENTITY_WIN_*), since those were consolidated into the FSAL types */ void fsal_id_to_panace_id(fsal_ace_t *ace, pan_identity_t *pan_id) { if (IS_FSAL_ACE_SPECIAL_ID(*ace)) { if (IS_FSAL_ACE_SPECIAL_EVERYONE(*ace)) { pan_id->type = PAN_IDENTITY_PAN_GROUP; pan_id->u.pan_gid = PAN_IDENTITY_EVERYONE_GROUP_ID; } else { pan_id->type = PAN_IDENTITY_UNKNOWN; pan_id->u.uid = ace->who.uid; } } else { if (IS_FSAL_ACE_GROUP_ID(*ace)) { pan_id->type = PAN_IDENTITY_UNIX_GROUP; pan_id->u.gid = ace->who.gid; } else { pan_id->type = PAN_IDENTITY_UNIX_USER; pan_id->u.uid = ace->who.uid; } } } fsal_acetype_t panace_info_to_fsal_type(pan_fs_ace_info_t aceinfo) { uint32_t type = aceinfo & PAN_FS_ACE_TYPE_MASK; switch (type) { case PAN_FS_ACE_POS: case 8: return FSAL_ACE_TYPE_ALLOW; case PAN_FS_ACE_NEG: return FSAL_ACE_TYPE_DENY; case PAN_FS_ACE_AUDIT: return FSAL_ACE_TYPE_AUDIT; case PAN_FS_ACE_ALARM: return FSAL_ACE_TYPE_ALARM; default: return (uint32_t)-1; } } fsal_aceflag_t panace_info_to_fsal_flag(pan_fs_ace_info_t aceinfo) { uint32_t flag = aceinfo & PAN_FS_ACE_INHERIT_TYPE_MASK; fsal_aceflag_t ret = 0; if (flag & PAN_FS_ACE_OBJECT_INHERIT) ret |= FSAL_ACE_FLAG_FILE_INHERIT; if (flag & PAN_FS_ACE_CONTAINER_INHERIT) ret |= FSAL_ACE_FLAG_DIR_INHERIT; if (flag & PAN_FS_ACE_NO_PROPAGATE_INHERIT) ret |= FSAL_ACE_FLAG_NO_PROPAGATE; if (flag & PAN_FS_ACE_INHERIT_ONLY) ret |= FSAL_ACE_FLAG_INHERIT_ONLY; if (flag & PAN_FS_ACE_SUCCESSFUL_ACC_ACE_FLAG) ret |= FSAL_ACE_FLAG_SUCCESSFUL; if (flag & PAN_FS_ACE_FAILED_ACC_ACE_FLAG) ret |= FSAL_ACE_FLAG_FAILED; if (flag & PAN_FS_ACE_INHERITED_ACE) ret |= FSAL_ACE_FLAG_INHERITED; return ret; } void panace_id_to_fsal_id(pan_identity_t *pan_id, fsal_ace_t *ace) { switch (pan_id->type) { case PAN_IDENTITY_PAN_USER: case PAN_IDENTITY_UNIX_USER: case PAN_IDENTITY_WIN_USER: /* Consolidate users */ ace->flag &= ~(FSAL_ACE_FLAG_GROUP_ID); ace->who.uid = pan_id->u.uid; break; case PAN_IDENTITY_PAN_GROUP: if (pan_id->u.pan_gid == PAN_IDENTITY_EVERYONE_GROUP_ID) { ace->iflag |= FSAL_ACE_IFLAG_SPECIAL_ID; /* Do not set IDENTIFIER_GROUP flag for EVERYONE@ */ ace->who.gid = FSAL_ACE_SPECIAL_EVERYONE; break; } /* FALLTHROUGH */ case PAN_IDENTITY_UNIX_GROUP: case PAN_IDENTITY_WIN_GROUP: /* Consolidate groups */ ace->flag |= FSAL_ACE_FLAG_GROUP_ID; ace->who.gid = pan_id->u.gid; break; case PAN_IDENTITY_UNKNOWN: ace->iflag |= FSAL_ACE_IFLAG_SPECIAL_ID; /* FALLTHROUGH */ default: /* XXX Store unknown type? Won't work for some types, they won't * fit */ ace->who.uid = pan_id->u.unknown; break; } } fsal_aceperm_t panace_perm_to_fsal_perm(pan_acl_permission_t perms) { uint32_t flag = perms & PAN_ACL_PERM_ALL; fsal_aceperm_t ret = 0; if (flag & PAN_ACL_PERM_LIST_DIR) ret |= FSAL_ACE_PERM_LIST_DIR; if (flag & PAN_ACL_PERM_READ) ret |= FSAL_ACE_PERM_READ_DATA; if (flag & PAN_ACL_PERM_WRITE) ret |= FSAL_ACE_PERM_WRITE_DATA; if (flag & PAN_ACL_PERM_CREATE) ret |= FSAL_ACE_PERM_ADD_FILE; if (flag & PAN_ACL_PERM_APPEND) ret |= FSAL_ACE_PERM_APPEND_DATA; if (flag & PAN_ACL_PERM_CREATE_DIR) ret |= FSAL_ACE_PERM_ADD_SUBDIRECTORY; if (flag & PAN_ACL_PERM_READ_NAMED_ATTRS) ret |= FSAL_ACE_PERM_READ_NAMED_ATTR; if (flag & PAN_ACL_PERM_WRITE_NAMED_ATTRS) ret |= FSAL_ACE_PERM_WRITE_NAMED_ATTR; if (flag & PAN_ACL_PERM_EXECUTE) ret |= FSAL_ACE_PERM_EXECUTE; if (flag & PAN_ACL_PERM_DELETE_CHILD) ret |= FSAL_ACE_PERM_DELETE_CHILD; if (flag & PAN_ACL_PERM_READ_ATTRS) ret |= FSAL_ACE_PERM_READ_ATTR; if (flag & PAN_ACL_PERM_WRITE_ATTRS) ret |= FSAL_ACE_PERM_WRITE_ATTR; if (flag & PAN_ACL_PERM_DELETE) ret |= FSAL_ACE_PERM_DELETE; if (flag & PAN_ACL_PERM_READ_ACL) ret |= FSAL_ACE_PERM_READ_ACL; if (flag & PAN_ACL_PERM_CHANGE_ACL) ret |= FSAL_ACE_PERM_WRITE_ACL; if (flag & PAN_ACL_PERM_TAKE_OWNER) ret |= FSAL_ACE_PERM_WRITE_OWNER; if (flag & PAN_ACL_PERM_SYNCHRONIZE) ret |= FSAL_ACE_PERM_SYNCHRONIZE; return ret; } /** * @brief Convert FSAL ACLs to PanFS ACLs * * @param[in] attrib Source FSAL attribute list * @param[in] panacl Target PanFS ACL set * @return FSAL Status */ static fsal_status_t fsal_acl_2_panfs_acl(struct attrlist *attrib, struct pan_fs_acl_s *panacl) { fsal_errors_t ret = ERR_FSAL_NO_ERROR; struct pan_fs_ace_s *panace; fsal_ace_t *ace = NULL; fsal_acl_t *acl = NULL; /* sanity checks */ if (!attrib || !attrib->acl || !panacl) return fsalstat(ERR_FSAL_FAULT, EFAULT); if (attrib->acl->naces > panacl->naces) { /* Too many ACEs for storing */ return fsalstat(ERR_FSAL_INVAL, EFAULT); } /* Create Panfs acl data. */ panacl->naces = attrib->acl->naces; LogDebug(COMPONENT_FSAL, "Converting %u aces:", panacl->naces); for (ace = attrib->acl->aces, panace = panacl->aces; ace < attrib->acl->aces + attrib->acl->naces; ace++, panace++) { fsal_print_ace(COMPONENT_FSAL, NIV_DEBUG, ace); panace->info = fsal_to_panace_info(ace->type, ace->flag); if (panace->info == (uint16_t)-1) { ret = ERR_FSAL_INVAL; goto fail; } panace->permissions = fsal_perm_to_panace_perm(ace->perm); fsal_id_to_panace_id(ace, &panace->identity); } return fsalstat(ERR_FSAL_NO_ERROR, 0); fail: (void)nfs4_acl_release_entry(acl); return fsalstat(ret, 0); } /** * @brief Convert PanFS ACLs to FSAL ACLs * * @param[in] panacl Source PanFS ACL set * @param[in,out] attrib Target FSAL attribute list * @return FSAL Status */ static fsal_status_t panfs_acl_2_fsal_acl(struct pan_fs_acl_s *panacl, struct attrlist *attrib) { fsal_acl_status_t status; fsal_acl_data_t acldata; struct pan_fs_ace_s *panace; fsal_ace_t *ace = NULL; fsal_acl_t *acl = NULL; /* sanity checks */ if (!attrib || !panacl) return fsalstat(ERR_FSAL_FAULT, EFAULT); /* Count the number of aces */ acldata.naces = 0; for (panace = panacl->aces; panace < panacl->aces + panacl->naces; panace++) { if (panace_info_to_fsal_type(panace->info) == (uint32_t)-1) continue; acldata.naces++; } if (acldata.naces == 0) { /* No ACEs on this object */ FSAL_UNSET_MASK(attrib->mask, ATTR_ACL); attrib->acl = NULL; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* Create fsal acl data. */ acldata.aces = (fsal_ace_t *) nfs4_ace_alloc(acldata.naces); LogDebug(COMPONENT_FSAL, "Converting %u aces:", acldata.naces); ace = acldata.aces; for (panace = panacl->aces; panace < panacl->aces + panacl->naces; panace++) { ace->type = panace_info_to_fsal_type(panace->info); if (ace->type == (uint32_t)-1) continue; ace->flag = panace_info_to_fsal_flag(panace->info); ace->perm = panace_perm_to_fsal_perm(panace->permissions); panace_id_to_fsal_id(&panace->identity, ace); fsal_print_ace(COMPONENT_FSAL, NIV_DEBUG, ace); ace++; } /* Create a new hash table entry for fsal acl. */ acl = nfs4_acl_new_entry(&acldata, &status); if (!acl) return fsalstat(ERR_FSAL_FAULT, status); /* Add fsal acl to attribute. */ attrib->acl = acl; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Get file attributes from PanFS * * Get the attributes and convert them into VFS FSAL attributes * * @param[in] panfs_hdl PanFS Object handle to operate on * @param[in] fd Open FD to operate on * @param[in,out] attrib Attributes to get / storage for attributes * @return ERR_FSAL_NO_ERROR on success, other error code on failure */ fsal_status_t PanFS_getattrs(struct panfs_fsal_obj_handle *panfs_hdl, int fd, struct attrlist *attrib) { fsal_status_t st; struct pan_attrs pattrs; struct pan_fs_ace_s paces[PAN_FS_ACL_LEN_MAX]; pattrs.acls.naces = PAN_FS_ACL_LEN_MAX; pattrs.acls.aces = paces; st = panfs_um_get_attr(fd, &pattrs); if (FSAL_IS_ERROR(st)) { FSAL_UNSET_MASK(attrib->mask, ATTR_ACL); attrib->acl = NULL; return fsalstat(ERR_FSAL_NO_ERROR, 0); /*return st;*/ } return panfs_acl_2_fsal_acl(&pattrs.acls, attrib); } /** * @brief Set file attributes into PanFS * * Convert attributes into PanFS format and set them * * @param[in] panfs_hdl PanFS Object handle to operate on * @param[in] fd Open FD to operate on * @param[in,out] attrib Attributes to get / storage for attributes * @return ERR_FSAL_NO_ERROR on success, other error code on failure */ fsal_status_t PanFS_setattrs(struct panfs_fsal_obj_handle *panfs_hdl, int fd, struct attrlist *attrib) { fsal_status_t st; struct pan_attrs pattrs; struct pan_fs_ace_s paces[PAN_FS_ACL_LEN_MAX]; pattrs.acls.naces = PAN_FS_ACL_LEN_MAX; pattrs.acls.aces = paces; st = fsal_acl_2_panfs_acl(attrib, &pattrs.acls); if (FSAL_IS_ERROR(st)) return st; st = panfs_um_set_attr(fd, &pattrs); if (FSAL_IS_ERROR(st)) return st; return fsalstat(ERR_FSAL_NO_ERROR, 0); } nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/panfs/attrs.h000066400000000000000000000056401324272410200211070ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) CohortFS LLC, 2014 * Author: Daniel Gryniewicz dang@cohortfs.com * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* attrs.h * PanFS Attribute handling API */ #ifndef __PANFS_ATTRS_H #define __PANFS_ATTRS_H #include #include #include "panfs_int.h" typedef enum { PAN_ACL_PERM_LIST_DIR = 0x00000001, PAN_ACL_PERM_READ = 0x00000001, PAN_ACL_PERM_WRITE = 0x00000004, PAN_ACL_PERM_CREATE = 0x00000010, PAN_ACL_PERM_APPEND = 0x00000008, PAN_ACL_PERM_CREATE_DIR = 0x00001000, PAN_ACL_PERM_READ_NAMED_ATTRS = 0x00002000, PAN_ACL_PERM_WRITE_NAMED_ATTRS = 0x00004000, PAN_ACL_PERM_EXECUTE = 0x00000002, PAN_ACL_PERM_DELETE_CHILD = 0x00008000, PAN_ACL_PERM_READ_ATTRS = 0x00010000, PAN_ACL_PERM_WRITE_ATTRS = 0x00020000, PAN_ACL_PERM_DELETE = 0x00000020, PAN_ACL_PERM_READ_ACL = 0x00040000, PAN_ACL_PERM_CHANGE_ACL = 0x00000080, PAN_ACL_PERM_TAKE_OWNER = 0x00000100, PAN_ACL_PERM_SYNCHRONIZE = 0x00080000, PAN_ACL_PERM_ALL = 0x003ff3ff } pan_acl_permission_t; typedef enum { /* Type flags (PAN_FS_ACE_TYPE_MASK) */ PAN_FS_ACE_INVALID = 0x0000, PAN_FS_ACE_POS = 0x0001, PAN_FS_ACE_NEG = 0x0002, PAN_FS_ACE_AUDIT = 0x0003, PAN_FS_ACE_ALARM = 0x0004, PAN_FS_ACE_NUM_TYPES = 0x0005, PAN_FS_ACE_INHERIT_FLAG_NONE = 0x0000, /* Interitance flags (PAN_FS_ACE_INHERIT_TYPE_MASK) */ PAN_FS_ACE_OBJECT_INHERIT = 0x0100, PAN_FS_ACE_CONTAINER_INHERIT = 0x0200, PAN_FS_ACE_NO_PROPAGATE_INHERIT = 0x0400, PAN_FS_ACE_INHERIT_ONLY = 0x0800, PAN_FS_ACE_SUCCESSFUL_ACC_ACE_FLAG = 0x1000, PAN_FS_ACE_FAILED_ACC_ACE_FLAG = 0x2000, PAN_FS_ACE_IDENTIFIER_GROUP = 0x4000, PAN_FS_ACE_INHERITED_ACE = 0x8000, PAN_FS_ACE_TYPE_MASK = 0x00ff, PAN_FS_ACE_INHERIT_TYPE_MASK = 0xff00 } pan_fs_ace_info_t; struct pan_fs_acl_s { uint32_t naces; struct pan_fs_ace_s *aces; }; struct pan_attrs { struct pan_fs_acl_s acls; }; struct attrlist; struct panfs_fsal_obj_handle; fsal_status_t PanFS_getattrs(struct panfs_fsal_obj_handle *panfs_hdl, int fd, struct attrlist *attrib); fsal_status_t PanFS_setattrs(struct panfs_fsal_obj_handle *panfs_hdl, int fd, struct attrlist *attrib); #endif /* __PANFS_ATTRS_H */ nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/panfs/handle.c000066400000000000000000000043521324272410200211770ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) CohortFS LLC, 2014 * Author: Daniel Gryniewicz dang@cohortfs.com * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* handle.c * PanFS (file|dir) handle object */ #include "config.h" #include "fsal.h" #include "fsal_convert.h" #include "../vfs_methods.h" #include "panfs.h" #include "attrs.h" static fsal_status_t panfs_getattrs(struct vfs_fsal_obj_handle *vfs_hdl, int fd, attrmask_t request_mask) { struct panfs_fsal_obj_handle *panfs_hdl = OBJ_PANFS_FROM_VFS(vfs_hdl); struct attrlist *attrib = &vfs_hdl->attributes; fsal_status_t st; /*if (FSAL_TEST_MASK(request_mask, ATTR_ACL)) {*/ FSAL_SET_MASK(attrib->mask, ATTR_ACL); st = PanFS_getattrs(panfs_hdl, fd, attrib); if (FSAL_IS_ERROR(st)) return st; /*}*/ return fsalstat(ERR_FSAL_NO_ERROR, 0); } static fsal_status_t panfs_setattrs(struct vfs_fsal_obj_handle *vfs_hdl, int fd, attrmask_t request_mask, struct attrlist *attrib_set) { struct panfs_fsal_obj_handle *panfs_hdl = OBJ_PANFS_FROM_VFS(vfs_hdl); fsal_status_t st; if (FSAL_TEST_MASK(request_mask, ATTR_ACL) && attrib_set->acl) { st = PanFS_setattrs(panfs_hdl, fd, attrib_set); if (FSAL_IS_ERROR(st)) return st; FSAL_SET_MASK(attrib_set->valid_mask, ATTR_ACL); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } void panfs_handle_ops_init(struct panfs_fsal_obj_handle *panfs_hdl) { panfs_hdl->vfs_obj_handle.sub_ops = &panfs_hdl->panfs_ops; panfs_hdl->panfs_ops.getattrs = panfs_getattrs; panfs_hdl->panfs_ops.setattrs = panfs_setattrs; } nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/panfs/main.c000066400000000000000000000131061324272410200206650ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ /* main.c * Module core functions */ #include "config.h" #include /* used for 'dirname' */ #include #include #include #include #include "gsh_list.h" #include "fsal.h" #include "FSAL/fsal_init.h" /* PANFS FSAL module private storage */ /* defined the set of attributes supported with POSIX */ #define PANFS_SUPPORTED_ATTRIBUTES ((const attrmask_t) (ATTRS_POSIX | ATTR_ACL)) struct panfs_fsal_module { struct fsal_module fsal; struct fsal_staticfsinfo_t fs_info; /* panfs_specific_initinfo_t specific_info; placeholder */ }; const char myname[] = "PANFS"; /* filesystem info for PANFS */ static struct fsal_staticfsinfo_t default_posix_info = { .maxfilesize = UINT64_MAX, .maxlink = _POSIX_LINK_MAX, .maxnamelen = 1024, .maxpathlen = 1024, .no_trunc = true, .chown_restricted = true, .case_insensitive = false, .case_preserving = true, .lock_support = false, .lock_support_async_block = false, .named_attr = true, .unique_handles = true, .lease_time = {10, 0}, .acl_support = FSAL_ACLSUPPORT_ALLOW | FSAL_ACLSUPPORT_DENY, .homogenous = true, .supported_attrs = PANFS_SUPPORTED_ATTRIBUTES, .maxread = FSAL_MAXIOSIZE, .maxwrite = FSAL_MAXIOSIZE, .link_supports_permission_checks = false, }; static struct config_item panfs_params[] = { CONF_ITEM_BOOL("link_support", true, fsal_staticfsinfo_t, link_support), CONF_ITEM_BOOL("symlink_support", true, fsal_staticfsinfo_t, symlink_support), CONF_ITEM_BOOL("cansettime", true, fsal_staticfsinfo_t, cansettime), CONF_ITEM_UI64("maxread", 512, FSAL_MAXIOSIZE, FSAL_MAXIOSIZE, fsal_staticfsinfo_t, maxread), CONF_ITEM_UI64("maxwrite", 512, FSAL_MAXIOSIZE, FSAL_MAXIOSIZE, fsal_staticfsinfo_t, maxwrite), CONF_ITEM_MODE("umask", 0, fsal_staticfsinfo_t, umask), CONF_ITEM_BOOL("auth_xdev_export", false, fsal_staticfsinfo_t, auth_exportpath_xdev), CONF_ITEM_MODE("xattr_access_rights", 0400, fsal_staticfsinfo_t, xattr_access_rights), CONFIG_EOL }; struct config_block panfs_param = { .dbus_interface_name = "org.ganesha.nfsd.config.fsal.panfs", .blk_desc.name = "PANFS", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = noop_conf_init, .blk_desc.u.blk.params = panfs_params, .blk_desc.u.blk.commit = noop_conf_commit }; /* private helper for export object */ struct fsal_staticfsinfo_t *vfs_staticinfo(struct fsal_module *hdl) { struct panfs_fsal_module *myself; myself = container_of(hdl, struct panfs_fsal_module, fsal); return &myself->fs_info; } /* Module methods */ /* init_config * must be called with a reference taken (via lookup_fsal) */ static fsal_status_t init_config(struct fsal_module *fsal_hdl, config_file_t config_struct, struct config_error_type *err_type) { struct panfs_fsal_module *panfs_me = container_of(fsal_hdl, struct panfs_fsal_module, fsal); panfs_me->fs_info = default_posix_info; /* copy the consts */ (void) load_config_from_parse(config_struct, &panfs_param, &panfs_me->fs_info, true, err_type); if (!config_error_is_harmless(err_type)) return fsalstat(ERR_FSAL_INVAL, 0); display_fsinfo(&panfs_me->fs_info); LogFullDebug(COMPONENT_FSAL, "Supported attributes constant = 0x%" PRIx64, PANFS_SUPPORTED_ATTRIBUTES); LogFullDebug(COMPONENT_FSAL, "Supported attributes default = 0x%" PRIx64, default_posix_info.supported_attrs); LogDebug(COMPONENT_FSAL, "FSAL INIT: Supported attributes mask = 0x%" PRIx64, panfs_me->fs_info.supported_attrs); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* Internal PANFS method linkage to export object */ fsal_status_t vfs_create_export(struct fsal_module *fsal_hdl, void *parse_node, struct config_error_type *err_type, const struct fsal_up_vector *up_ops); /* Module initialization. * Called by dlopen() to register the module * keep a private pointer to me in myself */ /* my module private storage */ static struct panfs_fsal_module PANFS; /* linkage to the exports and handle ops initializers */ MODULE_INIT void panfs_init(void) { int retval; struct fsal_module *myself = &PANFS.fsal; retval = register_fsal(myself, myname, FSAL_MAJOR_VERSION, FSAL_MINOR_VERSION, FSAL_ID_PANFS); if (retval != 0) { fprintf(stderr, "PANFS module failed to register"); return; } myself->m_ops.create_export = vfs_create_export; myself->m_ops.init_config = init_config; } MODULE_FINI void panfs_unload(void) { int retval; retval = unregister_fsal(&PANFS.fsal); if (retval != 0) { fprintf(stderr, "PANFS module failed to unregister"); return; } } nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/panfs/mds.c000066400000000000000000000261331324272410200205300ustar00rootroot00000000000000/* * Copyright © from 2012 Panasas Inc. * Author: Boaz Harrosh * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, email to the Free Software * Foundation, Inc., licensing@fsf.org * * ------------- */ #include #include #include "mds.h" #include "panfs_um_pnfs.h" #include "fsal_up.h" #include "../vfs_methods.h" /** * @file FSAL_VFS/pnfs_panfs/mds.c * @author Boaz Harrosh * * @brief pNFS Metadata Server Operations for the PanFS FSAL * * This file implements the layoutget, layoutreturn, layoutcommit, * getdeviceinfo operations support for the PanFS FSAL. * * In general all this file does is Translates from export_pub / fsal_obj_handle * Into an "fd" and calles the appropreate panfs_um_pnfs.h function. * * This file is edited with the LINUX coding style: (Will be enforced) * - Tab characters of 8 spaces wide * - Lines not longer then 80 chars * - etc ... (See linux Documentation/CodingStyle.txt) */ static void _XDR_2_ioctlxdr_read_begin(XDR *xdr, struct pan_ioctl_xdr *pixdr) { pixdr->xdr_buff = xdr_inline_encode(xdr, 0); pixdr->xdr_alloc_len = xdr_size_inline(xdr); pixdr->xdr_len = 0; LogDebug(COMPONENT_FSAL, "alloc_len=%d xdr_buff=%p", pixdr->xdr_alloc_len, pixdr->xdr_buff); } /* We need to update the XDR with the encoded bytes */ static void _XDR_2_ioctlxdr_read_end(XDR *xdr, struct pan_ioctl_xdr *pixdr) { void *p = xdr_inline_encode(xdr, pixdr->xdr_len); LogDebug(COMPONENT_FSAL, "xdr_len=%d xdr_buff_end=%p", pixdr->xdr_len, p); } static void _XDR_2_ioctlxdr_write(XDR *xdr, struct pan_ioctl_xdr *pixdr) { if (xdr) { pixdr->xdr_len = xdr_getpos(xdr); xdr_setpos(xdr, 0); /* return the head of the buffer, and reset the pos again */ pixdr->xdr_buff = xdr_inline_decode(xdr, pixdr->xdr_len); } else { /* ensure NULL for next test */ pixdr->xdr_buff = NULL; } if (!pixdr->xdr_buff) { /* ensure 0 for next assignment */ pixdr->xdr_len = 0; } pixdr->xdr_alloc_len = pixdr->xdr_len; LogDebug(COMPONENT_FSAL, "xdr_len=%d xdr_buff=%p", pixdr->xdr_len, pixdr->xdr_buff); } /* * Given a PanFS fsal_export. Return the export's root directory file-descriptor */ static inline int _get_root_fd(struct fsal_module *fsal_hdl) { struct fsal_export *exp_hdl; exp_hdl = glist_first_entry(&fsal_hdl->exports, struct fsal_export, exports); return vfs_get_root_fd(exp_hdl); } /* * Given a PanFS fsal_obj_handle. Return the file-descriptor of this object. * The passed obj_hdl must be of a regular file that was pre-opened for * read/write. */ static inline int _get_obj_fd(struct fsal_obj_handle *obj_hdl) { struct vfs_fsal_obj_handle *myself; myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); if (myself->u.file.fd.openflags != FSAL_O_CLOSED) return myself->u.file.fd.fd; else return -1; } /*================================= fsal ops ===============================*/ static size_t fs_da_addr_size(struct fsal_module *fsal_hdl) { LogFullDebug(COMPONENT_FSAL, "Ret => ~0UL"); return ~0UL; } static nfsstat4 getdeviceinfo(struct fsal_module *fsal_hdl, XDR *da_addr_body, const layouttype4 type, const struct pnfs_deviceid *deviceid) { struct pan_ioctl_xdr pixdr; int fd = _get_root_fd(fsal_hdl); nfsstat4 ret; _XDR_2_ioctlxdr_read_begin(da_addr_body, &pixdr); ret = panfs_um_getdeviceinfo(fd, &pixdr, type, deviceid); if (!ret) _XDR_2_ioctlxdr_read_end(da_addr_body, &pixdr); LogFullDebug(COMPONENT_FSAL, "deviceid(%"PRIx64") ret => %d", deviceid->devid, ret); return ret; } /*================================= export ops ===============================*/ static nfsstat4 getdevicelist(struct fsal_export *exp_hdl, layouttype4 type, void *opaque, bool(*cb) (void *opaque, const uint64_t id), struct fsal_getdevicelist_res *res) { res->eof = true; LogFullDebug(COMPONENT_FSAL, "ret => %d", NFS4_OK); return NFS4_OK; } static void fs_layouttypes(struct fsal_export *exp_hdl, int32_t *count, const layouttype4 **types) { static const layouttype4 supported_layout_type = LAYOUT4_OSD2_OBJECTS; *types = &supported_layout_type; *count = 1; LogFullDebug(COMPONENT_FSAL, "count = 1"); } uint32_t fs_layout_blocksize(struct fsal_export *exp_hdl) { LogFullDebug(COMPONENT_FSAL, "ret => 9 * 64 * 1024"); /* Should not be called */ return 9 * 64 * 1024; } static uint32_t fs_maximum_segments(struct fsal_export *exp_hdl) { LogFullDebug(COMPONENT_FSAL, "ret => 1"); return 1; } /* * @return ~0UL means client's maximum */ static size_t fs_loc_body_size(struct fsal_export *exp_hdl) { LogFullDebug(COMPONENT_FSAL, "ret => ~0UL"); return ~0UL; } /*================================= handle ops ===============================*/ static nfsstat4 layoutget(struct fsal_obj_handle *obj_hdl, struct req_op_context *req_ctx, XDR *loc_body, const struct fsal_layoutget_arg *arg, struct fsal_layoutget_res *res) { struct vfs_fsal_obj_handle *myself = container_of(obj_hdl, typeof(*myself), obj_handle); struct pan_ioctl_xdr pixdr; uint64_t clientid = req_ctx->clientid ? *req_ctx->clientid : 0; nfsstat4 ret; res->last_segment = true; _XDR_2_ioctlxdr_read_begin(loc_body, &pixdr); /* Take read lock on object to protect file descriptor. */ PTHREAD_RWLOCK_rdlock(&obj_hdl->obj_lock); ret = panfs_um_layoutget(_get_obj_fd(obj_hdl), &pixdr, clientid, myself, arg, res); if (!ret) _XDR_2_ioctlxdr_read_end(loc_body, &pixdr); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); LogDebug(COMPONENT_FSAL, "layout[0x%lx,0x%lx,0x%x] ret => %d", res->segment.offset, res->segment.length, res->segment.io_mode, ret); return ret; } static nfsstat4 layoutreturn(struct fsal_obj_handle *obj_hdl, struct req_op_context *req_ctx, XDR *lrf_body, const struct fsal_layoutreturn_arg *arg) { struct pan_ioctl_xdr pixdr; nfsstat4 ret; LogDebug(COMPONENT_FSAL, "reclaim=%d return_type=%d fsal_seg_data=%p dispose=%d last_segment=%d ncookies=%zu", arg->circumstance, arg->return_type, arg->fsal_seg_data, arg->dispose, arg->last_segment, arg->ncookies); _XDR_2_ioctlxdr_write(lrf_body, &pixdr); /* Take read lock on object to protect file descriptor. */ PTHREAD_RWLOCK_rdlock(&obj_hdl->obj_lock); ret = panfs_um_layoutreturn(_get_obj_fd(obj_hdl), &pixdr, arg); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); LogDebug(COMPONENT_FSAL, "layout[0x%lx,0x%lx,0x%x] ret => %d", arg->cur_segment.offset, arg->cur_segment.length, arg->cur_segment.io_mode, ret); return ret; } static nfsstat4 layoutcommit(struct fsal_obj_handle *obj_hdl, struct req_op_context *req_ctx, XDR *lou_body, const struct fsal_layoutcommit_arg *arg, struct fsal_layoutcommit_res *res) { struct pan_ioctl_xdr pixdr; nfsstat4 ret; _XDR_2_ioctlxdr_write(lou_body, &pixdr); /* Take read lock on object to protect file descriptor. */ PTHREAD_RWLOCK_rdlock(&obj_hdl->obj_lock); ret = panfs_um_layoutcommit(_get_obj_fd(obj_hdl), &pixdr, arg, res); PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); LogDebug(COMPONENT_FSAL, "layout[0x%lx,0x%lx,0x%x] last_write=0x%lx ret => %d", arg->segment.offset, arg->segment.length, arg->segment.io_mode, arg->last_write, ret); return ret; } static void initiate_recall(struct vfs_fsal_obj_handle *myself, struct pnfs_segment *seg, void *r_cookie) { struct pnfs_segment up_segment = *seg; struct gsh_buffdesc handle = { .addr = myself->handle->handle_data, .len = myself->handle->handle_len }; up_segment.io_mode = LAYOUTIOMODE4_ANY; /*TODO: seg->io_mode */ /* For layoutrecall up_ops are probably set to default recieved at * vfs_create_export */ myself->up_ops->layoutrecall(myself->up_ops->export, &handle, LAYOUT4_OSD2_OBJECTS, false, &up_segment, r_cookie, NULL); } struct _recall_thread { pthread_t thread; int fd; /** @toodo: not sure if this needs to be volatile */ volatile bool stop; }; static void *callback_thread(void *callback_info) { struct _recall_thread *_rt = callback_info; enum { E_MAX_EVENTS = 128 }; struct pan_cb_layoutrecall_event events[E_MAX_EVENTS]; int err = 0; while (!_rt->stop) { int num_events = 0; int e; err = panfs_um_recieve_layoutrecall(_rt->fd, events, E_MAX_EVENTS, &num_events); if (err) { LogDebug(COMPONENT_FSAL, "callback thread: => %d (%s)", err, strerror(err)); break; } for (e = 0; e < num_events; ++e) { struct vfs_fsal_obj_handle *myself = events[e].recall_file_info; struct pnfs_segment seg = events[e].seg; void *r_cookie = events[e].cookie; LogDebug(COMPONENT_FSAL, "%d] layout[0x%lx,0x%lx,0x%x] myself=%p r_cookie=%p", e, seg.offset, seg.length, seg.io_mode, myself, r_cookie); initiate_recall(myself, &seg, r_cookie); } } return (void *)(long)err; } static int _start_callback_thread(int root_fd, void **pnfs_data) { struct _recall_thread *_rt; int err; _rt = gsh_calloc(1, sizeof(*_rt)); _rt->fd = root_fd; err = pthread_create(&_rt->thread, NULL, &callback_thread, _rt); if (err) { LogCrit(COMPONENT_FSAL, "Could not create callback thread %d: %s", err, strerror(err)); goto error; } *pnfs_data = _rt; LogDebug(COMPONENT_FSAL, "Started callback thread 0x%lx", (long)_rt->thread); return 0; error: gsh_free(_rt); return err; } static void _stop_callback_thread(void *td) { struct _recall_thread *_rt = td; void *tret; _rt->stop = true; panfs_um_cancel_recalls(_rt->fd, 0); pthread_join(_rt->thread, &tret); LogDebug(COMPONENT_FSAL, "Stopped callback thread. Join ret => %ld", (long)tret); gsh_free(_rt); } /*============================== initialization ==============================*/ void export_ops_pnfs(struct export_ops *ops) { ops->getdevicelist = getdevicelist; ops->fs_layouttypes = fs_layouttypes; ops->fs_layout_blocksize = fs_layout_blocksize; ops->fs_maximum_segments = fs_maximum_segments; ops->fs_loc_body_size = fs_loc_body_size; LogFullDebug(COMPONENT_FSAL, "Init'd export vector"); } void handle_ops_pnfs(struct fsal_obj_ops *ops) { ops->layoutget = layoutget; ops->layoutreturn = layoutreturn; ops->layoutcommit = layoutcommit; LogDebug(COMPONENT_FSAL, "Init'd handle vector"); } void fsal_ops_pnfs(struct fsal_ops *ops) { ops->getdeviceinfo = getdeviceinfo; ops->fs_da_addr_size = fs_da_addr_size; LogDebug(COMPONENT_FSAL, "Init'd fsal vector"); } int pnfs_panfs_init(int root_fd, void **pnfs_data /*OUT*/) { int err = _start_callback_thread(root_fd, pnfs_data); return err; } void pnfs_panfs_fini(void *pnfs_data) { if (!pnfs_data) return; _stop_callback_thread(pnfs_data); } nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/panfs/mds.h000066400000000000000000000032631324272410200205340ustar00rootroot00000000000000/* * Copyright © from 2012 Panasas Inc. * Author: Boaz Harrosh * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, email to the Free Software * Foundation, Inc., licensing@fsf.org * * ------------- */ /** * @file mds.h * @author Boaz Harrosh * * @brief Declare mds.c externals * * This file is edited with the LINUX coding style: (Will be enforced) * - Tab characters of 8 spaces wide * - Lines not longer then 80 chars * - etc ... (See linux Documentation/CodingStyle.txt) */ #include "fsal.h" /*============================== initialization ==============================*/ /* Need to call this to initialize export_ops for pnfs */ void export_ops_pnfs(struct export_ops *ops); /* Need to call this to initialize obj_ops for pnfs */ void handle_ops_pnfs(struct fsal_obj_ops *ops); /* Need to call this to initialize ops for pnfs */ void fsal_ops_pnfs(struct fsal_ops *ops); /* Start the up calls thread for LAYOUT RECALLS*/ int pnfs_panfs_init(int root_fd, void **pnfs_data /*OUT*/); /* Stop and clean the up calls thread*/ void pnfs_panfs_fini(void *pnfs_data); nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/panfs/panfs.h000066400000000000000000000032311324272410200210530ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) CohortFS LLC, 2014 * Author: Daniel Gryniewicz dang@cohortfs.com * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* panfs.h * PanFS FSAL API */ #ifndef PANFS_H #define PANFS_H #include "../vfs_methods.h" struct panfs_fsal_export { struct vfs_fsal_export vfs_export; bool pnfs_enabled; void *pnfs_data; }; #define EXPORT_PANFS_FROM_VFS(vfs) \ container_of((vfs), struct panfs_fsal_export, vfs_export) #define EXPORT_PANFS_FROM_FSAL(fsal) \ EXPORT_PANFS_FROM_VFS(EXPORT_VFS_FROM_FSAL((fsal))) struct panfs_fsal_obj_handle { struct vfs_fsal_obj_handle vfs_obj_handle; struct vfs_subfsal_obj_ops panfs_ops; }; #define OBJ_PANFS_FROM_VFS(vfs) \ container_of((vfs), struct panfs_fsal_obj_handle, vfs_obj_handle) #define OBJ_PANFS_FROM_FSAL(fsal) \ OBJ_PANFS_FROM_VFS(OBJ_VFS_FROM_FSAL((fsal))) void panfs_handle_ops_init(struct panfs_fsal_obj_handle *panfs_hdl); #endif /* PANFS_H */ nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/panfs/panfs_int.h000066400000000000000000000162531324272410200217350ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) 2014 Panasas Inc. * Author: Daniel Gryniewicz dang@cohortfs.com * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* panfs_int.h * Internal PanFS structs/defines */ #ifndef __PANFS_INT_H #define __PANFS_INT_H typedef int pan_bool_t; #define PAN_FALSE ((pan_bool_t)0) #define PAN_TRUE ((pan_bool_t) 1) typedef uint64_t pan_stor_dev_id_t; typedef uint32_t pan_stor_obj_grp_id_t; typedef uint64_t pan_stor_obj_uniq_t; /* Object ID */ struct pan_stor_obj_id_s { pan_stor_dev_id_t dev_id; pan_stor_obj_uniq_t obj_id; pan_stor_obj_grp_id_t grp_id; }; typedef struct pan_stor_obj_id_s pan_stor_obj_id_t; /* Object map hint */ typedef uint64_t pan_sm_obj_map_hint_comp_t; typedef pan_sm_obj_map_hint_comp_t pan_sm_obj_map_hint_comp_a[2]; struct pan_sm_obj_map_hint_s { pan_sm_obj_map_hint_comp_a comp; }; typedef struct pan_sm_obj_map_hint_s pan_sm_obj_map_hint_t; /* Time */ typedef struct pan_timespec_s pan_timespec_t; struct pan_timespec_s { uint32_t ts_sec; uint32_t ts_nsec; }; /* User */ typedef uint32_t pan_identity_type_t; #define PAN_IDENTITY_UNKNOWN ((pan_identity_type_t) 0UL) #define PAN_IDENTITY_UNIX_USER ((pan_identity_type_t) 1UL) #define PAN_IDENTITY_WIN_USER ((pan_identity_type_t) 2UL) #define PAN_IDENTITY_PAN_USER ((pan_identity_type_t) 3UL) #define PAN_IDENTITY_UNIX_GROUP ((pan_identity_type_t) 4UL) #define PAN_IDENTITY_WIN_GROUP ((pan_identity_type_t) 5UL) #define PAN_IDENTITY_PAN_GROUP ((pan_identity_type_t) 6UL) #define PAN_IDENTITY_MGR ((pan_identity_type_t) 7UL) #define PAN_IDENTITY_BLADE ((pan_identity_type_t) 8UL) #define PAN_IDENTITY_MAX_VALID ((pan_identity_type_t) 8UL) #define PAN_IDENTITY_SAVED_UNKNOWN (PAN_IDENTITY_MAX_VALID + 1) #define PAN_SID_SUB_AUTH_MAX ((uint32_t) 7UL) #define PAN_SID_HEADER_LEN ((uint32_t) 8UL) typedef uint8_t pan_id_auth_t[6]; typedef uint32_t pan_sub_auths_t[7]; typedef uint8_t pan_brick_serial_t[32]; struct pan_sid_s { uint8_t sid_rev_num; uint8_t num_auths; pan_id_auth_t id_auth; pan_sub_auths_t sub_auths; }; typedef struct pan_sid_s pan_sid_t; #define PAN_IDENTITY_UNKNOWN_NULL ((uint32_t) 0UL) #define PAN_IDENTITY_UNKNOWN_USER ((uint32_t) 1UL) #define PAN_IDENTITY_UNKNOWN_GROUP ((uint32_t) 2UL) #define PAN_IDENTITY_UNKNOWN_VOLUME ((uint32_t) 3UL) #define PAN_IDENTITY_EVERYONE_GROUP_ID ((uint32_t) 1UL) #define PAN_IDENTITY_TEMP_PRIMARY_GROUP_ID ((uint32_t) 2UL) #define PAN_IDENTITY_CIFS_ADMIN_GROUP_ID ((uint32_t) 3UL) #define PAN_IDENTITY_TEMP_OWNER_ID ((uint32_t) 1UL) struct pan_identity_s { pan_identity_type_t type; union { uint32_t unknown; uint32_t uid; uint32_t gid; pan_sid_t user_sid; pan_sid_t group_sid; uint32_t pan_uid; uint32_t pan_gid; uint64_t mgr_id; pan_brick_serial_t blade_serial; } u; }; typedef struct pan_identity_s pan_identity_t; /* Local ACL representation for ls -al */ struct pan_fs_client_llapi_access_acl_local_s { uid_t owner; gid_t group; mode_t mode; }; struct pan_fs_client_llapi_access_s { struct pan_fs_client_llapi_access_acl_local_s local_acl; }; typedef struct pan_fs_client_llapi_access_s pan_fs_client_llapi_access_t; /* File layout */ struct pan_agg_simple_header_s { uint8_t unused; }; typedef struct pan_agg_simple_header_s pan_agg_simple_header_t; struct pan_agg_raid1_header_s { uint16_t num_comps; }; typedef struct pan_agg_raid1_header_s pan_agg_raid1_header_t; struct pan_agg_raid0_header_s { uint16_t num_comps; uint32_t stripe_unit; }; typedef struct pan_agg_raid0_header_s pan_agg_raid0_header_t; struct pan_agg_raid5_left_header_s { uint16_t num_comps; uint32_t stripe_unit0; uint32_t stripe_unit1; uint32_t stripe_unit2; }; typedef struct pan_agg_raid5_left_header_s pan_agg_raid5_left_header_t; struct pan_agg_policy_raid5_left_header_s { uint8_t stripe_width_policy; uint8_t stripe_unit_policy; }; typedef struct pan_agg_policy_raid5_left_header_s pan_agg_policy_raid5_left_header_t; struct pan_agg_grp_raid5_left_header_s { uint16_t num_comps; uint32_t stripe_unit; uint16_t rg_width; uint16_t rg_depth; uint8_t group_layout_policy; }; typedef struct pan_agg_grp_raid5_left_header_s pan_agg_grp_raid5_left_header_t; struct pan_agg_grp_raidn_left_header_s { uint16_t num_comps; uint32_t stripe_unit; uint16_t rg_width; uint16_t rg_depth; uint8_t max_faults; uint8_t encoding; }; typedef struct pan_agg_grp_raidn_left_header_s pan_agg_grp_raidn_left_header_t; #define PAN_AGG_NULL_MAP ((uint8_t) 0x00) #define PAN_AGG_SIMPLE ((uint8_t) 0x01) #define PAN_AGG_RAID1 ((uint8_t) 0x02) #define PAN_AGG_RAID0 ((uint8_t) 0x03) #define PAN_AGG_RAID5_LEFT ((uint8_t) 0x04) #define PAN_AGG_POLICY_RAID5_LEFT ((uint8_t) 0x05) #define PAN_AGG_GRP_RAID5_LEFT ((uint8_t) 0x06) #define PAN_AGG_GRP_RAIDN_LEFT ((uint8_t) 0x07) #define PAN_AGG_MINTYPE ((uint8_t) 0x01) #define PAN_AGG_MAXTYPE ((uint8_t) 0x07) struct pan_agg_layout_hdr_s { uint8_t type; uint8_t pad[3]; union { uint64_t null; pan_agg_simple_header_t simple; pan_agg_raid1_header_t raid1; pan_agg_raid0_header_t raid0; pan_agg_raid5_left_header_t raid5_left; pan_agg_policy_raid5_left_header_t policy_raid5_left; pan_agg_grp_raid5_left_header_t grp_raid5_left; pan_agg_grp_raidn_left_header_t grp_raidn_left; } hdr; }; typedef struct pan_agg_layout_hdr_s pan_agg_layout_hdr_t; /* ACE */ struct pan_fs_ace_s { pan_identity_t identity; uint32_t permissions; uint16_t info; }; typedef struct pan_fs_ace_s pan_fs_ace_t; /* ACL defines */ #define PAN_FS_ACL_VERSION_MIN ((uint32_t) 1UL) #define PAN_FS_ACL_VERSION_MAX ((uint32_t) 2UL) #define PAN_FS_ACL_VERSION ((uint32_t) 2UL) #define PAN_FS_ACL_LEN_MAX ((uint16_t) 128U) /* Object flags */ #define PAN_FS_OBJ_F_NONE ((uint64_t)0) #define PAN_FS_OBJ_F_FS_ARCHIVE ((uint64_t)1) #define PAN_FS_OBJ_F_FS_HIDDEN ((uint64_t)2) #define PAN_FS_OBJ_F_FS_SYSTEM ((uint64_t)4) #define PAN_FS_OBJ_F_FS_DO_NOT_CACHE ((uint64_t)8) #define PAN_FS_OBJ_F_FS_SETUID ((uint64_t)16) #define PAN_FS_OBJ_F_FS_SETGID ((uint64_t)32) #define PAN_FS_OBJ_F_FS_STICKY ((uint64_t)64) #define PAN_FS_OBJ_F_FS_READONLY ((uint64_t)128) #define PAN_FS_OBJ_F_FS_CW_OPEN ((uint64_t)256) #define PAN_FS_OBJ_F_FS_TIER0 ((uint64_t)512) #define PAN_FS_OBJ_F_FM_DIR_REALM_ROOT ((uint64_t)4294967296) #define PAN_FS_OBJ_F_FM_DIR_VOLUME_ROOT ((uint64_t)8589934592) #define PAN_FS_OBJ_F_FM_DIR_DO_NOT_HASH ((uint64_t)17179869184) #define PAN_FS_OBJ_F_FM_DIR_83_NAMES ((uint64_t)34359738368) #define PAN_FS_OBJ_F_FM_ACL_V2 ((uint64_t)68719476736) #define PAN_FS_OBJ_F_FM_RESERVED ((uint64_t)18446744069414584320) #endif /* __PANFS_INT_H */ nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/panfs/panfs_pnfs_ioctl.h000066400000000000000000000247441324272410200233070ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * DirectFlow IOCTL API for pNFS * * Copyright (C) from 2012 Panasas Inc. All rights reserved. * * Authors: * Boaz Harrosh * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * */ #ifndef __PAN_FS_PNFS_API_H__ #define __PAN_FS_PNFS_API_H__ #include "panfs_int.h" #include "fsal_pnfs.h" #ifndef KERNEL #if (__FreeBSD__ > 0) #include #endif /* (__FreeBSD__ > 0) */ #if (__linux__ > 0) #include #endif /* (__linux__ > 0) */ #endif /* !defined(KERNEL) */ /* Taken from pan_fs_client_sdk.h */ #define PAN_FS_CLIENT_SDK_IOCTL ((unsigned int)0x24) struct pan_ioctl_hdr { uint32_t size; /* unused */ uint32_t nfsstat; /* HOST ORDER nfsstat4 enum */ }; struct pan_ioctl_xdr { void *xdr_buff; uint32_t xdr_alloc_len; uint32_t xdr_len; }; /** * @brief Grant a layout segment. * * This IOCTL is called by nfs41_op_layoutget. * * @param[out] loc_body An XDR stream to which the layout specific portion * of the granted layout segment is encoded. * @param[in] arg Input arguments of the function * @param[in,out] res In/out and output arguments of the function * * Valid error codes in RFC 5661, pp. 366-7. */ struct pan_layoutget_ioctl { struct pan_ioctl_hdr hdr; /* IN/OUT */ struct pan_ioctl_xdr loc_body; /* IN/OUT */ uint64_t clientid; /* IN */ void *recall_file_info; /* IN */ const struct fsal_layoutget_arg *arg; /* IN */ struct fsal_layoutget_res *res; /* IN/OUT */ }; #define PAN_FS_CLIENT_PNFS_LAYOUTGET \ _IOWR(PAN_FS_CLIENT_SDK_IOCTL, 100, struct pan_layoutget_ioctl) /** * @brief Get information about a pNFS device * * This IOCTL returns device information at the @c da_addr_body stream. * * @param[out] da_addr_body An XDR stream to which recieves * the layout type-specific information * corresponding to the deviceid. * @param[in] type The type of layout that specified the * device * @param[in] deviceid The device to look up * * @return Valid error codes in RFC 5661, p. 365. */ struct pan_getdeviceinfo_ioctl { struct pan_ioctl_hdr hdr; struct pan_ioctl_xdr da_addr_body; /* input only params */ const layouttype4 type; const struct pnfs_deviceid deviceid; }; #define PAN_FS_CLIENT_PNFS_DEVICEINFO \ _IOWR(PAN_FS_CLIENT_SDK_IOCTL, 101, struct pan_getdeviceinfo_ioctl) /** * @brief Potentially return one layout segment * * This function is called once on each segment matching the IO mode * and intersecting the range specified in a LAYOUTRETURN operation or * for all layouts corresponding to a given stateid on last close, * leas expiry, or a layoutreturn with a return-type of FSID or ALL. * Whther it is called in the former or latter case is indicated by * the synthetic flag in the arg structure, with synthetic being true * in the case of last-close or lease expiry. * * If arg->dispose is true, all resources associated with the * layout must be freed. * * @param[in] lrf_body In the case of a non-synthetic return, this is * an XDR stream corresponding to the layout * type-specific argument to LAYOUTRETURN. In * the case of a synthetic or bulk return, * this is a NULL pointer. * @param[in] arg Input arguments of the function * * @return Valid error codes in RFC 5661, p. 367. */ struct pan_layoutreturn_ioctl { struct pan_ioctl_hdr hdr; /* IN/OUT */ struct pan_ioctl_xdr lrf_body; /* IN */ const struct fsal_layoutreturn_arg *arg; /* IN */ }; #define PAN_FS_CLIENT_PNFS_LAYOUTRETURN \ _IOWR(PAN_FS_CLIENT_SDK_IOCTL, 102, struct pan_layoutreturn_ioctl) /** * @brief Commit on a writable layout * * @param[in] lou_body An XDR stream containing the layout * type-specific portion of the LAYOUTCOMMIT * arguments. * @param[in] arg Input arguments of the function * @param[in,out] res In/out and output arguments of the function * * @return Valid error codes in RFC 5661, p. 366. */ struct pan_layoutcommit_ioctl { struct pan_ioctl_hdr hdr; /* IN/OUT */ struct pan_ioctl_xdr lou_body; /* IN */ const struct fsal_layoutcommit_arg *arg; /* IN */ struct fsal_layoutcommit_res *res; /* OUT */ }; #define PAN_FS_CLIENT_PNFS_LAYOUTCOMMIT \ _IOWR(PAN_FS_CLIENT_SDK_IOCTL, 103, struct pan_layoutcommit_ioctl) /** * @brief Retrieve next layout Recall * * @param[out] events An array of recall events to recieve one * or more events to recall. * @param[in] max_events Max elements at events array * @param[out] num_events Num of valid events returned * */ struct pan_cb_layoutrecall_ioctl { struct pan_ioctl_hdr hdr; /* IN/OUT */ struct pan_cb_layoutrecall_event { struct pnfs_segment seg; void *recall_file_info; void *cookie; uint64_t clientid; uint32_t flags; } *events; /* OUT */ uint32_t max_events; /* IN */ uint32_t num_events; /* OUT */ }; #define PAN_FS_CLIENT_PNFS_LAYOUTRECALL \ _IOWR(PAN_FS_CLIENT_SDK_IOCTL, 104, struct pan_cb_layoutrecall_ioctl) /** * @brief Tell Kernel to release any callback threads * */ struct pan_cancel_recalls_ioctl { struct pan_ioctl_hdr hdr; /* IN/OUT */ /* debug_magic must be zero or else ... */ uint32_t debug_magic; /* IN */ }; #define PAN_FS_CLIENT_PNFS_CANCEL_RECALLS \ _IOWR(PAN_FS_CLIENT_SDK_IOCTL, 105, struct pan_cancel_recalls_ioctl) /** * @brief Lookup a file in PanFS * */ #define PAN_FS_CLIENT_IOC_LOOKUP_NAME_SIZE 256 struct pan_fs_client_ioctl_lookup_args_s { char name[PAN_FS_CLIENT_IOC_LOOKUP_NAME_SIZE]; pan_bool_t target_found; uint32_t target_pannode_type; pan_stor_obj_id_t target_obj_id; }; #define PAN_FS_CLIENT_IOC_LOOKUP \ _IOWR(PAN_FS_CLIENT_SDK_IOCTL, 11, \ struct pan_fs_client_ioctl_lookup_args_s) /** * @brief Get ACL attributes for a file */ struct pan_fs_client_ioctl_get_attr_args_s { pan_stor_obj_id_t obj_id; /**< Key OUT */ pan_sm_obj_map_hint_t map_hint; /**< unused */ uint32_t flags; /**< Key IN */ uint64_t storage_length; uint64_t capacity_used; pan_timespec_t data_modified_time; pan_timespec_t attr_modified_time; pan_timespec_t obj_creation_time; uint16_t obj_type; uint64_t obj_flags; pan_identity_t owner; pan_identity_t primary_group; pan_fs_client_llapi_access_t access_item; uint64_t mgr_id; uint64_t link_count; pan_agg_layout_hdr_t agg_layout_hdr; union { struct { uint16_t num_components_created; } file; struct { uint32_t major; uint32_t minor; } dev; struct { pan_agg_layout_hdr_t def_layout_hdr; pan_stor_obj_id_t parent_obj_id; uint16_t dir_version; } dir; } spec_attr; /* ACL */ uint16_t num_aces; pan_fs_ace_t panfs_acl[PAN_FS_ACL_LEN_MAX]; uint32_t acl_version; }; typedef struct pan_fs_client_ioctl_get_attr_args_s pan_fs_client_ioctl_get_attr_args_t; /* Bits for PAN_FS_CLIENT_IOC_ATTR_GET flags. * * PAN_FS_CLIENT_IOCTL_GET_F__SORT_V1_ACL * The client should sort a V1 ACL, if witnessed. * PAN_FS_CLIENT_IOCTL_GET_F__SORT_V1_ACL * The V2 ACL will be cached in the ACL cache (gateway only). Caching should * only be requested if sorting is also requested. */ #define PAN_FS_CLIENT_IOCTL_GET_F__NONE 0x0000 #define PAN_FS_CLIENT_IOCTL_GET_F__GET_CACHED 0x0001 #define PAN_FS_CLIENT_IOCTL_GET_F__OPT_ATTRS 0x0002 #define PAN_FS_CLIENT_IOCTL_GET_F__SORT_V1_ACL 0x0004 #define PAN_FS_CLIENT_IOCTL_GET_F__CACHE_ACL 0x0008 /* Implies SORT_V1_ACL */ struct pan_fs_client_ioctl_get_attr_args_holder_s { pan_fs_client_ioctl_get_attr_args_t *get_attr_args; }; #define PAN_FS_CLIENT_IOC_ATTR_GET \ _IOWR(PAN_FS_CLIENT_SDK_IOCTL, 1, \ struct pan_fs_client_ioctl_get_attr_args_holder_s) /** * @brief Set ACL attributes for a file */ /** * An ioctl command for setting PanFS attributes. PanFS specific ACLs can also * be set using this ioctl. * * @author ssubbarayan * @version 1.0 * * @param data structure for passing data * @see #pan_fs_client_ioctl_set_attr_args_t * * @since 1.0 */ /* Bits to determine what attributes are being set */ #define PAN_FS_CLIENT_IOCTL_SET_ATTR_LENGTH (1<<1) #define PAN_FS_CLIENT_IOCTL_SET_ATTR_TIME_DATA_MOD (1<<2) #define PAN_FS_CLIENT_IOCTL_SET_ATTR_TIME_ATTR_MOD (1<<3) #define PAN_FS_CLIENT_IOCTL_SET_ATTR_AGG_DIR_DEF_LAYOUT (1<<4) #define PAN_FS_CLIENT_IOCTL_SET_ATTR_OBJ_FLAGS (1<<5) #define PAN_FS_CLIENT_IOCTL_SET_ATTR_OBJ_FLAGS_MASK (1<<6) #define PAN_FS_CLIENT_IOCTL_SET_ATTR_OWNER (1<<7) #define PAN_FS_CLIENT_IOCTL_SET_ATTR_PRIMARY_GROUP (1<<8) #define PAN_FS_CLIENT_IOCTL_SET_ATTR_ACL (1<<9) #define PAN_FS_CLIENT_IOCTL_SET_ATTR_ACL_REPLACE (1<<10) /* * Note: acl_version must be provided if ATTR_ACL_REPLACE is being done. The * acl_version should be set to the version of the ACL being provided. If the * ACL being set came from another panfs object, the acl_version can be * retrieved by an ioctl getattr on that object. */ typedef struct pan_fs_client_ioctl_set_attr_args_s pan_fs_client_ioctl_set_attr_args_t; struct pan_fs_client_ioctl_set_attr_args_s { uint32_t attr_mask; /* Length of the object as it appears on the storage media */ uint64_t storage_length; pan_timespec_t data_modified_time; pan_timespec_t attr_modified_time; pan_agg_layout_hdr_t dir_def_layout; /* File Manager attributes */ uint64_t obj_flags; uint64_t pos_obj_flags; uint64_t neg_obj_flags; pan_identity_t owner; pan_identity_t primary_group; /* ACL */ uint16_t num_aces; pan_fs_ace_t panfs_acl[PAN_FS_ACL_LEN_MAX]; uint32_t acl_version; }; struct pan_fs_client_ioctl_set_attr_args_holder_s { pan_fs_client_ioctl_set_attr_args_t *set_attr_args; }; #define PAN_FS_CLIENT_IOC_ATTR_SET \ _IOWR(PAN_FS_CLIENT_SDK_IOCTL, 3, \ struct pan_fs_client_ioctl_set_attr_args_holder_s) #endif /* __PAN_FS_PNFS_API_H__ */ nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/panfs/panfs_um_pnfs.c000066400000000000000000000134271324272410200226050ustar00rootroot00000000000000/* * Copyright © from 2012 Panasas Inc. * Author: Boaz Harrosh * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, email to the Free Software * Foundation, Inc., licensing@fsf.org * * ------------- */ #include #include #include #include #include "fsal_convert.h" /* Implementing this */ #include "panfs_um_pnfs.h" /* With this */ #include "panfs_pnfs_ioctl.h" #include "attrs.h" /** * @file panfs_um_pnfs.c * @author Boaz Harrosh * * @brief pNFS IOCTL wrapper library Implementation * * Given an open file descriptor, these functions below will setup the * appropriate IOCTL call into the panfs.ko filesystem. * * This file is edited with the LINUX coding style: (Will be enforced) * - Tab characters of 8 spaces wide * - Lines not longer then 80 chars * - etc ... (See linux Documentation/CodingStyle.txt) */ nfsstat4 panfs_um_getdeviceinfo(int fd, struct pan_ioctl_xdr *da_addr_body, const layouttype4 type, const struct pnfs_deviceid *deviceid) { struct pan_getdeviceinfo_ioctl pgi = { .hdr.size = sizeof(pgi), .da_addr_body = *da_addr_body, .type = type, .deviceid = *deviceid, }; int ret; ret = ioctl(fd, PAN_FS_CLIENT_PNFS_DEVICEINFO, &pgi); if (ret) return NFS4ERR_SERVERFAULT; *da_addr_body = pgi.da_addr_body; return pgi.hdr.nfsstat; } nfsstat4 panfs_um_layoutget(int fd, struct pan_ioctl_xdr *loc_body, uint64_t clientid, void *recall_file_info, const struct fsal_layoutget_arg *arg, struct fsal_layoutget_res *res) { struct pan_layoutget_ioctl pli = { .hdr.size = sizeof(pli), .loc_body = *loc_body, .clientid = clientid, .recall_file_info = recall_file_info, .arg = arg, .res = res, }; int ret; ret = ioctl(fd, PAN_FS_CLIENT_PNFS_LAYOUTGET, &pli); if (ret) return NFS4ERR_SERVERFAULT; *loc_body = pli.loc_body; return pli.hdr.nfsstat; } nfsstat4 panfs_um_layoutreturn(int fd, struct pan_ioctl_xdr *lrf_body, const struct fsal_layoutreturn_arg *arg) { struct pan_layoutreturn_ioctl plri = { .hdr.size = sizeof(plri), .lrf_body = *lrf_body, .arg = arg, }; int ret; ret = ioctl(fd, PAN_FS_CLIENT_PNFS_LAYOUTRETURN, &plri); if (ret) return NFS4ERR_SERVERFAULT; return plri.hdr.nfsstat; } nfsstat4 panfs_um_layoutcommit(int fd, struct pan_ioctl_xdr *lou_body, const struct fsal_layoutcommit_arg *arg, struct fsal_layoutcommit_res *res) { struct pan_layoutcommit_ioctl plci = { .hdr.size = sizeof(plci), .lou_body = *lou_body, .arg = arg, .res = res, }; int ret; ret = ioctl(fd, PAN_FS_CLIENT_PNFS_LAYOUTCOMMIT, &plci); if (ret) return NFS4ERR_SERVERFAULT; *lou_body = plci.lou_body; return plci.hdr.nfsstat; } int panfs_um_recieve_layoutrecall(int fd, struct pan_cb_layoutrecall_event *events, int max_events, int *num_events) { struct pan_cb_layoutrecall_ioctl pcli = { .hdr.size = sizeof(pcli), .events = events, .max_events = max_events, }; int ret; ret = ioctl(fd, PAN_FS_CLIENT_PNFS_LAYOUTRECALL, &pcli); if (ret) return errno; *num_events = pcli.num_events; return pcli.hdr.nfsstat; } int panfs_um_cancel_recalls(int fd, int debug_magic) { struct pan_cancel_recalls_ioctl pcri = { .hdr.size = sizeof(pcri), .debug_magic = debug_magic, }; int ret; ret = ioctl(fd, PAN_FS_CLIENT_PNFS_CANCEL_RECALLS, &pcri); if (ret) return errno; return pcri.hdr.nfsstat; } fsal_status_t panfs_um_get_attr(int fd, struct pan_attrs *pan_attrs) { fsal_errors_t error = ERR_FSAL_NO_ERROR; int ret; pan_fs_client_ioctl_get_attr_args_t args; struct pan_fs_client_ioctl_get_attr_args_holder_s get_attrs; int num_aces; memset(&args, 0, sizeof(args)); get_attrs.get_attr_args = &args; args.flags = PAN_FS_CLIENT_IOCTL_GET_F__GET_CACHED | PAN_FS_CLIENT_IOCTL_GET_F__OPT_ATTRS | PAN_FS_CLIENT_IOCTL_GET_F__SORT_V1_ACL | PAN_FS_CLIENT_IOCTL_GET_F__CACHE_ACL; args.acl_version = PAN_FS_ACL_VERSION; ret = ioctl(fd, PAN_FS_CLIENT_IOC_ATTR_GET, &get_attrs); if (ret) { ret = errno; error = posix2fsal_error(ret); return fsalstat(error, ret); } if (args.acl_version < PAN_FS_ACL_VERSION_MIN || args.acl_version > PAN_FS_ACL_VERSION_MAX) { ret = -EINVAL; error = posix2fsal_error(ret); return fsalstat(error, ret); } num_aces = MIN(args.num_aces, PAN_FS_ACL_LEN_MAX); memcpy(pan_attrs->acls.aces, args.panfs_acl, num_aces * sizeof(struct pan_fs_ace_s)); pan_attrs->acls.naces = num_aces; return fsalstat(error, 0); } fsal_status_t panfs_um_set_attr(int fd, struct pan_attrs *pan_attrs) { fsal_errors_t error = ERR_FSAL_NO_ERROR; int ret; pan_fs_client_ioctl_set_attr_args_t args; struct pan_fs_client_ioctl_set_attr_args_holder_s set_attrs; int num_aces; memset(&args, 0, sizeof(args)); set_attrs.set_attr_args = &args; num_aces = MIN(pan_attrs->acls.naces, PAN_FS_ACL_LEN_MAX); memcpy(args.panfs_acl, pan_attrs->acls.aces, num_aces * sizeof(struct pan_fs_ace_s)); args.num_aces = num_aces; args.attr_mask |= PAN_FS_CLIENT_IOCTL_SET_ATTR_ACL_REPLACE; args.acl_version = PAN_FS_ACL_VERSION; ret = ioctl(fd, PAN_FS_CLIENT_IOC_ATTR_SET, &set_attrs); if (ret) { ret = errno; error = posix2fsal_error(ret); return fsalstat(error, ret); } return fsalstat(error, 0); } nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/panfs/panfs_um_pnfs.h000066400000000000000000000045521324272410200226110ustar00rootroot00000000000000/* * Copyright © from 2012 Panasas Inc. * Author: Boaz Harrosh * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, email to the Free Software * Foundation, Inc., licensing@fsf.org * * ------------- */ #ifndef __PANFS_UM_PNFS_H #define __PANFS_UM_PNFS_H #include "fsal_types.h" #include "fsal_pnfs.h" #include "panfs_pnfs_ioctl.h" /** * @file panfs_um_pnfs.h * @author Boaz Harrosh * * @brief pNFS IOCTL wrapper library API * * This file is edited with the LINUX coding style: (Will be enforced) * - Tab characters of 8 spaces wide * - Lines not longer then 80 chars * - etc ... (See linux Documentation/CodingStyle.txt) */ struct pan_ioctl_xdr; void pan_ioctl_xdr_init(void *buff, int alloc_len, int cur_len); nfsstat4 panfs_um_getdeviceinfo(int fd, struct pan_ioctl_xdr *da_addr_body, const layouttype4 type, const struct pnfs_deviceid *deviceid); nfsstat4 panfs_um_layoutget(int fd, struct pan_ioctl_xdr *loc_body, uint64_t clientid, void *recall_file_info, const struct fsal_layoutget_arg *arg, struct fsal_layoutget_res *res); nfsstat4 panfs_um_layoutreturn(int fd, struct pan_ioctl_xdr *lrf_body, const struct fsal_layoutreturn_arg *arg); nfsstat4 panfs_um_layoutcommit(int fd, struct pan_ioctl_xdr *lou_body, const struct fsal_layoutcommit_arg *arg, struct fsal_layoutcommit_res *res); int panfs_um_recieve_layoutrecall(int fd, struct pan_cb_layoutrecall_event *events, int max_events, int *num_events); int panfs_um_cancel_recalls(int fd, int debug_magic); struct pan_attrs; struct pan_stor_obj_id_s; fsal_status_t panfs_um_get_attr(int fd, struct pan_attrs *pan_attrs); fsal_status_t panfs_um_set_attr(int fd, struct pan_attrs *pan_attrs); #endif /* __PANFS_UM_PNFS_H */ nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/panfs/subfsal_panfs.c000066400000000000000000000072331324272410200225730ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) CohortFS LLC, 2014 * Author: Daniel Gryniewicz dang@cohortfs.com * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* export_panfs.c * PanFS Sub-FSAL export object */ #include "config.h" #include "fsal_types.h" #include "fsal_api.h" #include "panfs.h" #include "../subfsal.h" #include "mds.h" /* Export */ static struct config_item_list fsid_types[] = { CONFIG_LIST_TOK("None", FSID_NO_TYPE), CONFIG_LIST_TOK("One64", FSID_ONE_UINT64), CONFIG_LIST_TOK("Major64", FSID_MAJOR_64), CONFIG_LIST_TOK("Two64", FSID_TWO_UINT64), CONFIG_LIST_TOK("uuid", FSID_TWO_UINT64), CONFIG_LIST_TOK("Two32", FSID_TWO_UINT32), CONFIG_LIST_TOK("Dev", FSID_DEVICE), CONFIG_LIST_TOK("Device", FSID_DEVICE), CONFIG_LIST_EOL }; struct config_item export_params[] = { CONF_ITEM_NOOP("name"), CONF_ITEM_BOOL("pnfs", false, panfs_fsal_export, pnfs_enabled), CONF_ITEM_TOKEN("fsid_type", FSID_NO_TYPE, fsid_types, panfs_fsal_export, vfs_export.fsid_type), CONFIG_EOL }; static struct config_block export_param_block = { .dbus_interface_name = "org.ganesha.nfsd.config.fsal.panfs-export%d", .blk_desc.name = "FSAL", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = noop_conf_init, .blk_desc.u.blk.params = export_params, .blk_desc.u.blk.commit = noop_conf_commit }; struct config_block *vfs_sub_export_param = &export_param_block; /* Handle syscalls */ void vfs_sub_fini(struct vfs_fsal_export *vfs) { struct panfs_fsal_export *myself = EXPORT_PANFS_FROM_VFS(vfs); pnfs_panfs_fini(myself->pnfs_data); } void vfs_sub_init_export_ops(struct vfs_fsal_export *vfs, const char *export_path) { struct panfs_fsal_export *myself = EXPORT_PANFS_FROM_VFS(vfs); if (myself->pnfs_enabled) { LogInfo(COMPONENT_FSAL, "pnfs_panfs was enabled for [%s]", export_path); export_ops_pnfs(&vfs->export.exp_ops); fsal_ops_pnfs(&vfs->export.fsal->m_ops); } } int vfs_sub_init_export(struct vfs_fsal_export *vfs) { int retval = 0; struct panfs_fsal_export *myself = EXPORT_PANFS_FROM_VFS(vfs); if (myself->pnfs_enabled) { retval = pnfs_panfs_init(vfs_get_root_fd(&vfs->export), &myself->pnfs_data); if (retval) { LogCrit(COMPONENT_FSAL, "vfs export_ops_pnfs failed => %d [%s]", retval, strerror(retval)); } } return retval; } struct vfs_fsal_obj_handle *vfs_sub_alloc_handle(void) { struct panfs_fsal_obj_handle *hdl; hdl = gsh_calloc(1, (sizeof(struct panfs_fsal_obj_handle) + sizeof(vfs_file_handle_t))); hdl->vfs_obj_handle.handle = (vfs_file_handle_t *) &hdl[1]; return &hdl->vfs_obj_handle; } int vfs_sub_init_handle(struct vfs_fsal_export *vfs_export, struct vfs_fsal_obj_handle *vfs_hdl, const char *path) { struct panfs_fsal_export *myself = EXPORT_PANFS_FROM_VFS(vfs_export); struct panfs_fsal_obj_handle *hdl = OBJ_PANFS_FROM_VFS(vfs_hdl); if (myself->pnfs_enabled) handle_ops_pnfs(&vfs_hdl->obj_handle.obj_ops); panfs_handle_ops_init(hdl); return 0; } nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/state.c000066400000000000000000000063411324272410200177550ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) CohortFS Inc., 2015 * Author: Daniel Gryniewicz dang@cohortfs.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* state.c * VFS state management */ #include "config.h" #include #include #include "gsh_types.h" #include "sal_data.h" #include "sal_functions.h" #include "avltree.h" #include "vfs_methods.h" struct vfs_state_entry { struct gsh_buffdesc fs_key; /**< Key for tree */ struct avltree_node fs_node; /**< AVL tree node */ struct state_hdl ostate; /**< Actual file state */ }; static struct avltree vfs_state_tree = {0}; /** * @brief VFS state comparator for AVL tree walk * */ static int vfs_state_cmpf(const struct avltree_node *lhs, const struct avltree_node *rhs) { struct vfs_state_entry *lk, *rk; lk = avltree_container_of(lhs, struct vfs_state_entry, fs_node); rk = avltree_container_of(rhs, struct vfs_state_entry, fs_node); if (lk->fs_key.len != rk->fs_key.len) return (lk->fs_key.len < rk->fs_key.len) ? -1 : 1; return memcmp(lk->fs_key.addr, rk->fs_key.addr, lk->fs_key.len); } static struct vfs_state_entry *vfs_state_lookup(struct gsh_buffdesc *key) { struct vfs_state_entry key_entry; struct avltree_node *node; key_entry.fs_key = *key; node = avltree_lookup(&key_entry.fs_node, &vfs_state_tree); if (!node) return NULL; return avltree_container_of(node, struct vfs_state_entry, fs_node); } void vfs_state_init(void) { if (vfs_state_tree.cmp_fn == NULL) avltree_init(&vfs_state_tree, vfs_state_cmpf, 0); } void vfs_state_release(struct gsh_buffdesc *key) { struct vfs_state_entry *fs_entry; fs_entry = vfs_state_lookup(key); if (!fs_entry) return; avltree_remove(&fs_entry->fs_node, &vfs_state_tree); gsh_free(fs_entry); } struct state_hdl *vfs_state_locate(struct fsal_obj_handle *obj) { struct vfs_state_entry *fs_entry; struct avltree_node *node; struct gsh_buffdesc key; obj->obj_ops.handle_to_key(obj, &key); fs_entry = vfs_state_lookup(&key); if (fs_entry) { fs_entry->ostate.file.obj = obj; return &fs_entry->ostate; } fs_entry = gsh_calloc(sizeof(struct vfs_state_entry), 1); fs_entry->fs_key = key; node = avltree_insert(&fs_entry->fs_node, &vfs_state_tree); if (unlikely(node)) { /* Race won */ gsh_free(fs_entry); fs_entry = avltree_container_of(node, struct vfs_state_entry, fs_node); } else { state_hdl_init(&fs_entry->ostate, obj->type, obj); } /* Always update with current handle pointer */ fs_entry->ostate.file.obj = obj; return &fs_entry->ostate; } nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/subfsal.h000066400000000000000000000035301324272410200202760ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) CohortFS LLC, 2014 * Author: Daniel Gryniewicz dang@cohortfs.com * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* subfsal.h * VFS Sub-FSAL API */ #ifndef SUBFSAL_H #define SUBFSAL_H #include "config_parsing.h" /* Export parameters */ extern struct config_block *vfs_sub_export_param; /** Routines for sub-FSALS */ void vfs_sub_fini(struct vfs_fsal_export *myself); void vfs_sub_init_export_ops(struct vfs_fsal_export *myself, const char *export_path); int vfs_sub_init_export(struct vfs_fsal_export *myself); /** * @brief Allocate the SubFSAL object handle * * Allocate the SubFSAL object handle. It must be large enough to hold a * vfs_file_handle_t after the end of the normal handle, and the @a handle field * of the vfs_fsal_obj_handle must point to the correct location for the * vfs_file_handle_t. * * @return VFS object handle on success, NULL on failure */ struct vfs_fsal_obj_handle *vfs_sub_alloc_handle(void); int vfs_sub_init_handle(struct vfs_fsal_export *myself, struct vfs_fsal_obj_handle *hdl, const char *path); #endif /* SUBFSAL_H */ nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/vfs/000077500000000000000000000000001324272410200172635ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/vfs/CMakeLists.txt000066400000000000000000000011421324272410200220210ustar00rootroot00000000000000add_definitions( -D__USE_GNU -D_GNU_SOURCE ) SET(fsalvfs_LIB_SRCS main.c ../export.c ../handle.c ../handle_syscalls.c ../file.c ../xattrs.c ../vfs_methods.h ../state.c subfsal_vfs.c ) if(ENABLE_VFS_DEBUG_ACL) set(fsalvfs_LIB_SRCS ${fsalvfs_LIB_SRCS} attrs.c) endif(ENABLE_VFS_DEBUG_ACL) add_library(fsalvfs MODULE ${fsalvfs_LIB_SRCS}) add_sanitizers(fsalvfs) target_link_libraries(fsalvfs gos fsal_os ${SYSTEM_LIBRARIES} ) set_target_properties(fsalvfs PROPERTIES VERSION 4.2.0 SOVERSION 4) install(TARGETS fsalvfs COMPONENT fsal DESTINATION ${FSAL_DESTINATION} ) nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/vfs/attrs.c000066400000000000000000000127071324272410200205730ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) CohortFS LLC, 2015 * Author: Daniel Gryniewicz dang@cohortfs.com * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* attrs.c * VFS attribute caching handle object */ #include "config.h" #include "avltree.h" #include "fsal.h" #include "fsal_convert.h" #include "FSAL/access_check.h" #include "../vfs_methods.h" #include "attrs.h" #include "nfs4_acls.h" struct vfs_acl_entry { struct gsh_buffdesc fa_key; /**< Key for tree */ struct avltree_node fa_node; /**< AVL tree node */ fsal_acl_data_t fa_acl; /**< Actual ACLs */ }; static struct avltree vfs_acl_tree = {0}; /** * @brief VFS acl comparator for AVL tree walk * */ static int vfs_acl_cmpf(const struct avltree_node *lhs, const struct avltree_node *rhs) { struct vfs_acl_entry *lk, *rk; lk = avltree_container_of(lhs, struct vfs_acl_entry, fa_node); rk = avltree_container_of(rhs, struct vfs_acl_entry, fa_node); if (lk->fa_key.len != rk->fa_key.len) return (lk->fa_key.len < rk->fa_key.len) ? -1 : 1; return memcmp(lk->fa_key.addr, rk->fa_key.addr, lk->fa_key.len); } static struct vfs_acl_entry *vfs_acl_lookup(struct gsh_buffdesc *key) { struct vfs_acl_entry key_entry; struct avltree_node *node; key_entry.fa_key = *key; node = avltree_lookup(&key_entry.fa_node, &vfs_acl_tree); if (!node) return NULL; return avltree_container_of(node, struct vfs_acl_entry, fa_node); } static struct vfs_acl_entry *vfs_acl_locate(struct fsal_obj_handle *obj) { struct vfs_acl_entry *fa_entry; struct avltree_node *node; struct gsh_buffdesc key; obj->obj_ops.handle_to_key(obj, &key); fa_entry = vfs_acl_lookup(&key); if (fa_entry) { LogDebug(COMPONENT_FSAL, "found"); return fa_entry; } LogDebug(COMPONENT_FSAL, "create"); fa_entry = gsh_calloc(1, sizeof(struct vfs_acl_entry)); fa_entry->fa_key = key; node = avltree_insert(&fa_entry->fa_node, &vfs_acl_tree); if (unlikely(node)) { /* Race won */ gsh_free(fa_entry); fa_entry = avltree_container_of(node, struct vfs_acl_entry, fa_node); } else { fa_entry->fa_acl.aces = (fsal_ace_t *) nfs4_ace_alloc(0); } return fa_entry; } void vfs_acl_init(void) { if (vfs_acl_tree.cmp_fn == NULL) avltree_init(&vfs_acl_tree, vfs_acl_cmpf, 0); } void vfs_acl_release(struct gsh_buffdesc *key) { struct vfs_acl_entry *fa_entry; fa_entry = vfs_acl_lookup(key); if (!fa_entry) return; avltree_remove(&fa_entry->fa_node, &vfs_acl_tree); gsh_free(fa_entry); } fsal_status_t vfs_sub_getattrs(struct vfs_fsal_obj_handle *vfs_hdl, int fd, attrmask_t request_mask, struct attrlist *attrib) { fsal_acl_status_t status; struct vfs_acl_entry *fa; fsal_acl_data_t acldata; fsal_acl_t *acl; LogDebug(COMPONENT_FSAL, "Enter"); if (attrib->acl != NULL) { /* We should never be passed attributes that have an * ACL attached, but just in case some future code * path changes that assumption, let's not release the * old ACL properly. */ int acl_status; acl_status = nfs4_acl_release_entry(attrib->acl); if (acl_status != NFS_V4_ACL_SUCCESS) LogCrit(COMPONENT_FSAL, "Failed to release old acl, status=%d", acl_status); attrib->acl = NULL; } fa = vfs_acl_locate(&vfs_hdl->obj_handle); if (!fa->fa_acl.naces) { /* No ACLs yet */ FSAL_UNSET_MASK(attrib->valid_mask, ATTR_ACL); return fsalstat(ERR_FSAL_NO_ERROR, 0); } fsal_print_acl(COMPONENT_FSAL, NIV_FULL_DEBUG, (fsal_acl_t *)&fa->fa_acl); acldata.naces = fa->fa_acl.naces; acldata.aces = (fsal_ace_t *) nfs4_ace_alloc(acldata.naces); memcpy(acldata.aces, fa->fa_acl.aces, acldata.naces * sizeof(fsal_ace_t)); acl = nfs4_acl_new_entry(&acldata, &status); if (!acl) return fsalstat(ERR_FSAL_FAULT, status); fsal_print_acl(COMPONENT_FSAL, NIV_FULL_DEBUG, acl); attrib->acl = acl; FSAL_SET_MASK(attrib->valid_mask, ATTR_ACL); return fsalstat(ERR_FSAL_NO_ERROR, 0); } fsal_status_t vfs_sub_setattrs(struct vfs_fsal_obj_handle *vfs_hdl, int fd, attrmask_t request_mask, struct attrlist *attrib) { struct vfs_acl_entry *fa; if (!FSAL_TEST_MASK(request_mask, ATTR_ACL) || !attrib || !attrib->acl) return fsalstat(ERR_FSAL_NO_ERROR, 0); LogDebug(COMPONENT_FSAL, "Enter"); fsal_print_acl(COMPONENT_FSAL, NIV_FULL_DEBUG, attrib->acl); fa = vfs_acl_locate(&vfs_hdl->obj_handle); nfs4_ace_free(fa->fa_acl.aces); fa->fa_acl.naces = attrib->acl->naces; fa->fa_acl.aces = (fsal_ace_t *) nfs4_ace_alloc(fa->fa_acl.naces); memcpy(fa->fa_acl.aces, attrib->acl->aces, fa->fa_acl.naces * sizeof(fsal_ace_t)); fsal_print_acl(COMPONENT_FSAL, NIV_FULL_DEBUG, (fsal_acl_t *)&fa->fa_acl); if (attrib->valid_mask & ATTR_MODE) vfs_hdl->mode = attrib->mode; FSAL_SET_MASK(attrib->valid_mask, ATTR_ACL); return fsalstat(ERR_FSAL_NO_ERROR, 0); } nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/vfs/attrs.h000066400000000000000000000025371324272410200206000ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) CohortFS LLC, 2015 * Author: Daniel Gryniewicz dang@cohortfs.com * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* attrs.h * VFS debug attribute handling */ #ifndef __VFS_ATTRS_H #define __VFS_ATTRS_H #include "../vfs_methods.h" void vfs_acl_init(void); fsal_status_t vfs_sub_getattrs(struct vfs_fsal_obj_handle *vfs_hdl, int fd, attrmask_t request_mask, struct attrlist *attrs); fsal_status_t vfs_sub_setattrs(struct vfs_fsal_obj_handle *vfs_hdl, int fd, attrmask_t request_mask, struct attrlist *attrib_set); #endif /* __VFS_ATTRS_H */ nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/vfs/main.c000066400000000000000000000151141324272410200203550ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ /* main.c * Module core functions */ #include "config.h" #include /* used for 'dirname' */ #include #include #include #include #include "gsh_list.h" #include "fsal.h" #include "FSAL/fsal_init.h" #include "fsal_handle_syscalls.h" /* VFS FSAL module private storage */ /* defined the set of attributes supported with POSIX */ #ifndef ENABLE_VFS_DEBUG_ACL #define VFS_SUPPORTED_ATTRIBUTES ((const attrmask_t) (ATTRS_POSIX)) #else #define VFS_SUPPORTED_ATTRIBUTES ((const attrmask_t) (ATTRS_POSIX | ATTR_ACL)) #endif struct vfs_fsal_module { struct fsal_module fsal; struct fsal_staticfsinfo_t fs_info; /* vfsfs_specific_initinfo_t specific_info; placeholder */ }; const char myname[] = "VFS"; /* filesystem info for VFS */ static struct fsal_staticfsinfo_t default_posix_info = { .maxfilesize = UINT64_MAX, .maxlink = _POSIX_LINK_MAX, .maxnamelen = 1024, .maxpathlen = 1024, .no_trunc = true, .chown_restricted = true, .case_insensitive = false, .case_preserving = true, .lock_support = false, .lock_support_async_block = false, .named_attr = true, .unique_handles = true, .lease_time = {10, 0}, .acl_support = FSAL_ACLSUPPORT_ALLOW, .homogenous = true, .supported_attrs = VFS_SUPPORTED_ATTRIBUTES, .maxread = FSAL_MAXIOSIZE, .maxwrite = FSAL_MAXIOSIZE, .link_supports_permission_checks = false, }; static struct config_item vfs_params[] = { CONF_ITEM_BOOL("link_support", true, fsal_staticfsinfo_t, link_support), CONF_ITEM_BOOL("symlink_support", true, fsal_staticfsinfo_t, symlink_support), CONF_ITEM_BOOL("cansettime", true, fsal_staticfsinfo_t, cansettime), CONF_ITEM_UI64("maxread", 512, FSAL_MAXIOSIZE, FSAL_MAXIOSIZE, fsal_staticfsinfo_t, maxread), CONF_ITEM_UI64("maxwrite", 512, FSAL_MAXIOSIZE, FSAL_MAXIOSIZE, fsal_staticfsinfo_t, maxwrite), CONF_ITEM_MODE("umask", 0, fsal_staticfsinfo_t, umask), CONF_ITEM_BOOL("auth_xdev_export", false, fsal_staticfsinfo_t, auth_exportpath_xdev), CONF_ITEM_MODE("xattr_access_rights", 0400, fsal_staticfsinfo_t, xattr_access_rights), CONFIG_EOL }; struct config_block vfs_param = { .dbus_interface_name = "org.ganesha.nfsd.config.fsal.vfs", .blk_desc.name = "VFS", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = noop_conf_init, .blk_desc.u.blk.params = vfs_params, .blk_desc.u.blk.commit = noop_conf_commit }; /* private helper for export object */ struct fsal_staticfsinfo_t *vfs_staticinfo(struct fsal_module *hdl) { struct vfs_fsal_module *myself; myself = container_of(hdl, struct vfs_fsal_module, fsal); return &myself->fs_info; } /* Module methods */ /* init_config * must be called with a reference taken (via lookup_fsal) */ static fsal_status_t init_config(struct fsal_module *fsal_hdl, config_file_t config_struct, struct config_error_type *err_type) { struct vfs_fsal_module *vfs_me = container_of(fsal_hdl, struct vfs_fsal_module, fsal); #ifdef F_OFD_GETLK int fd, rc; struct flock lock; char *temp_name; #endif vfs_me->fs_info = default_posix_info; /* copy the consts */ #ifdef F_OFD_GETLK /* If on a system that might support OFD locks, verify them. * Only if they exist will we declare lock support. */ LogInfo(COMPONENT_FSAL, "FSAL_VFS testing OFD Locks"); temp_name = gsh_strdup("/tmp/ganesha.nfsd.locktestXXXXXX"); fd = mkstemp(temp_name); if (fd >= 0) { lock.l_whence = SEEK_SET; lock.l_type = F_RDLCK; lock.l_start = 0; lock.l_len = 0; lock.l_pid = 0; rc = fcntl(fd, F_OFD_GETLK, &lock); if (rc == 0) vfs_me->fs_info.lock_support = true; else LogInfo(COMPONENT_FSAL, "Could not use OFD locks"); close(fd); unlink(temp_name); } else { LogCrit(COMPONENT_FSAL, "Could not create file %s to test OFD locks", temp_name); } gsh_free(temp_name); #endif if (vfs_me->fs_info.lock_support) LogInfo(COMPONENT_FSAL, "FSAL_VFS enabling OFD Locks"); else LogInfo(COMPONENT_FSAL, "FSAL_VFS disabling lock support"); (void) load_config_from_parse(config_struct, &vfs_param, &vfs_me->fs_info, true, err_type); if (!config_error_is_harmless(err_type)) return fsalstat(ERR_FSAL_INVAL, 0); display_fsinfo(&vfs_me->fs_info); LogFullDebug(COMPONENT_FSAL, "Supported attributes constant = 0x%" PRIx64, VFS_SUPPORTED_ATTRIBUTES); LogFullDebug(COMPONENT_FSAL, "Supported attributes default = 0x%" PRIx64, default_posix_info.supported_attrs); LogDebug(COMPONENT_FSAL, "FSAL INIT: Supported attributes mask = 0x%" PRIx64, vfs_me->fs_info.supported_attrs); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* Internal VFS method linkage to export object */ fsal_status_t vfs_create_export(struct fsal_module *fsal_hdl, void *parse_node, struct config_error_type *err_type, const struct fsal_up_vector *up_ops); /* Module initialization. * Called by dlopen() to register the module * keep a private pointer to me in myself */ /* my module private storage */ static struct vfs_fsal_module VFS; /* linkage to the exports and handle ops initializers */ MODULE_INIT void vfs_init(void) { int retval; struct fsal_module *myself = &VFS.fsal; retval = register_fsal(myself, myname, FSAL_MAJOR_VERSION, FSAL_MINOR_VERSION, FSAL_ID_VFS); if (retval != 0) { fprintf(stderr, "VFS module failed to register"); return; } myself->m_ops.create_export = vfs_create_export; myself->m_ops.init_config = init_config; } MODULE_FINI void vfs_unload(void) { int retval; retval = unregister_fsal(&VFS.fsal); if (retval != 0) { fprintf(stderr, "VFS module failed to unregister"); return; } } nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/vfs/subfsal_vfs.c000066400000000000000000000057101324272410200217470ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) CohortFS LLC, 2014 * Author: Daniel Gryniewicz dang@cohortfs.com * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* export_vfs.c * VFS Sub-FSAL export object */ #include "config.h" #include "fsal_types.h" #include "fsal_api.h" #include "../vfs_methods.h" #include "../subfsal.h" #ifdef ENABLE_VFS_DEBUG_ACL #include "attrs.h" #endif /* ENABLE_VFS_DEBUG_ACL */ /* Export */ static struct config_item_list fsid_types[] = { CONFIG_LIST_TOK("None", FSID_NO_TYPE), CONFIG_LIST_TOK("One64", FSID_ONE_UINT64), CONFIG_LIST_TOK("Major64", FSID_MAJOR_64), CONFIG_LIST_TOK("Two64", FSID_TWO_UINT64), CONFIG_LIST_TOK("uuid", FSID_TWO_UINT64), CONFIG_LIST_TOK("Two32", FSID_TWO_UINT32), CONFIG_LIST_TOK("Dev", FSID_DEVICE), CONFIG_LIST_TOK("Device", FSID_DEVICE), CONFIG_LIST_EOL }; static struct config_item export_params[] = { CONF_ITEM_NOOP("name"), CONF_ITEM_TOKEN("fsid_type", FSID_NO_TYPE, fsid_types, vfs_fsal_export, fsid_type), CONFIG_EOL }; static struct config_block export_param_block = { .dbus_interface_name = "org.ganesha.nfsd.config.fsal.vfs-export%d", .blk_desc.name = "FSAL", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = noop_conf_init, .blk_desc.u.blk.params = export_params, .blk_desc.u.blk.commit = noop_conf_commit }; struct config_block *vfs_sub_export_param = &export_param_block; /* Handle syscalls */ void vfs_sub_fini(struct vfs_fsal_export *myself) { } void vfs_sub_init_export_ops(struct vfs_fsal_export *myself, const char *export_path) { } int vfs_sub_init_export(struct vfs_fsal_export *myself) { #ifdef ENABLE_VFS_DEBUG_ACL vfs_acl_init(); #endif /* ENABLE_VFS_DEBUG_ACL */ return 0; } struct vfs_fsal_obj_handle *vfs_sub_alloc_handle(void) { struct vfs_fsal_obj_handle *hdl; hdl = gsh_calloc(1, (sizeof(struct vfs_fsal_obj_handle) + sizeof(vfs_file_handle_t))); hdl->handle = (vfs_file_handle_t *) &hdl[1]; return hdl; } struct vfs_subfsal_obj_ops vfs_obj_subops = { #ifdef ENABLE_VFS_DEBUG_ACL vfs_sub_getattrs, vfs_sub_setattrs, #endif /* ENABLE_VFS_DEBUG_ACL */ }; int vfs_sub_init_handle(struct vfs_fsal_export *myself, struct vfs_fsal_obj_handle *hdl, const char *path) { hdl->sub_ops = &vfs_obj_subops; return 0; } nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/vfs_methods.h000066400000000000000000000243251324272410200211650ustar00rootroot00000000000000/* * Copyright (C) International Business Machines Corp., 2010 * Author(s): Aneesh Kumar K.V * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * @file FSAL/FSAL_VFS/vfs_methods.h * @brief System calls for the FreeBSD handle calls */ /* VFS methods for handles */ #ifndef VFS_METHODS_H #define VFS_METHODS_H #include "fsal_handle_syscalls.h" #include "fsal_api.h" #include "FSAL/fsal_commonlib.h" struct vfs_fsal_obj_handle; struct vfs_fsal_export; struct vfs_filesystem; /* * VFS internal export */ struct vfs_fsal_export { struct fsal_export export; struct fsal_filesystem *root_fs; struct glist_head filesystems; int fsid_type; }; #define EXPORT_VFS_FROM_FSAL(fsal) \ container_of((fsal), struct vfs_fsal_export, export) /* * VFS internal filesystem */ struct vfs_filesystem { struct fsal_filesystem *fs; int root_fd; struct glist_head exports; }; /* * Link VFS file systems and exports * Supports a many-to-many relationship */ struct vfs_filesystem_export_map { struct vfs_fsal_export *exp; struct vfs_filesystem *fs; struct glist_head on_exports; struct glist_head on_filesystems; }; void vfs_unexport_filesystems(struct vfs_fsal_export *exp); /* private helpers from export */ void vfs_handle_ops_init(struct fsal_obj_ops *ops); int vfs_get_root_fd(struct fsal_export *exp_hdl); /* method proto linkage to handle.c for export */ fsal_status_t vfs_lookup_path(struct fsal_export *exp_hdl, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out); fsal_status_t vfs_create_handle(struct fsal_export *exp_hdl, struct gsh_buffdesc *hdl_desc, struct fsal_obj_handle **handle, struct attrlist *attrs_out); struct vfs_subfsal_obj_ops { /** * @brief Gte sub-fsal attributes from an object * * @param[in] vfs_hdl The VFS object to modify * @param[in] fd Open filehandle on object * * @return FSAL status. */ fsal_status_t (*getattrs)(struct vfs_fsal_obj_handle *vfs_hdl, int fd, attrmask_t request_mask, struct attrlist *attrs); /** * @brief Set sub-fsal attributes on an object * * @param[in] vfs_hdl The VFS object to modify * @param[in] fd Open filehandle on object * @param[in] attrib_set Attributes to set * * @return FSAL status. */ fsal_status_t (*setattrs)(struct vfs_fsal_obj_handle *vfs_hdl, int fd, attrmask_t request_mask, struct attrlist *attrib_set); }; struct vfs_fd { /** The open and share mode etc. */ fsal_openflags_t openflags; /* rw lock to protect the file descriptor */ pthread_rwlock_t fdlock; /** The kernel file descriptor. */ int fd; }; struct vfs_state_fd { struct state_t state; struct vfs_fd vfs_fd; }; /* * VFS internal object handle * handle is a pointer because * a) the last element of file_handle is a char[] meaning variable len... * b) we cannot depend on it *always* being last or being the only * variable sized struct here... a pointer is safer. * wrt locks, should this be a lock counter?? * AF_UNIX sockets are strange ducks. I personally cannot see why they * are here except for the ability of a client to see such an animal with * an 'ls' or get rid of one with an 'rm'. You can't open them in the * usual file way so open_by_handle_at leads to a deadend. To work around * this, we save the args that were used to mknod or lookup the socket. */ struct vfs_fsal_obj_handle { struct fsal_obj_handle obj_handle; fsal_dev_t dev; vfs_file_handle_t *handle; #ifdef ENABLE_VFS_DEBUG_ACL uint32_t mode; /*< POSIX access mode */ #endif struct vfs_subfsal_obj_ops *sub_ops; /*< Optional subfsal ops */ const struct fsal_up_vector *up_ops; /*< Upcall operations */ union { struct { struct fsal_share share; struct vfs_fd fd; } file; struct { char *path; char *fs_location; } directory; struct { unsigned char *link_content; int link_size; } symlink; struct { vfs_file_handle_t *dir; char *name; } unopenable; } u; }; #define OBJ_VFS_FROM_FSAL(fsal) \ container_of((fsal), struct vfs_fsal_obj_handle, obj_handle) /* default vex ops */ int vfs_fd_to_handle(int fd, struct fsal_filesystem *fs, vfs_file_handle_t *fh); int vfs_name_to_handle(int atfd, struct fsal_filesystem *fs, const char *name, vfs_file_handle_t *fh); int vfs_open_by_handle(struct vfs_filesystem *fs, vfs_file_handle_t *fh, int openflags, fsal_errors_t *fsal_error); int vfs_encode_dummy_handle(vfs_file_handle_t *fh, struct fsal_filesystem *fs); bool vfs_is_dummy_handle(vfs_file_handle_t *fh); fsal_status_t vfs_check_handle(struct fsal_export *exp_hdl, struct gsh_buffdesc *hdl_desc, struct fsal_filesystem **fs, vfs_file_handle_t *fh, bool *dummy); bool vfs_valid_handle(struct gsh_buffdesc *desc); int vfs_readlink(struct vfs_fsal_obj_handle *myself, fsal_errors_t *fsal_error); int vfs_extract_fsid(vfs_file_handle_t *fh, enum fsid_type *fsid_type, struct fsal_fsid__ *fsid); int vfs_get_root_handle(struct vfs_filesystem *vfs_fs, struct vfs_fsal_export *exp); int vfs_re_index(struct vfs_filesystem *vfs_fs, struct vfs_fsal_export *exp); /* * VFS structure to tell subfunctions wether they should close the * returned fd or not */ struct closefd { int fd; int close_fd; }; int vfs_fsal_open(struct vfs_fsal_obj_handle *hdl, int openflags, fsal_errors_t *fsal_error); struct vfs_fsal_obj_handle *alloc_handle(int dirfd, vfs_file_handle_t *fh, struct fsal_filesystem *fs, struct stat *stat, vfs_file_handle_t *dir_fh, const char *path, struct fsal_export *exp_hdl); static inline bool vfs_unopenable_type(object_file_type_t type) { if ((type == SOCKET_FILE) || (type == CHARACTER_FILE) || (type == BLOCK_FILE)) { return true; } else { return false; } } struct closefd vfs_fsal_open_and_stat(struct fsal_export *exp, struct vfs_fsal_obj_handle *myself, struct stat *stat, fsal_openflags_t flags, fsal_errors_t *fsal_error); /* State storage */ void vfs_state_init(void); void vfs_state_release(struct gsh_buffdesc *key); struct state_hdl *vfs_state_locate(struct fsal_obj_handle *obj); /* I/O management */ fsal_status_t vfs_close_my_fd(struct vfs_fd *my_fd); fsal_status_t vfs_close(struct fsal_obj_handle *obj_hdl); /* Multiple file descriptor methods */ struct state_t *vfs_alloc_state(struct fsal_export *exp_hdl, enum state_type state_type, struct state_t *related_state); void vfs_free_state(struct fsal_export *exp_hdl, struct state_t *state); fsal_status_t vfs_merge(struct fsal_obj_handle *orig_hdl, struct fsal_obj_handle *dupe_hdl); 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); fsal_status_t vfs_reopen2(struct fsal_obj_handle *obj_hdl, struct state_t *state, fsal_openflags_t openflags); fsal_status_t vfs_read2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buffer_size, void *buffer, size_t *read_amount, bool *end_of_file, struct io_info *info); fsal_status_t vfs_write2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buffer_size, void *buffer, size_t *wrote_amount, bool *fsal_stable, struct io_info *info); fsal_status_t vfs_commit2(struct fsal_obj_handle *obj_hdl, off_t offset, size_t len); 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); fsal_status_t getattr2(struct fsal_obj_handle *obj_hdl); fsal_status_t vfs_getattr2(struct fsal_obj_handle *obj_hdl, struct attrlist *attrs); fsal_status_t vfs_setattr2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, struct attrlist *attrib_set); fsal_status_t vfs_close2(struct fsal_obj_handle *obj_hdl, struct state_t *state); /* extended attributes management */ fsal_status_t vfs_list_ext_attrs(struct fsal_obj_handle *obj_hdl, unsigned int cookie, fsal_xattrent_t *xattrs_tab, unsigned int xattrs_tabsize, unsigned int *p_nb_returned, int *end_of_list); fsal_status_t vfs_getextattr_id_by_name(struct fsal_obj_handle *obj_hdl, const char *xattr_name, unsigned int *pxattr_id); fsal_status_t vfs_getextattr_value_by_name(struct fsal_obj_handle *obj_hdl, const char *xattr_name, caddr_t buffer_addr, size_t buffer_size, size_t *p_output_size); fsal_status_t vfs_getextattr_value_by_id(struct fsal_obj_handle *obj_hdl, unsigned int xattr_id, caddr_t buffer_addr, size_t buffer_size, size_t *p_output_size); fsal_status_t vfs_setextattr_value(struct fsal_obj_handle *obj_hdl, const char *xattr_name, caddr_t buffer_addr, size_t buffer_size, int create); fsal_status_t vfs_setextattr_value_by_id(struct fsal_obj_handle *obj_hdl, unsigned int xattr_id, caddr_t buffer_addr, size_t buffer_size); fsal_status_t vfs_remove_extattr_by_id(struct fsal_obj_handle *obj_hdl, unsigned int xattr_id); fsal_status_t vfs_remove_extattr_by_name(struct fsal_obj_handle *obj_hdl, const char *xattr_name); #endif /* VFS_METHODS_H */ nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/xattrs.c000066400000000000000000000337761324272410200201760ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* xattrs.c * VFS object (file|dir) handle object extended attributes */ #include "config.h" #include /* used for 'dirname' */ #include #include #include #include #include #include "gsh_list.h" #include "fsal_api.h" #include "fsal_convert.h" #include "FSAL/fsal_commonlib.h" #include "vfs_methods.h" #include "common_utils.h" typedef int (*xattr_getfunc_t) (struct fsal_obj_handle *, /* object handle */ caddr_t, /* output buff */ size_t, /* output buff size */ size_t *, /* output size */ void *arg); /* optionnal argument */ typedef int (*xattr_setfunc_t) (struct fsal_obj_handle *, /* object handle */ caddr_t, /* input buff */ size_t, /* input size */ int, /* creation flag */ void *arg); /* optionnal argument */ struct fsal_xattr_def { char xattr_name[MAXNAMLEN + 1]; xattr_getfunc_t get_func; xattr_setfunc_t set_func; int flags; void *arg; }; /* * DEFINE GET/SET FUNCTIONS */ int print_vfshandle(struct fsal_obj_handle *obj_hdl, caddr_t buffer_addr, size_t buffer_size, size_t *p_output_size, void *arg) { *p_output_size = snprintf(buffer_addr, buffer_size, "(not yet implemented)"); return 0; } /* print_fid */ /* DEFINE HERE YOUR ATTRIBUTES LIST */ static struct fsal_xattr_def xattr_list[] = { {"vfshandle", print_vfshandle, NULL, XATTR_FOR_ALL | XATTR_RO, NULL}, }; #define XATTR_COUNT 1 #define XATTR_SYSTEM (INT_MAX - 1) /* we assume that this number is < 254 */ #if (XATTR_COUNT > 254) #error "ERROR: xattr count > 254" #endif /* test if an object has a given attribute */ static int do_match_type(int xattr_flag, object_file_type_t obj_type) { switch (obj_type) { case REGULAR_FILE: return ((xattr_flag & XATTR_FOR_FILE) == XATTR_FOR_FILE); case DIRECTORY: return ((xattr_flag & XATTR_FOR_DIR) == XATTR_FOR_DIR); case SYMBOLIC_LINK: return ((xattr_flag & XATTR_FOR_SYMLINK) == XATTR_FOR_SYMLINK); default: return ((xattr_flag & XATTR_FOR_ALL) == XATTR_FOR_ALL); } } static int attr_is_read_only(unsigned int attr_index) { if (attr_index < XATTR_COUNT) { if (xattr_list[attr_index].flags & XATTR_RO) return true; } /* else : standard xattr */ return false; } static int xattr_id_to_name(int fd, unsigned int xattr_id, char *name) { unsigned int index; unsigned int curr_idx; char names[MAXPATHLEN], *ptr; ssize_t namesize; size_t len = 0; if (xattr_id < XATTR_COUNT) return ERR_FSAL_INVAL; index = xattr_id - XATTR_COUNT; /* get xattrs */ namesize = flistxattr(fd, names, sizeof(names)); if (namesize < 0) return ERR_FSAL_NOENT; errno = 0; if (xattr_id == XATTR_SYSTEM) { strcpy(name, "system.posix_acl_access"); return ERR_FSAL_NO_ERROR; } for (ptr = names, curr_idx = 0; ptr < names + namesize; curr_idx++, ptr += len + 1) { len = strlen(ptr); if (curr_idx == index) { strcpy(name, ptr); return ERR_FSAL_NO_ERROR; } } return ERR_FSAL_NOENT; } /** * return index if found, * negative value on error. */ static int xattr_name_to_id(int fd, const char *name) { unsigned int i; char names[MAXPATHLEN], *ptr; ssize_t namesize; /* get xattrs */ namesize = flistxattr(fd, names, sizeof(names)); if (namesize < 0) return -ERR_FSAL_NOENT; if (!strncmp(name, "system.posix_acl_access", MAXNAMLEN)) return XATTR_SYSTEM; for (ptr = names, i = 0; ptr < names + namesize; i++, ptr += strlen(ptr) + 1) { if (!strcmp(name, ptr)) return i + XATTR_COUNT; } return -ERR_FSAL_NOENT; } fsal_status_t vfs_list_ext_attrs(struct fsal_obj_handle *obj_hdl, unsigned int argcookie, fsal_xattrent_t *xattrs_tab, unsigned int xattrs_tabsize, unsigned int *p_nb_returned, int *end_of_list) { unsigned int index; unsigned int out_index; unsigned int cookie = argcookie; struct vfs_fsal_obj_handle *obj_handle = NULL; int fd = -1; fsal_errors_t fe; char names[MAXPATHLEN], *ptr; ssize_t namesize; int xattr_idx; obj_handle = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); /* Deal with special cookie */ if (cookie == XATTR_RW_COOKIE) cookie = XATTR_COUNT; for (index = cookie, out_index = 0; index < XATTR_COUNT && out_index < xattrs_tabsize; index++) { if (do_match_type (xattr_list[index].flags, obj_handle->obj_handle.type)) { /* fills an xattr entry */ xattrs_tab[out_index].xattr_id = index; strncpy(xattr_list[index].xattr_name, xattrs_tab[out_index].xattr_name, MAXNAMLEN); xattr_list[index].xattr_name[MAXNAMLEN] = '\0'; xattrs_tab[out_index].xattr_cookie = index + 1; /* next output slot */ out_index++; } } /* save a call if output array is full */ if (out_index == xattrs_tabsize) { *end_of_list = false; *p_nb_returned = out_index; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* get the path of the file in file system */ fd = (obj_hdl->type == DIRECTORY) ? vfs_fsal_open(obj_handle, O_DIRECTORY, &fe) : vfs_fsal_open(obj_handle, O_RDWR, &fe); if (fd < 0) return fsalstat(fe, -fd); /* get xattrs */ namesize = flistxattr(fd, names, sizeof(names)); if (namesize >= 0) { size_t len = 0; errno = 0; for (ptr = names, xattr_idx = 0; (ptr < names + namesize) && (out_index < xattrs_tabsize); xattr_idx++, ptr += len + 1) { len = strlen(ptr); index = XATTR_COUNT + xattr_idx; /* skip if index is before cookie */ if (index < cookie) continue; /* fills an xattr entry */ xattrs_tab[out_index].xattr_id = index; strncpy(xattrs_tab[out_index].xattr_name, ptr, len + 1); xattrs_tab[out_index].xattr_cookie = index + 1; /* next output slot */ out_index++; } /* all xattrs are in the output array */ if (ptr >= names + namesize) *end_of_list = true; else *end_of_list = false; } else /* no xattrs */ *end_of_list = true; *p_nb_returned = out_index; close(fd); return fsalstat(ERR_FSAL_NO_ERROR, 0); } fsal_status_t vfs_getextattr_id_by_name(struct fsal_obj_handle *obj_hdl, const char *xattr_name, unsigned int *pxattr_id) { unsigned int index; int rc; bool found = false; struct vfs_fsal_obj_handle *obj_handle = NULL; int fd = -1; obj_handle = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); for (index = 0; index < XATTR_COUNT; index++) { if (!strcmp(xattr_list[index].xattr_name, xattr_name)) { found = true; break; } } /* search in xattrs */ if (!found) { fsal_errors_t fe; int openflags; switch (obj_hdl->type) { case DIRECTORY: openflags = O_DIRECTORY; break; case SYMBOLIC_LINK: return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); default: openflags = O_RDWR; } fd = vfs_fsal_open(obj_handle, openflags, &fe); if (fd < 0) return fsalstat(fe, -fd); errno = 0; rc = xattr_name_to_id(fd, xattr_name); if (rc < 0) { int minor = errno; close(fd); return fsalstat(-rc, minor); } else { index = rc; found = true; } close(fd); } *pxattr_id = index; return fsalstat(ERR_FSAL_NO_ERROR, 0); } fsal_status_t vfs_getextattr_value_by_id(struct fsal_obj_handle *obj_hdl, unsigned int xattr_id, caddr_t buffer_addr, size_t buffer_size, size_t *p_output_size) { struct vfs_fsal_obj_handle *obj_handle = NULL; int fd = -1; int rc = 0; obj_handle = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); /* check that this index match the type of entry */ if ((xattr_id < XATTR_COUNT) && !do_match_type(xattr_list[xattr_id].flags, obj_handle->obj_handle.type)) { return fsalstat(ERR_FSAL_INVAL, 0); } else if (xattr_id >= XATTR_COUNT) { char attr_name[MAXPATHLEN]; fsal_errors_t fe; fd = (obj_hdl->type == DIRECTORY) ? vfs_fsal_open(obj_handle, O_DIRECTORY, &fe) : vfs_fsal_open(obj_handle, O_RDWR, &fe); if (fd < 0) return fsalstat(fe, -fd); /* get the name for this attr */ rc = xattr_id_to_name(fd, xattr_id, attr_name); if (rc) { int minor = errno; close(fd); return fsalstat(-rc, minor); } rc = fgetxattr(fd, attr_name, buffer_addr, buffer_size); if (rc < 0) { rc = errno; close(fd); return fsalstat(posix2fsal_error(rc), rc); } /* the xattr value can be a binary, or a string. * trying to determine its type... */ *p_output_size = rc; close(fd); return fsalstat(ERR_FSAL_NO_ERROR, 0); } else { /* built-in attr */ /* get the value */ rc = xattr_list[xattr_id].get_func(obj_hdl, buffer_addr, buffer_size, p_output_size, xattr_list[xattr_id].arg); return fsalstat(rc, 0); } } fsal_status_t vfs_getextattr_value_by_name(struct fsal_obj_handle *obj_hdl, const char *xattr_name, caddr_t buffer_addr, size_t buffer_size, size_t *p_output_size) { struct vfs_fsal_obj_handle *obj_handle = NULL; int fd = -1; int rc = 0; int openflags; unsigned int index; fsal_errors_t fe; obj_handle = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); /* sanity checks */ if (!obj_hdl || !p_output_size || !buffer_addr || !xattr_name) return fsalstat(ERR_FSAL_FAULT, 0); /* look for this name */ for (index = 0; index < XATTR_COUNT; index++) { if (do_match_type(xattr_list[index].flags, obj_handle->obj_handle.type) && !strcmp(xattr_list[index].xattr_name, xattr_name)) { return vfs_getextattr_value_by_id(obj_hdl, index, buffer_addr, buffer_size, p_output_size); } } switch (obj_hdl->type) { case DIRECTORY: openflags = O_DIRECTORY; break; case SYMBOLIC_LINK: return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); default: openflags = O_RDWR; } fd = vfs_fsal_open(obj_handle, openflags, &fe); if (fd < 0) return fsalstat(fe, -fd); /* is it an xattr? */ rc = fgetxattr(fd, xattr_name, buffer_addr, buffer_size); if (rc < 0) { rc = errno; close(fd); return fsalstat(posix2fsal_error(rc), rc); } /* the xattr value can be a binary, or a string. * trying to determine its type... */ *p_output_size = rc; close(fd); return fsalstat(ERR_FSAL_NO_ERROR, 0); } fsal_status_t vfs_setextattr_value(struct fsal_obj_handle *obj_hdl, const char *xattr_name, caddr_t buffer_addr, size_t buffer_size, int create) { struct vfs_fsal_obj_handle *obj_handle = NULL; int fd = -1; fsal_errors_t fe; int rc = 0; obj_handle = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); fd = (obj_hdl->type == DIRECTORY) ? vfs_fsal_open(obj_handle, O_DIRECTORY, &fe) : vfs_fsal_open(obj_handle, O_RDWR, &fe); if (fd < 0) return fsalstat(fe, -fd); if (buffer_size == 0) rc = fsetxattr(fd, xattr_name, "", 1, create ? XATTR_CREATE : XATTR_REPLACE); else rc = fsetxattr(fd, xattr_name, (char *)buffer_addr, buffer_size, create ? XATTR_CREATE : XATTR_REPLACE); if (rc != 0) { rc = errno; close(fd); return fsalstat(posix2fsal_error(rc), rc); } close(fd); return fsalstat(ERR_FSAL_NO_ERROR, 0); } fsal_status_t vfs_setextattr_value_by_id(struct fsal_obj_handle *obj_hdl, unsigned int xattr_id, caddr_t buffer_addr, size_t buffer_size) { char name[MAXNAMLEN]; struct vfs_fsal_obj_handle *obj_handle = NULL; int fd = -1; fsal_errors_t fe; int rc = 0; obj_handle = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); if (attr_is_read_only(xattr_id)) return fsalstat(ERR_FSAL_PERM, 0); else if (xattr_id < XATTR_COUNT) return fsalstat(ERR_FSAL_PERM, 0); fd = (obj_hdl->type == DIRECTORY) ? vfs_fsal_open(obj_handle, O_DIRECTORY, &fe) : vfs_fsal_open(obj_handle, O_RDWR, &fe); if (fd < 0) return fsalstat(fe, -fd); rc = xattr_id_to_name(fd, xattr_id, name); if (rc) { int minor = errno; close(fd); return fsalstat(-rc, minor); } close(fd); return vfs_setextattr_value(obj_hdl, name, buffer_addr, buffer_size, false); } fsal_status_t vfs_remove_extattr_by_id(struct fsal_obj_handle *obj_hdl, unsigned int xattr_id) { int rc; char name[MAXNAMLEN]; struct vfs_fsal_obj_handle *obj_handle = NULL; int fd = -1; fsal_errors_t fe; obj_handle = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); fd = (obj_hdl->type == DIRECTORY) ? vfs_fsal_open(obj_handle, O_DIRECTORY, &fe) : vfs_fsal_open(obj_handle, O_RDWR, &fe); if (fd < 0) return fsalstat(fe, -fd); rc = xattr_id_to_name(fd, xattr_id, name); if (rc) { int minor = errno; close(fd); return fsalstat(-rc, minor); } rc = fremovexattr(fd, name); if (rc) { rc = errno; close(fd); return fsalstat(posix2fsal_error(rc), rc); } close(fd); return fsalstat(ERR_FSAL_NO_ERROR, 0); } fsal_status_t vfs_remove_extattr_by_name(struct fsal_obj_handle *obj_hdl, const char *xattr_name) { struct vfs_fsal_obj_handle *obj_handle = NULL; int fd = -1; int rc = 0; fsal_errors_t fe; obj_handle = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); fd = (obj_hdl->type == DIRECTORY) ? vfs_fsal_open(obj_handle, O_DIRECTORY, &fe) : vfs_fsal_open(obj_handle, O_RDWR, &fe); if (fd < 0) return fsalstat(fe, -fd); rc = fremovexattr(fd, xattr_name); if (rc) { rc = errno; close(fd); return fsalstat(posix2fsal_error(rc), rc); } close(fd); return fsalstat(ERR_FSAL_NO_ERROR, 0); } nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/xfs/000077500000000000000000000000001324272410200172655ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/xfs/.gitignore000066400000000000000000000000171324272410200212530ustar00rootroot00000000000000libxfsfdhdl.so nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/xfs/CMakeLists.txt000066400000000000000000000013031324272410200220220ustar00rootroot00000000000000add_definitions( -D__USE_GNU -D_GNU_SOURCE ) SET(fsalxfs_LIB_SRCS main.c ../export.c ../handle.c handle_syscalls.c ../file.c ../xattrs.c ../state.c ../vfs_methods.h subfsal_xfs.c ) add_library(fsalxfs MODULE ${fsalxfs_LIB_SRCS}) add_sanitizers(fsalxfs) if(PATH_LIBHANDLE) add_library(handle SHARED IMPORTED) add_sanitizers(handle) set_target_properties(handle PROPERTIES IMPORTED_LOCATION ${PATH_LIBHANDLE}) endif(PATH_LIBHANDLE) target_link_libraries(fsalxfs gos ${SYSTEM_LIBRARIES} ) target_link_libraries(fsalxfs handle) set_target_properties(fsalxfs PROPERTIES VERSION 4.2.0 SOVERSION 4) install(TARGETS fsalxfs COMPONENT fsal DESTINATION ${FSAL_DESTINATION} ) nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/xfs/handle_syscalls.c000066400000000000000000000234531324272410200226100ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "fsal.h" #include "fsal_handle_syscalls.h" #include #include #include #include #include "gsh_list.h" #include "fsal_convert.h" #include "FSAL/fsal_commonlib.h" #include "../vfs_methods.h" #include "handle_syscalls.h" void display_xfs_handle(struct display_buffer *dspbuf, struct vfs_file_handle *fh) { xfs_handle_t *hdl = (xfs_handle_t *) fh->handle_data; (void) display_printf(dspbuf, "Handle len %hhu: fsid=0x%016" PRIx32".0x%016"PRIx32 " fid_len=%"PRIu16 " fid_pad=%"PRIu16 " fid_gen=%"PRIu32 " fid_ino=%"PRIu64, fh->handle_len, hdl->ha_fsid.val[0], hdl->ha_fsid.val[1], hdl->ha_fid.fid_len, hdl->ha_fid.fid_pad, hdl->ha_fid.fid_gen, hdl->ha_fid.fid_ino); } #define LogXFSHandle(fh) \ do { \ if (isMidDebug(COMPONENT_FSAL)) { \ char buf[256] = "\0"; \ struct display_buffer dspbuf = \ {sizeof(buf), buf, buf}; \ \ display_xfs_handle(&dspbuf, fh); \ \ LogMidDebug(COMPONENT_FSAL, "%s", buf); \ } \ } while (0) static int xfs_fsal_bulkstat_inode(int fd, xfs_ino_t ino, xfs_bstat_t *bstat) { xfs_fsop_bulkreq_t bulkreq; __u64 i = ino; bulkreq.lastip = &i; bulkreq.icount = 1; bulkreq.ubuffer = bstat; bulkreq.ocount = NULL; return ioctl(fd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq); } static int xfs_fsal_inode2handle(int fd, ino_t ino, vfs_file_handle_t *fh) { xfs_bstat_t bstat; xfs_handle_t *hdl = (xfs_handle_t *) fh->handle_data; void *data; size_t sz; if (fh->handle_len < sizeof(*hdl)) { errno = E2BIG; return -1; } /* Get the information pertinent to this inode, and * the file handle for the reference fd. */ if ((xfs_fsal_bulkstat_inode(fd, ino, &bstat) < 0) || (fd_to_handle(fd, &data, &sz) < 0)) return -1; /* Copy the fsid from the reference fd */ memcpy(&hdl->ha_fsid, data, sizeof(xfs_fsid_t)); /* Fill in the rest of the handle with the information * pertinent to this inode. */ hdl->ha_fid.fid_len = sizeof(xfs_handle_t) - sizeof(xfs_fsid_t) - sizeof(hdl->ha_fid.fid_len); hdl->ha_fid.fid_pad = 0; hdl->ha_fid.fid_gen = bstat.bs_gen; hdl->ha_fid.fid_ino = bstat.bs_ino; fh->handle_len = sizeof(*hdl); free_handle(data, sz); return 0; } int vfs_open_by_handle(struct vfs_filesystem *fs, vfs_file_handle_t *fh, int openflags, fsal_errors_t *fsal_error) { int fd; LogXFSHandle(fh); if (openflags == (O_PATH | O_NOACCESS)) openflags = O_DIRECTORY; fd = open_by_handle(fh->handle_data, fh->handle_len, openflags); if (fd < 0) { fd = -errno; if (fd == -ENOENT) *fsal_error = posix2fsal_error(ESTALE); else *fsal_error = posix2fsal_error(-fd); } return fd; } int vfs_fd_to_handle(int fd, struct fsal_filesystem *fs, vfs_file_handle_t *fh) { void *data; size_t sz; int rv = 0; if (fd_to_handle(fd, &data, &sz) < 0) return -1; if (sz >= fh->handle_len) { errno = E2BIG; rv = -1; } else { memcpy(fh->handle_data, data, sz); fh->handle_len = sz; LogXFSHandle(fh); } free_handle(data, sz); return rv; } int vfs_name_to_handle(int fd, struct fsal_filesystem *fs, const char *name, vfs_file_handle_t *fh) { int retval; struct stat stat; if (fstatat(fd, name, &stat, AT_SYMLINK_NOFOLLOW) < 0) return -1; if (S_ISDIR(stat.st_mode) || S_ISREG(stat.st_mode)) { int e; int tmpfd = openat(fd, name, O_RDONLY | O_NOFOLLOW, 0600); if (tmpfd < 0) return -1; retval = vfs_fd_to_handle(tmpfd, fs, fh); e = errno; close(tmpfd); errno = e; } else { retval = xfs_fsal_inode2handle(fd, stat.st_ino, fh); } LogXFSHandle(fh); return retval; } int vfs_readlink(struct vfs_fsal_obj_handle *hdl, fsal_errors_t *ferr) { char ldata[MAXPATHLEN + 1]; int retval; LogXFSHandle(hdl->handle); retval = readlink_by_handle(hdl->handle->handle_data, hdl->handle->handle_len, ldata, sizeof(ldata)); if (retval < 0) { retval = -errno; *ferr = posix2fsal_error(retval); goto out; } ldata[retval] = '\0'; hdl->u.symlink.link_content = gsh_strdup(ldata); hdl->u.symlink.link_size = retval + 1; retval = 0; out: return retval; } int vfs_extract_fsid(vfs_file_handle_t *fh, enum fsid_type *fsid_type, struct fsal_fsid__ *fsid) { xfs_handle_t *hdl = (xfs_handle_t *) fh->handle_data; LogXFSHandle(fh); if (hdl->ha_fid.fid_pad != 0) { char handle_data[sizeof(struct fsal_fsid__)]; int rc; *fsid_type = (enum fsid_type) (hdl->ha_fid.fid_pad - 1); memcpy(handle_data, &hdl->ha_fsid, sizeof(hdl->ha_fsid)); memcpy(handle_data + sizeof(hdl->ha_fsid), &hdl->ha_fid.fid_ino, sizeof(hdl->ha_fid.fid_ino)); rc = decode_fsid(handle_data, sizeof(handle_data), fsid, *fsid_type); if (rc < 0) { errno = EINVAL; return rc; } return 0; } *fsid_type = FSID_TWO_UINT32; fsid->major = hdl->ha_fsid.val[0]; fsid->minor = hdl->ha_fsid.val[1]; return 0; } int vfs_encode_dummy_handle(vfs_file_handle_t *fh, struct fsal_filesystem *fs) { xfs_handle_t *hdl = (xfs_handle_t *) fh->handle_data; char handle_data[sizeof(struct fsal_fsid__)]; int rc; memset(handle_data, 0, sizeof(handle_data)); /* Pack fsid into handle_data */ rc = encode_fsid(handle_data, sizeof(handle_data), &fs->fsid, fs->fsid_type); if (rc < 0) { errno = EINVAL; return rc; } assert(sizeof(handle_data) == (sizeof(hdl->ha_fsid) + sizeof(hdl->ha_fid.fid_ino))); memcpy(&hdl->ha_fsid, handle_data, sizeof(hdl->ha_fsid)); memcpy(&hdl->ha_fid.fid_ino, handle_data + sizeof(hdl->ha_fsid), sizeof(hdl->ha_fid.fid_ino)); hdl->ha_fid.fid_len = sizeof(xfs_handle_t) - sizeof(xfs_fsid_t) - sizeof(hdl->ha_fid.fid_len); hdl->ha_fid.fid_pad = fs->fsid_type + 1; hdl->ha_fid.fid_gen = 0; fh->handle_len = sizeof(*hdl); LogXFSHandle(fh); return 0; } bool vfs_is_dummy_handle(vfs_file_handle_t *fh) { xfs_handle_t *hdl = (xfs_handle_t *) fh->handle_data; return hdl->ha_fid.fid_pad != 0; } bool vfs_valid_handle(struct gsh_buffdesc *desc) { xfs_handle_t *hdl = (xfs_handle_t *) desc->addr; bool fsid_type_ok = false; if ((desc->addr == NULL) || (desc->len != sizeof(xfs_handle_t))) return false; if (isMidDebug(COMPONENT_FSAL)) { char buf[256] = "\0"; struct display_buffer dspbuf = {sizeof(buf), buf, buf}; (void) display_printf(&dspbuf, "Handle len %d: fsid=0x%016" PRIx32".0x%016"PRIx32 " fid_len=%"PRIu16 " fid_pad=%"PRIu16 " fid_gen=%"PRIu32 " fid_ino=%"PRIu64, (int) desc->len, hdl->ha_fsid.val[0], hdl->ha_fsid.val[1], hdl->ha_fid.fid_len, hdl->ha_fid.fid_pad, hdl->ha_fid.fid_gen, hdl->ha_fid.fid_ino); LogMidDebug(COMPONENT_FSAL, "%s", buf); } if (hdl->ha_fid.fid_pad != 0) { switch ((enum fsid_type) (hdl->ha_fid.fid_pad - 1)) { case FSID_NO_TYPE: case FSID_ONE_UINT64: case FSID_MAJOR_64: case FSID_TWO_UINT64: case FSID_TWO_UINT32: case FSID_DEVICE: fsid_type_ok = true; break; } if (!fsid_type_ok) { LogDebug(COMPONENT_FSAL, "FSID Type %02"PRIu16" invalid", hdl->ha_fid.fid_pad - 1); return false; } if (hdl->ha_fid.fid_gen != 0) return false; } return hdl->ha_fid.fid_len == (sizeof(xfs_handle_t) - sizeof(xfs_fsid_t) - sizeof(hdl->ha_fid.fid_len)); } int vfs_get_root_handle(struct vfs_filesystem *vfs_fs, struct vfs_fsal_export *exp) { enum fsid_type fsid_type; struct fsal_fsid__ fsid; int fd; int retval; void *data; size_t sz; vfs_file_handle_t *fh; vfs_alloc_handle(fh); if (path_to_fshandle(vfs_fs->fs->path, &data, &sz) < 0) { retval = errno; LogMajor(COMPONENT_FSAL, "Export root %s could not be established for XFS error %s", vfs_fs->fs->path, strerror(retval)); return retval; } fd = open(vfs_fs->fs->path, O_RDONLY | O_DIRECTORY); if (fd < 0) { retval = errno; LogMajor(COMPONENT_FSAL, "Could not open XFS mount point %s: rc = %s (%d)", vfs_fs->fs->path, strerror(retval), retval); return retval; } retval = vfs_fd_to_handle(fd, vfs_fs->fs, fh); if (retval != 0) { retval = errno; LogMajor(COMPONENT_FSAL, "Get root handle for %s failed with %s (%d)", vfs_fs->fs->path, strerror(retval), retval); goto errout; } /* Extract fsid from the root handle and re-index the filesystem * using it. This is because the file handle already has an fsid in * it. */ (void) vfs_extract_fsid(fh, &fsid_type, &fsid); retval = re_index_fs_fsid(vfs_fs->fs, fsid_type, &fsid); if (retval < 0) { LogCrit(COMPONENT_FSAL, "Could not re-index XFS file system fsid for %s", vfs_fs->fs->path); retval = -retval; } errout: close(fd); return retval; } nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/xfs/handle_syscalls.h000066400000000000000000000021501324272410200226040ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ /* defined by libhandle but no prototype in xfs/handle.h */ int fd_to_handle(int fd, void **hanp, size_t *hlen); nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/xfs/main.c000066400000000000000000000150711324272410200203610ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ /* main.c * Module core functions */ #include "config.h" #include /* used for 'dirname' */ #include #include #include #include #include "fsal.h" #include "FSAL/fsal_init.h" #include "fsal_handle_syscalls.h" /* VFS FSAL module private storage */ /* defined the set of attributes supported with POSIX */ #ifndef ENABLE_VFS_DEBUG_ACL #define XFS_SUPPORTED_ATTRIBUTES ((const attrmask_t) (ATTRS_POSIX)) #else #define XFS_SUPPORTED_ATTRIBUTES ((const attrmask_t) (ATTRS_POSIX | ATTR_ACL)) #endif struct xfs_fsal_module { struct fsal_module fsal; struct fsal_staticfsinfo_t fs_info; /* xfsfs_specific_initinfo_t specific_info; placeholder */ }; const char myname[] = "XFS"; /* filesystem info for XFS */ static struct fsal_staticfsinfo_t default_posix_info = { .maxfilesize = UINT64_MAX, .maxlink = _POSIX_LINK_MAX, .maxnamelen = 1024, .maxpathlen = 1024, .no_trunc = true, .chown_restricted = true, .case_insensitive = false, .case_preserving = true, .lock_support = false, .lock_support_async_block = false, .named_attr = true, .unique_handles = true, .lease_time = {10, 0}, .acl_support = FSAL_ACLSUPPORT_ALLOW, .homogenous = true, .supported_attrs = XFS_SUPPORTED_ATTRIBUTES, .maxread = FSAL_MAXIOSIZE, .maxwrite = FSAL_MAXIOSIZE, .link_supports_permission_checks = false, }; static struct config_item xfs_params[] = { CONF_ITEM_BOOL("link_support", true, fsal_staticfsinfo_t, link_support), CONF_ITEM_BOOL("symlink_support", true, fsal_staticfsinfo_t, symlink_support), CONF_ITEM_BOOL("cansettime", true, fsal_staticfsinfo_t, cansettime), CONF_ITEM_UI64("maxread", 512, FSAL_MAXIOSIZE, FSAL_MAXIOSIZE, fsal_staticfsinfo_t, maxread), CONF_ITEM_UI64("maxwrite", 512, FSAL_MAXIOSIZE, FSAL_MAXIOSIZE, fsal_staticfsinfo_t, maxwrite), CONF_ITEM_MODE("umask", 0, fsal_staticfsinfo_t, umask), CONF_ITEM_BOOL("auth_xdev_export", false, fsal_staticfsinfo_t, auth_exportpath_xdev), CONF_ITEM_MODE("xattr_access_rights", 0400, fsal_staticfsinfo_t, xattr_access_rights), CONFIG_EOL }; struct config_block xfs_param = { .dbus_interface_name = "org.ganesha.nfsd.config.fsal.xfs", .blk_desc.name = "XFS", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = noop_conf_init, .blk_desc.u.blk.params = xfs_params, .blk_desc.u.blk.commit = noop_conf_commit }; /* private helper for export object */ struct fsal_staticfsinfo_t *vfs_staticinfo(struct fsal_module *hdl) { struct xfs_fsal_module *myself; myself = container_of(hdl, struct xfs_fsal_module, fsal); return &myself->fs_info; } /* Module methods */ /* init_config * must be called with a reference taken (via lookup_fsal) */ static fsal_status_t init_config(struct fsal_module *fsal_hdl, config_file_t config_struct, struct config_error_type *err_type) { struct xfs_fsal_module *xfs_me = container_of(fsal_hdl, struct xfs_fsal_module, fsal); #ifdef F_OFD_GETLK int fd, rc; struct flock lock; char *temp_name; #endif xfs_me->fs_info = default_posix_info; /* copy the consts */ #ifdef F_OFD_GETLK /* If on a system that might support OFD locks, verify them. * Only if they exist will we declare lock support. */ LogInfo(COMPONENT_FSAL, "FSAL_XFS testing OFD Locks"); temp_name = gsh_strdup("/tmp/ganesha.nfsd.locktestXXXXXX"); fd = mkstemp(temp_name); if (fd >= 0) { lock.l_whence = SEEK_SET; lock.l_type = F_RDLCK; lock.l_start = 0; lock.l_len = 0; lock.l_pid = 0; rc = fcntl(fd, F_OFD_GETLK, &lock); if (rc == 0) xfs_me->fs_info.lock_support = true; else LogInfo(COMPONENT_FSAL, "Could not use OFD locks"); close(fd); unlink(temp_name); } else { LogCrit(COMPONENT_FSAL, "Could not create file %s to test OFD locks", temp_name); } gsh_free(temp_name); #endif if (xfs_me->fs_info.lock_support) LogInfo(COMPONENT_FSAL, "FSAL_XFS enabling OFD Locks"); else LogInfo(COMPONENT_FSAL, "FSAL_XFS disabling lock support"); (void) load_config_from_parse(config_struct, &xfs_param, &xfs_me->fs_info, true, err_type); if (!config_error_is_harmless(err_type)) return fsalstat(ERR_FSAL_INVAL, 0); display_fsinfo(&xfs_me->fs_info); LogFullDebug(COMPONENT_FSAL, "Supported attributes constant = 0x%" PRIx64, XFS_SUPPORTED_ATTRIBUTES); LogFullDebug(COMPONENT_FSAL, "Supported attributes default = 0x%" PRIx64, default_posix_info.supported_attrs); LogDebug(COMPONENT_FSAL, "FSAL INIT: Supported attributes mask = 0x%" PRIx64, xfs_me->fs_info.supported_attrs); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* Internal XFS method linkage to export object */ fsal_status_t vfs_create_export(struct fsal_module *fsal_hdl, void *parse_node, struct config_error_type *err_type, const struct fsal_up_vector *up_ops); /* Module initialization. * Called by dlopen() to register the module * keep a private pointer to me in myself */ /* my module private storage */ static struct xfs_fsal_module XFS; /* linkage to the exports and handle ops initializers */ MODULE_INIT void xfs_init(void) { int retval; struct fsal_module *myself = &XFS.fsal; retval = register_fsal(myself, myname, FSAL_MAJOR_VERSION, FSAL_MINOR_VERSION, FSAL_ID_NO_PNFS); if (retval != 0) { fprintf(stderr, "XFS module failed to register"); return; } myself->m_ops.create_export = vfs_create_export; myself->m_ops.init_config = init_config; } MODULE_FINI void xfs_unload(void) { int retval; retval = unregister_fsal(&XFS.fsal); if (retval != 0) { fprintf(stderr, "XFS module failed to unregister"); return; } } nfs-ganesha-2.6.0/src/FSAL/FSAL_VFS/xfs/subfsal_xfs.c000066400000000000000000000042001324272410200217440ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) CohortFS LLC, 2014 * Author: Daniel Gryniewicz dang@cohortfs.com * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* export_panfs.c * XFS Sub-FSAL export object */ #include "config.h" #include "fsal_types.h" #include "fsal_api.h" #include "../vfs_methods.h" #include "../subfsal.h" /* Export */ static struct config_item export_params[] = { CONF_ITEM_NOOP("name"), CONFIG_EOL }; static struct config_block export_param_block = { .dbus_interface_name = "org.ganesha.nfsd.config.fsal.xfs-export%d", .blk_desc.name = "FSAL", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = noop_conf_init, .blk_desc.u.blk.params = export_params, .blk_desc.u.blk.commit = noop_conf_commit }; struct config_block *vfs_sub_export_param = &export_param_block; /* Handle syscalls */ void vfs_sub_fini(struct vfs_fsal_export *myself) { } void vfs_sub_init_export_ops(struct vfs_fsal_export *myself, const char *export_path) { } int vfs_sub_init_export(struct vfs_fsal_export *myself) { return 0; } struct vfs_fsal_obj_handle *vfs_sub_alloc_handle(void) { struct vfs_fsal_obj_handle *hdl; hdl = gsh_calloc(1, (sizeof(struct vfs_fsal_obj_handle) + sizeof(vfs_file_handle_t))); hdl->handle = (vfs_file_handle_t *) &hdl[1]; return hdl; } int vfs_sub_init_handle(struct vfs_fsal_export *myself, struct vfs_fsal_obj_handle *hdl, const char *path) { return 0; } nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/000077500000000000000000000000001324272410200201035ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/CMakeLists.txt000066400000000000000000000001441324272410200226420ustar00rootroot00000000000000if(USE_FSAL_NULL) add_subdirectory(FSAL_NULL) endif(USE_FSAL_NULL) add_subdirectory(FSAL_MDCACHE) nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/000077500000000000000000000000001324272410200217345ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/CMakeLists.txt000066400000000000000000000011461324272410200244760ustar00rootroot00000000000000add_definitions( -D__USE_GNU -D_GNU_SOURCE ) if(USE_DBUS) include_directories( ${DBUS_INCLUDE_DIRS} ) endif(USE_DBUS) set( LIB_PREFIX 64) ########### next target ############### SET(fsalmdcache_LIB_SRCS mdcache_avl.h mdcache_ext.h mdcache_int.h mdcache_hash.h mdcache_lru.h mdcache_handle.c mdcache_file.c mdcache_xattrs.c mdcache_main.c mdcache_export.c mdcache_helpers.c mdcache_lru.c mdcache_hash.c mdcache_avl.c mdcache_read_conf.c mdcache_up.c ) add_library(fsalmdcache STATIC ${fsalmdcache_LIB_SRCS}) add_sanitizers(fsalmdcache) ########### install files ############### nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_avl.c000066400000000000000000000540151324272410200243330ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) 2010, The Linux Box Corporation * Contributor : Matt Benjamin * * Some portions Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @addtogroup FSAL_MDCACHE * @{ */ /** * @file mdcache_avl.c * @brief AVL tree for caching directory entries */ #include "config.h" #include "log.h" #include "fsal.h" #include "mdcache_int.h" #include "mdcache_avl.h" #include "murmur3.h" #include "city.h" #include #include #include #include #include #include void mdcache_avl_init(mdcache_entry_t *entry) { avltree_init(&entry->fsobj.fsdir.avl.t, avl_dirent_hk_cmpf, 0 /* flags */); avltree_init(&entry->fsobj.fsdir.avl.c, avl_dirent_hk_cmpf, 0 /* flags */); avltree_init(&entry->fsobj.fsdir.avl.ck, avl_dirent_ck_cmpf, 0 /* flags */); avltree_init(&entry->fsobj.fsdir.avl.sorted, avl_dirent_sorted_cmpf, 0 /* flags */); } static inline struct avltree_node * avltree_inline_lookup_hk(const struct avltree_node *key, const struct avltree *tree) { return avltree_inline_lookup(key, tree, avl_dirent_hk_cmpf); } void avl_dirent_set_deleted(mdcache_entry_t *entry, mdcache_dir_entry_t *v) { struct avltree_node *node; mdcache_dir_entry_t *next; LogFullDebug(COMPONENT_CACHE_INODE, "Delete dir entry %p %s", v, v->name); #ifdef DEBUG_MDCACHE assert(entry->content_lock.__data.__writer); #endif assert(!(v->flags & DIR_ENTRY_FLAG_DELETED)); node = avltree_inline_lookup_hk(&v->node_hk, &entry->fsobj.fsdir.avl.t); assert(node); avltree_remove(&v->node_hk, &entry->fsobj.fsdir.avl.t); v->flags |= DIR_ENTRY_FLAG_DELETED; mdcache_key_delete(&v->ckey); /* save cookie in deleted avl */ node = avltree_insert(&v->node_hk, &entry->fsobj.fsdir.avl.c); assert(!node); /* Do stuff if chunked... */ if (v->chunk != NULL) { struct dir_chunk *chunk = v->chunk; mdcache_entry_t *parent = chunk->parent; if (v->ck == parent->fsobj.fsdir.first_ck) { /* This is no longer the first entry in the directory... * Find the first non-deleted entry. */ next = v; while (next != NULL && next->flags & DIR_ENTRY_FLAG_DELETED) { next = glist_next_entry(&chunk->dirents, mdcache_dir_entry_t, chunk_list, &next->chunk_list); if (next != NULL) { /* Evaluate it in the while condition. */ continue; } /* End of the chunk... */ /** @todo FSF This entire chunk is deleted * entries, we really should just free * the chunk... */ /* Look for the next chunk */ if (chunk->next_ck != 0 && mdcache_avl_lookup_ck(parent, chunk->next_ck, &next)) { chunk = next->chunk; } } if (next != NULL) { /* This entry is now the first_ck. */ parent->fsobj.fsdir.first_ck = next->ck; } else { /* There are no more cached chunks */ parent->fsobj.fsdir.first_ck = 0; } } /* For now leave the entry in the ck hash so we can re-start * directory from that position, this means that directory * enumeration will have to skip deleted entries. */ } } /** * @brief Remove a dirent from a chunk. * * @param[in] dirent The dirent to remove * */ void unchunk_dirent(mdcache_dir_entry_t *dirent) { mdcache_entry_t *parent = dirent->chunk->parent; LogFullDebug(COMPONENT_CACHE_INODE, "Unchunking %p %s", dirent, dirent->name); #ifdef DEBUG_MDCACHE assert(parent->content_lock.__data.__writer); #endif /* Dirent is part of a chunk, must do additional clean * up. */ /* Remove from chunk */ glist_del(&dirent->chunk_list); /* Remove from FSAL cookie AVL tree */ avltree_remove(&dirent->node_ck, &parent->fsobj.fsdir.avl.ck); /* Check if this was the first dirent in the directory. */ if (parent->fsobj.fsdir.first_ck == dirent->ck) { /* The first dirent in the directory is no longer chunked... */ parent->fsobj.fsdir.first_ck = 0; } /* Check if this entry was in the sorted AVL tree */ if (dirent->flags & DIR_ENTRY_SORTED) { /* It was, remove it. */ avltree_remove(&dirent->node_sorted, &parent->fsobj.fsdir.avl.sorted); } /* Just make sure... */ dirent->chunk = NULL; } /** * @brief Remove and free a dirent. * * @param[in] parent The directory removing from * @param[in] dirent The dirent to remove * */ void mdcache_avl_remove(mdcache_entry_t *parent, mdcache_dir_entry_t *dirent) { struct dir_chunk *chunk = dirent->chunk; if (dirent->flags & DIR_ENTRY_FLAG_DELETED) { /* Remove from deleted names tree */ avltree_remove(&dirent->node_hk, &parent->fsobj.fsdir.avl.c); } else { /* Remove from active names tree */ avltree_remove(&dirent->node_hk, &parent->fsobj.fsdir.avl.t); } if (dirent->chunk != NULL) { /* Dirent belongs to a chunk so remove it from the chunk. */ unchunk_dirent(dirent); } else { /* The dirent might be a detached dirent on an LRU list */ rmv_detached_dirent(parent, dirent); } if (dirent->ckey.kv.len) mdcache_key_delete(&dirent->ckey); gsh_free(dirent); LogFullDebug(COMPONENT_CACHE_INODE, "Just freed dirent %p from chunk %p parent %p", dirent, chunk, (chunk) ? chunk->parent : NULL); } /** * @brief Insert a dirent into the lookup by name AVL tree. * * @param[in] entry The directory * @param[in] v The dirent * @param[in] j Part of iteration count * @param[in] j2 Part of iterarion count * @param[in,out] vout The existing entry when there is name collision * * @retval 0 Success * @retval -1 Failure * @retval -2 Name collision * */ static inline int mdcache_avl_insert_impl(mdcache_entry_t *entry, mdcache_dir_entry_t *v, int j, int j2, mdcache_dir_entry_t **vout) { int code = -1; struct avltree_node *node; mdcache_dir_entry_t *v2; struct avltree *t = &entry->fsobj.fsdir.avl.t; struct avltree *c = &entry->fsobj.fsdir.avl.c; LogFullDebug(COMPONENT_CACHE_INODE, "Insert dir entry %p %s j=%d j2=%d", v, v->name, j, j2); #ifdef DEBUG_MDCACHE assert(entry->content_lock.__data.__writer); #endif /* first check for a previously-deleted entry */ node = avltree_inline_lookup_hk(&v->node_hk, c); /* We must not allow persist-cookies to overrun resource * management processes. Note this is not a limit on * directory size, but rather indirectly on the lifetime * of cookie offsets of directories under mutation. */ if ((!node) && (avltree_size(c) > mdcache_param.dir.avl_max_deleted)) { /* ie, recycle the smallest deleted entry */ node = avltree_first(c); } if (node) { /* We can't really re-use slots safely since filenames can * have wildly differing lengths, so remove and free the * "deleted" entry. */ mdcache_avl_remove(entry, avltree_container_of(node, mdcache_dir_entry_t, node_hk)); node = NULL; } node = avltree_insert(&v->node_hk, t); if (!node) { /* success, note iterations */ v->hk.p = j + j2; if (entry->fsobj.fsdir.avl.collisions < v->hk.p) entry->fsobj.fsdir.avl.collisions = v->hk.p; LogDebug(COMPONENT_CACHE_INODE, "inserted new dirent for %s on entry=%p cookie=%" PRIu64 " collisions %d", v->name, entry, v->hk.k, entry->fsobj.fsdir.avl.collisions); code = 0; } else { v2 = avltree_container_of(node, mdcache_dir_entry_t, node_hk); if (strcmp(v->name, v2->name) == 0) { /* Same name, probably already inserted. */ LogDebug(COMPONENT_CACHE_INODE, "Already existent when inserting new dirent on entry=%p name=%s, cookie=%" PRIu64, entry, v->name, v->hk.k); code = -2; if (vout != NULL) *vout = v2; } else { /* Hash collision, keep trying at current j, j2 */ LogDebug(COMPONENT_CACHE_INODE, "Hash collision with %s when inserting new dirent on entry=%p name=%s, cookie=%" PRIu64 " this should never happen.", v2->name, entry, v->name, v->hk.k); code = -1; } } return code; } /** * @brief Insert a dirent into the lookup by FSAL cookie AVL tree. * * @param[in] entry The directory * @param[in] v The dirent * * @retval 0 Success * @retval -1 Failure * */ int mdcache_avl_insert_ck(mdcache_entry_t *entry, mdcache_dir_entry_t *v) { struct avltree_node *node; LogFullDebug(COMPONENT_CACHE_INODE, "Insert dirent %p for %s on entry=%p FSAL cookie=%"PRIx64, v, v->name, entry, v->ck); #ifdef DEBUG_MDCACHE assert(entry->content_lock.__data.__writer); #endif node = avltree_inline_insert(&v->node_ck, &entry->fsobj.fsdir.avl.ck, avl_dirent_ck_cmpf); if (!node) { LogDebug(COMPONENT_CACHE_INODE, "inserted dirent %p for %s on entry=%p FSAL cookie=%" PRIx64, v, v->name, entry, v->ck); return 0; } /* already inserted */ LogDebug(COMPONENT_CACHE_INODE, "Already existent when inserting dirent %p for %s on entry=%p FSAL cookie=%" PRIx64 ", duplicated directory cookies make READDIR unreliable.", v, v->name, entry, v->ck); return -1; } #define MIN_COOKIE_VAL 3 /* * Insert with quadatic, linear probing. A unique k is assured for * any k whenever size(t) < max(uint64_t). * * First try quadratic probing, with coeff. 2 (since m = 2^n.) * A unique k is not assured, since the codomain is not prime. * If this fails, fall back to linear probing from hk.k+1. * * On return, the stored key is in v->hk.k, the iteration * count in v->hk.p. * * In the case of a name collision, assuming the ckey in the dirents matches, * and the flags are the same, then this will be treated as a success and the * dirent passed in will be freed and the dirent will be set to the found one. * * If any error occurs, the passed in dirent will be freed and the dirent * will be set to NULL. * * @param[in] entry The directory * @param[in] dirent The dirent * * @retval 0 Success * @retval -1 Hash collision after 2^65 attempts * @retval -2 Name collision * @retval -3 Duplicate file name but different cookie * @retval -4 FSAL cookie collision * **/ int mdcache_avl_qp_insert(mdcache_entry_t *entry, mdcache_dir_entry_t **dirent) { mdcache_dir_entry_t *v = *dirent, *v2; #if AVL_HASH_MURMUR3 uint32_t hk[4]; #endif int j, j2, code = -1; LogFullDebug(COMPONENT_CACHE_INODE, "Insert dir entry %p %s", v, v->name); #ifdef DEBUG_MDCACHE assert(entry->content_lock.__data.__writer); #endif /* don't permit illegal cookies */ #if AVL_HASH_MURMUR3 MurmurHash3_x64_128(v->name, strlen(v->name), 67, hk); memcpy(&v->hk.k, hk, 8); #else v->hk.k = CityHash64WithSeed(v->name, strlen(v->name), 67); #endif #ifdef _USE_9P /* tmp hook : it seems like client running v9fs dislike "negative" * cookies just kill the sign bit, making * cookies 63 bits... */ v->hk.k &= ~(1ULL << 63); #endif /* XXX would we really wait for UINT64_MAX? if not, how many * probes should we attempt? */ for (j = 0; j < UINT64_MAX; j++) { v->hk.k = (v->hk.k + (j * 2)); /* reject values 0, 1 and 2 */ if (v->hk.k < MIN_COOKIE_VAL) continue; again: code = mdcache_avl_insert_impl(entry, v, j, 0, &v2); if (code >= 0) { if (v->chunk != NULL) { /* This entry is part of a chunked directory * enter it into the "by FSAL cookie" avl also. */ code = mdcache_avl_insert_ck(entry, v); if (code < 0) { /* We failed to insert into FSAL cookie * AVL tree, remove from lookup by name * AVL tree. */ avltree_remove(&v->node_hk, &entry ->fsobj.fsdir.avl.t); v2 = NULL; code = -4; goto out; } } if (isFullDebug(COMPONENT_CACHE_INODE)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = { sizeof(str), str, str }; (void) display_mdcache_key(&dspbuf, &v->ckey); LogFullDebug(COMPONENT_CACHE_INODE, "Inserted dirent %s with ckey %s", v->name, str); } return code; } else if (code == -1) { /* Deal with hash collision, skip it and try next hash. */ continue; } /* Deal with name collision. */ if (mdcache_key_cmp(&v->ckey, &v2->ckey) != 0) { /* The two names don't seem to have the same object * handle digest. Discard the old dirent and try again. */ if (isFullDebug(COMPONENT_CACHE_INODE)) { char str1[LOG_BUFF_LEN / 2] = "\0"; char str2[LOG_BUFF_LEN / 2] = "\0"; struct display_buffer dspbuf1 = { sizeof(str1), str1, str1 }; struct display_buffer dspbuf2 = { sizeof(str2), str2, str2 }; (void) display_mdcache_key(&dspbuf1, &v->ckey); (void) display_mdcache_key(&dspbuf2, &v2->ckey); LogFullDebug(COMPONENT_CACHE_INODE, "Keys for %s don't match v=%s v2=%s", v->name, str1, str2); } /* Remove the found dirent. */ mdcache_avl_remove(entry, v2); v2 = NULL; goto again; } /* The v2 entry should NOT be deleted... */ assert((v2->flags & DIR_ENTRY_FLAG_DELETED) == 0); if (v->chunk != NULL && v2->chunk == NULL) { /* This entry is part of a chunked directory enter the * old dirent into the "by FSAL cookie" AVL tree also. * We need to update the old dirent for the FSAL cookie * bits... */ v2->chunk = v->chunk; v2->ck = v->ck; v2->eod = v->eod; code = mdcache_avl_insert_ck(entry, v2); if (code < 0) { /* We failed to insert into FSAL cookie AVL * tree, leave in lookup by name AVL tree but * don't return a dirent. Also, undo the changes * to the old dirent. */ v2->chunk = NULL; v2->ck = 0; v2 = NULL; code = -4; } else { if (isFullDebug(COMPONENT_CACHE_INODE)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = { sizeof(str), str, str }; (void) display_mdcache_key(&dspbuf, &v2->ckey); LogFullDebug(COMPONENT_CACHE_INODE, "Updated dirent %p with ck=%" PRIx64 " and chunk %p eod=%s ckey=%s", v2, v2->ck, v2->chunk, v2->eod ? "true" : "false", str); } /* Remove v2 from the detached entry cache */ rmv_detached_dirent(entry, v2); } } else if (v->chunk != NULL && v2->chunk != NULL) { /* Handle cases where existing entry is in a chunk as * well as previous entry. Somehow an entry is showing * up twice. Will prefer existing entry. */ if (v->ck == v2->ck) { /* completely a duplicate entry, ignore it */ LogDebug(COMPONENT_CACHE_INODE, "Duplicate filename %s insert into chunk %p, existing was in chunk %p, ignoring", v->name, v->chunk, v2->chunk); code = 0; } else { /* This is an odd case, and this error is * handled. Inform this condition via debug. */ LogDebug(COMPONENT_CACHE_INODE, "Duplicate filename %s with different cookies ckey %" PRIx64 " chunk %p don't match existing ckey %" PRIx64" chunk %p", v->name, v->ck, v->chunk, v2->ck, v2->chunk); code = -3; v2 = NULL; } } else { /* New entry is not in a chunk, existing entry might * be in a chunk, in any case, the entry already * exists so we are good. */ LogFullDebug(COMPONENT_CACHE_INODE, "Duplicate insert of %s v->chunk=%p v2->chunk=%p", v->name, v->chunk, v2->chunk); code = 0; } goto out; } LogCrit(COMPONENT_CACHE_INODE, "could not insert at j=%d (%s)", j, v->name); #ifdef _USE_9P /* tmp hook : it seems like client running v9fs dislike "negative" * cookies */ v->hk.k &= ~(1ULL << 63); #endif for (j2 = 1 /* tried j=0 */; j2 < UINT64_MAX; j2 += 2) { v->hk.k = v->hk.k + j2; code = mdcache_avl_insert_impl(entry, v, j, j2, NULL); if (code >= 0) return code; } LogCrit(COMPONENT_CACHE_INODE, "could not insert at j=%d (%s)", j, v->name); code = -1; v2 = NULL; out: mdcache_key_delete(&v->ckey); gsh_free(v); *dirent = v2; return code; } /** * @brief Look up a dirent by k-value * * Look up a dirent by k-value. If @ref MDCACHE_FLAG_NEXT_ACTIVE is set in @a * flags then the dirent after the give k-value is returend (this is for * readdir). If @ref MDCACHE_FLAG_ONLY_ACTIVE is set, then only the active tree * is searched. Otherwise, the deleted tree is searched, and, if found, the * dirent after that deleted dirnet is returned. * * @param[in] entry Directory to search in * @param[in] k K-value to find * @param[in] flags MDCACHE_FLAG_* * @param[out] dirent Returned dirent, if found, NULL otherwise * @return MDCACHE_AVL_NO_ERROR if found; MDCACHE_AVL_NOT_FOUND if not found; * MDCACHE_AVL_LAST if next requested and found was last; and * MDCACHE_AVL_DELETED if all subsequent dirents are deleted. */ enum mdcache_avl_err mdcache_avl_lookup_k(mdcache_entry_t *entry, uint64_t k, uint32_t flags, mdcache_dir_entry_t **dirent) { struct avltree *t = &entry->fsobj.fsdir.avl.t; struct avltree *c = &entry->fsobj.fsdir.avl.c; mdcache_dir_entry_t dirent_key[1]; struct avltree_node *node, *node2; *dirent = NULL; dirent_key->hk.k = k; node = avltree_inline_lookup_hk(&dirent_key->node_hk, t); if (node) { if (flags & MDCACHE_FLAG_NEXT_ACTIVE) /* client wants the cookie -after- the last we sent, and * the Linux 3.0 and 3.1.0-rc7 clients misbehave if we * resend the last one */ node = avltree_next(node); if (!node) { LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "seek to cookie=%" PRIu64 " fail (no next entry)", k); return MDCACHE_AVL_LAST; } } /* only the forward AVL is valid for conflict checking */ if (flags & MDCACHE_FLAG_ONLY_ACTIVE) goto done; /* Try the deleted AVL. If a node with hk.k == v->hk.k is found, * return its least upper bound in -t-, if any. */ if (!node) { node2 = avltree_inline_lookup_hk(&dirent_key->node_hk, c); if (node2) { node = avltree_sup(&dirent_key->node_hk, t); if (!node) return MDCACHE_AVL_DELETED; } LogDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "node %p found deleted supremum %p", node2, node); } done: if (node) { *dirent = avltree_container_of(node, mdcache_dir_entry_t, node_hk); return MDCACHE_AVL_NO_ERROR; } return MDCACHE_AVL_NOT_FOUND; } /** * @brief Look up a dirent by FSAL cookie * * Look up a dirent by FSAL cookie. * * @param[in] entry Directory to search in * @param[in] ck FSAL cookie to find * @param[out] dirent Returned dirent, if found, NULL otherwise * * @retval true if found * @retval false if not found */ bool mdcache_avl_lookup_ck(mdcache_entry_t *entry, uint64_t ck, mdcache_dir_entry_t **dirent) { struct avltree *tck = &entry->fsobj.fsdir.avl.ck; mdcache_dir_entry_t dirent_key[1]; mdcache_dir_entry_t *ent; struct avltree_node *node; *dirent = NULL; dirent_key->ck = ck; node = avltree_inline_lookup(&dirent_key->node_ck, tck, avl_dirent_ck_cmpf); if (node) { struct dir_chunk *chunk; /* This is the entry we are looking for... This function is * passed the cookie of the next entry of interest in the * directory. */ ent = avltree_container_of(node, mdcache_dir_entry_t, node_ck); chunk = ent->chunk; if (chunk == NULL) { /* This entry doesn't belong to a chunk, something * is horribly wrong. */ assert(!chunk); return false; } *dirent = ent; return true; } return false; } mdcache_dir_entry_t * mdcache_avl_qp_lookup_s(mdcache_entry_t *entry, const char *name, int maxj) { struct avltree *t = &entry->fsobj.fsdir.avl.t; struct avltree_node *node; mdcache_dir_entry_t *v2; #if AVL_HASH_MURMUR3 uint32_t hashbuff[4]; #endif int j; size_t namelen = strlen(name); mdcache_dir_entry_t v; LogFullDebug(COMPONENT_CACHE_INODE, "Lookup %s", name); #if AVL_HASH_MURMUR3 MurmurHash3_x64_128(name, namelen, 67, hashbuff); /* This seems to be correct. The avltree_lookup function looks as hk.k, but does no namecmp on its own, so there's no need to allocate space for or copy the name in the key. */ memcpy(&v.hk.k, hashbuff, 8); #else v.hk.k = CityHash64WithSeed(name, namelen, 67); #endif #ifdef _USE_9P /* tmp hook : it seems like client running v9fs dislike "negative" * cookies */ v.hk.k &= ~(1ULL << 63); #endif for (j = 0; j < maxj; j++) { v.hk.k = (v.hk.k + (j * 2)); node = avltree_lookup(&v.node_hk, t); if (node) { /* ensure that node is related to v */ v2 = avltree_container_of(node, mdcache_dir_entry_t, node_hk); if (strcmp(name, v2->name) == 0) { assert(!(v2->flags & DIR_ENTRY_FLAG_DELETED)); return v2; } } } LogFullDebug(COMPONENT_CACHE_INODE, "entry not found at j=%d (%s)", j, name); return NULL; } /** * @brief Remove and free all dirents from the dirent trees for a directory * * @param[in] parent The directory removing from */ void mdcache_avl_clean_trees(mdcache_entry_t *parent) { struct avltree_node *dirent_node; mdcache_dir_entry_t *dirent; #ifdef DEBUG_MDCACHE assert(parent->content_lock.__data.__writer); #endif while ((dirent_node = avltree_first(&parent->fsobj.fsdir.avl.t))) { dirent = avltree_container_of(dirent_node, mdcache_dir_entry_t, node_hk); LogFullDebug(COMPONENT_CACHE_INODE, "Invalidate %p %s", dirent, dirent->name); mdcache_avl_remove(parent, dirent); } while ((dirent_node = avltree_first(&parent->fsobj.fsdir.avl.c))) { dirent = avltree_container_of(dirent_node, mdcache_dir_entry_t, node_hk); LogFullDebug(COMPONENT_CACHE_INODE, "Invalidate %p %s", dirent, dirent->name); mdcache_avl_remove(parent, dirent); } } /** @} */ nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_avl.h000066400000000000000000000104671324272410200243430ustar00rootroot00000000000000/* * Copyright (C) 2010, Linux Box Corporation * All Rights Reserved * * Contributor: Matt Benjamin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @addtogroup FSAL_MDCACHE * @{ */ /** * @file mdcache_avl.h * @author Matt Benjamin * @brief Definitions supporting AVL dirent representation * */ /** * @page AVLOverview Overview * * Definitions supporting AVL dirent representation. The current * design represents dirents as a single AVL tree ordered by a * collision-resistent hash function (currently, Murmur3, which * appears to be several times faster than lookup3 on x86_64 * architecture). Quadratic probing is used to emulate perfect * hashing. Worst case behavior is challenging to reproduce. * Heuristic methods are used to detect worst-case scenarios and fall * back to tractable (e.g., lookup) algorthims. * */ #ifndef MDCACHE_AVL_H #define MDCACHE_AVL_H #include "config.h" #include "log.h" #include "mdcache_int.h" #include "avltree.h" static inline int avl_dirent_hk_cmpf(const struct avltree_node *lhs, const struct avltree_node *rhs) { mdcache_dir_entry_t *lk, *rk; lk = avltree_container_of(lhs, mdcache_dir_entry_t, node_hk); rk = avltree_container_of(rhs, mdcache_dir_entry_t, node_hk); if (lk->hk.k < rk->hk.k) return -1; if (lk->hk.k == rk->hk.k) return 0; return 1; } static inline int avl_dirent_ck_cmpf(const struct avltree_node *lhs, const struct avltree_node *rhs) { mdcache_dir_entry_t *lk, *rk; lk = avltree_container_of(lhs, mdcache_dir_entry_t, node_ck); rk = avltree_container_of(rhs, mdcache_dir_entry_t, node_ck); if (lk->ck < rk->ck) return -1; if (lk->ck == rk->ck) return 0; return 1; } static inline int avl_dirent_sorted_cmpf(const struct avltree_node *lhs, const struct avltree_node *rhs) { mdcache_dir_entry_t *lk, *rk; int rc; lk = avltree_container_of(lhs, mdcache_dir_entry_t, node_sorted); rk = avltree_container_of(rhs, mdcache_dir_entry_t, node_sorted); /* On create a dirent will not yet belong to a chunk, but only * one of the two nodes in comparison can not belong to a chunk. */ subcall( if (lk->chunk != NULL) rc = lk->chunk->parent->sub_handle->obj_ops.dirent_cmp( lk->chunk->parent->sub_handle, lk->name, lk->ck, rk->name, rk->ck); else rc = rk->chunk->parent->sub_handle->obj_ops.dirent_cmp( rk->chunk->parent->sub_handle, lk->name, lk->ck, rk->name, rk->ck) ); return rc; } void mdcache_avl_remove(mdcache_entry_t *parent, mdcache_dir_entry_t *dirent); void avl_dirent_set_deleted(mdcache_entry_t *entry, mdcache_dir_entry_t *v); void mdcache_avl_init(mdcache_entry_t *entry); int mdcache_avl_qp_insert(mdcache_entry_t *entry, mdcache_dir_entry_t **dirent); int mdcache_avl_insert_ck(mdcache_entry_t *entry, mdcache_dir_entry_t *v); #define MDCACHE_FLAG_NONE 0x0000 #define MDCACHE_FLAG_NEXT_ACTIVE 0x0001 #define MDCACHE_FLAG_ONLY_ACTIVE 0x0002 enum mdcache_avl_err { MDCACHE_AVL_NO_ERROR = 0, /*< Entry was found */ MDCACHE_AVL_NOT_FOUND, /*< Entry was not found */ MDCACHE_AVL_LAST, /*< Requested next, but was last */ MDCACHE_AVL_DELETED, /*< Entry was deleted */ }; enum mdcache_avl_err mdcache_avl_lookup_k(mdcache_entry_t *entry, uint64_t k, uint32_t flags, mdcache_dir_entry_t **dirent); bool mdcache_avl_lookup_ck(mdcache_entry_t *entry, uint64_t ck, mdcache_dir_entry_t **dirent); mdcache_dir_entry_t *mdcache_avl_qp_lookup_s(mdcache_entry_t *entry, const char *name, int maxj); void mdcache_avl_clean_trees(mdcache_entry_t *parent); void unchunk_dirent(mdcache_dir_entry_t *dirent); #endif /* MDCACHE_AVL_H */ /** @} */ nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_debug.h000066400000000000000000000035171324272410200246450ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright 2015-2017 Red Hat, Inc. and/or its affiliates. * Author: Daniel Gryniewicz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * @file mdcache_debug.h * @brief MDCache debug interface * * This is a debug interface for MDCACHE. It should *not* be used for any * production codepaths, but only for debugging and white-box-testing. */ #ifndef MDCACHE_DEBUG_H #define MDCACHE_DEBUG_H #include "mdcache_int.h" /** * @brief Get the sub-FSAL handle from an MDCACHE handle * * This allows access down the stack to the sub-FSAL's handle, given an MDCACHE * handle. It can be used to bypass MDCACHE for a debug operation. * * @note Keep a ref on the MDCACHE handle for the duration of the use of the * sub-FSAL's handle, or it might be freed from under you. * * @param[in] obj_hdl MDCACHE handle * @return sub-FSAL handle on success, NULL on failure */ struct fsal_obj_handle *mdcdb_get_sub_handle(struct fsal_obj_handle *obj_hdl) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); return entry->sub_handle; } #endif /* MDCACHE_DEBUG_H */ nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_export.c000066400000000000000000000560761324272410200251030ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright 2015-2017 Red Hat, Inc. and/or its affiliates. * Author: Daniel Gryniewicz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * */ /** * @addtogroup FSAL_MDCACHE * @{ */ /** * @file main.c * @brief FSAL export functions */ #include "config.h" #include "fsal.h" #include /* used for 'dirname' */ #include #include #include #include #include #include #include "gsh_list.h" #include "config_parsing.h" #include "fsal_convert.h" #include "FSAL/fsal_commonlib.h" #include "FSAL/fsal_config.h" #include "mdcache_lru.h" #include "mdcache_hash.h" #include "nfs_exports.h" #include "export_mgr.h" #include "gsh_config.h" /* * helpers to/from other NULL objects */ struct fsal_staticfsinfo_t *mdcache_staticinfo(struct fsal_module *hdl); /* * export object methods */ /** * @brief Return the name of the sub-FSAL * * For MDCACHE, we append "/MDC" onto the name. * * @param[in] exp_hdl Our export handle * @return Name of sub-FSAL */ static const char *mdcache_get_name(struct fsal_export *exp_hdl) { struct mdcache_fsal_export *exp = mdc_cur_export(); return exp->name; } /** * @brief Un-export an MDCACHE export * * Clean up all the cache entries on this export. * * @param[in] exp_hdl Export to unexport * @param[in] root_obj Root object for export */ static void mdcache_unexport(struct fsal_export *exp_hdl, struct fsal_obj_handle *root_obj) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; mdcache_entry_t *root_entry = container_of(root_obj, mdcache_entry_t, obj_handle); mdcache_entry_t *entry; struct entry_export_map *expmap; fsal_status_t status; bool rc; /* Indicate this export is going away so we don't create any new * export map entries. */ atomic_set_uint8_t_bits(&exp->flags, MDC_UNEXPORT); /* Next, clean up our cache entries on the export */ while (true) { PTHREAD_RWLOCK_rdlock(&exp->mdc_exp_lock); expmap = glist_first_entry(&exp->entry_list, struct entry_export_map, entry_per_export); if (unlikely(expmap == NULL)) { PTHREAD_RWLOCK_unlock(&exp->mdc_exp_lock); break; } entry = expmap->entry; /* Get a ref across cleanup */ status = mdcache_get(entry); PTHREAD_RWLOCK_unlock(&exp->mdc_exp_lock); if (FSAL_IS_ERROR(status)) { /* Entry was stale; skip it */ continue; } /* Must get attr_lock before mdc_exp_lock */ PTHREAD_RWLOCK_wrlock(&entry->attr_lock); PTHREAD_RWLOCK_wrlock(&exp->mdc_exp_lock); mdc_remove_export_map(expmap); expmap = glist_first_entry(&entry->export_list, struct entry_export_map, export_per_entry); if (expmap == NULL) { /* We must not hold entry->attr_lock across * try_cleanup_push (LRU lane lock order) */ PTHREAD_RWLOCK_unlock(&exp->mdc_exp_lock); PTHREAD_RWLOCK_unlock(&entry->attr_lock); /* There are no exports referencing this entry, attempt * to push it to cleanup queue. */ mdcache_lru_cleanup_try_push(entry); } else { /* Make sure first export pointer is still valid */ atomic_store_int32_t( &entry->first_export_id, (int32_t) expmap->exp->mfe_exp.export_id); PTHREAD_RWLOCK_unlock(&exp->mdc_exp_lock); PTHREAD_RWLOCK_unlock(&entry->attr_lock); } /* Release above ref */ mdcache_put(entry); }; /* Last unexport for the sub-FSAL */ subcall_raw(exp, sub_export->exp_ops.unexport(sub_export, root_entry->sub_handle) ); /* Unhash the root object */ rc = cih_remove_checked(root_entry); assert(!rc); } /** * @brief Release an MDCACHE export * * @param[in] exp_hdl Export to release */ static void mdcache_exp_release(struct fsal_export *exp_hdl) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; struct fsal_module *fsal_hdl; fsal_hdl = sub_export->fsal; LogInfo(COMPONENT_FSAL, "Releasing %s export %" PRIu16 " for %s", fsal_hdl->name, op_ctx->ctx_export->export_id, export_path(op_ctx->ctx_export)); /* Release the sub_export */ subcall_shutdown_raw(exp, sub_export->exp_ops.release(sub_export) ); fsal_put(fsal_hdl); LogFullDebug(COMPONENT_FSAL, "FSAL %s refcount %"PRIu32, fsal_hdl->name, atomic_fetch_int32_t(&fsal_hdl->refcount)); fsal_detach_export(exp_hdl->fsal, &exp_hdl->exports); free_export_ops(exp_hdl); gsh_free(exp->name); gsh_free(exp); /* elvis has left the building */ } /** * @brief Get FS information * * Pass through to underlying FSAL. * * Note dang: Should this gather info about MDCACHE? * * @param[in] exp_hdl Export to operate on * @param[in] obj_hdl Object to operate on * @param[out] infop Output information on success * @return FSAL status */ static fsal_status_t mdcache_get_dynamic_info(struct fsal_export *exp_hdl, struct fsal_obj_handle *obj_hdl, fsal_dynamicfsinfo_t *infop) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); fsal_status_t status; subcall_raw(exp, status = sub_export->exp_ops.get_fs_dynamic_info( sub_export, entry->sub_handle, infop) ); return status; } /** * @brief See if a feature is supported * * For the moment, MDCACHE supports no features, so just pass through to the * base FSAL. * * @param[in] exp_hdl Export to check * @param[in] option Option to check for support * @return true if supported, false otherwise */ static bool mdcache_fs_supports(struct fsal_export *exp_hdl, fsal_fsinfo_options_t option) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; bool result; subcall_raw(exp, result = sub_export->exp_ops.fs_supports(sub_export, option) ); return result; } /** * @brief Find the maximum supported file size * * MDCACHE only caches metadata, so it imposes no restrictions itself. * * @param[in] exp_hdl Export to query * @return Max size in bytes */ static uint64_t mdcache_fs_maxfilesize(struct fsal_export *exp_hdl) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; uint64_t result; subcall_raw(exp, result = sub_export->exp_ops.fs_maxfilesize(sub_export) ); return result; } /** * @brief Get the maximum supported read size * * MDCACHE only caches metadata, so it imposes no restrictions itself. * * @param[in] exp_hdl Export to query * @return Max size in bytes */ static uint32_t mdcache_fs_maxread(struct fsal_export *exp_hdl) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; uint32_t result; subcall_raw(exp, result = sub_export->exp_ops.fs_maxread(sub_export) ); return result; } /** * @brief Get the maximum supported write size * * MDCACHE only caches metadata, so it imposes no restrictions itself. * * @param[in] exp_hdl Export to query * @return Max size in bytes */ static uint32_t mdcache_fs_maxwrite(struct fsal_export *exp_hdl) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; uint32_t result; subcall_raw(exp, result = sub_export->exp_ops.fs_maxwrite(sub_export) ); return result; } /** * @brief Get the maximum supported link count * * MDCACHE only caches metadata, so it imposes no restrictions itself. * * @param[in] exp_hdl Export to query * @return Max number of links to a file */ static uint32_t mdcache_fs_maxlink(struct fsal_export *exp_hdl) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; uint32_t result; subcall_raw(exp, result = sub_export->exp_ops.fs_maxlink(sub_export) ); return result; } /** * @brief Get the maximum supported name length * * MDCACHE only caches metadata, so it imposes no restrictions itself. * * @param[in] exp_hdl Export to query * @return Max length of name in bytes */ static uint32_t mdcache_fs_maxnamelen(struct fsal_export *exp_hdl) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; uint32_t result; subcall_raw(exp, result = sub_export->exp_ops.fs_maxnamelen(sub_export) ); return result; } /** * @brief Get the maximum supported name length * * MDCACHE only caches metadata, so it imposes no restrictions itself. * * @param[in] exp_hdl Export to query * @return Max length of name in bytes */ static uint32_t mdcache_fs_maxpathlen(struct fsal_export *exp_hdl) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; uint32_t result; subcall_raw(exp, result = sub_export->exp_ops.fs_maxpathlen(sub_export) ); return result; } /** * @brief Get the FS lease time * * MDCACHE only caches metadata, so it imposes no restrictions itself. * * @param[in] exp_hdl Export to query * @return Lease time */ static struct timespec mdcache_fs_lease_time(struct fsal_export *exp_hdl) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; struct timespec result; subcall_raw(exp, result = sub_export->exp_ops.fs_lease_time(sub_export) ); return result; } /** * @brief Get the NFSv4 ACLSUPPORT attribute * * MDCACHE does not provide or restrict ACLs * * @param[in] exp_hdl Export to query * @return ACLSUPPORT */ static fsal_aclsupp_t mdcache_fs_acl_support(struct fsal_export *exp_hdl) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; fsal_aclsupp_t result; subcall_raw(exp, result = sub_export->exp_ops.fs_acl_support(sub_export) ); return result; } /** * @brief Get the list of supported attributes * * MDCACHE does not provide or restrict attributes * * @param[in] exp_hdl Export to query * @return Mask of supported attributes */ static attrmask_t mdcache_fs_supported_attrs(struct fsal_export *exp_hdl) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; attrmask_t result; subcall_raw(exp, result = sub_export->exp_ops.fs_supported_attrs(sub_export) ); return result; } /** * @brief Get the configured umask on the export * * MDCACHE only caches metadata, so it imposes no restrictions itself. * * @param[in] exp_hdl Export to query * @return umask value */ static uint32_t mdcache_fs_umask(struct fsal_export *exp_hdl) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; uint32_t result; subcall_raw(exp, result = sub_export->exp_ops.fs_umask(sub_export) ); return result; } /** * @brief Get the configured xattr access mask * * MDCACHE only caches metadata, so it imposes no restrictions itself. * * @param[in] exp_hdl Export to query * @return POSIX access bits for xattrs */ static uint32_t mdcache_fs_xattr_access_rights(struct fsal_export *exp_hdl) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; uint32_t result; subcall_raw(exp, result = sub_export->exp_ops.fs_xattr_access_rights(sub_export) ); return result; } /** * @brief Check quota on a file * * MDCACHE only caches metadata, so it imposes no restrictions itself. * * @param[in] exp_hdl Export to query * @param[in] filepath Path to file to query * @param[in] quota_type Type of quota (user or group) * @return FSAL status */ static fsal_status_t mdcache_check_quota(struct fsal_export *exp_hdl, const char *filepath, int quota_type) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; fsal_status_t status; subcall_raw(exp, status = sub_export->exp_ops.check_quota(sub_export, filepath, quota_type) ); return status; } /** * @brief Get quota information for a file * * MDCACHE only caches metadata, so it imposes no restrictions itself. * * @param[in] exp_hdl Export to query * @param[in] filepath Path to file to query * @param[in] quota_type Type of quota (user or group) * @param[in] quota_id Id for getting quota information * @param[out] pquota Resulting quota information * @return FSAL status */ static fsal_status_t mdcache_get_quota(struct fsal_export *exp_hdl, const char *filepath, int quota_type, int quota_id, fsal_quota_t *pquota) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; fsal_status_t status; subcall_raw(exp, status = sub_export->exp_ops.get_quota(sub_export, filepath, quota_type, quota_id, pquota)); return status; } /** * @brief Set a quota for a file * * MDCACHE only caches metadata, so it imposes no restrictions itself. * * @param[in] exp_hdl Export to query * @param[in] filepath Path to file to query * @param[in] quota_type Type of quota (user or group) * @param[in] quota_id Id for which quota is set * @param[in] pquota Quota information to set * @param[out] presquota Quota after set * @return FSAL status */ static fsal_status_t mdcache_set_quota(struct fsal_export *exp_hdl, const char *filepath, int quota_type, int quota_id, fsal_quota_t *pquota, fsal_quota_t *presquota) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; fsal_status_t status; subcall_raw(exp, status = sub_export->exp_ops.set_quota(sub_export, filepath, quota_type, quota_id, pquota, presquota) ); return status; } /** * @brief List pNFS devices * * MDCACHE only caches metadata, pass it through * * @param[in] exp_hdl Export to query * @param[in] type Layout type for query * @param[in] cb Callback for devices * @param[in] res Devicelist result * @return NFSv4 Status */ static nfsstat4 mdcache_getdevicelist(struct fsal_export *exp_hdl, layouttype4 type, void *opaque, bool (*cb)(void *opaque, const uint64_t id), struct fsal_getdevicelist_res *res) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; nfsstat4 status; subcall_raw(exp, status = sub_export->exp_ops.getdevicelist(sub_export, type, opaque, cb, res) ); return status; } /** * @brief List supported pNFS layout types * * MDCACHE only caches metadata, pass it through * * @param[in] exp_hdl Export to query * @param[out] count Number of types returned * @param[out] types Layout types supported */ static void mdcache_fs_layouttypes(struct fsal_export *exp_hdl, int32_t *count, const layouttype4 **types) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; subcall_raw(exp, sub_export->exp_ops.fs_layouttypes(sub_export, count, types) ); } /** * @brief Get pNFS layout block size * * MDCACHE only caches metadata, pass it through * * @param[in] exp_hdl Export to query * @return Number of bytes in block */ static uint32_t mdcache_fs_layout_blocksize(struct fsal_export *exp_hdl) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; uint32_t status; subcall_raw(exp, status = sub_export->exp_ops.fs_layout_blocksize(sub_export) ); return status; } /** * @brief Get pNFS maximum number of segments * * MDCACHE only caches metadata, pass it through * * @param[in] exp_hdl Export to query * @return Number of segments */ static uint32_t mdcache_fs_maximum_segments(struct fsal_export *exp_hdl) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; uint32_t status; subcall_raw(exp, status = sub_export->exp_ops.fs_maximum_segments(sub_export) ); return status; } /** * @brief Get size of pNFS loc_body * * MDCACHE only caches metadata, pass it through * * @param[in] exp_hdl Export to query * @return Size of loc_body */ static size_t mdcache_fs_loc_body_size(struct fsal_export *exp_hdl) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; size_t status; subcall_raw(exp, status = sub_export->exp_ops.fs_loc_body_size(sub_export) ); return status; } /** * @brief Get write verifier * * MDCACHE only caches metadata, pass it through * * @param[in] exp_hdl Export to query * @param[in,out] verf_desc Address and length of verifier */ static void mdcache_get_write_verifier(struct fsal_export *exp_hdl, struct gsh_buffdesc *verf_desc) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; subcall_raw(exp, sub_export->exp_ops.get_write_verifier(sub_export, verf_desc) ); } /** * @brief Decode the wire handle into something the FSAL can understand * * Wire formats are delegated to the underlying FSAL. * * @param[in] exp_hdl Export to operate on * @param[in] in_type Type of handle to extract * @param[in,out] fh_desc Source/dest for extracted digest * @param[in] flags Related flages (currently endian) * @return FSAL status */ static fsal_status_t mdcache_wire_to_host(struct fsal_export *exp_hdl, fsal_digesttype_t in_type, struct gsh_buffdesc *fh_desc, int flags) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; fsal_status_t status; subcall_raw(exp, status = sub_export->exp_ops.wire_to_host(sub_export, in_type, fh_desc, flags) ); return status; } /** * @brief Produce handle-key from host-handle * * delegated to the underlying FSAL. * * @param[in] exp_hdl Export to operate on * @param[in,out] fh_desc Source/dest for extracted digest * @return FSAL status */ static fsal_status_t mdcache_host_to_key(struct fsal_export *exp_hdl, struct gsh_buffdesc *fh_desc) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; fsal_status_t status; subcall_raw(exp, status = sub_export->exp_ops.host_to_key(sub_export, fh_desc) ); return status; } /** * @brief Allocate state_t structure * * @param[in] exp_hdl Export to operate on * @param[in] state_type Type of state to allocate * @param[in] related_state Related state if appropriate * @return New state structure */ static struct state_t *mdcache_alloc_state(struct fsal_export *exp_hdl, enum state_type state_type, struct state_t *related_state) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; struct state_t *state; subcall_raw(exp, state = sub_export->exp_ops.alloc_state(sub_export, state_type, related_state) ); /* Replace stored export with ours so stacking works */ state->state_exp = exp_hdl; return state; } /** * @brief Free state_t structure * * @param[in] exp_hdl Export state is associated with * @param[in] state State to free */ static void mdcache_free_state(struct fsal_export *exp_hdl, struct state_t *state) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; subcall_raw(exp, sub_export->exp_ops.free_state(sub_export, state) ); } /** * @brief Check to see if a user is superuser * * @param[in] exp_hdl Export state_t is associated with * @param[in] creds Credentials to check for superuser * * @returns NULL on failure otherwise a state structure. */ static bool mdcache_is_superuser(struct fsal_export *exp_hdl, const struct user_cred *creds) { struct mdcache_fsal_export *exp = mdc_export(exp_hdl); struct fsal_export *sub_export = exp->mfe_exp.sub_export; bool status; subcall_raw(exp, status = sub_export->exp_ops.is_superuser(sub_export, creds) ); return status; } /* mdcache_export_ops_init * overwrite vector entries with the methods that we support */ void mdcache_export_ops_init(struct export_ops *ops) { ops->get_name = mdcache_get_name; ops->unexport = mdcache_unexport; ops->release = mdcache_exp_release; ops->lookup_path = mdcache_lookup_path; /* lookup_junction unimplemented because deprecated */ ops->wire_to_host = mdcache_wire_to_host; ops->host_to_key = mdcache_host_to_key; ops->create_handle = mdcache_create_handle; ops->get_fs_dynamic_info = mdcache_get_dynamic_info; ops->fs_supports = mdcache_fs_supports; ops->fs_maxfilesize = mdcache_fs_maxfilesize; ops->fs_maxread = mdcache_fs_maxread; ops->fs_maxwrite = mdcache_fs_maxwrite; ops->fs_maxlink = mdcache_fs_maxlink; ops->fs_maxnamelen = mdcache_fs_maxnamelen; ops->fs_maxpathlen = mdcache_fs_maxpathlen; ops->fs_lease_time = mdcache_fs_lease_time; ops->fs_acl_support = mdcache_fs_acl_support; ops->fs_supported_attrs = mdcache_fs_supported_attrs; ops->fs_umask = mdcache_fs_umask; ops->fs_xattr_access_rights = mdcache_fs_xattr_access_rights; ops->check_quota = mdcache_check_quota; ops->get_quota = mdcache_get_quota; ops->set_quota = mdcache_set_quota; ops->getdevicelist = mdcache_getdevicelist; ops->fs_layouttypes = mdcache_fs_layouttypes; ops->fs_layout_blocksize = mdcache_fs_layout_blocksize; ops->fs_maximum_segments = mdcache_fs_maximum_segments; ops->fs_loc_body_size = mdcache_fs_loc_body_size; ops->get_write_verifier = mdcache_get_write_verifier; ops->alloc_state = mdcache_alloc_state; ops->free_state = mdcache_free_state; ops->is_superuser = mdcache_is_superuser; } #if 0 struct mdcache_fsal_args { struct subfsal_args subfsal; }; static struct config_item sub_fsal_params[] = { CONF_ITEM_STR("name", 1, 10, NULL, subfsal_args, name), CONFIG_EOL }; static struct config_item export_params[] = { CONF_ITEM_NOOP("name"), CONF_RELAX_BLOCK("FSAL", sub_fsal_params, noop_conf_init, subfsal_commit, mdcache_fsal_args, subfsal), CONFIG_EOL }; static struct config_block export_param = { .dbus_interface_name = "org.ganesha.nfsd.config.fsal.mdcache-export%d", .blk_desc.name = "FSAL", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = noop_conf_init, .blk_desc.u.blk.params = export_params, .blk_desc.u.blk.commit = noop_conf_commit }; #endif /** @} */ nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_ext.h000066400000000000000000000112051324272410200243500ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright 2015-2017 Red Hat, Inc. and/or its affiliates. * Author: Daniel Gryniewicz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * @file mdcache_ext.h * @brief MDCache external interface * * Stuff that can be accessed outside MDCACHE. Things in here are generally * hacks that should be removed. */ #ifndef MDCACHE_EXT_H #define MDCACHE_EXT_H /** * @defgroup config_mdcache Structure and defaults for MDCACHE * * @{ */ /** * @brief Structure to hold MDCACHE paramaters */ struct mdcache_parameter { /** Partitions in the Cache_Inode tree. Defaults to 7, * settable with NParts. */ uint32_t nparts; /** Per-partition hash table size. Defaults to 32633, * settable with Cache_Size. */ uint32_t cache_size; /** Use getattr for directory invalidation. Defaults to false. Settable with Use_Getattr_Directory_Invalidation. */ bool getattr_dir_invalidation; struct { /** Max size of per-directory cache of removed entries */ uint32_t avl_max_deleted; /** Max size of per-directory dirent cache */ uint32_t avl_max; /** Size of per-directory dirent cache chunks, 0 means * directory chunking is not enabled. */ uint32_t avl_chunk; /** Size of a dirent chunk at which point the chunk should * be split. Pre-computed for simplicity. */ uint32_t avl_chunk_split; /** Detached dirent multiplier (of avl_chunk) */ uint32_t avl_detached_mult; /** Computed max detached dirents */ uint32_t avl_detached_max; } dir; /** High water mark for cache entries. Defaults to 100000, settable by Entries_HWMark. */ uint32_t entries_hwmark; /** High water mark for chunks. Defaults to 100000, settable by Chunks_HWMark. */ uint32_t chunks_hwmark; /** Base interval in seconds between runs of the LRU cleaner thread. Defaults to 60, settable with LRU_Run_Interval. */ uint32_t lru_run_interval; /** Whether to cache open files. Defaults to true, settable with Cache_FDs. */ bool use_fd_cache; /** The percentage of the system-imposed maximum of file descriptors at which Ganesha will deny requests. Defaults to 99, settable with FD_Limit_Percent. */ uint32_t fd_limit_percent; /** The percentage of the system-imposed maximum of file descriptors above which Ganesha will make greater efforts at reaping. Defaults to 90, settable with FD_HWMark_Percent. */ uint32_t fd_hwmark_percent; /** The percentage of the system-imposed maximum of file descriptors below which Ganesha will not reap file descriptonot reap file descriptors. Defaults to 50, settable with FD_LWMark_Percent. */ uint32_t fd_lwmark_percent; /** Roughly, the amount of work to do on each pass through the thread under normal conditions. (Ideally, a multiple of the number of lanes.) Defaults to 1000, settable with Reaper_Work. */ uint32_t reaper_work; /** The amount of work for the reaper thread to do per-lane under normal conditions. Settable with Repaper_Work_Per_Thread */ uint32_t reaper_work_per_lane; /** The largest window (as a percentage of the system-imposed limit on FDs) of work that we will do in extremis. Defaults to 40, settable with Biggest_Window */ uint32_t biggest_window; /** Percentage of progress toward the high water mark required in in a pass through the thread when in extremis. Defaults to 5, settable with Required_Progress. */ uint32_t required_progress; /** Number of failures to approach the high watermark before we disable caching, when in extremis. Defaults to 8, settable with Futility_Count */ uint32_t futility_count; /** Behavior for when readdir fails for some reason: true will ask the client to retry later, false will give the client a partial reply based on what we have. Defaults to false, settable with Retry_Readdir */ bool retry_readdir; }; extern struct mdcache_parameter mdcache_param; /** @} */ #endif /* MDCACHE_EXT_H */ nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_file.c000066400000000000000000000522611324272410200244710ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright 2015-2017 Red Hat, Inc. and/or its affiliates. * Author: Daniel Gryniewicz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* file.c * File I/O methods for NULL module */ #include "config.h" #include #include "fsal.h" #include "FSAL/access_check.h" #include "fsal_convert.h" #include #include #include "FSAL/fsal_commonlib.h" #include "mdcache_int.h" #include "mdcache_lru.h" #include "mdcache.h" /** * * @brief Set a timestamp to the current time * * @param[out] time Pointer to time to be set * * @return true on success, false on failure * */ bool mdc_set_time_current(struct timespec *time) { struct timeval t; if (time == NULL) return false; if (gettimeofday(&t, NULL) != 0) return false; time->tv_sec = t.tv_sec; time->tv_nsec = 1000 * t.tv_usec; return true; } /** * @brief Seek to data or hole * * Delegate to sub-FSAL * * @param[in] obj_hdl File to seek in * @param[in,out] info Information about the data * * @return FSAL status. */ fsal_status_t mdcache_seek(struct fsal_obj_handle *obj_hdl, struct io_info *info) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); fsal_status_t status; subcall( status = entry->sub_handle->obj_ops.seek(entry->sub_handle, info) ); return status; } /** * @brief IO Advise * * Delegate to sub-FSAL * * @param[in] obj_hdl File to be written * @param[in,out] info Information about the data * * @return FSAL status. */ fsal_status_t mdcache_io_advise(struct fsal_obj_handle *obj_hdl, struct io_hints *hints) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); fsal_status_t status; subcall( status = entry->sub_handle->obj_ops.io_advise( entry->sub_handle, hints) ); return status; } /** * @brief Close a file * * @param[in] obj_hdl File to close * @return FSAL status */ fsal_status_t mdcache_close(struct fsal_obj_handle *obj_hdl) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); fsal_status_t status; /* XXX dang caching FDs? How does it interact with multi-FD */ subcall( status = entry->sub_handle->obj_ops.close(entry->sub_handle) ); return status; } static fsal_status_t mdc_open2_by_name(mdcache_entry_t *mdc_parent, struct state_t *state, fsal_openflags_t openflags, enum fsal_create_mode createmode, const char *name, struct attrlist *attrib_set, struct attrlist *attrs_out, fsal_verifier_t verifier, mdcache_entry_t **new_entry, bool *caller_perm_check) { fsal_status_t status; bool uncached = createmode >= FSAL_GUARDED; mdcache_entry_t *entry; struct fsal_obj_handle *sub_handle; *new_entry = NULL; if (!name) return fsalstat(ERR_FSAL_INVAL, 0); status = mdc_lookup(mdc_parent, name, uncached, &entry, NULL); if (FSAL_IS_ERROR(status)) { /* Does not exist, or other error, return to open2 to * proceed if not found, otherwise to return the error. */ LogFullDebug(COMPONENT_CACHE_INODE, "Lookup failed"); return status; } /* Found to exist */ if (createmode == FSAL_GUARDED) { mdcache_put(entry); return fsalstat(ERR_FSAL_EXIST, 0); } else if (createmode == FSAL_EXCLUSIVE) { /* Exclusive create with entry found, check verifier */ if (!mdcache_check_verifier(&entry->obj_handle, verifier)) { /* Verifier check failed. */ LogFullDebug(COMPONENT_CACHE_INODE, "Verifier check failed."); mdcache_put(entry); return fsalstat(ERR_FSAL_EXIST, 0); } /* Verifier matches, go ahead and open the file. */ } /* else UNGUARDED, go ahead and open the file. */ subcall( status = entry->sub_handle->obj_ops.open2( entry->sub_handle, state, openflags, createmode, NULL, attrib_set, verifier, &sub_handle, attrs_out, caller_perm_check) ); if (FSAL_IS_ERROR(status)) { /* Open failed. */ LogFullDebug(COMPONENT_CACHE_INODE, "Open failed %s", msg_fsal_err(status.major)); mdcache_put(entry); return status; } LogFullDebug(COMPONENT_CACHE_INODE, "Opened entry %p, sub_handle %p", entry, entry->sub_handle); if (openflags & FSAL_O_TRUNC) { /* Invalidate the attributes since we just truncated. */ atomic_clear_uint32_t_bits(&entry->mde_flags, MDCACHE_TRUST_ATTRS); } if (attrs_out) { /* Handle attribute request */ if (!(attrs_out->valid_mask & ATTR_RDATTR_ERR)) { struct attrlist attrs; /* open2() gave us attributes. Update the cache */ fsal_prepare_attrs( &attrs, op_ctx->fsal_export->exp_ops.fs_supported_attrs( op_ctx->fsal_export) | ATTR_RDATTR_ERR); fsal_copy_attrs(&attrs, attrs_out, false); PTHREAD_RWLOCK_wrlock(&entry->attr_lock); mdc_update_attr_cache(entry, &attrs); PTHREAD_RWLOCK_unlock(&entry->attr_lock); /* mdc_update_attr_cache() consumes attrs; the release * is here only for code inspection. */ fsal_release_attrs(&attrs); } else if (attrs_out->request_mask & ATTR_RDATTR_ERR) { /* We didn't get attributes from open2, but the caller * wants them. Try a full getattrs() */ status = entry->obj_handle.obj_ops.getattrs( &entry->obj_handle, attrs_out); if (FSAL_IS_ERROR(status)) { LogFullDebug(COMPONENT_CACHE_INODE, "getattrs failed status=%s", fsal_err_txt(status)); } } } *new_entry = entry; return status; } /** * @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 FSAL_UNCHECKED, * FSAL_GUARDED, FSAL_EXCLUSIVE_41, or FSAL_EXCLUSIVE_9P. * * 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. * * The caller is expected to invoke fsal_release_attrs to release any * resources held by the set attributes. The FSAL layer MAY have added an * inherited ACL. * * The caller will set the request_mask in attrs_out to indicate the attributes * of interest. ATTR_ACL SHOULD NOT be requested and need not be provided. If * not all the requested attributes can be provided, this method MUST return * an error unless the ATTR_RDATTR_ERR bit was set in the request_mask. * * Since this method may instantiate a new fsal_obj_handle, it will be forced * to fetch at least some attributes in order to even know what the object * type is (as well as it's fileid and fsid). For this reason, the operation * as a whole can be expected to fail if the attributes were not able to be * fetched. * * The attributes will not be returned if this is an open by object as * opposed to an open by name. * * @note If the file was created, @a new_obj has been ref'd * * @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] attrs_in 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 Optional attributes for newly created object * @param[in,out] caller_perm_check The caller must do a permission check * * @return FSAL status. */ fsal_status_t mdcache_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 *attrs_in, fsal_verifier_t verifier, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out, bool *caller_perm_check) { mdcache_entry_t *mdc_parent = container_of(obj_hdl, mdcache_entry_t, obj_handle); mdcache_entry_t *new_entry = NULL; struct fsal_obj_handle *sub_handle = NULL; fsal_status_t status; struct attrlist attrs; const char *dispname = name != NULL ? name : ""; struct mdcache_fsal_export *export = mdc_cur_export(); bool invalidate; LogAttrlist(COMPONENT_CACHE_INODE, NIV_FULL_DEBUG, "attrs_in ", attrs_in, false); if (name) { if (!state && !mdcache_lru_fds_available()) { /* This seems the best idea, let the * client try again later after the reap. */ return fsalstat(ERR_FSAL_DELAY, 0); } /* Check if we have the file already cached, in which case * we can open by object instead of by name. */ status = mdc_open2_by_name(mdc_parent, state, openflags, createmode, name, attrs_in, attrs_out, verifier, &new_entry, caller_perm_check); if (status.major == ERR_FSAL_NO_ERROR) { /* Return the newly opened file. */ *new_obj = &new_entry->obj_handle; return status; } if (status.major != ERR_FSAL_NOENT) { /* Return the error */ *new_obj = NULL; return status; } } /* Ask for all supported attributes except ACL (we defer fetching ACL * until asked for it (including a permission check). * * We can survive if we don't actually succeed in fetching the * attributes. */ fsal_prepare_attrs(&attrs, (op_ctx->fsal_export->exp_ops.fs_supported_attrs( op_ctx->fsal_export) & ~ATTR_ACL) | ATTR_RDATTR_ERR); subcall( status = mdc_parent->sub_handle->obj_ops.open2( mdc_parent->sub_handle, state, openflags, createmode, name, attrs_in, verifier, &sub_handle, &attrs, caller_perm_check) ); if (unlikely(FSAL_IS_ERROR(status))) { LogDebug(COMPONENT_CACHE_INODE, "open2 %s failed with %s", dispname, fsal_err_txt(status)); if (status.major == ERR_FSAL_STALE) { /* If we got ERR_FSAL_STALE, the previous FSAL call * must have failed with a bad parent. */ mdcache_kill_entry(mdc_parent); } fsal_release_attrs(&attrs); *new_obj = NULL; return status; } if (!name) { /* Wasn't a create and/or entry already found */ if (openflags & FSAL_O_TRUNC) { /* Mark the attributes as not-trusted, so we will * refresh the attributes. */ atomic_clear_uint32_t_bits(&mdc_parent->mde_flags, MDCACHE_TRUST_ATTRS); } LogFullDebug(COMPONENT_CACHE_INODE, "Open2 of object succeeded."); *new_obj = obj_hdl; /* We didn't actually get any attributes, but release anyway * for code consistency. */ fsal_release_attrs(&attrs); return status; } invalidate = createmode != FSAL_NO_CREATE; PTHREAD_RWLOCK_wrlock(&mdc_parent->content_lock); /* We will invalidate parent attrs if we did any form of create. */ status = mdcache_alloc_and_check_handle(export, sub_handle, new_obj, false, &attrs, attrs_out, "open2 ", mdc_parent, name, &invalidate, state); PTHREAD_RWLOCK_unlock(&mdc_parent->content_lock); fsal_release_attrs(&attrs); if (createmode != FSAL_NO_CREATE && !invalidate) { /* Refresh destination directory attributes without * invalidating dirents. */ mdcache_refresh_attrs_no_invalidate(mdc_parent); } return status; } /** * @brief Check the verifier * * @param[in] obj_hdl File to check * @param[in] verifier Verifier to check * @return FSAL status */ bool mdcache_check_verifier(struct fsal_obj_handle *obj_hdl, fsal_verifier_t verifier) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); bool result; /* XXX dang caching FDs? How does it interact with multi-FD */ subcall( result = entry->sub_handle->obj_ops.check_verifier( entry->sub_handle, verifier) ); return result; } /** * @brief Get the open status of a file (new style) * * Delegate to sub-FSAL, since this isn't cached metadata currently * * @param[in] obj_hdl Object owning state * @param[in] state Open file state to check * @return Open flags indicating state */ fsal_openflags_t mdcache_status2(struct fsal_obj_handle *obj_hdl, struct state_t *state) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); fsal_openflags_t status; subcall( status = entry->sub_handle->obj_ops.status2( entry->sub_handle, state) ); return status; } /** * @brief Re-open a file with different flags (new style) * * Delegate to sub-FSAL. This should not be called unless the sub-FSAL supports * reopen2. * * @param[in] obj_hdl Object owning state * @param[in] state Open file state to re-open * @param[in] openflags New open flags * @return FSAL status */ fsal_status_t mdcache_reopen2(struct fsal_obj_handle *obj_hdl, struct state_t *state, fsal_openflags_t openflags) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); fsal_status_t status; bool truncated = openflags & FSAL_O_TRUNC; subcall( status = entry->sub_handle->obj_ops.reopen2( entry->sub_handle, state, openflags) ); if (FSAL_IS_ERROR(status) && (status.major == ERR_FSAL_STALE)) mdcache_kill_entry(entry); if (truncated && !FSAL_IS_ERROR(status)) { atomic_clear_uint32_t_bits(&entry->mde_flags, MDCACHE_TRUST_ATTRS); } return status; } /** * @brief Read from a file (new style) * * Delegate to sub-FSAL * * @param[in] obj_hdl Object owning state * @param[in] bypass Bypass deny read * @param[in] state Open file state to read * @param[in] offset Offset into file * @param[in] buf_size Size of read buffer * @param[in,out] buffer Buffer to read into * @param[out] read_amount Amount read in bytes * @param[out] eof true if End of File was hit * @param[in] info io_info for READ_PLUS * @return FSAL status */ fsal_status_t mdcache_read2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buf_size, void *buffer, size_t *read_amount, bool *eof, struct io_info *info) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); fsal_status_t status; subcall( status = entry->sub_handle->obj_ops.read2( entry->sub_handle, bypass, state, offset, buf_size, buffer, read_amount, eof, info) ); if (!FSAL_IS_ERROR(status)) mdc_set_time_current(&entry->attrs.atime); else if (status.major == ERR_FSAL_DELAY) mdcache_kill_entry(entry); return status; } /** * @brief Write to a file (new style) * * Delegate to sub-FSAL * * @param[in] obj_hdl Object owning state * @param[in] bypass Bypass any non-mandatory deny write * @param[in] state Open file state to write * @param[in] offset Offset into file * @param[in] buf_size Size of write buffer * @param[in] buffer Buffer to write from * @param[out] write_amount Amount written in bytes * @param[out] fsal_stable true if write was to stable storage * @param[in] info io_info for WRITE_PLUS * @return FSAL status */ fsal_status_t mdcache_write2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buf_size, void *buffer, size_t *write_amount, bool *fsal_stable, struct io_info *info) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); fsal_status_t status; subcall( status = entry->sub_handle->obj_ops.write2( entry->sub_handle, bypass, state, offset, buf_size, buffer, write_amount, fsal_stable, info) ); if (status.major == ERR_FSAL_STALE) mdcache_kill_entry(entry); else atomic_clear_uint32_t_bits(&entry->mde_flags, MDCACHE_TRUST_ATTRS); return status; } /** * @brief Seek within a file (new style) * * Delegate to sub-FSAL * * @param[in] obj_hdl Object owning state * @param[in] state Open file state to seek within * @param[in] info io_info for seek * @return FSAL status */ fsal_status_t mdcache_seek2(struct fsal_obj_handle *obj_hdl, struct state_t *state, struct io_info *info) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); fsal_status_t status; subcall( status = entry->sub_handle->obj_ops.seek2( entry->sub_handle, state, info) ); if (status.major == ERR_FSAL_DELAY) mdcache_kill_entry(entry); return status; } /** * @brief Advise access pattern for a file (new style) * * Delegate to sub-FSAL * * @param[in] obj_hdl Object owning state * @param[in] state Open file state to advise on * @param[in] hints io_hints containing advice * @return FSAL status */ fsal_status_t mdcache_io_advise2(struct fsal_obj_handle *obj_hdl, struct state_t *state, struct io_hints *hints) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); fsal_status_t status; subcall( status = entry->sub_handle->obj_ops.io_advise2( entry->sub_handle, state, hints) ); if (status.major == ERR_FSAL_DELAY) mdcache_kill_entry(entry); return status; } /** * @brief Commit to a file (new style) * * Delegate to sub-FSAL * * @param[in] obj_hdl Object to commit * @param[in] offset Offset into file * @param[in] len Length of commit * @return FSAL status */ fsal_status_t mdcache_commit2(struct fsal_obj_handle *obj_hdl, off_t offset, size_t len) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); fsal_status_t status; subcall( status = entry->sub_handle->obj_ops.commit2( entry->sub_handle, offset, len) ); if (status.major == ERR_FSAL_STALE) mdcache_kill_entry(entry); else atomic_clear_uint32_t_bits(&entry->mde_flags, MDCACHE_TRUST_ATTRS); return status; } /** * @brief Lock/unlock a range in a file (new style) * * Delegate to sub-FSAL * * @param[in] obj_hdl Object owning state * @param[in] state Open file state to lock/unlock * @param[in] p_owner Private data for lock * @param[in] lock_op Lock operation * @param[in] req_lock Parameters for requested lock * @param[in] conflicting_lock Description of existing conflicting lock * @return FSAL status */ fsal_status_t mdcache_lock_op2(struct fsal_obj_handle *obj_hdl, struct state_t *state, void *p_owner, fsal_lock_op_t lock_op, fsal_lock_param_t *req_lock, fsal_lock_param_t *conflicting_lock) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); fsal_status_t status; subcall( status = entry->sub_handle->obj_ops.lock_op2( entry->sub_handle, state, p_owner, lock_op, req_lock, conflicting_lock) ); return status; } /** * @brief Get/Release delegation for a file (new style) * * Delegate to sub-FSAL * * @param[in] obj_hdl Object owning state * @param[in] state Open file state to get/release * @param[in] p_owner Private owner * @param[in] deleg Delegation operation * @return FSAL status */ fsal_status_t mdcache_lease_op2(struct fsal_obj_handle *obj_hdl, struct state_t *state, void *p_owner, fsal_deleg_t deleg) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); fsal_status_t status; subcall( status = entry->sub_handle->obj_ops.lease_op2( entry->sub_handle, state, p_owner, deleg); ); return status; } /** * @brief Close a file (new style) * * @param[in] obj_hdl Object owning state * @param[in] state Open file state to close * @return FSAL status */ fsal_status_t mdcache_close2(struct fsal_obj_handle *obj_hdl, struct state_t *state) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); fsal_status_t status; subcall( status = entry->sub_handle->obj_ops.close2( entry->sub_handle, state) ); if (test_mde_flags(entry, MDCACHE_UNREACHABLE) && !mdc_has_state(entry)) { /* Entry was marked unreachable, and last state is gone */ (void)mdcache_kill_entry(entry); } return status; } nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_handle.c000066400000000000000000001443131324272410200250050ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright 2015-2017 Red Hat, Inc. and/or its affiliates. * Author: Daniel Gryniewicz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * */ /* handle.c */ #include "config.h" #include "fsal.h" #include /* used for 'dirname' */ #include #include #include #include "gsh_list.h" #include "fsal_convert.h" #include "FSAL/fsal_commonlib.h" #include "nfs4_acls.h" #include "nfs_exports.h" #include "sal_functions.h" #include #include "mdcache_lru.h" #include "mdcache_hash.h" #include "mdcache_avl.h" /* * handle methods */ /** * Attempts to create a new mdcache handle, or cleanup memory if it fails. * * This function is a wrapper of mdcache_alloc_handle. It adds error checking * and logging. It also cleans objects allocated in the subfsal if it fails. * * @note the caller must hold the content lock on the parent. * * This does not cause an ABBA lock conflict with the potential getattrs * if we lose a race to create the cache entry since our caller CAN NOT hold * any locks on the cache entry created. * * Invalidate can be changed from true to false if mdcache is able to add * the new dirent to a chunk. In this case, the caller MUST refresh the * parent's attributes (we can't do it here due to lock ordering) in a way that * does not invalidate the dirent cache. * * @param[in] export The mdcache export used by the handle. * @param[in,out] sub_handle The handle used by the subfsal. * @param[in] fs The filesystem of the new handle. * @param[in] new_handle Address where the new allocated pointer should * be written. * @param[in] new_directory Indicates a new directory has been created. * @param[in,out] attrs_out Optional attributes for newly created object. * @param[in] parent Parent directory to add dirent to. * @param[in] name Name of the dirent to add. * @param[in,out] invalidate Invalidate parent attr (and chunk cache) * @param[in] state Optional state_t representing open file. * * @note This returns an INITIAL ref'd entry on success * * @return An error code for the function. */ fsal_status_t mdcache_alloc_and_check_handle( struct mdcache_fsal_export *export, struct fsal_obj_handle *sub_handle, struct fsal_obj_handle **new_obj, bool new_directory, struct attrlist *attrs_in, struct attrlist *attrs_out, const char *tag, mdcache_entry_t *parent, const char *name, bool *invalidate, struct state_t *state) { fsal_status_t status; mdcache_entry_t *new_entry; status = mdcache_new_entry(export, sub_handle, attrs_in, attrs_out, new_directory, &new_entry, state); if (FSAL_IS_ERROR(status)) { *new_obj = NULL; return status; } LogFullDebug(COMPONENT_CACHE_INODE, "%sCreated entry %p FSAL %s for %s", tag, new_entry, new_entry->sub_handle->fsal->name, name); if (*invalidate) { /* This function is called after a create, so go ahead * and invalidate the parent directory attributes. */ atomic_clear_uint32_t_bits(&parent->mde_flags, MDCACHE_TRUST_ATTRS); } /* Add this entry to the directory (also takes an internal ref) */ status = mdcache_dirent_add(parent, name, new_entry, invalidate); if (FSAL_IS_ERROR(status)) { LogDebug(COMPONENT_CACHE_INODE, "%s%s failed because add dirent failed", tag, name); mdcache_put(new_entry); *new_obj = NULL; return status; } if (new_entry->obj_handle.type == DIRECTORY) { /* Insert Parent's key */ PTHREAD_RWLOCK_wrlock(&new_entry->content_lock); mdc_dir_add_parent(new_entry, parent); PTHREAD_RWLOCK_unlock(&new_entry->content_lock); } *new_obj = &new_entry->obj_handle; if (attrs_out != NULL) { LogAttrlist(COMPONENT_CACHE_INODE, NIV_FULL_DEBUG, tag, attrs_out, true); } return status; } /** * @brief Lookup a name * * Lookup a name relative to another object * * @param[in] parent Handle of parent * @param[in] name Name to look up * @param[out] handle Handle of found object, on success * @param[in,out] attrs_out Optional attributes for newly created object * * @note This returns an INITIAL ref'd entry on success * @return FSAL status */ static fsal_status_t mdcache_lookup(struct fsal_obj_handle *parent, const char *name, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { mdcache_entry_t *mdc_parent = container_of(parent, mdcache_entry_t, obj_handle); mdcache_entry_t *entry = NULL; fsal_status_t status; *handle = NULL; status = mdc_lookup(mdc_parent, name, true, &entry, attrs_out); if (entry) *handle = &entry->obj_handle; return status; } /** * @brief Make a directory * * @param[in] dir_hdl Parent directory handle * @param[in] name Name for new directory * @param[in] attrib Attributes for new directory * @param[out] handle Resulting handle on success * * @note This returns an INITIAL ref'd entry on success * @return FSAL status */ static fsal_status_t mdcache_mkdir(struct fsal_obj_handle *dir_hdl, const char *name, struct attrlist *attrib, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { mdcache_entry_t *parent = container_of(dir_hdl, mdcache_entry_t, obj_handle); struct mdcache_fsal_export *export = mdc_cur_export(); struct fsal_obj_handle *sub_handle; fsal_status_t status; struct attrlist attrs; bool invalidate = true; *handle = NULL; /* Ask for all supported attributes except ACL (we defer fetching ACL * until asked for it (including a permission check). */ fsal_prepare_attrs(&attrs, op_ctx->fsal_export->exp_ops.fs_supported_attrs( op_ctx->fsal_export) & ~ATTR_ACL); subcall_raw(export, status = parent->sub_handle->obj_ops.mkdir( parent->sub_handle, name, attrib, &sub_handle, &attrs) ); if (unlikely(FSAL_IS_ERROR(status))) { LogDebug(COMPONENT_CACHE_INODE, "mkdir %s failed with %s", name, fsal_err_txt(status)); if (status.major == ERR_FSAL_STALE) { /* If we got ERR_FSAL_STALE, the previous FSAL call * must have failed with a bad parent. */ LogEvent(COMPONENT_CACHE_INODE, "FSAL returned STALE on mkdir"); mdcache_kill_entry(parent); } *handle = NULL; fsal_release_attrs(&attrs); return status; } PTHREAD_RWLOCK_wrlock(&parent->content_lock); status = mdcache_alloc_and_check_handle(export, sub_handle, handle, true, &attrs, attrs_out, "mkdir ", parent, name, &invalidate, NULL); PTHREAD_RWLOCK_unlock(&parent->content_lock); fsal_release_attrs(&attrs); if (!invalidate) { /* Refresh destination directory attributes without * invalidating dirents. */ mdcache_refresh_attrs_no_invalidate(parent); } return status; } /** * @brief Make a device node * * @param[in] dir_hdl Parent directory handle * @param[in] name Name of new node * @param[in] nodetype Type of new node * @param[in] attrib Attributes for new node * @param[out] handle New object handle on success * * @note This returns an INITIAL ref'd entry on success * @return FSAL status */ static fsal_status_t mdcache_mknode(struct fsal_obj_handle *dir_hdl, const char *name, object_file_type_t nodetype, struct attrlist *attrib, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { mdcache_entry_t *parent = container_of(dir_hdl, mdcache_entry_t, obj_handle); struct mdcache_fsal_export *export = mdc_cur_export(); struct fsal_obj_handle *sub_handle; fsal_status_t status; struct attrlist attrs; bool invalidate = true; *handle = NULL; /* Ask for all supported attributes except ACL (we defer fetching ACL * until asked for it (including a permission check). */ fsal_prepare_attrs(&attrs, op_ctx->fsal_export->exp_ops.fs_supported_attrs( op_ctx->fsal_export) & ~ATTR_ACL); subcall_raw(export, status = parent->sub_handle->obj_ops.mknode( parent->sub_handle, name, nodetype, attrib, &sub_handle, &attrs) ); if (unlikely(FSAL_IS_ERROR(status))) { LogDebug(COMPONENT_CACHE_INODE, "mknod %s failed with %s", name, fsal_err_txt(status)); if (status.major == ERR_FSAL_STALE) { /* If we got ERR_FSAL_STALE, the previous FSAL call * must have failed with a bad parent. */ LogEvent(COMPONENT_CACHE_INODE, "FSAL returned STALE on mknod"); mdcache_kill_entry(parent); } *handle = NULL; fsal_release_attrs(&attrs); return status; } PTHREAD_RWLOCK_wrlock(&parent->content_lock); status = mdcache_alloc_and_check_handle(export, sub_handle, handle, false, &attrs, attrs_out, "mknode ", parent, name, &invalidate, NULL); PTHREAD_RWLOCK_unlock(&parent->content_lock); fsal_release_attrs(&attrs); if (!invalidate) { /* Refresh destination directory attributes without * invalidating dirents. */ mdcache_refresh_attrs_no_invalidate(parent); } return status; } /** * @brief Make a symlink * * @param[in] dir_hdl Parent directory handle * @param[in] name Name of new node * @param[in] link_path Contents of symlink * @param[in] attrib Attributes for new simlink * @param[out] handle New object handle on success * * @note This returns an INITIAL ref'd entry on success * @return FSAL status */ static fsal_status_t mdcache_symlink(struct fsal_obj_handle *dir_hdl, const char *name, const char *link_path, struct attrlist *attrib, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { mdcache_entry_t *parent = container_of(dir_hdl, mdcache_entry_t, obj_handle); struct mdcache_fsal_export *export = mdc_cur_export(); struct fsal_obj_handle *sub_handle; fsal_status_t status; struct attrlist attrs; bool invalidate = true; *handle = NULL; /* Ask for all supported attributes except ACL (we defer fetching ACL * until asked for it (including a permission check). */ fsal_prepare_attrs(&attrs, op_ctx->fsal_export->exp_ops.fs_supported_attrs( op_ctx->fsal_export) & ~ATTR_ACL); subcall_raw(export, status = parent->sub_handle->obj_ops.symlink( parent->sub_handle, name, link_path, attrib, &sub_handle, &attrs) ); if (unlikely(FSAL_IS_ERROR(status))) { LogDebug(COMPONENT_CACHE_INODE, "symlink %s failed with %s", name, fsal_err_txt(status)); if (status.major == ERR_FSAL_STALE) { /* If we got ERR_FSAL_STALE, the previous FSAL call * must have failed with a bad parent. */ LogEvent(COMPONENT_CACHE_INODE, "FSAL returned STALE on symlink"); mdcache_kill_entry(parent); } *handle = NULL; fsal_release_attrs(&attrs); return status; } PTHREAD_RWLOCK_wrlock(&parent->content_lock); status = mdcache_alloc_and_check_handle(export, sub_handle, handle, false, &attrs, attrs_out, "symlink ", parent, name, &invalidate, NULL); PTHREAD_RWLOCK_unlock(&parent->content_lock); fsal_release_attrs(&attrs); if (!invalidate) { /* Refresh destination directory attributes without * invalidating dirents. */ mdcache_refresh_attrs_no_invalidate(parent); } return status; } /** * @brief Read a symlink * * @param[in] obj_hdl Handle for symlink * @param[out] link_content Buffer to fill with link contents * @param[in] refresh If true, refresh attributes on symlink * @return FSAL status */ static fsal_status_t mdcache_readlink(struct fsal_obj_handle *obj_hdl, struct gsh_buffdesc *link_content, bool refresh) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); fsal_status_t status; PTHREAD_RWLOCK_rdlock(&entry->content_lock); if (!refresh && !test_mde_flags(entry, MDCACHE_TRUST_CONTENT)) { /* Our data are stale. Drop the lock, get a write-lock, load in new data, and copy it out to the caller. */ PTHREAD_RWLOCK_unlock(&entry->content_lock); PTHREAD_RWLOCK_wrlock(&entry->content_lock); /* Make sure nobody updated the content while we were waiting. */ refresh = !test_mde_flags(entry, MDCACHE_TRUST_CONTENT); } subcall( status = entry->sub_handle->obj_ops.readlink( entry->sub_handle, link_content, refresh) ); if (refresh && !(FSAL_IS_ERROR(status))) atomic_set_uint32_t_bits(&entry->mde_flags, MDCACHE_TRUST_CONTENT); PTHREAD_RWLOCK_unlock(&entry->content_lock); return status; } /** * @brief Create a hard link * * @param[in] obj_hdl Object to link to. * @param[in] destdir_hdl Destination dirctory into which to link * @param[in] name Name of new link * @return FSAL status */ static fsal_status_t mdcache_link(struct fsal_obj_handle *obj_hdl, struct fsal_obj_handle *destdir_hdl, const char *name) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); mdcache_entry_t *dest = container_of(destdir_hdl, mdcache_entry_t, obj_handle); fsal_status_t status; bool invalidate = true; subcall( status = entry->sub_handle->obj_ops.link( entry->sub_handle, dest->sub_handle, name) ); if (FSAL_IS_ERROR(status)) { LogFullDebug(COMPONENT_CACHE_INODE, "link failed %s", fsal_err_txt(status)); return status; } PTHREAD_RWLOCK_wrlock(&dest->content_lock); /* Add this entry to the directory (also takes an internal ref) */ status = mdcache_dirent_add(dest, name, entry, &invalidate); PTHREAD_RWLOCK_unlock(&dest->content_lock); /* Invalidate attributes, so refresh will be forced */ atomic_clear_uint32_t_bits(&entry->mde_flags, MDCACHE_TRUST_ATTRS); if (!invalidate) { /* Refresh destination directory attributes without * invalidating dirents. */ mdcache_refresh_attrs_no_invalidate(dest); } return status; } /** * Read the contents of a dirctory * * If necessary, populate the dirent cache from the underlying FSAL. Then, walk * the dirent cache calling the callback. * * @note The object passed into the callback is ref'd and must be unref'd by the * callback. * * @param[in] dir_hdl the directory to read * @param[in] whence where to start (next) * @param[in] dir_state pass thru of state to callback * @param[in] cb callback function * @param[in] attrmask Which attributes to fill * @param[out] eod_met eod marker true == end of dir * * @return FSAL status */ static fsal_status_t mdcache_readdir(struct fsal_obj_handle *dir_hdl, fsal_cookie_t *whence, void *dir_state, fsal_readdir_cb cb, attrmask_t attrmask, bool *eod_met) { mdcache_entry_t *directory = container_of(dir_hdl, mdcache_entry_t, obj_handle); mdcache_dir_entry_t *dirent = NULL; struct avltree_node *dirent_node = NULL; fsal_status_t status = {0, 0}; enum fsal_dir_result cb_result = DIR_CONTINUE; if (!(directory->obj_handle.type == DIRECTORY)) return fsalstat(ERR_FSAL_NOTDIR, 0); if (test_mde_flags(directory, MDCACHE_BYPASS_DIRCACHE)) { /* Not caching dirents; pass through directly to FSAL */ return mdcache_readdir_uncached(directory, whence, dir_state, cb, attrmask, eod_met); } if (mdcache_param.dir.avl_chunk > 0) { /* Dirent chunking is enabled. */ LogDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Calling mdcache_readdir_chunked whence=%"PRIx64, whence ? *whence : (uint64_t) 0); return mdcache_readdir_chunked(directory, whence ? *whence : (uint64_t) 0, dir_state, cb, attrmask, eod_met); } /* Dirent's are being cached; check to see if it needs updating */ if (!mdc_dircache_trusted(directory)) { PTHREAD_RWLOCK_wrlock(&directory->content_lock); status = mdcache_dirent_populate(directory); PTHREAD_RWLOCK_unlock(&directory->content_lock); if (FSAL_IS_ERROR(status)) { if (status.major == ERR_FSAL_STALE) { LogEvent(COMPONENT_CACHE_INODE, "FSAL returned STALE from readdir."); mdcache_kill_entry(directory); } else if (status.major == ERR_FSAL_OVERFLOW) { /* Directory is too big. Invalidate, set * MDCACHE_BYPASS_DIRCACHE and pass through */ atomic_set_uint32_t_bits(&directory->mde_flags, MDCACHE_BYPASS_DIRCACHE); PTHREAD_RWLOCK_wrlock(&directory->content_lock); mdcache_dirent_invalidate_all(directory); PTHREAD_RWLOCK_unlock(&directory->content_lock); return mdcache_readdir_uncached(directory, whence, dir_state, cb, attrmask, eod_met); } LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "mdcache_dirent_populate status=%s", fsal_err_txt(status)); return status; } } PTHREAD_RWLOCK_rdlock(&directory->content_lock); /* Get initial starting position */ if (*whence > 0) { enum mdcache_avl_err aerr; /* Not a full directory walk */ if (*whence < 3) { /* mdcache always uses 1 and 2 for . and .. */ LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Bad cookie"); status = fsalstat(ERR_FSAL_BADCOOKIE, 0); goto unlock_dir; } aerr = mdcache_avl_lookup_k(directory, *whence, MDCACHE_FLAG_NEXT_ACTIVE, &dirent); switch (aerr) { case MDCACHE_AVL_NOT_FOUND: LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "seek to cookie=%" PRIu64 " fail", *whence); status = fsalstat(ERR_FSAL_BADCOOKIE, 0); goto unlock_dir; case MDCACHE_AVL_LAST: case MDCACHE_AVL_DELETED: /* dirent was last, or all dirents after this one are * deleted */ LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "EOD because empty result"); *eod_met = true; goto unlock_dir; case MDCACHE_AVL_NO_ERROR: assert(dirent); dirent_node = &dirent->node_hk; break; } } else { /* Start at beginning */ dirent_node = avltree_first(&directory->fsobj.fsdir.avl.t); } LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "About to readdir directory=%p cookie=%" PRIu64 " collisions %d", directory, *whence, directory->fsobj.fsdir.avl.collisions); /* Now satisfy the request from the cached readdir--stop when either * the requested sequence or dirent sequence is exhausted */ *eod_met = false; for (; cb_result < DIR_TERMINATE && dirent_node != NULL; dirent_node = avltree_next(dirent_node)) { struct attrlist attrs; mdcache_entry_t *entry = NULL; dirent = avltree_container_of(dirent_node, mdcache_dir_entry_t, node_hk); /* Get actual entry */ status = mdc_try_get_cached(directory, dirent->name, &entry); if (status.major == ERR_FSAL_STALE) { /* NOTE: We're supposed to hold the content_lock for * write here, but to drop the lock we would then * have to resume the readdir, which would mean * adjusting whence from dirent->ck. */ status = mdc_lookup_uncached(directory, dirent->name, &entry, NULL); } if (FSAL_IS_ERROR(status)) { if (status.major == ERR_FSAL_STALE) { PTHREAD_RWLOCK_unlock(&directory->content_lock); mdcache_kill_entry(directory); return status; } LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "lookup failed status=%s", fsal_err_txt(status)); goto unlock_dir; } /* Ensure the attribute cache is valid. The simplest way to do * this is to call getattrs(). We need a copy anyway, to ensure * thread safety. */ fsal_prepare_attrs(&attrs, attrmask); status = entry->obj_handle.obj_ops.getattrs(&entry->obj_handle, &attrs); if (FSAL_IS_ERROR(status)) { LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "getattrs failed status=%s", fsal_err_txt(status)); goto unlock_dir; } #ifdef USE_LTTNG tracepoint(mdcache, mdc_readdir, __func__, __LINE__, entry, entry->lru.refcnt); #endif cb_result = cb(dirent->name, &entry->obj_handle, &attrs, dir_state, dirent->hk.k); fsal_release_attrs(&attrs); if (cb_result >= DIR_TERMINATE) break; } LogDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "dirent_node = %p, cb_result = %s", dirent_node, fsal_dir_result_str(cb_result)); if (!dirent_node && cb_result < DIR_TERMINATE) *eod_met = true; else *eod_met = false; unlock_dir: PTHREAD_RWLOCK_unlock(&directory->content_lock); return status; } /** * @brief Check access for a given user against a given object * * Currently, all FSALs use the default method. We call the default method * directly, so that the test uses cached attributes, rather than having the * lower level need to query attributes each call. This works as long as all * FSALs call the default method. This should be revisited if a FSAL wants to * override test_access(). * * @note If @a owner_skip is provided, we test against the cached owner. This * is because doing a getattrs() potentially on each read and write (writes * invalidate cached attributes) is a huge performance hit. Eventually, finer * grained attribute validity would be a better solution * * @param[in] obj_hdl Handle to check * @param[in] access_type Access requested * @param[out] allowed Returned access that could be granted * @param[out] denied Returned access that would be granted * @param[in] owner_skip Skip test if op_ctx->creds is owner * * @return FSAL status. */ static fsal_status_t mdcache_test_access(struct fsal_obj_handle *obj_hdl, fsal_accessflags_t access_type, fsal_accessflags_t *allowed, fsal_accessflags_t *denied, bool owner_skip) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); if (owner_skip && entry->attrs.owner == op_ctx->creds->caller_uid) return fsalstat(ERR_FSAL_NO_ERROR, 0); return fsal_test_access(obj_hdl, access_type, allowed, denied, owner_skip); } /** * @brief Rename an object * * Rename the given object from @a old_name in @a olddir_hdl to @a new_name in * @a newdir_hdl. The old and new directories may be the same. * * @param[in] obj_hdl Object to rename * @param[in] olddir_hdl Directory containing @a obj_hdl * @param[in] old_name Current name of @a obj_hdl * @param[in] newdir_hdl Directory to move @a obj_hdl to * @param[in] new_name Name to rename @a obj_hdl to * @return FSAL status */ static fsal_status_t mdcache_rename(struct fsal_obj_handle *obj_hdl, struct fsal_obj_handle *olddir_hdl, const char *old_name, struct fsal_obj_handle *newdir_hdl, const char *new_name) { mdcache_entry_t *mdc_olddir = container_of(olddir_hdl, mdcache_entry_t, obj_handle); mdcache_entry_t *mdc_newdir = container_of(newdir_hdl, mdcache_entry_t, obj_handle); mdcache_entry_t *mdc_obj = container_of(obj_hdl, mdcache_entry_t, obj_handle); mdcache_entry_t *mdc_lookup_dst = NULL; struct fsal_export *sub_export = op_ctx->fsal_export->sub_export; bool refresh = false; bool rename_change_key; fsal_status_t status; /* Now update cached dirents. Must take locks in the correct order */ mdcache_src_dest_lock(mdc_olddir, mdc_newdir); status = mdc_try_get_cached(mdc_newdir, new_name, &mdc_lookup_dst); if (!FSAL_IS_ERROR(status)) { if (mdc_obj == mdc_lookup_dst) { /* Same source and destination */ goto unlock; } if (obj_is_junction(&mdc_lookup_dst->obj_handle)) { /* Cannot rename on top of junction */ status = fsalstat(ERR_FSAL_XDEV, 0); goto unlock; } } subcall( status = mdc_olddir->sub_handle->obj_ops.rename( mdc_obj->sub_handle, mdc_olddir->sub_handle, old_name, mdc_newdir->sub_handle, new_name) ); if (FSAL_IS_ERROR(status)) goto unlock; if (mdc_lookup_dst != NULL) { /* Mark target file attributes as invalid */ atomic_clear_uint32_t_bits(&mdc_lookup_dst->mde_flags, MDCACHE_TRUST_ATTRS); } /* Mark renamed file attributes as invalid */ atomic_clear_uint32_t_bits(&mdc_obj->mde_flags, MDCACHE_TRUST_ATTRS); /* Mark directory attributes as invalid */ atomic_clear_uint32_t_bits(&mdc_olddir->mde_flags, MDCACHE_TRUST_ATTRS); if (olddir_hdl != newdir_hdl) { atomic_clear_uint32_t_bits(&mdc_newdir->mde_flags, MDCACHE_TRUST_ATTRS); } /* NOTE: Below we mostly don't check if the directory is not * cached. The cache manipulation functions we call already * bail out if we aren't cached. However, for rename into a * new directory, we need to bypass if not cached even if * chunking is enabled, so we check in that case to make * chunk management simpler. */ if (mdc_lookup_dst) { /* Remove the entry from parent dir_entries avl */ status = mdcache_dirent_remove(mdc_newdir, new_name); if (FSAL_IS_ERROR(status)) { LogDebug(COMPONENT_CACHE_INODE, "remove entry failed with status %s", fsal_err_txt(status)); /* Protected by mdcache_src_dst_lock() above */ mdcache_dirent_invalidate_all(mdc_newdir); } /* Mark unreachable */ mdc_unreachable(mdc_lookup_dst); } subcall( rename_change_key = sub_export->exp_ops.fs_supports( sub_export, fso_rename_changes_key) ); if (rename_change_key) { LogDebug(COMPONENT_CACHE_INODE, "Rename (%p,%s)->(%p,%s) : key changing", mdc_olddir, old_name, mdc_newdir, new_name); /* FSAL changes keys on rename. Just remove the dirent(s) */ /* Old dirent first */ status = mdcache_dirent_remove(mdc_olddir, old_name); if (FSAL_IS_ERROR(status)) { LogDebug(COMPONENT_CACHE_INODE, "Remove stale dirent returned %s", fsal_err_txt(status)); /* Protected by mdcache_src_dst_lock() above */ mdcache_dirent_invalidate_all(mdc_olddir); } /** @todo: With chunking and compute cookie, we can actually * figure out which chunk the new dirent belongs to * without a lookup, so we could just invalidate that * chunk and the rest of the directory can remain * cached. */ /* Now new directory. Here, we just need to invalidate dirents, * since we have a known missing dirent */ mdcache_dirent_invalidate_all(mdc_newdir); /* Handle key is changing. This means the old handle is * useless. Mark it unreachable, forcing a lookup next time */ mdc_unreachable(mdc_obj); } else if (mdc_olddir == mdc_newdir && mdcache_param.dir.avl_chunk == 0) { /** @todo: This code really doesn't accomplish anything * different than the code below, and actually the * code below has better invalidation characteristics * for chunking, so we will remove it later. */ /* if the rename operation is made within the same dir, then we * use an optimization: mdcache_rename_dirent is used * instead of adding/removing dirent. This limits the use of * resource in this case */ LogDebug(COMPONENT_CACHE_INODE, "Rename (%p,%s)->(%p,%s) : source and target directory the same", mdc_olddir, old_name, mdc_newdir, new_name); status = mdcache_dirent_rename(mdc_newdir, old_name, new_name); if (FSAL_IS_ERROR(status)) { if (status.major == ERR_FSAL_NOENT) { /* Someone raced us, and reloaded the directory * after the sub-FSAL rename. This is not an * error. Fall through to invalidate just in * case. */ status = fsalstat(ERR_FSAL_NO_ERROR, 0); } /* We're obviously out of date. Throw out the cached directory */ /* Protected by mdcache_src_dst_lock() above */ mdcache_dirent_invalidate_all(mdc_newdir); } } else { bool invalidate = true; LogDebug(COMPONENT_CACHE_INODE, "Rename (%p,%s)->(%p,%s) : moving entry", mdc_olddir, old_name, mdc_newdir, new_name); /* Remove the old entry */ status = mdcache_dirent_remove(mdc_olddir, old_name); if (FSAL_IS_ERROR(status)) { LogDebug(COMPONENT_CACHE_INODE, "Remove old dirent returned %s", fsal_err_txt(status)); /* Protected by mdcache_src_dst_lock() above */ mdcache_dirent_invalidate_all(mdc_olddir); } /* Don't rename dirents if newdir is not being cached */ if (test_mde_flags(mdc_newdir, MDCACHE_BYPASS_DIRCACHE)) goto unlock; /* We may have a cache entry for the destination * filename. If we do, we must delete it : it is stale. */ status = mdcache_dirent_remove(mdc_newdir, new_name); if (FSAL_IS_ERROR(status)) { LogDebug(COMPONENT_CACHE_INODE, "Remove stale dirent returned %s", fsal_err_txt(status)); /* Protected by mdcache_src_dst_lock() above */ mdcache_dirent_invalidate_all(mdc_newdir); } status = mdcache_dirent_add(mdc_newdir, new_name, mdc_obj, &invalidate); if (FSAL_IS_ERROR(status)) { /* We're obviously out of date. Throw out the cached directory */ LogCrit(COMPONENT_CACHE_INODE, "Add dirent returned %s", fsal_err_txt(status)); /* Protected by mdcache_src_dst_lock() above */ mdcache_dirent_invalidate_all(mdc_newdir); } else if (!invalidate) { /* Refresh destination directory attributes without * invalidating dirents. */ refresh = true; } } unlock: /* unlock entries */ mdcache_src_dest_unlock(mdc_olddir, mdc_newdir); /* Refresh, if necessary. Must be done without lock held */ if (FSAL_IS_SUCCESS(status)) { if (refresh) mdcache_refresh_attrs_no_invalidate(mdc_newdir); /* If we're moving a directory out, update parent hash */ if (mdc_olddir != mdc_newdir && obj_hdl->type == DIRECTORY) { PTHREAD_RWLOCK_wrlock(&mdc_obj->content_lock); mdcache_free_fh(&mdc_obj->fsobj.fsdir.parent); mdc_dir_add_parent(mdc_obj, mdc_newdir); PTHREAD_RWLOCK_unlock(&mdc_obj->content_lock); } } if (mdc_lookup_dst) mdcache_put(mdc_lookup_dst); return status; } /** * @brief Refresh the attributes for an mdcache entry. * * The caller must also call mdcache_kill_entry after releasing the * attr_lock if ERR_FSAL_STALE is returned. * * @note The caller must hold the attribute lock for WRITE * * @param[in] entry The mdcache entry to refresh attributes for. * @param[in] need_acl Indicates if the ACL needs updating. * @param[in] invalidate Invalidate the dirent cache if the entry is a * directory. */ fsal_status_t mdcache_refresh_attrs(mdcache_entry_t *entry, bool need_acl, bool invalidate) { struct attrlist attrs; fsal_status_t status = {0, 0}; struct timespec oldmtime; /* Use this to detect if we should invalidate a directory. */ oldmtime = entry->attrs.mtime; /* We always ask for all regular attributes, even if the caller was * only interested in the ACL. */ fsal_prepare_attrs(&attrs, op_ctx->fsal_export->exp_ops.fs_supported_attrs( op_ctx->fsal_export) | ATTR_RDATTR_ERR); if (!need_acl) { /* Don't request the ACL if not necessary. */ attrs.request_mask &= ~ATTR_ACL; } /* We will want all the requested attributes in the entry */ entry->attrs.request_mask = attrs.request_mask; subcall( status = entry->sub_handle->obj_ops.getattrs( entry->sub_handle, &attrs) ); if (FSAL_IS_ERROR(status)) { /* Done with the attrs */ fsal_release_attrs(&attrs); return status; } mdc_update_attr_cache(entry, &attrs); /* Done with the attrs (we didn't need to call this since the * fsal_copy_attrs preceding consumed all the references, but we * release them anyway to make it easy to scan the code for correctness. */ fsal_release_attrs(&attrs); LogAttrlist(COMPONENT_CACHE_INODE, NIV_FULL_DEBUG, "attrs ", &entry->attrs, true); if (invalidate && entry->obj_handle.type == DIRECTORY && gsh_time_cmp(&oldmtime, &entry->attrs.mtime) < 0) { PTHREAD_RWLOCK_wrlock(&entry->content_lock); mdcache_dirent_invalidate_all(entry); PTHREAD_RWLOCK_unlock(&entry->content_lock); } return status; } /** * @brief Get the attributes for an object * * If the attribute cache is valid, just return them. Otherwise, resfresh the * cache. * * @param[in] obj_hdl Object to get attributes from * @param[in,out] attrs_out Attributes fetched * @return FSAL status */ static fsal_status_t mdcache_getattrs(struct fsal_obj_handle *obj_hdl, struct attrlist *attrs_out) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); fsal_status_t status = {0, 0}; PTHREAD_RWLOCK_rdlock(&entry->attr_lock); if (mdcache_is_attrs_valid(entry, attrs_out->request_mask)) { /* Up-to-date */ goto unlock; } /* Promote to write lock */ PTHREAD_RWLOCK_unlock(&entry->attr_lock); PTHREAD_RWLOCK_wrlock(&entry->attr_lock); if (mdcache_is_attrs_valid(entry, attrs_out->request_mask)) { /* Someone beat us to it */ goto unlock; } status = mdcache_refresh_attrs( entry, (attrs_out->request_mask & ATTR_ACL) != 0, true); if (FSAL_IS_ERROR(status)) { /* We failed to fetch any attributes. Pass that fact back to * the caller. We do not change the validity of the current * entry attributes. */ if (attrs_out->request_mask & ATTR_RDATTR_ERR) attrs_out->valid_mask = ATTR_RDATTR_ERR; goto unlock_no_attrs; } unlock: /* Struct copy */ fsal_copy_attrs(attrs_out, &entry->attrs, false); unlock_no_attrs: PTHREAD_RWLOCK_unlock(&entry->attr_lock); if (FSAL_IS_ERROR(status) && (status.major == ERR_FSAL_STALE)) mdcache_kill_entry(entry); LogAttrlist(COMPONENT_CACHE_INODE, NIV_FULL_DEBUG, "attrs ", attrs_out, true); return status; } /** * @brief Set attributes on an object (new style) * * @param[in] obj_hdl Object owning state * @param[in] state Open file state to set attributes on * @param[in] attrs Attributes to set * @return FSAL status */ static fsal_status_t mdcache_setattr2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, struct attrlist *attrs) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); fsal_status_t status, status2; uint64_t change; bool need_acl = false, kill_entry = false; change = entry->attrs.change; subcall( status = entry->sub_handle->obj_ops.setattr2( entry->sub_handle, bypass, state, attrs) ); if (FSAL_IS_ERROR(status)) { if (status.major == ERR_FSAL_STALE) kill_entry = true; goto out; } /* In case of ACL enabled, any of the below attribute changes * result in change of ACL set as well. */ if (!op_ctx_export_has_option(EXPORT_OPTION_DISABLE_ACL) && (FSAL_TEST_MASK(attrs->valid_mask, ATTR_MODE | ATTR_OWNER | ATTR_GROUP | ATTR_ACL))) { need_acl = true; } PTHREAD_RWLOCK_wrlock(&entry->attr_lock); status2 = mdcache_refresh_attrs(entry, need_acl, false); if (FSAL_IS_ERROR(status2)) { /* Assume that the cache is bogus now */ atomic_clear_uint32_t_bits(&entry->mde_flags, MDCACHE_TRUST_ATTRS | MDCACHE_TRUST_ACL); if (status2.major == ERR_FSAL_STALE) kill_entry = true; } else if (change == entry->attrs.change) { LogDebug(COMPONENT_CACHE_INODE, "setattr2 did not change change attribute before %lld after = %lld", (long long int) change, (long long int) entry->attrs.change); entry->attrs.change = change + 1; } PTHREAD_RWLOCK_unlock(&entry->attr_lock); out: if (kill_entry) mdcache_kill_entry(entry); return status; } /** * @brief Unlink an object * * Does some junction handling * * @param[in] dir_hdl Parent directory handle * @param[in] obj_hdl Object being removed * @param[in] name Name of object to remove * @return FSAL status */ static fsal_status_t mdcache_unlink(struct fsal_obj_handle *dir_hdl, struct fsal_obj_handle *obj_hdl, const char *name) { mdcache_entry_t *parent = container_of(dir_hdl, mdcache_entry_t, obj_handle); mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); fsal_status_t status; LogFullDebug(COMPONENT_CACHE_INODE, "Unlink %p/%s (%p)", parent, name, entry); if (obj_is_junction(&entry->obj_handle)) { /* Cannot remove a junction */ return fsalstat(ERR_FSAL_XDEV, 0); } subcall( status = parent->sub_handle->obj_ops.unlink( parent->sub_handle, entry->sub_handle, name) ); if (FSAL_IS_ERROR(status)) { LogDebug(COMPONENT_CACHE_INODE, "unlink %s returned %s", name, fsal_err_txt(status)); if (status.major == ERR_FSAL_STALE) (void)mdcache_kill_entry(parent); else if (status.major == ERR_FSAL_NOTEMPTY && (obj_hdl->type == DIRECTORY)) { PTHREAD_RWLOCK_wrlock(&entry->content_lock); mdcache_dirent_invalidate_all(entry); PTHREAD_RWLOCK_unlock(&entry->content_lock); } else { /* Some real error. Bail. */ return status; } } else { PTHREAD_RWLOCK_wrlock(&parent->content_lock); (void)mdcache_dirent_remove(parent, name); PTHREAD_RWLOCK_unlock(&parent->content_lock); /* Invalidate attributes of parent and entry */ atomic_clear_uint32_t_bits(&parent->mde_flags, MDCACHE_TRUST_ATTRS); atomic_clear_uint32_t_bits(&entry->mde_flags, MDCACHE_TRUST_ATTRS); if (entry->obj_handle.type == DIRECTORY) { PTHREAD_RWLOCK_wrlock(&entry->content_lock); mdcache_free_fh(&entry->fsobj.fsdir.parent); PTHREAD_RWLOCK_unlock(&entry->content_lock); } mdc_unreachable(entry); } LogFullDebug(COMPONENT_CACHE_INODE, "Unlink %s %p/%s (%p)", FSAL_IS_ERROR(status) ? "failed" : "done", parent, name, entry); return status; } /** * @brief get fs_locations * * This function returns the fs locations for an object. * * @param[in] obj_hdl Object to get fs locations for * @param[out] fs_locs fs locations * * @return FSAL status */ static fsal_status_t mdcache_fs_locations(struct fsal_obj_handle *obj_hdl, struct fs_locations4 *fs_locs) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); fsal_status_t status; subcall( status = entry->sub_handle->obj_ops.fs_locations( entry->sub_handle, fs_locs) ); return status; } /** * @brief Test handle type * * All FSALs currently use the default, but delegate in case a FSAL wants to * override. * * @param[in] obj_hdl Handle to test * @param[in] type Type to test * @retval true if it is. * @retval false if it isn't. */ static bool mdcache_handle_is(struct fsal_obj_handle *obj_hdl, object_file_type_t type) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); bool status; subcall( status = entry->sub_handle->obj_ops.handle_is( entry->sub_handle, type) ); return status; } /** * @brief Get the wire version of a handle * * Just pass through to the underlying FSAL * * @param[in] obj_hdl Handle to digest * @param[in] out_type Type of digest to get * @param[out] fh_desc Buffer to write digest into * @return FSAL status */ static fsal_status_t mdcache_handle_to_wire( const struct fsal_obj_handle *obj_hdl, fsal_digesttype_t out_type, struct gsh_buffdesc *fh_desc) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); fsal_status_t status; subcall( status = entry->sub_handle->obj_ops.handle_to_wire( entry->sub_handle, out_type, fh_desc) ); return status; } /** * @brief Get the unique key for a handle * * Just pass through to the underlying FSAL * * @param[in] obj_hdl Handle to digest * @param[out] fh_desc Buffer to write key into */ static void mdcache_handle_to_key(struct fsal_obj_handle *obj_hdl, struct gsh_buffdesc *fh_desc) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); subcall( entry->sub_handle->obj_ops.handle_to_key(entry->sub_handle, fh_desc) ); } /** * @brief Compare two handles * * All FSALs currently use the default, but delegate in case a FSAL wants to * override. * * @param[in] obj_hdl1 The first handle to compare * @param[in] obj_hdl2 The second handle to compare * * @return True if match, false otherwise */ static bool mdcache_handle_cmp(struct fsal_obj_handle *obj_hdl1, struct fsal_obj_handle *obj_hdl2) { mdcache_entry_t *entry1 = container_of(obj_hdl1, mdcache_entry_t, obj_handle); mdcache_entry_t *entry2 = container_of(obj_hdl2, mdcache_entry_t, obj_handle); bool status; subcall( status = entry1->sub_handle->obj_ops.handle_cmp( entry1->sub_handle, entry2->sub_handle) ); return status; } /** * @brief Grant a layout segment. * * Delegate to sub-FSAL * * @param[in] obj_hdl The handle of the file on which the layout is * requested. * @param[in] req_ctx Request context * @param[out] loc_body An XDR stream to which the FSAL must encode * the layout specific portion of the granted * layout segment. * @param[in] arg Input arguments of the function * @param[in,out] res In/out and output arguments of the function * * @return Valid error codes in RFC 5661, pp. 366-7. */ static nfsstat4 mdcache_layoutget(struct fsal_obj_handle *obj_hdl, struct req_op_context *req_ctx, XDR *loc_body, const struct fsal_layoutget_arg *arg, struct fsal_layoutget_res *res) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); nfsstat4 status; subcall( status = entry->sub_handle->obj_ops.layoutget( entry->sub_handle, req_ctx, loc_body, arg, res) ); return status; } /** * @brief Potentially return one layout segment * * Delegate to sub-FSAL * * @param[in] obj_hdl The object on which a segment is to be returned * @param[in] req_ctx Request context * @param[in] lrf_body In the case of a non-synthetic return, this is * an XDR stream corresponding to the layout * type-specific argument to LAYOUTRETURN. In * the case of a synthetic or bulk return, * this is a NULL pointer. * @param[in] arg Input arguments of the function * * @return Valid error codes in RFC 5661, p. 367. */ static nfsstat4 mdcache_layoutreturn(struct fsal_obj_handle *obj_hdl, struct req_op_context *req_ctx, XDR *lrf_body, const struct fsal_layoutreturn_arg *arg) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); nfsstat4 status; subcall( status = entry->sub_handle->obj_ops.layoutreturn( entry->sub_handle, req_ctx, lrf_body, arg) ); return status; } /** * @brief Commit a segment of a layout * * Delegate to sub-FSAL * * @param[in] obj_hdl The object on which to commit * @param[in] req_ctx Request context * @param[in] lou_body An XDR stream containing the layout * type-specific portion of the LAYOUTCOMMIT * arguments. * @param[in] arg Input arguments of the function * @param[in,out] res In/out and output arguments of the function * * @return Valid error codes in RFC 5661, p. 366. */ static nfsstat4 mdcache_layoutcommit(struct fsal_obj_handle *obj_hdl, struct req_op_context *req_ctx, XDR *lou_body, const struct fsal_layoutcommit_arg *arg, struct fsal_layoutcommit_res *res) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); nfsstat4 status; subcall( status = entry->sub_handle->obj_ops.layoutcommit( entry->sub_handle, req_ctx, lou_body, arg, res) ); if (status == NFS4_OK) atomic_clear_uint32_t_bits(&entry->mde_flags, MDCACHE_TRUST_ATTRS); return status; } /** * @brief Get a reference to the handle * * @param[in] obj_hdl Handle to ref * @return FSAL status */ static void mdcache_get_ref(struct fsal_obj_handle *obj_hdl) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); mdcache_get(entry); } /** * @brief Put a reference to the handle * * @param[in] obj_hdl Handle to unref * @return FSAL status */ static void mdcache_put_ref(struct fsal_obj_handle *obj_hdl) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); mdcache_put(entry); } /** * @brief Release an object handle * * This force cleans-up. * * @param[in] obj_hdl Handle to release * @return FSAL status */ static void mdcache_hdl_release(struct fsal_obj_handle *obj_hdl) { mdcache_entry_t *entry = container_of(obj_hdl, mdcache_entry_t, obj_handle); LogDebug(COMPONENT_CACHE_INODE, "Releasing obj_hdl=%p, entry=%p", obj_hdl, entry); mdcache_kill_entry(entry); } /** * @brief Merge a duplicate handle with an original handle * * Delegate to sub-FSAL. This should not happen, because of the cache, but * handle it anyway. * * @param[in] orig_hdl Original handle * @param[in] dupe_hdl Handle to merge into original * @return FSAL status */ static fsal_status_t mdcache_merge(struct fsal_obj_handle *orig_hdl, struct fsal_obj_handle *dupe_hdl) { mdcache_entry_t *entry = container_of(orig_hdl, mdcache_entry_t, obj_handle); fsal_status_t status; subcall( status = entry->sub_handle->obj_ops.merge(entry->sub_handle, dupe_hdl) ); return status; } void mdcache_handle_ops_init(struct fsal_obj_ops *ops) { ops->get_ref = mdcache_get_ref; ops->put_ref = mdcache_put_ref; ops->release = mdcache_hdl_release; ops->merge = mdcache_merge; ops->lookup = mdcache_lookup; ops->readdir = mdcache_readdir; ops->mkdir = mdcache_mkdir; ops->mknode = mdcache_mknode; ops->symlink = mdcache_symlink; ops->readlink = mdcache_readlink; ops->test_access = mdcache_test_access; ops->getattrs = mdcache_getattrs; ops->link = mdcache_link; ops->rename = mdcache_rename; ops->unlink = mdcache_unlink; ops->fs_locations = mdcache_fs_locations; ops->seek = mdcache_seek; ops->io_advise = mdcache_io_advise; ops->close = mdcache_close; ops->handle_is = mdcache_handle_is; ops->handle_to_wire = mdcache_handle_to_wire; ops->handle_to_key = mdcache_handle_to_key; ops->handle_cmp = mdcache_handle_cmp; /* pNFS */ ops->layoutget = mdcache_layoutget; ops->layoutreturn = mdcache_layoutreturn; ops->layoutcommit = mdcache_layoutcommit; /* Multi-FD */ ops->open2 = mdcache_open2; ops->check_verifier = mdcache_check_verifier; ops->status2 = mdcache_status2; ops->reopen2 = mdcache_reopen2; ops->read2 = mdcache_read2; ops->write2 = mdcache_write2; ops->seek2 = mdcache_seek2; ops->io_advise2 = mdcache_io_advise2; ops->commit2 = mdcache_commit2; ops->lock_op2 = mdcache_lock_op2; ops->lease_op2 = mdcache_lease_op2; ops->setattr2 = mdcache_setattr2; ops->close2 = mdcache_close2; /* xattr related functions */ ops->list_ext_attrs = mdcache_list_ext_attrs; ops->getextattr_id_by_name = mdcache_getextattr_id_by_name; ops->getextattr_value_by_name = mdcache_getextattr_value_by_name; ops->getextattr_value_by_id = mdcache_getextattr_value_by_id; ops->setextattr_value = mdcache_setextattr_value; ops->setextattr_value_by_id = mdcache_setextattr_value_by_id; ops->remove_extattr_by_id = mdcache_remove_extattr_by_id; ops->remove_extattr_by_name = mdcache_remove_extattr_by_name; ops->getxattrs = mdcache_getxattrs; ops->setxattrs = mdcache_setxattrs; ops->removexattrs = mdcache_removexattrs; ops->listxattrs = mdcache_listxattrs; } /* * export methods that create object handles */ /** * @brief Lookup a path from the export * * Lookup in the sub-FSAL, and wrap with a MDCACHE entry. This is the * equivalent of ...lookup_path() followed by mdcache_new_entry() * * @param[in] exp_hdl FSAL export to look in * @param[in] path Path to find * @param[out] handle Resulting object handle * @param[in,out] attrs_out Optional attributes for newly created object * * @note This returns an INITIAL ref'd entry on success * @return FSAL status */ fsal_status_t mdcache_lookup_path(struct fsal_export *exp_hdl, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { struct fsal_obj_handle *sub_handle = NULL; struct mdcache_fsal_export *export = container_of(exp_hdl, struct mdcache_fsal_export, mfe_exp); struct fsal_export *sub_export = export->mfe_exp.sub_export; fsal_status_t status; struct attrlist attrs; mdcache_entry_t *new_entry; *handle = NULL; /* Ask for all supported attributes except ACL (we defer fetching ACL * until asked for it (including a permission check). */ fsal_prepare_attrs(&attrs, op_ctx->fsal_export->exp_ops.fs_supported_attrs( op_ctx->fsal_export) & ~ATTR_ACL); subcall_raw(export, status = sub_export->exp_ops.lookup_path(sub_export, path, &sub_handle, &attrs) ); if (unlikely(FSAL_IS_ERROR(status))) { LogDebug(COMPONENT_CACHE_INODE, "lookup_path %s failed with %s", path, fsal_err_txt(status)); fsal_release_attrs(&attrs); return status; } status = mdcache_new_entry(export, sub_handle, &attrs, attrs_out, false, &new_entry, NULL); fsal_release_attrs(&attrs); if (!FSAL_IS_ERROR(status)) { LogFullDebug(COMPONENT_CACHE_INODE, "lookup_path Created entry %p FSAL %s", new_entry, new_entry->sub_handle->fsal->name); /* Make sure this entry has a parent pointer */ PTHREAD_RWLOCK_wrlock(&new_entry->content_lock); mdc_get_parent(export, new_entry); PTHREAD_RWLOCK_unlock(&new_entry->content_lock); *handle = &new_entry->obj_handle; } if (attrs_out != NULL) { LogAttrlist(COMPONENT_CACHE_INODE, NIV_FULL_DEBUG, "lookup_path ", attrs_out, true); } return status; } /** * @brief Find or create a cache entry from a host-handle * * This is the equivalent of mdcache_get(). It returns a ref'd entry that * must be put using obj_ops.release(). * * @param[in] exp_hdl The export in which to create the handle * @param[in] hdl_desc Buffer descriptor for the host handle * @param[out] handle FSAL object handle * @param[in,out] attrs_out Optional attributes for newly created object * * @return FSAL status */ fsal_status_t mdcache_create_handle(struct fsal_export *exp_hdl, struct gsh_buffdesc *fh_desc, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { struct mdcache_fsal_export *export = container_of(exp_hdl, struct mdcache_fsal_export, mfe_exp); mdcache_entry_t *entry; fsal_status_t status; *handle = NULL; status = mdcache_locate_host(fh_desc, export, &entry, attrs_out); if (FSAL_IS_ERROR(status)) return status; /* Make sure this entry has a parent pointer */ PTHREAD_RWLOCK_wrlock(&entry->content_lock); mdc_get_parent(export, entry); PTHREAD_RWLOCK_unlock(&entry->content_lock); if (attrs_out != NULL) { LogAttrlist(COMPONENT_CACHE_INODE, NIV_FULL_DEBUG, "create_handle ", attrs_out, true); } *handle = &entry->obj_handle; return fsalstat(ERR_FSAL_NO_ERROR, 0); } nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_hash.c000066400000000000000000000061631324272410200244750ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) 2013, The Linux Box Corporation * Contributor : Matt Benjamin * * Some portions Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ #include "config.h" #include #include #include #include #include #include #include #include #include "fsal.h" #include "nfs_core.h" #include "log.h" #include "mdcache_int.h" #include "mdcache_hash.h" /** * @addtogroup FSAL_MDCACHE * @{ */ /** * * @file mdcache_hash.c * @author Matt Benjamin * @brief Cache inode hashed dictionary package * * @section Description * * This module exports an interface for efficient lookup of cache entries * by file handle. Refactored from the prior abstract HashTable * implementation. */ struct cih_lookup_table cih_fhcache; static bool initialized; /** * @brief Initialize the package. */ void cih_pkginit(void) { pthread_rwlockattr_t rwlock_attr; cih_partition_t *cp; int ix; /* avoid writer starvation */ pthread_rwlockattr_init(&rwlock_attr); #ifdef GLIBC pthread_rwlockattr_setkind_np( &rwlock_attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP); #endif cih_fhcache.npart = mdcache_param.nparts; cih_fhcache.partition = gsh_calloc(cih_fhcache.npart, sizeof(cih_partition_t)); cih_fhcache.cache_sz = mdcache_param.cache_size; for (ix = 0; ix < cih_fhcache.npart; ++ix) { cp = &cih_fhcache.partition[ix]; cp->part_ix = ix; PTHREAD_RWLOCK_init(&cp->lock, &rwlock_attr); avltree_init(&cp->t, cih_fh_cmpf, 0 /* must be 0 */); cp->cache = gsh_calloc(cih_fhcache.cache_sz, sizeof(struct avltree_node *)); } initialized = true; } /** * @brief Destroy the package. */ void cih_pkgdestroy(void) { /* Index over partitions */ int ix = 0; /* Destroy the partitions, warning if not empty */ for (ix = 0; ix < cih_fhcache.npart; ++ix) { if (avltree_first(&cih_fhcache.partition[ix].t) != NULL) LogMajor(COMPONENT_CACHE_INODE, "Cache inode AVL tree not empty"); PTHREAD_RWLOCK_destroy(&cih_fhcache.partition[ix].lock); gsh_free(cih_fhcache.partition[ix].cache); } /* Destroy the partition table */ gsh_free(cih_fhcache.partition); cih_fhcache.partition = NULL; initialized = false; } /** @} */ nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_hash.h000066400000000000000000000263531324272410200245050ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) 2013, The Linux Box Corporation * Contributor : Matt Benjamin * * Some portions Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @addtogroup FSAL_MDCACHE * @{ */ /** * @file mdcache_hash.h * @brief Cache inode hashed dictionary package * * This module exports an interface for efficient lookup of cache entries * by file handle, (etc?). Refactored from the prior abstract HashTable * implementation. */ #ifndef CACHE_INODE_HASH_H #define CACHE_INODE_HASH_H #include "config.h" #include "log.h" #include "abstract_atomic.h" #include "mdcache_int.h" #include "gsh_intrinsic.h" #include "mdcache_lru.h" #include "city.h" #include #ifdef USE_LTTNG #include "gsh_lttng/mdcache.h" #endif /** * @brief The table partition * * Each tree is independent, having its own lock, thus reducing thread * contention. */ typedef struct cih_partition { uint32_t part_ix; pthread_rwlock_t lock; struct avltree t; struct avltree_node **cache; #ifdef ENABLE_LOCKTRACE struct { char *func; uint32_t line; } locktrace; #endif GSH_CACHE_PAD(0); } cih_partition_t; /** * @brief The weakref table structure * * This is the structure corresponding to a single table of weakrefs. */ struct cih_lookup_table { GSH_CACHE_PAD(0); cih_partition_t *partition; uint32_t npart; uint32_t cache_sz; }; /* Support inline lookups */ extern struct cih_lookup_table cih_fhcache; /** * @brief Initialize the package. */ void cih_pkginit(void); /** * @brief Destroy the package. */ void cih_pkgdestroy(void); /** * @brief Find the correct partition for a pointer * * To lower thread contention, the table is composed of multiple * trees, with the tree that receives a pointer determined by a * modulus. This macro yields an expression that yields a pointer to * the correct partition. */ #define cih_partition_of_scalar(lt, k) \ (((lt)->partition)+(((uint64_t)k)%(lt)->npart)) /** * @brief Compute cache slot for an entry * * This function computes a hash slot, taking an address modulo the * number of cache slotes (which should be prime). * * @param wt [in] The table * @param ptr [in] Entry address * * @return The computed offset. */ static inline uint32_t cih_cache_offsetof(struct cih_lookup_table *lt, uint64_t k) { return k % lt->cache_sz; } /** * @brief Cache inode FH hashed comparison function. * * Entries are ordered by integer hash first, and second by bitwise * comparison of the corresponding file handle. * * For key prototypes, which have no object handle, the buffer pointed to * by fh_k.fh_desc_k is taken to be the file handle. Further, ONLY key * prototype entries may have a non-NULL value for fh_k.fh_desc_k. * * @param lhs [in] First node * @param rhs [in] Second node * * @retval -1: lhs compares as less than rhs * @retval 0: lhs and rhs compare equal * @retval 1: lhs is greater than rhs */ static inline int cih_fh_cmpf(const struct avltree_node *lhs, const struct avltree_node *rhs) { mdcache_entry_t *lk, *rk; lk = avltree_container_of(lhs, mdcache_entry_t, fh_hk.node_k); rk = avltree_container_of(rhs, mdcache_entry_t, fh_hk.node_k); return mdcache_key_cmp(&lk->fh_hk.key, &rk->fh_hk.key); } /** * @brief Open-coded avltree lookup * * Search for an entry matching key in avltree tree. * * @todo dang this should be in the avltree implementation. It's dangerous to * open-code an avltree lookup elsewhere. * * @param tree [in] The avltree to search * @param key [in] Entry being searched for, as an avltree node * * @return Pointer to node if found, else NULL. */ static inline struct avltree_node * cih_fhcache_inline_lookup(const struct avltree *tree, const struct avltree_node *key) { return avltree_inline_lookup(key, tree, cih_fh_cmpf); } #define CIH_HASH_NONE 0x0000 #define CIH_HASH_KEY_PROTOTYPE 0x0001 /** * @brief Convenience function to compute hash for mdcache_entry_t * * Computes hash of entry using input fh_desc. If entry is not a * disposable key prototype, fh_desc is duplicated in entry. * * @param entry [in] Entry to be hashed * @param fh_desc [in] Hash input bytes * * @return (void) */ static inline bool cih_hash_key(mdcache_key_t *key, struct fsal_module *fsal, struct gsh_buffdesc *fh_desc, uint32_t flags) { key->fsal = fsal; /* fh prototype fixup */ if (flags & CIH_HASH_KEY_PROTOTYPE) { key->kv = *fh_desc; } else { /* XXX dups fh_desc */ key->kv.len = fh_desc->len; key->kv.addr = gsh_malloc(fh_desc->len); memcpy(key->kv.addr, fh_desc->addr, fh_desc->len); } /* hash it */ key->hk = CityHash64WithSeed(fh_desc->addr, fh_desc->len, 557); return true; } #define CIH_GET_NONE 0x0000 #define CIH_GET_RLOCK 0x0001 #define CIH_GET_WLOCK 0x0002 #define CIH_GET_UNLOCK_ON_MISS 0x0004 /** * @brief Hash latch structure. * * Used to memoize a partition and its lock state between calls. Nod * to Adam's precursor in HashTable. */ typedef struct cih_latch { cih_partition_t *cp; } cih_latch_t; static inline void cih_hash_release(cih_latch_t *latch) { PTHREAD_RWLOCK_unlock(&(latch->cp->lock)); } /** * @brief Latch the partition of key. * * @param key [in] The key * @param latch [inout] Latch * @param flags [in] Flags * * @return true on success, false on failure */ static inline bool cih_latch_entry(mdcache_key_t *key, cih_latch_t *latch, uint32_t flags, const char *func, int line) { cih_partition_t *cp; latch->cp = cp = cih_partition_of_scalar(&cih_fhcache, key->hk); if (flags & CIH_GET_WLOCK) PTHREAD_RWLOCK_wrlock(&cp->lock); /* SUBTREE_WLOCK */ else PTHREAD_RWLOCK_rdlock(&cp->lock); /* SUBTREE_RLOCK */ #ifdef ENABLE_LOCKTRACE cp->locktrace.func = (char *)func; cp->locktrace.line = line; #endif return true; } /** * @brief Lookup cache entry by key * * Lookup cache entry by fh, optionally return with hash partition shared * or exclusive locked. Differs from the fh variant in using the precomputed * hash stored with key. * * @param key [in] Key being searched * @param latch [out] Pointer to partition * @param flags [in] Flags * * @return Pointer to cache entry if found, else NULL */ static inline mdcache_entry_t * cih_get_by_key_latch(mdcache_key_t *key, cih_latch_t *latch, uint32_t flags, const char *func, int line) { mdcache_entry_t k_entry, *entry = NULL; struct avltree_node *node; void **cache_slot; if (!cih_latch_entry(key, latch, flags, func, line)) return NULL; k_entry.fh_hk.key = *key; /* check cache */ cache_slot = (void **) &(latch->cp->cache[cih_cache_offsetof(&cih_fhcache, key->hk)]); node = (struct avltree_node *)atomic_fetch_voidptr(cache_slot); if (node) { if (cih_fh_cmpf(&k_entry.fh_hk.node_k, node) == 0) { /* got it in 1 */ LogDebug(COMPONENT_HASHTABLE_CACHE, "cih cache hit slot %d", cih_cache_offsetof(&cih_fhcache, key->hk)); goto found; } } /* check AVL */ node = cih_fhcache_inline_lookup(&latch->cp->t, &k_entry.fh_hk.node_k); if (!node) { if (flags & CIH_GET_UNLOCK_ON_MISS) cih_hash_release(latch); LogDebug(COMPONENT_HASHTABLE_CACHE, "fdcache MISS"); goto out; } /* update cache */ atomic_store_voidptr(cache_slot, node); LogDebug(COMPONENT_HASHTABLE_CACHE, "cih AVL hit slot %d", cih_cache_offsetof(&cih_fhcache, key->hk)); found: entry = avltree_container_of(node, mdcache_entry_t, fh_hk.node_k); out: return entry; } #define CIH_SET_NONE 0x0000 #define CIH_SET_HASHED 0x0001 /* previously hashed entry */ #define CIH_SET_UNLOCK 0x0002 /** * @brief Insert cache entry on partition previously locked. * * Insert cache entry on partition previously locked. * * @param entry [in] Entry to be inserted * @param fh_desc [in] Hash input bytes (MUST be those used previously) * @param flags [in] Flags * * @return Pointer to cache entry if found, else NULL */ static inline int cih_set_latched(mdcache_entry_t *entry, cih_latch_t *latch, struct fsal_module *fsal, struct gsh_buffdesc *fh_desc, uint32_t flags) { cih_partition_t *cp = latch->cp; /* Omit hash if you are SURE we hashed it, and that the * hash remains valid */ if (unlikely(!(flags & CIH_SET_HASHED))) if (!cih_hash_key(&entry->fh_hk.key, fsal, fh_desc, CIH_HASH_NONE)) return 1; (void)avltree_insert(&entry->fh_hk.node_k, &cp->t); entry->fh_hk.inavl = true; #ifdef USE_LTTNG tracepoint(mdcache, mdc_lru_insert, __func__, __LINE__, entry, entry->lru.refcnt); #endif if (likely(flags & CIH_SET_UNLOCK)) cih_hash_release(latch); return 0; } /** * @brief Remove cache entry with existence check. * * Remove cache entry with existence check. The entry is assumed to * be hashed. * * @param entry [in] Entry to be removed. * * @return (void) */ static inline bool cih_remove_checked(mdcache_entry_t *entry) { struct avltree_node *node; cih_partition_t *cp = cih_partition_of_scalar(&cih_fhcache, entry->fh_hk.key.hk); bool freed = false; PTHREAD_RWLOCK_wrlock(&cp->lock); node = cih_fhcache_inline_lookup(&cp->t, &entry->fh_hk.node_k); if (entry->fh_hk.inavl && node) { #ifdef USE_LTTNG tracepoint(mdcache, mdc_lru_remove, __func__, __LINE__, entry, entry->lru.refcnt); #endif avltree_remove(node, &cp->t); cp->cache[cih_cache_offsetof(&cih_fhcache, entry->fh_hk.key.hk)] = NULL; entry->fh_hk.inavl = false; /* return sentinel ref */ freed = mdcache_lru_unref(entry); } PTHREAD_RWLOCK_unlock(&cp->lock); return freed; } /** * @brief Remove cache entry protected by latch * * Remove cache entry. * * @note Must NOT be called with qlane lock held. * * @param entry [in] Entry to be removed.0 * * @return true if entry is invalid */ #define CIH_REMOVE_NONE 0x0000 #define CIH_REMOVE_UNLOCK 0x0001 static inline bool cih_remove_latched(mdcache_entry_t *entry, cih_latch_t *latch, uint32_t flags) { cih_partition_t *cp = cih_partition_of_scalar(&cih_fhcache, entry->fh_hk.key.hk); if (entry->fh_hk.inavl) { #ifdef USE_LTTNG tracepoint(mdcache, mdc_lru_remove, __func__, __LINE__, entry, entry->lru.refcnt); #endif avltree_remove(&entry->fh_hk.node_k, &cp->t); cp->cache[cih_cache_offsetof(&cih_fhcache, entry->fh_hk.key.hk)] = NULL; entry->fh_hk.inavl = false; mdcache_lru_unref(entry); if (flags & CIH_REMOVE_UNLOCK) cih_hash_release(latch); return true; } if (flags & CIH_REMOVE_UNLOCK) cih_hash_release(latch); return false; } #endif /* CACHE_INODE_HASH_H */ /** @} */ nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_helpers.c000066400000000000000000003210011324272410200252030ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright 2015-2018 Red Hat, Inc. and/or its affiliates. * Author: Daniel Gryniewicz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * */ /** * @addtogroup FSAL_MDCACHE * @{ */ /** * @file mdcache_helpers.c * @brief Miscellaneous helper functions */ #include "config.h" #include "sal_functions.h" #include "fsal.h" #include "FSAL/fsal_commonlib.h" #include "fsal_convert.h" #include #include #include #include #include "nfs_exports.h" #include "mdcache_lru.h" #include "mdcache_hash.h" #include "mdcache_avl.h" #ifdef USE_LTTNG #include "gsh_lttng/mdcache.h" #endif static inline bool trust_negative_cache(mdcache_entry_t *parent) { bool trust = op_ctx_export_has_option( EXPORT_OPTION_TRUST_READIR_NEGATIVE_CACHE) && test_mde_flags(parent, MDCACHE_DIR_POPULATED); if (trust) LogFullDebug(COMPONENT_CACHE_INODE, "Entry %p Trust negative cache", parent); else LogFullDebug(COMPONENT_CACHE_INODE, "Entry %p Don't Trust negative cache", parent); return trust; } /** * @brief Add a detached dirent to the LRU list (in the MRU position). * * If rhe maximum number of detached dirents would be exceeded, remove the * LRU dirent. * * @note mdc_parent MUST have it's content_lock held for writing * * @param[in] parent Parent entry * @param[in] dirent Dirent to move to MRU */ static inline void add_detached_dirent(mdcache_entry_t *parent, mdcache_dir_entry_t *dirent) { #ifdef DEBUG_MDCACHE assert(parent->content_lock.__data.__writer != 0); #endif if (parent->fsobj.fsdir.detached_count == mdcache_param.dir.avl_detached_max) { /* Need to age out oldest detached dirent. */ mdcache_dir_entry_t *removed; /* Find the oldest detached dirent and remove it. * We just hold the spin lock for the list operation. * Technically we don't need it since the content lock is held * for write, there can be no conflicting threads. Since we * don't have a racing thread, it's ok that the list is * unprotected by spin lock while we make the AVL call. */ pthread_spin_lock(&parent->fsobj.fsdir.spin); removed = glist_last_entry(&parent->fsobj.fsdir.detached, mdcache_dir_entry_t, chunk_list); pthread_spin_unlock(&parent->fsobj.fsdir.spin); /* Remove from active names tree */ mdcache_avl_remove(parent, removed); } /* Add new entry to MRU (head) of list */ pthread_spin_lock(&parent->fsobj.fsdir.spin); glist_add(&parent->fsobj.fsdir.detached, &dirent->chunk_list); parent->fsobj.fsdir.detached_count++; pthread_spin_unlock(&parent->fsobj.fsdir.spin); } /** * Allocate and initialize a new mdcache handle. * * This function doesn't free the sub_handle if the allocation fails. It must * be done in the calling function. * * @param[in] export The mdcache export used by the handle. * @param[in] sub_handle The handle used by the subfsal. * @param[in] fs The filesystem of the new handle. * * @return The new handle, or NULL if the unexport in progress. */ static mdcache_entry_t *mdcache_alloc_handle( struct mdcache_fsal_export *export, struct fsal_obj_handle *sub_handle, struct fsal_filesystem *fs) { mdcache_entry_t *result; fsal_status_t status; result = mdcache_lru_get(); if (result == NULL) { /* Should never happen, but our caller will handle... */ return NULL; } /* Base data */ result->sub_handle = sub_handle; result->obj_handle.type = sub_handle->type; result->obj_handle.fsid = sub_handle->fsid; result->obj_handle.fileid = sub_handle->fileid; result->obj_handle.fs = fs; /* default handlers */ fsal_obj_handle_init(&result->obj_handle, &export->mfe_exp, sub_handle->type); /* mdcache handlers */ mdcache_handle_ops_init(&result->obj_handle.obj_ops); /* state */ if (sub_handle->type == DIRECTORY) result->obj_handle.state_hdl = &result->fsobj.fsdir.dhdl; else result->obj_handle.state_hdl = &result->fsobj.hdl; state_hdl_init(result->obj_handle.state_hdl, result->obj_handle.type, &result->obj_handle); /* Initialize common fields */ result->mde_flags = 0; glist_init(&result->export_list); atomic_store_int32_t(&result->first_export_id, -1); /* Map the export before we put this entry into the LRU, but after it's * well enough set up to be able to be unrefed by unexport should there * be a race. */ status = mdc_check_mapping(result); if (unlikely(FSAL_IS_ERROR(status))) { /* The current export is in process to be unexported, don't * create new mdcache entries. */ LogDebug(COMPONENT_CACHE_INODE, "Trying to allocate a new entry %p for export id %" PRIi16" that is in the process of being unexported", result, op_ctx->ctx_export->export_id); /* sub_handle will be freed by the caller */ result->sub_handle = NULL; mdcache_put(result); /* Handle is not yet in hash / LRU, so just put the sentinal * ref */ mdcache_put(result); return NULL; } mdcache_lru_insert(result); return result; } /** * * @brief Cleans up an entry so it can be reused * * @param[in] entry The cache entry to clean */ void mdc_clean_entry(mdcache_entry_t *entry) { struct glist_head *glist; struct glist_head *glistn; /* Must get attr_lock before mdc_exp_lock */ PTHREAD_RWLOCK_wrlock(&entry->attr_lock); glist_for_each_safe(glist, glistn, &entry->export_list) { struct entry_export_map *expmap; struct mdcache_fsal_export *export; expmap = glist_entry(glist, struct entry_export_map, export_per_entry); export = expmap->exp; PTHREAD_RWLOCK_wrlock(&export->mdc_exp_lock); mdc_remove_export_map(expmap); PTHREAD_RWLOCK_unlock(&export->mdc_exp_lock); } /* Clear out first_export */ atomic_store_int32_t(&entry->first_export_id, -1); PTHREAD_RWLOCK_unlock(&entry->attr_lock); if (entry->obj_handle.type == DIRECTORY) { PTHREAD_RWLOCK_wrlock(&entry->content_lock); /* Clean up dirents */ mdcache_dirent_invalidate_all(entry); /* Clean up parent key */ mdcache_free_fh(&entry->fsobj.fsdir.parent); PTHREAD_RWLOCK_unlock(&entry->content_lock); } cih_remove_checked(entry); } /** * * Check the active export mapping for this entry and update if necessary. * * If the entry does not have a mapping for the active export, add one. * * If an unexport is in progress, return ERR_FSAL_STALE to prevent the caller * from proceeding. * * @param[in] entry The cache inode * * @return FSAL Status * */ fsal_status_t mdc_check_mapping(mdcache_entry_t *entry) { struct mdcache_fsal_export *export = mdc_cur_export(); struct glist_head *glist; struct entry_export_map *expmap; bool try_write = false; if (atomic_fetch_uint8_t(&export->flags) & MDC_UNEXPORT) { /* In the process of unexporting, don't check export mapping. * Return a stale error. */ return fsalstat(ERR_FSAL_STALE, ESTALE); } /* Fast path check to see if this export is already mapped */ if (atomic_fetch_int32_t(&entry->first_export_id) == (int32_t) op_ctx->ctx_export->export_id) return fsalstat(ERR_FSAL_NO_ERROR, 0); PTHREAD_RWLOCK_rdlock(&entry->attr_lock); again: (void)atomic_inc_uint64_t(&cache_stp->inode_mapping); glist_for_each(glist, &entry->export_list) { expmap = glist_entry(glist, struct entry_export_map, export_per_entry); /* Found active export on list */ if (expmap->exp == export) { PTHREAD_RWLOCK_unlock(&entry->attr_lock); return fsalstat(ERR_FSAL_NO_ERROR, 0); } } if (!try_write) { /* Now take write lock and try again in * case another thread has raced with us. */ PTHREAD_RWLOCK_unlock(&entry->attr_lock); PTHREAD_RWLOCK_wrlock(&entry->attr_lock); try_write = true; goto again; } /* We have the write lock and did not find * this export on the list, add it. */ PTHREAD_RWLOCK_wrlock(&export->mdc_exp_lock); /* Check for unexport again, this prevents an interlock issue where * we passed above, but now unexport is in progress. This is required * because the various locks are acquired, dropped, and re-acquired * in such a way that unexport may have started after we made the * check at the top. */ if (atomic_fetch_uint8_t(&export->flags) & MDC_UNEXPORT) { /* In the process of unexporting, don't allow creating a new * export mapping. Return a stale error. */ PTHREAD_RWLOCK_unlock(&export->mdc_exp_lock); PTHREAD_RWLOCK_unlock(&entry->attr_lock); return fsalstat(ERR_FSAL_STALE, ESTALE); } expmap = gsh_calloc(1, sizeof(*expmap)); /* If export_list is empty, store this export as first */ if (glist_empty(&entry->export_list)) { atomic_store_int32_t(&entry->first_export_id, (int32_t) op_ctx->ctx_export->export_id); } expmap->exp = export; expmap->entry = entry; glist_add_tail(&entry->export_list, &expmap->export_per_entry); glist_add_tail(&export->entry_list, &expmap->entry_per_export); PTHREAD_RWLOCK_unlock(&export->mdc_exp_lock); PTHREAD_RWLOCK_unlock(&entry->attr_lock); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* entry's content_lock must be held in exclusive mode */ fsal_status_t mdc_get_parent_handle(struct mdcache_fsal_export *export, mdcache_entry_t *entry, struct fsal_obj_handle *sub_parent) { char buf[NFS4_FHSIZE]; struct gsh_buffdesc fh_desc = { buf, NFS4_FHSIZE }; fsal_status_t status; #ifdef DEBUG_MDCACHE assert(entry->content_lock.__data.__writer != 0); #endif /* Get a wire handle that can be used with create_handle() */ subcall_raw(export, status = sub_parent->obj_ops.handle_to_wire(sub_parent, FSAL_DIGEST_NFSV4, &fh_desc) ); if (FSAL_IS_ERROR(status)) return status; /* And store in the parent host-handle */ mdcache_copy_fh(&entry->fsobj.fsdir.parent, &fh_desc); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* entry's content_lock must be held in exclusive mode */ void mdc_get_parent(struct mdcache_fsal_export *export, mdcache_entry_t *entry) { struct fsal_obj_handle *sub_handle; fsal_status_t status; #ifdef DEBUG_MDCACHE assert(entry->content_lock.__data.__writer != 0); #endif if (entry->obj_handle.type != DIRECTORY) { /* Parent pointer only for directories */ return; } if (entry->fsobj.fsdir.parent.len != 0) { /* Already has a parent pointer */ return; } subcall_raw(export, status = entry->sub_handle->obj_ops.lookup( entry->sub_handle, "..", &sub_handle, NULL) ); if (FSAL_IS_ERROR(status)) { /* Top of filesystem */ return; } mdc_get_parent_handle(export, entry, sub_handle); /* Release parent handle */ subcall_raw(export, sub_handle->obj_ops.release(sub_handle) ); } /** * @brief Cleans all the dirents belonging to a directory chunk. * * @note The content lock MUST be held for write * * @param[in,out] chunk The chunk being cleaned. * */ void mdcache_clean_dirent_chunk(struct dir_chunk *chunk) { struct glist_head *glist, *glistn; struct mdcache_fsal_obj_handle *parent = chunk->parent; glist_for_each_safe(glist, glistn, &chunk->dirents) { mdcache_dir_entry_t *dirent; dirent = glist_entry(glist, mdcache_dir_entry_t, chunk_list); /* Remove from deleted or active names tree */ mdcache_avl_remove(parent, dirent); } /* Remove chunk from directory. */ glist_del(&chunk->chunks); /* At this point the following is true about the chunk: * * chunks is {NULL, NULL} do to the glist_del * dirents is {&dirents, &dirents}, i.e. empty as a result of the * glist_for_each_safe above * the other fields are untouched. */ /* This chunk is about to be freed or reused, clean up a few more * things. */ chunk->parent = NULL; chunk->prev_chunk = NULL; chunk->next_ck = 0; chunk->num_entries = 0; } /** * @brief Cleans all the dirent chunks belonging to a directory. * * @note The content lock MUST be held for write * * @param[in,out] emtry The directory being cleaned. * */ void mdcache_clean_dirent_chunks(mdcache_entry_t *entry) { struct glist_head *glist, *glistn; glist_for_each_safe(glist, glistn, &entry->fsobj.fsdir.chunks) { lru_remove_chunk(glist_entry(glist, struct dir_chunk, chunks)); } } /** * @brief Invalidates and releases all cached entries for a directory * * Invalidates all the entries for a cached directory. * * @note The content lock MUST be held for write * * @param[in,out] entry The directory to be managed * */ void mdcache_dirent_invalidate_all(mdcache_entry_t *entry) { LogFullDebug(COMPONENT_CACHE_INODE, "Invalidating directory for %p, clearing MDCACHE_DIR_POPULATED setting MDCACHE_TRUST_CONTENT and MDCACHE_TRUST_DIR_CHUNKS", entry); /* Clean the chunks first, that will clean most of the active * entries also. */ mdcache_clean_dirent_chunks(entry); /* Clean the active and deleted trees */ mdcache_avl_clean_trees(entry); atomic_clear_uint32_t_bits(&entry->mde_flags, MDCACHE_DIR_POPULATED); atomic_set_uint32_t_bits(&entry->mde_flags, MDCACHE_TRUST_CONTENT | MDCACHE_TRUST_DIR_CHUNKS); } /** * @brief Adds a new entry to the cache * * This function adds a new entry to the cache. It will allocate * entries of any kind. * * The caller is responsible for releasing attrs_in, however, the references * will have been transferred to the new mdcache entry. fsal_copy_attrs leaves * the state of the source attributes still safe to call fsal_release_attrs, * so all will be well. * * @param[in] export Export for this cache * @param[in] sub_handle Handle for sub-FSAL * @param[in] attrs_in Attributes provided for the object * @param[in,out] attrs_out Attributes requested for the object * @param[in] new_directory Indicate a new directory was created * @param[out] entry Newly instantiated cache entry * @param[in] state Optional state_t representing open file. * * @note This returns an INITIAL ref'd entry on success * * @return FSAL status */ fsal_status_t mdcache_new_entry(struct mdcache_fsal_export *export, struct fsal_obj_handle *sub_handle, struct attrlist *attrs_in, struct attrlist *attrs_out, bool new_directory, mdcache_entry_t **entry, struct state_t *state) { fsal_status_t status; mdcache_entry_t *oentry, *nentry = NULL; struct gsh_buffdesc fh_desc; cih_latch_t latch; bool has_hashkey = false; int rc = 0; mdcache_key_t key; *entry = NULL; /* Get FSAL-specific key */ subcall_raw(export, sub_handle->obj_ops.handle_to_key(sub_handle, &fh_desc) ); (void) cih_hash_key(&key, export->mfe_exp.sub_export->fsal, &fh_desc, CIH_HASH_KEY_PROTOTYPE); /* Check if the entry already exists. We allow the following race * because mdcache_lru_get has a slow path, and the latch is a * shared lock. */ status = mdcache_find_keyed(&key, entry); if (!FSAL_IS_ERROR(status)) { LogDebug(COMPONENT_CACHE_INODE, "Trying to add an already existing entry. Found entry %p type: %d, New type: %d", *entry, (*entry)->obj_handle.type, sub_handle->type); /* If it was unreachable before, mark it reachable */ atomic_clear_uint32_t_bits(&(*entry)->mde_flags, MDCACHE_UNREACHABLE); /* Don't need a new sub_handle ref */ goto out_no_new_entry_yet; } else if (status.major != ERR_FSAL_NOENT) { /* Real error , don't need a new sub_handle ref */ goto out_no_new_entry_yet; } /* !LATCHED */ /* We did not find the object. Pull an entry off the LRU. The entry * will already be mapped. */ nentry = mdcache_alloc_handle(export, sub_handle, sub_handle->fs); if (nentry == NULL) { /* We didn't get an entry because of unexport in progress, * go ahead and bail out now. */ status = fsalstat(ERR_FSAL_STALE, 0); goto out_no_new_entry_yet; } /* See if someone raced us. */ oentry = cih_get_by_key_latch(&key, &latch, CIH_GET_WLOCK, __func__, __LINE__); if (oentry) { /* Entry is already in the cache, do not add it. */ LogDebug(COMPONENT_CACHE_INODE, "lost race to add entry %p type: %d, New type: %d", oentry, oentry->obj_handle.type, sub_handle->type); *entry = oentry; /* Ref it */ status = mdcache_lru_ref(*entry, LRU_REQ_INITIAL); if (!FSAL_IS_ERROR(status)) { /* We used to return ERR_FSAL_EXIST but all callers * just converted that to ERR_FSAL_NO_ERROR, so * leave the status alone. */ (void)atomic_inc_uint64_t(&cache_stp->inode_conf); } /* It it was unreachable before, mark it reachable */ atomic_clear_uint32_t_bits(&(*entry)->mde_flags, MDCACHE_UNREACHABLE); /* Release the subtree hash table lock */ cih_hash_release(&latch); goto out_release_new_entry; } /* We won the race. */ /* Set cache key */ has_hashkey = cih_hash_key(&nentry->fh_hk.key, export->mfe_exp.sub_export->fsal, &fh_desc, CIH_HASH_NONE); if (!has_hashkey) { cih_hash_release(&latch); LogCrit(COMPONENT_CACHE_INODE, "Could not hash new entry"); status = fsalstat(ERR_FSAL_NOMEM, 0); goto out_release_new_entry; } switch (nentry->obj_handle.type) { case REGULAR_FILE: LogDebug(COMPONENT_CACHE_INODE, "Adding a REGULAR_FILE, entry=%p", nentry); /* Init statistics used for intelligently granting delegations*/ init_deleg_heuristics(&nentry->obj_handle); break; case DIRECTORY: LogDebug(COMPONENT_CACHE_INODE, "Adding a DIRECTORY, entry=%p setting MDCACHE_TRUST_CONTENT %s", nentry, new_directory ? "setting MDCACHE_DIR_POPULATED" : "clearing MDCACHE_DIR_POPULATED"); atomic_set_uint32_t_bits(&nentry->mde_flags, MDCACHE_TRUST_CONTENT); /* If the directory is newly created, it is empty. Because we know its content, we consider it read. */ if (new_directory) { atomic_set_uint32_t_bits(&nentry->mde_flags, MDCACHE_DIR_POPULATED); } else { atomic_clear_uint32_t_bits(&nentry->mde_flags, MDCACHE_DIR_POPULATED); } /* init avl tree */ mdcache_avl_init(nentry); /* init chunk list and detached dirents list */ glist_init(&nentry->fsobj.fsdir.chunks); glist_init(&nentry->fsobj.fsdir.detached); (void) pthread_spin_init(&nentry->fsobj.fsdir.spin, PTHREAD_PROCESS_PRIVATE); break; case SYMBOLIC_LINK: case SOCKET_FILE: case FIFO_FILE: case BLOCK_FILE: case CHARACTER_FILE: LogDebug(COMPONENT_CACHE_INODE, "Adding a special file of type %d entry=%p", nentry->obj_handle.type, nentry); break; default: /* Should never happen */ cih_hash_release(&latch); status = fsalstat(ERR_FSAL_INVAL, 0); LogMajor(COMPONENT_CACHE_INODE, "unknown type %u provided", nentry->obj_handle.type); goto out_release_new_entry; } /* nentry not reachable yet; no need to lock */ /* Copy over the attributes and pass off the ACL reference. We also * copy the output attrs at this point to avoid needing the attr_lock. */ if (attrs_out != NULL) fsal_copy_attrs(attrs_out, attrs_in, false); /* Use the attrs_in request_mask because it will know if ACL was * requested or not (anyone calling mdcache_new_entry will have * requested all supported attributes including ACL). */ nentry->attrs.request_mask = attrs_in->request_mask; fsal_copy_attrs(&nentry->attrs, attrs_in, true); if (nentry->attrs.expire_time_attr == 0) { nentry->attrs.expire_time_attr = atomic_fetch_uint32_t( &op_ctx->ctx_export->expire_time_attr); } /* Validate the attributes we just set. */ mdc_fixup_md(nentry, &nentry->attrs); /* Hash and insert entry, after this would need attr_lock to * access attributes. */ rc = cih_set_latched(nentry, &latch, op_ctx->fsal_export->fsal, &fh_desc, CIH_SET_UNLOCK | CIH_SET_HASHED); if (unlikely(rc)) { LogCrit(COMPONENT_CACHE_INODE, "entry could not be added to hash, rc=%d", rc); status = fsalstat(ERR_FSAL_NOMEM, 0); if (attrs_out != NULL) { /* Release the attrs we just copied. */ fsal_release_attrs(attrs_out); } goto out_release_new_entry; } if (isFullDebug(COMPONENT_CACHE_INODE)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str }; (void) display_mdcache_key(&dspbuf, &nentry->fh_hk.key); LogFullDebug(COMPONENT_CACHE_INODE, "New entry %p added with fh_hk.key %s", nentry, str); } else { LogDebug(COMPONENT_CACHE_INODE, "New entry %p added", nentry); } *entry = nentry; (void)atomic_inc_uint64_t(&cache_stp->inode_added); return fsalstat(ERR_FSAL_NO_ERROR, 0); out_release_new_entry: /* We raced or failed, release the new entry we acquired, this will * result in inline deconstruction. This will release the attributes, we * may not have copied yet, in which case mask and acl are 0/NULL. This * entry is not yet in the hash or LRU, so just put it's sentinal ref. */ nentry->sub_handle = NULL; mdcache_put(nentry); mdcache_put(nentry); out_no_new_entry_yet: /* If attributes were requested, fetch them now if we still have a * success return since we did not actually create a new object and * use the provided attributes (we can't trust that the provided * attributes are newer). * * NOTE: There can not be an ABBA lock ordering issue since our caller * does not hold a lock on the "new" entry. */ if (!FSAL_IS_ERROR(status) && attrs_out != NULL) { status = get_optional_attrs(&(*entry)->obj_handle, attrs_out); if (FSAL_IS_ERROR(status)) { /* Oops, failed to get attributes and ATTR_RDATTR_ERR * was not requested, so we are failing and thus must * drop the object reference we got. */ mdcache_put(*entry); *entry = NULL; } } if (!FSAL_IS_ERROR(status)) { /* Give the FSAL a chance to merge new_obj into * oentry->obj_handle since we will be using * oentry->obj_handle for all access to the oject. */ struct fsal_obj_handle *old_sub_handle = (*entry)->sub_handle; status = old_sub_handle->obj_ops.merge(old_sub_handle, sub_handle); if (FSAL_IS_ERROR(status)) { /* Report this error and unref the entry */ LogDebug(COMPONENT_CACHE_INODE, "Merge of object handles after race returned %s", fsal_err_txt(status)); mdcache_put(*entry); *entry = NULL; } } if (FSAL_IS_ERROR(status) && state != NULL) { /* Our caller passed in a state for an open file, since * there is not a valid entry to use, or a merge failed * we must close that file before disposing of new_obj. */ fsal_status_t cstatus = sub_handle->obj_ops.close2(sub_handle, state); LogDebug(COMPONENT_CACHE_INODE, "Close of state during error processing returned %s", fsal_err_txt(cstatus)); } /* must free sub_handle if no new entry was created to reference it. */ sub_handle->obj_ops.release(sub_handle); return status; } int display_mdcache_key(struct display_buffer *dspbuf, mdcache_key_t *key) { int b_left = display_printf(dspbuf, "hk=%"PRIx64" fsal=%p key=", key->hk, key->fsal); if (b_left <= 0) return b_left; return display_opaque_bytes(dspbuf, key->kv.addr, key->kv.len); } /** * @brief Find a cache entry by it's key * * Lookup a cache entry by key. If it is not in the cache, it is not returned. * * @param[in] key Cache key to use for lookup * @param[out] entry Entry, if found * * @note This returns an INITIAL ref'd entry on success * * @return Status */ fsal_status_t mdcache_find_keyed(mdcache_key_t *key, mdcache_entry_t **entry) { cih_latch_t latch; if (key->kv.addr == NULL) { LogDebug(COMPONENT_CACHE_INODE, "Attempt to use NULL key"); return fsalstat(ERR_FSAL_INVAL, 0); } if (isFullDebug(COMPONENT_CACHE_INODE)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = { sizeof(str), str, str }; (void) display_mdcache_key(&dspbuf, key); LogFullDebug(COMPONENT_CACHE_INODE, "Looking for %s", str); } *entry = cih_get_by_key_latch(key, &latch, CIH_GET_RLOCK | CIH_GET_UNLOCK_ON_MISS, __func__, __LINE__); if (likely(*entry)) { fsal_status_t status; /* Initial Ref on entry */ status = mdcache_lru_ref(*entry, LRU_REQ_INITIAL); /* Release the subtree hash table lock */ cih_hash_release(&latch); if (FSAL_IS_ERROR(status)) { /* Return error instead of entry */ LogFullDebug(COMPONENT_CACHE_INODE, "Found entry %p, but could not ref error %s", entry, fsal_err_txt(status)); *entry = NULL; return status; } status = mdc_check_mapping(*entry); if (unlikely(FSAL_IS_ERROR(status))) { /* Export is in the process of being removed, don't * add this entry to the export, and bail out of the * operation sooner than later. */ mdcache_put(*entry); *entry = NULL; return status; } LogFullDebug(COMPONENT_CACHE_INODE, "Found entry %p", entry); (void)atomic_inc_uint64_t(&cache_stp->inode_hit); return fsalstat(ERR_FSAL_NO_ERROR, 0); } return fsalstat(ERR_FSAL_NOENT, 0); } /** * @brief Find or create a cache entry by it's host-handle * * Locate a cache entry by host-handle. If it is not in the cache, an attempt * will be made to create it and insert it in the cache. * * @param[in] key Cache key to use for lookup * @param[in] export Export for this cache * @param[out] entry Entry, if found * @param[in,out] attrs_out Optional attributes for newly created object * * @note This returns an INITIAL ref'd entry on success * * @return Status */ fsal_status_t mdcache_locate_host(struct gsh_buffdesc *fh_desc, struct mdcache_fsal_export *export, mdcache_entry_t **entry, struct attrlist *attrs_out) { struct fsal_export *sub_export = export->mfe_exp.sub_export; mdcache_key_t key; struct fsal_obj_handle *sub_handle; struct attrlist attrs; fsal_status_t status; /* Copy the fh_desc into key, todo: is there a function for this? */ /* We want to save fh_desc */ key.kv.len = fh_desc->len; key.kv.addr = alloca(key.kv.len); memcpy(key.kv.addr, fh_desc->addr, key.kv.len); subcall_raw(export, status = sub_export->exp_ops.host_to_key(sub_export, &key.kv) ); if (FSAL_IS_ERROR(status)) return status; (void)cih_hash_key(&key, sub_export->fsal, &key.kv, CIH_HASH_KEY_PROTOTYPE); status = mdcache_find_keyed(&key, entry); if (!FSAL_IS_ERROR(status)) { status = get_optional_attrs(&(*entry)->obj_handle, attrs_out); return status; } else if (status.major != ERR_FSAL_NOENT) { /* Actual error */ return status; } /* Ask for all supported attributes except ACL (we defer fetching ACL * until asked for it (including a permission check). */ fsal_prepare_attrs(&attrs, op_ctx->fsal_export->exp_ops.fs_supported_attrs( op_ctx->fsal_export) & ~ATTR_ACL); sub_export = export->mfe_exp.sub_export; subcall_raw(export, status = sub_export->exp_ops.create_handle(sub_export, fh_desc, &sub_handle, &attrs) ); if (unlikely(FSAL_IS_ERROR(status))) { LogDebug(COMPONENT_CACHE_INODE, "create_handle failed with %s", fsal_err_txt(status)); *entry = NULL; fsal_release_attrs(&attrs); return status; } status = mdcache_new_entry(export, sub_handle, &attrs, attrs_out, false, entry, NULL); fsal_release_attrs(&attrs); if (!FSAL_IS_ERROR(status)) { LogFullDebug(COMPONENT_CACHE_INODE, "create_handle Created entry %p FSAL %s", (*entry), (*entry)->sub_handle->fsal->name); } return status; } /** * @brief Create a new entry and add it to the parent's cache * * A new entry for @a sub_handle is created, and it is added to the dirent cache * of @a mdc_parent. * * @note mdc_parent MUST have it's content_lock held for writing * * @note Currently this function is only used when caching entire directories. * * @param[in] mdc_parent Parent entry * @param[in] name Name of new entry * @param[in] sub_handle Handle from sub-FSAL for new entry * @param[in] attrs_in Attributes for new entry * * @return FSAL status (ERR_FSAL_OVERFLOW if dircache full) */ fsal_status_t mdc_add_cache(mdcache_entry_t *mdc_parent, const char *name, struct fsal_obj_handle *sub_handle, struct attrlist *attrs_in) { struct mdcache_fsal_export *export = mdc_cur_export(); fsal_status_t status; mdcache_entry_t *new_entry = NULL; bool invalidate = false; #ifdef DEBUG_MDCACHE assert(mdc_parent->content_lock.__data.__writer != 0); #endif LogFullDebug(COMPONENT_CACHE_INODE, "Creating entry for %s", name); status = mdcache_new_entry(export, sub_handle, attrs_in, NULL, false, &new_entry, NULL); if (FSAL_IS_ERROR(status)) return status; #ifdef USE_LTTNG tracepoint(mdcache, mdc_readdir_populate, __func__, __LINE__, new_entry, new_entry->lru.refcnt); #endif LogFullDebug(COMPONENT_CACHE_INODE, "Created entry %p FSAL %s for %s", new_entry, new_entry->sub_handle->fsal->name, name); if (avltree_size(&mdc_parent->fsobj.fsdir.avl.t) > mdcache_param.dir.avl_max) { LogFullDebug(COMPONENT_CACHE_INODE, "Parent %p at max", mdc_parent); mdcache_put(new_entry); return fsalstat(ERR_FSAL_OVERFLOW, 0); } /* Entry was found in the FSAL, add this entry to the parent directory */ status = mdcache_dirent_add(mdc_parent, name, new_entry, &invalidate); if (status.major == ERR_FSAL_EXIST) status = fsalstat(ERR_FSAL_NO_ERROR, 0); if (!FSAL_IS_ERROR(status) && new_entry->obj_handle.type == DIRECTORY) { /* Insert Parent's key */ PTHREAD_RWLOCK_wrlock(&new_entry->content_lock); mdc_dir_add_parent(new_entry, mdc_parent); PTHREAD_RWLOCK_unlock(&new_entry->content_lock); } mdcache_put(new_entry); return status; } /** * @brief Try to get a cached child * * Get the cached entry child of @a mdc_parent If the cached entry cannot be * found, for whatever reason, return ERR_FSAL_STALE * * @note Caller MUST hold the content_lock for read * * @param[in] mdc_parent Parent directory * @param[in] name Name of child * @param[out] entry Child entry, on success * * @note This returns an INITIAL ref'd entry on success * @return FSAL status */ fsal_status_t mdc_try_get_cached(mdcache_entry_t *mdc_parent, const char *name, mdcache_entry_t **entry) { mdcache_dir_entry_t *dirent = NULL; fsal_status_t status = {0, 0}; LogFullDebug(COMPONENT_CACHE_INODE, "Look in cache %s, trust content %s", name, test_mde_flags(mdc_parent, MDCACHE_TRUST_CONTENT) ? "yes" : "no"); #ifdef DEBUG_MDCACHE assert(mdc_parent->content_lock.__data.__nr_readers || mdc_parent->content_lock.__data.__writer); #endif *entry = NULL; /* If parent isn't caching, return stale */ if (test_mde_flags(mdc_parent, MDCACHE_BYPASS_DIRCACHE)) return fsalstat(ERR_FSAL_STALE, 0); /* If the dirent cache is untrustworthy, don't even ask it */ if (!test_mde_flags(mdc_parent, MDCACHE_TRUST_CONTENT)) return fsalstat(ERR_FSAL_STALE, 0); dirent = mdcache_avl_qp_lookup_s(mdc_parent, name, 1); if (dirent) { if (dirent->chunk != NULL) { /* Bump the chunk in the LRU */ lru_bump_chunk(dirent->chunk); } else { /* Bump the detached dirent. */ bump_detached_dirent(mdc_parent, dirent); } status = mdcache_find_keyed(&dirent->ckey, entry); if (!FSAL_IS_ERROR(status)) return status; LogFullDebug(COMPONENT_CACHE_INODE, "mdcache_find_keyed %s failed %s", name, fsal_err_txt(status)); } else { /* ! dirent */ LogFullDebug(COMPONENT_CACHE_INODE, "mdcache_avl_qp_lookup_s %s failed trust negative %s", name, trust_negative_cache(mdc_parent) ? "yes" : "no"); if (trust_negative_cache(mdc_parent)) { /* If the dirent cache is both fully populated and * valid, it can serve negative lookups. */ return fsalstat(ERR_FSAL_NOENT, 0); } } return fsalstat(ERR_FSAL_STALE, 0); } /** * @brief Lookup a name (helper) * * Lookup a name relative to another object. If @a uncached is true and a cache * miss occurs, then the underlying file is looked up and added to the cache, if * it exists. * * The caller will set the request_mask in attrs_out to indicate the attributes * of interest. ATTR_ACL SHOULD NOT be requested and need not be provided. If * not all the requested attributes can be provided, this method MUST return * an error unless the ATTR_RDATTR_ERR bit was set in the request_mask. * * Since this method instantiates a new fsal_obj_handle, it will be forced * to fetch at least some attributes in order to even know what the object * type is (as well as it's fileid and fsid). For this reason, the operation * as a whole can be expected to fail if the attributes were not able to be * fetched. * * @param[in] parent Handle of container * @param[in] name Name to look up * @param[in] uncached If true, do an uncached lookup on cache failure * @param[out] handle Handle of found object, on success * @param[in,out] attrs_out Optional attributes for newly created object * * @note This returns an INITIAL ref'd entry on success * @return FSAL status */ fsal_status_t mdc_lookup(mdcache_entry_t *mdc_parent, const char *name, bool uncached, mdcache_entry_t **new_entry, struct attrlist *attrs_out) { *new_entry = NULL; fsal_status_t status; LogFullDebug(COMPONENT_CACHE_INODE, "Lookup %s", name); PTHREAD_RWLOCK_rdlock(&mdc_parent->content_lock); /* ".." doesn't end up in the cache */ if (!strcmp(name, "..")) { struct mdcache_fsal_export *export = mdc_cur_export(); struct gsh_buffdesc tmpfh; LogFullDebug(COMPONENT_CACHE_INODE, "Lookup parent (..) of %p", mdc_parent); if (mdc_parent->fsobj.fsdir.parent.len == 0) { /* we need write lock */ PTHREAD_RWLOCK_unlock(&mdc_parent->content_lock); PTHREAD_RWLOCK_wrlock(&mdc_parent->content_lock); mdc_get_parent(export, mdc_parent); } /* We need to drop the content lock around the locate, as that * will try to take the attribute lock on the parent to refresh * it's attributes, which can cause an ABBA with lookup/readdir. * Copy the parent filehandle, so we can drop the lock. */ mdcache_copy_fh(&tmpfh, &mdc_parent->fsobj.fsdir.parent); PTHREAD_RWLOCK_unlock(&mdc_parent->content_lock); status = mdcache_locate_host(&tmpfh, export, new_entry, attrs_out); mdcache_free_fh(&tmpfh); if (status.major == ERR_FSAL_STALE) status.major = ERR_FSAL_NOENT; return status; } if (test_mde_flags(mdc_parent, MDCACHE_BYPASS_DIRCACHE)) { /* Parent isn't caching dirents; call directly. * NOTE: Technically we will call mdc_lookup_uncached not * holding the content_lock write as required, however * since we are operating uncached here, ultimately there * will be no addition to the dirent cache, and thus no * need to hold the write lock. */ goto uncached; } /* We first try avltree_lookup by name. If that fails, we dispatch to * the FSAL. */ status = mdc_try_get_cached(mdc_parent, name, new_entry); if (status.major == ERR_FSAL_STALE) { /* Get a write lock and try again */ PTHREAD_RWLOCK_unlock(&mdc_parent->content_lock); LogFullDebug(COMPONENT_CACHE_INODE, "Try again %s", name); PTHREAD_RWLOCK_wrlock(&mdc_parent->content_lock); status = mdc_try_get_cached(mdc_parent, name, new_entry); } if (!FSAL_IS_ERROR(status)) { /* Success! Now fetch attr if requested, drop content_lock * to avoid ABBA locking situation. */ PTHREAD_RWLOCK_unlock(&mdc_parent->content_lock); LogFullDebug(COMPONENT_CACHE_INODE, "Found, possible getattrs %s (%s)", name, attrs_out != NULL ? "yes" : "no"); status = get_optional_attrs(&(*new_entry)->obj_handle, attrs_out); if (FSAL_IS_ERROR(status)) { /* Oops, failed to get attributes and ATTR_RDATTR_ERR * was not requested, so we are failing lookup and * thus must drop the object reference we got. */ mdcache_put(*new_entry); *new_entry = NULL; } return status; } else if (!uncached) { /* Was only looking in cache, so don't bother looking further */ goto out; } else if (status.major != ERR_FSAL_STALE) { /* Actual failure */ LogDebug(COMPONENT_CACHE_INODE, "Lookup %s failed %s", name, fsal_err_txt(status)); goto out; } /* Need to look up. */ if (!test_mde_flags(mdc_parent, MDCACHE_TRUST_CONTENT)) { /* We have the write lock and the content is * still invalid. Empty it out and mark it * valid in preparation for caching the result of this lookup. */ mdcache_dirent_invalidate_all(mdc_parent); } LogDebug(COMPONENT_CACHE_INODE, "Cache Miss detected for %s", name); uncached: status = mdc_lookup_uncached(mdc_parent, name, new_entry, attrs_out); out: PTHREAD_RWLOCK_unlock(&mdc_parent->content_lock); if (status.major == ERR_FSAL_STALE) status.major = ERR_FSAL_NOENT; return status; } /** * @brief Lookup an uncached entry from the sub-FSAL * * The entry has already been determined to not be cached, and the parent is * already write-locked. Lookup the child and create a cached entry for it. * * @note mdc_parent MUST have it's content_lock held for writing * * @param[in] mdc_parent Parent entry * @param[in] name Name of entry to find * @param[out] new_entry New entry to return; * @param[in,out] attrs_out Optional attributes for entry * * @note This returns an INITIAL ref'd entry on success * * @return FSAL status */ fsal_status_t mdc_lookup_uncached(mdcache_entry_t *mdc_parent, const char *name, mdcache_entry_t **new_entry, struct attrlist *attrs_out) { struct fsal_obj_handle *sub_handle = NULL, *new_obj = NULL; fsal_status_t status; struct mdcache_fsal_export *export = mdc_cur_export(); struct attrlist attrs; bool invalidate = false; /* Ask for all supported attributes except ACL (we defer fetching ACL * until asked for it (including a permission check). */ fsal_prepare_attrs(&attrs, op_ctx->fsal_export->exp_ops.fs_supported_attrs( op_ctx->fsal_export) & ~ATTR_ACL); subcall( status = mdc_parent->sub_handle->obj_ops.lookup( mdc_parent->sub_handle, name, &sub_handle, &attrs) ); if (unlikely(FSAL_IS_ERROR(status))) { LogDebug(COMPONENT_CACHE_INODE, "lookup %s failed with %s", name, fsal_err_txt(status)); *new_entry = NULL; fsal_release_attrs(&attrs); return status; } /* We are only called to fill cache, we should not need to invalidate * parents attributes (or dirents if chunked). * * NOTE: This does mean that a pure lookup of a file that had been added * external to this Ganesha instance could cause us to not dump * the dirent cache, however, that should still result in an * attribute change which should dump the cache. */ status = mdcache_alloc_and_check_handle(export, sub_handle, &new_obj, false, &attrs, attrs_out, "lookup ", mdc_parent, name, &invalidate, NULL); fsal_release_attrs(&attrs); if (FSAL_IS_ERROR(status)) { *new_entry = NULL; } else { *new_entry = container_of(new_obj, mdcache_entry_t, obj_handle); } return status; } /** * @brief Lock two directories in order * * This function gets the locks on both entries. If src and dest are * the same, it takes only one lock. Locks are acquired with lowest * cache_entry first to avoid deadlocks. * * @param[in] src Source directory to lock * @param[in] dest Destination directory to lock */ void mdcache_src_dest_lock(mdcache_entry_t *src, mdcache_entry_t *dest) { int rc; /* * A problem found in this order * 1. mdcache_readdir holds A's content_lock, and tries to * grab B's attr_lock. * 2. mdcache_remove holds B's attr_lock, and tries to grab B's * content_lock * 3. mdcache_rename holds B's content_lock, and tries to grab the * A's content_lock (which is held by thread 1). * This change is to avoid this deadlock. */ retry_lock: if (src == dest) PTHREAD_RWLOCK_wrlock(&src->content_lock); else if (src < dest) { PTHREAD_RWLOCK_wrlock(&src->content_lock); rc = pthread_rwlock_trywrlock(&dest->content_lock); if (rc) { LogDebug(COMPONENT_CACHE_INODE, "retry dest %p lock, src %p", dest, src); PTHREAD_RWLOCK_unlock(&src->content_lock); sleep(1); goto retry_lock; } } else { PTHREAD_RWLOCK_wrlock(&dest->content_lock); rc = pthread_rwlock_trywrlock(&src->content_lock); if (rc) { LogDebug(COMPONENT_CACHE_INODE, "retry src %p lock, dest %p", src, dest); PTHREAD_RWLOCK_unlock(&dest->content_lock); sleep(1); goto retry_lock; } } } /** * @brief Unlock two directories in order * * This function releases the locks on both entries. If src and dest * are the same, it releases the lock and returns. Locks are released * with lowest cache_entry first. * * @param[in] src Source directory to lock * @param[in] dest Destination directory to lock */ void mdcache_src_dest_unlock(mdcache_entry_t *src, mdcache_entry_t *dest) { if (src == dest) PTHREAD_RWLOCK_unlock(&src->content_lock); else if (src < dest) { PTHREAD_RWLOCK_unlock(&dest->content_lock); PTHREAD_RWLOCK_unlock(&src->content_lock); } else { PTHREAD_RWLOCK_unlock(&src->content_lock); PTHREAD_RWLOCK_unlock(&dest->content_lock); } } /** * @brief Find a cached directory entry * * Look up the entry in the cache. Success is if found (obviously), or if the * cache isn't trusted. NOENT is only retrned if both not found and trusted. * * Note that only if we are not chunking will we return ERR_FSAL_NOENT. * * @note Caller MUST hold the content_lock for read * * @param[in] dir Directory to search * @param[in] name Name to find * @param[in] direntp Directory entry, if found * @return FSAL status */ fsal_status_t mdcache_dirent_find(mdcache_entry_t *dir, const char *name, mdcache_dir_entry_t **direntp) { mdcache_dir_entry_t *dirent; LogFullDebug(COMPONENT_CACHE_INODE, "Find dir entry %s", name); *direntp = NULL; /* Sanity check */ if (dir->obj_handle.type != DIRECTORY) return fsalstat(ERR_FSAL_NOTDIR, 0); /* If no active entry, do nothing */ if (avltree_size(&dir->fsobj.fsdir.avl.t) == 0) { if (mdc_dircache_trusted(dir)) return fsalstat(ERR_FSAL_NOENT, 0); else return fsalstat(ERR_FSAL_NO_ERROR, 0); } dirent = mdcache_avl_qp_lookup_s(dir, name, 1); if (!dirent) { if (mdc_dircache_trusted(dir)) return fsalstat(ERR_FSAL_NOENT, 0); return fsalstat(ERR_FSAL_NO_ERROR, 0); } *direntp = dirent; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * * @brief Adds a directory entry to a cached directory. * * This function adds a new directory entry to a directory. Directory * entries have only weak references, so they do not prevent recycling * or freeing the entry they locate. This function may be called * either once (for handling creation) or iteratively in directory * population. * * @note Caller MUST hold the content_lock for write * * @param[in,out] parent Cache entry of the directory being updated * @param[in] name The name to add to the entry * @param[in] entry The cache entry associated with name * @param[in,out] invalidate Invalidate the parent directory contents if * adding to a chunk fails, if adding to a chunk * succeeds, invalidate will be reset to false * and the caller MUST refresh the attributes * without invalidating the dirent cache. * * @return FSAL status */ fsal_status_t mdcache_dirent_add(mdcache_entry_t *parent, const char *name, mdcache_entry_t *entry, bool *invalidate) { mdcache_dir_entry_t *new_dir_entry, *allocated_dir_entry; size_t namesize = strlen(name) + 1; int code = 0; LogFullDebug(COMPONENT_CACHE_INODE, "Add dir entry %s", name); #ifdef DEBUG_MDCACHE assert(parent->content_lock.__data.__writer != 0); #endif /* Sanity check */ if (parent->obj_handle.type != DIRECTORY) return fsalstat(ERR_FSAL_NOTDIR, 0); /* Don't cache if parent is not being cached */ if (test_mde_flags(parent, MDCACHE_BYPASS_DIRCACHE)) return fsalstat(ERR_FSAL_NO_ERROR, 0); /* in cache avl, we always insert on pentry_parent */ new_dir_entry = gsh_calloc(1, sizeof(mdcache_dir_entry_t) + namesize); new_dir_entry->flags = DIR_ENTRY_FLAG_NONE; allocated_dir_entry = new_dir_entry; memcpy(&new_dir_entry->name, name, namesize); mdcache_key_dup(&new_dir_entry->ckey, &entry->fh_hk.key); /* add to avl */ code = mdcache_avl_qp_insert(parent, &new_dir_entry); if (code < 0) { /* Technically only a -2 is a name collision, however, we will * treat a hash collision (which per current code we should * never actually see) the same. */ LogDebug(COMPONENT_CACHE_INODE, "Returning EEXIST for %s code %d", name, code); return fsalstat(ERR_FSAL_EXIST, 0); } /* we're going to succeed */ if (new_dir_entry == allocated_dir_entry && mdcache_param.dir.avl_chunk > 0) { /* Place new dirent into a chunk or as detached. */ place_new_dirent(parent, new_dir_entry); /* Since we are chunking, we can preserve the dirent cache for * the purposes of lookups even if we could not add the new * dirent to a chunk, so we don't want to invalidate the parent * directory. */ *invalidate = false; } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Remove a cached directory entry * * @note Caller MUST hold the content_lock for write * * @param[in] parent Parent directory * @param[in] name Name to remove * @return FSAL status */ fsal_status_t mdcache_dirent_remove(mdcache_entry_t *parent, const char *name) { mdcache_dir_entry_t *dirent; fsal_status_t status; #ifdef DEBUG_MDCACHE assert(parent->content_lock.__data.__writer != 0); #endif /* Don't remove if parent is not being cached */ if (test_mde_flags(parent, MDCACHE_BYPASS_DIRCACHE)) return fsalstat(ERR_FSAL_NO_ERROR, 0); LogFullDebug(COMPONENT_CACHE_INODE, "Remove dir entry %s", name); status = mdcache_dirent_find(parent, name, &dirent); if (FSAL_IS_ERROR(status)) { if (status.major == ERR_FSAL_NOENT) /* Wasn't there */ return fsalstat(ERR_FSAL_NO_ERROR, 0); return status; } else if (!dirent) return status; avl_dirent_set_deleted(parent, dirent); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Rename a cached directory entry * * @note Caller MUST hold the content_lock for write * * @param[in] parent Parent directory * @param[in] oldname Current name of dirent * @param[in] newname New name for dirent * @return FSAL status */ fsal_status_t mdcache_dirent_rename(mdcache_entry_t *parent, const char *oldname, const char *newname) { mdcache_dir_entry_t *dirent, *dirent2; fsal_status_t status; int code = 0; LogFullDebug(COMPONENT_CACHE_INODE, "Rename dir entry %s to %s", oldname, newname); #ifdef DEBUG_MDCACHE assert(parent->content_lock.__data.__writer != 0); #endif /* Don't rename if parent is not being cached */ if (test_mde_flags(parent, MDCACHE_BYPASS_DIRCACHE)) return fsalstat(ERR_FSAL_NO_ERROR, 0); /* Don't rename if chunking. */ if (mdcache_param.dir.avl_chunk > 0) { /* Dump the dirent cache for this directory. */ mdcache_dirent_invalidate_all(parent); return fsalstat(ERR_FSAL_NO_ERROR, 0); } status = mdcache_dirent_find(parent, oldname, &dirent); /* If not chunking, and the directory was fully populated, and we did * not find the entry, we will return error, and the caller will * invalidate the directory. With chunking there can be no failure. */ if (FSAL_IS_ERROR(status)) return status; if (!dirent) return status; status = mdcache_dirent_find(parent, newname, &dirent2); if (FSAL_IS_ERROR(status) && status.major != ERR_FSAL_NOENT) return status; if (dirent2) { /* rename would cause a collision */ if (test_mde_flags(parent, MDCACHE_TRUST_CONTENT)) { /* overwrite, replace entry and expire the old */ mdcache_entry_t *oldentry; (void)mdcache_find_keyed(&dirent2->ckey, &oldentry); /* dirent2 (newname) will now point to renamed entry */ mdcache_key_delete(&dirent2->ckey); mdcache_key_dup(&dirent2->ckey, &dirent->ckey); /* Delete dirent for oldname */ avl_dirent_set_deleted(parent, dirent); if (oldentry) { /* if it is still around, mark it gone/stale */ LogFullDebug(COMPONENT_CACHE_INODE, "Entry %p Clearing MDCACHE_TRUST_ATTRS, MDCACHE_TRUST_CONTENT, MDCACHE_DIR_POPULATED", oldentry); atomic_clear_uint32_t_bits( &oldentry->mde_flags, MDCACHE_TRUST_ATTRS | MDCACHE_TRUST_CONTENT | MDCACHE_DIR_POPULATED); mdcache_put(oldentry); } return status; } else { LogDebug(COMPONENT_CACHE_INODE, "Returning EEXIST for %s", newname); return fsalstat(ERR_FSAL_EXIST, 0); } } /* Size (including terminating NULL) of the filename */ size_t newnamesize = strlen(newname) + 1; /* try to rename--no longer in-place */ dirent2 = gsh_calloc(1, sizeof(mdcache_dir_entry_t) + newnamesize); memcpy(dirent2->name, newname, newnamesize); dirent2->flags = DIR_ENTRY_FLAG_NONE; mdcache_key_dup(&dirent2->ckey, &dirent->ckey); /* Delete the entry for oldname */ avl_dirent_set_deleted(parent, dirent); /* Insert the entry for newname */ code = mdcache_avl_qp_insert(parent, &dirent2); /* We should not be able to have a name collision. */ assert(code != -2); if (code < 0) { /* We had a hash collision (impossible for all practical * purposes). Just abandon... */ /* dirent2 was never inserted */ return fsalstat(ERR_FSAL_SERVERFAULT, 0); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief State to be passed to FSAL readdir callbacks */ struct mdcache_populate_cb_state { struct mdcache_fsal_export *export; mdcache_entry_t *dir; fsal_status_t *status; fsal_readdir_cb cb; void *dir_state; /** dirent to be filled in when whence_is_name */ mdcache_dir_entry_t **dirent; /** Cookie is what we are actually searching for */ fsal_cookie_t cookie; /** Indicates if FSAL expects whence to be a name. */ bool whence_is_name; /** If whence_is_name, indicate if we are looking for caller's cookie. */ bool whence_search; }; /** * @brief Handle a readdir callback on uncache dir * * Cache a sindle object, passing it up the stack to the caller. This is for * handling readdir on a directory that is not being cached, for example because * is is too big. Dirents are not created by this callback, just objects. * * @param[in] name Name of the directory entry * @param[in] sub_handle Object for entry * @param[in] attrs Attributes requested for the object * @param[in,out] dir_state Callback state * @param[in] cookie Directory cookie * * @returns fsal_dir_result */ static enum fsal_dir_result mdc_readdir_uncached_cb(const char *name, struct fsal_obj_handle *sub_handle, struct attrlist *attrs, void *dir_state, fsal_cookie_t cookie) { struct mdcache_populate_cb_state *state = dir_state; fsal_status_t status = { 0, 0 }; mdcache_entry_t *directory = container_of(&state->dir->obj_handle, mdcache_entry_t, obj_handle); mdcache_entry_t *new_entry = NULL; enum fsal_dir_result rv; /* This is in the middle of a subcall. Do a supercall */ supercall_raw(state->export, status = mdcache_new_entry(state->export, sub_handle, attrs, NULL, false, &new_entry, NULL) ); if (FSAL_IS_ERROR(status)) { *state->status = status; if (status.major == ERR_FSAL_XDEV) { LogInfoAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Ignoring XDEV entry %s", name); *state->status = fsalstat(ERR_FSAL_NO_ERROR, 0); return DIR_CONTINUE; } LogInfoAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Lookup failed on %s in dir %p with %s", name, directory, fsal_err_txt(*state->status)); return DIR_TERMINATE; } /* Call up the stack. Do a supercall */ supercall_raw(state->export, rv = state->cb(name, &new_entry->obj_handle, attrs, state->dir_state, cookie) ); return rv; } /** * Perform an uncached readdir * * Large directories do not have their dirents cached. This performs readdir on * such directories, by passing the sub-FSAL's results back up through the * stack. * * @note The object passed into the callback is ref'd and must be unref'd by the * callback. * * @param[in] directory the directory to read * @param[in] whence where to start (next) * @param[in] dir_state pass thru of state to callback * @param[in] cb callback function * @param[in] attrmask Which attributes to fill * @param[out] eod_met eod marker true == end of dir * * @return FSAL status */ fsal_status_t mdcache_readdir_uncached(mdcache_entry_t *directory, fsal_cookie_t *whence, void *dir_state, fsal_readdir_cb cb, attrmask_t attrmask, bool *eod_met) { fsal_status_t status = {0, 0}; fsal_status_t readdir_status = {0, 0}; struct mdcache_populate_cb_state state; state.export = mdc_cur_export(); state.dir = directory; state.status = &status; state.cb = cb; state.dir_state = dir_state; subcall( readdir_status = directory->sub_handle->obj_ops.readdir( directory->sub_handle, whence, &state, mdc_readdir_uncached_cb, attrmask, eod_met) ); if (FSAL_IS_ERROR(readdir_status)) return readdir_status; return status; } /** * @brief Place a new dirent from create, lookup, or rename into a chunk if * possible, otherwise place as a detached dirent. * * If addition is not possible because the entry does not belong to an * active dirent chunk, nothing happens. The dirent may still be inserted * into the by name lookup as a detached dirent. * * @note If we can't insert the dirent into a chunk because we can't figure * out which chunk it belongs to, we can still trust the chunks, the new dirent * is not within their range, and if inserted between two non-adjacent chunks, * a subsequent readdir that enumerates that part of the directory will pick up * the new dirent since it will have to populate at least one new chunk in the * gap. * * @note parent_dir MUST have it's content_lock held for writing * * @param[in] parent_dir The directory this dir entry is part of * @param[in] new_dir_entry The dirent to add. * */ void place_new_dirent(mdcache_entry_t *parent_dir, mdcache_dir_entry_t *new_dir_entry) { mdcache_dir_entry_t *left; mdcache_dir_entry_t *right; struct avltree_node *node, *parent, *unbalanced, *other; int is_left, code; fsal_cookie_t ck, nck; struct dir_chunk *chunk; bool invalidate_chunks = true; #ifdef DEBUG_MDCACHE assert(parent_dir->content_lock.__data.__writer != 0); #endif subcall( ck = parent_dir->sub_handle->obj_ops.compute_readdir_cookie( parent_dir->sub_handle, new_dir_entry->name) ); if (ck == 0) { /* FSAL does not support computing readdir cookie, so we can't * add this entry to a chunk, nor can we trust the chunks. */ LogFullDebug(COMPONENT_CACHE_INODE, "Could not add %s to chunk for directory %p, compute_readdir_cookie failed", new_dir_entry->name, parent_dir); goto out; } new_dir_entry->ck = ck; node = avltree_do_lookup(&new_dir_entry->node_sorted, &parent_dir->fsobj.fsdir.avl.sorted, &parent, &unbalanced, &is_left, avl_dirent_sorted_cmpf); if (isFullDebug(COMPONENT_CACHE_INODE)) { if (node) { right = avltree_container_of(node, mdcache_dir_entry_t, node_sorted); } LogFullDebug(COMPONENT_CACHE_INODE, "avltree_do_lookup returned node=%p (name=%s, ck=%" PRIx64") parent=%p unbalanced=%p is_left=%s", node, node ? right->name : "", node ? right->ck : 0, parent, unbalanced, is_left ? "true" : "false"); } if (node) { right = avltree_container_of(node, mdcache_dir_entry_t, node_sorted); if (ck == FIRST_COOKIE && right->ck == FIRST_COOKIE) { /* Special case of inserting a new first entry. * We should only have to do this for FSALs that * sort dirents by cookie value that support * compute_readdir_cookie and are unable to actually * compute the cookie for the very first directory * entry. */ subcall( nck = parent_dir->sub_handle ->obj_ops.compute_readdir_cookie( parent_dir->sub_handle, right->name) ); if (nck == 0) { /* Oops, could not compute new cookie... * We can no longer trust the chunks. */ LogCrit(COMPONENT_CACHE_INODE, "Could not compute new cookie for %s in directory %p", right->name, parent_dir); goto out; } /* Just change up the old first entries cookie, which * will leave room to insert the new entry with cookie * of FIRST_COOKIE. */ right->ck = nck; } else { /* This should not happen... Let's no longer trust the * chunks. */ LogCrit(COMPONENT_CACHE_INODE, "Could not add %s to chunk for directory %p, node %s found withck=%" PRIx64, new_dir_entry->name, parent_dir, right->name, right->ck); goto out; } } if (parent == NULL) { /* The tree must be empty, there are no chunks to add this * entry to. There are no chunks to trust... */ LogFullDebug(COMPONENT_CACHE_INODE, "Could not add %s to chunk for directory %p, tree was empty", new_dir_entry->name, parent_dir); goto out; } if (is_left) { /* Parent will be to the right of the key. */ right = avltree_container_of(parent, mdcache_dir_entry_t, node_sorted); other = avltree_prev(parent); if (other) { left = avltree_container_of(other, mdcache_dir_entry_t, node_sorted); LogFullDebug(COMPONENT_CACHE_INODE, "%s is between %s and parent %s", new_dir_entry->name, left->name, right->name); } else { left = NULL; if (parent_dir->fsobj.fsdir.first_ck == right->ck) { /* The right node is the first entry in the * directory. Add this key to the beginning of * the first chunk and fixup the chunk. */ LogFullDebug(COMPONENT_CACHE_INODE, "Adding %s as new first entry", new_dir_entry->name); } else { /* The right entry is not the first entry in * the directory, so the key is a dirent * somewhere before the first chunked dirent. * we can't insert this key into a chunk, * however, we can still trust the chunks since * the new entry is part of the directory we * don't have cached, a readdir that wants that * part of the directory will populate a new * chunk. */ LogFullDebug(COMPONENT_CACHE_INODE, "Could not add %s to chunk for directory %p, somewhere before first chunk", new_dir_entry->name, parent_dir); invalidate_chunks = false; goto out; } } } else { /* Parent will be to the left of the key. */ left = avltree_container_of(parent, mdcache_dir_entry_t, node_sorted); other = avltree_next(parent); if (other) { right = avltree_container_of(other, mdcache_dir_entry_t, node_sorted); LogFullDebug(COMPONENT_CACHE_INODE, "%s is between parent %s and %s", new_dir_entry->name, left->name, right->name); } else { right = NULL; if (left->eod) { /* The right node is the last entry in the * directory. Add this key to the end of the * last chunk and fixup the chunk. */ LogFullDebug(COMPONENT_CACHE_INODE, "Adding %s as new last entry", new_dir_entry->name); } else { /* The left entry is not the last entry in * the directory, so the key is a dirent * somewhere after the last chunked dirent. * we can't insert this key into a chunk, * however, we can still trust the chunks since * the new entry is part of the directory we * don't have cached, a readdir that wants that * part of the directory will populate a new * chunk. */ LogFullDebug(COMPONENT_CACHE_INODE, "Could not add %s to chunk for directory %p, somewhere after last chunk", new_dir_entry->name, parent_dir); invalidate_chunks = false; goto out; } } } /* Note in the following, every dirent that is in the sorted tree MUST * be in a chunk, so we don't check for chunk != NULL. */ if (left != NULL && right != NULL && left->chunk != right->chunk && left->chunk != right->chunk->prev_chunk) { /* left and right are in different non-adjacent chunks, however, * we can still trust the chunks since the new entry is part of * the directory we don't have cached, a readdir that wants that * part of the directory will populate a new chunk. */ invalidate_chunks = false; goto out; } /* Set up to add to chunk and by cookie AVL tree. */ if (right == NULL) { /* Will go at end of left chunk. */ chunk = new_dir_entry->chunk = left->chunk; } else { /* Will go at begin of right chunk. */ chunk = new_dir_entry->chunk = right->chunk; } code = mdcache_avl_insert_ck(parent_dir, new_dir_entry); if (code < 0) { /* We failed to insert into FSAL cookie AVL tree, will fail. * Nothing to clean up since we haven't done anything * unreversible, and we no longer trust the chunks. */ goto out; } /* Get the node into the actual tree... */ avltree_do_insert(&new_dir_entry->node_sorted, &parent_dir->fsobj.fsdir.avl.sorted, parent, unbalanced, is_left); LogFullDebug(COMPONENT_CACHE_INODE, "Inserted %s into sorted tree left=%p right=%p", new_dir_entry->name, new_dir_entry->node_sorted.left, new_dir_entry->node_sorted.right); LogFullDebug(COMPONENT_CACHE_INODE, "Adding %s to chunk %p between %s and %s for directory %p", new_dir_entry->name, right ? right->chunk : left->chunk, left ? left->name : "BEGIN", right ? right->name : "END", parent_dir); /* And now add it to the chunk */ if (right == NULL) { /* Insert node at END of the chunk represented by left. */ glist_add_tail(&left->chunk->dirents, &new_dir_entry->chunk_list); /* Make the new entry the eod entry. */ new_dir_entry->eod = true; left->eod = false; } else { /* Insert to left of right, which if left and right are * different chunks, inserts into the right hand chunk. * * NOTE: This looks weird, normally we pass the list head to * glist_add_tail, but glist_add_tail really just * inserts the entry before the first parameter, recall * that the list head is just a member of the list... * * If left == NULL, then the "list node" to the left of * right is the actual list head, and this all works out... */ glist_add_tail(&right->chunk_list, &new_dir_entry->chunk_list); if (left != NULL) { /* Fixup left chunk's next cookie */ left->chunk->next_ck = new_dir_entry->ck; } else { /* New first entry in directory */ LogFullDebug(COMPONENT_CACHE_INODE, "Setting directory first_ck=%"PRIx64, new_dir_entry->ck); parent_dir->fsobj.fsdir.first_ck = new_dir_entry->ck; } } /* And now increment the number of entries in the chunk. */ chunk->num_entries++; /* And bump the chunk in the LRU */ lru_bump_chunk(chunk); if (chunk->num_entries == mdcache_param.dir.avl_chunk_split) { /* Create a new chunk */ struct dir_chunk *split; struct glist_head *glist; mdcache_dir_entry_t *here = NULL; int i = 0; uint32_t split_count = mdcache_param.dir.avl_chunk_split / 2; split = mdcache_get_chunk(parent_dir); split->prev_chunk = chunk; split->next_ck = chunk->next_ck; glist_add_tail(&chunk->parent->fsobj.fsdir.chunks, &split->chunks); /* Make sure this chunk is in the MRU of L1 */ lru_bump_chunk(split); /* Scan the list to find what will be the first dirent in the * new split chunk. */ glist_for_each(glist, &chunk->dirents) { if (++i > (split_count)) { /* Got past the halfway point. */ here = glist_entry(glist, mdcache_dir_entry_t, chunk_list); break; } } assert(here != NULL); LogFullDebug(COMPONENT_CACHE_INODE, "Splitting chunk %p for directory %p at %s", chunk, parent_dir, here->name); /* Split chunk->dirents into split->dirents at here */ glist_split(&chunk->dirents, &split->dirents, glist); chunk->num_entries = split_count; split->num_entries = split_count; /* Fill in the first chunk's next_ck to be the cookie of the * first dirent in the new split chunk. */ chunk->next_ck = here->ck; } new_dir_entry->flags |= DIR_ENTRY_SORTED; invalidate_chunks = false; out: if (invalidate_chunks) { /* Indicate we not longer trust the chunk cache. */ LogFullDebug(COMPONENT_CACHE_INODE, "Entry %p clearing MDCACHE_DIR_POPULATED, MDCACHE_TRUST_DIR_CHUNKS", parent_dir); atomic_clear_uint32_t_bits(&parent_dir->mde_flags, MDCACHE_DIR_POPULATED | MDCACHE_TRUST_DIR_CHUNKS); } if (new_dir_entry->chunk == NULL) { /* This is a detached directory entry, add it to the LRU list of * detached directory entries. This is the one and only place a * detached dirent can be added. */ add_detached_dirent(parent_dir, new_dir_entry); } } /** * @brief Handle adding an element to a dirent chunk * * Cache a sindle object, and add it to the directory chunk in progress. * * @param[in] name Name of the directory entry * @param[in] sub_handle Object for entry * @param[in] attrs Attributes requested for the object * @param[in,out] dir_state Callback state * @param[in] cookie Directory cookie * * @returns fsal_dir_result */ static enum fsal_dir_result mdc_readdir_chunk_object(const char *name, struct fsal_obj_handle *sub_handle, struct attrlist *attrs_in, void *dir_state, fsal_cookie_t cookie) { struct mdcache_populate_cb_state *state = dir_state; struct dir_chunk *chunk = state->dir_state; mdcache_entry_t *mdc_parent = container_of(&state->dir->obj_handle, mdcache_entry_t, obj_handle); struct mdcache_fsal_export *export = mdc_cur_export(); mdcache_entry_t *new_entry = NULL; mdcache_dir_entry_t *new_dir_entry = NULL, *allocated_dir_entry = NULL; size_t namesize = strlen(name) + 1; int code = 0; fsal_status_t status; enum fsal_dir_result result = DIR_CONTINUE; if (chunk->num_entries == mdcache_param.dir.avl_chunk) { /* We are being called readahead. */ struct dir_chunk *new_chunk; LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Readdir readahead first entry in new chunk %s", name); /* Now add the previous chunk to the list of chunks for the * directory. */ glist_add_tail(&chunk->parent->fsobj.fsdir.chunks, &chunk->chunks); /* Now start a new chunk. */ new_chunk = mdcache_get_chunk(chunk->parent); /* Setup new chunk. */ new_chunk->prev_chunk = chunk; /* And switch over to new chunk. */ state->dir_state = new_chunk; chunk = new_chunk; /* And start accepting entries into the new chunk. */ } LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Creating cache entry for %s cookie=0x%"PRIx64 " sub_handle=0x%p", name, cookie, sub_handle); status = mdcache_new_entry(export, sub_handle, attrs_in, NULL, false, &new_entry, NULL); if (FSAL_IS_ERROR(status)) { *state->status = status; LogInfoAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "mdcache_new_entry failed on %s in dir %p with %s", name, mdc_parent, fsal_err_txt(status)); return DIR_TERMINATE; } /* Entry was found in the FSAL, add this entry to the parent directory */ LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Add mdcache entry %p for %s for FSAL %s", new_entry, name, new_entry->sub_handle->fsal->name); /* in cache avl, we always insert on mdc_parent */ new_dir_entry = gsh_calloc(1, sizeof(mdcache_dir_entry_t) + namesize); new_dir_entry->flags = DIR_ENTRY_FLAG_NONE; new_dir_entry->chunk = chunk; new_dir_entry->ck = cookie; allocated_dir_entry = new_dir_entry; /** @todo FSF - we could eventually try and support duplicated FSAL * cookies assuming they come sequentially (which they * would from EXT4 as far as I can tell from the EXT4 * code). We could never start a chunk with a duplicate * so we would have to put all of them into the same * chunk, posssibly making the chunk larger than normal. */ memcpy(&new_dir_entry->name, name, namesize); mdcache_key_dup(&new_dir_entry->ckey, &new_entry->fh_hk.key); /* add to avl */ code = mdcache_avl_qp_insert(mdc_parent, &new_dir_entry); if (code < 0) { /* We can get here with the following possibilities: * * - FSAL cookie collision, nothing we can do about this, but * also really should never happen. * - Name collision, something is broken and the FSAL has * given us multiple directory entries with the same name * but for different objects. Again, not much we can do. * - Degenerate name hash collision, we have tried many many * times to find a workable hash for the name and failed. * Due to the number of retries we should never get here. * * In any case, we will just ignore this entry. */ /* Technically only a -2 is a name collision, however, we will * treat a hash collision (which per current code we should * never actually see) the same. */ mdcache_put(new_entry); /* Check for return code -3 and/or -4. * -3: This indicates the file name is duplicate but FSAL * cookie is different. This may happen in case lots of new * entries got added to the directory while running readdir. * -4: This indicates that it is FSAL cookie duplication / * collision. This could happen due to fast mutating directory. * In both cases already cached contents are stale/invalid. * Need to invalidate the cache and inform client to re-read * the directory. */ if (code == -3 || code == -4) { atomic_clear_uint32_t_bits(&state->dir->mde_flags, MDCACHE_TRUST_CONTENT); state->status->major = ERR_FSAL_DELAY; state->status->minor = 0; return DIR_TERMINATE; } LogCrit(COMPONENT_CACHE_INODE, "Collision while adding dirent for %s", name); return DIR_CONTINUE; } /* Note that if this dirent was already in the lookup by name AVL * tree (mdc_parent->fsobj.fsdir.avl.t), then mdcache_avl_qp_insert * freed the dirent we allocated above, and returned the one that was * in tree. It will have set chunk, ck, and nk. * * The existing dirent might or might not be part of a chunk already. */ if (new_dir_entry != allocated_dir_entry) { LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Swapped using %p instead of %p, new_dir_entry->chunk=%p", new_dir_entry, allocated_dir_entry, new_dir_entry->chunk); } assert(new_dir_entry->chunk); if (state->whence_search && new_dir_entry->ck == state->cookie) { /* We have found the dirent the caller is looking for. */ LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Found dirent %s caller is looking for cookie = %" PRIx64, name, state->cookie); *(state->dirent) = new_dir_entry; } if (op_ctx->fsal_export->exp_ops.fs_supports( op_ctx->fsal_export, fso_compute_readdir_cookie)) { struct avltree_node *node; node = avltree_inline_insert( &new_dir_entry->node_sorted, &mdc_parent->fsobj.fsdir.avl.sorted, avl_dirent_sorted_cmpf); if (node != NULL) { if (node == &new_dir_entry->node_sorted) { LogDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "New entry %s was already in sorted tree", name); } else if (isDebug(COMPONENT_CACHE_INODE)) { mdcache_dir_entry_t *other; other = avltree_container_of( node, mdcache_dir_entry_t, node_sorted); LogDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "New entry %s collided with entry %s already in sorted tree", name, other->name); } } else { LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Inserted %s into sorted tree left=%p right=%p", name, new_dir_entry->node_sorted.left, new_dir_entry->node_sorted.right); new_dir_entry->flags |= DIR_ENTRY_SORTED; } } /* Add this dirent to the chunk if not already added. */ if (glist_null(&new_dir_entry->chunk_list)) { /* If this dirent is not already on a chunk_list, then we add * it. It could be the allocated_dir_entry or it could be an * old dirent that was not part of a chunk, but it is NOT the * same dirent that was already part of some other chunk. */ glist_add_tail(&chunk->dirents, &new_dir_entry->chunk_list); if (chunk->num_entries == 0 && chunk->prev_chunk != NULL) { /* Link the first dirent in a new chunk to the previous * chunk so linkage across chunks works. * * This could be linking readahead chunks, or we * could have had to read another chunk to satisfy * readdir request, in which case prev_chunk had been * passed into mdcache_populate_dir_chunk. */ chunk->prev_chunk->next_ck = cookie; } chunk->num_entries++; } if (new_dir_entry->chunk != chunk) { /* We have the situation where we have collided with a * previously used chunk (and thus we have a partial chunk). * Since dirent is pointing to the existing dirent and the one * we allocated above has been freed we don't need to do any * cleanup. * * Don't allow readahead in this case just indicate this * directory is terminated. */ result = DIR_TERMINATE; /* Since the chunk we were working on collides with a previously * used chunk, we should link our chunk into that other chunk. */ chunk->next_ck = cookie; } else if (chunk->num_entries == mdcache_param.dir.avl_chunk) { /* Chunk is full. Since dirent is pointing to the existing * dirent and the one we allocated above has been freed we don't * need to do any cleanup. * * Allow readahead. * * If there's actually any readahead, chunk->next_ck will get * filled in. */ result = DIR_READAHEAD; } if (new_entry->obj_handle.type == DIRECTORY) { /* Insert Parent's key */ PTHREAD_RWLOCK_wrlock(&new_entry->content_lock); mdc_dir_add_parent(new_entry, mdc_parent); PTHREAD_RWLOCK_unlock(&new_entry->content_lock); } LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "About to put entry %p refcnt=%"PRIi32, new_entry, atomic_fetch_int32_t(&new_entry->lru.refcnt)); mdcache_put(new_entry); return result; } /** * @brief Handle a readdir callback for a chunked directory. * * This is a supercall wrapper around the function above that actually does * the work. * * @param[in] name Name of the directory entry * @param[in] sub_handle Object for entry * @param[in] attrs Attributes requested for the object * @param[in,out] dir_state Callback state * @param[in] cookie Directory cookie * * @returns fsal_dir_result */ static enum fsal_dir_result mdc_readdir_chunked_cb(const char *name, struct fsal_obj_handle *sub_handle, struct attrlist *attrs, void *dir_state, fsal_cookie_t cookie) { struct mdcache_populate_cb_state *state = dir_state; enum fsal_dir_result result; /* This is in the middle of a subcall. Do a supercall */ supercall_raw(state->export, result = mdc_readdir_chunk_object(name, sub_handle, attrs, dir_state, cookie) ); return result; } /** * @brief Skip directory chunks while re-filling dirent cache in search of * a specific cookie that is not in cache. * * @param[in] directory The directory being read * @param[in] next_ck The next cookie to find the next chunk * * @returns The chunk found, or NULL. */ static struct dir_chunk *mdcache_skip_chunks(mdcache_entry_t *directory, fsal_cookie_t next_ck) { mdcache_dir_entry_t *dirent = NULL; struct dir_chunk *chunk = NULL; /* We need to skip chunks that are already cached. */ while (next_ck != 0 && mdcache_avl_lookup_ck(directory, next_ck, &dirent)) { chunk = dirent->chunk; next_ck = chunk->next_ck; } /* At this point, we have the last cached chunk before a gap. */ return chunk; } /** * @brief Read the next chunk of a directory * * If called for an FSAL that only supports whence as the dirent name to * continue from, and prev_chunk is NULL, we must scan the directory from the * beginning. If prev_chunk is not NULL, we can scan the directory starting with * the last dirent name in prev_chunk, but we must still scan the directory * until we find whence. * * @param[in] directory The directory to read * @param[in] whence Where to start (next) * @param[in,out] dirent The first dirent of the chunk * @param[in] prev_chunk The previous chunk populated * @param[in,out] eod_met The end of directory has been hit. * * @return FSAL status */ fsal_status_t mdcache_populate_dir_chunk(mdcache_entry_t *directory, fsal_cookie_t whence, mdcache_dir_entry_t **dirent, struct dir_chunk *prev_chunk, bool *eod_met) { fsal_status_t status = {0, 0}; fsal_status_t readdir_status = {0, 0}; struct mdcache_populate_cb_state state; struct dir_chunk *first_chunk = mdcache_get_chunk(directory); struct dir_chunk *chunk = first_chunk; attrmask_t attrmask; fsal_cookie_t *whence_ptr = &whence; attrmask = op_ctx->fsal_export->exp_ops.fs_supported_attrs( op_ctx->fsal_export) | ATTR_RDATTR_ERR; state.export = mdc_cur_export(); state.dir = directory; state.status = &status; state.cb = NULL; /* We don't use the call back during chunking. */ state.dir_state = chunk; /* Pass the chunk to the callback */ state.cookie = whence; state.dirent = dirent; state.whence_is_name = op_ctx->fsal_export->exp_ops.fs_supports( op_ctx->fsal_export, fso_whence_is_name); state.whence_search = state.whence_is_name && whence != 0 && prev_chunk == NULL; if (state.whence_is_name) { LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "whence_is_name %s cookie %" PRIx64, state.whence_search ? "search" : "no search", state.cookie); } again: /* In whence_is_name case, we may need to do another FSAL readdir * call to continue scanning for the desired cookie, so we will jump * back to here to accomplish that. chunk is newly allocated and * prev_chunk has been updated to point to the last cached chunk. */ chunk->prev_chunk = prev_chunk; if (state.whence_is_name) { if (prev_chunk != NULL) { /* Start from end of prev_chunk */ /* If end of directory, mark last dirent as eod. */ mdcache_dir_entry_t *last; last = glist_last_entry(&prev_chunk->dirents, mdcache_dir_entry_t, chunk_list); whence_ptr = (fsal_cookie_t *)last->name; if (state.whence_search) { LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Calling FSAL readdir whence = %s, search %" PRIx64, last->name, state.cookie); } else { LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Calling FSAL readdir whence = %s, no search", last->name); } } else { /* Signal start from beginning by passing NULL pointer. */ whence_ptr = NULL; if (state.whence_search) { LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Calling FSAL readdir whence = NULL, search %" PRIx64, state.cookie); } else { LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Calling FSAL readdir whence = NULL, no search"); } } } else { LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Calling FSAL readdir whence = %"PRIx64, whence); } subcall( readdir_status = directory->sub_handle->obj_ops.readdir( directory->sub_handle, whence_ptr, &state, mdc_readdir_chunked_cb, attrmask, eod_met) ); if (FSAL_IS_ERROR(readdir_status)) { LogDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "FSAL readdir status=%s", fsal_err_txt(readdir_status)); *dirent = NULL; lru_remove_chunk(chunk); return readdir_status; } if (FSAL_IS_ERROR(status)) { LogDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "status=%s", fsal_err_txt(status)); *dirent = NULL; lru_remove_chunk(chunk); return status; } /* Recover the most recent chunk from dir_state, if we had readahead. * it might have changed. */ chunk = state.dir_state; if (chunk->num_entries == 0) { /* Save the previous chunk in case we need it. */ struct dir_chunk *prev_chunk = chunk->prev_chunk; mdcache_dir_entry_t *last; /* Chunk is empty - should only happen for an empty directory * but could happen if the FSAL failed to indicate end of * directory. This COULD happen on a readahead chunk, but it * would be unusual. */ LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Empty chunk"); lru_remove_chunk(chunk); if (prev_chunk != NULL) { /* We need to mark the end of directory */ last = glist_last_entry(&prev_chunk->dirents, mdcache_dir_entry_t, chunk_list); last->eod = true; LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Setting last dirent %p %s of chunk %p as eod", last, last->name, prev_chunk); } if (chunk == first_chunk) { /* We really got nothing on this readdir, so don't * return a dirent. */ *dirent = NULL; LogDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "status=%s", fsal_err_txt(status)); return status; } /* If whence_is_name, and we actually have this odd condition, * we need the previous chunk. */ chunk = prev_chunk; } else { /* Retain this chunk and if end of directory, mark last * dirent of current chunk as eod. */ if (*eod_met) { /* If end of directory, mark last dirent as eod. */ mdcache_dir_entry_t *last; last = glist_last_entry(&chunk->dirents, mdcache_dir_entry_t, chunk_list); last->eod = true; } LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Chunk first entry %s%s", *dirent != NULL ? (*dirent)->name : "", *eod_met ? " EOD" : ""); /* Now add this chunk to the list of chunks for the directory. */ glist_add_tail(&directory->fsobj.fsdir.chunks, &chunk->chunks); } if (state.whence_search && *dirent == NULL) { if (*eod_met) { /* Did not find cookie. */ status = fsalstat(ERR_FSAL_BADCOOKIE, 0); LogDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Could not find search cookie status=%s", fsal_err_txt(status)); return status; } /* We are re-scanning directory, and we have not found our * cookie yet, we either used up the FSAL's readdir (with any * readahead) or we collided with an already cached chunk, * which we know DOES NOT have our cookie (because otherwise we * would have found it on lookup), so we will start from where * we left off. * * chunk points to the last valid chunk of what we just read, * but we also have to check if we must skip chunks that had * already been in cache. * * If chunk->next_ck is 0, then we didn't collide, so there are * no chunks to skip. */ LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Rescan dir to find cookie needs to continue search for %" PRIx64, state.cookie); if (chunk->next_ck != 0) { /* In the collision case, chunk->next_ck was set, * so now start skipping. */ LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Search skipping from cookie %"PRIx64, chunk->next_ck); chunk = mdcache_skip_chunks(directory, chunk->next_ck); } /* We need to start a new FSAL readdir call, but we don't just * want to call mdcache_populate_dir_chunk raw, so set up a few * things and jump to again... */ /* The chunk we just dealt with is now prev_chunk. */ prev_chunk = chunk; LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "About to allocate a new chunk to continue search, prev chunk = %p", prev_chunk); /* And we need to allocate a fresh chunk. */ chunk = mdcache_get_chunk(directory); /* And switch over to new chunk. */ state.dir_state = chunk; /* And go start a new FSAL readdir call. * chunk->prev_chunk will get set from prev_chunk. */ goto again; } if (*dirent == NULL) { /* We haven't set dirent yet, return the first entry of the * first chunk. */ *dirent = glist_first_entry(&first_chunk->dirents, mdcache_dir_entry_t, chunk_list); } LogDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "status=%s", fsal_err_txt(status)); return status; } /** * @brief Read the contents of a directory * * If necessary, populate dirent cache chunks from the underlying FSAL. Then, * walk the dirent cache chunks calling the callback. * * @note The object passed into the callback is ref'd and must be unref'd by the * callback. * * @param[in] directory The directory to read * @param[in] whence Where to start (next) * @param[in] dir_state Pass thru of state to callback * @param[in] cb Callback function * @param[in] attrmask Which attributes to fill * @param[out] eod_met eod marker true == end of dir * * @return FSAL status */ fsal_status_t mdcache_readdir_chunked(mdcache_entry_t *directory, fsal_cookie_t whence, void *dir_state, fsal_readdir_cb cb, attrmask_t attrmask, bool *eod_met) { mdcache_dir_entry_t *dirent = NULL; bool has_write, set_first_ck; fsal_cookie_t next_ck = whence, look_ck = whence; struct dir_chunk *chunk = NULL; bool first_pass = true; bool eod = false; LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Starting chunked READDIR for %p, MDCACHE_TRUST_CONTENT %s, MDCACHE_TRUST_DIR_CHUNKS %s", directory, test_mde_flags(directory, MDCACHE_TRUST_CONTENT) ? "true" : "false", test_mde_flags(directory, MDCACHE_TRUST_DIR_CHUNKS) ? "true" : "false"); /* Dirent's are being chunked; check to see if it needs updating */ if (!test_mde_flags(directory, MDCACHE_TRUST_CONTENT | MDCACHE_TRUST_DIR_CHUNKS)) { /* Clean out the existing entries in the directory. */ LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Flushing invalid dirent cache"); PTHREAD_RWLOCK_wrlock(&directory->content_lock); mdcache_dirent_invalidate_all(directory); has_write = true; } else { PTHREAD_RWLOCK_rdlock(&directory->content_lock); has_write = false; } if (look_ck == 0) { /* If starting from beginning, use the first_ck from the * directory instead, this is only non-zero if the first * chunk of the directory is still present. */ look_ck = directory->fsobj.fsdir.first_ck; } /* We need to know if we need to set first_ck. */ set_first_ck = whence == 0 && look_ck == 0; again: /* Get here on first pass, retry if we don't hold the write lock, * and repeated passes if we need to fetch another chunk. */ LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Readdir chunked next_ck=0x%"PRIx64" look_ck=%"PRIx64, next_ck, look_ck); if (look_ck == 0 || !mdcache_avl_lookup_ck(directory, look_ck, &dirent)) { fsal_status_t status; /* This starting position isn't in our cache... * Go populate the cache and process from there. */ if (!has_write) { /* Upgrade to write lock and retry just in case * another thread managed to populate this cookie * in the meantime. */ PTHREAD_RWLOCK_unlock(&directory->content_lock); PTHREAD_RWLOCK_wrlock(&directory->content_lock); has_write = true; goto again; } /* Assure that dirent is NULL */ dirent = NULL; if (op_ctx->fsal_export->exp_ops.fs_supports( op_ctx->fsal_export, fso_whence_is_name) && first_pass && directory->fsobj.fsdir.first_ck != 0) { /* If whence must be the directory entry name we wish * to continue from, we need to start at the beginning * of the directory and readdir until we find the * caller's cookie, but we have the beginning of the * directory cached, so skip any chunks cached from * the start. * * Since the chunk we pass to * mdcache_populate_dir_chunk is the previous chunk * that function will use the chunk we resolved to * fetch the dirent name to continue from. * * If we DID NOT HAVE at least the first chunk cached, * mdcache_populate_dir_chunk MUST start from the * beginning, this is signaled by the fact that * prev_chunk will be NULL. * * In any case, whence will be the cookie we are looking * for. */ LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Search skipping initial chunks to find cookie"); chunk = mdcache_skip_chunks( directory, directory->fsobj.fsdir.first_ck); /* Since first_ck was not 0, we MUST have found at least * one chunk... */ assert(chunk != NULL); } LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Readdir chunked about to populate chunk %p next_ck=0x%" PRIx64, chunk, next_ck); /* No, we need to populate a chunk using this cookie. * * NOTE: empty directory can result in dirent being NULL, and * we will ALWAYS re-read an empty directory every time. * Although we do end up setting MDCACHE_DIR_POPULATED on * an empty directory, we don't consider that here, and * will re-read the directory. */ status = mdcache_populate_dir_chunk(directory, next_ck, &dirent, chunk, &eod); if (FSAL_IS_ERROR(status)) { PTHREAD_RWLOCK_unlock(&directory->content_lock); LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "mdcache_populate_dir_chunk failed status=%s", fsal_err_txt(status)); if (status.major == ERR_FSAL_STALE) mdcache_kill_entry(directory); return status; } if (dirent == NULL) { /* We must have reached the end of the directory, or the * directory was empty. In any case, there is no next * chunk or dirent. */ *eod_met = true; if (whence == 0) { /* Since eod is true and whence is 0, we know * the entire directory is populated. This can * indicate that an empty directory may be * considered "populated." */ atomic_set_uint32_t_bits(&directory->mde_flags, MDCACHE_DIR_POPULATED); } PTHREAD_RWLOCK_unlock(&directory->content_lock); LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "readdir completed, eod = %s", *eod_met ? "true" : "false"); return status; } if ((whence == 0) && eod) { /* We started at the beginning of the directory and * populated through to end of directory, thus we can * indicate the directory is fully populated. */ atomic_set_uint32_t_bits(&directory->mde_flags, MDCACHE_DIR_POPULATED); } else { /* Since we just populated a chunk and have not * determined that we read the entire directory, make * sure the MDCACHE_DIR_POPULATED is cleared. */ atomic_clear_uint32_t_bits(&directory->mde_flags, MDCACHE_DIR_POPULATED); } chunk = dirent->chunk; LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "mdcache_populate_dir_chunk finsihed chunk %p dirent %p %s", chunk, dirent, dirent->name); if (set_first_ck) { /* We just populated the first dirent in the directory, * save it's cookie as first_ck. */ LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Setting directory first_ck=%"PRIx64, dirent->ck); directory->fsobj.fsdir.first_ck = dirent->ck; set_first_ck = false; } } else { /* We found the dirent... If next_ck is NOT whence, we SHOULD * have found the first dirent in the chunk, if not, then * something went wrong at some point. That chunk is valid, */ chunk = dirent->chunk; LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "found dirent in cached chunk %p dirent %p %s", chunk, dirent, dirent->name); } /* Bump the chunk in the LRU */ lru_bump_chunk(chunk); LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "About to read directory=%p cookie=%" PRIx64, directory, next_ck); /* Now satisfy the request from the cached readdir--stop when either * the requested sequence or dirent sequence is exhausted */ for (; dirent != NULL; dirent = glist_next_entry(&chunk->dirents, mdcache_dir_entry_t, chunk_list, &dirent->chunk_list)) { fsal_status_t status; enum fsal_dir_result cb_result; mdcache_entry_t *entry = NULL; struct attrlist attrs; if (dirent->ck == whence) { /* When called with whence, the caller always wants the * next entry, skip this entry. */ continue; } if (dirent->flags & DIR_ENTRY_FLAG_DELETED) { /* Skip deleted entries */ continue; } /* Get actual entry using the dirent ckey */ status = mdcache_find_keyed(&dirent->ckey, &entry); if (FSAL_IS_ERROR(status)) { /* Failed using ckey, do full lookup. */ LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Lookup by key for %s failed, lookup by name now", dirent->name); /* mdc_lookup_uncached needs write lock, dropping the * read lock means we can no longer trust the dirent or * the chunk. */ if (!has_write) { /* We will have to re-find this dirent after we * re-acquire the lock. */ look_ck = dirent->ck; PTHREAD_RWLOCK_unlock(&directory->content_lock); PTHREAD_RWLOCK_wrlock(&directory->content_lock); has_write = true; /* Since we have dropped the lock, if we are * using dirent name as cookie, we can't assume * anything about the dirent cache, so we may * need to rescan (see logic above that uses * first pass. We also can no longer trust the * chunk pointer. Note that if this chunk is * actually discarded, we will read a new * chunk that may not start at the same place as * the previous chunk. */ first_pass = true; chunk = NULL; /* Now we need to look for this dirent again. * We haven't updated next_ck for this dirent * yet, so it is the right whence to use for a * repopulation readdir if the chunk is * discarded. */ goto again; } status = mdc_lookup_uncached(directory, dirent->name, &entry, NULL); if (FSAL_IS_ERROR(status)) { PTHREAD_RWLOCK_unlock(&directory->content_lock); LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "lookup by name failed status=%s", fsal_err_txt(status)); if (status.major == ERR_FSAL_STALE) mdcache_kill_entry(directory); return status; } } next_ck = dirent->ck; /* Ensure the attribute cache is valid. The simplest way to do * this is to call getattrs(). We need a copy anyway, to ensure * thread safety. */ fsal_prepare_attrs(&attrs, attrmask); status = entry->obj_handle.obj_ops.getattrs(&entry->obj_handle, &attrs); if (FSAL_IS_ERROR(status)) { PTHREAD_RWLOCK_unlock(&directory->content_lock); LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "getattrs failed status=%s", fsal_err_txt(status)); return status; } cb_result = cb(dirent->name, &entry->obj_handle, &entry->attrs, dir_state, next_ck); fsal_release_attrs(&attrs); LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "dirent = %p %s, cb_result = %s, eod = %s", dirent, dirent->name, fsal_dir_result_str(cb_result), dirent->eod ? "true" : "false"); if (cb_result >= DIR_TERMINATE || dirent->eod) { /* Caller is done, or we have reached the end of * the directory, no need to get another dirent. */ /* If cb_result is DIR_TERMINATE, the callback did * not consume this entry, so we can not have reached * end of directory. */ *eod_met = cb_result != DIR_TERMINATE && dirent->eod; if (*eod_met && whence == 0) { /* Since eod is true and whence is 0, we know * the entire directory is populated. */ atomic_set_uint32_t_bits(&directory->mde_flags, MDCACHE_DIR_POPULATED); } LogDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "readdir completed, eod = %s", *eod_met ? "true" : "false"); PTHREAD_RWLOCK_unlock(&directory->content_lock); return status; } } if (chunk->next_ck != 0) { /* If the chunk has a known chunk following it, use the first * cookie in that chunk for AVL tree lookup, which will succeed * rather than having to do a readdir to find the next entry. * * If the chunk is no longer present, the lookup will fail, in * which case next_ck is the right cookie to use as the whence * for the next readdir. */ look_ck = chunk->next_ck; } else { /* The next chunk is not resident, skip right to populating * the next chunk. next_ck is the right cookie to use as the * whence for the next readdir. */ look_ck = 0; } /* Due to the conditions we return from inside the loop, we know that if * we reach the end of the chunk we must fetch another chunk to satisfy * the directory read. The next_ck is the cookie for the next dirent to * find, which should be the first dirent of the next chunk. */ /* NOTE: An FSAL that does not return 0 or LAST_COOKIE * as the cookie for the last directory entry will * result in our attempting to find one more * chunk, which will not succeed and then the eod * condition detected above before the while loop * will kick in. */ /* NOTE: We also keep the write lock if we already had * it. Most likely we will need to populate the * next chunk also. It's probably not worth * dropping the write lock and taking the read * lock just in case the next chunk actually * happens to be populated. */ first_pass = false; goto again; } /** * @brief Populate a single dir entry * * This callback serves to populate a single dir entry from the * readdir. * * NOTE: Attributes are passed up from sub-FSAL, it will call * fsal_release_attrs, though if we do an fsal_copy_attrs(dest, src, true), any * references will have been transferred to the mdcache entry and the FSAL's * fsal_release_attrs will not really have anything to do. * * @param[in] name Name of the directory entry * @param[in] sub_handle Object for entry * @param[in] attrs Attributes requested for the object * @param[in,out] dir_state Callback state * @param[in] cookie Directory cookie * * @returns fsal_dir_result */ static enum fsal_dir_result mdc_populate_dirent(const char *name, struct fsal_obj_handle *sub_handle, struct attrlist *attrs, void *dir_state, fsal_cookie_t cookie) { struct mdcache_populate_cb_state *state = dir_state; fsal_status_t status = { 0, 0 }; mdcache_entry_t *directory = container_of(&state->dir->obj_handle, mdcache_entry_t, obj_handle); /* This is in the middle of a subcall. Do a supercall */ supercall_raw(state->export, status = mdc_add_cache(directory, name, sub_handle, attrs) ); if (FSAL_IS_ERROR(status)) { *state->status = status; if (status.major == ERR_FSAL_XDEV) { LogInfo(COMPONENT_CACHE_INODE, "Ignoring XDEV entry %s", name); *state->status = fsalstat(ERR_FSAL_NO_ERROR, 0); return DIR_CONTINUE; } if ((*state->status).major == ERR_FSAL_OVERFLOW) { LogFullDebug(COMPONENT_CACHE_INODE, "Lookup failed on %s in dir %p with %s", name, directory, fsal_err_txt(*state->status)); } else { LogInfo(COMPONENT_CACHE_INODE, "Lookup failed on %s in dir %p with %s", name, directory, fsal_err_txt(*state->status)); } return DIR_TERMINATE; } return DIR_CONTINUE; } /** * * @brief Cache complete directory contents * * This function reads a complete directory from the FSAL and caches * both the names and files. The content lock must be held on the * directory being read. * * @note dir MUST have it's content_lock held for writing * * @param[in] dir Entry for the parent directory to be read * * @return FSAL status */ fsal_status_t mdcache_dirent_populate(mdcache_entry_t *dir) { fsal_status_t fsal_status; fsal_status_t status = {0, 0}; bool eod = false; attrmask_t attrmask; struct mdcache_populate_cb_state state; #ifdef DEBUG_MDCACHE assert(dir->content_lock.__data.__writer != 0); #endif /* Only DIRECTORY entries are concerned */ if (dir->obj_handle.type != DIRECTORY) { LogDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "CACHE_INODE_NOT_A_DIRECTORY"); return fsalstat(ERR_FSAL_NOTDIR, 0); } LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Entry %p, MDCACHE_TRUST_CONTENT %s, MDCACHE_DIR_POPULATED %s", dir, test_mde_flags(dir, MDCACHE_TRUST_CONTENT) ? "true" : "false", test_mde_flags(dir, MDCACHE_DIR_POPULATED) ? "true" : "false"); if (test_mde_flags(dir, MDCACHE_DIR_POPULATED | MDCACHE_TRUST_CONTENT)) { return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* Invalidate all the dirents */ mdcache_dirent_invalidate_all(dir); state.export = mdc_cur_export(); state.dir = dir; state.status = &status; state.cb = NULL; /* cached dirs don't use callback */ state.dir_state = NULL; /* cached dirs don't use dir_state */ attrmask = op_ctx->fsal_export->exp_ops.fs_supported_attrs( op_ctx->fsal_export) | ATTR_RDATTR_ERR; subcall_raw(state.export, fsal_status = dir->sub_handle->obj_ops.readdir( dir->sub_handle, NULL, (void *)&state, mdc_populate_dirent, attrmask, &eod) ); if (FSAL_IS_ERROR(fsal_status)) { LogDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "FSAL readdir status=%s", fsal_err_txt(fsal_status)); return fsal_status; } if (status.major == ERR_FSAL_OVERFLOW) return status; /* we were supposed to read to the end.... */ if (!eod && mdcache_param.retry_readdir) { LogInfoAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Readdir didn't reach eod on dir %p (status %s)", &dir->sub_handle, fsal_err_txt(status)); return fsalstat(ERR_FSAL_DELAY, 0); } else if (eod) { /* End of work */ LogFullDebugAlt(COMPONENT_NFS_READDIR, COMPONENT_CACHE_INODE, "Entry %p setting MDCACHE_DIR_POPULATED", dir); atomic_set_uint32_t_bits(&dir->mde_flags, MDCACHE_DIR_POPULATED); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* If !eod (and fsal_status isn't an error), then the only error path * is through a callback failure and status has been set the * mdc_populate_dirent callback */ return status; } /** * @brief Forcibly remove an entry from the cache (top half) * * This function is used to invalidate a cache entry when it * has become unusable (for example, when the FSAL declares it to be * stale). * * To simplify interaction with the SAL, this function no longer * finalizes the entry, but schedules the entry for out-of-line * cleanup, after first making it unreachable. * * @param[in] entry The entry to be killed */ void _mdcache_kill_entry(mdcache_entry_t *entry, char *file, int line, char *function) { bool freed; if (isDebug(COMPONENT_CACHE_INODE)) { DisplayLogComponentLevel(COMPONENT_CACHE_INODE, file, line, function, NIV_DEBUG, "Kill %s entry %p obj_handle %p", object_file_type_to_str( entry->obj_handle.type), entry, &entry->obj_handle); } freed = cih_remove_checked(entry); /* !reachable, drop sentinel ref */ #ifdef USE_LTTNG tracepoint(mdcache, mdc_kill_entry, function, line, entry, entry->lru.refcnt, freed); #endif if (!freed) { /* queue for cleanup */ mdcache_lru_cleanup_push(entry); } } /** * @brief Update the cached attributes * * Update the cached attributes on @a entry with the attributes in @a attrs * * @note The caller must hold the attribute lock for WRITE * * @param[in] entry Entry to update * @param[in] attrs New attributes to cache * @return FSAL status */ void mdc_update_attr_cache(mdcache_entry_t *entry, struct attrlist *attrs) { if (entry->attrs.acl != NULL) { /* We used to have an ACL... */ if (attrs->acl != NULL) { /* We got an ACL from the sub FSAL whether we asked for * it or not, given that we had an ACL before, and we * got a new one, update the ACL, so release the old * one. */ nfs4_acl_release_entry(entry->attrs.acl); } else { /* A new ACL wasn't provided, so move the old one * into the new attributes so it will be preserved * by the fsal_copy_attrs. */ attrs->acl = entry->attrs.acl; attrs->valid_mask |= ATTR_ACL; } /* NOTE: Because we already had an ACL, * entry->attrs.request_mask MUST have the ATTR_ACL bit set. * This will assure that fsal_copy_attrs below will copy the * selected ACL (old or new) into entry->attrs. */ /* ACL was released or moved to new attributes. */ entry->attrs.acl = NULL; } else if (attrs->acl != NULL) { /* We didn't have an ACL before, but we got a new one. We may * not have asked for it, but receive it anyway. */ entry->attrs.request_mask |= ATTR_ACL; } if (attrs->expire_time_attr == 0) { /* FSAL did not set this, retain what was in the entry. */ attrs->expire_time_attr = entry->attrs.expire_time_attr; } /* Now move the new attributes into the entry. */ fsal_copy_attrs(&entry->attrs, attrs, true); /* Note that we use &entry->attrs here in case attrs.request_mask was * modified by the FSAL. entry->attrs.request_mask reflects the * attributes we requested, and was updated to "request" ACL if the * FSAL provided one for us gratis. */ mdc_fixup_md(entry, &entry->attrs); } /** @} */ nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_int.h000066400000000000000000000754771324272410200243670ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright 2015-2017 Red Hat, Inc. and/or its affiliates. * Author: Daniel Gryniewicz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * @file mdcache_int.h * @brief MDCache main internal interface. * * Main data structures and profiles for MDCache */ #ifndef MDCACHE_INT_H #define MDCACHE_INT_H #include #include #include "config.h" #include "mdcache_ext.h" #include "sal_data.h" #include "fsal_up.h" #include "fsal_convert.h" #include "display.h" typedef struct mdcache_fsal_obj_handle mdcache_entry_t; #define MDC_UNEXPORT 1 /* * MDCACHE internal export */ struct mdcache_fsal_export { struct fsal_export mfe_exp; char *name; /** My up_ops */ struct fsal_up_vector up_ops; /** Higher level up_ops for ops we don't consume */ struct fsal_up_vector super_up_ops; /** The list of cache inode entries belonging to this export */ struct glist_head entry_list; /** Lock protecting entry_list */ pthread_rwlock_t mdc_exp_lock; /** Flags for the export. */ uint8_t flags; }; /** * @brief Structure representing a cache key. * * Wraps an underlying FSAL-specific key. */ typedef struct mdcache_key { uint64_t hk; /* hash key */ void *fsal; /*< sub-FSAL module */ struct gsh_buffdesc kv; /*< fsal handle */ } mdcache_key_t; int display_mdcache_key(struct display_buffer *dspbuf, mdcache_key_t *key); static inline int mdcache_key_cmp(const struct mdcache_key *k1, const struct mdcache_key *k2) { if (likely(k1->hk < k2->hk)) return -1; if (likely(k1->hk > k2->hk)) return 1; if (unlikely(k1->kv.len < k2->kv.len)) return -1; if (unlikely(k1->kv.len > k2->kv.len)) return 1; if (unlikely(k1->fsal < k2->fsal)) return -1; if (unlikely(k1->fsal > k2->fsal)) return 1; /* deep compare */ return memcmp(k1->kv.addr, k2->kv.addr, k1->kv.len); } /** * Data for tracking a cache entry's position the LRU. */ /** * Valid LRU queues. */ enum lru_q_id { LRU_ENTRY_NONE = 0, /* entry not queued */ LRU_ENTRY_L1, LRU_ENTRY_L2, LRU_ENTRY_CLEANUP }; #define LRU_CLEANUP 0x00000001 /* Entry is on cleanup queue */ #define LRU_CLEANED 0x00000002 /* Entry has been cleaned */ typedef struct mdcache_lru__ { struct glist_head q; /*< Link in the physical deque impelmenting a portion of the logical LRU. */ enum lru_q_id qid; /*< Queue identifier */ int32_t refcnt; /*< Reference count. This is signed to make mistakes easy to see. */ uint32_t flags; /*< Status flags; MUST use atomic ops */ uint32_t lane; /*< The lane in which an entry currently *< resides, so we can lock the deque and *< decrement the correct counter when moving *< or deleting the entry. */ uint32_t cf; /*< Confounder */ } mdcache_lru_t; /** * cache inode statistics. */ struct mdcache_stats { uint64_t inode_req; uint64_t inode_hit; uint64_t inode_miss; uint64_t inode_conf; uint64_t inode_added; uint64_t inode_mapping; }; extern struct mdcache_stats *cache_stp; /** * @brief Represents one of the many-many links between inodes and exports. * */ struct entry_export_map { /** The relevant cache inode entry */ mdcache_entry_t *entry; /** The export the entry belongs to */ struct mdcache_fsal_export *exp; /** List of entries per export */ struct glist_head entry_per_export; /** List of exports per entry */ struct glist_head export_per_entry; }; /** * Flags */ /** Trust stored attributes */ #define MDCACHE_TRUST_ATTRS FSAL_UP_INVALIDATE_ATTRS /** Trust stored ACL */ #define MDCACHE_TRUST_ACL FSAL_UP_INVALIDATE_ACL /** Trust inode content (for the moment, directory and symlink) */ #define MDCACHE_TRUST_CONTENT FSAL_UP_INVALIDATE_CONTENT /** The directory has been populated (negative lookups are meaningful) */ #define MDCACHE_DIR_POPULATED FSAL_UP_INVALIDATE_DIR_POPULATED /** The directory chunks are considered valid */ #define MDCACHE_TRUST_DIR_CHUNKS FSAL_UP_INVALIDATE_DIR_CHUNKS /** The entry has been removed, but not unhashed due to state */ static const uint32_t MDCACHE_UNREACHABLE = 0x100; /** The directory is too big; skip the dirent cache */ static const uint32_t MDCACHE_BYPASS_DIRCACHE = 0x200; /** * @brief Represents a cached inode * * Information representing a cached file (inode) including metadata, * and for directories and symlinks, pointers to cached content. This * is also the anchor for state held on a file. * * Regarding the locking discipline: * (1) attr_lock protects the attrs field, the export_list, and attr_time * * (2) content_lock must be held for WRITE when modifying the AVL tree * of a directory or any dirent contained therein. It must be * held for READ when accessing any of this information. * * (3) content_lock must be held for WRITE when updating the cached * content of a symlink or when NULLing the object.symlink pointer * preparatory to freeing the link structure. It must be held for * READ when dereferencing the object.symlink pointer or reading * cached content. XXX dang symlink content is in FSAL now * * The handle, cache key, and type fields are unprotected, as they are * considered to be immutable throughout the life of the object. * * The flags field is unprotected, however it should be modified only * through the functions atomic_set_uint32_t_bits and * atomic_clear_uint32_t_bits. * * The lru field has its own mutex to protect it. * * The attributes and symlink contents are stored in the handle for * api simplicity but these locks apply around their access methods. * * @note As part of the transition to the new api, the handle was * moved out of the union and made a pointer to a separately allocated * object. However, locking applies everywhere except handle object * creation time (nobody knows about it yet). The symlink content * cache has moved into the handle as, well. The mdcache_entry and * fsal_obj_handle are two parts of the same thing, a cached inode. * mdcache_entry holds the cache stuff and fsal_obj_handle holds the * stuff the the fsal has to manage, i.e. filesystem bits. */ struct mdcache_fsal_obj_handle { /** Reader-writer lock for attributes */ pthread_rwlock_t attr_lock; /** MDCache FSAL Handle */ struct fsal_obj_handle obj_handle; /** Sub-FSAL handle */ struct fsal_obj_handle *sub_handle; /** Cached attributes */ struct attrlist attrs; /** FH hash linkage */ struct { struct avltree_node node_k; /*< AVL node in tree */ mdcache_key_t key; /*< Key of this entry */ bool inavl; } fh_hk; /** Flags for this entry */ uint32_t mde_flags; /** Time at which we last refreshed attributes. */ time_t attr_time; /** Time at which we last refreshed acl. */ time_t acl_time; /** New style LRU link */ mdcache_lru_t lru; /** Exports per entry (protected by attr_lock) */ struct glist_head export_list; /** ID of the first mapped export for fast path * This is an int32_t because we need it to be -1 to indicate * no mapped export. */ int32_t first_export_id; /** Lock on type-specific cached content. See locking discipline for details. */ pthread_rwlock_t content_lock; /** Filetype specific data, discriminated by the type field. Note that data for special files is in attributes.rawdev */ union mdcache_fsobj { struct state_hdl hdl; struct { /** List of chunks in this directory, not ordered */ struct glist_head chunks; /** List of detached directory entries. */ struct glist_head detached; /** Spin lock to protect the detached list. */ pthread_spinlock_t spin; /** Count of detached directory entries. */ int detached_count; /** @todo FSF * * This is somewhat fragile, however, a reorganization * is possible. If state_lock was to be moved into * state_file and state_dir, and the state code was * made clear which it was working with, dhdl could * be replaced with a state_dir which would be * smaller than state_file, and then the additional * members of fsdir would basically overlay * the larger state_file that hdl is. * * Such a reorg could save memory AND make for a * crisper interface. */ struct state_hdl dhdl; /**< Storage for dir state */ /** The parent host-handle of this directory ('..') */ struct gsh_buffdesc parent; /** The first dirent cookie in this directory. * 0 if not known. */ fsal_cookie_t first_ck; struct { /** Children by name hash */ struct avltree t; /** Persist cookies for deleted entries */ struct avltree c; /** Table of dirents by FSAL cookie */ struct avltree ck; /** Table of dirents in sorted order. */ struct avltree sorted; /** Heuristic. Expect 0. */ uint32_t collisions; } avl; } fsdir; /**< DIRECTORY data */ } fsobj; }; struct dir_chunk { /** This chunk is part of a directory */ struct glist_head chunks; /** List of dirents in this chunk */ struct glist_head dirents; /** Directory this chunk belongs to */ struct mdcache_fsal_obj_handle *parent; /** LRU link */ mdcache_lru_t chunk_lru; /** The previous chunk, this pointer is only de-referenced during * chunk population (where the content_lock prevents the previous * chunk from going invalid), or used to double check but not * de-referenced. */ struct dir_chunk *prev_chunk; /** Cookie of first entry in sequentially next chunk, will be set to * 0 if there is no sequentially next chunk. */ fsal_cookie_t next_ck; /** Number of entries in chunk */ int num_entries; }; /** * @brief Represents a cached directory entry * * This is a cached directory entry that associates a name and cookie * with a cache entry. */ #define DIR_ENTRY_FLAG_NONE 0x0000 #define DIR_ENTRY_FLAG_DELETED 0x0001 #define DIR_ENTRY_SORTED 0x0004 typedef struct mdcache_dir_entry__ { /** This dirent is part of a chunk */ struct glist_head chunk_list; /** The chunk this entry belongs to */ struct dir_chunk *chunk; /** node in tree by name */ struct avltree_node node_hk; /** AVL node in tree by cookie */ struct avltree_node node_ck; /** AVL node in tree by sorted order */ struct avltree_node node_sorted; /** Cookie value from FSAL * This is the coookie that is the "key" to find THIS entry, however * a readdir with whence will be looking for the NEXT entry. */ uint64_t ck; /** Indicates if this dirent is the last dirent in a chunked directory. */ bool eod; struct { /** Name Hash */ uint64_t k; /** Number of probes, an efficiency metric */ uint32_t p; } hk; /** Key of cache entry */ mdcache_key_t ckey; /** Flags */ uint32_t flags; /** The NUL-terminated filename */ char name[]; } mdcache_dir_entry_t; /** * @brief Move a detached dirent to MRU postion in LRU list. * * @param[in] parent Parent entry * @param[in] dirent Dirent to move to MRU */ static inline void bump_detached_dirent(mdcache_entry_t *parent, mdcache_dir_entry_t *dirent) { pthread_spin_lock(&parent->fsobj.fsdir.spin); if (glist_first_entry(&parent->fsobj.fsdir.detached, mdcache_dir_entry_t, chunk_list) != dirent) { glist_del(&dirent->chunk_list); glist_add(&parent->fsobj.fsdir.detached, &dirent->chunk_list); } pthread_spin_unlock(&parent->fsobj.fsdir.spin); } /** * @brief Remove a detached dirent from the LRU list. * * @param[in] parent Parent entry * @param[in] dirent Dirent to remove */ static inline void rmv_detached_dirent(mdcache_entry_t *parent, mdcache_dir_entry_t *dirent) { pthread_spin_lock(&parent->fsobj.fsdir.spin); /* Note that the dirent might not be on the detached list if it * was being reaped by another thread. All is well here... */ if (!glist_null(&dirent->chunk_list)) { glist_del(&dirent->chunk_list); parent->fsobj.fsdir.detached_count--; } pthread_spin_unlock(&parent->fsobj.fsdir.spin); } /* Helpers */ fsal_status_t mdcache_alloc_and_check_handle( struct mdcache_fsal_export *exp, struct fsal_obj_handle *sub_handle, struct fsal_obj_handle **new_obj, bool new_directory, struct attrlist *attrs_in, struct attrlist *attrs_out, const char *tag, mdcache_entry_t *parent, const char *name, bool *invalidate, struct state_t *state); fsal_status_t mdcache_refresh_attrs(mdcache_entry_t *entry, bool need_acl, bool invalidate); static inline void mdcache_refresh_attrs_no_invalidate(mdcache_entry_t *entry) { fsal_status_t status; PTHREAD_RWLOCK_wrlock(&entry->attr_lock); status = mdcache_refresh_attrs(entry, false, false); PTHREAD_RWLOCK_unlock(&entry->attr_lock); if (FSAL_IS_ERROR(status)) { LogDebug(COMPONENT_CACHE_INODE, "Refresh attributes failed %s", fsal_err_txt(status)); } } fsal_status_t mdcache_new_entry(struct mdcache_fsal_export *exp, struct fsal_obj_handle *sub_handle, struct attrlist *attrs_in, struct attrlist *attrs_out, bool new_directory, mdcache_entry_t **entry, struct state_t *state); fsal_status_t mdcache_find_keyed(mdcache_key_t *key, mdcache_entry_t **entry); fsal_status_t mdcache_locate_host(struct gsh_buffdesc *fh_desc, struct mdcache_fsal_export *exp, mdcache_entry_t **entry, struct attrlist *attrs_out); fsal_status_t mdc_try_get_cached(mdcache_entry_t *mdc_parent, const char *name, mdcache_entry_t **entry); fsal_status_t mdc_lookup(mdcache_entry_t *mdc_parent, const char *name, bool uncached, mdcache_entry_t **new_entry, struct attrlist *attrs_out); fsal_status_t mdc_lookup_uncached(mdcache_entry_t *mdc_parent, const char *name, mdcache_entry_t **new_entry, struct attrlist *attrs_out); void mdcache_src_dest_lock(mdcache_entry_t *src, mdcache_entry_t *dest); void mdcache_src_dest_unlock(mdcache_entry_t *src, mdcache_entry_t *dest); fsal_status_t mdcache_dirent_remove(mdcache_entry_t *parent, const char *name); fsal_status_t mdcache_dirent_add(mdcache_entry_t *parent, const char *name, mdcache_entry_t *entry, bool *invalidate); fsal_status_t mdcache_dirent_rename(mdcache_entry_t *parent, const char *oldname, const char *newname); void mdcache_dirent_invalidate_all(mdcache_entry_t *entry); fsal_status_t mdcache_dirent_populate(mdcache_entry_t *dir); fsal_status_t mdcache_readdir_uncached(mdcache_entry_t *directory, fsal_cookie_t *whence, void *dir_state, fsal_readdir_cb cb, attrmask_t attrmask, bool *eod_met); void mdcache_clean_dirent_chunk(struct dir_chunk *chunk); void place_new_dirent(mdcache_entry_t *parent_dir, mdcache_dir_entry_t *new_dir_entry); fsal_status_t mdcache_readdir_chunked(mdcache_entry_t *directory, fsal_cookie_t whence, void *dir_state, fsal_readdir_cb cb, attrmask_t attrmask, bool *eod_met); void mdc_get_parent(struct mdcache_fsal_export *exp, mdcache_entry_t *entry); void mdc_update_attr_cache(mdcache_entry_t *entry, struct attrlist *attrs); /** * @brief Atomically test the bits in mde_flags. * * @param[in] entry The mdcache entry to test * @param[in] bits The bits to test if set * * @returns true if all the bits are set. * */ static inline bool test_mde_flags(mdcache_entry_t *entry, uint32_t bits) { return (atomic_fetch_uint32_t(&entry->mde_flags) & bits) == bits; } static inline bool mdc_dircache_trusted(mdcache_entry_t *dir) { /* This function returns false if chunking is enabled, the only caller * that matters for chunking, mdcache_dirent_find will thus return * ERR_FSAL_NO_ERROR if an entry is not found, and it's callers will * do the right thing... */ return ((mdcache_param.dir.avl_chunk == 0) && test_mde_flags(dir, MDCACHE_TRUST_CONTENT | MDCACHE_DIR_POPULATED)); } static inline struct mdcache_fsal_export *mdc_export( struct fsal_export *fsal_export) { return container_of(fsal_export, struct mdcache_fsal_export, mfe_exp); } static inline struct mdcache_fsal_export *mdc_cur_export(void) { return mdc_export(op_ctx->fsal_export); } void mdc_clean_entry(mdcache_entry_t *entry); fsal_status_t mdc_check_mapping(mdcache_entry_t *entry); void _mdcache_kill_entry(mdcache_entry_t *entry, char *file, int line, char *function); #define mdcache_kill_entry(entry) \ _mdcache_kill_entry(entry, \ (char *) __FILE__, __LINE__, (char *) __func__) fsal_status_t mdc_get_parent_handle(struct mdcache_fsal_export *exp, mdcache_entry_t *entry, struct fsal_obj_handle *sub_parent); extern struct config_block mdcache_param_blk; /* Call a sub-FSAL function using it's export, safe for use during shutdown */ #define subcall_shutdown_raw(myexp, call) do { \ if (op_ctx) \ op_ctx->fsal_export = (myexp)->mfe_exp.sub_export; \ call; \ if (op_ctx) \ op_ctx->fsal_export = &(myexp)->mfe_exp; \ } while (0) /* Call a sub-FSAL function using it's export */ #define subcall_raw(myexp, call) do { \ op_ctx->fsal_export = (myexp)->mfe_exp.sub_export; \ call; \ op_ctx->fsal_export = &(myexp)->mfe_exp; \ } while (0) /* Call a sub-FSAL function using it's export */ #define subcall(call) do { \ struct mdcache_fsal_export *__export = mdc_cur_export(); \ subcall_raw(__export, call); \ } while (0) /* During a callback from a sub-FSAL, call using MDCACHE's export */ #define supercall_raw(myexp, call) do { \ LogFullDebug(COMPONENT_CACHE_INODE, "supercall %s", myexp->name); \ op_ctx->fsal_export = &(myexp)->mfe_exp; \ call; \ op_ctx->fsal_export = (myexp)->mfe_exp.sub_export; \ } while (0) /** * @brief Lock context for content lock recursion * * long description */ typedef struct { mdcache_entry_t *entry; bool iswrite; int count; } mdc_lock_context_t; /** * @brief Dup a cache key. * * Deep copies the key passed in src, to tgt. On return, tgt->kv.addr * is overwritten with a new buffer of length src->kv.len, and the buffer * is copied. * * @param tgt [inout] Destination of copy * @param src [in] Source of copy * * @return 0 on success. */ static inline void mdcache_key_dup(mdcache_key_t *tgt, mdcache_key_t *src) { tgt->kv.len = src->kv.len; tgt->kv.addr = gsh_malloc(src->kv.len); memcpy(tgt->kv.addr, src->kv.addr, src->kv.len); tgt->hk = src->hk; tgt->fsal = src->fsal; } /** * @brief Set the parent key of an entry * * If the parent key is not set, set it. This keeps keys from being leaked. * * @param[in] entry Entry to set * @return Return description */ static inline void mdc_dir_add_parent(mdcache_entry_t *entry, mdcache_entry_t *mdc_parent) { if (entry->fsobj.fsdir.parent.len == 0) { /* The parent key must be a host-handle so that * create_handle() works in all cases. */ mdc_get_parent_handle(mdc_cur_export(), entry, mdc_parent->sub_handle); } } /** * @brief Delete a cache key. * * Delete a cache key. Safe to call even if key was not allocated. * * @param key [in] The key to delete * * @return void. */ static inline void mdcache_key_delete(mdcache_key_t *key) { key->kv.len = 0; gsh_free(key->kv.addr); key->kv.addr = NULL; } /* Create a copy of host-handle */ static inline void mdcache_copy_fh(struct gsh_buffdesc *dest, struct gsh_buffdesc *src) { dest->len = src->len; dest->addr = gsh_malloc(dest->len); (void)memcpy(dest->addr, src->addr, dest->len); } /* Delete stored parent host-handle */ static inline void mdcache_free_fh(struct gsh_buffdesc *fh_desc) { fh_desc->len = 0; gsh_free(fh_desc->addr); fh_desc->addr = NULL; } /** * @brief Update entry metadata from its attributes * * This function, to be used after a FSAL_getattr, updates the * attribute trust flag and time, and stores the change time * in the main mdcache_entry_t. * * @note the caller MUST hold attr_lock for write * * @param[in,out] entry The entry on which we operate. * @param[in] attrs The attributes that have just been updated * (we actually only care about the masks) */ static inline void mdc_fixup_md(mdcache_entry_t *entry, struct attrlist *attrs) { uint32_t flags = 0; /* As long as the ACL was requested, and we get here, we assume no * failure to fetch ACL (differentiated from no ACL to fetch), and * thus we only look at the fact that ACL was requested to determine * that we can trust the ACL. */ if (attrs->request_mask & ATTR_ACL) flags |= MDCACHE_TRUST_ACL; /* If the other attributes were requested, we can trust the other * attributes. Note that if not all could be provided, we assumed * that an error occurred. */ if (attrs->request_mask & ~ATTR_ACL) flags |= MDCACHE_TRUST_ATTRS; if (attrs->valid_mask == ATTR_RDATTR_ERR) { /* The attribute fetch failed, mark the attributes and ACL as * untrusted. */ atomic_clear_uint32_t_bits(&entry->mde_flags, MDCACHE_TRUST_ACL | MDCACHE_TRUST_ATTRS); return; } /* Set the refresh time for the cache entry */ if (flags & MDCACHE_TRUST_ACL) { if (entry->attrs.expire_time_attr > 0) entry->acl_time = time(NULL); else entry->acl_time = 0; } if (flags & MDCACHE_TRUST_ATTRS) { if (entry->attrs.expire_time_attr > 0) entry->attr_time = time(NULL); else entry->attr_time = 0; } /* We have just loaded the attributes from the FSAL. */ atomic_set_uint32_t_bits(&entry->mde_flags, flags); } static inline bool mdcache_test_attrs_trust(mdcache_entry_t *entry, attrmask_t mask) { uint32_t flags = 0; /* Check what attributes were originally requested for refresh. */ if (mask & ATTR_ACL) flags |= MDCACHE_TRUST_ACL; if (mask & ~ATTR_ACL) flags |= MDCACHE_TRUST_ATTRS; /* If any of the requested attributes are not valid, return. */ if (!test_mde_flags(entry, flags)) return false; return true; } /** * @brief Check if attributes are valid * * @note the caller MUST hold attr_lock for read * * @param[in] entry The entry to check */ static inline bool mdcache_is_attrs_valid(mdcache_entry_t *entry, attrmask_t mask) { if (!mdcache_test_attrs_trust(entry, mask)) return false; if (entry->attrs.valid_mask == ATTR_RDATTR_ERR) return false; if (entry->obj_handle.type == DIRECTORY && mdcache_param.getattr_dir_invalidation) return false; if ((mask & ~ATTR_ACL) != 0 && entry->attrs.expire_time_attr == 0) return false; if ((mask & ~ATTR_ACL) != 0 && entry->attrs.expire_time_attr > 0) { time_t current_time = time(NULL); if (current_time - entry->attr_time > entry->attrs.expire_time_attr) return false; } if ((mask & ATTR_ACL) != 0 && entry->attrs.expire_time_attr == 0) return false; if ((mask & ATTR_ACL) != 0 && entry->attrs.expire_time_attr > 0) { time_t current_time = time(NULL); if (current_time - entry->acl_time > entry->attrs.expire_time_attr) return false; } return true; } /** * @brief Remove an export <-> entry mapping * * @param[in] expmap Mapping to remove * * @note must be called with the mdc_exp_lock and attr_lock held */ static inline void mdc_remove_export_map(struct entry_export_map *expmap) { glist_del(&expmap->export_per_entry); glist_del(&expmap->entry_per_export); gsh_free(expmap); } /** * @brief Check to see if an entry has state * * long description * * @param[in] entry Entry to check * @return true if has state, false otherwise */ static inline bool mdc_has_state(mdcache_entry_t *entry) { switch (entry->obj_handle.type) { case REGULAR_FILE: if (!glist_empty(&entry->fsobj.hdl.file.list_of_states)) return true; if (!glist_empty(&entry->fsobj.hdl.file.layoutrecall_list)) return true; if (!glist_empty(&entry->fsobj.hdl.file.lock_list)) return true; if (!glist_empty(&entry->fsobj.hdl.file.nlm_share_list)) return true; return false; case DIRECTORY: if (entry->fsobj.fsdir.dhdl.dir.junction_export) return true; if (entry->fsobj.fsdir.dhdl.dir.exp_root_refcount) return true; return false; default: /* No state for these types */ return false; } } /** * @brief Mark an entry as unreachable * * An entry has become unreachable. If it has no state, kill it. Otherwise, * mark it unreachable so that it can be killed when state is freed. * * @param[in] entry Entry to mark */ static inline void _mdc_unreachable(mdcache_entry_t *entry, char *file, int line, char *function) { if (isDebug(COMPONENT_CACHE_INODE)) { DisplayLogComponentLevel(COMPONENT_CACHE_INODE, file, line, function, NIV_DEBUG, "Unreachable %s entry %p %s state", object_file_type_to_str( entry->obj_handle.type), entry, mdc_has_state(entry) ? "has" : "doesn't have"); } if (!mdc_has_state(entry)) { mdcache_kill_entry(entry); return; } atomic_set_uint32_t_bits(&entry->mde_flags, MDCACHE_UNREACHABLE); } #define mdc_unreachable(entry) \ _mdc_unreachable(entry, \ (char *) __FILE__, __LINE__, (char *) __func__) /* Handle methods */ /** * Structure used to store data for read_dirents callback. * * Before executing the upper level callback (it might be another * stackable fsal or the inode cache), the context has to be restored. */ struct mdcache_readdir_state { fsal_readdir_cb cb; /*< Callback to the upper layer. */ struct mdcache_fsal_export *exp; /*< Export of the current mdcache. */ void *dir_state; /*< State to be sent to the next callback. */ }; fsal_status_t mdcache_lookup_path(struct fsal_export *exp_hdl, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out); fsal_status_t mdcache_create_handle(struct fsal_export *exp_hdl, struct gsh_buffdesc *hdl_desc, struct fsal_obj_handle **handle, struct attrlist *attrs_out); int mdcache_fsal_open(struct mdcache_fsal_obj_handle *, int, fsal_errors_t *); int mdcache_fsal_readlink(struct mdcache_fsal_obj_handle *, fsal_errors_t *); static inline bool mdcache_unopenable_type(object_file_type_t type) { if ((type == SOCKET_FILE) || (type == CHARACTER_FILE) || (type == BLOCK_FILE)) { return true; } else { return false; } } /* I/O management */ fsal_status_t mdcache_seek(struct fsal_obj_handle *obj_hdl, struct io_info *info); fsal_status_t mdcache_io_advise(struct fsal_obj_handle *obj_hdl, struct io_hints *hints); fsal_status_t mdcache_close(struct fsal_obj_handle *obj_hdl); fsal_status_t mdcache_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); bool mdcache_check_verifier(struct fsal_obj_handle *obj_hdl, fsal_verifier_t verifier); fsal_openflags_t mdcache_status2(struct fsal_obj_handle *obj_hdl, struct state_t *state); fsal_status_t mdcache_reopen2(struct fsal_obj_handle *obj_hdl, struct state_t *state, fsal_openflags_t openflags); fsal_status_t mdcache_read2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buf_size, void *buffer, size_t *read_amount, bool *eof, struct io_info *info); fsal_status_t mdcache_write2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buf_size, void *buffer, size_t *write_amount, bool *fsal_stable, struct io_info *info); fsal_status_t mdcache_seek2(struct fsal_obj_handle *obj_hdl, struct state_t *state, struct io_info *info); fsal_status_t mdcache_io_advise2(struct fsal_obj_handle *obj_hdl, struct state_t *state, struct io_hints *hints); fsal_status_t mdcache_commit2(struct fsal_obj_handle *obj_hdl, off_t offset, size_t len); fsal_status_t mdcache_lock_op2(struct fsal_obj_handle *obj_hdl, struct state_t *state, void *p_owner, fsal_lock_op_t lock_op, fsal_lock_param_t *req_lock, fsal_lock_param_t *conflicting_lock); fsal_status_t mdcache_lease_op2(struct fsal_obj_handle *obj_hdl, struct state_t *state, void *owner, fsal_deleg_t deleg); fsal_status_t mdcache_close2(struct fsal_obj_handle *obj_hdl, struct state_t *state); /* extended attributes management */ fsal_status_t mdcache_list_ext_attrs(struct fsal_obj_handle *obj_hdl, unsigned int cookie, fsal_xattrent_t *xattrs_tab, unsigned int xattrs_tabsize, unsigned int *p_nb_returned, int *end_of_list); fsal_status_t mdcache_getextattr_id_by_name(struct fsal_obj_handle *obj_hdl, const char *xattr_name, unsigned int *pxattr_id); fsal_status_t mdcache_getextattr_value_by_name(struct fsal_obj_handle *obj_hdl, const char *xattr_name, caddr_t buffer_addr, size_t buffer_size, size_t *p_output_size); fsal_status_t mdcache_getextattr_value_by_id(struct fsal_obj_handle *obj_hdl, unsigned int xattr_id, caddr_t buffer_addr, size_t buffer_size, size_t *p_output_size); fsal_status_t mdcache_setextattr_value(struct fsal_obj_handle *obj_hdl, const char *xattr_name, caddr_t buffer_addr, size_t buffer_size, int create); fsal_status_t mdcache_setextattr_value_by_id(struct fsal_obj_handle *obj_hdl, unsigned int xattr_id, caddr_t buffer_addr, size_t buffer_size); fsal_status_t mdcache_remove_extattr_by_id(struct fsal_obj_handle *obj_hdl, unsigned int xattr_id); fsal_status_t mdcache_remove_extattr_by_name(struct fsal_obj_handle *obj_hdl, const char *xattr_name); fsal_status_t mdcache_getxattrs(struct fsal_obj_handle *obj_hdl, xattrname4 *name, xattrvalue4 *value); fsal_status_t mdcache_setxattrs(struct fsal_obj_handle *obj_hdl, setxattr_type4 type, xattrname4 *name, xattrvalue4 *value); fsal_status_t mdcache_removexattrs(struct fsal_obj_handle *obj_hdl, xattrname4 *name); fsal_status_t mdcache_listxattrs(struct fsal_obj_handle *obj_hdl, count4 len, nfs_cookie4 *cookie, verifier4 *verf, bool_t *eof, xattrlist4 *names); /* Handle functions */ void mdcache_handle_ops_init(struct fsal_obj_ops *ops); /* Export functions */ void mdcache_export_ops_init(struct export_ops *ops); /* Upcall functions */ fsal_status_t mdcache_export_up_ops_init(struct fsal_up_vector *my_up_ops, const struct fsal_up_vector *super_up_ops); /* Debug functions */ #define MDC_LOG_KEY(key) do { \ LogFullDebugOpaque(COMPONENT_CACHE_INODE, \ "FSAL key: %s", 128, (key)->kv.addr, \ (key)->kv.len); \ LogFullDebug(COMPONENT_CACHE_INODE, "hash key: %lx", (key)->hk); \ } while (0) #endif /* MDCACHE_INT_H */ nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_lru.c000066400000000000000000001566751324272410200243720ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) 2010, The Linux Box Corporation * Contributor : Matt Benjamin * * Some portions Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @addtogroup FSAL_MDCACHE * @{ */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "fsal.h" #include "fsal_convert.h" #include "FSAL/fsal_commonlib.h" #include "nfs_core.h" #include "log.h" #include "mdcache_lru.h" #include "mdcache_hash.h" #include "abstract_atomic.h" #include "gsh_intrinsic.h" #include "sal_functions.h" #include "nfs_exports.h" #ifdef USE_LTTNG #include "gsh_lttng/mdcache.h" #endif /** * * @file mdcache_lru.c * @author Matt Benjamin * @brief Constant-time cache inode cache management implementation */ /** * @page LRUOverview LRU Overview * * This module implements a constant-time cache management strategy * based on LRU. Some ideas are taken from 2Q [Johnson and Shasha 1994] * and MQ [Zhou, Chen, Li 2004]. In this system, cache management does * interact with cache entry lifecycle, but the lru queue is not a garbage * collector. Most imporantly, cache management operations execute in constant * time, as expected with LRU (and MQ). * * Cache entries in use by a currently-active protocol request (or other * operation) have a positive refcount, and threfore should not be present * at the cold end of an lru queue if the cache is well-sized. * * As noted below, initial references to cache entries may only be granted * under the cache inode hash table latch. Likewise, entries must first be * made unreachable to the cache inode hash table, then independently reach * a refcnt of 0, before they may be disposed or recycled. */ struct lru_state lru_state; /** * A single queue structure. */ struct lru_q { struct glist_head q; /* LRU is at HEAD, MRU at tail */ enum lru_q_id id; uint64_t size; }; /** * A single queue lane, holding all entries. */ struct lru_q_lane { struct lru_q L1; struct lru_q L2; struct lru_q cleanup; /* deferred cleanup */ pthread_mutex_t mtx; /* LRU thread scan position */ struct { bool active; struct glist_head *glist; struct glist_head *glistn; } iter; CACHE_PAD(0); }; /* The queue lock and the partition lock interact. The partition lock must * always be taken before the queue lock to avoid deadlock */ #ifdef USE_LTTNG #define QLOCK(qlane) \ do { \ PTHREAD_MUTEX_lock(&(qlane)->mtx); \ tracepoint(mdcache, qlock, __func__, __LINE__, qlane); \ } while (0) #define QUNLOCK(qlane) \ do { \ PTHREAD_MUTEX_unlock(&(qlane)->mtx); \ tracepoint(mdcache, qunlock, __func__, __LINE__, qlane); \ } while (0) #else #define QLOCK(qlane) \ PTHREAD_MUTEX_lock(&(qlane)->mtx) #define QUNLOCK(qlane) \ PTHREAD_MUTEX_unlock(&(qlane)->mtx) #endif /** * A multi-level LRU algorithm inspired by MQ [Zhou]. Transition from * L1 to L2 implies various checks (open files, etc) have been * performed, so ensures they are performed only once. A * correspondence to the "scan resistance" property of 2Q and MQ is * accomplished by recycling/clean loads onto the LRU of L1. Async * processing onto L2 constrains oscillation in this algorithm. */ static struct lru_q_lane LRU[LRU_N_Q_LANES]; static struct lru_q_lane CHUNK_LRU[LRU_N_Q_LANES]; /** * The refcount mechanism distinguishes 3 key object states: * * 1. unreferenced (unreachable) * 2. unincremented, but reachable * 3. incremented * * It seems most convenient to make unreferenced correspond to refcount==0. * Then refcount==1 is a SENTINEL_REFCOUNT in which the only reference to * the entry is the set of functions which can grant new references. An * object with refcount > 1 has been referenced by some thread, which must * release its reference at some point. * * More specifically, in the current implementation, reachability is * serialized by the cache lookup table latch. * * Currently, I propose to distinguish between objects with positive refcount * and objects with state. The latter could be evicted, in the normal case, * only with loss of protocol correctness, but may have only the sentinel * refcount. To preserve constant time operation, they are stored in an * independent partition of the LRU queue. */ static struct fridgethr *lru_fridge; enum lru_edge { LRU_LRU, /* LRU */ LRU_MRU /* MRU */ }; static const uint32_t FD_FALLBACK_LIMIT = 0x400; /* Some helper macros */ #define LRU_NEXT(n) \ (atomic_inc_uint32_t(&(n)) % LRU_N_Q_LANES) /* Delete lru, use iif the current thread is not the LRU * thread. The node being removed is lru, glist a pointer to L1's q, * qlane its lane. */ #define LRU_DQ_SAFE(lru, q) \ do { \ if ((lru)->qid == LRU_ENTRY_L1) { \ struct lru_q_lane *qlane = &LRU[(lru)->lane]; \ if (unlikely((qlane->iter.active) && \ ((&(lru)->q) == qlane->iter.glistn))) { \ qlane->iter.glistn = (lru)->q.next; \ } \ } \ glist_del(&(lru)->q); \ --((q)->size); \ } while (0) #define CHUNK_LRU_DQ_SAFE(lru, qq) \ do { \ if ((lru)->qid == LRU_ENTRY_L1) { \ struct lru_q_lane *qlane = &CHUNK_LRU[(lru)->lane]; \ if (unlikely((qlane->iter.active) && \ ((&(lru)->q) == qlane->iter.glistn))) { \ qlane->iter.glistn = (lru)->q.next; \ } \ } \ glist_del(&(lru)->q); \ --((qq)->size); \ } while (0) #define LRU_ENTRY_L1_OR_L2(e) \ (((e)->lru.qid == LRU_ENTRY_L2) || \ ((e)->lru.qid == LRU_ENTRY_L1)) #define LRU_ENTRY_RECLAIMABLE(e, n) \ (LRU_ENTRY_L1_OR_L2(e) && \ ((n) == LRU_SENTINEL_REFCOUNT+1) && \ ((e)->fh_hk.inavl)) /** * @brief Initialize a single base queue. * * This function initializes a single queue partition (L1, L2, * etc) */ static inline void lru_init_queue(struct lru_q *q, enum lru_q_id qid) { glist_init(&q->q); q->id = qid; q->size = 0; } static inline void lru_init_queues(void) { int ix; for (ix = 0; ix < LRU_N_Q_LANES; ++ix) { struct lru_q_lane *qlane; /* Initialize mdcache_entry_t LRU */ qlane = &LRU[ix]; /* one mutex per lane */ PTHREAD_MUTEX_init(&qlane->mtx, NULL); /* init iterator */ qlane->iter.active = false; /* init lane queues */ lru_init_queue(&LRU[ix].L1, LRU_ENTRY_L1); lru_init_queue(&LRU[ix].L2, LRU_ENTRY_L2); lru_init_queue(&LRU[ix].cleanup, LRU_ENTRY_CLEANUP); /* Initialize dir_chunk LRU */ qlane = &CHUNK_LRU[ix]; /* one mutex per lane */ PTHREAD_MUTEX_init(&qlane->mtx, NULL); /* init iterator */ qlane->iter.active = false; /* init lane queues */ lru_init_queue(&CHUNK_LRU[ix].L1, LRU_ENTRY_L1); lru_init_queue(&CHUNK_LRU[ix].L2, LRU_ENTRY_L2); lru_init_queue(&CHUNK_LRU[ix].cleanup, LRU_ENTRY_CLEANUP); } } /** * @brief Return a pointer to the current queue of entry * * This function returns a pointer to the queue on which entry is linked, * or NULL if entry is not on any queue. * * @note The caller @a MUST hold the lane lock * * @param[in] entry The entry. * * @return A pointer to entry's current queue, NULL if none. */ static inline struct lru_q * lru_queue_of(mdcache_entry_t *entry) { struct lru_q *q; switch (entry->lru.qid) { case LRU_ENTRY_L1: q = &LRU[(entry->lru.lane)].L1; break; case LRU_ENTRY_L2: q = &LRU[(entry->lru.lane)].L2; break; case LRU_ENTRY_CLEANUP: q = &LRU[(entry->lru.lane)].cleanup; break; default: /* LRU_NO_LANE */ q = NULL; break; } /* switch */ return q; } /** * @brief Return a pointer to the current queue of chunk * * This function returns a pointer to the queue on which a chunk is linked, * or NULL if chunk is not on any queue. * * @note The caller @a MUST hold the lane lock * * @param[in] chunk The chunk. * * @return A pointer to chunk's current queue, NULL if none. */ static inline struct lru_q * chunk_lru_queue_of(struct dir_chunk *chunk) { struct lru_q *q; switch (chunk->chunk_lru.qid) { case LRU_ENTRY_L1: q = &CHUNK_LRU[(chunk->chunk_lru.lane)].L1; break; case LRU_ENTRY_L2: q = &CHUNK_LRU[(chunk->chunk_lru.lane)].L2; break; case LRU_ENTRY_CLEANUP: q = &CHUNK_LRU[(chunk->chunk_lru.lane)].cleanup; break; default: /* LRU_NO_LANE */ q = NULL; break; } /* switch */ return q; } /** * @brief Get the appropriate lane for a LRU chunk or entry * * This function gets the LRU lane by taking the modulus of the * supplied pointer. * * @param[in] entry A pointer to a LRU chunk or entry * * @return The LRU lane in which that entry should be stored. */ static inline uint32_t lru_lane_of(void *entry) { return (uint32_t) ((((uintptr_t) entry) / 2*sizeof(uintptr_t)) % LRU_N_Q_LANES); } /** * @brief Insert an entry into the specified queue and lane * * This function determines the queue corresponding to the supplied * lane and flags, inserts the entry into that queue, and updates the * entry to hold the flags and lane. * * @note The caller MUST hold a lock on the queue lane. * * @param[in] lru The LRU entry to insert * @param[in] q The queue to insert on * @param[in] edge One of LRU_LRU or LRU_MRU */ static inline void lru_insert(mdcache_lru_t *lru, struct lru_q *q, enum lru_edge edge) { lru->qid = q->id; /* initial */ if (lru->qid == LRU_ENTRY_CLEANUP) atomic_set_uint32_t_bits(&lru->flags, LRU_CLEANUP); switch (edge) { case LRU_LRU: glist_add(&q->q, &lru->q); break; case LRU_MRU: default: glist_add_tail(&q->q, &lru->q); break; } ++(q->size); } /** * @brief Insert an entry into the specified queue and lane with locking * * This function determines the queue corresponding to the supplied * lane and flags, inserts the entry into that queue, and updates the * entry to hold the flags and lane. * * @note The caller MUST NOT hold a lock on the queue lane. * * @param[in] lru The LRU entry to insert * @param[in] q The queue to insert on * @param[in] edge One of LRU_LRU or LRU_MRU */ static inline void lru_insert_entry(mdcache_entry_t *entry, struct lru_q *q, enum lru_edge edge) { mdcache_lru_t *lru = &entry->lru; struct lru_q_lane *qlane = &LRU[lru->lane]; QLOCK(qlane); lru_insert(lru, q, edge); QUNLOCK(qlane); } /** * @brief Insert a chunk into the specified queue and lane with locking * * This function determines the queue corresponding to the supplied * lane and flags, inserts the chunk into that queue, and updates the * chunk to hold the flags and lane. * * @note The caller MUST NOT hold a lock on the queue lane. * * @param[in] lru The LRU chunk to insert * @param[in] q The queue to insert on * @param[in] edge One of LRU_LRU or LRU_MRU */ static inline void lru_insert_chunk(struct dir_chunk *chunk, struct lru_q *q, enum lru_edge edge) { mdcache_lru_t *lru = &chunk->chunk_lru; struct lru_q_lane *qlane = &CHUNK_LRU[lru->lane]; QLOCK(qlane); lru_insert(lru, q, edge); QUNLOCK(qlane); } /** * @brief Clean an entry for recycling. * * This function cleans an entry up before it's recycled or freed. * * @param[in] entry The entry to clean */ static inline void mdcache_lru_clean(mdcache_entry_t *entry) { fsal_status_t status = {0, 0}; /* Free SubFSAL resources */ if (entry->sub_handle) { /* There are four basic paths to get here. * * One path is that this cache entry is being reaped. In that * case, if an unexport is in progress removing the last export * this entry was mapped to, in the process of being completely * detached from an export, it also became unreapable (placed on * the LRU_ENTRY_CLEANUP queue not L1 or L2). Therefore, if we * get here with a reaped entry, it MUST still be attached to * an export. * * Another path to get here is the export is still valid, and * this entry is being killed. In that case, all the export * stuff is fine. * * Another path is that we have removed the final export, and * unexport is releasing the last reference. In that case, * the unexport process has the export in question in the op_ctx * so we are fine. * * The final case is that this entry was referenced by a thread * other than the unexport, and the operational thread is the * one releasing the last LRU reference. In that case, the * caller's op_ctx must have the correct export. * * This is true even for operations that require two handles. * NFS v3 checks for xdev before converting from a handle to an * LRU reference. NFS v4 holds an LRU reference for the saved FH * so the last reference can only be dropped when the saved FH * is cleaned up, which will be done with the correct op_ctx. 9P * also assures that LRU references are released with the proper * op_ctx. * * So in all cases, we can either trust the current export, or * we can use the first_export_id to get a valid export for * a reaping case. */ struct root_op_context ctx; struct req_op_context *saved_ctx = op_ctx; int32_t export_id; struct gsh_export *export; /* Find the first export id. */ export_id = atomic_fetch_int32_t(&entry->first_export_id); if (export_id >= 0 && op_ctx != NULL && op_ctx->ctx_export != NULL && op_ctx->ctx_export->export_id != export_id) { /* We can't be sure the op_ctx has a valid export_id for * this entry, so we'll use the first export_id and set * up a new op_ctx. * * Get a reference to the first_export_id. */ export = get_gsh_export(export_id); if (export == NULL) { /* This really should not happen, if an unexport * is in progress, the export_id is now not * removed until after mdcache has detached all * entries from the export. An entry that is * actually in the process of being detached has * an LRU reference which prevents it from being * reaped, so there is no path to get into * mdcache_lru_clean without the export still * being valid. */ LogFatal(COMPONENT_CACHE_INODE, "An entry (%p) having an unmappable export_id (%" PRIi32") is unexpected", entry, export_id); } LogFullDebug(COMPONENT_CACHE_INODE, "Creating a new context with export id%" PRIi32, export_id); init_root_op_context(&ctx, export, export->fsal_export, 0, 0, UNKNOWN_REQUEST); } else { /* We MUST have a valid op_ctx based on the conditions * we could get here. first_export_id coild be -1 or it * could match the current op_ctx export. In either case * we will trust the current op_ctx. */ assert(op_ctx); assert(op_ctx->ctx_export); LogFullDebug(COMPONENT_CACHE_INODE, "Trusting op_ctx export id %"PRIu16, op_ctx->ctx_export->export_id); } /* Make sure any FSAL global file descriptor is closed. * Don't bother with the content_lock since we have exclusive * ownership of this entry. */ status = fsal_close(&entry->obj_handle); if (FSAL_IS_ERROR(status)) { LogCrit(COMPONENT_CACHE_INODE_LRU, "Error closing file in cleanup: %s", fsal_err_txt(status)); } subcall( entry->sub_handle->obj_ops.release(entry->sub_handle) ); entry->sub_handle = NULL; if (op_ctx != saved_ctx) { /* We had to use our own op_ctx, clean it up and revert * to the saved op_ctx. */ put_gsh_export(op_ctx->ctx_export); op_ctx = saved_ctx; } } /* Done with the attrs */ fsal_release_attrs(&entry->attrs); /* Clean our handle */ fsal_obj_handle_fini(&entry->obj_handle); /* Clean out the export mapping before deconstruction */ mdc_clean_entry(entry); /* Finalize last bits of the cache entry, delete the key if any and * destroy the rw locks. */ mdcache_key_delete(&entry->fh_hk.key); PTHREAD_RWLOCK_destroy(&entry->content_lock); PTHREAD_RWLOCK_destroy(&entry->attr_lock); } /** * @brief Try to pull an entry off the queue * * This function examines the end of the specified queue and if the * entry found there can be re-used, it returns with the entry * locked. Otherwise, it returns NULL. * * This function follows the locking discipline detailed above. It * returns an lru entry removed from the queue system and which we are * permitted to dispose or recycle. * * @note The caller @a MUST @a NOT hold the lane lock * * @param[in] qid Queue to reap * @return Available entry if found, NULL otherwise */ static uint32_t reap_lane; static inline mdcache_lru_t * lru_reap_impl(enum lru_q_id qid) { uint32_t lane; struct lru_q_lane *qlane; struct lru_q *lq; mdcache_lru_t *lru; mdcache_entry_t *entry; uint32_t refcnt; cih_latch_t latch; int ix; lane = LRU_NEXT(reap_lane); for (ix = 0; ix < LRU_N_Q_LANES; ++ix, lane = LRU_NEXT(reap_lane)) { qlane = &LRU[lane]; lq = (qid == LRU_ENTRY_L1) ? &qlane->L1 : &qlane->L2; QLOCK(qlane); lru = glist_first_entry(&lq->q, mdcache_lru_t, q); if (!lru) { QUNLOCK(qlane); continue; } refcnt = atomic_inc_int32_t(&lru->refcnt); entry = container_of(lru, mdcache_entry_t, lru); QUNLOCK(qlane); if (unlikely(refcnt != (LRU_SENTINEL_REFCOUNT + 1))) { /* cant use it. */ mdcache_put(entry); continue; } /* potentially reclaimable */ /* entry must be unreachable from CIH when recycled */ if (cih_latch_entry(&entry->fh_hk.key, &latch, CIH_GET_WLOCK, __func__, __LINE__)) { QLOCK(qlane); refcnt = atomic_fetch_int32_t(&entry->lru.refcnt); /* there are two cases which permit reclaim, * entry is: * 1. reachable but unref'd (refcnt==2) * 2. unreachable, being removed (plus refcnt==0) * for safety, take only the former */ if (LRU_ENTRY_RECLAIMABLE(entry, refcnt)) { /* it worked */ struct lru_q *q = lru_queue_of(entry); #ifdef USE_LTTNG tracepoint(mdcache, mdc_lru_reap, __func__, __LINE__, entry, entry->lru.refcnt); #endif LRU_DQ_SAFE(lru, q); entry->lru.qid = LRU_ENTRY_NONE; QUNLOCK(qlane); cih_remove_latched(entry, &latch, CIH_REMOVE_UNLOCK); /* Note, we're not releasing our ref here. * cih_remove_latched() called * mdcache_lru_unref(), which released the * sentinal ref, leaving just the one ref we * took earlier. Returning this as is leaves it * with a ref of 1 (ie, just the sentinal ref) * */ goto out; } cih_hash_release(&latch); QUNLOCK(qlane); /* return the ref we took above--unref deals * correctly with reclaim case */ mdcache_lru_unref(entry); } else { /* ! QLOCKED but needs to be Unref'ed */ mdcache_lru_unref(entry); continue; } } /* foreach lane */ /* ! reclaimable */ lru = NULL; out: return lru; } static inline mdcache_lru_t * lru_try_reap_entry(void) { mdcache_lru_t *lru; if (lru_state.entries_used < lru_state.entries_hiwat) return NULL; /* XXX dang why not start with the cleanup list? */ lru = lru_reap_impl(LRU_ENTRY_L2); if (!lru) lru = lru_reap_impl(LRU_ENTRY_L1); return lru; } /** * @brief Try to pull an chunk off the queue * * This function examines the end of the specified queue and if the * chunk found there can be re-used. Otherwise, it returns NULL. * * This function follows the locking discipline detailed above. It * returns an lru object removed from the queue system and which we are * permitted to dispose or recycle. * * This function can reap a chunk from the directory a chunk is requested * for. In that case, since the content_lock is already held, we can * proceed somewhat easier. * * @note The caller @a MUST @a NOT hold the lane lock * * @param[in] qid Queue to reap * @param[in] parent The directory we desire a chunk for * * @return Available chunk if found, NULL otherwise */ static uint32_t chunk_reap_lane; static inline mdcache_lru_t * lru_reap_chunk_impl(enum lru_q_id qid, mdcache_entry_t *parent) { uint32_t lane; struct lru_q_lane *qlane; struct lru_q *lq; mdcache_lru_t *lru; mdcache_entry_t *entry; struct dir_chunk *chunk; int ix; lane = LRU_NEXT(chunk_reap_lane); for (ix = 0; ix < LRU_N_Q_LANES; ++ix, lane = LRU_NEXT(chunk_reap_lane)) { qlane = &CHUNK_LRU[lane]; lq = (qid == LRU_ENTRY_L1) ? &qlane->L1 : &qlane->L2; QLOCK(qlane); lru = glist_first_entry(&lq->q, mdcache_lru_t, q); if (!lru) { QUNLOCK(qlane); continue; } /* Get the chunk and parent entry that owns the chunk, all of * this is valid because we hold the QLANE lock, the chunk was * in the LRU, and thus the chunk is not yet being destroyed, * and thus the parent entry must still also be valid. */ chunk = container_of(lru, struct dir_chunk, chunk_lru); entry = chunk->parent; /* If this chunk belongs to the parent seeking another chunk, * or if we can get the content_lock for the chunk's parent, * we can reap this chunk. */ if (entry == parent || pthread_rwlock_trywrlock(&entry->content_lock) == 0) { /* This chunk is eligible for reaping, we can proceed. */ if (entry != parent) { /* We need an LRU ref on parent entry to protect * it while we do work on it's chunk. */ (void) atomic_inc_int32_t(&entry->lru.refcnt); } /* Dequeue the chunk so it won't show up anymore */ CHUNK_LRU_DQ_SAFE(lru, lq); chunk->chunk_lru.qid = LRU_ENTRY_NONE; /* Drop the lane lock, we can now safely clean up the * chunk. We hold the content_lock on the parent of * the chunk (even if the chunk belonged to the * directory a new chunk is requested for). */ QUNLOCK(qlane); #ifdef USE_LTTNG tracepoint(mdcache, mdc_lru_reap_chunk, __func__, __LINE__, entry, chunk); #endif /* Clean the chunk out and indicate the directory is no * longer completely populated. */ mdcache_clean_dirent_chunk(chunk); atomic_clear_uint32_t_bits(&entry->mde_flags, MDCACHE_DIR_POPULATED); /* Clean out the fields not touched by the cleanup. */ chunk->parent = NULL; chunk->prev_chunk = NULL; chunk->next_ck = 0; chunk->num_entries = 0; if (entry != parent) { /* And now we're done with the parent of the * chunk if it wasn't the directory we are * acquiring a new chunk for. */ PTHREAD_RWLOCK_unlock(&entry->content_lock); mdcache_put(entry); } return lru; } /* Couldn't get the content_lock, the parent is busy * doing something with dirents... This chunk is not * eligible for reaping. Try the next lane... */ QUNLOCK(qlane); } /* foreach lane */ /* ! reclaimable */ return NULL; } /** * @brief Re-use or allocate a chunk * * This function repurposes a resident chunk in the LRU system if the system is * above the high-water mark, and allocates a new one otherwise. * * @note The caller must hold the content_lock of the parent for write. * * @param[in] parent The parent directory we desire a chunk for * * @return reused or allocated chunk */ struct dir_chunk *mdcache_get_chunk(mdcache_entry_t *parent) { mdcache_lru_t *lru = NULL; struct dir_chunk *chunk = NULL; if (lru_state.chunks_used >= lru_state.chunks_hiwat) { lru = lru_reap_chunk_impl(LRU_ENTRY_L2, parent); if (!lru) lru = lru_reap_chunk_impl(LRU_ENTRY_L1, parent); } if (lru) { /* we uniquely hold chunk, it has already been cleaned up. * The dirents list is effectively properly initialized. */ chunk = container_of(lru, struct dir_chunk, chunk_lru); LogFullDebug(COMPONENT_CACHE_INODE, "Recycling chunk at %p.", chunk); } else { /* alloc chunk (if fails, aborts) */ chunk = gsh_calloc(1, sizeof(struct dir_chunk)); glist_init(&chunk->dirents); LogFullDebug(COMPONENT_CACHE_INODE, "New chunk %p.", chunk); (void) atomic_inc_int64_t(&lru_state.chunks_used); } /* Set the chunk's parent. */ chunk->parent = parent; /* Chunk refcnt is not used (chunks are always protected by content_lock * and outside of LRU operations are not found other than while holding * the content lock). */ chunk->chunk_lru.refcnt = 0; chunk->chunk_lru.cf = 0; chunk->chunk_lru.lane = lru_lane_of(chunk); /* Enqueue into MRU of L2. * * NOTE: A newly allocated and filled chunk will be promoted to L1 LRU * when readdir_chunked starts passing entries up to the caller. * This gets us the expected positioning for a new chunk that is * utilized to form a readdir response. * * The benefit of this mechanism comes when the FSAL supports * readahead. In that case, the chunks that are readahead will * be left in L2 MRU. This helps keep the chunks associated with * a particular FSAL readdir call including readahead from being * immediate candidates for reaping, thus keeping the readahead * from cannibalizing itself. Of course if the L2 queue is * empty due to activity, and the readahead is significant, it * is possible to cannibalize the chunks. */ lru_insert_chunk(chunk, &CHUNK_LRU[chunk->chunk_lru.lane].L2, LRU_MRU); return chunk; } /** * @brief Push a killed entry to the cleanup queue for out-of-line cleanup * * This function appends entry to the appropriate lane of the global cleanup * queue, and marks the entry. * * @param[in] entry The entry to clean */ void mdcache_lru_cleanup_push(mdcache_entry_t *entry) { mdcache_lru_t *lru = &entry->lru; struct lru_q_lane *qlane = &LRU[lru->lane]; QLOCK(qlane); if (!(lru->qid == LRU_ENTRY_CLEANUP)) { struct lru_q *q; /* out with the old queue */ q = lru_queue_of(entry); LRU_DQ_SAFE(lru, q); /* in with the new */ q = &qlane->cleanup; lru_insert(lru, q, LRU_LRU); } QUNLOCK(qlane); } /** * @brief Push an entry to the cleanup queue that may be unexported * for out-of-line cleanup * * This routine is used to try pushing a cache inode into the cleanup * queue. If the entry ends up with another LRU reference before this * is accomplished, then don't push it to cleanup. * * This will be used when unexporting an export. Any cache inode entry * that only belonged to that export is a candidate for cleanup. * However, it is possible the entry is still accessible via another * export, and an LRU reference might be gained before we can lock the * AVL tree. In that case, the entry must be left alone (thus * mdcache_kill_entry is NOT suitable for this purpose). * * @param[in] entry The entry to clean */ void mdcache_lru_cleanup_try_push(mdcache_entry_t *entry) { mdcache_lru_t *lru = &entry->lru; struct lru_q_lane *qlane = &LRU[lru->lane]; cih_latch_t latch; if (cih_latch_entry(&entry->fh_hk.key, &latch, CIH_GET_WLOCK, __func__, __LINE__)) { uint32_t refcnt; QLOCK(qlane); refcnt = atomic_fetch_int32_t(&entry->lru.refcnt); /* there are two cases which permit reclaim, * entry is: * 1. reachable but unref'd (refcnt==2) * 2. unreachable, being removed (plus refcnt==0) * for safety, take only the former */ if (LRU_ENTRY_RECLAIMABLE(entry, refcnt)) { /* it worked */ struct lru_q *q = lru_queue_of(entry); LRU_DQ_SAFE(lru, q); entry->lru.qid = LRU_ENTRY_CLEANUP; atomic_set_uint32_t_bits(&entry->lru.flags, LRU_CLEANUP); /* Note: we didn't take a ref here, so the only ref left * is the one owned by mdcache_unexport(). When it * unref's, that will free this object. */ /* Now we can safely clean out the first_export_id to * indicate this entry is unmapped. */ atomic_store_int32_t(&entry->first_export_id, -1); QUNLOCK(qlane); cih_remove_latched(entry, &latch, CIH_REMOVE_NONE); } else { QUNLOCK(qlane); } cih_hash_release(&latch); } } /** * @brief Function that executes in the lru thread to process one lane * * @param[in] lane The lane to process * @param[in,out] totalclosed Track the number of file closes * * @returns the number of files worked on (workdone) * */ static inline size_t lru_run_lane(size_t lane, uint64_t *const totalclosed) { struct lru_q *q; /* The amount of work done on this lane on this pass. */ size_t workdone = 0; /* The entry being examined */ mdcache_lru_t *lru = NULL; /* Number of entries closed in this run. */ size_t closed = 0; fsal_status_t status; /* a cache entry */ mdcache_entry_t *entry; /* Current queue lane */ struct lru_q_lane *qlane = &LRU[lane]; /* entry refcnt */ uint32_t refcnt; q = &qlane->L1; LogDebug(COMPONENT_CACHE_INODE_LRU, "Reaping up to %d entries from lane %zd", lru_state.per_lane_work, lane); /* ACTIVE */ QLOCK(qlane); qlane->iter.active = true; /* While for_each_safe per se is NOT MT-safe, the iteration can be made * so by the convention that any competing thread which would invalidate * the iteration also adjusts glist and (in particular) glistn */ glist_for_each_safe(qlane->iter.glist, qlane->iter.glistn, &q->q) { struct lru_q *q; struct root_op_context ctx; struct req_op_context *saved_ctx = op_ctx; int32_t export_id; struct gsh_export *export; /* check per-lane work */ if (workdone >= lru_state.per_lane_work) goto next_lane; lru = glist_entry(qlane->iter.glist, mdcache_lru_t, q); refcnt = atomic_inc_int32_t(&lru->refcnt); /* get entry early */ entry = container_of(lru, mdcache_entry_t, lru); /* Get a reference to the first export and build an op context * with it. By holding the QLANE lock while we get the export * reference we assure that the entry doesn't get detached from * the export before we get an export reference, which * guarantees the export is good for the length of time we need * it to perform sub_fsal operations. */ export_id = atomic_fetch_int32_t(&entry->first_export_id); if (export_id < 0) { /* This should never happen, since any entry that only * had a sentinel reference must either have a mapped * export or be in the LRU_ENTRY_CLEANUP queue and thus * not eligible for lru_run_lane. */ LogFatal(COMPONENT_CACHE_INODE, "No first_export for entry %p is unexpected.", entry); } export = get_gsh_export(export_id); if (export == NULL) { /* This really should not happen, if an unexport is in * progress, the export_id is now not removed until * after mdcache has detached all entries from the * export. An entry that is actually in the process of * being detached has an LRU reference which prevents it * from being processed by lru_run_lane, so there is no * path to get here without the export still being * valid. */ LogFatal(COMPONENT_CACHE_INODE, "An entry (%p) having an unmappable export_id (%" PRIi32") is unexpected", entry, export_id); } init_root_op_context(&ctx, export, export->fsal_export, 0, 0, UNKNOWN_REQUEST); /* check refcnt in range */ if (unlikely(refcnt > 2)) { /* This unref is ok to be done without a valid op_ctx * because we always map a new entry to an export before * we could possibly release references in * mdcache_new_entry. */ QUNLOCK(qlane); mdcache_lru_unref(entry); goto next_lru; } /* Move entry to MRU of L2 */ q = &qlane->L1; LRU_DQ_SAFE(lru, q); lru->qid = LRU_ENTRY_L2; q = &qlane->L2; lru_insert(lru, q, LRU_MRU); /* Drop the lane lock while performing (slow) operations on * entry */ QUNLOCK(qlane); /* Make sure any FSAL global file descriptor is closed. */ status = fsal_close(&entry->obj_handle); if (FSAL_IS_ERROR(status)) { LogCrit(COMPONENT_CACHE_INODE_LRU, "Error closing file in LRU thread."); } else { ++(*totalclosed); ++closed; } mdcache_lru_unref(entry); next_lru: QLOCK(qlane); /* QLOCKED */ put_gsh_export(export); op_ctx = saved_ctx; ++workdone; } /* for_each_safe lru */ next_lane: qlane->iter.active = false; /* !ACTIVE */ QUNLOCK(qlane); LogDebug(COMPONENT_CACHE_INODE_LRU, "Actually processed %zd entries on lane %zd closing %zd descriptors", workdone, lane, closed); return workdone; } /** * @brief Function that executes in the lru thread * * This function performs long-term reorganization, compaction, and * other operations that are not performed in-line with referencing * and dereferencing. * * This function is responsible for deferred cleanup of cache entries * killed in request or upcall (or most other) contexts. * * This function is responsible for cleaning the FD cache. It works * by the following rules: * * - If the number of open FDs is below the low water mark, do * nothing. * * - If the number of open FDs is between the low and high water * mark, make one pass through the queues, and exit. Each pass * consists of taking an entry from L1, examining to see if it is a * regular file not bearing state with an open FD, closing the open * FD if it is, and then moving it to L2. The advantage of the two * level system is twofold: First, seldom used entries congregate * in L2 and the promotion behaviour provides some scan * resistance. Second, once an entry is examined, it is moved to * L2, so we won't examine the same cache entry repeatedly. * * - If the number of open FDs is greater than the high water mark, * we consider ourselves to be in extremis. In this case we make a * number of passes through the queue not to exceed the number of * passes that would be required to process the number of entries * equal to a biggest_window percent of the system specified * maximum. * * - If we are in extremis, and performing the maximum amount of work * allowed has not moved the open FD count required_progress% * toward the high water mark, increment lru_state.futility. If * lru_state.futility reaches futility_count, temporarily disable * FD caching. * * - Every time we wake through timeout, reset futility_count to 0. * * - If we fall below the low water mark and FD caching has been * temporarily disabled, re-enable it. * * This function uses the lock discipline for functions accessing LRU * entries through a queue partition. * * @param[in] ctx Fridge context */ static void lru_run(struct fridgethr_context *ctx) { /* Index */ size_t lane = 0; /* True if we were explicitly awakened. */ bool woke = ctx->woke; /* Finalized */ uint32_t fdratepersec = 1, fds_avg, fddelta; float fdnorm, fdwait_ratio, fdmulti; time_t threadwait = fridgethr_getwait(ctx); /* True if we are taking extreme measures to reclaim FDs */ bool extremis = false; /* Total work done in all passes so far. If this exceeds the * window, stop. */ size_t totalwork = 0; uint64_t totalclosed = 0; /* The current count (after reaping) of open FDs */ size_t currentopen = 0; time_t new_thread_wait; SetNameFunction("cache_lru"); fds_avg = (lru_state.fds_hiwat - lru_state.fds_lowat) / 2; if (mdcache_param.use_fd_cache) extremis = (atomic_fetch_size_t(&open_fd_count) > lru_state.fds_hiwat); LogFullDebug(COMPONENT_CACHE_INODE_LRU, "LRU awakes."); if (!woke) { /* If we make it all the way through a timed sleep without being woken, we assume we aren't racing against the impossible. */ lru_state.futility = 0; } LogFullDebug(COMPONENT_CACHE_INODE_LRU, "lru entries: %" PRIu64, lru_state.entries_used); /* Reap file descriptors. This is a preliminary example of the L2 functionality rather than something we expect to be permanent. (It will have to adapt heavily to the new FSAL API, for example.) */ currentopen = atomic_fetch_size_t(&open_fd_count); if ((currentopen < lru_state.fds_lowat) && mdcache_param.use_fd_cache) { LogDebug(COMPONENT_CACHE_INODE_LRU, "FD count is %zd and low water mark is %d: not reaping.", atomic_fetch_size_t(&open_fd_count), lru_state.fds_lowat); if (mdcache_param.use_fd_cache && !lru_state.caching_fds) { lru_state.caching_fds = true; LogEvent(COMPONENT_CACHE_INODE_LRU, "Re-enabling FD cache."); } } else { /* The count of open file descriptors before this run of the reaper. */ size_t formeropen = atomic_fetch_size_t(&open_fd_count); /* Work done in the most recent pass of all queues. if value is less than the work to do in a single queue, don't spin through more passes. */ size_t workpass = 0; time_t curr_time = time(NULL); fdratepersec = (curr_time <= lru_state.prev_time) ? 1 : (formeropen - lru_state.prev_fd_count) / (curr_time - lru_state.prev_time); LogFullDebug(COMPONENT_CACHE_INODE_LRU, "fdrate:%u fdcount:%zd slept for %" PRIu64 " sec", fdratepersec, formeropen, ((uint64_t) (curr_time - lru_state.prev_time))); if (extremis) { LogDebug(COMPONENT_CACHE_INODE_LRU, "Open FDs over high water mark, reapring aggressively."); } /* Total fds closed between all lanes and all current runs. */ do { workpass = 0; for (lane = 0; lane < LRU_N_Q_LANES; ++lane) { LogDebug(COMPONENT_CACHE_INODE_LRU, "Reaping up to %d entries from lane %zd", lru_state.per_lane_work, lane); LogFullDebug(COMPONENT_CACHE_INODE_LRU, "formeropen=%zd totalwork=%zd workpass=%zd totalclosed:%" PRIu64, formeropen, totalwork, workpass, totalclosed); workpass += lru_run_lane(lane, &totalclosed); } totalwork += workpass; } while (extremis && (workpass >= lru_state.per_lane_work) && (totalwork < lru_state.biggest_window)); currentopen = atomic_fetch_size_t(&open_fd_count); if (extremis && ((currentopen > formeropen) || (formeropen - currentopen < (((formeropen - lru_state.fds_hiwat) * mdcache_param.required_progress) / 100)))) { if (++lru_state.futility > mdcache_param.futility_count) { LogCrit(COMPONENT_CACHE_INODE_LRU, "Futility count exceeded. The LRU thread is unable to make progress in reclaiming FDs. Disabling FD cache."); lru_state.caching_fds = false; } } } /* The following calculation will progressively garbage collect * more frequently as these two factors increase: * 1. current number of open file descriptors * 2. rate at which file descriptors are being used. * * When there is little activity, this thread will sleep at the * "LRU_Run_Interval" from the config. * * When there is a lot of activity, the thread will sleep for a * much shorter time. */ lru_state.prev_fd_count = currentopen; lru_state.prev_time = time(NULL); fdnorm = (fdratepersec + fds_avg) / fds_avg; fddelta = (currentopen > lru_state.fds_lowat) ? (currentopen - lru_state.fds_lowat) : 0; fdmulti = (fddelta * 10) / fds_avg; fdmulti = fdmulti ? fdmulti : 1; fdwait_ratio = lru_state.fds_hiwat / ((lru_state.fds_hiwat + fdmulti * fddelta) * fdnorm); new_thread_wait = threadwait * fdwait_ratio; if (new_thread_wait < mdcache_param.lru_run_interval / 10) new_thread_wait = mdcache_param.lru_run_interval / 10; fridgethr_setwait(ctx, new_thread_wait); LogDebug(COMPONENT_CACHE_INODE_LRU, "After work, open_fd_count:%zd count:%" PRIu64 " fdrate:%u threadwait=%" PRIu64, atomic_fetch_size_t(&open_fd_count), lru_state.entries_used, fdratepersec, ((uint64_t) threadwait)); LogFullDebug(COMPONENT_CACHE_INODE_LRU, "currentopen=%zd futility=%d totalwork=%zd biggest_window=%d extremis=%d lanes=%d fds_lowat=%d ", currentopen, lru_state.futility, totalwork, lru_state.biggest_window, extremis, LRU_N_Q_LANES, lru_state.fds_lowat); } /** * @brief Function that executes in the lru thread to process one lane * * This function really just demotes chunks from L1 to L2, so very simple. * * @param[in] lane The lane to process * * @returns the number of chunks worked on (workdone) * */ static inline size_t chunk_lru_run_lane(size_t lane) { struct lru_q *q; /* The amount of work done on this lane on this pass. */ size_t workdone = 0; /* The lru object being examined */ mdcache_lru_t *lru = NULL; /* Current queue lane */ struct lru_q_lane *qlane = &CHUNK_LRU[lane]; q = &qlane->L1; LogFullDebug(COMPONENT_CACHE_INODE_LRU, "Reaping up to %d chunks from lane %zd", lru_state.per_lane_work, lane); /* ACTIVE */ QLOCK(qlane); qlane->iter.active = true; /* While for_each_safe per se is NOT MT-safe, the iteration can be made * so by the convention that any competing thread which would invalidate * the iteration also adjusts glist and (in particular) glistn */ glist_for_each_safe(qlane->iter.glist, qlane->iter.glistn, &q->q) { struct lru_q *q; /* check per-lane work */ if (workdone >= lru_state.per_lane_work) goto next_lane; lru = glist_entry(qlane->iter.glist, mdcache_lru_t, q); /* Move lru object to MRU of L2 */ q = &qlane->L1; LRU_DQ_SAFE(lru, q); lru->qid = LRU_ENTRY_L2; q = &qlane->L2; lru_insert(lru, q, LRU_MRU); ++workdone; } /* for_each_safe lru */ next_lane: qlane->iter.active = false; /* !ACTIVE */ QUNLOCK(qlane); LogFullDebug(COMPONENT_CACHE_INODE_LRU, "Actually processed %zd chunks on lane %zd", workdone, lane); return workdone; } /** * @brief Function that executes in the lru thread * * This function reorganizes the L1 and L2 queues, demoting least recently * used L1 chunks to L2. * * This function uses the lock discipline for functions accessing LRU * entries through a queue partition. * * @param[in] ctx Fridge context */ static void chunk_lru_run(struct fridgethr_context *ctx) { /* Index */ size_t lane; /* A ratio computed to adjust wait time based on how close to high * water mark for number of chunks we are. */ float wait_ratio; /* The computed wait time. */ time_t new_thread_wait; /* Total work done (number of chunks demoted) across all lanes. */ size_t totalwork = 0; SetNameFunction("chunk_lru"); LogFullDebug(COMPONENT_CACHE_INODE_LRU, "LRU awakes, lru chunks used: %" PRIu64, lru_state.chunks_used); /* Total chunks demoted to L2 between all lanes and all current runs. */ for (lane = 0; lane < LRU_N_Q_LANES; ++lane) { LogFullDebug(COMPONENT_CACHE_INODE_LRU, "Reaping up to %d chunks from lane %zd totalwork=%zd", lru_state.per_lane_work, lane, totalwork); totalwork += chunk_lru_run_lane(lane); } /* Run more frequently the closer to max number of chunks we are. */ wait_ratio = 1.0 - (lru_state.chunks_used / lru_state.chunks_hiwat); new_thread_wait = mdcache_param.lru_run_interval * wait_ratio; if (new_thread_wait < mdcache_param.lru_run_interval / 10) new_thread_wait = mdcache_param.lru_run_interval / 10; fridgethr_setwait(ctx, new_thread_wait); LogDebug(COMPONENT_CACHE_INODE_LRU, "After work, threadwait=%" PRIu64 " totalwork=%zd", ((uint64_t) new_thread_wait), totalwork); } void init_fds_limit(void) { int code = 0; /* Rlimit for open file descriptors */ struct rlimit rlim = { .rlim_cur = RLIM_INFINITY, .rlim_max = RLIM_INFINITY }; /* Find out the system-imposed file descriptor limit */ if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) { code = errno; LogCrit(COMPONENT_CACHE_INODE_LRU, "Call to getrlimit failed with error %d. This should not happen. Assigning default of %d.", code, FD_FALLBACK_LIMIT); lru_state.fds_system_imposed = FD_FALLBACK_LIMIT; } else { if (rlim.rlim_cur < rlim.rlim_max) { /* Save the old soft value so we can fall back to it if setrlimit fails. */ rlim_t old_soft = rlim.rlim_cur; LogInfo(COMPONENT_CACHE_INODE_LRU, "Attempting to increase soft limit from %" PRIu64 " to hard limit of %" PRIu64, (uint64_t) rlim.rlim_cur, (uint64_t) rlim.rlim_max); rlim.rlim_cur = rlim.rlim_max; if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) { code = errno; LogWarn(COMPONENT_CACHE_INODE_LRU, "Attempt to raise soft FD limit to hard FD limit failed with error %d. Sticking to soft limit.", code); rlim.rlim_cur = old_soft; } } if (rlim.rlim_cur == RLIM_INFINITY) { FILE *nr_open; nr_open = fopen("/proc/sys/fs/nr_open", "r"); if (nr_open == NULL) { code = errno; LogWarn(COMPONENT_CACHE_INODE_LRU, "Attempt to open /proc/sys/fs/nr_open failed (%d)", code); goto err_open; } code = fscanf(nr_open, "%" SCNu32 "\n", &lru_state.fds_system_imposed); if (code != 1) { code = errno; LogMajor(COMPONENT_CACHE_INODE_LRU, "The rlimit on open file descriptors is infinite and the attempt to find the system maximum failed with error %d.", code); LogMajor(COMPONENT_CACHE_INODE_LRU, "Assigning the default fallback of %d which is almost certainly too small.", FD_FALLBACK_LIMIT); LogMajor(COMPONENT_CACHE_INODE_LRU, "If you are on a Linux system, this should never happen."); LogMajor(COMPONENT_CACHE_INODE_LRU, "If you are running some other system, please set an rlimit on file descriptors (for example, with ulimit) for this process and consider editing " __FILE__ "to add support for finding your system's maximum."); lru_state.fds_system_imposed = FD_FALLBACK_LIMIT; } fclose(nr_open); err_open: ; } else { lru_state.fds_system_imposed = rlim.rlim_cur; } LogInfo(COMPONENT_CACHE_INODE_LRU, "Setting the system-imposed limit on FDs to %d.", lru_state.fds_system_imposed); } lru_state.fds_hard_limit = (mdcache_param.fd_limit_percent * lru_state.fds_system_imposed) / 100; lru_state.fds_hiwat = (mdcache_param.fd_hwmark_percent * lru_state.fds_system_imposed) / 100; lru_state.fds_lowat = (mdcache_param.fd_lwmark_percent * lru_state.fds_system_imposed) / 100; lru_state.futility = 0; if (mdcache_param.reaper_work) { /* Backwards compatibility */ lru_state.per_lane_work = (mdcache_param.reaper_work + LRU_N_Q_LANES - 1) / LRU_N_Q_LANES; } else { /* New parameter */ lru_state.per_lane_work = mdcache_param.reaper_work_per_lane; } lru_state.biggest_window = (mdcache_param.biggest_window * lru_state.fds_system_imposed) / 100; } /* Public functions */ /** * Initialize subsystem */ fsal_status_t mdcache_lru_pkginit(void) { /* Return code from system calls */ int code = 0; struct fridgethr_params frp; memset(&frp, 0, sizeof(struct fridgethr_params)); frp.thr_max = 2; frp.thr_min = 2; frp.thread_delay = mdcache_param.lru_run_interval; frp.flavor = fridgethr_flavor_looper; atomic_store_size_t(&open_fd_count, 0); lru_state.prev_fd_count = 0; lru_state.caching_fds = mdcache_param.use_fd_cache; init_fds_limit(); /* Set high and low watermark for cache entries. XXX This seems a bit fishy, so come back and revisit this. */ lru_state.entries_hiwat = mdcache_param.entries_hwmark; lru_state.entries_used = 0; /* Set high and low watermark for chunks. XXX This seems a bit fishy, so come back and revisit this. */ lru_state.chunks_hiwat = mdcache_param.chunks_hwmark; lru_state.chunks_used = 0; /* init queue complex */ lru_init_queues(); /* spawn LRU background thread */ code = fridgethr_init(&lru_fridge, "LRU_fridge", &frp); if (code != 0) { LogMajor(COMPONENT_CACHE_INODE_LRU, "Unable to initialize LRU fridge, error code %d.", code); return fsalstat(posix2fsal_error(code), code); } code = fridgethr_submit(lru_fridge, lru_run, NULL); if (code != 0) { LogMajor(COMPONENT_CACHE_INODE_LRU, "Unable to start Entry LRU thread, error code %d.", code); return fsalstat(posix2fsal_error(code), code); } code = fridgethr_submit(lru_fridge, chunk_lru_run, NULL); if (code != 0) { LogMajor(COMPONENT_CACHE_INODE_LRU, "Unable to start Chunk LRU thread, error code %d.", code); return fsalstat(posix2fsal_error(code), code); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * Shutdown subsystem * * @return 0 on success, POSIX errors on failure. */ fsal_status_t mdcache_lru_pkgshutdown(void) { int rc = fridgethr_sync_command(lru_fridge, fridgethr_comm_stop, 120); if (rc == ETIMEDOUT) { LogMajor(COMPONENT_CACHE_INODE_LRU, "Shutdown timed out, cancelling threads."); fridgethr_cancel(lru_fridge); } else if (rc != 0) { LogMajor(COMPONENT_CACHE_INODE_LRU, "Failed shutting down LRU thread: %d", rc); } return fsalstat(posix2fsal_error(rc), rc); } static inline void init_rw_locks(mdcache_entry_t *entry) { /* Initialize the entry locks */ PTHREAD_RWLOCK_init(&entry->attr_lock, NULL); PTHREAD_RWLOCK_init(&entry->content_lock, NULL); } mdcache_entry_t *alloc_cache_entry(void) { mdcache_entry_t *nentry; nentry = pool_alloc(mdcache_entry_pool); /* Initialize the entry locks */ init_rw_locks(nentry); (void) atomic_inc_int64_t(&lru_state.entries_used); return nentry; } /** * @brief Re-use or allocate an entry * * This function repurposes a resident entry in the LRU system if the system is * above the high-water mark, and allocates a new one otherwise. On success, * this function always returns an entry with two references (one for the * sentinel, one to allow the caller's use.) * * The caller MUST call mdcache_lru_insert when the entry is sufficiently * constructed. * * @return a usable entry or NULL if unexport is in progress. */ mdcache_entry_t *mdcache_lru_get(void) { mdcache_lru_t *lru; mdcache_entry_t *nentry = NULL; lru = lru_try_reap_entry(); if (lru) { /* we uniquely hold entry */ nentry = container_of(lru, mdcache_entry_t, lru); LogFullDebug(COMPONENT_CACHE_INODE_LRU, "Recycling entry at %p.", nentry); mdcache_lru_clean(nentry); memset(&nentry->attrs, 0, sizeof(nentry->attrs)); init_rw_locks(nentry); } else { /* alloc entry (if fails, aborts) */ nentry = alloc_cache_entry(); } /* Since the entry isn't in a queue, nobody can bump refcnt. */ nentry->lru.refcnt = 2; nentry->lru.cf = 0; nentry->lru.lane = lru_lane_of(nentry); #ifdef USE_LTTNG tracepoint(mdcache, mdc_lru_get, __func__, __LINE__, nentry, nentry->lru.refcnt); #endif return nentry; } /** * @brief Insert a new entry into the LRU. * * Entry is inserted into LRU of L1 queue. * * @param [in] ntry Entry to insert. */ void mdcache_lru_insert(mdcache_entry_t *entry) { /* Enqueue. */ lru_insert_entry(entry, &LRU[entry->lru.lane].L1, LRU_LRU); } /** * @brief Get a reference * * This function acquires a reference on the given cache entry. * * @param[in] entry The entry on which to get a reference * @param[in] flags One of LRU_REQ_INITIAL, or LRU_FLAG_NONE * * A flags value of LRU_REQ_INITIAL indicates an initial * reference. A non-initial reference is an "extra" reference in some call * path, hence does not influence LRU, and is lockless. * * A flags value of LRU_REQ_INITIAL indicates an ordinary initial reference, * and strongly influences LRU. Essentially, the first ref during a callpath * should take an LRU_REQ_INITIAL ref, and all subsequent callpaths should take * LRU_FLAG_NONE refs. * * @return FSAL status */ fsal_status_t _mdcache_lru_ref(mdcache_entry_t *entry, uint32_t flags, const char *func, int line) { mdcache_lru_t *lru = &entry->lru; struct lru_q_lane *qlane = &LRU[lru->lane]; struct lru_q *q; #ifdef USE_LTTNG int32_t refcnt = #endif atomic_inc_int32_t(&entry->lru.refcnt); #ifdef USE_LTTNG tracepoint(mdcache, mdc_lru_ref, func, line, entry, refcnt); #endif /* adjust LRU on initial refs */ if (flags & LRU_REQ_INITIAL) { QLOCK(qlane); switch (lru->qid) { case LRU_ENTRY_L1: q = lru_queue_of(entry); /* advance entry to MRU (of L1) */ LRU_DQ_SAFE(lru, q); lru_insert(lru, q, LRU_MRU); break; case LRU_ENTRY_L2: q = lru_queue_of(entry); /* move entry to LRU of L1 */ glist_del(&lru->q); /* skip L1 fixups */ --(q->size); q = &qlane->L1; lru_insert(lru, q, LRU_LRU); break; default: /* do nothing */ break; } /* switch qid */ QUNLOCK(qlane); } /* initial ref */ return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Relinquish a reference * * This function relinquishes a reference on the given cache entry. * It follows the disposal/recycling lock discipline given at the * beginning of the file. * * The supplied entry is always either unlocked or destroyed by the * time this function returns. * * @param[in] entry The entry on which to release a reference * @param[in] flags Currently significant are and LRU_FLAG_LOCKED * (indicating that the caller holds the LRU mutex * lock for this entry.) * @return true if entry freed, false otherwise */ bool _mdcache_lru_unref(mdcache_entry_t *entry, uint32_t flags, const char *func, int line) { int32_t refcnt; bool do_cleanup = false; uint32_t lane = entry->lru.lane; struct lru_q_lane *qlane = &LRU[lane]; bool other_lock_held = entry->fsobj.hdl.no_cleanup; bool freed = false; if (!other_lock_held) { QLOCK(qlane); if (((entry->lru.flags & LRU_CLEANED) == 0) && (entry->lru.qid == LRU_ENTRY_CLEANUP)) { do_cleanup = true; atomic_set_uint32_t_bits(&entry->lru.flags, LRU_CLEANED); } QUNLOCK(qlane); if (do_cleanup) { LogDebug(COMPONENT_CACHE_INODE, "LRU_ENTRY_CLEANUP of entry %p", entry); state_wipe_file(&entry->obj_handle); } } refcnt = atomic_dec_int32_t(&entry->lru.refcnt); #ifdef USE_LTTNG tracepoint(mdcache, mdc_lru_unref, func, line, entry, refcnt); #endif if (unlikely(refcnt == 0)) { struct lru_q *q; /* we MUST recheck that refcount is still 0 */ QLOCK(qlane); refcnt = atomic_fetch_int32_t(&entry->lru.refcnt); if (unlikely(refcnt > 0)) { QUNLOCK(qlane); goto out; } /* Really zero. Remove entry and mark it as dead. */ q = lru_queue_of(entry); if (q) { /* as of now, entries leaving the cleanup queue * are LRU_ENTRY_NONE */ LRU_DQ_SAFE(&entry->lru, q); } QUNLOCK(qlane); mdcache_lru_clean(entry); pool_free(mdcache_entry_pool, entry); freed = true; (void) atomic_dec_int64_t(&lru_state.entries_used); } /* refcnt == 0 */ out: return freed; } /** * @brief Remove a chunk from LRU, clean it, and free it. * * @param[in] chunk The chunk to be removed from LRU */ void lru_remove_chunk(struct dir_chunk *chunk) { uint32_t lane = chunk->chunk_lru.lane; struct lru_q_lane *qlane = &CHUNK_LRU[lane]; struct lru_q *lq; LogFullDebug(COMPONENT_CACHE_INODE, "Removing chunk %p", chunk); QLOCK(qlane); /* Remove chunk and mark it as dead. */ lq = chunk_lru_queue_of(chunk); if (lq) { /* dequeue the chunk */ CHUNK_LRU_DQ_SAFE(&chunk->chunk_lru, lq); } QUNLOCK(qlane); (void) atomic_dec_int64_t(&lru_state.chunks_used); /* Then do the actual cleaning work. */ mdcache_clean_dirent_chunk(chunk); /* And now we can free the chunk. */ LogFullDebug(COMPONENT_CACHE_INODE, "Freeing chunk %p", chunk); gsh_free(chunk); } /** * @brief Indicate that a chunk is being used, bump it up in the LRU * */ void lru_bump_chunk(struct dir_chunk *chunk) { mdcache_lru_t *lru = &chunk->chunk_lru; struct lru_q_lane *qlane = &CHUNK_LRU[lru->lane]; struct lru_q *q = chunk_lru_queue_of(chunk); QLOCK(qlane); switch (lru->qid) { case LRU_ENTRY_L1: /* advance chunk to MRU (of L1) */ LRU_DQ_SAFE(lru, q); lru_insert(lru, q, LRU_MRU); break; case LRU_ENTRY_L2: /* move chunk to LRU of L1 */ glist_del(&lru->q); /* skip L1 fixups */ --(q->size); q = &qlane->L1; lru_insert(lru, q, LRU_LRU); break; default: /* do nothing */ break; } QUNLOCK(qlane); } /** * * @brief Wake the LRU thread to free FDs. * * This function wakes the LRU reaper thread to free FDs and should be * called when we are over the high water mark. */ void lru_wake_thread(void) { fridgethr_wake(lru_fridge); } /** @} */ nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_lru.h000066400000000000000000000136431324272410200243620ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) 2012, The Linux Box Corporation * Contributor: Matt Benjamin * * Some portions Copyright CEA/DAM/DIF (2008) * contributeur: Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @addtogroup FSAL_MDCACHE * @{ */ #ifndef MDCACHE_LRU_H #define MDCACHE_LRU_H #include "config.h" #include "log.h" #include "mdcache_int.h" /** * @file mdcache_lru.h * @author Matt Benjamin * @brief Constant-time cache inode cache management implementation * * @section DESCRIPTION * * This module implements a constant-time cache management strategy * based on LRU. Some ideas are taken from 2Q [Johnson and Shasha 1994] * and MQ [Zhou, Chen, Li 2004]. In this system, cache management does * interact with cache entry lifecycle. Also, the cache size high- and * low- water mark management is maintained, but executes asynchronously * to avoid inline request delay. Cache management operations execute in * constant time, as expected with LRU (and MQ). * * Cache entries in use by a currently-active protocol request (or other * operation) have a positive refcount, and threfore should not be present * at the cold end of an lru queue if the cache is well-sized. * * Cache entries with lock and open state are not eligible for collection * under ordinary circumstances, so are kept on a separate lru_pinned * list to retain constant time. * */ struct lru_state { uint64_t entries_hiwat; uint64_t entries_used; uint64_t chunks_hiwat; uint64_t chunks_used; uint32_t fds_system_imposed; uint32_t fds_hard_limit; uint32_t fds_hiwat; uint32_t fds_lowat; /** This is the actual counter of 'futile' attempts at reaping made in a given time period. When it reaches the futility count, we turn off caching of file descriptors. */ uint32_t futility; uint32_t per_lane_work; uint32_t biggest_window; uint64_t prev_fd_count; /* previous # of open fds */ time_t prev_time; /* previous time the gc thread was run. */ bool caching_fds; }; extern struct lru_state lru_state; /** Cache entries pool */ extern pool_t *mdcache_entry_pool; /** * Flags for functions in the LRU package */ /** * No flag at all. */ #define LRU_FLAG_NONE 0x0000 /** * The caller holds the lock on the LRU entry. */ #define LRU_FLAG_LOCKED 0x0001 /** * The caller is fetching an initial reference */ #define LRU_REQ_INITIAL 0x0002 /** * qlane is locked */ #define LRU_UNREF_QLOCKED 0x0008 /** * entry->state_lock is held * * This will prevent cleanup on unref. The calling thread MUST hold another * reference that will be release without holding the state_lock (which SHOULD * be true in order to even be able to reference entry->state_lock), which * release will allow cleanup if necessary. */ #define LRU_UNREF_STATE_LOCK_HELD 0x0010 /** * The minimum reference count for a cache entry not being recycled. */ #define LRU_SENTINEL_REFCOUNT 1 /** * The number of lanes comprising a logical queue. This must be * prime. */ #define LRU_N_Q_LANES 17 fsal_status_t mdcache_lru_pkginit(void); fsal_status_t mdcache_lru_pkgshutdown(void); extern size_t open_fd_count; mdcache_entry_t *mdcache_lru_get(void); void mdcache_lru_insert(mdcache_entry_t *entry); #define mdcache_lru_ref(e, f) _mdcache_lru_ref(e, f, __func__, __LINE__) fsal_status_t _mdcache_lru_ref(mdcache_entry_t *entry, uint32_t flags, const char *func, int line); /* XXX */ void mdcache_lru_kill(mdcache_entry_t *entry); void mdcache_lru_cleanup_push(mdcache_entry_t *entry); void mdcache_lru_cleanup_try_push(mdcache_entry_t *entry); #define mdcache_lru_unref(e) _mdcache_lru_unref(e, LRU_FLAG_NONE, \ __func__, __LINE__) bool _mdcache_lru_unref(mdcache_entry_t *entry, uint32_t flags, const char *func, int line); void lru_wake_thread(void); void mdcache_lru_kill_for_shutdown(mdcache_entry_t *entry); /** * * @brief Get a logical reference to a cache entry * * @param[in] entry Cache entry being returned */ static inline fsal_status_t mdcache_get(mdcache_entry_t *entry) { return mdcache_lru_ref(entry, LRU_FLAG_NONE); } /** * * @brief Release logical reference to a cache entry * * This function releases a logical reference to a cache entry * acquired by a previous mdcache handle op (such as lookup, create, etc.) * * The result is typically to decrement the reference count on entry, * but additional side effects include LRU adjustment, movement * to/from the protected LRU partition, or recyling if the caller has * raced an operation which made entry unreachable (and this current * caller has the last reference). Caller MUST NOT make further * accesses to the memory pointed to by entry. * * @param[in] entry Cache entry being returned */ static inline void mdcache_put(mdcache_entry_t *entry) { mdcache_lru_unref(entry); } /** * Return true if we are currently caching file descriptors. */ static inline bool mdcache_lru_caching_fds(void) { return lru_state.caching_fds; } void lru_remove_chunk(struct dir_chunk *chunk); struct dir_chunk *mdcache_get_chunk(mdcache_entry_t *parent); void lru_bump_chunk(struct dir_chunk *chunk); #endif /* MDCACHE_LRU_H */ /** @} */ nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_main.c000066400000000000000000000266761324272410200245110ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright 2015-2017 Red Hat, Inc. and/or its affiliates. * Author: Daniel Gryniewicz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * @addtogroup FSAL_MDCACHE * @{ */ /** * @file main.c * @brief FSAL entry functions */ #include "config.h" #include "fsal.h" #include /* used for 'dirname' */ #include #include #include #include #include "gsh_list.h" #ifdef USE_DBUS #include "gsh_dbus.h" #endif #include "FSAL/fsal_init.h" #include "FSAL/fsal_commonlib.h" #include "mdcache_hash.h" #include "mdcache_lru.h" pool_t *mdcache_entry_pool; /* MDCACHE FSAL module private storage */ struct mdcache_fsal_module { struct fsal_module fsal; struct fsal_staticfsinfo_t fs_info; /* mdcachefs_specific_initinfo_t specific_info; placeholder */ }; struct mdcache_stats cache_st; struct mdcache_stats *cache_stp = &cache_st; /* my module private storage */ static struct mdcache_fsal_module MDCACHE; /* FSAL name determines name of shared library: libfsal.so */ const char mdcachename[] = "MDCACHE"; /* filesystem info for MDCACHE */ static struct fsal_staticfsinfo_t default_posix_info = { .maxfilesize = UINT64_MAX, .maxlink = _POSIX_LINK_MAX, .maxnamelen = 1024, .maxpathlen = 1024, .no_trunc = true, .chown_restricted = true, .case_insensitive = false, .case_preserving = true, .link_support = true, .symlink_support = true, .lock_support = true, .lock_support_async_block = false, .named_attr = true, .unique_handles = true, .lease_time = {10, 0}, .acl_support = FSAL_ACLSUPPORT_ALLOW, .cansettime = true, .homogenous = true, .supported_attrs = ALL_ATTRIBUTES, .maxread = FSAL_MAXIOSIZE, .maxwrite = FSAL_MAXIOSIZE, .umask = 0, .auth_exportpath_xdev = false, .xattr_access_rights = 0400, /* root=RW, owner=R */ .link_supports_permission_checks = true, }; /* private helper for export object */ struct fsal_staticfsinfo_t *mdcache_staticinfo(struct fsal_module *hdl) { struct mdcache_fsal_module *myself; myself = container_of(hdl, struct mdcache_fsal_module, fsal); return &myself->fs_info; } /* Module methods */ /* mdcache_fsal_init_config * must be called with a reference taken (via lookup_fsal) */ static fsal_status_t mdcache_fsal_init_config(struct fsal_module *fsal_hdl, config_file_t config_struct, struct config_error_type *err_type) { struct mdcache_fsal_module *mdcache_me = container_of(fsal_hdl, struct mdcache_fsal_module, fsal); /* get a copy of the defaults */ mdcache_me->fs_info = default_posix_info; /* Configuration setting options: * 1. there are none that are changeable. (this case) * * 2. we set some here. These must be independent of whatever * may be set by lower level fsals. * * If there is any filtering or change of parameters in the stack, * this must be done in export data structures, not fsal params because * a stackable could be configured above multiple fsals for multiple * diverse exports. */ display_fsinfo(&mdcache_me->fs_info); LogFullDebug(COMPONENT_FSAL, "Supported attributes default = 0x%" PRIx64, default_posix_info.supported_attrs); LogDebug(COMPONENT_FSAL, "FSAL INIT: Supported attributes mask = 0x%" PRIx64, mdcache_me->fs_info.supported_attrs); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Clean up caching for a FSAL export on error * * If init has an error after @ref mdcache_export_init is called, this should be * called to clean up any MDCACHE state on the export. This is only intended to * be called on startup error. * */ void mdcache_export_uninit(void) { struct mdcache_fsal_export *exp = mdc_cur_export(); struct fsal_export *sub_export = exp->mfe_exp.sub_export; fsal_put(sub_export->fsal); LogFullDebug(COMPONENT_FSAL, "FSAL %s refcount %"PRIu32, sub_export->fsal->name, atomic_fetch_int32_t(&sub_export->fsal->refcount)); fsal_detach_export(op_ctx->fsal_export->fsal, &op_ctx->fsal_export->exports); free_export_ops(op_ctx->fsal_export); gsh_free(exp); /* Put back sub export */ op_ctx->fsal_export = sub_export; op_ctx->fsal_module = sub_export->fsal; } /** * @brief Create an export for MDCACHE * * Create the stacked export for MDCACHE to allow metadata caching on another * export. Unlike other Stackable FSALs, this one is created @b after the FSAL * underneath. It assumes the sub-FSAL's export is already created and * available via the @e fsal_export member of @link op_ctx @endlink, the same * way that this export is returned. * * There is currently no config; FSALs that want caching should call @ref * mdcache_export_init * * @param[in] sub_fsal Sub-FSAL module handle * @param[in] parse_node Config node for export * @param[out] err_type Parse errors * @param[in] super_up_ops Upcall ops for export * @return FSAL status */ fsal_status_t mdcache_fsal_create_export(struct fsal_module *sub_fsal, void *parse_node, struct config_error_type *err_type, const struct fsal_up_vector *super_up_ops) { fsal_status_t status = {0, 0}; struct mdcache_fsal_export *myself; int namelen; pthread_rwlockattr_t attrs; myself = gsh_calloc(1, sizeof(struct mdcache_fsal_export)); namelen = strlen(sub_fsal->name) + 5; myself->name = gsh_calloc(1, namelen); snprintf(myself->name, namelen, "%s/MDC", sub_fsal->name); fsal_export_init(&myself->mfe_exp); mdcache_export_ops_init(&myself->mfe_exp.exp_ops); myself->super_up_ops = *super_up_ops; /* Struct copy */ mdcache_export_up_ops_init(&myself->up_ops, super_up_ops); myself->up_ops.up_gsh_export = op_ctx->ctx_export; myself->up_ops.up_fsal_export = &myself->mfe_exp; myself->mfe_exp.up_ops = &myself->up_ops; myself->mfe_exp.fsal = &MDCACHE.fsal; glist_init(&myself->entry_list); pthread_rwlockattr_init(&attrs); #ifdef GLIBC pthread_rwlockattr_setkind_np(&attrs, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP); #endif PTHREAD_RWLOCK_init(&myself->mdc_exp_lock, &attrs); status = sub_fsal->m_ops.create_export(sub_fsal, parse_node, err_type, &myself->up_ops); if (FSAL_IS_ERROR(status)) { LogMajor(COMPONENT_FSAL, "Failed to call create_export on underlying FSAL %s", sub_fsal->name); gsh_free(myself->name); gsh_free(myself); return status; } /* Get ref for sub-FSAL */ fsal_get(myself->mfe_exp.fsal); LogFullDebug(COMPONENT_FSAL, "FSAL %s refcount %"PRIu32, myself->mfe_exp.fsal->name, atomic_fetch_int32_t(&myself->mfe_exp.fsal->refcount)); fsal_export_stack(op_ctx->fsal_export, &myself->mfe_exp); /* Set up op_ctx */ op_ctx->fsal_export = &myself->mfe_exp; op_ctx->fsal_module = &MDCACHE.fsal; /* Stacking is setup and ready to take upcalls now */ up_ready_set(&myself->up_ops); return status; } /* Module initialization. * Called by dlopen() to register the module * keep a private pointer to me in myself */ /* linkage to the exports and handle ops initializers */ static int mdcache_fsal_unload(struct fsal_module *fsal_hdl) { fsal_status_t status; int retval; /* Destroy the cache inode AVL tree */ cih_pkgdestroy(); status = mdcache_lru_pkgshutdown(); if (FSAL_IS_ERROR(status)) fprintf(stderr, "MDCACHE LRU failed to shut down"); /* Destroy the cache inode entry pool */ pool_destroy(mdcache_entry_pool); mdcache_entry_pool = NULL; retval = unregister_fsal(&MDCACHE.fsal); if (retval != 0) fprintf(stderr, "MDCACHE module failed to unregister"); if (FSAL_IS_ERROR(status)) return status.major; return retval; } void mdcache_fsal_init(void) { int retval; struct fsal_module *myself = &MDCACHE.fsal; retval = register_fsal(myself, mdcachename, FSAL_MAJOR_VERSION, FSAL_MINOR_VERSION, FSAL_ID_NO_PNFS); if (retval != 0) { fprintf(stderr, "MDCACHE module failed to register"); return; } /*myself->m_ops.create_export = mdcache_fsal_create_export;*/ myself->m_ops.init_config = mdcache_fsal_init_config; myself->m_ops.unload = mdcache_fsal_unload; } /** * @brief Initialize the MDCACHE package. * * This shuold be called once at startup, after parsing config * * @param[in] parm Parameter description * @return FSAL status */ fsal_status_t mdcache_pkginit(void) { fsal_status_t status; if (mdcache_entry_pool) return fsalstat(ERR_FSAL_NO_ERROR, 0); mdcache_entry_pool = pool_basic_init("MDCACHE Entry Pool", sizeof(mdcache_entry_t)); status = mdcache_lru_pkginit(); if (FSAL_IS_ERROR(status)) { pool_destroy(mdcache_entry_pool); mdcache_entry_pool = NULL; return status; } cih_pkginit(); return status; } /** * @brief Check if FDs are available. * * This function checks if FDs are available to serve open * requests. This function also wakes the LRU thread if the * current FD count is above the high water mark. * * @return true if there are FDs available to serve open requests, * false otherwise. */ bool mdcache_lru_fds_available(void) { if ((atomic_fetch_size_t(&open_fd_count) >= lru_state.fds_hard_limit) && lru_state.caching_fds) { LogCrit(COMPONENT_CACHE_INODE_LRU, "FD Hard Limit Exceeded. Disabling FD Cache and waking LRU thread."); lru_state.caching_fds = false; lru_wake_thread(); return false; } if (atomic_fetch_size_t(&open_fd_count) >= lru_state.fds_hiwat) { LogInfo(COMPONENT_CACHE_INODE_LRU, "FDs above high water mark, waking LRU thread."); lru_wake_thread(); } return true; } #ifdef USE_DBUS void mdcache_dbus_show(DBusMessageIter *iter) { struct timespec timestamp; DBusMessageIter struct_iter; char *type; now(×tamp); dbus_append_timestamp(iter, ×tamp); dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &struct_iter); type = "cache_req"; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &type); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &cache_st.inode_req); type = "cache_hit"; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &type); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &cache_st.inode_hit); type = "cache_miss"; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &type); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &cache_st.inode_miss); type = "cache_conf"; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &type); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &cache_st.inode_conf); type = "cache_added"; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &type); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &cache_st.inode_added); type = "cache_mapping"; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &type); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &cache_st.inode_mapping); dbus_message_iter_close_container(iter, &struct_iter); } #endif /* USE_DBUS */ /** @} */ nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_read_conf.c000066400000000000000000000111111324272410200254570ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @addtogroup cache_inode * @{ */ /** * @file cache_inode_read_conf.c * @brief Cache inode configuration parameter tables. */ #include "config.h" #include "log.h" #include "hashtable.h" #include "fsal.h" #include "mdcache_int.h" #include "config_parsing.h" #include #include #include #include #include #include /** File cache configuration, settable in the CacheInode stanza. */ struct mdcache_parameter mdcache_param; static struct config_item mdcache_params[] = { CONF_ITEM_UI32("NParts", 1, 32633, 7, mdcache_parameter, nparts), CONF_ITEM_UI32("Cache_Size", 1, UINT32_MAX, 32633, mdcache_parameter, cache_size), CONF_ITEM_BOOL("Use_Getattr_Directory_Invalidation", false, mdcache_parameter, getattr_dir_invalidation), CONF_ITEM_UI32("Dir_Max_Deleted", 1, UINT32_MAX, 65536, mdcache_parameter, dir.avl_max_deleted), CONF_ITEM_UI32("Dir_Max", 1, UINT32_MAX, 65536, mdcache_parameter, dir.avl_max), CONF_ITEM_UI32("Dir_Chunk", 0, UINT32_MAX, 128, mdcache_parameter, dir.avl_chunk), CONF_ITEM_UI32("Detached_Mult", 1, UINT32_MAX, 1, mdcache_parameter, dir.avl_detached_mult), CONF_ITEM_UI32("Entries_HWMark", 1, UINT32_MAX, 100000, mdcache_parameter, entries_hwmark), CONF_ITEM_UI32("Chunks_HWMark", 1, UINT32_MAX, 100000, mdcache_parameter, chunks_hwmark), CONF_ITEM_UI32("LRU_Run_Interval", 1, 24 * 3600, 90, mdcache_parameter, lru_run_interval), CONF_ITEM_BOOL("Cache_FDs", true, mdcache_parameter, use_fd_cache), CONF_ITEM_UI32("FD_Limit_Percent", 0, 100, 99, mdcache_parameter, fd_limit_percent), CONF_ITEM_UI32("FD_HWMark_Percent", 0, 100, 90, mdcache_parameter, fd_hwmark_percent), CONF_ITEM_UI32("FD_LWMark_Percent", 0, 100, 50, mdcache_parameter, fd_lwmark_percent), CONF_ITEM_UI32("Reaper_Work", 1, 2000, 0, mdcache_parameter, reaper_work), CONF_ITEM_UI32("Reaper_Work_Per_Lane", 1, 2000, 50, mdcache_parameter, reaper_work_per_lane), CONF_ITEM_UI32("Biggest_Window", 1, 100, 40, mdcache_parameter, biggest_window), CONF_ITEM_UI32("Required_Progress", 1, 50, 5, mdcache_parameter, required_progress), CONF_ITEM_UI32("Futility_Count", 1, 50, 8, mdcache_parameter, futility_count), CONF_ITEM_BOOL("Retry_Readdir", false, mdcache_parameter, retry_readdir), CONFIG_EOL }; static void *mdcache_param_init(void *link_mem, void *self_struct) { if (self_struct == NULL) return &mdcache_param; else return NULL; } struct config_block mdcache_param_blk = { .dbus_interface_name = "org.ganesha.nfsd.config.cache_inode", .blk_desc.name = "CacheInode", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = mdcache_param_init, .blk_desc.u.blk.params = mdcache_params, .blk_desc.u.blk.commit = noop_conf_commit }; int mdcache_set_param_from_conf(config_file_t parse_tree, struct config_error_type *err_type) { (void) load_config_from_parse(parse_tree, &mdcache_param_blk, NULL, true, err_type); if (!config_error_is_harmless(err_type)) { LogCrit(COMPONENT_INIT, "Error while parsing MDCACHE specific configuration"); return -1; } /* Compute avl_chunk_split after reading config, make sure it's a * multiple of two. */ mdcache_param.dir.avl_chunk_split = ((mdcache_param.dir.avl_chunk * 3) / 2) & (UINT32_MAX - 1); /* Compute avl_detached_max from avl_chunk and avl_detached_mult */ mdcache_param.dir.avl_detached_max = mdcache_param.dir.avl_chunk * mdcache_param.dir.avl_detached_mult; return 0; } /** @} */ nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_up.c000066400000000000000000000314351324272410200241760ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright 2015-2017 Red Hat, Inc. and/or its affiliates. * Author: Daniel Gryniewicz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * */ /** * @addtogroup FSAL_MDCACHE * @{ */ /** * @file mdcache_helpers.c * @brief Miscellaneous helper functions */ #include "config.h" #include "fsal.h" #include "nfs4_acls.h" #include "mdcache_hash.h" #include "mdcache_int.h" static fsal_status_t mdc_up_invalidate(const struct fsal_up_vector *vec, struct gsh_buffdesc *handle, uint32_t flags) { mdcache_entry_t *entry; fsal_status_t status; struct req_op_context *save_ctx, req_ctx = {0}; mdcache_key_t key; req_ctx.ctx_export = vec->up_gsh_export; req_ctx.fsal_export = vec->up_fsal_export; save_ctx = op_ctx; op_ctx = &req_ctx; key.fsal = vec->up_fsal_export->sub_export->fsal; (void) cih_hash_key(&key, vec->up_fsal_export->sub_export->fsal, handle, CIH_HASH_KEY_PROTOTYPE); status = mdcache_find_keyed(&key, &entry); if (status.major == ERR_FSAL_NOENT) { /* Not cached, so invalidate is a success */ status = fsalstat(ERR_FSAL_NO_ERROR, 0); goto out; } else if (FSAL_IS_ERROR(status)) { /* Real error */ goto out; } atomic_clear_uint32_t_bits(&entry->mde_flags, flags & FSAL_UP_INVALIDATE_CACHE); if (flags & FSAL_UP_INVALIDATE_CLOSE) status = fsal_close(&entry->obj_handle); mdcache_put(entry); out: op_ctx = save_ctx; return status; } /** * @brief Update cached attributes * * @param[in] vec Up ops vector * @param[in] handle Export containing object * @param[in] attr New attributes * @param[in] flags Flags to govern update * * @return FSAL status */ static fsal_status_t mdc_up_update(const struct fsal_up_vector *vec, struct gsh_buffdesc *handle, struct attrlist *attr, uint32_t flags) { mdcache_entry_t *entry; fsal_status_t status; /* Have necessary changes been made? */ bool mutatis_mutandis = false; struct req_op_context *save_ctx, req_ctx = {0}; mdcache_key_t key; /* These cannot be updated, changing any of them is tantamount to destroying and recreating the file. */ if (FSAL_TEST_MASK (attr->valid_mask, ATTR_TYPE | ATTR_FSID | ATTR_FILEID | ATTR_RAWDEV | ATTR_RDATTR_ERR | ATTR_GENERATION)) { return fsalstat(ERR_FSAL_INVAL, 0); } /* Filter out garbage flags */ if (flags & ~(fsal_up_update_filesize_inc | fsal_up_update_atime_inc | fsal_up_update_creation_inc | fsal_up_update_ctime_inc | fsal_up_update_mtime_inc | fsal_up_update_chgtime_inc | fsal_up_update_spaceused_inc | fsal_up_nlink)) { return fsalstat(ERR_FSAL_INVAL, 0); } req_ctx.ctx_export = vec->up_gsh_export; req_ctx.fsal_export = vec->up_fsal_export; save_ctx = op_ctx; op_ctx = &req_ctx; key.fsal = vec->up_fsal_export->sub_export->fsal; (void) cih_hash_key(&key, vec->up_fsal_export->sub_export->fsal, handle, CIH_HASH_KEY_PROTOTYPE); status = mdcache_find_keyed(&key, &entry); if (status.major == ERR_FSAL_NOENT) { /* Not cached, so invalidate is a success */ status = fsalstat(ERR_FSAL_NO_ERROR, 0); goto out; } else if (FSAL_IS_ERROR(status)) { /* Real error */ goto out; } /* Knock things out if the link count falls to 0. */ if ((flags & fsal_up_nlink) && (attr->numlinks == 0)) { LogFullDebug(COMPONENT_CACHE_INODE, "Entry %p Clearing MDCACHE_TRUST_ATTRS, MDCACHE_TRUST_CONTENT, MDCACHE_DIR_POPULATED", entry); atomic_clear_uint32_t_bits(&entry->mde_flags, MDCACHE_TRUST_ATTRS | MDCACHE_TRUST_CONTENT | MDCACHE_DIR_POPULATED); status = fsal_close(&entry->obj_handle); if (FSAL_IS_ERROR(status)) goto put; } if (attr->valid_mask == 0) { /* Done */ goto put; } /* If the attributes are invalid, we can't update a subset. Just bail, * and update them on demand */ if (!mdcache_test_attrs_trust(entry, attr->valid_mask)) { goto put; } PTHREAD_RWLOCK_wrlock(&entry->attr_lock); if (attr->expire_time_attr != 0) entry->attrs.expire_time_attr = attr->expire_time_attr; if (FSAL_TEST_MASK(attr->valid_mask, ATTR_SIZE)) { if (flags & fsal_up_update_filesize_inc) { if (attr->filesize > entry->attrs.filesize) { entry->attrs.filesize = attr->filesize; mutatis_mutandis = true; } } else { entry->attrs.filesize = attr->filesize; mutatis_mutandis = true; } } if (FSAL_TEST_MASK(attr->valid_mask, ATTR_SPACEUSED)) { if (flags & fsal_up_update_spaceused_inc) { if (attr->spaceused > entry->attrs.spaceused) { entry->attrs.spaceused = attr->spaceused; mutatis_mutandis = true; } } else { entry->attrs.spaceused = attr->spaceused; mutatis_mutandis = true; } } if (FSAL_TEST_MASK(attr->valid_mask, ATTR_ACL)) { /** * @todo Someone who knows the ACL code, please look * over this. We assume that the FSAL takes a * reference on the supplied ACL that we can then hold * onto. This seems the most reasonable approach in * an asynchronous call. */ nfs4_acl_release_entry(entry->attrs.acl); entry->attrs.acl = attr->acl; mutatis_mutandis = true; } if (FSAL_TEST_MASK(attr->valid_mask, ATTR_MODE)) { entry->attrs.mode = attr->mode; mutatis_mutandis = true; } if (FSAL_TEST_MASK(attr->valid_mask, ATTR_NUMLINKS)) { entry->attrs.numlinks = attr->numlinks; mutatis_mutandis = true; } if (FSAL_TEST_MASK(attr->valid_mask, ATTR_OWNER)) { entry->attrs.owner = attr->owner; mutatis_mutandis = true; } if (FSAL_TEST_MASK(attr->valid_mask, ATTR_GROUP)) { entry->attrs.group = attr->group; mutatis_mutandis = true; } if (FSAL_TEST_MASK(attr->valid_mask, ATTR_ATIME) && ((flags & ~fsal_up_update_atime_inc) || (gsh_time_cmp(&attr->atime, &entry->attrs.atime) == 1))) { entry->attrs.atime = attr->atime; mutatis_mutandis = true; } if (FSAL_TEST_MASK(attr->valid_mask, ATTR_CREATION) && ((flags & ~fsal_up_update_creation_inc) || (gsh_time_cmp(&attr->creation, &entry->attrs.creation) == 1))) { entry->attrs.creation = attr->creation; mutatis_mutandis = true; } if (FSAL_TEST_MASK(attr->valid_mask, ATTR_CTIME) && ((flags & ~fsal_up_update_ctime_inc) || (gsh_time_cmp(&attr->ctime, &entry->attrs.ctime) == 1))) { entry->attrs.ctime = attr->ctime; mutatis_mutandis = true; } if (FSAL_TEST_MASK(attr->valid_mask, ATTR_MTIME) && ((flags & ~fsal_up_update_mtime_inc) || (gsh_time_cmp(&attr->mtime, &entry->attrs.mtime) == 1))) { entry->attrs.mtime = attr->mtime; mutatis_mutandis = true; } if (FSAL_TEST_MASK(attr->valid_mask, ATTR_CHGTIME) && ((flags & ~fsal_up_update_chgtime_inc) || (gsh_time_cmp(&attr->chgtime, &entry->attrs.chgtime) == 1))) { entry->attrs.chgtime = attr->chgtime; mutatis_mutandis = true; } if (FSAL_TEST_MASK(attr->valid_mask, ATTR_CHANGE)) { entry->attrs.change = attr->change; mutatis_mutandis = true; } if (mutatis_mutandis) { mdc_fixup_md(entry, attr); /* If directory can not trust content anymore. */ if (entry->obj_handle.type == DIRECTORY) { LogFullDebug(COMPONENT_CACHE_INODE, "Entry %p Clearing MDCACHE_TRUST_CONTENT, MDCACHE_DIR_POPULATED", entry); atomic_clear_uint32_t_bits(&entry->mde_flags, MDCACHE_TRUST_CONTENT | MDCACHE_DIR_POPULATED); } status = fsalstat(ERR_FSAL_NO_ERROR, 0); } else { atomic_clear_uint32_t_bits(&entry->mde_flags, MDCACHE_TRUST_ATTRS); status = fsalstat(ERR_FSAL_INVAL, 0); } PTHREAD_RWLOCK_unlock(&entry->attr_lock); put: mdcache_put(entry); out: op_ctx = save_ctx; return status; } /** * @brief Invalidate a cached entry * * @note doesn't need op_ctx, handled in mdc_up_invalidate * * @param[in] vec Up ops vector * @param[in] key Key to specify object * @param[in] flags FSAL_UP_INVALIDATE* * * @return FSAL status */ static fsal_status_t mdc_up_invalidate_close(const struct fsal_up_vector *vec, struct gsh_buffdesc *key, uint32_t flags) { fsal_status_t status; status = up_async_invalidate(general_fridge, vec, key, flags | FSAL_UP_INVALIDATE_CLOSE, NULL, NULL); return status; } /** Grant a lock to a client * * Pass up to upper layer * * @param[in] vec Up ops vector * @param[in] file The file in question * @param[in] owner The lock owner * @param[in] lock_param A description of the lock * */ state_status_t mdc_up_lock_grant(const struct fsal_up_vector *vec, struct gsh_buffdesc *file, void *owner, fsal_lock_param_t *lock_param) { struct mdcache_fsal_export *myself = mdc_export(vec->up_fsal_export); state_status_t rc; struct req_op_context *save_ctx, req_ctx = {0}; req_ctx.ctx_export = vec->up_gsh_export; req_ctx.fsal_export = vec->up_fsal_export; save_ctx = op_ctx; op_ctx = &req_ctx; rc = myself->super_up_ops.lock_grant(vec, file, owner, lock_param); op_ctx = save_ctx; return rc; } /** Signal lock availability * * Pass up to upper layer * * @param[in] vec Up ops vector * @param[in] file The file in question * @param[in] owner The lock owner * @param[in] lock_param A description of the lock * */ state_status_t mdc_up_lock_avail(const struct fsal_up_vector *vec, struct gsh_buffdesc *file, void *owner, fsal_lock_param_t *lock_param) { struct mdcache_fsal_export *myself = mdc_export(vec->up_fsal_export); state_status_t rc; struct req_op_context *save_ctx, req_ctx = {0}; req_ctx.ctx_export = vec->up_gsh_export; req_ctx.fsal_export = vec->up_fsal_export; save_ctx = op_ctx; op_ctx = &req_ctx; rc = myself->super_up_ops.lock_avail(vec, file, owner, lock_param); op_ctx = save_ctx; return rc; } /** Perform a layoutrecall on a single file * * Pass to upper layer * * @param[in] vec Up ops vector * @param[in] handle Handle on which the layout is held * @param[in] layout_type The type of layout to recall * @param[in] changed Whether the layout has changed and the * client ought to finish writes through MDS * @param[in] segment Segment to recall * @param[in] cookie A cookie returned with the return that * completely satisfies a recall * @param[in] spec Lets us be fussy about what clients we send * to. May beNULL. * */ state_status_t mdc_up_layoutrecall(const struct fsal_up_vector *vec, struct gsh_buffdesc *handle, layouttype4 layout_type, bool changed, const struct pnfs_segment *segment, void *cookie, struct layoutrecall_spec *spec) { struct mdcache_fsal_export *myself = mdc_export(vec->up_fsal_export); state_status_t rc; struct req_op_context *save_ctx, req_ctx = {0}; req_ctx.ctx_export = vec->up_gsh_export; req_ctx.fsal_export = vec->up_fsal_export; save_ctx = op_ctx; op_ctx = &req_ctx; rc = myself->super_up_ops.layoutrecall(vec, handle, layout_type, changed, segment, cookie, spec); op_ctx = save_ctx; return rc; } /** Recall a delegation * * Pass to upper layer * * @param[in] vec Up ops vector * @param[in] handle Handle on which the delegation is held */ state_status_t mdc_up_delegrecall(const struct fsal_up_vector *vec, struct gsh_buffdesc *handle) { struct mdcache_fsal_export *myself = mdc_export(vec->up_fsal_export); state_status_t rc; struct req_op_context *save_ctx, req_ctx = {0}; req_ctx.ctx_export = vec->up_gsh_export; req_ctx.fsal_export = vec->up_fsal_export; save_ctx = op_ctx; op_ctx = &req_ctx; rc = myself->super_up_ops.delegrecall(vec, handle); op_ctx = save_ctx; return rc; } fsal_status_t mdcache_export_up_ops_init(struct fsal_up_vector *my_up_ops, const struct fsal_up_vector *super_up_ops) { /* Init with super ops. Struct copy */ *my_up_ops = *super_up_ops; up_ready_init(my_up_ops); /* Replace cache-related calls */ my_up_ops->invalidate = mdc_up_invalidate; my_up_ops->update = mdc_up_update; my_up_ops->invalidate_close = mdc_up_invalidate_close; /* These are pass-through calls that set op_ctx */ my_up_ops->lock_grant = mdc_up_lock_grant; my_up_ops->lock_avail = mdc_up_lock_avail; my_up_ops->layoutrecall = mdc_up_layoutrecall; /* notify_device cannot call into MDCACHE */ my_up_ops->delegrecall = mdc_up_delegrecall; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** @} */ nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_xattrs.c000066400000000000000000000227441324272410200251020ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright 2015-2016 Red Hat, Inc. and/or its affiliates. * Author: Daniel Gryniewicz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * */ /* xattrs.c * NULL object (file|dir) handle object extended attributes */ #include "config.h" #include "fsal.h" #include /* used for 'dirname' */ #include #include #include #include #include #include "gsh_list.h" #include "fsal_convert.h" #include "FSAL/fsal_commonlib.h" #include "mdcache_int.h" /** * @brief List extended attributes on a file * * Pass through to sub-FSAL * * @param[in] obj_hdl File to walk * @param[in] argcookie Cookie for caller * @param[out] xattrs_tab Output buffer for xattrs * @param[in] xattrs_tabsize Size of @a xattrs_tab * @param[out] p_nb_returned Number of xattrs returned * @param[out] end_of_list True if reached end of list * @return FSAL status */ fsal_status_t mdcache_list_ext_attrs(struct fsal_obj_handle *obj_hdl, unsigned int argcookie, fsal_xattrent_t *xattrs_tab, unsigned int xattrs_tabsize, unsigned int *p_nb_returned, int *end_of_list) { struct mdcache_fsal_obj_handle *handle = container_of(obj_hdl, struct mdcache_fsal_obj_handle, obj_handle); fsal_status_t status; subcall( status = handle->sub_handle->obj_ops.list_ext_attrs( handle->sub_handle, argcookie, xattrs_tab, xattrs_tabsize, p_nb_returned, end_of_list) ); return status; } /** * @brief Get ID of xattr by name * * Pass through to sub-FSAL * * @param[in] obj_hdl File to search * @param[in] name Name of xattr to look up * @param[out] p_id ID of xattr, if found * @return FSAL status */ fsal_status_t mdcache_getextattr_id_by_name(struct fsal_obj_handle *obj_hdl, const char *name, unsigned int *p_id) { struct mdcache_fsal_obj_handle *handle = container_of(obj_hdl, struct mdcache_fsal_obj_handle, obj_handle); fsal_status_t status; subcall( status = handle->sub_handle->obj_ops.getextattr_id_by_name( handle->sub_handle, name, p_id) ); return status; } /** * @brief Get contents of xattr by ID * * Pass through to sub-FSAL * * @param[in] obj_hdl File to search * @param[in] id ID of xattr to read * @param[out] buf Output buffer * @param[in] buf_size Size of @a buf * @param[out] p_output_size Amount written to buf * @return FSAL status */ fsal_status_t mdcache_getextattr_value_by_id(struct fsal_obj_handle *obj_hdl, unsigned int id, caddr_t buf, size_t buf_size, size_t *p_output_size) { struct mdcache_fsal_obj_handle *handle = container_of(obj_hdl, struct mdcache_fsal_obj_handle, obj_handle); fsal_status_t status; subcall( status = handle->sub_handle->obj_ops.getextattr_value_by_id( handle->sub_handle, id, buf, buf_size, p_output_size) ); return status; } /** * @brief Get contents of xattr by name * * Pass through to sub-FSAL * * @param[in] obj_hdl File to search * @param[in] name Name of xattr to look up * @param[out] buf Output buffer * @param[in] buf_size Size of @a buf * @param[out] p_output_size Amount written to buf * @return FSAL status */ fsal_status_t mdcache_getextattr_value_by_name(struct fsal_obj_handle *obj_hdl, const char *name, caddr_t buf, size_t buf_size, size_t *p_output_size) { struct mdcache_fsal_obj_handle *handle = container_of(obj_hdl, struct mdcache_fsal_obj_handle, obj_handle); fsal_status_t status; subcall( status = handle->sub_handle->obj_ops.getextattr_value_by_name( handle->sub_handle, name, buf, buf_size, p_output_size) ); return status; } /** * @brief Set contents of xattr by name * * Pass through to sub-FSAL * * @param[in] obj_hdl File to search * @param[in] name Name of xattr to set * @param[out] buf Output buffer * @param[in] buf_size Size of @a buf * @param[in] create If true, create xattr * @return FSAL status */ fsal_status_t mdcache_setextattr_value(struct fsal_obj_handle *obj_hdl, const char *name, caddr_t buf, size_t buf_size, int create) { struct mdcache_fsal_obj_handle *handle = container_of(obj_hdl, struct mdcache_fsal_obj_handle, obj_handle); fsal_status_t status; subcall( status = handle->sub_handle->obj_ops.setextattr_value( handle->sub_handle, name, buf, buf_size, create) ); return status; } /** * @brief Set contents of xattr by ID * * Pass through to sub-FSAL * * @param[in] obj_hdl File to search * @param[in] id ID of xattr to set * @param[out] buf Output buffer * @param[in] buf_size Size of @a buf * @return FSAL status */ fsal_status_t mdcache_setextattr_value_by_id(struct fsal_obj_handle *obj_hdl, unsigned int id, caddr_t buf, size_t buf_size) { struct mdcache_fsal_obj_handle *handle = container_of(obj_hdl, struct mdcache_fsal_obj_handle, obj_handle); fsal_status_t status; subcall( status = handle->sub_handle->obj_ops.setextattr_value_by_id( handle->sub_handle, id, buf, buf_size) ); return status; } /** * @brief Remove an xattr by ID * * Pass through to sub-FSAL * * @param[in] obj_hdl File to search * @param[in] id ID of xattr to remove * @return FSAL status */ fsal_status_t mdcache_remove_extattr_by_id(struct fsal_obj_handle *obj_hdl, unsigned int id) { struct mdcache_fsal_obj_handle *handle = container_of(obj_hdl, struct mdcache_fsal_obj_handle, obj_handle); fsal_status_t status; subcall( status = handle->sub_handle->obj_ops.remove_extattr_by_id( handle->sub_handle, id) ); return status; } /** * @brief Remove an xattr by name * * Pass through to sub-FSAL * * @param[in] obj_hdl File to search * @param[in] name Name of xattr to remove * @return FSAL status */ fsal_status_t mdcache_remove_extattr_by_name(struct fsal_obj_handle *obj_hdl, const char *name) { struct mdcache_fsal_obj_handle *handle = container_of(obj_hdl, struct mdcache_fsal_obj_handle, obj_handle); fsal_status_t status; subcall( status = handle->sub_handle->obj_ops.remove_extattr_by_name( handle->sub_handle, name) ); return status; } /** * @brief Get an Extended Attribute * * Pass through to sub-FSAL * * @param[in] obj_hdl File to search * @param[in] name Name of attribute * @param[out] value Value of attribute * @return FSAL status */ fsal_status_t mdcache_getxattrs(struct fsal_obj_handle *obj_hdl, xattrname4 *name, xattrvalue4 *value) { struct mdcache_fsal_obj_handle *handle = container_of(obj_hdl, struct mdcache_fsal_obj_handle, obj_handle); fsal_status_t status; subcall( status = handle->sub_handle->obj_ops.getxattrs( handle->sub_handle, name, value) ); return status; } /** * @brief Set an Extended Attribute * * Pass through to sub-FSAL * * @param[in] obj_hdl File to search * @param[in] type Type of attribute * @param[in] name Name of attribute * @param[in] value Value of attribute * @return FSAL status */ fsal_status_t mdcache_setxattrs(struct fsal_obj_handle *obj_hdl, setxattr_type4 type, xattrname4 *name, xattrvalue4 *value) { struct mdcache_fsal_obj_handle *handle = container_of(obj_hdl, struct mdcache_fsal_obj_handle, obj_handle); fsal_status_t status; subcall( status = handle->sub_handle->obj_ops.setxattrs( handle->sub_handle, type, name, value) ); return status; } /** * @brief Remove an Extended Attribute * * Pass through to sub-FSAL * * @param[in] obj_hdl File to search * @param[in] name Type of attribute * @return FSAL status */ fsal_status_t mdcache_removexattrs(struct fsal_obj_handle *obj_hdl, xattrname4 *name) { struct mdcache_fsal_obj_handle *handle = container_of(obj_hdl, struct mdcache_fsal_obj_handle, obj_handle); fsal_status_t status; subcall( status = handle->sub_handle->obj_ops.removexattrs( handle->sub_handle, name) ); return status; } /** * @brief List Extended Attributes * * Pass through to sub-FSAL * * @param[in] obj_hdl File to search * @param[in] len Length of names buffer * @param[in,out] cookie cookie for list * @param[in,out] verf cookie verifier * @param[out] eof set if no more extended attributes * @param[out] names list of extended attribute names * @return FSAL status */ fsal_status_t mdcache_listxattrs(struct fsal_obj_handle *obj_hdl, count4 len, nfs_cookie4 *cookie, verifier4 *verf, bool_t *eof, xattrlist4 *names) { struct mdcache_fsal_obj_handle *handle = container_of(obj_hdl, struct mdcache_fsal_obj_handle, obj_handle); fsal_status_t status; subcall( status = handle->sub_handle->obj_ops.listxattrs( handle->sub_handle, len, cookie, verf, eof, names) ); return status; } nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_NULL/000077500000000000000000000000001324272410200214625ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_NULL/CMakeLists.txt000066400000000000000000000010041324272410200242150ustar00rootroot00000000000000add_definitions( -D__USE_GNU -D_GNU_SOURCE ) set( LIB_PREFIX 64) ########### next target ############### SET(fsalnull_LIB_SRCS handle.c file.c xattrs.c nullfs_methods.h main.c export.c ) add_library(fsalnull MODULE ${fsalnull_LIB_SRCS}) add_sanitizers(fsalnull) target_link_libraries(fsalnull gos ) set_target_properties(fsalnull PROPERTIES VERSION 4.2.0 SOVERSION 4) install(TARGETS fsalnull COMPONENT fsal DESTINATION ${FSAL_DESTINATION} ) ########### install files ############### nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_NULL/export.c000066400000000000000000000365541324272410200231640ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * */ /* export.c * NULL FSAL export object */ #include "config.h" #include "fsal.h" #include /* used for 'dirname' */ #include #include #include #include #include #include #include "gsh_list.h" #include "config_parsing.h" #include "fsal_convert.h" #include "FSAL/fsal_commonlib.h" #include "FSAL/fsal_config.h" #include "nullfs_methods.h" #include "nfs_exports.h" #include "export_mgr.h" /* helpers to/from other NULL objects */ struct fsal_staticfsinfo_t *nullfs_staticinfo(struct fsal_module *hdl); /* export object methods */ static void release(struct fsal_export *exp_hdl) { struct nullfs_fsal_export *myself; struct fsal_module *sub_fsal; myself = container_of(exp_hdl, struct nullfs_fsal_export, export); sub_fsal = myself->export.sub_export->fsal; /* Release the sub_export */ myself->export.sub_export->exp_ops.release(myself->export.sub_export); fsal_put(sub_fsal); LogFullDebug(COMPONENT_FSAL, "FSAL %s refcount %"PRIu32, sub_fsal->name, atomic_fetch_int32_t(&sub_fsal->refcount)); fsal_detach_export(exp_hdl->fsal, &exp_hdl->exports); free_export_ops(exp_hdl); gsh_free(myself); /* elvis has left the building */ } static fsal_status_t get_dynamic_info(struct fsal_export *exp_hdl, struct fsal_obj_handle *obj_hdl, fsal_dynamicfsinfo_t *infop) { struct nullfs_fsal_export *exp = container_of(exp_hdl, struct nullfs_fsal_export, export); struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); /* calling subfsal method */ op_ctx->fsal_export = exp->export.sub_export; fsal_status_t status = op_ctx->fsal_export->exp_ops.get_fs_dynamic_info( op_ctx->fsal_export, handle->sub_handle, infop); op_ctx->fsal_export = &exp->export; return status; } static bool fs_supports(struct fsal_export *exp_hdl, fsal_fsinfo_options_t option) { struct nullfs_fsal_export *exp = container_of(exp_hdl, struct nullfs_fsal_export, export); op_ctx->fsal_export = exp->export.sub_export; bool result = exp->export.sub_export->exp_ops.fs_supports( exp->export.sub_export, option); op_ctx->fsal_export = &exp->export; return result; } static uint64_t fs_maxfilesize(struct fsal_export *exp_hdl) { struct nullfs_fsal_export *exp = container_of(exp_hdl, struct nullfs_fsal_export, export); op_ctx->fsal_export = exp->export.sub_export; uint64_t result = exp->export.sub_export->exp_ops.fs_maxfilesize( exp->export.sub_export); op_ctx->fsal_export = &exp->export; return result; } static uint32_t fs_maxread(struct fsal_export *exp_hdl) { struct nullfs_fsal_export *exp = container_of(exp_hdl, struct nullfs_fsal_export, export); op_ctx->fsal_export = exp->export.sub_export; uint32_t result = exp->export.sub_export->exp_ops.fs_maxread( exp->export.sub_export); op_ctx->fsal_export = &exp->export; return result; } static uint32_t fs_maxwrite(struct fsal_export *exp_hdl) { struct nullfs_fsal_export *exp = container_of(exp_hdl, struct nullfs_fsal_export, export); op_ctx->fsal_export = exp->export.sub_export; uint32_t result = exp->export.sub_export->exp_ops.fs_maxwrite( exp->export.sub_export); op_ctx->fsal_export = &exp->export; return result; } static uint32_t fs_maxlink(struct fsal_export *exp_hdl) { struct nullfs_fsal_export *exp = container_of(exp_hdl, struct nullfs_fsal_export, export); op_ctx->fsal_export = exp->export.sub_export; uint32_t result = exp->export.sub_export->exp_ops.fs_maxlink( exp->export.sub_export); op_ctx->fsal_export = &exp->export; return result; } static uint32_t fs_maxnamelen(struct fsal_export *exp_hdl) { struct nullfs_fsal_export *exp = container_of(exp_hdl, struct nullfs_fsal_export, export); op_ctx->fsal_export = exp->export.sub_export; uint32_t result = exp->export.sub_export->exp_ops.fs_maxnamelen( exp->export.sub_export); op_ctx->fsal_export = &exp->export; return result; } static uint32_t fs_maxpathlen(struct fsal_export *exp_hdl) { struct nullfs_fsal_export *exp = container_of(exp_hdl, struct nullfs_fsal_export, export); op_ctx->fsal_export = exp->export.sub_export; uint32_t result = exp->export.sub_export->exp_ops.fs_maxpathlen( exp->export.sub_export); op_ctx->fsal_export = &exp->export; return result; } static struct timespec fs_lease_time(struct fsal_export *exp_hdl) { struct nullfs_fsal_export *exp = container_of(exp_hdl, struct nullfs_fsal_export, export); op_ctx->fsal_export = exp->export.sub_export; struct timespec result = exp->export.sub_export->exp_ops.fs_lease_time( exp->export.sub_export); op_ctx->fsal_export = &exp->export; return result; } static fsal_aclsupp_t fs_acl_support(struct fsal_export *exp_hdl) { struct nullfs_fsal_export *exp = container_of(exp_hdl, struct nullfs_fsal_export, export); op_ctx->fsal_export = exp->export.sub_export; fsal_aclsupp_t result = exp->export.sub_export->exp_ops.fs_acl_support( exp->export.sub_export); op_ctx->fsal_export = &exp->export; return result; } static attrmask_t fs_supported_attrs(struct fsal_export *exp_hdl) { struct nullfs_fsal_export *exp = container_of(exp_hdl, struct nullfs_fsal_export, export); op_ctx->fsal_export = exp->export.sub_export; attrmask_t result = exp->export.sub_export->exp_ops.fs_supported_attrs( exp->export.sub_export); op_ctx->fsal_export = &exp->export; return result; } static uint32_t fs_umask(struct fsal_export *exp_hdl) { struct nullfs_fsal_export *exp = container_of(exp_hdl, struct nullfs_fsal_export, export); op_ctx->fsal_export = exp->export.sub_export; uint32_t result = exp->export.sub_export->exp_ops.fs_umask( exp->export.sub_export); op_ctx->fsal_export = &exp->export; return result; } static uint32_t fs_xattr_access_rights(struct fsal_export *exp_hdl) { struct nullfs_fsal_export *exp = container_of(exp_hdl, struct nullfs_fsal_export, export); op_ctx->fsal_export = exp->export.sub_export; uint32_t result = exp->export.sub_export->exp_ops.fs_xattr_access_rights( exp->export.sub_export); op_ctx->fsal_export = &exp->export; return result; } /* get_quota * return quotas for this export. * path could cross a lower mount boundary which could * mask lower mount values with those of the export root * if this is a real issue, we can scan each time with setmntent() * better yet, compare st_dev of the file with st_dev of root_fd. * on linux, can map st_dev -> /proc/partitions name -> /dev/ */ static fsal_status_t get_quota(struct fsal_export *exp_hdl, const char *filepath, int quota_type, int quota_id, fsal_quota_t *pquota) { struct nullfs_fsal_export *exp = container_of(exp_hdl, struct nullfs_fsal_export, export); op_ctx->fsal_export = exp->export.sub_export; fsal_status_t result = exp->export.sub_export->exp_ops.get_quota( exp->export.sub_export, filepath, quota_type, quota_id, pquota); op_ctx->fsal_export = &exp->export; return result; } /* set_quota * same lower mount restriction applies */ static fsal_status_t set_quota(struct fsal_export *exp_hdl, const char *filepath, int quota_type, int quota_id, fsal_quota_t *pquota, fsal_quota_t *presquota) { struct nullfs_fsal_export *exp = container_of(exp_hdl, struct nullfs_fsal_export, export); op_ctx->fsal_export = exp->export.sub_export; fsal_status_t result = exp->export.sub_export->exp_ops.set_quota( exp->export.sub_export, filepath, quota_type, quota_id, pquota, presquota); op_ctx->fsal_export = &exp->export; return result; } static struct state_t *nullfs_alloc_state(struct fsal_export *exp_hdl, enum state_type state_type, struct state_t *related_state) { struct nullfs_fsal_export *exp = container_of(exp_hdl, struct nullfs_fsal_export, export); op_ctx->fsal_export = exp->export.sub_export; state_t *state = exp->export.sub_export->exp_ops.alloc_state( exp->export.sub_export, state_type, related_state); op_ctx->fsal_export = &exp->export; /* Replace stored export with ours so stacking works */ state->state_exp = exp_hdl; return state; } static void nullfs_free_state(struct fsal_export *exp_hdl, struct state_t *state) { struct nullfs_fsal_export *exp = container_of(exp_hdl, struct nullfs_fsal_export, export); op_ctx->fsal_export = exp->export.sub_export; exp->export.sub_export->exp_ops.free_state(exp->export.sub_export, state); op_ctx->fsal_export = &exp->export; } static bool nullfs_is_superuser(struct fsal_export *exp_hdl, const struct user_cred *creds) { struct nullfs_fsal_export *exp = container_of(exp_hdl, struct nullfs_fsal_export, export); bool rv; op_ctx->fsal_export = exp->export.sub_export; rv = exp->export.sub_export->exp_ops.is_superuser( exp->export.sub_export, creds); op_ctx->fsal_export = &exp->export; return rv; } /* extract a file handle from a buffer. * do verification checks and flag any and all suspicious bits. * Return an updated fh_desc into whatever was passed. The most * common behavior, done here is to just reset the length. */ static fsal_status_t wire_to_host(struct fsal_export *exp_hdl, fsal_digesttype_t in_type, struct gsh_buffdesc *fh_desc, int flags) { struct nullfs_fsal_export *exp = container_of(exp_hdl, struct nullfs_fsal_export, export); op_ctx->fsal_export = exp->export.sub_export; fsal_status_t result = exp->export.sub_export->exp_ops.wire_to_host( exp->export.sub_export, in_type, fh_desc, flags); op_ctx->fsal_export = &exp->export; return result; } static fsal_status_t nullfs_host_to_key(struct fsal_export *exp_hdl, struct gsh_buffdesc *fh_desc) { struct nullfs_fsal_export *exp = container_of(exp_hdl, struct nullfs_fsal_export, export); op_ctx->fsal_export = exp->export.sub_export; fsal_status_t result = exp->export.sub_export->exp_ops.host_to_key( exp->export.sub_export, fh_desc); op_ctx->fsal_export = &exp->export; return result; } /* nullfs_export_ops_init * overwrite vector entries with the methods that we support */ void nullfs_export_ops_init(struct export_ops *ops) { ops->release = release; ops->lookup_path = nullfs_lookup_path; ops->wire_to_host = wire_to_host; ops->host_to_key = nullfs_host_to_key; ops->create_handle = nullfs_create_handle; ops->get_fs_dynamic_info = get_dynamic_info; ops->fs_supports = fs_supports; ops->fs_maxfilesize = fs_maxfilesize; ops->fs_maxread = fs_maxread; ops->fs_maxwrite = fs_maxwrite; ops->fs_maxlink = fs_maxlink; ops->fs_maxnamelen = fs_maxnamelen; ops->fs_maxpathlen = fs_maxpathlen; ops->fs_lease_time = fs_lease_time; ops->fs_acl_support = fs_acl_support; ops->fs_supported_attrs = fs_supported_attrs; ops->fs_umask = fs_umask; ops->fs_xattr_access_rights = fs_xattr_access_rights; ops->get_quota = get_quota; ops->set_quota = set_quota; ops->alloc_state = nullfs_alloc_state; ops->free_state = nullfs_free_state; ops->is_superuser = nullfs_is_superuser; } struct nullfsal_args { struct subfsal_args subfsal; }; static struct config_item sub_fsal_params[] = { CONF_ITEM_STR("name", 1, 10, NULL, subfsal_args, name), CONFIG_EOL }; static struct config_item export_params[] = { CONF_ITEM_NOOP("name"), CONF_RELAX_BLOCK("FSAL", sub_fsal_params, noop_conf_init, subfsal_commit, nullfsal_args, subfsal), CONFIG_EOL }; static struct config_block export_param = { .dbus_interface_name = "org.ganesha.nfsd.config.fsal.nullfs-export%d", .blk_desc.name = "FSAL", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = noop_conf_init, .blk_desc.u.blk.params = export_params, .blk_desc.u.blk.commit = noop_conf_commit }; /* create_export * Create an export point and return a handle to it to be kept * in the export list. * First lookup the fsal, then create the export and then put the fsal back. * returns the export with one reference taken. */ fsal_status_t nullfs_create_export(struct fsal_module *fsal_hdl, void *parse_node, struct config_error_type *err_type, const struct fsal_up_vector *up_ops) { fsal_status_t expres; struct fsal_module *fsal_stack; struct nullfs_fsal_export *myself; struct nullfsal_args nullfsal; int retval; /* process our FSAL block to get the name of the fsal * underneath us. */ retval = load_config_from_node(parse_node, &export_param, &nullfsal, true, err_type); if (retval != 0) return fsalstat(ERR_FSAL_INVAL, 0); fsal_stack = lookup_fsal(nullfsal.subfsal.name); if (fsal_stack == NULL) { LogMajor(COMPONENT_FSAL, "nullfs create export failed to lookup for FSAL %s", nullfsal.subfsal.name); return fsalstat(ERR_FSAL_INVAL, EINVAL); } myself = gsh_calloc(1, sizeof(struct nullfs_fsal_export)); expres = fsal_stack->m_ops.create_export(fsal_stack, nullfsal.subfsal.fsal_node, err_type, up_ops); fsal_put(fsal_stack); LogFullDebug(COMPONENT_FSAL, "FSAL %s refcount %"PRIu32, fsal_stack->name, atomic_fetch_int32_t(&fsal_stack->refcount)); if (FSAL_IS_ERROR(expres)) { LogMajor(COMPONENT_FSAL, "Failed to call create_export on underlying FSAL %s", nullfsal.subfsal.name); gsh_free(myself); return expres; } fsal_export_stack(op_ctx->fsal_export, &myself->export); /* Init next_ops structure */ /*** FIX ME!!! * This structure had 3 mallocs that were never freed, * and would leak for every export created. * Now static to avoid the leak, the saved contents were * never restored back to the original. */ memcpy(&next_ops.exp_ops, &myself->export.sub_export->exp_ops, sizeof(struct export_ops)); #ifdef EXPORT_OPS_INIT /*** FIX ME!!! * Need to iterate through the lists to save and restore. */ memcpy(&next_ops.obj_ops, myself->export.sub_export->obj_ops, sizeof(struct fsal_obj_ops)); memcpy(&next_ops.dsh_ops, myself->export.sub_export->dsh_ops, sizeof(struct fsal_dsh_ops)); #endif /* EXPORT_OPS_INIT */ next_ops.up_ops = up_ops; fsal_export_init(&myself->export); nullfs_export_ops_init(&myself->export.exp_ops); #ifdef EXPORT_OPS_INIT /*** FIX ME!!! * Need to iterate through the lists to save and restore. */ nullfs_handle_ops_init(myself->export.obj_ops); #endif /* EXPORT_OPS_INIT */ myself->export.up_ops = up_ops; myself->export.fsal = fsal_hdl; /* lock myself before attaching to the fsal. * keep myself locked until done with creating myself. */ op_ctx->fsal_export = &myself->export; return fsalstat(ERR_FSAL_NO_ERROR, 0); } nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_NULL/file.c000066400000000000000000000226511324272410200225530ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* file.c * File I/O methods for NULL module */ #include "config.h" #include #include "fsal.h" #include "FSAL/access_check.h" #include "fsal_convert.h" #include #include #include "FSAL/fsal_commonlib.h" #include "nullfs_methods.h" /* nullfs_close * Close the file if it is still open. * Yes, we ignor lock status. Closing a file in POSIX * releases all locks but that is state and cache inode's problem. */ fsal_status_t nullfs_close(struct fsal_obj_handle *obj_hdl) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.close(handle->sub_handle); op_ctx->fsal_export = &export->export; return status; } fsal_status_t nullfs_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 *attrs_in, fsal_verifier_t verifier, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out, bool *caller_perm_check) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); struct fsal_obj_handle *sub_handle = NULL; /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.open2(handle->sub_handle, state, openflags, createmode, name, attrs_in, verifier, &sub_handle, attrs_out, caller_perm_check); op_ctx->fsal_export = &export->export; if (sub_handle) { /* wrap the subfsal handle in a nullfs handle. */ return nullfs_alloc_and_check_handle(export, sub_handle, obj_hdl->fs, new_obj, status); } return status; } bool nullfs_check_verifier(struct fsal_obj_handle *obj_hdl, fsal_verifier_t verifier) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; bool result = handle->sub_handle->obj_ops.check_verifier(handle->sub_handle, verifier); op_ctx->fsal_export = &export->export; return result; } fsal_openflags_t nullfs_status2(struct fsal_obj_handle *obj_hdl, struct state_t *state) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_openflags_t result = handle->sub_handle->obj_ops.status2(handle->sub_handle, state); op_ctx->fsal_export = &export->export; return result; } fsal_status_t nullfs_reopen2(struct fsal_obj_handle *obj_hdl, struct state_t *state, fsal_openflags_t openflags) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.reopen2(handle->sub_handle, state, openflags); op_ctx->fsal_export = &export->export; return status; } fsal_status_t nullfs_read2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buf_size, void *buffer, size_t *read_amount, bool *eof, struct io_info *info) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.read2(handle->sub_handle, bypass, state, offset, buf_size, buffer, read_amount, eof, info); op_ctx->fsal_export = &export->export; return status; } fsal_status_t nullfs_write2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buf_size, void *buffer, size_t *write_amount, bool *fsal_stable, struct io_info *info) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.write2(handle->sub_handle, bypass, state, offset, buf_size, buffer, write_amount, fsal_stable, info); op_ctx->fsal_export = &export->export; return status; } fsal_status_t nullfs_seek2(struct fsal_obj_handle *obj_hdl, struct state_t *state, struct io_info *info) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.seek2(handle->sub_handle, state, info); op_ctx->fsal_export = &export->export; return status; } fsal_status_t nullfs_io_advise2(struct fsal_obj_handle *obj_hdl, struct state_t *state, struct io_hints *hints) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.io_advise2(handle->sub_handle, state, hints); op_ctx->fsal_export = &export->export; return status; } fsal_status_t nullfs_commit2(struct fsal_obj_handle *obj_hdl, off_t offset, size_t len) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.commit2(handle->sub_handle, offset, len); op_ctx->fsal_export = &export->export; return status; } fsal_status_t nullfs_lock_op2(struct fsal_obj_handle *obj_hdl, struct state_t *state, void *p_owner, fsal_lock_op_t lock_op, fsal_lock_param_t *req_lock, fsal_lock_param_t *conflicting_lock) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.lock_op2(handle->sub_handle, state, p_owner, lock_op, req_lock, conflicting_lock); op_ctx->fsal_export = &export->export; return status; } fsal_status_t nullfs_close2(struct fsal_obj_handle *obj_hdl, struct state_t *state) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.close2(handle->sub_handle, state); op_ctx->fsal_export = &export->export; return status; } nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_NULL/handle.c000066400000000000000000000562401324272410200230700ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * */ /* handle.c */ #include "config.h" #include "fsal.h" #include /* used for 'dirname' */ #include #include #include #include "gsh_list.h" #include "fsal_convert.h" #include "FSAL/fsal_commonlib.h" #include "nullfs_methods.h" #include "nfs4_acls.h" #include /* helpers */ /* handle methods */ /** * Allocate and initialize a new nullfs handle. * * This function doesn't free the sub_handle if the allocation fails. It must * be done in the calling function. * * @param[in] export The nullfs export used by the handle. * @param[in] sub_handle The handle used by the subfsal. * @param[in] fs The filesystem of the new handle. * * @return The new handle, or NULL if the allocation failed. */ static struct nullfs_fsal_obj_handle *nullfs_alloc_handle( struct nullfs_fsal_export *export, struct fsal_obj_handle *sub_handle, struct fsal_filesystem *fs) { struct nullfs_fsal_obj_handle *result; result = gsh_calloc(1, sizeof(struct nullfs_fsal_obj_handle)); /* default handlers */ fsal_obj_handle_init(&result->obj_handle, &export->export, sub_handle->type); /* nullfs handlers */ nullfs_handle_ops_init(&result->obj_handle.obj_ops); result->sub_handle = sub_handle; result->obj_handle.type = sub_handle->type; result->obj_handle.fsid = sub_handle->fsid; result->obj_handle.fileid = sub_handle->fileid; result->obj_handle.fs = fs; result->obj_handle.state_hdl = sub_handle->state_hdl; result->refcnt = 1; return result; } /** * Attempts to create a new nullfs handle, or cleanup memory if it fails. * * This function is a wrapper of nullfs_alloc_handle. It adds error checking * and logging. It also cleans objects allocated in the subfsal if it fails. * * @param[in] export The nullfs export used by the handle. * @param[in,out] sub_handle The handle used by the subfsal. * @param[in] fs The filesystem of the new handle. * @param[in] new_handle Address where the new allocated pointer should be * written. * @param[in] subfsal_status Result of the allocation of the subfsal handle. * * @return An error code for the function. */ fsal_status_t nullfs_alloc_and_check_handle( struct nullfs_fsal_export *export, struct fsal_obj_handle *sub_handle, struct fsal_filesystem *fs, struct fsal_obj_handle **new_handle, fsal_status_t subfsal_status) { /** Result status of the operation. */ fsal_status_t status = subfsal_status; if (!FSAL_IS_ERROR(subfsal_status)) { struct nullfs_fsal_obj_handle *null_handle; null_handle = nullfs_alloc_handle(export, sub_handle, fs); *new_handle = &null_handle->obj_handle; } return status; } /* lookup * deprecated NULL parent && NULL path implies root handle */ static fsal_status_t lookup(struct fsal_obj_handle *parent, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { /** Parent as nullfs handle.*/ struct nullfs_fsal_obj_handle *null_parent = container_of(parent, struct nullfs_fsal_obj_handle, obj_handle); /** Handle given by the subfsal. */ struct fsal_obj_handle *sub_handle = NULL; *handle = NULL; /* call to subfsal lookup with the good context. */ fsal_status_t status; /** Current nullfs export. */ struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); op_ctx->fsal_export = export->export.sub_export; status = null_parent->sub_handle->obj_ops.lookup( null_parent->sub_handle, path, &sub_handle, attrs_out); op_ctx->fsal_export = &export->export; /* wraping the subfsal handle in a nullfs handle. */ return nullfs_alloc_and_check_handle(export, sub_handle, parent->fs, handle, status); } static fsal_status_t makedir(struct fsal_obj_handle *dir_hdl, const char *name, struct attrlist *attrs_in, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out) { *new_obj = NULL; /** Parent directory nullfs handle. */ struct nullfs_fsal_obj_handle *parent_hdl = container_of(dir_hdl, struct nullfs_fsal_obj_handle, obj_handle); /** Current nullfs export. */ struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /** Subfsal handle of the new directory.*/ struct fsal_obj_handle *sub_handle; /* Creating the directory with a subfsal handle. */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = parent_hdl->sub_handle->obj_ops.mkdir( parent_hdl->sub_handle, name, attrs_in, &sub_handle, attrs_out); op_ctx->fsal_export = &export->export; /* wraping the subfsal handle in a nullfs handle. */ return nullfs_alloc_and_check_handle(export, sub_handle, dir_hdl->fs, new_obj, status); } static fsal_status_t makenode(struct fsal_obj_handle *dir_hdl, const char *name, object_file_type_t nodetype, struct attrlist *attrs_in, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out) { /** Parent directory nullfs handle. */ struct nullfs_fsal_obj_handle *nullfs_dir = container_of(dir_hdl, struct nullfs_fsal_obj_handle, obj_handle); /** Current nullfs export. */ struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /** Subfsal handle of the new node.*/ struct fsal_obj_handle *sub_handle; *new_obj = NULL; /* Creating the node with a subfsal handle. */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = nullfs_dir->sub_handle->obj_ops.mknode( nullfs_dir->sub_handle, name, nodetype, attrs_in, &sub_handle, attrs_out); op_ctx->fsal_export = &export->export; /* wraping the subfsal handle in a nullfs handle. */ return nullfs_alloc_and_check_handle(export, sub_handle, dir_hdl->fs, new_obj, status); } /** makesymlink * Note that we do not set mode bits on symlinks for Linux/POSIX * They are not really settable in the kernel and are not checked * anyway (default is 0777) because open uses that target's mode */ static fsal_status_t makesymlink(struct fsal_obj_handle *dir_hdl, const char *name, const char *link_path, struct attrlist *attrs_in, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out) { /** Parent directory nullfs handle. */ struct nullfs_fsal_obj_handle *nullfs_dir = container_of(dir_hdl, struct nullfs_fsal_obj_handle, obj_handle); /** Current nullfs export. */ struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /** Subfsal handle of the new link.*/ struct fsal_obj_handle *sub_handle; *new_obj = NULL; /* creating the file with a subfsal handle. */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = nullfs_dir->sub_handle->obj_ops.symlink( nullfs_dir->sub_handle, name, link_path, attrs_in, &sub_handle, attrs_out); op_ctx->fsal_export = &export->export; /* wraping the subfsal handle in a nullfs handle. */ return nullfs_alloc_and_check_handle(export, sub_handle, dir_hdl->fs, new_obj, status); } static fsal_status_t readsymlink(struct fsal_obj_handle *obj_hdl, struct gsh_buffdesc *link_content, bool refresh) { struct nullfs_fsal_obj_handle *handle = (struct nullfs_fsal_obj_handle *) obj_hdl; struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.readlink(handle->sub_handle, link_content, refresh); op_ctx->fsal_export = &export->export; return status; } static fsal_status_t linkfile(struct fsal_obj_handle *obj_hdl, struct fsal_obj_handle *destdir_hdl, const char *name) { struct nullfs_fsal_obj_handle *handle = (struct nullfs_fsal_obj_handle *) obj_hdl; struct nullfs_fsal_obj_handle *nullfs_dir = (struct nullfs_fsal_obj_handle *) destdir_hdl; struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.link( handle->sub_handle, nullfs_dir->sub_handle, name); op_ctx->fsal_export = &export->export; return status; } /** * Callback function for read_dirents. * * See fsal_readdir_cb type for more details. * * This function restores the context for the upper stacked fsal or inode. * * @param name Directly passed to upper layer. * @param dir_state A nullfs_readdir_state struct. * @param cookie Directly passed to upper layer. * * @return Result coming from the upper layer. */ static enum fsal_dir_result nullfs_readdir_cb( const char *name, struct fsal_obj_handle *sub_handle, struct attrlist *attrs, void *dir_state, fsal_cookie_t cookie) { struct nullfs_readdir_state *state = (struct nullfs_readdir_state *) dir_state; struct fsal_obj_handle *new_obj; if (FSAL_IS_ERROR(nullfs_alloc_and_check_handle(state->exp, sub_handle, sub_handle->fs, &new_obj, fsalstat(ERR_FSAL_NO_ERROR, 0)))) { return false; } op_ctx->fsal_export = &state->exp->export; enum fsal_dir_result result = state->cb(name, new_obj, attrs, state->dir_state, cookie); op_ctx->fsal_export = state->exp->export.sub_export; return result; } /** * read_dirents * read the directory and call through the callback function for * each entry. * @param dir_hdl [IN] the directory to read * @param whence [IN] where to start (next) * @param dir_state [IN] pass thru of state to callback * @param cb [IN] callback function * @param eof [OUT] eof marker true == end of dir */ static fsal_status_t read_dirents(struct fsal_obj_handle *dir_hdl, fsal_cookie_t *whence, void *dir_state, fsal_readdir_cb cb, attrmask_t attrmask, bool *eof) { struct nullfs_fsal_obj_handle *handle = container_of(dir_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); struct nullfs_readdir_state cb_state = { .cb = cb, .dir_state = dir_state, .exp = export }; /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.readdir(handle->sub_handle, whence, &cb_state, nullfs_readdir_cb, attrmask, eof); op_ctx->fsal_export = &export->export; return status; } /** * @brief Compute the readdir cookie for a given filename. * * Some FSALs are able to compute the cookie for a filename deterministically * from the filename. They also have a defined order of entries in a directory * based on the name (could be strcmp sort, could be strict alpha sort, could * be deterministic order based on cookie - in any case, the dirent_cmp method * will also be provided. * * The returned cookie is the cookie that can be passed as whence to FIND that * directory entry. This is different than the cookie passed in the readdir * callback (which is the cookie of the NEXT entry). * * @param[in] parent Directory file name belongs to. * @param[in] name File name to produce the cookie for. * * @retval 0 if not supported. * @returns The cookie value. */ fsal_cookie_t compute_readdir_cookie(struct fsal_obj_handle *parent, const char *name) { fsal_cookie_t cookie; struct nullfs_fsal_obj_handle *handle = container_of(parent, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; cookie = handle->sub_handle->obj_ops.compute_readdir_cookie( handle->sub_handle, name); op_ctx->fsal_export = &export->export; return cookie; } /** * @brief Help sort dirents. * * For FSALs that are able to compute the cookie for a filename * deterministically from the filename, there must also be a defined order of * entries in a directory based on the name (could be strcmp sort, could be * strict alpha sort, could be deterministic order based on cookie). * * Although the cookies could be computed, the caller will already have them * and thus will provide them to save compute time. * * @param[in] parent Directory entries belong to. * @param[in] name1 File name of first dirent * @param[in] cookie1 Cookie of first dirent * @param[in] name2 File name of second dirent * @param[in] cookie2 Cookie of second dirent * * @retval < 0 if name1 sorts before name2 * @retval == 0 if name1 sorts the same as name2 * @retval >0 if name1 sorts after name2 */ int dirent_cmp(struct fsal_obj_handle *parent, const char *name1, fsal_cookie_t cookie1, const char *name2, fsal_cookie_t cookie2) { int rc; struct nullfs_fsal_obj_handle *handle = container_of(parent, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; rc = handle->sub_handle->obj_ops.dirent_cmp(handle->sub_handle, name1, cookie1, name2, cookie2); op_ctx->fsal_export = &export->export; return rc; } static fsal_status_t renamefile(struct fsal_obj_handle *obj_hdl, struct fsal_obj_handle *olddir_hdl, const char *old_name, struct fsal_obj_handle *newdir_hdl, const char *new_name) { struct nullfs_fsal_obj_handle *nullfs_olddir = container_of(olddir_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_obj_handle *nullfs_newdir = container_of(newdir_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_obj_handle *nullfs_obj = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = nullfs_olddir->sub_handle->obj_ops.rename( nullfs_obj->sub_handle, nullfs_olddir->sub_handle, old_name, nullfs_newdir->sub_handle, new_name); op_ctx->fsal_export = &export->export; return status; } static fsal_status_t getattrs(struct fsal_obj_handle *obj_hdl, struct attrlist *attrib_get) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.getattrs(handle->sub_handle, attrib_get); op_ctx->fsal_export = &export->export; return status; } static fsal_status_t nullfs_setattr2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, struct attrlist *attrs) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.setattr2( handle->sub_handle, bypass, state, attrs); op_ctx->fsal_export = &export->export; return status; } /* file_unlink * unlink the named file in the directory */ static fsal_status_t file_unlink(struct fsal_obj_handle *dir_hdl, struct fsal_obj_handle *obj_hdl, const char *name) { struct nullfs_fsal_obj_handle *nullfs_dir = container_of(dir_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_obj_handle *nullfs_obj = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = nullfs_dir->sub_handle->obj_ops.unlink( nullfs_dir->sub_handle, nullfs_obj->sub_handle, name); op_ctx->fsal_export = &export->export; return status; } /* handle_to_wire * fill in the opaque f/s file handle part. * we zero the buffer to length first. This MAY already be done above * at which point, remove memset here because the caller is zeroing * the whole struct. */ static fsal_status_t handle_to_wire(const struct fsal_obj_handle *obj_hdl, fsal_digesttype_t output_type, struct gsh_buffdesc *fh_desc) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.handle_to_wire( handle->sub_handle, output_type, fh_desc); op_ctx->fsal_export = &export->export; return status; } /** * handle_to_key * return a handle descriptor into the handle in this object handle * @TODO reminder. make sure things like hash keys don't point here * after the handle is released. */ static void handle_to_key(struct fsal_obj_handle *obj_hdl, struct gsh_buffdesc *fh_desc) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; handle->sub_handle->obj_ops.handle_to_key(handle->sub_handle, fh_desc); op_ctx->fsal_export = &export->export; } /* * release * release our handle first so they know we are gone */ static void release(struct fsal_obj_handle *obj_hdl) { struct nullfs_fsal_obj_handle *hdl = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; hdl->sub_handle->obj_ops.release(hdl->sub_handle); op_ctx->fsal_export = &export->export; /* cleaning data allocated by nullfs */ fsal_obj_handle_fini(&hdl->obj_handle); gsh_free(hdl); } void nullfs_handle_ops_init(struct fsal_obj_ops *ops) { ops->release = release; ops->lookup = lookup; ops->readdir = read_dirents; ops->compute_readdir_cookie = compute_readdir_cookie, ops->dirent_cmp = dirent_cmp, ops->mkdir = makedir; ops->mknode = makenode; ops->symlink = makesymlink; ops->readlink = readsymlink; ops->getattrs = getattrs; ops->link = linkfile; ops->rename = renamefile; ops->unlink = file_unlink; ops->close = nullfs_close; ops->handle_to_wire = handle_to_wire; ops->handle_to_key = handle_to_key; /* Multi-FD */ ops->open2 = nullfs_open2; ops->check_verifier = nullfs_check_verifier; ops->status2 = nullfs_status2; ops->reopen2 = nullfs_reopen2; ops->read2 = nullfs_read2; ops->write2 = nullfs_write2; ops->seek2 = nullfs_seek2; ops->io_advise2 = nullfs_io_advise2; ops->commit2 = nullfs_commit2; ops->lock_op2 = nullfs_lock_op2; ops->setattr2 = nullfs_setattr2; ops->close2 = nullfs_close2; /* xattr related functions */ ops->list_ext_attrs = nullfs_list_ext_attrs; ops->getextattr_id_by_name = nullfs_getextattr_id_by_name; ops->getextattr_value_by_name = nullfs_getextattr_value_by_name; ops->getextattr_value_by_id = nullfs_getextattr_value_by_id; ops->setextattr_value = nullfs_setextattr_value; ops->setextattr_value_by_id = nullfs_setextattr_value_by_id; ops->remove_extattr_by_id = nullfs_remove_extattr_by_id; ops->remove_extattr_by_name = nullfs_remove_extattr_by_name; } /* export methods that create object handles */ /* lookup_path * modeled on old api except we don't stuff attributes. * KISS */ fsal_status_t nullfs_lookup_path(struct fsal_export *exp_hdl, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { /** Handle given by the subfsal. */ struct fsal_obj_handle *sub_handle = NULL; *handle = NULL; /* call underlying FSAL ops with underlying FSAL handle */ struct nullfs_fsal_export *exp = container_of(exp_hdl, struct nullfs_fsal_export, export); /* call to subfsal lookup with the good context. */ fsal_status_t status; op_ctx->fsal_export = exp->export.sub_export; status = exp->export.sub_export->exp_ops.lookup_path( exp->export.sub_export, path, &sub_handle, attrs_out); op_ctx->fsal_export = &exp->export; /* wraping the subfsal handle in a nullfs handle. */ /* Note : nullfs filesystem = subfsal filesystem or NULL ? */ return nullfs_alloc_and_check_handle(exp, sub_handle, NULL, handle, status); } /* create_handle * Does what original FSAL_ExpandHandle did (sort of) * returns a ref counted handle to be later used in cache_inode etc. * NOTE! you must release this thing when done with it! * BEWARE! Thanks to some holes in the *AT syscalls implementation, * we cannot get an fd on an AF_UNIX socket, nor reliably on block or * character special devices. Sorry, it just doesn't... * we could if we had the handle of the dir it is in, but this method * is for getting handles off the wire for cache entries that have LRU'd. * Ideas and/or clever hacks are welcome... */ fsal_status_t nullfs_create_handle(struct fsal_export *exp_hdl, struct gsh_buffdesc *hdl_desc, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { /** Current nullfs export. */ struct nullfs_fsal_export *export = container_of(exp_hdl, struct nullfs_fsal_export, export); struct fsal_obj_handle *sub_handle; /*< New subfsal handle.*/ *handle = NULL; /* call to subfsal lookup with the good context. */ fsal_status_t status; op_ctx->fsal_export = export->export.sub_export; status = export->export.sub_export->exp_ops.create_handle( export->export.sub_export, hdl_desc, &sub_handle, attrs_out); op_ctx->fsal_export = &export->export; /* wraping the subfsal handle in a nullfs handle. */ /* Note : nullfs filesystem = subfsal filesystem or NULL ? */ return nullfs_alloc_and_check_handle(export, sub_handle, NULL, handle, status); } nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_NULL/main.c000066400000000000000000000114401324272410200225520ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /* main.c * Module core functions */ #include "config.h" #include "fsal.h" #include /* used for 'dirname' */ #include #include #include #include #include "gsh_list.h" #include "FSAL/fsal_init.h" #include "nullfs_methods.h" /* NULLFS FSAL module private storage */ struct nullfs_fsal_module { struct fsal_module fsal; struct fsal_staticfsinfo_t fs_info; /* nullfsfs_specific_initinfo_t specific_info; placeholder */ }; /* FSAL name determines name of shared library: libfsal.so */ const char myname[] = "NULL"; /* filesystem info for NULLFS */ static struct fsal_staticfsinfo_t default_posix_info = { .maxfilesize = UINT64_MAX, .maxlink = _POSIX_LINK_MAX, .maxnamelen = 1024, .maxpathlen = 1024, .no_trunc = true, .chown_restricted = true, .case_insensitive = false, .case_preserving = true, .link_support = true, .symlink_support = true, .lock_support = true, .lock_support_async_block = false, .named_attr = true, .unique_handles = true, .lease_time = {10, 0}, .acl_support = FSAL_ACLSUPPORT_ALLOW, .cansettime = true, .homogenous = true, .supported_attrs = ALL_ATTRIBUTES, .maxread = FSAL_MAXIOSIZE, .maxwrite = FSAL_MAXIOSIZE, .umask = 0, .auth_exportpath_xdev = false, .xattr_access_rights = 0400, /* root=RW, owner=R */ .link_supports_permission_checks = true, }; /* private helper for export object */ struct fsal_staticfsinfo_t *nullfs_staticinfo(struct fsal_module *hdl) { struct nullfs_fsal_module *myself; myself = container_of(hdl, struct nullfs_fsal_module, fsal); return &myself->fs_info; } /* Module methods */ /* init_config * must be called with a reference taken (via lookup_fsal) */ static fsal_status_t init_config(struct fsal_module *fsal_hdl, config_file_t config_struct, struct config_error_type *err_type) { struct nullfs_fsal_module *nullfs_me = container_of(fsal_hdl, struct nullfs_fsal_module, fsal); /* get a copy of the defaults */ nullfs_me->fs_info = default_posix_info; /* Configuration setting options: * 1. there are none that are changeable. (this case) * * 2. we set some here. These must be independent of whatever * may be set by lower level fsals. * * If there is any filtering or change of parameters in the stack, * this must be done in export data structures, not fsal params because * a stackable could be configured above multiple fsals for multiple * diverse exports. */ display_fsinfo(&nullfs_me->fs_info); LogFullDebug(COMPONENT_FSAL, "Supported attributes default = 0x%" PRIx64, default_posix_info.supported_attrs); LogDebug(COMPONENT_FSAL, "FSAL INIT: Supported attributes mask = 0x%" PRIx64, nullfs_me->fs_info.supported_attrs); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* Internal NULLFS method linkage to export object */ fsal_status_t nullfs_create_export(struct fsal_module *fsal_hdl, void *parse_node, struct config_error_type *err_type, const struct fsal_up_vector *up_ops); /* Module initialization. * Called by dlopen() to register the module * keep a private pointer to me in myself */ /* my module private storage */ static struct nullfs_fsal_module NULLFS; struct next_ops next_ops; /* linkage to the exports and handle ops initializers */ MODULE_INIT void nullfs_init(void) { int retval; struct fsal_module *myself = &NULLFS.fsal; retval = register_fsal(myself, myname, FSAL_MAJOR_VERSION, FSAL_MINOR_VERSION, FSAL_ID_NO_PNFS); if (retval != 0) { fprintf(stderr, "NULLFS module failed to register"); return; } myself->m_ops.create_export = nullfs_create_export; myself->m_ops.init_config = init_config; } MODULE_FINI void nullfs_unload(void) { int retval; retval = unregister_fsal(&NULLFS.fsal); if (retval != 0) { fprintf(stderr, "NULLFS module failed to unregister"); return; } } nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_NULL/nullfs_methods.h000066400000000000000000000136451324272410200246720ustar00rootroot00000000000000/* NULLFS methods for handles */ struct nullfs_fsal_obj_handle; struct next_ops { struct export_ops exp_ops; /*< Vector of operations */ struct fsal_obj_ops obj_ops; /*< Shared handle methods vector */ struct fsal_dsh_ops dsh_ops; /*< Shared handle methods vector */ const struct fsal_up_vector *up_ops; /*< Upcall operations */ }; /** * Structure used to store data for read_dirents callback. * * Before executing the upper level callback (it might be another * stackable fsal or the inode cache), the context has to be restored. */ struct nullfs_readdir_state { fsal_readdir_cb cb; /*< Callback to the upper layer. */ struct nullfs_fsal_export *exp; /*< Export of the current nullfsal. */ void *dir_state; /*< State to be sent to the next callback. */ }; extern struct next_ops next_ops; extern struct fsal_up_vector fsal_up_top; void nullfs_handle_ops_init(struct fsal_obj_ops *ops); /* * NULLFS internal export */ struct nullfs_fsal_export { struct fsal_export export; /* Other private export data goes here */ }; fsal_status_t nullfs_lookup_path(struct fsal_export *exp_hdl, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out); fsal_status_t nullfs_create_handle(struct fsal_export *exp_hdl, struct gsh_buffdesc *hdl_desc, struct fsal_obj_handle **handle, struct attrlist *attrs_out); fsal_status_t nullfs_alloc_and_check_handle( struct nullfs_fsal_export *export, struct fsal_obj_handle *sub_handle, struct fsal_filesystem *fs, struct fsal_obj_handle **new_handle, fsal_status_t subfsal_status); /* * NULLFS internal object handle * * It contains a pointer to the fsal_obj_handle used by the subfsal. * * AF_UNIX sockets are strange ducks. I personally cannot see why they * are here except for the ability of a client to see such an animal with * an 'ls' or get rid of one with an 'rm'. You can't open them in the * usual file way so open_by_handle_at leads to a deadend. To work around * this, we save the args that were used to mknod or lookup the socket. */ struct nullfs_fsal_obj_handle { struct fsal_obj_handle obj_handle; /*< Handle containing nullfs data.*/ struct fsal_obj_handle *sub_handle; /*< Handle of the sub fsal.*/ int32_t refcnt; /*< Reference count. This is signed to make mistakes easy to see. */ }; int nullfs_fsal_open(struct nullfs_fsal_obj_handle *, int, fsal_errors_t *); int nullfs_fsal_readlink(struct nullfs_fsal_obj_handle *, fsal_errors_t *); static inline bool nullfs_unopenable_type(object_file_type_t type) { if ((type == SOCKET_FILE) || (type == CHARACTER_FILE) || (type == BLOCK_FILE)) { return true; } else { return false; } } /* I/O management */ fsal_status_t nullfs_close(struct fsal_obj_handle *obj_hdl); /* Multi-FD */ fsal_status_t nullfs_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 *attrs_in, fsal_verifier_t verifier, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out, bool *caller_perm_check); bool nullfs_check_verifier(struct fsal_obj_handle *obj_hdl, fsal_verifier_t verifier); fsal_openflags_t nullfs_status2(struct fsal_obj_handle *obj_hdl, struct state_t *state); fsal_status_t nullfs_reopen2(struct fsal_obj_handle *obj_hdl, struct state_t *state, fsal_openflags_t openflags); fsal_status_t nullfs_read2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buf_size, void *buffer, size_t *read_amount, bool *eof, struct io_info *info); fsal_status_t nullfs_write2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buf_size, void *buffer, size_t *write_amount, bool *fsal_stable, struct io_info *info); fsal_status_t nullfs_seek2(struct fsal_obj_handle *obj_hdl, struct state_t *state, struct io_info *info); fsal_status_t nullfs_io_advise2(struct fsal_obj_handle *obj_hdl, struct state_t *state, struct io_hints *hints); fsal_status_t nullfs_commit2(struct fsal_obj_handle *obj_hdl, off_t offset, size_t len); fsal_status_t nullfs_lock_op2(struct fsal_obj_handle *obj_hdl, struct state_t *state, void *p_owner, fsal_lock_op_t lock_op, fsal_lock_param_t *req_lock, fsal_lock_param_t *conflicting_lock); fsal_status_t nullfs_close2(struct fsal_obj_handle *obj_hdl, struct state_t *state); /* extended attributes management */ fsal_status_t nullfs_list_ext_attrs(struct fsal_obj_handle *obj_hdl, unsigned int cookie, fsal_xattrent_t *xattrs_tab, unsigned int xattrs_tabsize, unsigned int *p_nb_returned, int *end_of_list); fsal_status_t nullfs_getextattr_id_by_name(struct fsal_obj_handle *obj_hdl, const char *xattr_name, unsigned int *pxattr_id); fsal_status_t nullfs_getextattr_value_by_name(struct fsal_obj_handle *obj_hdl, const char *xattr_name, caddr_t buffer_addr, size_t buffer_size, size_t *p_output_size); fsal_status_t nullfs_getextattr_value_by_id(struct fsal_obj_handle *obj_hdl, unsigned int xattr_id, caddr_t buffer_addr, size_t buffer_size, size_t *p_output_size); fsal_status_t nullfs_setextattr_value(struct fsal_obj_handle *obj_hdl, const char *xattr_name, caddr_t buffer_addr, size_t buffer_size, int create); fsal_status_t nullfs_setextattr_value_by_id(struct fsal_obj_handle *obj_hdl, unsigned int xattr_id, caddr_t buffer_addr, size_t buffer_size); fsal_status_t nullfs_remove_extattr_by_id(struct fsal_obj_handle *obj_hdl, unsigned int xattr_id); fsal_status_t nullfs_remove_extattr_by_name(struct fsal_obj_handle *obj_hdl, const char *xattr_name); nfs-ganesha-2.6.0/src/FSAL/Stackable_FSALs/FSAL_NULL/xattrs.c000066400000000000000000000153521324272410200231610ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * */ /* xattrs.c * NULL object (file|dir) handle object extended attributes */ #include "config.h" #include "fsal.h" #include /* used for 'dirname' */ #include #include #include #include #include #include "gsh_list.h" #include "fsal_convert.h" #include "FSAL/fsal_commonlib.h" #include "nullfs_methods.h" fsal_status_t nullfs_list_ext_attrs(struct fsal_obj_handle *obj_hdl, unsigned int argcookie, fsal_xattrent_t *xattrs_tab, unsigned int xattrs_tabsize, unsigned int *p_nb_returned, int *end_of_list) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.list_ext_attrs( handle->sub_handle, argcookie, xattrs_tab, xattrs_tabsize, p_nb_returned, end_of_list); op_ctx->fsal_export = &export->export; return status; } fsal_status_t nullfs_getextattr_id_by_name(struct fsal_obj_handle *obj_hdl, const char *xattr_name, unsigned int *pxattr_id) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.getextattr_id_by_name( handle->sub_handle, xattr_name, pxattr_id); op_ctx->fsal_export = &export->export; return status; } fsal_status_t nullfs_getextattr_value_by_id(struct fsal_obj_handle *obj_hdl, unsigned int xattr_id, caddr_t buffer_addr, size_t buffer_size, size_t *p_output_size) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.getextattr_value_by_id( handle->sub_handle, xattr_id, buffer_addr, buffer_size, p_output_size); op_ctx->fsal_export = &export->export; return status; } fsal_status_t nullfs_getextattr_value_by_name(struct fsal_obj_handle *obj_hdl, const char *xattr_name, caddr_t buffer_addr, size_t buffer_size, size_t *p_output_size) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.getextattr_value_by_name( handle->sub_handle, xattr_name, buffer_addr, buffer_size, p_output_size); op_ctx->fsal_export = &export->export; return status; } fsal_status_t nullfs_setextattr_value(struct fsal_obj_handle *obj_hdl, const char *xattr_name, caddr_t buffer_addr, size_t buffer_size, int create) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.setextattr_value( handle->sub_handle, xattr_name, buffer_addr, buffer_size, create); op_ctx->fsal_export = &export->export; return status; } fsal_status_t nullfs_setextattr_value_by_id(struct fsal_obj_handle *obj_hdl, unsigned int xattr_id, caddr_t buffer_addr, size_t buffer_size) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.setextattr_value_by_id( handle->sub_handle, xattr_id, buffer_addr, buffer_size); op_ctx->fsal_export = &export->export; return status; } fsal_status_t nullfs_remove_extattr_by_id(struct fsal_obj_handle *obj_hdl, unsigned int xattr_id) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.remove_extattr_by_id( handle->sub_handle, xattr_id); op_ctx->fsal_export = &export->export; return status; } fsal_status_t nullfs_remove_extattr_by_name(struct fsal_obj_handle *obj_hdl, const char *xattr_name) { struct nullfs_fsal_obj_handle *handle = container_of(obj_hdl, struct nullfs_fsal_obj_handle, obj_handle); struct nullfs_fsal_export *export = container_of(op_ctx->fsal_export, struct nullfs_fsal_export, export); /* calling subfsal method */ op_ctx->fsal_export = export->export.sub_export; fsal_status_t status = handle->sub_handle->obj_ops.remove_extattr_by_name( handle->sub_handle, xattr_name); op_ctx->fsal_export = &export->export; return status; } nfs-ganesha-2.6.0/src/FSAL/access_check.c000066400000000000000000000570631324272410200177770ustar00rootroot00000000000000/** * @addtogroup FSAL * @{ */ /** * @file access_check.c * @brief File/object access checking */ #include "config.h" #include "fsal.h" #include "nfs_core.h" #include #include "FSAL/access_check.h" #include #include #include #include #include #include static bool fsal_check_ace_owner(uid_t uid, struct user_cred *creds) { return (creds->caller_uid == uid); } static bool fsal_check_ace_group(gid_t gid, struct user_cred *creds) { int i; if (creds->caller_gid == gid) return true; for (i = 0; i < creds->caller_glen; i++) { if (creds->caller_garray[i] == gid) return true; } return false; } static bool fsal_check_ace_matches(fsal_ace_t *pace, struct user_cred *creds, bool is_owner, bool is_group) { bool result = false; char *cause = ""; if (IS_FSAL_ACE_SPECIAL_ID(*pace)) switch (pace->who.uid) { case FSAL_ACE_SPECIAL_OWNER: if (is_owner) { result = true; cause = "special owner"; } break; case FSAL_ACE_SPECIAL_GROUP: if (is_group) { result = true; cause = "special group"; } break; case FSAL_ACE_SPECIAL_EVERYONE: result = true; cause = "special everyone"; break; default: break; } else if (IS_FSAL_ACE_GROUP_ID(*pace)) { if (fsal_check_ace_group(pace->who.gid, creds)) { result = true; cause = "group"; } } else { if (fsal_check_ace_owner(pace->who.uid, creds)) { result = true; cause = "owner"; } } LogFullDebug(COMPONENT_NFS_V4_ACL, "result: %d, cause: %s, flag: 0x%X, who: %d", result, cause, pace->flag, GET_FSAL_ACE_WHO(*pace)); return result; } static bool fsal_check_ace_applicable(fsal_ace_t *pace, struct user_cred *creds, bool is_dir, bool is_owner, bool is_group, bool is_root) { bool is_applicable = false; bool is_file = !is_dir; /* To be applicable, the entry should not be INHERIT_ONLY. */ if (IS_FSAL_ACE_INHERIT_ONLY(*pace)) { LogFullDebug(COMPONENT_NFS_V4_ACL, "Not applicable, inherit only"); return false; } /* Use internal flag to further check the entry is applicable * to this object type. */ if (is_file) { if (!IS_FSAL_FILE_APPLICABLE(*pace)) { LogFullDebug(COMPONENT_NFS_V4_ACL, "Not applicable to file"); return false; } } else { /* directory */ if (!IS_FSAL_DIR_APPLICABLE(*pace)) { LogFullDebug(COMPONENT_NFS_V4_ACL, "Not applicable to dir"); return false; } } /* The user should match who value. */ is_applicable = is_root || fsal_check_ace_matches(pace, creds, is_owner, is_group); if (is_applicable) LogFullDebug(COMPONENT_NFS_V4_ACL, "Applicable, flag=0X%x", pace->flag); else LogFullDebug(COMPONENT_NFS_V4_ACL, "Not applicable to given user"); return is_applicable; } static const char *fsal_ace_type(fsal_acetype_t type) { switch (type) { case FSAL_ACE_TYPE_ALLOW: return "A"; case FSAL_ACE_TYPE_DENY: return "D "; case FSAL_ACE_TYPE_AUDIT: return "U"; case FSAL_ACE_TYPE_ALARM: return "L"; default: return "unknown"; } } static const char *fsal_ace_perm(fsal_aceperm_t perm) { static char buf[64]; char *c = buf; if (perm & FSAL_ACE_PERM_READ_DATA) *c++ = 'r'; if (perm & FSAL_ACE_PERM_WRITE_DATA) *c++ = 'w'; if (perm & FSAL_ACE_PERM_APPEND_DATA) *c++ = 'a'; if (perm & FSAL_ACE_PERM_EXECUTE) *c++ = 'x'; if (perm & FSAL_ACE_PERM_DELETE) *c++ = 'd'; if (perm & FSAL_ACE_PERM_DELETE_CHILD) *c++ = 'D'; if (perm & FSAL_ACE_PERM_READ_ATTR) *c++ = 't'; if (perm & FSAL_ACE_PERM_WRITE_ATTR) *c++ = 'T'; if (perm & FSAL_ACE_PERM_READ_NAMED_ATTR) *c++ = 'n'; if (perm & FSAL_ACE_PERM_WRITE_NAMED_ATTR) *c++ = 'N'; if (perm & FSAL_ACE_PERM_READ_ACL) *c++ = 'c'; if (perm & FSAL_ACE_PERM_WRITE_ACL) *c++ = 'C'; if (perm & FSAL_ACE_PERM_WRITE_OWNER) *c++ = 'o'; if (perm & FSAL_ACE_PERM_SYNCHRONIZE) *c++ = 'y'; *c = '\0'; return buf; } static const char *fsal_ace_flag(char *buf, fsal_aceflag_t flag) { char *c = buf; if (flag & FSAL_ACE_FLAG_GROUP_ID) *c++ = 'g'; if (flag & FSAL_ACE_FLAG_FILE_INHERIT) *c++ = 'f'; if (flag & FSAL_ACE_FLAG_DIR_INHERIT) *c++ = 'd'; if (flag & FSAL_ACE_FLAG_NO_PROPAGATE) *c++ = 'n'; if (flag & FSAL_ACE_FLAG_INHERIT_ONLY) *c++ = 'i'; if (flag & FSAL_ACE_FLAG_SUCCESSFUL) *c++ = 'S'; if (flag & FSAL_ACE_FLAG_FAILED) *c++ = 'F'; if (flag & FSAL_ACE_FLAG_INHERITED) *c++ = 'I'; if (flag & FSAL_ACE_IFLAG_EXCLUDE_FILES) *c++ = 'x'; if (flag & FSAL_ACE_IFLAG_EXCLUDE_DIRS) *c++ = 'X'; if (flag & FSAL_ACE_IFLAG_SPECIAL_ID) *c++ = 'S'; if (flag & FSAL_ACE_IFLAG_MODE_GEN) *c++ = 'G'; *c = '\0'; return buf; } void fsal_print_ace_int(log_components_t component, log_levels_t debug, fsal_ace_t *ace, char *file, int line, char *function) { char fbuf[16]; char ibuf[16]; if (!isLevel(component, debug)) return; DisplayLogComponentLevel(component, file, line, function, debug, "ACE %s:%s(%s):%u:%s", fsal_ace_type(ace->type), fsal_ace_flag(fbuf, ace->flag), fsal_ace_flag(ibuf, ace->iflag), ace->who.uid, fsal_ace_perm(ace->perm)); } void fsal_print_acl_int(log_components_t component, log_levels_t debug, fsal_acl_t *acl, char *file, int line, char *function) { fsal_ace_t *ace = NULL; if (!isLevel(component, debug)) return; DisplayLogComponentLevel(component, file, line, function, debug, "ACL naces: %u aces:", acl->naces); for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) fsal_print_ace_int(component, debug, ace, file, line, function); } int display_fsal_inherit_flags(struct display_buffer *dspbuf, fsal_ace_t *pace) { if (!pace) return display_cat(dspbuf, "NULL"); return display_printf(dspbuf, "Inherit:%s%s%s%s", IS_FSAL_ACE_FILE_INHERIT(*pace) ? " file" : "", IS_FSAL_ACE_DIR_INHERIT(*pace) ? " dir" : "", IS_FSAL_ACE_INHERIT_ONLY(*pace) ? " inherit_only" : "", IS_FSAL_ACE_NO_PROPAGATE(*pace) ? " no_propagate" : ""); } int display_fsal_ace(struct display_buffer *dspbuf, int ace_number, fsal_ace_t *pace, bool is_dir) { int b_left; if (!pace) return display_cat(dspbuf, "ACE: "); /* Print the entire ACE. */ b_left = display_printf(dspbuf, "ACE %d:", ace_number); /* ACE type. */ if (b_left > 0) b_left = display_cat(dspbuf, IS_FSAL_ACE_ALLOW(*pace) ? " allow" : IS_FSAL_ACE_DENY(*pace) ? " deny" : IS_FSAL_ACE_AUDIT(*pace) ? " audit" : " ?"); /* ACE who and its type. */ if (b_left > 0 && IS_FSAL_ACE_SPECIAL_ID(*pace)) b_left = display_cat(dspbuf, IS_FSAL_ACE_SPECIAL_OWNER(*pace) ? " owner@" : IS_FSAL_ACE_SPECIAL_GROUP(*pace) ? " group@" : IS_FSAL_ACE_SPECIAL_EVERYONE(*pace) ? " everyone@" : ""); if (b_left > 0 && !IS_FSAL_ACE_SPECIAL_ID(*pace)) { if (IS_FSAL_ACE_SPECIAL_ID(*pace)) b_left = display_printf(dspbuf, " gid %d", pace->who.gid); else b_left = display_printf(dspbuf, " uid %d", pace->who.uid); } /* ACE mask. */ if (b_left > 0) b_left = display_fsal_v4mask(dspbuf, pace->perm, is_dir); /* ACE Inherit flags. */ if (b_left > 0 && IS_FSAL_ACE_INHERIT(*pace)) b_left = display_fsal_inherit_flags(dspbuf, pace); return b_left; } int display_fsal_v4mask(struct display_buffer *dspbuf, fsal_aceperm_t v4mask, bool is_dir) { int b_left = display_printf(dspbuf, "0x%06x", v4mask); if (b_left > 0 && IS_FSAL_ACE_BIT(v4mask, FSAL_ACE_PERM_READ_DATA)) b_left = display_cat(dspbuf, " READ"); if (b_left > 0 && IS_FSAL_ACE_BIT(v4mask, FSAL_ACE_PERM_WRITE_DATA) && is_dir) b_left = display_cat(dspbuf, " ADD_FILE"); if (b_left > 0 && IS_FSAL_ACE_BIT(v4mask, FSAL_ACE_PERM_WRITE_DATA) && !is_dir) b_left = display_cat(dspbuf, " WRITE"); if (b_left > 0 && IS_FSAL_ACE_BIT(v4mask, FSAL_ACE_PERM_APPEND_DATA) && is_dir) b_left = display_cat(dspbuf, " ADD_SUBDIR"); if (b_left > 0 && IS_FSAL_ACE_BIT(v4mask, FSAL_ACE_PERM_APPEND_DATA) && !is_dir) b_left = display_cat(dspbuf, " APPEND"); if (b_left > 0 && IS_FSAL_ACE_BIT(v4mask, FSAL_ACE_PERM_READ_NAMED_ATTR)) b_left = display_cat(dspbuf, " READ_NAMED"); if (b_left > 0 && IS_FSAL_ACE_BIT(v4mask, FSAL_ACE_PERM_WRITE_NAMED_ATTR)) b_left = display_cat(dspbuf, " WRITE_NAMED"); if (b_left > 0 && IS_FSAL_ACE_BIT(v4mask, FSAL_ACE_PERM_EXECUTE)) b_left = display_cat(dspbuf, " EXECUTE"); if (b_left > 0 && IS_FSAL_ACE_BIT(v4mask, FSAL_ACE_PERM_DELETE_CHILD)) b_left = display_cat(dspbuf, " DELETE_CHILD"); if (b_left > 0 && IS_FSAL_ACE_BIT(v4mask, FSAL_ACE_PERM_READ_ATTR)) b_left = display_cat(dspbuf, " READ_ATTR"); if (b_left > 0 && IS_FSAL_ACE_BIT(v4mask, FSAL_ACE_PERM_WRITE_ATTR)) b_left = display_cat(dspbuf, " WRITE_ATTR"); if (b_left > 0 && IS_FSAL_ACE_BIT(v4mask, FSAL_ACE_PERM_DELETE)) b_left = display_cat(dspbuf, " DELETE"); if (b_left > 0 && IS_FSAL_ACE_BIT(v4mask, FSAL_ACE_PERM_READ_ACL)) b_left = display_cat(dspbuf, " READ_ACL"); if (b_left > 0 && IS_FSAL_ACE_BIT(v4mask, FSAL_ACE_PERM_WRITE_ACL)) b_left = display_cat(dspbuf, " WRITE_ACL"); if (b_left > 0 && IS_FSAL_ACE_BIT(v4mask, FSAL_ACE_PERM_WRITE_OWNER)) b_left = display_cat(dspbuf, " WRITE_OWNER"); if (b_left > 0 && IS_FSAL_ACE_BIT(v4mask, FSAL_ACE_PERM_SYNCHRONIZE)) b_left = display_cat(dspbuf, " SYNCHRONIZE"); if (b_left > 0 && IS_FSAL_ACE_BIT(v4mask, FSAL_ACE4_PERM_CONTINUE)) b_left = display_cat(dspbuf, " CONTINUE"); return b_left; } static void fsal_print_access_by_acl(int naces, int ace_number, fsal_ace_t *pace, fsal_aceperm_t perm, enum fsal_errors_t access_result, bool is_dir, struct user_cred *creds) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = { sizeof(str), str, str }; int b_left; if (!isFullDebug(COMPONENT_NFS_V4_ACL)) return; if (access_result == ERR_FSAL_NO_ERROR) b_left = display_cat(&dspbuf, "access granted"); else if (access_result == ERR_FSAL_PERM) b_left = display_cat(&dspbuf, "access denied (EPERM)"); else b_left = display_cat(&dspbuf, "access denied (EACCESS)"); if (b_left > 0) b_left = display_printf(&dspbuf, " uid %u gid %u Access req:", creds->caller_uid, creds->caller_gid); if (b_left > 0) b_left = display_fsal_v4mask(&dspbuf, perm, is_dir); if (b_left > 0 && (naces != ace_number)) b_left = display_fsal_ace(&dspbuf, ace_number, pace, is_dir); LogFullDebug(COMPONENT_NFS_V4_ACL, "%s", str); } /** * @brief Check access using v4 ACL list * * @param[in] creds * @param[in] v4mask * @param[in] allowed * @param[in] denied * @param[in] p_object_attributes * * @return ERR_FSAL_NO_ERROR, ERR_FSAL_ACCESS, or ERR_FSAL_NO_ACE */ static fsal_status_t fsal_check_access_acl(struct user_cred *creds, fsal_aceperm_t v4mask, fsal_accessflags_t *allowed, fsal_accessflags_t *denied, struct attrlist *p_object_attributes) { fsal_aceperm_t missing_access; fsal_aceperm_t tperm; uid_t uid; gid_t gid; fsal_acl_t *pacl = NULL; fsal_ace_t *pace = NULL; int ace_number = 0; bool is_dir = false; bool is_owner = false; bool is_group = false; bool is_root = false; if (allowed != NULL) *allowed = 0; if (denied != NULL) *denied = 0; if (!p_object_attributes->acl) { /* Means that FSAL_ACE4_REQ_FLAG was set, but no ACLs */ LogFullDebug(COMPONENT_NFS_V4_ACL, "Allow ACE required, but no ACLs"); return fsalstat(ERR_FSAL_NO_ACE, 0); } /* unsatisfied flags */ missing_access = v4mask & ~FSAL_ACE4_PERM_CONTINUE; if (!missing_access) { LogFullDebug(COMPONENT_NFS_V4_ACL, "Nothing was requested"); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* Get file ownership information. */ uid = p_object_attributes->owner; gid = p_object_attributes->group; pacl = p_object_attributes->acl; is_dir = (p_object_attributes->type == DIRECTORY); is_root = op_ctx->fsal_export->exp_ops.is_superuser( op_ctx->fsal_export, creds); if (is_root) { if (is_dir) { if (allowed != NULL) *allowed = v4mask; /* On a directory, allow root anything. */ LogFullDebug(COMPONENT_NFS_V4_ACL, "Met root privileges on directory"); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* Otherwise, allow root anything but execute. */ missing_access &= FSAL_ACE_PERM_EXECUTE; if (allowed != NULL) *allowed = v4mask & ~FSAL_ACE_PERM_EXECUTE; if (!missing_access) { LogFullDebug(COMPONENT_NFS_V4_ACL, "Met root privileges"); return fsalstat(ERR_FSAL_NO_ERROR, 0); } } LogFullDebug(COMPONENT_NFS_V4_ACL, "file acl=%p, file uid=%u, file gid=%u, ", pacl, uid, gid); if (isFullDebug(COMPONENT_NFS_V4_ACL)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = { sizeof(str), str, str }; (void)display_fsal_v4mask(&dspbuf, v4mask, p_object_attributes->type == DIRECTORY); LogFullDebug(COMPONENT_NFS_V4_ACL, "user uid=%u, user gid= %u, v4mask=%s", creds->caller_uid, creds->caller_gid, str); } is_owner = fsal_check_ace_owner(uid, creds); is_group = fsal_check_ace_group(gid, creds); /* Always grant READ_ACL, WRITE_ACL and READ_ATTR, WRITE_ATTR * to the file owner. */ if (is_owner) { if (allowed != NULL) *allowed |= v4mask & (FSAL_ACE_PERM_WRITE_ACL | FSAL_ACE_PERM_READ_ACL | FSAL_ACE_PERM_WRITE_ATTR | FSAL_ACE_PERM_READ_ATTR); missing_access &= ~(FSAL_ACE_PERM_WRITE_ACL | FSAL_ACE_PERM_READ_ACL); missing_access &= ~(FSAL_ACE_PERM_WRITE_ATTR | FSAL_ACE_PERM_READ_ATTR); if (!missing_access) { LogFullDebug(COMPONENT_NFS_V4_ACL, "Met owner privileges"); return fsalstat(ERR_FSAL_NO_ERROR, 0); } } /** @todo Even if user is admin, audit/alarm checks should be done. */ for (pace = pacl->aces; pace < pacl->aces + pacl->naces; pace++) { ace_number += 1; LogFullDebug(COMPONENT_NFS_V4_ACL, "ace numnber: %d ace type 0x%X perm 0x%X flag 0x%X who %u", ace_number, pace->type, pace->perm, pace->flag, GET_FSAL_ACE_WHO(*pace)); /* Process Allow and Deny entries. */ if (!IS_FSAL_ACE_ALLOW(*pace) && !IS_FSAL_ACE_DENY(*pace)) { LogFullDebug(COMPONENT_NFS_V4_ACL, "not allow or deny"); continue; } LogFullDebug(COMPONENT_NFS_V4_ACL, "allow or deny"); /* Check if this ACE is applicable. */ if (fsal_check_ace_applicable(pace, creds, is_dir, is_owner, is_group, is_root)) { if (IS_FSAL_ACE_ALLOW(*pace)) { /* Do not set bits which are already denied */ if (denied) tperm = pace->perm & ~*denied; else tperm = pace->perm; LogFullDebug(COMPONENT_NFS_V4_ACL, "allow perm 0x%X remainingPerms 0x%X", tperm, missing_access); if (allowed != NULL) *allowed |= v4mask & tperm; missing_access &= ~(tperm & missing_access); if (!missing_access) { fsal_print_access_by_acl( pacl->naces, ace_number, pace, v4mask, ERR_FSAL_NO_ERROR, is_dir, creds); break; } } else if ((pace->perm & missing_access) && !is_root) { fsal_print_access_by_acl( pacl->naces, ace_number, pace, v4mask, #ifndef ENABLE_RFC_ACL (pace->perm & missing_access & (FSAL_ACE_PERM_WRITE_ATTR | FSAL_ACE_PERM_WRITE_ACL | FSAL_ACE_PERM_WRITE_OWNER)) != 0 ? ERR_FSAL_PERM : #endif /* ENABLE_RFC_ACL */ ERR_FSAL_ACCESS, is_dir, creds); if (denied != NULL) *denied |= v4mask & pace->perm; if (denied == NULL || (v4mask & FSAL_ACE4_PERM_CONTINUE) == 0) { #ifndef ENABLE_RFC_ACL if ((pace->perm & missing_access & (FSAL_ACE_PERM_WRITE_ATTR | FSAL_ACE_PERM_WRITE_ACL | FSAL_ACE_PERM_WRITE_OWNER)) != 0) { LogDebug(COMPONENT_NFS_V4_ACL, "access denied (EPERM)"); return fsalstat(ERR_FSAL_PERM, 0); } else { LogDebug(COMPONENT_NFS_V4_ACL, "access denied (EACCESS)"); return fsalstat(ERR_FSAL_ACCESS, 0); } #else /* ENABLE_RFC_ACL */ LogDebug(COMPONENT_NFS_V4_ACL, "access denied (EACCESS)"); return fsalstat(ERR_FSAL_ACCESS, 0); #endif /* ENABLE_RFC_ACL */ } missing_access &= ~(pace->perm & missing_access); /* If this DENY ACE blocked the last * remaining requested access * bits, break out of the loop because * we're done and don't * want to evaluate any more ACEs. */ if (!missing_access) break; } } } if (IS_FSAL_ACE4_REQ(v4mask) && missing_access) { LogDebug(COMPONENT_NFS_V4_ACL, "final access unknown (NO_ACE)"); return fsalstat(ERR_FSAL_NO_ACE, 0); } else if (missing_access || (denied != NULL && *denied != 0)) { #ifndef ENABLE_RFC_ACL if ((missing_access & (FSAL_ACE_PERM_WRITE_ATTR | FSAL_ACE_PERM_WRITE_ACL | FSAL_ACE_PERM_WRITE_OWNER)) != 0) { LogDebug(COMPONENT_NFS_V4_ACL, "final access denied (EPERM)"); return fsalstat(ERR_FSAL_PERM, 0); } else { LogDebug(COMPONENT_NFS_V4_ACL, "final access denied (EACCESS)"); return fsalstat(ERR_FSAL_ACCESS, 0); } #else /* ENABLE_RFC_ACL */ LogDebug(COMPONENT_NFS_V4_ACL, "final access denied (EACCESS)"); return fsalstat(ERR_FSAL_ACCESS, 0); #endif /* ENABLE_RFC_ACL */ } else { LogFullDebug(COMPONENT_NFS_V4_ACL, "access granted"); return fsalstat(ERR_FSAL_NO_ERROR, 0); } } /** * @brief Check access using mode bits only * * @param[in] creds * @param[in] access_type * @param[in] allowed * @param[in] denied * @param[in] p_object_attributes * * @return ERR_FSAL_NO_ERROR or ERR_FSAL_ACCESS */ static fsal_status_t fsal_check_access_no_acl(struct user_cred *creds, fsal_accessflags_t access_type, fsal_accessflags_t *allowed, fsal_accessflags_t *denied, struct attrlist *p_object_attributes) { uid_t uid; gid_t gid; mode_t mode; fsal_accessflags_t mask; bool rc; if (allowed != NULL) *allowed = 0; if (denied != NULL) *denied = 0; if (!access_type) { LogFullDebug(COMPONENT_NFS_V4_ACL, "Nothing was requested"); return fsalstat(ERR_FSAL_NO_ERROR, 0); } uid = p_object_attributes->owner; gid = p_object_attributes->group; mode = p_object_attributes->mode; LogFullDebug(COMPONENT_NFS_V4_ACL, "file Mode=%#o, file uid=%u, file gid= %u, user uid=%u, user gid= %u, access_type=0X%x", mode, uid, gid, creds->caller_uid, creds->caller_gid, access_type); if (op_ctx->fsal_export->exp_ops.is_superuser(op_ctx->fsal_export, creds)) { if (p_object_attributes->type == DIRECTORY) { if (allowed != NULL) *allowed = access_type; LogFullDebug(COMPONENT_NFS_V4_ACL, "Root has full access on directories."); return fsalstat(ERR_FSAL_NO_ERROR, 0); } rc = ((access_type & FSAL_X_OK) == 0) || ((mode & (S_IXOTH | S_IXUSR | S_IXGRP)) != 0); if (!rc) { if (allowed != NULL) *allowed = access_type & ~FSAL_X_OK; if (denied != NULL) *denied = access_type & FSAL_X_OK; LogFullDebug(COMPONENT_NFS_V4_ACL, "Root is not allowed execute access unless at least one user is allowed execute access."); } else { if (allowed != NULL) *allowed = access_type; LogFullDebug(COMPONENT_NFS_V4_ACL, "Root is granted access."); } return rc ? fsalstat(ERR_FSAL_NO_ERROR, 0) : fsalstat(ERR_FSAL_ACCESS, 0); } /* If the uid of the file matches the uid of the user, * then the uid mode bits take precedence. */ if (creds->caller_uid == uid) { LogFullDebug(COMPONENT_NFS_V4_ACL, "Using owner mode %#o", mode & S_IRWXU); mode >>= 6; } else { /* followed by group(s) */ if (creds->caller_gid == gid) { LogFullDebug(COMPONENT_NFS_V4_ACL, "Using group mode %#o", mode & S_IRWXG); mode >>= 3; } else { /* Test if file belongs to alt user's groups */ int i; for (i = 0; i < creds->caller_glen; i++) { if (creds->caller_garray[i] == gid) { LogFullDebug(COMPONENT_NFS_V4_ACL, "Using group mode %#o for alt group #%d", mode & S_IRWXG, i); mode >>= 3; break; } } } } /* others fall out the bottom... */ /* Convert the shifted mode into an access_type mask */ mask = ((mode & S_IROTH) ? FSAL_R_OK : 0) | ((mode & S_IWOTH) ? FSAL_W_OK : 0) | ((mode & S_IXOTH) ? FSAL_X_OK : 0); LogFullDebug(COMPONENT_NFS_V4_ACL, "Mask=0X%x, Access Type=0X%x Allowed=0X%x Denied=0X%x %s", mask, access_type, mask & access_type, ~mask & access_type, (mask & access_type) == access_type ? "ALLOWED" : "DENIED"); if (allowed != NULL) *allowed = mask & access_type; if (denied != NULL) *denied = ~mask & access_type; /* Success if mask covers all the requested bits */ return (mask & access_type) == access_type ? fsalstat(ERR_FSAL_NO_ERROR, 0) : fsalstat(ERR_FSAL_ACCESS, 0); } /* test_access * common (default) access check method for fsal_obj_handle objects. * NOTE: A fsal can replace this method with their own custom access * checker. If so and they wish to have an option to switch * between their custom and this one, it their test_access * method's responsibility to do that test and select this one. */ fsal_status_t fsal_test_access(struct fsal_obj_handle *obj_hdl, fsal_accessflags_t access_type, fsal_accessflags_t *allowed, fsal_accessflags_t *denied, bool owner_skip) { struct attrlist attrs; fsal_status_t status; fsal_prepare_attrs(&attrs, op_ctx->fsal_export->exp_ops.fs_supported_attrs( op_ctx->fsal_export) & (ATTRS_CREDS | ATTR_MODE | ATTR_ACL)); status = obj_hdl->obj_ops.getattrs(obj_hdl, &attrs); if (FSAL_IS_ERROR(status)) goto out; if (owner_skip && attrs.owner == op_ctx->creds->caller_uid) { status = fsalstat(ERR_FSAL_NO_ERROR, 0); goto out; } if (IS_FSAL_ACE4_REQ(access_type) || (attrs.acl != NULL && IS_FSAL_ACE4_MASK_VALID(access_type))) { status = fsal_check_access_acl(op_ctx->creds, FSAL_ACE4_MASK(access_type), allowed, denied, &attrs); } else { /* fall back to use mode to check access. */ status = fsal_check_access_no_acl(op_ctx->creds, FSAL_MODE_MASK(access_type), allowed, denied, &attrs); } out: /* Done with the attrs */ fsal_release_attrs(&attrs); return status; } uid_t ganesha_uid; gid_t ganesha_gid; int ganesha_ngroups; gid_t *ganesha_groups; void fsal_set_credentials(const struct user_cred *creds) { if (set_threadgroups(creds->caller_glen, creds->caller_garray) != 0) LogFatal(COMPONENT_FSAL, "Could not set Context credentials"); setgroup(creds->caller_gid); setuser(creds->caller_uid); } void fsal_save_ganesha_credentials(void) { int i; char buffer[1024], *p = buffer; ganesha_uid = setuser(0); setuser(ganesha_uid); ganesha_gid = setgroup(0); setgroup(ganesha_gid); ganesha_ngroups = getgroups(0, NULL); if (ganesha_ngroups > 0) { ganesha_groups = gsh_malloc(ganesha_ngroups * sizeof(gid_t)); if (getgroups(ganesha_ngroups, ganesha_groups) != ganesha_ngroups) { LogFatal(COMPONENT_FSAL, "Could not get list of ganesha groups"); } } p += sprintf(p, "Ganesha uid=%d gid=%d ngroups=%d", (int)ganesha_uid, (int)ganesha_gid, ganesha_ngroups); if (ganesha_ngroups != 0) p += sprintf(p, " ("); for (i = 0; i < ganesha_ngroups; i++) { if ((p - buffer) < (sizeof(buffer) - 10)) { if (i == 0) p += sprintf(p, "%d", (int)ganesha_groups[i]); else p += sprintf(p, " %d", (int)ganesha_groups[i]); } } if (ganesha_ngroups != 0) p += sprintf(p, ")"); LogInfo(COMPONENT_FSAL, "%s", buffer); } void fsal_restore_ganesha_credentials(void) { setuser(ganesha_uid); setgroup(ganesha_gid); if (set_threadgroups(ganesha_ngroups, ganesha_groups) != 0) LogFatal(COMPONENT_FSAL, "Could not set Ganesha credentials"); } /** @} */ nfs-ganesha-2.6.0/src/FSAL/common_pnfs.c000066400000000000000000000260521324272410200177110ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) 2011 The Linux Box Corporation * Author: Adam C. Emerson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @addtogroup FSAL * @{ */ #include "config.h" #include #include #include #include #include #include #include #include "log.h" #include "fsal.h" #include "fsal_pnfs.h" #include "pnfs_utils.h" #include "nfs4.h" #include "nfs_exports.h" #include "nfs_file_handle.h" #include "nfs_proto_functions.h" struct fsal_module *pnfs_fsal[FSAL_ID_COUNT]; /** * @file common_pnfs.c * @author Adam C. Emerson * @brief Utility functions for pNFS * * Utility functions expected to be used by more than one FSAL * * implementing pNFS. */ /* * Functions potentially useful to all MDSs of layout types */ /** * * @brief Encode/decode an fsal_deviceid_t * * The difference between this and xdr_deviceid4 is that this function * treats the deviceid as two 64-bit integers (putting them in network * byte order) while xdr_deviceid4 treats the deviceid as an opaque * string of 16 bytes. This function may be more convenient given * that we expect the high quad to be in network byte order and assign * significance to it in nfs4_op_getdeviceinfo. * * @param[in,out] xdrs The XDR stream * @param[in,out] deviceid The deviceid to encode/decode * * @retval true on success. * @retval false on failure. */ bool xdr_fsal_deviceid(XDR *xdrs, struct pnfs_deviceid *deviceid) { if (!xdr_opaque(xdrs, (char *)deviceid, NFS4_DEVICEID4_SIZE)) return false; return true; } /** * @brief Encode most IPv4 netaddrs * * This convenience function writes an encoded netaddr4 to an XDR * stream given a protocol, IP address, and port. * * @param[in,out] xdrs The XDR stream * @param[in] proto The protocol identifier. Currently this most * be one of 6 (TCP), 17 (UDP), or 132 (SCTP) * in host byte order * @param[in] addr The IPv4 address in host byte order * @param[in] port The port address in host byte order * * @return NFSv4 status codes. */ nfsstat4 FSAL_encode_ipv4_netaddr(XDR *xdrs, uint16_t proto, uint32_t addr, uint16_t port) { /* The address family mark string */ const char *mark = NULL; /* Six groups of up to three digits each, five dots, and a null */ const size_t v4_addrbuff_len = 24; /* The buffer to which we output the string form of the address */ char addrbuff[v4_addrbuff_len]; /* Pointer to the beginning of the buffer, the reference of which is passed to xdr_string */ char *buffptr = &addrbuff[0]; /* Return value from snprintf to check for overflow or error */ size_t written_length = 0; /* First, we output the correct netid for the protocol */ switch (proto) { case 6: mark = "tcp"; break; case 17: mark = "udp"; break; case 123: mark = "sctp"; break; default: LogCrit(COMPONENT_FSAL, "Caller supplied invalid protocol %u", proto); return NFS4ERR_SERVERFAULT; } if (!xdr_string(xdrs, (char **)&mark, 5)) { LogCrit(COMPONENT_FSAL, "Unable to encode protocol mark."); return NFS4ERR_SERVERFAULT; } /* Then we convert the address and port to a string and encode it. */ written_length = snprintf(addrbuff, v4_addrbuff_len, "%u.%u.%u.%u.%u.%u", (unsigned int) ((addr & 0xff000000) >> 0x18), (unsigned int) ((addr & 0x00ff0000) >> 0x10), (unsigned int) ((addr & 0x0000ff00) >> 0x08), (unsigned int) (addr & 0x000000ff), (unsigned int) ((port & 0xff00) >> 0x08), (unsigned int) (port & 0x00ff)); if (written_length >= v4_addrbuff_len) { LogCrit(COMPONENT_FSAL, "Programming error at %s:%u %s causing snprintf to overflow address buffer.", __FILE__, __LINE__, __func__); return NFS4ERR_SERVERFAULT; } if (!xdr_string(xdrs, &buffptr, v4_addrbuff_len)) { LogCrit(COMPONENT_FSAL, "Unable to encode address."); return NFS4ERR_SERVERFAULT; } return NFS4_OK; } /* * Functions specific to NFSV4_1_FILES layouts */ /** * * @brief Internal function to convert file handles * * This function creates a filehandle (that will be recognizes by * Ganesha as a DS filehandle) containing the supplied opaque. * * @param[in] fh_desc FSAL specific DS handle * @param[in] export_id Export ID (So we don't have to require that * the MDS export and DS export share IDs in all * cases.) * @param[out] v4_handle An nfs_fh4 descriptor for the handle. * * @return NFSv4 error codes */ static nfsstat4 make_file_handle_ds(const struct gsh_buffdesc *fh_desc, const uint16_t server_id, nfs_fh4 *wirehandle) { /* The v4_handle being constructed */ file_handle_v4_t *v4_handle = (file_handle_v4_t *) wirehandle->nfs_fh4_val; if ((offsetof(struct file_handle_v4, fsopaque) + fh_desc->len) > wirehandle->nfs_fh4_len) { LogMajor(COMPONENT_PNFS, "DS handle too big to encode!"); return NFS4ERR_SERVERFAULT; } wirehandle->nfs_fh4_len = offsetof(struct file_handle_v4, fsopaque)+fh_desc->len; v4_handle->fhversion = GANESHA_FH_VERSION; v4_handle->fs_len = fh_desc->len; memcpy(v4_handle->fsopaque, fh_desc->addr, fh_desc->len); v4_handle->id.servers = htons(server_id); #if (BYTE_ORDER == BIG_ENDIAN) v4_handle->fhflags1 = FILE_HANDLE_V4_FLAG_DS | FH_FSAL_BIG_ENDIAN; #else v4_handle->fhflags1 = FILE_HANDLE_V4_FLAG_DS; #endif return NFS4_OK; } /** * @brief Convenience function to encode loc_body * * This function allows the FSAL to encode an nfsv4_1_files_layout4 * without having to allocate and construct all the components of the * structure, including file handles. * * To encode a completed nfsv4_1_file_layout4 structure, call * xdr_nfsv4_1_file_layout4. * * @note This function encodes Ganesha data server handles in the * loc_body, it does not use the FSAL's DS handle unadorned. * * @param[out] xdrs XDR stream * @param[in] deviceid The deviceid for the layout * @param[in] util Stripe width and flags for the layout * @param[in] first_idx First stripe index * @param[in] ptrn_ofst Pattern offset * @param[in] ds_ids Server IDs of DSs for each file handle * @param[in] num_fhs Number of file handles in array * @param[in] fhs Array if buffer descriptors holding opaque DS * handles * @return NFS status codes. */ nfsstat4 FSAL_encode_file_layout(XDR *xdrs, const struct pnfs_deviceid *deviceid, nfl_util4 util, const uint32_t first_idx, const offset4 ptrn_ofst, const uint16_t *ds_ids, const uint32_t num_fhs, const struct gsh_buffdesc *fhs) { /* Index for traversing FH array */ size_t i = 0; /* NFS status code */ nfsstat4 nfs_status = 0; offset4 *p_ofst = (offset4 *) &ptrn_ofst; /* kill compile warnings */ if (!xdr_fsal_deviceid(xdrs, (struct pnfs_deviceid *)deviceid)) { LogMajor(COMPONENT_PNFS, "Failed encoding deviceid."); return NFS4ERR_SERVERFAULT; } if (!xdr_nfl_util4(xdrs, &util)) { LogMajor(COMPONENT_PNFS, "Failed encoding nfl_util4."); return NFS4ERR_SERVERFAULT; } if (!xdr_uint32_t(xdrs, (uint32_t *) &first_idx)) { LogMajor(COMPONENT_PNFS, "Failed encoding first_stripe_index."); return NFS4ERR_SERVERFAULT; } if (!xdr_offset4(xdrs, p_ofst)) { LogMajor(COMPONENT_PNFS, "Failed encoding pattern_offset."); return NFS4ERR_SERVERFAULT; } if (!xdr_uint32_t(xdrs, (int32_t *) &num_fhs)) { LogMajor(COMPONENT_PNFS, "Failed encoding length of FH array."); return NFS4ERR_SERVERFAULT; } for (i = 0; i < num_fhs; i++) { nfs_fh4 handle; char buffer[NFS4_FHSIZE]; handle.nfs_fh4_val = buffer; handle.nfs_fh4_len = sizeof(buffer); memset(buffer, 0, sizeof(buffer)); nfs_status = make_file_handle_ds(fhs + i, *(ds_ids + i), &handle); if (nfs_status != NFS4_OK) { LogMajor(COMPONENT_PNFS, "Failed converting FH %zu.", i); return nfs_status; } if (!xdr_bytes(xdrs, (char **)&handle.nfs_fh4_val, &handle.nfs_fh4_len, handle.nfs_fh4_len)) { LogMajor(COMPONENT_PNFS, "Failed encoding FH %zu.", i); return NFS4ERR_SERVERFAULT; } } return NFS4_OK; } /** * @brief Convenience function to encode one multipath_list * * This function writes a multipath list representation of an array of * hosts accessed through most IPv4 protocols. * * @param[in,out] xdrs The XDR stream * @param[in] num_hosts Number of hosts in array * @param[in] hosts Array of hosts * * @return NFSv4 Status code * */ nfsstat4 FSAL_encode_v4_multipath(XDR *xdrs, const uint32_t num_hosts, const fsal_multipath_member_t *hosts) { /* Index for traversing host array */ size_t i = 0; /* NFS status */ nfsstat4 nfs_status = 0; if (!xdr_uint32_t(xdrs, (uint32_t *) &num_hosts)) { LogMajor(COMPONENT_PNFS, "Failed encoding length of FH array."); return NFS4ERR_SERVERFAULT; } for (i = 0; i < num_hosts; i++) { nfs_status = FSAL_encode_ipv4_netaddr(xdrs, hosts[i].proto, hosts[i].addr, hosts[i].port); if (nfs_status != NFS4_OK) return nfs_status; } return NFS4_OK; } /** * @brief Convert POSIX error codes to NFS 4 error codes * * @param[in] posix_errorcode The error code returned from POSIX. * * @return The NFSv4 error code associated to posix_errorcode. * */ nfsstat4 posix2nfs4_error(const int posix_errorcode) { switch (posix_errorcode) { case EPERM: return NFS4ERR_PERM; case ENOENT: return NFS4ERR_NOENT; case ECONNREFUSED: case ECONNABORTED: case ECONNRESET: case EIO: case ENFILE: case EMFILE: case EPIPE: return NFS4ERR_IO; case ENODEV: case ENXIO: return NFS4ERR_NXIO; case EBADF: return NFS4ERR_OPENMODE; case ENOMEM: return NFS4ERR_SERVERFAULT; case EACCES: return NFS4ERR_ACCESS; case EFAULT: return NFS4ERR_SERVERFAULT; case EEXIST: return NFS4ERR_EXIST; case EXDEV: return NFS4ERR_XDEV; case ENOTDIR: return NFS4ERR_NOTDIR; case EISDIR: return NFS4ERR_ISDIR; case EINVAL: return NFS4ERR_INVAL; case EFBIG: return NFS4ERR_FBIG; case ENOSPC: return NFS4ERR_NOSPC; case EMLINK: return NFS4ERR_MLINK; case EDQUOT: return NFS4ERR_DQUOT; case ENAMETOOLONG: return NFS4ERR_NAMETOOLONG; case ENOTEMPTY: return NFS4ERR_NOTEMPTY; case ESTALE: return NFS4ERR_STALE; case ENOTSUP: return NFS4ERR_NOTSUPP; default: return NFS4ERR_SERVERFAULT; } } /** @} */ nfs-ganesha-2.6.0/src/FSAL/commonlib.c000066400000000000000000002210661324272410200173540ustar00rootroot00000000000000/* * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @addtogroup FSAL * @{ */ /** * @file fsal_commonlib.c * @author Jim Lieb * @brief Common functions for and private to FSAL modules. * * The prime requirement for functions to be here is that they operate only * on the public part of the FSAL api and are therefore sharable by all fsal * implementations. */ #include "config.h" #include /* avoid conflicts with sys/queue.h */ #ifdef LINUX #include /* for major(3), minor(3) */ #endif #include /* used for 'dirname' */ #include #include #include #include #include #include #include #include #if __FreeBSD__ #include #else #include #endif #include #include "common_utils.h" #ifdef HAVE_MNTENT_H #include #endif #include "gsh_config.h" #include "gsh_list.h" #ifdef USE_BLKID #include #include #endif #include "fsal.h" #include "fsal_api.h" #include "FSAL/fsal_commonlib.h" #include "FSAL/access_check.h" #include "fsal_private.h" #include "fsal_convert.h" #include "nfs4_acls.h" #include "sal_data.h" #include "nfs_init.h" #include "mdcache.h" #ifdef USE_BLKID static struct blkid_struct_cache *cache; #endif /* fsal_attach_export * called from the FSAL's create_export method with a reference on the fsal. */ int fsal_attach_export(struct fsal_module *fsal_hdl, struct glist_head *obj_link) { int retval = 0; if (atomic_fetch_int32_t(&fsal_hdl->refcount) > 0) { glist_add(&fsal_hdl->exports, obj_link); } else { LogCrit(COMPONENT_CONFIG, "Attaching export with out holding a reference!. hdl= = 0x%p", fsal_hdl); retval = EINVAL; } return retval; } /* fsal_detach_export * called by an export when it is releasing itself. * does not require a reference to be taken. The list has * kept the fsal "busy". */ void fsal_detach_export(struct fsal_module *fsal_hdl, struct glist_head *obj_link) { PTHREAD_RWLOCK_wrlock(&fsal_hdl->lock); glist_del(obj_link); PTHREAD_RWLOCK_unlock(&fsal_hdl->lock); } /** * @brief Initialize export ops vectors * * @param[in] exp Export handle * */ void fsal_export_init(struct fsal_export *exp) { memcpy(&exp->exp_ops, &def_export_ops, sizeof(struct export_ops)); exp->export_id = op_ctx->ctx_export->export_id; } /** * @brief Stack an export on top of another * * Set up export stacking for stackable FSALs * * @param[in] sub_export Export being stacked on * @param[in] super_export Export stacking on top * @return Return description */ void fsal_export_stack(struct fsal_export *sub_export, struct fsal_export *super_export) { sub_export->super_export = super_export; super_export->sub_export = sub_export; } /** * @brief Free export ops vectors * * Free the memory allocated by init_export_ops. Poison pointers. * * @param[in] exp_hdl Export handle * */ void free_export_ops(struct fsal_export *exp_hdl) { memset(&exp_hdl->exp_ops, 0, sizeof(exp_hdl->exp_ops)); /* poison */ } /* fsal_export to fsal_obj_handle helpers */ void fsal_obj_handle_init(struct fsal_obj_handle *obj, struct fsal_export *exp, object_file_type_t type) { pthread_rwlockattr_t attrs; memcpy(&obj->obj_ops, &def_handle_ops, sizeof(struct fsal_obj_ops)); obj->fsal = exp->fsal; obj->type = type; pthread_rwlockattr_init(&attrs); #ifdef GLIBC pthread_rwlockattr_setkind_np( &attrs, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP); #endif PTHREAD_RWLOCK_init(&obj->obj_lock, &attrs); PTHREAD_RWLOCK_wrlock(&obj->fsal->lock); glist_add(&obj->fsal->handles, &obj->handles); PTHREAD_RWLOCK_unlock(&obj->fsal->lock); } void fsal_obj_handle_fini(struct fsal_obj_handle *obj) { PTHREAD_RWLOCK_wrlock(&obj->fsal->lock); glist_del(&obj->handles); PTHREAD_RWLOCK_unlock(&obj->fsal->lock); PTHREAD_RWLOCK_destroy(&obj->obj_lock); memset(&obj->obj_ops, 0, sizeof(obj->obj_ops)); /* poison myself */ obj->fsal = NULL; } /* fsal_module to fsal_pnfs_ds helpers */ void fsal_pnfs_ds_init(struct fsal_pnfs_ds *pds, struct fsal_module *fsal) { pthread_rwlockattr_t attrs; pds->refcount = 1; /* we start out with a reference */ fsal->m_ops.fsal_pnfs_ds_ops(&pds->s_ops); pds->fsal = fsal; pthread_rwlockattr_init(&attrs); #ifdef GLIBC pthread_rwlockattr_setkind_np( &attrs, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP); #endif PTHREAD_RWLOCK_init(&pds->lock, &attrs); glist_init(&pds->ds_handles); PTHREAD_RWLOCK_wrlock(&fsal->lock); glist_add(&fsal->servers, &pds->server); PTHREAD_RWLOCK_unlock(&fsal->lock); } void fsal_pnfs_ds_fini(struct fsal_pnfs_ds *pds) { PTHREAD_RWLOCK_wrlock(&pds->fsal->lock); glist_del(&pds->server); PTHREAD_RWLOCK_unlock(&pds->fsal->lock); PTHREAD_RWLOCK_destroy(&pds->lock); memset(&pds->s_ops, 0, sizeof(pds->s_ops)); /* poison myself */ pds->fsal = NULL; } /* fsal_pnfs_ds to fsal_ds_handle helpers */ void fsal_ds_handle_init(struct fsal_ds_handle *dsh, struct fsal_pnfs_ds *pds) { dsh->refcount = 1; /* we start out with a reference */ pds->s_ops.fsal_dsh_ops(&dsh->dsh_ops); dsh->pds = pds; PTHREAD_RWLOCK_wrlock(&pds->lock); glist_add(&pds->ds_handles, &dsh->ds_handle); PTHREAD_RWLOCK_unlock(&pds->lock); } void fsal_ds_handle_fini(struct fsal_ds_handle *dsh) { PTHREAD_RWLOCK_wrlock(&dsh->pds->lock); glist_del(&dsh->ds_handle); PTHREAD_RWLOCK_unlock(&dsh->pds->lock); memset(&dsh->dsh_ops, 0, sizeof(dsh->dsh_ops)); /* poison myself */ dsh->pds = NULL; } /** * @brief FSAL error code to error message * * @param[in] fsal_err Error code * * @return Error message, empty string if not found. */ const char *msg_fsal_err(fsal_errors_t fsal_err) { switch (fsal_err) { case ERR_FSAL_NO_ERROR: return "No error"; case ERR_FSAL_PERM: return "Forbidden action"; case ERR_FSAL_NOENT: return "No such file or directory"; case ERR_FSAL_IO: return "I/O error"; case ERR_FSAL_NXIO: return "No such device or address"; case ERR_FSAL_NOMEM: return "Not enough memory"; case ERR_FSAL_ACCESS: return "Permission denied"; case ERR_FSAL_FAULT: return "Bad address"; case ERR_FSAL_EXIST: return "This object already exists"; case ERR_FSAL_XDEV: return "This operation can't cross filesystems"; case ERR_FSAL_NOTDIR: return "This object is not a directory"; case ERR_FSAL_ISDIR: return "Directory used in a nondirectory operation"; case ERR_FSAL_INVAL: return "Invalid object type"; case ERR_FSAL_FBIG: return "File exceeds max file size"; case ERR_FSAL_NOSPC: return "No space left on filesystem"; case ERR_FSAL_ROFS: return "Read-only filesystem"; case ERR_FSAL_MLINK: return "Too many hard links"; case ERR_FSAL_DQUOT: return "Quota exceeded"; case ERR_FSAL_NAMETOOLONG: return "Max name length exceeded"; case ERR_FSAL_NOTEMPTY: return "The directory is not empty"; case ERR_FSAL_STALE: return "The file no longer exists"; case ERR_FSAL_BADHANDLE: return "Illegal filehandle"; case ERR_FSAL_BADCOOKIE: return "Invalid cookie"; case ERR_FSAL_NOTSUPP: return "Operation not supported"; case ERR_FSAL_TOOSMALL: return "Output buffer too small"; case ERR_FSAL_SERVERFAULT: return "Undefined server error"; case ERR_FSAL_BADTYPE: return "Invalid type for create operation"; case ERR_FSAL_DELAY: return "File busy, retry"; case ERR_FSAL_FHEXPIRED: return "Filehandle expired"; case ERR_FSAL_SYMLINK: return "This is a symbolic link, should be file/directory"; case ERR_FSAL_ATTRNOTSUPP: return "Attribute not supported"; case ERR_FSAL_NOT_INIT: return "Filesystem not initialized"; case ERR_FSAL_ALREADY_INIT: return "Filesystem already initialised"; case ERR_FSAL_BAD_INIT: return "Filesystem initialisation error"; case ERR_FSAL_SEC: return "Security context error"; case ERR_FSAL_NO_QUOTA: return "No Quota available"; case ERR_FSAL_NOT_OPENED: return "File/directory not opened"; case ERR_FSAL_DEADLOCK: return "Deadlock"; case ERR_FSAL_OVERFLOW: return "Overflow"; case ERR_FSAL_INTERRUPT: return "Operation Interrupted"; case ERR_FSAL_BLOCKED: return "Lock Blocked"; case ERR_FSAL_SHARE_DENIED: return "Share Denied"; case ERR_FSAL_LOCKED: return "Locked"; case ERR_FSAL_TIMEOUT: return "Timeout"; case ERR_FSAL_FILE_OPEN: return "File Open"; case ERR_FSAL_UNION_NOTSUPP: return "Union Not Supported"; case ERR_FSAL_IN_GRACE: return "Server in Grace"; case ERR_FSAL_NO_DATA: return "No Data"; case ERR_FSAL_NO_ACE: return "No matching ACE"; case ERR_FSAL_BAD_RANGE: return "Lock not in allowable range"; case ERR_FSAL_CROSS_JUNCTION: return "Crossed Junction"; case ERR_FSAL_BADNAME: return "Invalid Name"; } return "Unknown FSAL error"; } const char *fsal_dir_result_str(enum fsal_dir_result result) { switch (result) { case DIR_CONTINUE: return "DIR_CONTINUE"; case DIR_READAHEAD: return "DIR_READAHEAD"; case DIR_TERMINATE: return "DIR_TERMINATE"; } return ""; } /** * @brief Dump and fsal_staticfsinfo_t to a log * * This is used for debugging * * @param[in] info The info to dump */ void display_fsinfo(struct fsal_staticfsinfo_t *info) { LogDebug(COMPONENT_FSAL, "FileSystem info: {"); LogDebug(COMPONENT_FSAL, " maxfilesize = %" PRIX64 " ", (uint64_t) info->maxfilesize); LogDebug(COMPONENT_FSAL, " maxlink = %" PRIu32, info->maxlink); LogDebug(COMPONENT_FSAL, " maxnamelen = %" PRIu32, info->maxnamelen); LogDebug(COMPONENT_FSAL, " maxpathlen = %" PRIu32, info->maxpathlen); LogDebug(COMPONENT_FSAL, " no_trunc = %d ", info->no_trunc); LogDebug(COMPONENT_FSAL, " chown_restricted = %d ", info->chown_restricted); LogDebug(COMPONENT_FSAL, " case_insensitive = %d ", info->case_insensitive); LogDebug(COMPONENT_FSAL, " case_preserving = %d ", info->case_preserving); LogDebug(COMPONENT_FSAL, " link_support = %d ", info->link_support); LogDebug(COMPONENT_FSAL, " symlink_support = %d ", info->symlink_support); LogDebug(COMPONENT_FSAL, " lock_support = %d ", info->lock_support); LogDebug(COMPONENT_FSAL, " lock_support_async_block = %d ", info->lock_support_async_block); LogDebug(COMPONENT_FSAL, " named_attr = %d ", info->named_attr); LogDebug(COMPONENT_FSAL, " unique_handles = %d ", info->unique_handles); LogDebug(COMPONENT_FSAL, " acl_support = %hu ", info->acl_support); LogDebug(COMPONENT_FSAL, " cansettime = %d ", info->cansettime); LogDebug(COMPONENT_FSAL, " homogenous = %d ", info->homogenous); LogDebug(COMPONENT_FSAL, " supported_attrs = %" PRIX64, info->supported_attrs); LogDebug(COMPONENT_FSAL, " maxread = %" PRIu64, info->maxread); LogDebug(COMPONENT_FSAL, " maxwrite = %" PRIu64, info->maxwrite); LogDebug(COMPONENT_FSAL, " umask = %X ", info->umask); LogDebug(COMPONENT_FSAL, " auth_exportpath_xdev = %d ", info->auth_exportpath_xdev); LogDebug(COMPONENT_FSAL, " xattr_access_rights = %#o ", info->xattr_access_rights); LogDebug(COMPONENT_FSAL, " delegations = %d ", info->delegations); LogDebug(COMPONENT_FSAL, " pnfs_mds = %d ", info->pnfs_mds); LogDebug(COMPONENT_FSAL, " pnfs_ds = %d ", info->pnfs_ds); LogDebug(COMPONENT_FSAL, " fsal_trace = %d ", info->fsal_trace); LogDebug(COMPONENT_FSAL, " fsal_grace = %d ", info->fsal_grace); LogDebug(COMPONENT_FSAL, "}"); } int display_attrlist(struct display_buffer *dspbuf, struct attrlist *attr, bool is_obj) { int b_left = display_start(dspbuf); if (attr->request_mask == 0 && attr->valid_mask == 0 && attr->supported == 0) return display_cat(dspbuf, "No attributes"); if (b_left > 0 && attr->request_mask != 0) b_left = display_printf(dspbuf, "Request Mask=%08x ", (unsigned int) attr->request_mask); if (b_left > 0 && attr->valid_mask != 0) b_left = display_printf(dspbuf, "Valid Mask=%08x ", (unsigned int) attr->valid_mask); if (b_left > 0 && attr->supported != 0) b_left = display_printf(dspbuf, "Supported Mask=%08x ", (unsigned int) attr->supported); if (b_left > 0 && is_obj) b_left = display_printf(dspbuf, "%s", object_file_type_to_str(attr->type)); if (b_left > 0 && FSAL_TEST_MASK(attr->valid_mask, ATTR_NUMLINKS)) b_left = display_printf(dspbuf, " numlinks=0x%"PRIx32, attr->numlinks); if (b_left > 0 && FSAL_TEST_MASK(attr->valid_mask, ATTR_SIZE)) b_left = display_printf(dspbuf, " size=0x%"PRIx64, attr->filesize); if (b_left > 0 && FSAL_TEST_MASK(attr->valid_mask, ATTR_MODE)) b_left = display_printf(dspbuf, " mode=0%"PRIo32, attr->mode); if (b_left > 0 && FSAL_TEST_MASK(attr->valid_mask, ATTR_OWNER)) b_left = display_printf(dspbuf, " owner=0x%"PRIx64, attr->owner); if (b_left > 0 && FSAL_TEST_MASK(attr->valid_mask, ATTR_GROUP)) b_left = display_printf(dspbuf, " group=0x%"PRIx64, attr->group); if (b_left > 0 && FSAL_TEST_MASK(attr->valid_mask, ATTR_ATIME_SERVER)) b_left = display_cat(dspbuf, " atime=SERVER"); if (b_left > 0 && FSAL_TEST_MASK(attr->valid_mask, ATTR_MTIME_SERVER)) b_left = display_cat(dspbuf, " mtime=SERVER"); if (b_left > 0 && FSAL_TEST_MASK(attr->valid_mask, ATTR_ATIME)) { b_left = display_cat(dspbuf, " atime="); if (b_left > 0) b_left = display_timespec(dspbuf, &attr->atime); } if (b_left > 0 && FSAL_TEST_MASK(attr->valid_mask, ATTR_MTIME)) { b_left = display_cat(dspbuf, " mtime="); if (b_left > 0) b_left = display_timespec(dspbuf, &attr->mtime); } return b_left; } void log_attrlist(log_components_t component, log_levels_t level, const char *reason, struct attrlist *attr, bool is_obj, char *file, int line, char *function) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; (void) display_attrlist(&dspbuf, attr, is_obj); DisplayLogComponentLevel(component, file, line, function, level, "%s %s attributes %s", reason, is_obj ? "obj" : "set", str); } int open_dir_by_path_walk(int first_fd, const char *path, struct stat *stat) { char *name, *rest, *p; int fd = first_fd, len, rc, err; /* Get length of the path */ len = strlen(path); /* Strip terminating '/' by shrinking length */ while (path[len-1] == '/' && len > 1) len--; /* Allocate space for duplicate */ name = alloca(len + 1); /* Copy the string */ memcpy(name, path, len); /* Terminate it */ name[len] = '\0'; /* Determine if this is a relative path off some directory * or an absolute path. If absolute path, open root dir. */ if (first_fd == -1) { if (name[0] != '/') { LogInfo(COMPONENT_FSAL, "Absolute path %s must start with '/'", path); return -EINVAL; } rest = name + 1; fd = open("/", O_RDONLY | O_NOFOLLOW); } else { rest = name; fd = dup(first_fd); } if (fd == -1) { err = errno; LogCrit(COMPONENT_FSAL, "Failed initial directory open for path %s with %s", path, strerror(err)); return -err; } while (rest[0] != '\0') { /* Find the end of this path element */ p = index(rest, '/'); /* NUL terminate element (if not at end of string */ if (p != NULL) *p = '\0'; /* Skip extra '/' */ if (rest[0] == '\0') { rest++; continue; } /* Disallow .. elements... */ if (strcmp(rest, "..") == 0) { close(fd); LogInfo(COMPONENT_FSAL, "Failed due to '..' element in path %s", path); return -EACCES; } /* Open the next directory in the path */ rc = openat(fd, rest, O_RDONLY | O_NOFOLLOW); err = errno; close(fd); if (rc == -1) { LogDebug(COMPONENT_FSAL, "openat(%s) in path %s failed with %s", rest, path, strerror(err)); return -err; } fd = rc; /* Done, break out */ if (p == NULL) break; /* Skip the '/' */ rest = p+1; } rc = fstat(fd, stat); err = errno; if (rc == -1) { close(fd); LogDebug(COMPONENT_FSAL, "fstat %s failed with %s", path, strerror(err)); return -err; } if (!S_ISDIR(stat->st_mode)) { close(fd); LogInfo(COMPONENT_FSAL, "Path %s is not a directory", path); return -ENOTDIR; } return fd; } pthread_rwlock_t fs_lock = PTHREAD_RWLOCK_INITIALIZER; struct glist_head posix_file_systems = { &posix_file_systems, &posix_file_systems }; struct avltree avl_fsid; struct avltree avl_dev; static inline int fsal_fs_cmpf_fsid(const struct avltree_node *lhs, const struct avltree_node *rhs) { struct fsal_filesystem *lk, *rk; lk = avltree_container_of(lhs, struct fsal_filesystem, avl_fsid); rk = avltree_container_of(rhs, struct fsal_filesystem, avl_fsid); if (lk->fsid_type < rk->fsid_type) return -1; if (lk->fsid_type > rk->fsid_type) return 1; if (lk->fsid.major < rk->fsid.major) return -1; if (lk->fsid.major > rk->fsid.major) return 1; /* No need to compare minors as they should be * zeros if the type is FSID_MAJOR_64 */ if (lk->fsid_type == FSID_MAJOR_64) { assert(rk->fsid_type == FSID_MAJOR_64); return 0; } if (lk->fsid.minor < rk->fsid.minor) return -1; if (lk->fsid.minor > rk->fsid.minor) return 1; return 0; } static inline struct fsal_filesystem * avltree_inline_fsid_lookup(const struct avltree_node *key) { struct avltree_node *node = avltree_inline_lookup(key, &avl_fsid, fsal_fs_cmpf_fsid); if (node != NULL) return avltree_container_of(node, struct fsal_filesystem, avl_fsid); else return NULL; } static inline int fsal_fs_cmpf_dev(const struct avltree_node *lhs, const struct avltree_node *rhs) { struct fsal_filesystem *lk, *rk; lk = avltree_container_of(lhs, struct fsal_filesystem, avl_dev); rk = avltree_container_of(rhs, struct fsal_filesystem, avl_dev); if (lk->dev.major < rk->dev.major) return -1; if (lk->dev.major > rk->dev.major) return 1; if (lk->dev.minor < rk->dev.minor) return -1; if (lk->dev.minor > rk->dev.minor) return 1; return 0; } static inline struct fsal_filesystem * avltree_inline_dev_lookup(const struct avltree_node *key) { struct avltree_node *node = avltree_inline_lookup(key, &avl_dev, fsal_fs_cmpf_dev); if (node != NULL) return avltree_container_of(node, struct fsal_filesystem, avl_dev); else return NULL; } void remove_fs(struct fsal_filesystem *fs) { if (fs->in_fsid_avl) avltree_remove(&fs->avl_fsid, &avl_fsid); if (fs->in_dev_avl) avltree_remove(&fs->avl_dev, &avl_dev); glist_del(&fs->siblings); glist_del(&fs->filesystems); } void free_fs(struct fsal_filesystem *fs) { gsh_free(fs->path); gsh_free(fs->device); gsh_free(fs->type); gsh_free(fs); } int re_index_fs_fsid(struct fsal_filesystem *fs, enum fsid_type fsid_type, struct fsal_fsid__ *fsid) { struct avltree_node *node; struct fsal_fsid__ old_fsid = fs->fsid; enum fsid_type old_fsid_type = fs->fsid_type; LogDebug(COMPONENT_FSAL, "Reindex %s from 0x%016"PRIx64".0x%016"PRIx64 " to 0x%016"PRIx64".0x%016"PRIx64, fs->path, fs->fsid.major, fs->fsid.minor, fsid->major, fsid->minor); /* It is not valid to use this routine to * remove fs from index. */ if (fsid_type == FSID_NO_TYPE) return -EINVAL; if (fs->in_fsid_avl) avltree_remove(&fs->avl_fsid, &avl_fsid); fs->fsid.major = fsid->major; fs->fsid.minor = fsid->minor; fs->fsid_type = fsid_type; node = avltree_insert(&fs->avl_fsid, &avl_fsid); if (node != NULL) { /* This is a duplicate file system. */ fs->fsid = old_fsid; fs->fsid_type = old_fsid_type; if (fs->in_fsid_avl) { /* Put it back where it was */ node = avltree_insert(&fs->avl_fsid, &avl_fsid); if (node != NULL) { LogFatal(COMPONENT_FSAL, "Could not re-insert filesystem %s", fs->path); } } return -EEXIST; } fs->in_fsid_avl = true; return 0; } int re_index_fs_dev(struct fsal_filesystem *fs, struct fsal_dev__ *dev) { struct avltree_node *node; struct fsal_dev__ old_dev = fs->dev; /* It is not valid to use this routine to * remove fs from index. */ if (dev == NULL) return -EINVAL; if (fs->in_dev_avl) avltree_remove(&fs->avl_dev, &avl_dev); fs->dev = *dev; node = avltree_insert(&fs->avl_dev, &avl_dev); if (node != NULL) { /* This is a duplicate file system. */ fs->dev = old_dev; if (fs->in_dev_avl) { /* Put it back where it was */ node = avltree_insert(&fs->avl_dev, &avl_dev); if (node != NULL) { LogFatal(COMPONENT_FSAL, "Could not re-insert filesystem %s", fs->path); } } return -EEXIST; } fs->in_dev_avl = true; return 0; } #define MASK_32 ((uint64_t) UINT32_MAX) int change_fsid_type(struct fsal_filesystem *fs, enum fsid_type fsid_type) { struct fsal_fsid__ fsid = {0}; bool valid = false; if (fs->fsid_type == fsid_type) return 0; switch (fsid_type) { case FSID_ONE_UINT64: if (fs->fsid_type == FSID_TWO_UINT64) { /* Use the same compression we use for NFS v3 fsid */ fsid.major = squash_fsid(&fs->fsid); valid = true; } else if (fs->fsid_type == FSID_TWO_UINT32) { /* Put major in the high order 32 bits and minor * in the low order 32 bits. */ fsid.major = fs->fsid.major << 32 | fs->fsid.minor; valid = true; } fsid.minor = 0; break; case FSID_MAJOR_64: /* Nothing to do, will ignore fsid.minor in index */ valid = true; fsid.major = fs->fsid.major; fsid.minor = fs->fsid.minor; break; case FSID_TWO_UINT64: if (fs->fsid_type == FSID_MAJOR_64) { /* Must re-index since minor was not indexed * previously. */ fsid.major = fs->fsid.major; fsid.minor = fs->fsid.minor; valid = true; } else { /* Nothing to do, FSID_TWO_UINT32 will just have high * order zero bits while FSID_ONE_UINT64 will have * minor = 0, without changing the actual value. */ fs->fsid_type = fsid_type; return 0; } break; case FSID_DEVICE: fsid.major = fs->dev.major; fsid.minor = fs->dev.minor; valid = true; case FSID_TWO_UINT32: if (fs->fsid_type == FSID_TWO_UINT64) { /* Shrink each 64 bit quantity to 32 bits by xoring the * two halves. */ fsid.major = (fs->fsid.major & MASK_32) ^ (fs->fsid.major >> 32); fsid.minor = (fs->fsid.minor & MASK_32) ^ (fs->fsid.minor >> 32); valid = true; } else if (fs->fsid_type == FSID_ONE_UINT64) { /* Split 64 bit that is in major into two 32 bit using * the high order 32 bits as major. */ fsid.major = fs->fsid.major >> 32; fsid.minor = fs->fsid.major & MASK_32; valid = true; } break; case FSID_NO_TYPE: /* It is not valid to use this routine to remove an fs */ break; } if (!valid) return -EINVAL; return re_index_fs_fsid(fs, fsid_type, &fsid); } static bool posix_get_fsid(struct fsal_filesystem *fs) { struct statfs stat_fs; struct stat mnt_stat; #ifdef USE_BLKID char *dev_name; char *uuid_str; #endif LogFullDebug(COMPONENT_FSAL, "statfs of %s pathlen %d", fs->path, fs->pathlen); if (statfs(fs->path, &stat_fs) != 0) LogCrit(COMPONENT_FSAL, "stat_fs of %s resulted in error %s(%d)", fs->path, strerror(errno), errno); #if __FreeBSD__ fs->namelen = stat_fs.f_namemax; #else fs->namelen = stat_fs.f_namelen; #endif if (stat(fs->path, &mnt_stat) != 0) { LogCrit(COMPONENT_FSAL, "stat of %s resulted in error %s(%d)", fs->path, strerror(errno), errno); return false; } fs->dev = posix2fsal_devt(mnt_stat.st_dev); if (nfs_param.core_param.fsid_device) { fs->fsid_type = FSID_DEVICE; fs->fsid.major = fs->dev.major; fs->fsid.minor = fs->dev.minor; return true; } #ifdef USE_BLKID if (cache == NULL) goto out; dev_name = blkid_devno_to_devname(mnt_stat.st_dev); if (dev_name == NULL) { LogInfo(COMPONENT_FSAL, "blkid_devno_to_devname of %s failed for dev %d.%d", fs->path, major(mnt_stat.st_dev), minor(mnt_stat.st_dev)); goto out; } if (blkid_get_dev(cache, dev_name, BLKID_DEV_NORMAL) == NULL) { LogInfo(COMPONENT_FSAL, "blkid_get_dev of %s failed for devname %s", fs->path, dev_name); free(dev_name); goto out; } uuid_str = blkid_get_tag_value(cache, "UUID", dev_name); free(dev_name); if (uuid_str == NULL) { LogInfo(COMPONENT_FSAL, "blkid_get_tag_value of %s failed", fs->path); goto out; } if (uuid_parse(uuid_str, (char *) &fs->fsid) == -1) { LogInfo(COMPONENT_FSAL, "uuid_parse of %s failed for uuid %s", fs->path, uuid_str); free(uuid_str); goto out; } free(uuid_str); fs->fsid_type = FSID_TWO_UINT64; return true; out: #endif fs->fsid_type = FSID_TWO_UINT32; #if __FreeBSD__ fs->fsid.major = (unsigned int) stat_fs.f_fsid.val[0]; fs->fsid.minor = (unsigned int) stat_fs.f_fsid.val[1]; #else fs->fsid.major = (unsigned int) stat_fs.f_fsid.__val[0]; fs->fsid.minor = (unsigned int) stat_fs.f_fsid.__val[1]; #endif if ((fs->fsid.major == 0) && (fs->fsid.minor == 0)) { fs->fsid.major = fs->dev.major; fs->fsid.minor = fs->dev.minor; } return true; } static void posix_create_file_system(struct mntent *mnt) { struct fsal_filesystem *fs; struct avltree_node *node; if (strncasecmp(mnt->mnt_type, "nfs", 3) == 0) { LogDebug(COMPONENT_FSAL, "Ignoring %s because type %s", mnt->mnt_dir, mnt->mnt_type); return; } fs = gsh_calloc(1, sizeof(*fs)); fs->path = gsh_strdup(mnt->mnt_dir); fs->device = gsh_strdup(mnt->mnt_fsname); fs->type = gsh_strdup(mnt->mnt_type); if (!posix_get_fsid(fs)) { free_fs(fs); return; } fs->pathlen = strlen(mnt->mnt_dir); node = avltree_insert(&fs->avl_fsid, &avl_fsid); if (node != NULL) { /* This is a duplicate file system. */ struct fsal_filesystem *fs1; fs1 = avltree_container_of(node, struct fsal_filesystem, avl_fsid); LogDebug(COMPONENT_FSAL, "Skipped duplicate %s namelen=%d fsid=0x%016"PRIx64 ".0x%016"PRIx64" %"PRIu64".%"PRIu64, fs->path, (int) fs->namelen, fs->fsid.major, fs->fsid.minor, fs->fsid.major, fs->fsid.minor); if (fs1->device[0] != '/' && fs->device[0] == '/') { LogDebug(COMPONENT_FSAL, "Switching device for %s from %s to %s type from %s to %s", fs->path, fs1->device, fs->device, fs1->type, fs->type); gsh_free(fs1->device); fs1->device = fs->device; fs->device = NULL; gsh_free(fs1->type); fs1->type = fs->type; fs->type = NULL; } free_fs(fs); return; } fs->in_fsid_avl = true; node = avltree_insert(&fs->avl_dev, &avl_dev); if (node != NULL) { /* This is a duplicate file system. */ struct fsal_filesystem *fs1; fs1 = avltree_container_of(node, struct fsal_filesystem, avl_dev); LogDebug(COMPONENT_FSAL, "Skipped duplicate %s namelen=%d dev=%" PRIu64".%"PRIu64, fs->path, (int) fs->namelen, fs->dev.major, fs->dev.minor); if (fs1->device[0] != '/' && fs->device[0] == '/') { LogDebug(COMPONENT_FSAL, "Switching device for %s from %s to %s type from %s to %s", fs->path, fs1->device, fs->device, fs1->type, fs->type); gsh_free(fs1->device); fs1->device = fs->device; fs->device = NULL; gsh_free(fs1->type); fs1->type = fs->type; fs->type = NULL; } remove_fs(fs); free_fs(fs); return; } fs->in_dev_avl = true; glist_add_tail(&posix_file_systems, &fs->filesystems); glist_init(&fs->children); LogInfo(COMPONENT_FSAL, "Added filesystem %s namelen=%d dev=%"PRIu64".%"PRIu64 " fsid=0x%016"PRIx64".0x%016"PRIx64" %"PRIu64".%"PRIu64, fs->path, (int) fs->namelen, fs->dev.major, fs->dev.minor, fs->fsid.major, fs->fsid.minor, fs->fsid.major, fs->fsid.minor); } static void posix_find_parent(struct fsal_filesystem *this) { struct glist_head *glist; struct fsal_filesystem *fs; int plen = 0; /* Check if it already has parent */ if (this->parent != NULL) return; /* Check for root fs, it has no parent */ if (this->pathlen == 1 && this->path[0] == '/') return; glist_for_each(glist, &posix_file_systems) { fs = glist_entry(glist, struct fsal_filesystem, filesystems); /* If this path is longer than this path, then it * can't be a parent, or if it's shorter than the * current match; */ if (fs->pathlen >= this->pathlen || fs->pathlen < plen) continue; /* Check for sub-string match */ if (strncmp(fs->path, this->path, fs->pathlen) != 0) continue; /* Differentiate between /fs1 and /fs10 for parent of * /fs10/fs2, however, if fs->path is "/", we need to * special case. */ if (fs->pathlen != 1 && this->path[fs->pathlen] != '/') continue; this->parent = fs; plen = fs->pathlen; } if (this->parent == NULL) { LogInfo(COMPONENT_FSAL, "Unattached file system %s", this->path); return; } /* Add to parent's list of children */ glist_add_tail(&this->parent->children, &this->siblings); LogInfo(COMPONENT_FSAL, "File system %s is a child of %s", this->path, this->parent->path); } void show_tree(struct fsal_filesystem *this, int nest) { struct glist_head *glist; char blanks[1024]; memset(blanks, ' ', nest * 2); blanks[nest * 2] = '\0'; LogInfo(COMPONENT_FSAL, "%s%s", blanks, this->path); /* Claim the children now */ glist_for_each(glist, &this->children) { show_tree(glist_entry(glist, struct fsal_filesystem, siblings), nest + 1); } } int populate_posix_file_systems(bool force) { FILE *fp; struct mntent *mnt; int retval = 0; struct glist_head *glist; struct fsal_filesystem *fs; PTHREAD_RWLOCK_wrlock(&fs_lock); if (glist_empty(&posix_file_systems)) { LogDebug(COMPONENT_FSAL, "Initializing posix file systems"); avltree_init(&avl_fsid, fsal_fs_cmpf_fsid, 0); avltree_init(&avl_dev, fsal_fs_cmpf_dev, 0); } else if (!force) { LogDebug(COMPONENT_FSAL, "File systems are initialized"); goto out; } /* start looking for the mount point */ fp = setmntent(MOUNTED, "r"); if (fp == NULL) { retval = errno; LogCrit(COMPONENT_FSAL, "Error %d in setmntent(%s): %s", retval, MOUNTED, strerror(retval)); goto out; } #ifdef USE_BLKID if (blkid_get_cache(&cache, NULL) != 0) LogInfo(COMPONENT_FSAL, "blkid_get_cache failed"); #endif while ((mnt = getmntent(fp)) != NULL) { if (mnt->mnt_dir == NULL) continue; posix_create_file_system(mnt); } #ifdef USE_BLKID if (cache) { blkid_put_cache(cache); cache = NULL; } #endif endmntent(fp); /* build tree of POSIX file systems */ glist_for_each(glist, &posix_file_systems) posix_find_parent(glist_entry(glist, struct fsal_filesystem, filesystems)); /* show tree */ glist_for_each(glist, &posix_file_systems) { fs = glist_entry(glist, struct fsal_filesystem, filesystems); if (fs->parent == NULL) show_tree(fs, 0); } out: PTHREAD_RWLOCK_unlock(&fs_lock); return retval; } int resolve_posix_filesystem(const char *path, struct fsal_module *fsal, struct fsal_export *exp, claim_filesystem_cb claim, unclaim_filesystem_cb unclaim, struct fsal_filesystem **root_fs) { int retval = 0; retval = populate_posix_file_systems(false); if (retval != 0) { LogCrit(COMPONENT_FSAL, "populate_posix_file_systems returned %s (%d)", strerror(retval), retval); return retval; } retval = claim_posix_filesystems(path, fsal, exp, claim, unclaim, root_fs); /* second attempt to resolve file system with force option in case of * ganesha isn't during startup. */ if (!nfs_init.init_complete || retval != EAGAIN) return retval; LogDebug(COMPONENT_FSAL, "Call populate_posix_file_systems one more time"); retval = populate_posix_file_systems(true); if (retval != 0) { LogCrit(COMPONENT_FSAL, "populate_posix_file_systems returned %s (%d)", strerror(retval), retval); return retval; } retval = claim_posix_filesystems(path, fsal, exp, claim, unclaim, root_fs); if (retval != 0) { if (retval == EAGAIN) retval = ENOENT; LogCrit(COMPONENT_FSAL, "claim_posix_filesystems(%s) returned %s (%d)", path, strerror(retval), retval); } return retval; } static void release_posix_file_system(struct fsal_filesystem *fs) { struct fsal_filesystem *child_fs; if (fs->unclaim != NULL) { LogWarn(COMPONENT_FSAL, "Filesystem %s is still claimed", fs->path); unclaim_fs(fs); } while ((child_fs = glist_first_entry(&fs->children, struct fsal_filesystem, siblings))) { release_posix_file_system(child_fs); } LogDebug(COMPONENT_FSAL, "Releasing filesystem %s (%p)", fs->path, fs); remove_fs(fs); free_fs(fs); } void release_posix_file_systems(void) { struct fsal_filesystem *fs; PTHREAD_RWLOCK_wrlock(&fs_lock); while ((fs = glist_first_entry(&posix_file_systems, struct fsal_filesystem, filesystems))) { release_posix_file_system(fs); } PTHREAD_RWLOCK_unlock(&fs_lock); } struct fsal_filesystem *lookup_fsid_locked(struct fsal_fsid__ *fsid, enum fsid_type fsid_type) { struct fsal_filesystem key; key.fsid = *fsid; key.fsid_type = fsid_type; return avltree_inline_fsid_lookup(&key.avl_fsid); } struct fsal_filesystem *lookup_dev_locked(struct fsal_dev__ *dev) { struct fsal_filesystem key; key.dev = *dev; return avltree_inline_dev_lookup(&key.avl_dev); } struct fsal_filesystem *lookup_fsid(struct fsal_fsid__ *fsid, enum fsid_type fsid_type) { struct fsal_filesystem *fs; PTHREAD_RWLOCK_rdlock(&fs_lock); fs = lookup_fsid_locked(fsid, fsid_type); PTHREAD_RWLOCK_unlock(&fs_lock); return fs; } struct fsal_filesystem *lookup_dev(struct fsal_dev__ *dev) { struct fsal_filesystem *fs; PTHREAD_RWLOCK_rdlock(&fs_lock); fs = lookup_dev_locked(dev); PTHREAD_RWLOCK_unlock(&fs_lock); return fs; } void unclaim_fs(struct fsal_filesystem *this) { /* One call to unclaim resolves all claims to the filesystem */ if (this->unclaim != NULL) { LogDebug(COMPONENT_FSAL, "Have FSAL %s unclaim filesystem %s", this->fsal->name, this->path); this->unclaim(this); } this->fsal = NULL; this->unclaim = NULL; this->exported = false; } int process_claim(const char *path, int pathlen, struct fsal_filesystem *this, struct fsal_module *fsal, struct fsal_export *exp, claim_filesystem_cb claim, unclaim_filesystem_cb unclaim) { struct glist_head *glist; struct fsal_filesystem *fs; int retval = 0; /* Check if the filesystem is already directly exported by some other * FSAL - note we can only get here is this is the root filesystem for * the export, once we start processing nested filesystems, we skip * any that are directly exported. */ if (this->fsal != NULL && this->fsal != fsal && this->exported) { LogCrit(COMPONENT_FSAL, "Filesystem %s already exported by FSAL %s for export path %s", this->path, this->fsal->name, path); return EINVAL; } /* Check if another FSAL had claimed this file system as a sub-mounted * file system. */ if (this->fsal != fsal) unclaim_fs(this); /* Now claim the file system (we may call claim multiple times */ retval = claim(this, exp); if (retval == ENXIO) { if (path != NULL) { LogCrit(COMPONENT_FSAL, "FSAL %s could not to claim root file system %s for export %s", fsal->name, this->path, path); return EINVAL; } else { LogInfo(COMPONENT_FSAL, "FSAL %s could not to claim file system %s", fsal->name, this->path); return 0; } } if (retval != 0) { LogCrit(COMPONENT_FSAL, "FSAL %s failed to claim file system %s error %s", fsal->name, this->path, strerror(retval)); return retval; } LogDebug(COMPONENT_FSAL, "FSAL %s Claiming %s", fsal->name, this->path); /* Complete the claim */ this->fsal = fsal; this->unclaim = unclaim; /* If this was the root of the export, indicate this filesystem is * directly exported. */ if (path != NULL) this->exported = true; /* If this has no children, done */ if (glist_empty(&this->children)) return 0; /* Claim the children now */ glist_for_each(glist, &this->children) { fs = glist_entry(glist, struct fsal_filesystem, siblings); /* If path is provided, only consider children that are * children of the provided directory. This handles the * case of an export of something other than the root * of a file system. */ if (path != NULL && (fs->pathlen < pathlen || (strncmp(fs->path, path, pathlen) != 0))) continue; /* Test if this fs is directly exported, if so, no more * sub-mounted exports. */ if (fs->exported) continue; /* Try to claim this child */ retval = process_claim(NULL, 0, fs, fsal, exp, claim, unclaim); if (retval != 0) break; } return retval; } int claim_posix_filesystems(const char *path, struct fsal_module *fsal, struct fsal_export *exp, claim_filesystem_cb claim, unclaim_filesystem_cb unclaim, struct fsal_filesystem **root_fs) { int retval = 0; struct fsal_filesystem *fs, *root = NULL; struct glist_head *glist; struct stat statbuf; struct fsal_dev__ dev; PTHREAD_RWLOCK_wrlock(&fs_lock); if (stat(path, &statbuf) != 0) { retval = errno; LogCrit(COMPONENT_FSAL, "Could not stat directory for path %s", path); goto out; } dev = posix2fsal_devt(statbuf.st_dev); /* Scan POSIX file systems to find export root fs */ glist_for_each(glist, &posix_file_systems) { fs = glist_entry(glist, struct fsal_filesystem, filesystems); if (fs->dev.major == dev.major && fs->dev.minor == dev.minor) { root = fs; break; } } /* Check if we found a filesystem */ if (root == NULL) { retval = EAGAIN; goto out; } /* Claim this file system and it's children */ retval = process_claim(path, strlen(path), root, fsal, exp, claim, unclaim); if (retval == 0) { LogInfo(COMPONENT_FSAL, "Root fs for export %s is %s", path, root->path); *root_fs = root; } out: PTHREAD_RWLOCK_unlock(&fs_lock); return retval; } int encode_fsid(char *buf, int max, struct fsal_fsid__ *fsid, enum fsid_type fsid_type) { uint32_t u32; if (sizeof_fsid(fsid_type) > max) return -1; /* Pack fsid into the bytes */ switch (fsid_type) { case FSID_NO_TYPE: break; case FSID_ONE_UINT64: case FSID_MAJOR_64: memcpy(buf, &fsid->major, sizeof(fsid->major)); break; case FSID_TWO_UINT64: memcpy(buf, fsid, sizeof(*fsid)); break; case FSID_TWO_UINT32: case FSID_DEVICE: u32 = fsid->major; memcpy(buf, &u32, sizeof(u32)); u32 = fsid->minor; memcpy(buf + sizeof(u32), &u32, sizeof(u32)); } return sizeof_fsid(fsid_type); } int decode_fsid(char *buf, int max, struct fsal_fsid__ *fsid, enum fsid_type fsid_type) { uint32_t u32; if (sizeof_fsid(fsid_type) > max) return -1; switch (fsid_type) { case FSID_NO_TYPE: memset(fsid, 0, sizeof(*fsid)); break; case FSID_ONE_UINT64: case FSID_MAJOR_64: memcpy(&fsid->major, buf, sizeof(fsid->major)); fsid->minor = 0; break; case FSID_TWO_UINT64: memcpy(fsid, buf, sizeof(*fsid)); break; case FSID_TWO_UINT32: case FSID_DEVICE: memcpy(&u32, buf, sizeof(u32)); fsid->major = u32; memcpy(&u32, buf + sizeof(u32), sizeof(u32)); fsid->minor = u32; break; } return sizeof_fsid(fsid_type); } static inline bool is_dup_ace(fsal_ace_t *ace, fsal_aceflag_t inherit) { if (!IS_FSAL_ACE_INHERIT(*ace)) return false; if (inherit != FSAL_ACE_FLAG_DIR_INHERIT) /* Only dup on directories */ return false; if (IS_FSAL_ACE_NO_PROPAGATE(*ace)) return false; if (IS_FSAL_ACE_FILE_INHERIT(*ace) && !IS_FSAL_ACE_DIR_INHERIT(*ace)) return false; if (!IS_FSAL_ACE_PERM(*ace)) return false; return true; } static fsal_errors_t dup_ace(fsal_ace_t *sace, fsal_ace_t *dace) { *dace = *sace; GET_FSAL_ACE_FLAG(*sace) |= FSAL_ACE_FLAG_INHERIT_ONLY; GET_FSAL_ACE_FLAG(*dace) &= ~(FSAL_ACE_FLAG_INHERIT | FSAL_ACE_FLAG_NO_PROPAGATE); return ERR_FSAL_NO_ERROR; } fsal_errors_t fsal_inherit_acls(struct attrlist *attrs, fsal_acl_t *sacl, fsal_aceflag_t inherit) { int naces; fsal_ace_t *sace, *dace; if (!sacl || !sacl->aces || sacl->naces == 0) return ERR_FSAL_NO_ERROR; if (attrs->acl && attrs->acl->aces && attrs->acl->naces > 0) return ERR_FSAL_EXIST; naces = 0; for (sace = sacl->aces; sace < sacl->aces + sacl->naces; sace++) { if (IS_FSAL_ACE_FLAG(*sace, inherit)) naces++; if (is_dup_ace(sace, inherit)) naces++; } if (naces == 0) return ERR_FSAL_NO_ERROR; if (attrs->acl != NULL) { /* We should never be passed attributes that have an * ACL attached, but just in case some future code * path changes that assumption, let's not release the * old ACL properly. */ int acl_status; acl_status = nfs4_acl_release_entry(attrs->acl); if (acl_status != NFS_V4_ACL_SUCCESS) LogCrit(COMPONENT_FSAL, "Failed to release old acl, status=%d", acl_status); } attrs->acl = nfs4_acl_alloc(); attrs->acl->aces = (fsal_ace_t *) nfs4_ace_alloc(naces); dace = attrs->acl->aces; for (sace = sacl->aces; sace < sacl->aces + sacl->naces; sace++) { if (IS_FSAL_ACE_FLAG(*sace, inherit)) { *dace = *sace; if (IS_FSAL_ACE_NO_PROPAGATE(*dace)) GET_FSAL_ACE_FLAG(*dace) &= ~(FSAL_ACE_FLAG_INHERIT | FSAL_ACE_FLAG_NO_PROPAGATE); else if (inherit == FSAL_ACE_FLAG_DIR_INHERIT && IS_FSAL_ACE_FILE_INHERIT(*dace) && !IS_FSAL_ACE_DIR_INHERIT(*dace)) GET_FSAL_ACE_FLAG(*dace) |= FSAL_ACE_FLAG_NO_PROPAGATE; else if (is_dup_ace(dace, inherit)) { dup_ace(dace, dace + 1); dace++; } dace++; } } attrs->acl->naces = naces; FSAL_SET_MASK(attrs->valid_mask, ATTR_ACL); return ERR_FSAL_NO_ERROR; } fsal_status_t fsal_remove_access(struct fsal_obj_handle *dir_hdl, struct fsal_obj_handle *rem_hdl, bool isdir) { fsal_status_t fsal_status = { 0, 0 }; fsal_status_t del_status = { 0, 0 }; /* draft-ietf-nfsv4-acls section 12 */ /* If no execute on dir, deny */ fsal_status = dir_hdl->obj_ops.test_access( dir_hdl, FSAL_MODE_MASK_SET(FSAL_X_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_EXECUTE), NULL, NULL, false); if (FSAL_IS_ERROR(fsal_status)) { LogFullDebug(COMPONENT_FSAL, "Could not delete: No execute permession on parent: %s", msg_fsal_err(fsal_status.major)); return fsal_status; } /* We can delete if we have *either* ACE_PERM_DELETE or * ACE_PERM_DELETE_CHILD. 7530 - 6.2.1.3.2 */ del_status = rem_hdl->obj_ops.test_access( rem_hdl, FSAL_MODE_MASK_SET(FSAL_W_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_DELETE) | FSAL_ACE4_REQ_FLAG, NULL, NULL, false); fsal_status = dir_hdl->obj_ops.test_access( dir_hdl, FSAL_MODE_MASK_SET(FSAL_W_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_DELETE_CHILD) | FSAL_ACE4_REQ_FLAG, NULL, NULL, false); if (FSAL_IS_ERROR(fsal_status) && FSAL_IS_ERROR(del_status)) { /* Neither was explicitly allowed */ if (fsal_status.major != ERR_FSAL_NO_ACE) { /* Explicitly denied */ LogFullDebug(COMPONENT_FSAL, "Could not delete (DELETE_CHILD) %s", msg_fsal_err(fsal_status.major)); return fsal_status; } if (del_status.major != ERR_FSAL_NO_ACE) { /* Explicitly denied */ LogFullDebug(COMPONENT_FSAL, "Could not delete (DELETE) %s", msg_fsal_err(del_status.major)); return del_status; } /* Neither ACE_PERM_DELETE nor ACE_PERM_DELETE_CHILD are set. * Check for ADD_FILE in parent */ fsal_status = dir_hdl->obj_ops.test_access( dir_hdl, FSAL_MODE_MASK_SET(FSAL_W_OK) | FSAL_ACE4_MASK_SET(isdir ? FSAL_ACE_PERM_ADD_SUBDIRECTORY : FSAL_ACE_PERM_ADD_FILE), NULL, NULL, false); if (FSAL_IS_ERROR(fsal_status)) { LogFullDebug(COMPONENT_FSAL, "Could not delete (ADD_CHILD) %s", msg_fsal_err(fsal_status.major)); return fsal_status; } /* Allowed; fall through */ } return fsalstat(ERR_FSAL_NO_ERROR, 0); } fsal_status_t fsal_rename_access(struct fsal_obj_handle *src_dir_hdl, struct fsal_obj_handle *src_obj_hdl, struct fsal_obj_handle *dst_dir_hdl, struct fsal_obj_handle *dst_obj_hdl, bool isdir) { fsal_status_t status = {0, 0}; fsal_accessflags_t access_type; status = fsal_remove_access(src_dir_hdl, src_obj_hdl, isdir); if (FSAL_IS_ERROR(status)) return status; if (dst_obj_hdl) { status = fsal_remove_access(dst_dir_hdl, dst_obj_hdl, isdir); if (FSAL_IS_ERROR(status)) return status; } access_type = FSAL_MODE_MASK_SET(FSAL_W_OK); if (isdir) access_type |= FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_ADD_SUBDIRECTORY); else access_type |= FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_ADD_FILE); status = dst_dir_hdl->obj_ops.test_access(dst_dir_hdl, access_type, NULL, NULL, false); if (FSAL_IS_ERROR(status)) return status; return fsalstat(ERR_FSAL_NO_ERROR, 0); } static fsal_status_t fsal_mode_set_ace(fsal_ace_t *deny, fsal_ace_t *allow, uint32_t mode) { GET_FSAL_ACE_TYPE(*allow) = FSAL_ACE_TYPE_ALLOW; GET_FSAL_ACE_TYPE(*deny) = FSAL_ACE_TYPE_DENY; if (mode & S_IRUSR) GET_FSAL_ACE_PERM(*allow) |= FSAL_ACE_PERM_READ_DATA; else GET_FSAL_ACE_PERM(*deny) |= FSAL_ACE_PERM_READ_DATA; if (mode & S_IWUSR) GET_FSAL_ACE_PERM(*allow) |= FSAL_ACE_PERM_WRITE_DATA | FSAL_ACE_PERM_APPEND_DATA; else GET_FSAL_ACE_PERM(*deny) |= FSAL_ACE_PERM_WRITE_DATA | FSAL_ACE_PERM_APPEND_DATA; if (mode & S_IXUSR) GET_FSAL_ACE_PERM(*allow) |= FSAL_ACE_PERM_EXECUTE; else GET_FSAL_ACE_PERM(*deny) |= FSAL_ACE_PERM_EXECUTE; return fsalstat(ERR_FSAL_NO_ERROR, 0); } static fsal_status_t fsal_mode_gen_set(fsal_ace_t *ace, uint32_t mode) { fsal_ace_t *allow, *deny; /* @OWNER */ deny = ace; allow = deny + 1; GET_FSAL_ACE_USER(*allow) = FSAL_ACE_SPECIAL_OWNER; GET_FSAL_ACE_IFLAG(*allow) |= (FSAL_ACE_IFLAG_MODE_GEN | FSAL_ACE_IFLAG_SPECIAL_ID); GET_FSAL_ACE_USER(*deny) = FSAL_ACE_SPECIAL_OWNER; GET_FSAL_ACE_IFLAG(*deny) |= (FSAL_ACE_IFLAG_MODE_GEN | FSAL_ACE_IFLAG_SPECIAL_ID); fsal_mode_set_ace(deny, allow, mode & S_IRWXU); /* @GROUP */ deny += 2; allow = deny + 1; GET_FSAL_ACE_USER(*allow) = FSAL_ACE_SPECIAL_GROUP; GET_FSAL_ACE_IFLAG(*allow) |= (FSAL_ACE_IFLAG_MODE_GEN | FSAL_ACE_IFLAG_SPECIAL_ID); GET_FSAL_ACE_FLAG(*allow) = FSAL_ACE_FLAG_GROUP_ID; GET_FSAL_ACE_USER(*deny) = FSAL_ACE_SPECIAL_GROUP; GET_FSAL_ACE_IFLAG(*deny) |= (FSAL_ACE_IFLAG_MODE_GEN | FSAL_ACE_IFLAG_SPECIAL_ID); GET_FSAL_ACE_FLAG(*deny) = FSAL_ACE_FLAG_GROUP_ID; fsal_mode_set_ace(deny, allow, (mode & S_IRWXG) << 3); /* @EVERYONE */ deny += 2; allow = deny + 1; GET_FSAL_ACE_USER(*allow) = FSAL_ACE_SPECIAL_EVERYONE; GET_FSAL_ACE_IFLAG(*allow) |= (FSAL_ACE_IFLAG_MODE_GEN | FSAL_ACE_IFLAG_SPECIAL_ID); GET_FSAL_ACE_USER(*deny) = FSAL_ACE_SPECIAL_EVERYONE; GET_FSAL_ACE_IFLAG(*deny) |= (FSAL_ACE_IFLAG_MODE_GEN | FSAL_ACE_IFLAG_SPECIAL_ID); fsal_mode_set_ace(deny, allow, (mode & S_IRWXO) << 6); return fsalstat(ERR_FSAL_NO_ERROR, 0); } static fsal_status_t fsal_mode_gen_acl(struct attrlist *attrs) { if (attrs->acl != NULL) { /* We should never be passed attributes that have an * ACL attached, but just in case some future code * path changes that assumption, let's not release the * old ACL properly. */ int acl_status; acl_status = nfs4_acl_release_entry(attrs->acl); if (acl_status != NFS_V4_ACL_SUCCESS) LogCrit(COMPONENT_FSAL, "Failed to release old acl, status=%d", acl_status); } attrs->acl = nfs4_acl_alloc(); attrs->acl->naces = 6; attrs->acl->aces = (fsal_ace_t *) nfs4_ace_alloc(attrs->acl->naces); fsal_mode_gen_set(attrs->acl->aces, attrs->mode); FSAL_SET_MASK(attrs->valid_mask, ATTR_ACL); return fsalstat(ERR_FSAL_NO_ERROR, 0); } fsal_status_t fsal_mode_to_acl(struct attrlist *attrs, fsal_acl_t *sacl) { int naces; fsal_ace_t *sace, *dace; if (!FSAL_TEST_MASK(attrs->valid_mask, ATTR_MODE)) return fsalstat(ERR_FSAL_NO_ERROR, 0); if (!sacl || sacl->naces == 0) return fsal_mode_gen_acl(attrs); naces = 0; for (sace = sacl->aces; sace < sacl->aces + sacl->naces; sace++) { if (IS_FSAL_ACE_MODE_GEN(*sace)) { /* Don't copy mode geneated ACEs; will be re-created */ continue; } naces++; if (IS_FSAL_ACE_INHERIT_ONLY(*sace)) continue; if (!IS_FSAL_ACE_PERM(*sace)) continue; if (IS_FSAL_ACE_INHERIT(*sace)) { /* Dup this ACE */ naces++; } /* XXX dang dup for non-special case */ } if (naces == 0) { /* Only mode generate aces */ return fsal_mode_gen_acl(attrs); } /* Space for generated ACEs at the end */ naces += 6; if (attrs->acl != NULL) { /* We should never be passed attributes that have an * ACL attached, but just in case some future code * path changes that assumption, let's not release the * old ACL properly. */ int acl_status; acl_status = nfs4_acl_release_entry(attrs->acl); if (acl_status != NFS_V4_ACL_SUCCESS) LogCrit(COMPONENT_FSAL, "Failed to release old acl, status=%d", acl_status); } attrs->acl = nfs4_acl_alloc(); attrs->acl->aces = (fsal_ace_t *) nfs4_ace_alloc(naces); attrs->acl->naces = 0; dace = attrs->acl->aces; for (sace = sacl->aces; sace < sacl->aces + sacl->naces; sace++, dace++) { if (IS_FSAL_ACE_MODE_GEN(*sace)) continue; *dace = *sace; attrs->acl->naces++; if (IS_FSAL_ACE_INHERIT_ONLY(*dace) || (!IS_FSAL_ACE_PERM(*dace))) continue; if (IS_FSAL_ACE_INHERIT(*dace)) { /* Need to duplicate */ GET_FSAL_ACE_FLAG(*dace) |= FSAL_ACE_FLAG_INHERIT_ONLY; dace++; *dace = *sace; attrs->acl->naces++; GET_FSAL_ACE_FLAG(*dace) &= ~(FSAL_ACE_FLAG_INHERIT); } if (IS_FSAL_ACE_SPECIAL(*dace)) { GET_FSAL_ACE_PERM(*dace) &= ~(FSAL_ACE_PERM_READ_DATA | FSAL_ACE_PERM_LIST_DIR | FSAL_ACE_PERM_WRITE_DATA | FSAL_ACE_PERM_ADD_FILE | FSAL_ACE_PERM_APPEND_DATA | FSAL_ACE_PERM_ADD_SUBDIRECTORY | FSAL_ACE_PERM_EXECUTE); } else { /* Do non-special stuff */ } } if (naces - attrs->acl->naces != 6) { LogDebug(COMPONENT_FSAL, "Bad naces: %d not %d", attrs->acl->naces, naces - 6); return fsalstat(ERR_FSAL_SERVERFAULT, 0); } fsal_mode_gen_set(dace, attrs->mode); attrs->acl->naces = naces; FSAL_SET_MASK(attrs->valid_mask, ATTR_ACL); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* fsal_acl_to_mode helpers */ static uint32_t ace_modes[3][3] = { { /* owner */ S_IRUSR, S_IWUSR, S_IXUSR }, { /* group */ S_IRGRP, S_IWGRP, S_IXGRP }, { /* everyone */ S_IRUSR | S_IRGRP | S_IROTH, S_IWUSR | S_IWGRP | S_IWOTH, S_IXUSR | S_IXGRP | S_IXOTH, } }; static inline void set_mode(struct attrlist *attrs, uint32_t mode, bool allow) { if (allow) attrs->mode |= mode; else attrs->mode &= ~(mode); } fsal_status_t fsal_acl_to_mode(struct attrlist *attrs) { fsal_ace_t *ace = NULL; uint32_t *modes; if (!FSAL_TEST_MASK(attrs->valid_mask, ATTR_ACL)) return fsalstat(ERR_FSAL_NO_ERROR, 0); if (!attrs->acl || attrs->acl->naces == 0) return fsalstat(ERR_FSAL_NO_ERROR, 0); for (ace = attrs->acl->aces; ace < attrs->acl->aces + attrs->acl->naces; ace++) { if (IS_FSAL_ACE_SPECIAL_OWNER(*ace)) modes = ace_modes[0]; else if (IS_FSAL_ACE_SPECIAL_GROUP(*ace)) modes = ace_modes[1]; else if (IS_FSAL_ACE_SPECIAL_EVERYONE(*ace)) modes = ace_modes[2]; else continue; if (IS_FSAL_ACE_READ_DATA(*ace)) set_mode(attrs, modes[0], IS_FSAL_ACE_ALLOW(*ace)); if (IS_FSAL_ACE_WRITE_DATA(*ace) || IS_FSAL_ACE_APPEND_DATA(*ace)) set_mode(attrs, modes[1], IS_FSAL_ACE_ALLOW(*ace)); if (IS_FSAL_ACE_EXECUTE(*ace)) set_mode(attrs, modes[2], IS_FSAL_ACE_ALLOW(*ace)); } FSAL_SET_MASK(attrs->valid_mask, ATTR_MODE); return fsalstat(ERR_FSAL_NO_ERROR, 0); } void set_common_verifier(struct attrlist *attrs, fsal_verifier_t verifier) { uint32_t verf_hi = 0, verf_lo = 0; memcpy(&verf_hi, verifier, sizeof(uint32_t)); memcpy(&verf_lo, verifier + sizeof(uint32_t), sizeof(uint32_t)); LogFullDebug(COMPONENT_FSAL, "Passed verifier %"PRIx32" %"PRIx32, verf_hi, verf_lo); if (isDebug(COMPONENT_FSAL) && (FSAL_TEST_MASK(attrs->valid_mask, ATTR_ATIME) || (FSAL_TEST_MASK(attrs->valid_mask, ATTR_MTIME)))) { LogWarn(COMPONENT_FSAL, "atime or mtime was already set in attributes%" PRIx32" %"PRIx32, (uint32_t) attrs->atime.tv_sec, (uint32_t) attrs->mtime.tv_sec); } attrs->atime.tv_sec = verf_hi; attrs->mtime.tv_sec = verf_lo; FSAL_SET_MASK(attrs->valid_mask, ATTR_ATIME | ATTR_MTIME); } /** * @brief Update the ref counter of share state * * The caller is responsible for protecting the share. * * @param[in] share Share to update * @param[in] old_openflags Previous access/deny mode * @param[in] new_openflags Current access/deny mode */ void update_share_counters(struct fsal_share *share, fsal_openflags_t old_openflags, fsal_openflags_t new_openflags) { int access_read_inc = ((int)(new_openflags & FSAL_O_READ) != 0) - ((int)(old_openflags & FSAL_O_READ) != 0); int access_write_inc = ((int)(new_openflags & FSAL_O_WRITE) != 0) - ((int)(old_openflags & FSAL_O_WRITE) != 0); int deny_read_inc = ((int)(new_openflags & FSAL_O_DENY_READ) != 0) - ((int)(old_openflags & FSAL_O_DENY_READ) != 0); /* Combine both FSAL_O_DENY_WRITE and FSAL_O_DENY_WRITE_MAND */ int deny_write_inc = ((int)(new_openflags & FSAL_O_DENY_WRITE) != 0) - ((int)(old_openflags & FSAL_O_DENY_WRITE) != 0) + ((int)(new_openflags & FSAL_O_DENY_WRITE_MAND) != 0) - ((int)(old_openflags & FSAL_O_DENY_WRITE_MAND) != 0); int deny_write_mand_inc = ((int)(new_openflags & FSAL_O_DENY_WRITE_MAND) != 0) - ((int)(old_openflags & FSAL_O_DENY_WRITE_MAND) != 0); share->share_access_read += access_read_inc; share->share_access_write += access_write_inc; share->share_deny_read += deny_read_inc; share->share_deny_write += deny_write_inc; share->share_deny_write_mand += deny_write_mand_inc; LogFullDebug(COMPONENT_FSAL, "share counter: access_read %u, access_write %u, deny_read %u, deny_write %u, deny_write_v4 %u", share->share_access_read, share->share_access_write, share->share_deny_read, share->share_deny_write, share->share_deny_write_mand); } /** * @brief Check for share conflict * * The caller is responsible for protecting the share. * * This function is NOT called if the caller holds a share reservation covering * the requested access. * * @param[in] share File to query * @param[in] openflags Desired access and deny mode * @param[in] bypass Bypasses share_deny_read and share_deny_write but * not share_deny_write_mand * * @retval ERR_FSAL_SHARE_DENIED - a conflict occurred. * */ fsal_status_t check_share_conflict(struct fsal_share *share, fsal_openflags_t openflags, bool bypass) { char *cause = ""; if ((openflags & FSAL_O_READ) != 0 && share->share_deny_read > 0 && !bypass) { cause = "access read denied by existing deny read"; goto out_conflict; } if ((openflags & FSAL_O_WRITE) != 0 && (share->share_deny_write_mand > 0 || (!bypass && share->share_deny_write > 0))) { cause = "access write denied by existing deny write"; goto out_conflict; } if ((openflags & FSAL_O_DENY_READ) != 0 && share->share_access_read > 0) { cause = "deny read denied by existing access read"; goto out_conflict; } if (((openflags & FSAL_O_DENY_WRITE) != 0 || (openflags & FSAL_O_DENY_WRITE_MAND) != 0) && share->share_access_write > 0) { cause = "deny write denied by existing access write"; goto out_conflict; } return fsalstat(ERR_FSAL_NO_ERROR, 0); out_conflict: LogDebugAlt(COMPONENT_STATE, COMPONENT_FSAL, "Share conflict detected: %s openflags=%d bypass=%s", cause, (int) openflags, bypass ? "yes" : "no"); LogFullDebugAlt(COMPONENT_STATE, COMPONENT_FSAL, "share->share_deny_read=%d share->share_deny_write=%d share->share_access_read=%d share->share_access_write=%d", share->share_deny_read, share->share_deny_write, share->share_access_read, share->share_access_write); return fsalstat(ERR_FSAL_SHARE_DENIED, 0); } /** * @brief Check two shares for conflict and merge. * * The caller is responsible for protecting the share. * * When two object handles are merged that both contain shares, we must * check if the duplicate has a share conflict with the original. If * so, we will return ERR_FSAL_SHARE_DENIED. * * @param[in] orig_share Original share * @param[in] dupe_share Duplicate share * * @retval ERR_FSAL_SHARE_DENIED - a conflict occurred. * */ fsal_status_t merge_share(struct fsal_share *orig_share, struct fsal_share *dupe_share) { char *cause = ""; if (dupe_share->share_access_read > 0 && orig_share->share_deny_read > 0) { cause = "access read denied by existing deny read"; goto out_conflict; } if (dupe_share->share_deny_read > 0 && orig_share->share_access_read > 0) { cause = "deny read denied by existing access read"; goto out_conflict; } /* When checking deny write, we ONLY need to look at share_deny_write * since it counts BOTH FSAL_O_DENY_WRITE and FSAL_O_DENY_WRITE_MAND. */ if (dupe_share->share_access_write > 0 && orig_share->share_deny_write > 0) { cause = "access write denied by existing deny write"; goto out_conflict; } if (dupe_share->share_deny_write > 0 && orig_share->share_access_write > 0) { cause = "deny write denied by existing access write"; goto out_conflict; } /* Now that we are ok, merge the share counters in the original */ orig_share->share_access_read += dupe_share->share_access_read; orig_share->share_access_write += dupe_share->share_access_write; orig_share->share_deny_read += dupe_share->share_deny_read; orig_share->share_deny_write += dupe_share->share_deny_write; orig_share->share_deny_write_mand += dupe_share->share_deny_write_mand; return fsalstat(ERR_FSAL_NO_ERROR, 0); out_conflict: LogDebug(COMPONENT_STATE, "Share conflict detected: %s", cause); return fsalstat(ERR_FSAL_SHARE_DENIED, 0); } /** * @brief Reopen the fd associated with the object handle. * * This function assures that the fd is open in the mode requested. If * the fd was already open, it closes it and reopens with the OR of the * requested modes. * * This function will return with the object handle lock held for read * if successful, except in the case where a temporary file descriptor is * in use because of a conflict with another thread. By not holding the * lock in that case, it may allow yet a third thread to open the global * file descriptor in a usable mode reducing the use of temporary file * descriptors. * * On calling, out_fd must point to a temporary fd. On return, out_fd * will either still point to the temporary fd, which has now been opened * and must be closed when done, or it will point to the object handle's * global fd, which should be left open. * * Optionally, out_fd can be NULL in which case a file is not actually * opened, in this case, all that actually happens is the share reservation * check (which may result in the lock being held). * * @param[in] obj_hdl File on which to operate * @param[in] check_share Indicates we must check for share conflict * @param[in] openflags Mode for open * @param[in] my_fd The file descriptor associated with the object * @param[in] share The fsal_share associated with the object * @param[in] open_func Function to open a file descriptor * @param[in] close_func Function to close a file descriptor * @param[in,out] out_fd File descriptor that is to be used * @param[out] has_lock Indicates that obj_hdl->lock is held read * @param[out] closefd Indicates that file descriptor must be closed * * @return FSAL status. */ fsal_status_t fsal_reopen_obj(struct fsal_obj_handle *obj_hdl, bool check_share, bool bypass, fsal_openflags_t openflags, struct fsal_fd *my_fd, struct fsal_share *share, fsal_open_func open_func, fsal_close_func close_func, struct fsal_fd **out_fd, bool *has_lock, bool *closefd) { fsal_status_t status = {ERR_FSAL_NO_ERROR, 0}; bool retried = false; fsal_openflags_t try_openflags; int rc; *closefd = false; /* Take read lock on object to protect file descriptor. * We only take a read lock because we are not changing the * state of the file descriptor. */ PTHREAD_RWLOCK_rdlock(&obj_hdl->obj_lock); if (check_share) { /* Note we will check again if we drop and re-acquire the lock * just to be on the safe side. */ status = check_share_conflict(share, openflags, bypass); if (FSAL_IS_ERROR(status)) { PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); LogDebug(COMPONENT_FSAL, "check_share_conflict failed with %s", msg_fsal_err(status.major)); *has_lock = false; return status; } } if (out_fd == NULL) { /* We are just checking share reservation if at all. * There is no need to proceed, we either passed the * share check, or didn't need it. In either case, there * is no need to open a file. */ *has_lock = true; return fsalstat(ERR_FSAL_NO_ERROR, 0); } again: LogFullDebug(COMPONENT_FSAL, "Open mode = %x, desired mode = %x", (int) my_fd->openflags, (int) openflags); if (not_open_usable(my_fd->openflags, openflags)) { /* Drop the read lock */ PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); if (retried) { /* Since we drop write lock for 'obj_hdl->obj_lock' * and acquire read lock for 'obj_hdl->obj_lock' after * opening the global file descriptor, some other * thread could have closed the file causing * verification of 'openflags' to fail. * * We will now attempt to just provide a temporary * file descriptor */ LogDebug(COMPONENT_FSAL, "Retry failed."); *has_lock = false; } else { /* Switch to write lock on object to protect file * descriptor. * By using trylock, we don't block if another thread * is using the file descriptor right now. In that * case, we just open a temporary file descriptor. * * This prevents us from blocking for the duration of * an I/O request. */ rc = pthread_rwlock_trywrlock(&obj_hdl->obj_lock); } if (retried || rc == EBUSY) { /* Someone else is using the file descriptor. * Just provide a temporary file descriptor. * We still take a read lock so we can protect the * share reservation for the duration of the caller's * operation if we needed to check. */ if (check_share) { PTHREAD_RWLOCK_rdlock(&obj_hdl->obj_lock); status = check_share_conflict(share, openflags, bypass); if (FSAL_IS_ERROR(status)) { PTHREAD_RWLOCK_unlock( &obj_hdl->obj_lock); LogDebug(COMPONENT_FSAL, "check_share_conflict failed with %s", msg_fsal_err(status.major)); *has_lock = false; return status; } } status = open_func(obj_hdl, openflags, *out_fd); if (FSAL_IS_ERROR(status)) { if (check_share) PTHREAD_RWLOCK_unlock( &obj_hdl->obj_lock); *has_lock = false; return status; } /* Return the temp fd, with the lock only held if * share reservations were checked. */ *closefd = true; *has_lock = check_share; return fsalstat(ERR_FSAL_NO_ERROR, 0); } else if (rc != 0) { LogCrit(COMPONENT_RW_LOCK, "Error %d, write locking %p", rc, obj_hdl); abort(); } if (check_share) { status = check_share_conflict(share, openflags, bypass); if (FSAL_IS_ERROR(status)) { PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); LogDebug(COMPONENT_FSAL, "check_share_conflict failed with %s", msg_fsal_err(status.major)); *has_lock = false; return status; } } LogFullDebug(COMPONENT_FSAL, "Open mode = %x, desired mode = %x", (int) my_fd->openflags, (int) openflags); if (not_open_usable(my_fd->openflags, openflags)) { if (my_fd->openflags != FSAL_O_CLOSED) { /* Add desired mode to existing mode. */ try_openflags = openflags | my_fd->openflags; /* Now close the already open descriptor. */ status = close_func(obj_hdl, my_fd); (void) atomic_dec_size_t(&open_fd_count); if (FSAL_IS_ERROR(status)) { PTHREAD_RWLOCK_unlock( &obj_hdl->obj_lock); LogDebug(COMPONENT_FSAL, "close_func failed with %s", msg_fsal_err(status.major)); *has_lock = false; return status; } } else if (openflags == FSAL_O_ANY) { try_openflags = FSAL_O_READ; } else { try_openflags = openflags; } LogFullDebug(COMPONENT_FSAL, "try_openflags = %x", try_openflags); if (!mdcache_lru_fds_available()) { PTHREAD_RWLOCK_unlock( &obj_hdl->obj_lock); *has_lock = false; /* This seems the best idea, let the * client try again later after the reap. */ return fsalstat(ERR_FSAL_DELAY, 0); } /* Actually open the file */ status = open_func(obj_hdl, try_openflags, my_fd); if (FSAL_IS_ERROR(status)) { PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); LogDebug(COMPONENT_FSAL, "open_func failed with %s", msg_fsal_err(status.major)); *has_lock = false; return status; } (void) atomic_inc_size_t(&open_fd_count); } /* Ok, now we should be in the correct mode. * Switch back to read lock and try again. * We don't want to hold the write lock because that would * block other users of the file descriptor. * Since we dropped the lock, we need to verify mode is still' * good after we re-aquire the read lock, thus the retry. */ PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); PTHREAD_RWLOCK_rdlock(&obj_hdl->obj_lock); retried = true; if (check_share) { status = check_share_conflict(share, openflags, bypass); if (FSAL_IS_ERROR(status)) { PTHREAD_RWLOCK_unlock(&obj_hdl->obj_lock); LogDebug(COMPONENT_FSAL, "check_share_conflict failed with %s", msg_fsal_err(status.major)); *has_lock = false; return status; } } goto again; } /* Return the global fd, with the lock held. */ *out_fd = my_fd; *has_lock = true; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Find a useable file descriptor for a regular file. * * This function specifically does NOT return with the obj_handle's lock * held if the fd associated with a state_t is being used. These fds are * considered totally separate from the global fd and don't need protection * and should not interfere with other operations on the object. * * Optionally, out_fd can be NULL in which case a file is not actually * opened, in this case, all that actually happens is the share reservation * check (which may result in the lock being held). * * @param[in,out] out_fd File descriptor that is to be used * @param[in] obj_hdl File on which to operate * @param[in] obj_fd The file descriptor associated with the object * @param[in] bypass If state doesn't indicate a share reservation, * bypass any deny read * @param[in] state state_t to use for this operation * @param[in] openflags Mode for open * @param[in] open_func Function to open a file descriptor * @param[in] close_func Function to close a file descriptor * @param[out] has_lock Indicates that obj_hdl->obj_lock is held read * @param[out] closefd Indicates that file descriptor must be closed * @param[in] open_for_locks Indicates file is open for locks * @param[out] reusing_open_state_fd Indicates whether already opened fd * can be reused * * @return FSAL status. */ fsal_status_t fsal_find_fd(struct fsal_fd **out_fd, struct fsal_obj_handle *obj_hdl, struct fsal_fd *obj_fd, struct fsal_share *share, bool bypass, struct state_t *state, fsal_openflags_t openflags, fsal_open_func open_func, fsal_close_func close_func, bool *has_lock, bool *closefd, bool open_for_locks, bool *reusing_open_state_fd) { fsal_status_t status = {ERR_FSAL_NO_ERROR, 0}; struct fsal_fd *state_fd; if (state == NULL) goto global; /* Check if we can use the fd in the state */ state_fd = (struct fsal_fd *) (state + 1); LogFullDebug(COMPONENT_FSAL, "state_fd->openflags = %d openflags = %d", state_fd->openflags, openflags); if (open_correct(state_fd->openflags, openflags)) { /* It was valid, return it. * Since we found a valid fd in the state, no need to * check deny modes. */ LogFullDebug(COMPONENT_FSAL, "Use state_fd %p", state_fd); if (out_fd) *out_fd = state_fd; *has_lock = false; return status; } if (open_for_locks) { if (state_fd->openflags != FSAL_O_CLOSED) { LogCrit(COMPONENT_FSAL, "Conflicting open, can not re-open fd with locks"); return fsalstat(posix2fsal_error(EINVAL), EINVAL); } /* This is being opened for locks, we will not be able to * re-open so open for read/write unless openstate indicates * something different. */ if (state->state_data.lock.openstate != NULL) { struct fsal_fd *related_fd = (struct fsal_fd *) (state->state_data.lock.openstate + 1); openflags = related_fd->openflags & FSAL_O_RDWR; } else { /* No associated open, open read/write. */ openflags = FSAL_O_RDWR; } status = open_func(obj_hdl, openflags, state_fd); if (FSAL_IS_ERROR(status)) { LogCrit(COMPONENT_FSAL, "Open for locking failed"); } else { LogFullDebug(COMPONENT_FSAL, "Opened state_fd %p", state_fd); *out_fd = state_fd; } *has_lock = false; return status; } /* Check if there is a related state, in which case, can we use it's * fd (this will support FSALs that have an open file per open state * but don't bother with opening a separate file for the lock state). */ if ((state->state_type == STATE_TYPE_LOCK || state->state_type == STATE_TYPE_NLM_LOCK) && state->state_data.lock.openstate != NULL) { struct fsal_fd *related_fd = (struct fsal_fd *) (state->state_data.lock.openstate + 1); LogFullDebug(COMPONENT_FSAL, "related_fd->openflags = %d openflags = %d", related_fd->openflags, openflags); if (open_correct(related_fd->openflags, openflags)) { /* It was valid, return it. * Since we found a valid fd in the open state, no * need to check deny modes. */ LogFullDebug(COMPONENT_FSAL, "Use related_fd %p", related_fd); if (out_fd) { *out_fd = related_fd; /* The associated open state has an open fd, * however some FSALs can not use it and must * need to dup the fd into the lock state * instead. So to signal this to the caller * function the following flag */ *reusing_open_state_fd = true; } *has_lock = false; return status; } } global: /* No useable state_t so return the global file descriptor. */ LogFullDebug(COMPONENT_FSAL, "Use global fd openflags = %x", openflags); /* Make sure global is open as necessary otherwise return a * temporary file descriptor. Check share reservation if not * opening FSAL_O_ANY. */ return fsal_reopen_obj(obj_hdl, openflags != FSAL_O_ANY, bypass, openflags, obj_fd, share, open_func, close_func, out_fd, has_lock, closefd); } /** * @brief Check the exclusive create verifier for a file. * * The default behavior is to check verifier against atime and mtime. * * @param[in] st POSIX attributes for the file (from stat) * @param[in] verifier Verifier to use for exclusive create * * @retval true if verifier matches */ bool check_verifier_stat(struct stat *st, fsal_verifier_t verifier) { uint32_t verf_hi = 0, verf_lo = 0; memcpy(&verf_hi, verifier, sizeof(uint32_t)); memcpy(&verf_lo, verifier + sizeof(uint32_t), sizeof(uint32_t)); LogFullDebug(COMPONENT_FSAL, "Passed verifier %"PRIx32" %"PRIx32 " file verifier %"PRIx32" %"PRIx32, verf_hi, verf_lo, (uint32_t) st->st_atim.tv_sec, (uint32_t) st->st_mtim.tv_sec); return st->st_atim.tv_sec == verf_hi && st->st_mtim.tv_sec == verf_lo; } /** * @brief Check the exclusive create verifier for a file. * * The default behavior is to check verifier against atime and mtime. * * @param[in] attrlist Attributes for the file * @param[in] verifier Verifier to use for exclusive create * * @retval true if verifier matches */ bool check_verifier_attrlist(struct attrlist *attrs, fsal_verifier_t verifier) { uint32_t verf_hi = 0, verf_lo = 0; memcpy(&verf_hi, verifier, sizeof(uint32_t)); memcpy(&verf_lo, verifier + sizeof(uint32_t), sizeof(uint32_t)); LogFullDebug(COMPONENT_FSAL, "Passed verifier %"PRIx32" %"PRIx32 " file verifier %"PRIx32" %"PRIx32, verf_hi, verf_lo, (uint32_t) attrs->atime.tv_sec, (uint32_t) attrs->mtime.tv_sec); return attrs->atime.tv_sec == verf_hi && attrs->mtime.tv_sec == verf_lo; } /** @} */ nfs-ganesha-2.6.0/src/FSAL/default_methods.c000066400000000000000000001173341324272410200205460ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @addtogroup FSAL * @{ */ /** * @file default_methods.c * @author Jim Lieb * @brief System wide default FSAL methods */ #include "config.h" #include #include #include #include #include #include #include #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "config_parsing.h" #include "gsh_types.h" #include "FSAL/fsal_commonlib.h" #include "fsal_private.h" #include "pnfs_utils.h" #include "nfs_creds.h" #include "sal_data.h" /** fsal module method defaults and common methods */ /** NOTE * These are the common and default methods. One important requirement * is that older fsals must safely run with newer ganesha core. * This is observed by the following rules: * * 1. New methods are *always* appended to the m_ops vector in fsal_api.h * * 2. This file is updated to add the default method. * * 3. The version numbers are bumped in fsal_api.h appropriately so * version detection is correct. */ /* unload fsal * called while holding the last remaining reference * remove from list and dlclose the module * if references are held, return EBUSY * if it is a static, return EACCES */ static int unload_fsal(struct fsal_module *fsal_hdl) { int retval = EBUSY; /* someone still has a reference */ int32_t refcount = atomic_fetch_int32_t(&fsal_hdl->refcount); LogDebug(COMPONENT_FSAL, "refcount = %"PRIi32, refcount); PTHREAD_MUTEX_lock(&fsal_lock); if (refcount != 0 || !glist_empty(&fsal_hdl->exports)) { LogCrit(COMPONENT_FSAL, "Can not unload FSAL %s refcount=%"PRIi32, fsal_hdl->name, refcount); goto err; } if (fsal_hdl->dl_handle == NULL) { LogCrit(COMPONENT_FSAL, "Can not unload static linked FSAL %s", fsal_hdl->name); retval = EACCES; goto err; } glist_del(&fsal_hdl->fsals); PTHREAD_RWLOCK_destroy(&fsal_hdl->lock); retval = dlclose(fsal_hdl->dl_handle); PTHREAD_MUTEX_unlock(&fsal_lock); return retval; err: PTHREAD_RWLOCK_unlock(&fsal_hdl->lock); PTHREAD_MUTEX_unlock(&fsal_lock); return retval; } /* init_config * default case is we have no config so return happy */ static fsal_status_t init_config(struct fsal_module *fsal_hdl, config_file_t config_struct, struct config_error_type *err_type) { return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* dump_config * default is to do nothing */ static void dump_config(struct fsal_module *fsal_hdl, int log_fd) { /* return */ } /* create_export * default is we cannot create an export */ static fsal_status_t create_export(struct fsal_module *fsal_hdl, void *parse_node, struct config_error_type *err_type, const struct fsal_up_vector *up_ops) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /** * @brief Default emergency cleanup method * * Do nothing. */ static void emergency_cleanup(void) { /* return */ } /** * @brief Be uninformative about a device */ static nfsstat4 getdeviceinfo(struct fsal_module *fsal_hdl, XDR *da_addr_body, const layouttype4 type, const struct pnfs_deviceid *deviceid) { return NFS4ERR_NOTSUPP; } /** * No da_addr. */ static size_t fs_da_addr_size(struct fsal_module *fsal_hdl) { return 0; } /** * @brief Try to create a FSAL pNFS data server * * @param[in] fsal_hdl FSAL module * @param[in] parse_node opaque pointer to parse tree node for * export options to be passed to * load_config_from_node * @param[out] handle FSAL pNFS DS * * @return FSAL status. */ static fsal_status_t fsal_pnfs_ds(struct fsal_module *const fsal_hdl, void *parse_node, struct fsal_pnfs_ds **const handle) { LogDebug(COMPONENT_PNFS, "Default pNFS DS creation!"); if (*handle == NULL) *handle = pnfs_ds_alloc(); fsal_pnfs_ds_init(*handle, fsal_hdl); op_ctx->fsal_pnfs_ds = *handle; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Initialize FSAL specific values for pNFS data server * * @param[in] ops FSAL pNFS Data Server operations vector */ static void fsal_pnfs_ds_ops(struct fsal_pnfs_ds_ops *ops) { memcpy(ops, &def_pnfs_ds_ops, sizeof(struct fsal_pnfs_ds_ops)); } /** * @brief Provides function to extract FSAL stats * * @param[in] fsal_hdl FSAL module * @param[in] iter opaque pointer to DBusMessageIter */ static void fsal_extract_stats(struct fsal_module *const fsal_hdl, void *iter) { LogDebug(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); } /** * @brief FSAL function to reset FSAL stats * * @param[in] fsal_hdl FSAL module */ static void fsal_reset_stats(struct fsal_module *const fsal_hdl) { LogDebug(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); } /* Default fsal module method vector. * copied to allocated vector at register time */ struct fsal_ops def_fsal_ops = { .unload = unload_fsal, .init_config = init_config, .dump_config = dump_config, .create_export = create_export, .emergency_cleanup = emergency_cleanup, .getdeviceinfo = getdeviceinfo, .fs_da_addr_size = fs_da_addr_size, .fsal_pnfs_ds = fsal_pnfs_ds, .fsal_pnfs_ds_ops = fsal_pnfs_ds_ops, .fsal_extract_stats = fsal_extract_stats, .fsal_reset_stats = fsal_reset_stats, }; /* get_name * default case is to return the name of the FSAL */ static const char *get_name(struct fsal_export *exp_hdl) { return exp_hdl->fsal->name; } /* export_unexport * Nothing to do in the default case */ static void export_unexport(struct fsal_export *exp_hdl, struct fsal_obj_handle *root_obj) { /* return */ } /* export_release * Nothing to do in the default case */ static void export_release(struct fsal_export *exp_hdl) { /* return */ } /* lookup_path * default case is not supported */ fsal_status_t lookup_path(struct fsal_export *exp_hdl, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } static fsal_status_t lookup_junction(struct fsal_export *exp_hdl, struct fsal_obj_handle *junction, struct fsal_obj_handle **handle) { return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* wire_to_host * default case is not supported */ static fsal_status_t wire_to_host(struct fsal_export *exp_hdl, fsal_digesttype_t in_type, struct gsh_buffdesc *fh_desc, int flags) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* exp_host_to_key Produce handle-key from host-handle * * default case is that "handle-key" is same as host-handle! */ static fsal_status_t exp_host_to_key(struct fsal_export *exp_hdl, struct gsh_buffdesc *fh_desc) { return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* create_handle * default case is not supported */ static fsal_status_t create_handle(struct fsal_export *exp_hdl, struct gsh_buffdesc *hdl_desc, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* get_dynamic_info * default case is not supported */ static fsal_status_t get_dynamic_info(struct fsal_export *exp_hdl, struct fsal_obj_handle *obj_hdl, fsal_dynamicfsinfo_t *infop) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* fs_supports * default case is supports nothing */ static bool fs_supports(struct fsal_export *exp_hdl, fsal_fsinfo_options_t option) { return false; } /* fs_maxfilesize * default case is zero size */ static uint64_t fs_maxfilesize(struct fsal_export *exp_hdl) { return 0; } /* fs_maxread * default case is zero length */ static uint32_t fs_maxread(struct fsal_export *exp_hdl) { return 0; } /* fs_maxwrite * default case is zero length */ static uint32_t fs_maxwrite(struct fsal_export *exp_hdl) { return 0; } /* fs_maxlink * default case is zero links */ static uint32_t fs_maxlink(struct fsal_export *exp_hdl) { return 0; } /* fs_maxnamelen * default case is zero length */ static uint32_t fs_maxnamelen(struct fsal_export *exp_hdl) { return 0; } /* fs_maxpathlen * default case is zero length */ static uint32_t fs_maxpathlen(struct fsal_export *exp_hdl) { return 0; } /* fs_lease_time * default case is zero interval time */ static struct timespec fs_lease_time(struct fsal_export *exp_hdl) { struct timespec lease_time = { 0, 0 }; return lease_time; } /* fs_acl_support * default case is none, neither deny or allow */ static fsal_aclsupp_t fs_acl_support(struct fsal_export *exp_hdl) { return 0; } /* fs_supported_attrs * default case is none */ static attrmask_t fs_supported_attrs(struct fsal_export *exp_hdl) { return 0; } /* fs_umask * default case is no access */ static uint32_t fs_umask(struct fsal_export *exp_hdl) { return 0000; } /* fs_xattr_access_rights * default case is no access */ static uint32_t fs_xattr_access_rights(struct fsal_export *exp_hdl) { return 0000; } /* check_quota * return happiness for now. */ static fsal_status_t check_quota(struct fsal_export *exp_hdl, const char *filepath, int quota_type) { return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* get_quota * default case not supported */ static fsal_status_t get_quota(struct fsal_export *exp_hdl, const char *filepath, int quota_type, int quota_id, fsal_quota_t *pquota) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* set_quota * default case not supported */ static fsal_status_t set_quota(struct fsal_export *exp_hdl, const char *filepath, int quota_type, int quota_id, fsal_quota_t *pquota, fsal_quota_t *presquota) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /** * @brief Be uninformative about all devices */ static nfsstat4 getdevicelist(struct fsal_export *exp_hdl, layouttype4 type, void *opaque, bool(*cb) (void *opaque, const uint64_t id), struct fsal_getdevicelist_res *res) { return NFS4ERR_NOTSUPP; } /** * @brief Support no layout types */ static void fs_layouttypes(struct fsal_export *exp_hdl, int32_t *count, const layouttype4 **types) { *count = 0; *types = NULL; } /** * @brief Read no bytes through layouts */ static uint32_t fs_layout_blocksize(struct fsal_export *exp_hdl) { return 0; } /** * @brief No segments */ static uint32_t fs_maximum_segments(struct fsal_export *exp_hdl) { return 0; } /** * @brief No loc_body */ static size_t fs_loc_body_size(struct fsal_export *exp_hdl) { return 0; } /** * @brief Get write verifier * * This function is called by write and commit to match the commit verifier * with the one returned on write. * * @param[in,out] verf_desc Address and length of verifier * * @return No errors */ static void global_verifier(struct fsal_export *exp_hdl, struct gsh_buffdesc *verf_desc) { memcpy(verf_desc->addr, &NFS4_write_verifier, verf_desc->len); } /** * @brief Allocate a state_t structure * * Note that this is not expected to fail since memory allocation is * expected to abort on failure. * * @param[in] exp_hdl Export state_t will be associated with * @param[in] state_type Type of state to allocate * @param[in] related_state Related state if appropriate * * @returns a state structure. */ static struct state_t *alloc_state(struct fsal_export *exp_hdl, enum state_type state_type, struct state_t *related_state) { return init_state(gsh_calloc(1, sizeof(struct state_t)), exp_hdl, state_type, related_state); } /** * @brief Free a state_t structure * * @param[in] state state_t structure to free. * * @returns NULL on failure otherwise a state structure. */ void free_state(struct fsal_export *exp_hdl, struct state_t *state) { gsh_free(state); } /** * @brief Check to see if a user is superuser * * @param[in] exp_hdl Export state_t is associated with * @param[in] creds Credentials to check for superuser * * @returns NULL on failure otherwise a state structure. */ bool is_superuser(struct fsal_export *exp_hdl, const struct user_cred *creds) { return (creds->caller_uid == 0); } /* Default fsal export method vector. * copied to allocated vector at register time */ struct export_ops def_export_ops = { .get_name = get_name, .unexport = export_unexport, .release = export_release, .lookup_path = lookup_path, .lookup_junction = lookup_junction, .wire_to_host = wire_to_host, .host_to_key = exp_host_to_key, .create_handle = create_handle, .get_fs_dynamic_info = get_dynamic_info, .fs_supports = fs_supports, .fs_maxfilesize = fs_maxfilesize, .fs_maxread = fs_maxread, .fs_maxwrite = fs_maxwrite, .fs_maxlink = fs_maxlink, .fs_maxnamelen = fs_maxnamelen, .fs_maxpathlen = fs_maxpathlen, .fs_lease_time = fs_lease_time, .fs_acl_support = fs_acl_support, .fs_supported_attrs = fs_supported_attrs, .fs_umask = fs_umask, .fs_xattr_access_rights = fs_xattr_access_rights, .check_quota = check_quota, .get_quota = get_quota, .set_quota = set_quota, .getdevicelist = getdevicelist, .fs_layouttypes = fs_layouttypes, .fs_layout_blocksize = fs_layout_blocksize, .fs_maximum_segments = fs_maximum_segments, .fs_loc_body_size = fs_loc_body_size, .get_write_verifier = global_verifier, .alloc_state = alloc_state, .free_state = free_state, .is_superuser = is_superuser, }; /* fsal_obj_handle common methods */ /* handle_is * test the type of this handle */ static bool handle_is(struct fsal_obj_handle *obj_hdl, object_file_type_t type) { return obj_hdl->type == type; } /* get_ref * This MUST be handled by someone. For many FSALs, it is handled by * FSAL_MDCACHE. */ static void handle_get_ref(struct fsal_obj_handle *obj_hdl) { LogFatal(COMPONENT_FSAL, "FSAL Must support get_ref"); } /* put_ref * This MUST be handled by someone. For many FSALs, it is handled by * FSAL_MDCACHE. */ static void handle_put_ref(struct fsal_obj_handle *obj_hdl) { LogFatal(COMPONENT_FSAL, "FSAL Must support put_ref"); } /* handle_release * default case is to throw a fault error. * creating an handle is not supported so getting here is bad */ static void handle_release(struct fsal_obj_handle *obj_hdl) { /* return */ } /** * @brief Merge a duplicate handle with an original handle * * This function is used if an upper layer detects that a duplicate * object handle has been created. It allows the FSAL to merge anything * from the duplicate back into the original. * * The caller must release the object (the caller may have to close * files if the merge is unsuccessful). * * @param[in] orig_hdl Original handle * @param[in] dupe_hdl Handle to merge into original * * @return FSAL status. * */ static fsal_status_t handle_merge(struct fsal_obj_handle *orig_hdl, struct fsal_obj_handle *dupe_hdl) { /* Default is to do nothing. */ return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* lookup * default case not supported */ static fsal_status_t lookup(struct fsal_obj_handle *parent, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* read_dirents * default case not supported */ static fsal_status_t read_dirents(struct fsal_obj_handle *dir_hdl, fsal_cookie_t *whence, void *dir_state, fsal_readdir_cb cb, attrmask_t attrmask, bool *eof) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* compute_readdir_cookie * default is to return 0 which indicates not supported */ fsal_cookie_t compute_readdir_cookie(struct fsal_obj_handle *parent, const char *name) { return 0; } /* dirent_cmp * Sort dirents by name. */ int dirent_cmp(struct fsal_obj_handle *parent, const char *name1, fsal_cookie_t cookie1, const char *name2, fsal_cookie_t cookie2) { /* Not supported by default. */ assert(false); return 0; } /* makedir * default case not supported */ static fsal_status_t makedir(struct fsal_obj_handle *dir_hdl, const char *name, struct attrlist *attrib, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* makenode * default case not supported */ static fsal_status_t makenode(struct fsal_obj_handle *dir_hdl, const char *name, object_file_type_t nodetype, struct attrlist *attrib, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* makesymlink * default case not supported */ static fsal_status_t makesymlink(struct fsal_obj_handle *dir_hdl, const char *name, const char *link_path, struct attrlist *attrib, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* readsymlink * default case not supported */ static fsal_status_t readsymlink(struct fsal_obj_handle *obj_hdl, struct gsh_buffdesc *link_content, bool refresh) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* getxattrs * default case not supported */ static fsal_status_t getxattrs(struct fsal_obj_handle *obj_hdl, xattrname4 *xa_name, xattrvalue4 *xa_value) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* setxattrs * default case not supported */ static fsal_status_t setxattrs(struct fsal_obj_handle *obj_hdl, setxattr_type4 sa_type, xattrname4 *xa_name, xattrvalue4 *xa_value) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* removexattrs * default case not supported */ static fsal_status_t removexattrs(struct fsal_obj_handle *obj_hdl, xattrname4 *xa_name) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* listxattrs * default case not supported */ static fsal_status_t listxattrs(struct fsal_obj_handle *obj_hdl, count4 la_maxcount, nfs_cookie4 *la_cookie, verifier4 *la_cookieverf, bool_t *lr_eof, xattrlist4 *lr_names) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* getattrs * default case not supported */ static fsal_status_t getattrs(struct fsal_obj_handle *obj_hdl, struct attrlist *attrs) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* linkfile * default case not supported */ static fsal_status_t linkfile(struct fsal_obj_handle *obj_hdl, struct fsal_obj_handle *destdir_hdl, const char *name) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* renamefile * default case not supported */ static fsal_status_t renamefile(struct fsal_obj_handle *obj_hdl, struct fsal_obj_handle *olddir_hdl, const char *old_name, struct fsal_obj_handle *newdir_hdl, const char *new_name) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* file_unlink * default case not supported */ static fsal_status_t file_unlink(struct fsal_obj_handle *dir_hdl, struct fsal_obj_handle *obj_hdl, const char *name) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* fs_locations * default case not supported */ static fsal_status_t fs_locations(struct fsal_obj_handle *obj_hdl, struct fs_locations4 *fs_locs) { return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* seek * default case not supported */ static fsal_status_t file_seek(struct fsal_obj_handle *obj_hdl, struct io_info *info) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* io advise * default case not supported */ static fsal_status_t file_io_advise(struct fsal_obj_handle *obj_hdl, struct io_hints *hints) { hints->hints = 0; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* file_close * default case not supported */ static fsal_status_t file_close(struct fsal_obj_handle *obj_hdl) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* list_ext_attrs * default case not supported */ static fsal_status_t list_ext_attrs(struct fsal_obj_handle *obj_hdl, unsigned int cookie, fsal_xattrent_t *xattrs_tab, unsigned int xattrs_tabsize, unsigned int *p_nb_returned, int *end_of_list) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* getextattr_id_by_name * default case not supported */ static fsal_status_t getextattr_id_by_name(struct fsal_obj_handle *obj_hdl, const char *xattr_name, unsigned int *pxattr_id) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* getextattr_value_by_name * default case not supported */ static fsal_status_t getextattr_value_by_name(struct fsal_obj_handle *obj_hdl, const char *xattr_name, caddr_t buffer_addr, size_t buffer_size, size_t *p_output_size) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* getextattr_value_by_id * default case not supported */ static fsal_status_t getextattr_value_by_id(struct fsal_obj_handle *obj_hdl, unsigned int xattr_id, caddr_t buffer_addr, size_t buffer_size, size_t *p_output_size) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* setextattr_value * default case not supported */ static fsal_status_t setextattr_value(struct fsal_obj_handle *obj_hdl, const char *xattr_name, caddr_t buffer_addr, size_t buffer_size, int create) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* setextattr_value_by_id * default case not supported */ static fsal_status_t setextattr_value_by_id(struct fsal_obj_handle *obj_hdl, unsigned int xattr_id, caddr_t buffer_addr, size_t buffer_size) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* remove_extattr_by_id * default case not supported */ static fsal_status_t remove_extattr_by_id(struct fsal_obj_handle *obj_hdl, unsigned int xattr_id) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* remove_extattr_by_name * default case not supported */ static fsal_status_t remove_extattr_by_name(struct fsal_obj_handle *obj_hdl, const char *xattr_name) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* handle_to_wire * default case server fault */ static fsal_status_t handle_to_wire(const struct fsal_obj_handle *obj_hdl, fsal_digesttype_t output_type, struct gsh_buffdesc *fh_desc) { return fsalstat(ERR_FSAL_SERVERFAULT, 0); } /** * handle_cmp * default case compare with handle_to_key */ static bool handle_cmp(struct fsal_obj_handle *obj_hdl1, struct fsal_obj_handle *obj_hdl2) { struct gsh_buffdesc key1, key2; if (obj_hdl1 == NULL || obj_hdl2 == NULL) return false; if (obj_hdl1 == obj_hdl2) return true; obj_hdl1->obj_ops.handle_to_key(obj_hdl1, &key1); obj_hdl2->obj_ops.handle_to_key(obj_hdl2, &key2); if (key1.len != key2.len) return false; return !memcmp(key1.addr, key2.addr, key1.len); } /** * handle_to_key * default case return a safe empty key */ static void handle_to_key(struct fsal_obj_handle *obj_hdl, struct gsh_buffdesc *fh_desc) { fh_desc->addr = obj_hdl; fh_desc->len = 0; } /** * @brief Fail to grant a layout segment. * * @param[in] obj_hdl The handle of the file on which the layout is * requested. * @param[in] req_ctx Request context * @param[out] loc_body An XDR stream to which the FSAL must encode * the layout specific portion of the granted * layout segment. * @param[in] arg Input arguments of the function * @param[in,out] res In/out and output arguments of the function * * @return NFS4ERR_LAYOUTUNAVAILABLE */ static nfsstat4 layoutget(struct fsal_obj_handle *obj_hdl, struct req_op_context *req_ctx, XDR *loc_body, const struct fsal_layoutget_arg *arg, struct fsal_layoutget_res *res) { return NFS4ERR_LAYOUTUNAVAILABLE; } /** * @brief Don't return a layout segment * * @param[in] obj_hdl The object on which a segment is to be returned * @param[in] req_ctx Request context * @param[in] lrf_body In the case of a non-synthetic return, this is * an XDR stream corresponding to the layout * type-specific argument to LAYOUTRETURN. In * the case of a synthetic or bulk return, * this is a NULL pointer. * @param[in] arg Input arguments of the function * * @return NFS4ERR_NOTSUPP */ static nfsstat4 layoutreturn(struct fsal_obj_handle *obj_hdl, struct req_op_context *req_ctx, XDR *lrf_body, const struct fsal_layoutreturn_arg *arg) { return NFS4ERR_NOTSUPP; } /** * @brief Fail to commit a segment of a layout * * @param[in] obj_hdl The object on which to commit * @param[in] req_ctx Request context * @param[in] lou_body An XDR stream containing the layout * type-specific portion of the LAYOUTCOMMIT * arguments. * @param[in] arg Input arguments of the function * @param[in,out] res In/out and output arguments of the function * * @return Valid error codes in RFC 5661, p. 366. */ static nfsstat4 layoutcommit(struct fsal_obj_handle *obj_hdl, struct req_op_context *req_ctx, XDR *lou_body, const struct fsal_layoutcommit_arg *arg, struct fsal_layoutcommit_res *res) { return NFS4ERR_NOTSUPP; } /* open2 * default case not supported */ static fsal_status_t open2(struct fsal_obj_handle *obj_hdl, struct state_t *fd, 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) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /** * @brief Check the exclusive create verifier for a file. * * The default behavior is to check verifier against atime and mtime. * * @param[in] obj_hdl File to check verifier * @param[in] verifier Verifier to use for exclusive create * * @retval true if verifier matches */ static bool check_verifier(struct fsal_obj_handle *obj_hdl, fsal_verifier_t verifier) { struct attrlist attrs; bool result; fsal_prepare_attrs(&attrs, ATTR_ATIME | ATTR_MTIME); if (FSAL_IS_ERROR(obj_hdl->obj_ops.getattrs(obj_hdl, &attrs))) return false; result = check_verifier_attrlist(&attrs, verifier); /* Done with the attrs */ fsal_release_attrs(&attrs); return result; } /* status2 * default case return openflags */ static fsal_openflags_t status2(struct fsal_obj_handle *obj_hdl, struct state_t *state) { struct fsal_fd *fd = (struct fsal_fd *)(state + 1); return fd->openflags; } /* reopen2 * default case not supported */ static fsal_status_t reopen2(struct fsal_obj_handle *obj_hdl, struct state_t *state, fsal_openflags_t openflags) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* read2 * default case not supported */ static fsal_status_t read2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t seek_descriptor, size_t buffer_size, void *buffer, size_t *read_amount, bool *end_of_file, struct io_info *info) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* write2 * default case not supported */ static fsal_status_t write2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t seek_descriptor, size_t buffer_size, void *buffer, size_t *write_amount, bool *fsal_stable, struct io_info *info) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* seek2 * default case not supported */ static fsal_status_t seek2(struct fsal_obj_handle *obj_hdl, struct state_t *fd, struct io_info *info) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* io io_advise2 * default case not supported */ static fsal_status_t io_advise2(struct fsal_obj_handle *obj_hdl, struct state_t *fd, struct io_hints *hints) { hints->hints = 0; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* commit2 * default case not supported */ static fsal_status_t commit2(struct fsal_obj_handle *obj_hdl, off_t offset, size_t len) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* lock_op2 * default case not supported */ static fsal_status_t lock_op2(struct fsal_obj_handle *obj_hdl, struct state_t *state, void *p_owner, fsal_lock_op_t lock_op, fsal_lock_param_t *request_lock, fsal_lock_param_t *conflicting_lock) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* lease_op2 * default case not supported */ static fsal_status_t lease_op2(struct fsal_obj_handle *obj_hdl, struct state_t *state, void *owner, fsal_deleg_t deleg) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* setattr2 * default case not supported */ static fsal_status_t setattr2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, struct attrlist *attrs) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* close2 * default case not supported */ static fsal_status_t close2(struct fsal_obj_handle *obj_hdl, struct state_t *fd) { LogCrit(COMPONENT_FSAL, "Invoking unsupported FSAL operation"); return fsalstat(ERR_FSAL_NOTSUPP, ENOTSUP); } /* Default fsal handle object method vector. * copied to allocated vector at register time */ struct fsal_obj_ops def_handle_ops = { .get_ref = handle_get_ref, .put_ref = handle_put_ref, .release = handle_release, .merge = handle_merge, .lookup = lookup, .readdir = read_dirents, .compute_readdir_cookie = compute_readdir_cookie, .dirent_cmp = dirent_cmp, .mkdir = makedir, .mknode = makenode, .symlink = makesymlink, .readlink = readsymlink, .test_access = fsal_test_access, /* default is use common test */ .getattrs = getattrs, .link = linkfile, .rename = renamefile, .unlink = file_unlink, .fs_locations = fs_locations, .seek = file_seek, .io_advise = file_io_advise, .close = file_close, .list_ext_attrs = list_ext_attrs, .getextattr_id_by_name = getextattr_id_by_name, .getextattr_value_by_name = getextattr_value_by_name, .getextattr_value_by_id = getextattr_value_by_id, .setextattr_value = setextattr_value, .setextattr_value_by_id = setextattr_value_by_id, .remove_extattr_by_id = remove_extattr_by_id, .remove_extattr_by_name = remove_extattr_by_name, .handle_is = handle_is, .handle_to_wire = handle_to_wire, .handle_cmp = handle_cmp, .handle_to_key = handle_to_key, .layoutget = layoutget, .layoutreturn = layoutreturn, .layoutcommit = layoutcommit, .getxattrs = getxattrs, .setxattrs = setxattrs, .removexattrs = removexattrs, .listxattrs = listxattrs, .open2 = open2, .check_verifier = check_verifier, .status2 = status2, .reopen2 = reopen2, .read2 = read2, .write2 = write2, .seek2 = seek2, .io_advise2 = io_advise2, .commit2 = commit2, .lock_op2 = lock_op2, .lease_op2 = lease_op2, .setattr2 = setattr2, .close2 = close2, }; /* fsal_pnfs_ds common methods */ /** * @brief Clean up a pNFS Data Server * * Getting here is normal! * * @param[in] pds Handle to release */ static void pds_release(struct fsal_pnfs_ds *const pds) { LogDebug(COMPONENT_PNFS, "Default pNFS DS release!"); fsal_pnfs_ds_fini(pds); gsh_free(pds); } /** * @brief Initialize FSAL specific permissions per pNFS DS * * @param[in] pds FSAL pNFS DS * @param[in] req Incoming request. * * @retval NFS4_OK, NFS4ERR_ACCESS, NFS4ERR_WRONGSEC. */ static nfsstat4 pds_permissions(struct fsal_pnfs_ds *const pds, struct svc_req *req) { /* FIX ME!!! Replace with a non-export dependent system. * For now, reset per init_root_op_context() */ op_ctx->export_perms->set = root_op_export_set; op_ctx->export_perms->options = root_op_export_options; return NFS4_OK; } /** * @brief Try to create a FSAL data server handle * * @param[in] pds FSAL pNFS DS * @param[in] hdl_desc Buffer from which to create the struct * @param[out] handle FSAL DS handle * * @retval NFS4_OK, NFS4ERR_SERVERFAULT. */ static nfsstat4 pds_handle(struct fsal_pnfs_ds *const pds, const struct gsh_buffdesc *const hdl_desc, struct fsal_ds_handle **const handle, int flags) { LogCrit(COMPONENT_PNFS, "Unimplemented DS handle creation!"); *handle = gsh_calloc(1, sizeof(struct fsal_ds_handle)); fsal_ds_handle_init(*handle, pds); return NFS4_OK; } /** * @brief Initialize FSAL specific values for data server handle * * @param[in] ops FSAL DS handle operations vector */ static void pds_handle_ops(struct fsal_dsh_ops *ops) { memcpy(ops, &def_dsh_ops, sizeof(struct fsal_dsh_ops)); } struct fsal_pnfs_ds_ops def_pnfs_ds_ops = { .release = pds_release, .permissions = pds_permissions, .make_ds_handle = pds_handle, .fsal_dsh_ops = pds_handle_ops, }; /* fsal_ds_handle common methods */ /** * @brief Fail to clean up a DS handle * * Getting here is bad, it means we support but have not completely * implemented DS handles. * * @param[in] release Handle to release */ static void ds_release(struct fsal_ds_handle *const ds_hdl) { LogCrit(COMPONENT_PNFS, "Unimplemented DS handle release!"); fsal_ds_handle_fini(ds_hdl); gsh_free(ds_hdl); } /** * @brief Fail to read from a data-server handle. * * @param[in] ds_hdl FSAL DS handle * @param[in] req_ctx Credentials * @param[in] stateid The stateid supplied with the READ operation, * for validation * @param[in] offset The offset at which to read * @param[in] requested_length Length of read requested (and size of buffer) * @param[out] buffer The buffer to which to store read data * @param[out] supplied_length Length of data read * @param[out] eof true on end of file * * @return NFS4ERR_NOTSUPP. */ static nfsstat4 ds_read(struct fsal_ds_handle *const ds_hdl, struct req_op_context *const req_ctx, const stateid4 *stateid, const offset4 offset, const count4 requested_length, void *const buffer, count4 * const supplied_length, bool * const end_of_file) { LogCrit(COMPONENT_PNFS, "Unimplemented DS read!"); return NFS4ERR_NOTSUPP; } static nfsstat4 ds_read_plus(struct fsal_ds_handle *const ds_hdl, struct req_op_context *const req_ctx, const stateid4 *stateid, const offset4 offset, const count4 requested_length, void *const buffer, const count4 supplied_length, bool * const end_of_file, struct io_info *info) { LogCrit(COMPONENT_PNFS, "Unimplemented DS read_plus!"); return NFS4ERR_NOTSUPP; } /** * @brief Fail to write to a data-server handle. * * @param[in] ds_hdl FSAL DS handle * @param[in] req_ctx Credentials * @param[in] stateid The stateid supplied with the READ operation, * for validation * @param[in] offset The offset at which to read * @param[in] write_length Length of write requested (and size of buffer) * @param[out] buffer The buffer to which to store read data * @param[in] stability wanted Stability of write * @param[out] written_length Length of data written * @param[out] writeverf Write verifier * @param[out] stability_got Stability used for write (must be as * or more stable than request) * * @return An NFSv4.1 status code. */ static nfsstat4 ds_write(struct fsal_ds_handle *const ds_hdl, struct req_op_context *const req_ctx, const stateid4 *stateid, const offset4 offset, const count4 write_length, const void *buffer, const stable_how4 stability_wanted, count4 * const written_length, verifier4 * const writeverf, stable_how4 * const stability_got) { LogCrit(COMPONENT_PNFS, "Unimplemented DS write!"); return NFS4ERR_NOTSUPP; } static nfsstat4 ds_write_plus(struct fsal_ds_handle *const ds_hdl, struct req_op_context *const req_ctx, const stateid4 *stateid, const offset4 offset, const count4 write_length, const void *buffer, const stable_how4 stability_wanted, count4 * const written_length, verifier4 * const writeverf, stable_how4 * const stability_got, struct io_info *info) { LogCrit(COMPONENT_PNFS, "Unimplemented DS write_plus!"); return NFS4ERR_NOTSUPP; } /** * @brief Fail to commit a byte range on a DS handle. * * @param[in] ds_hdl FSAL DS handle * @param[in] req_ctx Credentials * @param[in] offset Start of commit window * @param[in] count Length of commit window * @param[out] writeverf Write verifier * * @return An NFSv4.1 status code. */ static nfsstat4 ds_commit(struct fsal_ds_handle *const ds_hdl, struct req_op_context *const req_ctx, const offset4 offset, const count4 count, verifier4 * const writeverf) { LogCrit(COMPONENT_PNFS, "Unimplemented DS commit!"); return NFS4ERR_NOTSUPP; } struct fsal_dsh_ops def_dsh_ops = { .release = ds_release, .read = ds_read, .read_plus = ds_read_plus, .write = ds_write, .write_plus = ds_write_plus, .commit = ds_commit }; /** @} */ nfs-ganesha-2.6.0/src/FSAL/fsal_config.c000066400000000000000000000077771324272410200176620ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @addtogroup FSAL * @{ */ /** * @file fsal_config.c * @author Jim Lieb * @brief Initialize configuration parameters */ #include "config.h" #include #include #include #include #include #include #include #include "log.h" #include "fsal.h" #include "FSAL/fsal_init.h" /* filesystem info handlers * common functions for fsal info methods */ bool fsal_supports(struct fsal_staticfsinfo_t *info, fsal_fsinfo_options_t option) { switch (option) { case fso_no_trunc: return !!info->no_trunc; case fso_chown_restricted: return !!info->chown_restricted; case fso_case_insensitive: return !!info->case_insensitive; case fso_case_preserving: return !!info->case_preserving; case fso_link_support: return !!info->link_support; case fso_symlink_support: return !!info->symlink_support; case fso_lock_support: return !!info->lock_support; case fso_lock_support_async_block: return !!info->lock_support_async_block; case fso_named_attr: return !!info->named_attr; case fso_unique_handles: return !!info->unique_handles; case fso_cansettime: return !!info->cansettime; case fso_homogenous: return !!info->homogenous; case fso_auth_exportpath_xdev: return !!info->auth_exportpath_xdev; case fso_delegations_r: return !!(info->delegations & FSAL_OPTION_FILE_READ_DELEG); case fso_delegations_w: return !!(info->delegations & FSAL_OPTION_FILE_WRITE_DELEG); case fso_pnfs_mds_supported: return !!info->pnfs_mds; case fso_pnfs_ds_supported: return !!info->pnfs_ds; case fso_grace_method: return !!info->fsal_grace; case fso_link_supports_permission_checks: return !!info->link_supports_permission_checks; case fso_rename_changes_key: return !!info->rename_changes_key; case fso_compute_readdir_cookie: return !!info->compute_readdir_cookie; case fso_whence_is_name: return !!info->whence_is_name; default: return false; /* whatever I don't know about, * you can't do */ } } uint64_t fsal_maxfilesize(struct fsal_staticfsinfo_t *info) { return info->maxfilesize; } uint32_t fsal_maxlink(struct fsal_staticfsinfo_t *info) { return info->maxlink; } uint32_t fsal_maxnamelen(struct fsal_staticfsinfo_t *info) { return info->maxnamelen; } uint32_t fsal_maxpathlen(struct fsal_staticfsinfo_t *info) { return info->maxpathlen; } struct timespec fsal_lease_time(struct fsal_staticfsinfo_t *info) { return info->lease_time; } fsal_aclsupp_t fsal_acl_support(struct fsal_staticfsinfo_t *info) { return info->acl_support; } attrmask_t fsal_supported_attrs(struct fsal_staticfsinfo_t *info) { return info->supported_attrs; } uint32_t fsal_maxread(struct fsal_staticfsinfo_t *info) { return info->maxread; } uint32_t fsal_maxwrite(struct fsal_staticfsinfo_t *info) { return info->maxwrite; } uint32_t fsal_umask(struct fsal_staticfsinfo_t *info) { return info->umask; } uint32_t fsal_xattr_access_rights(struct fsal_staticfsinfo_t *info) { return info->xattr_access_rights; } /** @} */ nfs-ganesha-2.6.0/src/FSAL/fsal_convert.c000066400000000000000000000240421324272410200200550ustar00rootroot00000000000000/** * @addtogroup FSAL * @{ */ /** * @file FSAL/fsal_convert.c * @brief FSAL type translation functions. */ #include "config.h" #ifdef LINUX #include /* for major(3), minor(3) */ #endif #include #include #include #include #include #include #include "fsal.h" #include "fsal_convert.h" #include "common_utils.h" /** * posix2fsal_error : * Convert POSIX error codes to FSAL error codes. * * \param posix_errorcode (input): * The error code returned from POSIX. * * \return The FSAL error code associated * to posix_errorcode. * */ fsal_errors_t posix2fsal_error(int posix_errorcode) { struct rlimit rlim = { .rlim_cur = RLIM_INFINITY, .rlim_max = RLIM_INFINITY }; switch (posix_errorcode) { case 0: return ERR_FSAL_NO_ERROR; case EPERM: return ERR_FSAL_PERM; case ENOENT: return ERR_FSAL_NOENT; /* connection error */ #ifdef _AIX_5 case ENOCONNECT: #elif defined _LINUX case ECONNREFUSED: case ECONNABORTED: case ECONNRESET: #endif /* IO error */ case EIO: /* too many open files */ case ENFILE: case EMFILE: /* broken pipe */ case EPIPE: /* all shown as IO errors */ if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) { LogInfo(COMPONENT_FSAL, "Mapping %d to ERR_FSAL_IO, getrlimit failed", posix_errorcode); } else { LogInfo(COMPONENT_FSAL, "Mapping %d to ERR_FSAL_IO, rlim_cur=%" PRId64 " rlim_max=%" PRId64, +posix_errorcode, rlim.rlim_cur, rlim.rlim_max); } return ERR_FSAL_IO; /* no such device */ case ENOTTY: case ENODEV: case ENXIO: LogInfo(COMPONENT_FSAL, "Mapping %d to ERR_FSAL_NXIO", posix_errorcode); return ERR_FSAL_NXIO; /* invalid file descriptor : */ case EBADF: /* we suppose it was not opened... */ /** * @todo: The EBADF error also happens when file * is opened for reading, and we try writting in it. * In this case, we return ERR_FSAL_NOT_OPENED, * but it doesn't seems to be a correct error translation. */ return ERR_FSAL_NOT_OPENED; case ENOMEM: case ENOLCK: LogInfo(COMPONENT_FSAL, "Mapping %d to ERR_FSAL_NOMEM", posix_errorcode); return ERR_FSAL_NOMEM; case EACCES: return ERR_FSAL_ACCESS; case EFAULT: return ERR_FSAL_FAULT; case EEXIST: return ERR_FSAL_EXIST; case EXDEV: return ERR_FSAL_XDEV; case ENOTDIR: return ERR_FSAL_NOTDIR; case EISDIR: return ERR_FSAL_ISDIR; case EINVAL: return ERR_FSAL_INVAL; case EROFS: return ERR_FSAL_ROFS; case ETXTBSY: return ERR_FSAL_SHARE_DENIED; case EFBIG: return ERR_FSAL_FBIG; case ENOSPC: return ERR_FSAL_NOSPC; case EMLINK: return ERR_FSAL_MLINK; case EDQUOT: return ERR_FSAL_DQUOT; case ESRCH: /* Returned by quotaclt */ return ERR_FSAL_NO_QUOTA; case ENAMETOOLONG: return ERR_FSAL_NAMETOOLONG; /** * @warning * AIX returns EEXIST where BSD uses ENOTEMPTY; * We want ENOTEMPTY to be interpreted anyway on AIX plateforms. * Thus, we explicitely write its value (87). */ #ifdef _AIX case 87: #else case ENOTEMPTY: case -ENOTEMPTY: #endif return ERR_FSAL_NOTEMPTY; case ESTALE: return ERR_FSAL_STALE; /* Error code that needs a retry */ case EAGAIN: case EBUSY: #ifdef ETIME case ETIME: #endif LogInfo(COMPONENT_FSAL, "Mapping %d to ERR_FSAL_DELAY", posix_errorcode); return ERR_FSAL_DELAY; case ENOTSUP: return ERR_FSAL_NOTSUPP; case EOVERFLOW: return ERR_FSAL_OVERFLOW; case EDEADLK: return ERR_FSAL_DEADLOCK; case EINTR: return ERR_FSAL_INTERRUPT; #ifdef ENODATA case ENODATA: return ERR_FSAL_NO_DATA; #endif case ERANGE: return ERR_FSAL_BAD_RANGE; default: LogCrit(COMPONENT_FSAL, "Mapping %d(default) to ERR_FSAL_SERVERFAULT", posix_errorcode); /* other unexpected errors */ return ERR_FSAL_SERVERFAULT; } } /** * @brief Convert FSAL permission flags to Posix permission flags. * * @param[in] testperm FSAL permission flags to be tested * * @return POSIX permission flags to be tested. */ int fsal2posix_testperm(fsal_accessflags_t testperm) { int posix_testperm = 0; if (testperm & FSAL_R_OK) posix_testperm |= R_OK; if (testperm & FSAL_W_OK) posix_testperm |= W_OK; if (testperm & FSAL_X_OK) posix_testperm |= X_OK; return posix_testperm; } /* mode bits are a uint16_t and chmod masks off type */ #define S_IALLUGO (~S_IFMT & 0xFFFF) /** * @brief Convert FSAL mode to POSIX mode * * @param[in] fsal_mode FSAL mode to be translated * * @return The POSIX mode associated to fsal_mode. */ mode_t fsal2unix_mode(uint32_t fsal_mode) { return fsal_mode & S_IALLUGO; } /** * @brief Convert POSIX mode to FSAL mode * * @param[in] unix_mode POSIX mode to be translated * * @return FSAL mode associated with @c unix_mode */ uint32_t unix2fsal_mode(mode_t unix_mode) { return unix_mode & S_IALLUGO; } /** * @brief Convert POSIX object type to an FSAL object type * * @param[in] posix_type_in POSIX object type * * @retval The FSAL node type associated to @c posix_type_in. * @retval -1 if the input type is unknown. */ object_file_type_t posix2fsal_type(mode_t posix_type_in) { switch (posix_type_in & S_IFMT) { case S_IFIFO: return FIFO_FILE; case S_IFCHR: return CHARACTER_FILE; case S_IFDIR: return DIRECTORY; case S_IFBLK: return BLOCK_FILE; case S_IFREG: case S_IFMT: return REGULAR_FILE; case S_IFLNK: return SYMBOLIC_LINK; case S_IFSOCK: return SOCKET_FILE; default: LogWarn(COMPONENT_FSAL, "Unknown object type: %d", posix_type_in); return -1; } } /** * @brief Convert a stat(2) style dev_t to an FSAL fsid * * @param[in] posix_devid The device id * * @return The FSAL fsid. */ fsal_fsid_t posix2fsal_fsid(dev_t posix_devid) { fsal_fsid_t fsid; fsid.major = major(posix_devid); fsid.minor = minor(posix_devid); return fsid; } /** * @brief Convert a stat(2) style dev_t to an fsal_dev_t * * @param[in] posix_devid The device id * * @return The FSAL device. */ fsal_dev_t posix2fsal_devt(dev_t posix_devid) { fsal_dev_t dev; dev.major = major(posix_devid); dev.minor = minor(posix_devid); return dev; } /** * @brief Convert FSAL open flags to POSIX open flags * * @param[in] fsal_flags FSAL open flags to be translated * @param[out] p_posix_flags POSIX open flags. * * @retval ERR_FSAL_NO_ERROR, no error. * @retval ERR_FSAL_INVAL, invalid or incompatible input flags. */ void fsal2posix_openflags(fsal_openflags_t fsal_flags, int *p_posix_flags) { /* Ignore any flags that are not actually used, there are flags * that are passed to FSAL operations that don't convert to * POSIX open flags, which is fine. */ /* conversion */ *p_posix_flags = 0; if ((fsal_flags & FSAL_O_RDWR) == FSAL_O_RDWR) *p_posix_flags |= O_RDWR; else if ((fsal_flags & FSAL_O_RDWR) == FSAL_O_READ) *p_posix_flags |= O_RDONLY; else if ((fsal_flags & FSAL_O_RDWR) == FSAL_O_WRITE) *p_posix_flags |= O_WRONLY; else if ((fsal_flags & FSAL_O_ANY) != 0) *p_posix_flags |= O_RDONLY; if (fsal_flags & FSAL_O_TRUNC) *p_posix_flags |= O_TRUNC; } /** * @brief Return string for object type * * @param[in] type The FSAL object type * * @return A string naming the type or "unexpected type". */ const char *object_file_type_to_str(object_file_type_t type) { switch (type) { case NO_FILE_TYPE: return "NO_FILE_TYPE"; case REGULAR_FILE: return "REGULAR_FILE"; case CHARACTER_FILE: return "CHARACTER_FILE"; case BLOCK_FILE: return "BLOCK_FILE"; case SYMBOLIC_LINK: return "SYMBOLIC_LINK"; case SOCKET_FILE: return "SOCKET_FILE"; case FIFO_FILE: return "FIFO_FILE"; case DIRECTORY: return "DIRECTORY"; case EXTENDED_ATTR: return "EXTENDED_ATTR"; } return "unexpected type"; } void posix2fsal_attributes_all(const struct stat *buffstat, struct attrlist *fsalattr) { fsalattr->valid_mask |= ATTRS_POSIX; posix2fsal_attributes(buffstat, fsalattr); } /* fsalattr->valid_mask should be set to POSIX attributes that need to * be filled in. buffstat is expected to have those attributes filled in * correctly for converting the attributes from POSIX to FSAL. */ void posix2fsal_attributes(const struct stat *buffstat, struct attrlist *fsalattr) { fsalattr->supported = op_ctx->fsal_export->exp_ops.fs_supported_attrs( op_ctx->fsal_export); /* Fills the output struct */ if (FSAL_TEST_MASK(fsalattr->valid_mask, ATTR_TYPE)) fsalattr->type = posix2fsal_type(buffstat->st_mode); if (FSAL_TEST_MASK(fsalattr->valid_mask, ATTR_SIZE)) fsalattr->filesize = buffstat->st_size; if (FSAL_TEST_MASK(fsalattr->valid_mask, ATTR_FSID)) fsalattr->fsid = posix2fsal_fsid(buffstat->st_dev); if (FSAL_TEST_MASK(fsalattr->valid_mask, ATTR_FILEID)) fsalattr->fileid = (uint64_t)buffstat->st_ino; if (FSAL_TEST_MASK(fsalattr->valid_mask, ATTR_MODE)) fsalattr->mode = unix2fsal_mode(buffstat->st_mode); if (FSAL_TEST_MASK(fsalattr->valid_mask, ATTR_NUMLINKS)) fsalattr->numlinks = buffstat->st_nlink; if (FSAL_TEST_MASK(fsalattr->valid_mask, ATTR_OWNER)) fsalattr->owner = buffstat->st_uid; if (FSAL_TEST_MASK(fsalattr->valid_mask, ATTR_GROUP)) fsalattr->group = buffstat->st_gid; if (FSAL_TEST_MASK(fsalattr->valid_mask, ATTR_ATIME)) { #ifdef FREEBSD fsalattr->atime = buffstat->st_atimespec; #else fsalattr->atime = buffstat->st_atim; #endif } if (FSAL_TEST_MASK(fsalattr->valid_mask, ATTR_CTIME)) { #ifdef FREEBSD fsalattr->ctime = buffstat->st_ctimespec; #else fsalattr->ctime = buffstat->st_ctim; #endif } if (FSAL_TEST_MASK(fsalattr->valid_mask, ATTR_MTIME)) { #ifdef FREEBSD fsalattr->mtime = buffstat->st_mtimespec; #else fsalattr->mtime = buffstat->st_mtim; #endif } if (FSAL_TEST_MASK(fsalattr->valid_mask, ATTR_CHGTIME)) { fsalattr->chgtime = gsh_time_cmp(&fsalattr->mtime, &fsalattr->ctime) > 0 ? fsalattr->mtime : fsalattr->ctime; fsalattr->change = timespec_to_nsecs(&fsalattr->chgtime); } if (FSAL_TEST_MASK(fsalattr->valid_mask, ATTR_SPACEUSED)) fsalattr->spaceused = buffstat->st_blocks * S_BLKSIZE; if (FSAL_TEST_MASK(fsalattr->valid_mask, ATTR_RAWDEV)) fsalattr->rawdev = posix2fsal_devt(buffstat->st_rdev); } /** @} */ nfs-ganesha-2.6.0/src/FSAL/fsal_destroyer.c000066400000000000000000000142551324272410200204220ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) 2011 The Linux Box Corporation * Author: Adam C. Emerson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @addtogroup FSAL * @{ */ #include "gsh_list.h" #include "fsal.h" #include "fsal_api.h" #include "nfs_exports.h" #include "nfs_core.h" #include "fsal_private.h" #include "FSAL/fsal_commonlib.h" /** * @file fsal_destroyer.c * @author Adam C. Emerson * @brief Kill the FSAL with prejudice */ /** * @brief Dispose of lingering file handles * * @param[in] fsal fsal module to clean up */ static void shutdown_handles(struct fsal_module *fsal) { /* Handle iterator */ struct glist_head *hi = NULL; /* Next pointer in handle iteration */ struct glist_head *hn = NULL; if (glist_empty(&fsal->handles)) return; LogDebug(COMPONENT_FSAL, "Extra file handles hanging around."); glist_for_each_safe(hi, hn, &fsal->handles) { struct fsal_obj_handle *h = glist_entry(hi, struct fsal_obj_handle, handles); LogDebug(COMPONENT_FSAL, "Releasing handle"); h->obj_ops.release(h); } } /** * @brief Dispose of lingering DS handles * * @param[in] pds pNFS DS to clean up */ static void shutdown_ds_handles(struct fsal_pnfs_ds *pds) { /* Handle iterator */ struct glist_head *hi = NULL; /* Next pointer in handle iteration */ struct glist_head *hn = NULL; if (glist_empty(&pds->ds_handles)) return; LogDebug(COMPONENT_FSAL, "Extra DS file handles hanging around."); glist_for_each_safe(hi, hn, &pds->ds_handles) { struct fsal_ds_handle *h = glist_entry(hi, struct fsal_ds_handle, ds_handle); int64_t refcount = atomic_fetch_int64_t(&h->refcount); if (refcount != 0) { LogDebug(COMPONENT_FSAL, "Extra references (%"PRIi64") hanging around.", refcount); atomic_store_int64_t(&h->refcount, 0); } h->dsh_ops.release(h); } } /** * @brief Dispose of lingering pNFS Data Servers * * @param[in] fsal fsal module to clean up */ static void shutdown_pnfs_ds(struct fsal_module *fsal) { /* Handle iterator */ struct glist_head *hi = NULL; /* Next pointer in handle iteration */ struct glist_head *hn = NULL; if (glist_empty(&fsal->servers)) return; LogDebug(COMPONENT_FSAL, "Extra pNFS Data Servers hanging around."); glist_for_each_safe(hi, hn, &fsal->servers) { struct fsal_pnfs_ds *h = glist_entry(hi, struct fsal_pnfs_ds, server); int32_t refcount; shutdown_ds_handles(h); refcount = atomic_fetch_int32_t(&h->refcount); if (refcount != 0) { LogDebug(COMPONENT_FSAL, "Extra references (%"PRIi32") hanging around.", refcount); atomic_store_int32_t(&h->refcount, 0); } h->s_ops.release(h); } } /** * @brief Shut down an individual export * * @param[in] export The export to shut down */ static void shutdown_export(struct fsal_export *export) { struct fsal_module *fsal = export->fsal; LogDebug(COMPONENT_FSAL, "Releasing export"); export->exp_ops.release(export); fsal_put(fsal); LogFullDebug(COMPONENT_FSAL, "FSAL %s refcount %"PRIu32, fsal->name, atomic_fetch_int32_t(&fsal->refcount)); } /** * @brief Destroy FSALs */ void destroy_fsals(void) { /* Module iterator */ struct glist_head *mi = NULL; /* Next module */ struct glist_head *mn = NULL; glist_for_each_safe(mi, mn, &fsal_list) { /* The module to destroy */ struct fsal_module *m = glist_entry(mi, struct fsal_module, fsals); /* Iterator over exports */ struct glist_head *ei = NULL; /* Next export */ struct glist_head *en = NULL; int32_t refcount = atomic_fetch_int32_t(&m->refcount); LogEvent(COMPONENT_FSAL, "Shutting down handles for FSAL %s", m->name); shutdown_handles(m); LogEvent(COMPONENT_FSAL, "Shutting down DS handles for FSAL %s", m->name); shutdown_pnfs_ds(m); LogEvent(COMPONENT_FSAL, "Shutting down exports for FSAL %s", m->name); glist_for_each_safe(ei, en, &m->exports) { /* The module to destroy */ struct fsal_export *e = glist_entry(ei, struct fsal_export, exports); shutdown_export(e); } LogEvent(COMPONENT_FSAL, "Exports for FSAL %s shut down", m->name); if (refcount != 0) { LogCrit(COMPONENT_FSAL, "Extra references (%"PRIi32 ") hanging around to FSAL %s", refcount, m->name); /** * @todo Forcibly blowing away all references * should work fine on files and objects if * we're shutting down, however it will cause * trouble once we have stackable FSALs. As a * practical matter, though, if the system is * working properly we shouldn't even reach * this point. */ atomic_store_int32_t(&m->refcount, 0); } if (m->dl_handle) { int rc = 0; char *fsal_name = gsh_strdup(m->name); LogEvent(COMPONENT_FSAL, "Unloading FSAL %s", fsal_name); rc = m->m_ops.unload(m); if (rc != 0) { LogMajor(COMPONENT_FSAL, "Unload of %s failed with error %d", fsal_name, rc); } LogEvent(COMPONENT_FSAL, "FSAL %s unloaded", fsal_name); gsh_free(fsal_name); } } release_posix_file_systems(); } /** * @brief Emergency Halt FSALs */ void emergency_cleanup_fsals(void) { /* Module iterator */ struct glist_head *mi = NULL; /* Next module */ struct glist_head *mn = NULL; glist_for_each_safe(mi, mn, &fsal_list) { /* The module to destroy */ struct fsal_module *m = glist_entry(mi, struct fsal_module, fsals); m->m_ops.emergency_cleanup(); } } /** @} */ nfs-ganesha-2.6.0/src/FSAL/fsal_helper.c000066400000000000000000001373271324272410200176670ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) CohrotFS Inc., 2015 * Author: Daniel Gryniewicz * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @addtogroup FSAL * @{ */ /** * @file fsal_helper.c * @author Daniel Gryniewicz * @brief FSAL helper for clients */ #include "config.h" #include #include #include #include #include #include "log.h" #include "fsal.h" #include "nfs_convert.h" #include "nfs_exports.h" #include "nfs4_acls.h" #include "sal_data.h" #include "sal_functions.h" #include "FSAL/fsal_commonlib.h" /** * This is a global counter of files opened. * * This is preliminary expected to go away. Problems with this method are that * it overcounts file descriptors for FSALs that don't use them for open files, * and, under the Lieb Rearchitecture, FSALs will be responsible for caching * their own file descriptors, with interfaces for Cache_Inode to interrogate * them as to usage or instruct them to close them. */ size_t open_fd_count; static bool fsal_not_in_group_list(gid_t gid) { const struct user_cred *creds = op_ctx->creds; int i; if (creds->caller_gid == gid) { LogDebug(COMPONENT_FSAL, "User %u is has active group %u", creds->caller_uid, gid); return false; } for (i = 0; i < creds->caller_glen; i++) { if (creds->caller_garray[i] == gid) { LogDebug(COMPONENT_FSAL, "User %u is member of group %u", creds->caller_uid, gid); return false; } } LogDebug(COMPONENT_FSAL, "User %u IS NOT member of group %u", creds->caller_uid, gid); return true; } /** * @brief Check permissions on opening a file. * * @param[in] obj The file being opened * @param[in] openflags The access reqested on opening the file * @param[in] exclusive_create Indicates the file is being exclusive create * @param[out] reason Description of why the access failed. * * @returns Status of the permission check. */ static fsal_status_t check_open_permission(struct fsal_obj_handle *obj, fsal_openflags_t openflags, bool exclusive_create, char **reason) { fsal_status_t status = {0, 0}; fsal_accessflags_t access_mask = 0; if (openflags & FSAL_O_READ) access_mask |= FSAL_READ_ACCESS; if (openflags & FSAL_O_WRITE) access_mask |= FSAL_WRITE_ACCESS; /* Ask for owner_skip on exclusive create (we will be checking the * verifier later, so this allows a replay of * open("foo", O_RDWR | O_CREAT | O_EXCL, 0) to succeed). */ status = obj->obj_ops.test_access(obj, access_mask, NULL, NULL, exclusive_create); if (!FSAL_IS_ERROR(status)) { *reason = ""; return status; } /* If non-permission error, return it. */ if (status.major != ERR_FSAL_PERM) { *reason = "fsal_access failed - "; return status; } /* If WRITE access is requested, return permission * error */ if (openflags & FSAL_O_WRITE) { *reason = "fsal_access failed with WRITE_ACCESS - "; return status; } /* If just a permission error and file was opened read * only, try execute permission. * * NOTE: We don't do anything special for exclusive create here, if an * exclusive create replay failed the above permission check, it * presumably is no longer exclusively the creator of the file * because somehow the owner changed. * */ status = fsal_access(obj, FSAL_EXECUTE_ACCESS); if (!FSAL_IS_ERROR(status)) *reason = ""; else *reason = "fsal_access failed with EXECUTE_ACCESS - "; return status; } /** * @brief Checks permissions on an entry for setattrs * * This function checks if the supplied credentials are sufficient to perform * the required setattrs. * * @param[in] obj The file to be checked * @param[in] attr Attributes to set * @param[in] current Current attributes for object * * @return FSAL status */ static fsal_status_t fsal_check_setattr_perms(struct fsal_obj_handle *obj, struct attrlist *attr, struct attrlist *current) { fsal_status_t status = {0, 0}; fsal_accessflags_t access_check = 0; bool not_owner; char *note = ""; const struct user_cred *creds = op_ctx->creds; /* Shortcut, if current user is root, then we can just bail out with * success. */ if (op_ctx->fsal_export->exp_ops.is_superuser(op_ctx->fsal_export, creds)) { note = " (Ok for root user)"; goto out; } fsal_prepare_attrs(current, op_ctx->fsal_export->exp_ops.fs_supported_attrs( op_ctx->fsal_export) & (ATTRS_CREDS | ATTR_MODE | ATTR_ACL)); status = obj->obj_ops.getattrs(obj, current); if (FSAL_IS_ERROR(status)) return status; not_owner = (creds->caller_uid != current->owner); /* Only ownership change need to be checked for owner */ if (FSAL_TEST_MASK(attr->valid_mask, ATTR_OWNER)) { /* non-root is only allowed to "take ownership of file" */ if (attr->owner != creds->caller_uid) { status = fsalstat(ERR_FSAL_PERM, 0); note = " (new OWNER was not user)"; goto out; } /* Owner of file will always be able to "change" the owner to * himself. */ if (not_owner) { access_check |= FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_WRITE_OWNER); LogDebug(COMPONENT_FSAL, "Change OWNER requires FSAL_ACE_PERM_WRITE_OWNER"); } } /* Check if we are changing the owner_group, if owner_group is passed, * but is the current owner_group, then that will be considered a * NO-OP and allowed IF the caller is the owner of the file. */ if (FSAL_TEST_MASK(attr->valid_mask, ATTR_GROUP) && (attr->group != current->group || not_owner)) { /* non-root is only allowed to change group_owner to a group * user is a member of. */ int not_in_group = fsal_not_in_group_list(attr->group); if (not_in_group) { status = fsalstat(ERR_FSAL_PERM, 0); note = " (user is not member of new GROUP)"; goto out; } /* Owner is always allowed to change the group_owner of a file * to a group they are a member of. */ if (not_owner) { access_check |= FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_WRITE_OWNER); LogDebug(COMPONENT_FSAL, "Change GROUP requires FSAL_ACE_PERM_WRITE_OWNER"); } } /* Any attribute after this is always changeable by the owner. * And the above attributes have already been validated as a valid * change for the file owner to make. Note that the owner may be * setting ATTR_OWNER but at this point it MUST be to himself, and * thus is no-op and does not need FSAL_ACE_PERM_WRITE_OWNER. */ if (!not_owner) { note = " (Ok for owner)"; goto out; } if (FSAL_TEST_MASK(attr->valid_mask, ATTR_MODE) || FSAL_TEST_MASK(attr->valid_mask, ATTR_ACL)) { /* Changing mode or ACL requires ACE4_WRITE_ACL */ access_check |= FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_WRITE_ACL); LogDebug(COMPONENT_FSAL, "Change MODE or ACL requires FSAL_ACE_PERM_WRITE_ACL"); } if (FSAL_TEST_MASK(attr->valid_mask, ATTR_SIZE)) { /* Changing size requires owner or write permission */ /** @todo: does FSAL_ACE_PERM_APPEND_DATA allow enlarging the file? */ access_check |= FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_WRITE_DATA); LogDebug(COMPONENT_FSAL, "Change SIZE requires FSAL_ACE_PERM_WRITE_DATA"); } /* Check if just setting atime and mtime to "now" */ if ((FSAL_TEST_MASK(attr->valid_mask, ATTR_MTIME_SERVER) || FSAL_TEST_MASK(attr->valid_mask, ATTR_ATIME_SERVER)) && !FSAL_TEST_MASK(attr->valid_mask, ATTR_MTIME) && !FSAL_TEST_MASK(attr->valid_mask, ATTR_ATIME)) { /* If either atime and/or mtime are set to "now" then need only * have write permission. * * Technically, client should not send atime updates, but if * they really do, we'll let them to make the perm check a bit * simpler. */ access_check |= FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_WRITE_DATA); LogDebug(COMPONENT_FSAL, "Change ATIME and MTIME to NOW requires FSAL_ACE_PERM_WRITE_DATA"); } else if (FSAL_TEST_MASK(attr->valid_mask, ATTR_MTIME_SERVER) || FSAL_TEST_MASK(attr->valid_mask, ATTR_ATIME_SERVER) || FSAL_TEST_MASK(attr->valid_mask, ATTR_MTIME) || FSAL_TEST_MASK(attr->valid_mask, ATTR_ATIME)) { /* Any other changes to atime or mtime require owner, root, or * ACES4_WRITE_ATTRIBUTES. * * NOTE: we explicity do NOT check for update of atime only to * "now". Section 10.6 of both RFC 3530 and RFC 5661 document * the reasons clients should not do atime updates. */ access_check |= FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_WRITE_ATTR); LogDebug(COMPONENT_FSAL, "Change ATIME and/or MTIME requires FSAL_ACE_PERM_WRITE_ATTR"); } if (isDebug(COMPONENT_FSAL) || isDebug(COMPONENT_NFS_V4_ACL)) { char *need_write_owner = ""; char *need_write_acl = ""; char *need_write_data = ""; char *need_write_attr = ""; if (access_check & FSAL_ACE_PERM_WRITE_OWNER) need_write_owner = " WRITE_OWNER"; if (access_check & FSAL_ACE_PERM_WRITE_ACL) need_write_acl = " WRITE_ACL"; if (access_check & FSAL_ACE_PERM_WRITE_DATA) need_write_data = " WRITE_DATA"; if (access_check & FSAL_ACE_PERM_WRITE_ATTR) need_write_attr = " WRITE_ATTR"; LogDebug(COMPONENT_FSAL, "Requires %s%s%s%s", need_write_owner, need_write_acl, need_write_data, need_write_attr); } if (current->acl) { status = obj->obj_ops.test_access(obj, access_check, NULL, NULL, false); note = " (checked ACL)"; goto out; } if (access_check != FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_WRITE_DATA)) { /* Without an ACL, this user is not allowed some operation */ status = fsalstat(ERR_FSAL_PERM, 0); note = " (no ACL to check)"; goto out; } status = obj->obj_ops.test_access(obj, FSAL_W_OK, NULL, NULL, false); note = " (checked mode)"; out: if (FSAL_IS_ERROR(status)) { /* Done with the current attrs, caller will not expect them. */ fsal_release_attrs(current); } LogDebug(COMPONENT_FSAL, "Access check returned %s%s", fsal_err_txt(status), note); return status; } fsal_status_t open2_by_name(struct fsal_obj_handle *in_obj, struct state_t *state, fsal_openflags_t openflags, enum fsal_create_mode createmode, const char *name, struct attrlist *attr, fsal_verifier_t verifier, struct fsal_obj_handle **obj, struct attrlist *attrs_out) { fsal_status_t status = { 0, 0 }; fsal_status_t close_status = { 0, 0 }; bool caller_perm_check = false; char *reason; *obj = NULL; if (name == NULL) return fsalstat(ERR_FSAL_INVAL, 0); if (in_obj->type != DIRECTORY) return fsalstat(ERR_FSAL_INVAL, 0); if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { /* Can't open "." or ".."... */ return fsalstat(ERR_FSAL_ISDIR, 0); } /* Check directory permission for LOOKUP */ status = fsal_access(in_obj, FSAL_EXECUTE_ACCESS); if (FSAL_IS_ERROR(status)) return status; status = in_obj->obj_ops.open2(in_obj, state, openflags, createmode, name, attr, verifier, obj, attrs_out, &caller_perm_check); if (FSAL_IS_ERROR(status)) { LogFullDebug(COMPONENT_FSAL, "FSAL %d %s returned %s", (int) op_ctx->ctx_export->export_id, op_ctx->ctx_export->fullpath, fsal_err_txt(status)); return status; } if (!state) { (void) atomic_inc_size_t(&open_fd_count); } LogFullDebug(COMPONENT_FSAL, "Created entry %p FSAL %s for %s", *obj, (*obj)->fsal->name, name); if (!caller_perm_check) return status; /* Do a permission check on the just opened file. */ status = check_open_permission(*obj, openflags, createmode >= FSAL_EXCLUSIVE, &reason); if (!FSAL_IS_ERROR(status)) return status; LogDebug(COMPONENT_FSAL, "Closing file check_open_permission failed %s-%s", reason, fsal_err_txt(status)); if (state != NULL) close_status = (*obj)->obj_ops.close2(*obj, state); else close_status = fsal_close(*obj); if (FSAL_IS_ERROR(close_status)) { /* Just log but don't return this error (we want to * preserve the error that got us here). */ LogDebug(COMPONENT_FSAL, "FSAL close2 failed with %s", fsal_err_txt(close_status)); } return status; } /** * @brief Set attributes on a file * * The new attributes are copied over @a attr on success. * * The caller is expected to invoke fsal_release_attrs to release any * resources held by the set attributes. The FSAL layer MAY have added an * inherited ACL. * * @param[in] obj File to set attributes on * @param[in] bypass Bypass share reservation checking * @param[in] state Possible state associated with the entry * @param[in,out] attr Attributes to set * @return FSAL status */ fsal_status_t fsal_setattr(struct fsal_obj_handle *obj, bool bypass, struct state_t *state, struct attrlist *attr) { fsal_status_t status = { 0, 0 }; const struct user_cred *creds = op_ctx->creds; struct attrlist current; bool is_superuser; if ((attr->valid_mask & (ATTR_SIZE | ATTR4_SPACE_RESERVED)) && (obj->type != REGULAR_FILE)) { LogWarn(COMPONENT_FSAL, "Attempt to truncate non-regular file: type=%d", obj->type); return fsalstat(ERR_FSAL_BADTYPE, 0); } /* Is it allowed to change times ? */ if (!op_ctx->fsal_export->exp_ops.fs_supports(op_ctx->fsal_export, fso_cansettime) && (FSAL_TEST_MASK (attr->valid_mask, (ATTR_ATIME | ATTR_CREATION | ATTR_CTIME | ATTR_MTIME)))) return fsalstat(ERR_FSAL_INVAL, 0); /* Do permission checks, which returns with the attributes for the * object if the caller is not root. */ status = fsal_check_setattr_perms(obj, attr, ¤t); if (FSAL_IS_ERROR(status)) return status; is_superuser = op_ctx->fsal_export->exp_ops.is_superuser( op_ctx->fsal_export, creds); /* Test for the following condition from chown(2): * * When the owner or group of an executable file are changed by an * unprivileged user the S_ISUID and S_ISGID mode bits are cleared. * POSIX does not specify whether this also should happen when * root does the chown(); the Linux behavior depends on the kernel * version. In case of a non-group-executable file (i.e., one for * which the S_IXGRP bit is not set) the S_ISGID bit indicates * mandatory locking, and is not cleared by a chown(). * */ if (!is_superuser && (FSAL_TEST_MASK(attr->valid_mask, ATTR_OWNER) || FSAL_TEST_MASK(attr->valid_mask, ATTR_GROUP)) && ((current.mode & (S_IXOTH | S_IXUSR | S_IXGRP)) != 0) && ((current.mode & (S_ISUID | S_ISGID)) != 0)) { /* Non-priviledged user changing ownership on an executable * file with S_ISUID or S_ISGID bit set, need to be cleared. */ if (!FSAL_TEST_MASK(attr->valid_mask, ATTR_MODE)) { /* Mode wasn't being set, so set it now, start with * the current attributes. */ attr->mode = current.mode; FSAL_SET_MASK(attr->valid_mask, ATTR_MODE); } /* Don't clear S_ISGID if the file isn't group executable. * In that case, S_ISGID indicates mandatory locking and * is not cleared by chown. */ if ((current.mode & S_IXGRP) != 0) attr->mode &= ~S_ISGID; /* Clear S_ISUID. */ attr->mode &= ~S_ISUID; } /* Test for the following condition from chmod(2): * * If the calling process is not privileged (Linux: does not have * the CAP_FSETID capability), and the group of the file does not * match the effective group ID of the process or one of its * supplementary group IDs, the S_ISGID bit will be turned off, * but this will not cause an error to be returned. * * We test the actual mode being set before testing for group * membership since that is a bit more expensive. */ if (!is_superuser && FSAL_TEST_MASK(attr->valid_mask, ATTR_MODE) && (attr->mode & S_ISGID) != 0 && fsal_not_in_group_list(current.group)) { /* Clear S_ISGID */ attr->mode &= ~S_ISGID; } status = obj->obj_ops.setattr2(obj, bypass, state, attr); if (FSAL_IS_ERROR(status)) { if (status.major == ERR_FSAL_STALE) { LogEvent(COMPONENT_FSAL, "FSAL returned STALE from setattr2"); } return status; } if (!is_superuser) { /* Done with the current attrs */ fsal_release_attrs(¤t); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Read the contents of a symlink * * @param[in] obj Symlink to read * @param[out] link_content Buffer to fill with link contents * @return FSAL status */ fsal_status_t fsal_readlink(struct fsal_obj_handle *obj, struct gsh_buffdesc *link_content) { if (obj->type != SYMBOLIC_LINK) return fsalstat(ERR_FSAL_BADTYPE, 0); /* Never refresh. FSAL_MDCACHE will override for cached FSALs. */ return obj->obj_ops.readlink(obj, link_content, false); } /** * * @brief Links a new name to a file * * This function hard links a new name to an existing file. * * @param[in] obj The file to which to add the new name. Must * not be a directory. * @param[in] dest_dir The directory in which to create the new name * @param[in] name The new name to add to the file * * @return FSAL status * in destination. */ fsal_status_t fsal_link(struct fsal_obj_handle *obj, struct fsal_obj_handle *dest_dir, const char *name) { fsal_status_t status = { 0, 0 }; /* The file to be hardlinked can't be a DIRECTORY */ if (obj->type == DIRECTORY) return fsalstat(ERR_FSAL_BADTYPE, 0); /* Is the destination a directory? */ if (dest_dir->type != DIRECTORY) return fsalstat(ERR_FSAL_NOTDIR, 0); /* Must be the same FS */ if (obj->fs != dest_dir->fs) return fsalstat(ERR_FSAL_XDEV, 0); if (!op_ctx->fsal_export->exp_ops.fs_supports( op_ctx->fsal_export, fso_link_supports_permission_checks)) { status = fsal_access(dest_dir, FSAL_MODE_MASK_SET(FSAL_W_OK) | FSAL_MODE_MASK_SET(FSAL_X_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_EXECUTE) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_ADD_FILE)); if (FSAL_IS_ERROR(status)) return status; } /* Rather than performing a lookup first, just try to make the link and return the FSAL's error if it fails. */ status = obj->obj_ops.link(obj, dest_dir, name); return status; } /** * @brief Look up a name in a directory * * @param[in] parent Handle for the parent directory to be managed. * @param[in] name Name of the file that we are looking up. * @param[out] obj Found file * * @note On success, @a handle has been ref'd * * @return FSAL status */ fsal_status_t fsal_lookup(struct fsal_obj_handle *parent, const char *name, struct fsal_obj_handle **obj, struct attrlist *attrs_out) { fsal_status_t fsal_status = { 0, 0 }; fsal_accessflags_t access_mask = (FSAL_MODE_MASK_SET(FSAL_X_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_EXECUTE)); *obj = NULL; if (parent->type != DIRECTORY) { *obj = NULL; return fsalstat(ERR_FSAL_NOTDIR, 0); } fsal_status = fsal_access(parent, access_mask); if (FSAL_IS_ERROR(fsal_status)) return fsal_status; if (strcmp(name, ".") == 0) { parent->obj_ops.get_ref(parent); *obj = parent; return get_optional_attrs(*obj, attrs_out); } else if (strcmp(name, "..") == 0) return fsal_lookupp(parent, obj, attrs_out); return parent->obj_ops.lookup(parent, name, obj, attrs_out); } /** * @brief Look up a directory's parent * * @param[in] obj File whose parent is to be obtained. * @param[out] parent Parent directory * * @return FSAL status */ fsal_status_t fsal_lookupp(struct fsal_obj_handle *obj, struct fsal_obj_handle **parent, struct attrlist *attrs_out) { *parent = NULL; /* Never even think of calling FSAL_lookup on root/.. */ if (obj->type == DIRECTORY) { fsal_status_t status = {0, 0}; struct fsal_obj_handle *root_obj = NULL; status = nfs_export_get_root_entry(op_ctx->ctx_export, &root_obj); if (FSAL_IS_ERROR(status)) return status; if (obj == root_obj) { /* This entry is the root of the current export, so if * we get this far, return itself. Note that NFS v4 * LOOKUPP will not come here, it catches the root entry * earlier. */ *parent = obj; if (attrs_out != NULL) { /* Need to return the attributes of the * current object. */ return obj->obj_ops.getattrs(obj, attrs_out); } else { /* Success */ return fsalstat(ERR_FSAL_NO_ERROR, 0); } } else { /* Return entry from nfs_export_get_root_entry */ root_obj->obj_ops.put_ref(root_obj); } } return obj->obj_ops.lookup(obj, "..", parent, attrs_out); } /** * @brief Set the create verifier * * This function sets the mtime/atime attributes according to the create * verifier * * @param[in] sattr attrlist to be managed. * @param[in] verf_hi High long of verifier * @param[in] verf_lo Low long of verifier * */ void fsal_create_set_verifier(struct attrlist *sattr, uint32_t verf_hi, uint32_t verf_lo) { sattr->atime.tv_sec = verf_hi; sattr->atime.tv_nsec = 0; FSAL_SET_MASK(sattr->valid_mask, ATTR_ATIME); sattr->mtime.tv_sec = verf_lo; sattr->mtime.tv_nsec = 0; FSAL_SET_MASK(sattr->valid_mask, ATTR_MTIME); } /** * @brief Creates an object in a directory * * This function creates an entry in the FSAL. If the @a name exists, the * returned error is ERR_FSAL_EXIST, and @a obj is set if the existing object * has the same type as the requested one. * * The caller is expected to set the mode. Any other specified attributes * will also be set. * * The caller is expected to invoke fsal_release_attrs to release any * resources held by the set attributes. The FSAL layer MAY have added an * inherited ACL. * * @param[in] parent Parent directory * @param[in] name Name of the object to create * @param[in] type Type of the object to create * @param[in] attrs Attributes to be used at file creation * @param[in] link_content Contents for symlink * @param[out] obj Created file * * @note On success, @a obj has been ref'd * * @return FSAL status */ fsal_status_t fsal_create(struct fsal_obj_handle *parent, const char *name, object_file_type_t type, struct attrlist *attrs, const char *link_content, struct fsal_obj_handle **obj, struct attrlist *attrs_out) { fsal_status_t status = { 0, 0 }; attrmask_t orig_mask = attrs->valid_mask; if ((type != REGULAR_FILE) && (type != DIRECTORY) && (type != SYMBOLIC_LINK) && (type != SOCKET_FILE) && (type != FIFO_FILE) && (type != CHARACTER_FILE) && (type != BLOCK_FILE)) { status = fsalstat(ERR_FSAL_BADTYPE, 0); LogFullDebug(COMPONENT_FSAL, "create failed because of bad type"); *obj = NULL; goto out; } /* For support_ex API, turn off owner and/or group attr * if they are the same as the credentials. */ if ((attrs->valid_mask & ATTR_OWNER) && attrs->owner == op_ctx->creds->caller_uid) FSAL_UNSET_MASK(attrs->valid_mask, ATTR_OWNER); if ((attrs->valid_mask & ATTR_GROUP) && attrs->group == op_ctx->creds->caller_gid) FSAL_UNSET_MASK(attrs->valid_mask, ATTR_GROUP); /* Permission checking will be done by the FSAL operation. */ /* Try to create it first */ switch (type) { case REGULAR_FILE: status = fsal_open2(parent, NULL, FSAL_O_RDWR, FSAL_UNCHECKED, name, attrs, NULL, obj, attrs_out); if (FSAL_IS_SUCCESS(status)) { /* Close it again; this is just a create */ (void)fsal_close(*obj); } break; case DIRECTORY: status = parent->obj_ops.mkdir(parent, name, attrs, obj, attrs_out); break; case SYMBOLIC_LINK: status = parent->obj_ops.symlink(parent, name, link_content, attrs, obj, attrs_out); break; case SOCKET_FILE: case FIFO_FILE: case BLOCK_FILE: case CHARACTER_FILE: status = parent->obj_ops.mknode(parent, name, type, attrs, obj, attrs_out); break; case NO_FILE_TYPE: case EXTENDED_ATTR: /* we should never go there */ status = fsalstat(ERR_FSAL_BADTYPE, 0); *obj = NULL; LogFullDebug(COMPONENT_FSAL, "create failed because inconsistent entry"); goto out; } /* Check for the result */ if (FSAL_IS_ERROR(status)) { if (status.major == ERR_FSAL_STALE) { LogEvent(COMPONENT_FSAL, "FSAL returned STALE on create type %d", type); } else if (status.major == ERR_FSAL_EXIST) { /* Already exists. Check if type if correct */ status = fsal_lookup(parent, name, obj, attrs_out); if (*obj != NULL) { status = fsalstat(ERR_FSAL_EXIST, 0); LogFullDebug(COMPONENT_FSAL, "create failed because it already exists"); if ((*obj)->type != type) { /* Incompatible types, returns NULL */ (*obj)->obj_ops.put_ref((*obj)); *obj = NULL; goto out; } if ((type == REGULAR_FILE) && (attrs->valid_mask & ATTR_SIZE) && attrs->filesize == 0) { attrs->valid_mask &= ATTR_SIZE; goto out; } } } else { *obj = NULL; } goto out; } out: /* Restore original mask so caller isn't bamboozled... */ attrs->valid_mask = orig_mask; LogFullDebug(COMPONENT_FSAL, "Returning obj=%p status=%s for %s FSAL=%s", *obj, fsal_err_txt(status), name, parent->fsal->name); return status; } /** * @brief Return true if create verifier matches * * This function returns true if the create verifier matches * * @param[in] obj File to be managed. * @param[in] verf_hi High long of verifier * @param[in] verf_lo Low long of verifier * * @return true if verified, false otherwise * */ bool fsal_create_verify(struct fsal_obj_handle *obj, uint32_t verf_hi, uint32_t verf_lo) { /* True if the verifier matches */ bool verified = false; struct attrlist attrs; fsal_prepare_attrs(&attrs, ATTR_ATIME | ATTR_MTIME); obj->obj_ops.getattrs(obj, &attrs); if (FSAL_TEST_MASK(attrs.valid_mask, ATTR_ATIME) && FSAL_TEST_MASK(attrs.valid_mask, ATTR_MTIME) && attrs.atime.tv_sec == verf_hi && attrs.mtime.tv_sec == verf_lo) verified = true; /* Done with the attrs */ fsal_release_attrs(&attrs); return verified; } /** * @brief New style reads * * @param[in] obj File to be read or written * @param[in] bypass If state doesn't indicate a share reservation, * bypass any deny read * @param[in] state state_t associated with the operation * @param[in] offset Absolute file position for I/O * @param[in] io_size Amount of data to be read or written * @param[out] bytes_moved The length of data successfuly read * @param[in,out] buffer Where in memory to read data * @param[out] eof Whether a READ encountered the end of file * @param[in] info io_info for READ_PLUS * * @return FSAL status */ fsal_status_t fsal_read2(struct fsal_obj_handle *obj, bool bypass, struct state_t *state, uint64_t offset, size_t io_size, size_t *bytes_moved, void *buffer, bool *eof, struct io_info *info) { /* Error return from FSAL calls */ fsal_status_t status = { 0, 0 }; status = obj->obj_ops.read2(obj, bypass, state, offset, io_size, buffer, bytes_moved, eof, info); /* Fixup FSAL_SHARE_DENIED status */ if (status.major == ERR_FSAL_SHARE_DENIED) status = fsalstat(ERR_FSAL_LOCKED, 0); LogFullDebug(COMPONENT_FSAL, "FSAL READ operation returned %s, asked_size=%zu, effective_size=%zu", fsal_err_txt(status), io_size, *bytes_moved); if (FSAL_IS_ERROR(status)) { *bytes_moved = 0; return status; } LogFullDebug(COMPONENT_FSAL, "inode/direct: io_size=%zu, bytes_moved=%zu, offset=%" PRIu64, io_size, *bytes_moved, offset); if (!*eof) { /** @todo FSF: add a config option for this behavior? */ /* * NFS requires to set the EOF flag for all reads that * reach the EOF, i.e., even the ones returning data. * Most FSALs don't set the flag in this case. The only * client that cares about this is ESXi. Other clients * will just see a short read and continue reading and then * get the EOF flag as 0 bytes are returned. */ struct attrlist attrs; fsal_prepare_attrs(&attrs, ATTR_SIZE); if (FSAL_IS_SUCCESS(obj->obj_ops.getattrs(obj, &attrs))) *eof = (offset + *bytes_moved) >= attrs.filesize; fsal_release_attrs(&attrs); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief New style writes * * @param[in] obj File to be read or written * @param[in] bypass If state doesn't indicate a share reservation, * bypass any non-mandatory deny write * @param[in] state state_t associated with the operation * @param[in] offset Absolute file position for I/O * @param[in] io_size Amount of data to be written * @param[out] bytes_moved The length of data successfuly written * @param[in,out] buffer Where in memory to write data * @param[in] sync Whether the write is synchronous or not * @param[in] info io_info for WRITE_PLUS * * @return FSAL status */ fsal_status_t fsal_write2(struct fsal_obj_handle *obj, bool bypass, struct state_t *state, uint64_t offset, size_t io_size, size_t *bytes_moved, void *buffer, bool *sync, struct io_info *info) { /* Error return from FSAL calls */ fsal_status_t status = { 0, 0 }; if (op_ctx->export_perms->options & EXPORT_OPTION_COMMIT) { /* Force sync if export requires it */ *sync = true; } status = obj->obj_ops.write2(obj, bypass, state, offset, io_size, buffer, bytes_moved, sync, info); /* Fixup ERR_FSAL_SHARE_DENIED status */ if (status.major == ERR_FSAL_SHARE_DENIED) status = fsalstat(ERR_FSAL_LOCKED, 0); LogFullDebug(COMPONENT_FSAL, "FSAL WRITE operation returned %s, asked_size=%zu, effective_size=%zu", fsal_err_txt(status), io_size, *bytes_moved); if (FSAL_IS_ERROR(status)) { *bytes_moved = 0; return status; } LogFullDebug(COMPONENT_FSAL, "inode/direct: io_size=%zu, bytes_moved=%zu, offset=%" PRIu64, io_size, *bytes_moved, offset); return fsalstat(ERR_FSAL_NO_ERROR, 0); } struct fsal_populate_cb_state { struct fsal_obj_handle *directory; fsal_status_t *status; helper_readdir_cb cb; fsal_cookie_t last_cookie; enum cb_state cb_state; unsigned int *cb_nfound; attrmask_t attrmask; struct fsal_readdir_cb_parms cb_parms; }; static enum fsal_dir_result populate_dirent(const char *name, struct fsal_obj_handle *obj, struct attrlist *attrs, void *dir_state, fsal_cookie_t cookie) { struct fsal_populate_cb_state *state = (struct fsal_populate_cb_state *)dir_state; fsal_status_t status = {0, 0}; enum fsal_dir_result retval; retval = DIR_CONTINUE; state->cb_parms.name = name; status.major = state->cb(&state->cb_parms, obj, attrs, attrs->fileid, cookie, state->cb_state); if (status.major == ERR_FSAL_CROSS_JUNCTION) { struct fsal_obj_handle *junction_obj; struct gsh_export *junction_export = NULL; struct fsal_export *saved_export; struct attrlist attrs2; PTHREAD_RWLOCK_rdlock(&obj->state_hdl->state_lock); /* Get a reference to the junction_export and remember it * only if the junction export is valid. */ if (obj->state_hdl->dir.junction_export != NULL && export_ready(obj->state_hdl->dir.junction_export)) { get_gsh_export_ref(obj->state_hdl->dir.junction_export); junction_export = obj->state_hdl->dir.junction_export; } PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); /* Get the root of the export across the junction. */ if (junction_export != NULL) { status = nfs_export_get_root_entry(junction_export, &junction_obj); if (FSAL_IS_ERROR(status)) { LogMajor(COMPONENT_FSAL, "Failed to get root for %s, id=%d, status = %s", junction_export->fullpath, junction_export->export_id, fsal_err_txt(status)); /* Need to signal problem to callback */ state->cb_state = CB_PROBLEM; (void) state->cb(&state->cb_parms, NULL, NULL, 0, cookie, state->cb_state); /* Protocol layers NEVER do readahead. */ retval = DIR_TERMINATE; put_gsh_export(junction_export); goto out; } } else { LogMajor(COMPONENT_FSAL, "A junction became stale"); /* Need to signal problem to callback */ state->cb_state = CB_PROBLEM; (void) state->cb(&state->cb_parms, NULL, NULL, 0, cookie, state->cb_state); /* Protocol layers NEVER do readahead. */ retval = DIR_TERMINATE; goto out; } /* Now we need to get the cross-junction attributes. */ saved_export = op_ctx->fsal_export; op_ctx->fsal_export = junction_export->fsal_export; fsal_prepare_attrs(&attrs2, op_ctx->fsal_export->exp_ops .fs_supported_attrs(op_ctx->fsal_export) | ATTR_RDATTR_ERR); status = junction_obj->obj_ops.getattrs(junction_obj, &attrs2); if (!FSAL_IS_ERROR(status)) { /* Now call the callback again with that. */ state->cb_state = CB_JUNCTION; status.major = state->cb(&state->cb_parms, junction_obj, &attrs2, junction_export ->exp_mounted_on_file_id, cookie, state->cb_state); state->cb_state = CB_ORIGINAL; } fsal_release_attrs(&attrs2); /* Release our refs */ op_ctx->fsal_export = saved_export; junction_obj->obj_ops.put_ref(junction_obj); put_gsh_export(junction_export); } if (!state->cb_parms.in_result) { /* Protocol layers NEVER do readahead. */ retval = DIR_TERMINATE; goto out; } (*state->cb_nfound)++; out: /* Put the ref on obj that readdir took */ obj->obj_ops.put_ref(obj); return retval; } /** * @brief Reads a directory * * This function iterates over the directory entries and invokes a supplied * callback function for each one. * * @param[in] directory The directory to be read * @param[in] cookie Starting cookie for the readdir operation * @param[out] eod_met Whether the end of directory was met * @param[in] attrmask Attributes requested, used for permission checking * really all that matters is ATTR_ACL and any attrs * at all, specifics never actually matter. * @param[in] cb The callback function to receive entries * @param[in] opaque A pointer passed to be passed in * fsal_readdir_cb_parms * * @return FSAL status */ fsal_status_t fsal_readdir(struct fsal_obj_handle *directory, uint64_t cookie, unsigned int *nbfound, bool *eod_met, attrmask_t attrmask, helper_readdir_cb cb, void *opaque) { fsal_status_t fsal_status = {0, 0}; fsal_status_t cb_status = {0, 0}; struct fsal_populate_cb_state state; *nbfound = 0; /* The access mask corresponding to permission to list directory entries */ fsal_accessflags_t access_mask = (FSAL_MODE_MASK_SET(FSAL_R_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_LIST_DIR)); fsal_accessflags_t access_mask_attr = (FSAL_MODE_MASK_SET(FSAL_R_OK) | FSAL_MODE_MASK_SET(FSAL_X_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_LIST_DIR) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_EXECUTE)); /* readdir can be done only with a directory */ if (directory->type != DIRECTORY) { LogDebug(COMPONENT_NFS_READDIR, "Not a directory"); return fsalstat(ERR_FSAL_NOTDIR, 0); } /* Adjust access mask if ACL is asked for. * NOTE: We intentionally do NOT check ACE4_READ_ATTR. */ if ((attrmask & ATTR_ACL) != 0) { access_mask |= FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_READ_ACL); access_mask_attr |= FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_READ_ACL); } fsal_status = fsal_access(directory, access_mask); if (FSAL_IS_ERROR(fsal_status)) { LogDebug(COMPONENT_NFS_READDIR, "permission check for directory status=%s", fsal_err_txt(fsal_status)); return fsal_status; } if (attrmask != 0) { /* Check for access permission to get attributes */ fsal_status_t attr_status = fsal_access(directory, access_mask_attr); if (FSAL_IS_ERROR(attr_status)) LogDebug(COMPONENT_NFS_READDIR, "permission check for attributes status=%s", fsal_err_txt(attr_status)); state.cb_parms.attr_allowed = !FSAL_IS_ERROR(attr_status); } else { /* No attributes requested. */ state.cb_parms.attr_allowed = false; } state.directory = directory; state.status = &cb_status; state.cb = cb; state.last_cookie = 0; state.cb_parms.opaque = opaque; state.cb_parms.in_result = true; state.cb_parms.name = NULL; state.cb_state = CB_ORIGINAL; state.cb_nfound = nbfound; state.attrmask = attrmask; fsal_status = directory->obj_ops.readdir(directory, &cookie, (void *)&state, populate_dirent, attrmask, eod_met); return fsal_status; } /** * * @brief Remove a name from a directory. * * @param[in] parent Handle for the parent directory to be managed * @param[in] name Name to be removed * * @retval fsal_status_t */ fsal_status_t fsal_remove(struct fsal_obj_handle *parent, const char *name) { struct fsal_obj_handle *to_remove_obj = NULL; fsal_status_t status = { 0, 0 }; if (parent->type != DIRECTORY) { status = fsalstat(ERR_FSAL_NOTDIR, 0); goto out_no_obj; } /* Looks up for the entry to remove */ status = fsal_lookup(parent, name, &to_remove_obj, NULL); if (FSAL_IS_ERROR(status)) { LogFullDebug(COMPONENT_FSAL, "lookup %s failure %s", name, fsal_err_txt(status)); return status; } /* Do not remove a junction node or an export root. */ if (obj_is_junction(to_remove_obj)) { LogCrit(COMPONENT_FSAL, "Attempt to remove export %s", name); status = fsalstat(ERR_FSAL_NOTEMPTY, 0); goto out; } LogFullDebug(COMPONENT_FSAL, "%s", name); /* Make sure the to_remove_obj is closed since unlink of an * open file results in 'silly rename' on certain platforms. */ status = fsal_close(to_remove_obj); if (FSAL_IS_ERROR(status)) { /* non-fatal error. log the warning and move on */ LogCrit(COMPONENT_FSAL, "Error closing %s before unlink: %s.", name, fsal_err_txt(status)); } #ifdef ENABLE_RFC_ACL status = fsal_remove_access(parent, to_remove_obj, (to_remove_obj->type == DIRECTORY)); if (FSAL_IS_ERROR(status)) goto out; #endif /* ENABLE_RFC_ACL */ status = parent->obj_ops.unlink(parent, to_remove_obj, name); if (FSAL_IS_ERROR(status)) { LogFullDebug(COMPONENT_FSAL, "unlink %s failure %s", name, fsal_err_txt(status)); goto out; } out: to_remove_obj->obj_ops.put_ref(to_remove_obj); out_no_obj: LogFullDebug(COMPONENT_FSAL, "remove %s: status=%s", name, fsal_err_txt(status)); return status; } /** * @brief Renames a file * * @param[in] dir_src The source directory * @param[in] oldname The current name of the file * @param[in] dir_dest The destination directory * @param[in] newname The name to be assigned to the object * * @return FSAL status */ fsal_status_t fsal_rename(struct fsal_obj_handle *dir_src, const char *oldname, struct fsal_obj_handle *dir_dest, const char *newname) { fsal_status_t fsal_status = { 0, 0 }; struct fsal_obj_handle *lookup_src = NULL; if ((dir_src->type != DIRECTORY) || (dir_dest->type != DIRECTORY)) return fsalstat(ERR_FSAL_NOTDIR, 0); /* Check for . and .. on oldname and newname. */ if (!strcmp(oldname, ".") || !strcmp(oldname, "..") || !strcmp(newname, ".") || !strcmp(newname, "..")) { return fsalstat(ERR_FSAL_BADNAME, 0); } /* Check for object existence in source directory */ fsal_status = fsal_lookup(dir_src, oldname, &lookup_src, NULL); if (FSAL_IS_ERROR(fsal_status)) { LogDebug(COMPONENT_FSAL, "Rename (%p,%s)->(%p,%s) : source doesn't exist", dir_src, oldname, dir_dest, newname); goto out; } /* Do not rename a junction node or an export root. */ if (obj_is_junction(lookup_src)) { LogCrit(COMPONENT_FSAL, "Attempt to rename export %s", oldname); fsal_status = fsalstat(ERR_FSAL_NOTEMPTY, 0); goto out; } /* Don't allow rename of an object as parent of itself */ if (dir_dest == lookup_src) { fsal_status = fsalstat(ERR_FSAL_INVAL, 0); goto out; } LogFullDebug(COMPONENT_FSAL, "about to call FSAL rename"); fsal_status = dir_src->obj_ops.rename(lookup_src, dir_src, oldname, dir_dest, newname); LogFullDebug(COMPONENT_FSAL, "returned from FSAL rename"); if (FSAL_IS_ERROR(fsal_status)) { LogFullDebug(COMPONENT_FSAL, "FSAL rename failed with %s", fsal_err_txt(fsal_status)); goto out; } out: if (lookup_src) { /* Note that even with a junction, this object is in the same * export since that would be the junction node, NOT the export * root node on the other side of the junction. */ lookup_src->obj_ops.put_ref(lookup_src); } return fsal_status; } /** * @brief Opens a file by name or by handle. * * This function accomplishes both a LOOKUP if necessary and an open. * * Returns with an LRU reference held on the entry. * * state can be NULL which indicates a stateless open (such as via the * NFS v3 CREATE operation). * * 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. * * The caller is expected to invoke fsal_release_attrs to release any * resources held by the set attributes. The FSAL layer MAY have added an * inherited ACL. * * @param[in] in_obj Parent directory or obj * @param[in,out] state state_t to operate on * @param[in] openflags Details of how to open the file * @param[in] createmode Mode if creating * @param[in] name If name is not NULL, entry is the parent directory * @param[in] attr Attributes to set on the file * @param[in] verifier Verifier to use with exclusive create * @param[out] obj New entry for the opened file * * @return FSAL status */ fsal_status_t fsal_open2(struct fsal_obj_handle *in_obj, struct state_t *state, fsal_openflags_t openflags, enum fsal_create_mode createmode, const char *name, struct attrlist *attr, fsal_verifier_t verifier, struct fsal_obj_handle **obj, struct attrlist *attrs_out) { fsal_status_t status = { 0, 0 }; bool caller_perm_check = false; char *reason; *obj = NULL; if (attr != NULL) LogAttrlist(COMPONENT_FSAL, NIV_FULL_DEBUG, "attrs ", attr, false); /* Handle attribute size = 0 here, normalize to FSAL_O_TRUNC * instead of setting ATTR_SIZE. */ if (attr != NULL && FSAL_TEST_MASK(attr->valid_mask, ATTR_SIZE) && attr->filesize == 0) { LogFullDebug(COMPONENT_FSAL, "Truncate"); /* Handle truncate to zero on open */ openflags |= FSAL_O_TRUNC; /* Don't set the size if we later set the attributes */ FSAL_UNSET_MASK(attr->valid_mask, ATTR_SIZE); } if (createmode >= FSAL_EXCLUSIVE && verifier == NULL) return fsalstat(ERR_FSAL_INVAL, 0); if (name) return open2_by_name(in_obj, state, openflags, createmode, name, attr, verifier, obj, attrs_out); /* No name, directories don't make sense */ if (in_obj->type == DIRECTORY) { if (createmode != FSAL_NO_CREATE) return fsalstat(ERR_FSAL_INVAL, 0); return fsalstat(ERR_FSAL_ISDIR, 0); } if (in_obj->type != REGULAR_FILE) return fsalstat(ERR_FSAL_BADTYPE, 0); /* Do a permission check on the file before opening. */ status = check_open_permission(in_obj, openflags, createmode >= FSAL_EXCLUSIVE, &reason); if (FSAL_IS_ERROR(status)) { LogDebug(COMPONENT_FSAL, "Not opening file file %s%s", reason, fsal_err_txt(status)); return status; } /* Open THIS entry, so name must be NULL. The attr are passed in case * this is a create with size = 0. We pass the verifier because this * might be an exclusive recreate replay and we want the FSAL to * check the verifier. */ status = in_obj->obj_ops.open2(in_obj, state, openflags, createmode, NULL, attr, verifier, obj, attrs_out, &caller_perm_check); if (!FSAL_IS_ERROR(status)) { /* Get a reference to the entry. */ *obj = in_obj; in_obj->obj_ops.get_ref(in_obj); } return status; } /** * @brief Re-Opens a file by handle. * * This MAY be used to open a file the first time if there is no need for * open by name or create semantics. * * @param[in] obj File to operate on * @param[in,out] state state_t to operate on * @param[in] openflags Details of how to open the file * @param[in] check_permission Indicate if permission should be checked * * @return FSAL status */ fsal_status_t fsal_reopen2(struct fsal_obj_handle *obj, struct state_t *state, fsal_openflags_t openflags, bool check_permission) { fsal_status_t status = { 0, 0 }; char *reason = "FSAL reopen failed - "; if (check_permission) { /* Do a permission check on the file before re-opening. */ status = check_open_permission(obj, openflags, false, &reason); if (FSAL_IS_ERROR(status)) goto out; } /* Re-open the entry in the FSAL. */ status = obj->obj_ops.reopen2(obj, state, openflags); out: if (FSAL_IS_ERROR(status)) { LogDebug(COMPONENT_FSAL, "Not re-opening file file %s%s", reason, fsal_err_txt(status)); } return status; } fsal_status_t fsal_statfs(struct fsal_obj_handle *obj, fsal_dynamicfsinfo_t *dynamicinfo) { fsal_status_t fsal_status; struct fsal_export *export; export = op_ctx->ctx_export->fsal_export; /* Get FSAL to get dynamic info */ fsal_status = export->exp_ops.get_fs_dynamic_info(export, obj, dynamicinfo); LogFullDebug(COMPONENT_FSAL, "dynamicinfo: {total_bytes = %" PRIu64 ", free_bytes = %" PRIu64 ", avail_bytes = %" PRIu64 ", total_files = %" PRIu64 ", free_files = %" PRIu64 ", avail_files = %" PRIu64 "}", dynamicinfo->total_bytes, dynamicinfo->free_bytes, dynamicinfo->avail_bytes, dynamicinfo->total_files, dynamicinfo->free_files, dynamicinfo->avail_files); return fsal_status; } /** * @brief Verify an exclusive create replay when the file is already open. * * This may not be necessary in real life, however, pynfs definitely has a * test case that walks this path. * * @param[in] obj File to verify * @param[in] verifier Verifier to use with exclusive create * * @return FSAL status */ fsal_status_t fsal_verify2(struct fsal_obj_handle *obj, fsal_verifier_t verifier) { if (!obj->obj_ops.check_verifier(obj, verifier)) { /* Verifier check failed. */ return fsalstat(ERR_FSAL_EXIST, 0); } return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Fetch optional attributes * * The request_mask should be set in attrs_out indicating which attributes * are desired. If ATTR_RDATTR_ERR is set, and the getattrs fails, * the error ERR_FSAL_NO_ERROR will be returned, however the attributes * valid_mask will be set to ATTR_RDATTR_ERR. Otherwise, if * ATTR_RDATTR_ERR is not set and the getattrs fails, the error returned * by getattrs will be returned. * * @param[in] obj_hdl Object to get attributes for. * @param[in,out] attrs_out Optional attributes for the object * * @return FSAL status. **/ fsal_status_t get_optional_attrs(struct fsal_obj_handle *obj_hdl, struct attrlist *attrs_out) { fsal_status_t status; if (attrs_out == NULL) return fsalstat(ERR_FSAL_NO_ERROR, 0); status = obj_hdl->obj_ops.getattrs(obj_hdl, attrs_out); if (FSAL_IS_ERROR(status)) { if (attrs_out->request_mask & ATTR_RDATTR_ERR) { /* Indicate the failure of requesting attributes by * marking the ATTR_RDATTR_ERR in the mask. */ attrs_out->valid_mask = ATTR_RDATTR_ERR; status = fsalstat(ERR_FSAL_NO_ERROR, 0); } /* otherwise let the error stand. */ } return status; } /** @} */ nfs-ganesha-2.6.0/src/FSAL/fsal_manager.c000066400000000000000000000351101324272410200200050ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @addtogroup FSAL * @{ */ /** * @file fsal_manager.c * @author Jim Lieb * @brief FSAL module manager */ #include "config.h" #include #include #include #include #include #include #include #include #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "config_parsing.h" #include "pnfs_utils.h" #include "fsal_private.h" /** * @brief List of loaded fsal modules * * Static to be private to the functions in this module * fsal_lock is taken whenever the list is walked. */ pthread_mutex_t fsal_lock = PTHREAD_MUTEX_INITIALIZER; GLIST_HEAD(fsal_list); /** * @{ * * Variables for passing status/errors between shared object * and this module. They must be accessed under lock. */ static char *dl_error; static int so_error; static struct fsal_module *new_fsal; /** * @} */ /** * @brief FSAL load state */ static enum load_state { init, /*< In server start state. .init sections can run */ idle, /*< Switch from init->idle early in main() */ loading, /*< In dlopen(). set by load_fsal() just prior */ registered, /*< signal by registration that all is well */ error /*< signal by registration that all is not well */ } load_state = init; /** * @brief Start a static FSAL * * Start a FSAL that's statically linked in. * * @param[in] name FSAL name * @param[in] init Initialization function for FSAL */ static void load_fsal_static(const char *name, void (*init)(void)) { char pname[24]; char *dl_path; struct fsal_module *fsal; snprintf(pname, sizeof(pname), "Builtin-%s", name); dl_path = gsh_strdup(pname); PTHREAD_MUTEX_lock(&fsal_lock); if (load_state != idle) LogFatal(COMPONENT_INIT, "Couldn't Register FSAL_%s", name); if (dl_error) { gsh_free(dl_error); dl_error = NULL; } load_state = loading; PTHREAD_MUTEX_unlock(&fsal_lock); /* now it is the module's turn to register itself */ init(); PTHREAD_MUTEX_lock(&fsal_lock); if (load_state != registered) LogFatal(COMPONENT_INIT, "Couldn't Register FSAL_%s", name); /* we now finish things up, doing things the module can't see */ fsal = new_fsal; /* recover handle from .ctor and poison again */ new_fsal = NULL; fsal->path = dl_path; fsal->dl_handle = NULL; so_error = 0; load_state = idle; PTHREAD_MUTEX_unlock(&fsal_lock); } /** * @brief Start_fsals * * Called early server initialization. Set load_state to idle * at this point as a check on dynamic loading not starting too early. */ void start_fsals(void) { /* .init was a long time ago... */ load_state = idle; /* Load FSAL_MDCACHE */ load_fsal_static("MDCACHE", mdcache_fsal_init); /* Load FSAL_PSEUDO */ load_fsal_static("PSEUDO", pseudo_fsal_init); } /** * Enforced filename for FSAL library objects. */ static const char *pathfmt = "%s/libfsal%s.so"; /** * @brief Load the fsal's shared object. * * The dlopen() will trigger a .init constructor which will do the * actual registration. after a successful load, the returned handle * needs to be "put" back after any other initialization is done. * * @param[in] name Name of the FSAL to load * @param[out] fsal_hdl_p Newly allocated FSAL handle * * @retval 0 Success, when finished, put_fsal_handle() to free * @retval EBUSY the loader is busy (should not happen) * @retval EEXIST the module is already loaded * @retval ENOLCK register_fsal without load_fsal holding the lock. * @retval EINVAL wrong loading state for registration * @retval ENOMEM out of memory * @retval ENOENT could not find "module_init" function * @retval EFAULT module_init has a bad address * @retval other general dlopen errors are possible, all of them bad */ int load_fsal(const char *name, struct fsal_module **fsal_hdl_p) { void *dl = NULL; int retval = EBUSY; /* already loaded */ char *dl_path; struct fsal_module *fsal; char *bp; char *path = alloca(strlen(nfs_param.core_param.ganesha_modules_loc) + strlen(name) + strlen(pathfmt) + 1); sprintf(path, pathfmt, nfs_param.core_param.ganesha_modules_loc, name); bp = rindex(path, '/'); bp++; /* now it is the basename, lcase it */ while (*bp != '\0') { if (isupper(*bp)) *bp = tolower(*bp); bp++; } dl_path = gsh_strdup(path); PTHREAD_MUTEX_lock(&fsal_lock); if (load_state != idle) goto errout; if (dl_error) { gsh_free(dl_error); dl_error = NULL; } load_state = loading; PTHREAD_MUTEX_unlock(&fsal_lock); LogDebug(COMPONENT_INIT, "Loading FSAL %s with %s", name, path); #if defined(LINUX) && !defined(SANITIZE_ADDRESS) dl = dlopen(path, RTLD_NOW | RTLD_LOCAL | RTLD_DEEPBIND); #elif defined(FREEBSD) || defined(SANITIZE_ADDRESS) dl = dlopen(path, RTLD_NOW | RTLD_LOCAL); #endif PTHREAD_MUTEX_lock(&fsal_lock); if (dl == NULL) { #ifdef ELIBACC retval = ELIBACC; /* hand craft a meaningful error */ #else retval = EPERM; /* ELIBACC does not exist on MacOS */ #endif dl_error = gsh_strdup(dlerror()); LogCrit(COMPONENT_INIT, "Could not dlopen module:%s Error:%s", path, dl_error); goto errout; } dlerror(); /* clear it */ /* now it is the module's turn to register itself */ if (load_state == loading) { /* constructor didn't fire */ void (*module_init)(void); char *sym_error; module_init = dlsym(dl, "fsal_init"); sym_error = (char *)dlerror(); if (sym_error != NULL) { dl_error = gsh_strdup(sym_error); so_error = ENOENT; LogCrit(COMPONENT_INIT, "Could not execute symbol fsal_init from module:%s Error:%s", path, dl_error); goto dlerr; } if ((void *)module_init == NULL) { so_error = EFAULT; LogCrit(COMPONENT_INIT, "Could not execute symbol fsal_init from module:%s Error:%s", path, dl_error); goto dlerr; } PTHREAD_MUTEX_unlock(&fsal_lock); (*module_init) (); /* try registering by hand this time */ PTHREAD_MUTEX_lock(&fsal_lock); } if (load_state == error) { /* we are in registration hell */ retval = so_error; /* this is the registration error */ LogCrit(COMPONENT_INIT, "Could not execute symbol fsal_init from module:%s Error:%s", path, dl_error); goto dlerr; } if (load_state != registered) { retval = EPERM; LogCrit(COMPONENT_INIT, "Could not execute symbol fsal_init from module:%s Error:%s", path, dl_error); goto dlerr; } /* we now finish things up, doing things the module can't see */ fsal = new_fsal; /* recover handle from .ctor and poison again */ new_fsal = NULL; /* take initial ref so we can pass it back... */ fsal_get(fsal); LogFullDebug(COMPONENT_FSAL, "FSAL %s refcount %"PRIu32, name, atomic_fetch_int32_t(&fsal->refcount)); fsal->path = dl_path; fsal->dl_handle = dl; so_error = 0; *fsal_hdl_p = fsal; load_state = idle; PTHREAD_MUTEX_unlock(&fsal_lock); return 0; dlerr: dlclose(dl); errout: load_state = idle; PTHREAD_MUTEX_unlock(&fsal_lock); LogMajor(COMPONENT_INIT, "Failed to load module (%s) because: %s", path, strerror(retval)); gsh_free(dl_path); return retval; } /** * @brief Look up an FSAL * * Acquire a handle to the named FSAL and take a reference to it. This * must be done before using any methods. Once done, release it with * @c put_fsal. * * @param[in] name Name to look up * * @return Module pointer or NULL if not found. */ struct fsal_module *lookup_fsal(const char *name) { struct fsal_module *fsal; struct glist_head *entry; PTHREAD_MUTEX_lock(&fsal_lock); glist_for_each(entry, &fsal_list) { fsal = glist_entry(entry, struct fsal_module, fsals); if (strcasecmp(name, fsal->name) == 0) { fsal_get(fsal); PTHREAD_MUTEX_unlock(&fsal_lock); op_ctx->fsal_module = fsal; LogFullDebug(COMPONENT_FSAL, "FSAL %s refcount %"PRIu32, name, atomic_fetch_int32_t(&fsal->refcount)); return fsal; } } PTHREAD_MUTEX_unlock(&fsal_lock); return NULL; } /* functions only called by modules at ctor/dtor time */ /** * @brief Register the fsal in the system * * This can be called from three places: * * + the server program's .init section if the fsal was statically linked * + the shared object's .init section when load_fsal() dynamically loads it. * + from the shared object's 'fsal_init' function if dlopen does not support * .init/.fini sections. * * Any other case is an error. * Change load_state only for dynamically loaded modules. * * @param[in] fsal_hdl FSAL module handle * @param[in] name FSAL name * @param[in] major_version Major version * @param[in] minor_version Minor version * * @return 0 on success, otherwise POSIX errors. */ /** @todo implement api versioning and pass the major,minor here */ int register_fsal(struct fsal_module *fsal_hdl, const char *name, uint32_t major_version, uint32_t minor_version, uint8_t fsal_id) { pthread_rwlockattr_t attrs; PTHREAD_MUTEX_lock(&fsal_lock); if ((major_version != FSAL_MAJOR_VERSION) || (minor_version > FSAL_MINOR_VERSION)) { so_error = EINVAL; LogCrit(COMPONENT_INIT, "FSAL \"%s\" failed to register because of version mismatch core = %d.%d, fsal = %d.%d", name, FSAL_MAJOR_VERSION, FSAL_MINOR_VERSION, major_version, minor_version); load_state = error; goto errout; } so_error = 0; if (!(load_state == loading || load_state == init)) { so_error = EACCES; goto errout; } new_fsal = fsal_hdl; if (name != NULL) new_fsal->name = gsh_strdup(name); /* init ops vector to system wide defaults * from FSAL/default_methods.c */ memcpy(&fsal_hdl->m_ops, &def_fsal_ops, sizeof(struct fsal_ops)); pthread_rwlockattr_init(&attrs); #ifdef GLIBC pthread_rwlockattr_setkind_np( &attrs, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP); #endif PTHREAD_RWLOCK_init(&fsal_hdl->lock, &attrs); glist_init(&fsal_hdl->servers); glist_init(&fsal_hdl->handles); glist_init(&fsal_hdl->exports); glist_add_tail(&fsal_list, &fsal_hdl->fsals); if (load_state == loading) load_state = registered; if (fsal_id != FSAL_ID_NO_PNFS && fsal_id < FSAL_ID_COUNT) pnfs_fsal[fsal_id] = fsal_hdl; PTHREAD_MUTEX_unlock(&fsal_lock); return 0; errout: gsh_free(fsal_hdl->path); gsh_free(fsal_hdl->name); load_state = error; PTHREAD_MUTEX_unlock(&fsal_lock); LogCrit(COMPONENT_INIT, "FSAL \"%s\" failed to register because: %s", name, strerror(so_error)); return so_error; } /** * @brief Unregisterx an FSAL * * Verify that the fsal is not busy and release all its resources * owned at this level. RW Lock is already freed. Called from the * module's MODULE_FINI * * @param[in] fsal_hdl FSAL handle * * @retval 0 on success. * @retval EBUSY if FSAL is in use. */ int unregister_fsal(struct fsal_module *fsal_hdl) { int32_t refcount = atomic_fetch_int32_t(&fsal_hdl->refcount); if (refcount != 0) { /* this would be very bad */ LogCrit(COMPONENT_FSAL, "Unregister FSAL %s with non-zero refcount=%"PRIi32, fsal_hdl->name, refcount); return EBUSY; } gsh_free(fsal_hdl->path); gsh_free(fsal_hdl->name); return 0; } /** * @brief Init and commit for FSAL sub-block */ /** * @brief Initialize space for an FSAL sub-block. * * We allocate space to hold the name parameter so that * is available in the commit phase. */ void *fsal_init(void *link_mem, void *self_struct) { struct fsal_args *fp; assert(link_mem != NULL || self_struct != NULL); if (link_mem == NULL) { return self_struct; /* NOP */ } else if (self_struct == NULL) { return gsh_calloc(1, sizeof(struct fsal_args)); } else { fp = self_struct; gsh_free(fp->name); gsh_free(fp); return NULL; } } /** * @brief Load and initialize FSAL module * * Use the name parameter to lookup the fsal. If the fsal is not * loaded (yet), load it and call its init. This will trigger the * processing of a top level block of the same name as the fsal, i.e. * the VFS fsal will look for a VFS block and process it (if found). * * @param[in] node parse node of FSAL block * @param[in] name name of the FSAL to load and initialize (if * not already loaded) * @param[out] fsal_hdl Pointer to FSAL module or NULL if not found * @param[out] err_type pointer to error type * * @retval 0 on success, error count on errors */ int fsal_load_init(void *node, const char *name, struct fsal_module **fsal_hdl, struct config_error_type *err_type) { fsal_status_t status; if (name == NULL || strlen(name) == 0) { config_proc_error(node, err_type, "Name of FSAL is missing"); err_type->missing = true; return 1; } *fsal_hdl = lookup_fsal(name); if (*fsal_hdl == NULL) { int retval; config_file_t myconfig; retval = load_fsal(name, fsal_hdl); if (retval != 0) { config_proc_error(node, err_type, "Failed to load FSAL (%s) because: %s", name, strerror(retval)); err_type->fsal = true; return 1; } op_ctx->fsal_module = *fsal_hdl; myconfig = get_parse_root(node); status = (*fsal_hdl)->m_ops.init_config(*fsal_hdl, myconfig, err_type); if (FSAL_IS_ERROR(status)) { config_proc_error(node, err_type, "Failed to initialize FSAL (%s)", name); fsal_put(*fsal_hdl); err_type->fsal = true; LogFullDebug(COMPONENT_FSAL, "FSAL %s refcount %"PRIu32, name, atomic_fetch_int32_t( &(*fsal_hdl)->refcount)); return 1; } } return 0; } /** * @brief Load and initialize sub-FSAL module * * @retval 0 on success, error count on errors */ int subfsal_commit(void *node, void *link_mem, void *self_struct, struct config_error_type *err_type) { struct fsal_module *fsal_next; struct subfsal_args *subfsal = (struct subfsal_args *)self_struct; int errcnt = fsal_load_init(node, subfsal->name, &fsal_next, err_type); if (errcnt == 0) subfsal->fsal_node = node; return errcnt; } /** @} */ nfs-ganesha-2.6.0/src/FSAL/fsal_private.h000066400000000000000000000031121324272410200200470ustar00rootroot00000000000000/* * * * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ #ifndef FSAL_PRIVATE_H #define FSAL_PRIVATE_H /* Define some externals for FSAL */ extern struct fsal_ops def_fsal_ops; extern struct export_ops def_export_ops; /* freebsd gcc workaround */ extern struct fsal_obj_ops def_handle_ops; extern struct fsal_pnfs_ds_ops def_pnfs_ds_ops; extern struct fsal_dsh_ops def_dsh_ops; /* Global lock for fsal list. * kept in fsal_manager.c * Special checkpatch case here. This is private between the two files. */ extern pthread_mutex_t fsal_lock; extern struct glist_head fsal_list; /* Definitions for static FSALs */ void pseudo_fsal_init(void); void mdcache_fsal_init(void); #endif /* FSAL_PRIVATE_H */ nfs-ganesha-2.6.0/src/FSAL_UP/000077500000000000000000000000001324272410200156265ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/FSAL_UP/fsal_up_async.c000066400000000000000000000265441324272410200206330ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @addtogroup fsal_up * @{ */ /** * @file fsal_up_async.c * @author Adam C. Emerson * @brief Asynchrony wrappers for FSAL Upcall system * * This is not the most elegant design in history, but should be * reasonably efficient. At present, we have to copy the key supplied * rather than saving a pointer. Once version 2.1 comes out and we * can from the FSAL object to the cache entry with container of, * we'll be able to jump up, grab a ref on the cache entry, and just * store the pointer. * * Every async call requires one allocation and one queue into the * thread fridge. We make the thread fridge a parameter, so an FSAL * that's expecting to shoot out lots and lots of upcalls can make one * holding several threads wide. * * Every async call takes a callback function and an argument, to * allow it to receive errors. The callback function may be NULL if * the caller doesn't care. This doesn't affect methods that may be * called asynchronously by upcall handlers like @c layoutreturn. * * Every async call takes a reference on the export, the queued action * returns it after execution. * * Every async call returns 0 on success and a POSIX error code on error. */ #include "config.h" #include #include #include #include "nfs_core.h" #include "log.h" #include "fsal.h" #include "fsal_up.h" #include "fsal_convert.h" #include "sal_functions.h" #include "pnfs_utils.h" /* Invalidate */ struct invalidate_args { const struct fsal_up_vector *vec; struct gsh_buffdesc obj; uint32_t flags; void (*cb)(void *, fsal_status_t); void *cb_arg; char key[]; }; static void queue_invalidate(struct fridgethr_context *ctx) { struct invalidate_args *args = ctx->arg; fsal_status_t status; status = args->vec->up_fsal_export->up_ops->invalidate(args->vec, &args->obj, args->flags); if (args->cb) args->cb(args->cb_arg, status); gsh_free(args); } fsal_status_t up_async_invalidate(struct fridgethr *fr, const struct fsal_up_vector *vec, struct gsh_buffdesc *obj, uint32_t flags, void (*cb)(void *, fsal_status_t), void *cb_arg) { struct invalidate_args *args = NULL; int rc = 0; args = gsh_malloc(sizeof(struct invalidate_args) + obj->len); args->vec = vec; args->flags = flags; args->cb = cb; args->cb_arg = cb_arg; memcpy(args->key, obj->addr, obj->len); args->obj.addr = args->key; args->obj.len = obj->len; rc = fridgethr_submit(fr, queue_invalidate, args); if (rc != 0) gsh_free(args); return fsalstat(posix2fsal_error(rc), rc); } /* Update */ struct update_args { const struct fsal_up_vector *vec; struct gsh_buffdesc obj; struct attrlist attr; uint32_t flags; void (*cb)(void *, fsal_status_t); void *cb_arg; char key[]; }; static void queue_update(struct fridgethr_context *ctx) { struct update_args *args = ctx->arg; fsal_status_t status; status = args->vec->up_fsal_export->up_ops->update(args->vec, &args->obj, &args->attr, args->flags); if (args->cb) args->cb(args->cb_arg, status); gsh_free(args); } fsal_status_t up_async_update(struct fridgethr *fr, const struct fsal_up_vector *vec, struct gsh_buffdesc *obj, struct attrlist *attr, uint32_t flags, void (*cb)(void *, fsal_status_t), void *cb_arg) { struct update_args *args = NULL; int rc = 0; args = gsh_malloc(sizeof(struct update_args) + obj->len); args->vec = vec; args->attr = *attr; args->flags = flags; args->cb = cb; args->cb_arg = cb_arg; memcpy(args->key, obj->addr, obj->len); args->obj.addr = args->key; args->obj.len = obj->len; rc = fridgethr_submit(fr, queue_update, args); if (rc != 0) gsh_free(args); return fsalstat(posix2fsal_error(rc), rc); } /* Lock grant */ struct lock_grant_args { const struct fsal_up_vector *vec; struct gsh_buffdesc file; void *owner; fsal_lock_param_t lock_param; void (*cb)(void *, state_status_t); void *cb_arg; char key[]; }; static void queue_lock_grant(struct fridgethr_context *ctx) { struct lock_grant_args *args = ctx->arg; state_status_t status; status = args->vec->up_fsal_export->up_ops->lock_grant(args->vec, &args->file, args->owner, &args->lock_param); if (args->cb) args->cb(args->cb_arg, status); gsh_free(args); } fsal_status_t up_async_lock_grant(struct fridgethr *fr, const struct fsal_up_vector *vec, struct gsh_buffdesc *file, void *owner, fsal_lock_param_t *lock_param, void (*cb)(void *, state_status_t), void *cb_arg) { struct lock_grant_args *args = NULL; int rc = 0; args = gsh_malloc(sizeof(struct lock_grant_args) + file->len); args->vec = vec; args->owner = owner; args->lock_param = *lock_param; args->cb = cb; args->cb_arg = cb_arg; memcpy(args->key, file->addr, file->len); args->file.addr = args->key; args->file.len = file->len; rc = fridgethr_submit(fr, queue_lock_grant, args); if (rc != 0) gsh_free(args); return fsalstat(posix2fsal_error(rc), rc); } /* Lock avail */ struct lock_avail_args { const struct fsal_up_vector *vec; struct gsh_buffdesc file; void *owner; fsal_lock_param_t lock_param; void (*cb)(void *, state_status_t); void *cb_arg; char key[]; }; static void queue_lock_avail(struct fridgethr_context *ctx) { struct lock_avail_args *args = ctx->arg; state_status_t status; status = args->vec->up_fsal_export->up_ops->lock_avail(args->vec, &args->file, args->owner, &args->lock_param); if (args->cb) args->cb(args->cb_arg, status); gsh_free(args); } fsal_status_t up_async_lock_avail(struct fridgethr *fr, const struct fsal_up_vector *vec, struct gsh_buffdesc *file, void *owner, fsal_lock_param_t *lock_param, void (*cb)(void *, state_status_t), void *cb_arg) { struct lock_avail_args *args = NULL; int rc = 0; args = gsh_malloc(sizeof(struct lock_avail_args) + file->len); args->vec = vec; args->owner = owner; args->lock_param = *lock_param; args->cb = cb; args->cb_arg = cb_arg; memcpy(args->key, file->addr, file->len); args->file.addr = args->key; args->file.len = file->len; rc = fridgethr_submit(fr, queue_lock_avail, args); if (rc != 0) gsh_free(args); return fsalstat(posix2fsal_error(rc), rc); } /* Layoutrecall */ struct layoutrecall_args { const struct fsal_up_vector *vec; struct gsh_buffdesc handle; layouttype4 layout_type; bool changed; struct pnfs_segment segment; void *cookie; struct layoutrecall_spec spec; void (*cb)(void *, state_status_t); void *cb_arg; char data[]; }; static void queue_layoutrecall(struct fridgethr_context *ctx) { struct layoutrecall_args *args = ctx->arg; state_status_t status; status = args->vec->up_fsal_export->up_ops->layoutrecall(args->vec, &args->handle, args->layout_type, args->changed, &args->segment, args->cookie, args->spec.how == layoutrecall_not_specced ? NULL : &args->spec); if (args->cb) args->cb(args->cb_arg, status); gsh_free(args); } fsal_status_t up_async_layoutrecall(struct fridgethr *fr, const struct fsal_up_vector *vec, struct gsh_buffdesc *handle, layouttype4 layout_type, bool changed, const struct pnfs_segment *segment, void *cookie, struct layoutrecall_spec *spec, void (*cb)(void *, state_status_t), void *cb_arg) { struct layoutrecall_args *args = NULL; int rc = 0; args = gsh_malloc(sizeof(struct layoutrecall_args) + handle->len); args->vec = vec; args->cb = cb; args->cb_arg = cb_arg; memcpy(args->data, handle->addr, handle->len); args->handle.addr = args->data; args->handle.len = handle->len; args->layout_type = layout_type; args->changed = changed; args->segment = *segment; args->cookie = cookie; if (spec) args->spec = *spec; else args->spec.how = layoutrecall_not_specced; rc = fridgethr_submit(fr, queue_layoutrecall, args); if (rc != 0) gsh_free(args); return fsalstat(posix2fsal_error(rc), rc); } /* Notify Device */ struct notify_device_args { const struct fsal_up_vector *vec; notify_deviceid_type4 notify_type; layouttype4 layout_type; struct pnfs_deviceid devid; bool immediate; void (*cb)(void *, state_status_t); void *cb_arg; char data[]; }; static void queue_notify_device(struct fridgethr_context *ctx) { struct notify_device_args *args = ctx->arg; state_status_t status; status = args->vec->up_fsal_export->up_ops->notify_device( args->notify_type, args->layout_type, args->devid, args->immediate); if (args->cb) args->cb(args->cb_arg, status); gsh_free(args); } fsal_status_t up_async_notify_device(struct fridgethr *fr, const struct fsal_up_vector *vec, notify_deviceid_type4 notify_type, layouttype4 layout_type, struct pnfs_deviceid *devid, bool immediate, void (*cb)(void *, state_status_t), void *cb_arg) { struct notify_device_args *args = NULL; int rc = 0; args = gsh_malloc(sizeof(struct notify_device_args)); args->vec = vec; args->cb = cb; args->cb_arg = cb_arg; args->notify_type = notify_type; args->layout_type = layout_type; args->devid = *devid; args->immediate = immediate; rc = fridgethr_submit(fr, queue_notify_device, args); if (rc != 0) gsh_free(args); return fsalstat(posix2fsal_error(rc), rc); } /* Delegrecall */ struct delegrecall_args { const struct fsal_up_vector *vec; struct gsh_buffdesc handle; void (*cb)(void *, state_status_t); void *cb_arg; char key[]; }; static void queue_delegrecall(struct fridgethr_context *ctx) { struct fsal_obj_handle *obj = ctx->arg; (void)delegrecall_impl(obj); } int async_delegrecall(struct fridgethr *fr, struct fsal_obj_handle *obj) { int rc = fridgethr_submit(fr, queue_delegrecall, obj); return rc; } static void up_queue_delegrecall(struct fridgethr_context *ctx) { struct delegrecall_args *args = ctx->arg; state_status_t status; status = args->vec->up_fsal_export->up_ops->delegrecall(args->vec, &args->handle); if (args->cb) args->cb(args->cb_arg, status); gsh_free(args); } /* XXX dang refcount exports in these? */ fsal_status_t up_async_delegrecall(struct fridgethr *fr, const struct fsal_up_vector *vec, struct gsh_buffdesc *handle, void (*cb)(void *, state_status_t), void *cb_arg) { struct delegrecall_args *args = NULL; int rc = 0; args = gsh_malloc(sizeof(struct delegrecall_args) + handle->len); args->vec = vec; args->cb = cb; args->cb_arg = cb_arg; memcpy(args->key, handle->addr, handle->len); args->handle.addr = args->key; args->handle.len = handle->len; rc = fridgethr_submit(fr, up_queue_delegrecall, args); if (rc != 0) gsh_free(args); return fsalstat(posix2fsal_error(rc), rc); } /** @} */ nfs-ganesha-2.6.0/src/FSAL_UP/fsal_up_top.c000066400000000000000000001300041324272410200203030ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @addtogroup fsal_up * @{ */ /** * @file fsal_up_top.c * @author Adam C. Emerson * @brief Top level FSAL Upcall handlers */ #include "config.h" #include #include #include #include "nfs_core.h" #include "log.h" #include "fsal.h" #include "hashtable.h" #include "fsal_up.h" #include "sal_functions.h" #include "pnfs_utils.h" #include "nfs_rpc_callback.h" #include "nfs_proto_tools.h" #include "nfs_convert.h" #include "delayed_exec.h" #include "export_mgr.h" #include "server_stats.h" struct delegrecall_context { /* Reserve lease during delegation recall */ nfs_client_id_t *drc_clid; /* Preserve the stateid we are recalling */ stateid4 drc_stateid; /* Hold a reference to the export during delegation recall */ struct gsh_export *drc_exp; }; enum recall_resp_action { DELEG_RECALL_SCHED, DELEG_RET_WAIT, REVOKE }; static int schedule_delegrevoke_check(struct delegrecall_context *ctx, uint32_t delay); static int schedule_delegrecall_task(struct delegrecall_context *ctx, uint32_t delay); /** Invalidate some or all of a cache entry and close if open * * This version should NOT be used if an FSAL supports extended * operations, instead, the FSAL may directly close the file as * necessary. * * @param[in] vec Up ops vector * @param[in] handle Handle being invalidated * @param[in] flags Flags governing invalidation * * @return FSAL status * */ static fsal_status_t invalidate_close(const struct fsal_up_vector *vec, struct gsh_buffdesc *handle, uint32_t flags) { return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** Invalidate some or all of a cache entry * * @param[in] vec Up ops vector * @param[in] handle Handle being invalidated * @param[in] flags Flags governing invalidation * * @return FSAL status * */ fsal_status_t invalidate(const struct fsal_up_vector *vec, struct gsh_buffdesc *handle, uint32_t flags) { /* No need to invalidate with no cache */ return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Update cached attributes * * @param[in] vec Up ops vector * @param[in] obj Key to specify object * @param[in] attr New attributes * @param[in] flags Flags to govern update * * @return FSAL status */ static fsal_status_t update(const struct fsal_up_vector *vec, struct gsh_buffdesc *obj, struct attrlist *attr, uint32_t flags) { /* No need to update with no cache */ return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Initiate a lock grant * * @param[in] file The file in question * @param[in] owner The lock owner * @param[in] lock_param description of the lock * * @return STATE_SUCCESS or errors. */ static state_status_t lock_grant(const struct fsal_up_vector *vec, struct gsh_buffdesc *file, void *owner, fsal_lock_param_t *lock_param) { struct fsal_obj_handle *obj; fsal_status_t status; struct fsal_export *export = vec->up_fsal_export; status = export->exp_ops.create_handle(export, file, &obj, NULL); if (FSAL_IS_ERROR(status)) return STATE_NOT_FOUND; grant_blocked_lock_upcall(obj, owner, lock_param); obj->obj_ops.put_ref(obj); return STATE_SUCCESS; } /** * @brief Signal lock availability * * @param[in] file The file in question * @param[in] owner The lock owner * @param[in] lock_param description of the lock * * @return STATE_SUCCESS or errors. */ static state_status_t lock_avail(const struct fsal_up_vector *vec, struct gsh_buffdesc *file, void *owner, fsal_lock_param_t *lock_param) { struct fsal_obj_handle *obj; fsal_status_t status; struct fsal_export *export = vec->up_fsal_export; status = export->exp_ops.create_handle(export, file, &obj, NULL); if (FSAL_IS_ERROR(status)) return STATE_NOT_FOUND; available_blocked_lock_upcall(obj, owner, lock_param); obj->obj_ops.put_ref(obj); return STATE_SUCCESS; } /* @note The state_lock MUST be held for write */ static void destroy_recall(struct state_layout_recall_file *recall) { if (recall == NULL) return; while (!glist_empty(&recall->state_list)) { struct recall_state_list *list_entry; /* The first entry in the queue */ list_entry = glist_first_entry(&recall->state_list, struct recall_state_list, link); dec_state_t_ref(list_entry->state); glist_del(&list_entry->link); gsh_free(list_entry); } /* Remove from entry->layoutrecall_list */ glist_del(&recall->entry_link); gsh_free(recall); } /** * @brief Create layout recall state * * This function creates the layout recall state and work list for a * LAYOUTRECALL operation on a file. * * @note the state_lock MUST be held for write * * @param[in,out] obj The file on which to send the recall * @param[in] type The layout type * @param[in] offset The offset of the interval to recall * @param[in] length The length of the interval to recall * @param[in] cookie The recall cookie (to be returned to the FSAL * on the final return satisfying this recall.) * @param[in] spec Lets us be fussy about what clients we send * to. May be NULL. * @param[out] recout The recall object * * @retval STATE_SUCCESS if successfully queued. * @retval STATE_INVALID_ARGUMENT if the range is zero or overflows. * @retval STATE_NOT_FOUND if no layouts satisfying the range exist. */ static state_status_t create_file_recall(struct fsal_obj_handle *obj, layouttype4 type, const struct pnfs_segment *segment, void *cookie, struct layoutrecall_spec *spec, struct state_layout_recall_file **recout) { /* True if a layout matching the request has been found */ bool found = false; /* Iterator over all states on the cache entry */ struct glist_head *state_iter = NULL; /* Error return code */ state_status_t rc = STATE_SUCCESS; /* The recall object referenced by future returns */ struct state_layout_recall_file *recall = gsh_malloc(sizeof(struct state_layout_recall_file)); glist_init(&recall->state_list); recall->entry_link.next = NULL; recall->entry_link.prev = NULL; recall->obj = obj; recall->type = type; recall->segment = *segment; recall->recall_cookie = cookie; if ((segment->length == 0) || ((segment->length != UINT64_MAX) && (segment->offset <= UINT64_MAX - segment->length))) { rc = STATE_INVALID_ARGUMENT; goto out; } glist_for_each(state_iter, &obj->state_hdl->file.list_of_states) { /* Entry in the state list */ struct recall_state_list *list_entry = NULL; /* Iterator over segments on this state */ struct glist_head *seg_iter = NULL; /* The state under examination */ state_t *s = glist_entry(state_iter, state_t, state_list); /* Does this state have a matching segment? */ bool match = false; /* referenced owner */ state_owner_t *owner = get_state_owner_ref(s); if (owner == NULL) { /* This state is going stale, skip */ continue; } if ((s->state_type != STATE_TYPE_LAYOUT) || (s->state_data.layout.state_layout_type != type)) { continue; } if (spec) { switch (spec->how) { case layoutrecall_howspec_exactly: if (spec->u.client != owner->so_owner.so_nfs4_owner.so_clientid) { dec_state_owner_ref(owner); continue; } break; case layoutrecall_howspec_complement: if (spec->u.client == owner->so_owner.so_nfs4_owner.so_clientid) { dec_state_owner_ref(owner); continue; } break; case layoutrecall_not_specced: break; } } dec_state_owner_ref(owner); glist_for_each(seg_iter, &s->state_data.layout.state_segments) { state_layout_segment_t *g = glist_entry( seg_iter, state_layout_segment_t, sls_state_segments); if (pnfs_segments_overlap(segment, &g->sls_segment)) match = true; } if (match) { /** * @todo This is where you would record that a * recall was initiated. The range recalled * is specified in @c segment. The clientid * is in * s->state_owner->so_owner.so_nfs4_owner.so_clientid * But you may want to ignore this location entirely. */ list_entry = gsh_malloc(sizeof(struct recall_state_list)); list_entry->state = s; glist_add_tail(&recall->state_list, &list_entry->link); inc_state_t_ref(s); found = true; } } if (!found) rc = STATE_NOT_FOUND; out: if (rc == STATE_SUCCESS) { glist_add_tail(&obj->state_hdl->file.layoutrecall_list, &recall->entry_link); *recout = recall; } else { /* Destroy the recall list constructed so far. */ destroy_recall(recall); } return rc; } static void layoutrecall_one_call(void *arg); /** * @brief Data used to handle the response to CB_LAYOUTRECALL */ struct layoutrecall_cb_data { char stateid_other[OTHERSIZE]; /*< "Other" part of state id */ struct pnfs_segment segment; /*< Segment to recall */ nfs_cb_argop4 arg; /*< So we don't free */ nfs_client_id_t *client; /*< The client we're calling. */ struct timespec first_recall; /*< Time of first recall */ uint32_t attempts; /*< Number of times we've recalled */ }; /** * @brief Initiate layout recall * * This function validates the recall, creates the recall object, and * sends out CB_LAYOUTRECALL messages. * * @param[in] handle Handle on which the layout is held * @param[in] layout_type The type of layout to recall * @param[in] changed Whether the layout has changed and the * client ought to finish writes through MDS * @param[in] segment Segment to recall * @param[in] cookie A cookie returned with the return that * completely satisfies a recall * @param[in] spec Lets us be fussy about what clients we send * to. May be NULL. * * @retval STATE_SUCCESS if scheduled. * @retval STATE_NOT_FOUND if no matching layouts exist. * @retval STATE_INVALID_ARGUMENT if a nonsensical layout recall has * been specified. * @retval STATE_MALLOC_ERROR if there was insufficient memory to construct the * recall state. */ state_status_t layoutrecall(const struct fsal_up_vector *vec, struct gsh_buffdesc *handle, layouttype4 layout_type, bool changed, const struct pnfs_segment *segment, void *cookie, struct layoutrecall_spec *spec) { /* Return code */ state_status_t rc = STATE_SUCCESS; /* file on which to operate */ struct fsal_obj_handle *obj = NULL; /* The recall object */ struct state_layout_recall_file *recall = NULL; /* Iterator over the work list */ struct glist_head *wi = NULL; struct gsh_export *exp = NULL; state_owner_t *owner = NULL; struct fsal_export *export = vec->up_fsal_export; rc = state_error_convert(export->exp_ops.create_handle(export, handle, &obj, NULL)); if (rc != STATE_SUCCESS) return rc; PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); /* We build up the list before consuming it so that we have every state on the list before we start executing returns. */ rc = create_file_recall(obj, layout_type, segment, cookie, spec, &recall); PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); if (rc != STATE_SUCCESS) goto out; /** * @todo This leaves us open to a race if a return comes in * while we're traversing the work list. However, the race may now * be harmless since everything is refcounted. */ glist_for_each(wi, &recall->state_list) { /* The current entry in the queue */ struct recall_state_list *g = glist_entry(wi, struct recall_state_list, link); struct state_t *s = g->state; struct layoutrecall_cb_data *cb_data; nfs_cb_argop4 *arg; CB_LAYOUTRECALL4args *cb_layoutrec; layoutrecall_file4 *layout; cb_data = gsh_malloc(sizeof(struct layoutrecall_cb_data)); arg = &cb_data->arg; arg->argop = NFS4_OP_CB_LAYOUTRECALL; cb_layoutrec = &arg->nfs_cb_argop4_u.opcblayoutrecall; layout = &cb_layoutrec->clora_recall.layoutrecall4_u.lor_layout; cb_layoutrec->clora_type = layout_type; cb_layoutrec->clora_iomode = segment->io_mode; cb_layoutrec->clora_changed = changed; cb_layoutrec->clora_recall.lor_recalltype = LAYOUTRECALL4_FILE; layout->lor_offset = segment->offset; layout->lor_length = segment->length; if (!get_state_obj_export_owner_refs(s, NULL, &exp, &owner)) { /* The export, owner, or state_t has gone stale, * skip this entry */ gsh_free(cb_data); continue; } if (!nfs4_FSALToFhandle(true, &layout->lor_fh, obj, exp)) { gsh_free(cb_data); put_gsh_export(exp); dec_state_owner_ref(owner); rc = STATE_MALLOC_ERROR; goto out; } put_gsh_export(exp); update_stateid(s, &layout->lor_stateid, NULL, "LAYOUTRECALL"); memcpy(cb_data->stateid_other, s->stateid_other, OTHERSIZE); cb_data->segment = *segment; cb_data->client = owner->so_owner.so_nfs4_owner.so_clientrec; cb_data->attempts = 0; dec_state_owner_ref(owner); layoutrecall_one_call(cb_data); } out: /* Free the recall list resources */ destroy_recall(recall); obj->obj_ops.put_ref(obj); return rc; } /** * @brief Free a CB_LAYOUTRECALL * * @param[in] op Operation to free */ static void free_layoutrec(nfs_cb_argop4 *op) { layoutrecall4 *clora_recall = &op->nfs_cb_argop4_u.opcblayoutrecall.clora_recall; gsh_free(clora_recall->layoutrecall4_u.lor_layout.lor_fh.nfs_fh4_val); } /** * @brief Complete a CB_LAYOUTRECALL * * This function handles the client response to a layoutrecall. In * the event of success it does nothing. In the case of most errors, * it revokes the layout. * * For NOMATCHINGLAYOUT, under the agreed-upon interpretation of the * forgetful model, it acts as if the client had returned a layout * exactly matching the recall. * * For DELAY, it backs off in plateaus, then revokes the layout if the * period of delay has surpassed the lease period. * * @param[in] call The RPC call being completed */ static void layoutrec_completion(rpc_call_t *call) { struct layoutrecall_cb_data *cb_data = call->call_arg; bool deleted = false; state_t *state = NULL; struct root_op_context root_op_context; struct fsal_obj_handle *obj = NULL; struct gsh_export *export = NULL; state_owner_t *owner = NULL; bool ok = false; /* Initialize req_ctx */ init_root_op_context(&root_op_context, NULL, NULL, 0, 0, UNKNOWN_REQUEST); LogFullDebug(COMPONENT_NFS_CB, "status %d cb_data %p", call->cbt.v_u.v4.res.status, cb_data); /* Get this out of the way up front */ if (call->states & NFS_CB_CALL_ABORTED) goto revoke; if (call->cbt.v_u.v4.res.status == NFS4_OK) { /** * @todo This is where you would record that a * recall was acknowledged and that a layoutreturn * will be sent later. * The number of times we retried the call is * specified in cb_data->attempts and the time we * specified the first call is in * cb_data->first_recall. * We don't have the clientid here. If you want it, * we could either move the stateid look up to be * above this point in the function, or we could stash * the clientid in cb_data. */ free_layoutrec(&call->cbt.v_u.v4.args.argarray.argarray_val[1]); nfs41_release_single(call); gsh_free(cb_data); goto out; } else if (call->cbt.v_u.v4.res.status == NFS4ERR_DELAY) { struct timespec current; nsecs_elapsed_t delay; now(¤t); if (timespec_diff(&cb_data->first_recall, ¤t) > (nfs_param.nfsv4_param.lease_lifetime * NS_PER_SEC)) { goto revoke; } if (cb_data->attempts < 5) delay = 0; else if (cb_data->attempts < 10) delay = 1 * NS_PER_MSEC; else if (cb_data->attempts < 20) delay = 10 * NS_PER_MSEC; else if (cb_data->attempts < 30) delay = 100 * NS_PER_MSEC; else delay = 1 * NS_PER_SEC; /* We don't free the argument here, because we'll be re-using that to make the queued call. */ nfs41_release_single(call); delayed_submit(layoutrecall_one_call, cb_data, delay); goto out; } /** * @todo Better error handling later when we have more * session/revocation infrastructure. */ revoke: /* If we don't find the state, there's nothing to return. */ state = nfs4_State_Get_Pointer(cb_data->stateid_other); ok = get_state_obj_export_owner_refs(state, &obj, &export, &owner); if (ok) { enum fsal_layoutreturn_circumstance circumstance; if (!(call->states & NFS_CB_CALL_ABORTED) && call->cbt.v_u.v4.res.status == NFS4ERR_NOMATCHING_LAYOUT) circumstance = circumstance_client; else circumstance = circumstance_revoke; /** * @todo This is where you would record that a * recall was completed, one way or the other. * The clientid is specified in * owner->so_owner.so_nfs4_owner.so_clientid * The number of times we retried the call is * specified in cb_data->attempts and the time we * specified the first call is in * cb_data->first_recall. If * call->cbt.v_u.v4.res.status is * NFS4ERR_NOMATCHING_LAYOUT it was a successful * return, otherwise we count it as an error. */ PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); root_op_context.req_ctx.clientid = &owner->so_owner.so_nfs4_owner.so_clientid; root_op_context.req_ctx.ctx_export = export; root_op_context.req_ctx.fsal_export = export->fsal_export; nfs4_return_one_state(obj, LAYOUTRETURN4_FILE, circumstance, state, cb_data->segment, 0, NULL, &deleted); PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); } if (state != NULL) { /* Release the reference taken above */ dec_state_t_ref(state); } free_layoutrec(&call->cbt.v_u.v4.args.argarray.argarray_val[1]); nfs41_release_single(call); gsh_free(cb_data); out: release_root_op_context(); if (ok) { /* Release the export */ put_gsh_export(export); /* Release object ref */ obj->obj_ops.put_ref(obj); /* Release the owner */ dec_state_owner_ref(owner); } } /** * @brief Return one layout on error * * This is only invoked in the case of a send error on the first * attempt to issue a CB_LAYOUTRECALL, so that we don't call into the * FSAL's layoutreturn function while its layoutrecall function may be * holding locks. * * @param[in] arg Structure holding all arguments, so we can queue * this function in delayed_exec. */ static void return_one_async(void *arg) { struct layoutrecall_cb_data *cb_data = arg; state_t *state; bool deleted = false; struct root_op_context root_op_context; struct fsal_obj_handle *obj = NULL; struct gsh_export *export = NULL; state_owner_t *owner = NULL; bool ok = false; /* Initialize req_ctx */ init_root_op_context(&root_op_context, NULL, NULL, 0, 0, UNKNOWN_REQUEST); state = nfs4_State_Get_Pointer(cb_data->stateid_other); ok = get_state_obj_export_owner_refs(state, &obj, &export, &owner); if (ok) { PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); root_op_context.req_ctx.clientid = &owner->so_owner.so_nfs4_owner.so_clientid; root_op_context.req_ctx.ctx_export = export; root_op_context.req_ctx.fsal_export = export->fsal_export; nfs4_return_one_state(obj, LAYOUTRETURN4_FILE, circumstance_revoke, state, cb_data->segment, 0, NULL, &deleted); PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); } release_root_op_context(); gsh_free(cb_data); if (state != NULL) { /* Release the reference taken above */ dec_state_t_ref(state); } if (ok) { /* Release the export */ put_gsh_export(export); /* Release object ref */ obj->obj_ops.put_ref(obj); /* Release the owner */ dec_state_owner_ref(owner); } } /** * @brief Send one layoutrecall to one client * * @param[in] arg Structure holding all arguments, so we can queue * this function in delayed_exec for retry on NFS4ERR_DELAY. */ static void layoutrecall_one_call(void *arg) { struct layoutrecall_cb_data *cb_data = arg; state_t *state; int code; struct root_op_context root_op_context; struct fsal_obj_handle *obj = NULL; struct gsh_export *export = NULL; state_owner_t *owner = NULL; bool ok = false; /* Initialize req_ctx */ init_root_op_context(&root_op_context, NULL, NULL, 0, 0, UNKNOWN_REQUEST); if (cb_data->attempts == 0) now(&cb_data->first_recall); state = nfs4_State_Get_Pointer(cb_data->stateid_other); ok = get_state_obj_export_owner_refs(state, &obj, &export, &owner); if (ok) { PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); root_op_context.req_ctx.clientid = &owner->so_owner.so_nfs4_owner.so_clientid; root_op_context.req_ctx.ctx_export = export; root_op_context.req_ctx.fsal_export = export->fsal_export; code = nfs_rpc_cb_single(cb_data->client, &cb_data->arg, &state->state_refer, layoutrec_completion, cb_data); if (code != 0) { /** * @todo On failure to submit a callback, we * ought to give the client at least one lease * period to establish a back channel before * we start revoking state. We don't have the * infrasturcture to properly handle layout * revocation, however. Once we get the * capability to revoke layouts we should * queue requests on the clientid, obey the * retransmission rule, and provide a callback * to dispose of a call and revoke state after * some number of lease periods. * * At present we just assume the client has * gone completely out to lunch and fake a * return. */ /** * @todo This is where you would record that a * recall failed. (It indicates a transport error.) * The clientid is specified in * s->state_owner->so_owner.so_nfs4_owner.so_clientid * The number of times we retried the call is * specified in cb_data->attempts and the time * we specified the first call is in * cb_data->first_recall. */ if (cb_data->attempts == 0) { delayed_submit(return_one_async, cb_data, 0); } else { bool deleted = false; nfs4_return_one_state(obj, LAYOUTRETURN4_FILE, circumstance_revoke, state, cb_data->segment, 0, NULL, &deleted); free_layoutrec(&cb_data->arg); gsh_free(cb_data); } } else { ++cb_data->attempts; } PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); } else { gsh_free(cb_data); } release_root_op_context(); if (state != NULL) { /* Release the reference taken above */ dec_state_t_ref(state); } if (ok) { /* Release the export */ put_gsh_export(export); /* Release object ref */ obj->obj_ops.put_ref(obj); /* Release the owner */ dec_state_owner_ref(owner); } } /** * @brief Data for CB_NOTIFY and CB_NOTIFY_DEVICEID response handler */ struct cb_notify { nfs_cb_argop4 arg; /*< Arguments (so we can free them) */ struct notify4 notify; /*< For notify response */ struct notify_deviceid_delete4 notify_del; /*< For notify_deviceid response. */ }; /** * @brief Handle CB_NOTIFY_DEVICE response * * @param[in] call The RPC call being completed */ static void notifydev_completion(rpc_call_t *call) { LogFullDebug(COMPONENT_NFS_CB, "status %d arg %p", call->cbt.v_u.v4.res.status, call->call_arg); gsh_free(call->call_arg); } /** * The arguments for devnotify_client_callback packed up in a struct */ struct devnotify_cb_data { notify_deviceid_type4 notify_type; layouttype4 layout_type; struct pnfs_deviceid devid; }; /** * @brief Send a single notifydev to a single client * * @param[in] clientid The client record * @param[in] devnotify The device notify args * * @return True on success, false on error. */ static bool devnotify_client_callback(nfs_client_id_t *clientid, void *devnotify) { int code = 0; CB_NOTIFY_DEVICEID4args *cb_notify_dev; struct cb_notify *arg; struct devnotify_cb_data *devicenotify = devnotify; if (clientid) { LogFullDebug(COMPONENT_NFS_CB, "CliP %p ClientID=%" PRIx64 " ver %d", clientid, clientid->cid_clientid, clientid->cid_minorversion); } else { return false; } /* free in notifydev_completion */ arg = gsh_malloc(sizeof(struct cb_notify)); cb_notify_dev = &arg->arg.nfs_cb_argop4_u.opcbnotify_deviceid; arg->arg.argop = NFS4_OP_CB_NOTIFY_DEVICEID; cb_notify_dev->cnda_changes.cnda_changes_len = 1; cb_notify_dev->cnda_changes.cnda_changes_val = &arg->notify; arg->notify.notify_mask.bitmap4_len = 1; arg->notify.notify_mask.map[0] = devicenotify->notify_type; arg->notify.notify_vals.notifylist4_len = sizeof(struct notify_deviceid_delete4); arg->notify.notify_vals.notifylist4_val = (char *)&arg->notify_del; arg->notify_del.ndd_layouttype = devicenotify->layout_type; memcpy(arg->notify_del.ndd_deviceid, &devicenotify->devid, sizeof(arg->notify_del.ndd_deviceid)); code = nfs_rpc_cb_single(clientid, &arg->arg, NULL, notifydev_completion, &arg->arg); if (code != 0) gsh_free(arg); return true; } /** * @brief Remove or change a deviceid * * @param[in] dev_exportid Export responsible for the device ID * @param[in] notify_type Change or remove * @param[in] layout_type The layout type affected * @param[in] devid The lower quad of the device id, unique * within this export * @param[in] immediate Whether the change is immediate (in the case * of a change.) * * @return STATE_SUCCESS or errors. */ state_status_t notify_device(notify_deviceid_type4 notify_type, layouttype4 layout_type, struct pnfs_deviceid devid, bool immediate) { struct devnotify_cb_data *cb_data; cb_data = gsh_malloc(sizeof(struct devnotify_cb_data)); cb_data->notify_type = notify_type; cb_data->layout_type = layout_type; cb_data->devid = devid; nfs41_foreach_client_callback(devnotify_client_callback, cb_data); return STATE_SUCCESS; } /** * @brief Check if the delegation needs to be revoked. * * @param[in] deleg_entry SLE entry for the delegaion * * @return true, if the delegation need to be revoked. * @return false, if the delegation should not be revoked. */ bool eval_deleg_revoke(struct state_t *deleg_state) { struct cf_deleg_stats *clfl_stats; time_t curr_time; time_t recall_success_time, first_recall_time; uint32_t lease_lifetime = nfs_param.nfsv4_param.lease_lifetime; clfl_stats = &deleg_state->state_data.deleg.sd_clfile_stats; curr_time = time(NULL); recall_success_time = clfl_stats->cfd_rs_time; first_recall_time = clfl_stats->cfd_r_time; if ((recall_success_time > 0) && (curr_time - recall_success_time) > lease_lifetime) { LogInfo(COMPONENT_STATE, "More than one lease time has passed since recall was successfully sent"); return true; } if ((first_recall_time > 0) && (curr_time - first_recall_time) > (2 * lease_lifetime)) { LogInfo(COMPONENT_STATE, "More than two lease times have passed since recall was attempted"); return true; } return false; } /** * @brief Handle recall response * * @param[in] call The RPC call being completed * @param[in] clfl_stats client-file deleg heuristics * @param[in] p_cargs deleg recall context * */ static enum recall_resp_action handle_recall_response( struct delegrecall_context *p_cargs, struct state_t *state, rpc_call_t *call) { enum recall_resp_action resp_action; char str[DISPLAY_STATEID_OTHER_SIZE] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; bool str_valid = false; if (isDebug(COMPONENT_NFS_CB)) { display_stateid_other(&dspbuf, p_cargs->drc_stateid.other); str_valid = true; } struct cf_deleg_stats *clfl_stats = &state->state_data.deleg.sd_clfile_stats; switch (call->cbt.v_u.v4.res.status) { case NFS4_OK: if (str_valid) LogDebug(COMPONENT_NFS_CB, "Delegation %s successfully recalled", str); resp_action = DELEG_RET_WAIT; clfl_stats->cfd_rs_time = time(NULL); break; case NFS4ERR_BADHANDLE: if (str_valid) LogDebug(COMPONENT_NFS_CB, "Client sent NFS4ERR_BADHANDLE response, retrying recall for Delegation %s", str); resp_action = DELEG_RECALL_SCHED; break; case NFS4ERR_DELAY: if (str_valid) LogDebug(COMPONENT_NFS_CB, "Client sent NFS4ERR_DELAY response, retrying recall for Delegation %s", str); resp_action = DELEG_RECALL_SCHED; break; case NFS4ERR_BAD_STATEID: if (str_valid) LogDebug(COMPONENT_NFS_CB, "Client sent NFS4ERR_BAD_STATEID response, retrying recall for Delegation %s", str); resp_action = DELEG_RECALL_SCHED; break; default: /* some other NFS error, consider the recall failed */ if (str_valid) LogDebug(COMPONENT_NFS_CB, "Client sent %d response, retrying recall for Delegation %s", call->cbt.v_u.v4.res.status, str); resp_action = DELEG_RECALL_SCHED; break; } return resp_action; } static inline void free_delegrecall_context(struct delegrecall_context *deleg_ctx) { PTHREAD_MUTEX_lock(&deleg_ctx->drc_clid->cid_mutex); update_lease(deleg_ctx->drc_clid); PTHREAD_MUTEX_unlock(&deleg_ctx->drc_clid->cid_mutex); put_gsh_export(deleg_ctx->drc_exp); dec_client_id_ref(deleg_ctx->drc_clid); gsh_free(deleg_ctx); } /** * @brief Handle the reply to a CB_RECALL * * @param[in] call The RPC call being completed * * @return 0, constantly. */ static void delegrecall_completion_func(rpc_call_t *call) { enum recall_resp_action resp_act; nfsstat4 rc = NFS4_OK; struct delegrecall_context *deleg_ctx = call->call_arg; uint32_t minorversion = deleg_ctx->drc_clid->cid_minorversion; struct state_t *state; struct fsal_obj_handle *obj = NULL; char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; CB_RECALL4args *opcbrecall; struct req_op_context *save_ctx = op_ctx, req_ctx = {0}; struct gsh_export *export = NULL; bool ret = false; LogDebug(COMPONENT_NFS_CB, "%p %s", call, !(call->states & NFS_CB_CALL_ABORTED) ? "Success" : "Failed"); state = nfs4_State_Get_Pointer(deleg_ctx->drc_stateid.other); if (state == NULL) { LogDebug(COMPONENT_NFS_CB, "Delegation is already returned"); goto out_free_drc; } ret = get_state_obj_export_owner_refs(state, &obj, &export, NULL); if (!ret || obj == NULL) { LogDebug(COMPONENT_NFS_CB, "Stale file"); goto out_free_drc; } op_ctx = &req_ctx; op_ctx->ctx_export = export; op_ctx->fsal_export = export->fsal_export; if (isDebug(COMPONENT_NFS_CB)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_stateid(&dspbuf, state); LogDebug(COMPONENT_NFS_CB, "deleg_entry %s", str); } if (!(call->states & NFS_CB_CALL_ABORTED)) { LogMidDebug(COMPONENT_NFS_CB, "call result: %d", call->call_req.cc_error.re_status); if (call->call_req.cc_error.re_status != RPC_SUCCESS) { LogEvent(COMPONENT_NFS_CB, "call result: %d, marking CB channel down", call->call_req.cc_error.re_status); set_cb_chan_down(deleg_ctx->drc_clid, true); resp_act = DELEG_RECALL_SCHED; } else resp_act = handle_recall_response(deleg_ctx, state, call); } else { LogEvent(COMPONENT_NFS_CB, "Aborted: %d, marking CB channel down", call->call_req.cc_error.re_status); set_cb_chan_down(deleg_ctx->drc_clid, true); /* Mark the recall as failed */ resp_act = DELEG_RECALL_SCHED; } switch (resp_act) { case DELEG_RECALL_SCHED: if (eval_deleg_revoke(state)) goto out_revoke; else { if (schedule_delegrecall_task(deleg_ctx, 1)) goto out_revoke; goto out_free; } break; case DELEG_RET_WAIT: if (schedule_delegrevoke_check(deleg_ctx, 1)) goto out_revoke; goto out_free; case REVOKE: goto out_revoke; } out_revoke: display_stateid(&dspbuf, state); LogCrit(COMPONENT_NFS_V4, "Revoking delegation for %s", str); deleg_ctx->drc_clid->num_revokes++; inc_revokes(deleg_ctx->drc_clid->gsh_client); rc = deleg_revoke(obj, state); if (rc != NFS4_OK) { LogCrit(COMPONENT_NFS_V4, "Delegation could not be revoked for %s", str); } else { LogDebug(COMPONENT_NFS_V4, "Delegation revoked for %s", str); } out_free_drc: free_delegrecall_context(deleg_ctx); out_free: if (minorversion == 0) { opcbrecall = &call->cbt.v_u.v4.args.argarray.argarray_val[0] .nfs_cb_argop4_u.opcbrecall; nfs4_freeFH(&opcbrecall->fh); } else { opcbrecall = &call->cbt.v_u.v4.args.argarray.argarray_val[1] .nfs_cb_argop4_u.opcbrecall; nfs4_freeFH(&opcbrecall->fh); nfs41_release_single(call); } if (state != NULL) dec_state_t_ref(state); /* Release the obj ref and export ref. */ if (obj != NULL) obj->obj_ops.put_ref(obj); if (export != NULL) put_gsh_export(export); op_ctx = save_ctx; } /** * @brief Send one delegation recall to one client. * * This function sends a cb_recall for one delegation, the caller has to lock * cache_entry->state_lock before calling this function. * * @param[in] obj The file being delegated * @param[in] deleg_entry Lock entry covering the delegation * @param[in] delegrecall_context */ void delegrecall_one(struct fsal_obj_handle *obj, struct state_t *state, struct delegrecall_context *p_cargs) { int ret; nfs_cb_argop4 argop; struct cf_deleg_stats *clfl_stats; char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; bool str_valid = false; clfl_stats = &state->state_data.deleg.sd_clfile_stats; if (isDebug(COMPONENT_FSAL_UP)) { display_stateid(&dspbuf, state); str_valid = true; } /* record the first attempt to recall this delegation */ if (clfl_stats->cfd_r_time == 0) clfl_stats->cfd_r_time = time(NULL); if (str_valid) LogFullDebug(COMPONENT_FSAL_UP, "Recalling delegation %s", str); inc_recalls(p_cargs->drc_clid->gsh_client); argop.argop = NFS4_OP_CB_RECALL; COPY_STATEID(&argop.nfs_cb_argop4_u.opcbrecall.stateid, state); argop.nfs_cb_argop4_u.opcbrecall.truncate = false; /* Convert it to a file handle */ if (!nfs4_FSALToFhandle(true, &argop.nfs_cb_argop4_u.opcbrecall.fh, obj, p_cargs->drc_exp)) { LogCrit(COMPONENT_FSAL_UP, "nfs4_FSALToFhandle failed, can not process recall"); goto out; } ret = nfs_rpc_cb_single(p_cargs->drc_clid, &argop, &state->state_refer, delegrecall_completion_func, p_cargs); if (ret == 0) return; LogDebug(COMPONENT_FSAL_UP, "nfs_rpc_cb_single returned %d", ret); out: inc_failed_recalls(p_cargs->drc_clid->gsh_client); nfs4_freeFH(&argop.nfs_cb_argop4_u.opcbrecall.fh); if (!eval_deleg_revoke(state) && !schedule_delegrecall_task(p_cargs, 1)) { /* Keep the delegation in p_cargs */ if (str_valid) LogDebug(COMPONENT_FSAL_UP, "Retry delegation for %s", str); return; } if (!str_valid) display_stateid(&dspbuf, state); LogCrit(COMPONENT_STATE, "Delegation will be revoked for %s", str); p_cargs->drc_clid->num_revokes++; inc_revokes(p_cargs->drc_clid->gsh_client); if (deleg_revoke(obj, state) != NFS4_OK) { LogDebug(COMPONENT_FSAL_UP, "Failed to revoke delegation %s.", str); } else { LogDebug(COMPONENT_FSAL_UP, "Delegation revoked %s", str); } free_delegrecall_context(p_cargs); } /** * @brief Check if the delegation needs to be revoked. * * @param[in] ctx Delegation recall context describing the delegation */ static void delegrevoke_check(void *ctx) { nfsstat4 rc = NFS4_OK; struct delegrecall_context *deleg_ctx = ctx; struct fsal_obj_handle *obj = NULL; struct state_t *state = NULL; bool free_drc = true; char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; bool str_valid = false; struct req_op_context *save_ctx = op_ctx, req_ctx = {0}; struct gsh_export *export = NULL; bool ret = false; state = nfs4_State_Get_Pointer(deleg_ctx->drc_stateid.other); if (state == NULL) { LogDebug(COMPONENT_NFS_CB, "Delegation is already returned"); goto out; } if (isDebug(COMPONENT_NFS_CB)) { display_stateid(&dspbuf, state); str_valid = true; } ret = get_state_obj_export_owner_refs(state, &obj, &export, NULL); if (!ret || obj == NULL) { LogDebug(COMPONENT_NFS_CB, "Stale file"); goto out; } /* op_ctx may be used by state_del_locked and others */ op_ctx = &req_ctx; op_ctx->ctx_export = export; op_ctx->fsal_export = export->fsal_export; if (eval_deleg_revoke(state)) { if (str_valid) LogDebug(COMPONENT_STATE, "Revoking delegation for %s", str); rc = deleg_revoke(obj, state); if (rc != NFS4_OK) { if (!str_valid) display_stateid(&dspbuf, state); LogCrit(COMPONENT_NFS_V4, "Delegation could not be revoked for %s", str); } else { if (str_valid) LogDebug(COMPONENT_NFS_V4, "Delegation revoked for %s", str); } } else { if (str_valid) LogFullDebug(COMPONENT_STATE, "Not yet revoking the delegation for %s", str); schedule_delegrevoke_check(deleg_ctx, 1); free_drc = false; } out: if (free_drc) free_delegrecall_context(deleg_ctx); if (state != NULL) dec_state_t_ref(state); /* Release the obj ref and export ref. */ if (obj != NULL) obj->obj_ops.put_ref(obj); if (export != NULL) put_gsh_export(export); op_ctx = save_ctx; } static void delegrecall_task(void *ctx) { struct delegrecall_context *deleg_ctx = ctx; struct state_t *state; struct fsal_obj_handle *obj; bool ret = false; struct req_op_context *save_ctx, req_ctx = {0}; struct gsh_export *export = NULL; state = nfs4_State_Get_Pointer(deleg_ctx->drc_stateid.other); if (state == NULL) { LogDebug(COMPONENT_NFS_CB, "Delgation is already returned"); free_delegrecall_context(deleg_ctx); } else { ret = get_state_obj_export_owner_refs(state, &obj, &export, NULL); if (!ret || obj == NULL) { LogDebug(COMPONENT_NFS_CB, "Delgation recall skipped due to stale file"); goto out; } /* op_ctx may be used by state_del_locked and others */ save_ctx = op_ctx; op_ctx = &req_ctx; op_ctx->ctx_export = export; op_ctx->fsal_export = export->fsal_export; delegrecall_one(obj, state, deleg_ctx); /* Release the obj ref and export ref. */ obj->obj_ops.put_ref(obj); put_gsh_export(export); op_ctx = save_ctx; out: dec_state_t_ref(state); } } static int schedule_delegrecall_task(struct delegrecall_context *ctx, uint32_t delay) { int rc = 0; assert(ctx); rc = delayed_submit(delegrecall_task, ctx, delay * NS_PER_SEC); if (rc) LogDebug(COMPONENT_THREAD, "delayed_submit failed with rc = %d", rc); return rc; } static int schedule_delegrevoke_check(struct delegrecall_context *ctx, uint32_t delay) { int rc = 0; assert(ctx); rc = delayed_submit(delegrevoke_check, ctx, delay * NS_PER_SEC); if (rc) LogDebug(COMPONENT_THREAD, "delayed_submit failed with rc = %d", rc); return rc; } state_status_t delegrecall_impl(struct fsal_obj_handle *obj) { struct glist_head *glist, *glist_n; state_status_t rc = 0; uint32_t *deleg_state = NULL; struct state_t *state; state_owner_t *owner; struct delegrecall_context *drc_ctx; struct req_op_context *save_ctx = op_ctx, req_ctx = {0}; LogDebug(COMPONENT_FSAL_UP, "FSAL_UP_DELEG: obj %p type %u", obj, obj->type); PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); glist_for_each_safe(glist, glist_n, &obj->state_hdl->file.list_of_states) { state = glist_entry(glist, struct state_t, state_list); if (state->state_type != STATE_TYPE_DELEG) continue; if (isDebug(COMPONENT_NFS_CB)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_stateid(&dspbuf, state); LogDebug(COMPONENT_NFS_CB, "Delegation for %s", str); } deleg_state = &state->state_data.deleg.sd_state; if (*deleg_state != DELEG_GRANTED) { LogDebug(COMPONENT_FSAL_UP, "Delegation already being recalled, NOOP"); continue; } *deleg_state = DELEG_RECALL_WIP; drc_ctx = gsh_malloc(sizeof(struct delegrecall_context)); /* Get references on the owner and the the export. The * export reference we will hold while we perform the recall. * The owner reference will be used to get access to the * clientid and reserve the lease. */ if (!get_state_obj_export_owner_refs(state, NULL, &drc_ctx->drc_exp, &owner)) { LogDebug(COMPONENT_FSAL_UP, "Something is going stale, no need to recall delegation"); gsh_free(drc_ctx); continue; } /* op_ctx may be used by state_del_locked and others */ op_ctx = &req_ctx; op_ctx->ctx_export = drc_ctx->drc_exp; op_ctx->fsal_export = drc_ctx->drc_exp->fsal_export; drc_ctx->drc_clid = owner->so_owner.so_nfs4_owner.so_clientrec; COPY_STATEID(&drc_ctx->drc_stateid, state); inc_client_id_ref(drc_ctx->drc_clid); dec_state_owner_ref(owner); obj->state_hdl->file.fdeleg_stats.fds_last_recall = time(NULL); /* Prevent client's lease expiring until we complete * this recall/revoke operation. If the client's lease * has already expired, let the reaper thread handling * expired clients revoke this delegation, and we just * skip it here. */ PTHREAD_MUTEX_lock(&drc_ctx->drc_clid->cid_mutex); if (!reserve_lease(drc_ctx->drc_clid)) { PTHREAD_MUTEX_unlock(&drc_ctx->drc_clid->cid_mutex); put_gsh_export(drc_ctx->drc_exp); dec_client_id_ref(drc_ctx->drc_clid); gsh_free(drc_ctx); continue; } PTHREAD_MUTEX_unlock(&drc_ctx->drc_clid->cid_mutex); delegrecall_one(obj, state, drc_ctx); } PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); op_ctx = save_ctx; return rc; } /** * @brief Recall a delegation * * @param[in] handle Handle on which the delegation is held * * @return STATE_SUCCESS or errors. */ state_status_t delegrecall(const struct fsal_up_vector *vec, struct gsh_buffdesc *handle) { struct fsal_export *export = vec->up_fsal_export; struct fsal_obj_handle *obj = NULL; state_status_t rc = 0; if (!nfs_param.nfsv4_param.allow_delegations) { LogCrit(COMPONENT_FSAL_UP, "BUG: Got BREAK_DELEGATION: upcall when delegations are disabled, ignoring"); return STATE_SUCCESS; } rc = state_error_convert(export->exp_ops.create_handle(export, handle, &obj, NULL)); if (rc != STATE_SUCCESS) { LogDebug(COMPONENT_FSAL_UP, "FSAL_UP_DELEG: cache inode get failed, rc %d", rc); /* Not an error. Expecting some nodes will not have it * in cache in a cluster. */ return rc; } rc = delegrecall_impl(obj); obj->obj_ops.put_ref(obj); return rc; } void up_ready_init(struct fsal_up_vector *up_ops) { up_ops->up_ready = false; up_ops->up_cancel = false; PTHREAD_MUTEX_init(&up_ops->up_mutex, NULL); PTHREAD_COND_init(&up_ops->up_cond, NULL); } /* This should be called when a module is ready to take upcalls */ void up_ready_set(struct fsal_up_vector *up_ops) { PTHREAD_MUTEX_lock(&up_ops->up_mutex); up_ops->up_ready = true; pthread_cond_broadcast(&up_ops->up_cond); PTHREAD_MUTEX_unlock(&up_ops->up_mutex); } /* This is to cancel waiting and resume Upcall threads */ void up_ready_cancel(struct fsal_up_vector *up_ops) { PTHREAD_MUTEX_lock(&up_ops->up_mutex); up_ops->up_cancel = true; pthread_cond_broadcast(&up_ops->up_cond); PTHREAD_MUTEX_unlock(&up_ops->up_mutex); } /* Upcall threads should call this to wait for upcall readiness */ void up_ready_wait(struct fsal_up_vector *up_ops) { PTHREAD_MUTEX_lock(&up_ops->up_mutex); while (!up_ops->up_ready && !up_ops->up_cancel) pthread_cond_wait(&up_ops->up_cond, &up_ops->up_mutex); PTHREAD_MUTEX_unlock(&up_ops->up_mutex); } /** * @brief The top level vector of operations * * This is the basis for UP calls. It should not be used directly, but copied * into a per-export structure, overridden if necessary, and fsal_export set. * FSAL_MDCACHE does this, so any cached FSALs don't need to worry. */ struct fsal_up_vector fsal_up_top = { .up_gsh_export = NULL, .up_fsal_export = NULL, .lock_grant = lock_grant, .lock_avail = lock_avail, .invalidate = invalidate, .update = update, .layoutrecall = layoutrecall, .notify_device = notify_device, .delegrecall = delegrecall, .invalidate_close = invalidate_close }; /** @} */ nfs-ganesha-2.6.0/src/KNOWN_BUGS000066400000000000000000000005131324272410200161330ustar00rootroot00000000000000This file references the known bugs (some of them are under investigation) - All operations made via FSAL_PROXY are done as root when RPCSEC_GSS is used to contact the remote server ------------------ Fixed bug - A deadlock may occur inbetween workers and tcp dispatchers in case of important traffic (fixed in 0.99.46) nfs-ganesha-2.6.0/src/LICENSE.txt000066400000000000000000000167431324272410200163330ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. nfs-ganesha-2.6.0/src/MainNFSD/000077500000000000000000000000001324272410200160345ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/MainNFSD/.gitignore000066400000000000000000000000161324272410200200210ustar00rootroot00000000000000*ganesha.nfsd nfs-ganesha-2.6.0/src/MainNFSD/9p_dispatcher.c000066400000000000000000000654661324272410200207570ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_dispatcher.c * \date $Date: 2006/02/23 12:33:05 $ * \brief 9P protocol dispatch thread. * * The file that contains the '_9p_dispatcher_thread' routine for ganesha * (and all the related stuff). * */ #include "config.h" #include #include #include #include #include /* for having FNDELAY */ #include #include #include #include #include #include #include /* For inet_ntop() */ #include "hashtable.h" #include "log.h" #include "abstract_mem.h" #include "abstract_atomic.h" #include "nfs_init.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_file_handle.h" #include "nfs_req_queue.h" #include "client_mgr.h" #include "server_stats.h" #include "9p.h" #include #define P_FAMILY AF_INET6 static struct fridgethr *worker_fridge; static struct nfs_req_st nfs_req_st; /*< Shared request queues */ static const char *req_q_s[N_REQ_QUEUES] = { "REQ_Q_LOW_LATENCY", }; /* static */ uint32_t _9p_outstanding_reqs_est(void) { static uint32_t ctr; static uint32_t nreqs; struct req_q_pair *qpair; uint32_t treqs; int ix; if ((atomic_inc_uint32_t(&ctr) % 10) != 0) return atomic_fetch_uint32_t(&nreqs); treqs = 0; for (ix = 0; ix < N_REQ_QUEUES; ++ix) { qpair = &(nfs_req_st.reqs.nfs_request_q.qset[ix]); treqs += atomic_fetch_uint32_t(&qpair->producer.size); treqs += atomic_fetch_uint32_t(&qpair->consumer.size); } atomic_store_uint32_t(&nreqs, treqs); return treqs; } static inline request_data_t *_9p_consume_req(struct req_q_pair *qpair) { request_data_t *reqdata = NULL; pthread_spin_lock(&qpair->consumer.sp); if (qpair->consumer.size > 0) { reqdata = glist_first_entry(&qpair->consumer.q, request_data_t, req_q); glist_del(&reqdata->req_q); --(qpair->consumer.size); pthread_spin_unlock(&qpair->consumer.sp); goto out; } else { char *s = NULL; uint32_t csize = ~0U; uint32_t psize = ~0U; pthread_spin_lock(&qpair->producer.sp); if (isFullDebug(COMPONENT_DISPATCH)) { s = (char *)qpair->s; csize = qpair->consumer.size; psize = qpair->producer.size; } if (qpair->producer.size > 0) { /* splice */ glist_splice_tail(&qpair->consumer.q, &qpair->producer.q); qpair->consumer.size = qpair->producer.size; qpair->producer.size = 0; /* consumer.size > 0 */ pthread_spin_unlock(&qpair->producer.sp); reqdata = glist_first_entry(&qpair->consumer.q, request_data_t, req_q); glist_del(&reqdata->req_q); --(qpair->consumer.size); pthread_spin_unlock(&qpair->consumer.sp); if (s) LogFullDebug(COMPONENT_DISPATCH, "try splice, qpair %s consumer qsize=%u producer qsize=%u", s, csize, psize); goto out; } pthread_spin_unlock(&qpair->producer.sp); pthread_spin_unlock(&qpair->consumer.sp); if (s) LogFullDebug(COMPONENT_DISPATCH, "try splice, qpair %s consumer qsize=%u producer qsize=%u", s, csize, psize); } out: return reqdata; } static request_data_t *nfs_rpc_dequeue_req(nfs_worker_data_t *worker) { request_data_t *reqdata = NULL; struct req_q_set *nfs_request_q = &nfs_req_st.reqs.nfs_request_q; struct req_q_pair *qpair; uint32_t ix, slot; struct timespec timeout; /* XXX: the following stands in for a more robust/flexible * weighting function */ retry_deq: slot = atomic_inc_uint32_t(&nfs_req_st.reqs.ctr) % N_REQ_QUEUES; for (ix = 0; ix < N_REQ_QUEUES; ++ix) { qpair = &(nfs_request_q->qset[slot]); LogFullDebug(COMPONENT_DISPATCH, "dequeue_req try qpair %s %p:%p", qpair->s, &qpair->producer, &qpair->consumer); /* anything? */ reqdata = _9p_consume_req(qpair); if (reqdata) { break; } ++slot; slot = slot % N_REQ_QUEUES; } /* for */ /* wait */ if (!reqdata) { struct fridgethr_context *ctx = container_of(worker, struct fridgethr_context, wd); wait_q_entry_t *wqe = &worker->wqe; assert(wqe->waiters == 0); /* wqe is not on any wait queue */ PTHREAD_MUTEX_lock(&wqe->lwe.mtx); wqe->flags = Wqe_LFlag_WaitSync; wqe->waiters = 1; /* XXX functionalize */ pthread_spin_lock(&nfs_req_st.reqs.sp); glist_add_tail(&nfs_req_st.reqs.wait_list, &wqe->waitq); ++(nfs_req_st.reqs.waiters); pthread_spin_unlock(&nfs_req_st.reqs.sp); while (!(wqe->flags & Wqe_LFlag_SyncDone)) { timeout.tv_sec = time(NULL) + 5; timeout.tv_nsec = 0; pthread_cond_timedwait(&wqe->lwe.cv, &wqe->lwe.mtx, &timeout); if (fridgethr_you_should_break(ctx)) { /* We are returning; * so take us out of the waitq */ pthread_spin_lock(&nfs_req_st.reqs.sp); if (wqe->waitq.next != NULL || wqe->waitq.prev != NULL) { /* Element is still in wqitq, * remove it */ glist_del(&wqe->waitq); --(nfs_req_st.reqs.waiters); --(wqe->waiters); wqe->flags &= ~(Wqe_LFlag_WaitSync | Wqe_LFlag_SyncDone); } pthread_spin_unlock(&nfs_req_st.reqs.sp); PTHREAD_MUTEX_unlock(&wqe->lwe.mtx); return NULL; } } /* XXX wqe was removed from nfs_req_st.waitq * (by signalling thread) */ wqe->flags &= ~(Wqe_LFlag_WaitSync | Wqe_LFlag_SyncDone); PTHREAD_MUTEX_unlock(&wqe->lwe.mtx); LogFullDebug(COMPONENT_DISPATCH, "wqe wakeup %p", wqe); goto retry_deq; } /* !reqdata */ #if defined(HAVE_BLKIN) /* thread id */ BLKIN_KEYVAL_INTEGER( &reqdata->r_u.req.svc.bl_trace, &reqdata->r_u.req.xprt->blkin.endp, "worker-id", worker->worker_index ); BLKIN_TIMESTAMP( &reqdata->r_u.req.svc.bl_trace, &reqdata->r_u.req.xprt->blkin.endp, "dequeue-req"); #endif return reqdata; } static void nfs_rpc_enqueue_req(request_data_t *reqdata) { struct req_q_set *nfs_request_q; struct req_q_pair *qpair; struct req_q *q; #if defined(HAVE_BLKIN) BLKIN_TIMESTAMP( &reqdata->r_u.req.svc.bl_trace, &reqdata->r_u.req.xprt->blkin.endp, "enqueue-enter"); #endif nfs_request_q = &nfs_req_st.reqs.nfs_request_q; switch (reqdata->rtype) { case _9P_REQUEST: /* XXX identify high-latency requests and allocate * to the high-latency queue, as above */ qpair = &(nfs_request_q->qset[REQ_Q_LOW_LATENCY]); break; case NFS_REQUEST: case NFS_CALL: default: goto out; } /* this one is real, timestamp it */ now(&reqdata->time_queued); /* always append to producer queue */ q = &qpair->producer; pthread_spin_lock(&q->sp); glist_add_tail(&q->q, &reqdata->req_q); ++(q->size); pthread_spin_unlock(&q->sp); #if defined(HAVE_BLKIN) /* log the queue depth */ BLKIN_KEYVAL_INTEGER( &reqdata->r_u.req.svc.bl_trace, &reqdata->r_u.req.xprt->blkin.endp, "reqs-est", _9p_outstanding_reqs_est() ); BLKIN_TIMESTAMP( &reqdata->r_u.req.svc.bl_trace, &reqdata->r_u.req.xprt->blkin.endp, "enqueue-exit"); #endif LogDebug(COMPONENT_DISPATCH, "enqueued req, q %p (%s %p:%p) size is %d (enq %" PRIu64 " deq %" PRIu64 ")", q, qpair->s, &qpair->producer, &qpair->consumer, q->size, health.enqueued_reqs, health.dequeued_reqs); /* potentially wakeup some thread */ /* global waitq */ { wait_q_entry_t *wqe; /* SPIN LOCKED */ pthread_spin_lock(&nfs_req_st.reqs.sp); if (nfs_req_st.reqs.waiters) { wqe = glist_first_entry(&nfs_req_st.reqs.wait_list, wait_q_entry_t, waitq); LogFullDebug(COMPONENT_DISPATCH, "nfs_req_st.reqs.waiters %u signal wqe %p (for q %p)", nfs_req_st.reqs.waiters, wqe, q); /* release 1 waiter */ glist_del(&wqe->waitq); --(nfs_req_st.reqs.waiters); --(wqe->waiters); /* ! SPIN LOCKED */ pthread_spin_unlock(&nfs_req_st.reqs.sp); PTHREAD_MUTEX_lock(&wqe->lwe.mtx); /* XXX reliable handoff */ wqe->flags |= Wqe_LFlag_SyncDone; if (wqe->flags & Wqe_LFlag_WaitSync) pthread_cond_signal(&wqe->lwe.cv); PTHREAD_MUTEX_unlock(&wqe->lwe.mtx); } else /* ! SPIN LOCKED */ pthread_spin_unlock(&nfs_req_st.reqs.sp); } out: return; } /** * @brief Execute a 9p request * * @param[in,out] req9p 9p request */ static void _9p_execute(request_data_t *reqdata) { struct _9p_request_data *req9p = &reqdata->r_u._9p; struct req_op_context req_ctx; struct export_perms export_perms; memset(&req_ctx, 0, sizeof(struct req_op_context)); memset(&export_perms, 0, sizeof(struct export_perms)); op_ctx = &req_ctx; op_ctx->caller_addr = (sockaddr_t *)&reqdata->r_u._9p.pconn->addrpeer; op_ctx->req_type = reqdata->rtype; op_ctx->export_perms = &export_perms; if (req9p->pconn->trans_type == _9P_TCP) _9p_tcp_process_request(req9p); #ifdef _USE_9P_RDMA else if (req9p->pconn->trans_type == _9P_RDMA) _9p_rdma_process_request(req9p); #endif op_ctx = NULL; } /* _9p_execute */ /** * @brief Free resources allocated for a 9p request * * This does not free the request itself. * * @param[in] nfsreq 9p request */ static void _9p_free_reqdata(struct _9p_request_data *req9p) { if (req9p->pconn->trans_type == _9P_TCP) gsh_free(req9p->_9pmsg); /* decrease connection refcount */ (void) atomic_dec_uint32_t(&req9p->pconn->refcount); } static uint32_t worker_indexer; /** * @brief Initialize a worker thread * * @param[in] ctx Thread fridge context */ static void worker_thread_initializer(struct fridgethr_context *ctx) { struct nfs_worker_data *wd = &ctx->wd; char thr_name[32]; wd->worker_index = atomic_inc_uint32_t(&worker_indexer); snprintf(thr_name, sizeof(thr_name), "work-%u", wd->worker_index); SetNameFunction(thr_name); /* Initialize thr waitq */ init_wait_q_entry(&wd->wqe); } /** * @brief Finalize a worker thread * * @param[in] ctx Thread fridge context */ static void worker_thread_finalizer(struct fridgethr_context *ctx) { ctx->thread_info = NULL; } /** * @brief The main function for a worker thread * * This is the body of the worker thread. Its starting arguments are * located in global array worker_data. The argument is no pointer but * the worker's index. It then uses this index to address its own * worker data in the array. * * @param[in] ctx Fridge thread context */ static void worker_run(struct fridgethr_context *ctx) { struct nfs_worker_data *worker_data = &ctx->wd; request_data_t *reqdata; /* Worker's loop */ while (!fridgethr_you_should_break(ctx)) { reqdata = nfs_rpc_dequeue_req(worker_data); if (!reqdata) continue; switch (reqdata->rtype) { case _9P_REQUEST: _9p_execute(reqdata); _9p_free_reqdata(&reqdata->r_u._9p); break; case UNKNOWN_REQUEST: case NFS_REQUEST: case NFS_CALL: default: LogCrit(COMPONENT_DISPATCH, "Unexpected unknown request"); break; } /* Free the req by releasing the entry */ LogFullDebug(COMPONENT_DISPATCH, "Invalidating processed entry"); pool_free(request_pool, reqdata); (void) atomic_inc_uint64_t(&health.dequeued_reqs); } } int _9p_worker_init(void) { struct fridgethr_params frp; struct req_q_pair *qpair; int ix; int rc = 0; /* Init request queue before workers */ pthread_spin_init(&nfs_req_st.reqs.sp, PTHREAD_PROCESS_PRIVATE); nfs_req_st.reqs.size = 0; for (ix = 0; ix < N_REQ_QUEUES; ++ix) { qpair = &(nfs_req_st.reqs.nfs_request_q.qset[ix]); qpair->s = req_q_s[ix]; nfs_rpc_q_init(&qpair->producer); nfs_rpc_q_init(&qpair->consumer); } /* waitq */ glist_init(&nfs_req_st.reqs.wait_list); nfs_req_st.reqs.waiters = 0; memset(&frp, 0, sizeof(struct fridgethr_params)); frp.thr_max = nfs_param.core_param.nb_worker; frp.thr_min = nfs_param.core_param.nb_worker; frp.flavor = fridgethr_flavor_looper; frp.thread_initialize = worker_thread_initializer; frp.thread_finalize = worker_thread_finalizer; frp.wake_threads = nfs_rpc_queue_awaken; frp.wake_threads_arg = &nfs_req_st; rc = fridgethr_init(&worker_fridge, "9P", &frp); if (rc != 0) { LogMajor(COMPONENT_DISPATCH, "Unable to initialize worker fridge: %d", rc); return rc; } rc = fridgethr_populate(worker_fridge, worker_run, NULL); if (rc != 0) { LogMajor(COMPONENT_DISPATCH, "Unable to populate worker fridge: %d", rc); } return rc; } int _9p_worker_shutdown(void) { int rc = fridgethr_sync_command(worker_fridge, fridgethr_comm_stop, 120); if (rc == ETIMEDOUT) { LogMajor(COMPONENT_DISPATCH, "Shutdown timed out, cancelling threads."); fridgethr_cancel(worker_fridge); } else if (rc != 0) { LogMajor(COMPONENT_DISPATCH, "Failed shutting down worker threads: %d", rc); } return rc; } void DispatchWork9P(request_data_t *req) { switch (req->rtype) { case _9P_REQUEST: switch (req->r_u._9p.pconn->trans_type) { case _9P_TCP: LogDebug(COMPONENT_DISPATCH, "Dispatching 9P/TCP request %p, tcpsock=%lu", req, req->r_u._9p.pconn->trans_data.sockfd); break; case _9P_RDMA: LogDebug(COMPONENT_DISPATCH, "Dispatching 9P/RDMA request %p", req); break; default: LogCrit(COMPONENT_DISPATCH, "/!\\ Implementation error, bad 9P transport type"); return; } break; default: LogCrit(COMPONENT_DISPATCH, "/!\\ Implementation error, 9P Dispatch function is called for non-9P request !!!!"); return; } /* increase connection refcount */ (void) atomic_inc_uint32_t(&req->r_u._9p.pconn->refcount); /* new-style dispatch */ nfs_rpc_enqueue_req(req); } /** * _9p_socket_thread: 9p socket manager. * * This function is the main loop for the 9p socket manager. * One such thread exists per connection. * * @param Arg the socket number cast as a void * in pthread_create * * @return NULL * */ void *_9p_socket_thread(void *Arg) { long int tcp_sock = (long int)Arg; int rc = -1; struct pollfd fds[1]; int fdcount = 1; static char my_name[MAXNAMLEN + 1]; char strcaller[INET6_ADDRSTRLEN]; request_data_t *req = NULL; int tag; unsigned long sequence = 0; unsigned int i = 0; char *_9pmsg = NULL; uint32_t msglen; struct _9p_conn _9p_conn; socklen_t addrpeerlen; int readlen = 0; int total_readlen = 0; snprintf(my_name, MAXNAMLEN, "9p_sock_mgr#fd=%ld", tcp_sock); SetNameFunction(my_name); /* Init the struct _9p_conn structure */ memset(&_9p_conn, 0, sizeof(_9p_conn)); PTHREAD_MUTEX_init(&_9p_conn.sock_lock, NULL); _9p_conn.trans_type = _9P_TCP; _9p_conn.trans_data.sockfd = tcp_sock; for (i = 0; i < FLUSH_BUCKETS; i++) { PTHREAD_MUTEX_init(&_9p_conn.flush_buckets[i].lock, NULL); glist_init(&_9p_conn.flush_buckets[i].list); } atomic_store_uint32_t(&_9p_conn.refcount, 0); /* Init the fids pointers array */ memset(&_9p_conn.fids, 0, _9P_FID_PER_CONN * sizeof(struct _9p_fid *)); /* Set initial msize. * Client may request a lower value during TVERSION */ _9p_conn.msize = _9p_param._9p_tcp_msize; if (gettimeofday(&_9p_conn.birth, NULL) == -1) LogFatal(COMPONENT_9P, "Cannot get connection's time of birth"); addrpeerlen = sizeof(_9p_conn.addrpeer); rc = getpeername(tcp_sock, (struct sockaddr *)&_9p_conn.addrpeer, &addrpeerlen); if (rc == -1) { LogMajor(COMPONENT_9P, "Cannot get peername to tcp socket for 9p, error %d (%s)", errno, strerror(errno)); /* XXX */ strncpy(strcaller, "(unresolved)", INET6_ADDRSTRLEN); strcaller[12] = '\0'; goto end; } else { switch (_9p_conn.addrpeer.ss_family) { case AF_INET: inet_ntop(_9p_conn.addrpeer.ss_family, &((struct sockaddr_in *)&_9p_conn.addrpeer)-> sin_addr, strcaller, INET6_ADDRSTRLEN); break; case AF_INET6: inet_ntop(_9p_conn.addrpeer.ss_family, &((struct sockaddr_in6 *)&_9p_conn.addrpeer)-> sin6_addr, strcaller, INET6_ADDRSTRLEN); break; default: snprintf(strcaller, INET6_ADDRSTRLEN, "BAD ADDRESS"); break; } LogEvent(COMPONENT_9P, "9p socket #%ld is connected to %s", tcp_sock, strcaller); } _9p_conn.client = get_gsh_client(&_9p_conn.addrpeer, false); /* Set up the structure used by poll */ memset((char *)fds, 0, sizeof(struct pollfd)); fds[0].fd = tcp_sock; fds[0].events = POLLIN | POLLPRI | POLLRDBAND | POLLRDNORM | POLLRDHUP | POLLHUP | POLLERR | POLLNVAL; for (;;) { total_readlen = 0; /* new message */ rc = poll(fds, fdcount, -1); if (rc == -1) { /* timeout = -1 => Wait indefinitely for events */ /* Interruption if not an issue */ if (errno == EINTR) continue; LogCrit(COMPONENT_9P, "Got error %u (%s) on fd %ld connect to %s while polling on socket", errno, strerror(errno), tcp_sock, strcaller); } if (fds[0].revents & POLLNVAL) { LogEvent(COMPONENT_9P, "Client %s on socket %lu produced POLLNVAL", strcaller, tcp_sock); goto end; } if (fds[0].revents & (POLLERR | POLLHUP | POLLRDHUP)) { LogEvent(COMPONENT_9P, "Client %s on socket %lu has shut down and closed", strcaller, tcp_sock); goto end; } if (!(fds[0].revents & (POLLIN | POLLRDNORM))) continue; /* Prepare to read the message */ _9pmsg = gsh_malloc(_9p_conn.msize); /* An incoming 9P request: the msg has a 4 bytes header showing the size of the msg including the header */ readlen = recv(fds[0].fd, _9pmsg, _9P_HDR_SIZE, MSG_WAITALL); if (readlen != _9P_HDR_SIZE) goto badmsg; msglen = *(uint32_t *) _9pmsg; if (msglen > _9p_conn.msize) { LogCrit(COMPONENT_9P, "Message size too big! got %u, max = %u", msglen, _9p_conn.msize); goto end; } LogFullDebug(COMPONENT_9P, "Received 9P/TCP message of size %u from client %s on socket %lu", msglen, strcaller, tcp_sock); total_readlen += readlen; while (total_readlen < msglen) { readlen = recv(fds[0].fd, _9pmsg + total_readlen, msglen - total_readlen, 0); if (readlen > 0) { total_readlen += readlen; continue; } if (readlen == 0 || (readlen < 0 && errno != EINTR)) goto badmsg; } /* while */ server_stats_transport_done(_9p_conn.client, total_readlen, 1, 0, 0, 0, 0); /* Message is good. */ (void) atomic_inc_uint64_t(&health.enqueued_reqs); req = pool_alloc(request_pool); req->rtype = _9P_REQUEST; req->r_u._9p._9pmsg = _9pmsg; req->r_u._9p.pconn = &_9p_conn; /* Add this request to the request list, * should it be flushed later. */ tag = *(u16 *) (_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE); _9p_AddFlushHook(&req->r_u._9p, tag, sequence++); LogFullDebug(COMPONENT_9P, "Request tag is %d\n", tag); /* Message was OK push it */ DispatchWork9P(req); /* Not our buffer anymore */ _9pmsg = NULL; continue; badmsg: if (readlen == 0) LogEvent(COMPONENT_9P, "Premature end for Client %s on socket %lu, total read = %u", strcaller, tcp_sock, total_readlen); else if (readlen < 0) { LogEvent(COMPONENT_9P, "Read error client %s on socket %lu errno=%d, total read = %u", strcaller, tcp_sock, errno, total_readlen); } else LogEvent(COMPONENT_9P, "Header too small! for client %s on socket %lu: readlen=%u expected=%u", strcaller, tcp_sock, readlen, _9P_HDR_SIZE); /* Either way, we close the connection. * It is not possible to survive * once we get out of sync in the TCP stream * with the client */ break; /* bail out */ } /* for( ;; ) */ end: LogEvent(COMPONENT_9P, "Closing connection on socket %lu", tcp_sock); close(tcp_sock); /* Free buffer if we encountered an error * before we could give it to a worker */ if (_9pmsg) gsh_free(_9pmsg); while (atomic_fetch_uint32_t(&_9p_conn.refcount)) { LogEvent(COMPONENT_9P, "Waiting for workers to release pconn"); sleep(1); } _9p_cleanup_fids(&_9p_conn); if (_9p_conn.client != NULL) put_gsh_client(_9p_conn.client); pthread_exit(NULL); } /* _9p_socket_thread */ /** * _9p_create_socket_V4 : create the socket and bind for 9P using * the available V4 interfaces on the host. This is not the default * for ganesha. We expect _9p_create_socket_V6 to be called first * and succeed. Only when the V6 function returns failure is this * function expected to be called. See _9p_create_socket(). * * @return socket fd or -1 in case of failure * */ static int _9p_create_socket_V4(void) { int sock = -1; int one = 1; int centvingt = 120; int neuf = 9; struct netbuf netbuf_tcp; struct t_bind bindaddr_tcp; struct __rpc_sockinfo si_tcp; struct sockaddr_in sinaddr_tcp; sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock == -1) { LogWarn(COMPONENT_9P_DISPATCH, "Error creating 9p V4 socket, error %d(%s)", errno, strerror(errno)); return -1; } if ((setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1) || (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) == -1) || (setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, ¢vingt, sizeof(centvingt)) == -1) || (setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, ¢vingt, sizeof(centvingt)) == -1) || (setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &neuf, sizeof(neuf)) == -1)) { LogWarn(COMPONENT_9P_DISPATCH, "Error setting 9p V4 socket option, error %d(%s)", errno, strerror(errno)); goto err; } memset(&sinaddr_tcp, 0, sizeof(sinaddr_tcp)); sinaddr_tcp.sin_family = AF_INET; /* All the interfaces on the machine are used */ sinaddr_tcp.sin_addr.s_addr = htonl(INADDR_ANY); sinaddr_tcp.sin_port = htons(_9p_param._9p_tcp_port); netbuf_tcp.maxlen = sizeof(sinaddr_tcp); netbuf_tcp.len = sizeof(sinaddr_tcp); netbuf_tcp.buf = &sinaddr_tcp; bindaddr_tcp.qlen = SOMAXCONN; bindaddr_tcp.addr = netbuf_tcp; if (!__rpc_fd2sockinfo(sock, &si_tcp)) { LogWarn(COMPONENT_9P_DISPATCH, "Cannot get 9p socket info for tcp V4 socket error %d(%s)", errno, strerror(errno)); goto err; } if (bind(sock, (struct sockaddr *)bindaddr_tcp.addr.buf, (socklen_t) si_tcp.si_alen) == -1) { LogWarn(COMPONENT_9P_DISPATCH, "Cannot bind 9p tcp V4 socket, error %d(%s)", errno, strerror(errno)); goto err; } if (listen(sock, 20) == -1) { LogWarn(COMPONENT_9P_DISPATCH, "Cannot bind 9p tcp V4 socket, error %d(%s)", errno, strerror(errno)); goto err; } return sock; err: close(sock); return -1; } /** * _9p_create_socket_V6 : create the socket and bind for 9P using * the available V6 interfaces on the host * * @return socket fd or -1 in case of failure * */ static int _9p_create_socket_V6(void) { int sock = -1; int one = 1; int centvingt = 120; int neuf = 9; struct sockaddr_in6 sinaddr_tcp6; struct netbuf netbuf_tcp6; struct t_bind bindaddr_tcp6; struct __rpc_sockinfo si_tcp6; sock = socket(P_FAMILY, SOCK_STREAM, IPPROTO_TCP); if (sock == -1) { if (errno == EAFNOSUPPORT) { LogWarn(COMPONENT_9P_DISPATCH, "Error creating socket, V6 intfs disabled? error %d(%s)", errno, strerror(errno)); return _9p_create_socket_V4(); } return -1; } if ((setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1) || (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) == -1) || (setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, ¢vingt, sizeof(centvingt)) == -1) || (setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, ¢vingt, sizeof(centvingt)) == -1) || (setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &neuf, sizeof(neuf)) == -1)) { LogWarn(COMPONENT_9P_DISPATCH, "Error setting V6 socket option, error %d(%s)", errno, strerror(errno)); goto err; } memset(&sinaddr_tcp6, 0, sizeof(sinaddr_tcp6)); sinaddr_tcp6.sin6_family = AF_INET6; /* All the interfaces on the machine are used */ sinaddr_tcp6.sin6_addr = in6addr_any; sinaddr_tcp6.sin6_port = htons(_9p_param._9p_tcp_port); netbuf_tcp6.maxlen = sizeof(sinaddr_tcp6); netbuf_tcp6.len = sizeof(sinaddr_tcp6); netbuf_tcp6.buf = &sinaddr_tcp6; bindaddr_tcp6.qlen = SOMAXCONN; bindaddr_tcp6.addr = netbuf_tcp6; if (!__rpc_fd2sockinfo(sock, &si_tcp6)) { LogWarn(COMPONENT_9P_DISPATCH, "Cannot get 9p socket info for tcp6 socket error %d(%s)", errno, strerror(errno)); goto err; } if (bind(sock, (struct sockaddr *)bindaddr_tcp6.addr.buf, (socklen_t) si_tcp6.si_alen) == -1) { LogWarn(COMPONENT_9P_DISPATCH, "Cannot bind 9p tcp6 socket, error %d (%s)", errno, strerror(errno)); goto err; } if (listen(sock, 20) == -1) { LogWarn(COMPONENT_9P_DISPATCH, "Cannot bind 9p tcp6 socket, error %d (%s)", errno, strerror(errno)); goto err; } return sock; err: close(sock); return -1; } /** * _9p_dispatcher_thread: thread used for RPC dispatching. * * This function is the main loop for the 9p dispatcher. * It never returns because it is an infinite loop. * * @param Arg (unused) * * @return Pointer to the result (but this function will mostly loop forever). * */ void *_9p_dispatcher_thread(void *Arg) { int _9p_socket; int rc = 0; long int newsock = -1; pthread_attr_t attr_thr; pthread_t tcp_thrid; SetNameFunction("_9p_disp"); /* Calling dispatcher main loop */ LogInfo(COMPONENT_9P_DISPATCH, "Entering nfs/rpc dispatcher"); LogDebug(COMPONENT_9P_DISPATCH, "My pthread id is %p", (caddr_t) pthread_self()); /* Set up the _9p_socket (trying V6 first, will fall back to V4 * if V6 fails). */ _9p_socket = _9p_create_socket_V6(); if (_9p_socket == -1) { LogFatal(COMPONENT_9P_DISPATCH, "Can't get socket for 9p dispatcher"); } /* Init for thread parameter (mostly for scheduling) */ if (pthread_attr_init(&attr_thr) != 0) LogDebug(COMPONENT_9P_DISPATCH, "can't init pthread's attributes"); if (pthread_attr_setscope(&attr_thr, PTHREAD_SCOPE_SYSTEM) != 0) LogDebug(COMPONENT_9P_DISPATCH, "can't set pthread's scope"); if (pthread_attr_setdetachstate(&attr_thr, PTHREAD_CREATE_DETACHED) != 0) LogDebug(COMPONENT_9P_DISPATCH, "can't set pthread's join state"); LogEvent(COMPONENT_9P_DISPATCH, "9P dispatcher started"); while (true) { newsock = accept(_9p_socket, NULL, NULL); if (newsock < 0) { LogCrit(COMPONENT_9P_DISPATCH, "accept failed: %d", errno); continue; } /* Starting the thread dedicated to signal handling */ rc = pthread_create(&tcp_thrid, &attr_thr, _9p_socket_thread, (void *)newsock); if (rc != 0) { LogFatal(COMPONENT_THREAD, "Could not create 9p socket manager thread, error = %d (%s)", errno, strerror(errno)); } } /* while */ close(_9p_socket); return NULL; } /* _9p_dispatcher_thread */ nfs-ganesha-2.6.0/src/MainNFSD/9p_rdma_callbacks.c000066400000000000000000000141221324272410200215320ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2012) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_rdma_callbacks.c * \brief This file contains the callbacks used for 9P/RDMA. * * 9p_rdma_callbacks.c: This file contains the callbacks used for 9P/RDMA. * */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "abstract_mem.h" #include "abstract_atomic.h" #include "nfs_init.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_dupreq.h" #include "nfs_file_handle.h" #include "server_stats.h" #include "9p.h" #include void _9p_rdma_callback_send(msk_trans_t *trans, msk_data_t *data, void *arg) { struct _9p_rdma_priv *priv = _9p_rdma_priv_of(trans); PTHREAD_MUTEX_lock(&priv->outqueue->lock); data->next = priv->outqueue->data; priv->outqueue->data = data; pthread_cond_signal(&priv->outqueue->cond); PTHREAD_MUTEX_unlock(&priv->outqueue->lock); server_stats_transport_done(priv->pconn->client, 0, 0, 0, data->size, 1, 0); } void _9p_rdma_callback_send_err(msk_trans_t *trans, msk_data_t *data, void *arg) { struct _9p_rdma_priv *priv = _9p_rdma_priv_of(trans); /** * @todo: This should probably try to send again a few times * before unlocking */ if (priv && priv->outqueue) { PTHREAD_MUTEX_lock(&priv->outqueue->lock); data->next = priv->outqueue->data; priv->outqueue->data = data; pthread_cond_signal(&priv->outqueue->cond); PTHREAD_MUTEX_unlock(&priv->outqueue->lock); } if (priv && priv->pconn && priv->pconn->client) server_stats_transport_done(priv->pconn->client, 0, 0, 0, 0, 0, 1); } void _9p_rdma_callback_recv_err(msk_trans_t *trans, msk_data_t *data, void *arg) { struct _9p_rdma_priv *priv = _9p_rdma_priv_of(trans); if (trans->state == MSK_CONNECTED) { msk_post_recv(trans, data, _9p_rdma_callback_recv, _9p_rdma_callback_recv_err, arg); if (priv && priv->pconn && priv->pconn->client) server_stats_transport_done(priv->pconn->client, 0, 0, 1, 0, 0, 0); } } void _9p_rdma_callback_disconnect(msk_trans_t *trans) { if (!trans || !trans->private_data) return; _9p_rdma_cleanup_conn(trans); } void _9p_rdma_process_request(struct _9p_request_data *req9p) { uint32_t msglen; int rc = 0; msk_trans_t *trans = req9p->pconn->trans_data.rdma_trans; struct _9p_rdma_priv *priv = _9p_rdma_priv_of(trans); msk_data_t *dataout; /* get output buffer and move forward in queue */ PTHREAD_MUTEX_lock(&priv->outqueue->lock); while (priv->outqueue->data == NULL) { LogDebug(COMPONENT_9P, "Waiting for outqueue buffer on trans %p\n", trans); pthread_cond_wait(&priv->outqueue->cond, &priv->outqueue->lock); } dataout = priv->outqueue->data; priv->outqueue->data = dataout->next; dataout->next = NULL; PTHREAD_MUTEX_unlock(&priv->outqueue->lock); dataout->size = 0; dataout->mr = priv->pernic->outmr; /* Use buffer received via RDMA as a 9P message */ req9p->_9pmsg = req9p->data->data; msglen = *(uint32_t *)req9p->_9pmsg; if (req9p->data->size < _9P_HDR_SIZE || msglen != req9p->data->size) { LogMajor(COMPONENT_9P, "Malformed 9P/RDMA packet, bad header size"); /* send a rerror ? */ msk_post_recv(trans, req9p->data, _9p_rdma_callback_recv, _9p_rdma_callback_recv_err, NULL); } else { LogFullDebug(COMPONENT_9P, "Received 9P/RDMA message of size %u", msglen); rc = _9p_process_buffer(req9p, dataout->data, &dataout->size); if (rc != 1) { LogMajor(COMPONENT_9P, "Could not process 9P buffer on trans %p", req9p->pconn->trans_data.rdma_trans); } msk_post_recv(trans, req9p->data, _9p_rdma_callback_recv, _9p_rdma_callback_recv_err, NULL); /* If earlier processing succeeded, post it */ if (rc == 1) { if (0 != msk_post_send(trans, dataout, _9p_rdma_callback_send, _9p_rdma_callback_send_err, NULL)) rc = -1; } if (rc != 1) { LogMajor(COMPONENT_9P, "Could not send buffer on trans %p", req9p->pconn->trans_data.rdma_trans); /* Give the buffer back right away * since no buffer is being sent */ PTHREAD_MUTEX_lock(&priv->outqueue->lock); dataout->next = priv->outqueue->data; priv->outqueue->data = dataout; pthread_cond_signal(&priv->outqueue->cond); PTHREAD_MUTEX_unlock(&priv->outqueue->lock); } } _9p_DiscardFlushHook(req9p); } void _9p_rdma_callback_recv(msk_trans_t *trans, msk_data_t *data, void *arg) { request_data_t *req = NULL; u16 tag = 0; char *_9pmsg = NULL; (void) atomic_inc_uint64_t(&health.enqueued_reqs); req = pool_alloc(request_pool); req->rtype = _9P_REQUEST; req->r_u._9p._9pmsg = _9pmsg; req->r_u._9p.pconn = _9p_rdma_priv_of(trans)->pconn; req->r_u._9p.data = data; /* Add this request to the request list, should it be flushed later. */ _9pmsg = data->data; tag = *(u16 *) (_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE); _9p_AddFlushHook(&req->r_u._9p, tag, req->r_u._9p.pconn->sequence++); DispatchWork9P(req); server_stats_transport_done(_9p_rdma_priv_of(trans)->pconn->client, data->size, 1, 0, 0, 0, 0); } /* _9p_rdma_callback_recv */ nfs-ganesha-2.6.0/src/MainNFSD/9p_rdma_dispatcher.c000066400000000000000000000261271324272410200217510ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_rdma_dispatcher.c * \date $Date: 2006/02/23 12:33:05 $ * \brief The file that contains the '_9p_rdma_dispatcher_thread' function. * * */ #include "config.h" #include #include #include #include #include /* for having FNDELAY */ #include #include #include #include #include #include #include #include "hashtable.h" #include "log.h" #include "abstract_mem.h" #include "abstract_atomic.h" #include "nfs_init.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_dupreq.h" #include "nfs_file_handle.h" #include "client_mgr.h" #include "9p.h" #include static void *_9p_rdma_cleanup_conn_thread(void *arg) { msk_trans_t *trans = arg; struct _9p_rdma_priv *priv = _9p_rdma_priv_of(trans); if (priv) { if (priv->pconn) { LogDebug(COMPONENT_9P, "9P/RDMA: waiting till we're done with all requests on trans [%p]", trans); while (atomic_fetch_uint32_t(&priv->pconn->refcount) != 0) { sleep(1); } } LogDebug(COMPONENT_9P, "9P/RDMA: Freeing data associated with trans [%p]", trans); if (priv->pconn) { if (priv->pconn->client != NULL) put_gsh_client(priv->pconn->client); _9p_cleanup_fids(priv->pconn); } if (priv->pconn) gsh_free(priv->pconn); gsh_free(priv); } msk_destroy_trans(&trans); pthread_exit(NULL); } void _9p_rdma_cleanup_conn(msk_trans_t *trans) { pthread_attr_t attr_thr; pthread_t thr_id; /* Set the pthread attributes */ memset((char *)&attr_thr, 0, sizeof(attr_thr)); if (pthread_attr_init(&attr_thr) || pthread_attr_setscope(&attr_thr, PTHREAD_SCOPE_SYSTEM) || pthread_attr_setdetachstate(&attr_thr, PTHREAD_CREATE_DETACHED)) return; if (pthread_create(&thr_id, &attr_thr, _9p_rdma_cleanup_conn_thread, trans)) LogMajor(COMPONENT_9P, "9P/RDMA : dispatcher cleanup could not spawn a related thread"); else LogDebug(COMPONENT_9P, "9P/RDMA: thread #%x spawned to cleanup trans [%p]", (unsigned int)thr_id, trans); } /** * _9p_rdma_handle_trans_thr: 9P/RDMA listener * * @param Arg : contains the child trans to be managed * * @return NULL * */ /* Equivalent du _9p_socket_thread */ void *_9p_rdma_thread(void *Arg) { msk_trans_t *trans = Arg; struct _9p_rdma_priv *priv = NULL; struct _9p_conn *p_9p_conn = NULL; unsigned int i = 0; int rc = 0; struct _9p_outqueue *outqueue = trans->private_data; struct sockaddr *addrpeer; priv = gsh_calloc(1, sizeof(*priv)); trans->private_data = priv; priv->pernic = msk_getpd(trans)->private; priv->outqueue = outqueue; p_9p_conn = gsh_calloc(1, sizeof(*p_9p_conn)); priv->pconn = p_9p_conn; for (i = 0; i < FLUSH_BUCKETS; i++) { PTHREAD_MUTEX_init(&p_9p_conn->flush_buckets[i].lock, NULL); glist_init(&p_9p_conn->flush_buckets[i].list); } p_9p_conn->sequence = 0; atomic_store_uint32_t(&p_9p_conn->refcount, 0); p_9p_conn->trans_type = _9P_RDMA; p_9p_conn->trans_data.rdma_trans = trans; addrpeer = msk_get_dst_addr(trans); if (addrpeer == NULL) { LogCrit(COMPONENT_9P, "Cannot get peer address"); goto error; } memcpy(&p_9p_conn->addrpeer, addrpeer, MIN(sizeof(*addrpeer), sizeof(p_9p_conn->addrpeer))); p_9p_conn->client = get_gsh_client(&p_9p_conn->addrpeer, false); /* Init the fids pointers array */ memset(&p_9p_conn->fids, 0, _9P_FID_PER_CONN * sizeof(struct _9p_fid *)); /* Set initial msize. * Client may request a lower value during TVERSION */ p_9p_conn->msize = _9p_param._9p_rdma_msize; if (gettimeofday(&p_9p_conn->birth, NULL) == -1) LogMajor(COMPONENT_9P, "Cannot get connection's time of birth"); /* Finalize accept */ rc = msk_finalize_accept(trans); if (rc != 0) { LogMajor(COMPONENT_9P, "9P/RDMA: trans handler could not finalize accept, rc=%u", rc); goto error; } pthread_exit(NULL); error: _9p_rdma_cleanup_conn_thread(trans); pthread_exit(NULL); } /* _9p_rdma_handle_trans */ static void _9p_rdma_setup_pernic(msk_trans_t *trans, uint8_t *outrdmabuf) { struct _9p_rdma_priv_pernic *pernic; int rc, i; /* Do nothing if we already have stuff setup */ if (msk_getpd(trans)->private) return; pernic = gsh_calloc(1, sizeof(*pernic)); /* register output buffers */ pernic->outmr = msk_reg_mr(trans, outrdmabuf, _9p_param._9p_rdma_outpool_size * _9p_param._9p_rdma_msize, IBV_ACCESS_LOCAL_WRITE); if (pernic->outmr == NULL) { rc = errno; LogFatal(COMPONENT_9P, "9P/RDMA: pernic setup could not register outrdmabuf, errno: %s (%d)", strerror(rc), rc); } /* register input buffers */ /* Alloc rdmabuf */ pernic->rdmabuf = gsh_malloc(_9p_param._9p_rdma_inpool_size * _9p_param._9p_rdma_msize); /* Register rdmabuf */ pernic->inmr = msk_reg_mr(trans, pernic->rdmabuf, _9p_param._9p_rdma_inpool_size * _9p_param._9p_rdma_msize, IBV_ACCESS_LOCAL_WRITE); if (pernic->inmr == NULL) { rc = errno; LogFatal(COMPONENT_9P, "9P/RDMA: trans handler could not register rdmabuf, errno: %s (%d)", strerror(rc), rc); } /* Get prepared to recv data */ pernic->rdata = gsh_calloc(_9p_param._9p_rdma_inpool_size, sizeof(*pernic->rdata)); for (i = 0; i < _9p_param._9p_rdma_inpool_size; i++) { pernic->rdata[i].data = pernic->rdmabuf + i * _9p_param._9p_rdma_msize; pernic->rdata[i].max_size = _9p_param._9p_rdma_msize; pernic->rdata[i].mr = pernic->inmr; rc = msk_post_recv(trans, pernic->rdata + i, _9p_rdma_callback_recv, _9p_rdma_callback_recv_err, NULL); if (rc != 0) { LogEvent(COMPONENT_9P, "9P/RDMA: trans handler could post_recv first byte of data[%u], rc=%u", i, rc); msk_dereg_mr(pernic->inmr); msk_dereg_mr(pernic->outmr); gsh_free(pernic->rdmabuf); gsh_free(pernic->rdata); gsh_free(pernic); return; } } msk_getpd(trans)->private = pernic; } static void _9p_rdma_setup_global(uint8_t **poutrdmabuf, msk_data_t **pwdata, struct _9p_outqueue **poutqueue) { uint8_t *outrdmabuf; int i; msk_data_t *wdata; struct _9p_outqueue *outqueue; outrdmabuf = gsh_malloc(_9p_param._9p_rdma_outpool_size * _9p_param._9p_rdma_msize); *poutrdmabuf = outrdmabuf; wdata = gsh_malloc(_9p_param._9p_rdma_outpool_size * sizeof(*wdata)); for (i = 0; i < _9p_param._9p_rdma_outpool_size; i++) { wdata[i].data = outrdmabuf + i * _9p_param._9p_rdma_msize; wdata[i].max_size = _9p_param._9p_rdma_msize; if (i != _9p_param._9p_rdma_outpool_size - 1) wdata[i].next = &wdata[i+1]; else wdata[i].next = NULL; } *pwdata = wdata; outqueue = gsh_malloc(sizeof(*outqueue)); PTHREAD_MUTEX_init(&outqueue->lock, NULL); PTHREAD_COND_init(&outqueue->cond, NULL); outqueue->data = wdata; *poutqueue = outqueue; } /** * _9p_rdma_dispatcher_thread: 9P/RDMA dispatcher * * @param Arg the socket number cast as a void * in pthread_create * * @return NULL * */ void *_9p_rdma_dispatcher_thread(void *Arg) { msk_trans_t *trans; msk_trans_t *child_trans; msk_trans_attr_t trans_attr; pthread_attr_t attr_thr; pthread_t thrid_handle_trans; uint8_t *outrdmabuf = NULL; msk_data_t *wdata; struct _9p_outqueue *outqueue; #define PORT_MAX_LEN 6 char port[PORT_MAX_LEN]; memset(&trans_attr, 0, sizeof(msk_trans_attr_t)); trans_attr.server = _9p_param._9p_rdma_backlog; trans_attr.rq_depth = _9p_param._9p_rdma_inpool_size + 1; trans_attr.sq_depth = _9p_param._9p_rdma_outpool_size + 1; snprintf(port, PORT_MAX_LEN, "%d", _9p_param._9p_rdma_port); trans_attr.port = port; trans_attr.node = "::"; trans_attr.use_srq = 1; trans_attr.disconnect_callback = _9p_rdma_callback_disconnect; trans_attr.worker_count = -1; /* if worker_count isn't -1: trans_attr.worker_queue_size = 256; */ trans_attr.debug = MSK_DEBUG_EVENT; /* mooshika stats: * trans_attr.stats_prefix + trans_attr.debug |= MSK_DEBUG_SPEED */ SetNameFunction("_9p_rdma_disp"); /* Calling dispatcher main loop */ LogInfo(COMPONENT_9P_DISPATCH, "Entering 9P/RDMA dispatcher"); LogDebug(COMPONENT_9P_DISPATCH, "My pthread id is %p", (caddr_t) pthread_self()); /* Prepare attr_thr for dispatch */ memset((char *)&attr_thr, 0, sizeof(attr_thr)); /* Set the pthread attributes */ if (pthread_attr_init(&attr_thr)) LogFatal(COMPONENT_9P, "9P/RDMA dispatcher could not init pthread_attr_t"); if (pthread_attr_setscope(&attr_thr, PTHREAD_SCOPE_SYSTEM)) LogFatal(COMPONENT_9P, "9P/RDMA dispatcher could not set pthread_attr_t:scope_system"); if (pthread_attr_setdetachstate(&attr_thr, PTHREAD_CREATE_DETACHED)) LogFatal(COMPONENT_9P, "9P/RDMA dispatcher could not set pthread_attr_t:create_joignable"); /* Init RDMA via mooshika */ if (msk_init(&trans, &trans_attr)) LogFatal(COMPONENT_9P, "9P/RDMA dispatcher could not start mooshika engine"); else LogEvent(COMPONENT_9P, "Mooshika engine is started"); /* Bind Mooshika */ if (msk_bind_server(trans)) LogFatal(COMPONENT_9P, "9P/RDMA dispatcher could not bind mooshika engine"); else LogEvent(COMPONENT_9P, "Mooshika engine is bound"); /* Start infinite loop here */ while (1) { child_trans = msk_accept_one(trans); if (child_trans == NULL) LogMajor(COMPONENT_9P, "9P/RDMA : dispatcher failed to accept a new client"); else { /* Create output buffers on first connection. * need it here because we don't want multiple * children to do this job. */ if (!outrdmabuf) { _9p_rdma_setup_global(&outrdmabuf, &wdata, &outqueue); /* this means ENOMEM - abort */ if (!outrdmabuf || !wdata || !outqueue) break; } _9p_rdma_setup_pernic(child_trans, outrdmabuf); child_trans->private_data = outqueue; if (pthread_create(&thrid_handle_trans, &attr_thr, _9p_rdma_thread, child_trans)) LogMajor(COMPONENT_9P, "9P/RDMA : dispatcher accepted a new client but could not spawn a related thread"); else LogEvent(COMPONENT_9P, "9P/RDMA: thread #%x spawned to managed new child_trans [%p]", (unsigned int)thrid_handle_trans, child_trans); } } /* for( ;; ) */ pthread_exit(NULL); } /* _9p_rdma_dispatcher_thread */ nfs-ganesha-2.6.0/src/MainNFSD/CMakeLists.txt000066400000000000000000000044631324272410200206030ustar00rootroot00000000000000add_definitions( -D__USE_GNU -D_GNU_SOURCE ) if(USE_DBUS) include_directories( ${DBUS_INCLUDE_DIRS} ) endif(USE_DBUS) ########### next target ############### SET(MainServices_STAT_SRCS nfs_admin_thread.c nfs_rpc_callback.c nfs_worker_thread.c nfs_rpc_dispatcher_thread.c nfs_rpc_tcp_socket_manager_thread.c nfs_init.c nfs_lib.c nfs_reaper_thread.c ../support/client_mgr.c ) if(USE_9P) SET(MainServices_STAT_SRCS ${MainServices_STAT_SRCS} 9p_dispatcher.c) endif(USE_9P) if(USE_9P AND USE_9P_RDMA) SET(MainServices_STAT_SRCS ${MainServices_STAT_SRCS} 9p_rdma_dispatcher.c 9p_rdma_callbacks.c) endif(USE_9P AND USE_9P_RDMA) if(USE_NFS_RDMA) add_definitions(-D_USE_NFS_RDMA) endif(USE_NFS_RDMA) if(USE_CB_SIMULATOR) SET(MainServices_STAT_SRCS ${MainServices_STAT_SRCS} nfs_rpc_callback_simulator.c) endif(USE_CB_SIMULATOR) if(USE_UPCALL_SIMULATOR) SET(MainServices_STAT_SRCS ${MainServices_STAT_SRCS} fsal_upcall_simulator_thread.c ) endif(USE_UPCALL_SIMULATOR) if(USE_CB_SIMULATOR) SET(MainServices_STAT_SRCS ${MainServices_STAT_SRCS} nfs_rpc_callback_simulator.c ) endif(USE_CB_SIMULATOR) add_library(MainServices STATIC ${MainServices_STAT_SRCS}) add_sanitizers(MainServices) # FSAL core sources # fsal_manager and fsal_destroyer are the only objects referenced by the # core server all the rest are for the common support of fsal plugins. set(fsal_CORE_SRCS ../FSAL/fsal_convert.c ../FSAL/commonlib.c ../FSAL/fsal_manager.c ../FSAL/access_check.c ../FSAL/fsal_config.c ../FSAL/default_methods.c ../FSAL/common_pnfs.c ../FSAL/fsal_destroyer.c ../FSAL/fsal_helper.c ../FSAL_UP/fsal_up_top.c ../FSAL_UP/fsal_up_async.c ) add_library(FsalCore STATIC ${fsal_CORE_SRCS}) add_sanitizers(FsalCore) ########### next target ############### SET(ganesha.nfsd_SRCS nfs_main.c ) add_executable(ganesha.nfsd ${ganesha.nfsd_SRCS} ${fsal_CORE_SRCS} ) add_sanitizers(ganesha.nfsd) target_link_libraries(ganesha.nfsd MainServices ${PROTOCOLS} ${GANESHA_CORE} config_parsing ${LIBTIRPC_LIBRARIES} ${SYSTEM_LIBRARIES} ${LTTNG_LIBRARIES} ) install(TARGETS ganesha.nfsd COMPONENT daemon DESTINATION bin) ########### install files ############### # We are still missing the install of docs and stuff nfs-ganesha-2.6.0/src/MainNFSD/GNUmakefile.VERSION.include000066400000000000000000000005161324272410200226560ustar00rootroot00000000000000# This file is to be included in the GNUmakefile for versionning information VERSION = "0.99-11-snmp-64bits-tcp--rpcsecgss" VERSION_COMMENT = "GANESHA 64 bits compliant. SNMP exported stats. New TCP connection management. Branch with RPCSEC_GSS support" VERSION_FLAGS= -DVERSION='$(VERSION)' -DVERSION_COMMENT='$(VERSION_COMMENT)' nfs-ganesha-2.6.0/src/MainNFSD/maketest.conf000066400000000000000000000027711324272410200205270ustar00rootroot00000000000000# # Fichier de configuration de test # # $Header: /cea/home/cvs/cvs/SHERPA/BaseCvs/GANESHA/src/MainNFSD/maketest.conf,v 1.2 2004/10/22 09:24:32 deniel Exp $ # # $Log: maketest.conf,v $ # Revision 1.2 2004/10/22 09:24:32 deniel # No more dynamic libraries compiled # # Revision 1.1 2004/10/04 14:05:19 deniel # Dispatcher thread presque ok # # Revision 1.1 2004/09/30 14:08:42 deniel # Ajout des log en anglais (a partir des logs aglae) # # # Test Test_libloghash_Static { Product = Static version of the log library Command = ksh ../scripts/run_test_liblog.ksh Comment = Check for correctness of the log Success TestOk { STDOUT =~ /Test reussi : tous les tests sont passes avec succes/m AND STATUS == 0 } Failure FoundBadValue { STATUS != 0 AND STDOUT =~ /Test ECHOUE : la valeur lue est incorrecte/m } Failure BadHashTableInit { STATUS != 0 AND STDOUT =~ /Test ECHOUE : Mauvaise init/m } Failure BadHashDelete { STATUS != 0 AND STDOUT =~ /Test ECHOUE : effacement incorrect/m } Failure BadStatistique { STATUS != 0 AND STDOUT =~ /Test ECHOUE : statistiques incorrectes/m } Failure KeyIncoherence { STATUS != 0 AND STDOUT =~ /Test ECHOUE : La clef n'est pas trouvee/m } Failure RedondantKey { STATUS != 0 AND STDOUT =~ /Test ECHOUE : Clef redondante/m } } nfs-ganesha-2.6.0/src/MainNFSD/nfs_admin_thread.c000066400000000000000000000272701324272410200214750ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_admin_thread.c * @brief The admin_thread and support code. */ #include "config.h" #include #include #include #include "nfs_core.h" #include "log.h" #include "sal_functions.h" #include "sal_data.h" #include "idmapper.h" #include "delayed_exec.h" #include "export_mgr.h" #include "pnfs_utils.h" #include "fsal.h" #include "netgroup_cache.h" #ifdef USE_DBUS #include "gsh_dbus.h" #include "mdcache.h" #endif /** * @brief Mutex protecting shutdown flag. */ static pthread_mutex_t admin_control_mtx = PTHREAD_MUTEX_INITIALIZER; /** * @brief Condition variable to signal change in shutdown flag. */ static pthread_cond_t admin_control_cv = PTHREAD_COND_INITIALIZER; /** * @brief Flag to indicate shutdown Ganesha. * * Protected by admin_control_mtx and signaled by admin_control_cv. */ static bool admin_shutdown; #ifdef USE_DBUS /** * @brief Dbus method get grace period status * * @param[in] args dbus args * @param[out] reply dbus reply message with grace period status */ static bool admin_dbus_get_grace(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { char *errormsg = "get grace success"; bool success = true; DBusMessageIter iter; dbus_bool_t ingrace; dbus_message_iter_init_append(reply, &iter); if (args != NULL) { errormsg = "Get grace takes no arguments."; success = false; LogWarn(COMPONENT_DBUS, "%s", errormsg); goto out; } ingrace = nfs_in_grace(); dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &ingrace); out: dbus_status_reply(&iter, success, errormsg); return success; } static struct gsh_dbus_method method_get_grace = { .name = "get_grace", .method = admin_dbus_get_grace, .args = { {.name = "isgrace", .type = "b", .direction = "out", }, STATUS_REPLY, END_ARG_LIST} }; /** * @brief Dbus method start grace period * * @param[in] args Unused * @param[out] reply Unused */ static bool admin_dbus_grace(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { char *errormsg = "Started grace period"; bool success = true; DBusMessageIter iter; nfs_grace_start_t gsp; char *input = NULL; char *ip; dbus_message_iter_init_append(reply, &iter); if (args == NULL) { errormsg = "Grace period take 1 arguments: event:IP-address."; LogWarn(COMPONENT_DBUS, "%s", errormsg); success = false; goto out; } if (dbus_message_iter_get_arg_type(args) != DBUS_TYPE_STRING) { errormsg = "Grace period arg 1 not a string."; success = false; LogWarn(COMPONENT_DBUS, "%s", errormsg); goto out; } dbus_message_iter_get_basic(args, &input); gsp.nodeid = -1; gsp.event = EVENT_TAKE_IP; ip = index(input, ':'); if (ip == NULL) gsp.ipaddr = input; /* no event specified */ else { char *buf = alloca(strlen(input) + 1); gsp.ipaddr = ip + 1; /* point at the ip passed the : */ strcpy(buf, input); ip = strstr(buf, ":"); if (ip != NULL) { *ip = '\0'; /* replace ":" with null */ gsp.event = atoi(buf); } if (gsp.event == EVENT_TAKE_NODEID) gsp.nodeid = atoi(gsp.ipaddr); } nfs_start_grace(&gsp); out: dbus_status_reply(&iter, success, errormsg); return success; } static struct gsh_dbus_method method_grace_period = { .name = "grace", .method = admin_dbus_grace, .args = {IPADDR_ARG, STATUS_REPLY, END_ARG_LIST} }; /** * @brief Dbus method for shutting down Ganesha * * @param[in] args Unused * @param[out] reply Unused */ static bool admin_dbus_shutdown(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { char *errormsg = "Server shut down"; bool success = true; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); if (args != NULL) { errormsg = "Shutdown takes no arguments."; success = false; LogWarn(COMPONENT_DBUS, "%s", errormsg); goto out; } admin_halt(); out: dbus_status_reply(&iter, success, errormsg); return success; } static struct gsh_dbus_method method_shutdown = { .name = "shutdown", .method = admin_dbus_shutdown, .args = {STATUS_REPLY, END_ARG_LIST} }; /** * @brief Dbus method for flushing manage gids cache * * @param[in] args * @param[out] reply */ static bool admin_dbus_purge_gids(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { char *errormsg = "Purge gids cache"; bool success = true; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); if (args != NULL) { errormsg = "Purge gids takes no arguments."; success = false; LogWarn(COMPONENT_DBUS, "%s", errormsg); goto out; } uid2grp_clear_cache(); out: dbus_status_reply(&iter, success, errormsg); return success; } static struct gsh_dbus_method method_purge_gids = { .name = "purge_gids", .method = admin_dbus_purge_gids, .args = {STATUS_REPLY, END_ARG_LIST} }; /** * @brief Dbus method for flushing netgroup cache * * @param[in] args * @param[out] reply */ static bool admin_dbus_purge_netgroups(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { char *errormsg = "Purge netgroup cache"; bool success = true; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); if (args != NULL) { errormsg = "Purge netgroup takes no arguments."; success = false; LogWarn(COMPONENT_DBUS, "%s", errormsg); goto out; } ng_clear_cache(); out: dbus_status_reply(&iter, success, errormsg); return success; } static struct gsh_dbus_method method_purge_netgroups = { .name = "purge_netgroups", .method = admin_dbus_purge_netgroups, .args = {STATUS_REPLY, END_ARG_LIST} }; /** * @brief Dbus method for flushing idmapper cache * * @param[in] args * @param[out] reply */ static bool admin_dbus_purge_idmapper_cache(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { char *errormsg = "Purge idmapper cache"; bool success = true; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); if (args != NULL) { errormsg = "Purge idmapper takes no arguments."; success = false; LogWarn(COMPONENT_DBUS, "%s", errormsg); goto out; } idmapper_clear_cache(); out: dbus_status_reply(&iter, success, errormsg); return success; } static struct gsh_dbus_method method_purge_idmapper_cache = { .name = "purge_idmapper_cache", .method = admin_dbus_purge_idmapper_cache, .args = {STATUS_REPLY, END_ARG_LIST} }; /** * @brief Dbus method for updating open fd limit * * @param[in] args * @param[out] reply */ static bool admin_dbus_init_fds_limit(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { char *errormsg = "Init fds limit"; bool success = true; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); if (args != NULL) { errormsg = "Init fds limit takes no arguments."; success = false; LogWarn(COMPONENT_DBUS, "%s", errormsg); goto out; } init_fds_limit(); out: dbus_status_reply(&iter, success, errormsg); return success; } static struct gsh_dbus_method method_init_fds_limit = { .name = "init_fds_limit", .method = admin_dbus_init_fds_limit, .args = {STATUS_REPLY, END_ARG_LIST} }; static struct gsh_dbus_method *admin_methods[] = { &method_shutdown, &method_grace_period, &method_get_grace, &method_purge_gids, &method_purge_netgroups, &method_init_fds_limit, &method_purge_idmapper_cache, NULL }; static struct gsh_dbus_signal heartbeat_signal = { .name = HEARTBEAT_NAME, .signal = NULL, .args = {HEARTBEAT_ARG, END_ARG_LIST} }; static struct gsh_dbus_signal *admin_signals[] = { &heartbeat_signal, NULL }; static struct gsh_dbus_interface admin_interface = { .name = DBUS_ADMIN_IFACE, .props = NULL, .methods = admin_methods, .signals = admin_signals }; static struct gsh_dbus_interface *admin_interfaces[] = { &admin_interface, &log_interface, NULL }; #endif /* USE_DBUS */ /** * @brief Initialize admin thread control state and DBUS methods. */ void nfs_Init_admin_thread(void) { #ifdef USE_DBUS gsh_dbus_register_path("admin", admin_interfaces); #endif /* USE_DBUS */ LogEvent(COMPONENT_NFS_CB, "Admin thread initialized"); } /** * @brief Signal the admin thread to shut down the system */ void admin_halt(void) { PTHREAD_MUTEX_lock(&admin_control_mtx); if (!admin_shutdown) { admin_shutdown = true; pthread_cond_broadcast(&admin_control_cv); } PTHREAD_MUTEX_unlock(&admin_control_mtx); } static void do_shutdown(void) { int rc = 0; bool disorderly = false; LogEvent(COMPONENT_MAIN, "NFS EXIT: stopping NFS service"); #ifdef USE_DBUS /* DBUS shutdown */ gsh_dbus_pkgshutdown(); #endif LogEvent(COMPONENT_MAIN, "Stopping delayed executor."); delayed_shutdown(); LogEvent(COMPONENT_MAIN, "Delayed executor stopped."); LogEvent(COMPONENT_MAIN, "Stopping state asynchronous request thread"); rc = state_async_shutdown(); if (rc != 0) { LogMajor(COMPONENT_THREAD, "Error shutting down state asynchronous request system: %d", rc); disorderly = true; } else { LogEvent(COMPONENT_THREAD, "State asynchronous request system shut down."); } LogEvent(COMPONENT_MAIN, "Unregistering ports used by NFS service"); /* finalize RPC package */ Clean_RPC(); LogEvent(COMPONENT_MAIN, "Stopping worker threads"); #ifdef _USE_9P rc = _9p_worker_shutdown(); if (rc != 0) { LogMajor(COMPONENT_THREAD, "Unable to shut down worker threads: %d", rc); disorderly = true; } else { LogEvent(COMPONENT_THREAD, "Worker threads successfully shut down."); } #endif rc = general_fridge_shutdown(); if (rc != 0) { LogMajor(COMPONENT_THREAD, "Error shutting down general fridge: %d", rc); disorderly = true; } else { LogEvent(COMPONENT_THREAD, "General fridge shut down."); } rc = reaper_shutdown(); if (rc != 0) { LogMajor(COMPONENT_THREAD, "Error shutting down reaper thread: %d", rc); disorderly = true; } else { LogEvent(COMPONENT_THREAD, "Reaper thread shut down."); } LogEvent(COMPONENT_MAIN, "Removing all exports."); remove_all_exports(); LogEvent(COMPONENT_MAIN, "Removing all DSs."); remove_all_dss(); (void)svc_shutdown(SVC_SHUTDOWN_FLAG_NONE); if (disorderly) { LogMajor(COMPONENT_MAIN, "Error in shutdown, taking emergency cleanup."); /* We don't attempt to free state, clean the cache, or unload the FSALs more cleanly, since doing anything more than this risks hanging up on potentially invalid locks. */ emergency_cleanup_fsals(); } else { LogEvent(COMPONENT_MAIN, "Destroying the FSAL system."); destroy_fsals(); LogEvent(COMPONENT_MAIN, "FSAL system destroyed."); } unlink(pidfile_path); } void *admin_thread(void *UnusedArg) { SetNameFunction("Admin"); PTHREAD_MUTEX_lock(&admin_control_mtx); while (!admin_shutdown) { /* Wait for shutdown indication. */ pthread_cond_wait(&admin_control_cv, &admin_control_mtx); } PTHREAD_MUTEX_unlock(&admin_control_mtx); do_shutdown(); return NULL; } nfs-ganesha-2.6.0/src/MainNFSD/nfs_init.c000066400000000000000000000670751324272410200200300ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_init.c * @brief Most of the init routines */ #include "config.h" #include "nfs_init.h" #include "log.h" #include "fsal.h" #include "rquota.h" #include "nfs_core.h" #include "nfs_file_handle.h" #include "nfs_exports.h" #include "nfs_ip_stats.h" #include "nfs_proto_functions.h" #include "nfs_dupreq.h" #include "config_parsing.h" #include "nfs4_acls.h" #include "nfs_rpc_callback.h" #ifdef USE_DBUS #include "gsh_dbus.h" #endif #ifdef _USE_CB_SIMULATOR #include "nfs_rpc_callback_simulator.h" #endif #include #include #include #include #include #include #ifdef _USE_NLM #include "nlm_util.h" #endif /* _USE_NLM */ #include "nsm.h" #include "sal_functions.h" #include "fridgethr.h" #include "idmapper.h" #include "delayed_exec.h" #include "client_mgr.h" #include "export_mgr.h" #ifdef USE_CAPS #include /* For capget/capset */ #endif #include "uid2grp.h" #include "netgroup_cache.h" #include "pnfs_utils.h" #include "mdcache.h" #include #include "common_utils.h" #include "nfs_init.h" /** * @brief init_complete used to indicate if ganesha is during * startup or not */ struct nfs_init nfs_init; /* global information exported to all layers (as extern vars) */ pool_t *request_pool; nfs_parameter_t nfs_param; struct _nfs_health health; static struct _nfs_health healthstats; /* ServerEpoch is ServerBootTime unless overriden by -E command line option */ struct timespec ServerBootTime; time_t ServerEpoch; verifier4 NFS4_write_verifier; /* NFS V4 write verifier */ writeverf3 NFS3_write_verifier; /* NFS V3 write verifier */ /* node ID used to identify an individual node in a cluster */ int g_nodeid; nfs_start_info_t nfs_start_info; pthread_t admin_thrid; pthread_t sigmgr_thrid; tirpc_pkg_params ntirpc_pp = { TIRPC_DEBUG_FLAG_DEFAULT, 0, SetNameFunction, (mem_format_t)rpc_warnx, gsh_free_size, gsh_malloc__, gsh_malloc_aligned__, gsh_calloc__, gsh_realloc__, }; #ifdef _USE_9P pthread_t _9p_dispatcher_thrid; #endif #ifdef _USE_9P_RDMA pthread_t _9p_rdma_dispatcher_thrid; #endif #ifdef _USE_NFS_RDMA pthread_t nfs_rdma_dispatcher_thrid; #endif char *config_path = GANESHA_CONFIG_PATH; char *pidfile_path = GANESHA_PIDFILE_PATH; /** * @brief Reread the configuration file to accomplish update of options. * * The following option blocks are currently supported for update: * * LOG {} * LOG { COMPONENTS {} } * LOG { FACILITY {} } * LOG { FORMAT {} } * EXPORT {} * EXPORT { CLIENT {} } * */ struct config_error_type err_type; void reread_config(void) { int status = 0; int i; config_file_t config_struct; /* Clear out the flag indicating component was set from environment. */ for (i = COMPONENT_ALL; i < COMPONENT_COUNT; i++) LogComponents[i].comp_env_set = false; /* If no configuration file is given, then the caller must want to * reparse the configuration file from startup. */ if (config_path[0] == '\0') { LogCrit(COMPONENT_CONFIG, "No configuration file was specified for reloading log config."); return; } /* Create a memstream for parser+processing error messages */ if (!init_error_type(&err_type)) return; /* Attempt to parse the new configuration file */ config_struct = config_ParseFile(config_path, &err_type); if (!config_error_no_error(&err_type)) { config_Free(config_struct); LogCrit(COMPONENT_CONFIG, "Error while parsing new configuration file %s", config_path); report_config_errors(&err_type, NULL, config_errs_to_log); return; } /* Update the logging configuration */ status = read_log_config(config_struct, &err_type); if (status < 0) LogCrit(COMPONENT_CONFIG, "Error while parsing LOG entries"); /* Update the export configuration */ status = reread_exports(config_struct, &err_type); if (status < 0) LogCrit(COMPONENT_CONFIG, "Error while parsing EXPORT entries"); report_config_errors(&err_type, NULL, config_errs_to_log); config_Free(config_struct); } /** * @brief This thread is in charge of signal management * * @param[in] UnusedArg Unused * * @return NULL. */ static void *sigmgr_thread(void *UnusedArg) { SetNameFunction("sigmgr"); int signal_caught = 0; /* Loop until we catch SIGTERM */ while (signal_caught != SIGTERM) { sigset_t signals_to_catch; sigemptyset(&signals_to_catch); sigaddset(&signals_to_catch, SIGTERM); sigaddset(&signals_to_catch, SIGHUP); if (sigwait(&signals_to_catch, &signal_caught) != 0) { LogFullDebug(COMPONENT_THREAD, "sigwait exited with error"); continue; } if (signal_caught == SIGHUP) { LogEvent(COMPONENT_MAIN, "SIGHUP_HANDLER: Received SIGHUP.... initiating export list reload"); reread_config(); #ifdef _HAVE_GSSAPI svcauth_gss_release_cred(); #endif /* _HAVE_GSSAPI */ } } LogDebug(COMPONENT_THREAD, "sigmgr thread exiting"); admin_halt(); /* Might as well exit - no need for this thread any more */ return NULL; } static void gsh_backtrace(void) { #define MAX_STACK_DEPTH 32 /* enough ? */ void *buffer[MAX_STACK_DEPTH]; char **traces; int i, nlines; nlines = backtrace(buffer, MAX_STACK_DEPTH); traces = backtrace_symbols(buffer, nlines); if (!traces) { return; } for (i = 0; i < nlines; i++) { LogMajor(COMPONENT_INIT, "%s", traces[i]); } free(traces); } static void crash_handler(int signo, siginfo_t *info, void *ctx) { gsh_backtrace(); /* re-raise the signal for the default signal handler to dump core */ raise(signo); } static void install_sighandler(int signo, void (*handler)(int, siginfo_t *, void *)) { struct sigaction sa = {}; int ret; sa.sa_sigaction = handler; /* set SA_RESETHAND to restore default handler */ sa.sa_flags = SA_SIGINFO | SA_RESETHAND | SA_NODEFER; sigemptyset(&sa.sa_mask); ret = sigaction(signo, &sa, NULL); if (ret) { LogWarn(COMPONENT_INIT, "Install handler for signal (%s) failed", strsignal(signo)); } } static void init_crash_handlers(void) { install_sighandler(SIGSEGV, crash_handler); install_sighandler(SIGABRT, crash_handler); install_sighandler(SIGBUS, crash_handler); install_sighandler(SIGILL, crash_handler); install_sighandler(SIGFPE, crash_handler); install_sighandler(SIGQUIT, crash_handler); } /** * @brief Initialize NFSd prerequisites * * @param[in] program_name Name of the program * @param[in] host_name Server host name * @param[in] debug_level Debug level * @param[in] log_path Log path * @param[in] dump_trace Dump trace when segfault */ void nfs_prereq_init(char *program_name, char *host_name, int debug_level, char *log_path, bool dump_trace) { healthstats.enqueued_reqs = health.enqueued_reqs = 0; healthstats.enqueued_reqs = health.dequeued_reqs = 0; /* Initialize logging */ SetNamePgm(program_name); SetNameFunction("main"); SetNameHost(host_name); init_logging(log_path, debug_level); if (dump_trace) { init_crash_handlers(); } /* Redirect TI-RPC allocators, log channel */ if (!tirpc_control(TIRPC_PUT_PARAMETERS, &ntirpc_pp)) { LogFatal(COMPONENT_INIT, "Setting nTI-RPC parameters failed"); } } /** * @brief Print the nfs_parameter_structure */ void nfs_print_param_config(void) { printf("NFS_Core_Param\n{\n"); printf("\tNFS_Port = %u ;\n", nfs_param.core_param.port[P_NFS]); printf("\tMNT_Port = %u ;\n", nfs_param.core_param.port[P_MNT]); printf("\tNFS_Program = %u ;\n", nfs_param.core_param.program[P_NFS]); printf("\tMNT_Program = %u ;\n", nfs_param.core_param.program[P_NFS]); printf("\tNb_Worker = %u ;\n", nfs_param.core_param.nb_worker); printf("\tDRC_TCP_Npart = %u ;\n", nfs_param.core_param.drc.tcp.npart); printf("\tDRC_TCP_Size = %u ;\n", nfs_param.core_param.drc.tcp.size); printf("\tDRC_TCP_Cachesz = %u ;\n", nfs_param.core_param.drc.tcp.cachesz); printf("\tDRC_TCP_Hiwat = %u ;\n", nfs_param.core_param.drc.tcp.hiwat); printf("\tDRC_TCP_Recycle_Npart = %u ;\n", nfs_param.core_param.drc.tcp.recycle_npart); printf("\tDRC_TCP_Recycle_Expire_S = %u ;\n", nfs_param.core_param.drc.tcp.recycle_expire_s); printf("\tDRC_TCP_Checksum = %u ;\n", nfs_param.core_param.drc.tcp.checksum); printf("\tDRC_UDP_Npart = %u ;\n", nfs_param.core_param.drc.udp.npart); printf("\tDRC_UDP_Size = %u ;\n", nfs_param.core_param.drc.udp.size); printf("\tDRC_UDP_Cachesz = %u ;\n", nfs_param.core_param.drc.udp.cachesz); printf("\tDRC_UDP_Hiwat = %u ;\n", nfs_param.core_param.drc.udp.hiwat); printf("\tDRC_UDP_Checksum = %u ;\n", nfs_param.core_param.drc.udp.checksum); printf("\tBlocked_Lock_Poller_Interval = %" PRIu64 " ;\n", (uint64_t) nfs_param.core_param.blocked_lock_poller_interval); printf("\tManage_Gids_Expiration = %" PRIu64 " ;\n", (uint64_t) nfs_param.core_param.manage_gids_expiration); if (nfs_param.core_param.drop_io_errors) printf("\tDrop_IO_Errors = true ;\n"); else printf("\tDrop_IO_Errors = false ;\n"); if (nfs_param.core_param.drop_inval_errors) printf("\tDrop_Inval_Errors = true ;\n"); else printf("\tDrop_Inval_Errors = false ;\n"); if (nfs_param.core_param.drop_delay_errors) printf("\tDrop_Delay_Errors = true ;\n"); else printf("\tDrop_Delay_Errors = false ;\n"); printf("}\n\n"); } /** * @brief Load parameters from config file * * @param[in] config_struct Parsed config file * @param[out] p_start_info Startup parameters * @param[out] err_type error reporting state * * @return -1 on failure. */ int nfs_set_param_from_conf(config_file_t parse_tree, nfs_start_info_t *p_start_info, struct config_error_type *err_type) { /* * Initialize exports and clients so config parsing can use them * early. */ client_pkginit(); export_pkginit(); server_pkginit(); /* Core parameters */ (void) load_config_from_parse(parse_tree, &nfs_core, &nfs_param.core_param, true, err_type); if (!config_error_is_harmless(err_type)) { LogCrit(COMPONENT_INIT, "Error while parsing core configuration"); return -1; } /* Worker paramters: ip/name hash table and expiration for each entry */ (void) load_config_from_parse(parse_tree, &nfs_ip_name, NULL, true, err_type); if (!config_error_is_harmless(err_type)) { LogCrit(COMPONENT_INIT, "Error while parsing IP/name configuration"); return -1; } #ifdef _HAVE_GSSAPI /* NFS kerberos5 configuration */ (void) load_config_from_parse(parse_tree, &krb5_param, &nfs_param.krb5_param, true, err_type); if (!config_error_is_harmless(err_type)) { LogCrit(COMPONENT_INIT, "Error while parsing NFS/KRB5 configuration for RPCSEC_GSS"); return -1; } #endif /* NFSv4 specific configuration */ (void) load_config_from_parse(parse_tree, &version4_param, &nfs_param.nfsv4_param, true, err_type); if (!config_error_is_harmless(err_type)) { LogCrit(COMPONENT_INIT, "Error while parsing NFSv4 specific configuration"); return -1; } #ifdef _USE_9P (void) load_config_from_parse(parse_tree, &_9p_param_blk, NULL, true, err_type); if (!config_error_is_harmless(err_type)) { LogCrit(COMPONENT_INIT, "Error while parsing 9P specific configuration"); return -1; } #endif if (mdcache_set_param_from_conf(parse_tree, err_type) < 0) return -1; #ifdef USE_RADOS_RECOV if (rados_kv_set_param_from_conf(parse_tree, err_type) < 0) return -1; #endif LogEvent(COMPONENT_INIT, "Configuration file successfully parsed"); return 0; } int init_server_pkgs(void) { fsal_status_t fsal_status; state_status_t state_status; /* init uid2grp cache */ uid2grp_cache_init(); ng_cache_init(); /* netgroup cache */ /* MDCACHE Initialisation */ fsal_status = mdcache_pkginit(); if (FSAL_IS_ERROR(fsal_status)) { LogCrit(COMPONENT_INIT, "MDCACHE FSAL could not be initialized, status=%s", fsal_err_txt(fsal_status)); return -1; } state_status = state_lock_init(); if (state_status != STATE_SUCCESS) { LogCrit(COMPONENT_INIT, "State Lock Layer could not be initialized, status=%s", state_err_str(state_status)); return -1; } LogInfo(COMPONENT_INIT, "State lock layer successfully initialized"); /* Init the IP/name cache */ LogDebug(COMPONENT_INIT, "Now building IP/name cache"); if (nfs_Init_ip_name() != IP_NAME_SUCCESS) { LogCrit(COMPONENT_INIT, "Error while initializing IP/name cache"); return -1; } LogInfo(COMPONENT_INIT, "IP/name cache successfully initialized"); LogEvent(COMPONENT_INIT, "Initializing ID Mapper."); if (!idmapper_init()) { LogCrit(COMPONENT_INIT, "Failed initializing ID Mapper."); return -1; } LogEvent(COMPONENT_INIT, "ID Mapper successfully initialized."); return 0; } static void nfs_Start_threads(void) { int rc = 0; pthread_attr_t attr_thr; LogDebug(COMPONENT_THREAD, "Starting threads"); /* Init for thread parameter (mostly for scheduling) */ if (pthread_attr_init(&attr_thr) != 0) LogDebug(COMPONENT_THREAD, "can't init pthread's attributes"); if (pthread_attr_setscope(&attr_thr, PTHREAD_SCOPE_SYSTEM) != 0) LogDebug(COMPONENT_THREAD, "can't set pthread's scope"); if (pthread_attr_setdetachstate(&attr_thr, PTHREAD_CREATE_JOINABLE) != 0) LogDebug(COMPONENT_THREAD, "can't set pthread's join state"); LogEvent(COMPONENT_THREAD, "Starting delayed executor."); delayed_start(); /* Starting the thread dedicated to signal handling */ rc = pthread_create(&sigmgr_thrid, &attr_thr, sigmgr_thread, NULL); if (rc != 0) { LogFatal(COMPONENT_THREAD, "Could not create sigmgr_thread, error = %d (%s)", errno, strerror(errno)); } LogDebug(COMPONENT_THREAD, "sigmgr thread started"); #ifdef _USE_9P rc = _9p_worker_init(); if (rc != 0) { LogFatal(COMPONENT_THREAD, "Could not start worker threads: %d", errno); } /* Starting the 9P/TCP dispatcher thread */ if (nfs_param.core_param.core_options & CORE_OPTION_9P) { rc = pthread_create(&_9p_dispatcher_thrid, &attr_thr, _9p_dispatcher_thread, NULL); if (rc != 0) { LogFatal(COMPONENT_THREAD, "Could not create 9P/TCP dispatcher, error = %d (%s)", errno, strerror(errno)); } LogEvent(COMPONENT_THREAD, "9P/TCP dispatcher thread was started successfully"); } #endif #ifdef _USE_9P_RDMA /* Starting the 9P/RDMA dispatcher thread */ if (nfs_param.core_param.core_options & CORE_OPTION_9P) { rc = pthread_create(&_9p_rdma_dispatcher_thrid, &attr_thr, _9p_rdma_dispatcher_thread, NULL); if (rc != 0) { LogFatal(COMPONENT_THREAD, "Could not create 9P/RDMA dispatcher, error = %d (%s)", errno, strerror(errno)); } LogEvent(COMPONENT_THREAD, "9P/RDMA dispatcher thread was started successfully"); } #endif #ifdef USE_DBUS /* DBUS event thread */ rc = pthread_create(&gsh_dbus_thrid, &attr_thr, gsh_dbus_thread, NULL); if (rc != 0) { LogFatal(COMPONENT_THREAD, "Could not create gsh_dbus_thread, error = %d (%s)", errno, strerror(errno)); } LogEvent(COMPONENT_THREAD, "gsh_dbusthread was started successfully"); #endif /* Starting the admin thread */ rc = pthread_create(&admin_thrid, &attr_thr, admin_thread, NULL); if (rc != 0) { LogFatal(COMPONENT_THREAD, "Could not create admin_thread, error = %d (%s)", errno, strerror(errno)); } LogEvent(COMPONENT_THREAD, "admin thread was started successfully"); /* Starting the reaper thread */ rc = reaper_init(); if (rc != 0) { LogFatal(COMPONENT_THREAD, "Could not create reaper_thread, error = %d (%s)", errno, strerror(errno)); } LogEvent(COMPONENT_THREAD, "reaper thread was started successfully"); /* Starting the general fridge */ rc = general_fridge_init(); if (rc != 0) { LogFatal(COMPONENT_THREAD, "Could not create general fridge, error = %d (%s)", errno, strerror(errno)); } LogEvent(COMPONENT_THREAD, "General fridge was started successfully"); } /** * @brief Init the nfs daemon * * @param[in] p_start_info Unused */ static void nfs_Init(const nfs_start_info_t *p_start_info) { #ifdef _HAVE_GSSAPI gss_buffer_desc gss_service_buf; OM_uint32 maj_stat, min_stat; char GssError[MAXNAMLEN + 1]; #endif #ifdef USE_DBUS /* DBUS init */ gsh_dbus_pkginit(); dbus_export_init(); dbus_client_init(); #endif /* acls cache may be needed by exports_pkginit */ LogDebug(COMPONENT_INIT, "Now building NFSv4 ACL cache"); if (nfs4_acls_init() != 0) LogFatal(COMPONENT_INIT, "Error while initializing NFSv4 ACLs"); LogInfo(COMPONENT_INIT, "NFSv4 ACL cache successfully initialized"); /* finish the job with exports by caching the root entries */ exports_pkginit(); nfs41_session_pool = pool_basic_init("NFSv4.1 session pool", sizeof(nfs41_session_t)); request_pool = pool_basic_init("Request pool", sizeof(request_data_t)); /* If rpcsec_gss is used, set the path to the keytab */ #ifdef _HAVE_GSSAPI #ifdef HAVE_KRB5 if (nfs_param.krb5_param.active_krb5) { OM_uint32 gss_status = GSS_S_COMPLETE; if (*nfs_param.krb5_param.keytab != '\0') gss_status = krb5_gss_register_acceptor_identity( nfs_param.krb5_param.keytab); if (gss_status != GSS_S_COMPLETE) { log_sperror_gss(GssError, gss_status, 0); LogFatal(COMPONENT_INIT, "Error setting krb5 keytab to value %s is %s", nfs_param.krb5_param.keytab, GssError); } LogInfo(COMPONENT_INIT, "krb5 keytab path successfully set to %s", nfs_param.krb5_param.keytab); #endif /* HAVE_KRB5 */ /* Set up principal to be use for GSSAPPI within GSSRPC/KRB5 */ gss_service_buf.value = nfs_param.krb5_param.svc.principal; gss_service_buf.length = strlen(nfs_param.krb5_param.svc.principal) + 1; /* The '+1' is not to be forgotten, for the '\0' at the end */ maj_stat = gss_import_name(&min_stat, &gss_service_buf, (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &nfs_param.krb5_param.svc.gss_name); if (maj_stat != GSS_S_COMPLETE) { log_sperror_gss(GssError, maj_stat, min_stat); LogFatal(COMPONENT_INIT, "Error importing gss principal %s is %s", nfs_param.krb5_param.svc.principal, GssError); } if (nfs_param.krb5_param.svc.gss_name == GSS_C_NO_NAME) LogInfo(COMPONENT_INIT, "Regression: svc.gss_name == GSS_C_NO_NAME"); LogInfo(COMPONENT_INIT, "gss principal \"%s\" successfully set", nfs_param.krb5_param.svc.principal); /* Set the principal to GSSRPC */ if (!svcauth_gss_set_svc_name (nfs_param.krb5_param.svc.gss_name)) { LogFatal(COMPONENT_INIT, "Impossible to set gss principal to GSSRPC"); } /* Don't release name until shutdown, it will be used by the * backchannel. */ #ifdef HAVE_KRB5 } /* if( nfs_param.krb5_param.active_krb5 ) */ #endif /* HAVE_KRB5 */ #endif /* _HAVE_GSSAPI */ /* Init the NFSv4 Clientid cache */ LogDebug(COMPONENT_INIT, "Now building NFSv4 clientid cache"); if (nfs_Init_client_id() != CLIENT_ID_SUCCESS) { LogFatal(COMPONENT_INIT, "Error while initializing NFSv4 clientid cache"); } LogInfo(COMPONENT_INIT, "NFSv4 clientid cache successfully initialized"); /* Init duplicate request cache */ dupreq2_pkginit(); LogInfo(COMPONENT_INIT, "duplicate request hash table cache successfully initialized"); /* Init The NFSv4 State id cache */ LogDebug(COMPONENT_INIT, "Now building NFSv4 State Id cache"); if (nfs4_Init_state_id() != 0) { LogFatal(COMPONENT_INIT, "Error while initializing NFSv4 State Id cache"); } LogInfo(COMPONENT_INIT, "NFSv4 State Id cache successfully initialized"); /* Init The NFSv4 Open Owner cache */ LogDebug(COMPONENT_INIT, "Now building NFSv4 Owner cache"); if (Init_nfs4_owner() != 0) { LogFatal(COMPONENT_INIT, "Error while initializing NFSv4 Owner cache"); } LogInfo(COMPONENT_INIT, "NFSv4 Open Owner cache successfully initialized"); #ifdef _USE_NLM if (nfs_param.core_param.enable_NLM) { /* Init The NLM Owner cache */ LogDebug(COMPONENT_INIT, "Now building NLM Owner cache"); if (Init_nlm_hash() != 0) { LogFatal(COMPONENT_INIT, "Error while initializing NLM Owner cache"); } LogInfo(COMPONENT_INIT, "NLM Owner cache successfully initialized"); /* Init The NLM Owner cache */ LogDebug(COMPONENT_INIT, "Now building NLM State cache"); if (Init_nlm_state_hash() != 0) { LogFatal(COMPONENT_INIT, "Error while initializing NLM State cache"); } LogInfo(COMPONENT_INIT, "NLM State cache successfully initialized"); nlm_init(); } #endif /* _USE_NLM */ #ifdef _USE_9P /* Init the 9P lock owner cache */ LogDebug(COMPONENT_INIT, "Now building 9P Owner cache"); if (Init_9p_hash() != 0) { LogFatal(COMPONENT_INIT, "Error while initializing 9P Owner cache"); } LogInfo(COMPONENT_INIT, "9P Owner cache successfully initialized"); #endif LogDebug(COMPONENT_INIT, "Now building NFSv4 Session Id cache"); if (nfs41_Init_session_id() != 0) { LogFatal(COMPONENT_INIT, "Error while initializing NFSv4 Session Id cache"); } LogInfo(COMPONENT_INIT, "NFSv4 Session Id cache successfully initialized"); #ifdef _USE_9P LogDebug(COMPONENT_INIT, "Now building 9P resources"); if (_9p_init()) { LogCrit(COMPONENT_INIT, "Error while initializing 9P Resources"); exit(1); } LogInfo(COMPONENT_INIT, "9P resources successfully initialized"); #endif /* _USE_9P */ /* Creates the pseudo fs */ LogDebug(COMPONENT_INIT, "Now building pseudo fs"); create_pseudofs(); LogInfo(COMPONENT_INIT, "NFSv4 pseudo file system successfully initialized"); /* Save Ganesha thread credentials with Frank's routine for later use */ fsal_save_ganesha_credentials(); /* Create stable storage directory, this needs to be done before * starting the recovery thread. */ nfs4_recovery_init(); /* Start grace period */ nfs_start_grace(NULL); /* RPC Initialisation - exits on failure */ nfs_Init_svc(); LogInfo(COMPONENT_INIT, "RPC resources successfully initialized"); /* Admin initialisation */ nfs_Init_admin_thread(); /* callback dispatch */ nfs_rpc_cb_pkginit(); #ifdef _USE_CB_SIMULATOR nfs_rpc_cbsim_pkginit(); #endif /* _USE_CB_SIMULATOR */ } /* nfs_Init */ #ifdef USE_CAPS /** * @brief Lower my capabilities (privs) so quotas work right * * This will/should be moved to set_credentials where it belongs * Deal with capabilities in order to remove CAP_SYS_RESOURCE (needed * for proper management of data quotas) */ static void lower_my_caps(void) { struct __user_cap_header_struct caphdr = { .version = _LINUX_CAPABILITY_VERSION }; cap_user_data_t capdata; ssize_t capstrlen = 0; cap_t my_cap; char *cap_text; int capsz; if (!nfs_start_info.drop_caps) { /* Skip dropping caps by request */ return; } (void) capget(&caphdr, NULL); switch (caphdr.version) { case _LINUX_CAPABILITY_VERSION_1: capsz = _LINUX_CAPABILITY_U32S_1; break; case _LINUX_CAPABILITY_VERSION_2: capsz = _LINUX_CAPABILITY_U32S_2; break; default: abort(); /* can't happen */ } capdata = gsh_calloc(capsz, sizeof(struct __user_cap_data_struct)); caphdr.pid = getpid(); if (capget(&caphdr, capdata) != 0) LogFatal(COMPONENT_INIT, "Failed to query capabilities for process, errno=%u", errno); /* Set the capability bitmask to remove CAP_SYS_RESOURCE */ if (capdata->effective & CAP_TO_MASK(CAP_SYS_RESOURCE)) capdata->effective &= ~CAP_TO_MASK(CAP_SYS_RESOURCE); if (capdata->permitted & CAP_TO_MASK(CAP_SYS_RESOURCE)) capdata->permitted &= ~CAP_TO_MASK(CAP_SYS_RESOURCE); if (capdata->inheritable & CAP_TO_MASK(CAP_SYS_RESOURCE)) capdata->inheritable &= ~CAP_TO_MASK(CAP_SYS_RESOURCE); if (capset(&caphdr, capdata) != 0) LogFatal(COMPONENT_INIT, "Failed to set capabilities for process, errno=%u", errno); else LogEvent(COMPONENT_INIT, "CAP_SYS_RESOURCE was successfully removed for proper quota management in FSAL"); /* Print newly set capabilities (same as what CLI "getpcaps" displays */ my_cap = cap_get_proc(); cap_text = cap_to_text(my_cap, &capstrlen); LogEvent(COMPONENT_INIT, "currenty set capabilities are: %s", cap_text); cap_free(cap_text); cap_free(my_cap); gsh_free(capdata); } #endif /** * @brief Start NFS service * * @param[in] p_start_info Startup parameters */ void nfs_start(nfs_start_info_t *p_start_info) { /* store the start info so it is available for all layers */ nfs_start_info = *p_start_info; if (p_start_info->dump_default_config == true) { nfs_print_param_config(); exit(0); } /* Make sure Ganesha runs with a 0000 umask. */ umask(0000); { /* Set the write verifiers */ union { verifier4 NFS4_write_verifier; writeverf3 NFS3_write_verifier; uint64_t epoch; } build_verifier; build_verifier.epoch = (uint64_t) ServerEpoch; memcpy(NFS3_write_verifier, build_verifier.NFS3_write_verifier, sizeof(NFS3_write_verifier)); memcpy(NFS4_write_verifier, build_verifier.NFS4_write_verifier, sizeof(NFS4_write_verifier)); } #ifdef USE_CAPS lower_my_caps(); #endif /* Initialize all layers and service threads */ nfs_Init(p_start_info); nfs_Start_threads(); /* Spawns service threads */ nfs_init_complete(); #ifdef _USE_NLM if (nfs_param.core_param.enable_NLM) { /* NSM Unmonitor all */ nsm_unmonitor_all(); } #endif /* _USE_NLM */ LogEvent(COMPONENT_INIT, "-------------------------------------------------"); LogEvent(COMPONENT_INIT, " NFS SERVER INITIALIZED"); LogEvent(COMPONENT_INIT, "-------------------------------------------------"); /* Wait for dispatcher to exit */ LogDebug(COMPONENT_THREAD, "Wait for admin thread to exit"); pthread_join(admin_thrid, NULL); /* Regular exit */ LogEvent(COMPONENT_MAIN, "NFS EXIT: regular exit"); /* Try to lift the grace period */ nfs_try_lift_grace(); Cleanup(); /* let main return 0 to exit */ } void nfs_init_init(void) { PTHREAD_MUTEX_init(&nfs_init.init_mutex, NULL); PTHREAD_COND_init(&nfs_init.init_cond, NULL); nfs_init.init_complete = false; } void nfs_init_complete(void) { PTHREAD_MUTEX_lock(&nfs_init.init_mutex); nfs_init.init_complete = true; pthread_cond_broadcast(&nfs_init.init_cond); PTHREAD_MUTEX_unlock(&nfs_init.init_mutex); } void nfs_init_wait(void) { PTHREAD_MUTEX_lock(&nfs_init.init_mutex); while (!nfs_init.init_complete) { pthread_cond_wait(&nfs_init.init_cond, &nfs_init.init_mutex); } PTHREAD_MUTEX_unlock(&nfs_init.init_mutex); } bool nfs_health(void) { uint64_t newenq, newdeq; uint64_t dequeue_diff, enqueue_diff; bool healthy; newenq = health.enqueued_reqs; newdeq = health.dequeued_reqs; enqueue_diff = newenq - healthstats.enqueued_reqs; dequeue_diff = newdeq - healthstats.dequeued_reqs; /* Consider healthy and making progress if we have dequeued some * requests or there is nothing to dequeue. */ healthy = dequeue_diff > 0 || enqueue_diff == 0; if (!healthy) { LogWarn(COMPONENT_DBUS, "Health status is unhealthy. " "enq new: %" PRIu64 ", old: %" PRIu64 "; " "deq new: %" PRIu64 ", old: %" PRIu64, newenq, healthstats.enqueued_reqs, newdeq, healthstats.dequeued_reqs); } healthstats.enqueued_reqs = newenq; healthstats.dequeued_reqs = newdeq; return healthy; } nfs-ganesha-2.6.0/src/MainNFSD/nfs_lib.c000066400000000000000000000155101324272410200176160ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_main.c * @brief The file that contain the 'main' routine for the nfsd. * */ #include "config.h" #include #include #include #include #include #include #include /* for sigaction */ #include #include "fsal.h" #include "log.h" #include "nfs_init.h" #include "nfs_exports.h" #include "pnfs_utils.h" #include "conf_url.h" /** * @brief LTTng trace enabling magic * * Every trace include file must be added here regardless whether it * is actually used in this source file. The file must also be * included ONLY ONCE. Failure to do so will create interesting * build time failure messages. The key bit is the definitions of * TRACEPOINT_DEFINE and TRACEPOINT_PROBE_DYNAMIC_LINKAGE that are here * to trigger the global definitions as a shared object with the right * (weak) symbols to make the module loading optional. * * If and when this file gets some tracepoints of its own, the include * here is necessary and sufficient. */ #ifdef USE_LTTNG #define TRACEPOINT_DEFINE #define TRACEPOINT_PROBE_DYNAMIC_LINKAGE #include "gsh_lttng/logger.h" #include "gsh_lttng/mdcache.h" #include "gsh_lttng/nfs_rpc.h" #include "gsh_lttng/state.h" #include "gsh_lttng/fsal_mem.h" #endif /* USE_LTTNG */ /* parameters for NFSd startup and default values */ nfs_start_info_t my_nfs_start_info = { .dump_default_config = false, .lw_mark_trigger = false, .drop_caps = false }; config_file_t config_struct; char *log_path; char *exec_name = "nfs-ganesha"; char *host_name = "localhost"; int debug_level = -1; int detach_flag = true; /** * nfs_libmain: library initializer * * @return status to calling program by calling the exit(3C) function. * */ int nfs_libmain(const char *ganesha_conf, const char *lpath, const int dlevel) { char localmachine[MAXHOSTNAMELEN + 1]; int dsc; int rc; sigset_t signals_to_block; struct config_error_type err_type; /* Set the server's boot time and epoch */ now(&ServerBootTime); ServerEpoch = (time_t) ServerBootTime.tv_sec; if (ganesha_conf) config_path = gsh_strdup(ganesha_conf); if (lpath) log_path = gsh_strdup(lpath); debug_level = dlevel; /* get host name */ if (gethostname(localmachine, sizeof(localmachine)) != 0) { fprintf(stderr, "Could not get local host name, exiting...\n"); exit(1); } else { host_name = gsh_strdup(localmachine); if (!host_name) { fprintf(stderr, "Unable to allocate memory for hostname, exiting...\n"); exit(1); } } /* initialize memory and logging */ nfs_prereq_init(exec_name, host_name, debug_level, log_path, false); #if GANESHA_BUILD_RELEASE LogEvent(COMPONENT_MAIN, "%s Starting: Ganesha Version %s", exec_name, GANESHA_VERSION); #else LogEvent(COMPONENT_MAIN, "%s Starting: %s", exec_name, "Ganesha Version " _GIT_DESCRIBE ", built at " __DATE__ " " __TIME__ " on " BUILD_HOST); #endif /* initialize nfs_init */ nfs_init_init(); nfs_check_malloc(); /* Make sure Linux file i/o will return with error * if file size is exceeded. */ #ifdef _LINUX signal(SIGXFSZ, SIG_IGN); #endif /* Set up for the signal handler. * Blocks the signals the signal handler will handle. */ sigemptyset(&signals_to_block); sigaddset(&signals_to_block, SIGPIPE); /* XXX */ if (pthread_sigmask(SIG_BLOCK, &signals_to_block, NULL) != 0) LogFatal(COMPONENT_MAIN, "pthread_sigmask failed"); /* init URL package */ config_url_init(); /* Create a memstream for parser+processing error messages */ if (!init_error_type(&err_type)) goto fatal_die; if (config_path == NULL || config_path[0] == '\0') { LogWarn(COMPONENT_INIT, "No configuration file named."); config_struct = NULL; } else config_struct = config_ParseFile(config_path, &err_type); if (!config_error_no_error(&err_type)) { char *errstr = err_type_str(&err_type); if (!config_error_is_harmless(&err_type)) { LogCrit(COMPONENT_INIT, "Error %s while parsing (%s)", errstr != NULL ? errstr : "unknown", config_path); if (errstr != NULL) gsh_free(errstr); goto fatal_die; } else LogWarn(COMPONENT_INIT, "Error %s while parsing (%s)", errstr != NULL ? errstr : "unknown", config_path); if (errstr != NULL) gsh_free(errstr); } if (read_log_config(config_struct, &err_type) < 0) { LogCrit(COMPONENT_INIT, "Error while parsing log configuration"); goto fatal_die; } /* We need all the fsal modules loaded so we can have * the list available at exports parsing time. */ start_fsals(); /* parse configuration file */ if (nfs_set_param_from_conf(config_struct, &my_nfs_start_info, &err_type)) { LogCrit(COMPONENT_INIT, "Error setting parameters from configuration file."); goto fatal_die; } /* initialize core subsystems and data structures */ if (init_server_pkgs() != 0) { LogCrit(COMPONENT_INIT, "Failed to initialize server packages"); goto fatal_die; } /* Load Data Server entries from parsed file * returns the number of DS entries. */ dsc = ReadDataServers(config_struct, &err_type); if (dsc < 0) { LogCrit(COMPONENT_INIT, "Error while parsing DS entries"); goto fatal_die; } /* Load export entries from parsed file * returns the number of export entries. */ rc = ReadExports(config_struct, &err_type); if (rc < 0) { LogCrit(COMPONENT_INIT, "Error while parsing export entries"); goto fatal_die; } if (rc == 0 && dsc == 0) LogWarn(COMPONENT_INIT, "No export entries found in configuration file !!!"); report_config_errors(&err_type, NULL, config_errs_to_log); /* freeing syntax tree : */ config_Free(config_struct); /* Everything seems to be OK! We can now start service threads */ nfs_start(&my_nfs_start_info); return 0; fatal_die: report_config_errors(&err_type, NULL, config_errs_to_log); LogFatal(COMPONENT_INIT, "Fatal errors. Server exiting..."); /* NOT REACHED */ return 2; } nfs-ganesha-2.6.0/src/MainNFSD/nfs_main.c000066400000000000000000000330751324272410200200020ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_main.c * @brief The file that contain the 'main' routine for the nfsd. * */ #include "config.h" #include #include #include #include #include #include #include /* for sigaction */ #include #include "fsal.h" #include "log.h" #include "gsh_rpc.h" #include "nfs_init.h" #include "nfs_exports.h" #include "pnfs_utils.h" #include "config_parsing.h" #include "conf_url.h" /** * @brief LTTng trace enabling magic * * Every trace include file must be added here regardless whether it * is actually used in this source file. The file must also be * included ONLY ONCE. Failure to do so will create interesting * build time failure messages. The key bit is the definitions of * TRACEPOINT_DEFINE and TRACEPOINT_PROBE_DYNAMIC_LINKAGE that are here * to trigger the global definitions as a shared object with the right * (weak) symbols to make the module loading optional. * * If and when this file gets some tracepoints of its own, the include * here is necessary and sufficient. */ #ifdef USE_LTTNG #define TRACEPOINT_DEFINE #define TRACEPOINT_PROBE_DYNAMIC_LINKAGE #include "gsh_lttng/logger.h" #include "gsh_lttng/mdcache.h" #include "gsh_lttng/nfs_rpc.h" #include "gsh_lttng/state.h" #include "gsh_lttng/fsal_mem.h" #endif /* USE_LTTNG */ /* parameters for NFSd startup and default values */ nfs_start_info_t my_nfs_start_info = { .dump_default_config = false, .lw_mark_trigger = false, .drop_caps = true }; config_file_t config_struct; char *log_path; char *exec_name = "nfs-ganesha"; char *host_name = "localhost"; int debug_level = -1; int detach_flag = true; bool dump_trace; /* command line syntax */ char options[] = "v@L:N:f:p:FRTE:Ch"; char usage[] = "Usage: %s [-hd][-L ][-N ][-f ]\n" "\t[-v] display version information\n" "\t[-L ] set the default logfile for the daemon\n" "\t[-N ] set the verbosity level\n" "\t[-f ] set the config file to be used\n" "\t[-p ] set the pid file\n" "\t[-F] the program stays in foreground\n" "\t[-R] daemon will manage RPCSEC_GSS (default is no RPCSEC_GSS)\n" "\t[-T] dump the default configuration on stdout\n" "\t[-E] ;\n"); fprintf(stderr, "\tKeytabPath = /etc/krb5.keytab ;\n"); fprintf(stderr, "\tActive_krb5 = true ;\n"); fprintf(stderr, "}\n\n\n"); exit(1); break; case 'T': /* Dump the default configuration on stdout */ my_nfs_start_info.dump_default_config = true; break; case 'C': dump_trace = true; break; case 'E': ServerEpoch = (time_t) atoll(optarg); break; case 'h': fprintf(stderr, usage, exec_name); exit(0); default: /* '?' */ fprintf(stderr, "Try '%s -h' for usage\n", exec_name); exit(1); } } /* initialize memory and logging */ nfs_prereq_init(exec_name, host_name, debug_level, log_path, dump_trace); #if GANESHA_BUILD_RELEASE LogEvent(COMPONENT_MAIN, "%s Starting: Ganesha Version %s", exec_name, GANESHA_VERSION); #else LogEvent(COMPONENT_MAIN, "%s Starting: %s", exec_name, "Ganesha Version " _GIT_DESCRIBE ", built at " __DATE__ " " __TIME__ " on " BUILD_HOST); #endif /* initialize nfs_init */ nfs_init_init(); nfs_check_malloc(); /* Start in background, if wanted */ if (detach_flag) { #ifdef HAVE_DAEMON /* daemonize the process (fork, close xterm fds, * detach from parent process) */ if (daemon(0, 0)) LogFatal(COMPONENT_MAIN, "Error detaching process from parent: %s", strerror(errno)); /* In the child process, change the log header * if not, the header will contain the parent's pid */ set_const_log_str(); #else /* Step 1: forking a service process */ switch (son_pid = fork()) { case -1: /* Fork failed */ LogFatal(COMPONENT_MAIN, "Could not start nfs daemon (fork error %d (%s)", errno, strerror(errno)); break; case 0: /* This code is within the son (that will actually work) * Let's make it the leader of its group of process */ if (setsid() == -1) { LogFatal(COMPONENT_MAIN, "Could not start nfs daemon (setsid error %d (%s)", errno, strerror(errno)); } /* stdin, stdout and stderr should not refer to a tty * I close 0, 1 & 2 and redirect them to /dev/null */ dev_null_fd = open("/dev/null", O_RDWR); if (dev_null_fd < 0) LogFatal(COMPONENT_MAIN, "Could not open /dev/null: %d (%s)", errno, strerror(errno)); if (close(STDIN_FILENO) == -1) LogEvent(COMPONENT_MAIN, "Error while closing stdin: %d (%s)", errno, strerror(errno)); else { LogEvent(COMPONENT_MAIN, "stdin closed"); dup(dev_null_fd); } if (close(STDOUT_FILENO) == -1) LogEvent(COMPONENT_MAIN, "Error while closing stdout: %d (%s)", errno, strerror(errno)); else { LogEvent(COMPONENT_MAIN, "stdout closed"); dup(dev_null_fd); } if (close(STDERR_FILENO) == -1) LogEvent(COMPONENT_MAIN, "Error while closing stderr: %d (%s)", errno, strerror(errno)); else { LogEvent(COMPONENT_MAIN, "stderr closed"); dup(dev_null_fd); } if (close(dev_null_fd) == -1) LogFatal(COMPONENT_MAIN, "Could not close tmp fd to /dev/null: %d (%s)", errno, strerror(errno)); /* In the child process, change the log header * if not, the header will contain the parent's pid */ set_const_log_str(); break; default: /* This code is within the parent process, * it is useless, it must die */ LogFullDebug(COMPONENT_MAIN, "Starting a child of pid %d", son_pid); exit(0); break; } #endif } /* Make sure Linux file i/o will return with error * if file size is exceeded. */ #ifdef _LINUX signal(SIGXFSZ, SIG_IGN); #endif /* Echo PID into pidfile */ pidfile = open(pidfile_path, O_CREAT | O_RDWR, 0644); if (pidfile == -1) { LogFatal(COMPONENT_MAIN, "Can't open pid file %s for writing", pidfile_path); } else { char linebuf[1024]; struct flock lk; /* Try to obtain a lock on the file */ lk.l_type = F_WRLCK; lk.l_whence = SEEK_SET; lk.l_start = (off_t) 0; lk.l_len = (off_t) 0; if (fcntl(pidfile, F_SETLK, &lk) == -1) LogFatal(COMPONENT_MAIN, "Ganesha already started"); /* Put pid into file, then close it */ (void)snprintf(linebuf, sizeof(linebuf), "%u\n", getpid()); if (write(pidfile, linebuf, strlen(linebuf)) == -1) LogCrit(COMPONENT_MAIN, "Couldn't write pid to file %s", pidfile_path); } /* Set up for the signal handler. * Blocks the signals the signal handler will handle. */ sigemptyset(&signals_to_block); sigaddset(&signals_to_block, SIGTERM); sigaddset(&signals_to_block, SIGHUP); sigaddset(&signals_to_block, SIGPIPE); if (pthread_sigmask(SIG_BLOCK, &signals_to_block, NULL) != 0) LogFatal(COMPONENT_MAIN, "Could not start nfs daemon, pthread_sigmask failed"); /* init URL package */ config_url_init(); /* Create a memstream for parser+processing error messages */ if (!init_error_type(&err_type)) goto fatal_die; /* Parse the configuration file so we all know what is going on. */ if (config_path == NULL || config_path[0] == '\0') { LogWarn(COMPONENT_INIT, "No configuration file named."); config_struct = NULL; } else config_struct = config_ParseFile(config_path, &err_type); if (!config_error_no_error(&err_type)) { char *errstr = err_type_str(&err_type); if (!config_error_is_harmless(&err_type)) { LogCrit(COMPONENT_INIT, "Error %s while parsing (%s)", errstr != NULL ? errstr : "unknown", config_path); if (errstr != NULL) gsh_free(errstr); goto fatal_die; } else LogWarn(COMPONENT_INIT, "Error %s while parsing (%s)", errstr != NULL ? errstr : "unknown", config_path); if (errstr != NULL) gsh_free(errstr); } if (read_log_config(config_struct, &err_type) < 0) { LogCrit(COMPONENT_INIT, "Error while parsing log configuration"); goto fatal_die; } /* We need all the fsal modules loaded so we can have * the list available at exports parsing time. */ start_fsals(); /* parse configuration file */ if (nfs_set_param_from_conf(config_struct, &my_nfs_start_info, &err_type)) { LogCrit(COMPONENT_INIT, "Error setting parameters from configuration file."); goto fatal_die; } /* initialize core subsystems and data structures */ if (init_server_pkgs() != 0) { LogCrit(COMPONENT_INIT, "Failed to initialize server packages"); goto fatal_die; } /* Load Data Server entries from parsed file * returns the number of DS entries. */ dsc = ReadDataServers(config_struct, &err_type); if (dsc < 0) { LogCrit(COMPONENT_INIT, "Error while parsing DS entries"); goto fatal_die; } /* Load export entries from parsed file * returns the number of export entries. */ rc = ReadExports(config_struct, &err_type); if (rc < 0) { LogCrit(COMPONENT_INIT, "Error while parsing export entries"); goto fatal_die; } if (rc == 0 && dsc == 0) LogWarn(COMPONENT_INIT, "No export entries found in configuration file !!!"); report_config_errors(&err_type, NULL, config_errs_to_log); /* freeing syntax tree : */ config_Free(config_struct); /* Everything seems to be OK! We can now start service threads */ nfs_start(&my_nfs_start_info); return 0; fatal_die: report_config_errors(&err_type, NULL, config_errs_to_log); LogFatal(COMPONENT_INIT, "Fatal errors. Server exiting..."); /* NOT REACHED */ return 2; } nfs-ganesha-2.6.0/src/MainNFSD/nfs_reaper_thread.c000066400000000000000000000154471324272410200216660ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_reaper_thread.c * @brief check for expired clients and whack them. */ #include "config.h" #include #include #include "log.h" #include "nfs4.h" #include "sal_functions.h" #include "nfs_proto_functions.h" #include "nfs_core.h" #include "log.h" #include "fridgethr.h" #define REAPER_DELAY 10 unsigned int reaper_delay = REAPER_DELAY; static struct fridgethr *reaper_fridge; static int reap_hash_table(hash_table_t *ht_reap) { struct rbt_head *head_rbt; struct hash_data *addr = NULL; uint32_t i; struct rbt_node *pn; nfs_client_id_t *client_id; nfs_client_record_t *client_rec; int count = 0; /* For each bucket of the requested hashtable */ for (i = 0; i < ht_reap->parameter.index_size; i++) { head_rbt = &ht_reap->partitions[i].rbt; restart: /* acquire mutex */ PTHREAD_RWLOCK_wrlock(&ht_reap->partitions[i].lock); /* go through all entries in the red-black-tree */ RBT_LOOP(head_rbt, pn) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; bool str_valid = false; addr = RBT_OPAQ(pn); client_id = addr->val.addr; count++; PTHREAD_MUTEX_lock(&client_id->cid_mutex); if (valid_lease(client_id)) { PTHREAD_MUTEX_unlock(&client_id->cid_mutex); RBT_INCREMENT(pn); continue; } if (isDebug(COMPONENT_CLIENTID)) { display_client_id_rec(&dspbuf, client_id); LogFullDebug(COMPONENT_CLIENTID, "Expire index %d %s", i, str); str_valid = true; } /* Get the client record */ client_rec = client_id->cid_client_record; /* get a ref to client_id as we might drop the * last reference with expiring. */ inc_client_id_ref(client_id); /* if record is STALE, the linkage to client_record is * removed already. Acquire a ref on client record * before we drop the mutex on clientid */ if (client_rec != NULL) inc_client_record_ref(client_rec); PTHREAD_MUTEX_unlock(&client_id->cid_mutex); PTHREAD_RWLOCK_unlock(&ht_reap->partitions[i].lock); if (client_rec != NULL) PTHREAD_MUTEX_lock(&client_rec->cr_mutex); nfs_client_id_expire(client_id, false); if (client_rec != NULL) { PTHREAD_MUTEX_unlock(&client_rec->cr_mutex); dec_client_record_ref(client_rec); } if (isFullDebug(COMPONENT_CLIENTID)) { if (!str_valid) display_printf(&dspbuf, "clientid %p", client_id); LogFullDebug(COMPONENT_CLIENTID, "Reaper done, expired {%s}", str); } /* drop our reference to the client_id */ dec_client_id_ref(client_id); goto restart; } PTHREAD_RWLOCK_unlock(&ht_reap->partitions[i].lock); } return count; } static int reap_expired_open_owners(void) { int count = 0; time_t tnow = time(NULL); time_t texpire; state_owner_t *owner; struct state_nfs4_owner_t *nfs4_owner; PTHREAD_MUTEX_lock(&cached_open_owners_lock); /* Walk the list of cached NFS 4 open owners. Because we hold * the mutex while walking this list, it is impossible for another * thread to get a primary reference to these owners while we * process, and thus prevent them from expiring. */ while (true) { owner = glist_first_entry(&cached_open_owners, state_owner_t, so_owner.so_nfs4_owner.so_cache_entry); if (owner == NULL) break; nfs4_owner = &owner->so_owner.so_nfs4_owner; texpire = atomic_fetch_time_t(&nfs4_owner->so_cache_expire); if (texpire > tnow) { /* This owner has not yet expired. */ if (isFullDebug(COMPONENT_STATE)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = { sizeof(str), str, str}; display_owner(&dspbuf, owner); LogFullDebug(COMPONENT_STATE, "Did not release CLOSE_PENDING %d seconds left for {%s}", (int) (texpire - tnow), str); } /* Because entries are not moved on this list, and * they are added when they first become eligible, * the entries are in order of expiration time, and * thus once we hit one that is not expired yet, the * rest are also not expired. */ break; } /* This cached owner has expired, uncache it. */ uncache_nfs4_owner(nfs4_owner); count++; } PTHREAD_MUTEX_unlock(&cached_open_owners_lock); return count; } struct reaper_state { size_t count; bool logged; }; static struct reaper_state reaper_state; static void reaper_run(struct fridgethr_context *ctx) { struct reaper_state *rst = ctx->arg; SetNameFunction("reaper"); /* try to lift the grace period */ nfs_try_lift_grace(); if (isDebug(COMPONENT_CLIENTID) && ((rst->count > 0) || !rst->logged)) { LogDebug(COMPONENT_CLIENTID, "Now checking NFS4 clients for expiration"); rst->logged = (rst->count == 0); #ifdef DEBUG_SAL if (rst->count == 0) { dump_all_states(); dump_all_owners(); } #endif } rst->count = (reap_hash_table(ht_confirmed_client_id) + reap_hash_table(ht_unconfirmed_client_id)); rst->count += reap_expired_open_owners(); } int reaper_init(void) { struct fridgethr_params frp; int rc = 0; if (nfs_param.nfsv4_param.lease_lifetime < (2 * REAPER_DELAY)) reaper_delay = nfs_param.nfsv4_param.lease_lifetime / 2; memset(&frp, 0, sizeof(struct fridgethr_params)); frp.thr_max = 1; frp.thr_min = 1; frp.thread_delay = reaper_delay; frp.flavor = fridgethr_flavor_looper; rc = fridgethr_init(&reaper_fridge, "reaper", &frp); if (rc != 0) { LogMajor(COMPONENT_CLIENTID, "Unable to initialize reaper fridge, error code %d.", rc); return rc; } rc = fridgethr_submit(reaper_fridge, reaper_run, &reaper_state); if (rc != 0) { LogMajor(COMPONENT_CLIENTID, "Unable to start reaper thread, error code %d.", rc); return rc; } return 0; } int reaper_shutdown(void) { int rc = fridgethr_sync_command(reaper_fridge, fridgethr_comm_stop, 120); if (rc == ETIMEDOUT) { LogMajor(COMPONENT_CLIENTID, "Shutdown timed out, cancelling threads."); fridgethr_cancel(reaper_fridge); } else if (rc != 0) { LogMajor(COMPONENT_CLIENTID, "Failed shutting down reaper thread: %d", rc); } return rc; } nfs-ganesha-2.6.0/src/MainNFSD/nfs_rpc_callback.c000066400000000000000000001036511324272410200214540ustar00rootroot00000000000000/* * Copyright (C) 2012, The Linux Box Corporation * Copyright (c) 2012-2017 Red Hat, Inc. and/or its affiliates. * Contributor : Matt Benjamin * William Allen Simpson * * Some portions Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @file nfs_rpc_callback.c * @author Matt Benjamin * @author Lee Dobryden * @author Adam C. Emerson * @brief RPC callback dispatch package * * This module implements APIs for submission, and dispatch of NFSv4.0 * and NFSv4.1 callbacks. * */ #include "config.h" #include #include #include #include #include #include #include #include #include "fsal.h" #include "nfs_core.h" #include "log.h" #include "nfs_rpc_callback.h" #include "nfs4.h" #ifdef _HAVE_GSSAPI #include "gss_credcache.h" #endif /* _HAVE_GSSAPI */ #include "sal_data.h" #include "sal_functions.h" #include const struct __netid_nc_table netid_nc_table[9] = { { "-", 1, _NC_ERR, 0}, { "tcp", 3, _NC_TCP, AF_INET}, { "tcp6", 4, _NC_TCP6, AF_INET6}, { "rdma", 4, _NC_RDMA, AF_INET}, { "rdma6", 5, _NC_RDMA6, AF_INET6}, { "sctp", 4, _NC_SCTP, AF_INET}, { "sctp6", 5, _NC_SCTP6, AF_INET6}, { "udp", 3, _NC_UDP, AF_INET}, { "udp6", 4, _NC_UDP6, AF_INET6},}; /* retry timeout default to the moon and back */ static const struct timespec tout = { 3, 0 }; /** * @brief Initialize the callback credential cache * * @param[in] ccache Location of credential cache */ static inline void nfs_rpc_cb_init_ccache(const char *ccache) { #ifdef _HAVE_GSSAPI int code; if (mkdir(ccache, 0700) < 0) { if (errno == EEXIST) LogEvent(COMPONENT_INIT, "Callback creds directory (%s) already exists", ccache); else LogWarn(COMPONENT_INIT, "Could not create credential cache directory: %s (%s)", ccache, strerror(errno)); } ccachesearch[0] = nfs_param.krb5_param.ccache_dir; code = gssd_refresh_krb5_machine_credential( host_name, NULL, nfs_param.krb5_param.svc.principal); if (code) LogWarn(COMPONENT_INIT, "gssd_refresh_krb5_machine_credential failed (%d:%d)", code, errno); #endif /* _HAVE_GSSAPI */ } /** * @brief Initialize callback subsystem */ void nfs_rpc_cb_pkginit(void) { #ifdef _HAVE_GSSAPI /* ccache */ nfs_rpc_cb_init_ccache(nfs_param.krb5_param.ccache_dir); /* sanity check GSSAPI */ if (gssd_check_mechs() != 0) LogCrit(COMPONENT_INIT, "sanity check: gssd_check_mechs() failed"); #endif /* _HAVE_GSSAPI */ } /** * @brief Shutdown callback subsystem */ void nfs_rpc_cb_pkgshutdown(void) { /* return */ } /** * @brief Convert a netid label * * @todo This is automatically redundant, but in fact upstream TI-RPC is * not up-to-date with RFC 5665, will fix (Matt) * * @copyright 2012-2017, Linux Box Corp * * @param[in] netid The netid label dictating the protocol * * @return The numerical protocol identifier. */ nc_type nfs_netid_to_nc(const char *netid) { if (!strncmp(netid, netid_nc_table[_NC_TCP6].netid, netid_nc_table[_NC_TCP6].netid_len)) return _NC_TCP6; if (!strncmp(netid, netid_nc_table[_NC_TCP].netid, netid_nc_table[_NC_TCP].netid_len)) return _NC_TCP; if (!strncmp(netid, netid_nc_table[_NC_UDP6].netid, netid_nc_table[_NC_UDP6].netid_len)) return _NC_UDP6; if (!strncmp(netid, netid_nc_table[_NC_UDP].netid, netid_nc_table[_NC_UDP].netid_len)) return _NC_UDP; if (!strncmp(netid, netid_nc_table[_NC_RDMA6].netid, netid_nc_table[_NC_RDMA6].netid_len)) return _NC_RDMA6; if (!strncmp(netid, netid_nc_table[_NC_RDMA].netid, netid_nc_table[_NC_RDMA].netid_len)) return _NC_RDMA; if (!strncmp(netid, netid_nc_table[_NC_SCTP6].netid, netid_nc_table[_NC_SCTP6].netid_len)) return _NC_SCTP6; if (!strncmp(netid, netid_nc_table[_NC_SCTP].netid, netid_nc_table[_NC_SCTP].netid_len)) return _NC_SCTP; return _NC_ERR; } /** * @brief Convert string format address to sockaddr * * This function takes the host.port format used in the NFSv4.0 * clientaddr4 and converts it to a POSIX sockaddr structure stored in * the callback information of the clientid. * * @param[in,out] clientid The clientid in which to store the sockaddr * @param[in] uaddr na_r_addr from the clientaddr4 */ static inline void setup_client_saddr(nfs_client_id_t *clientid, const char *uaddr) { char addr_buf[SOCK_NAME_MAX + 1]; uint32_t bytes[11]; int code; assert(clientid->cid_minorversion == 0); memset(&clientid->cid_cb.v40.cb_addr.ss, 0, sizeof(struct sockaddr_storage)); switch (clientid->cid_cb.v40.cb_addr.nc) { case _NC_TCP: case _NC_RDMA: case _NC_SCTP: case _NC_UDP: /* IPv4 (ws inspired) */ if (sscanf(uaddr, "%u.%u.%u.%u.%u.%u", &bytes[1], &bytes[2], &bytes[3], &bytes[4], &bytes[5], &bytes[6]) != 6) return; struct sockaddr_in *sin = ((struct sockaddr_in *) &clientid->cid_cb.v40.cb_addr.ss); snprintf(addr_buf, sizeof(addr_buf), "%u.%u.%u.%u", bytes[1], bytes[2], bytes[3], bytes[4]); sin->sin_family = AF_INET; sin->sin_port = htons((bytes[5] << 8) | bytes[6]); code = inet_pton(AF_INET, addr_buf, &sin->sin_addr); if (code != 1) LogWarn(COMPONENT_NFS_CB, "inet_pton failed (%d %s)", code, addr_buf); else LogDebug(COMPONENT_NFS_CB, "client callback addr:port %s:%d", addr_buf, ntohs(sin->sin_port)); break; case _NC_TCP6: case _NC_RDMA6: case _NC_SCTP6: case _NC_UDP6: /* IPv6 (ws inspired) */ if (sscanf(uaddr, "%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x.%u.%u", &bytes[1], &bytes[2], &bytes[3], &bytes[4], &bytes[5], &bytes[6], &bytes[7], &bytes[8], &bytes[9], &bytes[10]) != 10) return; struct sockaddr_in6 *sin6 = ((struct sockaddr_in6 *) &clientid->cid_cb.v40.cb_addr.ss); snprintf(addr_buf, sizeof(addr_buf), "%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x", bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8]); code = inet_pton(AF_INET6, addr_buf, &sin6->sin6_addr); sin6->sin6_port = htons((bytes[9] << 8) | bytes[10]); sin6->sin6_family = AF_INET6; if (code != 1) LogWarn(COMPONENT_NFS_CB, "inet_pton failed (%d %s)", code, addr_buf); else LogDebug(COMPONENT_NFS_CB, "client callback addr:port %s:%d", addr_buf, ntohs(sin6->sin6_port)); break; default: /* unknown netid */ break; }; } /** * @brief Set the callback location for an NFSv4.0 clientid * * @param[in,out] clientid The clientid in which to set the location * @param[in] addr4 The client's supplied callback address */ void nfs_set_client_location(nfs_client_id_t *clientid, const clientaddr4 *addr4) { assert(clientid->cid_minorversion == 0); clientid->cid_cb.v40.cb_addr.nc = nfs_netid_to_nc(addr4->r_netid); strlcpy(clientid->cid_cb.v40.cb_client_r_addr, addr4->r_addr, SOCK_NAME_MAX); setup_client_saddr(clientid, clientid->cid_cb.v40.cb_client_r_addr); } /** * @brief Get the fd of an NFSv4.0 callback connection * * @param[in] clientid The clientid to query * @param[out] fd The file descriptor * @param[out] proto The protocol used on this connection * * @return 0 or values of errno. */ static inline int32_t nfs_clid_connected_socket(nfs_client_id_t *clientid, int *fd, int *proto) { int domain, sock_type, protocol, sock_size; int nfd; int code; assert(clientid->cid_minorversion == 0); *fd = 0; *proto = -1; switch (clientid->cid_cb.v40.cb_addr.nc) { case _NC_TCP: case _NC_TCP6: sock_type = SOCK_STREAM; protocol = IPPROTO_TCP; break; case _NC_UDP6: case _NC_UDP: sock_type = SOCK_DGRAM; protocol = IPPROTO_UDP; break; default: return EINVAL; } switch (clientid->cid_cb.v40.cb_addr.ss.ss_family) { case AF_INET: domain = PF_INET; sock_size = sizeof(struct sockaddr_in); break; case AF_INET6: domain = PF_INET6; sock_size = sizeof(struct sockaddr_in6); break; default: return EINVAL; } nfd = socket(domain, sock_type, protocol); if (nfd < 0) { code = errno; LogWarn(COMPONENT_NFS_CB, "socket failed %d (%s)", code, strerror(code)); return code; } code = connect(nfd, (struct sockaddr *)&clientid->cid_cb.v40.cb_addr.ss, sock_size); if (code < 0) { code = errno; LogWarn(COMPONENT_NFS_CB, "connect fail errno %d (%s)", code, strerror(code)); close(nfd); return code; } *proto = protocol; *fd = nfd; return code; } /* end refactorable RPC code */ /** * @brief Check if an authentication flavor is supported * * @param[in] flavor RPC authentication flavor * * @retval true if supported. * @retval false if not. */ static inline bool supported_auth_flavor(int flavor) { switch (flavor) { case RPCSEC_GSS: case AUTH_SYS: case AUTH_NONE: return true; default: return false; }; } /** * @brief Kerberos OID * * This value comes from kerberos source, gssapi_krb5.c (Umich). */ #ifdef _HAVE_GSSAPI gss_OID_desc krb5oid = { 9, "\052\206\110\206\367\022\001\002\002" }; #endif /* _HAVE_GSSAPI */ /** * @brief Format a principal name for an RPC call channel * * @param[in] chan Call channel * @param[out] buf Buffer to hold formatted name * @param[in] len Size of buffer * * @return The principle or NULL. */ static inline char *format_host_principal(rpc_call_channel_t *chan, char *buf, size_t len) { char addr_buf[SOCK_NAME_MAX + 1]; const char *host = NULL; void *sin; switch (chan->type) { case RPC_CHAN_V40: sin = &chan->source.clientid->cid_cb.v40.cb_addr.ss; break; default: return NULL; } switch (((struct sockaddr_in *)sin)->sin_family) { case AF_INET: host = inet_ntop(AF_INET, &((struct sockaddr_in *)sin)->sin_addr, addr_buf, INET_ADDRSTRLEN); break; case AF_INET6: host = inet_ntop(AF_INET6, &((struct sockaddr_in6 *)sin)->sin6_addr, addr_buf, INET6_ADDRSTRLEN); break; default: break; } if (host) { snprintf(buf, len, "nfs@%s", host); return buf; } return NULL; } /** * @brief Set up GSS on a callback channel * * @param[in,out] chan Channel on which to set up GSS * @param[in] cred GSS Credential * * @return chan->auth->ah_error; check AUTH_FAILURE or AUTH_SUCCESS. */ #ifdef _HAVE_GSSAPI static inline void nfs_rpc_callback_setup_gss(rpc_call_channel_t *chan, nfs_client_cred_t *cred) { char hprinc[MAXPATHLEN + 1]; char *principal = nfs_param.krb5_param.svc.principal; int32_t code; assert(cred->flavor == RPCSEC_GSS); /* MUST RFC 3530bis, section 3.3.3 */ chan->gss_sec.svc = cred->auth_union.auth_gss.svc; chan->gss_sec.qop = cred->auth_union.auth_gss.qop; /* the GSSAPI k5 mech needs to find an unexpired credential * for nfs/hostname in an accessible k5ccache */ code = gssd_refresh_krb5_machine_credential(host_name, NULL, principal); if (code) { LogWarn(COMPONENT_NFS_CB, "gssd_refresh_krb5_machine_credential failed (%d:%d)", code, errno); return; } if (!format_host_principal(chan, hprinc, sizeof(hprinc))) { LogCrit(COMPONENT_NFS_CB, "format_host_principal failed"); return; } chan->gss_sec.cred = GSS_C_NO_CREDENTIAL; chan->gss_sec.req_flags = 0; if (chan->gss_sec.svc != RPCSEC_GSS_SVC_NONE) { /* no more lipkey, spkm3 */ chan->gss_sec.mech = (gss_OID) & krb5oid; chan->gss_sec.req_flags = GSS_C_MUTUAL_FLAG; /* XXX */ chan->auth = authgss_ncreate_default(chan->clnt, hprinc, &chan->gss_sec); } } #endif /* _HAVE_GSSAPI */ /** * @brief Create a channel for an NFSv4.0 client * * @param[in] clientid Client record * @param[in] flags Currently unused * * @return Status code. */ int nfs_rpc_create_chan_v40(nfs_client_id_t *clientid, uint32_t flags) { rpc_call_channel_t *chan = &clientid->cid_cb.v40.cb_chan; char *err; struct netbuf raddr; int fd; int proto; int code; assert(!chan->clnt); assert(clientid->cid_minorversion == 0); /* XXX we MUST error RFC 3530bis, sec. 3.3.3 */ if (!supported_auth_flavor(clientid->cid_credential.flavor)) return EINVAL; chan->type = RPC_CHAN_V40; chan->source.clientid = clientid; code = nfs_clid_connected_socket(clientid, &fd, &proto); if (code) { LogWarn(COMPONENT_NFS_CB, "Failed creating socket"); return code; } raddr.buf = &clientid->cid_cb.v40.cb_addr.ss; switch (proto) { case IPPROTO_TCP: raddr.maxlen = raddr.len = sizeof(struct sockaddr_in); chan->clnt = clnt_vc_ncreatef(fd, &raddr, clientid->cid_cb.v40.cb_program, NFS_CB /* Errata ID: 2291 */, 0, 0, CLNT_CREATE_FLAG_CLOSE | CLNT_CREATE_FLAG_CONNECT); break; case IPPROTO_UDP: raddr.maxlen = raddr.len = sizeof(struct sockaddr_in6); chan->clnt = clnt_dg_ncreatef(fd, &raddr, clientid->cid_cb.v40.cb_program, NFS_CB /* Errata ID: 2291 */, 0, 0, CLNT_CREATE_FLAG_CLOSE); break; default: break; } if (CLNT_FAILURE(chan->clnt)) { err = rpc_sperror(&chan->clnt->cl_error, "failed"); LogDebug(COMPONENT_NFS_CB, "%s", err); gsh_free(err); CLNT_DESTROY(chan->clnt); chan->clnt = NULL; return EINVAL; } /* channel protection */ switch (clientid->cid_credential.flavor) { #ifdef _HAVE_GSSAPI case RPCSEC_GSS: nfs_rpc_callback_setup_gss(chan, &clientid->cid_credential); break; #endif /* _HAVE_GSSAPI */ case AUTH_SYS: chan->auth = authunix_ncreate_default(); break; case AUTH_NONE: chan->auth = authnone_ncreate(); break; default: return EINVAL; } if (AUTH_FAILURE(chan->auth)) { err = rpc_sperror(&chan->auth->ah_error, "failed"); LogDebug(COMPONENT_NFS_CB, "%s", err); gsh_free(err); AUTH_DESTROY(chan->auth); chan->auth = NULL; return EINVAL; } return 0; } /** * @brief Dispose of a channel * * The caller should hold the channel mutex. * * @param[in] chan The channel to dispose of */ static void _nfs_rpc_destroy_chan(rpc_call_channel_t *chan) { assert(chan); /* clean up auth, if any */ if (chan->auth) { AUTH_DESTROY(chan->auth); chan->auth = NULL; } /* channel has a dedicated RPC client */ if (chan->clnt) { /* destroy it */ CLNT_DESTROY(chan->clnt); chan->clnt = NULL; } chan->last_called = 0; } /** * Call the NFSv4 client's CB_NULL procedure. * * @param[in] chan Channel on which to call * @param[in] timeout Timeout for client call * @param[in] locked True if the channel is already locked * * @return Client status. */ static enum clnt_stat rpc_cb_null(rpc_call_channel_t *chan, bool locked) { struct clnt_req *cc; enum clnt_stat stat; /* XXX TI-RPC does the signal masking */ if (!locked) PTHREAD_MUTEX_lock(&chan->mtx); if (!chan->clnt) { stat = RPC_INTR; goto unlock; } cc = gsh_malloc(sizeof(*cc)); clnt_req_fill(cc, chan->clnt, chan->auth, CB_NULL, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_void, NULL); stat = clnt_req_setup(cc, tout); if (stat == RPC_SUCCESS) { cc->cc_refreshes = 1; stat = CLNT_CALL_WAIT(cc); } clnt_req_release(cc); /* If a call fails, we have to assume path down, or equally fatal * error. We may need back-off. */ if (stat != RPC_SUCCESS) _nfs_rpc_destroy_chan(chan); unlock: if (!locked) PTHREAD_MUTEX_unlock(&chan->mtx); return stat; } /** * @brief Create a channel for an NFSv4.1 session * * This function creates a channel on an NFSv4.1 session, using the * given security parameters. If a channel already exists, it is * removed and replaced. * * @param[in,out] session The session on which to create the * back channel * @param[in] num_sec_parms Length of sec_parms list * @param[in] sec_parms Allowable security parameters * * @return 0 or POSIX error code. */ int nfs_rpc_create_chan_v41(nfs41_session_t *session, int num_sec_parms, callback_sec_parms4 *sec_parms) { rpc_call_channel_t *chan = &session->cb_chan; char *err; int i; int code = 0; bool authed = false; PTHREAD_MUTEX_lock(&chan->mtx); if (chan->clnt) { /* Something better later. */ code = EEXIST; goto out; } chan->type = RPC_CHAN_V41; chan->source.session = session; assert(session->xprt); if (svc_get_xprt_type(session->xprt) == XPRT_RDMA) { LogWarn(COMPONENT_NFS_CB, "refusing to create back channel over RDMA for now"); code = EINVAL; goto out; } /* connect an RPC client * Use version 1 per errata ID 2291 for RFC 5661 */ chan->clnt = clnt_vc_ncreate_svc(session->xprt, session->cb_program, NFS_CB /* Errata ID: 2291 */, CLNT_CREATE_FLAG_NONE); if (CLNT_FAILURE(chan->clnt)) { err = rpc_sperror(&chan->clnt->cl_error, "failed"); LogDebug(COMPONENT_NFS_CB, "%s", err); gsh_free(err); CLNT_DESTROY(chan->clnt); chan->clnt = NULL; code = EINVAL; goto out; } for (i = 0; i < num_sec_parms; ++i) { if (sec_parms[i].cb_secflavor == AUTH_NONE) { chan->auth = authnone_ncreate(); authed = true; break; } else if (sec_parms[i].cb_secflavor == AUTH_SYS) { struct authunix_parms *sys_parms = &sec_parms[i].callback_sec_parms4_u.cbsp_sys_cred; chan->auth = authunix_ncreate(sys_parms->aup_machname, sys_parms->aup_uid, sys_parms->aup_gid, sys_parms->aup_len, sys_parms->aup_gids); if (AUTH_SUCCESS(chan->auth)) { authed = true; break; } } else if (sec_parms[i].cb_secflavor == RPCSEC_GSS) { /** * @todo ACE: Come back later and implement * GSS. */ continue; } else { LogMajor(COMPONENT_NFS_CB, "Client sent unknown auth type."); continue; } err = rpc_sperror(&chan->auth->ah_error, "failed"); LogDebug(COMPONENT_NFS_CB, "%s", err); gsh_free(err); AUTH_DESTROY(chan->auth); chan->auth = NULL; } if (!authed) { code = EPERM; LogMajor(COMPONENT_NFS_CB, "No working auth in sec_params."); goto out; } if (rpc_cb_null(chan, true) != RPC_SUCCESS) #ifdef EBADFD code = EBADFD; #else /* !EBADFD */ code = EBADF; #endif /* !EBADFD */ else atomic_set_uint32_t_bits(&session->flags, session_bc_up); out: if (code != 0) { LogWarn(COMPONENT_NFS_CB, "can not create back channel, code %d", code); if (chan->clnt) _nfs_rpc_destroy_chan(chan); } PTHREAD_MUTEX_unlock(&chan->mtx); return code; } /** * @brief Get a backchannel for a clientid * * This function works for both NFSv4.0 and NFSv4.1. For NFSv4.0, if * the channel isn't up, it tries to create it. * * @param[in,out] clientid The clientid to use * @param[out] flags Unused * * @return The back channel or NULL if none existed or could be * established. */ rpc_call_channel_t *nfs_rpc_get_chan(nfs_client_id_t *clientid, uint32_t flags) { rpc_call_channel_t *chan; struct glist_head *glist; nfs41_session_t *session; if (clientid->cid_minorversion == 0) { chan = &clientid->cid_cb.v40.cb_chan; if (!chan->clnt) (void)nfs_rpc_create_chan_v40(clientid, flags); return chan; } /* Get the first working back channel we have */ chan = NULL; pthread_mutex_lock(&clientid->cid_mutex); glist_for_each(glist, &clientid->cid_cb.v41.cb_session_list) { session = glist_entry(glist, nfs41_session_t, session_link); if (atomic_fetch_uint32_t(&session->flags) & session_bc_up) { chan = &session->cb_chan; break; } } pthread_mutex_unlock(&clientid->cid_mutex); return chan; } /** * @brief Dispose of a channel * * @param[in] chan The channel to dispose of */ void nfs_rpc_destroy_chan(rpc_call_channel_t *chan) { assert(chan); PTHREAD_MUTEX_lock(&chan->mtx); _nfs_rpc_destroy_chan(chan); PTHREAD_MUTEX_unlock(&chan->mtx); } /** * @brief Free callback arguments * * @param[in] op The argop to free */ static inline void free_argop(nfs_cb_argop4 *op) { gsh_free(op); } /** * @brief Free callback result * * @param[in] op The resop to free */ static inline void free_resop(nfs_cb_resop4 *op) { gsh_free(op); } /** * @brief Allocate an RPC call * * @return The newly allocated call or NULL. */ rpc_call_t *alloc_rpc_call(void) { request_data_t *reqdata = pool_alloc(request_pool); (void) atomic_inc_uint64_t(&health.enqueued_reqs); reqdata->rtype = NFS_CALL; return &reqdata->r_u.call; } /** * @brief Free an RPC call * * @param[in] call The call to free */ void free_rpc_call(rpc_call_t *call) { free_argop(call->cbt.v_u.v4.args.argarray.argarray_val); free_resop(call->cbt.v_u.v4.res.resarray.resarray_val); clnt_req_release(&call->call_req); } /** * @brief Free the RPC call context * * @param[in] cc The call context to free */ static void nfs_rpc_call_free(struct clnt_req *cc, size_t unused) { rpc_call_t *call = container_of(cc, rpc_call_t, call_req); request_data_t *reqdata = container_of(call, request_data_t, r_u.call); pool_free(request_pool, reqdata); (void) atomic_inc_uint64_t(&health.dequeued_reqs); } /** * @brief Call response processing * * @param[in] cc The RPC call request context */ static void nfs_rpc_call_process(struct clnt_req *cc) { rpc_call_t *call = container_of(cc, rpc_call_t, call_req); /* always TCP for retries, cc_refreshes only for AUTH_REFRESH() */ if (cc->cc_error.re_status == RPC_AUTHERROR && cc->cc_refreshes-- > 0 && AUTH_REFRESH(cc->cc_auth, NULL)) { if (clnt_req_refresh(cc) == RPC_SUCCESS) { cc->cc_error.re_status = CLNT_CALL_BACK(cc); return; } } call->states |= NFS_CB_CALL_FINISHED; if (call->call_hook) call->call_hook(call); free_rpc_call(call); } /** * @brief Dispatch a call * * @param[in,out] call The call to dispatch * @param[in] flags Flags governing call * * @return enum clnt_stat. */ enum clnt_stat nfs_rpc_call(rpc_call_t *call, uint32_t flags) { struct clnt_req *cc = &call->call_req; call->states = NFS_CB_CALL_DISPATCH; /* XXX TI-RPC does the signal masking */ PTHREAD_MUTEX_lock(&call->chan->mtx); clnt_req_fill(cc, call->chan->clnt, call->chan->auth, CB_COMPOUND, (xdrproc_t) xdr_CB_COMPOUND4args, &call->cbt.v_u.v4.args, (xdrproc_t) xdr_CB_COMPOUND4res, &call->cbt.v_u.v4.res); cc->cc_size = sizeof(request_data_t); cc->cc_free_cb = nfs_rpc_call_free; if (!call->chan->clnt) { cc->cc_error.re_status = RPC_INTR; goto unlock; } if (clnt_req_setup(cc, tout) == RPC_SUCCESS) { cc->cc_process_cb = nfs_rpc_call_process; cc->cc_error.re_status = CLNT_CALL_BACK(cc); } /* If a call fails, we have to assume path down, or equally fatal * error. We may need back-off. */ if (cc->cc_error.re_status != RPC_SUCCESS) { _nfs_rpc_destroy_chan(call->chan); call->states |= NFS_CB_CALL_ABORTED; } unlock: PTHREAD_MUTEX_unlock(&call->chan->mtx); /* any broadcast or signalling done in completion function */ return cc->cc_error.re_status; } /** * @brief Abort a call * * @param[in] call The call to abort * * @todo function doesn't seem to do anything. * * @return But it does it successfully. */ int32_t nfs_rpc_abort_call(rpc_call_t *call) { return 0; } /** * @brief Construct a CB_COMPOUND for v41 * * This function constructs a compound with a CB_SEQUENCE and one * other operation. * * @param[in] session Session on whose back channel we make the call * @param[in] op The operation to add * @param[in] refer Referral data, NULL if none * @param[in] slot Slot number to use * * @return The constructed call or NULL. */ static rpc_call_t *construct_v41(nfs41_session_t *session, nfs_cb_argop4 *op, struct state_refer *refer, slotid4 slot, slotid4 highest_slot) { rpc_call_t *call = alloc_rpc_call(); nfs_cb_argop4 sequenceop; CB_SEQUENCE4args *sequence = &sequenceop.nfs_cb_argop4_u.opcbsequence; const uint32_t minor = session->clientid_record->cid_minorversion; call->chan = &session->cb_chan; cb_compound_init_v4(&call->cbt, 2, minor, 0, NULL, 0); memset(sequence, 0, sizeof(CB_SEQUENCE4args)); sequenceop.argop = NFS4_OP_CB_SEQUENCE; memcpy(sequence->csa_sessionid, session->session_id, NFS4_SESSIONID_SIZE); sequence->csa_sequenceid = session->bc_slots[slot].sequence; sequence->csa_slotid = slot; sequence->csa_highest_slotid = highest_slot; sequence->csa_cachethis = false; if (refer) { referring_call_list4 *list; referring_call4 *ref_call = NULL; list = gsh_calloc(1, sizeof(referring_call_list4)); ref_call = gsh_malloc(sizeof(referring_call4)); sequence->csa_referring_call_lists.csa_referring_call_lists_len = 1; sequence->csa_referring_call_lists.csa_referring_call_lists_val = list; memcpy(list->rcl_sessionid, refer->session, sizeof(NFS4_SESSIONID_SIZE)); list->rcl_referring_calls.rcl_referring_calls_len = 1; list->rcl_referring_calls.rcl_referring_calls_val = ref_call; ref_call->rc_sequenceid = refer->sequence; ref_call->rc_slotid = refer->slot; } else { sequence->csa_referring_call_lists.csa_referring_call_lists_len = 0; sequence->csa_referring_call_lists.csa_referring_call_lists_val = NULL; } cb_compound_add_op(&call->cbt, &sequenceop); cb_compound_add_op(&call->cbt, op); return call; } /** * @brief Free a CB sequence for v41 * * @param[in] call The call to free */ static void release_v41(rpc_call_t *call) { nfs_cb_argop4 *argarray_val = call->cbt.v_u.v4.args.argarray.argarray_val; CB_SEQUENCE4args *sequence = &argarray_val[0].nfs_cb_argop4_u.opcbsequence; referring_call_list4 *call_lists = sequence->csa_referring_call_lists.csa_referring_call_lists_val; if (call_lists == NULL) return; gsh_free(call_lists->rcl_referring_calls.rcl_referring_calls_val); gsh_free(call_lists); } /** * @brief Find a callback slot * * Find and reserve a slot, if we can. If @c wait is set to true, we * wait on the condition variable for a limited time. * * @param[in,out] session Sesson on which to operate * @param[in] wait Whether to wait on the condition variable if * no slot can be found * @param[out] slot Slot to use * @param[out] highest_slot Highest slot in use * * @retval false if a slot was not found. * @retval true if a slot was found. */ static bool find_cb_slot(nfs41_session_t *session, bool wait, slotid4 *slot, slotid4 *highest_slot) { slotid4 cur = 0; bool found = false; PTHREAD_MUTEX_lock(&session->cb_mutex); retry: for (cur = 0; cur < MIN(session->back_channel_attrs.ca_maxrequests, session->nb_slots); ++cur) { if (!(session->bc_slots[cur].in_use) && (!found)) { found = true; *slot = cur; *highest_slot = cur; } if (session->bc_slots[cur].in_use) *highest_slot = cur; } if (!found && wait) { struct timespec ts; bool woke = false; clock_gettime(CLOCK_REALTIME, &ts); timespec_addms(&ts, 100); woke = (pthread_cond_timedwait(&session->cb_cond, &session->cb_mutex, &ts) != ETIMEDOUT); if (woke) { wait = false; goto retry; } } if (found) { session->bc_slots[*slot].in_use = true; ++session->bc_slots[*slot].sequence; assert(*slot < session->back_channel_attrs.ca_maxrequests); } PTHREAD_MUTEX_unlock(&session->cb_mutex); return found; } /** * @brief Release a reserved callback slot and wake waiters * * @param[in,out] session Session holding slot to release * @param[in] slot Slot to release * @param[in] bool Whether the operation was ever sent */ static void release_cb_slot(nfs41_session_t *session, slotid4 slot, bool sent) { PTHREAD_MUTEX_lock(&session->cb_mutex); session->bc_slots[slot].in_use = false; if (!sent) --session->bc_slots[slot].sequence; pthread_cond_broadcast(&session->cb_cond); PTHREAD_MUTEX_unlock(&session->cb_mutex); } static int nfs_rpc_v41_single(nfs_client_id_t *clientid, nfs_cb_argop4 *op, struct state_refer *refer, void (*completion)(rpc_call_t *), void *completion_arg) { struct glist_head *glist; int ret = ENOTCONN; bool wait = false; restart: pthread_mutex_lock(&clientid->cid_mutex); glist_for_each(glist, &clientid->cid_cb.v41.cb_session_list) { nfs41_session_t *scur, *session; slotid4 slot = 0; slotid4 highest_slot = 0; rpc_call_t *call = NULL; scur = glist_entry(glist, nfs41_session_t, session_link); /* * This is part of the infinite loop avoidance. When we * attempt to use a session and that fails, we clear the * session_bc_up flag. Then, we can avoid that session until * the backchannel has been reestablished. */ if (!(atomic_fetch_uint32_t(&scur->flags) & session_bc_up)) { LogDebug(COMPONENT_NFS_CB, "bc is down"); continue; } /* * We get a slot before we try to get a reference to the * session, which is odd, but necessary, as we can't hold * the cid_mutex when we go to put the session reference. */ if (!(find_cb_slot(scur, wait, &slot, &highest_slot))) { LogDebug(COMPONENT_NFS_CB, "can't get slot"); continue; } /* * Get a reference to the session. * * @todo: We don't really need to do the hashtable lookup * here since we have a pointer, but it's currently the only * safe way to get a reference. */ if (!nfs41_Session_Get_Pointer(scur->session_id, &session)) { release_cb_slot(scur, slot, false); continue; } assert(session == scur); /* Drop mutex since we have a session ref */ pthread_mutex_unlock(&clientid->cid_mutex); call = construct_v41(session, op, refer, slot, highest_slot); call->call_hook = completion; call->call_arg = completion_arg; ret = nfs_rpc_call(call, NFS_RPC_CALL_NONE); if (ret == 0) return 0; /* * Tear down channel since there is likely something * wrong with it. */ LogDebug(COMPONENT_NFS_CB, "nfs_rpc_call failed: %d", ret); atomic_clear_uint32_t_bits(&session->flags, session_bc_up); release_v41(call); free_rpc_call(call); release_cb_slot(session, slot, false); dec_session_ref(session); goto restart; } pthread_mutex_unlock(&clientid->cid_mutex); /* If it didn't work, then try again and wait on a slot */ if (ret && !wait) { wait = true; goto restart; } return ret; } /** * @brief Free information associated with any 'single' call */ void nfs41_release_single(rpc_call_t *call) { release_cb_slot(call->chan->source.session, call->cbt.v_u.v4.args.argarray.argarray_val[0] .nfs_cb_argop4_u.opcbsequence.csa_slotid, true); dec_session_ref(call->chan->source.session); release_v41(call); } /** * @brief test the state of callback channel for a clientid using NULL. * @return enum clnt_stat */ enum clnt_stat nfs_test_cb_chan(nfs_client_id_t *clientid) { rpc_call_channel_t *chan; enum clnt_stat stat; int retries = 1; /* create (fix?) channel */ do { chan = nfs_rpc_get_chan(clientid, NFS_RPC_FLAG_NONE); if (!chan) { LogCrit(COMPONENT_NFS_CB, "nfs_rpc_get_chan failed"); return RPC_SYSTEMERROR; } if (!chan->clnt) { LogCrit(COMPONENT_NFS_CB, "nfs_rpc_get_chan failed (no clnt)"); return RPC_SYSTEMERROR; } /* try the CB_NULL proc -- inline here, should be ok-ish */ stat = rpc_cb_null(chan, false); LogDebug(COMPONENT_NFS_CB, "rpc_cb_null on client %p returns %d", clientid, stat); /* RPC_INTR indicates that we should refresh the * channel and retry */ } while (stat == RPC_INTR && retries-- > 0); return stat; } static int nfs_rpc_v40_single(nfs_client_id_t *clientid, nfs_cb_argop4 *op, void (*completion)(rpc_call_t *), void *completion_arg) { rpc_call_channel_t *chan; rpc_call_t *call; int rc; /* Attempt a recall only if channel state is UP */ if (get_cb_chan_down(clientid)) { LogCrit(COMPONENT_NFS_CB, "Call back channel down, not issuing a recall"); return ENOTCONN; } chan = nfs_rpc_get_chan(clientid, NFS_RPC_FLAG_NONE); if (!chan) { LogCrit(COMPONENT_NFS_CB, "nfs_rpc_get_chan failed"); /* TODO: move this to nfs_rpc_get_chan ? */ set_cb_chan_down(clientid, true); return ENOTCONN; } if (!chan->clnt) { LogCrit(COMPONENT_NFS_CB, "nfs_rpc_get_chan failed (no clnt)"); set_cb_chan_down(clientid, true); return ENOTCONN; } call = alloc_rpc_call(); call->chan = chan; cb_compound_init_v4(&call->cbt, 1, 0, clientid->cid_cb.v40.cb_callback_ident, NULL, 0); cb_compound_add_op(&call->cbt, op); call->call_hook = completion; call->call_arg = completion_arg; rc = nfs_rpc_call(call, NFS_RPC_CALL_NONE); if (rc) free_rpc_call(call); return rc; } /** * @brief Send CB_COMPOUND with a single operation * * In the case of v4.1+, this actually sends two opearations, a CB_SEQUENCE * and the supplied operation. It works as a convenience function to handle * the details of callback management, finding a connection with a working * back channel, and so forth. * * @note This should work for most practical purposes, but is not * ideal. What we ought to have is a per-clientid queue that * operations can be submitted to that will be sent when a * back-channel is re-established, with a per-session queue for * operations that were sent but had the back-channel fail before the * response was received. * * @param[in] clientid Client record * @param[in] op The operation to perform * @param[in] refer Referral tracking info (or NULL) * @param[in] completion Completion function for this operation * @param[in] c_arg Argument provided to completion hook * * @return POSIX error codes. */ int nfs_rpc_cb_single(nfs_client_id_t *clientid, nfs_cb_argop4 *op, struct state_refer *refer, void (*completion)(rpc_call_t *), void *c_arg) { if (clientid->cid_minorversion == 0) return nfs_rpc_v40_single(clientid, op, completion, c_arg); return nfs_rpc_v41_single(clientid, op, refer, completion, c_arg); } nfs-ganesha-2.6.0/src/MainNFSD/nfs_rpc_callback_simulator.c000066400000000000000000000252301324272410200235470ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) 2010, The Linux Box Corporation * Copyright (c) 2010-2017 Red Hat, Inc. and/or its affiliates. * Contributor : Matt Benjamin * * Some portions Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ #include "config.h" #include #include #include #include #include #include #include "gsh_list.h" #include "abstract_mem.h" #include "fsal.h" #include "nfs_core.h" #include "log.h" #include "nfs_rpc_callback.h" #include "nfs_rpc_callback_simulator.h" #include "sal_functions.h" #include "gsh_dbus.h" /** * @file nfs_rpc_callback_simulator.c * @author Matt Benjamin * @author Lee Dobryden * @brief RPC callback dispatch package * * This module implements a stocastic dispatcher for callbacks, which * works by traversing the list of connected clients and, dispatching * a callback at random in consideration of state. * * This concept is inspired by the upcall simulator, though * necessarily less fully satisfactory until delegation and layout * state are available. */ /** * @brief Return a timestamped list of NFSv4 client ids. * * For all NFSv4 clients, a clientid reliably indicates a callback * channel * * @param args (not used) * @param reply the message reply */ static bool nfs_rpc_cbsim_get_v40_client_ids(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { hash_table_t *ht = ht_confirmed_client_id; struct rbt_head *head_rbt; struct hash_data *pdata = NULL; struct rbt_node *pn; nfs_client_id_t *pclientid; uint64_t clientid; DBusMessageIter iter, sub_iter; struct timespec ts; uint32_t i; /* create a reply from the message */ now(&ts); dbus_message_iter_init_append(reply, &iter); dbus_append_timestamp(&iter, &ts); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT64_AS_STRING, &sub_iter); /* For each bucket of the hashtable */ for (i = 0; i < ht->parameter.index_size; i++) { head_rbt = &(ht->partitions[i].rbt); /* acquire mutex */ PTHREAD_RWLOCK_wrlock(&(ht->partitions[i].lock)); /* go through all entries in the red-black-tree */ RBT_LOOP(head_rbt, pn) { pdata = RBT_OPAQ(pn); pclientid = pdata->val.addr; clientid = pclientid->cid_clientid; dbus_message_iter_append_basic(&sub_iter, DBUS_TYPE_UINT64, &clientid); RBT_INCREMENT(pn); } PTHREAD_RWLOCK_unlock(&(ht->partitions[i].lock)); } dbus_message_iter_close_container(&iter, &sub_iter); return true; } /* DBUS get_client_ids method descriptor */ static struct gsh_dbus_method cbsim_get_client_ids = { .name = "get_client_ids", .method = nfs_rpc_cbsim_get_v40_client_ids, .args = { { .name = "time", .type = "(tt)", .direction = "out"}, { .name = "clientids", .type = "at", .direction = "out"}, {NULL, NULL, NULL} } }; /** * @brief Return a timestamped list of session ids. * * @param args (not used) * @param reply the message reply */ static bool nfs_rpc_cbsim_get_session_ids(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { uint32_t i; hash_table_t *ht = ht_session_id; struct rbt_head *head_rbt; struct hash_data *pdata = NULL; struct rbt_node *pn; char session_id[2 * NFS4_SESSIONID_SIZE]; /* guaranteed to fit */ nfs41_session_t *session_data; DBusMessageIter iter, sub_iter; struct timespec ts; /* create a reply from the message */ now(&ts); dbus_message_iter_init_append(reply, &iter); dbus_append_timestamp(&iter, &ts); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT64_AS_STRING, &sub_iter); /* For each bucket of the hashtable */ for (i = 0; i < ht->parameter.index_size; i++) { head_rbt = &(ht->partitions[i].rbt); /* acquire mutex */ PTHREAD_RWLOCK_wrlock(&(ht->partitions[i].lock)); /* go through all entries in the red-black-tree */ RBT_LOOP(head_rbt, pn) { pdata = RBT_OPAQ(pn); session_data = pdata->val.addr; /* format */ b64_ntop((unsigned char *)session_data->session_id, NFS4_SESSIONID_SIZE, session_id, (2 * NFS4_SESSIONID_SIZE)); dbus_message_iter_append_basic(&sub_iter, DBUS_TYPE_STRING, &session_id); RBT_INCREMENT(pn); } PTHREAD_RWLOCK_unlock(&(ht->partitions[i].lock)); } dbus_message_iter_close_container(&iter, &sub_iter); return true; } /* DBUS get_session_ids method descriptor */ static struct gsh_dbus_method cbsim_get_session_ids = { .name = "get_session_ids", .method = nfs_rpc_cbsim_get_session_ids, .args = { { .name = "time", .type = "(tt)", .direction = "out"}, { .name = "sessionids", .type = "at", .direction = "out"}, {NULL, NULL, NULL} } }; static int cbsim_test_bchan(clientid4 clientid) { nfs_client_id_t *pclientid = NULL; int code; code = nfs_client_id_get_confirmed(clientid, &pclientid); if (code != CLIENT_ID_SUCCESS) { LogCrit(COMPONENT_NFS_CB, "No clid record for %" PRIx64 " (%d) code %d", clientid, (int32_t) clientid, code); return EINVAL; } nfs_test_cb_chan(pclientid); return code; } /** * Demonstration callback invocation. */ static void cbsim_free_compound(nfs4_compound_t *cbt) __attribute__ ((unused)); static void cbsim_free_compound(nfs4_compound_t *cbt) { int ix; nfs_cb_argop4 *argop = NULL; for (ix = 0; ix < cbt->v_u.v4.args.argarray.argarray_len; ++ix) { argop = cbt->v_u.v4.args.argarray.argarray_val + ix; if (argop) { CB_RECALL4args *opcbrecall = &argop->nfs_cb_argop4_u.opcbrecall; switch (argop->argop) { case NFS4_OP_CB_RECALL: gsh_free(opcbrecall->fh.nfs_fh4_val); break; default: /* TODO: ahem */ break; } } } /* XXX general free (move ?) */ cb_compound_free(cbt); } static void cbsim_completion_func(rpc_call_t *call) { LogDebug(COMPONENT_NFS_CB, "%p %s", call, !(call->states & NFS_CB_CALL_ABORTED) ? "Success" : "Failed"); if (!(call->states & NFS_CB_CALL_ABORTED)) { /* potentially, do something more interesting here */ LogMidDebug(COMPONENT_NFS_CB, "call result: %d", call->call_req.cc_error.re_status); } else { LogDebug(COMPONENT_NFS_CB, "Aborted: %d", call->call_req.cc_error.re_status); } } static int cbsim_fake_cbrecall(clientid4 clientid) { nfs_client_id_t *pclientid = NULL; rpc_call_channel_t *chan = NULL; nfs_cb_argop4 argop[1]; rpc_call_t *call; int code; LogDebug(COMPONENT_NFS_CB, "called with clientid %" PRIx64, clientid); code = nfs_client_id_get_confirmed(clientid, &pclientid); if (code != CLIENT_ID_SUCCESS) { LogCrit(COMPONENT_NFS_CB, "No clid record for %" PRIx64 " (%d) code %d", clientid, (int32_t) clientid, code); code = EINVAL; goto out; } assert(pclientid); chan = nfs_rpc_get_chan(pclientid, NFS_RPC_FLAG_NONE); if (!chan) { LogCrit(COMPONENT_NFS_CB, "nfs_rpc_get_chan failed"); goto out; } if (!chan->clnt) { LogCrit(COMPONENT_NFS_CB, "nfs_rpc_get_chan failed (no clnt)"); goto out; } /* allocate a new call--freed in completion hook */ call = alloc_rpc_call(); call->chan = chan; /* setup a compound */ cb_compound_init_v4(&call->cbt, 6, 0, pclientid->cid_cb.v40.cb_callback_ident, "brrring!!!", 10); /* TODO: api-ify */ memset(argop, 0, sizeof(nfs_cb_argop4)); argop->argop = NFS4_OP_CB_RECALL; argop->nfs_cb_argop4_u.opcbrecall.stateid.seqid = 0xdeadbeef; strlcpy(argop->nfs_cb_argop4_u.opcbrecall.stateid.other, "0xdeadbeef", 12); argop->nfs_cb_argop4_u.opcbrecall.truncate = TRUE; argop->nfs_cb_argop4_u.opcbrecall.fh.nfs_fh4_len = 11; /* leaks, sorry */ argop->nfs_cb_argop4_u.opcbrecall.fh.nfs_fh4_val = gsh_strdup("0xabadcafe"); /* add ops, till finished (dont exceed count) */ cb_compound_add_op(&call->cbt, argop); /* set completion hook */ call->call_hook = cbsim_completion_func; call->call_arg = NULL; /* call it (here, in current thread context) */ code = nfs_rpc_call(call, NFS_RPC_CALL_NONE); if (code) free_rpc_call(call); out: return code; } /** * @brief Fake/force a recall of a client id. * * For all NFSv4 clients, a clientid reliably indicates a callback * channel * * @param args the client id to be recalled * @param reply the message reply (empty) */ static bool nfs_rpc_cbsim_fake_recall(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { clientid4 clientid = 9315; /* XXX ew! */ LogDebug(COMPONENT_NFS_CB, "called!"); /* read the arguments */ if (args == NULL) { LogDebug(COMPONENT_DBUS, "message has no arguments"); } else if (dbus_message_iter_get_arg_type(args) != DBUS_TYPE_UINT64) { LogDebug(COMPONENT_DBUS, "arg not uint64"); } else { dbus_message_iter_get_basic(args, &clientid); LogDebug(COMPONENT_DBUS, "param: %" PRIx64, clientid); } cbsim_test_bchan(clientid); cbsim_fake_cbrecall(clientid); return true; } /* DBUS fake_recall method descriptor */ static struct gsh_dbus_method cbsim_fake_recall = { .name = "fake_recall", .method = nfs_rpc_cbsim_fake_recall, .args = { { .name = "clientid", .type = "t", .direction = "in"}, {NULL, NULL, NULL} } }; /* DBUS org.ganesha.nfsd.cbsim methods list */ static struct gsh_dbus_method *cbsim_methods[] = { &cbsim_get_client_ids, &cbsim_get_session_ids, &cbsim_fake_recall, NULL }; static struct gsh_dbus_interface cbsim_interface = { .name = "org.ganesha.nfsd.cbsim", .props = NULL, .methods = cbsim_methods, .signals = NULL }; /* DBUS list of interfaces on /org/ganesha/nfsd/CBSIM */ static struct gsh_dbus_interface *cbsim_interfaces[] = { &cbsim_interface, NULL }; /** * @brief Initialize subsystem */ void nfs_rpc_cbsim_pkginit(void) { gsh_dbus_register_path("CBSIM", cbsim_interfaces); LogEvent(COMPONENT_NFS_CB, "Callback Simulator Initialized"); } /** * @brief Shutdown subsystem */ void nfs_rpc_cbsim_pkgshutdown(void) { /* return */ } nfs-ganesha-2.6.0/src/MainNFSD/nfs_rpc_dispatcher_thread.c000066400000000000000000001065621324272410200234010ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * William Allen Simpson * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_rpc_dispatcher_thread.c * @brief Contains the @c rpc_dispatcher_thread routine and support code */ #include "config.h" #include #include #include #include #include /* for having FNDELAY */ #include #include #ifdef RPC_VSOCK #include #include #include #endif #include #include #include #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "abstract_atomic.h" #include "nfs23.h" #include "nfs4.h" #include "mount.h" #include "nlm4.h" #include "rquota.h" #include "nfs_init.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_dupreq.h" #include "nfs_file_handle.h" #define NFS_pcp nfs_param.core_param #define NFS_options NFS_pcp.core_options #define NFS_program NFS_pcp.program /** * TI-RPC event channels. Each channel is a thread servicing an event * demultiplexer. */ struct rpc_evchan { uint32_t chan_id; /*< Channel ID */ }; enum evchan { UDP_UREG_CHAN, /*< Put UDP on a dedicated channel */ TCP_UREG_CHAN, /*< Accepts new TCP connections */ #ifdef _USE_NFS_RDMA RDMA_UREG_CHAN, /*< Accepts new RDMA connections */ #endif EVCHAN_SIZE }; #define N_TCP_EVENT_CHAN 3 /*< We don't really want to have too many, relative to the number of available cores. */ #define N_EVENT_CHAN (N_TCP_EVENT_CHAN + EVCHAN_SIZE) static struct rpc_evchan rpc_evchan[EVCHAN_SIZE]; static enum xprt_stat nfs_rpc_tcp_user_data(SVCXPRT *); static enum xprt_stat nfs_rpc_free_user_data(SVCXPRT *); static enum xprt_stat nfs_rpc_decode_request(SVCXPRT *, XDR *); const char *xprt_stat_s[XPRT_DESTROYED + 1] = { "XPRT_IDLE", "XPRT_DISPATCH", "XPRT_DIED", "XPRT_DESTROYED" }; /** * @brief Function never called, but the symbol is needed for svc_register. * * @param[in] ptr_req Unused * @param[in] ptr_svc Unused */ void nfs_rpc_dispatch_dummy(struct svc_req *req) { LogMajor(COMPONENT_DISPATCH, "Possible error, function %s should never be called", __func__); } const char *tags[] = { "NFS", "MNT", "NLM", "RQUOTA", "NFS_VSOCK", "NFS_RDMA", }; typedef struct proto_data { struct sockaddr_in sinaddr_udp; struct sockaddr_in sinaddr_tcp; struct sockaddr_in6 sinaddr_udp6; struct sockaddr_in6 sinaddr_tcp6; struct netbuf netbuf_udp6; struct netbuf netbuf_tcp6; struct t_bind bindaddr_udp6; struct t_bind bindaddr_tcp6; struct __rpc_sockinfo si_udp6; struct __rpc_sockinfo si_tcp6; } proto_data; proto_data pdata[P_COUNT]; struct netconfig *netconfig_udpv4; struct netconfig *netconfig_tcpv4; struct netconfig *netconfig_udpv6; struct netconfig *netconfig_tcpv6; /* RPC Service Sockets and Transports */ int udp_socket[P_COUNT]; int tcp_socket[P_COUNT]; SVCXPRT *udp_xprt[P_COUNT]; SVCXPRT *tcp_xprt[P_COUNT]; /* Flag to indicate if V6 interfaces on the host are enabled */ bool v6disabled; bool vsock; bool rdma; /** * @brief Unregister an RPC program. * * @param[in] prog Program to unregister * @param[in] vers1 Lowest version * @param[in] vers2 Highest version */ static void unregister(const rpcprog_t prog, const rpcvers_t vers1, const rpcvers_t vers2) { rpcvers_t vers; for (vers = vers1; vers <= vers2; vers++) { rpcb_unset(prog, vers, netconfig_udpv4); rpcb_unset(prog, vers, netconfig_tcpv4); if (netconfig_udpv6) rpcb_unset(prog, vers, netconfig_udpv6); if (netconfig_tcpv6) rpcb_unset(prog, vers, netconfig_tcpv6); } } static void unregister_rpc(void) { if ((NFS_options & CORE_OPTION_NFSV3) != 0) { unregister(NFS_program[P_NFS], NFS_V2, NFS_V4); unregister(NFS_program[P_MNT], MOUNT_V1, MOUNT_V3); } else { unregister(NFS_program[P_NFS], NFS_V4, NFS_V4); } #ifdef _USE_NLM if (nfs_param.core_param.enable_NLM) unregister(NFS_program[P_NLM], 1, NLM4_VERS); #endif /* _USE_NLM */ if (nfs_param.core_param.enable_RQUOTA) { unregister(NFS_program[P_RQUOTA], RQUOTAVERS, EXT_RQUOTAVERS); } } static inline bool nfs_protocol_enabled(protos p) { bool nfsv3 = NFS_options & CORE_OPTION_NFSV3; switch (p) { case P_NFS: return true; case P_MNT: /* valid only for NFSv3 environments */ if (nfsv3) return true; break; #ifdef _USE_NLM case P_NLM: /* valid only for NFSv3 environments */ if (nfsv3 && nfs_param.core_param.enable_NLM) return true; break; #endif case P_RQUOTA: if (nfs_param.core_param.enable_RQUOTA) return true; break; default: break; } return false; } /** * @brief Close transports and file descriptors used for RPC services. * * So that restarting the NFS server wont encounter issues of "Address * Already In Use" - this has occurred even though we set the * SO_REUSEADDR option when restarting the server with a single export * (i.e.: a small config) & no logging at all, making the restart very * fast. when closing a listening socket it will be closed * immediately if no connection is pending on it, hence drastically * reducing the probability for trouble. */ static void close_rpc_fd(void) { protos p; for (p = P_NFS; p < P_COUNT; p++) { if (udp_socket[p] != -1) close(udp_socket[p]); if (tcp_socket[p] != -1) close(tcp_socket[p]); } /* no need for special tcp_xprt[P_NFS_VSOCK] treatment */ } /** * @brief Dispatch after rendezvous * * Record activity on a rendezvous transport handle. * * @note * Cases are distinguished by separate callbacks for each fd. * UDP connections are bound to socket NFS_UDPSocket * TCP initial connections are bound to socket NFS_TCPSocket * all the other cases are requests from already connected TCP Clients */ static enum xprt_stat nfs_rpc_dispatch_udp_NFS(SVCXPRT *xprt) { LogFullDebug(COMPONENT_DISPATCH, "NFS UDP request for SVCXPRT %p fd %d", xprt, xprt->xp_fd); xprt->xp_dispatch.process_cb = nfs_rpc_valid_NFS; return SVC_RECV(xprt); } static enum xprt_stat nfs_rpc_dispatch_udp_MNT(SVCXPRT *xprt) { LogFullDebug(COMPONENT_DISPATCH, "MOUNT UDP request for SVCXPRT %p fd %d", xprt, xprt->xp_fd); xprt->xp_dispatch.process_cb = nfs_rpc_valid_MNT; return SVC_RECV(xprt); } static enum xprt_stat nfs_rpc_dispatch_udp_NLM(SVCXPRT *xprt) { LogFullDebug(COMPONENT_DISPATCH, "NLM UDP request for SVCXPRT %p fd %d", xprt, xprt->xp_fd); xprt->xp_dispatch.process_cb = nfs_rpc_valid_NLM; return SVC_RECV(xprt); } static enum xprt_stat nfs_rpc_dispatch_udp_RQUOTA(SVCXPRT *xprt) { LogFullDebug(COMPONENT_DISPATCH, "RQUOTA UDP request for SVCXPRT %p fd %d", xprt, xprt->xp_fd); xprt->xp_dispatch.process_cb = nfs_rpc_valid_RQUOTA; return SVC_RECV(xprt); } const svc_xprt_fun_t udp_dispatch[] = { nfs_rpc_dispatch_udp_NFS, nfs_rpc_dispatch_udp_MNT, nfs_rpc_dispatch_udp_NLM, nfs_rpc_dispatch_udp_RQUOTA, NULL, NULL, }; static enum xprt_stat nfs_rpc_dispatch_tcp_NFS(SVCXPRT *xprt) { LogFullDebug(COMPONENT_DISPATCH, "NFS TCP request on SVCXPRT %p fd %d", xprt, xprt->xp_fd); xprt->xp_dispatch.process_cb = nfs_rpc_valid_NFS; return nfs_rpc_tcp_user_data(xprt); } static enum xprt_stat nfs_rpc_dispatch_tcp_MNT(SVCXPRT *xprt) { LogFullDebug(COMPONENT_DISPATCH, "MOUNT TCP request on SVCXPRT %p fd %d", xprt, xprt->xp_fd); xprt->xp_dispatch.process_cb = nfs_rpc_valid_MNT; return nfs_rpc_tcp_user_data(xprt); } static enum xprt_stat nfs_rpc_dispatch_tcp_NLM(SVCXPRT *xprt) { LogFullDebug(COMPONENT_DISPATCH, "NLM TCP request on SVCXPRT %p fd %d", xprt, xprt->xp_fd); xprt->xp_dispatch.process_cb = nfs_rpc_valid_NLM; return nfs_rpc_tcp_user_data(xprt); } static enum xprt_stat nfs_rpc_dispatch_tcp_RQUOTA(SVCXPRT *xprt) { LogFullDebug(COMPONENT_DISPATCH, "RQUOTA TCP request on SVCXPRT %p fd %d", xprt, xprt->xp_fd); xprt->xp_dispatch.process_cb = nfs_rpc_valid_RQUOTA; return nfs_rpc_tcp_user_data(xprt); } static enum xprt_stat nfs_rpc_dispatch_tcp_VSOCK(SVCXPRT *xprt) { LogFullDebug(COMPONENT_DISPATCH, "VSOCK TCP request on SVCXPRT %p fd %d", xprt, xprt->xp_fd); xprt->xp_dispatch.process_cb = nfs_rpc_valid_NFS; return nfs_rpc_tcp_user_data(xprt); } const svc_xprt_fun_t tcp_dispatch[] = { nfs_rpc_dispatch_tcp_NFS, nfs_rpc_dispatch_tcp_MNT, nfs_rpc_dispatch_tcp_NLM, nfs_rpc_dispatch_tcp_RQUOTA, nfs_rpc_dispatch_tcp_VSOCK, NULL, }; void Create_udp(protos prot) { udp_xprt[prot] = svc_dg_create(udp_socket[prot], nfs_param.core_param.rpc.max_send_buffer_size, nfs_param.core_param.rpc.max_recv_buffer_size); if (udp_xprt[prot] == NULL) LogFatal(COMPONENT_DISPATCH, "Cannot allocate %s/UDP SVCXPRT", tags[prot]); udp_xprt[prot]->xp_dispatch.rendezvous_cb = udp_dispatch[prot]; /* Hook xp_free_user_data (finalize/free private data) */ (void)SVC_CONTROL(udp_xprt[prot], SVCSET_XP_FREE_USER_DATA, nfs_rpc_free_user_data); (void)svc_rqst_evchan_reg(rpc_evchan[UDP_UREG_CHAN].chan_id, udp_xprt[prot], SVC_RQST_FLAG_XPRT_UREG); } void Create_tcp(protos prot) { tcp_xprt[prot] = svc_vc_ncreatef(tcp_socket[prot], nfs_param.core_param.rpc.max_send_buffer_size, nfs_param.core_param.rpc.max_recv_buffer_size, SVC_CREATE_FLAG_CLOSE | SVC_CREATE_FLAG_LISTEN); if (tcp_xprt[prot] == NULL) LogFatal(COMPONENT_DISPATCH, "Cannot allocate %s/TCP SVCXPRT", tags[prot]); tcp_xprt[prot]->xp_dispatch.rendezvous_cb = tcp_dispatch[prot]; /* Hook xp_free_user_data (finalize/free private data) */ (void)SVC_CONTROL(tcp_xprt[prot], SVCSET_XP_FREE_USER_DATA, nfs_rpc_free_user_data); (void)svc_rqst_evchan_reg(rpc_evchan[TCP_UREG_CHAN].chan_id, tcp_xprt[prot], SVC_RQST_FLAG_XPRT_UREG); } #ifdef _USE_NFS_RDMA struct rpc_rdma_attr rpc_rdma_xa = { .statistics_prefix = NULL, .node = "::", .port = "20049", .sq_depth = 32, /* default was 50 */ .max_send_sge = 32, /* minimum 2 */ .rq_depth = 32, /* default was 50 */ .max_recv_sge = 31, /* minimum 1 */ .backlog = 10, /* minimum 2 */ .credits = 30, /* default 10 */ .destroy_on_disconnect = true, .use_srq = false, }; static enum xprt_stat nfs_rpc_dispatch_RDMA(SVCXPRT *xprt) { LogFullDebug(COMPONENT_DISPATCH, "RDMA request on SVCXPRT %p fd %d", xprt, xprt->xp_fd); xprt->xp_dispatch.process_cb = nfs_rpc_valid_NFS; return SVC_STAT(xprt->xp_parent); } void Create_RDMA(protos prot) { /* This has elements of both UDP and TCP setup */ tcp_xprt[prot] = svc_rdma_create(&rpc_rdma_xa, nfs_param.core_param.rpc.max_send_buffer_size, nfs_param.core_param.rpc.max_recv_buffer_size); if (tcp_xprt[prot] == NULL) LogFatal(COMPONENT_DISPATCH, "Cannot allocate RPC/%s SVCXPRT", tags[prot]); tcp_xprt[prot]->xp_dispatch.rendezvous_cb = nfs_rpc_dispatch_RDMA; /* Hook xp_free_user_data (finalize/free private data) */ (void)SVC_CONTROL(tcp_xprt[prot], SVCSET_XP_FREE_USER_DATA, nfs_rpc_free_user_data); (void)svc_rqst_evchan_reg(rpc_evchan[RDMA_UREG_CHAN].chan_id, tcp_xprt[prot], SVC_RQST_FLAG_XPRT_UREG); } #endif /** * @brief Create the SVCXPRT for each protocol in use */ void Create_SVCXPRTs(void) { protos p; LogFullDebug(COMPONENT_DISPATCH, "Allocation of the SVCXPRT"); for (p = P_NFS; p < P_COUNT; p++) if (nfs_protocol_enabled(p)) { Create_udp(p); Create_tcp(p); } #ifdef RPC_VSOCK if (vsock) Create_tcp(P_NFS_VSOCK); #endif /* RPC_VSOCK */ #ifdef _USE_NFS_RDMA if (rdma) Create_RDMA(P_NFS_RDMA); #endif /* _USE_NFS_RDMA */ } /** * @brief Bind the udp and tcp sockets for V6 Interfaces */ static int Bind_sockets_V6(void) { protos p; int rc = 0; for (p = P_NFS; p < P_COUNT; p++) { if (nfs_protocol_enabled(p)) { proto_data *pdatap = &pdata[p]; memset(&pdatap->sinaddr_udp6, 0, sizeof(pdatap->sinaddr_udp6)); pdatap->sinaddr_udp6.sin6_family = AF_INET6; /* all interfaces */ pdatap->sinaddr_udp6.sin6_addr = ((struct sockaddr_in6 *) &nfs_param.core_param.bind_addr)->sin6_addr; pdatap->sinaddr_udp6.sin6_port = htons(nfs_param.core_param.port[p]); pdatap->netbuf_udp6.maxlen = sizeof(pdatap->sinaddr_udp6); pdatap->netbuf_udp6.len = sizeof(pdatap->sinaddr_udp6); pdatap->netbuf_udp6.buf = &pdatap->sinaddr_udp6; pdatap->bindaddr_udp6.qlen = SOMAXCONN; pdatap->bindaddr_udp6.addr = pdatap->netbuf_udp6; if (!__rpc_fd2sockinfo(udp_socket[p], &pdatap->si_udp6)) { LogWarn(COMPONENT_DISPATCH, "Cannot get %s socket info for udp6 socket errno=%d (%s)", tags[p], errno, strerror(errno)); return -1; } rc = bind(udp_socket[p], (struct sockaddr *)pdatap->bindaddr_udp6.addr.buf, (socklen_t) pdatap->si_udp6.si_alen); if (rc == -1) { LogWarn(COMPONENT_DISPATCH, "Cannot bind %s udp6 socket, error %d (%s)", tags[p], errno, strerror(errno)); goto exit; } memset(&pdatap->sinaddr_tcp6, 0, sizeof(pdatap->sinaddr_tcp6)); pdatap->sinaddr_tcp6.sin6_family = AF_INET6; /* all interfaces */ pdatap->sinaddr_tcp6.sin6_addr = ((struct sockaddr_in6 *) &nfs_param.core_param.bind_addr)->sin6_addr; pdatap->sinaddr_tcp6.sin6_port = htons(nfs_param.core_param.port[p]); pdatap->netbuf_tcp6.maxlen = sizeof(pdatap->sinaddr_tcp6); pdatap->netbuf_tcp6.len = sizeof(pdatap->sinaddr_tcp6); pdatap->netbuf_tcp6.buf = &pdatap->sinaddr_tcp6; pdatap->bindaddr_tcp6.qlen = SOMAXCONN; pdatap->bindaddr_tcp6.addr = pdatap->netbuf_tcp6; if (!__rpc_fd2sockinfo(tcp_socket[p], &pdatap->si_tcp6)) { LogWarn(COMPONENT_DISPATCH, "Cannot get %s socket info for tcp6 socket errno=%d (%s)", tags[p], errno, strerror(errno)); return -1; } rc = bind(tcp_socket[p], (struct sockaddr *) pdatap->bindaddr_tcp6.addr.buf, (socklen_t) pdatap->si_tcp6.si_alen); if (rc == -1) { LogWarn(COMPONENT_DISPATCH, "Cannot bind %s tcp6 socket, error %d (%s)", tags[p], errno, strerror(errno)); goto exit; } } } exit: return rc; } /** * @brief Bind the udp and tcp sockets for V4 Interfaces */ static int Bind_sockets_V4(void) { protos p; int rc = 0; for (p = P_NFS; p < P_COUNT; p++) { if (nfs_protocol_enabled(p)) { proto_data *pdatap = &pdata[p]; memset(&pdatap->sinaddr_udp, 0, sizeof(pdatap->sinaddr_udp)); pdatap->sinaddr_udp.sin_family = AF_INET; /* all interfaces */ pdatap->sinaddr_udp.sin_addr.s_addr = ((struct sockaddr_in *) &nfs_param.core_param.bind_addr)->sin_addr.s_addr; pdatap->sinaddr_udp.sin_port = htons(nfs_param.core_param.port[p]); pdatap->netbuf_udp6.maxlen = sizeof(pdatap->sinaddr_udp); pdatap->netbuf_udp6.len = sizeof(pdatap->sinaddr_udp); pdatap->netbuf_udp6.buf = &pdatap->sinaddr_udp; pdatap->bindaddr_udp6.qlen = SOMAXCONN; pdatap->bindaddr_udp6.addr = pdatap->netbuf_udp6; if (!__rpc_fd2sockinfo(udp_socket[p], &pdatap->si_udp6)) { LogWarn(COMPONENT_DISPATCH, "Cannot get %s socket info for udp6 socket errno=%d (%s)", tags[p], errno, strerror(errno)); return -1; } rc = bind(udp_socket[p], (struct sockaddr *) pdatap->bindaddr_udp6.addr.buf, (socklen_t) pdatap->si_udp6.si_alen); if (rc == -1) { LogWarn(COMPONENT_DISPATCH, "Cannot bind %s udp6 socket, error %d (%s)", tags[p], errno, strerror(errno)); return -1; } memset(&pdatap->sinaddr_tcp, 0, sizeof(pdatap->sinaddr_tcp)); pdatap->sinaddr_tcp.sin_family = AF_INET; /* all interfaces */ pdatap->sinaddr_udp.sin_addr.s_addr = ((struct sockaddr_in *) &nfs_param.core_param.bind_addr)->sin_addr.s_addr; pdatap->sinaddr_tcp.sin_port = htons(nfs_param.core_param.port[p]); pdatap->netbuf_tcp6.maxlen = sizeof(pdatap->sinaddr_tcp); pdatap->netbuf_tcp6.len = sizeof(pdatap->sinaddr_tcp); pdatap->netbuf_tcp6.buf = &pdatap->sinaddr_tcp; pdatap->bindaddr_tcp6.qlen = SOMAXCONN; pdatap->bindaddr_tcp6.addr = pdatap->netbuf_tcp6; if (!__rpc_fd2sockinfo(tcp_socket[p], &pdatap->si_tcp6)) { LogWarn(COMPONENT_DISPATCH, "V4 : Cannot get %s socket info for tcp socket error %d(%s)", tags[p], errno, strerror(errno)); return -1; } rc = bind(tcp_socket[p], (struct sockaddr *) pdatap->bindaddr_tcp6.addr.buf, (socklen_t) pdatap->si_tcp6.si_alen); if (rc == -1) { LogWarn(COMPONENT_DISPATCH, "Cannot bind %s tcp socket, error %d(%s)", tags[p], errno, strerror(errno)); return -1; } } } return rc; } #ifdef RPC_VSOCK int bind_sockets_vsock(void) { int rc = 0; struct sockaddr_vm sa_listen = { .svm_family = AF_VSOCK, .svm_cid = VMADDR_CID_ANY, .svm_port = nfs_param.core_param.port[P_NFS], }; rc = bind(tcp_socket[P_NFS_VSOCK], (struct sockaddr *) (struct sockaddr *)&sa_listen, sizeof(sa_listen)); if (rc == -1) { LogWarn(COMPONENT_DISPATCH, "cannot bind %s stream socket, error %d(%s)", tags[P_NFS_VSOCK], errno, strerror(errno)); } return rc; } #endif /* RPC_VSOCK */ void Bind_sockets(void) { int rc = 0; /* * See Allocate_sockets(), which should already * have set the global v6disabled accordingly */ if (v6disabled) { rc = Bind_sockets_V4(); if (rc) LogFatal(COMPONENT_DISPATCH, "Error binding to V4 interface. Cannot continue."); } else { rc = Bind_sockets_V6(); if (rc) LogFatal(COMPONENT_DISPATCH, "Error binding to V6 interface. Cannot continue."); } #ifdef RPC_VSOCK if (vsock) { rc = bind_sockets_vsock(); if (rc) LogMajor(COMPONENT_DISPATCH, "AF_VSOCK bind failed (continuing startup)"); } #endif /* RPC_VSOCK */ LogInfo(COMPONENT_DISPATCH, "Bind sockets successful, v6disabled = %d, vsock = %d, rdma = %d", v6disabled, vsock, rdma); } /** * @brief Function to set the socket options on the allocated * udp and tcp sockets * */ static int alloc_socket_setopts(int p) { int one = 1; const struct nfs_core_param *nfs_cp = &nfs_param.core_param; /* Use SO_REUSEADDR in order to avoid wait * the 2MSL timeout */ if (setsockopt(udp_socket[p], SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) { LogWarn(COMPONENT_DISPATCH, "Bad udp socket options for %s, error %d(%s)", tags[p], errno, strerror(errno)); return -1; } if (setsockopt(tcp_socket[p], SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) { LogWarn(COMPONENT_DISPATCH, "Bad tcp socket option reuseaddr for %s, error %d(%s)", tags[p], errno, strerror(errno)); return -1; } if (nfs_cp->enable_tcp_keepalive) { if (setsockopt(tcp_socket[p], SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one))) { LogWarn(COMPONENT_DISPATCH, "Bad tcp socket option keepalive for %s, error %d(%s)", tags[p], errno, strerror(errno)); return -1; } if (nfs_cp->tcp_keepcnt) { if (setsockopt(tcp_socket[p], IPPROTO_TCP, TCP_KEEPCNT, &nfs_cp->tcp_keepcnt, sizeof(nfs_cp->tcp_keepcnt))) { LogWarn(COMPONENT_DISPATCH, "Bad tcp socket option TCP_KEEPCNT for %s, error %d(%s)", tags[p], errno, strerror(errno)); return -1; } } if (nfs_cp->tcp_keepidle) { if (setsockopt(tcp_socket[p], IPPROTO_TCP, TCP_KEEPIDLE, &nfs_cp->tcp_keepidle, sizeof(nfs_cp->tcp_keepidle))) { LogWarn(COMPONENT_DISPATCH, "Bad tcp socket option TCP_KEEPIDLE for %s, error %d(%s)", tags[p], errno, strerror(errno)); return -1; } } if (nfs_cp->tcp_keepintvl) { if (setsockopt(tcp_socket[p], IPPROTO_TCP, TCP_KEEPINTVL, &nfs_cp->tcp_keepintvl, sizeof(nfs_cp->tcp_keepintvl))) { LogWarn(COMPONENT_DISPATCH, "Bad tcp socket option TCP_KEEPINTVL for %s, error %d(%s)", tags[p], errno, strerror(errno)); return -1; } } } /* We prefer using non-blocking socket * in the specific case */ if (fcntl(udp_socket[p], F_SETFL, FNDELAY) == -1) { LogWarn(COMPONENT_DISPATCH, "Cannot set udp socket for %s as non blocking, error %d(%s)", tags[p], errno, strerror(errno)); return -1; } return 0; } /** * @brief Allocate the tcp and udp sockets for the nfs daemon * using V4 interfaces */ static int Allocate_sockets_V4(int p) { udp_socket[p] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (udp_socket[p] == -1) { if (errno == EAFNOSUPPORT) { LogInfo(COMPONENT_DISPATCH, "No V6 and V4 intfs configured?!"); } LogWarn(COMPONENT_DISPATCH, "Cannot allocate a udp socket for %s, error %d(%s)", tags[p], errno, strerror(errno)); return -1; } tcp_socket[p] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (tcp_socket[p] == -1) { LogWarn(COMPONENT_DISPATCH, "Cannot allocate a tcp socket for %s, error %d(%s)", tags[p], errno, strerror(errno)); return -1; } return 0; } #ifdef RPC_VSOCK /** * @brief Create vmci stream socket */ static int allocate_socket_vsock(void) { int one = 1; tcp_socket[P_NFS_VSOCK] = socket(AF_VSOCK, SOCK_STREAM, 0); if (tcp_socket[P_NFS_VSOCK] == -1) { LogWarn(COMPONENT_DISPATCH, "socket create failed for %s, error %d(%s)", tags[P_NFS_VSOCK], errno, strerror(errno)); return -1; } if (setsockopt(tcp_socket[P_NFS_VSOCK], SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) { LogWarn(COMPONENT_DISPATCH, "bad tcp socket options for %s, error %d(%s)", tags[P_NFS_VSOCK], errno, strerror(errno)); return -1; } LogDebug(COMPONENT_DISPATCH, "Socket numbers are: %s tcp=%u", tags[P_NFS_VSOCK], tcp_socket[P_NFS_VSOCK]); return 0; } #endif /* RPC_VSOCK */ /** * @brief Allocate the tcp and udp sockets for the nfs daemon */ static void Allocate_sockets(void) { protos p; int rc = 0; LogFullDebug(COMPONENT_DISPATCH, "Allocation of the sockets"); for (p = P_NFS; p < P_COUNT; p++) { /* Initialize all the sockets to -1 because * it makes some code later easier */ udp_socket[p] = -1; tcp_socket[p] = -1; if (nfs_protocol_enabled(p)) { if (v6disabled) goto try_V4; udp_socket[p] = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); if (udp_socket[p] == -1) { /* * We assume that EAFNOSUPPORT points * to the likely case when the host has * V6 interfaces disabled. So we will * try to use the existing V4 interfaces * instead */ if (errno == EAFNOSUPPORT) { v6disabled = true; LogWarn(COMPONENT_DISPATCH, "System may not have V6 intfs configured error %d(%s)", errno, strerror(errno)); goto try_V4; } LogFatal(COMPONENT_DISPATCH, "Cannot allocate a udp socket for %s, error %d(%s)", tags[p], errno, strerror(errno)); } tcp_socket[p] = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); /* We fail with LogFatal here on error because it * shouldn't be that we have managed to create a * V6 based udp socket and have failed for the tcp * sock. If it were a case of V6 being disabled, * then we would have encountered that case with * the first udp sock create and would have moved * on to create the V4 sockets. */ if (tcp_socket[p] == -1) LogFatal(COMPONENT_DISPATCH, "Cannot allocate a tcp socket for %s, error %d(%s)", tags[p], errno, strerror(errno)); try_V4: if (v6disabled) { rc = Allocate_sockets_V4(p); if (rc) { LogFatal(COMPONENT_DISPATCH, "Error allocating V4 socket for proto %d, %s", p, tags[p]); } } rc = alloc_socket_setopts(p); if (rc) { LogFatal(COMPONENT_DISPATCH, "Error setting socket option for proto %d, %s", p, tags[p]); } LogDebug(COMPONENT_DISPATCH, "Socket numbers are: %s tcp=%u udp=%u", tags[p], tcp_socket[p], udp_socket[p]); } } #ifdef RPC_VSOCK if (vsock) allocate_socket_vsock(); #endif /* RPC_VSOCK */ } /* The following routine must ONLY be called from the shutdown * thread */ void Clean_RPC(void) { /** * @todo Consider the need to call Svc_dg_destroy for UDP & ?? for * TCP based services */ unregister_rpc(); close_rpc_fd(); freenetconfigent(netconfig_udpv4); freenetconfigent(netconfig_tcpv4); freenetconfigent(netconfig_udpv6); freenetconfigent(netconfig_tcpv6); } #define UDP_REGISTER(prot, vers, netconfig) \ svc_reg(udp_xprt[prot], NFS_program[prot], \ (u_long) vers, \ nfs_rpc_dispatch_dummy, netconfig) #define TCP_REGISTER(prot, vers, netconfig) \ svc_reg(tcp_xprt[prot], NFS_program[prot], \ (u_long) vers, \ nfs_rpc_dispatch_dummy, netconfig) void Register_program(protos prot, int flag, int vers) { if ((NFS_options & flag) != 0) { LogInfo(COMPONENT_DISPATCH, "Registering %s V%d/UDP", tags[prot], (int)vers); /* XXXX fix svc_register! */ if (!UDP_REGISTER(prot, vers, netconfig_udpv4)) LogFatal(COMPONENT_DISPATCH, "Cannot register %s V%d on UDP", tags[prot], (int)vers); if (!v6disabled && netconfig_udpv6) { LogInfo(COMPONENT_DISPATCH, "Registering %s V%d/UDPv6", tags[prot], (int)vers); if (!UDP_REGISTER(prot, vers, netconfig_udpv6)) LogFatal(COMPONENT_DISPATCH, "Cannot register %s V%d on UDPv6", tags[prot], (int)vers); } #ifndef _NO_TCP_REGISTER LogInfo(COMPONENT_DISPATCH, "Registering %s V%d/TCP", tags[prot], (int)vers); if (!TCP_REGISTER(prot, vers, netconfig_tcpv4)) LogFatal(COMPONENT_DISPATCH, "Cannot register %s V%d on TCP", tags[prot], (int)vers); if (!v6disabled && netconfig_tcpv6) { LogInfo(COMPONENT_DISPATCH, "Registering %s V%d/TCPv6", tags[prot], (int)vers); if (!TCP_REGISTER(prot, vers, netconfig_tcpv6)) LogFatal(COMPONENT_DISPATCH, "Cannot register %s V%d on TCPv6", tags[prot], (int)vers); } #endif /* _NO_TCP_REGISTER */ } } /** * @brief Init the svc descriptors for the nfs daemon * * Perform all the required initialization for the RPC subsystem and event * channels. */ void nfs_Init_svc(void) { svc_init_params svc_params; int ix; int code; LogDebug(COMPONENT_DISPATCH, "NFS INIT: Core options = %d", NFS_options); LogInfo(COMPONENT_DISPATCH, "NFS INIT: using TIRPC"); memset(&svc_params, 0, sizeof(svc_params)); #ifdef __FreeBSD__ v6disabled = true; #else v6disabled = false; #endif #ifdef RPC_VSOCK vsock = NFS_options & CORE_OPTION_NFS_VSOCK; #endif #ifdef _USE_NFS_RDMA rdma = NFS_options & CORE_OPTION_NFS_RDMA; #endif /* New TI-RPC package init function */ svc_params.disconnect_cb = NULL; svc_params.request_cb = nfs_rpc_decode_request; svc_params.flags = SVC_INIT_EPOLL; /* use EPOLL event mgmt */ svc_params.flags |= SVC_INIT_NOREG_XPRTS; /* don't call xprt_register */ svc_params.max_connections = nfs_param.core_param.rpc.max_connections; svc_params.max_events = 1024; /* length of epoll event queue */ svc_params.ioq_send_max = nfs_param.core_param.rpc.max_send_buffer_size; svc_params.channels = N_EVENT_CHAN; svc_params.idle_timeout = nfs_param.core_param.rpc.idle_timeout_s; svc_params.ioq_thrd_max = /* max ioq worker threads */ nfs_param.core_param.rpc.ioq_thrd_max; /* GSS ctx cache tuning, expiration */ svc_params.gss_ctx_hash_partitions = nfs_param.core_param.rpc.gss.ctx_hash_partitions; svc_params.gss_max_ctx = nfs_param.core_param.rpc.gss.max_ctx; svc_params.gss_max_gc = nfs_param.core_param.rpc.gss.max_gc; /* Only after TI-RPC allocators, log channel are setup */ if (!svc_init(&svc_params)) LogFatal(COMPONENT_INIT, "SVC initialization failed"); for (ix = 0; ix < EVCHAN_SIZE; ++ix) { rpc_evchan[ix].chan_id = 0; code = svc_rqst_new_evchan(&rpc_evchan[ix].chan_id, NULL /* u_data */, SVC_RQST_FLAG_NONE); if (code) LogFatal(COMPONENT_DISPATCH, "Cannot create TI-RPC event channel (%d, %d)", ix, code); /* XXX bail?? */ } /* Get the netconfig entries from /etc/netconfig */ netconfig_udpv4 = (struct netconfig *)getnetconfigent("udp"); if (netconfig_udpv4 == NULL) LogFatal(COMPONENT_DISPATCH, "Cannot get udp netconfig, cannot get an entry for udp in netconfig file. Check file /etc/netconfig..."); /* Get the netconfig entries from /etc/netconfig */ netconfig_tcpv4 = (struct netconfig *)getnetconfigent("tcp"); if (netconfig_tcpv4 == NULL) LogFatal(COMPONENT_DISPATCH, "Cannot get tcp netconfig, cannot get an entry for tcp in netconfig file. Check file /etc/netconfig..."); /* A short message to show that /etc/netconfig parsing was a success */ LogFullDebug(COMPONENT_DISPATCH, "netconfig found for UDPv4 and TCPv4"); LogInfo(COMPONENT_DISPATCH, "NFS INIT: Using IPv6"); /* Get the netconfig entries from /etc/netconfig */ netconfig_udpv6 = (struct netconfig *)getnetconfigent("udp6"); if (netconfig_udpv6 == NULL) LogInfo(COMPONENT_DISPATCH, "Cannot get udp6 netconfig, cannot get an entry for udp6 in netconfig file. Check file /etc/netconfig..."); /* Get the netconfig entries from /etc/netconfig */ netconfig_tcpv6 = (struct netconfig *)getnetconfigent("tcp6"); if (netconfig_tcpv6 == NULL) LogInfo(COMPONENT_DISPATCH, "Cannot get tcp6 netconfig, cannot get an entry for tcp in netconfig file. Check file /etc/netconfig..."); /* A short message to show that /etc/netconfig parsing was a success * for ipv6 */ if (netconfig_udpv6 && netconfig_tcpv6) LogFullDebug(COMPONENT_DISPATCH, "netconfig found for UDPv6 and TCPv6"); /* Allocate the UDP and TCP sockets for the RPC */ Allocate_sockets(); if ((NFS_options & CORE_OPTION_ALL_NFS_VERS) != 0) { /* Bind the tcp and udp sockets */ Bind_sockets(); /* Unregister from portmapper/rpcbind */ unregister_rpc(); /* Set up well-known xprt handles */ Create_SVCXPRTs(); } #ifdef _HAVE_GSSAPI /* Acquire RPCSEC_GSS basis if needed */ if (nfs_param.krb5_param.active_krb5) { if (!svcauth_gss_import_name (nfs_param.krb5_param.svc.principal)) { LogFatal(COMPONENT_DISPATCH, "Could not import principal name %s into GSSAPI", nfs_param.krb5_param.svc.principal); } else { LogInfo(COMPONENT_DISPATCH, "Successfully imported principal %s into GSSAPI", nfs_param.krb5_param.svc.principal); /* Trying to acquire a credentials * for checking name's validity */ if (!svcauth_gss_acquire_cred()) LogCrit(COMPONENT_DISPATCH, "Cannot acquire credentials for principal %s", nfs_param.krb5_param.svc.principal); else LogDebug(COMPONENT_DISPATCH, "Principal %s is suitable for acquiring credentials", nfs_param.krb5_param.svc.principal); } } #endif /* _HAVE_GSSAPI */ #ifndef _NO_PORTMAPPER /* Perform all the RPC registration, for UDP and TCP, * for NFS_V2, NFS_V3 and NFS_V4 */ #ifdef _USE_NFS3 Register_program(P_NFS, CORE_OPTION_NFSV3, NFS_V3); #endif /* _USE_NFS3 */ Register_program(P_NFS, CORE_OPTION_NFSV4, NFS_V4); Register_program(P_MNT, CORE_OPTION_NFSV3, MOUNT_V1); Register_program(P_MNT, CORE_OPTION_NFSV3, MOUNT_V3); #ifdef _USE_NLM if (nfs_param.core_param.enable_NLM) Register_program(P_NLM, CORE_OPTION_NFSV3, NLM4_VERS); #endif /* _USE_NLM */ if (nfs_param.core_param.enable_RQUOTA && (NFS_options & (CORE_OPTION_NFSV3 | CORE_OPTION_NFSV4))) { Register_program(P_RQUOTA, CORE_OPTION_ALL_VERS, RQUOTAVERS); Register_program(P_RQUOTA, CORE_OPTION_ALL_VERS, EXT_RQUOTAVERS); } #endif /* _NO_PORTMAPPER */ } /** * @brief Rendezvous callout. This routine will be called by TI-RPC * after newxprt has been accepted. * * Register newxprt on a TCP event channel. Balancing events/channels * could become involved. To start with, just cycle through them as * new connections are accepted. * * @param[in] newxprt Newly created transport * * @return status of parent. */ static enum xprt_stat nfs_rpc_tcp_user_data(SVCXPRT *newxprt) { return SVC_STAT(newxprt->xp_parent); } /** * @brief xprt destructor callout * * @param[in] xprt Transport to destroy */ static enum xprt_stat nfs_rpc_free_user_data(SVCXPRT *xprt) { if (xprt->xp_u2) { nfs_dupreq_put_drc(xprt->xp_u2, DRC_FLAG_RELEASE); xprt->xp_u2 = NULL; } return XPRT_DESTROYED; } /** * @brief Allocate a new request * * @param[in] xprt Transport to use * * @return New request data */ static inline request_data_t *alloc_nfs_request(SVCXPRT *xprt, XDR *xdrs) { request_data_t *reqdata = pool_alloc(request_pool); (void) atomic_inc_uint64_t(&health.enqueued_reqs); /* set the request as NFS already-read */ reqdata->rtype = NFS_REQUEST; /* set up req */ SVC_REF(xprt, SVC_REF_FLAG_NONE); reqdata->r_u.req.svc.rq_xprt = xprt; reqdata->r_u.req.svc.rq_xdrs = xdrs; reqdata->r_u.req.svc.rq_refs = 1; return reqdata; } int free_nfs_request(request_data_t *reqdata) { SVCXPRT *xprt = reqdata->r_u.req.svc.rq_xprt; uint32_t refs = atomic_dec_uint32_t(&reqdata->r_u.req.svc.rq_refs); LogDebug(COMPONENT_DISPATCH, "%s: %p fd %d xp_refs %" PRIu32 " rq_refs %" PRIu32, __func__, xprt, xprt->xp_fd, xprt->xp_refs, refs); if (refs) return refs; switch (reqdata->rtype) { case NFS_REQUEST: /* dispose RPC header */ if (reqdata->r_u.req.svc.rq_auth) SVCAUTH_RELEASE(&(reqdata->r_u.req.svc)); XDR_DESTROY(reqdata->r_u.req.svc.rq_xdrs); break; default: break; } SVC_RELEASE(xprt, SVC_RELEASE_FLAG_NONE); pool_free(request_pool, reqdata); (void) atomic_inc_uint64_t(&health.dequeued_reqs); return 0; } static enum xprt_stat nfs_rpc_decode_request(SVCXPRT *xprt, XDR *xdrs) { request_data_t *reqdata; enum xprt_stat stat; if (!xprt) { LogFatal(COMPONENT_DISPATCH, "missing xprt!"); return XPRT_DIED; } if (!xdrs) { LogFatal(COMPONENT_DISPATCH, "missing xdrs!"); return XPRT_DIED; } LogDebug(COMPONENT_DISPATCH, "%p fd %d context %p", xprt, xprt->xp_fd, xdrs); reqdata = alloc_nfs_request(xprt, xdrs); #if HAVE_BLKIN blkin_init_new_trace(&reqdata->r_u.req.svc.bl_trace, "nfs-ganesha", &xprt->blkin.endp); #endif #if defined(HAVE_BLKIN) BLKIN_TIMESTAMP( &reqdata->r_u.req.svc.bl_trace, &xprt->blkin.endp, "pre-recv"); #endif stat = SVC_DECODE(&reqdata->r_u.req.svc); #if defined(HAVE_BLKIN) BLKIN_TIMESTAMP( &reqdata->r_u.req.svc.bl_trace, &xprt->blkin.endp, "post-recv"); BLKIN_KEYVAL_INTEGER( &reqdata->r_u.req.svc.bl_trace, &reqdata->r_u.req.xprt->blkin.endp, "rq-xid", reqdata->r_u.req.svc.rq_xid); #endif if (unlikely(stat > XPRT_DESTROYED)) { LogInfo(COMPONENT_DISPATCH, "SVC_DECODE on %p fd %d returned unknown %u", xprt, xprt->xp_fd, stat); } else { sockaddr_t addr; char addrbuf[SOCK_NAME_MAX + 1]; if (isDebug(COMPONENT_DISPATCH)) { if (copy_xprt_addr(&addr, xprt) == 1) sprint_sockaddr(&addr, addrbuf, sizeof(addrbuf)); else sprintf(addrbuf, ""); } LogDebug(COMPONENT_DISPATCH, "SVC_DECODE on %p fd %d (%s) xid=%" PRIu32 " returned %s", xprt, xprt->xp_fd, addrbuf, reqdata->r_u.req.svc.rq_msg.rm_xid, xprt_stat_s[stat]); } /* refresh status before possible release */ stat = SVC_STAT(xprt); free_nfs_request(reqdata); return stat; } nfs-ganesha-2.6.0/src/MainNFSD/nfs_rpc_tcp_socket_manager_thread.c000066400000000000000000000035051324272410200250740ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_rpc_tcp_socket_manager_thread.c * @brief The file that contain the 'rpc_tcp_socket_manager_thread' routine * for the nfsd. * * nfs_rpc_dispatcher.c : The file that contain the * 'rpc_tcp_socket_manager_thread.c' routine for the nfsd (and all * the related stuff). * */ #include "config.h" #include #include #include #include #include /* for having FNDELAY */ #include #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "nfs23.h" #include "nfs4.h" #include "mount.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_file_handle.h" #if 0 /* XXX This routine was used only in prior revision of the rendezvous_request * which spawned a dedicated thread for each client connection. */ #endif nfs-ganesha-2.6.0/src/MainNFSD/nfs_worker_thread.c000066400000000000000000001477171324272410200217270ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * William Allen Simpson * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_worker_thread.c * @brief The file that contain the 'worker_thread' routine for the nfsd. */ #include "config.h" #ifdef FREEBSD #include #endif #include #include #include #include #include #include #include /* for having FNDELAY */ #include #include #include "hashtable.h" #include "abstract_atomic.h" #include "log.h" #include "fsal.h" #include "rquota.h" #include "nfs_init.h" #include "nfs_convert.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_creds.h" #include "nfs_proto_functions.h" #include "nfs_dupreq.h" #include "nfs_file_handle.h" #include "client_mgr.h" #include "export_mgr.h" #include "server_stats.h" #include "uid2grp.h" #ifdef USE_LTTNG #include "gsh_lttng/nfs_rpc.h" #endif #define NFS_pcp nfs_param.core_param #define NFS_options NFS_pcp.core_options #define NFS_program NFS_pcp.program const nfs_function_desc_t invalid_funcdesc = { .service_function = nfs_null, .free_function = nfs_null_free, .xdr_decode_func = (xdrproc_t) xdr_void, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "invalid_function", .dispatch_behaviour = NOTHING_SPECIAL }; #ifdef _USE_NFS3 const nfs_function_desc_t nfs3_func_desc[] = { { .service_function = nfs_null, .free_function = nfs_null_free, .xdr_decode_func = (xdrproc_t) xdr_void, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "nfs3_null", .dispatch_behaviour = NOTHING_SPECIAL}, { .service_function = nfs3_getattr, .free_function = nfs3_getattr_free, .xdr_decode_func = (xdrproc_t) xdr_GETATTR3args, .xdr_encode_func = (xdrproc_t) xdr_GETATTR3res, .funcname = "nfs3_getattr", .dispatch_behaviour = NEEDS_CRED | SUPPORTS_GSS}, { .service_function = nfs3_setattr, .free_function = nfs3_setattr_free, .xdr_decode_func = (xdrproc_t) xdr_SETATTR3args, .xdr_encode_func = (xdrproc_t) xdr_SETATTR3res, .funcname = "nfs3_setattr", .dispatch_behaviour = (MAKES_WRITE | NEEDS_CRED | CAN_BE_DUP | SUPPORTS_GSS) }, { .service_function = nfs3_lookup, .free_function = nfs3_lookup_free, .xdr_decode_func = (xdrproc_t) xdr_LOOKUP3args, .xdr_encode_func = (xdrproc_t) xdr_LOOKUP3res, .funcname = "nfs3_lookup", .dispatch_behaviour = NEEDS_CRED | SUPPORTS_GSS}, { .service_function = nfs3_access, .free_function = nfs3_access_free, .xdr_decode_func = (xdrproc_t) xdr_ACCESS3args, .xdr_encode_func = (xdrproc_t) xdr_ACCESS3res, .funcname = "nfs3_access", .dispatch_behaviour = NEEDS_CRED | SUPPORTS_GSS}, { .service_function = nfs3_readlink, .free_function = nfs3_readlink_free, .xdr_decode_func = (xdrproc_t) xdr_READLINK3args, .xdr_encode_func = (xdrproc_t) xdr_READLINK3res, .funcname = "nfs3_readlink", .dispatch_behaviour = NEEDS_CRED | SUPPORTS_GSS}, { .service_function = nfs3_read, .free_function = nfs3_read_free, .xdr_decode_func = (xdrproc_t) xdr_READ3args, .xdr_encode_func = (xdrproc_t) xdr_READ3res, .funcname = "nfs3_read", .dispatch_behaviour = NEEDS_CRED | SUPPORTS_GSS | MAKES_IO}, { .service_function = nfs3_write, .free_function = nfs3_write_free, .xdr_decode_func = (xdrproc_t) xdr_WRITE3args, .xdr_encode_func = (xdrproc_t) xdr_WRITE3res, .funcname = "nfs3_write", .dispatch_behaviour = (MAKES_WRITE | NEEDS_CRED | CAN_BE_DUP | SUPPORTS_GSS | MAKES_IO) }, { .service_function = nfs3_create, .free_function = nfs3_create_free, .xdr_decode_func = (xdrproc_t) xdr_CREATE3args, .xdr_encode_func = (xdrproc_t) xdr_CREATE3res, .funcname = "nfs3_create", .dispatch_behaviour = (MAKES_WRITE | NEEDS_CRED | CAN_BE_DUP | SUPPORTS_GSS) }, { .service_function = nfs3_mkdir, .free_function = nfs3_mkdir_free, .xdr_decode_func = (xdrproc_t) xdr_MKDIR3args, .xdr_encode_func = (xdrproc_t) xdr_MKDIR3res, .funcname = "nfs3_mkdir", .dispatch_behaviour = (MAKES_WRITE | NEEDS_CRED | CAN_BE_DUP | SUPPORTS_GSS) }, { .service_function = nfs3_symlink, .free_function = nfs3_symlink_free, .xdr_decode_func = (xdrproc_t) xdr_SYMLINK3args, .xdr_encode_func = (xdrproc_t) xdr_SYMLINK3res, .funcname = "nfs3_symlink", .dispatch_behaviour = (MAKES_WRITE | NEEDS_CRED | CAN_BE_DUP | SUPPORTS_GSS) }, { .service_function = nfs3_mknod, .free_function = nfs3_mknod_free, .xdr_decode_func = (xdrproc_t) xdr_MKNOD3args, .xdr_encode_func = (xdrproc_t) xdr_MKNOD3res, .funcname = "nfs3_mknod", .dispatch_behaviour = (MAKES_WRITE | NEEDS_CRED | CAN_BE_DUP | SUPPORTS_GSS) }, { .service_function = nfs3_remove, .free_function = nfs3_remove_free, .xdr_decode_func = (xdrproc_t) xdr_REMOVE3args, .xdr_encode_func = (xdrproc_t) xdr_REMOVE3res, .funcname = "nfs3_remove", .dispatch_behaviour = (MAKES_WRITE | NEEDS_CRED | CAN_BE_DUP | SUPPORTS_GSS) }, { .service_function = nfs3_rmdir, .free_function = nfs3_rmdir_free, .xdr_decode_func = (xdrproc_t) xdr_RMDIR3args, .xdr_encode_func = (xdrproc_t) xdr_RMDIR3res, .funcname = "nfs3_rmdir", .dispatch_behaviour = (MAKES_WRITE | NEEDS_CRED | CAN_BE_DUP | SUPPORTS_GSS) }, { .service_function = nfs3_rename, .free_function = nfs3_rename_free, .xdr_decode_func = (xdrproc_t) xdr_RENAME3args, .xdr_encode_func = (xdrproc_t) xdr_RENAME3res, .funcname = "nfs3_rename", .dispatch_behaviour = (MAKES_WRITE | NEEDS_CRED | CAN_BE_DUP | SUPPORTS_GSS) }, { .service_function = nfs3_link, .free_function = nfs3_link_free, .xdr_decode_func = (xdrproc_t) xdr_LINK3args, .xdr_encode_func = (xdrproc_t) xdr_LINK3res, .funcname = "nfs3_link", .dispatch_behaviour = (MAKES_WRITE | NEEDS_CRED | CAN_BE_DUP | SUPPORTS_GSS) }, { .service_function = nfs3_readdir, .free_function = nfs3_readdir_free, .xdr_decode_func = (xdrproc_t) xdr_READDIR3args, .xdr_encode_func = (xdrproc_t) xdr_READDIR3res, .funcname = "nfs3_readdir", .dispatch_behaviour = (NEEDS_CRED | SUPPORTS_GSS) }, { .service_function = nfs3_readdirplus, .free_function = nfs3_readdirplus_free, .xdr_decode_func = (xdrproc_t) xdr_READDIRPLUS3args, .xdr_encode_func = (xdrproc_t) xdr_READDIRPLUS3res, .funcname = "nfs3_readdirplus", .dispatch_behaviour = (NEEDS_CRED | SUPPORTS_GSS) }, { .service_function = nfs3_fsstat, .free_function = nfs3_fsstat_free, .xdr_decode_func = (xdrproc_t) xdr_FSSTAT3args, .xdr_encode_func = (xdrproc_t) xdr_FSSTAT3res, .funcname = "nfs3_fsstat", .dispatch_behaviour = (NEEDS_CRED | SUPPORTS_GSS) }, { .service_function = nfs3_fsinfo, .free_function = nfs3_fsinfo_free, .xdr_decode_func = (xdrproc_t) xdr_FSINFO3args, .xdr_encode_func = (xdrproc_t) xdr_FSINFO3res, .funcname = "nfs3_fsinfo", .dispatch_behaviour = (NEEDS_CRED) }, { .service_function = nfs3_pathconf, .free_function = nfs3_pathconf_free, .xdr_decode_func = (xdrproc_t) xdr_PATHCONF3args, .xdr_encode_func = (xdrproc_t) xdr_PATHCONF3res, .funcname = "nfs3_pathconf", .dispatch_behaviour = (NEEDS_CRED | SUPPORTS_GSS) }, { .service_function = nfs3_commit, .free_function = nfs3_commit_free, .xdr_decode_func = (xdrproc_t) xdr_COMMIT3args, .xdr_encode_func = (xdrproc_t) xdr_COMMIT3res, .funcname = "nfs3_commit", .dispatch_behaviour = (MAKES_WRITE | NEEDS_CRED | SUPPORTS_GSS) } }; #endif /* _USE_NFS3 */ /* Remeber that NFSv4 manages authentication though junction crossing, and * so does it for RO FS management (for each operation) */ const nfs_function_desc_t nfs4_func_desc[] = { { .service_function = nfs_null, .free_function = nfs_null_free, .xdr_decode_func = (xdrproc_t) xdr_void, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "nfs_null", .dispatch_behaviour = NOTHING_SPECIAL}, { .service_function = nfs4_Compound, .free_function = nfs4_Compound_Free, .xdr_decode_func = (xdrproc_t) xdr_COMPOUND4args, .xdr_encode_func = (xdrproc_t) xdr_COMPOUND4res, .funcname = "nfs4_Comp", .dispatch_behaviour = CAN_BE_DUP} }; const nfs_function_desc_t mnt1_func_desc[] = { { .service_function = mnt_Null, .free_function = mnt_Null_Free, .xdr_decode_func = (xdrproc_t) xdr_void, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "mnt_Null", .dispatch_behaviour = NOTHING_SPECIAL}, { .service_function = mnt_Mnt, .free_function = mnt1_Mnt_Free, .xdr_decode_func = (xdrproc_t) xdr_dirpath, .xdr_encode_func = (xdrproc_t) xdr_fhstatus2, .funcname = "mnt_Mnt", .dispatch_behaviour = NEEDS_CRED}, { .service_function = mnt_Dump, .free_function = mnt_Dump_Free, .xdr_decode_func = (xdrproc_t) xdr_void, .xdr_encode_func = (xdrproc_t) xdr_mountlist, .funcname = "mnt_Dump", .dispatch_behaviour = NOTHING_SPECIAL}, { .service_function = mnt_Umnt, .free_function = mnt_Umnt_Free, .xdr_decode_func = (xdrproc_t) xdr_dirpath, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "mnt_Umnt", .dispatch_behaviour = NOTHING_SPECIAL}, { .service_function = mnt_UmntAll, .free_function = mnt_UmntAll_Free, .xdr_decode_func = (xdrproc_t) xdr_void, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "mnt_UmntAll", .dispatch_behaviour = NOTHING_SPECIAL}, { .service_function = mnt_Export, .free_function = mnt_Export_Free, .xdr_decode_func = (xdrproc_t) xdr_void, .xdr_encode_func = (xdrproc_t) xdr_exports, .funcname = "mnt_Export", .dispatch_behaviour = NOTHING_SPECIAL} }; const nfs_function_desc_t mnt3_func_desc[] = { { .service_function = mnt_Null, .free_function = mnt_Null_Free, .xdr_decode_func = (xdrproc_t) xdr_void, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "mnt_Null", .dispatch_behaviour = NOTHING_SPECIAL}, { .service_function = mnt_Mnt, .free_function = mnt3_Mnt_Free, .xdr_decode_func = (xdrproc_t) xdr_dirpath, .xdr_encode_func = (xdrproc_t) xdr_mountres3, .funcname = "mnt_Mnt", .dispatch_behaviour = NOTHING_SPECIAL}, { .service_function = mnt_Dump, .free_function = mnt_Dump_Free, .xdr_decode_func = (xdrproc_t) xdr_void, .xdr_encode_func = (xdrproc_t) xdr_mountlist, .funcname = "mnt_Dump", .dispatch_behaviour = NOTHING_SPECIAL}, { .service_function = mnt_Umnt, .free_function = mnt_Umnt_Free, .xdr_decode_func = (xdrproc_t) xdr_dirpath, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "mnt_Umnt", .dispatch_behaviour = NOTHING_SPECIAL}, { .service_function = mnt_UmntAll, .free_function = mnt_UmntAll_Free, .xdr_decode_func = (xdrproc_t) xdr_void, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "mnt_UmntAll", .dispatch_behaviour = NOTHING_SPECIAL}, { .service_function = mnt_Export, .free_function = mnt_Export_Free, .xdr_decode_func = (xdrproc_t) xdr_void, .xdr_encode_func = (xdrproc_t) xdr_exports, .funcname = "mnt_Export", .dispatch_behaviour = NOTHING_SPECIAL} }; #define nlm4_Unsupported nlm_Null #define nlm4_Unsupported_Free nlm_Null_Free #ifdef _USE_NLM const nfs_function_desc_t nlm4_func_desc[] = { [NLMPROC4_NULL] = { .service_function = nlm_Null, .free_function = nlm_Null_Free, .xdr_decode_func = (xdrproc_t) xdr_void, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "nlm_Null", .dispatch_behaviour = NOTHING_SPECIAL}, [NLMPROC4_TEST] = { .service_function = nlm4_Test, .free_function = nlm4_Test_Free, .xdr_decode_func = (xdrproc_t) xdr_nlm4_testargs, .xdr_encode_func = (xdrproc_t) xdr_nlm4_testres, .funcname = "nlm4_Test", .dispatch_behaviour = NEEDS_CRED}, [NLMPROC4_LOCK] = { .service_function = nlm4_Lock, .free_function = nlm4_Lock_Free, .xdr_decode_func = (xdrproc_t) xdr_nlm4_lockargs, .xdr_encode_func = (xdrproc_t) xdr_nlm4_res, .funcname = "nlm4_Lock", .dispatch_behaviour = NEEDS_CRED}, [NLMPROC4_CANCEL] = { .service_function = nlm4_Cancel, .free_function = nlm4_Cancel_Free, .xdr_decode_func = (xdrproc_t) xdr_nlm4_cancargs, .xdr_encode_func = (xdrproc_t) xdr_nlm4_res, .funcname = "nlm4_Cancel", .dispatch_behaviour = NEEDS_CRED}, [NLMPROC4_UNLOCK] = { .service_function = nlm4_Unlock, .free_function = nlm4_Unlock_Free, .xdr_decode_func = (xdrproc_t) xdr_nlm4_unlockargs, .xdr_encode_func = (xdrproc_t) xdr_nlm4_res, .funcname = "nlm4_Unlock", .dispatch_behaviour = NEEDS_CRED}, [NLMPROC4_GRANTED] = { .service_function = nlm4_Unsupported, .free_function = nlm4_Unsupported_Free, .xdr_decode_func = (xdrproc_t) xdr_void, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "nlm4_Granted", .dispatch_behaviour = NOTHING_SPECIAL}, [NLMPROC4_TEST_MSG] = { .service_function = nlm4_Test_Message, .free_function = nlm4_Test_Free, .xdr_decode_func = (xdrproc_t) xdr_nlm4_testargs, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "nlm4_Test_msg", .dispatch_behaviour = NEEDS_CRED}, [NLMPROC4_LOCK_MSG] = { .service_function = nlm4_Lock_Message, .free_function = nlm4_Lock_Free, .xdr_decode_func = (xdrproc_t) xdr_nlm4_lockargs, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "nlm4_Lock_msg", .dispatch_behaviour = NEEDS_CRED}, [NLMPROC4_CANCEL_MSG] = { .service_function = nlm4_Cancel_Message, .free_function = nlm4_Cancel_Free, .xdr_decode_func = (xdrproc_t) xdr_nlm4_cancargs, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "nlm4_Cancel_msg", .dispatch_behaviour = NEEDS_CRED}, [NLMPROC4_UNLOCK_MSG] = { .service_function = nlm4_Unlock_Message, .free_function = nlm4_Unlock_Free, .xdr_decode_func = (xdrproc_t) xdr_nlm4_unlockargs, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "nlm4_Unlock_msg", .dispatch_behaviour = NEEDS_CRED}, [NLMPROC4_GRANTED_MSG] = { .service_function = nlm4_Unsupported, .free_function = nlm4_Unsupported_Free, .xdr_decode_func = (xdrproc_t) xdr_void, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "nlm4_Granted_msg", .dispatch_behaviour = NOTHING_SPECIAL}, [NLMPROC4_TEST_RES] = { .service_function = nlm4_Unsupported, .free_function = nlm4_Unsupported_Free, .xdr_decode_func = (xdrproc_t) xdr_void, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "nlm4_Test_res", .dispatch_behaviour = NOTHING_SPECIAL}, [NLMPROC4_LOCK_RES] = { .service_function = nlm4_Unsupported, .free_function = nlm4_Unsupported_Free, .xdr_decode_func = (xdrproc_t) xdr_void, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "nlm4_Lock_res", .dispatch_behaviour = NOTHING_SPECIAL}, [NLMPROC4_CANCEL_RES] = { .service_function = nlm4_Unsupported, .free_function = nlm4_Unsupported_Free, .xdr_decode_func = (xdrproc_t) xdr_void, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "nlm4_Cancel_res", .dispatch_behaviour = NOTHING_SPECIAL}, [NLMPROC4_UNLOCK_RES] = { .service_function = nlm4_Unsupported, .free_function = nlm4_Unsupported_Free, .xdr_decode_func = (xdrproc_t) xdr_void, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "nlm4_Unlock_res", .dispatch_behaviour = NOTHING_SPECIAL}, [NLMPROC4_GRANTED_RES] = { .service_function = nlm4_Granted_Res, .free_function = nlm4_Granted_Res_Free, .xdr_decode_func = (xdrproc_t) xdr_nlm4_res, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "nlm4_Granted_res", .dispatch_behaviour = NOTHING_SPECIAL}, [NLMPROC4_SM_NOTIFY] = { .service_function = nlm4_Sm_Notify, .free_function = nlm4_Sm_Notify_Free, .xdr_decode_func = (xdrproc_t) xdr_nlm4_sm_notifyargs, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "nlm4_sm_notify", .dispatch_behaviour = NOTHING_SPECIAL}, [17] = { .service_function = nlm4_Unsupported, .free_function = nlm4_Unsupported_Free, .xdr_decode_func = (xdrproc_t) xdr_void, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "nlm4_Granted_res", .dispatch_behaviour = NOTHING_SPECIAL}, [18] = { .service_function = nlm4_Unsupported, .free_function = nlm4_Unsupported_Free, .xdr_decode_func = (xdrproc_t) xdr_void, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "nlm4_Granted_res", .dispatch_behaviour = NOTHING_SPECIAL}, [19] = { .service_function = nlm4_Unsupported, .free_function = nlm4_Unsupported_Free, .xdr_decode_func = (xdrproc_t) xdr_void, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "nlm4_Granted_res", .dispatch_behaviour = NOTHING_SPECIAL}, [NLMPROC4_SHARE] = { .service_function = nlm4_Share, .free_function = nlm4_Share_Free, .xdr_decode_func = (xdrproc_t) xdr_nlm4_shareargs, .xdr_encode_func = (xdrproc_t) xdr_nlm4_shareres, .funcname = "nlm4_Share", .dispatch_behaviour = NEEDS_CRED}, [NLMPROC4_UNSHARE] = { .service_function = nlm4_Unshare, .free_function = nlm4_Unshare_Free, .xdr_decode_func = (xdrproc_t) xdr_nlm4_shareargs, .xdr_encode_func = (xdrproc_t) xdr_nlm4_shareres, .funcname = "nlm4_Unshare", .dispatch_behaviour = NEEDS_CRED}, [NLMPROC4_NM_LOCK] = { /* NLM_NM_LOCK uses the same handling as NLM_LOCK * except for monitoring, nlm4_Lock will make * that determination. */ .service_function = nlm4_Lock, .free_function = nlm4_Lock_Free, .xdr_decode_func = (xdrproc_t) xdr_nlm4_lockargs, .xdr_encode_func = (xdrproc_t) xdr_nlm4_res, .funcname = "nlm4_Nm_lock", .dispatch_behaviour = NEEDS_CRED}, [NLMPROC4_FREE_ALL] = { .service_function = nlm4_Free_All, .free_function = nlm4_Free_All_Free, .xdr_decode_func = (xdrproc_t) xdr_nlm4_free_allargs, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "nlm4_Free_all", .dispatch_behaviour = NOTHING_SPECIAL}, }; #endif /* _USE_NLM */ const nfs_function_desc_t rquota1_func_desc[] = { [0] = { .service_function = rquota_Null, .free_function = rquota_Null_Free, .xdr_decode_func = (xdrproc_t) xdr_void, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "rquota_Null", .dispatch_behaviour = NOTHING_SPECIAL}, [RQUOTAPROC_GETQUOTA] = { .service_function = rquota_getquota, .free_function = rquota_getquota_Free, .xdr_decode_func = (xdrproc_t) xdr_getquota_args, .xdr_encode_func = (xdrproc_t) xdr_getquota_rslt, .funcname = "rquota_Getquota", .dispatch_behaviour = NEEDS_CRED}, [RQUOTAPROC_GETACTIVEQUOTA] = { .service_function = rquota_getactivequota, .free_function = rquota_getactivequota_Free, .xdr_decode_func = (xdrproc_t) xdr_getquota_args, .xdr_encode_func = (xdrproc_t) xdr_getquota_rslt, .funcname = "rquota_Getactivequota", .dispatch_behaviour = NEEDS_CRED}, [RQUOTAPROC_SETQUOTA] = { .service_function = rquota_setquota, .free_function = rquota_setquota_Free, .xdr_decode_func = (xdrproc_t) xdr_setquota_args, .xdr_encode_func = (xdrproc_t) xdr_setquota_rslt, .funcname = "rquota_Setactivequota", .dispatch_behaviour = NEEDS_CRED}, [RQUOTAPROC_SETACTIVEQUOTA] = { .service_function = rquota_setactivequota, .free_function = rquota_setactivequota_Free, .xdr_decode_func = (xdrproc_t) xdr_setquota_args, .xdr_encode_func = (xdrproc_t) xdr_setquota_rslt, .funcname = "rquota_Getactivequota", .dispatch_behaviour = NEEDS_CRED} }; const nfs_function_desc_t rquota2_func_desc[] = { [0] = { .service_function = rquota_Null, .free_function = rquota_Null_Free, .xdr_decode_func = (xdrproc_t) xdr_void, .xdr_encode_func = (xdrproc_t) xdr_void, .funcname = "rquota_Null", .dispatch_behaviour = NOTHING_SPECIAL}, [RQUOTAPROC_GETQUOTA] = { .service_function = rquota_getquota, .free_function = rquota_getquota_Free, .xdr_decode_func = (xdrproc_t) xdr_ext_getquota_args, .xdr_encode_func = (xdrproc_t) xdr_getquota_rslt, .funcname = "rquota_Ext_Getquota", .dispatch_behaviour = NEEDS_CRED}, [RQUOTAPROC_GETACTIVEQUOTA] = { .service_function = rquota_getactivequota, .free_function = rquota_getactivequota_Free, .xdr_decode_func = (xdrproc_t) xdr_ext_getquota_args, .xdr_encode_func = (xdrproc_t) xdr_getquota_rslt, .funcname = "rquota_Ext_Getactivequota", .dispatch_behaviour = NEEDS_CRED}, [RQUOTAPROC_SETQUOTA] = { .service_function = rquota_setquota, .free_function = rquota_setquota_Free, .xdr_decode_func = (xdrproc_t) xdr_ext_setquota_args, .xdr_encode_func = (xdrproc_t) xdr_setquota_rslt, .funcname = "rquota_Ext_Setactivequota", .dispatch_behaviour = NEEDS_CRED}, [RQUOTAPROC_SETACTIVEQUOTA] = { .service_function = rquota_setactivequota, .free_function = rquota_setactivequota_Free, .xdr_decode_func = (xdrproc_t) xdr_ext_setquota_args, .xdr_encode_func = (xdrproc_t) xdr_setquota_rslt, .funcname = "rquota_Ext_Getactivequota", .dispatch_behaviour = NEEDS_CRED} }; /** * @brief Main RPC dispatcher routine * * @param[in,out] reqdata NFS request * */ static enum xprt_stat nfs_rpc_process_request(request_data_t *reqdata) { const char *client_ip = ""; const char *progname = "unknown"; const nfs_function_desc_t *reqdesc = reqdata->r_u.req.funcdesc; nfs_arg_t *arg_nfs = &reqdata->r_u.req.arg_nfs; SVCXPRT *xprt = reqdata->r_u.req.svc.rq_xprt; XDR *xdrs = reqdata->r_u.req.svc.rq_xdrs; nfs_res_t *res_nfs; struct export_perms export_perms; struct user_cred user_credentials; struct req_op_context req_ctx; dupreq_status_t dpq_status; struct timespec timer_start; enum auth_stat auth_rc; enum xprt_stat xprt_rc; int port; int rc = NFS_REQ_OK; #ifdef _USE_NFS3 int exportid = -1; #endif /* _USE_NFS3 */ bool no_dispatch = false; #ifdef USE_LTTNG tracepoint(nfs_rpc, start, reqdata); #endif #if defined(HAVE_BLKIN) BLKIN_TIMESTAMP( &reqdata->r_u.req.svc.bl_trace, &reqdata->r_u.req.svc.rq_xprt->blkin.endp, "nfs_rpc_process_request-start"); #endif LogFullDebug(COMPONENT_DISPATCH, "About to authenticate Prog=%" PRIu32 ", vers=%" PRIu32 ", proc=%" PRIu32 ", xid=%" PRIu32 ", SVCXPRT=%p, fd=%d", reqdata->r_u.req.svc.rq_msg.cb_prog, reqdata->r_u.req.svc.rq_msg.cb_vers, reqdata->r_u.req.svc.rq_msg.cb_proc, reqdata->r_u.req.svc.rq_msg.rm_xid, xprt, xprt->xp_fd); /* If authentication is AUTH_NONE or AUTH_UNIX, then the value of * no_dispatch remains false and the request proceeds normally. * * If authentication is RPCSEC_GSS, no_dispatch may have value true, * this means that gc->gc_proc != RPCSEC_GSS_DATA and that the message * is in fact an internal negotiation message from RPCSEC_GSS using * GSSAPI. It should not be processed by the worker and SVC_STAT * should be returned to the dispatcher. */ auth_rc = svc_auth_authenticate(&reqdata->r_u.req.svc, &no_dispatch); if (auth_rc != AUTH_OK) { LogInfo(COMPONENT_DISPATCH, "Could not authenticate request... rejecting with AUTH_STAT=%s", auth_stat2str(auth_rc)); return svcerr_auth(&reqdata->r_u.req.svc, auth_rc); #ifdef _HAVE_GSSAPI } else if (reqdata->r_u.req.svc.rq_msg.RPCM_ack.ar_verf.oa_flavor == RPCSEC_GSS) { struct rpc_gss_cred *gc = (struct rpc_gss_cred *) reqdata->r_u.req.svc.rq_msg.rq_cred_body; LogFullDebug(COMPONENT_DISPATCH, "RPCSEC_GSS no_dispatch=%d gc->gc_proc=(%" PRIu32 ") %s", no_dispatch, gc->gc_proc, str_gc_proc(gc->gc_proc)); if (no_dispatch) return SVC_STAT(xprt); #endif } /* * Extract RPC argument. */ LogFullDebug(COMPONENT_DISPATCH, "Before SVCAUTH_CHECKSUM on SVCXPRT %p fd %d", xprt, xprt->xp_fd); memset(arg_nfs, 0, sizeof(nfs_arg_t)); reqdata->r_u.req.svc.rq_msg.rm_xdr.where = (caddr_t) arg_nfs; reqdata->r_u.req.svc.rq_msg.rm_xdr.proc = reqdesc->xdr_decode_func; xdrs->x_public = &reqdata->r_u.req.lookahead; if (!SVCAUTH_CHECKSUM(&reqdata->r_u.req.svc)) { LogInfo(COMPONENT_DISPATCH, "SVCAUTH_CHECKSUM failed for Program %" PRIu32 ", Version %" PRIu32 ", Function %" PRIu32 ", xid=%" PRIu32 ", SVCXPRT=%p, fd=%d", reqdata->r_u.req.svc.rq_msg.cb_prog, reqdata->r_u.req.svc.rq_msg.cb_vers, reqdata->r_u.req.svc.rq_msg.cb_proc, reqdata->r_u.req.svc.rq_msg.rm_xid, xprt, xprt->xp_fd); if (!xdr_free(reqdesc->xdr_decode_func, (caddr_t) arg_nfs)) { LogCrit(COMPONENT_DISPATCH, "%s FAILURE: Bad xdr_free for %s", __func__, reqdesc->funcname); } return svcerr_decode(&reqdata->r_u.req.svc); } /* set up the request context */ memset(&export_perms, 0, sizeof(export_perms)); memset(&req_ctx, 0, sizeof(req_ctx)); op_ctx = &req_ctx; op_ctx->creds = &user_credentials; op_ctx->caller_addr = (sockaddr_t *)svc_getrpccaller(xprt); op_ctx->nfs_vers = reqdata->r_u.req.svc.rq_msg.cb_vers; op_ctx->req_type = reqdata->rtype; op_ctx->export_perms = &export_perms; /* Set up initial export permissions that don't allow anything. */ export_check_access(); /* start the processing clock * we measure all time stats as intervals (elapsed nsecs) from * server boot time. This gets high precision with simple 64 bit math. */ now(&timer_start); op_ctx->start_time = timespec_diff(&ServerBootTime, &timer_start); op_ctx->queue_wait = op_ctx->start_time - timespec_diff(&ServerBootTime, &reqdata->time_queued); /* Initialized user_credentials */ init_credentials(); /* XXX must hold lock when calling any TI-RPC channel function, * including svc_sendreply2 and the svcerr_* calls */ /* XXX also, need to check UDP correctness, this may need some more * TI-RPC work (for UDP, if we -really needed it-, we needed to * capture hostaddr at SVC_RECV). For TCP, if we intend to use * this, we should sprint a buffer once, in when we're setting up * xprt private data. */ port = get_port(op_ctx->caller_addr); op_ctx->client = get_gsh_client(op_ctx->caller_addr, false); if (op_ctx->client == NULL) { LogDebug(COMPONENT_DISPATCH, "Cannot get client block for Program %" PRIu32 ", Version %" PRIu32 ", Function %" PRIu32, reqdata->r_u.req.svc.rq_msg.cb_prog, reqdata->r_u.req.svc.rq_msg.cb_vers, reqdata->r_u.req.svc.rq_msg.cb_proc); } else { /* Set the Client IP for this thread */ SetClientIP(op_ctx->client->hostaddr_str); client_ip = op_ctx->client->hostaddr_str; LogDebug(COMPONENT_DISPATCH, "Request from %s for Program %" PRIu32 ", Version %" PRIu32 ", Function %" PRIu32 " has xid=%" PRIu32, client_ip, reqdata->r_u.req.svc.rq_msg.cb_prog, reqdata->r_u.req.svc.rq_msg.cb_vers, reqdata->r_u.req.svc.rq_msg.cb_proc, reqdata->r_u.req.svc.rq_msg.rm_xid); } #if defined(HAVE_BLKIN) BLKIN_TIMESTAMP( &reqdata->r_u.req.svc.bl_trace, &reqdata->r_u.req.svc.rq_xprt->blkin.endp, "nfs_rpc_process_request-have-clientid"); #endif /* If req is uncacheable, or if req is v41+, nfs_dupreq_start will do * nothing but allocate a result object and mark the request (ie, the * path is short, lockless, and does no hash/search). */ dpq_status = nfs_dupreq_start(&reqdata->r_u.req, &reqdata->r_u.req.svc); res_nfs = reqdata->r_u.req.res_nfs; if (dpq_status == DUPREQ_SUCCESS) { /* A new request, continue processing it. */ LogFullDebug(COMPONENT_DISPATCH, "Current request is not duplicate or not cacheable."); } else { switch (dpq_status) { case DUPREQ_EXISTS: /* Found the request in the dupreq cache. * Send cached reply. */ LogFullDebug(COMPONENT_DISPATCH, "DUP: DupReq Cache Hit: using previous reply, rpcxid=%" PRIu32, reqdata->r_u.req.svc.rq_msg.rm_xid); LogFullDebug(COMPONENT_DISPATCH, "Before svc_sendreply on socket %d (dup req)", xprt->xp_fd); reqdata->r_u.req.svc.rq_msg.RPCM_ack.ar_results.where = (caddr_t) res_nfs; reqdata->r_u.req.svc.rq_msg.RPCM_ack.ar_results.proc = reqdesc->xdr_encode_func; xprt_rc = svc_sendreply(&reqdata->r_u.req.svc); if (xprt_rc >= XPRT_DIED) { LogDebug(COMPONENT_DISPATCH, "NFS DISPATCHER: FAILURE: Error while calling svc_sendreply on a duplicate request. rpcxid=%" PRIu32 " socket=%d function:%s client:%s program:%" PRIu32 " nfs version:%" PRIu32 " proc:%" PRIu32 " errno: %d", reqdata->r_u.req.svc.rq_msg.rm_xid, xprt->xp_fd, reqdesc->funcname, client_ip, reqdata->r_u.req.svc.rq_msg.cb_prog, reqdata->r_u.req.svc.rq_msg.cb_vers, reqdata->r_u.req.svc.rq_msg.cb_proc, errno); svcerr_systemerr(&reqdata->r_u.req.svc); } break; /* Another thread owns the request */ case DUPREQ_BEING_PROCESSED: LogFullDebug(COMPONENT_DISPATCH, "DUP: Request xid=%" PRIu32 " is already being processed; the active thread will reply", reqdata->r_u.req.svc.rq_msg.rm_xid); /* Free the arguments */ /* Ignore the request, send no error */ break; /* something is very wrong with * the duplicate request cache */ case DUPREQ_ERROR: LogCrit(COMPONENT_DISPATCH, "DUP: Did not find the request in the duplicate request cache and couldn't add the request."); svcerr_systemerr(&reqdata->r_u.req.svc); break; /* oom */ case DUPREQ_INSERT_MALLOC_ERROR: LogCrit(COMPONENT_DISPATCH, "DUP: Cannot process request, not enough memory available!"); svcerr_systemerr(&reqdata->r_u.req.svc); break; default: LogCrit(COMPONENT_DISPATCH, "DUP: Unknown duplicate request cache status. This should never be reached!"); svcerr_systemerr(&reqdata->r_u.req.svc); break; } server_stats_nfs_done(reqdata, rc, true); goto freeargs; } /* Don't waste time for null or invalid ops * null op code in all valid protos == 0 * and invalid protos all point to invalid_funcdesc * NFS v2 is set to invalid_funcdesc in nfs_rpc_get_funcdesc() */ if (reqdesc == &invalid_funcdesc || reqdata->r_u.req.svc.rq_msg.cb_proc == NFSPROC_NULL) goto null_op; /* Get the export entry */ if (reqdata->r_u.req.svc.rq_msg.cb_prog == NFS_program[P_NFS]) { /* The NFSv3 functions' arguments always begin with the file * handle (but not the NULL function). This hook is used to * get the fhandle with the arguments and so determine the * export entry to be used. In NFSv4, junction traversal * is managed by the protocol. */ progname = "NFS"; #ifdef _USE_NFS3 if (reqdata->r_u.req.svc.rq_msg.cb_vers == NFS_V3) { exportid = nfs3_FhandleToExportId((nfs_fh3 *) arg_nfs); if (exportid < 0) { LogInfo(COMPONENT_DISPATCH, "NFS3 Request from client %s has badly formed handle", client_ip); /* Bad handle, report to client */ res_nfs->res_getattr3.status = NFS3ERR_BADHANDLE; rc = NFS_REQ_OK; goto req_error; } op_ctx->ctx_export = get_gsh_export(exportid); if (op_ctx->ctx_export == NULL) { LogInfoAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "NFS3 Request from client %s has invalid export %d", client_ip, exportid); /* Bad export, report to client */ res_nfs->res_getattr3.status = NFS3ERR_STALE; rc = NFS_REQ_OK; goto req_error; } op_ctx->fsal_export = op_ctx->ctx_export->fsal_export; LogMidDebugAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "Found export entry for path=%s as exportid=%d", op_ctx_export_path(op_ctx->ctx_export), op_ctx->ctx_export->export_id); } #endif /* _USE_NFS3 */ /* NFS V4 gets its own export id from the ops * in the compound */ #ifdef _USE_NLM } else if (reqdata->r_u.req.svc.rq_msg.cb_prog == NFS_program[P_NLM]) { netobj *pfh3 = NULL; progname = "NLM"; switch (reqdata->r_u.req.svc.rq_msg.cb_proc) { case NLMPROC4_NULL: /* caught above and short circuited */ case NLMPROC4_TEST_RES: case NLMPROC4_LOCK_RES: case NLMPROC4_CANCEL_RES: case NLMPROC4_UNLOCK_RES: case NLMPROC4_GRANTED_RES: case NLMPROC4_SM_NOTIFY: case NLMPROC4_FREE_ALL: break; case NLMPROC4_TEST: case NLMPROC4_TEST_MSG: case NLMPROC4_GRANTED: case NLMPROC4_GRANTED_MSG: pfh3 = &arg_nfs->arg_nlm4_test.alock.fh; break; case NLMPROC4_LOCK: case NLMPROC4_LOCK_MSG: case NLMPROC4_NM_LOCK: pfh3 = &arg_nfs->arg_nlm4_lock.alock.fh; break; case NLMPROC4_CANCEL: case NLMPROC4_CANCEL_MSG: pfh3 = &arg_nfs->arg_nlm4_cancel.alock.fh; break; case NLMPROC4_UNLOCK: case NLMPROC4_UNLOCK_MSG: pfh3 = &arg_nfs->arg_nlm4_unlock.alock.fh; break; case NLMPROC4_SHARE: case NLMPROC4_UNSHARE: pfh3 = &arg_nfs->arg_nlm4_share.share.fh; break; } if (pfh3 != NULL) { exportid = nlm4_FhandleToExportId(pfh3); if (exportid < 0) { LogInfo(COMPONENT_DISPATCH, "NLM4 Request from client %s has badly formed handle", client_ip); op_ctx->ctx_export = NULL; op_ctx->fsal_export = NULL; /* We need to send a NLM4_STALE_FH response * (NLM doesn't have an error code for * BADHANDLE), but we don't know how to do that * here, we will send a NULL pexport to NLM * routine to let it know what to do since it * can respond to ASYNC calls. */ } else { op_ctx->ctx_export = get_gsh_export(exportid); if (op_ctx->ctx_export == NULL) { LogInfoAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "NLM4 Request from client %s has invalid export %d", client_ip, exportid); /* We need to send a NLM4_STALE_FH * response (NLM doesn't have an error * code for BADHANDLE), but we don't * know how to do that here, we will * send a NULL pexport to NLM routine * to let it know what to do since it * can respond to ASYNC calls. */ op_ctx->fsal_export = NULL; } else { op_ctx->fsal_export = op_ctx->ctx_export->fsal_export; LogMidDebugAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "Found export entry for dirname=%s as exportid=%d", op_ctx_export_path( op_ctx->ctx_export), op_ctx->ctx_export->export_id); } } } #endif /* _USE_NLM */ } else if (reqdata->r_u.req.svc.rq_msg.cb_prog == NFS_program[P_MNT]) { progname = "MNT"; } /* Only do access check if we have an export. */ if (op_ctx->ctx_export != NULL) { /* We ONLY get here for NFS v3 or NLM requests with a handle */ xprt_type_t xprt_type = svc_get_xprt_type(xprt); LogMidDebugAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "%s about to call nfs_export_check_access for client %s", __func__, client_ip); export_check_access(); if ((export_perms.options & EXPORT_OPTION_ACCESS_MASK) == 0) { LogInfoAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "Client %s is not allowed to access Export_Id %d %s, vers=%" PRIu32 ", proc=%" PRIu32, client_ip, op_ctx->ctx_export->export_id, op_ctx_export_path(op_ctx->ctx_export), reqdata->r_u.req.svc.rq_msg.cb_vers, reqdata->r_u.req.svc.rq_msg.cb_proc); auth_rc = AUTH_TOOWEAK; goto auth_failure; } if ((EXPORT_OPTION_NFSV3 & export_perms.options) == 0) { LogInfoAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "%s Version %" PRIu32 " not allowed on Export_Id %d %s for client %s", progname, reqdata->r_u.req.svc.rq_msg.cb_vers, op_ctx->ctx_export->export_id, op_ctx_export_path(op_ctx->ctx_export), client_ip); auth_rc = AUTH_FAILED; goto auth_failure; } /* Check transport type */ if (((xprt_type == XPRT_UDP) && ((export_perms.options & EXPORT_OPTION_UDP) == 0)) || ((xprt_type == XPRT_TCP) && ((export_perms.options & EXPORT_OPTION_TCP) == 0))) { LogInfoAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "%s Version %" PRIu32 " over %s not allowed on Export_Id %d %s for client %s", progname, reqdata->r_u.req.svc.rq_msg.cb_vers, xprt_type_to_str(xprt_type), op_ctx->ctx_export->export_id, op_ctx_export_path(op_ctx->ctx_export), client_ip); auth_rc = AUTH_FAILED; goto auth_failure; } /* Test if export allows the authentication provided */ if ((reqdesc->dispatch_behaviour & SUPPORTS_GSS) && !export_check_security(&reqdata->r_u.req.svc)) { LogInfoAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "%s Version %" PRIu32 " auth not allowed on Export_Id %d %s for client %s", progname, reqdata->r_u.req.svc.rq_msg.cb_vers, op_ctx->ctx_export->export_id, op_ctx_export_path(op_ctx->ctx_export), client_ip); auth_rc = AUTH_TOOWEAK; goto auth_failure; } /* Check if client is using a privileged port, * but only for NFS protocol */ if ((reqdata->r_u.req.svc.rq_msg.cb_prog == NFS_program[P_NFS]) && (export_perms.options & EXPORT_OPTION_PRIVILEGED_PORT) && (port >= IPPORT_RESERVED)) { LogInfoAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "Non-reserved Port %d is not allowed on Export_Id %d %s for client %s", port, op_ctx->ctx_export->export_id, op_ctx_export_path(op_ctx->ctx_export), client_ip); auth_rc = AUTH_TOOWEAK; goto auth_failure; } } /* * It is now time for checking if export list allows the machine * to perform the request */ if (op_ctx->ctx_export != NULL && (reqdesc->dispatch_behaviour & MAKES_IO) && !(export_perms.options & EXPORT_OPTION_RW_ACCESS)) { /* Request of type MDONLY_RO were rejected at the * nfs_rpc_dispatcher level. * This is done by replying EDQUOT * (this error is known for not disturbing * the client's requests cache) */ if (reqdata->r_u.req.svc.rq_msg.cb_prog == NFS_program[P_NFS]) switch (reqdata->r_u.req.svc.rq_msg.cb_vers) { #ifdef _USE_NFS3 case NFS_V3: LogDebugAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "Returning NFS3ERR_DQUOT because request is on an MD Only export"); res_nfs->res_getattr3.status = NFS3ERR_DQUOT; rc = NFS_REQ_OK; break; #endif /* _USE_NFS3 */ default: LogDebugAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "Dropping IO request on an MD Only export"); rc = NFS_REQ_DROP; break; } else { LogDebugAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "Dropping IO request on an MD Only export"); rc = NFS_REQ_DROP; } } else if (op_ctx->ctx_export != NULL && (reqdesc->dispatch_behaviour & MAKES_WRITE) && (export_perms.options & (EXPORT_OPTION_WRITE_ACCESS | EXPORT_OPTION_MD_WRITE_ACCESS)) == 0) { if (reqdata->r_u.req.svc.rq_msg.cb_prog == NFS_program[P_NFS]) switch (reqdata->r_u.req.svc.rq_msg.cb_vers) { #ifdef _USE_NFS3 case NFS_V3: LogDebugAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "Returning NFS3ERR_ROFS because request is on a Read Only export"); res_nfs->res_getattr3.status = NFS3ERR_ROFS; rc = NFS_REQ_OK; break; #endif /* _USE_NFS3 */ default: LogDebugAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "Dropping request on a Read Only export"); rc = NFS_REQ_DROP; break; } else { LogDebugAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "Dropping request on a Read Only export"); rc = NFS_REQ_DROP; } } else if (op_ctx->ctx_export != NULL && (export_perms.options & (EXPORT_OPTION_READ_ACCESS | EXPORT_OPTION_MD_READ_ACCESS)) == 0) { LogInfoAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "Client %s is not allowed to access Export_Id %d %s, vers=%" PRIu32 ", proc=%" PRIu32, client_ip, op_ctx->ctx_export->export_id, op_ctx_export_path(op_ctx->ctx_export), reqdata->r_u.req.svc.rq_msg.cb_vers, reqdata->r_u.req.svc.rq_msg.cb_proc); auth_rc = AUTH_TOOWEAK; goto auth_failure; } else { /* Get user credentials */ if (reqdesc->dispatch_behaviour & NEEDS_CRED) { /* If we don't have an export, don't squash */ if (op_ctx->fsal_export == NULL) { export_perms.options &= ~EXPORT_OPTION_SQUASH_TYPES; } if (nfs_req_creds(&reqdata->r_u.req.svc) != NFS4_OK) { LogInfoAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "could not get uid and gid, rejecting client %s", client_ip); auth_rc = AUTH_TOOWEAK; goto auth_failure; } } /* processing * At this point, op_ctx->ctx_export has one of the following * conditions: * non-NULL - valid handle for NFS v3 or NLM functions * that take handles * NULL - For NULL RPC calls * NULL - for RQUOTAD calls * NULL - for NFS v4 COMPOUND call * NULL - for MOUNT calls * NULL - for NLM calls where handle is bad, NLM must handle * response in the case of async "MSG" calls, so we * just defer to NLM routines to respond with * NLM4_STALE_FH (NLM doesn't have a BADHANDLE code) */ #ifdef _ERROR_INJECTION if (worker_delay_time != 0) sleep(worker_delay_time); else if (next_worker_delay_time != 0) { sleep(next_worker_delay_time); next_worker_delay_time = 0; } #endif null_op: #ifdef USE_LTTNG tracepoint(nfs_rpc, op_start, reqdata, reqdesc->funcname, (op_ctx->ctx_export != NULL ? op_ctx->ctx_export->export_id : -1)); #endif #if defined(HAVE_BLKIN) BLKIN_TIMESTAMP( &reqdata->r_u.req.svc.bl_trace, &reqdata->r_u.req.svc.rq_xprt->blkin.endp, "nfs_rpc_process_request-pre-service"); BLKIN_KEYVAL_STRING( &reqdata->r_u.req.svc.bl_trace, &reqdata->r_u.req.svc.rq_xprt->blkin.endp, "op-name", reqdesc->funcname ); BLKIN_KEYVAL_INTEGER( &reqdata->r_u.req.svc.bl_trace, &reqdata->r_u.req.svc.rq_xprt->blkin.endp, "export-id", (op_ctx->ctx_export != NULL) ? op_ctx->ctx_export->export_id : -1); #endif rc = reqdesc->service_function(arg_nfs, &reqdata->r_u.req.svc, res_nfs); #ifdef USE_LTTNG tracepoint(nfs_rpc, op_end, reqdata); #endif #if defined(HAVE_BLKIN) BLKIN_TIMESTAMP( &reqdata->r_u.req.svc.bl_trace, &reqdata->r_u.req.svc.rq_xprt->blkin.endp, "nfs_rpc_process_request-post-service"); #endif } #ifdef _USE_NFS3 req_error: #endif /* _USE_NFS3 */ /* NFSv4 stats are handled in nfs4_compound() */ if (reqdata->r_u.req.svc.rq_msg.cb_prog != NFS_program[P_NFS] || reqdata->r_u.req.svc.rq_msg.cb_vers != NFS_V4) server_stats_nfs_done(reqdata, rc, false); /* If request is dropped, no return to the client */ if (rc == NFS_REQ_DROP) { /* The request was dropped */ LogDebug(COMPONENT_DISPATCH, "Drop request rpc_xid=%" PRIu32 ", program %" PRIu32 ", version %" PRIu32 ", function %" PRIu32, reqdata->r_u.req.svc.rq_msg.rm_xid, reqdata->r_u.req.svc.rq_msg.cb_prog, reqdata->r_u.req.svc.rq_msg.cb_vers, reqdata->r_u.req.svc.rq_msg.cb_proc); /* If the request is not normally cached, then the entry * will be removed later. We only remove a reply that is * normally cached that has been dropped. */ if (nfs_dupreq_delete(&reqdata->r_u.req.svc) != DUPREQ_SUCCESS) { LogCrit(COMPONENT_DISPATCH, "Attempt to delete duplicate request failed on line %d", __LINE__); } goto freeargs; } else { LogFullDebug(COMPONENT_DISPATCH, "Before svc_sendreply on socket %d", xprt->xp_fd); reqdata->r_u.req.svc.rq_msg.RPCM_ack.ar_results.where = (caddr_t) res_nfs; reqdata->r_u.req.svc.rq_msg.RPCM_ack.ar_results.proc = reqdesc->xdr_encode_func; xprt_rc = svc_sendreply(&reqdata->r_u.req.svc); if (xprt_rc >= XPRT_DIED) { LogDebug(COMPONENT_DISPATCH, "NFS DISPATCHER: FAILURE: Error while calling svc_sendreply on a new request. rpcxid=%" PRIu32 " socket=%d function:%s client:%s program:%" PRIu32 " nfs version:%" PRIu32 " proc:%" PRIu32 " errno: %d", reqdata->r_u.req.svc.rq_msg.rm_xid, xprt->xp_fd, reqdesc->funcname, client_ip, reqdata->r_u.req.svc.rq_msg.cb_prog, reqdata->r_u.req.svc.rq_msg.cb_vers, reqdata->r_u.req.svc.rq_msg.cb_proc, errno); SVC_DESTROY(xprt); goto freeargs; } LogFullDebug(COMPONENT_DISPATCH, "After svc_sendreply on socket %d", xprt->xp_fd); } /* rc == NFS_REQ_DROP */ /* Finish any request not already deleted */ if (dpq_status == DUPREQ_SUCCESS) dpq_status = nfs_dupreq_finish(&reqdata->r_u.req.svc, res_nfs); goto freeargs; /* Reject the request for authentication reason (incompatible * file handle) */ if (isInfo(COMPONENT_DISPATCH) || isInfo(COMPONENT_EXPORT)) { char dumpfh[1024]; sprint_fhandle3(dumpfh, (nfs_fh3 *) arg_nfs); LogInfo(COMPONENT_DISPATCH, "%s Request from host %s V3 not allowed on this export, proc=%" PRIu32 ", FH=%s", progname, client_ip, reqdata->r_u.req.svc.rq_msg.cb_proc, dumpfh); } auth_rc = AUTH_FAILED; auth_failure: svcerr_auth(&reqdata->r_u.req.svc, auth_rc); /* nb, a no-op when req is uncacheable */ if (nfs_dupreq_delete(&reqdata->r_u.req.svc) != DUPREQ_SUCCESS) { LogCrit(COMPONENT_DISPATCH, "Attempt to delete duplicate request failed on line %d", __LINE__); } freeargs: /* Free the allocated resources once the work is done */ /* Free the arguments */ if ((reqdata->r_u.req.svc.rq_msg.cb_vers == 2) || (reqdata->r_u.req.svc.rq_msg.cb_vers == 3) || (reqdata->r_u.req.svc.rq_msg.cb_vers == 4)) { if (!xdr_free(reqdesc->xdr_decode_func, (caddr_t) arg_nfs)) { LogCrit(COMPONENT_DISPATCH, "%s FAILURE: Bad xdr_free for %s", __func__, reqdesc->funcname); } } /* Finalize the request. */ if (res_nfs) nfs_dupreq_rele(&reqdata->r_u.req.svc, reqdesc); SetClientIP(NULL); if (op_ctx->client != NULL) { put_gsh_client(op_ctx->client); op_ctx->client = NULL; } if (op_ctx->ctx_export != NULL) { put_gsh_export(op_ctx->ctx_export); op_ctx->ctx_export = NULL; } clean_credentials(); op_ctx = NULL; #ifdef USE_LTTNG tracepoint(nfs_rpc, end, reqdata); #endif return SVC_STAT(xprt); } /** * @brief Report Invalid Program number * * @param[in] reqnfs NFS request * */ static enum xprt_stat nfs_rpc_noprog(request_data_t *reqdata) { LogFullDebug(COMPONENT_DISPATCH, "Invalid Program number %" PRIu32, reqdata->r_u.req.svc.rq_msg.cb_prog); return svcerr_noprog(&reqdata->r_u.req.svc); } /** * @brief Report Invalid protocol Version * * @param[in] reqnfs NFS request * */ static enum xprt_stat nfs_rpc_novers(request_data_t *reqdata, int lo_vers, int hi_vers) { LogFullDebug(COMPONENT_DISPATCH, "Invalid protocol Version %" PRIu32 " for Program number %" PRIu32, reqdata->r_u.req.svc.rq_msg.cb_vers, reqdata->r_u.req.svc.rq_msg.cb_prog); return svcerr_progvers(&reqdata->r_u.req.svc, lo_vers, hi_vers); } /** * @brief Report Invalid Procedure * * @param[in] reqnfs NFS request * */ static enum xprt_stat nfs_rpc_noproc(request_data_t *reqdata) { LogFullDebug(COMPONENT_DISPATCH, "Invalid Procedure %" PRIu32 " in protocol Version %" PRIu32 " for Program number %" PRIu32, reqdata->r_u.req.svc.rq_msg.cb_proc, reqdata->r_u.req.svc.rq_msg.cb_vers, reqdata->r_u.req.svc.rq_msg.cb_prog); return svcerr_noproc(&reqdata->r_u.req.svc); } /** * @brief Validate rpc calls, extract nfs function descriptor. * * Validate the rpc call program, version, and procedure within range. * Send svcerr_* reply on errors. * * Choose the function descriptor, either a valid one or the default * invalid handler. * * @param[in,out] req service request * * @return whether the request is valid. */ enum xprt_stat nfs_rpc_valid_NFS(struct svc_req *req) { request_data_t *reqdata = container_of(req, struct request_data, r_u.req.svc); int lo_vers; int hi_vers; reqdata->r_u.req.funcdesc = &invalid_funcdesc; if (req->rq_msg.cb_prog == NFS_program[P_NFS]) { if (req->rq_msg.cb_vers == NFS_V4) { if ((NFS_options & CORE_OPTION_NFSV4) && req->rq_msg.cb_proc <= NFSPROC4_COMPOUND) { reqdata->r_u.req.funcdesc = &nfs4_func_desc[req->rq_msg.cb_proc]; return nfs_rpc_process_request(reqdata); } return nfs_rpc_noproc(reqdata); } if (req->rq_msg.cb_vers == NFS_V3) { #ifdef _USE_NFS3 if ((NFS_options & CORE_OPTION_NFSV3) && req->rq_msg.cb_proc <= NFSPROC3_COMMIT) { reqdata->r_u.req.funcdesc = &nfs3_func_desc[req->rq_msg.cb_proc]; return nfs_rpc_process_request(reqdata); } #endif /* _USE_NFS3 */ return nfs_rpc_noproc(reqdata); } lo_vers = NFS_V4; hi_vers = NFS_V3; #ifdef _USE_NFS3 if (NFS_options & CORE_OPTION_NFSV3) lo_vers = NFS_V3; #endif /* _USE_NFS3 */ if (NFS_options & CORE_OPTION_NFSV4) hi_vers = NFS_V4; return nfs_rpc_novers(reqdata, lo_vers, hi_vers); } return nfs_rpc_noprog(reqdata); } enum xprt_stat nfs_rpc_valid_NLM(struct svc_req *req) { request_data_t *reqdata = container_of(req, struct request_data, r_u.req.svc); reqdata->r_u.req.funcdesc = &invalid_funcdesc; #ifdef _USE_NLM if (req->rq_msg.cb_prog == NFS_program[P_NLM] && (NFS_options & CORE_OPTION_NFSV3)) { if (req->rq_msg.cb_vers == NLM4_VERS) { if (req->rq_msg.cb_proc <= NLMPROC4_FREE_ALL) { reqdata->r_u.req.funcdesc = &nlm4_func_desc[req->rq_msg.cb_proc]; return nfs_rpc_process_request(reqdata); } return nfs_rpc_noproc(reqdata); } return nfs_rpc_novers(reqdata, NLM4_VERS, NLM4_VERS); } #endif /* _USE_NLM */ return nfs_rpc_noprog(reqdata); } enum xprt_stat nfs_rpc_valid_MNT(struct svc_req *req) { request_data_t *reqdata = container_of(req, struct request_data, r_u.req.svc); reqdata->r_u.req.funcdesc = &invalid_funcdesc; if (req->rq_msg.cb_prog == NFS_program[P_MNT] && (NFS_options & CORE_OPTION_NFSV3)) { reqdata->r_u.req.lookahead.flags |= NFS_LOOKAHEAD_MOUNT; /* Some clients may use the wrong mount version to * umount, so always allow umount. Otherwise, only allow * request if the appropriate mount version is enabled. * Also need to allow dump and export, so just disallow * mount if version not supported. */ if (req->rq_msg.cb_vers == MOUNT_V3) { if (req->rq_msg.cb_proc <= MOUNTPROC3_EXPORT) { reqdata->r_u.req.funcdesc = &mnt3_func_desc[req->rq_msg.cb_proc]; return nfs_rpc_process_request(reqdata); } return nfs_rpc_noproc(reqdata); } if (req->rq_msg.cb_vers == MOUNT_V1) { if (req->rq_msg.cb_proc <= MOUNTPROC2_EXPORT && req->rq_msg.cb_proc != MOUNTPROC2_MNT) { reqdata->r_u.req.funcdesc = &mnt1_func_desc[req->rq_msg.cb_proc]; return nfs_rpc_process_request(reqdata); } return nfs_rpc_noproc(reqdata); } return nfs_rpc_novers(reqdata, MOUNT_V1, MOUNT_V3); } return nfs_rpc_noprog(reqdata); } enum xprt_stat nfs_rpc_valid_RQUOTA(struct svc_req *req) { request_data_t *reqdata = container_of(req, struct request_data, r_u.req.svc); reqdata->r_u.req.funcdesc = &invalid_funcdesc; if (req->rq_msg.cb_prog == NFS_program[P_RQUOTA]) { if (req->rq_msg.cb_vers == EXT_RQUOTAVERS) { if (req->rq_msg.cb_proc <= RQUOTAPROC_SETACTIVEQUOTA) { reqdata->r_u.req.funcdesc = &rquota2_func_desc[req->rq_msg.cb_proc]; return nfs_rpc_process_request(reqdata); } return nfs_rpc_noproc(reqdata); } if (req->rq_msg.cb_vers == RQUOTAVERS) { if (req->rq_msg.cb_proc <= RQUOTAPROC_SETACTIVEQUOTA) { reqdata->r_u.req.funcdesc = &rquota1_func_desc[req->rq_msg.cb_proc]; return nfs_rpc_process_request(reqdata); } return nfs_rpc_noproc(reqdata); } return nfs_rpc_novers(reqdata, RQUOTAVERS, EXT_RQUOTAVERS); } return nfs_rpc_noprog(reqdata); } nfs-ganesha-2.6.0/src/Protocols/000077500000000000000000000000001324272410200164615ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/Protocols/.gitignore000066400000000000000000000000171324272410200204470ustar00rootroot00000000000000test_mnt_proto nfs-ganesha-2.6.0/src/Protocols/9P/000077500000000000000000000000001324272410200167515ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/Protocols/9P/9P_Structures.txt000066400000000000000000000102111324272410200222400ustar00rootroot00000000000000 /* Structures for Protocol Operations */ struct _9p_rlerror { u32 ecode; }; struct _9p_tstatfs { u32 fid; }; struct _9p_rstatfs { u32 type; u32 bsize; u64 blocks; u64 bfree; u64 bavail; u64 files; u64 ffree; u64 fsid; u32 namelen; }; struct _9p_tlopen { u32 fid; u32 flags; }; struct _9p_rlopen { struct _9p_qid qid; u32 iounit; }; struct _9p_tlcreate { u32 fid; struct _9p_str name; u32 flags; u32 mode; u32 gid; }; struct _9p_rlcreate { struct _9p_qid qid; u32 iounit; }; struct _9p_tsymlink { u32 fid; struct _9p_str name; struct _9p_str symtgt; u32 gid; }; struct _9p_rsymlink { struct _9p_qid qid; }; struct _9p_tmknod { u32 fid; struct _9p_str name; u32 mode; u32 major; u32 minor; u32 gid; }; struct _9p_rmknod { struct _9p_qid qid; }; struct _9p_trename { u32 fid; u32 dfid; struct _9p_str name; }; struct _9p_rrename { }; struct _9p_treadlink { u32 fid; }; struct _9p_rreadlink { struct _9p_str target; }; struct _9p_tgetattr { u32 fid; u64 request_mask; }; struct _9p_rgetattr { u64 valid; struct _9p_qid qid; u32 mode; u32 uid; u32 gid; u64 nlink; u64 rdev; u64 size; u64 blksize; u64 blocks; u64 atime_sec; u64 atime_nsec; u64 mtime_sec; u64 mtime_nsec; u64 ctime_sec; u64 ctime_nsec; u64 btime_sec; u64 btime_nsec; u64 gen; u64 data_version; }; struct _9p_tsetattr { u32 fid; u32 valid; u32 mode; u32 uid; u32 gid; u64 size; u64 atime_sec; u64 atime_nsec; u64 mtime_sec; u64 mtime_nsec; }; struct _9p_rsetattr { }; struct _9p_txattrwalk { u32 fid; u32 attrfid; struct _9p_str name; }; struct _9p_rxattrwalk { u64 size; }; struct _9p_txattrcreate { u32 fid; struct _9p_str name; u64 size; u32 flag; }; struct _9p_rxattrcreate { }; struct _9p_treaddir { u32 fid; u64 offset; u32 count; }; struct _9p_rreaddir { u32 count; u8 *data; }; struct _9p_tfsync { u32 fid; }; struct _9p_rfsync { }; struct _9p_tlock { u32 fid; u8 type; u32 flags; u64 start; u64 length; u32 proc_id; struct _9p_str client_id; }; struct _9p_rlock { u8 status; }; struct _9p_tgetlock { u32 fid; u8 type; u64 start; u64 length; u32 proc_id; struct _9p_str client_id; }; struct _9p_rgetlock { u8 type; u64 start; u64 length; u32 proc_id; struct _9p_str client_id; }; struct _9p_tlink { u32 dfid; u32 fid; struct _9p_str name; }; struct _9p_rlink { }; struct _9p_tmkdir { u32 fid; struct _9p_str name; u32 mode; u32 gid; }; struct _9p_rmkdir { struct _9p_qid qid; }; struct _9p_trenameat { u32 olddirfid; struct _9p_str oldname; u32 newdirfid; struct _9p_str newname; }; struct _9p_rrenameat { }; struct _9p_tunlinkat { u32 dirfid; struct _9p_str name; u32 flags; }; struct _9p_runlinkat { }; struct _9p_tawrite { u32 fid; u8 datacheck; u64 offset; u32 count; u32 rsize; u8 *data; u32 check; }; struct _9p_rawrite { u32 count; }; struct _9p_tversion { u32 msize ; struct _9p_str version ; }; struct _9p_rversion { u32 msize; struct _9p_str version; }; struct _9p_tauth { u32 afid; struct _9p_str uname; struct _9p_str aname; u32 n_uname; /* 9P2000.u extensions */ }; struct _9p_rauth { struct _9p_qid qid; }; struct _9p_rerror { struct _9p_str error; u32 errnum; /* 9p2000.u extension */ }; struct _9p_tflush { u16 oldtag; }; struct _9p_rflush { }; struct _9p_tattach { u32 fid; u32 afid; struct _9p_str uname; struct _9p_str aname; u32 n_uname; /* 9P2000.u extensions */ }; struct _9p_rattach { struct _9p_qid qid; }; struct _9p_twalk { u32 fid; u32 newfid; u16 nwname; struct _9p_str wnames[_9P_MAXWELEM]; }; struct _9p_rwalk { u16 nwqid; struct _9p_qid wqids[_9P_MAXWELEM]; }; struct _9p_topen { u32 fid; u8 mode; }; struct _9p_ropen { struct _9p_qid qid; u32 iounit; }; struct _9p_tcreate { u32 fid; struct _9p_str name; u32 perm; u8 mode; struct _9p_str extension; }; struct _9p_rcreate { struct _9p_qid qid; u32 iounit; }; struct _9p_tread { u32 fid; u64 offset; u32 count; }; struct _9p_rread { u32 count; u8 *data; }; struct _9p_twrite { u32 fid; u64 offset; u32 count; u8 *data; }; struct _9p_rwrite { u32 count; }; struct _9p_tclunk { u32 fid; }; struct _9p_rclunk { }; struct _9p_tremove { u32 fid; }; struct _9p_rremove { }; union _9p_tmsg { } ; nfs-ganesha-2.6.0/src/Protocols/9P/9p_attach.c000066400000000000000000000155051324272410200207770ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_attach.c * \brief 9P version * * 9p_attach.c : _9P_interpretor, request ATTACH * * */ #include "config.h" #include #include #include #include "nfs_core.h" #include "export_mgr.h" #include "log.h" #include "fsal.h" #include "nfs_exports.h" #include "9p.h" int _9p_attach(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u32 *afid = NULL; u16 *uname_len = NULL; char *uname_str = NULL; u16 *aname_len = NULL; char *aname_str = NULL; u32 *n_uname = NULL; u32 err = 0; struct _9p_fid *pfid = NULL; fsal_status_t fsal_status; char exppath[MAXPATHLEN+1]; struct gsh_buffdesc fh_desc; struct fsal_obj_handle *pfsal_handle; int port; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getptr(cursor, afid, u32); _9p_getstr(cursor, uname_len, uname_str); _9p_getstr(cursor, aname_len, aname_str); _9p_getptr(cursor, n_uname, u32); LogDebug(COMPONENT_9P, "TATTACH: tag=%u fid=%u afid=%d uname='%.*s' aname='%.*s' n_uname=%d", (u32) *msgtag, *fid, *afid, (int) *uname_len, uname_str, (int) *aname_len, aname_str, *n_uname); if (*fid >= _9P_FID_PER_CONN) { err = ERANGE; goto errout; } /* * Find the export for the aname (using as well Path or Tag) * * Keep it in the op_ctx. */ if (*aname_len >= sizeof(exppath)) { err = ENAMETOOLONG; goto errout; } snprintf(exppath, sizeof(exppath), "%.*s", (int)*aname_len, aname_str); /* Find the export for the dirname (using as well Path, Pseudo, or Tag) */ if (exppath[0] != '/') { LogFullDebug(COMPONENT_9P, "Searching for export by tag for %s", exppath); op_ctx->ctx_export = get_gsh_export_by_tag(exppath); } else if (nfs_param.core_param.mount_path_pseudo) { LogFullDebug(COMPONENT_9P, "Searching for export by pseudo for %s", exppath); op_ctx->ctx_export = get_gsh_export_by_pseudo(exppath, false); } else { LogFullDebug(COMPONENT_9P, "Searching for export by path for %s", exppath); op_ctx->ctx_export = get_gsh_export_by_path(exppath, false); } /* Did we find something ? */ if (op_ctx->ctx_export == NULL) { err = ENOENT; goto errout; } /* Fill in more of the op_ctx */ op_ctx->fsal_export = op_ctx->ctx_export->fsal_export; op_ctx->caller_addr = &req9p->pconn->addrpeer; /* We store the export_perms in pconn so we only have to evaluate * them once. */ op_ctx->export_perms = &req9p->pconn->export_perms; /* And fill in the op_ctx export_perms and then check them. */ export_check_access(); if ((op_ctx->export_perms->options & EXPORT_OPTION_9P) == 0) { LogInfo(COMPONENT_9P, "9P is not allowed for this export entry, rejecting client"); err = EACCES; goto errout; } port = get_port(&req9p->pconn->addrpeer); if (op_ctx->export_perms->options & EXPORT_OPTION_PRIVILEGED_PORT && port >= IPPORT_RESERVED) { LogInfo(COMPONENT_9P, "Port %d is too high for this export entry, rejecting client", port); err = EACCES; goto errout; } /* Set export and fid id in fid */ pfid = gsh_calloc(1, sizeof(struct _9p_fid)); /* Copy the export into the pfid with reference. */ pfid->export = op_ctx->ctx_export; get_gsh_export_ref(pfid->export); pfid->fid = *fid; req9p->pconn->fids[*fid] = pfid; /* Is user name provided as a string or as an uid ? */ if (*n_uname != _9P_NONUNAME) { /* Build the fid creds */ err = _9p_tools_get_req_context_by_uid(*n_uname, pfid); if (err != 0) { err = -err; goto errout; } } else if (*uname_len != 0) { /* Build the fid creds */ err = _9p_tools_get_req_context_by_name(*uname_len, uname_str, pfid); if (err != 0) { err = -err; goto errout; } } else { /* No n_uname nor uname */ err = EINVAL; goto errout; } if (exppath[0] != '/' || !strcmp(exppath, export_path(op_ctx->ctx_export))) { /* Check if root object is correctly set, fetch it, and take an * LRU reference. */ fsal_status = nfs_export_get_root_entry(op_ctx->ctx_export, &pfid->pentry); if (FSAL_IS_ERROR(fsal_status)) { err = _9p_tools_errno(fsal_status); goto errout; } } else { fsal_status = op_ctx->fsal_export->exp_ops.lookup_path( op_ctx->fsal_export, exppath, &pfsal_handle, NULL); if (FSAL_IS_ERROR(fsal_status)) { err = _9p_tools_errno(fsal_status); goto errout; } pfsal_handle->obj_ops.handle_to_key(pfsal_handle, &fh_desc); fsal_status = op_ctx->fsal_export->exp_ops.create_handle( op_ctx->fsal_export, &fh_desc, &pfid->pentry, NULL); if (FSAL_IS_ERROR(fsal_status)) { err = _9p_tools_errno(fsal_status); goto errout; } } /* Initialize state_t embeded in fid. The refcount is initialized * to one to represent the state_t being embeded in the fid. This * prevents it from ever being reduced to zero by dec_state_t_ref. */ pfid->state = op_ctx->fsal_export->exp_ops.alloc_state(op_ctx->fsal_export, STATE_TYPE_9P_FID, NULL); glist_init(&pfid->state->state_data.fid.state_locklist); pfid->state->state_refcount = 1; /* Compute the qid */ pfid->qid.type = _9P_QTDIR; pfid->qid.version = 0; /* No cache, we want the client * to stay synchronous with the server */ pfid->qid.path = pfid->pentry->fileid; pfid->xattr = NULL; /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RATTACH); _9p_setptr(cursor, msgtag, u16); _9p_setqid(cursor, pfid->qid); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RATTACH: tag=%u fid=%u qid=(type=%u,version=%u,path=%llu)", *msgtag, *fid, (u32) pfid->qid.type, pfid->qid.version, (unsigned long long)pfid->qid.path); return 1; errout: _9p_release_opctx(); if (pfid != NULL) free_fid(pfid); return _9p_rerror(req9p, msgtag, err, plenout, preply); } nfs-ganesha-2.6.0/src/Protocols/9P/9p_auth.c000066400000000000000000000042321324272410200204670ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_attach.c * \brief 9P version * * 9p_attach.c : _9P_interpretor, request ATTACH * * */ #include "config.h" #include #include #include #include "nfs_core.h" #include "export_mgr.h" #include "log.h" #include "fsal.h" #include "9p.h" int _9p_auth(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *afid = NULL; u16 *uname_len = NULL; char *uname_str = NULL; u16 *aname_len = NULL; char *aname_str = NULL; u32 *n_aname = NULL; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, afid, u32); _9p_getstr(cursor, uname_len, uname_str); _9p_getstr(cursor, aname_len, aname_str); _9p_getptr(cursor, n_aname, u32); LogDebug(COMPONENT_9P, "TAUTH: tag=%u afid=%d uname='%.*s' aname='%.*s' n_uname=%d", (u32) *msgtag, *afid, (int) *uname_len, uname_str, (int) *aname_len, aname_str, *n_aname); if (*afid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); /* This message is not implemented yet, return ENOTSUPP */ return _9p_rerror(req9p, msgtag, EOPNOTSUPP, plenout, preply); } nfs-ganesha-2.6.0/src/Protocols/9P/9p_clunk.c000066400000000000000000000045301324272410200206430ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_clunk.c * \brief 9P version * * 9p_clunk.c : _9P_interpretor, request ATTACH * * */ #include "config.h" #include #include #include #include "nfs_core.h" #include "log.h" #include "9p.h" int _9p_clunk(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; int rc; struct _9p_fid *pfid = NULL; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); LogDebug(COMPONENT_9P, "TCLUNK: tag=%u fid=%u", (u32) *msgtag, *fid); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "clunk request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); rc = _9p_tools_clunk(pfid); req9p->pconn->fids[*fid] = NULL; if (rc) { return _9p_rerror(req9p, msgtag, rc, plenout, preply); } /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RCLUNK); _9p_setptr(cursor, msgtag, u16); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RCLUNK: tag=%u fid=%u", (u32) *msgtag, *fid); return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_flush.c000066400000000000000000000036751324272410200206610ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_flush.c * \brief 9P version * * 9p_flush.c : _9P_interpretor, request ATTACH * * */ #include "config.h" #include #include #include #include "nfs_core.h" #include "log.h" #include "fsal.h" #include "9p.h" int _9p_flush(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u16 *oldtag = NULL; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, oldtag, u16); LogDebug(COMPONENT_9P, "TFLUSH: tag=%u oldtag=%u", (u32) *msgtag, (u32) *oldtag); _9p_FlushFlushHook(req9p->pconn, (int)*oldtag, req9p->flush_hook.sequence); /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RFLUSH); _9p_setptr(cursor, msgtag, u16); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RFLUSH: tag=%u oldtag=%u", (u32) *msgtag, (u32) *oldtag); return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_flush_hook.c000066400000000000000000000075471324272410200217030ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_flush_hook.c * \date $Date: 2006/02/23 12:33:05 $ * \brief The file that contain the routines dedicated to TFLUSH management * * 9p_flush_hook.c.c : routines for TFLUSH management. * */ #include "config.h" #include #include #include #include #include /* for having FNDELAY */ #include #include #include #include #include #include #include "hashtable.h" #include "log.h" #include "abstract_mem.h" #include "abstract_atomic.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_dupreq.h" #include "nfs_file_handle.h" #include "9p.h" struct flush_condition { pthread_cond_t condition; int reply_sent; }; void _9p_AddFlushHook(struct _9p_request_data *req, int tag, unsigned long sequence) { int bucket = tag % FLUSH_BUCKETS; struct _9p_flush_hook *hook = &req->flush_hook; struct _9p_conn *conn = req->pconn; hook->tag = tag; hook->condition = NULL; hook->sequence = sequence; PTHREAD_MUTEX_lock(&conn->flush_buckets[bucket].lock); glist_add(&conn->flush_buckets[bucket].list, &hook->list); PTHREAD_MUTEX_unlock(&conn->flush_buckets[bucket].lock); } void _9p_FlushFlushHook(struct _9p_conn *conn, int tag, unsigned long sequence) { int bucket = tag % FLUSH_BUCKETS; struct glist_head *node; struct _9p_flush_hook *hook = NULL; struct flush_condition fc; PTHREAD_MUTEX_lock(&conn->flush_buckets[bucket].lock); glist_for_each(node, &conn->flush_buckets[bucket].list) { hook = glist_entry(node, struct _9p_flush_hook, list); /* Cancel a request that has the right tag * --AND-- is older than the flush request. **/ if ((hook->tag == tag) && (hook->sequence < sequence)) { PTHREAD_COND_init(&fc.condition, NULL); fc.reply_sent = 0; hook->condition = &fc; glist_del(&hook->list); LogFullDebug(COMPONENT_9P, "Found tag to flush %d\n", tag); /* * Now, wait until the request is complete * so we can send the RFLUSH. * warning: this will unlock the bucket lock */ while (!fc.reply_sent) pthread_cond_wait( &fc.condition, &conn->flush_buckets[bucket].lock); break; } } PTHREAD_MUTEX_unlock(&conn->flush_buckets[bucket].lock); } void _9p_DiscardFlushHook(struct _9p_request_data *req) { struct _9p_flush_hook *hook = &req->flush_hook; struct _9p_conn *conn = req->pconn; int bucket = hook->tag % FLUSH_BUCKETS; PTHREAD_MUTEX_lock(&conn->flush_buckets[bucket].lock); /* If no flush request arrived, we have to * remove the hook from the list. * If a flush request arrived, signal the thread that is waiting */ if (hook->condition == NULL) glist_del(&hook->list); else { hook->condition->reply_sent = 1; pthread_cond_signal(&hook->condition->condition); } PTHREAD_MUTEX_unlock(&conn->flush_buckets[bucket].lock); } nfs-ganesha-2.6.0/src/Protocols/9P/9p_fsync.c000066400000000000000000000050401324272410200206460ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_fsync.c * * 9p_fsync.c : _9P_interpretor, request FSYNC * * */ #include "config.h" #include #include #include #include #include "nfs_core.h" #include "log.h" #include "fsal.h" #include "9p.h" int _9p_fsync(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; struct _9p_fid *pfid = NULL; fsal_status_t fsal_status; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); LogDebug(COMPONENT_9P, "TFSYNC: tag=%u fid=%u", (u32) *msgtag, *fid); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid open file */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); fsal_status = fsal_commit(pfid->pentry, 0LL, /* start at beginning of file */ 0LL); /* Mimic sync_file_range's behavior: */ /* count=0 means "whole file" */ if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RFSYNC); _9p_setptr(cursor, msgtag, u16); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RFSYNC: tag=%u fid=%u", (u32) *msgtag, *fid); return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_getattr.c000066400000000000000000000151611324272410200212030ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_getattr.c * \brief 9P version * * 9p_getattr.c : _9P_interpretor, request ATTACH * * */ #include "config.h" #include #include #include #include #include "nfs_core.h" #include "log.h" #include "export_mgr.h" #include "fsal.h" #include "9p.h" int _9p_getattr(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u64 *request_mask = NULL; fsal_status_t fsal_status; struct attrlist attrs; struct _9p_fid *pfid = NULL; u64 valid = 0LL; /* Not a pointer */ u32 mode = 0; /* Not a pointer */ u32 uid = 0; u32 gid = 0; u64 nlink = 0LL; u64 rdev = 0LL; u64 size = 0LL; u64 blksize = 0LL; /* Be careful, this one is no pointer */ u64 blocks = 0LL; /* And so does this one... */ u64 atime_sec = 0LL; u64 atime_nsec = 0LL; u64 mtime_sec = 0LL; u64 mtime_nsec = 0LL; u64 ctime_sec = 0LL; u64 ctime_nsec = 0LL; u64 btime_sec = 0LL; u64 btime_nsec = 0LL; u64 gen = 0LL; u64 data_version = 0LL; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getptr(cursor, request_mask, u64); LogDebug(COMPONENT_9P, "TGETATTR: tag=%u fid=%u request_mask=0x%llx", (u32) *msgtag, *fid, (unsigned long long) *request_mask); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); fsal_prepare_attrs(&attrs, ATTRS_NFS3); fsal_status = pfid->pentry->obj_ops.getattrs(pfid->pentry, &attrs); if (FSAL_IS_ERROR(fsal_status)) { LogDebug(COMPONENT_9P, "fsal_refresh_attrs failed %s", fsal_err_txt(fsal_status)); /* Done with the attrs */ fsal_release_attrs(&attrs); return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); } /* Attach point is found, build the requested attributes */ valid = _9P_GETATTR_BASIC; /* FSAL covers all basic attributes */ if (*request_mask & _9P_GETATTR_RDEV) { mode = (u32) attrs.mode; if (attrs.type == DIRECTORY) mode |= __S_IFDIR; if (attrs.type == REGULAR_FILE) mode |= __S_IFREG; if (attrs.type == SYMBOLIC_LINK) mode |= __S_IFLNK; if (attrs.type == SOCKET_FILE) mode |= __S_IFSOCK; if (attrs.type == BLOCK_FILE) mode |= __S_IFBLK; if (attrs.type == CHARACTER_FILE) mode |= __S_IFCHR; if (attrs.type == FIFO_FILE) mode |= __S_IFIFO; } else mode = 0; /** @todo this is racy, use cache_inode_lock_trust_attrs */ uid = (*request_mask & _9P_GETATTR_UID) ? (u32) attrs.owner : 0; gid = (*request_mask & _9P_GETATTR_GID) ? (u32) attrs.group : 0; nlink = (*request_mask & _9P_GETATTR_NLINK) ? (u64) attrs.numlinks : 0LL; /* rdev = (*request_mask & _9P_GETATTR_RDEV) ? * (u64) attrs.rawdev.major : * 0LL; */ rdev = (*request_mask & _9P_GETATTR_RDEV) ? (u64) pfid->export->filesystem_id.major : 0LL; size = (*request_mask & _9P_GETATTR_SIZE) ? (u64) attrs.filesize : 0LL; blksize = (*request_mask & _9P_GETATTR_BLOCKS) ? (u64) _9P_BLK_SIZE : 0LL; blocks = (*request_mask & _9P_GETATTR_BLOCKS) ? (u64) (attrs.filesize / DEV_BSIZE) : 0LL; atime_sec = (*request_mask & _9P_GETATTR_ATIME) ? (u64) attrs.atime.tv_sec : 0LL; atime_nsec = (*request_mask & _9P_GETATTR_ATIME) ? (u64) attrs.atime.tv_nsec : 0LL; mtime_sec = (*request_mask & _9P_GETATTR_MTIME) ? (u64) attrs.mtime.tv_sec : 0LL; mtime_nsec = (*request_mask & _9P_GETATTR_MTIME) ? (u64) attrs.mtime.tv_nsec : 0LL; ctime_sec = (*request_mask & _9P_GETATTR_CTIME) ? (u64) attrs.ctime.tv_sec : 0LL; ctime_nsec = (*request_mask & _9P_GETATTR_CTIME) ? (u64) attrs.ctime.tv_nsec : 0LL; /* Not yet supported attributes */ btime_sec = 0LL; btime_nsec = 0LL; gen = 0LL; data_version = 0LL; /* Done with the attrs */ fsal_release_attrs(&attrs); /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RGETATTR); _9p_setptr(cursor, msgtag, u16); _9p_setvalue(cursor, valid, u64); _9p_setqid(cursor, pfid->qid); _9p_setvalue(cursor, mode, u32); _9p_setvalue(cursor, uid, u32); _9p_setvalue(cursor, gid, u32); _9p_setvalue(cursor, nlink, u64); _9p_setvalue(cursor, rdev, u64); _9p_setvalue(cursor, size, u64); _9p_setvalue(cursor, blksize, u64); _9p_setvalue(cursor, blocks, u64); _9p_setvalue(cursor, atime_sec, u64); _9p_setvalue(cursor, atime_nsec, u64); _9p_setvalue(cursor, mtime_sec, u64); _9p_setvalue(cursor, mtime_nsec, u64); _9p_setvalue(cursor, ctime_sec, u64); _9p_setvalue(cursor, ctime_nsec, u64); _9p_setvalue(cursor, btime_sec, u64); _9p_setvalue(cursor, btime_nsec, u64); _9p_setvalue(cursor, gen, u64); _9p_setvalue(cursor, data_version, u64); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RGETATTR: tag=%u valid=0x%"PRIx64 "qid=(type=%u,version=%u, path=%" PRIu64") mode=0%o uid=%u gid=%u nlink=%"PRIu64 " rdev=%"PRIu64" size=%"PRIu64" blksize=%"PRIu64 " blocks=%"PRIu64" atime=(%"PRIu64",%"PRIu64") mtime=(%"PRIu64 ",%"PRIu64") ctime=(%"PRIu64",%"PRIu64") btime=(%"PRIu64 ",%"PRIu64") gen=%"PRIu64", data_version=%"PRIu64, *msgtag, valid, pfid->qid.type, pfid->qid.version, pfid->qid.path, mode, uid, gid, nlink, rdev, size, blksize, blocks, atime_sec, atime_nsec, mtime_sec, mtime_nsec, ctime_sec, ctime_nsec, btime_sec, btime_nsec, gen, data_version); return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_getlock.c000066400000000000000000000060071324272410200211600ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_getlock.c * \brief 9P version * * 9p_getlock.c : _9P_interpretor, request GETLOCK * * */ #include "config.h" #include #include #include #include #include "nfs_core.h" #include "abstract_mem.h" #include "log.h" #include "sal_functions.h" #include "fsal.h" #include "9p.h" int _9p_getlock(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u8 *type = NULL; u64 *start = NULL; u64 *length = NULL; u32 *proc_id = NULL; u16 *client_id_len = NULL; char *client_id_str = NULL; /* struct _9p_fid * pfid = NULL ; */ /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getptr(cursor, type, u8); _9p_getptr(cursor, start, u64); _9p_getptr(cursor, length, u64); _9p_getptr(cursor, proc_id, u32); _9p_getstr(cursor, client_id_len, client_id_str); LogDebug(COMPONENT_9P, "TGETLOCK: tag=%u fid=%u type=%u start=%llu length=%llu proc_id=%u client=%.*s", (u32) *msgtag, *fid, *type, (unsigned long long) *start, (unsigned long long)*length, *proc_id, *client_id_len, client_id_str); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); /* pfid = req9p->pconn->fids[*fid] ; */ /** @todo This function does nothing for the moment. * Make it compliant with fcntl( F_GETLCK, ... */ /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RGETLOCK); _9p_setptr(cursor, msgtag, u16); _9p_setptr(cursor, type, u8); _9p_setptr(cursor, start, u64); _9p_setptr(cursor, length, u64); _9p_setptr(cursor, proc_id, u32); _9p_setstr(cursor, *client_id_len, client_id_str); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RGETLOCK: tag=%u fid=%u type=%u start=%llu length=%llu proc_id=%u client=%.*s", (u32) *msgtag, *fid, *type, (unsigned long long) *start, (unsigned long long)*length, *proc_id, *client_id_len, client_id_str); return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_interpreter.c000066400000000000000000000141601324272410200220720ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * @file 9p_interpreter.c * @brief 9P interpretor */ #include "config.h" #include #include #include #include "nfs_core.h" #include "9p.h" #include "fsal.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_dupreq.h" #include "nfs_file_handle.h" #include "server_stats.h" /* opcode to function array */ const struct _9p_function_desc _9pfuncdesc[] = { [0] = {_9p_not_2000L, "no function"}, /* out of bounds */ [_9P_TSTATFS] = {_9p_statfs, "_9P_TSTATFS"}, [_9P_TLOPEN] = {_9p_lopen, "_9P_TLOPEN"}, [_9P_TLCREATE] = {_9p_lcreate, "_9P_TLCREATE"}, [_9P_TSYMLINK] = {_9p_symlink, "_9P_TSYMLINK"}, [_9P_TMKNOD] = {_9p_mknod, "_9P_TMKNOD"}, [_9P_TRENAME] = {_9p_rename, "_9P_TRENAME"}, [_9P_TREADLINK] = {_9p_readlink, "_9P_TREADLINK"}, [_9P_TGETATTR] = {_9p_getattr, "_9P_TGETATTR"}, [_9P_TSETATTR] = {_9p_setattr, "_9P_TSETATTR"}, [_9P_TXATTRWALK] = {_9p_xattrwalk, "_9P_TXATTRWALK"}, [_9P_TXATTRCREATE] = {_9p_xattrcreate, "_9P_TXATTRCREATE"}, [_9P_TREADDIR] = {_9p_readdir, "_9P_TREADDIR"}, [_9P_TFSYNC] = {_9p_fsync, "_9P_TFSYNC"}, [_9P_TLOCK] = {_9p_lock, "_9P_TLOCK"}, [_9P_TGETLOCK] = {_9p_getlock, "_9P_TGETLOCK"}, [_9P_TLINK] = {_9p_link, "_9P_TLINK"}, [_9P_TMKDIR] = {_9p_mkdir, "_9P_TMKDIR"}, [_9P_TRENAMEAT] = {_9p_renameat, "_9P_TRENAMEAT"}, [_9P_TUNLINKAT] = {_9p_unlinkat, "_9P_TUNLINKAT"}, [_9P_TVERSION] = {_9p_version, "_9P_TVERSION"}, [_9P_TAUTH] = {_9p_auth, "_9P_TAUTH"}, [_9P_TATTACH] = {_9p_attach, "_9P_TATTACH"}, [_9P_TFLUSH] = {_9p_flush, "_9P_TFLUSH"}, [_9P_TWALK] = {_9p_walk, "_9P_TWALK"}, [_9P_TOPEN] = {_9p_not_2000L, "_9P_TOPEN"}, [_9P_TCREATE] = {_9p_not_2000L, "_9P_TCREATE"}, [_9P_TREAD] = {_9p_read, "_9P_TREAD"}, [_9P_TWRITE] = {_9p_write, "_9P_TWRITE"}, [_9P_TCLUNK] = {_9p_clunk, "_9P_TCLUNK"}, [_9P_TREMOVE] = {_9p_remove, "_9P_TREMOVE"}, [_9P_TSTAT] = {_9p_not_2000L, "_9P_TSTAT"}, [_9P_TWSTAT] = {_9p_not_2000L, "_9P_TWSTAT"} }; int _9p_not_2000L(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *msgdata = req9p->_9pmsg + _9P_HDR_SIZE; u8 msgtype = 0; u16 msgtag = 0; char *funcname = "inval"; /* Get message's type */ msgtype = *(u8 *)msgdata; /* TWSTAT is the last element in func_desc array, make sure * we don't lookup past it */ if (msgtype <= _9P_TWSTAT) funcname = _9pfuncdesc[msgtype].funcname; LogEvent(COMPONENT_9P, "(%u|%s) is not a 9P2000.L message, returning ENOTSUP", msgtype, funcname); _9p_rerror(req9p, &msgtag, ENOTSUP, plenout, preply); return -1; } /* _9p_not_2000L */ static ssize_t tcp_conn_send(struct _9p_conn *conn, const void *buf, size_t len, int flags) { ssize_t ret; PTHREAD_MUTEX_lock(&conn->sock_lock); ret = send(conn->trans_data.sockfd, buf, len, flags); PTHREAD_MUTEX_unlock(&conn->sock_lock); if (ret < 0) server_stats_transport_done(conn->client, 0, 0, 0, 0, 0, 1); else server_stats_transport_done(conn->client, 0, 0, 0, ret, 1, 0); return ret; } void _9p_tcp_process_request(struct _9p_request_data *req9p) { u32 outdatalen = 0; int rc = 0; char replydata[_9P_MSG_SIZE]; rc = _9p_process_buffer(req9p, replydata, &outdatalen); if (rc != 1) { LogMajor(COMPONENT_9P, "Could not process 9P buffer on socket #%lu", req9p->pconn->trans_data.sockfd); } else { if (tcp_conn_send(req9p->pconn, replydata, outdatalen, 0) != outdatalen) LogMajor(COMPONENT_9P, "Could not send 9P/TCP reply correctly on socket #%lu", req9p->pconn->trans_data.sockfd); } _9p_DiscardFlushHook(req9p); } /* _9p_process_request */ int _9p_process_buffer(struct _9p_request_data *req9p, char *replydata, u32 *poutlen) { char *msgdata; u32 msglen; u8 msgtype; int rc = 0; msgdata = req9p->_9pmsg; /* Get message's length */ msglen = *(u32 *) msgdata; msgdata += _9P_HDR_SIZE; /* Get message's type */ msgtype = *(u8 *) msgdata; msgdata += _9P_TYPE_SIZE; /* Check boundaries. 0 is no_function fallback */ if (msgtype < _9P_TSTATFS || msgtype > _9P_TWSTAT || _9pfuncdesc[msgtype].service_function == NULL) msgtype = 0; LogFullDebug(COMPONENT_9P, "9P msg: length=%u type (%u|%s)", msglen, (u32) msgtype, _9pfuncdesc[msgtype].funcname); /* Temporarily set outlen to maximum message size. This value will be * used inside the protocol functions for additional bound checking, * and then replaced by the actual message size, (see _9p_checkbound()) */ *poutlen = req9p->pconn->msize; /* Call the 9P service function */ rc = _9pfuncdesc[msgtype].service_function(req9p, poutlen, replydata); /* Record 9P statistics */ server_stats_9p_done(msgtype, req9p); _9p_release_opctx(); op_ctx = NULL; /* poison the op context to disgard it */ if (rc < 0) LogDebug(COMPONENT_9P, "%s: Error", _9pfuncdesc[msgtype].funcname); /** * @todo ops stats accounting goes here. * service function return codes need to be reworked to return error code * properly so that internal error code (currently -1) is distinguished * from protocol op error, currently partially handled in rerror, and * success return here so we can count errors and totals properly. * I/O stats handled in read and write as in nfs. */ return rc; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_lcreate.c000066400000000000000000000116321324272410200211470ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_lcreate.c * \brief 9P version * * 9p_lcreate.c : _9P_interpretor, request LCREATE * * */ #include "config.h" #include #include #include #include #include "nfs_core.h" #include "nfs_exports.h" #include "log.h" #include "fsal.h" #include "9p.h" int _9p_lcreate(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u32 *flags = NULL; u32 *mode = NULL; u32 *gid = NULL; u16 *name_len = NULL; char *name_str = NULL; struct _9p_fid *pfid = NULL; struct _9p_qid qid_newfile; u32 iounit = _9P_IOUNIT; struct fsal_obj_handle *pentry_newfile = NULL; char file_name[MAXNAMLEN+1]; fsal_status_t fsal_status; fsal_openflags_t openflags = 0; struct attrlist sattr; fsal_verifier_t verifier; enum fsal_create_mode createmode = FSAL_UNCHECKED; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getstr(cursor, name_len, name_str); _9p_getptr(cursor, flags, u32); _9p_getptr(cursor, mode, u32); _9p_getptr(cursor, gid, u32); LogDebug(COMPONENT_9P, "TLCREATE: tag=%u fid=%u name=%.*s flags=0%o mode=0%o gid=%u", (u32) *msgtag, *fid, *name_len, name_str, *flags, *mode, *gid); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); if (*name_len >= sizeof(file_name)) { LogDebug(COMPONENT_9P, "request with name too long (%u)", *name_len); return _9p_rerror(req9p, msgtag, ENAMETOOLONG, plenout, preply); } snprintf(file_name, sizeof(file_name), "%.*s", *name_len, name_str); _9p_openflags2FSAL(flags, &openflags); pfid->state->state_data.fid.share_access = _9p_openflags_to_share_access(flags); memset(&verifier, 0, sizeof(verifier)); memset(&sattr, 0, sizeof(sattr)); sattr.valid_mask = ATTR_MODE | ATTR_GROUP; sattr.mode = *mode; sattr.group = *gid; if (*flags & 0x10) { /* Filesize is already 0. */ sattr.valid_mask |= ATTR_SIZE; } if (*flags & 0x1000) { /* If OEXCL, use FSAL_EXCLUSIVE_9P create mode * so that we can pass the attributes specified * above. Verifier is ignored for this create mode * because we don't have to deal with retry. */ createmode = FSAL_EXCLUSIVE_9P; } fsal_status = fsal_open2(pfid->pentry, pfid->state, openflags, createmode, file_name, &sattr, verifier, &pentry_newfile, NULL); /* Release the attributes (may release an inherited ACL) */ fsal_release_attrs(&sattr); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); /* put parent directory entry */ pfid->pentry->obj_ops.put_ref(pfid->pentry); /* Build the qid */ qid_newfile.type = _9P_QTFILE; qid_newfile.version = 0; qid_newfile.path = pentry_newfile->fileid; /* The fid will represent the new file now - we can't fail anymore */ pfid->pentry = pentry_newfile; pfid->qid = qid_newfile; pfid->xattr = NULL; pfid->opens = 1; /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RLCREATE); _9p_setptr(cursor, msgtag, u16); _9p_setqid(cursor, qid_newfile); _9p_setvalue(cursor, iounit, u32); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RLCREATE: tag=%u fid=%u name=%.*s qid=(type=%u,version=%u,path=%llu) iounit=%u pentry=%p", (u32) *msgtag, *fid, *name_len, name_str, qid_newfile.type, qid_newfile.version, (unsigned long long)qid_newfile.path, iounit, pfid->pentry); return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_link.c000066400000000000000000000076641324272410200204770ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_link.c * \brief 9P version * * 9p_link.c : _9P_interpretor, request LINK * * */ #include "config.h" #include #include #include #include #include "nfs_core.h" #include "nfs_exports.h" #include "log.h" #include "fsal.h" #include "9p.h" int _9p_link(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *dfid = NULL; u32 *targetfid = NULL; u16 *name_len = NULL; char *name_str = NULL; struct _9p_fid *pdfid = NULL; struct _9p_fid *ptargetfid = NULL; fsal_status_t fsal_status; char link_name[MAXNAMLEN+1]; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, dfid, u32); _9p_getptr(cursor, targetfid, u32); _9p_getstr(cursor, name_len, name_str); LogDebug(COMPONENT_9P, "TLINK: tag=%u dfid=%u targetfid=%u name=%.*s", (u32) *msgtag, *dfid, *targetfid, *name_len, name_str); if (*dfid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pdfid = req9p->pconn->fids[*dfid]; /* Check that it is a valid fid */ if (pdfid == NULL || pdfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid dfid=%u", *dfid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pdfid, req9p); if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); if (*targetfid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); ptargetfid = req9p->pconn->fids[*targetfid]; /* Check that it is a valid fid */ if (ptargetfid == NULL || ptargetfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid targetfid=%u", *targetfid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } /* Check that pfid and pdfid are in the same export. */ if (ptargetfid->export != NULL && pdfid->export != NULL && ptargetfid->export->export_id != pdfid->export->export_id) { LogDebug(COMPONENT_9P, "request on targetfid=%u and dfid=%u crosses exports", *targetfid, *dfid); return _9p_rerror(req9p, msgtag, EXDEV, plenout, preply); } /* Let's do the job */ if (*name_len >= sizeof(link_name)) { LogDebug(COMPONENT_9P, "request with name too long (%u)", *name_len); return _9p_rerror(req9p, msgtag, ENAMETOOLONG, plenout, preply); } snprintf(link_name, sizeof(link_name), "%.*s", *name_len, name_str); fsal_status = fsal_link(ptargetfid->pentry, pdfid->pentry, link_name); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RLINK); _9p_setptr(cursor, msgtag, u16); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "TLINK: tag=%u dfid=%u targetfid=%u name=%.*s", (u32) *msgtag, *dfid, *targetfid, *name_len, name_str); return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_lock.c000066400000000000000000000135601324272410200204620ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_lock.c * \brief 9P version * * 9p_lock.c : _9P_interpretor, request LOCK * * */ #include "config.h" #include #include #include #include #include #include "nfs_core.h" #include "log.h" #include "export_mgr.h" #include "sal_functions.h" #include "fsal.h" #include "9p.h" /* * Reminder: * LOCK_TYPE_RDLCK = 0 * LOCK_TYPE_WRLCK = 1 * LOCK_TYPE_UNLCK = 2 */ char *strtype[] = { "RDLOCK", "WRLOCK", "UNLOCK" }; /* * Remonder: * LOCK_SUCCESS = 0 * LOCK_BLOCKED = 1 * LOCK_ERROR = 2 * LOCK_GRACE = 3 */ char *strstatus[] = { "SUCCESS", "BLOCKED", "ERROR", "GRACE" }; int _9p_lock(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u8 *type = NULL; u32 *flags = NULL; u64 *start = NULL; u64 *length = NULL; u32 *proc_id = NULL; u16 *client_id_len = NULL; char *client_id_str = NULL; u8 status = _9P_LOCK_SUCCESS; state_status_t state_status = STATE_SUCCESS; state_owner_t *holder; state_owner_t *powner; fsal_lock_param_t lock; fsal_lock_param_t conflict; memset(&lock, 0, sizeof(lock)); memset(&conflict, 0, sizeof(conflict)); char name[MAXNAMLEN+1]; struct addrinfo hints, *result; struct sockaddr_storage client_addr; struct _9p_fid *pfid = NULL; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getptr(cursor, type, u8); _9p_getptr(cursor, flags, u32); _9p_getptr(cursor, start, u64); _9p_getptr(cursor, length, u64); _9p_getptr(cursor, proc_id, u32); _9p_getstr(cursor, client_id_len, client_id_str); LogDebug(COMPONENT_9P, "TLOCK: tag=%u fid=%u type=%u|%s flags=0x%x start=%llu length=%llu proc_id=%u client=%.*s", (u32) *msgtag, *fid, *type, strtype[*type], *flags, (unsigned long long)*start, (unsigned long long)*length, *proc_id, *client_id_len, client_id_str); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); /* Tmp hook to avoid lock issue when compiling kernels. * This should not impact ONE client only * get the client's ip addr */ if (*client_id_len >= sizeof(name)) { LogDebug(COMPONENT_9P, "request with name too long (%u)", *client_id_len); return _9p_rerror(req9p, msgtag, ENAMETOOLONG, plenout, preply); } snprintf(name, sizeof(name), "%.*s", *client_id_len, client_id_str); memset((char *)&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_INET; if (getaddrinfo(name, NULL, &hints, &result)) return _9p_rerror(req9p, msgtag, EINVAL, plenout, preply); memcpy((char *)&client_addr, (char *)result->ai_addr, result->ai_addrlen); /* variable result is not needed anymore, let's free it */ freeaddrinfo(result); powner = get_9p_owner(&client_addr, *proc_id); if (powner == NULL) return _9p_rerror(req9p, msgtag, EINVAL, plenout, preply); /* Do the job */ switch (*type) { case _9P_LOCK_TYPE_RDLCK: case _9P_LOCK_TYPE_WRLCK: /* Fill in plock */ lock.lock_type = (*type == _9P_LOCK_TYPE_WRLCK) ? FSAL_LOCK_W : FSAL_LOCK_R; lock.lock_start = *start; lock.lock_length = *length; if (nfs_in_grace()) { status = _9P_LOCK_GRACE; break; } PTHREAD_RWLOCK_wrlock(&pfid->pentry->state_hdl->state_lock); state_status = state_lock(pfid->pentry, powner, pfid->state, STATE_NON_BLOCKING, NULL, &lock, &holder, &conflict); PTHREAD_RWLOCK_unlock(&pfid->pentry->state_hdl->state_lock); if (state_status == STATE_SUCCESS) status = _9P_LOCK_SUCCESS; else if (state_status == STATE_LOCK_BLOCKED || state_status == STATE_LOCK_CONFLICT) /** * Should handle _9P_LOCK_FLAGS_BLOCK in *flags, * but linux client replays blocking requests */ status = _9P_LOCK_BLOCKED; else status = _9P_LOCK_ERROR; break; case _9P_LOCK_TYPE_UNLCK: if (state_unlock(pfid->pentry, pfid->state, powner, false, 0, &lock) != STATE_SUCCESS) status = _9P_LOCK_ERROR; else status = _9P_LOCK_SUCCESS; break; default: return _9p_rerror(req9p, msgtag, EINVAL, plenout, preply); } /* switch( *type ) */ /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RLOCK); _9p_setptr(cursor, msgtag, u16); _9p_setvalue(cursor, status, u8); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RLOCK: tag=%u fid=%u type=%u|%s flags=0x%x start=%llu length=%llu proc_id=%u client=%.*s status=%u|%s", (u32) *msgtag, *fid, *type, strtype[*type], *flags, (unsigned long long) *start, (unsigned long long) *length, *proc_id, *client_id_len, client_id_str, status, strstatus[status]); return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_lopen.c000066400000000000000000000061551324272410200206510ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_lopen.c * \brief 9P version * * 9p_lopen.c : _9P_interpretor, request LOPEN * * */ #include "config.h" #include #include #include #include #include "nfs_core.h" #include "log.h" #include "fsal.h" #include "9p.h" int _9p_lopen(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u32 *flags = NULL; fsal_status_t fsal_status; fsal_openflags_t openflags = 0; struct _9p_fid *pfid = NULL; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getptr(cursor, flags, u32); LogDebug(COMPONENT_9P, "TLOPEN: tag=%u fid=%u flags=0x%x", (u32) *msgtag, *fid, *flags); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_openflags2FSAL(flags, &openflags); pfid->state->state_data.fid.share_access = _9p_openflags_to_share_access(flags); _9p_init_opctx(pfid, req9p); if (pfid->pentry->type == REGULAR_FILE) { /** @todo: Maybe other types (FIFO, SOCKET,...) require * to be opened too */ if (*flags & 0x10) openflags |= FSAL_O_TRUNC; fsal_status = fsal_reopen2(pfid->pentry, pfid->state, openflags, true); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); atomic_inc_uint32_t(&pfid->opens); } /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RLOPEN); _9p_setptr(cursor, msgtag, u16); _9p_setqid(cursor, pfid->qid); _9p_setvalue(cursor, _9P_IOUNIT, u32); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RLOPEN: tag=%u fid=%u qid=(type=%u,version=%u,path=%llu) iounit=%u", *msgtag, *fid, (u32) pfid->qid.type, pfid->qid.version, (unsigned long long)pfid->qid.path, _9P_IOUNIT); return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_mkdir.c000066400000000000000000000075751324272410200206510ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_mkdir.c * \brief 9P version * * 9p_mkdir.c : _9P_interpretor, request MKDIR * * */ #include "config.h" #include #include #include #include #include "nfs_core.h" #include "nfs_exports.h" #include "log.h" #include "fsal.h" #include "9p.h" int _9p_mkdir(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u32 *mode = NULL; u32 *gid = NULL; u16 *name_len = NULL; char *name_str = NULL; struct _9p_fid *pfid = NULL; struct _9p_qid qid_newdir; struct fsal_obj_handle *pentry_newdir = NULL; char dir_name[MAXNAMLEN+1]; fsal_status_t fsal_status; struct attrlist sattr; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getstr(cursor, name_len, name_str); _9p_getptr(cursor, mode, u32); _9p_getptr(cursor, gid, u32); LogDebug(COMPONENT_9P, "TMKDIR: tag=%u fid=%u name=%.*s mode=0%o gid=%u", (u32) *msgtag, *fid, *name_len, name_str, *mode, *gid); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); if (*name_len >= sizeof(dir_name)) { LogDebug(COMPONENT_9P, "request with name too long (%u)", *name_len); return _9p_rerror(req9p, msgtag, ENAMETOOLONG, plenout, preply); } snprintf(dir_name, sizeof(dir_name), "%.*s", *name_len, name_str); fsal_prepare_attrs(&sattr, ATTR_MODE); sattr.mode = *mode; sattr.valid_mask = ATTR_MODE; /* Create the directory */ /* BUGAZOMEU: @todo : the gid parameter is not used yet */ fsal_status = fsal_create(pfid->pentry, dir_name, DIRECTORY, &sattr, NULL, &pentry_newdir, NULL); /* Release the attributes (may release an inherited ACL) */ fsal_release_attrs(&sattr); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); pentry_newdir->obj_ops.put_ref(pentry_newdir); /* Build the qid */ qid_newdir.type = _9P_QTDIR; qid_newdir.version = 0; qid_newdir.path = pentry_newdir->fileid; /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RMKDIR); _9p_setptr(cursor, msgtag, u16); _9p_setqid(cursor, qid_newdir); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RMKDIR: tag=%u fid=%u name=%.*s qid=(type=%u,version=%u,path=%llu)", (u32) *msgtag, *fid, *name_len, name_str, qid_newdir.type, qid_newdir.version, (unsigned long long)qid_newdir.path); return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_mknod.c000066400000000000000000000112441324272410200206370ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_mknod.c * \brief 9P version * * 9p_mknod.c : _9P_interpretor, request MKNOD * * */ #include "config.h" #include #include #include #include #include "nfs_core.h" #include "nfs_exports.h" #include "log.h" #include "fsal.h" #include "9p.h" int _9p_mknod(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u32 *mode = NULL; u32 *gid = NULL; u32 *major = NULL; u32 *minor = NULL; u16 *name_len = NULL; char *name_str = NULL; struct _9p_fid *pfid = NULL; struct _9p_qid qid_newobj; struct fsal_obj_handle *pentry_newobj = NULL; char obj_name[MAXNAMLEN+1]; uint64_t fileid = 0LL; fsal_status_t fsal_status; object_file_type_t nodetype; struct attrlist object_attributes; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getstr(cursor, name_len, name_str); _9p_getptr(cursor, mode, u32); _9p_getptr(cursor, major, u32); _9p_getptr(cursor, minor, u32); _9p_getptr(cursor, gid, u32); LogDebug(COMPONENT_9P, "TMKNOD: tag=%u fid=%u name=%.*s mode=0%o major=%u minor=%u gid=%u", (u32) *msgtag, *fid, *name_len, name_str, *mode, *major, *minor, *gid); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); if (*name_len >= sizeof(obj_name)) { LogDebug(COMPONENT_9P, "request with name too long (%u)", *name_len); return _9p_rerror(req9p, msgtag, ENAMETOOLONG, plenout, preply); } snprintf(obj_name, sizeof(obj_name), "%.*s", *name_len, name_str); /* Set the nodetype */ if (S_ISDIR(*mode)) nodetype = CHARACTER_FILE; else if (S_ISBLK(*mode)) nodetype = BLOCK_FILE; else if (S_ISFIFO(*mode)) nodetype = FIFO_FILE; else if (S_ISSOCK(*mode)) nodetype = SOCKET_FILE; else /* bad type */ return _9p_rerror(req9p, msgtag, EINVAL, plenout, preply); fsal_prepare_attrs(&object_attributes, ATTR_RAWDEV | ATTR_MODE); object_attributes.rawdev.major = *major; object_attributes.rawdev.minor = *minor; object_attributes.valid_mask |= ATTR_RAWDEV; object_attributes.mode = *mode; /* Create the directory */ /** @todo BUGAZOMEU the gid parameter is not used yet */ fsal_status = fsal_create(pfid->pentry, obj_name, nodetype, &object_attributes, NULL, &pentry_newobj, NULL); /* Release the attributes (may release an inherited ACL) */ fsal_release_attrs(&object_attributes); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); /* we don't keep a reference to the entry */ pentry_newobj->obj_ops.put_ref(pentry_newobj); /* Build the qid */ qid_newobj.type = _9P_QTTMP; /** @todo BUGAZOMEU For wanting of something better */ qid_newobj.version = 0; qid_newobj.path = fileid; /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RMKNOD); _9p_setptr(cursor, msgtag, u16); _9p_setqid(cursor, qid_newobj); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "TMKNOD: tag=%u fid=%u name=%.*s major=%u minor=%u qid=(type=%u,version=%u,path=%llu)", (u32) *msgtag, *fid, *name_len, name_str, *major, *minor, qid_newobj.type, qid_newobj.version, (unsigned long long)qid_newobj.path); return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_proto_tools.c000066400000000000000000000233771324272410200221240ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_proto_tools.c * \brief 9P version * * 9p_proto_tools.c : _9P_interpretor, protocol's service functions * */ #include "config.h" #include #include #include #include #include #include #include #include "nfs_core.h" #include "log.h" #include "fsal.h" #include "9p.h" #include "idmapper.h" #include "uid2grp.h" #include "export_mgr.h" #include "fsal_convert.h" /** * @brief Allocate a new struct _9p_user_cred, with refcounter set to 1. * * @return NULL if the allocation failed, else the new structure. */ static struct _9p_user_cred *new_9p_user_creds() { struct _9p_user_cred *result = gsh_calloc(1, sizeof(struct _9p_user_cred)); result->refcount = 1; return result; } /** * @brief Get a new reference of user credential. * * This function increments the refcount of the argument. Any reference returned * by this function must be released. * * @param[in,out] creds The 9P credential containing the wanted credentials. * @return A reference to the credentials. */ static struct user_cred *get_user_cred_ref(struct _9p_user_cred *creds) { (void) atomic_inc_int64_t(&creds->refcount); return &creds->creds; } void get_9p_user_cred_ref(struct _9p_user_cred *creds) { (void) atomic_inc_int64_t(&creds->refcount); } void release_9p_user_cred_ref(struct _9p_user_cred *creds) { int64_t refcount = atomic_dec_int64_t(&creds->refcount); if (refcount != 0) { assert(refcount > 0); return; } gsh_free(creds); } /** * @brief Release a reference obtained with get_user_cred_ref. * * This function decrements the refcounter of the containing _9p_user_cred * structure. If this counter reaches 0, the structure is freed. * * @param creds The reference that is released. */ static void release_user_cred_ref(struct user_cred *creds) { struct _9p_user_cred *cred_9p = container_of(creds, struct _9p_user_cred, creds); release_9p_user_cred_ref(cred_9p); } int _9p_init(void) { return 0; } /* _9p_init */ void _9p_init_opctx(struct _9p_fid *pfid, struct _9p_request_data *req9p) { if (pfid->export != NULL) { /* export affectation (require refcount handling). */ if (op_ctx->ctx_export != pfid->export) { if (op_ctx->ctx_export != NULL) { LogCrit(COMPONENT_9P, "Op_ctx was already initialized, or was not allocated/cleaned up properly."); /* This tells there's an error in the code. * Use an assert because : * - if compiled in debug mode, the program will * crash and tell the developer he has something * to fix here. * - if compiled for production, we'll try to * recover. */ assert(false); } get_gsh_export_ref(pfid->export); op_ctx->ctx_export = pfid->export; op_ctx->fsal_export = op_ctx->ctx_export->fsal_export; } } if (req9p != NULL) op_ctx->export_perms = &req9p->pconn->export_perms; op_ctx->creds = get_user_cred_ref(pfid->ucred); } void _9p_release_opctx(void) { if (op_ctx->ctx_export != NULL) { put_gsh_export(op_ctx->ctx_export); op_ctx->ctx_export = NULL; } if (op_ctx->creds != NULL) { release_user_cred_ref(op_ctx->creds); op_ctx->creds = NULL; } } int _9p_tools_get_req_context_by_uid(u32 uid, struct _9p_fid *pfid) { struct group_data *grpdata; if (!uid2grp(uid, &grpdata)) return -ENOENT; pfid->ucred = new_9p_user_creds(); pfid->gdata = grpdata; pfid->ucred->creds.caller_uid = grpdata->uid; pfid->ucred->creds.caller_gid = grpdata->gid; pfid->ucred->creds.caller_glen = grpdata->nbgroups; pfid->ucred->creds.caller_garray = grpdata->groups; if (op_ctx->creds != NULL) release_user_cred_ref(op_ctx->creds); op_ctx->creds = get_user_cred_ref(pfid->ucred); op_ctx->req_type = _9P_REQUEST; return 0; } /* _9p_tools_get_fsal_cred */ int _9p_tools_get_req_context_by_name(int uname_len, char *uname_str, struct _9p_fid *pfid) { struct gsh_buffdesc name = { .addr = uname_str, .len = uname_len }; struct group_data *grpdata; if (!name2grp(&name, &grpdata)) return -ENOENT; pfid->ucred = new_9p_user_creds(); pfid->gdata = grpdata; pfid->ucred->creds.caller_uid = grpdata->uid; pfid->ucred->creds.caller_gid = grpdata->gid; pfid->ucred->creds.caller_glen = grpdata->nbgroups; pfid->ucred->creds.caller_garray = grpdata->groups; if (op_ctx->creds != NULL) release_user_cred_ref(op_ctx->creds); op_ctx->creds = get_user_cred_ref(pfid->ucred); op_ctx->req_type = _9P_REQUEST; return 0; } /* _9p_tools_get_fsal_cred */ int _9p_tools_errno(fsal_status_t fsal_status) { int rc = 0; switch (fsal_status.major) { case ERR_FSAL_NO_ERROR: rc = 0; break; case ERR_FSAL_NOMEM: rc = ENOMEM; break; case ERR_FSAL_NOTDIR: rc = ENOTDIR; break; case ERR_FSAL_EXIST: rc = EEXIST; break; case ERR_FSAL_NOTEMPTY: rc = ENOTEMPTY; break; case ERR_FSAL_NOENT: rc = ENOENT; break; case ERR_FSAL_ISDIR: rc = EISDIR; break; case ERR_FSAL_PERM: case ERR_FSAL_SEC: rc = EPERM; break; case ERR_FSAL_INVAL: case ERR_FSAL_NAMETOOLONG: case ERR_FSAL_NOT_OPENED: case ERR_FSAL_BADTYPE: case ERR_FSAL_SYMLINK: rc = EINVAL; break; case ERR_FSAL_NOSPC: rc = ENOSPC; break; case ERR_FSAL_ROFS: rc = EROFS; break; case ERR_FSAL_STALE: case ERR_FSAL_FHEXPIRED: rc = ESTALE; break; case ERR_FSAL_DQUOT: case ERR_FSAL_NO_QUOTA: rc = EDQUOT; break; case ERR_FSAL_IO: case ERR_FSAL_NXIO: rc = EIO; break; case ERR_FSAL_NOTSUPP: case ERR_FSAL_ATTRNOTSUPP: rc = ENOTSUP; break; case ERR_FSAL_ACCESS: rc = EACCES; break; case ERR_FSAL_DELAY: rc = EAGAIN; break; case ERR_FSAL_NO_DATA: rc = ENODATA; break; default: rc = EIO; break; } return rc; } /* _9p_tools_errno */ void _9p_openflags2FSAL(u32 *inflags, fsal_openflags_t *outflags) { if (inflags == NULL || outflags == NULL) return; if (*inflags & O_WRONLY) *outflags |= FSAL_O_WRITE; if (*inflags & O_RDWR) *outflags |= FSAL_O_RDWR; /* Exception: O_RDONLY is 0, it can't be tested with a logical and */ /* We consider that non( has O_WRONLY or has O_RDWR ) is RD_ONLY */ if (!(*inflags & (O_WRONLY | O_RDWR))) *outflags = FSAL_O_READ; } /* _9p_openflags2FSAL */ void free_fid(struct _9p_fid *pfid) { if (pfid->state != NULL) { if ((pfid->pentry->type == REGULAR_FILE) && pfid->opens) { /* We need to close the state before freeing the state. */ (void) pfid->pentry->obj_ops.close2( pfid->pentry, pfid->state); } pfid->state->state_exp->exp_ops.free_state( pfid->state->state_exp, pfid->state); } if (pfid->pentry != NULL) pfid->pentry->obj_ops.put_ref(pfid->pentry); if (pfid->ppentry != NULL) pfid->ppentry->obj_ops.put_ref(pfid->ppentry); if (pfid->export != NULL) put_gsh_export(pfid->export); if (pfid->ucred != NULL) release_9p_user_cred_ref(pfid->ucred); gsh_free(pfid->xattr); gsh_free(pfid); } int _9p_tools_clunk(struct _9p_fid *pfid) { fsal_status_t fsal_status; /* pentry may be null in the case of an aborted TATTACH * this would happens when trying to mount a non-existing * or non-authorized directory */ if (pfid->pentry == NULL) { LogEvent(COMPONENT_9P, "Trying to clunk a fid with NULL pentry. Bad mount ?"); return 0; } /* unref the related group list */ uid2grp_unref(pfid->gdata); /* If the fid is related to a xattr, free the related memory */ if (pfid->xattr != NULL && pfid->xattr->xattr_write == _9P_XATTR_DID_WRITE) { /* Check size give at TXATTRCREATE with * the one resulting from the writes */ if (pfid->xattr->xattr_size != pfid->xattr->xattr_offset) { free_fid(pfid); return EINVAL; } fsal_status = pfid->pentry->obj_ops.setextattr_value( pfid->pentry, pfid->xattr->xattr_name, pfid->xattr->xattr_content, pfid->xattr->xattr_size, false); if (FSAL_IS_ERROR(fsal_status)) { free_fid(pfid); return _9p_tools_errno(fsal_status); } } /* If object is an opened file, close it */ if ((pfid->pentry->type == REGULAR_FILE) && pfid->opens) { pfid->opens = 0; /* dead */ LogDebug(COMPONENT_9P, "Calling close on %s entry %p", object_file_type_to_str(pfid->pentry->type), pfid->pentry); fsal_status = pfid->pentry->obj_ops.close2(pfid->pentry, pfid->state); if (FSAL_IS_ERROR(fsal_status)) { free_fid(pfid); return _9p_tools_errno(fsal_status); } } free_fid(pfid); return 0; } void _9p_cleanup_fids(struct _9p_conn *conn) { int i; /* Allocate op_ctx, is should always be NULL here * Note we only need it if there is a non-null fid, * might be worth optimizing for huge clusters */ op_ctx = gsh_calloc(1, sizeof(struct req_op_context)); for (i = 0; i < _9P_FID_PER_CONN; i++) { if (conn->fids[i]) { _9p_init_opctx(conn->fids[i], NULL); _9p_tools_clunk(conn->fids[i]); _9p_release_opctx(); conn->fids[i] = NULL; /* poison the entry */ } } gsh_free(op_ctx); op_ctx = NULL; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_read.c000066400000000000000000000102651324272410200204440ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_read.c * \brief 9P version * * 9p_read.c : _9P_interpretor, request READ * * */ #include "config.h" #include #include #include #include #include "nfs_core.h" #include "log.h" #include "fsal.h" #include "9p.h" #include "server_stats.h" #include "client_mgr.h" int _9p_read(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; char *databuffer; u16 *msgtag = NULL; u32 *fid = NULL; u64 *offset = NULL; u32 *count = NULL; u32 outcount = 0; struct _9p_fid *pfid = NULL; size_t read_size = 0; bool eof_met; fsal_status_t fsal_status; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getptr(cursor, offset, u64); _9p_getptr(cursor, count, u32); LogDebug(COMPONENT_9P, "TREAD: tag=%u fid=%u offset=%llu count=%u", (u32) *msgtag, *fid, (unsigned long long)*offset, *count); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Make sure the requested amount of data respects negotiated msize */ if (*count + _9P_ROOM_RREAD > req9p->pconn->msize) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); /* Start building the reply already * So we don't need to use an intermediate data buffer */ _9p_setinitptr(cursor, preply, _9P_RREAD); _9p_setptr(cursor, msgtag, u16); databuffer = _9p_getbuffertofill(cursor); /* Do the job */ if (pfid->xattr != NULL) { /* Copy the value cached during xattrwalk */ if (*offset > pfid->xattr->xattr_size) return _9p_rerror(req9p, msgtag, EINVAL, plenout, preply); if (pfid->xattr->xattr_write != _9P_XATTR_READ_ONLY) return _9p_rerror(req9p, msgtag, EINVAL, plenout, preply); read_size = MIN(*count, pfid->xattr->xattr_size - *offset); memcpy(databuffer, pfid->xattr->xattr_content + *offset, read_size); outcount = read_size; } else { /* Call the new fsal_read */ fsal_status = fsal_read2(pfid->pentry, false, pfid->state, *offset, *count, &read_size, databuffer, &eof_met, NULL); /* Get the handle, for stats */ struct gsh_client *client = req9p->pconn->client; if (client == NULL) { LogDebug(COMPONENT_9P, "Cannot get client block for 9P request"); } else { op_ctx->client = client; server_stats_io_done(*count, read_size, FSAL_IS_ERROR(fsal_status), false); } if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); outcount = (u32) read_size; } _9p_setfilledbuffer(cursor, outcount); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RREAD: tag=%u fid=%u offset=%llu count=%u", (u32) *msgtag, *fid, (unsigned long long)*offset, *count); /** * @todo read statistics accounting goes here * modeled on nfs I/O statistics */ return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_read_conf.c000066400000000000000000000045631324272410200214550ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * @brief 9P protocol parameter tables * */ #include "config.h" #include "9p.h" #include "config_parsing.h" /* 9P parameters, settable in the 9P stanza. */ struct _9p_param _9p_param; static struct config_item _9p_params[] = { CONF_ITEM_UI16("_9P_TCP_Port", 1, UINT16_MAX, _9P_TCP_PORT, _9p_param, _9p_tcp_port), CONF_ITEM_UI16("_9P_RDMA_Port", 1, UINT16_MAX, _9P_RDMA_PORT, _9p_param, _9p_rdma_port), CONF_ITEM_UI32("_9P_TCP_Msize", 1024, UINT32_MAX, _9P_TCP_MSIZE, _9p_param, _9p_tcp_msize), CONF_ITEM_UI32("_9P_RDMA_Msize", 1024, UINT32_MAX, _9P_RDMA_MSIZE, _9p_param, _9p_rdma_msize), CONF_ITEM_UI16("_9P_RDMA_Backlog", 1, UINT16_MAX, _9P_RDMA_BACKLOG, _9p_param, _9p_rdma_backlog), CONF_ITEM_UI16("_9P_RDMA_Inpool_size", 1, UINT16_MAX, _9P_RDMA_INPOOL_SIZE, _9p_param, _9p_rdma_inpool_size), CONF_ITEM_UI16("_9P_RDMA_Outpool_Size", 1, UINT16_MAX, _9P_RDMA_OUTPOOL_SIZE, _9p_param, _9p_rdma_outpool_size), CONFIG_EOL }; static void *_9p_param_init(void *link_mem, void *self_struct) { if (self_struct == NULL) return &_9p_param; else return NULL; } struct config_block _9p_param_blk = { .dbus_interface_name = "org.ganesha.nfsd.config.9p", .blk_desc.name = "_9P", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = _9p_param_init, .blk_desc.u.blk.params = _9p_params, .blk_desc.u.blk.commit = noop_conf_commit }; nfs-ganesha-2.6.0/src/Protocols/9P/9p_readdir.c000066400000000000000000000171771324272410200211540ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_readdir.c * \brief 9P version * * 9p_readdir.c : _9P_interpretor, request READDIR * * */ #include "config.h" #include #include #include #include #include "nfs_core.h" #include "log.h" #include "fsal.h" #include "9p.h" #include "abstract_mem.h" char pathdot[] = "."; char pathdotdot[] = ".."; struct _9p_cb_entry { u64 qid_path; u8 *qid_type; char d_type; /* Attention, this is a VFS d_type, not a 9P type */ const char *name_str; u16 name_len; uint64_t cookie; }; struct _9p_cb_data { u8 *cursor; unsigned int count; unsigned int max; }; static inline u8 *fill_entry(u8 *cursor, u8 qid_type, u64 qid_path, u64 cookie, u8 d_type, u16 name_len, const char *name_str) { /* qid in 3 parts */ _9p_setvalue(cursor, qid_type, u8); /* 9P entry type */ /* qid_version set to 0 to prevent the client from caching */ _9p_setvalue(cursor, 0, u32); _9p_setvalue(cursor, qid_path, u64); /* offset */ _9p_setvalue(cursor, cookie, u64); /* Type (this time outside the qid) - VFS d_type (like in getdents) */ _9p_setvalue(cursor, d_type, u8); /* name */ _9p_setstr(cursor, name_len, name_str); return cursor; } static fsal_errors_t _9p_readdir_callback(void *opaque, struct fsal_obj_handle *obj, const struct attrlist *attr, uint64_t mounted_on_fileid, uint64_t cookie, enum cb_state cb_state) { struct fsal_readdir_cb_parms *cb_parms = opaque; struct _9p_cb_data *tracker = cb_parms->opaque; int name_len = strlen(cb_parms->name); u8 qid_type, d_type; if (tracker == NULL) { cb_parms->in_result = false; return ERR_FSAL_NO_ERROR; } if (tracker->count + 24 + name_len > tracker->max) { cb_parms->in_result = false; return ERR_FSAL_NO_ERROR; } switch (obj->type) { case FIFO_FILE: qid_type = _9P_QTFILE; d_type = DT_FIFO; break; case CHARACTER_FILE: qid_type = _9P_QTFILE; d_type = DT_CHR; break; case BLOCK_FILE: qid_type = _9P_QTFILE; d_type = DT_BLK; break; case REGULAR_FILE: qid_type = _9P_QTFILE; d_type = DT_REG; break; case SOCKET_FILE: qid_type = _9P_QTFILE; d_type = DT_SOCK; break; case DIRECTORY: qid_type = _9P_QTDIR; d_type = DT_DIR; break; case SYMBOLIC_LINK: qid_type = _9P_QTSYMLINK; d_type = DT_LNK; break; default: cb_parms->in_result = false; return ERR_FSAL_NO_ERROR; } /* Add 13 bytes in recsize for qid + 8 bytes for offset + 1 for type * + 2 for strlen = 24 bytes */ tracker->count += 24 + name_len; tracker->cursor = fill_entry(tracker->cursor, qid_type, obj->fileid, cookie, d_type, name_len, cb_parms->name); cb_parms->in_result = true; return ERR_FSAL_NO_ERROR; } int _9p_readdir(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; struct _9p_cb_data tracker; u16 *msgtag = NULL; u32 *fid = NULL; u64 *offset = NULL; u32 *count = NULL; u32 dcount = 0; char *dcount_pos = NULL; fsal_status_t fsal_status; bool eod_met; struct fsal_obj_handle *pentry_dot_dot = NULL; uint64_t cookie = 0LL; unsigned int num_entries = 0; struct _9p_fid *pfid = NULL; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getptr(cursor, offset, u64); _9p_getptr(cursor, count, u32); LogDebug(COMPONENT_9P, "TREADDIR: tag=%u fid=%u offset=%llu count=%u", (u32) *msgtag, *fid, (unsigned long long)*offset, *count); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Make sure the requested amount of data respects negotiated msize */ if (*count + _9P_ROOM_RREADDIR > req9p->pconn->msize) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); /* For each entry, returns: * qid = 13 bytes * offset = 8 bytes * type = 1 byte * namelen = 2 bytes * namestr = ~16 bytes (average size) * ------------------- * total = ~40 bytes (average size) per dentry */ if (*count < 52) /* require room for . and .. */ return _9p_rerror(req9p, msgtag, EIO, plenout, preply); /* Build the reply - it'll just be overwritten if error */ _9p_setinitptr(cursor, preply, _9P_RREADDIR); _9p_setptr(cursor, msgtag, u16); /* Remember dcount position for later use */ _9p_savepos(cursor, dcount_pos, u32); /* Is this the first request ? */ if (*offset == 0LL) { /* compute the parent entry */ fsal_status = fsal_lookupp(pfid->pentry, &pentry_dot_dot, NULL); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); /* Deal with "." and ".." */ cursor = fill_entry(cursor, _9P_QTDIR, pfid->pentry->fileid, 1LL, DT_DIR, strlen(pathdot), pathdot); dcount += 24 + strlen(pathdot); cursor = fill_entry(cursor, _9P_QTDIR, pentry_dot_dot->fileid, 2LL, DT_DIR, strlen(pathdotdot), pathdotdot); dcount += 24 + strlen(pathdotdot); /* put the parent */ pentry_dot_dot->obj_ops.put_ref(pentry_dot_dot); cookie = 0LL; } else if (*offset == 1LL) { /* compute the parent entry */ fsal_status = fsal_lookupp(pfid->pentry, &pentry_dot_dot, NULL); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); cursor = fill_entry(cursor, _9P_QTDIR, pentry_dot_dot->fileid, 2LL, DT_DIR, strlen(pathdotdot), pathdotdot); dcount += 24 + strlen(pathdotdot); /* put the parent */ pentry_dot_dot->obj_ops.put_ref(pentry_dot_dot); cookie = 0LL; } else if (*offset == 2LL) { cookie = 0LL; } else { cookie = (uint64_t) (*offset); } tracker.cursor = cursor; tracker.count = dcount; tracker.max = *count; fsal_status = fsal_readdir(pfid->pentry, cookie, &num_entries, &eod_met, 0, _9p_readdir_callback, &tracker); if (FSAL_IS_ERROR(fsal_status)) { /* The avl lookup will try to get the next entry after 'cookie'. * If none is found CACHE_INODE_NOT_FOUND is returned * In the 9P logic, this situation just mean * "end of directory reached" */ return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); } cursor = tracker.cursor; /* Set buffsize in previously saved position */ _9p_setvalue(dcount_pos, tracker.count, u32); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RREADDIR: tag=%u fid=%u dcount=%u", (u32) *msgtag, *fid, dcount); return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_readlink.c000066400000000000000000000052471324272410200213260ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_readlink.c * \brief 9P version * * 9p_readlink.c : _9P_interpretor, request ATTACH * * */ #include "config.h" #include #include #include #include #include "nfs_core.h" #include "log.h" #include "fsal.h" #include "9p.h" int _9p_readlink(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; struct _9p_fid *pfid = NULL; fsal_status_t fsal_status; struct gsh_buffdesc link_buffer = {.addr = NULL, .len = 0 }; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); LogDebug(COMPONENT_9P, "TREADLINK: tag=%u fid=%u", (u32) *msgtag, *fid); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); /* let's do the job */ fsal_status = fsal_readlink(pfid->pentry, &link_buffer); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RREADLINK); _9p_setptr(cursor, msgtag, u16); _9p_setstr(cursor, link_buffer.len - 1, link_buffer.addr); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RREADLINK: tag=%u fid=%u link=%s", *msgtag, (u32) *fid, (char *) link_buffer.addr); gsh_free(link_buffer.addr); return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_remove.c000066400000000000000000000066771324272410200210420ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_remove.c * \brief 9P version * * 9p_remove.c : _9P_interpretor, request ATTACH * * */ #include "config.h" #include #include #include #include "nfs_core.h" #include "nfs_exports.h" #include "log.h" #include "fsal.h" #include "9p.h" #define FREE_FID(pfid, fid, req9p) do { \ /* mark object no longer reachable */ \ pfid->pentry->obj_ops.put_ref(pfid->pentry); \ pfid->pentry = NULL; \ /* Free the fid */ \ free_fid(pfid); \ req9p->pconn->fids[*fid] = NULL; \ } while (0) int _9p_remove(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; struct _9p_fid *pfid = NULL; fsal_status_t fsal_status; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); LogDebug(COMPONENT_9P, "TREMOVE: tag=%u fid=%u", (u32) *msgtag, *fid); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); fsal_status = fsal_remove(pfid->ppentry, pfid->name); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); /* If object is an opened file, close it */ if ((pfid->pentry->type == REGULAR_FILE) && pfid->opens) { pfid->opens = 0; /* dead */ fsal_status = pfid->pentry->obj_ops.close2(pfid->pentry, pfid->state); if (FSAL_IS_ERROR(fsal_status)) { FREE_FID(pfid, fid, req9p); return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); } } /* Clean the fid */ FREE_FID(pfid, fid, req9p); /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RREMOVE); _9p_setptr(cursor, msgtag, u16); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "TREMOVE: tag=%u fid=%u", (u32) *msgtag, *fid); /* _9p_stat_update( *pmsgtype, TRUE, &pwkrdata->stats._9p_stat_req); */ return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_rename.c000066400000000000000000000076341324272410200210060ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_rename.c * \brief 9P version * * 9p_rename.c : _9P_interpretor, request ATTACH * * */ #include "config.h" #include #include #include #include "nfs_core.h" #include "nfs_exports.h" #include "log.h" #include "fsal.h" #include "9p.h" int _9p_rename(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u32 *dfid = NULL; u16 *name_len = NULL; char *name_str = NULL; /* for unused-but-set-variable */ struct _9p_fid *pfid = NULL; struct _9p_fid *pdfid = NULL; char newname[MAXNAMLEN+1]; fsal_status_t fsal_status; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getptr(cursor, dfid, u32); _9p_getstr(cursor, name_len, name_str); LogDebug(COMPONENT_9P, "TRENAME: tag=%u fid=%u dfid=%u name=%.*s", (u32) *msgtag, *fid, *dfid, *name_len, name_str); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); if (*dfid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pdfid = req9p->pconn->fids[*dfid]; /* Check that it is a valid fid */ if (pdfid == NULL || pdfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *dfid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } /* Check that pfid and pdfid are in the same export. */ if (pfid->export != NULL && pdfid->export != NULL && pfid->export->export_id != pdfid->export->export_id) { LogDebug(COMPONENT_9P, "request on fid=%u and dfid=%u crosses exports", *fid, *dfid); return _9p_rerror(req9p, msgtag, EXDEV, plenout, preply); } if (*name_len >= sizeof(newname)) { LogDebug(COMPONENT_9P, "request with name too long (%u)", *name_len); return _9p_rerror(req9p, msgtag, ENAMETOOLONG, plenout, preply); } snprintf(newname, sizeof(newname), "%.*s", *name_len, name_str); fsal_status = fsal_rename(pfid->ppentry, pfid->name, pdfid->pentry, newname); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RRENAME); _9p_setptr(cursor, msgtag, u16); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RRENAMEAT: tag=%u fid=%u dfid=%u newname=%.*s", (u32) *msgtag, *fid, *dfid, *name_len, name_str); /* _9p_stat_update(*pmsgtype, TRUE, &pwkrdata->stats._9p_stat_req); */ return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_renameat.c000066400000000000000000000107541324272410200213300ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_renameat.c * \brief 9P version * * 9p_renameat.c : _9P_interpretor, request ATTACH * * */ #include "config.h" #include #include #include #include "nfs_core.h" #include "nfs_exports.h" #include "log.h" #include "fsal.h" #include "9p.h" int _9p_renameat(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *oldfid = NULL; u16 *oldname_len = NULL; char *oldname_str = NULL; u32 *newfid = NULL; u16 *newname_len = NULL; char *newname_str = NULL; struct _9p_fid *poldfid = NULL; struct _9p_fid *pnewfid = NULL; fsal_status_t fsal_status; char oldname[MAXNAMLEN+1]; char newname[MAXNAMLEN+1]; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, oldfid, u32); _9p_getstr(cursor, oldname_len, oldname_str); _9p_getptr(cursor, newfid, u32); _9p_getstr(cursor, newname_len, newname_str); LogDebug(COMPONENT_9P, "TRENAMEAT: tag=%u oldfid=%u oldname=%.*s newfid=%u newname=%.*s", (u32) *msgtag, *oldfid, *oldname_len, oldname_str, *newfid, *newname_len, newname_str); if (*oldfid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); poldfid = req9p->pconn->fids[*oldfid]; /* Check that it is a valid fid */ if (poldfid == NULL || poldfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *oldfid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(poldfid, req9p); if (*newfid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pnewfid = req9p->pconn->fids[*newfid]; /* Check that it is a valid fid */ if (pnewfid == NULL || pnewfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *newfid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } /* Check that poldfid and pnewfid are in the same export. */ if (poldfid->export != NULL && pnewfid->export != NULL && poldfid->export->export_id != pnewfid->export->export_id) { LogDebug(COMPONENT_9P, "request on oldfid=%u and newfid=%u crosses exports", *oldfid, *newfid); return _9p_rerror(req9p, msgtag, EXDEV, plenout, preply); } if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); /* Let's do the job */ if (*oldname_len >= sizeof(oldname)) { LogDebug(COMPONENT_9P, "request with names too long (%u or %u)", *oldname_len, *newname_len); return _9p_rerror(req9p, msgtag, ENAMETOOLONG, plenout, preply); } snprintf(oldname, sizeof(oldname), "%.*s", *oldname_len, oldname_str); if (*newname_len >= sizeof(newname)) { LogDebug(COMPONENT_9P, "request with names too long (%u or %u)", *oldname_len, *newname_len); return _9p_rerror(req9p, msgtag, ENAMETOOLONG, plenout, preply); } snprintf(newname, sizeof(newname), "%.*s", *newname_len, newname_str); fsal_status = fsal_rename(poldfid->pentry, oldname, pnewfid->pentry, newname); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RRENAMEAT); _9p_setptr(cursor, msgtag, u16); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RRENAMEAT: tag=%u oldfid=%u oldname=%.*s newfid=%u newname=%.*s", (u32) *msgtag, *oldfid, *oldname_len, oldname_str, *newfid, *newname_len, newname_str); return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_rerror.c000066400000000000000000000036761324272410200210540ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_rerror.c * \brief 9P version * * 9p_rerror.c : _9P_interpretor, request RERROR * * */ #include "config.h" #include #include #include #include "nfs_core.h" #include "log.h" #include "9p.h" int _9p_rerror(struct _9p_request_data *req9p, u16 *msgtag, u32 err, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u8 msgtype = *(req9p->_9pmsg + _9P_HDR_SIZE); /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RERROR); _9p_setptr(cursor, msgtag, u16); _9p_setvalue(cursor, err, u32); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); /* Check boundaries. 0 is no_function fallback */ if (msgtype < _9P_TSTATFS || msgtype > _9P_TWSTAT || _9pfuncdesc[msgtype].service_function == NULL) msgtype = 0; LogDebug(COMPONENT_9P, "RERROR(%s) tag=%u err=(%u|%s)", _9pfuncdesc[msgtype].funcname, *msgtag, err, strerror(err)); return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_setattr.c000066400000000000000000000130261324272410200212150ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_setattr.c * \brief 9P version * * 9p_setattr.c : _9P_interpretor, request SETATTR * * */ #include "config.h" #include #include #include #include #include "nfs_core.h" #include "nfs_exports.h" #include "log.h" #include "fsal.h" #include "9p.h" int _9p_setattr(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u32 *valid = NULL; u32 *mode = NULL; u32 *uid = NULL; u32 *gid = NULL; u64 *size = NULL; u64 *atime_sec = NULL; u64 *atime_nsec = NULL; u64 *mtime_sec = NULL; u64 *mtime_nsec = NULL; struct _9p_fid *pfid = NULL; struct attrlist fsalattr; fsal_status_t fsal_status; struct timeval t; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getptr(cursor, valid, u32); _9p_getptr(cursor, mode, u32); _9p_getptr(cursor, uid, u32); _9p_getptr(cursor, gid, u32); _9p_getptr(cursor, size, u64); _9p_getptr(cursor, atime_sec, u64); _9p_getptr(cursor, atime_nsec, u64); _9p_getptr(cursor, mtime_sec, u64); _9p_getptr(cursor, mtime_nsec, u64); LogDebug(COMPONENT_9P, "TSETATTR: tag=%u fid=%u valid=0x%x mode=0%o uid=%u gid=%u size=%" PRIu64 " atime=(%llu|%llu) mtime=(%llu|%llu)", (u32) *msgtag, *fid, *valid, *mode, *uid, *gid, *size, (unsigned long long)*atime_sec, (unsigned long long)*atime_nsec, (unsigned long long)*mtime_sec, (unsigned long long)*mtime_nsec); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); /* If a "time" change is required, but not with the "_set" suffix, * use gettimeofday */ if (*valid & (_9P_SETATTR_ATIME | _9P_SETATTR_CTIME | _9P_SETATTR_MTIME)) { if (gettimeofday(&t, NULL) == -1) { LogMajor(COMPONENT_9P, "TSETATTR: tag=%u fid=%u ERROR !! gettimeofday returned -1 with errno=%u", (u32) *msgtag, *fid, errno); return _9p_rerror(req9p, msgtag, errno, plenout, preply); } } /* Let's do the job */ memset((char *)&fsalattr, 0, sizeof(fsalattr)); if (*valid & _9P_SETATTR_MODE) { fsalattr.valid_mask |= ATTR_MODE; fsalattr.mode = *mode; } if (*valid & _9P_SETATTR_UID) { fsalattr.valid_mask |= ATTR_OWNER; fsalattr.owner = *uid; } if (*valid & _9P_SETATTR_GID) { fsalattr.valid_mask |= ATTR_GROUP; fsalattr.group = *gid; } if (*valid & _9P_SETATTR_SIZE) { fsalattr.valid_mask |= ATTR_SIZE; fsalattr.filesize = *size; } if (*valid & _9P_SETATTR_ATIME) { fsalattr.valid_mask |= ATTR_ATIME; fsalattr.atime.tv_sec = t.tv_sec; fsalattr.atime.tv_nsec = t.tv_usec * 1000; } if (*valid & _9P_SETATTR_MTIME) { fsalattr.valid_mask |= ATTR_MTIME; fsalattr.mtime.tv_sec = t.tv_sec; fsalattr.mtime.tv_nsec = t.tv_usec * 1000; } if (*valid & _9P_SETATTR_CTIME) { fsalattr.valid_mask |= ATTR_CTIME; fsalattr.ctime.tv_sec = t.tv_sec; fsalattr.ctime.tv_nsec = t.tv_usec * 1000; } if (*valid & _9P_SETATTR_ATIME_SET) { fsalattr.valid_mask |= ATTR_ATIME; fsalattr.atime.tv_sec = *atime_sec; fsalattr.atime.tv_nsec = *atime_nsec; } if (*valid & _9P_SETATTR_MTIME_SET) { fsalattr.valid_mask |= ATTR_MTIME; fsalattr.mtime.tv_sec = *mtime_sec; fsalattr.mtime.tv_nsec = *mtime_nsec; } /* Now set the attr */ fsal_status = fsal_setattr(pfid->pentry, false, pfid->state, &fsalattr); /* Release the attributes (may release an inherited ACL) */ fsal_release_attrs(&fsalattr); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RSETATTR); _9p_setptr(cursor, msgtag, u16); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RSETATTR: tag=%u fid=%u valid=0x%x mode=0%o uid=%u gid=%u size=%" PRIu64 " atime=(%llu|%llu) mtime=(%llu|%llu)", (u32) *msgtag, *fid, *valid, *mode, *uid, *gid, *size, (unsigned long long)*atime_sec, (unsigned long long)*atime_nsec, (unsigned long long)*mtime_sec, (unsigned long long)*mtime_nsec); /* _9p_stat_update(*pmsgtype, TRUE, &pwkrdata->stats._9p_stat_req); */ return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_statfs.c000066400000000000000000000072271324272410200210410ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_statfs.c * \brief 9P version * * 9p_statfs.c : _9P_interpretor, request ATTACH * * */ #include "config.h" #include #include #include #include #include "nfs_core.h" #include "log.h" #include "fsal.h" #include "9p.h" int _9p_statfs(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; struct _9p_fid *pfid = NULL; u32 type = 0x01021997; /* V9FS_MAGIC */ u32 bsize = 1; /* fsal_statfs and * FSAL already care for blocksize */ u64 *blocks = NULL; u64 *bfree = NULL; u64 *bavail = NULL; u64 *files = NULL; u64 *ffree = NULL; u64 fsid = 0LL; u32 namelen = MAXNAMLEN; fsal_dynamicfsinfo_t dynamicinfo; fsal_status_t fsal_status; struct attrlist attrs; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); LogDebug(COMPONENT_9P, "TSTATFS: tag=%u fid=%u", (u32) *msgtag, *fid); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; if (pfid == NULL) return _9p_rerror(req9p, msgtag, EINVAL, plenout, preply); _9p_init_opctx(pfid, req9p); /* Get the obj's attributes */ fsal_prepare_attrs(&attrs, ATTRS_NFS3); fsal_status = pfid->pentry->obj_ops.getattrs(pfid->pentry, &attrs); if (FSAL_IS_ERROR(fsal_status)) { /* Done with the attrs */ fsal_release_attrs(&attrs); return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); } /* Get the FS's stats */ fsal_status = fsal_statfs(pfid->pentry, &dynamicinfo); if (FSAL_IS_ERROR(fsal_status)) { /* Done with the attrs */ fsal_release_attrs(&attrs); return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); } blocks = (u64 *) &dynamicinfo.total_bytes; bfree = (u64 *) &dynamicinfo.free_bytes; bavail = (u64 *) &dynamicinfo.avail_bytes; files = (u64 *) &dynamicinfo.total_files; ffree = (u64 *) &dynamicinfo.free_files; fsid = (u64) attrs.rawdev.major; /* Done with the attrs */ fsal_release_attrs(&attrs); /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RSTATFS); _9p_setptr(cursor, msgtag, u16); _9p_setvalue(cursor, type, u32); _9p_setvalue(cursor, bsize, u32); _9p_setptr(cursor, blocks, u64); _9p_setptr(cursor, bfree, u64); _9p_setptr(cursor, bavail, u64); _9p_setptr(cursor, files, u64); _9p_setptr(cursor, ffree, u64); _9p_setvalue(cursor, fsid, u64); _9p_setvalue(cursor, namelen, u32); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RSTATFS: tag=%u fid=%u", (u32) *msgtag, *fid); return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_symlink.c000066400000000000000000000105611324272410200212160ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_symlink.c * \brief 9P version * * 9p_symlink.c : _9P_interpretor, request SYMLINK * * */ #include "config.h" #include #include #include #include #include "nfs_core.h" #include "nfs_exports.h" #include "log.h" #include "fsal.h" #include "9p.h" int _9p_symlink(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u16 *name_len = NULL; char *name_str = NULL; u16 *linkcontent_len = NULL; char *linkcontent_str = NULL; u32 *gid = NULL; struct _9p_fid *pfid = NULL; struct _9p_qid qid_symlink; struct fsal_obj_handle *pentry_symlink = NULL; char symlink_name[MAXNAMLEN+1]; char *link_content = NULL; fsal_status_t fsal_status; uint32_t mode = 0777; struct attrlist object_attributes; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getstr(cursor, name_len, name_str); _9p_getstr(cursor, linkcontent_len, linkcontent_str); _9p_getptr(cursor, gid, u32); LogDebug(COMPONENT_9P, "TSYMLINK: tag=%u fid=%u name=%.*s linkcontent=%.*s gid=%u", (u32) *msgtag, *fid, *name_len, name_str, *linkcontent_len, linkcontent_str, *gid); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); if (*name_len >= sizeof(symlink_name)) { LogDebug(COMPONENT_9P, "request with name too long (%u)", *name_len); return _9p_rerror(req9p, msgtag, ENAMETOOLONG, plenout, preply); } snprintf(symlink_name, sizeof(symlink_name), "%.*s", *name_len, name_str); link_content = gsh_malloc(*linkcontent_len + 1); memcpy(link_content, linkcontent_str, *linkcontent_len); link_content[*linkcontent_len] = '\0'; fsal_prepare_attrs(&object_attributes, ATTR_MODE); object_attributes.mode = mode; object_attributes.valid_mask = ATTR_MODE; /* Let's do the job */ /* BUGAZOMEU: @todo : the gid parameter is not used yet, * flags is not yet used */ fsal_status = fsal_create(pfid->pentry, symlink_name, SYMBOLIC_LINK, &object_attributes, link_content, &pentry_symlink, NULL); /* Release the attributes (may release an inherited ACL) */ fsal_release_attrs(&object_attributes); gsh_free(link_content); if (pentry_symlink == NULL) { return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); } pentry_symlink->obj_ops.put_ref(pentry_symlink); /* Build the qid */ qid_symlink.type = _9P_QTSYMLINK; qid_symlink.version = 0; qid_symlink.path = pentry_symlink->fileid; /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RSYMLINK); _9p_setptr(cursor, msgtag, u16); _9p_setqid(cursor, qid_symlink); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RSYMLINK: tag=%u fid=%u name=%.*s qid=(type=%u,version=%u,path=%llu)", (u32) *msgtag, *fid, *name_len, name_str, qid_symlink.type, qid_symlink.version, (unsigned long long)qid_symlink.path); return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_unlinkat.c000066400000000000000000000062121324272410200213530ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_unlinkat.c * \brief 9P version * * 9p_unlinkat.c : _9P_interpretor, request ATTACH * * */ #include "config.h" #include #include #include #include "nfs_core.h" #include "nfs_exports.h" #include "log.h" #include "fsal.h" #include "9p.h" int _9p_unlinkat(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *dfid = NULL; u16 *name_len = NULL; char *name_str = NULL; /* flags are not used */ __attribute__ ((unused)) u32 *flags = NULL; struct _9p_fid *pdfid = NULL; fsal_status_t fsal_status; char name[MAXNAMLEN+1]; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, dfid, u32); _9p_getstr(cursor, name_len, name_str); _9p_getptr(cursor, flags, u32); LogDebug(COMPONENT_9P, "TUNLINKAT: tag=%u dfid=%u name=%.*s", (u32) *msgtag, *dfid, *name_len, name_str); if (*dfid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pdfid = req9p->pconn->fids[*dfid]; /* Check that it is a valid fid */ if (pdfid == NULL || pdfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *dfid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pdfid, req9p); if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); /* Let's do the job */ if (*name_len >= sizeof(name)) { LogDebug(COMPONENT_9P, "request with name too long (%u)", *name_len); return _9p_rerror(req9p, msgtag, ENAMETOOLONG, plenout, preply); } snprintf(name, sizeof(name), "%.*s", *name_len, name_str); fsal_status = fsal_remove(pdfid->pentry, name); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RUNLINKAT); _9p_setptr(cursor, msgtag, u16); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "TUNLINKAT: tag=%u dfid=%u name=%.*s", (u32) *msgtag, *dfid, *name_len, name_str); return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_version.c000066400000000000000000000053331324272410200212160ustar00rootroot00000000000000 /* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_version.c * \brief 9P version * * 9p_version.c : _9P_interpretor, request VERSION * * */ #include "config.h" #include #include #include #include "nfs_core.h" #include "log.h" #include "fsal.h" #include "9p.h" static const char version_9p200l[] = "9P2000.L"; int _9p_version(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *msize = NULL; u16 *version_len = NULL; char *version_str = NULL; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, msize, u32); _9p_getstr(cursor, version_len, version_str); LogDebug(COMPONENT_9P, "TVERSION: tag=%u msize=%u version='%.*s'", (u32) *msgtag, *msize, (int)*version_len, version_str); if (strncmp(version_str, version_9p200l, *version_len)) { LogEvent(COMPONENT_9P, "RVERSION: BAD VERSION"); return _9p_rerror(req9p, msgtag, ENOENT, plenout, preply); } if (req9p->pconn->msize < *msize) *msize = req9p->pconn->msize; else req9p->pconn->msize = *msize; LogDebug(COMPONENT_9P, "Negotiated msize is %u", *msize); /* A too small msize would result in buffer overflows on calls * such as STAT. Make sure it is not ridiculously low. */ if (*msize < 512) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); /* Good version, build the reply */ _9p_setinitptr(cursor, preply, _9P_RVERSION); _9p_setptr(cursor, msgtag, u16); _9p_setptr(cursor, msize, u32); _9p_setstr(cursor, *version_len, version_str); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RVERSION: msize=%u version='%.*s'", *msize, (int)*version_len, version_str); return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_walk.c000066400000000000000000000144001324272410200204620ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_walk.c * \brief 9P version * * 9p_walk.c : _9P_interpretor, request WALK * * */ #include "config.h" #include #include #include #include #include "nfs_core.h" #include "log.h" #include "fsal.h" #include "9p.h" int _9p_walk(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; unsigned int i = 0; u16 *msgtag = NULL; u32 *fid = NULL; u32 *newfid = NULL; u16 *nwname = NULL; u16 *wnames_len; char *wnames_str; fsal_status_t fsal_status; struct fsal_obj_handle *pentry = NULL; char name[MAXNAMLEN+1]; u16 *nwqid; struct _9p_fid *pfid = NULL; struct _9p_fid *pnewfid = NULL; /* Now Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getptr(cursor, newfid, u32); _9p_getptr(cursor, nwname, u16); LogDebug(COMPONENT_9P, "TWALK: tag=%u fid=%u newfid=%u nwname=%u", (u32) *msgtag, *fid, *newfid, *nwname); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); if (*newfid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); pnewfid = gsh_calloc(1, sizeof(struct _9p_fid)); /* Is this a lookup or a fid cloning operation ? */ if (*nwname == 0) { /* Cloning operation */ memcpy((char *)pnewfid, (char *)pfid, sizeof(struct _9p_fid)); /* Set the new fid id */ pnewfid->fid = *newfid; /* Increments refcount */ pnewfid->pentry->obj_ops.get_ref(pnewfid->pentry); } else { /* the walk is in fact a lookup */ pentry = pfid->pentry; for (i = 0; i < *nwname; i++) { _9p_getstr(cursor, wnames_len, wnames_str); if (*wnames_len >= sizeof(name)) { gsh_free(pnewfid); return _9p_rerror(req9p, msgtag, ENAMETOOLONG, plenout, preply); } snprintf(name, sizeof(name), "%.*s", *wnames_len, wnames_str); LogDebug(COMPONENT_9P, "TWALK (lookup): tag=%u fid=%u newfid=%u (component %u/%u :%s)", (u32) *msgtag, *fid, *newfid, i + 1, *nwname, name); if (pnewfid->pentry == pentry) pnewfid->pentry = NULL; /* refcount +1 */ fsal_status = fsal_lookup(pentry, name, &pnewfid->pentry, NULL); if (FSAL_IS_ERROR(fsal_status)) { gsh_free(pnewfid); return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); } if (pentry != pfid->pentry) pentry->obj_ops.put_ref(pentry); pentry = pnewfid->pentry; } pnewfid->fid = *newfid; pnewfid->ppentry = pfid->pentry; strncpy(pnewfid->name, name, MAXNAMLEN); /* gdata ref is not hold : the pfid, which use same gdata */ /* will be clunked after pnewfid */ /* This clunk release the gdata */ pnewfid->gdata = pfid->gdata; /* Refcounted object (incremented at the end of the function, * if there was no errors). */ pnewfid->export = pfid->export; pnewfid->ucred = pfid->ucred; /* Build the qid */ /* No cache, we want the client to stay synchronous * with the server */ pnewfid->qid.version = 0; pnewfid->qid.path = pnewfid->pentry->fileid; pnewfid->xattr = NULL; switch (pnewfid->pentry->type) { case REGULAR_FILE: case CHARACTER_FILE: case BLOCK_FILE: case SOCKET_FILE: case FIFO_FILE: pnewfid->qid.type = _9P_QTFILE; break; case SYMBOLIC_LINK: pnewfid->qid.type = _9P_QTSYMLINK; break; case DIRECTORY: pnewfid->qid.type = _9P_QTDIR; break; default: LogMajor(COMPONENT_9P, "implementation error, you should not see this message !!!!!!"); pentry->obj_ops.put_ref(pentry); gsh_free(pnewfid); return _9p_rerror(req9p, msgtag, EINVAL, plenout, preply); break; } } /* Initialize state_t embeded in fid. The refcount is initialized * to one to represent the state_t being embeded in the fid. This * prevents it from ever being reduced to zero by dec_state_t_ref. */ pnewfid->state = pnewfid->export->fsal_export->exp_ops.alloc_state( pnewfid->export->fsal_export, STATE_TYPE_9P_FID, NULL); glist_init(&pnewfid->state->state_data.fid.state_locklist); pnewfid->state->state_refcount = 1; /* keep info on new fid */ req9p->pconn->fids[*newfid] = pnewfid; /* As much qid as requested fid */ nwqid = nwname; /* Increment refcounters. */ uid2grp_hold_group_data(pnewfid->gdata); get_9p_user_cred_ref(pnewfid->ucred); get_gsh_export_ref(pnewfid->export); if (pnewfid->ppentry != NULL) { /* Increments refcount for ppentry */ pnewfid->ppentry->obj_ops.get_ref(pnewfid->ppentry); } /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RWALK); _9p_setptr(cursor, msgtag, u16); _9p_setptr(cursor, nwqid, u16); for (i = 0; i < *nwqid; i++) { /** @todo: should be different qids * for each directory walked through */ _9p_setqid(cursor, pnewfid->qid); } _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RWALK: tag=%u fid=%u newfid=%u nwqid=%u fileid=%llu pentry=%p", (u32) *msgtag, *fid, *newfid, *nwqid, (unsigned long long)pnewfid->qid.path, pnewfid->pentry); return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_write.c000066400000000000000000000115361324272410200206650ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_write.c * \brief 9P version * * 9p_write.c : _9P_interpretor, request WRITE * * */ #include "config.h" #include #include #include #include #include "nfs_core.h" #include "nfs_exports.h" #include "log.h" #include "fsal.h" #include "9p.h" #include "server_stats.h" #include "client_mgr.h" int _9p_write(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u64 *offset = NULL; u32 *count = NULL; u32 outcount = 0; struct _9p_fid *pfid = NULL; size_t size; size_t written_size = 0; fsal_status_t fsal_status; /* bool sync = true; */ bool sync = false; char *databuffer = NULL; /* fsal_status_t fsal_status; */ /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getptr(cursor, offset, u64); _9p_getptr(cursor, count, u32); databuffer = cursor; LogDebug(COMPONENT_9P, "TWRITE: tag=%u fid=%u offset=%llu count=%u", (u32) *msgtag, *fid, (unsigned long long)*offset, *count); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Make sure the requested amount of data respects negotiated msize */ if (*count + _9P_ROOM_TWRITE > req9p->pconn->msize) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); /* Do the job */ size = *count; if (pfid->xattr != NULL) { if (*offset > pfid->xattr->xattr_size) return _9p_rerror(req9p, msgtag, EINVAL, plenout, preply); if (pfid->xattr->xattr_write != _9P_XATTR_CAN_WRITE && pfid->xattr->xattr_write != _9P_XATTR_DID_WRITE) return _9p_rerror(req9p, msgtag, EINVAL, plenout, preply); written_size = MIN(*count, pfid->xattr->xattr_size - *offset); memcpy(pfid->xattr->xattr_content + *offset, databuffer, written_size); pfid->xattr->xattr_offset += size; pfid->xattr->xattr_write = _9P_XATTR_DID_WRITE; /* ADD CODE TO DETECT GAP */ #if 0 fsal_status = pfid->pentry->ops->setextattr_value_by_id( pfid->pentry, &pfid->op_context, pfid->xattr->xattr_id, xattrval, size + 1); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); #endif outcount = written_size; } else { /* Call the new fsal_write */ fsal_status = fsal_write2(pfid->pentry, false, pfid->state, *offset, size, &written_size, databuffer, &sync, NULL); /* Get the handle, for stats */ struct gsh_client *client = req9p->pconn->client; if (client == NULL) { LogDebug(COMPONENT_9P, "Cannot get client block for 9P request"); } else { op_ctx->client = client; server_stats_io_done(size, written_size, FSAL_IS_ERROR(fsal_status), true); } if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); outcount = (u32) written_size; } /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RWRITE); _9p_setptr(cursor, msgtag, u16); _9p_setvalue(cursor, outcount, u32); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RWRITE: tag=%u fid=%u offset=%llu input count=%u output count=%u", (u32) *msgtag, *fid, (unsigned long long)*offset, *count, outcount); /** * @todo write statistics accounting goes here * modeled on nfs I/O stats */ return 1; } nfs-ganesha-2.6.0/src/Protocols/9P/9p_xattrcreate.c000066400000000000000000000123661324272410200220630ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_xattrcreate.c * \brief 9P version * * 9p_xattrcreate.c : _9P_interpretor, request XATTRCREATE * * */ #include "config.h" #include #include #include #include #include #include #include "nfs_core.h" #include "nfs_exports.h" #include "log.h" #include "fsal.h" #include "9p.h" int _9p_xattrcreate(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; int create; u16 *msgtag = NULL; u32 *fid = NULL; u64 *size; u32 *flag; u16 *name_len; char *name_str; struct _9p_fid *pfid = NULL; fsal_status_t fsal_status = { .major = ERR_FSAL_NO_ERROR, .minor = 0 }; char name[MAXNAMLEN+1]; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getstr(cursor, name_len, name_str); _9p_getptr(cursor, size, u64); _9p_getptr(cursor, flag, u32); LogDebug(COMPONENT_9P, "TXATTRCREATE: tag=%u fid=%u name=%.*s size=%llu flag=%u", (u32) *msgtag, *fid, *name_len, name_str, (unsigned long long)*size, *flag); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); if (*size > _9P_XATTR_MAX_SIZE) return _9p_rerror(req9p, msgtag, ENOSPC, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } /* set op_ctx, it will be useful if FSAL is later called */ _9p_init_opctx(pfid, req9p); if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); if (*name_len >= sizeof(name)) { LogDebug(COMPONENT_9P, "request with name too long (%u)", *name_len); return _9p_rerror(req9p, msgtag, ENAMETOOLONG, plenout, preply); } snprintf(name, sizeof(name), "%.*s", *name_len, name_str); if (*size == 0LL) { /* Size == 0 : this is in fact a call to removexattr */ LogDebug(COMPONENT_9P, "TXATTRCREATE: tag=%u fid=%u : will remove xattr %s", (u32) *msgtag, *fid, name); fsal_status = pfid->pentry->obj_ops.remove_extattr_by_name(pfid->pentry, name); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); } else { /* Size != 0 , this is a creation/replacement of xattr */ /* Create the xattr at the FSAL level and cache result */ pfid->xattr = gsh_malloc(sizeof(*pfid->xattr) + *size); pfid->xattr->xattr_size = *size; pfid->xattr->xattr_offset = 0LL; pfid->xattr->xattr_write = _9P_XATTR_CAN_WRITE; strncpy(pfid->xattr->xattr_name, name, MAXNAMLEN); /* /!\ POSIX_ACL RELATED HOOK * Setting a POSIX ACL (using setfacl for example) means * settings a xattr named system.posix_acl_access BUT this * attribute is to be used and should not be created * (it exists already since acl feature is on) */ if (!strncmp(name, "system.posix_acl_access", MAXNAMLEN)) goto skip_create; /* try to create if flag doesn't have REPLACE bit */ if ((*flag & XATTR_REPLACE) == 0) create = true; else create = false; fsal_status = pfid->pentry->obj_ops.setextattr_value(pfid->pentry, name, pfid->xattr->xattr_content, *size, create); /* Try again with create = false if flag was set to 0 * and create failed because attribute already exists */ if (FSAL_IS_ERROR(fsal_status) && fsal_status.major == ERR_FSAL_EXIST && (*flag == 0)) { fsal_status = pfid->pentry->obj_ops.setextattr_value(pfid->pentry, name, pfid->xattr->xattr_content, *size, false); } if (FSAL_IS_ERROR(fsal_status)) { gsh_free(pfid->xattr); return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); } } skip_create: /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RXATTRCREATE); _9p_setptr(cursor, msgtag, u16); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RXATTRCREATE: tag=%u fid=%u name=%.*s size=%llu flag=%u", (u32) *msgtag, *fid, *name_len, name_str, (unsigned long long)*size, *flag); return 1; } /* _9p_xattrcreate */ nfs-ganesha-2.6.0/src/Protocols/9P/9p_xattrwalk.c000066400000000000000000000170341324272410200215530ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file 9p_xattrwalk.c * \brief 9P version * * 9p_xattrwalk.c : _9P_interpretor, request XATTRWALK * * */ #include "config.h" #include #include #include #include #include #include #include "nfs_core.h" #include "log.h" #include "fsal.h" #include "9p.h" #define XATTRS_ARRAY_LEN 100 int _9p_xattrwalk(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u32 *attrfid = NULL; u16 *name_len; char *name_str; size_t attrsize = 0; fsal_status_t fsal_status; char name[MAXNAMLEN+1]; fsal_xattrent_t xattrs_arr[XATTRS_ARRAY_LEN]; int eod_met = false; unsigned int nb_xattrs_read = 0; unsigned int i = 0; char *xattr_cursor = NULL; unsigned int tmplen = 0; struct _9p_fid *pfid = NULL; struct _9p_fid *pxattrfid = NULL; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getptr(cursor, attrfid, u32); LogDebug(COMPONENT_9P, "TXATTRWALK: tag=%u fid=%u attrfid=%u", (u32) *msgtag, *fid, *attrfid); _9p_getstr(cursor, name_len, name_str); if (*name_len == 0) LogDebug(COMPONENT_9P, "TXATTRWALK (component): tag=%u fid=%u attrfid=%u name=(LIST XATTR)", (u32) *msgtag, *fid, *attrfid); else LogDebug(COMPONENT_9P, "TXATTRWALK (component): tag=%u fid=%u attrfid=%u name=%.*s", (u32) *msgtag, *fid, *attrfid, *name_len, name_str); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); if (*attrfid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } if (*name_len >= sizeof(name)) { LogDebug(COMPONENT_9P, "request with name too long (%u)", *name_len); return _9p_rerror(req9p, msgtag, ENAMETOOLONG, plenout, preply); } pxattrfid = gsh_calloc(1, sizeof(struct _9p_fid)); /* set op_ctx, it will be useful if FSAL is later called */ _9p_init_opctx(pfid, req9p); /* Initiate xattr's fid by copying file's fid in it. * Don't copy the state_t pointer. */ memcpy((char *)pxattrfid, (char *)pfid, sizeof(struct _9p_fid)); pxattrfid->state = NULL; snprintf(name, sizeof(name), "%.*s", *name_len, name_str); pxattrfid->xattr = gsh_malloc(sizeof(*pxattrfid->xattr) + XATTR_BUFFERSIZE); if (*name_len == 0) { /* xattrwalk is used with an empty name, * this is a listxattr request */ fsal_status = pxattrfid->pentry->obj_ops.list_ext_attrs(pxattrfid->pentry, FSAL_XATTR_RW_COOKIE, /* Start with RW cookie, * hiding RO ones */ xattrs_arr, XATTRS_ARRAY_LEN, /** @todo fix static length */ &nb_xattrs_read, &eod_met); if (FSAL_IS_ERROR(fsal_status)) { gsh_free(pxattrfid->xattr); gsh_free(pxattrfid); return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); } /* if all xattrent are not read, * returns ERANGE as listxattr does */ if (eod_met != true) { gsh_free(pxattrfid->xattr); gsh_free(pxattrfid); return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); } xattr_cursor = pxattrfid->xattr->xattr_content; attrsize = 0; for (i = 0; i < nb_xattrs_read; i++) { tmplen = strnlen(xattrs_arr[i].xattr_name, MAXNAMLEN); /* Make sure not to go beyond the buffer * @todo realloc here too */ if (attrsize + tmplen + 1 > XATTR_BUFFERSIZE) { gsh_free(pxattrfid->xattr); gsh_free(pxattrfid); return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); } memcpy(xattr_cursor, xattrs_arr[i].xattr_name, tmplen); xattr_cursor[tmplen] = '\0'; /* +1 for trailing '\0' */ xattr_cursor += tmplen + 1; attrsize += tmplen + 1; } } else { /* xattrwalk has a non-empty name, use regular getxattr */ fsal_status = pxattrfid->pentry->obj_ops.getextattr_value_by_name( pxattrfid->pentry, name, pxattrfid->xattr->xattr_content, XATTR_BUFFERSIZE, &attrsize); if (fsal_status.minor == ERANGE) { /* we need a bigger buffer, do one more request with * 0-size to get length and reallocate/try again */ fsal_status = pxattrfid->pentry->obj_ops.getextattr_value_by_name( pxattrfid->pentry, name, pxattrfid->xattr->xattr_content, 0, &attrsize); if (FSAL_IS_ERROR(fsal_status)) { gsh_free(pxattrfid->xattr); gsh_free(pxattrfid); /* fsal_status.minor is a valid errno code */ return _9p_rerror(req9p, msgtag, fsal_status.minor, plenout, preply); } /* Check our own limit too before reallocating */ if (attrsize > _9P_XATTR_MAX_SIZE) { gsh_free(pxattrfid->xattr); gsh_free(pxattrfid); return _9p_rerror(req9p, msgtag, E2BIG, plenout, preply); } pxattrfid->xattr = gsh_realloc(pxattrfid->xattr, sizeof(*pxattrfid->xattr) + attrsize); fsal_status = pxattrfid->pentry->obj_ops.getextattr_value_by_name( pxattrfid->pentry, name, pxattrfid->xattr->xattr_content, attrsize, &attrsize); } if (FSAL_IS_ERROR(fsal_status)) { gsh_free(pxattrfid->xattr); gsh_free(pxattrfid); /* ENOENT for xattr is ENOATTR */ if (fsal_status.major == ERR_FSAL_NOENT) return _9p_rerror(req9p, msgtag, ENOATTR, plenout, preply); /* fsal_status.minor is a valid errno code */ return _9p_rerror(req9p, msgtag, fsal_status.minor, plenout, preply); } } pxattrfid->xattr->xattr_size = attrsize; pxattrfid->xattr->xattr_write = _9P_XATTR_READ_ONLY; req9p->pconn->fids[*attrfid] = pxattrfid; /* Increments refcount as we're manually making a new copy */ pfid->pentry->obj_ops.get_ref(pfid->pentry); /* hold reference on gdata */ uid2grp_hold_group_data(pxattrfid->gdata); get_gsh_export_ref(pfid->export); get_9p_user_cred_ref(pfid->ucred); if (pxattrfid->ppentry != NULL) { /* Increments refcount for ppentry */ pxattrfid->ppentry->obj_ops.get_ref(pxattrfid->ppentry); } /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RXATTRWALK); _9p_setptr(cursor, msgtag, u16); _9p_setvalue(cursor, attrsize, u64); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RXATTRWALK: tag=%u fid=%u attrfid=%u name=%.*s size=%llu", (u32) *msgtag, *fid, *attrfid, *name_len, name_str, (unsigned long long)attrsize); return 1; } /* _9p_xattrwalk */ nfs-ganesha-2.6.0/src/Protocols/9P/CMakeLists.txt000066400000000000000000000012471324272410200215150ustar00rootroot00000000000000 ########### next target ############### SET(9p_STAT_SRCS 9p_interpreter.c 9p_proto_tools.c 9p_read_conf.c 9p_attach.c 9p_auth.c 9p_clunk.c 9p_flush.c 9p_flush_hook.c 9p_getattr.c 9p_getlock.c 9p_lcreate.c 9p_lopen.c 9p_link.c 9p_lock.c 9p_mkdir.c 9p_mknod.c 9p_read.c 9p_readdir.c 9p_readlink.c 9p_remove.c 9p_rename.c 9p_renameat.c 9p_fsync.c 9p_unlinkat.c 9p_setattr.c 9p_statfs.c 9p_symlink.c 9p_version.c 9p_walk.c 9p_xattrcreate.c 9p_xattrwalk.c 9p_write.c 9p_rerror.c ) add_library(9p STATIC ${9p_STAT_SRCS}) add_sanitizers(9p) ########### install files ############### nfs-ganesha-2.6.0/src/Protocols/CMakeLists.txt000066400000000000000000000003241324272410200212200ustar00rootroot00000000000000add_subdirectory(NFS) add_subdirectory(XDR) if(USE_NLM) add_subdirectory(NLM) endif(USE_NLM) add_subdirectory(RQUOTA) if(USE_9P) add_subdirectory(9P) endif(USE_9P) ########### install files ############### nfs-ganesha-2.6.0/src/Protocols/NFS/000077500000000000000000000000001324272410200171075ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/Protocols/NFS/CMakeLists.txt000066400000000000000000000041271324272410200216530ustar00rootroot00000000000000 ########### next target ############### SET(nfsproto_STAT_SRCS mnt_Dump.c mnt_Export.c mnt_Mnt.c mnt_Null.c mnt_Umnt.c mnt_UmntAll.c nfs_null.c nfs4_Compound.c nfs4_op_access.c nfs4_op_close.c nfs4_op_commit.c nfs4_op_create.c nfs4_op_create_session.c nfs4_op_delegpurge.c nfs4_op_delegreturn.c nfs4_op_destroy_session.c nfs4_op_exchange_id.c nfs4_op_free_stateid.c nfs4_op_getattr.c nfs4_op_getdeviceinfo.c nfs4_op_getdevicelist.c nfs4_op_getfh.c nfs4_op_illegal.c nfs4_op_layoutcommit.c nfs4_op_layoutget.c nfs4_op_layoutreturn.c nfs4_op_link.c nfs4_op_lock.c nfs4_op_lockt.c nfs4_op_locku.c nfs4_op_lookup.c nfs4_op_lookupp.c nfs4_op_nverify.c nfs4_op_open.c nfs4_op_open_confirm.c nfs4_op_open_downgrade.c nfs4_op_openattr.c nfs4_op_putfh.c nfs4_op_putpubfh.c nfs4_op_putrootfh.c nfs4_op_read.c nfs4_op_readdir.c nfs4_op_readlink.c nfs4_op_reclaim_complete.c nfs4_op_release_lockowner.c nfs4_op_remove.c nfs4_op_rename.c nfs4_op_renew.c nfs4_op_restorefh.c nfs4_op_savefh.c nfs4_op_secinfo.c nfs4_op_secinfo_no_name.c nfs4_op_sequence.c nfs4_op_set_ssv.c nfs4_op_setattr.c nfs4_op_xattr.c nfs4_op_setclientid.c nfs4_op_setclientid_confirm.c nfs4_op_destroy_clientid.c nfs4_op_test_stateid.c nfs4_op_verify.c nfs4_op_write.c nfs4_pseudo.c nfs_proto_tools.c ) if(USE_NFS3) SET(nfsproto_STAT_SRCS ${nfsproto_STAT_SRCS} nfs3_access.c nfs3_commit.c nfs3_create.c nfs3_fsinfo.c nfs3_fsstat.c nfs3_getattr.c nfs3_link.c nfs3_lookup.c nfs3_mkdir.c nfs3_mknod.c nfs3_pathconf.c nfs3_read.c nfs3_readdir.c nfs3_readdirplus.c nfs3_readlink.c nfs3_remove.c nfs3_rename.c nfs3_rmdir.c nfs3_setattr.c nfs3_symlink.c nfs3_write.c ) endif(USE_NFS3) add_library(nfsproto STATIC ${nfsproto_STAT_SRCS}) add_sanitizers(nfsproto) set(nfs4callbackSRCS nfs4_cb_Compound.c ) add_library(nfs4callbacks STATIC ${nfs4callbackSRCS}) add_sanitizers(nfs4callbacks) nfs-ganesha-2.6.0/src/Protocols/NFS/maketest.conf000066400000000000000000000015731324272410200216010ustar00rootroot00000000000000 Test mount_protocol { Product = Mount protocol. Command = ../../bin/`archi -M`/test_mntproto Comment = Testing mount protocol. # all tests OK Success TestOk { STDOUT =~ /test_mnt_Null : OK/ AND STDOUT =~ /test_mnt_Export : OK/ } # Command execution problem Failure ERROR_MNTPROC_NULL { NOT STDOUT =~ /test_mnt_Null : OK/ } # Command execution problem Failure ERROR_MNTPROC_EXPORT { NOT STDOUT =~ /test_mnt_Export : OK/ } # other problem ? Failure UNKNOWN_ERROR { STDOUT =~ /ERROR/ } # anormal termination (should return 0) Failure AnormalTermination { STATUS != 0 } } nfs-ganesha-2.6.0/src/Protocols/NFS/mnt_Dump.c000066400000000000000000000036371324272410200210470ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * file mnt_Dump.c * brief MOUNTPROC_Dump for Mount protocol v1 and v3. * */ #include "config.h" #include "log.h" #include "nfs_core.h" #include "nfs_exports.h" #include "mount.h" #include "nfs_proto_functions.h" /** * @brief The Mount proc Dump function, for all versions. * * @param[in] parg Arguments (ignored) * @param[in] preq Ignored * @param[out] pres Pointer to results * */ int mnt_Dump(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling MNT_DUMP"); /* Ganesha does not support the mount list so this is a NOOP */ res->res_dump = NULL; return NFS_REQ_OK; } /* mnt_Null */ /** * mnt_Dump_Free: Frees the result structure allocated for mnt_Dump. * * Frees the result structure allocated for mnt_Dump. * * @param res [INOUT] Pointer to the result structure. * */ void mnt_Dump_Free(nfs_res_t *res) { /* Nothing to do */ } nfs-ganesha-2.6.0/src/Protocols/NFS/mnt_Export.c000066400000000000000000000122111324272410200214070ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * file mnt_Export.c * brief MOUNTPROC_EXPORT for Mount protocol v1 and v3. * * mnt_Null.c : MOUNTPROC_EXPORT in V1, V3. * * Exporting client hosts and networks OK. * */ #include "config.h" #include #include "log.h" #include "nfs_core.h" #include "nfs_exports.h" #include "fsal.h" #include "nfs_proto_functions.h" #include "export_mgr.h" struct proc_state { exports head; exports tail; int retval; }; static bool proc_export(struct gsh_export *export, void *arg) { struct proc_state *state = arg; struct exportnode *new_expnode; struct glist_head *glist_item; exportlist_client_entry_t *client; struct groupnode *group, *grp_tail = NULL; const char *grp_name; state->retval = 0; /* If client does not have any access to the export, * don't add it to the list */ op_ctx->ctx_export = export; op_ctx->fsal_export = export->fsal_export; export_check_access(); if (!(op_ctx->export_perms->options & EXPORT_OPTION_ACCESS_MASK)) { LogFullDebug(COMPONENT_NFSPROTO, "Client is not allowed to access Export_Id %d %s", export->export_id, export_path(export)); return true; } if (!(op_ctx->export_perms->options & EXPORT_OPTION_NFSV3)) { LogFullDebug(COMPONENT_NFSPROTO, "Not exported for NFSv3, Export_Id %d %s", export->export_id, export_path(export)); return true; } new_expnode = gsh_calloc(1, sizeof(struct exportnode)); new_expnode->ex_dir = gsh_strdup(export_path(export)); PTHREAD_RWLOCK_rdlock(&op_ctx->ctx_export->lock); glist_for_each(glist_item, &export->clients) { client = glist_entry(glist_item, exportlist_client_entry_t, cle_list); group = gsh_calloc(1, sizeof(struct groupnode)); if (grp_tail == NULL) new_expnode->ex_groups = group; else grp_tail->gr_next = group; grp_tail = group; switch (client->type) { case NETWORK_CLIENT: grp_name = cidr_to_str(client->client.network.cidr, CIDR_NOFLAGS); if (grp_name == NULL) { state->retval = errno; grp_name = "Invalid Network Address"; } break; case NETGROUP_CLIENT: grp_name = client->client.netgroup.netgroupname; break; case GSSPRINCIPAL_CLIENT: grp_name = client->client.gssprinc.princname; break; case MATCH_ANY_CLIENT: grp_name = "*"; break; case WILDCARDHOST_CLIENT: grp_name = client->client.wildcard.wildcard; break; default: grp_name = ""; } LogFullDebug(COMPONENT_NFSPROTO, "Export %s client %s", export_path(export), grp_name); group->gr_name = gsh_strdup(grp_name); } PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock); if (state->head == NULL) state->head = new_expnode; else state->tail->ex_next = new_expnode; state->tail = new_expnode; return true; } /** * @brief The Mount proc EXPORT function, for all versions. * * Return a list of all exports and their allowed clients/groups/networks. * * @param[in] arg Ignored * @param[in] req Ignored * @param[out] res Pointer to the export list * */ int mnt_Export(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct proc_state proc_state; /* init everything of interest to good state. */ memset(res, 0, sizeof(nfs_res_t)); memset(&proc_state, 0, sizeof(proc_state)); (void)foreach_gsh_export(proc_export, false, &proc_state); if (proc_state.retval != 0) { LogCrit(COMPONENT_NFSPROTO, "Processing exports failed. error = \"%s\" (%d)", strerror(proc_state.retval), proc_state.retval); } op_ctx->ctx_export = NULL; op_ctx->fsal_export = NULL; res->res_mntexport = proc_state.head; return NFS_REQ_OK; } /* mnt_Export */ /** * mnt_Export_Free: Frees the result structure allocated for mnt_Export. * * Frees the result structure allocated for mnt_Dump. * * @param res [INOUT] Pointer to the result structure. * */ void mnt_Export_Free(nfs_res_t *res) { struct exportnode *exp, *next_exp; struct groupnode *grp, *next_grp; exp = res->res_mntexport; while (exp != NULL) { next_exp = exp->ex_next; grp = exp->ex_groups; while (grp != NULL) { next_grp = grp->gr_next; if (grp->gr_name != NULL) gsh_free(grp->gr_name); gsh_free(grp); grp = next_grp; } gsh_free(exp); exp = next_exp; } } /* mnt_Export_Free */ nfs-ganesha-2.6.0/src/Protocols/NFS/mnt_Mnt.c000066400000000000000000000161761324272410200207020ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * file mnt_Mnt.c * brief MOUNTPROC_MNT for Mount protocol v1 and v3. * * mnt_Null.c : MOUNTPROC_EXPORT in V1, V3. * */ #include "config.h" #include "log.h" #include "nfs_core.h" #include "nfs_exports.h" #include "fsal.h" #include "nfs_file_handle.h" #include "nfs_proto_functions.h" #include "client_mgr.h" #include "export_mgr.h" /** * @brief The Mount proc mount function for MOUNT_V3 version * * The MOUNT proc proc function for MOUNT_V3 version * * @param[in] arg The export path to be mounted * @param[in] req ignored * @param[out] res Result structure. * */ int mnt_Mnt(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct gsh_export *export = NULL; int auth_flavor[NB_AUTH_FLAVOR]; int index_auth = 0; int i = 0; char dumpfh[1024]; int retval = NFS_REQ_OK; nfs_fh3 *fh3 = (nfs_fh3 *) &res->res_mnt3.mountres3_u.mountinfo.fhandle; struct fsal_obj_handle *obj = NULL; LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling MNT_MNT path=%s", arg->arg_mnt); /* Paranoid command to clean the result struct. */ memset(res, 0, sizeof(nfs_res_t)); /* Quick escape if an unsupported MOUNT version */ if (req->rq_msg.cb_vers != MOUNT_V3) { res->res_mnt1.status = NFSERR_ACCES; goto out; } if (arg->arg_mnt == NULL) { LogCrit(COMPONENT_NFSPROTO, "NULL path passed as Mount argument !!!"); retval = NFS_REQ_DROP; goto out; } /* If the path ends with a '/', get rid of it */ /** @todo: should it be a while()?? */ if ((strlen(arg->arg_mnt) > 1) && (arg->arg_mnt[strlen(arg->arg_mnt) - 1] == '/')) arg->arg_mnt[strlen(arg->arg_mnt) - 1] = '\0'; /* Find the export for the dirname (using as well Path, Pseudo, or Tag) */ if (arg->arg_mnt[0] != '/') { LogFullDebug(COMPONENT_NFSPROTO, "Searching for export by tag for %s", arg->arg_mnt); export = get_gsh_export_by_tag(arg->arg_mnt); } else if (nfs_param.core_param.mount_path_pseudo) { LogFullDebug(COMPONENT_NFSPROTO, "Searching for export by pseudo for %s", arg->arg_mnt); export = get_gsh_export_by_pseudo(arg->arg_mnt, false); } else { LogFullDebug(COMPONENT_NFSPROTO, "Searching for export by path for %s", arg->arg_mnt); export = get_gsh_export_by_path(arg->arg_mnt, false); } if (export == NULL) { /* No export found, return ACCESS error. */ LogEvent(COMPONENT_NFSPROTO, "MOUNT: Export entry for %s not found", arg->arg_mnt); /* entry not found. */ /* @todo : not MNT3ERR_NOENT => ok */ res->res_mnt3.fhs_status = MNT3ERR_ACCES; goto out; } /* set the export in the context */ op_ctx->ctx_export = export; op_ctx->fsal_export = op_ctx->ctx_export->fsal_export; /* Check access based on client. Don't bother checking TCP/UDP as some * clients use UDP for MOUNT even when they will use TCP for NFS. */ export_check_access(); if ((op_ctx->export_perms->options & EXPORT_OPTION_NFSV3) == 0) { LogInfo(COMPONENT_NFSPROTO, "MOUNT: Export entry %s does not support NFS v3 for client %s", export_path(export), op_ctx->client ? op_ctx->client->hostaddr_str : "unknown client"); res->res_mnt3.fhs_status = MNT3ERR_ACCES; goto out; } /* retrieve the associated NFS handle */ if (arg->arg_mnt[0] != '/' || !strcmp(arg->arg_mnt, export_path(export))) { if (FSAL_IS_ERROR(nfs_export_get_root_entry(export, &obj))) { res->res_mnt3.fhs_status = MNT3ERR_ACCES; goto out; } } else { LogInfo(COMPONENT_NFSPROTO, "MOUNT: Performance warning: Export entry is not cached"); if (FSAL_IS_ERROR(op_ctx->fsal_export->exp_ops.lookup_path( op_ctx->fsal_export, arg->arg_mnt, &obj, NULL))) { res->res_mnt3.fhs_status = MNT3ERR_ACCES; goto out; } } /* convert the fsal_handle to a file handle */ /** @todo: * The mountinfo.fhandle definition is an overlay on/of nfs_fh3. * redefine and eliminate one or the other. */ if (!nfs3_FSALToFhandle(true, fh3, obj, export)) { res->res_mnt3.fhs_status = MNT3ERR_INVAL; } else { if (isDebug(COMPONENT_NFSPROTO)) sprint_fhandle3(dumpfh, fh3); res->res_mnt3.fhs_status = MNT3_OK; } /* Release the fsal_obj_handle created for the path */ LogFullDebug(COMPONENT_FSAL, "Releasing %p", obj); obj->obj_ops.put_ref(obj); /* Return the supported authentication flavor in V3 based * on the client's export permissions. These should be listed * in a preferred order. */ #ifdef _HAVE_GSSAPI if (nfs_param.krb5_param.active_krb5 == true) { if (op_ctx->export_perms->options & EXPORT_OPTION_RPCSEC_GSS_PRIV) auth_flavor[index_auth++] = MNT_RPC_GSS_PRIVACY; if (op_ctx->export_perms->options & EXPORT_OPTION_RPCSEC_GSS_INTG) auth_flavor[index_auth++] = MNT_RPC_GSS_INTEGRITY; if (op_ctx->export_perms->options & EXPORT_OPTION_RPCSEC_GSS_NONE) auth_flavor[index_auth++] = MNT_RPC_GSS_NONE; } #endif if (op_ctx->export_perms->options & EXPORT_OPTION_AUTH_UNIX) auth_flavor[index_auth++] = AUTH_UNIX; if (op_ctx->export_perms->options & EXPORT_OPTION_AUTH_NONE) auth_flavor[index_auth++] = AUTH_NONE; LogDebug(COMPONENT_NFSPROTO, "MOUNT: Entry supports %d different flavours handle=%s for client %s", index_auth, dumpfh, op_ctx->client ? op_ctx->client->hostaddr_str : "unknown client"); mountres3_ok * const RES_MOUNTINFO = &res->res_mnt3.mountres3_u.mountinfo; RES_MOUNTINFO->auth_flavors.auth_flavors_val = gsh_calloc(index_auth, sizeof(int)); RES_MOUNTINFO->auth_flavors.auth_flavors_len = index_auth; for (i = 0; i < index_auth; i++) RES_MOUNTINFO->auth_flavors.auth_flavors_val[i] = auth_flavor[i]; out: if (export != NULL) { op_ctx->ctx_export = NULL; op_ctx->fsal_export = NULL; put_gsh_export(export); } return retval; } /* mnt_Mnt */ /** * mnt_Mnt_Free: Frees the result structure allocated for mnt_Mnt. * * Frees the result structure allocated for mnt_Mnt. * * @param res [INOUT] Pointer to the result structure. * */ void mnt1_Mnt_Free(nfs_res_t *res) { /* return */ } void mnt3_Mnt_Free(nfs_res_t *res) { mountres3_ok *resok = &res->res_mnt3.mountres3_u.mountinfo; if (res->res_mnt3.fhs_status == MNT3_OK) { gsh_free(resok->auth_flavors.auth_flavors_val); gsh_free(resok->fhandle.fhandle3_val); } } nfs-ganesha-2.6.0/src/Protocols/NFS/mnt_Null.c000066400000000000000000000034461324272410200210520ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * file mnt_Null.c * brief MOUNTPROC_NULL for Mount protocol v1 and v3. * * mnt_Null.c : MOUNTPROC_NULL in V1, V3. * */ #include "config.h" #include "log.h" #include "nfs_core.h" #include "mount.h" #include "nfs_proto_functions.h" /** * @brief The Mount proc null function, for all versions. * * The MOUNT proc null function, for all versions. * * @param[in] arg ignored * @param[in] req ignored * @param[out] res ignored * */ int mnt_Null(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling MNT_NULL"); return MNT3_OK; } /* mnt_Null */ /** * @brief Frees the result structure allocated for mnt_Null * * @param res [INOUT] Pointer to the result structure. * */ void mnt_Null_Free(nfs_res_t *res) { /* return */ } nfs-ganesha-2.6.0/src/Protocols/NFS/mnt_Umnt.c000066400000000000000000000035501324272410200210570ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * file mnt_Umnt.c * brief MOUNTPROC_UMNT for Mount protocol v1 and v3. * * */ #include "config.h" #include "log.h" #include "nfs_core.h" #include "nfs_exports.h" #include "mount.h" #include "nfs_proto_functions.h" /** * @brief The Mount proc umount function, for all versions. * * @param[in] arg * @param[in] req * @param[out] res * */ int mnt_Umnt(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling MNT_UMNT path %s", arg->arg_mnt); /* Ganesha does not support the mount list so this is a NOOP */ return NFS_REQ_OK; } /* mnt_Umnt */ /** * mnt_Umnt_Free: Frees the result structure allocated for mnt_Umnt. * * Frees the result structure allocated for mnt_UmntAll. * * @param res [INOUT] Pointer to the result structure. * */ void mnt_Umnt_Free(nfs_res_t *res) { /* Nothing to do */ } nfs-ganesha-2.6.0/src/Protocols/NFS/mnt_UmntAll.c000066400000000000000000000035531324272410200215130ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * file mnt_UmntAll.c * brief MOUNTPROC_UMNTALL for Mount protocol v1 and v3. * * */ #include "config.h" #include "log.h" #include "nfs_core.h" #include "nfs_exports.h" #include "mount.h" #include "nfs_proto_functions.h" /** * @brief The Mount proc umount_all function, for all versions. * * @param[in] arg * @param[in] req * @param[out] res * */ int mnt_UmntAll(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling MNT_UMNTALL"); /* Ganesha does not support the mount list so this is a NOOP */ return NFS_REQ_OK; } /* mnt_UmntAll */ /** * mnt_UmntAll_Free: Frees the result structure allocated for mnt_UmntAll. * * Frees the result structure allocated for mnt_UmntAll. * * @param res [INOUT] Pointer to the result structure. * */ void mnt_UmntAll_Free(nfs_res_t *res) { /* Nothing to do */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs3_access.c000066400000000000000000000072651324272410200214570ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * @file nfs3_access.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. */ #include "config.h" #include #include #include #include #include /* for having FNDELAY */ #include "hashtable.h" #include "log.h" #include "nfs23.h" #include "nfs4.h" #include "mount.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_creds.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_convert.h" /** * Implements NFSPROC3_ACCESS. * * This function implements NFSPROC3_ACCESS. * * @param[in] arg NFS arguments 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_access(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { fsal_errors_t fsal_errors; struct fsal_obj_handle *entry = NULL; int rc = NFS_REQ_OK; if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; sprint_fhandle3(str, &(arg->arg_access3.object)); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling NFS3_ACCESS handle: %s", str); } /* to avoid setting it on each error case */ res->res_access3.ACCESS3res_u.resfail.obj_attributes.attributes_follow = FALSE; /* Convert file handle into a vnode */ entry = nfs3_FhandleToCache(&(arg->arg_access3.object), &(res->res_access3.status), &rc); if (entry == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } /* Perform the 'access' call */ fsal_errors = nfs_access_op(entry, arg->arg_access3.access, &res->res_access3.ACCESS3res_u.resok.access, NULL); if (fsal_errors == ERR_FSAL_NO_ERROR || fsal_errors == ERR_FSAL_ACCESS) { /* Build Post Op Attributes */ nfs_SetPostOpAttr( entry, &res->res_access3.ACCESS3res_u.resok.obj_attributes, NULL); res->res_access3.status = NFS3_OK; rc = NFS_REQ_OK; goto out; } /* If we are here, there was an error */ if (nfs_RetryableError(fsal_errors)) { rc = NFS_REQ_DROP; goto out; } res->res_access3.status = nfs3_Errno(fsal_errors); nfs_SetPostOpAttr(entry, &res->res_access3.ACCESS3res_u.resfail.obj_attributes, NULL); out: if (entry) entry->obj_ops.put_ref(entry); return rc; } /* nfs3_access */ /** * @brief Free the result structure allocated for nfs3_access. * * this function frees the result structure allocated for nfs3_access. * * @param[in,out] res Result structure. * */ void nfs3_access_free(nfs_res_t *res) { /* Nothing to do */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs3_commit.c000066400000000000000000000070621324272410200215010ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * file nfs3_commit.c * brief Routines used for managing the NFS4 COMPOUND functions. * * nfs3_commit.c : Routines used for managing the NFS4 COMPOUND functions. * */ #include "config.h" #include #include #include #include #include /* for having FNDELAY */ #include "hashtable.h" #include "log.h" #include "nfs23.h" #include "nfs4.h" #include "mount.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_convert.h" /** * @brief Implements NFSPROC3_COMMIT * * Implements NFSPROC3_COMMIT. * * @param[in] arg NFS arguments 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_commit(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { fsal_status_t fsal_status; struct fsal_obj_handle *obj = NULL; int rc = NFS_REQ_OK; if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; sprint_fhandle3(str, &(arg->arg_commit3.file)); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling NFS3_COMMIT handle: %s", str); } /* To avoid setting it on each error case */ res->res_commit3.COMMIT3res_u.resfail.file_wcc.before.attributes_follow = FALSE; res->res_commit3.COMMIT3res_u.resfail.file_wcc.after.attributes_follow = FALSE; obj = nfs3_FhandleToCache(&arg->arg_commit3.file, &res->res_commit3.status, &rc); if (obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } fsal_status = fsal_commit(obj, arg->arg_commit3.offset, arg->arg_commit3.count); if (FSAL_IS_ERROR(fsal_status)) { res->res_commit3.status = nfs3_Errno_status(fsal_status); nfs_SetWccData(NULL, obj, &res->res_commit3.COMMIT3res_u.resfail.file_wcc); rc = NFS_REQ_OK; goto out; } nfs_SetWccData(NULL, obj, &(res->res_commit3.COMMIT3res_u.resok.file_wcc)); /* Set the write verifier */ memcpy(res->res_commit3.COMMIT3res_u.resok.verf, NFS3_write_verifier, sizeof(writeverf3)); res->res_commit3.status = NFS3_OK; out: if (obj) obj->obj_ops.put_ref(obj); return rc; } /* nfs3_commit */ /** * @brief Free the result structure allocated for nfs3_commit. * * This function frees the result structure allocated for nfs3_commit. * * @param[in,out] res Result structure * */ void nfs3_commit_free(nfs_res_t *res) { /* Nothing to do here */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs3_create.c000066400000000000000000000154361324272410200214600ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * @file nfs3_create.c * @brief Routines used for managing the NFS4 COMPOUND functions. */ #include "config.h" #include #include #include #include #include /* for having FNDELAY */ #include "hashtable.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_creds.h" #include "nfs_proto_functions.h" #include "nfs_convert.h" #include "nfs_file_handle.h" #include "nfs_proto_tools.h" #include "fsal_convert.h" #include "export_mgr.h" /** * * @brief The NFSPROC3_CREATE * * Implements the NFSPROC3_CREATE function. * * @param[in] arg NFS arguments 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 successfull * @retval NFS_REQ_DROP if failed but retryable * @retval NFS_REQ_FAILED if failed and not retryable * */ int nfs3_create(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { const char *file_name = arg->arg_create3.where.name; struct fsal_obj_handle *file_obj = NULL; struct fsal_obj_handle *parent_obj = NULL; pre_op_attr pre_parent = { .attributes_follow = false }; struct attrlist sattr, attrs; fsal_status_t fsal_status = {0, 0}; int rc = NFS_REQ_OK; fsal_verifier_t verifier; enum fsal_create_mode createmode; if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; nfs_FhandleToStr(req->rq_msg.cb_vers, &(arg->arg_create3.where.dir), NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling NFS3_CREATE handle: %s name: %s", str, file_name ? file_name : ""); } /* We have the option of not sending attributes, so set ATTR_RDATTR_ERR. */ fsal_prepare_attrs(&attrs, ATTRS_NFS3 | ATTR_RDATTR_ERR); memset(&sattr, 0, sizeof(struct attrlist)); /* to avoid setting it on each error case */ res->res_create3.CREATE3res_u.resfail.dir_wcc.before.attributes_follow = FALSE; res->res_create3.CREATE3res_u.resfail.dir_wcc.after.attributes_follow = FALSE; parent_obj = nfs3_FhandleToCache(&arg->arg_create3.where.dir, &res->res_create3.status, &rc); if (parent_obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } /* get directory attributes before action (for V3 reply) */ nfs_SetPreOpAttr(parent_obj, &pre_parent); /* Sanity checks: new file name must be non-null; parent must be a directory. */ if (parent_obj->type != DIRECTORY) { res->res_create3.status = NFS3ERR_NOTDIR; rc = NFS_REQ_OK; goto out; } /* if quota support is active, then we should check is the FSAL allows inode creation or not */ fsal_status = op_ctx->fsal_export->exp_ops.check_quota(op_ctx->fsal_export, op_ctx->ctx_export->fullpath, FSAL_QUOTA_INODES); if (FSAL_IS_ERROR(fsal_status)) { res->res_create3.status = NFS3ERR_DQUOT; rc = NFS_REQ_OK; goto out; } if (file_name == NULL || *file_name == '\0') { fsal_status = fsalstat(ERR_FSAL_INVAL, 0); goto out_fail; } /* Check if asked attributes are correct */ if (arg->arg_create3.how.mode == GUARDED || arg->arg_create3.how.mode == UNCHECKED) { if (nfs3_Sattr_To_FSALattr( &sattr, &arg->arg_create3.how.createhow3_u.obj_attributes) == 0) { res->res_create3.status = NFS3ERR_INVAL; rc = NFS_REQ_OK; goto out; } } if (!(sattr.valid_mask & ATTR_MODE)) { /* Make sure mode is set. */ sattr.mode = 0600; sattr.valid_mask |= ATTR_MODE; } /* Set the createmode */ createmode = nfs3_createmode_to_fsal(arg->arg_create3.how.mode); if (createmode == FSAL_EXCLUSIVE) { /* Set the verifier if EXCLUSIVE */ memcpy(verifier, &arg->arg_create3.how.createhow3_u.verf, sizeof(fsal_verifier_t)); } /* If owner or owner_group are set, and the credential was * squashed, then we must squash the set owner and owner_group. */ squash_setattr(&sattr); /* Stateless open, assume Read/Write. */ fsal_status = fsal_open2(parent_obj, NULL, FSAL_O_RDWR, createmode, file_name, &sattr, verifier, &file_obj, &attrs); if (FSAL_IS_ERROR(fsal_status)) goto out_fail; /* Release the attributes (may release an inherited ACL) */ fsal_release_attrs(&sattr); /* Build file handle and set Post Op Fh3 structure */ if (!nfs3_FSALToFhandle( true, &res->res_create3.CREATE3res_u.resok.obj.post_op_fh3_u.handle, file_obj, op_ctx->ctx_export)) { res->res_create3.status = NFS3ERR_BADHANDLE; rc = NFS_REQ_OK; goto out; } /* Set Post Op Fh3 structure */ res->res_create3.CREATE3res_u.resok.obj.handle_follows = TRUE; /* Build entry attributes */ nfs_SetPostOpAttr(file_obj, &res->res_create3.CREATE3res_u.resok.obj_attributes, &attrs); nfs_SetWccData(&pre_parent, parent_obj, &res->res_create3.CREATE3res_u.resok.dir_wcc); res->res_create3.status = NFS3_OK; rc = NFS_REQ_OK; out: /* return references */ if (file_obj) file_obj->obj_ops.put_ref(file_obj); if (parent_obj) parent_obj->obj_ops.put_ref(parent_obj); return rc; out_fail: /* Release the attributes. */ fsal_release_attrs(&attrs); if (nfs_RetryableError(fsal_status.major)) { rc = NFS_REQ_DROP; } else { res->res_create3.status = nfs3_Errno_status(fsal_status); nfs_SetWccData(&pre_parent, parent_obj, &res->res_create3.CREATE3res_u.resfail.dir_wcc); } goto out; } /* nfs3_create */ /** * @brief Free the result structure allocated for nfs3_create. * * Thsi function frees the result structure allocated for nfs3_create. * * @param[in,out] res Result structure * */ void nfs3_create_free(nfs_res_t *res) { nfs_fh3 *handle = &res->res_create3.CREATE3res_u.resok.obj.post_op_fh3_u.handle; if ((res->res_create3.status == NFS3_OK) && (res->res_create3.CREATE3res_u.resok.obj.handle_follows)) { gsh_free(handle->data.data_val); } } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs3_fsinfo.c000066400000000000000000000111201324272410200214630ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs3_fsinfo.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. */ #include "config.h" #include #include #include #include "hashtable.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "export_mgr.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" /** * @brief Implement NFSPROC3_FSINFO * * This function Implements NFSPROC3_FSINFO. * * @todo ACE: This function uses a lot of constants instead of * retrieving information from the FSAL as it ought. * * @param[in] arg NFS arguments 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_fsinfo(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct fsal_obj_handle *obj = NULL; int rc = NFS_REQ_OK; if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; sprint_fhandle3(str, &(arg->arg_fsinfo3.fsroot)); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling NFS3_FSINFO handle: %s", str); } /* To avoid setting it on each error case */ res->res_fsinfo3.FSINFO3res_u.resfail.obj_attributes.attributes_follow = FALSE; obj = nfs3_FhandleToCache(&arg->arg_fsinfo3.fsroot, &res->res_fsinfo3.status, &rc); if (obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } /* New fields were added to nfs_config_t to handle this value. We use them */ FSINFO3resok * const FSINFO_FIELD = &res->res_fsinfo3.FSINFO3res_u.resok; FSINFO_FIELD->rtmax = atomic_fetch_uint64_t(&op_ctx->ctx_export->MaxRead); FSINFO_FIELD->rtpref = atomic_fetch_uint64_t(&op_ctx->ctx_export->PrefRead); /* This field is generally unused, it will be removed in V4 */ FSINFO_FIELD->rtmult = DEV_BSIZE; FSINFO_FIELD->wtmax = atomic_fetch_uint64_t(&op_ctx->ctx_export->MaxWrite); FSINFO_FIELD->wtpref = atomic_fetch_uint64_t(&op_ctx->ctx_export->PrefWrite); /* This field is generally unused, it will be removed in V4 */ FSINFO_FIELD->wtmult = DEV_BSIZE; FSINFO_FIELD->dtpref = atomic_fetch_uint64_t(&op_ctx->ctx_export->PrefReaddir); FSINFO_FIELD->maxfilesize = op_ctx->fsal_export->exp_ops.fs_maxfilesize(op_ctx->fsal_export); FSINFO_FIELD->time_delta.tv_sec = 1; FSINFO_FIELD->time_delta.tv_nsec = 0; LogFullDebug(COMPONENT_NFSPROTO, "rtmax = %d | rtpref = %d | trmult = %d", FSINFO_FIELD->rtmax, FSINFO_FIELD->rtpref, FSINFO_FIELD->rtmult); LogFullDebug(COMPONENT_NFSPROTO, "wtmax = %d | wtpref = %d | wrmult = %d", FSINFO_FIELD->wtmax, FSINFO_FIELD->wtpref, FSINFO_FIELD->wtmult); LogFullDebug(COMPONENT_NFSPROTO, "dtpref = %d | maxfilesize = %llu ", FSINFO_FIELD->dtpref, FSINFO_FIELD->maxfilesize); /* Allow all kinds of operations to be performed on the server through NFS v3 */ FSINFO_FIELD->properties = FSF3_LINK | FSF3_SYMLINK | FSF3_HOMOGENEOUS | FSF3_CANSETTIME; nfs_SetPostOpAttr(obj, &res->res_fsinfo3.FSINFO3res_u.resok.obj_attributes, NULL); res->res_fsinfo3.status = NFS3_OK; out: if (obj) obj->obj_ops.put_ref(obj); return rc; } /* nfs3_fsinfo */ /** * @brief Free the result structure allocated for nfs3_fsinfo. * * This function frees the result structure allocated for nfs3_fsinfo. * * @param[in,out] res The result structure * */ void nfs3_fsinfo_free(nfs_res_t *res) { /* Nothing to do here */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs3_fsstat.c000066400000000000000000000121111324272410200215040ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs3_fsstat.c * @brief Routines used for managing the NFS4 COMPOUND functions. */ #include "config.h" #include #include #include #include #include /* for having FNDELAY */ #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "nfs23.h" #include "nfs4.h" #include "mount.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_convert.h" #include "nfs_proto_tools.h" /** * * @brief The NFSPROC3_FSSTAT * * Implements the NFSPROC3_FSSTAT. * * @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_fsstat(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { fsal_dynamicfsinfo_t dynamicinfo; fsal_status_t fsal_status; struct fsal_obj_handle *obj = NULL; int rc = NFS_REQ_OK; if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; nfs_FhandleToStr(req->rq_msg.cb_vers, &(arg->arg_fsstat3.fsroot), NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling NFS3_FSSTAT handle: %s", str); } /* to avoid setting it on each error case */ res->res_fsstat3.FSSTAT3res_u.resfail.obj_attributes.attributes_follow = FALSE; obj = nfs3_FhandleToCache(&arg->arg_fsstat3.fsroot, &res->res_fsstat3.status, &rc); if (obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ return rc; } /* Get statistics and convert from FSAL */ fsal_status = fsal_statfs(obj, &dynamicinfo); if (FSAL_IS_ERROR(fsal_status)) { /* At this point we met an error */ LogFullDebug(COMPONENT_NFSPROTO, "failed statfs: fsal_status=%s", fsal_err_txt(fsal_status)); if (nfs_RetryableError(fsal_status.major)) { /* Drop retryable errors. */ rc = NFS_REQ_DROP; } else { res->res_fsstat3.status = nfs3_Errno_status(fsal_status); rc = NFS_REQ_OK; } goto out; } LogFullDebug(COMPONENT_NFSPROTO, "nfs_Fsstat --> dynamicinfo.total_bytes=%" PRIu64 " dynamicinfo.free_bytes=%" PRIu64 " dynamicinfo.avail_bytes=%" PRIu64, dynamicinfo.total_bytes, dynamicinfo.free_bytes, dynamicinfo.avail_bytes); LogFullDebug(COMPONENT_NFSPROTO, "nfs_Fsstat --> dynamicinfo.total_files=%" PRIu64 " dynamicinfo.free_files=%" PRIu64 " dynamicinfo.avail_files=%" PRIu64, dynamicinfo.total_files, dynamicinfo.free_files, dynamicinfo.avail_files); nfs_SetPostOpAttr(obj, &res->res_fsstat3.FSSTAT3res_u.resok.obj_attributes, NULL); res->res_fsstat3.FSSTAT3res_u.resok.tbytes = dynamicinfo.total_bytes; res->res_fsstat3.FSSTAT3res_u.resok.fbytes = dynamicinfo.free_bytes; res->res_fsstat3.FSSTAT3res_u.resok.abytes = dynamicinfo.avail_bytes; res->res_fsstat3.FSSTAT3res_u.resok.tfiles = dynamicinfo.total_files; res->res_fsstat3.FSSTAT3res_u.resok.ffiles = dynamicinfo.free_files; res->res_fsstat3.FSSTAT3res_u.resok.afiles = dynamicinfo.avail_files; /* volatile FS */ res->res_fsstat3.FSSTAT3res_u.resok.invarsec = 0; res->res_fsstat3.status = NFS3_OK; LogFullDebug(COMPONENT_NFSPROTO, "nfs_Fsstat --> tbytes=%llu fbytes=%llu abytes=%llu", res->res_fsstat3.FSSTAT3res_u.resok.tbytes, res->res_fsstat3.FSSTAT3res_u.resok.fbytes, res->res_fsstat3.FSSTAT3res_u.resok.abytes); LogFullDebug(COMPONENT_NFSPROTO, "nfs_Fsstat --> tfiles=%llu fffiles=%llu afiles=%llu", res->res_fsstat3.FSSTAT3res_u.resok.tfiles, res->res_fsstat3.FSSTAT3res_u.resok.ffiles, res->res_fsstat3.FSSTAT3res_u.resok.afiles); rc = NFS_REQ_OK; out: /* return references */ obj->obj_ops.put_ref(obj); return rc; } /* nfs3_fsstat */ /** * @brief Free the result structure allocated for nfs3_fsstat * * This function frees the result structure allocated for nfs3_fsstat. * * @param[in] res Result structure * */ void nfs3_fsstat_free(nfs_res_t *res) { /* Nothing to do here */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs3_getattr.c000066400000000000000000000065761324272410200216740ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * @file nfs3_getattr.c * @brief Implements the NFSv3 GETATTR proc */ #include "config.h" #include #include #include #include #include /* for having FNDELAY */ #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "nfs23.h" #include "nfs4.h" #include "mount.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_convert.h" #include "nfs_proto_tools.h" /** * * @brief Get attributes for a file * * Get attributes for a file. Implements NFSPROC3_GETATTR. * * @param[in] arg NFS arguments 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 successfull * @retval NFS_REQ_DROP if failed but retryable * @retval NFS_REQ_FAILED if failed and not retryable */ int nfs3_getattr(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct fsal_obj_handle *obj = NULL; int rc = NFS_REQ_OK; struct attrlist attrs; fsal_status_t status; if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; nfs_FhandleToStr(req->rq_msg.cb_vers, &(arg->arg_getattr3.object), NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling NFS3_GETATTR handle: %s", str); } fsal_prepare_attrs(&attrs, ATTRS_NFS3); obj = nfs3_FhandleToCache(&arg->arg_getattr3.object, &res->res_getattr3.status, &rc); if (obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ LogFullDebug(COMPONENT_NFSPROTO, "nfs_Getattr returning %d", rc); goto out; } status = obj->obj_ops.getattrs(obj, &attrs); if (FSAL_IS_ERROR(status)) { res->res_getattr3.status = nfs3_Errno_status(status); LogFullDebug(COMPONENT_NFSPROTO, "nfs_Getattr set failed status v3"); rc = NFS_REQ_OK; goto out; } nfs3_FSALattr_To_Fattr( obj, &attrs, &res->res_getattr3.GETATTR3res_u.resok.obj_attributes); res->res_getattr3.status = NFS3_OK; LogFullDebug(COMPONENT_NFSPROTO, "nfs_Getattr succeeded"); rc = NFS_REQ_OK; out: /* Done with the attrs */ fsal_release_attrs(&attrs); /* return references */ if (obj) obj->obj_ops.put_ref(obj); return rc; } /** * @brief Free the result structure allocated for nfs3_getattr. * * @param[in,out] resp Result structure * */ void nfs3_getattr_free(nfs_res_t *resp) { /* Nothing to do here */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs3_link.c000066400000000000000000000127441324272410200211510ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * @file nfs3_link.c * @brief everything that is needed to handle NFS PROC3 LINK. */ #include "config.h" #include #include #include #include #include /* for having FNDELAY */ #include "hashtable.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_convert.h" #include "nfs_proto_tools.h" #include "nfs_file_handle.h" #include "client_mgr.h" /** * @brief Helper to verify validity of export ID's * * @l3_arg NFS argument * @req SVC request * * @return nfsstat3 */ static enum nfsstat3 nfs3_verify_exportid(struct LINK3args *l3_arg, struct svc_req *req) { const short to_exportid = nfs3_FhandleToExportId(&l3_arg->link.dir); const short from_exportid = nfs3_FhandleToExportId(&l3_arg->file); if (to_exportid < 0 || from_exportid < 0) { LogInfo(COMPONENT_DISPATCH, "NFS%d LINK Request from client %s has badly formed handle for link dir", req->rq_msg.cb_vers, op_ctx->client ? op_ctx->client->hostaddr_str : "unknown client"); return NFS3ERR_BADHANDLE; } /* Both objects have to be in the same filesystem */ if (to_exportid != from_exportid) return NFS3ERR_XDEV; return NFS3_OK; } /** * * @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 */ /** * @brief Free the result structure allocated for nfs3_link * * This function frees the result structure allocated for nfs3_link. * * @param[in,out] resp Result structure * */ void nfs3_link_free(nfs_res_t *resp) { /* Nothing to do here */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs3_lookup.c000066400000000000000000000104451324272410200215210ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs3_lookup.c * @brief everything that is needed to handle NFS PROC3 LINK. */ #include "config.h" #include #include #include #include #include /* for having FNDELAY */ #include "hashtable.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_convert.h" #include "nfs_proto_tools.h" /** * * @brief The NFS3_LOOKUP * * Implements the NFS3_LOOKUP function. * * @param[in] arg NFS arguments 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 successfull * @retval NFS_REQ_DROP if failed but retryable * @retval NFS_REQ_FAILED if failed and not retryable * */ int nfs3_lookup(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct fsal_obj_handle *obj_dir = NULL; struct fsal_obj_handle *obj_file = NULL; fsal_status_t fsal_status; char *name = NULL; int rc = NFS_REQ_OK; struct attrlist attrs; LOOKUP3resfail *resfail = &res->res_lookup3.LOOKUP3res_u.resfail; LOOKUP3resok *resok = &res->res_lookup3.LOOKUP3res_u.resok; /* We have the option of not sending attributes, so set ATTR_RDATTR_ERR. */ fsal_prepare_attrs(&attrs, ATTRS_NFS3 | ATTR_RDATTR_ERR); if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; name = arg->arg_lookup3.what.name; nfs_FhandleToStr(req->rq_msg.cb_vers, &(arg->arg_lookup3.what.dir), NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs_Lookup handle: %s name: %s", str, name); } /* to avoid setting it on each error case */ resfail->dir_attributes.attributes_follow = FALSE; obj_dir = nfs3_FhandleToCache(&arg->arg_lookup3.what.dir, &res->res_lookup3.status, &rc); if (obj_dir == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } name = arg->arg_lookup3.what.name; fsal_status = fsal_lookup(obj_dir, name, &obj_file, &attrs); if (FSAL_IS_ERROR(fsal_status)) { /* If we are here, there was an error */ if (nfs_RetryableError(fsal_status.major)) { rc = NFS_REQ_DROP; goto out; } res->res_lookup3.status = nfs3_Errno_status(fsal_status); nfs_SetPostOpAttr(obj_dir, &resfail->dir_attributes, NULL); } else { /* Build FH */ if (nfs3_FSALToFhandle(true, &resok->object, obj_file, op_ctx->ctx_export)) { /* Build entry attributes */ nfs_SetPostOpAttr(obj_file, &resok->obj_attributes, &attrs); /* Build directory attributes */ nfs_SetPostOpAttr(obj_dir, &resok->dir_attributes, NULL); res->res_lookup3.status = NFS3_OK; } else { res->res_lookup3.status = NFS3ERR_BADHANDLE; } } rc = NFS_REQ_OK; out: /* Release the attributes. */ fsal_release_attrs(&attrs); /* return references */ if (obj_dir) obj_dir->obj_ops.put_ref(obj_dir); if (obj_file) obj_file->obj_ops.put_ref(obj_file); return rc; } /* nfs3_lookup */ /** * @brief Free the result structure allocated for nfs3_lookup. * * This function frees the result structure allocated for nfs3_lookup. * * @param[in,out] res Result structure * */ void nfs3_lookup_free(nfs_res_t *res) { if (res->res_lookup3.status == NFS3_OK) { gsh_free( res->res_lookup3.LOOKUP3res_u.resok.object.data.data_val); } } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs3_mkdir.c000066400000000000000000000132241324272410200213140ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs3_mkdir.c * @brief Evrythinhg you need to handle NFSv3 MKDIR */ #include "config.h" #include #include #include #include #include /* for having FNDELAY */ #include "hashtable.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_creds.h" #include "nfs_proto_functions.h" #include "nfs_convert.h" #include "nfs_file_handle.h" #include "nfs_proto_tools.h" #include "export_mgr.h" /** * * @brief The NFSPROC3_MKDIR * * Implements the NFSPROC3_MKDIR. * * @param[in] arg NFS arguments 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_mkdir(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { const char *dir_name = arg->arg_mkdir3.where.name; struct fsal_obj_handle *dir_obj = NULL; struct fsal_obj_handle *parent_obj = NULL; pre_op_attr pre_parent = { .attributes_follow = false }; fsal_status_t fsal_status = {0, 0}; int rc = NFS_REQ_OK; struct attrlist sattr, attrs; MKDIR3resfail *resfail = &res->res_mkdir3.MKDIR3res_u.resfail; MKDIR3resok *resok = &res->res_mkdir3.MKDIR3res_u.resok; /* We have the option of not sending attributes, so set ATTR_RDATTR_ERR. */ fsal_prepare_attrs(&attrs, ATTRS_NFS3 | ATTR_RDATTR_ERR); memset(&sattr, 0, sizeof(sattr)); if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; nfs_FhandleToStr(req->rq_msg.cb_vers, &(arg->arg_mkdir3.where.dir), NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling NFS3_MKDIR handle: %s name: %s", str, dir_name); } /* to avoid setting it on each error case */ resfail->dir_wcc.before.attributes_follow = FALSE; resfail->dir_wcc.after.attributes_follow = FALSE; parent_obj = nfs3_FhandleToCache(&arg->arg_mkdir3.where.dir, &res->res_mkdir3.status, &rc); if (parent_obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } /* Sanity checks */ if (parent_obj->type != DIRECTORY) { res->res_mkdir3.status = NFS3ERR_NOTDIR; rc = NFS_REQ_OK; goto out; } /* if quota support is active, then we should check is the FSAL allows inode creation or not */ fsal_status = op_ctx->fsal_export->exp_ops.check_quota(op_ctx->fsal_export, op_ctx->ctx_export->fullpath, FSAL_QUOTA_INODES); if (FSAL_IS_ERROR(fsal_status)) { res->res_mkdir3.status = NFS3ERR_DQUOT; rc = NFS_REQ_OK; goto out; } if (dir_name == NULL || *dir_name == '\0') { fsal_status = fsalstat(ERR_FSAL_INVAL, 0); goto out_fail; } if (nfs3_Sattr_To_FSALattr(&sattr, &arg->arg_mkdir3.attributes) == 0) { fsal_status = fsalstat(ERR_FSAL_INVAL, 0); goto out_fail; } squash_setattr(&sattr); if (!(sattr.valid_mask & ATTR_MODE)) { /* Make sure mode is set. */ sattr.mode = 0; sattr.valid_mask |= ATTR_MODE; } /* Try to create the directory */ fsal_status = fsal_create(parent_obj, dir_name, DIRECTORY, &sattr, NULL, &dir_obj, &attrs); /* Release the attributes (may release an inherited ACL) */ fsal_release_attrs(&sattr); if (FSAL_IS_ERROR(fsal_status)) goto out_fail; /* Build file handle */ if (!nfs3_FSALToFhandle(true, &resok->obj.post_op_fh3_u.handle, dir_obj, op_ctx->ctx_export)) { res->res_mkdir3.status = NFS3ERR_BADHANDLE; rc = NFS_REQ_OK; goto out; } /* Set Post Op Fh3 structure */ resok->obj.handle_follows = true; /* Build entry attributes */ nfs_SetPostOpAttr(dir_obj, &resok->obj_attributes, &attrs); /* Build Weak Cache Coherency data */ nfs_SetWccData(&pre_parent, parent_obj, &resok->dir_wcc); res->res_mkdir3.status = NFS3_OK; rc = NFS_REQ_OK; goto out; out_fail: res->res_mkdir3.status = nfs3_Errno_status(fsal_status); nfs_SetWccData(&pre_parent, parent_obj, &resfail->dir_wcc); if (nfs_RetryableError(fsal_status.major)) rc = NFS_REQ_DROP; out: /* Release the attributes. */ fsal_release_attrs(&attrs); /* return references */ if (dir_obj) dir_obj->obj_ops.put_ref(dir_obj); if (parent_obj) parent_obj->obj_ops.put_ref(parent_obj); return rc; } /** * @brief Free the result structure allocated for nfs3_mkdir. * * This function frees the result structure allocated for nfs3_mkdir. * * @param[in,out] res Result structure * */ void nfs3_mkdir_free(nfs_res_t *res) { nfs_fh3 *handle = &res->res_mkdir3.MKDIR3res_u.resok.obj.post_op_fh3_u.handle; if ((res->res_mkdir3.status == NFS3_OK) && (res->res_mkdir3.MKDIR3res_u.resok.obj.handle_follows)) { gsh_free(handle->data.data_val); } } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs3_mknod.c000066400000000000000000000156021324272410200213200ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * @file nfs3_mknod.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. * * */ #include "config.h" #include #include #include #include #include /* for having FNDELAY */ #include "hashtable.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_creds.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_convert.h" #include "export_mgr.h" /** * @brief Implements NFSPROC3_MKNOD * * Implements NFSPROC3_MKNOD. * * @param[in] arg NFS arguments 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_mknod(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct fsal_obj_handle *parent_obj = NULL; struct fsal_obj_handle *node_obj = NULL; pre_op_attr pre_parent; object_file_type_t nodetype; const char *file_name = arg->arg_mknod3.where.name; int rc = NFS_REQ_OK; fsal_status_t fsal_status; struct attrlist sattr, attrs; MKNOD3resfail *resfail = &res->res_mknod3.MKNOD3res_u.resfail; /* We have the option of not sending attributes, so set ATTR_RDATTR_ERR. */ fsal_prepare_attrs(&attrs, ATTRS_NFS3 | ATTR_RDATTR_ERR); memset(&sattr, 0, sizeof(sattr)); if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; const char *fname; fname = (file_name == NULL || *file_name == '\0') ? "" : file_name; sprint_fhandle3(str, &(arg->arg_mknod3.where.dir)); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling NFS3_MKNOD handle: %s name: %s", str, fname); } /* to avoid setting them on each error case */ resfail->dir_wcc.before.attributes_follow = FALSE; resfail->dir_wcc.after.attributes_follow = FALSE; /* retrieve parent entry */ parent_obj = nfs3_FhandleToCache(&arg->arg_mknod3.where.dir, &res->res_mknod3.status, &rc); if (parent_obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } nfs_SetPreOpAttr(parent_obj, &pre_parent); /* Sanity checks: new node name must be non-null; parent must be a directory. */ if (parent_obj->type != DIRECTORY) { res->res_mknod3.status = NFS3ERR_NOTDIR; rc = NFS_REQ_OK; goto out; } if (file_name == NULL || *file_name == '\0') { res->res_mknod3.status = NFS3ERR_INVAL; rc = NFS_REQ_OK; goto out; } switch (arg->arg_mknod3.what.type) { case NF3CHR: case NF3BLK: if (nfs3_Sattr_To_FSALattr( &sattr, &arg->arg_mknod3.what.mknoddata3_u.device.dev_attributes) == 0) { res->res_mknod3.status = NFS3ERR_INVAL; rc = NFS_REQ_OK; goto out; } sattr.rawdev.major = arg->arg_mknod3.what.mknoddata3_u.device.spec.specdata1; sattr.rawdev.minor = arg->arg_mknod3.what.mknoddata3_u.device.spec.specdata2; sattr.valid_mask |= ATTR_RAWDEV; break; case NF3FIFO: case NF3SOCK: if (nfs3_Sattr_To_FSALattr( &sattr, &arg->arg_mknod3.what.mknoddata3_u.pipe_attributes) == 0) { res->res_mknod3.status = NFS3ERR_INVAL; rc = NFS_REQ_OK; goto out; } break; default: res->res_mknod3.status = NFS3ERR_BADTYPE; rc = NFS_REQ_OK; goto out; } switch (arg->arg_mknod3.what.type) { case NF3CHR: nodetype = CHARACTER_FILE; break; case NF3BLK: nodetype = BLOCK_FILE; break; case NF3FIFO: nodetype = FIFO_FILE; break; case NF3SOCK: nodetype = SOCKET_FILE; break; default: res->res_mknod3.status = NFS3ERR_BADTYPE; rc = NFS_REQ_OK; goto out; } /* if quota support is active, then we should check is the FSAL allows inode creation or not */ fsal_status = op_ctx->fsal_export->exp_ops.check_quota(op_ctx->fsal_export, op_ctx->ctx_export->fullpath, FSAL_QUOTA_INODES); if (FSAL_IS_ERROR(fsal_status)) { res->res_mknod3.status = NFS3ERR_DQUOT; return NFS_REQ_OK; } squash_setattr(&sattr); if (!(sattr.valid_mask & ATTR_MODE)) { /* Make sure mode is set. */ sattr.mode = 0; sattr.valid_mask |= ATTR_MODE; } /* Try to create it */ fsal_status = fsal_create(parent_obj, file_name, nodetype, &sattr, NULL, &node_obj, &attrs); /* Release the attributes (may release an inherited ACL) */ fsal_release_attrs(&sattr); if (FSAL_IS_ERROR(fsal_status)) goto out_fail; MKNOD3resok * const rok = &res->res_mknod3.MKNOD3res_u.resok; /* Build file handle */ if (!nfs3_FSALToFhandle(true, &rok->obj.post_op_fh3_u.handle, node_obj, op_ctx->ctx_export)) { res->res_mknod3.status = NFS3ERR_BADHANDLE; rc = NFS_REQ_OK; goto out; } /* Set Post Op Fh3 structure */ rok->obj.handle_follows = TRUE; /* Build entry attributes */ nfs_SetPostOpAttr(node_obj, &rok->obj_attributes, &attrs); /* Build Weak Cache Coherency data */ nfs_SetWccData(&pre_parent, parent_obj, &rok->dir_wcc); res->res_mknod3.status = NFS3_OK; rc = NFS_REQ_OK; goto out; out_fail: res->res_mknod3.status = nfs3_Errno_status(fsal_status); nfs_SetWccData(&pre_parent, parent_obj, &resfail->dir_wcc); if (nfs_RetryableError(fsal_status.major)) rc = NFS_REQ_DROP; out: /* Release the attributes. */ fsal_release_attrs(&attrs); /* return references */ if (parent_obj) parent_obj->obj_ops.put_ref(parent_obj); if (node_obj) node_obj->obj_ops.put_ref(node_obj); return rc; } /* nfs3_mknod */ /** * @brief Free the result structure allocated for nfs3_mknod. * * This function frees the result structure allocated for nfs3_mknod. * * @param[in,out] res The result structure. * */ void nfs3_mknod_free(nfs_res_t *res) { nfs_fh3 *handle = &res->res_mknod3.MKNOD3res_u.resok.obj.post_op_fh3_u.handle; if ((res->res_mknod3.status == NFS3_OK) && (res->res_mknod3.MKNOD3res_u.resok.obj.handle_follows)) { gsh_free(handle->data.data_val); } } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs3_pathconf.c000066400000000000000000000071301324272410200220070ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs3_pathconf.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. */ #include "config.h" #include #include #include #include #include /* for having FNDELAY */ #include "hashtable.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" /** * @brief Implements NFSPROC3_PATHCONF * * Implements NFSPROC3_PATHCONF. * * @param[in] arg NFS arguments 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_pathconf(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct fsal_obj_handle *obj = NULL; int rc = NFS_REQ_OK; struct fsal_export *exp_hdl = op_ctx->fsal_export; PATHCONF3resfail *resfail = &res->res_pathconf3.PATHCONF3res_u.resfail; PATHCONF3resok *resok = &res->res_pathconf3.PATHCONF3res_u.resok; if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; sprint_fhandle3(str, &(arg->arg_pathconf3.object)); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling NFS3_PATHCONF handle: %s", str); } /* to avoid setting it on each error case */ resfail->obj_attributes.attributes_follow = FALSE; /* Convert file handle into a fsal_handle */ obj = nfs3_FhandleToCache(&arg->arg_pathconf3.object, &res->res_pathconf3.status, &rc); if (obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } resok->linkmax = exp_hdl->exp_ops.fs_maxlink(exp_hdl); resok->name_max = exp_hdl->exp_ops.fs_maxnamelen(exp_hdl); resok->no_trunc = exp_hdl->exp_ops.fs_supports(exp_hdl, fso_no_trunc); resok->chown_restricted = exp_hdl->exp_ops.fs_supports(exp_hdl, fso_chown_restricted); resok->case_insensitive = exp_hdl->exp_ops.fs_supports(exp_hdl, fso_case_insensitive); resok->case_preserving = exp_hdl->exp_ops.fs_supports(exp_hdl, fso_case_preserving); /* Build post op file attributes */ nfs_SetPostOpAttr(obj, &resok->obj_attributes, NULL); out: if (obj) obj->obj_ops.put_ref(obj); return rc; } /* nfs3_pathconf */ /** * @brief Free the result structure allocated for nfs3_pathconf. * * This function free the result structure allocated for nfs3_pathconf. * * @param[in,out] res Result structure. * */ void nfs3_pathconf_free(nfs_res_t *res) { /* Nothing to do here */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs3_read.c000066400000000000000000000157431324272410200211310ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * @file nfs3_read.c * @brief Everything you need to read. */ #include "config.h" #include #include #include #include #include /* for having FNDELAY */ #include "hashtable.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_convert.h" #include "server_stats.h" #include "export_mgr.h" #include "sal_functions.h" static void nfs_read_ok(struct svc_req *req, nfs_res_t *res, char *data, uint32_t read_size, struct fsal_obj_handle *obj, int eof) { if ((read_size == 0) && (data != NULL)) { gsh_free(data); data = NULL; } /* Build Post Op Attributes */ nfs_SetPostOpAttr(obj, &res->res_read3.READ3res_u.resok.file_attributes, NULL); res->res_read3.READ3res_u.resok.eof = eof; res->res_read3.READ3res_u.resok.count = read_size; res->res_read3.READ3res_u.resok.data.data_val = data; res->res_read3.READ3res_u.resok.data.data_len = read_size; res->res_read3.status = NFS3_OK; } /** * * @brief The NFSPROC3_READ * * Implements the NFSPROC3_READ function. * * @param[in] arg NFS arguments 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_read(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct fsal_obj_handle *obj; pre_op_attr pre_attr; fsal_status_t fsal_status = {0, 0}; size_t size = 0; size_t read_size = 0; uint64_t offset = 0; void *data = NULL; bool eof_met = false; int rc = NFS_REQ_OK; uint64_t MaxRead = atomic_fetch_uint64_t(&op_ctx->ctx_export->MaxRead); uint64_t MaxOffsetRead = atomic_fetch_uint64_t(&op_ctx->ctx_export->MaxOffsetRead); READ3resfail *resfail = &res->res_read3.READ3res_u.resfail; if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; offset = arg->arg_read3.offset; size = arg->arg_read3.count; nfs_FhandleToStr(req->rq_msg.cb_vers, &arg->arg_read3.file, NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs_Read handle: %s start: %" PRIu64 " len: %zu", str, offset, size); } /* to avoid setting it on each error case */ resfail->file_attributes.attributes_follow = FALSE; /* initialize for read of size 0 */ res->res_read3.READ3res_u.resok.eof = FALSE; res->res_read3.READ3res_u.resok.count = 0; res->res_read3.READ3res_u.resok.data.data_val = NULL; res->res_read3.READ3res_u.resok.data.data_len = 0; res->res_read3.status = NFS3_OK; obj = nfs3_FhandleToCache(&arg->arg_read3.file, &res->res_read3.status, &rc); if (obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } nfs_SetPreOpAttr(obj, &pre_attr); fsal_status = obj->obj_ops.test_access(obj, FSAL_READ_ACCESS, NULL, NULL, true); if (fsal_status.major == ERR_FSAL_ACCESS) { /* Test for execute permission */ fsal_status = fsal_access(obj, FSAL_MODE_MASK_SET(FSAL_X_OK) | FSAL_ACE4_MASK_SET (FSAL_ACE_PERM_EXECUTE)); } if (FSAL_IS_ERROR(fsal_status)) { res->res_read3.status = nfs3_Errno_status(fsal_status); rc = NFS_REQ_OK; goto out; } /* Sanity check: read only from a regular file */ if (obj->type != REGULAR_FILE) { if (obj->type == DIRECTORY) res->res_read3.status = NFS3ERR_ISDIR; else res->res_read3.status = NFS3ERR_INVAL; rc = NFS_REQ_OK; goto out; } /* Extract the argument from the request */ offset = arg->arg_read3.offset; size = arg->arg_read3.count; /* do not exceed maxium READ offset if set */ if (MaxOffsetRead < UINT64_MAX) { LogFullDebug(COMPONENT_NFSPROTO, "Read offset=%" PRIu64 " count=%zd MaxOffSet=%" PRIu64, offset, size, MaxOffsetRead); if ((offset + size) > MaxOffsetRead) { LogEvent(COMPONENT_NFSPROTO, "A client tryed to violate max file size %" PRIu64 " for exportid #%hu", MaxOffsetRead, op_ctx->ctx_export->export_id); res->res_read3.status = NFS3ERR_FBIG; nfs_SetPostOpAttr(obj, &resfail->file_attributes, NULL); rc = NFS_REQ_OK; goto out; } } /* We should not exceed the FSINFO rtmax field for the size */ if (size > MaxRead) { /* The client asked for too much, normally this should not happen because the client is calling nfs_Fsinfo at mount time and so is aware of the server maximum write size */ size = MaxRead; } if (size == 0) { nfs_read_ok(req, res, NULL, 0, obj, 0); rc = NFS_REQ_OK; goto out; } else { data = gsh_malloc(size); /* Check for delegation conflict. */ if (state_deleg_conflict(obj, false)) { res->res_read3.status = NFS3ERR_JUKEBOX; rc = NFS_REQ_OK; gsh_free(data); goto out; } /* Call the new fsal_read2 */ /** @todo for now pass NULL state */ fsal_status = fsal_read2(obj, true, NULL, offset, size, &read_size, data, &eof_met, NULL); if (!FSAL_IS_ERROR(fsal_status)) { nfs_read_ok(req, res, data, read_size, obj, eof_met); rc = NFS_REQ_OK; goto out; } gsh_free(data); } /* If we are here, there was an error */ if (nfs_RetryableError(fsal_status.major)) { rc = NFS_REQ_DROP; goto out; } res->res_read3.status = nfs3_Errno_status(fsal_status); nfs_SetPostOpAttr(obj, &resfail->file_attributes, NULL); rc = NFS_REQ_OK; out: /* return references */ if (obj) obj->obj_ops.put_ref(obj); server_stats_io_done(size, read_size, (rc == NFS_REQ_OK) ? true : false, false); return rc; } /* nfs3_read */ /** * @brief Free the result structure allocated for nfs3_read. * * This function frees the result structure allocated for nfs3_read. * * @param[in,out] res Result structure */ void nfs3_read_free(nfs_res_t *res) { if ((res->res_read3.status == NFS3_OK) && (res->res_read3.READ3res_u.resok.data.data_len != 0)) { gsh_free(res->res_read3.READ3res_u.resok.data.data_val); } } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs3_readdir.c000066400000000000000000000276231324272410200216300ustar00rootroot00000000000000 /* * vimf:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs3_readdir.c * @brief Everything you need for a simple READDIR * */ #include "config.h" #include #include #include #include #include "hashtable.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "export_mgr.h" #include "nfs_proto_functions.h" #include "nfs_convert.h" #include "nfs_proto_tools.h" #include fsal_errors_t nfs3_readdir_callback(void *opaque, struct fsal_obj_handle *obj, const struct attrlist *attr, uint64_t mounted_on_fileid, uint64_t cookie, enum cb_state cb_state); static void free_entry3s(entry3 *entry3s); /** * @brief Opaque bookkeeping structure for NFSv3 readdir * * This structure keeps track of the process of writing out an NFSv3 * READDIR response between calls to nfs3_readdir_callback. */ struct nfs3_readdir_cb_data { entry3 *entries; /*< The array holding individual entries */ size_t mem_left; /*< The amount of memory remaining before we hit maxcount */ size_t count; /*< The count of complete entries stored in the buffer */ size_t total_entries; /*< The total number of entries in the array */ nfsstat3 error; /*< Set to a value other than NFS_OK if the callback function finds a fatal error. */ }; static nfsstat3 nfs_readdir_dot_entry(struct fsal_obj_handle *obj, const char *name, uint64_t cookie, helper_readdir_cb cb, struct nfs3_readdir_cb_data *tracker) { struct fsal_readdir_cb_parms cb_parms; fsal_status_t fsal_status; cb_parms.opaque = tracker; cb_parms.name = name; cb_parms.attr_allowed = true; cb_parms.in_result = true; /* NFS v3 READDIR does not use attributes, so pass NULL */ fsal_status.major = cb(&cb_parms, obj, NULL, 0, cookie, CB_ORIGINAL); if (FSAL_IS_ERROR(fsal_status)) return nfs3_Errno_status(fsal_status); else return tracker->error; } /** * * @brief The NFSPROC3_READDIR * * Implements the NFSPROC3_READDIR function. * * @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_readdir(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct fsal_obj_handle *dir_obj = NULL; struct fsal_obj_handle *parent_dir_obj = NULL; unsigned long count = 0; uint64_t cookie = 0; uint64_t fsal_cookie = 0; cookieverf3 cookie_verifier; unsigned int num_entries = 0; unsigned long estimated_num_entries = 0; object_file_type_t dir_filetype = 0; bool eod_met = false; fsal_status_t fsal_status = {0, 0}; fsal_status_t fsal_status_gethandle = {0, 0}; int rc = NFS_REQ_OK; struct nfs3_readdir_cb_data tracker = { NULL }; bool use_cookie_verifier = op_ctx_export_has_option( EXPORT_OPTION_USE_COOKIE_VERIFIER); READDIR3resfail *resfail = &res->res_readdir3.READDIR3res_u.resfail; if (isDebug(COMPONENT_NFSPROTO) || isDebug(COMPONENT_NFS_READDIR)) { char str[LEN_FH_STR]; nfs_FhandleToStr(req->rq_msg.cb_vers, &(arg->arg_readdir3.dir), NULL, str); LogDebugAlt(COMPONENT_NFSPROTO, COMPONENT_NFS_READDIR, "REQUEST PROCESSING: Calling nfs_Readdir handle: %s", str); } READDIR3resok * const RES_READDIR3_OK = &res->res_readdir3.READDIR3res_u.resok; /* to avoid setting it on each error case */ resfail->dir_attributes.attributes_follow = FALSE; /* Look up object for filehandle */ dir_obj = nfs3_FhandleToCache(&(arg->arg_readdir3.dir), &(res->res_readdir3.status), &rc); if (dir_obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } /* Extract the filetype */ dir_filetype = dir_obj->type; /* Sanity checks -- must be a directory */ if (dir_filetype != DIRECTORY) { res->res_readdir3.status = NFS3ERR_NOTDIR; rc = NFS_REQ_OK; goto out; } /* Parse out request arguments and decide how many entries we * want. For NFSv3, deal with the cookie verifier. */ count = arg->arg_readdir3.count; cookie = arg->arg_readdir3.cookie; estimated_num_entries = MIN(count / (sizeof(entry3) - sizeof(char *)), 120); LogDebug(COMPONENT_NFS_READDIR, "---> NFS3_READDIR: count=%lu cookie=%" PRIu64 " estimated_num_entries=%lu", count, cookie, estimated_num_entries); if (estimated_num_entries == 0) { res->res_readdir3.status = NFS3ERR_TOOSMALL; rc = NFS_REQ_OK; goto out; } /* To make or check the cookie verifier */ memset(cookie_verifier, 0, sizeof(cookieverf3)); /* If cookie verifier is used, then a * non-trivial value is returned to the * client. * * This value is the ctime of the directory. If verifier is * unused (as in many NFS Servers) then only a set of zeros * is returned (trivial value). */ if (use_cookie_verifier) { struct attrlist attrs; fsal_prepare_attrs(&attrs, ATTR_CTIME); fsal_status = dir_obj->obj_ops.getattrs(dir_obj, &attrs); if (FSAL_IS_ERROR(fsal_status)) { res->res_readdir3.status = nfs3_Errno_status(fsal_status); LogDebug(COMPONENT_NFS_READDIR, "getattrs returned %s", msg_fsal_err(fsal_status.major)); goto out; } memcpy(cookie_verifier, &attrs.ctime.tv_sec, sizeof(attrs.ctime.tv_sec)); /* Done with the attrs */ fsal_release_attrs(&attrs); } if (cookie != 0 && use_cookie_verifier) { /* Not the first call, so we have to check the cookie * verifier */ if (memcmp(cookie_verifier, arg->arg_readdir3.cookieverf, NFS3_COOKIEVERFSIZE) != 0) { res->res_readdir3.status = NFS3ERR_BAD_COOKIE; rc = NFS_REQ_OK; goto out; } } tracker.entries = gsh_calloc(estimated_num_entries, sizeof(entry3)); tracker.total_entries = estimated_num_entries; tracker.mem_left = count - sizeof(READDIR3resok); tracker.count = 0; tracker.error = NFS3_OK; /* Adjust the cookie we supply to fsal */ if (cookie > 2) { /* it is not the cookie for "." nor ".." */ fsal_cookie = cookie; } else { fsal_cookie = 0; } /* Fills "." */ if (cookie == 0) { res->res_readdir3.status = nfs_readdir_dot_entry(dir_obj, ".", 1, nfs3_readdir_callback, &tracker); if (res->res_readdir3.status != NFS3_OK) { rc = NFS_REQ_OK; goto out; } } /* Fills ".." */ if ((cookie <= 1) && (estimated_num_entries > 1)) { /* Get parent pentry */ fsal_status_gethandle = fsal_lookupp(dir_obj, &parent_dir_obj, NULL); if (parent_dir_obj == NULL) { res->res_readdir3.status = nfs3_Errno_status(fsal_status_gethandle); rc = NFS_REQ_OK; goto out; } res->res_readdir3.status = nfs_readdir_dot_entry(parent_dir_obj, "..", 2, nfs3_readdir_callback, &tracker); if (res->res_readdir3.status != NFS3_OK) { rc = NFS_REQ_OK; goto out; } parent_dir_obj->obj_ops.put_ref(parent_dir_obj); parent_dir_obj = NULL; } /* Call readdir */ fsal_status = fsal_readdir(dir_obj, fsal_cookie, &num_entries, &eod_met, 0, /* no attr */ nfs3_readdir_callback, &tracker); if (FSAL_IS_ERROR(fsal_status)) { if (nfs_RetryableError(fsal_status.major)) { rc = NFS_REQ_DROP; goto out; } res->res_readdir3.status = nfs3_Errno_status(fsal_status); nfs_SetPostOpAttr(dir_obj, &resfail->dir_attributes, NULL); goto out; } if (tracker.error != NFS3_OK) { res->res_readdir3.status = tracker.error; nfs_SetPostOpAttr(dir_obj, &resfail->dir_attributes, NULL); goto out; } LogDebug(COMPONENT_NFS_READDIR, "-- Readdir -> Call to fsal_readdir(cookie=%" PRIu64 ")", fsal_cookie); if ((num_entries == 0) && (cookie > 1)) { RES_READDIR3_OK->reply.entries = NULL; RES_READDIR3_OK->reply.eof = TRUE; } else { RES_READDIR3_OK->reply.entries = tracker.entries; RES_READDIR3_OK->reply.eof = eod_met; } nfs_SetPostOpAttr(dir_obj, &RES_READDIR3_OK->dir_attributes, NULL); memcpy(RES_READDIR3_OK->cookieverf, cookie_verifier, sizeof(cookieverf3)); res->res_readdir3.status = NFS3_OK; rc = NFS_REQ_OK; out: /* return references */ if (dir_obj) dir_obj->obj_ops.put_ref(dir_obj); if (parent_dir_obj) parent_dir_obj->obj_ops.put_ref(parent_dir_obj); /* Deallocate memory in the event of an error */ if (((res->res_readdir3.status != NFS3_OK) || (rc != NFS_REQ_OK) || ((num_entries == 0) && (cookie > 1))) && (tracker.entries != NULL)) { free_entry3s(tracker.entries); RES_READDIR3_OK->reply.entries = NULL; } return rc; } /* nfs3_readdir */ /** * @brief Free the result structure allocated for nfs3_readdir * * @param[in,out] resp Result structure * */ void nfs3_readdir_free(nfs_res_t *resp) { if (resp->res_readdir3.status == NFS3_OK) { free_entry3s( resp->res_readdir3.READDIR3res_u.resok.reply.entries); } } /** * @brief Populate entry3s when called from fsal_readdir * * This function is a callback passed to fsal_readdir. It * fills in a pre-allocated array of entry3 structures and allocates * space for the name and attributes. This space must be freed. * * @param opaque [in] Pointer to a struct nfs3_readdir_cb_data that is * gives the location of the array and other * bookeeping information * @param name [in] The filename for the current obj * @param handle [in] The current obj's filehandle * @param attrs [in] The current obj's attributes * @param cookie [in] The readdir cookie for the current obj */ fsal_errors_t nfs3_readdir_callback(void *opaque, struct fsal_obj_handle *obj, const struct attrlist *attr, uint64_t mounted_on_fileid, uint64_t cookie, enum cb_state cb_state) { /* Not-so-opaque pointer to callback data` */ struct fsal_readdir_cb_parms *cb_parms = opaque; struct nfs3_readdir_cb_data *tracker = cb_parms->opaque; /* Length of the current filename */ size_t namelen = strlen(cb_parms->name); entry3 *e3 = tracker->entries + tracker->count; size_t need = sizeof(entry3) + ((namelen + 3) & ~3) + 4 - sizeof(char *) - sizeof(entry3 *); if (tracker->count == tracker->total_entries) { cb_parms->in_result = false; return ERR_FSAL_NO_ERROR; } if (tracker->mem_left < need) { if (tracker->count == 0) tracker->error = NFS3ERR_TOOSMALL; cb_parms->in_result = false; return ERR_FSAL_NO_ERROR; } e3->fileid = obj->fileid; e3->name = gsh_strdup(cb_parms->name); e3->cookie = cookie; if (tracker->count > 0) tracker->entries[tracker->count - 1].nextentry = e3; tracker->mem_left -= need; ++(tracker->count); cb_parms->in_result = true; return ERR_FSAL_NO_ERROR; } /* */ /** * @brief Clean up memory allocated to serve NFSv3 READDIR * * This function traverses the list of entries, freeing all names * allocated in the callback function, then frees the list itself. * * @param entry3s [in] Pointer to first obj */ static void free_entry3s(entry3 *entry3s) { entry3 *entry = NULL; for (entry = entry3s; entry != NULL; entry = entry->nextentry) gsh_free(entry->name); gsh_free(entry3s); } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs3_readdirplus.c000066400000000000000000000345061324272410200225320ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs3_readdirplus.c * @brief Everything you need for the NFSv3 readdirplus operation */ #include "config.h" #include #include #include #include #include "hashtable.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "export_mgr.h" #include "nfs_proto_functions.h" #include "nfs_convert.h" #include "nfs_file_handle.h" #include "nfs_proto_tools.h" #include fsal_errors_t nfs3_readdirplus_callback(void *opaque, struct fsal_obj_handle *obj, const struct attrlist *attr, uint64_t mounted_on_fileid, uint64_t cookie, enum cb_state cb_state); static void free_entryplus3s(entryplus3 *entryplus3s); /** * @brief Opaque bookkeeping structure for NFSPROC3_READDIRPLUS * * This structure keeps track of the process of writing out an NFSv3 * READDIRPLUS response between calls to nfs3_readdirplus_callback. */ struct nfs3_readdirplus_cb_data { entryplus3 *entries; /*< The array holding individual entries */ size_t mem_left; /*< The amount of memory remaining before we hit maxcount */ size_t count; /*< The count of complete entries stored in the buffer */ size_t total_entries; /*< The number of entires we allocated for the array. */ nfsstat3 error; /*< Set to a value other than NFS_OK if the callback function finds a fatal error. */ }; static nfsstat3 nfs_readdir_dot_entry(struct fsal_obj_handle *obj, const char *name, uint64_t cookie, helper_readdir_cb cb, struct nfs3_readdirplus_cb_data *tracker, struct attrlist *attrs) { struct fsal_readdir_cb_parms cb_parms; fsal_status_t fsal_status; cb_parms.opaque = tracker; cb_parms.name = name; cb_parms.attr_allowed = true; cb_parms.in_result = true; fsal_status.major = cb(&cb_parms, obj, attrs, 0, cookie, CB_ORIGINAL); if (FSAL_IS_ERROR(fsal_status)) return nfs3_Errno_status(fsal_status); else return tracker->error; } /** * @brief The NFSPROC3_READDIRPLUS * * Implements the NFSPROC3_READDIRPLUS function * * @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 successfull * @retval NFS_REQ_DROP if failed but retryable * @retval NFS_REQ_FAILED if failed and not retryable */ int nfs3_readdirplus(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct fsal_obj_handle *dir_obj = NULL; uint64_t begin_cookie = 0; uint64_t fsal_cookie = 0; cookieverf3 cookie_verifier; unsigned int num_entries = 0; unsigned long estimated_num_entries = 0; object_file_type_t dir_filetype = 0; bool eod_met = false; fsal_status_t fsal_status = {0, 0}; fsal_status_t fsal_status_gethandle = {0, 0}; int rc = NFS_REQ_OK; struct nfs3_readdirplus_cb_data tracker = { .entries = NULL, .mem_left = 0, .count = 0, .error = NFS3_OK, }; struct attrlist attrs_dir, attrs_parent; bool use_cookie_verifier = op_ctx_export_has_option( EXPORT_OPTION_USE_COOKIE_VERIFIER); READDIRPLUS3resfail *resfail = &res->res_readdirplus3.READDIRPLUS3res_u.resfail; READDIRPLUS3resok *resok = &res->res_readdirplus3.READDIRPLUS3res_u.resok; /* We have the option of not sending attributes, so set ATTR_RDATTR_ERR. */ fsal_prepare_attrs(&attrs_dir, ATTRS_NFS3 | ATTR_RDATTR_ERR); fsal_prepare_attrs(&attrs_parent, ATTRS_NFS3 | ATTR_RDATTR_ERR); if (isDebug(COMPONENT_NFSPROTO) || isDebug(COMPONENT_NFS_READDIR)) { char str[LEN_FH_STR]; sprint_fhandle3(str, &(arg->arg_readdirplus3.dir)); LogDebugAlt(COMPONENT_NFSPROTO, COMPONENT_NFS_READDIR, "REQUEST PROCESSING: Calling NFS3_READDIRPLUS handle: %s", str); } /* to avoid setting it on each error case */ res->res_readdir3.READDIR3res_u.resfail.dir_attributes.attributes_follow = FALSE; if (op_ctx_export_has_option(EXPORT_OPTION_NO_READDIR_PLUS)) { res->res_readdirplus3.status = NFS3ERR_NOTSUPP; LogDebug(COMPONENT_NFS_READDIR, "Request not supported"); goto out; } tracker.mem_left = (arg->arg_readdirplus3.maxcount * 9) / 10; begin_cookie = arg->arg_readdirplus3.cookie; tracker.mem_left -= sizeof(READDIRPLUS3resok); /* Estimate assuming that we're going to send no names and no handles. * Don't count space for pointers for nextentry or * name_handle.data.data_val in entryplus3 */ estimated_num_entries = MIN((tracker.mem_left + sizeof(entryplus3 *)) / (sizeof(entryplus3) - sizeof(char *) * 2), 50); tracker.total_entries = estimated_num_entries; LogDebug(COMPONENT_NFS_READDIR, "NFS3_READDIRPLUS: dircount=%u begin_cookie=%" PRIu64 " estimated_num_entries=%lu, mem_left=%zd", arg->arg_readdirplus3.dircount, begin_cookie, estimated_num_entries, tracker.mem_left); /* Convert file handle into a vnode */ dir_obj = nfs3_FhandleToCache(&(arg->arg_readdirplus3.dir), &(res->res_readdirplus3.status), &rc); if (dir_obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } /* Extract the filetype */ dir_filetype = dir_obj->type; /* Sanity checks -- must be a directory */ if (dir_filetype != DIRECTORY) { res->res_readdirplus3.status = NFS3ERR_NOTDIR; rc = NFS_REQ_OK; goto out; } /* Fetch the attributes for use later. */ fsal_status = dir_obj->obj_ops.getattrs(dir_obj, &attrs_dir); if (FSAL_IS_ERROR(fsal_status)) { res->res_readdirplus3.status = nfs3_Errno_status(fsal_status); LogDebug(COMPONENT_NFS_READDIR, "Error %s fetching attributes", fsal_err_txt(fsal_status)); goto out; } memset(cookie_verifier, 0, sizeof(cookieverf3)); /* If cookie verifier is used, then an non-trivial value is * returned to the client This value is the ctime of the * directory. If verifier is unused (as in many NFS Servers) then * only a set of zeros is returned (trivial value) */ if (use_cookie_verifier) { if (attrs_dir.valid_mask == ATTR_RDATTR_ERR) { res->res_readdir3.status = NFS3ERR_SERVERFAULT; LogDebug(COMPONENT_NFS_READDIR, "Could not fetch ctime"); goto out_fail; } memcpy(cookie_verifier, &attrs_dir.ctime.tv_sec, sizeof(attrs_dir.ctime.tv_sec)); } if (use_cookie_verifier && begin_cookie != 0) { /* Not the first call, so we have to check the cookie verifier */ if (memcmp(cookie_verifier, arg->arg_readdirplus3.cookieverf, NFS3_COOKIEVERFSIZE) != 0) { res->res_readdirplus3.status = NFS3ERR_BAD_COOKIE; rc = NFS_REQ_OK; goto out_fail; } } resok->reply.entries = NULL; resok->reply.eof = FALSE; /* Fudge cookie for "." and "..", if necessary */ if (begin_cookie > 2) fsal_cookie = begin_cookie; else fsal_cookie = 0; /* Allocate space for entries */ tracker.entries = gsh_calloc(estimated_num_entries, sizeof(entryplus3)); if (begin_cookie == 0) { /* Fill in "." */ res->res_readdir3.status = nfs_readdir_dot_entry(dir_obj, ".", 1, nfs3_readdirplus_callback, &tracker, &attrs_dir); if (res->res_readdir3.status != NFS3_OK) { rc = NFS_REQ_OK; goto out_fail; } } /* Fill in ".." */ if (begin_cookie <= 1) { struct fsal_obj_handle *parent_dir_obj = NULL; fsal_status_gethandle = fsal_lookupp(dir_obj, &parent_dir_obj, &attrs_parent); if (parent_dir_obj == NULL) { res->res_readdirplus3.status = nfs3_Errno_status(fsal_status_gethandle); rc = NFS_REQ_OK; goto out_fail; } res->res_readdir3.status = nfs_readdir_dot_entry(parent_dir_obj, "..", 2, nfs3_readdirplus_callback, &tracker, &attrs_parent); parent_dir_obj->obj_ops.put_ref(parent_dir_obj); if (res->res_readdir3.status != NFS3_OK) { rc = NFS_REQ_OK; goto out_fail; } } LogDebug(COMPONENT_NFS_READDIR, "Readdirplus3 -> Call to fsal_readdir, cookie=%" PRIu64, fsal_cookie); /* Call readdir */ fsal_status = fsal_readdir(dir_obj, fsal_cookie, &num_entries, &eod_met, ATTRS_NFS3, nfs3_readdirplus_callback, &tracker); if (FSAL_IS_ERROR(fsal_status)) { /* Is this a retryable error */ if (nfs_RetryableError(fsal_status.major)) { rc = NFS_REQ_DROP; goto out_fail; } res->res_readdirplus3.status = nfs3_Errno_status(fsal_status); goto out_fail; } if (tracker.error != NFS3_OK) { res->res_readdir3.status = tracker.error; goto out_fail; } if ((num_entries == 0) && (begin_cookie > 1)) { res->res_readdirplus3.status = NFS3_OK; resok->reply.entries = NULL; resok->reply.eof = TRUE; } else { resok->reply.entries = tracker.entries; resok->reply.eof = eod_met; } nfs_SetPostOpAttr(dir_obj, &resok->dir_attributes, &attrs_dir); memcpy(resok->cookieverf, cookie_verifier, sizeof(cookieverf3)); res->res_readdirplus3.status = NFS3_OK; rc = NFS_REQ_OK; goto out; out_fail: /* Set the post op attributes as we have found them. */ nfs_SetPostOpAttr(dir_obj, &resfail->dir_attributes, &attrs_dir); out: /* Release the attributes. */ fsal_release_attrs(&attrs_dir); fsal_release_attrs(&attrs_parent); if (dir_obj) dir_obj->obj_ops.put_ref(dir_obj); if (((res->res_readdir3.status != NFS3_OK) || (rc != NFS_REQ_OK)) && (tracker.entries != NULL)) free_entryplus3s(tracker.entries); return rc; } /* nfs3_readdirplus */ /** * @brief Frees the result structure allocated for nfs3_readdirplus. * * Frees the result structure allocated for nfs3_readdirplus. * * @param resp [in,out] Pointer to the result structure * */ void nfs3_readdirplus_free(nfs_res_t *resp) { #define RESREADDIRPLUSREPLY resp->res_readdirplus3.READDIRPLUS3res_u.resok.reply if ((resp->res_readdirplus3.status == NFS3_OK) && (RESREADDIRPLUSREPLY.entries != NULL)) free_entryplus3s(RESREADDIRPLUSREPLY.entries); } /** * @brief Populate entryplus3s when called from fsal_readdir * * This function is a callback passed to fsal_readdir. It * fills in a pre-allocated array of entryplys3 structures and allocates * space for the name and attributes. This space must be freed. * * @param opaque [in] Pointer to a struct nfs3_readdirplus_cb_data that is * gives the location of the array and other * bookeeping information * @param name [in] The filename for the current obj * @param handle [in] The current obj's filehandle * @param attrs [in] The current obj's attributes * @param cookie [in] The readdir cookie for the current obj */ fsal_errors_t nfs3_readdirplus_callback(void *opaque, struct fsal_obj_handle *obj, const struct attrlist *attr, uint64_t mounted_on_fileid, uint64_t cookie, enum cb_state cb_state) { /* Not-so-opaque pointer to callback data` */ struct fsal_readdir_cb_parms *cb_parms = opaque; struct nfs3_readdirplus_cb_data *tracker = cb_parms->opaque; /* Length of the current filename */ size_t namelen = strlen(cb_parms->name); entryplus3 *ep3 = tracker->entries + tracker->count; if (tracker->count == tracker->total_entries) { cb_parms->in_result = false; return ERR_FSAL_NO_ERROR; } /* This is a pessimistic check, which assumes that we're going * to send attributes and full size handle - if it fails then * we're close enough to the buffer size limit and t's time to * stop anyway */ if (tracker->mem_left < (sizeof(entryplus3) + namelen + NFS3_FHSIZE)) { if (tracker->count == 0) tracker->error = NFS3ERR_TOOSMALL; cb_parms->in_result = false; return ERR_FSAL_NO_ERROR; } LogDebug(COMPONENT_NFS_READDIR, "Callback for %s cookie %"PRIu64, cb_parms->name, cookie); ep3->fileid = obj->fileid; ep3->name = gsh_strdup(cb_parms->name); ep3->cookie = cookie; /* Account for file name + length + cookie */ tracker->mem_left -= sizeof(ep3->cookie) + ((namelen + 3) & ~3) + 4; if (cb_parms->attr_allowed) { ep3->name_handle.handle_follows = TRUE; if (!nfs3_FSALToFhandle(true, &ep3->name_handle.post_op_fh3_u.handle, obj, op_ctx->ctx_export)) { tracker->error = NFS3ERR_SERVERFAULT; gsh_free(ep3->name); cb_parms->in_result = false; return ERR_FSAL_NO_ERROR; } /* Account for filehande + length + follows + nextentry */ tracker->mem_left -= ep3->name_handle.post_op_fh3_u.handle.data.data_len + 12; /* Check if attributes follow and then place the attributes * that follow */ ep3->name_attributes.attributes_follow = nfs3_FSALattr_To_Fattr( obj, attr, &ep3->name_attributes.post_op_attr_u.attributes); } else { ep3->name_handle.handle_follows = false; ep3->name_attributes.attributes_follow = false; tracker->mem_left -= sizeof(ep3->name_handle.handle_follows); } if (ep3->name_attributes.attributes_follow) tracker->mem_left -= sizeof(ep3->name_attributes); else tracker->mem_left -= sizeof(ep3->name_attributes.attributes_follow); if (tracker->count > 0) tracker->entries[tracker->count - 1].nextentry = ep3; ++(tracker->count); cb_parms->in_result = true; return ERR_FSAL_NO_ERROR; } /* nfs3_readdirplus_callback */ /** * @brief Clean up memory allocated to serve NFSv3 READDIRPLUS * * This function traverses the list of entries, freeing all names * allocated in the callback function, then frees the list itself. * * @param entryplus3s [in] Pointer to first obj */ static void free_entryplus3s(entryplus3 *entryplus3s) { entryplus3 *entry = NULL; for (entry = entryplus3s; entry != NULL; entry = entry->nextentry) { gsh_free(entry->name); gsh_free(entry->name_handle.post_op_fh3_u.handle.data.data_val); } gsh_free(entryplus3s); } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs3_readlink.c000066400000000000000000000075351324272410200220070ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * @file nfs3_readlink.c * @brief Everything you need for NFSv3 READLINK. */ #include "config.h" #include #include #include #include #include /* for having FNDELAY */ #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "nfs23.h" #include "nfs4.h" #include "mount.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_convert.h" #include "nfs_proto_tools.h" /** * * @brief The NFSPROC3_READLINK. * * This function implements the NFSPROC3_READLINK function. * * @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 * * @see fsal_readlink * * @retval NFS_REQ_OK if successfull * @retval NFS_REQ_DROP if failed but retryable * @retval NFS_REQ_FAILED if failed and not retryable */ int nfs3_readlink(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct fsal_obj_handle *obj = NULL; fsal_status_t fsal_status; struct gsh_buffdesc link_buffer = { .addr = NULL, .len = 0 }; int rc = NFS_REQ_OK; READLINK3resfail *resfail = &res->res_readlink3.READLINK3res_u.resfail; READLINK3resok *resok = &res->res_readlink3.READLINK3res_u.resok; if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; nfs_FhandleToStr(req->rq_msg.cb_vers, &(arg->arg_readlink3.symlink), NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs_Readlink handle: %s", str); } /* to avoid setting it on each error case */ resfail->symlink_attributes.attributes_follow = false; obj = nfs3_FhandleToCache(&arg->arg_readlink3.symlink, &res->res_readlink3.status, &rc); if (obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } /* Sanity Check: the obj must be a link */ if (obj->type != SYMBOLIC_LINK) { res->res_readlink3.status = NFS3ERR_INVAL; rc = NFS_REQ_OK; goto out; } fsal_status = fsal_readlink(obj, &link_buffer); if (FSAL_IS_ERROR(fsal_status)) { res->res_readlink3.status = nfs3_Errno_status(fsal_status); nfs_SetPostOpAttr(obj, &resfail->symlink_attributes, NULL); if (nfs_RetryableError(fsal_status.major)) rc = NFS_REQ_DROP; goto out; } /* Reply to the client */ resok->data = link_buffer.addr; nfs_SetPostOpAttr(obj, &resok->symlink_attributes, NULL); res->res_readlink3.status = NFS3_OK; rc = NFS_REQ_OK; out: /* return references */ if (obj) obj->obj_ops.put_ref(obj); return rc; } /* nfs3_readlink */ /** * @brief Free the result structure allocated for nfs3_readlink. * * This function frees the result structure allocated for * nfs3_readlink. * * @param[in,out] res Result structure * */ void nfs3_readlink_free(nfs_res_t *res) { if (res->res_readlink3.status == NFS3_OK) gsh_free(res->res_readlink3.READLINK3res_u.resok.data); } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs3_remove.c000066400000000000000000000112001324272410200214730ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * @file nfs3_remove.c * @brief Everything you need for NFSv3 REMOVE */ #include "config.h" #include #include #include #include #include /* for having FNDELAY */ #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "nfs23.h" #include "nfs4.h" #include "mount.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_convert.h" #include "nfs_proto_tools.h" /** * * @brief The NFSPROC3_REMOVE * * Implements the NFSPROC3_REMOVE function. * * @param[in] arg NFS arguments 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_remove(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct fsal_obj_handle *parent_obj = NULL; struct fsal_obj_handle *child_obj = NULL; pre_op_attr pre_parent = { .attributes_follow = false }; fsal_status_t fsal_status; const char *name = arg->arg_remove3.object.name; int rc = NFS_REQ_OK; if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; nfs_FhandleToStr(req->rq_msg.cb_vers, &arg->arg_create3.where.dir, NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs_Remove handle: %s name: %s", str, name); } /* Convert file handle into a pentry */ /* to avoid setting it on each error case */ res->res_remove3.REMOVE3res_u.resfail.dir_wcc.before.attributes_follow = FALSE; res->res_remove3.REMOVE3res_u.resfail.dir_wcc.after.attributes_follow = FALSE; parent_obj = nfs3_FhandleToCache(&arg->arg_remove3.object.dir, &res->res_remove3.status, &rc); if (parent_obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } nfs_SetPreOpAttr(parent_obj, &pre_parent); /* Sanity checks: file name must be non-null; parent must be a * directory. */ if (parent_obj->type != DIRECTORY) { res->res_remove3.status = NFS3ERR_NOTDIR; rc = NFS_REQ_OK; goto out; } if (name == NULL || *name == '\0') { fsal_status = fsalstat(ERR_FSAL_INVAL, 0); goto out_fail; } /* Lookup the child entry to verify that it is not a directory */ fsal_status = fsal_lookup(parent_obj, name, &child_obj, NULL); if (!FSAL_IS_ERROR(fsal_status)) { /* Sanity check: make sure we are not removing a * directory */ if (child_obj->type == DIRECTORY) { res->res_remove3.status = NFS3ERR_ISDIR; rc = NFS_REQ_OK; goto out; } } LogFullDebug(COMPONENT_NFSPROTO, "Trying to remove file %s", name); /* Remove the entry. */ fsal_status = fsal_remove(parent_obj, name); if (FSAL_IS_ERROR(fsal_status)) goto out_fail; /* Build Weak Cache Coherency data */ nfs_SetWccData(&pre_parent, parent_obj, &res->res_remove3.REMOVE3res_u.resok.dir_wcc); res->res_remove3.status = NFS3_OK; rc = NFS_REQ_OK; goto out; out_fail: res->res_remove3.status = nfs3_Errno_status(fsal_status); nfs_SetWccData(&pre_parent, parent_obj, &res->res_remove3.REMOVE3res_u.resfail.dir_wcc); if (nfs_RetryableError(fsal_status.major)) rc = NFS_REQ_DROP; out: /* return references */ if (child_obj) child_obj->obj_ops.put_ref(child_obj); if (parent_obj) parent_obj->obj_ops.put_ref(parent_obj); return rc; } /* nfs3_remove */ /** * @brief Free the result structure allocated for nfs3_remove. * * This function frees the result structure allocated for nfs3_remove. * * @param[in,out] res Result structure * */ void nfs3_remove_free(nfs_res_t *res) { /* Nothing to do here */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs3_rename.c000066400000000000000000000132121324272410200214520ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs3_rename.c * @brief Everything you need for NFSv3 RENAME */ #include "config.h" #include #include #include #include #include #include "hashtable.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_convert.h" #include "nfs_proto_tools.h" #include "client_mgr.h" /** * * @brief The NFSPROC3_RENAME * * Implements the NFSPROC3_RENAME function. * * @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_rename(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { const char *entry_name = arg->arg_rename3.from.name; const char *new_entry_name = arg->arg_rename3.to.name; struct fsal_obj_handle *parent_obj = NULL; struct fsal_obj_handle *new_parent_obj = NULL; fsal_status_t fsal_status; int to_exportid = 0; int from_exportid = 0; int rc = NFS_REQ_OK; RENAME3resfail *resfail = &res->res_rename3.RENAME3res_u.resfail; RENAME3resok *resok = &res->res_rename3.RENAME3res_u.resok; pre_op_attr pre_parent = { .attributes_follow = false }; pre_op_attr pre_new_parent = { .attributes_follow = false }; if (isDebug(COMPONENT_NFSPROTO)) { char strto[LEN_FH_STR], strfrom[LEN_FH_STR]; nfs_FhandleToStr(req->rq_msg.cb_vers, &arg->arg_rename3.from.dir, NULL, strfrom); nfs_FhandleToStr(req->rq_msg.cb_vers, &arg->arg_rename3.to.dir, NULL, strto); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs_Rename from handle: %s name %s to handle: %s name: %s", strfrom, entry_name, strto, new_entry_name); } /* to avoid setting it on each error case */ resfail->fromdir_wcc.before.attributes_follow = FALSE; resfail->fromdir_wcc.after.attributes_follow = FALSE; resfail->todir_wcc.before.attributes_follow = FALSE; resfail->todir_wcc.after.attributes_follow = FALSE; /* Get the exportids for the two handles. */ to_exportid = nfs3_FhandleToExportId(&(arg->arg_rename3.to.dir)); from_exportid = nfs3_FhandleToExportId(&(arg->arg_rename3.from.dir)); /* Validate the to_exportid */ if (to_exportid < 0 || from_exportid < 0) { LogInfo(COMPONENT_DISPATCH, "NFS%d RENAME Request from client %s has badly formed handle for to dir", req->rq_msg.cb_vers, op_ctx->client ? op_ctx->client->hostaddr_str : "unknown client"); /* Bad handle, report to client */ res->res_rename3.status = NFS3ERR_BADHANDLE; goto out; } /* Both objects have to be in the same filesystem */ if (to_exportid != from_exportid) { res->res_rename3.status = NFS3ERR_XDEV; goto out; } /* Convert fromdir file handle into a FSAL obj */ parent_obj = nfs3_FhandleToCache(&arg->arg_rename3.from.dir, &res->res_rename3.status, &rc); if (parent_obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } nfs_SetPreOpAttr(parent_obj, &pre_parent); /* Convert todir file handle into a FSAL obj */ new_parent_obj = nfs3_FhandleToCache(&arg->arg_rename3.to.dir, &res->res_rename3.status, &rc); if (new_parent_obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } nfs_SetPreOpAttr(new_parent_obj, &pre_new_parent); if (entry_name == NULL || *entry_name == '\0' || new_entry_name == NULL || *new_entry_name == '\0') { fsal_status = fsalstat(ERR_FSAL_INVAL, 0); goto out_fail; } fsal_status = fsal_rename(parent_obj, entry_name, new_parent_obj, new_entry_name); if (FSAL_IS_ERROR(fsal_status)) goto out_fail; res->res_rename3.status = NFS3_OK; nfs_SetWccData(&pre_parent, parent_obj, &resok->fromdir_wcc); nfs_SetWccData(&pre_new_parent, new_parent_obj, &resok->todir_wcc); rc = NFS_REQ_OK; goto out; out_fail: res->res_rename3.status = nfs3_Errno_status(fsal_status); nfs_SetWccData(&pre_parent, parent_obj, &resfail->fromdir_wcc); nfs_SetWccData(&pre_new_parent, new_parent_obj, &resfail->todir_wcc); /* If we are here, there was an error */ if (nfs_RetryableError(fsal_status.major)) rc = NFS_REQ_DROP; out: if (parent_obj) parent_obj->obj_ops.put_ref(parent_obj); if (new_parent_obj) new_parent_obj->obj_ops.put_ref(new_parent_obj); return rc; } /** * @brief Free the result structure allocated for nfs3_rename. * * This function frees the result structure allocated for nfs3_rename. * * @param[in,out] res Result structure * */ void nfs3_rename_free(nfs_res_t *res) { /* Nothing to do here */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs3_rmdir.c000066400000000000000000000110021324272410200213130ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs3_rmdir.c * @brief Everything you need for NFSv3 RMDIR */ #include "config.h" #include #include #include #include #include #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "nfs23.h" #include "nfs4.h" #include "mount.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_convert.h" #include "nfs_proto_tools.h" /** * * @brief The NFSPROC3_RMDIR * * Implements the NFSPROC3_RMDIR function. * * @param[in] arg NFS arguments 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_rmdir(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct fsal_obj_handle *parent_obj = NULL; struct fsal_obj_handle *child_obj = NULL; pre_op_attr pre_parent = { .attributes_follow = false }; fsal_status_t fsal_status; const char *name = arg->arg_rmdir3.object.name; int rc = NFS_REQ_OK; if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; nfs_FhandleToStr(req->rq_msg.cb_vers, &arg->arg_rmdir3.object.dir, NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling NFS3_RMDIR handle: %s name: %s", str, name); } /* Convert file handle into a pentry */ /* to avoid setting it on each error case */ res->res_rmdir3.RMDIR3res_u.resfail.dir_wcc.before.attributes_follow = FALSE; res->res_rmdir3.RMDIR3res_u.resfail.dir_wcc.after.attributes_follow = FALSE; parent_obj = nfs3_FhandleToCache(&arg->arg_rmdir3.object.dir, &res->res_rmdir3.status, &rc); if (parent_obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } nfs_SetPreOpAttr(parent_obj, &pre_parent); /* Sanity checks: directory name must be non-null; parent * must be a directory. */ if (parent_obj->type != DIRECTORY) { res->res_rmdir3.status = NFS3ERR_NOTDIR; rc = NFS_REQ_OK; goto out; } if ((name == NULL) || (*name == '\0')) { fsal_status = fsalstat(ERR_FSAL_INVAL, 0); goto out_fail; } /* Lookup to the entry to be removed to check that it is a * directory */ fsal_status = fsal_lookup(parent_obj, name, &child_obj, NULL); if (child_obj != NULL) { /* Sanity check: make sure we are about to remove a * directory */ if (child_obj->type != DIRECTORY) { res->res_rmdir3.status = NFS3ERR_NOTDIR; rc = NFS_REQ_OK; goto out; } } fsal_status = fsal_remove(parent_obj, name); if (FSAL_IS_ERROR(fsal_status)) goto out_fail; nfs_SetWccData(&pre_parent, parent_obj, &res->res_rmdir3.RMDIR3res_u.resok.dir_wcc); res->res_rmdir3.status = NFS3_OK; rc = NFS_REQ_OK; goto out; out_fail: res->res_rmdir3.status = nfs3_Errno_status(fsal_status); nfs_SetWccData(&pre_parent, parent_obj, &res->res_rmdir3.RMDIR3res_u.resfail.dir_wcc); /* If we are here, there was an error */ if (nfs_RetryableError(fsal_status.major)) rc = NFS_REQ_DROP; out: /* return references */ if (child_obj) child_obj->obj_ops.put_ref(child_obj); if (parent_obj) parent_obj->obj_ops.put_ref(parent_obj); return rc; } /* nfs3_rmdir */ /** * @brief Free the result structure allocated for nfs3_rmdir * * This function frees the result structure allocated for nfs3_rmdir. * * @param[in,out] res Result structure * */ void nfs3_rmdir_free(nfs_res_t *res) { /* Nothing to do here */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs3_setattr.c000066400000000000000000000147131324272410200217000ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs3_setattr.c * @brief Everything you need for NFSv3 SETATTR */ #include "config.h" #include #include #include #include #include /* for having FNDELAY */ #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "nfs23.h" #include "nfs4.h" #include "mount.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_creds.h" #include "nfs_proto_functions.h" #include "nfs_convert.h" #include "nfs_proto_tools.h" #include "sal_functions.h" /** * @brief The NFSPROC3_SETATTR * * Implements the NFSPROC3_SETATTR function. * * @param[in] arg NFS arguments 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 successfull * @retval NFS_REQ_DROP if failed but retryable * @retval NFS_REQ_FAILED if failed and not retryable * */ int nfs3_setattr(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct attrlist setattr; struct fsal_obj_handle *obj = NULL; pre_op_attr pre_attr = { .attributes_follow = false }; fsal_status_t fsal_status = {0, 0}; int rc = NFS_REQ_OK; SETATTR3resfail *resfail = &res->res_setattr3.SETATTR3res_u.resfail; SETATTR3resok *resok = &res->res_setattr3.SETATTR3res_u.resok; memset(&setattr, 0, sizeof(setattr)); if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; nfs_FhandleToStr(req->rq_msg.cb_vers, &arg->arg_setattr3.object, NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs_Setattr handle: %s", str); } /* to avoid setting it on each error case */ resfail->obj_wcc.before.attributes_follow = FALSE; resfail->obj_wcc.after.attributes_follow = FALSE; obj = nfs3_FhandleToCache(&arg->arg_setattr3.object, &res->res_setattr3.status, &rc); if (obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ LogFullDebug(COMPONENT_NFSPROTO, "nfs3_FhandleToCache failed"); goto out; } /* Don't allow attribute change while we are in grace period. * Required for delegation reclaims and may be needed for other * reclaimable states as well. No NFS4ERR_GRACE in NFS v3, so * send jukebox error. */ if (nfs_in_grace()) { res->res_setattr3.status = NFS3ERR_JUKEBOX; rc = NFS_REQ_OK; LogFullDebug(COMPONENT_NFSPROTO, "nfs_in_grace is true"); goto out; } nfs_SetPreOpAttr(obj, &pre_attr); if (arg->arg_setattr3.guard.check) { /* This pack of lines implements the "guard check" setattr. This * feature of nfsv3 is used to avoid several setattr to occur * concurently on the same object, from different clients */ nfstime3 *obj_ctime = &arg->arg_setattr3.guard.sattrguard3_u.obj_ctime; nfstime3 *pre_ctime = &pre_attr.pre_op_attr_u.attributes.ctime; LogFullDebug(COMPONENT_NFSPROTO, "css=%d acs=%d csn=%d acn=%d", obj_ctime->tv_sec, pre_ctime->tv_sec, obj_ctime->tv_nsec, pre_ctime->tv_nsec); if (obj_ctime->tv_sec != pre_ctime->tv_sec || obj_ctime->tv_nsec != pre_ctime->tv_nsec) { res->res_setattr3.status = NFS3ERR_NOT_SYNC; rc = NFS_REQ_OK; LogFullDebug(COMPONENT_NFSPROTO, "guard check failed"); goto out; } } /* Conversion to FSAL attributes */ if (!nfs3_Sattr_To_FSALattr(&setattr, &arg->arg_setattr3.new_attributes)) { res->res_setattr3.status = NFS3ERR_INVAL; rc = NFS_REQ_OK; LogFullDebug(COMPONENT_NFSPROTO, "nfs3_Sattr_To_FSALattr failed"); goto out; } if (setattr.valid_mask != 0) { /* If owner or owner_group are set, and the credential was * squashed, then we must squash the set owner and owner_group. */ squash_setattr(&setattr); if (arg->arg_setattr3.new_attributes.size.set_it) { /* Check for delegation conflict. */ if (state_deleg_conflict(obj, true)) { res->res_setattr3.status = NFS3ERR_JUKEBOX; LogFullDebug(COMPONENT_NFSPROTO, "state_share_anonymous_io_start failed"); goto out_fail; } } /* For now we don't look for states, so indicate bypass so * we will get through an NLM_SHARE with deny. */ fsal_status = fsal_setattr(obj, true, NULL, &setattr); if (FSAL_IS_ERROR(fsal_status)) { res->res_setattr3.status = nfs3_Errno_status(fsal_status); LogFullDebug(COMPONENT_NFSPROTO, "fsal_setattr failed"); goto out_fail; } } /* Set the NFS return */ /* Build Weak Cache Coherency data */ res->res_setattr3.status = NFS3_OK; if (arg->arg_setattr3.new_attributes.size.set_it && !(setattr.valid_mask ^ (ATTR_SPACEUSED | ATTR_SIZE))) { resfail->obj_wcc.before.attributes_follow = FALSE; resfail->obj_wcc.after.attributes_follow = FALSE; } else { nfs_SetWccData(&pre_attr, obj, &resok->obj_wcc); } rc = NFS_REQ_OK; out: /* Release the attributes (may release an inherited ACL) */ fsal_release_attrs(&setattr); /* return references */ if (obj) obj->obj_ops.put_ref(obj); LogDebug(COMPONENT_NFSPROTO, "Result %s%s", nfsstat3_to_str(res->res_setattr3.status), rc == NFS_REQ_DROP ? " Dropping response" : ""); return rc; out_fail: nfs_SetWccData(&pre_attr, obj, &resfail->obj_wcc); if (nfs_RetryableError(fsal_status.major)) { /* Drop retryable request. */ rc = NFS_REQ_DROP; } goto out; } /* nfs3_setattr */ /** * @brief Free the result structure allocated for nfs3_setattr. * * This function frees the result structure allocated for nfs3_setattr. * * @param[in,out] res Result structure */ void nfs3_setattr_free(nfs_res_t *res) { /* Nothing to do here */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs3_symlink.c000066400000000000000000000136161324272410200217010ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs3_symlink.c * @brief Everything you need for NFSv3 SYMLINK */ #include "config.h" #include #include #include #include #include #include "hashtable.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_creds.h" #include "nfs_proto_functions.h" #include "nfs_convert.h" #include "nfs_file_handle.h" #include "nfs_proto_tools.h" #include "export_mgr.h" /** * * @brief The NFSPROC3_SYMLINK * * Implements the NFSPROC3_SYMLINK function. * * @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_symlink(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { const char *symlink_name = arg->arg_symlink3.where.name; char *target_path = arg->arg_symlink3.symlink.symlink_data; struct fsal_obj_handle *symlink_obj = NULL; struct fsal_obj_handle *parent_obj; pre_op_attr pre_parent; fsal_status_t fsal_status; int rc = NFS_REQ_OK; struct attrlist sattr, attrs; SYMLINK3resfail *resfail = &res->res_symlink3.SYMLINK3res_u.resfail; SYMLINK3resok *resok = &res->res_symlink3.SYMLINK3res_u.resok; /* We have the option of not sending attributes, so set ATTR_RDATTR_ERR. */ fsal_prepare_attrs(&attrs, ATTRS_NFS3 | ATTR_RDATTR_ERR); memset(&sattr, 0, sizeof(sattr)); if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; nfs_FhandleToStr(req->rq_msg.cb_vers, &arg->arg_symlink3.where.dir, NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs_Symlink handle: %s name: %s target: %s", str, symlink_name, target_path); } /* to avoid setting it on each error case */ resfail->dir_wcc.before.attributes_follow = false; resfail->dir_wcc.after.attributes_follow = false; parent_obj = nfs3_FhandleToCache(&arg->arg_symlink3.where.dir, &res->res_symlink3.status, &rc); if (parent_obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } nfs_SetPreOpAttr(parent_obj, &pre_parent); if (parent_obj->type != DIRECTORY) { res->res_symlink3.status = NFS3ERR_NOTDIR; rc = NFS_REQ_OK; goto out; } /* if quota support is active, then we should check is the * FSAL allows inode creation or not */ fsal_status = op_ctx->fsal_export->exp_ops.check_quota(op_ctx->fsal_export, op_ctx->ctx_export->fullpath, FSAL_QUOTA_INODES); if (FSAL_IS_ERROR(fsal_status)) { res->res_symlink3.status = NFS3ERR_DQUOT; rc = NFS_REQ_OK; goto out; } if (symlink_name == NULL || *symlink_name == '\0' || target_path == NULL || *target_path == '\0') { fsal_status = fsalstat(ERR_FSAL_INVAL, 0); goto out_fail; } /* Some clients (like the Spec NFS benchmark) set * attributes with the NFSPROC3_SYMLINK request */ if (!nfs3_Sattr_To_FSALattr( &sattr, &arg->arg_symlink3.symlink.symlink_attributes)) { res->res_symlink3.status = NFS3ERR_INVAL; rc = NFS_REQ_OK; goto out; } squash_setattr(&sattr); if (!(sattr.valid_mask & ATTR_MODE)) { /* Make sure mode is set. */ sattr.mode = 0777; sattr.valid_mask |= ATTR_MODE; } /* Make the symlink */ fsal_status = fsal_create(parent_obj, symlink_name, SYMBOLIC_LINK, &sattr, target_path, &symlink_obj, &attrs); /* Release the attributes (may release an inherited ACL) */ fsal_release_attrs(&sattr); if (FSAL_IS_ERROR(fsal_status)) goto out_fail; if (!nfs3_FSALToFhandle(true, &resok->obj.post_op_fh3_u.handle, symlink_obj, op_ctx->ctx_export)) { res->res_symlink3.status = NFS3ERR_BADHANDLE; rc = NFS_REQ_OK; goto out; } resok->obj.handle_follows = TRUE; /* Build entry attributes */ nfs_SetPostOpAttr(symlink_obj, &resok->obj_attributes, &attrs); /* Build Weak Cache Coherency data */ nfs_SetWccData(&pre_parent, parent_obj, &resok->dir_wcc); res->res_symlink3.status = NFS3_OK; rc = NFS_REQ_OK; goto out; out_fail: res->res_symlink3.status = nfs3_Errno_status(fsal_status); nfs_SetWccData(&pre_parent, parent_obj, &resfail->dir_wcc); if (nfs_RetryableError(fsal_status.major)) rc = NFS_REQ_DROP; out: /* Release the attributes. */ fsal_release_attrs(&attrs); /* return references */ if (parent_obj) parent_obj->obj_ops.put_ref(parent_obj); if (symlink_obj) symlink_obj->obj_ops.put_ref(symlink_obj); return rc; } /* nfs3_symlink */ /** * @brief Free the result structure allocated for nfs3_symlink. * * This function frees the result structure allocated for nfs3_symlink. * * @param[in,out] res Result structure * */ void nfs3_symlink_free(nfs_res_t *res) { SYMLINK3resok *resok = &res->res_symlink3.SYMLINK3res_u.resok; if (res->res_symlink3.status == NFS3_OK && resok->obj.handle_follows) gsh_free(resok->obj.post_op_fh3_u.handle.data.data_val); } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs3_write.c000066400000000000000000000161321324272410200213410ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs3_write.c * @brief Everything you need for NFSv3 WRITE. */ #include "config.h" #include #include #include #include #include #include #include "hashtable.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_convert.h" #include "nfs_proto_tools.h" #include "server_stats.h" #include "export_mgr.h" #include "sal_functions.h" /** * * @brief The NFSPROC3_WRITE * * Implements the NFSPROC3_WRITE function. * * @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_write(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct fsal_obj_handle *obj; pre_op_attr pre_attr = { .attributes_follow = false }; fsal_status_t fsal_status = {0, 0}; size_t size = 0; size_t written_size = 0; uint64_t offset = 0; void *data = NULL; bool sync = false; int rc = NFS_REQ_OK; uint64_t MaxWrite = atomic_fetch_uint64_t(&op_ctx->ctx_export->MaxWrite); uint64_t MaxOffsetWrite = atomic_fetch_uint64_t(&op_ctx->ctx_export->MaxOffsetWrite); WRITE3resfail *resfail = &res->res_write3.WRITE3res_u.resfail; WRITE3resok *resok = &res->res_write3.WRITE3res_u.resok; offset = arg->arg_write3.offset; size = arg->arg_write3.count; if ((arg->arg_write3.stable == DATA_SYNC) || (arg->arg_write3.stable == FILE_SYNC)) sync = true; if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR], *stables = ""; switch (arg->arg_write3.stable) { case UNSTABLE: stables = "UNSTABLE"; break; case DATA_SYNC: stables = "DATA_SYNC"; break; case FILE_SYNC: stables = "FILE_SYNC"; break; } nfs_FhandleToStr(req->rq_msg.cb_vers, &arg->arg_write3.file, NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs_Write handle: %s start: %" PRIx64 " len: %zx %s", str, offset, size, stables); } /* to avoid setting it on each error case */ resfail->file_wcc.before.attributes_follow = false; resfail->file_wcc.after.attributes_follow = false; obj = nfs3_FhandleToCache(&arg->arg_write3.file, &res->res_write3.status, &rc); if (obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ return rc; } nfs_SetPreOpAttr(obj, &pre_attr); fsal_status = obj->obj_ops.test_access(obj, FSAL_WRITE_ACCESS, NULL, NULL, true); if (FSAL_IS_ERROR(fsal_status)) { res->res_write3.status = nfs3_Errno_status(fsal_status); rc = NFS_REQ_OK; goto out; } /* Sanity check: write only a regular file */ if (obj->type != REGULAR_FILE) { if (obj->type == DIRECTORY) res->res_write3.status = NFS3ERR_ISDIR; else res->res_write3.status = NFS3ERR_INVAL; rc = NFS_REQ_OK; goto out; } /* if quota support is active, then we should check is the FSAL allows inode creation or not */ fsal_status = op_ctx->fsal_export->exp_ops.check_quota(op_ctx->fsal_export, op_ctx->ctx_export->fullpath, FSAL_QUOTA_BLOCKS); if (FSAL_IS_ERROR(fsal_status)) { res->res_write3.status = NFS3ERR_DQUOT; rc = NFS_REQ_OK; goto out; } if (size > arg->arg_write3.data.data_len) { /* should never happen */ res->res_write3.status = NFS3ERR_INVAL; rc = NFS_REQ_OK; goto out; } data = arg->arg_write3.data.data_val; /* Do not exceed maxium WRITE offset if set */ if (MaxOffsetWrite < UINT64_MAX) { LogFullDebug(COMPONENT_NFSPROTO, "Write offset=%" PRIu64 " size=%zu MaxOffSet=%" PRIu64, offset, size, MaxOffsetWrite); if ((offset + size) > MaxOffsetWrite) { LogEvent(COMPONENT_NFSPROTO, "A client tryed to violate max file size %" PRIu64 " for exportid #%hu", MaxOffsetWrite, op_ctx->ctx_export->export_id); res->res_write3.status = NFS3ERR_FBIG; nfs_SetWccData(NULL, obj, &resfail->file_wcc); rc = NFS_REQ_OK; goto out; } } /* We should take care not to exceed FSINFO wtmax field for the size */ if (size > MaxWrite) { /* The client asked for too much data, we must restrict him */ size = MaxWrite; } if (size == 0) { fsal_status = fsalstat(ERR_FSAL_NO_ERROR, 0); written_size = 0; res->res_write3.status = NFS3_OK; nfs_SetWccData(NULL, obj, &resfail->file_wcc); rc = NFS_REQ_OK; goto out; } /* An actual write is to be made, prepare it */ /* Check for delegation conflict. */ if (state_deleg_conflict(obj, true)) { res->res_write3.status = NFS3ERR_JUKEBOX; rc = NFS_REQ_OK; goto out; } /* Call the new fsal_write */ /** @todo for now pass NULL state */ fsal_status = fsal_write2(obj, true, NULL, offset, size, &written_size, data, &sync, NULL); if (FSAL_IS_ERROR(fsal_status)) { /* If we are here, there was an error */ LogFullDebug(COMPONENT_NFSPROTO, "failed write: fsal_status=%s", fsal_err_txt(fsal_status)); if (nfs_RetryableError(fsal_status.major)) { rc = NFS_REQ_DROP; goto out; } res->res_write3.status = nfs3_Errno_status(fsal_status); nfs_SetWccData(NULL, obj, &resfail->file_wcc); rc = NFS_REQ_OK; } else { /* Build Weak Cache Coherency data */ nfs_SetWccData(NULL, obj, &resok->file_wcc); /* Set the written size */ resok->count = written_size; /* How do we commit data ? */ if (sync) resok->committed = FILE_SYNC; else resok->committed = UNSTABLE; /* Set the write verifier */ memcpy(resok->verf, NFS3_write_verifier, sizeof(writeverf3)); res->res_write3.status = NFS3_OK; } rc = NFS_REQ_OK; out: /* return references */ obj->obj_ops.put_ref(obj); server_stats_io_done(size, written_size, (rc == NFS_REQ_OK) ? true : false, true); return rc; } /* nfs3_write */ /** * @brief Frees the result structure allocated for nfs3_write. * * Frees the result structure allocated for nfs3_write. * * @param[in,out] res Result structure * */ void nfs3_write_free(nfs_res_t *res) { /* Nothing to do here */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_Compound.c000066400000000000000000000767301324272410200220060ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_Compound.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. * */ #include "config.h" #include "fsal.h" #include "sal_functions.h" #include "nfs_convert.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "server_stats.h" #include "export_mgr.h" #include "nfs_creds.h" #ifdef USE_LTTNG #include "gsh_lttng/nfs_rpc.h" #endif struct nfs4_op_desc { char *name; int (*funct)(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); void (*free_res)(nfs_resop4 *); int exp_perm_flags; }; /** * @brief NFSv4 and 4.1 ops table. * indexed by opcode */ static const struct nfs4_op_desc optabv4[] = { [0] = { /* all out of bounds illegals go here to die */ .name = "OP_ILLEGAL", .funct = nfs4_op_illegal, .free_res = nfs4_op_illegal_Free, .exp_perm_flags = 0}, [1] = { .name = "OP_ILLEGAL", .funct = nfs4_op_illegal, .free_res = nfs4_op_illegal_Free, .exp_perm_flags = 0}, [2] = { .name = "OP_ILLEGAL", .funct = nfs4_op_illegal, .free_res = nfs4_op_illegal_Free, .exp_perm_flags = 0}, [NFS4_OP_ACCESS] = { .name = "OP_ACCESS", .funct = nfs4_op_access, .free_res = nfs4_op_access_Free, .exp_perm_flags = EXPORT_OPTION_MD_READ_ACCESS}, [NFS4_OP_CLOSE] = { .name = "OP_CLOSE", .funct = nfs4_op_close, .free_res = nfs4_op_close_Free, .exp_perm_flags = EXPORT_OPTION_MD_READ_ACCESS}, [NFS4_OP_COMMIT] = { .name = "OP_COMMIT", .funct = nfs4_op_commit, .free_res = nfs4_op_commit_Free, .exp_perm_flags = EXPORT_OPTION_MD_WRITE_ACCESS}, [NFS4_OP_CREATE] = { .name = "OP_CREATE", .funct = nfs4_op_create, .free_res = nfs4_op_create_Free, .exp_perm_flags = EXPORT_OPTION_MD_WRITE_ACCESS}, [NFS4_OP_DELEGPURGE] = { .name = "OP_DELEGPURGE", .funct = nfs4_op_delegpurge, .free_res = nfs4_op_delegpurge_Free, .exp_perm_flags = 0}, [NFS4_OP_DELEGRETURN] = { .name = "OP_DELEGRETURN", .funct = nfs4_op_delegreturn, .free_res = nfs4_op_delegreturn_Free, .exp_perm_flags = EXPORT_OPTION_MD_READ_ACCESS}, [NFS4_OP_GETATTR] = { .name = "OP_GETATTR", .funct = nfs4_op_getattr, .free_res = nfs4_op_getattr_Free, .exp_perm_flags = EXPORT_OPTION_MD_READ_ACCESS}, [NFS4_OP_GETFH] = { .name = "OP_GETFH", .funct = nfs4_op_getfh, .free_res = nfs4_op_getfh_Free, .exp_perm_flags = 0}, [NFS4_OP_LINK] = { .name = "OP_LINK", .funct = nfs4_op_link, .free_res = nfs4_op_link_Free, .exp_perm_flags = EXPORT_OPTION_MD_WRITE_ACCESS}, [NFS4_OP_LOCK] = { .name = "OP_LOCK", .funct = nfs4_op_lock, .free_res = nfs4_op_lock_Free, .exp_perm_flags = EXPORT_OPTION_MD_READ_ACCESS}, [NFS4_OP_LOCKT] = { .name = "OP_LOCKT", .funct = nfs4_op_lockt, .free_res = nfs4_op_lockt_Free, .exp_perm_flags = EXPORT_OPTION_MD_READ_ACCESS}, [NFS4_OP_LOCKU] = { .name = "OP_LOCKU", .funct = nfs4_op_locku, .free_res = nfs4_op_locku_Free, .exp_perm_flags = EXPORT_OPTION_MD_READ_ACCESS}, [NFS4_OP_LOOKUP] = { .name = "OP_LOOKUP", .funct = nfs4_op_lookup, .free_res = nfs4_op_lookup_Free, .exp_perm_flags = EXPORT_OPTION_MD_READ_ACCESS}, [NFS4_OP_LOOKUPP] = { .name = "OP_LOOKUPP", .funct = nfs4_op_lookupp, .free_res = nfs4_op_lookupp_Free, .exp_perm_flags = EXPORT_OPTION_MD_READ_ACCESS}, [NFS4_OP_NVERIFY] = { .name = "OP_NVERIFY", .funct = nfs4_op_nverify, .free_res = nfs4_op_nverify_Free, .exp_perm_flags = EXPORT_OPTION_MD_READ_ACCESS}, [NFS4_OP_OPEN] = { .name = "OP_OPEN", .funct = nfs4_op_open, .free_res = nfs4_op_open_Free, .exp_perm_flags = EXPORT_OPTION_MD_READ_ACCESS}, [NFS4_OP_OPENATTR] = { .name = "OP_OPENATTR", .funct = nfs4_op_openattr, .free_res = nfs4_op_openattr_Free, .exp_perm_flags = EXPORT_OPTION_MD_READ_ACCESS}, [NFS4_OP_OPEN_CONFIRM] = { .name = "OP_OPEN_CONFIRM", .funct = nfs4_op_open_confirm, .free_res = nfs4_op_open_confirm_Free, .exp_perm_flags = 0}, [NFS4_OP_OPEN_DOWNGRADE] = { .name = "OP_OPEN_DOWNGRADE", .funct = nfs4_op_open_downgrade, .free_res = nfs4_op_open_downgrade_Free, .exp_perm_flags = EXPORT_OPTION_MD_READ_ACCESS}, [NFS4_OP_PUTFH] = { .name = "OP_PUTFH", .funct = nfs4_op_putfh, .free_res = nfs4_op_putfh_Free, .exp_perm_flags = 0}, [NFS4_OP_PUTPUBFH] = { .name = "OP_PUTPUBFH", .funct = nfs4_op_putpubfh, .free_res = nfs4_op_putpubfh_Free, .exp_perm_flags = 0}, [NFS4_OP_PUTROOTFH] = { .name = "OP_PUTROOTFH", .funct = nfs4_op_putrootfh, .free_res = nfs4_op_putrootfh_Free, .exp_perm_flags = 0}, [NFS4_OP_READ] = { .name = "OP_READ", .funct = nfs4_op_read, .free_res = nfs4_op_read_Free, .exp_perm_flags = EXPORT_OPTION_READ_ACCESS}, [NFS4_OP_READDIR] = { .name = "OP_READDIR", .funct = nfs4_op_readdir, .free_res = nfs4_op_readdir_Free, .exp_perm_flags = EXPORT_OPTION_MD_READ_ACCESS}, [NFS4_OP_READLINK] = { .name = "OP_READLINK", .funct = nfs4_op_readlink, .free_res = nfs4_op_readlink_Free, .exp_perm_flags = EXPORT_OPTION_MD_READ_ACCESS}, [NFS4_OP_REMOVE] = { .name = "OP_REMOVE", .funct = nfs4_op_remove, .free_res = nfs4_op_remove_Free, .exp_perm_flags = EXPORT_OPTION_MD_WRITE_ACCESS}, [NFS4_OP_RENAME] = { .name = "OP_RENAME", .funct = nfs4_op_rename, .free_res = nfs4_op_rename_Free, .exp_perm_flags = EXPORT_OPTION_MD_WRITE_ACCESS}, [NFS4_OP_RENEW] = { .name = "OP_RENEW", .funct = nfs4_op_renew, .free_res = nfs4_op_renew_Free, .exp_perm_flags = 0}, [NFS4_OP_RESTOREFH] = { .name = "OP_RESTOREFH", .funct = nfs4_op_restorefh, .free_res = nfs4_op_restorefh_Free, .exp_perm_flags = 0}, [NFS4_OP_SAVEFH] = { .name = "OP_SAVEFH", .funct = nfs4_op_savefh, .free_res = nfs4_op_savefh_Free, .exp_perm_flags = 0}, [NFS4_OP_SECINFO] = { .name = "OP_SECINFO", .funct = nfs4_op_secinfo, .free_res = nfs4_op_secinfo_Free, .exp_perm_flags = EXPORT_OPTION_MD_READ_ACCESS}, [NFS4_OP_SETATTR] = { .name = "OP_SETATTR", .funct = nfs4_op_setattr, .free_res = nfs4_op_setattr_Free, .exp_perm_flags = EXPORT_OPTION_MD_WRITE_ACCESS}, [NFS4_OP_SETCLIENTID] = { .name = "OP_SETCLIENTID", .funct = nfs4_op_setclientid, .free_res = nfs4_op_setclientid_Free, .exp_perm_flags = 0}, [NFS4_OP_SETCLIENTID_CONFIRM] = { .name = "OP_SETCLIENTID_CONFIRM", .funct = nfs4_op_setclientid_confirm, .free_res = nfs4_op_setclientid_confirm_Free, .exp_perm_flags = 0}, [NFS4_OP_VERIFY] = { .name = "OP_VERIFY", .funct = nfs4_op_verify, .free_res = nfs4_op_verify_Free, .exp_perm_flags = EXPORT_OPTION_MD_READ_ACCESS}, [NFS4_OP_WRITE] = { .name = "OP_WRITE", .funct = nfs4_op_write, .free_res = nfs4_op_write_Free, .exp_perm_flags = EXPORT_OPTION_WRITE_ACCESS}, [NFS4_OP_RELEASE_LOCKOWNER] = { .name = "OP_RELEASE_LOCKOWNER", .funct = nfs4_op_release_lockowner, .free_res = nfs4_op_release_lockowner_Free, .exp_perm_flags = 0}, [NFS4_OP_BACKCHANNEL_CTL] = { .name = "OP_BACKCHANNEL_CTL", .funct = nfs4_op_illegal, .free_res = nfs4_op_illegal_Free, .exp_perm_flags = 0 /* tbd */}, [NFS4_OP_BIND_CONN_TO_SESSION] = { .name = "OP_BIND_CONN_TO_SESSION", .funct = nfs4_op_illegal, .free_res = nfs4_op_illegal_Free, .exp_perm_flags = 0 /* tbd */}, [NFS4_OP_EXCHANGE_ID] = { .name = "OP_EXCHANGE_ID", .funct = nfs4_op_exchange_id, .free_res = nfs4_op_exchange_id_Free, .exp_perm_flags = 0}, [NFS4_OP_CREATE_SESSION] = { .name = "OP_CREATE_SESSION", .funct = nfs4_op_create_session, .free_res = nfs4_op_create_session_Free, .exp_perm_flags = 0}, [NFS4_OP_DESTROY_SESSION] = { .name = "OP_DESTROY_SESSION", .funct = nfs4_op_destroy_session, .free_res = nfs4_op_reclaim_complete_Free, .exp_perm_flags = 0}, [NFS4_OP_FREE_STATEID] = { .name = "OP_FREE_STATEID", .funct = nfs4_op_free_stateid, .free_res = nfs4_op_free_stateid_Free, .exp_perm_flags = 0}, [NFS4_OP_GET_DIR_DELEGATION] = { .name = "OP_GET_DIR_DELEGATION", .funct = nfs4_op_illegal, .free_res = nfs4_op_illegal_Free, .exp_perm_flags = 0 /* tbd */}, [NFS4_OP_GETDEVICEINFO] = { .name = "OP_GETDEVICEINFO", .funct = nfs4_op_getdeviceinfo, .free_res = nfs4_op_getdeviceinfo_Free, .exp_perm_flags = 0}, [NFS4_OP_GETDEVICELIST] = { .name = "OP_GETDEVICELIST", .funct = nfs4_op_getdevicelist, .free_res = nfs4_op_getdevicelist_Free, .exp_perm_flags = EXPORT_OPTION_MD_READ_ACCESS}, [NFS4_OP_LAYOUTCOMMIT] = { .name = "OP_LAYOUTCOMMIT", .funct = nfs4_op_layoutcommit, .free_res = nfs4_op_reclaim_complete_Free, .exp_perm_flags = 0}, [NFS4_OP_LAYOUTGET] = { .name = "OP_LAYOUTGET", .funct = nfs4_op_layoutget, .free_res = nfs4_op_reclaim_complete_Free, .exp_perm_flags = EXPORT_OPTION_MD_READ_ACCESS}, [NFS4_OP_LAYOUTRETURN] = { .name = "OP_LAYOUTRETURN", .funct = nfs4_op_layoutreturn, .free_res = nfs4_op_reclaim_complete_Free, .exp_perm_flags = 0}, [NFS4_OP_SECINFO_NO_NAME] = { .name = "OP_SECINFO_NO_NAME", .funct = nfs4_op_secinfo_no_name, .free_res = nfs4_op_secinfo_no_name_Free, .exp_perm_flags = EXPORT_OPTION_MD_READ_ACCESS}, [NFS4_OP_SEQUENCE] = { .name = "OP_SEQUENCE", .funct = nfs4_op_sequence, .free_res = nfs4_op_sequence_Free, .exp_perm_flags = 0}, [NFS4_OP_SET_SSV] = { .name = "OP_SET_SSV", .funct = nfs4_op_set_ssv, .free_res = nfs4_op_reclaim_complete_Free, .exp_perm_flags = 0}, [NFS4_OP_TEST_STATEID] = { .name = "OP_TEST_STATEID", .funct = nfs4_op_test_stateid, .free_res = nfs4_op_test_stateid_Free, .exp_perm_flags = 0}, [NFS4_OP_WANT_DELEGATION] = { .name = "OP_WANT_DELEGATION", .funct = nfs4_op_illegal, .free_res = nfs4_op_illegal_Free, .exp_perm_flags = EXPORT_OPTION_MD_READ_ACCESS /* tbd */}, [NFS4_OP_DESTROY_CLIENTID] = { .name = "OP_DESTROY_CLIENTID", .funct = nfs4_op_destroy_clientid, .free_res = nfs4_op_destroy_clientid_Free, .exp_perm_flags = 0 /* tbd */}, [NFS4_OP_RECLAIM_COMPLETE] = { .name = "OP_RECLAIM_COMPLETE", .funct = nfs4_op_reclaim_complete, .free_res = nfs4_op_reclaim_complete_Free, .exp_perm_flags = 0}, /* NFSv4.2 */ [NFS4_OP_ALLOCATE] = { .name = "OP_ALLOCATE", .funct = nfs4_op_allocate, .free_res = nfs4_op_write_Free, .exp_perm_flags = 0}, [NFS4_OP_COPY] = { .name = "OP_COPY", .funct = nfs4_op_notsupp, .free_res = nfs4_op_notsupp_Free, .exp_perm_flags = 0}, [NFS4_OP_COPY_NOTIFY] = { .name = "OP_COPY_NOTIFY", .funct = nfs4_op_notsupp, .free_res = nfs4_op_notsupp_Free, .exp_perm_flags = 0}, [NFS4_OP_DEALLOCATE] = { .name = "OP_DEALLOCATE", .funct = nfs4_op_deallocate, .free_res = nfs4_op_write_Free, .exp_perm_flags = 0}, [NFS4_OP_IO_ADVISE] = { .name = "OP_IO_ADVISE", .funct = nfs4_op_io_advise, .free_res = nfs4_op_io_advise_Free, .exp_perm_flags = 0}, [NFS4_OP_LAYOUTERROR] = { .name = "OP_LAYOUTERROR", .funct = nfs4_op_layouterror, .free_res = nfs4_op_layouterror_Free, .exp_perm_flags = 0}, [NFS4_OP_LAYOUTSTATS] = { .name = "OP_LAYOUTSTATS", .funct = nfs4_op_layoutstats, .free_res = nfs4_op_layoutstats_Free, .exp_perm_flags = 0}, [NFS4_OP_OFFLOAD_CANCEL] = { .name = "OP_OFFLOAD_CANCEL", .funct = nfs4_op_notsupp, .free_res = nfs4_op_notsupp_Free, .exp_perm_flags = 0}, [NFS4_OP_OFFLOAD_STATUS] = { .name = "OP_OFFLOAD_STATUS", .funct = nfs4_op_notsupp, .free_res = nfs4_op_notsupp_Free, .exp_perm_flags = 0}, [NFS4_OP_READ_PLUS] = { .name = "OP_READ_PLUS", .funct = nfs4_op_read_plus, .free_res = nfs4_op_read_plus_Free, .exp_perm_flags = 0}, [NFS4_OP_SEEK] = { .name = "OP_SEEK", .funct = nfs4_op_seek, .free_res = nfs4_op_write_Free, .exp_perm_flags = 0}, [NFS4_OP_WRITE_SAME] = { .name = "OP_WRITE_SAME", .funct = nfs4_op_write_same, .free_res = nfs4_op_write_same_Free, .exp_perm_flags = 0}, [NFS4_OP_CLONE] = { .name = "OP_CLONE", .funct = nfs4_op_notsupp, .free_res = nfs4_op_notsupp_Free, .exp_perm_flags = 0}, /* NFSv4.3 */ [NFS4_OP_GETXATTR] = { .name = "OP_GETXATTR", .funct = nfs4_op_getxattr, .free_res = nfs4_op_getxattr_Free, .exp_perm_flags = 0}, [NFS4_OP_SETXATTR] = { .name = "OP_SETXATTR", .funct = nfs4_op_setxattr, .free_res = nfs4_op_setxattr_Free, .exp_perm_flags = 0}, [NFS4_OP_LISTXATTR] = { .name = "OP_LISTXATTR", .funct = nfs4_op_listxattr, .free_res = nfs4_op_listxattr_Free, .exp_perm_flags = 0}, [NFS4_OP_REMOVEXATTR] = { .name = "OP_REMOVEXATTR", .funct = nfs4_op_removexattr, .free_res = nfs4_op_removexattr_Free, .exp_perm_flags = 0}, }; /** Define the last valid NFS v4 op for each minor version. * */ nfs_opnum4 LastOpcode[] = { NFS4_OP_RELEASE_LOCKOWNER, NFS4_OP_RECLAIM_COMPLETE, NFS4_OP_REMOVEXATTR }; /** * @brief The NFS PROC4 COMPOUND * * Implements the NFS PROC4 COMPOUND. This routine processes the * content of the nfsv4 operation list and composes the result. On * this aspect it is a little similar to a dispatch routine. * Operation and functions necessary to process them are defined in * the optabv4 array. * * * @param[in] arg Generic nfs arguments * @param[in] req NFSv4 request structure * @param[out] res NFSv4 reply structure * * @see nfs4_op_<*> functions * @see nfs4_GetPseudoFs * * @retval NFS_REQ_OKAY if a result is sent. * @retval NFS_REQ_DROP if we pretend we never saw the request. */ int nfs4_Compound(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { unsigned int i = 0; int status = NFS4_OK; compound_data_t data; nfs_opnum4 opcode; const uint32_t compound4_minor = arg->arg_compound4.minorversion; const uint32_t argarray_len = arg->arg_compound4.argarray.argarray_len; /* Array of op arguments */ nfs_argop4 * const argarray = arg->arg_compound4.argarray.argarray_val; nfs_resop4 *resarray; nsecs_elapsed_t op_start_time; struct timespec ts; int perm_flags; char *tagname = NULL; char *notag = "NO TAG"; if (compound4_minor > 2) { LogCrit(COMPONENT_NFS_V4, "Bad Minor Version %d", compound4_minor); res->res_compound4.status = NFS4ERR_MINOR_VERS_MISMATCH; res->res_compound4.resarray.resarray_len = 0; return NFS_REQ_OK; } if ((nfs_param.nfsv4_param.minor_versions & (1 << compound4_minor)) == 0) { LogInfo(COMPONENT_NFS_V4, "Unsupported minor version %d", compound4_minor); res->res_compound4.status = NFS4ERR_MINOR_VERS_MISMATCH; res->res_compound4.resarray.resarray_len = 0; return NFS_REQ_OK; } /* Keeping the same tag as in the arguments */ res->res_compound4.tag.utf8string_len = arg->arg_compound4.tag.utf8string_len; if (res->res_compound4.tag.utf8string_len > 0) { res->res_compound4.tag.utf8string_val = gsh_malloc(res->res_compound4.tag.utf8string_len + 1); memcpy(res->res_compound4.tag.utf8string_val, arg->arg_compound4.tag.utf8string_val, res->res_compound4.tag.utf8string_len); res->res_compound4.tag.utf8string_val[ res->res_compound4.tag.utf8string_len] = '\0'; /* Check if the tag is a valid utf8 string */ status = nfs4_utf8string2dynamic(&(res->res_compound4.tag), UTF8_SCAN_ALL, &tagname); if (status != 0) { status = NFS4ERR_INVAL; res->res_compound4.status = status; res->res_compound4.resarray.resarray_len = 0; return NFS_REQ_OK; } } else { res->res_compound4.tag.utf8string_val = NULL; tagname = notag; } /* Managing the operation list */ LogDebug(COMPONENT_NFS_V4, "COMPOUND: There are %d operations, res = %p, tag = %s", argarray_len, res, tagname); if (tagname != notag) gsh_free(tagname); /* Check for empty COMPOUND request */ if (argarray_len == 0) { LogMajor(COMPONENT_NFS_V4, "An empty COMPOUND (no operation in it) was received"); res->res_compound4.status = NFS4_OK; res->res_compound4.resarray.resarray_len = 0; return NFS_REQ_OK; } /* Check for too long request */ if (argarray_len > 100) { LogMajor(COMPONENT_NFS_V4, "A COMPOUND with too many operations (%d) was received", argarray_len); res->res_compound4.status = NFS4ERR_RESOURCE; res->res_compound4.resarray.resarray_len = 0; return NFS_REQ_OK; } /* Initialisation of the compound request internal's data */ memset(&data, 0, sizeof(data)); op_ctx->nfs_minorvers = compound4_minor; /* Minor version related stuff */ data.minorversion = compound4_minor; data.req = req; /* Building the client credential field */ if (nfs_rpc_req2client_cred(req, &(data.credential)) == -1) return NFS_REQ_DROP; /* Malformed credential */ /* Keeping the same tag as in the arguments */ res->res_compound4.tag.utf8string_len = arg->arg_compound4.tag.utf8string_len; /* Allocating the reply nfs_resop4 */ res->res_compound4.resarray.resarray_val = gsh_calloc(argarray_len, sizeof(struct nfs_resop4)); res->res_compound4.resarray.resarray_len = argarray_len; resarray = res->res_compound4.resarray.resarray_val; /* Manage errors NFS4ERR_OP_NOT_IN_SESSION and NFS4ERR_NOT_ONLY_OP. * These checks apply only to 4.1 */ if (compound4_minor > 0) { /* Check for valid operation to start an NFS v4.1 COMPOUND: */ if (argarray[0].argop != NFS4_OP_ILLEGAL && argarray[0].argop != NFS4_OP_SEQUENCE && argarray[0].argop != NFS4_OP_EXCHANGE_ID && argarray[0].argop != NFS4_OP_CREATE_SESSION && argarray[0].argop != NFS4_OP_DESTROY_SESSION && argarray[0].argop != NFS4_OP_BIND_CONN_TO_SESSION && argarray[0].argop != NFS4_OP_DESTROY_CLIENTID) { status = NFS4ERR_OP_NOT_IN_SESSION; res->res_compound4.status = status; res->res_compound4.resarray.resarray_len = 0; return NFS_REQ_OK; } if (argarray_len > 1) { /* If not prepended by OP4_SEQUENCE, OP4_EXCHANGE_ID * should be the only request in the compound see * 18.35.3. and test EID8 for details * * If not prepended bu OP4_SEQUENCE, OP4_CREATE_SESSION * should be the only request in the compound see * 18.36.3 and test CSESS23 for details * * If the COMPOUND request does not start with SEQUENCE, * and if DESTROY_SESSION is not the sole operation, * then server MUST return NFS4ERR_NOT_ONLY_OP. See * 18.37.3 nd test DSESS9005 for details */ if (argarray[0].argop == NFS4_OP_EXCHANGE_ID || argarray[0].argop == NFS4_OP_CREATE_SESSION || argarray[0].argop == NFS4_OP_DESTROY_CLIENTID || argarray[0].argop == NFS4_OP_DESTROY_SESSION || argarray[0].argop == NFS4_OP_BIND_CONN_TO_SESSION) { status = NFS4ERR_NOT_ONLY_OP; res->res_compound4.status = status; res->res_compound4.resarray.resarray_len = 0; return NFS_REQ_OK; } } /* If the COMPOUND request starts with SEQUENCE, and if the * sessionids specified in SEQUENCE and DESTROY_SESSION are the * same, then DESTROY_SESSION MUST be the final operation in the * COMPOUND request. */ if (argarray_len > 2 && argarray[0].argop == NFS4_OP_SEQUENCE && argarray[1].argop == NFS4_OP_DESTROY_SESSION && strncmp(argarray[0].nfs_argop4_u.opsequence.sa_sessionid, argarray[1] .nfs_argop4_u.opdestroy_session.dsa_sessionid, NFS4_SESSIONID_SIZE) == 0) { status = NFS4ERR_NOT_ONLY_OP; res->res_compound4.status = status; res->res_compound4.resarray.resarray_len = 0; return NFS_REQ_OK; } } for (i = 0; i < argarray_len; i++) { /* Used to check if OP_SEQUENCE is the first operation */ data.oppos = i; /* Verify BIND_CONN_TO_SESSION is not used in a compound * with length > 1. */ if (i > 0 && argarray[i].argop == NFS4_OP_BIND_CONN_TO_SESSION) { status = NFS4ERR_NOT_ONLY_OP; goto bad_op_state; } /* time each op */ now(&ts); op_start_time = timespec_diff(&ServerBootTime, &ts); opcode = argarray[i].argop; /* Handle opcode overflow */ if (opcode > LastOpcode[compound4_minor]) opcode = 0; if (compound4_minor > 0 && data.session != NULL && data.session->fore_channel_attrs.ca_maxoperations == i) { status = NFS4ERR_TOO_MANY_OPS; goto bad_op_state; } LogDebug(COMPONENT_NFS_V4, "Request %d: opcode %d is %s", i, argarray[i].argop, optabv4[opcode].name); perm_flags = optabv4[opcode].exp_perm_flags & EXPORT_OPTION_ACCESS_MASK; if (perm_flags != 0) { status = nfs4_Is_Fh_Empty(&data.currentFH); if (status != NFS4_OK) { LogDebug(COMPONENT_NFS_V4, "Status of %s for CurrentFH in position %d = %s", optabv4[opcode].name, i, nfsstat4_to_str(status)); goto bad_op_state; } /* Operation uses a CurrentFH, so we can check export * perms. Perms should even be set reasonably for pseudo * file system. */ LogMidDebugAlt(COMPONENT_NFS_V4, COMPONENT_EXPORT, "Check export perms export = %08x req = %08x", op_ctx->export_perms->options & EXPORT_OPTION_ACCESS_MASK, perm_flags); if ((op_ctx->export_perms->options & perm_flags) != perm_flags) { /* Export doesn't allow requested * access for this client. */ if ((perm_flags & EXPORT_OPTION_MODIFY_ACCESS) != 0) status = NFS4ERR_ROFS; else status = NFS4ERR_ACCESS; LogDebugAlt(COMPONENT_NFS_V4, COMPONENT_EXPORT, "Status of %s due to export permissions in position %d = %s", optabv4[opcode].name, i, nfsstat4_to_str(status)); bad_op_state: /* All the operation, like NFS4_OP_ACESS, have * a first replied field called .status */ resarray[i].nfs_resop4_u.opaccess.status = status; resarray[i].resop = argarray[i].argop; /* Do not manage the other requests in the * COMPOUND. */ res->res_compound4.resarray.resarray_len = i + 1; break; } } #ifdef USE_LTTNG tracepoint(nfs_rpc, v4op_start, i, argarray[i].argop, optabv4[opcode].name); #endif status = (optabv4[opcode].funct) (&argarray[i], &data, &resarray[i]); #ifdef USE_LTTNG tracepoint(nfs_rpc, v4op_end, i, argarray[i].argop, optabv4[opcode].name, nfsstat4_to_str(status)); #endif LogCompoundFH(&data); /* All the operation, like NFS4_OP_ACESS, have a first replyied * field called .status */ resarray[i].nfs_resop4_u.opaccess.status = status; server_stats_nfsv4_op_done(opcode, op_start_time, status); LogDebug(COMPONENT_NFS_V4, "Status of %s in position %d = %s", optabv4[opcode].name, i, nfsstat4_to_str(status)); if (status != NFS4_OK) { /* An error occured, we do not manage the other requests * in the COMPOUND, this may be a regular behavior */ res->res_compound4.resarray.resarray_len = i + 1; break; } /* Check Req size */ /* NFS_V4.1 specific stuff */ if (data.use_drc) { /* Replay cache, only true for SEQUENCE or * CREATE_SESSION w/o SEQUENCE. Since will only be set * in those cases, no need to check operation or * anything. */ /* Free the reply allocated above */ gsh_free(res->res_compound4.resarray.resarray_val); /* Copy the reply from the cache */ res->res_compound4_extended = *data.cached_res; status = ((COMPOUND4res *) data.cached_res)->status; LogFullDebug(COMPONENT_SESSIONS, "Use session replay cache %p result %s", data.cached_res, nfsstat4_to_str(status)); break; /* Exit the for loop */ } } /* for */ server_stats_compound_done(argarray_len, status); /* Complete the reply, in particular, tell where you stopped if * unsuccessfull COMPOUD */ res->res_compound4.status = status; /* Manage session's DRC: keep NFS4.1 replay for later use, but don't * save a replayed result again. */ if (data.cached_res != NULL && !data.use_drc) { /* Pointer has been set by nfs4_op_sequence and points to slot * to cache result in. */ LogFullDebug(COMPONENT_SESSIONS, "Save result in session replay cache %p sizeof nfs_res_t=%d", data.cached_res, (int)sizeof(nfs_res_t)); /* Indicate to nfs4_Compound_Free that this reply is cached. */ res->res_compound4_extended.res_cached = true; /* If the cache is already in use, free it. */ if (data.cached_res->res_cached) { data.cached_res->res_cached = false; nfs4_Compound_Free((nfs_res_t *) data.cached_res); } /* Save the result in the cache. */ *data.cached_res = res->res_compound4_extended; } /* If we have reserved a lease, update it and release it */ if (data.preserved_clientid != NULL) { /* Update and release lease */ PTHREAD_MUTEX_lock(&data.preserved_clientid->cid_mutex); update_lease(data.preserved_clientid); PTHREAD_MUTEX_unlock(&data.preserved_clientid->cid_mutex); } if (status != NFS4_OK) LogDebug(COMPONENT_NFS_V4, "End status = %s lastindex = %d", nfsstat4_to_str(status), i); compound_data_Free(&data); return NFS_REQ_OK; } /* nfs4_Compound */ /** * * @brief Free the result for one NFS4_OP * * This function frees any memory allocated for the result of an NFSv4 * operation. * * @param[in,out] res The result to be freed * */ void nfs4_Compound_FreeOne(nfs_resop4 *res) { int opcode; opcode = (res->resop != NFS4_OP_ILLEGAL) ? res->resop : 0; /* opcode 0 for illegals */ optabv4[opcode].free_res(res); } /** * * @brief Free the result for NFS4PROC_COMPOUND * * This function frees the result for one NFS4PROC_COMPOUND. * * @param[in] res The result * */ void nfs4_Compound_Free(nfs_res_t *res) { unsigned int i = 0; log_components_t component = COMPONENT_NFS_V4; if (isFullDebug(COMPONENT_SESSIONS)) component = COMPONENT_SESSIONS; if (res->res_compound4_extended.res_cached) { LogFullDebug(component, "Skipping free of NFS4 result %p", res); return; } LogFullDebug(component, "Compound Free %p (resarraylen=%i)", res, res->res_compound4.resarray.resarray_len); for (i = 0; i < res->res_compound4.resarray.resarray_len; i++) { nfs_resop4 *val = &res->res_compound4.resarray.resarray_val[i]; if (val) { /* !val is an error case, but it can occur, so avoid * indirect on NULL */ nfs4_Compound_FreeOne(val); } } gsh_free(res->res_compound4.resarray.resarray_val); if (res->res_compound4.tag.utf8string_val) gsh_free(res->res_compound4.tag.utf8string_val); } /** * @brief Free a compound data structure * * This function frees one compound data structure. * * @param[in,out] data The compound_data_t to be freed * */ void compound_data_Free(compound_data_t *data) { /* Release refcounted cache entries */ set_current_entry(data, NULL); set_saved_entry(data, NULL); if (data->session) { dec_session_ref(data->session); data->session = NULL; } /* Release CurrentFH reference to export. */ if (op_ctx->ctx_export) { put_gsh_export(op_ctx->ctx_export); op_ctx->ctx_export = NULL; op_ctx->fsal_export = NULL; } /* Release SavedFH reference to export. */ if (data->saved_export) { put_gsh_export(data->saved_export); data->saved_export = NULL; } if (data->currentFH.nfs_fh4_val != NULL) gsh_free(data->currentFH.nfs_fh4_val); if (data->savedFH.nfs_fh4_val != NULL) gsh_free(data->savedFH.nfs_fh4_val); } /* compound_data_Free */ /** * * @brief Copy the result for one NFS4_OP * * This function copies the result structure for a single NFSv4 * operation. * * @param[out] res_dst Buffer to which to copy the result * @param[in] res_src The result to copy * */ void nfs4_Compound_CopyResOne(nfs_resop4 *res_dst, nfs_resop4 *res_src) { /* Copy base data structure */ memcpy(res_dst, res_src, sizeof(*res_dst)); /* Do deep copy where necessary */ switch (res_src->resop) { case NFS4_OP_ACCESS: break; case NFS4_OP_CLOSE: nfs4_op_close_CopyRes(&res_dst->nfs_resop4_u.opclose, &res_src->nfs_resop4_u.opclose); return; case NFS4_OP_COMMIT: case NFS4_OP_CREATE: case NFS4_OP_DELEGPURGE: case NFS4_OP_DELEGRETURN: case NFS4_OP_GETATTR: case NFS4_OP_GETFH: case NFS4_OP_LINK: break; case NFS4_OP_LOCK: nfs4_op_lock_CopyRes(&res_dst->nfs_resop4_u.oplock, &res_src->nfs_resop4_u.oplock); return; case NFS4_OP_LOCKT: break; case NFS4_OP_LOCKU: nfs4_op_locku_CopyRes(&res_dst->nfs_resop4_u.oplocku, &res_src->nfs_resop4_u.oplocku); return; case NFS4_OP_LOOKUP: case NFS4_OP_LOOKUPP: case NFS4_OP_NVERIFY: break; case NFS4_OP_OPEN: nfs4_op_open_CopyRes(&res_dst->nfs_resop4_u.opopen, &res_src->nfs_resop4_u.opopen); return; case NFS4_OP_OPENATTR: break; case NFS4_OP_OPEN_CONFIRM: nfs4_op_open_confirm_CopyRes( &res_dst->nfs_resop4_u.opopen_confirm, &res_src->nfs_resop4_u.opopen_confirm); return; case NFS4_OP_OPEN_DOWNGRADE: nfs4_op_open_downgrade_CopyRes( &res_dst->nfs_resop4_u.opopen_downgrade, &res_src->nfs_resop4_u.opopen_downgrade); return; case NFS4_OP_PUTFH: case NFS4_OP_PUTPUBFH: case NFS4_OP_PUTROOTFH: case NFS4_OP_READ: case NFS4_OP_READDIR: case NFS4_OP_READLINK: case NFS4_OP_REMOVE: case NFS4_OP_RENAME: case NFS4_OP_RENEW: case NFS4_OP_RESTOREFH: case NFS4_OP_SAVEFH: case NFS4_OP_SECINFO: case NFS4_OP_SETATTR: case NFS4_OP_SETCLIENTID: case NFS4_OP_SETCLIENTID_CONFIRM: case NFS4_OP_VERIFY: case NFS4_OP_WRITE: case NFS4_OP_RELEASE_LOCKOWNER: break; case NFS4_OP_EXCHANGE_ID: case NFS4_OP_CREATE_SESSION: case NFS4_OP_SEQUENCE: case NFS4_OP_GETDEVICEINFO: case NFS4_OP_GETDEVICELIST: case NFS4_OP_BACKCHANNEL_CTL: case NFS4_OP_BIND_CONN_TO_SESSION: case NFS4_OP_DESTROY_SESSION: case NFS4_OP_FREE_STATEID: case NFS4_OP_GET_DIR_DELEGATION: case NFS4_OP_LAYOUTCOMMIT: case NFS4_OP_LAYOUTGET: case NFS4_OP_LAYOUTRETURN: case NFS4_OP_SECINFO_NO_NAME: case NFS4_OP_SET_SSV: case NFS4_OP_TEST_STATEID: case NFS4_OP_WANT_DELEGATION: case NFS4_OP_DESTROY_CLIENTID: case NFS4_OP_RECLAIM_COMPLETE: /* NFSv4.2 */ case NFS4_OP_ALLOCATE: case NFS4_OP_COPY: case NFS4_OP_COPY_NOTIFY: case NFS4_OP_DEALLOCATE: case NFS4_OP_IO_ADVISE: case NFS4_OP_LAYOUTERROR: case NFS4_OP_LAYOUTSTATS: case NFS4_OP_OFFLOAD_CANCEL: case NFS4_OP_OFFLOAD_STATUS: case NFS4_OP_READ_PLUS: case NFS4_OP_SEEK: case NFS4_OP_WRITE_SAME: case NFS4_OP_CLONE: /* NFSv4.3 */ case NFS4_OP_GETXATTR: case NFS4_OP_SETXATTR: case NFS4_OP_LISTXATTR: case NFS4_OP_REMOVEXATTR: case NFS4_OP_LAST_ONE: break; case NFS4_OP_ILLEGAL: break; } /* switch */ LogFatal(COMPONENT_NFS_V4, "Copy one result not implemented for %d", res_src->resop); } /** * * @brief Copy the result for NFS4PROC_COMPOUND * * This function copies a single COMPOUND result. * * @param[out] res_dst Buffer to which to copy the result * @param[in] res_src Result to copy * */ void nfs4_Compound_CopyRes(nfs_res_t *res_dst, nfs_res_t *res_src) { unsigned int i = 0; LogFullDebug(COMPONENT_NFS_V4, "Copy result of %p to %p (resarraylen : %i)", res_src, res_dst, res_src->res_compound4.resarray.resarray_len); for (i = 0; i < res_src->res_compound4.resarray.resarray_len; i++) nfs4_Compound_CopyResOne( &res_dst->res_compound4.resarray.resarray_val[i], &res_src->res_compound4.resarray.resarray_val[i]); } /* @} */ nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_cb_Compound.c000066400000000000000000000060211324272410200224340ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * Portions Copyright (C) 2012, The Linux Box Corporation * Contributor : Matt Benjamin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * \file nfs4_cb_Compound.c * \brief Routines used for managing the NFS4/CB COMPOUND functions. * * Routines used for managing the NFS4/CB COMPOUND functions. */ #include "config.h" #include #include #include #include #include #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "nfs4.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_rpc_callback.h" static const nfs4_cb_tag_t cbtagtab4[] = { {NFS4_CB_TAG_DEFAULT, "Ganesha CB Compound", 19}, }; /* Some CITI-inspired compound helper ideas */ void cb_compound_init_v4(nfs4_compound_t *cbt, uint32_t n_ops, uint32_t minorversion, uint32_t ident, char *tag, uint32_t tag_len) { /* args */ memset(cbt, 0, sizeof(nfs4_compound_t)); /* XDRS */ cbt->v_u.v4.args.minorversion = minorversion; cbt->v_u.v4.args.callback_ident = ident; cbt->v_u.v4.args.argarray.argarray_val = alloc_cb_argop(n_ops); cbt->v_u.v4.args.argarray.argarray_len = 0; /* not n_ops, see below */ if (tag) { /* sender must ensure tag is safe to queue */ cbt->v_u.v4.args.tag.utf8string_val = tag; cbt->v_u.v4.args.tag.utf8string_len = tag_len; } else { cbt->v_u.v4.args.tag.utf8string_val = cbtagtab4[NFS4_CB_TAG_DEFAULT].val; cbt->v_u.v4.args.tag.utf8string_len = cbtagtab4[NFS4_CB_TAG_DEFAULT].len; } cbt->v_u.v4.res.resarray.resarray_val = alloc_cb_resop(n_ops); cbt->v_u.v4.res.resarray.resarray_len = 0; } void cb_compound_add_op(nfs4_compound_t *cbt, nfs_cb_argop4 *src) { /* old value */ uint32_t ix = (cbt->v_u.v4.args.argarray.argarray_len)++; nfs_cb_argop4 *dst = cbt->v_u.v4.args.argarray.argarray_val + ix; *dst = *src; /* nothing to do for (zero) val region */ cbt->v_u.v4.res.resarray.resarray_len++; } void cb_compound_free(nfs4_compound_t *cbt) { free_cb_argop(cbt->v_u.v4.args.argarray.argarray_val); free_cb_resop(cbt->v_u.v4.res.resarray.resarray_val); } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_access.c000066400000000000000000000066051324272410200221530ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_access.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. */ #include "config.h" #include #include #include #include #include "log.h" #include "gsh_rpc.h" #include "nfs4.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_creds.h" #include "nfs_proto_functions.h" #include "nfs_convert.h" #include "nfs_file_handle.h" #include "nfs_proto_tools.h" /** * @brief NFS4_OP_ACCESS, checks for file's accessibility. * * This function impelments the NFS4_OP_ACCESS operation, which checks * for file's accessibility. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 362 * */ int nfs4_op_access(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { ACCESS4args * const arg_ACCESS4 = &op->nfs_argop4_u.opaccess; ACCESS4res * const res_ACCESS4 = &resp->nfs_resop4_u.opaccess; fsal_errors_t fsal_status; uint32_t max_access = (ACCESS4_READ | ACCESS4_LOOKUP | ACCESS4_MODIFY | ACCESS4_EXTEND | ACCESS4_DELETE | ACCESS4_EXECUTE); /* initialize output */ res_ACCESS4->ACCESS4res_u.resok4.supported = 0; res_ACCESS4->ACCESS4res_u.resok4.access = 0; resp->resop = NFS4_OP_ACCESS; res_ACCESS4->status = NFS4_OK; /* Do basic checks on a filehandle */ res_ACCESS4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false); if (res_ACCESS4->status != NFS4_OK) return res_ACCESS4->status; /* Check for input parameter's sanity */ if (arg_ACCESS4->access > max_access) { res_ACCESS4->status = NFS4ERR_INVAL; return res_ACCESS4->status; } /* Perform the 'access' call */ fsal_status = nfs_access_op(data->current_obj, arg_ACCESS4->access, &res_ACCESS4->ACCESS4res_u.resok4.access, &res_ACCESS4->ACCESS4res_u.resok4.supported); if (fsal_status == ERR_FSAL_NO_ERROR || fsal_status == ERR_FSAL_ACCESS) res_ACCESS4->status = NFS4_OK; else res_ACCESS4->status = nfs4_Errno(fsal_status); return res_ACCESS4->status; } /* nfs4_op_access */ /** * @brief Free memory allocated for ACCESS result * * This function frees any memory allocated for the result of the * NFS4_OP_ACCESS operatino. * * @param[in,out] resp nfs4_op results */ void nfs4_op_access_Free(nfs_resop4 *resp) { /* Nothing to do */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_close.c000066400000000000000000000230551324272410200220150ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_close.c * * @brief Implementation of the NFS4_OP_CLOSE operation * */ #include "config.h" #include #include "log.h" #include "fsal.h" #include "nfs4.h" #include "sal_functions.h" #include "nfs_proto_tools.h" #include "nfs_proto_functions.h" #include "nfs_convert.h" /* Tag passed to state functions */ static const char *close_tag = "CLOSE"; /** * @brief Clean up the current layouts * * @note state_lock MUST be held for write * * @param[in] data Current compound data */ void cleanup_layouts(compound_data_t *data) { struct glist_head *glist = NULL; struct glist_head *glistn = NULL; struct state_hdl *ostate; ostate = data->current_obj->state_hdl; if (!ostate) return; /* We can't simply grab a pointer to a layout state * and free it later, since a client could have * multiple layout states (since a layout state covers * layouts of only one layout type) each marked * return_on_close. */ glist_for_each(glist, &ostate->file.list_of_states) { state_t *state = glist_entry(glist, state_t, state_list); state_owner_t *owner = get_state_owner_ref(state); if (owner == NULL) { /* Skip states that have gone stale. */ continue; } if ((state->state_type == STATE_TYPE_SHARE) && (owner->so_type == STATE_OPEN_OWNER_NFSV4) && (owner->so_owner.so_nfs4_owner.so_clientid == data->session->clientid)) { dec_state_owner_ref(owner); return; } dec_state_owner_ref(owner); } glist_for_each_safe(glist, glistn, &ostate->file.list_of_states) { state_t *state = glist_entry(glist, state_t, state_list); bool deleted = false; struct pnfs_segment entire = { .io_mode = LAYOUTIOMODE4_ANY, .offset = 0, .length = NFS4_UINT64_MAX }; state_owner_t *owner = get_state_owner_ref(state); if (owner == NULL) { /* Skip states that have gone stale. */ continue; } if ((state->state_type == STATE_TYPE_LAYOUT) && (owner->so_owner.so_nfs4_owner.so_clientrec == data->session->clientid_record) && state->state_data.layout.state_return_on_close) { nfs4_return_one_state(data->current_obj, LAYOUTRETURN4_FILE, circumstance_roc, state, entire, 0, NULL, &deleted); if (!deleted) { LogCrit(COMPONENT_PNFS, "Layout state not destroyed on last close return."); } } dec_state_owner_ref(owner); } } /** * * Brief Implemtation of NFS4_OP_CLOSE * * This function implemtats the NFS4_OP_CLOSE * operation. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 362 */ int nfs4_op_close(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { /* Short alias for arguments */ CLOSE4args * const arg_CLOSE4 = &op->nfs_argop4_u.opclose; /* Short alias for response */ CLOSE4res * const res_CLOSE4 = &resp->nfs_resop4_u.opclose; /* Status for NFS protocol functions */ nfsstat4 nfs_status = NFS4_OK; /* The state for the open to be closed */ state_t *state_found = NULL; /* The open owner of the open state being closed */ state_owner_t *open_owner = NULL; /* Iterator over the state list */ struct glist_head *glist = NULL; /* Secondary safe iterator to continue traversal on delete */ struct glist_head *glistn = NULL; LogDebug(COMPONENT_STATE, "Entering NFS v4 CLOSE handler ----------------------------"); memset(res_CLOSE4, 0, sizeof(CLOSE4res)); resp->resop = NFS4_OP_CLOSE; res_CLOSE4->status = NFS4_OK; /* Do basic checks on a filehandle Object should be a file */ res_CLOSE4->status = nfs4_sanity_check_FH(data, REGULAR_FILE, false); if (res_CLOSE4->status != NFS4_OK) return res_CLOSE4->status; /* Check stateid correctness and get pointer to state */ nfs_status = nfs4_Check_Stateid(&arg_CLOSE4->open_stateid, data->current_obj, &state_found, data, data->minorversion == 0 ? STATEID_SPECIAL_FOR_CLOSE_40 : STATEID_SPECIAL_FOR_CLOSE_41, arg_CLOSE4->seqid, data->minorversion == 0, close_tag); if (nfs_status != NFS4_OK && nfs_status != NFS4ERR_REPLAY) { res_CLOSE4->status = nfs_status; LogDebug(COMPONENT_STATE, "CLOSE failed nfs4_Check_Stateid"); return res_CLOSE4->status; } if (state_found == NULL) { /* Assume this is a replayed close */ res_CLOSE4->status = NFS4_OK; memcpy(res_CLOSE4->CLOSE4res_u.open_stateid.other, arg_CLOSE4->open_stateid.other, OTHERSIZE); res_CLOSE4->CLOSE4res_u.open_stateid.seqid = arg_CLOSE4->open_stateid.seqid + 1; if (res_CLOSE4->CLOSE4res_u.open_stateid.seqid == 0) res_CLOSE4->CLOSE4res_u.open_stateid.seqid = 1; LogDebug(COMPONENT_STATE, "CLOSE failed nfs4_Check_Stateid must have already been closed. But treating it as replayed close and returning NFS4_OK"); return res_CLOSE4->status; } open_owner = get_state_owner_ref(state_found); if (open_owner == NULL) { /* Unexpected, but something just went stale. */ res_CLOSE4->status = NFS4ERR_STALE; goto out3; } PTHREAD_MUTEX_lock(&open_owner->so_mutex); /* Check seqid */ if (data->minorversion == 0) { if (!Check_nfs4_seqid(open_owner, arg_CLOSE4->seqid, op, data->current_obj, resp, close_tag)) { /* Response is all setup for us and LogDebug * told what was wrong */ PTHREAD_MUTEX_unlock(&open_owner->so_mutex); goto out2; } } PTHREAD_MUTEX_unlock(&open_owner->so_mutex); PTHREAD_RWLOCK_wrlock(&data->current_obj->state_hdl->state_lock); /* Check is held locks remain */ glist_for_each(glist, &state_found->state_data.share.share_lockstates) { state_t *lock_state = glist_entry(glist, state_t, state_data.lock.state_sharelist); if (!glist_empty(&lock_state->state_data.lock.state_locklist)) { /* Is this actually what we want to do, rather * than freeing all locks on close? * Especially since the next thing we do is * go through ane release any lock states. */ res_CLOSE4->status = NFS4ERR_LOCKS_HELD; PTHREAD_RWLOCK_unlock( &data->current_obj->state_hdl->state_lock); LogDebug(COMPONENT_STATE, "NFS4 Close with existing locks"); goto out; } } if (data->minorversion == 0) { /* Handle stateid/seqid for success for v4.0 */ update_stateid(state_found, &res_CLOSE4->CLOSE4res_u.open_stateid, data, close_tag); } else { /* In NFS V4.1 and later, the server SHOULD return a special * invalid stateid to prevent re-use of a now closed stateid. */ memcpy(&res_CLOSE4->CLOSE4res_u.open_stateid.other, all_zero, sizeof(res_CLOSE4->CLOSE4res_u.open_stateid.other)); res_CLOSE4->CLOSE4res_u.open_stateid.seqid = UINT32_MAX; } /* File is closed, release the corresponding lock states */ glist_for_each_safe(glist, glistn, &state_found->state_data.share.share_lockstates) { state_t *lock_state = glist_entry(glist, state_t, state_data.lock.state_sharelist); /* If the FSAL supports extended ops, this will result in * closing any open files the FSAL has for this lock state. */ state_del_locked(lock_state); } /* File is closed, release the corresponding state. If the FSAL * supports extended ops, this will result in closing any open files * the FSAL has for this state. */ state_del_locked(state_found); /* Poison the current stateid */ data->current_stateid_valid = false; if (data->minorversion > 0) cleanup_layouts(data); /* Fill in the clientid for NFSv4.0 */ if (data->minorversion == 0) { op_ctx->clientid = &open_owner->so_owner.so_nfs4_owner.so_clientid; } if (data->minorversion == 0) op_ctx->clientid = NULL; PTHREAD_RWLOCK_unlock(&data->current_obj->state_hdl->state_lock); res_CLOSE4->status = NFS4_OK; if (isFullDebug(COMPONENT_STATE) && isFullDebug(COMPONENT_MEMLEAKS)) { nfs_State_PrintAll(); nfs4_owner_PrintAll(); } out: /* Save the response in the open owner */ if (data->minorversion == 0) { Copy_nfs4_state_req(open_owner, arg_CLOSE4->seqid, op, data->current_obj, resp, close_tag); } out2: dec_state_owner_ref(open_owner); out3: dec_state_t_ref(state_found); return res_CLOSE4->status; } /* nfs4_op_close */ /** * @brief Free memory allocated for CLOSE result * * This function frees any memory allocated for the result of the * NFS4_OP_CLOSE operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_close_Free(nfs_resop4 *resp) { /* Nothing to be done */ } void nfs4_op_close_CopyRes(CLOSE4res *res_dst, CLOSE4res *res_src) { /* Nothing to deep copy */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_commit.c000066400000000000000000000110071324272410200221720ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_commit.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. * * */ #include "config.h" #include #include #include #include #include #include "hashtable.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_convert.h" #include "nfs_file_handle.h" #include "fsal_pnfs.h" static int op_dscommit(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp); /** * @brief Implemtation of NFS4_OP_COMMIT * * This function implemtats NFS4_OP_COMMIT. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661 p. 362-3 * */ int nfs4_op_commit(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { COMMIT4args * const arg_COMMIT4 = &op->nfs_argop4_u.opcommit; COMMIT4res * const res_COMMIT4 = &resp->nfs_resop4_u.opcommit; fsal_status_t fsal_status = { 0, 0 }; struct gsh_buffdesc verf_desc; resp->resop = NFS4_OP_COMMIT; res_COMMIT4->status = NFS4_OK; LogFullDebug(COMPONENT_NFS_V4, "Commit order over offset = %" PRIu64", size = %" PRIu32, arg_COMMIT4->offset, arg_COMMIT4->count); if ((nfs4_Is_Fh_DSHandle(&data->currentFH))) return op_dscommit(op, data, resp); /* * Do basic checks on a filehandle Commit is done only on a file */ res_COMMIT4->status = nfs4_sanity_check_FH(data, REGULAR_FILE, true); if (res_COMMIT4->status != NFS4_OK) return res_COMMIT4->status; fsal_status = fsal_commit(data->current_obj, arg_COMMIT4->offset, arg_COMMIT4->count); if (FSAL_IS_ERROR(fsal_status)) { res_COMMIT4->status = nfs4_Errno_status(fsal_status); return res_COMMIT4->status; } verf_desc.addr = &res_COMMIT4->COMMIT4res_u.resok4.writeverf; verf_desc.len = sizeof(verifier4); op_ctx->fsal_export->exp_ops.get_write_verifier(op_ctx->fsal_export, &verf_desc); LogFullDebug(COMPONENT_NFS_V4, "Commit verifier %d-%d", ((int *)verf_desc.addr)[0], ((int *)verf_desc.addr)[1]); /* If you reach this point, then an error occured */ res_COMMIT4->status = NFS4_OK; return res_COMMIT4->status; } /* nfs4_op_commit */ /** * @brief Free memory allocated for COMMIT result * * This function frees any memory allocated for the result of the * NFS4_OP_COMMIT operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_commit_Free(nfs_resop4 *resp) { /* Nothing to be done */ } /** * * @brief Call pNFS data server commit * * This function bypasses cache_inode and calls down the FSAL to * perform a data-server commit. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661 p. 362-3 * */ static int op_dscommit(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { COMMIT4args * const arg_COMMIT4 = &op->nfs_argop4_u.opcommit; COMMIT4res * const res_COMMIT4 = &resp->nfs_resop4_u.opcommit; /* NFSv4 status code */ nfsstat4 nfs_status = 0; /* Construct the FSAL file handle */ /* Call the commit operation */ nfs_status = data->current_ds->dsh_ops.commit( data->current_ds, op_ctx, arg_COMMIT4->offset, arg_COMMIT4->count, &res_COMMIT4->COMMIT4res_u.resok4.writeverf); res_COMMIT4->status = nfs_status; return res_COMMIT4->status; } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_create.c000066400000000000000000000205531324272410200221530ustar00rootroot00000000000000 /* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_create.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. * */ #include "config.h" #include #include #include #include #include #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_creds.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_convert.h" #include "nfs_file_handle.h" #include "export_mgr.h" /** * @brief NFS4_OP_CREATE, creates a non-regular entry * * This function implements the NFS4_OP_CREATE operation, which * creates a non-regular entry. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 363 */ int nfs4_op_create(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { CREATE4args * const arg_CREATE4 = &op->nfs_argop4_u.opcreate; CREATE4res * const res_CREATE4 = &resp->nfs_resop4_u.opcreate; struct fsal_obj_handle *obj_parent = NULL; struct fsal_obj_handle *obj_new = NULL; struct attrlist sattr; int convrc = 0; char *name = NULL; char *link_content = NULL; struct fsal_export *exp_hdl; fsal_status_t fsal_status; object_file_type_t type; memset(&sattr, 0, sizeof(sattr)); resp->resop = NFS4_OP_CREATE; res_CREATE4->status = NFS4_OK; /* Do basic checks on a filehandle */ res_CREATE4->status = nfs4_sanity_check_FH(data, DIRECTORY, false); if (res_CREATE4->status != NFS4_OK) goto out; /* if quota support is active, then we should check is the FSAL allows * inode creation or not */ exp_hdl = op_ctx->fsal_export; fsal_status = exp_hdl->exp_ops.check_quota(exp_hdl, op_ctx->ctx_export->fullpath, FSAL_QUOTA_INODES); if (FSAL_IS_ERROR(fsal_status)) { res_CREATE4->status = NFS4ERR_DQUOT; goto out; } /* Ask only for supported attributes */ if (!nfs4_Fattr_Supported(&arg_CREATE4->createattrs)) { res_CREATE4->status = NFS4ERR_ATTRNOTSUPP; goto out; } /* Do not use READ attr, use WRITE attr */ if (!nfs4_Fattr_Check_Access (&arg_CREATE4->createattrs, FATTR4_ATTR_WRITE)) { res_CREATE4->status = NFS4ERR_INVAL; goto out; } /* This operation is used to create a non-regular file, * this means: - a symbolic link * - a block device file * - a character device file * - a socket file * - a fifo * - a directory * * You can't use this operation to create a regular file, * you have to use NFS4_OP_OPEN for this */ /* Validate and convert the UFT8 objname to a regular string */ res_CREATE4->status = nfs4_utf8string2dynamic(&arg_CREATE4->objname, UTF8_SCAN_ALL, &name); if (res_CREATE4->status != NFS4_OK) goto out; /* Convert current FH into a obj, the current_obj (assocated with the current FH will be used for this */ obj_parent = data->current_obj; /* The currentFH must point to a directory * (objects are always created within a directory) */ if (data->current_filetype != DIRECTORY) { res_CREATE4->status = NFS4ERR_NOTDIR; goto out; } res_CREATE4->CREATE4res_u.resok4.cinfo.before = fsal_get_changeid4(obj_parent); /* Convert the incoming fattr4 to a vattr structure, * if such arguments are supplied */ if (arg_CREATE4->createattrs.attrmask.bitmap4_len != 0) { /* Arguments were supplied, extract them */ convrc = nfs4_Fattr_To_FSAL_attr(&sattr, &arg_CREATE4->createattrs, data); if (convrc != NFS4_OK) { res_CREATE4->status = convrc; goto out; } } /* Create either a symbolic link or a directory */ switch (arg_CREATE4->objtype.type) { case NF4LNK: /* Convert the name to link from into a regular string */ type = SYMBOLIC_LINK; res_CREATE4->status = nfs4_utf8string2dynamic( &arg_CREATE4->objtype.createtype4_u.linkdata, UTF8_SCAN_SYMLINK, &link_content); if (res_CREATE4->status != NFS4_OK) goto out; break; case NF4DIR: /* Create a new directory */ type = DIRECTORY; break; case NF4SOCK: /* Create a new socket file */ type = SOCKET_FILE; break; case NF4FIFO: /* Create a new socket file */ type = FIFO_FILE; break; case NF4CHR: /* Create a new socket file */ type = CHARACTER_FILE; sattr.rawdev.major = arg_CREATE4->objtype.createtype4_u.devdata.specdata1; sattr.rawdev.minor = arg_CREATE4->objtype.createtype4_u.devdata.specdata2; sattr.valid_mask |= ATTR_RAWDEV; break; case NF4BLK: /* Create a new socket file */ type = BLOCK_FILE; sattr.rawdev.major = arg_CREATE4->objtype.createtype4_u.devdata.specdata1; sattr.rawdev.minor = arg_CREATE4->objtype.createtype4_u.devdata.specdata2; sattr.valid_mask |= ATTR_RAWDEV; break; default: /* Should never happen, but return NFS4ERR_BADTYPE *in this case */ res_CREATE4->status = NFS4ERR_BADTYPE; goto out; } /* switch( arg_CREATE4.objtype.type ) */ if (!(sattr.valid_mask & ATTR_MODE)) { /* Make sure mode is set. */ if (type == DIRECTORY) sattr.mode = 0700; else sattr.mode = 0600; sattr.valid_mask |= ATTR_MODE; } fsal_status = fsal_create(obj_parent, name, type, &sattr, link_content, &obj_new, NULL); /* Release the attributes (may release an inherited ACL) */ fsal_release_attrs(&sattr); if (FSAL_IS_ERROR(fsal_status)) { res_CREATE4->status = nfs4_Errno_status(fsal_status); goto out; } /* Building the new file handle to replace the current FH */ if (!nfs4_FSALToFhandle(false, &data->currentFH, obj_new, op_ctx->ctx_export)) { res_CREATE4->status = NFS4ERR_SERVERFAULT; goto out; } /* Mark current_stateid as invalid */ data->current_stateid_valid = false; /* Set the mode if requested */ /* Use the same fattr mask for reply, if one attribute was not settable, NFS4ERR_ATTRNOTSUPP was replyied */ res_CREATE4->CREATE4res_u.resok4.attrset.bitmap4_len = arg_CREATE4->createattrs.attrmask.bitmap4_len; if (arg_CREATE4->createattrs.attrmask.bitmap4_len != 0) { /* copy over bitmap */ res_CREATE4->CREATE4res_u.resok4.attrset = arg_CREATE4->createattrs.attrmask; } memset(&res_CREATE4->CREATE4res_u.resok4.cinfo.after, 0, sizeof(changeid4)); res_CREATE4->CREATE4res_u.resok4.cinfo.after = fsal_get_changeid4(obj_parent); /* Operation is supposed to be atomic .... */ res_CREATE4->CREATE4res_u.resok4.cinfo.atomic = FALSE; LogFullDebug(COMPONENT_NFS_V4, "CREATE CINFO before = %" PRIu64 " after = %" PRIu64 " atomic = %d", res_CREATE4->CREATE4res_u.resok4.cinfo.before, res_CREATE4->CREATE4res_u.resok4.cinfo.after, res_CREATE4->CREATE4res_u.resok4.cinfo.atomic); /* @todo : BUGAZOMEU: fair ele free dans cette fonction */ /* Keep the vnode entry for the file in the compound data */ set_current_entry(data, obj_new); /* If you reach this point, then no error occured */ res_CREATE4->status = NFS4_OK; out: if (obj_new) { /* Put our ref */ obj_new->obj_ops.put_ref(obj_new); } gsh_free(name); gsh_free(link_content); return res_CREATE4->status; } /* nfs4_op_create */ /** * @brief Free memory allocated for CREATE result * * This function frees any memory allocated for the result of the * NFS4_OP_CREATE operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_create_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_create_session.c000066400000000000000000000375631324272410200237270ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_create_session.c * @brief Routines used for managing the NFS4_OP_CREATE_SESSION operation. */ #include "config.h" #include #include #include "log.h" #include "nfs4.h" #include "nfs_core.h" #include "nfs_rpc_callback.h" #include "nfs_proto_functions.h" #include "sal_functions.h" #include "nfs_creds.h" #include "client_mgr.h" #include "fsal.h" /** * * @brief The NFS4_OP_CREATE_SESSION operation * * @param[in] op nfs4_op arguments * @param[in,out] data Compound request's data * @param[out] resp nfs4_op results * * @return Values as per RFC5661 p. 363 * * @see nfs4_Compound * */ int nfs4_op_create_session(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { /* Result of looking up the clientid in the confirmed ID table */ nfs_client_id_t *conf = NULL; /* XXX these are not good names */ /* Result of looking up the clientid in the unconfirmed ID table */ nfs_client_id_t *unconf = NULL; /* The found clientid (either one of the preceding) */ nfs_client_id_t *found = NULL; /* The found client record */ nfs_client_record_t *client_record; /* The created session */ nfs41_session_t *nfs41_session = NULL; /* Client supplied clientid */ clientid4 clientid = 0; /* The client address as a string, for gratuitous logging */ 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}; /* Return code from clientid calls */ int i, rc = 0; /* Component for logging */ log_components_t component = COMPONENT_CLIENTID; /* Abbreviated alias for arguments */ CREATE_SESSION4args * const arg_CREATE_SESSION4 = &op->nfs_argop4_u.opcreate_session; /* Abbreviated alias for response */ CREATE_SESSION4res * const res_CREATE_SESSION4 = &resp->nfs_resop4_u.opcreate_session; /* Abbreviated alias for successful response */ CREATE_SESSION4resok * const res_CREATE_SESSION4ok = &res_CREATE_SESSION4->CREATE_SESSION4res_u.csr_resok4; /* Make sure str_client is always printable even * if log level changes midstream. */ display_printf(&dspbuf_client, "(unknown)"); display_reset_buffer(&dspbuf_client); if (op_ctx->client != NULL) str_client_addr = op_ctx->client->hostaddr_str; if (isDebug(COMPONENT_SESSIONS)) component = COMPONENT_SESSIONS; resp->resop = NFS4_OP_CREATE_SESSION; res_CREATE_SESSION4->csr_status = NFS4_OK; clientid = arg_CREATE_SESSION4->csa_clientid; display_clientid(&dspbuf_clientid4, clientid); if (data->minorversion == 0) return res_CREATE_SESSION4->csr_status = NFS4ERR_INVAL; LogDebug(component, "CREATE_SESSION client addr=%s clientid=%s -------------------", str_client_addr, str_clientid4); /* 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; found = unconf; } else { rc = nfs_client_id_get_confirmed(clientid, &conf); if (rc != CLIENT_ID_SUCCESS) { /* No record whatsoever of this clientid */ LogDebug(component, "%s clientid=%s", clientid_error_to_str(rc), str_clientid4); if (rc == CLIENT_ID_EXPIRED) rc = CLIENT_ID_STALE; res_CREATE_SESSION4->csr_status = clientid_error_to_nfsstat_no_expire(rc); return res_CREATE_SESSION4->csr_status; } client_record = conf->cid_client_record; found = conf; } PTHREAD_MUTEX_lock(&client_record->cr_mutex); inc_client_record_ref(client_record); if (isFullDebug(component)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_record(&dspbuf, client_record); LogFullDebug(component, "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 conf and unconf is * non-NULL, and found also references the single clientid * record that was found. */ LogDebug(component, "CREATE_SESSION clientid=%s csa_sequence=%" PRIu32 " clientid_cs_seq=%" PRIu32 " data_oppos=%d data_use_drc=%d", str_clientid4, arg_CREATE_SESSION4->csa_sequence, found->cid_create_session_sequence, data->oppos, data->use_drc); if (isFullDebug(component)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, found); LogFullDebug(component, "Found %s", str); } data->use_drc = false; if ((arg_CREATE_SESSION4->csa_sequence + 1) == found->cid_create_session_sequence) { *res_CREATE_SESSION4 = found->cid_create_session_slot; LogDebug(component, "CREATE_SESSION replay=%p special case", data->cached_res); goto out; } else if (arg_CREATE_SESSION4->csa_sequence != found->cid_create_session_sequence) { res_CREATE_SESSION4->csr_status = NFS4ERR_SEQ_MISORDERED; LogDebug(component, "CREATE_SESSION returning NFS4ERR_SEQ_MISORDERED"); goto out; } if (unconf != NULL) { /* First must match principal */ if (!nfs_compare_clientcred(&unconf->cid_credential, &data->credential)) { if (isDebug(component)) { char *unconfirmed_addr = "(unknown)"; if (unconf->gsh_client != NULL) unconfirmed_addr = unconf->gsh_client->hostaddr_str; LogDebug(component, "Unconfirmed ClientId %s->'%s': Principals do not match... unconfirmed addr=%s Return NFS4ERR_CLID_INUSE", str_clientid4, str_client_addr, unconfirmed_addr); } res_CREATE_SESSION4->csr_status = NFS4ERR_CLID_INUSE; goto out; } } if (conf != NULL) { if (isDebug(component) && conf != NULL) display_clientid_name(&dspbuf_client, conf); /* First must match principal */ if (!nfs_compare_clientcred(&conf->cid_credential, &data->credential)) { if (isDebug(component)) { char *confirmed_addr = "(unknown)"; if (conf->gsh_client != NULL) confirmed_addr = conf->gsh_client->hostaddr_str; LogDebug(component, "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_CREATE_SESSION4->csr_status = NFS4ERR_CLID_INUSE; goto out; } /* In this case, the record was confirmed proceed with CREATE_SESSION */ } /* 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. */ /* At this point, we need to try and create the session before * we modify the confirmed and/or unconfirmed clientid * records. */ /* Check flags value (test CSESS15) */ if (arg_CREATE_SESSION4->csa_flags & ~(CREATE_SESSION4_FLAG_PERSIST | CREATE_SESSION4_FLAG_CONN_BACK_CHAN | CREATE_SESSION4_FLAG_CONN_RDMA)) { LogDebug(component, "Invalid create session flags %" PRIu32, arg_CREATE_SESSION4->csa_flags); res_CREATE_SESSION4->csr_status = NFS4ERR_INVAL; goto out; } /* Record session related information at the right place */ nfs41_session = pool_alloc(nfs41_session_pool); if (nfs41_session == NULL) { LogCrit(component, "Could not allocate memory for a session"); res_CREATE_SESSION4->csr_status = NFS4ERR_SERVERFAULT; goto out; } nfs41_session->clientid = clientid; nfs41_session->clientid_record = found; nfs41_session->refcount = 2; /* sentinel ref + call path ref */ nfs41_session->fore_channel_attrs = arg_CREATE_SESSION4->csa_fore_chan_attrs; nfs41_session->back_channel_attrs = arg_CREATE_SESSION4->csa_back_chan_attrs; nfs41_session->xprt = data->req->rq_xprt; nfs41_session->flags = false; nfs41_session->cb_program = 0; PTHREAD_MUTEX_init(&nfs41_session->cb_mutex, NULL); PTHREAD_COND_init(&nfs41_session->cb_cond, NULL); nfs41_session->nb_slots = MIN(nfs_param.nfsv4_param.nb_slots, nfs41_session->fore_channel_attrs.ca_maxrequests); nfs41_session->fc_slots = gsh_calloc(nfs41_session->nb_slots, sizeof(nfs41_session_slot_t)); nfs41_session->bc_slots = gsh_calloc(nfs41_session->nb_slots, sizeof(nfs41_cb_session_slot_t)); for (i = 0; i < nfs41_session->nb_slots; i++) PTHREAD_MUTEX_init(&nfs41_session->fc_slots[i].lock, NULL); /* Take reference to clientid record on behalf the session. */ inc_client_id_ref(found); /* add to head of session list (encapsulate?) */ PTHREAD_MUTEX_lock(&found->cid_mutex); glist_add(&found->cid_cb.v41.cb_session_list, &nfs41_session->session_link); PTHREAD_MUTEX_unlock(&found->cid_mutex); /* Set ca_maxrequests */ nfs41_session->fore_channel_attrs.ca_maxrequests = nfs41_session->nb_slots; nfs41_Build_sessionid(&clientid, nfs41_session->session_id); res_CREATE_SESSION4ok->csr_sequence = arg_CREATE_SESSION4->csa_sequence; /* return the input for wanting of something better (will * change in later versions) */ res_CREATE_SESSION4ok->csr_fore_chan_attrs = nfs41_session->fore_channel_attrs; res_CREATE_SESSION4ok->csr_back_chan_attrs = nfs41_session->back_channel_attrs; res_CREATE_SESSION4ok->csr_flags = 0; memcpy(res_CREATE_SESSION4ok->csr_sessionid, nfs41_session->session_id, NFS4_SESSIONID_SIZE); LogDebug(component, "CREATE_SESSION replay=%p", data->cached_res); if (!nfs41_Session_Set(nfs41_session)) { LogDebug(component, "Could not insert session into table"); /* Release the sentinel session resource (our reference will * be dropped on exit. */ dec_session_ref(nfs41_session); /* Maybe a more precise status would be better */ res_CREATE_SESSION4->csr_status = NFS4ERR_SERVERFAULT; goto out; } /* 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) && conf != NULL) display_clientid_name(&dspbuf_client, conf); /* Need a reference to the confirmed record for below */ if (conf != NULL) { /* This is the only point at which we have BOTH an * unconfirmed AND confirmed record. found MUST be * the unconfirmed record. */ inc_client_id_ref(conf); } } if (conf != NULL && conf->cid_clientid != clientid) { /* Old confirmed record - need to expire it */ if (isDebug(component)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, conf); LogDebug(component, "Expiring %s", str); } /* Expire clientid and release our reference. * NOTE: found MUST NOT BE conf (otherwise clientid would have * matched). */ 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. */ display_clientid(&dspbuf_clientid4, conf->cid_clientid); LogDebug(component, "Updating clientid %s->%s cb_program=%u", str_clientid4, str_client, arg_CREATE_SESSION4->csa_cb_program); if (unconf != NULL) { /* Deal with the ONLY situation where we have both a * confirmed and unconfirmed record by unhashing * the unconfirmed clientid record */ remove_unconfirmed_client_id(unconf); /* Release our reference to the unconfirmed entry */ dec_client_id_ref(unconf); /* And now to keep code simple, set found to the * confirmed record. */ found = conf; } if (isDebug(component)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, conf); LogDebug(component, "Updated %s", str); } } else { /* This is a new clientid */ if (isFullDebug(component)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, unconf); LogFullDebug(component, "Confirming new %s", str); } rc = nfs_client_id_confirm(unconf, component); if (rc != CLIENT_ID_SUCCESS) { res_CREATE_SESSION4->csr_status = clientid_error_to_nfsstat_no_expire(rc); /* Need to destroy the session */ if (!nfs41_Session_Del(nfs41_session->session_id)) LogDebug(component, "Oops nfs41_Session_Del failed"); goto out; } nfs4_chk_clid(unconf); conf = unconf; unconf = NULL; if (isDebug(component)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, conf); LogDebug(component, "Confirmed %s", str); } } conf->cid_create_session_sequence++; /* Bump the lease timer */ conf->cid_last_renew = time(NULL); if (isFullDebug(component)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_record(&dspbuf, client_record); LogFullDebug(component, "Client Record %s cr_confirmed_rec=%p cr_unconfirmed_rec=%p", str, client_record->cr_confirmed_rec, client_record->cr_unconfirmed_rec); } /* Handle the creation of the back channel, if the client requested one. */ if (arg_CREATE_SESSION4->csa_flags & CREATE_SESSION4_FLAG_CONN_BACK_CHAN) { nfs41_session->cb_program = arg_CREATE_SESSION4->csa_cb_program; if (nfs_rpc_create_chan_v41( nfs41_session, arg_CREATE_SESSION4->csa_sec_parms.csa_sec_parms_len, arg_CREATE_SESSION4->csa_sec_parms.csa_sec_parms_val) == 0) { res_CREATE_SESSION4ok->csr_flags |= CREATE_SESSION4_FLAG_CONN_BACK_CHAN; } } if (isDebug(component)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_session(&dspbuf, nfs41_session); LogDebug(component, "success %s csa_flags 0x%X csr_flags 0x%X", str, arg_CREATE_SESSION4->csa_flags, res_CREATE_SESSION4ok->csr_flags); } /* Successful exit */ res_CREATE_SESSION4->csr_status = NFS4_OK; /* Cache response */ /** @todo: Warning, if we ever have ca_rdma_ird_len == 1, we need to be * more careful since there would be allocated memory attached * to the response. */ conf->cid_create_session_slot = *res_CREATE_SESSION4; out: /* Release our reference to the found record (confirmed or unconfirmed) */ dec_client_id_ref(found); if (nfs41_session != NULL) { /* Release our reference to the session */ dec_session_ref(nfs41_session); } PTHREAD_MUTEX_unlock(&client_record->cr_mutex); /* Release our reference to the client record and return */ dec_client_record_ref(client_record); return res_CREATE_SESSION4->csr_status; } /** * @brief free what was allocated to handle nfs41_op_create_session * * @param[in,out] resp nfs4_op results * */ void nfs4_op_create_session_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_delegpurge.c000066400000000000000000000050131324272410200230250ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_delegpurge.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. */ #include "config.h" #include #include #include #include #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "nfs4.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" /** * @brief NFS4_OP_DELEGPURGE * * This function implements the NFS4_OP_DELEGPURGE operation. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC 5661, pp. 363-4 * */ int nfs4_op_delegpurge(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { /* Unused for now, but when we actually implement this function it won't be, so remove the attribute. */ DELEGPURGE4args * const arg_DELEGPURGE4 __attribute__ ((unused)) = &op->nfs_argop4_u.opdelegpurge; DELEGPURGE4res * const res_DELEGPURGE4 = &resp->nfs_resop4_u.opdelegpurge; /* Lock are not supported */ resp->resop = NFS4_OP_DELEGPURGE; res_DELEGPURGE4->status = NFS4ERR_NOTSUPP; return res_DELEGPURGE4->status; } /* nfs4_op_delegpurge */ /** * @brief Free memory allocated for DELEGPURGE result * * This function frees any memory allocated for the result of the * NFS4_OP_DELEGPURGE operation. * * @param[in,out] resp nfs4_op results * */ void nfs4_op_delegpurge_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_delegreturn.c000066400000000000000000000103651324272410200232300ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_delegreturn.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. * * */ #include "config.h" #include #include #include #include #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "nfs4.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_file_handle.h" #include "nfs_proto_functions.h" #include "sal_functions.h" /** * @brief NFS4_OP_DELEGRETURN * * This function implements the NFS4_OP_DELEGRETURN operation. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC 5661, p. 364 */ int nfs4_op_delegreturn(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { DELEGRETURN4args * const arg_DELEGRETURN4 = &op->nfs_argop4_u.opdelegreturn; DELEGRETURN4res * const res_DELEGRETURN4 = &resp->nfs_resop4_u.opdelegreturn; state_status_t state_status; state_t *state_found; const char *tag = "DELEGRETURN"; state_owner_t *owner; LogDebug(COMPONENT_NFS_V4_LOCK, "Entering NFS v4 DELEGRETURN handler -----------------------------------------------------"); /* Initialize to sane default */ resp->resop = NFS4_OP_DELEGRETURN; /* If the filehandle is invalid. Delegations are only supported on * regular files at the moment. */ res_DELEGRETURN4->status = nfs4_sanity_check_FH(data, REGULAR_FILE, false); if (res_DELEGRETURN4->status != NFS4_OK) { if (res_DELEGRETURN4->status == NFS4ERR_ISDIR) res_DELEGRETURN4->status = NFS4ERR_INVAL; return res_DELEGRETURN4->status; } /* Check stateid correctness and get pointer to state */ res_DELEGRETURN4->status = nfs4_Check_Stateid(&arg_DELEGRETURN4->deleg_stateid, data->current_obj, &state_found, data, STATEID_SPECIAL_FOR_LOCK, 0, false, tag); if (res_DELEGRETURN4->status != NFS4_OK) return res_DELEGRETURN4->status; owner = get_state_owner_ref(state_found); if (owner == NULL) { /* Something has gone stale. */ LogDebug(COMPONENT_NFS_V4_LOCK, "Stale state"); res_DELEGRETURN4->status = NFS4ERR_STALE; goto out_unlock; } deleg_heuristics_recall(data->current_obj, owner, state_found); /* Release reference taken above. */ dec_state_owner_ref(owner); PTHREAD_RWLOCK_wrlock(&data->current_obj->state_hdl->state_lock); /* Now we have a lock owner and a stateid. * Go ahead and push unlock into SAL (and FSAL) to return * the delegation. */ state_status = release_lease_lock(data->current_obj, state_found); res_DELEGRETURN4->status = nfs4_Errno_state(state_status); if (state_status == STATE_SUCCESS) { /* Successful exit */ LogDebug(COMPONENT_NFS_V4_LOCK, "Successful exit"); state_del_locked(state_found); } PTHREAD_RWLOCK_unlock(&data->current_obj->state_hdl->state_lock); out_unlock: dec_state_t_ref(state_found); return res_DELEGRETURN4->status; } /* nfs4_op_delegreturn */ /** * @brief Free memory allocated for DELEGRETURN result * * This function frees any memory allocated for the result of the * DELEGRETURN operation. * * @param resp [INOUT] Pointer to nfs4_op results */ void nfs4_op_delegreturn_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_destroy_clientid.c000066400000000000000000000134551324272410200242570ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_destroy_clientid.c * @brief Provides NFS4_OP_DESTROY_CLIENTID implementation */ #include "config.h" #include #include "log.h" #include "nfs4.h" #include "sal_functions.h" #include "nfs_proto_functions.h" #include "nfs_core.h" /** * * @brief The NFS4_OP_DESTROY_CLIENTID operation. * * @param[in] op nfs4_op arguments * @param[in,out] data Compound request data * @param[out] resp nfs4_op results * * @retval NFS4_OK or errors for NFSv4.1. * @retval NFS4ERR_NOTSUPP for NFSv4.0. * */ int nfs4_op_destroy_clientid(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { DESTROY_CLIENTID4args * const arg_DESTROY_CLIENTID4 = &op->nfs_argop4_u.opdestroy_clientid; DESTROY_CLIENTID4res * const res_DESTROY_CLIENTID4 = &resp->nfs_resop4_u.opdestroy_clientid; nfs_client_record_t *client_record = NULL; nfs_client_id_t *conf = NULL, *unconf = NULL, *found = NULL; clientid4 clientid; int rc; resp->resop = NFS4_OP_DESTROY_CLIENTID; clientid = arg_DESTROY_CLIENTID4->dca_clientid; if (isDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_clientid(&dspbuf, clientid); LogDebug(COMPONENT_CLIENTID, "DESTROY_CLIENTID clientid=%s", str); } res_DESTROY_CLIENTID4->dcr_status = NFS4_OK; /* First try to look up confirmed record */ rc = nfs_client_id_get_confirmed(clientid, &conf); if (rc == CLIENT_ID_SUCCESS) { client_record = conf->cid_client_record; found = conf; } else { /* fall back to unconfirmed */ rc = nfs_client_id_get_unconfirmed(clientid, &unconf); if (rc == CLIENT_ID_SUCCESS) { client_record = unconf->cid_client_record; found = unconf; } /* handle the perverse case of a clientid being confirmed * in the above interval */ rc = nfs_client_id_get_confirmed(clientid, &conf); if (rc == CLIENT_ID_SUCCESS) { if (found != NULL) dec_client_id_ref(found); client_record = conf->cid_client_record; found = conf; } } /* ref +1 */ if (client_record == NULL) { /* Fine. We're done. */ res_DESTROY_CLIENTID4->dcr_status = NFS4ERR_STALE_CLIENTID; goto out; } (void) inc_client_record_ref(client_record); PTHREAD_MUTEX_lock(&client_record->cr_mutex); 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); } /* per Frank, we must check the confirmed and unconfirmed * state of client_record again now that we hold cr_mutex */ conf = client_record->cr_confirmed_rec; unconf = client_record->cr_unconfirmed_rec; if ((!conf) && (!unconf)) { /* We raced a thread destroying clientid, and lost. * We're done. */ goto cleanup; } if (conf) { /* We MUST NOT destroy a clientid that has nfsv41 sessions or * state. Since the minorversion is 4.1 or higher, this is * equivalent to a session check. */ PTHREAD_MUTEX_lock(&conf->cid_mutex); if (!glist_empty(&conf->cid_cb.v41.cb_session_list)) { res_DESTROY_CLIENTID4->dcr_status = NFS4ERR_CLIENTID_BUSY; PTHREAD_MUTEX_unlock(&conf->cid_mutex); goto cleanup; } PTHREAD_MUTEX_unlock(&conf->cid_mutex); /* Delete the confirmed clientid record. Because we * have the cr_mutex, we have won any race to deal * with this clientid 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); LogDebug(COMPONENT_CLIENTID, "Removing confirmed clientid %s", str); } /* remove stable-storage record (if any) */ nfs4_rm_clid(conf); /* unhash the clientid record */ (void)remove_confirmed_client_id(conf); } if (unconf) { /* Delete the unconfirmed clientid record. Because we * have the cr_mutex, we have won any race to deal * with this clientid 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); LogDebug(COMPONENT_CLIENTID, "Removing unconfirmed clientid %s", str); } /* unhash the clientid record */ (void)remove_unconfirmed_client_id(unconf); } cleanup: PTHREAD_MUTEX_unlock(&client_record->cr_mutex); dec_client_record_ref(client_record); /* ref +0 */ if (found != NULL) dec_client_id_ref(found); out: return res_DESTROY_CLIENTID4->dcr_status; } /** * @brief Free DESTROY_CLIENTID result * * @param[in,out] resp nfs4_op results */ void nfs4_op_destroy_clientid_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_destroy_session.c000066400000000000000000000067171324272410200241520ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_destroy_session.c * @brief Routines used for managing the NFS4_OP_DESTROY_SESSION operation. * * Routines used for managing the NFS4_OP_DESTROY_SESSION operation. * * */ #include "config.h" #include "sal_functions.h" /** * * @brief The NFS4_OP_DESTROY_SESSION operation * * This function implements the NFS4_OP_DESTROY_SESSION operation. * * @param[in] op nfs4_op arguments * @param[in,out] data Compound request's data * @param[out] resp nfs4_op results * * @return values as per RFC5661 p. 364 * * @see nfs4_Compound * */ int nfs4_op_destroy_session(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { DESTROY_SESSION4args * const arg_DESTROY_SESSION4 = &op->nfs_argop4_u.opdestroy_session; DESTROY_SESSION4res * const res_DESTROY_SESSION4 = &resp->nfs_resop4_u.opdestroy_session; sockaddr_t nw_addr; sockaddr_t client_addr; nfs41_session_t *session; resp->resop = NFS4_OP_DESTROY_SESSION; res_DESTROY_SESSION4->dsr_status = NFS4_OK; if (data->minorversion == 0) { res_DESTROY_SESSION4->dsr_status = NFS4ERR_INVAL; return res_DESTROY_SESSION4->dsr_status = NFS4ERR_INVAL; } if (!nfs41_Session_Get_Pointer(arg_DESTROY_SESSION4->dsa_sessionid, &session)) { res_DESTROY_SESSION4->dsr_status = NFS4ERR_BADSESSION; return res_DESTROY_SESSION4->dsr_status; } /* DESTROY_SESSION MUST be invoked on a connection that is associated * with the session being destroyed */ /* Copy the address coming over the wire. */ copy_xprt_addr(&nw_addr, data->req->rq_xprt); /* Copy the address recorded in the session. */ copy_xprt_addr(&client_addr, session->xprt); /* Compare these fields. */ if (!cmp_sockaddr(&nw_addr, &client_addr, false)) { res_DESTROY_SESSION4->dsr_status = NFS4ERR_CONN_NOT_BOUND_TO_SESSION; dec_session_ref(session); return res_DESTROY_SESSION4->dsr_status; } if (!nfs41_Session_Del(arg_DESTROY_SESSION4->dsa_sessionid)) res_DESTROY_SESSION4->dsr_status = NFS4ERR_BADSESSION; else res_DESTROY_SESSION4->dsr_status = NFS4_OK; /* Release ref taken in get_pointer */ dec_session_ref(session); return res_DESTROY_SESSION4->dsr_status; } /* nfs41_op_destroy_session */ /** * @brief Free memory allocated for result of nfs41_op_destroy_session * * This function frees memory allocated for result of * nfs41_op_destroy_session * * @param[in,out] resp nfs4_op results * */ void nfs4_op_destroy_session_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_exchange_id.c000066400000000000000000000264351324272410200231530ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_exchange_id.c * @brief The NFS4_OP_EXCHANGE_ID operation */ #include "config.h" #include #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_proto_functions.h" #include "sal_functions.h" #include "nfs_creds.h" int get_raddr(SVCXPRT *xprt) { struct sockaddr_storage *ss = svc_getrpclocal(xprt); int addr = 0; if (ss == NULL) return addr; switch (ss->ss_family) { case AF_INET6: { void *ab = &(((struct sockaddr_in6 *)ss)-> sin6_addr.s6_addr[12]); addr = ntohl(*(uint32_t *) ab); } break; case AF_INET: addr = ntohl(((struct sockaddr_in *)ss)->sin_addr.s_addr); break; default: break; } return addr; } /** * @brief The NFS4_OP_EXCHANGE_ID operation * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 364 * * @see nfs4_Compound * */ int nfs4_op_exchange_id(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { nfs_client_record_t *client_record; nfs_client_id_t *conf; nfs_client_id_t *unconf; int rc; int owner_len, scope_len; char *temp; bool update; uint32_t pnfs_flags; in_addr_t server_addr = 0; char cid_server_owner[MAXNAMLEN+1]; /* max hostname length */ const char cid_server_scope_suffix[] = "_NFS-Ganesha"; /* Arguments and response */ EXCHANGE_ID4args * const arg_EXCHANGE_ID4 = &op->nfs_argop4_u.opexchange_id; EXCHANGE_ID4res * const res_EXCHANGE_ID4 = &resp->nfs_resop4_u.opexchange_id; EXCHANGE_ID4resok * const res_EXCHANGE_ID4_ok = &resp->nfs_resop4_u.opexchange_id.EXCHANGE_ID4res_u.eir_resok4; resp->resop = NFS4_OP_EXCHANGE_ID; if (data->minorversion == 0) return res_EXCHANGE_ID4->eir_status = NFS4ERR_INVAL; if ((arg_EXCHANGE_ID4->eia_flags & ~(EXCHGID4_FLAG_SUPP_MOVED_REFER | EXCHGID4_FLAG_SUPP_MOVED_MIGR | EXCHGID4_FLAG_BIND_PRINC_STATEID | EXCHGID4_FLAG_USE_NON_PNFS | EXCHGID4_FLAG_USE_PNFS_MDS | EXCHGID4_FLAG_USE_PNFS_DS | EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) ) != 0) return res_EXCHANGE_ID4->eir_status = NFS4ERR_INVAL; /* If client did not ask for pNFS related server roles than just set server roles */ pnfs_flags = arg_EXCHANGE_ID4->eia_flags & EXCHGID4_FLAG_MASK_PNFS; if (pnfs_flags == 0) { if (nfs_param.nfsv4_param.pnfs_mds) pnfs_flags |= EXCHGID4_FLAG_USE_PNFS_MDS; if (nfs_param.nfsv4_param.pnfs_ds) pnfs_flags |= EXCHGID4_FLAG_USE_PNFS_DS; if (pnfs_flags == 0) pnfs_flags |= EXCHGID4_FLAG_USE_NON_PNFS; } /* If client did ask for pNFS related server roles than try to match the server roles to the client request. */ else { if ((arg_EXCHANGE_ID4->eia_flags & EXCHGID4_FLAG_USE_PNFS_MDS) && (nfs_param.nfsv4_param.pnfs_mds)) pnfs_flags |= EXCHGID4_FLAG_USE_PNFS_MDS; if ((arg_EXCHANGE_ID4->eia_flags & EXCHGID4_FLAG_USE_PNFS_DS) && (nfs_param.nfsv4_param.pnfs_ds)) pnfs_flags |= EXCHGID4_FLAG_USE_PNFS_DS; if (pnfs_flags == 0) pnfs_flags |= EXCHGID4_FLAG_USE_NON_PNFS; } LogDebug(COMPONENT_CLIENTID, "EXCHANGE_ID pnfs_flags 0x%08x eia_flags 0x%08x", pnfs_flags, arg_EXCHANGE_ID4->eia_flags); update = (arg_EXCHANGE_ID4->eia_flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A) != 0; server_addr = get_raddr(data->req->rq_xprt); /* Do we already have one or more records for client id (x)? */ client_record = get_client_record( arg_EXCHANGE_ID4->eia_clientowner.co_ownerid.co_ownerid_val, arg_EXCHANGE_ID4->eia_clientowner.co_ownerid. co_ownerid_len, pnfs_flags, server_addr); if (client_record == NULL) { /* Some major failure */ LogCrit(COMPONENT_CLIENTID, "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 */ PTHREAD_MUTEX_lock(&client_record->cr_mutex); conf = client_record->cr_confirmed_rec; if (conf != NULL) { /* Need a reference to the confirmed record for below */ inc_client_id_ref(conf); } if (conf != NULL && !update) { /* EXCHGID4_FLAG_UPD_CONFIRMED_REC_A not set * * Compare the client credentials, but don't compare * the client address. Doing so interferes with * trunking and the ability of a client to reconnect * after being assigned a new address. */ if (!nfs_compare_clientcred(&conf->cid_credential, &data->credential)) { PTHREAD_MUTEX_lock(&conf->cid_mutex); if (!valid_lease(conf) || !client_id_has_state(conf)) { PTHREAD_MUTEX_unlock(&conf->cid_mutex); /* CASE 3, client collisions, old * clientid is expired * * Expire clientid and release our reference. */ nfs_client_id_expire(conf, false); dec_client_id_ref(conf); conf = NULL; } else { PTHREAD_MUTEX_unlock(&conf->cid_mutex); /* CASE 3, client collisions, old * clientid is not expired */ res_EXCHANGE_ID4->eir_status = NFS4ERR_CLID_INUSE; /* Release our reference to the * confirmed clientid. */ dec_client_id_ref(conf); goto out; } } else if (memcmp(arg_EXCHANGE_ID4->eia_clientowner.co_verifier, conf->cid_incoming_verifier, NFS4_VERIFIER_SIZE) == 0) { /* CASE 2, Non-Update on Existing Client ID * * Return what was last returned without * changing any refcounts */ unconf = conf; res_EXCHANGE_ID4_ok->eir_flags |= EXCHGID4_FLAG_CONFIRMED_R; goto return_ok; } else { /* CASE 5, client restart */ /** @todo FSF: expire old clientid? */ } } else if (conf != NULL) { /* EXCHGID4_FLAG_UPD_CONFIRMED_REC_A set */ if (memcmp(arg_EXCHANGE_ID4->eia_clientowner.co_verifier, conf->cid_incoming_verifier, NFS4_VERIFIER_SIZE) == 0) { if (!nfs_compare_clientcred(&conf->cid_credential, &data->credential) || op_ctx->client == NULL || conf->gsh_client == NULL || op_ctx->client != conf->gsh_client) { /* CASE 9, Update but wrong principal */ res_EXCHANGE_ID4->eir_status = NFS4ERR_PERM; } else { /* CASE 6, Update */ /** @todo: we don't track or handle the things * that are updated, but we can still * allow the update. */ LogDebug(COMPONENT_CLIENTID, "EXCHANGE_ID Update ignored"); unconf = conf; res_EXCHANGE_ID4_ok->eir_flags |= EXCHGID4_FLAG_CONFIRMED_R; goto return_ok; } } else { /* CASE 8, Update but wrong verifier */ res_EXCHANGE_ID4->eir_status = NFS4ERR_NOT_SAME; } /* Release our reference to the confirmed clientid. */ dec_client_id_ref(conf); goto out; } else if (conf == NULL && update) { /* CASE 7, Update but No Confirmed Record */ 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. */ unconf = client_record->cr_unconfirmed_rec; if (unconf != NULL) { /* CASE 4, replacement of unconfirmed record * * Delete the unconfirmed clientid record * unhash the clientid record */ remove_unconfirmed_client_id(unconf); } /* Now we can proceed to build the new unconfirmed record. We * have determined the clientid and setclientid_confirm values * above. */ unconf = create_client_id(0, client_record, &data->credential, data->minorversion); if (unconf == NULL) { /* Error already logged, return */ res_EXCHANGE_ID4->eir_status = NFS4ERR_RESOURCE; goto out; } unconf->cid_create_session_sequence = 1; unconf->cid_create_session_slot.csr_status = NFS4ERR_SEQ_MISORDERED; glist_init(&unconf->cid_cb.v41.cb_session_list); memcpy(unconf->cid_incoming_verifier, arg_EXCHANGE_ID4->eia_clientowner.co_verifier, NFS4_VERIFIER_SIZE); if (gethostname(cid_server_owner, sizeof(cid_server_owner)) == -1) { /* Free the clientid record and return */ free_client_id(unconf); res_EXCHANGE_ID4->eir_status = NFS4ERR_SERVERFAULT; goto out; } LogDebug(COMPONENT_CLIENTID, "Serving IP %s", cid_server_owner); rc = nfs_client_id_insert(unconf); if (rc != CLIENT_ID_SUCCESS) { /* Record is already freed, return. */ res_EXCHANGE_ID4->eir_status = clientid_error_to_nfsstat_no_expire(rc); goto out; } return_ok: /* Build the reply */ res_EXCHANGE_ID4_ok->eir_clientid = unconf->cid_clientid; res_EXCHANGE_ID4_ok->eir_sequenceid = unconf->cid_create_session_sequence; res_EXCHANGE_ID4_ok->eir_flags |= client_record->cr_pnfs_flags; res_EXCHANGE_ID4_ok->eir_flags |= EXCHGID4_FLAG_SUPP_MOVED_REFER; res_EXCHANGE_ID4_ok->eir_state_protect.spr_how = SP4_NONE; owner_len = strlen(cid_server_owner); temp = gsh_malloc(owner_len + 1); memcpy(temp, cid_server_owner, owner_len + 1); res_EXCHANGE_ID4_ok->eir_server_owner.so_major_id.so_major_id_len = owner_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; scope_len = strlen(cid_server_scope_suffix); temp = gsh_malloc(owner_len + scope_len + 1); memcpy(temp, cid_server_owner, owner_len); memcpy(temp + owner_len, cid_server_scope_suffix, scope_len + 1); res_EXCHANGE_ID4_ok->eir_server_scope.eir_server_scope_len = owner_len + scope_len + 1; 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; res_EXCHANGE_ID4->eir_status = NFS4_OK; if (unconf == conf) { /* We just updated a confirmed clientid, release the refcount * now. */ dec_client_id_ref(conf); } out: PTHREAD_MUTEX_unlock(&client_record->cr_mutex); /* Release our reference to the client record */ dec_client_record_ref(client_record); return res_EXCHANGE_ID4->eir_status; } /** * @brief free memory alocated for nfs4_op_exchange_id result * * @param[in,out] resp Pointer to nfs4_op results */ void nfs4_op_exchange_id_Free(nfs_resop4 *res) { EXCHANGE_ID4res *resp = &res->nfs_resop4_u.opexchange_id; EXCHANGE_ID4resok *resok = &resp->EXCHANGE_ID4res_u.eir_resok4; if (resp->eir_status == NFS4_OK) { gsh_free(resok->eir_server_scope.eir_server_scope_val); gsh_free(resok->eir_server_owner.so_major_id.so_major_id_val); gsh_free(resok->eir_server_impl_id.eir_server_impl_id_val); } } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_free_stateid.c000066400000000000000000000101511324272410200233370ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_free_stateid.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. * * */ #include "config.h" #include #include #include #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "nfs4.h" #include "nfs_core.h" #include "sal_functions.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "gsh_list.h" /** * * @brief The NFS4_OP_FREE_STATEID operation. * * This function implements the NFS4_OP_FREE_STATEID operation in * nfs4_Compound. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661 pp. 364-5 * * @see nfs4_Compound */ int nfs4_op_free_stateid(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { FREE_STATEID4args * const arg_FREE_STATEID4 __attribute__ ((unused)) = &op->nfs_argop4_u.opfree_stateid; FREE_STATEID4res * const res_FREE_STATEID4 = &resp->nfs_resop4_u.opfree_stateid; state_t *state; struct gsh_export *save_exp; struct gsh_export *export; struct fsal_obj_handle *obj; resp->resop = NFS4_OP_FREE_STATEID; res_FREE_STATEID4->fsr_status = NFS4_OK; if (data->minorversion == 0) return res_FREE_STATEID4->fsr_status = NFS4ERR_INVAL; res_FREE_STATEID4->fsr_status = nfs4_Check_Stateid(&arg_FREE_STATEID4->fsa_stateid, NULL, &state, data, STATEID_SPECIAL_CURRENT, 0, false, "FREE_STATEID"); if (res_FREE_STATEID4->fsr_status != NFS4_OK) return res_FREE_STATEID4->fsr_status; if (!get_state_obj_export_owner_refs(state, &obj, &export, NULL)) { /* If this happens, something is going stale, just return * NFS4ERR_BAD_STATEID, whatever is going stale will become * more apparent to the client soon... */ res_FREE_STATEID4->fsr_status = NFS4ERR_BAD_STATEID; return res_FREE_STATEID4->fsr_status; } save_exp = op_ctx->ctx_export; op_ctx->ctx_export = export; op_ctx->fsal_export = op_ctx->ctx_export->fsal_export; PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); if (state->state_type == STATE_TYPE_LOCK && glist_empty(&state->state_data.lock.state_locklist)) { /* At the moment, only return success for a lock stateid with * no locks. */ /** @todo: Do we also have to handle other kinds of stateids? */ res_FREE_STATEID4->fsr_status = NFS4_OK; state_del_locked(state); } else { res_FREE_STATEID4->fsr_status = NFS4ERR_LOCKS_HELD; } PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); dec_state_t_ref(state); op_ctx->ctx_export = save_exp; op_ctx->fsal_export = save_exp != NULL ? save_exp->fsal_export : NULL; obj->obj_ops.put_ref(obj); put_gsh_export(export); return res_FREE_STATEID4->fsr_status; } /* nfs41_op_free_stateid */ /** * @brief free memory allocated for FREE_STATEID result * * This function frees memory allocated for the NFS4_OP_FREE_STATEID * result. * * @param[in,out] resp nfs4_op results * */ void nfs4_op_free_stateid_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_getattr.c000066400000000000000000000116761324272410200223700ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_getattr.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. */ #include "config.h" #include #include #include #include "log.h" #include "gsh_rpc.h" #include "nfs4.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_file_handle.h" static inline bool check_fs_locations(struct fsal_obj_handle *obj) { fsal_status_t st; fs_locations4 fs_locs; fs_location4 fs_loc; component4 fs_server; char server[MAXHOSTNAMELEN]; nfs4_pathname4_alloc(&fs_locs.fs_root, NULL); fs_server.utf8string_len = sizeof(server); fs_server.utf8string_val = server; fs_loc.server.server_len = 1; fs_loc.server.server_val = &fs_server; nfs4_pathname4_alloc(&fs_loc.rootpath, NULL); fs_locs.locations.locations_len = 1; fs_locs.locations.locations_val = &fs_loc; /* For now allow for one fs locations, fs_locations() should set: root and update its length, can not be bigger than MAXPATHLEN path and update its length, can not be bigger than MAXPATHLEN server and update its length, can not be bigger than MAXHOSTNAMELEN */ st = obj->obj_ops.fs_locations(obj, &fs_locs); nfs4_pathname4_free(&fs_locs.fs_root); nfs4_pathname4_free(&fs_loc.rootpath); return !FSAL_IS_ERROR(st); } /** * @brief Gets attributes for an entry in the FSAL. * * Impelments the NFS4_OP_GETATTR operation, which gets attributes for * an entry in the FSAL. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 365 * */ int nfs4_op_getattr(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { GETATTR4args * const arg_GETATTR4 = &op->nfs_argop4_u.opgetattr; GETATTR4res * const res_GETATTR4 = &resp->nfs_resop4_u.opgetattr; attrmask_t mask; struct attrlist attrs; /* This is a NFS4_OP_GETTAR */ resp->resop = NFS4_OP_GETATTR; res_GETATTR4->status = NFS4_OK; /* Do basic checks on a filehandle */ res_GETATTR4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false); if (res_GETATTR4->status != NFS4_OK) return res_GETATTR4->status; /* Sanity check: if no attributes are wanted, nothing is to be * done. In this case NFS4_OK is to be returned */ if (arg_GETATTR4->attr_request.bitmap4_len == 0) { res_GETATTR4->status = NFS4_OK; return res_GETATTR4->status; } /* Get only attributes that are allowed to be read */ if (!nfs4_Fattr_Check_Access_Bitmap(&arg_GETATTR4->attr_request, FATTR4_ATTR_READ)) { res_GETATTR4->status = NFS4ERR_INVAL; return res_GETATTR4->status; } res_GETATTR4->status = bitmap4_to_attrmask_t(&arg_GETATTR4->attr_request, &mask); if (res_GETATTR4->status != NFS4_OK) return res_GETATTR4->status; /* Add mode to what we actually ask for so we can do fslocations * test. */ fsal_prepare_attrs(&attrs, mask | ATTR_MODE); nfs4_bitmap4_Remove_Unsupported(&arg_GETATTR4->attr_request); res_GETATTR4->status = file_To_Fattr( data, mask, &attrs, &res_GETATTR4->GETATTR4res_u.resok4.obj_attributes, &arg_GETATTR4->attr_request); if (data->current_obj->type == DIRECTORY && is_sticky_bit_set(data->current_obj, &attrs) && !attribute_is_set(&arg_GETATTR4->attr_request, FATTR4_FS_LOCATIONS) && check_fs_locations(data->current_obj)) res_GETATTR4->status = NFS4ERR_MOVED; /* Done with the attrs */ fsal_release_attrs(&attrs); return res_GETATTR4->status; } /* nfs4_op_getattr */ /** * @brief Free memory allocated for GETATTR result * * This function frees any memory allocated for the result of the * NFS4_OP_GETATTR operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_getattr_Free(nfs_resop4 *res) { GETATTR4res *resp = &res->nfs_resop4_u.opgetattr; if (resp->status == NFS4_OK) nfs4_Fattr_Free(&resp->GETATTR4res_u.resok4.obj_attributes); } /* nfs4_op_getattr_Free */ nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_getdeviceinfo.c000066400000000000000000000132221324272410200235160ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_getdeviceinfo.c * @brief Routines used for managing the NFS4_OP_GETDEVICEINFO operation. * * Routines used for managing the GETDEVICEINFO operation. */ #include "config.h" #include #include #include #include #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_tools.h" #include "nfs_proto_functions.h" #include "nfs_file_handle.h" #include "nfs_convert.h" #include "fsal_pnfs.h" #include "nfs_proto_functions.h" #include "nfs_file_handle.h" #include "export_mgr.h" /** * * @brief The NFS4_OP_GETDEVICEINFO operation. * * This function returns information on a pNFS device. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661 p. 365-6 * * @see nfs4_Compound * */ int nfs4_op_getdeviceinfo(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { /* Convenience alias for arguments */ GETDEVICEINFO4args * const arg_GETDEVICEINFO4 = &op->nfs_argop4_u.opgetdeviceinfo; /* Convenience alias for response */ GETDEVICEINFO4res * const res_GETDEVICEINFO4 = &resp->nfs_resop4_u.opgetdeviceinfo; /* Convenience alias for response */ GETDEVICEINFO4resok *resok = &res_GETDEVICEINFO4->GETDEVICEINFO4res_u.gdir_resok4; /* The separated deviceid passed to the FSAL */ struct pnfs_deviceid *deviceid; /* NFS4 return code */ nfsstat4 nfs_status = 0; /* XDR stream into which the FSAl shall encode the da_addr_body */ XDR da_addr_body; /* The position before any bytes are sent to the stream */ size_t da_beginning = 0; /* The total length of the XDR-encoded da_addr_body */ size_t da_length = 0; /* Address of the buffer that backs the stream */ char *da_buffer = NULL; /* The space necessary to hold one response */ count4 mincount = 0; /* The FSAL's requested size for the da_addr_body opaque */ size_t da_addr_size = 0; /* Pointer to the fsal appropriate to this deviceid */ struct fsal_module *fsal = NULL; resp->resop = NFS4_OP_GETDEVICEINFO; if (data->minorversion == 0) return res_GETDEVICEINFO4->gdir_status = NFS4ERR_INVAL; /* Overlay Ganesha's pnfs_deviceid on arg */ deviceid = (struct pnfs_deviceid *) arg_GETDEVICEINFO4->gdia_device_id; if (deviceid->fsal_id >= FSAL_ID_COUNT) { LogInfo(COMPONENT_PNFS, "GETDEVICEINFO with invalid fsal id %0hhx", deviceid->fsal_id); return res_GETDEVICEINFO4->gdir_status = NFS4ERR_INVAL; } fsal = pnfs_fsal[deviceid->fsal_id]; if (fsal == NULL) { LogInfo(COMPONENT_PNFS, "GETDEVICEINFO with inactive fsal id %0hhx", deviceid->fsal_id); return res_GETDEVICEINFO4->gdir_status = NFS4ERR_INVAL; } /* Check that we have space */ mincount = sizeof(uint32_t) + /* Count for the empty bitmap */ sizeof(layouttype4) + /* Type in the device_addr4 */ sizeof(uint32_t); /* Number of bytes in da_addr_body */ da_addr_size = MIN(fsal->m_ops.fs_da_addr_size(fsal), arg_GETDEVICEINFO4->gdia_maxcount - mincount); if (da_addr_size == 0) { LogCrit(COMPONENT_PNFS, "The FSAL must specify a non-zero da_addr size."); nfs_status = NFS4ERR_NOENT; goto out; } /* Set up the device_addr4 and get stream for FSAL to write into */ resok->gdir_device_addr.da_layout_type = arg_GETDEVICEINFO4->gdia_layout_type; da_buffer = gsh_malloc(da_addr_size); xdrmem_create(&da_addr_body, da_buffer, da_addr_size, XDR_ENCODE); da_beginning = xdr_getpos(&da_addr_body); nfs_status = fsal->m_ops.getdeviceinfo( fsal, &da_addr_body, arg_GETDEVICEINFO4->gdia_layout_type, deviceid); da_length = xdr_getpos(&da_addr_body) - da_beginning; xdr_destroy(&da_addr_body); if (nfs_status != NFS4_OK) goto out; memset(&resok->gdir_notification, 0, sizeof(resok->gdir_notification)); resok->gdir_device_addr.da_addr_body.da_addr_body_len = da_length; resok->gdir_device_addr.da_addr_body.da_addr_body_val = da_buffer; nfs_status = NFS4_OK; out: if ((nfs_status != NFS4_OK) && da_buffer) gsh_free(da_buffer); res_GETDEVICEINFO4->gdir_status = nfs_status; return res_GETDEVICEINFO4->gdir_status; } /* nfs41_op_getdeviceinfo */ /** * @brief Free memory allocated for GETDEVICEINFO result * * This function frees memory allocated for the result of an * NFS4_OP_GETDEVICEINFO response. * * @param[in,out] resp Results for nfs4_op * */ void nfs4_op_getdeviceinfo_Free(nfs_resop4 *res) { GETDEVICEINFO4res *resp = &res->nfs_resop4_u.opgetdeviceinfo; GETDEVICEINFO4resok *resok = &resp->GETDEVICEINFO4res_u.gdir_resok4; if (resp->gdir_status == NFS4_OK) { gsh_free(resok->gdir_device_addr.da_addr_body.da_addr_body_val); } } /* nfs41_op_getdeviceinfo_Free */ nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_getdevicelist.c000066400000000000000000000113501324272410200235360ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_getdevicelist.c * @brief Routines used for managing the NFS4_OP_GETDEVICELIST operation. * * Routines used for managing the GETDEVICELIST operation. * * */ #include "config.h" #include #include #include #include #include #include "hashtable.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_tools.h" #include "nfs_proto_functions.h" #include "nfs_file_handle.h" #include "nfs_convert.h" #include "fsal_pnfs.h" #include "export_mgr.h" struct cb_data { deviceid4 *buffer; size_t count; size_t max; uint64_t swexport; }; static bool cb(void *opaque, uint64_t id) { struct cb_data *data = (struct cb_data *)opaque; if (data->count > data->max) return false; *((uint64_t *) data->buffer[data->count]) = data->swexport; *((uint64_t *) (data->buffer[data->count] + sizeof(uint64_t))) = nfs_htonl64(id); ++data->count; return true; } /** * * @brief The NFS4_OP_GETDEVICELIST operation. * * This function returns a list of pNFS devices for a given * filesystem. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661 p. 365 * * @see nfs4_Compound * */ int nfs4_op_getdevicelist(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { /* Convenience alias for arguments */ GETDEVICELIST4args * const arg_GETDEVICELIST4 = &op->nfs_argop4_u.opgetdevicelist; /* Convenience alias for response */ GETDEVICELIST4res * const res_GETDEVICELIST4 = &resp->nfs_resop4_u.opgetdevicelist; /* Convenience alias for response */ GETDEVICELIST4resok *resok = &res_GETDEVICELIST4->GETDEVICELIST4res_u.gdlr_resok4; /* NFS4 return code */ nfsstat4 nfs_status = 0; /* Input/output and output parameters of FSAL function */ struct fsal_getdevicelist_res res; /* Structure for callback */ struct cb_data cb_opaque; resp->resop = NFS4_OP_GETDEVICELIST; if (data->minorversion == 0) return res_GETDEVICELIST4->gdlr_status = NFS4ERR_INVAL; nfs_status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false); if (nfs_status != NFS4_OK) goto out; memset(&res, 0, sizeof(struct fsal_getdevicelist_res)); res.cookie = arg_GETDEVICELIST4->gdla_cookie; memcpy(&res.cookieverf, arg_GETDEVICELIST4->gdla_cookieverf, NFS4_VERIFIER_SIZE); cb_opaque.count = 0; cb_opaque.max = 32; cb_opaque.swexport = nfs_htonl64(op_ctx->ctx_export->export_id); resok->gdlr_deviceid_list.gdlr_deviceid_list_val = gsh_malloc(cb_opaque.max * sizeof(deviceid4)); cb_opaque.buffer = resok->gdlr_deviceid_list.gdlr_deviceid_list_val; nfs_status = op_ctx->fsal_export->exp_ops.getdevicelist( op_ctx->fsal_export, arg_GETDEVICELIST4->gdla_layout_type, &cb_opaque, cb, &res); if (nfs_status != NFS4_OK) { gsh_free(cb_opaque.buffer); goto out; } resok->gdlr_cookie = res.cookie; memcpy(resok->gdlr_cookieverf, &res.cookieverf, NFS4_VERIFIER_SIZE); resok->gdlr_deviceid_list.gdlr_deviceid_list_len = cb_opaque.count; resok->gdlr_eof = res.eof; nfs_status = NFS4_OK; out: res_GETDEVICELIST4->gdlr_status = nfs_status; return res_GETDEVICELIST4->gdlr_status; } /** * @brief Free memory allocated for GETDEVICELIST result * * This function frees the memory allocates for the result of the * NFS4_OP_GETDEVICELIST operation. * * @param[in,out] resp nfs4_op results * */ void nfs4_op_getdevicelist_Free(nfs_resop4 *res) { GETDEVICELIST4res *resp = &res->nfs_resop4_u.opgetdevicelist; GETDEVICELIST4resok *resok = &resp->GETDEVICELIST4res_u.gdlr_resok4; if (resp->gdlr_status == NFS4_OK) { gsh_free(resok->gdlr_deviceid_list.gdlr_deviceid_list_val); } } /* nfs41_op_getdevicelist_Free */ nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_getfh.c000066400000000000000000000057531324272410200220120ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ---------------------------------------*/ #include "config.h" #include #include #include #include #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "nfs4.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_file_handle.h" /** * @brief The NFS4_OP_GETFH operation * * Gets the currentFH for the current compound requests. This * operation returns the current FH in the reply structure. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 366 * * @see nfs4_Compound */ int nfs4_op_getfh(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { GETFH4res * const res_GETFH = &resp->nfs_resop4_u.opgetfh; resp->resop = NFS4_OP_GETFH; res_GETFH->status = NFS4_OK; LogHandleNFS4("NFS4 GETFH BEFORE: ", &data->currentFH); /* Do basic checks on a filehandle */ res_GETFH->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, true); if (res_GETFH->status != NFS4_OK) return res_GETFH->status; /* Copy the filehandle to the reply structure */ nfs4_AllocateFH(&res_GETFH->GETFH4res_u.resok4.object); /* Put the data in place */ res_GETFH->GETFH4res_u.resok4.object.nfs_fh4_len = data->currentFH.nfs_fh4_len; memcpy(res_GETFH->GETFH4res_u.resok4.object.nfs_fh4_val, data->currentFH.nfs_fh4_val, data->currentFH.nfs_fh4_len); LogHandleNFS4("NFS4 GETFH AFTER: ", &res_GETFH->GETFH4res_u.resok4.object); res_GETFH->status = NFS4_OK; return NFS4_OK; } /* nfs4_op_getfh */ /** * @brief Free memory allocated for GETFH result * * This function frees any memory allocated for the result of the * NFS4_OP_GETFH operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_getfh_Free(nfs_resop4 *res) { GETFH4res *resp = &res->nfs_resop4_u.opgetfh; if (resp->status == NFS4_OK) gsh_free(resp->GETFH4res_u.resok4.object.nfs_fh4_val); } /* nfs4_op_getfh_Free */ nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_illegal.c000066400000000000000000000056131324272410200223210ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * \file nfs4_op_illegal.c * \brief Routines used for managing the NFS4 COMPOUND functions. */ #include "config.h" #include #include #include #include #include "log.h" #include "gsh_rpc.h" #include "nfs23.h" #include "nfs4.h" #include "mount.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" /** * @brief Always fail * * This function is the designated ILLEGAL operation. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op results * * @retval NFS4ERR_OP_ILLEGAL always. * */ int nfs4_op_illegal(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { resp->resop = NFS4_OP_ILLEGAL; resp->nfs_resop4_u.opillegal.status = NFS4ERR_OP_ILLEGAL; return NFS4ERR_OP_ILLEGAL; } /* nfs4_op_illegal */ /** * @brief Free memory allocated for ILLEGAL result * * This function frees any memory allocated for the result of the * NFS4_OP_ILLEGAL operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_illegal_Free(nfs_resop4 *resp) { /* Nothing to be done */ } /** * @brief Always fail * * This function is the designated NOTSUPP operation. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op results * * @retval NFS4ERR_NOTSUPP always. * */ int nfs4_op_notsupp(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { resp->resop = op->argop; resp->nfs_resop4_u.opillegal.status = NFS4ERR_NOTSUPP; return NFS4ERR_NOTSUPP; } /* nfs4_op_notsupp */ /** * @brief Free memory allocated for NOTSUPP result * * This function frees any memory allocated for the result of the * NFS4_OP_NOTSUPP operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_notsupp_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_layoutcommit.c000066400000000000000000000132241324272410200234330ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_layoutcommit.c * @brief The NFSv4.1 LAYOUTCOMMIT operation. */ #include "config.h" #include #include #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_convert.h" #include "fsal_pnfs.h" #include "sal_functions.h" /** * * @brief The NFS4_OP_LAYOUTCOMMIT operation * * This function implements the NFS4_OP_LAYOUTCOMMIT operation. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661 p. 366 * * @see nfs4_Compound * */ int nfs4_op_layoutcommit(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { /* Convenience alias for arguments */ LAYOUTCOMMIT4args * const args = &op->nfs_argop4_u.oplayoutcommit; /* Convenience alias for response */ LAYOUTCOMMIT4res * const res_LAYOUTCOMMIT4 = &resp->nfs_resop4_u.oplayoutcommit; /* Convenience alias for response */ LAYOUTCOMMIT4resok *resok = &res_LAYOUTCOMMIT4->LAYOUTCOMMIT4res_u.locr_resok4; /* NFS4 return code */ nfsstat4 nfs_status = 0; /* State indicated by client */ state_t *layout_state = NULL; /* Tag for logging in state operations */ const char *tag = "LAYOUTCOMMIT"; /* Iterator along linked list */ struct glist_head *glist = NULL; /* Input arguments of FSAL_layoutcommit */ struct fsal_layoutcommit_arg arg; /* Input/output and output arguments of FSAL_layoutcommit */ struct fsal_layoutcommit_res res; /* The segment being traversed */ state_layout_segment_t *segment; /* XDR stream holding the lrf_body opaque */ XDR lou_body; /* The beginning of the stream */ unsigned int beginning = 0; resp->resop = NFS4_OP_LAYOUTCOMMIT; if (data->minorversion == 0) { res_LAYOUTCOMMIT4->locr_status = NFS4ERR_INVAL; return res_LAYOUTCOMMIT4->locr_status; } nfs_status = nfs4_sanity_check_FH(data, REGULAR_FILE, false); if (nfs_status != NFS4_OK) { res_LAYOUTCOMMIT4->locr_status = nfs_status; return res_LAYOUTCOMMIT4->locr_status; } memset(&arg, 0, sizeof(struct fsal_layoutcommit_arg)); memset(&res, 0, sizeof(struct fsal_layoutcommit_res)); /* Suggest a new size, if we have it */ if (args->loca_last_write_offset.no_newoffset) { arg.new_offset = true; arg.last_write = args->loca_last_write_offset.newoffset4_u.no_offset; } else arg.new_offset = false; arg.reclaim = args->loca_reclaim; xdrmem_create(&lou_body, args->loca_layoutupdate.lou_body.lou_body_val, args->loca_layoutupdate.lou_body.lou_body_len, XDR_DECODE); beginning = xdr_getpos(&lou_body); /* Suggest a new modification time if we have it */ if (args->loca_time_modify.nt_timechanged) { arg.time_changed = true; arg.new_time.seconds = args->loca_time_modify.newtime4_u.nt_time.seconds; arg.new_time.nseconds = args->loca_time_modify.newtime4_u.nt_time.nseconds; } /* Retrieve state corresponding to supplied ID */ nfs_status = nfs4_Check_Stateid(&args->loca_stateid, data->current_obj, &layout_state, data, STATEID_SPECIAL_CURRENT, 0, false, tag); if (nfs_status != NFS4_OK) goto out; arg.type = layout_state->state_data.layout.state_layout_type; PTHREAD_RWLOCK_wrlock(&data->current_obj->state_hdl->state_lock); glist_for_each(glist, &layout_state->state_data.layout.state_segments) { segment = glist_entry(glist, state_layout_segment_t, sls_state_segments); arg.segment = segment->sls_segment; arg.fsal_seg_data = segment->sls_fsal_data; nfs_status = data->current_obj->obj_ops.layoutcommit( data->current_obj, op_ctx, &lou_body, &arg, &res); if (nfs_status != NFS4_OK) { PTHREAD_RWLOCK_unlock( &data->current_obj->state_hdl->state_lock); goto out; } if (res.commit_done) break; /* This really should work in all cases for an in-memory decode stream. */ xdr_setpos(&lou_body, beginning); } PTHREAD_RWLOCK_unlock(&data->current_obj->state_hdl->state_lock); resok->locr_newsize.ns_sizechanged = res.size_supplied; if (res.size_supplied) { resok->locr_newsize.newsize4_u.ns_size = res.new_size; } nfs_status = NFS4_OK; out: if (layout_state != NULL) dec_state_t_ref(layout_state); xdr_destroy(&lou_body); res_LAYOUTCOMMIT4->locr_status = nfs_status; return res_LAYOUTCOMMIT4->locr_status; } /* nfs41_op_layoutcommit */ /** * @brief free memory allocated for response of LAYOUTCOMMIT * * This function frees the memory allocated for response of * the NFS4_OP_LAYOUTCOMMIT operation. * * @param[in,out] resp Results for nfs4_op * */ void nfs4_op_layoutcommit_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_layoutget.c000066400000000000000000000376101324272410200227270ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ #include "config.h" #include #include #include #include #include #include #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "nfs23.h" #include "nfs4.h" #include "mount.h" #include "nfs_core.h" #include "export_mgr.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_file_handle.h" #include "fsal.h" #include "fsal_api.h" #include "fsal_pnfs.h" #include "sal_data.h" #include "sal_functions.h" /** * * @brief Get or make a layout state * * If the stateid supplied by the client refers to a layout state, * that state is returned. Otherwise, if it is a share, lock, or * delegation, a new state is created. Any layout state matching * clientid, file, and type is freed. * * @param[in,out] data Compound Compound request's data * @param[in] supplied_stateid The stateid included in * the arguments to layoutget * @param[in] layout_type Type of layout being requested * @param[out] layout_state The layout state * * @return NFS4_OK if successfull, other values on error */ static nfsstat4 acquire_layout_state(compound_data_t *data, stateid4 *supplied_stateid, layouttype4 layout_type, state_t **layout_state, const char *tag) { /* State associated with the client-supplied stateid */ state_t *supplied_state = NULL; /* State owner for per-clientid states */ state_owner_t *clientid_owner = NULL; /* Return from this function */ nfsstat4 nfs_status = 0; /* Return from state functions */ state_status_t state_status = 0; /* Layout state, forgotten about by caller */ state_t *condemned_state = NULL; /* Tracking data for the layout state */ struct state_refer refer; bool lock_held = false; memcpy(refer.session, data->session->session_id, sizeof(sessionid4)); refer.sequence = data->sequence; refer.slot = data->slot; clientid_owner = &data->session->clientid_record->cid_owner; /* Retrieve state corresponding to supplied ID, inspect it * and, if necessary, create a new layout state */ nfs_status = nfs4_Check_Stateid(supplied_stateid, data->current_obj, &supplied_state, data, STATEID_SPECIAL_CURRENT, 0, false, tag); if (nfs_status != NFS4_OK) { /* The supplied stateid was invalid */ return nfs_status; } if (supplied_state->state_type == STATE_TYPE_LAYOUT) { /* If the state supplied is a layout state, we can * simply use it, return with the reference we just * acquired. */ *layout_state = supplied_state; return nfs_status; } else if ((supplied_state->state_type == STATE_TYPE_SHARE) || (supplied_state->state_type == STATE_TYPE_DELEG) || (supplied_state->state_type == STATE_TYPE_LOCK)) { /* For share, delegation, and lock states, create a new layout state. */ union state_data layout_data; memset(&layout_data, 0, sizeof(layout_data)); PTHREAD_RWLOCK_wrlock( &data->current_obj->state_hdl->state_lock); lock_held = true; /* See if a layout state already exists */ state_status = state_lookup_layout_state(data->current_obj, clientid_owner, layout_type, &condemned_state); /* If it does, we assume that the client is using the * forgetful model and has forgotten it had any * layouts. Free all layouts associated with the * state and delete it. */ if (state_status == STATE_SUCCESS) { /* Flag indicating whether all layouts were returned * and the state was deleted */ bool deleted = false; struct pnfs_segment entire = { .io_mode = LAYOUTIOMODE4_ANY, .offset = 0, .length = NFS4_UINT64_MAX }; if (condemned_state->state_data.layout.granting) { nfs_status = NFS4ERR_DELAY; dec_state_t_ref(condemned_state); goto out; } nfs_status = nfs4_return_one_state(data->current_obj, 0, circumstance_forgotten, condemned_state, entire, 0, NULL, &deleted); dec_state_t_ref(condemned_state); if (nfs_status != NFS4_OK) goto out; if (!deleted) { nfs_status = NFS4ERR_SERVERFAULT; goto out; } condemned_state = NULL; } layout_data.layout.state_layout_type = layout_type; layout_data.layout.state_return_on_close = false; state_status = state_add_impl(data->current_obj, STATE_TYPE_LAYOUT, &layout_data, clientid_owner, layout_state, &refer); if (state_status != STATE_SUCCESS) { nfs_status = nfs4_Errno_state(state_status); goto out; } glist_init(&(*layout_state)->state_data.layout.state_segments); } else { /* A state eixsts but is of an invalid type. */ nfs_status = NFS4ERR_BAD_STATEID; goto out; } out: /* We are done with the supplied_state, release the reference. */ dec_state_t_ref(supplied_state); if (lock_held) PTHREAD_RWLOCK_unlock( &data->current_obj->state_hdl->state_lock); return nfs_status; } /** * * @brief Free layouts array. * * @param[in] layouts Layouts array * @param[in] numlayouts Size of array */ void free_layouts(layout4 *layouts, uint32_t numlayouts) { size_t i; for (i = 0; i < numlayouts; i++) { if (layouts[i].lo_content.loc_body.loc_body_val) gsh_free(layouts[i].lo_content.loc_body.loc_body_val); } gsh_free(layouts); } /** * * @brief Grant and add one layout segment * * This is a wrapper around the FSAL call that populates one entry in * the logr_layout array and adds one segment to the state list. * * @param[in] obj File handle * @param[in] arg Input arguments to the FSAL * @param[in,out] res Input/output and output arguments to the FSAL * @param[out] current Current entry in the logr_layout array. * * @return NFS4_OK if successfull, other values show an error. */ static nfsstat4 one_segment(struct fsal_obj_handle *obj, state_t *layout_state, const struct fsal_layoutget_arg *arg, struct fsal_layoutget_res *res, layout4 *current) { /* The initial position of the XDR stream after creation, so we can find the total length of encoded data. */ size_t start_position = 0; /* XDR stream to encode loc_body of the current segment */ XDR loc_body; /* Return code from this function */ nfsstat4 nfs_status = 0; /* Return from state calls */ state_status_t state_status = 0; /* Size of a loc_body buffer */ size_t loc_body_size = MIN( op_ctx->fsal_export->exp_ops.fs_loc_body_size(op_ctx->fsal_export), arg->maxcount); if (loc_body_size == 0) { LogCrit(COMPONENT_PNFS, "The FSAL must specify a non-zero loc_body_size."); nfs_status = NFS4ERR_SERVERFAULT; goto out; } /* Initialize the layout_content4 structure, allocate a buffer, and create an XDR stream for the FSAL to encode to. */ current->lo_content.loc_type = arg->type; current->lo_content.loc_body.loc_body_val = gsh_malloc(loc_body_size); xdrmem_create(&loc_body, current->lo_content.loc_body.loc_body_val, loc_body_size, XDR_ENCODE); start_position = xdr_getpos(&loc_body); ++layout_state->state_data.layout.granting; nfs_status = obj->obj_ops.layoutget(obj, op_ctx, &loc_body, arg, res); --layout_state->state_data.layout.granting; current->lo_content.loc_body.loc_body_len = xdr_getpos(&loc_body) - start_position; xdr_destroy(&loc_body); if (nfs_status != NFS4_OK) goto out; current->lo_offset = res->segment.offset; current->lo_length = res->segment.length; current->lo_iomode = res->segment.io_mode; state_status = state_add_segment(layout_state, &res->segment, res->fsal_seg_data, res->return_on_close); if (state_status != STATE_SUCCESS) { nfs_status = nfs4_Errno_state(state_status); goto out; } /** * @todo This is where you would want to record layoutget * operation. You can get the details of every segment added * here, including the segment description in * res->fsal_seg_data and clientid in *req_ctx->clientid. */ out: if (nfs_status != NFS4_OK) { if (current->lo_content.loc_body.loc_body_val) gsh_free(current->lo_content.loc_body.loc_body_val); } return nfs_status; } /** * @brief The NFS4_OP_LAYOUTGET operation * * This function implements the NFS4_OP_LAYOUTGET operation. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661 pp. 366-7 * * @see nfs4_Compound */ int nfs4_op_layoutget(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { /* Convenience alias for arguments */ LAYOUTGET4args * const arg_LAYOUTGET4 = &op->nfs_argop4_u.oplayoutget; /* Convenience alias for response */ LAYOUTGET4res * const res_LAYOUTGET4 = &resp->nfs_resop4_u.oplayoutget; /* Convenience alias for response */ LAYOUTGET4resok *resok = &res_LAYOUTGET4->LAYOUTGET4res_u.logr_resok4; /* NFSv4.1 status code */ nfsstat4 nfs_status = 0; /* Pointer to state governing layouts */ state_t *layout_state = NULL; /* Pointer to the array of layouts */ layout4 *layouts = NULL; /* Total number of layout segments returned by the FSAL */ uint32_t numlayouts = 0; /* Tag supplied to SAL functions for debugging messages */ const char *tag = "LAYOUTGET"; /* Input arguments of layoutget */ struct fsal_layoutget_arg arg; /* Input/output and output arguments of layoutget */ struct fsal_layoutget_res res; /* Maximum number of segments this FSAL will ever return for a single LAYOUTGET */ int max_segment_count = 0; resp->resop = NFS4_OP_LAYOUTGET; if (data->minorversion == 0) { res_LAYOUTGET4->logr_status = NFS4ERR_INVAL; return res_LAYOUTGET4->logr_status; } nfs_status = nfs4_sanity_check_FH(data, REGULAR_FILE, false); if (nfs_status != NFS4_OK) goto out; /* max_segment_count is also an indication of if fsal supports pnfs */ max_segment_count = op_ctx->fsal_export->exp_ops.fs_maximum_segments( op_ctx->fsal_export); if (max_segment_count == 0) { LogWarn(COMPONENT_PNFS, "The FSAL must specify a non-zero fs_maximum_segments."); nfs_status = NFS4ERR_LAYOUTUNAVAILABLE; goto out; } nfs_status = acquire_layout_state(data, &arg_LAYOUTGET4->loga_stateid, arg_LAYOUTGET4->loga_layout_type, &layout_state, tag); if (nfs_status != NFS4_OK) goto out; /* * Blank out argument structures and get the filehandle. */ memset(&arg, 0, sizeof(struct fsal_layoutget_arg)); memset(&res, 0, sizeof(struct fsal_layoutget_res)); /* * Initialize segment array and fill out input-only arguments */ layouts = gsh_calloc(max_segment_count, sizeof(layout4)); arg.type = arg_LAYOUTGET4->loga_layout_type; arg.minlength = arg_LAYOUTGET4->loga_minlength; arg.export_id = op_ctx->ctx_export->export_id; arg.maxcount = arg_LAYOUTGET4->loga_maxcount; /* Guaranteed on the first call */ res.context = NULL; /** * @todo ACE: Currently we have no callbacks, so it makes no * sense to pass the client-supplied value to the FSAL. When * we get callbacks, it will. */ res.signal_available = false; do { /* Since the FSAL writes to tis structure with every call, we re-initialize it with the operation's arguments */ res.segment.io_mode = arg_LAYOUTGET4->loga_iomode; res.segment.offset = arg_LAYOUTGET4->loga_offset; res.segment.length = arg_LAYOUTGET4->loga_length; /* Clear anything from a previous segment */ res.fsal_seg_data = NULL; nfs_status = one_segment(data->current_obj, layout_state, &arg, &res, layouts + numlayouts); if (nfs_status != NFS4_OK) goto out; arg.maxcount -= layouts[numlayouts].lo_content.loc_body.loc_body_len; numlayouts++; if ((numlayouts == max_segment_count) && !res.last_segment) { nfs_status = NFS4ERR_SERVERFAULT; goto out; } } while (!res.last_segment); /* Update stateid.seqid and copy to current */ update_stateid(layout_state, &resok->logr_stateid, data, tag); resok->logr_return_on_close = layout_state->state_data.layout.state_return_on_close; /* Now the layout specific information */ resok->logr_layout.logr_layout_len = numlayouts; resok->logr_layout.logr_layout_val = layouts; nfs_status = NFS4_OK; out: if (res_LAYOUTGET4->logr_status != NFS4_OK || nfs_status != NFS4_OK) { if (layouts != NULL) free_layouts(layouts, numlayouts); if ((layout_state) && (layout_state->state_seqid == 0)) { state_del(layout_state); layout_state = NULL; } /* Poison the current stateid */ data->current_stateid_valid = false; } if (layout_state != NULL) dec_state_t_ref(layout_state); res_LAYOUTGET4->logr_status = nfs_status; return res_LAYOUTGET4->logr_status; } /* nfs4_op_layoutget */ /** * @brief Free memory allocated for LAYOUTGET result * * This function frees any memory allocated for the NFS4_OP_LAYOUTGET * result. * * @param[in,out] resp nfs4_op results */ void nfs4_op_layoutget_Free(nfs_resop4 *res) { LAYOUTGET4res *resp = &res->nfs_resop4_u.oplayoutget; LAYOUTGET4resok *resok = &resp->LAYOUTGET4res_u.logr_resok4; if (resp->logr_status == NFS4_OK) free_layouts(resok->logr_layout.logr_layout_val, resok->logr_layout.logr_layout_len); } /* nfs41_op_layoutget_Free */ int nfs4_op_layouterror(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { /* Convenience alias for arguments */ LAYOUTERROR4args * const arg_LAYOUTERROR4 = &op->nfs_argop4_u.oplayouterror; /* Convenience alias for response */ LAYOUTERROR4res * const res_LAYOUTERROR4 = &resp->nfs_resop4_u.oplayouterror; /* NFSv4.2 status code */ nfsstat4 nfs_status = 0; LogEvent(COMPONENT_PNFS, "LAYOUTERROR OP %d status %d offset: %" PRIu64 " length: %" PRIu64, arg_LAYOUTERROR4->lea_errors.de_opnum, arg_LAYOUTERROR4->lea_errors.de_status, arg_LAYOUTERROR4->lea_offset, arg_LAYOUTERROR4->lea_length); /** @todo: what else do we want to do with this error ??? */ res_LAYOUTERROR4->ler_status = nfs_status; return res_LAYOUTERROR4->ler_status; } void nfs4_op_layouterror_Free(nfs_resop4 *res) { } int nfs4_op_layoutstats(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { /* Convenience alias for arguments */ LAYOUTSTATS4args * const arg_LAYOUTSTATS4 = &op->nfs_argop4_u.oplayoutstats; /* Convenience alias for response */ LAYOUTSTATS4res * const res_LAYOUTSTATS4 = &resp->nfs_resop4_u.oplayoutstats; /* NFSv4.2 status code */ nfsstat4 nfs_status = 0; LogEvent(COMPONENT_PNFS, "LAYOUTSTATS offset %" PRIu64 " length %" PRIu64, arg_LAYOUTSTATS4->lsa_offset, arg_LAYOUTSTATS4->lsa_length); LogEvent(COMPONENT_PNFS, "LAYOUTSTATS read count %u bytes %" PRIu64 " write count %u bytes %" PRIu64, arg_LAYOUTSTATS4->lsa_read.ii_count, arg_LAYOUTSTATS4->lsa_read.ii_bytes, arg_LAYOUTSTATS4->lsa_write.ii_count, arg_LAYOUTSTATS4->lsa_write.ii_bytes); /** @todo: what else do we want to do with the stats ??? */ res_LAYOUTSTATS4->lsr_status = nfs_status; return res_LAYOUTSTATS4->lsr_status; } void nfs4_op_layoutstats_Free(nfs_resop4 *res) { } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_layoutreturn.c000066400000000000000000000430131324272410200234610ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_layoutreturn.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. */ #include "config.h" #include #include #include #include #include #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "nfs4.h" #include "nfs_core.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_file_handle.h" #include "nfs_convert.h" #include "fsal.h" #include "pnfs_utils.h" #include "sal_data.h" #include "sal_functions.h" /** * * @brief The NFS4_OP_LAYOUTRETURN operation. * * This function implements the NFS4_OP_LAYOUTRETURN operation. * * @param[in] op Arguments fo nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661 p. 367 * * @see nfs4_Compound */ int nfs4_op_layoutreturn(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { /* Convenience alias for arguments */ LAYOUTRETURN4args * const arg_LAYOUTRETURN4 = &op->nfs_argop4_u.oplayoutreturn; /* Convenience alias for arguments */ layoutreturn_file4 *lr_layout = &arg_LAYOUTRETURN4->lora_layoutreturn.layoutreturn4_u.lr_layout; /* Convenience alias for response */ LAYOUTRETURN4res * const res_LAYOUTRETURN4 = &resp->nfs_resop4_u.oplayoutreturn; /* Convenience alias for response */ layoutreturn_stateid *lorr_stateid = &res_LAYOUTRETURN4->LAYOUTRETURN4res_u.lorr_stateid; /* Convenience alias for response */ stateid4 *lrs_stateid = &lorr_stateid->layoutreturn_stateid_u.lrs_stateid; /* NFS4 status code */ nfsstat4 nfs_status = 0; /* FSID of candidate file to return */ fsal_fsid_t fsid = { 0, 0 }; /* True if the supplied layout state was deleted */ bool deleted = false; /* State specified in the case of LAYOUTRETURN4_FILE */ state_t *layout_state = NULL; /* State owner associated with this clientid, for bulk returns */ state_owner_t *clientid_owner = NULL; struct glist_head *state_list; /* Linked list node for iteration */ struct glist_head *glist = NULL; /* Saved next node for safe iteration */ struct glist_head *glistn = NULL; /* Tag to identify caller in tate log messages */ const char *tag = "LAYOUTRETURN"; /* Segment selecting which segments to return. */ struct pnfs_segment spec = { 0, 0, 0 }; /* Remember if we need to do fsid based return */ bool return_fsid = false; /* Referenced file */ struct fsal_obj_handle *obj = NULL; /* Referenced export */ struct gsh_export *export = NULL; /* Root op context for returning fsid or all layouts */ struct root_op_context root_op_context; /* Keep track of so_mutex */ bool so_mutex_locked = false; state_t *first; resp->resop = NFS4_OP_LAYOUTRETURN; if (data->minorversion == 0) { res_LAYOUTRETURN4->lorr_status = NFS4ERR_INVAL; return res_LAYOUTRETURN4->lorr_status; } switch (arg_LAYOUTRETURN4->lora_layoutreturn.lr_returntype) { case LAYOUTRETURN4_FILE: nfs_status = nfs4_sanity_check_FH(data, REGULAR_FILE, false); if (nfs_status != NFS4_OK) { res_LAYOUTRETURN4->lorr_status = nfs_status; break; } /* Retrieve state corresponding to supplied ID */ if (!arg_LAYOUTRETURN4->lora_reclaim) { nfs_status = nfs4_Check_Stateid( &lr_layout->lrf_stateid, data->current_obj, &layout_state, data, STATEID_SPECIAL_CURRENT, 0, false, tag); if (nfs_status != NFS4_OK) { res_LAYOUTRETURN4->lorr_status = nfs_status; break; } } spec.io_mode = arg_LAYOUTRETURN4->lora_iomode; spec.offset = lr_layout->lrf_offset; spec.length = lr_layout->lrf_length; PTHREAD_RWLOCK_wrlock( &data->current_obj->state_hdl->state_lock); res_LAYOUTRETURN4->lorr_status = nfs4_return_one_state( data->current_obj, arg_LAYOUTRETURN4->lora_layoutreturn.lr_returntype, arg_LAYOUTRETURN4->lora_reclaim ? circumstance_reclaim : circumstance_client, layout_state, spec, lr_layout->lrf_body.lrf_body_len, lr_layout->lrf_body.lrf_body_val, &deleted); PTHREAD_RWLOCK_unlock( &data->current_obj->state_hdl->state_lock); if (res_LAYOUTRETURN4->lorr_status == NFS4_OK) { if (deleted) { /* Poison the current stateid */ data->current_stateid_valid = false; lorr_stateid->lrs_present = 0; } else { lorr_stateid->lrs_present = 1; /* Update stateid.seqid and copy to current */ update_stateid(layout_state, lrs_stateid, data, tag); } } if (!arg_LAYOUTRETURN4->lora_reclaim) dec_state_t_ref(layout_state); break; case LAYOUTRETURN4_FSID: nfs_status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false); if (nfs_status != NFS4_OK) { res_LAYOUTRETURN4->lorr_status = nfs_status; break; } fsid = data->current_obj->fsid; return_fsid = true; /* FALLTHROUGH */ case LAYOUTRETURN4_ALL: spec.io_mode = arg_LAYOUTRETURN4->lora_iomode; spec.offset = 0; spec.length = NFS4_UINT64_MAX; clientid_owner = &data->session->clientid_record->cid_owner; /* Initialize req_ctx */ init_root_op_context(&root_op_context, NULL, NULL, 0, 0, UNKNOWN_REQUEST); /* We need the safe version because return_one_state * can delete the current state. * * Since we can not hold so_mutex (which protects the list) * the entire time, we will have to restart here after * dropping the mutex. * * Since we push each entry to the end of the list, we will * not need to continually examine entries that need to be * skipped, except for one final pass. * * An example flow might be: * skip 1 * skip 2 * do some work on 3 * restart * skip 4 * do some work on 5 * restart * skip 1 * skip 2 * skip 4 * done */ again: PTHREAD_MUTEX_lock(&clientid_owner->so_mutex); so_mutex_locked = true; first = NULL; state_list = &clientid_owner->so_owner.so_nfs4_owner.so_state_list; glist_for_each_safe(glist, glistn, state_list) { layout_state = glist_entry(glist, state_t, state_owner_list); if (first == NULL) first = layout_state; else if (first == layout_state) break; /* Move to end of list in case of error to ease * retries and push off dealing with non-layout * states (which should only be delegations). */ glist_del(&layout_state->state_owner_list); glist_add_tail(state_list, &layout_state->state_owner_list); if (layout_state->state_type != STATE_TYPE_LAYOUT) continue; if (!get_state_obj_export_owner_refs(layout_state, &obj, &export, NULL)) { /* This state is associated with a file or * export that is going stale, skip it (it * will be cleaned up as part of the stale * entry or export processing. */ continue; } /* Set up the root op context for this state */ root_op_context.req_ctx.clientid = &clientid_owner->so_owner.so_nfs4_owner.so_clientid; root_op_context.req_ctx.ctx_export = export; root_op_context.req_ctx.fsal_export = export->fsal_export; /* Take a reference on the state_t */ inc_state_t_ref(layout_state); /* Now we need to drop so_mutex to continue the * processing. */ PTHREAD_MUTEX_unlock(&clientid_owner->so_mutex); so_mutex_locked = false; if (return_fsid) { if (!memcmp(&fsid, &data->current_obj->fsid, sizeof(fsid))) { obj->obj_ops.put_ref(obj); put_gsh_export(export); dec_state_t_ref(layout_state); /* Since we had to drop so_mutex, the * list may have changed under us, we * MUST start over. */ goto again; } } PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); res_LAYOUTRETURN4->lorr_status = nfs4_return_one_state( obj, arg_LAYOUTRETURN4->lora_layoutreturn.lr_returntype, arg_LAYOUTRETURN4->lora_reclaim ? circumstance_reclaim : circumstance_client, layout_state, spec, 0, NULL, &deleted); PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); /* Release the state_t reference */ dec_state_t_ref(layout_state); if (res_LAYOUTRETURN4->lorr_status != NFS4_OK) break; /* Since we had to drop so_mutex, the list may have * changed under us, we MUST start over. */ obj->obj_ops.put_ref(obj); put_gsh_export(export); goto again; } if (so_mutex_locked) PTHREAD_MUTEX_lock(&clientid_owner->so_mutex); /* Poison the current stateid */ data->current_stateid_valid = false; lorr_stateid->lrs_present = 0; break; default: res_LAYOUTRETURN4->lorr_status = NFS4ERR_INVAL; } if (arg_LAYOUTRETURN4->lora_layoutreturn.lr_returntype == LAYOUTRETURN4_FSID || arg_LAYOUTRETURN4->lora_layoutreturn.lr_returntype == LAYOUTRETURN4_ALL ) { /* Release the root op context we setup above */ release_root_op_context(); } if (obj != NULL) { /* Release object ref */ obj->obj_ops.put_ref(obj); } if (export != NULL) { /* Release the export */ put_gsh_export(export); } return res_LAYOUTRETURN4->lorr_status; } /* nfs41_op_layoutreturn */ /** * @brief Free memory allocated for LAYOUTRETURN result * * This function frees any memory allocated for the result from * the NFS4_OP_LAYOUTRETURN operation. * * @param[in] resp nfs4_op results * */ void nfs4_op_layoutreturn_Free(nfs_resop4 *resp) { /* Nothing to be done */ } /** * @brief Handle recalls corresponding to one stateid * * @note the state_lock MUST be held for write * * @param[in] args Layout return args * @param[in] ostate File state * @param[in] state The state in question * @param[in] segment Segment specified in return * */ void handle_recalls(struct fsal_layoutreturn_arg *arg, struct state_hdl *ostate, state_t *state, const struct pnfs_segment *segment) { /* Iterator over the recall list */ struct glist_head *recall_iter = NULL; /* Next recall for safe iteration */ struct glist_head *recall_next = NULL; struct glist_head *state_segments = &state->state_data.layout.state_segments; glist_for_each_safe(recall_iter, recall_next, &ostate->file.layoutrecall_list) { /* The current recall state */ struct state_layout_recall_file *r; /* Iteration on states */ struct glist_head *state_iter = NULL; /* Next entry in state list */ struct glist_head *state_next = NULL; r = glist_entry(recall_iter, struct state_layout_recall_file, entry_link); glist_for_each_safe(state_iter, state_next, &r->state_list) { struct recall_state_list *s; /* Iteration on segments */ struct glist_head *seg_iter = NULL; /* We found a segment that satisfies the recall */ bool satisfaction = false; s = glist_entry(state_iter, struct recall_state_list, link); if (s->state != state) continue; glist_for_each(seg_iter, state_segments) { struct state_layout_segment *g; g = glist_entry(seg_iter, struct state_layout_segment, sls_state_segments); if (!pnfs_segments_overlap(&g->sls_segment, segment)) { /* We don't even touch this */ break; } else if (!pnfs_segment_contains( segment, &g->sls_segment)) { /* Not satisfied completely */ } else satisfaction = true; } if (satisfaction && glist_length(state_segments) == 1) { dec_state_t_ref(s->state); glist_del(&s->link); arg->recall_cookies[arg->ncookies++] = r->recall_cookie; gsh_free(s); } } if (glist_empty(&r->state_list)) { /* Remove from entry->layoutrecall_list */ glist_del(&r->entry_link); gsh_free(r); } } } /** * @brief Return layouts corresponding to one stateid * * This function returns one or more layouts corresponding to a layout * stateid, calling FSAL_layoutreturn for each layout falling within * the specified range and iomode. If all layouts have been returned, * it deletes the state. * * @note The state_lock MUST be held for write * * @param[in] obj File whose layouts we return * @param[in] return_type Whether this is a file, fs, or server return * @param[in] circumstance Why the layout is being returned * @param[in,out] state State whose segments we return * @param[in] spec_segment Segment specified in return * @param[in] body_len Length of type-specific layout return data * @param[in] body_val Type-specific layout return data * @param[out] deleted True if the layout state has been deleted * * @return NFSv4.1 status codes */ nfsstat4 nfs4_return_one_state(struct fsal_obj_handle *obj, layoutreturn_type4 return_type, enum fsal_layoutreturn_circumstance circumstance, state_t *state, struct pnfs_segment spec_segment, size_t body_len, const void *body_val, bool *deleted) { /* Return from SAL calls */ state_status_t state_status = 0; /* Return from this function */ nfsstat4 nfs_status = 0; /* Iterator along segment list */ struct glist_head *seg_iter = NULL; /* Saved 'next' pointer for iterating over segment list */ struct glist_head *seg_next = NULL; /* Input arguments to FSAL_layoutreturn */ struct fsal_layoutreturn_arg *arg; /* XDR stream holding the lrf_body opaque */ XDR lrf_body; /* The beginning of the stream */ unsigned int beginning = 0; /* Number of recalls currently on the entry */ size_t recalls = 0; /* The current segment in iteration */ state_layout_segment_t *g = NULL; struct glist_head *state_segments = &state->state_data.layout.state_segments; recalls = glist_length(&obj->state_hdl->file.layoutrecall_list); if (body_val) { xdrmem_create(&lrf_body, (char *)body_val, /* Decoding won't modify */ body_len, XDR_DECODE); beginning = xdr_getpos(&lrf_body); } arg = alloca(sizeof(struct fsal_layoutreturn_arg) + sizeof(void *) * (recalls - 1)); memset(arg, 0, sizeof(struct fsal_layoutreturn_arg)); arg->circumstance = circumstance; arg->return_type = return_type; arg->spec_segment = spec_segment; arg->ncookies = 0; /** * @todo This is where you would want to record * layoutreturns. There are lots of things that are * effectively layoutreturns that don't go through the * nfs4_op_layoutreturn function, but they do all go through * here. For circumstance values of circumstance_client, * circumstance_roc, and circumstance_forgotten, it should * count as a legitimate client operation. * circumstance_revoke means that we attempted a recall and * the client misbehaved. circumstance_shutdown and * circumstance_reclaim are probably not worth dealing with. */ if (circumstance != circumstance_reclaim) { arg->lo_type = state->state_data.layout.state_layout_type; /* The _safe version of glist_for_each allows us to * delete segments while we iterate. */ glist_for_each_safe(seg_iter, seg_next, state_segments) { /* The current segment in iteration */ g = glist_entry(seg_iter, state_layout_segment_t, sls_state_segments); arg->cur_segment = g->sls_segment; arg->fsal_seg_data = g->sls_fsal_data; /* TODO: why this check does not work */ arg->last_segment = (seg_next->next == seg_next); if (pnfs_segment_contains (&spec_segment, &g->sls_segment)) { arg->dispose = true; } else if (pnfs_segments_overlap(&spec_segment, &g->sls_segment)) arg->dispose = false; else continue; handle_recalls(arg, obj->state_hdl, state, &g->sls_segment); nfs_status = obj->obj_ops.layoutreturn( obj, op_ctx, body_val ? &lrf_body : NULL, arg); if (nfs_status != NFS4_OK) goto out; if (arg->dispose) { state_status = state_delete_segment(g); if (state_status != STATE_SUCCESS) { nfs_status = nfs4_Errno_state(state_status); goto out; } } else { g->sls_segment = pnfs_segment_difference(&spec_segment, &g->sls_segment); } } if (body_val) { /* This really should work in all cases for an * in-memory decode stream. */ xdr_setpos(&lrf_body, beginning); } if (glist_empty(state_segments)) { state_del_locked(state); *deleted = true; } else *deleted = false; } else { /* For a reclaim return, there are no recorded segments in * state. */ arg->cur_segment.io_mode = 0; arg->cur_segment.offset = 0; arg->cur_segment.length = 0; arg->fsal_seg_data = NULL; arg->last_segment = false; arg->dispose = false; nfs_status = obj->obj_ops.layoutreturn( obj, op_ctx, body_val ? &lrf_body : NULL, arg); if (nfs_status != NFS4_OK) goto out; *deleted = true; } nfs_status = NFS4_OK; out: if (body_val) xdr_destroy(&lrf_body); return nfs_status; } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_link.c000066400000000000000000000077151324272410200216520ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * \file nfs4_op_link.c * \brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. * * */ #include "config.h" #include #include #include #include #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "nfs4.h" #include "nfs_core.h" #include "fsal.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_convert.h" #include "nfs_file_handle.h" /** * @brief The NFS4_OP_LINK operation. * * This functions handles the NFS4_OP_LINK operation in NFSv4. This * function can be called only from nfs4_Compound. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 367 */ int nfs4_op_link(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { LINK4args * const arg_LINK4 = &op->nfs_argop4_u.oplink; LINK4res * const res_LINK4 = &resp->nfs_resop4_u.oplink; struct fsal_obj_handle *dir_obj = NULL; struct fsal_obj_handle *file_obj = NULL; fsal_status_t status = {0, 0}; char *newname = NULL; resp->resop = NFS4_OP_LINK; res_LINK4->status = NFS4_OK; /* Do basic checks on a filehandle */ res_LINK4->status = nfs4_sanity_check_FH(data, DIRECTORY, false); if (res_LINK4->status != NFS4_OK) goto out; res_LINK4->status = nfs4_sanity_check_saved_FH(data, -DIRECTORY, false); if (res_LINK4->status != NFS4_OK) goto out; /* Check that both handles are in the same export. */ if (op_ctx->ctx_export != NULL && data->saved_export != NULL && op_ctx->ctx_export->export_id != data->saved_export->export_id) { res_LINK4->status = NFS4ERR_XDEV; goto out; } /* * This operation creates a hard link, for the file * represented by the saved FH, in directory represented by * currentFH under the name arg_LINK4.target */ /* Validate and convert the UFT8 objname to a regular string */ res_LINK4->status = nfs4_utf8string2dynamic(&arg_LINK4->newname, UTF8_SCAN_ALL, &newname); if (res_LINK4->status != NFS4_OK) goto out; /* get info from compound data */ dir_obj = data->current_obj; res_LINK4->LINK4res_u.resok4.cinfo.before = fsal_get_changeid4(dir_obj); file_obj = data->saved_obj; /* make the link */ status = fsal_link(file_obj, dir_obj, newname); if (FSAL_IS_ERROR(status)) { res_LINK4->status = nfs4_Errno_status(status); goto out; } res_LINK4->LINK4res_u.resok4.cinfo.after = fsal_get_changeid4(dir_obj); res_LINK4->LINK4res_u.resok4.cinfo.atomic = FALSE; res_LINK4->status = NFS4_OK; out: if (newname) gsh_free(newname); return res_LINK4->status; } /* nfs4_op_link */ /** * @brief Free memory allocated for LINK result * * This function frees any memory allocated for the result of the * NFS4_OP_LINK operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_link_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_lock.c000066400000000000000000000467061324272410200216500ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_lock.c * @brief Implementation of NFS4_OP_LOCK */ #include "config.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "sal_functions.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "gsh_list.h" #include "export_mgr.h" static const char *lock_tag = "LOCK"; /** * @brief The NFS4_OP_LOCK operation. * * This function implements the NFS4_OP_LOCK operation. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC 5661, pp. 367-8 * * @see nfs4_Compound * */ int nfs4_op_lock(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { /* Shorter alias for arguments */ LOCK4args * const arg_LOCK4 = &op->nfs_argop4_u.oplock; open_to_lock_owner4 *arg_open_owner = &arg_LOCK4->locker.locker4_u.open_owner; /* Shorter alias for response */ LOCK4res * const res_LOCK4 = &resp->nfs_resop4_u.oplock; /* Status code from state calls */ state_status_t state_status = STATE_SUCCESS; /* Data for lock state to be created */ union state_data candidate_data; /* Status code for protocol functions */ nfsstat4 nfs_status = 0; /* Created or found lock state */ state_t *lock_state = NULL; /* Associated open state */ state_t *state_open = NULL; /* The lock owner */ state_owner_t *lock_owner = NULL; /* The open owner */ state_owner_t *open_owner = NULL; /* The owner of a conflicting lock */ state_owner_t *conflict_owner = NULL; /* The owner in which to store the response for NFSv4.0 */ state_owner_t *resp_owner = NULL; /* Sequence ID, for NFSv4.0 */ seqid4 seqid = 0; /* The client performing these operations */ nfs_client_id_t *clientid = NULL; /* Name for the lock owner */ state_nfs4_owner_name_t owner_name; /* Description of requrested lock */ fsal_lock_param_t lock_desc; /* Description of conflicting lock */ fsal_lock_param_t conflict_desc; /* Whether to block */ state_blocking_t blocking = STATE_NON_BLOCKING; /* Tracking data for the lock state */ struct state_refer refer; /* Indicate if we let FSAL to handle requests during grace. */ bool_t fsal_grace = false; int rc; struct fsal_obj_handle *obj = data->current_obj; bool state_lock_held = false; LogDebug(COMPONENT_NFS_V4_LOCK, "Entering NFS v4 LOCK handler ----------------------"); /* Initialize to sane starting values */ resp->resop = NFS4_OP_LOCK; res_LOCK4->status = NFS4_OK; /* Record the sequence info */ if (data->minorversion > 0) { memcpy(refer.session, data->session->session_id, sizeof(sessionid4)); refer.sequence = data->sequence; refer.slot = data->slot; } res_LOCK4->status = nfs4_sanity_check_FH(data, REGULAR_FILE, false); if (res_LOCK4->status != NFS4_OK) return res_LOCK4->status; /* Convert lock parameters to internal types */ switch (arg_LOCK4->locktype) { case READW_LT: blocking = STATE_NFSV4_BLOCKING; /* Fall through */ case READ_LT: lock_desc.lock_type = FSAL_LOCK_R; break; case WRITEW_LT: blocking = STATE_NFSV4_BLOCKING; /* Fall through */ case WRITE_LT: lock_desc.lock_type = FSAL_LOCK_W; break; default: LogDebug(COMPONENT_NFS_V4_LOCK, "Invalid lock type"); res_LOCK4->status = NFS4ERR_INVAL; return res_LOCK4->status; } lock_desc.lock_start = arg_LOCK4->offset; lock_desc.lock_sle_type = FSAL_POSIX_LOCK; lock_desc.lock_reclaim = arg_LOCK4->reclaim; if (arg_LOCK4->length != STATE_LOCK_OFFSET_EOF) lock_desc.lock_length = arg_LOCK4->length; else lock_desc.lock_length = 0; if (arg_LOCK4->locker.new_lock_owner) { /* Check stateid correctness and get pointer to state */ nfs_status = nfs4_Check_Stateid( &arg_open_owner->open_stateid, obj, &state_open, data, STATEID_SPECIAL_FOR_LOCK, arg_open_owner->open_seqid, data->minorversion == 0, lock_tag); if (nfs_status != NFS4_OK) { if (nfs_status == NFS4ERR_REPLAY) { open_owner = get_state_owner_ref(state_open); LogStateOwner("Open: ", open_owner); if (open_owner != NULL) { resp_owner = open_owner; seqid = arg_LOCK4->locker.locker4_u .open_owner.open_seqid; goto check_seqid; } } res_LOCK4->status = nfs_status; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed nfs4_Check_Stateid for open owner"); return res_LOCK4->status; } open_owner = get_state_owner_ref(state_open); LogStateOwner("Open: ", open_owner); if (open_owner == NULL) { /* State is going stale. */ res_LOCK4->status = NFS4ERR_STALE; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed nfs4_Check_Stateid, stale open owner"); goto out2; } lock_state = NULL; lock_owner = NULL; resp_owner = open_owner; seqid = arg_open_owner->open_seqid; LogLock(COMPONENT_NFS_V4_LOCK, NIV_FULL_DEBUG, "LOCK New lock owner from open owner", obj, open_owner, &lock_desc); /* Check is the clientid is known or not */ rc = nfs_client_id_get_confirmed( data->minorversion == 0 ? arg_open_owner->lock_owner.clientid : data->session->clientid, &clientid); if (rc != CLIENT_ID_SUCCESS) { res_LOCK4->status = clientid_error_to_nfsstat(rc); LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed nfs_client_id_get"); goto out2; } if (isDebug(COMPONENT_CLIENTID) && (clientid != open_owner->so_owner.so_nfs4_owner.so_clientrec)) { char str_open[LOG_BUFF_LEN / 2] = "\0"; struct display_buffer dspbuf_open = { sizeof(str_open), str_open, str_open}; char str_lock[LOG_BUFF_LEN / 2] = "\0"; struct display_buffer dspbuf_lock = { sizeof(str_lock), str_lock, str_lock}; display_client_id_rec(&dspbuf_open, open_owner->so_owner .so_nfs4_owner.so_clientrec); display_client_id_rec(&dspbuf_lock, clientid); LogDebug(COMPONENT_CLIENTID, "Unexpected, new lock owner clientid {%s} doesn't match open owner clientid {%s}", str_lock, str_open); } /* The related stateid is already stored in state_open */ /* An open state has been found. Check its type */ if (state_open->state_type != STATE_TYPE_SHARE) { res_LOCK4->status = NFS4ERR_BAD_STATEID; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed open stateid is not a SHARE"); goto out2; } /* Is this lock_owner known ? */ convert_nfs4_lock_owner(&arg_open_owner->lock_owner, &owner_name); LogStateOwner("Lock: ", lock_owner); } else { /* Existing lock owner Find the lock stateid From * that, get the open_owner * * There was code here before to handle all-0 stateid, * but that really doesn't apply - when we handle * temporary locks for I/O operations (which is where * we will see all-0 or all-1 stateid, those will not * come in through nfs4_op_lock. * * Check stateid correctness and get pointer to state */ nfs_status = nfs4_Check_Stateid( &arg_LOCK4->locker.locker4_u.lock_owner.lock_stateid, obj, &lock_state, data, STATEID_SPECIAL_FOR_LOCK, arg_LOCK4->locker.locker4_u.lock_owner.lock_seqid, data->minorversion == 0, lock_tag); if (nfs_status != NFS4_OK) { if (nfs_status == NFS4ERR_REPLAY) { lock_owner = get_state_owner_ref(lock_state); LogStateOwner("Lock: ", lock_owner); if (lock_owner != NULL) { open_owner = lock_owner->so_owner .so_nfs4_owner.so_related_owner; inc_state_owner_ref(open_owner); resp_owner = lock_owner; seqid = arg_LOCK4->locker.locker4_u .lock_owner.lock_seqid; goto check_seqid; } } res_LOCK4->status = nfs_status; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed nfs4_Check_Stateid for existing lock owner"); return res_LOCK4->status; } /* Check if lock state belongs to same export */ if (!state_same_export(lock_state, op_ctx->ctx_export)) { LogEvent(COMPONENT_STATE, "Lock Owner Export Conflict, Lock held for export %" PRIu16" request for export %"PRIu16, state_export_id(lock_state), op_ctx->ctx_export->export_id); res_LOCK4->status = NFS4ERR_INVAL; goto out2; } /* A lock state has been found. Check its type */ if (lock_state->state_type != STATE_TYPE_LOCK) { res_LOCK4->status = NFS4ERR_BAD_STATEID; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed existing lock owner, state type is not LOCK"); goto out2; } /* Get the old lockowner. We can do the following * 'cast', in NFSv4 lock_owner4 and open_owner4 are * different types but with the same definition */ lock_owner = get_state_owner_ref(lock_state); LogStateOwner("Lock: ", lock_owner); if (lock_owner == NULL) { /* State is going stale. */ res_LOCK4->status = NFS4ERR_STALE; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed nfs4_Check_Stateid, stale open owner"); goto out2; } open_owner = lock_owner->so_owner.so_nfs4_owner.so_related_owner; LogStateOwner("Open: ", open_owner); inc_state_owner_ref(open_owner); state_open = lock_state->state_data.lock.openstate; inc_state_t_ref(state_open); resp_owner = lock_owner; seqid = arg_LOCK4->locker.locker4_u.lock_owner.lock_seqid; LogLock(COMPONENT_NFS_V4_LOCK, NIV_FULL_DEBUG, "LOCK Existing lock owner", obj, lock_owner, &lock_desc); /* Get the client for this open owner */ clientid = open_owner->so_owner.so_nfs4_owner.so_clientrec; inc_client_id_ref(clientid); } check_seqid: /* Check seqid (lock_seqid or open_seqid) */ if (data->minorversion == 0) { if (!Check_nfs4_seqid(resp_owner, seqid, op, obj, resp, lock_tag)) { /* Response is all setup for us and LogDebug * told what was wrong */ goto out2; } } /* Lock length should not be 0 */ if (arg_LOCK4->length == 0LL) { res_LOCK4->status = NFS4ERR_INVAL; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed length == 0"); goto out; } /* Check for range overflow. Comparing beyond 2^64 is not * possible int 64 bits precision, but off+len > 2^64-1 is * equivalent to len > 2^64-1 - off */ if (lock_desc.lock_length > (STATE_LOCK_OFFSET_EOF - lock_desc.lock_start)) { res_LOCK4->status = NFS4ERR_INVAL; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed length overflow"); goto out; } /* Check if open state has correct access for type of lock. * * Don't need to check for conflicting states since this open * state assures there are no conflicting states. */ if (((arg_LOCK4->locktype == WRITE_LT || arg_LOCK4->locktype == WRITEW_LT) && ((state_open->state_data.share.share_access & OPEN4_SHARE_ACCESS_WRITE) == 0)) || ((arg_LOCK4->locktype == READ_LT || arg_LOCK4->locktype == READW_LT) && ((state_open->state_data.share.share_access & OPEN4_SHARE_ACCESS_READ) == 0))) { /* The open state doesn't allow access based on the * type of lock */ LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG, "LOCK failed, SHARE doesn't allow access", obj, lock_owner, &lock_desc); res_LOCK4->status = NFS4ERR_OPENMODE; goto out; } /* Do grace period checking (use resp_owner below since a new * lock request with a new lock owner doesn't have a lock owner * yet, but does have an open owner - resp_owner is always one or * the other and non-NULL at this point - so makes for a better log). */ if (nfs_in_grace()) { if (op_ctx->fsal_export->exp_ops.fs_supports( op_ctx->fsal_export, fso_grace_method)) fsal_grace = true; if (!fsal_grace && !arg_LOCK4->reclaim) { LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG, "LOCK failed, non-reclaim while in grace", obj, resp_owner, &lock_desc); res_LOCK4->status = NFS4ERR_GRACE; goto out; } if (!fsal_grace && arg_LOCK4->reclaim && !clientid->cid_allow_reclaim) { LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG, "LOCK failed, invalid reclaim while in grace", obj, resp_owner, &lock_desc); res_LOCK4->status = NFS4ERR_NO_GRACE; goto out; } } else { if (arg_LOCK4->reclaim) { LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG, "LOCK failed, reclaim while not in grace", obj, resp_owner, &lock_desc); res_LOCK4->status = NFS4ERR_NO_GRACE; goto out; } } /* Test if this request is attempting to create a new lock owner */ if (arg_LOCK4->locker.new_lock_owner) { bool_t isnew; /* A lock owner is always associated with a previously made open which has itself a previously made stateid */ /* This lock owner is not known yet, allocated and set up a new one */ lock_owner = create_nfs4_owner(&owner_name, clientid, STATE_LOCK_OWNER_NFSV4, open_owner, 0, &isnew, CARE_ALWAYS, true); LogStateOwner("Lock: ", lock_owner); if (lock_owner == NULL) { res_LOCK4->status = NFS4ERR_RESOURCE; LogLock(COMPONENT_NFS_V4_LOCK, NIV_EVENT, "LOCK failed to create new lock owner", obj, open_owner, &lock_desc); goto out2; } if (!isnew) { PTHREAD_MUTEX_lock(&lock_owner->so_mutex); /* Check lock_seqid if it has attached locks. */ if (!glist_empty(&lock_owner->so_lock_list) && (data->minorversion == 0) && !Check_nfs4_seqid(lock_owner, arg_open_owner->lock_seqid, op, obj, resp, lock_tag)) { LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG, "LOCK failed to create new lock owner, re-use", obj, open_owner, &lock_desc); dump_all_locks( "All locks (re-use of lock owner)"); PTHREAD_MUTEX_unlock(&lock_owner->so_mutex); /* Response is all setup for us and * LogDebug told what was wrong */ goto out2; } PTHREAD_MUTEX_unlock(&lock_owner->so_mutex); /* Lock owner is known, see if we also already have * a stateid. Do this here since it's impossible for * there to be such a state if the lock owner was * previously unknown. * This handles 4.0 replay locks with an open stateid * and new_lock_owner == true. It also handles * situations where all locks have been released and * the client is claiming a new lock owner to get a * new stateid, we will attempt to recycle. */ PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); state_lock_held = true; lock_state = nfs4_State_Get_Obj(obj, lock_owner); } else { /* Take the state_lock now */ PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); state_lock_held = true; } if (lock_state == NULL) { /* Prepare state management structure */ memset(&candidate_data, 0, sizeof(candidate_data)); candidate_data.lock.openstate = state_open; /* Add the lock state to the lock table */ state_status = state_add_impl(obj, STATE_TYPE_LOCK, &candidate_data, lock_owner, &lock_state, data->minorversion > 0 ? &refer : NULL); if (state_status != STATE_SUCCESS) { res_LOCK4->status = NFS4ERR_RESOURCE; LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG, "LOCK failed to add new stateid", obj, lock_owner, &lock_desc); goto out2; } glist_init(&lock_state->state_data.lock.state_locklist); /* Add lock state to the list of lock states belonging to the open state */ glist_add_tail( &state_open->state_data.share.share_lockstates, &lock_state->state_data.lock.state_sharelist); } } else { /* Take the state_lock now */ PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); state_lock_held = true; } if (data->minorversion == 0) { op_ctx->clientid = &lock_owner->so_owner.so_nfs4_owner.so_clientid; } /* Now we have a lock owner and a stateid. Go ahead and push * lock into SAL (and FSAL). */ state_status = state_lock(obj, lock_owner, lock_state, blocking, NULL, /* No block data for now */ &lock_desc, &conflict_owner, &conflict_desc); if (state_status != STATE_SUCCESS) { if (state_status == STATE_LOCK_CONFLICT) { /* A conflicting lock from a different lock_owner, returns NFS4ERR_DENIED */ Process_nfs4_conflict(&res_LOCK4->LOCK4res_u.denied, conflict_owner, &conflict_desc); } LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed with status %s", state_err_str(state_status)); res_LOCK4->status = nfs4_Errno_state(state_status); /* Save the response in the lock or open owner */ if (res_LOCK4->status != NFS4ERR_RESOURCE && res_LOCK4->status != NFS4ERR_BAD_STATEID && data->minorversion == 0) { Copy_nfs4_state_req(resp_owner, seqid, op, obj, resp, lock_tag); } if (arg_LOCK4->locker.new_lock_owner) { /* Need to destroy new state */ state_del_locked(lock_state); } goto out2; } if (data->minorversion == 0) op_ctx->clientid = NULL; res_LOCK4->status = NFS4_OK; /* Handle stateid/seqid for success */ update_stateid(lock_state, &res_LOCK4->LOCK4res_u.resok4.lock_stateid, data, lock_tag); if (arg_LOCK4->locker.new_lock_owner) { /* Also save the response in the lock owner */ Copy_nfs4_state_req(lock_owner, arg_open_owner->lock_seqid, op, obj, resp, lock_tag); } if (isFullDebug(COMPONENT_NFS_V4_LOCK)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_stateid(&dspbuf, lock_state); LogFullDebug(COMPONENT_NFS_V4_LOCK, "LOCK stateid %s", str); } LogLock(COMPONENT_NFS_V4_LOCK, NIV_FULL_DEBUG, "LOCK applied", obj, lock_owner, &lock_desc); out: if (data->minorversion == 0) { /* Save the response in the lock or open owner */ Copy_nfs4_state_req(resp_owner, seqid, op, obj, resp, lock_tag); } out2: if (state_lock_held) { /* Now release the state_lock */ PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); } if (state_open != NULL) dec_state_t_ref(state_open); if (lock_state != NULL) dec_state_t_ref(lock_state); LogStateOwner("Open: ", open_owner); LogStateOwner("Lock: ", lock_owner); if (open_owner != NULL) dec_state_owner_ref(open_owner); if (lock_owner != NULL) dec_state_owner_ref(lock_owner); if (clientid != NULL) dec_client_id_ref(clientid); return res_LOCK4->status; } /* nfs4_op_lock */ /** * @brief Free memory allocated for LOCK result * * This function frees any memory allocated for the result of the * NFS4_OP_LOCK operation. * * @param[in,out] resp nfs4_op results * */ void nfs4_op_lock_Free(nfs_resop4 *res) { LOCK4res *resp = &res->nfs_resop4_u.oplock; if (resp->status == NFS4ERR_DENIED) Release_nfs4_denied(&resp->LOCK4res_u.denied); } /* nfs4_op_lock_Free */ void nfs4_op_lock_CopyRes(LOCK4res *res_dst, LOCK4res *res_src) { if (res_src->status == NFS4ERR_DENIED) Copy_nfs4_denied(&res_dst->LOCK4res_u.denied, &res_src->LOCK4res_u.denied); } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_lockt.c000066400000000000000000000155701324272410200220270ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_lockt.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. */ #include "config.h" #include #include #include #include "hashtable.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "sal_functions.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" /** * * @brief The NFS4_OP_LOCKT operation * * This function implements the NFS4_OP_LOCKT operation. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 368 * * @see nfs4_Compound */ int nfs4_op_lockt(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { /* Alias for arguments */ LOCKT4args * const arg_LOCKT4 = &op->nfs_argop4_u.oplockt; /* Alias for response */ LOCKT4res * const res_LOCKT4 = &resp->nfs_resop4_u.oplockt; /* Return code from state calls */ state_status_t state_status = STATE_SUCCESS; /* Client id record */ nfs_client_id_t *clientid = NULL; /* Lock owner name */ state_nfs4_owner_name_t owner_name; /* Lock owner record */ state_owner_t *lock_owner = NULL; /* Owner of conflicting lock */ state_owner_t *conflict_owner = NULL; /* Description of lock to test */ fsal_lock_param_t lock_desc = { FSAL_POSIX_LOCK, FSAL_NO_LOCK, 0, 0, false }; /* Description of conflicting lock */ fsal_lock_param_t conflict_desc; /* return code from id confirm calls */ int rc; /* stateid if available matching owner and entry */ state_t *state; LogDebug(COMPONENT_NFS_V4_LOCK, "Entering NFS v4 LOCKT handler ----------------------------"); /* Initialize to sane default */ resp->resop = NFS4_OP_LOCKT; res_LOCKT4->status = nfs4_sanity_check_FH(data, REGULAR_FILE, false); if (res_LOCKT4->status != NFS4_OK) return res_LOCKT4->status; /* Lock length should not be 0 */ if (arg_LOCKT4->length == 0LL) { res_LOCKT4->status = NFS4ERR_INVAL; return res_LOCKT4->status; } if (nfs_in_grace()) { res_LOCKT4->status = NFS4ERR_GRACE; return res_LOCKT4->status; } /* Convert lock parameters to internal types */ switch (arg_LOCKT4->locktype) { case READ_LT: case READW_LT: lock_desc.lock_type = FSAL_LOCK_R; break; case WRITE_LT: case WRITEW_LT: lock_desc.lock_type = FSAL_LOCK_W; break; default: LogDebug(COMPONENT_NFS_V4_LOCK, "Invalid lock type"); res_LOCKT4->status = NFS4ERR_INVAL; return res_LOCKT4->status; } lock_desc.lock_start = arg_LOCKT4->offset; if (arg_LOCKT4->length != STATE_LOCK_OFFSET_EOF) lock_desc.lock_length = arg_LOCKT4->length; else lock_desc.lock_length = 0; /* Check for range overflow. Comparing beyond 2^64 is not * possible in 64 bit precision, but off+len > 2^64-1 is * equivalent to len > 2^64-1 - off */ if (lock_desc.lock_length > (STATE_LOCK_OFFSET_EOF - lock_desc.lock_start)) { res_LOCKT4->status = NFS4ERR_INVAL; return res_LOCKT4->status; } /* Check clientid */ rc = nfs_client_id_get_confirmed(data->minorversion == 0 ? arg_LOCKT4->owner.clientid : data->session->clientid, &clientid); if (rc != CLIENT_ID_SUCCESS) { res_LOCKT4->status = clientid_error_to_nfsstat(rc); return res_LOCKT4->status; } PTHREAD_MUTEX_lock(&clientid->cid_mutex); if (data->minorversion == 0 && !reserve_lease(clientid)) { PTHREAD_MUTEX_unlock(&clientid->cid_mutex); dec_client_id_ref(clientid); res_LOCKT4->status = NFS4ERR_EXPIRED; return res_LOCKT4->status; } PTHREAD_MUTEX_unlock(&clientid->cid_mutex); /* Is this lock_owner known ? */ convert_nfs4_lock_owner(&arg_LOCKT4->owner, &owner_name); /* This lock owner is not known yet, allocated and set up a new one */ lock_owner = create_nfs4_owner(&owner_name, clientid, STATE_LOCK_OWNER_NFSV4, NULL, 0, NULL, CARE_ALWAYS, true); LogStateOwner("Lock: ", lock_owner); if (lock_owner == NULL) { LogEvent(COMPONENT_NFS_V4_LOCK, "LOCKT unable to create lock owner"); res_LOCKT4->status = NFS4ERR_SERVERFAULT; goto out; } LogLock(COMPONENT_NFS_V4_LOCK, NIV_FULL_DEBUG, "LOCKT", data->current_obj, lock_owner, &lock_desc); if (data->minorversion == 0) { op_ctx->clientid = &lock_owner->so_owner.so_nfs4_owner.so_clientid; } /* Get the stateid, if any, related to this entry and owner */ state = nfs4_State_Get_Obj(data->current_obj, lock_owner); /* Now we have a lock owner and a stateid. Go ahead and test * the lock in SAL (and FSAL). */ state_status = state_test(data->current_obj, state, lock_owner, &lock_desc, &conflict_owner, &conflict_desc); if (state_status == STATE_LOCK_CONFLICT) { /* A conflicting lock from a different lock_owner, * returns NFS4ERR_DENIED */ LogStateOwner("Conflict: ", conflict_owner); Process_nfs4_conflict(&res_LOCKT4->LOCKT4res_u.denied, conflict_owner, &conflict_desc); } if (data->minorversion == 0) op_ctx->clientid = NULL; /* Release NFS4 Open Owner reference */ dec_state_owner_ref(lock_owner); /* Release stateid reference */ if (state != NULL) dec_state_t_ref(state); /* Return result */ res_LOCKT4->status = nfs4_Errno_state(state_status); out: /* Update the lease before exit */ if (data->minorversion == 0) { PTHREAD_MUTEX_lock(&clientid->cid_mutex); update_lease(clientid); PTHREAD_MUTEX_unlock(&clientid->cid_mutex); } dec_client_id_ref(clientid); return res_LOCKT4->status; } /* nfs4_op_lockt */ /** * @brief Free memory allocated for LOCKT result * * This function frees any memory allocated for the result of the * NFS4_OP_LOCKT operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_lockt_Free(nfs_resop4 *res) { LOCKT4res *resp = &res->nfs_resop4_u.oplockt; if (resp->status == NFS4ERR_DENIED) Release_nfs4_denied(&resp->LOCKT4res_u.denied); } /* nfs4_op_lockt_Free */ nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_locku.c000066400000000000000000000141541324272410200220250ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_locku.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. * * */ #include "config.h" #include #include #include #include "hashtable.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "sal_functions.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" static const char *locku_tag = "LOCKU"; /** * * @brief The NFS4_OP_LOCKU operation * * This function implements the NFS4_OP_LOCKU operation. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 368 * * @see nfs4_Compound */ int nfs4_op_locku(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { /* Alias for arguments */ LOCKU4args * const arg_LOCKU4 = &op->nfs_argop4_u.oplocku; /* Alias for response */ LOCKU4res * const res_LOCKU4 = &resp->nfs_resop4_u.oplocku; /* Return for state functions */ state_status_t state_status = STATE_SUCCESS; /* Found lock state */ state_t *state_found = NULL; /* Owner of lock state */ state_owner_t *lock_owner = NULL; /* Descritpion of lock to free */ fsal_lock_param_t lock_desc; /* */ nfsstat4 nfs_status = NFS4_OK; LogDebug(COMPONENT_NFS_V4_LOCK, "Entering NFS v4 LOCKU handler ----------------------------"); /* Initialize to sane default */ resp->resop = NFS4_OP_LOCKU; res_LOCKU4->status = NFS4_OK; res_LOCKU4->status = nfs4_sanity_check_FH(data, REGULAR_FILE, false); if (res_LOCKU4->status != NFS4_OK) return res_LOCKU4->status; /* Convert lock parameters to internal types */ switch (arg_LOCKU4->locktype) { case READ_LT: case READW_LT: lock_desc.lock_type = FSAL_LOCK_R; break; case WRITE_LT: case WRITEW_LT: lock_desc.lock_type = FSAL_LOCK_W; break; default: LogDebug(COMPONENT_NFS_V4_LOCK, "Invalid lock type"); res_LOCKU4->status = NFS4ERR_INVAL; return res_LOCKU4->status; } lock_desc.lock_start = arg_LOCKU4->offset; lock_desc.lock_sle_type = FSAL_POSIX_LOCK; lock_desc.lock_reclaim = false; if (arg_LOCKU4->length != STATE_LOCK_OFFSET_EOF) lock_desc.lock_length = arg_LOCKU4->length; else lock_desc.lock_length = 0; /* Check stateid correctness and get pointer to state */ nfs_status = nfs4_Check_Stateid(&arg_LOCKU4->lock_stateid, data->current_obj, &state_found, data, STATEID_SPECIAL_FOR_LOCK, arg_LOCKU4->seqid, data->minorversion == 0, locku_tag); if (nfs_status != NFS4_OK && nfs_status != NFS4ERR_REPLAY) { res_LOCKU4->status = nfs_status; return res_LOCKU4->status; } lock_owner = get_state_owner_ref(state_found); if (lock_owner == NULL) { /* State is going stale. */ res_LOCKU4->status = NFS4ERR_STALE; LogDebug(COMPONENT_NFS_V4_LOCK, "UNLOCK failed nfs4_Check_Stateid, stale lock owner"); goto out3; } /* Check seqid (lock_seqid or open_seqid) */ if (data->minorversion == 0) { if (!Check_nfs4_seqid(lock_owner, arg_LOCKU4->seqid, op, data->current_obj, resp, locku_tag)) { /* Response is all setup for us and LogDebug * told what was wrong */ goto out2; } } /* Lock length should not be 0 */ if (arg_LOCKU4->length == 0LL) { res_LOCKU4->status = NFS4ERR_INVAL; goto out; } /* Check for range overflow Remember that a length with all bits set to 1 means "lock until the end of file" (RFC3530, page 157) */ if (lock_desc.lock_length > (STATE_LOCK_OFFSET_EOF - lock_desc.lock_start)) { res_LOCKU4->status = NFS4ERR_INVAL; goto out; } LogLock(COMPONENT_NFS_V4_LOCK, NIV_FULL_DEBUG, locku_tag, data->current_obj, lock_owner, &lock_desc); if (data->minorversion == 0) { op_ctx->clientid = &lock_owner->so_owner.so_nfs4_owner.so_clientid; } /* Now we have a lock owner and a stateid. Go ahead and push unlock into SAL (and FSAL). */ state_status = state_unlock(data->current_obj, state_found, lock_owner, false, 0, &lock_desc); if (state_status != STATE_SUCCESS) { res_LOCKU4->status = nfs4_Errno_state(state_status); goto out; } if (data->minorversion == 0) op_ctx->clientid = NULL; /* Successful exit */ res_LOCKU4->status = NFS4_OK; /* Handle stateid/seqid for success */ update_stateid(state_found, &res_LOCKU4->LOCKU4res_u.lock_stateid, data, locku_tag); out: if (data->minorversion == 0) { /* Save the response in the lock owner */ Copy_nfs4_state_req(lock_owner, arg_LOCKU4->seqid, op, data->current_obj, resp, locku_tag); } out2: dec_state_owner_ref(lock_owner); out3: dec_state_t_ref(state_found); return res_LOCKU4->status; } /* nfs4_op_locku */ /** * @brief Free memory allocated for LOCKU result * * This function frees any memory allocated for the result of the * NFS4_OP_LOCKU operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_locku_Free(nfs_resop4 *resp) { /* Nothing to be done */ } void nfs4_op_locku_CopyRes(LOCKU4res *res_dst, LOCKU4res *res_src) { /* Nothing to deep copy */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_lookup.c000066400000000000000000000157761324272410200222340ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_lookup.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. * * */ #include "config.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_creds.h" #include "nfs_proto_tools.h" #include "nfs_convert.h" #include "export_mgr.h" /** * @brief NFS4_OP_LOOKUP * * This function implments the NFS4_OP_LOOKUP operation, which looks * a filename up in the FSAL. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, pp. 368-9 * */ int nfs4_op_lookup(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { /* Convenient alias for the arguments */ LOOKUP4args * const arg_LOOKUP4 = &op->nfs_argop4_u.oplookup; /* Convenient alias for the response */ LOOKUP4res * const res_LOOKUP4 = &resp->nfs_resop4_u.oplookup; /* The name to look up */ char *name = NULL; /* The directory in which to look up the name */ struct fsal_obj_handle *dir_obj = NULL; /* The name found */ struct fsal_obj_handle *file_obj = NULL; /* Status code from fsal */ fsal_status_t status = {0, 0}; resp->resop = NFS4_OP_LOOKUP; res_LOOKUP4->status = NFS4_OK; /* Do basic checks on a filehandle */ res_LOOKUP4->status = nfs4_sanity_check_FH(data, DIRECTORY, false); if (res_LOOKUP4->status != NFS4_OK) { /* for some reason lookup is picky. Just not being * dir is not enough. We want to know it is a symlink */ if (res_LOOKUP4->status == NFS4ERR_NOTDIR && data->current_filetype == SYMBOLIC_LINK) res_LOOKUP4->status = NFS4ERR_SYMLINK; goto out; } /* Validate and convert the UFT8 objname to a regular string */ res_LOOKUP4->status = nfs4_utf8string2dynamic(&arg_LOOKUP4->objname, UTF8_SCAN_ALL, &name); if (res_LOOKUP4->status != NFS4_OK) goto out; LogDebug(COMPONENT_NFS_V4, "name=%s", name); /* Do the lookup in the FSAL */ file_obj = NULL; dir_obj = data->current_obj; /* Sanity check: dir_obj should be ACTUALLY a directory */ status = fsal_lookup(dir_obj, name, &file_obj, NULL); if (FSAL_IS_ERROR(status)) { res_LOOKUP4->status = nfs4_Errno_status(status); goto out; } if (file_obj->type == DIRECTORY) { PTHREAD_RWLOCK_rdlock(&file_obj->state_hdl->state_lock); if (file_obj->state_hdl->dir.junction_export != NULL) { /* Handle junction */ struct fsal_obj_handle *obj = NULL; /* Attempt to get a reference to the export across the * junction. */ if (!export_ready( file_obj->state_hdl->dir.junction_export)) { /* If we could not get a reference, return * stale. Release state_lock */ LogDebug(COMPONENT_EXPORT, "NFS4ERR_STALE on LOOKUP of %s", name); res_LOOKUP4->status = NFS4ERR_STALE; PTHREAD_RWLOCK_unlock( &file_obj->state_hdl->state_lock); goto out; } get_gsh_export_ref( file_obj->state_hdl->dir.junction_export); /* Release any old export reference */ if (op_ctx->ctx_export != NULL) put_gsh_export(op_ctx->ctx_export); /* Stash the new export in the compound data. */ op_ctx->ctx_export = file_obj->state_hdl->dir.junction_export; op_ctx->fsal_export = op_ctx->ctx_export->fsal_export; PTHREAD_RWLOCK_unlock(&file_obj->state_hdl->state_lock); /* Build credentials */ res_LOOKUP4->status = nfs4_export_check_access(data->req); /* Test for access error (export should not be visible). */ if (res_LOOKUP4->status == NFS4ERR_ACCESS) { /* If return is NFS4ERR_ACCESS then this client * doesn't have access to this export, return * NFS4ERR_NOENT to hide it. It was not visible * in READDIR response. */ LogDebug(COMPONENT_EXPORT, "NFS4ERR_ACCESS Hiding Export_Id %d Pseudo %s with NFS4ERR_NOENT", op_ctx->ctx_export->export_id, op_ctx->ctx_export->pseudopath); res_LOOKUP4->status = NFS4ERR_NOENT; goto out; } if (res_LOOKUP4->status == NFS4ERR_WRONGSEC) { /* LogInfo already documents why */ goto out; } if (res_LOOKUP4->status != NFS4_OK) { /* Should never get here, * nfs4_export_check_access can only return * NFS4_OK, NFS4ERR_ACCESS or NFS4ERR_WRONGSEC. */ LogMajor(COMPONENT_EXPORT, "PSEUDO FS JUNCTION TRAVERSAL: Failed with %s for %s, id=%d", nfsstat4_to_str(res_LOOKUP4->status), op_ctx->ctx_export->pseudopath, op_ctx->ctx_export->export_id); goto out; } status = nfs_export_get_root_entry(op_ctx->ctx_export, &obj); if (FSAL_IS_ERROR(status)) { LogMajor(COMPONENT_EXPORT, "PSEUDO FS JUNCTION TRAVERSAL: Failed to get root for %s, id=%d, status = %s", op_ctx->ctx_export->pseudopath, op_ctx->ctx_export->export_id, msg_fsal_err(status.major)); res_LOOKUP4->status = nfs4_Errno_status(status); goto out; } LogDebug(COMPONENT_EXPORT, "PSEUDO FS JUNCTION TRAVERSAL: Crossed to %s, id=%d for name=%s", op_ctx->ctx_export->pseudopath, op_ctx->ctx_export->export_id, name); file_obj->obj_ops.put_ref(file_obj); file_obj = obj; } else { PTHREAD_RWLOCK_unlock(&file_obj->state_hdl->state_lock); } } /* Convert it to a file handle */ if (!nfs4_FSALToFhandle(false, &data->currentFH, file_obj, op_ctx->ctx_export)) { res_LOOKUP4->status = NFS4ERR_SERVERFAULT; goto out; } /* Keep the pointer within the compound data */ set_current_entry(data, file_obj); /* Put our ref */ file_obj->obj_ops.put_ref(file_obj); file_obj = NULL; /* Return successfully */ res_LOOKUP4->status = NFS4_OK; out: /* Release reference on file_obj if we didn't utilze it. */ if (file_obj) file_obj->obj_ops.put_ref(file_obj); gsh_free(name); return res_LOOKUP4->status; } /* nfs4_op_lookup */ /** * @brief Free memory allocated for LOOKUP result * * This function frees any memory allocated for the result of the * NFS4_OP_LOOKUP operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_lookup_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_lookupp.c000066400000000000000000000163561324272410200224070ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_lookupp.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. * * */ #include "config.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_creds.h" #include "nfs_proto_tools.h" #include "nfs_file_handle.h" #include "nfs_convert.h" #include "export_mgr.h" /** * @brief NFS4_OP_LOOKUPP * * This function implements the NFS4_OP_LOOKUPP operation, which looks * up the parent of the supplied directory. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 369 * */ int nfs4_op_lookupp(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { LOOKUPP4res * const res_LOOKUPP4 = &resp->nfs_resop4_u.oplookupp; struct fsal_obj_handle *dir_obj = NULL; struct fsal_obj_handle *file_obj; struct fsal_obj_handle *root_obj; fsal_status_t status; struct gsh_export *original_export = op_ctx->ctx_export; resp->resop = NFS4_OP_LOOKUPP; res_LOOKUPP4->status = NFS4_OK; /* Do basic checks on a filehandle */ res_LOOKUPP4->status = nfs4_sanity_check_FH(data, DIRECTORY, false); if (res_LOOKUPP4->status != NFS4_OK) return res_LOOKUPP4->status; /* Preparing for cache_inode_lookup ".." */ file_obj = NULL; dir_obj = data->current_obj; /* If Filehandle points to the root of the current export, then backup * through junction into the containing export. */ if (data->current_obj->type != DIRECTORY) goto not_junction; PTHREAD_RWLOCK_rdlock(&original_export->lock); status = nfs_export_get_root_entry(original_export, &root_obj); if (FSAL_IS_ERROR(status)) { res_LOOKUPP4->status = nfs4_Errno_status(status); PTHREAD_RWLOCK_unlock(&original_export->lock); return res_LOOKUPP4->status; } if (data->current_obj == root_obj) { struct gsh_export *parent_exp = NULL; /* Handle reverse junction */ LogDebug(COMPONENT_EXPORT, "Handling reverse junction from Export_Id %d Pseudo %s Parent=%p", original_export->export_id, original_export->pseudopath, original_export->exp_parent_exp); if (original_export->exp_parent_exp == NULL) { /* lookupp on the root on the pseudofs should return * NFS4ERR_NOENT (RFC3530, page 166) */ root_obj->obj_ops.put_ref(root_obj); PTHREAD_RWLOCK_unlock(&original_export->lock); res_LOOKUPP4->status = NFS4ERR_NOENT; return res_LOOKUPP4->status; } PTHREAD_RWLOCK_unlock(&original_export->lock); /* Clear out data->current entry outside lock * so if it cascades into cleanup, we aren't holding * an export lock that would cause trouble. */ set_current_entry(data, NULL); /* We need to protect accessing the parent information * with the export lock. We use the current export's lock * which is plenty, the parent can't go away without * grabbing the current export's lock to clean out the * parent information. */ PTHREAD_RWLOCK_rdlock(&original_export->lock); /* Get the junction inode into dir_obj and parent_exp * for reference. */ dir_obj = original_export->exp_junction_obj; parent_exp = original_export->exp_parent_exp; /* Check if there is a problem with the export and try and * get a reference to the parent export. */ if (dir_obj == NULL || parent_exp == NULL || !export_ready(parent_exp)) { /* Export is in the process of dying */ root_obj->obj_ops.put_ref(root_obj); PTHREAD_RWLOCK_unlock(&original_export->lock); LogCrit(COMPONENT_EXPORT, "Reverse junction from Export_Id %d Pseudo %s Parent=%p is stale", original_export->export_id, original_export->pseudopath, parent_exp); res_LOOKUPP4->status = NFS4ERR_STALE; return res_LOOKUPP4->status; } get_gsh_export_ref(parent_exp); dir_obj->obj_ops.get_ref(dir_obj); /* Set up dir_obj as current obj with an LRU reference * while still holding the lock. */ set_current_entry(data, dir_obj); /* Put our ref */ dir_obj->obj_ops.put_ref(dir_obj); /* Stash parent export in opctx while still holding the lock. */ op_ctx->ctx_export = parent_exp; op_ctx->fsal_export = op_ctx->ctx_export->fsal_export; /* Now we are safely transitioned to the parent export and can * release the lock. */ PTHREAD_RWLOCK_unlock(&original_export->lock); /* Release old export reference that was held by opctx. */ put_gsh_export(original_export); /* Build credentials */ res_LOOKUPP4->status = nfs4_export_check_access(data->req); /* Test for access error (export should not be visible). */ if (res_LOOKUPP4->status == NFS4ERR_ACCESS) { /* If return is NFS4ERR_ACCESS then this client doesn't * have access to this export, return NFS4ERR_NOENT to * hide it. It was not visible in READDIR response. */ root_obj->obj_ops.put_ref(root_obj); LogDebug(COMPONENT_EXPORT, "NFS4ERR_ACCESS Hiding Export_Id %d Pseudo %s with NFS4ERR_NOENT", parent_exp->export_id, parent_exp->pseudopath); res_LOOKUPP4->status = NFS4ERR_NOENT; return res_LOOKUPP4->status; } } else { /* Release the lock taken above */ PTHREAD_RWLOCK_unlock(&original_export->lock); } /* Return our ref from above */ root_obj->obj_ops.put_ref(root_obj); not_junction: status = fsal_lookupp(dir_obj, &file_obj, NULL); if (file_obj != NULL) { /* Convert it to a file handle */ if (!nfs4_FSALToFhandle(false, &data->currentFH, file_obj, op_ctx->ctx_export)) { res_LOOKUPP4->status = NFS4ERR_SERVERFAULT; file_obj->obj_ops.put_ref(file_obj); return res_LOOKUPP4->status; } /* Keep the pointer within the compound data */ set_current_entry(data, file_obj); /* Put our ref */ file_obj->obj_ops.put_ref(file_obj); /* Return successfully */ res_LOOKUPP4->status = NFS4_OK; } else { /* Unable to look up parent for some reason. * Return error. */ set_current_entry(data, NULL); res_LOOKUPP4->status = nfs4_Errno_status(status); } return res_LOOKUPP4->status; } /* nfs4_op_lookupp */ /** * @brief Free memory allocated for LOOKUPP result * * This function frees any memory allocated for the result of the * NFS4_OP_LOOKUPP operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_lookupp_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_nverify.c000066400000000000000000000070621324272410200223720ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * \file nfs4_op_nverify.c * \brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. * * */ #include "config.h" #include "log.h" #include "nfs4.h" #include "nfs_core.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" /** * * @brief Implemtation of NFS4_OP_NVERIFY * * This function implements the NFS4_OP_NVERIFY operation. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC 5661, p. 369 * */ int nfs4_op_nverify(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { NVERIFY4args * const arg_NVERIFY4 = &op->nfs_argop4_u.opnverify; NVERIFY4res * const res_NVERIFY4 = &resp->nfs_resop4_u.opnverify; fattr4 file_attr4; int rc = 0; struct attrlist attrs; resp->resop = NFS4_OP_NVERIFY; res_NVERIFY4->status = NFS4_OK; /* Do basic checks on a filehandle */ res_NVERIFY4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false); if (res_NVERIFY4->status != NFS4_OK) return res_NVERIFY4->status; /* Get only attributes that are allowed to be read */ if (!nfs4_Fattr_Check_Access(&arg_NVERIFY4->obj_attributes, FATTR4_ATTR_READ)) { res_NVERIFY4->status = NFS4ERR_INVAL; return res_NVERIFY4->status; } /* Ask only for supported attributes */ if (!nfs4_Fattr_Supported(&arg_NVERIFY4->obj_attributes)) { res_NVERIFY4->status = NFS4ERR_ATTRNOTSUPP; return res_NVERIFY4->status; } fsal_prepare_attrs(&attrs, 0); res_NVERIFY4->status = bitmap4_to_attrmask_t(&arg_NVERIFY4->obj_attributes.attrmask, &attrs.request_mask); if (res_NVERIFY4->status != NFS4_OK) return res_NVERIFY4->status; res_NVERIFY4->status = file_To_Fattr(data, attrs.request_mask, &attrs, &file_attr4, &arg_NVERIFY4->obj_attributes.attrmask); if (res_NVERIFY4->status != NFS4_OK) return res_NVERIFY4->status; /* Done with the attrs */ fsal_release_attrs(&attrs); rc = nfs4_Fattr_cmp(&arg_NVERIFY4->obj_attributes, &file_attr4); if (rc == false) { res_NVERIFY4->status = NFS4_OK; } else { if (rc == -1) res_NVERIFY4->status = NFS4ERR_INVAL; else res_NVERIFY4->status = NFS4ERR_SAME; } nfs4_Fattr_Free(&file_attr4); return res_NVERIFY4->status; } /* nfs4_op_nverify */ /** * @brief Free memory allocated for NVERIFY result * * This function frees any memory allocated for the result of the * NFS4_OP_NVERIFY operation. * * @param[in] resp nfs4_op results */ void nfs4_op_nverify_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_open.c000066400000000000000000001220511324272410200216450ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_open.c * @brief NFS4_OP_OPEN * * Function implementing the NFS4_OP_OPEN operation and support code. */ #include "config.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "sal_functions.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_convert.h" #include "fsal_convert.h" #include "nfs_creds.h" #include "export_mgr.h" #include "nfs_rpc_callback.h" static const char *open_tag = "OPEN"; /** * @brief Copy an OPEN result * * This function copies an open result to the supplied destination. * * @param[out] res_dst Buffer to which to copy the result * @param[in] res_src The result to copy */ void nfs4_op_open_CopyRes(OPEN4res *res_dst, OPEN4res *res_src) { res_dst->OPEN4res_u.resok4.attrset = res_src->OPEN4res_u.resok4.attrset; } /** * @brief Create an NFSv4 filehandle * * This function creates an NFSv4 filehandle from the supplied file * and sets it to be the current filehandle. * * @note This calls @ref set_current_entry which takes a ref; this then drops * it's ref. * * @param[in,out] data Compound's data * @param[in] obj File * * @retval NFS4_OK on success. * @retval Valid errors for NFS4_OP_OPEN. */ static nfsstat4 open4_create_fh(compound_data_t *data, struct fsal_obj_handle *obj, bool state_lock_held) { bool set_no_cleanup = false; /* Building a new fh */ if (!nfs4_FSALToFhandle(false, &data->currentFH, obj, op_ctx->ctx_export)) { obj->obj_ops.put_ref(obj); return NFS4ERR_SERVERFAULT; } /* Update the current entry */ set_current_entry(data, obj); if (state_lock_held) { /* Make sure we don't do cleanup holding the state_lock. * there will be an additional put_ref without the state_lock * being held. */ obj->state_hdl->no_cleanup = true; set_no_cleanup = true; } /* Put our ref */ obj->obj_ops.put_ref(obj); if (set_no_cleanup) { /* And clear the no_cleanup we set above. */ obj->state_hdl->no_cleanup = false; } return NFS4_OK; } /** * @brief Validate claim type * * Check that the claim type specified is allowed and return the * appropriate error code if not. * * @param[in] data Compound's data * @param[in] claim Claim type * * @retval NFS4_OK claim is valid. * @retval NFS4ERR_GRACE new open not allowed in grace period. * @retval NFS4ERR_NO_GRACE reclaim not allowed after grace period or * reclaim complete. * @retval NFS4ERR_NOTSUPP claim type not supported by minor version. * @retval NFS4ERR_INVAL unknown claim type. */ static nfsstat4 open4_validate_claim(compound_data_t *data, open_claim_type4 claim, nfs_client_id_t *clientid) { /* Return code */ nfsstat4 status = NFS4_OK; /* Indicate if we let FSAL to handle requests during grace. */ bool_t fsal_grace = false; /* Pick off erroneous claims so we don't have to deal with them later. */ switch (claim) { case CLAIM_NULL: if (nfs_in_grace() || ((data->minorversion > 0) && !clientid->cid_cb.v41.cid_reclaim_complete)) status = NFS4ERR_GRACE; break; case CLAIM_FH: if (data->minorversion == 0) status = NFS4ERR_NOTSUPP; if (op_ctx->fsal_export->exp_ops.fs_supports( op_ctx->fsal_export, fso_grace_method)) fsal_grace = true; if (!fsal_grace && nfs_in_grace()) status = NFS4ERR_GRACE; break; case CLAIM_DELEGATE_PREV: status = NFS4ERR_NOTSUPP; break; case CLAIM_PREVIOUS: if (!clientid->cid_allow_reclaim || !nfs_in_grace() || ((data->minorversion > 0) && clientid->cid_cb.v41.cid_reclaim_complete)) status = NFS4ERR_NO_GRACE; break; case CLAIM_DELEGATE_CUR: break; case CLAIM_DELEG_CUR_FH: case CLAIM_DELEG_PREV_FH: status = NFS4ERR_NOTSUPP; break; default: status = NFS4ERR_INVAL; } return status; } /** * @brief Validate and create an open owner * * This function finds or creates an owner to be associated with the * requested open state. * * @param[in] arg Arguments to OPEN4 operation * @param[in,out] data Compound's data * @param[out] res Response to OPEN4 operation * @param[in] clientid Clientid record for this request * @param[out] owner The found/created owner owner * * @return false if error or replay (res_OPEN4->status is already set), * true otherwise. */ bool open4_open_owner(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *res, nfs_client_id_t *clientid, state_owner_t **owner) { /* Shortcut to open args */ OPEN4args * const arg_OPEN4 = &(op->nfs_argop4_u.opopen); /* Shortcut to open args */ OPEN4res * const res_OPEN4 = &(res->nfs_resop4_u.opopen); /* The parsed-out name of the open owner */ state_nfs4_owner_name_t owner_name; /* Indicates if the owner is new */ bool_t isnew; /* Return value of FSAL operations */ fsal_status_t status = {0, 0}; struct fsal_obj_handle *obj_lookup = NULL; /* Is this open_owner known? If so, get it so we can use * replay cache */ convert_nfs4_open_owner(&arg_OPEN4->owner, &owner_name); /* If this open owner is not known yet, allocate and set up a new one */ *owner = create_nfs4_owner(&owner_name, clientid, STATE_OPEN_OWNER_NFSV4, NULL, 0, &isnew, CARE_ALWAYS, data->minorversion != 0); LogStateOwner("Open: ", *owner); if (*owner == NULL) { res_OPEN4->status = NFS4ERR_RESOURCE; LogEvent(COMPONENT_STATE, "NFS4 OPEN returning NFS4ERR_RESOURCE for CLAIM_NULL (could not create NFS4 Owner"); return false; } /* Seqid checking is only proper for reused NFSv4.0 owner */ if (isnew || (data->minorversion != 0)) return true; if (arg_OPEN4->seqid == 0) { LogDebug(COMPONENT_STATE, "Previously known open_owner is used with seqid=0, ask the client to confirm it again"); (*owner)->so_owner.so_nfs4_owner.so_confirmed = false; return true; } /* Check for replay */ if (Check_nfs4_seqid(*owner, arg_OPEN4->seqid, op, data->current_obj, res, open_tag)) { /* No replay */ return true; } /* Response is setup for us and LogDebug told what was * wrong. * * Or if this is a seqid replay, find the file entry * and update currentFH */ if (res_OPEN4->status == NFS4_OK) { utf8string *utfile; open_claim4 *oc = &arg_OPEN4->claim; char *filename; /* Load up CLAIM_DELEGATE_CUR file */ switch (oc->claim) { case CLAIM_NULL: utfile = &oc->open_claim4_u.file; break; case CLAIM_DELEGATE_CUR: utfile = &oc->open_claim4_u.delegate_cur_info.file; break; case CLAIM_DELEGATE_PREV: default: return false; } /* Check if filename is correct */ res_OPEN4->status = nfs4_utf8string2dynamic( utfile, UTF8_SCAN_ALL, &filename); if (res_OPEN4->status != NFS4_OK) return false; status = fsal_lookup(data->current_obj, filename, &obj_lookup, NULL); gsh_free(filename); if (obj_lookup == NULL) { res_OPEN4->status = nfs4_Errno_status(status); return false; } res_OPEN4->status = open4_create_fh(data, obj_lookup, false); } return false; } /** * @brief Check delegation claims while opening a file * * This function implements the CLAIM_DELEGATE_CUR claim. * * @param[in] arg OPEN4 arguments * @param[in,out] data Comopund's data */ static nfsstat4 open4_claim_deleg(OPEN4args *arg, compound_data_t *data) { open_claim_type4 claim = arg->claim.claim; stateid4 *rcurr_state; struct fsal_obj_handle *obj_lookup; const utf8string *utfname; char *filename; fsal_status_t fsal_status; nfsstat4 status; state_t *found_state = NULL; if (!(op_ctx->fsal_export->exp_ops.fs_supports( op_ctx->fsal_export, fso_delegations_w) || op_ctx->fsal_export->exp_ops.fs_supports( op_ctx->fsal_export, fso_delegations_r)) ) { LogDebug(COMPONENT_STATE, "NFS4 OPEN returning NFS4ERR_NOTSUPP for CLAIM_DELEGATE"); return NFS4ERR_NOTSUPP; } assert(claim == CLAIM_DELEGATE_CUR); utfname = &arg->claim.open_claim4_u.delegate_cur_info.file; rcurr_state = &arg->claim.open_claim4_u.delegate_cur_info.delegate_stateid; LogDebug(COMPONENT_NFS_V4, "file name: %.*s", utfname->utf8string_len, utfname->utf8string_val); /* Check if filename is correct */ status = nfs4_utf8string2dynamic(utfname, UTF8_SCAN_ALL, &filename); if (status != NFS4_OK) { LogDebug(COMPONENT_NFS_V4, "Invalid filename"); return status; } /* Does a file with this name already exist ? */ fsal_status = fsal_lookup(data->current_obj, filename, &obj_lookup, NULL); if (FSAL_IS_ERROR(fsal_status)) { LogDebug(COMPONENT_NFS_V4, "%s lookup failed.", filename); gsh_free(filename); return nfs4_Errno_status(fsal_status); } gsh_free(filename); status = open4_create_fh(data, obj_lookup, false); if (status != NFS4_OK) { LogDebug(COMPONENT_NFS_V4, "open4_create_fh failed"); return status; } found_state = nfs4_State_Get_Pointer(rcurr_state->other); if (found_state == NULL) { LogDebug(COMPONENT_NFS_V4, "state not found with CLAIM_DELEGATE_CUR"); return NFS4ERR_BAD_STATEID; } else { if (isFullDebug(COMPONENT_NFS_V4)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_stateid(&dspbuf, found_state); LogFullDebug(COMPONENT_NFS_V4, "found matching %s", str); } dec_state_t_ref(found_state); } LogFullDebug(COMPONENT_NFS_V4, "done with CLAIM_DELEGATE_CUR"); return NFS4_OK; } /** * @brief Create a new delegation state then get the delegation. * * Create a new delegation state for this client and file. * Then attempt to get a LEASE lock to delegate the file * according to whether the client opened READ or READ/WRITE. * * @note state_lock must be held for WRITE * * @param[in] data Compound data for this request * @param[in] op NFS arguments for the request * @param[in] open_state Open state for the inode to be delegated. * @param[in] openowner Open owner of the open state. * @param[in] client Client that will own the delegation. * @param[in/out] resok Delegation attempt result to be returned to client. * @param[in] prerecall flag for reclaims. */ static void get_delegation(compound_data_t *data, OPEN4args *args, state_t *open_state, state_owner_t *openowner, nfs_client_id_t *client, OPEN4resok *resok, bool prerecall) { state_status_t state_status; union state_data state_data; open_delegation_type4 deleg_type; state_owner_t *clientowner = &client->cid_owner; struct state_refer refer; state_t *new_state = NULL; struct state_hdl *ostate; open_write_delegation4 *writeres = &resok->delegation.open_delegation4_u.write; open_read_delegation4 *readres = &resok->delegation.open_delegation4_u.read; open_none_delegation4 *whynone = &resok->delegation.open_delegation4_u.od_whynone; ostate = data->current_obj->state_hdl; if (!ostate) { LogFullDebug(COMPONENT_NFS_V4_LOCK, "Could not get file state"); whynone->ond_why = WND4_RESOURCE; return; } /* Record the sequence info */ if (data->minorversion > 0) { memcpy(refer.session, data->session->session_id, sizeof(sessionid4)); refer.sequence = data->sequence; refer.slot = data->slot; } if (args->share_access & OPEN4_SHARE_ACCESS_WRITE) { deleg_type = OPEN_DELEGATE_WRITE; } else { assert(args->share_access & OPEN4_SHARE_ACCESS_READ); deleg_type = OPEN_DELEGATE_READ; } LogDebug(COMPONENT_STATE, "Attempting to grant %s delegation", deleg_type == OPEN_DELEGATE_WRITE ? "WRITE" : "READ"); init_new_deleg_state(&state_data, deleg_type, client); /* Add the delegation state */ state_status = state_add_impl(data->current_obj, STATE_TYPE_DELEG, &state_data, clientowner, &new_state, data->minorversion > 0 ? &refer : NULL); if (state_status != STATE_SUCCESS) { LogDebug(COMPONENT_NFS_V4_LOCK, "get delegation call failed to add state with status %s", state_err_str(state_status)); whynone->ond_why = WND4_RESOURCE; return; } else { new_state->state_seqid++; LogFullDebugOpaque(COMPONENT_STATE, "delegation state added, stateid: %s", 100, new_state->stateid_other, OTHERSIZE); /* acquire_lease_lock() gets the delegation from FSAL */ state_status = acquire_lease_lock(ostate, clientowner, new_state); if (state_status != STATE_SUCCESS) { LogDebug(COMPONENT_NFS_V4_LOCK, "get delegation call added state but failed to lock with status %s", state_err_str(state_status)); state_del_locked(new_state); dec_state_t_ref(new_state); if (state_status == STATE_LOCK_CONFLICT) whynone->ond_why = WND4_CONTENTION; else whynone->ond_why = WND4_RESOURCE; return; } else { nfs_space_limit4 *space_limit = &writeres->space_limit; resok->delegation.delegation_type = deleg_type; ostate->file.fdeleg_stats.fds_deleg_type = deleg_type; if (deleg_type == OPEN_DELEGATE_WRITE) { space_limit->limitby = NFS_LIMIT_SIZE; space_limit->nfs_space_limit4_u.filesize = DELEG_SPACE_LIMIT_FILESZ; COPY_STATEID(&writeres->stateid, new_state); writeres->recall = prerecall; get_deleg_perm(&writeres->permissions, deleg_type); } else { assert(deleg_type == OPEN_DELEGATE_READ); COPY_STATEID(&readres->stateid, new_state); readres->recall = prerecall; get_deleg_perm(&readres->permissions, deleg_type); } } } if (isDebug(COMPONENT_NFS_V4_LOCK)) { char str1[LOG_BUFF_LEN / 2] = "\0"; char str2[LOG_BUFF_LEN / 2] = "\0"; struct display_buffer dspbuf1 = {sizeof(str1), str1, str1}; struct display_buffer dspbuf2 = {sizeof(str2), str2, str2}; display_nfs4_owner(&dspbuf1, openowner); display_nfs4_owner(&dspbuf2, clientowner); LogDebug(COMPONENT_NFS_V4_LOCK, "get delegation openowner %s clientowner %s status %s", str1, str2, state_err_str(state_status)); } dec_state_t_ref(new_state); } static void do_delegation(OPEN4args *arg_OPEN4, OPEN4res *res_OPEN4, compound_data_t *data, state_owner_t *owner, state_t *open_state, nfs_client_id_t *clientid) { OPEN4resok *resok = &res_OPEN4->OPEN4res_u.resok4; bool prerecall; struct state_hdl *ostate; ostate = data->current_obj->state_hdl; if (!ostate) { LogFullDebug(COMPONENT_STATE, "Could not get file state"); return; } /* This will be updated later if we actually delegate */ if (clientid->cid_minorversion == 0) resok->delegation.delegation_type = OPEN_DELEGATE_NONE; else resok->delegation.delegation_type = OPEN_DELEGATE_NONE_EXT; /* Client doesn't want a delegation. */ if (arg_OPEN4->share_access & OPEN4_SHARE_ACCESS_WANT_NO_DELEG) { resok->delegation.open_delegation4_u.od_whynone.ond_why = WND4_NOT_WANTED; LogFullDebug(COMPONENT_STATE, "Client didn't want delegation."); return; } /* Check if delegations are supported */ if (!deleg_supported(data->current_obj, op_ctx->fsal_export, op_ctx->export_perms, arg_OPEN4->share_access)) { resok->delegation.open_delegation4_u.od_whynone.ond_why = WND4_NOT_SUPP_FTYPE; LogFullDebug(COMPONENT_STATE, "Delegation type not supported."); return; } /* Decide if we should delegate, then add it. */ if (can_we_grant_deleg(ostate, open_state) && should_we_grant_deleg(ostate, clientid, open_state, arg_OPEN4, resok, owner, &prerecall)) { /* Update delegation open stats */ if (ostate->file.fdeleg_stats.fds_num_opens == 0) ostate->file.fdeleg_stats.fds_first_open = time(NULL); ostate->file.fdeleg_stats.fds_num_opens++; LogDebug(COMPONENT_STATE, "Attempting to grant delegation"); get_delegation(data, arg_OPEN4, open_state, owner, clientid, resok, prerecall); } } /** * @brief NFS4_OP_OPEN create processing for use with extended FSAL API * * @param[in] arg Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] res_OPEN4 Results for nfs4_op * @param[in,out] verifier The verifier for exclusive create * @param[in,out] createmode The method of create * @param[in,out] sattr The attributes to set * */ static void open4_ex_create_args(OPEN4args *arg, compound_data_t *data, OPEN4res *res_OPEN4, void *verifier, enum fsal_create_mode *createmode, struct attrlist *sattr) { createhow4 *createhow = &arg->openhow.openflag4_u.how; fattr4 *arg_attrs = NULL; *createmode = nfs4_createmode_to_fsal(createhow->mode); if (createhow->mode == EXCLUSIVE4_1) { memcpy(verifier, createhow->createhow4_u.ch_createboth.cva_verf, sizeof(fsal_verifier_t)); } else if (createhow->mode == EXCLUSIVE4) { memcpy(verifier, createhow->createhow4_u.createverf, sizeof(fsal_verifier_t)); } /* Select the correct attributes */ if (createhow->mode == GUARDED4 || createhow->mode == UNCHECKED4) arg_attrs = &createhow->createhow4_u.createattrs; else if (createhow->mode == EXCLUSIVE4_1) arg_attrs = &createhow->createhow4_u.ch_createboth.cva_attrs; if (arg_attrs != NULL) { /* Check if asked attributes are correct */ if (!nfs4_Fattr_Supported(arg_attrs)) { res_OPEN4->status = NFS4ERR_ATTRNOTSUPP; return; } if (!nfs4_Fattr_Check_Access(arg_attrs, FATTR4_ATTR_WRITE)) { res_OPEN4->status = NFS4ERR_INVAL; return; } /* Convert the attributes */ if (arg_attrs->attrmask.bitmap4_len != 0) { /* Convert fattr4 so nfs4_sattr */ res_OPEN4->status = nfs4_Fattr_To_FSAL_attr(sattr, arg_attrs, data); if (res_OPEN4->status != NFS4_OK) return; } if (createhow->mode == EXCLUSIVE4_1) { /** @todo FSF: this needs to be corrected in case FSAL * uses different attributes for the * verifier. */ /* Check that we aren't trying to set the verifier * attributes. */ if (FSAL_TEST_MASK(sattr->valid_mask, ATTR_ATIME) || FSAL_TEST_MASK(sattr->valid_mask, ATTR_MTIME)) { res_OPEN4->status = NFS4ERR_INVAL; return; } } /* If owner or owner_group are set, and the credential was * squashed, then we must squash the set owner and owner_group. */ squash_setattr(sattr); } if (!(sattr->valid_mask & ATTR_MODE)) { /* Make sure mode is set, even for exclusive create. */ sattr->mode = 0600; sattr->valid_mask |= ATTR_MODE; } } /** * @brief NFS4_OP_OPEN processing using extended FSAL API * * This function impelments the NFS4_OP_OPEN operation, which * potentially creates and opens a regular file. * * @param[in] arg Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] res_OPEN4 Results for nfs4_op * @param[out] file_state state_t created for this operation * @param[out] new_state Indicates if the state_t is new * */ static void open4_ex(OPEN4args *arg, compound_data_t *data, OPEN4res *res_OPEN4, nfs_client_id_t *clientid, state_owner_t *owner, state_t **file_state, bool *new_state) { /* Parent directory in which to open the file. */ struct fsal_obj_handle *parent = NULL; /* The entry we associated with the desired file before open. */ struct fsal_obj_handle *file_obj = NULL; /* Indicator that file_obj came from lookup. */ bool looked_up_file_obj = false; /* The in_obj to pass to fsal_open2. */ struct fsal_obj_handle *in_obj = NULL; /* The entry open associated with the file. */ struct fsal_obj_handle *out_obj = NULL; fsal_openflags_t openflags = 0; fsal_openflags_t old_openflags = 0; enum fsal_create_mode createmode = FSAL_NO_CREATE; /* The filename to create */ char *filename = NULL; /* The supplied calim type */ open_claim_type4 claim = arg->claim.claim; fsal_verifier_t verifier; struct attrlist sattr; /* Status for fsal calls */ fsal_status_t status = {0, 0}; /* The open state for the file */ bool state_lock_held = false; /* Make sure the attributes are initialized */ memset(&sattr, 0, sizeof(sattr)); /* Make sure... */ *file_state = NULL; /* Pre-process the claim type */ switch (claim) { case CLAIM_NULL: /* Check parent */ parent = data->current_obj; in_obj = parent; /* Parent must be a directory */ if (parent->type != DIRECTORY) { if (parent->type == SYMBOLIC_LINK) { res_OPEN4->status = NFS4ERR_SYMLINK; goto out; } else { res_OPEN4->status = NFS4ERR_NOTDIR; goto out; } } /* Validate and convert the utf8 filename */ res_OPEN4->status = nfs4_utf8string2dynamic(&arg->claim.open_claim4_u.file, UTF8_SCAN_ALL, &filename); if (res_OPEN4->status != NFS4_OK) goto out; /* Set the createmode if appropriate) */ if (arg->openhow.opentype == OPEN4_CREATE) { open4_ex_create_args(arg, data, res_OPEN4, verifier, &createmode, &sattr); if (res_OPEN4->status != NFS4_OK) goto out; } status = fsal_lookup(parent, filename, &file_obj, NULL); if (!FSAL_IS_ERROR(status)) { /* Check create situations. */ if (arg->openhow.opentype == OPEN4_CREATE) { if (createmode >= FSAL_EXCLUSIVE) { /* Could be a replay, need to continue. */ LogFullDebug(COMPONENT_STATE, "EXCLUSIVE open with existing file %s", filename); } else if (createmode == FSAL_GUARDED) { /* This will be a failure no matter' * what. */ looked_up_file_obj = true; res_OPEN4->status = NFS4ERR_EXIST; goto out; } else { /* FSAL_UNCHECKED, may be a truncate * and we need to pass in the case * of fsal_reopen2 case. */ if (FSAL_TEST_MASK(sattr.valid_mask, ATTR_SIZE) && sattr.filesize == 0) { LogFullDebug(COMPONENT_STATE, "Truncate"); openflags |= FSAL_O_TRUNC; } } } /* We found the file by lookup, discard the filename * and remember that we found the entry by lookup. */ looked_up_file_obj = true; gsh_free(filename); filename = NULL; } else if (status.major != ERR_FSAL_NOENT || arg->openhow.opentype != OPEN4_CREATE) { /* A real error occurred */ res_OPEN4->status = nfs4_Errno_status(status); goto out; } break; /* Both of these just use the current filehandle. */ case CLAIM_PREVIOUS: owner->so_owner.so_nfs4_owner.so_confirmed = true; if (!nfs4_check_deleg_reclaim(clientid, &data->currentFH)) { /* It must have been revoked. Can't reclaim.*/ LogInfo(COMPONENT_NFS_V4, "Can't reclaim delegation"); res_OPEN4->status = NFS4ERR_RECLAIM_BAD; goto out; } openflags |= FSAL_O_RECLAIM; file_obj = data->current_obj; break; case CLAIM_FH: file_obj = data->current_obj; break; case CLAIM_DELEGATE_PREV: /* FIXME: Remove this when we have full support * for CLAIM_DELEGATE_PREV and delegpurge operations */ res_OPEN4->status = NFS4ERR_NOTSUPP; goto out; case CLAIM_DELEGATE_CUR: res_OPEN4->status = open4_claim_deleg(arg, data); if (res_OPEN4->status != NFS4_OK) goto out; openflags |= FSAL_O_RECLAIM; file_obj = data->current_obj; break; default: LogFatal(COMPONENT_STATE, "Programming error. Invalid claim after check."); } if ((arg->share_access & OPEN4_SHARE_ACCESS_READ) != 0) openflags |= FSAL_O_READ; if ((arg->share_access & OPEN4_SHARE_ACCESS_WRITE) != 0) openflags |= FSAL_O_WRITE; if ((arg->share_deny & OPEN4_SHARE_DENY_READ) != 0) openflags |= FSAL_O_DENY_READ; if ((arg->share_deny & OPEN4_SHARE_DENY_WRITE) != 0) openflags |= FSAL_O_DENY_WRITE_MAND; /* Check if file_obj a REGULAR_FILE */ if (file_obj != NULL && file_obj->type != REGULAR_FILE) { LogDebug(COMPONENT_NFS_V4, "Wrong file type expected REGULAR_FILE actual %s", object_file_type_to_str(file_obj->type)); if (file_obj->type == DIRECTORY) { res_OPEN4->status = NFS4ERR_ISDIR; } else { /* All special nodes must return NFS4ERR_SYMLINK for * proper client behavior per this linux-nfs post: * http://marc.info/?l=linux-nfs&m=131342421825436&w=2 */ res_OPEN4->status = NFS4ERR_SYMLINK; } goto out; } if (file_obj != NULL) { /* Go ahead and take the state lock now. */ PTHREAD_RWLOCK_wrlock(&file_obj->state_hdl->state_lock); state_lock_held = true; in_obj = file_obj; /* Check if any existing delegations conflict with this open. * Delegation recalls will be scheduled if there is a conflict. */ if (state_deleg_conflict(file_obj, (arg->share_access & OPEN4_SHARE_ACCESS_WRITE) != 0)) { res_OPEN4->status = NFS4ERR_DELAY; goto out; } /* Check if there is already a state for this entry and owner. */ *file_state = nfs4_State_Get_Obj(file_obj, owner); if (isFullDebug(COMPONENT_STATE) && *file_state != NULL) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_stateid(&dspbuf, *file_state); LogFullDebug(COMPONENT_STATE, "Found existing state %s", str); } /* Check if open from another export */ if (*file_state != NULL && !state_same_export(*file_state, op_ctx->ctx_export)) { LogEvent(COMPONENT_STATE, "Lock Owner Export Conflict, Lock held for export %" PRIu16" request for export %"PRIu16, state_export_id(*file_state), op_ctx->ctx_export->export_id); res_OPEN4->status = NFS4ERR_INVAL; goto out; } } /* If that did not succeed, allocate a state from the FSAL. */ if (*file_state == NULL) { *file_state = op_ctx->fsal_export->exp_ops.alloc_state( op_ctx->fsal_export, STATE_TYPE_SHARE, NULL); /* Remember we allocated a new state */ *new_state = true; /* We are ready to perform the open (with possible create). * in_obj has been set to the file itself or the parent. * filename is NULL if in_obj is the file itself. * * Permission check has been done on directory if appropriate, * otherwise fsal_open2 will do a directory permission * check. * * fsal_open2 handles the permission check on the file * itself and also handles all the share reservation stuff. * * fsal_open2 returns with a ref on out_obj, which should be * passed to the state. */ LogFullDebug(COMPONENT_STATE, "Calling open2 for %s", filename); status = fsal_open2(in_obj, *file_state, openflags, createmode, filename, &sattr, verifier, &out_obj, NULL); if (FSAL_IS_ERROR(status)) { res_OPEN4->status = nfs4_Errno_status(status); goto out; } } else if (createmode >= FSAL_EXCLUSIVE) { /* We have an EXCLUSIVE create with an existing * state. We still need to verify it, but no need * to call reopen2. */ LogFullDebug(COMPONENT_STATE, "Calling verify2 "); status = fsal_verify2(file_obj, verifier); if (FSAL_IS_ERROR(status)) { res_OPEN4->status = nfs4_Errno_status(status); goto out; } /* We need an extra reference below. */ file_obj->obj_ops.get_ref(file_obj); } else { old_openflags = file_obj->obj_ops.status2(file_obj, *file_state); /* Open upgrade */ LogFullDebug(COMPONENT_STATE, "Calling reopen2"); status = fsal_reopen2(file_obj, *file_state, openflags | old_openflags, false); if (FSAL_IS_ERROR(status)) { res_OPEN4->status = nfs4_Errno_status(status); goto out; } /* We need an extra reference below. */ file_obj->obj_ops.get_ref(file_obj); } if (file_obj == NULL) { /* We have a new cache inode entry, take the state lock. */ file_obj = out_obj; PTHREAD_RWLOCK_wrlock(&file_obj->state_hdl->state_lock); state_lock_held = true; } /* Now the state_lock is held for sure and we have an extra LRU * reference to file_obj, which is the opened file. */ if (*new_state) { /* The state data to be added */ union state_data candidate_data; /* Tracking data for the open state */ struct state_refer refer, *p_refer = NULL; state_status_t state_status; candidate_data.share.share_access = arg->share_access & OPEN4_SHARE_ACCESS_BOTH; candidate_data.share.share_deny = arg->share_deny; candidate_data.share.share_access_prev = (1 << candidate_data.share.share_access); candidate_data.share.share_deny_prev = (1 << candidate_data.share.share_deny); LogFullDebug(COMPONENT_STATE, "Creating new state access=%x deny=%x access_prev=%x deny_prev=%x", candidate_data.share.share_access, candidate_data.share.share_deny, candidate_data.share.share_access_prev, candidate_data.share.share_deny_prev); /* Record the sequence info */ if (data->minorversion > 0) { memcpy(refer.session, data->session->session_id, sizeof(sessionid4)); refer.sequence = data->sequence; refer.slot = data->slot; p_refer = &refer; } /* We need to register this state now. */ state_status = state_add_impl(file_obj, STATE_TYPE_SHARE, &candidate_data, owner, file_state, p_refer); if (state_status != STATE_SUCCESS) { /* state_add_impl failure closed and freed state. * file_state will also be NULL at this point. Also * release the ref on file_obj, since the state add * failed. */ file_obj->obj_ops.put_ref(file_obj); res_OPEN4->status = nfs4_Errno_state(state_status); *new_state = false; goto out; } glist_init(&(*file_state)->state_data.share.share_lockstates); } res_OPEN4->status = open4_create_fh(data, file_obj, true); if (res_OPEN4->status != NFS4_OK) { if (*new_state) { /* state_del_locked will close the file. */ state_del_locked(*file_state); *file_state = NULL; *new_state = false; } else { /*Do an open downgrade to the old open flags */ status = file_obj->obj_ops.reopen2(file_obj, *file_state, old_openflags); if (FSAL_IS_ERROR(status)) { LogCrit(COMPONENT_NFS_V4, "Failed to allocate handle, reopen2 failed with %s", fsal_err_txt(status)); } /* Need to release the state_lock before the put_ref * call. */ PTHREAD_RWLOCK_unlock(&file_obj->state_hdl->state_lock); state_lock_held = false; /* Release the extra LRU reference on file_obj. */ file_obj->obj_ops.put_ref(file_obj); goto out; } } /* Since open4_create_fh succeeded the LRU reference to file_obj was * consumed by data->current_obj. */ if (!(*new_state)) { LogFullDebug(COMPONENT_STATE, "Open upgrade old access=%x deny=%x access_prev=%x deny_prev=%x", (*file_state)->state_data.share.share_access, (*file_state)->state_data.share.share_deny, (*file_state)->state_data.share.share_access_prev, (*file_state)->state_data.share.share_deny_prev); LogFullDebug(COMPONENT_STATE, "Open upgrade to access=%x deny=%x", arg->share_access, arg->share_deny); /* Update share_access and share_deny */ (*file_state)->state_data.share.share_access |= arg->share_access & OPEN4_SHARE_ACCESS_BOTH; (*file_state)->state_data.share.share_deny |= arg->share_deny; /* Update share_access_prev and share_deny_prev */ (*file_state)->state_data.share.share_access_prev |= (1 << (arg->share_access & OPEN4_SHARE_ACCESS_BOTH)); (*file_state)->state_data.share.share_deny_prev |= (1 << arg->share_deny); LogFullDebug(COMPONENT_STATE, "Open upgrade new access=%x deny=%x access_prev=%x deny_prev=%x", (*file_state)->state_data.share.share_access, (*file_state)->state_data.share.share_deny, (*file_state)->state_data.share.share_access_prev, (*file_state)->state_data.share.share_deny_prev); } do_delegation(arg, res_OPEN4, data, owner, *file_state, clientid); out: /* Release the attributes (may release an inherited ACL) */ fsal_release_attrs(&sattr); if (state_lock_held) PTHREAD_RWLOCK_unlock(&file_obj->state_hdl->state_lock); if (filename) gsh_free(filename); if (res_OPEN4->status != NFS4_OK) { /* Cleanup state on error */ if (*new_state) (*file_state) ->state_exp->exp_ops.free_state( (*file_state)->state_exp, *file_state); else if (*file_state != NULL) dec_state_t_ref(*file_state); *file_state = NULL; } if (looked_up_file_obj) { /* We got file_obj via lookup, we need to unref it. */ file_obj->obj_ops.put_ref(file_obj); } } /** * @brief NFS4_OP_OPEN * * This function impelments the NFS4_OP_OPEN operation, which * potentially creates and opens a regular file. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, pp. 369-70 */ int nfs4_op_open(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { /* Shorter alias for OPEN4 arguments */ OPEN4args * const arg_OPEN4 = &(op->nfs_argop4_u.opopen); /* Shorter alias for OPEN4 response */ OPEN4res * const res_OPEN4 = &(resp->nfs_resop4_u.opopen); /* The handle from which the change_info4 is to be * generated. Every mention of change_info4 in RFC5661 * speaks of the parent directory of the file being opened. * However, with CLAIM_FH, CLAIM_DELEG_CUR_FH, and * CLAIM_DELEG_PREV_FH, there is no way to derive the parent * directory from the file handle. It is Unclear what the * correct behavior is. In our implementation, we take the * change_info4 of whatever filehandle is current when the * OPEN operation is invoked. */ struct fsal_obj_handle *obj_change = NULL; /* The found client record */ nfs_client_id_t *clientid = NULL; /* The found or created state owner for this open */ state_owner_t *owner = NULL; /* The supplied calim type */ open_claim_type4 claim = arg_OPEN4->claim.claim; /* The open state for the file */ state_t *file_state = NULL; /* True if the state was newly created */ bool new_state = false; int retval; LogDebug(COMPONENT_STATE, "Entering NFS v4 OPEN handler -----------------------------"); /* What kind of open is it ? */ LogFullDebug(COMPONENT_STATE, "OPEN: Claim type = %d, Open Type = %d, Share Deny = %d, Share Access = %d ", arg_OPEN4->claim.claim, arg_OPEN4->openhow.opentype, arg_OPEN4->share_deny, arg_OPEN4->share_access); resp->resop = NFS4_OP_OPEN; res_OPEN4->status = NFS4_OK; res_OPEN4->OPEN4res_u.resok4.rflags = 0; /* Check export permissions if OPEN4_CREATE */ if ((arg_OPEN4->openhow.opentype == OPEN4_CREATE) && ((op_ctx->export_perms->options & EXPORT_OPTION_MD_WRITE_ACCESS) == 0)) { res_OPEN4->status = NFS4ERR_ROFS; LogDebug(COMPONENT_NFS_V4, "Status of OP_OPEN due to export permissions = %s", nfsstat4_to_str(res_OPEN4->status)); return res_OPEN4->status; } /* Check export permissions if OPEN4_SHARE_ACCESS_WRITE */ if (((arg_OPEN4->share_access & OPEN4_SHARE_ACCESS_WRITE) != 0) && ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0)) { res_OPEN4->status = NFS4ERR_ROFS; LogDebug(COMPONENT_NFS_V4, "Status of OP_OPEN due to export permissions = %s", nfsstat4_to_str(res_OPEN4->status)); return res_OPEN4->status; } /* Do basic checks on a filehandle */ res_OPEN4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false); if (res_OPEN4->status != NFS4_OK) return res_OPEN4->status; if (data->current_obj == NULL) { /* This should be impossible, as PUTFH fills in the * current entry and previous checks weed out handles * in the PseudoFS and DS handles. */ res_OPEN4->status = NFS4ERR_SERVERFAULT; LogCrit(COMPONENT_NFS_V4, "Impossible condition in compound data at %s:%u.", __FILE__, __LINE__); goto out3; } /* It this a known client id? */ LogDebug(COMPONENT_STATE, "OPEN Client id = %" PRIx64, arg_OPEN4->owner.clientid); retval = nfs_client_id_get_confirmed( data->minorversion == 0 ? arg_OPEN4->owner.clientid : data->session->clientid, &clientid); if (retval != CLIENT_ID_SUCCESS) { res_OPEN4->status = clientid_error_to_nfsstat(retval); LogDebug(COMPONENT_NFS_V4, "nfs_client_id_get_confirmed failed"); return res_OPEN4->status; } /* Check if lease is expired and reserve it */ PTHREAD_MUTEX_lock(&clientid->cid_mutex); if (data->minorversion == 0 && !reserve_lease(clientid)) { PTHREAD_MUTEX_unlock(&clientid->cid_mutex); res_OPEN4->status = NFS4ERR_EXPIRED; LogDebug(COMPONENT_NFS_V4, "Lease expired"); goto out3; } PTHREAD_MUTEX_unlock(&clientid->cid_mutex); /* Get the open owner */ if (!open4_open_owner(op, data, resp, clientid, &owner)) { LogDebug(COMPONENT_NFS_V4, "open4_open_owner failed"); goto out2; } /* Do the claim check here, so we can save the result in the * owner for NFSv4.0. */ res_OPEN4->status = open4_validate_claim(data, claim, clientid); if (res_OPEN4->status != NFS4_OK) { LogDebug(COMPONENT_NFS_V4, "open4_validate_claim failed"); goto out; } /* After this point we know we have only CLAIM_NULL, * CLAIM_FH, or CLAIM_PREVIOUS, and that our grace period and * minor version are appropriate for the claim specified. */ if ((arg_OPEN4->openhow.opentype == OPEN4_CREATE) && (claim != CLAIM_NULL)) { res_OPEN4->status = NFS4ERR_INVAL; LogDebug(COMPONENT_NFS_V4, "OPEN4_CREATE but not CLAIM_NULL"); goto out2; } /* So we still have a reference even after we repalce the * current FH. */ obj_change = data->current_obj; obj_change->obj_ops.get_ref(obj_change); /* Update the change info for entry_change. */ res_OPEN4->OPEN4res_u.resok4.cinfo.before = fsal_get_changeid4(obj_change); /* Check if share_access does not have any access set, or has * invalid bits that are set. check that share_deny doesn't * have any invalid bits set. */ if (!(arg_OPEN4->share_access & OPEN4_SHARE_ACCESS_BOTH) || (data->minorversion == 0 && arg_OPEN4->share_access & ~OPEN4_SHARE_ACCESS_BOTH) || (arg_OPEN4->share_access & (~OPEN4_SHARE_ACCESS_WANT_DELEG_MASK & ~OPEN4_SHARE_ACCESS_BOTH)) || (arg_OPEN4->share_deny & ~OPEN4_SHARE_DENY_BOTH)) { res_OPEN4->status = NFS4ERR_INVAL; LogDebug(COMPONENT_NFS_V4, "Invalid SHARE_ACCESS or SHARE_DENY"); goto out; } /* Utilize the extended FSAL APU functionality to perform the open. */ open4_ex(arg_OPEN4, data, res_OPEN4, clientid, owner, &file_state, &new_state); if (res_OPEN4->status != NFS4_OK) goto out; memset(&res_OPEN4->OPEN4res_u.resok4.attrset, 0, sizeof(struct bitmap4)); if (arg_OPEN4->openhow.openflag4_u.how.mode == EXCLUSIVE4 || arg_OPEN4->openhow.openflag4_u.how.mode == EXCLUSIVE4_1) { struct bitmap4 *bits = &res_OPEN4->OPEN4res_u.resok4.attrset; set_attribute_in_bitmap(bits, FATTR4_TIME_ACCESS); set_attribute_in_bitmap(bits, FATTR4_TIME_MODIFY); } /* If server use OPEN_CONFIRM4, set the correct flag, * but not for 4.1 */ if (owner->so_owner.so_nfs4_owner.so_confirmed == false) res_OPEN4->OPEN4res_u.resok4.rflags |= OPEN4_RESULT_CONFIRM; res_OPEN4->OPEN4res_u.resok4.rflags |= OPEN4_RESULT_LOCKTYPE_POSIX; LogFullDebug(COMPONENT_STATE, "NFS4 OPEN returning NFS4_OK"); /* regular exit */ res_OPEN4->status = NFS4_OK; /* Update change_info4 */ res_OPEN4->OPEN4res_u.resok4.cinfo.after = fsal_get_changeid4(obj_change); res_OPEN4->OPEN4res_u.resok4.cinfo.atomic = FALSE; /* Handle open stateid/seqid for success */ update_stateid(file_state, &res_OPEN4->OPEN4res_u.resok4.stateid, data, open_tag); out: if (res_OPEN4->status != NFS4_OK) { LogDebug(COMPONENT_STATE, "failed with status %s", nfsstat4_to_str(res_OPEN4->status)); } /* Save the response in the open owner. * obj_change is either the parent directory or for a CLAIM_PREV is * the entry itself. In either case, it's the right entry to use in * saving the request results. */ if (data->minorversion == 0) { Copy_nfs4_state_req(owner, arg_OPEN4->seqid, op, obj_change, resp, open_tag); } out2: /* Update the lease before exit */ if (data->minorversion == 0) { PTHREAD_MUTEX_lock(&clientid->cid_mutex); update_lease(clientid); PTHREAD_MUTEX_unlock(&clientid->cid_mutex); } if (file_state != NULL) dec_state_t_ref(file_state); /* Clean up if we have an error exit */ if ((file_state != NULL) && new_state && (res_OPEN4->status != NFS4_OK)) { /* Need to destroy open owner and state */ state_del(file_state); } if (obj_change) obj_change->obj_ops.put_ref(obj_change); if (owner != NULL) { /* Need to release the open owner for this call */ dec_state_owner_ref(owner); } out3: dec_client_id_ref(clientid); return res_OPEN4->status; } /* nfs4_op_open */ /** * @brief Free memory allocated for OPEN result * * This function frees any memory allocated for the result of the * NFS4_OP_OPEN function. * * @param[in,out] resp nfs4_op results */ void nfs4_op_open_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_open_confirm.c000066400000000000000000000113171324272410200233640ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_open_confirm.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. */ #include "config.h" #include "log.h" #include "gsh_rpc.h" #include "nfs4.h" #include "nfs_core.h" #include "sal_functions.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" /** * @brief NFS4_OP_OPEN_CONFIRM * * This function implements the NFS4_OP_OPEN_CONFIRM operation. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @retval NFS4_OK or errors for NFSv4.0 * @retval NFS4ERR_NOTSUPP for NFSv4.1 * */ int nfs4_op_open_confirm(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { OPEN_CONFIRM4args * const arg_OPEN_CONFIRM4 = &op->nfs_argop4_u.opopen_confirm; OPEN_CONFIRM4res * const res_OPEN_CONFIRM4 = &resp->nfs_resop4_u.opopen_confirm; OPEN_CONFIRM4resok *resok = &res_OPEN_CONFIRM4->OPEN_CONFIRM4res_u.resok4; int rc = 0; state_t *state_found = NULL; state_owner_t *open_owner; const char *tag = "OPEN_CONFIRM"; resp->resop = NFS4_OP_OPEN_CONFIRM; res_OPEN_CONFIRM4->status = NFS4_OK; if (data->minorversion > 0) { res_OPEN_CONFIRM4->status = NFS4ERR_NOTSUPP; return res_OPEN_CONFIRM4->status; } /* Do basic checks on a filehandle * Should not operate on non-file objects */ res_OPEN_CONFIRM4->status = nfs4_sanity_check_FH(data, REGULAR_FILE, false); if (res_OPEN_CONFIRM4->status != NFS4_OK) return res_OPEN_CONFIRM4->status; /* Check stateid correctness and get pointer to state */ rc = nfs4_Check_Stateid(&arg_OPEN_CONFIRM4->open_stateid, data->current_obj, &state_found, data, STATEID_SPECIAL_FOR_LOCK, arg_OPEN_CONFIRM4->seqid, data->minorversion == 0, tag); if (rc != NFS4_OK && rc != NFS4ERR_REPLAY) { res_OPEN_CONFIRM4->status = rc; return res_OPEN_CONFIRM4->status; } open_owner = get_state_owner_ref(state_found); if (open_owner == NULL) { /* State is going stale. */ res_OPEN_CONFIRM4->status = NFS4ERR_STALE; LogDebug(COMPONENT_NFS_V4, "OPEN CONFIRM failed nfs4_Check_Stateid, stale open owner"); goto out2; } PTHREAD_MUTEX_lock(&open_owner->so_mutex); /* Check seqid */ if (!Check_nfs4_seqid(open_owner, arg_OPEN_CONFIRM4->seqid, op, data->current_obj, resp, tag)) { /* Response is all setup for us and LogDebug * told what was wrong */ PTHREAD_MUTEX_unlock(&open_owner->so_mutex); goto out; } /* If opened file is already confirmed, retrun NFS4ERR_BAD_STATEID */ if (open_owner->so_owner.so_nfs4_owner.so_confirmed) { PTHREAD_MUTEX_unlock(&open_owner->so_mutex); res_OPEN_CONFIRM4->status = NFS4ERR_BAD_STATEID; goto out; } /* Set the state as confirmed */ open_owner->so_owner.so_nfs4_owner.so_confirmed = true; PTHREAD_MUTEX_unlock(&open_owner->so_mutex); /* Handle stateid/seqid for success */ update_stateid(state_found, &resok->open_stateid, data, tag); /* Save the response in the open owner */ Copy_nfs4_state_req(open_owner, arg_OPEN_CONFIRM4->seqid, op, data->current_obj, resp, tag); out: dec_state_owner_ref(open_owner); out2: dec_state_t_ref(state_found); return res_OPEN_CONFIRM4->status; } /* nfs4_op_open_confirm */ /** * @brief Free memory allocated for OPEN_CONFIRM result * * Thisf unction frees any memory allocated for the result of the * NFS4_OP_OPEN_CONFIRM operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_open_confirm_Free(nfs_resop4 *resp) { /* Nothing to be done */ } void nfs4_op_open_confirm_CopyRes(OPEN_CONFIRM4res *resp_dst, OPEN_CONFIRM4res *resp_src) { /* Nothing to deep copy */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_open_downgrade.c000066400000000000000000000175701324272410200237100ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_open_downgrade.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. */ #include "config.h" #include "log.h" #include "gsh_rpc.h" #include "nfs4.h" #include "nfs_core.h" #include "sal_functions.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "fsal.h" /** * @brief NFS4_OP_OPEN_DOWNGRADE * * This function implements the NFS4_OP_OPEN_DOWNGRADE operation. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 370 * */ static nfsstat4 nfs4_do_open_downgrade(struct nfs_argop4 *op, compound_data_t *data, state_owner_t *owner, state_t *state, char **cause); int nfs4_op_open_downgrade(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { OPEN_DOWNGRADE4args * const arg_OPEN_DOWNGRADE4 = &op->nfs_argop4_u.opopen_downgrade; OPEN_DOWNGRADE4res * const res_OPEN_DOWNGRADE4 = &resp->nfs_resop4_u.opopen_downgrade; OPEN_DOWNGRADE4resok *resok = &res_OPEN_DOWNGRADE4->OPEN_DOWNGRADE4res_u.resok4; state_t *state_found = NULL; state_owner_t *open_owner; int rc; const char *tag = "OPEN_DOWNGRADE"; char *cause = ""; resp->resop = NFS4_OP_OPEN_DOWNGRADE; res_OPEN_DOWNGRADE4->status = NFS4_OK; /* Do basic checks on a filehandle */ res_OPEN_DOWNGRADE4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false); if (res_OPEN_DOWNGRADE4->status != NFS4_OK) return res_OPEN_DOWNGRADE4->status; /* Open downgrade is done only on a file */ if (data->current_filetype != REGULAR_FILE) { res_OPEN_DOWNGRADE4->status = NFS4ERR_INVAL; return res_OPEN_DOWNGRADE4->status; } /* Check stateid correctness and get pointer to state */ rc = nfs4_Check_Stateid(&arg_OPEN_DOWNGRADE4->open_stateid, data->current_obj, &state_found, data, STATEID_SPECIAL_FOR_LOCK, arg_OPEN_DOWNGRADE4->seqid, data->minorversion == 0, tag); if (rc != NFS4_OK && rc != NFS4ERR_REPLAY) { res_OPEN_DOWNGRADE4->status = rc; LogDebug(COMPONENT_STATE, "OPEN_DOWNGRADE failed nfs4_Check_Stateid"); return res_OPEN_DOWNGRADE4->status; } open_owner = get_state_owner_ref(state_found); if (open_owner == NULL) { /* Unexpected, but something just went stale. */ res_OPEN_DOWNGRADE4->status = NFS4ERR_STALE; goto out2; } PTHREAD_MUTEX_lock(&open_owner->so_mutex); /* Check seqid */ if (data->minorversion == 0 && !Check_nfs4_seqid(open_owner, arg_OPEN_DOWNGRADE4->seqid, op, data->current_obj, resp, tag)) { /* Response is all setup for us and LogDebug told what was wrong */ PTHREAD_MUTEX_unlock(&open_owner->so_mutex); goto out; } PTHREAD_MUTEX_unlock(&open_owner->so_mutex); /* What kind of open is it ? */ LogFullDebug(COMPONENT_STATE, "OPEN_DOWNGRADE: Share Deny = %d Share Access = %d ", arg_OPEN_DOWNGRADE4->share_deny, arg_OPEN_DOWNGRADE4->share_access); res_OPEN_DOWNGRADE4->status = nfs4_do_open_downgrade(op, data, open_owner, state_found, &cause); if (res_OPEN_DOWNGRADE4->status != NFS4_OK) { LogEvent(COMPONENT_STATE, "Failed to open downgrade: %s", cause); goto out; } /* Successful exit */ res_OPEN_DOWNGRADE4->status = NFS4_OK; /* Handle stateid/seqid for success */ update_stateid(state_found, &resok->open_stateid, data, tag); /* Save the response in the open owner */ if (data->minorversion == 0) { Copy_nfs4_state_req(open_owner, arg_OPEN_DOWNGRADE4->seqid, op, data->current_obj, resp, tag); } out: dec_state_owner_ref(open_owner); out2: dec_state_t_ref(state_found); return res_OPEN_DOWNGRADE4->status; } /* nfs4_op_opendowngrade */ /** * @brief Free memory allocated for OPEN_DOWNGRADE result * * This function frees any memory allocated for the result of the * NFS4_OP_OPEN_DOWNGRADE operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_open_downgrade_Free(nfs_resop4 *resp) { /* Nothing to be done */ } void nfs4_op_open_downgrade_CopyRes(OPEN_DOWNGRADE4res *res_dst, OPEN_DOWNGRADE4res *res_src) { /* Nothing to deep copy */ } static nfsstat4 nfs4_do_open_downgrade(struct nfs_argop4 *op, compound_data_t *data, state_owner_t *owner, state_t *state, char **cause) { state_status_t state_status; OPEN_DOWNGRADE4args *args = &op->nfs_argop4_u.opopen_downgrade; fsal_status_t fsal_status; fsal_openflags_t openflags = 0; LogFullDebug(COMPONENT_STATE, "Open downgrade current access=%x deny=%x access_prev=%x deny_prev=%x", state->state_data.share.share_access, state->state_data.share.share_deny, state->state_data.share.share_access_prev, state->state_data.share.share_deny_prev); LogFullDebug(COMPONENT_STATE, "Open downgrade to access=%x deny=%x", args->share_access, args->share_deny); PTHREAD_RWLOCK_wrlock(&data->current_obj->state_hdl->state_lock); /* Check if given share access is subset of current share access */ if ((state->state_data.share.share_access & args->share_access) != (args->share_access)) { /* Open share access is not a superset of * downgrade share access */ *cause = " (invalid share access for downgrade)"; PTHREAD_RWLOCK_unlock( &data->current_obj->state_hdl->state_lock); return NFS4ERR_INVAL; } /* Check if given share deny is subset of current share deny */ if ((state->state_data.share.share_deny & args->share_deny) != (args->share_deny)) { /* Open share deny is not a superset of * downgrade share deny */ *cause = " (invalid share deny for downgrade)"; PTHREAD_RWLOCK_unlock( &data->current_obj->state_hdl->state_lock); return NFS4ERR_INVAL; } /* Check if given share access is previously seen */ if (((state->state_data.share.share_access_prev & (1 << args->share_access)) == 0) || ((state->state_data.share.share_deny_prev & (1 << args->share_deny)) == 0)) { *cause = " (share access or deny never seen before)"; PTHREAD_RWLOCK_unlock( &data->current_obj->state_hdl->state_lock); return NFS4ERR_INVAL; } if ((args->share_access & OPEN4_SHARE_ACCESS_READ) != 0) openflags |= FSAL_O_READ; if ((args->share_access & OPEN4_SHARE_ACCESS_WRITE) != 0) openflags |= FSAL_O_WRITE; if ((args->share_deny & OPEN4_SHARE_DENY_READ) != 0) openflags |= FSAL_O_DENY_READ; if ((args->share_deny & OPEN4_SHARE_DENY_WRITE) != 0) openflags |= FSAL_O_DENY_WRITE_MAND; fsal_status = fsal_reopen2(data->current_obj, state, openflags, true); state_status = state_error_convert(fsal_status); PTHREAD_RWLOCK_unlock(&data->current_obj->state_hdl->state_lock); if (state_status != STATE_SUCCESS) { *cause = " (state_share_downgrade failed)"; return NFS4ERR_SERVERFAULT; } return NFS4_OK; } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_openattr.c000066400000000000000000000042341324272410200225420ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_openattr.c * @brief Routines used for managing the NFS4 COMPOUND functions. */ #include "config.h" #include "hashtable.h" #include "log.h" #include "nfs4.h" #include "nfs_core.h" #include "nfs_proto_functions.h" /** * * @brief NFS4_OP_OPENATTR * * This function implements the NFS4_OP_OPENATTRR operation. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, pp. 370-1 * */ int nfs4_op_openattr(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { OPENATTR4args * const arg_OPENATTR4 __attribute__ ((unused)) = &op->nfs_argop4_u.opopenattr; OPENATTR4res * const res_OPENATTR4 = &resp->nfs_resop4_u.opopenattr; resp->resop = NFS4_OP_OPENATTR; res_OPENATTR4->status = NFS4ERR_NOTSUPP; return res_OPENATTR4->status; } /* nfs4_op_openattr */ /** * @brief Free memory allocated for OPENATTR result * * This function frees any memory allocated for the result of the * NFS4_OP_OPENATTR operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_openattr_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_putfh.c000066400000000000000000000207001324272410200220300ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_putfh.c * @brief Routines used for managing the NFS4_OP_PUTFH operation. * * Routines used for managing the NFS4_OP_PUTFH operation. * */ #include "config.h" #include "hashtable.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_creds.h" #include "nfs_proto_functions.h" #include "nfs_convert.h" #include "export_mgr.h" #include "client_mgr.h" #include "fsal_convert.h" #include "nfs_file_handle.h" #include "pnfs_utils.h" static int nfs4_ds_putfh(compound_data_t *data) { struct file_handle_v4 *v4_handle = (struct file_handle_v4 *)data->currentFH.nfs_fh4_val; struct fsal_pnfs_ds *pds; struct gsh_buffdesc fh_desc; bool changed = true; LogFullDebug(COMPONENT_FILEHANDLE, "NFS4 Handle 0x%X export id %d", v4_handle->fhflags1, ntohs(v4_handle->id.exports)); /* Find any existing server by the "id" from the handle, * before releasing the old DS (to prevent thrashing). */ pds = pnfs_ds_get(ntohs(v4_handle->id.servers)); if (pds == NULL) { LogInfoAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "NFS4 Request from client (%s) has invalid server identifier %d", op_ctx->client ? op_ctx->client->hostaddr_str : "unknown", ntohs(v4_handle->id.servers)); return NFS4ERR_STALE; } /* If old CurrentFH had a related server, release reference. */ if (op_ctx->fsal_pnfs_ds != NULL) { changed = ntohs(v4_handle->id.servers) != op_ctx->fsal_pnfs_ds->id_servers; pnfs_ds_put(op_ctx->fsal_pnfs_ds); } /* If old CurrentFH had a related export, release reference. */ if (op_ctx->ctx_export != NULL) { changed = op_ctx->ctx_export != pds->mds_export; put_gsh_export(op_ctx->ctx_export); } if (pds->mds_export == NULL) { /* most likely */ op_ctx->ctx_export = NULL; op_ctx->fsal_export = NULL; } else if (pds->pnfs_ds_status == PNFS_DS_READY) { /* special case: avoid lookup of related export. * get_gsh_export_ref() was bumped in pnfs_ds_get() */ op_ctx->ctx_export = pds->mds_export; op_ctx->fsal_export = pds->mds_fsal_export; } else { /* export reference has been dropped. */ put_gsh_export(pds->mds_export); op_ctx->ctx_export = NULL; op_ctx->fsal_export = NULL; return NFS4ERR_STALE; } /* Clear out current entry for now */ set_current_entry(data, NULL); /* update _ctx fields */ op_ctx->fsal_pnfs_ds = pds; if (changed) { int status; /* permissions may have changed */ status = pds->s_ops.permissions(pds, data->req); if (status != NFS4_OK) return status; } fh_desc.len = v4_handle->fs_len; fh_desc.addr = &v4_handle->fsopaque; /* Leave the current_entry as NULL, but indicate a * regular file. */ data->current_filetype = REGULAR_FILE; return pds->s_ops.make_ds_handle(pds, &fh_desc, &data->current_ds, v4_handle->fhflags1); } static int nfs4_mds_putfh(compound_data_t *data) { struct file_handle_v4 *v4_handle = (struct file_handle_v4 *)data->currentFH.nfs_fh4_val; struct gsh_export *exporting; struct fsal_export *export; struct gsh_buffdesc fh_desc; struct fsal_obj_handle *new_hdl; fsal_status_t fsal_status = { 0, 0 }; bool changed = true; LogFullDebug(COMPONENT_FILEHANDLE, "NFS4 Handle flags 0x%X export id %d", v4_handle->fhflags1, ntohs(v4_handle->id.exports)); LogFullDebugOpaque(COMPONENT_FILEHANDLE, "NFS4 FSAL Handle %s", LEN_FH_STR, v4_handle->fsopaque, v4_handle->fs_len); /* Find any existing export by the "id" from the handle, * before releasing the old export (to prevent thrashing). */ exporting = get_gsh_export(ntohs(v4_handle->id.exports)); if (exporting == NULL) { LogInfoAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "NFS4 Request from client (%s) has invalid export identifier %d", op_ctx->client ? op_ctx->client->hostaddr_str : "unknown", ntohs(v4_handle->id.exports)); return NFS4ERR_STALE; } /* If old CurrentFH had a related export, release reference. */ if (op_ctx->ctx_export != NULL) { changed = ntohs(v4_handle->id.exports) != op_ctx->ctx_export->export_id; put_gsh_export(op_ctx->ctx_export); } /* If old CurrentFH had a related server, release reference. */ if (op_ctx->fsal_pnfs_ds != NULL) { pnfs_ds_put(op_ctx->fsal_pnfs_ds); op_ctx->fsal_pnfs_ds = NULL; } /* Clear out current entry for now */ set_current_entry(data, NULL); /* update _ctx fields needed by nfs4_export_check_access */ op_ctx->ctx_export = exporting; op_ctx->fsal_export = export = exporting->fsal_export; if (changed) { int status; status = nfs4_export_check_access(data->req); if (status != NFS4_OK) { LogFullDebug(COMPONENT_FILEHANDLE, "Export check access failed %s", nfsstat4_to_str(status)); return status; } } fh_desc.len = v4_handle->fs_len; fh_desc.addr = &v4_handle->fsopaque; /* adjust the handle opaque into a cache key */ fsal_status = export->exp_ops.wire_to_host(export, FSAL_DIGEST_NFSV4, &fh_desc, v4_handle->fhflags1); if (FSAL_IS_ERROR(fsal_status)) { LogFullDebug(COMPONENT_FILEHANDLE, "wire_to_host failed %s", msg_fsal_err(fsal_status.major)); return nfs4_Errno_status(fsal_status); } fsal_status = export->exp_ops.create_handle(export, &fh_desc, &new_hdl, NULL); if (FSAL_IS_ERROR(fsal_status)) { LogDebug(COMPONENT_FILEHANDLE, "could not get create_handle object error %s", msg_fsal_err(fsal_status.major)); return nfs4_Errno_status(fsal_status); } /* Set the current entry using the ref from get */ set_current_entry(data, new_hdl); /* Put our ref */ new_hdl->obj_ops.put_ref(new_hdl); LogFullDebug(COMPONENT_FILEHANDLE, "File handle is of type %s(%d)", object_file_type_to_str(data->current_filetype), data->current_filetype); return NFS4_OK; } /** * @brief The NFS4_OP_PUTFH operation * * Sets the current FH with the value given in argument. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 371 * * @see nfs4_Compound * */ int nfs4_op_putfh(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { /* Convenience alias for args */ PUTFH4args * const arg_PUTFH4 = &op->nfs_argop4_u.opputfh; /* Convenience alias for resopnse */ PUTFH4res * const res_PUTFH4 = &resp->nfs_resop4_u.opputfh; resp->resop = NFS4_OP_PUTFH; /* First check the handle. If it is rubbish, we go no further */ res_PUTFH4->status = nfs4_Is_Fh_Invalid(&arg_PUTFH4->object); if (res_PUTFH4->status != NFS4_OK) return res_PUTFH4->status; /* If no currentFH were set, allocate one */ if (data->currentFH.nfs_fh4_val == NULL) nfs4_AllocateFH(&data->currentFH); /* Copy the filehandle from the arg structure */ data->currentFH.nfs_fh4_len = arg_PUTFH4->object.nfs_fh4_len; memcpy(data->currentFH.nfs_fh4_val, arg_PUTFH4->object.nfs_fh4_val, arg_PUTFH4->object.nfs_fh4_len); /* The export and fsalid should be updated, but DS handles * don't support metadata operations. Thus, we can't call into * cache_inode to populate the metadata cache. */ if (nfs4_Is_Fh_DSHandle(&data->currentFH)) res_PUTFH4->status = nfs4_ds_putfh(data); else res_PUTFH4->status = nfs4_mds_putfh(data); return res_PUTFH4->status; } /* nfs4_op_putfh */ /** * @brief Free memory allocated for PUTFH result * * This function frees any memory allocated for the result of the * NFS4_OP_PUTFH operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_putfh_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_putpubfh.c000066400000000000000000000043741324272410200225500ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_putpubfh.c * @brief Routines used for managing the NFS4_OP_PUTPUBFH operation. * * Routines used for managing the NFS4_OP_PUTPUBFH operation. * */ #include "config.h" #include "log.h" #include "nfs4.h" #include "nfs_core.h" #include "nfs_proto_functions.h" /** * @brief The NFS4_OP_PUTFH operation * * This function sets the publicFH for the current compound requests * as the current FH. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 371 * * @see nfs4_Compound * */ int nfs4_op_putpubfh(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { PUTPUBFH4res * const res_PUTPUBFH4 = &resp->nfs_resop4_u.opputpubfh; /* PUTPUBFH really isn't used, just make PUTROOTFH do our work and * call it our own... */ res_PUTPUBFH4->status = nfs4_op_putrootfh(op, data, resp); resp->resop = NFS4_OP_PUTPUBFH; return res_PUTPUBFH4->status; } /** * @brief Free memory allocated for PUTPUBFH result * * This function frees the memory allocated for the result of the * NFS4_OP_PUTPUBFH operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_putpubfh_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_putrootfh.c000066400000000000000000000107031324272410200227360ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_putrootfh.c * @brief Routines used for managing the NFS4_OP_PUTROOTFH operation. * * Routines used for managing the NFS4_OP_PUTROOTFH operation. */ #include "config.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_convert.h" #include "nfs_exports.h" #include "nfs_file_handle.h" #include "export_mgr.h" #include "nfs_creds.h" /** * * @brief The NFS4_OP_PUTROOTFH operation. * * This functions handles the NFS4_OP_PUTROOTFH operation in * NFSv4. This function can be called only from nfs4_Compound. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 371 * * @see CreateROOTFH4 * */ int nfs4_op_putrootfh(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { fsal_status_t status = {0, 0}; struct fsal_obj_handle *file_obj; PUTROOTFH4res * const res_PUTROOTFH4 = &resp->nfs_resop4_u.opputrootfh; /* First of all, set the reply to zero to make sure * it contains no parasite information */ memset(resp, 0, sizeof(struct nfs_resop4)); resp->resop = NFS4_OP_PUTROOTFH; /* Clear out current entry for now */ set_current_entry(data, NULL); /* Release any old export reference */ if (op_ctx->ctx_export != NULL) put_gsh_export(op_ctx->ctx_export); op_ctx->ctx_export = NULL; op_ctx->fsal_export = NULL; /* Get the root export of the Pseudo FS */ op_ctx->ctx_export = get_gsh_export_by_pseudo("/", true); if (op_ctx->ctx_export == NULL) { LogCrit(COMPONENT_EXPORT, "Could not get export for Pseudo Root"); res_PUTROOTFH4->status = NFS4ERR_NOENT; return res_PUTROOTFH4->status; } op_ctx->fsal_export = op_ctx->ctx_export->fsal_export; /* Build credentials */ res_PUTROOTFH4->status = nfs4_export_check_access(data->req); /* Test for access error (export should not be visible). */ if (res_PUTROOTFH4->status == NFS4ERR_ACCESS) { /* Client has no access at all */ LogDebug(COMPONENT_EXPORT, "Client doesn't have access to Pseudo Root"); return res_PUTROOTFH4->status; } if (res_PUTROOTFH4->status != NFS4_OK) { LogMajor(COMPONENT_EXPORT, "Failed to get FSAL credentials Pseudo Root"); return res_PUTROOTFH4->status; } /* Get the Pesudo Root inode of the mounted on export */ status = nfs_export_get_root_entry(op_ctx->ctx_export, &file_obj); if (FSAL_IS_ERROR(status)) { LogCrit(COMPONENT_EXPORT, "Could not get root inode for Pseudo Root"); res_PUTROOTFH4->status = nfs4_Errno_status(status); return res_PUTROOTFH4->status; } LogMidDebug(COMPONENT_EXPORT, "Root node %p", data->current_obj); set_current_entry(data, file_obj); /* Put our ref */ file_obj->obj_ops.put_ref(file_obj); /* Convert it to a file handle */ if (!nfs4_FSALToFhandle(data->currentFH.nfs_fh4_val == NULL, &data->currentFH, data->current_obj, op_ctx->ctx_export)) { LogCrit(COMPONENT_EXPORT, "Could not get handle for Pseudo Root"); res_PUTROOTFH4->status = NFS4ERR_SERVERFAULT; return res_PUTROOTFH4->status; } LogHandleNFS4("NFS4 PUTROOTFH CURRENT FH: ", &data->currentFH); res_PUTROOTFH4->status = NFS4_OK; return res_PUTROOTFH4->status; } /* nfs4_op_putrootfh */ /** * @brief Free memory allocated for PUTROOTFH result * * This function frees any memory allocated for the result of * the NFS4_OP_PUTROOTFH function. * * @param[in,out] resp nfs4_op results */ void nfs4_op_putrootfh_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_read.c000066400000000000000000000513261324272410200216250ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_read.c * @brief NFSv4 read operation * * This file implements NFS4_OP_READ within an NFSv4 compound call. */ #include "config.h" #include #include #include #include "hashtable.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "sal_functions.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_convert.h" #include #include #include "fsal_pnfs.h" #include "server_stats.h" #include "export_mgr.h" /** * @brief Read on a pNFS pNFS data server * * This function bypasses cache_inode and calls directly into the FSAL * to perform a data-server read. * * @param[in] op Arguments for nfs41_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs41_op * * @return per RFC5661, p. 371 * */ static int op_dsread(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { READ4args * const arg_READ4 = &op->nfs_argop4_u.opread; READ4res * const res_READ4 = &resp->nfs_resop4_u.opread; /* NFSv4 return code */ nfsstat4 nfs_status = 0; /* Buffer into which data is to be read */ void *buffer = NULL; /* End of file flag */ bool eof = false; /* Don't bother calling the FSAL if the read length is 0. */ if (arg_READ4->count == 0) { res_READ4->READ4res_u.resok4.eof = FALSE; res_READ4->READ4res_u.resok4.data.data_len = 0; res_READ4->READ4res_u.resok4.data.data_val = NULL; res_READ4->status = NFS4_OK; return res_READ4->status; } /* Construct the FSAL file handle */ buffer = gsh_malloc_aligned(4096, arg_READ4->count); res_READ4->READ4res_u.resok4.data.data_val = buffer; nfs_status = data->current_ds->dsh_ops.read( data->current_ds, op_ctx, &arg_READ4->stateid, arg_READ4->offset, arg_READ4->count, res_READ4->READ4res_u.resok4.data.data_val, &res_READ4->READ4res_u.resok4.data.data_len, &eof); if (nfs_status != NFS4_OK) { gsh_free(buffer); res_READ4->READ4res_u.resok4.data.data_val = NULL; } if (eof) res_READ4->READ4res_u.resok4.eof = TRUE; else res_READ4->READ4res_u.resok4.eof = FALSE; res_READ4->status = nfs_status; return res_READ4->status; } /** * @brief Read on a pNFS pNFS data server * * This function bypasses cache_inode and calls directly into the FSAL * to perform a data-server read. * * @param[in] op Arguments for nfs41_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs41_op * * @return per RFC5661, p. 371 * */ static int op_dsread_plus(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp, struct io_info *info) { READ4args * const arg_READ4 = &op->nfs_argop4_u.opread; READ_PLUS4res * const res_RPLUS = &resp->nfs_resop4_u.opread_plus; contents *contentp = &res_RPLUS->rpr_resok4.rpr_contents; /* NFSv4 return code */ nfsstat4 nfs_status = 0; /* Buffer into which data is to be read */ void *buffer = NULL; /* End of file flag */ bool eof = false; /* Don't bother calling the FSAL if the read length is 0. */ if (arg_READ4->count == 0) { res_RPLUS->rpr_resok4.rpr_contents_count = 1; res_RPLUS->rpr_resok4.rpr_eof = FALSE; contentp->what = NFS4_CONTENT_DATA; contentp->data.d_offset = arg_READ4->offset; contentp->data.d_data.data_len = 0; contentp->data.d_data.data_val = NULL; res_RPLUS->rpr_status = NFS4_OK; return res_RPLUS->rpr_status; } /* Construct the FSAL file handle */ buffer = gsh_malloc_aligned(4096, arg_READ4->count); nfs_status = data->current_ds->dsh_ops.read_plus( data->current_ds, op_ctx, &arg_READ4->stateid, arg_READ4->offset, arg_READ4->count, buffer, arg_READ4->count, &eof, info); res_RPLUS->rpr_status = nfs_status; if (nfs_status != NFS4_OK) { gsh_free(buffer); return res_RPLUS->rpr_status; } contentp->what = info->io_content.what; res_RPLUS->rpr_resok4.rpr_contents_count = 1; res_RPLUS->rpr_resok4.rpr_eof = eof; if (info->io_content.what == NFS4_CONTENT_HOLE) { contentp->hole.di_offset = info->io_content.hole.di_offset; contentp->hole.di_length = info->io_content.hole.di_length; } if (info->io_content.what == NFS4_CONTENT_DATA) { contentp->data.d_offset = info->io_content.data.d_offset; contentp->data.d_data.data_len = info->io_content.data.d_data.data_len; contentp->data.d_data.data_val = info->io_content.data.d_data.data_val; } return res_RPLUS->rpr_status; } static int nfs4_read(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp, fsal_io_direction_t io, struct io_info *info) { READ4args * const arg_READ4 = &op->nfs_argop4_u.opread; READ4res * const res_READ4 = &resp->nfs_resop4_u.opread; uint64_t size = 0; size_t read_size = 0; uint64_t offset = 0; bool eof_met = false; void *bufferdata = NULL; fsal_status_t fsal_status = {0, 0}; state_t *state_found = NULL; state_t *state_open = NULL; struct fsal_obj_handle *obj = NULL; bool anonymous_started = false; state_owner_t *owner = NULL; bool bypass = false; uint64_t MaxRead = atomic_fetch_uint64_t(&op_ctx->ctx_export->MaxRead); uint64_t MaxOffsetRead = atomic_fetch_uint64_t( &op_ctx->ctx_export->MaxOffsetRead); /* Say we are managing NFS4_OP_READ */ resp->resop = NFS4_OP_READ; res_READ4->status = NFS4_OK; /* Do basic checks on a filehandle Only files can be read */ if ((data->minorversion > 0) && nfs4_Is_Fh_DSHandle(&data->currentFH)) { if (io == FSAL_IO_READ) return op_dsread(op, data, resp); else return op_dsread_plus(op, data, resp, info); } res_READ4->status = nfs4_sanity_check_FH(data, REGULAR_FILE, true); if (res_READ4->status != NFS4_OK) return res_READ4->status; obj = data->current_obj; /* Check stateid correctness and get pointer to state (also checks for special stateids) */ res_READ4->status = nfs4_Check_Stateid(&arg_READ4->stateid, obj, &state_found, data, STATEID_SPECIAL_ANY, 0, false, "READ"); if (res_READ4->status != NFS4_OK) return res_READ4->status; /* NB: After this point, if state_found == NULL, then the stateid is all-0 or all-1 */ if (state_found != NULL) { struct state_deleg *sdeleg; if (info) info->io_advise = state_found->state_data.io_advise; switch (state_found->state_type) { case STATE_TYPE_SHARE: state_open = state_found; /* Note this causes an extra refcount, but it * simplifies logic below. */ inc_state_t_ref(state_open); /** * @todo FSF: need to check against existing locks */ break; case STATE_TYPE_LOCK: state_open = state_found->state_data.lock.openstate; inc_state_t_ref(state_open); /** * @todo FSF: should check that write is in * range of an byte range lock... */ break; case STATE_TYPE_DELEG: /* Check if the delegation state allows READ */ sdeleg = &state_found->state_data.deleg; if (!(sdeleg->sd_type & OPEN_DELEGATE_READ) || (sdeleg->sd_state != DELEG_GRANTED)) { /* Invalid delegation for this operation. */ LogDebug(COMPONENT_STATE, "Delegation type:%d state:%d", sdeleg->sd_type, sdeleg->sd_state); res_READ4->status = NFS4ERR_BAD_STATEID; goto out; } state_open = NULL; break; default: res_READ4->status = NFS4ERR_BAD_STATEID; LogDebug(COMPONENT_NFS_V4_LOCK, "READ with invalid statid of type %d", state_found->state_type); goto out; } /* This is a read operation, this means that the file MUST have been opened for reading */ if (state_open != NULL && (state_open->state_data.share.share_access & OPEN4_SHARE_ACCESS_READ) == 0) { /* Even if file is open for write, the client * may do accidently read operation (caching). * Because of this, READ is allowed if not * explicitly denied. See page 112 in RFC 7530 * for more details. */ if (state_open->state_data.share.share_deny & OPEN4_SHARE_DENY_READ) { /* Bad open mode, return NFS4ERR_OPENMODE */ res_READ4->status = NFS4ERR_OPENMODE; if (isDebug(COMPONENT_NFS_V4_LOCK)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = { sizeof(str), str, str}; display_stateid(&dspbuf, state_found); LogDebug(COMPONENT_NFS_V4_LOCK, "READ %s doesn't have OPEN4_SHARE_ACCESS_READ", str); } goto out; } } /** * @todo : this piece of code looks a bit suspicious * (see Rong's mail) * * @todo: ACE: This works for now. How do we want to * handle owner confirmation across NFSv4.0/NFSv4.1? * Do we want to mark every NFSv4.1 owner * pre-confirmed, or make the check conditional on * minorversion like we do here? */ switch (state_found->state_type) { case STATE_TYPE_SHARE: if (!state_owner_confirmed(state_found)) { res_READ4->status = NFS4ERR_BAD_STATEID; goto out; } break; case STATE_TYPE_LOCK: case STATE_TYPE_DELEG: break; default: /* Sanity check: all other types are illegal. * we should not got that place (similar check * above), anyway it costs nothing to add this * test */ res_READ4->status = NFS4ERR_BAD_STATEID; goto out; } } else { /* Special stateid, no open state, check to see if any share conflicts */ state_open = NULL; /* Special stateid, no open state, check to see if any share * conflicts The stateid is all-0 or all-1 */ bypass = arg_READ4->stateid.seqid != 0; /* Check for delegation conflict. */ if (state_deleg_conflict(obj, false)) { res_READ4->status = NFS4ERR_DELAY; goto out; } anonymous_started = true; } /* Need to permission check the read. */ fsal_status = obj->obj_ops.test_access(obj, FSAL_READ_ACCESS, NULL, NULL, true); if (fsal_status.major == ERR_FSAL_ACCESS) { /* Test for execute permission */ fsal_status = fsal_access(obj, FSAL_MODE_MASK_SET(FSAL_X_OK) | FSAL_ACE4_MASK_SET (FSAL_ACE_PERM_EXECUTE)); } if (FSAL_IS_ERROR(fsal_status)) { res_READ4->status = nfs4_Errno_status(fsal_status); goto done; } /* Get the size and offset of the read operation */ offset = arg_READ4->offset; size = arg_READ4->count; if (MaxOffsetRead < UINT64_MAX) { LogFullDebug(COMPONENT_NFS_V4, "Read offset=%" PRIu64 " size=%" PRIu64 " MaxOffSet=%" PRIu64, offset, size, MaxOffsetRead); if ((offset + size) > MaxOffsetRead) { LogEvent(COMPONENT_NFS_V4, "A client tryed to violate max file size %" PRIu64 " for exportid #%hu", MaxOffsetRead, op_ctx->ctx_export->export_id); res_READ4->status = NFS4ERR_FBIG; goto done; } } if (size > MaxRead) { /* the client asked for too much data, this should normally not happen because client will get FATTR4_MAXREAD value at mount time */ if (info == NULL || info->io_content.what != NFS4_CONTENT_HOLE) { LogFullDebug(COMPONENT_NFS_V4, "read requested size = %"PRIu64 " read allowed size = %" PRIu64, size, MaxRead); size = MaxRead; } } /* If size == 0, no I/O is to be made and everything is alright */ if (size == 0) { /* A size = 0 can not lead to EOF */ res_READ4->READ4res_u.resok4.eof = false; res_READ4->READ4res_u.resok4.data.data_len = 0; res_READ4->READ4res_u.resok4.data.data_val = NULL; res_READ4->status = NFS4_OK; goto done; } /* Some work is to be done */ bufferdata = gsh_malloc_aligned(4096, size); if (!anonymous_started && data->minorversion == 0) { owner = get_state_owner_ref(state_found); if (owner != NULL) { op_ctx->clientid = &owner->so_owner.so_nfs4_owner.so_clientid; } } /* Call the new fsal_read2 */ fsal_status = fsal_read2(obj, bypass, state_found, offset, size, &read_size, bufferdata, &eof_met, info); if (FSAL_IS_ERROR(fsal_status)) { res_READ4->status = nfs4_Errno_status(fsal_status); gsh_free(bufferdata); res_READ4->READ4res_u.resok4.data.data_val = NULL; goto done; } if (!anonymous_started && data->minorversion == 0) op_ctx->clientid = NULL; res_READ4->READ4res_u.resok4.data.data_len = read_size; res_READ4->READ4res_u.resok4.data.data_val = bufferdata; LogFullDebug(COMPONENT_NFS_V4, "NFS4_OP_READ: offset = %" PRIu64 " read length = %zu eof=%u", offset, read_size, eof_met); /* Is EOF met or not ? */ res_READ4->READ4res_u.resok4.eof = eof_met; /* Say it is ok */ res_READ4->status = NFS4_OK; done: server_stats_io_done(size, read_size, (res_READ4->status == NFS4_OK) ? true : false, false); out: if (owner != NULL) dec_state_owner_ref(owner); if (state_found != NULL) dec_state_t_ref(state_found); if (state_open != NULL) dec_state_t_ref(state_open); return res_READ4->status; } /* nfs4_op_read */ /** * @brief The NFS4_OP_READ operation * * This functions handles the READ operation in NFSv4.0 This * function can be called only from nfs4_Compound. * * @param[in] op The nfs4_op arguments * @param[in,out] data The compound request's data * @param[out] resp The nfs4_op results * * @return Errors as specified by RFC3550 RFC5661 p. 371. */ int nfs4_op_read(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { int err; err = nfs4_read(op, data, resp, FSAL_IO_READ, NULL); return err; } /** * @brief Free data allocated for READ result. * * This function frees any data allocated for the result of the * NFS4_OP_READ operation. * * @param[in,out] resp Results fo nfs4_op * */ void nfs4_op_read_Free(nfs_resop4 *res) { READ4res *resp = &res->nfs_resop4_u.opread; if (resp->status == NFS4_OK) if (resp->READ4res_u.resok4.data.data_val != NULL) gsh_free(resp->READ4res_u.resok4.data.data_val); } /** * @brief The NFS4_OP_READ_PLUS operation * * This functions handles the READ_PLUS operation in NFSv4.2 This * function can be called only from nfs4_Compound. * * @param[in] op The nfs4_op arguments * @param[in,out] data The compound request's data * @param[out] resp The nfs4_op results * * @return Errors as specified by RFC3550 RFC5661 p. 371. */ int nfs4_op_read_plus(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { struct nfs_resop4 res; struct io_info info; /* Response */ READ_PLUS4res * const res_RPLUS = &resp->nfs_resop4_u.opread_plus; READ4res *res_READ4 = &res.nfs_resop4_u.opread; contents *contentp = &res_RPLUS->rpr_resok4.rpr_contents; resp->resop = NFS4_OP_READ_PLUS; nfs4_read(op, data, &res, FSAL_IO_READ_PLUS, &info); res_RPLUS->rpr_status = res_READ4->status; if (res_RPLUS->rpr_status != NFS4_OK) return res_RPLUS->rpr_status; contentp->what = info.io_content.what; res_RPLUS->rpr_resok4.rpr_contents_count = 1; res_RPLUS->rpr_resok4.rpr_eof = res_READ4->READ4res_u.resok4.eof; if (info.io_content.what == NFS4_CONTENT_HOLE) { contentp->hole.di_offset = info.io_content.hole.di_offset; contentp->hole.di_length = info.io_content.hole.di_length; } if (info.io_content.what == NFS4_CONTENT_DATA) { contentp->data.d_offset = info.io_content.data.d_offset; contentp->data.d_data.data_len = info.io_content.data.d_data.data_len; contentp->data.d_data.data_val = info.io_content.data.d_data.data_val; } return res_RPLUS->rpr_status; } void nfs4_op_read_plus_Free(nfs_resop4 *res) { READ_PLUS4res *resp = &res->nfs_resop4_u.opread_plus; contents *conp = &resp->rpr_resok4.rpr_contents; if (resp->rpr_status == NFS4_OK && conp->what == NFS4_CONTENT_DATA) if (conp->data.d_data.data_val != NULL) gsh_free(conp->data.d_data.data_val); } /** * @brief The NFS4_OP_IO_ADVISE operation * * This functions handles the IO_ADVISE operation in NFSv4.2 This * function can be called only from nfs4_Compound. * * @param[in] op The nfs4_op arguments * @param[out] resp The nfs4_op results * * @return Errors as specified by RFC3550 RFC5661 p. 371. */ int nfs4_op_io_advise(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { IO_ADVISE4args * const arg_IO_ADVISE = &op->nfs_argop4_u.opio_advise; IO_ADVISE4res * const res_IO_ADVISE = &resp->nfs_resop4_u.opio_advise; fsal_status_t fsal_status = { 0, 0 }; struct io_hints hints; state_t *state_found = NULL; struct fsal_obj_handle *obj = NULL; /* Say we are managing NFS4_OP_IO_ADVISE */ resp->resop = NFS4_OP_IO_ADVISE; res_IO_ADVISE->iaa_status = NFS4_OK; hints.hints = 0; hints.offset = 0; hints.count = 0; if (data->minorversion < 2) { res_IO_ADVISE->iaa_status = NFS4ERR_NOTSUPP; goto done; } /* Do basic checks on a filehandle Only files can be set */ res_IO_ADVISE->iaa_status = nfs4_sanity_check_FH(data, REGULAR_FILE, true); if (res_IO_ADVISE->iaa_status != NFS4_OK) goto done; obj = data->current_obj; /* Check stateid correctness and get pointer to state (also checks for special stateids) */ res_IO_ADVISE->iaa_status = nfs4_Check_Stateid(&arg_IO_ADVISE->iaa_stateid, obj, &state_found, data, STATEID_SPECIAL_ANY, 0, false, "IO_ADVISE"); if (res_IO_ADVISE->iaa_status != NFS4_OK) goto done; if (state_found && obj) { hints.hints = arg_IO_ADVISE->iaa_hints.map[0]; hints.offset = arg_IO_ADVISE->iaa_offset; hints.count = arg_IO_ADVISE->iaa_count; fsal_status = obj->obj_ops.io_advise(obj, &hints); if (FSAL_IS_ERROR(fsal_status)) { res_IO_ADVISE->iaa_status = NFS4ERR_NOTSUPP; goto done; } /* save hints to use with other operations */ state_found->state_data.io_advise = hints.hints; res_IO_ADVISE->iaa_status = NFS4_OK; res_IO_ADVISE->iaa_hints.bitmap4_len = 1; res_IO_ADVISE->iaa_hints.map[0] = hints.hints; } done: LogDebug(COMPONENT_NFS_V4, "Status %s hints 0x%X offset %" PRIu64 " count %" PRIu64, nfsstat4_to_str(res_IO_ADVISE->iaa_status), hints.hints, hints.offset, hints.count); if (state_found != NULL) dec_state_t_ref(state_found); return res_IO_ADVISE->iaa_status; } /* nfs4_op_io_advise */ /** * @brief Free memory allocated for IO_ADVISE result * * This function frees any memory allocated for the result of the * NFS4_OP_IO_ADVISE operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_io_advise_Free(nfs_resop4 *resp) { /* Nothing to be done */ } int nfs4_op_seek(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { SEEK4args * const arg_SEEK = &op->nfs_argop4_u.opseek; SEEK4res * const res_SEEK = &resp->nfs_resop4_u.opseek; fsal_status_t fsal_status = { 0, 0 }; state_t *state_found = NULL; struct fsal_obj_handle *obj = NULL; struct io_info info; /* Say we are managing NFS4_OP_SEEK */ resp->resop = NFS4_OP_SEEK; if (data->minorversion < 2) { res_SEEK->sr_status = NFS4ERR_NOTSUPP; goto done; } res_SEEK->sr_status = NFS4_OK; /* Do basic checks on a filehandle Only files can be set */ res_SEEK->sr_status = nfs4_sanity_check_FH(data, REGULAR_FILE, true); if (res_SEEK->sr_status != NFS4_OK) goto done; obj = data->current_obj; /* Check stateid correctness and get pointer to state (also checks for special stateids) */ res_SEEK->sr_status = nfs4_Check_Stateid(&arg_SEEK->sa_stateid, obj, &state_found, data, STATEID_SPECIAL_ANY, 0, false, "SEEK"); if (res_SEEK->sr_status != NFS4_OK) goto done; if (state_found != NULL) { info.io_advise = state_found->state_data.io_advise; info.io_content.what = arg_SEEK->sa_what; if (arg_SEEK->sa_what == NFS4_CONTENT_DATA || arg_SEEK->sa_what == NFS4_CONTENT_HOLE) { info.io_content.hole.di_offset = arg_SEEK->sa_offset; } else info.io_content.adb.adb_offset = arg_SEEK->sa_offset; fsal_status = obj->obj_ops.seek(obj, &info); if (FSAL_IS_ERROR(fsal_status)) { res_SEEK->sr_status = NFS4ERR_NXIO; goto done; } res_SEEK->sr_resok4.sr_eof = info.io_eof; res_SEEK->sr_resok4.sr_offset = info.io_content.hole.di_offset; } done: LogDebug(COMPONENT_NFS_V4, "Status %s type %d offset %" PRIu64, nfsstat4_to_str(res_SEEK->sr_status), arg_SEEK->sa_what, arg_SEEK->sa_offset); if (state_found != NULL) dec_state_t_ref(state_found); return res_SEEK->sr_status; } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_readdir.c000066400000000000000000000512461324272410200223250ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ #include "config.h" #include "log.h" #include "gsh_rpc.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_creds.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_file_handle.h" #include "nfs_convert.h" #include "export_mgr.h" /** * @brief Opaque bookkeeping structure for NFSv4 readdir * * This structure keeps track of the process of writing out an NFSv4 * READDIR response between calls to nfs4_readdir_callback. */ struct nfs4_readdir_cb_data { entry4 *entries; /*< The array holding individual entries */ size_t mem_left; /*< The amount of memory remaining before we hit maxcount */ size_t count; /*< The count of complete entries stored in the buffer */ size_t total_entries; /*< The total number of entries available in the array */ nfsstat4 error; /*< Set to a value other than NFS4_OK if the callback function finds a fatal error. */ struct bitmap4 *req_attr; /*< The requested attributes */ compound_data_t *data; /*< The compound data, so we can produce nfs_fh4s. */ struct export_perms save_export_perms; /*< Saved export perms. */ struct gsh_export *saved_gsh_export; /*< Saved export */ }; static void restore_data(struct nfs4_readdir_cb_data *tracker) { /* Restore export stuff */ if (op_ctx->ctx_export) put_gsh_export(op_ctx->ctx_export); *op_ctx->export_perms = tracker->save_export_perms; op_ctx->ctx_export = tracker->saved_gsh_export; op_ctx->fsal_export = op_ctx->ctx_export->fsal_export; tracker->saved_gsh_export = NULL; /* Restore creds */ if (nfs_req_creds(tracker->data->req) != NFS4_OK) { LogCrit(COMPONENT_EXPORT, "Failure to restore creds"); } } /** * @brief Populate entry4s when called from fsal_readdir * * This function is a callback passed to fsal_readdir. It * fills in a pre-allocated array of entry4 structures and allocates * space for the name and attributes. This space must be freed. * * @param[in,out] opaque A struct nfs4_readdir_cb_data that stores the * location of the array and other bookeeping * information * @param[in] obj Current file * @param[in] attrs The current file's attributes * @param[in] cookie The readdir cookie for the current entry */ fsal_errors_t nfs4_readdir_callback(void *opaque, struct fsal_obj_handle *obj, const struct attrlist *attr, uint64_t mounted_on_fileid, uint64_t cookie, enum cb_state cb_state) { struct fsal_readdir_cb_parms *cb_parms = opaque; struct nfs4_readdir_cb_data *tracker = cb_parms->opaque; size_t namelen = 0; char val_fh[NFS4_FHSIZE]; nfs_fh4 entryFH = { .nfs_fh4_len = 0, .nfs_fh4_val = val_fh }; struct xdr_attrs_args args; compound_data_t *data = tracker->data; nfsstat4 rdattr_error = NFS4_OK; entry4 *tracker_entry = tracker->entries + tracker->count; fsal_status_t fsal_status; fsal_accessflags_t access_mask_attr = 0; /* Cleanup after problem with junction processing. */ if (cb_state == CB_PROBLEM) { /* Restore the export. */ restore_data(tracker); return ERR_FSAL_NO_ERROR; } if (tracker->total_entries == tracker->count) goto not_inresult; /* Test if this is a junction. * * NOTE: If there is a junction within a file system (perhaps setting * up different permissions for part of the file system), the * junction inode will ALSO be the root of the nested export. * By testing cb_state, we allow the call back to process * that root inode to proceed rather than getting stuck in a * junction crossing infinite loop. */ PTHREAD_RWLOCK_rdlock(&obj->state_hdl->state_lock); if (cb_parms->attr_allowed && obj->type == DIRECTORY && obj->state_hdl->dir.junction_export != NULL && cb_state == CB_ORIGINAL) { /* This is a junction. Code used to not recognize this * which resulted in readdir giving different attributes * (including FH, FSid, etc...) to clients from a * lookup. AIX refused to list the directory because of * this. Now we go to the junction to get the * attributes. */ LogDebug(COMPONENT_EXPORT, "Offspring DIR %s is a junction Export_id %d Pseudo %s", cb_parms->name, obj->state_hdl->dir.junction_export->export_id, obj->state_hdl->dir.junction_export->pseudopath); /* Get a reference to the export and stash it in * compound data. */ if (!export_ready(obj->state_hdl->dir.junction_export)) { /* Export is in the process of being released. * Pretend it's not actually a junction. */ goto not_junction; } get_gsh_export_ref(obj->state_hdl->dir.junction_export); /* Save the compound data context */ tracker->save_export_perms = *op_ctx->export_perms; tracker->saved_gsh_export = op_ctx->ctx_export; /* Cross the junction */ op_ctx->ctx_export = obj->state_hdl->dir.junction_export; op_ctx->fsal_export = op_ctx->ctx_export->fsal_export; /* Build the credentials */ rdattr_error = nfs4_export_check_access(data->req); if (rdattr_error == NFS4ERR_ACCESS) { /* If return is NFS4ERR_ACCESS then this client * doesn't have access to this export, quietly * skip the export. */ LogDebug(COMPONENT_EXPORT, "NFS4ERR_ACCESS Skipping Export_Id %d Pseudo %s", op_ctx->ctx_export->export_id, op_ctx->ctx_export->pseudopath); /* Restore export and creds */ restore_data(tracker); /* Indicate success without adding another entry */ cb_parms->in_result = true; PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); return ERR_FSAL_NO_ERROR; } if (rdattr_error == NFS4ERR_WRONGSEC) { /* Client isn't using the right SecType for this export, * we will report NFS4ERR_WRONGSEC in * FATTR4_RDATTR_ERROR. * * If the ONLY attributes requested are * FATTR4_RDATTR_ERROR and FATTR4_MOUNTED_ON_FILEID we * will not return an error and instead will return * success with FATTR4_MOUNTED_ON_FILEID. AIX clients * make this request and expect it to succeed. */ if (check_for_wrongsec_ok_attr(tracker->req_attr)) { /* Client is requesting attr that are allowed * when NFS4ERR_WRONGSEC occurs. */ LogDebug(COMPONENT_EXPORT, "Ignoring NFS4ERR_WRONGSEC (only asked for MOUNTED_IN_FILEID) On ReadDir Export_Id %d Path %s", op_ctx->ctx_export->export_id, op_ctx->ctx_export->pseudopath); /* Because we are not asking for any attributes * which are a property of the exported file * system's root, really just asking for * MOUNTED_ON_FILEID, we can just get the attr * for this node since it will result in the * correct value for MOUNTED_ON_FILEID since * the fileid of the junction node is the * MOUNTED_ON_FILEID of the root across the * junction, and the mounted_on_filed passed * is the fileid of the junction (since the * node can't be the root of the current * export). * * Go ahead and proceed without an error. */ rdattr_error = NFS4_OK; } else { /* We really must report the NFS4ERR_WRONGSEC. * We will report it below, but we need to get * the name into the entry. */ LogDebug(COMPONENT_EXPORT, "NFS4ERR_WRONGSEC On ReadDir Export_Id %d Pseudo %s", op_ctx->ctx_export->export_id, op_ctx->ctx_export->pseudopath); } } else if (rdattr_error == NFS4_OK) { /* Now we must traverse the junction to get the * attributes. We have already set up the compound data. * * Signal to populate_dirent to call back with the * root node of the export across the junction. Also * signal to ourselves that the call back will be * across the junction. */ LogDebug(COMPONENT_EXPORT, "Need to cross junction to Export_Id %d Pseudo %s", op_ctx->ctx_export->export_id, op_ctx->ctx_export->pseudopath); PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); return ERR_FSAL_CROSS_JUNCTION; } /* An error occurred and we will report it, but we need to get * the name into the entry to proceed. * * Restore export and creds. */ LogDebug(COMPONENT_EXPORT, "Need to report error for junction to Export_Id %d Pseudo %s", op_ctx->ctx_export->export_id, op_ctx->ctx_export->pseudopath); restore_data(tracker); } not_junction: PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); /* Now process the entry */ memset(val_fh, 0, NFS4_FHSIZE); /* Bits that don't require allocation */ if (tracker->mem_left < sizeof(entry4)) { if (tracker->count == 0) tracker->error = NFS4ERR_TOOSMALL; goto failure; } tracker->mem_left -= sizeof(entry4); tracker_entry->cookie = cookie; tracker_entry->nextentry = NULL; /* The filename. We don't use str2utf8 because that has an * additional copy into a buffer before copying into the * destination. */ namelen = strlen(cb_parms->name); if (tracker->mem_left < (namelen + 1)) { if (tracker->count == 0) tracker->error = NFS4ERR_TOOSMALL; goto failure; } tracker->mem_left -= (namelen + 1); tracker_entry->name.utf8string_len = namelen; tracker_entry->name.utf8string_val = gsh_malloc(namelen + 1); memcpy(tracker_entry->name.utf8string_val, cb_parms->name, namelen); /* If we carried an error from above, now that we have * the name set up, go ahead and try and put error in * results. */ if (rdattr_error != NFS4_OK) { LogDebug(COMPONENT_NFS_READDIR, "Skipping because of %s", nfsstat4_to_str(rdattr_error)); goto skip; } if (cb_parms->attr_allowed && attribute_is_set(tracker->req_attr, FATTR4_FILEHANDLE) && !nfs4_FSALToFhandle(false, &entryFH, obj, op_ctx->ctx_export)) goto server_fault; if (!cb_parms->attr_allowed) { /* fsal_readdir is signaling us that client didn't have * search permission in this directory, so we can't return any * attributes, but must indicate NFS4ERR_ACCESS. */ rdattr_error = NFS4ERR_ACCESS; LogDebug(COMPONENT_NFS_READDIR, "Skipping because of %s", nfsstat4_to_str(rdattr_error)); goto skip; } /* Adjust access mask if ACL is asked for. * NOTE: We intentionally do NOT check ACE4_READ_ATTR. */ if (attribute_is_set(tracker->req_attr, FATTR4_ACL)) access_mask_attr |= FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_READ_ACL); /* Attrs were refreshed before call */ fsal_status = obj->obj_ops.test_access(obj, access_mask_attr, NULL, NULL, false); if (FSAL_IS_ERROR(fsal_status)) { LogDebug(COMPONENT_NFS_READDIR, "permission check for attributes status=%s", msg_fsal_err(fsal_status.major)); rdattr_error = nfs4_Errno_status(fsal_status); LogDebug(COMPONENT_NFS_READDIR, "Skipping because of %s", nfsstat4_to_str(rdattr_error)); goto skip; } memset(&args, 0, sizeof(args)); args.attrs = (struct attrlist *)attr; args.data = data; args.hdl4 = &entryFH; args.mounted_on_fileid = mounted_on_fileid; args.fileid = obj->fileid; args.fsid = obj->fsid; if (nfs4_FSALattr_To_Fattr(&args, tracker->req_attr, &tracker_entry->attrs) != 0) { LogCrit(COMPONENT_NFS_READDIR, "nfs4_FSALattr_To_Fattr failed to convert attr"); goto server_fault; } if (obj->type == DIRECTORY && is_sticky_bit_set(obj, attr)) { rdattr_error = NFS4ERR_MOVED; LogDebug(COMPONENT_NFS_READDIR, "Skipping because of %s", nfsstat4_to_str(rdattr_error)); } skip: if (rdattr_error != NFS4_OK) { if (!attribute_is_set(tracker->req_attr, FATTR4_RDATTR_ERROR)) { tracker->error = rdattr_error; goto failure; } if (nfs4_Fattr_Fill_Error(&tracker_entry->attrs, rdattr_error) == -1) goto server_fault; } if (tracker->mem_left < ((tracker_entry->attrs.attrmask.bitmap4_len * sizeof(uint32_t)) + (tracker_entry->attrs.attr_vals.attrlist4_len))) { if (tracker->count == 0) tracker->error = NFS4ERR_TOOSMALL; goto failure; } tracker->mem_left -= tracker_entry->attrs.attrmask.bitmap4_len * sizeof(uint32_t); tracker->mem_left -= tracker_entry->attrs.attr_vals.attrlist4_len; if (tracker->count != 0) tracker->entries[tracker->count - 1].nextentry = tracker_entry; ++(tracker->count); cb_parms->in_result = true; goto out; server_fault: tracker->error = NFS4ERR_SERVERFAULT; failure: if (tracker_entry->attrs.attr_vals.attrlist4_val != NULL) { gsh_free(tracker_entry->attrs.attr_vals.attrlist4_val); tracker_entry->attrs.attr_vals.attrlist4_val = NULL; } if (tracker_entry->name.utf8string_val != NULL) { gsh_free(tracker_entry->name.utf8string_val); tracker_entry->name.utf8string_val = NULL; } not_inresult: cb_parms->in_result = false; out: return ERR_FSAL_NO_ERROR; } /** * @brief Free a list of entry4s * * This function frees a list of entry4s and all dependent strctures. * * @param[in,out] entries The entries to be freed */ static void free_entries(entry4 *entries) { entry4 *entry = NULL; for (entry = entries; entry != NULL; entry = entry->nextentry) { if (entry->attrs.attr_vals.attrlist4_val != NULL) gsh_free(entry->attrs.attr_vals.attrlist4_val); if (entry->name.utf8string_val != NULL) gsh_free(entry->name.utf8string_val); } gsh_free(entries); } /** * @brief NFS4_OP_READDIR * * Implements the NFS4_OP_READDIR opeartion. If fh is a pseudo FH, * then call is routed to routine nfs4_op_readdir_pseudo * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, pp. 371-2 * */ int nfs4_op_readdir(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { READDIR4args * const arg_READDIR4 = &op->nfs_argop4_u.opreaddir; READDIR4res * const res_READDIR4 = &resp->nfs_resop4_u.opreaddir; struct fsal_obj_handle *dir_obj = NULL; bool eod_met = false; unsigned long dircount = 0; unsigned long maxcount = 0; entry4 *entries = NULL; verifier4 cookie_verifier; uint64_t cookie = 0; unsigned int estimated_num_entries = 0; unsigned int num_entries = 0; struct nfs4_readdir_cb_data tracker; fsal_status_t fsal_status = {0, 0}; attrmask_t attrmask; bool use_cookie_verifier = op_ctx_export_has_option( EXPORT_OPTION_USE_COOKIE_VERIFIER); resp->resop = NFS4_OP_READDIR; res_READDIR4->status = NFS4_OK; res_READDIR4->status = nfs4_sanity_check_FH(data, DIRECTORY, false); if (res_READDIR4->status != NFS4_OK) goto out; memset(&tracker, 0, sizeof(tracker)); dir_obj = data->current_obj; /* get the characteristic value for readdir operation */ dircount = arg_READDIR4->dircount; maxcount = (arg_READDIR4->maxcount * 9) / 10; cookie = 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 */ estimated_num_entries = 50; tracker.total_entries = estimated_num_entries; LogDebug(COMPONENT_NFS_READDIR, "dircount=%lu maxcount=%lu cookie=%" PRIu64 " estimated_num_entries=%u", dircount, maxcount, cookie, estimated_num_entries); /* Since we never send a cookie of 1 or 2, we shouldn't ever get * them back. */ if (cookie == 1 || cookie == 2) { res_READDIR4->status = NFS4ERR_BAD_COOKIE; LogDebug(COMPONENT_NFS_READDIR, "Bad cookie"); goto out; } /* 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; LogDebug(COMPONENT_NFS_READDIR, "Requested invalid attributes"); goto out; } /* If maxcount is too short (14 should be enough for an empty * directory) return NFS4ERR_TOOSMALL */ if (maxcount < 14 || estimated_num_entries == 0) { res_READDIR4->status = NFS4ERR_TOOSMALL; LogDebug(COMPONENT_NFS_READDIR, "Response too small"); goto out; } /* If a cookie verifier is used, then a non-trivial value is * returned to the client. This value is the change_time of the * directory. 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 (use_cookie_verifier) { time_t change_time; struct attrlist attrs; fsal_prepare_attrs(&attrs, ATTR_CHGTIME); fsal_status = data->current_obj->obj_ops.getattrs(data->current_obj, &attrs); if (FSAL_IS_ERROR(fsal_status)) { res_READDIR4->status = nfs4_Errno_status(fsal_status); LogDebug(COMPONENT_NFS_READDIR, "getattrs returned %s", msg_fsal_err(fsal_status.major)); goto out; } change_time = timespec_to_nsecs(&attrs.chgtime); memcpy(cookie_verifier, &change_time, sizeof(change_time)); /* Done with the attrs */ fsal_release_attrs(&attrs); } /* Cookie delivered by the server and used by the client SHOULD * not be 0, 1 or 2 because these values are reserved (see RFC * 3530, p. 192/RFC 5661, p468). * * 0 - cookie for first READDIR * 1 - reserved for . on client * 2 - reserved for .. on client * * '.' and '..' are not returned, so all cookies will be offset by 2 */ if (cookie != 0 && use_cookie_verifier) { if (memcmp(cookie_verifier, arg_READDIR4->cookieverf, NFS4_VERIFIER_SIZE) != 0) { res_READDIR4->status = NFS4ERR_BAD_COOKIE; LogDebug(COMPONENT_NFS_READDIR, "Bad cookie"); goto out; } } /* Prepare to read the entries */ entries = gsh_calloc(estimated_num_entries, sizeof(entry4)); tracker.entries = entries; tracker.mem_left = maxcount - sizeof(READDIR4resok); tracker.count = 0; tracker.error = NFS4_OK; tracker.req_attr = &arg_READDIR4->attr_request; tracker.data = data; /* Assume we need at least the NFS v3 attr. * Any attr is sufficient for permission checking. */ attrmask = ATTRS_NFS3; /* If ACL is requested, we need to add that for permission checking. */ if (attribute_is_set(tracker.req_attr, FATTR4_ACL)) attrmask |= ATTR_ACL; /* Perform the readdir operation */ fsal_status = fsal_readdir(dir_obj, cookie, &num_entries, &eod_met, attrmask, nfs4_readdir_callback, &tracker); if (FSAL_IS_ERROR(fsal_status)) { res_READDIR4->status = nfs4_Errno_status(fsal_status); LogDebug(COMPONENT_NFS_READDIR, "fsal_readdir returned %s", msg_fsal_err(fsal_status.major)); goto out; } LogDebug(COMPONENT_NFS_READDIR, "fsal_readdir returned %s", msg_fsal_err(fsal_status.major)); res_READDIR4->status = tracker.error; if (res_READDIR4->status != NFS4_OK) { LogDebug(COMPONENT_NFS_READDIR, "Tracker error"); goto out; } if (tracker.count != 0) { /* Put the entry's list in the READDIR reply if * there were any. */ res_READDIR4->READDIR4res_u.resok4.reply.entries = entries; } else { gsh_free(entries); entries = NULL; res_READDIR4->READDIR4res_u.resok4.reply.entries = NULL; } /* This slight bit of oddness is caused by most booleans * throughout Ganesha being of C99's bool type (taking the values * true and false), but fields in XDR being of the older bool_t * type i(taking the values TRUE and FALSE) */ if (eod_met) res_READDIR4->READDIR4res_u.resok4.reply.eof = TRUE; else res_READDIR4->READDIR4res_u.resok4.reply.eof = FALSE; /* Do not forget to set the verifier */ memcpy(res_READDIR4->READDIR4res_u.resok4.cookieverf, cookie_verifier, NFS4_VERIFIER_SIZE); res_READDIR4->status = NFS4_OK; out: if ((res_READDIR4->status != NFS4_OK) && (entries != NULL)) free_entries(entries); LogDebug(COMPONENT_NFS_READDIR, "Returning %s", nfsstat4_to_str(res_READDIR4->status)); return res_READDIR4->status; } /* nfs4_op_readdir */ /** * @brief Free memory allocated for READDIR result * * This function frees any memory allocated for the results of the * NFS4_OP_READDIR operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_readdir_Free(nfs_resop4 *res) { READDIR4res *resp = &res->nfs_resop4_u.opreaddir; free_entries(resp->READDIR4res_u.resok4.reply.entries); } /* nfs4_op_readdir_Free */ nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_readlink.c000066400000000000000000000062371324272410200225040ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_readlink.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. */ #include "config.h" #include "log.h" #include "nfs4.h" #include "nfs_core.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_convert.h" #include "nfs_file_handle.h" #include "gsh_types.h" /** * @brief The NFS4_OP_READLINK operation. * * This function implements the NFS4_OP_READLINK operation. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 372 * * @see nfs4_Compound * */ int nfs4_op_readlink(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { READLINK4res * const res_READLINK4 = &resp->nfs_resop4_u.opreadlink; fsal_status_t fsal_status = {0, 0}; struct gsh_buffdesc link_buffer = {.addr = NULL, .len = 0 }; resp->resop = NFS4_OP_READLINK; res_READLINK4->status = NFS4_OK; /* * Do basic checks on a filehandle You can readlink only on a link * ... */ res_READLINK4->status = nfs4_sanity_check_FH(data, SYMBOLIC_LINK, false); if (res_READLINK4->status != NFS4_OK) return res_READLINK4->status; fsal_status = fsal_readlink(data->current_obj, &link_buffer); if (FSAL_IS_ERROR(fsal_status)) { res_READLINK4->status = nfs4_Errno_status(fsal_status); return res_READLINK4->status; } res_READLINK4->READLINK4res_u.resok4.link.utf8string_val = link_buffer.addr; /* NFSv4 does not require the \NUL terminator. */ res_READLINK4->READLINK4res_u.resok4.link.utf8string_len = link_buffer.len - 1; res_READLINK4->status = NFS4_OK; return res_READLINK4->status; } /* nfs4_op_readlink */ /** * @brief Free memory allocated for READLINK result * * This function frees the memory allocated for the resutl of the * NFS4_OP_READLINK operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_readlink_Free(nfs_resop4 *res) { READLINK4res *resp = &res->nfs_resop4_u.opreadlink; if (resp->status == NFS4_OK && resp->READLINK4res_u.resok4.link.utf8string_val) gsh_free(resp->READLINK4res_u.resok4.link.utf8string_val); } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_reclaim_complete.c000066400000000000000000000066431324272410200242200ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_reclaim_complete.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. * * */ #include "config.h" #include #include #include #include #include #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "nfs23.h" #include "nfs4.h" #include "mount.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_file_handle.h" #include "sal_data.h" #include "sal_functions.h" /** * * @brief The NFS4_OP_RECLAIM_COMPLETE4 operation. * * This function implements the NFS4_OP_RECLAIM_COMPLETE4 operation. * * @param[in] op Arguments for the nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Retuls for the nfs4_op * * @return per RFC5661 p. 372 * * @see nfs4_Compound * */ int nfs4_op_reclaim_complete(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { RECLAIM_COMPLETE4args * const arg_RECLAIM_COMPLETE4 __attribute__ ((unused)) = &op->nfs_argop4_u.opreclaim_complete; RECLAIM_COMPLETE4res * const res_RECLAIM_COMPLETE4 = &resp->nfs_resop4_u.opreclaim_complete; resp->resop = NFS4_OP_RECLAIM_COMPLETE; res_RECLAIM_COMPLETE4->rcr_status = NFS4_OK; if (data->minorversion == 0) { res_RECLAIM_COMPLETE4->rcr_status = NFS4ERR_INVAL; return res_RECLAIM_COMPLETE4->rcr_status; } if (data->session == NULL) { res_RECLAIM_COMPLETE4->rcr_status = NFS4ERR_OP_NOT_IN_SESSION; return res_RECLAIM_COMPLETE4->rcr_status; } /* For now, we don't handle rca_one_fs, so we won't complain about * complete already for it. */ if (data->session->clientid_record->cid_cb.v41.cid_reclaim_complete && !arg_RECLAIM_COMPLETE4->rca_one_fs) { res_RECLAIM_COMPLETE4->rcr_status = NFS4ERR_COMPLETE_ALREADY; return res_RECLAIM_COMPLETE4->rcr_status; } if (!arg_RECLAIM_COMPLETE4->rca_one_fs) { data->session->clientid_record->cid_cb.v41.cid_reclaim_complete = true; nfs4_recovery_reclaim_complete(data->session->clientid_record); } return res_RECLAIM_COMPLETE4->rcr_status; } /* nfs41_op_reclaim_complete */ /** * @brief Free memory allocated for RECLAIM_COMPLETE result * * This function frees anty memory allocated for the result of the * NFS4_OP_RECLAIM_COMPLETE operation. */ void nfs4_op_reclaim_complete_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_release_lockowner.c000066400000000000000000000105131324272410200244060ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_release_lockowner.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. * * */ #include "config.h" #include "log.h" #include "nfs4.h" #include "nfs_core.h" #include "sal_functions.h" #include "nfs_proto_functions.h" /** * @brief NFS4_OP_RELEASE_LOCKOWNER * * This function implements the NFS4_OP_RELEASE_LOCKOWNER function. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @retval NFS4_OK or errors for NFSv4.0. * @retval NFS4ERR_NOTSUPP for NFSv4.1. */ int nfs4_op_release_lockowner(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { RELEASE_LOCKOWNER4args * const arg_RELEASE_LOCKOWNER4 = &op->nfs_argop4_u.oprelease_lockowner; RELEASE_LOCKOWNER4res * const res_RELEASE_LOCKOWNER4 = &resp->nfs_resop4_u.oprelease_lockowner; nfs_client_id_t *nfs_client_id; state_owner_t *lock_owner; state_nfs4_owner_name_t owner_name; int rc; LogDebug(COMPONENT_NFS_V4_LOCK, "Entering NFS v4 RELEASE_LOCKOWNER handler ----------------------"); resp->resop = NFS4_OP_RELEASE_LOCKOWNER; res_RELEASE_LOCKOWNER4->status = NFS4_OK; if (data->minorversion > 0) { res_RELEASE_LOCKOWNER4->status = NFS4ERR_NOTSUPP; return res_RELEASE_LOCKOWNER4->status; } /* Check clientid */ rc = nfs_client_id_get_confirmed( arg_RELEASE_LOCKOWNER4->lock_owner.clientid, &nfs_client_id); if (rc != CLIENT_ID_SUCCESS) { res_RELEASE_LOCKOWNER4->status = clientid_error_to_nfsstat(rc); goto out2; } PTHREAD_MUTEX_lock(&nfs_client_id->cid_mutex); if (!reserve_lease(nfs_client_id)) { PTHREAD_MUTEX_unlock(&nfs_client_id->cid_mutex); dec_client_id_ref(nfs_client_id); res_RELEASE_LOCKOWNER4->status = NFS4ERR_EXPIRED; goto out2; } PTHREAD_MUTEX_unlock(&nfs_client_id->cid_mutex); /* look up the lock owner and see if we can find it */ convert_nfs4_lock_owner(&arg_RELEASE_LOCKOWNER4->lock_owner, &owner_name); /* If this lock owner is not known yet, allocated * and set up a new one */ lock_owner = create_nfs4_owner(&owner_name, nfs_client_id, STATE_LOCK_OWNER_NFSV4, NULL, 0, NULL, CARE_NOT, true); if (lock_owner == NULL) { /* the owner doesn't exist, we are done */ LogDebug(COMPONENT_NFS_V4_LOCK, "lock owner does not exist"); res_RELEASE_LOCKOWNER4->status = NFS4_OK; goto out1; } res_RELEASE_LOCKOWNER4->status = release_lock_owner(lock_owner); /* Release the reference to the lock owner acquired * via create_nfs4_owner */ dec_state_owner_ref(lock_owner); out1: /* Update the lease before exit */ PTHREAD_MUTEX_lock(&nfs_client_id->cid_mutex); update_lease(nfs_client_id); PTHREAD_MUTEX_unlock(&nfs_client_id->cid_mutex); dec_client_id_ref(nfs_client_id); out2: LogDebug(COMPONENT_NFS_V4_LOCK, "Leaving NFS v4 RELEASE_LOCKOWNER handler -----------------------"); return res_RELEASE_LOCKOWNER4->status; } /* nfs4_op_release_lock_owner */ /** * @brief Free memory allocated for REELASE_LOCKOWNER result * * This function frees any memory allocated for the result of the * NFS4_OP_REELASE_LOCKOWNER operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_release_lockowner_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_remove.c000066400000000000000000000074621324272410200222110ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * \file nfs4_op_remove.c * \brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. */ #include "config.h" #include "log.h" #include "nfs4.h" #include "nfs_core.h" #include "sal_functions.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_convert.h" #include "nfs_file_handle.h" #include "sal_functions.h" #include "fsal.h" /** * @brief The NFS4_OP_REMOVE operation. * * This function implements the NFS4_OP_REMOVE operation in * NFSv4. This function can be called only from nfs4_Compound. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, pp. 372-3 */ int nfs4_op_remove(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { REMOVE4args * const arg_REMOVE4 = &op->nfs_argop4_u.opremove; REMOVE4res * const res_REMOVE4 = &resp->nfs_resop4_u.opremove; struct fsal_obj_handle *parent_obj = NULL; char *name = NULL; fsal_status_t fsal_status = {0, 0}; resp->resop = NFS4_OP_REMOVE; res_REMOVE4->status = NFS4_OK; /* Do basic checks on a filehandle * Delete arg_REMOVE4.target in directory pointed by currentFH * Make sure the currentFH is pointed a directory */ res_REMOVE4->status = nfs4_sanity_check_FH(data, DIRECTORY, false); if (res_REMOVE4->status != NFS4_OK) goto out; /* Validate and convert the UFT8 target to a regular string */ res_REMOVE4->status = nfs4_utf8string2dynamic(&arg_REMOVE4->target, UTF8_SCAN_ALL, &name); if (res_REMOVE4->status != NFS4_OK) goto out; if (nfs_in_grace()) { res_REMOVE4->status = NFS4ERR_GRACE; goto out; } /* Get the parent obj (aka the current one in the compound data) */ parent_obj = data->current_obj; /* We have to keep track of the 'change' file attribute * for reply structure */ memset(&res_REMOVE4->REMOVE4res_u.resok4.cinfo.before, 0, sizeof(changeid4)); res_REMOVE4->REMOVE4res_u.resok4.cinfo.before = fsal_get_changeid4(parent_obj); fsal_status = fsal_remove(parent_obj, name); if (FSAL_IS_ERROR(fsal_status)) { res_REMOVE4->status = nfs4_Errno_status(fsal_status); goto out; } res_REMOVE4->REMOVE4res_u.resok4.cinfo.after = fsal_get_changeid4(parent_obj); /* Operation was not atomic .... */ res_REMOVE4->REMOVE4res_u.resok4.cinfo.atomic = FALSE; /* If you reach this point, everything was ok */ res_REMOVE4->status = NFS4_OK; out: if (name) gsh_free(name); return res_REMOVE4->status; } /* nfs4_op_remove */ /** * @brief Free memory allocated for REMOVE result * * This function frees any memory allocated for the result of the * NFS4_OP_REMOVE operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_remove_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_rename.c000066400000000000000000000107221324272410200221540ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_rename.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. * * */ #include "config.h" #include "log.h" #include "nfs4.h" #include "nfs_core.h" #include "sal_functions.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_convert.h" #include "nfs_file_handle.h" #include "sal_functions.h" #include "fsal.h" /** * @brief The NFS4_OP_RENAME operation * * This function implemenats the NFS4_OP_RENAME operation. This * function can be called only from nfs4_Compound * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 373 */ int nfs4_op_rename(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { RENAME4args * const arg_RENAME4 = &op->nfs_argop4_u.oprename; RENAME4res * const res_RENAME4 = &resp->nfs_resop4_u.oprename; struct fsal_obj_handle *dst_obj = NULL; struct fsal_obj_handle *src_obj = NULL; nfsstat4 status = NFS4_OK; fsal_status_t fsal_status = {0, 0}; char *oldname = NULL; char *newname = NULL; resp->resop = NFS4_OP_RENAME; res_RENAME4->status = NFS4_OK; /* Read and validate oldname and newname from uft8 strings. */ res_RENAME4->status = nfs4_utf8string2dynamic(&arg_RENAME4->oldname, UTF8_SCAN_ALL, &oldname); if (res_RENAME4->status != NFS4_OK) goto out; res_RENAME4->status = nfs4_utf8string2dynamic(&arg_RENAME4->newname, UTF8_SCAN_ALL, &newname); if (res_RENAME4->status != NFS4_OK) goto out; /* Do basic checks on a filehandle */ res_RENAME4->status = nfs4_sanity_check_FH(data, DIRECTORY, false); if (res_RENAME4->status != NFS4_OK) goto out; res_RENAME4->status = nfs4_sanity_check_saved_FH(data, DIRECTORY, false); if (res_RENAME4->status != NFS4_OK) goto out; /* Check that both handles are in the same export. */ if (op_ctx->ctx_export != NULL && data->saved_export != NULL && op_ctx->ctx_export->export_id != data->saved_export->export_id) { res_RENAME4->status = NFS4ERR_XDEV; goto out; } if (nfs_in_grace()) { res_RENAME4->status = NFS4ERR_GRACE; goto out; } dst_obj = data->current_obj; src_obj = data->saved_obj; res_RENAME4->RENAME4res_u.resok4.source_cinfo.before = fsal_get_changeid4(src_obj); res_RENAME4->RENAME4res_u.resok4.target_cinfo.before = fsal_get_changeid4(dst_obj); status = nfs4_Errno_status(fsal_rename(src_obj, oldname, dst_obj, newname)); if (status != NFS4_OK) { res_RENAME4->status = status; goto out; } /* If you reach this point, then everything was alright * For the change_info4, get the 'change' attributes * for both directories */ res_RENAME4->RENAME4res_u.resok4.source_cinfo.after = fsal_get_changeid4(src_obj); res_RENAME4->RENAME4res_u.resok4.target_cinfo.after = fsal_get_changeid4(dst_obj); res_RENAME4->RENAME4res_u.resok4.target_cinfo.atomic = FALSE; res_RENAME4->RENAME4res_u.resok4.source_cinfo.atomic = FALSE; res_RENAME4->status = nfs4_Errno_status(fsal_status); out: if (oldname) gsh_free(oldname); if (newname) gsh_free(newname); return res_RENAME4->status; } /** * @brief Free memory allocated for RENAME result * * This function frees any memory allocated for the result of the * NFS4_OP_RENAME operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_rename_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_renew.c000066400000000000000000000071111324272410200220230ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_renew.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. * */ #include "config.h" #include "log.h" #include "nfs4.h" #include "sal_functions.h" #include "nfs_proto_functions.h" #include "nfs_core.h" #include "nfs_rpc_callback.h" #include "server_stats.h" /** * @brief The NFS4_OP_RENEW operation. * * This function implements the NFS4_OP_RENEW operation. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @retval NFS4_OK or errors for NFSv4.0. * @retval NFS4ERR_NOTSUPP for NFSv4.1. * * @see nfs4_Compound * */ int nfs4_op_renew(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { RENEW4args * const arg_RENEW4 = &op->nfs_argop4_u.oprenew; RENEW4res * const res_RENEW4 = &resp->nfs_resop4_u.oprenew; nfs_client_id_t *clientid; int rc; /* Lock are not supported */ memset(resp, 0, sizeof(struct nfs_resop4)); resp->resop = NFS4_OP_RENEW; if (data->minorversion > 0) { res_RENEW4->status = NFS4ERR_NOTSUPP; return res_RENEW4->status; } /* Tell the admin what I am doing... */ LogFullDebug(COMPONENT_CLIENTID, "RENEW Client id = %" PRIx64, arg_RENEW4->clientid); /* Is this an existing client id ? */ rc = nfs_client_id_get_confirmed(arg_RENEW4->clientid, &clientid); if (rc != CLIENT_ID_SUCCESS) { /* Unknown client id */ res_RENEW4->status = clientid_error_to_nfsstat(rc); return res_RENEW4->status; } PTHREAD_MUTEX_lock(&clientid->cid_mutex); if (!reserve_lease(clientid)) { res_RENEW4->status = NFS4ERR_EXPIRED; } else { update_lease(clientid); /* update the lease, check the state of callback * path and return correct error */ if (nfs_param.nfsv4_param.allow_delegations && get_cb_chan_down(clientid) && clientid->curr_deleg_grants) { res_RENEW4->status = NFS4ERR_CB_PATH_DOWN; /* Set the time for first PATH_DOWN response */ if (clientid->first_path_down_resp_time == 0) clientid->first_path_down_resp_time = time(NULL); } else { res_RENEW4->status = NFS4_OK; /* Reset */ clientid->first_path_down_resp_time = 0; } } PTHREAD_MUTEX_unlock(&clientid->cid_mutex); dec_client_id_ref(clientid); return res_RENEW4->status; } /* nfs4_op_renew */ /** * @brief Free memory allocated for RENEW result * * This function frees any memory allocated for the result of the * NFS4_OP_RENEW operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_renew_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_restorefh.c000066400000000000000000000113231324272410200227040ustar00rootroot00000000000000/* * Vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_restorefh.c * @brief The NFS4_OP_RESTOREFH operation. * * Routines used for managing the NFS4_OP_RESTOREFH operation. */ #include "config.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_file_handle.h" #include "export_mgr.h" /** * * @brief The NFS4_OP_RESTOREFH operation. * * This functions handles the NFS4_OP_RESTOREFH operation in * NFSv4. This function can be called only from nfs4_Compound. This * operation replaces the current FH with the previously saved FH. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 373 * * @see nfs4_Compound * */ int nfs4_op_restorefh(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { RESTOREFH4res * const res_RESTOREFH = &resp->nfs_resop4_u.oprestorefh; /* First of all, set the reply to zero to make sure it contains no parasite information */ memset(resp, 0, sizeof(struct nfs_resop4)); resp->resop = NFS4_OP_RESTOREFH; res_RESTOREFH->status = NFS4_OK; LogFullDebugOpaque(COMPONENT_FILEHANDLE, "Saved FH %s", LEN_FH_STR, data->savedFH.nfs_fh4_val, data->savedFH.nfs_fh4_len); /* If there is no savedFH, then return an error */ if (nfs4_Is_Fh_Empty(&(data->savedFH)) == NFS4ERR_NOFILEHANDLE) { /* There is no current FH, return NFS4ERR_RESTOREFH * (cg RFC3530, page 202) */ res_RESTOREFH->status = NFS4ERR_RESTOREFH; return res_RESTOREFH->status; } /* Do basic checks on saved filehandle */ res_RESTOREFH->status = nfs4_sanity_check_saved_FH(data, NO_FILE_TYPE, true); if (res_RESTOREFH->status != NFS4_OK) return res_RESTOREFH->status; /* Determine if we can get a new export reference. If there is * no saved export, don't get a reference to it. */ if (data->saved_export != NULL) { if (!export_ready(data->saved_export)) { /* The SavedFH export has gone bad. */ res_RESTOREFH->status = NFS4ERR_STALE; return res_RESTOREFH->status; } get_gsh_export_ref(data->saved_export); } /* Copy the data from saved FH to current FH */ memcpy(data->currentFH.nfs_fh4_val, data->savedFH.nfs_fh4_val, data->savedFH.nfs_fh4_len); data->currentFH.nfs_fh4_len = data->savedFH.nfs_fh4_len; if (op_ctx->ctx_export != NULL) put_gsh_export(op_ctx->ctx_export); /* Restore the export information */ op_ctx->ctx_export = data->saved_export; if (op_ctx->ctx_export != NULL) op_ctx->fsal_export = op_ctx->ctx_export->fsal_export; *op_ctx->export_perms = data->saved_export_perms; /* No need to call nfs4_SetCompoundExport or nfs4_MakeCred * because we are restoring saved information, and the * credential checking may be skipped. */ /* Update the current entry */ set_current_entry(data, data->saved_obj); /* Restore the saved stateid */ data->current_stateid = data->saved_stateid; data->current_stateid_valid = data->saved_stateid_valid; /* Make RESTOREFH work right for DS handle */ if (data->current_ds != NULL) { data->current_ds = data->saved_ds; data->current_filetype = data->saved_filetype; ds_handle_get_ref(data->current_ds); } if (isFullDebug(COMPONENT_NFS_V4)) { char str[LEN_FH_STR]; sprint_fhandle4(str, &data->currentFH); LogFullDebug(COMPONENT_NFS_V4, "RESTORE FH: Current FH %s", str); } return NFS4_OK; } /* nfs4_op_restorefh */ /** * @brief Free memory allocated for RESTOREFH result * * This function frees any memory allocated for the result of the * NFS4_OP_RESTOREFH operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_restorefh_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_savefh.c000066400000000000000000000103701324272410200221600ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_savefh.c * @brief Routines used for managing the NFS4_OP_SAVEFH operation. * * Routines used for managing the NFS4_OP_SAVEFH operation. */ #include "config.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_file_handle.h" #include "export_mgr.h" /** * * @brief the NFS4_OP_SAVEFH operation * * This functions handles the NFS4_OP_SAVEFH operation in NFSv4. This * function can be called only from nfs4_Compound. The operation set * the savedFH with the value of the currentFH. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 373 * * @see nfs4_Compound * */ int nfs4_op_savefh(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { SAVEFH4res * const res_SAVEFH = &resp->nfs_resop4_u.opsavefh; /* First of all, set the reply to zero to make sure it contains no * parasite information */ memset(resp, 0, sizeof(struct nfs_resop4)); resp->resop = NFS4_OP_SAVEFH; res_SAVEFH->status = NFS4_OK; /* Do basic checks on a filehandle */ res_SAVEFH->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, true); if (res_SAVEFH->status != NFS4_OK) return res_SAVEFH->status; /* If the savefh is not allocated, do it now */ if (data->savedFH.nfs_fh4_val == NULL) nfs4_AllocateFH(&data->savedFH); /* Determine if we can get a new export reference. If there is * no op_ctx->ctx_export, don't get a reference. */ if (op_ctx->ctx_export != NULL) { if (!export_ready(op_ctx->ctx_export)) { /* The CurrentFH export has gone bad. */ res_SAVEFH->status = NFS4ERR_STALE; return res_SAVEFH->status; } get_gsh_export_ref(op_ctx->ctx_export); } /* Copy the data from current FH to saved FH */ memcpy(data->savedFH.nfs_fh4_val, data->currentFH.nfs_fh4_val, data->currentFH.nfs_fh4_len); data->savedFH.nfs_fh4_len = data->currentFH.nfs_fh4_len; /* If saved and current entry are equal, skip the following. */ if (data->saved_obj != data->current_obj) { set_saved_entry(data, data->current_obj); /* Make SAVEFH work right for DS handle */ if (data->current_ds != NULL) { data->saved_ds = data->current_ds; ds_handle_get_ref(data->saved_ds); } } /* Save the current stateid */ data->saved_stateid = data->current_stateid; data->saved_stateid_valid = data->current_stateid_valid; /* If old SavedFH had a related export, release reference. */ if (data->saved_export != NULL) put_gsh_export(data->saved_export); /* Save the export information (reference already taken above). */ data->saved_export = op_ctx->ctx_export; data->saved_export_perms = *op_ctx->export_perms; if (isFullDebug(COMPONENT_NFS_V4)) { char str[LEN_FH_STR]; sprint_fhandle4(str, &data->savedFH); LogFullDebug(COMPONENT_NFS_V4, "SAVE FH: Saved FH %s", str); } res_SAVEFH->status = NFS4_OK; return NFS4_OK; } /* nfs4_op_savefh */ /** * @brief Free memory allocated for SAVEFH result * * This function frees any memory allocated for the result of the * NFS4_OP_SAVEFH function. * * @param[in,out] resp nfs4_op results */ void nfs4_op_savefh_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_secinfo.c000066400000000000000000000217441324272410200223410ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_secinfo.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. */ #include "config.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_creds.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_convert.h" #include "sal_functions.h" #include "export_mgr.h" /** * @brief NFSv4 SECINFO operation * * This function impelments the NFSv4 SECINFO operation. * * @param[in] op Operation reqest * @param[in,out] data Compound data * @param[out] resp Response * * @return NFS status codes. */ int nfs4_op_secinfo(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { SECINFO4args * const arg_SECINFO4 = &op->nfs_argop4_u.opsecinfo; SECINFO4res * const res_SECINFO4 = &resp->nfs_resop4_u.opsecinfo; secinfo4 *resok_val; char *secinfo_fh_name = NULL; fsal_status_t fsal_status = {0, 0}; struct fsal_obj_handle *obj_src = NULL; #ifdef _HAVE_GSSAPI sec_oid4 v5oid = { krb5oid.length, (char *)krb5oid.elements }; #endif /* _HAVE_GSSAPI */ int num_entry = 0; struct export_perms save_export_perms = { 0, }; struct gsh_export *saved_gsh_export = NULL; resp->resop = NFS4_OP_SECINFO; res_SECINFO4->status = NFS4_OK; /* Read name from uft8 strings, if one is empty then returns * NFS4ERR_INVAL */ res_SECINFO4->status = nfs4_utf8string2dynamic(&arg_SECINFO4->name, UTF8_SCAN_ALL, &secinfo_fh_name); if (res_SECINFO4->status != NFS4_OK) goto out; /* Do basic checks on a filehandle SecInfo is done only on a directory */ res_SECINFO4->status = nfs4_sanity_check_FH(data, DIRECTORY, false); if (res_SECINFO4->status != NFS4_OK) goto out; fsal_status = fsal_lookup(data->current_obj, secinfo_fh_name, &obj_src, NULL); if (obj_src == NULL) { res_SECINFO4->status = nfs4_Errno_status(fsal_status); goto out; } /* Get state lock for junction_export */ PTHREAD_RWLOCK_rdlock(&obj_src->state_hdl->state_lock); if (obj_src->type == DIRECTORY && obj_src->state_hdl->dir.junction_export != NULL) { /* Handle junction */ struct gsh_export *junction_export = obj_src->state_hdl->dir.junction_export; struct fsal_obj_handle *obj = NULL; /* Try to get a reference to the export. */ if (!export_ready(junction_export)) { /* Export has gone bad. */ LogDebug(COMPONENT_EXPORT, "NFS4ERR_STALE On Export_Id %d Pseudo %s", junction_export->export_id, junction_export->pseudopath); res_SECINFO4->status = NFS4ERR_STALE; PTHREAD_RWLOCK_unlock(&obj_src->state_hdl->state_lock); goto out; } get_gsh_export_ref(junction_export); PTHREAD_RWLOCK_unlock(&obj_src->state_hdl->state_lock); /* Save the compound data context */ save_export_perms = *op_ctx->export_perms; saved_gsh_export = op_ctx->ctx_export; op_ctx->ctx_export = junction_export; op_ctx->fsal_export = op_ctx->ctx_export->fsal_export; /* Build credentials */ res_SECINFO4->status = nfs4_export_check_access(data->req); /* Test for access error (export should not be visible). */ if (res_SECINFO4->status == NFS4ERR_ACCESS) { /* If return is NFS4ERR_ACCESS then this client doesn't * have access to this export, return NFS4ERR_NOENT to * hide it. It was not visible in READDIR response. */ LogDebug(COMPONENT_EXPORT, "NFS4ERR_ACCESS Hiding Export_Id %d Pseudo %s with NFS4ERR_NOENT", op_ctx->ctx_export->export_id, op_ctx->ctx_export->pseudopath); res_SECINFO4->status = NFS4ERR_NOENT; goto out; } /* Only other error is NFS4ERR_WRONGSEC which is actually * what we expect here. Finish crossing the junction. */ fsal_status = nfs_export_get_root_entry(op_ctx->ctx_export, &obj); if (FSAL_IS_ERROR(fsal_status)) { LogMajor(COMPONENT_EXPORT, "PSEUDO FS JUNCTION TRAVERSAL: Failed to get root for %s, id=%d, status = %s", op_ctx->ctx_export->pseudopath, op_ctx->ctx_export->export_id, fsal_err_txt(fsal_status)); res_SECINFO4->status = nfs4_Errno_status(fsal_status); goto out; } LogDebug(COMPONENT_EXPORT, "PSEUDO FS JUNCTION TRAVERSAL: Crossed to %s, id=%d for name=%s", op_ctx->ctx_export->pseudopath, op_ctx->ctx_export->export_id, secinfo_fh_name); /* Swap in the obj on the other side of the junction. */ obj_src->obj_ops.put_ref(obj_src); obj_src = obj; } else { /* Not a junction, release lock */ PTHREAD_RWLOCK_unlock(&obj_src->state_hdl->state_lock); } /* Get the number of entries */ if (op_ctx->export_perms->options & EXPORT_OPTION_AUTH_NONE) num_entry++; if (op_ctx->export_perms->options & EXPORT_OPTION_AUTH_UNIX) num_entry++; if (op_ctx->export_perms->options & EXPORT_OPTION_RPCSEC_GSS_NONE) num_entry++; if (op_ctx->export_perms->options & EXPORT_OPTION_RPCSEC_GSS_INTG) num_entry++; if (op_ctx->export_perms->options & EXPORT_OPTION_RPCSEC_GSS_PRIV) num_entry++; resok_val = gsh_calloc(num_entry, sizeof(secinfo4)); res_SECINFO4->SECINFO4res_u.resok4.SECINFO4resok_val = resok_val; /** * @todo We have the opportunity to associate a preferred * security triple with a specific fs/export. For now, list * all implemented. */ int idx = 0; /* List the security flavors in the order we prefer */ #ifdef _HAVE_GSSAPI if (op_ctx->export_perms->options & EXPORT_OPTION_RPCSEC_GSS_PRIV) { resok_val[idx].flavor = RPCSEC_GSS; resok_val[idx].secinfo4_u.flavor_info.service = RPCSEC_GSS_SVC_PRIVACY; resok_val[idx].secinfo4_u.flavor_info.qop = GSS_C_QOP_DEFAULT; resok_val[idx++].secinfo4_u.flavor_info.oid = v5oid; } if (op_ctx->export_perms->options & EXPORT_OPTION_RPCSEC_GSS_INTG) { resok_val[idx].flavor = RPCSEC_GSS; resok_val[idx].secinfo4_u.flavor_info.service = RPCSEC_GSS_SVC_INTEGRITY; resok_val[idx].secinfo4_u.flavor_info.qop = GSS_C_QOP_DEFAULT; resok_val[idx++].secinfo4_u.flavor_info.oid = v5oid; } if (op_ctx->export_perms->options & EXPORT_OPTION_RPCSEC_GSS_NONE) { resok_val[idx].flavor = RPCSEC_GSS; resok_val[idx].secinfo4_u.flavor_info.service = RPCSEC_GSS_SVC_NONE; resok_val[idx].secinfo4_u.flavor_info.qop = GSS_C_QOP_DEFAULT; resok_val[idx++].secinfo4_u.flavor_info.oid = v5oid; } #endif /* _HAVE_GSSAPI */ if (op_ctx->export_perms->options & EXPORT_OPTION_AUTH_UNIX) resok_val[idx++].flavor = AUTH_UNIX; if (op_ctx->export_perms->options & EXPORT_OPTION_AUTH_NONE) resok_val[idx++].flavor = AUTH_NONE; res_SECINFO4->SECINFO4res_u.resok4.SECINFO4resok_len = idx; if (data->minorversion != 0) { /* Need to clear out CurrentFH */ set_current_entry(data, NULL); data->currentFH.nfs_fh4_len = 0; /* Release CurrentFH reference to export. */ if (op_ctx->ctx_export) { put_gsh_export(op_ctx->ctx_export); op_ctx->ctx_export = NULL; op_ctx->fsal_export = NULL; } if (saved_gsh_export != NULL) { /* Don't need saved export */ put_gsh_export(saved_gsh_export); saved_gsh_export = NULL; } } res_SECINFO4->status = NFS4_OK; out: if (saved_gsh_export != NULL) { /* Restore export stuff */ if (op_ctx->ctx_export) put_gsh_export(op_ctx->ctx_export); *op_ctx->export_perms = save_export_perms; op_ctx->ctx_export = saved_gsh_export; op_ctx->fsal_export = op_ctx->ctx_export->fsal_export; /* Restore creds */ if (nfs_req_creds(data->req) != NFS4_OK) { LogCrit(COMPONENT_EXPORT, "Failure to restore creds"); } } if (obj_src) obj_src->obj_ops.put_ref(obj_src); if (secinfo_fh_name) gsh_free(secinfo_fh_name); return res_SECINFO4->status; } /* nfs4_op_secinfo */ /** * @brief Free memory allocated for SECINFO result * * This function frees any memory allocated for the result of the * NFS4_OP_SECINFO operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_secinfo_Free(nfs_resop4 *res) { SECINFO4res *resp = &res->nfs_resop4_u.opsecinfo; if ((resp->status == NFS4_OK) && (resp->SECINFO4res_u.resok4.SECINFO4resok_val)) { gsh_free(resp->SECINFO4res_u.resok4.SECINFO4resok_val); resp->SECINFO4res_u.resok4.SECINFO4resok_val = NULL; } } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_secinfo_no_name.c000066400000000000000000000125541324272410200240340ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_secinfo_no_name.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. */ #include "config.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "sal_functions.h" #include "export_mgr.h" /** * @brief NFSv4 SECINFO_NO_NAME operation * * This function impelments the NFSv4 SECINFO_NO_NAME operation. * * @param[in] op Operation reqest * @param[in,out] data Compound data * @param[out] resp Response * * @return NFS status codes. */ int nfs4_op_secinfo_no_name(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { SECINFO_NO_NAME4res * const res_SECINFO_NO_NAME4 = &resp->nfs_resop4_u.opsecinfo_no_name; secinfo4 *resok_val; #ifdef _HAVE_GSSAPI sec_oid4 v5oid = { krb5oid.length, (char *)krb5oid.elements }; #endif /* _HAVE_GSSAPI */ int num_entry = 0; res_SECINFO_NO_NAME4->status = NFS4_OK; /* Do basic checks on a filehandle */ res_SECINFO_NO_NAME4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false); if (res_SECINFO_NO_NAME4->status != NFS4_OK) goto out; if (op->nfs_argop4_u.opsecinfo_no_name == SECINFO_STYLE4_PARENT) { /* Use LOOKUPP to get the parent into CurrentFH. */ res_SECINFO_NO_NAME4->status = nfs4_op_lookupp(op, data, resp); if (res_SECINFO_NO_NAME4->status != NFS4_OK) goto out; } /* Get the number of entries */ if (op_ctx->export_perms->options & EXPORT_OPTION_AUTH_NONE) num_entry++; if (op_ctx->export_perms->options & EXPORT_OPTION_AUTH_UNIX) num_entry++; if (op_ctx->export_perms->options & EXPORT_OPTION_RPCSEC_GSS_NONE) num_entry++; if (op_ctx->export_perms->options & EXPORT_OPTION_RPCSEC_GSS_INTG) num_entry++; if (op_ctx->export_perms->options & EXPORT_OPTION_RPCSEC_GSS_PRIV) num_entry++; resok_val = gsh_calloc(num_entry, sizeof(secinfo4)); res_SECINFO_NO_NAME4->SECINFO4res_u.resok4.SECINFO4resok_val = resok_val; /** * @todo We give here the order in which the client should try * different authentifications. Might want to give it in the * order given in the config. */ int idx = 0; #ifdef _HAVE_GSSAPI if (op_ctx->export_perms->options & EXPORT_OPTION_RPCSEC_GSS_PRIV) { resok_val[idx].flavor = RPCSEC_GSS; resok_val[idx].secinfo4_u.flavor_info.service = RPCSEC_GSS_SVC_PRIVACY; resok_val[idx].secinfo4_u.flavor_info.qop = GSS_C_QOP_DEFAULT; resok_val[idx++].secinfo4_u.flavor_info.oid = v5oid; } if (op_ctx->export_perms->options & EXPORT_OPTION_RPCSEC_GSS_INTG) { resok_val[idx].flavor = RPCSEC_GSS; resok_val[idx].secinfo4_u.flavor_info.service = RPCSEC_GSS_SVC_INTEGRITY; resok_val[idx].secinfo4_u.flavor_info.qop = GSS_C_QOP_DEFAULT; resok_val[idx++].secinfo4_u.flavor_info.oid = v5oid; } if (op_ctx->export_perms->options & EXPORT_OPTION_RPCSEC_GSS_NONE) { resok_val[idx].flavor = RPCSEC_GSS; resok_val[idx].secinfo4_u.flavor_info.service = RPCSEC_GSS_SVC_NONE; resok_val[idx].secinfo4_u.flavor_info.qop = GSS_C_QOP_DEFAULT; resok_val[idx++].secinfo4_u.flavor_info.oid = v5oid; } #endif /* _HAVE_GSSAPI */ if (op_ctx->export_perms->options & EXPORT_OPTION_AUTH_UNIX) resok_val[idx++].flavor = AUTH_UNIX; if (op_ctx->export_perms->options & EXPORT_OPTION_AUTH_NONE) resok_val[idx++].flavor = AUTH_NONE; res_SECINFO_NO_NAME4->SECINFO4res_u.resok4.SECINFO4resok_len = idx; /* Need to clear out CurrentFH */ set_current_entry(data, NULL); data->currentFH.nfs_fh4_len = 0; /* Release CurrentFH reference to export. */ if (op_ctx->ctx_export) { put_gsh_export(op_ctx->ctx_export); op_ctx->ctx_export = NULL; op_ctx->fsal_export = NULL; } res_SECINFO_NO_NAME4->status = NFS4_OK; out: resp->resop = NFS4_OP_SECINFO_NO_NAME; return res_SECINFO_NO_NAME4->status; } /* nfs4_op_secinfo_no_name */ /** * @brief Free memory allocated for SECINFO_NO_NAME result * * This function frees any memory allocated for the result of the * NFS4_OP_SECINFO_NO_NAME operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_secinfo_no_name_Free(nfs_resop4 *res) { SECINFO_NO_NAME4res *resp = &res->nfs_resop4_u.opsecinfo_no_name; if ((resp->status == NFS4_OK) && (resp->SECINFO4res_u.resok4.SECINFO4resok_val)) { gsh_free(resp->SECINFO4res_u.resok4.SECINFO4resok_val); resp->SECINFO4res_u.resok4.SECINFO4resok_val = NULL; } } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_sequence.c000066400000000000000000000161101324272410200225120ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_sequence.c * @brief Routines used for managing the NFS4_OP_SEQUENCE operation */ #include "config.h" #include "fsal.h" #include "sal_functions.h" #include "nfs_rpc_callback.h" #include "nfs_convert.h" /** * @brief the NFS4_OP_SEQUENCE operation * * @param[in] op nfs4_op arguments * @param[in,out] data Compound request's data * @param[out] resp nfs4_op results * * @return per RFC5661, p. 374 * * @see nfs4_Compound * */ int nfs4_op_sequence(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { SEQUENCE4args * const arg_SEQUENCE4 = &op->nfs_argop4_u.opsequence; SEQUENCE4res * const res_SEQUENCE4 = &resp->nfs_resop4_u.opsequence; nfs41_session_t *session; nfs41_session_slot_t *slot; resp->resop = NFS4_OP_SEQUENCE; res_SEQUENCE4->sr_status = NFS4_OK; if (data->minorversion == 0) { res_SEQUENCE4->sr_status = NFS4ERR_INVAL; return res_SEQUENCE4->sr_status; } /* OP_SEQUENCE is always the first operation of the request */ if (data->oppos != 0) { res_SEQUENCE4->sr_status = NFS4ERR_SEQUENCE_POS; return res_SEQUENCE4->sr_status; } if (!nfs41_Session_Get_Pointer(arg_SEQUENCE4->sa_sessionid, &session)) { res_SEQUENCE4->sr_status = NFS4ERR_BADSESSION; LogDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID, "SEQUENCE returning status %s", nfsstat4_to_str(res_SEQUENCE4->sr_status)); return res_SEQUENCE4->sr_status; } /* session->refcount +1 */ LogDebug(COMPONENT_SESSIONS, "SEQUENCE session=%p", session); /* Check if lease is expired and reserve it */ PTHREAD_MUTEX_lock(&session->clientid_record->cid_mutex); if (!reserve_lease(session->clientid_record)) { PTHREAD_MUTEX_unlock(&session->clientid_record->cid_mutex); dec_session_ref(session); res_SEQUENCE4->sr_status = NFS4ERR_EXPIRED; LogDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID, "SEQUENCE returning status %s", nfsstat4_to_str(res_SEQUENCE4->sr_status)); return res_SEQUENCE4->sr_status; } data->preserved_clientid = session->clientid_record; PTHREAD_MUTEX_unlock(&session->clientid_record->cid_mutex); /* Check is slot is compliant with ca_maxrequests */ if (arg_SEQUENCE4->sa_slotid >= session->fore_channel_attrs.ca_maxrequests) { dec_session_ref(session); res_SEQUENCE4->sr_status = NFS4ERR_BADSLOT; LogDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID, "SEQUENCE returning status %s", nfsstat4_to_str(res_SEQUENCE4->sr_status)); return res_SEQUENCE4->sr_status; } /* By default, no DRC replay */ data->use_drc = false; slot = &session->fc_slots[arg_SEQUENCE4->sa_slotid]; PTHREAD_MUTEX_lock(&slot->lock); if (slot->sequence + 1 != arg_SEQUENCE4->sa_sequenceid) { if (slot->sequence == arg_SEQUENCE4->sa_sequenceid) { #if IMPLEMENT_CACHETHIS /** @todo * * Ganesha always caches result anyway so ignore * cachethis */ if (slot->cache_used) { #endif /* Replay operation through the DRC */ data->use_drc = true; data->cached_res = &slot->cached_result; LogFullDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID, "Use sesson slot %" PRIu32 "=%p for DRC", arg_SEQUENCE4->sa_slotid, data->cached_res); PTHREAD_MUTEX_unlock(&slot->lock); dec_session_ref(session); res_SEQUENCE4->sr_status = NFS4_OK; return res_SEQUENCE4->sr_status; #if IMPLEMENT_CACHETHIS } else { /* Illegal replay */ PTHREAD_MUTEX_unlock(&slot->lock); dec_session_ref(session); res_SEQUENCE4->sr_status = NFS4ERR_RETRY_UNCACHED_REP; LogDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID, "SEQUENCE returning status %s", nfsstat4_to_str( res_SEQUENCE4->sr_status)); return res_SEQUENCE4->sr_status; } #endif } PTHREAD_MUTEX_unlock(&slot->lock); dec_session_ref(session); res_SEQUENCE4->sr_status = NFS4ERR_SEQ_MISORDERED; LogDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID, "SEQUENCE returning status %s", nfsstat4_to_str(res_SEQUENCE4->sr_status)); return res_SEQUENCE4->sr_status; } /* Keep memory of the session in the COMPOUND's data */ data->session = session; /* Record the sequenceid and slotid in the COMPOUND's data */ data->sequence = arg_SEQUENCE4->sa_sequenceid; data->slot = arg_SEQUENCE4->sa_slotid; /* Update the sequence id within the slot */ slot->sequence += 1; memcpy(res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_sessionid, arg_SEQUENCE4->sa_sessionid, NFS4_SESSIONID_SIZE); res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_sequenceid = slot->sequence; res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_slotid = arg_SEQUENCE4->sa_slotid; res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_highest_slotid = session->nb_slots - 1; res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_target_highest_slotid = session->fore_channel_attrs.ca_maxrequests - 1; res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_status_flags = 0; if (nfs_rpc_get_chan(session->clientid_record, 0) == NULL) { res_SEQUENCE4->SEQUENCE4res_u.sr_resok4.sr_status_flags |= SEQ4_STATUS_CB_PATH_DOWN; } #if IMPLEMENT_CACHETHIS /* Ganesha always caches result anyway so ignore cachethis */ if (arg_SEQUENCE4->sa_cachethis) { #endif data->cached_res = &slot->cached_result; slot->cache_used = true; LogFullDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID, "Use sesson slot %" PRIu32 "=%p for DRC", arg_SEQUENCE4->sa_slotid, data->cached_res); #if IMPLEMENT_CACHETHIS } else { data->cached_res = NULL; slot->cache_used = false; LogFullDebugAlt(COMPONENT_SESSIONS, COMPONENT_CLIENTID, "Don't use sesson slot %" PRIu32 "=NULL for DRC", arg_SEQUENCE4->sa_slotid); } #endif PTHREAD_MUTEX_unlock(&slot->lock); /* If we were successful, stash the clientid in the request * context. */ op_ctx->clientid = &data->session->clientid; res_SEQUENCE4->sr_status = NFS4_OK; return res_SEQUENCE4->sr_status; } /* nfs41_op_sequence */ /** * @brief Free memory allocated for SEQUENCE result * * This function frees any memory allocated for the result of the * NFS4_OP_SEQUENCE operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_sequence_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_set_ssv.c000066400000000000000000000051471324272410200224000ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_set_ssv.c * @brief Routines for the NFS4_OP_SET_SSV operation * * Routines for the NFS4_OP_SEQUENCE operation. * * */ #include "config.h" #include #include #include #include #include "log.h" #include "gsh_rpc.h" #include "nfs4.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_file_handle.h" /** * * @brief The NFS4_OP_SET_SSV operation * * This functions handles the NFS4_OP_SET_SSV operation in NFSv4. This * function can be called only from nfs4_Compound. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, pp. 374-5 * * @see nfs4_Compound * */ int nfs4_op_set_ssv(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { SET_SSV4args * const arg_SET_SSV4 __attribute__ ((unused)) = &op->nfs_argop4_u.opset_ssv; SET_SSV4res * const res_SET_SSV4 = &resp->nfs_resop4_u.opset_ssv; resp->resop = NFS4_OP_SET_SSV; res_SET_SSV4->ssr_status = NFS4_OK; if (data->minorversion == 0) { res_SET_SSV4->ssr_status = NFS4ERR_INVAL; return res_SET_SSV4->ssr_status; } /* I know this is pretty dirty... * But this is an early implementation... */ return res_SET_SSV4->ssr_status; } /* nfs41_op_set_ssv */ /** * @brief Free memory allocated for SET_SSV result * * This function frees any memory allocated for the result of the * NFS4_OP_SET_SSV operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_set_ssv_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_setattr.c000066400000000000000000000160211324272410200223710ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_setattr.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. * * */ #include "config.h" #include "log.h" #include "nfs4.h" #include "nfs_core.h" #include "fsal.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_convert.h" #include "sal_functions.h" #include "nfs_creds.h" /** * @brief The NFS4_OP_SETATTR operation. * * This functions handles the NFS4_OP_SETATTR operation in NFSv4. This * function can be called only from nfs4_Compound * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 373-4 */ int nfs4_op_setattr(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { SETATTR4args * const arg_SETATTR4 = &op->nfs_argop4_u.opsetattr; SETATTR4res * const res_SETATTR4 = &resp->nfs_resop4_u.opsetattr; struct attrlist sattr; fsal_status_t fsal_status = {0, 0}; const char *tag = "SETATTR"; state_t *state_found = NULL; state_t *state_open = NULL; struct fsal_obj_handle *obj = NULL; resp->resop = NFS4_OP_SETATTR; res_SETATTR4->status = NFS4_OK; /* Do basic checks on a filehandle */ res_SETATTR4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false); if (res_SETATTR4->status != NFS4_OK) return res_SETATTR4->status; /* Don't allow attribute change while we are in grace period. * Required for delegation reclaims and may be needed for other * reclaimable states as well. */ if (nfs_in_grace()) { res_SETATTR4->status = NFS4ERR_GRACE; return res_SETATTR4->status; } /* Get only attributes that are allowed to be read */ if (!nfs4_Fattr_Check_Access (&arg_SETATTR4->obj_attributes, FATTR4_ATTR_WRITE)) { res_SETATTR4->status = NFS4ERR_INVAL; return res_SETATTR4->status; } /* Ask only for supported attributes */ if (!nfs4_Fattr_Supported(&arg_SETATTR4->obj_attributes)) { res_SETATTR4->status = NFS4ERR_ATTRNOTSUPP; return res_SETATTR4->status; } /* Convert the fattr4 in the request to a fsal sattr structure */ res_SETATTR4->status = nfs4_Fattr_To_FSAL_attr(&sattr, &arg_SETATTR4->obj_attributes, data); if (res_SETATTR4->status != NFS4_OK) return res_SETATTR4->status; /* Trunc may change Xtime so we have to start with trunc and * finish by the mtime and atime */ if ((FSAL_TEST_MASK(sattr.valid_mask, ATTR_SIZE)) || (FSAL_TEST_MASK(sattr.valid_mask, ATTR4_SPACE_RESERVED))) { /* Setting the size of a directory is prohibited */ if (data->current_filetype == DIRECTORY) { res_SETATTR4->status = NFS4ERR_ISDIR; return res_SETATTR4->status; } /* Object should be a file */ if (data->current_obj->type != REGULAR_FILE) { res_SETATTR4->status = NFS4ERR_INVAL; return res_SETATTR4->status; } obj = data->current_obj; /* Check stateid correctness and get pointer to state */ res_SETATTR4->status = nfs4_Check_Stateid(&arg_SETATTR4->stateid, data->current_obj, &state_found, data, STATEID_SPECIAL_ANY, 0, false, tag); if (res_SETATTR4->status != NFS4_OK) return res_SETATTR4->status; /* NB: After this point, if state_found == NULL, then * the stateid is all-0 or all-1 */ if (state_found != NULL) { switch (state_found->state_type) { case STATE_TYPE_SHARE: state_open = state_found; /* Note this causes an extra refcount, but it * simplifies logic below. */ inc_state_t_ref(state_open); break; case STATE_TYPE_LOCK: state_open = state_found->state_data.lock.openstate; inc_state_t_ref(state_open); break; case STATE_TYPE_DELEG: state_open = NULL; break; default: res_SETATTR4->status = NFS4ERR_BAD_STATEID; return res_SETATTR4->status; } /* This is a size operation, this means that * the file MUST have been opened for writing */ if (state_open != NULL && (state_open->state_data.share.share_access & OPEN4_SHARE_ACCESS_WRITE) == 0) { /* Bad open mode, return NFS4ERR_OPENMODE */ res_SETATTR4->status = NFS4ERR_OPENMODE; return res_SETATTR4->status; } } else { /* Special stateid, no open state, check to * see if any share conflicts */ state_open = NULL; /* Check for delegation conflict. */ if (state_deleg_conflict(obj, true)) { res_SETATTR4->status = NFS4ERR_DELAY; goto done; } } } const time_t S_NSECS = 1000000000UL; /* Set the atime and mtime (ctime is not setable) */ /* A carry into seconds considered invalid */ if (sattr.atime.tv_nsec >= S_NSECS) { res_SETATTR4->status = NFS4ERR_INVAL; goto done; } if (sattr.mtime.tv_nsec >= S_NSECS) { res_SETATTR4->status = NFS4ERR_INVAL; goto done; } /* If owner or owner_group are set, and the credential was * squashed, then we must squash the set owner and owner_group. */ squash_setattr(&sattr); /* If a SETATTR comes with an open stateid, and size is being * set, then the open MUST be for write (checked above), so * is_open_write is simple at this stage, it's just a check that * we have an open owner. */ fsal_status = fsal_setattr(data->current_obj, false, state_found, &sattr); /* Release the attributes (may release an explicit or inherited ACL) */ fsal_release_attrs(&sattr); if (FSAL_IS_ERROR(fsal_status)) { res_SETATTR4->status = nfs4_Errno_status(fsal_status); goto done; } /* Set the replyed structure */ res_SETATTR4->attrsset = arg_SETATTR4->obj_attributes.attrmask; /* Exit with no error */ res_SETATTR4->status = NFS4_OK; done: if (state_found != NULL) dec_state_t_ref(state_found); if (state_open != NULL) dec_state_t_ref(state_open); return res_SETATTR4->status; } /* nfs4_op_setattr */ /** * @brief Free memory allocated for SETATTR result * * This function fres any memory allocated for the result of the * NFS4_OP_SETATTR operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_setattr_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_setclientid.c000066400000000000000000000260721324272410200232210ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_setclientid.c * @brief Routines used for managing the NFS4_OP_SETCLIENTID operation */ #include "config.h" #include #include "log.h" #include "fsal.h" #include "sal_functions.h" #include "nfs_proto_functions.h" #include "nfs_core.h" #include "nfs_creds.h" #include "nfs_file_handle.h" #include "client_mgr.h" #include "fsal.h" /** * * @brief The NFS4_OP_SETCLIENTID operation. * * @param[in] op nfs4_op arguments * @param[in,out] data Compound request's data * @param[out] resp nfs4_op results * * @retval NFS4_OK or errors for NFSv4.0. * @retval NFS4ERR_NOTSUPP for NFSv4.1. * */ int nfs4_op_setclientid(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { SETCLIENTID4args * const arg_SETCLIENTID4 = &op->nfs_argop4_u.opsetclientid; SETCLIENTID4res * const res_SETCLIENTID4 = &resp->nfs_resop4_u.opsetclientid; clientaddr4 * const res_SETCLIENTID4_INUSE = &resp->nfs_resop4_u.opsetclientid.SETCLIENTID4res_u.client_using; char str_verifier[NFS4_VERIFIER_SIZE * 2 + 1]; struct display_buffer dspbuf_verifier = { sizeof(str_verifier), str_verifier, str_verifier}; char str_client[NFS4_OPAQUE_LIMIT * 2 + 1]; struct display_buffer dspbuf_client = { sizeof(str_client), str_client, str_client}; const char *str_client_addr = "(unknown)"; /* 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}; nfs_client_record_t *client_record; nfs_client_id_t *conf; nfs_client_id_t *unconf; clientid4 clientid; verifier4 verifier; int rc; resp->resop = NFS4_OP_SETCLIENTID; if (data->minorversion > 0) { res_SETCLIENTID4->status = NFS4ERR_NOTSUPP; return res_SETCLIENTID4->status; } if (op_ctx->client != NULL) str_client_addr = op_ctx->client->hostaddr_str; if (isDebug(COMPONENT_CLIENTID)) { display_opaque_value(&dspbuf_client, arg_SETCLIENTID4->client.id.id_val, arg_SETCLIENTID4->client.id.id_len); display_opaque_bytes(&dspbuf_verifier, arg_SETCLIENTID4->client.verifier, NFS4_VERIFIER_SIZE); } else { str_client[0] = '\0'; str_verifier[0] = '\0'; } LogDebug(COMPONENT_CLIENTID, "SETCLIENTID Client addr=%s id=%s verf=%s callback={program=%u r_addr=%s r_netid=%s} ident=%u", str_client_addr, str_client, str_verifier, arg_SETCLIENTID4->callback.cb_program, arg_SETCLIENTID4->callback.cb_location.r_addr, arg_SETCLIENTID4->callback.cb_location.r_netid, arg_SETCLIENTID4->callback_ident); /* Do we already have one or more records for client id (x)? */ client_record = get_client_record(arg_SETCLIENTID4->client.id.id_val, arg_SETCLIENTID4->client.id.id_len, 0, 0); if (client_record == NULL) { /* Some major failure */ LogCrit(COMPONENT_CLIENTID, "SETCLIENTID failed"); res_SETCLIENTID4->status = NFS4ERR_SERVERFAULT; return res_SETCLIENTID4->status; } /* The following checks are based on RFC3530bis draft 16 * * This attempts to implement the logic described in * 15.35.5. IMPLEMENTATION Consider the major bullets as CASE * 1, CASE 2, CASE 3, CASE 4, and CASE 5. */ PTHREAD_MUTEX_lock(&client_record->cr_mutex); 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); } conf = client_record->cr_confirmed_rec; if (conf != NULL) { bool credmatch; /* Need a reference to the confirmed record for below */ inc_client_id_ref(conf); clientid = conf->cid_clientid; display_clientid(&dspbuf_clientid4, clientid); credmatch = nfs_compare_clientcred(&conf->cid_credential, &data->credential) && op_ctx->client != NULL && conf->gsh_client != NULL && op_ctx->client == conf->gsh_client; /* Only reject if the principal doesn't match and the * clientid has live state associated. Per RFC 7530 * Section 9.1.2. Server Release of Client ID. */ if (!credmatch && clientid_has_state(conf)) { /* CASE 1: * * Confirmed record exists and not the same principal */ 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': Principals do not match... confirmed addr=%s Return NFS4ERR_CLID_INUSE", str_clientid4, str_client, confirmed_addr); } res_SETCLIENTID4->status = NFS4ERR_CLID_INUSE; res_SETCLIENTID4_INUSE->r_netid = (char *)netid_nc_table[conf->cid_cb.v40.cb_addr.nc] .netid; res_SETCLIENTID4_INUSE->r_addr = gsh_strdup(conf->cid_cb.v40.cb_client_r_addr); /* Release our reference to the confirmed clientid. */ dec_client_id_ref(conf); goto out; } /* Check if confirmed record is for (v, x, c, l, s) */ if (credmatch && memcmp(arg_SETCLIENTID4->client.verifier, conf->cid_incoming_verifier, NFS4_VERIFIER_SIZE) == 0) { /* CASE 2: * * A confirmed record exists for this long * form client id and verifier. * * Consider this to be a possible update of * the call-back information. * * Remove any pre-existing unconfirmed record * for (v, x, c). * * Return the same short form client id (c), * but a new setclientid_confirm verifier (t). */ LogFullDebug(COMPONENT_CLIENTID, "Update ClientId %s->%s", str_clientid4, str_client); new_clientid_verifier(verifier); } else { /* Must be CASE 3 or CASE 4 * * Confirmed record is for (u, x, c, l, s). * * These are actually the same, doesn't really * matter if an unconfirmed record exists or * not. Any existing unconfirmed record will * be removed and a new unconfirmed record * added. * * Return a new short form clientid (d) and a * new setclientid_confirm verifier (t). (Note * the spec calls the values e and r for CASE * 4). */ LogFullDebug(COMPONENT_CLIENTID, "Replace ClientId %s->%s", str_clientid4, str_client); clientid = new_clientid(); new_clientid_verifier(verifier); } /* Release our reference to the confirmed clientid. */ dec_client_id_ref(conf); } else { /* CASE 5: * * * Remove any existing unconfirmed record. * * Return a new short form clientid (d) and a new * setclientid_confirm verifier (t). */ clientid = new_clientid(); display_clientid(&dspbuf_clientid4, clientid); LogFullDebug(COMPONENT_CLIENTID, "New client %s", str_clientid4); new_clientid_verifier(verifier); } /* At this point, no matter what the case was above, we should * remove any pre-existing unconfirmed record. */ unconf = client_record->cr_unconfirmed_rec; if (unconf != NULL) { /* Delete the unconfirmed clientid record. Because we * have the cr_mutex, we have won any race to deal * with this clientid record (whether we raced with a * SETCLIENTID_CONFIRM or the reaper thread (if either * of those operations had won the race, * cr_punconfirmed_id would have been NULL). */ 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, "Replacing %s", str); } /* unhash the clientid record */ remove_unconfirmed_client_id(unconf); unconf = NULL; } /* Now we can proceed to build the new unconfirmed record. We * have determined the clientid and setclientid_confirm values * above. */ unconf = create_client_id(clientid, client_record, &data->credential, 0); if (unconf == NULL) { /* Error already logged, return */ res_SETCLIENTID4->status = NFS4ERR_RESOURCE; goto out; } if (strmaxcpy(unconf->cid_cb.v40.cb_client_r_addr, arg_SETCLIENTID4->callback.cb_location.r_addr, sizeof(unconf->cid_cb.v40.cb_client_r_addr)) == -1) { LogCrit(COMPONENT_CLIENTID, "Callback r_addr %s too long", arg_SETCLIENTID4->callback.cb_location.r_addr); free_client_id(unconf); res_SETCLIENTID4->status = NFS4ERR_INVAL; free_client_id(unconf); goto out; } nfs_set_client_location(unconf, &arg_SETCLIENTID4->callback.cb_location); memcpy(unconf->cid_incoming_verifier, arg_SETCLIENTID4->client.verifier, NFS4_VERIFIER_SIZE); memcpy(unconf->cid_verifier, verifier, sizeof(NFS4_write_verifier)); unconf->cid_cb.v40.cb_program = arg_SETCLIENTID4->callback.cb_program; unconf->cid_cb.v40.cb_callback_ident = arg_SETCLIENTID4->callback_ident; rc = nfs_client_id_insert(unconf); if (rc != CLIENT_ID_SUCCESS) { /* Record is already freed, return. */ res_SETCLIENTID4->status = clientid_error_to_nfsstat_no_expire(rc); goto out; } if (isDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; sprint_mem(str_verifier, verifier, NFS4_VERIFIER_SIZE); display_client_id_rec(&dspbuf, unconf); LogDebug(COMPONENT_CLIENTID, "SETCLIENTID reply Verifier=%s %s", str_verifier, str); } res_SETCLIENTID4->status = NFS4_OK; res_SETCLIENTID4->SETCLIENTID4res_u.resok4.clientid = clientid; memcpy(res_SETCLIENTID4->SETCLIENTID4res_u.resok4.setclientid_confirm, &verifier, NFS4_VERIFIER_SIZE); out: PTHREAD_MUTEX_unlock(&client_record->cr_mutex); /* Release our reference to the client record */ dec_client_record_ref(client_record); return res_SETCLIENTID4->status; } /** * @brief Free memory alocated for SETCLIENTID result * * @param[in,out] resp nfs4_op results */ void nfs4_op_setclientid_Free(nfs_resop4 *res) { SETCLIENTID4res *resp = &res->nfs_resop4_u.opsetclientid; if (resp->status == NFS4ERR_CLID_INUSE) { if (resp->SETCLIENTID4res_u.client_using.r_addr != NULL) gsh_free(resp->SETCLIENTID4res_u.client_using.r_addr); } } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_setclientid_confirm.c000066400000000000000000000344621324272410200247400ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_setclientid_confirm.c * @brief Routines used for managing the NFS4_OP_SETCLIENTID_CONFIRM operation. * * Routines used for managing the NFS4_OP_SETCLIENTID_CONFIRM operation. */ #include "config.h" #include #include "log.h" #include "nfs4.h" #include "nfs_core.h" #include "sal_functions.h" #include "nfs_proto_functions.h" #include "nfs_rpc_callback.h" #include "nfs_creds.h" #include "nfs_file_handle.h" #include "client_mgr.h" #include "fsal.h" /** * * @brief The NFS4_OP_SETCLIENTID_CONFIRM operation * * @param[in] op nfs4_op arguments * @param[in,out] data The compound request's data * @param[out] resp nfs4_op results * * @retval NFS4_OK or errors for NFSv4.0. * @retval NFS4ERR_NOTSUPP for NFSv4.1. * * @see nfs4_Compound */ 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; } /** * @brief Free memory allocated for SETCLIENTID_CONFIRM result * * @param[in,out] resp nfs4_op results */ void nfs4_op_setclientid_confirm_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_test_stateid.c000066400000000000000000000053661324272410200234110ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_test_stateid.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. * * */ #include "config.h" #include #include #include #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "nfs4.h" #include "nfs_core.h" #include "sal_functions.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "gsh_list.h" /** * @brief The NFS4_OP_TEST_STATEID operation. * * This function implements the NFS4_OP_TEST_STATEID operation. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return RFC 5661, p. 375 * * @see nfs4_Compound * */ int nfs4_op_test_stateid(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { TEST_STATEID4args * const arg_TEST_STATEID4 __attribute__ ((unused)) = &op->nfs_argop4_u.optest_stateid; TEST_STATEID4res * const res_TEST_STATEID4 = &resp->nfs_resop4_u.optest_stateid; /* Lock are not supported */ resp->resop = NFS4_OP_TEST_STATEID; res_TEST_STATEID4->tsr_status = NFS4_OK; if (data->minorversion == 0) { res_TEST_STATEID4->tsr_status = NFS4ERR_INVAL; return res_TEST_STATEID4->tsr_status; } /* Do basic checks on a filehandle */ res_TEST_STATEID4->tsr_status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false); return res_TEST_STATEID4->tsr_status; } /* nfs41_op_lock */ /** * @brief Free memory allocated for TEST_STATEID result * * This function frees any memory allocated for the result of the * NFS4_OP_TEST_STATEID operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_test_stateid_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_verify.c000066400000000000000000000070671324272410200222210ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * \file nfs4_op_verify.c * \brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. * */ #include "config.h" #include "log.h" #include "nfs4.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_file_handle.h" /** * * @brief Implemtation of NFS4_OP_VERIFY * * This function implemtats the NFS4_OP_VERIFY operation. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 375 */ int nfs4_op_verify(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { VERIFY4args * const arg_VERIFY4 = &op->nfs_argop4_u.opverify; VERIFY4res * const res_VERIFY4 = &resp->nfs_resop4_u.opverify; fattr4 file_attr4; int rc = 0; struct attrlist attrs; resp->resop = NFS4_OP_VERIFY; res_VERIFY4->status = NFS4_OK; /* Do basic checks on a filehandle */ res_VERIFY4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false); if (res_VERIFY4->status != NFS4_OK) return res_VERIFY4->status; /* Get only attributes that are allowed to be read */ if (!nfs4_Fattr_Check_Access (&arg_VERIFY4->obj_attributes, FATTR4_ATTR_READ)) { res_VERIFY4->status = NFS4ERR_INVAL; return res_VERIFY4->status; } /* Ask only for supported attributes */ if (!nfs4_Fattr_Supported(&arg_VERIFY4->obj_attributes)) { res_VERIFY4->status = NFS4ERR_ATTRNOTSUPP; return res_VERIFY4->status; } fsal_prepare_attrs(&attrs, 0); res_VERIFY4->status = bitmap4_to_attrmask_t(&arg_VERIFY4->obj_attributes.attrmask, &attrs.request_mask); if (res_VERIFY4->status != NFS4_OK) return res_VERIFY4->status; res_VERIFY4->status = file_To_Fattr(data, attrs.request_mask, &attrs, &file_attr4, &arg_VERIFY4->obj_attributes.attrmask); if (res_VERIFY4->status != NFS4_OK) return res_VERIFY4->status; /* Done with the attrs */ fsal_release_attrs(&attrs); rc = nfs4_Fattr_cmp(&(arg_VERIFY4->obj_attributes), &file_attr4); if (rc == true) res_VERIFY4->status = NFS4_OK; else if (rc == -1) res_VERIFY4->status = NFS4ERR_INVAL; else res_VERIFY4->status = NFS4ERR_NOT_SAME; nfs4_Fattr_Free(&file_attr4); return res_VERIFY4->status; } /* nfs4_op_verify */ /** * @brief Frees memory allocated for VERIFY result. * * This function frees any memory allocated for the result of the * NFS4_OP_VERIFY operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_verify_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_write.c000066400000000000000000000401271324272410200220410ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_write.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. */ #include "config.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "sal_functions.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_convert.h" #include "fsal_pnfs.h" #include "server_stats.h" #include "export_mgr.h" /** * @brief Write for a data server * * This function bypasses cache_inode and calls directly into the FSAL * to perform a pNFS data server write. * * @param[in] op Arguments for nfs41_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs41_op * * @return per RFC5661, p. 376 * */ static int op_dswrite(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { WRITE4args * const arg_WRITE4 = &op->nfs_argop4_u.opwrite; WRITE4res * const res_WRITE4 = &resp->nfs_resop4_u.opwrite; /* NFSv4 return code */ nfsstat4 nfs_status = 0; nfs_status = data->current_ds->dsh_ops.write( data->current_ds, op_ctx, &arg_WRITE4->stateid, arg_WRITE4->offset, arg_WRITE4->data.data_len, arg_WRITE4->data.data_val, arg_WRITE4->stable, &res_WRITE4->WRITE4res_u.resok4.count, &res_WRITE4->WRITE4res_u.resok4.writeverf, &res_WRITE4->WRITE4res_u.resok4.committed); res_WRITE4->status = nfs_status; return res_WRITE4->status; } /** * @brief Write plus for a data server * * This function bypasses cache_inode and calls directly into the FSAL * to perform a pNFS data server write. * * @param[in] op Arguments for nfs41_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs41_op * * @return per RFC5661, p. 376 * */ static int op_dswrite_plus(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp, struct io_info *info) { WRITE4args * const arg_WRITE4 = &op->nfs_argop4_u.opwrite; WRITE4res * const res_WRITE4 = &resp->nfs_resop4_u.opwrite; /* NFSv4 return code */ nfsstat4 nfs_status = 0; if (info->io_content.what == NFS4_CONTENT_DATA) nfs_status = data->current_ds->dsh_ops.write( data->current_ds, op_ctx, &arg_WRITE4->stateid, arg_WRITE4->offset, arg_WRITE4->data.data_len, arg_WRITE4->data.data_val, arg_WRITE4->stable, &res_WRITE4->WRITE4res_u.resok4.count, &res_WRITE4->WRITE4res_u.resok4.writeverf, &res_WRITE4->WRITE4res_u.resok4.committed); else nfs_status = data->current_ds->dsh_ops.write_plus( data->current_ds, op_ctx, &arg_WRITE4->stateid, arg_WRITE4->offset, arg_WRITE4->data.data_len, arg_WRITE4->data.data_val, arg_WRITE4->stable, &res_WRITE4->WRITE4res_u.resok4.count, &res_WRITE4->WRITE4res_u.resok4.writeverf, &res_WRITE4->WRITE4res_u.resok4.committed, info); res_WRITE4->status = nfs_status; return res_WRITE4->status; } /** * @brief The NFS4_OP_WRITE operation * * This functions handles the NFS4_OP_WRITE operation in NFSv4. This * function can be called only from nfs4_Compound. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 376 */ static int nfs4_write(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp, fsal_io_direction_t io, struct io_info *info) { WRITE4args * const arg_WRITE4 = &op->nfs_argop4_u.opwrite; WRITE4res * const res_WRITE4 = &resp->nfs_resop4_u.opwrite; uint64_t size = 0; size_t written_size = 0; uint64_t offset; bool sync = false; void *bufferdata; stable_how4 stable_how; state_t *state_found = NULL; state_t *state_open = NULL; fsal_status_t fsal_status = {0, 0}; struct fsal_obj_handle *obj = NULL; bool anonymous_started = false; struct gsh_buffdesc verf_desc; state_owner_t *owner = NULL; uint64_t MaxWrite = atomic_fetch_uint64_t(&op_ctx->ctx_export->MaxWrite); uint64_t MaxOffsetWrite = atomic_fetch_uint64_t(&op_ctx->ctx_export->MaxOffsetWrite); /* Lock are not supported */ resp->resop = NFS4_OP_WRITE; res_WRITE4->status = NFS4_OK; if ((data->minorversion > 0) && (nfs4_Is_Fh_DSHandle(&data->currentFH))) { if (io == FSAL_IO_WRITE) return op_dswrite(op, data, resp); else return op_dswrite_plus(op, data, resp, info); } /* * Do basic checks on a filehandle * Only files can be written */ res_WRITE4->status = nfs4_sanity_check_FH(data, REGULAR_FILE, true); if (res_WRITE4->status != NFS4_OK) return res_WRITE4->status; /* if quota support is active, then we should check is the FSAL allows inode creation or not */ fsal_status = op_ctx->fsal_export->exp_ops.check_quota( op_ctx->fsal_export, op_ctx->ctx_export->fullpath, FSAL_QUOTA_INODES); if (FSAL_IS_ERROR(fsal_status)) { res_WRITE4->status = NFS4ERR_DQUOT; return res_WRITE4->status; } /* vnode to manage is the current one */ obj = data->current_obj; /* Check stateid correctness and get pointer to state * (also checks for special stateids) */ res_WRITE4->status = nfs4_Check_Stateid(&arg_WRITE4->stateid, obj, &state_found, data, STATEID_SPECIAL_ANY, 0, false, "WRITE"); if (res_WRITE4->status != NFS4_OK) return res_WRITE4->status; /* NB: After this points, if state_found == NULL, then * the stateid is all-0 or all-1 */ if (state_found != NULL) { struct state_deleg *sdeleg; if (info) info->io_advise = state_found->state_data.io_advise; switch (state_found->state_type) { case STATE_TYPE_SHARE: state_open = state_found; /* Note this causes an extra refcount, but it * simplifies logic below. */ inc_state_t_ref(state_open); /** @todo FSF: need to check against existing locks */ break; case STATE_TYPE_LOCK: state_open = state_found->state_data.lock.openstate; inc_state_t_ref(state_open); /** * @todo FSF: should check that write is in range of an * exclusive lock... */ break; case STATE_TYPE_DELEG: /* Check if the delegation state allows READ */ sdeleg = &state_found->state_data.deleg; if (!(sdeleg->sd_type & OPEN_DELEGATE_WRITE) || (sdeleg->sd_state != DELEG_GRANTED)) { /* Invalid delegation for this operation. */ LogDebug(COMPONENT_STATE, "Delegation type:%d state:%d", sdeleg->sd_type, sdeleg->sd_state); res_WRITE4->status = NFS4ERR_BAD_STATEID; return res_WRITE4->status; } state_open = NULL; break; case STATE_TYPE_LAYOUT: state_open = NULL; break; default: res_WRITE4->status = NFS4ERR_BAD_STATEID; LogDebug(COMPONENT_NFS_V4_LOCK, "WRITE with invalid stateid of type %d", (int)state_found->state_type); return res_WRITE4->status; } /* This is a write operation, this means that the file * MUST have been opened for writing */ if (state_open != NULL && (state_open->state_data.share.share_access & OPEN4_SHARE_ACCESS_WRITE) == 0) { /* Bad open mode, return NFS4ERR_OPENMODE */ res_WRITE4->status = NFS4ERR_OPENMODE; if (isDebug(COMPONENT_NFS_V4_LOCK)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = { sizeof(str), str, str}; display_stateid(&dspbuf, state_found); LogDebug(COMPONENT_NFS_V4_LOCK, "WRITE %s doesn't have OPEN4_SHARE_ACCESS_WRITE", str); } goto out; } } else { /* Special stateid, no open state, check to see if any * share conflicts */ state_open = NULL; /* Check for delegation conflict. */ if (state_deleg_conflict(obj, true)) { res_WRITE4->status = NFS4ERR_DELAY; goto out; } anonymous_started = true; } /* Need to permission check the write. */ fsal_status = obj->obj_ops.test_access(obj, FSAL_WRITE_ACCESS, NULL, NULL, true); if (FSAL_IS_ERROR(fsal_status)) { res_WRITE4->status = nfs4_Errno_status(fsal_status); goto done; } /* Get the characteristics of the I/O to be made */ offset = arg_WRITE4->offset; size = arg_WRITE4->data.data_len; stable_how = arg_WRITE4->stable; LogFullDebug(COMPONENT_NFS_V4, "offset = %" PRIu64 " length = %" PRIu64 " stable = %d", offset, size, stable_how); if (MaxOffsetWrite < UINT64_MAX) { LogFullDebug(COMPONENT_NFS_V4, "Write offset=%" PRIu64 " count=%" PRIu64 " MaxOffSet=%" PRIu64, offset, size, MaxOffsetWrite); if ((offset + size) > MaxOffsetWrite) { LogEvent(COMPONENT_NFS_V4, "A client tryed to violate max file size %" PRIu64 " for exportid #%hu", MaxOffsetWrite, op_ctx->ctx_export->export_id); res_WRITE4->status = NFS4ERR_FBIG; goto done; } } if (size > MaxWrite) { /* * The client asked for too much data, we * must restrict him */ if (info == NULL || info->io_content.what != NFS4_CONTENT_HOLE) { LogFullDebug(COMPONENT_NFS_V4, "write requested size = %" PRIu64 " write allowed size = %" PRIu64, size, MaxWrite); size = MaxWrite; } } /* Where are the data ? */ bufferdata = arg_WRITE4->data.data_val; LogFullDebug(COMPONENT_NFS_V4, "offset = %" PRIu64 " length = %" PRIu64, offset, size); /* if size == 0 , no I/O) are actually made and everything is alright */ if (size == 0) { res_WRITE4->WRITE4res_u.resok4.count = 0; res_WRITE4->WRITE4res_u.resok4.committed = FILE_SYNC4; verf_desc.addr = res_WRITE4->WRITE4res_u.resok4.writeverf; verf_desc.len = sizeof(verifier4); op_ctx->fsal_export->exp_ops.get_write_verifier( op_ctx->fsal_export, &verf_desc); res_WRITE4->status = NFS4_OK; goto done; } if (arg_WRITE4->stable == UNSTABLE4) sync = false; else sync = true; if (!anonymous_started && data->minorversion == 0) { owner = get_state_owner_ref(state_found); if (owner != NULL) { op_ctx->clientid = &owner->so_owner.so_nfs4_owner.so_clientid; } } /* Call the new fsal_write */ fsal_status = fsal_write2(obj, false, state_found, offset, size, &written_size, bufferdata, &sync, info); if (FSAL_IS_ERROR(fsal_status)) { LogDebug(COMPONENT_NFS_V4, "write returned %s", fsal_err_txt(fsal_status)); res_WRITE4->status = nfs4_Errno_status(fsal_status); goto done; } if (!anonymous_started && data->minorversion == 0) op_ctx->clientid = NULL; /* Set the returned value */ if (sync) res_WRITE4->WRITE4res_u.resok4.committed = FILE_SYNC4; else res_WRITE4->WRITE4res_u.resok4.committed = UNSTABLE4; res_WRITE4->WRITE4res_u.resok4.count = written_size; verf_desc.addr = res_WRITE4->WRITE4res_u.resok4.writeverf; verf_desc.len = sizeof(verifier4); op_ctx->fsal_export->exp_ops.get_write_verifier(op_ctx->fsal_export, &verf_desc); res_WRITE4->status = NFS4_OK; done: server_stats_io_done(size, written_size, (res_WRITE4->status == NFS4_OK) ? true : false, true); out: if (owner != NULL) dec_state_owner_ref(owner); if (state_found != NULL) dec_state_t_ref(state_found); if (state_open != NULL) dec_state_t_ref(state_open); return res_WRITE4->status; } /* nfs4_op_write */ /** * @brief The NFS4_OP_WRITE operation * * This functions handles the NFS4_OP_WRITE operation in NFSv4. This * function can be called only from nfs4_Compound. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 376 */ int nfs4_op_write(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { int err; err = nfs4_write(op, data, resp, FSAL_IO_WRITE, NULL); return err; } /** * @brief Free memory allocated for WRITE result * * This function frees any memory allocated for the result of the * NFS4_OP_WRITE operation. * * @param[in,out] resp nfs4_op results * */ void nfs4_op_write_Free(nfs_resop4 *resp) { /* Nothing to be done */ } /** * @brief The NFS4_OP_WRITE_SAME operation * * This functions handles the NFS4_OP_WRITE_SAME operation in NFSv4.2. This * function can be called only from nfs4_Compound. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * */ int nfs4_op_write_same(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { WRITE_SAME4res * const res_WSAME = &resp->nfs_resop4_u.opwrite_plus; resp->resop = NFS4_OP_WRITE_SAME; res_WSAME->wpr_status = NFS4ERR_NOTSUPP; return res_WSAME->wpr_status; } /** * @brief Free memory allocated for WRITE_SAME result * * This function frees any memory allocated for the result of the * NFS4_OP_WRITE_SAME operation. * * @param[in,out] resp nfs4_op results * */ void nfs4_op_write_same_Free(nfs_resop4 *resp) { /* Nothing to be done */ } /** * @brief The NFS4_OP_ALLOCATE * This functions handles the NFS4_OP_ALLOCATE operation in NFSv4.2. This * function can be called only from nfs4_Compound. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * */ int nfs4_op_allocate(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { struct nfs_resop4 res; struct nfs_argop4 arg; struct io_info info; /* Arguments and response */ ALLOCATE4args * const arg_ALLOC = &op->nfs_argop4_u.opallocate; ALLOCATE4res * const res_ALLOC = &resp->nfs_resop4_u.opallocate; resp->resop = NFS4_OP_ALLOCATE; res_ALLOC->ar_status = NFS4_OK; arg.nfs_argop4_u.opwrite.stateid = arg_ALLOC->aa_stateid; arg.nfs_argop4_u.opwrite.stable = true; info.io_content.what = NFS4_CONTENT_ALLOCATE; info.io_content.hole.di_offset = arg_ALLOC->aa_offset; info.io_content.hole.di_length = arg_ALLOC->aa_length; arg.nfs_argop4_u.opwrite.offset = arg_ALLOC->aa_offset; arg.nfs_argop4_u.opwrite.data.data_len = arg_ALLOC->aa_length; arg.nfs_argop4_u.opwrite.data.data_val = NULL; info.io_advise = 0; res_ALLOC->ar_status = nfs4_write(&arg, data, &res, FSAL_IO_WRITE_PLUS, &info); return res_ALLOC->ar_status; } /** * @brief The NFS4_OP_DEALLOCATE * This functions handles the NFS4_OP_DEALLOCATE operation in NFSv4.2. This * function can be called only from nfs4_Compound. * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * */ int nfs4_op_deallocate(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { struct nfs_resop4 res; struct nfs_argop4 arg; struct io_info info; /* Arguments and response */ DEALLOCATE4args * const arg_DEALLOC = &op->nfs_argop4_u.opdeallocate; DEALLOCATE4res * const res_DEALLOC = &resp->nfs_resop4_u.opdeallocate; resp->resop = NFS4_OP_DEALLOCATE; res_DEALLOC->dr_status = NFS4_OK; arg.nfs_argop4_u.opwrite.stateid = arg_DEALLOC->da_stateid; arg.nfs_argop4_u.opwrite.stable = true; info.io_content.what = NFS4_CONTENT_DEALLOCATE; info.io_content.hole.di_offset = arg_DEALLOC->da_offset; info.io_content.hole.di_length = arg_DEALLOC->da_length; arg.nfs_argop4_u.opwrite.offset = arg_DEALLOC->da_offset; arg.nfs_argop4_u.opwrite.data.data_len = arg_DEALLOC->da_length; arg.nfs_argop4_u.opwrite.data.data_val = NULL; info.io_advise = 0; res_DEALLOC->dr_status = nfs4_write(&arg, data, &res, FSAL_IO_WRITE_PLUS, &info); return res_DEALLOC->dr_status; } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_op_xattr.c000066400000000000000000000300751324272410200220520ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright IBM Corporation, 2015 * Contributor: Marc Eshel * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_op_xattr.c * @brief Routines used for managing the NFS4 COMPOUND functions. * * Routines used for managing the NFS4 COMPOUND functions. * * */ #include "config.h" #include "log.h" #include "nfs4.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_convert.h" #include "sal_functions.h" #include "nfs_creds.h" #define XATTR_VALUE_SIZE 1024 /** * @brief The NFS4_OP_GETXATTR operation. * * This functions handles the NFS4_OP_GETXATTR operation in NFSv4. This * function can be called only from nfs4_Compound * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * */ int nfs4_op_getxattr(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { GETXATTR4args * const arg_GETXATTR4 = &op->nfs_argop4_u.opgetxattr; GETXATTR4res * const res_GETXATTR4 = &resp->nfs_resop4_u.opgetxattr; xattrvalue4 gr_value; fsal_status_t fsal_status; struct fsal_obj_handle *obj_handle = data->current_obj; resp->resop = NFS4_OP_GETXATTR; res_GETXATTR4->status = NFS4_OK; LogDebug(COMPONENT_NFS_V4, "GetXattr len %d name: %s", arg_GETXATTR4->ga_name.utf8string_len, arg_GETXATTR4->ga_name.utf8string_val); res_GETXATTR4->GETXATTR4res_u.resok4.gr_value.utf8string_len = 0; res_GETXATTR4->GETXATTR4res_u.resok4.gr_value.utf8string_val = NULL; gr_value.utf8string_len = XATTR_VALUE_SIZE; gr_value.utf8string_val = gsh_malloc(gr_value.utf8string_len); /* Do basic checks on a filehandle */ res_GETXATTR4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false); if (res_GETXATTR4->status != NFS4_OK) return res_GETXATTR4->status; fsal_status = obj_handle->obj_ops.getxattrs(obj_handle, &arg_GETXATTR4->ga_name, &gr_value); if (FSAL_IS_ERROR(fsal_status)) { if (fsal_status.major == ERR_FSAL_TOOSMALL) { LogDebug(COMPONENT_NFS_V4, "FSAL buffer len %d too small", XATTR_VALUE_SIZE); /* Get size of xattr value */ gsh_free(gr_value.utf8string_val); gr_value.utf8string_len = 0; gr_value.utf8string_val = NULL; fsal_status = obj_handle->obj_ops.getxattrs(obj_handle, &arg_GETXATTR4->ga_name, &gr_value); if (FSAL_IS_ERROR(fsal_status)) return res_GETXATTR4->status = nfs4_Errno_state( state_error_convert(fsal_status)); LogDebug(COMPONENT_NFS_V4, "FSAL buffer new len %d", gr_value.utf8string_len); /* Try again with a bigger buffer */ gr_value.utf8string_val = gsh_malloc( gr_value.utf8string_len); fsal_status = obj_handle->obj_ops.getxattrs(obj_handle, &arg_GETXATTR4->ga_name, &gr_value); if (FSAL_IS_ERROR(fsal_status)) return res_GETXATTR4->status = nfs4_Errno_state( state_error_convert(fsal_status)); } else return res_GETXATTR4->status = nfs4_Errno_state( state_error_convert(fsal_status)); } res_GETXATTR4->status = NFS4_OK; res_GETXATTR4->GETXATTR4res_u.resok4.gr_value.utf8string_len = gr_value.utf8string_len; res_GETXATTR4->GETXATTR4res_u.resok4.gr_value.utf8string_val = gr_value.utf8string_val; return res_GETXATTR4->status; } /** * @brief Free memory allocated for GETXATTR result * * This function fres any memory allocated for the result of the * NFS4_OP_GETXATTR operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_getxattr_Free(nfs_resop4 *resp) { GETXATTR4res * const res_GETXATTR4 = &resp->nfs_resop4_u.opgetxattr; gsh_free(res_GETXATTR4->GETXATTR4res_u.resok4.gr_value.utf8string_val); } /** * @brief The NFS4_OP_SETXATTR operation. * * This functions handles the NFS4_OP_SETXATTR operation in NFSv4. This * function can be called only from nfs4_Compound * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * */ int nfs4_op_setxattr(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { SETXATTR4args * const arg_SETXATTR4 = &op->nfs_argop4_u.opsetxattr; SETXATTR4res * const res_SETXATTR4 = &resp->nfs_resop4_u.opsetxattr; fsal_status_t fsal_status; struct fsal_obj_handle *obj_handle = data->current_obj; resp->resop = NFS4_OP_SETXATTR; res_SETXATTR4->status = NFS4_OK; LogDebug(COMPONENT_NFS_V4, "SetXattr type %d len %d name: %s", arg_SETXATTR4->sa_type, arg_SETXATTR4->sa_xattr.xa_name.utf8string_len, arg_SETXATTR4->sa_xattr.xa_name.utf8string_val); /* Do basic checks on a filehandle */ res_SETXATTR4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false); if (res_SETXATTR4->status != NFS4_OK) return res_SETXATTR4->status; /* Don't allow attribute change while we are in grace period. * Required for delegation reclaims and may be needed for other * reclaimable states as well. */ if (nfs_in_grace()) { res_SETXATTR4->status = NFS4ERR_GRACE; return res_SETXATTR4->status; } res_SETXATTR4->SETXATTR4res_u.resok4.sr_info.atomic = false; res_SETXATTR4->SETXATTR4res_u.resok4.sr_info.before = fsal_get_changeid4(data->current_obj); fsal_status = obj_handle->obj_ops.setxattrs(obj_handle, arg_SETXATTR4->sa_type, &arg_SETXATTR4->sa_xattr.xa_name, &arg_SETXATTR4->sa_xattr.xa_value); if (FSAL_IS_ERROR(fsal_status)) return res_SETXATTR4->status = nfs4_Errno_state( state_error_convert(fsal_status)); res_SETXATTR4->SETXATTR4res_u.resok4.sr_info.after = fsal_get_changeid4(data->current_obj); return res_SETXATTR4->status; } /** * @brief Free memory allocated for SETXATTR result * * This function fres any memory allocated for the result of the * NFS4_OP_SETXATTR operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_setxattr_Free(nfs_resop4 *resp) { /* Nothing to be done */ } /** * @brief The NFS4_OP_LISTXATTR operation. * * This functions handles the NFS4_OP_LISTXATTR operation in NFSv4. This * function can be called only from nfs4_Compound * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * */ int nfs4_op_listxattr(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { LISTXATTR4args * const arg_LISTXATTR4 = &op->nfs_argop4_u.oplistxattr; LISTXATTR4res * const res_LISTXATTR4 = &resp->nfs_resop4_u.oplistxattr; fsal_status_t fsal_status; struct fsal_obj_handle *obj_handle = data->current_obj; xattrlist4 list; nfs_cookie4 la_cookie; verifier4 la_cookieverf; bool_t lr_eof; component4 *entry; int i; bool use_cookie_verifier = op_ctx_export_has_option( EXPORT_OPTION_USE_COOKIE_VERIFIER); resp->resop = NFS4_OP_LISTXATTR; res_LISTXATTR4->status = NFS4_OK; LogDebug(COMPONENT_NFS_V4, "SetXattr max count %d cookie %" PRIu64, arg_LISTXATTR4->la_maxcount, arg_LISTXATTR4->la_cookie); /* Do basic checks on a filehandle */ res_LISTXATTR4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false); if (res_LISTXATTR4->status != NFS4_OK) return res_LISTXATTR4->status; /* Do basic checks on a filehandle */ res_LISTXATTR4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false); if (res_LISTXATTR4->status != NFS4_OK) return res_LISTXATTR4->status; /* Double buf size, one half for compound and on half for names. */ list.entries = (component4 *)gsh_malloc(2*arg_LISTXATTR4->la_maxcount); la_cookie = arg_LISTXATTR4->la_cookie; memset(la_cookieverf, 0, NFS4_VERIFIER_SIZE); if (la_cookie == 0 && use_cookie_verifier) { if (memcmp(la_cookieverf, arg_LISTXATTR4->la_cookieverf, NFS4_VERIFIER_SIZE) != 0) { res_LISTXATTR4->status = NFS4ERR_BAD_COOKIE; LogFullDebug(COMPONENT_NFS_V4, "Bad cookie"); return res_LISTXATTR4->status; } } fsal_status = obj_handle->obj_ops.listxattrs(obj_handle, arg_LISTXATTR4->la_maxcount, &la_cookie, &la_cookieverf, &lr_eof, &list); if (FSAL_IS_ERROR(fsal_status)) { res_LISTXATTR4->status = nfs4_Errno_state(state_error_convert(fsal_status)); gsh_free(list.entries); res_LISTXATTR4->LISTXATTR4res_u.resok4.lr_names.entries = NULL; return res_LISTXATTR4->status; } res_LISTXATTR4->LISTXATTR4res_u.resok4.lr_cookie = la_cookie; res_LISTXATTR4->LISTXATTR4res_u.resok4.lr_eof = lr_eof; memcpy(res_LISTXATTR4->LISTXATTR4res_u.resok4.lr_cookieverf, la_cookieverf, NFS4_VERIFIER_SIZE); res_LISTXATTR4->LISTXATTR4res_u.resok4.lr_names = list; entry = list.entries; for (i = 0; i < list.entryCount; i++) { LogFullDebug(COMPONENT_FSAL, "entry %d at %p len %d at %p name %s", i, entry, entry->utf8string_len, entry->utf8string_val, entry->utf8string_val); entry += 1; } return res_LISTXATTR4->status; } /** * @brief Free memory allocated for LISTXATTR result * * This function fres any memory allocated for the result of the * NFS4_OP_LISTXATTR operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_listxattr_Free(nfs_resop4 *resp) { LISTXATTR4res *res_LISTXATTR4 = &resp->nfs_resop4_u.oplistxattr; gsh_free(res_LISTXATTR4->LISTXATTR4res_u.resok4.lr_names.entries); } /** * @brief The NFS4_OP_REMOVEXATTR operation. * * This functions handles the NFS4_OP_REMOVEXATTR operation in NFSv4. This * function can be called only from nfs4_Compound * * @param[in] op Arguments for nfs4_op * @param[in,out] data Compound request's data * @param[out] resp Results for nfs4_op * * @return per RFC5661, p. 373-4 */ int nfs4_op_removexattr(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { REMOVEXATTR4args * const arg_REMOVEXATTR4 = &op->nfs_argop4_u.opremovexattr; REMOVEXATTR4res * const res_REMOVEXATTR4 = &resp->nfs_resop4_u.opremovexattr; fsal_status_t fsal_status; struct fsal_obj_handle *obj_handle = data->current_obj; resp->resop = NFS4_OP_REMOVEXATTR; res_REMOVEXATTR4->status = NFS4_OK; LogDebug(COMPONENT_NFS_V4, "RemoveXattr len %d name: %s", arg_REMOVEXATTR4->ra_name.utf8string_len, arg_REMOVEXATTR4->ra_name.utf8string_val); /* Do basic checks on a filehandle */ res_REMOVEXATTR4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false); if (res_REMOVEXATTR4->status != NFS4_OK) return res_REMOVEXATTR4->status; /* Don't allow attribute change while we are in grace period. * Required for delegation reclaims and may be needed for other * reclaimable states as well. */ if (nfs_in_grace()) { res_REMOVEXATTR4->status = NFS4ERR_GRACE; return res_REMOVEXATTR4->status; } res_REMOVEXATTR4->REMOVEXATTR4res_u.resok4.rr_info.atomic = false; res_REMOVEXATTR4->REMOVEXATTR4res_u.resok4.rr_info.before = fsal_get_changeid4(data->current_obj); fsal_status = obj_handle->obj_ops.removexattrs(obj_handle, &arg_REMOVEXATTR4->ra_name); if (FSAL_IS_ERROR(fsal_status)) return res_REMOVEXATTR4->status = nfs4_Errno_state( state_error_convert(fsal_status)); res_REMOVEXATTR4->REMOVEXATTR4res_u.resok4.rr_info.after = fsal_get_changeid4(data->current_obj); return res_REMOVEXATTR4->status; } /** * @brief Free memory allocated for REMOVEXATTR result * * This function fres any memory allocated for the result of the * NFS4_OP_REMOVEXATTR operation. * * @param[in,out] resp nfs4_op results */ void nfs4_op_removexattr_Free(nfs_resop4 *resp) { /* Nothing to be done */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs4_pseudo.c000066400000000000000000000402531324272410200215100ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs4_pseudo.c * @brief Routines used for managing the NFS4 pseudo file system. * * Routines used for managing the NFS4 pseudo file system. */ #include "config.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_proto_tools.h" #include "nfs_exports.h" #include "fsal.h" #include "export_mgr.h" /** * @brief Find the node for this path component * * If not found, create it. Called from token_to_proc() interator * * @param token [IN] path name component * @param arg [IN] callback state * * @return status as bool. false terminates foreach */ struct pseudofs_state { struct gsh_export *export; struct fsal_obj_handle *obj; }; /** * @brief Check to see if an export is PSEUDO * * Can be PSEUDO, or MDCACHE on PSEUDO * * @param[in] export Export to check * @return true if PSEUDO, false otherwise */ static bool is_export_pseudo(struct gsh_export *export) { /* If it's PSEUDO, it's PSEUDO */ if (strcmp(export->fsal_export->fsal->name, "PSEUDO") == 0) return true; /* If it's !MDCACHE, it's !PSEUDO */ if (strcmp(export->fsal_export->fsal->name, "MDCACHE") != 0) return false; /* If it's MDCACHE stacked on PSEUDO, it's PSEUDO */ if (strcmp(export->fsal_export->sub_export->fsal->name, "PSEUDO") == 0) return true; return false; } /** * @brief Delete the unecessary directories from pseudo FS * * @param pseudopath [IN] full path of the node * @param entry [IN] cache entry for the last directory in the path * * If this entry is present is pseudo FSAL, and is unnecessary, then remove it. * Check recursively if the parent entry is needed. * * The pseudopath is deconstructed in place to create the subsequently shorter * pseudo paths. * * When called the first time, entry is the mount point of an export that has * been unmounted from the PseudoFS. By definition, it is NOT the root of a * PseudoFS. Also, the PseudoFS root filesystem is NOT mounted and thus this * function will not be called for it. The req_op_context references the * export for the PseudoFS entry is within. Note that the caller is * responsible for checking if it is an FSAL_PSEUDO export (we only clean up * directories in FSAL_PSEUDO filesystems). */ void cleanup_pseudofs_node(char *pseudopath, struct fsal_obj_handle *obj) { struct fsal_obj_handle *parent_obj; char *pos = pseudopath + strlen(pseudopath) - 1; char *name; fsal_status_t fsal_status; /* Strip trailing / from pseudopath */ while (*pos == '/') pos--; /* Replace first trailing / if any with NUL */ pos[1] = '\0'; /* Find the previous slash. * We will NEVER back up PAST the root, so no need to check * for walking off the beginning of the string. */ while (*pos != '/') pos--; /* Remember the element name for remove */ name = pos + 1; LogDebug(COMPONENT_EXPORT, "Checking if pseudo node %s is needed", pseudopath); fsal_status = fsal_lookupp(obj, &parent_obj, NULL); if (FSAL_IS_ERROR(fsal_status)) { /* Truncate the pseudopath to be the path to the parent */ *pos = '\0'; LogCrit(COMPONENT_EXPORT, "Could not find cache entry for parent directory %s", pseudopath); return; } fsal_status = fsal_remove(parent_obj, name); if (FSAL_IS_ERROR(fsal_status)) { /* Bailout if we get directory not empty error */ if (fsal_status.major == ERR_FSAL_NOTEMPTY) { LogDebug(COMPONENT_EXPORT, "PseudoFS parent directory %s is not empty", pseudopath); } else { LogCrit(COMPONENT_EXPORT, "Removing pseudo node %s failed with %s", pseudopath, msg_fsal_err(fsal_status.major)); } goto out; } /* Before recursing the check the parent, get export lock for looking at * exp_root_obj so we can check if we have reached the root of * the mounted on export. */ PTHREAD_RWLOCK_rdlock(&op_ctx->ctx_export->lock); if (parent_obj == op_ctx->ctx_export->exp_root_obj) { LogDebug(COMPONENT_EXPORT, "Reached root of PseudoFS %s", op_ctx->ctx_export->pseudopath); PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock); goto out; } PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock); /* Truncate the pseudopath to be the path to the parent */ *pos = '\0'; /* check if the parent directory is needed */ cleanup_pseudofs_node(pseudopath, parent_obj); out: parent_obj->obj_ops.put_ref(parent_obj); } bool make_pseudofs_node(char *name, struct pseudofs_state *state) { struct fsal_obj_handle *new_node = NULL; fsal_status_t fsal_status; bool retried = false; struct attrlist sattr; retry: /* First, try to lookup the entry */ fsal_status = fsal_lookup(state->obj, name, &new_node, NULL); if (!FSAL_IS_ERROR(fsal_status)) { /* Make sure new node is a directory */ if (new_node->type != DIRECTORY) { LogCrit(COMPONENT_EXPORT, "BUILDING PSEUDOFS: Export_Id %d Path %s Pseudo Path %s LOOKUP %s is not a directory", state->export->export_id, state->export->fullpath, state->export->pseudopath, name); /* Release the reference on the new node */ new_node->obj_ops.put_ref(new_node); return false; } LogDebug(COMPONENT_EXPORT, "BUILDING PSEUDOFS: Parent %p entry %p %s FSAL %s already exists", state->obj, new_node, name, new_node->fsal->name); state->obj->obj_ops.put_ref(state->obj); /* Make new node the current node */ state->obj = new_node; return true; } if (fsal_status.major != ERR_FSAL_NOENT) { /* An error occurred */ LogCrit(COMPONENT_EXPORT, "BUILDING PSEUDOFS: Export_Id %d Path %s Pseudo Path %s LOOKUP %s failed with %s", state->export->export_id, state->export->fullpath, state->export->pseudopath, name, msg_fsal_err(fsal_status.major)); return false; } if (strncmp(op_ctx->ctx_export->fsal_export->exp_ops.get_name( op_ctx->ctx_export->fsal_export), "PSEUDO", 6) != 0) { /* Only allowed to create directories on FSAL_PSEUDO */ LogCrit(COMPONENT_EXPORT, "BUILDING PSEUDOFS: Export_Id %d Path %s Pseudo Path %s LOOKUP %s failed with %s (can't create directory on non-PSEUDO FSAL)", state->export->export_id, state->export->fullpath, state->export->pseudopath, name, msg_fsal_err(fsal_status.major)); return false; } /* Node was not found and no other error, must create node. */ fsal_prepare_attrs(&sattr, ATTR_MODE); sattr.mode = 0755; fsal_status = fsal_create(state->obj, name, DIRECTORY, &sattr, NULL, &new_node, NULL); /* Release the attributes (may release an inherited ACL) */ fsal_release_attrs(&sattr); if (fsal_status.major == ERR_FSAL_EXIST && !retried) { LogDebug(COMPONENT_EXPORT, "BUILDING PSEUDOFS: Parent %p Node %p %s seems to already exist, try LOOKUP again", state->obj, new_node, name); retried = true; goto retry; } if (FSAL_IS_ERROR(fsal_status)) { /* An error occurred */ LogCrit(COMPONENT_EXPORT, "BUILDING PSEUDOFS: Export_Id %d Path %s Pseudo Path %s CREATE %s failed with %s", state->export->export_id, state->export->fullpath, state->export->pseudopath, name, msg_fsal_err(fsal_status.major)); return false; } LogDebug(COMPONENT_EXPORT, "BUILDING PSEUDOFS: Export_Id %d Path %s Pseudo Path %s CREATE %s obj %p state %p succeeded", state->export->export_id, state->export->fullpath, state->export->pseudopath, name, new_node, new_node->state_hdl); /* Release reference to the old node */ state->obj->obj_ops.put_ref(state->obj); /* Make new node the current node */ state->obj = new_node; return true; } /** * @brief Mount an export in the new Pseudo FS. * * @param exp [IN] export in question * * @return status as bool. */ bool pseudo_mount_export(struct gsh_export *export) { struct pseudofs_state state; char *tmp_pseudopath; char *last_slash; char *p; char *rest; fsal_status_t fsal_status; char *tok; char *saveptr = NULL; int rc; /* skip exports that aren't for NFS v4 * Also, nothing to actually do for Pseudo Root */ if ((export->export_perms.options & EXPORT_OPTION_NFSV4) == 0 || export->pseudopath == NULL || export->export_id == 0 || export->pseudopath[1] == '\0') return true; /* Initialize state and it's req_ctx. * Note that a zeroed creds works just fine as root creds. */ state.export = export; LogDebug(COMPONENT_EXPORT, "BUILDING PSEUDOFS: Export_Id %d Path %s Pseudo Path %s", export->export_id, export->fullpath, export->pseudopath); /* Make a copy of the path */ tmp_pseudopath = alloca(strlen(export->pseudopath) + 1); strcpy(tmp_pseudopath, export->pseudopath); /* Find last '/' in path */ p = tmp_pseudopath; last_slash = tmp_pseudopath; while (*p != '\0') { if (*p == '/') last_slash = p; p++; } /* Terminate path leading to junction. */ *last_slash = '\0'; LogDebug(COMPONENT_EXPORT, "BUILDING PSEUDOFS: Looking for export for %s", tmp_pseudopath); /* Now find the export we are mounted on */ op_ctx->ctx_export = get_gsh_export_by_pseudo(tmp_pseudopath, false); if (op_ctx->ctx_export == NULL) { LogFatal(COMPONENT_EXPORT, "Could not find mounted on export for %s, tmp=%s", export->pseudopath, tmp_pseudopath); } op_ctx->fsal_export = op_ctx->ctx_export->fsal_export; /* Put the slash back in */ *last_slash = '/'; /* Point to the portion of this export's pseudo path that is beyond the * mounted on export's pseudo path. */ if (op_ctx->ctx_export->pseudopath[1] == '\0') rest = tmp_pseudopath + 1; else rest = tmp_pseudopath + strlen(op_ctx->ctx_export->pseudopath) + 1; LogDebug(COMPONENT_EXPORT, "BUILDING PSEUDOFS: Export_Id %d Path %s Pseudo Path %s Rest %s", export->export_id, export->fullpath, export->pseudopath, rest); /* Get the root inode of the mounted on export */ fsal_status = nfs_export_get_root_entry(op_ctx->ctx_export, &state.obj); if (FSAL_IS_ERROR(fsal_status)) { LogCrit(COMPONENT_EXPORT, "BUILDING PSEUDOFS: Could not get root entry for Export_Id %d Path %s Pseudo Path %s", export->export_id, export->fullpath, export->pseudopath); /* Release the reference on the mounted on export. */ put_gsh_export(op_ctx->ctx_export); return false; } /* Now we need to process the rest of the path, creating directories * if necessary. */ for (tok = strtok_r(rest, "/", &saveptr); tok; tok = strtok_r(NULL, "/", &saveptr)) { rc = make_pseudofs_node(tok, &state); if (!rc) { /* Release reference on mount point inode * and the mounted on export */ state.obj->obj_ops.put_ref(state.obj); put_gsh_export(op_ctx->ctx_export); return false; } } /* Now that all entries are added to pseudofs tree, and we are pointing * to the final node, make it a proper junction. */ PTHREAD_RWLOCK_wrlock(&state.obj->state_hdl->state_lock); state.obj->state_hdl->dir.junction_export = export; PTHREAD_RWLOCK_unlock(&state.obj->state_hdl->state_lock); /* And fill in the mounted on information for the export. */ PTHREAD_RWLOCK_wrlock(&export->lock); export->exp_mounted_on_file_id = state.obj->fileid; /* Pass ref off to export */ export->exp_junction_obj = state.obj; export->exp_parent_exp = op_ctx->ctx_export; /* Add ourselves to the list of exports mounted on parent */ PTHREAD_RWLOCK_wrlock(&op_ctx->ctx_export->lock); glist_add_tail(&op_ctx->ctx_export->mounted_exports_list, &export->mounted_exports_node); PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock); PTHREAD_RWLOCK_unlock(&export->lock); LogDebug(COMPONENT_EXPORT, "BUILDING PSEUDOFS: Export_Id %d Path %s Pseudo Path %s junction %p", state.export->export_id, state.export->fullpath, state.export->pseudopath, state.obj->state_hdl->dir.junction_export); return true; } /** * @brief Build a pseudo fs from an exportlist * * foreach through the exports to create pseudofs entries. * * @return status as errno (0 == SUCCESS). */ void create_pseudofs(void) { struct root_op_context root_op_context; struct gsh_export *export; /* Initialize a root context */ init_root_op_context(&root_op_context, NULL, NULL, NFS_V4, 0, NFS_REQUEST); while (true) { export = export_take_mount_work(); if (export == NULL) break; if (!pseudo_mount_export(export)) LogFatal(COMPONENT_EXPORT, "Could not complete creating PseudoFS"); } release_root_op_context(); } /** * @brief Unmount an export from the Pseudo FS. * * @param exp [IN] export in question */ void pseudo_unmount_export(struct gsh_export *export) { struct gsh_export *mounted_on_export; struct gsh_export *sub_mounted_export; struct fsal_obj_handle *junction_inode; struct root_op_context root_op_context; /* Unmount any exports mounted on us */ while (true) { PTHREAD_RWLOCK_rdlock(&export->lock); /* Find a sub_mounted export */ sub_mounted_export = glist_first_entry(&export->mounted_exports_list, struct gsh_export, mounted_exports_node); if (sub_mounted_export == NULL) { /* If none, break out of the loop */ PTHREAD_RWLOCK_unlock(&export->lock); break; } /* Take a reference to that export. Export may be dead * already, but we should see if we can speed along its * unmounting. */ get_gsh_export_ref(sub_mounted_export); /* Drop the lock */ PTHREAD_RWLOCK_unlock(&export->lock); /* And unmount it */ pseudo_unmount_export(sub_mounted_export); /* And put the reference */ put_gsh_export(sub_mounted_export); } LogDebug(COMPONENT_EXPORT, "Unmount %s", export->pseudopath); /* Take the export write lock to get the junction inode. * We take write lock because if there is no junction inode, * we jump straight to cleaning up our presence in parent * export. */ PTHREAD_RWLOCK_wrlock(&export->lock); junction_inode = export->exp_junction_obj; if (junction_inode != NULL) { /* Clean up the junction inode */ /* Don't take a reference; there is a sentinal one */ /* Release the export lock so we can take take it write */ PTHREAD_RWLOCK_unlock(&export->lock); /* Make the node not accessible from the junction node. */ PTHREAD_RWLOCK_wrlock(&junction_inode->state_hdl->state_lock); junction_inode->state_hdl->dir.junction_export = NULL; PTHREAD_RWLOCK_unlock(&junction_inode->state_hdl->state_lock); /* Detach the export from the inode */ PTHREAD_RWLOCK_wrlock(&export->lock); export->exp_junction_obj = NULL; } /* Detach the export from the export it's mounted on */ mounted_on_export = export->exp_parent_exp; if (mounted_on_export != NULL) { export->exp_parent_exp = NULL; /* Remove ourselves from the list of exports mounted on * parent */ PTHREAD_RWLOCK_wrlock(&mounted_on_export->lock); glist_del(&export->mounted_exports_node); PTHREAD_RWLOCK_unlock(&mounted_on_export->lock); } /* Release the export lock */ PTHREAD_RWLOCK_unlock(&export->lock); if (mounted_on_export != NULL) { if (is_export_pseudo(mounted_on_export) && junction_inode != NULL) { char *pseudopath = gsh_strdup(export->pseudopath); /* Initialize req_ctx */ init_root_op_context( &root_op_context, mounted_on_export, mounted_on_export->fsal_export, NFS_V4, 0, NFS_REQUEST); /* Remove the unused PseudoFS nodes */ cleanup_pseudofs_node(pseudopath, junction_inode); gsh_free(pseudopath); release_root_op_context(); } /* Release our reference to the export we are mounted on. */ put_gsh_export(mounted_on_export); } if (junction_inode != NULL) { /* Release the LRU reference */ junction_inode->obj_ops.put_ref(junction_inode); } } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs_null.c000066400000000000000000000036571324272410200211060ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_null.c * @brief NFS NULL procedure for all versions */ #include "config.h" #include #include #include #include #include /* for having FNDELAY */ #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "nfs4.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" /** * @brief The NFS proc null function, for all versions. * * @param[in] arg ignored * @param[in] req ignored * @param[out] res ignored */ int nfs_null(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling NFS_NULL"); return NFS3_OK; } /**n * @brief Free memory allocated for the nfs_null result * * This function frees any memory allocated for the result of the * nfs_null operation. * * @param[in,out] res Result structure * */ void nfs_null_free(nfs_res_t *res) { /* Nothing to do here */ } nfs-ganesha-2.6.0/src/Protocols/NFS/nfs_proto_tools.c000066400000000000000000003276501324272410200225210ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * nfs_fh4 * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_proto_tools.c * @brief A set of functions used to managed NFS. * * A set of functions used to managed NFS. */ #include "log.h" #include "fsal.h" #include "fsal_convert.h" #include "nfs_core.h" #include "nfs_convert.h" #include "nfs_exports.h" #include "nfs_proto_tools.h" #include "nfs4_acls.h" #include "idmapper.h" #include "export_mgr.h" /* Define mapping of NFS4 who name and type. */ static struct { char *string; int stringlen; int type; } whostr_2_type_map[] = { { .string = "OWNER@", .stringlen = sizeof("OWNER@") - 1, .type = FSAL_ACE_SPECIAL_OWNER, }, { .string = "GROUP@", .stringlen = sizeof("GROUP@") - 1, .type = FSAL_ACE_SPECIAL_GROUP, }, { .string = "EVERYONE@", .stringlen = sizeof("EVERYONE@") - 1, .type = FSAL_ACE_SPECIAL_EVERYONE, }, }; #ifdef _USE_NFS3 /** * Converts FSAL Attributes to NFSv3 PostOp Attributes structure. * * This function converts FSAL Attributes to NFSv3 PostOp Attributes * structure. * * If attrs is passed in, the caller MUST call fsal_release_attrs. * * @param[in] obj FSAL object * @param[out] Fattr NFSv3 PostOp structure attributes. * @param[in] attrs Optional attributes passed in * */ void nfs_SetPostOpAttr(struct fsal_obj_handle *obj, post_op_attr *Fattr, struct attrlist *attrs) { struct attrlist attr_buf, *pattrs = attrs; if (attrs == NULL) { pattrs = &attr_buf; fsal_prepare_attrs(pattrs, ATTRS_NFS3 | ATTR_RDATTR_ERR); (void) obj->obj_ops.getattrs(obj, pattrs); } /* Check if attributes follow and place the following attributes */ Fattr->attributes_follow = nfs3_FSALattr_To_Fattr(obj, pattrs, &Fattr->post_op_attr_u.attributes); if (attrs == NULL) { /* Release any attributes fetched. Caller MUST release any * attributes that were passed in. */ fsal_release_attrs(pattrs); } } /** * @brief Converts FSAL Attributes to NFSv3 PreOp Attributes structure. * * This function Converts FSAL Attributes to NFSv3 PreOp Attributes * structure. * * @param[in] obj FSAL object * @param[out] attr NFSv3 PreOp structure attributes. */ void nfs_SetPreOpAttr(struct fsal_obj_handle *obj, pre_op_attr *attr) { fsal_status_t status; struct attrlist attrs; fsal_prepare_attrs(&attrs, ATTR_SIZE | ATTR_CTIME | ATTR_MTIME); status = obj->obj_ops.getattrs(obj, &attrs); if (FSAL_IS_ERROR(status)) attr->attributes_follow = false; else { attr->pre_op_attr_u.attributes.size = attrs.filesize; attr->pre_op_attr_u.attributes.mtime.tv_sec = attrs.mtime.tv_sec; attr->pre_op_attr_u.attributes.mtime.tv_nsec = attrs.mtime.tv_nsec; attr->pre_op_attr_u.attributes.ctime.tv_sec = attrs.ctime.tv_sec; attr->pre_op_attr_u.attributes.ctime.tv_nsec = attrs.ctime.tv_nsec; attr->attributes_follow = TRUE; } fsal_release_attrs(&attrs); } /** * @brief Set NFSv3 Weak Cache Coherency structure * * This function sets NFSv3 Weak Cache Coherency structure. * * @param[in] before_attr Pre-op attrs for before state * @param[in] obj The FSAL object after operation * @param[out] wcc_data the Weak Cache Coherency structure * */ void nfs_SetWccData(const struct pre_op_attr *before_attr, struct fsal_obj_handle *obj, wcc_data *wcc_data) { if (before_attr == NULL) wcc_data->before.attributes_follow = false; /* Build directory post operation attributes */ nfs_SetPostOpAttr(obj, &wcc_data->after, NULL); } /* nfs_SetWccData */ #endif /* _USE_NFS3 */ /** * @brief Indicate if an error is retryable * * This function indicates if an error is retryable or not. * * @param fsal_errors [IN] input FSAL error value, to be tested. * * @return true if retryable, false otherwise. * * @todo: Not implemented for NOW BUGAZEOMEU * */ bool nfs_RetryableError(fsal_errors_t fsal_errors) { switch (fsal_errors) { case ERR_FSAL_IO: case ERR_FSAL_NXIO: if (nfs_param.core_param.drop_io_errors) { /* Drop the request */ return true; } else { /* Propagate error to the client */ return false; } break; case ERR_FSAL_INVAL: case ERR_FSAL_OVERFLOW: if (nfs_param.core_param.drop_inval_errors) { /* Drop the request */ return true; } else { /* Propagate error to the client */ return false; } break; case ERR_FSAL_DELAY: if (nfs_param.core_param.drop_delay_errors) { /* Drop the request */ return true; } else { /* Propagate error to the client */ return false; } break; case ERR_FSAL_NO_ERROR: LogCrit(COMPONENT_NFSPROTO, "Possible implementation error: ERR_FSAL_NO_ERROR managed as an error"); return false; case ERR_FSAL_NOMEM: case ERR_FSAL_NOT_OPENED: /* Internal error, should be dropped and retryed */ return true; case ERR_FSAL_NOTDIR: case ERR_FSAL_SYMLINK: case ERR_FSAL_BADTYPE: case ERR_FSAL_EXIST: case ERR_FSAL_NOTEMPTY: case ERR_FSAL_NOENT: case ERR_FSAL_ACCESS: case ERR_FSAL_ISDIR: case ERR_FSAL_PERM: case ERR_FSAL_NOSPC: case ERR_FSAL_ROFS: case ERR_FSAL_STALE: case ERR_FSAL_FHEXPIRED: case ERR_FSAL_SEC: case ERR_FSAL_DQUOT: case ERR_FSAL_NO_QUOTA: case ERR_FSAL_NOTSUPP: case ERR_FSAL_ATTRNOTSUPP: case ERR_FSAL_UNION_NOTSUPP: case ERR_FSAL_NAMETOOLONG: case ERR_FSAL_BADCOOKIE: case ERR_FSAL_FBIG: case ERR_FSAL_FILE_OPEN: case ERR_FSAL_XDEV: case ERR_FSAL_MLINK: case ERR_FSAL_TOOSMALL: case ERR_FSAL_SHARE_DENIED: case ERR_FSAL_LOCKED: case ERR_FSAL_FAULT: case ERR_FSAL_SERVERFAULT: case ERR_FSAL_DEADLOCK: case ERR_FSAL_BADNAME: case ERR_FSAL_CROSS_JUNCTION: case ERR_FSAL_IN_GRACE: case ERR_FSAL_BADHANDLE: case ERR_FSAL_NO_DATA: case ERR_FSAL_BLOCKED: case ERR_FSAL_INTERRUPT: case ERR_FSAL_NOT_INIT: case ERR_FSAL_ALREADY_INIT: case ERR_FSAL_BAD_INIT: case ERR_FSAL_TIMEOUT: case ERR_FSAL_NO_ACE: case ERR_FSAL_BAD_RANGE: /* Non retryable error, return error to client */ return false; } /* Should never reach this */ LogCrit(COMPONENT_NFSPROTO, "fsal_errors=%u not managed properly in %s", fsal_errors, __func__); return false; } /** * @brief Returns the maximun attribute index possbile for a 4.x protocol. * * This function returns the maximun attribute index possbile for a * 4.x protocol. * returns -1 if the protocol is not recognized. * * @param minorversion[IN] input minorversion of the protocol. * * @return the max index possbile for the minorversion, -1 if minorversion is * not recognized. * */ static inline int nfs4_max_attr_index(compound_data_t *data) { if (data) { enum nfs4_minor_vers minorversion = data->minorversion; switch (minorversion) { case NFS4_MINOR_VERS_0: return FATTR4_MOUNTED_ON_FILEID; case NFS4_MINOR_VERS_1: return FATTR4_FS_CHARSET_CAP; case NFS4_MINOR_VERS_2: return FATTR4_XATTR_SUPPORT; } } else { return FATTR4_XATTR_SUPPORT; } /* Should never be here */ LogFatal(COMPONENT_NFS_V4, "Unexpected minor version for NFSv4"); return -1; } /** * @brief Check if a specific attribute is supported by the FSAL or if * the attribute isn't indicated in attrmask, is it at least * supported by Ganesha. * * @param[in] attr The NFSv4 attribute index of interest * @param[in] fsal_supported The FSAL attrmask_t indicating which are supported */ static inline bool atrib_supported(int attr, attrmask_t fsal_supported) { return fattr4tab[attr].supported && (fattr4tab[attr].attrmask == 0 || (fsal_supported & fattr4tab[attr].attrmask) != 0); } /* NFSv4.0+ Attribute management * XDR encode/decode/compare functions for FSAL <-> Fattr4 translations * There is a set of functions for each and every attribute in the tables * on page 39-46 of RFC3530. The translate between internal and the wire. * Ordered by attribute number */ /* * FATTR4_SUPPORTED_ATTRS */ /** supported attributes * this drives off of the table but it really should drive off of the * fs_supports in the export */ static fattr_xdr_result encode_supported_attrs(XDR *xdr, struct xdr_attrs_args *args) { struct bitmap4 bits; int attr, offset; int max_attr_idx; memset(&bits, 0, sizeof(bits)); max_attr_idx = nfs4_max_attr_index(args->data); for (attr = FATTR4_SUPPORTED_ATTRS; attr <= max_attr_idx; attr++) { LogAttrlist(COMPONENT_NFS_V4, NIV_FULL_DEBUG, "attrs ", args->attrs, false); if (atrib_supported(attr, args->attrs->supported)) { bool res = set_attribute_in_bitmap(&bits, attr); assert(res); } } if (!inline_xdr_u_int32_t(xdr, &bits.bitmap4_len)) return FATTR_XDR_FAILED; for (offset = 0; offset < bits.bitmap4_len; offset++) { if (!inline_xdr_u_int32_t(xdr, &bits.map[offset])) return FATTR_XDR_FAILED; } return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_supported_attrs(XDR *xdr, struct xdr_attrs_args *args) { struct bitmap4 bits; int attr, offset; int max_attr_idx; max_attr_idx = nfs4_max_attr_index(args->data); if (!inline_xdr_u_int32_t(xdr, &bits.bitmap4_len)) return FATTR_XDR_FAILED; if (bits.bitmap4_len > BITMAP4_MAPLEN) { LogWarn(COMPONENT_NFS_V4, "Decoded a too long bitmap : %d is more than %d", bits.bitmap4_len, BITMAP4_MAPLEN); return FATTR_XDR_FAILED; } for (offset = 0; offset < bits.bitmap4_len; offset++) { if (!inline_xdr_u_int32_t(xdr, &bits.map[offset])) return FATTR_XDR_FAILED; } FSAL_CLEAR_MASK(args->attrs->supported); for (attr = FATTR4_SUPPORTED_ATTRS; attr < bits.bitmap4_len*32 && attr <= max_attr_idx; attr++) { if (attribute_is_set(&bits, attr) && fattr4tab[attr].attrmask) FSAL_SET_MASK(args->attrs->supported, fattr4tab[attr].attrmask); } return FATTR_XDR_SUCCESS; } /* * FATTR4_TYPE */ static fattr_xdr_result encode_type(XDR *xdr, struct xdr_attrs_args *args) { uint32_t file_type; switch (args->attrs->type) { case REGULAR_FILE: case EXTENDED_ATTR: file_type = NF4REG; /* Regular file */ break; case DIRECTORY: file_type = NF4DIR; /* Directory */ break; case BLOCK_FILE: file_type = NF4BLK; /* Special File - block device */ break; case CHARACTER_FILE: file_type = NF4CHR; /* Special File - character device */ break; case SYMBOLIC_LINK: file_type = NF4LNK; /* Symbolic Link */ break; case SOCKET_FILE: file_type = NF4SOCK; /* Special File - socket */ break; case FIFO_FILE: file_type = NF4FIFO; /* Special File - fifo */ break; default: /* includes NO_FILE_TYPE & FS_JUNCTION: */ return FATTR_XDR_FAILED; /* silently skip bogus? */ } /* switch( pattr->type ) */ if (!xdr_u_int32_t(xdr, &file_type)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_type(XDR *xdr, struct xdr_attrs_args *args) { uint32_t t; if (!xdr_u_int32_t(xdr, &t)) return FATTR_XDR_FAILED; switch (t) { case NF4REG: args->attrs->type = REGULAR_FILE; break; case NF4DIR: args->attrs->type = DIRECTORY; break; case NF4BLK: args->attrs->type = BLOCK_FILE; break; case NF4CHR: args->attrs->type = CHARACTER_FILE; break; case NF4LNK: args->attrs->type = SYMBOLIC_LINK; break; case NF4SOCK: args->attrs->type = SOCKET_FILE; break; case NF4FIFO: args->attrs->type = FIFO_FILE; break; default: /* For wanting of a better solution */ return FATTR_XDR_FAILED; } /*update both args->attrs->type and args->type*/ args->type = args->attrs->type; return FATTR_XDR_SUCCESS; } /* * FATTR4_FH_EXPIRE_TYPE */ static fattr_xdr_result encode_expiretype(XDR *xdr, struct xdr_attrs_args *args) { uint32_t expire_type; /* For the moment, we handle only the persistent filehandle */ expire_type = FH4_PERSISTENT; if (!xdr_u_int32_t(xdr, &expire_type)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_expiretype(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_CHANGE */ static fattr_xdr_result encode_change(XDR *xdr, struct xdr_attrs_args *args) { if (!xdr_u_int64_t(xdr, &args->attrs->change)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_change(XDR *xdr, struct xdr_attrs_args *args) { uint64_t change; if (!xdr_u_int64_t(xdr, &change)) return FATTR_XDR_FAILED; args->attrs->chgtime.tv_sec = (uint32_t) change; args->attrs->chgtime.tv_nsec = 0; args->attrs->change = change; return FATTR_XDR_SUCCESS; } /* * FATTR4_SIZE */ static fattr_xdr_result encode_filesize(XDR *xdr, struct xdr_attrs_args *args) { if (!xdr_u_int64_t(xdr, &args->attrs->filesize)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_filesize(XDR *xdr, struct xdr_attrs_args *args) { if (!xdr_u_int64_t(xdr, &args->attrs->filesize)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } /* * FATTR4_LINK_SUPPORT */ static fattr_xdr_result encode_linksupport(XDR *xdr, struct xdr_attrs_args *args) { struct fsal_export *export; int linksupport = FALSE; if (args->data != NULL) { export = op_ctx->fsal_export; linksupport = export->exp_ops.fs_supports(export, fso_link_support); } if (!xdr_bool(xdr, &linksupport)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_linksupport(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_SYMLINK_SUPPORT */ static fattr_xdr_result encode_symlinksupport(XDR *xdr, struct xdr_attrs_args *args) { struct fsal_export *export; int symlinksupport = FALSE; if (args->data != NULL) { export = op_ctx->fsal_export; symlinksupport = export->exp_ops.fs_supports(export, fso_symlink_support); } if (!xdr_bool(xdr, &symlinksupport)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_symlinksupport(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_NAMED_ATTR */ /* For this version of the binary, named attributes is not supported */ static fattr_xdr_result encode_namedattrsupport(XDR *xdr, struct xdr_attrs_args *args) { struct fsal_export *export; int namedattrsupport = FALSE; if (args->data != NULL) { export = op_ctx->fsal_export; namedattrsupport = export->exp_ops.fs_supports(export, fso_named_attr); } if (!xdr_bool(xdr, &namedattrsupport)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_namedattrsupport(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_FSID */ static fattr_xdr_result encode_fsid(XDR *xdr, struct xdr_attrs_args *args) { fsid4 fsid = {0, 0}; if (args->data != NULL && op_ctx_export_has_option_set(EXPORT_OPTION_FSID_SET)) { fsid.major = op_ctx->ctx_export->filesystem_id.major; fsid.minor = op_ctx->ctx_export->filesystem_id.minor; } else { fsid.major = args->fsid.major; fsid.minor = args->fsid.minor; } LogDebug(COMPONENT_NFS_V4, "fsid.major = %"PRIu64", fsid.minor = %"PRIu64, fsid.major, fsid.minor); if (!xdr_u_int64_t(xdr, &fsid.major)) return FATTR_XDR_FAILED; if (!xdr_u_int64_t(xdr, &fsid.minor)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_fsid(XDR *xdr, struct xdr_attrs_args *args) { if (!xdr_u_int64_t(xdr, &args->fsid.major)) return FATTR_XDR_FAILED; if (!xdr_u_int64_t(xdr, &args->fsid.minor)) return FATTR_XDR_FAILED; /*update both : args->fsid and args->attrs->fsid*/ if (args->attrs != NULL) args->attrs->fsid = args->fsid; return FATTR_XDR_SUCCESS; } /* * FATTR4_UNIQUE_HANDLES */ static fattr_xdr_result encode_uniquehandles(XDR *xdr, struct xdr_attrs_args *args) { struct fsal_export *export; int uniquehandles = FALSE; if (args->data != NULL) { export = op_ctx->fsal_export; uniquehandles = export->exp_ops.fs_supports(export, fso_unique_handles); } if (!inline_xdr_bool(xdr, &uniquehandles)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_uniquehandles(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_LEASE_TIME */ static fattr_xdr_result encode_leaselife(XDR *xdr, struct xdr_attrs_args *args) { if (!inline_xdr_u_int32_t(xdr, &nfs_param.nfsv4_param.lease_lifetime)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_leaselife(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_RDATTR_ERROR */ /** todo we don't really do anything with rdattr_error. It is needed for full * readdir error handling. Check this to be correct when we do... */ static fattr_xdr_result encode_rdattr_error(XDR *xdr, struct xdr_attrs_args *args) { if (!inline_xdr_u_int32_t(xdr, &args->rdattr_error)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_rdattr_error(XDR *xdr, struct xdr_attrs_args *args) { if (!inline_xdr_u_int32_t(xdr, &args->rdattr_error)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } /* * FATTR4_ACL */ static fattr_xdr_result encode_acl(XDR *xdr, struct xdr_attrs_args *args) { if (args->attrs->acl) { fsal_ace_t *ace; int i; char *name = NULL; LogFullDebug(COMPONENT_NFS_V4, "Number of ACEs = %u", args->attrs->acl->naces); if (!inline_xdr_u_int32_t(xdr, &(args->attrs->acl->naces))) return FATTR_XDR_FAILED; for (ace = args->attrs->acl->aces; ace < args->attrs->acl->aces + args->attrs->acl->naces; ace++) { LogFullDebug(COMPONENT_NFS_V4, "type=0X%x, flag=0X%x, perm=0X%x", ace->type, ace->flag, ace->perm); if (!inline_xdr_u_int32_t(xdr, &ace->type)) return FATTR_XDR_FAILED; if (!inline_xdr_u_int32_t(xdr, &ace->flag)) return FATTR_XDR_FAILED; if (!inline_xdr_u_int32_t(xdr, &ace->perm)) return FATTR_XDR_FAILED; if (IS_FSAL_ACE_SPECIAL_ID(*ace)) { for (i = 0; i < FSAL_ACE_SPECIAL_EVERYONE; i++) { if (whostr_2_type_map[i].type == ace->who.uid) { name = whostr_2_type_map[i] .string; break; } } if (name == NULL || !xdr_string(xdr, &name, MAXNAMLEN)) return FATTR_XDR_FAILED; } else if (IS_FSAL_ACE_GROUP_ID(*ace)) { /* Encode group name. */ if (!xdr_encode_nfs4_group(xdr, ace->who.gid)) return FATTR_XDR_FAILED; } else { if (!xdr_encode_nfs4_owner (xdr, ace->who.uid)) { return FATTR_XDR_FAILED; } } } /* for ace... */ } else { uint32_t noacls = 0; if (!inline_xdr_u_int32_t(xdr, &noacls)) return FATTR_XDR_FAILED; } return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_acl(XDR *xdr, struct xdr_attrs_args *args) { fsal_acl_status_t status; fsal_acl_data_t acldata; fsal_ace_t *ace; char buffer[MAXNAMLEN + 1]; char *buffp = buffer; utf8string utf8buffer; fattr_xdr_result res = FATTR_XDR_FAILED; int who = 0; /* not ACE_SPECIAL anything */ acldata.naces = 0; if (!inline_xdr_u_int32_t(xdr, &acldata.naces)) return FATTR_XDR_FAILED; if (acldata.naces == 0) return FATTR_XDR_SUCCESS; /* no acls is not a crime */ acldata.aces = (fsal_ace_t *) nfs4_ace_alloc(acldata.naces); if (acldata.aces == NULL) { LogCrit(COMPONENT_NFS_V4, "Failed to allocate ACEs"); args->nfs_status = NFS4ERR_SERVERFAULT; return FATTR_XDR_FAILED; } for (ace = acldata.aces; ace < acldata.aces + acldata.naces; ace++) { int i; who = 0; if (!inline_xdr_u_int32_t(xdr, &ace->type)) goto baderr; if (ace->type >= FSAL_ACE_TYPE_MAX) { LogFullDebug(COMPONENT_NFS_V4, "Bad ACE type 0x%x", ace->type); res = FATTR_XDR_NOOP; goto baderr; } if (!inline_xdr_u_int32_t(xdr, &ace->flag)) goto baderr; if (!inline_xdr_u_int32_t(xdr, &ace->perm)) goto baderr; if (!inline_xdr_string(xdr, &buffp, MAXNAMLEN)) goto baderr; for (i = 0; i < FSAL_ACE_SPECIAL_EVERYONE; i++) { if (strncmp (buffer, whostr_2_type_map[i].string, strlen(buffer)) == 0) { who = whostr_2_type_map[i].type; break; } } if (who != 0) { /* Clear group flag for special users */ ace->flag &= ~(FSAL_ACE_FLAG_GROUP_ID); ace->iflag |= FSAL_ACE_IFLAG_SPECIAL_ID; ace->who.uid = who; LogFullDebug(COMPONENT_NFS_V4, "ACE special who.uid = 0x%x", ace->who.uid); } else { utf8buffer.utf8string_val = buffer; utf8buffer.utf8string_len = strlen(buffer); if (IS_FSAL_ACE_GROUP_ID(*ace)) { /* Decode group. */ struct gsh_buffdesc gname = { .addr = utf8buffer.utf8string_val, .len = utf8buffer.utf8string_len }; if (!name2gid( &gname, &ace->who.gid, get_anonymous_gid())) goto baderr; LogFullDebug(COMPONENT_NFS_V4, "ACE who.gid = 0x%x", ace->who.gid); } else { /* Decode user. */ struct gsh_buffdesc uname = { .addr = utf8buffer.utf8string_val, .len = utf8buffer.utf8string_len }; if (!name2uid( &uname, &ace->who.uid, get_anonymous_uid())) goto baderr; LogFullDebug(COMPONENT_NFS_V4, "ACE who.uid = 0x%x", ace->who.uid); } } /* Check if we can map a name string to uid or gid. If we * can't, do cleanup and bubble up NFS4ERR_BADOWNER. */ if ((IS_FSAL_ACE_GROUP_ID(*ace) ? ace->who.gid : ace->who.uid) == -1) { LogFullDebug(COMPONENT_NFS_V4, "ACE bad owner"); args->nfs_status = NFS4ERR_BADOWNER; goto baderr; } } args->attrs->acl = nfs4_acl_new_entry(&acldata, &status); if (args->attrs->acl == NULL) { LogCrit(COMPONENT_NFS_V4, "Failed to create a new obj for ACL"); args->nfs_status = NFS4ERR_SERVERFAULT; /* acldata has already been freed */ return FATTR_XDR_FAILED; } else { LogFullDebug(COMPONENT_NFS_V4, "Successfully created a new obj for ACL, status = %u", status); } /* Set new ACL */ LogFullDebug(COMPONENT_NFS_V4, "new acl = %p", args->attrs->acl); return FATTR_XDR_SUCCESS; baderr: nfs4_ace_free(acldata.aces); return res; } /* * FATTR4_ACLSUPPORT */ static fattr_xdr_result encode_aclsupport(XDR *xdr, struct xdr_attrs_args *args) { struct fsal_export *export; uint32_t aclsupport = 0; if (args->data != NULL) { export = op_ctx->fsal_export; aclsupport = export->exp_ops.fs_acl_support(export); } if (!inline_xdr_u_int32_t(xdr, &aclsupport)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_aclsupport(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_ARCHIVE */ static fattr_xdr_result encode_archive(XDR *xdr, struct xdr_attrs_args *args) { uint32_t archive; archive = FALSE; if (!inline_xdr_bool(xdr, &archive)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_archive(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_CANSETTIME */ static fattr_xdr_result encode_cansettime(XDR *xdr, struct xdr_attrs_args *args) { struct fsal_export *export; uint32_t cansettime = FALSE; if (args->data != NULL) { export = op_ctx->fsal_export; cansettime = export->exp_ops.fs_supports(export, fso_cansettime); } if (!inline_xdr_bool(xdr, &cansettime)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_cansettime(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_CASE_INSENSITIVE */ static fattr_xdr_result encode_case_insensitive(XDR *xdr, struct xdr_attrs_args *args) { struct fsal_export *export; uint32_t caseinsensitive = FALSE; if (args->data != NULL) { export = op_ctx->fsal_export; caseinsensitive = export->exp_ops.fs_supports(export, fso_case_insensitive); } if (!inline_xdr_bool(xdr, &caseinsensitive)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_case_insensitive(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_CASE_PRESERVING */ static fattr_xdr_result encode_case_preserving(XDR *xdr, struct xdr_attrs_args *args) { struct fsal_export *export; uint32_t casepreserving = FALSE; if (args->data != NULL) { export = op_ctx->fsal_export; casepreserving = export->exp_ops.fs_supports(export, fso_case_preserving); } if (!inline_xdr_bool(xdr, &casepreserving)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_case_preserving(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_CHOWN_RESTRICTED */ static fattr_xdr_result encode_chown_restricted(XDR *xdr, struct xdr_attrs_args *args) { struct fsal_export *export; uint32_t chownrestricted = FALSE; if (args->data != NULL) { export = op_ctx->fsal_export; chownrestricted = export->exp_ops.fs_supports(export, fso_chown_restricted); } if (!inline_xdr_bool(xdr, &chownrestricted)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_chown_restricted(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_FILEHANDLE */ static fattr_xdr_result encode_filehandle(XDR *xdr, struct xdr_attrs_args *args) { if (args->hdl4 == NULL || args->hdl4->nfs_fh4_val == NULL) return FATTR_XDR_FAILED; if (!inline_xdr_bytes (xdr, &args->hdl4->nfs_fh4_val, &args->hdl4->nfs_fh4_len, NFS4_FHSIZE)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } /* zero copy file handle reference dropped as potentially unsafe XDR */ static fattr_xdr_result decode_filehandle(XDR *xdr, struct xdr_attrs_args *args) { uint32_t fhlen = 0, pos; if (args->hdl4 == NULL || args->hdl4->nfs_fh4_val == NULL) { if (!inline_xdr_u_int32_t(xdr, &fhlen)) return FATTR_XDR_FAILED; pos = xdr_getpos(xdr); if (!xdr_setpos(xdr, pos + fhlen)) return FATTR_XDR_FAILED; } else { if (!inline_xdr_bytes (xdr, &args->hdl4->nfs_fh4_val, &args->hdl4->nfs_fh4_len, NFS4_FHSIZE)) return FATTR_XDR_FAILED; } return FATTR_XDR_SUCCESS; } /* * FATTR4_FILEID */ static fattr_xdr_result encode_fileid(XDR *xdr, struct xdr_attrs_args *args) { if (!inline_xdr_u_int64_t(xdr, &args->fileid)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_fileid(XDR *xdr, struct xdr_attrs_args *args) { if (!inline_xdr_u_int64_t(xdr, &args->fileid)) return FATTR_XDR_FAILED; /*update both : args->fileid and args->attrs->fileid*/ if (args->attrs != NULL) args->attrs->fileid = args->fileid; return FATTR_XDR_SUCCESS; } /* * Dynamic file system info */ static fattr_xdr_result encode_fetch_fsinfo(struct xdr_attrs_args *args) { fsal_status_t fsal_status = {0, 0}; if (args->data != NULL && args->data->current_obj != NULL) { fsal_status = fsal_statfs(args->data->current_obj, args->dynamicinfo); } else { args->dynamicinfo->avail_files = 512; args->dynamicinfo->free_files = 512; args->dynamicinfo->total_files = 512; args->dynamicinfo->total_bytes = 1024000; args->dynamicinfo->free_bytes = 512000; args->dynamicinfo->avail_bytes = 512000; } if (FSAL_IS_ERROR(fsal_status)) return FATTR_XDR_FAILED; args->statfscalled = true; return TRUE; } /* * FATTR4_FILES_AVAIL */ static fattr_xdr_result encode_files_avail(XDR *xdr, struct xdr_attrs_args *args) { if (!args->statfscalled) if (!encode_fetch_fsinfo(args)) return FATTR_XDR_FAILED; if (!inline_xdr_u_int64_t(xdr, &args->dynamicinfo->avail_files)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_files_avail(XDR *xdr, struct xdr_attrs_args *args) { return inline_xdr_u_int64_t(xdr, &args->dynamicinfo->avail_files) ? FATTR_XDR_SUCCESS : FATTR_XDR_FAILED; } /* * FATTR4_FILES_FREE */ static fattr_xdr_result encode_files_free(XDR *xdr, struct xdr_attrs_args *args) { if (!args->statfscalled) if (!encode_fetch_fsinfo(args)) return FATTR_XDR_FAILED; if (!inline_xdr_u_int64_t(xdr, &args->dynamicinfo->free_files)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_files_free(XDR *xdr, struct xdr_attrs_args *args) { return inline_xdr_u_int64_t(xdr, &args->dynamicinfo->free_files) ? FATTR_XDR_SUCCESS : FATTR_XDR_FAILED; } /* * FATTR4_FILES_TOTAL */ static fattr_xdr_result encode_files_total(XDR *xdr, struct xdr_attrs_args *args) { if (!args->statfscalled) if (!encode_fetch_fsinfo(args)) return FATTR_XDR_FAILED; if (!inline_xdr_u_int64_t(xdr, &args->dynamicinfo->total_files)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_files_total(XDR *xdr, struct xdr_attrs_args *args) { return xdr_u_int64_t(xdr, &args->dynamicinfo->total_files) ? FATTR_XDR_SUCCESS : FATTR_XDR_FAILED; } /* * allocate a dynamic pathname4 structure out of a filesystem path */ void nfs4_pathname4_alloc(pathname4 *pathname4, char *path) { char *path_sav, *token, *path_work; int i = 0; if (path == NULL) { pathname4->pathname4_val = gsh_malloc(sizeof(component4)); pathname4->pathname4_len = 1; pathname4->pathname4_val->utf8string_val = gsh_calloc(MAXPATHLEN, sizeof(char)); pathname4->pathname4_val->utf8string_len = MAXPATHLEN; } else { path_sav = gsh_strdup(path); /* count tokens */ path_work = path_sav; while ((token = strsep(&path_work, "/")) != NULL) { if (strlen(token) > 0) { i++; } } LogDebug(COMPONENT_NFS_V4, "%s has %d tokens", path, i); /* reset content of path_sav */ strcpy(path_sav, path); path_work = path_sav; /* fill component4 */ pathname4->pathname4_val = gsh_malloc(i * sizeof(component4)); i = 0; while ((token = strsep(&path_work, "/")) != NULL) { if (strlen(token) > 0) { LogDebug(COMPONENT_NFS_V4, "token %d is %s", i, token); pathname4->pathname4_val[i].utf8string_val = gsh_strdup(token); pathname4->pathname4_val[i].utf8string_len = strlen(token); i++; } } pathname4->pathname4_len = i; gsh_free(path_sav); } } /* * free dynamic pathname4 structure */ void nfs4_pathname4_free(pathname4 *pathname4) { int i; if (pathname4 == NULL) return; i = pathname4->pathname4_len; LogFullDebug(COMPONENT_NFS_V4, "number of pathname components to free: %d", i); if (pathname4->pathname4_val == NULL) return; while (i-- > 0) { if (pathname4->pathname4_val[i].utf8string_val != NULL) { LogFullDebug(COMPONENT_NFS_V4, "freeing component %d: %s", i+1, pathname4->pathname4_val[i].utf8string_val); gsh_free(pathname4->pathname4_val[i].utf8string_val); pathname4->pathname4_val[i].utf8string_val = NULL; } } gsh_free(pathname4->pathname4_val); pathname4->pathname4_val = NULL; } /* * FATTR4_FS_LOCATIONS */ static fattr_xdr_result encode_fs_locations(XDR *xdr, struct xdr_attrs_args *args) { fsal_status_t st; fs_locations4 fs_locs; fs_location4 fs_loc; component4 fs_server; char server[MAXHOSTNAMELEN]; if (args->data == NULL || args->data->current_obj == NULL) return FATTR_XDR_NOOP; if (args->data->current_obj->type != DIRECTORY) return FATTR_XDR_NOOP; nfs4_pathname4_alloc(&fs_locs.fs_root, NULL); fs_server.utf8string_len = sizeof(server); fs_server.utf8string_val = server; fs_loc.server.server_len = 1; fs_loc.server.server_val = &fs_server; nfs4_pathname4_alloc(&fs_loc.rootpath, NULL); fs_locs.locations.locations_len = 1; fs_locs.locations.locations_val = &fs_loc; /* For now allow for one fs locations, fs_locations() should set: root and update its length, can not be bigger than MAXPATHLEN path and update its length, can not be bigger than MAXPATHLEN server and update its length, can not be bigger than MAXHOSTNAMELEN */ st = args->data->current_obj->obj_ops.fs_locations( args->data->current_obj, &fs_locs); if (FSAL_IS_ERROR(st)) { strcpy(fs_locs.fs_root.pathname4_val->utf8string_val, "not_supported"); strcpy(fs_loc.rootpath.pathname4_val->utf8string_val, "not_supported"); strcpy(server, "not_supported"); fs_locs.fs_root.pathname4_val->utf8string_len = strlen(fs_locs.fs_root.pathname4_val->utf8string_val); fs_loc.rootpath.pathname4_val->utf8string_len = strlen(fs_loc.rootpath.pathname4_val->utf8string_val); fs_server.utf8string_len = strlen(server); LogDebug(COMPONENT_NFS_V4, "encode fs_locations obj_ops.fs_locations failed %s, %s, %s", fs_locs.fs_root.pathname4_val->utf8string_val, fs_loc.rootpath.pathname4_val->utf8string_val, server); } if (!xdr_fs_locations4(xdr, &fs_locs)) { LogEvent(COMPONENT_NFS_V4, "encode fs_locations xdr_fs_locations failed %s, %s, %s", fs_locs.fs_root.pathname4_val->utf8string_val, fs_loc.rootpath.pathname4_val->utf8string_val, server); return FATTR_XDR_FAILED; } nfs4_pathname4_free(&fs_locs.fs_root); nfs4_pathname4_free(&fs_loc.rootpath); return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_fs_locations(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_HIDDEN */ static fattr_xdr_result encode_hidden(XDR *xdr, struct xdr_attrs_args *args) { uint32_t hidden = FALSE; if (!inline_xdr_bool(xdr, &hidden)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_hidden(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_HOMOGENEOUS */ /* Unix semantic is homogeneous (all objects have the same kind of attributes) */ static fattr_xdr_result encode_homogeneous(XDR *xdr, struct xdr_attrs_args *args) { struct fsal_export *export; uint32_t homogeneous = FALSE; if (args->data != NULL) { export = op_ctx->fsal_export; homogeneous = export->exp_ops.fs_supports(export, fso_homogenous); } if (!inline_xdr_bool(xdr, &homogeneous)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_homogeneous(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_MAXFILESIZE */ static fattr_xdr_result encode_maxfilesize(XDR *xdr, struct xdr_attrs_args *args) { struct fsal_export *export; uint64_t maxfilesize = 0; if (args->data != NULL) { export = op_ctx->fsal_export; maxfilesize = export->exp_ops.fs_maxfilesize(export); } if (!inline_xdr_u_int64_t(xdr, &maxfilesize)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_maxfilesize(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_MAXLINK */ static fattr_xdr_result encode_maxlink(XDR *xdr, struct xdr_attrs_args *args) { struct fsal_export *export; uint32_t maxlink = 0; if (args->data != NULL) { export = op_ctx->fsal_export; maxlink = export->exp_ops.fs_maxlink(export); } if (!inline_xdr_u_int32_t(xdr, &maxlink)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_maxlink(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_MAXNAME */ static fattr_xdr_result encode_maxname(XDR *xdr, struct xdr_attrs_args *args) { struct fsal_export *export; uint32_t maxname = 0; if (args->data != NULL) { export = op_ctx->fsal_export; maxname = export->exp_ops.fs_maxnamelen(export); } if (!inline_xdr_u_int32_t(xdr, &maxname)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_maxname(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_MAXREAD */ /* The exports.c MAXREAD-MAXWRITE code establishes these semantics: * a. If you set the MaxWrite and MaxRead defaults in an export file * they apply. * b. If you set the MaxWrite and MaxRead defaults in the main.conf * file they apply unless overwritten by an export file setting. * c. If no settings are present in the export file or the main.conf * file then the defaults values in the FSAL apply. */ /** todo make these conditionals go away. The old code was the 'else' part of * this. this is a fast path. Do both read and write conditionals. */ static fattr_xdr_result encode_maxread(XDR *xdr, struct xdr_attrs_args *args) { uint64_t MaxRead = atomic_fetch_uint64_t(&op_ctx->ctx_export->MaxRead); if (!inline_xdr_u_int64_t(xdr, &MaxRead)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_maxread(XDR *xdr, struct xdr_attrs_args *args) { return xdr_u_int64_t(xdr, &args->dynamicinfo->maxread) ? FATTR_XDR_SUCCESS : FATTR_XDR_FAILED; } /* * FATTR4_MAXWRITE */ static fattr_xdr_result encode_maxwrite(XDR *xdr, struct xdr_attrs_args *args) { uint64_t MaxWrite = atomic_fetch_uint64_t(&op_ctx->ctx_export->MaxWrite); if (!inline_xdr_u_int64_t(xdr, &MaxWrite)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_maxwrite(XDR *xdr, struct xdr_attrs_args *args) { return xdr_u_int64_t(xdr, &args->dynamicinfo->maxwrite) ? FATTR_XDR_SUCCESS : FATTR_XDR_FAILED; } /* * FATTR4_MIMETYPE */ static fattr_xdr_result encode_mimetype(XDR *xdr, struct xdr_attrs_args *args) { int mimetype = FALSE; if (!inline_xdr_bool(xdr, &mimetype)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_mimetype(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_MODE */ static fattr_xdr_result encode_mode(XDR *xdr, struct xdr_attrs_args *args) { uint32_t file_mode = fsal2unix_mode(args->attrs->mode); if (!inline_xdr_u_int32_t(xdr, &file_mode)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_mode(XDR *xdr, struct xdr_attrs_args *args) { uint32_t file_mode = 0; if (!inline_xdr_u_int32_t(xdr, &file_mode)) return FATTR_XDR_FAILED; args->attrs->mode = unix2fsal_mode(file_mode); return FATTR_XDR_SUCCESS; } /* * FATTR4_NO_TRUNC */ static fattr_xdr_result encode_no_trunc(XDR *xdr, struct xdr_attrs_args *args) { struct fsal_export *export; uint32_t no_trunc = FALSE; if (args->data != NULL) { export = op_ctx->fsal_export; no_trunc = export->exp_ops.fs_supports(export, fso_no_trunc); } if (!inline_xdr_bool(xdr, &no_trunc)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_no_trunc(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_NUMLINKS */ static fattr_xdr_result encode_numlinks(XDR *xdr, struct xdr_attrs_args *args) { if (!inline_xdr_u_int32_t(xdr, &args->attrs->numlinks)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_numlinks(XDR *xdr, struct xdr_attrs_args *args) { if (!inline_xdr_u_int32_t(xdr, &args->attrs->numlinks)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } /* * FATTR4_OWNER */ static fattr_xdr_result encode_owner(XDR *xdr, struct xdr_attrs_args *args) { return xdr_encode_nfs4_owner(xdr, args->attrs->owner) ? FATTR_XDR_SUCCESS : FATTR_XDR_FAILED; } static fattr_xdr_result decode_owner(XDR *xdr, struct xdr_attrs_args *args) { uid_t uid; uint32_t len = 0; struct gsh_buffdesc ownerdesc; unsigned int pos, newpos; if (!inline_xdr_u_int(xdr, &len)) return FATTR_XDR_FAILED; if (len == 0) { args->nfs_status = NFS4ERR_INVAL; return FATTR_XDR_FAILED; } pos = xdr_getpos(xdr); newpos = pos + len; if (len % 4 != 0) newpos += (4 - (len % 4)); ownerdesc.len = len; ownerdesc.addr = xdr_inline_decode(xdr, len); if (!ownerdesc.addr) { LogMajor(COMPONENT_NFS_V4, "xdr_inline_decode on xdrmem stream failed!"); return FATTR_XDR_FAILED; } if (!name2uid(&ownerdesc, &uid, get_anonymous_uid())) { args->nfs_status = NFS4ERR_BADOWNER; return FATTR_BADOWNER; } xdr_setpos(xdr, newpos); args->attrs->owner = uid; return FATTR_XDR_SUCCESS; } /* * FATTR4_OWNER_GROUP */ static fattr_xdr_result encode_group(XDR *xdr, struct xdr_attrs_args *args) { return xdr_encode_nfs4_group(xdr, args->attrs->group) ? FATTR_XDR_SUCCESS : FATTR_XDR_FAILED; } static fattr_xdr_result decode_group(XDR *xdr, struct xdr_attrs_args *args) { gid_t gid; uint32_t len = 0; struct gsh_buffdesc groupdesc; unsigned int pos, newpos; if (!inline_xdr_u_int(xdr, &len)) return FATTR_XDR_FAILED; if (len == 0) { args->nfs_status = NFS4ERR_INVAL; return FATTR_XDR_FAILED; } pos = xdr_getpos(xdr); newpos = pos + len; if (len % 4 != 0) newpos += (4 - (len % 4)); groupdesc.len = len; groupdesc.addr = xdr_inline_decode(xdr, len); if (!groupdesc.addr) { LogMajor(COMPONENT_NFS_V4, "xdr_inline_decode on xdrmem stream failed!"); return FATTR_XDR_FAILED; } if (!name2gid(&groupdesc, &gid, get_anonymous_gid())) { args->nfs_status = NFS4ERR_BADOWNER; return FATTR_BADOWNER; } xdr_setpos(xdr, newpos); args->attrs->group = gid; return FATTR_XDR_SUCCESS; } /* * FATTR4_QUOTA_AVAIL_HARD */ static fattr_xdr_result encode_quota_avail_hard(XDR *xdr, struct xdr_attrs_args *args) { /** @todo: not the right answer, actual quotas should be implemented */ uint64_t quota = NFS_V4_MAX_QUOTA_HARD; if (!inline_xdr_u_int64_t(xdr, "a)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_quota_avail_hard(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_QUOTA_AVAIL_SOFT */ static fattr_xdr_result encode_quota_avail_soft(XDR *xdr, struct xdr_attrs_args *args) { uint64_t quota = NFS_V4_MAX_QUOTA_SOFT; if (!inline_xdr_u_int64_t(xdr, "a)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_quota_avail_soft(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_QUOTA_USED */ static fattr_xdr_result encode_quota_used(XDR *xdr, struct xdr_attrs_args *args) { uint64_t quota = args->attrs->filesize; if (!inline_xdr_u_int64_t(xdr, "a)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_quota_used(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_RAWDEV */ static fattr_xdr_result encode_rawdev(XDR *xdr, struct xdr_attrs_args *args) { struct specdata4 specdata4; specdata4.specdata1 = args->attrs->rawdev.major; specdata4.specdata2 = args->attrs->rawdev.minor; if (!inline_xdr_u_int32_t(xdr, &specdata4.specdata1)) return FATTR_XDR_FAILED; if (!inline_xdr_u_int32_t(xdr, &specdata4.specdata2)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_rawdev(XDR *xdr, struct xdr_attrs_args *args) { struct specdata4 specdata4 = {.specdata1 = 0, .specdata2 = 0 }; if (!inline_xdr_u_int32_t(xdr, &specdata4.specdata1)) return FATTR_XDR_FAILED; if (!inline_xdr_u_int32_t(xdr, &specdata4.specdata2)) return FATTR_XDR_FAILED; args->attrs->rawdev.major = specdata4.specdata1; args->attrs->rawdev.minor = specdata4.specdata2; return FATTR_XDR_SUCCESS; } /* * FATTR4_SPACE_AVAIL */ static fattr_xdr_result encode_sace_avail(XDR *xdr, struct xdr_attrs_args *args) { if (!args->statfscalled) if (!encode_fetch_fsinfo(args)) return FATTR_XDR_FAILED; if (!inline_xdr_u_int64_t(xdr, &args->dynamicinfo->avail_bytes)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_sace_avail(XDR *xdr, struct xdr_attrs_args *args) { return inline_xdr_u_int64_t(xdr, &args->dynamicinfo->avail_bytes) ? FATTR_XDR_SUCCESS : FATTR_XDR_FAILED; } /* * FATTR4_SPACE_FREE */ static fattr_xdr_result encode_sace_free(XDR *xdr, struct xdr_attrs_args *args) { if (!args->statfscalled) if (!encode_fetch_fsinfo(args)) return FATTR_XDR_FAILED; if (!inline_xdr_u_int64_t(xdr, &args->dynamicinfo->free_bytes)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_sace_free(XDR *xdr, struct xdr_attrs_args *args) { return inline_xdr_u_int64_t(xdr, &args->dynamicinfo->free_bytes) ? FATTR_XDR_SUCCESS : FATTR_XDR_FAILED; } /* * FATTR4_SPACE_TOTAL */ static fattr_xdr_result encode_sace_total(XDR *xdr, struct xdr_attrs_args *args) { if (!args->statfscalled) if (!encode_fetch_fsinfo(args)) return FATTR_XDR_FAILED; if (!inline_xdr_u_int64_t(xdr, &args->dynamicinfo->total_bytes)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_sace_total(XDR *xdr, struct xdr_attrs_args *args) { return inline_xdr_u_int64_t(xdr, &args->dynamicinfo->total_bytes) ? FATTR_XDR_SUCCESS : FATTR_XDR_FAILED; } /* * FATTR4_SPACE_USED */ /* the number of bytes on the filesystem used by the object * which is slightly different * from the file's size (there can be hole in the file) */ static fattr_xdr_result encode_spaceused(XDR *xdr, struct xdr_attrs_args *args) { uint64_t sace = args->attrs->spaceused; if (!inline_xdr_u_int64_t(xdr, &sace)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_spaceused(XDR *xdr, struct xdr_attrs_args *args) { uint64_t sace = 0; if (!inline_xdr_u_int64_t(xdr, &sace)) return FATTR_XDR_FAILED; args->attrs->spaceused = sace; return TRUE; } /* * FATTR4_SYSTEM */ /* This is not a windows system File-System with respect to the regarding API */ static fattr_xdr_result encode_system(XDR *xdr, struct xdr_attrs_args *args) { uint32_t system = FALSE; if (!inline_xdr_bool(xdr, &system)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_system(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * Time conversions */ static inline fattr_xdr_result encode_time(XDR *xdr, struct timespec *ts) { uint64_t seconds = ts->tv_sec; uint32_t nseconds = ts->tv_nsec; if (!inline_xdr_u_int64_t(xdr, &seconds)) return FATTR_XDR_FAILED; if (!inline_xdr_u_int32_t(xdr, &nseconds)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static inline fattr_xdr_result decode_time(XDR *xdr, struct xdr_attrs_args *args, struct timespec *ts) { uint64_t seconds = 0; uint32_t nseconds = 0; if (!inline_xdr_u_int64_t(xdr, &seconds)) return FATTR_XDR_FAILED; if (!inline_xdr_u_int32_t(xdr, &nseconds)) return FATTR_XDR_FAILED; ts->tv_sec = (uint32_t) seconds; /* !!! is this correct?? */ ts->tv_nsec = nseconds; if (nseconds >= 1000000000) { /* overflow */ args->nfs_status = NFS4ERR_INVAL; return FATTR_XDR_FAILED; } return FATTR_XDR_SUCCESS; } static inline fattr_xdr_result encode_timeset_server(XDR *xdr) { uint32_t how = SET_TO_SERVER_TIME4; return inline_xdr_u_int32_t(xdr, &how); } static inline fattr_xdr_result encode_timeset(XDR *xdr, struct timespec *ts) { uint32_t how = SET_TO_CLIENT_TIME4; if (!inline_xdr_u_int32_t(xdr, &how)) return FATTR_XDR_FAILED; return encode_time(xdr, ts); } static inline fattr_xdr_result decode_timeset(XDR *xdr, struct xdr_attrs_args *args, struct timespec *ts) { uint32_t how = 0; if (!inline_xdr_u_int32_t(xdr, &how)) return FATTR_XDR_FAILED; if (how == SET_TO_SERVER_TIME4) return FATTR_XDR_SUCCESS_EXP; else return decode_time(xdr, args, ts); } /* * FATTR4_TIME_ACCESS */ static fattr_xdr_result encode_accesstime(XDR *xdr, struct xdr_attrs_args *args) { return encode_time(xdr, &args->attrs->atime); } static fattr_xdr_result decode_accesstime(XDR *xdr, struct xdr_attrs_args *args) { return decode_time(xdr, args, &args->attrs->atime); } /* * FATTR4_TIME_ACCESS_SET */ static fattr_xdr_result encode_accesstimeset(XDR *xdr, struct xdr_attrs_args *args) { if (FSAL_TEST_MASK(args->attrs->valid_mask, ATTR_ATIME_SERVER)) return encode_timeset_server(xdr); else return encode_timeset(xdr, &args->attrs->atime); } static fattr_xdr_result decode_accesstimeset(XDR *xdr, struct xdr_attrs_args *args) { return decode_timeset(xdr, args, &args->attrs->atime); } /* * FATTR4_TIME_BACKUP */ /* No time backup, return unix's beginning of time */ static fattr_xdr_result encode_backuptime(XDR *xdr, struct xdr_attrs_args *args) { struct timespec ts; ts.tv_sec = 0LL; ts.tv_nsec = 0; return encode_time(xdr, &ts); } static fattr_xdr_result decode_backuptime(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_TIME_CREATE */ /* No time create, return unix's beginning of time */ static fattr_xdr_result encode_createtime(XDR *xdr, struct xdr_attrs_args *args) { struct timespec ts; ts.tv_sec = 0LL; ts.tv_nsec = 0; return encode_time(xdr, &ts); } static fattr_xdr_result decode_createtime(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_SUCCESS; } /* * FATTR4_TIME_DELTA */ /* According to RFC3530, this is "the smallest usefull server time granularity". * I set this to 1s. note: dynamicfsinfo has this value. use it??? */ static fattr_xdr_result encode_deltatime(XDR *xdr, struct xdr_attrs_args *args) { struct timespec ts; ts.tv_sec = 1LL; ts.tv_nsec = 0; return encode_time(xdr, &ts); } static fattr_xdr_result decode_deltatime(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_TIME_METADATA: */ static fattr_xdr_result encode_metatime(XDR *xdr, struct xdr_attrs_args *args) { return encode_time(xdr, &args->attrs->ctime); } static fattr_xdr_result decode_metatime(XDR *xdr, struct xdr_attrs_args *args) { return decode_time(xdr, args, &args->attrs->ctime); } /* * FATTR4_TIME_MODIFY */ static fattr_xdr_result encode_modifytime(XDR *xdr, struct xdr_attrs_args *args) { return encode_time(xdr, &args->attrs->mtime); } static fattr_xdr_result decode_modifytime(XDR *xdr, struct xdr_attrs_args *args) { return decode_time(xdr, args, &args->attrs->mtime); } /* * FATTR4_TIME_MODIFY_SET */ static fattr_xdr_result encode_modifytimeset(XDR *xdr, struct xdr_attrs_args *args) { if (FSAL_TEST_MASK(args->attrs->valid_mask, ATTR_MTIME_SERVER)) return encode_timeset_server(xdr); else return encode_timeset(xdr, &args->attrs->mtime); } static fattr_xdr_result decode_modifytimeset(XDR *xdr, struct xdr_attrs_args *args) { return decode_timeset(xdr, args, &args->attrs->mtime); } /* * FATTR4_MOUNTED_ON_FILEID */ static fattr_xdr_result encode_mounted_on_fileid(XDR *xdr, struct xdr_attrs_args *args) { if (!inline_xdr_u_int64_t(xdr, &args->mounted_on_fileid)) return FATTR_XDR_FAILED; return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_mounted_on_fileid(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_DIR_NOTIF_DELAY */ static fattr_xdr_result encode_dir_notif_delay(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } static fattr_xdr_result decode_dir_notif_delay(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_DIRENT_NOTIF_DELAY */ static fattr_xdr_result encode_dirent_notif_delay(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } static fattr_xdr_result decode_dirent_notif_delay(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_DACL */ static fattr_xdr_result encode_dacl(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } static fattr_xdr_result decode_dacl(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_SACL */ static fattr_xdr_result encode_sacl(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } static fattr_xdr_result decode_sacl(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_CHANGE_POLICY */ static fattr_xdr_result encode_change_policy(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } static fattr_xdr_result decode_change_policy(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_FS_STATUS */ static fattr_xdr_result encode_fs_status(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } static fattr_xdr_result decode_fs_status(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_FS_LAYOUT_TYPES: */ static fattr_xdr_result encode_fs_layout_types(XDR *xdr, struct xdr_attrs_args *args) { struct fsal_export *export; layouttype4 layout_type; int32_t typecount = 0; int32_t index = 0; /* layouts */ const layouttype4 *layouttypes = NULL; if (args->data == NULL) return FATTR_XDR_NOOP; export = op_ctx->fsal_export; export->exp_ops.fs_layouttypes(export, &typecount, &layouttypes); if (!inline_xdr_u_int32_t(xdr, (uint32_t *) &typecount)) return FATTR_XDR_FAILED; for (index = 0; index < typecount; index++) { layout_type = layouttypes[index]; if (!inline_xdr_u_int32_t(xdr, &layout_type)) return FATTR_XDR_FAILED; } return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_fs_layout_types(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_LAYOUT_HINT */ static fattr_xdr_result encode_layout_hint(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } static fattr_xdr_result decode_layout_hint(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_LAYOUT_TYPES */ static fattr_xdr_result encode_layout_types(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } static fattr_xdr_result decode_layout_types(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_LAYOUT_BLKSIZE */ static fattr_xdr_result encode_layout_blocksize(XDR *xdr, struct xdr_attrs_args *args) { if (args->data == NULL) { return FATTR_XDR_NOOP; } else { struct fsal_export *export = op_ctx->fsal_export; uint32_t blocksize = export->exp_ops.fs_layout_blocksize(export); if (!inline_xdr_u_int32_t(xdr, &blocksize)) return FATTR_XDR_FAILED; } return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_layout_blocksize(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_LAYOUT_ALIGNMENT */ static fattr_xdr_result encode_layout_alignment(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } static fattr_xdr_result decode_layout_alignment(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_FS_LOCATIONS_INFO */ static fattr_xdr_result encode_fs_locations_info(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } static fattr_xdr_result decode_fs_locations_info(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_MDSTHRESHOLD */ static fattr_xdr_result encode_mdsthreshold(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } static fattr_xdr_result decode_mdsthreshold(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_RETENTION_GET */ static fattr_xdr_result encode_retention_get(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } static fattr_xdr_result decode_retention_get(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_RETENTION_SET */ static fattr_xdr_result encode_retention_set(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } static fattr_xdr_result decode_retention_set(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_RETENTEVT_GET */ static fattr_xdr_result encode_retentevt_get(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } static fattr_xdr_result decode_retentevt_get(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_RETENTEVT_SET */ static fattr_xdr_result encode_retentevt_set(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } static fattr_xdr_result decode_retentevt_set(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_RETENTION_HOLD */ static fattr_xdr_result encode_retention_hold(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } static fattr_xdr_result decode_retention_hold(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_MODE_SET_MASKED */ static fattr_xdr_result encode_mode_set_masked(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } static fattr_xdr_result decode_mode_set_masked(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_SUPPATTR_EXCLCREAT */ static fattr_xdr_result encode_support_exclusive_create(XDR *xdr, struct xdr_attrs_args *args) { struct bitmap4 bits; int attr, offset; bool res; memset(&bits, 0, sizeof(bits)); for (attr = FATTR4_SUPPORTED_ATTRS; attr <= FATTR4_XATTR_SUPPORT; attr++) { if (fattr4tab[attr].supported) { res = set_attribute_in_bitmap(&bits, attr); assert(res); } } res = clear_attribute_in_bitmap(&bits, FATTR4_TIME_ACCESS_SET); assert(res); res = clear_attribute_in_bitmap(&bits, FATTR4_TIME_MODIFY_SET); assert(res); if (!inline_xdr_u_int32_t(xdr, &bits.bitmap4_len)) return FATTR_XDR_FAILED; for (offset = 0; offset < bits.bitmap4_len; offset++) { if (!inline_xdr_u_int32_t(xdr, &bits.map[offset])) return FATTR_XDR_FAILED; } return FATTR_XDR_SUCCESS; } static fattr_xdr_result decode_support_exclusive_create(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_FS_CHARSET_CAP */ static fattr_xdr_result encode_fs_charset_cap(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } static fattr_xdr_result decode_fs_charset_cap(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* * FATTR4_XATTR_SUPPORT */ static fattr_xdr_result encode_xattr_support(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } static fattr_xdr_result decode_xattr_support(XDR *xdr, struct xdr_attrs_args *args) { return FATTR_XDR_NOOP; } /* NFS V4.0+ attributes * This array reflects the tables on page 39-46 of RFC3530 * indexed by attribute number */ const struct fattr4_dent fattr4tab[FATTR4_XATTR_SUPPORT + 1] = { [FATTR4_SUPPORTED_ATTRS] = { .name = "FATTR4_SUPPORTED_ATTRS", .supported = 1, .size_fattr4 = sizeof(fattr4_supported_attrs), .attrmask = 0, .encode = encode_supported_attrs, .decode = decode_supported_attrs, .access = FATTR4_ATTR_READ} , [FATTR4_TYPE] = { .name = "FATTR4_TYPE", .supported = 1, .size_fattr4 = sizeof(fattr4_type), .attrmask = ATTR_TYPE, .encode = encode_type, .decode = decode_type, .access = FATTR4_ATTR_READ} , [FATTR4_FH_EXPIRE_TYPE] = { .name = "FATTR4_FH_EXPIRE_TYPE", .supported = 1, .size_fattr4 = sizeof(fattr4_fh_expire_type), .attrmask = 0, .encode = encode_expiretype, .decode = decode_expiretype, .access = FATTR4_ATTR_READ} , [FATTR4_CHANGE] = { .name = "FATTR4_CHANGE", .supported = 1, .size_fattr4 = sizeof(fattr4_change), .attrmask = (ATTR_CHGTIME | ATTR_CHANGE), .encode = encode_change, .decode = decode_change, .access = FATTR4_ATTR_READ} , [FATTR4_SIZE] = { .name = "FATTR4_SIZE", .supported = 1, .size_fattr4 = sizeof(fattr4_size), .attrmask = ATTR_SIZE, .encode = encode_filesize, .decode = decode_filesize, .access = FATTR4_ATTR_READ_WRITE} , [FATTR4_LINK_SUPPORT] = { .name = "FATTR4_LINK_SUPPORT", .supported = 1, .size_fattr4 = sizeof(fattr4_link_support), .attrmask = 0, .encode = encode_linksupport, .decode = decode_linksupport, .access = FATTR4_ATTR_READ} , [FATTR4_SYMLINK_SUPPORT] = { .name = "FATTR4_SYMLINK_SUPPORT", .supported = 1, .size_fattr4 = sizeof(fattr4_symlink_support), .attrmask = 0, .encode = encode_symlinksupport, .decode = decode_symlinksupport, .access = FATTR4_ATTR_READ} , [FATTR4_NAMED_ATTR] = { .name = "FATTR4_NAMED_ATTR", .supported = 1, .size_fattr4 = sizeof(fattr4_named_attr), .attrmask = 0, .encode = encode_namedattrsupport, .decode = decode_namedattrsupport, .access = FATTR4_ATTR_READ} , [FATTR4_FSID] = { .name = "FATTR4_FSID", .supported = 1, .size_fattr4 = sizeof(fattr4_fsid), .encode = encode_fsid, .decode = decode_fsid, .attrmask = ATTR_FSID, .access = FATTR4_ATTR_READ} , [FATTR4_UNIQUE_HANDLES] = { .name = "FATTR4_UNIQUE_HANDLES", .supported = 1, .size_fattr4 = sizeof(fattr4_unique_handles), .attrmask = 0, .encode = encode_uniquehandles, .decode = decode_uniquehandles, .access = FATTR4_ATTR_READ} , [FATTR4_LEASE_TIME] = { .name = "FATTR4_LEASE_TIME", .supported = 1, .size_fattr4 = sizeof(fattr4_lease_time), .attrmask = 0, .encode = encode_leaselife, .decode = decode_leaselife, .access = FATTR4_ATTR_READ} , [FATTR4_RDATTR_ERROR] = { .name = "FATTR4_RDATTR_ERROR", .supported = 1, .size_fattr4 = sizeof(fattr4_rdattr_error), .attrmask = 0, .encode = encode_rdattr_error, .decode = decode_rdattr_error, .access = FATTR4_ATTR_READ} , [FATTR4_ACL] = { .name = "FATTR4_ACL", .supported = 1, .size_fattr4 = sizeof(fattr4_acl), .encode = encode_acl, .decode = decode_acl, .attrmask = ATTR_ACL, .access = FATTR4_ATTR_READ_WRITE} , [FATTR4_ACLSUPPORT] = { .name = "FATTR4_ACLSUPPORT", .supported = 1, .size_fattr4 = sizeof(fattr4_aclsupport), .attrmask = ATTR_ACL, .encode = encode_aclsupport, .decode = decode_aclsupport, .access = FATTR4_ATTR_READ} , [FATTR4_ARCHIVE] = { .name = "FATTR4_ARCHIVE", .supported = 0, .size_fattr4 = sizeof(fattr4_archive), .attrmask = 0, .encode = encode_archive, .decode = decode_archive, .access = FATTR4_ATTR_READ_WRITE} , [FATTR4_CANSETTIME] = { .name = "FATTR4_CANSETTIME", .supported = 1, .size_fattr4 = sizeof(fattr4_cansettime), .attrmask = 0, .encode = encode_cansettime, .decode = decode_cansettime, .access = FATTR4_ATTR_READ} , [FATTR4_CASE_INSENSITIVE] = { .name = "FATTR4_CASE_INSENSITIVE", .supported = 1, .size_fattr4 = sizeof(fattr4_case_insensitive), .attrmask = 0, .encode = encode_case_insensitive, .decode = decode_case_insensitive, .access = FATTR4_ATTR_READ} , [FATTR4_CASE_PRESERVING] = { .name = "FATTR4_CASE_PRESERVING", .supported = 1, .size_fattr4 = sizeof(fattr4_case_preserving), .attrmask = 0, .encode = encode_case_preserving, .decode = decode_case_preserving, .access = FATTR4_ATTR_READ} , [FATTR4_CHOWN_RESTRICTED] = { .name = "FATTR4_CHOWN_RESTRICTED", .supported = 1, .size_fattr4 = sizeof(fattr4_chown_restricted), .attrmask = 0, .encode = encode_chown_restricted, .decode = decode_chown_restricted, .access = FATTR4_ATTR_READ} , [FATTR4_FILEHANDLE] = { .name = "FATTR4_FILEHANDLE", .supported = 1, .size_fattr4 = sizeof(fattr4_filehandle), .attrmask = 0, .encode = encode_filehandle, .decode = decode_filehandle, .access = FATTR4_ATTR_READ} , [FATTR4_FILEID] = { .name = "FATTR4_FILEID", .supported = 1, .size_fattr4 = sizeof(fattr4_fileid), .encode = encode_fileid, .decode = decode_fileid, .attrmask = ATTR_FILEID, .access = FATTR4_ATTR_READ} , [FATTR4_FILES_AVAIL] = { .name = "FATTR4_FILES_AVAIL", .supported = 1, .size_fattr4 = sizeof(fattr4_files_avail), .attrmask = 0, .encode = encode_files_avail, .decode = decode_files_avail, .access = FATTR4_ATTR_READ} , [FATTR4_FILES_FREE] = { .name = "FATTR4_FILES_FREE", .supported = 1, .size_fattr4 = sizeof(fattr4_files_free), .attrmask = 0, .encode = encode_files_free, .decode = decode_files_free, .access = FATTR4_ATTR_READ} , [FATTR4_FILES_TOTAL] = { .name = "FATTR4_FILES_TOTAL", .supported = 1, .size_fattr4 = sizeof(fattr4_files_total), .attrmask = 0, .encode = encode_files_total, .decode = decode_files_total, .access = FATTR4_ATTR_READ} , [FATTR4_FS_LOCATIONS] = { .name = "FATTR4_FS_LOCATIONS", .supported = 1, .size_fattr4 = sizeof(fattr4_fs_locations), .attrmask = ATTR4_FS_LOCATIONS, .encode = encode_fs_locations, .decode = decode_fs_locations, .access = FATTR4_ATTR_READ} , [FATTR4_HIDDEN] = { .name = "FATTR4_HIDDEN", .supported = 0, .size_fattr4 = sizeof(fattr4_hidden), .attrmask = 0, .encode = encode_hidden, .decode = decode_hidden, .access = FATTR4_ATTR_READ_WRITE} , [FATTR4_HOMOGENEOUS] = { .name = "FATTR4_HOMOGENEOUS", .supported = 1, .size_fattr4 = sizeof(fattr4_homogeneous), .attrmask = 0, .encode = encode_homogeneous, .decode = decode_homogeneous, .access = FATTR4_ATTR_READ} , [FATTR4_MAXFILESIZE] = { .name = "FATTR4_MAXFILESIZE", .supported = 1, .size_fattr4 = sizeof(fattr4_maxfilesize), .attrmask = 0, .encode = encode_maxfilesize, .decode = decode_maxfilesize, .access = FATTR4_ATTR_READ} , [FATTR4_MAXLINK] = { .name = "FATTR4_MAXLINK", .supported = 1, .size_fattr4 = sizeof(fattr4_maxlink), .attrmask = 0, .encode = encode_maxlink, .decode = decode_maxlink, .access = FATTR4_ATTR_READ} , [FATTR4_MAXNAME] = { .name = "FATTR4_MAXNAME", .supported = 1, .size_fattr4 = sizeof(fattr4_maxname), .attrmask = 0, .encode = encode_maxname, .decode = decode_maxname, .access = FATTR4_ATTR_READ} , [FATTR4_MAXREAD] = { .name = "FATTR4_MAXREAD", .supported = 1, .size_fattr4 = sizeof(fattr4_maxread), .attrmask = 0, .encode = encode_maxread, .decode = decode_maxread, .access = FATTR4_ATTR_READ} , [FATTR4_MAXWRITE] = { .name = "FATTR4_MAXWRITE", .supported = 1, .size_fattr4 = sizeof(fattr4_maxwrite), .attrmask = 0, .encode = encode_maxwrite, .decode = decode_maxwrite, .access = FATTR4_ATTR_READ} , [FATTR4_MIMETYPE] = { .name = "FATTR4_MIMETYPE", .supported = 0, .size_fattr4 = sizeof(fattr4_mimetype), .attrmask = 0, .encode = encode_mimetype, .decode = decode_mimetype, .access = FATTR4_ATTR_READ_WRITE} , [FATTR4_MODE] = { .name = "FATTR4_MODE", .supported = 1, .size_fattr4 = sizeof(fattr4_mode), .encode = encode_mode, .decode = decode_mode, .attrmask = ATTR_MODE, .access = FATTR4_ATTR_READ_WRITE} , [FATTR4_NO_TRUNC] = { .name = "FATTR4_NO_TRUNC", .supported = 1, .size_fattr4 = sizeof(fattr4_no_trunc), .attrmask = 0, .encode = encode_no_trunc, .decode = decode_no_trunc, .access = FATTR4_ATTR_READ} , [FATTR4_NUMLINKS] = { .name = "FATTR4_NUMLINKS", .supported = 1, .size_fattr4 = sizeof(fattr4_numlinks), .encode = encode_numlinks, .decode = decode_numlinks, .attrmask = ATTR_NUMLINKS, .access = FATTR4_ATTR_READ} , [FATTR4_OWNER] = { .name = "FATTR4_OWNER", .supported = 1, .size_fattr4 = sizeof(fattr4_owner), .encode = encode_owner, .decode = decode_owner, .attrmask = ATTR_OWNER, .access = FATTR4_ATTR_READ_WRITE} , [FATTR4_OWNER_GROUP] = { .name = "FATTR4_OWNER_GROUP", .supported = 1, .size_fattr4 = sizeof(fattr4_owner_group), .encode = encode_group, .decode = decode_group, .attrmask = ATTR_GROUP, .access = FATTR4_ATTR_READ_WRITE} , [FATTR4_QUOTA_AVAIL_HARD] = { .name = "FATTR4_QUOTA_AVAIL_HARD", .supported = 0, .size_fattr4 = sizeof(fattr4_quota_avail_hard), .attrmask = 0, .encode = encode_quota_avail_hard, .decode = decode_quota_avail_hard, .access = FATTR4_ATTR_READ} , [FATTR4_QUOTA_AVAIL_SOFT] = { .name = "FATTR4_QUOTA_AVAIL_SOFT", .supported = 0, .size_fattr4 = sizeof(fattr4_quota_avail_soft), .attrmask = 0, .encode = encode_quota_avail_soft, .decode = decode_quota_avail_soft, .access = FATTR4_ATTR_READ} , [FATTR4_QUOTA_USED] = { .name = "FATTR4_QUOTA_USED", .supported = 0, .size_fattr4 = sizeof(fattr4_quota_used), .attrmask = 0, .encode = encode_quota_used, .decode = decode_quota_used, .access = FATTR4_ATTR_READ} , [FATTR4_RAWDEV] = { .name = "FATTR4_RAWDEV", .supported = 1, /** @todo use FSAL attrs instead ??? */ .size_fattr4 = sizeof(fattr4_rawdev), .encode = encode_rawdev, .decode = decode_rawdev, .attrmask = ATTR_RAWDEV, .access = FATTR4_ATTR_READ} , [FATTR4_SPACE_AVAIL] = { .name = "FATTR4_SPACE_AVAIL", .supported = 1, .size_fattr4 = sizeof(fattr4_space_avail), .attrmask = 0, .encode = encode_sace_avail, .decode = decode_sace_avail, .access = FATTR4_ATTR_READ} , [FATTR4_SPACE_FREE] = { .name = "FATTR4_SPACE_FREE", .supported = 1, .size_fattr4 = sizeof(fattr4_space_used), .attrmask = 0, .encode = encode_sace_free, .decode = decode_sace_free, .access = FATTR4_ATTR_READ} , [FATTR4_SPACE_TOTAL] = { .name = "FATTR4_SPACE_TOTAL", .supported = 1, .size_fattr4 = sizeof(fattr4_space_total), .attrmask = 0, .encode = encode_sace_total, .decode = decode_sace_total, .access = FATTR4_ATTR_READ} , [FATTR4_SPACE_USED] = { .name = "FATTR4_SPACE_USED", .supported = 1, .size_fattr4 = sizeof(fattr4_space_used), .encode = encode_spaceused, .decode = decode_spaceused, .attrmask = ATTR_SPACEUSED, .access = FATTR4_ATTR_READ} , [FATTR4_SYSTEM] = { .name = "FATTR4_SYSTEM", .supported = 0, .size_fattr4 = sizeof(fattr4_system), .attrmask = 0, .encode = encode_system, .decode = decode_system, .access = FATTR4_ATTR_READ_WRITE} , [FATTR4_TIME_ACCESS] = { .name = "FATTR4_TIME_ACCESS", .supported = 1, /* ( fattr4_time_access ) not aligned on 32 bits */ .size_fattr4 = 12, .encode = encode_accesstime, .decode = decode_accesstime, .attrmask = ATTR_ATIME, .access = FATTR4_ATTR_READ} , [FATTR4_TIME_ACCESS_SET] = { .name = "FATTR4_TIME_ACCESS_SET", .supported = 1, .size_fattr4 = sizeof(fattr4_time_access_set), .encode = encode_accesstimeset, .decode = decode_accesstimeset, .attrmask = ATTR_ATIME, .exp_attrmask = ATTR_ATIME_SERVER, .access = FATTR4_ATTR_WRITE} , [FATTR4_TIME_BACKUP] = { .name = "FATTR4_TIME_BACKUP", .supported = 0, /*( fattr4_time_backup ) not aligned on 32 bits */ .size_fattr4 = 12, .attrmask = 0, .encode = encode_backuptime, .decode = decode_backuptime, .access = FATTR4_ATTR_READ_WRITE} , [FATTR4_TIME_CREATE] = { .name = "FATTR4_TIME_CREATE", .supported = 0, /* ( fattr4_time_create ) not aligned on 32 bits */ .size_fattr4 = 12, .attrmask = 0, .encode = encode_createtime, .decode = decode_createtime, .access = FATTR4_ATTR_READ_WRITE} , [FATTR4_TIME_DELTA] = { .name = "FATTR4_TIME_DELTA", .supported = 1, /* ( fattr4_time_delta ) not aligned on 32 bits */ .size_fattr4 = 12, .attrmask = 0, .encode = encode_deltatime, .decode = decode_deltatime, .access = FATTR4_ATTR_READ} , [FATTR4_TIME_METADATA] = { .name = "FATTR4_TIME_METADATA", .supported = 1, /* ( fattr4_time_metadata ) not aligned on 32 bits */ .size_fattr4 = 12, .encode = encode_metatime, .decode = decode_metatime, .attrmask = ATTR_CTIME, .access = FATTR4_ATTR_READ} , [FATTR4_TIME_MODIFY] = { .name = "FATTR4_TIME_MODIFY", .supported = 1, /* ( fattr4_time_modify ) not aligned on 32 bits */ .size_fattr4 = 12, .encode = encode_modifytime, .decode = decode_modifytime, .attrmask = ATTR_MTIME, .access = FATTR4_ATTR_READ} , [FATTR4_TIME_MODIFY_SET] = { .name = "FATTR4_TIME_MODIFY_SET", .supported = 1, .size_fattr4 = sizeof(fattr4_time_modify_set), .encode = encode_modifytimeset, .decode = decode_modifytimeset, .attrmask = ATTR_MTIME, .exp_attrmask = ATTR_MTIME_SERVER, .access = FATTR4_ATTR_WRITE} , [FATTR4_MOUNTED_ON_FILEID] = { .name = "FATTR4_MOUNTED_ON_FILEID", .supported = 1, .size_fattr4 = sizeof(fattr4_mounted_on_fileid), .attrmask = 0, .encode = encode_mounted_on_fileid, .decode = decode_mounted_on_fileid, .access = FATTR4_ATTR_READ} , [FATTR4_DIR_NOTIF_DELAY] = { .name = "FATTR4_DIR_NOTIF_DELAY", .supported = 0, .size_fattr4 = sizeof(fattr4_dir_notif_delay), .attrmask = 0, .encode = encode_dir_notif_delay, .decode = decode_dir_notif_delay, .access = FATTR4_ATTR_READ} , [FATTR4_DIRENT_NOTIF_DELAY] = { .name = "FATTR4_DIRENT_NOTIF_DELAY", .supported = 0, .size_fattr4 = sizeof(fattr4_dirent_notif_delay), .attrmask = 0, .encode = encode_dirent_notif_delay, .decode = decode_dirent_notif_delay, .access = FATTR4_ATTR_READ} , [FATTR4_DACL] = { .name = "FATTR4_DACL", .supported = 0, .size_fattr4 = sizeof(fattr4_dacl), .attrmask = 0, .encode = encode_dacl, .decode = decode_dacl, .access = FATTR4_ATTR_READ_WRITE} , [FATTR4_SACL] = { .name = "FATTR4_SACL", .supported = 0, .size_fattr4 = sizeof(fattr4_sacl), .encode = encode_sacl, .decode = decode_sacl, .access = FATTR4_ATTR_READ_WRITE} , [FATTR4_CHANGE_POLICY] = { .name = "FATTR4_CHANGE_POLICY", .supported = 0, .size_fattr4 = sizeof(fattr4_change_policy), .attrmask = 0, .encode = encode_change_policy, .decode = decode_change_policy, .access = FATTR4_ATTR_READ} , [FATTR4_FS_STATUS] = { .name = "FATTR4_FS_STATUS", .supported = 0, .size_fattr4 = sizeof(fattr4_fs_status), .attrmask = 0, .encode = encode_fs_status, .decode = decode_fs_status, .access = FATTR4_ATTR_READ} , [FATTR4_FS_LAYOUT_TYPES] = { .name = "FATTR4_FS_LAYOUT_TYPES", .supported = 1, .size_fattr4 = sizeof(fattr4_fs_layout_types), .attrmask = 0, .encode = encode_fs_layout_types, .decode = decode_fs_layout_types, .access = FATTR4_ATTR_READ} , [FATTR4_LAYOUT_HINT] = { .name = "FATTR4_LAYOUT_HINT", .supported = 0, .size_fattr4 = sizeof(fattr4_layout_hint), .attrmask = 0, .encode = encode_layout_hint, .decode = decode_layout_hint, .access = FATTR4_ATTR_WRITE} , [FATTR4_LAYOUT_TYPES] = { .name = "FATTR4_LAYOUT_TYPES", .supported = 0, .size_fattr4 = sizeof(fattr4_layout_types), .attrmask = 0, .encode = encode_layout_types, .decode = decode_layout_types, .access = FATTR4_ATTR_READ} , [FATTR4_LAYOUT_BLKSIZE] = { .name = "FATTR4_LAYOUT_BLKSIZE", .supported = 1, .size_fattr4 = sizeof(fattr4_layout_blksize), .attrmask = 0, .encode = encode_layout_blocksize, .decode = decode_layout_blocksize, .access = FATTR4_ATTR_READ} , [FATTR4_LAYOUT_ALIGNMENT] = { .name = "FATTR4_LAYOUT_ALIGNMENT", .supported = 0, .size_fattr4 = sizeof(fattr4_layout_alignment), .attrmask = 0, .encode = encode_layout_alignment, .decode = decode_layout_alignment, .access = FATTR4_ATTR_READ} , [FATTR4_FS_LOCATIONS_INFO] = { .name = "FATTR4_FS_LOCATIONS_INFO", .supported = 0, .size_fattr4 = sizeof(fattr4_fs_locations_info), .attrmask = 0, .encode = encode_fs_locations_info, .decode = decode_fs_locations_info, .access = FATTR4_ATTR_READ} , [FATTR4_MDSTHRESHOLD] = { .name = "FATTR4_MDSTHRESHOLD", .supported = 0, .size_fattr4 = sizeof(fattr4_mdsthreshold), .attrmask = 0, .encode = encode_mdsthreshold, .decode = decode_mdsthreshold, .access = FATTR4_ATTR_READ} , [FATTR4_RETENTION_GET] = { .name = "FATTR4_RETENTION_GET", .supported = 0, .size_fattr4 = sizeof(fattr4_retention_get), .attrmask = 0, .encode = encode_retention_get, .decode = decode_retention_get, .access = FATTR4_ATTR_READ} , [FATTR4_RETENTION_SET] = { .name = "FATTR4_RETENTION_SET", .supported = 0, .size_fattr4 = sizeof(fattr4_retention_set), .attrmask = 0, .encode = encode_retention_set, .decode = decode_retention_set, .access = FATTR4_ATTR_WRITE} , [FATTR4_RETENTEVT_GET] = { .name = "FATTR4_RETENTEVT_GET", .supported = 0, .size_fattr4 = sizeof(fattr4_retentevt_get), .attrmask = 0, .encode = encode_retentevt_get, .decode = decode_retentevt_get, .access = FATTR4_ATTR_READ} , [FATTR4_RETENTEVT_SET] = { .name = "FATTR4_RETENTEVT_SET", .supported = 0, .size_fattr4 = sizeof(fattr4_retentevt_set), .attrmask = 0, .encode = encode_retentevt_set, .decode = decode_retentevt_set, .access = FATTR4_ATTR_WRITE} , [FATTR4_RETENTION_HOLD] = { .name = "FATTR4_RETENTION_HOLD", .supported = 0, .size_fattr4 = sizeof(fattr4_retention_hold), .attrmask = 0, .encode = encode_retention_hold, .decode = decode_retention_hold, .access = FATTR4_ATTR_READ_WRITE} , [FATTR4_MODE_SET_MASKED] = { .name = "FATTR4_MODE_SET_MASKED", .supported = 0, .size_fattr4 = sizeof(fattr4_mode_set_masked), .attrmask = 0, .encode = encode_mode_set_masked, .decode = decode_mode_set_masked, .access = FATTR4_ATTR_WRITE} , [FATTR4_SUPPATTR_EXCLCREAT] = { .name = "FATTR4_SUPPATTR_EXCLCREAT", .supported = 1, .size_fattr4 = sizeof(fattr4_suppattr_exclcreat), .attrmask = 0, .encode = encode_support_exclusive_create, .decode = decode_support_exclusive_create, .access = FATTR4_ATTR_READ} , [FATTR4_FS_CHARSET_CAP] = { .name = "FATTR4_FS_CHARSET_CAP", .supported = 0, .size_fattr4 = sizeof(fattr4_fs_charset_cap), .attrmask = 0, .encode = encode_fs_charset_cap, .decode = decode_fs_charset_cap, .access = FATTR4_ATTR_READ} , [FATTR4_XATTR_SUPPORT] = { .name = "FATTR4_XATTR_SUPPORT", .supported = 1, .size_fattr4 = sizeof(fattr4_fs_charset_cap), .attrmask = ATTR4_XATTR, .encode = encode_xattr_support, .decode = decode_xattr_support, .access = FATTR4_ATTR_READ} }; /* goes in a more global header? */ /** path_filter * scan the path we are given for bad filenames. * * scan control: * UTF8_SCAN_NOSLASH - detect and reject '/' in names * UTF8_NODOT - detect and reject "." and ".." as the name * UTF8_SCAN_CKUTF8 - detect invalid utf8 sequences * * NULL termination is required. It also speeds up the scan * UTF-8 scanner courtesy Markus Kuhn * GPL licensed per licensing referenced in source. * */ static nfsstat4 path_filter(const char *name, utf8_scantype_t scan) { const unsigned char *np = (const unsigned char *)name; nfsstat4 status = NFS4_OK; unsigned int c, first; first = 1; c = *np++; while (c) { if (likely(c < 0x80)) { /* ascii */ if (unlikely(c == '/' && (scan & UTF8_SCAN_NOSLASH))) { status = NFS4ERR_BADCHAR; goto error; } if (unlikely (first && c == '.' && (scan & UTF8_SCAN_NODOT))) { if (np[0] == '\0' || (np[0] == '.' && np[1] == '\0')) { status = NFS4ERR_BADNAME; goto error; } } } else if (likely(scan & UTF8_SCAN_CKUTF8)) { /* UTF-8 range */ if ((c & 0xe0) == 0xc0) { /* 2 octet UTF-8 */ if ((*np & 0xc0) != 0x80 || (c & 0xfe) == 0xc0) { /* overlong */ goto badutf8; } else { np++; } } else if ((c & 0xf0) == 0xe0) { /* 3 octet UTF-8 */ if (/* overlong */ (*np & 0xc0) != 0x80 || (np[1] & 0xc0) != 0x80 || (c == 0xe0 && (*np & 0xe0) == 0x80) || /* surrogate */ (c == 0xed && (*np & 0xe0) == 0xa0) || (c == 0xef && *np == 0xbf && (np[1] & 0xfe) == 0xbe)) { /* U+fffe - u+ffff */ goto badutf8; } else { np += 2; } } else if ((c & 0xf8) == 0xf0) { /* 4 octet UTF-8 */ if (/* overlong */ (*np & 0xc0) != 0x80 || (np[1] & 0xc0) != 0x80 || (np[2] & 0xc0) != 0x80 || (c == 0xf0 && (*np & 0xf0) == 0x80) || (c == 0xf4 && *np > 0x8f) || c > 0xf4) { /* > u+10ffff */ goto badutf8; } else { np += 3; } } else { goto badutf8; } } c = *np++; first = 0; } return NFS4_OK; badutf8: status = NFS4ERR_INVAL; error: return status; } void nfs4_Fattr_Free(fattr4 *fattr) { if (fattr->attr_vals.attrlist4_val != NULL) { gsh_free(fattr->attr_vals.attrlist4_val); fattr->attr_vals.attrlist4_val = NULL; } } /** * @brief Fill NFSv4 Fattr from a file * * This function fills an NFSv4 Fattr from a file represented by * data->currentFH and data->current-obj. * * Memory for bitmap_val and attr_val is dynamically allocated, the caller is * responsible for freeing it. * * @param[in] data NFSv4 compoud request's data * @param[in] request_mask The original request attribute mask * @param[in/out] attr attrlist to fill in and mask to request * @param[out] Fattr NFSv4 Fattr buffer * @param[in] Bitmap Bitmap of attributes being requested * * @retval NFSv4 status */ nfsstat4 file_To_Fattr(compound_data_t *data, attrmask_t request_mask, struct attrlist *attr, fattr4 *Fattr, struct bitmap4 *Bitmap) { fsal_status_t status; struct xdr_attrs_args args = { .attrs = attr, .data = data, .hdl4 = &data->currentFH, }; /* Permission check only if ACL is asked for. * NOTE: We intentionally do NOT check ACE4_READ_ATTR. */ if (attribute_is_set(Bitmap, FATTR4_ACL)) { fsal_status_t status; LogDebug(COMPONENT_NFS_V4_ACL, "Permission check for ACL for obj %p", data->current_obj); status = fsal_access(data->current_obj, FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_READ_ACL)); if (FSAL_IS_ERROR(status)) { LogDebug(COMPONENT_NFS_V4_ACL, "Permission check for ACL for obj %p failed with %s", data->current_obj, msg_fsal_err(status.major)); return nfs4_Errno_status(status); } } else { #ifdef ENABLE_RFC_ACL LogDebug(COMPONENT_NFS_V4_ACL, "Permission check for ATTR for obj %p", data->current_obj); status = fsal_access(data->current_obj, FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_READ_ATTR)); if (FSAL_IS_ERROR(status)) { LogDebug(COMPONENT_NFS_V4_ACL, "Permission check for ATTR for obj %p failed with %s", data->current_obj, fsal_err_txt(status)); return nfs4_Errno_status(status); } #else /* ENABLE_RFC_ACL */ LogDebug(COMPONENT_NFS_V4_ACL, "No permission check for ACL for obj %p", data->current_obj); #endif /* ENABLE_RFC_ACL */ } if (attribute_is_set(Bitmap, FATTR4_MOUNTED_ON_FILEID)) { PTHREAD_RWLOCK_rdlock(&op_ctx->ctx_export->lock); if (data->current_obj == op_ctx->ctx_export->exp_root_obj) { /* This is the root of the current export, find our * mounted_on_fileid and use that. */ args.mounted_on_fileid = op_ctx->ctx_export->exp_mounted_on_file_id; } else { /* This is not the root of the current export, just * use fileid. */ args.mounted_on_fileid = data->current_obj->fileid; } PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock); } /* Fill in fileid and fsid into args */ args.fileid = data->current_obj->fileid; args.fsid = data->current_obj->fsid; status = data->current_obj->obj_ops.getattrs(data->current_obj, attr); if (FSAL_IS_ERROR(status)) return nfs4_Errno_status(status); /* Restore originally requested mask */ attr->request_mask = request_mask; if (nfs4_FSALattr_To_Fattr(&args, Bitmap, Fattr) != 0) { /* Done with the attrs, caller won't release, but we did * fetch them. */ fsal_release_attrs(attr); return NFS4ERR_IO; } return NFS4_OK; } int nfs4_Fattr_Fill_Error(fattr4 *Fattr, nfsstat4 rdattr_error) { u_int LastOffset; XDR attr_body; struct xdr_attrs_args args; fattr_xdr_result xdr_res; /* basic init */ memset(&Fattr->attrmask, 0, sizeof(Fattr->attrmask)); Fattr->attr_vals.attrlist4_val = gsh_malloc(fattr4tab[FATTR4_RDATTR_ERROR].size_fattr4); LastOffset = 0; memset(&attr_body, 0, sizeof(attr_body)); xdrmem_create(&attr_body, Fattr->attr_vals.attrlist4_val, fattr4tab[FATTR4_RDATTR_ERROR].size_fattr4, XDR_ENCODE); memset(&args, 0, sizeof(args)); args.rdattr_error = rdattr_error; xdr_res = fattr4tab[FATTR4_RDATTR_ERROR].encode(&attr_body, &args); if (xdr_res == FATTR_XDR_SUCCESS) { bool res = set_attribute_in_bitmap(&Fattr->attrmask, FATTR4_RDATTR_ERROR); assert(res); LogFullDebug(COMPONENT_NFS_V4, "Encoded attribute %d, name = %s", FATTR4_RDATTR_ERROR, fattr4tab[FATTR4_RDATTR_ERROR].name); /* mark the attribute in the bitmap should be new bitmap btw */ LastOffset = xdr_getpos(&attr_body); /* dumb but for now */ xdr_destroy(&attr_body); if (LastOffset == 0) { /* no supported attrs so we can free */ assert(Fattr->attrmask.bitmap4_len == 0); gsh_free(Fattr->attr_vals.attrlist4_val); Fattr->attr_vals.attrlist4_val = NULL; } Fattr->attr_vals.attrlist4_len = LastOffset; return 0; } else { LogFullDebug(COMPONENT_NFS_V4, "Encode FAILED for attribute %d, name = %s", FATTR4_RDATTR_ERROR, fattr4tab[FATTR4_RDATTR_ERROR].name); /* signal fail so if(LastOffset > 0) works right */ gsh_free(Fattr->attr_vals.attrlist4_val); Fattr->attr_vals.attrlist4_val = NULL; return -1; } } /** * @brief Converts FSAL Attributes to NFSv4 Fattr buffer. * * Converts FSAL Attributes to NFSv4 Fattr buffer. * * @param[in] args XDR attribute arguments * @param[in] Bitmap Bitmap of attributes being requested * @param[out] Fattr NFSv4 Fattr buffer * Memory for bitmap_val and attr_val is * dynamically allocated, * caller is responsible for freeing it. * * @return -1 if failed, 0 if successful. * */ int nfs4_FSALattr_To_Fattr(struct xdr_attrs_args *args, struct bitmap4 *Bitmap, fattr4 *Fattr) { int attribute_to_set = 0; int max_attr_idx; u_int LastOffset; fsal_dynamicfsinfo_t dynamicinfo; XDR attr_body; fattr_xdr_result xdr_res; uint32_t attrvals_buflen; /* basic init */ memset(Fattr, 0, sizeof(*Fattr)); if (Bitmap->bitmap4_len == 0) return 0; /* they ask for nothing, they get nothing */ attrvals_buflen = NFS4_ATTRVALS_BUFFLEN; if (attribute_is_set(Bitmap, FATTR4_ACL) && args->attrs->acl) { /* Calculating an exact needed xdr buffer size is laborious * and time consuming, so making a rough estimate */ attrvals_buflen += (sizeof(fsal_ace_t) + NFS4_MAX_DOMAIN_LEN) * args->attrs->acl->naces; } /* Check if the calculated len is less than the max send buffer size */ if (attrvals_buflen > nfs_param.core_param.rpc.max_send_buffer_size) attrvals_buflen = nfs_param.core_param.rpc.max_send_buffer_size; Fattr->attr_vals.attrlist4_val = gsh_malloc(attrvals_buflen); max_attr_idx = nfs4_max_attr_index(args->data); LogFullDebug(COMPONENT_NFS_V4, "Maximum allowed attr index = %d", max_attr_idx); LastOffset = 0; memset(&attr_body, 0, sizeof(attr_body)); xdrmem_create(&attr_body, Fattr->attr_vals.attrlist4_val, attrvals_buflen, XDR_ENCODE); if (args->dynamicinfo == NULL) args->dynamicinfo = &dynamicinfo; for (attribute_to_set = next_attr_from_bitmap(Bitmap, -1); attribute_to_set != -1; attribute_to_set = next_attr_from_bitmap(Bitmap, attribute_to_set)) { if (attribute_to_set > max_attr_idx) break; /* skip out of bounds */ xdr_res = fattr4tab[attribute_to_set].encode(&attr_body, args); if (xdr_res == FATTR_XDR_SUCCESS) { bool res = set_attribute_in_bitmap(&Fattr->attrmask, attribute_to_set); assert(res); LogFullDebug(COMPONENT_NFS_V4, "Encoded attr %d, name = %s", attribute_to_set, fattr4tab[attribute_to_set].name); } else if (xdr_res == FATTR_XDR_NOOP) { LogFullDebug(COMPONENT_NFS_V4, "Attr not supported %d name=%s", attribute_to_set, fattr4tab[attribute_to_set].name); continue; } else { LogFullDebug(COMPONENT_NFS_V4, "Encode FAILED for attr %d, name = %s", attribute_to_set, fattr4tab[attribute_to_set].name); /* signal fail so if(LastOffset > 0) works right */ goto err; } /* mark the attribute in the bitmap should be new bitmap btw */ } LastOffset = xdr_getpos(&attr_body); /* dumb but for now */ xdr_destroy(&attr_body); if (LastOffset == 0) { /* no supported attrs so we can free */ assert(Fattr->attrmask.bitmap4_len == 0); gsh_free(Fattr->attr_vals.attrlist4_val); Fattr->attr_vals.attrlist4_val = NULL; } Fattr->attr_vals.attrlist4_len = LastOffset; return 0; err: gsh_free(Fattr->attr_vals.attrlist4_val); Fattr->attr_vals.attrlist4_val = NULL; return -1; } /** * * nfs3_Sattr_To_FSALattr: Converts NFSv3 Sattr to FSAL Attributes. * * Converts NFSv3 Sattr to FSAL Attributes. * * @param FSAL_attr [OUT] computed FSAL attributes. * @param sattr [IN] NFSv3 sattr to be set. * * @retval true on success. * @retval false on failure. * */ bool nfs3_Sattr_To_FSALattr(struct attrlist *FSAL_attr, sattr3 *sattr) { FSAL_attr->valid_mask = 0; if (sattr->mode.set_it) { LogFullDebug(COMPONENT_NFSPROTO, "mode = %o", sattr->mode.set_mode3_u.mode); FSAL_attr->mode = unix2fsal_mode(sattr->mode.set_mode3_u.mode); FSAL_attr->valid_mask |= ATTR_MODE; } if (sattr->uid.set_it) { LogFullDebug(COMPONENT_NFSPROTO, "uid = %d", sattr->uid.set_uid3_u.uid); FSAL_attr->owner = sattr->uid.set_uid3_u.uid; FSAL_attr->valid_mask |= ATTR_OWNER; } if (sattr->gid.set_it) { LogFullDebug(COMPONENT_NFSPROTO, "gid = %d", sattr->gid.set_gid3_u.gid); FSAL_attr->group = sattr->gid.set_gid3_u.gid; FSAL_attr->valid_mask |= ATTR_GROUP; } if (sattr->size.set_it) { LogFullDebug(COMPONENT_NFSPROTO, "size = %lld", sattr->size.set_size3_u.size); FSAL_attr->filesize = sattr->size.set_size3_u.size; FSAL_attr->valid_mask |= ATTR_SIZE; } if (sattr->atime.set_it != DONT_CHANGE) { LogFullDebug(COMPONENT_NFSPROTO, "set=%d atime = %d,%d", sattr->atime.set_it, sattr->atime.set_atime_u.atime.tv_sec, sattr->atime.set_atime_u.atime.tv_nsec); if (sattr->atime.set_it == SET_TO_CLIENT_TIME) { FSAL_attr->atime.tv_sec = sattr->atime.set_atime_u.atime.tv_sec; FSAL_attr->atime.tv_nsec = sattr->atime.set_atime_u.atime.tv_nsec; FSAL_attr->valid_mask |= ATTR_ATIME; } else if (sattr->atime.set_it == SET_TO_SERVER_TIME) { /* Use the server's current time */ LogFullDebug(COMPONENT_NFSPROTO, "SET_TO_SERVER_TIME atime"); FSAL_attr->valid_mask |= ATTR_ATIME_SERVER; } else { LogCrit(COMPONENT_NFSPROTO, "Unexpected value for sattr->atime.set_it = %d", sattr->atime.set_it); } } if (sattr->mtime.set_it != DONT_CHANGE) { LogFullDebug(COMPONENT_NFSPROTO, "set=%d mtime = %d", sattr->atime.set_it, sattr->mtime.set_mtime_u.mtime.tv_sec); if (sattr->mtime.set_it == SET_TO_CLIENT_TIME) { FSAL_attr->mtime.tv_sec = sattr->mtime.set_mtime_u.mtime.tv_sec; FSAL_attr->mtime.tv_nsec = sattr->mtime.set_mtime_u.mtime.tv_nsec; FSAL_attr->valid_mask |= ATTR_MTIME; } else if (sattr->mtime.set_it == SET_TO_SERVER_TIME) { /* Use the server's current time */ LogFullDebug(COMPONENT_NFSPROTO, "SET_TO_SERVER_TIME Mtime"); FSAL_attr->valid_mask |= ATTR_MTIME_SERVER; } else { LogCrit(COMPONENT_NFSPROTO, "Unexpected value for sattr->mtime.set_it = %d", sattr->mtime.set_it); } } return true; } /* nfs3_Sattr_To_FSALattr */ /**** Glue related functions ****/ /* * Conversion of attributes */ /** * @brief Converts FSAL Attributes to NFSv3 attributes. * * Fill in the fields in the fattr3 structure. * * @param[in] obj FSAL object. * @param[in] FSAL_attr FSAL attributes related to the FSAL object. * @param[out] Fattr NFSv3 attributes. * */ static void nfs3_FSALattr_To_PartialFattr(struct fsal_obj_handle *obj, const struct attrlist *FSAL_attr, fattr3 *Fattr) { if (FSAL_attr->valid_mask & ATTR_TYPE) { switch (FSAL_attr->type) { case FIFO_FILE: Fattr->type = NF3FIFO; break; case CHARACTER_FILE: Fattr->type = NF3CHR; break; case DIRECTORY: Fattr->type = NF3DIR; break; case BLOCK_FILE: Fattr->type = NF3BLK; break; case REGULAR_FILE: case EXTENDED_ATTR: Fattr->type = NF3REG; break; case SYMBOLIC_LINK: Fattr->type = NF3LNK; break; case SOCKET_FILE: Fattr->type = NF3SOCK; break; default: LogEvent(COMPONENT_NFSPROTO, "nfs3_FSALattr_To_Fattr: Bogus type = %d", FSAL_attr->type); } } if (FSAL_attr->valid_mask & ATTR_MODE) { Fattr->mode = fsal2unix_mode(FSAL_attr->mode); } if (FSAL_attr->valid_mask & ATTR_NUMLINKS) { Fattr->nlink = FSAL_attr->numlinks; } if (FSAL_attr->valid_mask & ATTR_OWNER) { Fattr->uid = FSAL_attr->owner; } if (FSAL_attr->valid_mask & ATTR_GROUP) { Fattr->gid = FSAL_attr->group; } if (FSAL_attr->valid_mask & ATTR_SIZE) { Fattr->size = FSAL_attr->filesize; } if (FSAL_attr->valid_mask & ATTR_SPACEUSED) { Fattr->used = FSAL_attr->spaceused; } if (FSAL_attr->valid_mask & ATTR_RAWDEV) { Fattr->rdev.specdata1 = FSAL_attr->rawdev.major; Fattr->rdev.specdata2 = FSAL_attr->rawdev.minor; } if (FSAL_attr->valid_mask & ATTR_FSID) { /* xor filesystem_id major and rotated minor to create unique * on-wire fsid. */ Fattr->fsid = (nfs3_uint64) squash_fsid(&obj->fsid); LogFullDebug(COMPONENT_NFSPROTO, "Compressing fsid for NFS v3 from fsid major %#" PRIX64 " (%" PRIu64 "), minor %#" PRIX64 " (%" PRIu64 ") to nfs3_fsid = %#" PRIX64 " (%" PRIu64 ")", obj->fsid.major, obj->fsid.major, obj->fsid.minor, obj->fsid.minor, (uint64_t) Fattr->fsid, (uint64_t) Fattr->fsid); } if (FSAL_attr->valid_mask & ATTR_FILEID) { Fattr->fileid = obj->fileid; } if (FSAL_attr->valid_mask & ATTR_ATIME) { Fattr->atime.tv_sec = FSAL_attr->atime.tv_sec; Fattr->atime.tv_nsec = FSAL_attr->atime.tv_nsec; } if (FSAL_attr->valid_mask & ATTR_MTIME) { Fattr->mtime.tv_sec = FSAL_attr->mtime.tv_sec; Fattr->mtime.tv_nsec = FSAL_attr->mtime.tv_nsec; } if (FSAL_attr->valid_mask & ATTR_CTIME) { Fattr->ctime.tv_sec = FSAL_attr->ctime.tv_sec; Fattr->ctime.tv_nsec = FSAL_attr->ctime.tv_nsec; } } /* nfs3_FSALattr_To_PartialFattr */ /** * @brief Convert FSAL Attributes to NFSv3 attributes. * * This function converts FSAL Attributes to NFSv3 attributes. The * callee is expecting the full compliment of FSAL attributes to fill * in all the fields in the fattr3 structure. * * @param export [IN] the related export entry * @param FSAL_attr [IN] pointer to FSAL attributes. * @param Fattr [OUT] pointer to NFSv3 attributes. * * @return true if successful, false otherwise. * */ bool nfs3_FSALattr_To_Fattr(struct fsal_obj_handle *obj, const struct attrlist *FSAL_attr, fattr3 *Fattr) { /* We want to override the FSAL fsid with the export's configured fsid */ attrmask_t want = ATTRS_NFS3; /* If an error occurred when getting object attributes, return false */ if (FSAL_attr->valid_mask == ATTR_RDATTR_ERR) return false; if ((want & FSAL_attr->valid_mask) != want) { LogCrit(COMPONENT_NFSPROTO, "Likely bug: FSAL did not fill in a standard NFSv3 attribute: missing %" PRIx64, want & ~(FSAL_attr->valid_mask)); return false; } nfs3_FSALattr_To_PartialFattr(obj, FSAL_attr, Fattr); if (op_ctx_export_has_option_set(EXPORT_OPTION_FSID_SET)) { /* xor filesystem_id major and rotated minor to create unique * on-wire fsid. */ Fattr->fsid = (nfs3_uint64) squash_fsid( &op_ctx->ctx_export->filesystem_id); LogFullDebug(COMPONENT_NFSPROTO, "Compressing export filesystem_id for NFS v3 from fsid major %#" PRIX64 " (%" PRIu64 "), minor %#" PRIX64 " (%" PRIu64 ") to nfs3_fsid = %#" PRIX64 " (%" PRIu64 ")", op_ctx->ctx_export->filesystem_id.major, op_ctx->ctx_export->filesystem_id.major, op_ctx->ctx_export->filesystem_id.minor, op_ctx->ctx_export->filesystem_id.minor, (uint64_t) Fattr->fsid, (uint64_t) Fattr->fsid); } return true; } /** * @brief Checks if attributes have READ or WRITE access * * @param[in] bitmap NFSv4 attribute bitmap * @param[in] access Access to be checked, either FATTR4_ATTR_READ or * FATTR4_ATTR_WRITE * * @return true if successful, false otherwise. * */ bool nfs4_Fattr_Check_Access_Bitmap(struct bitmap4 *bitmap, int access) { int attribute; assert((access == FATTR4_ATTR_READ) || (access == FATTR4_ATTR_WRITE)); for (attribute = next_attr_from_bitmap(bitmap, -1); attribute != -1; attribute = next_attr_from_bitmap(bitmap, attribute)) { if (attribute > FATTR4_XATTR_SUPPORT) { /* Erroneous value... skip */ continue; } if (((int)fattr4tab[attribute].access & access) != access) return false; } return true; } /* nfs4_Fattr_Check_Access */ /** * * nfs4_Fattr_Check_Access: checks if attributes have READ or WRITE access. * * Checks if attributes have READ or WRITE access. * * @param Fattr [IN] pointer to NFSv4 attributes. * @param access [IN] access to be checked, either FATTR4_ATTR_READ or * FATTR4_ATTR_WRITE * * @return true if successful, false otherwise. * */ bool nfs4_Fattr_Check_Access(fattr4 *Fattr, int access) { return nfs4_Fattr_Check_Access_Bitmap(&Fattr->attrmask, access); } /* nfs4_Fattr_Check_Access */ /** * @brief Remove unsupported attributes from bitmap4 * * @todo .supported is not nearly as good as actually checking with * the export. * * @param[in] bitmap NFSv4 attribute bitmap. */ void nfs4_bitmap4_Remove_Unsupported(struct bitmap4 *bitmap) { int attribute; for (attribute = 0; attribute <= FATTR4_XATTR_SUPPORT; attribute++) { if (!fattr4tab[attribute].supported) { if (!clear_attribute_in_bitmap(bitmap, attribute)) break; } } } /** * * nfs4_Fattr_Supported: Checks if an attribute is supported. * * Checks if an attribute is supported. * * @param Fattr [IN] pointer to NFSv4 attributes. * * @return true if successful, false otherwise. * */ bool nfs4_Fattr_Supported(fattr4 *Fattr) { int attribute; attrmask_t fsal_supported; /* Get the set of supported attributes from the active export. */ fsal_supported = op_ctx->fsal_export->exp_ops.fs_supported_attrs( op_ctx->fsal_export); for (attribute = next_attr_from_bitmap(&Fattr->attrmask, -1); attribute != -1; attribute = next_attr_from_bitmap(&Fattr->attrmask, attribute)) { bool supported = atrib_supported(attribute, fsal_supported); LogFullDebug(COMPONENT_NFS_V4, "Attribute %s Ganesha %s FSAL %s", fattr4tab[attribute].name, fattr4tab[attribute].supported ? "supported" : "not supported", supported ? "supported" : "not supported"); if (!supported) return false; } return true; } /** * * nfs4_Fattr_cmp: compares 2 fattr4 buffers. * * Compares 2 fattr4 buffers. * * @param Fattr1 [IN] pointer to NFSv4 attributes. * @param Fattr2 [IN] pointer to NFSv4 attributes. * * @return 1 if attributes are the same, 0 otherwise, but -1 if RDATTR_ERROR is * set * */ int nfs4_Fattr_cmp(fattr4 *Fattr1, fattr4 *Fattr2) { u_int LastOffset; uint32_t i; uint32_t k; u_int len = 0; int attribute_to_set = 0; if (Fattr1->attrmask.bitmap4_len != Fattr2->attrmask.bitmap4_len) { /* different mask */ return 0; } for (i = 0; i < Fattr1->attrmask.bitmap4_len; i++) if (Fattr1->attrmask.map[i] != Fattr2->attrmask.map[i]) return FALSE; if (attribute_is_set(&Fattr1->attrmask, FATTR4_RDATTR_ERROR)) return -1; LastOffset = 0; len = 0; if (Fattr1->attr_vals.attrlist4_len != Fattr2->attr_vals.attrlist4_len) { /* can't trust Fattr to be constructed properly * as it can come from client */ return 0; } if (Fattr1->attr_vals.attrlist4_len == 0) { /* Could have no attrlist if all the flags in bitmask * are invalid, both buffers are empty so equal */ return 1; } /** There has got to be a better way to do this but we do have to cope * with unaligned buffers for opaque data */ for (attribute_to_set = next_attr_from_bitmap(&Fattr1->attrmask, -1); attribute_to_set != -1; attribute_to_set = next_attr_from_bitmap(&Fattr1->attrmask, attribute_to_set)) { if (attribute_to_set > FATTR4_XATTR_SUPPORT) { /* Erroneous value... skip */ continue; } LogFullDebug(COMPONENT_NFS_V4, "Comparing %s", fattr4tab[attribute_to_set].name); switch (attribute_to_set) { case FATTR4_SUPPORTED_ATTRS: memcpy(&len, (char *)(Fattr1->attr_vals.attrlist4_val + LastOffset), sizeof(u_int)); if (memcmp ((char *)(Fattr1->attr_vals.attrlist4_val + LastOffset), (char *)(Fattr2->attr_vals.attrlist4_val + LastOffset), sizeof(u_int)) != 0) return 0; len = htonl(len); LastOffset += sizeof(u_int); for (k = 0; k < len; k++) { if (memcmp ((char *)(Fattr1->attr_vals.attrlist4_val + LastOffset), (char *)(Fattr2->attr_vals.attrlist4_val + LastOffset), sizeof(uint32_t)) != 0) return 0; LastOffset += sizeof(uint32_t); } break; case FATTR4_FILEHANDLE: case FATTR4_OWNER: case FATTR4_OWNER_GROUP: memcpy(&len, (char *)(Fattr1->attr_vals.attrlist4_val + LastOffset), sizeof(u_int)); len = ntohl(len); /* xdr marshalling on fattr4 */ if (memcmp ((char *)(Fattr1->attr_vals.attrlist4_val + LastOffset), (char *)(Fattr2->attr_vals.attrlist4_val + LastOffset), sizeof(u_int)) != 0) return 0; LastOffset += sizeof(u_int); if (memcmp ((char *)(Fattr1->attr_vals.attrlist4_val + LastOffset), (char *)(Fattr2->attr_vals.attrlist4_val + LastOffset), len) != 0) return 0; break; case FATTR4_TYPE: case FATTR4_FH_EXPIRE_TYPE: case FATTR4_CHANGE: case FATTR4_SIZE: case FATTR4_LINK_SUPPORT: case FATTR4_SYMLINK_SUPPORT: case FATTR4_NAMED_ATTR: case FATTR4_FSID: case FATTR4_UNIQUE_HANDLES: case FATTR4_LEASE_TIME: case FATTR4_RDATTR_ERROR: case FATTR4_ACL: case FATTR4_ACLSUPPORT: case FATTR4_ARCHIVE: case FATTR4_CANSETTIME: case FATTR4_CASE_INSENSITIVE: case FATTR4_CASE_PRESERVING: case FATTR4_CHOWN_RESTRICTED: case FATTR4_FILEID: case FATTR4_FILES_AVAIL: case FATTR4_FILES_FREE: case FATTR4_FILES_TOTAL: case FATTR4_FS_LOCATIONS: case FATTR4_HIDDEN: case FATTR4_HOMOGENEOUS: case FATTR4_MAXFILESIZE: case FATTR4_MAXLINK: case FATTR4_MAXNAME: case FATTR4_MAXREAD: case FATTR4_MAXWRITE: case FATTR4_MIMETYPE: case FATTR4_MODE: case FATTR4_NO_TRUNC: case FATTR4_NUMLINKS: case FATTR4_QUOTA_AVAIL_HARD: case FATTR4_QUOTA_AVAIL_SOFT: case FATTR4_QUOTA_USED: case FATTR4_RAWDEV: case FATTR4_SPACE_AVAIL: case FATTR4_SPACE_FREE: case FATTR4_SPACE_TOTAL: case FATTR4_SPACE_USED: case FATTR4_SYSTEM: case FATTR4_TIME_ACCESS: case FATTR4_TIME_ACCESS_SET: case FATTR4_TIME_BACKUP: case FATTR4_TIME_CREATE: case FATTR4_TIME_DELTA: case FATTR4_TIME_METADATA: case FATTR4_TIME_MODIFY: case FATTR4_TIME_MODIFY_SET: case FATTR4_MOUNTED_ON_FILEID: if (memcmp ((char *)(Fattr1->attr_vals.attrlist4_val + LastOffset), (char *)(Fattr2->attr_vals.attrlist4_val + LastOffset), fattr4tab[attribute_to_set].size_fattr4) != 0) return 0; break; default: return 0; } } return 1; } /** * * @brief Converts NFSv4 attributes buffer to a FSAL attributes structure. * * Converts NFSv4 attributes buffer to a FSAL attributes structure. * * NB! If the pointer for the handle is provided the memory is not allocated, * the handle's nfs_fh4_val points inside fattr4. The pointer is valid * as long as fattr4 is valid. * * @param[out] attrs pointer to FSAL attributes. * @param[in] Fattr pointer to NFSv4 attributes. * @param[out] hdl4 optional pointer to return NFSv4 file handle * @param[out] dinfo optional pointer to return 'dynamic info' about FS * * @return NFS4_OK if successful, NFS4ERR codes if not. * */ static int Fattr4_To_FSAL_attr(struct attrlist *attrs, fattr4 *Fattr, nfs_fh4 *hdl4, fsal_dynamicfsinfo_t *dinfo, compound_data_t *data) { int attribute_to_set = next_attr_from_bitmap(&Fattr->attrmask, -1); int nfs_status = NFS4_OK; XDR attr_body; struct xdr_attrs_args args; fattr_xdr_result xdr_res; /* Check attributes data */ if ((Fattr->attr_vals.attrlist4_val == NULL) || (Fattr->attr_vals.attrlist4_len == 0)) return attribute_to_set == -1 ? NFS4_OK : NFS4ERR_BADXDR; /* Init */ memset(&attr_body, 0, sizeof(attr_body)); xdrmem_create(&attr_body, Fattr->attr_vals.attrlist4_val, Fattr->attr_vals.attrlist4_len, XDR_DECODE); if (attrs) attrs->valid_mask = 0; memset(&args, 0, sizeof(args)); args.attrs = attrs; args.hdl4 = hdl4; args.dynamicinfo = dinfo; args.nfs_status = NFS4_OK; args.data = data; for (attribute_to_set = next_attr_from_bitmap(&Fattr->attrmask, -1); attribute_to_set != -1; attribute_to_set = next_attr_from_bitmap(&Fattr->attrmask, attribute_to_set)) { const struct fattr4_dent *f4e = fattr4tab + attribute_to_set; if (attribute_to_set > FATTR4_XATTR_SUPPORT) { nfs_status = NFS4ERR_BADXDR; /* undefined attr */ goto decodeerr; } xdr_res = f4e->decode(&attr_body, &args); if (xdr_res == FATTR_XDR_SUCCESS) { if (attrs) attrs->valid_mask |= f4e->attrmask; LogFullDebug(COMPONENT_NFS_V4, "Decode attr %d, name = %s", attribute_to_set, f4e->name); } else if (xdr_res == FATTR_XDR_SUCCESS_EXP) { /* Since we are setting a time attribute to * server time, we must use a different mask * position in attrmask_t. */ if (attrs) attrs->valid_mask |= f4e->exp_attrmask; LogFullDebug(COMPONENT_NFS_V4, "Decode (exp) attr %d, name = %s", attribute_to_set, f4e->name); } else if (xdr_res == FATTR_XDR_NOOP) { LogFullDebug(COMPONENT_NFS_V4, "Attr not supported %d name=%s", attribute_to_set, f4e->name); if (nfs_status == NFS4_OK) { /* preserve previous error */ nfs_status = NFS4ERR_ATTRNOTSUPP; } goto decodeerr; } else { LogFullDebug(COMPONENT_NFS_V4, "Decode attr FAILED: %d, name = %s", attribute_to_set, f4e->name); if (args.nfs_status == NFS4_OK) nfs_status = NFS4ERR_BADXDR; else nfs_status = args.nfs_status; goto decodeerr; } } if (xdr_getpos(&attr_body) < Fattr->attr_vals.attrlist4_len) nfs_status = NFS4ERR_BADXDR; /* underrun on attribute */ decodeerr: xdr_destroy(&attr_body); return nfs_status; } /** * * @brief Converts NFSv4 attributes mask to a FSAL attribute mask. * * @param[in] bitmap4 Requested attributes * @param[out] mask FSAL attribute mask * * @return NFS4_OK if successful, NFS4ERR codes if not. * */ int bitmap4_to_attrmask_t(bitmap4 *bitmap4, attrmask_t *mask) { int attribute_to_set = next_attr_from_bitmap(bitmap4, -1); int nfs_status = NFS4_OK; *mask = 0; for (attribute_to_set = next_attr_from_bitmap(bitmap4, -1); attribute_to_set != -1; attribute_to_set = next_attr_from_bitmap(bitmap4, attribute_to_set)) { const struct fattr4_dent *f4e = fattr4tab + attribute_to_set; if (attribute_to_set > FATTR4_XATTR_SUPPORT) { nfs_status = NFS4ERR_BADXDR; /* undefined attr */ break; } *mask |= f4e->attrmask; LogFullDebug(COMPONENT_NFS_V4, "Request attr %d, name = %s", attribute_to_set, f4e->name); } return nfs_status; } /** * @brief Convert NFSv4 attribute buffer to an FSAL attribute list * * @param[out] FSAL_attr FSAL attributes * @param[in] Fattr NFSv4 attributes * @param[in] data Compound data * * @return NFS4_OK if successful, NFS4ERR codes if not. * */ int nfs4_Fattr_To_FSAL_attr(struct attrlist *FSAL_attr, fattr4 *Fattr, compound_data_t *data) { memset(FSAL_attr, 0, sizeof(struct attrlist)); return Fattr4_To_FSAL_attr(FSAL_attr, Fattr, NULL, NULL, data); } /** * * nfs4_Fattr_To_fsinfo: Decode filesystem info out of NFSv4 attributes. * * Converts information encoded in NFSv4 attributes buffer to 'dynamic info' * about an exported filesystem. * * It is assumed and expected that the fattr4 blob is not * going to have any other attributes expect those necessary * to fill in details about sace and inode utilization. * * @param dinfo [OUT] pointer to dynamic info * @param Fattr [IN] pointer to NFSv4 attributes. * * @return NFS4_OK if successful, NFS4ERR codes if not. * */ int nfs4_Fattr_To_fsinfo(fsal_dynamicfsinfo_t *dinfo, fattr4 *Fattr) { return Fattr4_To_FSAL_attr(NULL, Fattr, NULL, dinfo, NULL); } /* nfs4_utf8string2dynamic * unpack the input string from the XDR into a null term'd string * scan for bad chars */ nfsstat4 nfs4_utf8string2dynamic(const utf8string *input, utf8_scantype_t scan, char **obj_name) { nfsstat4 status = NFS4_OK; *obj_name = NULL; if (input->utf8string_val == NULL || input->utf8string_len == 0) return NFS4ERR_INVAL; if (((scan & UTF8_SCAN_PATH) && input->utf8string_len > MAXPATHLEN) || (!(scan & UTF8_SCAN_PATH) && input->utf8string_len > MAXNAMLEN)) return NFS4ERR_NAMETOOLONG; char *name = gsh_malloc(input->utf8string_len + 1); memcpy(name, input->utf8string_val, input->utf8string_len); name[input->utf8string_len] = '\0'; if (scan != UTF8_SCAN_NONE) status = path_filter(name, scan); if (status == NFS4_OK) *obj_name = name; else gsh_free(name); return status; } /** * @brief: is a directory's sticky bit set? * */ bool is_sticky_bit_set(struct fsal_obj_handle *obj, const struct attrlist *attr) { if (attr->mode & (S_IXUSR|S_IXGRP|S_IXOTH)) return false; if (!(attr->mode & S_ISVTX)) return false; LogDebug(COMPONENT_NFS_V4, "sticky bit is set on %" PRIi64, obj->fileid); return true; } nfs-ganesha-2.6.0/src/Protocols/NLM/000077500000000000000000000000001324272410200171075ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/Protocols/NLM/CMakeLists.txt000066400000000000000000000016371324272410200216560ustar00rootroot00000000000000########### next target ############### SET(nlm_STAT_SRCS nlm_Cancel.c nlm_Free_All.c nlm_Granted_Res.c nlm_Lock.c nlm_Null.c nlm_Share.c nlm_Sm_Notify.c nlm_Test.c nlm_Unlock.c nlm_Unshare.c nlm_async.c nlm_util.c nsm.c ) add_library(nlm STATIC ${nlm_STAT_SRCS}) add_sanitizers(nlm) ########### next target ############### if(LINUX) SET(sm_notify_SRCS sm_notify.c ) add_executable(sm_notify.ganesha ${sm_notify_SRCS}) add_sanitizers(sm_notify.ganesha) target_link_libraries(sm_notify.ganesha ${PROTOCOLS} ${LIBTIRPC_LIBRARIES} ${SYSTEM_LIBRARIES} ) #target_link_libraries(sm_notify.ganesha ${CMAKE_THREAD_LIBS_INIT}) if( USE_ADMIN_TOOLS ) install(TARGETS sm_notify.ganesha DESTINATION bin) endif( USE_ADMIN_TOOLS ) endif(LINUX) install(TARGETS COMPONENT daemon DESTINATION bin) ########### install files ############### nfs-ganesha-2.6.0/src/Protocols/NLM/nlm_Cancel.c000066400000000000000000000134641324272410200213160ustar00rootroot00000000000000/* * Copyright IBM Corporation, 2010 * Contributor: Aneesh Kumar K.v * * -------------------------- * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * */ #include "config.h" #include #include #include #include "log.h" #include "fsal.h" #include "nfs_proto_functions.h" #include "sal_functions.h" #include "nlm_util.h" #include "nlm_async.h" /** * @brief Cancel a blocked range lock * * @param[in] arg * @param[in] req * @param[out] res * */ int nlm4_Cancel(nfs_arg_t *args, struct svc_req *req, nfs_res_t *res) { nlm4_cancargs *arg = &args->arg_nlm4_cancel; struct fsal_obj_handle *obj; state_status_t state_status = STATE_SUCCESS; char buffer[MAXNETOBJ_SZ * 2] = "\0"; state_nsm_client_t *nsm_client; state_nlm_client_t *nlm_client; state_owner_t *nlm_owner; fsal_lock_param_t lock; int rc; /* NLM doesn't have a BADHANDLE error, nor can rpc_execute deal with * responding to an NLM_*_MSG call, so we check here if the export is * NULL and if so, handle the response. */ if (op_ctx->ctx_export == NULL) { res->res_nlm4.stat.stat = NLM4_STALE_FH; LogInfo(COMPONENT_NLM, "INVALID HANDLE: NLM4_CANCEL"); return NFS_REQ_OK; } netobj_to_string(&arg->cookie, buffer, 1024); LogDebug(COMPONENT_NLM, "REQUEST PROCESSING: Calling NLM4_CANCEL svid=%d off=%llx len=%llx cookie=%s", (int)arg->alock.svid, (unsigned long long)arg->alock.l_offset, (unsigned long long)arg->alock.l_len, buffer); copy_netobj(&res->res_nlm4test.cookie, &arg->cookie); if (nfs_in_grace()) { res->res_nlm4.stat.stat = NLM4_DENIED_GRACE_PERIOD; LogDebug(COMPONENT_NLM, "REQUEST RESULT: NLM4_CANCEL %s", lock_result_str(res->res_nlm4.stat.stat)); return NFS_REQ_OK; } /* cancel doesn't care if owner is found */ rc = nlm_process_parameters(req, arg->exclusive, &arg->alock, &lock, &obj, CARE_NOT, &nsm_client, &nlm_client, &nlm_owner, NULL, 0, NULL); if (rc >= 0) { /* resent the error back to the client */ res->res_nlm4.stat.stat = (nlm4_stats) rc; LogDebug(COMPONENT_NLM, "REQUEST RESULT: nlm4_Unlock %s", lock_result_str(res->res_nlm4.stat.stat)); return NFS_REQ_OK; } state_status = state_cancel(obj, nlm_owner, &lock); if (state_status != STATE_SUCCESS) { /* Cancel could fail in the FSAL and make a bit of a mess, * especially if we are in out of memory situation. Such an * error is logged by Cache Inode. */ res->res_nlm4test.test_stat.stat = nlm_convert_state_error(state_status); } else { res->res_nlm4.stat.stat = NLM4_GRANTED; } /* Release the NLM Client and NLM Owner references we have */ dec_nsm_client_ref(nsm_client); dec_nlm_client_ref(nlm_client); dec_state_owner_ref(nlm_owner); obj->obj_ops.put_ref(obj); LogDebug(COMPONENT_NLM, "REQUEST RESULT: NLM4_CANCEL %s", lock_result_str(res->res_nlm4.stat.stat)); return NFS_REQ_OK; } /* nlm4_Cancel */ static void nlm4_cancel_message_resp(state_async_queue_t *arg) { state_nlm_async_data_t *nlm_arg = &arg->state_async_data.state_nlm_async_data; nfs_res_t *res = &nlm_arg->nlm_async_args.nlm_async_res; if (isFullDebug(COMPONENT_NLM)) { char buffer[1024] = "\0"; netobj_to_string(&res->res_nlm4test.cookie, buffer, 1024); LogFullDebug(COMPONENT_NLM, "Calling nlm_send_async cookie=%s status=%s", buffer, lock_result_str(res->res_nlm4.stat.stat)); } nlm_send_async(NLMPROC4_CANCEL_RES, nlm_arg->nlm_async_host, res, NULL); nlm4_Cancel_Free(res); dec_nsm_client_ref(nlm_arg->nlm_async_host->slc_nsm_client); dec_nlm_client_ref(nlm_arg->nlm_async_host); gsh_free(arg); } /* Asynchronous Message Entry Point */ /** * @brief Cancel Lock Message * * @param[in] arg * @param[in] req * @param[out] res * */ int nlm4_Cancel_Message(nfs_arg_t *args, struct svc_req *req, nfs_res_t *res) { state_nlm_client_t *nlm_client = NULL; state_nsm_client_t *nsm_client; nlm4_cancargs *arg = &args->arg_nlm4_cancel; int rc = NFS_REQ_OK; LogDebug(COMPONENT_NLM, "REQUEST PROCESSING: Calling nlm_Cancel_Message"); nsm_client = get_nsm_client(CARE_NO_MONITOR, req->rq_xprt, arg->alock.caller_name); if (nsm_client != NULL) nlm_client = get_nlm_client(CARE_NO_MONITOR, req->rq_xprt, nsm_client, arg->alock.caller_name); if (nlm_client == NULL) rc = NFS_REQ_DROP; else rc = nlm4_Cancel(args, req, res); if (rc == NFS_REQ_OK) rc = nlm_send_async_res_nlm4(nlm_client, nlm4_cancel_message_resp, res); if (rc == NFS_REQ_DROP) { if (nsm_client != NULL) dec_nsm_client_ref(nsm_client); if (nlm_client != NULL) dec_nlm_client_ref(nlm_client); LogCrit(COMPONENT_NLM, "Could not send async response for nlm_Cancel_Message"); } return NFS_REQ_DROP; } /** * nlm4_Lock_Free: Frees the result structure allocated for nlm4_Lock * * Frees the result structure allocated for nlm4_Lock. Does Nothing in fact. * * @param res [INOUT] Pointer to the result structure. * */ void nlm4_Cancel_Free(nfs_res_t *res) { netobj_free(&res->res_nlm4test.cookie); } /* nlm4_Cancel_Free */ nfs-ganesha-2.6.0/src/Protocols/NLM/nlm_Free_All.c000066400000000000000000000047331324272410200216010ustar00rootroot00000000000000/* * Copyright IBM Corporation, 2012 * Contributor: Frank Filz * * -------------------------- * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * */ #include "config.h" #include #include #include #include "gsh_rpc.h" #include "log.h" #include "nlm4.h" #include "sal_functions.h" #include "nfs_proto_functions.h" #include "nlm_util.h" /** * @brief Free All Locks * * @param[in] arg * @param[in] req * @param[out] res */ int nlm4_Free_All(nfs_arg_t *args, struct svc_req *req, nfs_res_t *res) { nlm4_free_allargs *arg = &args->arg_nlm4_free_allargs; state_status_t state_status = STATE_SUCCESS; state_nsm_client_t *nsm_client; LogDebug(COMPONENT_NLM, "REQUEST PROCESSING: Calling NLM4_FREE_ALL for %s", arg->name); nsm_client = get_nsm_client(CARE_NOT, req->rq_xprt, arg->name); if (nsm_client != NULL) { /* NLM_FREE_ALL has the same semantics as handling SM_NOTIFY. * * Cast the state number into a state pointer to protect * locks from a client that has rebooted from being released * by this NLM_FREE_ALL. */ state_status = state_nlm_notify(nsm_client, false, 0); if (state_status != STATE_SUCCESS) { /* NLM_FREE_ALL has void result so all we can do is * log error */ LogWarn(COMPONENT_NLM, "NLM_FREE_ALL failed with result %s", state_err_str(state_status)); } dec_nsm_client_ref(nsm_client); } LogDebug(COMPONENT_NLM, "REQUEST RESULT: NLM4_FREE_ALL DONE"); return NFS_REQ_OK; } /** * nlm4_Free_All_Free: Frees the result structure allocated for nlm4_Free_All * * Frees the result structure allocated for nlm4_Free_All. Does Nothing in fact. * * @param res [INOUT] Pointer to the result structure. * */ void nlm4_Free_All_Free(nfs_res_t *res) { /* Nothing to do */ } nfs-ganesha-2.6.0/src/Protocols/NLM/nlm_Granted_Res.c000066400000000000000000000067401324272410200223250ustar00rootroot00000000000000/* * Copyright IBM Corporation, 2010 * Contributor: M. Mohan Kumar * * -------------------------- * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * */ #include "config.h" #include #include #include #include "log.h" #include "fsal.h" #include "nfs_proto_functions.h" #include "sal_functions.h" #include "nlm_util.h" #include "nlm_async.h" #include "export_mgr.h" /** * @brief Lock Granted Result Handler * * @param[in] arg * @param[in] req * @param[out] res * */ int nlm4_Granted_Res(nfs_arg_t *args, struct svc_req *req, nfs_res_t *res) { nlm4_res *arg = &args->arg_nlm4_res; char buffer[1024] = "\0"; state_status_t state_status = STATE_SUCCESS; state_cookie_entry_t *cookie_entry; netobj_to_string(&arg->cookie, buffer, 1024); LogDebug(COMPONENT_NLM, "REQUEST PROCESSING: Calling nlm_Granted_Res cookie=%s", buffer); state_status = state_find_grant(arg->cookie.n_bytes, arg->cookie.n_len, &cookie_entry); if (state_status != STATE_SUCCESS) { /* This must be an old NLM_GRANTED_RES */ LogFullDebug(COMPONENT_NLM, "Could not find cookie=%s (must be an old NLM_GRANTED_RES)", buffer); return NFS_REQ_OK; } if (cookie_entry->sce_lock_entry == NULL || cookie_entry->sce_lock_entry->sle_block_data == NULL) { /* This must be an old NLM_GRANTED_RES */ LogFullDebug(COMPONENT_NLM, "Could not find block data for cookie=%s (must be an old NLM_GRANTED_RES)", buffer); return NFS_REQ_OK; } /* Fill in op_ctx, nfs_rpc_process_request will release the export ref. * We take an export reference even if the export is stale because * we want to properly clean up the cookie_entry. */ op_ctx->ctx_export = cookie_entry->sce_lock_entry->sle_export; op_ctx->fsal_export = op_ctx->ctx_export->fsal_export; get_gsh_export_ref(op_ctx->ctx_export); /* If the client returned an error or the export has gone stale, * release the grant to properly clean up cookie_entry. */ if (arg->stat.stat != NLM4_GRANTED || !export_ready(op_ctx->ctx_export)) { LogMajor(COMPONENT_NLM, "Granted call failed due to %s, releasing lock", arg->stat.stat != NLM4_GRANTED ? "client error" : "export stale"); state_status = state_release_grant(cookie_entry); if (state_status != STATE_SUCCESS) { LogDebug(COMPONENT_NLM, "cache_inode_release_grant failed"); } } else { state_complete_grant(cookie_entry); nlm_signal_async_resp(cookie_entry); } return NFS_REQ_OK; } /** * nlm4_Granted_Res_Free: Frees the result structure allocated for * nlm4_Granted_Res * * Frees the result structure allocated for nlm4_Granted_Res. Does Nothing * in fact. * * @param res [INOUT] Pointer to the result structure. * */ void nlm4_Granted_Res_Free(nfs_res_t *res) { /* Nothing to do */ } nfs-ganesha-2.6.0/src/Protocols/NLM/nlm_Lock.c000066400000000000000000000202651324272410200210160ustar00rootroot00000000000000/* * Copyright IBM Corporation, 2010 * Contributor: Aneesh Kumar K.v * * -------------------------- * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "config.h" #include #include #include #include "log.h" #include "fsal.h" #include "nfs_proto_functions.h" #include "sal_functions.h" #include "nlm_util.h" #include "nlm_async.h" /** * @brief Set a range lock * * @param[in] arg * @param[in] req * @param[out] res * */ int nlm4_Lock(nfs_arg_t *args, struct svc_req *req, nfs_res_t *res) { nlm4_lockargs *arg = &args->arg_nlm4_lock; struct fsal_obj_handle *obj; state_status_t state_status = STATE_SUCCESS; char buffer[MAXNETOBJ_SZ * 2] = "\0"; state_nsm_client_t *nsm_client; state_nlm_client_t *nlm_client; state_owner_t *nlm_owner, *holder; state_t *nlm_state; fsal_lock_param_t lock, conflict; int rc; bool grace = nfs_in_grace(); state_block_data_t *pblock_data; const char *proc_name = "nlm4_Lock"; care_t care = CARE_MONITOR; /* Indicate if we let FSAL to handle requests during grace. */ bool_t fsal_grace = false; if (req->rq_msg.cb_proc == NLMPROC4_NM_LOCK) { /* If call is a NM lock, indicate that we care about NLM * client but will not monitor. */ proc_name = "nlm4_NM_Lock"; care = CARE_NO_MONITOR; } /* NLM doesn't have a BADHANDLE error, nor can rpc_execute deal with * responding to an NLM_*_MSG call, so we check here if the export is * NULL and if so, handle the response. */ if (op_ctx->ctx_export == NULL) { res->res_nlm4.stat.stat = NLM4_STALE_FH; LogInfo(COMPONENT_NLM, "INVALID HANDLE: %s", proc_name); return NFS_REQ_OK; } netobj_to_string(&arg->cookie, buffer, 1024); LogDebug(COMPONENT_NLM, "REQUEST PROCESSING: Calling %s svid=%d off=%llx len=%llx cookie=%s reclaim=%s", proc_name, (int)arg->alock.svid, (unsigned long long)arg->alock.l_offset, (unsigned long long)arg->alock.l_len, buffer, arg->reclaim ? "yes" : "no"); copy_netobj(&res->res_nlm4test.cookie, &arg->cookie); if (grace) { /* allow only reclaim lock request during recovery */ if (op_ctx->fsal_export->exp_ops.fs_supports( op_ctx->fsal_export, fso_grace_method)) fsal_grace = true; if (!fsal_grace && !arg->reclaim) { res->res_nlm4.stat.stat = NLM4_DENIED_GRACE_PERIOD; LogDebug(COMPONENT_NLM, "REQUEST RESULT: in grace %s %s", proc_name, lock_result_str(res->res_nlm4.stat.stat)); return NFS_REQ_OK; } } else if (arg->reclaim) { /* don't allow reclaim if not in recovery */ res->res_nlm4.stat.stat = NLM4_DENIED_GRACE_PERIOD; LogDebug(COMPONENT_NLM, "REQUEST RESULT: not in grace %s %s", proc_name, lock_result_str(res->res_nlm4.stat.stat)); return NFS_REQ_OK; } rc = nlm_process_parameters(req, arg->exclusive, &arg->alock, &lock, &obj, care, &nsm_client, &nlm_client, &nlm_owner, &pblock_data, arg->state, &nlm_state); lock.lock_reclaim = arg->reclaim; if (rc >= 0) { /* Present the error back to the client */ res->res_nlm4.stat.stat = (nlm4_stats) rc; LogDebug(COMPONENT_NLM, "REQUEST RESULT: %s %s", proc_name, lock_result_str(res->res_nlm4.stat.stat)); return NFS_REQ_OK; } /* Check if v4 delegations conflict with v3 op */ PTHREAD_RWLOCK_rdlock(&obj->state_hdl->state_lock); if (state_deleg_conflict(obj, lock.lock_type == FSAL_LOCK_W)) { PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); LogDebug(COMPONENT_NLM, "NLM lock request DROPPED due to delegation conflict"); rc = NFS_REQ_DROP; goto out; } else { (void) atomic_inc_uint32_t(&obj->state_hdl->file.anon_ops); PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); } /* Cast the state number into a state pointer to protect * locks from a client that has rebooted from the SM_NOTIFY * that will release old locks */ PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); state_status = state_lock(obj, nlm_owner, nlm_state, arg->block ? STATE_NLM_BLOCKING : STATE_NON_BLOCKING, pblock_data, &lock, &holder, &conflict); PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); /* We prevented delegations from being granted while trying to acquire * the lock. However, when attempting to get a delegation in the * future existing locks will result in a conflict. Thus, we can * decrement the anonymous operations counter now. */ (void) atomic_dec_uint32_t(&obj->state_hdl->file.anon_ops); if (state_status != STATE_SUCCESS) { res->res_nlm4test.test_stat.stat = nlm_convert_state_error(state_status); if (state_status == STATE_LOCK_CONFLICT) { nlm_process_conflict( &res->res_nlm4test.test_stat.nlm4_testrply_u.holder, holder, &conflict); } /* If we didn't block, release the block data */ if (state_status != STATE_LOCK_BLOCKED && pblock_data != NULL) gsh_free(pblock_data); if (state_status == STATE_IN_GRACE) { res->res_nlm4.stat.stat = NLM4_DENIED_GRACE_PERIOD; goto out_ok; } } else { res->res_nlm4.stat.stat = NLM4_GRANTED; } out_ok: rc = NFS_REQ_OK; out: /* Release the NLM Client and NLM Owner references we have */ dec_nsm_client_ref(nsm_client); dec_nlm_client_ref(nlm_client); dec_state_owner_ref(nlm_owner); obj->obj_ops.put_ref(obj); dec_nlm_state_ref(nlm_state); LogDebug(COMPONENT_NLM, "REQUEST RESULT: %s %s", proc_name, lock_result_str(res->res_nlm4.stat.stat)); return rc; } static void nlm4_lock_message_resp(state_async_queue_t *arg) { state_nlm_async_data_t *nlm_arg = &arg->state_async_data.state_nlm_async_data; nfs_res_t *res = &nlm_arg->nlm_async_args.nlm_async_res; if (isFullDebug(COMPONENT_NLM)) { char buffer[1024] = "\0"; netobj_to_string(&res->res_nlm4test.cookie, buffer, 1024); LogFullDebug(COMPONENT_NLM, "Calling nlm_send_async cookie=%s status=%s", buffer, lock_result_str(res->res_nlm4.stat.stat)); } nlm_send_async(NLMPROC4_LOCK_RES, nlm_arg->nlm_async_host, res, NULL); nlm4_Lock_Free(res); dec_nsm_client_ref(nlm_arg->nlm_async_host->slc_nsm_client); dec_nlm_client_ref(nlm_arg->nlm_async_host); gsh_free(arg); } /** * @brief Lock Message * * @param[in] arg * @param[in] req * @param[out] res * */ int nlm4_Lock_Message(nfs_arg_t *args, struct svc_req *req, nfs_res_t *res) { state_nlm_client_t *nlm_client = NULL; state_nsm_client_t *nsm_client; nlm4_lockargs *arg = &args->arg_nlm4_lock; int rc = NFS_REQ_OK; LogDebug(COMPONENT_NLM, "REQUEST PROCESSING: Calling nlm_Lock_Message"); nsm_client = get_nsm_client(CARE_NO_MONITOR, req->rq_xprt, arg->alock.caller_name); if (nsm_client != NULL) nlm_client = get_nlm_client(CARE_NO_MONITOR, req->rq_xprt, nsm_client, arg->alock.caller_name); if (nlm_client == NULL) rc = NFS_REQ_DROP; else rc = nlm4_Lock(args, req, res); if (rc == NFS_REQ_OK) rc = nlm_send_async_res_nlm4(nlm_client, nlm4_lock_message_resp, res); if (rc == NFS_REQ_DROP) { if (nsm_client != NULL) dec_nsm_client_ref(nsm_client); if (nlm_client != NULL) dec_nlm_client_ref(nlm_client); LogCrit(COMPONENT_NLM, "Could not send async response for nlm_Lock_Message"); } return NFS_REQ_DROP; } /** * nlm4_Lock_Free: Frees the result structure allocated for nlm4_Lock * * Frees the result structure allocated for nlm4_Lock. Does Nothing in fact. * * @param res [INOUT] Pointer to the result structure. * */ void nlm4_Lock_Free(nfs_res_t *res) { netobj_free(&res->res_nlm4test.cookie); } nfs-ganesha-2.6.0/src/Protocols/NLM/nlm_Null.c000066400000000000000000000033231324272410200210340ustar00rootroot00000000000000/* * Copyright IBM Corporation, 2010 * Contributor: Aneesh Kumar K.v * * -------------------------- * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * */ #include "config.h" #include #include #include #include "log.h" #include "gsh_rpc.h" #include "nlm4.h" #include "nlm_util.h" #include "nlm_async.h" /** * @brief The NLM proc null function, for all versions. * * The NLM proc null function, for all versions. * * @param[in] parg Ignored * @param[in] preq Ignored * @param[out] pres Ignored * */ int nlm_Null(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { LogDebug(COMPONENT_NLM, "REQUEST PROCESSING: Calling NLM_NULL"); /* 0 is success */ return 0; } /** * nlm_Null_Free: Frees the result structure allocated for nlm_Null * * Frees the result structure allocated for nlm_Null. Does Nothing in fact. * * @param res [INOUT] Pointer to the result structure. * */ void nlm_Null_Free(nfs_res_t *res) { /* Nothing to do */ } nfs-ganesha-2.6.0/src/Protocols/NLM/nlm_Share.c000066400000000000000000000107061324272410200211670ustar00rootroot00000000000000/* * Copyright IBM Corporation, 2012 * Contributor: Frank Filz * * -------------------------- * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * */ #include "config.h" #include #include #include #include "log.h" #include "fsal.h" #include "nfs_proto_functions.h" #include "sal_functions.h" #include "nlm_util.h" #include "nlm_async.h" /** * @brief Set a share reservation * * @param[in] arg * @param[in] req * @param[out] res */ int nlm4_Share(nfs_arg_t *args, struct svc_req *req, nfs_res_t *res) { nlm4_shareargs *arg = &args->arg_nlm4_share; struct fsal_obj_handle *obj; state_status_t state_status = STATE_SUCCESS; char buffer[MAXNETOBJ_SZ * 2] = "\0"; state_nsm_client_t *nsm_client; state_nlm_client_t *nlm_client; state_owner_t *nlm_owner; state_t *nlm_state; int rc; bool grace = nfs_in_grace(); /* Indicate if we let FSAL to handle requests during grace. */ bool_t fsal_grace = false; /* NLM doesn't have a BADHANDLE error, nor can rpc_execute deal with * responding to an NLM_*_MSG call, so we check here if the export is * NULL and if so, handle the response. */ if (op_ctx->ctx_export == NULL) { res->res_nlm4share.stat = NLM4_STALE_FH; LogInfo(COMPONENT_NLM, "INVALID HANDLE: NLM4_SHARE"); return NFS_REQ_OK; } res->res_nlm4share.sequence = 0; netobj_to_string(&arg->cookie, buffer, 1024); LogDebug(COMPONENT_NLM, "REQUEST PROCESSING: Calling NLM4_SHARE cookie=%s reclaim=%s", buffer, arg->reclaim ? "yes" : "no"); copy_netobj(&res->res_nlm4share.cookie, &arg->cookie); /* Allow only reclaim share request during recovery and visa versa. * Note: NLM_SHARE is indicated to be non-monitored, however, it does * have a reclaim flag, so we will honor the reclaim flag if used. */ if (grace) { if (op_ctx->fsal_export->exp_ops.fs_supports( op_ctx->fsal_export, fso_grace_method)) fsal_grace = true; if (!fsal_grace && !arg->reclaim) { res->res_nlm4share.stat = NLM4_DENIED_GRACE_PERIOD; LogDebug(COMPONENT_NLM, "REQUEST RESULT: NLM4_SHARE %s", lock_result_str(res->res_nlm4share.stat)); return NFS_REQ_OK; } } else if (arg->reclaim) { res->res_nlm4share.stat = NLM4_DENIED_GRACE_PERIOD; LogDebug(COMPONENT_NLM, "REQUEST RESULT: NLM4_SHARE %s", lock_result_str(res->res_nlm4share.stat)); return NFS_REQ_OK; } rc = nlm_process_share_parms(req, &arg->share, op_ctx->fsal_export, &obj, CARE_NO_MONITOR, &nsm_client, &nlm_client, &nlm_owner, &nlm_state); if (rc >= 0) { /* Present the error back to the client */ res->res_nlm4share.stat = (nlm4_stats) rc; LogDebug(COMPONENT_NLM, "REQUEST RESULT: NLM4_SHARE %s", lock_result_str(res->res_nlm4share.stat)); return NFS_REQ_OK; } state_status = state_nlm_share(obj, arg->share.access, arg->share.mode, nlm_owner, nlm_state, grace, false); if (state_status != STATE_SUCCESS) { res->res_nlm4share.stat = nlm_convert_state_error(state_status); } else { res->res_nlm4share.stat = NLM4_GRANTED; } /* Release the NLM Client and NLM Owner references we have */ dec_nsm_client_ref(nsm_client); dec_nlm_client_ref(nlm_client); dec_state_owner_ref(nlm_owner); obj->obj_ops.put_ref(obj); dec_nlm_state_ref(nlm_state); LogDebug(COMPONENT_NLM, "REQUEST RESULT: NLM4_SHARE %s", lock_result_str(res->res_nlm4share.stat)); return NFS_REQ_OK; } /** * nlm4_Share_Free: Frees the result structure allocated for nlm4_Lock * * Frees the result structure allocated for nlm4_Lock. Does Nothing in fact. * * @param res [INOUT] Pointer to the result structure. * */ void nlm4_Share_Free(nfs_res_t *res) { netobj_free(&res->res_nlm4share.cookie); } nfs-ganesha-2.6.0/src/Protocols/NLM/nlm_Sm_Notify.c000066400000000000000000000124031324272410200220300ustar00rootroot00000000000000/* * Copyright IBM Corporation, 2010 * Contributor: Aneesh Kumar K.v * * -------------------------- * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * */ #include "config.h" #include #include #include #include #include "log.h" #include "gsh_rpc.h" #include "nlm4.h" #include "sal_functions.h" #include "nfs_proto_functions.h" #include "nlm_util.h" #define IN4_LOCALHOST_STRING "127.0.0.1" #define IN6_LOCALHOST_STRING "::1" #define IN6_ENCAPSULATED_IN4_LOCALHOST_STRING "::ffff:127.0.0.1" static void check_use_caller_name_ipv4(char *name) { unsigned char addrbuf[4]; sockaddr_t name_addr; struct gsh_client *name_client; if (strcmp(op_ctx->client->hostaddr_str, IN4_LOCALHOST_STRING) != 0) return; /* get the gsh_client for the caller name */ if (inet_pton(AF_INET, name, addrbuf) != 1) return; memcpy(&(((struct sockaddr_in *)&name_addr)->sin_addr), addrbuf, 4); name_addr.ss_family = AF_INET; name_client = get_gsh_client(&name_addr, false); /* Check if localhost has sent SM NOTIFY request on behalf of a client * with name as caller name * If yes, then we can use the client IP instead of localhost IP */ if (name_client != NULL && (strcmp(name_client->hostaddr_str, op_ctx->client->hostaddr_str) != 0)) { LogDebug(COMPONENT_NLM, "SM_NOTIFY request using host address: %s", name_client->hostaddr_str); memcpy(&(((struct sockaddr_in *)op_ctx->caller_addr) ->sin_addr), addrbuf, 4); SetClientIP(name_client->hostaddr_str); op_ctx->client = name_client; } } static void check_use_caller_name_ipv6(char *name) { unsigned char addrbuf[16]; sockaddr_t name_addr; struct gsh_client *name_client; if ((strcmp(op_ctx->client->hostaddr_str, IN6_LOCALHOST_STRING) != 0) && (strcmp(op_ctx->client->hostaddr_str, IN6_ENCAPSULATED_IN4_LOCALHOST_STRING) != 0)) return; /* get the gsh_client for the caller name */ if (inet_pton(AF_INET6, name, addrbuf) != 1) return; memcpy(&(((struct sockaddr_in6 *)&name_addr)->sin6_addr), addrbuf, 16); name_addr.ss_family = AF_INET6; name_client = get_gsh_client(&name_addr, false); /* Check if localhost has sent SM NOTIFY request on behalf of a client * with name as caller name * If yes, then we can use the client IP instead of localhost IP */ if (name_client != NULL && (strcmp(name_client->hostaddr_str, op_ctx->client->hostaddr_str) != 0)) { LogDebug(COMPONENT_NLM, "SM_NOTIFY request using host address: %s", name_client->hostaddr_str); memcpy(&(((struct sockaddr_in6 *)op_ctx->caller_addr) ->sin6_addr), addrbuf, 16); SetClientIP(name_client->hostaddr_str); op_ctx->client = name_client; } } /* * Check if a SM_NOTIFY request is given by the 'localhost' * If that is the case check the caller name, if it is not same as 'localhost' * then a 'localhost' has formed a NLM_SM_NOTIFY request for a client which * has held lock(s) but not available to release the lock(s). The client is * specified with the caller name in the request. Use the client's caller * name to get the client IP and use it instead of the 'localhost' IP. */ static void check_use_caller_name_ip(char *name) { if (op_ctx->caller_addr->ss_family == AF_INET) check_use_caller_name_ipv4(name); else check_use_caller_name_ipv6(name); } /** * @brief NSM notification * * @param[in] args * @param[in] req * @param[out] res */ int nlm4_Sm_Notify(nfs_arg_t *args, struct svc_req *req, nfs_res_t *res) { nlm4_sm_notifyargs *arg = &args->arg_nlm4_sm_notify; state_status_t state_status = STATE_SUCCESS; state_nsm_client_t *nsm_client; LogDebug(COMPONENT_NLM, "REQUEST PROCESSING: Calling nlm4_sm_notify for %s", arg->name); /* Check if the SM_NOTIFY request is from the 'localhost' */ check_use_caller_name_ip(arg->name); nsm_client = get_nsm_client(CARE_NOT, NULL, arg->name); if (nsm_client != NULL) { /* Cast the state number into a state pointer to protect * locks from a client that has rebooted from being released * by this SM_NOTIFY. */ state_status = state_nlm_notify(nsm_client, true, arg->state); if (state_status != STATE_SUCCESS) { /** @todo FSF: Deal with error */ } dec_nsm_client_ref(nsm_client); } LogDebug(COMPONENT_NLM, "REQUEST RESULT: nlm4_sm_notify DONE"); return NFS_REQ_OK; } /** * nlm4_Sm_Notify_Free: Frees the result structure allocated for nlm4_Sm_Notify * * Frees the result structure allocated for nlm4_Sm_Notify. Does Nothing in * fact. * * @param res [INOUT] Pointer to the result structure. * */ void nlm4_Sm_Notify_Free(nfs_res_t *res) { /* Nothing to do */ } nfs-ganesha-2.6.0/src/Protocols/NLM/nlm_Test.c000066400000000000000000000146001324272410200210410ustar00rootroot00000000000000/* * Copyright IBM Corporation, 2010 * Contributor: Aneesh Kumar K.v * * -------------------------- * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * */ #include "config.h" #include #include #include #include "log.h" #include "fsal.h" #include "nfs_proto_functions.h" #include "sal_functions.h" #include "nlm_util.h" #include "nlm_async.h" /** * @brief Test lock * * @param[in] args * @param[in] req * @param[out] res * */ int nlm4_Test(nfs_arg_t *args, struct svc_req *req, nfs_res_t *res) { nlm4_testargs *arg = &args->arg_nlm4_test; struct fsal_obj_handle *obj; state_status_t state_status = STATE_SUCCESS; char buffer[MAXNETOBJ_SZ * 2] = "\0"; state_nsm_client_t *nsm_client; state_nlm_client_t *nlm_client; state_owner_t *nlm_owner; state_owner_t *holder = NULL; fsal_lock_param_t lock, conflict; int rc; state_t *state; nlm4_testrply *test_stat = &res->res_nlm4test.test_stat; /* NLM doesn't have a BADHANDLE error, nor can rpc_execute deal with * responding to an NLM_*_MSG call, so we check here if the export is * NULL and if so, handle the response. */ if (op_ctx->ctx_export == NULL) { test_stat->stat = NLM4_STALE_FH; LogInfo(COMPONENT_NLM, "INVALID HANDLE: NLM4_TEST"); return NFS_REQ_OK; } netobj_to_string(&arg->cookie, buffer, 1024); LogDebug(COMPONENT_NLM, "REQUEST PROCESSING: Calling NLM4_TEST svid=%d off=%llx len=%llx cookie=%s", (int)arg->alock.svid, (unsigned long long)arg->alock.l_offset, (unsigned long long)arg->alock.l_len, buffer); copy_netobj(&res->res_nlm4test.cookie, &arg->cookie); if (nfs_in_grace()) { test_stat->stat = NLM4_DENIED_GRACE_PERIOD; LogDebug(COMPONENT_NLM, "REQUEST RESULT: NLM4_TEST %s", lock_result_str(res->res_nlm4.stat.stat)); return NFS_REQ_OK; } /** @todo FSF: * * TEST passes CARE_NO_MONITOR for care because we do need a non-NULL * owner, but we could expand the options to allow for a "free" owner * to be returned, that doesn't need to be in the hash table, so if * the owner isn't found in the Hash table, don't add it, just return * the "free" owner. */ rc = nlm_process_parameters(req, arg->exclusive, &arg->alock, &lock, &obj, CARE_OWNER, &nsm_client, &nlm_client, &nlm_owner, NULL, 0, &state); if (rc >= 0) { /* resent the error back to the client */ res->res_nlm4.stat.stat = (nlm4_stats) rc; LogDebug(COMPONENT_NLM, "REQUEST RESULT: nlm4_Unlock %s", lock_result_str(res->res_nlm4.stat.stat)); return NFS_REQ_OK; } state_status = state_test(obj, state, nlm_owner, &lock, &holder, &conflict); if (state_status != STATE_SUCCESS) { test_stat->stat = nlm_convert_state_error(state_status); if (state_status == STATE_LOCK_CONFLICT) { nlm_process_conflict(&test_stat->nlm4_testrply_u.holder, holder, &conflict); } } else { res->res_nlm4.stat.stat = NLM4_GRANTED; } LogFullDebug(COMPONENT_NLM, "Back from state_test"); /* Release state_t reference if we got one */ if (state != NULL) dec_nlm_state_ref(state); /* Release the NLM Client and NLM Owner references we have */ dec_nsm_client_ref(nsm_client); dec_nlm_client_ref(nlm_client); dec_state_owner_ref(nlm_owner); obj->obj_ops.put_ref(obj); LogDebug(COMPONENT_NLM, "REQUEST RESULT: NLM4_TEST %s", lock_result_str(res->res_nlm4.stat.stat)); return NFS_REQ_OK; } static void nlm4_test_message_resp(state_async_queue_t *arg) { state_nlm_async_data_t *nlm_arg = &arg->state_async_data.state_nlm_async_data; nfs_res_t *res = &nlm_arg->nlm_async_args.nlm_async_res; if (isFullDebug(COMPONENT_NLM)) { char buffer[1024] = "\0"; netobj_to_string(&res->res_nlm4test.cookie, buffer, 1024); LogFullDebug(COMPONENT_NLM, "Calling nlm_send_async cookie=%s status=%s", buffer, lock_result_str(res->res_nlm4test.test_stat.stat)); } nlm_send_async(NLMPROC4_TEST_RES, nlm_arg->nlm_async_host, res, NULL); nlm4_Test_Free(res); dec_nsm_client_ref(nlm_arg->nlm_async_host->slc_nsm_client); dec_nlm_client_ref(nlm_arg->nlm_async_host); gsh_free(arg); } /** * @brief Test lock Message * * @param[in] args * @param[in] req * @param[out] res * */ int nlm4_Test_Message(nfs_arg_t *args, struct svc_req *req, nfs_res_t *res) { state_nlm_client_t *nlm_client = NULL; state_nsm_client_t *nsm_client; nlm4_testargs *arg = &args->arg_nlm4_test; int rc = NFS_REQ_OK; LogDebug(COMPONENT_NLM, "REQUEST PROCESSING: Calling nlm_Test_Message"); nsm_client = get_nsm_client(CARE_NO_MONITOR, req->rq_xprt, arg->alock.caller_name); if (nsm_client != NULL) nlm_client = get_nlm_client(CARE_NO_MONITOR, req->rq_xprt, nsm_client, arg->alock.caller_name); if (nlm_client == NULL) rc = NFS_REQ_DROP; else rc = nlm4_Test(args, req, res); if (rc == NFS_REQ_OK) rc = nlm_send_async_res_nlm4test(nlm_client, nlm4_test_message_resp, res); if (rc == NFS_REQ_DROP) { if (nsm_client != NULL) dec_nsm_client_ref(nsm_client); if (nlm_client != NULL) dec_nlm_client_ref(nlm_client); LogCrit(COMPONENT_NLM, "Could not send async response for nlm_Test_Message"); } return NFS_REQ_DROP; } /** * nlm_Test_Free: Frees the result structure allocated for nlm4_Test * * Frees the result structure allocated for nlm_Null. Does Nothing in fact. * * @param res [INOUT] Pointer to the result structure. * */ void nlm4_Test_Free(nfs_res_t *res) { nlm4_testrply *test_stat = &res->res_nlm4test.test_stat; netobj_free(&res->res_nlm4test.cookie); if (test_stat->stat == NLM4_DENIED) netobj_free(&test_stat->nlm4_testrply_u.holder.oh); } nfs-ganesha-2.6.0/src/Protocols/NLM/nlm_Unlock.c000066400000000000000000000134771324272410200213700ustar00rootroot00000000000000/* * Copyright IBM Corporation, 2010 * Contributor: Aneesh Kumar K.v * * -------------------------- * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "config.h" #include #include #include #include "log.h" #include "fsal.h" #include "nfs_proto_functions.h" #include "sal_functions.h" #include "nlm_util.h" #include "nlm_async.h" /** * @brief Free a range lock * * @param[in] args * @param[in] req * @param[out] res */ int nlm4_Unlock(nfs_arg_t *args, struct svc_req *req, nfs_res_t *res) { nlm4_unlockargs *arg = &args->arg_nlm4_unlock; struct fsal_obj_handle *obj; state_status_t state_status = STATE_SUCCESS; char buffer[MAXNETOBJ_SZ * 2] = "\0"; state_nsm_client_t *nsm_client; state_nlm_client_t *nlm_client; state_owner_t *nlm_owner; fsal_lock_param_t lock; int rc; state_t *state; /* NLM doesn't have a BADHANDLE error, nor can rpc_execute deal with * responding to an NLM_*_MSG call, so we check here if the export is * NULL and if so, handle the response. */ if (op_ctx->ctx_export == NULL) { res->res_nlm4.stat.stat = NLM4_STALE_FH; LogInfo(COMPONENT_NLM, "INVALID HANDLE: NLM4_UNLOCK"); return NFS_REQ_OK; } netobj_to_string(&arg->cookie, buffer, sizeof(buffer)); LogDebug(COMPONENT_NLM, "REQUEST PROCESSING: Calling NLM4_UNLOCK svid=%d off=%llx len=%llx cookie=%s", (int)arg->alock.svid, (unsigned long long)arg->alock.l_offset, (unsigned long long)arg->alock.l_len, buffer); copy_netobj(&res->res_nlm4test.cookie, &arg->cookie); if (nfs_in_grace()) { res->res_nlm4.stat.stat = NLM4_DENIED_GRACE_PERIOD; LogDebug(COMPONENT_NLM, "REQUEST RESULT: NLM4_UNLOCK %s", lock_result_str(res->res_nlm4.stat.stat)); return NFS_REQ_OK; } /* unlock doesn't care if owner is found */ rc = nlm_process_parameters(req, false, &arg->alock, &lock, &obj, CARE_NOT, &nsm_client, &nlm_client, &nlm_owner, NULL, 0, &state); if (rc >= 0) { /* resent the error back to the client */ res->res_nlm4.stat.stat = (nlm4_stats) rc; LogDebug(COMPONENT_NLM, "REQUEST RESULT: NLM4_UNLOCK %s", lock_result_str(res->res_nlm4.stat.stat)); return NFS_REQ_OK; } if (state != NULL) state_status = state_unlock(obj, state, nlm_owner, false, 0, &lock); if (state_status != STATE_SUCCESS) { /* Unlock could fail in the FSAL and make a bit of a mess, * especially if we are in out of memory situation. Such an * error is logged by Cache Inode. */ res->res_nlm4test.test_stat.stat = nlm_convert_state_error(state_status); } else { res->res_nlm4.stat.stat = NLM4_GRANTED; } /* Release the NLM Client and NLM Owner references we have */ if (state != NULL) dec_state_t_ref(state); dec_nsm_client_ref(nsm_client); dec_nlm_client_ref(nlm_client); dec_state_owner_ref(nlm_owner); obj->obj_ops.put_ref(obj); LogDebug(COMPONENT_NLM, "REQUEST RESULT: NLM4_UNLOCK %s", lock_result_str(res->res_nlm4.stat.stat)); return NFS_REQ_OK; } static void nlm4_unlock_message_resp(state_async_queue_t *arg) { state_nlm_async_data_t *nlm_arg = &arg->state_async_data.state_nlm_async_data; nfs_res_t *res = &nlm_arg->nlm_async_args.nlm_async_res; if (isFullDebug(COMPONENT_NLM)) { char buffer[1024] = "\0"; netobj_to_string(&res->res_nlm4test.cookie, buffer, 1024); LogFullDebug(COMPONENT_NLM, "Calling nlm_send_async cookie=%s status=%s", buffer, lock_result_str(res->res_nlm4.stat.stat)); } nlm_send_async(NLMPROC4_UNLOCK_RES, nlm_arg->nlm_async_host, res, NULL); nlm4_Unlock_Free(res); dec_nsm_client_ref(nlm_arg->nlm_async_host->slc_nsm_client); dec_nlm_client_ref(nlm_arg->nlm_async_host); gsh_free(arg); } /** * @brief Unlock Message * * @param[in] args * @param[in] req * @param[out] res * */ int nlm4_Unlock_Message(nfs_arg_t *args, struct svc_req *req, nfs_res_t *res) { state_nlm_client_t *nlm_client = NULL; state_nsm_client_t *nsm_client; nlm4_unlockargs *arg = &args->arg_nlm4_unlock; int rc = NFS_REQ_OK; LogDebug(COMPONENT_NLM, "REQUEST PROCESSING: Calling nlm_Unlock_Message"); nsm_client = get_nsm_client(CARE_NO_MONITOR, req->rq_xprt, arg->alock.caller_name); if (nsm_client != NULL) nlm_client = get_nlm_client(CARE_NO_MONITOR, req->rq_xprt, nsm_client, arg->alock.caller_name); if (nlm_client == NULL) rc = NFS_REQ_DROP; else rc = nlm4_Unlock(args, req, res); if (rc == NFS_REQ_OK) rc = nlm_send_async_res_nlm4(nlm_client, nlm4_unlock_message_resp, res); if (rc == NFS_REQ_DROP) { if (nsm_client != NULL) dec_nsm_client_ref(nsm_client); if (nlm_client != NULL) dec_nlm_client_ref(nlm_client); LogCrit(COMPONENT_NLM, "Could not send async response for nlm_Unlock_Message"); } return NFS_REQ_DROP; } /** * nlm4_Unlock_Free: Frees the result structure allocated for nlm4_Unlock * * Frees the result structure allocated for nlm4_Lock. Does Nothing in fact. * * @param res [INOUT] Pointer to the result structure. * */ void nlm4_Unlock_Free(nfs_res_t *res) { netobj_free(&res->res_nlm4test.cookie); } nfs-ganesha-2.6.0/src/Protocols/NLM/nlm_Unshare.c000066400000000000000000000102071324272410200215260ustar00rootroot00000000000000/* * Copyright IBM Corporation, 2012 * Contributor: Frank Filz * * -------------------------- * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * */ #include "config.h" #include #include #include #include "log.h" #include "fsal.h" #include "nfs_proto_functions.h" #include "sal_functions.h" #include "nlm_util.h" #include "nlm_async.h" /** * @brief Set a share reservation * * @param[in] args * @param[in] req * @param[out] res * */ int nlm4_Unshare(nfs_arg_t *args, struct svc_req *req, nfs_res_t *res) { nlm4_shareargs *arg = &args->arg_nlm4_share; struct fsal_obj_handle *obj; state_status_t state_status = STATE_SUCCESS; char buffer[MAXNETOBJ_SZ * 2] = "\0"; state_nsm_client_t *nsm_client; state_nlm_client_t *nlm_client; state_owner_t *nlm_owner; state_t *nlm_state; int rc; bool grace = nfs_in_grace(); /* NLM doesn't have a BADHANDLE error, nor can rpc_execute deal with * responding to an NLM_*_MSG call, so we check here if the export is * NULL and if so, handle the response. */ if (op_ctx->ctx_export == NULL) { res->res_nlm4share.stat = NLM4_STALE_FH; LogInfo(COMPONENT_NLM, "INVALID HANDLE: NLM4_UNSHARE"); return NFS_REQ_OK; } res->res_nlm4share.sequence = 0; netobj_to_string(&arg->cookie, buffer, 1024); LogDebug(COMPONENT_NLM, "REQUEST PROCESSING: Calling NLM4_UNSHARE cookie=%s reclaim=%s", buffer, arg->reclaim ? "yes" : "no"); copy_netobj(&res->res_nlm4share.cookie, &arg->cookie); /* Allow only reclaim share request during recovery and visa versa. * Note: NLM_SHARE is indicated to be non-monitored, however, it does * have a reclaim flag, so we will honor the reclaim flag if used. * This is a little more bizare for UNSHARE, but, we'll still honor * the reclaim flag. */ if ((grace && !arg->reclaim) || (!grace && arg->reclaim)) { res->res_nlm4share.stat = NLM4_DENIED_GRACE_PERIOD; LogDebug(COMPONENT_NLM, "REQUEST RESULT: NLM4_UNSHARE %s", lock_result_str(res->res_nlm4share.stat)); return NFS_REQ_OK; } rc = nlm_process_share_parms(req, &arg->share, op_ctx->fsal_export, &obj, CARE_NOT, &nsm_client, &nlm_client, &nlm_owner, &nlm_state); if (rc >= 0) { /* Present the error back to the client */ res->res_nlm4share.stat = (nlm4_stats) rc; LogDebug(COMPONENT_NLM, "REQUEST RESULT: NLM4_UNSHARE %s", lock_result_str(res->res_nlm4share.stat)); return NFS_REQ_OK; } state_status = state_nlm_share(obj, arg->share.access, arg->share.mode, nlm_owner, nlm_state, false, true); if (state_status != STATE_SUCCESS) { res->res_nlm4share.stat = nlm_convert_state_error(state_status); } else { res->res_nlm4share.stat = NLM4_GRANTED; } /* Release the NLM Client and NLM Owner references we have */ dec_nsm_client_ref(nsm_client); dec_nlm_client_ref(nlm_client); dec_state_owner_ref(nlm_owner); obj->obj_ops.put_ref(obj); dec_nlm_state_ref(nlm_state); LogDebug(COMPONENT_NLM, "REQUEST RESULT: NLM4_UNSHARE %s", lock_result_str(res->res_nlm4share.stat)); return NFS_REQ_OK; } /** * nlm4_Unshare_Free: Frees the result structure allocated for nlm4_Lock * * Frees the result structure allocated for nlm4_Lock. Does Nothing in fact. * * @param res [INOUT] Pointer to the result structure. * */ void nlm4_Unshare_Free(nfs_res_t *res) { netobj_free(&res->res_nlm4share.cookie); } nfs-ganesha-2.6.0/src/Protocols/NLM/nlm_async.c000066400000000000000000000217541324272410200212470ustar00rootroot00000000000000/* * Copyright IBM Corporation, 2010 * Contributor: Aneesh Kumar K.V * * -------------------------- * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * */ #include "config.h" #include #include #include #include #include #include "nfs_core.h" #include "nfs_proto_functions.h" #include "sal_functions.h" #include "nlm_util.h" #include "nlm_async.h" pthread_mutex_t nlm_async_resp_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t nlm_async_resp_cond = PTHREAD_COND_INITIALIZER; int nlm_send_async_res_nlm4(state_nlm_client_t *host, state_async_func_t func, nfs_res_t *pres) { state_async_queue_t *arg = gsh_malloc(sizeof(*arg)); state_nlm_async_data_t *nlm_arg; state_status_t status; nlm_arg = &arg->state_async_data.state_nlm_async_data; memset(arg, 0, sizeof(*arg)); arg->state_async_func = func; nlm_arg->nlm_async_host = host; nlm_arg->nlm_async_args.nlm_async_res = *pres; copy_netobj(&nlm_arg->nlm_async_args.nlm_async_res.res_nlm4.cookie, &pres->res_nlm4.cookie); status = state_async_schedule(arg); if (status != STATE_SUCCESS) { gsh_free(arg); return NFS_REQ_DROP; } return NFS_REQ_OK; } int nlm_send_async_res_nlm4test(state_nlm_client_t *host, state_async_func_t func, nfs_res_t *pres) { state_async_queue_t *arg = gsh_malloc(sizeof(*arg)); state_nlm_async_data_t *nlm_arg; state_status_t status; nfs_res_t *res; nlm_arg = &arg->state_async_data.state_nlm_async_data; res = &nlm_arg->nlm_async_args.nlm_async_res; memset(arg, 0, sizeof(*arg)); arg->state_async_func = func; nlm_arg->nlm_async_host = host; *res = *pres; copy_netobj(&res->res_nlm4test.cookie, &pres->res_nlm4test.cookie); if (pres->res_nlm4test.test_stat.stat == NLM4_DENIED) { copy_netobj( &res->res_nlm4test.test_stat.nlm4_testrply_u.holder.oh, &pres->res_nlm4test.test_stat.nlm4_testrply_u.holder.oh); } status = state_async_schedule(arg); if (status != STATE_SUCCESS) { nlm4_Test_Free(res); gsh_free(arg); return NFS_REQ_DROP; } return NFS_REQ_OK; } xdrproc_t nlm_reply_proc[] = { [NLMPROC4_GRANTED_MSG] = (xdrproc_t) xdr_nlm4_testargs, [NLMPROC4_TEST_RES] = (xdrproc_t) xdr_nlm4_testres, [NLMPROC4_LOCK_RES] = (xdrproc_t) xdr_nlm4_res, [NLMPROC4_CANCEL_RES] = (xdrproc_t) xdr_nlm4_res, [NLMPROC4_UNLOCK_RES] = (xdrproc_t) xdr_nlm4_res, }; static void *resp_key; static const int MAX_ASYNC_RETRY = 2; static const struct timespec tout = { 0, 0 }; /* one-shot */ /* Client routine to send the asynchrnous response, * key is used to wait for a response */ int nlm_send_async(int proc, state_nlm_client_t *host, void *inarg, void *key) { struct clnt_req *cc; char *t; struct timeval start, now; struct timespec timeout; int retval, retry; char *caller_name = host->slc_nsm_client->ssc_nlm_caller_name; const char *client_type_str = xprt_type_to_str(host->slc_client_type); for (retry = 0; retry < MAX_ASYNC_RETRY; retry++) { if (host->slc_callback_clnt == NULL) { LogFullDebug(COMPONENT_NLM, "clnt_ncreate %s", caller_name); if (host->slc_client_type == XPRT_TCP) { int fd; struct sockaddr_in6 server_addr; struct netbuf *buf, local_buf; struct addrinfo *result; struct addrinfo hints; char port_str[20]; fd = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP); if (fd < 0) return -1; memcpy(&server_addr, &(host->slc_server_addr), sizeof(struct sockaddr_in6)); server_addr.sin6_port = 0; if (bind(fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { LogMajor(COMPONENT_NLM, "Cannot bind"); close(fd); return -1; } buf = rpcb_find_mapped_addr( (char *) client_type_str, NLMPROG, NLM4_VERS, caller_name); /* handle error here, for example, * client side blocking rpc call */ if (buf == NULL) { LogMajor(COMPONENT_NLM, "Cannot create NLM async %s connection to client %s", client_type_str, caller_name); close(fd); return -1; } memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_INET6; /* only INET6 */ hints.ai_socktype = SOCK_STREAM; /* TCP */ hints.ai_protocol = 0; /* Any protocol */ hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; /* convert port to string format */ sprintf(port_str, "%d", htons(((struct sockaddr_in *) buf->buf)->sin_port)); /* buf with inet is only needed for the port */ gsh_free(buf->buf); gsh_free(buf); /* get the IPv4 mapped IPv6 address */ retval = getaddrinfo(caller_name, port_str, &hints, &result); /* retry for spurious EAI_NONAME errors */ if (retval == EAI_NONAME || retval == EAI_AGAIN) { LogEvent(COMPONENT_NLM, "failed to resolve %s to an address: %s", caller_name, gai_strerror(retval)); /* getaddrinfo() failed, retry */ retval = RPC_UNKNOWNADDR; usleep(1000); continue; } else if (retval != 0) { LogMajor(COMPONENT_NLM, "failed to resolve %s to an address: %s", caller_name, gai_strerror(retval)); return -1; } /* setup the netbuf with in6 address */ local_buf.buf = result->ai_addr; local_buf.len = local_buf.maxlen = result->ai_addrlen; host->slc_callback_clnt = clnt_vc_ncreate(fd, &local_buf, NLMPROG, NLM4_VERS, 0, 0); freeaddrinfo(result); } else { host->slc_callback_clnt = clnt_ncreate(caller_name, NLMPROG, NLM4_VERS, (char *) client_type_str); } if (CLNT_FAILURE(host->slc_callback_clnt)) { char *err = rpc_sperror( &host->slc_callback_clnt->cl_error, "failed"); LogMajor(COMPONENT_NLM, "Create NLM async %s connection to client %s %s", client_type_str, caller_name, err); gsh_free(err); CLNT_DESTROY(host->slc_callback_clnt); host->slc_callback_clnt = NULL; return -1; } /* split auth (for authnone, idempotent) */ host->slc_callback_auth = authnone_ncreate(); } PTHREAD_MUTEX_lock(&nlm_async_resp_mutex); resp_key = key; PTHREAD_MUTEX_unlock(&nlm_async_resp_mutex); LogFullDebug(COMPONENT_NLM, "About to make clnt_call"); cc = gsh_malloc(sizeof(*cc)); clnt_req_fill(cc, host->slc_callback_clnt, host->slc_callback_auth, proc, (xdrproc_t) nlm_reply_proc[proc], inarg, (xdrproc_t) xdr_void, NULL); retval = clnt_req_setup(cc, tout); if (retval == RPC_SUCCESS) { cc->cc_refreshes = 0; retval = CLNT_CALL_ONCE(cc); } LogFullDebug(COMPONENT_NLM, "Done with clnt_call"); if (retval == RPC_TIMEDOUT || retval == RPC_SUCCESS) { retval = RPC_SUCCESS; clnt_req_release(cc); break; } t = rpc_sperror(&cc->cc_error, "failed"); LogCrit(COMPONENT_NLM, "NLM async Client procedure call %d %s", proc, t); gsh_free(t); clnt_req_release(cc); CLNT_DESTROY(host->slc_callback_clnt); host->slc_callback_clnt = NULL; } if (retry == MAX_ASYNC_RETRY) { LogMajor(COMPONENT_NLM, "NLM async Client exceeded retry count %d", MAX_ASYNC_RETRY); PTHREAD_MUTEX_lock(&nlm_async_resp_mutex); resp_key = NULL; PTHREAD_MUTEX_unlock(&nlm_async_resp_mutex); return retval; } PTHREAD_MUTEX_lock(&nlm_async_resp_mutex); if (resp_key != NULL) { /* Wait for 5 seconds or a signal */ gettimeofday(&start, NULL); gettimeofday(&now, NULL); timeout.tv_sec = 5 + start.tv_sec; timeout.tv_nsec = 0; LogFullDebug(COMPONENT_NLM, "About to wait for signal for key %p", resp_key); while (resp_key != NULL && now.tv_sec < (start.tv_sec + 5)) { int rc; rc = pthread_cond_timedwait(&nlm_async_resp_cond, &nlm_async_resp_mutex, &timeout); LogFullDebug(COMPONENT_NLM, "pthread_cond_timedwait returned %d", rc); gettimeofday(&now, NULL); } LogFullDebug(COMPONENT_NLM, "Done waiting"); } PTHREAD_MUTEX_unlock(&nlm_async_resp_mutex); return retval; } void nlm_signal_async_resp(void *key) { PTHREAD_MUTEX_lock(&nlm_async_resp_mutex); if (resp_key == key) { resp_key = NULL; pthread_cond_signal(&nlm_async_resp_cond); LogFullDebug(COMPONENT_NLM, "Signaled condition variable"); } else { LogFullDebug(COMPONENT_NLM, "Didn't signal condition variable"); } PTHREAD_MUTEX_unlock(&nlm_async_resp_mutex); } nfs-ganesha-2.6.0/src/Protocols/NLM/nlm_util.c000066400000000000000000000415061324272410200211040ustar00rootroot00000000000000/* * Copyright IBM Corporation, 2010 * Contributor: Aneesh Kumar K.v * * * -------------------------- * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * */ #include "config.h" #include #include #include #include "log.h" #include "fsal.h" #include "sal_functions.h" #include "nfs_proto_tools.h" #include "nlm_util.h" #include "nsm.h" #include "nlm_async.h" #include "nfs_core.h" #include "export_mgr.h" /* nlm grace time tracking */ static struct timeval nlm_grace_tv; /* We manage our own cookie for GRANTED call backs * Cookie */ struct granted_cookie { unsigned long gc_seconds; unsigned long gc_microseconds; unsigned long gc_cookie; }; struct granted_cookie granted_cookie; pthread_mutex_t granted_mutex = PTHREAD_MUTEX_INITIALIZER; void next_granted_cookie(struct granted_cookie *cookie) { PTHREAD_MUTEX_lock(&granted_mutex); granted_cookie.gc_cookie++; *cookie = granted_cookie; PTHREAD_MUTEX_unlock(&granted_mutex); } const char *lock_result_str(int rc) { switch (rc) { case NLM4_GRANTED: return "NLM4_GRANTED"; case NLM4_DENIED: return "NLM4_DENIED"; case NLM4_DENIED_NOLOCKS: return "NLM4_DENIED_NOLOCKS"; case NLM4_BLOCKED: return "NLM4_BLOCKED"; case NLM4_DENIED_GRACE_PERIOD: return "NLM4_DENIED_GRACE_PERIOD"; case NLM4_DEADLCK: return "NLM4_DEADLCK"; case NLM4_ROFS: return "NLM4_ROFS"; case NLM4_STALE_FH: return "NLM4_STALE_FH"; case NLM4_FBIG: return "NLM4_FBIG"; case NLM4_FAILED: return "NLM4_FAILED"; default: return "Unknown"; } } inline uint64_t lock_end(uint64_t start, uint64_t len) { if (len == 0) return UINT64_MAX; else return start + len - 1; } void fill_netobj(netobj *dst, char *data, int len) { dst->n_len = 0; dst->n_bytes = NULL; if (len != 0) { dst->n_bytes = gsh_malloc(len); dst->n_len = len; memcpy(dst->n_bytes, data, len); } } void copy_netobj(netobj *dst, netobj *src) { if (src->n_len != 0) { dst->n_bytes = gsh_malloc(src->n_len); memcpy(dst->n_bytes, src->n_bytes, src->n_len); } else dst->n_bytes = NULL; dst->n_len = src->n_len; } void netobj_free(netobj *obj) { gsh_free(obj->n_bytes); } void netobj_to_string(netobj *obj, char *buffer, int maxlen) { int len = obj->n_len; struct display_buffer dspbuf = {maxlen, buffer, buffer}; display_opaque_value(&dspbuf, obj->n_bytes, len); } void nlm_init(void) { /* start NLM grace period */ gettimeofday(&nlm_grace_tv, NULL); /* also use this time to initialize granted_cookie */ granted_cookie.gc_seconds = (unsigned long)nlm_grace_tv.tv_sec; granted_cookie.gc_microseconds = (unsigned long)nlm_grace_tv.tv_usec; granted_cookie.gc_cookie = 0; } void free_grant_arg(state_async_queue_t *arg) { state_nlm_async_data_t *nlm_arg = &arg->state_async_data.state_nlm_async_data; netobj_free(&nlm_arg->nlm_async_args.nlm_async_grant.cookie); netobj_free(&nlm_arg->nlm_async_args.nlm_async_grant.alock.oh); netobj_free(&nlm_arg->nlm_async_args.nlm_async_grant.alock.fh); gsh_free(nlm_arg->nlm_async_args.nlm_async_grant.alock.caller_name); gsh_free(arg); } /** * * 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] = "\0"; state_status_t state_status = STATE_SUCCESS; state_cookie_entry_t *cookie_entry; state_nlm_async_data_t *nlm_arg = &arg->state_async_data.state_nlm_async_data; struct root_op_context root_op_context; struct gsh_export *export; nlm4_testargs *nlm_async_grant; nlm_async_grant = &nlm_arg->nlm_async_args.nlm_async_grant; if (isDebug(COMPONENT_NLM)) { netobj_to_string(&nlm_async_grant->cookie, buffer, sizeof(buffer)); LogDebug(COMPONENT_NLM, "Sending GRANTED for arg=%p svid=%d start=%" PRIx64 " len=%" PRIx64 " cookie=%s", arg, nlm_async_grant->alock.svid, nlm_async_grant->alock.l_offset, nlm_async_grant->alock.l_len, buffer); } retval = nlm_send_async(NLMPROC4_GRANTED_MSG, nlm_arg->nlm_async_host, nlm_async_grant, nlm_arg->nlm_async_key); dec_nlm_client_ref(nlm_arg->nlm_async_host); /* If success, we are done. */ if (retval == RPC_SUCCESS) goto out; /* * 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); state_status = state_find_grant( nlm_async_grant->cookie.n_bytes, nlm_async_grant->cookie.n_len, &cookie_entry); if (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)); goto out; } if (cookie_entry->sce_lock_entry->sle_block_data == NULL) { /* Wow, we're not doing well... */ LogFullDebug(COMPONENT_NLM, "Could not find block data for cookie=%s (must be an old NLM_GRANTED_RES)", buffer); goto out; } /* Initialize a context, it is ok if the export is stale because * we must clean up the cookie_entry. */ export = cookie_entry->sce_lock_entry->sle_export; get_gsh_export_ref(export); init_root_op_context(&root_op_context, export, export->fsal_export, NFS_V3, 0, NFS_REQUEST); state_status = state_release_grant(cookie_entry); release_root_op_context(); put_gsh_export(export); if (state_status != STATE_SUCCESS) { /* Huh? */ LogFullDebug(COMPONENT_NLM, "Could not release cookie=%s status=%s", buffer, state_err_str(state_status)); } out: free_grant_arg(arg); } int nlm_process_parameters(struct svc_req *req, bool exclusive, nlm4_lock *alock, fsal_lock_param_t *plock, struct fsal_obj_handle **ppobj, care_t care, state_nsm_client_t **ppnsm_client, state_nlm_client_t **ppnlm_client, state_owner_t **ppowner, state_block_data_t **block_data, int32_t nsm_state, state_t **state) { nfsstat3 nfsstat3; SVCXPRT *ptr_svc = req->rq_xprt; int rc; *ppnsm_client = NULL; *ppnlm_client = NULL; *ppowner = NULL; if (state != NULL) *state = NULL; /* Convert file handle into a fsal object */ *ppobj = nfs3_FhandleToCache((struct nfs_fh3 *)&alock->fh, &nfsstat3, &rc); if (*ppobj == 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). */ 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"); /* 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 (state != NULL) { rc = get_nlm_state(STATE_TYPE_NLM_LOCK, *ppobj, *ppowner, care, nsm_state, state); if (rc > 0) { LogDebug(COMPONENT_NLM, "Could not get NLM State"); goto out_put; } } if (block_data != NULL) { state_block_data_t *bdat = gsh_calloc(1, sizeof(*bdat)); *block_data = bdat; /* Fill in the block data. */ bdat->sbd_granted_callback = nlm_granted_callback; bdat->sbd_prot.sbd_nlm.sbd_nlm_fh.n_bytes = bdat->sbd_prot.sbd_nlm.sbd_nlm_fh_buf; bdat->sbd_prot.sbd_nlm.sbd_nlm_fh.n_len = alock->fh.n_len; memcpy(bdat->sbd_prot.sbd_nlm.sbd_nlm_fh_buf, alock->fh.n_bytes, alock->fh.n_len); } /* Fill in plock (caller will reset reclaim if appropriate) */ plock->lock_sle_type = FSAL_POSIX_LOCK; plock->lock_reclaim = false; 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: (*ppobj)->obj_ops.put_ref((*ppobj)); if (*ppnsm_client != NULL) { dec_nsm_client_ref(*ppnsm_client); *ppnsm_client = NULL; } if (*ppnlm_client != NULL) { dec_nlm_client_ref(*ppnlm_client); *ppnlm_client = NULL; } if (*ppowner != NULL) { dec_state_owner_ref(*ppowner); *ppowner = NULL; } *ppobj = NULL; return rc; } int nlm_process_share_parms(struct svc_req *req, nlm4_share *share, struct fsal_export *exp_hdl, struct fsal_obj_handle **ppobj, care_t care, state_nsm_client_t **ppnsm_client, state_nlm_client_t **ppnlm_client, state_owner_t **ppowner, state_t **state) { nfsstat3 nfsstat3; SVCXPRT *ptr_svc = req->rq_xprt; int rc; *ppnsm_client = NULL; *ppnlm_client = NULL; *ppowner = NULL; /* Convert file handle into a fsal object */ *ppobj = nfs3_FhandleToCache((struct nfs_fh3 *)&share->fh, &nfsstat3, &rc); if (*ppobj == 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). */ 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"); /* 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 (state != NULL) { rc = get_nlm_state(STATE_TYPE_NLM_SHARE, *ppobj, *ppowner, care, 0, state); if (rc > 0 || !(*state)) { LogDebug(COMPONENT_NLM, "Could not get NLM State"); goto out_put; } } LogFullDebug(COMPONENT_NLM, "Parameters Processed"); /* Return non NLM error code '-1' on success. */ return -1; out_put: if (*ppnsm_client != NULL) { dec_nsm_client_ref(*ppnsm_client); *ppnsm_client = NULL; } if (*ppnlm_client != NULL) { dec_nlm_client_ref(*ppnlm_client); *ppnlm_client = NULL; } if (*ppowner != NULL) { dec_state_owner_ref(*ppowner); *ppowner = NULL; } (*ppobj)->obj_ops.put_ref((*ppobj)); *ppobj = NULL; return rc; } void nlm_process_conflict(nlm4_holder *nlm_holder, state_owner_t *holder, fsal_lock_param_t *conflict) { if (conflict != NULL) { nlm_holder->exclusive = conflict->lock_type == FSAL_LOCK_W; nlm_holder->l_offset = conflict->lock_start; nlm_holder->l_len = conflict->lock_length; } else { /* For some reason, don't have an actual conflict, * just make it exclusive over the whole file * (which would conflict with any lock requested). */ nlm_holder->exclusive = true; nlm_holder->l_offset = 0; nlm_holder->l_len = 0; } if (holder != NULL) { if (holder->so_type == STATE_LOCK_OWNER_NLM) nlm_holder->svid = holder->so_owner.so_nlm_owner.so_nlm_svid; else nlm_holder->svid = 0; fill_netobj(&nlm_holder->oh, holder->so_owner_val, holder->so_owner_len); } else { /* If we don't have an NLM owner, not much we can do. */ nlm_holder->svid = 0; fill_netobj(&nlm_holder->oh, unknown_owner.so_owner_val, unknown_owner.so_owner_len); } /* Release any lock owner reference passed back from SAL */ if (holder != NULL) dec_state_owner_ref(holder); } nlm4_stats nlm_convert_state_error(state_status_t status) { switch (status) { case STATE_SUCCESS: return NLM4_GRANTED; case STATE_LOCK_CONFLICT: return NLM4_DENIED; case STATE_SHARE_DENIED: return NLM4_DENIED; case STATE_MALLOC_ERROR: return NLM4_DENIED_NOLOCKS; case STATE_LOCK_BLOCKED: return NLM4_BLOCKED; case STATE_GRACE_PERIOD: return NLM4_DENIED_GRACE_PERIOD; case STATE_LOCK_DEADLOCK: return NLM4_DEADLCK; case STATE_READ_ONLY_FS: return NLM4_ROFS; case STATE_NOT_FOUND: return NLM4_STALE_FH; case STATE_ESTALE: return NLM4_STALE_FH; case STATE_FILE_BIG: case STATE_BAD_RANGE: return NLM4_FBIG; default: return NLM4_FAILED; } } state_status_t nlm_granted_callback(struct fsal_obj_handle *obj, state_lock_entry_t *lock_entry) { state_block_data_t *block_data = lock_entry->sle_block_data; state_nlm_block_data_t *nlm_block_data = &block_data->sbd_prot.sbd_nlm; state_cookie_entry_t *cookie_entry = NULL; state_async_queue_t *arg; nlm4_testargs *inarg; state_nlm_async_data_t *nlm_async_data; state_nlm_owner_t *nlm_grant_owner = &lock_entry->sle_owner->so_owner.so_nlm_owner; state_nlm_client_t *nlm_grant_client = nlm_grant_owner->so_client; struct granted_cookie nlm_grant_cookie; state_status_t state_status; state_status_t state_status_int; arg = gsh_calloc(1, sizeof(*arg)); nlm_async_data = &arg->state_async_data.state_nlm_async_data; /* Get a cookie to use for this grant */ next_granted_cookie(&nlm_grant_cookie); /* Add a cookie to the blocked lock pending grant. * It will also request lock from FSAL. * Could return STATE_LOCK_BLOCKED because FSAL would have had to block. */ state_status = state_add_grant_cookie(obj, &nlm_grant_cookie, sizeof(nlm_grant_cookie), lock_entry, &cookie_entry); if (state_status != STATE_SUCCESS) { free_grant_arg(arg); return state_status; } /* Fill in the arguments for the NLMPROC4_GRANTED_MSG call */ inc_nlm_client_ref(nlm_grant_client); arg->state_async_func = nlm4_send_grant_msg; nlm_async_data->nlm_async_host = nlm_grant_client; nlm_async_data->nlm_async_key = cookie_entry; inarg = &nlm_async_data->nlm_async_args.nlm_async_grant; copy_netobj(&inarg->alock.fh, &nlm_block_data->sbd_nlm_fh); fill_netobj(&inarg->alock.oh, lock_entry->sle_owner->so_owner_val, lock_entry->sle_owner->so_owner_len); fill_netobj(&inarg->cookie, (char *)&nlm_grant_cookie, sizeof(nlm_grant_cookie)); inarg->alock.caller_name = gsh_strdup(nlm_grant_client->slc_nlm_caller_name); inarg->exclusive = lock_entry->sle_lock.lock_type == FSAL_LOCK_W; inarg->alock.svid = nlm_grant_owner->so_nlm_svid; inarg->alock.l_offset = lock_entry->sle_lock.lock_start; inarg->alock.l_len = lock_entry->sle_lock.lock_length; if (isDebug(COMPONENT_NLM)) { char buffer[1024] = "\0"; netobj_to_string(&inarg->cookie, buffer, sizeof(buffer)); LogDebug(COMPONENT_NLM, "Sending GRANTED for arg=%p svid=%d start=%llx len=%llx cookie=%s", arg, inarg->alock.svid, (unsigned long long)inarg->alock.l_offset, (unsigned long long)inarg->alock.l_len, buffer); } /* Now try to schedule NLMPROC4_GRANTED_MSG call */ state_status = state_async_schedule(arg); if (state_status != STATE_SUCCESS) goto grant_fail; return state_status; grant_fail: /* Something went wrong after we added a grant cookie, * need to clean up */ dec_nlm_client_ref(nlm_grant_client); /* Clean up NLMPROC4_GRANTED_MSG arguments */ free_grant_arg(arg); /* Cancel the pending grant to release the cookie */ state_status_int = state_cancel_grant(cookie_entry); if (state_status_int != STATE_SUCCESS) { /* Not much we can do other than log that something * bad happened. */ LogCrit(COMPONENT_NLM, "Unable to clean up GRANTED lock after error"); } return state_status; } nfs-ganesha-2.6.0/src/Protocols/NLM/nsm.c000066400000000000000000000157251324272410200200620ustar00rootroot00000000000000/* * Copyright IBM Corporation, 2010 * Contributor: Aneesh Kumar K.v * : M. Mohan Kumar * * -------------------------- * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * */ #include "config.h" #include #include "abstract_atomic.h" #include "gsh_rpc.h" #include "nsm.h" #include "sal_data.h" pthread_mutex_t nsm_mutex = PTHREAD_MUTEX_INITIALIZER; CLIENT *nsm_clnt; AUTH *nsm_auth; unsigned long nsm_count; char *nodename; /* retry timeout default to the moon and back */ static const struct timespec tout = { 3, 0 }; bool nsm_connect(void) { struct utsname utsname; if (nsm_clnt != NULL) return true; if (uname(&utsname) == -1) { LogCrit(COMPONENT_NLM, "uname failed with errno %d (%s)", errno, strerror(errno)); return false; } nodename = gsh_strdup(utsname.nodename); nsm_clnt = clnt_ncreate("localhost", SM_PROG, SM_VERS, "tcp"); if (CLNT_FAILURE(nsm_clnt)) { char *err = rpc_sperror(&nsm_clnt->cl_error, "failed"); LogCrit(COMPONENT_NLM, "connect to statd %s", err); gsh_free(err); CLNT_DESTROY(nsm_clnt); nsm_clnt = NULL; gsh_free(nodename); nodename = NULL; } /* split auth (for authnone, idempotent) */ nsm_auth = authnone_ncreate(); return nsm_clnt != NULL; } void nsm_disconnect(void) { if (nsm_count == 0 && nsm_clnt != NULL) { CLNT_DESTROY(nsm_clnt); nsm_clnt = NULL; AUTH_DESTROY(nsm_auth); nsm_auth = NULL; gsh_free(nodename); nodename = NULL; } } bool nsm_monitor(state_nsm_client_t *host) { struct clnt_req *cc; char *t; struct mon nsm_mon; struct sm_stat_res res; enum clnt_stat ret; if (host == NULL) return true; PTHREAD_MUTEX_lock(&host->ssc_mutex); if (atomic_fetch_int32_t(&host->ssc_monitored)) { PTHREAD_MUTEX_unlock(&host->ssc_mutex); return true; } memset(&nsm_mon, 0, sizeof(nsm_mon)); nsm_mon.mon_id.mon_name = host->ssc_nlm_caller_name; nsm_mon.mon_id.my_id.my_prog = NLMPROG; nsm_mon.mon_id.my_id.my_vers = NLM4_VERS; nsm_mon.mon_id.my_id.my_proc = NLMPROC4_SM_NOTIFY; /* nothing to put in the private data */ LogDebug(COMPONENT_NLM, "Monitor %s", host->ssc_nlm_caller_name); PTHREAD_MUTEX_lock(&nsm_mutex); /* create a connection to nsm on the localhost */ if (!nsm_connect()) { LogCrit(COMPONENT_NLM, "Monitor %s nsm_connect failed", nsm_mon.mon_id.mon_name); PTHREAD_MUTEX_unlock(&nsm_mutex); PTHREAD_MUTEX_unlock(&host->ssc_mutex); return false; } /* Set this after we call nsm_connect() */ nsm_mon.mon_id.my_id.my_name = nodename; cc = gsh_malloc(sizeof(*cc)); clnt_req_fill(cc, nsm_clnt, nsm_auth, SM_MON, (xdrproc_t) xdr_mon, &nsm_mon, (xdrproc_t) xdr_sm_stat_res, &res); ret = clnt_req_setup(cc, tout); if (ret == RPC_SUCCESS) { ret = CLNT_CALL_WAIT(cc); } if (ret != RPC_SUCCESS) { t = rpc_sperror(&cc->cc_error, "failed"); LogCrit(COMPONENT_NLM, "Monitor %s SM_MON %s", nsm_mon.mon_id.mon_name, t); gsh_free(t); clnt_req_release(cc); nsm_disconnect(); PTHREAD_MUTEX_unlock(&nsm_mutex); PTHREAD_MUTEX_unlock(&host->ssc_mutex); return false; } clnt_req_release(cc); if (res.res_stat != STAT_SUCC) { LogCrit(COMPONENT_NLM, "Monitor %s SM_MON failed (%d)", nsm_mon.mon_id.mon_name, res.res_stat); nsm_disconnect(); PTHREAD_MUTEX_unlock(&nsm_mutex); PTHREAD_MUTEX_unlock(&host->ssc_mutex); return false; } nsm_count++; atomic_store_int32_t(&host->ssc_monitored, true); LogDebug(COMPONENT_NLM, "Monitored %s for nodename %s", nsm_mon.mon_id.mon_name, nodename); PTHREAD_MUTEX_unlock(&nsm_mutex); PTHREAD_MUTEX_unlock(&host->ssc_mutex); return true; } bool nsm_unmonitor(state_nsm_client_t *host) { struct clnt_req *cc; char *t; struct sm_stat res; struct mon_id nsm_mon_id; enum clnt_stat ret; if (host == NULL) return true; PTHREAD_MUTEX_lock(&host->ssc_mutex); if (!atomic_fetch_int32_t(&host->ssc_monitored)) { PTHREAD_MUTEX_unlock(&host->ssc_mutex); return true; } nsm_mon_id.mon_name = host->ssc_nlm_caller_name; nsm_mon_id.my_id.my_prog = NLMPROG; nsm_mon_id.my_id.my_vers = NLM4_VERS; nsm_mon_id.my_id.my_proc = NLMPROC4_SM_NOTIFY; PTHREAD_MUTEX_lock(&nsm_mutex); /* create a connection to nsm on the localhost */ if (!nsm_connect()) { LogCrit(COMPONENT_NLM, "Unmonitor %s nsm_connect failed", nsm_mon_id.mon_name); PTHREAD_MUTEX_unlock(&nsm_mutex); PTHREAD_MUTEX_unlock(&host->ssc_mutex); return false; } /* Set this after we call nsm_connect() */ nsm_mon_id.my_id.my_name = nodename; cc = gsh_malloc(sizeof(*cc)); clnt_req_fill(cc, nsm_clnt, nsm_auth, SM_UNMON, (xdrproc_t) xdr_mon_id, &nsm_mon_id, (xdrproc_t) xdr_sm_stat, &res); ret = clnt_req_setup(cc, tout); if (ret == RPC_SUCCESS) { ret = CLNT_CALL_WAIT(cc); } if (ret != RPC_SUCCESS) { t = rpc_sperror(&cc->cc_error, "failed"); LogCrit(COMPONENT_NLM, "Unmonitor %s SM_MON %s", nsm_mon_id.mon_name, t); gsh_free(t); clnt_req_release(cc); nsm_disconnect(); PTHREAD_MUTEX_unlock(&nsm_mutex); PTHREAD_MUTEX_unlock(&host->ssc_mutex); return false; } clnt_req_release(cc); atomic_store_int32_t(&host->ssc_monitored, false); nsm_count--; LogDebug(COMPONENT_NLM, "Unmonitored %s for nodename %s", nsm_mon_id.mon_name, nodename); nsm_disconnect(); PTHREAD_MUTEX_unlock(&nsm_mutex); PTHREAD_MUTEX_unlock(&host->ssc_mutex); return true; } void nsm_unmonitor_all(void) { struct clnt_req *cc; char *t; struct sm_stat res; struct my_id nsm_id; enum clnt_stat ret; nsm_id.my_prog = NLMPROG; nsm_id.my_vers = NLM4_VERS; nsm_id.my_proc = NLMPROC4_SM_NOTIFY; PTHREAD_MUTEX_lock(&nsm_mutex); /* create a connection to nsm on the localhost */ if (!nsm_connect()) { LogCrit(COMPONENT_NLM, "Unmonitor all nsm_connect failed"); PTHREAD_MUTEX_unlock(&nsm_mutex); return; } /* Set this after we call nsm_connect() */ nsm_id.my_name = nodename; cc = gsh_malloc(sizeof(*cc)); clnt_req_fill(cc, nsm_clnt, nsm_auth, SM_UNMON_ALL, (xdrproc_t) xdr_my_id, &nsm_id, (xdrproc_t) xdr_sm_stat, &res); ret = clnt_req_setup(cc, tout); if (ret == RPC_SUCCESS) { ret = CLNT_CALL_WAIT(cc); } if (ret != RPC_SUCCESS) { t = rpc_sperror(&cc->cc_error, "failed"); LogCrit(COMPONENT_NLM, "Unmonitor all %s", t); gsh_free(t); } clnt_req_release(cc); nsm_disconnect(); PTHREAD_MUTEX_unlock(&nsm_mutex); } nfs-ganesha-2.6.0/src/Protocols/NLM/sm_notify.c000066400000000000000000000073751324272410200212760ustar00rootroot00000000000000#include /* for memset */ #include #include #include #include #include #include #include #include #include #include "nsm.h" #define STR_SIZE 100 #define USAGE "usage: %s [-p ] -l " \ "-m -r -s \n" #define ERR_MSG1 "%s address too long\n" /* attempt to match (irrational) behaviour of previous versions */ static const struct timespec tout = { 15, 0 }; /* This function is dragged in by the use of abstract_mem.h, so * we define a simple version that does a printf rather than * pull in the entirety of log_functions.c into this standalone * program. */ void LogMallocFailure(const char *file, int line, const char *function, const char *allocator) { printf("Aborting %s due to out of memory", allocator); } void * nsm_notify_1(notify *argp, CLIENT *clnt) { static char clnt_res; struct clnt_req *cc; enum clnt_stat ret; memset((char *)&clnt_res, 0, sizeof(clnt_res)); cc = gsh_malloc(sizeof(*cc)); clnt_req_fill(cc, clnt, authnone_ncreate(), SM_NOTIFY, (xdrproc_t) xdr_notify, argp, (xdrproc_t) xdr_void, &clnt_res); ret = clnt_req_setup(cc, tout); if (ret == RPC_SUCCESS) { cc->cc_refreshes = 1; ret = CLNT_CALL_WAIT(cc); } clnt_req_release(cc); if (ret != RPC_SUCCESS) return NULL; return (void *)&clnt_res; } int main(int argc, char **argv) { int c; int port = 0; int state = 0, sflag = 0; char mon_client[STR_SIZE], mflag = 0; char remote_addr_s[STR_SIZE], rflag = 0; char local_addr_s[STR_SIZE], lflag = 0; notify arg; CLIENT *clnt; struct netbuf *buf; char port_str[20]; struct sockaddr_in local_addr; int fd; while ((c = getopt(argc, argv, "p:r:m:l:s:")) != EOF) switch (c) { case 'p': port = atoi(optarg); break; case 's': state = atoi(optarg); sflag = 1; break; case 'm': if (strlen(optarg) >= STR_SIZE) { fprintf(stderr, ERR_MSG1, "monitor host"); exit(1); } strcpy(mon_client, optarg); mflag = 1; break; case 'r': if (strlen(optarg) >= STR_SIZE) { fprintf(stderr, ERR_MSG1, "remote address"); exit(1); } strcpy(remote_addr_s, optarg); rflag = 1; break; case 'l': if (strlen(optarg) >= STR_SIZE) { fprintf(stderr, ERR_MSG1, "local address"); exit(1); } strcpy(local_addr_s, optarg); lflag = 1; break; case '?': default: fprintf(stderr, USAGE, argv[0]); exit(1); break; } if ((sflag + lflag + mflag + rflag) != 4) { fprintf(stderr, USAGE, argv[0]); exit(1); } /* create a udp socket */ fd = socket(PF_INET, SOCK_DGRAM|SOCK_NONBLOCK, IPPROTO_UDP); if (fd < 0) { fprintf(stderr, "socket call failed. errno=%d\n", errno); exit(1); } /* set up the sockaddr for local endpoint */ memset(&local_addr, 0, sizeof(struct sockaddr_in)); local_addr.sin_family = PF_INET; local_addr.sin_port = htons(port); local_addr.sin_addr.s_addr = inet_addr(local_addr_s); if (bind(fd, (struct sockaddr *)&local_addr, sizeof(struct sockaddr)) < 0) { fprintf(stderr, "bind call failed. errno=%d\n", errno); exit(1); } /* find the port for SM service of the remote server */ buf = rpcb_find_mapped_addr( "udp", SM_PROG, SM_VERS, remote_addr_s); /* handle error here, for example, * client side blocking rpc call */ if (buf == NULL) { close(fd); exit(1); } /* convert port to string format */ sprintf(port_str, "%d", htons(((struct sockaddr_in *) buf->buf)->sin_port)); clnt = clnt_dg_ncreate(fd, buf, SM_PROG, SM_VERS, 0, 0); arg.my_name = mon_client; arg.state = state; nsm_notify_1(&arg, clnt); /* free resources */ gsh_free(buf->buf); gsh_free(buf); CLNT_DESTROY(clnt); close(fd); return 0; } nfs-ganesha-2.6.0/src/Protocols/RQUOTA/000077500000000000000000000000001324272410200174745ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/Protocols/RQUOTA/CMakeLists.txt000066400000000000000000000005461324272410200222410ustar00rootroot00000000000000 ########### next target ############### SET(rquota_STAT_SRCS rquota_Null.c rquota_getquota.c rquota_getactivequota.c rquota_setquota.c rquota_setactivequota.c rquota_common.c ) add_library(rquota STATIC ${rquota_STAT_SRCS}) add_sanitizers(rquota) target_link_libraries(rquota string_utils ) ########### install files ############### nfs-ganesha-2.6.0/src/Protocols/RQUOTA/rquota_Null.c000066400000000000000000000034771324272410200221600ustar00rootroot00000000000000/* * Copyright CEA/DAM/DIF 2010 * Author: Philippe Deniel (philippe.deniel@cea.fr) * * -------------------------- * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ #include "config.h" #include #include #include #include #include #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "nfs23.h" #include "nfs4.h" #include "nfs_core.h" #include "nfs_exports.h" #include "mount.h" #include "rquota.h" #include "nfs_proto_functions.h" /** * @brief The RQUOTA proc null function, for all versions. * * @param[in] arg Ignored * @param[in] req Ignored * @param[out] res Ignored */ int rquota_Null(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { LogFullDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling RQUOTA_NULL"); /* 0 is success */ return 0; } /** * rquota_Null_Free: Frees the result structure allocated for rquota_Null * * Frees the result structure allocated for rquota_Null. Does Nothing in fact. * * @param res [INOUT] Pointer to the result structure. * */ void rquota_Null_Free(nfs_res_t *res) { /* Nothing to do */ } nfs-ganesha-2.6.0/src/Protocols/RQUOTA/rquota_common.c000066400000000000000000000035441324272410200225310ustar00rootroot00000000000000/* * Copyright CEA/DAM/DIF 2016 * Author: Philippe Deniel (philippe.deniel@cea.fr) * * -------------------------- * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ #include #include #include "log.h" #include "export_mgr.h" /** * Check if leading slash is missing, if yes then prepend * root path to the pathname */ char *check_handle_lead_slash(char *quota_path, char *temp_path, size_t temp_path_size) { if (quota_path[0] != '/') { /* prepend root path */ struct gsh_export *exp; int pathlen; int qpathlen; exp = get_gsh_export(0); pathlen = strlen(exp->fullpath); if (pathlen >= temp_path_size) { put_gsh_export(exp); return NULL; } memcpy(temp_path, exp->fullpath, pathlen); put_gsh_export(exp); /* Add trailing slash if it is missing */ if ((pathlen > 0) && (temp_path[pathlen - 1] != '/')) temp_path[pathlen++] = '/'; qpathlen = strlen(quota_path); if ((pathlen + qpathlen) >= temp_path_size) { LogInfo(COMPONENT_NFSPROTO, "Quota path %s too long", quota_path); return NULL; } memcpy(temp_path + pathlen, quota_path, qpathlen + 1); return temp_path; } else { return quota_path; } } nfs-ganesha-2.6.0/src/Protocols/RQUOTA/rquota_getactivequota.c000066400000000000000000000034141324272410200242620ustar00rootroot00000000000000/* * Copyright CEA/DAM/DIF 2010 * Author: Philippe Deniel (philippe.deniel@cea.fr) * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * */ #include "config.h" #include #include #include #include #include /* for having FNDELAY */ #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "nfs23.h" #include "nfs4.h" #include "nfs_core.h" #include "nfs_exports.h" #include "mount.h" #include "rquota.h" #include "nfs_proto_functions.h" /** * @brief The Rquota getactivequota function, for all versions. * * @param[in] arg Ignored * @param[in] req Ignored * @param[out] res Ignored * */ int rquota_getactivequota(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { LogFullDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling RQUOTA_GETACTIVEQUOTA"); /* 0 is success */ return 0; } /** * @brief Frees the result structure allocated for rquota_getactivequota * * @param[in,oujt] res Pointer to the result structure. * */ void rquota_getactivequota_Free(nfs_res_t *res) { /* Nothing to do */ } nfs-ganesha-2.6.0/src/Protocols/RQUOTA/rquota_getquota.c000066400000000000000000000107261324272410200230720ustar00rootroot00000000000000/* * Copyright CEA/DAM/DIF 2010 * Author: Philippe Deniel (philippe.deniel@cea.fr) * * -------------------------- * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ #include "config.h" #include #include #include #include #include /* for having FNDELAY */ #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "nfs23.h" #include "nfs4.h" #include "nfs_core.h" #include "nfs_exports.h" #include "mount.h" #include /* For USRQUOTA */ #include "rquota.h" #include "nfs_proto_functions.h" #include "export_mgr.h" /** * @brief The Rquota getquota function, for all versions. * * @param[in] arg Ignored * @param[in] req Ignored * @param[out] res Ignored * */ int rquota_getquota(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { fsal_status_t fsal_status; fsal_quota_t fsal_quota; int quota_type = USRQUOTA; struct gsh_export *exp = NULL; char *quota_path; getquota_rslt *qres = &res->res_rquota_getquota; char path[MAXPATHLEN]; int quota_id; LogFullDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling RQUOTA_GETQUOTA"); if (req->rq_msg.cb_vers == EXT_RQUOTAVERS) { quota_type = arg->arg_ext_rquota_getquota.gqa_type; quota_id = arg->arg_ext_rquota_getquota.gqa_id; } else { quota_id = arg->arg_rquota_getquota.gqa_uid; } qres->status = Q_EPERM; quota_path = check_handle_lead_slash(arg->arg_rquota_getquota.gqa_pathp, path, MAXPATHLEN); if (quota_path == NULL) goto out; /* Find the export for the dirname (using as well Path, Pseudo, or Tag) */ if (quota_path[0] != '/') { LogFullDebug(COMPONENT_NFSPROTO, "Searching for export by tag for %s", quota_path); exp = get_gsh_export_by_tag(quota_path); if (exp != NULL) { /* By Tag must use fullpath for actual request. */ quota_path = exp->fullpath; } } else if (nfs_param.core_param.mount_path_pseudo) { LogFullDebug(COMPONENT_NFSPROTO, "Searching for export by pseudo for %s", quota_path); exp = get_gsh_export_by_pseudo(quota_path, false); if (exp != NULL) { /* By Pseudo must use fullpath for actual request. */ quota_path = exp->fullpath; } } else { LogFullDebug(COMPONENT_NFSPROTO, "Searching for export by path for %s", quota_path); exp = get_gsh_export_by_path(quota_path, false); } if (exp == NULL) { /* No export found, return ACCESS error. */ LogEvent(COMPONENT_NFSPROTO, "Export entry for %s not found", quota_path); /* entry not found. */ goto out; } fsal_status = exp->fsal_export->exp_ops.get_quota(exp->fsal_export, quota_path, quota_type, quota_id, &fsal_quota); if (FSAL_IS_ERROR(fsal_status)) { if (fsal_status.major == ERR_FSAL_NO_QUOTA) qres->status = Q_NOQUOTA; goto out; } /* success */ qres->getquota_rslt_u.gqr_rquota.rq_active = TRUE; qres->getquota_rslt_u.gqr_rquota.rq_bsize = fsal_quota.bsize; qres->getquota_rslt_u.gqr_rquota.rq_bhardlimit = fsal_quota.bhardlimit; qres->getquota_rslt_u.gqr_rquota.rq_bsoftlimit = fsal_quota.bsoftlimit; qres->getquota_rslt_u.gqr_rquota.rq_curblocks = fsal_quota.curblocks; qres->getquota_rslt_u.gqr_rquota.rq_curfiles = fsal_quota.curfiles; qres->getquota_rslt_u.gqr_rquota.rq_fhardlimit = fsal_quota.fhardlimit; qres->getquota_rslt_u.gqr_rquota.rq_fsoftlimit = fsal_quota.fsoftlimit; qres->getquota_rslt_u.gqr_rquota.rq_btimeleft = fsal_quota.btimeleft; qres->getquota_rslt_u.gqr_rquota.rq_ftimeleft = fsal_quota.ftimeleft; qres->status = Q_OK; out: if (exp != NULL) put_gsh_export(exp); return NFS_REQ_OK; } /* rquota_getquota */ /** * @brief Free the result structure allocated for rquota_getquota * * * @param[in,out] res Pointer to the result structure. */ void rquota_getquota_Free(nfs_res_t *res) { /* Nothing to do */ } nfs-ganesha-2.6.0/src/Protocols/RQUOTA/rquota_setactivequota.c000066400000000000000000000033501324272410200242750ustar00rootroot00000000000000/* * Copyright CEA/DAM/DIF 2010 * Author: Philippe Deniel (philippe.deniel@cea.fr) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ #include "config.h" #include #include #include #include #include #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "nfs23.h" #include "nfs4.h" #include "nfs_core.h" #include "nfs_exports.h" #include "mount.h" #include "rquota.h" #include "nfs_proto_functions.h" /** * @brief The Rquota setactivequota function, for all versions. * * @param[in] arg Ignored * @param[in] req Ignored * @param[out] res Ignored */ int rquota_setactivequota(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { LogFullDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling RQUOTA_SETACTIVEQUOTA"); /* 0 is success */ return 0; } /** * @brief Frees the result structure allocated for rquota_setactivequota * * @param[in,out] res Pointer to the result structure. * */ void rquota_setactivequota_Free(nfs_res_t *res) { /* Nothing to do */ } nfs-ganesha-2.6.0/src/Protocols/RQUOTA/rquota_setquota.c000066400000000000000000000131661324272410200231070ustar00rootroot00000000000000/* * Copyright CEA/DAM/DIF 2010 * Author: Philippe Deniel (philippe.deniel@cea.fr) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ #include "config.h" #include #include #include #include #include #include #include "hashtable.h" #include "log.h" #include "gsh_rpc.h" #include "nfs23.h" #include "nfs4.h" #include "nfs_core.h" #include "nfs_exports.h" #include "mount.h" #include "rquota.h" #include "nfs_proto_functions.h" #include "export_mgr.h" static int do_rquota_setquota(char *quota_path, int quota_type, int quota_id, sq_dqblk * quota_dqblk, setquota_rslt * qres); /** * @brief The Rquota setquota function, for all versions. * * The RQUOTA setquota function, for all versions. * * @param[in] arg quota args * @param[in] req contains quota version * @param[out] res returned quota (modified) * */ int rquota_setquota(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { char *quota_path; int quota_id; int quota_type = USRQUOTA; struct sq_dqblk *quota_dqblk; setquota_rslt *qres = &res->res_rquota_setquota; LogFullDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling RQUOTA_SETQUOTA"); /* check rquota version and extract arguments */ if (req->rq_msg.cb_vers == EXT_RQUOTAVERS) { quota_path = arg->arg_ext_rquota_setquota.sqa_pathp; quota_id = arg->arg_ext_rquota_setquota.sqa_id; quota_type = arg->arg_ext_rquota_setquota.sqa_type; quota_dqblk = &arg->arg_ext_rquota_setquota.sqa_dqblk; } else { quota_path = arg->arg_rquota_setquota.sqa_pathp; quota_id = arg->arg_rquota_setquota.sqa_id; quota_dqblk = &arg->arg_rquota_setquota.sqa_dqblk; } return do_rquota_setquota(quota_path, quota_type, quota_id, quota_dqblk, qres); } /* rquota_setquota */ static int do_rquota_setquota(char *quota_path, int quota_type, int quota_id, sq_dqblk *quota_dqblk, setquota_rslt *qres) { fsal_status_t fsal_status; fsal_quota_t fsal_quota_in; fsal_quota_t fsal_quota_out; struct gsh_export *exp = NULL; char *qpath; char path[MAXPATHLEN]; qres->status = Q_EPERM; qpath = check_handle_lead_slash(quota_path, path, MAXPATHLEN); if (qpath == NULL) goto out; /* Find the export for the dirname (using as well Path, Pseudo, or Tag) */ if (qpath[0] != '/') { LogFullDebug(COMPONENT_NFSPROTO, "Searching for export by tag for %s", qpath); exp = get_gsh_export_by_tag(qpath); if (exp != NULL) { /* By Tag must use fullpath for actual request. */ qpath = exp->fullpath; } } else if (nfs_param.core_param.mount_path_pseudo) { LogFullDebug(COMPONENT_NFSPROTO, "Searching for export by pseudo for %s", qpath); exp = get_gsh_export_by_pseudo(qpath, false); if (exp != NULL) { /* By Pseudo must use fullpath for actual request. */ qpath = exp->fullpath; } } else { LogFullDebug(COMPONENT_NFSPROTO, "Searching for export by path for %s", qpath); exp = get_gsh_export_by_path(qpath, false); } if (exp == NULL) { /* No export found, return ACCESS error. */ LogEvent(COMPONENT_NFSPROTO, "Export entry for %s not found", qpath); /* entry not found. */ goto out; } memset(&fsal_quota_in, 0, sizeof(fsal_quota_t)); memset(&fsal_quota_out, 0, sizeof(fsal_quota_t)); fsal_quota_in.bhardlimit = quota_dqblk->rq_bhardlimit; fsal_quota_in.bsoftlimit = quota_dqblk->rq_bsoftlimit; fsal_quota_in.curblocks = quota_dqblk->rq_curblocks; fsal_quota_in.fhardlimit = quota_dqblk->rq_fhardlimit; fsal_quota_in.fsoftlimit = quota_dqblk->rq_fsoftlimit; fsal_quota_in.btimeleft = quota_dqblk->rq_btimeleft; fsal_quota_in.ftimeleft = quota_dqblk->rq_ftimeleft; fsal_status = exp->fsal_export->exp_ops.set_quota(exp->fsal_export, qpath, quota_type, quota_id, &fsal_quota_in, &fsal_quota_out); if (FSAL_IS_ERROR(fsal_status)) { if (fsal_status.major == ERR_FSAL_NO_QUOTA) qres->status = Q_NOQUOTA; goto out; } /* is success */ qres->setquota_rslt_u.sqr_rquota.rq_active = TRUE; qres->setquota_rslt_u.sqr_rquota.rq_bhardlimit = fsal_quota_out.bhardlimit; qres->setquota_rslt_u.sqr_rquota.rq_bsoftlimit = fsal_quota_out.bsoftlimit; qres->setquota_rslt_u.sqr_rquota.rq_curblocks = fsal_quota_out.curblocks; qres->setquota_rslt_u.sqr_rquota.rq_fhardlimit = fsal_quota_out.fhardlimit; qres->setquota_rslt_u.sqr_rquota.rq_fsoftlimit = fsal_quota_out.fsoftlimit; qres->setquota_rslt_u.sqr_rquota.rq_btimeleft = fsal_quota_out.btimeleft; qres->setquota_rslt_u.sqr_rquota.rq_ftimeleft = fsal_quota_out.ftimeleft; qres->status = Q_OK; out: if (exp != NULL) put_gsh_export(exp); return NFS_REQ_OK; } /* do_rquota_setquota */ /** * @brief Frees the result structure allocated for rquota_setquota * * @param[in,out] res Pointer to the result structure. * */ void rquota_setquota_Free(nfs_res_t *res) { /* Nothing to do */ } nfs-ganesha-2.6.0/src/Protocols/XDR/000077500000000000000000000000001324272410200171165ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/Protocols/XDR/CMakeLists.txt000066400000000000000000000004411324272410200216550ustar00rootroot00000000000000 ########### next target ############### SET(nfs_mnt_xdr_STAT_SRCS xdr_mount.c xdr_nfs23.c xdr_nfsv41.c xdr_rquota.c xdr_nlm4.c xdr_nsm.c ) add_library(nfs_mnt_xdr STATIC ${nfs_mnt_xdr_STAT_SRCS}) add_sanitizers(nfs_mnt_xdr) ########### install files ############### nfs-ganesha-2.6.0/src/Protocols/XDR/flex_files_prot.x000066400000000000000000000102351324272410200224740ustar00rootroot00000000000000/* * Copyright (c) 2012 IETF Trust and the persons identified * as authors of the code. All rights reserved. * * Redistribution and use in source and binary forms, with * or without modification, are permitted provided that the * following conditions are met: * * o Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * o Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * o Neither the name of Internet Society, IETF or IETF * Trust, nor the names of specific contributors, may be * used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS * AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This code was derived from RFCTBD10. * Please reproduce this note if possible. */ /* * flex_files_prot.x */ /* * The following include statements are for example only. * The actual XDR definition files are generated separately * and independently and are likely to have a different name. * %#include * %#include */ struct ff_device_versions4 { uint32_t ffdv_version; uint32_t ffdv_minorversion; uint32_t ffdv_rsize; uint32_t ffdv_wsize; bool ffdv_tightly_coupled; }; struct ff_device_addr4 { multipath_list4 ffda_netaddrs; ff_device_versions4 ffda_versions<>; }; struct ff_data_server4 { deviceid4 ffds_deviceid; uint32_t ffds_efficiency; stateid4 ffds_stateid; nfs_fh4 ffds_fh_vers<>; fattr4_owner ffds_user; fattr4_owner_group ffds_group; }; struct ff_mirror4 { ff_data_server4 ffm_data_servers<>; }; struct ff_layout4 { length4 ffl_stripe_unit; ff_mirror4 ffl_mirrors<>; }; struct ff_ioerr4 { offset4 ffie_offset; length4 ffie_length; stateid4 ffie_stateid; device_error4 ffie_errors<>; }; struct ff_io_latency4 { nfstime4 ffil_min; nfstime4 ffil_max; nfstime4 ffil_avg; uint32_t ffil_count; }; struct ff_layoutupdate4 { netaddr4 ffl_addr; nfs_fh4 ffl_fhandle; ff_io_latency4 ffl_read; ff_io_latency4 ffl_write; nfstime4 ffl_duration; bool ffl_local; }; struct io_info4 { uint32_t ii_count; uint64_t ii_bytes; }; struct ff_iostats4 { offset4 ffis_offset; length4 ffis_length; stateid4 ffis_stateid; io_info4 ffis_read; io_info4 ffis_write; deviceid4 ffis_deviceid; ff_layoutupdate4 ffis_layoutupdate; }; struct ff_layoutreturn4 { ff_ioerr4 fflr_ioerr_report<>; ff_iostats4 fflr_iostats_report<>; }; union ff_mirrors_hint switch (bool ffmc_valid) { case TRUE: uint32_t ffmc_mirrors; case FALSE: void; }; struct ff_layouthint4 { ff_mirrors_hint fflh_mirrors_hint; }; enum ff_cb_recall_any_mask { FF_RCA4_TYPE_MASK_READ = -2, FF_RCA4_TYPE_MASK_RW = -1 }; nfs-ganesha-2.6.0/src/Protocols/XDR/mount.x000066400000000000000000000042241324272410200204530ustar00rootroot00000000000000 const MNTPATHLEN = 1024; /* Maximum bytes in a path name */ const MNTNAMLEN = 255; /* Maximum bytes in a name */ /* const FHSIZE2 = 32; const FHSIZE3 = 64; */ enum mountstat3 { MNT3_OK = 0, /* no error */ MNT3ERR_PERM = 1, /* Not owner */ MNT3ERR_NOENT = 2, /* No such file or directory */ MNT3ERR_IO = 5, /* I/O error */ MNT3ERR_ACCES = 13, /* Permission denied */ MNT3ERR_NOTDIR = 20, /* Not a directory */ MNT3ERR_INVAL = 22, /* Invalid argument */ MNT3ERR_NAMETOOLONG = 63, /* Filename too long */ MNT3ERR_NOTSUPP = 10004, /* Operation not supported */ MNT3ERR_SERVERFAULT = 10006 /* A failure on the server */ }; /* typedef opaque fhandle2[FHSIZE2]; */ typedef opaque fhandle3; typedef string dirpath; typedef string name; typedef struct groupnode *groups; struct groupnode { name gr_name; groups gr_next; }; typedef struct exportnode *exports; struct exportnode { dirpath ex_dir; groups ex_groups; exports ex_next; }; typedef struct mountbody *mountlist; struct mountbody { name ml_hostname; dirpath ml_directory; mountlist ml_next; }; /* union fhstatus2 switch (unsigned fhs_status) { case 0: fhandle2 directory; default: void; }; */ struct mountres3_ok { fhandle3 fhandle; int auth_flavors<>; }; union mountres3 switch (mountstat3 fhs_status) { case MNT3_OK: mountres3_ok mountinfo; default: void; }; program MOUNTPROG { /* * Version 1 of the mount protocol used with * version 2 of the NFS protocol. */ version MOUNT_V1 { void MOUNTPROC2_NULL(void) = 0; fhstatus2 MOUNTPROC2_MNT(dirpath) = 1; mountlist MOUNTPROC2_DUMP(void) = 2; void MOUNTPROC2_UMNT(dirpath) = 3; void MOUNTPROC2_UMNTALL(void) = 4; exports MOUNTPROC2_EXPORT(void) = 5; } = 1; version MOUNT_V3 { void MOUNTPROC3_NULL(void) = 0; mountres3 MOUNTPROC3_MNT(dirpath) = 1; mountlist MOUNTPROC3_DUMP(void) = 2; void MOUNTPROC3_UMNT(dirpath) = 3; void MOUNTPROC3_UMNTALL(void) = 4; exports MOUNTPROC3_EXPORT(void) = 5; } = 3; } = 100005; nfs-ganesha-2.6.0/src/Protocols/XDR/nfs23.x000066400000000000000000000441751324272410200202550ustar00rootroot00000000000000/* NFS V2 definitions */ const NFS2_MAXDATA = 8192; const NFS2_MAXPATHLEN = 1024; const NFS2_MAXNAMLEN = 255; const NFS2_COOKIESIZE = 4; const NFS2_FHSIZE = 32; const NFS2_MNTPATHLEN = 1024; const NFS2_MNTNAMLEN = 255; typedef string nfspath2; typedef string filename2; typedef opaque fhandle2[NFS2_FHSIZE]; typedef opaque nfsdata2; typedef opaque nfscookie2[NFS2_COOKIESIZE]; enum nfsstat2 { NFS_OK = 0, NFSERR_PERM=1, NFSERR_NOENT=2, NFSERR_IO=5, NFSERR_NXIO=6, NFSERR_ACCES=13, NFSERR_EXIST=17, NFSERR_NODEV=19, NFSERR_NOTDIR=20, NFSERR_ISDIR=21, NFSERR_FBIG=27, NFSERR_NOSPC=28, NFSERR_ROFS=30, NFSERR_NAMETOOLONG=63, NFSERR_NOTEMPTY=66, NFSERR_DQUOT=69, NFSERR_STALE=70, NFSERR_WFLUSH=99 }; enum ftype2 { NFNON = 0, NFREG = 1, NFDIR = 2, NFBLK = 3, NFCHR = 4, NFLNK = 5, NFSOCK = 6, NFBAD = 7, NFFIFO = 8 }; struct nfstime2 { unsigned int seconds; unsigned int useconds; }; struct fattr2 { ftype2 type; unsigned int mode; unsigned int nlink; unsigned int uid; unsigned int gid; unsigned int size; unsigned int blocksize; unsigned int rdev; unsigned int blocks; unsigned int fsid; unsigned int fileid; nfstime2 atime; nfstime2 mtime; nfstime2 ctime; }; union fhstatus2 switch (unsigned status) { case 0: fhandle2 directory; default: void; } ; struct diropargs2 { fhandle2 dir; filename2 name; }; struct DIROP2resok { fhandle2 file; fattr2 attributes; } ; union DIROP2res switch (nfsstat2 status) { case NFS_OK: DIROP2resok diropok ; default: void; }; union ATTR2res switch (nfsstat2 status) { case NFS_OK: fattr2 attributes; default: void; }; struct sattr2 { unsigned int mode; unsigned int uid; unsigned int gid; unsigned int size; nfstime2 atime; nfstime2 mtime; }; struct statinfo2 { unsigned tsize; unsigned bsize; unsigned blocks; unsigned bfree; unsigned bavail; } ; union STATFS2res switch (nfsstat2 status) { case NFS_OK: statinfo2 info; default: void; }; struct READDIR2args { fhandle2 dir; nfscookie2 cookie; unsigned count; }; struct entry2 { unsigned fileid; filename2 name; nfscookie2 cookie; entry2 *nextentry; }; struct READDIR2resok { entry2 *entries; bool eof; } ; union READDIR2res switch (nfsstat2 status) { case NFS_OK: READDIR2resok readdirok; default: void; }; struct SYMLINK2args { diropargs2 from; nfspath2 to; sattr2 attributes; }; struct LINK2args { fhandle2 from; diropargs2 to; }; struct RENAME2args { diropargs2 from; diropargs2 to; }; struct CREATE2args { diropargs2 where; sattr2 attributes; }; struct WRITE2args { fhandle2 file; unsigned beginoffset; unsigned offset; unsigned totalcount; nfsdata2 data; }; struct READ2resok { fattr2 attributes ; nfsdata2 data ; }; union READ2res switch (nfsstat2 status) { case NFS_OK: READ2resok readok; default: void; }; struct READ2args { fhandle2 file; unsigned offset; unsigned count; unsigned totalcount; }; union READLINK2res switch (nfsstat2 status) { case NFS_OK: nfspath2 data; default: void; }; struct SETATTR2args { fhandle2 file; sattr2 attributes; }; /* NFS V3 definitions */ #define NFS3_FHSIZE 64 #define NFS3_COOKIEVERFSIZE 8 #define NFS3_CREATEVERFSIZE 8 #define NFS3_WRITEVERFSIZE 8 typedef unsigned hyper nfs3_uint64; typedef hyper nfs3_int64; typedef unsigned int nfs3_uint32; typedef int nfs3_int32; typedef string filename3<>; typedef string nfspath3<>; typedef nfs3_uint64 fileid3; typedef nfs3_uint64 cookie3; typedef opaque cookieverf3[NFS3_COOKIEVERFSIZE]; typedef opaque createverf3[NFS3_CREATEVERFSIZE]; typedef opaque writeverf3[NFS3_WRITEVERFSIZE]; typedef nfs3_uint32 uid3; typedef nfs3_uint32 gid3; typedef nfs3_uint64 size3; typedef nfs3_uint64 offset3; typedef nfs3_uint32 mode3; typedef nfs3_uint32 count3; enum nfsstat3 { NFS3_OK = 0, NFS3ERR_PERM = 1, NFS3ERR_NOENT = 2, NFS3ERR_IO = 5, NFS3ERR_NXIO = 6, NFS3ERR_ACCES = 13, NFS3ERR_EXIST = 17, NFS3ERR_XDEV = 18, NFS3ERR_NODEV = 19, NFS3ERR_NOTDIR = 20, NFS3ERR_ISDIR = 21, NFS3ERR_INVAL = 22, NFS3ERR_FBIG = 27, NFS3ERR_NOSPC = 28, NFS3ERR_ROFS = 30, NFS3ERR_MLINK = 31, NFS3ERR_NAMETOOLONG = 63, NFS3ERR_NOTEMPTY = 66, NFS3ERR_DQUOT = 69, NFS3ERR_STALE = 70, NFS3ERR_REMOTE = 71, NFS3ERR_BADHANDLE = 10001, NFS3ERR_NOT_SYNC = 10002, NFS3ERR_BAD_COOKIE = 10003, NFS3ERR_NOTSUPP = 10004, NFS3ERR_TOOSMALL = 10005, NFS3ERR_SERVERFAULT = 10006, NFS3ERR_BADTYPE = 10007, NFS3ERR_JUKEBOX = 10008 }; enum ftype3 { NF3REG = 1, NF3DIR = 2, NF3BLK = 3, NF3CHR = 4, NF3LNK = 5, NF3SOCK = 6, NF3FIFO = 7 }; struct specdata3 { nfs3_uint32 specdata1; nfs3_uint32 specdata2; }; struct nfs_fh3 { opaque data; }; struct nfstime3 { nfs3_uint32 seconds; nfs3_uint32 nseconds; }; struct fattr3 { ftype3 type; mode3 mode; nfs3_uint32 nlink; uid3 uid; gid3 gid; size3 size; size3 used; specdata3 rdev; nfs3_uint64 fsid; fileid3 fileid; nfstime3 atime; nfstime3 mtime; nfstime3 ctime; }; union post_op_attr switch (bool attributes_follow) { case TRUE: fattr3 attributes; case FALSE: void; }; struct wcc_attr { size3 size; nfstime3 mtime; nfstime3 ctime; }; union pre_op_attr switch (bool attributes_follow) { case TRUE: wcc_attr attributes; case FALSE: void; }; struct wcc_data { pre_op_attr before; post_op_attr after; }; union post_op_fh3 switch (bool handle_follows) { case TRUE: nfs_fh3 handle; case FALSE: void; }; enum time_how { DONT_CHANGE = 0, SET_TO_SERVER_TIME = 1, SET_TO_CLIENT_TIME = 2 }; union set_mode3 switch (bool set_it) { case TRUE: mode3 mode; default: void; }; union set_uid3 switch (bool set_it) { case TRUE: uid3 uid; default: void; }; union set_gid3 switch (bool set_it) { case TRUE: gid3 gid; default: void; }; union set_size3 switch (bool set_it) { case TRUE: size3 size; default: void; }; union set_atime switch (time_how set_it) { case SET_TO_CLIENT_TIME: nfstime3 atime; default: void; }; union set_mtime switch (time_how set_it) { case SET_TO_CLIENT_TIME: nfstime3 mtime; default: void; }; struct sattr3 { set_mode3 mode; set_uid3 uid; set_gid3 gid; set_size3 size; set_atime atime; set_mtime mtime; }; struct diropargs3 { nfs_fh3 dir; filename3 name; }; struct GETATTR3args { nfs_fh3 object; }; struct GETATTR3resok { fattr3 obj_attributes; }; union GETATTR3res switch (nfsstat3 status) { case NFS3_OK: GETATTR3resok resok; default: void; }; union sattrguard3 switch (bool check) { case TRUE: nfstime3 obj_ctime; case FALSE: void; }; struct SETATTR3args { nfs_fh3 object; sattr3 new_attributes; sattrguard3 guard; }; struct SETATTR3resok { wcc_data obj_wcc; }; struct SETATTR3resfail { wcc_data obj_wcc; }; union SETATTR3res switch (nfsstat3 status) { case NFS3_OK: SETATTR3resok resok; default: SETATTR3resfail resfail; }; struct LOOKUP3args { diropargs3 what; }; struct LOOKUP3resok { nfs_fh3 object; post_op_attr obj_attributes; post_op_attr dir_attributes; }; struct LOOKUP3resfail { post_op_attr dir_attributes; }; union LOOKUP3res switch (nfsstat3 status) { case NFS3_OK: LOOKUP3resok resok; default: LOOKUP3resfail resfail; }; const ACCESS3_READ = 0x0001; const ACCESS3_LOOKUP = 0x0002; const ACCESS3_MODIFY = 0x0004; const ACCESS3_EXTEND = 0x0008; const ACCESS3_DELETE = 0x0010; const ACCESS3_EXECUTE = 0x0020; struct ACCESS3args { nfs_fh3 object; nfs3_uint32 access; }; struct ACCESS3resok { post_op_attr obj_attributes; nfs3_uint32 access; }; struct ACCESS3resfail { post_op_attr obj_attributes; }; union ACCESS3res switch (nfsstat3 status) { case NFS3_OK: ACCESS3resok resok; default: ACCESS3resfail resfail; }; struct READLINK3args { nfs_fh3 symlink; }; struct READLINK3resok { post_op_attr symlink_attributes; nfspath3 data; }; struct READLINK3resfail { post_op_attr symlink_attributes; }; union READLINK3res switch (nfsstat3 status) { case NFS3_OK: READLINK3resok resok; default: READLINK3resfail resfail; }; struct READ3args { nfs_fh3 file; offset3 offset; count3 count; }; struct READ3resok { post_op_attr file_attributes; count3 count; bool eof; opaque data<>; }; struct READ3resfail { post_op_attr file_attributes; }; union READ3res switch (nfsstat3 status) { case NFS3_OK: READ3resok resok; default: READ3resfail resfail; }; enum stable_how { UNSTABLE = 0, DATA_SYNC = 1, FILE_SYNC = 2 }; struct WRITE3args { nfs_fh3 file; offset3 offset; count3 count; stable_how stable; opaque data<>; }; struct WRITE3resok { wcc_data file_wcc; count3 count; stable_how committed; writeverf3 verf; }; struct WRITE3resfail { wcc_data file_wcc; }; union WRITE3res switch (nfsstat3 status) { case NFS3_OK: WRITE3resok resok; default: WRITE3resfail resfail; }; enum createmode3 { UNCHECKED = 0, GUARDED = 1, EXCLUSIVE = 2 }; union createhow3 switch (createmode3 mode) { case UNCHECKED: case GUARDED: sattr3 obj_attributes; case EXCLUSIVE: createverf3 verf; }; struct CREATE3args { diropargs3 where; createhow3 how; }; struct CREATE3resok { post_op_fh3 obj; post_op_attr obj_attributes; wcc_data dir_wcc; }; struct CREATE3resfail { wcc_data dir_wcc; }; union CREATE3res switch (nfsstat3 status) { case NFS3_OK: CREATE3resok resok; default: CREATE3resfail resfail; }; struct MKDIR3args { diropargs3 where; sattr3 attributes; }; struct MKDIR3resok { post_op_fh3 obj; post_op_attr obj_attributes; wcc_data dir_wcc; }; struct MKDIR3resfail { wcc_data dir_wcc; }; union MKDIR3res switch (nfsstat3 status) { case NFS3_OK: MKDIR3resok resok; default: MKDIR3resfail resfail; }; struct symlinkdata3 { sattr3 symlink_attributes; nfspath3 symlink_data; }; struct SYMLINK3args { diropargs3 where; symlinkdata3 symlink; }; struct SYMLINK3resok { post_op_fh3 obj; post_op_attr obj_attributes; wcc_data dir_wcc; }; struct SYMLINK3resfail { wcc_data dir_wcc; }; union SYMLINK3res switch (nfsstat3 status) { case NFS3_OK: SYMLINK3resok resok; default: SYMLINK3resfail resfail; }; struct devicedata3 { sattr3 dev_attributes; specdata3 spec; }; union mknoddata3 switch (ftype3 type) { case NF3CHR: case NF3BLK: devicedata3 device; case NF3SOCK: case NF3FIFO: sattr3 pipe_attributes; default: void; }; struct MKNOD3args { diropargs3 where; mknoddata3 what; }; struct MKNOD3resok { post_op_fh3 obj; post_op_attr obj_attributes; wcc_data dir_wcc; }; struct MKNOD3resfail { wcc_data dir_wcc; }; union MKNOD3res switch (nfsstat3 status) { case NFS3_OK: MKNOD3resok resok; default: MKNOD3resfail resfail; }; struct REMOVE3args { diropargs3 object; }; struct REMOVE3resok { wcc_data dir_wcc; }; struct REMOVE3resfail { wcc_data dir_wcc; }; union REMOVE3res switch (nfsstat3 status) { case NFS3_OK: REMOVE3resok resok; default: REMOVE3resfail resfail; }; struct RMDIR3args { diropargs3 object; }; struct RMDIR3resok { wcc_data dir_wcc; }; struct RMDIR3resfail { wcc_data dir_wcc; }; union RMDIR3res switch (nfsstat3 status) { case NFS3_OK: RMDIR3resok resok; default: RMDIR3resfail resfail; }; struct RENAME3args { diropargs3 from; diropargs3 to; }; struct RENAME3resok { wcc_data fromdir_wcc; wcc_data todir_wcc; }; struct RENAME3resfail { wcc_data fromdir_wcc; wcc_data todir_wcc; }; union RENAME3res switch (nfsstat3 status) { case NFS3_OK: RENAME3resok resok; default: RENAME3resfail resfail; }; struct LINK3args { nfs_fh3 file; diropargs3 link; }; struct LINK3resok { post_op_attr file_attributes; wcc_data linkdir_wcc; }; struct LINK3resfail { post_op_attr file_attributes; wcc_data linkdir_wcc; }; union LINK3res switch (nfsstat3 status) { case NFS3_OK: LINK3resok resok; default: LINK3resfail resfail; }; struct READDIR3args { nfs_fh3 dir; cookie3 cookie; cookieverf3 cookieverf; count3 count; }; struct entry3 { fileid3 fileid; filename3 name; cookie3 cookie; entry3 *nextentry; }; struct dirlist3 { entry3 *entries; bool eof; }; struct READDIR3resok { post_op_attr dir_attributes; cookieverf3 cookieverf; dirlist3 reply; }; struct READDIR3resfail { post_op_attr dir_attributes; }; union READDIR3res switch (nfsstat3 status) { case NFS3_OK: READDIR3resok resok; default: READDIR3resfail resfail; }; struct READDIRPLUS3args { nfs_fh3 dir; cookie3 cookie; cookieverf3 cookieverf; count3 dircount; count3 maxcount; }; struct entryplus3 { fileid3 fileid; filename3 name; cookie3 cookie; post_op_attr name_attributes; post_op_fh3 name_handle; entryplus3 *nextentry; }; struct dirlistplus3 { entryplus3 *entries; bool eof; }; struct READDIRPLUS3resok { post_op_attr dir_attributes; cookieverf3 cookieverf; dirlistplus3 reply; }; struct READDIRPLUS3resfail { post_op_attr dir_attributes; }; union READDIRPLUS3res switch (nfsstat3 status) { case NFS3_OK: READDIRPLUS3resok resok; default: READDIRPLUS3resfail resfail; }; struct FSSTAT3args { nfs_fh3 fsroot; }; struct FSSTAT3resok { post_op_attr obj_attributes; size3 tbytes; size3 fbytes; size3 abytes; size3 tfiles; size3 ffiles; size3 afiles; nfs3_uint32 invarsec; }; struct FSSTAT3resfail { post_op_attr obj_attributes; }; union FSSTAT3res switch (nfsstat3 status) { case NFS3_OK: FSSTAT3resok resok; default: FSSTAT3resfail resfail; }; const FSF3_LINK = 0x0001; const FSF3_SYMLINK = 0x0002; const FSF3_HOMOGENEOUS = 0x0008; const FSF3_CANSETTIME = 0x0010; struct FSINFO3args { nfs_fh3 fsroot; }; struct FSINFO3resok { post_op_attr obj_attributes; nfs3_uint32 rtmax; nfs3_uint32 rtpref; nfs3_uint32 rtmult; nfs3_uint32 wtmax; nfs3_uint32 wtpref; nfs3_uint32 wtmult; nfs3_uint32 dtpref; size3 maxfilesize; nfstime3 time_delta; nfs3_uint32 properties; }; struct FSINFO3resfail { post_op_attr obj_attributes; }; union FSINFO3res switch (nfsstat3 status) { case NFS3_OK: FSINFO3resok resok; default: FSINFO3resfail resfail; }; struct PATHCONF3args { nfs_fh3 object; }; struct PATHCONF3resok { post_op_attr obj_attributes; nfs3_uint32 linkmax; nfs3_uint32 name_max; bool no_trunc; bool chown_restricted; bool case_insensitive; bool case_preserving; }; struct PATHCONF3resfail { post_op_attr obj_attributes; }; union PATHCONF3res switch (nfsstat3 status) { case NFS3_OK: PATHCONF3resok resok; default: PATHCONF3resfail resfail; }; struct COMMIT3args { nfs_fh3 file; offset3 offset; count3 count; }; struct COMMIT3resok { wcc_data file_wcc; writeverf3 verf; }; struct COMMIT3resfail { wcc_data file_wcc; }; union COMMIT3res switch (nfsstat3 status) { case NFS3_OK: COMMIT3resok resok; default: COMMIT3resfail resfail; }; program NFS_PROGRAM { version NFS_V2 { void NFSPROC_NULL(void) = 0; ATTR2res NFSPROC_GETATTR(fhandle2) = 1; ATTR2res NFSPROC_SETATTR(SETATTR2args) = 2; void NFSPROC_ROOT(void) = 3; DIROP2res NFSPROC_LOOKUP(diropargs2) = 4; READLINK2res NFSPROC_READLINK(fhandle2) = 5; READ2res NFSPROC_READ(READ2args) = 6; void NFSPROC_WRITECACHE(void) = 7; ATTR2res NFSPROC_WRITE(WRITE2args) = 8; DIROP2res NFSPROC_CREATE(CREATE2args) = 9; nfsstat2 NFSPROC_REMOVE(diropargs2) = 10; nfsstat2 NFSPROC_RENAME(RENAME2args) = 11; nfsstat2 NFSPROC_LINK(LINK2args) = 12; nfsstat2 NFSPROC_SYMLINK(SYMLINK2args) = 13; DIROP2res NFSPROC_MKDIR(CREATE2args) = 14; nfsstat2 NFSPROC_RMDIR(diropargs2) = 15; READDIR2res NFSPROC_READDIR(READDIR2args) = 16; STATFS2res NFSPROC_STATFS(fhandle2) = 17; } = 2; version NFS_V3 { void NFSPROC3_NULL(void) = 0; GETATTR3res NFSPROC3_GETATTR(GETATTR3args) = 1; SETATTR3res NFSPROC3_SETATTR(SETATTR3args) = 2; LOOKUP3res NFSPROC3_LOOKUP(LOOKUP3args) = 3; ACCESS3res NFSPROC3_ACCESS(ACCESS3args) = 4; READLINK3res NFSPROC3_READLINK(READLINK3args) = 5; READ3res NFSPROC3_READ(READ3args) = 6; WRITE3res NFSPROC3_WRITE(WRITE3args) = 7; CREATE3res NFSPROC3_CREATE(CREATE3args) = 8; MKDIR3res NFSPROC3_MKDIR(MKDIR3args) = 9; SYMLINK3res NFSPROC3_SYMLINK(SYMLINK3args) = 10; MKNOD3res NFSPROC3_MKNOD(MKNOD3args) = 11; REMOVE3res NFSPROC3_REMOVE(REMOVE3args) = 12; RMDIR3res NFSPROC3_RMDIR(RMDIR3args) = 13; RENAME3res NFSPROC3_RENAME(RENAME3args) = 14; LINK3res NFSPROC3_LINK(LINK3args) = 15; READDIR3res NFSPROC3_READDIR(READDIR3args) = 16; READDIRPLUS3res NFSPROC3_READDIRPLUS(READDIRPLUS3args) = 17; FSSTAT3res NFSPROC3_FSSTAT(FSSTAT3args) = 18; FSINFO3res NFSPROC3_FSINFO(FSINFO3args) = 19; PATHCONF3res NFSPROC3_PATHCONF(PATHCONF3args) = 20; COMMIT3res NFSPROC3_COMMIT(COMMIT3args) = 21; } = 3; } = 100003; nfs-ganesha-2.6.0/src/Protocols/XDR/nfs4.x000066400000000000000000001104051324272410200201620ustar00rootroot00000000000000/* * This file was machine generated for [RFC7530]. * * Last updated Tue Mar 10 11:51:21 PDT 2015. */ /* * Copyright (c) 2015 IETF Trust and the persons identified * as authors of the code. All rights reserved. * * Redistribution and use in source and binary forms, with * or without modification, are permitted provided that the * following conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * - Neither the name of Internet Society, IETF or IETF * Trust, nor the names of specific contributors, may be * used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS * AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * This code was derived from RFC 7531. */ /* * nfs4_prot.x * */ %#pragma ident "@(#)nfs4_prot.x 1.122" /* * Basic typedefs for RFC 1832 data type definitions */ /* * typedef int int32_t; * typedef unsigned int uint32_t; * typedef hyper int64_t; * typedef unsigned hyper uint64_t; */ /* * Sizes */ const NFS4_FHSIZE = 128; const NFS4_VERIFIER_SIZE = 8; const NFS4_OTHER_SIZE = 12; const NFS4_OPAQUE_LIMIT = 1024; const NFS4_INT64_MAX = 0x7fffffffffffffff; const NFS4_UINT64_MAX = 0xffffffffffffffff; const NFS4_INT32_MAX = 0x7fffffff; const NFS4_UINT32_MAX = 0xffffffff; /* * File types */ enum nfs_ftype4 { NF4REG = 1, /* Regular File */ NF4DIR = 2, /* Directory */ NF4BLK = 3, /* Special File - block device */ NF4CHR = 4, /* Special File - character device */ NF4LNK = 5, /* Symbolic Link */ NF4SOCK = 6, /* Special File - socket */ NF4FIFO = 7, /* Special File - fifo */ NF4ATTRDIR = 8, /* Attribute Directory */ NF4NAMEDATTR = 9 /* Named Attribute */ }; /* * Error status */ enum nfsstat4 { NFS4_OK = 0, /* everything is okay */ NFS4ERR_PERM = 1, /* caller not privileged */ NFS4ERR_NOENT = 2, /* no such file/directory */ NFS4ERR_IO = 5, /* hard I/O error */ NFS4ERR_NXIO = 6, /* no such device */ NFS4ERR_ACCESS = 13, /* access denied */ NFS4ERR_EXIST = 17, /* file already exists */ NFS4ERR_XDEV = 18, /* different filesystems */ /* Unused/reserved 19 */ NFS4ERR_NOTDIR = 20, /* should be a directory */ NFS4ERR_ISDIR = 21, /* should not be directory */ NFS4ERR_INVAL = 22, /* invalid argument */ NFS4ERR_FBIG = 27, /* file exceeds server max */ NFS4ERR_NOSPC = 28, /* no space on filesystem */ NFS4ERR_ROFS = 30, /* read-only filesystem */ NFS4ERR_MLINK = 31, /* too many hard links */ NFS4ERR_NAMETOOLONG = 63, /* name exceeds server max */ NFS4ERR_NOTEMPTY = 66, /* directory not empty */ NFS4ERR_DQUOT = 69, /* hard quota limit reached*/ NFS4ERR_STALE = 70, /* file no longer exists */ NFS4ERR_BADHANDLE = 10001,/* Illegal filehandle */ NFS4ERR_BAD_COOKIE = 10003,/* READDIR cookie is stale */ NFS4ERR_NOTSUPP = 10004,/* operation not supported */ NFS4ERR_TOOSMALL = 10005,/* response limit exceeded */ NFS4ERR_SERVERFAULT = 10006,/* undefined server error */ NFS4ERR_BADTYPE = 10007,/* type invalid for CREATE */ NFS4ERR_DELAY = 10008,/* file "busy" - retry */ NFS4ERR_SAME = 10009,/* nverify says attrs same */ NFS4ERR_DENIED = 10010,/* lock unavailable */ NFS4ERR_EXPIRED = 10011,/* lock lease expired */ NFS4ERR_LOCKED = 10012,/* I/O failed due to lock */ NFS4ERR_GRACE = 10013,/* in grace period */ NFS4ERR_FHEXPIRED = 10014,/* filehandle expired */ NFS4ERR_SHARE_DENIED = 10015,/* share reserve denied */ NFS4ERR_WRONGSEC = 10016,/* wrong security flavor */ NFS4ERR_CLID_INUSE = 10017,/* clientid in use */ NFS4ERR_RESOURCE = 10018,/* resource exhaustion */ NFS4ERR_MOVED = 10019,/* filesystem relocated */ NFS4ERR_NOFILEHANDLE = 10020,/* current FH is not set */ NFS4ERR_MINOR_VERS_MISMATCH = 10021,/* minor vers not supp */ NFS4ERR_STALE_CLIENTID = 10022,/* server has rebooted */ NFS4ERR_STALE_STATEID = 10023,/* server has rebooted */ NFS4ERR_OLD_STATEID = 10024,/* state is out of sync */ NFS4ERR_BAD_STATEID = 10025,/* incorrect stateid */ NFS4ERR_BAD_SEQID = 10026,/* request is out of seq. */ NFS4ERR_NOT_SAME = 10027,/* verify - attrs not same */ NFS4ERR_LOCK_RANGE = 10028,/* lock range not supported*/ NFS4ERR_SYMLINK = 10029,/* should be file/directory*/ NFS4ERR_RESTOREFH = 10030,/* no saved filehandle */ NFS4ERR_LEASE_MOVED = 10031,/* some filesystem moved */ NFS4ERR_ATTRNOTSUPP = 10032,/* recommended attr not sup*/ NFS4ERR_NO_GRACE = 10033,/* reclaim outside of grace*/ NFS4ERR_RECLAIM_BAD = 10034,/* reclaim error at server */ NFS4ERR_RECLAIM_CONFLICT = 10035,/* conflict on reclaim */ NFS4ERR_BADXDR = 10036,/* XDR decode failed */ NFS4ERR_LOCKS_HELD = 10037,/* file locks held at CLOSE*/ NFS4ERR_OPENMODE = 10038,/* conflict in OPEN and I/O*/ NFS4ERR_BADOWNER = 10039,/* owner translation bad */ NFS4ERR_BADCHAR = 10040,/* UTF-8 char not supported*/ NFS4ERR_BADNAME = 10041,/* name not supported */ NFS4ERR_BAD_RANGE = 10042,/* lock range not supported*/ NFS4ERR_LOCK_NOTSUPP = 10043,/* no atomic up/downgrade */ NFS4ERR_OP_ILLEGAL = 10044,/* undefined operation */ NFS4ERR_DEADLOCK = 10045,/* file locking deadlock */ NFS4ERR_FILE_OPEN = 10046,/* open file blocks op. */ NFS4ERR_ADMIN_REVOKED = 10047,/* lock-owner state revoked */ NFS4ERR_CB_PATH_DOWN = 10048 /* callback path down */ }; /* * Basic data types */ typedef opaque attrlist4<>; typedef uint32_t bitmap4<>; typedef uint64_t changeid4; typedef uint64_t clientid4; typedef uint32_t count4; typedef uint64_t length4; typedef uint32_t mode4; typedef uint64_t nfs_cookie4; typedef opaque nfs_fh4; typedef uint32_t nfs_lease4; typedef uint64_t offset4; typedef uint32_t qop4; typedef opaque sec_oid4<>; typedef uint32_t seqid4; typedef opaque utf8string<>; typedef utf8string utf8str_cis; typedef utf8string utf8str_cs; typedef utf8string utf8str_mixed; typedef utf8str_cs component4; typedef opaque linktext4<>; typedef utf8string ascii_REQUIRED4; typedef component4 pathname4<>; typedef uint64_t nfs_lockid4; typedef opaque verifier4[NFS4_VERIFIER_SIZE]; /* * Timeval */ struct nfstime4 { int64_t seconds; uint32_t nseconds; }; enum time_how4 { SET_TO_SERVER_TIME4 = 0, SET_TO_CLIENT_TIME4 = 1 }; union settime4 switch (time_how4 set_it) { case SET_TO_CLIENT_TIME4: nfstime4 time; default: void; }; /* * File access handle */ typedef opaque nfs_fh4; /* * File attribute definitions */ /* * FSID structure for major/minor */ struct fsid4 { uint64_t major; uint64_t minor; }; /* * Filesystem locations attribute for relocation/migration */ struct fs_location4 { utf8str_cis server<>; pathname4 rootpath; }; struct fs_locations4 { pathname4 fs_root; fs_location4 locations<>; }; /* * Various Access Control Entry definitions */ /* * Mask that indicates which Access Control Entries * are supported. Values for the fattr4_aclsupport attribute. */ const ACL4_SUPPORT_ALLOW_ACL = 0x00000001; const ACL4_SUPPORT_DENY_ACL = 0x00000002; const ACL4_SUPPORT_AUDIT_ACL = 0x00000004; const ACL4_SUPPORT_ALARM_ACL = 0x00000008; typedef uint32_t acetype4; /* * acetype4 values; others can be added as needed. */ const ACE4_ACCESS_ALLOWED_ACE_TYPE = 0x00000000; const ACE4_ACCESS_DENIED_ACE_TYPE = 0x00000001; const ACE4_SYSTEM_AUDIT_ACE_TYPE = 0x00000002; const ACE4_SYSTEM_ALARM_ACE_TYPE = 0x00000003; /* * ACE flag */ typedef uint32_t aceflag4; /* * ACE flag values */ const ACE4_FILE_INHERIT_ACE = 0x00000001; const ACE4_DIRECTORY_INHERIT_ACE = 0x00000002; const ACE4_NO_PROPAGATE_INHERIT_ACE = 0x00000004; const ACE4_INHERIT_ONLY_ACE = 0x00000008; const ACE4_SUCCESSFUL_ACCESS_ACE_FLAG = 0x00000010; const ACE4_FAILED_ACCESS_ACE_FLAG = 0x00000020; const ACE4_IDENTIFIER_GROUP = 0x00000040; /* * ACE mask */ typedef uint32_t acemask4; /* * ACE mask values */ const ACE4_READ_DATA = 0x00000001; const ACE4_LIST_DIRECTORY = 0x00000001; const ACE4_WRITE_DATA = 0x00000002; const ACE4_ADD_FILE = 0x00000002; const ACE4_APPEND_DATA = 0x00000004; const ACE4_ADD_SUBDIRECTORY = 0x00000004; const ACE4_READ_NAMED_ATTRS = 0x00000008; const ACE4_WRITE_NAMED_ATTRS = 0x00000010; const ACE4_EXECUTE = 0x00000020; const ACE4_DELETE_CHILD = 0x00000040; const ACE4_READ_ATTRIBUTES = 0x00000080; const ACE4_WRITE_ATTRIBUTES = 0x00000100; const ACE4_DELETE = 0x00010000; const ACE4_READ_ACL = 0x00020000; const ACE4_WRITE_ACL = 0x00040000; const ACE4_WRITE_OWNER = 0x00080000; const ACE4_SYNCHRONIZE = 0x00100000; /* * ACE4_GENERIC_READ - defined as a combination of * ACE4_READ_ACL | * ACE4_READ_DATA | * ACE4_READ_ATTRIBUTES | * ACE4_SYNCHRONIZE */ const ACE4_GENERIC_READ = 0x00120081; /* * ACE4_GENERIC_WRITE - defined as a combination of * ACE4_READ_ACL | * ACE4_WRITE_DATA | * ACE4_WRITE_ATTRIBUTES | * ACE4_WRITE_ACL | * ACE4_APPEND_DATA | * ACE4_SYNCHRONIZE */ const ACE4_GENERIC_WRITE = 0x00160106; /* * ACE4_GENERIC_EXECUTE - defined as a combination of * ACE4_READ_ACL * ACE4_READ_ATTRIBUTES * ACE4_EXECUTE * ACE4_SYNCHRONIZE */ const ACE4_GENERIC_EXECUTE = 0x001200A0; /* * Access Control Entry definition */ struct nfsace4 { acetype4 type; aceflag4 flag; acemask4 access_mask; utf8str_mixed who; }; /* * Field definitions for the fattr4_mode attribute */ const MODE4_SUID = 0x800; /* set user id on execution */ const MODE4_SGID = 0x400; /* set group id on execution */ const MODE4_SVTX = 0x200; /* save text even after use */ const MODE4_RUSR = 0x100; /* read permission: owner */ const MODE4_WUSR = 0x080; /* write permission: owner */ const MODE4_XUSR = 0x040; /* execute permission: owner */ const MODE4_RGRP = 0x020; /* read permission: group */ const MODE4_WGRP = 0x010; /* write permission: group */ const MODE4_XGRP = 0x008; /* execute permission: group */ const MODE4_ROTH = 0x004; /* read permission: other */ const MODE4_WOTH = 0x002; /* write permission: other */ const MODE4_XOTH = 0x001; /* execute permission: other */ /* * Special data/attribute associated with * file types NF4BLK and NF4CHR. */ struct specdata4 { uint32_t specdata1; /* major device number */ uint32_t specdata2; /* minor device number */ }; /* * Values for fattr4_fh_expire_type */ const FH4_PERSISTENT = 0x00000000; const FH4_NOEXPIRE_WITH_OPEN = 0x00000001; const FH4_VOLATILE_ANY = 0x00000002; const FH4_VOL_MIGRATION = 0x00000004; const FH4_VOL_RENAME = 0x00000008; typedef bitmap4 fattr4_supported_attrs; typedef nfs_ftype4 fattr4_type; typedef uint32_t fattr4_fh_expire_type; typedef changeid4 fattr4_change; typedef uint64_t fattr4_size; typedef bool fattr4_link_support; typedef bool fattr4_symlink_support; typedef bool fattr4_named_attr; typedef fsid4 fattr4_fsid; typedef bool fattr4_unique_handles; typedef nfs_lease4 fattr4_lease_time; typedef nfsstat4 fattr4_rdattr_error; typedef nfsace4 fattr4_acl<>; typedef uint32_t fattr4_aclsupport; typedef bool fattr4_archive; typedef bool fattr4_cansettime; typedef bool fattr4_case_insensitive; typedef bool fattr4_case_preserving; typedef bool fattr4_chown_restricted; typedef uint64_t fattr4_fileid; typedef uint64_t fattr4_files_avail; typedef nfs_fh4 fattr4_filehandle; typedef uint64_t fattr4_files_free; typedef uint64_t fattr4_files_total; typedef fs_locations4 fattr4_fs_locations; typedef bool fattr4_hidden; typedef bool fattr4_homogeneous; typedef uint64_t fattr4_maxfilesize; typedef uint32_t fattr4_maxlink; typedef uint32_t fattr4_maxname; typedef uint64_t fattr4_maxread; typedef uint64_t fattr4_maxwrite; typedef ascii_REQUIRED4 fattr4_mimetype; typedef mode4 fattr4_mode; typedef uint64_t fattr4_mounted_on_fileid; typedef bool fattr4_no_trunc; typedef uint32_t fattr4_numlinks; typedef utf8str_mixed fattr4_owner; typedef utf8str_mixed fattr4_owner_group; typedef uint64_t fattr4_quota_avail_hard; typedef uint64_t fattr4_quota_avail_soft; typedef uint64_t fattr4_quota_used; typedef specdata4 fattr4_rawdev; typedef uint64_t fattr4_space_avail; typedef uint64_t fattr4_space_free; typedef uint64_t fattr4_space_total; typedef uint64_t fattr4_space_used; typedef bool fattr4_system; typedef nfstime4 fattr4_time_access; typedef settime4 fattr4_time_access_set; typedef nfstime4 fattr4_time_backup; typedef nfstime4 fattr4_time_create; typedef nfstime4 fattr4_time_delta; typedef nfstime4 fattr4_time_metadata; typedef nfstime4 fattr4_time_modify; typedef settime4 fattr4_time_modify_set; /* * Mandatory attributes */ const FATTR4_SUPPORTED_ATTRS = 0; const FATTR4_TYPE = 1; const FATTR4_FH_EXPIRE_TYPE = 2; const FATTR4_CHANGE = 3; const FATTR4_SIZE = 4; const FATTR4_LINK_SUPPORT = 5; const FATTR4_SYMLINK_SUPPORT = 6; const FATTR4_NAMED_ATTR = 7; const FATTR4_FSID = 8; const FATTR4_UNIQUE_HANDLES = 9; const FATTR4_LEASE_TIME = 10; const FATTR4_RDATTR_ERROR = 11; const FATTR4_FILEHANDLE = 19; /* * Recommended attributes */ const FATTR4_ACL = 12; const FATTR4_ACLSUPPORT = 13; const FATTR4_ARCHIVE = 14; const FATTR4_CANSETTIME = 15; const FATTR4_CASE_INSENSITIVE = 16; const FATTR4_CASE_PRESERVING = 17; const FATTR4_CHOWN_RESTRICTED = 18; const FATTR4_FILEID = 20; const FATTR4_FILES_AVAIL = 21; const FATTR4_FILES_FREE = 22; const FATTR4_FILES_TOTAL = 23; const FATTR4_FS_LOCATIONS = 24; const FATTR4_HIDDEN = 25; const FATTR4_HOMOGENEOUS = 26; const FATTR4_MAXFILESIZE = 27; const FATTR4_MAXLINK = 28; const FATTR4_MAXNAME = 29; const FATTR4_MAXREAD = 30; const FATTR4_MAXWRITE = 31; const FATTR4_MIMETYPE = 32; const FATTR4_MODE = 33; const FATTR4_NO_TRUNC = 34; const FATTR4_NUMLINKS = 35; const FATTR4_OWNER = 36; const FATTR4_OWNER_GROUP = 37; const FATTR4_QUOTA_AVAIL_HARD = 38; const FATTR4_QUOTA_AVAIL_SOFT = 39; const FATTR4_QUOTA_USED = 40; const FATTR4_RAWDEV = 41; const FATTR4_SPACE_AVAIL = 42; const FATTR4_SPACE_FREE = 43; const FATTR4_SPACE_TOTAL = 44; const FATTR4_SPACE_USED = 45; const FATTR4_SYSTEM = 46; const FATTR4_TIME_ACCESS = 47; const FATTR4_TIME_ACCESS_SET = 48; const FATTR4_TIME_BACKUP = 49; const FATTR4_TIME_CREATE = 50; const FATTR4_TIME_DELTA = 51; const FATTR4_TIME_METADATA = 52; const FATTR4_TIME_MODIFY = 53; const FATTR4_TIME_MODIFY_SET = 54; const FATTR4_MOUNTED_ON_FILEID = 55; /* * File attribute container */ struct fattr4 { bitmap4 attrmask; attrlist4 attr_vals; }; /* * Change info for the client */ struct change_info4 { bool atomic; changeid4 before; changeid4 after; }; struct clientaddr4 { /* see struct rpcb in RFC 1833 */ string r_netid<>; /* network id */ string r_addr<>; /* universal address */ }; /* * Callback program info as provided by the client */ struct cb_client4 { uint32_t cb_program; clientaddr4 cb_location; }; /* * Stateid */ struct stateid4 { uint32_t seqid; opaque other[NFS4_OTHER_SIZE]; }; /* * Client ID */ struct nfs_client_id4 { verifier4 verifier; opaque id; }; struct open_owner4 { clientid4 clientid; opaque owner; }; struct lock_owner4 { clientid4 clientid; opaque owner; }; enum nfs_lock_type4 { READ_LT = 1, WRITE_LT = 2, READW_LT = 3, /* blocking read */ WRITEW_LT = 4 /* blocking write */ }; /* * ACCESS: Check access permission */ const ACCESS4_READ = 0x00000001; const ACCESS4_LOOKUP = 0x00000002; const ACCESS4_MODIFY = 0x00000004; const ACCESS4_EXTEND = 0x00000008; const ACCESS4_DELETE = 0x00000010; const ACCESS4_EXECUTE = 0x00000020; struct ACCESS4args { /* CURRENT_FH: object */ uint32_t access; }; struct ACCESS4resok { uint32_t supported; uint32_t access; }; union ACCESS4res switch (nfsstat4 status) { case NFS4_OK: ACCESS4resok resok4; default: void; }; /* * CLOSE: Close a file and release share reservations */ struct CLOSE4args { /* CURRENT_FH: object */ seqid4 seqid; stateid4 open_stateid; }; union CLOSE4res switch (nfsstat4 status) { case NFS4_OK: stateid4 open_stateid; default: void; }; /* * COMMIT: Commit cached data on server to stable storage */ struct COMMIT4args { /* CURRENT_FH: file */ offset4 offset; count4 count; }; struct COMMIT4resok { verifier4 writeverf; }; union COMMIT4res switch (nfsstat4 status) { case NFS4_OK: COMMIT4resok resok4; default: void; }; /* * CREATE: Create a non-regular file */ union createtype4 switch (nfs_ftype4 type) { case NF4LNK: linktext4 linkdata; case NF4BLK: case NF4CHR: specdata4 devdata; case NF4SOCK: case NF4FIFO: case NF4DIR: void; default: void; /* server should return NFS4ERR_BADTYPE */ }; struct CREATE4args { /* CURRENT_FH: directory for creation */ createtype4 objtype; component4 objname; fattr4 createattrs; }; struct CREATE4resok { change_info4 cinfo; bitmap4 attrset; /* attributes set */ }; union CREATE4res switch (nfsstat4 status) { case NFS4_OK: CREATE4resok resok4; default: void; }; /* * DELEGPURGE: Purge Delegations Awaiting Recovery */ struct DELEGPURGE4args { clientid4 clientid; }; struct DELEGPURGE4res { nfsstat4 status; }; /* * DELEGRETURN: Return a delegation */ struct DELEGRETURN4args { /* CURRENT_FH: delegated file */ stateid4 deleg_stateid; }; struct DELEGRETURN4res { nfsstat4 status; }; /* * GETATTR: Get file attributes */ struct GETATTR4args { /* CURRENT_FH: directory or file */ bitmap4 attr_request; }; struct GETATTR4resok { fattr4 obj_attributes; }; union GETATTR4res switch (nfsstat4 status) { case NFS4_OK: GETATTR4resok resok4; default: void; }; /* * GETFH: Get current filehandle */ struct GETFH4resok { nfs_fh4 object; }; union GETFH4res switch (nfsstat4 status) { case NFS4_OK: GETFH4resok resok4; default: void; }; /* * LINK: Create link to an object */ struct LINK4args { /* SAVED_FH: source object */ /* CURRENT_FH: target directory */ component4 newname; }; struct LINK4resok { change_info4 cinfo; }; union LINK4res switch (nfsstat4 status) { case NFS4_OK: LINK4resok resok4; default: void; }; /* * For LOCK, transition from open_owner to new lock_owner */ struct open_to_lock_owner4 { seqid4 open_seqid; stateid4 open_stateid; seqid4 lock_seqid; lock_owner4 lock_owner; }; /* * For LOCK, existing lock_owner continues to request file locks */ struct exist_lock_owner4 { stateid4 lock_stateid; seqid4 lock_seqid; }; union locker4 switch (bool new_lock_owner) { case TRUE: open_to_lock_owner4 open_owner; case FALSE: exist_lock_owner4 lock_owner; }; /* * LOCK/LOCKT/LOCKU: Record lock management */ struct LOCK4args { /* CURRENT_FH: file */ nfs_lock_type4 locktype; bool reclaim; offset4 offset; length4 length; locker4 locker; }; struct LOCK4denied { offset4 offset; length4 length; nfs_lock_type4 locktype; lock_owner4 owner; }; struct LOCK4resok { stateid4 lock_stateid; }; union LOCK4res switch (nfsstat4 status) { case NFS4_OK: LOCK4resok resok4; case NFS4ERR_DENIED: LOCK4denied denied; default: void; }; struct LOCKT4args { /* CURRENT_FH: file */ nfs_lock_type4 locktype; offset4 offset; length4 length; lock_owner4 owner; }; union LOCKT4res switch (nfsstat4 status) { case NFS4ERR_DENIED: LOCK4denied denied; case NFS4_OK: void; default: void; }; struct LOCKU4args { /* CURRENT_FH: file */ nfs_lock_type4 locktype; seqid4 seqid; stateid4 lock_stateid; offset4 offset; length4 length; }; union LOCKU4res switch (nfsstat4 status) { case NFS4_OK: stateid4 lock_stateid; default: void; }; /* * LOOKUP: Lookup filename */ struct LOOKUP4args { /* CURRENT_FH: directory */ component4 objname; }; struct LOOKUP4res { /* CURRENT_FH: object */ nfsstat4 status; }; /* * LOOKUPP: Lookup parent directory */ struct LOOKUPP4res { /* CURRENT_FH: directory */ nfsstat4 status; }; /* * NVERIFY: Verify attributes different */ struct NVERIFY4args { /* CURRENT_FH: object */ fattr4 obj_attributes; }; struct NVERIFY4res { nfsstat4 status; }; /* * Share Access and Deny constants for open argument */ const OPEN4_SHARE_ACCESS_READ = 0x00000001; const OPEN4_SHARE_ACCESS_WRITE = 0x00000002; const OPEN4_SHARE_ACCESS_BOTH = 0x00000003; const OPEN4_SHARE_DENY_NONE = 0x00000000; const OPEN4_SHARE_DENY_READ = 0x00000001; const OPEN4_SHARE_DENY_WRITE = 0x00000002; const OPEN4_SHARE_DENY_BOTH = 0x00000003; /* * Various definitions for OPEN */ enum createmode4 { UNCHECKED4 = 0, GUARDED4 = 1, EXCLUSIVE4 = 2 }; union createhow4 switch (createmode4 mode) { case UNCHECKED4: case GUARDED4: fattr4 createattrs; case EXCLUSIVE4: verifier4 createverf; }; enum opentype4 { OPEN4_NOCREATE = 0, OPEN4_CREATE = 1 }; union openflag4 switch (opentype4 opentype) { case OPEN4_CREATE: createhow4 how; default: void; }; /* Next definitions used for OPEN delegation */ enum limit_by4 { NFS_LIMIT_SIZE = 1, NFS_LIMIT_BLOCKS = 2 /* others as needed */ }; struct nfs_modified_limit4 { uint32_t num_blocks; uint32_t bytes_per_block; }; union nfs_space_limit4 switch (limit_by4 limitby) { /* limit specified as file size */ case NFS_LIMIT_SIZE: uint64_t filesize; /* limit specified by number of blocks */ case NFS_LIMIT_BLOCKS: nfs_modified_limit4 mod_blocks; } ; enum open_delegation_type4 { OPEN_DELEGATE_NONE = 0, OPEN_DELEGATE_READ = 1, OPEN_DELEGATE_WRITE = 2 }; enum open_claim_type4 { CLAIM_NULL = 0, CLAIM_PREVIOUS = 1, CLAIM_DELEGATE_CUR = 2, CLAIM_DELEGATE_PREV = 3 }; struct open_claim_delegate_cur4 { stateid4 delegate_stateid; component4 file; }; union open_claim4 switch (open_claim_type4 claim) { /* * No special rights to file. * Ordinary OPEN of the specified file. */ case CLAIM_NULL: /* CURRENT_FH: directory */ component4 file; /* * Right to the file established by an * open previous to server reboot. File * identified by filehandle obtained at * that time rather than by name. */ case CLAIM_PREVIOUS: /* CURRENT_FH: file being reclaimed */ open_delegation_type4 delegate_type; /* * Right to file based on a delegation * granted by the server. File is * specified by name. */ case CLAIM_DELEGATE_CUR: /* CURRENT_FH: directory */ open_claim_delegate_cur4 delegate_cur_info; /* * Right to file based on a delegation * granted to a previous boot instance * of the client. File is specified by name. */ case CLAIM_DELEGATE_PREV: /* CURRENT_FH: directory */ component4 file_delegate_prev; }; /* * OPEN: Open a file, potentially receiving an open delegation */ struct OPEN4args { seqid4 seqid; uint32_t share_access; uint32_t share_deny; open_owner4 owner; openflag4 openhow; open_claim4 claim; }; struct open_read_delegation4 { stateid4 stateid; /* Stateid for delegation*/ bool recall; /* Pre-recalled flag for delegations obtained by reclaim (CLAIM_PREVIOUS). */ nfsace4 permissions; /* Defines users who don't need an ACCESS call to open for read. */ }; struct open_write_delegation4 { stateid4 stateid; /* Stateid for delegation */ bool recall; /* Pre-recalled flag for delegations obtained by reclaim (CLAIM_PREVIOUS). */ nfs_space_limit4 space_limit; /* Defines condition that the client must check to determine whether the file needs to be flushed to the server on close. */ nfsace4 permissions; /* Defines users who don't need an ACCESS call as part of a delegated open. */ }; union open_delegation4 switch (open_delegation_type4 delegation_type) { case OPEN_DELEGATE_NONE: void; case OPEN_DELEGATE_READ: open_read_delegation4 read; case OPEN_DELEGATE_WRITE: open_write_delegation4 write; }; /* * Result flags */ /* Client must confirm open */ const OPEN4_RESULT_CONFIRM = 0x00000002; /* Type of file locking behavior at the server */ const OPEN4_RESULT_LOCKTYPE_POSIX = 0x00000004; struct OPEN4resok { stateid4 stateid; /* Stateid for open */ change_info4 cinfo; /* Directory change info */ uint32_t rflags; /* Result flags */ bitmap4 attrset; /* attribute set for create*/ open_delegation4 delegation; /* Info on any open delegation */ }; union OPEN4res switch (nfsstat4 status) { case NFS4_OK: /* CURRENT_FH: opened file */ OPEN4resok resok4; default: void; }; /* * OPENATTR: open named attributes directory */ struct OPENATTR4args { /* CURRENT_FH: object */ bool createdir; }; struct OPENATTR4res { /* CURRENT_FH: named attr directory */ nfsstat4 status; }; /* * OPEN_CONFIRM: confirm the open */ struct OPEN_CONFIRM4args { /* CURRENT_FH: opened file */ stateid4 open_stateid; seqid4 seqid; }; struct OPEN_CONFIRM4resok { stateid4 open_stateid; }; union OPEN_CONFIRM4res switch (nfsstat4 status) { case NFS4_OK: OPEN_CONFIRM4resok resok4; default: void; }; /* * OPEN_DOWNGRADE: downgrade the access/deny for a file */ struct OPEN_DOWNGRADE4args { /* CURRENT_FH: opened file */ stateid4 open_stateid; seqid4 seqid; uint32_t share_access; uint32_t share_deny; }; struct OPEN_DOWNGRADE4resok { stateid4 open_stateid; }; union OPEN_DOWNGRADE4res switch(nfsstat4 status) { case NFS4_OK: OPEN_DOWNGRADE4resok resok4; default: void; }; /* * PUTFH: Set current filehandle */ struct PUTFH4args { nfs_fh4 object; }; struct PUTFH4res { /* CURRENT_FH: */ nfsstat4 status; }; /* * PUTPUBFH: Set public filehandle */ struct PUTPUBFH4res { /* CURRENT_FH: public fh */ nfsstat4 status; }; /* * PUTROOTFH: Set root filehandle */ struct PUTROOTFH4res { /* CURRENT_FH: root fh */ nfsstat4 status; }; /* * READ: Read from file */ struct READ4args { /* CURRENT_FH: file */ stateid4 stateid; offset4 offset; count4 count; }; struct READ4resok { bool eof; opaque data<>; }; union READ4res switch (nfsstat4 status) { case NFS4_OK: READ4resok resok4; default: void; }; /* * READDIR: Read directory */ struct READDIR4args { /* CURRENT_FH: directory */ nfs_cookie4 cookie; verifier4 cookieverf; count4 dircount; count4 maxcount; bitmap4 attr_request; }; struct entry4 { nfs_cookie4 cookie; component4 name; fattr4 attrs; entry4 *nextentry; }; struct dirlist4 { entry4 *entries; bool eof; }; struct READDIR4resok { verifier4 cookieverf; dirlist4 reply; }; union READDIR4res switch (nfsstat4 status) { case NFS4_OK: READDIR4resok resok4; default: void; }; /* * READLINK: Read symbolic link */ struct READLINK4resok { linktext4 link; }; union READLINK4res switch (nfsstat4 status) { case NFS4_OK: READLINK4resok resok4; default: void; }; /* * REMOVE: Remove filesystem object */ struct REMOVE4args { /* CURRENT_FH: directory */ component4 target; }; struct REMOVE4resok { change_info4 cinfo; }; union REMOVE4res switch (nfsstat4 status) { case NFS4_OK: REMOVE4resok resok4; default: void; }; /* * RENAME: Rename directory entry */ struct RENAME4args { /* SAVED_FH: source directory */ component4 oldname; /* CURRENT_FH: target directory */ component4 newname; }; struct RENAME4resok { change_info4 source_cinfo; change_info4 target_cinfo; }; union RENAME4res switch (nfsstat4 status) { case NFS4_OK: RENAME4resok resok4; default: void; }; /* * RENEW: Renew a Lease */ struct RENEW4args { clientid4 clientid; }; struct RENEW4res { nfsstat4 status; }; /* * RESTOREFH: Restore saved filehandle */ struct RESTOREFH4res { /* CURRENT_FH: value of saved fh */ nfsstat4 status; }; /* * SAVEFH: Save current filehandle */ struct SAVEFH4res { /* SAVED_FH: value of current fh */ nfsstat4 status; }; /* * SECINFO: Obtain Available Security Mechanisms */ struct SECINFO4args { /* CURRENT_FH: directory */ component4 name; }; /* * From RFC 2203 */ enum rpc_gss_svc_t { RPC_GSS_SVC_NONE = 1, RPC_GSS_SVC_INTEGRITY = 2, RPC_GSS_SVC_PRIVACY = 3 }; struct rpcsec_gss_info { sec_oid4 oid; qop4 qop; rpc_gss_svc_t service; }; /* RPCSEC_GSS has a value of '6'. See RFC 2203 */ union secinfo4 switch (uint32_t flavor) { case RPCSEC_GSS: rpcsec_gss_info flavor_info; default: void; }; typedef secinfo4 SECINFO4resok<>; union SECINFO4res switch (nfsstat4 status) { case NFS4_OK: SECINFO4resok resok4; default: void; }; /* * SETATTR: Set attributes */ struct SETATTR4args { /* CURRENT_FH: target object */ stateid4 stateid; fattr4 obj_attributes; }; struct SETATTR4res { nfsstat4 status; bitmap4 attrsset; }; /* * SETCLIENTID */ struct SETCLIENTID4args { nfs_client_id4 client; cb_client4 callback; uint32_t callback_ident; }; struct SETCLIENTID4resok { clientid4 clientid; verifier4 setclientid_confirm; }; union SETCLIENTID4res switch (nfsstat4 status) { case NFS4_OK: SETCLIENTID4resok resok4; case NFS4ERR_CLID_INUSE: clientaddr4 client_using; default: void; }; struct SETCLIENTID_CONFIRM4args { clientid4 clientid; verifier4 setclientid_confirm; }; struct SETCLIENTID_CONFIRM4res { nfsstat4 status; }; /* * VERIFY: Verify attributes same */ struct VERIFY4args { /* CURRENT_FH: object */ fattr4 obj_attributes; }; struct VERIFY4res { nfsstat4 status; }; /* * WRITE: Write to file */ enum stable_how4 { UNSTABLE4 = 0, DATA_SYNC4 = 1, FILE_SYNC4 = 2 }; struct WRITE4args { /* CURRENT_FH: file */ stateid4 stateid; offset4 offset; stable_how4 stable; opaque data<>; }; struct WRITE4resok { count4 count; stable_how4 committed; verifier4 writeverf; }; union WRITE4res switch (nfsstat4 status) { case NFS4_OK: WRITE4resok resok4; default: void; }; /* * RELEASE_LOCKOWNER: Notify server to release lockowner */ struct RELEASE_LOCKOWNER4args { lock_owner4 lock_owner; }; struct RELEASE_LOCKOWNER4res { nfsstat4 status; }; /* * ILLEGAL: Response for illegal operation numbers */ struct ILLEGAL4res { nfsstat4 status; }; /* * Operation arrays */ enum nfs_opnum4 { OP_ACCESS = 3, OP_CLOSE = 4, OP_COMMIT = 5, OP_CREATE = 6, OP_DELEGPURGE = 7, OP_DELEGRETURN = 8, OP_GETATTR = 9, OP_GETFH = 10, OP_LINK = 11, OP_LOCK = 12, OP_LOCKT = 13, OP_LOCKU = 14, OP_LOOKUP = 15, OP_LOOKUPP = 16, OP_NVERIFY = 17, OP_OPEN = 18, OP_OPENATTR = 19, OP_OPEN_CONFIRM = 20, OP_OPEN_DOWNGRADE = 21, OP_PUTFH = 22, OP_PUTPUBFH = 23, OP_PUTROOTFH = 24, OP_READ = 25, OP_READDIR = 26, OP_READLINK = 27, OP_REMOVE = 28, OP_RENAME = 29, OP_RENEW = 30, OP_RESTOREFH = 31, OP_SAVEFH = 32, OP_SECINFO = 33, OP_SETATTR = 34, OP_SETCLIENTID = 35, OP_SETCLIENTID_CONFIRM = 36, OP_VERIFY = 37, OP_WRITE = 38, OP_RELEASE_LOCKOWNER = 39, OP_ILLEGAL = 10044 }; union nfs_argop4 switch (nfs_opnum4 argop) { case OP_ACCESS: ACCESS4args opaccess; case OP_CLOSE: CLOSE4args opclose; case OP_COMMIT: COMMIT4args opcommit; case OP_CREATE: CREATE4args opcreate; case OP_DELEGPURGE: DELEGPURGE4args opdelegpurge; case OP_DELEGRETURN: DELEGRETURN4args opdelegreturn; case OP_GETATTR: GETATTR4args opgetattr; case OP_GETFH: void; case OP_LINK: LINK4args oplink; case OP_LOCK: LOCK4args oplock; case OP_LOCKT: LOCKT4args oplockt; case OP_LOCKU: LOCKU4args oplocku; case OP_LOOKUP: LOOKUP4args oplookup; case OP_LOOKUPP: void; case OP_NVERIFY: NVERIFY4args opnverify; case OP_OPEN: OPEN4args opopen; case OP_OPENATTR: OPENATTR4args opopenattr; case OP_OPEN_CONFIRM: OPEN_CONFIRM4args opopen_confirm; case OP_OPEN_DOWNGRADE: OPEN_DOWNGRADE4args opopen_downgrade; case OP_PUTFH: PUTFH4args opputfh; case OP_PUTPUBFH: void; case OP_PUTROOTFH: void; case OP_READ: READ4args opread; case OP_READDIR: READDIR4args opreaddir; case OP_READLINK: void; case OP_REMOVE: REMOVE4args opremove; case OP_RENAME: RENAME4args oprename; case OP_RENEW: RENEW4args oprenew; case OP_RESTOREFH: void; case OP_SAVEFH: void; case OP_SECINFO: SECINFO4args opsecinfo; case OP_SETATTR: SETATTR4args opsetattr; case OP_SETCLIENTID: SETCLIENTID4args opsetclientid; case OP_SETCLIENTID_CONFIRM: SETCLIENTID_CONFIRM4args opsetclientid_confirm; case OP_VERIFY: VERIFY4args opverify; case OP_WRITE: WRITE4args opwrite; case OP_RELEASE_LOCKOWNER: RELEASE_LOCKOWNER4args oprelease_lockowner; case OP_ILLEGAL: void; }; union nfs_resop4 switch (nfs_opnum4 resop){ case OP_ACCESS: ACCESS4res opaccess; case OP_CLOSE: CLOSE4res opclose; case OP_COMMIT: COMMIT4res opcommit; case OP_CREATE: CREATE4res opcreate; case OP_DELEGPURGE: DELEGPURGE4res opdelegpurge; case OP_DELEGRETURN: DELEGRETURN4res opdelegreturn; case OP_GETATTR: GETATTR4res opgetattr; case OP_GETFH: GETFH4res opgetfh; case OP_LINK: LINK4res oplink; case OP_LOCK: LOCK4res oplock; case OP_LOCKT: LOCKT4res oplockt; case OP_LOCKU: LOCKU4res oplocku; case OP_LOOKUP: LOOKUP4res oplookup; case OP_LOOKUPP: LOOKUPP4res oplookupp; case OP_NVERIFY: NVERIFY4res opnverify; case OP_OPEN: OPEN4res opopen; case OP_OPENATTR: OPENATTR4res opopenattr; case OP_OPEN_CONFIRM: OPEN_CONFIRM4res opopen_confirm; case OP_OPEN_DOWNGRADE: OPEN_DOWNGRADE4res opopen_downgrade; case OP_PUTFH: PUTFH4res opputfh; case OP_PUTPUBFH: PUTPUBFH4res opputpubfh; case OP_PUTROOTFH: PUTROOTFH4res opputrootfh; case OP_READ: READ4res opread; case OP_READDIR: READDIR4res opreaddir; case OP_READLINK: READLINK4res opreadlink; case OP_REMOVE: REMOVE4res opremove; case OP_RENAME: RENAME4res oprename; case OP_RENEW: RENEW4res oprenew; case OP_RESTOREFH: RESTOREFH4res oprestorefh; case OP_SAVEFH: SAVEFH4res opsavefh; case OP_SECINFO: SECINFO4res opsecinfo; case OP_SETATTR: SETATTR4res opsetattr; case OP_SETCLIENTID: SETCLIENTID4res opsetclientid; case OP_SETCLIENTID_CONFIRM: SETCLIENTID_CONFIRM4res opsetclientid_confirm; case OP_VERIFY: VERIFY4res opverify; case OP_WRITE: WRITE4res opwrite; case OP_RELEASE_LOCKOWNER: RELEASE_LOCKOWNER4res oprelease_lockowner; case OP_ILLEGAL: ILLEGAL4res opillegal; }; struct COMPOUND4args { utf8str_cs tag; uint32_t minorversion; nfs_argop4 argarray<>; }; struct COMPOUND4res { nfsstat4 status; utf8str_cs tag; nfs_resop4 resarray<>; }; /* * Remote file service routines */ program NFS4_PROGRAM { version NFS_V4 { void NFSPROC4_NULL(void) = 0; COMPOUND4res NFSPROC4_COMPOUND(COMPOUND4args) = 1; } = 4; } = 100003; /* * NFS4 callback procedure definitions and program */ /* * CB_GETATTR: Get Current Attributes */ struct CB_GETATTR4args { nfs_fh4 fh; bitmap4 attr_request; }; struct CB_GETATTR4resok { fattr4 obj_attributes; }; union CB_GETATTR4res switch (nfsstat4 status) { case NFS4_OK: CB_GETATTR4resok resok4; default: void; }; /* * CB_RECALL: Recall an Open Delegation */ struct CB_RECALL4args { stateid4 stateid; bool truncate; nfs_fh4 fh; }; struct CB_RECALL4res { nfsstat4 status; }; /* * CB_ILLEGAL: Response for illegal operation numbers */ struct CB_ILLEGAL4res { nfsstat4 status; }; /* * Various definitions for CB_COMPOUND */ enum nfs_cb_opnum4 { NFS4_OP_CB_GETATTR = 3, NFS4_OP_CB_RECALL = 4, NFS4_OP_CB_ILLEGAL = 10044 }; union nfs_cb_argop4 switch (unsigned argop) { case NFS4_OP_CB_GETATTR: CB_GETATTR4args opcbgetattr; case NFS4_OP_CB_RECALL: CB_RECALL4args opcbrecall; case NFS4_OP_CB_ILLEGAL: void; }; union nfs_cb_resop4 switch (unsigned resop){ case NFS4_OP_CB_GETATTR: CB_GETATTR4res opcbgetattr; case NFS4_OP_CB_RECALL: CB_RECALL4res opcbrecall; case NFS4_OP_CB_ILLEGAL: CB_ILLEGAL4res opcbillegal; }; struct CB_COMPOUND4args { utf8str_cs tag; uint32_t minorversion; uint32_t callback_ident; nfs_cb_argop4 argarray<>; }; struct CB_COMPOUND4res { nfsstat4 status; utf8str_cs tag; nfs_cb_resop4 resarray<>; }; /* * Program number is in the transient range, since the client * will assign the exact transient program number and provide * that to the server via the SETCLIENTID operation. */ program NFS4_CALLBACK { version NFS_CB { void CB_NULL(void) = 0; CB_COMPOUND4res CB_COMPOUND(CB_COMPOUND4args) = 1; } = 1; } = 0x40000000; nfs-ganesha-2.6.0/src/Protocols/XDR/nfsv41.x000066400000000000000000002522011324272410200204320ustar00rootroot00000000000000/* * Copyright (c) 2010 IETF Trust and the persons identified * as the document authors. All rights reserved. * * The document authors are identified in RFC 3530 and * RFC 5661. * * Redistribution and use in source and binary forms, with * or without modification, are permitted provided that the * following conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other * materials provided with the distribution. * * - Neither the name of Internet Society, IETF or IETF * Trust, nor the names of specific contributors, may be * used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS * AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * This code was derived from RFC 3530. Please * reproduce this note if possible. * * This code was derived from RFC 5661. Please * reproduce this note if possible. * * This file was machine generated from RFC 5662. */ /* * nfs4_prot.x */ %#ifndef _AUTH_SYS_DEFINE_FOR_NFSv41 %#define _AUTH_SYS_DEFINE_FOR_NFSv41 %#include %typedef struct authsys_parms authsys_parms; %#endif /* _AUTH_SYS_DEFINE_FOR_NFSv41 */ /* * Basic typedefs for RFC 1832 data type definitions */ /* * typedef int int32_t; * typedef unsigned int uint32_t; * typedef hyper int64_t; * typedef unsigned hyper uint64_t; */ /* * Sizes */ const NFS4_FHSIZE = 128; const NFS4_VERIFIER_SIZE = 8; const NFS4_OPAQUE_LIMIT = 1024; const NFS4_SESSIONID_SIZE = 16; const NFS4_INT64_MAX = 0x7fffffffffffffff; const NFS4_UINT64_MAX = 0xffffffffffffffff; const NFS4_INT32_MAX = 0x7fffffff; const NFS4_UINT32_MAX = 0xffffffff; const NFS4_MAXFILELEN = 0xffffffffffffffff; const NFS4_MAXFILEOFF = 0xfffffffffffffffe; /* * File types */ enum nfs_ftype4 { NF4REG = 1, /* Regular File */ NF4DIR = 2, /* Directory */ NF4BLK = 3, /* Special File - block device */ NF4CHR = 4, /* Special File - character device */ NF4LNK = 5, /* Symbolic Link */ NF4SOCK = 6, /* Special File - socket */ NF4FIFO = 7, /* Special File - fifo */ NF4ATTRDIR = 8, /* Attribute Directory */ NF4NAMEDATTR = 9 /* Named Attribute */ }; /* * Error status */ enum nfsstat4 { NFS4_OK = 0, /* everything is okay */ NFS4ERR_PERM = 1, /* caller not privileged */ NFS4ERR_NOENT = 2, /* no such file/directory */ NFS4ERR_IO = 5, /* hard I/O error */ NFS4ERR_NXIO = 6, /* no such device */ NFS4ERR_ACCESS = 13, /* access denied */ NFS4ERR_EXIST = 17, /* file already exists */ NFS4ERR_XDEV = 18, /* different filesystems */ /* * Please do not allocate value 19; it was used in NFSv3 * and we do not want a value in NFSv3 to have a different * meaning in NFSv4.x. */ NFS4ERR_NOTDIR = 20, /* should be a directory */ NFS4ERR_ISDIR = 21, /* should not be directory */ NFS4ERR_INVAL = 22, /* invalid argument */ NFS4ERR_FBIG = 27, /* file exceeds server max */ NFS4ERR_NOSPC = 28, /* no space on filesystem */ NFS4ERR_ROFS = 30, /* read-only filesystem */ NFS4ERR_MLINK = 31, /* too many hard links */ NFS4ERR_NAMETOOLONG = 63, /* name exceeds server max */ NFS4ERR_NOTEMPTY = 66, /* directory not empty */ NFS4ERR_DQUOT = 69, /* hard quota limit reached*/ NFS4ERR_STALE = 70, /* file no longer exists */ NFS4ERR_BADHANDLE = 10001,/* Illegal filehandle */ NFS4ERR_BAD_COOKIE = 10003,/* READDIR cookie is stale */ NFS4ERR_NOTSUPP = 10004,/* operation not supported */ NFS4ERR_TOOSMALL = 10005,/* response limit exceeded */ NFS4ERR_SERVERFAULT = 10006,/* undefined server error */ NFS4ERR_BADTYPE = 10007,/* type invalid for CREATE */ NFS4ERR_DELAY = 10008,/* file "busy" - retry */ NFS4ERR_SAME = 10009,/* nverify says attrs same */ NFS4ERR_DENIED = 10010,/* lock unavailable */ NFS4ERR_EXPIRED = 10011,/* lock lease expired */ NFS4ERR_LOCKED = 10012,/* I/O failed due to lock */ NFS4ERR_GRACE = 10013,/* in grace period */ NFS4ERR_FHEXPIRED = 10014,/* filehandle expired */ NFS4ERR_SHARE_DENIED = 10015,/* share reserve denied */ NFS4ERR_WRONGSEC = 10016,/* wrong security flavor */ NFS4ERR_CLID_INUSE = 10017,/* clientid in use */ /* NFS4ERR_RESOURCE is not a valid error in NFSv4.1 */ NFS4ERR_RESOURCE = 10018,/* resource exhaustion */ NFS4ERR_MOVED = 10019,/* filesystem relocated */ NFS4ERR_NOFILEHANDLE = 10020,/* current FH is not set */ NFS4ERR_MINOR_VERS_MISMATCH= 10021,/* minor vers not supp */ NFS4ERR_STALE_CLIENTID = 10022,/* server has rebooted */ NFS4ERR_STALE_STATEID = 10023,/* server has rebooted */ NFS4ERR_OLD_STATEID = 10024,/* state is out of sync */ NFS4ERR_BAD_STATEID = 10025,/* incorrect stateid */ NFS4ERR_BAD_SEQID = 10026,/* request is out of seq. */ NFS4ERR_NOT_SAME = 10027,/* verify - attrs not same */ NFS4ERR_LOCK_RANGE = 10028,/* overlapping lock range */ NFS4ERR_SYMLINK = 10029,/* should be file/directory*/ NFS4ERR_RESTOREFH = 10030,/* no saved filehandle */ NFS4ERR_LEASE_MOVED = 10031,/* some filesystem moved */ NFS4ERR_ATTRNOTSUPP = 10032,/* recommended attr not sup*/ NFS4ERR_NO_GRACE = 10033,/* reclaim outside of grace*/ NFS4ERR_RECLAIM_BAD = 10034,/* reclaim error at server */ NFS4ERR_RECLAIM_CONFLICT= 10035,/* conflict on reclaim */ NFS4ERR_BADXDR = 10036,/* XDR decode failed */ NFS4ERR_LOCKS_HELD = 10037,/* file locks held at CLOSE*/ NFS4ERR_OPENMODE = 10038,/* conflict in OPEN and I/O*/ NFS4ERR_BADOWNER = 10039,/* owner translation bad */ NFS4ERR_BADCHAR = 10040,/* utf-8 char not supported*/ NFS4ERR_BADNAME = 10041,/* name not supported */ NFS4ERR_BAD_RANGE = 10042,/* lock range not supported*/ NFS4ERR_LOCK_NOTSUPP = 10043,/* no atomic up/downgrade */ NFS4ERR_OP_ILLEGAL = 10044,/* undefined operation */ NFS4ERR_DEADLOCK = 10045,/* file locking deadlock */ NFS4ERR_FILE_OPEN = 10046,/* open file blocks op. */ NFS4ERR_ADMIN_REVOKED = 10047,/* lockowner state revoked */ NFS4ERR_CB_PATH_DOWN = 10048,/* callback path down */ /* NFSv4.1 errors start here. */ NFS4ERR_BADIOMODE = 10049, NFS4ERR_BADLAYOUT = 10050, NFS4ERR_BAD_SESSION_DIGEST = 10051, NFS4ERR_BADSESSION = 10052, NFS4ERR_BADSLOT = 10053, NFS4ERR_COMPLETE_ALREADY = 10054, NFS4ERR_CONN_NOT_BOUND_TO_SESSION = 10055, NFS4ERR_DELEG_ALREADY_WANTED = 10056, NFS4ERR_BACK_CHAN_BUSY = 10057,/*backchan reqs outstanding*/ NFS4ERR_LAYOUTTRYLATER = 10058, NFS4ERR_LAYOUTUNAVAILABLE = 10059, NFS4ERR_NOMATCHING_LAYOUT = 10060, NFS4ERR_RECALLCONFLICT = 10061, NFS4ERR_UNKNOWN_LAYOUTTYPE = 10062, NFS4ERR_SEQ_MISORDERED = 10063,/* unexpected seq.ID in req*/ NFS4ERR_SEQUENCE_POS = 10064,/* [CB_]SEQ. op not 1st op */ NFS4ERR_REQ_TOO_BIG = 10065,/* request too big */ NFS4ERR_REP_TOO_BIG = 10066,/* reply too big */ NFS4ERR_REP_TOO_BIG_TO_CACHE =10067,/* rep. not all cached*/ NFS4ERR_RETRY_UNCACHED_REP =10068,/* retry & rep. uncached*/ NFS4ERR_UNSAFE_COMPOUND =10069,/* retry/recovery too hard */ NFS4ERR_TOO_MANY_OPS = 10070,/*too many ops in [CB_]COMP*/ NFS4ERR_OP_NOT_IN_SESSION =10071,/* op needs [CB_]SEQ. op */ NFS4ERR_HASH_ALG_UNSUPP = 10072, /* hash alg. not supp. */ /* Error 10073 is unused. */ NFS4ERR_CLIENTID_BUSY = 10074,/* clientid has state */ NFS4ERR_PNFS_IO_HOLE = 10075,/* IO to _SPARSE file hole */ NFS4ERR_SEQ_FALSE_RETRY= 10076,/* Retry != original req. */ NFS4ERR_BAD_HIGH_SLOT = 10077,/* req has bad highest_slot*/ NFS4ERR_DEADSESSION = 10078,/*new req sent to dead sess*/ NFS4ERR_ENCR_ALG_UNSUPP= 10079,/* encr alg. not supp. */ NFS4ERR_PNFS_NO_LAYOUT = 10080,/* I/O without a layout */ NFS4ERR_NOT_ONLY_OP = 10081,/* addl ops not allowed */ NFS4ERR_WRONG_CRED = 10082,/* op done by wrong cred */ NFS4ERR_WRONG_TYPE = 10083,/* op on wrong type object */ NFS4ERR_DIRDELEG_UNAVAIL=10084,/* delegation not avail. */ NFS4ERR_REJECT_DELEG = 10085,/* cb rejected delegation */ NFS4ERR_RETURNCONFLICT = 10086,/* layout get before return*/ NFS4ERR_DELEG_REVOKED = 10087 /* deleg./layout revoked */ }; /* * Basic data types */ typedef opaque attrlist4<>; typedef uint32_t bitmap4<>; typedef uint64_t changeid4; typedef uint64_t clientid4; typedef uint32_t count4; typedef uint64_t length4; typedef uint32_t mode4; typedef uint64_t nfs_cookie4; typedef opaque nfs_fh4; typedef uint64_t offset4; typedef uint32_t qop4; typedef opaque sec_oid4<>; typedef uint32_t sequenceid4; typedef uint32_t seqid4; typedef opaque sessionid4[NFS4_SESSIONID_SIZE]; typedef uint32_t slotid4; typedef opaque utf8string<>; typedef utf8string utf8str_cis; typedef utf8string utf8str_cs; typedef utf8string utf8str_mixed; typedef utf8str_cs component4; typedef utf8str_cs linktext4; typedef component4 pathname4<>; typedef opaque verifier4[NFS4_VERIFIER_SIZE]; /* * Timeval */ struct nfstime4 { int64_t seconds; uint32_t nseconds; }; enum time_how4 { SET_TO_SERVER_TIME4 = 0, SET_TO_CLIENT_TIME4 = 1 }; union settime4 switch (time_how4 set_it) { case SET_TO_CLIENT_TIME4: nfstime4 time; default: void; }; typedef uint32_t nfs_lease4; /* * File attribute definitions */ /* * FSID structure for major/minor */ struct fsid4 { uint64_t major; uint64_t minor; }; /* * Filesystem locations attribute * for relocation/migration and * related attributes. */ struct change_policy4 { uint64_t cp_major; uint64_t cp_minor; }; struct fs_location4 { utf8str_cis server<>; pathname4 rootpath; }; struct fs_locations4 { pathname4 fs_root; fs_location4 locations<>; }; /* * Various Access Control Entry definitions */ /* * Mask that indicates which * Access Control Entries are supported. * Values for the fattr4_aclsupport attribute. */ const ACL4_SUPPORT_ALLOW_ACL = 0x00000001; const ACL4_SUPPORT_DENY_ACL = 0x00000002; const ACL4_SUPPORT_AUDIT_ACL = 0x00000004; const ACL4_SUPPORT_ALARM_ACL = 0x00000008; typedef uint32_t acetype4; /* * acetype4 values, others can be added as needed. */ const ACE4_ACCESS_ALLOWED_ACE_TYPE = 0x00000000; const ACE4_ACCESS_DENIED_ACE_TYPE = 0x00000001; const ACE4_SYSTEM_AUDIT_ACE_TYPE = 0x00000002; const ACE4_SYSTEM_ALARM_ACE_TYPE = 0x00000003; /* * ACE flag */ typedef uint32_t aceflag4; /* * ACE flag values */ const ACE4_FILE_INHERIT_ACE = 0x00000001; const ACE4_DIRECTORY_INHERIT_ACE = 0x00000002; const ACE4_NO_PROPAGATE_INHERIT_ACE = 0x00000004; const ACE4_INHERIT_ONLY_ACE = 0x00000008; const ACE4_SUCCESSFUL_ACCESS_ACE_FLAG = 0x00000010; const ACE4_FAILED_ACCESS_ACE_FLAG = 0x00000020; const ACE4_IDENTIFIER_GROUP = 0x00000040; const ACE4_INHERITED_ACE = 0x00000080; /* * ACE mask */ typedef uint32_t acemask4; /* * ACE mask values */ const ACE4_READ_DATA = 0x00000001; const ACE4_LIST_DIRECTORY = 0x00000001; const ACE4_WRITE_DATA = 0x00000002; const ACE4_ADD_FILE = 0x00000002; const ACE4_APPEND_DATA = 0x00000004; const ACE4_ADD_SUBDIRECTORY = 0x00000004; const ACE4_READ_NAMED_ATTRS = 0x00000008; const ACE4_WRITE_NAMED_ATTRS = 0x00000010; const ACE4_EXECUTE = 0x00000020; const ACE4_DELETE_CHILD = 0x00000040; const ACE4_READ_ATTRIBUTES = 0x00000080; const ACE4_WRITE_ATTRIBUTES = 0x00000100; const ACE4_WRITE_RETENTION = 0x00000200; const ACE4_WRITE_RETENTION_HOLD = 0x00000400; const ACE4_DELETE = 0x00010000; const ACE4_READ_ACL = 0x00020000; const ACE4_WRITE_ACL = 0x00040000; const ACE4_WRITE_OWNER = 0x00080000; const ACE4_SYNCHRONIZE = 0x00100000; /* * ACE4_GENERIC_READ -- defined as combination of * ACE4_READ_ACL | * ACE4_READ_DATA | * ACE4_READ_ATTRIBUTES | * ACE4_SYNCHRONIZE */ const ACE4_GENERIC_READ = 0x00120081; /* * ACE4_GENERIC_WRITE -- defined as combination of * ACE4_READ_ACL | * ACE4_WRITE_DATA | * ACE4_WRITE_ATTRIBUTES | * ACE4_WRITE_ACL | * ACE4_APPEND_DATA | * ACE4_SYNCHRONIZE */ const ACE4_GENERIC_WRITE = 0x00160106; /* * ACE4_GENERIC_EXECUTE -- defined as combination of * ACE4_READ_ACL * ACE4_READ_ATTRIBUTES * ACE4_EXECUTE * ACE4_SYNCHRONIZE */ const ACE4_GENERIC_EXECUTE = 0x001200A0; /* * Access Control Entry definition */ struct nfsace4 { acetype4 type; aceflag4 flag; acemask4 access_mask; utf8str_mixed who; }; /* * ACL flag */ typedef uint32_t aclflag4; /* * ACL flag values */ const ACL4_AUTO_INHERIT = 0x00000001; const ACL4_PROTECTED = 0x00000002; const ACL4_DEFAULTED = 0x00000004; /* * Version 4.1 Access Control List definition */ struct nfsacl41 { aclflag4 na41_flag; nfsace4 na41_aces<>; }; /* * Field definitions for the fattr4_mode * and fattr4_mode_set_masked attributes. */ const MODE4_SUID = 0x800; /* set user id on execution */ const MODE4_SGID = 0x400; /* set group id on execution */ const MODE4_SVTX = 0x200; /* save text even after use */ const MODE4_RUSR = 0x100; /* read permission: owner */ const MODE4_WUSR = 0x080; /* write permission: owner */ const MODE4_XUSR = 0x040; /* execute permission: owner */ const MODE4_RGRP = 0x020; /* read permission: group */ const MODE4_WGRP = 0x010; /* write permission: group */ const MODE4_XGRP = 0x008; /* execute permission: group */ const MODE4_ROTH = 0x004; /* read permission: other */ const MODE4_WOTH = 0x002; /* write permission: other */ const MODE4_XOTH = 0x001; /* execute permission: other */ /* * Masked mode for the mode_set_masked attribute. */ struct mode_masked4 { mode4 mm_value_to_set; /* Values of bits to set or reset in mode. */ mode4 mm_mask_bits; /* Mask of bits to set or reset in mode. */ }; /* * Special data/attribute associated with * file types NF4BLK and NF4CHR. */ struct specdata4 { uint32_t specdata1; /* major device number */ uint32_t specdata2; /* minor device number */ }; /* * Values for fattr4_fh_expire_type */ const FH4_PERSISTENT = 0x00000000; const FH4_NOEXPIRE_WITH_OPEN = 0x00000001; const FH4_VOLATILE_ANY = 0x00000002; const FH4_VOL_MIGRATION = 0x00000004; const FH4_VOL_RENAME = 0x00000008; struct netaddr4 { /* see struct rpcb in RFC 1833 */ string na_r_netid<>; /* network id */ string na_r_addr<>; /* universal address */ }; /* * data structures new to NFSv4.1 */ struct nfs_impl_id4 { utf8str_cis nii_domain; utf8str_cs nii_name; nfstime4 nii_date; }; /* * Stateid */ struct stateid4 { uint32_t seqid; opaque other[12]; }; enum layouttype4 { LAYOUT4_NFSV4_1_FILES = 0x1, LAYOUT4_OSD2_OBJECTS = 0x2, LAYOUT4_BLOCK_VOLUME = 0x3 }; struct layout_content4 { layouttype4 loc_type; opaque loc_body<>; }; %/* % * LAYOUT4_OSD2_OBJECTS loc_body description % * is in a separate .x file % */ % %/* % * LAYOUT4_BLOCK_VOLUME loc_body description % * is in a separate .x file % */ struct layouthint4 { layouttype4 loh_type; opaque loh_body<>; }; enum layoutiomode4 { LAYOUTIOMODE4_READ = 1, LAYOUTIOMODE4_RW = 2, LAYOUTIOMODE4_ANY = 3 }; struct layout4 { offset4 lo_offset; length4 lo_length; layoutiomode4 lo_iomode; layout_content4 lo_content; }; const NFS4_DEVICEID4_SIZE = 16; typedef opaque deviceid4[NFS4_DEVICEID4_SIZE]; struct device_addr4 { layouttype4 da_layout_type; opaque da_addr_body<>; }; struct layoutupdate4 { layouttype4 lou_type; opaque lou_body<>; }; % /* Constants used for LAYOUTRETURN and CB_LAYOUTRECALL */ const LAYOUT4_RET_REC_FILE = 1; const LAYOUT4_RET_REC_FSID = 2; const LAYOUT4_RET_REC_ALL = 3; % enum layoutreturn_type4 { LAYOUTRETURN4_FILE = LAYOUT4_RET_REC_FILE, LAYOUTRETURN4_FSID = LAYOUT4_RET_REC_FSID, LAYOUTRETURN4_ALL = LAYOUT4_RET_REC_ALL }; struct layoutreturn_file4 { offset4 lrf_offset; length4 lrf_length; stateid4 lrf_stateid; % /* layouttype4 specific data */ opaque lrf_body<>; }; union layoutreturn4 switch(layoutreturn_type4 lr_returntype) { case LAYOUTRETURN4_FILE: layoutreturn_file4 lr_layout; default: void; }; % enum fs4_status_type { STATUS4_FIXED = 1, STATUS4_UPDATED = 2, STATUS4_VERSIONED = 3, STATUS4_WRITABLE = 4, STATUS4_REFERRAL = 5 }; struct fs4_status { bool fss_absent; fs4_status_type fss_type; utf8str_cs fss_source; utf8str_cs fss_current; int32_t fss_age; nfstime4 fss_version; }; const TH4_READ_SIZE = 0; const TH4_WRITE_SIZE = 1; const TH4_READ_IOSIZE = 2; const TH4_WRITE_IOSIZE = 3; typedef length4 threshold4_read_size; typedef length4 threshold4_write_size; typedef length4 threshold4_read_iosize; typedef length4 threshold4_write_iosize; struct threshold_item4 { layouttype4 thi_layout_type; bitmap4 thi_hintset; opaque thi_hintlist<>; }; struct mdsthreshold4 { threshold_item4 mth_hints<>; }; const RET4_DURATION_INFINITE = 0xffffffffffffffff; struct retention_get4 { uint64_t rg_duration; nfstime4 rg_begin_time<1>; }; struct retention_set4 { bool rs_enable; uint64_t rs_duration<1>; }; const FSCHARSET_CAP4_CONTAINS_NON_UTF8 = 0x1; const FSCHARSET_CAP4_ALLOWS_ONLY_UTF8 = 0x2; typedef uint32_t fs_charset_cap4; /* * NFSv4.1 attributes */ typedef bitmap4 fattr4_supported_attrs; typedef nfs_ftype4 fattr4_type; typedef uint32_t fattr4_fh_expire_type; typedef changeid4 fattr4_change; typedef uint64_t fattr4_size; typedef bool fattr4_link_support; typedef bool fattr4_symlink_support; typedef bool fattr4_named_attr; typedef fsid4 fattr4_fsid; typedef bool fattr4_unique_handles; typedef nfs_lease4 fattr4_lease_time; typedef nfsstat4 fattr4_rdattr_error; typedef nfsace4 fattr4_acl<>; typedef uint32_t fattr4_aclsupport; typedef bool fattr4_archive; typedef bool fattr4_cansettime; typedef bool fattr4_case_insensitive; typedef bool fattr4_case_preserving; typedef bool fattr4_chown_restricted; typedef uint64_t fattr4_fileid; typedef uint64_t fattr4_files_avail; typedef nfs_fh4 fattr4_filehandle; typedef uint64_t fattr4_files_free; typedef uint64_t fattr4_files_total; typedef fs_locations4 fattr4_fs_locations; typedef bool fattr4_hidden; typedef bool fattr4_homogeneous; typedef uint64_t fattr4_maxfilesize; typedef uint32_t fattr4_maxlink; typedef uint32_t fattr4_maxname; typedef uint64_t fattr4_maxread; typedef uint64_t fattr4_maxwrite; typedef utf8str_cs fattr4_mimetype; typedef mode4 fattr4_mode; typedef mode_masked4 fattr4_mode_set_masked; typedef uint64_t fattr4_mounted_on_fileid; typedef bool fattr4_no_trunc; typedef uint32_t fattr4_numlinks; typedef utf8str_mixed fattr4_owner; typedef utf8str_mixed fattr4_owner_group; typedef uint64_t fattr4_quota_avail_hard; typedef uint64_t fattr4_quota_avail_soft; typedef uint64_t fattr4_quota_used; typedef specdata4 fattr4_rawdev; typedef uint64_t fattr4_space_avail; typedef uint64_t fattr4_space_free; typedef uint64_t fattr4_space_total; typedef uint64_t fattr4_space_used; typedef bool fattr4_system; typedef nfstime4 fattr4_time_access; typedef settime4 fattr4_time_access_set; typedef nfstime4 fattr4_time_backup; typedef nfstime4 fattr4_time_create; typedef nfstime4 fattr4_time_delta; typedef nfstime4 fattr4_time_metadata; typedef nfstime4 fattr4_time_modify; typedef settime4 fattr4_time_modify_set; /* * attributes new to NFSv4.1 */ typedef bitmap4 fattr4_suppattr_exclcreat; typedef nfstime4 fattr4_dir_notif_delay; typedef nfstime4 fattr4_dirent_notif_delay; typedef layouttype4 fattr4_fs_layout_types<>; typedef fs4_status fattr4_fs_status; typedef fs_charset_cap4 fattr4_fs_charset_cap; typedef uint32_t fattr4_layout_alignment; typedef uint32_t fattr4_layout_blksize; typedef layouthint4 fattr4_layout_hint; typedef layouttype4 fattr4_layout_types<>; typedef mdsthreshold4 fattr4_mdsthreshold; typedef retention_get4 fattr4_retention_get; typedef retention_set4 fattr4_retention_set; typedef retention_get4 fattr4_retentevt_get; typedef retention_set4 fattr4_retentevt_set; typedef uint64_t fattr4_retention_hold; typedef nfsacl41 fattr4_dacl; typedef nfsacl41 fattr4_sacl; typedef change_policy4 fattr4_change_policy; %/* % * REQUIRED Attributes % */ const FATTR4_SUPPORTED_ATTRS = 0; const FATTR4_TYPE = 1; const FATTR4_FH_EXPIRE_TYPE = 2; const FATTR4_CHANGE = 3; const FATTR4_SIZE = 4; const FATTR4_LINK_SUPPORT = 5; const FATTR4_SYMLINK_SUPPORT = 6; const FATTR4_NAMED_ATTR = 7; const FATTR4_FSID = 8; const FATTR4_UNIQUE_HANDLES = 9; const FATTR4_LEASE_TIME = 10; const FATTR4_RDATTR_ERROR = 11; const FATTR4_FILEHANDLE = 19; %/* new to NFSV4.1 */ const FATTR4_SUPPATTR_EXCLCREAT = 75; %/* % * RECOMMENDED Attributes % */ const FATTR4_ACL = 12; const FATTR4_ACLSUPPORT = 13; const FATTR4_ARCHIVE = 14; const FATTR4_CANSETTIME = 15; const FATTR4_CASE_INSENSITIVE = 16; const FATTR4_CASE_PRESERVING = 17; const FATTR4_CHOWN_RESTRICTED = 18; const FATTR4_FILEID = 20; const FATTR4_FILES_AVAIL = 21; const FATTR4_FILES_FREE = 22; const FATTR4_FILES_TOTAL = 23; const FATTR4_FS_LOCATIONS = 24; const FATTR4_HIDDEN = 25; const FATTR4_HOMOGENEOUS = 26; const FATTR4_MAXFILESIZE = 27; const FATTR4_MAXLINK = 28; const FATTR4_MAXNAME = 29; const FATTR4_MAXREAD = 30; const FATTR4_MAXWRITE = 31; const FATTR4_MIMETYPE = 32; const FATTR4_MODE = 33; const FATTR4_NO_TRUNC = 34; const FATTR4_NUMLINKS = 35; const FATTR4_OWNER = 36; const FATTR4_OWNER_GROUP = 37; const FATTR4_QUOTA_AVAIL_HARD = 38; const FATTR4_QUOTA_AVAIL_SOFT = 39; const FATTR4_QUOTA_USED = 40; const FATTR4_RAWDEV = 41; const FATTR4_SPACE_AVAIL = 42; const FATTR4_SPACE_FREE = 43; const FATTR4_SPACE_TOTAL = 44; const FATTR4_SPACE_USED = 45; const FATTR4_SYSTEM = 46; const FATTR4_TIME_ACCESS = 47; const FATTR4_TIME_ACCESS_SET = 48; const FATTR4_TIME_BACKUP = 49; const FATTR4_TIME_CREATE = 50; const FATTR4_TIME_DELTA = 51; const FATTR4_TIME_METADATA = 52; const FATTR4_TIME_MODIFY = 53; const FATTR4_TIME_MODIFY_SET = 54; const FATTR4_MOUNTED_ON_FILEID = 55; % %/* new to NFSV4.1 */ % const FATTR4_DIR_NOTIF_DELAY = 56; const FATTR4_DIRENT_NOTIF_DELAY = 57; const FATTR4_DACL = 58; const FATTR4_SACL = 59; const FATTR4_CHANGE_POLICY = 60; const FATTR4_FS_STATUS = 61; const FATTR4_FS_LAYOUT_TYPES = 62; const FATTR4_LAYOUT_HINT = 63; const FATTR4_LAYOUT_TYPES = 64; const FATTR4_LAYOUT_BLKSIZE = 65; const FATTR4_LAYOUT_ALIGNMENT = 66; const FATTR4_FS_LOCATIONS_INFO = 67; const FATTR4_MDSTHRESHOLD = 68; const FATTR4_RETENTION_GET = 69; const FATTR4_RETENTION_SET = 70; const FATTR4_RETENTEVT_GET = 71; const FATTR4_RETENTEVT_SET = 72; const FATTR4_RETENTION_HOLD = 73; const FATTR4_MODE_SET_MASKED = 74; const FATTR4_FS_CHARSET_CAP = 76; /* * File attribute container */ struct fattr4 { bitmap4 attrmask; attrlist4 attr_vals; }; /* * Change info for the client */ struct change_info4 { bool atomic; changeid4 before; changeid4 after; }; typedef netaddr4 clientaddr4; /* * Callback program info as provided by the client */ struct cb_client4 { uint32_t cb_program; netaddr4 cb_location; }; /* * NFSv4.0 Long Hand Client ID */ struct nfs_client_id4 { verifier4 verifier; opaque id; }; /* * NFSv4.1 Client Owner (aka long hand client ID) */ struct client_owner4 { verifier4 co_verifier; opaque co_ownerid; }; /* * NFSv4.1 server Owner */ struct server_owner4 { uint64_t so_minor_id; opaque so_major_id; }; struct state_owner4 { clientid4 clientid; opaque owner; }; typedef state_owner4 open_owner4; typedef state_owner4 lock_owner4; enum nfs_lock_type4 { READ_LT = 1, WRITE_LT = 2, READW_LT = 3, /* blocking read */ WRITEW_LT = 4 /* blocking write */ }; % %/* Input for computing subkeys */ enum ssv_subkey4 { SSV4_SUBKEY_MIC_I2T = 1, SSV4_SUBKEY_MIC_T2I = 2, SSV4_SUBKEY_SEAL_I2T = 3, SSV4_SUBKEY_SEAL_T2I = 4 }; % % %/* Input for computing smt_hmac */ struct ssv_mic_plain_tkn4 { uint32_t smpt_ssv_seq; opaque smpt_orig_plain<>; }; % % %/* SSV GSS PerMsgToken token */ struct ssv_mic_tkn4 { uint32_t smt_ssv_seq; opaque smt_hmac<>; }; % % %/* Input for computing ssct_encr_data and ssct_hmac */ struct ssv_seal_plain_tkn4 { opaque sspt_confounder<>; uint32_t sspt_ssv_seq; opaque sspt_orig_plain<>; opaque sspt_pad<>; }; % % %/* SSV GSS SealedMessage token */ struct ssv_seal_cipher_tkn4 { uint32_t ssct_ssv_seq; opaque ssct_iv<>; opaque ssct_encr_data<>; opaque ssct_hmac<>; }; % /* * Defines an individual server replica */ struct fs_locations_server4 { int32_t fls_currency; opaque fls_info<>; utf8str_cis fls_server; }; /* * Byte indices of items within * fls_info: flag fields, class numbers, * bytes indicating ranks and orders. */ const FSLI4BX_GFLAGS = 0; const FSLI4BX_TFLAGS = 1; const FSLI4BX_CLSIMUL = 2; const FSLI4BX_CLHANDLE = 3; const FSLI4BX_CLFILEID = 4; const FSLI4BX_CLWRITEVER = 5; const FSLI4BX_CLCHANGE = 6; const FSLI4BX_CLREADDIR = 7; const FSLI4BX_READRANK = 8; const FSLI4BX_WRITERANK = 9; const FSLI4BX_READORDER = 10; const FSLI4BX_WRITEORDER = 11; /* * Bits defined within the general flag byte. */ const FSLI4GF_WRITABLE = 0x01; const FSLI4GF_CUR_REQ = 0x02; const FSLI4GF_ABSENT = 0x04; const FSLI4GF_GOING = 0x08; const FSLI4GF_SPLIT = 0x10; /* * Bits defined within the transport flag byte. */ const FSLI4TF_RDMA = 0x01; /* * Defines a set of replicas sharing * a common value of the root path * with in the corresponding * single-server namespaces. */ struct fs_locations_item4 { fs_locations_server4 fli_entries<>; pathname4 fli_rootpath; }; /* * Defines the overall structure of * the fs_locations_info attribute. */ struct fs_locations_info4 { uint32_t fli_flags; int32_t fli_valid_for; pathname4 fli_fs_root; fs_locations_item4 fli_items<>; }; /* * Flag bits in fli_flags. */ const FSLI4IF_VAR_SUB = 0x00000001; typedef fs_locations_info4 fattr4_fs_locations_info; const NFL4_UFLG_MASK = 0x0000003F; const NFL4_UFLG_DENSE = 0x00000001; const NFL4_UFLG_COMMIT_THRU_MDS = 0x00000002; const NFL4_UFLG_STRIPE_UNIT_SIZE_MASK = 0xFFFFFFC0; typedef uint32_t nfl_util4; % enum filelayout_hint_care4 { NFLH4_CARE_DENSE = NFL4_UFLG_DENSE, NFLH4_CARE_COMMIT_THRU_MDS = NFL4_UFLG_COMMIT_THRU_MDS, NFLH4_CARE_STRIPE_UNIT_SIZE = 0x00000040, NFLH4_CARE_STRIPE_COUNT = 0x00000080 }; % %/* Encoded in the loh_body field of data type layouthint4: */ % struct nfsv4_1_file_layouthint4 { uint32_t nflh_care; nfl_util4 nflh_util; count4 nflh_stripe_count; }; % % typedef netaddr4 multipath_list4<>; % %/* % * Encoded in the da_addr_body field of % * data type device_addr4: % */ struct nfsv4_1_file_layout_ds_addr4 { uint32_t nflda_stripe_indices<>; multipath_list4 nflda_multipath_ds_list<>; }; % % %/* % * Encoded in the loc_body field of % * data type layout_content4: % */ struct nfsv4_1_file_layout4 { deviceid4 nfl_deviceid; nfl_util4 nfl_util; uint32_t nfl_first_stripe_index; offset4 nfl_pattern_offset; nfs_fh4 nfl_fh_list<>; }; % %/* % * Encoded in the lou_body field of data type layoutupdate4: % * Nothing. lou_body is a zero length array of bytes. % */ % %/* % * Encoded in the lrf_body field of % * data type layoutreturn_file4: % * Nothing. lrf_body is a zero length array of bytes. % */ % const ACCESS4_READ = 0x00000001; const ACCESS4_LOOKUP = 0x00000002; const ACCESS4_MODIFY = 0x00000004; const ACCESS4_EXTEND = 0x00000008; const ACCESS4_DELETE = 0x00000010; const ACCESS4_EXECUTE = 0x00000020; struct ACCESS4args { /* CURRENT_FH: object */ uint32_t access; }; struct ACCESS4resok { uint32_t supported; uint32_t access; }; union ACCESS4res switch (nfsstat4 status) { case NFS4_OK: ACCESS4resok resok4; default: void; }; struct CLOSE4args { /* CURRENT_FH: object */ seqid4 seqid; stateid4 open_stateid; }; union CLOSE4res switch (nfsstat4 status) { case NFS4_OK: stateid4 open_stateid; default: void; }; struct COMMIT4args { /* CURRENT_FH: file */ offset4 offset; count4 count; }; struct COMMIT4resok { verifier4 writeverf; }; union COMMIT4res switch (nfsstat4 status) { case NFS4_OK: COMMIT4resok resok4; default: void; }; union createtype4 switch (nfs_ftype4 type) { case NF4LNK: linktext4 linkdata; case NF4BLK: case NF4CHR: specdata4 devdata; case NF4SOCK: case NF4FIFO: case NF4DIR: void; default: void; /* server should return NFS4ERR_BADTYPE */ }; struct CREATE4args { /* CURRENT_FH: directory for creation */ createtype4 objtype; component4 objname; fattr4 createattrs; }; struct CREATE4resok { change_info4 cinfo; bitmap4 attrset; /* attributes set */ }; union CREATE4res switch (nfsstat4 status) { case NFS4_OK: /* new CURRENTFH: created object */ CREATE4resok resok4; default: void; }; struct DELEGPURGE4args { clientid4 clientid; }; struct DELEGPURGE4res { nfsstat4 status; }; struct DELEGRETURN4args { /* CURRENT_FH: delegated object */ stateid4 deleg_stateid; }; struct DELEGRETURN4res { nfsstat4 status; }; struct GETATTR4args { /* CURRENT_FH: object */ bitmap4 attr_request; }; struct GETATTR4resok { fattr4 obj_attributes; }; union GETATTR4res switch (nfsstat4 status) { case NFS4_OK: GETATTR4resok resok4; default: void; }; struct GETFH4resok { nfs_fh4 object; }; union GETFH4res switch (nfsstat4 status) { case NFS4_OK: GETFH4resok resok4; default: void; }; struct LINK4args { /* SAVED_FH: source object */ /* CURRENT_FH: target directory */ component4 newname; }; struct LINK4resok { change_info4 cinfo; }; union LINK4res switch (nfsstat4 status) { case NFS4_OK: LINK4resok resok4; default: void; }; /* * For LOCK, transition from open_stateid and lock_owner * to a lock stateid. */ struct open_to_lock_owner4 { seqid4 open_seqid; stateid4 open_stateid; seqid4 lock_seqid; lock_owner4 lock_owner; }; /* * For LOCK, existing lock stateid continues to request new * file lock for the same lock_owner and open_stateid. */ struct exist_lock_owner4 { stateid4 lock_stateid; seqid4 lock_seqid; }; union locker4 switch (bool new_lock_owner) { case TRUE: open_to_lock_owner4 open_owner; case FALSE: exist_lock_owner4 lock_owner; }; /* * LOCK/LOCKT/LOCKU: Record lock management */ struct LOCK4args { /* CURRENT_FH: file */ nfs_lock_type4 locktype; bool reclaim; offset4 offset; length4 length; locker4 locker; }; struct LOCK4denied { offset4 offset; length4 length; nfs_lock_type4 locktype; lock_owner4 owner; }; struct LOCK4resok { stateid4 lock_stateid; }; union LOCK4res switch (nfsstat4 status) { case NFS4_OK: LOCK4resok resok4; case NFS4ERR_DENIED: LOCK4denied denied; default: void; }; struct LOCKT4args { /* CURRENT_FH: file */ nfs_lock_type4 locktype; offset4 offset; length4 length; lock_owner4 owner; }; union LOCKT4res switch (nfsstat4 status) { case NFS4ERR_DENIED: LOCK4denied denied; case NFS4_OK: void; default: void; }; struct LOCKU4args { /* CURRENT_FH: file */ nfs_lock_type4 locktype; seqid4 seqid; stateid4 lock_stateid; offset4 offset; length4 length; }; union LOCKU4res switch (nfsstat4 status) { case NFS4_OK: stateid4 lock_stateid; default: void; }; struct LOOKUP4args { /* CURRENT_FH: directory */ component4 objname; }; struct LOOKUP4res { /* New CURRENT_FH: object */ nfsstat4 status; }; struct LOOKUPP4res { /* new CURRENT_FH: parent directory */ nfsstat4 status; }; struct NVERIFY4args { /* CURRENT_FH: object */ fattr4 obj_attributes; }; struct NVERIFY4res { nfsstat4 status; }; /* * Various definitions for OPEN */ enum createmode4 { UNCHECKED4 = 0, GUARDED4 = 1, /* Deprecated in NFSv4.1. */ EXCLUSIVE4 = 2, /* * New to NFSv4.1. If session is persistent, * GUARDED4 MUST be used. Otherwise, use * EXCLUSIVE4_1 instead of EXCLUSIVE4. */ EXCLUSIVE4_1 = 3 }; struct creatverfattr { verifier4 cva_verf; fattr4 cva_attrs; }; union createhow4 switch (createmode4 mode) { case UNCHECKED4: case GUARDED4: fattr4 createattrs; case EXCLUSIVE4: verifier4 createverf; case EXCLUSIVE4_1: creatverfattr ch_createboth; }; enum opentype4 { OPEN4_NOCREATE = 0, OPEN4_CREATE = 1 }; union openflag4 switch (opentype4 opentype) { case OPEN4_CREATE: createhow4 how; default: void; }; /* Next definitions used for OPEN delegation */ enum limit_by4 { NFS_LIMIT_SIZE = 1, NFS_LIMIT_BLOCKS = 2 /* others as needed */ }; struct nfs_modified_limit4 { uint32_t num_blocks; uint32_t bytes_per_block; }; union nfs_space_limit4 switch (limit_by4 limitby) { /* limit specified as file size */ case NFS_LIMIT_SIZE: uint64_t filesize; /* limit specified by number of blocks */ case NFS_LIMIT_BLOCKS: nfs_modified_limit4 mod_blocks; } ; /* * Share Access and Deny constants for open argument */ const OPEN4_SHARE_ACCESS_READ = 0x00000001; const OPEN4_SHARE_ACCESS_WRITE = 0x00000002; const OPEN4_SHARE_ACCESS_BOTH = 0x00000003; const OPEN4_SHARE_DENY_NONE = 0x00000000; const OPEN4_SHARE_DENY_READ = 0x00000001; const OPEN4_SHARE_DENY_WRITE = 0x00000002; const OPEN4_SHARE_DENY_BOTH = 0x00000003; /* new flags for share_access field of OPEN4args */ const OPEN4_SHARE_ACCESS_WANT_DELEG_MASK = 0xFF00; const OPEN4_SHARE_ACCESS_WANT_NO_PREFERENCE = 0x0000; const OPEN4_SHARE_ACCESS_WANT_READ_DELEG = 0x0100; const OPEN4_SHARE_ACCESS_WANT_WRITE_DELEG = 0x0200; const OPEN4_SHARE_ACCESS_WANT_ANY_DELEG = 0x0300; const OPEN4_SHARE_ACCESS_WANT_NO_DELEG = 0x0400; const OPEN4_SHARE_ACCESS_WANT_CANCEL = 0x0500; const OPEN4_SHARE_ACCESS_WANT_SIGNAL_DELEG_WHEN_RESRC_AVAIL = 0x10000; const OPEN4_SHARE_ACCESS_WANT_PUSH_DELEG_WHEN_UNCONTENDED = 0x20000; enum open_delegation_type4 { OPEN_DELEGATE_NONE = 0, OPEN_DELEGATE_READ = 1, OPEN_DELEGATE_WRITE = 2, OPEN_DELEGATE_NONE_EXT = 3 /* new to v4.1 */ }; enum open_claim_type4 { /* * Not a reclaim. */ CLAIM_NULL = 0, CLAIM_PREVIOUS = 1, CLAIM_DELEGATE_CUR = 2, CLAIM_DELEGATE_PREV = 3, /* * Not a reclaim. * * Like CLAIM_NULL, but object identified * by the current filehandle. */ CLAIM_FH = 4, /* new to v4.1 */ /* * Like CLAIM_DELEGATE_CUR, but object identified * by current filehandle. */ CLAIM_DELEG_CUR_FH = 5, /* new to v4.1 */ /* * Like CLAIM_DELEGATE_PREV, but object identified * by current filehandle. */ CLAIM_DELEG_PREV_FH = 6 /* new to v4.1 */ }; struct open_claim_delegate_cur4 { stateid4 delegate_stateid; component4 file; }; union open_claim4 switch (open_claim_type4 claim) { /* * No special rights to file. * Ordinary OPEN of the specified file. */ case CLAIM_NULL: /* CURRENT_FH: directory */ component4 file; /* * Right to the file established by an * open previous to server reboot. File * identified by filehandle obtained at * that time rather than by name. */ case CLAIM_PREVIOUS: /* CURRENT_FH: file being reclaimed */ open_delegation_type4 delegate_type; /* * Right to file based on a delegation * granted by the server. File is * specified by name. */ case CLAIM_DELEGATE_CUR: /* CURRENT_FH: directory */ open_claim_delegate_cur4 delegate_cur_info; /* * Right to file based on a delegation * granted to a previous boot instance * of the client. File is specified by name. */ case CLAIM_DELEGATE_PREV: /* CURRENT_FH: directory */ component4 file_delegate_prev; /* * Like CLAIM_NULL. No special rights * to file. Ordinary OPEN of the * specified file by current filehandle. */ case CLAIM_FH: /* new to v4.1 */ /* CURRENT_FH: regular file to open */ void; /* * Like CLAIM_DELEGATE_PREV. Right to file based on a * delegation granted to a previous boot * instance of the client. File is identified by * by filehandle. */ case CLAIM_DELEG_PREV_FH: /* new to v4.1 */ /* CURRENT_FH: file being opened */ void; /* * Like CLAIM_DELEGATE_CUR. Right to file based on * a delegation granted by the server. * File is identified by filehandle. */ case CLAIM_DELEG_CUR_FH: /* new to v4.1 */ /* CURRENT_FH: file being opened */ stateid4 oc_delegate_stateid; }; /* * OPEN: Open a file, potentially receiving an open delegation */ struct OPEN4args { seqid4 seqid; uint32_t share_access; uint32_t share_deny; open_owner4 owner; openflag4 openhow; open_claim4 claim; }; struct open_read_delegation4 { stateid4 stateid; /* Stateid for delegation*/ bool recall; /* Pre-recalled flag for delegations obtained by reclaim (CLAIM_PREVIOUS) */ nfsace4 permissions; /* Defines users who don't need an ACCESS call to open for read */ }; struct open_write_delegation4 { stateid4 stateid; /* Stateid for delegation */ bool recall; /* Pre-recalled flag for delegations obtained by reclaim (CLAIM_PREVIOUS) */ nfs_space_limit4 space_limit; /* Defines condition that the client must check to determine whether the file needs to be flushed to the server on close. */ nfsace4 permissions; /* Defines users who don't need an ACCESS call as part of a delegated open. */ }; enum why_no_delegation4 { /* new to v4.1 */ WND4_NOT_WANTED = 0, WND4_CONTENTION = 1, WND4_RESOURCE = 2, WND4_NOT_SUPP_FTYPE = 3, WND4_WRITE_DELEG_NOT_SUPP_FTYPE = 4, WND4_NOT_SUPP_UPGRADE = 5, WND4_NOT_SUPP_DOWNGRADE = 6, WND4_CANCELLED = 7, WND4_IS_DIR = 8 }; union open_none_delegation4 /* new to v4.1 */ switch (why_no_delegation4 ond_why) { case WND4_CONTENTION: bool ond_server_will_push_deleg; case WND4_RESOURCE: bool ond_server_will_signal_avail; default: void; }; union open_delegation4 switch (open_delegation_type4 delegation_type) { case OPEN_DELEGATE_NONE: void; case OPEN_DELEGATE_READ: open_read_delegation4 read; case OPEN_DELEGATE_WRITE: open_write_delegation4 write; case OPEN_DELEGATE_NONE_EXT: /* new to v4.1 */ open_none_delegation4 od_whynone; }; /* * Result flags */ /* Client must confirm open */ const OPEN4_RESULT_CONFIRM = 0x00000002; /* Type of file locking behavior at the server */ const OPEN4_RESULT_LOCKTYPE_POSIX = 0x00000004; /* Server will preserve file if removed while open */ const OPEN4_RESULT_PRESERVE_UNLINKED = 0x00000008; /* * Server may use CB_NOTIFY_LOCK on locks * derived from this open */ const OPEN4_RESULT_MAY_NOTIFY_LOCK = 0x00000020; struct OPEN4resok { stateid4 stateid; /* Stateid for open */ change_info4 cinfo; /* Directory Change Info */ uint32_t rflags; /* Result flags */ bitmap4 attrset; /* attribute set for create*/ open_delegation4 delegation; /* Info on any open delegation */ }; union OPEN4res switch (nfsstat4 status) { case NFS4_OK: /* New CURRENT_FH: opened file */ OPEN4resok resok4; default: void; }; struct OPENATTR4args { /* CURRENT_FH: object */ bool createdir; }; struct OPENATTR4res { /* * If status is NFS4_OK, * new CURRENT_FH: named attribute * directory */ nfsstat4 status; }; /* obsolete in NFSv4.1 */ struct OPEN_CONFIRM4args { /* CURRENT_FH: opened file */ stateid4 open_stateid; seqid4 seqid; }; struct OPEN_CONFIRM4resok { stateid4 open_stateid; }; union OPEN_CONFIRM4res switch (nfsstat4 status) { case NFS4_OK: OPEN_CONFIRM4resok resok4; default: void; }; struct OPEN_DOWNGRADE4args { /* CURRENT_FH: opened file */ stateid4 open_stateid; seqid4 seqid; uint32_t share_access; uint32_t share_deny; }; struct OPEN_DOWNGRADE4resok { stateid4 open_stateid; }; union OPEN_DOWNGRADE4res switch(nfsstat4 status) { case NFS4_OK: OPEN_DOWNGRADE4resok resok4; default: void; }; struct PUTFH4args { nfs_fh4 object; }; struct PUTFH4res { /* * If status is NFS4_OK, * new CURRENT_FH: argument to PUTFH */ nfsstat4 status; }; struct PUTPUBFH4res { /* * If status is NFS4_OK, * new CURRENT_FH: public fh */ nfsstat4 status; }; struct PUTROOTFH4res { /* * If status is NFS4_OK, * new CURRENT_FH: root fh */ nfsstat4 status; }; struct READ4args { /* CURRENT_FH: file */ stateid4 stateid; offset4 offset; count4 count; }; struct READ4resok { bool eof; opaque data<>; }; union READ4res switch (nfsstat4 status) { case NFS4_OK: READ4resok resok4; default: void; }; struct READDIR4args { /* CURRENT_FH: directory */ nfs_cookie4 cookie; verifier4 cookieverf; count4 dircount; count4 maxcount; bitmap4 attr_request; }; struct entry4 { nfs_cookie4 cookie; component4 name; fattr4 attrs; entry4 *nextentry; }; struct dirlist4 { entry4 *entries; bool eof; }; struct READDIR4resok { verifier4 cookieverf; dirlist4 reply; }; union READDIR4res switch (nfsstat4 status) { case NFS4_OK: READDIR4resok resok4; default: void; }; struct READLINK4resok { linktext4 link; }; union READLINK4res switch (nfsstat4 status) { case NFS4_OK: READLINK4resok resok4; default: void; }; struct REMOVE4args { /* CURRENT_FH: directory */ component4 target; }; struct REMOVE4resok { change_info4 cinfo; }; union REMOVE4res switch (nfsstat4 status) { case NFS4_OK: REMOVE4resok resok4; default: void; }; struct RENAME4args { /* SAVED_FH: source directory */ component4 oldname; /* CURRENT_FH: target directory */ component4 newname; }; struct RENAME4resok { change_info4 source_cinfo; change_info4 target_cinfo; }; union RENAME4res switch (nfsstat4 status) { case NFS4_OK: RENAME4resok resok4; default: void; }; /* Obsolete in NFSv4.1 */ struct RENEW4args { clientid4 clientid; }; struct RENEW4res { nfsstat4 status; }; struct RESTOREFH4res { /* * If status is NFS4_OK, * new CURRENT_FH: value of saved fh */ nfsstat4 status; }; struct SAVEFH4res { /* * If status is NFS4_OK, * new SAVED_FH: value of current fh */ nfsstat4 status; }; struct SECINFO4args { /* CURRENT_FH: directory */ component4 name; }; /* * From RFC 2203 */ enum rpc_gss_svc_t { RPC_GSS_SVC_NONE = 1, RPC_GSS_SVC_INTEGRITY = 2, RPC_GSS_SVC_PRIVACY = 3 }; struct rpcsec_gss_info { sec_oid4 oid; qop4 qop; rpc_gss_svc_t service; }; /* RPCSEC_GSS has a value of '6' - See RFC 2203 */ union secinfo4 switch (uint32_t flavor) { case RPCSEC_GSS: rpcsec_gss_info flavor_info; default: void; }; typedef secinfo4 SECINFO4resok<>; union SECINFO4res switch (nfsstat4 status) { case NFS4_OK: /* CURRENTFH: consumed */ SECINFO4resok resok4; default: void; }; struct SETATTR4args { /* CURRENT_FH: target object */ stateid4 stateid; fattr4 obj_attributes; }; struct SETATTR4res { nfsstat4 status; bitmap4 attrsset; }; /* Obsolete in NFSv4.1 */ struct SETCLIENTID4args { nfs_client_id4 client; cb_client4 callback; uint32_t callback_ident; }; struct SETCLIENTID4resok { clientid4 clientid; verifier4 setclientid_confirm; }; union SETCLIENTID4res switch (nfsstat4 status) { case NFS4_OK: SETCLIENTID4resok resok4; case NFS4ERR_CLID_INUSE: clientaddr4 client_using; default: void; }; /* Obsolete in NFSv4.1 */ struct SETCLIENTID_CONFIRM4args { clientid4 clientid; verifier4 setclientid_confirm; }; struct SETCLIENTID_CONFIRM4res { nfsstat4 status; }; struct VERIFY4args { /* CURRENT_FH: object */ fattr4 obj_attributes; }; struct VERIFY4res { nfsstat4 status; }; enum stable_how4 { UNSTABLE4 = 0, DATA_SYNC4 = 1, FILE_SYNC4 = 2 }; struct WRITE4args { /* CURRENT_FH: file */ stateid4 stateid; offset4 offset; stable_how4 stable; opaque data<>; }; struct WRITE4resok { count4 count; stable_how4 committed; verifier4 writeverf; }; union WRITE4res switch (nfsstat4 status) { case NFS4_OK: WRITE4resok resok4; default: void; }; /* Obsolete in NFSv4.1 */ struct RELEASE_LOCKOWNER4args { lock_owner4 lock_owner; }; struct RELEASE_LOCKOWNER4res { nfsstat4 status; }; struct ILLEGAL4res { nfsstat4 status; }; typedef opaque gsshandle4_t<>; struct gss_cb_handles4 { rpc_gss_svc_t gcbp_service; /* RFC 2203 */ gsshandle4_t gcbp_handle_from_server; gsshandle4_t gcbp_handle_from_client; }; union callback_sec_parms4 switch (uint32_t cb_secflavor) { case AUTH_NONE: void; case AUTH_SYS: authsys_parms cbsp_sys_cred; /* RFC 1831 */ case RPCSEC_GSS: gss_cb_handles4 cbsp_gss_handles; }; struct BACKCHANNEL_CTL4args { uint32_t bca_cb_program; callback_sec_parms4 bca_sec_parms<>; }; struct BACKCHANNEL_CTL4res { nfsstat4 bcr_status; }; enum channel_dir_from_client4 { CDFC4_FORE = 0x1, CDFC4_BACK = 0x2, CDFC4_FORE_OR_BOTH = 0x3, CDFC4_BACK_OR_BOTH = 0x7 }; struct BIND_CONN_TO_SESSION4args { sessionid4 bctsa_sessid; channel_dir_from_client4 bctsa_dir; bool bctsa_use_conn_in_rdma_mode; }; enum channel_dir_from_server4 { CDFS4_FORE = 0x1, CDFS4_BACK = 0x2, CDFS4_BOTH = 0x3 }; struct BIND_CONN_TO_SESSION4resok { sessionid4 bctsr_sessid; channel_dir_from_server4 bctsr_dir; bool bctsr_use_conn_in_rdma_mode; }; union BIND_CONN_TO_SESSION4res switch (nfsstat4 bctsr_status) { case NFS4_OK: BIND_CONN_TO_SESSION4resok bctsr_resok4; default: void; }; const EXCHGID4_FLAG_SUPP_MOVED_REFER = 0x00000001; const EXCHGID4_FLAG_SUPP_MOVED_MIGR = 0x00000002; const EXCHGID4_FLAG_BIND_PRINC_STATEID = 0x00000100; const EXCHGID4_FLAG_USE_NON_PNFS = 0x00010000; const EXCHGID4_FLAG_USE_PNFS_MDS = 0x00020000; const EXCHGID4_FLAG_USE_PNFS_DS = 0x00040000; const EXCHGID4_FLAG_MASK_PNFS = 0x00070000; const EXCHGID4_FLAG_UPD_CONFIRMED_REC_A = 0x40000000; const EXCHGID4_FLAG_CONFIRMED_R = 0x80000000; struct state_protect_ops4 { bitmap4 spo_must_enforce; bitmap4 spo_must_allow; }; struct ssv_sp_parms4 { state_protect_ops4 ssp_ops; sec_oid4 ssp_hash_algs<>; sec_oid4 ssp_encr_algs<>; uint32_t ssp_window; uint32_t ssp_num_gss_handles; }; enum state_protect_how4 { SP4_NONE = 0, SP4_MACH_CRED = 1, SP4_SSV = 2 }; union state_protect4_a switch(state_protect_how4 spa_how) { case SP4_NONE: void; case SP4_MACH_CRED: state_protect_ops4 spa_mach_ops; case SP4_SSV: ssv_sp_parms4 spa_ssv_parms; }; struct EXCHANGE_ID4args { client_owner4 eia_clientowner; uint32_t eia_flags; state_protect4_a eia_state_protect; nfs_impl_id4 eia_client_impl_id<1>; }; struct ssv_prot_info4 { state_protect_ops4 spi_ops; uint32_t spi_hash_alg; uint32_t spi_encr_alg; uint32_t spi_ssv_len; uint32_t spi_window; gsshandle4_t spi_handles<>; }; union state_protect4_r switch(state_protect_how4 spr_how) { case SP4_NONE: void; case SP4_MACH_CRED: state_protect_ops4 spr_mach_ops; case SP4_SSV: ssv_prot_info4 spr_ssv_info; }; struct EXCHANGE_ID4resok { clientid4 eir_clientid; sequenceid4 eir_sequenceid; uint32_t eir_flags; state_protect4_r eir_state_protect; server_owner4 eir_server_owner; opaque eir_server_scope; nfs_impl_id4 eir_server_impl_id<1>; }; union EXCHANGE_ID4res switch (nfsstat4 eir_status) { case NFS4_OK: EXCHANGE_ID4resok eir_resok4; default: void; }; struct channel_attrs4 { count4 ca_headerpadsize; count4 ca_maxrequestsize; count4 ca_maxresponsesize; count4 ca_maxresponsesize_cached; count4 ca_maxoperations; count4 ca_maxrequests; uint32_t ca_rdma_ird<1>; }; const CREATE_SESSION4_FLAG_PERSIST = 0x00000001; const CREATE_SESSION4_FLAG_CONN_BACK_CHAN = 0x00000002; const CREATE_SESSION4_FLAG_CONN_RDMA = 0x00000004; struct CREATE_SESSION4args { clientid4 csa_clientid; sequenceid4 csa_sequence; uint32_t csa_flags; channel_attrs4 csa_fore_chan_attrs; channel_attrs4 csa_back_chan_attrs; uint32_t csa_cb_program; callback_sec_parms4 csa_sec_parms<>; }; struct CREATE_SESSION4resok { sessionid4 csr_sessionid; sequenceid4 csr_sequence; uint32_t csr_flags; channel_attrs4 csr_fore_chan_attrs; channel_attrs4 csr_back_chan_attrs; }; union CREATE_SESSION4res switch (nfsstat4 csr_status) { case NFS4_OK: CREATE_SESSION4resok csr_resok4; default: void; }; struct DESTROY_SESSION4args { sessionid4 dsa_sessionid; }; struct DESTROY_SESSION4res { nfsstat4 dsr_status; }; struct FREE_STATEID4args { stateid4 fsa_stateid; }; struct FREE_STATEID4res { nfsstat4 fsr_status; }; typedef nfstime4 attr_notice4; struct GET_DIR_DELEGATION4args { /* CURRENT_FH: delegated directory */ bool gdda_signal_deleg_avail; bitmap4 gdda_notification_types; attr_notice4 gdda_child_attr_delay; attr_notice4 gdda_dir_attr_delay; bitmap4 gdda_child_attributes; bitmap4 gdda_dir_attributes; }; struct GET_DIR_DELEGATION4resok { verifier4 gddr_cookieverf; /* Stateid for get_dir_delegation */ stateid4 gddr_stateid; /* Which notifications can the server support */ bitmap4 gddr_notification; bitmap4 gddr_child_attributes; bitmap4 gddr_dir_attributes; }; enum gddrnf4_status { GDD4_OK = 0, GDD4_UNAVAIL = 1 }; union GET_DIR_DELEGATION4res_non_fatal switch (gddrnf4_status gddrnf_status) { case GDD4_OK: GET_DIR_DELEGATION4resok gddrnf_resok4; case GDD4_UNAVAIL: bool gddrnf_will_signal_deleg_avail; }; union GET_DIR_DELEGATION4res switch (nfsstat4 gddr_status) { case NFS4_OK: GET_DIR_DELEGATION4res_non_fatal gddr_res_non_fatal4; default: void; }; struct GETDEVICEINFO4args { deviceid4 gdia_device_id; layouttype4 gdia_layout_type; count4 gdia_maxcount; bitmap4 gdia_notify_types; }; struct GETDEVICEINFO4resok { device_addr4 gdir_device_addr; bitmap4 gdir_notification; }; union GETDEVICEINFO4res switch (nfsstat4 gdir_status) { case NFS4_OK: GETDEVICEINFO4resok gdir_resok4; case NFS4ERR_TOOSMALL: count4 gdir_mincount; default: void; }; struct GETDEVICELIST4args { /* CURRENT_FH: object belonging to the file system */ layouttype4 gdla_layout_type; /* number of deviceIDs to return */ count4 gdla_maxdevices; nfs_cookie4 gdla_cookie; verifier4 gdla_cookieverf; }; struct GETDEVICELIST4resok { nfs_cookie4 gdlr_cookie; verifier4 gdlr_cookieverf; deviceid4 gdlr_deviceid_list<>; bool gdlr_eof; }; union GETDEVICELIST4res switch (nfsstat4 gdlr_status) { case NFS4_OK: GETDEVICELIST4resok gdlr_resok4; default: void; }; union newtime4 switch (bool nt_timechanged) { case TRUE: nfstime4 nt_time; case FALSE: void; }; union newoffset4 switch (bool no_newoffset) { case TRUE: offset4 no_offset; case FALSE: void; }; struct LAYOUTCOMMIT4args { /* CURRENT_FH: file */ offset4 loca_offset; length4 loca_length; bool loca_reclaim; stateid4 loca_stateid; newoffset4 loca_last_write_offset; newtime4 loca_time_modify; layoutupdate4 loca_layoutupdate; }; union newsize4 switch (bool ns_sizechanged) { case TRUE: length4 ns_size; case FALSE: void; }; struct LAYOUTCOMMIT4resok { newsize4 locr_newsize; }; union LAYOUTCOMMIT4res switch (nfsstat4 locr_status) { case NFS4_OK: LAYOUTCOMMIT4resok locr_resok4; default: void; }; struct LAYOUTGET4args { /* CURRENT_FH: file */ bool loga_signal_layout_avail; layouttype4 loga_layout_type; layoutiomode4 loga_iomode; offset4 loga_offset; length4 loga_length; length4 loga_minlength; stateid4 loga_stateid; count4 loga_maxcount; }; struct LAYOUTGET4resok { bool logr_return_on_close; stateid4 logr_stateid; layout4 logr_layout<>; }; union LAYOUTGET4res switch (nfsstat4 logr_status) { case NFS4_OK: LAYOUTGET4resok logr_resok4; case NFS4ERR_LAYOUTTRYLATER: bool logr_will_signal_layout_avail; default: void; }; struct LAYOUTRETURN4args { /* CURRENT_FH: file */ bool lora_reclaim; layouttype4 lora_layout_type; layoutiomode4 lora_iomode; layoutreturn4 lora_layoutreturn; }; union layoutreturn_stateid switch (bool lrs_present) { case TRUE: stateid4 lrs_stateid; case FALSE: void; }; union LAYOUTRETURN4res switch (nfsstat4 lorr_status) { case NFS4_OK: layoutreturn_stateid lorr_stateid; default: void; }; enum secinfo_style4 { SECINFO_STYLE4_CURRENT_FH = 0, SECINFO_STYLE4_PARENT = 1 }; /* CURRENT_FH: object or child directory */ typedef secinfo_style4 SECINFO_NO_NAME4args; /* CURRENTFH: consumed if status is NFS4_OK */ typedef SECINFO4res SECINFO_NO_NAME4res; struct SEQUENCE4args { sessionid4 sa_sessionid; sequenceid4 sa_sequenceid; slotid4 sa_slotid; slotid4 sa_highest_slotid; bool sa_cachethis; }; const SEQ4_STATUS_CB_PATH_DOWN = 0x00000001; const SEQ4_STATUS_CB_GSS_CONTEXTS_EXPIRING = 0x00000002; const SEQ4_STATUS_CB_GSS_CONTEXTS_EXPIRED = 0x00000004; const SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED = 0x00000008; const SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED = 0x00000010; const SEQ4_STATUS_ADMIN_STATE_REVOKED = 0x00000020; const SEQ4_STATUS_RECALLABLE_STATE_REVOKED = 0x00000040; const SEQ4_STATUS_LEASE_MOVED = 0x00000080; const SEQ4_STATUS_RESTART_RECLAIM_NEEDED = 0x00000100; const SEQ4_STATUS_CB_PATH_DOWN_SESSION = 0x00000200; const SEQ4_STATUS_BACKCHANNEL_FAULT = 0x00000400; const SEQ4_STATUS_DEVID_CHANGED = 0x00000800; const SEQ4_STATUS_DEVID_DELETED = 0x00001000; struct SEQUENCE4resok { sessionid4 sr_sessionid; sequenceid4 sr_sequenceid; slotid4 sr_slotid; slotid4 sr_highest_slotid; slotid4 sr_target_highest_slotid; uint32_t sr_status_flags; }; union SEQUENCE4res switch (nfsstat4 sr_status) { case NFS4_OK: SEQUENCE4resok sr_resok4; default: void; }; struct ssa_digest_input4 { SEQUENCE4args sdi_seqargs; }; struct SET_SSV4args { opaque ssa_ssv<>; opaque ssa_digest<>; }; struct ssr_digest_input4 { SEQUENCE4res sdi_seqres; }; struct SET_SSV4resok { opaque ssr_digest<>; }; union SET_SSV4res switch (nfsstat4 ssr_status) { case NFS4_OK: SET_SSV4resok ssr_resok4; default: void; }; struct TEST_STATEID4args { stateid4 ts_stateids<>; }; struct TEST_STATEID4resok { nfsstat4 tsr_status_codes<>; }; union TEST_STATEID4res switch (nfsstat4 tsr_status) { case NFS4_OK: TEST_STATEID4resok tsr_resok4; default: void; }; union deleg_claim4 switch (open_claim_type4 dc_claim) { /* * No special rights to object. Ordinary delegation * request of the specified object. Object identified * by filehandle. */ case CLAIM_FH: /* new to v4.1 */ /* CURRENT_FH: object being delegated */ void; /* * Right to file based on a delegation granted * to a previous boot instance of the client. * File is specified by filehandle. */ case CLAIM_DELEG_PREV_FH: /* new to v4.1 */ /* CURRENT_FH: object being delegated */ void; /* * Right to the file established by an open previous * to server reboot. File identified by filehandle. * Used during server reclaim grace period. */ case CLAIM_PREVIOUS: /* CURRENT_FH: object being reclaimed */ open_delegation_type4 dc_delegate_type; }; struct WANT_DELEGATION4args { uint32_t wda_want; deleg_claim4 wda_claim; }; union WANT_DELEGATION4res switch (nfsstat4 wdr_status) { case NFS4_OK: open_delegation4 wdr_resok4; default: void; }; struct DESTROY_CLIENTID4args { clientid4 dca_clientid; }; struct DESTROY_CLIENTID4res { nfsstat4 dcr_status; }; struct RECLAIM_COMPLETE4args { /* * If rca_one_fs TRUE, * * CURRENT_FH: object in * filesystem reclaim is * complete for. */ bool rca_one_fs; }; struct RECLAIM_COMPLETE4res { nfsstat4 rcr_status; }; /* * Operation arrays */ enum nfs_opnum4 { OP_ACCESS = 3, OP_CLOSE = 4, OP_COMMIT = 5, OP_CREATE = 6, OP_DELEGPURGE = 7, OP_DELEGRETURN = 8, OP_GETATTR = 9, OP_GETFH = 10, OP_LINK = 11, OP_LOCK = 12, OP_LOCKT = 13, OP_LOCKU = 14, OP_LOOKUP = 15, OP_LOOKUPP = 16, OP_NVERIFY = 17, OP_OPEN = 18, OP_OPENATTR = 19, OP_OPEN_CONFIRM = 20, /* Mandatory not-to-implement */ OP_OPEN_DOWNGRADE = 21, OP_PUTFH = 22, OP_PUTPUBFH = 23, OP_PUTROOTFH = 24, OP_READ = 25, OP_READDIR = 26, OP_READLINK = 27, OP_REMOVE = 28, OP_RENAME = 29, OP_RENEW = 30, /* Mandatory not-to-implement */ OP_RESTOREFH = 31, OP_SAVEFH = 32, OP_SECINFO = 33, OP_SETATTR = 34, OP_SETCLIENTID = 35, /* Mandatory not-to-implement */ OP_SETCLIENTID_CONFIRM = 36, /* Mandatory not-to-implement */ OP_VERIFY = 37, OP_WRITE = 38, OP_RELEASE_LOCKOWNER = 39, /* Mandatory not-to-implement */ % %/* new operations for NFSv4.1 */ % OP_BACKCHANNEL_CTL = 40, OP_BIND_CONN_TO_SESSION = 41, OP_EXCHANGE_ID = 42, OP_CREATE_SESSION = 43, OP_DESTROY_SESSION = 44, OP_FREE_STATEID = 45, OP_GET_DIR_DELEGATION = 46, OP_GETDEVICEINFO = 47, OP_GETDEVICELIST = 48, OP_LAYOUTCOMMIT = 49, OP_LAYOUTGET = 50, OP_LAYOUTRETURN = 51, OP_SECINFO_NO_NAME = 52, OP_SEQUENCE = 53, OP_SET_SSV = 54, OP_TEST_STATEID = 55, OP_WANT_DELEGATION = 56, OP_DESTROY_CLIENTID = 57, OP_RECLAIM_COMPLETE = 58, OP_ILLEGAL = 10044 }; union nfs_argop4 switch (nfs_opnum4 argop) { case OP_ACCESS: ACCESS4args opaccess; case OP_CLOSE: CLOSE4args opclose; case OP_COMMIT: COMMIT4args opcommit; case OP_CREATE: CREATE4args opcreate; case OP_DELEGPURGE: DELEGPURGE4args opdelegpurge; case OP_DELEGRETURN: DELEGRETURN4args opdelegreturn; case OP_GETATTR: GETATTR4args opgetattr; case OP_GETFH: void; case OP_LINK: LINK4args oplink; case OP_LOCK: LOCK4args oplock; case OP_LOCKT: LOCKT4args oplockt; case OP_LOCKU: LOCKU4args oplocku; case OP_LOOKUP: LOOKUP4args oplookup; case OP_LOOKUPP: void; case OP_NVERIFY: NVERIFY4args opnverify; case OP_OPEN: OPEN4args opopen; case OP_OPENATTR: OPENATTR4args opopenattr; /* Not for NFSv4.1 */ case OP_OPEN_CONFIRM: OPEN_CONFIRM4args opopen_confirm; case OP_OPEN_DOWNGRADE: OPEN_DOWNGRADE4args opopen_downgrade; case OP_PUTFH: PUTFH4args opputfh; case OP_PUTPUBFH: void; case OP_PUTROOTFH: void; case OP_READ: READ4args opread; case OP_READDIR: READDIR4args opreaddir; case OP_READLINK: void; case OP_REMOVE: REMOVE4args opremove; case OP_RENAME: RENAME4args oprename; /* Not for NFSv4.1 */ case OP_RENEW: RENEW4args oprenew; case OP_RESTOREFH: void; case OP_SAVEFH: void; case OP_SECINFO: SECINFO4args opsecinfo; case OP_SETATTR: SETATTR4args opsetattr; /* Not for NFSv4.1 */ case OP_SETCLIENTID: SETCLIENTID4args opsetclientid; /* Not for NFSv4.1 */ case OP_SETCLIENTID_CONFIRM: SETCLIENTID_CONFIRM4args opsetclientid_confirm; case OP_VERIFY: VERIFY4args opverify; case OP_WRITE: WRITE4args opwrite; /* Not for NFSv4.1 */ case OP_RELEASE_LOCKOWNER: RELEASE_LOCKOWNER4args oprelease_lockowner; /* Operations new to NFSv4.1 */ case OP_BACKCHANNEL_CTL: BACKCHANNEL_CTL4args opbackchannel_ctl; case OP_BIND_CONN_TO_SESSION: BIND_CONN_TO_SESSION4args opbind_conn_to_session; case OP_EXCHANGE_ID: EXCHANGE_ID4args opexchange_id; case OP_CREATE_SESSION: CREATE_SESSION4args opcreate_session; case OP_DESTROY_SESSION: DESTROY_SESSION4args opdestroy_session; case OP_FREE_STATEID: FREE_STATEID4args opfree_stateid; case OP_GET_DIR_DELEGATION: GET_DIR_DELEGATION4args opget_dir_delegation; case OP_GETDEVICEINFO: GETDEVICEINFO4args opgetdeviceinfo; case OP_GETDEVICELIST: GETDEVICELIST4args opgetdevicelist; case OP_LAYOUTCOMMIT: LAYOUTCOMMIT4args oplayoutcommit; case OP_LAYOUTGET: LAYOUTGET4args oplayoutget; case OP_LAYOUTRETURN: LAYOUTRETURN4args oplayoutreturn; case OP_SECINFO_NO_NAME: SECINFO_NO_NAME4args opsecinfo_no_name; case OP_SEQUENCE: SEQUENCE4args opsequence; case OP_SET_SSV: SET_SSV4args opset_ssv; case OP_TEST_STATEID: TEST_STATEID4args optest_stateid; case OP_WANT_DELEGATION: WANT_DELEGATION4args opwant_delegation; case OP_DESTROY_CLIENTID: DESTROY_CLIENTID4args opdestroy_clientid; case OP_RECLAIM_COMPLETE: RECLAIM_COMPLETE4args opreclaim_complete; /* Operations not new to NFSv4.1 */ case OP_ILLEGAL: void; }; union nfs_resop4 switch (nfs_opnum4 resop) { case OP_ACCESS: ACCESS4res opaccess; case OP_CLOSE: CLOSE4res opclose; case OP_COMMIT: COMMIT4res opcommit; case OP_CREATE: CREATE4res opcreate; case OP_DELEGPURGE: DELEGPURGE4res opdelegpurge; case OP_DELEGRETURN: DELEGRETURN4res opdelegreturn; case OP_GETATTR: GETATTR4res opgetattr; case OP_GETFH: GETFH4res opgetfh; case OP_LINK: LINK4res oplink; case OP_LOCK: LOCK4res oplock; case OP_LOCKT: LOCKT4res oplockt; case OP_LOCKU: LOCKU4res oplocku; case OP_LOOKUP: LOOKUP4res oplookup; case OP_LOOKUPP: LOOKUPP4res oplookupp; case OP_NVERIFY: NVERIFY4res opnverify; case OP_OPEN: OPEN4res opopen; case OP_OPENATTR: OPENATTR4res opopenattr; /* Not for NFSv4.1 */ case OP_OPEN_CONFIRM: OPEN_CONFIRM4res opopen_confirm; case OP_OPEN_DOWNGRADE: OPEN_DOWNGRADE4res opopen_downgrade; case OP_PUTFH: PUTFH4res opputfh; case OP_PUTPUBFH: PUTPUBFH4res opputpubfh; case OP_PUTROOTFH: PUTROOTFH4res opputrootfh; case OP_READ: READ4res opread; case OP_READDIR: READDIR4res opreaddir; case OP_READLINK: READLINK4res opreadlink; case OP_REMOVE: REMOVE4res opremove; case OP_RENAME: RENAME4res oprename; /* Not for NFSv4.1 */ case OP_RENEW: RENEW4res oprenew; case OP_RESTOREFH: RESTOREFH4res oprestorefh; case OP_SAVEFH: SAVEFH4res opsavefh; case OP_SECINFO: SECINFO4res opsecinfo; case OP_SETATTR: SETATTR4res opsetattr; /* Not for NFSv4.1 */ case OP_SETCLIENTID: SETCLIENTID4res opsetclientid; /* Not for NFSv4.1 */ case OP_SETCLIENTID_CONFIRM: SETCLIENTID_CONFIRM4res opsetclientid_confirm; case OP_VERIFY: VERIFY4res opverify; case OP_WRITE: WRITE4res opwrite; /* Not for NFSv4.1 */ case OP_RELEASE_LOCKOWNER: RELEASE_LOCKOWNER4res oprelease_lockowner; /* Operations new to NFSv4.1 */ case OP_BACKCHANNEL_CTL: BACKCHANNEL_CTL4res opbackchannel_ctl; case OP_BIND_CONN_TO_SESSION: BIND_CONN_TO_SESSION4res opbind_conn_to_session; case OP_EXCHANGE_ID: EXCHANGE_ID4res opexchange_id; case OP_CREATE_SESSION: CREATE_SESSION4res opcreate_session; case OP_DESTROY_SESSION: DESTROY_SESSION4res opdestroy_session; case OP_FREE_STATEID: FREE_STATEID4res opfree_stateid; case OP_GET_DIR_DELEGATION: GET_DIR_DELEGATION4res opget_dir_delegation; case OP_GETDEVICEINFO: GETDEVICEINFO4res opgetdeviceinfo; case OP_GETDEVICELIST: GETDEVICELIST4res opgetdevicelist; case OP_LAYOUTCOMMIT: LAYOUTCOMMIT4res oplayoutcommit; case OP_LAYOUTGET: LAYOUTGET4res oplayoutget; case OP_LAYOUTRETURN: LAYOUTRETURN4res oplayoutreturn; case OP_SECINFO_NO_NAME: SECINFO_NO_NAME4res opsecinfo_no_name; case OP_SEQUENCE: SEQUENCE4res opsequence; case OP_SET_SSV: SET_SSV4res opset_ssv; case OP_TEST_STATEID: TEST_STATEID4res optest_stateid; case OP_WANT_DELEGATION: WANT_DELEGATION4res opwant_delegation; case OP_DESTROY_CLIENTID: DESTROY_CLIENTID4res opdestroy_clientid; case OP_RECLAIM_COMPLETE: RECLAIM_COMPLETE4res opreclaim_complete; /* Operations not new to NFSv4.1 */ case OP_ILLEGAL: ILLEGAL4res opillegal; }; struct COMPOUND4args { utf8str_cs tag; uint32_t minorversion; nfs_argop4 argarray<>; }; struct COMPOUND4res { nfsstat4 status; utf8str_cs tag; nfs_resop4 resarray<>; }; /* * Remote file service routines */ program NFS4_PROGRAM { version NFS_V4 { void NFSPROC4_NULL(void) = 0; COMPOUND4res NFSPROC4_COMPOUND(COMPOUND4args) = 1; } = 4; } = 100003; /* * NFS4 Callback Procedure Definitions and Program */ struct CB_GETATTR4args { nfs_fh4 fh; bitmap4 attr_request; }; struct CB_GETATTR4resok { fattr4 obj_attributes; }; union CB_GETATTR4res switch (nfsstat4 status) { case NFS4_OK: CB_GETATTR4resok resok4; default: void; }; struct CB_RECALL4args { stateid4 stateid; bool truncate; nfs_fh4 fh; }; struct CB_RECALL4res { nfsstat4 status; }; /* * CB_ILLEGAL: Response for illegal operation numbers */ struct CB_ILLEGAL4res { nfsstat4 status; }; /* * NFSv4.1 callback arguments and results */ enum layoutrecall_type4 { LAYOUTRECALL4_FILE = LAYOUT4_RET_REC_FILE, LAYOUTRECALL4_FSID = LAYOUT4_RET_REC_FSID, LAYOUTRECALL4_ALL = LAYOUT4_RET_REC_ALL }; struct layoutrecall_file4 { nfs_fh4 lor_fh; offset4 lor_offset; length4 lor_length; stateid4 lor_stateid; }; union layoutrecall4 switch(layoutrecall_type4 lor_recalltype) { case LAYOUTRECALL4_FILE: layoutrecall_file4 lor_layout; case LAYOUTRECALL4_FSID: fsid4 lor_fsid; case LAYOUTRECALL4_ALL: void; }; struct CB_LAYOUTRECALL4args { layouttype4 clora_type; layoutiomode4 clora_iomode; bool clora_changed; layoutrecall4 clora_recall; }; struct CB_LAYOUTRECALL4res { nfsstat4 clorr_status; }; /* * Directory notification types. */ enum notify_type4 { NOTIFY4_CHANGE_CHILD_ATTRS = 0, NOTIFY4_CHANGE_DIR_ATTRS = 1, NOTIFY4_REMOVE_ENTRY = 2, NOTIFY4_ADD_ENTRY = 3, NOTIFY4_RENAME_ENTRY = 4, NOTIFY4_CHANGE_COOKIE_VERIFIER = 5 }; /* Changed entry information. */ struct notify_entry4 { component4 ne_file; fattr4 ne_attrs; }; /* Previous entry information */ struct prev_entry4 { notify_entry4 pe_prev_entry; /* what READDIR returned for this entry */ nfs_cookie4 pe_prev_entry_cookie; }; struct notify_remove4 { notify_entry4 nrm_old_entry; nfs_cookie4 nrm_old_entry_cookie; }; struct notify_add4 { /* * Information on object * possibly renamed over. */ notify_remove4 nad_old_entry<1>; notify_entry4 nad_new_entry; /* what READDIR would have returned for this entry */ nfs_cookie4 nad_new_entry_cookie<1>; prev_entry4 nad_prev_entry<1>; bool nad_last_entry; }; struct notify_attr4 { notify_entry4 na_changed_entry; }; struct notify_rename4 { notify_remove4 nrn_old_entry; notify_add4 nrn_new_entry; }; struct notify_verifier4 { verifier4 nv_old_cookieverf; verifier4 nv_new_cookieverf; }; /* * Objects of type notify_<>4 and * notify_device_<>4 are encoded in this. */ typedef opaque notifylist4<>; struct notify4 { /* composed from notify_type4 or notify_deviceid_type4 */ bitmap4 notify_mask; notifylist4 notify_vals; }; struct CB_NOTIFY4args { stateid4 cna_stateid; nfs_fh4 cna_fh; notify4 cna_changes<>; }; struct CB_NOTIFY4res { nfsstat4 cnr_status; }; struct CB_PUSH_DELEG4args { nfs_fh4 cpda_fh; open_delegation4 cpda_delegation; }; struct CB_PUSH_DELEG4res { nfsstat4 cpdr_status; }; const RCA4_TYPE_MASK_RDATA_DLG = 0; const RCA4_TYPE_MASK_WDATA_DLG = 1; const RCA4_TYPE_MASK_DIR_DLG = 2; const RCA4_TYPE_MASK_FILE_LAYOUT = 3; const RCA4_TYPE_MASK_BLK_LAYOUT = 4; const RCA4_TYPE_MASK_OBJ_LAYOUT_MIN = 8; const RCA4_TYPE_MASK_OBJ_LAYOUT_MAX = 9; const RCA4_TYPE_MASK_OTHER_LAYOUT_MIN = 12; const RCA4_TYPE_MASK_OTHER_LAYOUT_MAX = 15; struct CB_RECALL_ANY4args { uint32_t craa_objects_to_keep; bitmap4 craa_type_mask; }; struct CB_RECALL_ANY4res { nfsstat4 crar_status; }; typedef CB_RECALL_ANY4args CB_RECALLABLE_OBJ_AVAIL4args; struct CB_RECALLABLE_OBJ_AVAIL4res { nfsstat4 croa_status; }; struct CB_RECALL_SLOT4args { slotid4 rsa_target_highest_slotid; }; struct CB_RECALL_SLOT4res { nfsstat4 rsr_status; }; struct referring_call4 { sequenceid4 rc_sequenceid; slotid4 rc_slotid; }; struct referring_call_list4 { sessionid4 rcl_sessionid; referring_call4 rcl_referring_calls<>; }; struct CB_SEQUENCE4args { sessionid4 csa_sessionid; sequenceid4 csa_sequenceid; slotid4 csa_slotid; slotid4 csa_highest_slotid; bool csa_cachethis; referring_call_list4 csa_referring_call_lists<>; }; struct CB_SEQUENCE4resok { sessionid4 csr_sessionid; sequenceid4 csr_sequenceid; slotid4 csr_slotid; slotid4 csr_highest_slotid; slotid4 csr_target_highest_slotid; }; union CB_SEQUENCE4res switch (nfsstat4 csr_status) { case NFS4_OK: CB_SEQUENCE4resok csr_resok4; default: void; }; struct CB_WANTS_CANCELLED4args { bool cwca_contended_wants_cancelled; bool cwca_resourced_wants_cancelled; }; struct CB_WANTS_CANCELLED4res { nfsstat4 cwcr_status; }; struct CB_NOTIFY_LOCK4args { nfs_fh4 cnla_fh; lock_owner4 cnla_lock_owner; }; struct CB_NOTIFY_LOCK4res { nfsstat4 cnlr_status; }; /* * Device notification types. */ enum notify_deviceid_type4 { NOTIFY_DEVICEID4_CHANGE = 1, NOTIFY_DEVICEID4_DELETE = 2 }; /* For NOTIFY4_DEVICEID4_DELETE */ struct notify_deviceid_delete4 { layouttype4 ndd_layouttype; deviceid4 ndd_deviceid; }; /* For NOTIFY4_DEVICEID4_CHANGE */ struct notify_deviceid_change4 { layouttype4 ndc_layouttype; deviceid4 ndc_deviceid; bool ndc_immediate; }; struct CB_NOTIFY_DEVICEID4args { notify4 cnda_changes<>; }; struct CB_NOTIFY_DEVICEID4res { nfsstat4 cndr_status; }; /* * Various definitions for CB_COMPOUND */ % enum nfs_cb_opnum4 { OP_CB_GETATTR = 3, OP_CB_RECALL = 4, %/* Callback operations new to NFSv4.1 */ OP_CB_LAYOUTRECALL = 5, OP_CB_NOTIFY = 6, OP_CB_PUSH_DELEG = 7, OP_CB_RECALL_ANY = 8, OP_CB_RECALLABLE_OBJ_AVAIL = 9, OP_CB_RECALL_SLOT = 10, OP_CB_SEQUENCE = 11, OP_CB_WANTS_CANCELLED = 12, OP_CB_NOTIFY_LOCK = 13, OP_CB_NOTIFY_DEVICEID = 14, OP_CB_ILLEGAL = 10044 }; union nfs_cb_argop4 switch (unsigned argop) { case OP_CB_GETATTR: CB_GETATTR4args opcbgetattr; case OP_CB_RECALL: CB_RECALL4args opcbrecall; case OP_CB_LAYOUTRECALL: CB_LAYOUTRECALL4args opcblayoutrecall; case OP_CB_NOTIFY: CB_NOTIFY4args opcbnotify; case OP_CB_PUSH_DELEG: CB_PUSH_DELEG4args opcbpush_deleg; case OP_CB_RECALL_ANY: CB_RECALL_ANY4args opcbrecall_any; case OP_CB_RECALLABLE_OBJ_AVAIL: CB_RECALLABLE_OBJ_AVAIL4args opcbrecallable_obj_avail; case OP_CB_RECALL_SLOT: CB_RECALL_SLOT4args opcbrecall_slot; case OP_CB_SEQUENCE: CB_SEQUENCE4args opcbsequence; case OP_CB_WANTS_CANCELLED: CB_WANTS_CANCELLED4args opcbwants_cancelled; case OP_CB_NOTIFY_LOCK: CB_NOTIFY_LOCK4args opcbnotify_lock; case OP_CB_NOTIFY_DEVICEID: CB_NOTIFY_DEVICEID4args opcbnotify_deviceid; case OP_CB_ILLEGAL: void; }; union nfs_cb_resop4 switch (unsigned resop) { case OP_CB_GETATTR: CB_GETATTR4res opcbgetattr; case OP_CB_RECALL: CB_RECALL4res opcbrecall; /* new NFSv4.1 operations */ case OP_CB_LAYOUTRECALL: CB_LAYOUTRECALL4res opcblayoutrecall; case OP_CB_NOTIFY: CB_NOTIFY4res opcbnotify; case OP_CB_PUSH_DELEG: CB_PUSH_DELEG4res opcbpush_deleg; case OP_CB_RECALL_ANY: CB_RECALL_ANY4res opcbrecall_any; case OP_CB_RECALLABLE_OBJ_AVAIL: CB_RECALLABLE_OBJ_AVAIL4res opcbrecallable_obj_avail; case OP_CB_RECALL_SLOT: CB_RECALL_SLOT4res opcbrecall_slot; case OP_CB_SEQUENCE: CB_SEQUENCE4res opcbsequence; case OP_CB_WANTS_CANCELLED: CB_WANTS_CANCELLED4res opcbwants_cancelled; case OP_CB_NOTIFY_LOCK: CB_NOTIFY_LOCK4res opcbnotify_lock; case OP_CB_NOTIFY_DEVICEID: CB_NOTIFY_DEVICEID4res opcbnotify_deviceid; /* Not new operation */ case OP_CB_ILLEGAL: CB_ILLEGAL4res opcbillegal; }; struct CB_COMPOUND4args { utf8str_cs tag; uint32_t minorversion; uint32_t callback_ident; nfs_cb_argop4 argarray<>; }; struct CB_COMPOUND4res { nfsstat4 status; utf8str_cs tag; nfs_cb_resop4 resarray<>; }; /* * Program number is in the transient range since the client * will assign the exact transient program number and provide * that to the server via the SETCLIENTID operation. */ program NFS4_CALLBACK { version NFS_CB { void CB_NULL(void) = 0; CB_COMPOUND4res CB_COMPOUND(CB_COMPOUND4args) = 1; } = 1; } = 0x40000000; nfs-ganesha-2.6.0/src/Protocols/XDR/nlm4.x000066400000000000000000000073441324272410200201710ustar00rootroot00000000000000 /* The maximum length of the string identifying the caller. */ const LM_MAXSTRLEN = 1024; /* The maximum number of bytes in the nlm_notify name argument. */ const LM_MAXNAMELEN = 1025; const MAXNETOBJ_SZ = 1024; /* NSM related constatnts */ const SM_MAXSTRLEN = 1024; const SM_PRIV_SZ = 16; /* * Basic typedefs for RFC 1832 data type definitions */ typedef int int32_t; typedef unsigned int uint32_t; typedef hyper int64_t; typedef unsigned hyper uint64_t; enum nlm4_stats { NLM4_GRANTED = 0, NLM4_DENIED = 1, NLM4_DENIED_NOLOCKS = 2, NLM4_BLOCKED = 3, NLM4_DENIED_GRACE_PERIOD = 4, NLM4_DEADLCK = 5, NLM4_ROFS = 6, NLM4_STALE_FH = 7, NLM4_FBIG = 8, NLM4_FAILED = 9 }; struct nlm4_stat { nlm4_stats stat; }; struct nlm4_res { netobj cookie; nlm4_stat stat; }; struct nlm4_holder { bool exclusive; int32_t svid; netobj oh; uint64_t l_offset; uint64_t l_len; }; union nlm4_testrply switch (nlm4_stats stat) { case NLM4_DENIED: struct nlm4_holder holder; /* holder of the lock */ default: void; }; struct nlm4_testres { netobj cookie; nlm4_testrply test_stat; }; struct nlm4_lock { string caller_name; netobj fh; netobj oh; int32_t svid; uint64_t l_offset; uint64_t l_len; }; struct nlm4_lockargs { netobj cookie; bool block; /* Flag to indicate blocking behaviour. */ bool exclusive; /* If exclusive access is desired. */ struct nlm4_lock alock; /* The actual lock data (see above) */ bool reclaim; /* used for recovering locks */ int32_t state; /* specify local NSM state */ }; struct nlm4_cancargs { netobj cookie; bool block; bool exclusive; struct nlm4_lock alock; }; struct nlm4_testargs { netobj cookie; bool exclusive; struct nlm4_lock alock; }; struct nlm4_unlockargs { netobj cookie; struct nlm4_lock alock; }; enum fsh4_mode { fsm_DN = 0, /* deny none */ fsm_DR = 1, /* deny read */ fsm_DW = 2, /* deny write */ fsm_DRW = 3 /* deny read/write */ }; enum fsh4_access { fsa_NONE = 0, /* for completeness */ fsa_R = 1, /* read-only */ fsa_W = 2, /* write-only */ fsa_RW = 3 /* read/write */ }; struct nlm4_share { string caller_name; netobj fh; netobj oh; fsh4_mode mode; fsh4_access access; }; struct nlm4_shareargs { netobj cookie; nlm4_share share; /* actual share data */ bool reclaim; /* used for recovering shares */ }; struct nlm4_shareres { netobj cookie; nlm4_stats stat; int32_t sequence; }; struct nlm4_notify { string name; int64_t state; }; struct nlm4_sm_notifyargs { string name; int32_t state; opaque priv[SM_PRIV_SZ]; }; #ifdef RPC_HDR %extern void nlm_init(void); #endif program NLMPROG { version NLM4_VERS { void NLMPROC4_NULL(void) = 0; nlm4_testres NLMPROC4_TEST(nlm4_testargs) = 1; nlm4_res NLMPROC4_LOCK(nlm4_lockargs) = 2; nlm4_res NLMPROC4_CANCEL(nlm4_cancargs) = 3; nlm4_res NLMPROC4_UNLOCK(nlm4_unlockargs) = 4; nlm4_res NLMPROC4_GRANTED(nlm4_testargs) = 5; void NLMPROC4_TEST_MSG(nlm4_testargs) = 6; void NLMPROC4_LOCK_MSG(nlm4_lockargs) = 7; void NLMPROC4_CANCEL_MSG(nlm4_cancargs) = 8; void NLMPROC4_UNLOCK_MSG(nlm4_unlockargs) = 9; void NLMPROC4_GRANTED_MSG(nlm4_testargs) = 10; void NLMPROC4_TEST_RES(nlm4_testres) = 11; void NLMPROC4_LOCK_RES(nlm4_res) = 12; void NLMPROC4_CANCEL_RES(nlm4_res) = 13; void NLMPROC4_UNLOCK_RES(nlm4_res) = 14; void NLMPROC4_GRANTED_RES(nlm4_res) = 15; void NLMPROC4_SM_NOTIFY(nlm4_sm_notifyargs) = 16; nlm4_shareres NLMPROC4_SHARE(nlm4_shareargs) = 20; nlm4_shareres NLMPROC4_UNSHARE(nlm4_shareargs) = 21; nlm4_res NLMPROC4_NM_LOCK(nlm4_lockargs) = 22; void NLMPROC4_FREE_ALL(nlm4_notify) = 23; } = 4; } = 100021; nfs-ganesha-2.6.0/src/Protocols/XDR/nsm.x000066400000000000000000000023221324272410200201030ustar00rootroot00000000000000/* NSM Interface */ /* * This defines the maximum length of the string * identifying the caller. */ const SM_MAXSTRLEN = 1024; const SM_PROG = 100024; const SM_VERS = 1; const SM_MON = 2; const SM_UNMON = 3; const SM_UNMON_ALL = 4; const SM_NOTIFY = 6; enum res { STAT_SUCC = 0, /* NSM agrees to monitor. */ STAT_FAIL = 1 /* NSM cannot monitor. */ }; struct sm_stat_res { res res_stat; int state; }; struct sm_stat { int state; /* state number of NSM */ }; struct my_id { string my_name; /* hostname */ int my_prog; /* RPC program number */ int my_vers; /* program version number */ int my_proc; /* procedure number */ }; struct mon_id { string mon_name; /* name of the host to be monitored */ struct my_id my_id; }; struct mon { struct mon_id mon_id; opaque priv[16]; /* private information */ }; struct notify { string my_name; /* hostname */ int state; /* state */ }; #ifdef RPC_HDR %extern int nsm_monitor(char *host); %extern int nsm_unmonitor(char *host); %extern int nsm_unmonitor_all(void); %extern int nsm_notify(char *host, int state); #endif nfs-ganesha-2.6.0/src/Protocols/XDR/rquota.x000066400000000000000000000066161324272410200206330ustar00rootroot00000000000000/* @(#)rquota.x 2.1 88/08/01 4.0 RPCSRC */ /* @(#)rquota.x 1.2 87/09/20 Copyr 1987 Sun Micro */ /* * Remote quota protocol * Requires unix authentication */ const RQ_PATHLEN = 1024; struct sq_dqblk { unsigned int rq_bhardlimit; /* absolute limit on disk blks alloc */ unsigned int rq_bsoftlimit; /* preferred limit on disk blks */ unsigned int rq_curblocks; /* current block count */ unsigned int rq_fhardlimit; /* absolute limit on allocated files */ unsigned int rq_fsoftlimit; /* preferred file limit */ unsigned int rq_curfiles; /* current # allocated files */ unsigned int rq_btimeleft; /* time left for excessive disk use */ unsigned int rq_ftimeleft; /* time left for excessive files */ }; struct getquota_args { string gqa_pathp; /* path to filesystem of interest */ int gqa_uid; /* Inquire about quota for uid */ }; struct setquota_args { int sqa_qcmd; string sqa_pathp; /* path to filesystem of interest */ int sqa_id; /* Set quota for uid */ sq_dqblk sqa_dqblk; }; struct ext_getquota_args { string gqa_pathp; /* path to filesystem of interest */ int gqa_type; /* Type of quota info is needed about */ int gqa_id; /* Inquire about quota for id */ }; struct ext_setquota_args { int sqa_qcmd; string sqa_pathp; /* path to filesystem of interest */ int sqa_id; /* Set quota for id */ int sqa_type; /* Type of quota to set */ sq_dqblk sqa_dqblk; }; /* * remote quota structure */ struct rquota { int rq_bsize; /* block size for block counts */ bool rq_active; /* indicates whether quota is active */ unsigned int rq_bhardlimit; /* absolute limit on disk blks alloc */ unsigned int rq_bsoftlimit; /* preferred limit on disk blks */ unsigned int rq_curblocks; /* current block count */ unsigned int rq_fhardlimit; /* absolute limit on allocated files */ unsigned int rq_fsoftlimit; /* preferred file limit */ unsigned int rq_curfiles; /* current # allocated files */ unsigned int rq_btimeleft; /* time left for excessive disk use */ unsigned int rq_ftimeleft; /* time left for excessive files */ }; enum qr_status { Q_OK = 1, /* quota returned */ Q_NOQUOTA = 2, /* noquota for uid */ Q_EPERM = 3 /* no permission to access quota */ }; union getquota_rslt switch (qr_status status) { case Q_OK: rquota gqr_rquota; /* valid if status == Q_OK */ case Q_NOQUOTA: void; case Q_EPERM: void; }; union setquota_rslt switch (qr_status status) { case Q_OK: rquota sqr_rquota; /* valid if status == Q_OK */ case Q_NOQUOTA: void; case Q_EPERM: void; }; program RQUOTAPROG { version RQUOTAVERS { /* * Get all quotas */ getquota_rslt RQUOTAPROC_GETQUOTA(getquota_args) = 1; /* * Get active quotas only */ getquota_rslt RQUOTAPROC_GETACTIVEQUOTA(getquota_args) = 2; /* * Set all quotas */ setquota_rslt RQUOTAPROC_SETQUOTA(setquota_args) = 3; /* * Get active quotas only */ setquota_rslt RQUOTAPROC_SETACTIVEQUOTA(setquota_args) = 4; } = 1; version EXT_RQUOTAVERS { /* * Get all quotas */ getquota_rslt RQUOTAPROC_GETQUOTA(ext_getquota_args) = 1; /* * Get active quotas only */ getquota_rslt RQUOTAPROC_GETACTIVEQUOTA(ext_getquota_args) = 2; /* * Set all quotas */ setquota_rslt RQUOTAPROC_SETQUOTA(ext_setquota_args) = 3; /* * Set active quotas only */ setquota_rslt RQUOTAPROC_SETACTIVEQUOTA(ext_setquota_args) = 4; } = 2; } = 100011; nfs-ganesha-2.6.0/src/Protocols/XDR/xdr_mount.c000066400000000000000000000112261324272410200213030ustar00rootroot00000000000000/* * Please do not edit this file. * It was generated using rpcgen. */ #include "config.h" #include "gsh_rpc.h" #include "mount.h" #include "nfs23.h" #include "nfs_fh.h" bool xdr_mountstat3(xdrs, objp) register XDR *xdrs; mountstat3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return (false); return (true); } bool xdr_fhandle3(xdrs, objp) register XDR *xdrs; fhandle3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!inline_xdr_bytes (xdrs, (char **)&objp->fhandle3_val, (u_int *) & objp->fhandle3_len, NFS3_FHSIZE)) return (false); return (true); } bool xdr_dirpath(xdrs, objp) register XDR *xdrs; dirpath *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!inline_xdr_string(xdrs, objp, MNTPATHLEN)) return (false); return (true); } bool xdr_name(xdrs, objp) register XDR *xdrs; name *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!inline_xdr_string(xdrs, objp, MNTNAMLEN)) return (false); return (true); } bool xdr_groups(xdrs, objp) register XDR *xdrs; groups *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_pointer (xdrs, (char **)objp, sizeof(struct groupnode), (xdrproc_t) xdr_groupnode)) return (false); return (true); } bool xdr_groupnode(xdrs, objp) register XDR *xdrs; groupnode *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_name(xdrs, &objp->gr_name)) return (false); if (!xdr_groups(xdrs, &objp->gr_next)) return (false); return (true); } bool xdr_exports(xdrs, objp) register XDR *xdrs; exports *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_pointer (xdrs, (char **)objp, sizeof(struct exportnode), (xdrproc_t) xdr_exportnode)) return (false); return (true); } bool xdr_exportnode(xdrs, objp) register XDR *xdrs; exportnode *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_dirpath(xdrs, &objp->ex_dir)) return (false); if (!xdr_groups(xdrs, &objp->ex_groups)) return (false); if (!xdr_exports(xdrs, &objp->ex_next)) return (false); return (true); } bool xdr_mountlist(xdrs, objp) register XDR *xdrs; mountlist *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_pointer (xdrs, (char **)objp, sizeof(struct mountbody), (xdrproc_t) xdr_mountbody)) return (false); return (true); } bool xdr_mountbody(xdrs, objp) register XDR *xdrs; mountbody *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_name(xdrs, &objp->ml_hostname)) return (false); if (!xdr_dirpath(xdrs, &objp->ml_directory)) return (false); if (!xdr_mountlist(xdrs, &objp->ml_next)) return (false); return (true); } bool xdr_mountres3_ok(xdrs, objp) register XDR *xdrs; mountres3_ok *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_fhandle3(xdrs, &objp->fhandle)) return (false); if (!xdr_array (xdrs, (char **)&objp->auth_flavors.auth_flavors_val, &objp->auth_flavors.auth_flavors_len, XDR_ARRAY_MAXLEN, sizeof(int), (xdrproc_t) xdr_int)) return (false); return (true); } bool xdr_mountres3(xdrs, objp) register XDR *xdrs; mountres3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_mountstat3(xdrs, &objp->fhs_status)) return (false); switch (objp->fhs_status) { case MNT3_OK: if (!xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo)) return (false); break; default: return (true); break; } return (true); } nfs-ganesha-2.6.0/src/Protocols/XDR/xdr_nfs23.c000066400000000000000000001610601324272410200210760ustar00rootroot00000000000000/* * Please do not edit this file. * It was generated using rpcgen. */ #include "config.h" #include "gsh_rpc.h" #include "nfs23.h" #include "nfs_fh.h" static struct nfs_request_lookahead dummy_lookahead = { .flags = 0, .read = 0, .write = 0 }; bool xdr_nfspath2(xdrs, objp) register XDR *xdrs; nfspath2 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_string(xdrs, objp, NFS2_MAXPATHLEN)) return (false); return (true); } bool xdr_filename2(xdrs, objp) register XDR *xdrs; filename2 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_string(xdrs, objp, NFS2_MAXNAMLEN)) return (false); return (true); } bool xdr_fhandle2(xdrs, objp) register XDR *xdrs; fhandle2 objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_opaque(xdrs, objp, NFS2_FHSIZE)) return (false); return (true); } bool xdr_nfsdata2(xdrs, objp) register XDR *xdrs; nfsdata2 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_bytes (xdrs, (char **)&objp->nfsdata2_val, (u_int *) & objp->nfsdata2_len, NFS2_MAXDATA)) return (false); return (true); } bool xdr_nfscookie2(xdrs, objp) register XDR *xdrs; nfscookie2 objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_opaque(xdrs, objp, NFS2_COOKIESIZE)) return (false); return (true); } bool xdr_fhstatus2(xdrs, objp) register XDR *xdrs; fhstatus2 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_u_int(xdrs, &objp->status)) return (false); switch (objp->status) { case 0: if (!xdr_fhandle2(xdrs, objp->fhstatus2_u.directory)) return (false); break; } return (true); } bool xdr_nfs3_uint64(xdrs, objp) register XDR *xdrs; nfs3_uint64 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_u_longlong_t(xdrs, (quad_t *) objp)) return (false); return (true); } bool xdr_nfs3_int64(xdrs, objp) register XDR *xdrs; nfs3_int64 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_longlong_t(xdrs, (quad_t *) objp)) return (false); return (true); } bool xdr_nfs3_uint32(xdrs, objp) register XDR *xdrs; nfs3_uint32 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_u_int(xdrs, objp)) return (false); return (true); } bool xdr_nfs3_int32(xdrs, objp) register XDR *xdrs; nfs3_int32 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_int(xdrs, objp)) return (false); return (true); } bool xdr_filename3(xdrs, objp) register XDR *xdrs; filename3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_string(xdrs, objp, XDR_STRING_MAXLEN)) return (false); return (true); } bool xdr_nfspath3(xdrs, objp) register XDR *xdrs; nfspath3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_string(xdrs, objp, XDR_STRING_MAXLEN)) return (false); return (true); } bool xdr_fileid3(xdrs, objp) register XDR *xdrs; fileid3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfs3_uint64(xdrs, objp)) return (false); return (true); } bool xdr_cookie3(xdrs, objp) register XDR *xdrs; cookie3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfs3_uint64(xdrs, objp)) return (false); return (true); } bool xdr_cookieverf3(xdrs, objp) register XDR *xdrs; cookieverf3 objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_opaque(xdrs, objp, 8)) return (false); return (true); } bool xdr_createverf3(xdrs, objp) register XDR *xdrs; createverf3 objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_opaque(xdrs, objp, 8)) return (false); return (true); } bool xdr_writeverf3(xdrs, objp) register XDR *xdrs; writeverf3 objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_opaque(xdrs, objp, 8)) return (false); return (true); } bool xdr_uid3(xdrs, objp) register XDR *xdrs; uid3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfs3_uint32(xdrs, objp)) return (false); return (true); } bool xdr_gid3(xdrs, objp) register XDR *xdrs; gid3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfs3_uint32(xdrs, objp)) return (false); return (true); } bool xdr_size3(xdrs, objp) register XDR *xdrs; size3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfs3_uint64(xdrs, objp)) return (false); return (true); } bool xdr_offset3(xdrs, objp) register XDR *xdrs; offset3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfs3_uint64(xdrs, objp)) return (false); return (true); } bool xdr_mode3(xdrs, objp) register XDR *xdrs; mode3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfs3_uint32(xdrs, objp)) return (false); return (true); } bool xdr_count3(xdrs, objp) register XDR *xdrs; count3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfs3_uint32(xdrs, objp)) return (false); return (true); } bool xdr_nfsstat3(xdrs, objp) register XDR *xdrs; nfsstat3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_enum(xdrs, (enum_t *) objp)) return (false); return (true); } bool xdr_ftype3(xdrs, objp) register XDR *xdrs; ftype3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_enum(xdrs, (enum_t *) objp)) return (false); return (true); } bool xdr_specdata3(xdrs, objp) register XDR *xdrs; specdata3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfs3_uint32(xdrs, &objp->specdata1)) return (false); if (!xdr_nfs3_uint32(xdrs, &objp->specdata2)) return (false); return (true); } bool xdr_nfs_fh3(xdrs, objp) register XDR *xdrs; nfs_fh3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_bytes (xdrs, (char **)&objp->data.data_val, (u_int *) & objp->data.data_len, 64)) return (false); return (true); } bool xdr_nfstime3(xdrs, objp) register XDR *xdrs; nfstime3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfs3_uint32(xdrs, &objp->tv_sec)) return (false); if (!xdr_nfs3_uint32(xdrs, &objp->tv_nsec)) return (false); return (true); } bool xdr_fattr3(xdrs, objp) register XDR *xdrs; fattr3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_ftype3(xdrs, &objp->type)) return (false); if (!xdr_mode3(xdrs, &objp->mode)) return (false); if (!xdr_nfs3_uint32(xdrs, &objp->nlink)) return (false); if (!xdr_uid3(xdrs, &objp->uid)) return (false); if (!xdr_gid3(xdrs, &objp->gid)) return (false); if (!xdr_size3(xdrs, &objp->size)) return (false); if (!xdr_size3(xdrs, &objp->used)) return (false); if (!xdr_specdata3(xdrs, &objp->rdev)) return (false); if (!xdr_nfs3_uint64(xdrs, &objp->fsid)) return (false); if (!xdr_fileid3(xdrs, &objp->fileid)) return (false); if (!xdr_nfstime3(xdrs, &objp->atime)) return (false); if (!xdr_nfstime3(xdrs, &objp->mtime)) return (false); if (!xdr_nfstime3(xdrs, &objp->ctime)) return (false); return (true); } bool xdr_post_op_attr(xdrs, objp) register XDR *xdrs; post_op_attr *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_bool(xdrs, &objp->attributes_follow)) return (false); switch (objp->attributes_follow) { case TRUE: if (!xdr_fattr3(xdrs, &objp->post_op_attr_u.attributes)) return (false); break; case FALSE: break; default: return (false); } return (true); } bool xdr_wcc_attr(xdrs, objp) register XDR *xdrs; wcc_attr *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_size3(xdrs, &objp->size)) return (false); if (!xdr_nfstime3(xdrs, &objp->mtime)) return (false); if (!xdr_nfstime3(xdrs, &objp->ctime)) return (false); return (true); } bool xdr_pre_op_attr(xdrs, objp) register XDR *xdrs; pre_op_attr *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_bool(xdrs, &objp->attributes_follow)) return (false); switch (objp->attributes_follow) { case TRUE: if (!xdr_wcc_attr(xdrs, &objp->pre_op_attr_u.attributes)) return (false); break; case FALSE: break; default: return (false); } return (true); } bool xdr_wcc_data(xdrs, objp) register XDR *xdrs; wcc_data *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_pre_op_attr(xdrs, &objp->before)) return (false); if (!xdr_post_op_attr(xdrs, &objp->after)) return (false); return (true); } bool xdr_post_op_fh3(xdrs, objp) register XDR *xdrs; post_op_fh3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_bool(xdrs, &objp->handle_follows)) return (false); switch (objp->handle_follows) { case TRUE: if (!xdr_nfs_fh3(xdrs, &objp->post_op_fh3_u.handle)) return (false); break; case FALSE: break; default: return (false); } return (true); } bool xdr_time_how(xdrs, objp) register XDR *xdrs; time_how *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_enum(xdrs, (enum_t *) objp)) return (false); return (true); } bool xdr_set_mode3(xdrs, objp) register XDR *xdrs; set_mode3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_bool(xdrs, &objp->set_it)) return (false); switch (objp->set_it) { case TRUE: if (!xdr_mode3(xdrs, &objp->set_mode3_u.mode)) return (false); break; } return (true); } bool xdr_set_uid3(xdrs, objp) register XDR *xdrs; set_uid3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_bool(xdrs, &objp->set_it)) return (false); switch (objp->set_it) { case TRUE: if (!xdr_uid3(xdrs, &objp->set_uid3_u.uid)) return (false); break; } return (true); } bool xdr_set_gid3(xdrs, objp) register XDR *xdrs; set_gid3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_bool(xdrs, &objp->set_it)) return (false); switch (objp->set_it) { case TRUE: if (!xdr_gid3(xdrs, &objp->set_gid3_u.gid)) return (false); break; } return (true); } bool xdr_set_size3(xdrs, objp) register XDR *xdrs; set_size3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_bool(xdrs, &objp->set_it)) return (false); switch (objp->set_it) { case TRUE: if (!xdr_size3(xdrs, &objp->set_size3_u.size)) return (false); break; } return (true); } bool xdr_set_atime(xdrs, objp) register XDR *xdrs; set_atime *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_time_how(xdrs, &objp->set_it)) return (false); switch (objp->set_it) { case SET_TO_CLIENT_TIME: if (!xdr_nfstime3(xdrs, &objp->set_atime_u.atime)) return (false); break; default: return (true); break; } return (true); } bool xdr_set_mtime(xdrs, objp) register XDR *xdrs; set_mtime *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_time_how(xdrs, &objp->set_it)) return (false); switch (objp->set_it) { case SET_TO_CLIENT_TIME: if (!xdr_nfstime3(xdrs, &objp->set_mtime_u.mtime)) return (false); break; default: return (true); break; } return (true); } bool xdr_sattr3(xdrs, objp) register XDR *xdrs; sattr3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_set_mode3(xdrs, &objp->mode)) return (false); if (!xdr_set_uid3(xdrs, &objp->uid)) return (false); if (!xdr_set_gid3(xdrs, &objp->gid)) return (false); if (!xdr_set_size3(xdrs, &objp->size)) return (false); if (!xdr_set_atime(xdrs, &objp->atime)) return (false); if (!xdr_set_mtime(xdrs, &objp->mtime)) return (false); return (true); } bool xdr_diropargs3(xdrs, objp) register XDR *xdrs; diropargs3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfs_fh3(xdrs, &objp->dir)) return (false); if (!xdr_filename3(xdrs, &objp->name)) return (false); return (true); } bool xdr_GETATTR3args(xdrs, objp) register XDR *xdrs; GETATTR3args *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfs_fh3(xdrs, &objp->object)) return (false); return (true); } bool xdr_GETATTR3resok(xdrs, objp) register XDR *xdrs; GETATTR3resok *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_fattr3(xdrs, &objp->obj_attributes)) return (false); return (true); } bool xdr_GETATTR3res(xdrs, objp) register XDR *xdrs; GETATTR3res *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfsstat3(xdrs, &objp->status)) return (false); switch (objp->status) { case NFS3_OK: if (!xdr_GETATTR3resok(xdrs, &objp->GETATTR3res_u.resok)) return (false); break; default: return (true); break; } return (true); } bool xdr_sattrguard3(xdrs, objp) register XDR *xdrs; sattrguard3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_bool(xdrs, &objp->check)) return (false); switch (objp->check) { case TRUE: if (!xdr_nfstime3(xdrs, &objp->sattrguard3_u.obj_ctime)) return (false); break; case FALSE: break; default: return (false); } return (true); } bool xdr_SETATTR3args(xdrs, objp) register XDR *xdrs; SETATTR3args *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfs_fh3(xdrs, &objp->object)) return (false); if (!xdr_sattr3(xdrs, &objp->new_attributes)) return (false); if (!xdr_sattrguard3(xdrs, &objp->guard)) return (false); return (true); } bool xdr_SETATTR3resok(xdrs, objp) register XDR *xdrs; SETATTR3resok *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_wcc_data(xdrs, &objp->obj_wcc)) return (false); return (true); } bool xdr_SETATTR3resfail(xdrs, objp) register XDR *xdrs; SETATTR3resfail *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_wcc_data(xdrs, &objp->obj_wcc)) return (false); return (true); } bool xdr_SETATTR3res(xdrs, objp) register XDR *xdrs; SETATTR3res *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfsstat3(xdrs, &objp->status)) return (false); switch (objp->status) { case NFS3_OK: if (!xdr_SETATTR3resok(xdrs, &objp->SETATTR3res_u.resok)) return (false); break; default: if (!xdr_SETATTR3resfail(xdrs, &objp->SETATTR3res_u.resfail)) return (false); break; } return (true); } bool xdr_LOOKUP3args(xdrs, objp) register XDR *xdrs; LOOKUP3args *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_diropargs3(xdrs, &objp->what)) return (false); return (true); } bool xdr_LOOKUP3resok(xdrs, objp) register XDR *xdrs; LOOKUP3resok *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfs_fh3(xdrs, &objp->object)) return (false); if (!xdr_post_op_attr(xdrs, &objp->obj_attributes)) return (false); if (!xdr_post_op_attr(xdrs, &objp->dir_attributes)) return (false); return (true); } bool xdr_LOOKUP3resfail(xdrs, objp) register XDR *xdrs; LOOKUP3resfail *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_post_op_attr(xdrs, &objp->dir_attributes)) return (false); return (true); } bool xdr_LOOKUP3res(xdrs, objp) register XDR *xdrs; LOOKUP3res *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfsstat3(xdrs, &objp->status)) return (false); switch (objp->status) { case NFS3_OK: if (!xdr_LOOKUP3resok(xdrs, &objp->LOOKUP3res_u.resok)) return (false); break; default: if (!xdr_LOOKUP3resfail(xdrs, &objp->LOOKUP3res_u.resfail)) return (false); break; } return (true); } bool xdr_ACCESS3args(xdrs, objp) register XDR *xdrs; ACCESS3args *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfs_fh3(xdrs, &objp->object)) return (false); if (!xdr_nfs3_uint32(xdrs, &objp->access)) return (false); return (true); } bool xdr_ACCESS3resok(xdrs, objp) register XDR *xdrs; ACCESS3resok *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_post_op_attr(xdrs, &objp->obj_attributes)) return (false); if (!xdr_nfs3_uint32(xdrs, &objp->access)) return (false); return (true); } bool xdr_ACCESS3resfail(xdrs, objp) register XDR *xdrs; ACCESS3resfail *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_post_op_attr(xdrs, &objp->obj_attributes)) return (false); return (true); } bool xdr_ACCESS3res(xdrs, objp) register XDR *xdrs; ACCESS3res *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfsstat3(xdrs, &objp->status)) return (false); switch (objp->status) { case NFS3_OK: if (!xdr_ACCESS3resok(xdrs, &objp->ACCESS3res_u.resok)) return (false); break; default: if (!xdr_ACCESS3resfail(xdrs, &objp->ACCESS3res_u.resfail)) return (false); break; } return (true); } bool xdr_READLINK3args(xdrs, objp) register XDR *xdrs; READLINK3args *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfs_fh3(xdrs, &objp->symlink)) return (false); return (true); } bool xdr_READLINK3resok(xdrs, objp) register XDR *xdrs; READLINK3resok *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_post_op_attr(xdrs, &objp->symlink_attributes)) return (false); if (!xdr_nfspath3(xdrs, &objp->data)) return (false); return (true); } bool xdr_READLINK3resfail(xdrs, objp) register XDR *xdrs; READLINK3resfail *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_post_op_attr(xdrs, &objp->symlink_attributes)) return (false); return (true); } bool xdr_READLINK3res(xdrs, objp) register XDR *xdrs; READLINK3res *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfsstat3(xdrs, &objp->status)) return (false); switch (objp->status) { case NFS3_OK: if (!xdr_READLINK3resok(xdrs, &objp->READLINK3res_u.resok)) return (false); break; default: if (!xdr_READLINK3resfail(xdrs, &objp->READLINK3res_u.resfail)) return (false); break; } return (true); } bool xdr_READ3args(xdrs, objp) register XDR *xdrs; READ3args *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif struct nfs_request_lookahead *lkhd = xdrs->x_public ? (struct nfs_request_lookahead *)xdrs-> x_public : &dummy_lookahead; if (!xdr_nfs_fh3(xdrs, &objp->file)) return (false); if (!xdr_offset3(xdrs, &objp->offset)) return (false); if (!xdr_count3(xdrs, &objp->count)) return (false); lkhd->flags = NFS_LOOKAHEAD_READ; (lkhd->read)++; return (true); } bool xdr_READ3resok(xdrs, objp) register XDR *xdrs; READ3resok *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_post_op_attr(xdrs, &objp->file_attributes)) return (false); if (!xdr_count3(xdrs, &objp->count)) return (false); if (!xdr_bool(xdrs, &objp->eof)) return (false); if (!xdr_bytes (xdrs, (char **)&objp->data.data_val, &objp->data.data_len, XDR_BYTES_MAXLEN_IO)) return (false); return (true); } bool xdr_READ3resfail(xdrs, objp) register XDR *xdrs; READ3resfail *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_post_op_attr(xdrs, &objp->file_attributes)) return (false); return (true); } bool xdr_READ3res(xdrs, objp) register XDR *xdrs; READ3res *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfsstat3(xdrs, &objp->status)) return (false); switch (objp->status) { case NFS3_OK: if (!xdr_READ3resok(xdrs, &objp->READ3res_u.resok)) return (false); break; default: if (!xdr_READ3resfail(xdrs, &objp->READ3res_u.resfail)) return (false); break; } return (true); } bool xdr_stable_how(xdrs, objp) register XDR *xdrs; stable_how *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_enum(xdrs, (enum_t *) objp)) return (false); return (true); } bool xdr_WRITE3args(xdrs, objp) register XDR *xdrs; WRITE3args *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif struct nfs_request_lookahead *lkhd = xdrs->x_public ? (struct nfs_request_lookahead *)xdrs-> x_public : &dummy_lookahead; if (!xdr_nfs_fh3(xdrs, &objp->file)) return (false); if (!xdr_offset3(xdrs, &objp->offset)) return (false); if (!xdr_count3(xdrs, &objp->count)) return (false); if (!xdr_stable_how(xdrs, &objp->stable)) return (false); if (!xdr_bytes (xdrs, (char **)&objp->data.data_val, &objp->data.data_len, XDR_BYTES_MAXLEN_IO)) return (false); lkhd->flags |= NFS_LOOKAHEAD_WRITE; (lkhd->write)++; return (true); } bool xdr_WRITE3resok(xdrs, objp) register XDR *xdrs; WRITE3resok *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_wcc_data(xdrs, &objp->file_wcc)) return (false); if (!xdr_count3(xdrs, &objp->count)) return (false); if (!xdr_stable_how(xdrs, &objp->committed)) return (false); if (!xdr_writeverf3(xdrs, objp->verf)) return (false); return (true); } bool xdr_WRITE3resfail(xdrs, objp) register XDR *xdrs; WRITE3resfail *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_wcc_data(xdrs, &objp->file_wcc)) return (false); return (true); } bool xdr_WRITE3res(xdrs, objp) register XDR *xdrs; WRITE3res *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfsstat3(xdrs, &objp->status)) return (false); switch (objp->status) { case NFS3_OK: if (!xdr_WRITE3resok(xdrs, &objp->WRITE3res_u.resok)) return (false); break; default: if (!xdr_WRITE3resfail(xdrs, &objp->WRITE3res_u.resfail)) return (false); break; } return (true); } bool xdr_createmode3(xdrs, objp) register XDR *xdrs; createmode3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_enum(xdrs, (enum_t *) objp)) return (false); return (true); } bool xdr_createhow3(xdrs, objp) register XDR *xdrs; createhow3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_createmode3(xdrs, &objp->mode)) return (false); switch (objp->mode) { case UNCHECKED: case GUARDED: if (!xdr_sattr3(xdrs, &objp->createhow3_u.obj_attributes)) return (false); break; case EXCLUSIVE: if (!xdr_createverf3(xdrs, objp->createhow3_u.verf)) return (false); break; default: return (false); } return (true); } bool xdr_CREATE3args(xdrs, objp) register XDR *xdrs; CREATE3args *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif struct nfs_request_lookahead *lkhd = xdrs->x_public ? (struct nfs_request_lookahead *)xdrs-> x_public : &dummy_lookahead; if (!xdr_diropargs3(xdrs, &objp->where)) return (false); if (!xdr_createhow3(xdrs, &objp->how)) return (false); lkhd->flags |= NFS_LOOKAHEAD_CREATE; return (true); } bool xdr_CREATE3resok(xdrs, objp) register XDR *xdrs; CREATE3resok *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_post_op_fh3(xdrs, &objp->obj)) return (false); if (!xdr_post_op_attr(xdrs, &objp->obj_attributes)) return (false); if (!xdr_wcc_data(xdrs, &objp->dir_wcc)) return (false); return (true); } bool xdr_CREATE3resfail(xdrs, objp) register XDR *xdrs; CREATE3resfail *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_wcc_data(xdrs, &objp->dir_wcc)) return (false); return (true); } bool xdr_CREATE3res(xdrs, objp) register XDR *xdrs; CREATE3res *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfsstat3(xdrs, &objp->status)) return (false); switch (objp->status) { case NFS3_OK: if (!xdr_CREATE3resok(xdrs, &objp->CREATE3res_u.resok)) return (false); break; default: if (!xdr_CREATE3resfail(xdrs, &objp->CREATE3res_u.resfail)) return (false); break; } return (true); } bool xdr_MKDIR3args(xdrs, objp) register XDR *xdrs; MKDIR3args *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_diropargs3(xdrs, &objp->where)) return (false); if (!xdr_sattr3(xdrs, &objp->attributes)) return (false); return (true); } bool xdr_MKDIR3resok(xdrs, objp) register XDR *xdrs; MKDIR3resok *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_post_op_fh3(xdrs, &objp->obj)) return (false); if (!xdr_post_op_attr(xdrs, &objp->obj_attributes)) return (false); if (!xdr_wcc_data(xdrs, &objp->dir_wcc)) return (false); return (true); } bool xdr_MKDIR3resfail(xdrs, objp) register XDR *xdrs; MKDIR3resfail *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_wcc_data(xdrs, &objp->dir_wcc)) return (false); return (true); } bool xdr_MKDIR3res(xdrs, objp) register XDR *xdrs; MKDIR3res *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfsstat3(xdrs, &objp->status)) return (false); switch (objp->status) { case NFS3_OK: if (!xdr_MKDIR3resok(xdrs, &objp->MKDIR3res_u.resok)) return (false); break; default: if (!xdr_MKDIR3resfail(xdrs, &objp->MKDIR3res_u.resfail)) return (false); break; } return (true); } bool xdr_symlinkdata3(xdrs, objp) register XDR *xdrs; symlinkdata3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_sattr3(xdrs, &objp->symlink_attributes)) return (false); if (!xdr_nfspath3(xdrs, &objp->symlink_data)) return (false); return (true); } bool xdr_SYMLINK3args(xdrs, objp) register XDR *xdrs; SYMLINK3args *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_diropargs3(xdrs, &objp->where)) return (false); if (!xdr_symlinkdata3(xdrs, &objp->symlink)) return (false); return (true); } bool xdr_SYMLINK3resok(xdrs, objp) register XDR *xdrs; SYMLINK3resok *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_post_op_fh3(xdrs, &objp->obj)) return (false); if (!xdr_post_op_attr(xdrs, &objp->obj_attributes)) return (false); if (!xdr_wcc_data(xdrs, &objp->dir_wcc)) return (false); return (true); } bool xdr_SYMLINK3resfail(xdrs, objp) register XDR *xdrs; SYMLINK3resfail *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_wcc_data(xdrs, &objp->dir_wcc)) return (false); return (true); } bool xdr_SYMLINK3res(xdrs, objp) register XDR *xdrs; SYMLINK3res *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfsstat3(xdrs, &objp->status)) return (false); switch (objp->status) { case NFS3_OK: if (!xdr_SYMLINK3resok(xdrs, &objp->SYMLINK3res_u.resok)) return (false); break; default: if (!xdr_SYMLINK3resfail(xdrs, &objp->SYMLINK3res_u.resfail)) return (false); break; } return (true); } bool xdr_devicedata3(xdrs, objp) register XDR *xdrs; devicedata3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_sattr3(xdrs, &objp->dev_attributes)) return (false); if (!xdr_specdata3(xdrs, &objp->spec)) return (false); return (true); } bool xdr_mknoddata3(xdrs, objp) register XDR *xdrs; mknoddata3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_ftype3(xdrs, &objp->type)) return (false); switch (objp->type) { case NF3CHR: case NF3BLK: if (!xdr_devicedata3(xdrs, &objp->mknoddata3_u.device)) return (false); break; case NF3SOCK: case NF3FIFO: if (!xdr_sattr3(xdrs, &objp->mknoddata3_u.pipe_attributes)) return (false); break; default: return (true); break; } return (true); } bool xdr_MKNOD3args(xdrs, objp) register XDR *xdrs; MKNOD3args *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_diropargs3(xdrs, &objp->where)) return (false); if (!xdr_mknoddata3(xdrs, &objp->what)) return (false); return (true); } bool xdr_MKNOD3resok(xdrs, objp) register XDR *xdrs; MKNOD3resok *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_post_op_fh3(xdrs, &objp->obj)) return (false); if (!xdr_post_op_attr(xdrs, &objp->obj_attributes)) return (false); if (!xdr_wcc_data(xdrs, &objp->dir_wcc)) return (false); return (true); } bool xdr_MKNOD3resfail(xdrs, objp) register XDR *xdrs; MKNOD3resfail *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_wcc_data(xdrs, &objp->dir_wcc)) return (false); return (true); } bool xdr_MKNOD3res(xdrs, objp) register XDR *xdrs; MKNOD3res *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfsstat3(xdrs, &objp->status)) return (false); switch (objp->status) { case NFS3_OK: if (!xdr_MKNOD3resok(xdrs, &objp->MKNOD3res_u.resok)) return (false); break; default: if (!xdr_MKNOD3resfail(xdrs, &objp->MKNOD3res_u.resfail)) return (false); break; } return (true); } bool xdr_REMOVE3args(xdrs, objp) register XDR *xdrs; REMOVE3args *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif struct nfs_request_lookahead *lkhd = xdrs->x_public ? (struct nfs_request_lookahead *)xdrs-> x_public : &dummy_lookahead; if (!xdr_diropargs3(xdrs, &objp->object)) return (false); lkhd->flags |= NFS_LOOKAHEAD_REMOVE; return (true); } bool xdr_REMOVE3resok(xdrs, objp) register XDR *xdrs; REMOVE3resok *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_wcc_data(xdrs, &objp->dir_wcc)) return (false); return (true); } bool xdr_REMOVE3resfail(xdrs, objp) register XDR *xdrs; REMOVE3resfail *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_wcc_data(xdrs, &objp->dir_wcc)) return (false); return (true); } bool xdr_REMOVE3res(xdrs, objp) register XDR *xdrs; REMOVE3res *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfsstat3(xdrs, &objp->status)) return (false); switch (objp->status) { case NFS3_OK: if (!xdr_REMOVE3resok(xdrs, &objp->REMOVE3res_u.resok)) return (false); break; default: if (!xdr_REMOVE3resfail(xdrs, &objp->REMOVE3res_u.resfail)) return (false); break; } return (true); } bool xdr_RMDIR3args(xdrs, objp) register XDR *xdrs; RMDIR3args *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_diropargs3(xdrs, &objp->object)) return (false); return (true); } bool xdr_RMDIR3resok(xdrs, objp) register XDR *xdrs; RMDIR3resok *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_wcc_data(xdrs, &objp->dir_wcc)) return (false); return (true); } bool xdr_RMDIR3resfail(xdrs, objp) register XDR *xdrs; RMDIR3resfail *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_wcc_data(xdrs, &objp->dir_wcc)) return (false); return (true); } bool xdr_RMDIR3res(xdrs, objp) register XDR *xdrs; RMDIR3res *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfsstat3(xdrs, &objp->status)) return (false); switch (objp->status) { case NFS3_OK: if (!xdr_RMDIR3resok(xdrs, &objp->RMDIR3res_u.resok)) return (false); break; default: if (!xdr_RMDIR3resfail(xdrs, &objp->RMDIR3res_u.resfail)) return (false); break; } return (true); } bool xdr_RENAME3args(xdrs, objp) register XDR *xdrs; RENAME3args *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif struct nfs_request_lookahead *lkhd = xdrs->x_public ? (struct nfs_request_lookahead *)xdrs-> x_public : &dummy_lookahead; if (!xdr_diropargs3(xdrs, &objp->from)) return (false); if (!xdr_diropargs3(xdrs, &objp->to)) return (false); lkhd->flags |= NFS_LOOKAHEAD_RENAME; return (true); } bool xdr_RENAME3resok(xdrs, objp) register XDR *xdrs; RENAME3resok *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_wcc_data(xdrs, &objp->fromdir_wcc)) return (false); if (!xdr_wcc_data(xdrs, &objp->todir_wcc)) return (false); return (true); } bool xdr_RENAME3resfail(xdrs, objp) register XDR *xdrs; RENAME3resfail *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_wcc_data(xdrs, &objp->fromdir_wcc)) return (false); if (!xdr_wcc_data(xdrs, &objp->todir_wcc)) return (false); return (true); } bool xdr_RENAME3res(xdrs, objp) register XDR *xdrs; RENAME3res *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfsstat3(xdrs, &objp->status)) return (false); switch (objp->status) { case NFS3_OK: if (!xdr_RENAME3resok(xdrs, &objp->RENAME3res_u.resok)) return (false); break; default: if (!xdr_RENAME3resfail(xdrs, &objp->RENAME3res_u.resfail)) return (false); break; } return (true); } bool xdr_LINK3args(xdrs, objp) register XDR *xdrs; LINK3args *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfs_fh3(xdrs, &objp->file)) return (false); if (!xdr_diropargs3(xdrs, &objp->link)) return (false); return (true); } bool xdr_LINK3resok(xdrs, objp) register XDR *xdrs; LINK3resok *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_post_op_attr(xdrs, &objp->file_attributes)) return (false); if (!xdr_wcc_data(xdrs, &objp->linkdir_wcc)) return (false); return (true); } bool xdr_LINK3resfail(xdrs, objp) register XDR *xdrs; LINK3resfail *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_post_op_attr(xdrs, &objp->file_attributes)) return (false); if (!xdr_wcc_data(xdrs, &objp->linkdir_wcc)) return (false); return (true); } bool xdr_LINK3res(xdrs, objp) register XDR *xdrs; LINK3res *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfsstat3(xdrs, &objp->status)) return (false); switch (objp->status) { case NFS3_OK: if (!xdr_LINK3resok(xdrs, &objp->LINK3res_u.resok)) return (false); break; default: if (!xdr_LINK3resfail(xdrs, &objp->LINK3res_u.resfail)) return (false); break; } return (true); } bool xdr_READDIR3args(xdrs, objp) register XDR *xdrs; READDIR3args *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif struct nfs_request_lookahead *lkhd = xdrs->x_public ? (struct nfs_request_lookahead *)xdrs-> x_public : &dummy_lookahead; if (!xdr_nfs_fh3(xdrs, &objp->dir)) return (false); if (!xdr_cookie3(xdrs, &objp->cookie)) return (false); if (!xdr_cookieverf3(xdrs, objp->cookieverf)) return (false); if (!xdr_count3(xdrs, &objp->count)) return (false); lkhd->flags |= NFS_LOOKAHEAD_READDIR; return (true); } bool xdr_entry3(xdrs, objp) register XDR *xdrs; entry3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_fileid3(xdrs, &objp->fileid)) return (false); if (!xdr_filename3(xdrs, &objp->name)) return (false); if (!xdr_cookie3(xdrs, &objp->cookie)) return (false); if (!xdr_pointer (xdrs, (char **)&objp->nextentry, sizeof(entry3), (xdrproc_t) xdr_entry3)) return (false); return (true); } bool xdr_dirlist3(xdrs, objp) register XDR *xdrs; dirlist3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_pointer (xdrs, (char **)&objp->entries, sizeof(entry3), (xdrproc_t) xdr_entry3)) return (false); if (!xdr_bool(xdrs, &objp->eof)) return (false); return (true); } bool xdr_READDIR3resok(xdrs, objp) register XDR *xdrs; READDIR3resok *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_post_op_attr(xdrs, &objp->dir_attributes)) return (false); if (!xdr_cookieverf3(xdrs, objp->cookieverf)) return (false); if (!xdr_dirlist3(xdrs, &objp->reply)) return (false); return (true); } bool xdr_READDIR3resfail(xdrs, objp) register XDR *xdrs; READDIR3resfail *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_post_op_attr(xdrs, &objp->dir_attributes)) return (false); return (true); } bool xdr_READDIR3res(xdrs, objp) register XDR *xdrs; READDIR3res *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfsstat3(xdrs, &objp->status)) return (false); switch (objp->status) { case NFS3_OK: if (!xdr_READDIR3resok(xdrs, &objp->READDIR3res_u.resok)) return (false); break; default: if (!xdr_READDIR3resfail(xdrs, &objp->READDIR3res_u.resfail)) return (false); break; } return (true); } bool xdr_READDIRPLUS3args(xdrs, objp) register XDR *xdrs; READDIRPLUS3args *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif struct nfs_request_lookahead *lkhd = xdrs->x_public ? (struct nfs_request_lookahead *)xdrs-> x_public : &dummy_lookahead; if (!xdr_nfs_fh3(xdrs, &objp->dir)) return (false); if (!xdr_cookie3(xdrs, &objp->cookie)) return (false); if (!xdr_cookieverf3(xdrs, objp->cookieverf)) return (false); if (!xdr_count3(xdrs, &objp->dircount)) return (false); if (!xdr_count3(xdrs, &objp->maxcount)) return (false); lkhd->flags |= NFS_LOOKAHEAD_READDIR; return (true); } bool xdr_entryplus3(xdrs, objp) register XDR *xdrs; entryplus3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_fileid3(xdrs, &objp->fileid)) return (false); if (!xdr_filename3(xdrs, &objp->name)) return (false); if (!xdr_cookie3(xdrs, &objp->cookie)) return (false); if (!xdr_post_op_attr(xdrs, &objp->name_attributes)) return (false); if (!xdr_post_op_fh3(xdrs, &objp->name_handle)) return (false); if (!xdr_pointer (xdrs, (char **)&objp->nextentry, sizeof(entryplus3), (xdrproc_t) xdr_entryplus3)) return (false); return (true); } bool xdr_dirlistplus3(xdrs, objp) register XDR *xdrs; dirlistplus3 *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_pointer (xdrs, (char **)&objp->entries, sizeof(entryplus3), (xdrproc_t) xdr_entryplus3)) return (false); if (!xdr_bool(xdrs, &objp->eof)) return (false); return (true); } bool xdr_READDIRPLUS3resok(xdrs, objp) register XDR *xdrs; READDIRPLUS3resok *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_post_op_attr(xdrs, &objp->dir_attributes)) return (false); if (!xdr_cookieverf3(xdrs, objp->cookieverf)) return (false); if (!xdr_dirlistplus3(xdrs, &objp->reply)) return (false); return (true); } bool xdr_READDIRPLUS3resfail(xdrs, objp) register XDR *xdrs; READDIRPLUS3resfail *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_post_op_attr(xdrs, &objp->dir_attributes)) return (false); return (true); } bool xdr_READDIRPLUS3res(xdrs, objp) register XDR *xdrs; READDIRPLUS3res *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfsstat3(xdrs, &objp->status)) return (false); switch (objp->status) { case NFS3_OK: if (!xdr_READDIRPLUS3resok (xdrs, &objp->READDIRPLUS3res_u.resok)) return (false); break; default: if (!xdr_READDIRPLUS3resfail (xdrs, &objp->READDIRPLUS3res_u.resfail)) return (false); break; } return (true); } bool xdr_FSSTAT3args(xdrs, objp) register XDR *xdrs; FSSTAT3args *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfs_fh3(xdrs, &objp->fsroot)) return (false); return (true); } bool xdr_FSSTAT3resok(xdrs, objp) register XDR *xdrs; FSSTAT3resok *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_post_op_attr(xdrs, &objp->obj_attributes)) return (false); if (!xdr_size3(xdrs, &objp->tbytes)) return (false); if (!xdr_size3(xdrs, &objp->fbytes)) return (false); if (!xdr_size3(xdrs, &objp->abytes)) return (false); if (!xdr_size3(xdrs, &objp->tfiles)) return (false); if (!xdr_size3(xdrs, &objp->ffiles)) return (false); if (!xdr_size3(xdrs, &objp->afiles)) return (false); if (!xdr_nfs3_uint32(xdrs, &objp->invarsec)) return (false); return (true); } bool xdr_FSSTAT3resfail(xdrs, objp) register XDR *xdrs; FSSTAT3resfail *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_post_op_attr(xdrs, &objp->obj_attributes)) return (false); return (true); } bool xdr_FSSTAT3res(xdrs, objp) register XDR *xdrs; FSSTAT3res *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfsstat3(xdrs, &objp->status)) return (false); switch (objp->status) { case NFS3_OK: if (!xdr_FSSTAT3resok(xdrs, &objp->FSSTAT3res_u.resok)) return (false); break; default: if (!xdr_FSSTAT3resfail(xdrs, &objp->FSSTAT3res_u.resfail)) return (false); break; } return (true); } bool xdr_FSINFO3args(xdrs, objp) register XDR *xdrs; FSINFO3args *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfs_fh3(xdrs, &objp->fsroot)) return (false); return (true); } bool xdr_FSINFO3resok(xdrs, objp) register XDR *xdrs; FSINFO3resok *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_post_op_attr(xdrs, &objp->obj_attributes)) return (false); if (!xdr_nfs3_uint32(xdrs, &objp->rtmax)) return (false); if (!xdr_nfs3_uint32(xdrs, &objp->rtpref)) return (false); if (!xdr_nfs3_uint32(xdrs, &objp->rtmult)) return (false); if (!xdr_nfs3_uint32(xdrs, &objp->wtmax)) return (false); if (!xdr_nfs3_uint32(xdrs, &objp->wtpref)) return (false); if (!xdr_nfs3_uint32(xdrs, &objp->wtmult)) return (false); if (!xdr_nfs3_uint32(xdrs, &objp->dtpref)) return (false); if (!xdr_size3(xdrs, &objp->maxfilesize)) return (false); if (!xdr_nfstime3(xdrs, &objp->time_delta)) return (false); if (!xdr_nfs3_uint32(xdrs, &objp->properties)) return (false); return (true); } bool xdr_FSINFO3resfail(xdrs, objp) register XDR *xdrs; FSINFO3resfail *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_post_op_attr(xdrs, &objp->obj_attributes)) return (false); return (true); } bool xdr_FSINFO3res(xdrs, objp) register XDR *xdrs; FSINFO3res *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfsstat3(xdrs, &objp->status)) return (false); switch (objp->status) { case NFS3_OK: if (!xdr_FSINFO3resok(xdrs, &objp->FSINFO3res_u.resok)) return (false); break; default: if (!xdr_FSINFO3resfail(xdrs, &objp->FSINFO3res_u.resfail)) return (false); break; } return (true); } bool xdr_PATHCONF3args(xdrs, objp) register XDR *xdrs; PATHCONF3args *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfs_fh3(xdrs, &objp->object)) return (false); return (true); } bool xdr_PATHCONF3resok(xdrs, objp) register XDR *xdrs; PATHCONF3resok *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_post_op_attr(xdrs, &objp->obj_attributes)) return (false); if (!xdr_nfs3_uint32(xdrs, &objp->linkmax)) return (false); if (!xdr_nfs3_uint32(xdrs, &objp->name_max)) return (false); if (!xdr_bool(xdrs, &objp->no_trunc)) return (false); if (!xdr_bool(xdrs, &objp->chown_restricted)) return (false); if (!xdr_bool(xdrs, &objp->case_insensitive)) return (false); if (!xdr_bool(xdrs, &objp->case_preserving)) return (false); return (true); } bool xdr_PATHCONF3resfail(xdrs, objp) register XDR *xdrs; PATHCONF3resfail *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_post_op_attr(xdrs, &objp->obj_attributes)) return (false); return (true); } bool xdr_PATHCONF3res(xdrs, objp) register XDR *xdrs; PATHCONF3res *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfsstat3(xdrs, &objp->status)) return (false); switch (objp->status) { case NFS3_OK: if (!xdr_PATHCONF3resok(xdrs, &objp->PATHCONF3res_u.resok)) return (false); break; default: if (!xdr_PATHCONF3resfail(xdrs, &objp->PATHCONF3res_u.resfail)) return (false); break; } return (true); } bool xdr_COMMIT3args(xdrs, objp) register XDR *xdrs; COMMIT3args *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif struct nfs_request_lookahead *lkhd = xdrs->x_public ? (struct nfs_request_lookahead *)xdrs-> x_public : &dummy_lookahead; if (!xdr_nfs_fh3(xdrs, &objp->file)) return (false); if (!xdr_offset3(xdrs, &objp->offset)) return (false); if (!xdr_count3(xdrs, &objp->count)) return (false); lkhd->flags |= NFS_LOOKAHEAD_COMMIT; return (true); } bool xdr_COMMIT3resok(xdrs, objp) register XDR *xdrs; COMMIT3resok *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_wcc_data(xdrs, &objp->file_wcc)) return (false); if (!xdr_writeverf3(xdrs, objp->verf)) return (false); return (true); } bool xdr_COMMIT3resfail(xdrs, objp) register XDR *xdrs; COMMIT3resfail *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_wcc_data(xdrs, &objp->file_wcc)) return (false); return (true); } bool xdr_COMMIT3res(xdrs, objp) register XDR *xdrs; COMMIT3res *objp; { #if defined(_LP64) || defined(_KERNEL) register int __attribute__ ((__unused__)) * buf; #else register long __attribute__ ((__unused__)) * buf; #endif if (!xdr_nfsstat3(xdrs, &objp->status)) return (false); switch (objp->status) { case NFS3_OK: if (!xdr_COMMIT3resok(xdrs, &objp->COMMIT3res_u.resok)) return (false); break; default: if (!xdr_COMMIT3resfail(xdrs, &objp->COMMIT3res_u.resfail)) return (false); break; } return (true); } nfs-ganesha-2.6.0/src/Protocols/XDR/xdr_nfsv41.c000066400000000000000000000006441324272410200212640ustar00rootroot00000000000000/* * The content of this file is a mix of rpcgen-generated * and hand-edited program text. It is not automatically * generated by, e.g., build processes. * * This file is under version control. */ #include "config.h" #include "nfsv41.h" /* Most Ganesha XDR decoder routines are inlines, defined in * nfsv41.h. The present file is retained as a location for * XDR decoder routines with external linkage. */ nfs-ganesha-2.6.0/src/Protocols/XDR/xdr_nlm4.c000066400000000000000000000106741324272410200210210ustar00rootroot00000000000000/* * Please do not edit this file. * It was generated using rpcgen. */ #include "config.h" #include "gsh_rpc.h" #include "nlm4.h" #include "nfs_fh.h" bool xdr_nlm4_stats(XDR * xdrs, nlm4_stats * objp) { if (!xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } bool xdr_nlm4_stat(XDR * xdrs, nlm4_stat * objp) { if (!xdr_nlm4_stats(xdrs, &objp->stat)) return false; return true; } bool xdr_nlm4_res(XDR * xdrs, nlm4_res * objp) { if (!xdr_netobj(xdrs, &objp->cookie)) return false; if (!xdr_nlm4_stat(xdrs, &objp->stat)) return false; return true; } bool xdr_nlm4_holder(XDR * xdrs, nlm4_holder * objp) { if (!xdr_bool(xdrs, &objp->exclusive)) return false; if (!xdr_int32_t(xdrs, &objp->svid)) return false; if (!xdr_netobj(xdrs, &objp->oh)) return false; if (!xdr_uint64_t(xdrs, &objp->l_offset)) return false; if (!xdr_uint64_t(xdrs, &objp->l_len)) return false; return true; } bool xdr_nlm4_testrply(XDR * xdrs, nlm4_testrply * objp) { if (!xdr_nlm4_stats(xdrs, &objp->stat)) return false; switch (objp->stat) { case NLM4_DENIED: if (!xdr_nlm4_holder(xdrs, &objp->nlm4_testrply_u.holder)) return false; break; default: break; } return true; } bool xdr_nlm4_testres(XDR * xdrs, nlm4_testres * objp) { if (!xdr_netobj(xdrs, &objp->cookie)) return false; if (!xdr_nlm4_testrply(xdrs, &objp->test_stat)) return false; return true; } bool xdr_nlm4_lock(XDR * xdrs, nlm4_lock * objp) { if (!xdr_string(xdrs, &objp->caller_name, LM_MAXSTRLEN)) return false; if (!xdr_netobj(xdrs, &objp->fh)) return false; if (!xdr_netobj(xdrs, &objp->oh)) return false; if (!xdr_int32_t(xdrs, &objp->svid)) return false; if (!xdr_uint64_t(xdrs, &objp->l_offset)) return false; if (!xdr_uint64_t(xdrs, &objp->l_len)) return false; return true; } bool xdr_nlm4_lockargs(XDR * xdrs, nlm4_lockargs * objp) { if (!xdr_netobj(xdrs, &objp->cookie)) return false; if (!xdr_bool(xdrs, &objp->block)) return false; if (!xdr_bool(xdrs, &objp->exclusive)) return false; if (!xdr_nlm4_lock(xdrs, &objp->alock)) return false; if (!xdr_bool(xdrs, &objp->reclaim)) return false; if (!xdr_int32_t(xdrs, &objp->state)) return false; return true; } bool xdr_nlm4_cancargs(XDR * xdrs, nlm4_cancargs * objp) { if (!xdr_netobj(xdrs, &objp->cookie)) return false; if (!xdr_bool(xdrs, &objp->block)) return false; if (!xdr_bool(xdrs, &objp->exclusive)) return false; if (!xdr_nlm4_lock(xdrs, &objp->alock)) return false; return true; } bool xdr_nlm4_testargs(XDR * xdrs, nlm4_testargs * objp) { if (!xdr_netobj(xdrs, &objp->cookie)) return false; if (!xdr_bool(xdrs, &objp->exclusive)) return false; if (!xdr_nlm4_lock(xdrs, &objp->alock)) return false; return true; } bool xdr_nlm4_unlockargs(XDR * xdrs, nlm4_unlockargs * objp) { if (!xdr_netobj(xdrs, &objp->cookie)) return false; if (!xdr_nlm4_lock(xdrs, &objp->alock)) return false; return true; } bool xdr_fsh4_mode(XDR * xdrs, fsh4_mode * objp) { if (!xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } bool xdr_fsh4_access(XDR * xdrs, fsh4_access * objp) { if (!xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } bool xdr_nlm4_share(XDR * xdrs, nlm4_share * objp) { if (!xdr_string(xdrs, &objp->caller_name, LM_MAXSTRLEN)) return false; if (!xdr_netobj(xdrs, &objp->fh)) return false; if (!xdr_netobj(xdrs, &objp->oh)) return false; if (!xdr_fsh4_mode(xdrs, &objp->mode)) return false; if (!xdr_fsh4_access(xdrs, &objp->access)) return false; return true; } bool xdr_nlm4_shareargs(XDR * xdrs, nlm4_shareargs * objp) { if (!xdr_netobj(xdrs, &objp->cookie)) return false; if (!xdr_nlm4_share(xdrs, &objp->share)) return false; if (!xdr_bool(xdrs, &objp->reclaim)) return false; return true; } bool xdr_nlm4_shareres(XDR * xdrs, nlm4_shareres * objp) { if (!xdr_netobj(xdrs, &objp->cookie)) return false; if (!xdr_nlm4_stats(xdrs, &objp->stat)) return false; if (!xdr_int32_t(xdrs, &objp->sequence)) return false; return true; } bool xdr_nlm4_free_allargs(XDR * xdrs, nlm4_free_allargs * objp) { if (!xdr_string(xdrs, &objp->name, LM_MAXNAMELEN)) return false; if (!xdr_uint32_t(xdrs, &objp->state)) return false; return true; } bool xdr_nlm4_sm_notifyargs(XDR * xdrs, nlm4_sm_notifyargs * objp) { if (!xdr_string(xdrs, &objp->name, SM_MAXSTRLEN)) return false; if (!xdr_int32_t(xdrs, &objp->state)) return false; if (!xdr_opaque(xdrs, objp->priv, SM_PRIV_SZ)) return false; return true; } nfs-ganesha-2.6.0/src/Protocols/XDR/xdr_nsm.c000066400000000000000000000024751324272410200207440ustar00rootroot00000000000000/* * Please do not edit this file. * It was generated using rpcgen. */ #include "config.h" #include "nsm.h" bool xdr_res(XDR * xdrs, res * objp) { if (!xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } bool xdr_sm_stat_res(XDR * xdrs, sm_stat_res * objp) { if (!xdr_res(xdrs, &objp->res_stat)) return false; if (!xdr_int(xdrs, &objp->state)) return false; return true; } bool xdr_sm_stat(XDR * xdrs, sm_stat * objp) { if (!xdr_int(xdrs, &objp->state)) return false; return true; } bool xdr_my_id(XDR * xdrs, my_id * objp) { if (!xdr_string(xdrs, &objp->my_name, SM_MAXSTRLEN)) return false; if (!xdr_int(xdrs, &objp->my_prog)) return false; if (!xdr_int(xdrs, &objp->my_vers)) return false; if (!xdr_int(xdrs, &objp->my_proc)) return false; return true; } bool xdr_mon_id(XDR * xdrs, mon_id * objp) { if (!xdr_string(xdrs, &objp->mon_name, SM_MAXSTRLEN)) return false; if (!xdr_my_id(xdrs, &objp->my_id)) return false; return true; } bool xdr_mon(XDR * xdrs, mon * objp) { if (!xdr_mon_id(xdrs, &objp->mon_id)) return false; if (!xdr_opaque(xdrs, objp->priv, 16)) return false; return true; } bool xdr_notify (XDR *xdrs, notify *objp) { if (!xdr_string(xdrs, &objp->my_name, SM_MAXSTRLEN)) return false; if (!xdr_int(xdrs, &objp->state)) return false; return true; } nfs-ganesha-2.6.0/src/Protocols/XDR/xdr_rquota.c000066400000000000000000000203461324272410200214570ustar00rootroot00000000000000/* * Hand updated. * It was generated using rpcgen. */ #include "config.h" #include "rquota.h" bool xdr_sq_dqblk(XDR * xdrs, sq_dqblk * objp) { register int32_t *buf; if (xdrs->x_op == XDR_ENCODE) { buf = xdr_inline_encode(xdrs, 8 * BYTES_PER_XDR_UNIT); if (buf != NULL) { /* most likely */ IXDR_PUT_U_INT32(buf, objp->rq_bhardlimit); IXDR_PUT_U_INT32(buf, objp->rq_bsoftlimit); IXDR_PUT_U_INT32(buf, objp->rq_curblocks); IXDR_PUT_U_INT32(buf, objp->rq_fhardlimit); IXDR_PUT_U_INT32(buf, objp->rq_fsoftlimit); IXDR_PUT_U_INT32(buf, objp->rq_curfiles); IXDR_PUT_U_INT32(buf, objp->rq_btimeleft); IXDR_PUT_U_INT32(buf, objp->rq_ftimeleft); } else { if (!XDR_PUTUINT32(xdrs, objp->rq_bhardlimit)) return false; if (!XDR_PUTUINT32(xdrs, objp->rq_bsoftlimit)) return false; if (!XDR_PUTUINT32(xdrs, objp->rq_curblocks)) return false; if (!XDR_PUTUINT32(xdrs, objp->rq_fhardlimit)) return false; if (!XDR_PUTUINT32(xdrs, objp->rq_fsoftlimit)) return false; if (!XDR_PUTUINT32(xdrs, objp->rq_curfiles)) return false; if (!XDR_PUTUINT32(xdrs, objp->rq_btimeleft)) return false; if (!XDR_PUTUINT32(xdrs, objp->rq_ftimeleft)) return false; } return true; } if (xdrs->x_op == XDR_DECODE) { buf = xdr_inline_decode(xdrs, 8 * BYTES_PER_XDR_UNIT); if (buf != NULL) { /* most likely */ objp->rq_bhardlimit = IXDR_GET_U_INT32(buf); objp->rq_bsoftlimit = IXDR_GET_U_INT32(buf); objp->rq_curblocks = IXDR_GET_U_INT32(buf); objp->rq_fhardlimit = IXDR_GET_U_INT32(buf); objp->rq_fsoftlimit = IXDR_GET_U_INT32(buf); objp->rq_curfiles = IXDR_GET_U_INT32(buf); objp->rq_btimeleft = IXDR_GET_U_INT32(buf); objp->rq_ftimeleft = IXDR_GET_U_INT32(buf); } else { if (!XDR_GETUINT32(xdrs, &objp->rq_bhardlimit)) return false; if (!XDR_GETUINT32(xdrs, &objp->rq_bsoftlimit)) return false; if (!XDR_GETUINT32(xdrs, &objp->rq_curblocks)) return false; if (!XDR_GETUINT32(xdrs, &objp->rq_fhardlimit)) return false; if (!XDR_GETUINT32(xdrs, &objp->rq_fsoftlimit)) return false; if (!XDR_GETUINT32(xdrs, &objp->rq_curfiles)) return false; if (!XDR_GETUINT32(xdrs, &objp->rq_btimeleft)) return false; if (!XDR_GETUINT32(xdrs, &objp->rq_ftimeleft)) return false; } return true; } if (!xdr_u_int(xdrs, &objp->rq_bhardlimit)) return false; if (!xdr_u_int(xdrs, &objp->rq_bsoftlimit)) return false; if (!xdr_u_int(xdrs, &objp->rq_curblocks)) return false; if (!xdr_u_int(xdrs, &objp->rq_fhardlimit)) return false; if (!xdr_u_int(xdrs, &objp->rq_fsoftlimit)) return false; if (!xdr_u_int(xdrs, &objp->rq_curfiles)) return false; if (!xdr_u_int(xdrs, &objp->rq_btimeleft)) return false; if (!xdr_u_int(xdrs, &objp->rq_ftimeleft)) return false; return true; } bool xdr_getquota_args(XDR * xdrs, getquota_args * objp) { register __attribute__ ((__unused__)) int32_t *buf; if (!xdr_string(xdrs, &objp->gqa_pathp, RQ_PATHLEN)) return false; if (!xdr_int(xdrs, &objp->gqa_uid)) return false; return true; } bool xdr_setquota_args(XDR * xdrs, setquota_args * objp) { register __attribute__ ((__unused__)) int32_t *buf; if (!xdr_int(xdrs, &objp->sqa_qcmd)) return false; if (!xdr_string(xdrs, &objp->sqa_pathp, RQ_PATHLEN)) return false; if (!xdr_int(xdrs, &objp->sqa_id)) return false; if (!xdr_sq_dqblk(xdrs, &objp->sqa_dqblk)) return false; return true; } bool xdr_ext_getquota_args(XDR * xdrs, ext_getquota_args * objp) { register __attribute__ ((__unused__)) int32_t *buf; if (!xdr_string(xdrs, &objp->gqa_pathp, RQ_PATHLEN)) return false; if (!xdr_int(xdrs, &objp->gqa_type)) return false; if (!xdr_int(xdrs, &objp->gqa_id)) return false; return true; } bool xdr_ext_setquota_args(XDR * xdrs, ext_setquota_args * objp) { register __attribute__ ((__unused__)) int32_t *buf; if (!xdr_int(xdrs, &objp->sqa_qcmd)) return false; if (!xdr_string(xdrs, &objp->sqa_pathp, RQ_PATHLEN)) return false; if (!xdr_int(xdrs, &objp->sqa_id)) return false; if (!xdr_int(xdrs, &objp->sqa_type)) return false; if (!xdr_sq_dqblk(xdrs, &objp->sqa_dqblk)) return false; return true; } bool xdr_rquota(XDR * xdrs, rquota * objp) { register int32_t *buf; if (xdrs->x_op == XDR_ENCODE) { buf = xdr_inline_encode(xdrs, 10 * BYTES_PER_XDR_UNIT); if (buf != NULL) { /* most likely */ IXDR_PUT_INT32(buf, objp->rq_bsize); IXDR_PUT_BOOL(buf, objp->rq_active); IXDR_PUT_U_INT32(buf, objp->rq_bhardlimit); IXDR_PUT_U_INT32(buf, objp->rq_bsoftlimit); IXDR_PUT_U_INT32(buf, objp->rq_curblocks); IXDR_PUT_U_INT32(buf, objp->rq_fhardlimit); IXDR_PUT_U_INT32(buf, objp->rq_fsoftlimit); IXDR_PUT_U_INT32(buf, objp->rq_curfiles); IXDR_PUT_U_INT32(buf, objp->rq_btimeleft); IXDR_PUT_U_INT32(buf, objp->rq_ftimeleft); } else { if (!XDR_PUTINT32(xdrs, objp->rq_bsize)) return false; if (!XDR_PUTBOOL(xdrs, objp->rq_active)) return false; if (!XDR_PUTUINT32(xdrs, objp->rq_bhardlimit)) return false; if (!XDR_PUTUINT32(xdrs, objp->rq_bsoftlimit)) return false; if (!XDR_PUTUINT32(xdrs, objp->rq_curblocks)) return false; if (!XDR_PUTUINT32(xdrs, objp->rq_fhardlimit)) return false; if (!XDR_PUTUINT32(xdrs, objp->rq_fsoftlimit)) return false; if (!XDR_PUTUINT32(xdrs, objp->rq_curfiles)) return false; if (!XDR_PUTUINT32(xdrs, objp->rq_btimeleft)) return false; if (!XDR_PUTUINT32(xdrs, objp->rq_ftimeleft)) return false; } return true; } if (xdrs->x_op == XDR_DECODE) { buf = xdr_inline_decode(xdrs, 10 * BYTES_PER_XDR_UNIT); if (buf != NULL) { /* most likely */ objp->rq_bsize = IXDR_GET_INT32(buf); objp->rq_active = IXDR_GET_BOOL(buf); objp->rq_bhardlimit = IXDR_GET_U_INT32(buf); objp->rq_bsoftlimit = IXDR_GET_U_INT32(buf); objp->rq_curblocks = IXDR_GET_U_INT32(buf); objp->rq_fhardlimit = IXDR_GET_U_INT32(buf); objp->rq_fsoftlimit = IXDR_GET_U_INT32(buf); objp->rq_curfiles = IXDR_GET_U_INT32(buf); objp->rq_btimeleft = IXDR_GET_U_INT32(buf); objp->rq_ftimeleft = IXDR_GET_U_INT32(buf); } else { if (!XDR_GETINT32(xdrs, &objp->rq_bsize)) return false; if (!XDR_GETBOOL(xdrs, &objp->rq_active)) return false; if (!XDR_GETUINT32(xdrs, &objp->rq_bhardlimit)) return false; if (!XDR_GETUINT32(xdrs, &objp->rq_bsoftlimit)) return false; if (!XDR_GETUINT32(xdrs, &objp->rq_curblocks)) return false; if (!XDR_GETUINT32(xdrs, &objp->rq_fhardlimit)) return false; if (!XDR_GETUINT32(xdrs, &objp->rq_fsoftlimit)) return false; if (!XDR_GETUINT32(xdrs, &objp->rq_curfiles)) return false; if (!XDR_GETUINT32(xdrs, &objp->rq_btimeleft)) return false; if (!XDR_GETUINT32(xdrs, &objp->rq_ftimeleft)) return false; } return true; } if (!xdr_int(xdrs, &objp->rq_bsize)) return false; if (!xdr_bool(xdrs, &objp->rq_active)) return false; if (!xdr_u_int(xdrs, &objp->rq_bhardlimit)) return false; if (!xdr_u_int(xdrs, &objp->rq_bsoftlimit)) return false; if (!xdr_u_int(xdrs, &objp->rq_curblocks)) return false; if (!xdr_u_int(xdrs, &objp->rq_fhardlimit)) return false; if (!xdr_u_int(xdrs, &objp->rq_fsoftlimit)) return false; if (!xdr_u_int(xdrs, &objp->rq_curfiles)) return false; if (!xdr_u_int(xdrs, &objp->rq_btimeleft)) return false; if (!xdr_u_int(xdrs, &objp->rq_ftimeleft)) return false; return true; } bool xdr_qr_status(XDR * xdrs, qr_status * objp) { register __attribute__ ((__unused__)) int32_t *buf; if (!xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } bool xdr_getquota_rslt(XDR * xdrs, getquota_rslt * objp) { register __attribute__ ((__unused__)) int32_t *buf; if (!xdr_qr_status(xdrs, &objp->status)) return false; switch (objp->status) { case Q_OK: if (!xdr_rquota(xdrs, &objp->getquota_rslt_u.gqr_rquota)) return false; break; case Q_NOQUOTA: break; case Q_EPERM: break; default: return false; } return true; } bool xdr_setquota_rslt(XDR * xdrs, setquota_rslt * objp) { register __attribute__ ((__unused__)) int32_t *buf; if (!xdr_qr_status(xdrs, &objp->status)) return false; switch (objp->status) { case Q_OK: if (!xdr_rquota(xdrs, &objp->setquota_rslt_u.sqr_rquota)) return false; break; case Q_NOQUOTA: break; case Q_EPERM: break; default: return false; } return true; } nfs-ganesha-2.6.0/src/README.new_api000066400000000000000000000354121324272410200170030ustar00rootroot00000000000000Request for Comments: New fsal api This README will be expanded as the work progresses. It will/should be turned into a fsal writer's HOWTO at some point. This is a work in progress and therefore still in flux at this point. State ----- The server builds and runs to the point of processing exports. Only the VFS fsal is functional at this point. Debugging continues. This branch is based on the latest stable tag of the mainline 'next' branch. The first patch of the new api work is this file, titled: "Add README.new_api" Contents -------- The patch series comes in four parts. The "Revert USE_SHARED configuration option from code" is the second commit which prepares the code base for the new api. New infrastructure These are new files in src/FSAL which implement common fsal functions. I have squashed all my incremental changes so that you have a single file to look at for current state. These are subject to further change and squashing. VFS fsal These are new files that implement the VFS fsal over the new infrastructure. The main.c file is the fsal object itself followed by export.c which implements fsal_export for VFS. handle.c implements fsal_obj_object. File management for handles is in file.c and extended attributes, not implemented yet for VFS are in xattr.c. FSAL initialization This set of changes to the main server load and initialize the fsals. Once they are all loaded, fsal_export objects are created as part of the export list initialization. Cache Inode changes Starting with the commit titled "Change cache_inode.h to use new api" we invade the cache_inode_* code. This series of patches replaces fsal_handle_t with a pointer to an allocated fsal_obj_handle and the various FSAL_* calls relevant to it. The readdir operation has been changed to use a callback. This greatly simplifies the FSAL method and eliminates multiple loops, fixed array storage, and a lot of extra work. File descriptors are removed from the API and will will be deprecated entirely from cache entries. An fd is a property of the FSAL and will be managed (cached) there. The VFS fsal and other POSIX based interfaces have an fd but library based FSALs have other structures and apis so fileno and friends are deprecated in the core. Protocol These files manage the protocol on one side and access to the cache entries in CacheInode on the other. The biggest change is to replace fsal_op_context in the compound structure with struct user_cred user_credentials. A large number of places where pcontext was passed around and were (or are no longer) used. These have simply been removed from the parameter lists. State and Locking fsal_op_context pcontext has been for the most part removed from these files. A few places require user credentials for looking up handles etc. As with protocol, user_credentials takes its place. Configuration File Changes -------------------------- In order to support multiple FSAL definitions in the configuration, the configuration file now accepts a new syntax for FSAL initialization. The following fragment defines the loading of the VFS fsal. ################################################### # # FSAL parameters. # # To use the default value for a parameter, # just comment the associated line. # ################################################### FSAL { LogLevel = "Red Alert"; Foo = "baz"; VFS { FSAL_Shared_Library = "/usr/local/lib/libfsalvfs.so"; # logging level (NIV_FULL_DEBUG, NIV_DEBUG, # NIV_EVNMT, NIV_CRIT, NIV_MAJ, NIV_NULL) DebugLevel = "NIV_FULL_DEBUG" ; # Logging file #Options: "/var/log/nfs-ganesha.log" or some other file path # "SYSLOG" prints to syslog # "STDERR" prints stderr messages to the console that # started the ganesha process # "STDOUT" prints stdout messages to the console that # started the ganesha process #LogFile = "SYSLOG"; LogFile = "/var/log/nfs-ganesha.log"; # maximum number of simultaneous calls # to the filesystem. # ( 0 = no limit ). max_FS_calls = 0; } } The "FSAL" block has one sub-block, in this example "VFS", for each fsal it is to load. There can be as many sub-blocks as make sense. This is the power of trhe new api, to support multiple fsals simultaneously. There is also provision for FSAL global parameters although none are actually defined at this point. The "name" of the FSAL is the tag at the beginning of the sub-block, in this case "VFS". This name is used for fsal module lookups and diagnostics. The "FSAL_Shared_Library" value is the absolute path to the module itself. The path can be anywhere in the local filesystem. The rest of the parameters are the usual FSAL configuration parameters. Exports are connected to a specific FSAL in the following fragment: EXPORT { # Export Id (mandatory) Export_Id = 77 ; # Exported path (mandatory) Path = "/home/tmp"; # Exporting FSAL FSAL = "VFS"; # more export parameters... } The "FSAL" keyword defines which FSAL module to use for this export. If This keyword is missing in an export, the VFS fsal is used. This logic may change in future to have a server global definition within the configuration file. Things to look for ------------------ The new api uses an object oriented design pattern to implement a fsal. Structure definitions The fsal_ops_context_t structure is deprecated and all of the 'pcontext' usage has either been replaced or removed. It was refactored some time back to be common across all FSALs and all it contained was a pointer to the "export" and user credentials. * struct compound_data now has 'user_credentials' in place of 'pcontext'. A pointer to these user credentials are passed through the call stack where access checking applies. * The use of 'pcontext' in all other cases has been removed. Nearly all affected function prototypes also had a pointer to the applicable cache entry 'pentry'. This can be dereferenced anywhere to get the export, i.e. pentry->handle->export. The fsal_path_t and fsal_name_t typedefs are not used in the API. In most instances, they are replaced by a 'const char *'. These arrays attempt to encapulate string management but they are of fixed size (mostly too big but also a potential for truncation or buffer overflows) and both waste space and cause extra structure copying. Existing core code dereferences the buffer and a NULL terminated string. They will eventually be deprecated and removed. The file include/fsal_api.h contains the full fsal api. The only part visible to the server core is defined here. Each file that implements an object has its version of the object definition. One element of this private definition is the public structure defined in fsal_api.h. The rest of the elements are private to the fsal itself. These two parts, the public and private are managed as follows: * A pointer to the public structure is returned when the object is created. * This pointer is used to access methods from it as in: exp_hdl->ops->lookup(exp_hdl, name, ...); Note that exp_hdl is used to dereference the method and it is also *always* the first argument to the method/function. Think of it as the 'this' argument. * Inside the method function, the public pointer gets dereferenced with the following sequence: struct vfs_fsal_export *myself; myself = container_of(exp_hdl, struct vfs_fsal_export, export); The 'container_of' is a macro that takes the public pointer/ handle 'exp_hdl' which is indicated as the element 'export' of structure type 'vfs_fsal_export'. Throughout the function where private elements are dereferenced, the 'myself' pointer is used. 'exp_hdl' is used in the public case. Object usage Mutex locks and reference counts are used to manage both concurrent usage and state. The reference counts are use to determine when the object is "free". Current use is for managing ref counts and lists. This will be expanded as more resources such as attributes and open fds are added. Since we cannot create objects out of thin air, there is an order based on one object being the "context" in which the other is created. In other words, a 'fsal_export' is created from the 'fsal_module' that connects it to the backing store (filesystem). The same applies to a 'fsal_obj_handle' that only makes sense for a specific 'fsal_export'. When an object is created, it is returned with a reference already taken. The callee of the creating method must then either keep a persistent reference to it or 'put' it back. For example, a 'fsal_export' gets created for each export in the configuration. a pointer to it gets saved in exportlist__ and it has a reference to reflect this. It is now safe to use it to do a 'lookup' which will return a 'fsal_obj_handle' which can then be kept in a cache inode entry. If we had done a 'put' on the export, it could be freed at any point and make a 'lookup' using it unsafe. In addition to a reference count, object that create other objects have a list of all the objects they create. This serves two purposes. The obvious case is to keep the object "busy" until all of its children are freed. Second, it provides a means to visit all of the objects it creates. Every object has a pointer to its parent. This is used for such things as managing the object list and for calling methods on the parent. Pointers vs. Structure copies In order to manage multiple fsals which have different sized private object storage, we only pass and save pointers. This keeps all of the referencing structures of constant size. The 'container_of' manages both the differences in private structure sizes and the actual layout of the private structures. This also saves the space and cpu overhead of expensive structure copies. Public FSAL Structure definitions The 'ops' element is the most commonly used element of an object's public structure. Any other elements should be used with care. Very little upper level code should directly reference any other elements. Common methods located in src/FSAL/fsal_commonlib.c are used instead. No size assumptions should be made either because, other than the 'ops' element, all other elements are subject to change. Think of them as 'protected'. The only reason they are declared in the public structure is because they are used in all fsals i.e. every 'fsal_export' has lock and reference count. FSAL structure -------------- The VFS fsal is the first candidate, primarily because it is the most promising base for the new work our group is doing. I have followed a simple pattern for this fsal. There is one file per object to be implemented. 'export.c' implements 'fsal_export' for VFS and 'main.c' implements 'fsal_module'. This allows the declaration of the object methods as 'static'. Only common methods are declared publicly in src/include/FSAL/fsal_commonlib.h and implemented in src/FSAL/fsal_commonlib.c. Likewise common helper functions are in the these files. I have used new names for the public fsal structures on purpose. All of the old api structures will be deprecated and removed from fsal_types.h and at that point, the compiler will find the usages that are still 'hiding' and complain. Otherwise, we could have subtle bugs arise from the change in structure and especially the use of the 'old' structure. At one level, it looks like the VFS fsal is a complete rewrite which is partly true. As with the structures and their names, I am forcing breakage at compile time. * There is a significant amount of code re-use. For example, a 'lookup' for a handle is still the same sequence of syscalls or library calls. That logic and the error paths have all been worked out. * Method functions now do only one thing. The 'extra' bit that acquires attributes, for example, is gone. If the upper layers want attributes, they should call the method to get them. This makes things smaller and smaller is good. * Access checking and all the (see above) is also gone. This is now moved to the core. The fsal can assume that if the method is being called, it is ok to do the work. If the action is not allowed, the core should never make the method call. * There is a 'test_access' method defined in the api and a common function is part of the library. Most fsal implementations would use the library supplied one. However, some implementations may want to supply their own. There are two very important caveats with this method: - This method is only for the core to use. It is *NEVER* called from within a fsal. Ever. - If a fsal supplies its own, all that is required is to substitute the fsal function in the definition of the handle ops vector. If the fsal implementation would also make this configurable, it should manage it by doing the test within its own function and call the library function itself, returning its return values directly. * Along with access checking in one place in the core server, the fsal is responsible for managing the difference between what NFS wants and the resource (filesystem) it manages provides. For example, the server assumes that every fsal supports ACLs. If the resource does not support them or supports them in a different way, the fsal is responsible for managing the differences. * There are some linkages between an object and its 'parent' object. These are managed in two ways. First, common functions are in fsal_commonlib.c so every fsal implementation can use them. The second case is for functions that are fsal specific such as 'lookup' which is a 'fsal_export' method that must have intimate knowledge of what a 'fsal_obj_handle' is. In this case, a function prototype is defined in export.c so it can be included in the ops vector and the method is declared global (not static) in handle.c. Function prototypes are declared in headers only if they are referenced by multiple modules. The goal is to break as much incorrect code at the compile level as we can. Use of _USE_FOO --------------- There are a number of places where fsal enabling config parameters are used in the middle of the core. All of this usage will be deprecated and removed. For example, some data structures have fsal specific elements defined. If the fsal implementation needs these elements, they should be moved to the private portion of the most relevant fsal object. This restriction is necessary to make the core completely fsal implementation agnostic. There should be no #ifdef conditionals on public data structures. If a feature is conditionally built, its data structures should be harmless if the feature is disabled. These conditionals are clutter and can break ABI which is now important with the new api NOTE: There are a few #ifdef conditionals in fsal_api.h. These are temporary and will be removed in the final version. At that time, the contents of fsal_api.h will be version controlled as a fixed ABI. nfs-ganesha-2.6.0/src/RPCAL/000077500000000000000000000000001324272410200153365ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/RPCAL/CMakeLists.txt000066400000000000000000000004711324272410200201000ustar00rootroot00000000000000########### next target ############### SET(rpcal_STAT_SRCS nfs_dupreq.c rpc_tools.c ) if(_HAVE_GSSAPI) set(rpcal_STAT_SRCS ${rpcal_STAT_SRCS} gss_credcache.c gss_extra.c) endif(_HAVE_GSSAPI) add_library(rpcal STATIC ${rpcal_STAT_SRCS}) add_sanitizers(rpcal) ########### install files ############### nfs-ganesha-2.6.0/src/RPCAL/gss_credcache.c000066400000000000000000000601111324272410200202560ustar00rootroot00000000000000/* Copyright (c) 2004 The Regents of the University of Michigan. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* GSS Credential Cache, redacted from gssd. */ #include "config.h" #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(HAVE_KRB5) && !defined(GSS_C_NT_HOSTBASED_SERVICE) #include #define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name #endif #ifdef HAVE_UNISTD_H #include #endif #include #ifdef HAVE_COM_ERR_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_PRIVATE_KRB5_FUNCTIONS #include #endif #include #include #include "log.h" #include "common_utils.h" #include "abstract_mem.h" #include "gss_credcache.h" /* * Hide away some of the MIT vs. Heimdal differences * here with macros... */ #ifdef HAVE_KRB5 #define k5_free_unparsed_name(ctx, name) \ krb5_free_unparsed_name((ctx), (name)) #define k5_free_default_realm(ctx, realm) \ krb5_free_default_realm((ctx), (realm)) #define k5_free_kt_entry(ctx, kte) \ krb5_free_keytab_entry_contents((ctx), (kte)) #else /* Heimdal */ #define k5_free_unparsed_name(ctx, name) \ gsh_free(name) #define k5_free_default_realm(ctx, realm) \ gsh_free(realm) #define k5_free_kt_entry(ctx, kte) \ krb5_kt_free_entry((ctx), (kte)) #undef USE_GSS_KRB5_CCACHE_NAME #define USE_GSS_KRB5_CCACHE_NAME 1 #endif #define GSSD_DEFAULT_CRED_PREFIX "krb5cc_" #define GSSD_DEFAULT_MACHINE_CRED_SUFFIX "machine" #define GSSD_DEFAULT_KEYTAB_FILE "/etc/krb5.keytab" #define GSSD_MAX_CCACHE_SEARCH 16 struct gssd_k5_kt_princ { struct gssd_k5_kt_princ *next; krb5_principal princ; char *ccname; char *realm; krb5_timestamp endtime; }; typedef void (*gssd_err_func_t)(const char *, ...); static char keytabfile[PATH_MAX] = GSSD_DEFAULT_KEYTAB_FILE; static int use_memcache; static struct gssd_k5_kt_princ *gssd_k5_kt_princ_list; static pthread_mutex_t ple_mtx = PTHREAD_MUTEX_INITIALIZER; static char *gssd_k5_err_msg(krb5_context context, krb5_error_code code); static int gssd_get_single_krb5_cred(krb5_context context, krb5_keytab kt, struct gssd_k5_kt_princ *ple, int nocache); static void gssd_set_krb5_ccache_name(char *ccname); static void gssd_err_default(int priority, const char *fmt, ...) { /* XXX do something */ } static gssd_err_func_t gss_err = (gssd_err_func_t) gssd_err_default; #define printerr(pri, ...) gss_err(__VA_ARGS__) /* Global list of principals/cache file names for machine credentials */ /* * Obtain credentials via a key in the keytab given * a keytab handle and a gssd_k5_kt_princ structure. * Checks to see if current credentials are expired, * if not, uses the keytab to obtain new credentials. * * Returns: * 0 => success (or credentials have not expired) * nonzero => error */ static int gssd_get_single_krb5_cred(krb5_context context, krb5_keytab kt, struct gssd_k5_kt_princ *ple, int nocache) { #if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS krb5_get_init_creds_opt * init_opts = NULL; #else krb5_get_init_creds_opt options; #endif krb5_get_init_creds_opt * opts; krb5_creds my_creds; krb5_ccache ccache = NULL; char kt_name[BUFSIZ]; char cc_name[BUFSIZ]; int code; time_t now = time(0); char *cache_type; char *pname = NULL; char *k5err = NULL; memset(&my_creds, 0, sizeof(my_creds)); if (ple->ccname && ple->endtime > now && !nocache) { printerr(2, "INFO: Credentials in CC '%s' are good until %d\n", ple->ccname, ple->endtime); code = 0; goto out; } code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ); if (code != 0) { printerr(0, "ERROR: Unable to get keytab name in gssd_get_single_krb5_cred\n"); goto out; } if ((krb5_unparse_name(context, ple->princ, &pname))) pname = NULL; #if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS code = krb5_get_init_creds_opt_alloc(context, &init_opts); if (code) { k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s allocating gic options\n", k5err); goto out; } if (krb5_get_init_creds_opt_set_addressless(context, init_opts, 1)) printerr(1, "WARNING: Unable to set option for addressless tickets. May have problems behind a NAT.\n"); #ifdef TEST_SHORT_LIFETIME /* set a short lifetime (for debugging only!) */ printerr(0, "WARNING: Using (debug) short machine cred lifetime!\n"); krb5_get_init_creds_opt_set_tkt_life(init_opts, 5 * 60); #endif opts = init_opts; #else /* HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS */ krb5_get_init_creds_opt_init(&options); krb5_get_init_creds_opt_set_address_list(&options, NULL); #ifdef TEST_SHORT_LIFETIME /* set a short lifetime (for debugging only!) */ printerr(0, "WARNING: Using (debug) short machine cred lifetime!\n"); krb5_get_init_creds_opt_set_tkt_life(&options, 5 * 60); #endif opts = &options; #endif code = krb5_get_init_creds_keytab(context, &my_creds, ple->princ, kt, 0, NULL, opts); if (code != 0) { k5err = gssd_k5_err_msg(context, code); printerr(1, "WARNING: %s while getting initial ticket for principal '%s' using keytab '%s'\n", k5err, pname ? pname : "", kt_name); goto out; } /* * Initialize cache file which we're going to be using */ if (use_memcache) cache_type = "MEMORY"; else cache_type = "FILE"; snprintf(cc_name, sizeof(cc_name), "%s:%s/%s%s_%s", cache_type, ccachesearch[0], GSSD_DEFAULT_CRED_PREFIX, GSSD_DEFAULT_MACHINE_CRED_SUFFIX, ple->realm); ple->endtime = my_creds.times.endtime; if (ple->ccname != NULL) gsh_free(ple->ccname); ple->ccname = gsh_strdup(cc_name); code = krb5_cc_resolve(context, cc_name, &ccache); if (code != 0) { k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s while opening credential cache '%s'\n", k5err, cc_name); goto out; } code = krb5_cc_initialize(context, ccache, ple->princ); if (code != 0) { k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s while initializing credential cache '%s'\n", k5err, cc_name); } code = krb5_cc_store_cred(context, ccache, &my_creds); if (code != 0) { k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s while storing credentials in '%s'\n", k5err, cc_name); goto out; } /* if we get this far, let gss mech know */ gssd_set_krb5_ccache_name(cc_name); code = 0; printerr(2, "Successfully obtained machine credentials for principal '%s' stored in ccache '%s'\n", pname, cc_name); out: #if HAVE_KRB5_GET_INIT_CREDS_OPT_SET_ADDRESSLESS if (init_opts) krb5_get_init_creds_opt_free(context, init_opts); #endif if (pname) k5_free_unparsed_name(context, pname); if (ccache) krb5_cc_close(context, ccache); krb5_free_cred_contents(context, &my_creds); gsh_free(k5err); return code; } /* * Depending on the version of Kerberos, we either need to use * a private function, or simply set the environment variable. */ static void gssd_set_krb5_ccache_name(char *ccname) { #ifdef USE_GSS_KRB5_CCACHE_NAME u_int maj_stat, min_stat; printerr(2, "using gss_krb5_ccache_name to select krb5 ccache %s\n", ccname); maj_stat = gss_krb5_ccache_name(&min_stat, ccname, NULL); if (maj_stat != GSS_S_COMPLETE) { printerr(0, "WARNING: gss_krb5_ccache_name with name '%s' failed (%s)\n", ccname, error_message(min_stat)); } #else /* * Set the KRB5CCNAME environment variable to tell the krb5 code * which credentials cache to use. (Instead of using the private * function above for which there is no generic gssapi * equivalent.) */ printerr(2, "using environment variable to select krb5 ccache %s\n", ccname); setenv("KRB5CCNAME", ccname, 1); #endif } /* * Given a principal, find a matching ple structure */ static struct gssd_k5_kt_princ *find_ple_by_princ(krb5_context context, krb5_principal princ) { struct gssd_k5_kt_princ *ple; for (ple = gssd_k5_kt_princ_list; ple != NULL; ple = ple->next) { if (krb5_principal_compare(context, ple->princ, princ)) return ple; } /* no match found */ return NULL; } /* * Create, initialize, and add a new ple structure to the global list */ static struct gssd_k5_kt_princ *new_ple(krb5_context context, krb5_principal princ) { struct gssd_k5_kt_princ *ple = NULL, *p; krb5_error_code code; char *default_realm; int is_default_realm = 0; ple = gsh_calloc(1, sizeof(struct gssd_k5_kt_princ)); #ifdef HAVE_KRB5 ple->realm = gsh_malloc(princ->realm.length + 1); memcpy(ple->realm, princ->realm.data, princ->realm.length); ple->realm[princ->realm.length] = '\0'; #else ple->realm = gsh_strdup(princ->realm); #endif code = krb5_copy_principal(context, princ, &ple->princ); if (code) { gsh_free(ple->realm); gsh_free(ple); return NULL; } /* * Add new entry onto the list (if this is the default * realm, always add to the front of the list) */ code = krb5_get_default_realm(context, &default_realm); if (code == 0) { if (strcmp(ple->realm, default_realm) == 0) is_default_realm = 1; k5_free_default_realm(context, default_realm); } if (is_default_realm) { ple->next = gssd_k5_kt_princ_list; gssd_k5_kt_princ_list = ple; } else { p = gssd_k5_kt_princ_list; while (p != NULL && p->next != NULL) p = p->next; if (p == NULL) gssd_k5_kt_princ_list = ple; else p->next = ple; } return ple; } /* * Given a principal, find an existing ple structure, or create one */ static struct gssd_k5_kt_princ *get_ple_by_princ(krb5_context context, krb5_principal princ) { struct gssd_k5_kt_princ *ple; PTHREAD_MUTEX_lock(&ple_mtx); ple = find_ple_by_princ(context, princ); if (ple == NULL) ple = new_ple(context, princ); PTHREAD_MUTEX_unlock(&ple_mtx); return ple; } /* * Given a (possibly unqualified) hostname, * return the fully qualified (lower-case!) hostname */ static int get_full_hostname(const char *inhost, char *outhost, int outhostlen) { struct addrinfo *addrs = NULL; struct addrinfo hints; int retval; char *c; memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_family = PF_UNSPEC; hints.ai_flags = AI_CANONNAME; /* Get full target hostname */ retval = getaddrinfo(inhost, NULL, &hints, &addrs); if (retval) { printerr(1, "%s while getting full hostname for '%s'\n", gai_strerror(retval), inhost); goto out; } strmaxcpy(outhost, addrs->ai_canonname, outhostlen); freeaddrinfo(addrs); for (c = outhost; *c != '\0'; c++) *c = tolower(*c); printerr(3, "Full hostname for '%s' is '%s'\n", inhost, outhost); retval = 0; out: return retval; } /* * If principal matches the given realm and service name, * and has *any* instance (hostname), return 1. * Otherwise return 0, indicating no match. */ #ifdef HAVE_KRB5 static int realm_and_service_match(krb5_principal p, const char *realm, const char *service) { /* Must have two components */ if (p->length != 2) return 0; if ((strlen(realm) == p->realm.length) && (strncmp(realm, p->realm.data, p->realm.length) == 0) && (strlen(service) == p->data[0].length) && (strncmp(service, p->data[0].data, p->data[0].length) == 0)) return 1; return 0; } #else static int realm_and_service_match(krb5_context context, krb5_principal p, const char *realm, const char *service) { const char *name, *inst; if (p->name.name_string.len != 2) return 0; name = krb5_principal_get_comp_string(context, p, 0); inst = krb5_principal_get_comp_string(context, p, 1); if (name == NULL || inst == NULL) return 0; if ((strcmp(realm, p->realm) == 0) && (strcmp(service, name) == 0)) return 1; return 0; } #endif /* * Search the given keytab file looking for an entry with the given * service name and realm, ignoring hostname (instance). * * Returns: * 0 => No error * non-zero => An error occurred * * If a keytab entry is found, "found" is set to one, and the keytab * entry is returned in "kte". Otherwise, "found" is zero, and the * value of "kte" is unpredictable. */ static int gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt, const char *realm, const char *service, int *found, krb5_keytab_entry *kte) { krb5_kt_cursor cursor; krb5_error_code code; struct gssd_k5_kt_princ *ple; int retval = -1, status; char kt_name[BUFSIZ]; char *pname; char *k5err = NULL; if (found == NULL) { retval = EINVAL; goto out; } *found = 0; /* * Look through each entry in the keytab file and determine * if we might want to use it as machine credentials. If so, * save info in the global principal list (gssd_k5_kt_princ_list). */ code = krb5_kt_get_name(context, kt, kt_name, BUFSIZ); if (code != 0) { k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s attempting to get keytab name\n", k5err); gsh_free(k5err); retval = code; goto out; } code = krb5_kt_start_seq_get(context, kt, &cursor); if (code != 0) { k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s while beginning keytab scan for keytab '%s'\n", k5err, kt_name); gsh_free(k5err); retval = code; goto out; } while ((code = krb5_kt_next_entry(context, kt, kte, &cursor)) == 0) { code = krb5_unparse_name(context, kte->principal, &pname); if (code != 0) { k5err = gssd_k5_err_msg(context, code); printerr(0, "WARNING: Skipping keytab entry because we failed to unparse principal name: %s\n", k5err); k5_free_kt_entry(context, kte); gsh_free(k5err); continue; } printerr(4, "Processing keytab entry for principal '%s'\n", pname); /* Use the first matching keytab entry found */ #ifdef HAVE_KRB5 status = realm_and_service_match(kte->principal, realm, service); #else status = realm_and_service_match(context, kte->principal, realm, service); #endif if (status) { printerr(4, "We WILL use this entry (%s)\n", pname); ple = get_ple_by_princ(context, kte->principal); /* * Return, don't free, keytab entry if * we were successful! */ if (unlikely(ple == NULL)) { retval = ENOMEM; k5_free_kt_entry(context, kte); k5_free_unparsed_name(context, pname); (void) krb5_kt_end_seq_get( context, kt, &cursor); goto out; } else { retval = 0; *found = 1; } k5_free_unparsed_name(context, pname); break; } else { printerr(4, "We will NOT use this entry (%s)\n", pname); } k5_free_unparsed_name(context, pname); k5_free_kt_entry(context, kte); } code = krb5_kt_end_seq_get(context, kt, &cursor); if (code != 0) { k5err = gssd_k5_err_msg(context, code); printerr(0, "WARNING: %s while ending keytab scan for keytab '%s'\n", k5err, kt_name); gsh_free(k5err); } retval = 0; out: return retval; } /* * Find a keytab entry to use for a given target hostname. * Tries to find the most appropriate keytab to use given the * name of the host we are trying to connect with. */ static int find_keytab_entry(krb5_context context, krb5_keytab kt, const char *hostname, krb5_keytab_entry *kte, const char **svcnames) { krb5_error_code code; char **realmnames = NULL; char myhostname[NI_MAXHOST], targethostname[NI_MAXHOST]; char myhostad[NI_MAXHOST + 1]; int i, j, retval; char *default_realm = NULL; char *realm; char *k5err = NULL; int tried_all = 0, tried_default = 0; krb5_principal princ; /* Get full target hostname */ retval = get_full_hostname(hostname, targethostname, sizeof(targethostname)); if (retval) goto out; /* Get full local hostname */ retval = gethostname(myhostname, sizeof(myhostname)); if (retval) { k5err = gssd_k5_err_msg(context, retval); printerr(1, "%s while getting local hostname\n", k5err); gsh_free(k5err); goto out; } /* Compute the active directory machine name HOST$ */ strcpy(myhostad, myhostname); for (i = 0; myhostad[i] != 0; ++i) myhostad[i] = toupper(myhostad[i]); myhostad[i] = '$'; myhostad[i + 1] = 0; retval = get_full_hostname(myhostname, myhostname, sizeof(myhostname)); if (retval) goto out; code = krb5_get_default_realm(context, &default_realm); if (code) { retval = code; k5err = gssd_k5_err_msg(context, code); printerr(1, "%s while getting default realm name\n", k5err); gsh_free(k5err); goto out; } /* * Get the realm name(s) for the target hostname. * In reality, this function currently only returns a * single realm, but we code with the assumption that * someday it may actually return a list. */ code = krb5_get_host_realm(context, targethostname, &realmnames); if (code) { k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s while getting realm(s) for host '%s'\n", k5err, targethostname); gsh_free(k5err); retval = code; goto out; } /* * Try the "appropriate" realm first, and if nothing found for that * realm, try the default realm (if it hasn't already been tried). */ i = 0; realm = realmnames[i]; while (1) { if (realm == NULL) { tried_all = 1; if (!tried_default) realm = default_realm; } if (tried_all && tried_default) break; if (strcmp(realm, default_realm) == 0) tried_default = 1; for (j = 0; svcnames[j] != NULL; j++) { char spn[300]; /* * The special svcname "$" means 'try the active * directory machine account' */ if (strcmp(svcnames[j], "$") == 0) { snprintf(spn, sizeof(spn), "%s@%s", myhostad, realm); code = krb5_build_principal_ext(context, &princ, strlen(realm), realm, strlen(myhostad), myhostad, NULL); } else { snprintf(spn, sizeof(spn), "%s/%s@%s", svcnames[j], myhostname, realm); code = krb5_build_principal_ext(context, &princ, strlen(realm), realm, strlen(svcnames [j]), svcnames[j], strlen(myhostname), myhostname, NULL); } if (code) { k5err = gssd_k5_err_msg(context, code); printerr(1, "%s while building principal for '%s'\n", k5err, spn); gsh_free(k5err); continue; } code = krb5_kt_get_entry(context, kt, princ, 0, 0, kte); krb5_free_principal(context, princ); if (code) { k5err = gssd_k5_err_msg(context, code); printerr(3, "%s while getting keytab entry for '%s'\n", k5err, spn); gsh_free(k5err); } else { printerr(3, "Success getting keytab entry for '%s'\n", spn); retval = 0; goto out; } retval = code; } /* * Nothing found with our hostname instance, now look for * names with any instance (they must have an instance) */ for (j = 0; svcnames[j] != NULL; j++) { int found = 0; if (strcmp(svcnames[j], "$") == 0) continue; code = gssd_search_krb5_keytab(context, kt, realm, svcnames[j], &found, kte); if (!code && found) { printerr(3, "Success getting keytab entry for %s/*@%s\n", svcnames[j], realm); retval = 0; goto out; } } if (!tried_all) { i++; realm = realmnames[i]; } } out: if (default_realm) k5_free_default_realm(context, default_realm); if (realmnames) krb5_free_host_realm(context, realmnames); return retval; } /* * A common routine for getting the Kerberos error message */ static char *gssd_k5_err_msg(krb5_context context, krb5_error_code code) { #if HAVE_KRB5_GET_ERROR_MESSAGE if (context != NULL) { const char *origmsg; char *msg = NULL; origmsg = krb5_get_error_message(context, code); msg = gsh_strdup(origmsg); krb5_free_error_message(context, origmsg); return msg; } #endif #if HAVE_KRB5 return gsh_strdup(error_message(code)); #else if (context != NULL) return gsh_strdup(krb5_get_err_text(context, code)); else return gsh_strdup(error_message(code)); #endif } /* Public Interfaces */ char *ccachesearch[GSSD_MAX_CCACHE_SEARCH + 1]; /* * Obtain (or refresh if necessary) Kerberos machine credentials */ int gssd_refresh_krb5_machine_credential(char *hostname, struct gssd_k5_kt_princ *ple, char *service) { krb5_error_code code = 0; krb5_context context; krb5_keytab kt = NULL; int retval = 0; char *k5err = NULL; const char *svcnames[5] = { "$", "root", "nfs", "host", NULL }; /* * If a specific service name was specified, use it. * Otherwise, use the default list. */ if (service != NULL && strcmp(service, "*") != 0) { svcnames[0] = service; svcnames[1] = NULL; } if (hostname == NULL && ple == NULL) return EINVAL; code = krb5_init_context(&context); if (code) { k5err = gssd_k5_err_msg(NULL, code); printerr(0, "ERROR: %s: %s while initializing krb5 context\n", __func__, k5err); retval = code; gsh_free(k5err); goto out_wo_context; } code = krb5_kt_resolve(context, keytabfile, &kt); if (code != 0) { k5err = gssd_k5_err_msg(context, code); printerr(0, "ERROR: %s: %s while resolving keytab '%s'\n", __func__, k5err, keytabfile); gsh_free(k5err); goto out; } if (ple == NULL) { krb5_keytab_entry kte; code = find_keytab_entry(context, kt, hostname, &kte, svcnames); if (code) { printerr(0, "ERROR: %s: no usable keytab entry found in keytab %s for connection with host %s\n", __func__, keytabfile, hostname); retval = code; goto out; } ple = get_ple_by_princ(context, kte.principal); k5_free_kt_entry(context, &kte); if (ple == NULL) { char *pname; if ((krb5_unparse_name(context, kte.principal, &pname))) { pname = NULL; } printerr(0, "ERROR: %s: Could not locate or create ple struct for principal %s for connection with host %s\n", __func__, pname ? pname : "", hostname); if (pname) k5_free_unparsed_name(context, pname); goto out; } } retval = gssd_get_single_krb5_cred(context, kt, ple, 0); out: if (kt) krb5_kt_close(context, kt); krb5_free_context(context); out_wo_context: return retval; } int gssd_check_mechs(void) { u_int32_t maj_stat, min_stat; gss_OID_set supported_mechs = GSS_C_NO_OID_SET; int retval = -1; maj_stat = gss_indicate_mechs(&min_stat, &supported_mechs); if (maj_stat != GSS_S_COMPLETE) { printerr(0, "Unable to obtain list of supported mechanisms. Check that gss library is properly configured.\n"); goto out; } if (supported_mechs == GSS_C_NO_OID_SET || supported_mechs->count == 0) { printerr(0, "Unable to obtain list of supported mechanisms. Check that gss library is properly configured.\n"); goto out; } maj_stat = gss_release_oid_set(&min_stat, &supported_mechs); retval = 0; out: return retval; } nfs-ganesha-2.6.0/src/RPCAL/gss_extra.c000066400000000000000000000063021324272410200175020ustar00rootroot00000000000000/* Copyright (c) 2000 The Regents of the University of Michigan. All rights reserved. Copyright (c) 2000 Dug Song . All rights reserved, all wrongs reversed. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Id: svc_auth_gss.c,v 1.28 2002/10/15 21:29:36 kwc Exp */ #include "config.h" #include #include #include #include #include "gsh_rpc.h" #ifdef HAVE_HEIMDAL #include #define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE #else #include #include #endif #include "nfs_core.h" #include "log.h" /** * @brief Convert GSSAPI status to a string * * @param[out] outmsg Output string * @param[in] tag Tag * @param[in] maj_stat GSSAPI major status * @param[in] min_stat GSSAPI minor status */ void log_sperror_gss(char *outmsg, OM_uint32 maj_stat, OM_uint32 min_stat) { OM_uint32 smin; gss_buffer_desc msg; gss_buffer_desc msg2; int msg_ctx = 0; if (gss_display_status (&smin, maj_stat, GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &msg) != GSS_S_COMPLETE) { sprintf(outmsg, "untranslatable error"); return; } if (gss_display_status (&smin, min_stat, GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &msg2) != GSS_S_COMPLETE) { gss_release_buffer(&smin, &msg); sprintf(outmsg, "%s : untranslatable error", (char *)msg.value); return; } sprintf(outmsg, "%s : %s ", (char *)msg.value, (char *)msg2.value); gss_release_buffer(&smin, &msg); gss_release_buffer(&smin, &msg2); } const char *str_gc_proc(rpc_gss_proc_t gc_proc) { switch (gc_proc) { case RPCSEC_GSS_DATA: return "RPCSEC_GSS_DATA"; case RPCSEC_GSS_INIT: return "RPCSEC_GSS_INIT"; case RPCSEC_GSS_CONTINUE_INIT: return "RPCSEC_GSS_CONTINUE_INIT"; case RPCSEC_GSS_DESTROY: return "RPCSEC_GSS_DESTROY"; } return "unknown"; } nfs-ganesha-2.6.0/src/RPCAL/nfs_dupreq.c000066400000000000000000001053441324272410200176570ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Portions Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * Portions Copyright (C) 2012, The Linux Box Corporation * Contributor : Matt Benjamin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @file nfs_dupreq.c * @author Matt Benjamin * @brief NFS Duplicate Request Cache */ #include "config.h" #include #include #include #include #include #include /* XXX prune: */ #include "log.h" #include "nfs_proto_functions.h" #include "nfs_dupreq.h" #include "city.h" #include "abstract_mem.h" #include "gsh_intrinsic.h" #include "gsh_wait_queue.h" #define DUPREQ_NOCACHE 0x02 #define DUPREQ_MAX_RETRIES 5 #define NFS_pcp nfs_param.core_param #define NFS_program NFS_pcp.program pool_t *dupreq_pool; pool_t *nfs_res_pool; pool_t *tcp_drc_pool; /* pool of per-connection DRC objects */ const char *dupreq_status_table[] = { "DUPREQ_SUCCESS", "DUPREQ_INSERT_MALLOC_ERROR", "DUPREQ_BEING_PROCESSED", "DUPREQ_EXISTS", "DUPREQ_ERROR", }; const char *dupreq_state_table[] = { "DUPREQ_START", "DUPREQ_COMPLETE", "DUPREQ_DELETED", }; /* drc_t holds the request/response cache. There is a single drc_t for * all udp connections. There is a drc_t for each tcp connection (aka * socket). Since a client could close a socket and reconnect, we would * like to use the same drc cache for the reconnection. For this reason, * we don't want to free the drc as soon as the tcp connection gets * closed, but rather keep them in a recycle list for sometime. * * The life of tcp drc: it gets allocated when we process the first * request on the connection. It is put into rbtree (tcp_drc_recycle_t). * drc cache maintains a ref count. Every request as well as the xprt * holds a ref count. Its ref count should go to zero when the * connection's xprt gets freed (all requests should be completed on the * xprt by this time). When the ref count goes to zero, it is also put * into a recycle queue (tcp_drc_recycle_q). When a reconnection * happens, we hope to find the same drc that was used before, and the * ref count goes up again. At the same time, the drc will be removed * from the recycle queue. Only drc's with ref count zero end up in the * recycle queue. If a reconnection doesn't happen in time, the drc gets * freed by drc_free_expired() after some period of inactivety. * * Most ref count methods assume that a ref count doesn't go up from * zero, so a thread that decrements the ref count to zero would be the * only one acting on it, and it could do so without any locks! Since * the drc ref count could go up from zero, care must be taken. The * thread that decrements the ref count to zero will have to put the drc * into the recycle queue. It will do so only after dropping the lock in * the current implementation. If we let nfs_dupreq_get_drc() reuse the * drc before it gets into recycle queue, we could end up with multiple * threads that decrement the ref count to zero. */ struct drc_st { pthread_mutex_t mtx; drc_t udp_drc; /* shared DRC */ struct rbtree_x tcp_drc_recycle_t; TAILQ_HEAD(drc_st_tailq, drc) tcp_drc_recycle_q; /* fifo */ int32_t tcp_drc_recycle_qlen; time_t last_expire_check; uint32_t expire_delta; }; static struct drc_st *drc_st; /** * @brief Comparison function for duplicate request entries. * * @param[in] lhs An integer * @param[in] rhs Another integer * * @return -1 if the left-hand is smaller than the right, 0 if they * are equal, and 1 if the left-hand is larger. */ static inline int uint32_cmpf(uint32_t lhs, uint32_t rhs) { if (lhs < rhs) return -1; if (lhs == rhs) return 0; return 1; } /** * @brief Comparison function for duplicate request entries. * * @param[in] lhs An integer * @param[in] rhs Another integer * * @return -1 if the left-hand is smaller than the right, 0 if they * are equal, and 1 if the left-hand is larger. */ static inline int uint64_cmpf(uint64_t lhs, uint64_t rhs) { if (lhs < rhs) return -1; if (lhs == rhs) return 0; return 1; } /** * @brief Comparison function for entries in a shared DRC * * @param[in] lhs Left-hand-side * @param[in] rhs Right-hand-side * * @return -1,0,1. */ static inline int dupreq_shared_cmpf(const struct opr_rbtree_node *lhs, const struct opr_rbtree_node *rhs) { dupreq_entry_t *lk, *rk; lk = opr_containerof(lhs, dupreq_entry_t, rbt_k); rk = opr_containerof(rhs, dupreq_entry_t, rbt_k); switch (sockaddr_cmpf(&lk->hin.addr, &rk->hin.addr, false)) { case -1: return -1; case 0: switch (uint32_cmpf(lk->hin.tcp.rq_xid, rk->hin.tcp.rq_xid)) { case -1: return -1; case 0: return uint64_cmpf(lk->hk, rk->hk); default: break; } /* xid */ break; default: break; } /* addr+port */ return 1; } /** * @brief Comparison function for entries in a per-connection (TCP) DRC * * @param[in] lhs Left-hand-side * @param[in] rhs Right-hand-side * * @return -1,0,1. */ static inline int dupreq_tcp_cmpf(const struct opr_rbtree_node *lhs, const struct opr_rbtree_node *rhs) { dupreq_entry_t *lk, *rk; LogDebug(COMPONENT_DUPREQ, "%s", __func__); lk = opr_containerof(lhs, dupreq_entry_t, rbt_k); rk = opr_containerof(rhs, dupreq_entry_t, rbt_k); if (lk->hin.tcp.rq_xid < rk->hin.tcp.rq_xid) return -1; if (lk->hin.tcp.rq_xid == rk->hin.tcp.rq_xid) { LogDebug(COMPONENT_DUPREQ, "xids eq %" PRIu32 ", ck1 %" PRIu64 " ck2 %" PRIu64, lk->hin.tcp.rq_xid, lk->hk, rk->hk); return uint64_cmpf(lk->hk, rk->hk); } return 1; } /** * @brief Comparison function for recycled per-connection (TCP) DRCs * * @param[in] lhs Left-hand-side * @param[in] rhs Right-hand-side * * @return -1,0,1. */ static inline int drc_recycle_cmpf(const struct opr_rbtree_node *lhs, const struct opr_rbtree_node *rhs) { drc_t *lk, *rk; lk = opr_containerof(lhs, drc_t, d_u.tcp.recycle_k); rk = opr_containerof(rhs, drc_t, d_u.tcp.recycle_k); return sockaddr_cmpf( &lk->d_u.tcp.addr, &rk->d_u.tcp.addr, false); } /** * @brief Initialize a shared duplicate request cache */ static inline void init_shared_drc(void) { drc_t *drc = &drc_st->udp_drc; int ix, code __attribute__ ((unused)) = 0; drc->type = DRC_UDP_V234; drc->refcnt = 0; drc->retwnd = 0; drc->d_u.tcp.recycle_time = 0; drc->maxsize = nfs_param.core_param.drc.udp.size; drc->cachesz = nfs_param.core_param.drc.udp.cachesz; drc->npart = nfs_param.core_param.drc.udp.npart; drc->hiwat = nfs_param.core_param.drc.udp.hiwat; gsh_mutex_init(&drc->mtx, NULL); /* init dict */ code = rbtx_init(&drc->xt, dupreq_shared_cmpf, drc->npart, RBT_X_FLAG_ALLOC | RBT_X_FLAG_CACHE_WT); assert(!code); /* completed requests */ TAILQ_INIT(&drc->dupreq_q); /* init closed-form "cache" partition */ for (ix = 0; ix < drc->npart; ++ix) { struct rbtree_x_part *xp = &(drc->xt.tree[ix]); drc->xt.cachesz = drc->cachesz; xp->cache = gsh_calloc(drc->cachesz, sizeof(struct opr_rbtree_node *)); } } /** * @brief Initialize the DRC package. */ void dupreq2_pkginit(void) { int code __attribute__ ((unused)) = 0; dupreq_pool = pool_basic_init("Duplicate Request Pool", sizeof(dupreq_entry_t)); nfs_res_pool = pool_basic_init("nfs_res_t pool", sizeof(nfs_res_t)); tcp_drc_pool = pool_basic_init("TCP DRC Pool", sizeof(drc_t)); drc_st = gsh_calloc(1, sizeof(struct drc_st)); /* init shared statics */ gsh_mutex_init(&drc_st->mtx, NULL); /* recycle_t */ code = rbtx_init(&drc_st->tcp_drc_recycle_t, drc_recycle_cmpf, nfs_param.core_param.drc.tcp.recycle_npart, RBT_X_FLAG_ALLOC); /* XXX error? */ /* init recycle_q */ TAILQ_INIT(&drc_st->tcp_drc_recycle_q); drc_st->tcp_drc_recycle_qlen = 0; drc_st->last_expire_check = time(NULL); drc_st->expire_delta = nfs_param.core_param.drc.tcp.recycle_expire_s; /* UDP DRC is global, shared */ init_shared_drc(); } /** * @brief Determine the protocol of the supplied TI-RPC SVCXPRT* * * @param[in] xprt The SVCXPRT * * @return IPPROTO_UDP or IPPROTO_TCP. */ static inline unsigned int get_ipproto_by_xprt(SVCXPRT *xprt) { switch (xprt->xp_type) { case XPRT_UDP: case XPRT_UDP_RENDEZVOUS: return IPPROTO_UDP; case XPRT_TCP: case XPRT_TCP_RENDEZVOUS: return IPPROTO_TCP; default: break; } return IPPROTO_IP; /* Dummy output */ } /** * @brief Determine the dupreq2 DRC type to handle the supplied svc_req * * @param[in] req The svc_req being processed * * @return a value of type enum_drc_type. */ static inline enum drc_type get_drc_type(struct svc_req *req) { if (get_ipproto_by_xprt(req->rq_xprt) == IPPROTO_UDP) return DRC_UDP_V234; else { if (req->rq_msg.cb_vers == 4) return DRC_TCP_V4; } return DRC_TCP_V3; } /** * @brief Allocate a duplicate request cache * * @param[in] dtype Style DRC to allocate (e.g., TCP, by enum drc_type) * @param[in] maxsz Upper bound on requests to cache * @param[in] cachesz Number of entries in the closed hash partition * @param[in] flags DRC flags * * @return the drc, if successfully allocated, else NULL. */ static inline drc_t *alloc_tcp_drc(enum drc_type dtype) { drc_t *drc = pool_alloc(tcp_drc_pool); int ix, code __attribute__ ((unused)) = 0; drc->type = dtype; /* DRC_TCP_V3 or DRC_TCP_V4 */ drc->refcnt = 0; drc->retwnd = 0; drc->d_u.tcp.recycle_time = 0; drc->maxsize = nfs_param.core_param.drc.tcp.size; drc->cachesz = nfs_param.core_param.drc.tcp.cachesz; drc->npart = nfs_param.core_param.drc.tcp.npart; drc->hiwat = nfs_param.core_param.drc.tcp.hiwat; PTHREAD_MUTEX_init(&drc->mtx, NULL); /* init dict */ code = rbtx_init(&drc->xt, dupreq_tcp_cmpf, drc->npart, RBT_X_FLAG_ALLOC | RBT_X_FLAG_CACHE_WT); assert(!code); /* completed requests */ TAILQ_INIT(&drc->dupreq_q); /* recycling DRC */ TAILQ_INIT_ENTRY(drc, d_u.tcp.recycle_q); /* init "cache" partition */ for (ix = 0; ix < drc->npart; ++ix) { struct rbtree_x_part *xp = &(drc->xt.tree[ix]); drc->xt.cachesz = drc->cachesz; xp->cache = gsh_calloc(drc->cachesz, sizeof(struct opr_rbtree_node *)); } return drc; } /** * @brief Deep-free a per-connection (TCP) duplicate request cache * * @param[in] drc The DRC to dispose * * Assumes that the DRC has been allocated from the tcp_drc_pool. */ static inline void free_tcp_drc(drc_t *drc) { int ix; for (ix = 0; ix < drc->npart; ++ix) { if (drc->xt.tree[ix].cache) gsh_free(drc->xt.tree[ix].cache); } PTHREAD_MUTEX_destroy(&drc->mtx); LogFullDebug(COMPONENT_DUPREQ, "free TCP drc %p", drc); pool_free(tcp_drc_pool, drc); } /** * @brief Increment the reference count on a DRC * * @param[in] drc The DRC to ref * * @return the new value of refcnt. */ static inline uint32_t nfs_dupreq_ref_drc(drc_t *drc) { return ++(drc->refcnt); /* locked */ } /** * @brief Decrement the reference count on a DRC * * @param[in] drc The DRC to unref * * @return the new value of refcnt. */ static inline uint32_t nfs_dupreq_unref_drc(drc_t *drc) { return --(drc->refcnt); /* locked */ } #define DRC_ST_LOCK() \ PTHREAD_MUTEX_lock(&drc_st->mtx) #define DRC_ST_UNLOCK() \ PTHREAD_MUTEX_unlock(&drc_st->mtx) /** * @brief Check for expired TCP DRCs. */ static inline void drc_free_expired(void) { drc_t *drc; time_t now = time(NULL); struct rbtree_x_part *t; struct opr_rbtree_node *odrc = NULL; DRC_ST_LOCK(); if ((drc_st->tcp_drc_recycle_qlen < 1) || (now - drc_st->last_expire_check) < 600) /* 10m */ goto unlock; do { drc = TAILQ_FIRST(&drc_st->tcp_drc_recycle_q); if (drc && (drc->d_u.tcp.recycle_time > 0) && ((now - drc->d_u.tcp.recycle_time) > drc_st->expire_delta)) { assert(drc->refcnt == 0); LogFullDebug(COMPONENT_DUPREQ, "remove expired drc %p from recycle queue", drc); t = rbtx_partition_of_scalar(&drc_st->tcp_drc_recycle_t, drc->d_u.tcp.hk); odrc = opr_rbtree_lookup(&t->t, &drc->d_u.tcp.recycle_k); if (!odrc) { LogCrit(COMPONENT_DUPREQ, "BUG: asked to dequeue DRC not on queue"); } else { (void)opr_rbtree_remove( &t->t, &drc->d_u.tcp.recycle_k); } TAILQ_REMOVE(&drc_st->tcp_drc_recycle_q, drc, d_u.tcp.recycle_q); --(drc_st->tcp_drc_recycle_qlen); free_tcp_drc(drc); } else { LogFullDebug(COMPONENT_DUPREQ, "unexpired drc %p in recycle queue expire check (nothing happens)", drc); drc_st->last_expire_check = now; break; } } while (1); unlock: DRC_ST_UNLOCK(); } /** * @brief Find and reference a DRC to process the supplied svc_req. * * @param[in] req The svc_req being processed. * * @return The ref'd DRC if sucessfully located, else NULL. */ static /* inline */ drc_t * nfs_dupreq_get_drc(struct svc_req *req) { enum drc_type dtype = get_drc_type(req); drc_t *drc = NULL; bool drc_check_expired = false; switch (dtype) { case DRC_UDP_V234: LogFullDebug(COMPONENT_DUPREQ, "ref shared UDP DRC"); drc = &(drc_st->udp_drc); DRC_ST_LOCK(); (void)nfs_dupreq_ref_drc(drc); DRC_ST_UNLOCK(); goto out; retry: case DRC_TCP_V4: case DRC_TCP_V3: /* Idempotent address, no need for lock; * xprt will be valid as long as svc_req. */ drc = (drc_t *)req->rq_xprt->xp_u2; if (drc) { /* found, no danger of removal */ LogFullDebug(COMPONENT_DUPREQ, "ref DRC=%p for xprt=%p", drc, req->rq_xprt); PTHREAD_MUTEX_lock(&drc->mtx); /* LOCKED */ } else { drc_t drc_k; struct rbtree_x_part *t = NULL; struct opr_rbtree_node *ndrc = NULL; drc_t *tdrc = NULL; memset(&drc_k, 0, sizeof(drc_k)); drc_k.type = dtype; /* Since the drc can last longer than the xprt, * copy the address. Read operation of constant data, * no xprt lock required. */ (void)copy_xprt_addr(&drc_k.d_u.tcp.addr, req->rq_xprt); drc_k.d_u.tcp.hk = CityHash64WithSeed((char *)&drc_k.d_u.tcp.addr, sizeof(sockaddr_t), 911); { char str[SOCK_NAME_MAX]; sprint_sockaddr(&drc_k.d_u.tcp.addr, str, sizeof(str)); LogFullDebug(COMPONENT_DUPREQ, "get drc for addr: %s", str); } t = rbtx_partition_of_scalar(&drc_st->tcp_drc_recycle_t, drc_k.d_u.tcp.hk); DRC_ST_LOCK(); /* Avoid double reference of drc, * rechecking xp_u2 after DRC_ST_LOCK */ if (req->rq_xprt->xp_u2) { DRC_ST_UNLOCK(); goto retry; } ndrc = opr_rbtree_lookup(&t->t, &drc_k.d_u.tcp.recycle_k); if (ndrc) { /* reuse old DRC */ tdrc = opr_containerof(ndrc, drc_t, d_u.tcp.recycle_k); PTHREAD_MUTEX_lock(&tdrc->mtx); /* LOCKED */ /* If the refcnt is zero and it is not * in the recycle queue, wait for the * other thread to put it in the queue. */ if (tdrc->refcnt == 0) { if (!(tdrc->flags & DRC_FLAG_RECYCLE)) { PTHREAD_MUTEX_unlock( &tdrc->mtx); DRC_ST_UNLOCK(); goto retry; } TAILQ_REMOVE(&drc_st->tcp_drc_recycle_q, tdrc, d_u.tcp.recycle_q); --(drc_st->tcp_drc_recycle_qlen); tdrc->flags &= ~DRC_FLAG_RECYCLE; } drc = tdrc; LogFullDebug(COMPONENT_DUPREQ, "recycle TCP DRC=%p for xprt=%p", tdrc, req->rq_xprt); } if (!drc) { drc = alloc_tcp_drc(dtype); LogFullDebug(COMPONENT_DUPREQ, "alloc new TCP DRC=%p for xprt=%p", drc, req->rq_xprt); /* assign addr */ memcpy(&drc->d_u.tcp.addr, &drc_k.d_u.tcp.addr, sizeof(sockaddr_t)); /* assign already-computed hash */ drc->d_u.tcp.hk = drc_k.d_u.tcp.hk; PTHREAD_MUTEX_lock(&drc->mtx); /* LOCKED */ /* insert dict */ opr_rbtree_insert(&t->t, &drc->d_u.tcp.recycle_k); } /* Avoid double reference of drc, * setting xp_u2 under DRC_ST_LOCK */ req->rq_xprt->xp_u2 = (void *)drc; (void)nfs_dupreq_ref_drc(drc); /* xprt ref */ DRC_ST_UNLOCK(); drc->d_u.tcp.recycle_time = 0; /* try to expire unused DRCs somewhat in proportion to * new connection arrivals */ drc_check_expired = true; LogFullDebug(COMPONENT_DUPREQ, "after ref drc %p refcnt==%u ", drc, drc->refcnt); } break; default: /* XXX error */ break; } /* call path ref */ (void)nfs_dupreq_ref_drc(drc); PTHREAD_MUTEX_unlock(&drc->mtx); if (drc_check_expired) drc_free_expired(); out: return drc; } /** * @brief Release previously-ref'd DRC. * * Release previously-ref'd DRC. If its refcnt drops to 0, the DRC * is queued for later recycling. * * @param[in] drc The DRC * @param[in] flags Control flags */ void nfs_dupreq_put_drc(drc_t *drc, uint32_t flags) { if (!(flags & DRC_FLAG_LOCKED)) PTHREAD_MUTEX_lock(&drc->mtx); /* drc LOCKED */ if (drc->refcnt == 0) { LogCrit(COMPONENT_DUPREQ, "drc %p refcnt will underrun refcnt=%u", drc, drc->refcnt); } nfs_dupreq_unref_drc(drc); LogFullDebug(COMPONENT_DUPREQ, "drc %p refcnt==%u", drc, drc->refcnt); switch (drc->type) { case DRC_UDP_V234: /* do nothing */ break; case DRC_TCP_V4: case DRC_TCP_V3: if (drc->refcnt != 0) /* quick path */ break; /* note t's lock order wrt drc->mtx is the opposite of * drc->xt[*].lock. Drop and reacquire locks in correct * order. */ PTHREAD_MUTEX_unlock(&drc->mtx); DRC_ST_LOCK(); PTHREAD_MUTEX_lock(&drc->mtx); /* Since we dropped and reacquired the drc lock for the * correct lock order, we need to recheck the drc fields * again! */ if (drc->refcnt == 0 && !(drc->flags & DRC_FLAG_RECYCLE)) { drc->d_u.tcp.recycle_time = time(NULL); drc->flags |= DRC_FLAG_RECYCLE; TAILQ_INSERT_TAIL(&drc_st->tcp_drc_recycle_q, drc, d_u.tcp.recycle_q); ++(drc_st->tcp_drc_recycle_qlen); LogFullDebug(COMPONENT_DUPREQ, "enqueue drc %p for recycle", drc); } DRC_ST_UNLOCK(); break; default: break; }; PTHREAD_MUTEX_unlock(&drc->mtx); /* !LOCKED */ } /** * @brief Resolve indirect request function vector for the supplied DRC entry * * @param[in] dv The duplicate request entry. * * @return The function vector if successful, else NULL. */ static inline const nfs_function_desc_t *nfs_dupreq_func(dupreq_entry_t *dv) { const nfs_function_desc_t *func = NULL; if (dv->hin.rq_prog == NFS_program[P_NFS]) { switch (dv->hin.rq_vers) { #ifdef _USE_NFS3 case NFS_V3: func = &nfs3_func_desc[dv->hin.rq_proc]; break; #endif /* _USE_NFS3 */ case NFS_V4: func = &nfs4_func_desc[dv->hin.rq_proc]; break; default: /* not reached */ LogMajor(COMPONENT_DUPREQ, "NFS Protocol version %" PRIu32 " unknown", dv->hin.rq_vers); } } else if (dv->hin.rq_prog == NFS_program[P_MNT]) { switch (dv->hin.rq_vers) { case MOUNT_V1: func = &mnt1_func_desc[dv->hin.rq_proc]; break; case MOUNT_V3: func = &mnt3_func_desc[dv->hin.rq_proc]; break; default: /* not reached */ LogMajor(COMPONENT_DUPREQ, "MOUNT Protocol version %" PRIu32 " unknown", dv->hin.rq_vers); break; } #ifdef _USE_NLM } else if (dv->hin.rq_prog == NFS_program[P_NLM]) { switch (dv->hin.rq_vers) { case NLM4_VERS: func = &nlm4_func_desc[dv->hin.rq_proc]; break; } #endif /* _USE_NLM */ } else if (dv->hin.rq_prog == NFS_program[P_RQUOTA]) { switch (dv->hin.rq_vers) { case RQUOTAVERS: func = &rquota1_func_desc[dv->hin.rq_proc]; break; case EXT_RQUOTAVERS: func = &rquota2_func_desc[dv->hin.rq_proc]; break; } } else { /* not reached */ LogMajor(COMPONENT_DUPREQ, "protocol %" PRIu32 " is not managed", dv->hin.rq_prog); } return func; } /** * @brief Construct a duplicate request cache entry. * * Entries are allocated from the dupreq_pool. Since dupre_entry_t * presently contains an expanded nfs_arg_t, zeroing of at least corresponding * value pointers is required for XDR allocation. * * @return The newly allocated dupreq entry or NULL. */ static inline dupreq_entry_t *alloc_dupreq(void) { dupreq_entry_t *dv; dv = pool_alloc(dupreq_pool); gsh_mutex_init(&dv->mtx, NULL); TAILQ_INIT_ENTRY(dv, fifo_q); return dv; } /** * @brief Deep-free a duplicate request cache entry. * * If the entry has processed request data, the corresponding free * function is called on the result. The cache entry is then returned * to the dupreq_pool. */ static inline void nfs_dupreq_free_dupreq(dupreq_entry_t *dv) { const nfs_function_desc_t *func; assert(dv->refcnt == 0); LogDebug(COMPONENT_DUPREQ, "freeing dupreq entry dv=%p, dv xid=%" PRIu32 " cksum %" PRIu64 " state=%s", dv, dv->hin.tcp.rq_xid, dv->hk, dupreq_state_table[dv->state]); if (dv->res) { func = nfs_dupreq_func(dv); func->free_function(dv->res); free_nfs_res(dv->res); } PTHREAD_MUTEX_destroy(&dv->mtx); pool_free(dupreq_pool, dv); } /** * @brief get a ref count on dupreq_entry_t */ static inline void dupreq_entry_get(dupreq_entry_t *dv) { (void)atomic_inc_uint32_t(&dv->refcnt); } /** * @brief release a ref count on dupreq_entry_t * * The caller must not access dv any more after this call as it could be * freed here. */ static inline void dupreq_entry_put(dupreq_entry_t *dv) { int32_t refcnt; refcnt = atomic_dec_uint32_t(&dv->refcnt); /* If ref count is zero, no one should be accessing it other * than us. so no lock is needed. */ if (refcnt == 0) nfs_dupreq_free_dupreq(dv); } /** * @page DRC_RETIRE DRC request retire heuristic. * * We add a new, per-drc semphore like counter, retwnd. The value of * retwnd begins at 0, and is always >= 0. The value of retwnd is increased * when a a duplicate req cache hit occurs. If it was 0, it is increased by * some small constant, say, 16, otherwise, by 1. And retwnd decreases by 1 * when we successfully finish any request. Likewise in finish, a cached * request may be retired iff we are above our water mark, and retwnd is 0. */ #define RETWND_START_BIAS 16 /** * @brief advance retwnd. * * If (drc)->retwnd is 0, advance its value to RETWND_START_BIAS, else * increase its value by 2 (corrects to 1) iff !full. * * @param[in] drc The duplicate request cache */ #define drc_inc_retwnd(drc) \ do { \ if ((drc)->retwnd == 0) \ (drc)->retwnd = RETWND_START_BIAS; \ else \ if ((drc)->retwnd < (drc)->maxsize) \ (drc)->retwnd += 2; \ } while (0) /** * @brief conditionally decrement retwnd. * * If (drc)->retwnd > 0, decrease its value by 1. * * @param[in] drc The duplicate request cache */ #define drc_dec_retwnd(drc) \ do { \ if ((drc)->retwnd > 0) \ --((drc)->retwnd); \ } while (0) /** * @brief retire request predicate. * * Calculate whether a request may be retired from the provided duplicate * request cache. * * @param[in] drc The duplicate request cache * * @return true if a request may be retired, else false. */ static inline bool drc_should_retire(drc_t *drc) { /* do not exeed the hard bound on cache size */ if (unlikely(drc->size > drc->maxsize)) return true; /* otherwise, are we permitted to retire requests */ if (unlikely(drc->retwnd > 0)) return false; /* finally, retire if drc->size is above intended high water mark */ if (unlikely(drc->size > drc->hiwat)) return true; return false; } static inline bool nfs_dupreq_v4_cacheable(nfs_request_t *reqnfs) { COMPOUND4args *arg_c4 = (COMPOUND4args *)&reqnfs->arg_nfs; if (arg_c4->minorversion > 0) return false; if ((reqnfs->lookahead.flags & (NFS_LOOKAHEAD_CREATE))) /* override OPEN4_CREATE */ return true; if ((reqnfs->lookahead.flags & (NFS_LOOKAHEAD_OPEN | /* all logical OPEN */ NFS_LOOKAHEAD_CLOSE | NFS_LOOKAHEAD_LOCK | /* includes LOCKU */ NFS_LOOKAHEAD_READ | /* because large, though idempotent */ NFS_LOOKAHEAD_READLINK | NFS_LOOKAHEAD_READDIR))) return false; return true; } /** * @brief Start a duplicate request transaction * * Finds any matching request entry in the cache, if one exists, else * creates one in the START state. On any non-error return, the refcnt * of the corresponding entry is incremented. * * @param[in] reqnfs The NFS request data * @param[in] req The request to be cached * * @retval DUPREQ_SUCCESS if successful. * @retval DUPREQ_INSERT_MALLOC_ERROR if an error occured during insertion. */ dupreq_status_t nfs_dupreq_start(nfs_request_t *reqnfs, struct svc_req *req) { dupreq_status_t status = DUPREQ_SUCCESS; dupreq_entry_t *dv = NULL, *dk = NULL; drc_t *drc; enum drc_type dtype = get_drc_type(req); if (nfs_param.core_param.drc.disabled) goto no_cache; switch (dtype) { case DRC_TCP_V4: if (reqnfs->funcdesc->service_function == nfs4_Compound) { if (!nfs_dupreq_v4_cacheable(reqnfs)) { /* for such requests, we merely thread * the request through for later * cleanup--all v41 caching is handled * by the v41 slot reply cache */ goto no_cache; } } break; default: /* likewise for other protocol requests we may not or choose not * to cache */ if (!(reqnfs->funcdesc->dispatch_behaviour & CAN_BE_DUP)) goto no_cache; break; } drc = nfs_dupreq_get_drc(req); dk = alloc_dupreq(); dk->hin.drc = drc; /* trans. call path ref to dv */ switch (drc->type) { case DRC_TCP_V4: case DRC_TCP_V3: dk->hin.tcp.rq_xid = req->rq_msg.rm_xid; /* XXX needed? */ dk->hin.rq_prog = req->rq_msg.cb_prog; dk->hin.rq_vers = req->rq_msg.cb_vers; dk->hin.rq_proc = req->rq_msg.cb_proc; break; case DRC_UDP_V234: dk->hin.tcp.rq_xid = req->rq_msg.rm_xid; if (unlikely(!copy_xprt_addr(&dk->hin.addr, req->rq_xprt))) { nfs_dupreq_put_drc(drc, DRC_FLAG_NONE); nfs_dupreq_free_dupreq(dk); return DUPREQ_INSERT_MALLOC_ERROR; } dk->hin.rq_prog = req->rq_msg.cb_prog; dk->hin.rq_vers = req->rq_msg.cb_vers; dk->hin.rq_proc = req->rq_msg.cb_proc; break; default: /* @todo: should this be an assert? */ nfs_dupreq_put_drc(drc, DRC_FLAG_NONE); nfs_dupreq_free_dupreq(dk); return DUPREQ_INSERT_MALLOC_ERROR; } dk->hk = req->rq_cksum; /* TI-RPC computed checksum */ dk->state = DUPREQ_START; dk->timestamp = time(NULL); { struct opr_rbtree_node *nv; struct rbtree_x_part *t = rbtx_partition_of_scalar(&drc->xt, dk->hk); PTHREAD_MUTEX_lock(&t->mtx); /* partition lock */ nv = rbtree_x_cached_lookup(&drc->xt, t, &dk->rbt_k, dk->hk); if (nv) { /* cached request */ nfs_dupreq_free_dupreq(dk); dv = opr_containerof(nv, dupreq_entry_t, rbt_k); PTHREAD_MUTEX_lock(&dv->mtx); if (unlikely(dv->state == DUPREQ_START)) { status = DUPREQ_BEING_PROCESSED; } else { /* satisfy req from the DRC, incref, extend window */ req->rq_u1 = dv; reqnfs->res_nfs = req->rq_u2 = dv->res; status = DUPREQ_EXISTS; dupreq_entry_get(dv); } PTHREAD_MUTEX_unlock(&dv->mtx); if (status == DUPREQ_EXISTS) { PTHREAD_MUTEX_lock(&drc->mtx); drc_inc_retwnd(drc); PTHREAD_MUTEX_unlock(&drc->mtx); } LogDebug(COMPONENT_DUPREQ, "dupreq hit dv=%p, dv xid=%" PRIu32 " cksum %" PRIu64 " state=%s", dv, dv->hin.tcp.rq_xid, dv->hk, dupreq_state_table[dv->state]); } else { /* new request */ req->rq_u1 = dk; dk->res = alloc_nfs_res(); reqnfs->res_nfs = req->rq_u2 = dk->res; /* cache--can exceed drc->maxsize */ (void)rbtree_x_cached_insert(&drc->xt, t, &dk->rbt_k, dk->hk); /* dupreq ref count starts with 2; one for the caller * and another for staying in the hash table. */ dk->refcnt = 2; /* add to q tail */ PTHREAD_MUTEX_lock(&drc->mtx); TAILQ_INSERT_TAIL(&drc->dupreq_q, dk, fifo_q); ++(drc->size); PTHREAD_MUTEX_unlock(&drc->mtx); LogFullDebug(COMPONENT_DUPREQ, "starting dk=%p xid=%" PRIu32 " on DRC=%p state=%s, status=%s, refcnt=%d, drc->size=%d", dk, dk->hin.tcp.rq_xid, drc, dupreq_state_table[dk->state], dupreq_status_table[status], dk->refcnt, drc->size); } PTHREAD_MUTEX_unlock(&t->mtx); } return status; no_cache: req->rq_u1 = (void *)DUPREQ_NOCACHE; reqnfs->res_nfs = req->rq_u2 = alloc_nfs_res(); return DUPREQ_SUCCESS; } /** * @brief Completes a request in the cache * * Completes a cache insertion operation begun in nfs_dupreq_start. * The refcnt of the corresponding duplicate request entry is unchanged * (ie, the caller must still call nfs_dupreq_rele). * * In contrast with the prior DRC implementation, completing a request * in the current implementation may under normal conditions cause one * or more cached requests to be retired. Requests are retired in the * order they were inserted. The primary retire algorithm is a high * water mark, and a windowing heuristic. One or more requests will be * retired if the water mark/timeout is exceeded, and if a no duplicate * requests have been found in the cache in a configurable window of * immediately preceding requests. A timeout may supplement the water mark, * in future. * * req->rq_u1 has either a magic value, or points to a duplicate request * cache entry allocated in nfs_dupreq_start. * * @param[in] req The request * @param[in] res_nfs The response * * @return DUPREQ_SUCCESS if successful. * @return DUPREQ_INSERT_MALLOC_ERROR if an error occured. */ dupreq_status_t nfs_dupreq_finish(struct svc_req *req, nfs_res_t *res_nfs) { dupreq_entry_t *ov = NULL, *dv = (dupreq_entry_t *)req->rq_u1; dupreq_status_t status = DUPREQ_SUCCESS; struct rbtree_x_part *t; drc_t *drc = NULL; int16_t cnt = 0; /* do nothing if req is marked no-cache */ if (dv == (void *)DUPREQ_NOCACHE) goto out; PTHREAD_MUTEX_lock(&dv->mtx); dv->res = res_nfs; dv->timestamp = time(NULL); dv->state = DUPREQ_COMPLETE; drc = dv->hin.drc; PTHREAD_MUTEX_unlock(&dv->mtx); /* cond. remove from q head */ PTHREAD_MUTEX_lock(&drc->mtx); LogFullDebug(COMPONENT_DUPREQ, "completing dv=%p xid=%" PRIu32 " on DRC=%p state=%s, status=%s, refcnt=%d, drc->size=%d", dv, dv->hin.tcp.rq_xid, drc, dupreq_state_table[dv->state], dupreq_status_table[status], dv->refcnt, drc->size); /* (all) finished requests count against retwnd */ drc_dec_retwnd(drc); /* conditionally retire entries */ dq_again: if (drc_should_retire(drc)) { ov = TAILQ_FIRST(&drc->dupreq_q); if (likely(ov)) { /* remove dict entry */ t = rbtx_partition_of_scalar(&drc->xt, ov->hk); uint64_t ov_hk = ov->hk; /* Need to acquire partition lock, but the lock * order is partition lock followed by drc lock. * Drop drc lock and reacquire it! */ PTHREAD_MUTEX_unlock(&drc->mtx); PTHREAD_MUTEX_lock(&t->mtx); /* partition lock */ PTHREAD_MUTEX_lock(&drc->mtx); /* Since we dropped drc lock and reacquired it, * the drc dupreq list may have changed. Get the * dupreq entry from the list again. */ ov = TAILQ_FIRST(&drc->dupreq_q); /* Make sure that we are removing the entry we * expected (imperfect, but harmless). */ if (ov == NULL || ov->hk != ov_hk) { PTHREAD_MUTEX_unlock(&t->mtx); goto unlock; } /* remove q entry */ TAILQ_REMOVE(&drc->dupreq_q, ov, fifo_q); --(drc->size); /* release dv's ref */ nfs_dupreq_put_drc(drc, DRC_FLAG_LOCKED); /* drc->mtx gets unlocked in the above call! */ rbtree_x_cached_remove(&drc->xt, t, &ov->rbt_k, ov->hk); PTHREAD_MUTEX_unlock(&t->mtx); LogDebug(COMPONENT_DUPREQ, "retiring ov=%p xid=%" PRIu32 " on DRC=%p state=%s, status=%s, refcnt=%d", ov, ov->hin.tcp.rq_xid, ov->hin.drc, dupreq_state_table[dv->state], dupreq_status_table[status], ov->refcnt); /* release hashtable ref count */ dupreq_entry_put(ov); /* conditionally retire another */ if (cnt++ < DUPREQ_MAX_RETRIES) { PTHREAD_MUTEX_lock(&drc->mtx); goto dq_again; /* calls drc_should_retire() */ } goto out; } } unlock: PTHREAD_MUTEX_unlock(&drc->mtx); out: return status; } /** * * @brief Remove an entry (request) from a duplicate request cache. * * The expected pattern is that nfs_rpc_process_request shall delete requests * only in error conditions. The refcnt of the corresponding duplicate request * entry is unchanged (ie., the caller must still call nfs_dupreq_rele). * * We assert req->rq_u1 now points to the corresonding duplicate request * cache entry. * * @param[in] req The svc_req structure. * * @return DUPREQ_SUCCESS if successful. * */ dupreq_status_t nfs_dupreq_delete(struct svc_req *req) { dupreq_entry_t *dv = (dupreq_entry_t *)req->rq_u1; dupreq_status_t status = DUPREQ_SUCCESS; struct rbtree_x_part *t; drc_t *drc; /* do nothing if req is marked no-cache */ if (dv == (void *)DUPREQ_NOCACHE) goto out; PTHREAD_MUTEX_lock(&dv->mtx); drc = dv->hin.drc; dv->state = DUPREQ_DELETED; PTHREAD_MUTEX_unlock(&dv->mtx); LogFullDebug(COMPONENT_DUPREQ, "deleting dv=%p xid=%" PRIu32 " on DRC=%p state=%s, status=%s, refcnt=%d", dv, dv->hin.tcp.rq_xid, drc, dupreq_state_table[dv->state], dupreq_status_table[status], dv->refcnt); /* XXX dv holds a ref on drc */ t = rbtx_partition_of_scalar(&drc->xt, dv->hk); PTHREAD_MUTEX_lock(&t->mtx); rbtree_x_cached_remove(&drc->xt, t, &dv->rbt_k, dv->hk); PTHREAD_MUTEX_unlock(&t->mtx); PTHREAD_MUTEX_lock(&drc->mtx); TAILQ_REMOVE(&drc->dupreq_q, dv, fifo_q); --(drc->size); /* release dv's ref on drc and unlock */ nfs_dupreq_put_drc(drc, DRC_FLAG_LOCKED); /* !LOCKED */ /* we removed the dupreq from hashtable, release a ref */ dupreq_entry_put(dv); out: return status; } /** * @brief Decrement the call path refcnt on a cache entry. * * We assert req->rq_u1 now points to the corresonding duplicate request * cache entry (dv). * * @param[in] req The svc_req structure. * @param[in] func The function descriptor for this request type */ void nfs_dupreq_rele(struct svc_req *req, const nfs_function_desc_t *func) { dupreq_entry_t *dv = (dupreq_entry_t *) req->rq_u1; /* no-cache cleanup */ if (dv == (void *)DUPREQ_NOCACHE) { LogFullDebug(COMPONENT_DUPREQ, "releasing no-cache res %p", req->rq_u2); func->free_function(req->rq_u2); free_nfs_res(req->rq_u2); goto out; } LogFullDebug(COMPONENT_DUPREQ, "releasing dv=%p xid=%" PRIu32 " on DRC=%p state=%s, refcnt=%d", dv, dv->hin.tcp.rq_xid, dv->hin.drc, dupreq_state_table[dv->state], dv->refcnt); dupreq_entry_put(dv); out: /* dispose RPC header */ if (req->rq_auth) SVCAUTH_RELEASE(req); } /** * @brief Shutdown the dupreq2 package. */ void dupreq2_pkgshutdown(void) { /* XXX do nothing */ } nfs-ganesha-2.6.0/src/RPCAL/rpc_tools.c000066400000000000000000000245061324272410200175150ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file rpc_tools.c * @brief Some tools very usefull in the nfs protocol implementation. * */ #include "config.h" #include #include #ifdef RPC_VSOCK #include #endif /* VSOCK */ #include #include /* for having isalnum */ #include /* for having atoi */ #include /* for having MAXNAMLEN */ #include #include #include #include #include #include #include /* for having FNDELAY */ #include #include #include "hashtable.h" #include "log.h" #include "nfs_core.h" #include "nfs23.h" #include "nfs4.h" #include "fsal.h" #include "nfs_exports.h" #include "nfs_file_handle.h" #include "nfs_dupreq.h" /* XXX doesn't ntirpc have an equivalent for all of the following? */ const char *str_sock_type(int st) { static char buf[16]; switch (st) { case SOCK_STREAM: return "SOCK_STREAM"; case SOCK_DGRAM: return "SOCK_DGRAM "; case SOCK_RAW: return "SOCK_RAW "; } sprintf(buf, "%d", st); return buf; } const char *str_ip_proto(int p) { static char buf[16]; switch (p) { case IPPROTO_IP: return "IPPROTO_IP "; case IPPROTO_TCP: return "IPPROTO_TCP"; case IPPROTO_UDP: return "IPPROTO_UDP"; } sprintf(buf, "%d", p); return buf; } const char *str_af(int af) { static char buf[16]; switch (af) { case AF_INET: return "AF_INET "; case AF_INET6: return "AF_INET6"; #ifdef RPC_VSOCK case AF_VSOCK: return "AF_VSOCK"; #endif /* VSOCK */ } sprintf(buf, "%d", af); return buf; } const char *xprt_type_to_str(xprt_type_t type) { switch (type) { case XPRT_UNKNOWN: return "UNKNOWN"; case XPRT_NON_RENDEZVOUS: return "UNUSED"; case XPRT_UDP: return "udp"; case XPRT_UDP_RENDEZVOUS: return "udp rendezvous"; case XPRT_TCP: return "tcp"; case XPRT_TCP_RENDEZVOUS: return "tcp rendezvous"; case XPRT_SCTP: return "sctp"; case XPRT_SCTP_RENDEZVOUS: return "sctp rendezvous"; case XPRT_RDMA: return "rdma"; case XPRT_RDMA_RENDEZVOUS: return "rdma rendezvous"; case XPRT_VSOCK: return "vsock"; case XPRT_VSOCK_RENDEZVOUS: return "vsock rendezvous"; } return "INVALID"; } /** * @brief Copy transport address into an address field * * @param[out] addr Address field to fill in. * @param[in] xprt Transport to get address from. * * @retval true if okay. * @retval false if not. */ bool copy_xprt_addr(sockaddr_t *addr, SVCXPRT *xprt) { struct netbuf *phostaddr = svc_getcaller_netbuf(xprt); if (phostaddr->len > sizeof(sockaddr_t) || phostaddr->buf == NULL) return 0; memcpy(addr, phostaddr->buf, phostaddr->len); return 1; } /** * @brief Create a hash value based on the sockaddr_t structure * * This creates a native pointer size (unsigned long int) hash value * from the sockaddr_t structure. It supports both IPv4 and IPv6, * other types can be added in time. * * XXX is this hash...good? * * @param[in] addr sockaddr_t address to hash * @param[in] ignore_port Whether to ignore the port * * @return hash value * */ uint64_t hash_sockaddr(sockaddr_t *addr, bool ignore_port) { unsigned long addr_hash = 0; int port; switch (addr->ss_family) { case AF_INET: { struct sockaddr_in *paddr = (struct sockaddr_in *)addr; addr_hash = paddr->sin_addr.s_addr; if (!ignore_port) { port = paddr->sin_port; addr_hash ^= (port << 16); } break; } case AF_INET6: { struct sockaddr_in6 *paddr = (struct sockaddr_in6 *)addr; uint32_t *va; va = (uint32_t *)&paddr->sin6_addr; addr_hash = va[0] ^ va[1] ^ va[2] ^ va[3]; if (!ignore_port) { port = paddr->sin6_port; addr_hash ^= (port << 16); } break; } #ifdef RPC_VSOCK case AF_VSOCK: { struct sockaddr_vm *svm; /* XXX checkpatch horror */ svm = (struct sockaddr_vm *) addr; addr_hash = svm->svm_cid; if (!ignore_port) addr_hash ^= svm->svm_port; } #endif /* VSOCK */ default: break; } return addr_hash; } int display_sockaddr(struct display_buffer *dspbuf, sockaddr_t *addr) { const char *name = NULL; char ipname[SOCK_NAME_MAX]; int port = 0; int b_left = display_start(dspbuf); if (b_left <= 0) return b_left; switch (addr->ss_family) { case AF_INET: name = inet_ntop(addr->ss_family, &(((struct sockaddr_in *)addr)->sin_addr), ipname, sizeof(ipname)); port = ntohs(((struct sockaddr_in *)addr)->sin_port); break; case AF_INET6: name = inet_ntop(addr->ss_family, &(((struct sockaddr_in6 *)addr)->sin6_addr), ipname, sizeof(ipname)); port = ntohs(((struct sockaddr_in6 *)addr)->sin6_port); break; case AF_LOCAL: return display_cat(dspbuf, ((struct sockaddr_un *)addr)->sun_path); } if (name == NULL) return display_cat(dspbuf, ""); else return display_printf(dspbuf, "%s:%d", name, port); } int sprint_sockip(sockaddr_t *addr, char *buf, int len) { const char *name = NULL; memset(buf, 0, len); switch (addr->ss_family) { case AF_INET: name = inet_ntop(addr->ss_family, &(((struct sockaddr_in *)addr)->sin_addr), buf, len); break; case AF_INET6: name = inet_ntop(addr->ss_family, &(((struct sockaddr_in6 *)addr)->sin6_addr), buf, len); break; case AF_LOCAL: strncpy(buf, ((struct sockaddr_un *)addr)->sun_path, len); name = buf; } if (name == NULL) { strncpy(buf, "", len); return 0; } return 1; } /** * * @brief Compare 2 sockaddrs, including ports * * @param[in] addr_1 First address * @param[in] addr_2 Second address * @param[in] ignore_port Whether to ignore the port * * @return Comparator trichotomy, */ int cmp_sockaddr(sockaddr_t *addr_1, sockaddr_t *addr_2, bool ignore_port) { if (addr_1->ss_family != addr_2->ss_family) return 0; switch (addr_1->ss_family) { case AF_INET: { struct sockaddr_in *inaddr1 = (struct sockaddr_in *)addr_1; struct sockaddr_in *inaddr2 = (struct sockaddr_in *)addr_2; return (inaddr1->sin_addr.s_addr == inaddr2->sin_addr.s_addr && (ignore_port || inaddr1->sin_port == inaddr2->sin_port)); } case AF_INET6: { struct sockaddr_in6 *ip6addr1 = (struct sockaddr_in6 *)addr_1; struct sockaddr_in6 *ip6addr2 = (struct sockaddr_in6 *)addr_2; return (memcmp (ip6addr1->sin6_addr.s6_addr, ip6addr2->sin6_addr.s6_addr, sizeof(ip6addr2->sin6_addr.s6_addr)) == 0) && (ignore_port || ip6addr1->sin6_port == ip6addr2->sin6_port); } default: return 0; } } /** * @brief Canonically compare 2 sockaddrs * * @param[in] addr1 First address * @param[in] addr2 Second address * @param[in] ignore_port Whether to ignore the port * * @return Comparator trichotomy */ int sockaddr_cmpf(sockaddr_t *addr1, sockaddr_t *addr2, bool ignore_port) { switch (addr1->ss_family) { case AF_INET: { struct sockaddr_in *in1 = (struct sockaddr_in *)addr1; struct sockaddr_in *in2 = (struct sockaddr_in *)addr2; if (in1->sin_addr.s_addr < in2->sin_addr.s_addr) return -1; if (in1->sin_addr.s_addr == in2->sin_addr.s_addr) { if (ignore_port) return 0; /* else */ if (in1->sin_port < in2->sin_port) return -1; if (in1->sin_port == in2->sin_port) return 0; return 1; } return 1; } case AF_INET6: { struct sockaddr_in6 *in1 = (struct sockaddr_in6 *)addr1; struct sockaddr_in6 *in2 = (struct sockaddr_in6 *)addr2; int acmp = memcmp(in1->sin6_addr.s6_addr, in2->sin6_addr.s6_addr, sizeof(struct in6_addr)); if (acmp == 0) { if (ignore_port) return 0; /* else */ if (in1->sin6_port < in2->sin6_port) return -1; if (in1->sin6_port == in2->sin6_port) return 0; return 1; } else return acmp < 0 ? -1 : 1; } default: /* unhandled AF */ return -2; } } in_addr_t get_in_addr(sockaddr_t *addr) { if (addr->ss_family == AF_INET) return ((struct sockaddr_in *)addr)->sin_addr.s_addr; else return 0; } int get_port(sockaddr_t *addr) { switch (addr->ss_family) { case AF_INET: return ntohs(((struct sockaddr_in *)addr)->sin_port); case AF_INET6: return ntohs(((struct sockaddr_in6 *)addr)->sin6_port); #ifdef RPC_VSOCK case AF_VSOCK: return ((struct sockaddr_vm *)addr)->svm_port; #endif /* VSOCK */ default: return -1; } } int ipstring_to_sockaddr(const char *str, sockaddr_t *addr) { struct addrinfo *info, hints, *p; int rc; char ipname[SOCK_NAME_MAX + 1]; memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG | AI_NUMERICHOST; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_RAW; hints.ai_protocol = 0; rc = getaddrinfo(str, NULL, &hints, &info); if (rc == 0 && info != NULL) { p = info; if (isFullDebug(COMPONENT_RPC)) { while (p != NULL) { sprint_sockip((sockaddr_t *) p->ai_addr, ipname, sizeof(ipname)); LogFullDebug(COMPONENT_RPC, "getaddrinfo %s returned %s family=%s socktype=%s protocol=%s", str, ipname, str_af(p->ai_family), str_sock_type(p->ai_socktype), str_ip_proto(p->ai_protocol)); p = p->ai_next; } } memcpy(addr, info->ai_addr, info->ai_addrlen); freeaddrinfo(info); } else { switch (rc) { case EAI_SYSTEM: LogFullDebug(COMPONENT_RPC, "getaddrinfo %s returned %d(%s)", str, errno, strerror(errno)); break; default: LogFullDebug(COMPONENT_RPC, "getaddrinfo %s returned %d(%s)", str, rc, gai_strerror(rc)); } } return rc; } nfs-ganesha-2.6.0/src/ReleaseNote000066400000000000000000000007231324272410200166300ustar00rootroot000000000000000.99.58 - Mon 14 Sep 2009 - Use SOMAXCON in listen - XDR modules now support NFSv4.1 protocol, the rest is to be implemented - Added nfs41_op_exchange_id, nfs41_op_create_session, nfs41_op_sequence, nfs41_op_lock, nfs41_op_locku, nfs41_op_lockt - Added early support for nfs41_op_create_session, nfs41_op_exchange_id, nfs41_op_destroy_session - command like 'mount -t nfs4 -o minorversion=1 ..' is now possible but basic connectathon is not fully ok nfs-ganesha-2.6.0/src/SAL/000077500000000000000000000000001324272410200151145ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/SAL/9p_owner.c000066400000000000000000000156641324272410200170360ustar00rootroot00000000000000/* * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup SAL State abstraction layer * @{ */ /** * @file 9p_owner.c * @brief Management of the 9P owner cache. */ #include "config.h" #include #include #include #include "log.h" #include "hashtable.h" #include "nfs_core.h" #include "sal_functions.h" /** * @brief Hash table for 9p owners */ hash_table_t *ht_9p_owner; /** * @brief Display a 9p owner * * @param[in] key The 9P owner * @param[out] str Output buffer * * @return the bytes remaining in the buffer. */ int display_9p_owner(struct display_buffer *dspbuf, state_owner_t *owner) { int b_left; if (owner == NULL) return display_printf(dspbuf, ""); b_left = display_printf(dspbuf, "STATE_LOCK_OWNER_9P %p", owner); if (b_left <= 0) return b_left; b_left = display_sockaddr(dspbuf, &owner->so_owner.so_9p_owner.client_addr); if (b_left <= 0) return b_left; b_left = display_printf(dspbuf, " proc_id=%u", owner->so_owner.so_9p_owner.proc_id); if (b_left <= 0) return b_left; return display_printf(dspbuf, " refcount=%d", atomic_fetch_int32_t(&owner->so_refcount)); } /** * @brief Display owner from hash key * * @param[in] buff Buffer pointing to owner * @param[out] str Output buffer * * @return Length of display string. */ int display_9p_owner_key(struct gsh_buffdesc *buff, char *str) { struct display_buffer dspbuf = {HASHTABLE_DISPLAY_STRLEN, str, str}; display_9p_owner(&dspbuf, buff->addr); return display_buffer_len(&dspbuf); } /** * @brief Display owner from hash value * * @param[in] buff Buffer pointing to owner * @param[out] str Output buffer * * @return Length of display string. */ int display_9p_owner_val(struct gsh_buffdesc *buff, char *str) { struct display_buffer dspbuf = {HASHTABLE_DISPLAY_STRLEN, str, str}; display_9p_owner(&dspbuf, buff->addr); return display_buffer_len(&dspbuf); } /** * @brief Compare two 9p owners * * @param[in] owner1 One owner * @param[in] owner2 Another owner * * @retval 1 if they differ. * @retval 0 if they're identical. */ int compare_9p_owner(state_owner_t *owner1, state_owner_t *owner2) { if (isFullDebug(COMPONENT_STATE) && isDebug(COMPONENT_HASHTABLE)) { char str1[LOG_BUFF_LEN / 2] = "\0"; char str2[LOG_BUFF_LEN / 2] = "\0"; struct display_buffer dspbuf1 = {sizeof(str1), str1, str1}; struct display_buffer dspbuf2 = {sizeof(str2), str2, str2}; display_9p_owner(&dspbuf1, owner1); display_9p_owner(&dspbuf2, owner2); LogFullDebug(COMPONENT_STATE, "{%s} vs {%s}", str1, str2); } if (owner1 == NULL || owner2 == NULL) return 1; if (owner1 == owner2) return 0; if (owner1->so_owner.so_9p_owner.proc_id != owner2->so_owner.so_9p_owner.proc_id) return 1; #if 0 if (memcmp (&owner1->so_owner.so_9p_owner.client_addr, &owner2->so_owner.so_9p_owner.client_addr, sizeof(struct sockaddr_storage))) return 1; #endif /* so_owner_len is always 0, don't compare so_owner_val */ return 0; } /** * @brief Compare two keys in the 9p owner hash table * * @param[in] buff1 One key * @param[in] buff2 Another key * * @retval 1 if they differ. * @retval 0 if they're the same. */ int compare_9p_owner_key(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2) { return compare_9p_owner(buff1->addr, buff2->addr); } /** * @brief Get the hash index from a 9p owner * * @param[in] hparam Hash parameters * @param[in] key The key to hash * * @return The hash index. */ uint32_t _9p_owner_value_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { unsigned long res; state_owner_t *pkey = key->addr; struct sockaddr_in *paddr = (struct sockaddr_in *)&pkey->so_owner.so_9p_owner.client_addr; /* so_owner_len is always zero so don't bother with so_owner_val */ /** @todo using sin_addr.s_addr as an int makes this only work for * IPv4. */ res = (unsigned long)(pkey->so_owner.so_9p_owner.proc_id) + (unsigned long)paddr->sin_addr.s_addr; if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_STATE, "value = %lu", res % hparam->index_size); return (uint32_t)(res % hparam->index_size); } /** * @brief Get the RBT hash from a 9p owner * * @param[in] hparam Hash parameters * @param[in] key The key to hash * * @return The RBT hash. */ uint64_t _9p_owner_rbt_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { uint64_t res; state_owner_t *pkey = key->addr; struct sockaddr_in *paddr = (struct sockaddr_in *)&pkey->so_owner.so_9p_owner.client_addr; /* so_owner_len is always zero so don't bother with so_owner_val */ /** @todo using sin_addr.s_addr as an int makes this only work for * IPv4. */ res = (uint64_t)(pkey->so_owner.so_9p_owner.proc_id) + (uint64_t)paddr->sin_addr.s_addr; if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_STATE, "rbt = %" PRIu64, res); return res; } static hash_parameter_t _9p_owner_hash_param = { .index_size = PRIME_STATE, .hash_func_key = _9p_owner_value_hash_func, .hash_func_rbt = _9p_owner_rbt_hash_func, .compare_key = compare_9p_owner_key, .key_to_str = display_9p_owner_key, .val_to_str = display_9p_owner_val, .flags = HT_FLAG_NONE, }; /** * @brief Init the hashtable for 9P Owner cache * * @retval 0 if successful. * @retval -1 otherwise. */ int Init_9p_hash(void) { ht_9p_owner = hashtable_init(&_9p_owner_hash_param); if (ht_9p_owner == NULL) { LogCrit(COMPONENT_STATE, "Cannot init 9P Owner cache"); return -1; } return 0; } /** * @brief Look up a 9p owner * * @param[in] client_addr 9p client address * @param[in] proc_id Process ID of owning process * * @return The found owner or NULL. */ state_owner_t *get_9p_owner(struct sockaddr_storage *client_addr, uint32_t proc_id) { state_owner_t key; memset(&key, 0, sizeof(key)); key.so_type = STATE_LOCK_OWNER_9P; key.so_refcount = 1; key.so_owner.so_9p_owner.proc_id = proc_id; memcpy(&key.so_owner.so_9p_owner.client_addr, client_addr, sizeof(*client_addr)); return get_state_owner(CARE_ALWAYS, &key, NULL, NULL); } /** @} */ nfs-ganesha-2.6.0/src/SAL/CMakeLists.txt000066400000000000000000000016511324272410200176570ustar00rootroot00000000000000########### next target ############### SET(sal_STAT_SRCS state_async.c state_lock.c state_share.c state_misc.c state_layout.c state_deleg.c nfs4_clientid.c nfs4_state.c nfs4_state_id.c nfs4_lease.c nfs4_recovery.c nfs41_session_id.c nfs4_owner.c recovery/recovery_fs.c recovery/recovery_fs_ng.c ) if(USE_NLM) set(sal_STAT_SRCS ${sal_STAT_SRCS} nlm_owner.c nlm_state.c ) endif(USE_NLM) if(USE_9P) set(sal_STAT_SRCS ${sal_STAT_SRCS} 9p_owner.c ) endif(USE_9P) if(USE_RADOS_RECOV) set(sal_STAT_SRCS ${sal_STAT_SRCS} recovery/recovery_rados_kv.c recovery/recovery_rados_ng.c ) endif(USE_RADOS_RECOV) add_library(sal STATIC ${sal_STAT_SRCS}) add_sanitizers(sal) if(USE_RADOS_RECOV) include_directories(${RADOS_INCLUDE_DIR}) target_link_libraries(sal ${RADOS_LIBRARIES}) endif(USE_RADOS_RECOV) ########### install files ############### nfs-ganesha-2.6.0/src/SAL/nfs41_session_id.c000066400000000000000000000233271324272410200204410ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup SAL State abstraction layer * @{ */ /** * @file nfs41_session_id.c * @brief The management of the session id cache. */ #include "config.h" #include "nfs_core.h" #include "nfs_proto_functions.h" #include "sal_functions.h" /** * @brief Pool for allocating session data */ pool_t *nfs41_session_pool; /** * @param Session ID hash */ hash_table_t *ht_session_id; /** * @param counter for creating session IDs. */ uint64_t global_sequence; /** * @brief Display a session ID * * @param[in/out] dspbuf display_buffer describing output string * @param[in] session_id The session ID * * @return the bytes remaining in the buffer. */ int display_session_id(struct display_buffer *dspbuf, char *session_id) { int b_left = display_cat(dspbuf, "sessionid="); if (b_left > 0) b_left = display_opaque_value(dspbuf, session_id, NFS4_SESSIONID_SIZE); return b_left; } /** * @brief Display a key in the session ID table * * @param[in] buff The key to display * @param[out] str Displayed key * * @return Length of output string. */ int display_session_id_key(struct gsh_buffdesc *buff, char *str) { struct display_buffer dspbuf = {HASHTABLE_DISPLAY_STRLEN, str, str}; display_session_id(&dspbuf, buff->addr); return display_buffer_len(&dspbuf); } /** * @brief Display a session object * * @param[in] buff The key to display * @param[in] session The session to display * * @return Length of output string. */ int display_session(struct display_buffer *dspbuf, nfs41_session_t *session) { int b_left = display_printf(dspbuf, "session %p {", session); if (b_left > 0) b_left = display_session_id(dspbuf, session->session_id); if (b_left > 0) b_left = display_cat(dspbuf, "}"); return b_left; } /** * @brief Display a value in the session ID table * * @param[in] buff The value to display * @param[out] str Displayed value * * @return Length of output string. */ int display_session_id_val(struct gsh_buffdesc *buff, char *str) { struct display_buffer dspbuf = {HASHTABLE_DISPLAY_STRLEN, str, str}; display_session(&dspbuf, buff->addr); return display_buffer_len(&dspbuf); } /** * @brief Compare two session IDs in the hash table * * @retval 0 if they are equal. * @retval 1 if they are not. */ int compare_session_id(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2) { return memcmp(buff1->addr, buff2->addr, NFS4_SESSIONID_SIZE); } /** * @brief Hash index of a sessionid * * @param[in] hparam Hash table parameters * @param[in] key The session key * * @return The hash index of the key. */ uint32_t session_id_value_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { /* Only need to take the mod of the global counter portion since it is unique */ uint64_t *counter = key->addr + sizeof(clientid4); return *counter % hparam->index_size; } /** * @brief RBT hash of a sessionid * * @param[in] hparam Hash table parameters * @param[in] key The session key * * @return The RBT hash of the key. */ uint64_t session_id_rbt_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { /* Only need to return the global counter portion since it is unique */ uint64_t *counter = key->addr + sizeof(clientid4); return *counter; } static hash_parameter_t session_id_param = { .index_size = PRIME_STATE, .hash_func_key = session_id_value_hash_func, .hash_func_rbt = session_id_rbt_hash_func, .ht_log_component = COMPONENT_SESSIONS, .compare_key = compare_session_id, .key_to_str = display_session_id_key, .val_to_str = display_session_id_val, .flags = HT_FLAG_CACHE, }; /** * @brief Init the hashtable for Session Id cache. * * @retval 0 if successful. * @retval -1 otherwise * */ int nfs41_Init_session_id(void) { ht_session_id = hashtable_init(&session_id_param); if (ht_session_id == NULL) { LogCrit(COMPONENT_SESSIONS, "NFS SESSION_ID: Cannot init Session Id cache"); return -1; } return 0; } /** * @brief Build a sessionid from a clientid * * @param[in] clientid Pointer to the related clientid * @param[out] sessionid The sessionid */ void nfs41_Build_sessionid(clientid4 *clientid, char *sessionid) { uint64_t seq; seq = atomic_inc_uint64_t(&global_sequence); memset(sessionid, 0, NFS4_SESSIONID_SIZE); memcpy(sessionid, clientid, sizeof(clientid4)); memcpy(sessionid + sizeof(clientid4), &seq, sizeof(seq)); } int32_t inc_session_ref(nfs41_session_t *session) { int32_t refcnt = atomic_inc_int32_t(&session->refcount); return refcnt; } int32_t dec_session_ref(nfs41_session_t *session) { int i; int32_t refcnt = atomic_dec_int32_t(&session->refcount); if (refcnt == 0) { /* Unlink the session from the client's list of sessions */ PTHREAD_MUTEX_lock(&session->clientid_record->cid_mutex); glist_del(&session->session_link); PTHREAD_MUTEX_unlock(&session->clientid_record->cid_mutex); /* Decrement our reference to the clientid record */ dec_client_id_ref(session->clientid_record); /* Destroy this session's mutexes and condition variable */ for (i = 0; i < session->nb_slots; i++) { nfs41_session_slot_t *slot; slot = &session->fc_slots[i]; PTHREAD_MUTEX_destroy(&slot->lock); if (slot->cached_result.res_cached) { slot->cached_result.res_cached = false; nfs4_Compound_Free((nfs_res_t *) &slot->cached_result); } } PTHREAD_COND_destroy(&session->cb_cond); PTHREAD_MUTEX_destroy(&session->cb_mutex); /* Destroy the session's back channel (if any) */ if (session->flags & session_bc_up) nfs_rpc_destroy_chan(&session->cb_chan); /* Free the slot tables */ gsh_free(session->fc_slots); gsh_free(session->bc_slots); /* Free the memory for the session */ pool_free(nfs41_session_pool, session); } return refcnt; } /** * @brief Set a session into the session hashtable. * * @param[in] sessionid Sessionid to add * @param[in] session_data Session data to add * * @retval 1 if successful. * @retval 0 otherwise. * */ int nfs41_Session_Set(nfs41_session_t *session_data) { struct gsh_buffdesc key; struct gsh_buffdesc val; struct hash_latch latch; hash_error_t code; int rc = 0; key.addr = session_data->session_id; key.len = NFS4_SESSIONID_SIZE; val.addr = session_data; val.len = sizeof(nfs41_session_t); /* The latch idiom isn't strictly necessary here */ code = hashtable_getlatch(ht_session_id, &key, &val, true, &latch); if (code == HASHTABLE_SUCCESS) { hashtable_releaselatched(ht_session_id, &latch); goto out; } if (code == HASHTABLE_ERROR_NO_SUCH_KEY) { /* nfs4_op_create_session ensures refcount == 2 for new * session records */ code = hashtable_setlatched(ht_session_id, &key, &val, &latch, false, NULL, NULL); if (code == HASHTABLE_SUCCESS) rc = 1; } out: return rc; } /** * @brief Get a pointer to a session from the session hashtable * * @param[in] sessionid The sessionid to look up * @param[out] session_data The associated session data * * @retval 1 if successful. * @retval 0 otherwise. */ int nfs41_Session_Get_Pointer(char sessionid[NFS4_SESSIONID_SIZE], nfs41_session_t **session_data) { struct gsh_buffdesc key; struct gsh_buffdesc val; struct hash_latch latch; char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; bool str_valid = false; hash_error_t code; if (isFullDebug(COMPONENT_SESSIONS)) { display_session_id(&dspbuf, sessionid); LogFullDebug(COMPONENT_SESSIONS, "Get Session %s", str); str_valid = true; } key.addr = sessionid; key.len = NFS4_SESSIONID_SIZE; code = hashtable_getlatch(ht_session_id, &key, &val, false, &latch); if (code != HASHTABLE_SUCCESS) { hashtable_releaselatched(ht_session_id, &latch); if (str_valid) LogFullDebug(COMPONENT_SESSIONS, "Session %s Not Found", str); return 0; } *session_data = val.addr; inc_session_ref(*session_data); /* XXX more locks? */ hashtable_releaselatched(ht_session_id, &latch); if (str_valid) LogFullDebug(COMPONENT_SESSIONS, "Session %s Found", str); return 1; } /** * @brief Remove a session from the session hashtable. * * This also shuts down any back channel and frees the session data. * * @param[in] sessionid The sessionid to remove * * @return 1 if successful. * @retval 0 otherwise. */ int nfs41_Session_Del(char sessionid[NFS4_SESSIONID_SIZE]) { struct gsh_buffdesc key, old_key, old_value; key.addr = sessionid; key.len = NFS4_SESSIONID_SIZE; if (HashTable_Del(ht_session_id, &key, &old_key, &old_value) == HASHTABLE_SUCCESS) { nfs41_session_t *session = old_value.addr; /* unref session */ dec_session_ref(session); return true; } else { return false; } } /** * @brief Display the content of the session hashtable */ void nfs41_Session_PrintAll(void) { hashtable_log(COMPONENT_SESSIONS, ht_session_id); } /** @} */ nfs-ganesha-2.6.0/src/SAL/nfs4_clientid.c000066400000000000000000001325321324272410200200130ustar00rootroot00000000000000/* * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup SAL State abstraction layer * @{ */ /** * @file nfs4_clientid.c * @brief The management of the client id cache. * */ #include "config.h" #include #include "hashtable.h" #include "log.h" #include "nfs_core.h" #include "nfs_exports.h" #include "config_parsing.h" #include #include #include #include #include "nfs4.h" #include "fsal.h" #include "sal_functions.h" #include "abstract_atomic.h" #include "city.h" #include "client_mgr.h" /** * @brief Hashtable used to cache NFSv4 clientids */ hash_table_t *ht_client_record; /** * @brief Hash table to store confirmed client IDs */ hash_table_t *ht_confirmed_client_id; /** * @brief Hash table to store unconfirmed client IDs */ hash_table_t *ht_unconfirmed_client_id; /** * @brief Counter to create clientids */ uint32_t clientid_counter; /** * @brief Verifier to construct clientids */ uint64_t clientid_verifier; /** * @brief Pool for client data structures */ pool_t *client_id_pool; /** * @brief Return the NFSv4 status for the client id error code * * @param[in] err Client id error code * * @return the corresponding nfs4 error code */ nfsstat4 clientid_error_to_nfsstat(clientid_status_t err) { switch (err) { case CLIENT_ID_SUCCESS: return NFS4_OK; case CLIENT_ID_INSERT_MALLOC_ERROR: return NFS4ERR_RESOURCE; case CLIENT_ID_INVALID_ARGUMENT: return NFS4ERR_SERVERFAULT; case CLIENT_ID_EXPIRED: return NFS4ERR_EXPIRED; case CLIENT_ID_STALE: return NFS4ERR_STALE_CLIENTID; } LogCrit(COMPONENT_CLIENTID, "Unexpected clientid error %d", err); return NFS4ERR_SERVERFAULT; } /** * @brief Return the NFSv4 status string for the client id error code * * @param[in] err client id error code * * @return the error string corresponding nfs4 error code */ const char *clientid_error_to_str(clientid_status_t err) { switch (err) { case CLIENT_ID_SUCCESS: return "CLIENT_ID_SUCCESS"; case CLIENT_ID_INSERT_MALLOC_ERROR: return "CLIENT_ID_INSERT_MALLOC_ERROR"; case CLIENT_ID_INVALID_ARGUMENT: return "CLIENT_ID_INVALID_ARGUMENT"; case CLIENT_ID_EXPIRED: return "CLIENT_ID_EXPIRED"; case CLIENT_ID_STALE: return "CLIENT_ID_STALE"; } LogCrit(COMPONENT_CLIENTID, "Unexpected clientid error %d", err); return "UNEXPECTED ERROR"; } /** * @brief Return a string corresponding to the confirm state * * @param[in] confirmed Confirm state * * @return Corresponding string. */ const char *clientid_confirm_state_to_str(nfs_clientid_confirm_state_t confirmed) { switch (confirmed) { case CONFIRMED_CLIENT_ID: return "CONFIRMED"; case UNCONFIRMED_CLIENT_ID: return "UNCONFIRMED"; case EXPIRED_CLIENT_ID: return "EXPIRED"; case STALE_CLIENT_ID: return "STALE"; } return "UNKNOWN STATE"; } /** * @brief Display a client record * * @param[in] clientid Client record * @param[out] str Output buffer * * @return Length of display string. */ int display_client_id_rec(struct display_buffer *dspbuf, nfs_client_id_t *clientid) { int delta; int b_left = display_printf(dspbuf, "%p ClientID={", clientid); if (b_left <= 0) return b_left; b_left = display_clientid(dspbuf, clientid->cid_clientid); if (b_left <= 0) return b_left; b_left = display_printf( dspbuf, "} %s Client={", clientid_confirm_state_to_str(clientid->cid_confirmed)); if (b_left <= 0) return b_left; if (clientid->cid_client_record != NULL) { b_left = display_client_record(dspbuf, clientid->cid_client_record); if (b_left <= 0) return b_left; } if (clientid->cid_lease_reservations > 0) delta = 0; else delta = time(NULL) - clientid->cid_last_renew; b_left = display_printf(dspbuf, "} t_delta=%d reservations=%d refcount=%"PRIu32, delta, clientid->cid_lease_reservations, atomic_fetch_int32_t(&clientid->cid_refcount)); if (b_left <= 0) return b_left; if (clientid->cid_minorversion == 0) { b_left = display_printf(dspbuf, " cb_prog=%u r_addr=%s r_netid=%s", clientid->cid_cb.v40.cb_program, clientid->cid_cb.v40.cb_client_r_addr, netid_nc_table[clientid->cid_cb.v40 .cb_addr.nc].netid); } return b_left; } /** * @brief Display a client owner * * @param[in] clientid The client record * @param[out] str Output buffer * * @return Length of display string. */ int display_clientid_name(struct display_buffer *dspbuf, nfs_client_id_t *clientid) { if (clientid->cid_client_record == NULL) return display_start(dspbuf); return display_opaque_value( dspbuf, clientid->cid_client_record->cr_client_val, clientid->cid_client_record->cr_client_val_len); } /** * @brief Increment the clientid refcount in the hash table * * @param[in] val Buffer pointing to client record */ static void Hash_inc_client_id_ref(struct gsh_buffdesc *val) { nfs_client_id_t *clientid = val->addr; inc_client_id_ref(clientid); } /** * @brief Increment the clientid refcount * * @param[in] clientid Client record */ int32_t inc_client_id_ref(nfs_client_id_t *clientid) { int32_t cid_refcount = atomic_inc_int32_t(&clientid->cid_refcount); if (isFullDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, clientid); LogFullDebug(COMPONENT_CLIENTID, "Increment refcount Clientid {%s} to %" PRId32, str, cid_refcount); } return cid_refcount; } /** * @brief Tests whether state exists on a client id * * We assume that open owners are predictive of open or lock state, * since they're collected when the last state is removed. * * @note The clientid mutex must be held when calling this function. * * @param[in] clientid Client record * * @retval true if there is state. * @retval false if there isn't. */ bool client_id_has_state(nfs_client_id_t *clientid) { bool result; if (glist_empty(&clientid->cid_openowners)) return false; PTHREAD_MUTEX_lock(&clientid->cid_owner.so_mutex); result = !glist_empty( &clientid->cid_owner.so_owner.so_nfs4_owner.so_state_list); PTHREAD_MUTEX_unlock(&clientid->cid_owner.so_mutex); return result; } /** * @brief Deconstruct and free a client record * * @param[in] clientid The client record to free */ void free_client_id(nfs_client_id_t *clientid) { assert(atomic_fetch_int32_t(&clientid->cid_refcount) == 0); if (clientid->cid_client_record != NULL) dec_client_record_ref(clientid->cid_client_record); #ifdef _HAVE_GSSAPI if (clientid->cid_credential.flavor == RPCSEC_GSS) { struct svc_rpc_gss_data *gd; gd = clientid->cid_credential.auth_union.auth_gss.gd; unref_svc_rpc_gss_data(gd, 0); } #endif /* _HAVE_GSSAPI */ /* For NFSv4.1 clientids, destroy all associated sessions */ if (clientid->cid_minorversion > 0) { struct glist_head *glist = NULL; struct glist_head *glistn = NULL; glist_for_each_safe(glist, glistn, &clientid->cid_cb.v41.cb_session_list) { nfs41_session_t *session = glist_entry(glist, nfs41_session_t, session_link); nfs41_Session_Del(session->session_id); } } gsh_free(clientid->cid_recov_tag); clientid->cid_recov_tag = NULL; PTHREAD_MUTEX_destroy(&clientid->cid_mutex); PTHREAD_MUTEX_destroy(&clientid->cid_owner.so_mutex); if (clientid->cid_minorversion == 0) PTHREAD_MUTEX_destroy(&clientid->cid_cb.v40.cb_chan.mtx); put_gsh_client(clientid->gsh_client); pool_free(client_id_pool, clientid); } /** * @brief Decrement the clientid refcount * * @param[in] clientid Client record */ int32_t dec_client_id_ref(nfs_client_id_t *clientid) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; int32_t cid_refcount; if (isFullDebug(COMPONENT_CLIENTID)) display_client_id_rec(&dspbuf, clientid); cid_refcount = atomic_dec_int32_t(&clientid->cid_refcount); LogFullDebug(COMPONENT_CLIENTID, "Decrement refcount Clientid {%s} refcount to %" PRId32, str, cid_refcount); if (cid_refcount > 0) return cid_refcount; /* We don't need a lock to look at cid_confirmed because when * refcount has gone to 0, no other threads can have a pointer * to the clientid record. */ if (clientid->cid_confirmed == EXPIRED_CLIENT_ID) { /* Is not in any hash table, so we can just delete it */ LogFullDebug(COMPONENT_CLIENTID, "Free Clientid refcount now=0 {%s}", str); free_client_id(clientid); } else { /* Clientid records should not be freed unless marked expired */ display_client_id_rec(&dspbuf, clientid); LogCrit(COMPONENT_CLIENTID, "Should not be here, try to remove last ref {%s}", str); assert(clientid->cid_confirmed == EXPIRED_CLIENT_ID); } return cid_refcount; } /** * @brief Computes the hash value for the entry in Client Id cache. * * This function computes the hash value for the entry in Client Id * cache. In fact, it just uses the clientid as the value (identity * function) modulo the size of the hash. This function is called * internal in the HasTable_* function * * @param[in] hparam Hash table parameter * @param[in] key Pointer to the hash key buffer * * @return The computed hash value. * * @see hashtable_init * */ uint32_t client_id_value_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { clientid4 clientid; memcpy(&clientid, key->addr, sizeof(clientid)); return (uint32_t) clientid % hparam->index_size; } /** * @brief Computes the RBT hash for the entry in Client Id cache * * Computes the rbt value for the entry in Client Id cache. In fact, * it just use the address value itself (which is an unsigned integer) * as the rbt value. This function is called internal in the * HasTable_* function * * @param[in] hparam Hash table parameter. * @param[in] key Hash key buffer * * @return The computed RBT value. * * @see hashtable_init * */ uint64_t client_id_rbt_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { clientid4 clientid; memcpy(&clientid, key->addr, sizeof(clientid)); return clientid; } /** * @brief Compares clientids stored in the key buffers. * * This function compares the clientid stored in the key buffers. This * function is to be used as 'compare_key' field in the hashtable * storing the client ids. * * @param[in] buff1 first key * @param[in] buff2 second key * * @retval 0 if keys are identifical. * @retval 1 if the keys are different. * */ int compare_client_id(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2) { clientid4 cl1 = *((clientid4 *) (buff1->addr)); clientid4 cl2 = *((clientid4 *) (buff2->addr)); return (cl1 == cl2) ? 0 : 1; } /** * @brief Displays the client_id stored from the hash table * * @param[in] buff Buffer to display * @param[out] str Output string * * @return Number of character written. * */ int display_client_id_key(struct gsh_buffdesc *buff, char *str) { struct display_buffer dspbuf = {DISPLAY_CLIENTID_SIZE, str, str}; int rc; rc = display_clientid(&dspbuf, *((clientid4 *) (buff->addr))); assert(rc >= 0); return display_buffer_len(&dspbuf); } /** * @brief Displays the client record from a hash table * * @param[in] buff Buffer to display * @param[out] str Output string * * @return Number of character written. * */ int display_client_id_val(struct gsh_buffdesc *buff, char *str) { struct display_buffer dspbuf = {HASHTABLE_DISPLAY_STRLEN, str, str}; display_client_id_rec(&dspbuf, buff->addr); return display_buffer_len(&dspbuf); } /** * @brief Create a new client record * * @param[in] clientid The associated clientid * @param[in] client_record The client owner record * @param[in] client_addr Client socket address * @param[in] credential Client credential * @param[in] minorversion Minor version client uses * * @return New client record or NULL. */ nfs_client_id_t *create_client_id(clientid4 clientid, nfs_client_record_t *client_record, nfs_client_cred_t *credential, uint32_t minorversion) { nfs_client_id_t *client_rec = pool_alloc(client_id_pool); state_owner_t *owner; PTHREAD_MUTEX_init(&client_rec->cid_mutex, NULL); owner = &client_rec->cid_owner; PTHREAD_MUTEX_init(&owner->so_mutex, NULL); /* initialize the chan mutex for v4 */ if (minorversion == 0) { PTHREAD_MUTEX_init(&client_rec->cid_cb.v40.cb_chan.mtx, NULL); client_rec->cid_cb.v40.cb_chan_down = true; client_rec->first_path_down_resp_time = 0; } if (clientid == 0) clientid = new_clientid(); client_rec->cid_confirmed = UNCONFIRMED_CLIENT_ID; client_rec->cid_clientid = clientid; client_rec->cid_last_renew = time(NULL); client_rec->cid_client_record = client_record; client_rec->cid_credential = *credential; /* We store the credential which includes gss context here for * using it later, so we should make sure that this doesn't go * away until we destroy this nfs clientid. */ #ifdef _HAVE_GSSAPI if (credential->flavor == RPCSEC_GSS) { struct svc_rpc_gss_data *gd; gd = credential->auth_union.auth_gss.gd; (void)atomic_inc_uint32_t(&gd->refcnt); } #endif /* _HAVE_GSSAPI */ client_rec->cid_minorversion = minorversion; client_rec->gsh_client = op_ctx->client; inc_gsh_client_refcount(op_ctx->client); /* need to init the list_head */ glist_init(&client_rec->cid_openowners); glist_init(&client_rec->cid_lockowners); /* set up the content of the clientid_owner */ owner->so_type = STATE_CLIENTID_OWNER_NFSV4; owner->so_owner.so_nfs4_owner.so_clientid = clientid; owner->so_owner.so_nfs4_owner.so_clientrec = client_rec; owner->so_owner.so_nfs4_owner.so_resp.resop = NFS4_OP_ILLEGAL; owner->so_owner.so_nfs4_owner.so_args.argop = NFS4_OP_ILLEGAL; owner->so_refcount = 1; /* Init the lists for the clientid_owner */ glist_init(&owner->so_lock_list); glist_init(&owner->so_owner.so_nfs4_owner.so_state_list); /* Get a reference to the client record */ (void)inc_client_record_ref(client_rec->cid_client_record); return client_rec; } /** * @brief Inserts an entry describing a clientid4 into the cache. * * @param[in] clientid the client id record * * @retval CLIENT_ID_SUCCESS if successfull. * @retval CLIENT_ID_INSERT_MALLOC_ERROR if an error occured during * the insertion process * @retval CLIENT_ID_NETDB_ERROR if an error occured during the netdb * query (via gethostbyaddr). */ clientid_status_t nfs_client_id_insert(nfs_client_id_t *clientid) { struct gsh_buffdesc buffkey; struct gsh_buffdesc buffdata; int rc; /* Create key from cid_clientid */ buffkey.addr = &clientid->cid_clientid; buffkey.len = sizeof(clientid->cid_clientid); buffdata.addr = clientid; buffdata.len = sizeof(nfs_client_id_t); rc = hashtable_test_and_set(ht_unconfirmed_client_id, &buffkey, &buffdata, HASHTABLE_SET_HOW_SET_NO_OVERWRITE); if (rc != HASHTABLE_SUCCESS) { LogDebug(COMPONENT_CLIENTID, "Could not insert unconfirmed clientid %" PRIx64 " error=%s", clientid->cid_clientid, hash_table_err_to_str(rc)); /* Free the clientid record and return */ free_client_id(clientid); return CLIENT_ID_INSERT_MALLOC_ERROR; } /* Take a reference to the unconfirmed clientid for the hash table. */ (void)inc_client_id_ref(clientid); if (isFullDebug(COMPONENT_CLIENTID) && isFullDebug(COMPONENT_HASHTABLE)) { LogFullDebug(COMPONENT_CLIENTID, "-=-=-=-=-=-=-=-=-=-> ht_unconfirmed_client_id "); hashtable_log(COMPONENT_CLIENTID, ht_unconfirmed_client_id); } /* Attach new clientid to client record's cr_punconfirmed_id. */ clientid->cid_client_record->cr_unconfirmed_rec = clientid; return CLIENT_ID_SUCCESS; } /** * @brief Removes a confirmed client id record. * * @param[in] clientid The client id record * * @return hash table error code */ int remove_confirmed_client_id(nfs_client_id_t *clientid) { int rc; struct gsh_buffdesc buffkey; struct gsh_buffdesc old_key; struct gsh_buffdesc old_value; buffkey.addr = &clientid->cid_clientid; buffkey.len = sizeof(clientid->cid_clientid); rc = HashTable_Del(ht_confirmed_client_id, &buffkey, &old_key, &old_value); if (rc != HASHTABLE_SUCCESS) { LogDebug(COMPONENT_CLIENTID, "Could not remove unconfirmed clientid %" PRIx64 " error=%s", clientid->cid_clientid, hash_table_err_to_str(rc)); return rc; } if (clientid->cid_client_record != NULL) clientid->cid_client_record->cr_confirmed_rec = NULL; /* Set this up so this client id record will be freed. */ clientid->cid_confirmed = EXPIRED_CLIENT_ID; /* Release hash table reference to the unconfirmed record */ (void)dec_client_id_ref(clientid); return rc; } /** * @brief Removes an unconfirmed client id record. * * @param[in] clientid The client id record * * @return hash table error code */ int remove_unconfirmed_client_id(nfs_client_id_t *clientid) { int rc; struct gsh_buffdesc buffkey; struct gsh_buffdesc old_key; struct gsh_buffdesc old_value; buffkey.addr = &clientid->cid_clientid; buffkey.len = sizeof(clientid->cid_clientid); rc = HashTable_Del(ht_unconfirmed_client_id, &buffkey, &old_key, &old_value); if (rc != HASHTABLE_SUCCESS) { LogCrit(COMPONENT_CLIENTID, "Could not remove unconfirmed clientid %" PRIx64 " error=%s", clientid->cid_clientid, hash_table_err_to_str(rc)); return rc; } /* XXX prevents calling remove_confirmed before removed_confirmed, * if we failed to maintain the invariant that the cases are * disjoint */ if (clientid->cid_client_record != NULL) clientid->cid_client_record->cr_unconfirmed_rec = NULL; /* Set this up so this client id record will be freed. */ clientid->cid_confirmed = EXPIRED_CLIENT_ID; /* Release hash table reference to the unconfirmed record */ (void)dec_client_id_ref(clientid); return rc; } /** * @brief Confirm a client id record. * * @param[in] clientid The client id record * @param[in] component Component ID for logging * * @retval CLIENT_ID_SUCCESS if successfull. * @retval CLIENT_ID_INVALID_ARGUMENT if unable to find record in * unconfirmed table * @retval CLIENT_ID_INSERT_MALLOC_ERROR if unable to insert record * into confirmed table * @retval CLIENT_ID_NETDB_ERROR if an error occured during the netdb * query (via gethostbyaddr). */ clientid_status_t nfs_client_id_confirm(nfs_client_id_t *clientid, log_components_t component) { int rc; struct gsh_buffdesc buffkey; struct gsh_buffdesc old_key; struct gsh_buffdesc old_value; buffkey.addr = &clientid->cid_clientid; buffkey.len = sizeof(clientid->cid_clientid); /* Remove the clientid as the unconfirmed entry for the client record */ clientid->cid_client_record->cr_unconfirmed_rec = NULL; rc = HashTable_Del(ht_unconfirmed_client_id, &buffkey, &old_key, &old_value); if (rc != HASHTABLE_SUCCESS) { if (isDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, clientid); LogCrit(COMPONENT_CLIENTID, "Unexpected problem %s, could not remove {%s}", hash_table_err_to_str(rc), str); } return CLIENT_ID_INVALID_ARGUMENT; } clientid->cid_confirmed = CONFIRMED_CLIENT_ID; rc = hashtable_test_and_set(ht_confirmed_client_id, &old_key, &old_value, HASHTABLE_SET_HOW_SET_NO_OVERWRITE); if (rc != HASHTABLE_SUCCESS) { if (isDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, clientid); LogCrit(COMPONENT_CLIENTID, "Unexpected problem %s, could not insert {%s}", hash_table_err_to_str(rc), str); } /* Set this up so this client id record will be freed. */ clientid->cid_confirmed = EXPIRED_CLIENT_ID; /* Release hash table reference to the unconfirmed record */ (void)dec_client_id_ref(clientid); return CLIENT_ID_INSERT_MALLOC_ERROR; } /* Add the clientid as the confirmed entry for the client record */ clientid->cid_client_record->cr_confirmed_rec = clientid; nfs4_add_clid(clientid); return CLIENT_ID_SUCCESS; } /** * @brief Check if a clientid has state associated with it. * * @param[in] clientid The client id of interest * * @retval true if the clientid has associated state. */ bool clientid_has_state(nfs_client_id_t *clientid) { bool live_state = false; struct glist_head *glist; PTHREAD_MUTEX_lock(&clientid->cid_mutex); /* Don't bother checking lock owners, there must ALSO be an * open owner with active open state in order for there to be * active lock state. */ /* Check if any open owners have active open state. */ glist_for_each(glist, &clientid->cid_openowners) { live_state = owner_has_state(glist_entry( glist, state_owner_t, so_owner.so_nfs4_owner.so_perclient)); if (live_state) break; } /* Delegations and Layouts are owned by clientid, so check for * active state held by the cid_owner. */ if (!live_state) live_state = owner_has_state(&clientid->cid_owner); PTHREAD_MUTEX_unlock(&clientid->cid_mutex); return live_state; } /** * @brief Client expires, need to take care of owners * * If there is a client_record attached to the clientid, * this function assumes caller holds record->cr_mutex and holds a * reference to record also. * * @param[in] clientid The client id to expire * @param[in] make_stale Set if client id expire is due to ip move. * * @return true if the clientid is successfully expired. */ bool nfs_client_id_expire(nfs_client_id_t *clientid, bool make_stale) { int rc, held; struct gsh_buffdesc buffkey; struct gsh_buffdesc old_key; struct gsh_buffdesc old_value; hash_table_t *ht_expire; nfs_client_record_t *record; char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; bool str_valid = false; struct root_op_context root_op_context; /* Initialize req_ctx */ init_root_op_context(&root_op_context, NULL, NULL, 0, 0, UNKNOWN_REQUEST); PTHREAD_MUTEX_lock(&clientid->cid_mutex); if (clientid->cid_confirmed == EXPIRED_CLIENT_ID) { if (isFullDebug(COMPONENT_CLIENTID)) { display_client_id_rec(&dspbuf, clientid); LogFullDebug(COMPONENT_CLIENTID, "Expired (skipped) {%s}", str); } PTHREAD_MUTEX_unlock(&clientid->cid_mutex); release_root_op_context(); return false; } if (isDebug(COMPONENT_CLIENTID)) { display_client_id_rec(&dspbuf, clientid); LogDebug(COMPONENT_CLIENTID, "Expiring {%s}", str); } if ((clientid->cid_confirmed == CONFIRMED_CLIENT_ID) || (clientid->cid_confirmed == STALE_CLIENT_ID)) ht_expire = ht_confirmed_client_id; else ht_expire = ht_unconfirmed_client_id; /* Need to clean up the client record. */ record = clientid->cid_client_record; clientid->cid_client_record = NULL; if (record != NULL) { /* Detach the clientid record from the client record */ if (record->cr_confirmed_rec == clientid) record->cr_confirmed_rec = NULL; if (record->cr_unconfirmed_rec == clientid) record->cr_unconfirmed_rec = NULL; /* the linkage was removed, update refcount */ dec_client_record_ref(record); } if (make_stale) { /* Keep clientid hashed, but mark it as stale */ clientid->cid_confirmed = STALE_CLIENT_ID; PTHREAD_MUTEX_unlock(&clientid->cid_mutex); } else { /* unhash clientids that are truly expired */ clientid->cid_confirmed = EXPIRED_CLIENT_ID; PTHREAD_MUTEX_unlock(&clientid->cid_mutex); buffkey.addr = &clientid->cid_clientid; buffkey.len = sizeof(clientid->cid_clientid); rc = HashTable_Del(ht_expire, &buffkey, &old_key, &old_value); if ((rc != HASHTABLE_SUCCESS) && (clientid->cid_confirmed == STALE_CLIENT_ID)) { /* Try in the unconfirmed hash table */ rc = HashTable_Del(ht_unconfirmed_client_id, &buffkey, &old_key, &old_value); } if (rc != HASHTABLE_SUCCESS) { LogFatal(COMPONENT_CLIENTID, "Could not remove expired clientid %" PRIx64 " error=%s", clientid->cid_clientid, hash_table_err_to_str(rc)); } } /* Traverse the client's lock owners, and release all * locks and owners. * * Note: If there is an owner refcount bug, this COULD infinite loop, * and it will spam the log with warnings... Such a refcount bug will * be quickly fixed :-). */ while (true) { state_owner_t *owner; PTHREAD_MUTEX_lock(&clientid->cid_mutex); owner = glist_first_entry(&clientid->cid_lockowners, state_owner_t, so_owner.so_nfs4_owner.so_perclient); if (owner == NULL) { PTHREAD_MUTEX_unlock(&clientid->cid_mutex); break; } /* Move owner to end of list in case it doesn't get * freed when we decrement the refcount. */ glist_del(&owner->so_owner.so_nfs4_owner.so_perclient); glist_add_tail(&clientid->cid_lockowners, &owner->so_owner.so_nfs4_owner.so_perclient); /* Hold a reference to the owner while we drop the cid_mutex. */ held = hold_state_owner(owner); PTHREAD_MUTEX_unlock(&clientid->cid_mutex); /* If this owner is in the process of being freed, skip * and work on the next owner. We also do yield for the * other thread to complete freeing this owner! */ if (!held) { sched_yield(); continue; } state_nfs4_owner_unlock_all(owner); if (isFullDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; int32_t refcount = atomic_fetch_int32_t(&owner->so_refcount); display_owner(&dspbuf, owner); if (refcount > 1) LogWarn(COMPONENT_CLIENTID, "Expired State, Possibly extra references to {%s}", str); else LogFullDebug(COMPONENT_CLIENTID, "Expired State for {%s}", str); } dec_state_owner_ref(owner); } /* revoke layouts for this client*/ revoke_owner_layouts(&clientid->cid_owner); /* release the corresponding open states , close files. * * Note: If there is an owner refcount bug, this COULD infinite loop, * and it will spam the log with warnings... Such a refcount bug will * be quickly fixed :-). */ while (true) { state_owner_t *owner; PTHREAD_MUTEX_lock(&clientid->cid_mutex); owner = glist_first_entry(&clientid->cid_openowners, state_owner_t, so_owner.so_nfs4_owner.so_perclient); if (owner == NULL) { PTHREAD_MUTEX_unlock(&clientid->cid_mutex); break; } /* Move owner to end of list in case it doesn't get * freed when we decrement the refcount. */ glist_del(&owner->so_owner.so_nfs4_owner.so_perclient); glist_add_tail(&clientid->cid_openowners, &owner->so_owner.so_nfs4_owner.so_perclient); /* Hold a reference to the owner while we drop the cid_mutex. */ held = hold_state_owner(owner); PTHREAD_MUTEX_unlock(&clientid->cid_mutex); /* If this owner is in the process of being freed, skip * and work on the next owner. We also do yield for the * other thread to complete freeing this owner! */ if (!held) { sched_yield(); continue; } release_openstate(owner); if (isFullDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; int32_t refcount = atomic_fetch_int32_t(&owner->so_refcount); display_owner(&dspbuf, owner); if (refcount > 1) LogFatal(COMPONENT_CLIENTID, "Expired State, Possibly extra references to {%s}", str); else LogFullDebug(COMPONENT_CLIENTID, "Expired State for {%s}", str); } dec_state_owner_ref(owner); } /* revoke delegations for this client*/ revoke_owner_delegs(&clientid->cid_owner); /* Destroy v4 callback channel */ if (clientid->cid_minorversion == 0 && clientid->cid_cb.v40.cb_chan.clnt) nfs_rpc_destroy_chan(&clientid->cid_cb.v40.cb_chan); /* For NFSv4.1 clientids, destroy all associated sessions */ if (clientid->cid_minorversion > 0) { struct glist_head *glist = NULL; struct glist_head *glistn = NULL; glist_for_each_safe(glist, glistn, &clientid->cid_cb.v41.cb_session_list) { nfs41_session_t *session = glist_entry(glist, nfs41_session_t, session_link); if (!nfs41_Session_Del(session->session_id)) { display_client_id_rec(&dspbuf, clientid); LogCrit(COMPONENT_SESSIONS, "Expire session failed for {%s}", str); } } } if (clientid->cid_recov_tag != NULL && !make_stale) { nfs4_rm_clid(clientid); gsh_free(clientid->cid_recov_tag); clientid->cid_recov_tag = NULL; } if (isDebug(COMPONENT_CLIENTID)) { display_client_id_rec(&dspbuf, clientid); LogDebug(COMPONENT_CLIENTID, "Expired (done), about to release last reference {%s}", str); str_valid = true; } /* Release the hash table reference to the clientid. */ if (!make_stale) (void)dec_client_id_ref(clientid); if (isFullDebug(COMPONENT_CLIENTID)) { if (!str_valid) display_printf(&dspbuf, "clientid %p", clientid); LogFullDebug(COMPONENT_CLIENTID, "Expired (done), released last reference {%s}", str); } release_root_op_context(); return true; } /** * @brief Get a clientid from a hash table * * @param[in] ht Table from which to fetch * @param[in] clientid Clientid to fetch * @param[out] client_rec Fetched client record * * @return Clientid status. */ clientid_status_t nfs_client_id_get(hash_table_t *ht, clientid4 clientid, nfs_client_id_t **client_rec) { struct gsh_buffdesc buffkey; struct gsh_buffdesc buffval; clientid_status_t status; uint64_t epoch_low = ServerEpoch & 0xFFFFFFFF; uint64_t cid_epoch = (uint64_t) (clientid >> (clientid4) 32); nfs_client_id_t *pclientid; /* Don't even bother to look up clientid if epochs don't match */ if (cid_epoch != epoch_low) { if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_CLIENTID, "%s NOTFOUND (epoch doesn't match, assumed STALE)", ht->parameter.ht_name); *client_rec = NULL; return CLIENT_ID_STALE; } buffkey.addr = &clientid; buffkey.len = sizeof(clientid4); if (isFullDebug(COMPONENT_CLIENTID) && isDebug(COMPONENT_HASHTABLE)) { LogFullDebug(COMPONENT_CLIENTID, "%s KEY {%" PRIx64 "}", ht->parameter.ht_name, clientid); } if (isFullDebug(COMPONENT_CLIENTID) && isFullDebug(COMPONENT_HASHTABLE)) { LogFullDebug(COMPONENT_CLIENTID, "-=-=-=-=-=-=-=-=-=-> %s", ht->parameter.ht_name); hashtable_log(COMPONENT_CLIENTID, ht); } if (hashtable_getref(ht, &buffkey, &buffval, Hash_inc_client_id_ref) == HASHTABLE_SUCCESS) { if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_CLIENTID, "%s FOUND", ht->parameter.ht_name); pclientid = buffval.addr; if (pclientid->cid_confirmed == STALE_CLIENT_ID) { /* Stale client becuse of ip detach and attach to * same node */ dec_client_id_ref(pclientid); status = CLIENT_ID_STALE; *client_rec = NULL; } else { *client_rec = pclientid; status = CLIENT_ID_SUCCESS; } } else { if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_CLIENTID, "%s NOTFOUND (assumed EXPIRED)", ht->parameter.ht_name); *client_rec = NULL; status = CLIENT_ID_EXPIRED; } return status; } /** * @brief Triy to get a pointer to an unconfirmed entry for client_id cache. * * @param[in] clientid The client id * @param[out] client_rec The found client id structure * * @return Same as nfs_client_id_get */ clientid_status_t nfs_client_id_get_unconfirmed(clientid4 clientid, nfs_client_id_t **client_rec) { return nfs_client_id_get(ht_unconfirmed_client_id, clientid, client_rec); } /** * @brief Tries to get a pointer to an confirmed entry for client_id cache. * * @param[in] clientid The client id * @param[out] client_rec The found client id * * @return the result previously set if the return code CLIENT_ID_SUCCESS * */ clientid_status_t nfs_client_id_get_confirmed(clientid4 clientid, nfs_client_id_t **client_rec) { return nfs_client_id_get(ht_confirmed_client_id, clientid, client_rec); } static hash_parameter_t cid_confirmed_hash_param = { .index_size = PRIME_STATE, .hash_func_key = client_id_value_hash_func, .hash_func_rbt = client_id_rbt_hash_func, .hash_func_both = NULL, .compare_key = compare_client_id, .key_to_str = display_client_id_key, .val_to_str = display_client_id_val, .ht_name = "Confirmed Client ID", .flags = HT_FLAG_CACHE, .ht_log_component = COMPONENT_CLIENTID, }; static hash_parameter_t cid_unconfirmed_hash_param = { .index_size = PRIME_STATE, .hash_func_key = client_id_value_hash_func, .hash_func_rbt = client_id_rbt_hash_func, .hash_func_both = NULL, .compare_key = compare_client_id, .key_to_str = display_client_id_key, .val_to_str = display_client_id_val, .ht_name = "Unconfirmed Client ID", .flags = HT_FLAG_CACHE, .ht_log_component = COMPONENT_CLIENTID, }; static hash_parameter_t cr_hash_param = { .index_size = PRIME_STATE, .hash_func_key = client_record_value_hash_func, .hash_func_rbt = client_record_rbt_hash_func, .hash_func_both = NULL, .compare_key = compare_client_record, .key_to_str = display_client_record_key, .val_to_str = display_client_record_val, .ht_name = "Client Record", .flags = HT_FLAG_CACHE, .ht_log_component = COMPONENT_CLIENTID, }; /** * @brief Init the hashtable for Client Id cache. * * Perform all the required initialization for hashtable Client Id cache * * @return 0 if successful, -1 otherwise */ int nfs_Init_client_id(void) { ht_confirmed_client_id = hashtable_init(&cid_confirmed_hash_param); if (ht_confirmed_client_id == NULL) { LogCrit(COMPONENT_INIT, "NFS CLIENT_ID: Cannot init Client Id cache"); return -1; } ht_unconfirmed_client_id = hashtable_init(&cid_unconfirmed_hash_param); if (ht_unconfirmed_client_id == NULL) { LogCrit(COMPONENT_INIT, "NFS CLIENT_ID: Cannot init Client Id cache"); return -1; } ht_client_record = hashtable_init(&cr_hash_param); if (ht_client_record == NULL) { LogCrit(COMPONENT_INIT, "NFS CLIENT_ID: Cannot init Client Record cache"); return -1; } client_id_pool = pool_basic_init("NFS4 Client ID Pool", sizeof(nfs_client_id_t)); return CLIENT_ID_SUCCESS; } int display_clientid(struct display_buffer *dspbuf, clientid4 clientid) { int b_left = display_buffer_remain(dspbuf); uint32_t counter = clientid & UINT32_MAX; uint32_t epoch = clientid >> (clientid4) 32; if (b_left <= 0) return b_left; return display_printf(dspbuf, "Epoch=0x%08"PRIx32" Counter=0x%08"PRIx32, epoch, counter); } /** * @brief Builds a new clientid4 value * * We use the clientid counter and the server epoch, the latter * ensures that clientids from old instances of Ganesha are marked as * invalid. * * @return The new clientid. */ clientid4 new_clientid(void) { clientid4 newid = atomic_inc_uint32_t(&clientid_counter); uint64_t epoch_low = ServerEpoch & UINT32_MAX; return newid + (epoch_low << (clientid4) 32); } /** * @brief Builds a new verifier4 value. * * @param[out] verf The verifier */ void new_clientid_verifier(char *verf) { uint64_t my_verifier = atomic_inc_uint64_t(&clientid_verifier); memcpy(verf, &my_verifier, NFS4_VERIFIER_SIZE); } /****************************************************************************** * * Functions to handle lookup of clientid by nfs_client_id4 received from * client. * *****************************************************************************/ /** * @brief Display a client owner record * * @param[in] record The record to display * @param[out] str Output buffer * * @return Length of output string. */ int display_client_record(struct display_buffer *dspbuf, nfs_client_record_t *record) { int b_left = display_printf(dspbuf, "%p name=", record); if (b_left <= 0) return b_left; b_left = display_opaque_value(dspbuf, record->cr_client_val, record->cr_client_val_len); if (b_left <= 0) return b_left; return display_printf(dspbuf, " refcount=%" PRId32, atomic_fetch_int32_t(&record->cr_refcount)); } /** * @brief Increment the refcount on a client owner record * * @param[in] record Record on which to take a reference */ int32_t inc_client_record_ref(nfs_client_record_t *record) { int32_t rec_refcnt = atomic_inc_int32_t(&record->cr_refcount); if (isFullDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_record(&dspbuf, record); LogFullDebug(COMPONENT_CLIENTID, "Increment refcount {%s}", str); } return rec_refcnt; } /** * @brief Deconstruct and free a client owner record * * @param[in] record The record to free */ void free_client_record(nfs_client_record_t *record) { PTHREAD_MUTEX_destroy(&record->cr_mutex); gsh_free(record); } /** * @brief Decrement the refcount on a client owner record * * @param[in] record Record on which to release a reference */ int32_t dec_client_record_ref(nfs_client_record_t *record) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; struct hash_latch latch; hash_error_t rc; struct gsh_buffdesc buffkey; struct gsh_buffdesc old_value; struct gsh_buffdesc old_key; int32_t refcount; bool str_valid = false; if (isDebug(COMPONENT_CLIENTID)) { display_client_record(&dspbuf, record); str_valid = true; } refcount = atomic_dec_int32_t(&record->cr_refcount); if (refcount > 0) { LogFullDebug(COMPONENT_CLIENTID, "Decrement refcount refcount now=%" PRId32 " {%s}", refcount, str); return refcount; } assert(refcount == 0); LogFullDebug(COMPONENT_CLIENTID, "Try to remove {%s}", str); buffkey.addr = record; buffkey.len = sizeof(*record); /* Since the refcount is zero, another thread that needs this * record might have deleted ours, so expect not to find one or * find someone else's record! */ rc = hashtable_getlatch(ht_client_record, &buffkey, &old_value, true, &latch); switch (rc) { case HASHTABLE_SUCCESS: /* If ours, delete from hash table */ if (old_value.addr == record) { hashtable_deletelatched(ht_client_record, &buffkey, &latch, &old_key, &old_value); } break; case HASHTABLE_ERROR_NO_SUCH_KEY: break; default: if (!str_valid) { display_client_record(&dspbuf, record); } LogCrit(COMPONENT_CLIENTID, "Error %s, could not find {%s}", hash_table_err_to_str(rc), str); return refcount; } /* Release the latch */ hashtable_releaselatched(ht_client_record, &latch); if (str_valid) LogFullDebug(COMPONENT_CLIENTID, "Free {%s}", str); free_client_record(record); return refcount; } /** * @brief Hash a client owner record key * * @param[in] key The client owner record * * @return The hash. */ uint64_t client_record_value_hash(nfs_client_record_t *key) { uint64_t other; other = key->cr_pnfs_flags; other = (other << 32) | key->cr_server_addr; return CityHash64WithSeed(key->cr_client_val, key->cr_client_val_len, other); } /** * * @brief Computes the hash value for the entry in Client Record cache. * * @param[in] hparam Hash table parameter. * @param[in] key The hash key buffer * * @return the computed hash value. * * @see hashtable_init * */ uint32_t client_record_value_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { uint64_t res; res = client_record_value_hash(key->addr) % hparam->index_size; if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_CLIENTID, "value = %" PRIu64, res); return (uint32_t) res; } /** * @brief Computes the RBT hash for the entry in Client Id cache. * * @param[in] hparam Hash table parameter * @param[in] key The hash key buffer * * @return The computed rbt value. * * @see hashtable_init * */ uint64_t client_record_rbt_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { uint64_t res; res = client_record_value_hash(key->addr); if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_CLIENTID, "value = %" PRIu64, res); return res; } /** * @brief Compares the cr_client_val the key buffers. * * @param[in] buff1 first key * @param[in] buff2 second key * * @return 0 if keys are identifical, 1 if they are different. * */ int compare_client_record(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2) { nfs_client_record_t *pkey1 = buff1->addr; nfs_client_record_t *pkey2 = buff2->addr; if (pkey1->cr_client_val_len != pkey2->cr_client_val_len) return 1; if (pkey1->cr_pnfs_flags != pkey2->cr_pnfs_flags) return 1; return memcmp(pkey1->cr_client_val, pkey2->cr_client_val, pkey1->cr_client_val_len); } /** * @brief Displays the client_record stored in the buffer. * * @param[in] buff Buffer to display * @param[out] str output string * * @return The number of character written. */ int display_client_record_key(struct gsh_buffdesc *buff, char *str) { struct display_buffer dspbuf = {HASHTABLE_DISPLAY_STRLEN, str, str}; display_client_record(&dspbuf, buff->addr); return display_buffer_len(&dspbuf); } /** * @brief Displays the client_record stored in the buffer. * * @param[in] buff Buffer to display * @param[out] str output string * * @return The number of character written. */ int display_client_record_val(struct gsh_buffdesc *buff, char *str) { struct display_buffer dspbuf = {HASHTABLE_DISPLAY_STRLEN, str, str}; display_client_record(&dspbuf, buff->addr); return display_buffer_len(&dspbuf); } /** * @brief Get a client record from the table * * @param[in] value Client owner name * @param[in] len Length of owner name * * @return The client record or NULL. */ nfs_client_record_t *get_client_record(const char *const value, const size_t len, const uint32_t pnfs_flags, const uint32_t server_addr) { nfs_client_record_t *record; nfs_client_record_t *old; struct gsh_buffdesc buffkey; struct gsh_buffdesc buffval; struct hash_latch latch; hash_error_t rc; int32_t refcount; assert(len); record = gsh_malloc(sizeof(nfs_client_record_t) + len); record->cr_refcount = 1; record->cr_client_val_len = len; record->cr_confirmed_rec = NULL; record->cr_unconfirmed_rec = NULL; memcpy(record->cr_client_val, value, len); record->cr_pnfs_flags = pnfs_flags; record->cr_server_addr = server_addr; buffkey.addr = record; buffkey.len = sizeof(*record); rc = hashtable_getlatch(ht_client_record, &buffkey, &buffval, true, &latch); switch (rc) { case HASHTABLE_SUCCESS: old = buffval.addr; refcount = atomic_inc_int32_t(&old->cr_refcount); if (refcount == 1) { /* This record is in the process of getting freed. * Delete from the hash table and pretend as * though we didn't find it! */ (void)atomic_dec_int32_t(&old->cr_refcount); hashtable_deletelatched(ht_client_record, &buffkey, &latch, NULL, NULL); break; } /* Use the existing record */ hashtable_releaselatched(ht_client_record, &latch); gsh_free(record); return old; case HASHTABLE_ERROR_NO_SUCH_KEY: break; default: LogFatal(COMPONENT_CLIENTID, "Client record hash table corrupt."); } /* Initialize and insert the new record */ PTHREAD_MUTEX_init(&record->cr_mutex, NULL); buffval.addr = record; buffval.len = sizeof(nfs_client_record_t) + len; rc = hashtable_setlatched(ht_client_record, &buffkey, &buffval, &latch, false, NULL, NULL); if (rc != HASHTABLE_SUCCESS) { LogFatal(COMPONENT_CLIENTID, "Client record hash table corrupt."); } return record; } struct client_callback_arg { void *state; nfs_client_id_t *pclientid; bool (*cb)(nfs_client_id_t *, void *); }; /** * @brief client callback */ static void client_cb(struct fridgethr_context *ctx) { struct client_callback_arg *cb_arg; cb_arg = ctx->arg; cb_arg->cb(cb_arg->pclientid, cb_arg->state); dec_client_id_ref(cb_arg->pclientid); gsh_free(cb_arg->state); gsh_free(cb_arg); } /** * @brief Walk the client tree and do the callback on each 4.1 nodes * * @param cb [IN] Callback function * @param state [IN] param block to pass */ void nfs41_foreach_client_callback(bool(*cb) (nfs_client_id_t *cl, void *state), void *state) { uint32_t i; hash_table_t *ht = ht_confirmed_client_id; struct rbt_head *head_rbt; struct hash_data *pdata = NULL; struct rbt_node *pn; nfs_client_id_t *pclientid; struct client_callback_arg *cb_arg; int rc; /* For each bucket of the hashtable */ for (i = 0; i < ht->parameter.index_size; i++) { head_rbt = &(ht->partitions[i].rbt); /* acquire mutex */ PTHREAD_RWLOCK_wrlock(&(ht->partitions[i].lock)); /* go through all entries in the red-black-tree */ RBT_LOOP(head_rbt, pn) { pdata = RBT_OPAQ(pn); pclientid = pdata->val.addr; RBT_INCREMENT(pn); if (pclientid->cid_minorversion > 0) { cb_arg = gsh_malloc( sizeof(struct client_callback_arg)); cb_arg->cb = cb; cb_arg->state = state; cb_arg->pclientid = pclientid; inc_client_id_ref(pclientid); rc = fridgethr_submit(state_async_fridge, client_cb, cb_arg); if (rc != 0) { LogCrit(COMPONENT_CLIENTID, "unable to start client cb thread %d", rc); gsh_free(cb_arg); dec_client_id_ref(pclientid); } } } PTHREAD_RWLOCK_unlock(&(ht->partitions[i].lock)); } } /** @} */ nfs-ganesha-2.6.0/src/SAL/nfs4_lease.c000066400000000000000000000077521324272410200173160ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup SAL State abstraction layer * @{ */ /** * @file nfs4_lease.c * @brief NFSv4 lease management */ #include "config.h" #include "log.h" #include "nfs_core.h" #include "nfs4.h" #include "sal_functions.h" /** * @brief Return the lifetime of a valid lease * * @param[in] clientid The client record to check * * @return The lease lifetime or 0 if expired. */ static unsigned int _valid_lease(nfs_client_id_t *clientid) { time_t t; if (clientid->cid_confirmed == EXPIRED_CLIENT_ID) return 0; if (clientid->cid_lease_reservations != 0) return nfs_param.nfsv4_param.lease_lifetime; t = time(NULL); if (clientid->cid_last_renew + nfs_param.nfsv4_param.lease_lifetime > t) return (clientid->cid_last_renew + nfs_param.nfsv4_param.lease_lifetime) - t; return 0; } /** * @brief Check if lease is valid * * The caller must hold cid_mutex. * * @param[in] clientid Record to check lease for. * * @return 1 if lease is valid, 0 if not. * */ bool valid_lease(nfs_client_id_t *clientid) { unsigned int valid; valid = _valid_lease(clientid); if (isFullDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, clientid); LogFullDebug(COMPONENT_CLIENTID, "Check Lease %s (Valid=%s %u seconds left)", str, valid ? "YES" : "NO", valid); } return valid != 0; } /** * @brief Check if lease is valid and reserve it and retain cid_mutex. * * Lease reservation prevents any other thread from expiring the lease. Caller * must call update lease to release the reservation. * * @param[in] clientid Client record to check lease for * * @return 1 if lease is valid, 0 if not. * */ int reserve_lease(nfs_client_id_t *clientid) { unsigned int valid; valid = _valid_lease(clientid); if (valid != 0) clientid->cid_lease_reservations++; if (isFullDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, clientid); LogFullDebug(COMPONENT_CLIENTID, "Reserve Lease %s (Valid=%s %u seconds left)", str, valid ? "YES" : "NO", valid); } return valid != 0; } /** * @brief Release a lease reservation and update lease. * * Lease reservation prevents any other thread from expiring the lease. This * function releases the lease reservation. Before releasing the last * reservation, cid_last_renew will be updated. * * @param[in] clientid Clientid record to update * * @return 1 if lease is valid, 0 if not. * */ void update_lease(nfs_client_id_t *clientid) { clientid->cid_lease_reservations--; /* Renew lease when last reservation is released */ if (clientid->cid_lease_reservations == 0) clientid->cid_last_renew = time(NULL); if (isFullDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_client_id_rec(&dspbuf, clientid); LogFullDebug(COMPONENT_CLIENTID, "Update Lease %s", str); } } /** @} */ nfs-ganesha-2.6.0/src/SAL/nfs4_owner.c000066400000000000000000000503771324272410200173600ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ /** * @defgroup SAL State abstraction layer * @{ */ /** * @file nfs4_owner.c * @brief The management of the NFS4 Owner cache. */ #include "config.h" #include #include #include "log.h" #include "hashtable.h" #include "nfs4.h" #include "sal_functions.h" #include "nfs_proto_functions.h" #include "nfs_core.h" hash_table_t *ht_nfs4_owner; /** * @brief Display an NFSv4 owner key * * @param[in] buff Key to display * @param[out] str Output buffer * * @return Length of output string. */ int display_nfs4_owner_key(struct gsh_buffdesc *buff, char *str) { struct display_buffer dspbuf = {HASHTABLE_DISPLAY_STRLEN, str, str}; display_nfs4_owner(&dspbuf, buff->addr); return display_buffer_len(&dspbuf); } /** * @brief Display NFSv4 owner * * @param[in] owner The state owner * @param[out] str Output string * * @return the bytes remaining in the buffer. */ int display_nfs4_owner(struct display_buffer *dspbuf, state_owner_t *owner) { int b_left; time_t texpire; struct state_nfs4_owner_t *nfs4_owner = &owner->so_owner.so_nfs4_owner; if (owner == NULL) return display_cat(dspbuf, ""); b_left = display_printf(dspbuf, "%s %p:", state_owner_type_to_str(owner->so_type), owner); if (b_left <= 0) return b_left; b_left = display_printf(dspbuf, " clientid={"); if (b_left <= 0) return b_left; b_left = display_client_id_rec(dspbuf, nfs4_owner->so_clientrec); if (b_left <= 0) return b_left; b_left = display_printf(dspbuf, "} owner="); if (b_left <= 0) return b_left; b_left = display_opaque_value(dspbuf, owner->so_owner_val, owner->so_owner_len); if (b_left <= 0) return b_left; b_left = display_printf(dspbuf, " confirmed=%u seqid=%u", nfs4_owner->so_confirmed, nfs4_owner->so_seqid); if (b_left <= 0) return b_left; if (nfs4_owner->so_related_owner != NULL) { b_left = display_printf(dspbuf, " related_owner={"); if (b_left <= 0) return b_left; b_left = display_nfs4_owner(dspbuf, nfs4_owner->so_related_owner); if (b_left <= 0) return b_left; b_left = display_printf(dspbuf, "}"); if (b_left <= 0) return b_left; } texpire = atomic_fetch_time_t(&nfs4_owner->so_cache_expire); if (texpire != 0) { b_left = display_printf(dspbuf, " cached(expires in %d secs)", texpire - time(NULL)); if (b_left <= 0) return b_left; } return display_printf(dspbuf, " refcount=%d", atomic_fetch_int32_t(&owner->so_refcount)); } /** * @brief Display owner from hash table * * @param[in] buff Buffer * @param[out] str Output buffer * * @return Length of the output string. */ int display_nfs4_owner_val(struct gsh_buffdesc *buff, char *str) { struct display_buffer dspbuf = {HASHTABLE_DISPLAY_STRLEN, str, str}; display_nfs4_owner(&dspbuf, buff->addr); return display_buffer_len(&dspbuf); } /** * @brief Compare two NFSv4 owners * * @param[in] owner1 One owner * @param[in] owner2 Another owner * * @retval 0 on equality. * @retval 1 on inequality. */ int compare_nfs4_owner(state_owner_t *owner1, state_owner_t *owner2) { if (isFullDebug(COMPONENT_STATE) && isDebug(COMPONENT_HASHTABLE)) { char str1[LOG_BUFF_LEN / 2] = "\0"; char str2[LOG_BUFF_LEN / 2] = "\0"; struct display_buffer dspbuf1 = {sizeof(str1), str1, str1}; struct display_buffer dspbuf2 = {sizeof(str2), str2, str2}; display_nfs4_owner(&dspbuf1, owner1); display_nfs4_owner(&dspbuf2, owner2); LogFullDebug(COMPONENT_STATE, "{%s} vs {%s}", str1, str2); } if (owner1 == NULL || owner2 == NULL) return 1; if (owner1 == owner2) return 0; if (owner1->so_type != owner2->so_type) return 1; if (owner1->so_owner.so_nfs4_owner.so_clientid != owner2->so_owner.so_nfs4_owner.so_clientid) return 1; if (owner1->so_owner_len != owner2->so_owner_len) return 1; return memcmp(owner1->so_owner_val, owner2->so_owner_val, owner1->so_owner_len); } /** * @brief Compare two NFSv4 owners in the hash table * * @param[in] buff1 One key * @param[in] buff2 Another owner * * @retval 0 on equality. * @retval 1 on inequality. */ int compare_nfs4_owner_key(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2) { state_owner_t *pkey1 = buff1->addr; state_owner_t *pkey2 = buff2->addr; if (isFullDebug(COMPONENT_STATE) && isDebug(COMPONENT_HASHTABLE)) { char str1[LOG_BUFF_LEN / 2] = "\0"; char str2[LOG_BUFF_LEN / 2] = "\0"; struct display_buffer dspbuf1 = {sizeof(str1), str1, str1}; struct display_buffer dspbuf2 = {sizeof(str2), str2, str2}; display_owner(&dspbuf1, pkey1); display_owner(&dspbuf2, pkey2); if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_STATE, "{%s} vs {%s}", str1, str2); } if (pkey1 == NULL || pkey2 == NULL) return 1; if (pkey1->so_type != pkey2->so_type) return 1; return compare_nfs4_owner(pkey1, pkey2); } /** * @brief Compute the hash index for an NFSv4 owner * * @todo Destroy this function and replace it with a real hash. * * @param[in] hparam Hash parameter * @param[in] key The key * * @return The hash index. */ uint32_t nfs4_owner_value_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { unsigned int sum = 0; unsigned int i = 0; unsigned char c = 0; uint32_t res = 0; state_owner_t *pkey = key->addr; /* Compute the sum of all the characters */ for (i = 0; i < pkey->so_owner_len; i++) { c = ((char *)pkey->so_owner_val)[i]; sum += c; } res = ((uint32_t) pkey->so_owner.so_nfs4_owner.so_clientid + (uint32_t) sum + pkey->so_owner_len + (uint32_t) pkey->so_type) % (uint32_t) hparam->index_size; if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_STATE, "value = %" PRIu32, res); return res; } /** * @brief Compute the RBT hash for an NFSv4 owner * * @todo Destroy this function and replace it with a real hash. * * @param[in] hparam Hash parameter * @param[in] key The key * * @return The RBT hash. */ uint64_t nfs4_owner_rbt_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { state_owner_t *pkey = key->addr; unsigned int sum = 0; unsigned int i = 0; unsigned char c = 0; uint64_t res = 0; /* Compute the sum of all the characters */ for (i = 0; i < pkey->so_owner_len; i++) { c = ((char *)pkey->so_owner_val)[i]; sum += c; } res = (uint64_t) pkey->so_owner.so_nfs4_owner.so_clientid + (uint64_t) sum + pkey->so_owner_len + (uint64_t) pkey->so_type; if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_STATE, "rbt = %" PRIu64, res); return res; } /** * @brief Free an NFS4 owner object * * @param[in] owner Owner to remove */ void free_nfs4_owner(state_owner_t *owner) { state_nfs4_owner_t *nfs4_owner = &owner->so_owner.so_nfs4_owner; if (nfs4_owner->so_related_owner != NULL) dec_state_owner_ref(nfs4_owner->so_related_owner); /* Release the saved response. */ nfs4_Compound_FreeOne(&nfs4_owner->so_resp); /* Remove the owner from the owners per clientid list. */ PTHREAD_MUTEX_lock(&nfs4_owner->so_clientrec->cid_mutex); glist_del(&nfs4_owner->so_perclient); PTHREAD_MUTEX_unlock(&nfs4_owner->so_clientrec->cid_mutex); dec_client_id_ref(nfs4_owner->so_clientrec); } static hash_parameter_t nfs4_owner_param = { .index_size = PRIME_STATE, .hash_func_key = nfs4_owner_value_hash_func, .hash_func_rbt = nfs4_owner_rbt_hash_func, .compare_key = compare_nfs4_owner_key, .key_to_str = display_nfs4_owner_key, .val_to_str = display_nfs4_owner_val, .flags = HT_FLAG_CACHE, }; /** * @brief Init the hashtable for NFSv4 owner cache * * @retval 0 if successful. * @retval -1 if we failed. */ int Init_nfs4_owner(void) { ht_nfs4_owner = hashtable_init(&nfs4_owner_param); if (ht_nfs4_owner == NULL) { LogCrit(COMPONENT_STATE, "Cannot init NFS Open Owner cache"); return -1; } return 0; } /* nfs4_Init_nfs4_owner */ /** * @brief Initialize an NFS4 open owner object * * @param[in] owner The owner record * */ static void init_nfs4_owner(state_owner_t *owner) { state_nfs4_owner_t *nfs4_owner = &owner->so_owner.so_nfs4_owner; glist_init(&nfs4_owner->so_state_list); /* Increment refcount on related owner */ if (nfs4_owner->so_related_owner != NULL) inc_state_owner_ref(nfs4_owner->so_related_owner); /* Increment reference count for clientid record */ inc_client_id_ref(nfs4_owner->so_clientrec); PTHREAD_MUTEX_lock(&nfs4_owner->so_clientrec->cid_mutex); if (owner->so_type == STATE_OPEN_OWNER_NFSV4) { /* If open owner, add to clientid lock owner list */ glist_add_tail(&nfs4_owner->so_clientrec->cid_openowners, &nfs4_owner->so_perclient); } else if (owner->so_type == STATE_LOCK_OWNER_NFSV4) { /* If lock owner, add to clientid open owner list */ glist_add_tail(&nfs4_owner->so_clientrec->cid_lockowners, &nfs4_owner->so_perclient); } PTHREAD_MUTEX_unlock(&nfs4_owner->so_clientrec->cid_mutex); } /** * @brief Display the NFSv4 owner table */ void nfs4_owner_PrintAll(void) { hashtable_log(COMPONENT_STATE, ht_nfs4_owner); } /** * @brief Create an NFSv4 state owner * * @param[in] name Owner name * @param[in] clientid Client record * @param[in] type Owner type * @param[in] related_owner For lock owners, the related open owner * @param[in] init_seqid The starting seqid (for NFSv4.0) * @param[out] pisnew Whether the owner actually is new * @param[in] care Care flag (to unify v3/v4 owners?) * @param[in] confirm Create with it already confirmed? * * @return A new state owner or NULL. */ state_owner_t *create_nfs4_owner(state_nfs4_owner_name_t *name, nfs_client_id_t *clientid, state_owner_type_t type, state_owner_t *related_owner, unsigned int init_seqid, bool_t *pisnew, care_t care, bool_t confirm) { state_owner_t key; state_owner_t *owner; bool_t isnew; /* set up the content of the open_owner */ memset(&key, 0, sizeof(key)); key.so_type = type; key.so_owner.so_nfs4_owner.so_seqid = init_seqid; key.so_owner.so_nfs4_owner.so_related_owner = related_owner; key.so_owner.so_nfs4_owner.so_clientid = clientid->cid_clientid; key.so_owner.so_nfs4_owner.so_clientrec = clientid; key.so_owner_len = name->son_owner_len; key.so_owner_val = name->son_owner_val; key.so_owner.so_nfs4_owner.so_resp.resop = NFS4_OP_ILLEGAL; key.so_owner.so_nfs4_owner.so_args.argop = NFS4_OP_ILLEGAL; key.so_refcount = 1; key.so_owner.so_nfs4_owner.so_confirmed = confirm; if (isFullDebug(COMPONENT_STATE)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_owner(&dspbuf, &key); LogFullDebug(COMPONENT_STATE, "Key=%s", str); } owner = get_state_owner(care, &key, init_nfs4_owner, &isnew); if (owner != NULL && related_owner != NULL) { PTHREAD_MUTEX_lock(&owner->so_mutex); /* Related owner already exists. */ if (owner->so_owner.so_nfs4_owner.so_related_owner == NULL) { /* Attach related owner to owner now that we know it. */ inc_state_owner_ref(related_owner); owner->so_owner.so_nfs4_owner.so_related_owner = related_owner; } else if (owner->so_owner.so_nfs4_owner.so_related_owner != related_owner) { char str1[LOG_BUFF_LEN / 2] = "\0"; char str2[LOG_BUFF_LEN / 2] = "\0"; struct display_buffer dspbuf1 = { sizeof(str1), str1, str1}; struct display_buffer dspbuf2 = { sizeof(str2), str2, str2}; display_owner(&dspbuf1, related_owner); display_owner(&dspbuf2, owner); LogCrit(COMPONENT_NFS_V4_LOCK, "Related {%s} doesn't match for {%s}", str1, str2); PTHREAD_MUTEX_unlock(&owner->so_mutex); /* Release the reference to the owner. */ dec_state_owner_ref(owner); return NULL; } PTHREAD_MUTEX_unlock(&owner->so_mutex); } if (!isnew && owner != NULL && pisnew != NULL) { if (isDebug(COMPONENT_STATE)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_owner(&dspbuf, owner); LogDebug(COMPONENT_STATE, "Previously known owner {%s} is being reused", str); } } if (pisnew != NULL) *pisnew = isnew; return owner; } /** * @brief Fill out an NFSv4 lock conflict * * @param[out] denied NFSv4 LOCK4denied structure * @param[in] holder Holder of the conflicting lock * @param[in] conflict The conflicting lock */ void Process_nfs4_conflict(LOCK4denied *denied, state_owner_t *holder, fsal_lock_param_t *conflict) { /* A conflicting lock from a different lock_owner, * returns NFS4ERR_DENIED */ denied->offset = conflict->lock_start; denied->length = conflict->lock_length; if (conflict->lock_type == FSAL_LOCK_R) denied->locktype = READ_LT; else denied->locktype = WRITE_LT; if (holder != NULL && holder->so_owner_len != 0) { denied->owner.owner.owner_val = gsh_malloc(holder->so_owner_len); denied->owner.owner.owner_len = holder->so_owner_len; memcpy(denied->owner.owner.owner_val, holder->so_owner_val, holder->so_owner_len); } else { denied->owner.owner.owner_len = unknown_owner.so_owner_len; denied->owner.owner.owner_val = unknown_owner.so_owner_val; } LogFullDebug(COMPONENT_STATE, "denied->owner.owner.owner_val = %p", denied->owner.owner.owner_val); if (holder != NULL && holder->so_type == STATE_LOCK_OWNER_NFSV4) denied->owner.clientid = holder->so_owner.so_nfs4_owner.so_clientid; else denied->owner.clientid = 0; /* Release any lock owner reference passed back from SAL */ if (holder != NULL) dec_state_owner_ref(holder); } /** * @brief Release data allocated for LOCK4denied * * @param[in] denied Structure to release */ void Release_nfs4_denied(LOCK4denied *denied) { if (denied->owner.owner.owner_val != unknown_owner.so_owner_val) { gsh_free(denied->owner.owner.owner_val); denied->owner.owner.owner_val = NULL; } } /** * @brief Deep copy a LOCK4denied * * @param[out] denied_dst Target * @param[in] denied_src Source */ void Copy_nfs4_denied(LOCK4denied *denied_dst, LOCK4denied *denied_src) { memcpy(denied_dst, denied_src, sizeof(*denied_dst)); if (denied_src->owner.owner.owner_val != unknown_owner.so_owner_val && denied_src->owner.owner.owner_val != NULL) { denied_dst->owner.owner.owner_val = gsh_malloc(denied_src->owner.owner.owner_len); LogFullDebug(COMPONENT_STATE, "denied_dst->owner.owner.owner_val = %p", denied_dst->owner.owner.owner_val); memcpy(denied_dst->owner.owner.owner_val, denied_src->owner.owner.owner_val, denied_src->owner.owner.owner_len); } if (denied_dst->owner.owner.owner_val == NULL) { denied_dst->owner.owner.owner_len = unknown_owner.so_owner_len; denied_dst->owner.owner.owner_val = unknown_owner.so_owner_val; } } /** * @brief Copy a operation into a state owner * * This is only used for NFSv4.0 and only for a specific subset of * operations for which it guarantees At-Most Once Semantics. * * @param[in,out] owner The owner to hold the operation * @param[in] seqid Seqid of this operation * @param[in] args Arguments of operation to copy * @param[in] data Compound data * @param[in] resp Response to copy * @param[in] tag Arbitrary string for logging/debugging */ void Copy_nfs4_state_req(state_owner_t *owner, seqid4 seqid, nfs_argop4 *args, struct fsal_obj_handle *obj, nfs_resop4 *resp, const char *tag) { /* Simplify use of this function when we may not be keeping any data * for the state owner */ if (owner == NULL) return; LogFullDebug(COMPONENT_STATE, "%s: saving response %p so_seqid %u new seqid %u", tag, owner, owner->so_owner.so_nfs4_owner.so_seqid, seqid); /* Free previous response */ nfs4_Compound_FreeOne(&owner->so_owner.so_nfs4_owner.so_resp); /* Copy new response */ nfs4_Compound_CopyResOne(&owner->so_owner.so_nfs4_owner.so_resp, resp); /** @todo Deep copy OPEN args? * if (owner->so_owner.so_nfs4_owner.so_args.argop == NFS4_OP_OPEN) */ /* Copy bnew args */ memcpy(&owner->so_owner.so_nfs4_owner.so_args, args, sizeof(owner->so_owner.so_nfs4_owner.so_args)); /* Copy new file, note we don't take any reference, so this entry * might not remain valid, but the pointer value suffices here. */ owner->so_owner.so_nfs4_owner.so_last_entry = obj; /** @todo Deep copy OPEN args? * if (args->argop == NFS4_OP_OPEN) */ /* Store new seqid */ owner->so_owner.so_nfs4_owner.so_seqid = seqid; } /** * @brief Check NFS4 request for valid seqid for replay, next request, or * BAD_SEQID. * * Returns true if the request is the next seqid. If the request is a * replay, copies the saved response and returns false. Otherwise, * sets status to NFS4ERR_BAD_SEQID and returns false. * * In either case, on a false return, the caller should send the * resulting response back to the client. * * @param[in] owner Owner to check * @param[in] seqid Seqid to check * @param[in] args Arguments of operation * @param[in] data Compound data * @param[out] resp Cached request, if replay * @param[in] tag Arbitrary string for logging/debugging * * @retval true if the caller should process the operation. * @retval false if the caller should immediately return the provides response. */ bool Check_nfs4_seqid(state_owner_t *owner, seqid4 seqid, nfs_argop4 *args, struct fsal_obj_handle *obj, nfs_resop4 *resp, const char *tag) { seqid4 next; char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; bool str_valid = false; /* Check if any owner to verify seqid against */ if (owner == NULL) { LogFullDebug(COMPONENT_STATE, "%s: Unknown owner doesn't have saved seqid, req seqid %u", tag, seqid); return true; } if (isDebug(COMPONENT_STATE)) { display_owner(&dspbuf, owner); str_valid = true; } /* If this is a new state owner, client may start with any seqid */ if (owner->so_owner.so_nfs4_owner.so_last_entry == NULL) { if (str_valid) LogFullDebug(COMPONENT_STATE, "%s: New {%s} doesn't have saved seqid, req seqid %u", tag, str, seqid); return true; } /* Check for valid next seqid */ next = owner->so_owner.so_nfs4_owner.so_seqid + 1; if (str_valid) LogFullDebug(COMPONENT_STATE, "%s: Check {%s} next %u req seqid %u", tag, str, next, seqid); if (seqid == next) return true; /* All NFS4 responses have the status in the same place, so use any to * set NFS4ERR_BAD_SEQID */ resp->nfs_resop4_u.oplock.status = NFS4ERR_BAD_SEQID; /* Now check for valid replay */ if (owner->so_owner.so_nfs4_owner.so_seqid != seqid) { if (str_valid) LogDebug(COMPONENT_STATE, "%s: Invalid seqid %u in request (not replay), expected seqid for {%s}, returning NFS4ERR_BAD_SEQID", tag, seqid, str); return false; } if (args->argop != owner->so_owner.so_nfs4_owner.so_args.argop) { if (str_valid) LogDebug(COMPONENT_STATE, "%s: Invalid seqid %u in request (not replay - not same op), expected seqid for {%s}, returning NFS4ERR_BAD_SEQID", tag, seqid, str); return false; } if (owner->so_owner.so_nfs4_owner.so_last_entry != obj) { if (str_valid) LogDebug(COMPONENT_STATE, "%s: Invalid seqid %u in request (not replay - wrong file), expected seqid for {%s}, returning NFS4ERR_BAD_SEQID", tag, seqid, str); return false; } /** @todo FSF: add more checks here... */ if (str_valid) LogDebug(COMPONENT_STATE, "%s: Copying saved response for seqid %u into {%s}", tag, seqid, str); /* Copy the saved response and tell caller to use it */ nfs4_Compound_CopyResOne(resp, &owner->so_owner.so_nfs4_owner.so_resp); return false; } /** @} */ nfs-ganesha-2.6.0/src/SAL/nfs4_recovery.c000066400000000000000000000444601324272410200200600ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup SAL State abstraction layer * @{ */ /** * @file nfs4_recovery.c * @brief NFSv4 recovery */ #include "config.h" #include "log.h" #include "nfs_core.h" #include "nfs4.h" #include "sal_functions.h" #include #include #include #include #include "bsd-base64.h" #include "client_mgr.h" #include "fsal.h" time_t current_grace; pthread_mutex_t grace_mutex = PTHREAD_MUTEX_INITIALIZER; /*< Mutex */ struct glist_head clid_list = GLIST_HEAD_INIT(clid_list); /*< Clients */ struct nfs4_recovery_backend *recovery_backend; static int clid_count; /* protected by grace_mutex */ static int32_t reclaim_completes; /* atomic */ static void nfs4_recovery_load_clids(nfs_grace_start_t *gsp); static void nfs_release_nlm_state(char *release_ip); static void nfs_release_v4_client(char *ip); clid_entry_t *nfs4_add_clid_entry(char *cl_name) { clid_entry_t *new_ent = gsh_malloc(sizeof(clid_entry_t)); glist_init(&new_ent->cl_rfh_list); strcpy(new_ent->cl_name, cl_name); new_ent->cl_reclaim_complete = false; glist_add(&clid_list, &new_ent->cl_list); ++clid_count; return new_ent; } rdel_fh_t *nfs4_add_rfh_entry(clid_entry_t *clid_ent, char *rfh_name) { rdel_fh_t *new_ent = gsh_malloc(sizeof(rdel_fh_t)); new_ent->rdfh_handle_str = gsh_strdup(rfh_name); glist_add(&clid_ent->cl_rfh_list, &new_ent->rdfh_list); return new_ent; } void nfs4_cleanup_clid_entrys(void) { struct clid_entry *clid_entry; /* when not doing a takeover, start with an empty list */ while ((clid_entry = glist_first_entry(&clid_list, struct clid_entry, cl_list)) != NULL) { glist_del(&clid_entry->cl_list); gsh_free(clid_entry); --clid_count; } } /** * Lift the grace period, if current_grace has not changed since we last * checked it. If something has changed in the interim, then don't do * anything. Either someone has set a new grace period, or someone else * beat us to lifting this one. */ static void nfs_lift_grace_locked(time_t current) { /* * Caller must hold grace_mutex. Only the thread that actually sets * the value to 0 gets to clean up the recovery db. */ if (atomic_fetch_time_t(¤t_grace) == current) { nfs4_recovery_cleanup(); __sync_synchronize(); atomic_store_time_t(¤t_grace, (time_t)0); LogEvent(COMPONENT_STATE, "NFS Server Now NOT IN GRACE"); } } /** * @brief Start grace period * * This routine can be called due to server start/restart or from * failover code. If this node is taking over for a node, that nodeid * will be passed to this routine inside of the grace start structure. * * @param[in] gsp Grace period start information */ void nfs_start_grace(nfs_grace_start_t *gsp) { bool was_grace; PTHREAD_MUTEX_lock(&grace_mutex); if (nfs_param.nfsv4_param.graceless) { nfs_lift_grace_locked(atomic_fetch_time_t(¤t_grace)); LogEvent(COMPONENT_STATE, "NFS Server skipping GRACE (Graceless is true)"); goto out; } /* grace should always be greater than or equal to lease time, * some clients are known to have problems with grace greater than 60 * seconds Lease_Lifetime should be set to a smaller value for those * setups. * * Checks against the grace period are lockless, so we want to ensure * that the callers see the * Full barrier to ensure enforcement begins ASAP. */ was_grace = (bool)atomic_fetch_time_t(¤t_grace); atomic_store_time_t(¤t_grace, time(NULL)); __sync_synchronize(); if ((int)nfs_param.nfsv4_param.grace_period < (int)nfs_param.nfsv4_param.lease_lifetime) { LogWarn(COMPONENT_STATE, "NFS Server GRACE duration should at least match LEASE period. Current configured values are GRACE(%d), LEASE(%d)", (int)nfs_param.nfsv4_param.grace_period, (int)nfs_param.nfsv4_param.lease_lifetime); } LogEvent(COMPONENT_STATE, "NFS Server Now IN GRACE, duration %d", (int)nfs_param.nfsv4_param.grace_period); /* * If we're just starting the grace period, then load the * clid database. Don't load it however if we're extending the * existing grace period. */ if (!gsp && !was_grace) { nfs4_recovery_load_clids(NULL); } else if (gsp && gsp->event != EVENT_JUST_GRACE) { /* * if called from failover code and given a nodeid, then this * node is doing a take over. read in the client ids from the * failing node. */ LogEvent(COMPONENT_STATE, "NFS Server recovery event %d nodeid %d ip %s", gsp->event, gsp->nodeid, gsp->ipaddr); if (gsp->event == EVENT_CLEAR_BLOCKED) cancel_all_nlm_blocked(); else { nfs_release_nlm_state(gsp->ipaddr); if (gsp->event == EVENT_RELEASE_IP) nfs_release_v4_client(gsp->ipaddr); else nfs4_recovery_load_clids(gsp); } } out: PTHREAD_MUTEX_unlock(&grace_mutex); } /** * @brief Check if we are in the grace period * * @retval true if so. * @retval false if not. */ bool nfs_in_grace(void) { return atomic_fetch_time_t(¤t_grace); } void nfs_try_lift_grace(void) { bool in_grace = true; int32_t rc_count = 0; time_t current = atomic_fetch_time_t(¤t_grace); if (!current) return; /* * If we know there are no NLM clients, then we can consider the grace * period done when all previous clients have sent a RECLAIM_COMPLETE. */ rc_count = atomic_fetch_int32_t(&reclaim_completes); if (!nfs_param.core_param.enable_NLM) in_grace = (rc_count != clid_count); /* Otherwise, wait for the timeout */ if (in_grace) in_grace = ((current + nfs_param.nfsv4_param.grace_period) > time(NULL)); /* * Can we lift the grace period now? If so, take the grace_mutex and * try to do it. */ if (!in_grace) { PTHREAD_MUTEX_lock(&grace_mutex); nfs_lift_grace_locked(current); PTHREAD_MUTEX_unlock(&grace_mutex); } } /** * @brief Create an entry in the recovery directory * * This entry allows the client to reclaim state after a server * reboot/restart. * * @param[in] clientid Client record */ void nfs4_add_clid(nfs_client_id_t *clientid) { PTHREAD_MUTEX_lock(&clientid->cid_mutex); recovery_backend->add_clid(clientid); PTHREAD_MUTEX_unlock(&clientid->cid_mutex); } /** * @brief Remove a client entry from the recovery directory * * This function would be called when a client expires. * */ void nfs4_rm_clid(nfs_client_id_t *clientid) { PTHREAD_MUTEX_lock(&clientid->cid_mutex); recovery_backend->rm_clid(clientid); PTHREAD_MUTEX_unlock(&clientid->cid_mutex); } static bool check_clid(nfs_client_id_t *clientid, clid_entry_t *clid_ent) { bool ret = false; PTHREAD_MUTEX_lock(&clientid->cid_mutex); LogDebug(COMPONENT_CLIENTID, "compare %s to %s", clientid->cid_recov_tag, clid_ent->cl_name); if (clientid->cid_recov_tag && !strncmp(clientid->cid_recov_tag, clid_ent->cl_name, PATH_MAX)) ret = true; PTHREAD_MUTEX_unlock(&clientid->cid_mutex); return ret; } void nfs4_recovery_reclaim_complete(nfs_client_id_t *clientid) { struct glist_head *node; /* If there are no clients */ if (clid_count == 0) return; /* * Loop through the list and try to find a matching client that hasn't * yet sent a reclaim_complete. If we find it, mark its clid_ent * record, and increment the reclaim_completes counter. */ PTHREAD_MUTEX_lock(&grace_mutex); glist_for_each(node, &clid_list) { clid_entry_t *clid_ent = glist_entry(node, clid_entry_t, cl_list); /* Skip any that have already sent a reclaim_complete */ if (clid_ent->cl_reclaim_complete) continue; if (check_clid(clientid, clid_ent)) { if (isDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = { sizeof(str), str, str}; display_client_id_rec(&dspbuf, clientid); LogFullDebug(COMPONENT_CLIENTID, "RECLAIM_COMPLETE for %s", str); } clid_ent->cl_reclaim_complete = true; atomic_inc_int32_t(&reclaim_completes); break; } } PTHREAD_MUTEX_unlock(&grace_mutex); } /** * @brief Determine whether or not this client may reclaim state * * If the server is not in grace period, then no reclaim can happen. * * @param[in] clientid Client record */ void nfs4_chk_clid_impl(nfs_client_id_t *clientid, clid_entry_t **clid_ent_arg) { struct glist_head *node; clid_entry_t *clid_ent; *clid_ent_arg = NULL; LogDebug(COMPONENT_CLIENTID, "chk for %lu", clientid->cid_clientid); /* If there were no clients at time of restart, we're done */ if (clid_count == 0) return; /* * loop through the list and try to find this client. if we * find it, mark it to allow reclaims. perhaps the client should * be removed from the list at this point to make the list shorter? */ glist_for_each(node, &clid_list) { clid_ent = glist_entry(node, clid_entry_t, cl_list); if (check_clid(clientid, clid_ent)) { if (isDebug(COMPONENT_CLIENTID)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = { sizeof(str), str, str}; display_client_id_rec(&dspbuf, clientid); LogFullDebug(COMPONENT_CLIENTID, "Allowed to reclaim ClientId %s", str); } clientid->cid_allow_reclaim = true; *clid_ent_arg = clid_ent; return; } } } void nfs4_chk_clid(nfs_client_id_t *clientid) { clid_entry_t *dummy_clid_ent; /* If we aren't in grace period, then reclaim is not possible */ if (!nfs_in_grace()) return; PTHREAD_MUTEX_lock(&grace_mutex); nfs4_chk_clid_impl(clientid, &dummy_clid_ent); PTHREAD_MUTEX_unlock(&grace_mutex); } /** * @brief Load clients for recovery * * @param[in] nodeid Node, on takeover * * Caller must hold grace_mutex. */ static void nfs4_recovery_load_clids(nfs_grace_start_t *gsp) { LogDebug(COMPONENT_STATE, "Load recovery cli %p", gsp); /* A NULL gsp pointer indicates an initial startup grace period */ if (gsp == NULL) nfs4_cleanup_clid_entrys(); recovery_backend->recovery_read_clids(gsp, nfs4_add_clid_entry, nfs4_add_rfh_entry); } static int load_backend(const char *name) { if (!strcmp(name, "fs")) fs_backend_init(&recovery_backend); #ifdef USE_RADOS_RECOV else if (!strcmp(name, "rados_kv")) rados_kv_backend_init(&recovery_backend); else if (!strcmp(name, "rados_ng")) rados_ng_backend_init(&recovery_backend); #endif else if (!strcmp(name, "fs_ng")) fs_ng_backend_init(&recovery_backend); else return -1; return 0; } /** * @brief Create the recovery directory * * The recovery directory may not exist yet, so create it. This * should only need to be done once (if at all). Also, the location * of the directory could be configurable. */ void nfs4_recovery_init(void) { if (load_backend(nfs_param.nfsv4_param.recovery_backend)) { LogCrit(COMPONENT_CLIENTID, "Unknown recovery backend"); return; } recovery_backend->recovery_init(); } /** * @brief Clean up recovery directory */ void nfs4_recovery_cleanup(void) { recovery_backend->recovery_cleanup(); } /** * @brief Record revoked filehandle under the client. * * @param[in] clientid Client record * @param[in] filehandle of the revoked file. */ void nfs4_record_revoke(nfs_client_id_t *delr_clid, nfs_fh4 *delr_handle) { /* A client's lease is reserved while recalling or revoking a * delegation which means the client will not expire until we * complete this revoke operation. The only exception is when * the reaper thread revokes delegations of an already expired * client! */ PTHREAD_MUTEX_lock(&delr_clid->cid_mutex); if (delr_clid->cid_confirmed == EXPIRED_CLIENT_ID) { /* Called from reaper thread, no need to record * revoked file handles for an expired client. */ PTHREAD_MUTEX_unlock(&delr_clid->cid_mutex); return; } recovery_backend->add_revoke_fh(delr_clid, delr_handle); PTHREAD_MUTEX_unlock(&delr_clid->cid_mutex); } /** * @brief Decides if it is allowed to reclaim a given delegation * * @param[in] clientid Client record * @param[in] filehandle of the revoked file. * @retval true if allowed and false if not. * */ bool nfs4_check_deleg_reclaim(nfs_client_id_t *clid, nfs_fh4 *fhandle) { char rhdlstr[NAME_MAX]; struct glist_head *node; rdel_fh_t *rfh_entry; clid_entry_t *clid_ent; int b64ret; bool retval = true; /* If we aren't in grace period, then reclaim is not possible */ if (!nfs_in_grace()) return false; /* Convert nfs_fh4_val into base64 encoded string */ b64ret = base64url_encode(fhandle->nfs_fh4_val, fhandle->nfs_fh4_len, rhdlstr, sizeof(rhdlstr)); assert(b64ret != -1); PTHREAD_MUTEX_lock(&grace_mutex); nfs4_chk_clid_impl(clid, &clid_ent); if (clid_ent) { glist_for_each(node, &clid_ent->cl_rfh_list) { rfh_entry = glist_entry(node, rdel_fh_t, rdfh_list); assert(rfh_entry != NULL); if (!strcmp(rhdlstr, rfh_entry->rdfh_handle_str)) { LogFullDebug(COMPONENT_CLIENTID, "Can't reclaim revoked fh:%s", rfh_entry->rdfh_handle_str); retval = false; break; } } } PTHREAD_MUTEX_unlock(&grace_mutex); LogFullDebug(COMPONENT_CLIENTID, "Returning %s", retval ? "TRUE" : "FALSE"); return retval; } #ifdef _USE_NLM /** * @brief Release NLM state */ static void nlm_releasecall(struct fridgethr_context *ctx) { state_nsm_client_t *nsm_cp; state_status_t err; nsm_cp = ctx->arg; err = state_nlm_notify(nsm_cp, false, 0); if (err != STATE_SUCCESS) LogDebug(COMPONENT_STATE, "state_nlm_notify failed with %d", err); dec_nsm_client_ref(nsm_cp); } #endif /* _USE_NLM */ void extractv4(char *ipv6, char *ipv4) { char *token, *saveptr; char *delim = ":"; token = strtok_r(ipv6, delim, &saveptr); while (token != NULL) { /* IPv4 delimiter is '.' */ if (strchr(token, '.') != NULL) { (void)strcpy(ipv4, token); return; } token = strtok_r(NULL, delim, &saveptr); } /* failed, copy a null string */ (void)strcpy(ipv4, ""); } bool ip_str_match(char *release_ip, char *server_ip) { bool ripv6, sipv6; char ipv4[SOCK_NAME_MAX + 1]; /* IPv6 delimiter is ':' */ ripv6 = (strchr(release_ip, ':') != NULL); sipv6 = (strchr(server_ip, ':') != NULL); if (ripv6) { if (sipv6) return !strcmp(release_ip, server_ip); else { /* extract v4 addr from release_ip*/ extractv4(release_ip, ipv4); return !strcmp(ipv4, server_ip); } } else { if (sipv6) { /* extract v4 addr from server_ip*/ extractv4(server_ip, ipv4); return !strcmp(ipv4, release_ip); } } /* Both are ipv4 addresses */ return !strcmp(release_ip, server_ip); } /** * @brief Release all NLM state */ static void nfs_release_nlm_state(char *release_ip) { #ifdef _USE_NLM hash_table_t *ht = ht_nlm_client; state_nlm_client_t *nlm_cp; state_nsm_client_t *nsm_cp; struct rbt_head *head_rbt; struct rbt_node *pn; struct hash_data *pdata; state_status_t state_status; char serverip[SOCK_NAME_MAX + 1]; int i; LogDebug(COMPONENT_STATE, "Release all NLM locks"); cancel_all_nlm_blocked(); /* walk the client list and call state_nlm_notify */ for (i = 0; i < ht->parameter.index_size; i++) { PTHREAD_RWLOCK_wrlock(&ht->partitions[i].lock); head_rbt = &ht->partitions[i].rbt; /* go through all entries in the red-black-tree */ RBT_LOOP(head_rbt, pn) { pdata = RBT_OPAQ(pn); nlm_cp = (state_nlm_client_t *) pdata->val.addr; sprint_sockip(&(nlm_cp->slc_server_addr), serverip, SOCK_NAME_MAX + 1); if (ip_str_match(release_ip, serverip)) { nsm_cp = nlm_cp->slc_nsm_client; inc_nsm_client_ref(nsm_cp); state_status = fridgethr_submit( state_async_fridge, nlm_releasecall, nsm_cp); if (state_status != STATE_SUCCESS) { dec_nsm_client_ref(nsm_cp); LogCrit(COMPONENT_STATE, "failed to submit nlm release thread "); } } RBT_INCREMENT(pn); } PTHREAD_RWLOCK_unlock(&ht->partitions[i].lock); } #endif /* _USE_NLM */ } static int ip_match(char *ip, nfs_client_id_t *cid) { LogDebug(COMPONENT_STATE, "NFS Server V4 match ip %s with (%s)", ip, cid->cid_client_record->cr_client_val); if (strlen(ip) == 0) /* No IP all are matching */ return 1; if (strstr(cid->cid_client_record->cr_client_val, ip) != NULL) return 1; return 0; /* no match */ } /* * try to find a V4 client that matches the IP we are releasing. * only search the confirmed clients, unconfirmed clients won't * have any state to release. */ static void nfs_release_v4_client(char *ip) { hash_table_t *ht = ht_confirmed_client_id; struct rbt_head *head_rbt; struct rbt_node *pn; struct hash_data *pdata; nfs_client_id_t *cp; nfs_client_record_t *recp; int i; LogEvent(COMPONENT_STATE, "NFS Server V4 recovery release ip %s", ip); /* go through the confirmed clients looking for a match */ for (i = 0; i < ht->parameter.index_size; i++) { PTHREAD_RWLOCK_wrlock(&ht->partitions[i].lock); head_rbt = &ht->partitions[i].rbt; /* go through all entries in the red-black-tree */ RBT_LOOP(head_rbt, pn) { pdata = RBT_OPAQ(pn); cp = (nfs_client_id_t *) pdata->val.addr; PTHREAD_MUTEX_lock(&cp->cid_mutex); if ((cp->cid_confirmed == CONFIRMED_CLIENT_ID) && ip_match(ip, cp)) { inc_client_id_ref(cp); /* Take a reference to the client record * before we drop cid_mutex. client record * may be decoupled, so check if it is still * coupled! */ recp = cp->cid_client_record; if (recp) inc_client_record_ref(recp); PTHREAD_MUTEX_unlock(&cp->cid_mutex); PTHREAD_RWLOCK_unlock(&ht->partitions[i].lock); /* nfs_client_id_expire requires cr_mutex * if not decoupled alread */ if (recp) PTHREAD_MUTEX_lock(&recp->cr_mutex); nfs_client_id_expire(cp, true); if (recp) { PTHREAD_MUTEX_unlock(&recp->cr_mutex); dec_client_record_ref(recp); } dec_client_id_ref(cp); return; } else { PTHREAD_MUTEX_unlock(&cp->cid_mutex); } RBT_INCREMENT(pn); } PTHREAD_RWLOCK_unlock(&ht->partitions[i].lock); } } /** @} */ nfs-ganesha-2.6.0/src/SAL/nfs4_state.c000066400000000000000000000733741324272410200173500ustar00rootroot00000000000000/* vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup SAL State abstraction layer * @{ */ /** * @file nfs4_state.c * @brief NFSv4 state functions. */ #include "config.h" #include #include #include #include #include #include #include #include "log.h" #include "hashtable.h" #include "nfs_core.h" #include "nfs4.h" #include "fsal.h" #include "sal_functions.h" #include "export_mgr.h" #include "fsal_up.h" #include "nfs_file_handle.h" #include "nfs_proto_tools.h" #ifdef USE_LTTNG #include "gsh_lttng/state.h" #endif #ifdef DEBUG_SAL struct glist_head state_v4_all = GLIST_HEAD_INIT(state_v4_all); pthread_mutex_t all_state_v4_mutex = PTHREAD_MUTEX_INITIALIZER; #endif /** * @brief adds a new state to a file * * This version of the function does not take the state lock on the * entry. It exists to allow callers to integrate state into a larger * operation. * * The caller may have already allocated a state, in which case state * need not be NULL. * * @note state_lock MUST be held for write * * @param[in,out] obj file to operate on * @param[in] state_type State to be defined * @param[in] state_data Data related to this state * @param[in] owner_input Related open_owner * @param[in,out] state The new state * @param[in] refer Reference to compound creating state * * @return Operation status */ state_status_t _state_add_impl(struct fsal_obj_handle *obj, enum state_type state_type, union state_data *state_data, state_owner_t *owner_input, state_t **state, struct state_refer *refer, const char *func, int line) { state_t *pnew_state = *state; struct state_hdl *ostate = obj->state_hdl; char str[DISPLAY_STATEID_OTHER_SIZE] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; bool str_valid = false; bool got_export_ref = false; state_status_t status = 0; bool mutex_init = false; struct state_t *openstate = NULL; if (isFullDebug(COMPONENT_STATE) && pnew_state != NULL) { display_stateid(&dspbuf, pnew_state); LogFullDebug(COMPONENT_STATE, "pnew_state=%s", str); display_reset_buffer(&dspbuf); } /* Attempt to get a reference to the export. */ if (!export_ready(op_ctx->ctx_export)) { /* If we could not get a reference, return stale. * Release attr_lock */ LogDebug(COMPONENT_STATE, "Stale export"); status = STATE_ESTALE; goto errout; } get_gsh_export_ref(op_ctx->ctx_export); got_export_ref = true; if (pnew_state == NULL) { if (state_type == STATE_TYPE_LOCK) openstate = state_data->lock.openstate; pnew_state = op_ctx->fsal_export->exp_ops.alloc_state( op_ctx->fsal_export, state_type, openstate); } PTHREAD_MUTEX_init(&pnew_state->state_mutex, NULL); mutex_init = true; /* Add the stateid.other, this will increment cid_stateid_counter */ nfs4_BuildStateId_Other( owner_input->so_owner.so_nfs4_owner.so_clientrec, pnew_state->stateid_other); /* Set the type and data for this state */ memcpy(&(pnew_state->state_data), state_data, sizeof(*state_data)); pnew_state->state_type = state_type; pnew_state->state_seqid = 0; /* will be incremented to 1 later */ pnew_state->state_refcount = 2; /* sentinel plus returned ref */ if (refer) pnew_state->state_refer = *refer; if (isFullDebug(COMPONENT_STATE)) { display_stateid_other(&dspbuf, pnew_state->stateid_other); str_valid = true; LogFullDebug(COMPONENT_STATE, "About to call nfs4_State_Set for %s", str); } glist_init(&pnew_state->state_list); /* We need to initialize state_owner, state_export, and state_obj now so * that the state can be indexed by owner/entry. We don't insert into * lists and take references yet since no one else can see this state * until we are completely done since we hold the state_lock. Might as * well grab export now also... */ pnew_state->state_export = op_ctx->ctx_export; pnew_state->state_owner = owner_input; pnew_state->state_obj = obj; /* Add the state to the related hashtable */ status = nfs4_State_Set(pnew_state); switch (status) { case STATE_SUCCESS: break; default: if (!str_valid) display_stateid_other(&dspbuf, pnew_state->stateid_other); LogCrit(COMPONENT_STATE, "Can't create a new state id %s for the obj %p (F)", str, obj); goto errout; } /* Each of the following blocks takes the state_mutex and releases it * because we always want state_mutex to be the last lock taken. * * NOTE: We don't have to worry about state_del/state_del_locked being * called in the midst of things because the state_lock is held. */ /* Attach this to an export */ PTHREAD_RWLOCK_wrlock(&op_ctx->ctx_export->lock); PTHREAD_MUTEX_lock(&pnew_state->state_mutex); glist_add_tail(&op_ctx->ctx_export->exp_state_list, &pnew_state->state_export_list); PTHREAD_MUTEX_unlock(&pnew_state->state_mutex); PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock); /* Add state to list for file */ PTHREAD_MUTEX_lock(&pnew_state->state_mutex); glist_add_tail(&ostate->file.list_of_states, &pnew_state->state_list); /* Get ref for this state entry */ obj->obj_ops.get_ref(obj); PTHREAD_MUTEX_unlock(&pnew_state->state_mutex); #ifdef USE_LTTNG tracepoint(state, add, func, line, obj, pnew_state); #endif /* Add state to list for owner */ PTHREAD_MUTEX_lock(&owner_input->so_mutex); PTHREAD_MUTEX_lock(&pnew_state->state_mutex); inc_state_owner_ref(owner_input); glist_add_tail(&owner_input->so_owner.so_nfs4_owner.so_state_list, &pnew_state->state_owner_list); PTHREAD_MUTEX_unlock(&pnew_state->state_mutex); PTHREAD_MUTEX_unlock(&owner_input->so_mutex); #ifdef DEBUG_SAL PTHREAD_MUTEX_lock(&all_state_v4_mutex); glist_add_tail(&state_v4_all, &pnew_state->state_list_all); PTHREAD_MUTEX_unlock(&all_state_v4_mutex); #endif if (pnew_state->state_type == STATE_TYPE_DELEG && pnew_state->state_data.deleg.sd_type == OPEN_DELEGATE_WRITE) ostate->file.write_delegated = true; /* Copy the result */ *state = pnew_state; if (str_valid) LogFullDebug(COMPONENT_STATE, "Add State: %p: %s", pnew_state, str); /* Regular exit */ status = STATE_SUCCESS; return status; errout: if (mutex_init) PTHREAD_MUTEX_destroy(&pnew_state->state_mutex); if (pnew_state != NULL) { /* Make sure the new state is closed (may have been passed in * with file open). */ (void) obj->obj_ops.close2(obj, pnew_state); pnew_state->state_exp->exp_ops.free_state(pnew_state->state_exp, pnew_state); } if (got_export_ref) put_gsh_export(op_ctx->ctx_export); *state = NULL; return status; } /* state_add */ /** * @brief Adds a new state to a file * * @param[in,out] obj File to operate on * @param[in] state_type State to be defined * @param[in] state_data Data related to this state * @param[in] owner_input Related open_owner * @param[out] state The new state * @param[in] refer Reference to compound creating state * * @return Operation status */ state_status_t _state_add(struct fsal_obj_handle *obj, enum state_type state_type, union state_data *state_data, state_owner_t *owner_input, state_t **state, struct state_refer *refer, const char *func, int line) { state_status_t status = 0; /* Ensure that states are are associated only with the appropriate owners */ if (((state_type == STATE_TYPE_SHARE) && (owner_input->so_type != STATE_OPEN_OWNER_NFSV4)) || ((state_type == STATE_TYPE_LOCK) && (owner_input->so_type != STATE_LOCK_OWNER_NFSV4)) || (((state_type == STATE_TYPE_DELEG) || (state_type == STATE_TYPE_LAYOUT)) && (owner_input->so_type != STATE_CLIENTID_OWNER_NFSV4))) { return STATE_BAD_TYPE; } PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); status = _state_add_impl(obj, state_type, state_data, owner_input, state, refer, func, line); PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); return status; } /** * @brief Remove a state from a file * * @note The state_lock MUST be held for write. * * @param[in] state The state to remove * */ void _state_del_locked(state_t *state, const char *func, int line) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; bool str_valid = false; struct fsal_obj_handle *obj; struct gsh_export *export; state_owner_t *owner; if (isDebug(COMPONENT_STATE)) { display_stateid(&dspbuf, state); str_valid = true; } /* Remove the entry from the HashTable. If it fails, we have lost the * race with another caller of state_del/state_del_locked. */ if (!nfs4_State_Del(state)) { if (str_valid) LogDebug(COMPONENT_STATE, "Racing to delete %s", str); return; } if (str_valid) LogFullDebug(COMPONENT_STATE, "Deleting %s", str); /* Protect extraction of all the referenced objects, we don't * actually need to test them or take references because we assure * that there is exactly one state_del_locked call that proceeds * this far, and thus if the refereces were non-NULL, they must still * be good. Holding the mutex is not strictly necessary for this * reason, however, static and dynamic code analysis have no way of * knowing this reference is safe. In addition, get_state_obj_ref() * would have taken the mutex anyway. */ PTHREAD_MUTEX_lock(&state->state_mutex); obj = get_state_obj_ref_locked(state); if (obj == NULL) { LogDebug(COMPONENT_STATE, "Entry for state is stale"); PTHREAD_MUTEX_unlock(&state->state_mutex); return; } #ifdef USE_LTTNG tracepoint(state, delete, func, line, obj, state); #endif export = state->state_export; owner = state->state_owner; PTHREAD_MUTEX_unlock(&state->state_mutex); /* Don't cleanup when ref is dropped, as this could recurse into here. * Caller must have a ref anyway. */ obj->state_hdl->no_cleanup = true; /* Remove from the list of states for a particular file */ PTHREAD_MUTEX_lock(&state->state_mutex); glist_del(&state->state_list); /* Put ref for this state entry */ obj->obj_ops.put_ref(obj); state->state_obj = NULL; PTHREAD_MUTEX_unlock(&state->state_mutex); /* We need to close the state at this point. The state will * eventually be freed and it must be closed before free. This * is the last point we have a valid reference to the object * handle. */ (void) obj->obj_ops.close2(obj, state); if (owner != NULL) { bool owner_retain = false; struct state_nfs4_owner_t *nfs4_owner; nfs4_owner = &owner->so_owner.so_nfs4_owner; /* Remove from list of states owned by owner and * release the state owner reference. */ PTHREAD_MUTEX_lock(&owner->so_mutex); PTHREAD_MUTEX_lock(&state->state_mutex); glist_del(&state->state_owner_list); state->state_owner = NULL; /* If we are dropping the last open state from an open * owner, we will want to retain a refcount and let the * reaper thread clean up with owner. * * @todo: should we make the following glist_null check * an assert or remove it altogether? */ owner_retain = owner->so_type == STATE_OPEN_OWNER_NFSV4 && glist_empty(&nfs4_owner->so_state_list) && glist_null(&nfs4_owner->so_cache_entry); PTHREAD_MUTEX_unlock(&state->state_mutex); if (owner_retain) { /* Retain the reference held by the state, and track * when this owner was last closed. */ PTHREAD_MUTEX_lock(&cached_open_owners_lock); atomic_store_time_t(&nfs4_owner->so_cache_expire, nfs_param.nfsv4_param.lease_lifetime + time(NULL)); glist_add_tail(&cached_open_owners, &nfs4_owner->so_cache_entry); if (isFullDebug(COMPONENT_STATE)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = { sizeof(str), str, str}; display_owner(&dspbuf, owner); LogFullDebug(COMPONENT_STATE, "Caching open owner {%s}", str); } PTHREAD_MUTEX_unlock(&cached_open_owners_lock); PTHREAD_MUTEX_unlock(&owner->so_mutex); } else { /* Drop the reference held by the state. */ PTHREAD_MUTEX_unlock(&owner->so_mutex); dec_state_owner_ref(owner); } } /* Remove from the list of lock states for a particular open state. * This is safe to do without any special checks. If we are not on * the list, glist_del does nothing, and the state_lock protects the * open state's state_sharelist. */ if (state->state_type == STATE_TYPE_LOCK) glist_del(&state->state_data.lock.state_sharelist); /* Reset write delegated if this is a write delegation */ if (state->state_type == STATE_TYPE_DELEG && state->state_data.deleg.sd_type == OPEN_DELEGATE_WRITE) obj->state_hdl->file.write_delegated = false; /* Remove from list of states for a particular export. * In this case, it is safe to look at state_export without yet * holding the state_mutex because this is the only place where it * is removed, and we have guaranteed we are the only thread * proceeding with state deletion. */ PTHREAD_RWLOCK_wrlock(&export->lock); PTHREAD_MUTEX_lock(&state->state_mutex); glist_del(&state->state_export_list); state->state_export = NULL; PTHREAD_MUTEX_unlock(&state->state_mutex); PTHREAD_RWLOCK_unlock(&export->lock); put_gsh_export(export); #ifdef DEBUG_SAL PTHREAD_MUTEX_lock(&all_state_v4_mutex); glist_del(&state->state_list_all); PTHREAD_MUTEX_unlock(&all_state_v4_mutex); #endif /* Remove the sentinel reference */ dec_state_t_ref(state); obj->obj_ops.put_ref(obj); /* Can cleanup now */ obj->state_hdl->no_cleanup = false; } /** * @brief Delete a state * * @param[in] state State to delete * */ void state_del(state_t *state) { struct fsal_obj_handle *obj = get_state_obj_ref(state); if (obj == NULL) { LogDebug(COMPONENT_STATE, "Entry for state is stale"); return; } PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); state_del_locked(state); PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); obj->obj_ops.put_ref(obj); } /** * @brief Get references to the various objects a state_t points to. * * @param[in] state The state_t to get references from * @param[in,out] obj Place to return the owning object (NULL if not desired) * @param[in,out] export Place to return the export (NULL if not desired) * @param[in,out] owner Place to return the owner (NULL if not desired) * * @retval true if all desired references were taken * @retval flase otherwise (in which case no references are taken) * * For convenience, returns false if state is NULL which helps simplify * code for some callers. */ bool get_state_obj_export_owner_refs(state_t *state, struct fsal_obj_handle **obj, struct gsh_export **export, state_owner_t **owner) { if (obj != NULL) *obj = NULL; if (export != NULL) *export = NULL; if (owner != NULL) *owner = NULL; if (state == NULL) return false; PTHREAD_MUTEX_lock(&state->state_mutex); LogFullDebug(COMPONENT_STATE, "state %p state_obj %p state_export %p state_owner %p", state, &state->state_obj, state->state_export, state->state_owner); if (obj != NULL) { *obj = get_state_obj_ref_locked(state); if ((*obj) == NULL) goto fail; } if (export != NULL) { if (state->state_export != NULL && export_ready(state->state_export)) { get_gsh_export_ref(state->state_export); *export = state->state_export; } else goto fail; } if (owner != NULL) { if (state->state_owner != NULL) { *owner = state->state_owner; inc_state_owner_ref(*owner); } else { goto fail; } } PTHREAD_MUTEX_unlock(&state->state_mutex); return true; fail: PTHREAD_MUTEX_unlock(&state->state_mutex); if (obj != NULL && *obj != NULL) { *obj = NULL; } if (export != NULL && *export != NULL) { put_gsh_export(*export); *export = NULL; } if (owner != NULL && *owner != NULL) { dec_state_owner_ref(*owner); *owner = NULL; } return false; } /** * @brief Remove all state from a file * * Used by cache_inode_kill_entry in the event that the FSAL says a * handle is stale. * * @note state_lock MUST be held for write * * @param[in,out] ostate File state to wipe */ void state_nfs4_state_wipe(struct state_hdl *ostate) { struct glist_head *glist, *glistn; state_t *state = NULL; if (glist_empty(&ostate->file.list_of_states)) return; glist_for_each_safe(glist, glistn, &ostate->file.list_of_states) { state = glist_entry(glist, state_t, state_list); if (state->state_type > STATE_TYPE_LAYOUT) continue; state_del_locked(state); } } /** * @brief Remove every state belonging to the lock owner. * * @param[in] lock_owner Lock owner to release */ enum nfsstat4 release_lock_owner(state_owner_t *owner) { PTHREAD_MUTEX_lock(&owner->so_mutex); if (!glist_empty(&owner->so_lock_list)) { PTHREAD_MUTEX_unlock(&owner->so_mutex); return NFS4ERR_LOCKS_HELD; } if (isDebug(COMPONENT_STATE)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_owner(&dspbuf, owner); LogDebug(COMPONENT_STATE, "Removing state for %s", str); } while (true) { state_t *state; struct fsal_export *save_exp; struct gsh_export *save_export; state = glist_first_entry(&owner->so_owner.so_nfs4_owner .so_state_list, state_t, state_owner_list); if (state == NULL) { PTHREAD_MUTEX_unlock(&owner->so_mutex); return NFS4_OK; } /* Make sure the state doesn't go away on us... */ inc_state_t_ref(state); PTHREAD_MUTEX_unlock(&owner->so_mutex); /* Set the fsal_export properly, since this can be called from * ops that don't do a putfh */ save_exp = op_ctx->fsal_export; save_export = op_ctx->ctx_export; op_ctx->fsal_export = state->state_exp; op_ctx->ctx_export = state->state_export; state_del(state); /* Restore export */ op_ctx->fsal_export = save_exp; op_ctx->ctx_export = save_export; dec_state_t_ref(state); PTHREAD_MUTEX_lock(&owner->so_mutex); } } /** * @brief Remove all state belonging to the open owner. * * @param[in,out] open_owner Open owner */ void release_openstate(state_owner_t *owner) { int errcnt = 0; bool ok; struct state_nfs4_owner_t *nfs4_owner = &owner->so_owner.so_nfs4_owner; if (isFullDebug(COMPONENT_STATE)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_owner(&dspbuf, owner); LogFullDebug(COMPONENT_STATE, "Release {%s}", str); } /* Only accept so many errors before giving up. */ while (errcnt < STATE_ERR_MAX) { state_t *state; struct fsal_obj_handle *obj = NULL; struct gsh_export *export = NULL; PTHREAD_MUTEX_lock(&owner->so_mutex); if (atomic_fetch_time_t(&nfs4_owner->so_cache_expire) != 0) { /* This owner has no state, it is a cached open owner. * Take cached_open_owners_lock and verify. * * We have to check every iteration since the state * list may have become empty and we are now cached. */ PTHREAD_MUTEX_lock(&cached_open_owners_lock); if (atomic_fetch_time_t(&nfs4_owner->so_cache_expire) != 0) { /* We aren't racing with the reaper thread or * with get_state_owner. * * NOTE: We could be called from the reaper * thead or this could be a clientid * expire due to SETCLIENTID. */ /* This cached owner has expired, uncache it. * uncache_nfs4_owner may destroy the * owner, so unlock so_mutex prior to * the call. so_state_list should be * empty as well, so return early. */ PTHREAD_MUTEX_unlock(&owner->so_mutex); uncache_nfs4_owner(nfs4_owner); PTHREAD_MUTEX_unlock(&cached_open_owners_lock); return; } PTHREAD_MUTEX_unlock(&cached_open_owners_lock); /* We should be done, but will fall through anyway * to remove any remote possibility of a race with * get_state_owner. * * At this point, so_state_list is now properly a list. */ } state = glist_first_entry(&nfs4_owner->so_state_list, state_t, state_owner_list); if (state == NULL) { PTHREAD_MUTEX_unlock(&owner->so_mutex); return; } /* Move to end of list in case of error to ease retries */ glist_del(&state->state_owner_list); glist_add_tail(&nfs4_owner->so_state_list, &state->state_owner_list); /* Get references to the file and export */ ok = get_state_obj_export_owner_refs(state, &obj, &export, NULL); if (!ok) { /* The file, export, or state must be about to * die, skip for now. */ PTHREAD_MUTEX_unlock(&owner->so_mutex); continue; } /* Make sure the state doesn't go away on us... */ inc_state_t_ref(state); PTHREAD_MUTEX_unlock(&owner->so_mutex); PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); /* In case op_ctx->export is not NULL... */ if (op_ctx->ctx_export != NULL) { put_gsh_export(op_ctx->ctx_export); } /* op_ctx may be used by state_del_locked and others */ op_ctx->ctx_export = export; op_ctx->fsal_export = export->fsal_export; /* If FSAL supports extended operations, file will be closed by * state_del_locked. */ state_del_locked(state); dec_state_t_ref(state); PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); /* Release refs we held during state_del */ obj->obj_ops.put_ref(obj); put_gsh_export(op_ctx->ctx_export); op_ctx->ctx_export = NULL; op_ctx->fsal_export = NULL; } if (errcnt == STATE_ERR_MAX) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_owner(&dspbuf, owner); LogFatal(COMPONENT_STATE, "Could not complete cleanup of lock state for lock owner %s", str); } } /** * @brief Revoke delagtions belonging to the client owner. * * @param[in,out] client owner */ void revoke_owner_delegs(state_owner_t *client_owner) { struct glist_head *glist, *glistn; state_t *state, *first; struct fsal_obj_handle *obj; bool so_mutex_held; struct gsh_export *export = NULL; struct root_op_context ctx; bool ok; again: first = NULL; PTHREAD_MUTEX_lock(&client_owner->so_mutex); so_mutex_held = true; glist_for_each_safe(glist, glistn, &client_owner->so_owner.so_nfs4_owner.so_state_list) { state = glist_entry(glist, state_t, state_owner_list); /* We set first to the first state we look in this iteration. * If the current state matches the first state, it implies * that went through the entire list without droping the lock * guarding the list. So nothing more left to process. */ if (first == NULL) first = state; else if (first == state) break; /* Move entry to end of list to handle errors and skipping of * non-delegation states. */ glist_del(&state->state_owner_list); glist_add_tail( &client_owner->so_owner.so_nfs4_owner.so_state_list, &state->state_owner_list); /* Skip non-delegation states. */ if (state->state_type != STATE_TYPE_DELEG) continue; /* Safely access the cache inode associated with the state. * This will get an LRU reference protecting our access * even after state_deleg_revoke releases the reference it * holds. */ ok = get_state_obj_export_owner_refs(state, &obj, &export, NULL); if (!ok || obj == NULL) { LogDebug(COMPONENT_STATE, "Stale state or file"); continue; } PTHREAD_MUTEX_unlock(&client_owner->so_mutex); so_mutex_held = false; /* If FSAL supports extended operations, file will be closed by * state_del_locked which is called from deleg_revoke. */ PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); /* Initialize req_ctx */ init_root_op_context(&ctx, export, export->fsal_export, 0, 0, UNKNOWN_REQUEST); state_deleg_revoke(obj, state); /* Release refs we held */ obj->obj_ops.put_ref(obj); put_gsh_export(op_ctx->ctx_export); op_ctx->ctx_export = NULL; op_ctx->fsal_export = NULL; release_root_op_context(); PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); /* Since we dropped so_mutex, we must restart the loop. */ goto again; } if (so_mutex_held) PTHREAD_MUTEX_unlock(&client_owner->so_mutex); } /** * @brief Remove all state belonging to an export. * */ void state_export_release_nfs4_state(void) { state_t *state; state_t *first; int errcnt = 0; struct glist_head *glist, *glistn; bool hold_export_lock; /* Revoke layouts first (so that open states are still present). * Because we have to drop the export lock, when we cycle around agin * we MUST restart. */ again: first = NULL; PTHREAD_RWLOCK_wrlock(&op_ctx->ctx_export->lock); hold_export_lock = true; glist_for_each_safe(glist, glistn, &op_ctx->ctx_export->exp_state_list) { struct fsal_obj_handle *obj = NULL; state_owner_t *owner = NULL; bool deleted = false; struct pnfs_segment entire = { .io_mode = LAYOUTIOMODE4_ANY, .offset = 0, .length = NFS4_UINT64_MAX }; state = glist_entry(glist, state_t, state_export_list); /* We set first to the first state we look in this iteration. * If the current state matches the first state, it implies * that went through the entire list without droping the lock * guarding the list. So nothing more left to process. */ if (first == NULL) first = state; else if (first == state) break; /* Move state to the end of the list in case an error * occurs or the state is going stale. This also keeps us * from continually re-examining non-layout states when * we restart the loop. */ glist_del(&state->state_export_list); glist_add_tail(&op_ctx->ctx_export->exp_state_list, &state->state_export_list); if (state->state_type != STATE_TYPE_LAYOUT) { /* Skip non-layout states. */ continue; } if (!get_state_obj_export_owner_refs(state, &obj, NULL, &owner)) { /* This state_t is in the process of being destroyed, * skip it. */ continue; } inc_state_t_ref(state); PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock); hold_export_lock = false; PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); /* this deletes the state too */ (void) nfs4_return_one_state(obj, LAYOUTRETURN4_FILE, circumstance_revoke, state, entire, 0, NULL, &deleted); if (!deleted) { LogCrit(COMPONENT_PNFS, "Layout state not destroyed during export cleanup."); errcnt++; } PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); /* Release the references taken above */ obj->obj_ops.put_ref(obj); dec_state_owner_ref(owner); dec_state_t_ref(state); if (errcnt < STATE_ERR_MAX) { /* Loop again, but since we droped the export lock, we * must restart. */ goto again; } /* Too many errors, quit. */ break; } while (errcnt < STATE_ERR_MAX) { struct fsal_obj_handle *obj = NULL; state_owner_t *owner = NULL; if (!hold_export_lock) { PTHREAD_RWLOCK_wrlock(&op_ctx->ctx_export->lock); hold_export_lock = true; } state = glist_first_entry(&op_ctx->ctx_export->exp_state_list, state_t, state_export_list); if (state == NULL) break; /* Move state to the end of the list in case an error * occurs or the state is going stale. */ glist_del(&state->state_export_list); glist_add_tail(&op_ctx->ctx_export->exp_state_list, &state->state_export_list); if (!get_state_obj_export_owner_refs(state, &obj, NULL, &owner)) { /* This state_t is in the process of being destroyed, * skip it. */ continue; } inc_state_t_ref(state); PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock); hold_export_lock = false; PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); if (state->state_type == STATE_TYPE_DELEG) { /* this deletes the state too */ state_deleg_revoke(obj, state); } else { state_del_locked(state); } PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); /* Release the references taken above */ obj->obj_ops.put_ref(obj); dec_state_owner_ref(owner); dec_state_t_ref(state); } if (hold_export_lock) PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock); if (errcnt == STATE_ERR_MAX) { LogFatal(COMPONENT_STATE, "Could not complete cleanup of layouts for export %s", op_ctx->ctx_export->pseudopath); } } #ifdef DEBUG_SAL void dump_all_states(void) { state_t *state; state_owner_t *owner; if (!isFullDebug(COMPONENT_STATE)) return; PTHREAD_MUTEX_lock(&all_state_v4_mutex); if (!glist_empty(&state_v4_all)) { struct glist_head *glist; LogFullDebug(COMPONENT_STATE, " =State List= "); glist_for_each(glist, &state_v4_all) { char str1[LOG_BUFF_LEN / 2] = "\0"; char str2[LOG_BUFF_LEN / 2] = "\0"; struct display_buffer dspbuf1 = { sizeof(str1), str1, str1}; struct display_buffer dspbuf2 = { sizeof(str2), str2, str2}; state = glist_entry(glist, state_t, state_list_all); owner = get_state_owner_ref(state); display_owner(&dspbuf1, owner); display_stateid(&dspbuf2, state); LogFullDebug(COMPONENT_STATE, "State {%s} owner {%s}", str2, str1); if (owner != NULL) dec_state_owner_ref(owner); } LogFullDebug(COMPONENT_STATE, " ----------------------"); } else LogFullDebug(COMPONENT_STATE, "All states released"); PTHREAD_MUTEX_unlock(&all_state_v4_mutex); } #endif /** @} */ nfs-ganesha-2.6.0/src/SAL/nfs4_state_id.c000066400000000000000000000754171324272410200200240ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup SAL State abstraction layer * @{ */ /** * @file nfs4_state_id.c * @brief NFSv4 state ids */ #include "config.h" #include #include #include /* for having isalnum */ #include /* for having atoi */ #include /* for having MAXNAMLEN */ #include #include #include #include #include #include #include /* for having FNDELAY */ #include #include #include #include "log.h" #include "gsh_rpc.h" #include "hashtable.h" #include "nfs_core.h" #include "nfs4.h" #include "fsal.h" #include "nfs_exports.h" #include "nfs_file_handle.h" #include "sal_functions.h" #include "nfs_proto_tools.h" #include "city.h" /** * @brief Hash table for stateids. */ hash_table_t *ht_state_id; hash_table_t *ht_state_obj; /** * @brief All-zeroes stateid4.other */ char all_zero[OTHERSIZE]; /** * @brief All-zeroes stateid4.other */ char all_ones[OTHERSIZE]; #define seqid_all_one 0xFFFFFFFF /** * @brief Display a stateid other * * @param[in/out] dspbuf display_buffer describing output string * @param[in] other The other component of the stateid * * @return the bytes remaining in the buffer. */ int display_stateid_other(struct display_buffer *dspbuf, char *other) { uint64_t clientid = *((uint64_t *) other); uint32_t count = *((uint32_t *) (other + sizeof(uint64_t))); int b_left = display_cat(dspbuf, "OTHER="); if (b_left <= 0) return b_left; b_left = display_opaque_bytes(dspbuf, other, OTHERSIZE); if (b_left <= 0) return b_left; b_left = display_cat(dspbuf, " {{CLIENTID "); if (b_left <= 0) return b_left; b_left = display_clientid(dspbuf, clientid); if (b_left <= 0) return b_left; return display_printf(dspbuf, "} StateIdCounter=0x%08"PRIx32"}", count); } /** * @brief Display a stateid other in the hash table * * @param[in] buff The key * @param[out] str Output buffer * * @return Length of output string. */ int display_state_id_key(struct gsh_buffdesc *buff, char *str) { struct display_buffer dspbuf = {HASHTABLE_DISPLAY_STRLEN, str, str}; display_stateid_other(&dspbuf, buff->addr); return display_buffer_len(&dspbuf); } /** * @brief Display a stateid4 from the wire * * @param[in/out] dspbuf display_buffer describing output string * @param[in] state The stateid * * @return the bytes remaining in the buffer. */ int display_stateid4(struct display_buffer *dspbuf, stateid4 *stateid) { int b_left = display_stateid_other(dspbuf, stateid->other); if (b_left <= 0) return b_left; return display_printf(dspbuf, " seqid=%"PRIu32, stateid->seqid); } const char *str_state_type(state_t *state) { switch (state->state_type) { case STATE_TYPE_NONE: return "NONE"; case STATE_TYPE_SHARE: return "SHARE"; case STATE_TYPE_DELEG: return "DELEGATION"; case STATE_TYPE_LOCK: return "LOCK"; case STATE_TYPE_LAYOUT: return "LAYOUT"; case STATE_TYPE_NLM_LOCK: return "NLM_LOCK"; case STATE_TYPE_NLM_SHARE: return "NLM_SHARE"; case STATE_TYPE_9P_FID: return "9P_FID"; } return "UNKNOWN"; } /** * @brief Display a stateid * * @param[in/out] dspbuf display_buffer describing output string * @param[in] state The stateid * * @return the bytes remaining in the buffer. */ int display_stateid(struct display_buffer *dspbuf, state_t *state) { int b_left; if (state == NULL) return display_cat(dspbuf, "STATE "); b_left = display_printf(dspbuf, "STATE %p ", state); if (b_left <= 0) return b_left; b_left = display_stateid_other(dspbuf, state->stateid_other); if (b_left <= 0) return b_left; b_left = display_printf(dspbuf, " obj=%p type=%s seqid=%"PRIu32" owner={", &state->state_obj, str_state_type(state), state->state_seqid); if (b_left <= 0) return b_left; b_left = display_nfs4_owner(dspbuf, state->state_owner); if (b_left <= 0) return b_left; return display_printf(dspbuf, "} refccount=%"PRId32, atomic_fetch_int32_t(&state->state_refcount)); } /** * @brief Display a state in the hash table * * @param[in] buff The value * @param[out] str Output buffer * * @return Length of output string. */ int display_state_id_val(struct gsh_buffdesc *buff, char *str) { struct display_buffer dspbuf = {HASHTABLE_DISPLAY_STRLEN, str, str}; display_stateid(&dspbuf, buff->addr); return display_buffer_len(&dspbuf); } /** * @brief Compare two stateids * * @param[in] buff1 One key * @param[in] buff2 Another key * * @retval 0 if equal. * @retval 1 if not equal. */ int compare_state_id(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2) { if (isFullDebug(COMPONENT_STATE)) { char str1[DISPLAY_STATEID_OTHER_SIZE] = "\0"; char str2[DISPLAY_STATEID_OTHER_SIZE] = "\0"; struct display_buffer dspbuf1 = {sizeof(str1), str1, str1}; struct display_buffer dspbuf2 = {sizeof(str2), str2, str2}; display_stateid_other(&dspbuf1, buff1->addr); display_stateid_other(&dspbuf2, buff2->addr); if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_STATE, "{%s} vs {%s}", str1, str2); } return memcmp(buff1->addr, buff2->addr, OTHERSIZE); } /* compare_state_id */ /** * @brief Hash a stateid * * @param[in] stateid Array aliased to stateid */ static inline uint32_t compute_stateid_hash_value(uint32_t *stateid) { return stateid[1] ^ stateid[2]; } /** * @brief Hash index for a stateid * * @param[in] hparam Hash parameter * @param[in] key Key to hash * * @return The hash index. */ uint32_t state_id_value_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { uint32_t val = compute_stateid_hash_value(key->addr) % hparam->index_size; if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_STATE, "val = %" PRIu32, val); return val; } /** * @brief RBT hash for a stateid * * @param[in] hparam Hash parameter * @param[in] key Key to hash * * @return The RBT hash. */ uint64_t state_id_rbt_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { uint64_t val = compute_stateid_hash_value(key->addr); if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_STATE, "rbt = %" PRIu64, val); return val; } static hash_parameter_t state_id_param = { .index_size = PRIME_STATE, .hash_func_key = state_id_value_hash_func, .hash_func_rbt = state_id_rbt_hash_func, .compare_key = compare_state_id, .key_to_str = display_state_id_key, .val_to_str = display_state_id_val, .flags = HT_FLAG_CACHE, .ht_log_component = COMPONENT_STATE, .ht_name = "State ID Table" }; /** * @brief Compare two stateids by entry/owner * * @param[in] buff1 One key * @param[in] buff2 Another key * * @retval 0 if equal. * @retval 1 if not equal. */ int compare_state_obj(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2) { state_t *state1 = buff1->addr; state_t *state2 = buff2->addr; if (state1 == NULL || state2 == NULL) return 1; if (state1->state_obj != state2->state_obj) return 1; return compare_nfs4_owner(state1->state_owner, state2->state_owner); } /** * @brief Hash index for a stateid by entry/owner * * @param[in] hparam Hash parameter * @param[in] key Key to hash * * @return The hash index. */ uint32_t state_obj_value_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { unsigned int sum = 0; unsigned int i = 0; unsigned char c = 0; uint32_t res = 0; struct gsh_buffdesc fh_desc; state_t *pkey = key->addr; pkey->state_obj->obj_ops.handle_to_key(pkey->state_obj, &fh_desc); /* Compute the sum of all the characters */ for (i = 0; i < pkey->state_owner->so_owner_len; i++) { c = ((char *)pkey->state_owner->so_owner_val)[i]; sum += c; } res = ((uint32_t) pkey->state_owner->so_owner.so_nfs4_owner.so_clientid + (uint32_t) sum + pkey->state_owner->so_owner_len + (uint32_t) pkey->state_owner->so_type + (uint64_t) CityHash64WithSeed(fh_desc.addr, fh_desc.len, 557)) % (uint32_t) hparam->index_size; if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_STATE, "value = %" PRIu32, res); return res; } /** * @brief RBT hash for a stateid by entry/owner * * @param[in] hparam Hash parameter * @param[in] key Key to hash * * @return The RBT hash. */ uint64_t state_obj_rbt_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { state_t *pkey = key->addr; struct gsh_buffdesc fh_desc; unsigned int sum = 0; unsigned int i = 0; unsigned char c = 0; uint64_t res = 0; pkey->state_obj->obj_ops.handle_to_key(pkey->state_obj, &fh_desc); /* Compute the sum of all the characters */ for (i = 0; i < pkey->state_owner->so_owner_len; i++) { c = ((char *)pkey->state_owner->so_owner_val)[i]; sum += c; } res = (uint64_t) pkey->state_owner->so_owner.so_nfs4_owner.so_clientid + (uint64_t) sum + pkey->state_owner->so_owner_len + (uint64_t) pkey->state_owner->so_type + (uint64_t) CityHash64WithSeed(fh_desc.addr, fh_desc.len, 557); if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_STATE, "rbt = %" PRIu64, res); return res; } static hash_parameter_t state_obj_param = { .index_size = PRIME_STATE, .hash_func_key = state_obj_value_hash_func, .hash_func_rbt = state_obj_rbt_hash_func, .compare_key = compare_state_obj, .key_to_str = display_state_id_val, .val_to_str = display_state_id_val, .flags = HT_FLAG_CACHE, .ht_log_component = COMPONENT_STATE, .ht_name = "State Obj Table" }; /** * @brief Init the hashtable for stateids * * @retval 0 if successful. * @retval -1 on failure. */ int nfs4_Init_state_id(void) { /* Init all_one */ memset(all_zero, 0, OTHERSIZE); memset(all_ones, 0xFF, OTHERSIZE); ht_state_id = hashtable_init(&state_id_param); if (ht_state_id == NULL) { LogCrit(COMPONENT_STATE, "Cannot init State Id cache"); return -1; } ht_state_obj = hashtable_init(&state_obj_param); if (ht_state_obj == NULL) { LogCrit(COMPONENT_STATE, "Cannot init State Entry cache"); return -1; } return 0; } /** * @brief Build the 12 byte "other" portion of a stateid * * It is built from the ServerEpoch and a 64 bit global counter. * * @param[in] other stateid.other object (a char[OTHERSIZE] string) */ void nfs4_BuildStateId_Other(nfs_client_id_t *clientid, char *other) { uint32_t my_stateid = atomic_inc_uint32_t(&clientid->cid_stateid_counter); /* The first part of the other is the 64 bit clientid, which * consists of the epoch in the high order 32 bits followed by * the clientid counter in the low order 32 bits. */ memcpy(other, &clientid->cid_clientid, sizeof(clientid->cid_clientid)); memcpy(other + sizeof(clientid->cid_clientid), &my_stateid, sizeof(my_stateid)); } /** * @brief Relinquish a reference on a state_t * * @param[in] state The state_t to release */ void dec_nfs4_state_ref(struct state_t *state) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; bool str_valid = false; int32_t refcount; if (isFullDebug(COMPONENT_STATE)) { display_stateid(&dspbuf, state); str_valid = true; } refcount = atomic_dec_int32_t(&state->state_refcount); if (refcount > 0) { if (str_valid) LogFullDebug(COMPONENT_STATE, "Decrement refcount now=%" PRId32 " {%s}", refcount, str); return; } PTHREAD_MUTEX_destroy(&state->state_mutex); state->state_exp->exp_ops.free_state(state->state_exp, state); if (str_valid) LogFullDebug(COMPONENT_STATE, "Deleted %s", str); } /** * @brief Set a state into the stateid hashtable. * * @param[in] other stateid4.other * @param[in] state The state to add * * @retval STATE_SUCCESS if able to insert the new state. * @retval STATE_ENTRY_EXISTS if state is already there. */ state_status_t nfs4_State_Set(state_t *state) { struct gsh_buffdesc buffkey; struct gsh_buffdesc buffval; hash_error_t err; buffkey.addr = state->stateid_other; buffkey.len = OTHERSIZE; buffval.addr = state; buffval.len = sizeof(state_t); err = hashtable_test_and_set(ht_state_id, &buffkey, &buffval, HASHTABLE_SET_HOW_SET_NO_OVERWRITE); switch (err) { case HASHTABLE_SUCCESS: break; default: LogCrit(COMPONENT_STATE, "ht_state_id hashtable_test_and_set failed %s for key %p", hash_table_err_to_str(err), buffkey.addr); return STATE_ENTRY_EXISTS; /* likely reason */ } /* If stateid is a LOCK or SHARE state, we also index by entry/owner */ if (state->state_type != STATE_TYPE_LOCK && state->state_type != STATE_TYPE_SHARE) return STATE_SUCCESS; buffkey.addr = state; buffkey.len = sizeof(state_t); buffval.addr = state; buffval.len = sizeof(state_t); err = hashtable_test_and_set(ht_state_obj, &buffkey, &buffval, HASHTABLE_SET_HOW_SET_NO_OVERWRITE); switch (err) { case HASHTABLE_SUCCESS: return STATE_SUCCESS; case HASHTABLE_ERROR_KEY_ALREADY_EXISTS: /* buggy client? */ default: /* error case */ LogCrit(COMPONENT_STATE, "ht_state_obj hashtable_test_and_set failed %s for key %p", hash_table_err_to_str(err), buffkey.addr); if (isFullDebug(COMPONENT_STATE)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; state_t *state2; display_stateid(&dspbuf, state); LogCrit(COMPONENT_STATE, "State %s", str); state2 = nfs4_State_Get_Obj(state->state_obj, state->state_owner); if (state2 != NULL) { display_reset_buffer(&dspbuf); display_stateid(&dspbuf, state2); LogCrit(COMPONENT_STATE, "Duplicate State %s", str); } } buffkey.addr = state->stateid_other; buffkey.len = OTHERSIZE; err = HashTable_Del(ht_state_id, &buffkey, NULL, NULL); if (err != HASHTABLE_SUCCESS) { LogCrit(COMPONENT_STATE, "Failure to delete stateid %s", hash_table_err_to_str(err)); } return STATE_ENTRY_EXISTS; /* likely reason */ } } /** * @brief Get the state from the stateid * * @param[in] other stateid4.other * * @returns The found state_t or NULL if not found. */ struct state_t *nfs4_State_Get_Pointer(char *other) { struct gsh_buffdesc buffkey; struct gsh_buffdesc buffval; hash_error_t rc; struct hash_latch latch; struct state_t *state; buffkey.addr = other; buffkey.len = OTHERSIZE; rc = hashtable_getlatch(ht_state_id, &buffkey, &buffval, true, &latch); if (rc != HASHTABLE_SUCCESS) { if (rc == HASHTABLE_ERROR_NO_SUCH_KEY) hashtable_releaselatched(ht_state_id, &latch); LogDebug(COMPONENT_STATE, "HashTable_Get returned %d", rc); return NULL; } state = buffval.addr; /* Take a reference under latch */ inc_state_t_ref(state); /* Release latch */ hashtable_releaselatched(ht_state_id, &latch); return state; } /** * @brief Get the state from the stateid by entry/owner * * @param[in] obj Object containing state * @param[in] owner Owner for state * * @returns The found state_t or NULL if not found. */ struct state_t *nfs4_State_Get_Obj(struct fsal_obj_handle *obj, state_owner_t *owner) { state_t state_key; struct gsh_buffdesc buffkey; struct gsh_buffdesc buffval; hash_error_t rc; struct hash_latch latch; struct state_t *state; memset(&state_key, 0, sizeof(state_key)); buffkey.addr = &state_key; buffkey.len = sizeof(state_key); state_key.state_owner = owner; state_key.state_obj = obj; rc = hashtable_getlatch(ht_state_obj, &buffkey, &buffval, true, &latch); if (rc != HASHTABLE_SUCCESS) { if (rc == HASHTABLE_ERROR_NO_SUCH_KEY) hashtable_releaselatched(ht_state_obj, &latch); LogDebug(COMPONENT_STATE, "HashTable_Get returned %d", rc); return NULL; } state = buffval.addr; /* Take a reference under latch */ inc_state_t_ref(state); /* Release latch */ hashtable_releaselatched(ht_state_obj, &latch); return state; } /** * @brief Remove a state from the stateid table * * @param[in] other stateid4.other * * @retval true if success * @retval false if failure */ bool nfs4_State_Del(state_t *state) { struct gsh_buffdesc buffkey, old_key, old_value; struct hash_latch latch; hash_error_t err; buffkey.addr = state->stateid_other; buffkey.len = OTHERSIZE; err = HashTable_Del(ht_state_id, &buffkey, &old_key, &old_value); if (err == HASHTABLE_ERROR_NO_SUCH_KEY) { /* Already gone */ return false; } else if (err != HASHTABLE_SUCCESS) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_stateid(&dspbuf, state); LogDebug(COMPONENT_STATE, "Failure to delete stateid %s %s", str, hash_table_err_to_str(err)); return false; } assert(state == old_value.addr); /* If stateid is a LOCK or SHARE state, we had also indexed by * entry/owner */ if (state->state_type != STATE_TYPE_LOCK && state->state_type != STATE_TYPE_SHARE) return true; /* Delete the stateid hashed by entry/owner. * Use the old_value from above as the key. */ buffkey.addr = old_value.addr; buffkey.len = old_value.len; /* Get latch: we need to check we're deleting the right state */ err = hashtable_getlatch(ht_state_obj, &buffkey, &old_value, true, &latch); if (err != HASHTABLE_SUCCESS) { if (err == HASHTABLE_ERROR_NO_SUCH_KEY) hashtable_releaselatched(ht_state_obj, &latch); LogCrit(COMPONENT_STATE, "hashtable get latch failed: %d", err); return false; } if (old_value.addr != state) { /* state obj had already been swapped out */ hashtable_releaselatched(ht_state_obj, &latch); return false; } hashtable_deletelatched(ht_state_obj, &buffkey, &latch, NULL, NULL); hashtable_releaselatched(ht_state_obj, &latch); return true; } /** * @brief Check and look up the supplied stateid * * This function yields the state for the stateid if it is valid. * * @param[in] stateid Stateid to look up * @param[in] fsal_obj Associated file (if any) * @param[out] state Found state * @param[in] data Compound data * @param[in] flags Flags governing special stateids * @param[in] owner_seqid seqid on v4.0 owner * @param[in] check_seqid Whether to validate owner_seqid * @param[in] tag Arbitrary string for logging/debugging * * @return NFSv4 status codes */ nfsstat4 nfs4_Check_Stateid(stateid4 *stateid, struct fsal_obj_handle *fsal_obj, state_t **state, compound_data_t *data, int flags, seqid4 owner_seqid, bool check_seqid, const char *tag) { uint32_t epoch = 0; uint64_t epoch_low = ServerEpoch & 0xFFFFFFFF; state_t *state2 = NULL; struct fsal_obj_handle *obj2 = NULL; state_owner_t *owner2 = NULL; char str[DISPLAY_STATEID4_SIZE] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; bool str_valid = false; int32_t diff; clientid4 clientid; nfs_client_id_t *pclientid; int rc; nfsstat4 status; if (isDebug(COMPONENT_STATE)) { display_stateid4(&dspbuf, stateid); str_valid = true; } LogFullDebug(COMPONENT_STATE, "Check %s stateid flags%s%s%s%s%s%s", tag, flags & STATEID_SPECIAL_ALL_0 ? " ALL_0" : "", flags & STATEID_SPECIAL_ALL_1 ? " ALL_1" : "", flags & STATEID_SPECIAL_CURRENT ? " CURRENT" : "", flags & STATEID_SPECIAL_CLOSE_40 ? " CLOSE_40" : "", flags & STATEID_SPECIAL_CLOSE_41 ? " CLOSE_41" : "", flags == 0 ? " NONE" : ""); /* Test for OTHER is all zeros */ if (memcmp(stateid->other, all_zero, OTHERSIZE) == 0) { if (stateid->seqid == 0 && (flags & STATEID_SPECIAL_ALL_0) != 0) { /* All 0 stateid */ LogDebug(COMPONENT_STATE, "Check %s stateid found special all 0 stateid", tag); /** @todo FSF: eventually this may want to return an * actual state for use in temporary locks for I/O. */ data->current_stateid_valid = false; goto success; } if (stateid->seqid == 1 && (flags & STATEID_SPECIAL_CURRENT) != 0) { /* Special current stateid */ LogDebug(COMPONENT_STATE, "Check %s stateid found special 'current' stateid", tag); if (!data->current_stateid_valid) { LogDebug(COMPONENT_STATE, "Check %s stateid STATEID_SPECIAL_CURRENT - current stateid is bad", tag); status = NFS4ERR_BAD_STATEID; goto failure; } /* Copy current stateid in and proceed to checks */ *stateid = data->current_stateid; goto check_it; } LogDebug(COMPONENT_STATE, "Check %s stateid with OTHER all zeros, seqid %u unexpected", tag, (unsigned int)stateid->seqid); status = NFS4ERR_BAD_STATEID; goto failure; } /* Test for OTHER is all ones */ if (memcmp(stateid->other, all_ones, OTHERSIZE) == 0) { /* Test for special all ones stateid */ if (stateid->seqid == seqid_all_one && (flags & STATEID_SPECIAL_ALL_1) != 0) { /* All 1 stateid */ LogDebug(COMPONENT_STATE, "Check %s stateid found special all 1 stateid", tag); /** @todo FSF: eventually this may want to return an * actual state for use in temporary locks for I/O. */ data->current_stateid_valid = false; goto success; } LogDebug(COMPONENT_STATE, "Check %s stateid with OTHER all ones, seqid %u unexpected", tag, (unsigned int)stateid->seqid); status = NFS4ERR_BAD_STATEID; goto failure; } check_it: /* Extract the clientid from the stateid other field */ memcpy(&clientid, stateid->other, sizeof(clientid)); /* Extract the epoch from the clientid */ epoch = clientid >> (clientid4) 32; /* Check if stateid was made from this server instance */ if (epoch != epoch_low) { if (str_valid) LogDebug(COMPONENT_STATE, "Check %s stateid found stale stateid %s", tag, str); status = NFS4ERR_STALE_STATEID; goto failure; } /* Try to get the related state */ state2 = nfs4_State_Get_Pointer(stateid->other); /* We also need a reference to the state_obj and state_owner. * If we can't get them, we will check below for lease invalidity. * Note that calling get_state_obj_export_owner_refs with a NULL * state2 returns false. */ if (!get_state_obj_export_owner_refs(state2, &obj2, NULL, &owner2)) { /* We matched this server's epoch, but could not find the * stateid. Chances are, the client was expired and the state * has all been freed. * * We could use another check here for a BAD stateid */ if (str_valid) LogDebug(COMPONENT_STATE, "Check %s stateid could not find %s", tag, str); /* Try and find the clientid */ rc = nfs_client_id_get_confirmed(clientid, &pclientid); if (rc != CLIENT_ID_SUCCESS) { /* Unknown client id (or other problem), * return that result. */ status = clientid_error_to_nfsstat(rc); goto failure; } if ((flags & (STATEID_SPECIAL_CLOSE_40 | STATEID_SPECIAL_CLOSE_41)) != 0) { /* This is a close with a valid clientid, but invalid * stateid counter. We will assume this is a replayed * close. */ if (data->preserved_clientid != NULL) { /* We don't expect this, but, just in case... * Update and release already reserved lease. */ PTHREAD_MUTEX_lock(&data->preserved_clientid ->cid_mutex); update_lease(data->preserved_clientid); PTHREAD_MUTEX_unlock(&data->preserved_clientid ->cid_mutex); data->preserved_clientid = NULL; } /* Check if lease is expired and reserve it */ PTHREAD_MUTEX_lock(&pclientid->cid_mutex); if (!reserve_lease(pclientid)) { LogDebug(COMPONENT_STATE, "Returning NFS4ERR_EXPIRED"); PTHREAD_MUTEX_unlock(&pclientid->cid_mutex); status = NFS4ERR_EXPIRED; goto failure; } if ((flags & STATEID_SPECIAL_CLOSE_40) != 0) { /* Just update the lease and leave the reserved * clientid NULL. */ update_lease(pclientid); } else { /* Remember the reserved clientid for the rest * of the compound. */ data->preserved_clientid = pclientid; } PTHREAD_MUTEX_unlock(&pclientid->cid_mutex); /* Replayed close, it's ok, but stateid doesn't exist */ LogDebug(COMPONENT_STATE, "Check %s stateid is a replayed close", tag); data->current_stateid_valid = false; goto success; } if (state2 == NULL) status = NFS4ERR_BAD_STATEID; else { /* We had a valid stateid, but the entry was stale. * Check if lease is expired and reserve it so we * can distinguish betwen the state_t being in the * midst of tear down due to expired lease or if * in fact the entry is actually stale. */ PTHREAD_MUTEX_lock(&pclientid->cid_mutex); if (!reserve_lease(pclientid)) { LogDebug(COMPONENT_STATE, "Returning NFS4ERR_EXPIRED"); PTHREAD_MUTEX_unlock(&pclientid->cid_mutex); /* Release the clientid reference we just * acquired. */ dec_client_id_ref(pclientid); status = NFS4ERR_EXPIRED; goto failure; } /* Just update the lease and leave the reserved * clientid NULL. */ update_lease(pclientid); PTHREAD_MUTEX_unlock(&pclientid->cid_mutex); /* The lease was valid, so this must be a stale * entry. */ status = NFS4ERR_STALE; } /* Release the clientid reference we just acquired. */ dec_client_id_ref(pclientid); goto failure; } /* Now, if this lease is not already reserved, reserve it */ if (data->preserved_clientid != owner2->so_owner.so_nfs4_owner.so_clientrec) { if (data->preserved_clientid != NULL) { /* We don't expect this to happen, but, just in case... * Update and release already reserved lease. */ PTHREAD_MUTEX_lock(&data->preserved_clientid ->cid_mutex); update_lease(data->preserved_clientid); PTHREAD_MUTEX_unlock(&data->preserved_clientid ->cid_mutex); data->preserved_clientid = NULL; } /* Check if lease is expired and reserve it */ PTHREAD_MUTEX_lock( &owner2->so_owner.so_nfs4_owner.so_clientrec->cid_mutex); if (!reserve_lease( owner2->so_owner.so_nfs4_owner.so_clientrec)) { LogDebug(COMPONENT_STATE, "Returning NFS4ERR_EXPIRED"); PTHREAD_MUTEX_unlock(&owner2->so_owner.so_nfs4_owner .so_clientrec->cid_mutex); status = NFS4ERR_EXPIRED; goto failure; } data->preserved_clientid = owner2->so_owner.so_nfs4_owner.so_clientrec; PTHREAD_MUTEX_unlock( &owner2->so_owner.so_nfs4_owner.so_clientrec->cid_mutex); } /* Sanity check : Is this the right file ? */ if (fsal_obj && !fsal_obj->obj_ops.handle_cmp(fsal_obj, obj2)) { if (str_valid) LogDebug(COMPONENT_STATE, "Check %s stateid found stateid %s has wrong file", tag, str); status = NFS4ERR_BAD_STATEID; goto failure; } /* Whether stateid.seqid may be zero depends on the state type exclusively, See RFC 5661 pp. 161,287-288. */ if ((state2->state_type == STATE_TYPE_LAYOUT) || (stateid->seqid != 0)) { /* Check seqid in stateid */ /** * @todo fsf: maybe change to simple comparison: * stateid->seqid < state2->state_seqid * as good enough and maybe makes pynfs happy. */ diff = stateid->seqid - state2->state_seqid; if (diff < 0) { /* if this is NFSv4.0 and stateid's seqid is one less * than current AND if owner_seqid is current * pass state back to allow replay check */ if ((check_seqid) && ((diff == -1) || ((state2->state_seqid == 1) && (stateid->seqid == seqid_all_one))) && (owner_seqid == owner2->so_owner.so_nfs4_owner.so_seqid)) { LogDebug(COMPONENT_STATE, "possible replay?"); *state = state2; status = NFS4ERR_REPLAY; goto replay; } /* OLD_STATEID */ if (str_valid) LogDebug(COMPONENT_STATE, "Check %s stateid found OLD stateid %s, expected seqid %" PRIu32, tag, str, state2->state_seqid); status = NFS4ERR_OLD_STATEID; goto failure; } /* stateid seqid is current and owner seqid is previous, * replay (should be an error condition that did not change * the stateid, no real need to check since the operation * must be the same) */ else if ((diff == 0) && (check_seqid) && (owner_seqid == owner2->so_owner.so_nfs4_owner.so_seqid)) { LogDebug(COMPONENT_STATE, "possible replay?"); *state = state2; status = NFS4ERR_REPLAY; goto replay; } else if (diff > 0) { /* BAD_STATEID */ if (str_valid) LogDebug(COMPONENT_STATE, "Check %s stateid found BAD stateid %s, expected seqid %" PRIu32, tag, str, state2->state_seqid); status = NFS4ERR_BAD_STATEID; goto failure; } } data->current_stateid_valid = true; if (str_valid) LogFullDebug(COMPONENT_STATE, "Check %s stateid found valid stateid %s - %p", tag, str, state2); /* Copy stateid into current for later use */ data->current_stateid = *stateid; data->current_stateid.seqid = state2->state_seqid; success: if (obj2 != NULL) { obj2->obj_ops.put_ref(obj2); dec_state_owner_ref(owner2); } *state = state2; return NFS4_OK; failure: if (state2 != NULL) dec_state_t_ref(state2); *state = NULL; replay: if (obj2 != NULL) { obj2->obj_ops.put_ref(obj2); dec_state_owner_ref(owner2); } data->current_stateid_valid = false; return status; } /** * @brief Display the stateid table */ void nfs_State_PrintAll(void) { if (isFullDebug(COMPONENT_STATE)) hashtable_log(COMPONENT_STATE, ht_state_id); } /** * @brief Update stateid and set current * * We increment the seqid, handling wraparound, and copy the id into * the response. * * @param[in,out] state State to update * @param[out] resp Stateid in response * @param[in,out] data Compound data to upddate with current stateid * (may be NULL) * @param[in] tag Arbitrary text for debug/log */ void update_stateid(state_t *state, stateid4 *resp, compound_data_t *data, const char *tag) { /* Increment state_seqid, handling wraparound */ state->state_seqid += 1; if (state->state_seqid == 0) state->state_seqid = 1; /* Copy stateid into current for later use */ if (data) { COPY_STATEID(&data->current_stateid, state); data->current_stateid_valid = true; } /* Copy stateid into response */ COPY_STATEID(resp, state); if (isFullDebug(COMPONENT_STATE)) { char str[DISPLAY_STATEID4_SIZE] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_stateid4(&dspbuf, resp); LogDebug(COMPONENT_STATE, "Update %s stateid to %s for response", tag, str); } } /** @} */ nfs-ganesha-2.6.0/src/SAL/nlm_owner.c000066400000000000000000001000441324272410200172570ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup SAL State abstraction layer * @{ */ /** * @file nlm_owner.c * @brief The management of the NLM owner cache. */ #include "config.h" #include #include #include #include "gsh_config.h" #include "sal_functions.h" #include "nsm.h" #include "log.h" #include "client_mgr.h" #include "fsal.h" /** * @brief NSM clients */ hash_table_t *ht_nsm_client; /** * @brief NLM Clients */ hash_table_t *ht_nlm_client; /** * @brief NLM owners */ hash_table_t *ht_nlm_owner; /******************************************************************************* * * NSM Client Routines * ******************************************************************************/ /** * @brief Display an NSM client * * @param[in/out] dspbuf display_buffer describing output string * @param[in] key The NSM client * * @return the bytes remaining in the buffer. */ int display_nsm_client(struct display_buffer *dspbuf, state_nsm_client_t *key) { int b_left; if (key == NULL) return display_printf(dspbuf, "NSM Client "); b_left = display_printf(dspbuf, "NSM Client %p: ", key); if (b_left <= 0) return b_left; if (nfs_param.core_param.nsm_use_caller_name) b_left = display_printf(dspbuf, "caller_name="); else b_left = display_printf(dspbuf, "addr="); if (b_left <= 0) return b_left; b_left = display_len_cat(dspbuf, key->ssc_nlm_caller_name, key->ssc_nlm_caller_name_len); if (b_left <= 0) return b_left; return display_printf(dspbuf, " ssc_client=%p %s refcount=%d", key->ssc_client, atomic_fetch_int32_t(&key->ssc_monitored) ? "monitored" : "unmonitored", atomic_fetch_int32_t(&key->ssc_refcount)); } /** * @brief Display an NSM client in the hash table * * @param[in] buff The key * @param[out] str Output buffer * * @return Length of output string. */ int display_nsm_client_key(struct gsh_buffdesc *buff, char *str) { struct display_buffer dspbuf = {HASHTABLE_DISPLAY_STRLEN, str, str}; display_nsm_client(&dspbuf, buff->addr); return display_buffer_len(&dspbuf); } /** * @brief Display an NSM client in the hash table * * @param[in] buff The value * @param[out] str Output buffer * * @return Length of output string. */ int display_nsm_client_val(struct gsh_buffdesc *buff, char *str) { struct display_buffer dspbuf = {HASHTABLE_DISPLAY_STRLEN, str, str}; display_nsm_client(&dspbuf, buff->addr); return display_buffer_len(&dspbuf); } /** * @brief Compare NSM clients * * @param[in] client1 A client * @param[in] client2 Another client * * @retval 0 on equality. * @retval 1 on inequality. */ int compare_nsm_client(state_nsm_client_t *client1, state_nsm_client_t *client2) { if (isFullDebug(COMPONENT_STATE) && isDebug(COMPONENT_HASHTABLE)) { char str1[LOG_BUFF_LEN / 2] = "\0"; char str2[LOG_BUFF_LEN / 2] = "\0"; struct display_buffer dspbuf1 = {sizeof(str1), str1, str1}; struct display_buffer dspbuf2 = {sizeof(str2), str2, str2}; display_nsm_client(&dspbuf1, client1); display_nsm_client(&dspbuf2, client2); LogFullDebug(COMPONENT_STATE, "{%s} vs {%s}", str1, str2); } if (client1 == NULL || client2 == NULL) return 1; if (client1 == client2) return 0; if (!nfs_param.core_param.nsm_use_caller_name) { if (client1->ssc_client != client2->ssc_client) return 1; return 0; } if (client1->ssc_nlm_caller_name_len != client2->ssc_nlm_caller_name_len) return 1; return memcmp(client1->ssc_nlm_caller_name, client2->ssc_nlm_caller_name, client1->ssc_nlm_caller_name_len); } /** * @brief Compare NSM clients in the hash table * * @param[in] buff1 A key * @param[in] buff2 Another key * * @retval 0 on equality. * @retval 1 on inequality. */ int compare_nsm_client_key(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2) { return compare_nsm_client(buff1->addr, buff2->addr); } /** * @brief Calculate hash index for an NSM key * * @todo Replace with a good hash function. * * @param[in] hparam Hash params * @param[out] key Key to hash * * @return The hash index. */ uint32_t nsm_client_value_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { unsigned long res; state_nsm_client_t *pkey = key->addr; if (nfs_param.core_param.nsm_use_caller_name) { unsigned int sum = 0; unsigned int i; /* Compute the sum of all the characters */ for (i = 0; i < pkey->ssc_nlm_caller_name_len; i++) sum += (unsigned char)pkey->ssc_nlm_caller_name[i]; res = (unsigned long)sum + (unsigned long)pkey->ssc_nlm_caller_name_len; } else { res = (unsigned long) pkey->ssc_client; } if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_STATE, "value = %lu", res % hparam->index_size); return (unsigned long)(res % hparam->index_size); } /** * @brief Calculate RBT hash for an NSM key * * @todo Replace with a good hash function. * * @param[in] hparam Hash params * @param[out] key Key to hash * * @return The RBT hash. */ uint64_t nsm_client_rbt_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { unsigned long res; state_nsm_client_t *pkey = key->addr; if (nfs_param.core_param.nsm_use_caller_name) { unsigned int sum = 0; unsigned int i; /* Compute the sum of all the characters */ for (i = 0; i < pkey->ssc_nlm_caller_name_len; i++) sum += (unsigned char)pkey->ssc_nlm_caller_name[i]; res = (unsigned long)sum + (unsigned long)pkey->ssc_nlm_caller_name_len; } else { res = (unsigned long) pkey->ssc_client; } if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_STATE, "rbt = %lu", res); return res; } /* nsm_client_rbt_hash_func */ /******************************************************************************* * * NLM Client Routines * ******************************************************************************/ /** * @brief Display an NLM client * * @param[in/out] dspbuf display_buffer describing output string * @param[in] key The NLM client * * @return the bytes remaining in the buffer. */ int display_nlm_client(struct display_buffer *dspbuf, state_nlm_client_t *key) { int b_left; if (key == NULL) return display_printf(dspbuf, "NLM Client "); b_left = display_printf(dspbuf, "NLM Client %p: {", key); if (b_left <= 0) return b_left; b_left = display_nsm_client(dspbuf, key->slc_nsm_client); if (b_left <= 0) return b_left; b_left = display_printf(dspbuf, "} caller_name="); if (b_left <= 0) return b_left; b_left = display_len_cat(dspbuf, key->slc_nlm_caller_name, key->slc_nlm_caller_name_len); if (b_left <= 0) return b_left; return display_printf(dspbuf, " type=%s refcount=%d", xprt_type_to_str(key->slc_client_type), atomic_fetch_int32_t(&key->slc_refcount)); } /** * @brief Display an NLM client in the hash table * * @param[in] buff The key * @param[out] str Output buffer * * @return Length of output string. */ int display_nlm_client_key(struct gsh_buffdesc *buff, char *str) { struct display_buffer dspbuf = {HASHTABLE_DISPLAY_STRLEN, str, str}; display_nlm_client(&dspbuf, buff->addr); return display_buffer_len(&dspbuf); } /** * @brief Display an NLM client in the hash table * * @param[in] buff The value * @param[out] str Output buffer * * @return Length of output string. */ int display_nlm_client_val(struct gsh_buffdesc *buff, char *str) { struct display_buffer dspbuf = {HASHTABLE_DISPLAY_STRLEN, str, str}; display_nlm_client(&dspbuf, buff->addr); return display_buffer_len(&dspbuf); } /** * @brief Compare NLM clients * * @param[in] client1 A client * @param[in] client2 Another client * * @retval 0 on equality. * @retval 1 on inequality. */ int compare_nlm_client(state_nlm_client_t *client1, state_nlm_client_t *client2) { if (isFullDebug(COMPONENT_STATE) && isDebug(COMPONENT_HASHTABLE)) { char str1[LOG_BUFF_LEN / 2] = "\0"; char str2[LOG_BUFF_LEN / 2] = "\0"; struct display_buffer dspbuf1 = {sizeof(str1), str1, str1}; struct display_buffer dspbuf2 = {sizeof(str2), str2, str2}; display_nlm_client(&dspbuf1, client1); display_nlm_client(&dspbuf2, client2); LogFullDebug(COMPONENT_STATE, "{%s} vs {%s}", str1, str2); } if (client1 == NULL || client2 == NULL) return 1; if (client1 == client2) return 0; if (compare_nsm_client(client1->slc_nsm_client, client2->slc_nsm_client) != 0) return 1; if (cmp_sockaddr(&client1->slc_server_addr, &client2->slc_server_addr, true) == 0) return 1; if (client1->slc_client_type != client2->slc_client_type) return 1; if (client1->slc_nlm_caller_name_len != client2->slc_nlm_caller_name_len) return 1; return memcmp(client1->slc_nlm_caller_name, client2->slc_nlm_caller_name, client1->slc_nlm_caller_name_len); } /** * @brief Compare NLM clients in the hash table * * @param[in] buff1 A key * @param[in] buff2 Another key * * @retval 0 on equality. * @retval 1 on inequality. */ int compare_nlm_client_key(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2) { return compare_nlm_client(buff1->addr, buff2->addr); } /** * @brief Calculate hash index for an NLM key * * @todo Replace with a good hash function. * * @param[in] hparam Hash params * @param[out] key Key to hash * * @return The hash index. */ uint32_t nlm_client_value_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { uint32_t sum = 0; unsigned int i; unsigned long res; state_nlm_client_t *pkey = key->addr; /* Compute the sum of all the characters */ for (i = 0; i < pkey->slc_nlm_caller_name_len; i++) sum += (unsigned char)pkey->slc_nlm_caller_name[i]; res = (unsigned long)sum + (unsigned long)pkey->slc_nlm_caller_name_len; if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_STATE, "value = %lu", res % hparam->index_size); return (unsigned long)(res % hparam->index_size); } /** * @brief Calculate RBT hash for an NLM key * * @todo Replace with a good hash function. * * @param[in] hparam Hash params * @param[out] key Key to hash * * @return The RBT hash. */ uint64_t nlm_client_rbt_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { unsigned int sum = 0; unsigned int i; unsigned long res; state_nlm_client_t *pkey = key->addr; /* Compute the sum of all the characters */ for (i = 0; i < pkey->slc_nlm_caller_name_len; i++) sum += (unsigned char)pkey->slc_nlm_caller_name[i]; res = (unsigned long)sum + (unsigned long)pkey->slc_nlm_caller_name_len; if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_STATE, "rbt = %lu", res); return res; } /* nlm_client_rbt_hash_func */ /******************************************************************************* * * NLM Owner Routines * ******************************************************************************/ /** * @brief Display an NLM cowner * * @param[in/out] dspbuf display_buffer describing output string * @param[in] key The NLM owner * * @return the bytes remaining in the buffer. */ int display_nlm_owner(struct display_buffer *dspbuf, state_owner_t *owner) { int b_left; if (owner == NULL) return display_printf(dspbuf, "STATE_LOCK_OWNER_NLM "); b_left = display_printf(dspbuf, "STATE_LOCK_OWNER_NLM %p: {", owner); if (b_left <= 0) return b_left; b_left = display_nlm_client(dspbuf, owner->so_owner.so_nlm_owner.so_client); if (b_left <= 0) return b_left; b_left = display_printf(dspbuf, "} oh="); if (b_left <= 0) return b_left; b_left = display_opaque_value(dspbuf, owner->so_owner_val, owner->so_owner_len); if (b_left <= 0) return b_left; return display_printf(dspbuf, " svid=%d refcount=%d", owner->so_owner.so_nlm_owner.so_nlm_svid, atomic_fetch_int32_t(&owner->so_refcount)); } /** * @brief Display an NLM owner in the hash table * * @param[in] buff The key * @param[out] str Output buffer * * @return Length of output string. */ int display_nlm_owner_key(struct gsh_buffdesc *buff, char *str) { struct display_buffer dspbuf = {HASHTABLE_DISPLAY_STRLEN, str, str}; display_nlm_owner(&dspbuf, buff->addr); return display_buffer_len(&dspbuf); } /** * @brief Display an NLM owner in the hash table * * @param[in] buff The value * @param[out] str Output buffer * * @return Length of output string. */ int display_nlm_owner_val(struct gsh_buffdesc *buff, char *str) { struct display_buffer dspbuf = {HASHTABLE_DISPLAY_STRLEN, str, str}; display_nlm_owner(&dspbuf, buff->addr); return display_buffer_len(&dspbuf); } /** * @brief Compare NLM owners * * @param[in] owner1 A client * @param[in] owner2 Another client * * @retval 0 on equality. * @retval 1 on inequality. */ int compare_nlm_owner(state_owner_t *owner1, state_owner_t *owner2) { if (isFullDebug(COMPONENT_STATE) && isDebug(COMPONENT_HASHTABLE)) { char str1[LOG_BUFF_LEN / 2] = "\0"; char str2[LOG_BUFF_LEN / 2] = "\0"; struct display_buffer dspbuf1 = {sizeof(str1), str1, str1}; struct display_buffer dspbuf2 = {sizeof(str2), str2, str2}; display_nlm_owner(&dspbuf1, owner1); display_nlm_owner(&dspbuf2, owner2); LogFullDebug(COMPONENT_STATE, "{%s} vs {%s}", str1, str2); } if (owner1 == NULL || owner2 == NULL) return 1; if (owner1 == owner2) return 0; if (compare_nlm_client (owner1->so_owner.so_nlm_owner.so_client, owner2->so_owner.so_nlm_owner.so_client) != 0) return 1; if (owner1->so_owner.so_nlm_owner.so_nlm_svid != owner2->so_owner.so_nlm_owner.so_nlm_svid) return 1; if (owner1->so_owner_len != owner2->so_owner_len) return 1; return memcmp(owner1->so_owner_val, owner2->so_owner_val, owner1->so_owner_len); } /** * @brief Compare NLM owners in the hash table * * @param[in] buff1 A key * @param[in] buff2 Another key * * @retval 0 on equality. * @retval 1 on inequality. */ int compare_nlm_owner_key(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2) { return compare_nlm_owner(buff1->addr, buff2->addr); } /** * @brief Calculate hash index for an NLM owner key * * @todo Replace with a good hash function. * * @param[in] hparam Hash params * @param[out] key Key to hash * * @return The hash index. */ uint32_t nlm_owner_value_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { unsigned int sum = 0; unsigned int i; unsigned long res; state_owner_t *pkey = key->addr; /* Compute the sum of all the characters */ for (i = 0; i < pkey->so_owner_len; i++) sum += (unsigned char)pkey->so_owner_val[i]; res = (unsigned long)(pkey->so_owner.so_nlm_owner.so_nlm_svid) + (unsigned long)sum + (unsigned long)pkey->so_owner_len; if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_STATE, "value = %lu", res % hparam->index_size); return (unsigned long)(res % hparam->index_size); } /** * @brief Calculate RBT hash for an NLM owner key * * @todo Replace with a good hash function. * * @param[in] hparam Hash params * @param[out] key Key to hash * * @return The RBT hash. */ uint64_t nlm_owner_rbt_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { unsigned int sum = 0; unsigned int i; unsigned long res; state_owner_t *pkey = key->addr; /* Compute the sum of all the characters */ for (i = 0; i < pkey->so_owner_len; i++) sum += (unsigned char)pkey->so_owner_val[i]; res = (unsigned long)(pkey->so_owner.so_nlm_owner.so_nlm_svid) + (unsigned long)sum + (unsigned long)pkey->so_owner_len; if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_STATE, "rbt = %lu", res); return res; } /* state_id_rbt_hash_func */ static hash_parameter_t nsm_client_hash_param = { .index_size = PRIME_STATE, .hash_func_key = nsm_client_value_hash_func, .hash_func_rbt = nsm_client_rbt_hash_func, .compare_key = compare_nsm_client_key, .key_to_str = display_nsm_client_key, .val_to_str = display_nsm_client_val, .flags = HT_FLAG_NONE, }; static hash_parameter_t nlm_client_hash_param = { .index_size = PRIME_STATE, .hash_func_key = nlm_client_value_hash_func, .hash_func_rbt = nlm_client_rbt_hash_func, .compare_key = compare_nlm_client_key, .key_to_str = display_nlm_client_key, .val_to_str = display_nlm_client_val, .flags = HT_FLAG_NONE, }; static hash_parameter_t nlm_owner_hash_param = { .index_size = PRIME_STATE, .hash_func_key = nlm_owner_value_hash_func, .hash_func_rbt = nlm_owner_rbt_hash_func, .compare_key = compare_nlm_owner_key, .key_to_str = display_nlm_owner_key, .val_to_str = display_nlm_owner_val, .flags = HT_FLAG_NONE, }; /** * @brief Init the hashtables for NLM support * * @return 0 if successful, -1 otherwise */ int Init_nlm_hash(void) { ht_nsm_client = hashtable_init(&nsm_client_hash_param); if (ht_nsm_client == NULL) { LogCrit(COMPONENT_STATE, "Cannot init NSM Client cache"); return -1; } ht_nlm_client = hashtable_init(&nlm_client_hash_param); if (ht_nlm_client == NULL) { LogCrit(COMPONENT_STATE, "Cannot init NLM Client cache"); return -1; } ht_nlm_owner = hashtable_init(&nlm_owner_hash_param); if (ht_nlm_owner == NULL) { LogCrit(COMPONENT_STATE, "Cannot init NLM Owner cache"); return -1; } return 0; } /* Init_nlm_hash */ /******************************************************************************* * * NSM Client Routines * ******************************************************************************/ /** * @brief Take a reference on NSM client * * @param[in] client The client to ref */ void inc_nsm_client_ref(state_nsm_client_t *client) { (void) atomic_inc_int32_t(&client->ssc_refcount); } /** * @brief Free an NSM client * * @param[in] client Client to free */ void free_nsm_client(state_nsm_client_t *client) { gsh_free(client->ssc_nlm_caller_name); if (client->ssc_client != NULL) put_gsh_client(client->ssc_client); PTHREAD_MUTEX_destroy(&client->ssc_mutex); gsh_free(client); } /** * @brief Relinquish a reference on an NSM client * * @param[in] client The client to release */ void dec_nsm_client_ref(state_nsm_client_t *client) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; bool str_valid = false; struct hash_latch latch; hash_error_t rc; struct gsh_buffdesc buffkey; struct gsh_buffdesc old_value; int32_t refcount; if (isDebug(COMPONENT_STATE)) { display_nsm_client(&dspbuf, client); str_valid = true; } refcount = atomic_dec_int32_t(&client->ssc_refcount); if (refcount > 0) { if (str_valid) LogFullDebug(COMPONENT_STATE, "Decrement refcount now=%" PRId32 " {%s}", refcount, str); return; } if (str_valid) LogFullDebug(COMPONENT_STATE, "Try to remove {%s}", str); buffkey.addr = client; buffkey.len = sizeof(*client); /* Since the refcnt is zero, another thread that needs this * entry may delete this nsm client to insert its own. * So expect not to find this nsm client or find someone * else's nsm client! */ rc = hashtable_getlatch(ht_nsm_client, &buffkey, &old_value, true, &latch); switch (rc) { case HASHTABLE_SUCCESS: if (old_value.addr == client) { /* our nsm client */ hashtable_deletelatched(ht_nsm_client, &buffkey, &latch, NULL, NULL); } break; case HASHTABLE_ERROR_NO_SUCH_KEY: break; default: if (!str_valid) display_nsm_client(&dspbuf, client); LogCrit(COMPONENT_STATE, "Error %s, could not find {%s}", hash_table_err_to_str(rc), str); return; } hashtable_releaselatched(ht_nsm_client, &latch); LogFullDebug(COMPONENT_STATE, "Free {%s}", str); nsm_unmonitor(client); free_nsm_client(client); } /** * @brief Get an NSM client * * @param[in] care Care status * @param[in] xprt RPC transport * @param[in] caller_name Caller name * * @return NSM client or NULL. */ state_nsm_client_t *get_nsm_client(care_t care, SVCXPRT *xprt, char *caller_name) { state_nsm_client_t key; state_nsm_client_t *pclient; char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; struct hash_latch latch; hash_error_t rc; struct gsh_buffdesc buffkey; struct gsh_buffdesc buffval; if (caller_name == NULL) return NULL; memset(&key, 0, sizeof(key)); if (nfs_param.core_param.nsm_use_caller_name) { key.ssc_nlm_caller_name_len = strlen(caller_name); if (key.ssc_nlm_caller_name_len > LM_MAXSTRLEN) return NULL; key.ssc_nlm_caller_name = caller_name; } else if (op_ctx->client == NULL) { LogCrit(COMPONENT_STATE, "No gsh_client for caller_name %s", caller_name); return NULL; } else { key.ssc_nlm_caller_name = op_ctx->client->hostaddr_str; key.ssc_nlm_caller_name_len = strlen(key.ssc_nlm_caller_name); key.ssc_client = op_ctx->client; } if (isFullDebug(COMPONENT_STATE)) { display_nsm_client(&dspbuf, &key); LogFullDebug(COMPONENT_STATE, "Find {%s}", str); } buffkey.addr = &key; buffkey.len = sizeof(key); rc = hashtable_getlatch(ht_nsm_client, &buffkey, &buffval, true, &latch); switch (rc) { case HASHTABLE_SUCCESS: pclient = buffval.addr; if (atomic_inc_int32_t(&pclient->ssc_refcount) == 1) { /* This nsm client is in the process of getting * deleted. Delete it from the hash table and * pretend as though we didn't find it. */ (void)atomic_dec_int32_t(&pclient->ssc_refcount); hashtable_deletelatched(ht_nsm_client, &buffkey, &latch, NULL, NULL); break; } /* Return the found NSM Client */ if (isFullDebug(COMPONENT_STATE)) { display_nsm_client(&dspbuf, pclient); LogFullDebug(COMPONENT_STATE, "Found {%s}", str); } hashtable_releaselatched(ht_nsm_client, &latch); if (care == CARE_MONITOR && !nsm_monitor(pclient)) { dec_nsm_client_ref(pclient); pclient = NULL; } return pclient; case HASHTABLE_ERROR_NO_SUCH_KEY: break; default: display_nsm_client(&dspbuf, &key); LogCrit(COMPONENT_STATE, "Error %s, could not find {%s}", hash_table_err_to_str(rc), str); return NULL; } /* Not found, but we don't care, return NULL */ if (care == CARE_NOT) { /* Return the found NSM Client */ if (isFullDebug(COMPONENT_STATE)) { display_nsm_client(&dspbuf, &key); LogFullDebug(COMPONENT_STATE, "Ignoring {%s}", str); } hashtable_releaselatched(ht_nsm_client, &latch); return NULL; } pclient = gsh_malloc(sizeof(*pclient)); /* Copy everything over */ memcpy(pclient, &key, sizeof(key)); PTHREAD_MUTEX_init(&pclient->ssc_mutex, NULL); pclient->ssc_nlm_caller_name = gsh_strdup(key.ssc_nlm_caller_name); glist_init(&pclient->ssc_lock_list); glist_init(&pclient->ssc_share_list); pclient->ssc_refcount = 1; if (op_ctx->client != NULL) { pclient->ssc_client = op_ctx->client; inc_gsh_client_refcount(op_ctx->client); } if (isFullDebug(COMPONENT_STATE)) { display_nsm_client(&dspbuf, pclient); LogFullDebug(COMPONENT_STATE, "New {%s}", str); } buffkey.addr = pclient; buffkey.len = sizeof(*pclient); buffval.addr = pclient; buffval.len = sizeof(*pclient); rc = hashtable_setlatched(ht_nsm_client, &buffval, &buffval, &latch, false, NULL, NULL); /* An error occurred, return NULL */ if (rc != HASHTABLE_SUCCESS) { display_nsm_client(&dspbuf, pclient); LogCrit(COMPONENT_STATE, "Error %s, inserting {%s}", hash_table_err_to_str(rc), str); PTHREAD_MUTEX_destroy(&pclient->ssc_mutex); free_nsm_client(pclient); return NULL; } if (care != CARE_MONITOR || nsm_monitor(pclient)) return pclient; /* Failed to monitor, release client reference * and almost certainly remove it from the hash table. */ dec_nsm_client_ref(pclient); return NULL; } /******************************************************************************* * * NLM Client Routines * ******************************************************************************/ /** * @brief Free an NLM client * * @param[in] client The client to free */ void free_nlm_client(state_nlm_client_t *client) { if (client->slc_nsm_client != NULL) dec_nsm_client_ref(client->slc_nsm_client); gsh_free(client->slc_nlm_caller_name); gsh_free(client); } /** * @brief Take a reference on an NLM client * * @param[in] client Client to reference */ void inc_nlm_client_ref(state_nlm_client_t *client) { (void) atomic_inc_int32_t(&client->slc_refcount); } /** * @brief Relinquish a reference on an NLM client * * @param[in] client Client to release */ void dec_nlm_client_ref(state_nlm_client_t *client) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; bool str_valid = false; struct hash_latch latch; hash_error_t rc; struct gsh_buffdesc buffkey; struct gsh_buffdesc old_value; struct gsh_buffdesc old_key; int32_t refcount; if (isDebug(COMPONENT_STATE)) { display_nlm_client(&dspbuf, client); str_valid = true; } refcount = atomic_dec_int32_t(&client->slc_refcount); if (refcount > 0) { if (str_valid) LogFullDebug(COMPONENT_STATE, "Decrement refcount now=%" PRId32 " {%s}", refcount, str); return; } if (str_valid) LogFullDebug(COMPONENT_STATE, "Try to remove {%s}", str); buffkey.addr = client; buffkey.len = sizeof(*client); /* Get the hash table entry and hold latch */ rc = hashtable_getlatch(ht_nlm_client, &buffkey, &old_value, true, &latch); /* Since the refcnt is zero, another thread that needs this * entry may delete this nlm client to insert its own nlm * client. So expect not to find this nlm client or find someone * else's nlm client! */ switch (rc) { case HASHTABLE_ERROR_NO_SUCH_KEY: break; case HASHTABLE_SUCCESS: if (old_value.addr == client) { /* our nlm client */ hashtable_deletelatched(ht_nlm_client, &buffkey, &latch, &old_key, &old_value); } break; default: if (!str_valid) display_nlm_client(&dspbuf, client); LogCrit(COMPONENT_STATE, "Error %s, could not find {%s}, client=%p", hash_table_err_to_str(rc), str, client); return; } /* Release the latch */ hashtable_releaselatched(ht_nlm_client, &latch); if (str_valid) LogFullDebug(COMPONENT_STATE, "Free {%s}", str); free_nlm_client(client); } /** * @brief Get an NLM client * * @param[in] care Care status * @param[in] xprt RPC transport * @param[in] nsm_client Related NSM client * @param[in] caller_name Caller name * * @return NLM client or NULL. */ state_nlm_client_t *get_nlm_client(care_t care, SVCXPRT *xprt, state_nsm_client_t *nsm_client, char *caller_name) { state_nlm_client_t key; state_nlm_client_t *pclient; char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; struct hash_latch latch; hash_error_t rc; struct gsh_buffdesc buffkey; struct gsh_buffdesc buffval; struct sockaddr_storage local_addr; socklen_t addr_len; uint32_t refcount; if (caller_name == NULL) return NULL; memset(&key, 0, sizeof(key)); key.slc_nsm_client = nsm_client; key.slc_nlm_caller_name_len = strlen(caller_name); key.slc_client_type = svc_get_xprt_type(xprt); addr_len = sizeof(local_addr); if (getsockname(xprt->xp_fd, (struct sockaddr *)&local_addr, &addr_len) == -1) { LogEvent(COMPONENT_CLIENTID, "Failed to get local addr."); } else { memcpy(&(key.slc_server_addr), &local_addr, sizeof(struct sockaddr_storage)); } if (key.slc_nlm_caller_name_len > LM_MAXSTRLEN) return NULL; key.slc_nlm_caller_name = caller_name; if (isFullDebug(COMPONENT_STATE)) { display_nlm_client(&dspbuf, &key); LogFullDebug(COMPONENT_STATE, "Find {%s}", str); } buffkey.addr = &key; buffkey.len = sizeof(key); rc = hashtable_getlatch(ht_nlm_client, &buffkey, &buffval, true, &latch); switch (rc) { case HASHTABLE_SUCCESS: pclient = buffval.addr; if (isFullDebug(COMPONENT_STATE)) { display_nlm_client(&dspbuf, pclient); LogFullDebug(COMPONENT_STATE, "Found {%s}", str); } refcount = atomic_inc_int32_t(&pclient->slc_refcount); if (refcount == 1) { /* This nlm client is in the process of getting * deleted. Let us delete it from the hash table * and pretend as though it isn't found in the * hash table. The thread that is trying to * delete this entry will not find it in the * hash table but will free its nlm client. */ (void)atomic_dec_int32_t(&pclient->slc_refcount); hashtable_deletelatched(ht_nlm_client, &buffkey, &latch, NULL, NULL); goto not_found; } hashtable_releaselatched(ht_nlm_client, &latch); if (care == CARE_MONITOR && !nsm_monitor(nsm_client)) { dec_nlm_client_ref(pclient); pclient = NULL; } return pclient; case HASHTABLE_ERROR_NO_SUCH_KEY: goto not_found; default: display_nlm_client(&dspbuf, &key); LogCrit(COMPONENT_STATE, "Error %s, could not find {%s}", hash_table_err_to_str(rc), str); return NULL; } not_found: /* Not found, but we don't care, return NULL */ if (care == CARE_NOT) { /* Return the found NLM Client */ if (isFullDebug(COMPONENT_STATE)) { display_nlm_client(&dspbuf, &key); LogFullDebug(COMPONENT_STATE, "Ignoring {%s}", str); } hashtable_releaselatched(ht_nlm_client, &latch); return NULL; } pclient = gsh_malloc(sizeof(*pclient)); /* Copy everything over */ memcpy(pclient, &key, sizeof(key)); pclient->slc_nlm_caller_name = gsh_strdup(key.slc_nlm_caller_name); /* Take a reference to the NSM Client */ inc_nsm_client_ref(nsm_client); pclient->slc_refcount = 1; if (isFullDebug(COMPONENT_STATE)) { display_nlm_client(&dspbuf, pclient); LogFullDebug(COMPONENT_STATE, "New {%s}", str); } buffkey.addr = pclient; buffkey.len = sizeof(*pclient); buffval.addr = pclient; buffval.len = sizeof(*pclient); rc = hashtable_setlatched(ht_nlm_client, &buffval, &buffval, &latch, false, NULL, NULL); /* An error occurred, return NULL */ if (rc != HASHTABLE_SUCCESS) { display_nlm_client(&dspbuf, pclient); LogCrit(COMPONENT_STATE, "Error %s, inserting {%s}", hash_table_err_to_str(rc), str); free_nlm_client(pclient); return NULL; } if (care != CARE_MONITOR || nsm_monitor(nsm_client)) return pclient; /* Failed to monitor, release client reference * and almost certainly remove it from the hash table. */ dec_nlm_client_ref(pclient); return NULL; } /******************************************************************************* * * NLM Owner Routines * ******************************************************************************/ /** * @brief Free an NLM owner object * * @param[in] owner Stored owner */ void free_nlm_owner(state_owner_t *owner) { if (owner->so_owner.so_nlm_owner.so_client != NULL) dec_nlm_client_ref(owner->so_owner.so_nlm_owner.so_client); } /** * @brief Initialize an NLM owner object * * @param[in] owner Stored owner */ static void init_nlm_owner(state_owner_t *owner) { inc_nlm_client_ref(owner->so_owner.so_nlm_owner.so_client); glist_init(&owner->so_owner.so_nlm_owner.so_nlm_shares); } /** * @brief Get an NLM owner * * @param[in] care Care status * @param[in] client Related NLM client * @param[in] oh Object handle * @param[in] svid Owner ID */ state_owner_t *get_nlm_owner(care_t care, state_nlm_client_t *client, netobj *oh, uint32_t svid) { state_owner_t key; if (client == NULL || oh == NULL || oh->n_len > MAX_NETOBJ_SZ) return NULL; memset(&key, 0, sizeof(key)); key.so_type = STATE_LOCK_OWNER_NLM; key.so_owner.so_nlm_owner.so_client = client; key.so_owner.so_nlm_owner.so_nlm_svid = svid; key.so_owner_len = oh->n_len; key.so_owner_val = oh->n_bytes; return get_state_owner(care, &key, init_nlm_owner, NULL); } /** @} */ nfs-ganesha-2.6.0/src/SAL/nlm_state.c000066400000000000000000000312041324272410200172460ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright Panasas Inc (2015) * contributor: Frank S Filz ffilzlnx@mindspring.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup SAL State abstraction layer * @{ */ /** * @file nlm_state.c * @brief The management of the NLM state caches. */ #include "config.h" #include #include #include #include "city.h" #include "sal_functions.h" #include "nsm.h" #include "log.h" #include "fsal.h" /** * @brief NLM States */ hash_table_t *ht_nlm_states; /******************************************************************************* * * NLM State Routines * ******************************************************************************/ /** * @brief Display an NLM State * * @param[in/out] dspbuf display_buffer describing output string * @param[in] key The NLM State * * @return the bytes remaining in the buffer. */ int display_nlm_state(struct display_buffer *dspbuf, state_t *key) { int b_left; if (key == NULL) return display_printf(dspbuf, "NLM State "); b_left = display_printf(dspbuf, "NLM State %p: ", key); if (b_left <= 0) return b_left; return b_left; } /** * @brief Display an NLM State in the hash table * * @param[in] buff The key * @param[out] str Output buffer * * @return Length of output string. */ int display_nlm_state_key(struct gsh_buffdesc *buff, char *str) { struct display_buffer dspbuf = {HASHTABLE_DISPLAY_STRLEN, str, str}; display_nlm_state(&dspbuf, buff->addr); return display_buffer_len(&dspbuf); } /** * @brief Display an NLM State in the hash table * * @param[in] buff The value * @param[out] str Output buffer * * @return Length of output string. */ int display_nlm_state_val(struct gsh_buffdesc *buff, char *str) { struct display_buffer dspbuf = {HASHTABLE_DISPLAY_STRLEN, str, str}; display_nlm_state(&dspbuf, buff->addr); return display_buffer_len(&dspbuf); } /** * @brief Compare NLM States * * @param[in] state1 An NLM State * @param[in] state2 Another NLM State * * @retval 0 on equality. * @retval 1 on inequality. */ int compare_nlm_state(state_t *state1, state_t *state2) { if (isFullDebug(COMPONENT_STATE) && isDebug(COMPONENT_HASHTABLE)) { char str1[LOG_BUFF_LEN / 2] = "\0"; char str2[LOG_BUFF_LEN / 2] = "\0"; struct display_buffer dspbuf1 = {sizeof(str1), str1, str1}; struct display_buffer dspbuf2 = {sizeof(str2), str2, str2}; display_nlm_state(&dspbuf1, state1); display_nlm_state(&dspbuf2, state2); LogFullDebug(COMPONENT_STATE, "{%s} vs {%s}", str1, str2); } if (state1 == NULL || state2 == NULL) return 1; if (state1 == state2) return 0; return state1->state_type != state2->state_type || state1->state_owner != state2->state_owner || state1->state_export != state2->state_export || state1->state_obj != state2->state_obj; } /** * @brief Compare NLM States in the hash table * * @param[in] buff1 A key * @param[in] buff2 Another key * * @retval 0 on equality. * @retval 1 on inequality. */ int compare_nlm_state_key(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2) { return compare_nlm_state(buff1->addr, buff2->addr); } /** * @brief Calculate hash index for an NSM key * * @todo Replace with a good hash function. * * @param[in] hparam Hash params * @param[out] key Key to hash * * @return The hash index. */ uint32_t nlm_state_value_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { state_t *pkey = key->addr; uint64_t hk; char *addr = (char *)&pkey->state_owner; /* We hash based on the owner pointer, and the object key. This depends * on them being sequential in memory. */ hk = CityHash64WithSeed(addr, sizeof(pkey->state_owner) + sizeof(pkey->state_obj), 557); if (pkey->state_type == STATE_TYPE_NLM_SHARE) hk = ~hk; if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_STATE, "value = %"PRIx32, (uint32_t)(hk % hparam->index_size)); return hk % hparam->index_size; } /** * @brief Calculate RBT hash for an NSM key * * @todo Replace with a good hash function. * * @param[in] hparam Hash params * @param[out] key Key to hash * * @return The RBT hash. */ uint64_t nlm_state_rbt_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { state_t *pkey = key->addr; uint64_t hk; char *addr = (char *)&pkey->state_owner; /* We hash based on the owner pointer, and the object key. This depends * on them being sequential in memory. */ hk = CityHash64WithSeed(addr, sizeof(pkey->state_owner) + sizeof(pkey->state_obj), 557); if (pkey->state_type == STATE_TYPE_NLM_SHARE) hk = ~hk; if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_STATE, "value = %"PRIx64, hk % hparam->index_size); return hk % hparam->index_size; } static hash_parameter_t nlm_state_hash_param = { .index_size = PRIME_STATE, .hash_func_key = nlm_state_value_hash_func, .hash_func_rbt = nlm_state_rbt_hash_func, .compare_key = compare_nlm_state_key, .key_to_str = display_nlm_state_key, .val_to_str = display_nlm_state_val, .flags = HT_FLAG_NONE, }; /** * @brief Init the hashtables for NLM state support * * @return 0 if successful, -1 otherwise */ int Init_nlm_state_hash(void) { ht_nlm_states = hashtable_init(&nlm_state_hash_param); if (ht_nlm_states == NULL) { LogCrit(COMPONENT_STATE, "Cannot init NLM States cache"); return -1; } return 0; } /** * @brief Relinquish a reference on an NLM State * * @param[in] state The NLM State to release */ void dec_nlm_state_ref(state_t *state) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; bool str_valid = false; struct hash_latch latch; hash_error_t rc; struct gsh_buffdesc buffkey; struct gsh_buffdesc old_value; int32_t refcount; struct fsal_obj_handle *obj; if (isDebug(COMPONENT_STATE)) { display_nlm_state(&dspbuf, state); str_valid = true; } refcount = atomic_dec_int32_t(&state->state_refcount); if (refcount > 0) { if (str_valid) LogFullDebug(COMPONENT_STATE, "Decrement refcount now=%" PRId32 " {%s}", refcount, str); return; } if (str_valid) LogFullDebug(COMPONENT_STATE, "Try to remove {%s}", str); buffkey.addr = state; buffkey.len = sizeof(*state); /* Get the hash table entry and hold latch */ rc = hashtable_getlatch(ht_nlm_states, &buffkey, &old_value, true, &latch); /* Another thread that needs this entry might have deleted this * nlm state to insert its own nlm state. So expect not to find * this nlm state or find someone else's nlm state! */ switch (rc) { case HASHTABLE_SUCCESS: if (old_value.addr == state) { /* our own state */ hashtable_deletelatched(ht_nlm_states, &buffkey, &latch, NULL, NULL); } break; case HASHTABLE_ERROR_NO_SUCH_KEY: break; default: if (!str_valid) display_nlm_state(&dspbuf, state); LogCrit(COMPONENT_STATE, "Error %s, could not find {%s}", hash_table_err_to_str(rc), str); return; } /* Release the latch */ hashtable_releaselatched(ht_nlm_states, &latch); LogFullDebug(COMPONENT_STATE, "Free {%s}", str); dec_state_owner_ref(state->state_owner); put_gsh_export(state->state_export); obj = get_state_obj_ref(state); if (obj == NULL) { LogDebug(COMPONENT_STATE, "Entry for state is stale"); return; } /* We need to close the state before freeing the state. */ (void) obj->obj_ops.close2(obj, state); state->state_exp->exp_ops.free_state(state->state_exp, state); /* Release 2 refs: our sentinal one, plus the one from * get_state_obj_ref() */ obj->obj_ops.put_ref(obj); obj->obj_ops.put_ref(obj); } /** * @brief Get an NLM State * * @param[in] state_type type of state (LOCK or SHARE) * @param[in] state_obj FSAL obj state applies to * @param[in] state_owner NLM owner of the state * @param[in] care Indicates to what degree the caller cares about * actually getting a state. * @param[in] nsm_state NSM state value for locks, only valid when * care == CARE_MONITOR * @param[out] pstate Pointer to return the found state in * * @return NLM Status code or 0 if no special return */ int get_nlm_state(enum state_type state_type, struct fsal_obj_handle *state_obj, state_owner_t *state_owner, care_t care, uint32_t nsm_state, state_t **pstate) { state_t key; state_t *state; char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; struct hash_latch latch; hash_error_t rc; struct gsh_buffdesc buffkey; struct gsh_buffdesc buffval; *pstate = NULL; memset(&key, 0, sizeof(key)); key.state_type = state_type; key.state_owner = state_owner; key.state_export = op_ctx->ctx_export; key.state_seqid = nsm_state; key.state_obj = state_obj; if (isFullDebug(COMPONENT_STATE)) { display_nlm_state(&dspbuf, &key); LogFullDebug(COMPONENT_STATE, "Find {%s}", str); } buffkey.addr = &key; buffkey.len = sizeof(key); rc = hashtable_getlatch(ht_nlm_states, &buffkey, &buffval, true, &latch); switch (rc) { case HASHTABLE_SUCCESS: state = buffval.addr; if (care == CARE_MONITOR && state->state_seqid != nsm_state) { /* We are getting new locks before the old ones * are gone. We need to unhash this state_t and * create a new one. * * Keep the latch after the delete to proceed with * the new insert. */ hashtable_deletelatched(ht_nlm_states, &buffkey, &latch, NULL, NULL); break; } if (atomic_inc_int32_t(&state->state_refcount) == 1) { /* The state is in the process of getting * deleted. Delete from the hashtable and * pretend as though we didn't find it. */ (void)atomic_dec_int32_t(&state->state_refcount); hashtable_deletelatched(ht_nlm_states, &buffkey, &latch, NULL, NULL); break; } /* Return the found NLM State */ if (isFullDebug(COMPONENT_STATE)) { display_nlm_state(&dspbuf, state); LogFullDebug(COMPONENT_STATE, "Found {%s}", str); } hashtable_releaselatched(ht_nlm_states, &latch); *pstate = state; return 0; case HASHTABLE_ERROR_NO_SUCH_KEY: break; default: /* An error occurred, return NULL */ display_nlm_state(&dspbuf, &key); LogCrit(COMPONENT_STATE, "Error %s, could not find {%s}", hash_table_err_to_str(rc), str); *pstate = NULL; return NLM4_DENIED_NOLOCKS; } /* If we don't care at all, or only care about owner, we don't want to * create a new state. */ if (care == CARE_NOT || care == CARE_OWNER) { hashtable_releaselatched(ht_nlm_states, &latch); return 0; } state = op_ctx->fsal_export->exp_ops.alloc_state(op_ctx->fsal_export, state_type, NULL); /* Copy everything over */ state->state_obj = state_obj; state->state_owner = state_owner; state->state_export = op_ctx->ctx_export; state->state_seqid = nsm_state; PTHREAD_MUTEX_init(&state->state_mutex, NULL); if (state_type == STATE_TYPE_NLM_LOCK) glist_init(&state->state_data.lock.state_locklist); state->state_refcount = 1; if (isFullDebug(COMPONENT_STATE)) { display_nlm_state(&dspbuf, state); LogFullDebug(COMPONENT_STATE, "New {%s}", str); } buffkey.addr = state; buffkey.len = sizeof(*state); buffval.addr = state; buffval.len = sizeof(*state); state_obj->obj_ops.get_ref(state_obj); rc = hashtable_setlatched(ht_nlm_states, &buffval, &buffval, &latch, false, NULL, NULL); /* An error occurred, return NULL */ if (rc != HASHTABLE_SUCCESS) { display_nlm_state(&dspbuf, state); LogCrit(COMPONENT_STATE, "Error %s, inserting {%s}", hash_table_err_to_str(rc), str); PTHREAD_MUTEX_destroy(&state->state_mutex); /* Free the ref taken above and the state. * No need to close here, the state was never opened. */ state->state_exp->exp_ops.free_state(state->state_exp, state); state_obj->obj_ops.put_ref(state_obj); *pstate = NULL; return NLM4_DENIED_NOLOCKS; } get_gsh_export_ref(state->state_export); inc_state_owner_ref(state->state_owner); *pstate = state; return 0; } nfs-ganesha-2.6.0/src/SAL/recovery/000077500000000000000000000000001324272410200167525ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/SAL/recovery/recovery_fs.c000066400000000000000000000502711324272410200214510ustar00rootroot00000000000000#include "config.h" #include "log.h" #include "nfs_core.h" #include "nfs4.h" #include "sal_functions.h" #include #include #include #include #include "bsd-base64.h" #include "client_mgr.h" #include "fsal.h" #include "recovery_fs.h" #define NFS_V4_OLD_DIR "v4old" char v4_recov_dir[PATH_MAX]; char v4_old_dir[PATH_MAX]; char recov_root[PATH_MAX]; /** * @brief convert clientid opaque bytes as a hex string for mkdir purpose. * * @param[in,out] dspbuf The buffer. * @param[in] value The bytes to display * @param[in] len The number of bytes to display * * @return the bytes remaining in the buffer. * */ static int fs_convert_opaque_value_max_for_dir(struct display_buffer *dspbuf, void *value, int len, int max) { unsigned int i = 0; int b_left = display_start(dspbuf); int cpy = len; if (b_left <= 0) return 0; /* Check that the length is ok * If the value is empty, display EMPTY value. */ if (len <= 0 || len > max) return 0; /* If the value is NULL, display NULL value. */ if (value == NULL) return 0; /* Determine if the value is entirely printable characters, */ /* and it contains no slash character (reserved for filename) */ for (i = 0; i < len; i++) if ((!isprint(((char *)value)[i])) || (((char *)value)[i] == '/')) break; if (i == len) { /* Entirely printable character, so we will just copy the * characters into the buffer (to the extent there is room * for them). */ b_left = display_len_cat(dspbuf, value, cpy); } else { b_left = display_opaque_bytes(dspbuf, value, cpy); } if (b_left <= 0) return 0; return b_left; } /** * @brief generate a name that identifies this client * * This name will be used to know that a client was talking to the * server before a restart so that it will be allowed to do reclaims * during grace period. * * @param[in] clientid Client record */ static void fs_create_clid_name(nfs_client_id_t *clientid) { nfs_client_record_t *cl_rec = clientid->cid_client_record; const char *str_client_addr = "(unknown)"; char cidstr[PATH_MAX] = { 0, }; struct display_buffer dspbuf = {sizeof(cidstr), cidstr, cidstr}; char cidstr_len[20]; int total_len; /* get the caller's IP addr */ if (clientid->gsh_client != NULL) str_client_addr = clientid->gsh_client->hostaddr_str; if (fs_convert_opaque_value_max_for_dir(&dspbuf, cl_rec->cr_client_val, cl_rec->cr_client_val_len, PATH_MAX) > 0) { /* fs_convert_opaque_value_max_for_dir does not prefix * the "(:". So we need to do it here */ snprintf(cidstr_len, sizeof(cidstr_len), "%zd", strlen(cidstr)); total_len = strlen(cidstr) + strlen(str_client_addr) + 5 + strlen(cidstr_len); /* hold both long form clientid and IP */ clientid->cid_recov_tag = gsh_malloc(total_len); (void) snprintf(clientid->cid_recov_tag, total_len, "%s-(%s:%s)", str_client_addr, cidstr_len, cidstr); } LogDebug(COMPONENT_CLIENTID, "Created client name [%s]", clientid->cid_recov_tag); } void fs_create_recov_dir(void) { int err; snprintf(recov_root, PATH_MAX, "%s", NFS_V4_RECOV_ROOT); err = mkdir(recov_root, 0755); if (err == -1 && errno != EEXIST) { LogEvent(COMPONENT_CLIENTID, "Failed to create v4 recovery dir (%s), errno=%d", recov_root, errno); } snprintf(v4_recov_dir, sizeof(v4_recov_dir), "%s/%s", recov_root, NFS_V4_RECOV_DIR); err = mkdir(v4_recov_dir, 0755); if (err == -1 && errno != EEXIST) { LogEvent(COMPONENT_CLIENTID, "Failed to create v4 recovery dir(%s), errno=%d", v4_recov_dir, errno); } snprintf(v4_old_dir, sizeof(v4_old_dir), "%s/%s", recov_root, NFS_V4_OLD_DIR); err = mkdir(v4_old_dir, 0755); if (err == -1 && errno != EEXIST) { LogEvent(COMPONENT_CLIENTID, "Failed to create v4 recovery dir(%s), errno=%d", v4_old_dir, errno); } if (nfs_param.core_param.clustered) { snprintf(v4_recov_dir, sizeof(v4_recov_dir), "%s/%s/node%d", recov_root, NFS_V4_RECOV_DIR, g_nodeid); err = mkdir(v4_recov_dir, 0755); if (err == -1 && errno != EEXIST) { LogEvent(COMPONENT_CLIENTID, "Failed to create v4 recovery dir(%s), errno=%d", v4_recov_dir, errno); } snprintf(v4_old_dir, sizeof(v4_old_dir), "%s/%s/node%d", recov_root, NFS_V4_OLD_DIR, g_nodeid); err = mkdir(v4_old_dir, 0755); if (err == -1 && errno != EEXIST) { LogEvent(COMPONENT_CLIENTID, "Failed to create v4 recovery dir(%s), errno=%d", v4_old_dir, errno); } } } void fs_add_clid(nfs_client_id_t *clientid) { int err = 0; char path[PATH_MAX] = {0}, segment[NAME_MAX + 1] = {0}; int length, position = 0; fs_create_clid_name(clientid); /* break clientid down if it is greater than max dir name */ /* and create a directory hierarchy to represent the clientid. */ snprintf(path, sizeof(path), "%s", v4_recov_dir); length = strlen(clientid->cid_recov_tag); while (position < length) { /* if the (remaining) clientid is shorter than 255 */ /* create the last level of dir and break out */ int len = strlen(&clientid->cid_recov_tag[position]); if (len <= NAME_MAX) { strcat(path, "/"); strncat(path, &clientid->cid_recov_tag[position], len); err = mkdir(path, 0700); break; } /* if (remaining) clientid is longer than 255, */ /* get the next 255 bytes and create a subdir */ strncpy(segment, &clientid->cid_recov_tag[position], NAME_MAX); strcat(path, "/"); strncat(path, segment, NAME_MAX); err = mkdir(path, 0700); if (err == -1 && errno != EEXIST) break; position += NAME_MAX; } if (err == -1 && errno != EEXIST) { LogEvent(COMPONENT_CLIENTID, "Failed to create client in recovery dir (%s), errno=%d", path, errno); } else { LogDebug(COMPONENT_CLIENTID, "Created client dir [%s]", path); } } /** * @brief Remove the revoked file handles created under a specific * client-id path on the stable storage. * * @param[in] path Path of the client-id on the stable storage. */ static void fs_rm_revoked_handles(char *path) { DIR *dp; struct dirent *dentp; char del_path[PATH_MAX]; dp = opendir(path); if (dp == NULL) { LogEvent(COMPONENT_CLIENTID, "opendir %s failed errno=%d", path, errno); return; } for (dentp = readdir(dp); dentp != NULL; dentp = readdir(dp)) { if (!strcmp(dentp->d_name, ".") || !strcmp(dentp->d_name, "..") || dentp->d_name[0] != '\x1') { continue; } snprintf(del_path, sizeof(del_path), "%s/%s", path, dentp->d_name); if (unlink(del_path) < 0) { LogEvent(COMPONENT_CLIENTID, "unlink of %s failed errno: %d", del_path, errno); } } (void)closedir(dp); } static void fs_rm_clid_impl(char *recov_dir, char *parent_path, int position) { int err; char *path; char *segment; int len, segment_len; int total_len; if (recov_dir == NULL) return; len = strlen(recov_dir); if (position == len) { /* We are at the tail directory of the clid, * remove revoked handles, if any. */ fs_rm_revoked_handles(parent_path); return; } segment = gsh_malloc(NAME_MAX+1); memset(segment, 0, NAME_MAX+1); strncpy(segment, &recov_dir[position], NAME_MAX); segment_len = strlen(segment); /* allocate enough memory for the new part of the string */ /* which is parent path + '/' + new segment */ total_len = strlen(parent_path) + segment_len + 2; path = gsh_malloc(total_len); memset(path, 0, total_len); (void) snprintf(path, total_len, "%s/%s", parent_path, segment); /* free setment as it has no use now */ gsh_free(segment); /* recursively remove the directory hirerchy which represent the *clientid */ fs_rm_clid_impl(recov_dir, path, position + segment_len); err = rmdir(path); if (err == -1) { LogEvent(COMPONENT_CLIENTID, "Failed to remove client recovery dir (%s), errno=%d", path, errno); } else { LogDebug(COMPONENT_CLIENTID, "Removed client dir [%s]", path); } gsh_free(path); } void fs_rm_clid(nfs_client_id_t *clientid) { char *recov_tag = clientid->cid_recov_tag; clientid->cid_recov_tag = NULL; fs_rm_clid_impl(recov_tag, v4_recov_dir, 0); gsh_free(recov_tag); } /** * @brief Copy and Populate revoked delegations for this client. * * Even after delegation revoke, it is possible for the client to * contiue its leas and other operatoins. Sever saves revoked delegations * in the memory so client will not be granted same delegation with * DELEG_CUR ; but it is possible that the server might reboot and has * no record of the delegatin. This list helps to reject delegations * client is obtaining through DELEG_PREV. * * @param[in] clientid Clientid that is being created. * @param[in] path Path of the directory structure. * @param[in] Target dir to copy. * @param[in] del Delete after populating */ static void fs_cp_pop_revoked_delegs(clid_entry_t *clid_ent, char *path, char *tgtdir, bool del, add_rfh_entry_hook add_rfh_entry) { struct dirent *dentp; DIR *dp; rdel_fh_t *new_ent; /* Read the contents from recov dir of this clientid. */ dp = opendir(path); if (dp == NULL) { LogEvent(COMPONENT_CLIENTID, "opendir %s failed errno=%d", path, errno); return; } for (dentp = readdir(dp); dentp != NULL; dentp = readdir(dp)) { if (!strcmp(dentp->d_name, ".") || !strcmp(dentp->d_name, "..")) continue; /* All the revoked filehandles stored with \x1 prefix */ if (dentp->d_name[0] != '\x1') { /* Something wrong; it should not happen */ LogMidDebug(COMPONENT_CLIENTID, "%s showed up along with revoked FHs. Skipping", dentp->d_name); continue; } if (tgtdir) { char lopath[PATH_MAX]; int fd; snprintf(lopath, sizeof(lopath), "%s/", tgtdir); strncat(lopath, dentp->d_name, strlen(dentp->d_name)); fd = creat(lopath, 0700); if (fd < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to copy revoked handle file %s to %s errno:%d\n", dentp->d_name, tgtdir, errno); } else { close(fd); } } /* Ignore the beginning \x1 and copy the rest (file handle) */ new_ent = add_rfh_entry(clid_ent, dentp->d_name + 1); LogFullDebug(COMPONENT_CLIENTID, "revoked handle: %s", new_ent->rdfh_handle_str); /* Since the handle is loaded into memory, go ahead and * delete it from the stable storage. */ if (del) { char del_path[PATH_MAX]; snprintf(del_path, sizeof(del_path), "%s/%s", path, dentp->d_name); if (unlink(del_path) < 0) { LogEvent(COMPONENT_CLIENTID, "unlink of %s failed errno: %d", del_path, errno); } } } (void)closedir(dp); } /** * @brief Create the client reclaim list * * When not doing a take over, first open the old state dir and read * in those entries. The reason for the two directories is in case of * a reboot/restart during grace period. Next, read in entries from * the recovery directory and then move them into the old state * directory. if called due to a take over, nodeid will be nonzero. * in this case, add that node's clientids to the existing list. Then * move those entries into the old state directory. * * @param[in] dp Recovery directory * @param[in] srcdir Path to the source directory on failover * @param[in] takeover Whether this is a takeover. * * @return POSIX error codes. */ static int fs_read_recov_clids_impl(const char *parent_path, char *clid_str, char *tgtdir, int takeover, add_clid_entry_hook add_clid_entry, add_rfh_entry_hook add_rfh_entry) { struct dirent *dentp; DIR *dp; clid_entry_t *new_ent; char *sub_path = NULL; char *new_path = NULL; char *build_clid = NULL; int rc = 0; int num = 0; char *ptr, *ptr2; char temp[10]; int cid_len, len; int segment_len; int total_len; int total_tgt_len; int total_clid_len; dp = opendir(parent_path); if (dp == NULL) { LogEvent(COMPONENT_CLIENTID, "Failed to open v4 recovery dir (%s), errno=%d", parent_path, errno); return -1; } for (dentp = readdir(dp); dentp != NULL; dentp = readdir(dp)) { /* don't add '.' and '..' entry */ if (!strcmp(dentp->d_name, ".") || !strcmp(dentp->d_name, "..")) continue; /* Skip names that start with '\x1' as they are files * representing revoked file handles */ if (dentp->d_name[0] == '\x1') continue; num++; new_path = NULL; /* construct the path by appending the subdir for the * next readdir. This recursion keeps reading the * subdirectory until reaching the end. */ segment_len = strlen(dentp->d_name); total_len = segment_len + 2 + strlen(parent_path); sub_path = gsh_malloc(total_len); memset(sub_path, 0, total_len); strcpy(sub_path, parent_path); strcat(sub_path, "/"); strncat(sub_path, dentp->d_name, segment_len); /* if tgtdir is not NULL, we need to build * nfs4old/currentnode */ if (tgtdir) { total_tgt_len = segment_len + 2 + strlen(tgtdir); new_path = gsh_malloc(total_tgt_len); memset(new_path, 0, total_tgt_len); strcpy(new_path, tgtdir); strcat(new_path, "/"); strncat(new_path, dentp->d_name, segment_len); rc = mkdir(new_path, 0700); if ((rc == -1) && (errno != EEXIST)) { LogEvent(COMPONENT_CLIENTID, "mkdir %s faied errno=%d", new_path, errno); } } /* keep building the clientid str by recursively */ /* reading the directory structure */ total_clid_len = segment_len + 1; if (clid_str) total_clid_len += strlen(clid_str); build_clid = gsh_calloc(1, total_clid_len); if (clid_str) strcpy(build_clid, clid_str); strncat(build_clid, dentp->d_name, segment_len); rc = fs_read_recov_clids_impl(sub_path, build_clid, new_path, takeover, add_clid_entry, add_rfh_entry); gsh_free(new_path); /* after recursion, if the subdir has no non-hidden * directory this is the end of this clientid str. Add * the clientstr to the list. */ if (rc == 0) { /* the clid format is * -(clid-len:long-form-clid-in-string-form) * make sure this reconstructed string is valid * by comparing clid-len and the actual * long-form-clid length in the string. This is * to prevent getting incompleted strings that * might exist due to program crash. */ if (strlen(build_clid) >= PATH_MAX) { LogEvent(COMPONENT_CLIENTID, "invalid clid format: %s, too long", build_clid); gsh_free(sub_path); gsh_free(build_clid); continue; } ptr = strchr(build_clid, '('); if (ptr == NULL) { LogEvent(COMPONENT_CLIENTID, "invalid clid format: %s", build_clid); gsh_free(sub_path); gsh_free(build_clid); continue; } ptr2 = strchr(ptr, ':'); if (ptr2 == NULL) { LogEvent(COMPONENT_CLIENTID, "invalid clid format: %s", build_clid); gsh_free(sub_path); gsh_free(build_clid); continue; } len = ptr2-ptr-1; if (len >= 9) { LogEvent(COMPONENT_CLIENTID, "invalid clid format: %s", build_clid); gsh_free(sub_path); gsh_free(build_clid); continue; } strncpy(temp, ptr+1, len); temp[len] = 0; cid_len = atoi(temp); len = strlen(ptr2); if ((len == (cid_len+2)) && (ptr2[len-1] == ')')) { new_ent = add_clid_entry(build_clid); fs_cp_pop_revoked_delegs(new_ent, sub_path, tgtdir, !takeover, add_rfh_entry); LogDebug(COMPONENT_CLIENTID, "added %s to clid list", new_ent->cl_name); } } gsh_free(build_clid); /* If this is not for takeover, remove the directory * hierarchy that represent the current clientid */ if (!takeover) { rc = rmdir(sub_path); if (rc == -1) { LogEvent(COMPONENT_CLIENTID, "Failed to rmdir (%s), errno=%d", sub_path, errno); } } gsh_free(sub_path); } (void)closedir(dp); return num; } static void fs_read_recov_clids_recover(add_clid_entry_hook add_clid_entry, add_rfh_entry_hook add_rfh_entry) { int rc; rc = fs_read_recov_clids_impl(v4_old_dir, NULL, NULL, 0, add_clid_entry, add_rfh_entry); if (rc == -1) { LogEvent(COMPONENT_CLIENTID, "Failed to read v4 recovery dir (%s)", v4_old_dir); return; } rc = fs_read_recov_clids_impl(v4_recov_dir, NULL, v4_old_dir, 0, add_clid_entry, add_rfh_entry); if (rc == -1) { LogEvent(COMPONENT_CLIENTID, "Failed to read v4 recovery dir (%s)", v4_recov_dir); return; } } /** * @brief Load clients for recovery, with no lock * * @param[in] nodeid Node, on takeover */ void fs_read_recov_clids_takeover(nfs_grace_start_t *gsp, add_clid_entry_hook add_clid_entry, add_rfh_entry_hook add_rfh_entry) { int rc; char path[PATH_MAX]; if (!gsp) { fs_read_recov_clids_recover(add_clid_entry, add_rfh_entry); return; } switch (gsp->event) { case EVENT_UPDATE_CLIENTS: snprintf(path, sizeof(path), "%s", v4_recov_dir); break; case EVENT_TAKE_IP: snprintf(path, sizeof(path), "%s/%s/%s", recov_root, gsp->ipaddr, NFS_V4_RECOV_DIR); break; case EVENT_TAKE_NODEID: snprintf(path, sizeof(path), "%s/%s/node%d", recov_root, NFS_V4_RECOV_DIR, gsp->nodeid); break; default: LogWarn(COMPONENT_STATE, "Recovery unknown event"); return; } LogEvent(COMPONENT_CLIENTID, "Recovery for nodeid %d dir (%s)", gsp->nodeid, path); rc = fs_read_recov_clids_impl(path, NULL, v4_old_dir, 1, add_clid_entry, add_rfh_entry); if (rc == -1) { LogEvent(COMPONENT_CLIENTID, "Failed to read v4 recovery dir (%s)", path); return; } } void fs_clean_old_recov_dir_impl(char *parent_path) { DIR *dp; struct dirent *dentp; char *path = NULL; int rc; int total_len; dp = opendir(parent_path); if (dp == NULL) { LogEvent(COMPONENT_CLIENTID, "Failed to open old v4 recovery dir (%s), errno=%d", v4_old_dir, errno); return; } for (dentp = readdir(dp); dentp != NULL; dentp = readdir(dp)) { /* don't remove '.' and '..' entry */ if (!strcmp(dentp->d_name, ".") || !strcmp(dentp->d_name, "..")) continue; /* If there is a filename starting with '\x1', then it is * a revoked handle, go ahead and remove it. */ if (dentp->d_name[0] == '\x1') { char del_path[PATH_MAX]; snprintf(del_path, sizeof(del_path), "%s/%s", parent_path, dentp->d_name); if (unlink(del_path) < 0) { LogEvent(COMPONENT_CLIENTID, "unlink of %s failed errno: %d", del_path, errno); } continue; } /* This is a directory, we need process files in it! */ total_len = strlen(parent_path) + strlen(dentp->d_name) + 2; path = gsh_malloc(total_len); snprintf(path, total_len, "%s/%s", parent_path, dentp->d_name); fs_clean_old_recov_dir_impl(path); rc = rmdir(path); if (rc == -1) { LogEvent(COMPONENT_CLIENTID, "Failed to remove %s, errno=%d", path, errno); } gsh_free(path); } (void)closedir(dp); } void fs_clean_old_recov_dir(void) { fs_clean_old_recov_dir_impl(v4_old_dir); } void fs_add_revoke_fh(nfs_client_id_t *delr_clid, nfs_fh4 *delr_handle) { char rhdlstr[NAME_MAX]; char path[PATH_MAX] = {0}, segment[NAME_MAX + 1] = {0}; int length, position = 0; int fd; int retval; /* Convert nfs_fh4_val into base64 encoded string */ retval = base64url_encode(delr_handle->nfs_fh4_val, delr_handle->nfs_fh4_len, rhdlstr, sizeof(rhdlstr)); assert(retval != -1); /* Parse through the clientid directory structure */ assert(delr_clid->cid_recov_tag != NULL); snprintf(path, sizeof(path), "%s", v4_recov_dir); length = strlen(delr_clid->cid_recov_tag); while (position < length) { int len = strlen(&delr_clid->cid_recov_tag[position]); if (len <= NAME_MAX) { strcat(path, "/"); strncat(path, &delr_clid->cid_recov_tag[position], len); strcat(path, "/\x1"); /* Prefix 1 to converted fh */ strncat(path, rhdlstr, strlen(rhdlstr)); fd = creat(path, 0700); if (fd < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to record revoke errno:%d\n", errno); } else { close(fd); } return; } strncpy(segment, &delr_clid->cid_recov_tag[position], NAME_MAX); strcat(path, "/"); strncat(path, segment, NAME_MAX); position += NAME_MAX; } } struct nfs4_recovery_backend fs_backend = { .recovery_init = fs_create_recov_dir, .recovery_cleanup = fs_clean_old_recov_dir, .recovery_read_clids = fs_read_recov_clids_takeover, .add_clid = fs_add_clid, .rm_clid = fs_rm_clid, .add_revoke_fh = fs_add_revoke_fh, }; void fs_backend_init(struct nfs4_recovery_backend **backend) { *backend = &fs_backend; } nfs-ganesha-2.6.0/src/SAL/recovery/recovery_fs.h000066400000000000000000000007211324272410200214510ustar00rootroot00000000000000#ifndef _RECOVERY_FS_H #define _RECOVERY_FS_H /* * Select bits from recovery_fs.c that we can reuse elsewhere */ #define NFS_V4_RECOV_DIR "v4recov" extern char v4_recov_dir[PATH_MAX]; extern char recov_root[PATH_MAX]; void fs_add_clid(nfs_client_id_t *clientid); void fs_rm_clid(nfs_client_id_t *clientid); void fs_add_revoke_fh(nfs_client_id_t *delr_clid, nfs_fh4 *delr_handle); void fs_clean_old_recov_dir_impl(char *parent_path); #endif /* _RECOVERY_FS_H */ nfs-ganesha-2.6.0/src/SAL/recovery/recovery_fs_ng.c000066400000000000000000000222161324272410200221330ustar00rootroot00000000000000#define _GNU_SOURCE #include "config.h" #include "log.h" #include "nfs_core.h" #include "nfs4.h" #include "sal_functions.h" #include #include #include #include #include #include #include "bsd-base64.h" #include "client_mgr.h" #include "fsal.h" #include "recovery_fs.h" char v4_recov_link[PATH_MAX]; /* * If we have a "legacy" fs driver database, we can allow clients to recover * using that. In order to handle it though, we must rename the thing and * set symlink for it. * * Unfortunately this is not atomic, but it should be a one-time thing. */ static void legacy_fs_db_migrate(void) { int ret; struct stat st; ret = lstat(v4_recov_link, &st); if (!ret && S_ISDIR(st.st_mode)) { char pathbuf[PATH_MAX]; char *dname; /* create empty tmpdir in same parent */ snprintf(pathbuf, sizeof(pathbuf), "%s.XXXXXX", v4_recov_link); dname = mkdtemp(pathbuf); if (!dname) { LogEvent(COMPONENT_CLIENTID, "Failed to create temp file (%s): %s", pathbuf, strerror(errno)); return; } ret = rename(v4_recov_link, dname); if (ret != 0) { LogEvent(COMPONENT_CLIENTID, "Failed to rename v4 recovery dir (%s) to (%s): %s", v4_recov_link, dname, strerror(errno)); return; } ret = symlink(basename(dname), v4_recov_link); if (ret != 0) { LogEvent(COMPONENT_CLIENTID, "Failed to set recoverydir symlink at %s: %s", dname, strerror(errno)); return; } } } static void fs_ng_create_recov_dir(void) { int err; char *newdir; char host[NI_MAXHOST]; snprintf(recov_root, PATH_MAX, "%s", NFS_V4_RECOV_ROOT); err = mkdir(recov_root, 0700); if (err == -1 && errno != EEXIST) { LogEvent(COMPONENT_CLIENTID, "Failed to create v4 recovery dir (%s): %s", recov_root, strerror(errno)); } snprintf(v4_recov_dir, sizeof(v4_recov_dir), "%s/%s", recov_root, NFS_V4_RECOV_DIR); err = mkdir(v4_recov_dir, 0700); if (err == -1 && errno != EEXIST) { LogEvent(COMPONENT_CLIENTID, "Failed to create v4 recovery dir(%s): %s", v4_recov_dir, strerror(errno)); } /* Populate link path string */ if (nfs_param.core_param.clustered) { snprintf(host, sizeof(host), "node%d", g_nodeid); } else { err = gethostname(host, sizeof(host)); if (err) { LogEvent(COMPONENT_CLIENTID, "Failed to gethostname: %s", strerror(errno)); return; } } snprintf(v4_recov_link, sizeof(v4_recov_link), "%s/%s/%s", recov_root, NFS_V4_RECOV_DIR, host); snprintf(v4_recov_dir, sizeof(v4_recov_dir), "%s.XXXXXX", v4_recov_link); newdir = mkdtemp(v4_recov_dir); if (newdir != v4_recov_dir) { LogEvent(COMPONENT_CLIENTID, "Failed to create v4 recovery dir(%s): %s", v4_recov_dir, strerror(errno)); } legacy_fs_db_migrate(); } /** * @brief Create the client reclaim list from previous database * * @param[in] dp Recovery directory * @param[in] srcdir Path to the source directory on failover * * @return POSIX error codes. */ static int fs_ng_read_recov_clids_impl(const char *parent_path, char *clid_str, add_clid_entry_hook add_clid_entry, add_rfh_entry_hook add_rfh_entry) { struct dirent *dentp; DIR *dp; clid_entry_t *new_ent; char *sub_path = NULL; char *build_clid = NULL; int rc = 0; int num = 0; char *ptr, *ptr2; char temp[10]; int cid_len, len; int segment_len; int total_len; int total_clid_len; dp = opendir(parent_path); if (dp == NULL) { LogEvent(COMPONENT_CLIENTID, "Failed to open v4 recovery dir (%s): %s", parent_path, strerror(errno)); return -1; } for (dentp = readdir(dp); dentp != NULL; dentp = readdir(dp)) { /* don't add '.' and '..' entry */ if (!strcmp(dentp->d_name, ".") || !strcmp(dentp->d_name, "..")) continue; /* Skip names that start with '\x1' as they are files * representing revoked file handles */ if (dentp->d_name[0] == '\x1') continue; num++; /* construct the path by appending the subdir for the * next readdir. This recursion keeps reading the * subdirectory until reaching the end. */ segment_len = strlen(dentp->d_name); total_len = segment_len + 2 + strlen(parent_path); sub_path = gsh_malloc(total_len); memset(sub_path, 0, total_len); strcpy(sub_path, parent_path); strcat(sub_path, "/"); strncat(sub_path, dentp->d_name, segment_len); /* keep building the clientid str by recursively */ /* reading the directory structure */ total_clid_len = segment_len + 1; if (clid_str) total_clid_len += strlen(clid_str); build_clid = gsh_calloc(1, total_clid_len); if (clid_str) strcpy(build_clid, clid_str); strncat(build_clid, dentp->d_name, segment_len); rc = fs_ng_read_recov_clids_impl(sub_path, build_clid, add_clid_entry, add_rfh_entry); /* after recursion, if the subdir has no non-hidden * directory this is the end of this clientid str. Add * the clientstr to the list. */ if (rc == 0) { /* the clid format is * -(clid-len:long-form-clid-in-string-form) * make sure this reconstructed string is valid * by comparing clid-len and the actual * long-form-clid length in the string. This is * to prevent getting incompleted strings that * might exist due to program crash. */ if (strlen(build_clid) >= PATH_MAX) { LogEvent(COMPONENT_CLIENTID, "invalid clid format: %s, too long", build_clid); gsh_free(sub_path); gsh_free(build_clid); continue; } ptr = strchr(build_clid, '('); if (ptr == NULL) { LogEvent(COMPONENT_CLIENTID, "invalid clid format: %s", build_clid); gsh_free(sub_path); gsh_free(build_clid); continue; } ptr2 = strchr(ptr, ':'); if (ptr2 == NULL) { LogEvent(COMPONENT_CLIENTID, "invalid clid format: %s", build_clid); gsh_free(sub_path); gsh_free(build_clid); continue; } len = ptr2-ptr-1; if (len >= 9) { LogEvent(COMPONENT_CLIENTID, "invalid clid format: %s", build_clid); gsh_free(sub_path); gsh_free(build_clid); continue; } strncpy(temp, ptr+1, len); temp[len] = 0; cid_len = atoi(temp); len = strlen(ptr2); if ((len == (cid_len+2)) && (ptr2[len-1] == ')')) { new_ent = add_clid_entry(build_clid); LogDebug(COMPONENT_CLIENTID, "added %s to clid list", new_ent->cl_name); } } gsh_free(build_clid); gsh_free(sub_path); } (void)closedir(dp); return num; } static void fs_ng_read_recov_clids_recover(add_clid_entry_hook add_clid_entry, add_rfh_entry_hook add_rfh_entry) { int rc; rc = fs_ng_read_recov_clids_impl(v4_recov_link, NULL, add_clid_entry, add_rfh_entry); if (rc == -1) { LogEvent(COMPONENT_CLIENTID, "Failed to read v4 recovery dir (%s)", v4_recov_link); return; } } /** * @brief Load clients for recovery, with no lock * * @param[in] nodeid Node, on takeover */ static void fs_ng_read_recov_clids(nfs_grace_start_t *gsp, add_clid_entry_hook add_clid_entry, add_rfh_entry_hook add_rfh_entry) { int rc; char path[PATH_MAX]; if (!gsp) { fs_ng_read_recov_clids_recover(add_clid_entry, add_rfh_entry); return; } /* * FIXME: make the rest of this work */ return; switch (gsp->event) { case EVENT_TAKE_NODEID: snprintf(path, sizeof(path), "%s/%s/node%d", recov_root, NFS_V4_RECOV_DIR, gsp->nodeid); break; default: LogWarn(COMPONENT_STATE, "Recovery unknown event: %d", gsp->event); return; } LogEvent(COMPONENT_CLIENTID, "Recovery for nodeid %d dir (%s)", gsp->nodeid, path); rc = fs_ng_read_recov_clids_impl(path, NULL, add_clid_entry, add_rfh_entry); if (rc == -1) { LogEvent(COMPONENT_CLIENTID, "Failed to read v4 recovery dir (%s)", path); return; } } static void fs_ng_swap_recov_dir(void) { int ret; char old_pathbuf[PATH_MAX]; char tmp_link[PATH_MAX]; char *old_path; /* save off the old link path so we can do some cleanup afterward */ old_path = realpath(v4_recov_link, old_pathbuf); /* Make a new symlink at a temporary location, pointing to new dir */ snprintf(tmp_link, PATH_MAX, "%s.tmp", v4_recov_link); /* unlink old symlink, if any */ ret = unlink(tmp_link); if (ret != 0 && errno != ENOENT) { LogEvent(COMPONENT_CLIENTID, "Unable to remove recoverydir symlink: %s", strerror(errno)); return; } /* make a new symlink in a temporary spot */ ret = symlink(basename(v4_recov_dir), tmp_link); if (ret != 0) { LogEvent(COMPONENT_CLIENTID, "Unable to create recoverydir symlink: %s", strerror(errno)); return; } /* rename tmp link into place */ ret = rename(tmp_link, v4_recov_link); if (ret != 0) { LogEvent(COMPONENT_CLIENTID, "Unable to rename recoverydir symlink: %s", strerror(errno)); return; } /* now clean up old path, if any */ if (old_path) { fs_clean_old_recov_dir_impl(old_path); rmdir(old_path); } } static struct nfs4_recovery_backend fs_ng_backend = { .recovery_init = fs_ng_create_recov_dir, .recovery_cleanup = fs_ng_swap_recov_dir, .recovery_read_clids = fs_ng_read_recov_clids, .add_clid = fs_add_clid, .rm_clid = fs_rm_clid, .add_revoke_fh = fs_add_revoke_fh, }; void fs_ng_backend_init(struct nfs4_recovery_backend **backend) { *backend = &fs_ng_backend; } nfs-ganesha-2.6.0/src/SAL/recovery/recovery_rados.h000066400000000000000000000037431324272410200221600ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright 2017 Red Hat, Inc. and/or its affiliates. * Author: Jeff Layton * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _RECOVERY_RADOS_H #define _RECOVERY_RADOS_H #define RADOS_KEY_MAX_LEN NAME_MAX #define RADOS_VAL_MAX_LEN PATH_MAX extern rados_t rados_recov_cluster; extern rados_ioctx_t rados_recov_io_ctx; extern char rados_recov_oid[NI_MAXHOST]; struct rados_kv_parameter { /** Connection to ceph cluster */ char *ceph_conf; /** User ID to ceph cluster */ char *userid; /** Pool for client info */ char *pool; }; extern struct rados_kv_parameter rados_kv_param; typedef void (*pop_clid_entry_t)(char *, char*, add_clid_entry_hook, add_rfh_entry_hook, bool old, bool takeover); typedef struct pop_args { add_clid_entry_hook add_clid_entry; add_rfh_entry_hook add_rfh_entry; bool old; bool takeover; } *pop_args_t; int rados_kv_get(char *key, char **val, size_t *val_len, char *object); void rados_kv_create_key(nfs_client_id_t *clientid, char *key); void rados_kv_create_val(nfs_client_id_t *clientid, char *val); int rados_kv_traverse(pop_clid_entry_t pop_func, pop_args_t pop_args, const char *object); void rados_kv_append_val_rdfh(char *val, char *rdfh, int rdfh_len); #endif /* _RECOVERY_RADOS_H */ nfs-ganesha-2.6.0/src/SAL/recovery/recovery_rados_kv.c000066400000000000000000000340041324272410200226450ustar00rootroot00000000000000#include "config.h" #include "log.h" #include "nfs_core.h" #include "nfs4.h" #include "sal_functions.h" #include #include #include #include #include "bsd-base64.h" #include "client_mgr.h" #include "fsal.h" #include "netdb.h" #include #include "recovery_rados.h" #define DEFAULT_POOL "nfs-ganesha" #define MAX_ITEMS 1024 /* relaxed */ rados_t rados_recov_cluster; rados_ioctx_t rados_recov_io_ctx; char rados_recov_oid[NI_MAXHOST]; static char rados_recov_old_oid[NI_MAXHOST]; struct rados_kv_parameter rados_kv_param; static struct config_item rados_kv_params[] = { CONF_ITEM_PATH("ceph_conf", 1, MAXPATHLEN, NULL, rados_kv_parameter, ceph_conf), CONF_ITEM_STR("userid", 1, MAXPATHLEN, NULL, rados_kv_parameter, userid), CONF_ITEM_STR("pool", 1, MAXPATHLEN, DEFAULT_POOL, rados_kv_parameter, pool), CONFIG_EOL }; static void *rados_kv_param_init(void *link_mem, void *self_struct) { if (self_struct == NULL) return &rados_kv_param; else return NULL; } struct config_block rados_kv_param_blk = { .dbus_interface_name = "org.ganesha.nfsd.config.rados_kv", .blk_desc.name = "RADOS_KV", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = rados_kv_param_init, .blk_desc.u.blk.params = rados_kv_params, .blk_desc.u.blk.commit = noop_conf_commit }; static int convert_opaque_val(struct display_buffer *dspbuf, void *value, int len, int max) { unsigned int i = 0; int b_left = display_start(dspbuf); int cpy = len; if (b_left <= 0) return 0; /* Check that the length is ok * If the value is empty, display EMPTY value. */ if (len <= 0 || len > max) return 0; /* If the value is NULL, display NULL value. */ if (value == NULL) return 0; /* Determine if the value is entirely printable characters, */ /* and it contains no slash character (reserved for filename) */ for (i = 0; i < len; i++) if ((!isprint(((char *)value)[i])) || (((char *)value)[i] == '/')) break; if (i == len) { /* Entirely printable character, so we will just copy the * characters into the buffer (to the extent there is room * for them). */ b_left = display_len_cat(dspbuf, value, cpy); } else { b_left = display_opaque_bytes(dspbuf, value, cpy); } if (b_left <= 0) return 0; return b_left; } void rados_kv_create_key(nfs_client_id_t *clientid, char *key) { snprintf(key, RADOS_KEY_MAX_LEN, "%lu", (uint64_t)clientid->cid_clientid); } void rados_kv_create_val(nfs_client_id_t *clientid, char *val) { char *src = clientid->cid_client_record->cr_client_val; int src_len = clientid->cid_client_record->cr_client_val_len; const char *str_client_addr = "(unknown)"; char cidstr[PATH_MAX] = { 0, }; struct display_buffer dspbuf = {sizeof(cidstr), cidstr, cidstr}; char cidstr_len[20]; int total_len; int ret; /* get the caller's IP addr */ if (clientid->gsh_client != NULL) str_client_addr = clientid->gsh_client->hostaddr_str; ret = convert_opaque_val(&dspbuf, src, src_len, PATH_MAX); assert(ret > 0); snprintf(cidstr_len, sizeof(cidstr_len), "%zd", strlen(cidstr)); total_len = strlen(cidstr) + strlen(str_client_addr) + 5 + strlen(cidstr_len); /* hold both long form clientid and IP */ snprintf(val, total_len, "%s-(%s:%s)", str_client_addr, cidstr_len, cidstr); LogDebug(COMPONENT_CLIENTID, "Created client name [%s]", clientid->cid_recov_tag); } static int rados_kv_put(char *key, char *val, char *object) { int ret; char *keys[1]; char *vals[1]; size_t lens[1]; rados_write_op_t write_op; keys[0] = key; vals[0] = val; lens[0] = strlen(val); write_op = rados_create_write_op(); rados_write_op_omap_set(write_op, (const char * const*)keys, (const char * const*)vals, lens, 1); ret = rados_write_op_operate(write_op, rados_recov_io_ctx, object, NULL, 0); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to put kv ret=%d, key=%s, val=%s", ret, key, val); } rados_release_write_op(write_op); return ret; } int rados_kv_get(char *key, char **val, size_t *val_len, char *object) { int ret; char *keys[1]; char *key_out = NULL; char *val_out = NULL; size_t val_len_out = 0; rados_omap_iter_t iter_vals; rados_read_op_t read_op; keys[0] = key; read_op = rados_create_read_op(); rados_read_op_omap_get_vals_by_keys(read_op, (const char * const*)keys, 1, &iter_vals, NULL); ret = rados_read_op_operate(read_op, rados_recov_io_ctx, object, 0); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to get kv ret=%d, key=%s", ret, key); goto out; } ret = rados_omap_get_next(iter_vals, &key_out, &val_out, &val_len_out); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to get kv ret=%d, key=%s", ret, key); goto out; } rados_omap_get_end(iter_vals); val_out[val_len_out] = '\0'; *val = val_out; *val_len = val_len_out; out: rados_release_read_op(read_op); return ret; } static int rados_kv_del(char *key, char *object) { int ret; char *keys[1]; rados_write_op_t write_op; keys[0] = key; write_op = rados_create_write_op(); rados_write_op_omap_rm_keys(write_op, (const char * const*)keys, 1); ret = rados_write_op_operate(write_op, rados_recov_io_ctx, object, NULL, 0); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to del kv ret=%d, key=%s", ret, key); } rados_release_write_op(write_op); return ret; } int rados_kv_traverse(pop_clid_entry_t pop_func, pop_args_t pop_args, const char *object) { int ret; char *key_out = NULL; char *val_out = NULL; size_t val_len_out = 0; bool pmore = false; char *start = ""; rados_omap_iter_t iter_vals; rados_read_op_t read_op; again: read_op = rados_create_read_op(); rados_read_op_omap_get_vals2(read_op, start, "", MAX_ITEMS, &iter_vals, (unsigned char *)&pmore, NULL); ret = rados_read_op_operate(read_op, rados_recov_io_ctx, object, 0); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to lst kv ret=%d", ret); goto out; } while (true) { rados_omap_get_next(iter_vals, &key_out, &val_out, &val_len_out); if (val_len_out == 0 && key_out == NULL && val_out == NULL) break; start = key_out; pop_func(key_out, val_out, pop_args->add_clid_entry, pop_args->add_rfh_entry, pop_args->old, pop_args->takeover); } rados_omap_get_end(iter_vals); /* more items, next round */ if (pmore) { rados_release_read_op(read_op); goto again; } out: rados_release_read_op(read_op); return ret; } void rados_kv_append_val_rdfh(char *val, char *rdfh, int rdfh_len) { char rdfhstr[NAME_MAX]; int rdfhstr_len; int ret; /* Convert nfs_fh4_val into base64 encoded string */ ret = base64url_encode(rdfh, rdfh_len, rdfhstr, NAME_MAX); assert(ret != -1); rdfhstr_len = strlen(rdfhstr); strncat(val, "#", 1); strncat(val, rdfhstr, rdfhstr_len); val[rdfhstr_len + 1] = '\0'; } int rados_kv_set_param_from_conf(config_file_t parse_tree, struct config_error_type *err_type) { (void) load_config_from_parse(parse_tree, &rados_kv_param_blk, NULL, true, err_type); if (!config_error_is_harmless(err_type)) { LogCrit(COMPONENT_INIT, "Error while parsing RadosKV specific configuration"); return -1; } return 0; } void rados_kv_init(void) { int ret; char host[NI_MAXHOST]; if (nfs_param.core_param.clustered) { snprintf(host, sizeof(host), "node%d", g_nodeid); } else { ret = gethostname(host, sizeof(host)); if (ret) { LogEvent(COMPONENT_CLIENTID, "Failed to gethostname: %s", strerror(errno)); return; } } snprintf(rados_recov_old_oid, sizeof(rados_recov_old_oid), "%s_old", host); snprintf(rados_recov_oid, sizeof(rados_recov_oid), "%s_recov", host); ret = rados_create(&rados_recov_cluster, rados_kv_param.userid); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to rados create"); return; } ret = rados_conf_read_file(rados_recov_cluster, rados_kv_param.ceph_conf); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to read ceph_conf"); rados_shutdown(rados_recov_cluster); return; } ret = rados_connect(rados_recov_cluster); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to connect to cluster"); rados_shutdown(rados_recov_cluster); return; } ret = rados_pool_create(rados_recov_cluster, rados_kv_param.pool); if (ret < 0 && ret != -EEXIST) { LogEvent(COMPONENT_CLIENTID, "Failed to create pool"); rados_shutdown(rados_recov_cluster); return; } ret = rados_ioctx_create(rados_recov_cluster, rados_kv_param.pool, &rados_recov_io_ctx); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to create ioctx"); rados_shutdown(rados_recov_cluster); return; } rados_write_op_t op = rados_create_write_op(); rados_write_op_create(op, LIBRADOS_CREATE_EXCLUSIVE, NULL); ret = rados_write_op_operate(op, rados_recov_io_ctx, rados_recov_old_oid, NULL, 0); if (ret < 0 && ret != -EEXIST) { LogEvent(COMPONENT_CLIENTID, "Failed to create object"); rados_release_write_op(op); rados_shutdown(rados_recov_cluster); return; } rados_release_write_op(op); op = rados_create_write_op(); rados_write_op_create(op, LIBRADOS_CREATE_EXCLUSIVE, NULL); ret = rados_write_op_operate(op, rados_recov_io_ctx, rados_recov_oid, NULL, 0); if (ret < 0 && ret != -EEXIST) { LogEvent(COMPONENT_CLIENTID, "Failed to create object"); rados_release_write_op(op); rados_shutdown(rados_recov_cluster); return; } rados_release_write_op(op); LogEvent(COMPONENT_CLIENTID, "Rados kv store init done"); } void rados_kv_add_clid(nfs_client_id_t *clientid) { char ckey[RADOS_KEY_MAX_LEN]; char *cval; int ret; cval = gsh_malloc(RADOS_VAL_MAX_LEN); rados_kv_create_key(clientid, ckey); rados_kv_create_val(clientid, cval); ret = rados_kv_put(ckey, cval, rados_recov_oid); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to add clid %lu", clientid->cid_clientid); goto out; } clientid->cid_recov_tag = gsh_malloc(strlen(cval) + 1); strncpy(clientid->cid_recov_tag, cval, strlen(cval) + 1); out: gsh_free(cval); } void rados_kv_rm_clid(nfs_client_id_t *clientid) { char ckey[RADOS_KEY_MAX_LEN]; int ret; rados_kv_create_key(clientid, ckey); ret = rados_kv_del(ckey, rados_recov_oid); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to del clid %lu", clientid->cid_clientid); return; } free(clientid->cid_recov_tag); clientid->cid_recov_tag = NULL; } static void rados_kv_pop_clid_entry(char *key, char *val, add_clid_entry_hook add_clid_entry, add_rfh_entry_hook add_rfh_entry, bool old, bool takeover) { int ret; char *dupval; char *cl_name, *rfh_names, *rfh_name; clid_entry_t *clid_ent; /* extract clid records */ dupval = gsh_strdup(val); cl_name = strtok(dupval, "#"); if (!cl_name) cl_name = dupval; clid_ent = add_clid_entry(cl_name); rfh_names = strtok(NULL, "#"); rfh_name = strtok(rfh_names, "#"); while (rfh_name) { add_rfh_entry(clid_ent, rfh_name); rfh_name = strtok(NULL, "#"); } gsh_free(dupval); if (!old) { ret = rados_kv_put(key, val, rados_recov_old_oid); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to move %s", key); } } if (!takeover) { if (old) { ret = rados_kv_del(key, rados_recov_old_oid); } else { ret = rados_kv_del(key, rados_recov_oid); } if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to del %s", key); } } } static void rados_kv_read_recov_clids_recover(add_clid_entry_hook add_clid_entry, add_rfh_entry_hook add_rfh_entry) { int ret; struct pop_args args = { .add_clid_entry = add_clid_entry, .add_rfh_entry = add_rfh_entry, .old = true, .takeover = false, }; ret = rados_kv_traverse(rados_kv_pop_clid_entry, &args, rados_recov_old_oid); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to recover, processing old entries"); return; } args.old = false; ret = rados_kv_traverse(rados_kv_pop_clid_entry, &args, rados_recov_oid); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to recover, processing recov entries"); } } void rados_kv_read_recov_clids_takeover(nfs_grace_start_t *gsp, add_clid_entry_hook add_clid_entry, add_rfh_entry_hook add_rfh_entry) { int ret; char object_takeover[NI_MAXHOST]; struct pop_args args = { .add_clid_entry = add_clid_entry, .add_rfh_entry = add_rfh_entry, .old = false, .takeover = true, }; if (!gsp) { rados_kv_read_recov_clids_recover(add_clid_entry, add_rfh_entry); return; } snprintf(object_takeover, NI_MAXHOST, "%s_recov", gsp->ipaddr); ret = rados_kv_traverse(rados_kv_pop_clid_entry, &args, object_takeover); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to takeover"); } } void rados_kv_cleanup_old(void) { int ret; rados_write_op_t write_op = rados_create_write_op(); rados_write_op_omap_clear(write_op); ret = rados_write_op_operate(write_op, rados_recov_io_ctx, rados_recov_old_oid, NULL, 0); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to clearup old"); } rados_release_write_op(write_op); } void rados_kv_add_revoke_fh(nfs_client_id_t *delr_clid, nfs_fh4 *delr_handle) { int ret; char ckey[RADOS_KEY_MAX_LEN]; char *cval; char *val_out; size_t val_out_len; cval = gsh_malloc(RADOS_VAL_MAX_LEN); rados_kv_create_key(delr_clid, ckey); ret = rados_kv_get(ckey, &val_out, &val_out_len, rados_recov_oid); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to get %s", ckey); goto out; } strncpy(cval, val_out, val_out_len); rados_kv_append_val_rdfh(cval, delr_handle->nfs_fh4_val, delr_handle->nfs_fh4_len); ret = rados_kv_put(ckey, cval, rados_recov_oid); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to add rdfh for clid %lu", delr_clid->cid_clientid); } out: gsh_free(cval); } struct nfs4_recovery_backend rados_kv_backend = { .recovery_init = rados_kv_init, .recovery_cleanup = rados_kv_cleanup_old, .recovery_read_clids = rados_kv_read_recov_clids_takeover, .add_clid = rados_kv_add_clid, .rm_clid = rados_kv_rm_clid, .add_revoke_fh = rados_kv_add_revoke_fh, }; void rados_kv_backend_init(struct nfs4_recovery_backend **backend) { *backend = &rados_kv_backend; } nfs-ganesha-2.6.0/src/SAL/recovery/recovery_rados_ng.c000066400000000000000000000220151324272410200226300ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright 2017 Red Hat, Inc. and/or its affiliates. * Author: Jeff Layton * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * recovery_rados_ng: a "safe by design" recovery backing store * * At startup, create a global write op, and set it up to clear out all of * the old keys. We then will spool up new client creation (and removals) to * that transaction during the grace period. * * When lifting the grace period, synchronously commit the transaction * to the kvstore. After that point, all client creation and removal is done * synchronously to the kvstore. * * This allows for better resilience when the server crashes during the grace * period. No changes are made to the backing store until the grace period * has been lifted. */ #include "config.h" #include #include #include "log.h" #include "nfs_core.h" #include "sal_functions.h" #include "recovery_rados.h" static rados_write_op_t grace_op; static pthread_mutex_t grace_op_lock = PTHREAD_MUTEX_INITIALIZER; static int rados_ng_put(char *key, char *val, char *object) { int ret; char *keys[1]; char *vals[1]; size_t lens[1]; rados_write_op_t write_op = NULL; bool in_grace; keys[0] = key; vals[0] = val; lens[0] = strlen(val); /* When there is an active grace_op, spool up the changes to it */ PTHREAD_MUTEX_lock(&grace_op_lock); in_grace = grace_op; write_op = grace_op; if (!write_op) write_op = rados_create_write_op(); rados_write_op_omap_set(write_op, (const char * const*)keys, (const char * const*)vals, lens, 1); PTHREAD_MUTEX_unlock(&grace_op_lock); if (in_grace) return 0; ret = rados_write_op_operate(write_op, rados_recov_io_ctx, object, NULL, 0); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to put kv ret=%d, key=%s, val=%s", ret, key, val); } rados_release_write_op(write_op); return ret; } static int rados_ng_del(char *key, char *object) { int ret; char *keys[1]; rados_write_op_t write_op; bool in_grace; keys[0] = key; PTHREAD_MUTEX_lock(&grace_op_lock); in_grace = grace_op; write_op = in_grace ? grace_op : rados_create_write_op(); rados_write_op_omap_rm_keys(write_op, (const char * const*)keys, 1); PTHREAD_MUTEX_unlock(&grace_op_lock); if (in_grace) return 0; ret = rados_write_op_operate(write_op, rados_recov_io_ctx, object, NULL, 0); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to del kv ret=%d, key=%s", ret, key); } rados_release_write_op(write_op); return ret; } void rados_ng_init(void) { int ret; char host[NI_MAXHOST]; rados_write_op_t op; if (nfs_param.core_param.clustered) { snprintf(host, sizeof(host), "node%d", g_nodeid); } else { ret = gethostname(host, sizeof(host)); if (ret) { LogEvent(COMPONENT_CLIENTID, "Failed to gethostname: %s", strerror(errno)); return; } } snprintf(rados_recov_oid, sizeof(rados_recov_oid), "%s.recov", host); ret = rados_create(&rados_recov_cluster, rados_kv_param.userid); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to rados create"); return; } ret = rados_conf_read_file(rados_recov_cluster, rados_kv_param.ceph_conf); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to read ceph_conf"); rados_shutdown(rados_recov_cluster); return; } ret = rados_connect(rados_recov_cluster); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to connect to cluster"); rados_shutdown(rados_recov_cluster); return; } ret = rados_pool_create(rados_recov_cluster, rados_kv_param.pool); if (ret < 0 && ret != -EEXIST) { LogEvent(COMPONENT_CLIENTID, "Failed to create pool"); rados_shutdown(rados_recov_cluster); return; } ret = rados_ioctx_create(rados_recov_cluster, rados_kv_param.pool, &rados_recov_io_ctx); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to create ioctx"); rados_shutdown(rados_recov_cluster); return; } op = rados_create_write_op(); rados_write_op_create(op, LIBRADOS_CREATE_EXCLUSIVE, NULL); ret = rados_write_op_operate(op, rados_recov_io_ctx, rados_recov_oid, NULL, 0); if (ret < 0 && ret != -EEXIST) { LogEvent(COMPONENT_CLIENTID, "Failed to create object"); rados_release_write_op(op); rados_shutdown(rados_recov_cluster); return; } rados_release_write_op(op); /* Create new grace_op to spool changes until grace period is done */ grace_op = rados_create_write_op(); rados_write_op_omap_clear(grace_op); LogEvent(COMPONENT_CLIENTID, "Rados kv store init done"); } void rados_ng_add_clid(nfs_client_id_t *clientid) { char ckey[RADOS_KEY_MAX_LEN]; char *cval; int ret; cval = gsh_malloc(RADOS_VAL_MAX_LEN); rados_kv_create_key(clientid, ckey); rados_kv_create_val(clientid, cval); LogDebug(COMPONENT_CLIENTID, "adding %s :: %s", ckey, cval); ret = rados_ng_put(ckey, cval, rados_recov_oid); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to add clid %lu", clientid->cid_clientid); goto out; } clientid->cid_recov_tag = gsh_malloc(strlen(cval) + 1); strncpy(clientid->cid_recov_tag, cval, strlen(cval) + 1); out: gsh_free(cval); } void rados_ng_rm_clid(nfs_client_id_t *clientid) { char ckey[RADOS_KEY_MAX_LEN]; int ret; rados_kv_create_key(clientid, ckey); LogDebug(COMPONENT_CLIENTID, "removing %s", ckey); ret = rados_ng_del(ckey, rados_recov_oid); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to del clid %lu", clientid->cid_clientid); return; } free(clientid->cid_recov_tag); clientid->cid_recov_tag = NULL; } static void rados_ng_pop_clid_entry(char *key, char *val, add_clid_entry_hook add_clid_entry, add_rfh_entry_hook add_rfh_entry, bool old, bool takeover) { char *dupval, *cl_name; char *rfh_names, *rfh_name; clid_entry_t *clid_ent; /* extract clid records */ dupval = gsh_strdup(val); cl_name = strtok(dupval, "#"); if (!cl_name) cl_name = dupval; clid_ent = add_clid_entry(cl_name); rfh_names = strtok(NULL, "#"); rfh_name = strtok(rfh_names, "#"); while (rfh_name) { add_rfh_entry(clid_ent, rfh_name); rfh_name = strtok(NULL, "#"); } gsh_free(dupval); } static void rados_ng_read_recov_clids_recover(add_clid_entry_hook add_clid_entry, add_rfh_entry_hook add_rfh_entry) { int ret; struct pop_args args = { .add_clid_entry = add_clid_entry, .add_rfh_entry = add_rfh_entry, }; ret = rados_kv_traverse(rados_ng_pop_clid_entry, &args, rados_recov_oid); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to recover, processing old entries"); return; } } void rados_ng_read_recov_clids_takeover(nfs_grace_start_t *gsp, add_clid_entry_hook add_clid_entry, add_rfh_entry_hook add_rfh_entry) { if (!gsp) { rados_ng_read_recov_clids_recover(add_clid_entry, add_rfh_entry); return; } LogEvent(COMPONENT_CLIENTID, "Unable to perform takeover with rados_ng recovery backend."); } void rados_ng_cleanup_old(void) { int ret; /* Commit pregrace transaction */ PTHREAD_MUTEX_lock(&grace_op_lock); ret = rados_write_op_operate(grace_op, rados_recov_io_ctx, rados_recov_oid, NULL, 0); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to commit grace period transactions: %s", strerror(ret)); } rados_release_write_op(grace_op); grace_op = NULL; PTHREAD_MUTEX_unlock(&grace_op_lock); } void rados_ng_add_revoke_fh(nfs_client_id_t *delr_clid, nfs_fh4 *delr_handle) { int ret; char ckey[RADOS_KEY_MAX_LEN]; char *cval; char *val_out; size_t val_out_len; cval = gsh_malloc(RADOS_VAL_MAX_LEN); rados_kv_create_key(delr_clid, ckey); ret = rados_kv_get(ckey, &val_out, &val_out_len, rados_recov_oid); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to get %s", ckey); goto out; } strncpy(cval, val_out, val_out_len); rados_kv_append_val_rdfh(cval, delr_handle->nfs_fh4_val, delr_handle->nfs_fh4_len); ret = rados_ng_put(ckey, cval, rados_recov_oid); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "Failed to add rdfh for clid %lu", delr_clid->cid_clientid); } out: gsh_free(cval); } struct nfs4_recovery_backend rados_ng_backend = { .recovery_init = rados_ng_init, .recovery_cleanup = rados_ng_cleanup_old, .recovery_read_clids = rados_ng_read_recov_clids_takeover, .add_clid = rados_ng_add_clid, .rm_clid = rados_ng_rm_clid, .add_revoke_fh = rados_ng_add_revoke_fh, }; void rados_ng_backend_init(struct nfs4_recovery_backend **backend) { *backend = &rados_ng_backend; } nfs-ganesha-2.6.0/src/SAL/state_async.c000066400000000000000000000122321324272410200175750ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup SAL State abstraction layer * @{ */ /** * @file state_async.c * @brief Management of SAL asynchronous processing */ #include "config.h" #include #include #include #include #include #include #include "log.h" #include "hashtable.h" #include "fsal.h" #include "sal_functions.h" #include "fridgethr.h" #include "gsh_config.h" struct fridgethr *state_async_fridge; struct fridgethr *state_poll_fridge; /** * @brief Process a blocked lock request * * We use this wrapper so we can void rewriting stuff. We can change * this later. * * @param[in] ctx Thread context, containing arguments. */ static void state_blocked_lock_caller(struct fridgethr_context *ctx) { state_block_data_t *block = ctx->arg; process_blocked_lock_upcall(block); } /** * @brief Process an async request * * We use this wrapper so we can avoid having to rewrite every async * func. Later on we might want to remove it. * * @param[in] ctx Thread context, containing arguments. */ static void state_async_func_caller(struct fridgethr_context *ctx) { state_async_queue_t *entry = ctx->arg; entry->state_async_func(entry); } /** * @brief Schedule an asynchronous action * * @param[in] arg Request to schedule * * @return State status. */ state_status_t state_async_schedule(state_async_queue_t *arg) { int rc; LogFullDebug(COMPONENT_STATE, "Schedule %p", arg); rc = fridgethr_submit(state_async_fridge, state_async_func_caller, arg); if (rc != 0) LogCrit(COMPONENT_STATE, "Unable to schedule request: %d", rc); return rc == 0 ? STATE_SUCCESS : STATE_SIGNAL_ERROR; } /** * @brief Schedule a lock notification * * @param[in] block Lock to schedule * * @return State status. */ state_status_t state_block_schedule(state_block_data_t *block) { int rc; LogFullDebug(COMPONENT_STATE, "Schedule notification %p", block); rc = fridgethr_submit(state_async_fridge, state_blocked_lock_caller, block); if (rc != 0) LogMajor(COMPONENT_STATE, "Unable to schedule request: %d", rc); return rc == 0 ? STATE_SUCCESS : STATE_SIGNAL_ERROR; } /** * @brief Initialize asynchronous request system * * @return State status. */ state_status_t state_async_init(void) { int rc = 0; struct fridgethr_params frp; memset(&frp, 0, sizeof(struct fridgethr_params)); frp.thr_max = 1; frp.deferment = fridgethr_defer_queue; rc = fridgethr_init(&state_async_fridge, "State_Async", &frp); if (rc != 0) { LogMajor(COMPONENT_STATE, "Unable to initialize state async thread fridge: %d", rc); return STATE_INIT_ENTRY_FAILED; } memset(&frp, 0, sizeof(struct fridgethr_params)); frp.thr_max = 1; frp.thr_min = 1; frp.thread_delay = nfs_param.core_param.blocked_lock_poller_interval; frp.flavor = fridgethr_flavor_looper; rc = fridgethr_init(&state_poll_fridge, "state_poll", &frp); if (rc != 0) { LogMajor(COMPONENT_STATE, "Unable to initialize state blocked lock polling thread fridge: %d", rc); return STATE_INIT_ENTRY_FAILED; } rc = fridgethr_submit(state_poll_fridge, blocked_lock_polling, NULL); if (rc != 0) { LogMajor(COMPONENT_STATE, "Unable to start blocked lock polling thread, error code %d.", rc); return STATE_INIT_ENTRY_FAILED; } return STATE_SUCCESS; } /** * @brief Shut down asynchronous request system * * @return State status. */ state_status_t state_async_shutdown(void) { int rc1, rc2; rc1 = fridgethr_sync_command(state_async_fridge, fridgethr_comm_stop, 120); if (rc1 == ETIMEDOUT) { LogMajor(COMPONENT_STATE, "Shutdown timed out, cancelling threads."); fridgethr_cancel(state_async_fridge); } else if (rc1 != 0) { LogMajor(COMPONENT_STATE, "Failed shutting down state async thread: %d", rc1); } rc2 = fridgethr_sync_command(state_poll_fridge, fridgethr_comm_stop, 120); if (rc2 == ETIMEDOUT) { LogMajor(COMPONENT_STATE, "Shutdown timed out, cancelling threads."); fridgethr_cancel(state_poll_fridge); } else if (rc2 != 0) { LogMajor(COMPONENT_STATE, "Failed shutting down state blocked lock polling thread: %d", rc2); } return ((rc1 == 0) && (rc2 == 0)) ? STATE_SUCCESS : STATE_SIGNAL_ERROR; } /** @} */ nfs-ganesha-2.6.0/src/SAL/state_deleg.c000066400000000000000000000416461324272410200175530ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright IBM (2014) * contributeur : Jeremy Bongio jbongio@us.ibm.com * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup SAL State abstraction layer * @{ */ /** * @file state_deleg.c * @brief Delegation management */ #include "config.h" #include #include #include #include #include #include #include #include "fsal.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs4.h" #include "sal_functions.h" #include "export_mgr.h" #include "nfs_rpc_callback.h" #include "server_stats.h" #include "fsal_up.h" #include "nfs_file_handle.h" /** * @brief Initialize new delegation state as argument for state_add() * * Initialize delegation state struct. This is then given as an argument * to state_add() * * @param[in/out] deleg_state Delegation state struct to be init. Can't be NULL. * @param[in] sd_type Type of delegation, READ or WRITE. * @param[in] client Client that will own this delegation. */ void init_new_deleg_state(union state_data *deleg_state, open_delegation_type4 deleg_type, nfs_client_id_t *client) { struct cf_deleg_stats *clfile_entry = &deleg_state->deleg.sd_clfile_stats; deleg_state->deleg.sd_type = deleg_type; deleg_state->deleg.sd_state = DELEG_GRANTED; clfile_entry->cfd_rs_time = 0; clfile_entry->cfd_r_time = 0; } /** * @brief Perform a lease lock operation * * We do state management and call down to the FSAL as appropriate, so * that the caller has a single entry point. * * @param[in] obj File on which to operate * @param[in] lock_op Operation to perform * @param[in] owner Lock operation * @param[in] lock Lock description * * @return State status. */ state_status_t do_lease_op(struct fsal_obj_handle *obj, state_t *state, state_owner_t *owner, fsal_deleg_t deleg) { fsal_status_t fsal_status; state_status_t status; /* Perform this delegation operation using the new * multiple file-descriptors. */ fsal_status = obj->obj_ops.lease_op2( obj, state, owner, deleg); status = state_error_convert(fsal_status); LogFullDebug(COMPONENT_STATE, "FSAL lease_op2 returned %s", state_err_str(status)); return status; } /** * @brief Attempt to acquire a lease lock (delegation) * * @note The state_lock MUST be held for write * * @param[in] ostate File state to get lease lock on * @param[in] owner Owner for the lease lock * @param[in] state Associated state for the lock */ state_status_t acquire_lease_lock(struct state_hdl *ostate, state_owner_t *owner, state_t *state) { state_status_t status; fsal_deleg_t deleg = FSAL_DELEG_RD; if (state->state_data.deleg.sd_type == OPEN_DELEGATE_WRITE) deleg = FSAL_DELEG_WR; /* Create a new deleg data object */ status = do_lease_op(ostate->file.obj, state, owner, deleg); if (status == STATE_SUCCESS) { update_delegation_stats(ostate, owner, state); } else { LogDebug(COMPONENT_STATE, "Could not set lease, error=%s", state_err_str(status)); } return status; } /** * @brief Release a lease lock (delegation) * * @param[in] state Associated state * * state_lock must be held while calling this function */ state_status_t release_lease_lock(struct fsal_obj_handle *obj, state_t *state) { state_status_t status; state_owner_t *owner = get_state_owner_ref(state); /* Something is going stale? */ if (owner == NULL) return STATE_ESTALE; status = do_lease_op(obj, state, owner, FSAL_DELEG_NONE); if (status != STATE_SUCCESS) LogMajor(COMPONENT_STATE, "Unable to unlock FSAL, error=%s", state_err_str(status)); dec_state_owner_ref(owner); return status; } /** * @brief Update statistics on successfully granted delegation. * * Update statistics on successfully granted delegation. * Note: This should be called only when a delegation is successfully granted. * So far this should only be called in state_lock(). * * @param[in] Delegation Entry */ void update_delegation_stats(struct state_hdl *ostate, state_owner_t *owner, struct state_t *deleg) { nfs_client_id_t *client = owner->so_owner.so_nfs4_owner.so_clientrec; /* Update delegation stats for file. */ struct file_deleg_stats *statistics = &ostate->file.fdeleg_stats; statistics->fds_curr_delegations++; statistics->fds_delegation_count++; statistics->fds_last_delegation = time(NULL); /* Update delegation stats for client. */ inc_grants(client->gsh_client); client->curr_deleg_grants++; } /* Add a new delegation length to the average length stat. */ static int advance_avg(time_t prev_avg, time_t new_time, uint32_t prev_tot, uint32_t curr_tot) { return ((prev_tot * prev_avg) + new_time) / curr_tot; } /** * @brief Update statistics on successfully recalled delegation. * * Update statistics on successfully recalled delegation. * Note: This should be called only when a delegation is successfully recalled. * * @param[in] deleg Delegation state */ void deleg_heuristics_recall(struct fsal_obj_handle *obj, state_owner_t *owner, struct state_t *deleg) { nfs_client_id_t *client = owner->so_owner.so_nfs4_owner.so_clientrec; /* Update delegation stats for file. */ struct file_deleg_stats *statistics = &obj->state_hdl->file.fdeleg_stats; statistics->fds_curr_delegations--; statistics->fds_recall_count++; /* Update delegation stats for client. */ dec_grants(client->gsh_client); client->curr_deleg_grants--; /* Update delegation stats for file. */ statistics->fds_avg_hold = advance_avg(statistics->fds_avg_hold, time(NULL) - statistics->fds_last_delegation, statistics->fds_recall_count - 1, statistics->fds_recall_count); } /** * @brief Initialize the file-specific delegation statistics * * Initialize the file-specific delegation statistics used later for deciding * if a delegation should be granted on this file based on heuristics. * * @param[in] obj File the delegation will be on. */ bool init_deleg_heuristics(struct fsal_obj_handle *obj) { struct file_deleg_stats *statistics; if (obj->type != REGULAR_FILE) { LogCrit(COMPONENT_STATE, "Initialization of delegation stats for an obj that is NOT a regular file!"); return false; } statistics = &obj->state_hdl->file.fdeleg_stats; statistics->fds_curr_delegations = 0; statistics->fds_deleg_type = OPEN_DELEGATE_NONE; statistics->fds_delegation_count = 0; statistics->fds_recall_count = 0; statistics->fds_last_delegation = 0; statistics->fds_last_recall = 0; statistics->fds_avg_hold = 0; statistics->fds_num_opens = 0; statistics->fds_first_open = 0; return true; } /* Most clients retry NFS operations after 5 seconds. The following * should be good enough to avoid starving a client's open */ #define RECALL2DELEG_TIME 10 /** * @brief Decide if a delegation should be granted based on heuristics. * * Decide if a delegation should be granted based on heuristics. * * @note The state_lock MUST be held for read * * @param[in] ostate File state the delegation will be on. * @param[in] client Client that would own the delegation. * @param[in] open_state The open state for the inode to be delegated. * @param[in/out] resok pointer to resok (for setting ond_why, primarily) * @param[in] owner state owner * @param[out] prerecall flag for reclaims. */ bool should_we_grant_deleg(struct state_hdl *ostate, nfs_client_id_t *client, state_t *open_state, OPEN4args *args, OPEN4resok *resok, state_owner_t *owner, bool *prerecall) { /* specific file, all clients, stats */ struct file_deleg_stats *file_stats = &ostate->file.fdeleg_stats; /* specific client, all files stats */ open_claim_type4 claim = args->claim.claim; LogDebug(COMPONENT_STATE, "Checking if we should grant delegation."); assert(open_state->state_type == STATE_TYPE_SHARE); *prerecall = false; if (!nfs_param.nfsv4_param.allow_delegations || !op_ctx->fsal_export->exp_ops.fs_supports( op_ctx->fsal_export, fso_delegations_r) || !(op_ctx->export_perms->options & EXPORT_OPTION_DELEGATIONS) || (!owner->so_owner.so_nfs4_owner.so_confirmed && claim == CLAIM_NULL) || claim == CLAIM_DELEGATE_CUR) { resok->delegation.open_delegation4_u.od_whynone.ond_why = WND4_NOT_SUPP_FTYPE; return false; } /* set the pre-recall flag for reclaims if the server does not want the * delegation to remain in force */ if (get_cb_chan_down(client)) { switch (claim) { case CLAIM_PREVIOUS: *prerecall = true; return args->claim.open_claim4_u.delegate_type == OPEN_DELEGATE_NONE ? false : true; case CLAIM_DELEGATE_PREV: *prerecall = true; return true; default: resok->delegation.open_delegation4_u.od_whynone.ond_why = WND4_RESOURCE; return false; } } else { *prerecall = false; switch (claim) { case CLAIM_PREVIOUS: return args->claim.open_claim4_u.delegate_type == OPEN_DELEGATE_NONE ? false : true; case CLAIM_DELEGATE_PREV: return true; default: break; } } /* If there is a recent recall on this file, the client that made * the conflicting open may retry the open later. Don't give out * delegation to avoid starving the client's open that caused * the recall. */ if (file_stats->fds_last_recall != 0 && time(NULL) - file_stats->fds_last_recall < RECALL2DELEG_TIME) { resok->delegation.open_delegation4_u.od_whynone.ond_why = WND4_CONTENTION; return false; } /* Check if this is a misbehaving or unreliable client */ if (client->num_revokes > 2) { /* more than 2 revokes */ resok->delegation.open_delegation4_u.od_whynone.ond_why = WND4_RESOURCE; return false; } LogDebug(COMPONENT_STATE, "Let's delegate!!"); return true; } /** * @brief Form the ACE mask for the delegated file. * * Form the ACE mask for the delegated file. * * @param[in,out] permissions ACE mask for delegated inode. * @param[in] type Type of delegation. Either READ or WRITE. */ void get_deleg_perm(nfsace4 *permissions, open_delegation_type4 type) { /* We need to create an access_mask that shows who * can OPEN this file. */ if (type == OPEN_DELEGATE_WRITE) ; else if (type == OPEN_DELEGATE_READ) ; permissions->type = ACE4_ACCESS_ALLOWED_ACE_TYPE; permissions->flag = 0; permissions->access_mask = 0; permissions->who.utf8string_len = 0; permissions->who.utf8string_val = NULL; } /** * @brief Mark a delegation revoked * * Mark the delegation state revoked, further ops on this state should return * NFS4ERR_REVOKED or NFS4ERR_EXPIRED * * @param[in] deleg state lock entry. * Should be called with state lock held. */ nfsstat4 deleg_revoke(struct fsal_obj_handle *obj, struct state_t *deleg_state) { state_status_t state_status; struct nfs_client_id_t *clid; nfs_fh4 fhandle; struct root_op_context root_op_context; struct gsh_export *export; state_owner_t *owner; /* Get reference to owner and export. Onwer reference also protects * the clientid. */ if (!get_state_obj_export_owner_refs(deleg_state, NULL, &export, &owner)) { /* Something is going stale. */ LogDebug(COMPONENT_NFS_V4_LOCK, "Stale state, owner, or export"); return NFS4ERR_STALE; } clid = owner->so_owner.so_nfs4_owner.so_clientrec; /* Building a new fh ; Ignore return code, should not fail*/ (void) nfs4_FSALToFhandle(true, &fhandle, obj, export); deleg_heuristics_recall(obj, owner, deleg_state); /* Build op_context for state_unlock_locked */ init_root_op_context(&root_op_context, NULL, NULL, 0, 0, UNKNOWN_REQUEST); root_op_context.req_ctx.clientid = &clid->cid_clientid; root_op_context.req_ctx.ctx_export = export; root_op_context.req_ctx.fsal_export = export->fsal_export; /* release_lease_lock() returns delegation to FSAL */ state_status = release_lease_lock(obj, deleg_state); release_root_op_context(); if (state_status != STATE_SUCCESS) { LogDebug(COMPONENT_NFS_V4_LOCK, "state unlock failed: %d", state_status); } /* Put the revoked delegation on the stable storage. */ nfs4_record_revoke(clid, &fhandle); state_del_locked(deleg_state); gsh_free(fhandle.nfs_fh4_val); /* Release references taken above */ dec_state_owner_ref(owner); put_gsh_export(export); return NFS4_OK; } /** * @brief Mark the delegation revoked * * Mark the delegation state revoked, further ops on this state should * return NFS4ERR_REVOKED or NFS4ERR_EXPIRED * * @note The state_lock MUST be held for write * * @param[in] obj File * @param[in] state Delegation state */ void state_deleg_revoke(struct fsal_obj_handle *obj, state_t *state) { /* If we are already in the process of recalling or revoking * this delegation from elsewhere, skip it here. */ if (state->state_data.deleg.sd_state != DELEG_GRANTED) return; state->state_data.deleg.sd_state = DELEG_RECALL_WIP; (void)deleg_revoke(obj, state); } /** * @brief Check if an operation is conflicting with delegations. * * Check if an operation will conflict with current delegations on a file. * Return TRUE if there is a conflict and the delegations have been recalled. * Return FALSE if there is no conflict. * * @note The state_lock MUST be held for read * * @param[in] obj File * @param[in] write a boolean indicating whether the operation will read or * change the file. * * @retval true if there is a conflict and the delegations have been recalled. * @retval false if there is no delegation conflict. */ bool state_deleg_conflict(struct fsal_obj_handle *obj, bool write) { struct file_deleg_stats *deleg_stats; if (obj->type != REGULAR_FILE) return false; deleg_stats = &obj->state_hdl->file.fdeleg_stats; if (deleg_stats->fds_curr_delegations > 0 && ((deleg_stats->fds_deleg_type == OPEN_DELEGATE_READ && write) || (deleg_stats->fds_deleg_type == OPEN_DELEGATE_WRITE)) ) { LogDebug(COMPONENT_STATE, "While trying to perform a %s op, found a conflicting %s delegation", write ? "write" : "read", (deleg_stats->fds_deleg_type == OPEN_DELEGATE_WRITE) ? "WRITE" : "READ"); if (async_delegrecall(general_fridge, obj) != 0) LogCrit(COMPONENT_STATE, "Failed to start thread to recall delegation from conflicting operation."); return true; } return false; } bool deleg_supported(struct fsal_obj_handle *obj, struct fsal_export *fsal_export, struct export_perms *export_perms, uint32_t share_access) { if (!nfs_param.nfsv4_param.allow_delegations) return false; if (obj->type != REGULAR_FILE) return false; /* In a read-write case, we handle write delegation. So we should * check for OPEN4_SHARE_ACCESS_WRITE bit first! */ if (share_access & OPEN4_SHARE_ACCESS_WRITE) { if (!fsal_export->exp_ops.fs_supports(fsal_export, fso_delegations_w)) return false; if (!(export_perms->options & EXPORT_OPTION_WRITE_DELEG)) return false; } else { assert(share_access & OPEN4_SHARE_ACCESS_READ); if (!fsal_export->exp_ops.fs_supports(fsal_export, fso_delegations_r)) return false; if (!(export_perms->options & EXPORT_OPTION_READ_DELEG)) return false; } return true; } /** * @brief Check to see if a delegation can be granted * * @note The state_lock MUST be held for read * * @param[in] ostate State to check * @return true if can grant, false otherwise */ bool can_we_grant_deleg(struct state_hdl *ostate, state_t *open_state) { struct glist_head *glist; state_lock_entry_t *lock_entry; const struct state_share *share = &open_state->state_data.share; /* Can't grant delegation if there is an anonymous operation * in progress */ if (atomic_fetch_uint32_t(&ostate->file.anon_ops) != 0) { LogFullDebug(COMPONENT_STATE, "Anonymous op in progress, not granting delegation"); return false; } /* Check for conflicting NLM locks. Write delegation would conflict * with any kind of NLM lock, and NLM write lock would conflict * with any kind of delegation. */ glist_for_each(glist, &ostate->file.lock_list) { lock_entry = glist_entry(glist, state_lock_entry_t, sle_list); if (lock_entry->sle_lock.lock_type == FSAL_NO_LOCK) continue; /* no lock, skip */ if (share->share_access & OPEN4_SHARE_ACCESS_WRITE || lock_entry->sle_lock.lock_type == FSAL_LOCK_W) { LogFullDebug(COMPONENT_STATE, "Conflicting NLM lock. Not granting delegation"); return false; } } return true; } nfs-ganesha-2.6.0/src/SAL/state_layout.c000066400000000000000000000167301324272410200200040ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) 2011, Linux Box Corporation * contributor: Adam C. Emerson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup SAL State abstraction layer * @{ */ /** * @file state_layout.c * @brief Layout state management. */ #include "config.h" #include #include #include #include #include #include #include "log.h" #include "hashtable.h" #include "fsal.h" #include "sal_functions.h" #include "nfs_core.h" #include "nfs_proto_tools.h" /** * @brief Add a segment to an existing layout state * * This function is intended to be used in nfs41_op_layoutget to add * each segment returned by FSAL_layoutget to an existing state of * type STATE_TYPE_LAYOUT. * * @note state_lock must be held for write. * * @param[in] state The layout state. * @param[in] segment Layout segment itself granted by the FSAL * @param[in] fsal_data Pointer to FSAL-specific data for this segment. * @param[in] return_on_close True for automatic return on last close * * @return STATE_SUCCESS on completion, other values of state_status_t * on failure. */ state_status_t state_add_segment(state_t *state, struct pnfs_segment *segment, void *fsal_data, bool return_on_close) { /* Pointer to the new segment being added to the state */ state_layout_segment_t *new_segment = NULL; if (state->state_type != STATE_TYPE_LAYOUT) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_stateid(&dspbuf, state); LogCrit(COMPONENT_PNFS, "Attempt to add layout segment to non-layout state: %s", str); return STATE_BAD_TYPE; } new_segment = gsh_calloc(1, sizeof(*new_segment)); new_segment->sls_fsal_data = fsal_data; new_segment->sls_state = state; new_segment->sls_segment = *segment; glist_add_tail(&state->state_data.layout.state_segments, &new_segment->sls_state_segments); /* Based on comments by Benny Halevy, if any segment is marked return_on_close, all segments should be treated as return_on_close. */ if (return_on_close) state->state_data.layout.state_return_on_close = true; return STATE_SUCCESS; } /** * @brief Delete a layout segment * * This function must be called with the mutex lock held. * * @param[in] segment Segment to delete * * @return State status. */ state_status_t state_delete_segment(state_layout_segment_t *segment) { glist_del(&segment->sls_state_segments); gsh_free(segment); return STATE_SUCCESS; } /** * @brief Find pre-existing layouts * * This function finds a layout corresponding to a given file, * clientid, and layout type if one exists. * * @note state_lock MUST be held for read * * @param[in] obj File * @param[in] owner The state owner. This must be a clientid owner. * @param[in] type Layout type specified by the client. * @param[out] state The found state, NULL if not found. * * @return STATE_SUCCESS if the layout is found, STATE_NOT_FOUND if it * isn't, and an appropriate code if other bad things happen. */ state_status_t state_lookup_layout_state(struct fsal_obj_handle *obj, state_owner_t *owner, layouttype4 type, state_t **state) { /* Pointer for iterating over the list of states on the file */ struct glist_head *glist_iter = NULL; /* The state under inspection in the loop */ state_t *state_iter = NULL; glist_for_each(glist_iter, &obj->state_hdl->file.list_of_states) { state_iter = glist_entry(glist_iter, state_t, state_list); if (state_iter->state_type == STATE_TYPE_LAYOUT && state_same_owner(state_iter, owner) && state_iter->state_data.layout.state_layout_type == type) { inc_state_t_ref(state_iter); *state = state_iter; return STATE_SUCCESS; } } return STATE_NOT_FOUND; } /** * @brief Revoke layouts belonging to the client owner. * * @param[in,out] client owner */ void revoke_owner_layouts(state_owner_t *client_owner) { state_t *state, *first; struct fsal_obj_handle *obj; struct gsh_export *saved_export = op_ctx->ctx_export; struct gsh_export *export; int errcnt = 0; struct glist_head *glist, *glistn; bool so_mutex_held; again: first = NULL; PTHREAD_MUTEX_lock(&client_owner->so_mutex); so_mutex_held = true; glist_for_each_safe(glist, glistn, &client_owner->so_owner.so_nfs4_owner.so_state_list) { bool deleted = false; struct pnfs_segment entire = { .io_mode = LAYOUTIOMODE4_ANY, .offset = 0, .length = NFS4_UINT64_MAX }; state = glist_entry(glist, state_t, state_owner_list); /* We set first to the first state we look in this iteration. * If the current state matches the first state, it implies * that went through the entire list without droping the lock * guarding the list. So nothing more left to process. */ if (first == NULL) first = state; else if (first == state) break; /* Move entry to end of list to handle errors and skipping of * non-layout states. */ glist_del(&state->state_owner_list); glist_add_tail( &client_owner->so_owner.so_nfs4_owner.so_state_list, &state->state_owner_list); /* Skip non-layout states. */ if (state->state_type != STATE_TYPE_LAYOUT) continue; if (!get_state_obj_export_owner_refs(state, &obj, &export, NULL)) { LogDebug(COMPONENT_STATE, "Stale state or file"); continue; } inc_state_t_ref(state); /* Set up the op_context with the proper export */ op_ctx->ctx_export = export; op_ctx->fsal_export = export->fsal_export; PTHREAD_MUTEX_unlock(&client_owner->so_mutex); so_mutex_held = false; PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); (void) nfs4_return_one_state(obj, LAYOUTRETURN4_FILE, circumstance_revoke, state, entire, 0, NULL, &deleted); if (!deleted) { errcnt++; LogCrit(COMPONENT_PNFS, "Layout state not destroyed during lease expiry."); } PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); /* Release the reference taken above */ obj->obj_ops.put_ref(obj); put_gsh_export(export); dec_state_t_ref(state); if (errcnt < STATE_ERR_MAX) { /* Loop again, but since we droped the so_mutex, we * must restart. */ goto again; } /* Too many errors, quit. */ break; } if (so_mutex_held) PTHREAD_MUTEX_unlock(&client_owner->so_mutex); if (errcnt == STATE_ERR_MAX) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_owner(&dspbuf, client_owner); LogFatal(COMPONENT_STATE, "Could not complete cleanup of layouts for client owner %s", str); } op_ctx->ctx_export = saved_export; if (saved_export != NULL) op_ctx->fsal_export = op_ctx->ctx_export->fsal_export; } /** @} */ nfs-ganesha-2.6.0/src/SAL/state_lock.c000066400000000000000000003024031324272410200174120ustar00rootroot00000000000000/* vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup SAL State abstraction layer * @{ */ /** * @file state_lock.c * @brief Functions used in lock management. */ #include "config.h" #include #include #include #include #include #include #include "log.h" #include "hashtable.h" #include "fsal.h" #include "nfs_core.h" #include "nfs4.h" #include "sal_functions.h" /*#include "nlm_util.h"*/ #include "export_mgr.h" /** * @page state_lock_entry_locking state_lock_entry_t locking rule * * The value is always updated/read with @c nlm_lock_entry->lock held If * we have @c nlm_lock_list mutex held we can read it safely, because the * value is always updated while walking the list with @c entry->state_lock * held. * * The update happens as below: * @code{.c} * pthread_rwlock_wrlock(&entry->state_mutex) * pthread_mutex_lock(lock_entry->sle_mutex) * update the lock_entry value * ........ * @endcode * * The value is ref counted with nlm_lock_entry->sle_ref_count so that * a parallel cancel/unlock won't endup freeing the datastructure. The * last release on the data structure ensure that it is freed. */ #ifdef DEBUG_SAL /** * @brief All locks. */ static struct glist_head state_all_locks = GLIST_HEAD_INIT(state_all_locks); /** * @brief All locks mutex */ pthread_mutex_t all_locks_mutex = PTHREAD_MUTEX_INITIALIZER; #endif /** * @brief All locks blocked in FSAL */ struct glist_head state_blocked_locks = GLIST_HEAD_INIT(state_blocked_locks); /** * @brief Mutex to protect lock lists */ pthread_mutex_t blocked_locks_mutex = PTHREAD_MUTEX_INITIALIZER; /** * @brief Owner of state with no defined owner */ state_owner_t unknown_owner = { .so_owner_val = "ganesha_unknown_owner", .so_type = STATE_LOCK_OWNER_UNKNOWN, .so_refcount = 1, .so_owner_len = 21, .so_lock_list = GLIST_HEAD_INIT(unknown_owner.so_lock_list), .so_mutex = PTHREAD_MUTEX_INITIALIZER }; static state_status_t do_lock_op(struct fsal_obj_handle *obj, state_t *state, fsal_lock_op_t lock_op, state_owner_t *owner, fsal_lock_param_t *lock, state_owner_t **holder, fsal_lock_param_t *conflict, bool overlap); /** * @brief Blocking lock cookies */ /** * Parameters used for lock cookie hash table initialization. * * @todo Switch the cookie table to something else and get rid * of this. */ static hash_parameter_t cookie_param = { .index_size = PRIME_STATE, .hash_func_key = lock_cookie_value_hash_func, .hash_func_rbt = lock_cookie_rbt_hash_func, .compare_key = compare_lock_cookie_key, .key_to_str = display_lock_cookie_key, .val_to_str = display_lock_cookie_val, .flags = HT_FLAG_NONE, }; static hash_table_t *ht_lock_cookies; /** * @brief Initalize locking * * @return State status. */ state_status_t state_lock_init(void) { state_status_t status = STATE_SUCCESS; ht_lock_cookies = hashtable_init(&cookie_param); if (ht_lock_cookies == NULL) { LogCrit(COMPONENT_STATE, "Cannot init NLM Client cache"); status = STATE_INIT_ENTRY_FAILED; return status; } status = state_async_init(); state_owner_pool = pool_basic_init("NFSv4 state owners", sizeof(state_owner_t)); return status; } /** * @brief Check whether a lock is from NLM * * @param[in] lock_entry Lock to check * * @retval true if the lock is from NLM. * @retval false if the lock is not from NLM. */ bool lock_owner_is_nlm(state_lock_entry_t *lock_entry) { #ifdef _USE_NLM return lock_entry->sle_owner->so_type == STATE_LOCK_OWNER_NLM; #else /* _USE_NLM */ return false; #endif /* _USE_NLM */ } /****************************************************************************** * * Functions to display various aspects of a lock * ******************************************************************************/ /** * @brief Find the end of lock range * * @param[in] lock Lock to check * * @return Last byte of lock. */ static inline uint64_t lock_end(fsal_lock_param_t *lock) { if (lock->lock_length == 0) return UINT64_MAX; else return lock->lock_start + lock->lock_length - 1; } /** * @brief String for lock type * * @param[in] ltype Lock type * * @return Readable string. */ const char *str_lockt(fsal_lock_t ltype) { switch (ltype) { case FSAL_LOCK_R: return "READ "; case FSAL_LOCK_W: return "WRITE"; case FSAL_NO_LOCK: return "NO LOCK"; } return "?????"; } /** * @brief Return string for blocking status * * @param[in] blocking Blocking status * * @return String for blocking status. */ const char *str_blocking(state_blocking_t blocking) { switch (blocking) { case STATE_NON_BLOCKING: return "NON_BLOCKING "; case STATE_NLM_BLOCKING: return "NLM_BLOCKING "; case STATE_NFSV4_BLOCKING: return "NFSV4_BLOCKING"; case STATE_GRANTING: return "GRANTING "; case STATE_CANCELED: return "CANCELED "; } return "unknown "; } /** * @brief Return string for blocking status * * @param[in] blocked Blocking status * * @return String for blocking status. */ const char *str_blocked(state_blocking_t blocked) { switch (blocked) { case STATE_NON_BLOCKING: return "GRANTED "; case STATE_NLM_BLOCKING: return "NLM_BLOCKING "; case STATE_NFSV4_BLOCKING: return "NFSV4_BLOCKING"; case STATE_GRANTING: return "GRANTING "; case STATE_CANCELED: return "CANCELED "; } return "unknown "; } const char *str_block_type(state_block_type_t btype) { switch (btype) { case STATE_BLOCK_NONE: return "STATE_BLOCK_NONE "; case STATE_BLOCK_INTERNAL: return "STATE_BLOCK_INTERNAL"; case STATE_BLOCK_ASYNC: return "STATE_BLOCK_ASYNC "; case STATE_BLOCK_POLL: return "STATE_BLOCK_POLL "; } return "unknown "; } /****************************************************************************** * * Function to compare lock parameters * ******************************************************************************/ /** * @brief Check if locks differ * * @note This is not complete, it doesn't check the owner's IP * address. * * @param[in] lock1 A lock * @param[in] lock2 Another lock * * @retval true if locks differ. * @retval false if locks are the same. */ static inline bool different_lock(fsal_lock_param_t *lock1, fsal_lock_param_t *lock2) { return (lock1->lock_type != lock2->lock_type) || (lock1->lock_start != lock2->lock_start) || (lock1->lock_length != lock2->lock_length); } /****************************************************************************** * * Functions to log locks in various ways * ******************************************************************************/ /** * @brief Log a lock entry with a passed refcount * * @param[in] reason Arbitrary string * @param[in] le Entry to log */ static void log_entry_ref_count(const char *reason, state_lock_entry_t *le, int32_t refcount, char *file, int line, char *function) { if (isFullDebug(COMPONENT_STATE)) { char owner[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(owner), owner, owner}; display_owner(&dspbuf, le->sle_owner); DisplayLogComponentLevel(COMPONENT_STATE, file, line, function, NIV_FULL_DEBUG, "%s Entry: %p obj=%p, fileid=%" PRIu64 ", export=%u, type=%s, start=0x%"PRIx64 ", end=0x%"PRIx64 ", blocked=%s/%p/%s, state=%p, refcount=%"PRIu32 ", owner={%s}", reason, le, le->sle_obj, (uint64_t) le->sle_obj->fileid, (unsigned int)le->sle_export->export_id, str_lockt(le->sle_lock.lock_type), le->sle_lock.lock_start, lock_end(&le->sle_lock), str_blocked(le->sle_blocked), le->sle_block_data, le->sle_block_data ? str_block_type(le->sle_block_data->sbd_block_type) : str_block_type(STATE_BLOCK_NONE), le->sle_state, refcount, owner); } } #define LogEntryRefCount(reason, le, refcount) \ log_entry_ref_count(reason, le, refcount, \ (char *) __FILE__, __LINE__, (char *) __func__) /** * @brief Log a lock entry * * @param[in] reason Arbitrary string * @param[in] le Entry to log */ #define LogEntry(reason, le) \ log_entry_ref_count(reason, le, \ atomic_fetch_int32_t(&le->sle_ref_count), \ (char *) __FILE__, __LINE__, (char *) __func__) /** * @brief Log a list of locks * * @param[in] reason Arbitrary string * @param[in] obj FSAL object (mostly unused) * @param[in] list List of lock entries * * @retval true if list is empty. * @retval false if list is non-empty. */ static bool LogList(const char *reason, struct fsal_obj_handle *obj, struct glist_head *list) { if (isFullDebug(COMPONENT_STATE)) { struct glist_head *glist; state_lock_entry_t *found_entry; if (glist_empty(list)) { if (obj != NULL) LogFullDebug(COMPONENT_STATE, "%s for %p is empty", reason, obj); else LogFullDebug(COMPONENT_STATE, "%s is empty", reason); return true; } glist_for_each(glist, list) { found_entry = glist_entry(glist, state_lock_entry_t, sle_list); LogEntry(reason, found_entry); if (found_entry->sle_obj == NULL) break; } } return false; } /** * @brief Log blocked locks on list * * Must hold blocked_locks_mutex. * * @param[in] reason Arbitrary string * @param[in] obj File * @param[in] list List of lock entries * * @retval true if list is empty. * @retval false if list is non-empty. */ static bool LogBlockedList(const char *reason, struct fsal_obj_handle *obj, struct glist_head *list) { if (isFullDebug(COMPONENT_STATE)) { struct glist_head *glist; state_lock_entry_t *found_entry; state_block_data_t *block_entry; if (glist_empty(list)) { if (obj != NULL) LogFullDebug(COMPONENT_STATE, "%s for %p is empty", reason, obj); else LogFullDebug(COMPONENT_STATE, "%s is empty", reason); return true; } glist_for_each(glist, list) { block_entry = glist_entry(glist, state_block_data_t, sbd_list); found_entry = block_entry->sbd_lock_entry; LogEntry(reason, found_entry); if (found_entry->sle_obj == NULL) break; } } return false; } /** * @brief Log a lock * * @param[in] component Component to log to * @param[in] debug Log level * @param[in] reason Arbitrary string * @param[in] obj File * @param[in] owner Lock owner * @param[in] lock Lock description */ void log_lock(log_components_t component, log_levels_t debug, const char *reason, struct fsal_obj_handle *obj, state_owner_t *owner, fsal_lock_param_t *lock, char *file, int line, char *function) { if (isLevel(component, debug)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; if (owner != NULL) display_owner(&dspbuf, owner); else display_cat(&dspbuf, "NONE"); DisplayLogComponentLevel(component, file, line, function, debug, "%s Lock: obj=%p, fileid=%" PRIu64 ", type=%s, start=0x%"PRIx64", end=0x%"PRIx64 ", owner={%s}", reason, obj, (uint64_t) obj->fileid, str_lockt(lock->lock_type), lock->lock_start, lock_end(lock), str); } } /** * @brief Log a lock description * * @param[in] component Component to log to * @param[in] debug Log level * @param[in] reason Arbitrary string * @param[in] obj FSAL obj handle * @param[in] owner Lock owner * @param[in] lock Lock description */ void log_lock_desc(log_components_t component, log_levels_t debug, const char *reason, struct fsal_obj_handle *obj, void *owner, fsal_lock_param_t *lock, char *file, int line, char *function) { if (isLevel(component, debug)) { DisplayLogComponentLevel(component, file, line, function, debug, "%s Lock: obj=%p, owner=%p, type=%s, start=0x%llx, end=0x%llx", reason, obj, owner, str_lockt(lock->lock_type), (unsigned long long)lock->lock_start, (unsigned long long)lock_end(lock)); } } #define LogLockDesc(component, debug, reason, obj, owner, lock) \ log_lock_desc(component, debug, reason, obj, owner, lock, \ (char *) __FILE__, __LINE__, (char *) __func__) /** * @brief Log all locks * * @param[in] label Arbitrary string */ void dump_all_locks(const char *label) { #ifdef DEBUG_SAL struct glist_head *glist; PTHREAD_MUTEX_lock(&all_locks_mutex); if (glist_empty(&state_all_locks)) { LogFullDebug(COMPONENT_STATE, "All Locks are freed"); PTHREAD_MUTEX_unlock(&all_locks_mutex); return; } glist_for_each(glist, &state_all_locks) LogEntry(label, glist_entry(glist, state_lock_entry_t, sle_all_locks)); PTHREAD_MUTEX_unlock(&all_locks_mutex); #else return; #endif } /****************************************************************************** * * Functions to manage lock entries and lock list * ******************************************************************************/ /** * @brief Create a lock entry * * This function aborts (in gsh_malloc) if no memory is available. * * @param[in] obj File to lock * @param[in] export Export being accessed * @param[in] blocked Blocking status * @param[in] owner Lock owner * @param[in] state State associated with lock * @param[in] lock Lock description * * @return The new entry. */ static state_lock_entry_t *create_state_lock_entry(struct fsal_obj_handle *obj, struct gsh_export *export, state_blocking_t blocked, state_owner_t *owner, state_t *state, fsal_lock_param_t *lock) { state_lock_entry_t *new_entry; new_entry = gsh_calloc(1, sizeof(*new_entry)); LogFullDebug(COMPONENT_STATE, "new_entry = %p owner %p", new_entry, owner); PTHREAD_MUTEX_init(&new_entry->sle_mutex, NULL); /* sle_block_data will be filled in later if necessary */ new_entry->sle_block_data = NULL; new_entry->sle_ref_count = 1; new_entry->sle_obj = obj; new_entry->sle_blocked = blocked; new_entry->sle_owner = owner; new_entry->sle_state = state; new_entry->sle_lock = *lock; new_entry->sle_export = export; #ifdef _USE_NLM if (owner->so_type == STATE_LOCK_OWNER_NLM) { /* Add to list of locks owned by client that owner belongs to */ state_nlm_client_t *client = owner->so_owner.so_nlm_owner.so_client; inc_nsm_client_ref(client->slc_nsm_client); PTHREAD_MUTEX_lock(&client->slc_nsm_client->ssc_mutex); glist_add_tail(&client->slc_nsm_client->ssc_lock_list, &new_entry->sle_client_locks); PTHREAD_MUTEX_unlock(&client->slc_nsm_client->ssc_mutex); } #endif /* _USE_NLM */ /* Add to list of locks owned by export */ PTHREAD_RWLOCK_wrlock(&export->lock); glist_add_tail(&export->exp_lock_list, &new_entry->sle_export_locks); PTHREAD_RWLOCK_unlock(&export->lock); get_gsh_export_ref(export); /* Get ref for sle_obj */ obj->obj_ops.get_ref(obj); /* Add to list of locks owned by owner */ inc_state_owner_ref(owner); PTHREAD_MUTEX_lock(&owner->so_mutex); if (state != NULL) { glist_add_tail(&state->state_data.lock.state_locklist, &new_entry->sle_state_locks); inc_state_t_ref(state); } glist_add_tail(&owner->so_lock_list, &new_entry->sle_owner_locks); PTHREAD_MUTEX_unlock(&owner->so_mutex); #ifdef DEBUG_SAL PTHREAD_MUTEX_lock(&all_locks_mutex); glist_add_tail(&state_all_locks, &new_entry->sle_all_locks); PTHREAD_MUTEX_unlock(&all_locks_mutex); #endif return new_entry; } /** * @brief Duplicate a lock entry * * @param[in] orig_entry Entry to duplicate * * @return New entry. */ static inline state_lock_entry_t * state_lock_entry_t_dup(state_lock_entry_t *orig_entry) { return create_state_lock_entry(orig_entry->sle_obj, orig_entry->sle_export, orig_entry->sle_blocked, orig_entry->sle_owner, orig_entry->sle_state, &orig_entry->sle_lock); } /** * @brief Take a reference on a lock entry * * @param[in,out] lock_entry Entry to reference */ static inline void lock_entry_inc_ref(state_lock_entry_t *lock_entry) { int32_t refcount = atomic_inc_int32_t(&lock_entry->sle_ref_count); LogEntryRefCount("Increment refcount", lock_entry, refcount); } /** * @brief Relinquish a reference on a lock entry * * @param[in,out] lock_entry Entry to release */ static void lock_entry_dec_ref(state_lock_entry_t *lock_entry) { int32_t refcount = atomic_dec_int32_t(&lock_entry->sle_ref_count); LogEntryRefCount(refcount != 0 ? "Decrement refcount" : "Decrement refcount and freeing", lock_entry, refcount); if (refcount == 0) { /* Release block data if present */ if (lock_entry->sle_block_data != NULL) { /* need to remove from the state_blocked_locks list */ PTHREAD_MUTEX_lock(&blocked_locks_mutex); glist_del(&lock_entry->sle_block_data->sbd_list); PTHREAD_MUTEX_unlock(&blocked_locks_mutex); gsh_free(lock_entry->sle_block_data); } #ifdef DEBUG_SAL PTHREAD_MUTEX_lock(&all_locks_mutex); glist_del(&lock_entry->sle_all_locks); PTHREAD_MUTEX_unlock(&all_locks_mutex); #endif lock_entry->sle_obj->obj_ops.put_ref(lock_entry->sle_obj); put_gsh_export(lock_entry->sle_export); PTHREAD_MUTEX_destroy(&lock_entry->sle_mutex); gsh_free(lock_entry); } } /** * @brief Remove an entry from the lock lists * * @param[in,out] lock_entry Entry to remove */ static void remove_from_locklist(state_lock_entry_t *lock_entry) { state_owner_t *owner = lock_entry->sle_owner; LogEntry("Removing", lock_entry); /* * If some other thread is holding a reference to this nlm_lock_entry * don't free the structure. But drop from the lock list */ if (owner != NULL) { #ifdef _USE_NLM if (owner->so_type == STATE_LOCK_OWNER_NLM) { /* Remove from list of locks owned * by client that owner belongs to */ state_nlm_client_t *client = owner->so_owner.so_nlm_owner.so_client; PTHREAD_MUTEX_lock(&client->slc_nsm_client->ssc_mutex); glist_del(&lock_entry->sle_client_locks); PTHREAD_MUTEX_unlock( &client->slc_nsm_client->ssc_mutex); dec_nsm_client_ref(client->slc_nsm_client); } #endif /* _USE_NLM */ /* Remove from list of locks owned by export */ PTHREAD_RWLOCK_wrlock(&lock_entry->sle_export->lock); glist_del(&lock_entry->sle_export_locks); PTHREAD_RWLOCK_unlock(&lock_entry->sle_export->lock); /* Remove from list of locks owned by owner */ PTHREAD_MUTEX_lock(&owner->so_mutex); glist_del(&lock_entry->sle_state_locks); glist_del(&lock_entry->sle_owner_locks); PTHREAD_MUTEX_unlock(&owner->so_mutex); dec_state_owner_ref(owner); if (lock_entry->sle_state != NULL) dec_state_t_ref(lock_entry->sle_state); } lock_entry->sle_owner = NULL; glist_del(&lock_entry->sle_list); lock_entry_dec_ref(lock_entry); } /** * @brief Find a conflicting entry * * @note The state_lock MUST be held for read * * @param[in] ostate File state to search * @param[in] owner The lock owner * @param[in] lock Lock to check * * @return A conflicting entry or NULL. */ static state_lock_entry_t *get_overlapping_entry(struct state_hdl *ostate, state_owner_t *owner, fsal_lock_param_t *lock) { struct glist_head *glist; state_lock_entry_t *found_entry = NULL; uint64_t found_entry_end, range_end = lock_end(lock); glist_for_each(glist, &ostate->file.lock_list) { found_entry = glist_entry(glist, state_lock_entry_t, sle_list); LogEntry("Checking", found_entry); /* Skip blocked or cancelled locks */ if (found_entry->sle_blocked == STATE_NLM_BLOCKING || found_entry->sle_blocked == STATE_NFSV4_BLOCKING || found_entry->sle_blocked == STATE_CANCELED) continue; found_entry_end = lock_end(&found_entry->sle_lock); if ((found_entry_end >= lock->lock_start) && (found_entry->sle_lock.lock_start <= range_end)) { /* lock overlaps see if we can allow: * allow if neither lock is exclusive or * the owner is the same */ if ((found_entry->sle_lock.lock_type == FSAL_LOCK_W || lock->lock_type == FSAL_LOCK_W) && different_owners(found_entry->sle_owner, owner) ) { /* found a conflicting lock, return it */ return found_entry; } } } return NULL; } /** * @brief Add a lock, potentially merging with existing locks * * We need to iterate over the full lock list and remove * any mapping entry. And l_offset = 0 and sle_lock.lock_length = 0 lock_entry * implies remove all entries * * @note The state_lock MUST be held for write * * @param[in,out] ostate File state to operate on * @param[in] lock_entry Lock to add */ static void merge_lock_entry(struct state_hdl *ostate, state_lock_entry_t *lock_entry) { state_lock_entry_t *check_entry; state_lock_entry_t *check_entry_right; uint64_t check_entry_end; uint64_t lock_entry_end; struct glist_head *glist; struct glist_head *glistn; /* lock_entry might be STATE_NON_BLOCKING or STATE_GRANTING */ glist_for_each_safe(glist, glistn, &ostate->file.lock_list) { check_entry = glist_entry(glist, state_lock_entry_t, sle_list); /* Skip entry being merged - it could be in the list */ if (check_entry == lock_entry) continue; if (different_owners (check_entry->sle_owner, lock_entry->sle_owner)) continue; /* Only merge fully granted locks */ if (check_entry->sle_blocked != STATE_NON_BLOCKING) continue; check_entry_end = lock_end(&check_entry->sle_lock); lock_entry_end = lock_end(&lock_entry->sle_lock); if ((check_entry_end + 1) < lock_entry->sle_lock.lock_start) /* nothing to merge */ continue; if ((lock_entry_end + 1) < check_entry->sle_lock.lock_start) /* nothing to merge */ continue; /* Need to handle locks of different types differently, may * split an old lock. If new lock totally overlaps old lock, * the new lock will replace the old lock so no special work * to be done. */ if ((check_entry->sle_lock.lock_type != lock_entry->sle_lock.lock_type) && ((lock_entry_end < check_entry_end) || (check_entry->sle_lock.lock_start < lock_entry->sle_lock.lock_start))) { if (lock_entry_end < check_entry_end && check_entry->sle_lock.lock_start < lock_entry->sle_lock.lock_start) { /* Need to split old lock */ check_entry_right = state_lock_entry_t_dup(check_entry); glist_add_tail(&ostate->file.lock_list, &(check_entry_right->sle_list)); } else { /* No split, just shrink, make the logic below * work on original lock */ check_entry_right = check_entry; } if (lock_entry_end < check_entry_end) { /* Need to shrink old lock from beginning * (right lock if split) */ LogEntry("Merge shrinking right", check_entry_right); check_entry_right->sle_lock.lock_start = lock_entry_end + 1; check_entry_right->sle_lock.lock_length = check_entry_end - lock_entry_end; LogEntry("Merge shrunk right", check_entry_right); } if (check_entry->sle_lock.lock_start < lock_entry->sle_lock.lock_start) { /* Need to shrink old lock from end * (left lock if split) */ LogEntry("Merge shrinking left", check_entry); check_entry->sle_lock.lock_length = lock_entry->sle_lock.lock_start - check_entry->sle_lock.lock_start; LogEntry("Merge shrunk left", check_entry); } /* Done splitting/shrinking old lock */ continue; } /* check_entry touches or overlaps lock_entry, expand * lock_entry */ if (lock_entry_end < check_entry_end) /* Expand end of lock_entry */ lock_entry_end = check_entry_end; if (check_entry->sle_lock.lock_start < lock_entry->sle_lock.lock_start) /* Expand start of lock_entry */ lock_entry->sle_lock.lock_start = check_entry->sle_lock.lock_start; /* Compute new lock length */ lock_entry->sle_lock.lock_length = lock_entry_end - lock_entry->sle_lock.lock_start + 1; /* Remove merged entry */ LogEntry("Merged", lock_entry); LogEntry("Merging removing", check_entry); remove_from_locklist(check_entry); } } /** * @brief Free a list of lock entries * * @param[in] list List of locks to free */ static void free_list(struct glist_head *list) { state_lock_entry_t *found_entry; struct glist_head *glist, *glistn; glist_for_each_safe(glist, glistn, list) { found_entry = glist_entry(glist, state_lock_entry_t, sle_list); remove_from_locklist(found_entry); } } /** * @brief Subtract a lock from a lock entry. * * This function places any remaining bits into the split list. * * @param[in,out] found_entry Lock being modified * @param[in] lock Lock being removed * @param[out] split_list Remaining fragments of found_entry * @param[out] remove_list Removed lock entries * @param[out] removed True if lock is removed * * @return State status. */ static state_status_t subtract_lock_from_entry(state_lock_entry_t *found_entry, fsal_lock_param_t *lock, struct glist_head *split_list, struct glist_head *remove_list, bool *removed) { uint64_t found_entry_end = lock_end(&found_entry->sle_lock); uint64_t range_end = lock_end(lock); state_lock_entry_t *found_entry_left = NULL; state_lock_entry_t *found_entry_right = NULL; state_status_t status = STATE_SUCCESS; if (range_end < found_entry->sle_lock.lock_start) { /* nothing to split */ *removed = false; return status; } if (found_entry_end < lock->lock_start) { /* nothing to split */ *removed = false; return status; } if ((lock->lock_start <= found_entry->sle_lock.lock_start) && range_end >= found_entry_end) { /* Fully overlap */ LogEntry("Remove Complete", found_entry); goto complete_remove; } LogEntry("Split", found_entry); /* Delete the old entry and add one or two new entries */ if (lock->lock_start > found_entry->sle_lock.lock_start) { found_entry_left = state_lock_entry_t_dup(found_entry); found_entry_left->sle_lock.lock_length = lock->lock_start - found_entry->sle_lock.lock_start; LogEntry("Left split", found_entry_left); glist_add_tail(split_list, &(found_entry_left->sle_list)); } if (range_end < found_entry_end) { found_entry_right = state_lock_entry_t_dup(found_entry); found_entry_right->sle_lock.lock_start = range_end + 1; /* found_entry_end being UINT64_MAX indicates that the * sle_lock.lock_length is zero and the lock is held till * the end of the file. In such case assign the split lock * length too to zero to indicate the file end. */ if (found_entry_end == UINT64_MAX) found_entry_right->sle_lock.lock_length = 0; else found_entry_right->sle_lock.lock_length = found_entry_end - range_end; LogEntry("Right split", found_entry_right); glist_add_tail(split_list, &(found_entry_right->sle_list)); } complete_remove: /* Remove the lock from the list it's * on and put it on the remove_list */ glist_del(&found_entry->sle_list); glist_add_tail(remove_list, &(found_entry->sle_list)); *removed = true; return status; } /** * @brief Subtract a lock from a list of locks * * This function possibly splits entries in the list. * * @param[in] owner Lock owner * @param[in] state Associated lock state * @param[in] lock Lock to remove * @param[out] removed True if an entry was removed * @param[in,out] list List of locks to modify * * @return State status. */ static state_status_t subtract_lock_from_list(state_owner_t *owner, bool state_applies, int32_t state, fsal_lock_param_t *lock, bool *removed, struct glist_head *list) { state_lock_entry_t *found_entry; struct glist_head split_lock_list, remove_list; struct glist_head *glist, *glistn; state_status_t status = STATE_SUCCESS; bool removed_one = false; *removed = false; glist_init(&split_lock_list); glist_init(&remove_list); glist_for_each_safe(glist, glistn, list) { found_entry = glist_entry(glist, state_lock_entry_t, sle_list); if (owner != NULL && different_owners(found_entry->sle_owner, owner)) continue; /* Only care about granted locks */ if (found_entry->sle_blocked != STATE_NON_BLOCKING) continue; /* Skip locks owned by this NLM state. * This protects NLM locks from the current iteration of an NLM * client from being released by SM_NOTIFY. */ if (state_applies && found_entry->sle_state->state_seqid == state) continue; /* We have matched owner. Even though we are taking a reference * to found_entry, we don't inc the ref count because we want * to drop the lock entry. */ status = subtract_lock_from_entry(found_entry, lock, &split_lock_list, &remove_list, &removed_one); *removed |= removed_one; if (status != STATE_SUCCESS) { /* We ran out of memory while splitting, * deal with it outside loop */ break; } } if (status != STATE_SUCCESS) { /* We ran out of memory while splitting. split_lock_list * has been freed. For each entry on the remove_list, put * it back on the list. */ LogDebug(COMPONENT_STATE, "Failed %s", state_err_str(status)); glist_for_each_safe(glist, glistn, &remove_list) { found_entry = glist_entry(glist, state_lock_entry_t, sle_list); glist_del(&found_entry->sle_list); glist_add_tail(list, &(found_entry->sle_list)); } } else { /* free the enttries on the remove_list */ free_list(&remove_list); /* now add the split lock list */ glist_add_list_tail(list, &split_lock_list); } LogFullDebug(COMPONENT_STATE, "List of all locks for list=%p returning %d", list, status); return status; } /****************************************************************************** * * Implement hash table to hash blocked lock entries by cookie * ******************************************************************************/ static void grant_blocked_locks(struct state_hdl *); /** * @brief Display lock cookie in hash table * * @param[in] buff Key to display * @param[out] str Output buffer * * @return Length of output string. */ int display_lock_cookie_key(struct gsh_buffdesc *buff, char *str) { struct display_buffer dspbuf = {HASHTABLE_DISPLAY_STRLEN, str, str}; display_lock_cookie(&dspbuf, buff); return display_buffer_len(&dspbuf); } /** * @brief Display lock cookie entry * * @param[in/out] dspbuf display_buffer describing output string * @param[in] he Cookie entry to display * * @return the bytes remaining in the buffer. */ int display_lock_cookie_entry(struct display_buffer *dspbuf, state_cookie_entry_t *he) { int b_left = display_printf(dspbuf, "%p: cookie ", he); if (b_left <= 0) return b_left; b_left = display_opaque_value(dspbuf, he->sce_cookie, he->sce_cookie_size); if (b_left <= 0) return b_left; b_left = display_printf(dspbuf, " obj {%p fileid=%" PRIu64 "} lock {", he->sce_obj, he->sce_obj->fileid); if (b_left <= 0) return b_left; if (he->sce_lock_entry != NULL) { b_left = display_printf(dspbuf, "%p owner {", he->sce_lock_entry); if (b_left <= 0) return b_left; b_left = display_owner(dspbuf, he->sce_lock_entry->sle_owner); if (b_left <= 0) return b_left; b_left = display_printf( dspbuf, "} type=%s start=0x%"PRIx64" end=0x%" PRIx64" blocked=%s}", str_lockt(he->sce_lock_entry->sle_lock.lock_type), he->sce_lock_entry->sle_lock.lock_start, lock_end(&he->sce_lock_entry->sle_lock), str_blocked(he->sce_lock_entry->sle_blocked)); } else { b_left = display_printf(dspbuf, "}"); } return b_left; } /** * @brief Display lock cookie entry in hash table * * @param[in] buff Value to display * @param[out] str Output buffer * * @return Length of output string. */ int display_lock_cookie_val(struct gsh_buffdesc *buff, char *str) { struct display_buffer dspbuf = {HASHTABLE_DISPLAY_STRLEN, str, str}; display_lock_cookie_entry(&dspbuf, buff->addr); return display_buffer_len(&dspbuf); } /** * @brief Compare lock cookie in hash table * * @param[in] buff1 A key * @param[in] buff2 Another key * * @retval 0 on equality. * @retval 1 on inequality. */ int compare_lock_cookie_key(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2) { if (isFullDebug(COMPONENT_STATE) && isDebug(COMPONENT_HASHTABLE)) { char str1[LOG_BUFF_LEN / 2] = "\0"; char str2[LOG_BUFF_LEN / 2] = "\0"; struct display_buffer dspbuf1 = {sizeof(str1), str1, str1}; struct display_buffer dspbuf2 = {sizeof(str2), str2, str2}; display_lock_cookie(&dspbuf1, buff1); display_lock_cookie(&dspbuf2, buff2); LogFullDebug(COMPONENT_STATE, "{%s} vs {%s}", str1, str2); } if (buff1->addr == buff2->addr) return 0; if (buff1->len != buff2->len) return 1; if (buff1->addr == NULL || buff2->addr == NULL) return 1; return memcmp(buff1->addr, buff2->addr, buff1->len); } /** * @brief Hash index for lock cookie * * @todo Replace with a good hash function. * * @param[in] hparam Hash parameters * @param[in] key Key to hash * * @return Hash index. */ uint32_t lock_cookie_value_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { unsigned int sum = 0; unsigned int i; unsigned long res; unsigned char *addr = key->addr; /* Compute the sum of all the characters */ for (i = 0; i < key->len; i++) sum += (unsigned char)addr[i]; res = (unsigned long)sum + (unsigned long)key->len; if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_STATE, "value = %lu", res % hparam->index_size); return (unsigned long)(res % hparam->index_size); } /** * @brief RBT hash for lock cookie * * @todo Replace with a good hash function. * * @param[in] hparam Hash parameters * @param[in] key Key to hash * * @return RBT hash. */ uint64_t lock_cookie_rbt_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key) { unsigned int sum = 0; unsigned int i; unsigned long res; unsigned char *addr = key->addr; /* Compute the sum of all the characters */ for (i = 0; i < key->len; i++) sum += (unsigned char)addr[i]; res = (unsigned long)sum + (unsigned long)key->len; if (isDebug(COMPONENT_HASHTABLE)) LogFullDebug(COMPONENT_STATE, "rbt = %lu", res); return res; } /** * @brief Free a cookie entry * * @param[in] cookie_entry Entry to free * @param[in] unblock Whether to remove block data */ void free_cookie(state_cookie_entry_t *cookie_entry, bool unblock) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; bool str_valid = false; void *cookie = cookie_entry->sce_cookie; state_lock_entry_t *lock_entry = cookie_entry->sce_lock_entry; if (isFullDebug(COMPONENT_STATE)) { display_lock_cookie_entry(&dspbuf, cookie_entry); str_valid = true; } /* Since the cookie is not in the hash table, * we can just free the memory */ if (str_valid) LogFullDebug(COMPONENT_STATE, "Free Lock Cookie {%s}", str); /* If block data is still attached to lock entry, remove it */ if (lock_entry != NULL && unblock) { if (lock_entry->sle_block_data != NULL) lock_entry->sle_block_data->sbd_blocked_cookie = NULL; lock_entry_dec_ref(lock_entry); } /* Free the memory for the cookie and the cookie entry */ gsh_free(cookie); gsh_free(cookie_entry); } /** * @brief Add a grant cookie to a blocked lock * * @param[in] obj File to operate on * @param[in] cookie Cookie to add * @param[in] cookie_size Cookie length * @param[in] lock_entry Lock entry * @param[out] cookie_entry New cookie entry * * @return State status. */ state_status_t state_add_grant_cookie(struct fsal_obj_handle *obj, void *cookie, int cookie_size, state_lock_entry_t *lock_entry, state_cookie_entry_t **cookie_entry) { struct gsh_buffdesc buffkey, buffval; state_cookie_entry_t *hash_entry; char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; bool str_valid = false; state_status_t status = 0; *cookie_entry = NULL; if (lock_entry->sle_block_data == NULL || cookie == NULL || cookie_size == 0) { /* Something's wrong with this entry */ status = STATE_INCONSISTENT_ENTRY; return status; } if (isFullDebug(COMPONENT_STATE)) { display_opaque_value(&dspbuf, cookie, cookie_size); str_valid = true; } hash_entry = gsh_calloc(1, sizeof(*hash_entry)); buffkey.addr = gsh_malloc(cookie_size); hash_entry->sce_obj = obj; hash_entry->sce_lock_entry = lock_entry; hash_entry->sce_cookie = buffkey.addr; hash_entry->sce_cookie_size = cookie_size; memcpy(buffkey.addr, cookie, cookie_size); buffkey.len = cookie_size; buffval.addr = (void *)hash_entry; buffval.len = sizeof(*hash_entry); if (isFullDebug(COMPONENT_STATE)) { display_lock_cookie_entry(&dspbuf, hash_entry); str_valid = true; } if (hashtable_test_and_set(ht_lock_cookies, &buffkey, &buffval, HASHTABLE_SET_HOW_SET_NO_OVERWRITE) != HASHTABLE_SUCCESS) { gsh_free(hash_entry); if (str_valid) LogFullDebug(COMPONENT_STATE, "Lock Cookie {%s} HASH TABLE ERROR", str); status = STATE_HASH_TABLE_ERROR; return status; } /* Increment lock entry reference count and link it to the cookie */ lock_entry_inc_ref(lock_entry); lock_entry->sle_block_data->sbd_blocked_cookie = hash_entry; if (str_valid) LogFullDebug(COMPONENT_STATE, "Lock Cookie {%s} Added", str); switch (lock_entry->sle_block_data->sbd_grant_type) { case STATE_GRANT_NONE: /* Shouldn't get here */ status = STATE_INCONSISTENT_ENTRY; break; case STATE_GRANT_POLL: case STATE_GRANT_FSAL_AVAILABLE: /* A poll triggered by the polling thread actually looks * exactly like a poll triggered by an FSAL upcall... * * Now that we are sure we can continue, acquire the FSAL lock. * If we get STATE_LOCK_BLOCKED we need to return... */ status = do_lock_op(obj, lock_entry->sle_state, FSAL_OP_LOCKB, lock_entry->sle_owner, &lock_entry->sle_lock, NULL, NULL, false); break; case STATE_GRANT_INTERNAL: /* Now that we are sure we can continue, acquire the FSAL lock. * If we get STATE_LOCK_BLOCKED we need to return... */ status = do_lock_op(obj, lock_entry->sle_state, FSAL_OP_LOCK, lock_entry->sle_owner, &lock_entry->sle_lock, NULL, NULL, false); break; case STATE_GRANT_FSAL: /* No need to go to FSAL for lock */ status = STATE_SUCCESS; break; } if (status != STATE_SUCCESS) { /* Lock will be returned to right blocking type if it is * still blocking. We could lose a block if we failed for * any other reason */ if (status == STATE_LOCK_BLOCKED) LogDebug(COMPONENT_STATE, "Unable to lock FSAL for %s lock, error=%s", str_blocked(lock_entry->sle_blocked), state_err_str(status)); else LogMajor(COMPONENT_STATE, "Unable to lock FSAL for %s lock, error=%s", str_blocked(lock_entry->sle_blocked), state_err_str(status)); LogEntry("Entry", lock_entry); /* And release the cookie without unblocking the lock. * grant_blocked_locks() will decide whether to keep or * free the block. */ free_cookie(hash_entry, false); return status; } *cookie_entry = hash_entry; return status; } /** * @brief Cancel a lock grant from the FSAL * * @param[in] cookie_entry Entry for the lock grant * * @return State status. */ state_status_t state_cancel_grant(state_cookie_entry_t *cookie_entry) { state_status_t status = 0; /* We had acquired an FSAL lock, need to release it. */ status = do_lock_op(cookie_entry->sce_obj, cookie_entry->sce_lock_entry->sle_state, FSAL_OP_UNLOCK, cookie_entry->sce_lock_entry->sle_owner, &cookie_entry->sce_lock_entry->sle_lock, NULL, /* no conflict expected */ NULL, false); if (status != STATE_SUCCESS) LogMajor(COMPONENT_STATE, "Unable to unlock FSAL for canceled GRANTED lock, error=%s", state_err_str(status)); /* And release the cookie and unblock lock * (because lock will be removed) */ free_cookie(cookie_entry, true); return status; } /** * @brief Find a grant matching a cookie * * @param[in] cookie Cookie to look up * @param[in] cookie_size Length of cookie * @param[out] cookie_entry Found entry * * @return State status. */ state_status_t state_find_grant(void *cookie, int cookie_size, state_cookie_entry_t **cookie_entry) { struct gsh_buffdesc buffkey; struct gsh_buffdesc buffval; struct gsh_buffdesc buffused_key; char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; bool str_valid = false; state_status_t status = 0; buffkey.addr = cookie; buffkey.len = cookie_size; if (isFullDebug(COMPONENT_STATE) && isDebug(COMPONENT_HASHTABLE)) { display_lock_cookie(&dspbuf, &buffkey); LogFullDebug(COMPONENT_STATE, "KEY {%s}", str); str_valid = true; } if (HashTable_Del(ht_lock_cookies, &buffkey, &buffused_key, &buffval) != HASHTABLE_SUCCESS) { if (str_valid) LogFullDebug(COMPONENT_STATE, "KEY {%s} NOTFOUND", str); status = STATE_BAD_COOKIE; return status; } *cookie_entry = buffval.addr; if (isFullDebug(COMPONENT_STATE) && isDebug(COMPONENT_HASHTABLE)) { display_lock_cookie_entry(&dspbuf, *cookie_entry); LogFullDebug(COMPONENT_STATE, "Found Lock Cookie {%s}", str); } status = STATE_SUCCESS; return status; } /** * @brief Grant a blocked lock * * @param[in] ostate File state on which to grant it * @param[in] lock_entry Lock entry */ void grant_blocked_lock_immediate(struct state_hdl *ostate, state_lock_entry_t *lock_entry) { state_cookie_entry_t *cookie = NULL; state_status_t state_status; /* Try to clean up blocked lock. */ if (lock_entry->sle_block_data != NULL) { if (lock_entry->sle_block_data->sbd_blocked_cookie != NULL) { /* Cookie is attached, try to get it */ cookie = lock_entry->sle_block_data->sbd_blocked_cookie; state_status = state_find_grant(cookie->sce_cookie, cookie->sce_cookie_size, &cookie); if (state_status == STATE_SUCCESS) { /* We've got the cookie, * free the cookie and the blocked lock */ free_cookie(cookie, true); } else { /* Otherwise, another thread has the cookie, * let it do it's business. */ return; } } else { /* We have block data but no cookie, * so we can just free the block data */ memset(lock_entry->sle_block_data, 0, sizeof(*lock_entry->sle_block_data)); gsh_free(lock_entry->sle_block_data); lock_entry->sle_block_data = NULL; } } /* Mark lock as granted */ lock_entry->sle_blocked = STATE_NON_BLOCKING; /* Merge any touching or overlapping locks into this one. */ LogEntry("Granted immediate, merging locks for", lock_entry); merge_lock_entry(ostate, lock_entry); LogEntry("Immediate Granted entry", lock_entry); /* A lock downgrade could unblock blocked locks */ grant_blocked_locks(ostate); } /** * @brief Finish granting a lock * * Do bookkeeping and merge the lock into the lock list. * * @param[in] cookie_entry Entry describing the grant */ void state_complete_grant(state_cookie_entry_t *cookie_entry) { state_lock_entry_t *lock_entry; struct fsal_obj_handle *obj; lock_entry = cookie_entry->sce_lock_entry; obj = cookie_entry->sce_obj; /* This routine does not call cache_inode_inc_pin_ref() because there * MUST be at least one lock present for there to be a cookie_entry * to even allow this routine to be called, and therefor the cache * entry MUST be pinned. */ PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); /* We need to make sure lock is ready to be granted */ if (lock_entry->sle_blocked == STATE_GRANTING) { /* Mark lock as granted */ lock_entry->sle_blocked = STATE_NON_BLOCKING; /* Merge any touching or overlapping locks into this one. */ LogEntry("Granted, merging locks for", lock_entry); merge_lock_entry(obj->state_hdl, lock_entry); LogEntry("Granted entry", lock_entry); /* A lock downgrade could unblock blocked locks */ grant_blocked_locks(obj->state_hdl); } /* Free cookie and unblock lock. * If somehow the lock was unlocked/canceled while the GRANT * was in progress, this will completely clean up the lock. */ free_cookie(cookie_entry, true); PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); } /** * @brief Attempt to grant a blocked lock * * @param[in] lock_entry Lock entry to grant */ void try_to_grant_lock(state_lock_entry_t *lock_entry) { granted_callback_t call_back; state_blocking_t blocked; state_status_t status; struct root_op_context root_op_context; struct gsh_export *export = lock_entry->sle_export; const char *reason; /* Try to grant if not cancelled and has block data and we are able * to get an export reference. */ if (lock_entry->sle_blocked == STATE_CANCELED) { reason = "Removing canceled blocked lock entry"; } else if (lock_entry->sle_block_data == NULL) { reason = "Removing blocked lock entry with no block data"; } else if (!export_ready(export)) { reason = "Removing blocked lock entry due to stale export"; } else { get_gsh_export_ref(export); call_back = lock_entry->sle_block_data->sbd_granted_callback; /* Mark the lock_entry as provisionally granted and make the * granted call back. The granted call back is responsible * for acquiring a reference to the lock entry if needed. */ blocked = lock_entry->sle_blocked; lock_entry->sle_blocked = STATE_GRANTING; if (lock_entry->sle_block_data->sbd_grant_type == STATE_GRANT_NONE) lock_entry->sle_block_data->sbd_grant_type = STATE_GRANT_INTERNAL; /* Initialize a root context, need to get a valid export. */ init_root_op_context(&root_op_context, export, export->fsal_export, 0, 0, UNKNOWN_REQUEST); status = call_back(lock_entry->sle_obj, lock_entry); put_gsh_export(export); release_root_op_context(); if (status == STATE_LOCK_BLOCKED) { /* The lock is still blocked, restore it's type and * leave it in the list. */ lock_entry->sle_blocked = blocked; lock_entry->sle_block_data->sbd_grant_type = STATE_GRANT_NONE; return; } /* At this point, we no longer need the entry on the * blocked lock list. */ PTHREAD_MUTEX_lock(&blocked_locks_mutex); glist_del(&lock_entry->sle_block_data->sbd_list); PTHREAD_MUTEX_unlock(&blocked_locks_mutex); if (status == STATE_SUCCESS) return; reason = "Removing unsucessfully granted blocked lock"; } /* There was no call back data, the call back failed, * or the block was cancelled. * Remove lock from list. */ LogEntry(reason, lock_entry); remove_from_locklist(lock_entry); } /** * @brief Routine to be called from the FSAL upcall handler * * @param[in] block_data Data describing blocked lock */ void process_blocked_lock_upcall(state_block_data_t *block_data) { state_lock_entry_t *lock_entry = block_data->sbd_lock_entry; lock_entry_inc_ref(lock_entry); PTHREAD_RWLOCK_wrlock(&lock_entry->sle_obj->state_hdl->state_lock); try_to_grant_lock(lock_entry); PTHREAD_RWLOCK_unlock(&lock_entry->sle_obj->state_hdl->state_lock); lock_entry_dec_ref(lock_entry); } /** * @brief Attempt to grant all blocked locks on a file * * @param[in] ostate File state */ static void grant_blocked_locks(struct state_hdl *ostate) { state_lock_entry_t *found_entry; struct glist_head *glist, *glistn; struct fsal_export *export = op_ctx->ctx_export->fsal_export; if (!ostate) return; /* If FSAL supports async blocking locks, * allow it to grant blocked locks. */ if (export->exp_ops.fs_supports(export, fso_lock_support_async_block)) return; glist_for_each_safe(glist, glistn, &ostate->file.lock_list) { found_entry = glist_entry(glist, state_lock_entry_t, sle_list); if (found_entry->sle_blocked != STATE_NLM_BLOCKING && found_entry->sle_blocked != STATE_NFSV4_BLOCKING) continue; /* Found a blocked entry for this file, * see if we can place the lock. */ if (get_overlapping_entry(ostate, found_entry->sle_owner, &found_entry->sle_lock) != NULL) continue; /* Found an entry that might work, try to grant it. */ try_to_grant_lock(found_entry); } } /** * @brief Cancel a blocked lock * * @param[in] obj File on which to cancel the lock * @param[in] lock_entry Lock to cancel * * @return State status. */ static void cancel_blocked_lock(struct fsal_obj_handle *obj, state_lock_entry_t *lock_entry) { state_cookie_entry_t *cookie = NULL; state_status_t state_status; /* Mark lock as canceled */ LogEntry("Cancelling blocked", lock_entry); lock_entry->sle_blocked = STATE_CANCELED; /* Unlocking the entire region will remove any FSAL locks we held, * whether from fully granted locks, or from blocking locks that were * in the process of being granted. */ /* Try to clean up blocked lock if a cookie is present */ if (lock_entry->sle_block_data != NULL && lock_entry->sle_block_data->sbd_blocked_cookie != NULL) { /* Cookie is attached, try to get it */ cookie = lock_entry->sle_block_data->sbd_blocked_cookie; state_status = state_find_grant(cookie->sce_cookie, cookie->sce_cookie_size, &cookie); if (state_status == STATE_SUCCESS) { /* We've got the cookie, * free the cookie and the blocked lock */ free_cookie(cookie, true); } /* otherwise, another thread has the cookie, let it do it's * business, which won't be much, since we've already marked * the lock CANCELED. */ } else { /* Otherwise, if block data is present, it will be freed when * the lock entry is freed. If the cookie is held, the refcount * it holds will prevent the lock entry from being released * until the cookie is freed. */ /* Since a cookie was not found, * the lock must still be in a state of needing cancelling. */ state_status = do_lock_op(obj, lock_entry->sle_state, FSAL_OP_CANCEL, lock_entry->sle_owner, &lock_entry->sle_lock, NULL, /* no conflict expected */ NULL, false); /* overlap not relevant */ if (state_status != STATE_SUCCESS) { /* Unable to cancel, * assume that granted upcall is on it's way. */ LogEntry("Unable to cancel (grant upcall expected)", lock_entry); return; } } /* Remove the lock from the lock list */ LogEntry("Removing", lock_entry); remove_from_locklist(lock_entry); } /** * * @brief Cancel blocked locks that overlap a lock * * Handle the situation where we have granted a lock and the client now * assumes it holds the lock, but we haven't received the GRANTED RSP, and * now the client is unlocking the lock. * * This will also handle the case of a client that uses UNLOCK to cancel * a blocked lock. * * Because this will release any blocked lock that was in the process of * being granted that overlaps the lock at all, we protect ourselves from * having a stuck lock at the risk of the client thinking it has a lock * it now doesn't. * * If the client unlock doesn't happen to fully overlap a blocked lock, * the blocked lock will be cancelled in full. Hopefully the client will * retry the remainder lock that should have still been blocking. * * @param[in,out] ostate File state on which to operate * @param[in] owner The state owner for the lock * @param[in] state Associated state * @param[in] lock Lock description */ void cancel_blocked_locks_range(struct state_hdl *ostate, state_owner_t *owner, bool state_applies, int32_t state, fsal_lock_param_t *lock) { struct glist_head *glist, *glistn; state_lock_entry_t *found_entry = NULL; uint64_t found_entry_end, range_end = lock_end(lock); glist_for_each_safe(glist, glistn, &ostate->file.lock_list) { found_entry = glist_entry(glist, state_lock_entry_t, sle_list); /* Skip locks not owned by owner */ if (owner != NULL && different_owners(found_entry->sle_owner, owner)) continue; /* Skip locks owned by this NLM state. * This protects NLM locks from the current iteration of an NLM * client from being released by SM_NOTIFY. */ if (state_applies && found_entry->sle_state->state_seqid == state) continue; /* Skip granted locks */ if (found_entry->sle_blocked == STATE_NON_BLOCKING) continue; LogEntry("Checking", found_entry); found_entry_end = lock_end(&found_entry->sle_lock); if ((found_entry_end >= lock->lock_start) && (found_entry->sle_lock.lock_start <= range_end)) { /* lock overlaps, cancel it. */ cancel_blocked_lock(ostate->file.obj, found_entry); } } } /** * @brief Release a lock grant * * @param[in] cookie_entry Grant entry * * @return State status. */ state_status_t state_release_grant(state_cookie_entry_t *cookie_entry) { state_lock_entry_t *lock_entry; struct fsal_obj_handle *obj; state_status_t status = STATE_SUCCESS; lock_entry = cookie_entry->sce_lock_entry; obj = cookie_entry->sce_obj; PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); /* We need to make sure lock is only "granted" once... * It's (remotely) possible that due to latency, we might end up * processing two GRANTED_RSP calls at the same time. */ if (lock_entry->sle_blocked == STATE_GRANTING) { /* Mark lock as canceled */ lock_entry->sle_blocked = STATE_CANCELED; /* We had acquired an FSAL lock, need to release it. */ status = do_lock_op(obj, lock_entry->sle_state, FSAL_OP_UNLOCK, lock_entry->sle_owner, &lock_entry->sle_lock, NULL, /* no conflict expected */ NULL, false); if (status != STATE_SUCCESS) LogMajor(COMPONENT_STATE, "Unable to unlock FSAL for released GRANTED lock, error=%s", state_err_str(status)); else { /* Remove the lock from the lock list. * Will not free yet because of cookie reference to * lock entry. */ LogEntry("Release Grant Removing", lock_entry); remove_from_locklist(lock_entry); } } /* Free the cookie and unblock the lock. This will release our final * reference on the lock entry and should free it. (Unless another * thread has a reference for some reason. */ free_cookie(cookie_entry, true); /* Check to see if we can grant any blocked locks. */ grant_blocked_locks(obj->state_hdl); PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); /* In case all locks have wound up free, * we must release the object reference. */ if (glist_empty(&obj->state_hdl->file.lock_list)) obj->obj_ops.put_ref(obj); return status; } /****************************************************************************** * * Functions to interract with FSAL * ******************************************************************************/ /** * @brief Human-readable string from the lock operation * * @param[in] op The lock operation * * @return The human-readable string. */ static inline const char *fsal_lock_op_str(fsal_lock_op_t op) { switch (op) { case FSAL_OP_LOCKT: return "FSAL_OP_LOCKT "; case FSAL_OP_LOCK: return "FSAL_OP_LOCK "; case FSAL_OP_LOCKB: return "FSAL_OP_LOCKB "; case FSAL_OP_UNLOCK: return "FSAL_OP_UNLOCK"; case FSAL_OP_CANCEL: return "FSAL_OP_CANCEL"; } return "unknown"; } /** * @brief Perform a lock operation * * We do state management and call down to the FSAL as appropriate, so * that the caller has a single entry point. * * @note The state_lock MUST be held for write * * @param[in] obj File on which to operate * @param[in] state state_t associated with lock if any * @param[in] lock_op Operation to perform * @param[in] owner Lock operation * @param[in] lock Lock description * @param[out] holder Owner of conflicting lock * @param[out] conflict Description of conflicting lock * @param[in] overlap Hint that lock overlaps * * @return State status. */ state_status_t do_lock_op(struct fsal_obj_handle *obj, state_t *state, fsal_lock_op_t lock_op, state_owner_t *owner, fsal_lock_param_t *lock, state_owner_t **holder, fsal_lock_param_t *conflict, bool overlap) { fsal_status_t fsal_status; state_status_t status = STATE_SUCCESS; fsal_lock_param_t conflicting_lock; struct fsal_export *fsal_export = op_ctx->fsal_export; fsal_lock_op_t fsal_lock_op = lock_op; lock->lock_sle_type = FSAL_POSIX_LOCK; /* Quick exit if: * Locks are not supported by FSAL * Async blocking locks are not supported and this is a cancel * Lock owners are not supported and hint tells us that lock fully * overlaps a lock we already have (no need to make another FSAL * call in that case) * * We do NOT need to quick exit if async blocking locks are not * supported and there is an overlap because we won't get here if * the overlap includes a write lock (which would cause a block). */ LogFullDebug(COMPONENT_STATE, "Reasons to quick exit fso_lock_support=%s fso_lock_support_async_block=%s overlap=%s", fsal_export->exp_ops.fs_supports( fsal_export, fso_lock_support) ? "yes" : "no", fsal_export->exp_ops.fs_supports( fsal_export, fso_lock_support_async_block) ? "yes" : "no", overlap ? "yes" : "no"); if (!fsal_export->exp_ops.fs_supports(fsal_export, fso_lock_support) || (!fsal_export->exp_ops.fs_supports(fsal_export, fso_lock_support_async_block) && lock_op == FSAL_OP_CANCEL)) return STATE_SUCCESS; LogLock(COMPONENT_STATE, NIV_FULL_DEBUG, fsal_lock_op_str(lock_op), obj, owner, lock); memset(&conflicting_lock, 0, sizeof(conflicting_lock)); if (lock_op == FSAL_OP_LOCKB && !fsal_export->exp_ops.fs_supports(fsal_export, fso_lock_support_async_block)) { fsal_lock_op = FSAL_OP_LOCK; } /* Perform this lock operation using the support_ex lock op. */ fsal_status = obj->obj_ops.lock_op2(obj, state, owner, fsal_lock_op, lock, &conflicting_lock); status = state_error_convert(fsal_status); LogFullDebug(COMPONENT_STATE, "FSAL_lock_op returned %s", state_err_str(status)); if (status == STATE_LOCK_BLOCKED && fsal_lock_op != FSAL_OP_LOCKB) { /* This is an unexpected return code, * make sure caller reports an error */ LogMajor(COMPONENT_STATE, "FSAL returned unexpected STATE_LOCK_BLOCKED result"); status = STATE_FSAL_ERROR; } else if (status == STATE_LOCK_CONFLICT && lock_op == FSAL_OP_LOCKB) { /* This must be a non-async blocking lock that was * blocked, where we actually made a non-blocking * call. In that case, actually return * STATE_LOCK_BLOCKED. */ status = STATE_LOCK_BLOCKED; } if (status == STATE_LOCK_CONFLICT) { if (holder != NULL) { *holder = &unknown_owner; inc_state_owner_ref(&unknown_owner); } if (conflict != NULL) *conflict = conflicting_lock; } return status; } /** * @brief Fill out conflict information * * @param[in] found_entry Conflicting lock * @param[out] holder Owner that holds conflicting lock * @param[out] conflict Description of conflicting lock */ void copy_conflict(state_lock_entry_t *found_entry, state_owner_t **holder, fsal_lock_param_t *conflict) { if (found_entry == NULL) return; if (holder != NULL) { *holder = found_entry->sle_owner; inc_state_owner_ref(found_entry->sle_owner); } if (conflict != NULL) *conflict = found_entry->sle_lock; } /****************************************************************************** * * Primary lock interface functions * ******************************************************************************/ /** * @brief Test for lock availability * * This function acquires the state lock on an entry and thus is only * suitable for operations like lockt. If one wishes to use it as * part of a larger lock or state operation one would need to split it * out. * * @param[in] obj File to test * @param[in] state Optional state_t to relate this test to a fsal_fd * @param[in] owner Lock owner making the test * @param[in] lock Lock description * @param[out] holder Owner that holds conflicting lock * @param[out] conflict Description of conflicting lock * * @return State status. */ state_status_t state_test(struct fsal_obj_handle *obj, state_t *state, state_owner_t *owner, fsal_lock_param_t *lock, state_owner_t **holder, fsal_lock_param_t *conflict) { state_lock_entry_t *found_entry; state_status_t status = 0; LogLock(COMPONENT_STATE, NIV_FULL_DEBUG, "TEST", obj, owner, lock); PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); found_entry = get_overlapping_entry(obj->state_hdl, owner, lock); if (found_entry != NULL) { /* found a conflicting lock, return it */ LogEntry("Found conflict", found_entry); copy_conflict(found_entry, holder, conflict); status = STATE_LOCK_CONFLICT; } else { /* Prepare to make call to FSAL for this lock */ status = do_lock_op(obj, state, FSAL_OP_LOCKT, owner, lock, holder, conflict, false); switch (status) { case STATE_SUCCESS: LogFullDebug(COMPONENT_STATE, "Lock success"); break; case STATE_LOCK_CONFLICT: LogLock(COMPONENT_STATE, NIV_FULL_DEBUG, "Conflict from FSAL", obj, *holder, conflict); break; default: LogMajor(COMPONENT_STATE, "Got error from FSAL lock operation, error=%s", state_err_str(status)); break; } } if (isFullDebug(COMPONENT_STATE) && isFullDebug(COMPONENT_MEMLEAKS)) LogList("Lock List", obj, &obj->state_hdl->file.lock_list); PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); return status; } /** * @brief Attempt to acquire a lock * * Must hold the state_lock * * @param[in] obj File to lock * @param[in] owner Lock owner * @param[in] state Associated state for the lock * @param[in] blocking Blocking type * @param[in] block_data Blocking lock data * @param[in] lock Lock description * @param[out] holder Holder of conflicting lock * @param[out] conflict Conflicting lock description * * @return State status. */ state_status_t state_lock(struct fsal_obj_handle *obj, state_owner_t *owner, state_t *state, state_blocking_t blocking, state_block_data_t *block_data, fsal_lock_param_t *lock, state_owner_t **holder, fsal_lock_param_t *conflict) { bool allow = true, overlap = false; struct glist_head *glist; state_lock_entry_t *found_entry; uint64_t found_entry_end; uint64_t range_end = lock_end(lock); struct fsal_export *fsal_export = op_ctx->fsal_export; fsal_lock_op_t lock_op; state_status_t status = 0; bool async; if (blocking != STATE_NON_BLOCKING) { /* First search for a blocked request. Client can ignore the * blocked request and keep sending us new lock request again * and again. So if we have a mapping blocked request return * that */ glist_for_each(glist, &obj->state_hdl->file.lock_list) { found_entry = glist_entry(glist, state_lock_entry_t, sle_list); if (different_owners(found_entry->sle_owner, owner)) continue; /* Need to reject lock request if this lock owner * already has a lock on this file via a different * export. */ if (found_entry->sle_export != op_ctx->ctx_export) { LogEvent(COMPONENT_STATE, "Lock Owner Export Conflict, Lock held for export %d (%s), request for export %d (%s)", found_entry->sle_export->export_id, op_ctx_export_path( found_entry->sle_export), op_ctx->ctx_export->export_id, op_ctx_export_path( op_ctx->ctx_export)); LogEntry( "Found lock entry belonging to another export", found_entry); status = STATE_INVALID_ARGUMENT; return status; } if (found_entry->sle_blocked != blocking) continue; if (different_lock(&found_entry->sle_lock, lock)) continue; /* We have matched all atribute of the existing lock. * Just return with blocked status. Client may be * polling. */ LogEntry("Found blocked", found_entry); status = STATE_LOCK_BLOCKED; return status; } } glist_for_each(glist, &obj->state_hdl->file.lock_list) { found_entry = glist_entry(glist, state_lock_entry_t, sle_list); /* Need to reject lock request if this lock owner already has * a lock on this file via a different export. */ if (found_entry->sle_export != op_ctx->ctx_export && !different_owners(found_entry->sle_owner, owner)) { LogEvent(COMPONENT_STATE, "Lock Owner Export Conflict, Lock held for export %d (%s), request for export %d (%s)", found_entry->sle_export->export_id, op_ctx_export_path(found_entry->sle_export), op_ctx->ctx_export->export_id, op_ctx_export_path(op_ctx->ctx_export)); LogEntry("Found lock entry belonging to another export", found_entry); status = STATE_INVALID_ARGUMENT; return status; } /* Don't skip blocked locks for fairness */ found_entry_end = lock_end(&found_entry->sle_lock); if (!(lock->lock_reclaim) && (found_entry_end >= lock->lock_start) && (found_entry->sle_lock.lock_start <= range_end)) { /* lock overlaps see if we can allow: * allow if neither lock is exclusive or * the owner is the same */ if ((found_entry->sle_lock.lock_type == FSAL_LOCK_W || lock->lock_type == FSAL_LOCK_W) && different_owners(found_entry->sle_owner, owner)) { /* Found a conflicting lock, break out of loop. * Also indicate overlap hint. */ LogEntry("Conflicts with", found_entry); LogList("Locks", obj, &obj->state_hdl->file.lock_list); copy_conflict(found_entry, holder, conflict); allow = false; overlap = true; break; } } if (found_entry_end >= range_end && found_entry->sle_lock.lock_start <= lock->lock_start && found_entry->sle_lock.lock_type == lock->lock_type && (found_entry->sle_blocked == STATE_NON_BLOCKING || found_entry->sle_blocked == STATE_GRANTING)) { /* Found an entry that entirely overlaps the new entry * (and due to the preceding test does not prevent * granting this lock - therefore there can't be any * other locks that would prevent granting this lock */ if (!different_owners(found_entry->sle_owner, owner)) { /* The lock actually has the same owner, we're * done, other than dealing with a lock in * GRANTING state. */ if (found_entry->sle_blocked == STATE_GRANTING) { /* Need to handle completion of granting * of this lock because a GRANT was in * progress. This could be a client * retrying a blocked lock due to * mis-trust of server. If the client * also accepts the GRANT_MSG with a * GRANT_RESP, that will be just fine. */ grant_blocked_lock_immediate( obj->state_hdl, found_entry); } LogEntry("Found existing", found_entry); status = STATE_SUCCESS; return status; } /* Found a compatible lock with a different lock owner * that fully overlaps, set hint. */ LogEntry("Found overlapping", found_entry); overlap = true; } } /* Decide how to proceed */ if (blocking == STATE_NLM_BLOCKING) { /* do_lock_op will handle FSAL_OP_LOCKB for those FSALs that * do not support async blocking locks. It will make a * non-blocking call in that case, and it will return * STATE_LOCK_BLOCKED if the lock was not granted. */ lock_op = FSAL_OP_LOCKB; } else if (allow) { /* No conflict found in Ganesha, and this is not a blocking * request. */ lock_op = FSAL_OP_LOCK; } else { /* Can't do async blocking lock in FSAL and have a conflict. * Return it. */ status = STATE_LOCK_CONFLICT; return status; } /* We have already returned if: * + we have found an identical blocking lock * + we have found an entirely overlapping lock with the same lock owner * + this was not a supported blocking lock and we found a conflict * * So at this point, we are either going to do one of the following (all * descriptions below assume no problems occur): * * (1) FSAL supports async blocking locks, we know there is a conflict, ( and this is a supported blocking lock request * * Make FSAL_OP_LOCKB call anyway, we will rely on FSAL to grant * blocking locks. We will return the conflict we know about rather * than what the FSAL returns. Insert blocking lock into queue. * * (2) FSAL supports async blocking locks, we don't know about any * conflict, and this is a supported blocking lock request * * Make FSAL_OP_LOCKB call, if it indicates block, insert blocking * lock into queue, and return the conflict the FSAL indicates. If * FSAL grants lock, then return granted lock and insert into lock * list, otherwise insert blocking lock into queue. * * (3) FSAL doesn't support async blocking locks, this is a supported * blocking lock and we know there is a conflict * * Insert blocking lock into queue, we will grant lock when * possible. * * (4) FSAL doesn't support async blocking locks and we don't know * about any conflict * * Make FSAL_OP_LOCK call, if it indicates conflict, return that. * Even if this is a supported blocking lock call, there is no way * to block. If lock is granted, return that and insert lock into * list. * * (5) FSAL supports async blocking locks, we don't know about any * conflict, and this is not a supported blocking lock request * * Make FSAL_OP_LOCK call, if it indicates conflict, return that. * If lock is granted, return that and insert lock into list. */ /* Create the new lock entry. * Provisionally mark this lock as granted. */ found_entry = create_state_lock_entry(obj, op_ctx->ctx_export, STATE_NON_BLOCKING, owner, state, lock); /* Check for async blocking lock support. */ async = fsal_export->exp_ops.fs_supports(fsal_export, fso_lock_support_async_block); /* If no conflict in lock list, or FSAL supports async blocking locks, * make FSAL call. Don't ask for conflict if we know about a conflict. */ if (allow || async) { /* Prepare to make call to FSAL for this lock */ status = do_lock_op(obj, state, lock_op, owner, lock, allow ? holder : NULL, allow ? conflict : NULL, overlap); } else { /* FSAL does not support async blocking locks and we have * a blocking lock within Ganesha, no need to make an * FSAL call that will just return denied. */ status = STATE_LOCK_BLOCKED; } if (status == STATE_SUCCESS) { /* Merge any touching or overlapping locks into this one */ LogEntry("FSAL lock acquired, merging locks for", found_entry); if (glist_empty(&obj->state_hdl->file.lock_list)) { /* List was empty, get ref for list. Check before * mergining, because that can remove the last entry * from the list if we are merging with it. */ obj->obj_ops.get_ref(obj); } merge_lock_entry(obj->state_hdl, found_entry); /* Insert entry into lock list */ LogEntry("New lock", found_entry); glist_add_tail(&obj->state_hdl->file.lock_list, &found_entry->sle_list); /* A lock downgrade could unblock blocked locks */ grant_blocked_locks(obj->state_hdl); } else if (status == STATE_LOCK_CONFLICT) { LogEntry("Conflict in FSAL for", found_entry); /* Discard lock entry */ remove_from_locklist(found_entry); } else if (status == STATE_LOCK_BLOCKED) { /* Mark entry as blocking and attach block_data */ found_entry->sle_block_data = block_data; found_entry->sle_blocked = blocking; block_data->sbd_lock_entry = found_entry; if (async) { /* Allow FSAL to signal when lock is granted or * available for retry. */ block_data->sbd_block_type = STATE_BLOCK_ASYNC; } else if (allow) { /* Actively poll for the lock. */ block_data->sbd_block_type = STATE_BLOCK_POLL; } else { /* Ganesha will attempt to grant the lock when * a conflicting lock is released. */ block_data->sbd_block_type = STATE_BLOCK_INTERNAL; } /* Insert entry into lock list */ LogEntry("FSAL block for", found_entry); glist_add_tail(&obj->state_hdl->file.lock_list, &found_entry->sle_list); PTHREAD_MUTEX_lock(&blocked_locks_mutex); glist_add_tail(&state_blocked_locks, &block_data->sbd_list); PTHREAD_MUTEX_unlock(&blocked_locks_mutex); } else { LogMajor(COMPONENT_STATE, "Unable to lock FSAL, error=%s", state_err_str(status)); /* Discard lock entry */ remove_from_locklist(found_entry); } return status; } /** * @brief Release a lock * * @param[in] obj File to unlock * @param[in] state Associated state_t (if any) * @param[in] owner Owner of lock * @param[in] state_applies Indicator if nsm_state is relevant * @param[in] nsm_state NSM state number * @param[in] lock Lock description */ state_status_t state_unlock(struct fsal_obj_handle *obj, state_t *state, state_owner_t *owner, bool state_applies, int32_t nsm_state, fsal_lock_param_t *lock) { bool empty = false; bool removed = false; state_status_t status = 0; if (obj->type != REGULAR_FILE) { LogLock(COMPONENT_STATE, NIV_DEBUG, "Bad Unlock", obj, owner, lock); return STATE_BAD_TYPE; } PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); /* If lock list is empty, there really isn't any work for us to do. */ if (glist_empty(&obj->state_hdl->file.lock_list)) { LogDebug(COMPONENT_STATE, "Unlock success on file with no locks"); status = STATE_SUCCESS; goto out_unlock; } LogFullDebug(COMPONENT_STATE, "----------------------------------------------------------------------"); LogLock(COMPONENT_STATE, NIV_FULL_DEBUG, "Subtracting", obj, owner, lock); LogFullDebug(COMPONENT_STATE, "----------------------------------------------------------------------"); /* First cancel any blocking locks that might * overlap the unlocked range. */ cancel_blocked_locks_range(obj->state_hdl, owner, state_applies, nsm_state, lock); /* Release the lock from cache inode lock list for entry */ status = subtract_lock_from_list(owner, state_applies, nsm_state, lock, &removed, &obj->state_hdl->file.lock_list); /* If the lock list has become zero; decrement the pin ref count pt * placed. Do this here just in case subtract_lock_from_list has made * list empty even if it failed. */ if (glist_empty(&obj->state_hdl->file.lock_list)) obj->obj_ops.put_ref(obj); if (status != STATE_SUCCESS) { /* The unlock has not taken affect (other than canceling any * blocking locks. */ LogMajor(COMPONENT_STATE, "Unable to remove lock from list for unlock, error=%s", state_err_str(status)); goto out_unlock; } /* Unlocking the entire region will remove any FSAL locks we held, * whether from fully granted locks, or from blocking locks that were * in the process of being granted. */ status = do_lock_op(obj, state, FSAL_OP_UNLOCK, owner, lock, NULL, /* no conflict expected */ NULL, false); if (status != STATE_SUCCESS) LogMajor(COMPONENT_STATE, "Unable to unlock FSAL, error=%s", state_err_str(status)); LogFullDebug(COMPONENT_STATE, "----------------------------------------------------------------------"); LogLock(COMPONENT_STATE, NIV_FULL_DEBUG, "Done", obj, owner, lock); LogFullDebug(COMPONENT_STATE, "----------------------------------------------------------------------"); if (isFullDebug(COMPONENT_STATE) && isFullDebug(COMPONENT_MEMLEAKS) && lock->lock_start == 0 && lock->lock_length == 0) empty = LogList("Lock List", obj, &obj->state_hdl->file.lock_list); grant_blocked_locks(obj->state_hdl); if (isFullDebug(COMPONENT_STATE) && isFullDebug(COMPONENT_MEMLEAKS) && lock->lock_start == 0 && lock->lock_length == 0 && empty) dump_all_locks("All locks (after unlock)"); out_unlock: PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); return status; } /** * @brief Cancel a blocking lock * * @param[in] obj File on which to cancel the lock * @param[in] owner Lock owner * @param[in] lock Lock description * * @return State status. */ state_status_t state_cancel(struct fsal_obj_handle *obj, state_owner_t *owner, fsal_lock_param_t *lock) { struct glist_head *glist; state_lock_entry_t *found_entry; if (obj->type != REGULAR_FILE) { LogLock(COMPONENT_STATE, NIV_DEBUG, "Bad Cancel", obj, owner, lock); return STATE_BAD_TYPE; } PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); /* If lock list is empty, there really isn't any work for us to do. */ if (glist_empty(&obj->state_hdl->file.lock_list)) { LogDebug(COMPONENT_STATE, "Cancel success on file with no locks"); goto out_unlock; } glist_for_each(glist, &obj->state_hdl->file.lock_list) { found_entry = glist_entry(glist, state_lock_entry_t, sle_list); if (different_owners(found_entry->sle_owner, owner)) continue; /* Can not cancel a lock once it is granted */ if (found_entry->sle_blocked == STATE_NON_BLOCKING) continue; if (different_lock(&found_entry->sle_lock, lock)) continue; /* Cancel the blocked lock */ cancel_blocked_lock(obj, found_entry); /* Check to see if we can grant any blocked locks. */ grant_blocked_locks(obj->state_hdl); break; } out_unlock: PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); return STATE_SUCCESS; } #ifdef _USE_NLM /** * @brief Handle an SM_NOTIFY from NLM * * Also used to handle NLM_FREE_ALL * * @param[in] nsmclient NSM client data * @param[in] state_applies Indicates if nsm_state is valid * @param[in] nsm_state NSM state value * * @return State status. */ state_status_t state_nlm_notify(state_nsm_client_t *nsmclient, bool state_applies, int32_t nsm_state) { state_owner_t *owner; state_lock_entry_t *found_entry; fsal_lock_param_t lock; struct fsal_obj_handle *obj; int errcnt = 0; struct glist_head newlocks; state_t *found_share; state_status_t status = 0; struct root_op_context root_op_context; struct gsh_export *export; state_t *state; /* Initialize a context */ init_root_op_context(&root_op_context, NULL, NULL, 0, 0, UNKNOWN_REQUEST); if (isFullDebug(COMPONENT_STATE)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_nsm_client(&dspbuf, nsmclient); LogFullDebug(COMPONENT_STATE, "Notify for %s", str); } glist_init(&newlocks); /* First remove byte range locks. * Only accept so many errors before giving up. */ while (errcnt < STATE_ERR_MAX) { PTHREAD_MUTEX_lock(&nsmclient->ssc_mutex); /* We just need to find any file this client has locks on. * We pick the first lock the client holds, and use it's file. */ found_entry = glist_first_entry(&nsmclient->ssc_lock_list, state_lock_entry_t, sle_client_locks); /* If we don't find any entries, then we are done. */ if (found_entry == NULL) { PTHREAD_MUTEX_unlock(&nsmclient->ssc_mutex); break; } /* Remove from the client lock list */ glist_del(&found_entry->sle_client_locks); /* If called as a result of SM_NOTIFY, we don't drop the * locks belonging to the "current" NSM state counter passed * in the SM_NOTIFTY. Otherwise, we drop all locks. * We expect never to get an SM_NOTIFY from a client that uses * non-monitored locks (it will send a FREE_ALL instead (which * will not set the state_applies flag). */ if (state_applies && found_entry->sle_state->state_seqid == nsm_state) { /* This is a new lock acquired since the client * rebooted, retain it. * * Note that although this entry is temporarily * not on the ssc_lock_list, we don't need to hold * an extra refrence to the entry since the list * still effectively protected by the ssc_mutex. * If something happens to remove this entry, it will * actually be removed from the newlocks list, which * in the end is correct. So we don't need to do * anything special. */ /** @todo FSF: vulnerability - if a SECOND SM_NOTIFY * comes in while this one is still being processed * the locks put on the newlocks list would not be * cleaned up. This list REALLY should be attached * to the nsm_client in some way, or we should block * handling of the 2nd SM_NOTIFY until this one is * complete. */ LogEntry("Don't release new lock", found_entry); glist_add_tail(&newlocks, &found_entry->sle_client_locks); PTHREAD_MUTEX_unlock(&nsmclient->ssc_mutex); continue; } LogEntry("Release client locks based on", found_entry); /* Extract the bits from the lock entry that we will need * to proceed with the operation (file, owner, and * export). */ obj = found_entry->sle_obj; owner = found_entry->sle_owner; export = found_entry->sle_export; state = found_entry->sle_state; root_op_context.req_ctx.ctx_export = export; root_op_context.req_ctx.fsal_export = export->fsal_export; /* Get a reference to the export while we still hold the * ssc_mutex. This assures that the export definitely can * not have had it's last refcount released. We will check * later to see if the export is being removed. */ get_gsh_export_ref(export); /* Get a reference to the owner */ inc_state_owner_ref(owner); /* Get a reference to the state_t */ inc_state_t_ref(state); /* Move this entry to the end of the list * (this will help if errors occur) */ glist_add_tail(&nsmclient->ssc_lock_list, &found_entry->sle_client_locks); /* Now we are done with this specific entry, release the mutex. */ PTHREAD_MUTEX_unlock(&nsmclient->ssc_mutex); /* Make lock that covers the whole file. * type doesn't matter for unlock */ lock.lock_type = FSAL_LOCK_R; lock.lock_start = 0; lock.lock_length = 0; if (export_ready(export)) { /* Remove all locks held by this NLM Client on * the file. */ status = state_unlock(obj, state, owner, state_applies, nsm_state, &lock); } else { /* The export is being removed, we didn't bother * calling state_unlock() because export cleanup * will remove all the state. This is assured by * the call to put_gsh_export immediately following. * Pretend succes. */ status = STATE_SUCCESS; } /* Release the refcounts we took above. */ put_gsh_export(export); dec_state_owner_ref(owner); obj->obj_ops.put_ref(obj); if (!state_unlock_err_ok(status)) { /* Increment the error count and try the next lock, * with any luck the memory pressure which is causing * the problem will resolve itself. */ LogFullDebug(COMPONENT_STATE, "state_unlock returned %s", state_err_str(status)); } } /* Now remove NLM_SHARE reservations. * Only accept so many errors before giving up. * We always drop all NLM_SHARE reservations since they are * non-monitored. */ while (errcnt < STATE_ERR_MAX) { PTHREAD_MUTEX_lock(&nsmclient->ssc_mutex); /* We just need to find any file this client has locks on. * We pick the first lock the client holds, and use it's file. */ found_share = glist_first_entry(&nsmclient->ssc_share_list, state_t, state_data.share.share_lockstates); /* If we don't find any entries, then we are done. */ if (found_share == NULL) { PTHREAD_MUTEX_unlock(&nsmclient->ssc_mutex); break; } /* Extract the bits from the lock entry that we will need * to proceed with the operation (object, owner, and * export). */ obj = get_state_obj_ref(found_share); if (obj == NULL) { LogDebug(COMPONENT_STATE, "Entry for state is stale"); PTHREAD_MUTEX_unlock(&nsmclient->ssc_mutex); break; } owner = found_share->state_owner; export = found_share->state_export; root_op_context.req_ctx.ctx_export = export; root_op_context.req_ctx.fsal_export = export->fsal_export; /* Get a reference to the export while we still hold the * ssc_mutex. This assures that the export definitely can * not have had it's last refcount released. We will check * later to see if the export is being removed. */ get_gsh_export_ref(export); /* Get a reference to the owner */ inc_state_owner_ref(owner); /* Get a reference to the state_t */ inc_state_t_ref(found_share); /* Move this entry to the end of the list * (this will help if errors occur) */ glist_add_tail(&nsmclient->ssc_share_list, &found_share->state_data.share.share_lockstates); PTHREAD_MUTEX_unlock(&nsmclient->ssc_mutex); if (export_ready(export)) { /* Remove all shares held by this NSM Client and * Owner on the file (on all exports) */ status = state_nlm_share(obj, OPEN4_SHARE_ACCESS_NONE, OPEN4_SHARE_DENY_NONE, owner, found_share, false, true); } else { /* The export is being removed, we didn't bother * calling state_unlock() because export cleanup * will remove all the state. This is assured by * the call to put_gsh_export immediately following. * Pretend succes. */ status = STATE_SUCCESS; } /* Release the refcounts we took above. */ put_gsh_export(export); dec_state_owner_ref(owner); obj->obj_ops.put_ref(obj); dec_state_t_ref(found_share); if (!state_unlock_err_ok(status)) { /* Increment the error count and try the next share, * with any luck the memory pressure which is causing * the problem will resolve itself. */ LogFullDebug(COMPONENT_STATE, "state_nlm_unshare returned %s", state_err_str(status)); } } if (errcnt == STATE_ERR_MAX) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_nsm_client(&dspbuf, nsmclient); LogFatal(COMPONENT_STATE, "Could not complete NLM notify for %s", str); } /* Put locks from current client incarnation onto end of list. * * Note that it's possible one or more of the locks we originally * put on the newlocks list have been removed. This is actually * just fine. Since everything is protected by the ssc_mutex, * even the fact that the newlocks list head is a stack local * variable is local will actually work just fine. */ PTHREAD_MUTEX_lock(&nsmclient->ssc_mutex); glist_add_list_tail(&nsmclient->ssc_lock_list, &newlocks); PTHREAD_MUTEX_unlock(&nsmclient->ssc_mutex); LogFullDebug(COMPONENT_STATE, "DONE"); release_root_op_context(); return status; } #endif /* _USE_NLM */ /** * @brief Release all locks held by an NFS v4 lock owner * * @param[in] owner Lock owner * */ void state_nfs4_owner_unlock_all(state_owner_t *owner) { fsal_lock_param_t lock; struct fsal_obj_handle *obj; int errcnt = 0; state_status_t status = 0; struct gsh_export *saved_export = op_ctx->ctx_export; struct gsh_export *export; state_t *state; bool ok; /* Only accept so many errors before giving up. */ while (errcnt < STATE_ERR_MAX) { PTHREAD_MUTEX_lock(&owner->so_mutex); /* We just need to find any file this owner has locks on. * We pick the first lock the owner holds, and use it's file. */ state = glist_first_entry( &owner->so_owner.so_nfs4_owner.so_state_list, state_t, state_owner_list); /* If we don't find any entries, then we are done. */ if (state == NULL) { PTHREAD_MUTEX_unlock(&owner->so_mutex); break; } inc_state_t_ref(state); /* Move this state to the end of the list * (this will help if errors occur) */ glist_del(&state->state_owner_list); glist_add_tail(&owner->so_owner.so_nfs4_owner.so_state_list, &state->state_owner_list); /* Get references to the obj and export */ ok = get_state_obj_export_owner_refs(state, &obj, &export, NULL); PTHREAD_MUTEX_unlock(&owner->so_mutex); if (!ok) { /* Entry and/or export is dying, skip this state, * it will be cleaned up soon enough. */ continue; } /* Set up the op_context with the proper export */ op_ctx->ctx_export = export; op_ctx->fsal_export = export->fsal_export; /* Make lock that covers the whole file. * type doesn't matter for unlock */ lock.lock_type = FSAL_LOCK_R; lock.lock_start = 0; lock.lock_length = 0; /* Remove all locks held by this owner on the file */ status = state_unlock(obj, state, owner, false, 0, &lock); if (!state_unlock_err_ok(status)) { /* Increment the error count and try the next lock, * with any luck the memory pressure which is causing * the problem will resolve itself. */ LogCrit(COMPONENT_STATE, "state_unlock failed %s", state_err_str(status)); errcnt++; } else if (status == STATE_SUCCESS) { /* Delete the state_t */ state_del(state); } dec_state_t_ref(state); /* Release the obj ref and export ref. */ obj->obj_ops.put_ref(obj); put_gsh_export(export); } if (errcnt == STATE_ERR_MAX) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_owner(&dspbuf, owner); LogFatal(COMPONENT_STATE, "Could not complete cleanup of lock state for lock owner %s", str); } op_ctx->ctx_export = saved_export; if (saved_export != NULL) op_ctx->fsal_export = op_ctx->ctx_export->fsal_export; } /** * @brief Release all locks held on an export * */ void state_export_unlock_all(void) { state_lock_entry_t *found_entry; fsal_lock_param_t lock; struct fsal_obj_handle *obj; int errcnt = 0; state_status_t status = 0; state_owner_t *owner; state_t *state; /* Only accept so many errors before giving up. */ while (errcnt < STATE_ERR_MAX) { PTHREAD_RWLOCK_wrlock(&op_ctx->ctx_export->lock); /* We just need to find any file this owner has locks on. * We pick the first lock the owner holds, and use it's file. */ found_entry = glist_first_entry( &op_ctx->ctx_export->exp_lock_list, state_lock_entry_t, sle_export_locks); /* If we don't find any entries, then we are done. */ if (found_entry == NULL) { PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock); break; } /* Extract the bits from the lock entry that we will need * to proceed with the operation (obj, owner, and * export). */ obj = found_entry->sle_obj; owner = found_entry->sle_owner; state = found_entry->sle_state; /* take a reference on the state_t */ inc_state_t_ref(state); /* Get a reference to the cache inode while we still hold * the ssc_mutex (since we hold this mutex, any other function * that might be cleaning up this lock CAN NOT have released * the last LRU reference, thus it is safe to grab another. */ obj->obj_ops.get_ref(obj); /* Get a reference to the owner */ inc_state_owner_ref(owner); /* Move this entry to the end of the list * (this will help if errors occur) */ glist_del(&found_entry->sle_export_locks); glist_add_tail(&op_ctx->ctx_export->exp_lock_list, &found_entry->sle_export_locks); /* Now we are done with this specific entry, release the lock. */ PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock); /* Make lock that covers the whole file. * type doesn't matter for unlock */ lock.lock_type = FSAL_LOCK_R; lock.lock_start = 0; lock.lock_length = 0; /* Remove all locks held by this NLM Client on * the file. */ status = state_unlock(obj, state, owner, false, 0, &lock); /* call state_del regardless of status as we're killing * the export */ if (owner->so_type == STATE_LOCK_OWNER_NFSV4) state_del(state); /* Release the refcounts we took above. */ dec_state_t_ref(state); dec_state_owner_ref(owner); obj->obj_ops.put_ref(obj); if (!state_unlock_err_ok(status)) { /* Increment the error count and try the next lock, * with any luck the memory pressure which is causing * the problem will resolve itself. */ LogDebug(COMPONENT_STATE, "state_unlock failed %s", state_err_str(status)); } } if (errcnt == STATE_ERR_MAX) { LogFatal(COMPONENT_STATE, "Could not complete cleanup of locks for %s", op_ctx_export_path(op_ctx->ctx_export)); } } /** * @brief Poll any blocked locks of type STATE_BLOCK_POLL * * @param[in] ctx Fridge Thread Context * */ void blocked_lock_polling(struct fridgethr_context *ctx) { state_lock_entry_t *found_entry; struct glist_head *glist; state_block_data_t *pblock; SetNameFunction("lk_poll"); PTHREAD_MUTEX_lock(&blocked_locks_mutex); if (isFullDebug(COMPONENT_STATE) && isFullDebug(COMPONENT_MEMLEAKS)) LogBlockedList("Blocked Lock List", NULL, &state_blocked_locks); glist_for_each(glist, &state_blocked_locks) { pblock = glist_entry(glist, state_block_data_t, sbd_list); found_entry = pblock->sbd_lock_entry; /* Check if got an entry */ if (found_entry == NULL) continue; /* Check if right type */ if (pblock->sbd_block_type != STATE_BLOCK_POLL) continue; /* Schedule async processing, leave the lock on the blocked * lock list since we might not succeed in granting this lock. */ pblock->sbd_grant_type = STATE_GRANT_POLL; if (state_block_schedule(pblock) != STATE_SUCCESS) { LogMajor(COMPONENT_STATE, "Unable to schedule lock notification."); } LogEntry("Blocked Lock found", found_entry); } /* glist_for_each_safe */ PTHREAD_MUTEX_unlock(&blocked_locks_mutex); } /** * @brief Find a lock and add to grant list * * @param[in] obj File to search * @param[in] owner Lock owner * @param[in] lock Lock description * @param[in] grant_type Grant type */ static void find_blocked_lock_upcall(struct fsal_obj_handle *obj, void *owner, fsal_lock_param_t *lock, state_grant_type_t grant_type) { state_lock_entry_t *found_entry; struct glist_head *glist; state_block_data_t *pblock; PTHREAD_MUTEX_lock(&blocked_locks_mutex); glist_for_each(glist, &state_blocked_locks) { pblock = glist_entry(glist, state_block_data_t, sbd_list); found_entry = pblock->sbd_lock_entry; /* Check if got an entry */ if (found_entry == NULL) continue; /* Check if for same file */ if (found_entry->sle_obj != obj) continue; /* Check if for same owner */ if (found_entry->sle_owner != owner) continue; /* Check if same lock */ if (different_lock(&found_entry->sle_lock, lock)) continue; /* Schedule async processing, leave the lock on the blocked * lock list for now (if we get multiple upcalls because our * async processing is slow for some reason, we don't want to * not find this lock entry and hit the LogFatal below... */ pblock->sbd_grant_type = grant_type; if (state_block_schedule(pblock) != STATE_SUCCESS) { LogMajor(COMPONENT_STATE, "Unable to schedule lock notification."); } LogEntry("Blocked Lock found", found_entry); PTHREAD_MUTEX_unlock(&blocked_locks_mutex); return; } /* glist_for_each_safe */ if (isFullDebug(COMPONENT_STATE) && isFullDebug(COMPONENT_MEMLEAKS)) LogBlockedList("Blocked Lock List", NULL, &state_blocked_locks); PTHREAD_MUTEX_unlock(&blocked_locks_mutex); /* We must be out of sync with FSAL, this is fatal */ LogLockDesc(COMPONENT_STATE, NIV_MAJ, "Blocked Lock Not Found for", obj, owner, lock); LogFatal(COMPONENT_STATE, "Locks out of sync with FSAL"); } /** * @brief Handle upcall for granted lock * * @param[in] obj File on which lock is granted * @param[in] owner Lock owner * @param[in] lock Lock description */ void grant_blocked_lock_upcall(struct fsal_obj_handle *obj, void *owner, fsal_lock_param_t *lock) { LogLockDesc(COMPONENT_STATE, NIV_DEBUG, "Grant Upcall for", obj, owner, lock); find_blocked_lock_upcall(obj, owner, lock, STATE_GRANT_FSAL); } /** * @brief Handle upcall for available lock * * @param[in] obj File on which lock has become available * @param[in] owner Lock owner * @param[in] lock Lock description */ void available_blocked_lock_upcall(struct fsal_obj_handle *obj, void *owner, fsal_lock_param_t *lock) { LogLockDesc(COMPONENT_STATE, NIV_DEBUG, "Available Upcall for", obj, owner, lock); find_blocked_lock_upcall(obj, owner, lock, STATE_GRANT_FSAL_AVAILABLE); } /** * @brief Free all locks on a file * * @param[in] obj File to free * @return true if locks were removed, false if list was empty */ bool state_lock_wipe(struct state_hdl *hstate) { if (glist_empty(&hstate->file.lock_list)) return false; free_list(&hstate->file.lock_list); return true; } void cancel_all_nlm_blocked(void) { state_lock_entry_t *found_entry; state_block_data_t *pblock; struct root_op_context root_op_context; /* Initialize context */ init_root_op_context(&root_op_context, NULL, NULL, 0, 0, NFS_REQUEST); LogDebug(COMPONENT_STATE, "Cancel all blocked locks"); PTHREAD_MUTEX_lock(&blocked_locks_mutex); pblock = glist_first_entry(&state_blocked_locks, state_block_data_t, sbd_list); if (pblock == NULL) { LogFullDebug(COMPONENT_STATE, "No blocked locks"); goto out; } while (pblock != NULL) { found_entry = pblock->sbd_lock_entry; /* Remove lock from blocked list */ glist_del(&pblock->sbd_list); lock_entry_inc_ref(found_entry); PTHREAD_MUTEX_unlock(&blocked_locks_mutex); root_op_context.req_ctx.ctx_export = found_entry->sle_export; root_op_context.req_ctx.fsal_export = root_op_context.req_ctx.ctx_export->fsal_export; get_gsh_export_ref(root_op_context.req_ctx.ctx_export); /** @todo also look at the LRU ref for pentry */ LogEntry("Blocked Lock found", found_entry); cancel_blocked_lock(found_entry->sle_obj, found_entry); gsh_free(pblock->sbd_blocked_cookie); gsh_free(found_entry->sle_block_data); found_entry->sle_block_data = NULL; LogEntry("Canceled Lock", found_entry); put_gsh_export(root_op_context.req_ctx.ctx_export); lock_entry_dec_ref(found_entry); PTHREAD_MUTEX_lock(&blocked_locks_mutex); /* Get next item off list */ pblock = glist_first_entry(&state_blocked_locks, state_block_data_t, sbd_list); } out: PTHREAD_MUTEX_unlock(&blocked_locks_mutex); release_root_op_context(); } /** @} */ nfs-ganesha-2.6.0/src/SAL/state_misc.c000066400000000000000000000746471324272410200174350ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup SAL State abstraction layer * @{ */ /** * @file state_misc.c * @brief Misc exported routines for the state abstraction layer */ #include "config.h" #include #include #include #include #include #include #include #include #include "log.h" #include "hashtable.h" #include "fsal.h" #include "nfs_core.h" #include "sal_functions.h" struct glist_head cached_open_owners = GLIST_HEAD_INIT(cached_open_owners); pthread_mutex_t cached_open_owners_lock = PTHREAD_MUTEX_INITIALIZER; pool_t *state_owner_pool; /*< Pool for NFSv4 files's open owner */ #ifdef DEBUG_SAL struct glist_head state_owners_all = GLIST_HEAD_INIT(state_owners_all); pthread_mutex_t all_state_owners_mutex = PTHREAD_MUTEX_INITIALIZER; #endif /* Error conversion routines */ /** * @brief Get a string from an error code * * @param[in] err Error code * * @return Error string. */ const char *state_err_str(state_status_t err) { switch (err) { case STATE_SUCCESS: return "STATE_SUCCESS"; case STATE_MALLOC_ERROR: return "STATE_MALLOC_ERROR"; case STATE_POOL_MUTEX_INIT_ERROR: return "STATE_POOL_MUTEX_INIT_ERROR"; case STATE_GET_NEW_LRU_ENTRY: return "STATE_GET_NEW_LRU_ENTRY"; case STATE_INIT_ENTRY_FAILED: return "STATE_INIT_ENTRY_FAILED"; case STATE_FSAL_ERROR: return "STATE_FSAL_ERROR"; case STATE_LRU_ERROR: return "STATE_LRU_ERROR"; case STATE_HASH_SET_ERROR: return "STATE_HASH_SET_ERROR"; case STATE_NOT_A_DIRECTORY: return "STATE_NOT_A_DIRECTORY"; case STATE_INCONSISTENT_ENTRY: return "STATE_INCONSISTENT_ENTRY"; case STATE_BAD_TYPE: return "STATE_BAD_TYPE"; case STATE_ENTRY_EXISTS: return "STATE_ENTRY_EXISTS"; case STATE_DIR_NOT_EMPTY: return "STATE_DIR_NOT_EMPTY"; case STATE_NOT_FOUND: return "STATE_NOT_FOUND"; case STATE_INVALID_ARGUMENT: return "STATE_INVALID_ARGUMENT"; case STATE_INSERT_ERROR: return "STATE_INSERT_ERROR"; case STATE_HASH_TABLE_ERROR: return "STATE_HASH_TABLE_ERROR"; case STATE_FSAL_EACCESS: return "STATE_FSAL_EACCESS"; case STATE_IS_A_DIRECTORY: return "STATE_IS_A_DIRECTORY"; case STATE_FSAL_EPERM: return "STATE_FSAL_EPERM"; case STATE_NO_SPACE_LEFT: return "STATE_NO_SPACE_LEFT"; case STATE_READ_ONLY_FS: return "STATE_READ_ONLY_FS"; case STATE_IO_ERROR: return "STATE_IO_ERROR"; case STATE_ESTALE: return "STATE_ESTALE"; case STATE_FSAL_ERR_SEC: return "STATE_FSAL_ERR_SEC"; case STATE_QUOTA_EXCEEDED: return "STATE_QUOTA_EXCEEDED"; case STATE_ASYNC_POST_ERROR: return "STATE_ASYNC_POST_ERROR"; case STATE_NOT_SUPPORTED: return "STATE_NOT_SUPPORTED"; case STATE_STATE_ERROR: return "STATE_STATE_ERROR"; case STATE_FSAL_DELAY: return "STATE_FSAL_DELAY"; case STATE_NAME_TOO_LONG: return "STATE_NAME_TOO_LONG"; case STATE_LOCK_CONFLICT: return "STATE_LOCK_CONFLICT"; case STATE_LOCKED: return "STATE_LOCKED"; case STATE_LOCK_BLOCKED: return "STATE_LOCK_BLOCKED"; case STATE_LOCK_DEADLOCK: return "STATE_LOCK_DEADLOCK"; case STATE_BAD_COOKIE: return "STATE_BAD_COOKIE"; case STATE_FILE_BIG: return "STATE_FILE_BIG"; case STATE_GRACE_PERIOD: return "STATE_GRACE_PERIOD"; case STATE_CACHE_INODE_ERR: return "STATE_CACHE_INODE_ERR"; case STATE_SIGNAL_ERROR: return "STATE_SIGNAL_ERROR"; case STATE_FILE_OPEN: return "STATE_FILE_OPEN"; case STATE_SHARE_DENIED: return "STATE_SHARE_DENIED"; case STATE_MLINK: return "STATE_MLINK"; case STATE_SERVERFAULT: return "STATE_SERVERFAULT"; case STATE_TOOSMALL: return "STATE_TOOSMALL"; case STATE_XDEV: return "STATE_XDEV"; case STATE_IN_GRACE: return "STATE_IN_GRACE"; case STATE_BADHANDLE: return "STATE_BADHANDLE"; case STATE_BAD_RANGE: return "STATE_BAD_RANGE"; } return "unknown"; } /** * @brief converts an FSAL error to the corresponding state error. * * @param[in] fsal_status Fsal error to be converted * * @return State status. */ state_status_t state_error_convert(fsal_status_t fsal_status) { switch (fsal_status.major) { case ERR_FSAL_NO_ERROR: return STATE_SUCCESS; case ERR_FSAL_NOENT: return STATE_NOT_FOUND; case ERR_FSAL_DELAY: case ERR_FSAL_ACCESS: /* EDELAY and EACCESS are documented by fcntl as * indicating lock conflict */ return STATE_LOCK_CONFLICT; case ERR_FSAL_PERM: return STATE_FSAL_EPERM; case ERR_FSAL_NOSPC: return STATE_NO_SPACE_LEFT; case ERR_FSAL_ROFS: return STATE_READ_ONLY_FS; case ERR_FSAL_IO: case ERR_FSAL_NXIO: return STATE_IO_ERROR; case ERR_FSAL_STALE: case ERR_FSAL_FHEXPIRED: return STATE_ESTALE; case ERR_FSAL_INVAL: case ERR_FSAL_OVERFLOW: return STATE_INVALID_ARGUMENT; case ERR_FSAL_SEC: return STATE_FSAL_ERR_SEC; case ERR_FSAL_NOTSUPP: case ERR_FSAL_ATTRNOTSUPP: case ERR_FSAL_UNION_NOTSUPP: return STATE_NOT_SUPPORTED; case ERR_FSAL_NOMEM: return STATE_MALLOC_ERROR; case ERR_FSAL_DEADLOCK: return STATE_LOCK_DEADLOCK; case ERR_FSAL_BADCOOKIE: return STATE_BAD_COOKIE; case ERR_FSAL_NOT_OPENED: LogCrit(COMPONENT_STATE, "Conversion of ERR_FSAL_NOT_OPENED to STATE_FSAL_ERROR"); return STATE_FSAL_ERROR; case ERR_FSAL_SYMLINK: case ERR_FSAL_BADTYPE: return STATE_BAD_TYPE; case ERR_FSAL_ISDIR: return STATE_IS_A_DIRECTORY; case ERR_FSAL_FBIG: return STATE_FILE_BIG; case ERR_FSAL_FILE_OPEN: return STATE_FILE_OPEN; case ERR_FSAL_SHARE_DENIED: return STATE_SHARE_DENIED; case ERR_FSAL_BLOCKED: return STATE_LOCK_BLOCKED; case ERR_FSAL_IN_GRACE: return STATE_IN_GRACE; case ERR_FSAL_BADHANDLE: return STATE_BADHANDLE; case ERR_FSAL_BAD_RANGE: return STATE_BAD_RANGE; case ERR_FSAL_LOCKED: return STATE_LOCKED; case ERR_FSAL_TOOSMALL: return STATE_TOOSMALL; case ERR_FSAL_DQUOT: case ERR_FSAL_NAMETOOLONG: case ERR_FSAL_EXIST: case ERR_FSAL_NOTEMPTY: case ERR_FSAL_NOTDIR: case ERR_FSAL_INTERRUPT: case ERR_FSAL_FAULT: case ERR_FSAL_NOT_INIT: case ERR_FSAL_ALREADY_INIT: case ERR_FSAL_BAD_INIT: case ERR_FSAL_NO_QUOTA: case ERR_FSAL_XDEV: case ERR_FSAL_MLINK: case ERR_FSAL_TIMEOUT: case ERR_FSAL_SERVERFAULT: case ERR_FSAL_NO_DATA: case ERR_FSAL_NO_ACE: case ERR_FSAL_CROSS_JUNCTION: case ERR_FSAL_BADNAME: /* These errors should be handled inside state * (or should never be seen by state) */ LogDebug(COMPONENT_STATE, "Conversion of FSAL error %d,%d to STATE_FSAL_ERROR", fsal_status.major, fsal_status.minor); return STATE_FSAL_ERROR; } /* We should never reach this line, this may produce a warning with * certain compiler */ LogCrit(COMPONENT_STATE, "Default conversion to STATE_FSAL_ERROR for error %d, line %u should never be reached", fsal_status.major, __LINE__); return STATE_FSAL_ERROR; } /** * @brief Converts a state status to a nfsv4 status * * @param[in] error Input state error * * @return the converted NFSv4 status. * */ nfsstat4 nfs4_Errno_state(state_status_t error) { nfsstat4 nfserror = NFS4ERR_INVAL; switch (error) { case STATE_SUCCESS: nfserror = NFS4_OK; break; case STATE_MALLOC_ERROR: nfserror = NFS4ERR_RESOURCE; break; case STATE_POOL_MUTEX_INIT_ERROR: case STATE_GET_NEW_LRU_ENTRY: case STATE_INIT_ENTRY_FAILED: nfserror = NFS4ERR_SERVERFAULT; break; case STATE_BAD_TYPE: nfserror = NFS4ERR_INVAL; break; case STATE_NOT_A_DIRECTORY: nfserror = NFS4ERR_NOTDIR; break; case STATE_ENTRY_EXISTS: nfserror = NFS4ERR_EXIST; break; case STATE_DIR_NOT_EMPTY: nfserror = NFS4ERR_NOTEMPTY; break; case STATE_NOT_FOUND: nfserror = NFS4ERR_NOENT; break; case STATE_FSAL_ERROR: case STATE_INSERT_ERROR: case STATE_LRU_ERROR: case STATE_HASH_SET_ERROR: nfserror = NFS4ERR_IO; break; case STATE_FSAL_EACCESS: nfserror = NFS4ERR_ACCESS; break; case STATE_FSAL_EPERM: case STATE_FSAL_ERR_SEC: nfserror = NFS4ERR_PERM; break; case STATE_NO_SPACE_LEFT: nfserror = NFS4ERR_NOSPC; break; case STATE_IS_A_DIRECTORY: nfserror = NFS4ERR_ISDIR; break; case STATE_READ_ONLY_FS: nfserror = NFS4ERR_ROFS; break; case STATE_IO_ERROR: nfserror = NFS4ERR_IO; break; case STATE_FILE_OPEN: nfserror = NFS4ERR_FILE_OPEN; break; case STATE_NAME_TOO_LONG: nfserror = NFS4ERR_NAMETOOLONG; break; case STATE_ESTALE: nfserror = NFS4ERR_STALE; break; case STATE_SHARE_DENIED: nfserror = NFS4ERR_SHARE_DENIED; break; case STATE_LOCKED: nfserror = NFS4ERR_LOCKED; break; case STATE_QUOTA_EXCEEDED: nfserror = NFS4ERR_DQUOT; break; case STATE_NOT_SUPPORTED: nfserror = NFS4ERR_NOTSUPP; break; case STATE_FSAL_DELAY: nfserror = NFS4ERR_DELAY; break; case STATE_FILE_BIG: nfserror = NFS4ERR_FBIG; break; case STATE_LOCK_DEADLOCK: nfserror = NFS4ERR_DEADLOCK; break; case STATE_LOCK_BLOCKED: case STATE_LOCK_CONFLICT: nfserror = NFS4ERR_DENIED; break; case STATE_STATE_ERROR: nfserror = NFS4ERR_BAD_STATEID; break; case STATE_BAD_COOKIE: nfserror = NFS4ERR_BAD_COOKIE; break; case STATE_GRACE_PERIOD: nfserror = NFS4ERR_GRACE; break; case STATE_SERVERFAULT: nfserror = NFS4ERR_SERVERFAULT; break; case STATE_MLINK: nfserror = NFS4ERR_MLINK; break; case STATE_TOOSMALL: nfserror = NFS4ERR_TOOSMALL; break; case STATE_IN_GRACE: nfserror = NFS4ERR_GRACE; break; case STATE_XDEV: nfserror = NFS4ERR_XDEV; break; case STATE_BADHANDLE: nfserror = NFS4ERR_BADHANDLE; break; case STATE_BAD_RANGE: nfserror = NFS4ERR_BAD_RANGE; break; case STATE_INVALID_ARGUMENT: case STATE_CACHE_INODE_ERR: case STATE_INCONSISTENT_ENTRY: case STATE_HASH_TABLE_ERROR: case STATE_ASYNC_POST_ERROR: case STATE_SIGNAL_ERROR: /* Should not occur */ nfserror = NFS4ERR_INVAL; break; } return nfserror; } /** * @brief Converts a state status to an NFSv3 status * * @param[in] error State error * * @return Converted NFSv3 status. */ nfsstat3 nfs3_Errno_state(state_status_t error) { nfsstat3 nfserror = NFS3ERR_INVAL; switch (error) { case STATE_SUCCESS: nfserror = NFS3_OK; break; case STATE_MALLOC_ERROR: case STATE_POOL_MUTEX_INIT_ERROR: case STATE_GET_NEW_LRU_ENTRY: case STATE_INIT_ENTRY_FAILED: case STATE_INSERT_ERROR: case STATE_LRU_ERROR: case STATE_HASH_SET_ERROR: case STATE_FILE_OPEN: LogCrit(COMPONENT_NFSPROTO, "Error %u converted to NFS3ERR_IO but was set non-retryable", error); nfserror = NFS3ERR_IO; break; case STATE_INVALID_ARGUMENT: nfserror = NFS3ERR_INVAL; break; case STATE_FSAL_ERROR: /** @todo: Check if this works by making stress tests */ LogCrit(COMPONENT_NFSPROTO, "Error STATE_FSAL_ERROR converted to NFS3ERR_IO but was set non-retryable"); nfserror = NFS3ERR_IO; break; case STATE_NOT_A_DIRECTORY: nfserror = NFS3ERR_NOTDIR; break; case STATE_ENTRY_EXISTS: nfserror = NFS3ERR_EXIST; break; case STATE_DIR_NOT_EMPTY: nfserror = NFS3ERR_NOTEMPTY; break; case STATE_NOT_FOUND: nfserror = NFS3ERR_NOENT; break; case STATE_FSAL_EACCESS: nfserror = NFS3ERR_ACCES; break; case STATE_LOCKED: case STATE_FSAL_EPERM: case STATE_FSAL_ERR_SEC: nfserror = NFS3ERR_PERM; break; case STATE_NO_SPACE_LEFT: nfserror = NFS3ERR_NOSPC; break; case STATE_IS_A_DIRECTORY: nfserror = NFS3ERR_ISDIR; break; case STATE_READ_ONLY_FS: nfserror = NFS3ERR_ROFS; break; case STATE_ESTALE: nfserror = NFS3ERR_STALE; break; case STATE_QUOTA_EXCEEDED: nfserror = NFS3ERR_DQUOT; break; case STATE_BAD_TYPE: nfserror = NFS3ERR_BADTYPE; break; case STATE_NOT_SUPPORTED: nfserror = NFS3ERR_NOTSUPP; break; case STATE_FSAL_DELAY: case STATE_SHARE_DENIED: nfserror = NFS3ERR_JUKEBOX; break; case STATE_IO_ERROR: LogCrit(COMPONENT_NFSPROTO, "Error STATE_IO_ERROR converted to NFS3ERR_IO but was set non-retryable"); nfserror = NFS3ERR_IO; break; case STATE_NAME_TOO_LONG: nfserror = NFS3ERR_NAMETOOLONG; break; case STATE_FILE_BIG: nfserror = NFS3ERR_FBIG; break; case STATE_BAD_COOKIE: nfserror = NFS3ERR_BAD_COOKIE; break; case STATE_MLINK: nfserror = NFS3ERR_MLINK; break; case STATE_SERVERFAULT: nfserror = NFS3ERR_SERVERFAULT; break; case STATE_TOOSMALL: nfserror = NFS3ERR_TOOSMALL; break; case STATE_XDEV: nfserror = NFS3ERR_XDEV; break; case STATE_IN_GRACE: nfserror = NFS3ERR_JUKEBOX; break; case STATE_BADHANDLE: nfserror = NFS3ERR_BADHANDLE; break; case STATE_CACHE_INODE_ERR: case STATE_INCONSISTENT_ENTRY: case STATE_HASH_TABLE_ERROR: case STATE_ASYNC_POST_ERROR: case STATE_STATE_ERROR: case STATE_LOCK_CONFLICT: case STATE_LOCK_BLOCKED: case STATE_LOCK_DEADLOCK: case STATE_GRACE_PERIOD: case STATE_SIGNAL_ERROR: case STATE_BAD_RANGE: /* Should not occur */ LogCrit(COMPONENT_NFSPROTO, "Unexpected status for conversion = %s", state_err_str(error)); nfserror = NFS3ERR_INVAL; break; } return nfserror; } bool state_unlock_err_ok(state_status_t status) { return status == STATE_SUCCESS || status == STATE_ESTALE; } /** String for undefined state owner types */ const char *invalid_state_owner_type = "INVALID STATE OWNER TYPE"; /** * @brief Return a string describing a state owner type * * @param[in] type The state owner type * * @return The string representation of the type. */ const char *state_owner_type_to_str(state_owner_type_t type) { switch (type) { case STATE_LOCK_OWNER_UNKNOWN: return "STATE_LOCK_OWNER_UNKNOWN"; #ifdef _USE_NLM case STATE_LOCK_OWNER_NLM: return "STATE_LOCK_OWNER_NLM"; #endif /* _USE_NLM */ #ifdef _USE_9P case STATE_LOCK_OWNER_9P: return "STALE_LOCK_OWNER_9P"; #endif case STATE_OPEN_OWNER_NFSV4: return "STATE_OPEN_OWNER_NFSV4"; case STATE_LOCK_OWNER_NFSV4: return "STATE_LOCK_OWNER_NFSV4"; case STATE_CLIENTID_OWNER_NFSV4: return "STATE_CLIENTID_OWNER_NFSV4"; } return invalid_state_owner_type; } /** * @brief See if two owners differ * * @param[in] owner1 An owner * @param[in] owner2 Another owner * * @retval true if owners differ. * @retval false if owners are the same. */ bool different_owners(state_owner_t *owner1, state_owner_t *owner2) { if (owner1 == NULL || owner2 == NULL) return true; /* Shortcut if we actually are pointing to the same owner structure */ if (owner1 == owner2) return false; if (owner1->so_type != owner2->so_type) return true; switch (owner1->so_type) { #ifdef _USE_NLM case STATE_LOCK_OWNER_NLM: return compare_nlm_owner(owner1, owner2); #endif /* _USE_NLM */ #ifdef _USE_9P case STATE_LOCK_OWNER_9P: return compare_9p_owner(owner1, owner2); #endif case STATE_OPEN_OWNER_NFSV4: case STATE_LOCK_OWNER_NFSV4: case STATE_CLIENTID_OWNER_NFSV4: return compare_nfs4_owner(owner1, owner2); case STATE_LOCK_OWNER_UNKNOWN: break; } return true; } /** * @brief Display a state owner * * @param[in/out] dspbuf display_buffer describing output string * @param[in] owner Owner to display * * @return the bytes remaining in the buffer. */ int display_owner(struct display_buffer *dspbuf, state_owner_t *owner) { if (owner == NULL) return display_printf(dspbuf, ""); switch (owner->so_type) { #ifdef _USE_NLM case STATE_LOCK_OWNER_NLM: return display_nlm_owner(dspbuf, owner); #endif /* _USE_NLM */ #ifdef _USE_9P case STATE_LOCK_OWNER_9P: return display_9p_owner(dspbuf, owner); #endif case STATE_OPEN_OWNER_NFSV4: case STATE_LOCK_OWNER_NFSV4: case STATE_CLIENTID_OWNER_NFSV4: return display_nfs4_owner(dspbuf, owner); case STATE_LOCK_OWNER_UNKNOWN: return display_printf(dspbuf, "%s powner=%p: refcount=%d", state_owner_type_to_str(owner->so_type), owner, atomic_fetch_int32_t(&owner->so_refcount)); } return display_printf(dspbuf, "%s powner=%p", invalid_state_owner_type, owner); } /** * @brief Acquire a reference on a state owner * * @param[in] owner Owner to acquire */ void inc_state_owner_ref(state_owner_t *owner) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; bool str_valid = false; int32_t refcount; if (isFullDebug(COMPONENT_STATE)) { display_owner(&dspbuf, owner); str_valid = true; } refcount = atomic_inc_int32_t(&owner->so_refcount); if (str_valid) LogFullDebug(COMPONENT_STATE, "Increment refcount now=%" PRId32 " {%s}", refcount, str); } /** * @brief Free a state owner object * * @param[in] owner Owner to free */ void free_state_owner(state_owner_t *owner) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; switch (owner->so_type) { #ifdef _USE_NLM case STATE_LOCK_OWNER_NLM: free_nlm_owner(owner); break; #endif /* _USE_NLM */ #ifdef _USE_9P case STATE_LOCK_OWNER_9P: break; #endif case STATE_OPEN_OWNER_NFSV4: case STATE_LOCK_OWNER_NFSV4: case STATE_CLIENTID_OWNER_NFSV4: free_nfs4_owner(owner); break; case STATE_LOCK_OWNER_UNKNOWN: display_owner(&dspbuf, owner); LogCrit(COMPONENT_STATE, "Unexpected removal of {%s}", str); return; } gsh_free(owner->so_owner_val); PTHREAD_MUTEX_destroy(&owner->so_mutex); #ifdef DEBUG_SAL PTHREAD_MUTEX_lock(&all_state_owners_mutex); glist_del(&owner->so_all_owners); PTHREAD_MUTEX_unlock(&all_state_owners_mutex); #endif pool_free(state_owner_pool, owner); } /** * @brief Get the hash table associated with a state owner object * * @param[in] owner Owner to get associated hash table for */ hash_table_t *get_state_owner_hash_table(state_owner_t *owner) { switch (owner->so_type) { #ifdef _USE_NLM case STATE_LOCK_OWNER_NLM: return ht_nlm_owner; #endif /* _USE_NLM */ #ifdef _USE_9P case STATE_LOCK_OWNER_9P: return ht_9p_owner; #endif case STATE_OPEN_OWNER_NFSV4: case STATE_LOCK_OWNER_NFSV4: case STATE_CLIENTID_OWNER_NFSV4: return ht_nfs4_owner; case STATE_LOCK_OWNER_UNKNOWN: break; } return NULL; } /** * @brief Relinquish a reference on a state owner * * @param[in] owner Owner to release */ void dec_state_owner_ref(state_owner_t *owner) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; bool str_valid = false; struct hash_latch latch; hash_error_t rc; struct gsh_buffdesc buffkey; struct gsh_buffdesc old_value; int32_t refcount; hash_table_t *ht_owner; if (isDebug(COMPONENT_STATE)) { display_owner(&dspbuf, owner); str_valid = true; } refcount = atomic_dec_int32_t(&owner->so_refcount); if (refcount != 0) { if (str_valid) LogFullDebug(COMPONENT_STATE, "Decrement refcount now=%" PRId32 " {%s}", refcount, str); assert(refcount > 0); return; } ht_owner = get_state_owner_hash_table(owner); if (ht_owner == NULL) { if (!str_valid) display_printf(&dspbuf, "Invalid owner %p", owner); LogCrit(COMPONENT_STATE, "Unexpected owner {%s}, type {%d}", str, owner->so_type); return; } buffkey.addr = owner; buffkey.len = sizeof(*owner); /* Since the refcount is zero, another thread that needs this * owner might have deleted ours, so expect not to find one or * find someone else's owner! */ rc = hashtable_getlatch(ht_owner, &buffkey, &old_value, true, &latch); switch (rc) { case HASHTABLE_SUCCESS: /* If ours, delete from hash table */ if (old_value.addr == owner) { hashtable_deletelatched(ht_owner, &buffkey, &latch, NULL, NULL); } break; case HASHTABLE_ERROR_NO_SUCH_KEY: break; default: if (!str_valid) display_printf(&dspbuf, "Invalid owner %p", owner); LogCrit(COMPONENT_STATE, "Error %s, could not find {%s}", hash_table_err_to_str(rc), str); return; } /* Release the latch */ hashtable_releaselatched(ht_owner, &latch); if (str_valid) LogFullDebug(COMPONENT_STATE, "Free {%s}", str); free_state_owner(owner); } /** @brief Remove an NFS 4 open owner from the cached owners list. * * The caller MUST hold the cached_open_owners_lock, also must NOT hold * so_mutex as the so_mutex may get destroyed after this call. * * If this owner is being revived, the refcount should have already been * incremented for the new primary reference. This function will release the * refcount that held it in the cache. * * @param[in] nfs4_owner The owner to release. * */ void uncache_nfs4_owner(struct state_nfs4_owner_t *nfs4_owner) { state_owner_t *owner = container_of(nfs4_owner, state_owner_t, so_owner.so_nfs4_owner); /* This owner is to be removed from the open owner cache: * 1. Remove it from the list. * 2. Make sure this is now a proper list head again. * 3. Indicate it is no longer cached. * 4. Release the reference held on behalf of the cache. */ if (isFullDebug(COMPONENT_STATE)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_owner(&dspbuf, owner); LogFullDebug(COMPONENT_STATE, "Uncache {%s}", str); } /* Remove owner from cached_open_owners */ glist_del(&nfs4_owner->so_cache_entry); atomic_store_time_t(&nfs4_owner->so_cache_expire, 0); dec_state_owner_ref(owner); } static inline void refresh_nfs4_open_owner(struct state_nfs4_owner_t *nfs4_owner) { time_t cache_expire; /* Since this owner is active, reset cache_expire. */ cache_expire = atomic_fetch_time_t(&nfs4_owner->so_cache_expire); if (cache_expire != 0) { PTHREAD_MUTEX_lock(&cached_open_owners_lock); /* Check again while holding the mutex. */ if (atomic_fetch_time_t(&nfs4_owner->so_cache_expire) != 0) { /* This is a cached open owner, uncache it for use. */ uncache_nfs4_owner(nfs4_owner); } PTHREAD_MUTEX_unlock(&cached_open_owners_lock); } } /** * @brief Get a state owner * * @param[in] care indicates how we care about the owner * @param[in] key the owner key we are searching for * @param[in] init_owner routine to initialize a new owner * @param[in,out] isnew pointer to flag indicating a new owner was created * * @return the owner found or NULL if no owner was found or created */ state_owner_t *get_state_owner(care_t care, state_owner_t *key, state_owner_init_t init_owner, bool_t *isnew) { state_owner_t *owner; char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; bool str_valid = false; struct hash_latch latch; hash_error_t rc; struct gsh_buffdesc buffkey; struct gsh_buffdesc buffval; hash_table_t *ht_owner; int32_t refcount; if (isnew != NULL) *isnew = false; if (isFullDebug(COMPONENT_STATE)) { display_owner(&dspbuf, key); LogFullDebug(COMPONENT_STATE, "Find {%s}", str); str_valid = true; } ht_owner = get_state_owner_hash_table(key); if (ht_owner == NULL) { if (!str_valid) display_owner(&dspbuf, key); LogCrit(COMPONENT_STATE, "ht=%p Unexpected key {%s}", ht_owner, str); return NULL; } buffkey.addr = key; buffkey.len = sizeof(*key); rc = hashtable_getlatch(ht_owner, &buffkey, &buffval, true, &latch); switch (rc) { case HASHTABLE_SUCCESS: owner = buffval.addr; refcount = atomic_inc_int32_t(&owner->so_refcount); if (refcount == 1) { /* This owner is in the process of getting freed. * Delete from the hash table and pretend as * though we didn't find it! */ (void)atomic_dec_int32_t(&owner->so_refcount); hashtable_deletelatched(ht_owner, &buffkey, &latch, NULL, NULL); goto not_found; } /* Return the found NSM Client */ if (isFullDebug(COMPONENT_STATE)) { display_owner(&dspbuf, owner); str_valid = true; } hashtable_releaselatched(ht_owner, &latch); /* Refresh an nfs4 open owner if needed. */ if (owner->so_type == STATE_OPEN_OWNER_NFSV4) { refresh_nfs4_open_owner(&owner->so_owner.so_nfs4_owner); } if (isFullDebug(COMPONENT_STATE)) { if (!str_valid) display_owner(&dspbuf, owner); LogFullDebug(COMPONENT_STATE, "Found {%s} refcount now=%" PRId32, str, refcount); } return owner; case HASHTABLE_ERROR_NO_SUCH_KEY: goto not_found; default: if (!str_valid) display_owner(&dspbuf, key); LogCrit(COMPONENT_STATE, "Error %s, could not find {%s}", hash_table_err_to_str(rc), str); return NULL; } not_found: /* Not found, but we don't care, return NULL */ if (care == CARE_NOT) { if (str_valid) LogFullDebug(COMPONENT_STATE, "Ignoring {%s}", str); hashtable_releaselatched(ht_owner, &latch); return NULL; } owner = pool_alloc(state_owner_pool); /* Copy everything over */ memcpy(owner, key, sizeof(*key)); PTHREAD_MUTEX_init(&owner->so_mutex, NULL); #ifdef DEBUG_SAL PTHREAD_MUTEX_lock(&all_state_owners_mutex); glist_add_tail(&state_owners_all, &owner->so_all_owners); PTHREAD_MUTEX_unlock(&all_state_owners_mutex); #endif /* Do any owner type specific initialization */ if (init_owner != NULL) init_owner(owner); if (key->so_owner_len != 0) { owner->so_owner_val = gsh_malloc(key->so_owner_len); memcpy(owner->so_owner_val, key->so_owner_val, key->so_owner_len); } glist_init(&owner->so_lock_list); owner->so_refcount = 1; if (isFullDebug(COMPONENT_STATE)) { display_reset_buffer(&dspbuf); display_owner(&dspbuf, owner); LogFullDebug(COMPONENT_STATE, "New {%s}", str); str_valid = true; } else { /* If we had the key, we don't want it anymore */ str_valid = false; } buffkey.addr = owner; buffkey.len = sizeof(*owner); buffval.addr = owner; buffval.len = sizeof(*owner); rc = hashtable_setlatched(ht_owner, &buffval, &buffval, &latch, false, NULL, NULL); /* An error occurred, return NULL */ if (rc != HASHTABLE_SUCCESS) { if (!str_valid) display_owner(&dspbuf, owner); LogCrit(COMPONENT_STATE, "Error %s, inserting {%s}", hash_table_err_to_str(rc), str); free_state_owner(owner); return NULL; } if (isnew != NULL) *isnew = true; return owner; } /** * @brief Acquire a reference on a state owner that might be getting freed. * * @param[in] owner Owner to acquire * * inc_state_owner_ref() can be called to hold a reference provided * someone already has a valid refcount. In other words, one can't call * inc_state_owner_ref on a owner whose refcount has possibly gone to * zero. * * If we don't know for sure if the owner refcount is positive, this * function must be called to hold a reference. This function places a * reference if the owner has a positive refcount already. * * @return True if we are able to hold a reference, False otherwise. */ bool hold_state_owner(state_owner_t *owner) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; bool str_valid = false; hash_table_t *ht_owner; struct gsh_buffdesc buffkey; struct hash_latch latch; hash_error_t rc; int32_t refcount; /* We need to increment and possibly decrement the refcount * atomically. This "increment" to check for the owner refcount * condition is also done in get_state_owner. Those are done * under the hash lock, so we do the same here. */ ht_owner = get_state_owner_hash_table(owner); if (ht_owner == NULL) { if (!str_valid) display_owner(&dspbuf, owner); LogCrit(COMPONENT_STATE, "ht=%p Unexpected key {%s}", ht_owner, str); return false; } buffkey.addr = owner; buffkey.len = sizeof(*owner); /* We don't care if this owner is in the hashtable or not. We * just need the partition lock in exclusive mode! */ rc = hashtable_acquire_latch(ht_owner, &buffkey, &latch); switch (rc) { case HASHTABLE_SUCCESS: refcount = atomic_inc_int32_t(&owner->so_refcount); if (refcount == 1) { /* This owner is in the process of getting freed */ (void)atomic_dec_int32_t(&owner->so_refcount); hashtable_releaselatched(ht_owner, &latch); return false; } hashtable_releaselatched(ht_owner, &latch); return true; default: assert(0); return false; } } /** * @brief Release all state on a file * * This function may not be called in any context which could hold * entry->state_lock. It will now be reliably called in cleanup * processing. * * @param[in,out] obj File to be wiped */ void state_wipe_file(struct fsal_obj_handle *obj) { bool release; /* * currently, only REGULAR files can have state; byte range locks and * stateid (for v4). In the future, 4.1, directories could have * delegations, which is state. At that point, we may need to modify * this routine to clear state on directories. */ if (obj->type != REGULAR_FILE) return; PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); release = state_lock_wipe(obj->state_hdl); #ifdef _USE_NLM state_share_wipe(obj->state_hdl); #endif /* _USE_NLM */ state_nfs4_state_wipe(obj->state_hdl); PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); #ifdef DEBUG_SAL dump_all_states(); #endif if (release) { /* Drop the ref for the lock_list */ obj->obj_ops.put_ref(obj); } } #ifdef DEBUG_SAL void dump_all_owners(void) { if (!isFullDebug(COMPONENT_STATE)) return; PTHREAD_MUTEX_lock(&all_state_owners_mutex); if (!glist_empty(&state_owners_all)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; struct glist_head *glist; LogFullDebug(COMPONENT_STATE, " ---------------------- State Owner List ----------------------"); glist_for_each(glist, &state_owners_all) { display_reset_buffer(&dspbuf); display_owner(&dspbuf, glist_entry(glist, state_owner_t, so_all_owners)); LogFullDebug(COMPONENT_STATE, "{%s}", str); } LogFullDebug(COMPONENT_STATE, " ----------------------"); } else LogFullDebug(COMPONENT_STATE, "All state owners released"); PTHREAD_MUTEX_unlock(&all_state_owners_mutex); } #endif /** * @brief Release all the state belonging to an export. * * @param[in] exp The export to release state for. * */ void state_release_export(struct gsh_export *export) { struct root_op_context root_op_context; /* Initialize req_ctx */ init_root_op_context(&root_op_context, export, export->fsal_export, 0, 0, UNKNOWN_REQUEST); state_export_unlock_all(); state_export_release_nfs4_state(); #ifdef _USE_NLM state_export_unshare_all(); #endif /* _USE_NLM */ release_root_op_context(); } /** @} */ nfs-ganesha-2.6.0/src/SAL/state_share.c000066400000000000000000000201731324272410200175650ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup SAL State abstraction layer * @{ */ /** * @file state_share.c * @brief Share reservation management */ #include "config.h" #include #include #include #include #include #include #include #include "fsal.h" #include "nfs_core.h" #include "nfs4.h" #include "sal_functions.h" /*#include "nlm_util.h"*/ #include "export_mgr.h" #ifdef _USE_NLM /** * @brief Remove an NLM share * * @param[in] state The state_t describing the share to remove * */ void remove_nlm_share(state_t *state) { state_owner_t *owner = state->state_owner; state_nlm_client_t *client = owner->so_owner.so_nlm_owner.so_client; /* Remove from share list for export */ PTHREAD_RWLOCK_wrlock(&op_ctx->ctx_export->lock); glist_del(&state->state_export_list); PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock); /* Remove the share from the list for the file. */ glist_del(&state->state_list); /* Remove the share from the NSM Client list */ PTHREAD_MUTEX_lock(&client->slc_nsm_client->ssc_mutex); glist_del(&state->state_data.nlm_share.share_perclient); PTHREAD_MUTEX_unlock(&client->slc_nsm_client->ssc_mutex); dec_nsm_client_ref(client->slc_nsm_client); /* Remove the share from the NLM Owner list */ PTHREAD_MUTEX_lock(&owner->so_mutex); glist_del(&state->state_owner_list); PTHREAD_MUTEX_unlock(&owner->so_mutex); /* Release the state_t reference for active share. If extended FSAL * operations are supported, this will close the file when the last * reference is released. */ dec_state_t_ref(state); } /** * @brief Implement NLM share call with FSAL extended ops * * @param[in,out] obj File on which to operate * @param[in] export Export through which file is accessed * @param[in] share_access Share mode requested * @param[in] share_deny Deny mode requested * @param[in] owner Share owner * @param[in] state state_t to manage the share * @param[in] reclaim Indicates if this is a reclaim * @param[in] unshare Indicates if this was an unshare * * @return State status. */ state_status_t state_nlm_share(struct fsal_obj_handle *obj, int share_access, int share_deny, state_owner_t *owner, state_t *state, bool reclaim, bool unshare) { fsal_status_t fsal_status = {0, 0}; fsal_openflags_t openflags = 0; state_nlm_client_t *client = owner->so_owner.so_nlm_owner.so_client; if (unshare) { unsigned int old_access; unsigned int old_deny; old_access = state->state_data.nlm_share.share_access; old_deny = state->state_data.nlm_share.share_deny; /* Remove share_access from old_access */ share_access = old_access - (old_access & share_access); /* Remove share_deny from old_deny */ share_deny = old_deny - (old_deny & share_deny); } /* Assume new access/deny is update in determining the openflags. */ if ((share_access & fsa_R) != 0) openflags |= FSAL_O_READ; if ((share_access & fsa_W) != 0) openflags |= FSAL_O_WRITE; if (openflags == FSAL_O_CLOSED) { /* This share or unshare is removing the final share. The file * will be closed when the final reference to the state is * released. */ remove_nlm_share(state); goto out_unlock; } if ((share_deny & fsm_DR) != 0) openflags |= FSAL_O_DENY_READ; if ((share_deny & fsm_DW) != 0) openflags |= FSAL_O_DENY_WRITE; if (reclaim) openflags |= FSAL_O_RECLAIM; /* Use reopen2 to open or re-open the file and check for share * conflict. */ fsal_status = fsal_reopen2(obj, state, openflags, true); if (FSAL_IS_ERROR(fsal_status)) { LogDebug(COMPONENT_STATE, "fsal_reopen2 failed with %s", fsal_err_txt(fsal_status)); goto out_unlock; } /* Add share to list for NLM Owner */ PTHREAD_MUTEX_lock(&owner->so_mutex); glist_add_tail(&owner->so_owner.so_nlm_owner.so_nlm_shares, &state->state_owner_list); PTHREAD_MUTEX_unlock(&owner->so_mutex); /* Add share to list for NSM Client */ inc_nsm_client_ref(client->slc_nsm_client); PTHREAD_MUTEX_lock(&client->slc_nsm_client->ssc_mutex); glist_add_tail(&client->slc_nsm_client->ssc_share_list, &state->state_data.nlm_share.share_perclient); PTHREAD_MUTEX_unlock(&client->slc_nsm_client->ssc_mutex); /* Add share to list for file. */ glist_add_tail(&obj->state_hdl->file.nlm_share_list, &state->state_list); /* Add to share list for export */ PTHREAD_RWLOCK_wrlock(&op_ctx->ctx_export->lock); glist_add_tail(&op_ctx->ctx_export->exp_nlm_share_list, &state->state_export_list); PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock); /* If we had never had a share, take a reference on the state_t * to retain it. */ if (state->state_data.nlm_share.share_access != OPEN4_SHARE_ACCESS_NONE) inc_state_t_ref(state); /* Update the current share type */ state->state_data.nlm_share.share_access = share_access; state->state_data.nlm_share.share_deny = share_deny; LogFullDebug(COMPONENT_STATE, "added share_access %u, share_deny %u", share_access, share_deny); out_unlock: return state_error_convert(fsal_status); } /** * @brief Remove all share state from a file * * @param[in] obj File to wipe */ void state_share_wipe(struct state_hdl *hstate) { state_t *state; struct glist_head *glist; struct glist_head *glistn; glist_for_each_safe(glist, glistn, &hstate->file.nlm_share_list) { state = glist_entry(glist, state_t, state_list); remove_nlm_share(state); } } void state_export_unshare_all(void) { int errcnt = 0; state_t *state; state_owner_t *owner; struct fsal_obj_handle *obj; state_status_t status; while (errcnt < STATE_ERR_MAX) { PTHREAD_RWLOCK_wrlock(&op_ctx->ctx_export->lock); state = glist_first_entry( &op_ctx->ctx_export->exp_nlm_share_list, state_t, state_export_list); if (state == NULL) { PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock); break; } obj = get_state_obj_ref(state); if (obj == NULL) { LogDebug(COMPONENT_STATE, "Entry for state is stale"); PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock); break; } owner = state->state_owner; /* Get a reference to the state_t */ inc_state_t_ref(state); /* get a reference to the owner */ inc_state_owner_ref(owner); /* Drop the export mutex to call unshare */ PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock); /* Remove all shares held by this Owner on this export */ status = state_nlm_share(obj, OPEN4_SHARE_ACCESS_BOTH, OPEN4_SHARE_DENY_BOTH, owner, state, false, true); /* Release references taken above. Should free the state_t. */ dec_state_owner_ref(owner); obj->obj_ops.put_ref(obj); dec_state_t_ref(state); if (!state_unlock_err_ok(status)) { /* Increment the error count and try the next share, * with any luck the memory pressure which is causing * the problem will resolve itself. */ LogCrit(COMPONENT_STATE, "state_unlock failed %s", state_err_str(status)); errcnt++; } } if (errcnt == STATE_ERR_MAX) { LogFatal(COMPONENT_STATE, "Could not complete cleanup of NLM shares for %s", export_path(op_ctx->ctx_export)); } } #endif /* _USE_NLM */ /** @} */ nfs-ganesha-2.6.0/src/THANKS000066400000000000000000000031461324272410200154140ustar00rootroot00000000000000This files list the people, outside of the original development team that help in the project. Their contributions were very helpful, so I guess they should have their names listed somewhere. Thanks to: Contributors that are not colleagues or students - Tom "spot" Callaway , for his experience and wiseness in RPM packaging. - People from maling-list nfsv4@linux-nfs.org (with special attention to J. Bruce Fields and Trond Myklebust) for their help understanding the complex logic within NFSv4 - Eric Sesterhenn who provided a nice patch fixing memleaks. - Erik Levinson who did lots of work to make libnfsidmap work with gssrpc in nfs-ganesha - Sean Dague who provides me with several typos and memleaks patches - Aneesh Kumar K. V. who implemented NLMv4 support (locking for NFSv3) - Frank Filz who provided me with several small fixes on the server's behavior. He idesigned i and wrote as well the new Log layer API. Students that came to make an intership with us and worked on the project - Mickael Guerin who wrote the first release of the FSAL_POSIX - Laureline Provost who wrote a PERL library that had been very useful to quickly write benchmarks and non-regression test - Cedric Cabessa who worked on the SNMP admin module - Adrien Grellier who provided us with the first TI-RPC support in the product - Rémi Duraffort who wrote the FSAL_ZFS implementation as well as the libzfswrap contrib. He fixed many other small bugs too nfs-ganesha-2.6.0/src/avl/000077500000000000000000000000001324272410200152575ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/avl/CMakeLists.txt000066400000000000000000000003341324272410200200170ustar00rootroot00000000000000 ########### next target ############### SET(avltree_STAT_SRCS avl.c bst.c rb.c splay.c ) add_library(avltree STATIC ${avltree_STAT_SRCS}) add_sanitizers(avltree) ########### install files ############### nfs-ganesha-2.6.0/src/avl/avl.c000066400000000000000000000307011324272410200162060ustar00rootroot00000000000000/* * avltree - Implements an AVL tree with parent pointers. * * Copyright (C) 2010-2014 Franck Bui-Huu * * This file is part of libtree which is free software; you can * redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software * Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the LICENSE file for license rights and limitations. */ #include #include "avltree.h" #if !defined UINTPTR_MAX || UINTPTR_MAX != UINT64_MAX static inline int is_root(struct avltree_node *node) { return node->parent == NULL; } static inline void INIT_NODE(struct avltree_node *node) { node->left = NULL; node->right = NULL; node->parent = NULL; node->balance = 0; } static inline void set_balance(int balance, struct avltree_node *node) { node->balance = balance; } static inline int inc_balance(struct avltree_node *node) { return ++node->balance; } static inline int dec_balance(struct avltree_node *node) { return --node->balance; } static inline struct avltree_node *get_parent(const struct avltree_node *node) { return node->parent; } static inline void set_parent(struct avltree_node *parent, struct avltree_node *node) { node->parent = parent; } #else static inline int is_root(struct avltree_node *node) { return !(node->parent & ~7UL); } static inline void INIT_NODE(struct avltree_node *node) { node->left = NULL; node->right = NULL; node->parent = 2; } static inline void set_balance(int balance, struct avltree_node *node) { node->parent = (node->parent & ~7UL) | (balance + 2); } static inline int inc_balance(struct avltree_node *node) { return (int)(++node->parent & 7) - 2; } static inline int dec_balance(struct avltree_node *node) { return (int)(--node->parent & 7) - 2; } static inline struct avltree_node *get_parent(const struct avltree_node *node) { return (struct avltree_node *)(node->parent & ~7UL); } static inline void set_parent(const struct avltree_node *parent, struct avltree_node *node) { node->parent = (uintptr_t) parent | (node->parent & 7); } #endif /* * Iterators */ static inline struct avltree_node *get_first(struct avltree_node *node) { while (node->left) node = node->left; return node; } static inline struct avltree_node *get_last(struct avltree_node *node) { while (node->right) node = node->right; return node; } struct avltree_node *avltree_next(const struct avltree_node *node) { struct avltree_node *parent; if (node->right) return get_first(node->right); while ((parent = get_parent(node)) && parent->right == node) node = parent; return parent; } struct avltree_node *avltree_prev(const struct avltree_node *node) { struct avltree_node *parent; if (node->left) return get_last(node->left); while ((parent = get_parent(node)) && parent->left == node) node = parent; return parent; } uint64_t avltree_size(const struct avltree * tree) { return tree->size; } /* * The AVL tree is more rigidly balanced than Red-Black trees, leading * to slower insertion and removal but faster retrieval. */ /* node->balance = height(node->right) - height(node->left); */ static void rotate_left(struct avltree_node *node, struct avltree *tree) { struct avltree_node *p = node; struct avltree_node *q = node->right; /* can't be NULL */ struct avltree_node *parent = get_parent(p); if (!is_root(p)) { if (parent->left == p) parent->left = q; else parent->right = q; } else tree->root = q; set_parent(parent, q); set_parent(q, p); p->right = q->left; if (p->right) set_parent(p, p->right); q->left = p; } static void rotate_right(struct avltree_node *node, struct avltree *tree) { struct avltree_node *p = node; struct avltree_node *q = node->left; /* can't be NULL */ struct avltree_node *parent = get_parent(p); if (!is_root(p)) { if (parent->left == p) parent->left = q; else parent->right = q; } else tree->root = q; set_parent(parent, q); set_parent(q, p); p->left = q->right; if (p->left) set_parent(p, p->left); q->right = p; } struct avltree_node *avltree_inf(const struct avltree_node *key, const struct avltree *tree) { struct avltree_node *parent __attribute__ ((unused)); struct avltree_node *lb; struct avltree_node *unbalanced __attribute__ ((unused)); struct avltree_node *node = tree->root; int is_left = 0; int res = 0; lb = avltree_first(tree); /* at worst, the first entry */ unbalanced = node; parent = NULL; is_left = 0; while (node) { if (get_balance(node) != 0) unbalanced = node; res = tree->cmp_fn(node, key); if (res == 0) { /* node is the infimum */ return node; } else if (res < 1) /* lb is less than key */ lb = node; parent = node; if ((is_left = res > 0)) node = node->left; else node = node->right; } /* while */ return (lb); } struct avltree_node *avltree_sup(const struct avltree_node *key, const struct avltree *tree) { struct avltree_node *parent __attribute__ ((unused)); struct avltree_node *ub = NULL; struct avltree_node *unbalanced __attribute__ ((unused)); struct avltree_node *node = tree->root; int is_left = 0; int res = 0; unbalanced = node; parent = NULL; is_left = 0; ub = node; while (node) { if (get_balance(node) != 0) unbalanced = node; res = tree->cmp_fn(node, key); if (res == 0) { /* node is the supremum */ return (node); } parent = node; if ((is_left = res > 0)) node = node->left; else node = node->right; if (node) { res = tree->cmp_fn(node, key); if (res > 0) /* XXX do we need reciprocal test on ub? */ ub = node; } } /* while */ return (ub); } static void set_child(struct avltree_node *child, struct avltree_node *node, int left) { if (left) node->left = child; else node->right = child; } /* Insertion never needs more than 2 rotations */ void avltree_do_insert(struct avltree_node *node, struct avltree *tree, struct avltree_node *parent, struct avltree_node *unbalanced, int is_left) { INIT_NODE(node); if (!parent) { tree->root = node; tree->first = tree->last = node; tree->height++; tree->size++; return; } if (is_left) { if (parent == tree->first) tree->first = node; } else { if (parent == tree->last) tree->last = node; } set_parent(parent, node); set_child(node, parent, is_left); for (;;) { if (parent->left == node) dec_balance(parent); else inc_balance(parent); if (parent == unbalanced) break; node = parent; parent = get_parent(parent); } /* inserted */ tree->size++; switch (get_balance(unbalanced)) { case 1: case -1: tree->height++; /* fall through */ case 0: break; case 2:{ struct avltree_node *right = unbalanced->right; if (get_balance(right) == 1) { set_balance(0, unbalanced); set_balance(0, right); } else { switch (get_balance(right->left)) { case 1: set_balance(-1, unbalanced); set_balance(0, right); break; case 0: set_balance(0, unbalanced); set_balance(0, right); break; case -1: set_balance(0, unbalanced); set_balance(1, right); break; } set_balance(0, right->left); rotate_right(right, tree); } rotate_left(unbalanced, tree); break; } case -2:{ struct avltree_node *left = unbalanced->left; if (get_balance(left) == -1) { set_balance(0, unbalanced); set_balance(0, left); } else { switch (get_balance(left->right)) { case 1: set_balance(0, unbalanced); set_balance(-1, left); break; case 0: set_balance(0, unbalanced); set_balance(0, left); break; case -1: set_balance(1, unbalanced); set_balance(0, left); break; } set_balance(0, left->right); rotate_left(left, tree); } rotate_right(unbalanced, tree); break; } } } /* Deletion might require up to log(n) rotations */ void avltree_remove(struct avltree_node *node, struct avltree *tree) { struct avltree_node *parent = get_parent(node); struct avltree_node *left = node->left; struct avltree_node *right = node->right; struct avltree_node *next; int is_left = 0; if (node == tree->first) tree->first = avltree_next(node); if (node == tree->last) tree->last = avltree_prev(node); if (!left) next = right; else if (!right) next = left; else next = get_first(right); if (parent) { is_left = parent->left == node; set_child(next, parent, is_left); } else tree->root = next; if (left && right) { set_balance(get_balance(node), next); next->left = left; set_parent(next, left); if (next != right) { parent = get_parent(next); set_parent(get_parent(node), next); node = next->right; parent->left = node; is_left = 1; next->right = right; set_parent(next, right); } else { set_parent(parent, next); parent = next; node = parent->right; is_left = 0; } assert(parent != NULL); } else node = next; if (node) set_parent(parent, node); /* removed */ tree->size--; /* * At this point, 'parent' can only be null, if 'node' is the * tree's root and has at most one child. * * case 1: the subtree is now balanced but its height has * decreased. * * case 2: the subtree is mostly balanced and its height is * unchanged. * * case 3: the subtree is unbalanced and its height may have * been changed during the rebalancing process, see below. * * case 3.1: after a left rotation, the subtree becomes mostly * balanced and its height is unchanged. * * case 3.2: after a left rotation, the subtree becomes * balanced but its height has decreased. * * case 3.3: after a left and a right rotation, the subtree * becomes balanced or mostly balanced but its height has * decreased for all cases. */ while (parent) { int balance; node = parent; parent = get_parent(parent); if (is_left) { is_left = parent && parent->left == node; balance = inc_balance(node); if (balance == 0) /* case 1 */ continue; if (balance == 1) /* case 2 */ return; right = node->right; /* case 3 */ switch (get_balance(right)) { case 0: /* case 3.1 */ set_balance(1, node); set_balance(-1, right); rotate_left(node, tree); return; case 1: /* case 3.2 */ set_balance(0, node); set_balance(0, right); break; case -1: /* case 3.3 */ switch (get_balance(right->left)) { case 1: set_balance(-1, node); set_balance(0, right); break; case 0: set_balance(0, node); set_balance(0, right); break; case -1: set_balance(0, node); set_balance(1, right); break; } set_balance(0, right->left); rotate_right(right, tree); } rotate_left(node, tree); } else { is_left = parent && parent->left == node; balance = dec_balance(node); if (balance == 0) continue; if (balance == -1) return; left = node->left; switch (get_balance(left)) { case 0: set_balance(-1, node); set_balance(1, left); rotate_right(node, tree); return; case -1: set_balance(0, node); set_balance(0, left); break; case 1: switch (get_balance(left->right)) { case 1: set_balance(0, node); set_balance(-1, left); break; case 0: set_balance(0, node); set_balance(0, left); break; case -1: set_balance(1, node); set_balance(0, left); break; } set_balance(0, left->right); rotate_left(left, tree); } rotate_right(node, tree); } } tree->height--; } void avltree_replace(struct avltree_node *old, struct avltree_node *new, struct avltree *tree) { struct avltree_node *parent = get_parent(old); if (parent) set_child(new, parent, parent->left == old); else { /* I'm skeptical this case should be permitted--this * says that if old is not in the tree, just make new * the root of the tree */ tree->root = new; tree->size++; } if (old->left) set_parent(new, old->left); if (old->right) set_parent(new, old->right); if (tree->first == old) tree->first = new; if (tree->last == old) tree->last = new; *new = *old; } int avltree_init(struct avltree *tree, avltree_cmp_fn_t cmp, unsigned long flags) { if (flags) return -1; tree->root = NULL; tree->cmp_fn = cmp; tree->height = -1; tree->first = NULL; tree->last = NULL; tree->size = 0; return 0; } nfs-ganesha-2.6.0/src/avl/bst.c000066400000000000000000000172051324272410200162200ustar00rootroot00000000000000/* * bstree - Implements a threaded binary search tree. * * Copyright (C) 2010-2014 Franck Bui-Huu * * This file is part of libtree which is free software; you can * redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software * Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the LICENSE file for license rights and limitations. */ #include "avltree.h" /* * This is where the black magic is defined. If you're lucky enough to * work on a system that doesn't support these kind of tricks then we * assume UINTPTR_MAX and uintptr_t type are not defined. * * Note that the getters returns a pointer when appropriate otherwise * NULL; */ #ifdef UINTPTR_MAX #define NODE_INIT { 0, } static inline void INIT_NODE(struct bstree_node *node) { node->left = 0; node->right = 0; } static inline void set_thread(struct bstree_node *t, uintptr_t * p) { *p = (uintptr_t) t | 1; } static inline struct bstree_node *get_thread(uintptr_t u) { return (struct bstree_node *)((u & -(int)(u & 1)) & ~1UL); } static inline void set_link(struct bstree_node *n, uintptr_t * p) { *p = (uintptr_t) n; } static inline struct bstree_node *get_link(uintptr_t u) { return (struct bstree_node *)(u & ((int)(u & 1) - 1)); } #define set_left(l,n) set_link(l, &(n)->left) #define set_right(r,n) set_link(r, &(n)->right) #define set_prev(p,n) set_thread(p, &(n)->left) #define set_next(s,n) set_thread(s, &(n)->right) #define get_left(n) get_link((n)->left) #define get_right(n) get_link((n)->right) #define get_prev(n) get_thread((n)->left) #define get_next(n) get_thread((n)->right) #else #define NODE_INIT { NULL, } static inline void INIT_NODE(struct bstree_node *node) { node->left = NULL; node->right = NULL; node->left_is_thread = 0; node->right_is_thread = 0; } static inline void set_left(struct bstree_node *l, struct bstree_node *n) { n->left = l; n->left_is_thread = 0; } static inline void set_right(struct bstree_node *r, struct bstree_node *n) { n->right = r; n->right_is_thread = 0; } static inline void set_prev(struct bstree_node *t, struct bstree_node *n) { n->left = t; n->left_is_thread = 1; } static inline void set_next(struct bstree_node *t, struct bstree_node *n) { n->right = t; n->right_is_thread = 1; } static inline struct bstree_node *get_left(const struct bstree_node *n) { if (n->left_is_thread) return NULL; return n->left; } static inline struct bstree_node *get_right(const struct bstree_node *n) { if (n->right_is_thread) return NULL; return n->right; } static inline struct bstree_node *get_prev(const struct bstree_node *n) { if (!n->left_is_thread) return NULL; return n->left; } static inline struct bstree_node *get_next(const struct bstree_node *n) { if (!n->right_is_thread) return NULL; return n->right; } #endif /* UINTPTR_MAX */ /* * Iterators */ static inline struct bstree_node *get_first(struct bstree_node *node) { struct bstree_node *left; while ((left = get_left(node))) node = left; return node; } static inline struct bstree_node *get_last(struct bstree_node *node) { struct bstree_node *right; while ((right = get_right(node))) node = right; return node; } struct bstree_node *bstree_first(const struct bstree *tree) { if (tree->root) return tree->first; return NULL; } struct bstree_node *bstree_last(const struct bstree *tree) { if (tree->root) return tree->last; return NULL; } struct bstree_node *bstree_next(const struct bstree_node *node) { struct bstree_node *right = get_right(node); if (right) return get_first(right); return get_next(node); } struct bstree_node *bstree_prev(const struct bstree_node *node) { struct bstree_node *left = get_left(node); if (left) return get_last(left); return get_prev(node); } /* * Main ops: lookup, insert, remove. */ static struct bstree_node *do_lookup(const struct bstree_node *key, const struct bstree *tree, struct bstree_node **pparent, int *is_left) { struct bstree_node *node = tree->root; *pparent = NULL; *is_left = 0; while (node) { int res = tree->cmp_fn(node, key); if (res == 0) return node; *pparent = node; if ((*is_left = res > 0)) node = get_left(node); else node = get_right(node); } return NULL; } struct bstree_node *bstree_lookup(const struct bstree_node *key, const struct bstree *tree) { struct bstree_node *parent; int is_left; return do_lookup(key, tree, &parent, &is_left); } struct bstree_node *bstree_insert(struct bstree_node *node, struct bstree *tree) { struct bstree_node *key, *parent; int is_left; key = do_lookup(node, tree, &parent, &is_left); if (key) return key; if (!parent) { INIT_NODE(node); tree->root = tree->first = tree->last = node; return NULL; } if (is_left) { if (parent == tree->first) tree->first = node; set_prev(get_prev(parent), node); set_next(parent, node); set_left(node, parent); } else { if (parent == tree->last) tree->last = node; set_prev(parent, node); set_next(get_next(parent), node); set_right(node, parent); } return NULL; } static void set_child(struct bstree_node *child, struct bstree_node *node, int left) { if (left) set_left(child, node); else set_right(child, node); } void bstree_remove(struct bstree_node *node, struct bstree *tree) { struct bstree_node *left, *right, *next; struct bstree_node fake_parent, *parent; int is_left; do_lookup(node, tree, &parent, &is_left); if (!parent) { INIT_NODE(&fake_parent); parent = &fake_parent; is_left = 0; } left = get_left(node); right = get_right(node); if (!left && !right) { if (is_left) set_prev(get_prev(node), parent); else set_next(get_next(node), parent); next = parent; goto update_first_last; } if (!left) { next = get_first(right); set_prev(get_prev(node), next); set_child(right, parent, is_left); goto update_first_last; } if (!right) { next = get_last(left); set_next(get_next(node), next); set_child(left, parent, is_left); goto update_first_last; } next = get_first(right); if (next != right) { /* 'm' is the parent of 'next' */ struct bstree_node *m = get_next(get_last(next)); if (get_right(next)) set_left(get_right(next), m); else set_prev(next, m); set_right(right, next); } set_child(next, parent, is_left); set_left(left, next); set_next(next, get_last(left)); out: if (parent == &fake_parent) tree->root = get_right(parent); return; update_first_last: if (node == tree->first) tree->first = next; if (node == tree->last) tree->last = next; goto out; } void bstree_replace(struct bstree_node *old, struct bstree_node *new, struct bstree *tree) { struct bstree_node *parent, *next, *prev; int is_left; if (tree->first == old) tree->first = new; if (tree->last == old) tree->last = new; if (tree->root == old) tree->root = new; else { /* * Update the parent: do a full lookup to retrieve * it. There's probably a better way but it's bst... */ do_lookup(old, tree, &parent, &is_left); if (parent) set_child(new, parent, is_left); } /* update the thread links */ prev = bstree_prev(old); if (prev && get_next(prev) == old) set_next(new, prev); next = bstree_next(old); if (next && get_prev(next) == old) set_prev(new, next); *new = *old; } int bstree_init(struct bstree *tree, bstree_cmp_fn_t cmp, unsigned long flags) { if (flags) return -1; tree->root = NULL; tree->cmp_fn = cmp; return 0; } nfs-ganesha-2.6.0/src/avl/rb.c000066400000000000000000000246311324272410200160340ustar00rootroot00000000000000/* * rbtree - Implements a red-black tree with parent pointers. * * Copyright (C) 2010-2014 Franck Bui-Huu * * This file is part of libtree which is free software; you can * redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software * Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the LICENSE file for license rights and limitations. */ /* * For recall a red-black tree has the following properties: * * 1. All nodes are either BLACK or RED * 2. Leafs are BLACK * 3. A RED node has BLACK children only * 4. Path from a node to any leafs has the same number of BLACK nodes. * */ #include "avltree.h" /* * Some helpers */ #ifdef UINTPTR_MAX static inline enum rb_color get_color(const struct rbtree_node *node) { return node->parent & 1; } static inline void set_color(enum rb_color color, struct rbtree_node *node) { node->parent = (node->parent & ~1UL) | color; } static inline struct rbtree_node *get_parent(const struct rbtree_node *node) { return (struct rbtree_node *)(node->parent & ~1UL); } static inline void set_parent(struct rbtree_node *parent, struct rbtree_node *node) { node->parent = (uintptr_t) parent | (node->parent & 1); } #else static inline enum rb_color get_color(const struct rbtree_node *node) { return node->color; } static inline void set_color(enum rb_color color, struct rbtree_node *node) { node->color = color; } static inline struct rbtree_node *get_parent(const struct rbtree_node *node) { return node->parent; } static inline void set_parent(struct rbtree_node *parent, struct rbtree_node *node) { node->parent = parent; } #endif /* UINTPTR_MAX */ static inline int is_root(struct rbtree_node *node) { return get_parent(node) == NULL; } static inline int is_black(struct rbtree_node *node) { return get_color(node) == RB_BLACK; } static inline int is_red(struct rbtree_node *node) { return !is_black(node); } /* * Iterators */ static inline struct rbtree_node *get_first(struct rbtree_node *node) { while (node->left) node = node->left; return node; } static inline struct rbtree_node *get_last(struct rbtree_node *node) { while (node->right) node = node->right; return node; } struct rbtree_node *rbtree_first(const struct rbtree *tree) { return tree->first; } struct rbtree_node *rbtree_last(const struct rbtree *tree) { return tree->last; } struct rbtree_node *rbtree_next(const struct rbtree_node *node) { struct rbtree_node *parent; if (node->right) return get_first(node->right); while ((parent = get_parent(node)) && parent->right == node) node = parent; return parent; } struct rbtree_node *rbtree_prev(const struct rbtree_node *node) { struct rbtree_node *parent; if (node->left) return get_last(node->left); while ((parent = get_parent(node)) && parent->left == node) node = parent; return parent; } /* * 'pparent' and 'is_left' are only used for insertions. Normally GCC * will notice this and get rid of them for lookups. */ static inline struct rbtree_node *do_lookup(const struct rbtree_node *key, const struct rbtree *tree, struct rbtree_node **pparent, int *is_left) { struct rbtree_node *node = tree->root; *pparent = NULL; *is_left = 0; while (node) { int res = tree->cmp_fn(node, key); if (res == 0) return node; *pparent = node; if ((*is_left = res > 0)) node = node->left; else node = node->right; } return NULL; } /* * Rotate operations (They preserve the binary search tree property, * assuming that the keys are unique). */ static void rotate_left(struct rbtree_node *node, struct rbtree *tree) { struct rbtree_node *p = node; struct rbtree_node *q = node->right; /* can't be NULL */ struct rbtree_node *parent = get_parent(p); if (!is_root(p)) { if (parent->left == p) parent->left = q; else parent->right = q; } else tree->root = q; set_parent(parent, q); set_parent(q, p); p->right = q->left; if (p->right) set_parent(p, p->right); q->left = p; } static void rotate_right(struct rbtree_node *node, struct rbtree *tree) { struct rbtree_node *p = node; struct rbtree_node *q = node->left; /* can't be NULL */ struct rbtree_node *parent = get_parent(p); if (!is_root(p)) { if (parent->left == p) parent->left = q; else parent->right = q; } else tree->root = q; set_parent(parent, q); set_parent(q, p); p->left = q->right; if (p->left) set_parent(p, p->left); q->right = p; } struct rbtree_node *rbtree_lookup(const struct rbtree_node *key, const struct rbtree *tree) { struct rbtree_node *parent; int is_left; return do_lookup(key, tree, &parent, &is_left); } static void set_child(struct rbtree_node *child, struct rbtree_node *node, int left) { if (left) node->left = child; else node->right = child; } struct rbtree_node *rbtree_insert(struct rbtree_node *node, struct rbtree *tree) { struct rbtree_node *key, *parent; int is_left; key = do_lookup(node, tree, &parent, &is_left); if (key) return key; node->left = NULL; node->right = NULL; set_color(RB_RED, node); set_parent(parent, node); if (parent) { if (is_left) { if (parent == tree->first) tree->first = node; } else { if (parent == tree->last) tree->last = node; } set_child(node, parent, is_left); } else { tree->root = node; tree->first = node; tree->last = node; } /* * Fixup the modified tree by recoloring nodes and performing * rotations (2 at most) hence the red-black tree properties are * preserved. */ while ((parent = get_parent(node)) && is_red(parent)) { struct rbtree_node *grandpa = get_parent(parent); if (parent == grandpa->left) { struct rbtree_node *uncle = grandpa->right; if (uncle && is_red(uncle)) { set_color(RB_BLACK, parent); set_color(RB_BLACK, uncle); set_color(RB_RED, grandpa); node = grandpa; } else { if (node == parent->right) { rotate_left(parent, tree); node = parent; parent = get_parent(node); } set_color(RB_BLACK, parent); set_color(RB_RED, grandpa); rotate_right(grandpa, tree); } } else { struct rbtree_node *uncle = grandpa->left; if (uncle && is_red(uncle)) { set_color(RB_BLACK, parent); set_color(RB_BLACK, uncle); set_color(RB_RED, grandpa); node = grandpa; } else { if (node == parent->left) { rotate_right(parent, tree); node = parent; parent = get_parent(node); } set_color(RB_BLACK, parent); set_color(RB_RED, grandpa); rotate_left(grandpa, tree); } } } set_color(RB_BLACK, tree->root); return NULL; } void rbtree_remove(struct rbtree_node *node, struct rbtree *tree) { struct rbtree_node *parent = get_parent(node); struct rbtree_node *left = node->left; struct rbtree_node *right = node->right; struct rbtree_node *next; enum rb_color color; if (node == tree->first) tree->first = rbtree_next(node); if (node == tree->last) tree->last = rbtree_prev(node); if (!left) next = right; else if (!right) next = left; else next = get_first(right); if (parent) set_child(next, parent, parent->left == node); else tree->root = next; if (left && right) { color = get_color(next); set_color(get_color(node), next); next->left = left; set_parent(next, left); if (next != right) { parent = get_parent(next); set_parent(get_parent(node), next); node = next->right; parent->left = node; next->right = right; set_parent(next, right); } else { set_parent(parent, next); parent = next; node = next->right; } } else { color = get_color(node); node = next; } /* * 'node' is now the sole successor's child and 'parent' its * new parent (since the successor can have been moved). */ if (node) set_parent(parent, node); /* * The 'easy' cases. */ if (color == RB_RED) return; if (node && is_red(node)) { set_color(RB_BLACK, node); return; } do { if (node == tree->root) break; if (node == parent->left) { struct rbtree_node *sibling = parent->right; if (is_red(sibling)) { set_color(RB_BLACK, sibling); set_color(RB_RED, parent); rotate_left(parent, tree); sibling = parent->right; } if ((!sibling->left || is_black(sibling->left)) && (!sibling->right || is_black(sibling->right))) { set_color(RB_RED, sibling); node = parent; parent = get_parent(parent); continue; } if (!sibling->right || is_black(sibling->right)) { set_color(RB_BLACK, sibling->left); set_color(RB_RED, sibling); rotate_right(sibling, tree); sibling = parent->right; } set_color(get_color(parent), sibling); set_color(RB_BLACK, parent); set_color(RB_BLACK, sibling->right); rotate_left(parent, tree); node = tree->root; break; } else { struct rbtree_node *sibling = parent->left; if (is_red(sibling)) { set_color(RB_BLACK, sibling); set_color(RB_RED, parent); rotate_right(parent, tree); sibling = parent->left; } if ((!sibling->left || is_black(sibling->left)) && (!sibling->right || is_black(sibling->right))) { set_color(RB_RED, sibling); node = parent; parent = get_parent(parent); continue; } if (!sibling->left || is_black(sibling->left)) { set_color(RB_BLACK, sibling->right); set_color(RB_RED, sibling); rotate_left(sibling, tree); sibling = parent->left; } set_color(get_color(parent), sibling); set_color(RB_BLACK, parent); set_color(RB_BLACK, sibling->left); rotate_right(parent, tree); node = tree->root; break; } } while (is_black(node)); if (node) set_color(RB_BLACK, node); } void rbtree_replace(struct rbtree_node *old, struct rbtree_node *new, struct rbtree *tree) { struct rbtree_node *parent = get_parent(old); if (parent) set_child(new, parent, parent->left == old); else tree->root = new; if (old->left) set_parent(new, old->left); if (old->right) set_parent(new, old->right); if (tree->first == old) tree->first = new; if (tree->last == old) tree->last = new; *new = *old; } int rbtree_init(struct rbtree *tree, rbtree_cmp_fn_t fn, unsigned long flags) { if (flags) return -1; tree->root = NULL; tree->cmp_fn = fn; tree->first = NULL; tree->last = NULL; return 0; } nfs-ganesha-2.6.0/src/avl/splay.c000066400000000000000000000172161324272410200165620ustar00rootroot00000000000000/* * splaytree - Implements a top-down threaded splay tree. * * Copyright (C) 2010-2014 Franck Bui-Huu * * This file is part of libtree which is free software; you can * redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software * Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the LICENSE file for license rights and limitations. */ #include #include "avltree.h" #ifdef UINTPTR_MAX #define NODE_INIT { 0, } static inline void INIT_NODE(struct splaytree_node *node) { node->left = 0; node->right = 0; } static inline void set_thread(struct splaytree_node *t, uintptr_t * p) { *p = (uintptr_t) t | 1; } static inline struct splaytree_node *get_thread(uintptr_t u) { return (struct splaytree_node *)((u & -(int)(u & 1)) & ~1UL); } static inline void set_link(struct splaytree_node *n, uintptr_t * p) { *p = (uintptr_t) n; } static inline struct splaytree_node *get_link(uintptr_t u) { return (struct splaytree_node *)(u & ((int)(u & 1) - 1)); } #define set_left(l,n) set_link(l, &(n)->left) #define set_right(r,n) set_link(r, &(n)->right) #define set_prev(p,n) set_thread(p, &(n)->left) #define set_next(s,n) set_thread(s, &(n)->right) #define get_left(n) get_link((n)->left) #define get_right(n) get_link((n)->right) #define get_prev(n) get_thread((n)->left) #define get_next(n) get_thread((n)->right) #else /* !UINTPTR_MAX */ #define NODE_INIT { NULL, } static inline void INIT_NODE(struct splaytree_node *node) { node->left = NULL; node->right = NULL; node->left_is_thread = 0; node->right_is_thread = 0; } static inline void set_left(struct splaytree_node *l, struct splaytree_node *n) { n->left = l; n->left_is_thread = 0; } static inline void set_right(struct splaytree_node *r, struct splaytree_node *n) { n->right = r; n->right_is_thread = 0; } static inline void set_prev(struct splaytree_node *t, struct splaytree_node *n) { n->left = t; n->left_is_thread = 1; } static inline void set_next(struct splaytree_node *t, struct splaytree_node *n) { n->right = t; n->right_is_thread = 1; } static inline struct splaytree_node *get_left(const struct splaytree_node *n) { if (!n->left_is_thread) return n->left; return NULL; } static inline struct splaytree_node *get_right(const struct splaytree_node *n) { if (!n->right_is_thread) return n->right; return NULL; } static inline struct splaytree_node *get_prev(const struct splaytree_node *n) { if (n->left_is_thread) return n->left; return NULL; } static inline struct splaytree_node *get_next(const struct splaytree_node *n) { if (n->right_is_thread) return n->right; return NULL; } #endif /* UINTPTR_MAX */ /* * Iterators */ static inline struct splaytree_node *get_first(struct splaytree_node *node) { struct splaytree_node *left; while ((left = get_left(node))) node = left; return node; } static inline struct splaytree_node *get_last(struct splaytree_node *node) { struct splaytree_node *right; while ((right = get_right(node))) node = right; return node; } struct splaytree_node *splaytree_first(const struct splaytree *tree) { return tree->first; } struct splaytree_node *splaytree_last(const struct splaytree *tree) { return tree->last; } struct splaytree_node *splaytree_next(const struct splaytree_node *node) { struct splaytree_node *right = get_right(node); if (right) return get_first(right); return get_next(node); } struct splaytree_node *splaytree_prev(const struct splaytree_node *node) { struct splaytree_node *left = get_left(node); if (left) return get_last(left); return get_prev(node); } static inline void rotate_right(struct splaytree_node *node) { struct splaytree_node *left = get_left(node); /* can't be NULL */ struct splaytree_node *r = get_right(left); if (r) set_left(r, node); else set_prev(left, node); set_right(node, left); } static inline void rotate_left(struct splaytree_node *node) { struct splaytree_node *right = get_right(node); /* can't be NULL */ struct splaytree_node *l = get_left(right); if (l) set_right(l, node); else set_next(right, node); set_left(node, right); } static int do_splay(const struct splaytree_node *key, struct splaytree *tree) { struct splaytree_node subroots = NODE_INIT; struct splaytree_node *subleft = &subroots, *subright = &subroots; struct splaytree_node *root = tree->root; splaytree_cmp_fn_t cmp = tree->cmp_fn; int rv; for (;;) { rv = cmp(key, root); if (rv == 0) break; if (rv < 0) { struct splaytree_node *left; left = get_left(root); if (!left) break; if ((rv = cmp(key, left)) < 0) { rotate_right(root); root = left; left = get_left(root); if (!left) break; } /* link left */ set_left(root, subright); subright = root; root = left; } else { struct splaytree_node *right; right = get_right(root); if (!right) break; if ((rv = cmp(key, right)) > 0) { rotate_left(root); root = right; right = get_right(root); if (!right) break; } /* link right */ set_right(root, subleft); subleft = root; root = right; } } /* assemble */ if (get_left(root)) set_right(get_left(root), subleft); else set_next(root, subleft); if (get_right(root)) set_left(get_right(root), subright); else set_prev(root, subright); set_left(get_right(&subroots), root); set_right(get_left(&subroots), root); tree->root = root; return rv; } struct splaytree_node *splaytree_lookup(const struct splaytree_node *key, struct splaytree *tree) { if (!tree->root) return NULL; if (do_splay(key, tree) != 0) return NULL; return tree->root; } struct splaytree_node *splaytree_insert(struct splaytree_node *node, struct splaytree *tree) { struct splaytree_node *root = tree->root; int res; if (!root) { INIT_NODE(node); tree->root = node; tree->first = node; tree->last = node; return NULL; } res = do_splay(node, tree); if (res == 0) return tree->root; root = tree->root; if (res < 0) { struct splaytree_node *left = get_left(root); set_left(left, node); set_right(root, node); if (left) set_next(node, get_last(left)); else tree->first = node; set_prev(node, root); } else { struct splaytree_node *right = get_right(root); set_right(right, node); set_left(root, node); if (right) set_prev(node, get_first(right)); else tree->last = node; set_next(node, root); } tree->root = node; return NULL; } void splaytree_remove(struct splaytree_node *node, struct splaytree *tree) { struct splaytree_node *right, *left, *prev; do_splay(node, tree); assert(tree->root == node); /* 'node' must be present */ right = get_right(node); left = get_left(node); if (!left) { tree->root = right; tree->first = splaytree_next(node); prev = NULL; } else { tree->root = left; do_splay(node, tree); set_right(right, tree->root); prev = tree->root; } if (right) set_prev(prev, get_first(right)); else tree->last = prev; } void splaytree_replace(struct splaytree_node *old, struct splaytree_node *new, struct splaytree *tree) { do_splay(old, tree); assert(tree->root == old); tree->root = new; if (tree->first == old) tree->first = new; if (tree->last == old) tree->last = new; *new = *old; } int splaytree_init(struct splaytree *tree, splaytree_cmp_fn_t cmp, unsigned long flags) { if (flags) return -1; tree->root = NULL; tree->first = NULL; tree->last = NULL; tree->cmp_fn = cmp; return 0; } nfs-ganesha-2.6.0/src/cidr/000077500000000000000000000000001324272410200154165ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/cidr/CMakeLists.txt000066400000000000000000000005311324272410200201550ustar00rootroot00000000000000 ########### next target ############### SET(cidr_STAT_SRCS cidr_addr.c cidr_compare.c cidr_from_str.c cidr_get.c cidr_inaddr.c cidr_mem.c cidr_misc.c cidr_net.c cidr_num.c cidr_to_str.c cidr_pow2_p.h ) add_library(cidr STATIC ${cidr_STAT_SRCS}) add_sanitizers(cidr) ########### install files ############### nfs-ganesha-2.6.0/src/cidr/LICENSE000066400000000000000000000030531324272410200164240ustar00rootroot00000000000000/* * Copyright (c) 2005, 2006 * Matthew D. Fuller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ This package also contains in tools/lorder a script borrowed from the FreeBSD operating system which is necessary to build (but not to run) libcidr. See that file for its license terms. nfs-ganesha-2.6.0/src/cidr/README.reindent000066400000000000000000000005211324272410200201030ustar00rootroot00000000000000This code is imported from: http://www.over-yonder.net/~fullermd/projects/libcidr It was inadvertently re-indented. I has been run through checkpatch.pl but the errors have not been fixed. Since this is upstream code, it should be removed and replaced by build linkage to the library itself, built separately. This is a V2.1+ task. nfs-ganesha-2.6.0/src/cidr/cidr_addr.c000066400000000000000000000040761324272410200175040ustar00rootroot00000000000000/* * Functions to generate various addresses based on a CIDR */ #include "config.h" #include #include #include "../include/cidr.h" /* Create a network address */ CIDR *cidr_addr_network(const CIDR * addr) { int i, j; CIDR *toret; toret = cidr_alloc(); toret->proto = addr->proto; /* The netmask is the same */ memcpy(toret->mask, addr->mask, (16 * sizeof(toret->mask[0]))); /* Now just figure out the network address and spit it out */ for (i = 0; i <= 15; i++) { for (j = 7; j >= 0; j--) { /* If we're into host bits, hop out */ if ((addr->mask[i] & 1 << j) == 0) return (toret); /* Else, copy this network bit */ toret->addr[i] |= (addr->addr[i] & 1 << j); } } /* * We only get here on host (/32 or /128) addresses; shorter masks * return earlier. But it's as correct as can be to just say the * same here, so... */ return (toret); } /* And a broadcast */ CIDR *cidr_addr_broadcast(const CIDR * addr) { int i, j; CIDR *toret; toret = cidr_alloc(); toret->proto = addr->proto; /* The netmask is the same */ memcpy(toret->mask, addr->mask, (16 * sizeof(toret->mask[0]))); /* Copy all the network bits */ for (i = 0; i <= 15; i++) { for (j = 7; j >= 0; j--) { /* If we're into host bits, hop out */ if ((addr->mask[i] & 1 << j) == 0) goto post; /* Else, copy this network bit */ toret->addr[i] |= (addr->addr[i] & 1 << j); } } post: /* Now set the remaining bits to 1 */ for ( /* i */ ; i <= 15; i++) { for ( /* j */ ; j >= 0; j--) toret->addr[i] |= (1 << j); j = 7; } /* And send it back */ return (toret); } /* Get the first host in a CIDR block */ CIDR *cidr_addr_hostmin(const CIDR * addr) { CIDR *toret; toret = cidr_addr_network(addr); if (toret == NULL) return (NULL); /* Preserve errno */ toret->addr[15] |= 1; return (toret); } /* Get the last host in a CIDR block */ CIDR *cidr_addr_hostmax(const CIDR * addr) { CIDR *toret; toret = cidr_addr_broadcast(addr); if (toret == NULL) return (NULL); /* Preserve errno */ toret->addr[15] &= 0xfe; return (toret); } nfs-ganesha-2.6.0/src/cidr/cidr_compare.c000066400000000000000000000047021324272410200202140ustar00rootroot00000000000000/* * Various comparison functions */ #include "config.h" #include #include /* For NULL */ #include "../include/cidr.h" /* Is one block entirely contained in another? */ int cidr_contains(const CIDR * big, const CIDR * little) { int i, oct, bit; int pflen; /* First off, they better be the same type */ if (big->proto != little->proto) { errno = EPROTO; return (-1); } /* We better understand the protocol, too */ if (big->proto != CIDR_IPV4 && big->proto != CIDR_IPV6) { errno = EINVAL; return (-1); } /* * little better be SMALL enough to fit in big. Note: The prefix * lengths CAN be the same, and little could still 'fit' in big if * the network bits are all the same. No need to special-case it, as * the normal tests below will DTRT. Save big's pflen for the test * loop. */ if (cidr_get_pflen(little) < (pflen = cidr_get_pflen(big))) { errno = 0; return (-1); } /* * Now let's compare. Note that for IPv4 addresses, the first 12 * octets are irrelevant. We take care throughout to keep them * zero'd out, so we don't _need_ to explicitly ignore them. But, * it's wasteful; it quadrules the amount of work needed to be done * to compare to v4 blocks, and this function may be useful in fairly * performance-sensitive parts of the application. Sure, an extra 12 * uint8_t compares better not be the make-or-break perforamnce point * for anything real, but why make it harder unnecessarily? */ if (big->proto == CIDR_IPV4) { i = 96; pflen += 96; } else if (big->proto == CIDR_IPV6) i = 0; else { /* Shouldn't happen */ errno = ENOENT; /* This is a really bad choice of errno */ return (-1); } /* Start comparing */ for ( /* i */ ; i < pflen; i++) { /* For convenience, set temp. vars to the octet/bit */ oct = i / 8; bit = 7 - (i % 8); if ((big->addr[oct] & (1 << bit)) != (little->addr[oct] & (1 << bit))) { errno = 0; return (-1); } } /* If we get here, all their network bits are the same */ return (0); } /* Are two CIDR's the same? */ int cidr_equals(const CIDR * one, const CIDR * two) { int i; /* Check protocols */ if (one->proto != two->proto) return (-1); /* Check addresses/masks */ if (one->proto == CIDR_IPV4) i = 12; else i = 0; for ( /* i */ ; i <= 15; i++) { if (one->addr[i] != two->addr[i]) return (-1); if (one->mask[i] != two->mask[i]) return (-1); } /* If we make it here, they're the same */ return (0); } nfs-ganesha-2.6.0/src/cidr/cidr_from_str.c000066400000000000000000000626171324272410200204320ustar00rootroot00000000000000/* * cidr_from_str() - Generate a CIDR structure from a string in addr/len * form. */ #include "config.h" #include #include #include /* I'm always stuffing debug printf's into here */ #include #include #include #include "../include/cidr.h" CIDR *cidr_from_str(const char *addr) { size_t alen; CIDR *toret, *ctmp; const char *pfx, *buf; char *buf2; /* strtoul() can't use a (const char *) */ int i, j; int pflen; unsigned long octet; int nocts, eocts; short foundpf, foundmask, nsect; alen = strlen(addr); /* There has to be *SOMETHING* to work with */ if (alen == 0) { errno = EINVAL; return (NULL); } /* And we know it can only contain a given set of chars */ buf = addr + strspn(addr, "0123456789abcdefABCDEFxX.:/in-rpt"); if (*buf != '\0') { errno = EINVAL; return (NULL); } toret = cidr_alloc(); /* First check if we're a PTR-style string */ /* * XXX This could be folded with *pfx; they aren't used in code paths * that overlap. I'm keeping them separate just to keep my sanity * though. */ buf = NULL; /* Handle the deprecated RFC1886 form of v6 PTR */ if ((alen >= 8) && strcasecmp(addr + alen - 8, ".ip6.int") == 0) { toret->proto = CIDR_IPV6; buf = addr + alen - 8; } if (buf != NULL || ((alen >= 5) && strcasecmp(addr + alen - 5, ".arpa") == 0)) { /* * Do all this processing here, instead of trying to intermix it * with the rest of the formats. This might lead to some code * duplication, but it'll be easier to read. */ if (buf == NULL) { /* If not set by .ip6.int above */ /* First, see what protocol it is */ if ((alen >= 9) && strncasecmp(addr + alen - 9, ".ip6", 3) == 0) { toret->proto = CIDR_IPV6; buf = addr + alen - 9; } else if ((alen >= 13) && strncasecmp(addr + alen - 13, ".in-addr", 7) == 0) { toret->proto = CIDR_IPV4; buf = addr + alen - 13; } else { /* Unknown */ cidr_free(toret); errno = EINVAL; return (NULL); } } /* * buf now points to the period after the last (first) bit of * address numbering in the PTR name. */ /* * Now convert based on that protocol. Note that we're going to * be slightly asymmetrical to the way cidr_to_str() works, in * how we handle the netmask. cidr_to_str() ignores it, and * treats the PTR-style output solely as host addresses. We'll * use the netmask bits to specify how much of the address is * given in the PTR we get. That is, if we get * "3.2.1.in-addr.arpa", we'll set a /24 netmask on the returned * result. This way, the calling program can tell the difference * between "3.2.1..." and "0.3.2.1..." if it really cares to. */ buf--; /* Step before the period */ if (toret->proto == CIDR_IPV4) { for (i = 11; i <= 14; /* */ ) { /* If we're before the beginning, we're done */ if (buf < addr) break; /* Step backward until we at the start of an octet */ while (isdigit(*buf) && buf >= addr) buf--; /* * Save that number (++i here to show that this octet is * now set. */ octet = strtoul(buf + 1, NULL, 10); if (octet > (unsigned long)0xff) { /* Bad octet! No biscuit! */ cidr_free(toret); errno = EINVAL; return (NULL); } toret->addr[++i] = octet; /* * Back up a step to get before the '.', and process the * next [previous] octet. If we were at the beginning of * the string already, the test at the top of the loop * will drop us out. */ buf--; } /* Too much? */ if (buf >= addr) { cidr_free(toret); errno = EINVAL; return (NULL); } /* * Now, what about the mask? We set the netmask bits to * describe how much information we've actually gotten, if we * didn't get all 4 octets. Because of the way .in-addr.arpa * works, the mask can only fall on an octet boundary, so we * don't need too many fancy tricks. 'i' is still set from * the above loop to whatever the last octet we filled in is, * so we don't even have to special case anything. */ for (j = 0; j <= i; j++) toret->mask[j] = 0xff; /* Done processing */ } else if (toret->proto == CIDR_IPV6) { /* * This processing happens somewhat similarly to IPV4 above, * the format is simplier, and we need to be a little * sneakier about the mask, since it can fall on a half-octet * boundary with .ip6.arpa format. */ for (i = 0; i <= 15; i++) { /* If we're before the beginning, we're done */ if (buf < addr) break; /* We better point at a number */ if (!isxdigit(*buf)) { /* Bad input */ cidr_free(toret); errno = EINVAL; return (NULL); } /* Save the current number */ octet = strtoul(buf, NULL, 16); if (octet > (unsigned long)0xff) { /* Bad octet! No biscuit! */ cidr_free(toret); errno = EINVAL; return (NULL); } toret->addr[i] = octet << 4; toret->mask[i] = 0xf0; /* If we're at the beginning of the string, we're thru */ if (buf == addr) { /* Shift back to skip error condition at end of loop */ buf--; break; } /* If we're not, stepping back should give us a period */ if (*--buf != '.') { /* Bad input */ cidr_free(toret); errno = EINVAL; return (NULL); } /* Stepping back again should give us a number */ if (!isxdigit(*--buf)) { /* Bad input */ cidr_free(toret); errno = EINVAL; return (NULL); } /* Save that one */ octet = strtoul(buf, NULL, 16); if (octet > (unsigned long)0xff) { /* Bad octet! No biscuit! */ cidr_free(toret); errno = EINVAL; return (NULL); } toret->addr[i] |= octet & 0x0f; toret->mask[i] |= 0x0f; /* * Step back and loop back around. If that last step * back moves us to before the beginning of the string, * the condition at the top of the loop will drop us out. */ while (*--buf == '.' && buf >= addr) /* nothing */ ; } /* Too much? */ if (buf >= addr) { cidr_free(toret); errno = EINVAL; return (NULL); } /* Mask is set in the loop for v6 */ } else { /* Shouldn't happen */ cidr_free(toret); errno = ENOENT; /* Bad choice of errno */ return (NULL); } /* Return the value we built up, and we're done! */ return (toret); /* NOTREACHED */ } buf = NULL; /* Done */ /* * It's not a PTR form, so find the '/' prefix marker if we can. We * support both prefix length and netmasks after the /, so flag if we * find a mask. */ foundpf = foundmask = 0; for (i = alen - 1; i >= 0; i--) { /* Handle both possible forms of netmasks */ if (addr[i] == '.' || addr[i] == ':') foundmask = 1; /* Are we at the beginning of the prefix? */ if (addr[i] == '/') { foundpf = 1; break; } } if (foundpf == 0) { /* We didn't actually find a prefix, so reset the foundmask */ foundmask = 0; /* * pfx is only used if foundpf==1, but set it to NULL here to * quiet gcc down. */ pfx = NULL; } else { /* Remember where the prefix is */ pfx = addr + i; if (foundmask == 0) { /* * If we didn't find a netmask, it may be that it's one of * the v4 forms without dots. Technically, it COULD be * expressed as a single (32-bit) number that happens to be * between 0 and 32 inclusive, so there's no way to be * ABSOLUTELY sure when we have a prefix length and not a * netmask. But, that would be a non-contiguous netmask, * which we don't attempt to support, so we can probably * safely ignore that case. So try a few things... */ /* If it's a hex or octal number, assume it's a mask */ if (pfx[1] == '0' && tolower(pfx[2]) == 'x') foundmask = 1; /* Hex */ else if (pfx[1] == '0') foundmask = 1; /* Oct */ else if (isdigit(pfx[1])) { /* * If we get here, it looks like a decimal number, and we * know there aren't any periods or colons in it, so if * it's valid, it can ONLY be a single 32-bit decimal * spanning the whole 4-byte v4 address range. If that's * true, it's GOTTA be a valid number, it's GOTTA reach * to the end of the strong, and it's GOTTA be at least * 2**31 and less than 2**32. */ octet = strtoul(pfx + 1, &buf2, 10); if (*buf2 == '\0' && octet >= (unsigned long)(1 << 31) && octet <= (unsigned long)0xffffffff) foundmask = 1; /* Valid! */ octet = 0; buf2 = NULL; /* Done */ } } } i = 0; /* Done */ /* * Now, let's figure out what kind of address this is. A v6 address * will contain a : within the first 5 characters ('0000:'), a v4 * address will have a . within the first 4 ('123.'), UNLESS it's * just a single number (in hex, octal, or decimal). Anything else * isn't an address we know anything about, so fail. */ if ((buf = strchr(addr, ':')) != NULL && (buf - addr) <= 5) toret->proto = CIDR_IPV6; else if ((buf = strchr(addr, '.')) != NULL && (buf - addr) <= 4) toret->proto = CIDR_IPV4; else { /* * Special v4 forms */ if (*addr == '0' && tolower(*(addr + 1)) == 'x') { /* Hex? */ buf = (addr + 2) + strspn(addr + 2, "0123456789abcdefABCDEF"); if (*buf == '\0' || *buf == '/') toret->proto = CIDR_IPV4; /* Yep */ } else if (*addr == '0') { /* Oct? */ /* (note: this also catches the [decimal] address '0' */ buf = (addr + 1) + strspn(addr + 1, "01234567"); if (*buf == '\0' || *buf == '/') toret->proto = CIDR_IPV4; /* Yep */ } else { /* Dec? */ buf = (addr) + strspn(addr, "0123456789"); if (*buf == '\0' || *buf == '/') toret->proto = CIDR_IPV4; /* Yep */ } /* Did we catch anything? */ if (toret->proto == 0) { /* Unknown */ cidr_free(toret); errno = EINVAL; return (NULL); } } buf = NULL; /* Done */ /* * So now we know what sort of address it is, we can go ahead and * have a parser for either. */ if (toret->proto == CIDR_IPV4) { /* * Parse a v4 address. Now, we're being a little tricksy here, * and parsing it from the end instead of from the front. */ /* * First, find out how many bits we have. We need to have 4 or * less... */ buf = strchr(addr, '.'); /* Through here, nsect counts dots */ for (nsect = 0; buf != NULL && (pfx != NULL ? buf < pfx : 1); buf = strchr(buf, '.')) { nsect++; /* One more section */ buf++; /* Move past . */ if (nsect > 3) { /* Bad! We can't have more than 4 sections... */ cidr_free(toret); errno = EINVAL; return (NULL); } } buf = NULL; /* Done */ nsect++; /* sects = dots+1 */ /* * First, initialize this so we can skip building the bits if we * don't have to. */ pflen = -1; /* * Initialize the first 12 octets of the address/mask to look * like a v6-mapped address. This is the correct info for those * octets to have if/when we decide to use this v4 address as a * v6 one. */ for (i = 0; i <= 9; i++) toret->addr[i] = 0; for (i = 10; i <= 11; i++) toret->addr[i] = 0xff; for (i = 0; i <= 11; i++) toret->mask[i] = 0xff; /* * Handle the prefix/netmask. If it's not set at all, slam it to * the maximum, and put us at the end of the string to start out. * Ditto if the '/' is the end of the string. */ if (foundpf == 0) { pflen = 32; i = alen - 1; } else if (foundpf == 1 && *(pfx + 1) == '\0') { pflen = 32; i = pfx - addr - 1; } /* * Or, if we found it, and it's a NETMASK, we need to parse it * just like an address. So, cheat a little and call ourself * recursively, and then just count the bits in our returned * address for the pflen. */ if (foundpf == 1 && foundmask == 1 && pflen == -1) { ctmp = cidr_from_str(pfx + 1); if (ctmp == NULL) { /* This shouldn't happen */ cidr_free(toret); return (NULL); /* Preserve errno */ } /* Stick it in the mask */ for (i = 0; i <= 11; i++) ctmp->mask[i] = 0; for (i = 12; i <= 15; i++) ctmp->mask[i] = ctmp->addr[i]; /* Get our prefix length */ pflen = cidr_get_pflen(ctmp); cidr_free(ctmp); if (pflen == -1) { /* Failed; probably non-contiguous */ cidr_free(toret); return (NULL); /* Preserve errno */ } /* And set us to before the '/' like below */ i = pfx - addr - 1; } /* * Finally, if we did find it and it's a normal prefix length, * just pull it it, parse it out, and set ourselves to the first * character before the / for the address reading */ if (foundpf == 1 && foundmask == 0 && pflen == -1) { pflen = (int)strtol(pfx + 1, NULL, 10); i = pfx - addr - 1; } /* * If pflen is set, we need to turn it into a mask for the bits. * XXX pflen actually should ALWAYS be set, so we might not need * to make this conditional at all... */ if (pflen > 0) { /* 0 < pflen <= 32 */ if (pflen < 0 || pflen > 32) { /* Always bad */ cidr_free(toret); errno = EINVAL; return (NULL); } /* * Now pflen is in the 0...32 range and thus good. Set it in * the structure. Note that memset zero'd the whole thing to * start. We ignore mask[<12] with v4 addresses normally, * but they're already set to all-1 anyway, since if we ever * DO care about them, that's the most appropriate thing for * them to be. * * This is a horribly grody set of macros. I'm only using * them here to test them out before using them in the v6 * section, where I'll need them more due to the sheer number * of clauses I'll have to get written. Here's the straight * code I had written that the macro should be writing for me * now: * * if(pflen>24) * for(j=24 ; jmask[15] |= 1<<(31-j); * if(pflen>16) * for(j=16 ; jmask[14] |= 1<<(23-j); * if(pflen>8) * for(j=8 ; jmask[13] |= 1<<(15-j); * if(pflen>0) * for(j=0 ; jmask[12] |= 1<<(7-j); */ #define UMIN(x,y) ((x)<(y)?(x):(y)) #define MASKNUM(x) (24-((15-x)*8)) #define WRMASKSET(x) \ if(pflen>MASKNUM(x)) \ for(j=MASKNUM(x) ; jmask[x] |= 1<<(MASKNUM(x)+7-j); WRMASKSET(15); WRMASKSET(14); WRMASKSET(13); WRMASKSET(12); #undef WRMASKET #undef MASKNUM #undef UMIN } /* Normal v4 prefix */ /* * Now we have 4 octets to grab. If any of 'em fail, or are * outside the 0...255 range, bomb. */ nocts = 0; /* Here, i should be before the /, but we may have multiple */ while (i > 0 && addr[i] == '/') i--; for ( /* i */ ; i >= 0; i--) { /* * As long as it's still a number or an 'x' (as in '0x'), * keep backing up. Could be hex, so don't just use * isdigit(). */ if ((isxdigit(addr[i]) || tolower(addr[i]) == 'x') && i > 0) continue; /* * It's no longer a number. So, grab the number we just * moved before. */ /* Cheat for "beginning-of-string" rather than "NaN" */ if (i == 0) i--; /* Theoretically, this can be in hex/oct/dec... */ if (addr[i + 1] == '0' && tolower(addr[i + 2]) == 'x') octet = strtoul(addr + i + 1, &buf2, 16); else if (addr[i + 1] == '0') octet = strtoul(addr + i + 1, &buf2, 8); else octet = strtoul(addr + i + 1, &buf2, 10); /* If buf isn't pointing at one of [./'\0'], it's screwed */ if (!(*buf2 == '.' || *buf2 == '/' || *buf2 == '\0')) { cidr_free(toret); errno = EINVAL; return (NULL); } buf2 = NULL; /* Done */ /* * Now, because of the way compressed IPv4 addresses work, * this number CAN be greater than 255, IF it's the last bit * in the address (the first bit we parse), in which case it * must be no bigger than needed to fill the unaccounted-for * 'slots' in the address. * * See * * for details. */ if ((nocts != 0 && octet > 255) || (nocts == 0 && octet > (0xffffffff >> (8 * (nsect - 1))))) { cidr_free(toret); errno = EINVAL; return (NULL); } /* Save the lower 8 bits into this octet */ toret->addr[15 - nocts++] = octet & 0xff; /* * If this is the 'last' piece of the address (the first we * process), and there are fewer than 4 pieces total, we need * to extend it out into additional fields. See above * reference. */ if (nocts == 1) { if (nsect <= 3) toret->addr[15 - nocts++] = (octet >> 8) & 0xff; if (nsect <= 2) toret->addr[15 - nocts++] = (octet >> 16) & 0xff; if (nsect == 1) toret->addr[15 - nocts++] = (octet >> 24) & 0xff; } /* * If we've got 4 of 'em, we're actually done. We got the * prefix above, so just return direct from here. */ if (nocts == 4) return (toret); } /* * If we get here, it failed to get all 4. That shouldn't * happen, since we catch proper abbreviated forms above. */ cidr_free(toret); errno = EINVAL; return (NULL); } else if (toret->proto == CIDR_IPV6) { /* * Parse a v6 address. Like the v4, we start from the end and * parse backward. However, to handle compressed form, if we hit * a ::, we drop off and start parsing from the beginning, * because at the end we'll then have a hole that is what the :: * is supposed to contain, which is already automagically 0 from * the memset() we did earlier. Neat! * * Initialize the prefix length */ pflen = -1; /* If no prefix was found, assume the max */ if (foundpf == 0) { pflen = 128; /* Stretch back to the end of the string */ i = alen - 1; } else if (foundpf == 1 && *(pfx + 1) == '\0') { pflen = 128; i = pfx - addr - 1; } /* * If we got a netmask, rather than a prefix length, parse it and * count the bits, like we did for v4. */ if (foundpf == 1 && foundmask == 1 && pflen == -1) { ctmp = cidr_from_str(pfx + 1); if (ctmp == NULL) { /* This shouldn't happen */ cidr_free(toret); return (NULL); /* Preserve errno */ } /* Stick it in the mask */ for (i = 0; i <= 15; i++) ctmp->mask[i] = ctmp->addr[i]; /* Get the prefix length */ pflen = cidr_get_pflen(ctmp); cidr_free(ctmp); if (pflen == -1) { /* Failed; probably non-contiguous */ cidr_free(toret); return (NULL); /* Preserve errno */ } /* And set us to before the '/' like below */ i = pfx - addr - 1; } /* Finally, the normal prefix case */ if (foundpf == 1 && foundmask == 0 && pflen == -1) { pflen = (int)strtol(pfx + 1, NULL, 10); i = pfx - addr - 1; } /* * Now, if we have a pflen, turn it into a mask. * XXX pflen actually should ALWAYS be set, so we might not need * to make this conditional at all... */ if (pflen > 0) { /* Better be 0...128 */ if (pflen < 0 || pflen > 128) { /* Always bad */ cidr_free(toret); errno = EINVAL; return (NULL); } /* * Now save the pflen. See comments on the similar code up in * the v4 section about the macros. */ #define UMIN(x,y) ((x)<(y)?(x):(y)) #define MASKNUM(x) (120-((15-x)*8)) #define WRMASKSET(x) \ if(pflen>MASKNUM(x)) \ for(j=MASKNUM(x) ; jmask[x] |= 1<<(MASKNUM(x)+7-j); WRMASKSET(15); WRMASKSET(14); WRMASKSET(13); WRMASKSET(12); WRMASKSET(11); WRMASKSET(10); WRMASKSET(9); WRMASKSET(8); WRMASKSET(7); WRMASKSET(6); WRMASKSET(5); WRMASKSET(4); WRMASKSET(3); WRMASKSET(2); WRMASKSET(1); WRMASKSET(0); #undef WRMASKET #undef MASKNUM #undef UMIN } /* * Now we have 16 octets to grab. If any of 'em fail, or are * outside the 0...0xff range, bomb. However, we MAY have a * v4-ish form, whether it's a formal v4 mapped/compat address, * or just a v4 address written in a v6 block. So, look for * .-separated octets, but there better be exactly 4 of them * before we hit a :. */ nocts = 0; /* Bump before / (or multiple /'s */ while (i > 0 && addr[i] == '/') i--; for ( /* i */ ; i >= 0; i--) { /* * First, check the . cases, and handle them all in one * place. These can only happen at the beginning, when we * have no octets yet, and if it happens at all, we need to * have 4 of them. */ if (nocts == 0 && addr[i] == '.') { i++; /* Shift back to after the '.' */ for ( /* i */ ; i > 0 && nocts < 4; i--) { /* This shouldn't happen except at the end */ if (addr[i] == ':' && nocts < 3) { cidr_free(toret); errno = EINVAL; return (NULL); } /* If it's not a . or :, move back 1 */ if (addr[i] != '.' && addr[i] != ':') continue; /* Should be a [decimal] octet right after here */ octet = strtoul(addr + i + 1, NULL, 10); /* Be sure */ if (octet > 255) { cidr_free(toret); errno = EINVAL; return (NULL); } /* Save it */ toret->addr[15 - nocts] = octet & 0xff; nocts++; /* And find the next octet */ } /* * At this point, 4 dotted-decimal octets should be * consumed. i has gone back one step past the : before * the decimal, so addr[i+1] should be the ':' that * preceeds them. Verify. */ if (nocts != 4 || addr[i + 1] != ':') { cidr_free(toret); errno = EINVAL; return (NULL); } } /* * Now we've either gotten 4 octets filled in from * dotted-decimal stuff, or we've filled in nothing and have * no dotted decimal. */ /* As long as it's not our separator, keep moving */ if (addr[i] != ':' && i > 0) continue; /* If it's a :, and our NEXT char is a : too, flee */ if (addr[i] == ':' && addr[i + 1] == ':') { /* * If i is 0, we're already at the beginning of the * string, so we can just return; we've already filled in * everything but the leading 0's, which are already * zero-filled from the memory */ if (i == 0) return (toret); /* Else, i!=0, and we break out */ break; } /* If it's not a number either... well, bad data */ if (!isxdigit(addr[i]) && addr[i] != ':' && i > 0) { cidr_free(toret); errno = EINVAL; return (NULL); } /* * It's no longer a number. So, grab the number we just * moved before. */ /* Cheat for "beginning-of-string" rather than "NaN" */ if (i == 0) i--; octet = strtoul(addr + i + 1, &buf2, 16); if (*buf2 != ':' && *buf2 != '/' && *buf2 != '\0') { /* Got something unexpected */ cidr_free(toret); errno = EINVAL; return (NULL); } buf2 = NULL; /* Remember, this is TWO octets */ if (octet > 0xffff) { cidr_free(toret); errno = EINVAL; return (NULL); } /* Save it */ toret->addr[15 - nocts] = octet & 0xff; nocts++; toret->addr[15 - nocts] = (octet >> 8) & 0xff; nocts++; /* If we've got all of 'em, just return from here. */ if (nocts == 16) return (toret); } /* * Now, if i is >=0 and we've got two :'s, jump around to the * front of the string and start parsing inward. */ if (i >= 0 && addr[i] == ':' && addr[i + 1] == ':') { /* Remember how many octets we put on the end */ eocts = nocts; /* Remember how far we were into the string */ j = i; /* Going this way, we do things a little differently */ i = 0; while (i < j) { /* * The first char better be a number. If it's not, bail * (a leading '::' was already handled in the loop above * by just returning). */ if (i == 0 && !isxdigit(addr[i])) { cidr_free(toret); errno = EINVAL; return (NULL); } /* * We should be pointing at the beginning of a digit * string now. Translate it into an octet. */ octet = strtoul(addr + i, &buf2, 16); if (*buf2 != ':' && *buf2 != '/' && *buf2 != '\0') { /* Got something unexpected */ cidr_free(toret); errno = EINVAL; return (NULL); } buf2 = NULL; /* Sanity (again, 2 octets) */ if (octet > 0xffff) { cidr_free(toret); errno = EINVAL; return (NULL); } /* Save it */ toret->addr[nocts - eocts] = (octet >> 8) & 0xff; nocts++; toret->addr[nocts - eocts] = octet & 0xff; nocts++; /* * Discussion: If we're in this code block, it's because * we hit a ::-compression while parsing from the end * backward. So, if we hit 15 octets here, it's an * error, because with the at-least-2 that were minimized, * that makes 17 total, which is too many. So, error * out. */ if (nocts == 15) { cidr_free(toret); return (NULL); } /* Now skip around to the end of this number */ while (isxdigit(addr[i]) && i < j) i++; /* * If i==j, we're back where we started. So we've filled * in all the leading stuff, and the struct is ready to * return. */ if (i == j) return (toret); /* * Else, there's more to come. We better be pointing at * a ':', else die. */ if (addr[i] != ':') { cidr_free(toret); return (NULL); } /* Skip past : */ i++; /* If we're at j now, we had a ':::', which is invalid */ if (i == j) { cidr_free(toret); return (NULL); } /* Head back around */ } } /* If we get here, it failed somewhere odd */ cidr_free(toret); errno = EINVAL; return (NULL); } else { /* Shouldn't happen */ cidr_free(toret); errno = ENOENT; /* Bad choice of errno */ return (NULL); } /* NOTREACHED */ errno = ENOENT; return (NULL); } nfs-ganesha-2.6.0/src/cidr/cidr_get.c000066400000000000000000000030321324272410200173400ustar00rootroot00000000000000/* * cidr_get - Get and return various semi-raw bits of info */ #include "config.h" #include #include #include #include "cidr.h" #include "abstract_mem.h" /* Get the prefix length */ int cidr_get_pflen(const CIDR * block) { int i, j; int foundnmh; int pflen; /* Where do we start? */ if (block->proto == CIDR_IPV4) i = 12; else if (block->proto == CIDR_IPV6) i = 0; else { errno = ENOENT; /* Bad errno */ return (-1); /* Unknown */ } /* * We're intentionally not supporting non-contiguous netmasks. So, * if we find one, bomb out. */ foundnmh = 0; pflen = 0; for ( /* i */ ; i <= 15; i++) { for (j = 7; j >= 0; j--) { if ((block->mask)[i] & (1 << j)) { /* * This is a network bit (1). If we've already seen a * host bit (0), we need to bomb. */ if (foundnmh == 1) { errno = EINVAL; return (-1); } pflen++; } else foundnmh = 1; /* A host bit */ } } /* If we get here, return the length */ return (pflen); } /* Get the address bits */ uint8_t *cidr_get_addr(const CIDR * addr) { uint8_t *toret; toret = gsh_calloc(16, sizeof(uint8_t)); /* Copy 'em in */ memcpy(toret, addr->addr, sizeof(addr->addr)); return (toret); } /* Get the netmask bits */ uint8_t *cidr_get_mask(const CIDR * addr) { uint8_t *toret; toret = gsh_calloc(16, sizeof(uint8_t)); /* Copy 'em in */ memcpy(toret, addr->mask, sizeof(addr->mask)); return (toret); } /* Get the protocol */ int cidr_get_proto(const CIDR * addr) { return (addr->proto); } nfs-ganesha-2.6.0/src/cidr/cidr_inaddr.c000066400000000000000000000076421324272410200200350ustar00rootroot00000000000000/* * Functions to convert to/from in[6]_addr structs */ #include "config.h" #include #include #include #include "abstract_mem.h" #include "cidr.h" /* Create a struct in_addr with the given v4 address */ struct in_addr *cidr_to_inaddr(const CIDR * addr, struct in_addr *uptr) { struct in_addr *toret; /* Better be a v4 address... */ if (addr->proto != CIDR_IPV4) { errno = EPROTOTYPE; return (NULL); } /* * Use the user's struct if possible, otherwise allocate one. It's * _their_ responsibility to give us the right type of struct to not * stomp all over the address space... */ toret = uptr; if (toret == NULL) toret = gsh_calloc(1, sizeof(struct in_addr)); /* Add 'em up and stuff 'em in */ toret->s_addr = ((addr->addr)[12] << 24) + ((addr->addr)[13] << 16) + ((addr->addr)[14] << 8) + ((addr->addr)[15]); /* * in_addr's are USUALLY used inside sockaddr_in's to do socket * stuff. The upshot of this is that they generally need to be in * network byte order. We'll do that transition here; if the user * wants to be different, they'll have to manually convert. */ toret->s_addr = htonl(toret->s_addr); return (toret); } /* Build up a CIDR struct from a given in_addr */ CIDR *cidr_from_inaddr(const struct in_addr * uaddr) { int i; CIDR *toret; in_addr_t taddr; toret = cidr_alloc(); toret->proto = CIDR_IPV4; /* * For IPv4, pretty straightforward, except that we need to jump * through a temp variable to convert into host byte order. */ taddr = ntohl(uaddr->s_addr); /* Mask these just to be safe */ toret->addr[15] = (taddr & 0xff); toret->addr[14] = ((taddr >> 8) & 0xff); toret->addr[13] = ((taddr >> 16) & 0xff); toret->addr[12] = ((taddr >> 24) & 0xff); /* Give it a single-host mask */ toret->mask[15] = toret->mask[14] = toret->mask[13] = toret->mask[12] = 0xff; /* Standard v4 overrides of addr and mask for mapped form */ for (i = 0; i <= 9; i++) toret->addr[i] = 0; for (i = 10; i <= 11; i++) toret->addr[i] = 0xff; for (i = 0; i <= 11; i++) toret->mask[i] = 0xff; /* That's it */ return (toret); } /* Create a struct in5_addr with the given v6 address */ struct in6_addr *cidr_to_in6addr(const CIDR * addr, struct in6_addr *uptr) { struct in6_addr *toret; int i; /* * Note: We're allowing BOTH IPv4 and IPv6 addresses to go through * this function. The reason is that this allows us to build up an * in6_addr struct to be used to connect to a v4 host (via a * v4-mapped address) through a v6 socket connection. A v4 * cidr_address, when built, has the upper bits of the address set * correctly for this to work. We don't support "compat"-mode * addresses here, though, and won't. */ if (addr->proto != CIDR_IPV6 && addr->proto != CIDR_IPV4) { errno = EPROTOTYPE; return (NULL); } /* Use their struct if they gave us one */ toret = uptr; if (toret == NULL) toret = gsh_calloc(1, sizeof(struct in6_addr)); /* * The in6_addr is defined to store it in 16 octets, just like we do. * But just to be safe, we're not going to stuff a giant copy in. * Most systems also use some union trickery to force alignment, but * we don't need to worry about that. * Now, this is defined to be in network byte order, which is * MSB-first. Since this is a structure of bytes, and we're filling * them in from the MSB onward ourself, we don't actually have to do * any conversions. */ for (i = 0; i <= 15; i++) toret->s6_addr[i] = addr->addr[i]; return (toret); } /* And create up a CIDR struct from a given in6_addr */ CIDR *cidr_from_in6addr(const struct in6_addr * uaddr) { int i; CIDR *toret; toret = cidr_alloc(); toret->proto = CIDR_IPV6; /* * For v6, just iterate over the arrays and return. Set all 1's in * the mask while we're at it, since this is a single host. */ for (i = 0; i <= 15; i++) { toret->addr[i] = uaddr->s6_addr[i]; toret->mask[i] = 0xff; } return (toret); } nfs-ganesha-2.6.0/src/cidr/cidr_mem.c000066400000000000000000000010001324272410200173300ustar00rootroot00000000000000/* * Various libcidr memory-related functions */ #include "config.h" #include #include #include #include "abstract_mem.h" #include "cidr.h" /* Allocate a struct cidr_addr */ CIDR *cidr_alloc(void) { return gsh_calloc(1, sizeof(CIDR)); } /* Duplicate a CIDR */ CIDR *cidr_dup(const CIDR * src) { CIDR *toret; toret = cidr_alloc(); memcpy(toret, src, sizeof(CIDR)); return (toret); } /* Free a struct cidr_addr */ void cidr_free(CIDR * tofree) { gsh_free(tofree); } nfs-ganesha-2.6.0/src/cidr/cidr_misc.c000066400000000000000000000011201324272410200175100ustar00rootroot00000000000000/* * Misc pieces */ #include "config.h" #include "cidr.h" static const char *__libcidr_version = CIDR_VERSION_STR; /* Library version info */ const char *cidr_version(void) { return (__libcidr_version); } /* Is a CIDR a v4-mapped IPv6 address? */ int cidr_is_v4mapped(const CIDR * addr) { int i; if (addr->proto != CIDR_IPV6) return (-1); /* First 10 octets should be 0 */ for (i = 0; i <= 9; i++) if (addr->addr[i] != 0) return (-1); /* Next 2 should be 0xff */ for (i = 10; i <= 11; i++) if (addr->addr[i] != 0xff) return (-1); /* Then it is */ return (0); } nfs-ganesha-2.6.0/src/cidr/cidr_net.c000066400000000000000000000040771324272410200173610ustar00rootroot00000000000000/* * Functions to generate various networks based on a CIDR */ #include "config.h" #include #include #include #include "cidr.h" #include "abstract_mem.h" /* Get the CIDR's immediate supernet */ CIDR *cidr_net_supernet(const CIDR * addr) { int i, j; int pflen; CIDR *toret; /* If it's already a /0 in its protocol, return nothing */ pflen = cidr_get_pflen(addr); if (pflen == 0) { errno = 0; return (NULL); } toret = cidr_dup(addr); if (toret == NULL) return (NULL); /* Preserve errno */ /* Chop a bit off the netmask */ /* This gets the last network bit */ if (toret->proto == CIDR_IPV4) pflen += 96; pflen--; i = pflen / 8; j = 7 - (pflen % 8); /* Make that bit a host bit */ (toret->mask)[i] &= ~(1 << j); /* * Now zero out the host bits in the addr. Do this manually instead * of calling cidr_addr_network() to save some extra copies and * allocationss and so forth. */ for ( /* i */ ; i <= 15; i++) { for ( /* j */ ; j >= 0; j--) (toret->addr)[i] &= ~(1 << j); j = 7; } /* And send it back */ return (toret); } /* Get the CIDR's two children */ CIDR **cidr_net_subnets(const CIDR * addr) { int i, j; int pflen; CIDR **toret; /* You can't split a host address! */ pflen = cidr_get_pflen(addr); if ((addr->proto == CIDR_IPV4 && pflen == 32) || (addr->proto == CIDR_IPV6 && pflen == 128)) { errno = 0; return (NULL); } toret = gsh_calloc(2, sizeof(CIDR *)); /* Get a blank-ish slate for the first kid */ toret[0] = cidr_addr_network(addr); if (toret[0] == NULL) { gsh_free(toret); return (NULL); /* Preserve errno */ } /* Find its first host bit */ if (toret[0]->proto == CIDR_IPV4) pflen += 96; i = pflen / 8; j = 7 - (pflen % 8); /* Make it a network bit */ (toret[0])->mask[i] |= 1 << j; /* Now dup the second kid off that */ toret[1] = cidr_dup(toret[0]); if (toret[1] == NULL) { cidr_free(toret[0]); gsh_free(toret); return (NULL); /* Preserve errno */ } /* And set that first host bit */ (toret[1])->addr[i] |= 1 << j; /* Return the pair */ return (toret); } nfs-ganesha-2.6.0/src/cidr/cidr_num.c000066400000000000000000000016661324272410200173730ustar00rootroot00000000000000/* * Show some numbers */ #include "config.h" #include #include #include "cidr.h" #include "cidr_pow2_p.h" /* Number of total addresses in a given prefix length */ const char *cidr_numaddr_pflen(int pflen) { if (pflen < 0 || pflen > 128) { errno = EINVAL; return (NULL); } return (__cidr_pow2[128 - pflen]); } /* Addresses in a CIDR block */ const char *cidr_numaddr(const CIDR * addr) { int pflen; pflen = cidr_get_pflen(addr); if (addr->proto == CIDR_IPV4) pflen += 96; return (cidr_numaddr_pflen(pflen)); } /* Hosts in a prefix length */ const char *cidr_numhost_pflen(int pflen) { if (pflen < 0 || pflen > 128) { errno = EINVAL; return (NULL); } return (__cidr_pow2m2[128 - pflen]); } /* Addresses in a CIDR block */ const char *cidr_numhost(const CIDR * addr) { int pflen; pflen = cidr_get_pflen(addr); if (addr->proto == CIDR_IPV4) pflen += 96; return (cidr_numhost_pflen(pflen)); } nfs-ganesha-2.6.0/src/cidr/cidr_pow2_p.h000066400000000000000000000203221324272410200177750ustar00rootroot00000000000000/* * Some lookup tables of powers of 2, used for describing the number of * hosts or networks in a given group. * XXX This isn't really well suited for localization... */ #ifndef __LIBCIDR__POW2_P_H #define __LIBCIDR__POW2_P_H /* Powers of two */ static const char *__cidr_pow2[] = { "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1,024", "2,048", "4,096", "8,192", "16,384", "32,768", "65,536", "131,072", "262,144", "524,288", "1,048,576", "2,097,152", "4,194,304", "8,388,608", "16,777,216", "33,554,432", "67,108,864", "134,217,728", "268,435,456", "536,870,912", "1,073,741,824", "2,147,483,648", "4,294,967,296", "8,589,934,592", "17,179,869,184", "34,359,738,368", "68,719,476,736", "137,438,953,472", "274,877,906,944", "549,755,813,888", "1,099,511,627,776", "2,199,023,255,552", "4,398,046,511,104", "8,796,093,022,208", "17,592,186,044,416", "35,184,372,088,832", "70,368,744,177,664", "140,737,488,355,328", "281,474,976,710,656", "562,949,953,421,312", "1,125,899,906,842,624", "2,251,799,813,685,248", "4,503,599,627,370,496", "9,007,199,254,740,992", "18,014,398,509,481,984", "36,028,797,018,963,968", "72,057,594,037,927,936", "144,115,188,075,855,872", "288,230,376,151,711,744", "576,460,752,303,423,488", "1,152,921,504,606,846,976", "2,305,843,009,213,693,952", "4,611,686,018,427,387,904", "9,223,372,036,854,775,808", "18,446,744,073,709,551,616", "36,893,488,147,419,103,232", "73,786,976,294,838,206,464", "147,573,952,589,676,412,928", "295,147,905,179,352,825,856", "590,295,810,358,705,651,712", "1,180,591,620,717,411,303,424", "2,361,183,241,434,822,606,848", "4,722,366,482,869,645,213,696", "9,444,732,965,739,290,427,392", "18,889,465,931,478,580,854,784", "37,778,931,862,957,161,709,568", "75,557,863,725,914,323,419,136", "151,115,727,451,828,646,838,272", "302,231,454,903,657,293,676,544", "604,462,909,807,314,587,353,088", "1,208,925,819,614,629,174,706,176", "2,417,851,639,229,258,349,412,352", "4,835,703,278,458,516,698,824,704", "9,671,406,556,917,033,397,649,408", "19,342,813,113,834,066,795,298,816", "38,685,626,227,668,133,590,597,632", "77,371,252,455,336,267,181,195,264", "154,742,504,910,672,534,362,390,528", "309,485,009,821,345,068,724,781,056", "618,970,019,642,690,137,449,562,112", "1,237,940,039,285,380,274,899,124,224", "2,475,880,078,570,760,549,798,248,448", "4,951,760,157,141,521,099,596,496,896", "9,903,520,314,283,042,199,192,993,792", "19,807,040,628,566,084,398,385,987,584", "39,614,081,257,132,168,796,771,975,168", "79,228,162,514,264,337,593,543,950,336", "158,456,325,028,528,675,187,087,900,672", "316,912,650,057,057,350,374,175,801,344", "633,825,300,114,114,700,748,351,602,688", "1,267,650,600,228,229,401,496,703,205,376", "2,535,301,200,456,458,802,993,406,410,752", "5,070,602,400,912,917,605,986,812,821,504", "10,141,204,801,825,835,211,973,625,643,008", "20,282,409,603,651,670,423,947,251,286,016", "40,564,819,207,303,340,847,894,502,572,032", "81,129,638,414,606,681,695,789,005,144,064", "162,259,276,829,213,363,391,578,010,288,128", "324,518,553,658,426,726,783,156,020,576,256", "649,037,107,316,853,453,566,312,041,152,512", "1,298,074,214,633,706,907,132,624,082,305,024", "2,596,148,429,267,413,814,265,248,164,610,048", "5,192,296,858,534,827,628,530,496,329,220,096", "10,384,593,717,069,655,257,060,992,658,440,192", "20,769,187,434,139,310,514,121,985,316,880,384", "41,538,374,868,278,621,028,243,970,633,760,768", "83,076,749,736,557,242,056,487,941,267,521,536", "166,153,499,473,114,484,112,975,882,535,043,072", "332,306,998,946,228,968,225,951,765,070,086,144", "664,613,997,892,457,936,451,903,530,140,172,288", "1,329,227,995,784,915,872,903,807,060,280,344,576", "2,658,455,991,569,831,745,807,614,120,560,689,152", "5,316,911,983,139,663,491,615,228,241,121,378,304", "10,633,823,966,279,326,983,230,456,482,242,756,608", "21,267,647,932,558,653,966,460,912,964,485,513,216", "42,535,295,865,117,307,932,921,825,928,971,026,432", "85,070,591,730,234,615,865,843,651,857,942,052,864", "170,141,183,460,469,231,731,687,303,715,884,105,728", "340,282,366,920,938,463,463,374,607,431,768,211,456" }; /* Powers of 2 minus two; hosts in a subnet with this many host bit */ static const char *__cidr_pow2m2[] = { "1", /* Special */ "2", /* Special */ "2", "6", "14", "30", "62", "126", "254", "510", "1,022", "2,046", "4,094", "8,190", "16,382", "32,766", "65,534", "131,070", "262,142", "524,286", "1,048,574", "2,097,150", "4,194,302", "8,388,606", "16,777,214", "33,554,430", "67,108,862", "134,217,726", "268,435,454", "536,870,910", "1,073,741,822", "2,147,483,646", "4,294,967,294", "8,589,934,590", "17,179,869,182", "34,359,738,366", "68,719,476,734", "137,438,953,470", "274,877,906,942", "549,755,813,886", "1,099,511,627,774", "2,199,023,255,550", "4,398,046,511,102", "8,796,093,022,206", "17,592,186,044,414", "35,184,372,088,830", "70,368,744,177,662", "140,737,488,355,326", "281,474,976,710,654", "562,949,953,421,310", "1,125,899,906,842,622", "2,251,799,813,685,246", "4,503,599,627,370,494", "9,007,199,254,740,990", "18,014,398,509,481,982", "36,028,797,018,963,966", "72,057,594,037,927,934", "144,115,188,075,855,870", "288,230,376,151,711,742", "576,460,752,303,423,486", "1,152,921,504,606,846,974", "2,305,843,009,213,693,950", "4,611,686,018,427,387,902", "9,223,372,036,854,775,806", "18,446,744,073,709,551,614", "36,893,488,147,419,103,230", "73,786,976,294,838,206,462", "147,573,952,589,676,412,926", "295,147,905,179,352,825,854", "590,295,810,358,705,651,710", "1,180,591,620,717,411,303,422", "2,361,183,241,434,822,606,846", "4,722,366,482,869,645,213,694", "9,444,732,965,739,290,427,390", "18,889,465,931,478,580,854,782", "37,778,931,862,957,161,709,566", "75,557,863,725,914,323,419,134", "151,115,727,451,828,646,838,270", "302,231,454,903,657,293,676,542", "604,462,909,807,314,587,353,086", "1,208,925,819,614,629,174,706,174", "2,417,851,639,229,258,349,412,350", "4,835,703,278,458,516,698,824,702", "9,671,406,556,917,033,397,649,406", "19,342,813,113,834,066,795,298,814", "38,685,626,227,668,133,590,597,630", "77,371,252,455,336,267,181,195,262", "154,742,504,910,672,534,362,390,526", "309,485,009,821,345,068,724,781,054", "618,970,019,642,690,137,449,562,110", "1,237,940,039,285,380,274,899,124,222", "2,475,880,078,570,760,549,798,248,446", "4,951,760,157,141,521,099,596,496,894", "9,903,520,314,283,042,199,192,993,790", "19,807,040,628,566,084,398,385,987,582", "39,614,081,257,132,168,796,771,975,166", "79,228,162,514,264,337,593,543,950,334", "158,456,325,028,528,675,187,087,900,670", "316,912,650,057,057,350,374,175,801,342", "633,825,300,114,114,700,748,351,602,686", "1,267,650,600,228,229,401,496,703,205,374", "2,535,301,200,456,458,802,993,406,410,750", "5,070,602,400,912,917,605,986,812,821,502", "10,141,204,801,825,835,211,973,625,643,006", "20,282,409,603,651,670,423,947,251,286,014", "40,564,819,207,303,340,847,894,502,572,030", "81,129,638,414,606,681,695,789,005,144,062", "162,259,276,829,213,363,391,578,010,288,126", "324,518,553,658,426,726,783,156,020,576,254", "649,037,107,316,853,453,566,312,041,152,510", "1,298,074,214,633,706,907,132,624,082,305,022", "2,596,148,429,267,413,814,265,248,164,610,046", "5,192,296,858,534,827,628,530,496,329,220,094", "10,384,593,717,069,655,257,060,992,658,440,190", "20,769,187,434,139,310,514,121,985,316,880,382", "41,538,374,868,278,621,028,243,970,633,760,766", "83,076,749,736,557,242,056,487,941,267,521,534", "166,153,499,473,114,484,112,975,882,535,043,070", "332,306,998,946,228,968,225,951,765,070,086,142", "664,613,997,892,457,936,451,903,530,140,172,286", "1,329,227,995,784,915,872,903,807,060,280,344,574", "2,658,455,991,569,831,745,807,614,120,560,689,150", "5,316,911,983,139,663,491,615,228,241,121,378,302", "10,633,823,966,279,326,983,230,456,482,242,756,606", "21,267,647,932,558,653,966,460,912,964,485,513,214", "42,535,295,865,117,307,932,921,825,928,971,026,430", "85,070,591,730,234,615,865,843,651,857,942,052,862", "170,141,183,460,469,231,731,687,303,715,884,105,726", "340,282,366,920,938,463,463,374,607,431,768,211,454" }; #endif /* __LIBCIDR__POW2_P_H */ nfs-ganesha-2.6.0/src/cidr/cidr_to_str.c000066400000000000000000000235041324272410200201010ustar00rootroot00000000000000/* * cidr_to_str() - Generate a textual representation of the given CIDR * subnet. */ #include "config.h" #include #include #include #include #include "cidr.h" #include "abstract_mem.h" char *cidr_to_str(const CIDR * block, int flags) { int i; int zst, zcur, zlen, zmax; short pflen; short lzer; /* Last zero */ char *toret; char tmpbuf[128]; /* We shouldn't need more than ~5 anywhere */ CIDR *nmtmp; char *nmstr; int nmflags; uint8_t moct; uint16_t v6sect; /* Just in case */ if (block->proto == CIDR_NOPROTO) { errno = EINVAL; return (NULL); } /* * Sanity: If we have both ONLYADDR and ONLYPFLEN, we really don't * have anything to *DO*... */ if ((flags & CIDR_ONLYADDR) && (flags & CIDR_ONLYPFLEN)) { errno = EINVAL; return (NULL); } /* * Now, in any case, there's a maximum length for any address, which * is the completely expanded form of a v6-{mapped,compat} address * with a netmask instead of a prefix. That's 8 pieces of 4 * characters each (32), separated by :'s (+7=39), plus the slash * (+1=40), plus another separated-8*4 (+39=79), plus the trailing * null (+1=80). We'll just allocate 128 for kicks. * * I'm not, at this time anyway, going to try and allocate only and * exactly as much as we need for any given address. Whether * consumers of the library can count on this behavior... well, I * haven't decided yet. Lemme alone. */ toret = gsh_calloc(1, 128); /* * If it's a v4 address, we mask off everything but the last 4 * octets, and just proceed from there. */ if ((block->proto == CIDR_IPV4 && !(flags & CIDR_FORCEV6)) || (flags & CIDR_FORCEV4)) { /* First off, creating the in-addr.arpa form is special */ if (flags & CIDR_REVERSE) { /* * Build the d.c.b.a.in-addr.arpa form. Note that we ignore * flags like CIDR_VERBOSE and the like here, since they lead * to non-valid reverse paths (or at least, paths that no DNS * implementation will look for). So it pretty much always * looks exactly the same. Also, we don't mess with dealing * with netmaks or anything here; we just assume it's a * host address, and treat it as such. */ sprintf(toret, "%d.%d.%d.%d.in-addr.arpa", block->addr[15], block->addr[14], block->addr[13], block->addr[12]); return (toret); } /* Are we bothering to show the address? */ if (!(flags & CIDR_ONLYPFLEN)) { /* If we're USEV6'ing, add whatever prefixes we need */ if (flags & CIDR_USEV6) { if (flags & CIDR_NOCOMPACT) { if (flags & CIDR_VERBOSE) strcat(toret, "0000:0000:0000:0000:0000:"); else strcat(toret, "0:0:0:0:0:"); } else strcat(toret, "::"); if (flags & CIDR_USEV4COMPAT) { if (flags & CIDR_NOCOMPACT) { if (flags & CIDR_VERBOSE) strcat(toret, "0000:"); else strcat(toret, "0:"); } } else strcat(toret, "ffff:"); } /* USEV6 */ /* Now, slap on the v4 address */ for (i = 12; i <= 15; i++) { sprintf(tmpbuf, "%u", (block->addr)[i]); strcat(toret, tmpbuf); if (i < 15) strcat(toret, "."); } } /* ! ONLYPFLEN */ /* Are we bothering to show the pf/mask? */ if (!(flags & CIDR_ONLYADDR)) { /* * And the prefix/netmask. Don't show the '/' if we're only * showing the pflen/mask. */ if (!(flags & CIDR_ONLYPFLEN)) strcat(toret, "/"); /* Which are we showing? */ if (flags & CIDR_NETMASK) { /* * In this case, we can just print out like the address * above. */ for (i = 12; i <= 15; i++) { moct = (block->mask)[i]; if (flags & CIDR_WILDCARD) moct = ~(moct); sprintf(tmpbuf, "%u", moct); strcat(toret, tmpbuf); if (i < 15) strcat(toret, "."); } } else { /* * For this, iterate over each octet, * then each bit within the octet. */ pflen = cidr_get_pflen(block); if (pflen == -1) { gsh_free(toret); return (NULL); /* Preserve errno */ } /* Special handling for forced modes */ if (block->proto == CIDR_IPV6 && (flags & CIDR_FORCEV4)) pflen -= 96; sprintf(tmpbuf, "%u", (flags & CIDR_USEV6) ? pflen + 96 : pflen); strcat(toret, tmpbuf); } } /* ! ONLYADDR */ /* That's it for a v4 address, in any of our forms */ } else if ((block->proto == CIDR_IPV6 && !(flags & CIDR_FORCEV4)) || (flags & CIDR_FORCEV6)) { /* First off, creating the .ip6.arpa form is special */ if (flags & CIDR_REVERSE) { /* * Build the ...ip6.arpa form. See notes in the CIDR_REVERSE * section of PROTO_IPV4 above for various notes. */ sprintf(toret, "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." "%x.%x.%x.%x.%x.ip6.arpa", block->addr[15] & 0x0f, block->addr[15] >> 4, block->addr[14] & 0x0f, block->addr[14] >> 4, block->addr[13] & 0x0f, block->addr[13] >> 4, block->addr[12] & 0x0f, block->addr[12] >> 4, block->addr[11] & 0x0f, block->addr[11] >> 4, block->addr[10] & 0x0f, block->addr[10] >> 4, block->addr[9] & 0x0f, block->addr[9] >> 4, block->addr[8] & 0x0f, block->addr[8] >> 4, block->addr[7] & 0x0f, block->addr[7] >> 4, block->addr[6] & 0x0f, block->addr[6] >> 4, block->addr[5] & 0x0f, block->addr[5] >> 4, block->addr[4] & 0x0f, block->addr[4] >> 4, block->addr[3] & 0x0f, block->addr[3] >> 4, block->addr[2] & 0x0f, block->addr[2] >> 4, block->addr[1] & 0x0f, block->addr[1] >> 4, block->addr[0] & 0x0f, block->addr[0] >> 4); return (toret); } /* Are we showing the address part? */ if (!(flags & CIDR_ONLYPFLEN)) { /* It's a simple, boring, normal v6 address */ /* First, find the longest string of 0's, if there is one */ zst = zcur = -1; zlen = zmax = 0; for (i = 0; i <= 15; i += 2) { if (block->addr[i] == 0 && block->addr[i + 1] == 0) { /* This section is zero */ if (zcur != -1) { /* We're already in a block of 0's */ zlen++; } else { /* Starting a new block */ zcur = i; zlen = 1; } } else { /* This section is non-zero */ if (zcur != -1) { /* * We were in 0's. See if we set a new record, * and if we did, note it and move on. */ if (zlen > zmax) { zst = zcur; zmax = zlen; } /* We're out of 0's, so reset start */ zcur = -1; } } } /* * If zcur is !=-1, we were in 0's when the loop ended. Redo * the "if we have a record, update" logic. */ if (zcur != -1 && zlen > zmax) { zst = zcur; zmax = zlen; } /* * Now, what makes it HARD is the options we have. To make * some things simpler, we'll take two octets at a time for * our run through. */ lzer = 0; for (i = 0; i <= 15; i += 2) { /* * Start with a cheat; if this begins our already-found * longest block of 0's, and we're not NOCOMPACT'ing, * stick in a ::, increment past them, and keep on * playing. */ if (i == zst && !(flags & CIDR_NOCOMPACT)) { strcat(toret, "::"); i += (zmax * 2) - 2; lzer = 1; continue; } /* * First, if we're not the first set, we may need a : * before us. If we're not compacting, we always want * it. If we ARE compacting, we want it unless the * previous octet was a 0 that we're minimizing. */ if (i != 0 && ((flags & CIDR_NOCOMPACT) || lzer == 0)) strcat(toret, ":"); lzer = 0; /* Reset */ /* * From here on, we no longer have to worry about * CIDR_NOCOMPACT. */ /* Combine the pair of octets into one number */ v6sect = 0; v6sect |= (block->addr)[i] << 8; v6sect |= (block->addr)[i + 1]; /* * If we're being VERBOSE, use leading 0's. Otherwise, * only use as many digits as we need. */ if (flags & CIDR_VERBOSE) sprintf(tmpbuf, "%.4x", v6sect); else sprintf(tmpbuf, "%x", v6sect); strcat(toret, tmpbuf); /* And loop back around to the next 2-octet set */ } /* for(each 16-bit set) */ } /* ! ONLYPFLEN */ /* Prefix/netmask */ if (!(flags & CIDR_ONLYADDR)) { /* Only show the / if we're not showing just the prefix */ if (!(flags & CIDR_ONLYPFLEN)) strcat(toret, "/"); if (flags & CIDR_NETMASK) { /* * We already wrote how to build the whole v6 form, so * just call ourselves recurively for this. */ nmtmp = cidr_alloc(); nmtmp->proto = block->proto; for (i = 0; i <= 15; i++) if (flags & CIDR_WILDCARD) nmtmp->addr[i] = ~(block->mask[i]); else nmtmp->addr[i] = block->mask[i]; /* * Strip flags: * - CIDR_NETMASK would make us recurse forever. * - CIDR_ONLYPFLEN would not show the address bit, which * is the part we want here. * Add flag CIDR_ONLYADDR because that's the bit we care * about. */ nmflags = flags; nmflags &= ~(CIDR_NETMASK) & ~(CIDR_ONLYPFLEN); nmflags |= CIDR_ONLYADDR; nmstr = cidr_to_str(nmtmp, nmflags); cidr_free(nmtmp); if (nmstr == NULL) { gsh_free(toret); return (NULL); /* Preserve errno */ } /* No need to strip the prefix, it doesn't have it */ /* Just add it on */ strcat(toret, nmstr); gsh_free(nmstr); } else { /* Just figure the and show prefix length */ pflen = cidr_get_pflen(block); if (pflen == -1) { gsh_free(toret); return (NULL); /* Preserve errno */ } /* Special handling for forced modes */ if (block->proto == CIDR_IPV4 && (flags & CIDR_FORCEV6)) pflen += 96; sprintf(tmpbuf, "%u", pflen); strcat(toret, tmpbuf); } } /* ! ONLYADDR */ } else { /* Well, *I* dunno what the fuck it is */ gsh_free(toret); errno = ENOENT; /* Bad choice of errno */ return (NULL); } /* Give back the string */ return (toret); } nfs-ganesha-2.6.0/src/cmake/000077500000000000000000000000001324272410200155555ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/cmake/COPYING-CMAKE-SCRIPTS000066400000000000000000000043431324272410200205570ustar00rootroot00000000000000NFS-Ganesha clarifications: The conditions set below apply only to the Cmake specific files in this directory. They do not apply to the NFS-ganesha project as a whole. See the top level licensing documentation for details about NFS-Ganesha licensing. Some scripts in this directory are a combination of modules copied from other cmake projects, net forums, or HOWTOs. Any files that were copied were, to the best of our ability, audited to have a proper open source license or come from a project with an appropriate open source license. Where the author placed a license clause in the cmake file, this has been preserved. If a file in this directory does not have a license notice attached, the following Cmake appropriate license and disclaimer applies. This is for the convenience of other developers who wish to use these files in their own open source projects. ---------------- Cmake modules and scripts license ------------------- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nfs-ganesha-2.6.0/src/cmake/build_configurations/000077500000000000000000000000001324272410200217665ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/cmake/build_configurations/bsd10.cmake000066400000000000000000000003351324272410200237020ustar00rootroot00000000000000# Only build VFS fsal for FreeBSD 10.1 set(USE_FSAL_PROXY OFF) set(USE_FSAL_CEPH OFF) set(USE_FSAL_GPFS OFF) set(_MSPAC_SUPPORT OFF) set(USE_9P OFF) set(USE_DBUS OFF) message(STATUS "Building BSD 10.1 configuration") nfs-ganesha-2.6.0/src/cmake/build_configurations/debian.cmake000066400000000000000000000002031324272410200242050ustar00rootroot00000000000000# Turn on everything in the options for a complete build set(USE_DBUS ON) set(USE_ADMIN_TOOLS ON) message(STATUS "Building DPKG") nfs-ganesha-2.6.0/src/cmake/build_configurations/everything.cmake000066400000000000000000000004601324272410200251540ustar00rootroot00000000000000# Turn on everything in the options for a complete build set(PROXY_HANDLE_MAPPING ON) set(_NO_XATTRD OFF) set(USE_DBUS ON) set(USE_CB_SIMULATOR ON) set(USE_FSAL_XFS ON) set(USE_FSAL_CEPH ON) set(USE_FSAL_RGW ON) set(USE_FSAL_GLUSTER ON) set(USE_TOOL_MULTILOCK ON) message(STATUS "Building everything") nfs-ganesha-2.6.0/src/cmake/build_configurations/gpfs.cmake000066400000000000000000000005131324272410200237260ustar00rootroot00000000000000 # where `make install` will place files set(CMAKE_PREFIX_PATH "/usr/") # FSAL's to build set(USE_FSAL_GPFS ON) set(USE_FSAL_VFS ON) set(USE_FSAL_PROXY ON) set(USE_DBUS ON) # Disable FSAL's we don't use set(USE_FSAL_CEPH OFF) set(_MSPAC_SUPPORT OFF) set(USE_9P OFF) message(STATUS "Building gpfs_vfs_pnfs_only configuration") nfs-ganesha-2.6.0/src/cmake/build_configurations/rpmbuild.cmake000066400000000000000000000001311324272410200246010ustar00rootroot00000000000000# Turn on everything in the options for a complete build message(STATUS "Building RPM") nfs-ganesha-2.6.0/src/cmake/build_configurations/vfs_only.cmake000066400000000000000000000003431324272410200246270ustar00rootroot00000000000000# Only build VFS fsal and other useful options set(USE_FSAL_PROXY OFF) set(USE_FSAL_CEPH OFF) set(USE_FSAL_GPFS OFF) set(_MSPAC_SUPPORT OFF) set(USE_9P OFF) set(USE_DBUS ON) message(STATUS "Building vfs_only configuration") nfs-ganesha-2.6.0/src/cmake/cpack_config.cmake000066400000000000000000000013431324272410200211660ustar00rootroot00000000000000# A few CPack related variables set(CPACK_PACKAGE_NAME "nfs-ganesha" ) set(CPACK_PACKAGE_VERSION "${GANESHA_VERSION}" ) set(CPACK_PACKAGE_VENDOR "NFS-Ganesha Project") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "NFS-Ganesha - A NFS Server runnning in user space") # CPack's debian stuff SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "nfs-ganesha-devel@lists.sourceforge.net") set(CPACK_RPM_COMPONENT_INSTALL OFF) set(CPACK_COMPONENTS_IGNORE_GROUPS "IGNORE") # Tell CPack the kind of packages to be generated set(CPACK_GENERATOR "TGZ") set(CPACK_SOURCE_GENERATOR "TGZ") set(CPACK_SOURCE_IGNORE_FILES "/.git/;/.gitignore/;/.bzr/;~$;${CPACK_SOURCE_IGNORE_FILES}") set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") nfs-ganesha-2.6.0/src/cmake/gitdesc_from_path.sh000077500000000000000000000002321324272410200215720ustar00rootroot00000000000000#!/bin/sh res=`echo $1 | sed -e 's/^.*_desc_//g' | sed -e 's/-[0-9]*\.[0-9]*\.[0-9]*$//g'` if [ -z "$res" ] ; then echo "NO-GIT" else echo $res fi nfs-ganesha-2.6.0/src/cmake/githead_from_path.sh000077500000000000000000000002151324272410200215560ustar00rootroot00000000000000#!/bin/sh res=`echo $1 | sed -e 's/[_-]/\n/g' | grep git | sed -e 's/git//g'` if [ -z "$res" ] ; then echo "NOT-GIT" else echo $res fi nfs-ganesha-2.6.0/src/cmake/maintainer_mode.cmake000066400000000000000000000042121324272410200217110ustar00rootroot00000000000000SET( CMAKE_CXX_FLAGS_MAINTAINER "-Wall -Wabi" CACHE STRING "Flags used by the C++ compiler during maintainer builds." FORCE ) SET( CMAKE_C_FLAGS_MAINTAINER "-Werror -Wall -Wimplicit -Wformat -Wmissing-braces -Wreturn-type -Wunused-variable -Wuninitialized -Wno-pointer-sign -Wno-strict-aliasing" CACHE STRING "Flags used by the C compiler during maintainer builds." FORCE ) SET( CMAKE_EXE_LINKER_FLAGS_MAINTAINER CACHE STRING "Flags used for linking binaries during maintainer builds." FORCE ) SET( CMAKE_SHARED_LINKER_FLAGS_MAINTAINER CACHE STRING "Flags used by the shared libraries linker during maintainer builds." FORCE ) MARK_AS_ADVANCED( CMAKE_CXX_FLAGS_MAINTAINER CMAKE_C_FLAGS_MAINTAINER CMAKE_EXE_LINKER_FLAGS_MAINTAINER CMAKE_SHARED_LINKER_FLAGS_MAINTAINER ) # Debug wants the same flags, plus -g SET( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_MAINTAINER} -g" CACHE STRING "Debug CXX flags" FORCE ) SET( CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_MAINTAINER} -g" CACHE STRING "Debug C FLAGS" FORCE ) SET( CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_MAINTAINER}" CACHE STRING "Debug exe linker flags" FORCE ) SET( CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_MAINTAINER}" CACHE STRING "Debug exe linker flags" FORCE ) MARK_AS_ADVANCED( CMAKE_CXX_FLAGS_DEBUG CMAKE_C_FLAGS_DEBUG CMAKE_EXE_LINKER_FLAGS_DEBUG CMAKE_SHARED_LINKER_FLAGS_DEBUG ) SET(ALLOWED_BUILD_TYPES None Debug Release RelWithDebInfo MinSizeRel Maintainer) STRING(REGEX REPLACE ";" " " ALLOWED_BUILD_TYPES_PRETTY "${ALLOWED_BUILD_TYPES}") # Update the documentation string of CMAKE_BUILD_TYPE for GUIs SET( CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING "Choose the type of build, options are: ${ALLOWED_BUILD_TYPES_PRETTY}." FORCE ) if( CMAKE_BUILD_TYPE STREQUAL "" ) message( WARNING "CMAKE_BUILD_TYPE is not set, defaulting to Debug" ) set( CMAKE_BUILD_TYPE "Debug" ) endif( CMAKE_BUILD_TYPE STREQUAL "" ) list(FIND ALLOWED_BUILD_TYPES ${CMAKE_BUILD_TYPE} BUILD_TYPE_INDEX) if (BUILD_TYPE_INDEX EQUAL -1) message(SEND_ERROR "${CMAKE_BUILD_TYPE} is not a valid build type.") endif() nfs-ganesha-2.6.0/src/cmake/modules/000077500000000000000000000000001324272410200172255ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/cmake/modules/CMakeParseArguments.cmake000066400000000000000000000132261324272410200240740ustar00rootroot00000000000000# CMAKE_PARSE_ARGUMENTS( args...) # # CMAKE_PARSE_ARGUMENTS() is intended to be used in macros or functions for # parsing the arguments given to that macro or function. # It processes the arguments and defines a set of variables which hold the # values of the respective options. # # The argument contains all options for the respective macro, # i.e. keywords which can be used when calling the macro without any value # following, like e.g. the OPTIONAL keyword of the install() command. # # The argument contains all keywords for this macro # which are followed by one value, like e.g. DESTINATION keyword of the # install() command. # # The argument contains all keywords for this macro # which can be followed by more than one value, like e.g. the TARGETS or # FILES keywords of the install() command. # # When done, CMAKE_PARSE_ARGUMENTS() will have defined for each of the # keywords listed in , and # a variable composed of the given # followed by "_" and the name of the respective keyword. # These variables will then hold the respective value from the argument list. # For the keywords this will be TRUE or FALSE. # # All remaining arguments are collected in a variable # _UNPARSED_ARGUMENTS, this can be checked afterwards to see whether # your macro was called with unrecognized parameters. # # As an example here a my_install() macro, which takes similar arguments as the # real install() command: # # function(MY_INSTALL) # set(options OPTIONAL FAST) # set(oneValueArgs DESTINATION RENAME) # set(multiValueArgs TARGETS CONFIGURATIONS) # cmake_parse_arguments(MY_INSTALL "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) # ... # # Assume my_install() has been called like this: # my_install(TARGETS foo bar DESTINATION bin OPTIONAL blub) # # After the cmake_parse_arguments() call the macro will have set the following # variables: # MY_INSTALL_OPTIONAL = TRUE # MY_INSTALL_FAST = FALSE (this option was not used when calling my_install() # MY_INSTALL_DESTINATION = "bin" # MY_INSTALL_RENAME = "" (was not used) # MY_INSTALL_TARGETS = "foo;bar" # MY_INSTALL_CONFIGURATIONS = "" (was not used) # MY_INSTALL_UNPARSED_ARGUMENTS = "blub" (no value expected after "OPTIONAL" # # You can the continue and process these variables. # # Keywords terminate lists of values, e.g. if directly after a one_value_keyword # another recognized keyword follows, this is interpreted as the beginning of # the new option. # E.g. my_install(TARGETS foo DESTINATION OPTIONAL) would result in # MY_INSTALL_DESTINATION set to "OPTIONAL", but MY_INSTALL_DESTINATION would # be empty and MY_INSTALL_OPTIONAL would be set to TRUE therefor. #============================================================================= # Copyright 2010 Alexander Neundorf # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) if(__CMAKE_PARSE_ARGUMENTS_INCLUDED) return() endif() set(__CMAKE_PARSE_ARGUMENTS_INCLUDED TRUE) function(CMAKE_PARSE_ARGUMENTS prefix _optionNames _singleArgNames _multiArgNames) # first set all result variables to empty/FALSE foreach(arg_name ${_singleArgNames} ${_multiArgNames}) set(${prefix}_${arg_name}) endforeach() foreach(option ${_optionNames}) set(${prefix}_${option} FALSE) endforeach() set(${prefix}_UNPARSED_ARGUMENTS) set(insideValues FALSE) set(currentArgName) # now iterate over all arguments and fill the result variables foreach(currentArg ${ARGN}) list(FIND _optionNames "${currentArg}" optionIndex) # ... then this marks the end of the arguments belonging to this keyword list(FIND _singleArgNames "${currentArg}" singleArgIndex) # ... then this marks the end of the arguments belonging to this keyword list(FIND _multiArgNames "${currentArg}" multiArgIndex) # ... then this marks the end of the arguments belonging to this keyword if(${optionIndex} EQUAL -1 AND ${singleArgIndex} EQUAL -1 AND ${multiArgIndex} EQUAL -1) if(insideValues) if("${insideValues}" STREQUAL "SINGLE") set(${prefix}_${currentArgName} ${currentArg}) set(insideValues FALSE) elseif("${insideValues}" STREQUAL "MULTI") list(APPEND ${prefix}_${currentArgName} ${currentArg}) endif() else() list(APPEND ${prefix}_UNPARSED_ARGUMENTS ${currentArg}) endif() else() if(NOT ${optionIndex} EQUAL -1) set(${prefix}_${currentArg} TRUE) set(insideValues FALSE) elseif(NOT ${singleArgIndex} EQUAL -1) set(currentArgName ${currentArg}) set(${prefix}_${currentArgName}) set(insideValues "SINGLE") elseif(NOT ${multiArgIndex} EQUAL -1) set(currentArgName ${currentArg}) set(${prefix}_${currentArgName}) set(insideValues "MULTI") endif() endif() endforeach() # propagate the result variables to the caller: foreach(arg_name ${_singleArgNames} ${_multiArgNames} ${_optionNames}) set(${prefix}_${arg_name} ${${prefix}_${arg_name}} PARENT_SCOPE) endforeach() set(${prefix}_UNPARSED_ARGUMENTS ${${prefix}_UNPARSED_ARGUMENTS} PARENT_SCOPE) endfunction() nfs-ganesha-2.6.0/src/cmake/modules/FindASan.cmake000066400000000000000000000041371324272410200216570ustar00rootroot00000000000000# The MIT License (MIT) # # Copyright (c) # 2013 Matthew Arsenault # 2015-2016 RWTH Aachen University, Federal Republic of Germany # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. option(SANITIZE_ADDRESS "Enable AddressSanitizer for sanitized targets." Off) set(FLAG_CANDIDATES # Clang 3.2+ use this version. The no-omit-frame-pointer option is optional. "-g -fsanitize=address -fno-omit-frame-pointer" "-g -fsanitize=address" # Older deprecated flag for ASan "-g -faddress-sanitizer" ) if (SANITIZE_ADDRESS AND (SANITIZE_THREAD OR SANITIZE_MEMORY)) message(FATAL_ERROR "AddressSanitizer is not compatible with " "ThreadSanitizer or MemorySanitizer.") endif () include(sanitize-helpers) if (SANITIZE_ADDRESS) sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "AddressSanitizer" "ASan") find_program(ASan_WRAPPER "asan-wrapper" PATHS ${CMAKE_MODULE_PATH}) mark_as_advanced(ASan_WRAPPER) endif () function (add_sanitize_address TARGET) if (NOT SANITIZE_ADDRESS) return() endif () saitizer_add_flags(${TARGET} "AddressSanitizer" "ASan") endfunction () nfs-ganesha-2.6.0/src/cmake/modules/FindCephFS.cmake000066400000000000000000000103061324272410200221400ustar00rootroot00000000000000# - Find CephFS # Find the Linux Trace Toolkit - next generation with associated includes path. # See http://ceph.org/ # # This module accepts the following optional variables: # CEPH_PREFIX = A hint on CEPHFS install path. # # This module defines the following variables: # CEPHFS_FOUND = Was CephFS found or not? # CEPHFS_LIBRARIES = The list of libraries to link to when using CephFS # CEPHFS_INCLUDE_DIR = The path to CephFS include directory # # On can set CEPH_PREFIX before using find_package(CephFS) and the # module with use the PATH as a hint to find CephFS. # # The hint can be given on the command line too: # cmake -DCEPH_PREFIX=/DATA/ERIC/CephFS /path/to/source if(CEPH_PREFIX) message(STATUS "FindCephFS: using PATH HINT: ${CEPH_PREFIX}") # Try to make the prefix override the normal paths find_path(CEPHFS_INCLUDE_DIR NAMES cephfs/libcephfs.h PATHS ${CEPH_PREFIX} PATH_SUFFIXES include NO_DEFAULT_PATH DOC "The CephFS include headers") find_path(CEPHFS_LIBRARY_DIR NAMES libcephfs.so PATHS ${CEPH_PREFIX} PATH_SUFFIXES lib/${CMAKE_LIBRARY_ARCHITECTURE} lib lib64 NO_DEFAULT_PATH DOC "The CephFS libraries") endif(CEPH_PREFIX) if (NOT CEPHFS_INCLUDE_DIR) find_path(CEPHFS_INCLUDE_DIR NAMES cephfs/libcephfs.h PATHS ${CEPH_PREFIX} PATH_SUFFIXES include DOC "The CephFS include headers") endif (NOT CEPHFS_INCLUDE_DIR) if (NOT CEPHFS_LIBRARY_DIR) find_path(CEPHFS_LIBRARY_DIR NAMES libcephfs.so PATHS ${CEPH_PREFIX} PATH_SUFFIXES lib/${CMAKE_LIBRARY_ARCHITECTURE} lib lib64 DOC "The CephFS libraries") endif (NOT CEPHFS_LIBRARY_DIR) find_library(CEPHFS_LIBRARY cephfs PATHS ${CEPHFS_LIBRARY_DIR} NO_DEFAULT_PATH) check_library_exists(cephfs ceph_ll_lookup ${CEPHFS_LIBRARY_DIR} CEPH_FS) if (NOT CEPH_FS) unset(CEPHFS_LIBRARY_DIR CACHE) unset(CEPHFS_INCLUDE_DIR CACHE) else (NOT CEPH_FS) check_library_exists(cephfs ceph_ll_mknod ${CEPHFS_LIBRARY_DIR} CEPH_FS_MKNOD) if(NOT CEPH_FS_MKNOD) message("Cannot find ceph_ll_mknod. Disabling CEPH fsal mknod method") set(USE_FSAL_CEPH_MKNOD OFF) else(CEPH_FS_MKNOD) set(USE_FSAL_CEPH_MKNOD ON) endif(NOT CEPH_FS_MKNOD) check_library_exists(cephfs ceph_ll_setlk ${CEPHFS_LIBRARY_DIR} CEPH_FS_SETLK) if(NOT CEPH_FS_SETLK) message("Cannot find ceph_ll_setlk. Disabling CEPH fsal lock2 method") set(USE_FSAL_CEPH_SETLK OFF) else(CEPH_FS_SETLK) set(USE_FSAL_CEPH_SETLK ON) endif(NOT CEPH_FS_SETLK) check_library_exists(cephfs ceph_ll_lookup_root ${CEPHFS_LIBRARY_DIR} CEPH_FS_LOOKUP_ROOT) if(NOT CEPH_FS_LOOKUP_ROOT) message("Cannot find ceph_ll_lookup_root. Working around it...") set(USE_FSAL_CEPH_LL_LOOKUP_ROOT OFF) else(NOT CEPH_FS_LOOKUP_ROOT) set(USE_FSAL_CEPH_LL_LOOKUP_ROOT ON) endif(NOT CEPH_FS_LOOKUP_ROOT) check_library_exists(cephfs ceph_ll_delegation ${CEPHFS_LIBRARY_DIR} CEPH_FS_DELEGATION) if(NOT CEPH_FS_DELEGATION) message("Cannot find ceph_ll_delegation. Disabling support for delegations.") set(USE_FSAL_CEPH_LL_DELEGATION OFF) else(NOT CEPH_FS_DELEGATION) set(USE_FSAL_CEPH_LL_DELEGATION ON) endif(NOT CEPH_FS_DELEGATION) set(CMAKE_REQUIRED_INCLUDES ${CEPHFS_INCLUDE_DIR}) check_symbol_exists(CEPH_STATX_INO "cephfs/libcephfs.h" CEPH_FS_CEPH_STATX) if(NOT CEPH_FS_CEPH_STATX) message("Cannot find CEPH_STATX_INO. Enabling backward compatibility for pre-ceph_statx APIs.") set(USE_FSAL_CEPH_STATX OFF) else(NOT CEPH_FS_CEPH_STATX) set(USE_FSAL_CEPH_STATX ON) endif(NOT CEPH_FS_CEPH_STATX) endif (NOT CEPH_FS) set(CEPHFS_LIBRARIES ${CEPHFS_LIBRARY}) message(STATUS "Found cephfs libraries: ${CEPHFS_LIBRARIES}") # handle the QUIETLY and REQUIRED arguments and set PRELUDE_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(CEPHFS REQUIRED_VARS CEPHFS_INCLUDE_DIR CEPHFS_LIBRARY_DIR) # VERSION FPHSA options not handled by CMake version < 2.8.2) # VERSION_VAR) mark_as_advanced(CEPHFS_INCLUDE_DIR) mark_as_advanced(CEPHFS_LIBRARY_DIR) mark_as_advanced(USE_FSAL_CEPH_MKNOD) mark_as_advanced(USE_FSAL_CEPH_SETLK) mark_as_advanced(USE_FSAL_CEPH_LL_LOOKUP_ROOT) mark_as_advanced(USE_FSAL_CEPH_STATX) nfs-ganesha-2.6.0/src/cmake/modules/FindEPOLL.cmake000066400000000000000000000013321324272410200217020ustar00rootroot00000000000000# - Find EPOLL # # This module defines the following variables: # EPOLL_FOUND = Was EPOLL found or not? # # On can set EPOLL_PATH_HINT before using find_package(EPOLL) and the # module with use the PATH as a hint to find EPOLL. # # The hint can be given on the command line too: # cmake -DEPOLL_PATH_HINT=/DATA/ERIC/EPOLL /path/to/source include(CheckIncludeFiles) include(CheckFunctionExists) check_include_files("sys/epoll.h" EPOLL_HEADER) check_function_exists(epoll_create EPOLL_FUNC) # handle the QUIETLY and REQUIRED arguments and set PRELUDE_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(EPOLL REQUIRED_VARS EPOLL_HEADER EPOLL_FUNC) nfs-ganesha-2.6.0/src/cmake/modules/FindExecInfo.cmake000066400000000000000000000010241324272410200225250ustar00rootroot00000000000000FIND_PATH(EXECINFO_INCLUDE_DIR execinfo.h) FIND_LIBRARY(EXECINFO_LIBRARY NAMES execinfo) IF (EXECINFO_INCLUDE_DIR AND EXECINFO_LIBRARY) SET(EXECINFO_FOUND TRUE) ENDIF (EXECINFO_INCLUDE_DIR AND EXECINFO_LIBRARY) IF (EXECINFO_FOUND) IF (NOT EXECINFO_FIND_QUIETLY) MESSAGE(STATUS "Found execinfo library: ${EXECINFO_LIBRARY}") ENDIF (NOT EXECINFO_FIND_QUIETLY) ELSE (EXECINFO_FOUND) IF (ExecInfo_FIND_REQUIRED) MESSAGE(FATAL_ERROR "Could not find libexecinfo") ENDIF (ExecInfo_FIND_REQUIRED) ENDIF (EXECINFO_FOUND) nfs-ganesha-2.6.0/src/cmake/modules/FindGperftools.cmake000066400000000000000000000022341324272410200231550ustar00rootroot00000000000000# Tries to find Gperftools. # # Usage of this module as follows: # # find_package(Gperftools) # # Variables used by this module, they can change the default behaviour and need # to be set before calling find_package: # # Gperftools_ROOT_DIR Set this variable to the root installation of # Gperftools if the module has problems finding # the proper installation path. # # Variables defined by this module: # # GPERFTOOLS_FOUND System has Gperftools libs/headers # GPERFTOOLS_LIBRARIES The Gperftools libraries (tcmalloc & profiler) # GPERFTOOLS_INCLUDE_DIR The location of Gperftools headers find_library(GPERFTOOLS_PROFILER NAMES profiler HINTS ${Gperftools_ROOT_DIR}/lib) find_path(GPERFTOOLS_INCLUDE_DIR NAMES gperftools/heap-profiler.h HINTS ${Gperftools_ROOT_DIR}/include) set(GPERFTOOLS_LIBRARIES ${GPERFTOOLS_PROFILER}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args( Gperftools DEFAULT_MSG GPERFTOOLS_LIBRARIES GPERFTOOLS_INCLUDE_DIR) mark_as_advanced( Gperftools_ROOT_DIR GPERFTOOLS_PROFILER GPERFTOOLS_LIBRARIES GPERFTOOLS_INCLUDE_DIR) nfs-ganesha-2.6.0/src/cmake/modules/FindJeMalloc.cmake000066400000000000000000000040571324272410200225240ustar00rootroot00000000000000# - Find JeMalloc library # Find the native JeMalloc includes and library # This module defines # JEMALLOC_INCLUDE_DIRS, where to find jemalloc.h, Set when # JEMALLOC_INCLUDE_DIR is found. # JEMALLOC_LIBRARIES, libraries to link against to use JeMalloc. # JEMALLOC_ROOT_DIR, The base directory to search for JeMalloc. # This can also be an environment variable. # JEMALLOC_FOUND, If false, do not try to use JeMalloc. # # also defined, but not for general use are # JEMALLOC_LIBRARY, where to find the JeMalloc library. #============================================================================= # Copyright 2011 Blender Foundation. # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # If JEMALLOC_ROOT_DIR was defined in the environment, use it. IF(NOT JEMALLOC_ROOT_DIR AND NOT $ENV{JEMALLOC_ROOT_DIR} STREQUAL "") SET(JEMALLOC_ROOT_DIR $ENV{JEMALLOC_ROOT_DIR}) ENDIF() SET(_jemalloc_SEARCH_DIRS ${JEMALLOC_ROOT_DIR} /usr/local /sw # Fink /opt/local # DarwinPorts /opt/csw # Blastwave ) FIND_PATH(JEMALLOC_INCLUDE_DIR NAMES jemalloc.h HINTS ${_jemalloc_SEARCH_DIRS} PATH_SUFFIXES include/jemalloc ) FIND_LIBRARY(JEMALLOC_LIBRARY NAMES jemalloc HINTS ${_jemalloc_SEARCH_DIRS} PATH_SUFFIXES lib64 lib ) # handle the QUIETLY and REQUIRED arguments and set JEMALLOC_FOUND to TRUE if # all listed variables are TRUE INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(JeMalloc DEFAULT_MSG JEMALLOC_LIBRARY JEMALLOC_INCLUDE_DIR) IF(JEMALLOC_FOUND) SET(JEMALLOC_LIBRARIES ${JEMALLOC_LIBRARY}) SET(JEMALLOC_INCLUDE_DIRS ${JEMALLOC_INCLUDE_DIR}) ENDIF(JEMALLOC_FOUND) MARK_AS_ADVANCED( JEMALLOC_INCLUDE_DIR JEMALLOC_LIBRARY ) nfs-ganesha-2.6.0/src/cmake/modules/FindKrb5.cmake000066400000000000000000000126671324272410200216470ustar00rootroot00000000000000# - Find kerberos 5 # Find the native Kerberos 5 headers and libraries. # KRB5_INCLUDE_DIRS - where to find krb5.h, etc. # KRB5_LIBRARIES - List of libraries when using kerberos 5. # KRB5_FOUND - True if kerberos 5 found. # KRB5 modules may be specified as components for this find module. # Modules may be listed by running "krb5-config". Modules include: # krb5 Kerberos 5 application # gssapi GSSAPI application with Kerberos 5 bindings # krb4 Kerberos 4 application # kadm-client Kadmin client # kadm-server Kadmin server # kdb Application that accesses the kerberos database # Typical usage: # FIND_PACKAGE(KRB5 REQUIRED gssapi) # First find the config script from which to obtain other values. IF(KRB5_PREFIX) FIND_PROGRAM(KRB5_C_CONFIG NAMES krb5-config PATHS ${KRB5_PREFIX} NO_SYSTEM_ENVIRONMENT_PATH NO_DEFAULT_PATH ) ENDIF(KRB5_PREFIX) FIND_PROGRAM(KRB5_C_CONFIG NAMES krb5-config) MESSAGE(STATUS "found krb5-config here ${KRB5_C_CONFIG}") # Check whether we found anything. IF(KRB5_C_CONFIG) SET(KRB5_FOUND 1) ELSE(KRB5_C_CONFIG) SET(KRB5_FOUND 0) ENDIF(KRB5_C_CONFIG) # Lookup the include directories needed for the components requested. IF(KRB5_FOUND) # Use the newer EXECUTE_PROCESS command if it is available. IF(COMMAND EXECUTE_PROCESS) EXECUTE_PROCESS( COMMAND ${KRB5_C_CONFIG} ${KRB5_FIND_COMPONENTS} --cflags OUTPUT_VARIABLE KRB5_C_CONFIG_CFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE RESULT_VARIABLE KRB5_C_CONFIG_RESULT ) ELSE(COMMAND EXECUTE_PROCESS) EXEC_PROGRAM(${KRB5_C_CONFIG} ARGS "${KRB5_FIND_COMPONENTS} --cflags" OUTPUT_VARIABLE KRB5_C_CONFIG_CFLAGS RETURN_VALUE KRB5_C_CONFIG_RESULT ) ENDIF(COMMAND EXECUTE_PROCESS) # Parse the include flags. IF("${KRB5_C_CONFIG_RESULT}" MATCHES "^0$") # Convert the compile flags to a CMake list. STRING(REGEX REPLACE " +" ";" KRB5_C_CONFIG_CFLAGS "${KRB5_C_CONFIG_CFLAGS}") # Look for -I options. SET(KRB5_INCLUDE_DIRS) FOREACH(flag ${KRB5_C_CONFIG_CFLAGS}) IF("${flag}" MATCHES "^-I") STRING(REGEX REPLACE "^-I" "" DIR "${flag}") FILE(TO_CMAKE_PATH "${DIR}" DIR) SET(KRB5_INCLUDE_DIRS ${KRB5_INCLUDE_DIRS} "${DIR}") ENDIF("${flag}" MATCHES "^-I") ENDFOREACH(flag) ELSE("${KRB5_C_CONFIG_RESULT}" MATCHES "^0$") MESSAGE("Error running ${KRB5_C_CONFIG}: [${KRB5_C_CONFIG_RESULT}]") SET(KRB5_FOUND 0) ENDIF("${KRB5_C_CONFIG_RESULT}" MATCHES "^0$") ENDIF(KRB5_FOUND) SET(KRB5_INCLUDE_DIRS "${KRB5_PREFIX}/include" ${KRB5_INCLUDE_DIRS}) # Lookup the libraries needed for the components requested. IF(KRB5_FOUND) # Use the newer EXECUTE_PROCESS command if it is available. IF(COMMAND EXECUTE_PROCESS) EXECUTE_PROCESS( COMMAND ${KRB5_C_CONFIG} ${KRB5_FIND_COMPONENTS} --libs OUTPUT_VARIABLE KRB5_C_CONFIG_LIBS OUTPUT_STRIP_TRAILING_WHITESPACE RESULT_VARIABLE KRB5_C_CONFIG_RESULT ) ELSE(COMMAND EXECUTE_PROCESS) EXEC_PROGRAM(${KRB5_C_CONFIG} ARGS "${KRB5_FIND_COMPONENTS} --libs" OUTPUT_VARIABLE KRB5_C_CONFIG_LIBS RETURN_VALUE KRB5_C_CONFIG_RESULT ) ENDIF(COMMAND EXECUTE_PROCESS) # Parse the library names and directories. IF("${KRB5_C_CONFIG_RESULT}" MATCHES "^0$") STRING(REGEX REPLACE " +" ";" KRB5_C_CONFIG_LIBS "${KRB5_C_CONFIG_LIBS}") # Look for -L flags for directories and -l flags for library names. SET(KRB5_LIBRARY_DIRS) SET(KRB5_LIBRARY_NAMES) FOREACH(flag ${KRB5_C_CONFIG_LIBS}) IF("${flag}" MATCHES "^-L") STRING(REGEX REPLACE "^-L" "" DIR "${flag}") FILE(TO_CMAKE_PATH "${DIR}" DIR) SET(KRB5_LIBRARY_DIRS ${KRB5_LIBRARY_DIRS} "${DIR}") ELSEIF("${flag}" MATCHES "^-l") STRING(REGEX REPLACE "^-l" "" NAME "${flag}") SET(KRB5_LIBRARY_NAMES ${KRB5_LIBRARY_NAMES} "${NAME}") ENDIF("${flag}" MATCHES "^-L") ENDFOREACH(flag) # add gssapi_krb5 (MIT) SET(KRB5_LIBRARY_NAMES ${KRB5_LIBRARY_NAMES} "gssapi_krb5") # Search for each library needed using the directories given. FOREACH(name ${KRB5_LIBRARY_NAMES}) # Look for this library. FIND_LIBRARY(KRB5_${name}_LIBRARY NAMES ${name} PATHS ${KRB5_LIBRARY_DIRS} NO_DEFAULT_PATH ) FIND_LIBRARY(KRB5_${name}_LIBRARY NAMES ${name}) MARK_AS_ADVANCED(KRB5_${name}_LIBRARY) # If any library is not found then the whole package is not found. IF(NOT KRB5_${name}_LIBRARY) SET(KRB5_FOUND 0) ENDIF(NOT KRB5_${name}_LIBRARY) # Build an ordered list of all the libraries needed. SET(KRB5_LIBRARIES ${KRB5_LIBRARIES} "${KRB5_${name}_LIBRARY}") ENDFOREACH(name) ELSE("${KRB5_C_CONFIG_RESULT}" MATCHES "^0$") MESSAGE("Error running ${KRB5_C_CONFIG}: [${KRB5_C_CONFIG_RESULT}]") SET(KRB5_FOUND 0) ENDIF("${KRB5_C_CONFIG_RESULT}" MATCHES "^0$") ENDIF(KRB5_FOUND) # Report the results. IF(NOT KRB5_FOUND) SET(KRB5_DIR_MESSAGE "KRB5 was not found. Make sure the entries KRB5_* are set.") IF(NOT KRB5_FIND_QUIETLY) MESSAGE(STATUS "${KRB5_DIR_MESSAGE}") ELSE(NOT KRB5_FIND_QUIETLY) IF(KRB5_FIND_REQUIRED) MESSAGE(FATAL_ERROR "${KRB5_DIR_MESSAGE}") ENDIF(KRB5_FIND_REQUIRED) ENDIF(NOT KRB5_FIND_QUIETLY) ELSE(NOT KRB5_FOUND) MESSAGE(STATUS "Found kerberos 5 headers: ${KRB5_INCLUDE_DIRS}") MESSAGE(STATUS "Found kerberos 5 libs: ${KRB5_LIBRARIES}") ENDIF(NOT KRB5_FOUND) nfs-ganesha-2.6.0/src/cmake/modules/FindLSB.cmake000066400000000000000000000070651324272410200214600ustar00rootroot00000000000000# - Find Linux Standard Base Release Tools # This module defines the following variables: # LSB_RELEASE_EXECUTABLE - path to lsb_release program # LSB_RELEASE_VERSION_SHORT - Output of "lsb_release -vs" # LSB_RELEASE_ID_SHORT - Output of "lsb_release -is" # LSB_RELEASE_DESCRIPTION_SHORT - Output of "lsb_release -ds" # LSB_RELEASE_RELEASE_SHORT - Output of "lsb_release -rs" # LSB_RELEASE_CODENAME_SHORT - Output of "lsb_release -cs" # #---------------------------------------------------------------------------- # Copyright (c) 2012, Ben Morgan, University of Warwick # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # * Neither the name of Ben Morgan, or the University of Warwick nor the # names of its contributors may be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. #---------------------------------------------------------------------------- find_program(LSB_RELEASE_EXECUTABLE lsb_release DOC "Linux Standard Base and Distribution command line query client") mark_as_advanced(LSB_RELEASE_EXECUTABLE) if(LSB_RELEASE_EXECUTABLE) # Extract the standard information in short format into CMake variables # - Version (strictly a colon separated list, kept as string for now) execute_process(COMMAND ${LSB_RELEASE_EXECUTABLE} -vs OUTPUT_VARIABLE LSB_RELEASE_VERSION_SHORT OUTPUT_STRIP_TRAILING_WHITESPACE ) # - Distributor ID execute_process(COMMAND ${LSB_RELEASE_EXECUTABLE} -is OUTPUT_VARIABLE LSB_RELEASE_ID_SHORT OUTPUT_STRIP_TRAILING_WHITESPACE ) # - Description execute_process(COMMAND ${LSB_RELEASE_EXECUTABLE} -ds OUTPUT_VARIABLE LSB_RELEASE_DESCRIPTION_SHORT OUTPUT_STRIP_TRAILING_WHITESPACE ) # Description might be quoted, so strip out if they're there string(REPLACE "\"" "" LSB_RELEASE_DESCRIPTION_SHORT "${LSB_RELEASE_DESCRIPTION_SHORT}") # - Release execute_process(COMMAND ${LSB_RELEASE_EXECUTABLE} -rs OUTPUT_VARIABLE LSB_RELEASE_RELEASE_SHORT OUTPUT_STRIP_TRAILING_WHITESPACE ) # - Codename execute_process(COMMAND ${LSB_RELEASE_EXECUTABLE} -cs OUTPUT_VARIABLE LSB_RELEASE_CODENAME_SHORT OUTPUT_STRIP_TRAILING_WHITESPACE ) endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(LSB_RELEASE DEFAULT_MSG LSB_RELEASE_EXECUTABLE) nfs-ganesha-2.6.0/src/cmake/modules/FindLTTng.cmake000066400000000000000000000056761324272410200220360ustar00rootroot00000000000000# - Find LTTng # Find the Linux Trace Toolkit - next generation with associated includes path. # See http://lttng.org/ # # This module accepts the following optional variables: # LTTNG_PATH_HINT = A hint on LTTNG install path. # # This module defines the following variables: # LTTNG_FOUND = Was LTTng found or not? # LTTNG_EXECUTABLE = The path to lttng command # LTTNG_LIBRARIES = The list of libraries to link to when using LTTng # LTTNG_INCLUDE_DIR = The path to LTTng include directory # # On can set LTTNG_PATH_HINT before using find_package(LTTng) and the # module with use the PATH as a hint to find LTTng. # # The hint can be given on the command line too: # cmake -DLTTNG_PATH_HINT=/DATA/ERIC/LTTng /path/to/source if(LTTNG_PATH_HINT) message(STATUS "FindLTTng: using PATH HINT: ${LTTNG_PATH_HINT}") else() set(LTTNG_PATH_HINT) endif() #One can add his/her own builtin PATH. #FILE(TO_CMAKE_PATH "/DATA/ERIC/LTTng" MYPATH) #list(APPEND LTTNG_PATH_HINT ${MYPATH}) find_path(LTTNG_INCLUDE_DIR NAMES lttng/tracepoint.h PATHS ${LTTNG_PATH_HINT} PATH_SUFFIXES include DOC "The LTTng include headers") find_path(LTTNG_LIBRARY_DIR NAMES liblttng-ust.so PATHS ${LTTNG_PATH_HINT} PATH_SUFFIXES lib lib64 DOC "The LTTng libraries") find_library(LTTNG_UST_LIBRARY lttng-ust PATHS ${LTTNG_LIBRARY_DIR}) find_library(URCU_LIBRARY urcu-bp PATHS ${LTTNG_LIBRARY_DIR}) find_library(UUID_LIBRARY uuid) set(LTTNG_LIBRARIES ${LTTNG_UST_LIBRARY} ${URCU_LIBRARY} ${UUID_LIBRARY}) find_path(LTTNG_CTL_INCLUDE_DIR NAMES lttng/lttng.h PATHS ${LTTNG_PATH_HINT} PATH_SUFFIXES include DOC "The LTTng CTL include headers") find_path(LTTNG_CTL_LIBRARY_DIR NAMES liblttng-ctl.so PATHS ${LTTNG_PATH_HINT} PATH_SUFFIXES lib lib64 DOC "The LTTng libraries") find_library(LTTNG_CTL_LIBRARY lttng-ctl PATHS ${LTTNG_CTL_LIBRARY_DIR}) set(LTTNG_CTL_LIBRARIES ${LTTNG_CTL_LIBRARY}) message(STATUS "Looking for lttng executable...") set(LTTNG_NAMES "lttng;lttng-ctl") # FIND_PROGRAM twice using NO_DEFAULT_PATH on first shot find_program(LTTNG_EXECUTABLE NAMES ${LTTNG_NAMES} PATHS ${LTTNG_PATH_HINT}/bin NO_DEFAULT_PATH DOC "The LTTNG command line tool") find_program(LEX_PROGRAM NAMES ${LTTNG_NAMES} PATHS ${LTTNG_PATH_HINT}/bin DOC "The LTTNG command line tool") # handle the QUIETLY and REQUIRED arguments and set PRELUDE_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(LTTNG REQUIRED_VARS LTTNG_INCLUDE_DIR LTTNG_LIBRARY_DIR) FIND_PACKAGE_HANDLE_STANDARD_ARGS(LTTNG_CTL REQUIRED_VARS LTTNG_CTL_INCLUDE_DIR LTTNG_CTL_LIBRARY_DIR) # VERSION FPHSA options not handled by CMake version < 2.8.2) # VERSION_VAR) mark_as_advanced(LTTNG_INCLUDE_DIR) mark_as_advanced(LTTNG_LIBRARY_DIR) nfs-ganesha-2.6.0/src/cmake/modules/FindMSan.cmake000066400000000000000000000043631324272410200216740ustar00rootroot00000000000000# The MIT License (MIT) # # Copyright (c) # 2013 Matthew Arsenault # 2015-2016 RWTH Aachen University, Federal Republic of Germany # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. option(SANITIZE_MEMORY "Enable MemorySanitizer for sanitized targets." Off) set(FLAG_CANDIDATES "-g -fsanitize=memory" ) include(sanitize-helpers) if (SANITIZE_MEMORY) if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") message(WARNING "MemorySanitizer disabled for target ${TARGET} because " "MemorySanitizer is supported for Linux systems only.") set(SANITIZE_MEMORY Off CACHE BOOL "Enable MemorySanitizer for sanitized targets." FORCE) elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) message(WARNING "MemorySanitizer disabled for target ${TARGET} because " "MemorySanitizer is supported for 64bit systems only.") set(SANITIZE_MEMORY Off CACHE BOOL "Enable MemorySanitizer for sanitized targets." FORCE) else () sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "MemorySanitizer" "MSan") endif () endif () function (add_sanitize_memory TARGET) if (NOT SANITIZE_MEMORY) return() endif () saitizer_add_flags(${TARGET} "MemorySanitizer" "MSan") endfunction () nfs-ganesha-2.6.0/src/cmake/modules/FindNTIRPC.cmake000066400000000000000000000025161324272410200220330ustar00rootroot00000000000000# - Find NTIRPC # Find the New TIRPC RPC library # # This module accepts the following optional variables: # NTIRPC_PATH_HINT = A hint on NTIRPC install path. # # This module defines the following variables: # NTIRPC_FOUND = Was NTIRPC found or not? # NTIRPC_LIBRARY = The list of libraries to link to when using NTIRPC # NTIRPC_INCLUDE_DIR = The path to NTIRPC include directory(s) # # On can set NTIRPC_PATH_HINT before using find_package(NTIRPC) and the # module with use the PATH as a hint to find NTIRPC. # # The hint can be given on the command line too: # cmake -DNTIRPC_PATH_HINT=/DATA/ERIC/NTIRPC /path/to/source include(LibFindMacros) libfind_pkg_detect(NTIRPC libntirpc FIND_PATH netconfig.h PATH_SUFFIXES ntirpc FIND_LIBRARY ntirpc) if (NTIRPC_LIBRARY) libfind_version_header(NTIRPC version.h NTIRPC_VERSION) endif (NTIRPC_LIBRARY) # handle the QUIETLY and REQUIRED arguments and set PRELUDE_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(NTIRPC REQUIRED_VARS NTIRPC_INCLUDE_DIR NTIRPC_LIBRARY VERSION_VAR NTIRPC_VERSION) # VERSION FPHSA options not handled by CMake version < 2.8.2) # VERSION_VAR) mark_as_advanced(NTIRPC_INCLUDE_DIR) mark_as_advanced(NTIRPC_LIBRARY) nfs-ganesha-2.6.0/src/cmake/modules/FindNfsIdmap.cmake000066400000000000000000000010251324272410200225270ustar00rootroot00000000000000FIND_PATH(NFSIDMAP_INCLUDE_DIR nfsidmap.h) FIND_LIBRARY(NFSIDMAP_LIBRARY NAMES nfsidmap) IF (NFSIDMAP_INCLUDE_DIR AND NFSIDMAP_LIBRARY) SET(NFSIDMAP_FOUND TRUE) ENDIF (NFSIDMAP_INCLUDE_DIR AND NFSIDMAP_LIBRARY) IF (NFSIDMAP_FOUND) IF (NOT NFSIDMAP_FIND_QUIETLY) MESSAGE(STATUS "Found nfs idmap library: ${NFSIDMAP_LIBRARY}") ENDIF (NOT NFSIDMAP_FIND_QUIETLY) ELSE (NFSIDMAP_FOUND) IF (NfsIdmap_FIND_REQUIRED) MESSAGE(FATAL_ERROR "Could not find libnfsidmap") ENDIF (NfsIdmap_FIND_REQUIRED) ENDIF (NFSIDMAP_FOUND) nfs-ganesha-2.6.0/src/cmake/modules/FindPackageHandleStandardArgs.cmake000066400000000000000000000321751324272410200260050ustar00rootroot00000000000000# FIND_PACKAGE_HANDLE_STANDARD_ARGS( ... ) # # This function is intended to be used in FindXXX.cmake modules files. # It handles the REQUIRED, QUIET and version-related arguments to find_package(). # It also sets the _FOUND variable. # The package is considered found if all variables ... listed contain # valid results, e.g. valid filepaths. # # There are two modes of this function. The first argument in both modes is # the name of the Find-module where it is called (in original casing). # # The first simple mode looks like this: # FIND_PACKAGE_HANDLE_STANDARD_ARGS( (DEFAULT_MSG|"Custom failure message") ... ) # If the variables to are all valid, then _FOUND # will be set to TRUE. # If DEFAULT_MSG is given as second argument, then the function will generate # itself useful success and error messages. You can also supply a custom error message # for the failure case. This is not recommended. # # The second mode is more powerful and also supports version checking: # FIND_PACKAGE_HANDLE_STANDARD_ARGS(NAME [FOUND_VAR ] # [REQUIRED_VARS ...] # [VERSION_VAR ] # [HANDLE_COMPONENTS] # [CONFIG_MODE] # [FAIL_MESSAGE "Custom failure message"] ) # # In this mode, the name of the result-variable can be set either to either # _FOUND or _FOUND using the FOUND_VAR option. # Other names for the result-variable are not allowed. # So for a Find-module named FindFooBar.cmake, the two possible names are # FooBar_FOUND and FOOBAR_FOUND. It is recommended to use the original case version. # If the FOUND_VAR option is not used, the default is _FOUND. # # As in the simple mode, if through are all valid, # _FOUND will be set to TRUE. # After REQUIRED_VARS the variables which are required for this package are listed. # Following VERSION_VAR the name of the variable can be specified which holds # the version of the package which has been found. If this is done, this version # will be checked against the (potentially) specified required version used # in the find_package() call. The EXACT keyword is also handled. The default # messages include information about the required version and the version # which has been actually found, both if the version is ok or not. # If the package supports components, use the HANDLE_COMPONENTS option to enable # handling them. In this case, find_package_handle_standard_args() will report # which components have been found and which are missing, and the _FOUND # variable will be set to FALSE if any of the required components (i.e. not the # ones listed after OPTIONAL_COMPONENTS) are missing. # Use the option CONFIG_MODE if your FindXXX.cmake module is a wrapper for # a find_package(... NO_MODULE) call. In this case VERSION_VAR will be set # to _VERSION and the macro will automatically check whether the # Config module was found. # Via FAIL_MESSAGE a custom failure message can be specified, if this is not # used, the default message will be displayed. # # Example for mode 1: # # find_package_handle_standard_args(LibXml2 DEFAULT_MSG LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR) # # LibXml2 is considered to be found, if both LIBXML2_LIBRARY and # LIBXML2_INCLUDE_DIR are valid. Then also LIBXML2_FOUND is set to TRUE. # If it is not found and REQUIRED was used, it fails with FATAL_ERROR, # independent whether QUIET was used or not. # If it is found, success will be reported, including the content of . # On repeated Cmake runs, the same message won't be printed again. # # Example for mode 2: # # find_package_handle_standard_args(LibXslt FOUND_VAR LibXslt_FOUND # REQUIRED_VARS LibXslt_LIBRARIES LibXslt_INCLUDE_DIRS # VERSION_VAR LibXslt_VERSION_STRING) # In this case, LibXslt is considered to be found if the variable(s) listed # after REQUIRED_VAR are all valid, i.e. LibXslt_LIBRARIES and LibXslt_INCLUDE_DIRS # in this case. The result will then be stored in LibXslt_FOUND . # Also the version of LibXslt will be checked by using the version contained # in LibXslt_VERSION_STRING. # Since no FAIL_MESSAGE is given, the default messages will be printed. # # Another example for mode 2: # # find_package(Automoc4 QUIET NO_MODULE HINTS /opt/automoc4) # find_package_handle_standard_args(Automoc4 CONFIG_MODE) # In this case, FindAutmoc4.cmake wraps a call to find_package(Automoc4 NO_MODULE) # and adds an additional search directory for automoc4. # Here the result will be stored in AUTOMOC4_FOUND. # The following FIND_PACKAGE_HANDLE_STANDARD_ARGS() call produces a proper # success/error message. #============================================================================= # Copyright 2007-2009 Kitware, Inc. # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) get_filename_component(INCLUDED_DIR ${CMAKE_CURRENT_LIST_FILE} PATH) include(${INCLUDED_DIR}/FindPackageMessage.cmake) include(${INCLUDED_DIR}/CMakeParseArguments.cmake) # internal helper macro macro(_FPHSA_FAILURE_MESSAGE _msg) if (${_NAME}_FIND_REQUIRED) message(FATAL_ERROR "${_msg}") else () if (NOT ${_NAME}_FIND_QUIETLY) message(STATUS "${_msg}") endif () endif () endmacro() # internal helper macro to generate the failure message when used in CONFIG_MODE: macro(_FPHSA_HANDLE_FAILURE_CONFIG_MODE) # _CONFIG is set, but FOUND is false, this means that some other of the REQUIRED_VARS was not found: if(${_NAME}_CONFIG) _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: missing: ${MISSING_VARS} (found ${${_NAME}_CONFIG} ${VERSION_MSG})") else() # If _CONSIDERED_CONFIGS is set, the config-file has been found, but no suitable version. # List them all in the error message: if(${_NAME}_CONSIDERED_CONFIGS) set(configsText "") list(LENGTH ${_NAME}_CONSIDERED_CONFIGS configsCount) math(EXPR configsCount "${configsCount} - 1") foreach(currentConfigIndex RANGE ${configsCount}) list(GET ${_NAME}_CONSIDERED_CONFIGS ${currentConfigIndex} filename) list(GET ${_NAME}_CONSIDERED_VERSIONS ${currentConfigIndex} version) set(configsText "${configsText} ${filename} (version ${version})\n") endforeach() if (${_NAME}_NOT_FOUND_MESSAGE) set(configsText "${configsText} Reason given by package: ${${_NAME}_NOT_FOUND_MESSAGE}\n") endif() _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:\n${configsText}") else() # Simple case: No Config-file was found at all: _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: found neither ${_NAME}Config.cmake nor ${_NAME_LOWER}-config.cmake ${VERSION_MSG}") endif() endif() endmacro() #function(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG) function(find_package_handle_standard_args _NAME _FIRST_ARG) # set up the arguments for CMAKE_PARSE_ARGUMENTS and check whether we are in # new extended or in the "old" mode: set(options CONFIG_MODE HANDLE_COMPONENTS) set(oneValueArgs FAIL_MESSAGE VERSION_VAR FOUND_VAR) set(multiValueArgs REQUIRED_VARS) set(_KEYWORDS_FOR_EXTENDED_MODE ${options} ${oneValueArgs} ${multiValueArgs} ) list(FIND _KEYWORDS_FOR_EXTENDED_MODE "${_FIRST_ARG}" INDEX) if(${INDEX} EQUAL -1) set(FPHSA_FAIL_MESSAGE ${_FIRST_ARG}) set(FPHSA_REQUIRED_VARS ${ARGN}) set(FPHSA_VERSION_VAR) else() CMAKE_PARSE_ARGUMENTS(FPHSA "${options}" "${oneValueArgs}" "${multiValueArgs}" ${_FIRST_ARG} ${ARGN}) if(FPHSA_UNPARSED_ARGUMENTS) message(FATAL_ERROR "Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \"${FPHSA_UNPARSED_ARGUMENTS}\"") endif() if(NOT FPHSA_FAIL_MESSAGE) set(FPHSA_FAIL_MESSAGE "DEFAULT_MSG") endif() endif() # now that we collected all arguments, process them if("${FPHSA_FAIL_MESSAGE}" STREQUAL "DEFAULT_MSG") set(FPHSA_FAIL_MESSAGE "Could NOT find ${_NAME}") endif() # In config-mode, we rely on the variable _CONFIG, which is set by find_package() # when it successfully found the config-file, including version checking: if(FPHSA_CONFIG_MODE) list(INSERT FPHSA_REQUIRED_VARS 0 ${_NAME}_CONFIG) list(REMOVE_DUPLICATES FPHSA_REQUIRED_VARS) set(FPHSA_VERSION_VAR ${_NAME}_VERSION) endif() if(NOT FPHSA_REQUIRED_VARS) message(FATAL_ERROR "No REQUIRED_VARS specified for FIND_PACKAGE_HANDLE_STANDARD_ARGS()") endif() list(GET FPHSA_REQUIRED_VARS 0 _FIRST_REQUIRED_VAR) string(TOUPPER ${_NAME} _NAME_UPPER) string(TOLOWER ${_NAME} _NAME_LOWER) if(FPHSA_FOUND_VAR) if(FPHSA_FOUND_VAR MATCHES "^${_NAME}_FOUND$" OR FPHSA_FOUND_VAR MATCHES "^${_NAME_UPPER}_FOUND$") set(_FOUND_VAR ${FPHSA_FOUND_VAR}) else() message(FATAL_ERROR "The argument for FOUND_VAR is \"${FPHSA_FOUND_VAR}\", but only \"${_NAME}_FOUND\" and \"${_NAME_UPPER}_FOUND\" are valid names.") endif() else() set(_FOUND_VAR ${_NAME_UPPER}_FOUND) endif() # collect all variables which were not found, so they can be printed, so the # user knows better what went wrong (#6375) set(MISSING_VARS "") set(DETAILS "") # check if all passed variables are valid unset(${_FOUND_VAR}) foreach(_CURRENT_VAR ${FPHSA_REQUIRED_VARS}) if(NOT ${_CURRENT_VAR}) set(${_FOUND_VAR} FALSE) set(MISSING_VARS "${MISSING_VARS} ${_CURRENT_VAR}") else() set(DETAILS "${DETAILS}[${${_CURRENT_VAR}}]") endif() endforeach() if(NOT "${${_FOUND_VAR}}" STREQUAL "FALSE") set(${_FOUND_VAR} TRUE) endif() # component handling unset(FOUND_COMPONENTS_MSG) unset(MISSING_COMPONENTS_MSG) if(FPHSA_HANDLE_COMPONENTS) foreach(comp ${${_NAME}_FIND_COMPONENTS}) if(${_NAME}_${comp}_FOUND) if(NOT DEFINED FOUND_COMPONENTS_MSG) set(FOUND_COMPONENTS_MSG "found components: ") endif() set(FOUND_COMPONENTS_MSG "${FOUND_COMPONENTS_MSG} ${comp}") else() if(NOT DEFINED MISSING_COMPONENTS_MSG) set(MISSING_COMPONENTS_MSG "missing components: ") endif() set(MISSING_COMPONENTS_MSG "${MISSING_COMPONENTS_MSG} ${comp}") if(${_NAME}_FIND_REQUIRED_${comp}) set(${_FOUND_VAR} FALSE) set(MISSING_VARS "${MISSING_VARS} ${comp}") endif() endif() endforeach() set(COMPONENT_MSG "${FOUND_COMPONENTS_MSG} ${MISSING_COMPONENTS_MSG}") set(DETAILS "${DETAILS}[c${COMPONENT_MSG}]") endif() # version handling: set(VERSION_MSG "") set(VERSION_OK TRUE) set(VERSION ${${FPHSA_VERSION_VAR}} ) if (${_NAME}_FIND_VERSION) if(VERSION) if(${_NAME}_FIND_VERSION_EXACT) # exact version required if (NOT "${${_NAME}_FIND_VERSION}" VERSION_EQUAL "${VERSION}") set(VERSION_MSG "Found unsuitable version \"${VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"") set(VERSION_OK FALSE) else () set(VERSION_MSG "(found suitable exact version \"${VERSION}\")") endif () else() # minimum version specified: if ("${${_NAME}_FIND_VERSION}" VERSION_GREATER "${VERSION}") set(VERSION_MSG "Found unsuitable version \"${VERSION}\", but required is at least \"${${_NAME}_FIND_VERSION}\"") set(VERSION_OK FALSE) else () set(VERSION_MSG "(found suitable version \"${VERSION}\", minimum required is \"${${_NAME}_FIND_VERSION}\")") endif () endif() else() # if the package was not found, but a version was given, add that to the output: if(${_NAME}_FIND_VERSION_EXACT) set(VERSION_MSG "(Required is exact version \"${${_NAME}_FIND_VERSION}\")") else() set(VERSION_MSG "(Required is at least version \"${${_NAME}_FIND_VERSION}\")") endif() endif() else () if(VERSION) set(VERSION_MSG "(found version \"${VERSION}\")") endif() endif () if(VERSION_OK) set(DETAILS "${DETAILS}[v${VERSION}(${${_NAME}_FIND_VERSION})]") else() set(${_FOUND_VAR} FALSE) endif() # print the result: if (${_FOUND_VAR}) FIND_PACKAGE_MESSAGE(${_NAME} "Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG} ${COMPONENT_MSG}" "${DETAILS}") else () if(FPHSA_CONFIG_MODE) _FPHSA_HANDLE_FAILURE_CONFIG_MODE() else() if(NOT VERSION_OK) _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: ${VERSION_MSG} (found ${${_FIRST_REQUIRED_VAR}})") else() _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} (missing: ${MISSING_VARS}) ${VERSION_MSG}") endif() endif() endif () set(${_FOUND_VAR} ${${_FOUND_VAR}} PARENT_SCOPE) endfunction() nfs-ganesha-2.6.0/src/cmake/modules/FindPackageMessage.cmake000066400000000000000000000036531324272410200236770ustar00rootroot00000000000000# FIND_PACKAGE_MESSAGE( "message for user" "find result details") # # This macro is intended to be used in FindXXX.cmake modules files. # It will print a message once for each unique find result. # This is useful for telling the user where a package was found. # The first argument specifies the name (XXX) of the package. # The second argument specifies the message to display. # The third argument lists details about the find result so that # if they change the message will be displayed again. # The macro also obeys the QUIET argument to the find_package command. # # Example: # # if(X11_FOUND) # FIND_PACKAGE_MESSAGE(X11 "Found X11: ${X11_X11_LIB}" # "[${X11_X11_LIB}][${X11_INCLUDE_DIR}]") # else() # ... # endif() #============================================================================= # Copyright 2008-2009 Kitware, Inc. # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) function(FIND_PACKAGE_MESSAGE pkg msg details) # Avoid printing a message repeatedly for the same find result. if(NOT ${pkg}_FIND_QUIETLY) string(REGEX REPLACE "[\n]" "" details "${details}") set(DETAILS_VAR FIND_PACKAGE_MESSAGE_DETAILS_${pkg}) if(NOT "${details}" STREQUAL "${${DETAILS_VAR}}") # The message has not yet been printed. message(STATUS "${msg}") # Save the find details in the cache to avoid printing the same # message again. set("${DETAILS_VAR}" "${details}" CACHE INTERNAL "Details about finding ${pkg}") endif() endif() endfunction() nfs-ganesha-2.6.0/src/cmake/modules/FindPkgConfig.cmake000066400000000000000000000350751324272410200227110ustar00rootroot00000000000000# - a pkg-config module for CMake # # Usage: # pkg_check_modules( [REQUIRED] [QUIET] []*) # checks for all the given modules # # pkg_search_module( [REQUIRED] [QUIET] []*) # checks for given modules and uses the first working one # # When the 'REQUIRED' argument was set, macros will fail with an error # when module(s) could not be found # # When the 'QUIET' argument is set, no status messages will be printed. # # It sets the following variables: # PKG_CONFIG_FOUND ... if pkg-config executable was found # PKG_CONFIG_EXECUTABLE ... pathname of the pkg-config program # PKG_CONFIG_VERSION_STRING ... the version of the pkg-config program found # (since CMake 2.8.8) # # For the following variables two sets of values exist; first one is the # common one and has the given PREFIX. The second set contains flags # which are given out when pkgconfig was called with the '--static' # option. # _FOUND ... set to 1 if module(s) exist # _LIBRARIES ... only the libraries (w/o the '-l') # _LIBRARY_DIRS ... the paths of the libraries (w/o the '-L') # _LDFLAGS ... all required linker flags # _LDFLAGS_OTHER ... all other linker flags # _INCLUDE_DIRS ... the '-I' preprocessor flags (w/o the '-I') # _CFLAGS ... all required cflags # _CFLAGS_OTHER ... the other compiler flags # # = for common case # = _STATIC for static linking # # There are some special variables whose prefix depends on the count # of given modules. When there is only one module, stays # unchanged. When there are multiple modules, the prefix will be # changed to _: # _VERSION ... version of the module # _PREFIX ... prefix-directory of the module # _INCLUDEDIR ... include-dir of the module # _LIBDIR ... lib-dir of the module # # = when |MODULES| == 1, else # = _ # # A parameter can have the following formats: # {MODNAME} ... matches any version # {MODNAME}>={VERSION} ... at least version is required # {MODNAME}={VERSION} ... exactly version is required # {MODNAME}<={VERSION} ... modules must not be newer than # # Examples # pkg_check_modules (GLIB2 glib-2.0) # # pkg_check_modules (GLIB2 glib-2.0>=2.10) # requires at least version 2.10 of glib2 and defines e.g. # GLIB2_VERSION=2.10.3 # # pkg_check_modules (FOO glib-2.0>=2.10 gtk+-2.0) # requires both glib2 and gtk2, and defines e.g. # FOO_glib-2.0_VERSION=2.10.3 # FOO_gtk+-2.0_VERSION=2.8.20 # # pkg_check_modules (XRENDER REQUIRED xrender) # defines e.g.: # XRENDER_LIBRARIES=Xrender;X11 # XRENDER_STATIC_LIBRARIES=Xrender;X11;pthread;Xau;Xdmcp # # pkg_search_module (BAR libxml-2.0 libxml2 libxml>=2) #============================================================================= # Copyright 2006-2009 Kitware, Inc. # Copyright 2006 Enrico Scholz # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) ### Common stuff #### set(PKG_CONFIG_VERSION 1) find_program(PKG_CONFIG_EXECUTABLE NAMES pkg-config DOC "pkg-config executable") mark_as_advanced(PKG_CONFIG_EXECUTABLE) if (PKG_CONFIG_EXECUTABLE) execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --version OUTPUT_VARIABLE PKG_CONFIG_VERSION_STRING ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) endif () get_filename_component(INCLUDED_DIR ${CMAKE_CURRENT_LIST_FILE} PATH) include(${INCLUDED_DIR}/FindPackageHandleStandardArgs.cmake) find_package_handle_standard_args(PkgConfig REQUIRED_VARS PKG_CONFIG_EXECUTABLE VERSION_VAR PKG_CONFIG_VERSION_STRING) # This is needed because the module name is "PkgConfig" but the name of # this variable has always been PKG_CONFIG_FOUND so this isn't automatically # handled by FPHSA. set(PKG_CONFIG_FOUND "${PKGCONFIG_FOUND}") # Unsets the given variables macro(_pkgconfig_unset var) set(${var} "" CACHE INTERNAL "") endmacro() macro(_pkgconfig_set var value) set(${var} ${value} CACHE INTERNAL "") endmacro() # Invokes pkgconfig, cleans up the result and sets variables macro(_pkgconfig_invoke _pkglist _prefix _varname _regexp) set(_pkgconfig_invoke_result) execute_process( COMMAND ${PKG_CONFIG_EXECUTABLE} ${ARGN} ${_pkglist} OUTPUT_VARIABLE _pkgconfig_invoke_result RESULT_VARIABLE _pkgconfig_failed) if (_pkgconfig_failed) set(_pkgconfig_${_varname} "") _pkgconfig_unset(${_prefix}_${_varname}) else() string(REGEX REPLACE "[\r\n]" " " _pkgconfig_invoke_result "${_pkgconfig_invoke_result}") string(REGEX REPLACE " +$" "" _pkgconfig_invoke_result "${_pkgconfig_invoke_result}") if (NOT ${_regexp} STREQUAL "") string(REGEX REPLACE "${_regexp}" " " _pkgconfig_invoke_result "${_pkgconfig_invoke_result}") endif() separate_arguments(_pkgconfig_invoke_result) #message(STATUS " ${_varname} ... ${_pkgconfig_invoke_result}") set(_pkgconfig_${_varname} ${_pkgconfig_invoke_result}) _pkgconfig_set(${_prefix}_${_varname} "${_pkgconfig_invoke_result}") endif() endmacro() # Invokes pkgconfig two times; once without '--static' and once with # '--static' macro(_pkgconfig_invoke_dyn _pkglist _prefix _varname cleanup_regexp) _pkgconfig_invoke("${_pkglist}" ${_prefix} ${_varname} "${cleanup_regexp}" ${ARGN}) _pkgconfig_invoke("${_pkglist}" ${_prefix} STATIC_${_varname} "${cleanup_regexp}" --static ${ARGN}) endmacro() # Splits given arguments into options and a package list macro(_pkgconfig_parse_options _result _is_req _is_silent) set(${_is_req} 0) set(${_is_silent} 0) foreach(_pkg ${ARGN}) if (_pkg STREQUAL "REQUIRED") set(${_is_req} 1) endif () if (_pkg STREQUAL "QUIET") set(${_is_silent} 1) endif () endforeach() set(${_result} ${ARGN}) list(REMOVE_ITEM ${_result} "REQUIRED") list(REMOVE_ITEM ${_result} "QUIET") endmacro() ### macro(_pkg_check_modules_internal _is_required _is_silent _prefix) _pkgconfig_unset(${_prefix}_FOUND) _pkgconfig_unset(${_prefix}_VERSION) _pkgconfig_unset(${_prefix}_PREFIX) _pkgconfig_unset(${_prefix}_INCLUDEDIR) _pkgconfig_unset(${_prefix}_LIBDIR) _pkgconfig_unset(${_prefix}_LIBS) _pkgconfig_unset(${_prefix}_LIBS_L) _pkgconfig_unset(${_prefix}_LIBS_PATHS) _pkgconfig_unset(${_prefix}_LIBS_OTHER) _pkgconfig_unset(${_prefix}_CFLAGS) _pkgconfig_unset(${_prefix}_CFLAGS_I) _pkgconfig_unset(${_prefix}_CFLAGS_OTHER) _pkgconfig_unset(${_prefix}_STATIC_LIBDIR) _pkgconfig_unset(${_prefix}_STATIC_LIBS) _pkgconfig_unset(${_prefix}_STATIC_LIBS_L) _pkgconfig_unset(${_prefix}_STATIC_LIBS_PATHS) _pkgconfig_unset(${_prefix}_STATIC_LIBS_OTHER) _pkgconfig_unset(${_prefix}_STATIC_CFLAGS) _pkgconfig_unset(${_prefix}_STATIC_CFLAGS_I) _pkgconfig_unset(${_prefix}_STATIC_CFLAGS_OTHER) # create a better addressable variable of the modules and calculate its size set(_pkg_check_modules_list ${ARGN}) list(LENGTH _pkg_check_modules_list _pkg_check_modules_cnt) if(PKG_CONFIG_EXECUTABLE) # give out status message telling checked module if (NOT ${_is_silent}) if (_pkg_check_modules_cnt EQUAL 1) message(STATUS "checking for module '${_pkg_check_modules_list}'") else() message(STATUS "checking for modules '${_pkg_check_modules_list}'") endif() endif() set(_pkg_check_modules_packages) set(_pkg_check_modules_failed) # iterate through module list and check whether they exist and match the required version foreach (_pkg_check_modules_pkg ${_pkg_check_modules_list}) set(_pkg_check_modules_exist_query) # check whether version is given if (_pkg_check_modules_pkg MATCHES ".*(>=|=|<=).*") string(REGEX REPLACE "(.*[^><])(>=|=|<=)(.*)" "\\1" _pkg_check_modules_pkg_name "${_pkg_check_modules_pkg}") string(REGEX REPLACE "(.*[^><])(>=|=|<=)(.*)" "\\2" _pkg_check_modules_pkg_op "${_pkg_check_modules_pkg}") string(REGEX REPLACE "(.*[^><])(>=|=|<=)(.*)" "\\3" _pkg_check_modules_pkg_ver "${_pkg_check_modules_pkg}") else() set(_pkg_check_modules_pkg_name "${_pkg_check_modules_pkg}") set(_pkg_check_modules_pkg_op) set(_pkg_check_modules_pkg_ver) endif() # handle the operands if (_pkg_check_modules_pkg_op STREQUAL ">=") list(APPEND _pkg_check_modules_exist_query --atleast-version) endif() if (_pkg_check_modules_pkg_op STREQUAL "=") list(APPEND _pkg_check_modules_exist_query --exact-version) endif() if (_pkg_check_modules_pkg_op STREQUAL "<=") list(APPEND _pkg_check_modules_exist_query --max-version) endif() # create the final query which is of the format: # * --atleast-version # * --exact-version # * --max-version # * --exists if (_pkg_check_modules_pkg_op) list(APPEND _pkg_check_modules_exist_query "${_pkg_check_modules_pkg_ver}") else() list(APPEND _pkg_check_modules_exist_query --exists) endif() _pkgconfig_unset(${_prefix}_${_pkg_check_modules_pkg_name}_VERSION) _pkgconfig_unset(${_prefix}_${_pkg_check_modules_pkg_name}_PREFIX) _pkgconfig_unset(${_prefix}_${_pkg_check_modules_pkg_name}_INCLUDEDIR) _pkgconfig_unset(${_prefix}_${_pkg_check_modules_pkg_name}_LIBDIR) list(APPEND _pkg_check_modules_exist_query "${_pkg_check_modules_pkg_name}") list(APPEND _pkg_check_modules_packages "${_pkg_check_modules_pkg_name}") # execute the query execute_process( COMMAND ${PKG_CONFIG_EXECUTABLE} ${_pkg_check_modules_exist_query} RESULT_VARIABLE _pkgconfig_retval) # evaluate result and tell failures if (_pkgconfig_retval) if(NOT ${_is_silent}) message(STATUS " package '${_pkg_check_modules_pkg}' not found") endif() set(_pkg_check_modules_failed 1) endif() endforeach() if(_pkg_check_modules_failed) # fail when requested if (${_is_required}) message(SEND_ERROR "A required package was not found") endif () else() # when we are here, we checked whether requested modules # exist. Now, go through them and set variables _pkgconfig_set(${_prefix}_FOUND 1) list(LENGTH _pkg_check_modules_packages pkg_count) # iterate through all modules again and set individual variables foreach (_pkg_check_modules_pkg ${_pkg_check_modules_packages}) # handle case when there is only one package required if (pkg_count EQUAL 1) set(_pkg_check_prefix "${_prefix}") else() set(_pkg_check_prefix "${_prefix}_${_pkg_check_modules_pkg}") endif() _pkgconfig_invoke(${_pkg_check_modules_pkg} "${_pkg_check_prefix}" VERSION "" --modversion ) _pkgconfig_invoke(${_pkg_check_modules_pkg} "${_pkg_check_prefix}" PREFIX "" --variable=prefix ) _pkgconfig_invoke(${_pkg_check_modules_pkg} "${_pkg_check_prefix}" INCLUDEDIR "" --variable=includedir ) _pkgconfig_invoke(${_pkg_check_modules_pkg} "${_pkg_check_prefix}" LIBDIR "" --variable=libdir ) if (NOT ${_is_silent}) message(STATUS " found ${_pkg_check_modules_pkg}, version ${_pkgconfig_VERSION}") endif () endforeach() # set variables which are combined for multiple modules _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" LIBRARIES "(^| )-l" --libs-only-l ) _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" LIBRARY_DIRS "(^| )-L" --libs-only-L ) _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" LDFLAGS "" --libs ) _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" LDFLAGS_OTHER "" --libs-only-other ) _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" INCLUDE_DIRS "(^| )-I" --cflags-only-I ) _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" CFLAGS "" --cflags ) _pkgconfig_invoke_dyn("${_pkg_check_modules_packages}" "${_prefix}" CFLAGS_OTHER "" --cflags-only-other ) endif() else() if (${_is_required}) message(SEND_ERROR "pkg-config tool not found") endif () endif() endmacro() ### ### User visible macros start here ### ### macro(pkg_check_modules _prefix _module0) # check cached value if (NOT DEFINED __pkg_config_checked_${_prefix} OR __pkg_config_checked_${_prefix} LESS ${PKG_CONFIG_VERSION} OR NOT ${_prefix}_FOUND) _pkgconfig_parse_options (_pkg_modules _pkg_is_required _pkg_is_silent "${_module0}" ${ARGN}) _pkg_check_modules_internal("${_pkg_is_required}" "${_pkg_is_silent}" "${_prefix}" ${_pkg_modules}) _pkgconfig_set(__pkg_config_checked_${_prefix} ${PKG_CONFIG_VERSION}) endif() endmacro() ### macro(pkg_search_module _prefix _module0) # check cached value if (NOT DEFINED __pkg_config_checked_${_prefix} OR __pkg_config_checked_${_prefix} LESS ${PKG_CONFIG_VERSION} OR NOT ${_prefix}_FOUND) set(_pkg_modules_found 0) _pkgconfig_parse_options(_pkg_modules_alt _pkg_is_required _pkg_is_silent "${_module0}" ${ARGN}) if (NOT ${_pkg_is_silent}) message(STATUS "checking for one of the modules '${_pkg_modules_alt}'") endif () # iterate through all modules and stop at the first working one. foreach(_pkg_alt ${_pkg_modules_alt}) if(NOT _pkg_modules_found) _pkg_check_modules_internal(0 1 "${_prefix}" "${_pkg_alt}") endif() if (${_prefix}_FOUND) set(_pkg_modules_found 1) endif() endforeach() if (NOT ${_prefix}_FOUND) if(${_pkg_is_required}) message(SEND_ERROR "None of the required '${_pkg_modules_alt}' found") endif() endif() _pkgconfig_set(__pkg_config_checked_${_prefix} ${PKG_CONFIG_VERSION}) endif() endmacro() ### Local Variables: ### mode: cmake ### End: nfs-ganesha-2.6.0/src/cmake/modules/FindRADOS.cmake000066400000000000000000000043271324272410200217060ustar00rootroot00000000000000# - Find RADOS # This module accepts the following optional variables: # RADOS_PREFIX = A hint on RADOS install path. # # This module defines the following variables: # RADOS_FOUND = Was RADOS found or not? # RADOS_LIBRARIES = The list of libraries to link to when using RADOS # RADOS_INCLUDE_DIR = The path to RADOS include directory # # On can set RADOS_PREFIX before using find_package(RADOS) and the # module with use the PATH as a hint to find RADOS. # # The hint can be given on the command line too: # cmake -DRADOS_PREFIX=/DATA/ERIC/RADOS /path/to/source if(RADOS_PREFIX) message(STATUS "FindRADOS: using PATH HINT: ${RADOS_PREFIX}") # Try to make the prefix override the normal paths find_path(RADOS_INCLUDE_DIR NAMES rados/librados.h PATHS ${RADOS_PREFIX} PATH_SUFFIXES include NO_DEFAULT_PATH DOC "The RADOS include headers") find_path(RADOS_LIBRARY_DIR NAMES librados.so PATHS ${RADOS_PREFIX} PATH_SUFFIXES lib lib64 NO_DEFAULT_PATH DOC "The RADOS libraries") endif(RADOS_PREFIX) if (NOT RADOS_INCLUDE_DIR) find_path(RADOS_INCLUDE_DIR NAMES rados/librados.h PATHS ${RADOS_PREFIX} PATH_SUFFIXES include DOC "The RADOS include headers") endif (NOT RADOS_INCLUDE_DIR) if (NOT RADOS_LIBRARY_DIR) find_path(RADOS_LIBRARY_DIR NAMES librados.so PATHS ${RADOS_PREFIX} PATH_SUFFIXES lib lib64 DOC "The RADOS libraries") endif (NOT RADOS_LIBRARY_DIR) find_library(RADOS_LIBRARY rados PATHS ${RADOS_LIBRARY_DIR} NO_DEFAULT_PATH) check_library_exists(rados rados_read_op_omap_get_vals2 ${RADOS_LIBRARY_DIR} RADOSLIB) if (NOT RADOSLIB) unset(RADOS_LIBRARY_DIR CACHE) unset(RADOS_INCLUDE_DIR CACHE) endif (NOT RADOSLIB) set(RADOS_LIBRARIES ${RADOS_LIBRARY}) message(STATUS "Found rados libraries: ${RADOS_LIBRARIES}") # handle the QUIETLY and REQUIRED arguments and set PRELUDE_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(RADOS REQUIRED_VARS RADOS_INCLUDE_DIR RADOS_LIBRARY_DIR ) # VERSION FPHSA options not handled by CMake version < 2.8.2) # VERSION_VAR) mark_as_advanced(RADOS_INCLUDE_DIR) mark_as_advanced(RADOS_LIBRARY_DIR) nfs-ganesha-2.6.0/src/cmake/modules/FindRDMA.cmake000066400000000000000000000050571324272410200215620ustar00rootroot00000000000000# - Find RDMA # Find the New TIRPC RPC library # # This module accepts the following optional variables: # RDMA_PATH_HINT = A hint on RDMA install path. # # This module defines the following variables: # RDMA_FOUND = Was RDMA found or not? # RDMA_LIBRARY = The list of libraries to link to when using RDMA # RDMA_INCLUDE_DIR = The path to RDMA include directory(s) # # One can set RDMA_PATH_HINT before using find_package(RDMA) and the # module with use the PATH as a hint to find RDMA. # Alternatively, one can set LIBIBVERBS_PREFIX and LIBRDMACM_PREFIX to the individual # hints for those libraries. # # The hint can be given on the command line too: # cmake -DRDMA_PATH_HINT=/DATA/ERIC/RDMA /path/to/source include(LibFindMacros) # ibverbs if (LIBIBVERBS_PREFIX) set(IBVERBS_PKGCONF_INCLUDE_DIRS ${LIBIBVERBS_PREFIX}/include) set(IBVERBS_PKGCONF_LIBRARY_DIRS ${LIBIBVERBS_PREFIX}/lib64 ${LIBIBVERBS_PREFIX}/lib) else (LIBIBVERBS_PREFIX) set(IBVERBS_PKGCONF_INCLUDE_DIRS ${RDMA_PATH_HINT}/include) set(IBVERBS_PKGCONF_LIBRARY_DIRS ${RDMA_PATH_HINT}/lib64 ${RDMA_PATH_HINT}/lib) endif (LIBIBVERBS_PREFIX) libfind_pkg_detect(IBVERBS libibverbs FIND_PATH infiniband/verbs.h FIND_LIBRARY ibverbs) libfind_process(IBVERBS) # rdmacm if (LIBRDMACM_PREFIX) set(RDMACM_PKGCONF_INCLUDE_DIRS ${LIBRDMACM_PREFIX}/include) set(RDMACM_PKGCONF_LIBRARY_DIRS ${LIBRDMACM_PREFIX}/lib64 ${LIBRDMACM_PREFIX}/lib) else (LIBRDMACM_PREFIX) set(RDMACM_PKGCONF_INCLUDE_DIRS ${RDMA_PATH_HINT}/include) set(RDMACM_PKGCONF_LIBRARY_DIRS ${RDMA_PATH_HINT}/lib64 ${RDMA_PATH_HINT}/lib) endif (LIBRDMACM_PREFIX) libfind_pkg_detect(RDMACM librdmacm FIND_PATH rdma/rdma_cma.h FIND_LIBRARY rdmacm) libfind_process(RDMACM) if (IBVERBS_FOUND AND RDMACM_FOUND) set(RDMA_FOUND true) set(RDMA_LIBRARY ${IBVERBS_LIBRARY} ${RDMACM_LIBRARY}) set(RDMA_INCLUDE_DIR ${IBVERBS_INCLUDE_DIR} ${RDMACM_INCLUDE_DIR}) else (IBVERBS_FOUND AND RDMACM_FOUND) set(RDMA_NOTFOUND true) endif (IBVERBS_FOUND AND RDMACM_FOUND) #if (RDMA_LIBRARY) #libfind_version_header(RDMA version.h RDMA_VERSION) #endif (RDMA_LIBRARY) # handle the QUIETLY and REQUIRED arguments and set PRELUDE_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(RDMA REQUIRED_VARS RDMA_INCLUDE_DIR RDMA_LIBRARY VERSION_VAR RDMA_VERSION) # VERSION FPHSA options not handled by CMake version < 2.8.2) # VERSION_VAR) mark_as_advanced(RDMA_INCLUDE_DIR) mark_as_advanced(RDMA_LIBRARY) nfs-ganesha-2.6.0/src/cmake/modules/FindRGW.cmake000066400000000000000000000063531324272410200214760ustar00rootroot00000000000000# - Find RGW # Find the Linux Trace Toolkit - next generation with associated includes path. # See http://ceph.org/ # # This module accepts the following optional variables: # RGW_PREFIX = A hint on RGW install path. # # This module defines the following variables: # RGW_FOUND = Was RGW found or not? # RGW_LIBRARIES = The list of libraries to link to when using RGW # RGW_INCLUDE_DIR = The path to RGW include directory # # On can set RGW_PREFIX before using find_package(RGW) and the # module with use the PATH as a hint to find RGW. # # The hint can be given on the command line too: # cmake -DRGW_PREFIX=/DATA/ERIC/RGW /path/to/source if(RGW_PREFIX) message(STATUS "FindRGW: using PATH HINT: ${RGW_PREFIX}") # Try to make the prefix override the normal paths find_path(RGW_INCLUDE_DIR NAMES include/rados/librgw.h PATHS ${RGW_PREFIX} NO_DEFAULT_PATH DOC "The RGW include headers") message("RGW_INCLUDE_DIR ${RGW_INCLUDE_DIR}") find_path(RGW_LIBRARY_DIR NAMES librgw.so PATHS ${RGW_PREFIX} PATH_SUFFIXES lib lib64 NO_DEFAULT_PATH DOC "The RGW libraries") endif() if (NOT RGW_INCLUDE_DIR) find_path(RGW_INCLUDE_DIR NAMES include/rados/librgw.h PATHS ${RGW_PREFIX} DOC "The RGW include headers") endif (NOT RGW_INCLUDE_DIR) if (NOT RGW_LIBRARY_DIR) find_path(RGW_LIBRARY_DIR NAMES librgw.so PATHS ${RGW_PREFIX} PATH_SUFFIXES lib lib64 DOC "The RGW libraries") endif (NOT RGW_LIBRARY_DIR) find_library(RGW_LIBRARY rgw PATHS ${RGW_LIBRARY_DIR} NO_DEFAULT_PATH) check_library_exists(rgw rgw_mount ${RGW_LIBRARY_DIR} RGWLIB) if (NOT RGWLIB) unset(RGW_LIBRARY_DIR CACHE) unset(RGW_INCLUDE_DIR CACHE) else (NOT RGWLIB) check_library_exists(rgw rgw_mount2 ${RGW_LIBRARY_DIR} RGW_MOUNT2) if(NOT RGW_MOUNT2) message("Cannot find rgw_mount2. Fallback to use rgw_mount") set(USE_FSAL_RGW_MOUNT2 OFF) else(RGW_MOUNT2) set(USE_FSAL_RGW_MOUNT2 ON) endif(NOT RGW_MOUNT2) endif (NOT RGWLIB) set(RGW_LIBRARIES ${RGW_LIBRARY}) message(STATUS "Found rgw libraries: ${RGW_LIBRARIES}") set(RGW_FILE_HEADER "${RGW_INCLUDE_DIR}/include/rados/rgw_file.h") if (EXISTS ${RGW_FILE_HEADER}) file(STRINGS ${RGW_FILE_HEADER} RGW_MAJOR REGEX "LIBRGW_FILE_VER_MAJOR (\\d*).*$") string(REGEX REPLACE ".+LIBRGW_FILE_VER_MAJOR (\\d*)" "\\1" RGW_MAJOR "${RGW_MAJOR}") file(STRINGS ${RGW_FILE_HEADER} RGW_MINOR REGEX "LIBRGW_FILE_VER_MINOR (\\d*).*$") string(REGEX REPLACE ".+LIBRGW_FILE_VER_MINOR (\\d*)" "\\1" RGW_MINOR "${RGW_MINOR}") file(STRINGS ${RGW_FILE_HEADER} RGW_EXTRA REGEX "LIBRGW_FILE_VER_EXTRA (\\d*).*$") string(REGEX REPLACE ".+LIBRGW_FILE_VER_EXTRA (\\d*)" "\\1" RGW_EXTRA "${RGW_EXTRA}") set(RGW_FILE_VERSION "${RGW_MAJOR}.${RGW_MINOR}.${RGW_EXTRA}") else() set(RGW_FILE_VERSION "0.0.0") endif() # handle the QUIETLY and REQUIRED arguments and set PRELUDE_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(RGW REQUIRED_VARS RGW_INCLUDE_DIR RGW_LIBRARY_DIR VERSION_VAR RGW_FILE_VERSION ) # VERSION FPHSA options not handled by CMake version < 2.8.2) # VERSION_VAR) mark_as_advanced(RGW_INCLUDE_DIR) mark_as_advanced(RGW_LIBRARY_DIR) nfs-ganesha-2.6.0/src/cmake/modules/FindReadline.cmake000066400000000000000000000010101324272410200225430ustar00rootroot00000000000000FIND_PATH(READLINE_INCLUDE_DIR readline/readline.h) FIND_LIBRARY(READLINE_LIBRARY NAMES readline) IF (READLINE_INCLUDE_DIR AND READLINE_LIBRARY) SET(READLINE_FOUND TRUE) ENDIF (READLINE_INCLUDE_DIR AND READLINE_LIBRARY) IF (READLINE_FOUND) IF (NOT Readline_FIND_QUIETLY) MESSAGE(STATUS "Found GNU readline: ${READLINE_LIBRARY}") ENDIF (NOT Readline_FIND_QUIETLY) ELSE (READLINE_FOUND) IF (Readline_FIND_REQUIRED) MESSAGE(FATAL_ERROR "Could not find GNU readline") ENDIF (Readline_FIND_REQUIRED) ENDIF (READLINE_FOUND) nfs-ganesha-2.6.0/src/cmake/modules/FindSanitizers.cmake000066400000000000000000000042721324272410200231700ustar00rootroot00000000000000# The MIT License (MIT) # # Copyright (c) # 2013 Matthew Arsenault # 2015-2016 RWTH Aachen University, Federal Republic of Germany # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # If any of the used compiler is a GNU compiler, add a second option to static # link against the sanitizers. option(SANITIZE_LINK_STATIC "Try to link static against sanitizers." Off) set(FIND_QUIETLY_FLAG "") if (DEFINED Sanitizers_FIND_QUIETLY) set(FIND_QUIETLY_FLAG "QUIET") endif () find_package(ASan ${FIND_QUIETLY_FLAG}) find_package(TSan ${FIND_QUIETLY_FLAG}) find_package(MSan ${FIND_QUIETLY_FLAG}) find_package(UBSan ${FIND_QUIETLY_FLAG}) function(sanitizer_add_blacklist_file FILE) if(NOT IS_ABSOLUTE ${FILE}) set(FILE "${CMAKE_CURRENT_SOURCE_DIR}/${FILE}") endif() get_filename_component(FILE "${FILE}" REALPATH) sanitizer_check_compiler_flags("-fsanitize-blacklist=${FILE}" "SanitizerBlacklist" "SanBlist") endfunction() function(add_sanitizers ...) foreach (TARGET ${ARGV}) add_sanitize_address(${TARGET}) add_sanitize_thread(${TARGET}) add_sanitize_memory(${TARGET}) add_sanitize_undefined(${TARGET}) endforeach () endfunction(add_sanitizers) nfs-ganesha-2.6.0/src/cmake/modules/FindTSan.cmake000066400000000000000000000046771324272410200217130ustar00rootroot00000000000000# The MIT License (MIT) # # Copyright (c) # 2013 Matthew Arsenault # 2015-2016 RWTH Aachen University, Federal Republic of Germany # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. option(SANITIZE_THREAD "Enable ThreadSanitizer for sanitized targets." Off) set(FLAG_CANDIDATES "-g -fsanitize=thread" ) # ThreadSanitizer is not compatible with MemorySanitizer. if (SANITIZE_THREAD AND SANITIZE_MEMORY) message(FATAL_ERROR "ThreadSanitizer is not compatible with " "MemorySanitizer.") endif () include(sanitize-helpers) if (SANITIZE_THREAD) if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") message(WARNING "ThreadSanitizer disabled for target ${TARGET} because " "ThreadSanitizer is supported for Linux systems only.") set(SANITIZE_THREAD Off CACHE BOOL "Enable ThreadSanitizer for sanitized targets." FORCE) elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) message(WARNING "ThreadSanitizer disabled for target ${TARGET} because " "ThreadSanitizer is supported for 64bit systems only.") set(SANITIZE_THREAD Off CACHE BOOL "Enable ThreadSanitizer for sanitized targets." FORCE) else () sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "ThreadSanitizer" "TSan") endif () endif () function (add_sanitize_thread TARGET) if (NOT SANITIZE_THREAD) return() endif () saitizer_add_flags(${TARGET} "ThreadSanitizer" "TSan") endfunction () nfs-ganesha-2.6.0/src/cmake/modules/FindTcMalloc.cmake000066400000000000000000000042151324272410200225300ustar00rootroot00000000000000# - Find Tcmalloc library # Find the native Tcmalloc includes and library # This module defines # TCMALLOC_INCLUDE_DIRS, where to find tcmalloc.h, Set when # TCMALLOC_INCLUDE_DIR is found. # TCMALLOC_LIBRARIES, libraries to link against to use Tcmalloc. # TCMALLOC_ROOT_DIR, The base directory to search for Tcmalloc. # This can also be an environment variable. # TCMALLOC_FOUND, If false, do not try to use Tcmalloc. # # also defined, but not for general use are # TCMALLOC_LIBRARY, where to find the Tcmalloc library. #============================================================================= # Copyright 2011 Blender Foundation. # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # If TCMALLOC_ROOT_DIR was defined in the environment, use it. IF(NOT TCMALLOC_ROOT_DIR AND NOT $ENV{TCMALLOC_ROOT_DIR} STREQUAL "") SET(TCMALLOC_ROOT_DIR $ENV{TCMALLOC_ROOT_DIR}) ENDIF() SET(_tcmalloc_SEARCH_DIRS ${TCMALLOC_ROOT_DIR} /usr/local /sw # Fink /opt/local # DarwinPorts /opt/csw # Blastwave /usr/include/google # Debain tcmalloc minimal /usr/include/gperftools # Debian gperftools ) FIND_PATH(TCMALLOC_INCLUDE_DIR NAMES tcmalloc.h HINTS ${_tcmalloc_SEARCH_DIRS} PATH_SUFFIXES include/tcmalloc ) FIND_LIBRARY(TCMALLOC_LIBRARY NAMES tcmalloc HINTS ${_tcmalloc_SEARCH_DIRS} PATH_SUFFIXES lib64 lib ) # handle the QUIETLY and REQUIRED arguments and set TCMALLOC_FOUND to TRUE if # all listed variables are TRUE INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(Tcmalloc DEFAULT_MSG TCMALLOC_LIBRARY TCMALLOC_INCLUDE_DIR) IF(TCMALLOC_FOUND) SET(TCMALLOC_LIBRARIES ${TCMALLOC_LIBRARY}) SET(TCMALLOC_INCLUDE_DIRS ${TCMALLOC_INCLUDE_DIR}) ENDIF(TCMALLOC_FOUND) MARK_AS_ADVANCED( TCMALLOC_INCLUDE_DIR TCMALLOC_LIBRARY ) nfs-ganesha-2.6.0/src/cmake/modules/FindToolchain.cmake000066400000000000000000000012471324272410200227540ustar00rootroot00000000000000 if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") if(${CMAKE_CXX_COMPILER_ID} MATCHES "MSVC") set(MSVC ON) endif(${CMAKE_CXX_COMPILER_ID} MATCHES "MSVC") endif(${CMAKE_SYSTEM_NAME} MATCHES "Windows") if(UNIX) execute_process( COMMAND ld -V OUTPUT_VARIABLE LINKER_VERS OUTPUT_STRIP_TRAILING_WHITESPACE RESULT_VARIABLE LINKER_VERS_RESULT ) if("${LINKER_VERS_RESULT}" MATCHES "^0$") if("${LINKER_VERS}" MATCHES "GNU gold") set(GOLD_LINKER ON) else("${LINKER_VERS}" MATCHES "GNU gold") endif("${LINKER_VERS}" MATCHES "GNU gold") endif("${LINKER_VERS_RESULT}" MATCHES "^0$") endif(UNIX) message(STATUS "toolchain options processed") nfs-ganesha-2.6.0/src/cmake/modules/FindUBSan.cmake000066400000000000000000000032371324272410200220050ustar00rootroot00000000000000# The MIT License (MIT) # # Copyright (c) # 2013 Matthew Arsenault # 2015-2016 RWTH Aachen University, Federal Republic of Germany # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. option(SANITIZE_UNDEFINED "Enable UndefinedBehaviorSanitizer for sanitized targets." Off) set(FLAG_CANDIDATES "-g -fsanitize=undefined" ) include(sanitize-helpers) if (SANITIZE_UNDEFINED) sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "UndefinedBehaviorSanitizer" "UBSan") endif () function (add_sanitize_undefined TARGET) if (NOT SANITIZE_UNDEFINED) return() endif () saitizer_add_flags(${TARGET} "UndefinedBehaviorSanitizer" "UBSan") endfunction () nfs-ganesha-2.6.0/src/cmake/modules/FindWBclient.cmake000066400000000000000000000027131324272410200225420ustar00rootroot00000000000000# Try to find a sufficiently recent wbclient if(SAMBA4_PREFIX) set(SAMBA4_INCLUDE_DIRS ${SAMBA4_PREFIX}/include) set(SAMBA4_LIBRARIES ${SAMBA4_PREFIX}/lib${LIB_SUFFIX}) endif() if(NOT WIN32) find_package(PkgConfig) if(PKG_CONFIG_FOUND) pkg_check_modules(_WBCLIENT_PC QUIET wbclient) endif(PKG_CONFIG_FOUND) endif(NOT WIN32) find_path(WBCLIENT_INCLUDE_DIR wbclient.h ${_WBCLIENT_PC_INCLUDE_DIRS} ${SAMBA4_INCLUDE_DIRS} /usr/include /usr/local/include ) LIST(APPEND CMAKE_REQUIRED_INCLUDES ${WBCLIENT_INCLUDE_DIR}) find_library(WBCLIENT_LIBRARIES NAMES wbclient PATHS ${_WBCLIENT_PC_LIBDIR} ) check_library_exists( wbclient wbcLookupSids ${WBCLIENT_LIBRARIES} WBCLIENT_LIB_OK ) # the stdint and stdbool includes are required (silly Cmake) check_include_files("stdint.h;stdbool.h;wbclient.h" WBCLIENT_H) # XXX this check is doing the heavy lifting LIST(APPEND CMAKE_REQUIRED_LIBRARIES ${WBCLIENT_LIBRARIES}) if(WBCLIENT_H) check_c_source_compiles(" /* do the enum */ #include #include #include #include int main(void) { enum wbcAuthUserLevel level = WBC_AUTH_USER_LEVEL_PAC; return (0); }" WBCLIENT4_H) endif(WBCLIENT_H) if(WBCLIENT_LIB_OK AND WBCLIENT4_H) set(WBCLIENT_FOUND 1) message(STATUS "Found Winbind4 client: ${WBCLIENT_LIB}") else(WBCLIENT_LIB_OK AND WBCLIENT4_H) message(STATUS "Winbind4 client not found ${SAMBA4_PREFIX}/lib") endif(WBCLIENT_LIB_OK AND WBCLIENT4_H) nfs-ganesha-2.6.0/src/cmake/modules/GetGitRevisionDescription.cmake000066400000000000000000000072761324272410200253510ustar00rootroot00000000000000# - Returns a version string from Git # # These functions force a re-configure on each git commit so that you can # trust the values of the variables in your build system. # # get_git_head_revision( [ ...]) # # Returns the refspec and sha hash of the current head revision # # git_describe( [ ...]) # # Returns the results of git describe on the source tree, and adjusting # the output so that it tests false if an error occurs. # # git_get_exact_tag( [ ...]) # # Returns the results of git describe --exact-match on the source tree, # and adjusting the output so that it tests false if there was no exact # matching tag. # # Requires CMake 2.6 or newer (uses the 'function' command) # # Original Author: # 2009-2010 Ryan Pavlik # http://academic.cleardefinition.com # Iowa State University HCI Graduate Program/VRAC # # Copyright Iowa State University 2009-2010. # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) if(__get_git_revision_description) return() endif() set(__get_git_revision_description YES) # We must run the following at "include" time, not at function call time, # to find the path to this module rather than the path to a calling list file get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) function(get_git_head_revision _refspecvar _hashvar) set(GIT_PARENT_DIR "${CMAKE_SOURCE_DIR}") set(GIT_DIR "${GIT_PARENT_DIR}/.git") while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) # We have reached the root directory, we are not in git set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) return() endif() set(GIT_DIR "${GIT_PARENT_DIR}/.git") endwhile() set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") if(NOT EXISTS "${GIT_DATA}") file(MAKE_DIRECTORY "${GIT_DATA}") endif() if(NOT EXISTS "${GIT_DIR}/HEAD") return() endif() set(HEAD_FILE "${GIT_DATA}/HEAD") configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" "${GIT_DATA}/grabRef.cmake" @ONLY) include("${GIT_DATA}/grabRef.cmake") set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) endfunction() function(git_describe _var) if(NOT GIT_FOUND) find_package(Git QUIET) endif() get_git_head_revision(refspec hash) if(NOT GIT_FOUND) set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) return() endif() if(NOT hash) set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) return() endif() # TODO sanitize #if((${ARGN}" MATCHES "&&") OR # (ARGN MATCHES "||") OR # (ARGN MATCHES "\\;")) # message("Please report the following error to the project!") # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") #endif() #message(STATUS "Arguments to execute_process: ${ARGN}") execute_process(COMMAND "${GIT_EXECUTABLE}" describe ${hash} ${ARGN} WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" RESULT_VARIABLE res OUTPUT_VARIABLE out ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT res EQUAL 0) set(out "${out}-${res}-NOTFOUND") endif() set(${_var} "${out}" PARENT_SCOPE) endfunction() function(git_get_exact_tag _var) git_describe(out --exact-match ${ARGN}) set(${_var} "${out}" PARENT_SCOPE) endfunction() nfs-ganesha-2.6.0/src/cmake/modules/GetGitRevisionDescription.cmake.in000066400000000000000000000022621324272410200257440ustar00rootroot00000000000000# # Internal file for GetGitRevisionDescription.cmake # # Requires CMake 2.6 or newer (uses the 'function' command) # # Original Author: # 2009-2010 Ryan Pavlik # http://academic.cleardefinition.com # Iowa State University HCI Graduate Program/VRAC # # Copyright Iowa State University 2009-2010. # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) set(HEAD_HASH) file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) if(HEAD_CONTENTS MATCHES "ref") # named branch string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") if(EXISTS "@GIT_DIR@/${HEAD_REF}") configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}") configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) set(HEAD_HASH "${HEAD_REF}") endif() else() # detached HEAD configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) endif() if(NOT HEAD_HASH) file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) string(STRIP "${HEAD_HASH}" HEAD_HASH) endif() nfs-ganesha-2.6.0/src/cmake/modules/InstallClobberImmune.cmake000066400000000000000000000025561324272410200243110ustar00rootroot00000000000000# Determines at `make install` time if a file, typically a configuration # file placed in $PREFIX/etc, shouldn't be installed to prevent overwrite # of an existing file. # # _srcfile: the file to install # _dstfile: the absolute file name after installation macro(InstallClobberImmune _srcfile _dstfile) install(CODE " set(_destfile \"${_dstfile}\") if (NOT \"\$ENV{DESTDIR}\" STREQUAL \"\") # prepend install root prefix with install-time DESTDIR set(_destfile \"\$ENV{DESTDIR}/${_dstfile}\") endif () if (EXISTS \${_destfile}) message(STATUS \"Skipping: \${_destfile} (already exists)\") execute_process(COMMAND \"${CMAKE_COMMAND}\" -E compare_files ${_srcfile} \${_destfile} RESULT_VARIABLE _diff) if (NOT \"\${_diff}\" STREQUAL \"0\") message(STATUS \"Installing: \${_destfile}.example\") configure_file(${_srcfile} \${_destfile}.example COPYONLY) endif () else () message(STATUS \"Installing: \${_destfile}\") # install() is not scriptable within install(), and # configure_file() is the next best thing configure_file(${_srcfile} \${_destfile} COPYONLY) # TODO: create additional install_manifest files? endif () ") endmacro(InstallClobberImmune) nfs-ganesha-2.6.0/src/cmake/modules/InstallPackageConfigFile.cmake000066400000000000000000000052731324272410200250460ustar00rootroot00000000000000include(InstallClobberImmune) # This macro can be used to install configuration files which # users are expected to modify after installation. It will: # # - If binary packaging is enabled: # Install the file in the typical CMake fashion, but append to the # INSTALLED_CONFIG_FILES cache variable for use with the Mac package's # pre/post install scripts # # - If binary packaging is not enabled: # Install the script in a way such that it will check at `make install` # time whether the file does not exist. See InstallClobberImmune.cmake # # - Always create a target "install-example-configs" which installs an # example version of the config file. # # _srcfile: the absolute path to the file to install # _dstdir: absolute path to the directory in which to install the file # _dstfilename: how to (re)name the file inside _dstdir macro(InstallPackageConfigFile _srcfile _dstdir _dstfilename) set(_dstfile ${_dstdir}/${_dstfilename}) if (BINARY_PACKAGING_MODE) # If packaging mode is enabled, always install the distribution's # version of the file. The Mac package's pre/post install scripts # or native functionality of RPMs will take care of not clobbering it. install(FILES ${_srcfile} DESTINATION ${_dstdir} RENAME ${_dstfilename}) # This cache variable is what the Mac package pre/post install scripts # use to avoid clobbering user-modified config files set(INSTALLED_CONFIG_FILES "${INSTALLED_CONFIG_FILES} ${_dstfile}" CACHE STRING "" FORCE) # Additionally, the Mac PackageMaker packages don't have any automatic # handling of configuration file conflicts so install an example file # that the post install script will cleanup in the case it's extraneous if (APPLE) install(FILES ${_srcfile} DESTINATION ${_dstdir} RENAME ${_dstfilename}.example) endif () else () # Have `make install` check at run time whether the file does not exist InstallClobberImmune(${_srcfile} ${_dstfile}) endif () if (NOT TARGET install-example-configs) add_custom_target(install-example-configs COMMENT "Installed example configuration files") endif () # '/' is invalid in target names, so replace w/ '.' string(REGEX REPLACE "/" "." _flatsrc ${_srcfile}) set(_example ${_dstfile}.example) add_custom_target(install-example-config-${_flatsrc} COMMAND "${CMAKE_COMMAND}" -E copy ${_srcfile} \${DESTDIR}${_example} COMMENT "Installing ${_example}") add_dependencies(install-example-configs install-example-config-${_flatsrc}) endmacro(InstallPackageConfigFile) nfs-ganesha-2.6.0/src/cmake/modules/LibFindMacros.cmake000066400000000000000000000247051324272410200227130ustar00rootroot00000000000000# Version 2.2 # Public Domain, originally written by Lasse Kärkkäinen # Maintained at https://github.com/Tronic/cmake-modules # Please send your improvements as pull requests on Github. # Find another package and make it a dependency of the current package. # This also automatically forwards the "REQUIRED" argument. # Usage: libfind_package( [extra args to find_package]) macro (libfind_package PREFIX PKG) set(${PREFIX}_args ${PKG} ${ARGN}) if (${PREFIX}_FIND_REQUIRED) set(${PREFIX}_args ${${PREFIX}_args} REQUIRED) endif() find_package(${${PREFIX}_args}) set(${PREFIX}_DEPENDENCIES ${${PREFIX}_DEPENDENCIES};${PKG}) unset(${PREFIX}_args) endmacro() # A simple wrapper to make pkg-config searches a bit easier. # Works the same as CMake's internal pkg_check_modules but is always quiet. macro (libfind_pkg_check_modules) find_package(PkgConfig QUIET) if (PKG_CONFIG_FOUND) pkg_check_modules(${ARGN} QUIET) endif() endmacro() # Avoid useless copy&pasta by doing what most simple libraries do anyway: # pkg-config, find headers, find library. # Usage: libfind_pkg_detect( FIND_PATH [other args] FIND_LIBRARY [other args]) # E.g. libfind_pkg_detect(SDL2 sdl2 FIND_PATH SDL.h PATH_SUFFIXES SDL2 FIND_LIBRARY SDL2) function (libfind_pkg_detect PREFIX) # Parse arguments set(argname pkgargs) foreach (i ${ARGN}) if ("${i}" STREQUAL "FIND_PATH") set(argname pathargs) elseif ("${i}" STREQUAL "FIND_LIBRARY") set(argname libraryargs) else() set(${argname} ${${argname}} ${i}) endif() endforeach() if (NOT pkgargs) message(FATAL_ERROR "libfind_pkg_detect requires at least a pkg_config package name to be passed.") endif() # Find library libfind_pkg_check_modules(${PREFIX}_PKGCONF ${pkgargs}) if (pathargs) find_path(${PREFIX}_INCLUDE_DIR NAMES ${pathargs} HINTS ${${PREFIX}_PKGCONF_INCLUDE_DIRS}) endif() if (libraryargs) find_library(${PREFIX}_LIBRARY NAMES ${libraryargs} HINTS ${${PREFIX}_PKGCONF_LIBRARY_DIRS}) endif() endfunction() # Extracts a version #define from a version.h file, output stored to _VERSION. # Usage: libfind_version_header(Foobar foobar/version.h FOOBAR_VERSION_STR) # Fourth argument "QUIET" may be used for silently testing different define names. # This function does nothing if the version variable is already defined. function (libfind_version_header PREFIX VERSION_H DEFINE_NAME) # Skip processing if we already have a version or if the include dir was not found if (${PREFIX}_VERSION OR NOT ${PREFIX}_INCLUDE_DIR) return() endif() set(quiet ${${PREFIX}_FIND_QUIETLY}) # Process optional arguments foreach(arg ${ARGN}) if (arg STREQUAL "QUIET") set(quiet TRUE) else() message(AUTHOR_WARNING "Unknown argument ${arg} to libfind_version_header ignored.") endif() endforeach() # Read the header and parse for version number set(filename "${${PREFIX}_INCLUDE_DIR}/${VERSION_H}") if (NOT EXISTS ${filename}) if (NOT quiet) message(AUTHOR_WARNING "Unable to find ${${PREFIX}_INCLUDE_DIR}/${VERSION_H}") endif() return() endif() file(READ "${filename}" header) string(REGEX REPLACE ".*#[ \t]*define[ \t]*${DEFINE_NAME}[ \t]*\"([^\n]*)\".*" "\\1" match "${header}") # No regex match? if (match STREQUAL header) if (NOT quiet) message(AUTHOR_WARNING "Unable to find \#define ${DEFINE_NAME} \"\" from ${${PREFIX}_INCLUDE_DIR}/${VERSION_H}") endif() return() endif() # Export the version string set(${PREFIX}_VERSION "${match}" PARENT_SCOPE) endfunction() # Do the final processing once the paths have been detected. # If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain # all the variables, each of which contain one include directory. # Ditto for ${PREFIX}_PROCESS_LIBS and library files. # Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES. # Also handles errors in case library detection was required, etc. function (libfind_process PREFIX) # Skip processing if already processed during this configuration run if (${PREFIX}_FOUND) return() endif() set(found TRUE) # Start with the assumption that the package was found # Did we find any files? Did we miss includes? These are for formatting better error messages. set(some_files FALSE) set(missing_headers FALSE) # Shorthands for some variables that we need often set(quiet ${${PREFIX}_FIND_QUIETLY}) set(required ${${PREFIX}_FIND_REQUIRED}) set(exactver ${${PREFIX}_FIND_VERSION_EXACT}) set(findver "${${PREFIX}_FIND_VERSION}") set(version "${${PREFIX}_VERSION}") # Lists of config option names (all, includes, libs) unset(configopts) set(includeopts ${${PREFIX}_PROCESS_INCLUDES}) set(libraryopts ${${PREFIX}_PROCESS_LIBS}) # Process deps to add to foreach (i ${PREFIX} ${${PREFIX}_DEPENDENCIES}) if (DEFINED ${i}_INCLUDE_OPTS OR DEFINED ${i}_LIBRARY_OPTS) # The package seems to export option lists that we can use, woohoo! list(APPEND includeopts ${${i}_INCLUDE_OPTS}) list(APPEND libraryopts ${${i}_LIBRARY_OPTS}) else() # If plural forms don't exist or they equal singular forms if ((NOT DEFINED ${i}_INCLUDE_DIRS AND NOT DEFINED ${i}_LIBRARIES) OR ({i}_INCLUDE_DIR STREQUAL ${i}_INCLUDE_DIRS AND ${i}_LIBRARY STREQUAL ${i}_LIBRARIES)) # Singular forms can be used if (DEFINED ${i}_INCLUDE_DIR) list(APPEND includeopts ${i}_INCLUDE_DIR) endif() if (DEFINED ${i}_LIBRARY) list(APPEND libraryopts ${i}_LIBRARY) endif() else() # Oh no, we don't know the option names message(FATAL_ERROR "We couldn't determine config variable names for ${i} includes and libs. Aieeh!") endif() endif() endforeach() if (includeopts) list(REMOVE_DUPLICATES includeopts) endif() if (libraryopts) list(REMOVE_DUPLICATES libraryopts) endif() string(REGEX REPLACE ".*[ ;]([^ ;]*(_INCLUDE_DIRS|_LIBRARIES))" "\\1" tmp "${includeopts} ${libraryopts}") if (NOT tmp STREQUAL "${includeopts} ${libraryopts}") message(AUTHOR_WARNING "Plural form ${tmp} found in config options of ${PREFIX}. This works as before but is now deprecated. Please only use singular forms INCLUDE_DIR and LIBRARY, and update your find scripts for LibFindMacros > 2.0 automatic dependency system (most often you can simply remove the PROCESS variables entirely).") endif() # Include/library names separated by spaces (notice: not CMake lists) unset(includes) unset(libs) # Process all includes and set found false if any are missing foreach (i ${includeopts}) list(APPEND configopts ${i}) if (NOT "${${i}}" STREQUAL "${i}-NOTFOUND") list(APPEND includes "${${i}}") else() set(found FALSE) set(missing_headers TRUE) endif() endforeach() # Process all libraries and set found false if any are missing foreach (i ${libraryopts}) list(APPEND configopts ${i}) if (NOT "${${i}}" STREQUAL "${i}-NOTFOUND") list(APPEND libs "${${i}}") else() set (found FALSE) endif() endforeach() # Version checks if (found AND findver) if (NOT version) message(WARNING "The find module for ${PREFIX} does not provide version information, so we'll just assume that it is OK. Please fix the module or remove package version requirements to get rid of this warning.") elseif (version VERSION_LESS findver OR (exactver AND NOT version VERSION_EQUAL findver)) set(found FALSE) set(version_unsuitable TRUE) endif() endif() # If all-OK, hide all config options, export variables, print status and exit if (found) foreach (i ${configopts}) mark_as_advanced(${i}) endforeach() if (NOT quiet) message(STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}") if (LIBFIND_DEBUG) message(STATUS " ${PREFIX}_DEPENDENCIES=${${PREFIX}_DEPENDENCIES}") message(STATUS " ${PREFIX}_INCLUDE_OPTS=${includeopts}") message(STATUS " ${PREFIX}_INCLUDE_DIRS=${includes}") message(STATUS " ${PREFIX}_LIBRARY_OPTS=${libraryopts}") message(STATUS " ${PREFIX}_LIBRARIES=${libs}") endif() set (${PREFIX}_INCLUDE_OPTS ${includeopts} PARENT_SCOPE) set (${PREFIX}_LIBRARY_OPTS ${libraryopts} PARENT_SCOPE) set (${PREFIX}_INCLUDE_DIRS ${includes} PARENT_SCOPE) set (${PREFIX}_LIBRARIES ${libs} PARENT_SCOPE) set (${PREFIX}_FOUND TRUE PARENT_SCOPE) endif() return() endif() # Format messages for debug info and the type of error set(vars "Relevant CMake configuration variables:\n") foreach (i ${configopts}) mark_as_advanced(CLEAR ${i}) set(val ${${i}}) if ("${val}" STREQUAL "${i}-NOTFOUND") set (val "") elseif (val AND NOT EXISTS ${val}) set (val "${val} (does not exist)") else() set(some_files TRUE) endif() set(vars "${vars} ${i}=${val}\n") endforeach() set(vars "${vars}You may use CMake GUI, cmake -D or ccmake to modify the values. Delete CMakeCache.txt to discard all values and force full re-detection if necessary.\n") if (version_unsuitable) set(msg "${PREFIX} ${${PREFIX}_VERSION} was found but") if (exactver) set(msg "${msg} only version ${findver} is acceptable.") else() set(msg "${msg} version ${findver} is the minimum requirement.") endif() else() if (missing_headers) set(msg "We could not find development headers for ${PREFIX}. Do you have the necessary dev package installed?") elseif (some_files) set(msg "We only found some files of ${PREFIX}, not all of them. Perhaps your installation is incomplete or maybe we just didn't look in the right place?") if(findver) set(msg "${msg} This could also be caused by incompatible version (if it helps, at least ${PREFIX} ${findver} should work).") endif() else() set(msg "We were unable to find package ${PREFIX}.") endif() endif() # Fatal error out if REQUIRED if (required) set(msg "REQUIRED PACKAGE NOT FOUND\n${msg} This package is REQUIRED and you need to install it or adjust CMake configuration in order to continue building ${CMAKE_PROJECT_NAME}.") message(FATAL_ERROR "${msg}\n${vars}") endif() # Otherwise just print a nasty warning if (NOT quiet) message(WARNING "WARNING: MISSING PACKAGE\n${msg} This package is NOT REQUIRED and you may ignore this warning but by doing so you may miss some functionality of ${CMAKE_PROJECT_NAME}. \n${vars}") endif() endfunction() nfs-ganesha-2.6.0/src/cmake/modules/sanitize-helpers.cmake000066400000000000000000000163521324272410200235240ustar00rootroot00000000000000# The MIT License (MIT) # # Copyright (c) # 2013 Matthew Arsenault # 2015-2016 RWTH Aachen University, Federal Republic of Germany # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # Helper function to get the language of a source file. function (sanitizer_lang_of_source FILE RETURN_VAR) get_filename_component(FILE_EXT "${FILE}" EXT) string(TOLOWER "${FILE_EXT}" FILE_EXT) string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT) get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) foreach (LANG ${ENABLED_LANGUAGES}) list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP) if (NOT ${TEMP} EQUAL -1) set(${RETURN_VAR} "${LANG}" PARENT_SCOPE) return() endif () endforeach() set(${RETURN_VAR} "" PARENT_SCOPE) endfunction () # Helper function to get compilers used by a target. function (sanitizer_target_compilers TARGET RETURN_VAR) # Check if all sources for target use the same compiler. If a target uses # e.g. C and Fortran mixed and uses different compilers (e.g. clang and # gfortran) this can trigger huge problems, because different compilers may # use different implementations for sanitizers. set(BUFFER "") get_target_property(TSOURCES ${TARGET} SOURCES) foreach (FILE ${TSOURCES}) # If expression was found, FILE is a generator-expression for an object # library. Object libraries will be ignored. string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE}) if ("${_file}" STREQUAL "") sanitizer_lang_of_source(${FILE} LANG) if (LANG) list(APPEND BUFFER ${CMAKE_${LANG}_COMPILER_ID}) endif () endif () endforeach () list(REMOVE_DUPLICATES BUFFER) set(${RETURN_VAR} "${BUFFER}" PARENT_SCOPE) endfunction () # Helper function to check compiler flags for language compiler. function (sanitizer_check_compiler_flag FLAG LANG VARIABLE) if (${LANG} STREQUAL "C") include(CheckCCompilerFlag) check_c_compiler_flag("${FLAG}" ${VARIABLE}) elseif (${LANG} STREQUAL "CXX") include(CheckCXXCompilerFlag) check_cxx_compiler_flag("${FLAG}" ${VARIABLE}) elseif (${LANG} STREQUAL "Fortran") # CheckFortranCompilerFlag was introduced in CMake 3.x. To be compatible # with older Cmake versions, we will check if this module is present # before we use it. Otherwise we will define Fortran coverage support as # not available. include(CheckFortranCompilerFlag OPTIONAL RESULT_VARIABLE INCLUDED) if (INCLUDED) check_fortran_compiler_flag("${FLAG}" ${VARIABLE}) elseif (NOT CMAKE_REQUIRED_QUIET) message(STATUS "Performing Test ${VARIABLE}") message(STATUS "Performing Test ${VARIABLE}" " - Failed (Check not supported)") endif () endif() endfunction () # Helper function to test compiler flags. function (sanitizer_check_compiler_flags FLAG_CANDIDATES NAME PREFIX) set(CMAKE_REQUIRED_QUIET ${${PREFIX}_FIND_QUIETLY}) get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) foreach (LANG ${ENABLED_LANGUAGES}) # Sanitizer flags are not dependend on language, but the used compiler. # So instead of searching flags foreach language, search flags foreach # compiler used. set(COMPILER ${CMAKE_${LANG}_COMPILER_ID}) if (NOT DEFINED ${PREFIX}_${COMPILER}_FLAGS) foreach (FLAG ${FLAG_CANDIDATES}) if(NOT CMAKE_REQUIRED_QUIET) message(STATUS "Try ${COMPILER} ${NAME} flag = [${FLAG}]") endif() set(CMAKE_REQUIRED_FLAGS "${FLAG}") unset(${PREFIX}_FLAG_DETECTED CACHE) sanitizer_check_compiler_flag("${FLAG}" ${LANG} ${PREFIX}_FLAG_DETECTED) if (${PREFIX}_FLAG_DETECTED) # If compiler is a GNU compiler, search for static flag, if # SANITIZE_LINK_STATIC is enabled. if (SANITIZE_LINK_STATIC AND (${COMPILER} STREQUAL "GNU")) string(TOLOWER ${PREFIX} PREFIX_lower) sanitizer_check_compiler_flag( "-static-lib${PREFIX_lower}" ${LANG} ${PREFIX}_STATIC_FLAG_DETECTED) if (${PREFIX}_STATIC_FLAG_DETECTED) set(FLAG "-static-lib${PREFIX_lower} ${FLAG}") endif () endif () set(${PREFIX}_${COMPILER}_FLAGS "${FLAG}" CACHE STRING "${NAME} flags for ${COMPILER} compiler.") mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS) break() endif () endforeach () if (NOT ${PREFIX}_FLAG_DETECTED) set(${PREFIX}_${COMPILER}_FLAGS "" CACHE STRING "${NAME} flags for ${COMPILER} compiler.") mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS) endif () endif () endforeach () endfunction () # Helper to assign sanitizer flags for TARGET. function (saitizer_add_flags TARGET NAME PREFIX) # Get list of compilers used by target and check, if target can be checked # by sanitizer. sanitizer_target_compilers(${TARGET} TARGET_COMPILER) list(LENGTH TARGET_COMPILER NUM_COMPILERS) if (NUM_COMPILERS GREATER 1) message(WARNING "${NAME} disabled for target ${TARGET} because it will " "be compiled by different compilers.") return() elseif ((NUM_COMPILERS EQUAL 0) OR ("${${PREFIX}_${TARGET_COMPILER}_FLAGS}" STREQUAL "")) message(WARNING "${NAME} disabled for target ${TARGET} because there is" " no sanitizer available for target sources.") return() endif() # Set compile- and link-flags for target. set_property(TARGET ${TARGET} APPEND_STRING PROPERTY COMPILE_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") set_property(TARGET ${TARGET} APPEND_STRING PROPERTY COMPILE_FLAGS " ${SanBlist_${TARGET_COMPILER}_FLAGS}") set_property(TARGET ${TARGET} APPEND_STRING PROPERTY LINK_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") endfunction () nfs-ganesha-2.6.0/src/cmake/portability_cmake_2.8/000077500000000000000000000000001324272410200216465ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/cmake/portability_cmake_2.8/FindBISON.cmake000066400000000000000000000161631324272410200243320ustar00rootroot00000000000000# - Find bison executable and provides macros to generate custom build rules # The module defines the following variables: # # BISON_EXECUTABLE - path to the bison program # BISON_VERSION - version of bison # BISON_FOUND - true if the program was found # # The minimum required version of bison can be specified using the # standard CMake syntax, e.g. find_package(BISON 2.1.3) # # If bison is found, the module defines the macros: # BISON_TARGET( [VERBOSE ] # [COMPILE_FLAGS ]) # which will create a custom rule to generate a parser. is # the path to a yacc file. is the name of the source file # generated by bison. A header file is also be generated, and contains # the token list. If COMPILE_FLAGS option is specified, the next # parameter is added in the bison command line. if VERBOSE option is # specified, is created and contains verbose descriptions of the # grammar and parser. The macro defines a set of variables: # BISON_${Name}_DEFINED - true is the macro ran successfully # BISON_${Name}_INPUT - The input source file, an alias for # BISON_${Name}_OUTPUT_SOURCE - The source file generated by bison # BISON_${Name}_OUTPUT_HEADER - The header file generated by bison # BISON_${Name}_OUTPUTS - The sources files generated by bison # BISON_${Name}_COMPILE_FLAGS - Options used in the bison command line # # ==================================================================== # Example: # # find_package(BISON) # BISON_TARGET(MyParser parser.y ${CMAKE_CURRENT_BINARY_DIR}/parser.cpp) # add_executable(Foo main.cpp ${BISON_MyParser_OUTPUTS}) # ==================================================================== #============================================================================= # Copyright 2009 Kitware, Inc. # Copyright 2006 Tristan Carel # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) find_program(BISON_EXECUTABLE NAMES bison win_bison DOC "path to the bison executable") mark_as_advanced(BISON_EXECUTABLE) if(BISON_EXECUTABLE) # the bison commands should be executed with the C locale, otherwise # the message (which are parsed) may be translated set(_Bison_SAVED_LC_ALL "$ENV{LC_ALL}") set(ENV{LC_ALL} C) execute_process(COMMAND ${BISON_EXECUTABLE} --version OUTPUT_VARIABLE BISON_version_output ERROR_VARIABLE BISON_version_error RESULT_VARIABLE BISON_version_result OUTPUT_STRIP_TRAILING_WHITESPACE) set(ENV{LC_ALL} ${_Bison_SAVED_LC_ALL}) if(NOT ${BISON_version_result} EQUAL 0) message(SEND_ERROR "Command \"${BISON_EXECUTABLE} --version\" failed with output:\n${BISON_version_error}") else() # Bison++ if("${BISON_version_output}" MATCHES "^bison\\+\\+") string(REGEX REPLACE "^bison\\+\\+ Version ([^,]+).*" "\\1" BISON_VERSION "${BISON_version_output}") # GNU Bison elseif("${BISON_version_output}" MATCHES "^bison[^+]") string(REGEX REPLACE "^bison \\(GNU Bison\\) ([^\n]+)\n.*" "\\1" BISON_VERSION "${BISON_version_output}") elseif("${BISON_version_output}" MATCHES "^GNU Bison ") string(REGEX REPLACE "^GNU Bison (version )?([^\n]+).*" "\\2" BISON_VERSION "${BISON_version_output}") endif() endif() # internal macro macro(BISON_TARGET_option_verbose Name BisonOutput filename) list(APPEND BISON_TARGET_cmdopt "--verbose") get_filename_component(BISON_TARGET_output_path "${BisonOutput}" PATH) get_filename_component(BISON_TARGET_output_name "${BisonOutput}" NAME_WE) add_custom_command(OUTPUT ${filename} COMMAND ${CMAKE_COMMAND} ARGS -E copy "${BISON_TARGET_output_path}/${BISON_TARGET_output_name}.output" "${filename}" DEPENDS "${BISON_TARGET_output_path}/${BISON_TARGET_output_name}.output" COMMENT "[BISON][${Name}] Copying bison verbose table to ${filename}" WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) set(BISON_${Name}_VERBOSE_FILE ${filename}) list(APPEND BISON_TARGET_extraoutputs "${BISON_TARGET_output_path}/${BISON_TARGET_output_name}.output") endmacro() # internal macro macro(BISON_TARGET_option_extraopts Options) set(BISON_TARGET_extraopts "${Options}") separate_arguments(BISON_TARGET_extraopts) list(APPEND BISON_TARGET_cmdopt ${BISON_TARGET_extraopts}) endmacro() #============================================================ # BISON_TARGET (public macro) #============================================================ # macro(BISON_TARGET Name BisonInput BisonOutput) set(BISON_TARGET_output_header "") set(BISON_TARGET_cmdopt "") set(BISON_TARGET_outputs "${BisonOutput}") if(NOT ${ARGC} EQUAL 3 AND NOT ${ARGC} EQUAL 5 AND NOT ${ARGC} EQUAL 7) message(SEND_ERROR "Usage") else() # Parsing parameters if(${ARGC} GREATER 5 OR ${ARGC} EQUAL 5) if("${ARGV3}" STREQUAL "VERBOSE") BISON_TARGET_option_verbose(${Name} ${BisonOutput} "${ARGV4}") endif() if("${ARGV3}" STREQUAL "COMPILE_FLAGS") BISON_TARGET_option_extraopts("${ARGV4}") endif() endif() if(${ARGC} EQUAL 7) if("${ARGV5}" STREQUAL "VERBOSE") BISON_TARGET_option_verbose(${Name} ${BisonOutput} "${ARGV6}") endif() if("${ARGV5}" STREQUAL "COMPILE_FLAGS") BISON_TARGET_option_extraopts("${ARGV6}") endif() endif() # Header's name generated by bison (see option -d) list(APPEND BISON_TARGET_cmdopt "-d") string(REGEX REPLACE "^(.*)(\\.[^.]*)$" "\\2" _fileext "${ARGV2}") string(REPLACE "c" "h" _fileext ${_fileext}) string(REGEX REPLACE "^(.*)(\\.[^.]*)$" "\\1${_fileext}" BISON_${Name}_OUTPUT_HEADER "${ARGV2}") list(APPEND BISON_TARGET_outputs "${BISON_${Name}_OUTPUT_HEADER}") add_custom_command(OUTPUT ${BISON_TARGET_outputs} ${BISON_TARGET_extraoutputs} COMMAND ${BISON_EXECUTABLE} ARGS ${BISON_TARGET_cmdopt} -o ${ARGV2} ${ARGV1} DEPENDS ${ARGV1} COMMENT "[BISON][${Name}] Building parser with bison ${BISON_VERSION}" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) # define target variables set(BISON_${Name}_DEFINED TRUE) set(BISON_${Name}_INPUT ${ARGV1}) set(BISON_${Name}_OUTPUTS ${BISON_TARGET_outputs}) set(BISON_${Name}_COMPILE_FLAGS ${BISON_TARGET_cmdopt}) set(BISON_${Name}_OUTPUT_SOURCE "${BisonOutput}") endif() endmacro() # #============================================================ endif() include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) FIND_PACKAGE_HANDLE_STANDARD_ARGS(BISON REQUIRED_VARS BISON_EXECUTABLE VERSION_VAR BISON_VERSION) # FindBISON.cmake ends here nfs-ganesha-2.6.0/src/cmake/portability_cmake_2.8/FindFLEX.cmake000066400000000000000000000144271324272410200242170ustar00rootroot00000000000000# - Find flex executable and provides a macro to generate custom build rules # # The module defines the following variables: # FLEX_FOUND - true is flex executable is found # FLEX_EXECUTABLE - the path to the flex executable # FLEX_VERSION - the version of flex # FLEX_LIBRARIES - The flex libraries # FLEX_INCLUDE_DIRS - The path to the flex headers # # The minimum required version of flex can be specified using the # standard syntax, e.g. find_package(FLEX 2.5.13) # # # If flex is found on the system, the module provides the macro: # FLEX_TARGET(Name FlexInput FlexOutput [COMPILE_FLAGS ]) # which creates a custom command to generate the file from # the file. If COMPILE_FLAGS option is specified, the next # parameter is added to the flex command line. Name is an alias used to # get details of this custom command. Indeed the macro defines the # following variables: # FLEX_${Name}_DEFINED - true is the macro ran successfully # FLEX_${Name}_OUTPUTS - the source file generated by the custom rule, an # alias for FlexOutput # FLEX_${Name}_INPUT - the flex source file, an alias for ${FlexInput} # # Flex scanners oftenly use tokens defined by Bison: the code generated # by Flex depends of the header generated by Bison. This module also # defines a macro: # ADD_FLEX_BISON_DEPENDENCY(FlexTarget BisonTarget) # which adds the required dependency between a scanner and a parser # where and are the first parameters of # respectively FLEX_TARGET and BISON_TARGET macros. # # ==================================================================== # Example: # # find_package(BISON) # find_package(FLEX) # # BISON_TARGET(MyParser parser.y ${CMAKE_CURRENT_BINARY_DIR}/parser.cpp) # FLEX_TARGET(MyScanner lexer.l ${CMAKE_CURRENT_BINARY_DIR}/lexer.cpp) # ADD_FLEX_BISON_DEPENDENCY(MyScanner MyParser) # # include_directories(${CMAKE_CURRENT_BINARY_DIR}) # add_executable(Foo # Foo.cc # ${BISON_MyParser_OUTPUTS} # ${FLEX_MyScanner_OUTPUTS} # ) # ==================================================================== #============================================================================= # Copyright 2009 Kitware, Inc. # Copyright 2006 Tristan Carel # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # (To distribute this file outside of CMake, substitute the full # License text for the above reference.) find_program(FLEX_EXECUTABLE NAMES flex win_flex DOC "path to the flex executable") mark_as_advanced(FLEX_EXECUTABLE) find_library(FL_LIBRARY NAMES fl DOC "Path to the fl library") find_path(FLEX_INCLUDE_DIR FlexLexer.h DOC "Path to the flex headers") mark_as_advanced(FL_LIBRARY FLEX_INCLUDE_DIR) set(FLEX_INCLUDE_DIRS ${FLEX_INCLUDE_DIR}) set(FLEX_LIBRARIES ${FL_LIBRARY}) if(FLEX_EXECUTABLE) execute_process(COMMAND ${FLEX_EXECUTABLE} --version OUTPUT_VARIABLE FLEX_version_output ERROR_VARIABLE FLEX_version_error RESULT_VARIABLE FLEX_version_result OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT ${FLEX_version_result} EQUAL 0) if(FLEX_FIND_REQUIRED) message(SEND_ERROR "Command \"${FLEX_EXECUTABLE} --version\" failed with output:\n${FLEX_version_output}\n${FLEX_version_error}") else() message("Command \"${FLEX_EXECUTABLE} --version\" failed with output:\n${FLEX_version_output}\n${FLEX_version_error}\nFLEX_VERSION will not be available") endif() else() # older versions of flex printed "/full/path/to/executable version X.Y" # newer versions use "basename(executable) X.Y" get_filename_component(FLEX_EXE_NAME_WE "${FLEX_EXECUTABLE}" NAME_WE) get_filename_component(FLEX_EXE_EXT "${FLEX_EXECUTABLE}" EXT) string(REGEX REPLACE "^.*${FLEX_EXE_NAME_WE}(${FLEX_EXE_EXT})?\"? (version )?([0-9]+[^ ]*)( .*)?$" "\\3" FLEX_VERSION "${FLEX_version_output}") unset(FLEX_EXE_EXT) unset(FLEX_EXE_NAME_WE) endif() #============================================================ # FLEX_TARGET (public macro) #============================================================ # macro(FLEX_TARGET Name Input Output) set(FLEX_TARGET_usage "FLEX_TARGET( [COMPILE_FLAGS ]") if(${ARGC} GREATER 3) if(${ARGC} EQUAL 5) if("${ARGV3}" STREQUAL "COMPILE_FLAGS") set(FLEX_EXECUTABLE_opts "${ARGV4}") separate_arguments(FLEX_EXECUTABLE_opts) else() message(SEND_ERROR ${FLEX_TARGET_usage}) endif() else() message(SEND_ERROR ${FLEX_TARGET_usage}) endif() endif() add_custom_command(OUTPUT ${Output} COMMAND ${FLEX_EXECUTABLE} ARGS ${FLEX_EXECUTABLE_opts} -o${Output} ${Input} DEPENDS ${Input} COMMENT "[FLEX][${Name}] Building scanner with flex ${FLEX_VERSION}" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) set(FLEX_${Name}_DEFINED TRUE) set(FLEX_${Name}_OUTPUTS ${Output}) set(FLEX_${Name}_INPUT ${Input}) set(FLEX_${Name}_COMPILE_FLAGS ${FLEX_EXECUTABLE_opts}) endmacro() #============================================================ #============================================================ # ADD_FLEX_BISON_DEPENDENCY (public macro) #============================================================ # macro(ADD_FLEX_BISON_DEPENDENCY FlexTarget BisonTarget) if(NOT FLEX_${FlexTarget}_OUTPUTS) message(SEND_ERROR "Flex target `${FlexTarget}' does not exists.") endif() if(NOT BISON_${BisonTarget}_OUTPUT_HEADER) message(SEND_ERROR "Bison target `${BisonTarget}' does not exists.") endif() set_source_files_properties(${FLEX_${FlexTarget}_OUTPUTS} PROPERTIES OBJECT_DEPENDS ${BISON_${BisonTarget}_OUTPUT_HEADER}) endmacro() #============================================================ endif() include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) FIND_PACKAGE_HANDLE_STANDARD_ARGS(FLEX REQUIRED_VARS FLEX_EXECUTABLE VERSION_VAR FLEX_VERSION) # FindFLEX.cmake ends here nfs-ganesha-2.6.0/src/cmake/rpmtools_config.cmake000066400000000000000000000013311324272410200217610ustar00rootroot00000000000000set( RPM_NAME ${PROJECT_NAME} ) set( PACKAGE_VERSION "${${PROJECT_NAME}_MAJOR_VERSION}.${${PROJECT_NAME}_MINOR_VERSION}.${${PROJECT_NAME}_PATCH_LEVEL}" ) set( RPM_SUMMARY "NFS-Ganesha is a NFS Server running in user space" ) set( RPM_RELEASE_BASE 1 ) set( RPM_RELEASE ${RPM_RELEASE_BASE}.git${_GIT_HEAD_COMMIT_ABBREV} ) set( RPM_PACKAGE_LICENSE "LGPLv3" ) set( RPM_PACKAGE_GROUP "Applications/System" ) set( RPM_URL "http://nfs-ganesha.sourceforge.net" ) set( RPM_CHANGELOG_FILE ${PROJECT_SOURCE_DIR}/rpm_changelog ) set( RPM_DESCRIPTION "NFS-GANESHA is a NFS Server running in user space. It comes with various back-end modules (called FSALs) provided as shared objects to support different file systems and name-spaces." ) nfs-ganesha-2.6.0/src/cmake/tsan.cmake000066400000000000000000000022031324272410200175210ustar00rootroot00000000000000# cmake rule for GCC thread-sanitizer (tsan). # # Make sure gcc 4.8 (or up) is installed and configured. # # To use a GCC different from the default version, set the env vars. # For example: # # $ export CC=/opt/gcc-4.8.2/bin/gcc # $ export CXX=/opt/gcc-4.8.2/bin/g++ # if (USE_TSAN) execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) if (GCC_VERSION VERSION_LESS 4.8) message(FATAL_ERROR "thread-sanitizer is not supported by GCC ${GCC_VERSION}") endif() message(STATUS "thread-sanitizer powered by GCC ${GCC_VERSION}") set(TSAN_C_FLAGS "-fsanitize=thread -fPIE") set(TSAN_CXX_FLAGS "-fsanitize=thread -fPIE") set(TSAN_EXE_LINKER_FLAGS "-fsanitize=thread -pie") set(TSAN_SHARED_LINKER_FLAGS "-fsanitize=thread -pie") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${TSAN_C_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TSAN_CXX_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${TSAN_EXE_LINKER_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${TSAN_SHARED_LINKER_FLAGS}") endif() # vim:expandtab:shiftwidth=2:tabstop=2: nfs-ganesha-2.6.0/src/config_parsing/000077500000000000000000000000001324272410200174655ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/config_parsing/.gitignore000066400000000000000000000000751324272410200214570ustar00rootroot00000000000000conf_lex.c conf_yacc.c conf_yacc.h test_parsing verif_syntax nfs-ganesha-2.6.0/src/config_parsing/CMakeLists.txt000066400000000000000000000031671324272410200222340ustar00rootroot00000000000000find_package(BISON) find_package(FLEX) BISON_TARGET( ConfigParser ${CMAKE_CURRENT_SOURCE_DIR}/conf_yacc.y ${CMAKE_CURRENT_BINARY_DIR}/conf_yacc.c COMPILE_FLAGS "--defines -pganesha_yy" ) FLEX_TARGET( ConfigScanner ${CMAKE_CURRENT_SOURCE_DIR}/conf_lex.l ${CMAKE_CURRENT_BINARY_DIR}/conf_lex.c COMPILE_FLAGS "-Pganeshun_yy -olex.yy.c" ) ADD_FLEX_BISON_DEPENDENCY(ConfigScanner ConfigParser) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) ########### next target ############### SET(config_parsing_STAT_SRCS analyse.c config_parsing.c conf_url.c analyse.h ) if(RADOS_URLS) set(config_parsing_STAT_SRCS ${config_parsing_STAT_SRCS} conf_url_rados.c ) include_directories(${RADOS_INCLUDE_DIR}) target_link_libraries(${RADOS_LIBRARIES}) endif(RADOS_URLS) add_library(config_parsing STATIC ${config_parsing_STAT_SRCS} ${BISON_ConfigParser_OUTPUTS} ${FLEX_ConfigScanner_OUTPUTS} ) add_sanitizers(config_parsing) ########### next target ############### SET(verif_syntax_SRCS verif_syntax.c ) add_executable(verif_syntax EXCLUDE_FROM_ALL ${verif_syntax_SRCS}) add_sanitizers(verif_syntax) target_link_libraries(verif_syntax config_parsing log ${CMAKE_THREAD_LIBS_INIT}) if(RADOS_URLS) target_link_libraries(config_parsing ${RADOS_LIBRARIES}) endif(RADOS_URLS) ########### next target ############### SET(test_parsing_SRCS test_parse.c ) add_executable(test_parsing EXCLUDE_FROM_ALL ${test_parsing_SRCS}) add_sanitizers(test_parsing) target_link_libraries(test_parsing config_parsing log ${CMAKE_THREAD_LIBS_INIT}) ########### install files ############### nfs-ganesha-2.6.0/src/config_parsing/analyse.c000066400000000000000000000176431324272410200213000ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ #include "config.h" #include "config_parsing.h" #include "analyse.h" #include #include #include #if HAVE_STRING_H #include #endif #include "abstract_mem.h" /** * @brief Insert a scanner token into the token table * * Look up the token in the list matching case insensitive. * If there is a match, return a pointer to the token in the table. * Otherwise, allocate space, link it and return the pointer. * if 'esc' == true, this is a double quoted string which needs to * be filtered. Turn the escaped non-printable into the non-printable. * * @param token [IN] pointer to the yytext token from flex * @param esc [IN] bool, filter if true * @param st [IN] pointer to parser state * @return pointer to persistant storage for token or NULL; */ char *save_token(char *token, bool esc, struct parser_state *st) { struct token_tab *tokp, *new_tok; for (tokp = st->root_node->tokens; tokp != NULL; tokp = tokp->next) { if (strcasecmp(token, tokp->token) == 0) return tokp->token; } new_tok = gsh_calloc(1, (sizeof(struct token_tab) + strlen(token) + 1)); if (new_tok == NULL) return NULL; if (esc) { char *sp, *dp; int c; sp = token; dp = new_tok->token; c = *sp++; if (c == '\"') c = *sp++; /* gobble leading '"' from regexp */ while (c != '\0') { if (c == '\\') { c = *sp++; if (c == '\0') break; switch (c) { case 'n': c = '\n'; break; case 't': c = '\t'; break; case 'r': c = '\r'; break; default: break; } } else if (c == '"' && *sp == '\0') break; /* skip trailing '"' from regexp */ *dp++ = c; c = *sp++; } } else { if (*token == '\'') /* skip and chomp "'" in an SQUOTE */ token++; strcpy(new_tok->token, token); if (new_tok->token[strlen(new_tok->token) - 1] == '\'') new_tok->token[strlen(new_tok->token) - 1] = '\0'; } new_tok->next = st->root_node->tokens; st->root_node->tokens = new_tok; return new_tok->token; } struct { const char *name; const char *desc; } config_term_type[] = { [TERM_TOKEN] = {"TOKEN", "option name or number"}, [TERM_REGEX] = {"REGEX", "regular expression option}"}, [TERM_PATH] = {"PATH", "file path name"}, [TERM_STRING] = {"STRING", "simple string"}, [TERM_DQUOTE] = {"DQUOTE", "double quoted string"}, [TERM_SQUOTE] = {"SQUOTE", "single quoted string"}, [TERM_TRUE] = {"TRUE", "boolean TRUE"}, [TERM_FALSE] = {"FALSE", "boolean FALSE"}, [TERM_DECNUM] = {"DECNUM", "decimal number"}, [TERM_HEXNUM] = {"HEXNUM", "hexadecimal number"}, [TERM_OCTNUM] = {"OCTNUM", "octal number"}, [TERM_V4_ANY] = {"V4_ANY", "IPv4 any address"}, [TERM_V4ADDR] = {"V4ADDR", "IPv4 numeric address"}, [TERM_V4CIDR] = {"V4CIDR", "IPv4 CIDR subnet"}, [TERM_V6ADDR] = {"V6ADDR", "IPv6 numeric address"}, [TERM_V6CIDR] = {"V6CIDR", "IPv6 CIDR subnet"}, [TERM_FSID] = {"FSID", "file system ID"}, [TERM_NETGROUP] = {"NETGROUP", "NIS netgroup"} }; const char *config_term_name(enum term_type type) { return config_term_type[(int)type].name; } const char *config_term_desc(enum term_type type) { return config_term_type[(int)type].desc; } /** * Displays the content of a list of blocks. */ static void print_node(FILE *output, struct config_node *node, unsigned int indent) { struct config_node *sub_node; struct glist_head *nsi, *nsn; if (node->type == TYPE_BLOCK) { fprintf(output, "%*s\n", indent, " ", node->u.nterm.name, node->filename, node->linenumber); glist_for_each_safe(nsi, nsn, &node->u.nterm.sub_nodes) { sub_node = glist_entry(nsi, struct config_node, node); print_node(output, sub_node, indent + 3); } fprintf(output, "%*s\n", indent, " ", node->u.nterm.name); } else if (node->type == TYPE_STMT) { fprintf(output, "%*s\n", indent, " ", node->u.nterm.name, node->filename, node->linenumber); glist_for_each_safe(nsi, nsn, &node->u.nterm.sub_nodes) { sub_node = glist_entry(nsi, struct config_node, node); print_node(output, sub_node, indent + 3); } fprintf(output, "%*s\n", indent, " ", node->u.nterm.name); } else { /* a statement value */ fprintf(output, "%*s(%s)'%s' '%s'\n", indent, " ", (node->u.term.type != 0 ? config_term_type[node->u.term.type].name : "unknown"), (node->u.term.op_code != NULL ? node->u.term.op_code : " "), node->u.term.varvalue); } } void print_parse_tree(FILE *output, struct config_root *tree) { struct config_node *node; struct file_list *file; struct token_tab *token; struct glist_head *nsi, *nsn; assert(tree->root.type == TYPE_ROOT); fprintf(output, "\n"); fprintf(output, " %zd \n", glist_length(&tree->root.u.nterm.sub_nodes)); fprintf(output, " \n"); for (file = tree->files; file != NULL; file = file->next) fprintf(output, " \"%s\" \n", file->pathname); fprintf(output, " \n"); fprintf(output, " \n"); for (token = tree->tokens; token != NULL; token = token->next) fprintf(output, " %s\n", token->token); fprintf(output, " \n"); fprintf(output, "\n"); fprintf(output, "\n"); glist_for_each_safe(nsi, nsn, &tree->root.u.nterm.sub_nodes) { node = glist_entry(nsi, struct config_node, node); print_node(output, node, 3); } fprintf(output, "\n"); return; } /** * @brief Free a parse tree node. * * Note that we do not free either u.nterm.name or u.term.varvalue. * this is because these are pointers into the token table which * is freed elsewhere. */ static void free_node(struct config_node *node) { if (node->type == TYPE_BLOCK || node->type == TYPE_STMT) { struct config_node *sub_node; struct glist_head *nsi, *nsn; glist_for_each_safe(nsi, nsn, &node->u.nterm.sub_nodes) { sub_node = glist_entry(nsi, struct config_node, node); glist_del(&sub_node->node); free_node(sub_node); } } gsh_free(node); return; } void free_parse_tree(struct config_root *tree) { struct file_list *file, *next_file; struct token_tab *token, *next_token; struct config_node *node; struct glist_head *nsi, *nsn; glist_for_each_safe(nsi, nsn, &tree->root.u.nterm.sub_nodes) { node = glist_entry(nsi, struct config_node, node); glist_del(&node->node); free_node(node); } gsh_free(tree->root.filename); if(tree->conf_dir != NULL) gsh_free(tree->conf_dir); file = tree->files; while (file != NULL) { next_file = file->next; gsh_free(file->pathname); gsh_free(file); file = next_file; } token = tree->tokens; while (token != NULL) { next_token = token->next; gsh_free(token); token = next_token; } gsh_free(tree); return; } void config_error(FILE *fp, const char *filename, int linenum, char *fmt, va_list args) { fprintf(fp, "Config File (%s:%d): ", filename, linenum); vfprintf(fp, fmt, args); fputc('\f', fp); /* form feed (remember those?) used as msg sep */ } nfs-ganesha-2.6.0/src/config_parsing/analyse.h000066400000000000000000000072471324272410200213040ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- * Copyright CEA/DAM/DIF (2007) * contributeur : Thomas LEIBOVICI thomas.leibovici@cea.fr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ #ifndef CONFPARSER_H #define CONFPARSER_H #include #include #include #include #include "gsh_list.h" /** * @brief Configuration parser data structures * and linkage betweek the parser, analyse.c and config_parsing.c */ /* * Parse tree node. */ /* Config nodes are either terminals or non-terminals. * BLOCK and STMT are non-terminals. * ROOT is a special case BLOCK (root of tree...) * TERM is a value/token terminal. */ enum node_type { TYPE_ROOT = 1, TYPE_BLOCK, TYPE_STMT, TYPE_TERM}; struct config_node { struct glist_head node; struct glist_head blocks; char *filename; /* pointer to filename in file list */ int linenumber; bool found; /* use accounting private in do_block_load */ enum node_type type; /* switches union contents */ union { /* sub_nodes are always struct config_node */ struct { /* TYPE_TERM */ enum term_type type; char *op_code; char *varvalue; } term; struct { /* TYPE_BLOCK | TYPE_STMT */ char *name; /* name */ struct config_node *parent; struct glist_head sub_nodes; } nterm; }u; }; /* * File list * Every config_node points to a pathname in this list */ struct file_list { struct file_list *next; char *pathname; }; /* * Symbol table * Every token the scanner keeps goes here. It is a linear * list but we don't need much more than that. What we do * need is an accounting of all that memory so we can free it * when the parser barfs and leaves stuff on its FSM stack. */ struct token_tab { struct token_tab *next; char token[]; }; /* * Parse tree root * A parse tree consists of several blocks, * each block consists of variables definitions * and subblocks. * All storage allocated into the parse tree is here */ struct bufstack; /* defined in conf_lex.l */ struct config_root { struct config_node root; char *conf_dir; struct file_list *files; struct token_tab *tokens; }; /* * parser/lexer linkage */ struct parser_state { struct config_root *root_node; void *scanner; struct bufstack *curbs; char *current_file; int block_depth; /* block/subblock nesting level */ struct config_error_type *err_type; }; char *save_token(char *token, bool esc, struct parser_state *st); int ganesha_yyparse(struct parser_state *st); int ganeshun_yy_init_parser(char *srcfile, struct parser_state *st); void ganeshun_yy_cleanup_parser(struct parser_state *st); /** * Error reporting */ void config_error(FILE *fp, const char *filename, int linenum, char *format, va_list args); /** * Displays the content of parse tree. */ void print_parse_tree(FILE * output, struct config_root *tree); /** * Free resources of parse tree */ void free_parse_tree(struct config_root *tree); #endif /* CONFPARSER_H */ nfs-ganesha-2.6.0/src/config_parsing/conf_lex.l000066400000000000000000000337471324272410200214550ustar00rootroot00000000000000%{ #pragma GCC diagnostic ignored "-Wunused-value" #pragma GCC diagnostic ignored "-Wunused-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include "config.h" #include "config_parsing.h" #include "conf_url.h" #include "analyse.h" #include "abstract_mem.h" #include "conf_yacc.h" #include #include #include #include #include "log.h" #if HAVE_STRING_H # include #endif /* Our versions of parser macros */ #define YY_USER_INIT \ do { \ BEGIN YY_INIT; \ } while (0); #define YY_USER_ACTION \ yylloc->first_line = yylloc->last_line = yylineno; \ yylloc->first_column = yylloc->last_column = yycolumn + yyleng -1; \ yycolumn += yyleng; \ yylloc->filename = stp->current_file; #ifdef _DEBUG_PARSING #define DEBUG_LEX printf #else #define DEBUG_LEX(...) (void)0 #endif #define BS_FLAG_NONE 0 #define BS_FLAG_URL 1 struct bufstack { struct bufstack *prev; YY_BUFFER_STATE bs; int lineno; char *filename; FILE *f; char *fbuf; uint32_t flags; }; static char *filter_string(char *src, int esc); static int new_file(char *filename, struct parser_state *st); static int fetch_url(char *name_tok, struct parser_state *st); static int pop_file(struct parser_state *st); %} %option nounput %option yylineno %option reentrant %option bison-bridge %option bison-locations %option extra-type="struct parser_state *" SPACE [ \t\r\f] NL [\n] EQUALS "=" COMMA "," SEMI ";" LCURLY "\{" RCURLY "\}" MINUS "\-" TWIDDLE "\~" SPLAT "\*" HUH "\?" BANG "\!" DOT "\." AT "@" CIDR \/[1-9][0-9]{0,1} V4OCTET [0-9]{1,3} IPV4ADDR {V4OCTET}{DOT}{V4OCTET}{DOT}{V4OCTET}{DOT}{V4OCTET} H16 [0-9A-Fa-f]{1,4} LS32 {H16}:{H16}|{IPV4ADDR} IPV6ADDR ({H16}:){6}{LS32}|::({H16}:){5}{LS32}|({H16})?::({H16}:){4}{LS32}|(({H16}:){0,1}{H16})?::({H16}:){3}{LS32}|(({H16}:){0,2}{H16})?::({H16}:){2}{LS32}|(({H16}:){0,3}{H16})?::{H16}:{LS32}|(({H16}:){0,4}{H16})?::{LS32}|(({H16}:){0,5}{H16})?::{H16}|(({H16}:){0,6}{H16})?:: SQUOTE \'[^\']*\' DQUOTE \"(\\.|[^\"])*\" YES [Yy][Ee][Ss] TRUE [Tt][Rr][Uu][Ee] ON [Oo][Nn] NO [Nn][Oo] FALSE [Ff][Aa][Ll][Ss][Ee] OFF [Oo][Ff][Ff] OCTNUM 0[1-7][0-7]* HEXNUM 0[xX][0-9a-fA-F]+ DECNUM (0|[1-9][0-9]*) NINEP 9[pP] INCLPATH \/?([a-zA-Z0-9\-\.\_])+(\/[a-zA-Z0-9\-\.\_]+)* PATHNAME \/([a-zA-Z0-9\-\.\_]+\/?)? LONGPATH (\/?[a-zA-Z0-9\-\.\_]+(\/[a-zA-Z0-9\-\.\_]+)+)\/? TOKEN_CHARS [a-zA-Z_\?][a-zA-Z0-9\._\-]* WC [a-zA-Z0-9\._\-] WR \[{BANG}?({WC})+\] WP ({WR}|{SPLAT}|{HUH}) WILDCARD ({WC}*{WP})+{WC}* COMMENTEXT #.*$ ID_CHARS [a-zA-Z_][a-zA-Z0-9_\-]* NETGROUP_CHARS [a-zA-Z_][a-zA-Z0-9_.\-]* /* URL types, e.g., (rados|http|ftp) */ URLTYPES (rados) INCLUDE_URL {URLTYPES}:\/\/[a-zA-Z0-9\-\.\_&=\/]+ /* INCLUDE state is used for picking the name of the include file */ %START YY_INIT DEFINITION TERM INCLUDE URL %% %{ struct parser_state *stp = yyextra; %} "%include" { /* include file start */ DEBUG_LEX("INCLUDE\n"); BEGIN INCLUDE; /* not a token, return nothing */ } {INCLPATH} { { int c; DEBUG_LEX("Calling new_file with unquoted %s\n", yytext); c = new_file(yytext, stp); if (c == ENOMEM) yyterminate(); BEGIN YY_INIT; DEBUG_LEX("done new file\n"); } } \"{INCLPATH}\" { { int c; DEBUG_LEX("Calling new_file with quoted %s\n", yytext); c = new_file(yytext, stp); if (c == ENOMEM) yyterminate(); BEGIN YY_INIT; DEBUG_LEX("done new file\n"); } } "%url" { /* URL include file start */ DEBUG_LEX("URL\n"); BEGIN URL; /* not a token, return nothing */ } {INCLUDE_URL} { { int c; DEBUG_LEX("Calling new_file with unquoted %s\n", yytext); c = fetch_url(yytext, stp); if (c == ENOMEM) yyterminate(); BEGIN YY_INIT; DEBUG_LEX("done new file\n"); } } \"{INCLUDE_URL}\" { { int c; DEBUG_LEX("Calling new_file with quoted %s\n", yytext); c = fetch_url(yytext, stp); if (c == ENOMEM) yyterminate(); BEGIN YY_INIT; DEBUG_LEX("done new file\n"); } } <> { /* end of included file */ DEBUG_LEX("\n"); if (pop_file(stp) == 0) yyterminate(); } /* Initial State. We start with a block identifier */ {ID_CHARS} { /* first block */ /* identifier */ DEBUG_LEX("[block:%s]\n",yytext); yylval->token = save_token(yytext, false, stp); BEGIN DEFINITION; return IDENTIFIER; } {ID_CHARS} { DEBUG_LEX("[id:%s",yytext); yylval->token = save_token(yytext, false, stp); return IDENTIFIER; } {EQUALS} { DEBUG_LEX(" EQUALS "); BEGIN TERM; return EQUAL_OP; } {LCURLY} { DEBUG_LEX("BEGIN_BLOCK\n"); BEGIN DEFINITION; stp->block_depth++; return LCURLY_OP; } {RCURLY} { /* end of block */ DEBUG_LEX("END_BLOCK\n"); stp->block_depth --; if (stp->block_depth <= 0) BEGIN YY_INIT; return RCURLY_OP; } {COMMA} { /* another terminal to follow ',' */ DEBUG_LEX(" ',' "); return COMMA_OP; } /* End of statement */ {SEMI} { /* end of statement */ DEBUG_LEX("]\n"); BEGIN DEFINITION; return SEMI_OP; } /* Double Quote, allows char escaping */ {DQUOTE} { /* start of a double quote string */ DEBUG_LEX("quote value:<%s>", yytext); yylval->token = save_token(yytext, true, stp); return DQUOTE; } /* Single Quote, single line with no escaping */ {SQUOTE} { /* start of a single quote string */ DEBUG_LEX("lit value:<%s>", yytext); yylval->token = save_token(yytext, false, stp); return SQUOTE; } {YES}|{TRUE}|{ON} { /* a boolean TRUE */ DEBUG_LEX("boolean TRUE:%s", yytext); yylval->token = save_token(yytext, false, stp); return TOK_TRUE; } {NO}|{FALSE}|{OFF} { /* a boolean FALSE */ DEBUG_LEX("boolean FALSE:%s", yytext); yylval->token = save_token(yytext, false, stp); return TOK_FALSE; } {MINUS}|{TWIDDLE} { /* an arithmetic op */ DEBUG_LEX(" arith op:%s", yytext); yylval->token = save_token(yytext, false, stp); return TOK_ARITH_OP; } {NINEP} { /* "9P" is here to take precedence over numbers, this is a special */ DEBUG_LEX("token value:%s",yytext); yylval->token = save_token(yytext, false, stp); return TOKEN; } ({OCTNUM}|{DECNUM}|{HEXNUM}){DOT}({OCTNUM}|{DECNUM}|{HEXNUM}) { /* an FSID */ DEBUG_LEX(" FSID :%s", yytext); yylval->token = save_token(yytext, false, stp); return TOK_FSID; } {OCTNUM} { /* an octal number */ DEBUG_LEX(" octal number:%s", yytext); yylval->token = save_token(yytext, false, stp); return TOK_OCTNUM; } {HEXNUM} { /* a hexidecimal number */ DEBUG_LEX(" hex number:%s", yytext); yylval->token = save_token(yytext, false, stp); return TOK_HEXNUM; } {DECNUM} { /* a decimal number */ DEBUG_LEX(" dec number:%s", yytext); yylval->token = save_token(yytext, false, stp); return TOK_DECNUM; } {SPLAT}|(0{DOT}0{DOT}0{DOT}0) { /* v4 address wildcard, ganesha only, not IETF */ DEBUG_LEX(" V4 any:%s", yytext); yylval->token = save_token(yytext, false, stp); return TOK_V4_ANY; } {IPV4ADDR}{CIDR}? { /* V4 CIDR */ DEBUG_LEX(" IPv4 :%s", yytext); yylval->token = save_token(yytext, false, stp); if (index(yylval->token, '/') == NULL) return TOK_V4ADDR; else return TOK_V4CIDR; } /* Mere mortals are not supposed to grok the pattern for IPV6ADDR. */ /* I got it from the Flex manual. */ {IPV6ADDR}{CIDR}? { /* V6 CIDR */ DEBUG_LEX(" IPv6 :%s", yytext); yylval->token = save_token(yytext, false, stp); if (index(yylval->token, '/') == NULL) return TOK_V6ADDR; else return TOK_V6CIDR; } {AT}{NETGROUP_CHARS} { /* a netgroup used for clients */ DEBUG_LEX(" netgroup :%s", yytext); yylval->token = save_token(yytext, false, stp); return TOK_NETGROUP; } /* Last resort terminals. PATHAME is here because it can confuse */ /* with a CIDR (precedence) and */ /* TOKEN_CHARS gobbles anything other than white and ";" */ {PATHNAME}|{LONGPATH} { /* a POSIX pathname */ DEBUG_LEX("pathname:%s", yytext); yylval->token = save_token(yytext, false, stp); return TOK_PATH; } {TOKEN_CHARS} { /* start of a number or label/tag */ DEBUG_LEX("token value:%s",yytext); yylval->token = save_token(yytext, false, stp); return TOKEN; } {WILDCARD} { /* start of a number or label/tag as glob(7) string */ DEBUG_LEX("token value:%s",yytext); yylval->token = save_token(yytext, false, stp); return REGEX_TOKEN; } /* Skip over stuff we don't send upstairs */ {COMMENTEXT} ;/* ignore */ {SPACE} ;/* ignore */ {NL} ;/* ignore */ /* Unrecognized chars. Must do better... */ . { /* ERROR: out of character character */ DEBUG_LEX("unexpected stuff (%s)\n", yytext); config_parse_error(yylloc, stp, "Unexpected character (%s)", yytext); stp->err_type->scan = true; yylval->token = save_token(yytext, false, stp); /* for error rpt */ return _ERROR_; } %% int ganeshun_yywrap(void *yyscanner){ return 1; } int ganeshun_yy_init_parser(char *srcfile, struct parser_state *st) { FILE *in_file; void *yyscanner = st->scanner; /* reentrant scanner macro magic requires this... */ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; struct file_list *flist; struct config_root *confroot; YY_BUFFER_STATE inbuf; int rc = ENOMEM; confroot = gsh_calloc(1, sizeof(struct config_root)); glist_init(&confroot->root.node); glist_init(&confroot->root.u.nterm.sub_nodes); confroot->root.type = TYPE_ROOT; st->root_node = confroot; ganeshun_yylex_init_extra(st, &st->scanner); rc = new_file(srcfile, st); if (rc == 0) confroot->root.filename = gsh_strdup(srcfile); return rc; } void ganeshun_yy_cleanup_parser(struct parser_state *st) { int rc; if (st->curbs != NULL) { st->err_type->parse = true; while(pop_file(st) != 0); } ganeshun_yylex_destroy(st->scanner); } static int new_file(char *name_tok, struct parser_state *st) { struct bufstack *bs = NULL; FILE *in_file; YY_BUFFER_STATE inbuf; struct file_list *flist = NULL; struct file_list *fp; void *yyscanner = st->scanner; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; struct config_root *confroot = st->root_node; char *fullpath = NULL; int rc = ENOMEM; char *filename = alloca(strlen(name_tok) + 1); if (*name_tok == '\"') { strcpy(filename, name_tok + 1); filename[strlen(filename) - 1] = '\0'; } else { strcpy(filename, name_tok); /* alloca'd memory freed on exit */ } if (confroot->files == NULL) { if (filename[0] == '/') { char *path = alloca(strlen(filename) + 1); strcpy(path, filename); confroot->conf_dir = gsh_strdup(dirname(path)); } else { confroot->conf_dir = gsh_strdup("."); } } if (filename[0] == '/') { fullpath = gsh_strdup(filename); } else { fullpath = gsh_malloc(strlen(filename) + strlen(confroot->conf_dir) + 2); sprintf(fullpath, "%s/%s", confroot->conf_dir, filename); } /* loop detection */ for (fp = confroot->files; fp != NULL; fp = fp->next) { if (!strcmp(fp->pathname, fullpath)) { config_parse_error(yylloc, st, "file (%s)already parsed, ignored", fullpath); rc = EINVAL; goto errout; } } bs = gsh_calloc(1, sizeof(struct bufstack)); flist = gsh_calloc(1, sizeof(struct file_list)); in_file = fopen(fullpath, "r" ); if (in_file == NULL) { rc = errno; config_parse_error(yylloc, st, "new file (%s) open error (%s), ignored", fullpath, strerror(rc)); goto errout; } bs->bs = ganeshun_yy_create_buffer(in_file, YY_BUF_SIZE, yyscanner); if (st->curbs) st->curbs->lineno = yylineno; bs->prev = st->curbs; bs->f = in_file; bs->filename = fullpath; ganeshun_yy_switch_to_buffer(bs->bs, yyscanner); st->current_file = fullpath; st->curbs = bs; flist->pathname = fullpath; flist->next = confroot->files; confroot->files = flist; return 0; errout: if (rc == ENOMEM) st->err_type->resource = true; else st->err_type->scan = true; gsh_free(flist); gsh_free(bs); gsh_free(fullpath); return rc; } /* fetch_url */ static int fetch_url(char *name_tok, struct parser_state *st) { struct bufstack *bs = NULL; YY_BUFFER_STATE inbuf; struct file_list *flist = NULL; struct file_list *fp; void *yyscanner = st->scanner; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; struct config_root *confroot = st->root_node; char *filename = NULL; int rc = ENOMEM; #ifdef NO_URL_RECURSION /* forbid URL chasing */ if (st->curbs && (st->curbs->flags & BS_FLAG_URL)) { config_parse_error(yylloc, st, "new url (%s) transitive fetch from (%s), ignored", name_tok, st->curbs->filename); goto errout; } #endif filename = gsh_strdup(name_tok); bs = gsh_calloc(1, sizeof(struct bufstack)); flist = gsh_calloc(1, sizeof(struct file_list)); rc = config_url_fetch(filename, &bs->f, &bs->fbuf); if (bs->f == NULL) { config_parse_error(yylloc, st, "new url (%s) open error (%s), ignored", filename, strerror(rc)); goto errout; } bs->bs = ganeshun_yy_create_buffer(bs->f, YY_BUF_SIZE, yyscanner); if (st->curbs) st->curbs->lineno = yylineno; bs->prev = st->curbs; bs->filename = filename; bs->flags = BS_FLAG_URL; ganeshun_yy_switch_to_buffer(bs->bs, yyscanner); st->current_file = gsh_strdup(bs->filename); st->curbs = bs; flist->pathname = gsh_strdup(bs->filename); /* XXX */ flist->next = confroot->files; confroot->files = flist; return 0; errout: if (rc == ENOMEM) st->err_type->resource = true; else st->err_type->scan = true; gsh_free(flist); gsh_free(bs); gsh_free(filename); return rc; } /* fetch_url() */ static int pop_file(struct parser_state *st) { struct bufstack *bs = st->curbs; struct bufstack *prevbs; void *yyscanner = st->scanner; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; if (bs == NULL) return 0; if (bs->flags & BS_FLAG_URL) { config_url_release(bs->f, bs->fbuf); } else { fclose(bs->f); } ganeshun_yy_delete_buffer(bs->bs, yyscanner); prevbs = bs->prev; st->curbs = prevbs; gsh_free(bs); if (prevbs == NULL) return 0; ganeshun_yy_switch_to_buffer(prevbs->bs, yyscanner); yylineno = st->curbs->lineno; st->current_file = st->curbs->filename; return 1; } nfs-ganesha-2.6.0/src/config_parsing/conf_url.c000066400000000000000000000102401324272410200214350ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- * Copyright (C) 2017, Red Hat, Inc. * contributeur : Matt Benjamin mbenjamin@redhat.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * --------------------------------------- */ #include "config.h" #include #include "log.h" #include "sal_functions.h" #include "conf_url.h" #ifdef RADOS_URLS #include "conf_url_rados.h" #endif static pthread_rwlock_t url_rwlock = PTHREAD_RWLOCK_INITIALIZER; static struct glist_head url_providers; static regex_t url_regex; /** @brief register handler for new url type */ int register_url_provider(struct gsh_url_provider *nurl_p) { struct gsh_url_provider *url_p; struct glist_head *gl; int code = 0; PTHREAD_RWLOCK_wrlock(&url_rwlock); glist_for_each(gl, &url_providers) { url_p = glist_entry(gl, struct gsh_url_provider, link); if (!strcasecmp(url_p->name, nurl_p->name)) { code = EEXIST; break; } } nurl_p->url_init(); glist_add_tail(&url_providers, &nurl_p->link); PTHREAD_RWLOCK_unlock(&url_rwlock); return code; } /* simplistic URL syntax */ #define CONFIG_URL_REGEX \ "^\"?(rados)://([^\"]+)\"?" /** @brief url regex initializer */ static void init_url_regex(void) { int r; r = regcomp(&url_regex, CONFIG_URL_REGEX, REG_EXTENDED); if (!!r) { LogFatal(COMPONENT_INIT, "Error initializing config url regex"); } } /** @brief package initializer */ void config_url_init(void) { glist_init(&url_providers); /* init well-known URL providers */ #ifdef RADOS_URLS conf_url_rados_pkginit(); #endif init_url_regex(); } /** @brief package shutdown */ void config_url_shutdown(void) { struct gsh_url_provider *url_p; PTHREAD_RWLOCK_wrlock(&url_rwlock); while ((url_p = glist_first_entry( &url_providers, struct gsh_url_provider, link))) { glist_del(&url_p->link); url_p->url_shutdown(); } PTHREAD_RWLOCK_unlock(&url_rwlock); regfree(&url_regex); } static inline char *match_dup(regmatch_t *m, char *in) { char *s = NULL; if (m->rm_so >= 0) { int size; size = m->rm_eo - m->rm_so + 1; s = (char *)gsh_malloc(size); snprintf(s, size, "%s", in + m->rm_so); } return s; } /** @brief generic url dispatch */ int config_url_fetch(const char *url, FILE **f, char **fbuf) { struct gsh_url_provider *url_p; struct glist_head *gl; regmatch_t match[3]; char *url_type = NULL, *m_url = NULL; int code = EINVAL; code = regexec(&url_regex, url, 3, match, 0); if (likely(!code)) { /* matched */ regmatch_t *m; m = &(match[1]); url_type = match_dup(m, (char *)url); m = &(match[2]); m_url = match_dup(m, (char *)url); if (!(url_type && m_url)) { LogWarn(COMPONENT_CONFIG, "%s: Failed to match %s as a config URL", __func__, url); goto out; } } else if (code == REG_NOMATCH) { LogWarn(COMPONENT_CONFIG, "%s: Failed to match %s as a config URL", __func__, url); goto out; } else { char ebuf[100]; regerror(code, &url_regex, ebuf, sizeof(ebuf)); LogWarn(COMPONENT_CONFIG, "%s: Error in regexec: %s", __func__, ebuf); goto out; } PTHREAD_RWLOCK_rdlock(&url_rwlock); glist_for_each(gl, &url_providers) { url_p = glist_entry(gl, struct gsh_url_provider, link); if (!strcasecmp(url_type, url_p->name)) { code = url_p->url_fetch(m_url, f, fbuf); break; } } PTHREAD_RWLOCK_unlock(&url_rwlock); out: gsh_free(url_type); gsh_free(m_url); return code; } /** @brief return resources allocated by url_fetch */ void config_url_release(FILE *f, char *fbuf) { fclose(f); free(fbuf); } nfs-ganesha-2.6.0/src/config_parsing/conf_url_rados.c000066400000000000000000000155571324272410200226450ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- * Copyright (C) 2017, Red Hat, Inc. * contributeur : Matt Benjamin mbenjamin@redhat.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * --------------------------------------- */ #include "conf_url.h" #include "conf_url_rados.h" #include #include #include #include "log.h" #include "sal_functions.h" #ifdef RADOS_URLS static regex_t url_regex; static rados_t cluster; static bool initialized; static struct rados_url_parameter { /** Path to ceph.conf */ char *ceph_conf; /** Userid (?) */ char *userid; } rados_url_param; static struct config_item rados_url_params[] = { CONF_ITEM_PATH("ceph_conf", 1, MAXPATHLEN, NULL, rados_url_parameter, ceph_conf), CONF_ITEM_STR("userid", 1, MAXPATHLEN, NULL, rados_url_parameter, userid), CONFIG_EOL }; static void *rados_url_param_init(void *link_mem, void *self_struct) { if (self_struct == NULL) return &rados_url_param; else return NULL; } struct config_block rados_url_param_blk = { .dbus_interface_name = "org.ganesha.nfsd.config.rados_urls", .blk_desc.name = "RADOS_URLS", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = rados_url_param_init, .blk_desc.u.blk.params = rados_url_params, .blk_desc.u.blk.commit = noop_conf_commit }; int rados_urls_set_param_from_conf(void *tree_node, struct config_error_type *err_type) { (void) load_config_from_node(tree_node, &rados_url_param_blk, NULL, true, err_type); if (!config_error_is_harmless(err_type)) { LogCrit(COMPONENT_INIT, "Error while parsing RADOS_URLS config block"); return -1; } LogFullDebug(COMPONENT_CONFIG, "%s parsed RADOS_URLS block, have ceph_conf=%s " " userid=%s", __func__, rados_url_param.ceph_conf, rados_url_param.userid); return 0; } /* decompose RADOS URL into (/)object * * verified to match each of the following: * * #define URL1 "my_rados_object" * #define URL2 "mypool_baby/myobject_baby" * #define URL3 "mypool-baby/myobject-baby" */ #define RADOS_URL_REGEX \ "([-a-zA-Z0-9_&=]+)/?([-a-zA-Z0-9_&=/]+)?" /** @brief url regex initializer */ static void init_url_regex(void) { int r; r = regcomp(&url_regex, RADOS_URL_REGEX, REG_EXTENDED); if (!!r) { LogFatal(COMPONENT_INIT, "Error initializing rados url regex"); } } static void cu_rados_url_early_init(void) { init_url_regex(); } extern struct config_error_type err_type; static void cu_rados_url_init(void) { int ret; void *node; node = config_GetBlockNode("RADOS_URLS"); if (!node) { LogWarn(COMPONENT_CONFIG, "%s: Failed to lookup RADOS_URLS config block", __func__); return; } ret = rados_urls_set_param_from_conf(node, &err_type); if (ret < 0) { LogEvent(COMPONENT_CONFIG, "%s: Failed to parse RADOS_URLS %d", __func__, ret); } ret = rados_create(&cluster, rados_url_param.userid); if (ret < 0) { LogEvent(COMPONENT_CONFIG, "%s: Failed in rados_create", __func__); return; } ret = rados_conf_read_file(cluster, rados_url_param.ceph_conf); if (ret < 0) { LogEvent(COMPONENT_CLIENTID, "%s: Failed to read ceph_conf", __func__); rados_shutdown(cluster); return; } ret = rados_connect(cluster); if (ret < 0) { LogEvent(COMPONENT_CONFIG, "%s: Failed to connect to cluster", __func__); rados_shutdown(cluster); return; } init_url_regex(); initialized = true; } static void cu_rados_url_shutdown(void) { if (initialized) { rados_shutdown(cluster); regfree(&url_regex); initialized = false; } } static inline char *match_dup(regmatch_t *m, char *in) { char *s = NULL; if (m->rm_so >= 0) { int size; size = m->rm_eo - m->rm_so + 1; s = (char *)gsh_malloc(size); snprintf(s, size, "%s", in + m->rm_so); } return s; } static int cu_rados_url_fetch(const char *url, FILE **f, char **fbuf) { rados_ioctx_t io_ctx; char *x0, *x1, *x2; char *pool_name; char *object_name; char *streambuf = NULL; /* not optional (buggy open_memstream) */ FILE *stream = NULL; char buf[1024]; regmatch_t match[3]; size_t streamsz; uint64_t off1 = 0; uint64_t off2 = 0; int ret; if (!initialized) { cu_rados_url_init(); } ret = regexec(&url_regex, url, 3, match, 0); if (likely(!ret)) { /* matched */ regmatch_t *m = &(match[0]); /* matched url pattern is NUL-terminated */ x0 = match_dup(m, (char *)url); m = &(match[1]); x1 = match_dup(m, (char *)url); m = &(match[2]); x2 = match_dup(m, (char *)url); if ((!x1) && (!x2)) goto out; if (x1) { if (!x2) { /* object only */ pool_name = NULL; object_name = x1; } else { pool_name = x1; object_name = x2; } } } else if (ret == REG_NOMATCH) { LogWarn(COMPONENT_CONFIG, "%s: Failed to match %s as a config URL", __func__, url); goto out; } else { char ebuf[100]; regerror(ret, &url_regex, ebuf, sizeof(ebuf)); LogWarn(COMPONENT_CONFIG, "%s: Error in regexec: %s", __func__, ebuf); goto out; } ret = rados_ioctx_create(cluster, pool_name, &io_ctx); if (ret < 0) { LogEvent(COMPONENT_CONFIG, "%s: Failed to create ioctx", __func__); cu_rados_url_shutdown(); goto out; } do { int nread, wrt, nwrt; nread = ret = rados_read(io_ctx, object_name, buf, 1024, off1); if (ret < 0) { LogEvent(COMPONENT_CONFIG, "%s: Failed reading %s/%s %s", __func__, pool_name, object_name, strerror(ret)); goto err; } off1 += nread; if (!stream) { streamsz = 1024; stream = open_memstream(&streambuf, &streamsz); } do { wrt = fwrite(buf+off2, nread, 1, stream); if (wrt > 0) { nwrt = MIN(nread, 1024); nread -= nwrt; off2 += nwrt; } } while (wrt > 0); } while (ret > 0); if (likely(stream)) { /* rewind */ fseek(stream, 0L, SEEK_SET); /* return--caller will release */ *f = stream; *fbuf = streambuf; } err: rados_ioctx_destroy(io_ctx); out: /* allocated or NULL */ gsh_free(x0); gsh_free(x1); gsh_free(x2); return ret; } static struct gsh_url_provider rados_url_provider = { .name = "rados", .url_init = cu_rados_url_early_init, .url_shutdown = cu_rados_url_shutdown, .url_fetch = cu_rados_url_fetch }; void conf_url_rados_pkginit(void) { register_url_provider(&rados_url_provider); } #endif /* RADOS_URLS */ nfs-ganesha-2.6.0/src/config_parsing/conf_url_rados.h000066400000000000000000000022751324272410200226430ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- * Copyright (C) 2017, Red Hat, Inc. * contributeur : Matt Benjamin mbenjamin@redhat.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * --------------------------------------- */ #ifndef CONF_URL_RADOS_H #define CONF_URL_RADOS_H #include "config.h" #ifdef RADOS_URLS #include #include "gsh_list.h" #include void conf_url_rados_pkginit(void); #endif /* RADOS_URLS */ #endif /* CONF_URL_RADOS_H */ nfs-ganesha-2.6.0/src/config_parsing/conf_yacc.y000066400000000000000000000251071324272410200216100ustar00rootroot00000000000000%code top { #pragma GCC diagnostic ignored "-Wunused-value" #pragma GCC diagnostic ignored "-Wunused-variable" #include "config.h" #include "config_parsing.h" #include "analyse.h" #include "abstract_mem.h" #include #include "log.h" #if HAVE_STRING_H # include #endif } /* Options and variants */ %pure-parser %lex-param {struct parser_state *st} %parse-param {struct parser_state *st} %locations %code requires { /* alert the parser that we have our own definition */ # define YYLTYPE_IS_DECLARED 1 } %union { char *token; struct config_node *node; } %code provides { typedef struct YYLTYPE { int first_line; int first_column; int last_line; int last_column; char *filename; } YYLTYPE; # define YYLLOC_DEFAULT(Current, Rhs, N) \ do \ if (N) \ { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ (Current).filename = YYRHSLOC (Rhs, 1).filename; \ } \ else \ { /* empty RHS */ \ (Current).first_line = (Current).last_line = \ YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = (Current).last_column = \ YYRHSLOC (Rhs, 0).last_column; \ (Current).filename = NULL; /* new */ \ } \ while (0) int ganeshun_yylex(YYSTYPE *yylval_param, YYLTYPE *yylloc_param, void *scanner); int ganesha_yylex(YYSTYPE *yylval_param, YYLTYPE *yylloc_param, struct parser_state *st); void config_parse_error(YYLTYPE *yylloc_param, struct parser_state *st, char *format, ...); void ganesha_yyerror(YYLTYPE *yylloc_param, void *yyscanner, char*); struct glist_head all_blocks; struct config_node *config_block(char *blockname, struct config_node *list, YYLTYPE *yylloc_param, struct parser_state *st); void link_node(struct config_node *node); struct config_node *link_sibling(struct config_node *first, struct config_node *second); struct config_node *config_stmt(char *varname, struct config_node *exprlist, YYLTYPE *yylloc_param, struct parser_state *st); struct config_node *config_term(char *opcode, char *varval, enum term_type type, YYLTYPE *yylloc_param, struct parser_state *st); } %token _ERROR_ %token LCURLY_OP %token RCURLY_OP %token EQUAL_OP %token COMMA_OP %token SEMI_OP %token IDENTIFIER %token STRING %token DQUOTE %token SQUOTE %token TOKEN %token REGEX_TOKEN %token TOK_PATH %token TOK_TRUE %token TOK_FALSE %token TOK_DECNUM %token TOK_HEXNUM %token TOK_OCTNUM %token TOK_ARITH_OP %token TOK_V4_ANY %token TOK_V4ADDR %token TOK_V4CIDR %token TOK_V6ADDR %token TOK_V6CIDR %token TOK_FSID %token TOK_NETGROUP %type deflist %type definition %type block %type statement %type exprlist %type expression %start config %% config: { /* empty */ config_parse_error(&yyloc, st, "Empty configuration file"); } | deflist { if ($1 != NULL) glist_add_tail(&($1)->node, &st->root_node->root.u.nterm.sub_nodes); link_node(&st->root_node->root); } ; deflist: definition { $$ = $1; } | deflist definition { $$ = link_sibling($1, $2); } ; /* definition: statement | block ; */ definition: IDENTIFIER EQUAL_OP statement { $$ = config_stmt($1, $3, &@$, st); } | IDENTIFIER LCURLY_OP block { $$=config_block($1, $3, &@$, st); } ; statement: exprlist SEMI_OP { $$ = $1; } | error SEMI_OP { config_parse_error(&@$, st, "Syntax error in statement"); yyerrok; $$ = NULL; } ; block: RCURLY_OP { /* empty */ $$ = NULL; } | deflist RCURLY_OP { $$ = $1; } | error RCURLY_OP { config_parse_error(&@$, st, "Syntax error in block"); yyerrok; $$ = NULL; } ; /* A statement is a comma separated sequence of option/value tokens */ exprlist: { /* empty */ $$ = NULL; } | expression { $$ = $1; } | exprlist COMMA_OP expression { $$ = link_sibling($1, $3); } ; expression: TOK_PATH { $$ = config_term(NULL, $1, TERM_PATH, &@$, st); } | TOKEN { $$ = config_term(NULL, $1, TERM_TOKEN, &@$, st); } | REGEX_TOKEN { $$ = config_term(NULL, $1, TERM_REGEX, &@$, st); } | STRING { $$ = config_term(NULL, $1, TERM_STRING, &@$, st); } | DQUOTE { $$ = config_term(NULL, $1, TERM_DQUOTE, &@$, st); } | SQUOTE { $$ = config_term(NULL, $1, TERM_SQUOTE, &@$, st); } | TOK_TRUE { $$ = config_term(NULL, $1, TERM_TRUE, &@$, st); } | TOK_FALSE { $$ = config_term(NULL, $1, TERM_FALSE, &@$, st); } | TOK_OCTNUM { $$ = config_term(NULL, $1, TERM_OCTNUM, &@$, st); } | TOK_HEXNUM { $$ = config_term(NULL, $1, TERM_HEXNUM, &@$, st); } | TOK_DECNUM { $$ = config_term(NULL, $1, TERM_DECNUM, &@$, st); } | TOK_ARITH_OP TOK_OCTNUM { $$ = config_term($1, $2, TERM_OCTNUM, &@$, st); } | TOK_ARITH_OP TOK_HEXNUM { $$ = config_term($1, $2, TERM_HEXNUM, &@$, st); } | TOK_ARITH_OP TOK_DECNUM { $$ = config_term($1, $2, TERM_DECNUM, &@$, st); } | TOK_V4_ANY { $$ = config_term(NULL, $1, TERM_V4_ANY, &@$, st); } | TOK_V4ADDR { $$ = config_term(NULL, $1, TERM_V4ADDR, &@$, st); } | TOK_V4CIDR { $$ = config_term(NULL, $1, TERM_V4CIDR, &@$, st); } | TOK_V6ADDR { $$ = config_term(NULL, $1, TERM_V6ADDR, &@$, st); } | TOK_V6CIDR { $$ = config_term(NULL, $1, TERM_V6CIDR, &@$, st); } | TOK_FSID { $$ = config_term(NULL, $1, TERM_FSID, &@$, st); } | TOK_NETGROUP { $$ = config_term(NULL, $1, TERM_NETGROUP, &@$, st); } ; %% /** * @brief Report an scanner/parser error * * Replacement for yyerror() to get more info. * HACK ALERT: new_file() does not have yylloc initialized yet for * first file so create a string and init line number for it. */ void config_parse_error(YYLTYPE *yylloc_param, struct parser_state *st, char *format, ...) { FILE *fp = st->err_type->fp; va_list arguments; char *filename = ""; int linenum = 0;; if (fp == NULL) return; /* no stream, no message */ if (yylloc_param != NULL) { filename = yylloc_param->filename; linenum = yylloc_param->first_line; } va_start(arguments, format); config_error(fp, filename, linenum, format, arguments); va_end(arguments); } /* This is here because bison wants it declared. * We do not use it because we can't get around the API. * Use config_parse_error() instead. */ void ganesha_yyerror(YYLTYPE *yylloc_param, void *yyscanner, char *s){ LogCrit(COMPONENT_CONFIG, "Config file (%s:%d) error: %s\n", yylloc_param->filename, yylloc_param->first_line, s); } /** * @brief Notes on parse tree linkage * * We use glist a little differently so beware. * Elsewhere in the server, we only glist_init() the head * and leave the 'node' members alone (both next and prev == NULL). * However, the parse FSM handles siblings as a list via the LR rules. * This means that while sub_nodes is the "head" of the list, it only * gets linked in when the rules add the already formed list is fully * parsed. Therefore, to make this all work, each node's 'node' member * gets a turn as the head which requires it to be glist_init()'d * contrary to what the rest of the code does. The last node to play * 'head' is then the 'sub_nodes' member of the parent. */ /** * Create a block item with the given content */ void dump_all_blocks(void) { struct glist_head *glh; struct config_node *node; int ix = 0; glist_for_each(glh, &all_blocks) { node = glist_entry(glh, struct config_node, blocks); printf("%s: ix: %d node blockname: %s\n", __func__, ix, node->u.nterm.name); ++ix; } } struct config_node *config_block(char *blockname, struct config_node *list, YYLTYPE *yylloc_param, struct parser_state *st) { struct config_node *node; node = gsh_calloc(1, sizeof(struct config_node)); if (node == NULL) { st->err_type->resource = true; return NULL; } glist_init(&node->node); glist_init(&node->blocks); node->u.nterm.name = blockname; node->filename = yylloc_param->filename; node->linenumber = yylloc_param->first_line; node->type = TYPE_BLOCK; glist_init(&node->u.nterm.sub_nodes); if (list != NULL) { glist_add_tail(&list->node, &node->u.nterm.sub_nodes); link_node(node); } glist_add_tail(&all_blocks, &node->blocks); return node; } /** * @brief Walk the subnode list and update all the sub-blocks in it * so we can find the root of the parse tree when we need it. */ void link_node(struct config_node *node) { struct config_node *subnode; struct glist_head *ns; assert(node->type == TYPE_BLOCK || node->type == TYPE_ROOT); glist_for_each(ns, &node->u.nterm.sub_nodes) { subnode = glist_entry(ns, struct config_node, node); if (subnode->type == TYPE_BLOCK) subnode->u.nterm.parent = node; } } /** * @brief Link siblings together * */ struct config_node *link_sibling(struct config_node *first, struct config_node *second) { if (first == NULL) { return second; } else { if (second != NULL) glist_add_tail(&first->node, &second->node); return first; } } /** * Create a term (value) */ struct config_node *config_term(char *opcode, char *varval, enum term_type type, YYLTYPE *yylloc_param, struct parser_state *st) { struct config_node *node; node = gsh_calloc(1, sizeof(struct config_node)); if (node == NULL) { st->err_type->resource = true; return NULL; } glist_init(&node->node); node->filename = yylloc_param->filename; node->linenumber = yylloc_param->first_line; node->type = TYPE_TERM; node->u.term.type = type; node->u.term.op_code = opcode; node->u.term.varvalue = varval; return node; } /** * Create a statement node (key = list of terms) */ struct config_node *config_stmt(char *varname, struct config_node *exprlist, YYLTYPE *yylloc_param, struct parser_state *st) { struct config_node *node; node = gsh_calloc(1, sizeof(struct config_node)); if (node == NULL) { st->err_type->resource = true; return NULL; } glist_init(&node->node); glist_init(&node->u.nterm.sub_nodes); node->filename = yylloc_param->filename; node->linenumber = yylloc_param->first_line; node->type = TYPE_STMT; node->u.nterm.name = varname; if (exprlist != NULL) glist_add_tail(&exprlist->node, &node->u.nterm.sub_nodes); return node; } int ganesha_yylex(YYSTYPE *yylval_param, YYLTYPE *yylloc_param, struct parser_state *st) { return ganeshun_yylex(yylval_param, yylloc_param, st->scanner); } nfs-ganesha-2.6.0/src/config_parsing/config_parsing.c000066400000000000000000001417611324272410200226330ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- * Copyright CEA/DAM/DIF (2007) * contributeur : Thomas LEIBOVICI thomas.leibovici@cea.fr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ #include "config.h" #include #include #include #if HAVE_STRING_H #include #endif #include #include #include #include #include #include "config_parsing.h" #include "analyse.h" #include "abstract_mem.h" #include "conf_yacc.h" #include "log.h" #include "fsal_convert.h" /* config_ParseFile: * Reads the content of a configuration file and * stores it in a memory structure. */ config_file_t config_ParseFile(char *file_path, struct config_error_type *err_type) { struct parser_state st; struct config_root *root; int rc; glist_init(&all_blocks); memset(&st, 0, sizeof(struct parser_state)); st.err_type = err_type; rc = ganeshun_yy_init_parser(file_path, &st); if (rc) { return NULL; } rc = ganesha_yyparse(&st); root = st.root_node; if (rc != 0) config_proc_error(root, err_type, (rc == 1 ? "Configuration syntax errors found" : "Configuration parse ran out of memory")); #ifdef DUMP_PARSE_TREE print_parse_tree(stderr, root); #endif ganeshun_yy_cleanup_parser(&st); return (config_file_t)root; } /** * Return the first node in the global config block list with * name == block_name */ void *config_GetBlockNode(const char *block_name) { struct glist_head *glh; struct config_node *node; glist_for_each(glh, &all_blocks) { node = glist_entry(glh, struct config_node, blocks); if (!strcasecmp(node->u.nterm.name, block_name)) { return node; } } return NULL; } /** * config_Print: * Print the content of the syntax tree * to a file. */ void config_Print(FILE * output, config_file_t config) { print_parse_tree(output, (struct config_root *)config); } /** * config_Free: * Free the memory structure that store the configuration. */ void config_Free(config_file_t config) { if (config != NULL) free_parse_tree((struct config_root *)config); return; } /** * @brief Return an error string constructed from an err_type * * The string is constructed in allocate memory that must be freed * by the caller. * * @param err_type [IN] the err_type struct in question. * * @return a NULL term'd string or NULL on failure. */ char *err_type_str(struct config_error_type *err_type) { char *buf = NULL; size_t bufsize; FILE *fp; if (config_error_no_error(err_type)) return gsh_strdup("(no errors)"); fp = open_memstream(&buf, &bufsize); if (fp == NULL) { LogCrit(COMPONENT_CONFIG, "Could not open memstream for err_type string"); return NULL; } fputc('(', fp); if (err_type->scan) fputs("token scan, ", fp); if (err_type->parse) fputs("parser rule, ", fp); if (err_type->init) fputs("block init, ", fp); if (err_type->fsal) fputs("fsal load, ", fp); if (err_type->export_) fputs("export create, ", fp); if (err_type->resource) fputs("resource alloc, ", fp); if (err_type->unique) fputs("not unique param, ", fp); if (err_type->invalid) fputs("invalid param value, ", fp); if (err_type->missing) fputs("missing mandatory param, ", fp); if (err_type->validate) fputs("block validation, ", fp); if (err_type->exists) fputs("block exists, ", fp); if (err_type->internal) fputs("internal error, ", fp); if (err_type->bogus) fputs("unknown param, ", fp); if (ferror(fp)) LogCrit(COMPONENT_CONFIG, "file error while constructing err_type string"); fclose(fp); if (buf == NULL) { LogCrit(COMPONENT_CONFIG, "close of memstream for err_type string failed"); return NULL; } /* each of the above strings (had better) have ', ' at the end! */ if (buf[strlen(buf) -1] == ' ') { buf[bufsize - 2] = ')'; buf[bufsize - 1] = '\0'; } return buf; } bool init_error_type(struct config_error_type *err_type) { memset(err_type, 0, sizeof(struct config_error_type)); err_type->fp = open_memstream(&err_type->diag_buf, &err_type->diag_buf_size); if (err_type->fp == NULL) { LogCrit(COMPONENT_MAIN, "Could not open memory stream for parser errors"); return false; } return true; } /** * @brief Log an error to the parse error stream * * cnode is void * because struct config_node is hidden outside parse code * and we can report errors in fsal_manager.c etc. */ void config_proc_error(void *cnode, struct config_error_type *err_type, char *format, ...) { struct config_node *node = cnode; FILE *fp = err_type->fp; char *filename = ""; int linenumber = 0; va_list arguments; if (fp == NULL) return; /* no stream, no message */ if (node != NULL && node->filename != NULL) { filename = node->filename; linenumber = node->linenumber; } va_start(arguments, format); config_error(fp, filename, linenumber, format, arguments); va_end(arguments); } void config_errs_to_log(char *err, void *dest, struct config_error_type *err_type) { log_levels_t log_level; if (config_error_is_fatal(err_type) || config_error_is_crit(err_type)) log_level = NIV_CRIT; else if (config_error_is_harmless(err_type)) log_level = NIV_WARN; else log_level = NIV_EVENT; DisplayLogComponentLevel(COMPONENT_CONFIG, __FILE__, __LINE__, (char *)__func__, log_level, "%s", err); } void report_config_errors(struct config_error_type *err_type, void *dest, void (*logger)(char *msg, void *dest, struct config_error_type *err_type)) { char *msgp, *cp; if (err_type->fp == NULL) return; fclose(err_type->fp); err_type->fp = NULL; msgp = err_type->diag_buf; if (msgp == NULL) return; while (*msgp != '\0') { cp = index(msgp, '\f'); if (cp != NULL) { *cp++ = '\0'; logger(msgp, dest, err_type); msgp = cp; } else { logger(msgp, dest, err_type); break; } } gsh_free(err_type->diag_buf); err_type->diag_buf = NULL; } static bool convert_bool(struct config_node *node, bool *b, struct config_error_type *err_type) { if (node->u.term.type == TERM_TRUE) *b = true; else if (node->u.term.type == TERM_FALSE) *b = false; else { config_proc_error(node, err_type, "Expected boolean (true/false) got (%s)", node->u.term.varvalue); err_type->errors++; err_type->invalid = true; return false; } return true; } static bool convert_number(struct config_node *node, struct config_item *item, uint64_t *num, struct config_error_type *err_type) { uint64_t val, mask, min, max; int64_t sval, smin, smax; char *endptr; int base; bool signed_int = false; if (node->type != TYPE_TERM) { config_proc_error(node, err_type, "Expected a number, got a %s", (node->type == TYPE_ROOT ? "root node" : (node->type == TYPE_BLOCK ? "block" : "statement"))); goto errout; } else if (node->u.term.type == TERM_DECNUM) { base = 10; } else if (node->u.term.type == TERM_HEXNUM) { base = 16; } else if (node->u.term.type == TERM_OCTNUM) { base = 8; } else { config_proc_error(node, err_type, "Expected a number, got a %s", config_term_desc(node->u.term.type)); goto errout; } errno = 0; assert(*node->u.term.varvalue != '\0'); val = strtoull(node->u.term.varvalue, &endptr, base); if (*endptr != '\0' || errno != 0) { config_proc_error(node, err_type, "(%s) is not an integer", node->u.term.varvalue); goto errout; } switch (item->type) { case CONFIG_INT16: smin = item->u.i16.minval; smax = item->u.i16.maxval; signed_int = true; break; case CONFIG_UINT16: mask = UINT16_MAX; min = item->u.ui16.minval; max = item->u.ui16.maxval; break; case CONFIG_INT32: smin = item->u.i32.minval; smax = item->u.i32.maxval; signed_int = true; break; case CONFIG_UINT32: mask = UINT32_MAX; min = item->u.ui32.minval; max = item->u.ui32.maxval; break; case CONFIG_ANON_ID: /* Internal to config, anonymous id is treated as int64_t */ smin = item->u.i64.minval; smax = item->u.i64.maxval; signed_int = true; break; case CONFIG_INT64: smin = item->u.i64.minval; smax = item->u.i64.maxval; signed_int = true; break; case CONFIG_UINT64: mask = UINT64_MAX; min = item->u.ui64.minval; max = item->u.ui64.maxval; break; default: goto errout; } if (signed_int) { if (node->u.term.op_code == NULL) { /* Check for overflow of int64_t on positive */ if (val > (uint64_t) INT64_MAX) { config_proc_error(node, err_type, "(%s) is out of range", node->u.term.varvalue); goto errout; } sval = val; } else if (*node->u.term.op_code == '-') { /* Check for underflow of int64_t on negative */ if (val > ((uint64_t) INT64_MAX) + 1) { config_proc_error(node, err_type, "(%s) is out of range", node->u.term.varvalue); goto errout; } sval = -((int64_t) val); } else { config_proc_error(node, err_type, "(%c) is not allowed for signed values", *node->u.term.op_code); goto errout; } if (sval < smin || sval > smax) { config_proc_error(node, err_type, "(%s) is out of range", node->u.term.varvalue); goto errout; } val = (uint64_t) sval; } else { if (node->u.term.op_code != NULL && *node->u.term.op_code == '~') { /* Check for overflow before negation */ if ((val & ~mask) != 0) { config_proc_error(node, err_type, "(%s) is out of range", node->u.term.varvalue); goto errout; } val = ~val & mask; } else if (node->u.term.op_code != NULL) { config_proc_error(node, err_type, "(%c) is not allowed for signed values", *node->u.term.op_code); goto errout; } if (val < min || val > max) { config_proc_error(node, err_type, "(%s) is out of range", node->u.term.varvalue); goto errout; } } *num = val; return true; errout: err_type->errors++; err_type->invalid = true; return false; } /** * @brief convert an fsid which is a "<64bit num>.<64bit num" * * NOTE: using assert() here because the parser has already * validated so bad things have happened to the parse tree if... * */ static bool convert_fsid(struct config_node *node, void *param, struct config_error_type *err_type) { struct fsal_fsid__ *fsid = (struct fsal_fsid__ *)param; uint64_t major, minor; char *endptr, *sp; int base; if (node->type != TYPE_TERM) { config_proc_error(node, err_type, "Expected an FSID, got a %s", (node->type == TYPE_ROOT ? "root node" : (node->type == TYPE_BLOCK ? "block" : "statement"))); goto errout; } if (node->u.term.type != TERM_FSID) { config_proc_error(node, err_type, "Expected an FSID, got a %s", config_term_desc(node->u.term.type)); goto errout; } errno = 0; sp = node->u.term.varvalue; if (sp[0] == '0') { if (sp[1] == 'x' || sp[1] == 'X') base = 16; else base = 8; } else base = 10; major = strtoull(sp, &endptr, base); assert(*endptr == '.'); if (errno != 0 || major == ULLONG_MAX) { config_proc_error(node, err_type, "(%s) major is out of range", node->u.term.varvalue); goto errout; } sp = endptr + 1; if (sp[0] == '0') { if (sp[1] == 'x' || sp[1] == 'X') base = 16; else base = 8; } else base = 10; minor = strtoull(sp, &endptr, base); assert(*endptr == '\0'); if (errno != 0 || minor == ULLONG_MAX) { config_proc_error(node, err_type, "(%s) minor is out of range", node->u.term.varvalue); goto errout; } fsid->major = major; fsid->minor = minor; return true; errout: err_type->invalid = true; err_type->errors++; return false; } /** * @brief Scan a list of CSV tokens. * */ static bool convert_list(struct config_node *node, struct config_item *item, uint32_t *flags, struct config_error_type *err_type) { struct config_item_list *tok; struct config_node *sub_node; struct glist_head *nsi, *nsn; bool found; int errors = 0; *flags = 0; glist_for_each_safe(nsi, nsn, &node->u.nterm.sub_nodes) { sub_node = glist_entry(nsi, struct config_node, node); assert(sub_node->type == TYPE_TERM); found = false; for (tok = item->u.lst.tokens; tok->token != NULL; tok++) { if (strcasecmp(sub_node->u.term.varvalue, tok->token) == 0) { *flags |= tok->value; found = true; } } if (!found) { config_proc_error(node, err_type, "Unknown token (%s)", sub_node->u.term.varvalue); err_type->bogus = true; errors++; } } err_type->errors += errors; return errors == 0; } static bool convert_enum(struct config_node *node, struct config_item *item, uint32_t *val, struct config_error_type *err_type) { struct config_item_list *tok; bool found; tok = item->u.lst.tokens; found = false; while (tok->token != NULL) { if (strcasecmp(node->u.term.varvalue, tok->token) == 0) { *val = tok->value; found = true; } tok++; } if (!found) { config_proc_error(node, err_type, "Unknown token (%s)", node->filename, node->linenumber, node->u.term.varvalue); err_type->bogus = true; err_type->errors++; } return found; } static void convert_inet_addr(struct config_node *node, struct config_item *item, sockaddr_t *sock, struct config_error_type *err_type) { struct addrinfo hints; struct addrinfo *res = NULL; int rc; if (node->u.term.type != TERM_V4ADDR && node->u.term.type != TERM_V4_ANY && node->u.term.type != TERM_V6ADDR) { config_proc_error(node, err_type, "Expected an IP address, got a %s", config_term_desc(node->u.term.type)); err_type->invalid = true; err_type->errors++; return; } /* Try IPv6 (with mapping) first. If this fails, fall back on IPv4, if * a v4 address was given. */ hints.ai_family = AF_INET6; hints.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED; hints.ai_socktype = 0; hints.ai_protocol = 0; rc = getaddrinfo(node->u.term.varvalue, NULL, &hints, &res); if (rc != 0 && (node->u.term.type == TERM_V4ADDR || node->u.term.type == TERM_V4_ANY)) { hints.ai_family = AF_INET; rc = getaddrinfo(node->u.term.varvalue, NULL, &hints, &res); } if (rc == 0) { memcpy(sock, res->ai_addr, res->ai_addrlen); } else { config_proc_error(node, err_type, "No IP address found for %s because:%s", node->u.term.varvalue, gai_strerror(rc)); err_type->invalid = true; err_type->errors++; } if (res != NULL) freeaddrinfo(res); return; } /** * @brief Walk the term node list and call handler for each node * * This is effectively a callback on each token in the list. * Pass along a type hint based on what the parser recognized. * * @param node [IN] pointer to the statement node * @param item [IN] pointer to the config_item table entry * @param param_addr [IN] pointer to target struct member * @param err_type [OUT] error handling ref * @return number of errors */ static void do_proc(struct config_node *node, struct config_item *item, void *param_addr, struct config_error_type *err_type) { struct config_node *term_node; struct glist_head *nsi, *nsn; int rc = 0; assert(node->type == TYPE_STMT); glist_for_each_safe(nsi, nsn, &node->u.nterm.sub_nodes) { term_node = glist_entry(nsi, struct config_node, node); rc += item->u.proc.handler(term_node->u.term.varvalue, term_node->u.term.type, item, param_addr, term_node, err_type); } err_type->errors += rc; } /** * @brief Lookup the first node in the list by this name * * @param list - head of the glist * @param name - node name of interest * * @return first matching node or NULL */ static struct config_node *lookup_node(struct glist_head *list, const char *name) { struct config_node *node; struct glist_head *ns; glist_for_each(ns, list) { node = glist_entry(ns, struct config_node, node); assert(node->type == TYPE_BLOCK || node->type == TYPE_STMT); if (strcasecmp(name, node->u.nterm.name) == 0) { node->found = true; return node; } } return NULL; } /** * @brief Lookup the next node in list * * @param list - head of the glist * @param start - continue the lookup from here * @param name - node name of interest * * @return first matching node or NULL */ static struct config_node *lookup_next_node(struct glist_head *list, struct glist_head *start, const char *name) { struct config_node *node; struct glist_head *ns; glist_for_each_next(start, ns, list) { node = glist_entry(ns, struct config_node, node); assert(node->type == TYPE_BLOCK || node->type == TYPE_STMT); if (strcasecmp(name, node->u.nterm.name) == 0) { node->found = true; return node; } } return NULL; } static const char *config_type_str(enum config_type type) { switch(type) { case CONFIG_NULL: return "CONFIG_NULL"; case CONFIG_INT16: return "CONFIG_INT16"; case CONFIG_UINT16: return "CONFIG_UINT16"; case CONFIG_INT32: return "CONFIG_INT32"; case CONFIG_UINT32: return "CONFIG_UINT32"; case CONFIG_INT64: return "CONFIG_INT64"; case CONFIG_UINT64: return "CONFIG_UINT64"; case CONFIG_FSID: return "CONFIG_FSID"; case CONFIG_ANON_ID: return "CONFIG_ANON_ID"; case CONFIG_STRING: return "CONFIG_STRING"; case CONFIG_PATH: return "CONFIG_PATH"; case CONFIG_LIST: return "CONFIG_LIST"; case CONFIG_ENUM: return "CONFIG_ENUM"; case CONFIG_TOKEN: return "CONFIG_TOKEN"; case CONFIG_BOOL: return "CONFIG_BOOL"; case CONFIG_BOOLBIT: return "CONFIG_BOOLBIT"; case CONFIG_IP_ADDR: return "CONFIG_IP_ADDR"; case CONFIG_BLOCK: return "CONFIG_BLOCK"; case CONFIG_PROC: return "CONFIG_PROC"; } return "unknown"; } static bool do_block_init(struct config_node *blk_node, struct config_item *params, void *param_struct, struct config_error_type *err_type) { struct config_item *item; void *param_addr; sockaddr_t *sock; struct addrinfo hints; struct addrinfo *res = NULL; int rc; int errors = 0; for (item = params; item->name != NULL; item++) { param_addr = ((char *)param_struct + item->off); LogFullDebug(COMPONENT_CONFIG, "%p name=%s type=%s", param_addr, item->name, config_type_str(item->type)); switch (item->type) { case CONFIG_NULL: break; case CONFIG_INT16: *(int16_t *)param_addr = item->u.i16.def; break; case CONFIG_UINT16: *(uint16_t *)param_addr = item->u.ui16.def; break; case CONFIG_INT32: *(int32_t *)param_addr = item->u.i32.def; break; case CONFIG_UINT32: *(uint32_t *)param_addr = item->u.ui32.def; break; case CONFIG_INT64: *(int64_t *)param_addr = item->u.i64.def; break; case CONFIG_UINT64: *(uint64_t *)param_addr = item->u.ui64.def; break; case CONFIG_ANON_ID: *(uid_t *)param_addr = item->u.i64.def; break; case CONFIG_FSID: ((struct fsal_fsid__ *)param_addr)->major = item->u.fsid.def_maj; ((struct fsal_fsid__ *)param_addr)->minor = item->u.fsid.def_min; break; case CONFIG_STRING: case CONFIG_PATH: if (item->u.str.def) *(char **)param_addr = gsh_strdup(item->u.str.def); else *(char **)param_addr = NULL; break; case CONFIG_TOKEN: *(uint32_t *)param_addr = item->u.lst.def; break; case CONFIG_BOOL: *(bool *)param_addr = item->u.b.def; break; case CONFIG_BOOLBIT: if (item->u.bit.def) *(uint32_t *)param_addr |= item->u.bit.bit; else *(uint32_t *)param_addr &= ~item->u.bit.bit; break; case CONFIG_LIST: *(uint32_t *)param_addr |= item->u.lst.def; LogFullDebug(COMPONENT_CONFIG, "%p CONFIG_LIST %s mask=%08x def=%08x" " value=%08"PRIx32, param_addr, item->name, item->u.lst.mask, item->u.lst.def, *(uint32_t *)param_addr); break; case CONFIG_ENUM: *(uint32_t *)param_addr |= item->u.lst.def; LogFullDebug(COMPONENT_CONFIG, "%p CONFIG_ENUM %s mask=%08x def=%08x" " value=%08"PRIx32, param_addr, item->name, item->u.lst.mask, item->u.lst.def, *(uint32_t *)param_addr); break; case CONFIG_IP_ADDR: sock = (sockaddr_t *)param_addr; memset(sock, 0, sizeof(sockaddr_t)); errno = 0; /* Try IPv6 (with mapping) first. If this fails, fall * back on IPv4, if a v4 address was given. */ hints.ai_family = AF_INET6; hints.ai_flags = AI_ADDRCONFIG | AI_V4MAPPED; hints.ai_socktype = 0; hints.ai_protocol = 0; rc = getaddrinfo(item->u.ip.def, NULL, &hints, &res); if (rc != 0) { hints.ai_family = AF_INET; rc = getaddrinfo(item->u.ip.def, NULL, &hints, &res); } if (rc == 0) { memcpy(sock, res->ai_addr, res->ai_addrlen); } else { config_proc_error(blk_node, err_type, "Cannot set IP default for %s to %s because %s", item->name, item->u.ip.def, strerror(errno)); errors++; } if (res != NULL) freeaddrinfo(res); break; case CONFIG_BLOCK: (void) item->u.blk.init(NULL, param_addr); break; case CONFIG_PROC: (void) item->u.proc.init(NULL, param_addr); break; default: config_proc_error(blk_node, err_type, "Cannot set default for parameter %s, type(%d) yet", item->name, item->type); errors++; break; } } err_type->errors += errors; return errors == 0; } /** * @brief This is the NOOP init for block and proc parsing * * @param link_mem [IN] pointer to member in referencing structure * @param self_struct [IN] pointer to space reserved/allocated for block * * @return a pointer depending on context */ void *noop_conf_init(void *link_mem, void *self_struct) { assert(link_mem != NULL || self_struct != NULL); if (link_mem == NULL) return self_struct; else if (self_struct == NULL) return link_mem; else return NULL; } int noop_conf_commit(void *node, void *link_mem, void *self_struct, struct config_error_type *err_type) { return 0; } static bool proc_block(struct config_node *node, struct config_item *item, void *link_mem, struct config_error_type *err_type); /* * All the types of integers supported by the config processor */ union gen_int { bool b; int16_t i16; uint16_t ui16; int32_t i32; uint32_t ui32; int64_t i64; uint64_t ui64; }; /** * @brief Process the defined tokens in the params table * * The order of the parameter table is important. If any * parameter requires that another parameter in the table be * processed first, order the table appropriately. * * @param blk [IN] a parse tree node of type CONFIG_BLOCK * @param params [IN] points to a NULL term'd table of config_item's * @param relax [IN] if true, don't report unrecognized params. * @param param_struct [IN/OUT] the structure to be filled. * * @returns 0 on success, number of errors on failure. */ static int do_block_load(struct config_node *blk, struct config_item *params, bool relax, void *param_struct, struct config_error_type *err_type) { struct config_item *item; void *param_addr; struct config_node *node, *term_node, *next_node = NULL; struct glist_head *ns; int errors = 0; for (item = params; item->name != NULL; item++) { uint64_t num64; bool bool_val; uint32_t num32 = 0; node = lookup_node(&blk->u.nterm.sub_nodes, item->name); if ((item->flags & CONFIG_MANDATORY) && (node == NULL)) { err_type->missing = true; errors = ++err_type->errors; config_proc_error(blk, err_type, "Mandatory field, %s is missing from block (%s)", item->name, blk->u.nterm.name); continue; } while (node != NULL) { next_node = lookup_next_node(&blk->u.nterm.sub_nodes, &node->node, item->name); if (next_node != NULL && (item->flags & CONFIG_UNIQUE)) { config_proc_error(next_node, err_type, "Parameter %s set more than once", next_node->u.nterm.name); err_type->unique = true; errors = ++err_type->errors; node = next_node; continue; } param_addr = ((char *)param_struct + item->off); LogFullDebug(COMPONENT_CONFIG, "%p name=%s type=%s", param_addr, item->name, config_type_str(item->type)); if (glist_empty(&node->u.nterm.sub_nodes)) { LogInfo(COMPONENT_CONFIG, "%s %s is empty", (node->type == TYPE_STMT ? "Statement" : "Block"), node->u.nterm.name); node = next_node; continue; } term_node = glist_first_entry(&node->u.nterm.sub_nodes, struct config_node, node); if ((item->type != CONFIG_BLOCK && item->type != CONFIG_PROC && item->type != CONFIG_LIST) && glist_length(&node->u.nterm.sub_nodes) > 1) { config_proc_error(node, err_type, "%s can have only one option. First one is (%s)", node->u.nterm.name, term_node->u.term.varvalue); err_type->invalid = true; errors = ++err_type->errors; node = next_node; continue; } switch (item->type) { case CONFIG_NULL: break; case CONFIG_INT16: if (convert_number(term_node, item, &num64, err_type)) *(int16_t *)param_addr = num64; break; case CONFIG_UINT16: if (convert_number(term_node, item, &num64, err_type)) *(uint16_t *)param_addr = num64; break; case CONFIG_INT32: if (convert_number(term_node, item, &num64, err_type)) { *(int32_t *)param_addr = num64; if (item->flags & CONFIG_MARK_SET) { void *mask_addr; mask_addr = ((char *)param_struct + item->u.i32.set_off); *(uint32_t *)mask_addr |= item->u.i32.bit; } } break; case CONFIG_UINT32: if (convert_number(term_node, item, &num64, err_type)) *(uint32_t *)param_addr = num64; break; case CONFIG_INT64: if (convert_number(term_node, item, &num64, err_type)) *(int64_t *)param_addr = num64; break; case CONFIG_UINT64: if (convert_number(term_node, item, &num64, err_type)) { *(uint64_t *)param_addr = num64; if (item->flags & CONFIG_MARK_SET) { void *mask_addr; mask_addr = ((char *)param_struct + item->u.ui64.set_off); *(uint32_t *)mask_addr |= item->u.ui64.bit; } } break; case CONFIG_ANON_ID: if (convert_number(term_node, item, &num64, err_type)) { *(uid_t *)param_addr = num64; if (item->flags & CONFIG_MARK_SET) { void *mask_addr; mask_addr = ((char *)param_struct + item->u.i64.set_off); *(uint32_t *)mask_addr |= item->u.i64.bit; } } break; case CONFIG_FSID: if (convert_fsid(term_node, param_addr, err_type)) { if (item->flags & CONFIG_MARK_SET) { void *mask_addr; mask_addr = ((char *)param_struct + item->u.fsid.set_off); *(uint32_t *)mask_addr |= item->u.fsid.bit; } } break; case CONFIG_STRING: if (*(char **)param_addr != NULL) gsh_free(*(char **)param_addr); *(char **)param_addr = gsh_strdup(term_node->u.term.varvalue); break; case CONFIG_PATH: if (*(char **)param_addr != NULL) gsh_free(*(char **)param_addr); /** @todo validate path with access() */ *(char **)param_addr = gsh_strdup(term_node->u.term.varvalue); break; case CONFIG_TOKEN: if (convert_enum(term_node, item, &num32, err_type)) *(uint32_t *)param_addr = num32; break; case CONFIG_BOOL: if (convert_bool(term_node, &bool_val, err_type)) *(bool *)param_addr = bool_val; break; case CONFIG_BOOLBIT: if (convert_bool(term_node, &bool_val, err_type)) { if (bool_val) *(uint32_t *)param_addr |= item->u.bit.bit; else *(uint32_t *)param_addr &= ~item->u.bit.bit; if (item->flags & CONFIG_MARK_SET) { void *mask_addr; mask_addr = ((char *)param_struct + item->u.bit.set_off); *(uint32_t *)mask_addr |= item->u.bit.bit; } } break; case CONFIG_LIST: if (item->u.lst.def == (*(uint32_t *)param_addr & item->u.lst.mask)) *(uint32_t *)param_addr &= ~item->u.lst.mask; if (convert_list(node, item, &num32, err_type)) { *(uint32_t *)param_addr |= num32; if (item->flags & CONFIG_MARK_SET) { void *mask_addr; mask_addr = ((char *)param_struct + item->u.lst.set_off); *(uint32_t *)mask_addr |= item->u.lst.mask; } } LogFullDebug(COMPONENT_CONFIG, "%p CONFIG_LIST %s mask=%08x flags=%08x" " value=%08"PRIx32, param_addr, item->name, item->u.lst.mask, num32, *(uint32_t *)param_addr); break; case CONFIG_ENUM: if (item->u.lst.def == (*(uint32_t *)param_addr & item->u.lst.mask)) *(uint32_t *)param_addr &= ~item->u.lst.mask; if (convert_enum(term_node, item, &num32, err_type)) { *(uint32_t *)param_addr |= num32; if (item->flags & CONFIG_MARK_SET) { void *mask_addr; mask_addr = ((char *)param_struct + item->u.lst.set_off); *(uint32_t *)mask_addr |= item->u.lst.mask; } } LogFullDebug(COMPONENT_CONFIG, "%p CONFIG_ENUM %s mask=%08x flags=%08x" " value=%08"PRIx32, param_addr, item->name, item->u.lst.mask, num32, *(uint32_t *)param_addr); break; case CONFIG_IP_ADDR: convert_inet_addr(term_node, item, (sockaddr_t *)param_addr, err_type); break; case CONFIG_BLOCK: if (!proc_block(node, item, param_addr, err_type)) config_proc_error(node, err_type, "Errors processing block (%s)", node->u.nterm.name); break; case CONFIG_PROC: do_proc(node, item, param_addr, err_type); break; default: config_proc_error(term_node, err_type, "Cannot set value for type(%d) yet", item->type); err_type->internal = true; errors = ++err_type->errors; break; } node = next_node; } } if (relax) return errors; /* We've been marking config nodes as being "seen" during the * scans. Report the bogus and typo inflicted bits. */ glist_for_each(ns, &blk->u.nterm.sub_nodes) { node = glist_entry(ns, struct config_node, node); if (node->found) node->found = false; else { config_proc_error(node, err_type, "Unknown parameter (%s)", node->u.nterm.name); err_type->bogus = true; errors++; } } return errors; } /** * @brief Process a block * * The item arg supplies two function pointers that * are defined as follows: * * init * This function manages memory for the sub-block's processing. * It has two arguments, a pointer to the link_mem param struct and * a pointer to the self_struct param struct. * If the self_struct argument is NULL, it returns a pointer to a usable * self_struct param struct. This can either be allocate memory * or a pointer to existing memory. * * If the self_struct argument is not NULL, it is the pointer it returned above. * The function reverts whatever it did above. * * commit * This function attaches the build param struct to its link_mem. * Before it does the attach, it will do validation of input if required. * Returns 0 if validation passes and the attach is successful. Otherwise, * it returns an error which will trigger a release of resourcs acquired * by the self_struct_init. * * Both of these functions are called in the context of the link_mem parse. * It is assumed that the link_mem has already been initialized including * the init of any glists before this function is called. * The self_struct does its own init of things like glist in param_mem. * * @param node - parse node of the subblock * @param item - config_item describing block * @param link_mem - pointer to the link_mem structure * @param err_type [OUT] pointer to error type return * * @ return true on success, false on errors. */ static bool proc_block(struct config_node *node, struct config_item *item, void *link_mem, struct config_error_type *err_type) { void *param_struct; int errors = 0; assert(item->type == CONFIG_BLOCK); if (node->type != TYPE_BLOCK) { config_proc_error(node, err_type, "%s is not a block!", item->name); err_type->invalid = true; err_type->errors++; return false; } param_struct = item->u.blk.init(link_mem, NULL); if (param_struct == NULL) { config_proc_error(node, err_type, "Could not init block for %s", item->name); err_type->init = true; err_type->errors++; return false; } LogFullDebug(COMPONENT_CONFIG, "------ At (%s:%d): do_block_init %s", node->filename, node->linenumber, item->name); if (!do_block_init(node, item->u.blk.params, param_struct, err_type)) { config_proc_error(node, err_type, "Could not initialize parameters for %s", item->name); err_type->init = true; goto err_out; } if (item->u.blk.display != NULL) item->u.blk.display("DEFAULTS", node, link_mem, param_struct); LogFullDebug(COMPONENT_CONFIG, "------ At (%s:%d): do_block_load %s", node->filename, node->linenumber, item->name); errors = do_block_load(node, item->u.blk.params, (item->flags & CONFIG_RELAX) ? true : false, param_struct, err_type); if (errors > 0 && !config_error_is_harmless(err_type)) { config_proc_error(node, err_type, "%d errors while processing parameters for %s", errors, item->name); goto err_out; } LogFullDebug(COMPONENT_CONFIG, "------ At (%s:%d): commit %s", node->filename, node->linenumber, item->name); errors = item->u.blk.commit(node, link_mem, param_struct, err_type); if (errors > 0 && !config_error_is_harmless(err_type)) { config_proc_error(node, err_type, "%d validation errors in block %s", errors, item->name); goto err_out; } if (item->u.blk.display != NULL) item->u.blk.display("RESULT", node, link_mem, param_struct); if (err_type->dispose) { /* We had a config update case where this block must be * disposed of. Need to clear the flag so the next config * block processed gets a clear slate. */ (void)item->u.blk.init(link_mem, param_struct); err_type->dispose = false; } return true; err_out: (void)item->u.blk.init(link_mem, param_struct); err_type->dispose = false; return false; } /** * @brief Find the root of the parse tree given a node. * * @param node [IN] pointer to a TYPE_BLOCK node. * * @return the root of the tree. Errors are asserted. */ config_file_t get_parse_root(void *node) { struct config_node *parent; struct config_root *root; parent = (struct config_node *)node; assert(parent->type == TYPE_BLOCK); while (parent->u.nterm.parent != NULL) { parent = parent->u.nterm.parent; assert(parent->type == TYPE_ROOT || parent->type == TYPE_BLOCK); } assert(parent->type == TYPE_ROOT); root = container_of(parent, struct config_root, root); return (config_file_t)root; } /** * @brief Data structures for walking parse trees * * These structures hold the result of the parse of a block * description string that is passed to find_config_nodes * * expr_parse is for blocks * * expr_parse_arg is for indexing/matching parameters. */ struct expr_parse_arg { char *name; char *value; struct expr_parse_arg *next; }; struct expr_parse { char *name; struct expr_parse_arg *arg; struct expr_parse *next; }; /** * @brief Skip 0 or more white space chars * * @return pointer to first non-white space char */ static inline char *skip_white(char *sp) { while (isspace(*sp)) sp++; return sp; } /** * @brief Find the end of a token, i.e. [A-Za-z0-9_]+ * * @return pointer to first unmatched char. */ static inline char *end_of_token(char *sp) { while (isalnum(*sp) || *sp == '_') sp++; return sp; } /** * @brief Find end of a value string. * * @return pointer to first white or syntax token */ static inline char *end_of_value(char *sp) { while (*sp != '\0') { if (isspace(*sp) || *sp == ',' || *sp == ')' || *sp == '(') break; sp++; } return sp; } /** * @brief Release storage for arg list */ static void free_expr_parse_arg(struct expr_parse_arg *argp) { struct expr_parse_arg *nxtarg, *arg = NULL; if (argp == NULL) return; for (arg = argp; arg != NULL; arg = nxtarg) { nxtarg = arg->next; gsh_free(arg->name); gsh_free(arg->value); gsh_free(arg); } } /** * @brief Release storage for the expression parse tree */ static void free_expr_parse(struct expr_parse *snode) { struct expr_parse *nxtnode, *node = NULL; if (snode == NULL) return; for (node = snode; node != NULL; node = nxtnode) { nxtnode = node->next; gsh_free(node->name); free_expr_parse_arg(node->arg); gsh_free(node); } } /** * @brief Parse "token = string" or "token1 = string1, ..." * * @param arg_str [IN] pointer to first char to parse * @param argp [OUT] list of parsed expr_parse_arg * * @return pointer to first unmatching char or NULL on parse error */ static char *parse_args(char *arg_str, struct expr_parse_arg **argp) { char *sp, *name, *val; int saved_char; struct expr_parse_arg *arg = NULL; sp = skip_white(arg_str); if (*sp == '\0') return NULL; name = sp; /* name matches [A-Za-z_][A-Za-z0-9_]* */ if (!(isalpha(*sp) || *sp == '_')) return NULL; sp = end_of_token(sp); if (isspace(*sp)) *sp++ = '\0'; sp = skip_white(sp); if (*sp != '=') return NULL; *sp++ = '\0'; /* name = ... */ sp = skip_white(sp); if (*sp == '\0') return NULL; val = sp; sp = end_of_value(sp); if (*sp == '\0') return NULL; if (isspace(*sp)) { /* name = val */ *sp++ = '\0'; sp = skip_white(sp); } if (*sp == '\0') return NULL; saved_char = *sp; *sp = '\0'; arg = gsh_calloc(1, sizeof(struct expr_parse_arg)); arg->name = gsh_strdup(name); arg->value = gsh_strdup(val); *argp = arg; if (saved_char != ',') { *sp = saved_char; return sp; } sp++; /* name = val , ... */ return parse_args(sp, &arg->next); } /** * @brief Parse "token ( .... )" and "token ( .... ) . token ( ... )" * * @param str [IN] pointer to first char to be parsed * @param node [OUT] reference pointer to returned expr_parse list * * @return pointer to first char after parse or NULL on errors */ static char *parse_block(char *str, struct expr_parse **node) { char *sp, *name; struct expr_parse_arg *arg = NULL; struct expr_parse *new_node; sp = skip_white(str); if (*sp == '\0') return NULL; name = sp; /* name matches [A-Za-z_][A-Za-z0-9_]* */ if (!(isalpha(*sp) || *sp != '_')) return NULL; if (*sp == '_') sp++; sp = end_of_token(sp); if (isspace(*sp)) *sp++ = '\0'; sp = skip_white(sp); if (*sp != '(') return NULL; *sp++ = '\0'; /* name ( ... */ sp = parse_args(sp, &arg); if (sp == NULL) goto errout; sp = skip_white(sp); if (*sp == ')') { /* name ( ... ) */ new_node = gsh_calloc(1, sizeof(struct expr_parse)); new_node->name = gsh_strdup(name); new_node->arg = arg; *node = new_node; return sp + 1; } errout: free_expr_parse_arg(arg); return NULL; } /** * @brief Parse a treewalk expression * * A full expression describes the path down a parse tree with * each node described as "block_name ( qualifiers )". * Each node in the path is a series of block name and qualifiers * separated by '.'. * * The parse errors are detected as either ret val == NULL * or *retval != '\0', i.e. pointing to a garbage char * * @param expr [IN] pointer to the expression to be parsed * @param expr_node [OUT] pointer to expression parse tree * * @return pointer to first char past parse or NULL for erros. */ static char *parse_expr(char *expr, struct expr_parse **expr_node) { char *sp; struct expr_parse *node = NULL, *prev_node = NULL, *root_node = NULL; char *lexpr = alloca(strlen(expr) + 1); strcpy(lexpr, expr); sp = lexpr; while (sp != NULL && *sp != '\0') { sp = parse_block(sp, &node); /* block is name ( ... ) */ if (root_node == NULL) root_node = node; else prev_node->next = node; prev_node = node; if (sp == NULL) break; sp = skip_white(sp); if (*sp == '.') { sp++; /* boock '.' block ... */ } else if (*sp != '\0') { sp = NULL; break; } } *expr_node = root_node; if (sp != NULL) return expr + (sp - lexpr); else return NULL; } static inline bool match_one_term(char *value, struct config_node *node) { struct config_node *term_node; struct glist_head *ts; glist_for_each(ts, &node->u.nterm.sub_nodes) { term_node = glist_entry(ts, struct config_node, node); assert(term_node->type == TYPE_TERM); if (strcasecmp(value, term_node->u.term.varvalue) == 0) return true; } return false; } /** * @brief Select block based on the evaluation of the qualifier args * * use each qualifier to match. The token name is found in the block. * once found, the token value is compared. '*' matches anything. else * it is a case-insensitive string compare. * * @param blk [IN] pointer to config_node descibing the block * @param expr [IN] expr_parse node to match * * @return true if matched, else false */ static bool match_block(struct config_node *blk, struct expr_parse *expr) { struct glist_head *ns; struct config_node *sub_node; struct expr_parse_arg *arg; bool found = false; assert(blk->type == TYPE_BLOCK); for (arg = expr->arg; arg != NULL; arg = arg->next) { glist_for_each(ns, &blk->u.nterm.sub_nodes) { sub_node = glist_entry(ns, struct config_node, node); if (sub_node->type == TYPE_STMT && strcasecmp(arg->name, sub_node->u.nterm.name) == 0) { found = true; if (expr->next == NULL && strcasecmp(arg->value, "*") == 0) continue; if (!match_one_term(arg->value, sub_node)) return false; } } } return found; } /** * @brief Find nodes in parse tree using expression * * Lookup signature describes the nested block it is of the form: * block_name '(' param_name '=' param_value ')' ... * where block_name and param_name are alphanumerics and param_value is * and arbitrary token. * This can name a subblock (the ...) part by a '.' sub-block_name... * as in: * some_block(indexing_param = foo).the_subblock(its_index = baz) * * NOTE: This will return ENOENT not only if the the comparison * fails but also if the search cannot find the token. In other words, * the match succeeds only if there is a node in the parse tree and it * matches. * * @param config [IN] root of parse tree * @param expr_str [IN] expression description of block * @param node_list [OUT] pointer to store node list * @param err_type [OUT] error processing * * @return 0 on success, errno for errors */ int find_config_nodes(config_file_t config, char *expr_str, struct config_node_list **node_list, struct config_error_type *err_type) { struct config_root *tree = (struct config_root *)config; struct glist_head *ns; struct config_node *sub_node; struct config_node *top; struct expr_parse *expr, *expr_head = NULL; struct config_node_list *list = NULL, *list_tail = NULL; char *ep; int rc = EINVAL; bool found = false; if (tree->root.type != TYPE_ROOT) { config_proc_error(&tree->root, err_type, "Expected to start at parse tree root for (%s)", expr_str); goto out; } top = &tree->root; ep = parse_expr(expr_str, &expr_head); if (ep == NULL || *ep != '\0') goto out; expr = expr_head; *node_list = NULL; again: glist_for_each(ns, &top->u.nterm.sub_nodes) { #ifdef DS_ONLY_WAS /* recent changes to parsing may prevent this, * but retain code here for future reference. * -- WAS */ if (ns == NULL) { config_proc_error(top, err_type, "Missing sub_node for (%s)", expr_str); break; } #endif sub_node = glist_entry(ns, struct config_node, node); if (strcasecmp(expr->name, sub_node->u.nterm.name) == 0 && sub_node->type == TYPE_BLOCK && match_block(sub_node, expr)) { if (expr->next != NULL) { top = sub_node; expr = expr->next; goto again; } list = gsh_calloc(1, sizeof(struct config_node_list)); list->tree_node = sub_node; if (*node_list == NULL) *node_list = list; else list_tail->next = list; list_tail = list; found = true; } } if (found) rc = 0; else rc = ENOENT; out: free_expr_parse(expr_head); return rc; } /** * @brief Fill configuration structure from a parse tree node * * If param == NULL, there is no link_mem and the self_struct storage * is allocated and attached to its destination dynamically. * * @param tree_node [IN] A CONFIG_BLOCK node in the parse tree * @param conf_blk [IN] pointer to configuration description * @param param [IN] pointer to struct to fill or NULL * @param unique [IN] bool if true, more than one is an error * @param err_type [OUT] error type return * * @returns -1 on errors, 0 for success */ int load_config_from_node(void *tree_node, struct config_block *conf_blk, void *param, bool unique, struct config_error_type *err_type) { struct config_node *node = (struct config_node *)tree_node; char *blkname = conf_blk->blk_desc.name; if (node == NULL) { config_proc_error(NULL, err_type, "Missing tree_node for (%s)", blkname); err_type->missing = true; return -1; } if (node->type == TYPE_BLOCK) { if (strcasecmp(node->u.nterm.name, blkname) != 0) { config_proc_error(node, err_type, "Looking for block (%s), got (%s)", blkname, node->u.nterm.name); err_type->invalid = true; err_type->errors++; return -1; } } else { config_proc_error(node, err_type, "Unrecognized parse tree node type for block (%s)", blkname); err_type->invalid = true; err_type->errors++; return -1; } if (!proc_block(node, &conf_blk->blk_desc, param, err_type)) { config_proc_error(node, err_type, "Errors found in configuration block %s", blkname); return -1; } return 0; } /** * @brief Fill configuration structure from parse tree * * If param == NULL, there is no link_mem and the self_struct storage * is allocated and attached to its destination dynamically. * * @param config [IN] root of parse tree * @param conf_blk [IN] pointer to configuration description * @param param [IN] pointer to struct to fill or NULL * @param unique [IN] bool if true, more than one is an error * @param err_type [OUT] pointer to error type return * * @returns number of blocks found. -1 on errors, errors are in err_type */ int load_config_from_parse(config_file_t config, struct config_block *conf_blk, void *param, bool unique, struct config_error_type *err_type) { struct config_root *tree = (struct config_root *)config; struct config_node *node = NULL; struct glist_head *ns; char *blkname = conf_blk->blk_desc.name; int found = 0; int prev_errs = err_type->errors; void *blk_mem = NULL; if (tree == NULL) { config_proc_error(NULL, err_type, "Missing parse tree root for (%s)", blkname); err_type->missing = true; return -1; } if (tree->root.type != TYPE_ROOT) { config_proc_error(&tree->root, err_type, "Expected to start at parse tree root for (%s)", blkname); err_type->internal = true; return -1; } if (param != NULL) { blk_mem = conf_blk->blk_desc.u.blk.init(NULL, param); if (blk_mem == NULL) { config_proc_error(&tree->root, err_type, "Top level block init failed for (%s)", blkname); err_type->internal = true; return -1; } } glist_for_each(ns, &tree->root.u.nterm.sub_nodes) { node = glist_entry(ns, struct config_node, node); if (node->type == TYPE_BLOCK && strcasecmp(blkname, node->u.nterm.name) == 0) { if (found > 0 && (conf_blk->blk_desc.flags & CONFIG_UNIQUE)) { config_proc_error(node, err_type, "Only one %s block allowed", blkname); } else { if (!proc_block(node, &conf_blk->blk_desc, blk_mem, err_type)) config_proc_error(node, err_type, "Errors processing block (%s)", blkname); else found++; } } } if (found == 0) { /* Found nothing but we have to do the allocate and init * at least. Use a fake, not NULL link_mem */ blk_mem = param != NULL ? param : conf_blk->blk_desc.u.blk.init((void *)~0UL, NULL); assert(blk_mem != NULL); if (!do_block_init(&tree->root, conf_blk->blk_desc.u.blk.params, blk_mem, err_type)) { config_proc_error(&tree->root, err_type, "Could not initialize defaults for block %s", blkname); err_type->init = true; } } if (err_type->errors > prev_errs) { char *errstr = err_type_str(err_type); config_proc_error(node, err_type, "%d %s errors found block %s", err_type->errors - prev_errs, errstr != NULL ? errstr : "unknown", blkname); if (errstr != NULL) gsh_free(errstr); } return found; } nfs-ganesha-2.6.0/src/config_parsing/test_parse.c000066400000000000000000000016121324272410200220020ustar00rootroot00000000000000#include "config_parsing.h" #include "log.h" #include int main(int argc, char **argv) { SetDefaultLogging("TEST"); SetNamePgm("test_parse"); config_file_t config; char *fichier; char *errtxt; if ((argc > 1) && (argv[1])) { fichier = argv[1]; } else { LogTest("Usage %s ", argv[0]); exit(EINVAL); } /* Exemple de parsing */ config = config_ParseFile(fichier); LogTest("config_pointer = %p", config); if (config == NULL) { errtxt = config_GetErrorMsg(); LogTest("Error in parsing %s : %s", argv[1], errtxt); exit(EINVAL); } config_Print(stdout, config); /* free and reload the file */ config_Free(config); config = config_ParseFile(fichier); LogTest("config_pointer = %p", config); if (config == NULL) { LogTest("Parsing error for %s", argv[1], errtxt); exit(EINVAL); } config_Print(stdout, config); config_Free(config); exit(0); } nfs-ganesha-2.6.0/src/config_parsing/verif_syntax.c000066400000000000000000000013001324272410200223440ustar00rootroot00000000000000/** * @file verif_syntax.c * @brief Test the syntax of the configuration file. */ #include "config_parsing.h" #include "log.h" #include int main(int argc, char **argv) { SetDefaultLogging("TEST"); SetNamePgm("verif_syntax"); char *errtxt; char *fichier; config_file_t config; if ((argc > 1) && (argv[1])) { fichier = argv[1]; } else { LogTest("Usage %s ", argv[0]); exit(EINVAL); } /* test de la syntaxe du fichier */ config = config_ParseFile(fichier); if (config == NULL) { LogTest("Error parsing %s", argv[1]); exit(EINVAL); } else { LogTest("The syntax of the file %s is correct!", argv[1]); exit(0); } config_Free(config); return 0; } nfs-ganesha-2.6.0/src/config_samples/000077500000000000000000000000001324272410200174665ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/config_samples/README000066400000000000000000000112741324272410200203530ustar00rootroot00000000000000This directory contains some example config files for various FSALs. It also contains a few files to help document config options. config.txt - this file documents all the config options logging.txt - this file documents the LOG config in more detail export.txt - this file documents the export config in more detail The sample FSAL files: ceph.conf gpfs.conf vfs.conf xfs.conf The most readily used FSAL for experimentation with Ganesha given a recent kernel is FSAL_VFS using vfs.conf. Configuration File Processing ============================= A configuration file uses the following syntax rules. Processing errors are reported to the log or, in the case of an 'AddExport' DBus command, in the reply message. System administrators should always check the log or DBus reply whenever they make changes to the configuration. The processing will attempt to process the whole file and report all errors, some of which can be serious enough to prevent the server from starting. Like all compilers that make a best effort to keep processing, there are some errors that cannot be cleanly recovered from. Comments -------- The syntax provides for comments. A comment is any text following a '#' character to the end of the line. The one exception where it is ignored is when the '#' is enclosed in a quoted string. Examples are: # This whole line is a comment Protocol = TCP; # The rest of this line is a comment user_date_format = "%D #"; # The '#' in the quoted string is just another character. Including Other files --------------------- Additional files can be referenced in a configuration, using %include or %url directives. An included file is inserted into the configuration text in place of the %include or %url line. The configuration following the inclusion is resumed after the end of the included files. File inclusion can be to any depth. %include This line replaces the line with the contents of the file . Examples are: %include base.conf or %include "base.conf" The quotes are optional. %url %url rados://mypool/myobject or %url "rados://mypool/myobject" The quotes are optional. Symbols ------- Symbols are case insensitive words separated by white space or punctuation. Symbols used for block and parameter names must be an alphabetic optionally followed by more alphabetics, numbers, '-', '.', or '_'. Symbols used for option values follow the same syntax unless enclosed between quote characters. Examples are: LOG { Default_Log_Level = EVENT; } is equivalent to log{default_Log_LEVEL=event;} Numeric and Boolean Option Values --------------------------------- Numeric options can be defined in octal, decimal, or hexadecimal. The format follows ANSI C syntax. Examples are: mode = 0755; # This is octal 0755, 493 (decimal) maxwrite = 65535; maxread = 0xffff; Maxread and maxwrite are equal. Values can be input in any base although log messages will only display in one base. For example, the log will report the value for 'mode' in octal regardless of the base used to set it in the configuration. Numeric values can also be negated or logical NOT'd. Examples are: anonomousuid = -2; # this is a negative 2 mask = ~0xff; # this is equivalent to 0xffffff00 (for 32 bit integers) The operator can have white space between the '-' or '~' and the numeric value. Boolean types are also recognized. The values 'true', 'yes', and 'on' are TRUE and the values 'false', 'no', and 'off' are FALSE. Note that '1' and '0' are rejected. Booleans and integers different. Single "'" and double '"' quoted strings can contain any arbitrary, multiline sequence of characters. The C escaped non-printables \n, \t, and \c as well as any escaped printable, e.g. \", can be escaped within a double quoted string. A single quoted string contains all the characters between the first "'" and the next "'". If the string must contain a "'", use a double quoted string. All other values must start with an alphanumeric, '_', or '.'. Any subsequent characters can also contain a '-'. Any value that has characters that does not fit this pattern must be quoted. Blocks ------ Options are grouped into "blocks" of related parameters. A block is a name followed by parameters enclosed between '{' and '}'. Examples are: export { export_id = 17; fsal { name = VFS; } } Note that 'fsal' is a sub-block. Statements ---------- A statement within a block is a parameter name followed by a '=' that is followed by an option value or a comma separated list of option values. A statement is terminated by a ';'. Examples are: CanSetTime = true; Protocols = 3, 4, 9p; The first case is a simple boolean parameter. The second is a list. Note that the list can span lines. nfs-ganesha-2.6.0/src/config_samples/ceph.conf000066400000000000000000000002331324272410200212520ustar00rootroot00000000000000EXPORT { Export_ID=1; Path = /; Pseudo = /nfsv4/pseudofs/ceph/; Access_Type = RW; Protocols = 4; Transports = TCP; FSAL { Name = CEPH; } } nfs-ganesha-2.6.0/src/config_samples/config.txt000066400000000000000000000433701324272410200215030ustar00rootroot00000000000000The following config blocks exist: NFS_CORE_PARAM {} NFS_IP_NAME {} NFS_KRB5 {} NFSV4 {} EXPORT_DEFAULTS {} EXPORT {} EXPORT { CLIENT {} } EXPORT { FSAL {} } EXPORT { FSAL { FSAL {} } } EXPORT { FSAL { PNFS {} } } LOG {} LOG { COMPONENTS {} } LOG { FACILITY {} } LOG { FORMAT {} } 9P {} CACHEINODE {} CEPH {} GPFS {} MEM {} RGW {} VFS {} XFS {} PROXY {} PROXY { Remote_Server {} } RADOS_KV {} RADOS_URLS {} Notably the following FSALs do not have a global config block: PSEUDO, PROXY, NULL, GLUSTER NFS_CORE_PARAM {} ----------------- NFS_Port (uint16, range 0 to UINT16_MAX, default 2049) MNT_Port (uint16, range 0 to UINT16_MAX, default 0) NLM_Port (uint16, range 0 to UINT16_MAX, default 0) Rquota_Port (uint16, range 0 to UINT16_MAX, default 875) Bind_addr(IP4 addr, default 0.0.0.0) * This eventually needs to support IPv6 NFS_Program(uint32, range 1 to INT32_MAX, default 100003) MNT_Program(uint32, range 1 to INT32_MAX, default 100005) NLM_Program(uint32, range 1 to INT32_MAX, default 100021) Rquota_Program(uint32, range 1 to INT32_MAX, default 100011) Nb_Worker(uint32, range 1 to 1024*128, default 256) Drop_IO_Errors(bool, default false) Drop_Inval_Errors(bool, default false) Drop_Delay_Errors(bool, default false) DRC_Disabled(boo, default false) DRC_TCP_Npart(uint32, range 1 to 20, default 1) DRC_TCP_Size(uint32, range 1 to 32767, default 1024) DRC_TCP_Cachesz(uint32, range 1 to 255, default 127) DRC_TCP_Hiwat(uint32, range 1 to 256, default 64) DRC_TCP_Recycle_Npart(uint32, range 1 to 20, default 7) DRC_TCP_Recycle_Expire_S(uint32, range 0 to 60*60, default 600) DRC_TCP_Checksum(bool, default true) DRC_UDP_Npart(uint32, range 1 to 100, default 7) DRC_UDP_Size(uint32, range 512, to 32768, default 32768) DRC_UDP_Cachesz(uint32, range 1 to 2047, default 599) DRC_UDP_Hiwat(uint32, range 1 to 32768, default 16384) DRC_UDP_Checksum(bool, default true) RPC_Max_Connections(uint32, range 1 to 10000, default 1024) RPC_Idle_Timeout_S(uint32, range 0 to 60*60, default 300) MaxRPCSendBufferSize(uint32, range 1 to 1048576*9, default 1048576) MaxRPCRecvBufferSize(uint32, range 1 to 1048576*9, default 1048576) RPC_Ioq_ThrdMax(uint32, range 1 to 1024*128 default 200) RPC_GSS_Npart(uint32, range 1 to 1021, default 13) RPC_GSS_Max_Ctx(uint32, range 1 to 1048576, default 16384) RPC_GSS_Max_Gc(uint32, range 1 to 1048576, default 200) Blocked_Lock_Poller_Interval(int64, range 0 to 180, default 10) NFS_Protocols(list, valid values [3, 4], default 3,4) NSM_Use_Caller_Name(bool, default false) Clustered(bool, default true) Enable_NLM(bool, default true) Enable_RQUOTA(bool, default true) Enable_TCP_keepalive(bool, default true) TCP_KEEPCNT(UINT32, range 0 to 255, default 0 -> use system defaults) TCP_KEEPIDLE(UINT32, range 0 to 65535, default 0 -> use system defautls) TCP_KEEPINTVL(INT32, range 0 to 65535, default 0 -> use system defaults) Enable_NFS_Stats(bool, default true) Enable_Fast_Stats(bool, default false) Enable_FSAL_Stats(bool, default false) Short_File_Handle(bool, default false) Manage_Gids_Expiration(int64, range 0 to 7*24*60*60, default 30*60) Plugins_Dir(path, default "/usr/lib64/ganesha") heartbeat_freq(uint32, range 0 to 5000 default 1000) fsid_device(bool, default false) mount_path_pseudo(bool, default false) Dbus_Name_Prefix(string, default NULL) NFS_IP_NAME {} -------------- Index_Size(uint32, range 1 to 51, default 17) Expiration_Time(uint32, range 1 to 60*60*24, default 3600) NFS_KRB5 {} ----------- PrincipalName(string, default "nfs") KeytabPath(path, default "") CCacheDir(path, default "/var/run/ganesha") Active_krb5(bool, default true) NFSV4 {} -------- Graceless(bool, default false) Lease_Lifetime(uint32, range 0 to 120, default 60) Grace_Period(uint32, range 0 to 180, default 90) DomainName(string, default "localdomain") IdmapConf(path, default "/etc/idmapd.conf") UseGetpwnam(bool, default false if using idmap, true otherwise) Allow_Numeric_Owners(bool, default true) Only_Numeric_Owners(bool, default false) Delegations(bool, default false) RecoveryBackend(path, default "fs") Minor_Versions(enum list, values [0, 1, 2], default [0, 1, 2]) Slot_Table_Size(uint32, range 1 to 1024, default 64) EXPORT_DEFAULTS {} ------------------ These options are all "export permissions" options, and will be repeated in the EXPORT {} and EXPORT { CLIENT {} } blocks. These options will all be dynamically updateable. Access_Type(enum, values [None, RW, RO, MDONLY, MDONLY_RO], default None) Protocols(enum list, values [3, 4, NFS3, NFS4, V3, V4, NFSv3, NFSv4, 9P], default [3, 4]) Transports(enum list, values [UDP, TCP, RDMA], default [UDP, TCP]) Anonymous_uid(anonid, range INT32MIN to UINT32MAX, default -2) Anonymous_gid(anonid, range INT32MIN to UINT32MAX, default -2) SecType(enum list, values [none, sys, krb5, krb5i, krb5p], default [none, sys]) PrivilegedPort(bool, default false) Manage_Gids(bool, default false) Squash(enum, values [root, root_squash, rootsquash, rootid, root_id_squash, rootidsquash, all, all_squash, allsquash, all_anomnymous, allanonymous, no_root_squash, none, noidsquash], default root_squash) * Each line of defaults above are synonyms NFS_Commit(bool, default false) Delegations(enum, values [None, read, write, readwrite, r, w, rw], default None) Attr_Expiration_Time(int32, range -1 to INT32_MAX, default 60) EXPORT {} --------- * Take all the "export permissions" options from EXPORT_DEFAULTS. * The first 5 options are considered static from the perspective of dynamic update. Path(path, no default, must be supplied) Pseudo(path, no default) Tag(string, no default) Export_id(uint16, range 0 to UINT16_MAX, default 1) Filesystem_id(fsid, format is uint64.uint64, default 666.666) * Updating Filesystem_id would have an adverse impact on existing client mounts and it's not currently a type that can be atomically updated. * The following options may be dynamically updated MaxRead(uint64, range 512 to 64*1024*1024, default 64*1024*1024) MaxWrite(uint64, range 512 to 64*1024*1024, default 64*1024*1024) PrefRead(uint64, range 512 to 64*1024*1024, default 64*1024*1024) PrefWrite(uint64, range 512 to 64*1024*1024, default 64*1024*1024) PrefReaddir(uint64, range 512 to 64*1024*1024, default 16384) MaxOffsetWrite(uint64, range 512 to UINT64_MAX, default UINT64_MAX) MaxOffsetRead(uint64, range 512 to UINT64_MAX, default UINT64_MAX) DisableReaddirPlus(bool, default false) Trust_Readdir_Negative_Cache(bool, default false) * The following options may have limits on dynamic effect UseCookieVerifier(bool, default true) * Updating UseCookieVerifier while a readdir is in progress may result in unexpected behavior. Disable_ACL(bool, default false) * Disable_ACL is processed at create_export time currently which makes it effectively a static option. Attr_Expiration_Time(int32, range -1 to INT32_MAX, default 60) * Attr_Expiration_Time is evaluated when an MDCACHE entry is created, so the dynamic effect of this option may be constrained to new entries. EXPORT { CLIENT {} } --------------------- * Take all the "export permissions" options from EXPORT_DEFAULTS. * The client lists are dynamically updateable. Clients(client list, empty) * Client list entries can take on one of the following forms: * Match any client @name Netgroup name x.x.x.x/y IPv4 network address wildcarded If the string contains at least one ? or * character (and is not simply "*"), the string is used to pattern match host names. Note that [] may also be used, but the pattern MUST have at least one ? or * hostname Match a single client (match is by IP address, all addresses returned by getaddrinfo will match, the getaddrinfo call is made at config parsing time) IP address Match a single client EXPORT { FSAL {} } ------------------ Name(string, default "VFS") FSAL_CEPH: ---------- User_Id(string, no default) * User_Id: cephx userid used to open the MDS session. This string is what gets appended to "client.". If not set, the ceph client libs will sort this out based on ceph configuration. Secret_Access_Key(string, no default) * Secret_Access_Key: key to use for the session (if any). If not set, then it uses the normal search path for cephx keyring files to find a key. FSAL_GLUSTER: ------------- volume(string, no default, must be supplied) hostname(string, no default, must be supplied) volpath(path, default "/") glfs_log(path, default "/tmp/gfapi.log") up_poll_usec(uint64, range 1 to 60*1000*1000, default 10) * up_poll_usec: The time interval (in micro-seconds) between any two consecutive upcall polls. By default set to 10us. enable_upcall(bool, default true) transport(enum, values [tcp, rdma], default tcp) FSAL_VFS: --------- pnfs(bool, default false) fsid_type(enum, values [None, One64, Major64, Two64, uuid, Two32, Dev, Device], no default) EXPORT { FSAL { PNFS { } } } --------------------------- Stripe_Unit(uint32, range 1024 to 1024*1024, default 8192) pnfs_enabled(bool, default false) FSAL_NULL: ---------- EXPORT { FSAL { FSAL {} } } describes the stacked FSAL's parameters LOG {} ------ Default_log_level(token, values [NULL, FATAL, MAJ, CRIT, WARN, EVENT, INFO, DEBUG, MID_DEBUG, M_DBG, FULL_DEBUG, F_DBG], default EVENT) RPC_Debug_Flags(uint32, range 0 to UINT32_MAX, default 7) LOG { COMPONENTS {} } --------------------- These entries are of the form: COMPONENT = LEVEL; The components are: ALL, LOG, LOG_EMERG, MEMLEAKS, FSAL, NFSPROTO, NFS_V4, EXPORT, FILEHANDLE, DISPATCH, CACHE_INODE, CACHE_INODE_LRU, HASHTABLE, HASHTABLE_CACHE, DUPREQ, INIT, MAIN, IDMAPPER, NFS_READDIR, NFS_V4_LOCK, CONFIG, CLIENTID, SESSIONS, PNFS, RW_LOCK, NLM, RPC, NFS_CB, THREAD, NFS_V4_ACL, STATE, 9P, 9P_DISPATCH, FSAL_UP, DBUS Some synonyms are: FH = FILEHANDLE HT = HASHTABLE INODE_LRU = CACHE_INODE_LRU INODE = CACHE_INODE DISP = DISPATCH LEAKS = MEMLEAKS NFS3 = NFSPROTO NFS4 = NFS_V4 HT_CACHE = HASHTABLE_CACHE NFS_STARTUP = INIT NFS4_LOCK = NFS_V4_LOCK NFS4_ACL = NFS_V4_ACL 9P_DISP = 9P_DISPATCH The log levels are: NULL, FATAL, MAJ, CRIT, WARN, EVENT, INFO, DEBUG, MID_DEBUG, M_DBG, FULL_DEBUG, F_DBG], default EVENT LOG { FACILITY {} } ------------------- name(string, no default) destination(string, no default, must be supplied) max_level(token, values [NULL, FATAL, MAJ, CRIT, WARN, EVENT, INFO, DEBUG, MID_DEBUG, M_DBG, FULL_DEBUG, F_DBG], default FULL_DEBUG) headers(token, values [none, component, all], default all) enable(token, values [idle, active, default], default idle) LOG { FORMAT {} } ----------------- date_format(enum, values [ganesha, true, local, 8601, ISO-8601, ISO 8601, ISO, syslog, syslog_usec, false, none, user_defined], default ganesha) time_format(enum, values [ganesha, true, local, 8601, ISO-8601, ISO 8601, ISO, syslog, syslog_usec, false, none, user_defined], default ganesha) user_date_format(string, no default) user_time_format(string, no default) EPOCH(bool, default true) CLIENTIP(bool, default false) HOSTNAME(bool, default true) PROGNAME(bool, default true) PID(bool, default true) THREAD_NAME(bool, default true) FILE_NAME(bool, default true) LINE_NUM(bool, default true) FUNCTION_NAME(bool, default true) COMPONENT(bool, default true) LEVEL(bool, default true) CACHEINODE ---------- NParts(uint32, range 1 to 32633, default 7) Cache_Size(uint32, range 1 to UINT32_MAX, default 32633) Use_Getattr_Directory_Invalidation(bool, default false) Dir_Max_Deleted(uint32, range 1 to UINT32_MAX, default 65536) Dir_Max(uint32, range 1 to UINT32_MAX, default 65536) Dir_Chunk(uint32, range 0 to UINT32_MAX, default 128) Detached_Mult(uint32, range 1 to UINT32_MAX, default 1) Chunks_HWMark(uint32, range 1 to UINT32_MAX, default 100000) Entries_HWMark(uint32, range 1 to UINT32_MAX, default 100000) LRU_Run_Interval(uint32, range 1 to 24 * 3600, default 90) Cache_FDs(bool, default true) FD_Limit_Percent(uint32, range 0 to 100, default 99) FD_HWMark_Percent(uint32, range 0 to 100, default 90) FD_LWMark_Percent(uint32, range 0 to 100, default 50) Reaper_Work(uint32, range 1 to 2000, default 0) Reaper_Work_Per_Lane(uint32, range 1 to 2000, default 50) Biggest_Window(uint32, range 1 to 100, default 40) Required_Progress(uint32, range 1 to 50, default 5) Futility_Count(uint32, range 1 to 50, default 8) Retry_Readdir(bool, default false) 9P {} ----- _9P_TCP_Port(uint16, range 1 to UINT16_MAX, default 564) _9P_RDMA_Port(uint16, range 1 to UINT16_MAX, default 5640) _9P_TCP_Msize(uint32, range 1024 to UINT32_MAX, default 65536) _9P_RDMA_Msize(uint32, range 1024 to UINT32_MAX, default 1048576) _9P_RDMA_Backlog(uint16, range 1 to UINT16_MAX, default 10) _9P_RDMA_Inpool_size(uint16, range 1 to UINT16_MAX, default 64) _9P_RDMA_Outpool_Size(uint16, range 1 to UINT16_MAX, default 32) CEPH {} ------- Ceph_Conf(path, default "") umask(mode, range 0 to 0777, default 0) xattr_access_rights(mode, range 0 to 0777, default 0) GPFS {} ------- link_support(bool, default true) symlink_support(bool, default true) cansettime(bool, default true) umask(mode, range 0 to 0777, default 0) auth_xdev_export(bool, default false) xattr_access_rights(mode, range 0 to 0777, default 0400) Delegations(enum, values [None, read, write, readwrite, r, w, rw], default read) pnfs_file(bool, default false) fsal_trace(bool, default true) fsal_grace(bool, default false) MEM {} ------- Inode_Size(uint32, range 0 to 2097152, default 0) Up_Test_Interval(uint32, range 0 to UINT32_MAX, default 0) RGW {} ------- The following configuration variables customize the startup of the FSAL's radosgw instance. * ceph_conf -- optional full-path to the Ceph configuration file (equivalent to passing "-c /path/to/ceph.conf" to any Ceph binary * name -- optional instance name (equivalent to passing "--name client.rgw.foohost" to the radosgw binary); the value provided here should be the same as the section name (sans brackets) of the radosgw facility in the Ceph configuration file (which must exist) * cluster -- optional cluster name (equivalent to passing "--cluster foo" to any Ceph binary); use of a non-default value for cluster name is uncommon, but can be verified by examining the startup options of Ceph binaries * init_args -- additional argument strings which will be passed verbatim to the radosgw instance startup process as if they had been given on the radosgw command line; provided for customization in uncommon setups ceph_conf(path, default "") name(string, default "") cluster(string, default "") init_args(string, default "") VFS {} ------ link_support(bool, default true) symlink_support(bool, default true) cansettime(bool, default true) maxread(uint64, range 512 to 64*1024*1024, default 64*1024*1024) maxwrite(uint64, range 512 to 64*1024*1024, default 64*1024*1024) umask(mode, range 0 to 0777, default 0) auth_xdev_export(bool, default false) xattr_access_rights(mode, range 0 to 0777, default 0400) XFS {} ------ link_support(bool, default true) symlink_support(bool, default true) cansettime(bool, default true) maxread(uint64, range 512 to 64*1024*1024, default 64*1024*1024) maxwrite(uint64, range 512 to 64*1024*1024, default 64*1024*1024) umask(mode, range 0 to 0777, default 0) auth_xdev_export(bool, default false) xattr_access_rights(mode, range 0 to 0777, default 0400) PROXY {} -------- link_support(bool, default true) symlink_support(bool, default true) cansettime(bool, default true) /*MAX_READ_WRITE_SIZE == 1MB*/ /*FSAL_MAXIOSIZE = 64 MB*/ /*SEND_RECV_HEADER_SPACE == 512 Bytes*/ maxread(uint64, range 512 to FSAL_MAXIOSIZE - SEND_RECV_HEADER_SPACE, default MAX_READ_WRITE_SIZE) maxwrite(uint64, range 512 to FSAL_MAXIOSIZE - SEND_RECV_HEADER_SPACE, default MAX_READ_WRITE_SIZE) umask(mode, range 0 to 0777, default 0) auth_xdev_export(bool, default false) xattr_access_rights(mode, range 0 to 0777, default 0400) PROXY { Remote_Server {} } -------------------------- Retry_SleepTime(uint32, range 0 to 60, default 10) Srv_Addr(ipv4_addr default "127.0.0.1") NFS_Service(uint32, range 0 to UINT32_MAX, default 100003) /*NFS_SendSize must be greater than maxwrite+SEND_RECV_HEADER_SPACE .*/ /*NFS_RecvSize must be greater than maxread+SEND_RECV_HEADER_SPACE .*/ /*MAX_READ_WRITE_SIZE == 1 MB*/ /*SEND_RECV_HEADER_SPACE == 512 Bytes*/ /*FSAL_MAXIOSIZE = 64 MB*/ NFS_SendSize(uint32, range 512 + SEND_RECV_HEADER_SPACE to FSAL_MAXIOSIZE, default MAX_READ_WRITE_SIZE + SEND_RECV_HEADER_SPACE) NFS_RecvSize(uint32, range 512 + SEND_RECV_HEADER_SPACE to FSAL_MAXIOSIZE, default MAX_READ_WRITE_SIZE + SEND_RECV_HEADER_SPACE) NFS_Port(uint16, range 0 to UINT16_MAX, default 2049) Use_Privileged_Client_Port(bool, default true) RPC_Client_Timeout(uint32, range 1 to 60*4, default 60) Remote_PrincipalName(string, no default) KeytabPath(string, default "/etc/krb5.keytab") Credential_LifeTime(uint32, range 0 to 86400*2, default 86400) Sec_Type(enum, values [krb5, krb5i, krb5p], default krb5) Active_krb5(bool, default false) Enable_Handle_Mapping(bool, default false) HandleMap_DB_Dir(string, default "/var/ganesha/handlemap") HandleMap_Tmp_Dir(string, default "/var/ganesha/tmp") HandleMap_DB_Count(uint32, range 1 to 16, default 8) HandleMap_HashTable_Size(uint32, range 1 to 127, default 103) RADOS_KV {} -------- ceph_conf(string, no default) userid(path, no default) pool(string, no default) RADOS_URLS {} -------- ceph_conf(string, no default) userid(path, no default) nfs-ganesha-2.6.0/src/config_samples/ds.conf000066400000000000000000000005221324272410200207420ustar00rootroot00000000000000################################################### # # DS # # To function, all that is required is one DS # # Define the absolutely minimal DS # ################################################### DS { # Identifier (optional, but each DS must be unique) Number = 0; # default # Related FSAL (mandatory) FSAL { Name = GPFS; } } nfs-ganesha-2.6.0/src/config_samples/export.txt000066400000000000000000000210371324272410200215530ustar00rootroot00000000000000# Sample export config # # This sample is not intended to be used as is, rather it is an illustration of # some of the flexibility of EXPORT configuration. There are a couple simple # EXPORT configurations at the end that are more usable. # # Options documentation: # # Export permission options available in EXPORT_DEFAULTS, EXPORT, and CLIENT # blocks. If an option is not set in a more specific block, the next less # specific block will be considered, until finally the default is taken if # the option is not specified in any applicable block, following this order: # CLIENT, EXPORT, EXPORT_DEFAULTS, baked in default. # # Access_Type (NONE): RW, RO, MDONLY, MDONLY_RO, NONE # RW allows all opertations # RO allows only operations that do not modify the server # MDONLY does not allow READ or WRITE operations, but # allows any other operation. # MDONLY_RO does not allow READ, WRITE, or any operation # that modifies file attributes or directory # content # NONE allows no access at all # # Protocols (3,4) The Protocols allowed. NFSV3, NFSV4, and 9P may be # specified. 3, 4, V3, V4, NFS3, and NFS4 may also be # used. # # Transports (UDP, TCP) The transport protocols allowed (UDP, TCP, and RDMA may # be specified) # # Squash (Root_Squash) What kind of user id squashing is performed: # No_Root_Squash, NoIdSquash, None # No user id squashing is performed # RootId, Root_Id_Squash, RootIdSquash # uid 0 and gid 0 are squashed to the # Anonymous_Uid and Anonymous_Gid # gid 0 in alt_groups lists is also squashed # Root, Root_Squash, RootSquash # uid 0 and gid of any value are squashed to the # Anonymous_Uid and Anonymous_Gid # alt_groups lists is discarded # All, All_Squash, AllSquash, All_Anonymous, AllAnonymous # All users are squashed # # Anonymous_Uid (-2) If a user id is squashed, this is the uid used # Ranges from -2147483648 to 4294967295 # uid are traditionally uint32_t however, tradition # has long been to specify NFS anonynmous uid as -2 # so negative values are allowed # # Anonymous_Gid (-2) If a group id is squashed, this is the gid used # Ranges from -2147483648 to 4294967295 # gid are traditionally uint32_t however, tradition # has long been to specify NFS anonynmous gid as -2 # so negative values are allowed # # SecType (none, sys) The RPC security flavors allowed, none (AUTH_NONE), # sys (AUTH_SYS/AUTH_UNIX), krb5 (RPCSEC_GSS), # krb5i (RPCSEC_GSS), krb5p (RCSEC_GSS) # # PrivilegedPort (false) If this option is true, client connections # must originate from port < 1024. This is # tradition based on some operating systems # requiring a user to be a privileged user to # create a socket with a source port < 1024 # # Manage_Gids (false) If this option is true, the alt groups list in # AUTH_SYS credentials will be replaced by a server # lookup of the group list. This allows bypassing the # 16 group limit of AUTH_SYS. # # Delegations (None) The types of delegations that may be granted. (None, Read, Write, # ReadWrite, R, W, and RW may be specified). # EXPORT_DEFAULTS block: # # All export permission options are usable. # # WARNING: If Access_Type is specified, that access type will be granted to # all clients on any export for which there is not an applicable CLIENT # block that explicitly provides a different Access_Type or for which the # EXPORT block does not provide a different Access_Type. # # If you desire to set a default Access_Type for all allowed clients, you # may then want to specify Access_Type = None; in every EXPORT block. EXPORT_DEFAULTS { SecType = sys, krb5, krb5i, krb5p; # Restrict all exports to NFS v4 unless otherwise specified Protocols = 4; } # EXPORT block # # All export permissions options are available, as well as the following: # # Export_id (required) An identifier for the export, must be unique and # betweem 0 and 65535. If Export_Id 0 is specified, Pseudo # must be the root path (/). # # Path (required) The directory in the exported file system this export # is rooted on (may be ignored for some FSALs). It need # not be unique if Pseudo and/or Tag are specified. # # Pseudo (required v4) This option specifies the position in the Pseudo FS # this export occupies if this is an NFS v4 export. It # must be unique. By using different Pseudo options, # the same Path may be exported multiple times. # # Tag (no default) This option allows an alternative access for NFS v3 # mounts. The option MUST not have a leading /. Clients # may not mount subdirectories (i.e. if Tag = foo, the # client may not mount foo/baz). By using different # Tag options, the same Path may be exported multiple # times. # # MaxRead (4194304) The maximum read size on this export # MaxWrite (4194304) The maximum write size on this export # PrefRead (4194304) The preferred read size on this export # PrefWrite (4194304) The preferred write size on this export # PrefReaddir (16384) The preferred readdir size on this export # These 5 options have the same range of values from # 512 to 9 megabytes. # # MaxOffsetWrite (18446744073709551615) Maximum file offset that may be written # MaxOffsetRead (18446744073709551615) Maximum file offset that may be read # These options may be used to restrict # the offsets within files. # # CLIENT (optional) See the CLIENT block below # # FSAL (required) See the FSAL block below EXPORT { Export_Id = 1; Path = /export/exp1; Pseudo = /export/exp1; Tag = exp1; # Override the default set in EXPORT_DEFAULTS Protocols = 3,4; MaxRead = 65536; MaxWrite = 65536; PrefRead = 65536; PrefWrite = 65536; # All clients for which there is no CLIENT block that specifies a # different Access_Type will have RW access (this would be an unusual # specification in the real world since barring a firewall, this # export is world readable and writeable). Access_Type = RW; # FSAL block # # This is required to indicate which Ganesha File System Abstraction # Layer (FSAL) will be used for this export. # # The only option available for all FSALs is: # # Name (required) The name of the FSAL # # Some FSALs have additional options, see individual FSAL documentation. FSAL { Name = VFS; } # CLIENT blocks # # An export may optionally have one or more CLIENT blocks. These blocks # specify export options for a restricted set of clients. The export # permission options specified in the EXPORT block will apply to any # client for which there is no applicable CLIENT block. # # All export permissions options are available, as well as the # following: # # Clients (required) The list of clients these export permissions # apply to. Clients may be specified by hostname, # ip address, netgroup, CIDR network address, # host name wild card, or simply "*" to apply to # all clients. CLIENT { Clients = 192.168.0.10, 192.168.1.0/8; Squash = None; } CLIENT { # Note the following specification is a larger network than # the first block, however, the first applicable CLIENT block # is used. Clients = 192.168.0.0/16; Squash = All; Access_Type = RO; } CLIENT { # This block is actually meaningless since 192.168.0.22 will # match the network address in the second CLIENT block. Clients = 192.168.0.22; Squash = None; Access_Type = RW; } } # Here is a simple sample EXPORT that should be used without an EXPORT_DEFAULTS # block. It takes advantage of the fact that whatever export permissions are # in the EXPORT block are applied to all clients for which there is no # matching CLIENT block. EXPORT { Export_Id = 2; Path = /export; Pseudo = /export; Access_Type = RW; Squash = None; FSAL { Name = VFS; } } # Here is an example with a simple CLIENT block EXPORT { Export_Id = 3; Path = /export2; Pseudo = /export2; FSAL { Name = VFS; } CLIENT { Clients = your, list, of, clients; Access_Type = RW; Squash = None; } } # Finally here is an example of how you can specify options for the Pseudo FS. # Note that even without specifying the Pseudo Root EXPORT, EXPORT_DEFAULTS will # still apply to it (except for Access_Type, Protocols, Transports, and Squash, # since those are all "set" options). EXPORT { Export_Id - 0; Path = /; Pseudo = /; CLIENT { Clients = 192.168.0.0/16; Access_Type = MDONLY_RO; SecType=sys,krb5,krb5i,krb5p; } } # The Automatically Generated Pseudo Root is effectively: EXPORT { Export_Id - 0; Path = /; Pseudo = /; Squash = None; Protcols = NFSV4; Transports = TCP; Access_Type = MDONLY_RO; Filesystem_Id = 152.152; MaxWrite = 67108864; MaxRead = 67108864; PrefWrite = 67108864; PrefRead = 67108864; PrefReaddir = 67108864; } nfs-ganesha-2.6.0/src/config_samples/ganesha.conf.example000066400000000000000000000044511324272410200234010ustar00rootroot00000000000000################################################### # # Ganesha Config Example # # This is a commented example configuration file for Ganesha. It is not # complete, but only has some common configuration options. See the man pages # for complete documentation. # ################################################### ## These are core parameters that affect Ganesha as a whole. #NFS_CORE_PARAM { ## Allow NFSv3 to mount paths with the Pseudo path, the same as NFSv4, ## instead of using the physical paths. #mount_path_pseudo = true; ## Configure the protocols that Ganesha will listen for. This is a hard ## limit, as this list determines which sockets are opened. This list ## can be restricted per export, but cannot be expanded. #Protocols = 3,4,9P; #} ## These are defaults for exports. They can be overridden per-export. #EXPORT_DEFAULTS { ## Access type for clients. Default is None, so some access must be ## given either here or in the export itself. #Access_Type = RW; #} ## Configure settings for the object handle cache #CACHEINODE { ## The point at which object cache entries will start being reused. #Entries_HWMark = 100000; #} ## Configure an export for some file tree #EXPORT #{ ## Export Id (mandatory, each EXPORT must have a unique Export_Id) #Export_Id = 12345; ## Exported path (mandatory) #Path = /nonexistant; ## Pseudo Path (required for NFSv4 or if mount_path_pseudo = true) #Pseudo = /nonexistant; ## Restrict the protocols that may use this export. This cannot allow ## access that is denied in NFS_CORE_PARAM. #Protocols = 3,4; ## Access type for clients. Default is None, so some access must be ## given. It can be here, in the EXPORT_DEFAULTS, or in a CLIENT block #Access_Type = RW; ## Whether to squash various users. #Squash = root_squash; ## Allowed security types for this export #Sectype = sys,krb5,krb5i,krb5p; ## Exporting FSAL #FSAL { #Name = VFS; #} #} ## Configure logging. Default is to log to Syslog. Basic logging can also be ## configured from the command line #LOG { ## Default log level for all components #Default_Log_Level = WARN; ## Configure per-component log levels. #Components { #FSAL = INFO; #NFS4 = EVENT; #} ## Where to log #Facility { #name = FILE; #destination = "/var/log/ganesha.log"; #enable = active; #} #} nfs-ganesha-2.6.0/src/config_samples/gluster.conf000066400000000000000000000014661324272410200220310ustar00rootroot00000000000000################################################### # # EXPORT # # To function, all that is required is an EXPORT # # Define the absolute minimal export # ################################################### EXPORT { # Export Id (mandatory, each EXPORT must have a unique Export_Id) Export_Id = 77; # Exported path (mandatory) Path = "/testvol"; # Pseudo Path (required for NFS v4) Pseudo = "/testvol"; # Required for access (default is None) # Could use CLIENT blocks instead Access_Type = RW; # Allow root access Squash = No_Root_Squash; # Security flavor supported SecType = "sys"; # Exporting FSAL FSAL { Name = "GLUSTER"; Hostname = localhost; Volume = "testvol"; Up_poll_usec = 10; # Upcall poll interval in microseconds Transport = tcp; # tcp or rdma } } nfs-ganesha-2.6.0/src/config_samples/gpfs.conf000066400000000000000000000021011324272410200212660ustar00rootroot00000000000000################################################### # # EXPORT_DEFAULTS Parameter # ################################################### EXPORT_DEFAULTS { # GPFS has an invalidate upcall that allows it # to trust attributes longer. Attr_Expiration_Time = 600; } ################################################### # # NFS_Core_Param # ################################################### NFS_Core_Param { # GPFS is clustered Clustered = TRUE; } ################################################### # # NFSv4 Specific configuration stuff # ################################################### NFSv4 { Lease_Lifetime=90; } ################################################### # # FSAL Specific config to enable READ delegation support. # ################################################### GPFS { delegations = R; } ################################################### # Export entries ################################################### # First export entry EXPORT { Export_Id = 77; Path = /ibm/gpfs0; Pseudo = /ibm/gpfs0; Access_Type=RW; FSAL { Name = GPFS; } } nfs-ganesha-2.6.0/src/config_samples/gpfs.ganesha.exports.conf000066400000000000000000000000001324272410200243720ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/config_samples/gpfs.ganesha.log.conf000066400000000000000000000014661324272410200234700ustar00rootroot00000000000000LOG { Default_log_level = EVENT; Facility { name = FILE; destination = /var/log/ganesha/nfs-ganesha.log; max_level = FULL_DEBUG; headers = all; enable = idle; } Format { date_format = ISO-8601; time_format = ISO-8601; EPOCH = TRUE; CLIENTIP = FALSE; HOSTNAME = TRUE; PID = TRUE; THREAD_NAME = TRUE; FILE_NAME = FALSE; LINE_NUM = FALSE; FUNCTION_NAME = TRUE; COMPONENT = TRUE; LEVEL = TRUE; } Components { # Eg. COMPONENT = LEVEL; ALL = EVENT; } } nfs-ganesha-2.6.0/src/config_samples/gpfs.ganesha.main.conf000066400000000000000000000016401324272410200236250ustar00rootroot00000000000000GPFS { fsal_trace = TRUE; fsal_grace = FALSE; } NFS_Core_Param { Nb_Worker = 256; Clustered = TRUE; NFS_Protocols= 3,4; NFS_Port = 2049; MNT_Port = 0; NLM_Port = 0; RQUOTA_Port = 0; RPC_Max_Connections = 10000; heartbeat_freq = 0; short_file_handle = FALSE; } NFSv4 { Lease_Lifetime = 60; DomainName = virtual1.com; Delegations = FALSE; } CacheInode { Entries_HWMark = 1500000; LRU_Run_Interval = 90; FD_HWMark_Percent = 60; FD_LWMark_Percent = 20; FD_Limit_Percent = 90; } Export_Defaults { Access_Type = none; Protocols = 3,4; Transports = TCP; Anonymous_uid = -2; Anonymous_gid = -2; SecType = sys; PrivilegedPort = FALSE; Manage_Gids = FALSE; Squash = root_squash; NFS_Commit = FALSE; } nfs-ganesha-2.6.0/src/config_samples/gpfs.ganesha.nfsd.conf000066400000000000000000000002111324272410200236240ustar00rootroot00000000000000%include /etc/ganesha/gpfs.ganesha.main.conf %include /etc/ganesha/gpfs.ganesha.log.conf %include /etc/ganesha/gpfs.ganesha.exports.conf nfs-ganesha-2.6.0/src/config_samples/logging.txt000066400000000000000000000103031324272410200216520ustar00rootroot00000000000000## # # This block controls the logging system # ## LOG { # The components block contains one or more logging components # and the setting to be used. components { # The ALL component is special. When set it defines the level # for all components and overrides any other setting in this block. # For convenience, many component names can be specified using two # different values. One is the #define constant without the # COMPONENT_ prefix, for example, HASHTABLE_CACHE. The other is # the string that shows up in the actual log with blank replaced with # underscore, for example HT_CACHE. # # Log levels are: # # NULL, FATAL, MAJ, CRIT, WARN, EVENT, INFO, DEBUG, MID_DEBUG, # FULL_DEBUG # # ALL = FULL_DEBUG; # this will likely kill performance # ALL = EVENT; # this is the default # INIT = INFO; # RPC = FULL_DEBUG; # DBUS = DEBUG; } # Formatting of Log Messages # Each parameter is a field in the log message Format { # date formats are: ganesha, local, ISO-8601, syslog, syslog_usec # user_defined, none # ganesha time format %d/%m/%Y (DD/MM/YYYY) # compatible with older Ganesha (pre 1.5) # ganesha date format %H:%M:%S (HH:MM:SS) # compatible with older Ganesha (pre 1.5) # local date format is the local format as would show using %c # in format string to strftime. # local time format %X (preferred local format) # ISO-8601 date format %F (YYYY-MM-DD) # ISO-8601 time format %X (preferred local format) # syslog date format %b %e (Mon MM) # syslog time format %X (preferred local format) # syslog_usec date format %F (YYYY-MM-DD) # syslog_usec time format T%H:%M:%S.%%06u%z (THH:MM:SS.UUUUUU+hhmm) # date and time are separated by "T" # instead of " ", +hhmm is the current UTC # offset (can be - of course) # none date format no date # none time format no time # user_defined date format specify a strftime format string # user_defined time format specify a strftime format string # you may specify the entire string for # either time or date, and set the # other to none # date and time default to ganesha. # # date_format = ganesha; # time_format = ganesha; # # If user_defined is set for date or time, these fields take a # strftime type format. These are examples. The default is empty. # Note that you will need single or double quotes because it could # have spaces and, in this particular example, it has a leading '%' # which is not part of a regular token which must have a leading alphabetic. # # user_date_format = "%D"; # user_time_format = "%T"; # the following, if true, adds that field to the message # these are the defaults # HOSTNAME=true; # PROGNAME=true; # PID=true; # EPOCH=true; # CLIENTIP=false; # THREAD_NAME=true; # FUNCTION_NAME=true; # COMPONENT=true; # LEVEL=true; # FILE_NAME= true; # LINE_NUM= true; } # Facilities # these can be added or modified. # Three are defined by default, STDERR, STDOUT, and SYSLOG # if the '-L' option is used on the command line, a FILE # facility is created at startup with the option argument as # the output file. # facility { # an arbitrary name. If it matches an existing, the other # parameters are used to modify it. # name = FILE; # Any higher level than this is not reported to this facility # max_level = FULL_DEBUG; # This can be stdout, stderr, or a file path # destination = /var/log/ganesha/nfs-ganesha.log; # facility state. Can be idle, active, or default # An idle facility just sits there # An active facility will accept log messages # The default facility is special in that it cannot # be removed or made idle. You can switch another in its # place however # enable = default; # } # The wired default level is EVENT. You change it here. # The default is set for any components not defined in the # components block. default_log_level = EVENT; } nfs-ganesha-2.6.0/src/config_samples/logrotate_fsal_gluster000066400000000000000000000001701324272410200241610ustar00rootroot00000000000000/var/log/ganesha/ganesha-gfapi.log { weekly rotate 52 copytruncate dateext compress missingok } nfs-ganesha-2.6.0/src/config_samples/logrotate_ganesha000066400000000000000000000001621324272410200230760ustar00rootroot00000000000000/var/log/ganesha/ganesha.log { weekly rotate 52 copytruncate dateext compress missingok } nfs-ganesha-2.6.0/src/config_samples/mem.conf000066400000000000000000000004631324272410200211160ustar00rootroot00000000000000EXPORT { Export_ID=1234; Path = "/any/path/you/want"; Pseudo = "/some/pseudo/path"; Access_Type = RW; FSAL { Name = MEM; } } MEM { # This is the size needed to pass pyNFS. Default is 0 Inode_Size = 1114112; # This creates a thread that exercises UP calls UP_Test_Interval = 20; } nfs-ganesha-2.6.0/src/config_samples/rgw.conf000066400000000000000000000006571324272410200211440ustar00rootroot00000000000000EXPORT { Export_ID=1; Path = "/"; Pseudo = "/"; Access_Type = RW; Protocols = 4; Transports = TCP; FSAL { Name = RGW; User_Id = "testuser"; Access_Key_Id =""; Secret_Access_Key = ""; } } RGW { ceph_conf = "//ceph.conf"; # for vstart cluster, name = "client.admin" name = "client.rgw.foohost"; cluster = "ceph"; # init_args = "-d --debug-rgw=16"; } nfs-ganesha-2.6.0/src/config_samples/rgw_bucket.conf000066400000000000000000000007021324272410200224700ustar00rootroot00000000000000EXPORT { Export_ID=1; Path = "testbucket"; Pseudo = "/testbucket"; Access_Type = RW; Protocols = 4; Transports = TCP; FSAL { Name = RGW; User_Id = "testuser"; Access_Key_Id =""; Secret_Access_Key = ""; } } RGW { ceph_conf = "//ceph.conf"; # for vstart cluster, name = "client.admin" name = "client.rgw.foohost"; cluster = "ceph"; # init_args = "-d --debug-rgw=16"; } nfs-ganesha-2.6.0/src/config_samples/vfs.conf000066400000000000000000000010551324272410200211340ustar00rootroot00000000000000################################################### # # EXPORT # # To function, all that is required is an EXPORT # # Define the absolute minimal export # ################################################### EXPORT { # Export Id (mandatory, each EXPORT must have a unique Export_Id) Export_Id = 77; # Exported path (mandatory) Path = /nonexistant; # Pseudo Path (required for NFS v4) Pseudo = /nonexistant; # Required for access (default is None) # Could use CLIENT blocks instead Access_Type = RW; # Exporting FSAL FSAL { Name = VFS; } } nfs-ganesha-2.6.0/src/config_samples/xfs.conf000066400000000000000000000010361324272410200211350ustar00rootroot00000000000000################################################### # # EXPORT # # To function, all that is required is an EXPORT # # Define the absolute minimal export # ################################################### EXPORT { # Export Id (mandatory, each EXPORT must have a unique Export_Id) Export_Id = 77; # Exported path (mandatory) Path = /xfs; # Pseudo Path (required for NFS v4) Pseudo = /xfs; # Required for access (default is None) # Could use CLIENT blocks instead Access_Type = RW; # Exporting FSAL FSAL { Name = XFS; } } nfs-ganesha-2.6.0/src/dbus/000077500000000000000000000000001324272410200154325ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/dbus/CMakeLists.txt000066400000000000000000000006451324272410200201770ustar00rootroot00000000000000include_directories( ${DBUS_INCLUDE_DIRS} ) ########### next target ############### if(FREEBSD) set (FBSD_SUBR "${PROJECT_SOURCE_DIR}/os/freebsd/memstream.c") endif(FREEBSD) SET(gshdbus_STAT_SRCS dbus_server.c properties_handler.c signal_handler.c dbus_heartbeat.c ${FBSD_SUBR} ) add_library(gshdbus STATIC ${gshdbus_STAT_SRCS}) add_sanitizers(gshdbus) ########### install files ############### nfs-ganesha-2.6.0/src/dbus/dbus_heartbeat.c000066400000000000000000000036671324272410200205660ustar00rootroot00000000000000/* * Copyright (C) IBM Inc., 2014 * Author: Jeremy Bongio * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /* * @file dbus_heartbeat.c * @author Jeremy Bongio * @brief DBUS Heartbeat */ #include "config.h" #include #include #include #include #include #include #include #include "fsal.h" #include "nfs_core.h" #include "log.h" #include "avltree.h" #include "gsh_types.h" #include "gsh_dbus.h" #include "abstract_atomic.h" #include "gsh_intrinsic.h" int dbus_heartbeat_cb(void *arg) { SetNameFunction("dbus_heartbeat"); int err = 0; int rc = BCAST_STATUS_OK; dbus_bool_t ishealthy = nfs_health(); if (ishealthy) { /* send the heartbeat pulse */ err = gsh_dbus_broadcast(DBUS_PATH HEARTBEAT_NAME, DBUS_ADMIN_IFACE, HEARTBEAT_NAME, DBUS_TYPE_BOOLEAN, &ishealthy, DBUS_TYPE_INVALID); if (err) { LogCrit(COMPONENT_DBUS, "heartbeat broadcast failed. err:%d", err); rc = BCAST_STATUS_WARN; } } return rc; } void init_heartbeat(void) { add_dbus_broadcast(&dbus_heartbeat_cb, NULL, nfs_param.core_param.heartbeat_freq*NS_PER_MSEC, BCAST_FOREVER); } nfs-ganesha-2.6.0/src/dbus/dbus_priv.h000066400000000000000000000007361324272410200176060ustar00rootroot00000000000000#ifndef DBUS_PRIV_H #define DBUS_PRIV_H bool dbus_proc_property(const char *method, DBusMessage *msg, DBusMessage *reply, DBusError *error, struct gsh_dbus_interface **interfaces); int dbus_append_signal_string(DBusMessageIter *args, void *sig_string); int dbus_send_signal(DBusConnection *conn, char *obj_name, char *int_name, char *sig_name, int (*payload) (DBusMessageIter *signal, void *args), void *sig_args); #endif /* DBUS_PRIV_H */ nfs-ganesha-2.6.0/src/dbus/dbus_server.c000066400000000000000000000602731324272410200201310ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) 2010, The Linux Box Corporation * Contributor : Matt Benjamin * * Some portions Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ #include "config.h" #include #include #include #include #include #include #include "gsh_list.h" #include #include #include #include #include #include "fsal.h" #include "nfs_core.h" #include "log.h" #include "nfs_rpc_callback.h" #include "gsh_dbus.h" #include #include "dbus_priv.h" #include "nfs_init.h" /** * * \file dbus_server.c * \author Matt Benjamin * \brief Low-level DBUS message server and callout framework. * * \section DESCRIPTION * * This module implements a (somewhat) generic service handler, initially to * support a planned callback simulator. Developers are encouraged to expand * on this interface to support other use cases. * * This module should be initialized before any service provider module * calls gsh_dbus_register_msg(); * */ #define GSH_DBUS_NONE 0x0000 #define GSH_DBUS_SHUTDOWN 0x0001 #define GSH_DBUS_SLEEPING 0x0002 static const char dbus_name[] = "org.ganesha.nfsd"; /* * List and mutex used by the dbus broadcast service */ struct glist_head dbus_broadcast_list; pthread_mutex_t dbus_bcast_lock; struct ganesha_dbus_handler { char *name; struct avltree_node node_k; DBusObjectPathVTable vtable; }; struct _dbus_thread_state { int initialized; pthread_t thread_id; wait_entry_t we; DBusConnection *dbus_conn; DBusError dbus_err; uint32_t dbus_serial; struct avltree callouts; uint32_t flags; }; static struct _dbus_thread_state thread_state; static inline int dbus_callout_cmpf(const struct avltree_node *lhs, const struct avltree_node *rhs) { struct ganesha_dbus_handler *lk, *rk; lk = avltree_container_of(lhs, struct ganesha_dbus_handler, node_k); rk = avltree_container_of(rhs, struct ganesha_dbus_handler, node_k); return strcmp(lk->name, rk->name); } static inline bool is_valid_dbus_prefix(const char *prefix) { if (prefix == NULL || *prefix == '\0') return false; if (!isalpha(*prefix) && *prefix != '_') return false; prefix++; while (*prefix != '\0') { if (!isalnum(*prefix) && *prefix != '_') return false; prefix++; } return true; } static inline void dbus_name_with_prefix(char *prefixed_dbus_name, const char *default_name, const char *prefix) { int prefix_len, total_len; if (!is_valid_dbus_prefix(prefix)) { if (prefix != NULL && prefix[0] != '\0') { LogEvent(COMPONENT_DBUS, "Dbus name prefix is invalid. Ignoring the prefix."); } strcpy(prefixed_dbus_name, default_name); return; } prefix_len = strlen(prefix); /* Additional length for separator (.) and null character */ total_len = strlen(default_name) + prefix_len + 2; if (total_len > NAME_MAX) { LogEvent(COMPONENT_DBUS, "Dbus name prefix too long. Ignoring the prefix."); strcpy(prefixed_dbus_name, default_name); return; } strcpy(prefixed_dbus_name, prefix); prefixed_dbus_name[prefix_len] = '.'; strcpy(prefixed_dbus_name + prefix_len + 1, default_name); } /* * @brief compare routine used to sort broadcast items * * Function used to sort the broadcast items according * to time expirey. Conforms to the glist_compare * function signature so it can be used with glist_insert_sorted * * @param a: Pointer to the glist of a dbus_bcast_item * @param b: Pointer to the glist of another dbus_bcast_item * to compare the first to */ int dbus_bcast_item_compare(struct glist_head *a, struct glist_head *b) { struct dbus_bcast_item *bcast_item_a; struct dbus_bcast_item *bcast_item_b; bcast_item_a = glist_entry(a, struct dbus_bcast_item, dbus_bcast_q); bcast_item_b = glist_entry(b, struct dbus_bcast_item, dbus_bcast_q); return gsh_time_cmp(&bcast_item_a->next_bcast_time, &bcast_item_b->next_bcast_time); } /* * @brief del_dbus_broadcast: Delete a broadcast item from the * broadcast service * * Function to be called by any thread wanting to remove a broadcast item * from the dbus broadcast service * * @param to_remove: The pointer to the dbus_bcast_item * returned from add_dbus_broadcast */ void del_dbus_broadcast(struct dbus_bcast_item *to_remove) { PTHREAD_MUTEX_lock(&dbus_bcast_lock); glist_del(&to_remove->dbus_bcast_q); PTHREAD_MUTEX_unlock(&dbus_bcast_lock); gsh_free(to_remove); } /* * @brief add_dbus_broadcast: Add a callback to the broadcast service * * Function to be called by any thread that wants to add a callback * to the dbus broadcast service * * @param bcast_callback: Function pointer to be called * @param arg: Arg that will be passed to the callback * @param bcast_interval: The time in nsec between calls * @param count: The number of times to invoke the callback * Pass BCAST_FOREVER to call indefinitely * * @return: The pointer to the dbus_bcast_item created */ struct dbus_bcast_item *add_dbus_broadcast( dbus_bcast_callback bcast_callback, void *bcast_arg, uint32_t bcast_interval, int count) { struct dbus_bcast_item *new_bcast = NULL; new_bcast = (struct dbus_bcast_item *) gsh_malloc(sizeof(struct dbus_bcast_item)); now(&new_bcast->next_bcast_time); new_bcast->bcast_interval = bcast_interval; new_bcast->count = count; new_bcast->bcast_arg = bcast_arg; new_bcast->bcast_callback = bcast_callback; PTHREAD_MUTEX_lock(&dbus_bcast_lock); glist_insert_sorted(&dbus_broadcast_list, &(new_bcast->dbus_bcast_q), &dbus_bcast_item_compare); PTHREAD_MUTEX_unlock(&dbus_bcast_lock); return new_bcast; } /* * @brief init_dbus_broadcast: Initializes broadcast list and mutex */ void init_dbus_broadcast(void) { PTHREAD_MUTEX_init(&dbus_bcast_lock, NULL); glist_init(&dbus_broadcast_list); if (nfs_param.core_param.heartbeat_freq > 0) init_heartbeat(); } void gsh_dbus_pkginit(void) { int code = 0; char prefixed_dbus_name[NAME_MAX]; LogDebug(COMPONENT_DBUS, "init"); avltree_init(&thread_state.callouts, dbus_callout_cmpf, 0 /* must be 0 */); dbus_error_init(&thread_state.dbus_err); /* sigh */ thread_state.dbus_conn = dbus_bus_get(DBUS_BUS_SYSTEM, &thread_state.dbus_err); if (dbus_error_is_set(&thread_state.dbus_err)) { LogCrit(COMPONENT_DBUS, "dbus_bus_get failed (%s)", thread_state.dbus_err.message); dbus_error_free(&thread_state.dbus_err); goto out; } dbus_name_with_prefix(prefixed_dbus_name, dbus_name, nfs_param.core_param.dbus_name_prefix); code = dbus_bus_request_name(thread_state.dbus_conn, prefixed_dbus_name, DBUS_NAME_FLAG_REPLACE_EXISTING, &thread_state.dbus_err); if (dbus_error_is_set(&thread_state.dbus_err)) { LogCrit(COMPONENT_DBUS, "server bus reg failed (%s, %s)", prefixed_dbus_name, thread_state.dbus_err.message); dbus_error_free(&thread_state.dbus_err); if (!code) code = EINVAL; goto out; } if (code != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { LogCrit(COMPONENT_DBUS, "server failed becoming primary bus owner (%s, %d)", prefixed_dbus_name, code); goto out; } init_dbus_broadcast(); thread_state.initialized = true; out: return; } #define INTROSPECT_HEAD \ "\n" \ "\n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" #define INTROSPECT_TAIL \ "\n" #define PROPERTIES_INTERFACE_HEAD \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" #define PROPERTIES_INTERFACE_SIGNAL \ " \n" \ " \n" \ " \n" \ " \n" \ " \n" #define PROPERTIES_INTERFACE_TAIL \ " \n" #define INTROSPECT_INTERFACE_HEAD \ " \n" #define INTROSPECT_INTERFACE_TAIL \ " \n" #define INTROSPECT_METHOD_HEAD \ " \n" #define INTROSPECT_METHOD_TAIL \ " \n" #define INTROSPECT_METHOD_ARG \ " \n" #define INTROSPECT_PROPERTY \ " \n" #define INTROSPECT_SIGNAL_HEAD \ " \n" #define INTROSPECT_SIGNAL_TAIL \ " \n" #define INTROSPECT_SIGNAL_ARG \ " \n" static const char * const prop_access[] = { [DBUS_PROP_READ] = "read", [DBUS_PROP_WRITE] = "write", [DBUS_PROP_READWRITE] = "readwrite" }; static bool dbus_reply_introspection(DBusMessage *reply, struct gsh_dbus_interface **interfaces) { DBusMessageIter iter; FILE *fp; char *introspection_xml = NULL; size_t xml_size = 0; struct gsh_dbus_interface **iface; bool have_props = false; bool props_signal = false; bool retval = true; fp = open_memstream(&introspection_xml, &xml_size); if (fp == NULL) { LogCrit(COMPONENT_DBUS, "open_memstream for introspection failed"); retval = false; goto out; } fputs(INTROSPECT_HEAD, fp); for (iface = interfaces; *iface; iface++) { fprintf(fp, INTROSPECT_INTERFACE_HEAD, (*iface)->name); if ((*iface)->props != NULL) { struct gsh_dbus_prop **prop; for (prop = (*iface)->props; *prop; prop++) { fprintf(fp, INTROSPECT_PROPERTY, (*prop)->name, (*prop)->type, prop_access[(*prop)->access]); } have_props = true; if ((*iface)->signal_props) props_signal = true; } if ((*iface)->methods != NULL) { struct gsh_dbus_method **method, *mp; struct gsh_dbus_arg *arg; for (method = (*iface)->methods; *method; method++) { mp = *method; fprintf(fp, INTROSPECT_METHOD_HEAD, mp->name); for (arg = mp->args; arg->name != NULL; arg++) { fprintf(fp, INTROSPECT_METHOD_ARG, arg->name, arg->direction, arg->type); } fputs(INTROSPECT_METHOD_TAIL, fp); } } if ((*iface)->signals != NULL) { struct gsh_dbus_signal **signal; struct gsh_dbus_arg *arg; for (signal = (*iface)->signals; *signal; signal++) { fprintf(fp, INTROSPECT_SIGNAL_HEAD, (*signal)->name); for (arg = (*signal)->args; arg->name != NULL; arg++) { fprintf(fp, INTROSPECT_SIGNAL_ARG, arg->name, arg->type); } fputs(INTROSPECT_SIGNAL_TAIL, fp); } } fputs(INTROSPECT_INTERFACE_TAIL, fp); } if (have_props) { fputs(PROPERTIES_INTERFACE_HEAD, fp); if (props_signal) fputs(PROPERTIES_INTERFACE_SIGNAL, fp); fputs(PROPERTIES_INTERFACE_TAIL, fp); } fputs(INTROSPECT_TAIL, fp); if (ferror(fp)) { LogCrit(COMPONENT_DBUS, "file error while constructing introspect xml"); } fclose(fp); if (introspection_xml == NULL) { LogCrit(COMPONENT_DBUS, "close of memstream for introspection failed"); retval = false; goto out; } /* create a reply from the message */ dbus_message_iter_init_append(reply, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &introspection_xml); gsh_free(introspection_xml); out: return retval; } /* @brief Stuff a status into the reply * * status reply is the first part of every reply message * dbus has its own error handling but that is for the connection. * this status is for ganesha level method result reporting. * If a NULL is passed for error message, we stuff a default * "BUSY". The error message is for things like GUI display or * logging but use the status bool for code flow. * * @param iter [IN] the iterator to append to * @param success [IN] the method stastus * @param errmessage [IN] an error message string */ void dbus_status_reply(DBusMessageIter *iter, bool success, char *errormsg) { char *error; dbus_bool_t retcode = success; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &retcode); if (errormsg == NULL) error = success ? "OK" : "BUSY"; else error = errormsg; dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &error); } void dbus_append_timestamp(DBusMessageIter *iterp, struct timespec *timestamp) { DBusMessageIter ts_iter; /* tv_sec and tv_nsec may not be same size as dbus_uint64_t on * 32 bit systems, so copy them here to dbus_uint64_t sized * symbols. */ dbus_uint64_t sec = timestamp->tv_sec; dbus_uint64_t nsec = timestamp->tv_nsec; dbus_message_iter_open_container(iterp, DBUS_TYPE_STRUCT, NULL, &ts_iter); dbus_message_iter_append_basic(&ts_iter, DBUS_TYPE_UINT64, &sec); dbus_message_iter_append_basic(&ts_iter, DBUS_TYPE_UINT64, &nsec); dbus_message_iter_close_container(iterp, &ts_iter); } static DBusHandlerResult dbus_message_entrypoint(DBusConnection *conn, DBusMessage *msg, void *user_data) { const char *interface = dbus_message_get_interface(msg); const char *method = dbus_message_get_member(msg); struct gsh_dbus_interface **interfaces = user_data; DBusMessage *reply = NULL; DBusError error; DBusMessageIter args, *argsp; bool success = false; DBusHandlerResult result = DBUS_HANDLER_RESULT_HANDLED; static uint32_t serial = 1; dbus_error_init(&error); if (interface == NULL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; reply = dbus_message_new_method_return(msg); if ((!strcmp(interface, DBUS_INTERFACE_INTROSPECTABLE)) || (method && (!strcmp(method, "Introspect")))) { success = dbus_reply_introspection(reply, interfaces); goto done; } if (method == NULL) { method = "No method arg"; goto done; } if (!strcmp(interface, DBUS_INTERFACE_PROPERTIES)) { success = dbus_proc_property(method, msg, reply, &error, interfaces); } else { struct gsh_dbus_interface **iface; if (dbus_message_iter_init(msg, &args)) argsp = &args; else argsp = NULL; for (iface = interfaces; *iface; iface++) { if (strcmp(interface, (*iface)->name) == 0) { struct gsh_dbus_method **m; for (m = (*iface)->methods; m && *m; m++) { if (strcmp(method, (*m)->name) == 0) { success = (*m)->method(argsp, reply, &error); goto done; } } LogMajor(COMPONENT_DBUS, "Unknown method (%s) on interface (%s)", method, interface); result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; goto done; } } LogMajor(COMPONENT_DBUS, "Unknown interface (%s)", interface); } done: if (!success) { const char *err_name, *err_text; if (dbus_error_is_set(&error)) { err_name = error.name; err_text = error.message; } else { err_name = interface; err_text = method; } LogMajor(COMPONENT_DBUS, "Method (%s) on (%s) failed: name = (%s), message = (%s)", method, interface, err_name, err_text); dbus_message_unref(reply); reply = dbus_message_new_error(msg, err_name, err_text); } success = dbus_connection_send(conn, reply, &serial); if (!success) { LogCrit(COMPONENT_DBUS, "reply failed"); result = DBUS_HANDLER_RESULT_NEED_MEMORY; dbus_connection_flush(conn); } if (reply) dbus_message_unref(reply); dbus_error_free(&error); serial++; return result; } static void path_unregistered_func(DBusConnection *connection, void *user_data) { /* connection was finalized -- do nothing */ } int32_t gsh_dbus_register_path(const char *name, struct gsh_dbus_interface **interfaces) { struct ganesha_dbus_handler *handler; struct avltree_node *node; char path[512]; int code = 0; /* XXX if this works, add ifc level */ snprintf(path, 512, "%s%s", DBUS_PATH, name); handler = (struct ganesha_dbus_handler *) gsh_malloc(sizeof(struct ganesha_dbus_handler)); handler->name = gsh_strdup(path); handler->vtable.unregister_function = path_unregistered_func; handler->vtable.message_function = dbus_message_entrypoint; if (!thread_state.dbus_conn) { LogCrit(COMPONENT_DBUS, "dbus_connection_register_object_path called with no DBUS connection"); gsh_free(handler->name); gsh_free(handler); goto out; } code = dbus_connection_register_object_path(thread_state.dbus_conn, handler->name, &handler->vtable, (void *)interfaces); if (!code) { LogFatal(COMPONENT_DBUS, "dbus_connection_register_object_path failed"); gsh_free(handler->name); gsh_free(handler); goto out; } node = avltree_insert(&handler->node_k, &thread_state.callouts); if (node) { LogFatal(COMPONENT_DBUS, "failed inserting method %s", path); code = EINVAL; } LogDebug(COMPONENT_DBUS, "registered handler for %s", path); out: return code; } void gsh_dbus_pkgshutdown(void) { struct avltree_node *node, *next_node; struct ganesha_dbus_handler *handler; int code = 0; char prefixed_dbus_name[NAME_MAX]; LogDebug(COMPONENT_DBUS, "shutdown"); /* Shutdown gsh_dbus_thread */ thread_state.flags |= GSH_DBUS_SHUTDOWN; pthread_join(gsh_dbus_thrid, NULL); /* remove and free handlers */ node = avltree_first(&thread_state.callouts); while (node) { next_node = avltree_next(node); handler = avltree_container_of(node, struct ganesha_dbus_handler, node_k); /* Unregister handler */ code = dbus_connection_unregister_object_path( thread_state.dbus_conn, handler->name); if (!code) { LogCrit(COMPONENT_DBUS, "dbus_connection_unregister_object_path called with no DBUS connection"); } avltree_remove(&handler->node_k, &thread_state.callouts); gsh_free(handler->name); gsh_free(handler); node = next_node; } avltree_init(&thread_state.callouts, dbus_callout_cmpf, 0); /* Unassign the name from dbus connection */ if (thread_state.dbus_conn) { dbus_name_with_prefix(prefixed_dbus_name, dbus_name, nfs_param.core_param.dbus_name_prefix); dbus_bus_release_name(thread_state.dbus_conn, prefixed_dbus_name, &thread_state.dbus_err); if (dbus_error_is_set(&thread_state.dbus_err)) { LogCrit(COMPONENT_DBUS, "err releasing name (%s, %s)", handler->name, thread_state.dbus_err.message); dbus_error_free(&thread_state.dbus_err); } /* * Shutdown bus: As per D-Bus documentation, a shared * connection created with dbus_connection_open() or * dbus_bus_get() should not be closed but instead be unref'ed */ dbus_connection_unref(thread_state.dbus_conn); } } void *gsh_dbus_thread(void *arg) { struct glist_head *glist = NULL; struct glist_head *glistn = NULL; struct timespec current_time; int time_expired; int rc = 0; SetNameFunction("dbus"); if (!thread_state.initialized) { LogCrit(COMPONENT_DBUS, "DBUS not initialized, service thread exiting"); goto out; } while (1) { if (thread_state.flags & GSH_DBUS_SHUTDOWN) break; LogFullDebug(COMPONENT_DBUS, "top of poll loop"); PTHREAD_MUTEX_lock(&dbus_bcast_lock); glist_for_each_safe(glist, glistn, &dbus_broadcast_list) { struct dbus_bcast_item *bcast_item = glist_entry(glist, struct dbus_bcast_item, dbus_bcast_q); now(¤t_time); time_expired = gsh_time_cmp( ¤t_time, &bcast_item->next_bcast_time); /* * list is sorted by soonest to latest * Break now if the next is not ready */ if (time_expired < 0) break; bcast_item->next_bcast_time = current_time; timespec_add_nsecs(bcast_item->bcast_interval, &bcast_item->next_bcast_time); rc = bcast_item->bcast_callback(bcast_item->bcast_arg); if (rc == BCAST_STATUS_WARN) { LogWarn(COMPONENT_DBUS, "Broadcast callback %p returned BCAST_STATUS_WARN", bcast_item); } else if (rc == BCAST_STATUS_FATAL) { LogWarn(COMPONENT_DBUS, "Broadcast callback %p returned BCAST_STATUS_FATAL", bcast_item); glist_del(&bcast_item->dbus_bcast_q); continue; } if (bcast_item->count > 0) bcast_item->count--; glist_del(&bcast_item->dbus_bcast_q); /* * If the callback should be called again, put it * back in the list sorted by soonest to longest */ if (bcast_item->count > 0 || bcast_item->count == BCAST_FOREVER) { glist_insert_sorted(&dbus_broadcast_list, &bcast_item->dbus_bcast_q, &dbus_bcast_item_compare); } } PTHREAD_MUTEX_unlock(&dbus_bcast_lock); /* do stuff */ if (!dbus_connection_read_write_dispatch (thread_state.dbus_conn, 100)) { LogCrit(COMPONENT_DBUS, "read_write_dispatch, got disconnected signal"); break; } /* here is where we do other stuff between messages */ } /* 1 */ out: LogEvent(COMPONENT_DBUS, "shutdown"); return NULL; } void gsh_dbus_wake_thread(uint32_t flags) { if (thread_state.flags & GSH_DBUS_SLEEPING) pthread_cond_signal(&thread_state.we.cv); } /* * @brief gsh_dbus_broadcast: Broadcast a dbus message * * Function to be called by a thread's callback routine * in order to broadcast a message over dbus * * @param obj_name: The path to the object emitting the signal * Ex: "/org/ganesha/nfsd/heartbeat" * * @param int_name: The interface the signal is emitted from * Ex: "org.ganesha.nfsd.heartbeat" * * @param sig_name: The name of the signal * Ex: "heartbeat" * * @param first_arg_type: The type of the first argument passed * Ex: DBUS_TYPE_STRING, DBUS_TYPE_UINT32, etc... * * @param ... : An alternating list of types and data * All data args must be passed by reference * Ex: &my_int, * DBUS_TYPE_STRING, &charPtr, * DBUS_TYPE_BOOLEAN, &my_bool * * @return 0 on sucess or errno on failure */ int gsh_dbus_broadcast(char *obj_name, char *int_name, char *sig_name, int first_arg_type, ...) { static dbus_uint32_t serial; DBusMessage *msg; va_list arguments; int rc = 0; msg = dbus_message_new_signal(obj_name, int_name, sig_name); if (msg == NULL) return -EINVAL; va_start(arguments, first_arg_type); dbus_message_append_args_valist(msg, first_arg_type, arguments); va_end(arguments); if (!dbus_connection_send(thread_state.dbus_conn, msg, &serial)) rc = -ENOMEM; dbus_message_unref(msg); return rc; } #ifdef _USE_9P /* parse the 9P operation in args */ bool arg_9p_op(DBusMessageIter *args, u8 *opcode, char **errormsg) { char *opname; u8 opc; bool success = true; if (args == NULL) { success = false; *errormsg = "message is missing argument"; } else if (dbus_message_iter_get_arg_type(args) != DBUS_TYPE_STRING) { success = false; *errormsg = "arg not a string"; } else { dbus_message_iter_get_basic(args, &opname); for (opc = _9P_TSTATFS; opc <= _9P_TWSTAT; opc++) { if (_9pfuncdesc[opc].funcname != NULL && !strncmp(opname, _9pfuncdesc[opc].funcname, 16)) break; } if (opc > _9P_TWSTAT) { success = false; *errormsg = "arg not a known 9P operation"; } else { *opcode = opc; } } return success; } #endif nfs-ganesha-2.6.0/src/dbus/properties_handler.c000066400000000000000000000205611324272410200214730ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) 2013, Panasas Inc. * Contributor : Jim Lieb * * Some portions Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ #include "config.h" #include #include #include #include #include #include #include "gsh_list.h" #include "fsal.h" #include "nfs_core.h" #include "log.h" #include "nfs_rpc_callback.h" #include "gsh_dbus.h" #include #include "dbus_priv.h" #ifndef DBUS_ERROR_UNKNOWN_INTERFACE #define DBUS_ERROR_UNKNOWN_INTERFACE \ "org.freedesktop.DBus.Error.UnknownInterface" #endif #ifndef DBUS_ERROR_UNKNOWN_PROPERTY #define DBUS_ERROR_UNKNOWN_PROPERTY \ "org.freedesktop.DBus.Error.UnknownProperty" #endif #ifndef DBUS_ERROR_PROPERTY_READ_ONLY #define DBUS_ERROR_PROPERTY_READ_ONLY \ "org.freedesktop.DBus.Error.PropertyReadOnly" #endif /** * @brief The properties interface for properties interfaces * * Seems if introspect says we have a properties interface for an * object, scanners want to wall it to see if it has its own props. * Fake one here because properties doesn't have properties; signals * maybe but no props. */ static struct gsh_dbus_interface props_interface = { .name = DBUS_INTERFACE_PROPERTIES, .props = NULL, .methods = NULL, .signals = NULL }; static struct gsh_dbus_interface *props_ptr = &props_interface; static inline struct gsh_dbus_interface **lookup_interface(const char *interface, struct gsh_dbus_interface **interfaces, DBusError *error) { struct gsh_dbus_interface **iface; if (strcmp(interface, DBUS_INTERFACE_PROPERTIES) == 0) return &props_ptr; for (iface = interfaces; *iface; iface++) { if (strcmp(interface, (*iface)->name) == 0) break; } if (*iface == NULL) { dbus_set_error(error, DBUS_ERROR_UNKNOWN_INTERFACE, "Requested interface: %s", interface); } return iface; } static inline struct gsh_dbus_prop **lookup_property(const char *prop_name, struct gsh_dbus_interface **iface, DBusError *error) { struct gsh_dbus_prop **prop; for (prop = (*iface)->props; prop && *prop; prop++) { if (strcmp(prop_name, (*prop)->name) == 0) break; } if (prop == NULL) { dbus_set_error(error, DBUS_ERROR_UNKNOWN_PROPERTY, "Requested property: %s from %s", prop_name, (*iface)->name); } return prop; } /** * * @brief Handle object properties * * Handle the three methods of the properties interface. * * @param argsp [IN] The message * @param reply [IN/OUT] Our reply * * @return bool success or failure if bad request */ bool dbus_proc_property(const char *method, DBusMessage *msg, DBusMessage *reply, DBusError *error, struct gsh_dbus_interface **interfaces) { const char *interface; const char *prop_name; bool retval = false; struct gsh_dbus_interface **iface; struct gsh_dbus_prop **prop; DBusMessageIter reply_iter; dbus_message_iter_init_append(reply, &reply_iter); if (strcmp(method, "GetAll") == 0) { DBusMessageIter getall_dict, dict_entry, val_iter; if (!dbus_message_get_args (msg, error, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) goto err_out; iface = lookup_interface(interface, interfaces, error); if (*iface == NULL) goto err_out; if (!dbus_message_iter_open_container (&reply_iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &getall_dict)) goto getall_err; for (prop = (*iface)->props; prop && *prop; prop++) { prop_name = (*prop)->name; if ((*prop)->access == DBUS_PROP_READ || (*prop)->access == DBUS_PROP_READWRITE) { if (!dbus_message_iter_open_container (&getall_dict, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry)) goto getall_err; if (!dbus_message_iter_append_basic (&dict_entry, DBUS_TYPE_STRING, &prop_name)) goto getall_err; if (!dbus_message_iter_open_container (&dict_entry, DBUS_TYPE_VARIANT, (*prop)->type, &val_iter)) goto getall_err; if (!(*prop)->get(&val_iter)) goto getall_err; if (!dbus_message_iter_close_container (&dict_entry, &val_iter)) goto getall_err; if (!dbus_message_iter_close_container (&getall_dict, &dict_entry)) goto getall_err; } else { dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, "%s of %s from %s (write only?)", method, prop_name, interface); /** @todo@ check does write only make sense?? */ goto err_out; } } if (!dbus_message_iter_close_container (&reply_iter, &getall_dict)) goto getall_err; return true; /* DONE! */ } else if (strcmp(method, "Get") == 0) { if (!dbus_message_get_args (msg, error, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &prop_name, DBUS_TYPE_INVALID)) goto err_out; iface = lookup_interface(interface, interfaces, error); if (*iface == NULL) goto err_out; prop = lookup_property(prop_name, iface, error); if (*prop == NULL) goto err_out; if ((*prop)->access == DBUS_PROP_READ || (*prop)->access == DBUS_PROP_READWRITE) { DBusMessageIter variant_iter; if (!dbus_message_iter_open_container (&reply_iter, DBUS_TYPE_VARIANT, (*prop)->type, &variant_iter)) { dbus_set_error_const(error, DBUS_ERROR_FAILED, "Couldn't open Get container"); goto err_out; } retval = (*prop)->get(&variant_iter); if (retval == false || !dbus_message_iter_close_container(&reply_iter, &variant_iter)) { dbus_set_error_const(error, DBUS_ERROR_FAILED, "Couldn't close Get container"); goto err_out; } } else { dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, "%s of %s from %s (write only?)", method, prop_name, interface); /** @todo@ check does write only make sense?? */ goto err_out; } return true; /* DONE! */ } else if (strcmp(method, "Set") == 0) { DBusMessageIter iter_args; if (!dbus_message_iter_init(msg, &iter_args) || dbus_message_iter_get_arg_type(&iter_args) != DBUS_TYPE_STRING) { goto invalid_args; } dbus_message_iter_get_basic(&iter_args, &interface); if (!dbus_message_iter_next(&iter_args) || dbus_message_iter_get_arg_type(&iter_args) != DBUS_TYPE_STRING) { goto invalid_args; } dbus_message_iter_get_basic(&iter_args, &prop_name); if (!dbus_message_iter_next(&iter_args) || dbus_message_iter_get_arg_type(&iter_args) != DBUS_TYPE_VARIANT || dbus_message_iter_has_next(&iter_args)) { goto invalid_args; } iface = lookup_interface(interface, interfaces, error); if (*iface == NULL) goto err_out; prop = lookup_property(prop_name, iface, error); if (*prop == NULL) goto err_out; if ((*prop)->access == DBUS_PROP_WRITE || (*prop)->access == DBUS_PROP_READWRITE) { DBusMessageIter arg; dbus_message_iter_recurse(&iter_args, &arg); return (*prop)->set(&arg); /* DONE! */ } else { dbus_set_error(error, DBUS_ERROR_PROPERTY_READ_ONLY, "%s of %s from %s", method, prop_name, interface); goto err_out; } } else { dbus_set_error(error, DBUS_ERROR_UNKNOWN_METHOD, "Requested method: %s", method); } return retval; getall_err: dbus_set_error(error, DBUS_ERROR_FAILED, "GetAll container failure"); goto err_out; invalid_args: dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "Method %s", method); err_out: return retval; } nfs-ganesha-2.6.0/src/dbus/signal_handler.c000066400000000000000000000046641324272410200205620ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) 2013, Panasas Inc. * Contributor : Jim Lieb * * Some portions Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ #include "config.h" #include #include #include #include #include #include #include "gsh_list.h" #include "fsal.h" #include "nfs_core.h" #include "log.h" #include "nfs_rpc_callback.h" #include "gsh_dbus.h" #include #include "dbus_priv.h" /** * @TODO@ This is a preliminary implementation to get * the basics down. Subject to change when we get real signals to send... */ /** * @brief Append a string to a signal */ int dbus_append_signal_string(DBusMessageIter *args, void *sig_string) { char *sigvalue = (char *)sig_string; if (!dbus_message_iter_append_basic(args, DBUS_TYPE_STRING, &sigvalue)) return ENOMEM; return 0; } /** * @brief Process a signal and send it */ int dbus_send_signal(DBusConnection *conn, char *obj_name, char *int_name, char *sig_name, int (*payload) (DBusMessageIter *signal, void *args), void *sig_args) { static dbus_uint32_t serial; DBusMessage *msg; DBusMessageIter sig_iter; int retval = 0; msg = dbus_message_new_signal(obj_name, int_name, sig_name); if (msg == NULL) return EINVAL; dbus_message_iter_init_append(msg, &sig_iter); retval = payload(&sig_iter, sig_args); if (retval != 0) return retval; if (!dbus_connection_send(conn, msg, &serial)) return ENOMEM; dbus_connection_flush(conn); dbus_message_unref(msg); return retval; } nfs-ganesha-2.6.0/src/doc/000077500000000000000000000000001324272410200152425ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/doc/CMakeLists.txt000066400000000000000000000000751324272410200200040ustar00rootroot00000000000000if(USE_MAN_PAGE) add_subdirectory(man) endif(USE_MAN_PAGE) nfs-ganesha-2.6.0/src/doc/man/000077500000000000000000000000001324272410200160155ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/doc/man/CMakeLists.txt000066400000000000000000000027401324272410200205600ustar00rootroot00000000000000set(sphinx_output_dir ${CMAKE_BINARY_DIR}/doc) set(man_srcs ganesha-config.rst ganesha-log-config.rst ganesha-cache-config.rst ganesha-export-config.rst ganesha-core-config.rst) if(USE_9P) list(APPEND man_srcs ganesha-9p-config.rst) endif() if(USE_FSAL_CEPH) list(APPEND man_srcs ganesha-ceph-config.rst) endif() if(USE_FSAL_RGW) list(APPEND man_srcs ganesha-rgw-config.rst) endif() if(USE_FSAL_XFS) list(APPEND man_srcs ganesha-xfs-config.rst) endif() if(USE_FSAL_GLUSTER) list(APPEND man_srcs ganesha-gluster-config.rst) endif() if(USE_FSAL_VFS) list(APPEND man_srcs ganesha-vfs-config.rst) endif() if(USE_FSAL_PROXY) list(APPEND man_srcs ganesha-proxy-config.rst) endif() if(USE_FSAL_GPFS) list(APPEND man_srcs ganesha-gpfs-config.rst) endif() foreach(man ${man_srcs}) list(APPEND sphinx_input ${CMAKE_CURRENT_SOURCE_DIR}/${man}) string(REGEX REPLACE ".rst$" "" cmd ${man}) list(APPEND sphinx_output ${sphinx_output_dir}/${cmd}.8) install(FILES ${sphinx_output_dir}/${cmd}.8 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man8/) endforeach() add_custom_command( OUTPUT ${sphinx_output} COMMAND ${SPHINX_BUILD} -b man -t man -d ${CMAKE_BINARY_DIR}/doc/doctrees -c ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${sphinx_output_dir} DEPENDS ${sphinx_input}) add_custom_target( manpages ALL DEPENDS ${sphinx_output} COMMENT "manpages building") nfs-ganesha-2.6.0/src/doc/man/conf.py000066400000000000000000000023631324272410200173200ustar00rootroot00000000000000import os project = u'NFS-Ganesha' def _get_description(fname, base): with open(fname) as f: one = None while True: line = f.readline().rstrip('\n') if not line: continue if line.startswith(':') and line.endswith(':'): continue one = line break two = f.readline().rstrip('\n') three = f.readline().rstrip('\n') assert one == three assert all(c=='=' for c in one) name, description = two.split('--', 1) assert name.strip() == base return description.strip() def _get_manpages(): man_dir = os.path.dirname(__file__) for filename in os.listdir(man_dir): base, ext = os.path.splitext(filename) if ext != '.rst': continue if base == 'index': continue path = os.path.join(man_dir, filename) description = _get_description(path, base) yield ( os.path.join(base), base, description, '', 8, ) man_pages = list(_get_manpages()) # sphinx warns if no toc is found, so feed it with a random file # which is also rendered in this run. master_doc = 'index' nfs-ganesha-2.6.0/src/doc/man/ganesha-9p-config.rst000066400000000000000000000024721324272410200217530ustar00rootroot00000000000000=================================================================== ganesha-9p-config -- NFS Ganesha 9p Configuration File =================================================================== .. program:: ganesha-9p-config SYNOPSIS ========================================================== | /etc/ganesha/ganesha.conf DESCRIPTION ========================================================== NFS-Ganesha obtains configuration data from the configuration file: /etc/ganesha/ganesha.conf This file lists 9p specific config options. 9P {} -------------------------------------------------------------------------------- **_9P_TCP_Port(uint16, range 1 to UINT16_MAX, default 564)** **_9P_RDMA_Port(uint16, range 1 to UINT16_MAX, default 5640)** **_9P_TCP_Msize(uint32, range 1024 to UINT32_MAX, default 65536)** **_9P_RDMA_Msize(uint32, range 1024 to UINT32_MAX, default 1048576)** **_9P_RDMA_Backlog(uint16, range 1 to UINT16_MAX, default 10)** **_9P_RDMA_Inpool_size(uint16, range 1 to UINT16_MAX, default 64)** **_9P_RDMA_Outpool_Size(uint16, range 1 to UINT16_MAX, default 32)** See also ============================== :doc:`ganesha-config `\(8) :doc:`ganesha-log-config `\(8) :doc:`ganesha-core-config `\(8) :doc:`ganesha-export-config `\(8) nfs-ganesha-2.6.0/src/doc/man/ganesha-cache-config.rst000066400000000000000000000066121324272410200224660ustar00rootroot00000000000000=================================================================== ganesha-cache-config -- NFS Ganesha Cache Configuration File =================================================================== .. program:: ganesha-cache-config SYNOPSIS ========================================================== | /etc/ganesha/ganesha.conf DESCRIPTION ========================================================== NFS-Ganesha reads the configuration data from: | /etc/ganesha/ganesha.conf This file lists NFS-Ganesha Cache config options. CACHEINODE {} -------------------------------------------------------------------------------- NParts (uint32, range 1 to 32633, default 7) Partitions in the Cache_Inode tree. Cache_Size(uint32, range 1 to UINT32_MAX, default 32633) Per-partition hash table size. Use_Getattr_Directory_Invalidation(bool, default false) Use getattr for directory invalidation. Dir_Max_Deleted(uint32, range 1 to UINT32_MAX, default 65536) Max size of per-directory cache of removed entries Dir_Max(uint32, range 1 to UINT32_MAX, default 65536) Max size of per-directory dirent cache Dir_Chunk(uint32, range 0 to UINT32_MAX, default 128) Size of per-directory dirent cache chunks, 0 means directory chunking is not enabled. Detached_Mult(uint32, range 1 to UINT32_MAX, default 1) Max number of detached directory entries expressed as a multiple of the chunk size. Entries_HWMark(uint32, range 1 to UINT32_MAX, default 100000) The point at which object cache entries will start being reused. LRU_Run_Interval(uint32, range 1 to 24 * 3600, default 90) Base interval in seconds between runs of the LRU cleaner thread. Cache_FDs(bool, default true) Whether to cache open files FD_Limit_Percent(uint32, range 0 to 100, default 99) The percentage of the system-imposed maximum of file descriptors at which Ganesha will deny requests. FD_HWMark_Percent(uint32, range 0 to 100, default 90) The percentage of the system-imposed maximum of file descriptors above which Ganesha will make greater efforts at reaping. FD_LWMark_Percent(uint32, range 0 to 100, default 50) The percentage of the system-imposed maximum of file descriptors below which Ganesha will not reap file descriptors. Reaper_Work(uint32, range 1 to 2000, default 0) Roughly, the amount of work to do on each pass through the thread under normal conditions. (Ideally, a multiple of the number of lanes.) *This setting is deprecated. Please use Reaper_Work_Per_Lane* Reaper_Work_Per_Lane(uint32, range 1 to 2000, default 50) This is the numer of handles per lane to scan when performing LRU maintenance. This task is performed by the Reaper thread. Biggest_Window(uint32, range 1 to 100, default 40) The largest window (as a percentage of the system-imposed limit on FDs) of work that we will do in extremis. Required_Progress(uint32, range 1 to 50, default 5) Percentage of progress toward the high water mark required in in a pass through the thread when in extremis Futility_Count(uint32, range 1 to 50, default 8) Number of failures to approach the high watermark before we disable caching, when in extremis. Retry_Readdir(bool, default false) Behavior for when readdir fails for some reason: * true will ask the client to retry later, * false will give the See also ============================== :doc:`ganesha-config `\(8) nfs-ganesha-2.6.0/src/doc/man/ganesha-ceph-config.rst000066400000000000000000000030031324272410200223310ustar00rootroot00000000000000=================================================================== ganesha-ceph-config -- NFS Ganesha CEPH Configuration File =================================================================== .. program:: ganesha-ceph-config SYNOPSIS ========================================================== | /etc/ganesha/ceph.conf DESCRIPTION ========================================================== NFS-Ganesha install config example for CEPH FSAL: | /etc/ganesha/ceph.conf This file lists CEPH specific config options. EXPORT { FSAL {} } -------------------------------------------------------------------------------- Name(string, "Ceph") Name of FSAL should always be Ceph. User_Id(string, no default) cephx userid used to open the MDS session. This string is what gets appended to "client.". If not set, the ceph client libs will sort this out based on ceph configuration. Secret_Access_Key(string, no default) Key to use for the session (if any). If not set, then it uses the normal search path for cephx keyring files to find a key. CEPH {} -------------------------------------------------------------------------------- **Ceph_Conf(path, default "")** **umask(mode, range 0 to 0777, default 0)** **xattr_access_rights(mode, range 0 to 0777, default 0)** See also ============================== :doc:`ganesha-config `\(8) :doc:`ganesha-log-config `\(8) :doc:`ganesha-core-config `\(8) :doc:`ganesha-export-config `\(8) nfs-ganesha-2.6.0/src/doc/man/ganesha-config.rst000066400000000000000000000136771324272410200214360ustar00rootroot00000000000000=================================================================== ganesha-config -- NFS Ganesha Configuration File =================================================================== .. program:: ganesha-config SYNOPSIS ========================================================== | /etc/ganesha/ganesha/conf DESCRIPTION ========================================================== NFS-Ganesha obtains configuration data from the configuration file: /etc/ganesha/ganesha.conf The configuration file constitues of following parts: Comments -------------------------------------------------------------------------------- Empty lines and lines starting with ‘#’ are comments.:: # This whole line is a comment Protocol = TCP; # The rest of this line is a comment Blocks -------------------------------------------------------------------------------- Related options are grouped together into "blocks". A block is a name followed by parameters enclosed between "{" and "}". A block can contain other sub blocks as well.:: Export { Export_ID = 1; FSAL { Name = VFS: } } NOTE: FSAL is a sub block. Refer to ``BLOCKS`` section for list of blocks and options. Options -------------------------------------------------------------------------------- Configuration options can be of following types. 1. **Numeric** Numeric options can be defined in octal, decimal, or hexadecimal. The format follows ANSI C syntax. eg.:: mode = 07555; # This is octal 0755, 493 (decimal) Numeric values can also be negated or logical NOT'd. eg.:: anonomousuid = -2; # this is a negative mask = ~0xff; # Equivalent to 0xffffff00 (for 32 bit integers) 2. **Boolean** Possible values are true, false, yes and no. 1 and 0 are not acceptable. 3. **List** The option can contain a list of possible applicable values. Protocols = 3, 4, 9p; Including other config files -------------------------------------------------------------------------------- Additional files can be referenced in a configuration using %include and %url directives.:: %include %url The included file is inserted into the configuration text in place of the %include or %url line. The configuration following the inclusion is resumed after the end of the included files. File inclusion can be to any depth. eg.:: %include base.conf %include "base.conf" %url rados://mypool/myobject %url "rados://mypool/myobject BLOCKS ========================================================== NFS-Ganesha supports the following blocks: EXPORT {} -------------------------------------------------------------------------------- Along with configuration options, it also support two subblocks: 1.**EXPORT { FSAL {} }** 2.**EXPORT { CLIENT {} }** Refer to :doc:`ganesha-export-config `\(8) for usage that this block and its sub blocks support. EXPORT_DEFAULTS {} -------------------------------------------------------------------------------- Refer to :doc:`ganesha-export-config `\(8) for usage CACHEINODE {} -------------------------------------------------------------------------------- Refer to :doc:`ganesha-cache-config `\(8) for usage NFS_CORE_PARAM {} -------------------------------------------------------------------------------- Refer to :doc:`ganesha-core-config `\(8) for usage NFS_IP_NAME {} -------------------------------------------------------------------------------- Refer to :doc:`ganesha-core-config `\(8) for usage NFS_KRB5 {} -------------------------------------------------------------------------------- Refer to :doc:`ganesha-core-config `\(8) for usage NFSv4 {} -------------------------------------------------------------------------------- Refer to :doc:`ganesha-core-config `\(8) for usage CEPH {} -------------------------------------------------------------------------------- Refer to :doc:`ganesha-ceph-config `\(8) for usage 9P {} -------------------------------------------------------------------------------- Refer to :doc:`ganesha-9p-config `\(8) for usage GPFS {} -------------------------------------------------------------------------------- Refer to :doc:`ganesha-gpfs-config `\(8) for usage LOG {} -------------------------------------------------------------------------------- Refer to :doc:`ganesha-log-config `\(8) for usage 1.**LOG { FACILITY {} }** 2.**LOG { FORMAT {} }** PROXY {} -------------------------------------------------------------------------------- Refer to :doc:`ganesha-proxy-config `\(8) for usage 1.**PROXY { Remote_Server {} }** RGW {} -------------------------------------------------------------------------------- Refer to :doc:`ganesha-rgw-config `\(8) for usage VFS {} -------------------------------------------------------------------------------- Refer to :doc:`ganesha-vfs-config `\(8) for usage XFS {} -------------------------------------------------------------------------------- Refer to :doc:`ganesha-xfs-config `\(8) for usage EXAMPLE ========================================================== Along with "ganesha.conf", for each installed FSAL, a sample config file is added at: | /etc/ganesha See also ============================== :doc:`ganesha-log-config `\(8) :doc:`ganesha-rgw-config `\(8) :doc:`ganesha-vfs-config `\(8) :doc:`ganesha-xfs-config `\(8) :doc:`ganesha-gpfs-config `\(8) :doc:`ganesha-9p-config `\(8) :doc:`ganesha-proxy-config `\(8) :doc:`ganesha-ceph-config `\(8) :doc:`ganesha-core-config `\(8) :doc:`ganesha-export-config `\(8) nfs-ganesha-2.6.0/src/doc/man/ganesha-core-config.rst000066400000000000000000000243711324272410200223550ustar00rootroot00000000000000=================================================================== ganesha-core-config -- NFS Ganesha Core Configuration File =================================================================== .. program:: ganesha-core-config SYNOPSIS ========================================================== | /etc/ganesha/ganesha.conf DESCRIPTION ========================================================== NFS-Ganesha reads the configuration data from: | /etc/ganesha/ganesha.conf This file lists NFS related core config options. NFS_CORE_PARAM {} -------------------------------------------------------------------------------- Core parameters: NFS_Port (uint16, range 0 to UINT16_MAX, default 2049) Port number used by NFS Protocol. MNT_Port (uint16, range 0 to UINT16_MAX, default 0) Port number used by MNT Protocol. NLM_Port (uint16, range 0 to UINT16_MAX, default 0) Port number used by NLM Protocol. Bind_addr(IP4 addr, default 0.0.0.0) The address to which to bind for our listening port. IPv4 only, for now. NFS_Program(uint32, range 1 to INT32_MAX, default 100003) RPC program number for NFS. MNT_Program(uint32, range 1 to INT32_MAX, default 100005) RPC program number for MNT. NLM_Program(uint32, range 1 to INT32_MAX, default 100021) RPC program number for NLM. Nb_Worker(uint32, range 1 to 1024*128, default 256) Number of worker threads. Drop_IO_Errors(bool, default false) For NFSv3, whether to drop rather than reply to requests yielding I/O errors. It results in client retry. Drop_Inval_Errors(bool, default false) For NFSv3, whether to drop rather than reply to requests yielding invalid argument errors. False by default and settable with Drop_Inval_Errors. Drop_Delay_Errors(bool, default false) For NFSv3, whether to drop rather than reply to requests yielding delay errors. False by default and settable with Drop_Delay_Errors. Plugins_Dir(path, default "/usr/lib64/ganesha") Path to the directory containing server specific modules Enable_NFS_Stats(bool, default true) Whether to collect perfomance statistics. By default the perfomance counting is enabled. Enable_NFS_Stats can be enabled or disabled dynamically via ganesha_stats. Enable_Fast_Stats(bool, default false) Whether to use fast stats. If enabled this will skip statistics counters collection for per client and per export. Enable_FSAL_Stats(bool, default false) Whether to count and collect FSAL specific performance statistics. Enable_FSAL_Stats can be enabled or disabled dynamically via ganesha_stats Short_File_Handle(bool, default false) Whether to use short NFS file handle to accommodate VMware NFS client. Enable this if you have a VMware NFSv3 client. VMware NFSv3 client has a max limit of 56 byte file handles. Manage_Gids_Expiration(int64, range 0 to 7*24*60*60, default 30*60) How long the server will trust information it got by calling getgroups() when "Manage_Gids = TRUE" is used in a export entry. heartbeat_freq(uint32, range 0 to 5000 default 1000) Frequency of dbus health heartbeat in ms. Enable_NLM(bool, default true) Whether to support the Network Lock Manager protocol. Blocked_Lock_Poller_Interval(int64, range 0 to 180, default 10) Polling interval for blocked lock polling thread Protocols(enum list, values [3, 4, NFS3, NFS4, V3, V4, NFSv3, NFSv4, 9P], default [3, 4, 9P]) The protocols that Ganesha will listen for. This is a hard limit, as this list determines which sockets are opened. This list can be restricted per export, but cannot be expanded. NSM_Use_Caller_Name(bool, default false) Whether to use the supplied name rather than the IP address in NSM operations. Clustered(bool, default true) Whether this Ganesha is part of a cluster of Ganeshas. Its vendor specific option. fsid_device(bool, default false) Whether to use device major/minor for fsid. mount_path_pseudo(bool, default false) Whether to use Pseudo (true) or Path (false) for NFS v3 and 9P mounts. This option defaults to false for backward compatibility, however, for new setups, it's strongly recommended to be set true since it then means the same server path for the mount is used for both v3 and v4.x. Dbus_Name_Prefix DBus name prefix. Required if one wants to run multiple ganesha instances on single host. The prefix should be different for every ganesha instance. If this is set, the dbus name will be .org.ganesha.nfsd Parameters controlling TCP DRC behavior: ---------------------------------------- DRC_Disabled(bool, default false) Whether to disable the DRC entirely. TCP_Npart(uint32, range 1 to 20, default 1) Number of partitions in the tree for the TCP DRC. DRC_TCP_Size(uint32, range 1 to 32767, default 1024) Maximum number of requests in a transport's DRC. DRC_TCP_Cachesz(uint32, range 1 to 255, default 127) Number of entries in the O(1) front-end cache to a TCP Duplicate Request Cache. DRC_TCP_Hiwat(uint32, range 1 to 256, default 64) High water mark for a TCP connection's DRC at which to start retiring entries if we can. DRC_TCP_Recycle_Npart(uint32, range 1 to 20, default 7) Number of partitions in the recycle tree that holds per-connection DRCs so they can be used on reconnection (or recycled.) DRC_TCP_Recycle_Expire_S(uint32, range 0 to 60*60, default 600) How long to wait (in seconds) before freeing the DRC of a disconnected client. DRC_TCP_Checksum(bool, default true) Whether to use a checksum to match requests as well as the XID Parameters controlling UDP DRC behavior: ---------------------------------------- DRC_UDP_Npart(uint32, range 1 to 100, default 7) Number of partitions in the tree for the UDP DRC. DRC_UDP_Size(uint32, range 512, to 32768, default 32768) Maximum number of requests in the UDP DRC. DRC_UDP_Cachesz(uint32, range 1 to 2047, default 599) Number of entries in the O(1) front-end cache to the UDP Duplicate Request Cache. DRC_UDP_Hiwat(uint32, range 1 to 32768, default 16384) High water mark for the UDP DRC at which to start retiring entries if we can DRC_UDP_Checksum(bool, default true) Whether to use a checksum to match requests as well as the XID. Parameters affecting the relation with TIRPC: -------------------------------------------------------------------------------- RPC_Max_Connections(uint32, range 1 to 10000, default 1024) Maximum number of connections for TIRPC. RPC_Idle_Timeout_S(uint32, range 0 to 60*60, default 300) Idle timeout (seconds). Default to 300 seconds. MaxRPCSendBufferSize(uint32, range 1 to 1048576*9, default 1048576) Size of RPC send buffer. MaxRPCRecvBufferSize(uint32, range 1 to 1048576*9, default 1048576) Size of RPC receive buffer. RPC_Ioq_ThrdMax(uint32, range 1 to 1024*128 default 200) TIRPC ioq max simultaneous io threads RPC_GSS_Npart(uint32, range 1 to 1021, default 13) Partitions in GSS ctx cache table RPC_GSS_Max_Ctx(uint32, range 1 to 1048576, default 16384) Max GSS contexts in cache. Default 16k RPC_GSS_Max_Gc(uint32, range 1 to 1048576, default 200) Max entries to expire in one idle check Parameters for TCP: -------------------------------------------------------------------------------- Enable_TCP_keepalive(bool, default true) Whether tcp sockets should use SO_KEEPALIVE TCP_KEEPCNT(UINT32, range 0 to 255, default 0 -> use system defaults) Maximum number of TCP probes before dropping the connection TCP_KEEPIDLE(UINT32, range 0 to 65535, default 0 -> use system defautls) Idle time before TCP starts to send keepalive probes TCP_KEEPINTVL(INT32, range 0 to 65535, default 0 -> use system defaults) Time between each keepalive probe NFS_IP_NAME {} -------------------------------------------------------------------------------- Index_Size(uint32, range 1 to 51, default 17) Configuration for hash table for NFS Name/IP map. Expiration_Time(uint32, range 1 to 60*60*24, default 3600) Expiration time for ip-name mappings. NFS_KRB5 {} -------------------------------------------------------------------------------- **PrincipalName(string, default "nfs")** KeytabPath(path, default "") Kerberos keytab. CCacheDir(path, default "/var/run/ganesha") The ganesha credential cache. Active_krb5(bool, default false) Whether to activate Kerberos 5. Defaults to true (if Kerberos support is compiled in) NFSv4 {} -------------------------------------------------------------------------------- Graceless(bool, default false) Whether to disable the NFSv4 grace period. Lease_Lifetime(uint32, range 0 to 120, default 60) The NFSv4 lease lifetime. Grace_Period(uint32, range 0 to 180, default 90) The NFS grace period. DomainName(string, default "localdomain") Domain to use if we aren't using the nfsidmap. IdmapConf(path, default "/etc/idmapd.conf") Path to the idmap configuration file. UseGetpwnam(bool, default false if using idmap, true otherwise) Whether to use local password (PAM, on Linux) rather than nfsidmap. Allow_Numeric_Owners(bool, default true) Whether to allow bare numeric IDs in NFSv4 owner and group identifiers. Only_Numeric_Owners(bool, default false) Whether to ONLY use bare numeric IDs in NFSv4 owner and group identifiers. Delegations(bool, default false) Whether to allow delegations. Deleg_Recall_Retry_Delay(uint32_t, range 0 to 10, default 1) Delay after which server will retry a recall in case of failures pnfs_mds(bool, default false) Whether this a pNFS MDS server. For FSAL Gluster, if this is true, set pnfs_mds in gluster block as well. pnfs_ds(bool, default false) Whether this a pNFS DS server. RecoveryBackend(path, default "fs") Use different backend for client info: - fs : shared filesystem - rados_kv : rados key-value Minor_Versions(enum list, values [0, 1, 2], default [0, 1, 2]) List of supported NFSV4 minor version numbers. Slot_Table_Size(uint32, range 1 to 1024, default 64) Size of the NFSv4.1 slot table RADOS_KV {} -------------------------------------------------------------------------------- ceph_conf(string, no default) Connection to ceph cluster, should be file path for ceph configuration. userid(path, no default) User ID to ceph cluster. pool(string, default nfs-ganesha) Pool for client info. nfs-ganesha-2.6.0/src/doc/man/ganesha-export-config.rst000066400000000000000000000145401324272410200227430ustar00rootroot00000000000000=================================================================== ganesha-export-config -- NFS Ganesha Export Configuration File =================================================================== .. program:: ganesha-export-config SYNOPSIS ========================================================== /etc/ganesha/ganesha.conf DESCRIPTION ========================================================== NFS-Ganesha obtains configuration data from the configuration file: /etc/ganesha/ganesha.conf This file lists NFS-Ganesha Export block config options. EXPORT_DEFAULTS {} -------------------------------------------------------------------------------- These options are all "export permissions" options, and will be repeated in the EXPORT {} and EXPORT { CLIENT {} } blocks. These options will all be dynamically updateable. Access_Type(enum, default None) Possible values: None, RW, RO, MDONLY, MDONLY_RO Protocols(enum list, default [3,4]) Possible values: 3, 4, NFS3, NFS4, V3, V4, NFSv3, NFSv4, 9P Transports(enum list, values [UDP, TCP, RDMA], default [UDP, TCP]) Anonymous_uid(anonid, range INT32MIN to UINT32MAX, default -2) Anonymous_gid(anonid, range INT32MIN to UINT32MAX, default -2) SecType(enum list, default [none, sys]) Possible values: none, sys, krb5, krb5i, krb5p PrivilegedPort(bool, default false) Manage_Gids(bool, default false) Squash(enum, default root_sqaush) Possible values: root, root_squash, rootsquash, rootid, root_id_squash, rootidsquash, all, all_squash, allsquash, all_anomnymous, allanonymous, no_root_squash, none, noidsquash Each line of defaults above are synonyms **NFS_Commit(bool, default false)** Delegations(enum, default None) Possible values: None, read, write, readwrite, r, w, rw **Attr_Expiration_Time(int32, range -1 to INT32_MAX, default 60)** EXPORT {} -------------------------------------------------------------------------------- Export_id (required): An identifier for the export, must be unique and betweem 0 and 65535. If Export_Id 0 is specified, Pseudo must be the root path (/). Path (required) The directory in the exported file system this export is rooted on (may be ignored for some FSALs). It need not be unique if Pseudo and/or Tag are specified. Note that if it is not unique, and the core option mount_path_pseudo is not set true, a v3 mount using the path will ONLY be able to access the first export configured. To access other exports the Tag option would need to be used. Pseudo (required v4) This option specifies the position in the Pseudo FS this export occupies if this is an NFS v4 export. It must be unique. By using different Pseudo options, the same Path may be exported multiple times. This option is used to place the export within the NFS v4 Pseudo Filesystem. This creates a single name space for NFS v4. Clients may mount the root of the Pseudo Filesystem and navigate to exports. Note that the Path and Tag options are not at all visible to NFS v4 clients. Export id 0 is automatically created to provide the root and any directories necessary to navigate to exports if there is no other export specified with Pseudo = /;. Note that if an export is specified with Pseudo = /;, it need not be export id 0. Specifying such an export with FSAL { name = PSEUDO; } may be used to create a Pseudo FS with specific options. Such an export may also use other FSALs (though directories to reach exports will ONLY be automatically created on FSAL PSEUDO exports). Tag (no default) This option allows an alternative access for NFS v3 mounts. The option MUST not have a leading /. Clients may not mount subdirectories (i.e. if Tag = foo, the client may not mount foo/baz). By using different Tag options, the same Path may be exported multiple times. MaxRead (4194304) The maximum read size on this export MaxWrite (4194304) The maximum write size on this export PrefRead (4194304) The preferred read size on this export PrefWrite (4194304) The preferred write size on this export PrefReaddir (16384) The preferred readdir size on this export MaxOffsetWrite (18446744073709551615) Maximum file offset that may be written MaxOffsetRead (18446744073709551615) Maximum file offset that may be read CLIENT (optional) See the ``EXPORT { CLIENT {} }`` block. FSAL (required) See the ``EXPORT { FSAL {} }`` block. EXPORT { CLIENT {} } -------------------------------------------------------------------------------- Take all the "export permissions" options from EXPORT_DEFAULTS. The client lists are dynamically updateable. Clients(client list, empty) Client list entries can take on one of the following forms: Match any client:: @name Netgroup name x.x.x.x/y IPv4 network address wildcarded If the string contains at least one ? or * character (and is not simply "*"), the string is used to pattern match host names. Note that [] may also be used, but the pattern MUST have at least one ? or * hostname Match a single client (match is by IP address, all addresses returned by getaddrinfo will match, the getaddrinfo call is made at config parsing time) IP address Match a single client EXPORT { FSAL {} } -------------------------------------------------------------------------------- NFS-Ganesha supports the following FSALs: **Ceph** **Gluster** **GPFS** **Proxy** **RGW** **VFS** Refer to individual FSAL config file for list of config options. .. FSAL PNFS Stripe_Unit(uint32, range 1024 to 1024*1024, default 8192) pnfs_enabled(bool, default false) FSAL_NULL: EXPORT { FSAL { FSAL {} } } describes the stacked FSAL's parameters See also ============================== :doc:`ganesha-config `\(8) :doc:`ganesha-rgw-config `\(8) :doc:`ganesha-vfs-config `\(8) :doc:`ganesha-xfs-config `\(8) :doc:`ganesha-gpfs-config `\(8) :doc:`ganesha-9p-config `\(8) :doc:`ganesha-proxy-config `\(8) :doc:`ganesha-ceph-config `\(8) nfs-ganesha-2.6.0/src/doc/man/ganesha-gluster-config.rst000066400000000000000000000026221324272410200231050ustar00rootroot00000000000000=================================================================== ganesha-gluster-config -- NFS Ganesha Gluster Configuration File =================================================================== .. program:: ganesha-gluster-config SYNOPSIS ========================================================== | /etc/ganesha/gluster.conf DESCRIPTION ========================================================== NFS-Ganesha install the following config file for Gluster FSAL: | /etc/ganesha/gluster.conf This file lists Gluster specific config options. EXPORT { FSAL {} } -------------------------------------------------------------------------------- Name(string, "GLUSTER") Name of FSAL should always be GLUSTER. **volume(string, no default, required)** **hostname(string, no default, required)** **volpath(path, default "/")** **glfs_log(path, default "/tmp/gfapi.log")** **up_poll_usec(uint64, range 1 to 60*1000*1000, default 10)** **enable_upcall(bool, default true)** **transport(enum, values [tcp, rdma], default tcp)** GLUSTER {} -------------------------------------------------------------------------------- **PNFS_MDS(bool, default FALSE)** Set this parameter to true to select this node as MDS See also ============================== :doc:`ganesha-log-config `\(8) :doc:`ganesha-core-config `\(8) :doc:`ganesha-export-config `\(8) nfs-ganesha-2.6.0/src/doc/man/ganesha-gpfs-config.rst000066400000000000000000000024161324272410200223600ustar00rootroot00000000000000=================================================================== ganesha-gpfs-config -- NFS Ganesha GPFS Configuration File =================================================================== .. program:: ganesha-gpfs-config SYNOPSIS ========================================================== | /etc/ganesha/gpfs.conf DESCRIPTION ========================================================== NFS-Ganesha install the following config file for GPFS FSAL: | /etc/ganesha/gpfs.conf This file lists GPFS specific config options. GPFS {} -------------------------------------------------------------------------------- **link_support(bool, default true)** **symlink_support(bool, default true)** **cansettime(bool, default true)** **umask(mode, range 0 to 0777, default 0)** **auth_xdev_export(bool, default false)** **xattr_access_rights(mode, range 0 to 0777, default 0400)** **Delegations(enum, default read)** Possible values: None, read, write, readwrite, r, w, rw **pnfs_file(bool, default false)** **fsal_trace(bool, default true)** **fsal_grace(bool, default false)** See also ============================== :doc:`ganesha-log-config `\(8) :doc:`ganesha-core-config `\(8) :doc:`ganesha-export-config `\(8) nfs-ganesha-2.6.0/src/doc/man/ganesha-log-config.rst000066400000000000000000000067121324272410200222050ustar00rootroot00000000000000=================================================================== ganesha-log-config -- NFS Ganesha Log Configuration File =================================================================== .. program:: ganesha-log-config SYNOPSIS ========================================================== | /etc/ganesha/ganesha.conf DESCRIPTION ========================================================== NFS-Ganesha reads the configuration data from: | /etc/ganesha/ganesha.conf This file lists NFS-Ganesha Log config options. LOG {} -------------------------------------------------------------------------------- Default_log_level(token,default EVENT) The log levels are: NULL, FATAL, MAJ, CRIT, WARN, EVENT, INFO, DEBUG, MID_DEBUG, M_DBG, FULL_DEBUG, F_DBG RPC_Debug_Flags(uint32, range 0 to UINT32_MAX, default 7) Debug flags for TIRPC (default 7 matches log level default EVENT). LOG { COMPONENTS {} } -------------------------------------------------------------------------------- **Default_log_level(token,default EVENT)** These entries are of the form: COMPONENT = LEVEL; The components are: ALL, LOG, LOG_EMERG, MEMLEAKS, FSAL, NFSPROTO, NFS_V4, EXPORT, FILEHANDLE, DISPATCH, CACHE_INODE, CACHE_INODE_LRU, HASHTABLE, HASHTABLE_CACHE, DUPREQ, INIT, MAIN, IDMAPPER, NFS_READDIR, NFS_V4_LOCK, CONFIG, CLIENTID, SESSIONS, PNFS, RW_LOCK, NLM, RPC, NFS_CB, THREAD, NFS_V4_ACL, STATE, 9P, 9P_DISPATCH, FSAL_UP, DBUS Some synonyms are: FH = FILEHANDLE HT = HASHTABLE INODE_LRU = CACHE_INODE_LRU INODE = CACHE_INODE DISP = DISPATCH LEAKS = MEMLEAKS NFS3 = NFSPROTO NFS4 = NFS_V4 HT_CACHE = HASHTABLE_CACHE NFS_STARTUP = INIT NFS4_LOCK = NFS_V4_LOCK NFS4_ACL = NFS_V4_ACL 9P_DISP = 9P_DISPATCH The log levels are: NULL, FATAL, MAJ, CRIT, WARN, EVENT, INFO, DEBUG, MID_DEBUG, M_DBG, FULL_DEBUG, F_DBG default EVENT LOG { FACILITY {} } -------------------------------------------------------------------------------- **name(string, no default)** **destination(string, no default, must be supplied)** **max_level(token,default FULL_DEBUG)** The log levels are: NULL, FATAL, MAJ, CRIT, WARN, EVENT, INFO, DEBUG, MID_DEBUG, M_DBG, FULL_DEBUG, F_DBG **headers(token, values [none, component, all], default all)** **enable(token, values [idle, active, default], default idle)** LOG { FORMAT {} } -------------------------------------------------------------------------------- date_format(enum,default ganesha) Possible values: ganesha, true, local, 8601, ISO-8601, ISO 8601, ISO, syslog, syslog_usec, false, none, user_defined time_format(enum,default ganesha) Possible values: ganesha, true, local, 8601, ISO-8601, ISO 8601, ISO, syslog, syslog_usec, false, none, user_defined **user_date_format(string, no default)** **user_time_format(string, no default)** **EPOCH(bool, default true)** **CLIENTIP(bool, default false)** **HOSTNAME(bool, default true)** **PROGNAME(bool, default true)** **PID(bool, default true)** **THREAD_NAME(bool, default true)** **FILE_NAME(bool, default true)** **LINE_NUM(bool, default true)** **FUNCTION_NAME(bool, default true)** **COMPONENT(bool, default true)** **LEVEL(bool, default true)** See also ============================== :doc:`ganesha-config `\(8) nfs-ganesha-2.6.0/src/doc/man/ganesha-proxy-config.rst000066400000000000000000000056401324272410200226040ustar00rootroot00000000000000=================================================================== ganesha-proxy-config -- NFS Ganesha Proxy Configuration File =================================================================== .. program:: ganesha-proxy-config SYNOPSIS ========================================================== | /etc/ganesha/ganesha.conf DESCRIPTION ========================================================== NFS-Ganesha install the following config file for Proxy FSAL: | /etc/ganesha/ganesha.conf This file lists Proxy specific config options. PROXY {} -------------------------------------------------------------------------------- **link_support(bool, default true)** **symlink_support(bool, default true)** **cansettime(bool, default true)** **MAX_READ_WRITE_SIZE(default 1MB)** **FSAL_MAXIOSIZE(default 64 MB)** **SEND_RECV_HEADER_SPACE(default, 512 Bytes)** **maxread(uint64, default MAX_READ_WRITE_SIZE)** range 512 to FSAL_MAXIOSIZE - SEND_RECV_HEADER_SPACE **maxwrite(uint64, default MAX_READ_WRITE_SIZE)** range 512 to FSAL_MAXIOSIZE - SEND_RECV_HEADER_SPACE **umask(mode, range 0 to 0777, default 0)** **auth_xdev_export(bool, default false)** **xattr_access_rights(mode, range 0 to 0777, default 0400)** PROXY { Remote_Server {} } -------------------------------------------------------------------------------- **Retry_SleepTime(uint32, range 0 to 60, default 10)** **Srv_Addr(ipv4_addr default "127.0.0.1")** **NFS_Service(uint32, range 0 to UINT32_MAX, default 100003)** **NFS_SendSize** must be greater than maxwrite+SEND_RECV_HEADER_SPACE **NFS_RecvSize** must be greater than maxread+SEND_RECV_HEADER_SPACE **MAX_READ_WRITE_SIZE(default 1 MB)** **SEND_RECV_HEADER_SPACE(default 512 Bytes)** **FSAL_MAXIOSIZE(default 64 MB)** NFS_SendSize(uint32, default MAX_READ_WRITE_SIZE + SEND_RECV_HEADER_SPACE) range 512 + SEND_RECV_HEADER_SPACE to FSAL_MAXIOSIZE NFS_RecvSize(uint32, default MAX_READ_WRITE_SIZE + SEND_RECV_HEADER_SPACE) range 512 + SEND_RECV_HEADER_SPACE to FSAL_MAXIOSIZE **NFS_Port(uint16, range 0 to UINT16_MAX, default 2049)** **Use_Privileged_Client_Port(bool, default true)** **RPC_Client_Timeout(uint32, range 1 to 60*4, default 60)** **Remote_PrincipalName(string, no default)** **KeytabPath(string, default "/etc/krb5.keytab")** **Credential_LifeTime(uint32, range 0 to 86400*2, default 86400)** **Sec_Type(enum, values [krb5, krb5i, krb5p], default krb5)** **Active_krb5(bool, default false)** **Enable_Handle_Mapping(bool, default false)** **HandleMap_DB_Dir(string, default "/var/ganesha/handlemap")** **HandleMap_Tmp_Dir(string, default "/var/ganesha/tmp")** **HandleMap_DB_Count(uint32, range 1 to 16, default 8)** **HandleMap_HashTable_Size(uint32, range 1 to 127, default 103)** See also ============================== :doc:`ganesha-log-config `\(8) :doc:`ganesha-core-config `\(8) :doc:`ganesha-export-config `\(8) nfs-ganesha-2.6.0/src/doc/man/ganesha-rgw-config.rst000066400000000000000000000044051324272410200222200ustar00rootroot00000000000000=================================================================== ganesha-rgw-config -- NFS Ganesha RGW Configuration File =================================================================== .. program:: ganesha-rgw-config SYNOPSIS ========================================================== | /etc/ganesha/rgw.conf | /etc/ganesha/rgw_bucket.conf DESCRIPTION ========================================================== NFS-Ganesha install two config examples for RGW FSAL: | /etc/ganesha/rgw.conf | /etc/ganesha/rgw_bucket.conf This file lists RGW specific config options. EXPORT { } -------------------------------------------------------------------------------- RGW supports exporting both the buckets and filesystem. .. Explain in detail about exporting bucket and filesystem EXPORT { FSAL {} } -------------------------------------------------------------------------------- Name(string, "RGW") Name of FSAL should always be RGW. **User_Id(string, no default)** **Access_Key(string, no default)** **Secret_Access_Key(string, no default)** RGW {} -------------------------------------------------------------------------------- The following configuration variables customize the startup of the FSAL's radosgw instance. ceph_conf optional full-path to the Ceph configuration file (equivalent to passing "-c /path/to/ceph.conf" to any Ceph binary name optional instance name (equivalent to passing "--name client.rgw.foohost" to the radosgw binary); the value provided here should be the same as the section name (sans brackets) of the radosgw facility in the Ceph configuration file (which must exist) cluster optional cluster name (equivalent to passing "--cluster foo" to any Ceph binary); use of a non-default value for cluster name is uncommon, but can be verified by examining the startup options of Ceph binaries init_args additional argument strings which will be passed verbatim to the radosgw instance startup process as if they had been given on the radosgw command line provided for customization in uncommon setups See also ============================== :doc:`ganesha-log-config `\(8) :doc:`ganesha-core-config `\(8) :doc:`ganesha-export-config `\(8) nfs-ganesha-2.6.0/src/doc/man/ganesha-vfs-config.rst000066400000000000000000000027311324272410200222170ustar00rootroot00000000000000=================================================================== ganesha-vfs-config -- NFS Ganesha VFS Configuration File =================================================================== .. program:: ganesha-vfs-config SYNOPSIS ========================================================== | /etc/ganesha/vfs.conf DESCRIPTION ========================================================== NFS-Ganesha installa the config example for VFS FSAL: | /etc/ganesha/vfs.conf This file lists VFS specific config options. EXPORT { FSAL {} } -------------------------------------------------------------------------------- Name(string, "vfs") Name of FSAL should always be vfs. **pnfs(bool, default false)** fsid_type(enum) Possible values: None, One64, Major64, Two64, uuid, Two32, Dev,Device VFS {} -------------------------------------------------------------------------------- **link_support(bool, default true)** **symlink_support(bool, default true)** **cansettime(bool, default true)** **maxread(uint64, range 512 to 64*1024*1024, default 64*1024*1024)** **maxwrite(uint64, range 512 to 64*1024*1024, default 64*1024*1024)** **umask(mode, range 0 to 0777, default 0)** **auth_xdev_export(bool, default false)** **xattr_access_rights(mode, range 0 to 0777, default 0400)** See also ============================== :doc:`ganesha-log-config `\(8) :doc:`ganesha-core-config `\(8) :doc:`ganesha-export-config `\(8) nfs-ganesha-2.6.0/src/doc/man/ganesha-xfs-config.rst000066400000000000000000000025441324272410200222230ustar00rootroot00000000000000=================================================================== ganesha-xfs-config -- NFS Ganesha XFS Configuration File =================================================================== .. program:: ganesha-xfs-config SYNOPSIS ========================================================== /etc/ganesha/xfs.conf DESCRIPTION ========================================================== NFS-Ganesha installs the config example for XFS FSAL: /etc/ganesha/xfs.conf This file lists xfs specific config options. EXPORT { FSAL {} } -------------------------------------------------------------------------------- Name(string, "XFS") Name of FSAL should always be XFS. XFS {} -------------------------------------------------------------------------------- **link_support(bool, default true)** **symlink_support(bool, default true)** **cansettime(bool, default true)** **maxread(uint64, range 512 to 64*1024*1024, default 64*1024*1024)** **maxwrite(uint64, range 512 to 64*1024*1024, default 64*1024*1024)** **umask(mode, range 0 to 0777, default 0)** **auth_xdev_export(bool, default false)** **xattr_access_rights(mode, range 0 to 0777, default 0400)** See also ============================== :doc:`ganesha-log-config `\(8) :doc:`ganesha-core-config `\(8) :doc:`ganesha-export-config `\(8) nfs-ganesha-2.6.0/src/doc/man/index.rst000066400000000000000000000005341324272410200176600ustar00rootroot00000000000000.. toctree:: :maxdepth: 1 :caption: Contents: ganesha-config ganesha-9p-config ganesha-ceph-config ganesha-log-config ganesha-gluster-config ganesha-gpfs-config ganesha-proxy-config ganesha-rgw-config ganesha-vfs-config ganesha-xfs-config ganesha-cache-config ganesha-export-config ganesha-core-config nfs-ganesha-2.6.0/src/ganesha.el000066400000000000000000000026771324272410200164410ustar00rootroot00000000000000;; ;; ganesha.el ;; ;; Made by Sean Dague ;; Login ;; ;; Started on Wed Mar 17 14:17:25 2010 Sean Dague ;; Last update Wed Mar 17 14:17:25 2010 Sean Dague ;; ;; The following defines ganesha C code style for emacs to the best of ;; my current understanding. This is useful for those developing for ;; ganesha that wish to keep their code in line with the existing ;; project style. I'm not an expert at such things, so corrections ;; are appreciated. ;; ;; To use this include this file in your .emacs, then C-c . to set ;; c-mode, and set it to "ganesha". (defconst ganesha-c-style '((c-tab-always-indent . t) (c-basic-offset . 4) (c-comment-only-line-offset . 0) (c-hanging-braces-alist . ((brace-entry-open before after) (substatement-open before after) (block-close . c-snug-do-while) (arglist-cont-nonempty))) (c-cleanup-list . (brace-else-brace brace-elseif-brace)) (c-offsets-alist . ((statement-block-intro . +) (knr-argdecl-intro . 0) (substatement-open . +) (substatement-label . 0) (label . 0) (brace-list-open . +) (statement-cont . +))) (indent-tabs-mode nil)) "Ganesha C Style") (c-add-style "ganesha" ganesha-c-style) nfs-ganesha-2.6.0/src/gtest/000077500000000000000000000000001324272410200156235ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/gtest/CMakeLists.txt000066400000000000000000000067761324272410200204030ustar00rootroot00000000000000# Set CMAKE_CXX_FLAGS, for gtest presently set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=gnu++14 -Wnon-virtual-dtor -Wno-invalid-offsetof") set(UNITTEST_LIBS ${GTEST_MAIN} ${GTEST} boost_program_options boost_system ${PTHREAD_LIBS}) set(UNITTEST_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${GTEST_PREFIX}/include -fno-strict-aliasing") set(GTEST_LIBRARIES MainServices ${PROTOCOLS} ${GANESHA_CORE} fsalpseudo FsalCore fsalpseudo FsalCore config_parsing ${LIBTIRPC_LIBRARIES} ${SYSTEM_LIBRARIES} ${UNITTEST_LIBS} ${LTTNG_LIBRARIES} ${LTTNG_CTL_LIBRARIES} ) # generic test set(test_example_SRCS test_example.cc ) add_executable(test_example EXCLUDE_FROM_ALL ${test_example_SRCS}) add_sanitizers(test_example) target_link_libraries(test_example ${CMAKE_THREAD_LIBS_INIT} ${UNITTEST_LIBS} ${LTTNG_LIBRARIES} pthread ) set_target_properties(test_example PROPERTIES COMPILE_FLAGS "${UNITTEST_CXX_FLAGS}") # Test using ganesha internals set(test_ci_hash_dist1_SRCS test_ci_hash_dist1.cc ) add_executable(test_ci_hash_dist1 ${test_ci_hash_dist1_SRCS}) add_sanitizers(test_ci_hash_dist1) target_link_libraries(test_ci_hash_dist1 ${GTEST_LIBRARIES} ) set_target_properties(test_ci_hash_dist1 PROPERTIES COMPILE_FLAGS "${UNITTEST_CXX_FLAGS}") set(test_lookup_latency_SRCS test_lookup_latency.cc ) add_executable(test_lookup_latency ${test_lookup_latency_SRCS}) add_sanitizers(test_lookup_latency) target_link_libraries(test_lookup_latency ${GTEST_LIBRARIES} ${GPERFTOOLS_LIBRARIES} ) set_target_properties(test_lookup_latency PROPERTIES COMPILE_FLAGS "${UNITTEST_CXX_FLAGS}") set(test_readlink_latency_SRCS test_readlink_latency.cc ) add_executable(test_readlink_latency ${test_readlink_latency_SRCS}) add_sanitizers(test_readlink_latency) target_link_libraries(test_readlink_latency ${GTEST_LIBRARIES} ) set_target_properties(test_readlink_latency PROPERTIES COMPILE_FLAGS "${UNITTEST_CXX_FLAGS}") set(test_mkdir_latency_SRCS test_mkdir_latency.cc ) add_executable(test_mkdir_latency ${test_mkdir_latency_SRCS}) add_sanitizers(test_mkdir_latency) target_link_libraries(test_mkdir_latency ${GTEST_LIBRARIES} ) set_target_properties(test_mkdir_latency PROPERTIES COMPILE_FLAGS "${UNITTEST_CXX_FLAGS}") set(test_symlink_latency_SRCS test_symlink_latency.cc ) add_executable(test_symlink_latency ${test_symlink_latency_SRCS}) add_sanitizers(test_symlink_latency) target_link_libraries(test_symlink_latency ${GTEST_LIBRARIES} ) set_target_properties(test_symlink_latency PROPERTIES COMPILE_FLAGS "${UNITTEST_CXX_FLAGS}") set(test_link_latency_SRCS test_link_latency.cc ) add_executable(test_link_latency ${test_link_latency_SRCS}) add_sanitizers(test_link_latency) target_link_libraries(test_link_latency ${GTEST_LIBRARIES} ) set_target_properties(test_link_latency PROPERTIES COMPILE_FLAGS "${UNITTEST_CXX_FLAGS}") set(test_unlink_latency_SRCS test_unlink_latency.cc ) add_executable(test_unlink_latency ${test_unlink_latency_SRCS}) add_sanitizers(test_unlink_latency) target_link_libraries(test_unlink_latency ${GTEST_LIBRARIES} ) set_target_properties(test_unlink_latency PROPERTIES COMPILE_FLAGS "${UNITTEST_CXX_FLAGS}") set(test_rename_latency_SRCS test_rename_latency.cc ) add_executable(test_rename_latency ${test_rename_latency_SRCS}) add_sanitizers(test_rename_latency) target_link_libraries(test_rename_latency ${GTEST_LIBRARIES} ) set_target_properties(test_rename_latency PROPERTIES COMPILE_FLAGS "${UNITTEST_CXX_FLAGS}") nfs-ganesha-2.6.0/src/gtest/README000066400000000000000000000016051324272410200165050ustar00rootroot00000000000000Google publishes gmock and gtest from the official/external Chromium repositories. I found that these repositories are not linked, but that the gmock source expects gtest to be checked out within. (The Ceph developers maintain a pair of linked repositories where this is already done, and do pullups from the external repositories.) The provided CMakeLists.txt files just work, but don't provide an install target. I did an in-tree build in gmock and assumed that the build tree would be dropped into some public location (e.g., /opt/gmock). Sources: git clone https://chromium.googlesource.com/external/gmock git clone https://chromium.googlesource.com/external/gtest I created a minimal example test driver with a single true assertion for reference (src/gtest/test_example.cc). For more information on how to use Google Test, see: http://code.google.com/p/googletest/wiki/Primer Mattnfs-ganesha-2.6.0/src/gtest/test_ci_hash_dist1.cc000066400000000000000000000112761324272410200217020ustar00rootroot00000000000000// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab /* * Copyright (C) 2015 Red Hat, Inc. * Contributor : Matt Benjamin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ #include #include #include #include #include #include #include #include "gtest/gtest.h" #include #include #include extern "C" { /* Ganesha headers */ #include "nfs_lib.h" #include "export_mgr.h" #include "nfs_exports.h" #include "sal_data.h" #include "fsal.h" } namespace bf = boost::filesystem; namespace { char* ganesha_conf = nullptr; char* lpath = nullptr; int dlevel = -1; uint16_t export_id = 77; struct req_op_context req_ctx; struct user_cred user_credentials; struct attrlist object_attributes; struct gsh_export* a_export = nullptr; struct fsal_obj_handle *root_entry = nullptr; struct fsal_obj_handle *test_root = nullptr; #if 0 std::uniform_int_distribution uint_dist; std::mt19937 rng; p->cksum = XXH64(p->data, 65536, 8675309); #endif int ganesha_server() { /* XXX */ return nfs_libmain( ganesha_conf, lpath, dlevel ); } } /* namespace */ TEST(CI_HASH_DIST1, INIT) { fsal_status_t status; a_export = get_gsh_export(export_id); ASSERT_NE(a_export, nullptr); status = nfs_export_get_root_entry(a_export, &root_entry); ASSERT_NE(root_entry, nullptr); /* Ganesha call paths need real or forged context info */ memset(&user_credentials, 0, sizeof(struct user_cred)); memset(&req_ctx, 0, sizeof(struct req_op_context)); memset(&object_attributes, 0, sizeof(object_attributes)); req_ctx.ctx_export = a_export; req_ctx.fsal_export = a_export->fsal_export; req_ctx.creds = &user_credentials; /* stashed in tls */ op_ctx = &req_ctx; } TEST(CI_HASH_DIST1, CREATE_ROOT) { fsal_status_t status; struct attrlist *attrs_out = nullptr; // create root directory for test FSAL_SET_MASK(object_attributes.request_mask, ATTR_MODE | ATTR_OWNER | ATTR_GROUP); object_attributes.mode = 777; /* XXX */ object_attributes.owner = 667; object_attributes.group = 766; status = root_entry->obj_ops.mkdir(root_entry, "ci_hash_dist1", &object_attributes, &test_root, attrs_out); ASSERT_NE(test_root, nullptr); } int main(int argc, char *argv[]) { int code = 0; using namespace std; using namespace std::literals; namespace po = boost::program_options; po::options_description opts("program options"); po::variables_map vm; try { opts.add_options() ("config", po::value(), "path to Ganesha conf file") ("logfile", po::value(), "log to the provided file path") ("export", po::value(), "id of export on which to operate (must exist)") ("debug", po::value(), "ganesha debug level") ; po::variables_map::iterator vm_iter; po::store(po::parse_command_line(argc, argv, opts), vm); po::notify(vm); // use config vars--leaves them on the stack vm_iter = vm.find("config"); if (vm_iter != vm.end()) { ganesha_conf = (char*) vm_iter->second.as().c_str(); } vm_iter = vm.find("logfile"); if (vm_iter != vm.end()) { lpath = (char*) vm_iter->second.as().c_str(); } vm_iter = vm.find("debug"); if (vm_iter != vm.end()) { dlevel = ReturnLevelAscii( (char*) vm_iter->second.as().c_str()); } vm_iter = vm.find("export"); if (vm_iter != vm.end()) { export_id = vm_iter->second.as(); } ::testing::InitGoogleTest(&argc, argv); std::thread ganesha(ganesha_server); std::this_thread::sleep_for(5s); code = RUN_ALL_TESTS(); ganesha.join(); } catch(po::error& e) { cout << "Error parsing opts " << e.what() << endl; } catch(...) { cout << "Unhandled exception in main()" << endl; } return code; } nfs-ganesha-2.6.0/src/gtest/test_example.cc000066400000000000000000000024561324272410200206330ustar00rootroot00000000000000// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab /* * Copyright (C) 2015 Red Hat, Inc. * Contributor : Matt Benjamin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ #include #include #include #include #include #include "gtest/gtest.h" extern "C" { /* Ganesha headers */ } namespace { bool global_decls = false; } /* namespace */ TEST(EXAMPLE, INIT) { ASSERT_EQ(0, 0); } int main(int argc, char *argv[]) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } nfs-ganesha-2.6.0/src/gtest/test_link_latency.cc000066400000000000000000000257741324272410200216640ustar00rootroot00000000000000// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab /* * Copyright (C) 2018 Red Hat, Inc. * Contributor : Girjesh Rajoria * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ #include #include #include #include #include #include #include #include "gtest/gtest.h" #include #include #include extern "C" { /* Manually forward this, an 9P is not C++ safe */ void admin_halt(void); /* Ganesha headers */ #include "nfs_lib.h" #include "export_mgr.h" #include "nfs_exports.h" #include "sal_data.h" #include "fsal.h" #include "common_utils.h" /* For MDCACHE bypass. Use with care */ #include "../FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_debug.h" } #define TEST_FILE "link_latency" #define TEST_FILE_LINK "link_to_link_latency" #define DIR_COUNT 100000 #define LOOP_COUNT 1000000 #define NAMELEN 16 namespace { char* ganesha_conf = nullptr; char* lpath = nullptr; int dlevel = -1; uint16_t export_id = 77; int ganesha_server() { /* XXX */ return nfs_libmain( ganesha_conf, lpath, dlevel ); } class Environment : public ::testing::Environment { public: Environment() : ganesha(ganesha_server) { using namespace std::literals; std::this_thread::sleep_for(5s); } virtual ~Environment() { admin_halt(); ganesha.join(); } virtual void SetUp() { } virtual void TearDown() { } std::thread ganesha; }; class LinkEmptyLatencyTest : public ::testing::Test { protected: virtual void SetUp() { fsal_status_t status; struct attrlist attrs_out; a_export = get_gsh_export(export_id); ASSERT_NE(a_export, nullptr); status = nfs_export_get_root_entry(a_export, &root_entry); ASSERT_EQ(status.major, 0); ASSERT_NE(root_entry, nullptr); /* Ganesha call paths need real or forged context info */ memset(&user_credentials, 0, sizeof(struct user_cred)); memset(&req_ctx, 0, sizeof(struct req_op_context)); memset(&attrs, 0, sizeof(attrs)); req_ctx.ctx_export = a_export; req_ctx.fsal_export = a_export->fsal_export; req_ctx.creds = &user_credentials; /* stashed in tls */ op_ctx = &req_ctx; // create file for test FSAL_SET_MASK(attrs.valid_mask, ATTR_MODE | ATTR_OWNER | ATTR_GROUP); attrs.mode = 0777; /* XXX */ attrs.owner = 667; attrs.group = 766; fsal_prepare_attrs(&attrs_out, 0); status = fsal_create(root_entry, TEST_FILE, REGULAR_FILE, &attrs, NULL, &test_file, &attrs_out); ASSERT_EQ(status.major, 0); ASSERT_NE(test_file, nullptr); fsal_release_attrs(&attrs_out); } virtual void TearDown() { fsal_status_t status; status = fsal_remove(root_entry, TEST_FILE); EXPECT_EQ(0, status.major); root_entry->obj_ops.put_ref(root_entry); root_entry = NULL; put_gsh_export(a_export); a_export = NULL; } struct req_op_context req_ctx; struct user_cred user_credentials; struct attrlist attrs; struct gsh_export* a_export = nullptr; struct fsal_obj_handle *root_entry = nullptr; struct fsal_obj_handle *test_file = nullptr; }; class LinkFullLatencyTest : public LinkEmptyLatencyTest { protected: virtual void SetUp() { fsal_status_t status; char fname[NAMELEN]; struct fsal_obj_handle *obj; struct attrlist attrs_out; LinkEmptyLatencyTest::SetUp(); /* create a bunch of dirents */ for (int i = 0; i < DIR_COUNT; ++i) { fsal_prepare_attrs(&attrs_out, 0); sprintf(fname, "file-%08x", i); status = fsal_create(root_entry, fname, REGULAR_FILE, &attrs, NULL, &obj, &attrs_out); ASSERT_EQ(status.major, 0); ASSERT_NE(obj, nullptr); fsal_release_attrs(&attrs_out); obj->obj_ops.put_ref(obj); } } virtual void TearDown() { fsal_status_t status; char fname[NAMELEN]; for (int i = 0; i < DIR_COUNT; ++i) { sprintf(fname, "file-%08x", i); status = fsal_remove(root_entry, fname); EXPECT_EQ(status.major, 0); } LinkEmptyLatencyTest::TearDown(); } }; } /* namespace */ TEST_F(LinkEmptyLatencyTest, SIMPLE) { fsal_status_t status; struct fsal_obj_handle *link; struct fsal_obj_handle *lookup; status = root_entry->obj_ops.link(test_file, root_entry, TEST_FILE_LINK); EXPECT_EQ(status.major, 0); root_entry->obj_ops.lookup(root_entry, TEST_FILE_LINK, &link, NULL); root_entry->obj_ops.lookup(root_entry, TEST_FILE, &lookup, NULL); EXPECT_EQ(lookup, link); link->obj_ops.put_ref(link); lookup->obj_ops.put_ref(lookup); /* Remove link created while running test */ status = fsal_remove(root_entry, TEST_FILE_LINK); ASSERT_EQ(status.major, 0); } TEST_F(LinkEmptyLatencyTest, SIMPLE_BYPASS) { fsal_status_t status; struct fsal_obj_handle *sub_hdl; struct fsal_obj_handle *link; struct fsal_obj_handle *lookup; sub_hdl = mdcdb_get_sub_handle(root_entry); ASSERT_NE(sub_hdl, nullptr); status = nfs_export_get_root_entry(a_export, &sub_hdl); ASSERT_EQ(status.major, 0); status = sub_hdl->obj_ops.link(test_file, sub_hdl, TEST_FILE_LINK); EXPECT_EQ(status.major, 0); root_entry->obj_ops.lookup(root_entry, TEST_FILE_LINK, &link, NULL); root_entry->obj_ops.lookup(root_entry, TEST_FILE, &lookup, NULL); EXPECT_EQ(lookup, link); link->obj_ops.put_ref(link); lookup->obj_ops.put_ref(lookup); /* Remove link created while running test */ status = fsal_remove(root_entry, TEST_FILE_LINK); ASSERT_EQ(status.major, 0); } TEST_F(LinkEmptyLatencyTest, LOOP) { fsal_status_t status; char fname[NAMELEN]; struct timespec s_time, e_time; now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "link-%08x", i); status = root_entry->obj_ops.link(test_file, root_entry, fname); EXPECT_EQ(status.major, 0); } now(&e_time); fprintf(stderr, "Average time per link: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); /* Remove link created while running test */ for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "link-%08x", i); status = fsal_remove(root_entry, fname); ASSERT_EQ(status.major, 0); } } TEST_F(LinkEmptyLatencyTest, FSALLINK) { fsal_status_t status; char fname[NAMELEN]; struct timespec s_time, e_time; now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "link-%08x", i); status = fsal_link(test_file, root_entry, fname); EXPECT_EQ(status.major, 0); } now(&e_time); fprintf(stderr, "Average time per fsal_link: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); /* Remove link created while running test */ for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "link-%08x", i); status = fsal_remove(root_entry, fname); ASSERT_EQ(status.major, 0); } } TEST_F(LinkFullLatencyTest, BIG) { fsal_status_t status; char fname[NAMELEN]; struct timespec s_time, e_time; now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "link-%08x", i); status = root_entry->obj_ops.link(test_file, root_entry, fname); ASSERT_EQ(status.major, 0) << " failed to link " << fname; } now(&e_time); fprintf(stderr, "Average time per link: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); /* Remove link created while running test */ for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "link-%08x", i); status = fsal_remove(root_entry, fname); ASSERT_EQ(status.major, 0); } } TEST_F(LinkFullLatencyTest, BIG_BYPASS) { fsal_status_t status; char fname[NAMELEN]; struct fsal_obj_handle *sub_hdl; struct timespec s_time, e_time; sub_hdl = mdcdb_get_sub_handle(root_entry); ASSERT_NE(sub_hdl, nullptr); status = nfs_export_get_root_entry(a_export, &sub_hdl); ASSERT_EQ(status.major, 0); ASSERT_NE(sub_hdl, nullptr); now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "link-%08x", i); status = sub_hdl->obj_ops.link(test_file, sub_hdl, fname); ASSERT_EQ(status.major, 0) << " failed to link " << fname; } now(&e_time); fprintf(stderr, "Average time per link: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); /* Remove link created while running test */ for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "link-%08x", i); status = fsal_remove(root_entry, fname); ASSERT_EQ(status.major, 0); } } int main(int argc, char *argv[]) { int code = 0; using namespace std; using namespace std::literals; namespace po = boost::program_options; po::options_description opts("program options"); po::variables_map vm; try { opts.add_options() ("config", po::value(), "path to Ganesha conf file") ("logfile", po::value(), "log to the provided file path") ("export", po::value(), "id of export on which to operate (must exist)") ("debug", po::value(), "ganesha debug level") ; po::variables_map::iterator vm_iter; po::command_line_parser parser{argc, argv}; parser.options(opts).allow_unregistered(); po::store(parser.run(), vm); po::notify(vm); // use config vars--leaves them on the stack vm_iter = vm.find("config"); if (vm_iter != vm.end()) { ganesha_conf = (char*) vm_iter->second.as().c_str(); } vm_iter = vm.find("logfile"); if (vm_iter != vm.end()) { lpath = (char*) vm_iter->second.as().c_str(); } vm_iter = vm.find("debug"); if (vm_iter != vm.end()) { dlevel = ReturnLevelAscii( (char*) vm_iter->second.as().c_str()); } vm_iter = vm.find("export"); if (vm_iter != vm.end()) { export_id = vm_iter->second.as(); } ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(new Environment); code = RUN_ALL_TESTS(); } catch(po::error& e) { cout << "Error parsing opts " << e.what() << endl; } catch(...) { cout << "Unhandled exception in main()" << endl; } return code; } nfs-ganesha-2.6.0/src/gtest/test_lookup_latency.cc000066400000000000000000000350241324272410200222250ustar00rootroot00000000000000// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab /* * Copyright (C) 2015 Red Hat, Inc. * Contributor : Matt Benjamin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ #include #include #include #include #include #include #include #include "gtest/gtest.h" #include #include #include extern "C" { /* Manually forward this, an 9P is not C++ safe */ void admin_halt(void); /* Ganesha headers */ #include "nfs_lib.h" #include "export_mgr.h" #include "nfs_exports.h" #include "sal_data.h" #include "fsal.h" #include "common_utils.h" /* For MDCACHE bypass. Use with care */ #include "../FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_debug.h" /* LTTng headers */ #include /* gperf headers */ #include } #define TEST_ROOT "lookup_latency" #define DIR_COUNT 100000 #define LOOP_COUNT 1000000 #define NAMELEN 16 namespace { char* ganesha_conf = nullptr; char* lpath = nullptr; int dlevel = -1; uint16_t export_id = 77; static struct lttng_handle* handle = nullptr; char* event_list = nullptr; char* profile_out = nullptr; int ganesha_server() { /* XXX */ return nfs_libmain( ganesha_conf, lpath, dlevel ); } class Environment : public ::testing::Environment { public: Environment() : Environment(NULL) {} Environment(char *_ses_name) : ganesha(ganesha_server), session_name(_ses_name) { using namespace std::literals; std::this_thread::sleep_for(5s); } virtual ~Environment() { admin_halt(); ganesha.join(); } virtual void SetUp() { struct lttng_domain dom; if (!session_name) { /* Don't setup LTTng */ return; } /* Set up LTTng */ memset(&dom, 0, sizeof(dom)); dom.type = LTTNG_DOMAIN_UST; dom.buf_type = LTTNG_BUFFER_PER_UID; handle = lttng_create_handle(session_name, &dom); } virtual void TearDown() { if (handle) { lttng_destroy_handle(handle); handle = NULL; } } std::thread ganesha; char *session_name; }; class GaneshaBaseTest : public ::testing::Test { protected: virtual void enableEvents(char *event_list) { struct lttng_event ev; int ret; if (!handle) { /* No LTTng this run */ return; } memset(&ev, 0, sizeof(ev)); ev.type = LTTNG_EVENT_TRACEPOINT; ev.loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; ev.loglevel = -1; if (!event_list) { /* Do them all */ strcpy(ev.name, "*"); ret = lttng_enable_event_with_exclusions(handle, &ev, NULL, NULL, 0, NULL); ASSERT_GE(ret, 0); } else { char *event_name; event_name = strtok(event_list, ","); while (event_name != NULL) { /* Copy name and type of the event */ strncpy(ev.name, event_name, LTTNG_SYMBOL_NAME_LEN); ev.name[sizeof(ev.name) - 1] = '\0'; ret = lttng_enable_event_with_exclusions(handle, &ev, NULL, NULL, 0, NULL); ASSERT_GE(ret, 0); /* Next event */ event_name = strtok(NULL, ","); } } } virtual void disableEvents(char *event_list) { struct lttng_event ev; int ret; if (!handle) { /* No LTTng this run */ return; } memset(&ev, 0, sizeof(ev)); ev.type = LTTNG_EVENT_ALL; ev.loglevel = -1; if (!event_list) { /* Do them all */ ret = lttng_disable_event_ext(handle, &ev, NULL, NULL); ASSERT_GE(ret, 0); } else { char *event_name; event_name = strtok(event_list, ","); while (event_name != NULL) { /* Copy name and type of the event */ strncpy(ev.name, event_name, LTTNG_SYMBOL_NAME_LEN); ev.name[sizeof(ev.name) - 1] = '\0'; ret = lttng_disable_event_ext(handle, &ev, NULL, NULL); ASSERT_GE(ret, 0); /* Next event */ event_name = strtok(NULL, ","); } } } virtual void SetUp() { } virtual void TearDown() { } }; class LookupEmptyLatencyTest : public GaneshaBaseTest { protected: virtual void SetUp() { fsal_status_t status; struct attrlist attrs_out; GaneshaBaseTest::SetUp(); a_export = get_gsh_export(export_id); ASSERT_NE(a_export, nullptr); status = nfs_export_get_root_entry(a_export, &root_entry); ASSERT_EQ(status.major, 0); ASSERT_NE(root_entry, nullptr); /* Ganesha call paths need real or forged context info */ memset(&user_credentials, 0, sizeof(struct user_cred)); memset(&req_ctx, 0, sizeof(struct req_op_context)); memset(&attrs, 0, sizeof(attrs)); req_ctx.ctx_export = a_export; req_ctx.fsal_export = a_export->fsal_export; req_ctx.creds = &user_credentials; /* stashed in tls */ op_ctx = &req_ctx; // create root directory for test FSAL_SET_MASK(attrs.valid_mask, ATTR_MODE | ATTR_OWNER | ATTR_GROUP); attrs.mode = 0777; /* XXX */ attrs.owner = 667; attrs.group = 766; fsal_prepare_attrs(&attrs_out, 0); status = fsal_create(root_entry, TEST_ROOT, DIRECTORY, &attrs, NULL, &test_root, &attrs_out); ASSERT_EQ(status.major, 0); ASSERT_NE(test_root, nullptr); fsal_release_attrs(&attrs_out); } virtual void TearDown() { fsal_status_t status; status = test_root->obj_ops.unlink(root_entry, test_root, TEST_ROOT); EXPECT_EQ(0, status.major); test_root->obj_ops.put_ref(test_root); test_root = NULL; root_entry->obj_ops.put_ref(root_entry); root_entry = NULL; put_gsh_export(a_export); a_export = NULL; GaneshaBaseTest::TearDown(); } struct req_op_context req_ctx; struct user_cred user_credentials; struct attrlist attrs; struct gsh_export* a_export = nullptr; struct fsal_obj_handle *root_entry = nullptr; struct fsal_obj_handle *test_root = nullptr; }; class LookupFullLatencyTest : public LookupEmptyLatencyTest { private: static fsal_errors_t readdir_callback(void *opaque, struct fsal_obj_handle *obj, const struct attrlist *attr, uint64_t mounted_on_fileid, uint64_t cookie, enum cb_state cb_state) { return ERR_FSAL_NO_ERROR; } protected: virtual void SetUp() { fsal_status_t status; char fname[NAMELEN]; struct fsal_obj_handle *obj; struct attrlist attrs_out; unsigned int num_entries; bool eod_met; attrmask_t attrmask = 0; uint32_t tracker; LookupEmptyLatencyTest::SetUp(); /* create a bunch of dirents */ for (int i = 0; i < DIR_COUNT; ++i) { fsal_prepare_attrs(&attrs_out, 0); sprintf(fname, "d-%08x", i); status = fsal_create(test_root, fname, REGULAR_FILE, &attrs, NULL, &obj, &attrs_out); ASSERT_EQ(status.major, 0); ASSERT_NE(obj, nullptr); fsal_release_attrs(&attrs_out); obj->obj_ops.put_ref(obj); } /* Prime the cache */ status = fsal_readdir(test_root, 0, &num_entries, &eod_met, attrmask, readdir_callback, &tracker); } virtual void TearDown() { fsal_status_t status; char fname[NAMELEN]; for (int i = 0; i < DIR_COUNT; ++i) { sprintf(fname, "d-%08x", i); status = fsal_remove(test_root, fname); EXPECT_EQ(status.major, 0); } LookupEmptyLatencyTest::TearDown(); } }; } /* namespace */ TEST_F(LookupEmptyLatencyTest, SIMPLE) { fsal_status_t status; struct fsal_obj_handle *lookup; enableEvents(event_list); status = root_entry->obj_ops.lookup(root_entry, TEST_ROOT, &lookup, NULL); EXPECT_EQ(status.major, 0); EXPECT_EQ(test_root, lookup); disableEvents(event_list); lookup->obj_ops.put_ref(lookup); } TEST_F(LookupEmptyLatencyTest, SIMPLE_BYPASS) { fsal_status_t status; struct fsal_obj_handle *sub_hdl; struct fsal_obj_handle *lookup; enableEvents(event_list); sub_hdl = mdcdb_get_sub_handle(root_entry); ASSERT_NE(sub_hdl, nullptr); status = sub_hdl->obj_ops.lookup(sub_hdl, TEST_ROOT, &lookup, NULL); EXPECT_EQ(status.major, 0); EXPECT_EQ(mdcdb_get_sub_handle(test_root), lookup); disableEvents(event_list); /* Lookup on sub-FSAL did not refcount, so no need to put it */ } TEST_F(LookupEmptyLatencyTest, LOOP) { fsal_status_t status; struct fsal_obj_handle *lookup; struct timespec s_time, e_time; enableEvents(event_list); now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { status = root_entry->obj_ops.lookup(root_entry, TEST_ROOT, &lookup, NULL); EXPECT_EQ(status.major, 0); EXPECT_EQ(test_root, lookup); } now(&e_time); disableEvents(event_list); /* Have the put_ref()'s outside the latency loop */ for (int i = 0; i < LOOP_COUNT; ++i) { lookup->obj_ops.put_ref(lookup); } fprintf(stderr, "Average time per lookup: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); } TEST_F(LookupEmptyLatencyTest, FSALLOOKUP) { fsal_status_t status; struct fsal_obj_handle *lookup; struct timespec s_time, e_time; enableEvents(event_list); now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { status = fsal_lookup(root_entry, TEST_ROOT, &lookup, NULL); EXPECT_EQ(status.major, 0); EXPECT_EQ(test_root, lookup); } now(&e_time); disableEvents(event_list); /* Have the put_ref()'s outside the latency loop */ for (int i = 0; i < LOOP_COUNT; ++i) { lookup->obj_ops.put_ref(lookup); } fprintf(stderr, "Average time per fsal_lookup: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); } TEST_F(LookupFullLatencyTest, BIG_SINGLE) { fsal_status_t status; char fname[NAMELEN]; struct fsal_obj_handle *obj; struct timespec s_time, e_time; enableEvents(event_list); now(&s_time); sprintf(fname, "d-%08x", DIR_COUNT / 5); status = test_root->obj_ops.lookup(test_root, fname, &obj, NULL); ASSERT_EQ(status.major, 0) << " failed to lookup " << fname; obj->obj_ops.put_ref(obj); now(&e_time); disableEvents(event_list); fprintf(stderr, "Average time per lookup: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time)); } TEST_F(LookupFullLatencyTest, BIG) { fsal_status_t status; char fname[NAMELEN]; struct fsal_obj_handle *obj; struct timespec s_time, e_time; enableEvents(event_list); if (profile_out) ProfilerStart(profile_out); now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "d-%08x", i % DIR_COUNT); status = test_root->obj_ops.lookup(test_root, fname, &obj, NULL); ASSERT_EQ(status.major, 0) << " failed to lookup " << fname; obj->obj_ops.put_ref(obj); } now(&e_time); if (profile_out) ProfilerStop(); disableEvents(event_list); fprintf(stderr, "Average time per lookup: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); } TEST_F(LookupFullLatencyTest, BIG_BYPASS) { fsal_status_t status; char fname[NAMELEN]; struct fsal_obj_handle *sub_hdl; struct fsal_obj_handle *obj; struct timespec s_time, e_time; sub_hdl = mdcdb_get_sub_handle(test_root); enableEvents(event_list); if (profile_out) ProfilerStart(profile_out); now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "d-%08x", i % DIR_COUNT); status = sub_hdl->obj_ops.lookup(sub_hdl, fname, &obj, NULL); ASSERT_EQ(status.major, 0) << " failed to lookup " << fname; /* Don't need to put_ref(obj) because sub-FSAL doesn't support refcounts */ } now(&e_time); if (profile_out) ProfilerStop(); disableEvents(event_list); fprintf(stderr, "Average time per lookup: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); } int main(int argc, char *argv[]) { int code = 0; char* session_name = NULL; using namespace std; using namespace std::literals; namespace po = boost::program_options; po::options_description opts("program options"); po::variables_map vm; try { opts.add_options() ("config", po::value(), "path to Ganesha conf file") ("logfile", po::value(), "log to the provided file path") ("export", po::value(), "id of export on which to operate (must exist)") ("debug", po::value(), "ganesha debug level") ("session", po::value(), "LTTng session name") ("event-list", po::value(), "LTTng event list, comma separated") ("profile", po::value(), "Enable profiling and set output file.") ; po::variables_map::iterator vm_iter; po::command_line_parser parser{argc, argv}; parser.options(opts).allow_unregistered(); po::store(parser.run(), vm); po::notify(vm); // use config vars--leaves them on the stack vm_iter = vm.find("config"); if (vm_iter != vm.end()) { ganesha_conf = (char*) vm_iter->second.as().c_str(); } vm_iter = vm.find("logfile"); if (vm_iter != vm.end()) { lpath = (char*) vm_iter->second.as().c_str(); } vm_iter = vm.find("debug"); if (vm_iter != vm.end()) { dlevel = ReturnLevelAscii( (char*) vm_iter->second.as().c_str()); } vm_iter = vm.find("export"); if (vm_iter != vm.end()) { export_id = vm_iter->second.as(); } vm_iter = vm.find("session"); if (vm_iter != vm.end()) { session_name = (char*) vm_iter->second.as().c_str(); } vm_iter = vm.find("event-list"); if (vm_iter != vm.end()) { event_list = (char*) vm_iter->second.as().c_str(); } vm_iter = vm.find("profile"); if (vm_iter != vm.end()) { profile_out = (char*) vm_iter->second.as().c_str(); } ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(new Environment(session_name)); code = RUN_ALL_TESTS(); } catch(po::error& e) { cout << "Error parsing opts " << e.what() << endl; } catch(...) { cout << "Unhandled exception in main()" << endl; } return code; } nfs-ganesha-2.6.0/src/gtest/test_mkdir_latency.cc000066400000000000000000000247101324272410200220220ustar00rootroot00000000000000// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab /* * Copyright (C) 2018 Red Hat, Inc. * Contributor : Girjesh Rajoria * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ #include #include #include #include #include #include #include #include "gtest/gtest.h" #include #include #include extern "C" { /* Manually forward this, an 9P is not C++ safe */ void admin_halt(void); /* Ganesha headers */ #include "nfs_lib.h" #include "export_mgr.h" #include "nfs_exports.h" #include "sal_data.h" #include "fsal.h" #include "common_utils.h" /* For MDCACHE bypass. Use with care */ #include "../FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_debug.h" } #define TEST_ROOT "mkdir_latency" #define DIR_COUNT 100000 #define LOOP_COUNT 1000000 #define NAMELEN 16 namespace { char* ganesha_conf = nullptr; char* lpath = nullptr; int dlevel = -1; uint16_t export_id = 77; int ganesha_server() { /* XXX */ return nfs_libmain( ganesha_conf, lpath, dlevel ); } class Environment : public ::testing::Environment { public: Environment() : ganesha(ganesha_server) { using namespace std::literals; std::this_thread::sleep_for(5s); } virtual ~Environment() { admin_halt(); ganesha.join(); } virtual void SetUp() { } virtual void TearDown() { } std::thread ganesha; }; class MkdirEmptyLatencyTest : public ::testing::Test { protected: virtual void SetUp() { fsal_status_t status; a_export = get_gsh_export(export_id); ASSERT_NE(a_export, nullptr); status = nfs_export_get_root_entry(a_export, &root_entry); ASSERT_EQ(status.major, 0); ASSERT_NE(root_entry, nullptr); /* Ganesha call paths need real or forged context info */ memset(&user_credentials, 0, sizeof(struct user_cred)); memset(&req_ctx, 0, sizeof(struct req_op_context)); memset(&attrs, 0, sizeof(attrs)); req_ctx.ctx_export = a_export; req_ctx.fsal_export = a_export->fsal_export; req_ctx.creds = &user_credentials; /* stashed in tls */ op_ctx = &req_ctx; } virtual void TearDown() { root_entry->obj_ops.put_ref(root_entry); root_entry = NULL; put_gsh_export(a_export); a_export = NULL; } struct req_op_context req_ctx; struct user_cred user_credentials; struct attrlist attrs; struct gsh_export* a_export = nullptr; struct fsal_obj_handle *root_entry = nullptr; }; class MkdirFullLatencyTest : public MkdirEmptyLatencyTest { protected: virtual void SetUp() { fsal_status_t status; char fname[NAMELEN]; struct fsal_obj_handle *obj; struct attrlist attrs_out; MkdirEmptyLatencyTest::SetUp(); /* create a bunch of dirents */ for (int i = 0; i < DIR_COUNT; ++i) { fsal_prepare_attrs(&attrs_out, 0); sprintf(fname, "file-%08x", i); status = fsal_create(root_entry, fname, REGULAR_FILE, &attrs, NULL, &obj, &attrs_out); ASSERT_EQ(status.major, 0); ASSERT_NE(obj, nullptr); fsal_release_attrs(&attrs_out); obj->obj_ops.put_ref(obj); } } virtual void TearDown() { fsal_status_t status; char fname[NAMELEN]; for (int i = 0; i < DIR_COUNT; ++i) { sprintf(fname, "file-%08x", i); status = fsal_remove(root_entry, fname); EXPECT_EQ(status.major, 0); } MkdirEmptyLatencyTest::TearDown(); } }; } /* namespace */ TEST_F(MkdirEmptyLatencyTest, SIMPLE) { fsal_status_t status; struct fsal_obj_handle *mkdir; struct fsal_obj_handle *lookup; status = root_entry->obj_ops.mkdir(root_entry, TEST_ROOT, &attrs, &mkdir, NULL); EXPECT_EQ(status.major, 0); root_entry->obj_ops.lookup(root_entry, TEST_ROOT, &lookup, NULL); EXPECT_EQ(lookup, mkdir); mkdir->obj_ops.put_ref(mkdir); lookup->obj_ops.put_ref(lookup); /* Remove directory created while running test */ status = fsal_remove(root_entry, TEST_ROOT); ASSERT_EQ(status.major, 0); } TEST_F(MkdirEmptyLatencyTest, SIMPLE_BYPASS) { fsal_status_t status; struct fsal_obj_handle *sub_hdl; struct fsal_obj_handle *mkdir; struct fsal_obj_handle *lookup; sub_hdl = mdcdb_get_sub_handle(root_entry); ASSERT_NE(sub_hdl, nullptr); status = nfs_export_get_root_entry(a_export, &sub_hdl); ASSERT_EQ(status.major, 0); status = sub_hdl->obj_ops.mkdir(sub_hdl, TEST_ROOT, &attrs, &mkdir, NULL); EXPECT_EQ(status.major, 0); root_entry->obj_ops.lookup(root_entry, TEST_ROOT, &lookup, NULL); EXPECT_EQ(lookup, mkdir); mkdir->obj_ops.put_ref(mkdir); lookup->obj_ops.put_ref(lookup); /* Remove directory created while running test */ status = fsal_remove(root_entry, TEST_ROOT); ASSERT_EQ(status.major, 0); } TEST_F(MkdirEmptyLatencyTest, LOOP) { fsal_status_t status; char fname[NAMELEN]; struct fsal_obj_handle *obj; struct timespec s_time, e_time; now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "d-%08x", i); status = root_entry->obj_ops.mkdir(root_entry, fname, &attrs, &obj, NULL); EXPECT_EQ(status.major, 0); obj->obj_ops.put_ref(obj); } now(&e_time); fprintf(stderr, "Average time per mkdir: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); /* Remove directories created while running test */ for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "d-%08x", i); status = fsal_remove(root_entry, fname); ASSERT_EQ(status.major, 0); } } TEST_F(MkdirEmptyLatencyTest, FSALCREATE) { fsal_status_t status; char fname[NAMELEN]; struct fsal_obj_handle *obj; struct timespec s_time, e_time; now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "d-%08x", i); status = fsal_create(root_entry, fname, DIRECTORY, &attrs, NULL, &obj, NULL); EXPECT_EQ(status.major, 0); obj->obj_ops.put_ref(obj); } now(&e_time); fprintf(stderr, "Average time per fsal_create: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); /* Remove directories created while running test */ for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "d-%08x", i); status = fsal_remove(root_entry, fname); ASSERT_EQ(status.major, 0); } } TEST_F(MkdirFullLatencyTest, BIG) { fsal_status_t status; char fname[NAMELEN]; struct fsal_obj_handle *obj; struct timespec s_time, e_time; now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "d-%08x", i); status = root_entry->obj_ops.mkdir(root_entry, fname, &attrs, &obj, NULL); ASSERT_EQ(status.major, 0) << " failed to mkdir " << fname; obj->obj_ops.put_ref(obj); } now(&e_time); fprintf(stderr, "Average time per mkdir: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); /* Remove directories created while running test */ for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "d-%08x", i); status = fsal_remove(root_entry, fname); ASSERT_EQ(status.major, 0); } } TEST_F(MkdirFullLatencyTest, BIG_BYPASS) { fsal_status_t status; char fname[NAMELEN]; struct fsal_obj_handle *sub_hdl; struct fsal_obj_handle *obj; struct timespec s_time, e_time; sub_hdl = mdcdb_get_sub_handle(root_entry); ASSERT_NE(sub_hdl, nullptr); status = nfs_export_get_root_entry(a_export, &sub_hdl); ASSERT_EQ(status.major, 0); now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "d-%08x", i); status = sub_hdl->obj_ops.mkdir(sub_hdl, fname, &attrs, &obj, NULL); ASSERT_EQ(status.major, 0) << " failed to mkdir " << fname; obj->obj_ops.put_ref(obj); } now(&e_time); fprintf(stderr, "Average time per mkdir: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); /* Remove directories created while running test */ for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "d-%08x", i); status = fsal_remove(root_entry, fname); ASSERT_EQ(status.major, 0); } } int main(int argc, char *argv[]) { int code = 0; using namespace std; using namespace std::literals; namespace po = boost::program_options; po::options_description opts("program options"); po::variables_map vm; try { opts.add_options() ("config", po::value(), "path to Ganesha conf file") ("logfile", po::value(), "log to the provided file path") ("export", po::value(), "id of export on which to operate (must exist)") ("debug", po::value(), "ganesha debug level") ; po::variables_map::iterator vm_iter; po::command_line_parser parser{argc, argv}; parser.options(opts).allow_unregistered(); po::store(parser.run(), vm); po::notify(vm); // use config vars--leaves them on the stack vm_iter = vm.find("config"); if (vm_iter != vm.end()) { ganesha_conf = (char*) vm_iter->second.as().c_str(); } vm_iter = vm.find("logfile"); if (vm_iter != vm.end()) { lpath = (char*) vm_iter->second.as().c_str(); } vm_iter = vm.find("debug"); if (vm_iter != vm.end()) { dlevel = ReturnLevelAscii( (char*) vm_iter->second.as().c_str()); } vm_iter = vm.find("export"); if (vm_iter != vm.end()) { export_id = vm_iter->second.as(); } ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(new Environment); code = RUN_ALL_TESTS(); } catch(po::error& e) { cout << "Error parsing opts " << e.what() << endl; } catch(...) { cout << "Unhandled exception in main()" << endl; } return code; } nfs-ganesha-2.6.0/src/gtest/test_readlink_latency.cc000066400000000000000000000244501324272410200225060ustar00rootroot00000000000000// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab /* * Copyright (C) 2018 Red Hat, Inc. * Contributor : Girjesh Rajoria * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ #include #include #include #include #include #include #include #include #include "gtest/gtest.h" #include #include #include extern "C" { /* Manually forward this, an 9P is not C++ safe */ void admin_halt(void); /* Ganesha headers */ #include "nfs_lib.h" #include "export_mgr.h" #include "nfs_exports.h" #include "sal_data.h" #include "fsal.h" #include "common_utils.h" /* For MDCACHE bypass. Use with care */ #include "../FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_debug.h" } #define TEST_ROOT "readlink_latency" #define TEST_ROOT_LINK "symlink_to_readlink_latency" #define DIR_COUNT 100000 #define LOOP_COUNT 1000000 #define NAMELEN 16 namespace { char* ganesha_conf = nullptr; char* lpath = nullptr; int dlevel = -1; uint16_t export_id = 77; int ganesha_server() { /* XXX */ return nfs_libmain( ganesha_conf, lpath, dlevel ); } class Environment : public ::testing::Environment { public: Environment() : ganesha(ganesha_server) { using namespace std::literals; std::this_thread::sleep_for(5s); } virtual ~Environment() { admin_halt(); ganesha.join(); } virtual void SetUp() { } virtual void TearDown() { } std::thread ganesha; }; class ReadlinkEmptyLatencyTest : public ::testing::Test { protected: virtual void SetUp() { fsal_status_t status; struct attrlist attrs_out; a_export = get_gsh_export(export_id); ASSERT_NE(a_export, nullptr); status = nfs_export_get_root_entry(a_export, &root_entry); ASSERT_EQ(status.major, 0); ASSERT_NE(root_entry, nullptr); /* Ganesha call paths need real or forged context info */ memset(&user_credentials, 0, sizeof(struct user_cred)); memset(&req_ctx, 0, sizeof(struct req_op_context)); memset(&attrs, 0, sizeof(attrs)); req_ctx.ctx_export = a_export; req_ctx.fsal_export = a_export->fsal_export; req_ctx.creds = &user_credentials; /* stashed in tls */ op_ctx = &req_ctx; // create root directory for test FSAL_SET_MASK(attrs.valid_mask, ATTR_MODE | ATTR_OWNER | ATTR_GROUP); attrs.mode = 0777; /* XXX */ attrs.owner = 667; attrs.group = 766; fsal_prepare_attrs(&attrs_out, 0); status = fsal_create(root_entry, TEST_ROOT, DIRECTORY, &attrs, NULL, &test_root, &attrs_out); ASSERT_EQ(status.major, 0); ASSERT_NE(test_root, nullptr); status = fsal_create(root_entry, TEST_ROOT_LINK, SYMBOLIC_LINK, &attrs, TEST_ROOT, &symlink_test_root, &attrs_out); ASSERT_EQ(status.major, 0); ASSERT_NE(symlink_test_root, nullptr); status = fsal_readlink(symlink_test_root, &bfr_content); ASSERT_EQ(status.major, 0); fsal_release_attrs(&attrs_out); } virtual void TearDown() { fsal_status_t status; status = symlink_test_root->obj_ops.unlink(root_entry, symlink_test_root, TEST_ROOT_LINK); EXPECT_EQ(0, status.major); symlink_test_root->obj_ops.put_ref(symlink_test_root); symlink_test_root = NULL; status = test_root->obj_ops.unlink(root_entry, test_root, TEST_ROOT); EXPECT_EQ(0, status.major); test_root->obj_ops.put_ref(test_root); test_root = NULL; root_entry->obj_ops.put_ref(root_entry); root_entry = NULL; put_gsh_export(a_export); a_export = NULL; } struct req_op_context req_ctx; struct user_cred user_credentials; struct attrlist attrs; struct gsh_export* a_export = nullptr; struct fsal_obj_handle *root_entry = nullptr; struct fsal_obj_handle *test_root = nullptr; struct fsal_obj_handle *symlink_test_root = nullptr; struct gsh_buffdesc bfr_content; }; class ReadlinkFullLatencyTest : public ReadlinkEmptyLatencyTest { protected: virtual void SetUp() { fsal_status_t status; char fname[NAMELEN]; struct fsal_obj_handle *obj; struct attrlist attrs_out; ReadlinkEmptyLatencyTest::SetUp(); /* create a bunch of dirents */ for (int i = 0; i < DIR_COUNT; ++i) { fsal_prepare_attrs(&attrs_out, 0); sprintf(fname, "d-%08x", i); status = fsal_create(test_root, fname, REGULAR_FILE, &attrs, NULL, &obj, &attrs_out); ASSERT_EQ(status.major, 0); ASSERT_NE(obj, nullptr); fsal_release_attrs(&attrs_out); obj->obj_ops.put_ref(obj); } } virtual void TearDown() { fsal_status_t status; char fname[NAMELEN]; for (int i = 0; i < DIR_COUNT; ++i) { sprintf(fname, "d-%08x", i); status = fsal_remove(test_root, fname); EXPECT_EQ(status.major, 0); } ReadlinkEmptyLatencyTest::TearDown(); } }; } /* namespace */ TEST_F(ReadlinkEmptyLatencyTest, SIMPLE) { fsal_status_t status; struct gsh_buffdesc link_content; int ret = -1; status = symlink_test_root->obj_ops.readlink(symlink_test_root, &link_content, false); EXPECT_EQ(status.major, 0); if(link_content.len == bfr_content.len) ret = memcmp(link_content.addr, bfr_content.addr, link_content.len); EXPECT_EQ(ret, 0); } TEST_F(ReadlinkEmptyLatencyTest, SIMPLE_BYPASS) { fsal_status_t status; struct fsal_obj_handle *sub_hdl; struct gsh_buffdesc link_content; int ret = -1; sub_hdl = mdcdb_get_sub_handle(symlink_test_root); ASSERT_NE(sub_hdl, nullptr); status = sub_hdl->obj_ops.readlink(sub_hdl, &link_content, false); EXPECT_EQ(status.major, 0); if(link_content.len == bfr_content.len) ret = memcmp(link_content.addr, bfr_content.addr, link_content.len); EXPECT_EQ(ret, 0); } TEST_F(ReadlinkEmptyLatencyTest, LOOP) { fsal_status_t status; struct gsh_buffdesc link_content; struct timespec s_time, e_time; now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { status = symlink_test_root->obj_ops.readlink(symlink_test_root, &link_content, false); EXPECT_EQ(status.major, 0); } now(&e_time); fprintf(stderr, "Average time per readlink: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); } TEST_F(ReadlinkEmptyLatencyTest, FSALREADLINK) { fsal_status_t status; struct gsh_buffdesc link_content; struct timespec s_time, e_time; now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { status = fsal_readlink(symlink_test_root, &link_content); EXPECT_EQ(status.major, 0); } now(&e_time); fprintf(stderr, "Average time per fsal_readlink: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); } TEST_F(ReadlinkFullLatencyTest, BIG) { fsal_status_t status; struct gsh_buffdesc link_content; struct timespec s_time, e_time; now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { status = symlink_test_root->obj_ops.readlink(symlink_test_root, &link_content, false); ASSERT_EQ(status.major, 0) << " failed to readlink " << TEST_ROOT_LINK; } now(&e_time); fprintf(stderr, "Average time per readlink: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); } TEST_F(ReadlinkFullLatencyTest, BIG_BYPASS) { fsal_status_t status; struct fsal_obj_handle *sub_hdl; struct gsh_buffdesc link_content; struct timespec s_time, e_time; sub_hdl = mdcdb_get_sub_handle(symlink_test_root); now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { status = sub_hdl->obj_ops.readlink(sub_hdl, &link_content, false); ASSERT_EQ(status.major, 0) << " failed to readlink " << TEST_ROOT_LINK; } now(&e_time); fprintf(stderr, "Average time per readlink: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); } int main(int argc, char *argv[]) { int code = 0; using namespace std; using namespace std::literals; namespace po = boost::program_options; po::options_description opts("program options"); po::variables_map vm; try { opts.add_options() ("config", po::value(), "path to Ganesha conf file") ("logfile", po::value(), "log to the provided file path") ("export", po::value(), "id of export on which to operate (must exist)") ("debug", po::value(), "ganesha debug level") ; po::variables_map::iterator vm_iter; po::command_line_parser parser{argc, argv}; parser.options(opts).allow_unregistered(); po::store(parser.run(), vm); po::notify(vm); // use config vars--leaves them on the stack vm_iter = vm.find("config"); if (vm_iter != vm.end()) { ganesha_conf = (char*) vm_iter->second.as().c_str(); } vm_iter = vm.find("logfile"); if (vm_iter != vm.end()) { lpath = (char*) vm_iter->second.as().c_str(); } vm_iter = vm.find("debug"); if (vm_iter != vm.end()) { dlevel = ReturnLevelAscii( (char*) vm_iter->second.as().c_str()); } vm_iter = vm.find("export"); if (vm_iter != vm.end()) { export_id = vm_iter->second.as(); } ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(new Environment); code = RUN_ALL_TESTS(); } catch(po::error& e) { cout << "Error parsing opts " << e.what() << endl; } catch(...) { cout << "Unhandled exception in main()" << endl; } return code; } nfs-ganesha-2.6.0/src/gtest/test_rename_latency.cc000066400000000000000000000311361324272410200221630ustar00rootroot00000000000000// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab /* * Copyright (C) 2018 Red Hat, Inc. * Contributor : Girjesh Rajoria * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ #include #include #include #include #include #include #include #include "gtest/gtest.h" #include #include #include extern "C" { /* Manually forward this, as 9P is not C++ safe */ void admin_halt(void); /* Ganesha headers */ #include "nfs_lib.h" #include "export_mgr.h" #include "nfs_exports.h" #include "sal_data.h" #include "fsal.h" #include "common_utils.h" /* For MDCACHE bypass. Use with care */ #include "../FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_debug.h" } #define TEST_ROOT "test_root" #define TEST_FILE "original_name" #define TEST_FILE_NEW "new_name" #define DIR_COUNT 100000 #define LOOP_COUNT 1000000 #define NAMELEN 16 namespace { char* ganesha_conf = nullptr; char* lpath = nullptr; int dlevel = -1; uint16_t export_id = 77; int ganesha_server() { /* XXX */ return nfs_libmain( ganesha_conf, lpath, dlevel ); } class Environment : public ::testing::Environment { public: Environment() : ganesha(ganesha_server) { using namespace std::literals; std::this_thread::sleep_for(5s); } virtual ~Environment() { admin_halt(); ganesha.join(); } virtual void SetUp() { } virtual void TearDown() { } std::thread ganesha; }; class RenameEmptyLatencyTest : public ::testing::Test { protected: virtual void SetUp() { fsal_status_t status; struct attrlist attrs_out; a_export = get_gsh_export(export_id); ASSERT_NE(a_export, nullptr); status = nfs_export_get_root_entry(a_export, &root_entry); ASSERT_EQ(status.major, 0); ASSERT_NE(root_entry, nullptr); /* Ganesha call paths need real or forged context info */ memset(&user_credentials, 0, sizeof(struct user_cred)); memset(&req_ctx, 0, sizeof(struct req_op_context)); memset(&attrs, 0, sizeof(attrs)); req_ctx.ctx_export = a_export; req_ctx.fsal_export = a_export->fsal_export; req_ctx.creds = &user_credentials; /* stashed in tls */ op_ctx = &req_ctx; // create root directory for test FSAL_SET_MASK(attrs.valid_mask, ATTR_MODE | ATTR_OWNER | ATTR_GROUP); attrs.mode = 0777; /* XXX */ attrs.owner = 667; attrs.group = 766; fsal_prepare_attrs(&attrs_out, 0); status = fsal_create(root_entry, TEST_ROOT, DIRECTORY, &attrs, NULL, &test_root, &attrs_out); ASSERT_EQ(status.major, 0); ASSERT_NE(test_root, nullptr); fsal_release_attrs(&attrs_out); } virtual void TearDown() { fsal_status_t status; status = test_root->obj_ops.unlink(root_entry, test_root, TEST_ROOT); EXPECT_EQ(0, status.major); test_root->obj_ops.put_ref(test_root); test_root = NULL; root_entry->obj_ops.put_ref(root_entry); root_entry = NULL; put_gsh_export(a_export); a_export = NULL; } struct req_op_context req_ctx; struct user_cred user_credentials; struct attrlist attrs; struct gsh_export* a_export = nullptr; struct fsal_obj_handle *root_entry = nullptr; struct fsal_obj_handle *test_root = nullptr; }; class RenameFullLatencyTest : public RenameEmptyLatencyTest { protected: virtual void SetUp() { fsal_status_t status; char fname[NAMELEN]; struct fsal_obj_handle *obj; struct attrlist attrs_out; RenameEmptyLatencyTest::SetUp(); /* create a bunch of dirents */ for (int i = 0; i < DIR_COUNT; ++i) { fsal_prepare_attrs(&attrs_out, 0); sprintf(fname, "file-%08x", i); status = fsal_create(test_root, fname, REGULAR_FILE, &attrs, NULL, &obj, &attrs_out); ASSERT_EQ(status.major, 0); ASSERT_NE(obj, nullptr); fsal_release_attrs(&attrs_out); obj->obj_ops.put_ref(obj); } } virtual void TearDown() { fsal_status_t status; char fname[NAMELEN]; for (int i = 0; i < DIR_COUNT; ++i) { sprintf(fname, "file-%08x", i); status = fsal_remove(test_root, fname); EXPECT_EQ(status.major, 0); } RenameEmptyLatencyTest::TearDown(); } }; } /* namespace */ TEST_F(RenameEmptyLatencyTest, SIMPLE) { fsal_status_t status; struct fsal_obj_handle *obj = nullptr; struct fsal_obj_handle *lookup = nullptr; /* Create file for the the test */ status = fsal_create(test_root, TEST_FILE, REGULAR_FILE, &attrs, NULL, &obj, NULL); ASSERT_EQ(status.major, 0); ASSERT_NE(obj, nullptr); status = test_root->obj_ops.rename(obj, test_root, TEST_FILE, test_root, TEST_FILE_NEW); EXPECT_EQ(status.major, 0); test_root->obj_ops.lookup(test_root, TEST_FILE_NEW, &lookup, NULL); EXPECT_EQ(lookup, obj); lookup->obj_ops.put_ref(lookup); obj->obj_ops.put_ref(obj); /* Delete file created for the test */ status = fsal_remove(test_root, TEST_FILE_NEW); ASSERT_EQ(status.major, 0); } TEST_F(RenameEmptyLatencyTest, SIMPLE_BYPASS) { fsal_status_t status; struct fsal_obj_handle *obj = nullptr; struct fsal_obj_handle *sub_hdl = nullptr; struct fsal_obj_handle *sub_hdl_obj = nullptr; struct fsal_obj_handle *lookup = nullptr; /* Create file for the the test */ status = fsal_create(test_root, TEST_FILE, REGULAR_FILE, &attrs, NULL, &obj, NULL); ASSERT_EQ(status.major, 0); ASSERT_NE(obj, nullptr); sub_hdl = mdcdb_get_sub_handle(test_root); ASSERT_NE(sub_hdl, nullptr); sub_hdl_obj = mdcdb_get_sub_handle(obj); ASSERT_NE(sub_hdl_obj, nullptr); status = sub_hdl->obj_ops.rename(sub_hdl_obj, sub_hdl, TEST_FILE, sub_hdl, TEST_FILE_NEW); EXPECT_EQ(status.major, 0); sub_hdl->obj_ops.lookup(sub_hdl, TEST_FILE_NEW, &lookup, NULL); EXPECT_EQ(lookup, mdcdb_get_sub_handle(obj)); obj->obj_ops.put_ref(obj); /* Delete file created for the test */ status = fsal_remove(test_root, TEST_FILE_NEW); ASSERT_EQ(status.major, 0); } TEST_F(RenameEmptyLatencyTest, LOOP) { fsal_status_t status; struct fsal_obj_handle *obj; char fname[NAMELEN] = TEST_FILE; char fname_new[NAMELEN]; struct timespec s_time, e_time; /* Create file for the the test */ status = fsal_create(test_root, TEST_FILE, REGULAR_FILE, &attrs, NULL, &obj, NULL); ASSERT_EQ(status.major, 0); ASSERT_NE(obj, nullptr); now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname_new, "d-%08x", i); status = test_root->obj_ops.rename(obj, test_root, fname, test_root, fname_new); EXPECT_EQ(status.major, 0); strncpy(fname, fname_new, NAMELEN); } now(&e_time); fprintf(stderr, "Average time per rename: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); obj->obj_ops.put_ref(obj); /* Delete file created for the test */ status = fsal_remove(test_root, fname); ASSERT_EQ(status.major, 0); } TEST_F(RenameEmptyLatencyTest, FSALRENAME) { fsal_status_t status; struct fsal_obj_handle *obj; char fname[NAMELEN] = TEST_FILE; char fname_new[NAMELEN]; struct timespec s_time, e_time; /* Create file for the the test */ status = fsal_create(test_root, TEST_FILE, REGULAR_FILE, &attrs, NULL, &obj, NULL); ASSERT_EQ(status.major, 0); ASSERT_NE(obj, nullptr); now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname_new, "d-%08x", i); status = fsal_rename(test_root, fname, test_root, fname_new); EXPECT_EQ(status.major, 0); strncpy(fname, fname_new, NAMELEN); } now(&e_time); fprintf(stderr, "Average time per fsal_rename: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); obj->obj_ops.put_ref(obj); /* Delete file created for the test */ status = fsal_remove(test_root, fname); ASSERT_EQ(status.major, 0); } TEST_F(RenameFullLatencyTest, BIG) { fsal_status_t status; struct fsal_obj_handle *obj; char fname[NAMELEN] = TEST_FILE; char fname_new[NAMELEN]; struct timespec s_time, e_time; /* Create file for the the test */ status = fsal_create(test_root, TEST_FILE, REGULAR_FILE, &attrs, NULL, &obj, NULL); ASSERT_EQ(status.major, 0); ASSERT_NE(obj, nullptr); now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname_new, "d-%08x", i); status = test_root->obj_ops.rename(obj, test_root, fname, test_root, fname_new); ASSERT_EQ(status.major, 0) << " failed to rename " << fname; strncpy(fname, fname_new, NAMELEN); } now(&e_time); fprintf(stderr, "Average time per rename: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); obj->obj_ops.put_ref(obj); /* Delete file created for the test */ status = fsal_remove(test_root, fname); ASSERT_EQ(status.major, 0); } TEST_F(RenameFullLatencyTest, BIG_BYPASS) { fsal_status_t status; struct fsal_obj_handle *obj; char fname[NAMELEN] = TEST_FILE; char fname_new[NAMELEN]; struct fsal_obj_handle *sub_hdl; struct fsal_obj_handle *sub_hdl_obj; struct timespec s_time, e_time; /* Create file for the the test */ status = fsal_create(test_root, TEST_FILE, REGULAR_FILE, &attrs, NULL, &obj, NULL); ASSERT_EQ(status.major, 0); ASSERT_NE(obj, nullptr); sub_hdl = mdcdb_get_sub_handle(test_root); ASSERT_NE(sub_hdl, nullptr); sub_hdl_obj = mdcdb_get_sub_handle(obj); ASSERT_NE(sub_hdl_obj, nullptr); now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname_new, "d-%08x", i); status = sub_hdl->obj_ops.rename(sub_hdl_obj, sub_hdl, fname, sub_hdl, fname_new); ASSERT_EQ(status.major, 0) << " failed to rename " << fname; strncpy(fname, fname_new, NAMELEN); } now(&e_time); fprintf(stderr, "Average time per rename: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); obj->obj_ops.put_ref(obj); /* Delete file created for the test */ status = fsal_remove(test_root, fname); ASSERT_EQ(status.major, 0); } int main(int argc, char *argv[]) { int code = 0; using namespace std; using namespace std::literals; namespace po = boost::program_options; po::options_description opts("program options"); po::variables_map vm; try { opts.add_options() ("config", po::value(), "path to Ganesha conf file") ("logfile", po::value(), "log to the provided file path") ("export", po::value(), "id of export on which to operate (must exist)") ("debug", po::value(), "ganesha debug level") ; po::variables_map::iterator vm_iter; po::command_line_parser parser{argc, argv}; parser.options(opts).allow_unregistered(); po::store(parser.run(), vm); po::notify(vm); // use config vars--leaves them on the stack vm_iter = vm.find("config"); if (vm_iter != vm.end()) { ganesha_conf = (char*) vm_iter->second.as().c_str(); } vm_iter = vm.find("logfile"); if (vm_iter != vm.end()) { lpath = (char*) vm_iter->second.as().c_str(); } vm_iter = vm.find("debug"); if (vm_iter != vm.end()) { dlevel = ReturnLevelAscii( (char*) vm_iter->second.as().c_str()); } vm_iter = vm.find("export"); if (vm_iter != vm.end()) { export_id = vm_iter->second.as(); } ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(new Environment); code = RUN_ALL_TESTS(); } catch(po::error& e) { cout << "Error parsing opts " << e.what() << endl; } catch(...) { cout << "Unhandled exception in main()" << endl; } return code; } nfs-ganesha-2.6.0/src/gtest/test_symlink_latency.cc000066400000000000000000000311541324272410200224020ustar00rootroot00000000000000// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab /* * Copyright (C) 2018 Red Hat, Inc. * Contributor : Girjesh Rajoria * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ #include #include #include #include #include #include #include #include "gtest/gtest.h" #include #include #include extern "C" { /* Manually forward this, as 9P is not C++ safe */ void admin_halt(void); /* Ganesha headers */ #include "nfs_lib.h" #include "export_mgr.h" #include "nfs_exports.h" #include "sal_data.h" #include "fsal.h" #include "common_utils.h" /* For MDCACHE bypass. Use with care */ #include "../FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_debug.h" } #define TEST_ROOT "symlink_latency" #define TEST_ROOT_LINK "symlink_to_symlink_latency" #define TEST_SYMLINK "test_symlink" #define DIR_COUNT 100000 #define LOOP_COUNT 1000000 #define NAMELEN 16 namespace { char* ganesha_conf = nullptr; char* lpath = nullptr; int dlevel = -1; uint16_t export_id = 77; int ganesha_server() { /* XXX */ return nfs_libmain( ganesha_conf, lpath, dlevel ); } class Environment : public ::testing::Environment { public: Environment() : ganesha(ganesha_server) { using namespace std::literals; std::this_thread::sleep_for(5s); } virtual ~Environment() { admin_halt(); ganesha.join(); } virtual void SetUp() { } virtual void TearDown() { } std::thread ganesha; }; class SymlinkEmptyLatencyTest : public ::testing::Test { protected: virtual void SetUp() { fsal_status_t status; struct attrlist attrs_out; a_export = get_gsh_export(export_id); ASSERT_NE(a_export, nullptr); status = nfs_export_get_root_entry(a_export, &root_entry); ASSERT_EQ(status.major, 0); ASSERT_NE(root_entry, nullptr); /* Ganesha call paths need real or forged context info */ memset(&user_credentials, 0, sizeof(struct user_cred)); memset(&req_ctx, 0, sizeof(struct req_op_context)); memset(&attrs, 0, sizeof(attrs)); req_ctx.ctx_export = a_export; req_ctx.fsal_export = a_export->fsal_export; req_ctx.creds = &user_credentials; /* stashed in tls */ op_ctx = &req_ctx; // create root directory for test FSAL_SET_MASK(attrs.valid_mask, ATTR_MODE | ATTR_OWNER | ATTR_GROUP); attrs.mode = 0777; /* XXX */ attrs.owner = 667; attrs.group = 766; fsal_prepare_attrs(&attrs_out, 0); status = fsal_create(root_entry, TEST_ROOT, DIRECTORY, &attrs, NULL, &test_root, &attrs_out); ASSERT_EQ(status.major, 0); ASSERT_NE(test_root, nullptr); status = fsal_create(root_entry, TEST_SYMLINK, SYMBOLIC_LINK, &attrs, TEST_ROOT, &test_symlink, &attrs_out); ASSERT_EQ(status.major, 0); ASSERT_NE(test_symlink, nullptr); status = fsal_readlink(test_symlink, &bfr_content); ASSERT_EQ(status.major, 0); fsal_release_attrs(&attrs_out); } virtual void TearDown() { fsal_status_t status; status = test_symlink->obj_ops.unlink(root_entry, test_symlink, TEST_SYMLINK); EXPECT_EQ(0, status.major); test_symlink->obj_ops.put_ref(test_symlink); test_symlink = NULL; status = test_root->obj_ops.unlink(root_entry, test_root, TEST_ROOT); EXPECT_EQ(0, status.major); test_root->obj_ops.put_ref(test_root); test_root = NULL; root_entry->obj_ops.put_ref(root_entry); root_entry = NULL; put_gsh_export(a_export); a_export = NULL; } struct req_op_context req_ctx; struct user_cred user_credentials; struct attrlist attrs; struct gsh_export* a_export = nullptr; struct fsal_obj_handle *root_entry = nullptr; struct fsal_obj_handle *test_root = nullptr; struct fsal_obj_handle *test_symlink = nullptr; struct gsh_buffdesc bfr_content; }; class SymlinkFullLatencyTest : public SymlinkEmptyLatencyTest { protected: virtual void SetUp() { fsal_status_t status; char fname[NAMELEN]; struct fsal_obj_handle *obj; struct attrlist attrs_out; SymlinkEmptyLatencyTest::SetUp(); /* create a bunch of dirents */ for (int i = 0; i < DIR_COUNT; ++i) { fsal_prepare_attrs(&attrs_out, 0); sprintf(fname, "file-%08x", i); status = fsal_create(root_entry, fname, REGULAR_FILE, &attrs, NULL, &obj, &attrs_out); ASSERT_EQ(status.major, 0); ASSERT_NE(obj, nullptr); fsal_release_attrs(&attrs_out); obj->obj_ops.put_ref(obj); } } virtual void TearDown() { fsal_status_t status; char fname[NAMELEN]; for (int i = 0; i < DIR_COUNT; ++i) { sprintf(fname, "file-%08x", i); status = fsal_remove(root_entry, fname); EXPECT_EQ(status.major, 0); } SymlinkEmptyLatencyTest::TearDown(); } }; } /* namespace */ TEST_F(SymlinkEmptyLatencyTest, SIMPLE) { fsal_status_t status; struct fsal_obj_handle *symlink; struct fsal_obj_handle *lookup; struct gsh_buffdesc link_content; int ret = -1; status = root_entry->obj_ops.symlink(root_entry, TEST_ROOT_LINK, TEST_ROOT, &attrs, &symlink, NULL); EXPECT_EQ(status.major, 0); root_entry->obj_ops.lookup(root_entry, TEST_ROOT_LINK, &lookup, NULL); EXPECT_EQ(lookup, symlink); status = symlink->obj_ops.readlink(symlink, &link_content, false); EXPECT_EQ(status.major, 0); if(link_content.len == bfr_content.len) ret = memcmp(link_content.addr, bfr_content.addr, link_content.len); EXPECT_EQ(ret, 0); symlink->obj_ops.put_ref(symlink); lookup->obj_ops.put_ref(lookup); /* Remove symlink created while running test */ status = fsal_remove(root_entry, TEST_ROOT_LINK); ASSERT_EQ(status.major, 0); } TEST_F(SymlinkEmptyLatencyTest, SIMPLE_BYPASS) { fsal_status_t status; struct fsal_obj_handle *sub_hdl; struct fsal_obj_handle *symlink; struct fsal_obj_handle *lookup; struct gsh_buffdesc link_content; int ret = -1; sub_hdl = mdcdb_get_sub_handle(root_entry); ASSERT_NE(sub_hdl, nullptr); status = nfs_export_get_root_entry(a_export, &sub_hdl); ASSERT_EQ(status.major, 0); status = sub_hdl->obj_ops.symlink(sub_hdl, TEST_ROOT_LINK, TEST_ROOT, &attrs, &symlink, NULL); EXPECT_EQ(status.major, 0); root_entry->obj_ops.lookup(root_entry, TEST_ROOT_LINK, &lookup, NULL); EXPECT_EQ(lookup, symlink); status = symlink->obj_ops.readlink(symlink, &link_content, false); EXPECT_EQ(status.major, 0); if(link_content.len == bfr_content.len) ret = memcmp(link_content.addr, bfr_content.addr, link_content.len); EXPECT_EQ(ret, 0); symlink->obj_ops.put_ref(symlink); lookup->obj_ops.put_ref(lookup); /* Remove symlink created while running test */ status = fsal_remove(root_entry, TEST_ROOT_LINK); ASSERT_EQ(status.major, 0); } TEST_F(SymlinkEmptyLatencyTest, LOOP) { fsal_status_t status; char fname[NAMELEN]; struct fsal_obj_handle *obj; struct timespec s_time, e_time; now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "d-%08x", i); status = root_entry->obj_ops.symlink(root_entry, fname, TEST_ROOT, &attrs, &obj, NULL); EXPECT_EQ(status.major, 0); obj->obj_ops.put_ref(obj); } now(&e_time); fprintf(stderr, "Average time per symlink: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); /* Remove symlink created while running test */ for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "d-%08x", i); status = fsal_remove(root_entry, fname); ASSERT_EQ(status.major, 0); } } TEST_F(SymlinkEmptyLatencyTest, FSALCREATE) { fsal_status_t status; char fname[NAMELEN]; struct fsal_obj_handle *obj; struct timespec s_time, e_time; now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "d-%08x", i); status = fsal_create(root_entry, fname, SYMBOLIC_LINK, &attrs, TEST_ROOT, &obj, NULL); EXPECT_EQ(status.major, 0); obj->obj_ops.put_ref(obj); } now(&e_time); fprintf(stderr, "Average time per fsal_create: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); /* Remove symlink created while running test */ for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "d-%08x", i); status = fsal_remove(root_entry, fname); ASSERT_EQ(status.major, 0); } } TEST_F(SymlinkFullLatencyTest, BIG) { fsal_status_t status; char fname[NAMELEN]; struct fsal_obj_handle *obj; struct timespec s_time, e_time; now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "d-%08x", i); status = root_entry->obj_ops.symlink(root_entry, fname, TEST_ROOT, &attrs, &obj, NULL); ASSERT_EQ(status.major, 0) << " failed to symlink " << fname; obj->obj_ops.put_ref(obj); } now(&e_time); fprintf(stderr, "Average time per symlink: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); /* Remove symlink created while running test */ for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "d-%08x", i); status = fsal_remove(root_entry, fname); ASSERT_EQ(status.major, 0); } } TEST_F(SymlinkFullLatencyTest, BIG_BYPASS) { fsal_status_t status; char fname[NAMELEN]; struct fsal_obj_handle *sub_hdl; struct fsal_obj_handle *obj; struct timespec s_time, e_time; sub_hdl = mdcdb_get_sub_handle(root_entry); ASSERT_NE(sub_hdl, nullptr); status = nfs_export_get_root_entry(a_export, &sub_hdl); ASSERT_EQ(status.major, 0); now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "d-%08x", i); status = sub_hdl->obj_ops.symlink(sub_hdl, fname, TEST_ROOT, &attrs, &obj, NULL); ASSERT_EQ(status.major, 0) << " failed to symlink " << fname; obj->obj_ops.put_ref(obj); } now(&e_time); fprintf(stderr, "Average time per symlink: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); /* Remove symlink created while running test */ for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "d-%08x", i); status = fsal_remove(root_entry, fname); ASSERT_EQ(status.major, 0); } } int main(int argc, char *argv[]) { int code = 0; using namespace std; using namespace std::literals; namespace po = boost::program_options; po::options_description opts("program options"); po::variables_map vm; try { opts.add_options() ("config", po::value(), "path to Ganesha conf file") ("logfile", po::value(), "log to the provided file path") ("export", po::value(), "id of export on which to operate (must exist)") ("debug", po::value(), "ganesha debug level") ; po::variables_map::iterator vm_iter; po::command_line_parser parser{argc, argv}; parser.options(opts).allow_unregistered(); po::store(parser.run(), vm); po::notify(vm); // use config vars--leaves them on the stack vm_iter = vm.find("config"); if (vm_iter != vm.end()) { ganesha_conf = (char*) vm_iter->second.as().c_str(); } vm_iter = vm.find("logfile"); if (vm_iter != vm.end()) { lpath = (char*) vm_iter->second.as().c_str(); } vm_iter = vm.find("debug"); if (vm_iter != vm.end()) { dlevel = ReturnLevelAscii( (char*) vm_iter->second.as().c_str()); } vm_iter = vm.find("export"); if (vm_iter != vm.end()) { export_id = vm_iter->second.as(); } ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(new Environment); code = RUN_ALL_TESTS(); } catch(po::error& e) { cout << "Error parsing opts " << e.what() << endl; } catch(...) { cout << "Unhandled exception in main()" << endl; } return code; } nfs-ganesha-2.6.0/src/gtest/test_unlink_latency.cc000066400000000000000000000267541324272410200222260ustar00rootroot00000000000000// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab /* * Copyright (C) 2018 Red Hat, Inc. * Contributor : Girjesh Rajoria * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ #include #include #include #include #include #include #include #include "gtest/gtest.h" #include #include #include extern "C" { /* Manually forward this, as 9P is not C++ safe */ void admin_halt(void); /* Ganesha headers */ #include "nfs_lib.h" #include "export_mgr.h" #include "nfs_exports.h" #include "sal_data.h" #include "fsal.h" #include "common_utils.h" /* For MDCACHE bypass. Use with care */ #include "../FSAL/Stackable_FSALs/FSAL_MDCACHE/mdcache_debug.h" } #define TEST_ROOT "unlink_latency" #define TEST_FILE "unlink_test_file" #define DIR_COUNT 100000 #define LOOP_COUNT 1000000 #define NAMELEN 16 namespace { char* ganesha_conf = nullptr; char* lpath = nullptr; int dlevel = -1; uint16_t export_id = 77; int ganesha_server() { /* XXX */ return nfs_libmain( ganesha_conf, lpath, dlevel ); } class Environment : public ::testing::Environment { public: Environment() : ganesha(ganesha_server) { using namespace std::literals; std::this_thread::sleep_for(5s); } virtual ~Environment() { admin_halt(); ganesha.join(); } virtual void SetUp() { } virtual void TearDown() { } std::thread ganesha; }; class UnlinkEmptyLatencyTest : public ::testing::Test { protected: virtual void SetUp() { fsal_status_t status; struct attrlist attrs_out; a_export = get_gsh_export(export_id); ASSERT_NE(a_export, nullptr); status = nfs_export_get_root_entry(a_export, &root_entry); ASSERT_EQ(status.major, 0); ASSERT_NE(root_entry, nullptr); /* Ganesha call paths need real or forged context info */ memset(&user_credentials, 0, sizeof(struct user_cred)); memset(&req_ctx, 0, sizeof(struct req_op_context)); memset(&attrs, 0, sizeof(attrs)); req_ctx.ctx_export = a_export; req_ctx.fsal_export = a_export->fsal_export; req_ctx.creds = &user_credentials; /* stashed in tls */ op_ctx = &req_ctx; // create root directory for test FSAL_SET_MASK(attrs.valid_mask, ATTR_MODE | ATTR_OWNER | ATTR_GROUP); attrs.mode = 0777; /* XXX */ attrs.owner = 667; attrs.group = 766; fsal_prepare_attrs(&attrs_out, 0); status = fsal_create(root_entry, TEST_ROOT, DIRECTORY, &attrs, NULL, &test_root, &attrs_out); ASSERT_EQ(status.major, 0); ASSERT_NE(test_root, nullptr); fsal_release_attrs(&attrs_out); } virtual void TearDown() { fsal_status_t status; status = fsal_remove(root_entry, TEST_ROOT); EXPECT_EQ(status.major, 0); test_root->obj_ops.put_ref(test_root); test_root = NULL; root_entry->obj_ops.put_ref(root_entry); root_entry = NULL; put_gsh_export(a_export); a_export = NULL; } struct req_op_context req_ctx; struct user_cred user_credentials; struct attrlist attrs; struct gsh_export* a_export = nullptr; struct fsal_obj_handle *root_entry = nullptr; struct fsal_obj_handle *test_root = nullptr; }; class UnlinkFullLatencyTest : public UnlinkEmptyLatencyTest { protected: virtual void SetUp() { fsal_status_t status; char fname[NAMELEN]; struct fsal_obj_handle *obj; struct attrlist attrs_out; UnlinkEmptyLatencyTest::SetUp(); /* create a bunch of dirents */ for (int i = 0; i < DIR_COUNT; ++i) { fsal_prepare_attrs(&attrs_out, 0); sprintf(fname, "file-%08x", i); status = fsal_create(test_root, fname, REGULAR_FILE, &attrs, NULL, &obj, &attrs_out); ASSERT_EQ(status.major, 0); ASSERT_NE(obj, nullptr); fsal_release_attrs(&attrs_out); obj->obj_ops.put_ref(obj); } } virtual void TearDown() { fsal_status_t status; char fname[NAMELEN]; for (int i = 0; i < DIR_COUNT; ++i) { sprintf(fname, "file-%08x", i); status = fsal_remove(test_root, fname); EXPECT_EQ(status.major, 0); } UnlinkEmptyLatencyTest::TearDown(); } }; } /* namespace */ TEST_F(UnlinkEmptyLatencyTest, SIMPLE) { fsal_status_t status; struct fsal_obj_handle *obj = nullptr; struct fsal_obj_handle *lookup = nullptr; /* Create file to unlink for the test */ status = fsal_create(test_root, TEST_FILE, REGULAR_FILE, &attrs, NULL, &obj, NULL); ASSERT_EQ(status.major, 0); ASSERT_NE(obj, nullptr); status = test_root->obj_ops.unlink(test_root, obj, TEST_FILE); EXPECT_EQ(status.major, 0); status = test_root->obj_ops.lookup(test_root, TEST_FILE, &lookup, NULL); EXPECT_EQ(status.major, ERR_FSAL_NOENT); EXPECT_EQ(lookup, nullptr); obj->obj_ops.put_ref(obj); } TEST_F(UnlinkEmptyLatencyTest, SIMPLE_BYPASS) { fsal_status_t status; struct fsal_obj_handle *obj; struct fsal_obj_handle *sub_hdl = nullptr; struct fsal_obj_handle *sub_hdl_obj = nullptr; struct fsal_obj_handle *lookup = nullptr; /* Create files to unlink for the test */ status = fsal_create(test_root, TEST_FILE, REGULAR_FILE, &attrs, NULL, &obj, NULL); ASSERT_EQ(status.major, 0); ASSERT_NE(obj, nullptr); sub_hdl = mdcdb_get_sub_handle(test_root); ASSERT_NE(sub_hdl, nullptr); sub_hdl_obj = mdcdb_get_sub_handle(obj); ASSERT_NE(sub_hdl_obj, nullptr); status = sub_hdl->obj_ops.unlink(sub_hdl, sub_hdl_obj, TEST_FILE); EXPECT_EQ(status.major, 0); status = sub_hdl->obj_ops.lookup(sub_hdl, TEST_FILE, &lookup, NULL); EXPECT_EQ(status.major, ERR_FSAL_NOENT); EXPECT_EQ(lookup, nullptr); obj->obj_ops.put_ref(obj); } TEST_F(UnlinkEmptyLatencyTest, FSALREMOVE) { fsal_status_t status; char fname[NAMELEN]; struct fsal_obj_handle *obj; struct timespec s_time, e_time; /* Create files to unlink for the test */ status = fsal_create(test_root, TEST_FILE, REGULAR_FILE, &attrs, NULL, &obj, NULL); ASSERT_EQ(status.major, 0); ASSERT_NE(obj, nullptr); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "fl-%08x", i); status = test_root->obj_ops.link(obj, test_root, fname); ASSERT_EQ(status.major, 0); } now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "fl-%08x", i); status = fsal_remove(test_root, fname); EXPECT_EQ(status.major, 0); } now(&e_time); fprintf(stderr, "Average time per fsal_remove: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); /* Remove file created for running the test */ status = fsal_remove(test_root, TEST_FILE); ASSERT_EQ(status.major, 0); } TEST_F(UnlinkFullLatencyTest, BIG) { fsal_status_t status; char fname[NAMELEN]; struct fsal_obj_handle *obj; struct timespec s_time, e_time; /* Create files to unlink for the test */ status = fsal_create(test_root, TEST_FILE, REGULAR_FILE, &attrs, NULL, &obj, NULL); ASSERT_EQ(status.major, 0); ASSERT_NE(obj, nullptr); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "fl-%08x", i); status = test_root->obj_ops.link(obj, test_root, fname); ASSERT_EQ(status.major, 0); } now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "fl-%08x", i); status = test_root->obj_ops.unlink(test_root, obj, fname); EXPECT_EQ(status.major, 0); } now(&e_time); fprintf(stderr, "Average time per unlink: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); obj->obj_ops.put_ref(obj); /* Remove file created for running the test */ status = fsal_remove(test_root, TEST_FILE); ASSERT_EQ(status.major, 0); } TEST_F(UnlinkFullLatencyTest, BIG_BYPASS) { fsal_status_t status; char fname[NAMELEN]; struct fsal_obj_handle *obj; struct fsal_obj_handle *sub_hdl; struct fsal_obj_handle *sub_hdl_obj; struct timespec s_time, e_time; /* Create files to unlink for the test */ status = fsal_create(test_root, TEST_FILE, REGULAR_FILE, &attrs, NULL, &obj, NULL); ASSERT_EQ(status.major, 0); ASSERT_NE(obj, nullptr); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "fl-%08x", i); status = test_root->obj_ops.link(obj, test_root, fname); ASSERT_EQ(status.major, 0); } sub_hdl = mdcdb_get_sub_handle(test_root); ASSERT_NE(sub_hdl, nullptr); sub_hdl_obj = mdcdb_get_sub_handle(obj); ASSERT_NE(sub_hdl_obj, nullptr); now(&s_time); for (int i = 0; i < LOOP_COUNT; ++i) { sprintf(fname, "fl-%08x", i); status = sub_hdl->obj_ops.unlink(sub_hdl, sub_hdl_obj, fname); EXPECT_EQ(status.major, 0); } now(&e_time); fprintf(stderr, "Average time per unlink: %" PRIu64 " ns\n", timespec_diff(&s_time, &e_time) / LOOP_COUNT); /* Remove file created for running the test */ status = fsal_remove(test_root, TEST_FILE); ASSERT_EQ(status.major, 0); obj->obj_ops.put_ref(obj); } int main(int argc, char *argv[]) { int code = 0; using namespace std; using namespace std::literals; namespace po = boost::program_options; po::options_description opts("program options"); po::variables_map vm; try { opts.add_options() ("config", po::value(), "path to Ganesha conf file") ("logfile", po::value(), "log to the provided file path") ("export", po::value(), "id of export on which to operate (must exist)") ("debug", po::value(), "ganesha debug level") ; po::variables_map::iterator vm_iter; po::command_line_parser parser{argc, argv}; parser.options(opts).allow_unregistered(); po::store(parser.run(), vm); po::notify(vm); // use config vars--leaves them on the stack vm_iter = vm.find("config"); if (vm_iter != vm.end()) { ganesha_conf = (char*) vm_iter->second.as().c_str(); } vm_iter = vm.find("logfile"); if (vm_iter != vm.end()) { lpath = (char*) vm_iter->second.as().c_str(); } vm_iter = vm.find("debug"); if (vm_iter != vm.end()) { dlevel = ReturnLevelAscii( (char*) vm_iter->second.as().c_str()); } vm_iter = vm.find("export"); if (vm_iter != vm.end()) { export_id = vm_iter->second.as(); } ::testing::InitGoogleTest(&argc, argv); ::testing::AddGlobalTestEnvironment(new Environment); code = RUN_ALL_TESTS(); } catch(po::error& e) { cout << "Error parsing opts " << e.what() << endl; } catch(...) { cout << "Unhandled exception in main()" << endl; } return code; } nfs-ganesha-2.6.0/src/hashtable/000077500000000000000000000000001324272410200164305ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/hashtable/CMakeLists.txt000066400000000000000000000003161324272410200211700ustar00rootroot00000000000000########### next target ############### SET(hashtable_STAT_SRCS hashtable.c ) add_library(hashtable STATIC ${hashtable_STAT_SRCS}) add_sanitizers(hashtable) ########### install files ############### nfs-ganesha-2.6.0/src/hashtable/hashtable.c000066400000000000000000000742051324272410200205370ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @addtogroup hashtable * @{ */ /** * @file hashtable.c * @brief Implement an RBTree-based partitioend hash lookup * * This file implements a partitioned, tree-based, concurrent * hash-lookup structure. For every key, two values are derived that * determine its location within the structure: an index, which * determines which of the partitions (each containing a tree and each * separately locked), and a hash which acts as the key within an * individual Red-Black Tree. */ #include "config.h" #include #include #include #include "hashtable.h" #include "log.h" #include "abstract_atomic.h" #include "common_utils.h" #include /** * @brief Total size of the cache page configured for a table * * This function returns the size of the cache page for the given hash * table, based on the configured entry count. * * @param[in] ht The hash table to query * * @return The cache page size */ static inline size_t cache_page_size(const hash_table_t *ht) { return (ht->parameter.cache_entry_count) * sizeof(struct rbt_node *); } /** * @brief Offset of a pointer in the cache * * This function returns the offset into a cache array of the given * hash value. * * @param[in] ht The hash table to query * @param[in] rbthash The hash value to look up * * @return the offset into the cache at which the hash value might be * found */ static inline int cache_offsetof(struct hash_table *ht, uint64_t rbthash) { return rbthash % ht->parameter.cache_entry_count; } /** * @brief Return an error string for an error code * * This function returns an error string corresponding to the supplied * error code. * * @param[in] err The error code to look up * * @return An error string or "UNKNOWN HASH TABLE ERROR" */ const char * hash_table_err_to_str(hash_error_t err) { switch (err) { case HASHTABLE_SUCCESS: return "HASHTABLE_SUCCESS"; case HASHTABLE_UNKNOWN_HASH_TYPE: return "HASHTABLE_UNKNOWN_HASH_TYPE"; case HASHTABLE_ERROR_NO_SUCH_KEY: return "HASHTABLE_ERROR_NO_SUCH_KEY"; case HASHTABLE_ERROR_KEY_ALREADY_EXISTS: return "HASHTABLE_ERROR_KEY_ALREADY_EXISTS"; case HASHTABLE_ERROR_INVALID_ARGUMENT: return "HASHTABLE_ERROR_INVALID_ARGUMENT"; case HASHTABLE_ERROR_DELALL_FAIL: return "HASHTABLE_ERROR_DELALL_FAIL"; case HASHTABLE_NOT_DELETED: return "HASHTABLE_NOT_DELETED"; case HASHTABLE_OVERWRITTEN: return "HASHTABLE_OVERWRITTEN"; } return "UNKNOWN HASH TABLE ERROR"; } /** * @brief Locate a key within a partition * * This function traverses the red-black tree within a hash table * partition and returns, if one exists, a pointer to a node matching * the supplied key. * * @param[in] ht The hashtable to be used * @param[in] key The key to look up * @param[in] index Index into RBT array * @param[in] rbthash Hash in red-black tree * @param[out] node On success, the found node, NULL otherwise * * @retval HASHTABLE_SUCCESS if successfull * @retval HASHTABLE_NO_SUCH_KEY if key was not found */ static hash_error_t key_locate(struct hash_table *ht, const struct gsh_buffdesc *key, uint32_t index, uint64_t rbthash, struct rbt_node **node) { /* The current partition */ struct hash_partition *partition = &(ht->partitions[index]); /* The root of the red black tree matching this index */ struct rbt_head *root = NULL; /* A pair of buffer descriptors locating key and value for this entry */ struct hash_data *data = NULL; /* The node in the red-black tree currently being traversed */ struct rbt_node *cursor = NULL; /* true if we have located the key */ int found = false; *node = NULL; if (partition->cache) { void **cache_slot = (void **) &(partition->cache[cache_offsetof(ht, rbthash)]); cursor = atomic_fetch_voidptr(cache_slot); LogFullDebug(COMPONENT_HASHTABLE_CACHE, "hash %s index %" PRIu32 " slot %d", (cursor) ? "hit" : "miss", index, cache_offsetof(ht, rbthash)); if (cursor) { data = RBT_OPAQ(cursor); if (ht->parameter.compare_key( (struct gsh_buffdesc *)key, &(data->key)) == 0) { goto out; } } } root = &(ht->partitions[index].rbt); /* The lefmost occurrence of the value is the one from which we may start iteration to visit all nodes containing a value. */ RBT_FIND_LEFT(root, cursor, rbthash); if (cursor == NULL) { if (isFullDebug(COMPONENT_HASHTABLE) && isFullDebug(ht->parameter.ht_log_component)) LogFullDebug(ht->parameter.ht_log_component, "Key not found: rbthash = %" PRIu64, rbthash); return HASHTABLE_ERROR_NO_SUCH_KEY; } while ((cursor != NULL) && (RBT_VALUE(cursor) == rbthash)) { data = RBT_OPAQ(cursor); if (ht->parameter.compare_key((struct gsh_buffdesc *)key, &(data->key)) == 0) { if (partition->cache) { void **cache_slot = (void **) &partition->cache[cache_offsetof(ht, rbthash)]; atomic_store_voidptr(cache_slot, cursor); } found = true; break; } RBT_INCREMENT(cursor); } if (!found) { if (isFullDebug(COMPONENT_HASHTABLE) && isFullDebug(ht->parameter.ht_log_component)) LogFullDebug(ht->parameter.ht_log_component, "Matching hash found, but no matching key."); return HASHTABLE_ERROR_NO_SUCH_KEY; } out: *node = cursor; return HASHTABLE_SUCCESS; } /** * @brief Compute the values to search a hash store * * This function computes the index and RBT hash values for the * specified key. * * @param[in] ht The hash table whose parameters determine computation * @param[in] key The key from which to compute the values * @param[out] index The partition index * @param[out] rbt_hash The hash in the Red-Black tree * * @retval HASHTABLE_SUCCESS if values computed * @retval HASHTABLE_ERROR_INVALID_ARGUMENT if the supplied function * fails */ static inline hash_error_t compute(struct hash_table *ht, const struct gsh_buffdesc *key, uint32_t *index, uint64_t *rbt_hash) { /* Compute the partition index and red-black tree hash */ if (ht->parameter.hash_func_both) { if (!(*(ht->parameter.hash_func_both)) (&ht->parameter, (struct gsh_buffdesc *)key, index, rbt_hash)) return HASHTABLE_ERROR_INVALID_ARGUMENT; } else { *index = (*(ht->parameter.hash_func_key)) (&ht->parameter, (struct gsh_buffdesc *) key); *rbt_hash = (*(ht->parameter.hash_func_rbt)) (&ht->parameter, (struct gsh_buffdesc *) key); } /* At the suggestion of Jim Lieb, die if a hash function sends us off the end of the array. */ assert(*index < ht->parameter.index_size); return HASHTABLE_SUCCESS; } /* The following are the hash table primitives implementing the actual functionality. */ /** * @brief Initialize a new hash table * * This function initializes and allocates storage for a hash table. * * @param[in] hparam Parameters to determine the hash table's * behaviour * * @return Pointer to the new hash table, NULL on failure * */ struct hash_table * hashtable_init(struct hash_param *hparam) { /* The hash table being constructed */ struct hash_table *ht = NULL; /* The index for initializing each partition */ uint32_t index = 0; /* Read-Write Lock attributes, to prevent write starvation under GLIBC */ pthread_rwlockattr_t rwlockattr; /* Hash partition */ struct hash_partition *partition = NULL; /* The number of fully initialized partitions */ uint32_t completed = 0; if (pthread_rwlockattr_init(&rwlockattr) != 0) return NULL; /* At some point factor this out into the OS directory. it is necessary to prevent writer starvation under GLIBC. */ #ifdef GLIBC if ((pthread_rwlockattr_setkind_np (&rwlockattrs, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP)) != 0) { LogCrit(COMPONENT_HASHTABLE, "Unable to set writer-preference on lock attribute."); goto deconstruct; } #endif /* GLIBC */ ht = gsh_calloc(1, sizeof(struct hash_table) + (sizeof(struct hash_partition) * hparam->index_size)); /* Fixup entry size */ if (hparam->flags & HT_FLAG_CACHE) { if (!hparam->cache_entry_count) /* works fine with a good hash algo */ hparam->cache_entry_count = 32767; } /* We need to save copy of the parameters in the table. */ ht->parameter = *hparam; for (index = 0; index < hparam->index_size; ++index) { partition = (&ht->partitions[index]); RBT_HEAD_INIT(&(partition->rbt)); if (pthread_rwlock_init(&partition->lock, &rwlockattr) != 0) { LogCrit(COMPONENT_HASHTABLE, "Unable to initialize lock in hash table."); goto deconstruct; } /* Allocate a cache if requested */ if (hparam->flags & HT_FLAG_CACHE) partition->cache = gsh_calloc(1, cache_page_size(ht)); completed++; } ht->node_pool = pool_basic_init(NULL, sizeof(rbt_node_t)); ht->data_pool = pool_basic_init(NULL, sizeof(struct hash_data)); pthread_rwlockattr_destroy(&rwlockattr); return ht; deconstruct: while (completed != 0) { if (hparam->flags & HT_FLAG_CACHE) gsh_free(ht->partitions[completed - 1].cache); PTHREAD_RWLOCK_destroy(&(ht->partitions[completed - 1].lock)); completed--; } if (ht->node_pool) pool_destroy(ht->node_pool); if (ht->data_pool) pool_destroy(ht->data_pool); gsh_free(ht); return ht = NULL; } /** * @brief Dispose of a hash table * * This function deletes all the entries from the given hash table and * then destroys the hash table. * * @param[in,out] ht Pointer to the hash table. After calling * this function, the memory pointed to by ht * must not be accessed in any way. * @param[in] free_func Function to free entries as they are * deleted * * @return HASHTABLE_SUCCESS on success, other things on failure */ hash_error_t hashtable_destroy(struct hash_table *ht, int (*free_func)(struct gsh_buffdesc, struct gsh_buffdesc)) { size_t index = 0; hash_error_t hrc = HASHTABLE_SUCCESS; hrc = hashtable_delall(ht, free_func); if (hrc != HASHTABLE_SUCCESS) goto out; for (index = 0; index < ht->parameter.index_size; ++index) { if (ht->partitions[index].cache) { gsh_free(ht->partitions[index].cache); ht->partitions[index].cache = NULL; } PTHREAD_RWLOCK_destroy(&(ht->partitions[index].lock)); } pool_destroy(ht->node_pool); pool_destroy(ht->data_pool); gsh_free(ht); out: return hrc; } /** * @brief acquire the partition lock for the given key * * This function acquires the partition write mode lock corresponding to * the given key. * * @brief[in] ht The hash table to search * @brief[in] key The key for which to acquire the partition lock * @brief[out] latch Opaque structure holding partition information * * @retval HASHTABLE_SUCCESS, the partition lock is acquired. * @retval Others, failure, the partition lock is not acquired * * NOTES: fast path if the caller just needs to lock the partition and * doesn't need to look for the entry. The lock needs to be released * with hashtable_releaselatched() */ hash_error_t hashtable_acquire_latch(struct hash_table *ht, const struct gsh_buffdesc *key, struct hash_latch *latch) { uint32_t index; uint64_t rbt_hash; hash_error_t rc = HASHTABLE_SUCCESS; memset(latch, 0, sizeof(struct hash_latch)); rc = compute(ht, key, &index, &rbt_hash); if (rc != HASHTABLE_SUCCESS) return rc; latch->index = index; PTHREAD_RWLOCK_wrlock(&(ht->partitions[index].lock)); return HASHTABLE_SUCCESS; } /** * @brief Look up an entry, latching the table * * This function looks up an entry in the hash table and latches the * partition in which that entry would belong in preparation for other * activities. This function is a primitive and is intended more for * use building other access functions than for client code itself. * * @brief[in] ht The hash table to search * @brief[in] key The key for which to search * @brief[out] val The value found * @brief[in] may_write This must be true if the followup call might * mutate the hash table (set or delete) * @brief[out] latch Opaque structure holding information on the * table. * * @retval HASHTABLE_SUCCESS The entry was found, the table is * latched. * @retval HASHTABLE_ERROR_NOT_FOUND The entry was not found, the * table is latched. * @retval Others, failure, the table is not latched. */ hash_error_t hashtable_getlatch(struct hash_table *ht, const struct gsh_buffdesc *key, struct gsh_buffdesc *val, bool may_write, struct hash_latch *latch) { /* The index specifying the partition to search */ uint32_t index = 0; /* The node found for the key */ struct rbt_node *locator = NULL; /* The buffer descritpros for the key and value for the found entry */ struct hash_data *data = NULL; /* The hash value to be searched for within the Red-Black tree */ uint64_t rbt_hash = 0; /* Stored error return */ hash_error_t rc = HASHTABLE_SUCCESS; /* This combination of options makes no sense ever */ assert(!(may_write && !latch)); rc = compute(ht, key, &index, &rbt_hash); if (rc != HASHTABLE_SUCCESS) return rc; /* Acquire mutex */ if (may_write) PTHREAD_RWLOCK_wrlock(&(ht->partitions[index].lock)); else PTHREAD_RWLOCK_rdlock(&(ht->partitions[index].lock)); rc = key_locate(ht, key, index, rbt_hash, &locator); if (rc == HASHTABLE_SUCCESS) { /* Key was found */ data = RBT_OPAQ(locator); if (val) { val->addr = data->val.addr; val->len = data->val.len; } if (isDebug(COMPONENT_HASHTABLE) && isFullDebug(ht->parameter.ht_log_component)) { char dispval[HASHTABLE_DISPLAY_STRLEN]; if (ht->parameter.val_to_str != NULL) ht->parameter.val_to_str(&data->val, dispval); else dispval[0] = '\0'; LogFullDebug(ht->parameter.ht_log_component, "Get %s returning Value=%p {%s}", ht->parameter.ht_name, data->val.addr, dispval); } } if (((rc == HASHTABLE_SUCCESS) || (rc == HASHTABLE_ERROR_NO_SUCH_KEY)) && (latch != NULL)) { latch->index = index; latch->rbt_hash = rbt_hash; latch->locator = locator; } else { PTHREAD_RWLOCK_unlock(&ht->partitions[index].lock); } if (rc != HASHTABLE_SUCCESS && isDebug(COMPONENT_HASHTABLE) && isFullDebug(ht->parameter.ht_log_component)) LogFullDebug(ht->parameter.ht_log_component, "Get %s returning failure %s", ht->parameter.ht_name, hash_table_err_to_str(rc)); return rc; } /** * * @brief Release lock held on hash table * * This function releases the lock on the hash partition acquired and * retained by a call to hashtable_getlatch. This function must be * used to free any acquired lock but ONLY if the lock was not already * freed by some other means (hashtable_setlatched or * HashTable_DelLatched). * * @param[in] ht The hash table with the lock to be released * @param[in] latch The latch structure holding retained state */ void hashtable_releaselatched(struct hash_table *ht, struct hash_latch *latch) { if (latch) { PTHREAD_RWLOCK_unlock(&ht->partitions[latch->index].lock); memset(latch, 0, sizeof(struct hash_latch)); } } /** * @brief Set a value in a table following a previous GetLatch * * This function sets a value in a hash table following a previous * call to the hashtable_getlatch function. It must only be used * after such a call made with the may_write parameter set to true. * In all cases, the lock on the hash table is released. * * @param[in,out] ht The hash store to be modified * @param[in] key A buffer descriptor locating the key to set * @param[in] val A buffer descriptor locating the value to insert * @param[in] latch A pointer to a structure filled by a previous * call to hashtable_getlatched. * @param[in] overwrite If true, overwrite a prexisting key, * otherwise return error on collision. * @param[out] stored_key If non-NULL, a buffer descriptor for an * overwritten key as stored. * @param[out] stored_val If non-NULL, a buffer descriptor for an * overwritten value as stored. * * @retval HASHTABLE_SUCCESS on non-colliding insert * @retval HASHTABLE_ERROR_KEY_ALREADY_EXISTS if overwrite disabled * @retval HASHTABLE_OVERWRITTEN on successful overwrite * @retval Other errors on failure */ hash_error_t hashtable_setlatched(struct hash_table *ht, struct gsh_buffdesc *key, struct gsh_buffdesc *val, struct hash_latch *latch, int overwrite, struct gsh_buffdesc *stored_key, struct gsh_buffdesc *stored_val) { /* Stored error return */ hash_error_t rc = HASHTABLE_SUCCESS; /* The pair of buffer descriptors locating both key and value for this object, what actually gets stored. */ struct hash_data *descriptors = NULL; /* Node giving the location to insert new node */ struct rbt_node *locator = NULL; /* New node for the case of non-overwrite */ struct rbt_node *mutator = NULL; if (isDebug(COMPONENT_HASHTABLE) && isFullDebug(ht->parameter.ht_log_component)) { char dispkey[HASHTABLE_DISPLAY_STRLEN]; char dispval[HASHTABLE_DISPLAY_STRLEN]; if (ht->parameter.key_to_str != NULL) ht->parameter.key_to_str(key, dispkey); else dispkey[0] = '\0'; if (ht->parameter.val_to_str != NULL) ht->parameter.val_to_str(val, dispval); else dispval[0] = '\0'; LogFullDebug(ht->parameter.ht_log_component, "Set %s Key=%p {%s} Value=%p {%s} index=%" PRIu32 " rbt_hash=%" PRIu64, ht->parameter.ht_name, key->addr, dispkey, val->addr, dispval, latch->index, latch->rbt_hash); } /* In the case of collision */ if (latch->locator) { if (!overwrite) { rc = HASHTABLE_ERROR_KEY_ALREADY_EXISTS; goto out; } descriptors = RBT_OPAQ(latch->locator); if (isDebug(COMPONENT_HASHTABLE) && isFullDebug(ht->parameter.ht_log_component)) { char dispkey[HASHTABLE_DISPLAY_STRLEN]; char dispval[HASHTABLE_DISPLAY_STRLEN]; if (ht->parameter.key_to_str != NULL) ht->parameter.key_to_str(&descriptors->key, dispkey); else dispkey[0] = '\0'; if (ht->parameter.val_to_str != NULL) ht->parameter.val_to_str(&descriptors->val, dispval); else dispval[0] = '\0'; LogFullDebug(ht->parameter.ht_log_component, "Set %s Key=%p {%s} Value=%p {%s} index=%" PRIu32" rbt_hash=%"PRIu64" was replaced", ht->parameter.ht_name, descriptors->key.addr, dispkey, descriptors->val.addr, dispval, latch->index, latch->rbt_hash); } if (stored_key) *stored_key = descriptors->key; if (stored_val) *stored_val = descriptors->val; descriptors->key = *key; descriptors->val = *val; rc = HASHTABLE_OVERWRITTEN; goto out; } /* We have no collision, so go about creating and inserting a new node. */ RBT_FIND(&ht->partitions[latch->index].rbt, locator, latch->rbt_hash); mutator = pool_alloc(ht->node_pool); descriptors = pool_alloc(ht->data_pool); RBT_OPAQ(mutator) = descriptors; RBT_VALUE(mutator) = latch->rbt_hash; RBT_INSERT(&ht->partitions[latch->index].rbt, mutator, locator); descriptors->key.addr = key->addr; descriptors->key.len = key->len; descriptors->val.addr = val->addr; descriptors->val.len = val->len; /* Only in the non-overwrite case */ ++ht->partitions[latch->index].count; rc = HASHTABLE_SUCCESS; out: hashtable_releaselatched(ht, latch); if (rc != HASHTABLE_SUCCESS && isDebug(COMPONENT_HASHTABLE) && isFullDebug(ht->parameter.ht_log_component)) LogFullDebug(ht->parameter.ht_log_component, "Set %s returning failure %s", ht->parameter.ht_name, hash_table_err_to_str(rc)); return rc; } /** * @brief Delete a value from the store following a previous GetLatch * * This function removes a value from the a hash store, the value * already having been looked up with GetLatched. In all cases, the * lock is retained. hashtable_getlatch must have been called with * may_write true. * * @param[in,out] ht The hash store to be modified * @param[in] key A buffer descriptore locating the key to remove * @param[in] latch A pointer to a structure filled by a previous * call to hashtable_getlatched. * @param[out] stored_key If non-NULL, a buffer descriptor the * removed key as stored. * @param[out] stored_val If non-NULL, a buffer descriptor for the * removed value as stored. * */ void hashtable_deletelatched(struct hash_table *ht, const struct gsh_buffdesc *key, struct hash_latch *latch, struct gsh_buffdesc *stored_key, struct gsh_buffdesc *stored_val) { /* The pair of buffer descriptors comprising the stored entry */ struct hash_data *data; /* Its partition */ struct hash_partition *partition = &ht->partitions[latch->index]; data = RBT_OPAQ(latch->locator); if (isDebug(COMPONENT_HASHTABLE) && isFullDebug(ht->parameter.ht_log_component)) { char dispkey[HASHTABLE_DISPLAY_STRLEN]; char dispval[HASHTABLE_DISPLAY_STRLEN]; if (ht->parameter.key_to_str != NULL) ht->parameter.key_to_str(&data->key, dispkey); else dispkey[0] = '\0'; if (ht->parameter.val_to_str != NULL) ht->parameter.val_to_str(&data->val, dispval); else dispval[0] = '\0'; LogFullDebug(ht->parameter.ht_log_component, "Delete %s Key=%p {%s} Value=%p {%s} index=%" PRIu32 " rbt_hash=%" PRIu64 " was removed", ht->parameter.ht_name, data->key.addr, dispkey, data->val.addr, dispval, latch->index, latch->rbt_hash); } if (stored_key) *stored_key = data->key; if (stored_val) *stored_val = data->val; /* Clear cache */ if (partition->cache) { uint32_t offset = cache_offsetof(ht, latch->rbt_hash); struct rbt_node *cnode = partition->cache[offset]; if (cnode) { #if COMPARE_BEFORE_CLEAR_CACHE struct hash_data *data1 = RBT_OPAQ(cnode); struct hash_data *data2 = RBT_OPAQ(latch->locator); if (ht->parameter.compare_key(&data1->key, &data2->key) == 0) { LogFullDebug(COMPONENT_HASHTABLE_CACHE, "hash clear index %d slot %" PRIu64 latch->index, offset); partition->cache[offset] = NULL; } #else LogFullDebug(COMPONENT_HASHTABLE_CACHE, "hash clear slot %d", offset); partition->cache[offset] = NULL; #endif } } /* Now remove the entry */ RBT_UNLINK(&partition->rbt, latch->locator); pool_free(ht->data_pool, data); pool_free(ht->node_pool, latch->locator); --ht->partitions[latch->index].count; /* Some callers re-use the latch to insert a record after this call, * so reset latch locator to avoid hashtable_setlatched() using the * invalid latch->locator */ latch->locator = NULL; } /** * @brief Remove and free all (key,val) couples from the hash store * * This function removes all (key,val) couples from the hashtable and * frees the stored data using the supplied function * * @param[in,out] ht The hashtable to be cleared of all entries * @param[in] free_func The function with which to free the contents * of each entry * * @return HASHTABLE_SUCCESS or errors */ hash_error_t hashtable_delall(struct hash_table *ht, int (*free_func)(struct gsh_buffdesc, struct gsh_buffdesc)) { /* Successive partition numbers */ uint32_t index = 0; for (index = 0; index < ht->parameter.index_size; index++) { /* The root of each successive partition */ struct rbt_head *root = &ht->partitions[index].rbt; /* Pointer to node in tree for removal */ struct rbt_node *cursor = NULL; PTHREAD_RWLOCK_wrlock(&ht->partitions[index].lock); /* Continue until there are no more entries in the red-black tree */ while ((cursor = RBT_LEFTMOST(root)) != NULL) { /* Pointer to the key and value descriptors for each successive entry */ struct hash_data *data = NULL; /* Aliased poitner to node, for freeing buffers after removal from tree */ struct rbt_node *holder = cursor; /* Buffer descriptor for key, as stored */ struct gsh_buffdesc key; /* Buffer descriptor for value, as stored */ struct gsh_buffdesc val; /* Return code from the free function. Zero on failure */ int rc = 0; RBT_UNLINK(root, cursor); data = RBT_OPAQ(holder); key = data->key; val = data->val; pool_free(ht->data_pool, data); pool_free(ht->node_pool, holder); --ht->partitions[index].count; rc = free_func(key, val); if (rc == 0) { PTHREAD_RWLOCK_unlock( &ht->partitions[index].lock); return HASHTABLE_ERROR_DELALL_FAIL; } } PTHREAD_RWLOCK_unlock(&ht->partitions[index].lock); } return HASHTABLE_SUCCESS; } /** * @brief Log information about the hashtable * * This debugging function prints information about the hash table to * the log. * * @param[in] component The component debugging config to use. * @param[in] ht The hashtable to be used. */ void hashtable_log(log_components_t component, struct hash_table *ht) { /* The current position in the hash table */ struct rbt_node *it = NULL; /* The root of the tree currently being inspected */ struct rbt_head *root; /* Buffer descriptors for the key and value */ struct hash_data *data = NULL; /* String representation of the key */ char dispkey[HASHTABLE_DISPLAY_STRLEN]; /* String representation of the stored value */ char dispval[HASHTABLE_DISPLAY_STRLEN]; /* Index for traversing the partitions */ uint32_t i = 0; /* Running count of entries */ size_t nb_entries = 0; /* Recomputed partitionindex */ uint32_t index = 0; /* Recomputed hash for Red-Black tree */ uint64_t rbt_hash = 0; LogFullDebug(component, "The hash is partitioned into %d trees", ht->parameter.index_size); for (i = 0; i < ht->parameter.index_size; i++) nb_entries += ht->partitions[i].count; LogFullDebug(component, "The hash contains %zd entries", nb_entries); for (i = 0; i < ht->parameter.index_size; i++) { root = &ht->partitions[i].rbt; LogFullDebug(component, "The partition in position %" PRIu32 "contains: %u entries", i, root->rbt_num_node); PTHREAD_RWLOCK_rdlock(&ht->partitions[i].lock); RBT_LOOP(root, it) { data = it->rbt_opaq; ht->parameter.key_to_str(&(data->key), dispkey); ht->parameter.val_to_str(&(data->val), dispval); if (compute(ht, &data->key, &index, &rbt_hash) != HASHTABLE_SUCCESS) { LogCrit(component, "Possible implementation error in hash_func_both"); index = 0; rbt_hash = 0; } LogFullDebug(component, "%s => %s; index=%" PRIu32 " rbt_hash=%" PRIu64, dispkey, dispval, index, rbt_hash); RBT_INCREMENT(it); } PTHREAD_RWLOCK_unlock(&ht->partitions[i].lock); } } /** * @brief Set a pair (key,value) into the Hash Table * * Depending on the value of 'how', this function sets a value into * the hash table or tests that the hash table contains that value. * * This function is deprecated. * * @param[in,out] ht The hashtable to test or alter * @param[in] key The key to be set * @param[in] val The value to be stored * @param[in] how A value determining whether this is a test, a set * with overwrite, or a set without overwrite. * * @retval HASHTABLE_SUCCESS if successfull. */ hash_error_t hashtable_test_and_set(struct hash_table *ht, struct gsh_buffdesc *key, struct gsh_buffdesc *val, hash_set_how_t how) { /* structure to hold retained state */ struct hash_latch latch; /* Stored return code */ hash_error_t rc = 0; rc = hashtable_getlatch(ht, key, NULL, (how != HASHTABLE_SET_HOW_TEST_ONLY), &latch); if ((rc != HASHTABLE_SUCCESS) && (rc != HASHTABLE_ERROR_NO_SUCH_KEY)) return rc; if (how == HASHTABLE_SET_HOW_TEST_ONLY) { hashtable_releaselatched(ht, &latch); return rc; } /* No point in calling hashtable_setlatched when we know it will error. */ if ((how == HASHTABLE_SET_HOW_SET_NO_OVERWRITE) && (rc == HASHTABLE_SUCCESS)) { hashtable_releaselatched(ht, &latch); return HASHTABLE_ERROR_KEY_ALREADY_EXISTS; } rc = hashtable_setlatched(ht, key, val, &latch, (how == HASHTABLE_SET_HOW_SET_OVERWRITE), NULL, NULL); if (rc == HASHTABLE_OVERWRITTEN) rc = HASHTABLE_SUCCESS; return rc; } /** * @brief Look up a value and take a reference * * This function attempts to locate a key in the hash store and return * the associated value. It also calls the supplied function to take * a reference before releasing the partition lock. It is implemented * as a wrapper around hashtable_getlatched. * * @param[in] ht The hash store to be searched * @param[in] key A buffer descriptore locating the key to find * @param[out] val A buffer descriptor locating the value found * @param[in] get_ref A function to take a reference on the supplied * value * * @return HASHTABLE_SUCCESS or errors */ hash_error_t hashtable_getref(hash_table_t *ht, struct gsh_buffdesc *key, struct gsh_buffdesc *val, void (*get_ref)(struct gsh_buffdesc *)) { /* structure to hold retained state */ struct hash_latch latch; /* Stored return code */ hash_error_t rc = 0; rc = hashtable_getlatch(ht, key, val, false, &latch); switch (rc) { case HASHTABLE_SUCCESS: if (get_ref != NULL) get_ref(val); case HASHTABLE_ERROR_NO_SUCH_KEY: hashtable_releaselatched(ht, &latch); break; default: break; } return rc; } /** @} */ nfs-ganesha-2.6.0/src/idmapper/000077500000000000000000000000001324272410200162765ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/idmapper/.gitignore000066400000000000000000000000161324272410200202630ustar00rootroot00000000000000test_idmapper nfs-ganesha-2.6.0/src/idmapper/CMakeLists.txt000066400000000000000000000004631324272410200210410ustar00rootroot00000000000000if(_MSPAC_SUPPORT) include_directories( ${WBCLIENT_INCLUDE_DIR} ) endif(_MSPAC_SUPPORT) ########### next target ############### SET(idmap_STAT_SRCS idmapper.c idmapper_cache.c ) add_library(idmap STATIC ${idmap_STAT_SRCS}) add_sanitizers(idmap) ########### install files ############### nfs-ganesha-2.6.0/src/idmapper/idmapper.c000066400000000000000000000457751324272410200202650ustar00rootroot00000000000000/* * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @addtogroup idmapper * @{ */ /** * @file idmapper.c * @brief Id mapping functions */ #include "config.h" #include /* for using gethostname */ #include /* for using exit */ #include #include #include #include #include #include #include #ifdef USE_NFSIDMAP #include #include "nfs_exports.h" #endif /* USE_NFSIDMAP */ #ifdef _MSPAC_SUPPORT #include #endif #include "common_utils.h" #include "gsh_rpc.h" #include "nfs_core.h" #include "idmapper.h" static struct gsh_buffdesc owner_domain; /** * @brief Initialize the ID Mapper * * @return true on success, false on failure */ bool idmapper_init(void) { #ifdef USE_NFSIDMAP if (!nfs_param.nfsv4_param.use_getpwnam) { if (nfs4_init_name_mapping(nfs_param.nfsv4_param.idmapconf) != 0) { return false; } owner_domain.addr = gsh_malloc(NFS4_MAX_DOMAIN_LEN + 1); if (nfs4_get_default_domain (NULL, owner_domain.addr, NFS4_MAX_DOMAIN_LEN) != 0) { gsh_free(owner_domain.addr); return false; } owner_domain.len = strlen(owner_domain.addr); } #endif /* USE_NFSIDMAP */ if (nfs_param.nfsv4_param.use_getpwnam) { owner_domain.addr = gsh_strdup(nfs_param.nfsv4_param .domainname); owner_domain.len = strlen(nfs_param.nfsv4_param.domainname); } idmapper_cache_init(); return true; } /** * @brief Encode a UID or GID as a string * * @param[in,out] xdrs XDR stream to which to encode * @param[in] id UID or GID * @param[in] group True if this is a GID, false for a UID * * @retval true on success. * @retval false on failure. */ static bool xdr_encode_nfs4_princ(XDR *xdrs, uint32_t id, bool group) { const struct gsh_buffdesc *found; uint32_t not_a_size_t; bool success = false; if (nfs_param.nfsv4_param.only_numeric_owners) { /* 2**32 is 10 digits long in decimal */ struct gsh_buffdesc name; char namebuf[11]; name.addr = namebuf; sprintf(namebuf, "%"PRIu32, id); name.len = strlen(namebuf); not_a_size_t = name.len; return inline_xdr_bytes(xdrs, (char **)&name.addr, ¬_a_size_t, UINT32_MAX); } PTHREAD_RWLOCK_rdlock(group ? &idmapper_group_lock : &idmapper_user_lock); if (group) success = idmapper_lookup_by_gid(id, &found); else success = idmapper_lookup_by_uid(id, &found, NULL); if (likely(success)) { not_a_size_t = found->len; /* Fully qualified owners are always stored in the hash table, no matter what our lookup method. */ success = inline_xdr_bytes(xdrs, (char **)&found->addr, ¬_a_size_t, UINT32_MAX); PTHREAD_RWLOCK_unlock(group ? &idmapper_group_lock : &idmapper_user_lock); return success; } else { PTHREAD_RWLOCK_unlock(group ? &idmapper_group_lock : &idmapper_user_lock); int rc; int size; bool looked_up = false; char *namebuff = NULL; struct gsh_buffdesc new_name; if (nfs_param.nfsv4_param.use_getpwnam) { if (group) size = sysconf(_SC_GETGR_R_SIZE_MAX); else size = sysconf(_SC_GETPW_R_SIZE_MAX); if (size == -1) size = PWENT_BEST_GUESS_LEN; new_name.len = size; size += owner_domain.len + 2; } else { size = NFS4_MAX_DOMAIN_LEN + 2; } namebuff = alloca(size); new_name.addr = namebuff; if (nfs_param.nfsv4_param.use_getpwnam) { char *cursor; bool nulled; if (group) { struct group g; struct group *gres; rc = getgrgid_r(id, &g, namebuff, new_name.len, &gres); nulled = (gres == NULL); } else { struct passwd p; struct passwd *pres; rc = getpwuid_r(id, &p, namebuff, new_name.len, &pres); nulled = (pres == NULL); } if ((rc == 0) && !nulled) { new_name.len = strlen(namebuff); cursor = namebuff + new_name.len; *(cursor++) = '@'; ++new_name.len; memcpy(cursor, owner_domain.addr, owner_domain.len); new_name.len += owner_domain.len; looked_up = true; } else { LogInfo(COMPONENT_IDMAPPER, "%s failed with code %d.", (group ? "getgrgid_r" : "getpwuid_r"), rc); } } else { #ifdef USE_NFSIDMAP if (group) { rc = nfs4_gid_to_name(id, owner_domain.addr, namebuff, NFS4_MAX_DOMAIN_LEN + 1); } else { rc = nfs4_uid_to_name(id, owner_domain.addr, namebuff, NFS4_MAX_DOMAIN_LEN + 1); } if (rc == 0) { new_name.len = strlen(namebuff); looked_up = true; } else { LogInfo(COMPONENT_IDMAPPER, "%s failed with code %d.", (group ? "nfs4_gid_to_name" : "nfs4_uid_to_name"), rc); } #else /* USE_NFSIDMAP */ looked_up = false; #endif /* !USE_NFSIDMAP */ } if (!looked_up) { if (nfs_param.nfsv4_param.allow_numeric_owners) { LogInfo(COMPONENT_IDMAPPER, "Lookup for %d failed, using numeric %s", id, (group ? "group" : "owner")); /* 2**32 is 10 digits long in decimal */ sprintf(namebuff, "%"PRIu32, id); new_name.len = strlen(namebuff); } else { LogInfo(COMPONENT_IDMAPPER, "Lookup for %d failed, using nobody.", id); memcpy(new_name.addr, "nobody", 6); new_name.len = 6; } } /* Add to the cache and encode the result. */ PTHREAD_RWLOCK_wrlock(group ? &idmapper_group_lock : &idmapper_user_lock); if (group) success = idmapper_add_group(&new_name, id); else success = idmapper_add_user(&new_name, id, NULL, false); PTHREAD_RWLOCK_unlock(group ? &idmapper_group_lock : &idmapper_user_lock); if (unlikely(!success)) { LogMajor(COMPONENT_IDMAPPER, "%s failed.", group ? "idmapper_add_group" : "idmaper_add_user"); } not_a_size_t = new_name.len; return inline_xdr_bytes(xdrs, (char **)&new_name.addr, ¬_a_size_t, UINT32_MAX); } } /** * @brief Encode a UID as a string * * @param[in,out] xdrs XDR stream to which to encode * @param[in] uid UID * * @retval true on success. * @retval false on failure. */ bool xdr_encode_nfs4_owner(XDR *xdrs, uid_t uid) { return xdr_encode_nfs4_princ(xdrs, uid, false); } /** * @brief Encode a GID as a string * * @param[in,out] xdrs XDR stream to which to encode * @param[in] gid GID * * @retval true on success. * @retval false on failure. */ bool xdr_encode_nfs4_group(XDR *xdrs, gid_t gid) { return xdr_encode_nfs4_princ(xdrs, gid, true); } /** * @brief Handle unqualified names * * @param[in] name C string of name * @param[in] len Length of name * @param[out] id ID found * @param[in] anon ID to use in case of nobody * * @return true on success, false on just phoning it in. */ static bool atless2id(char *name, size_t len, uint32_t *id, const uint32_t anon) { if ((len == 6) && (!memcmp(name, "nobody", 6))) { *id = anon; return true; } else if (nfs_param.nfsv4_param.allow_numeric_owners) { char *end = NULL; *id = strtol(name, &end, 10); if (!(end && *end != '\0')) return true; } /* Nothing else without an @ is allowed. */ return false; } /** * @brief Return gid given a group name * * @param[in] name group name * @param[out] gid address for gid to be filled in * * @return 0 on success and errno on failure. * * NOTE: If a group name doesn't exist, getgrnam_r returns 0 with the * result pointer set to NULL. We turn that into ENOENT error! Also, * getgrnam_r fails with ERANGE if there is a group with a large number * of users that it can't fill all those users into the supplied buffer. * This need not be the group we are asking for! ERANGE is handled here, * so this function never ends up returning ERANGE back to the caller. */ static int name_to_gid(const char *name, gid_t *gid) { struct group g; struct group *gres = NULL; char *buf; size_t buflen = sysconf(_SC_GETGR_R_SIZE_MAX); /* Upper bound on the buffer length. Just to bailout if there is * a bug in getgrname_r returning ERANGE incorrectly. 64MB * should be good enough for now. */ size_t maxlen = 64 * 1024 * 1024; int err; if (buflen == -1) buflen = PWENT_BEST_GUESS_LEN; do { buf = gsh_malloc(buflen); err = getgrnam_r(name, &g, buf, buflen, &gres); if (err == ERANGE) { buflen *= 16; gsh_free(buf); } } while (buflen <= maxlen && err == ERANGE); if (err == 0) { if (gres == NULL) err = ENOENT; else *gid = gres->gr_gid; } if (err != ERANGE) gsh_free(buf); return err; } /** * @brief Lookup a name using PAM * * @param[in] name C string of name * @param[in] len Length of name * @param[out] id ID found * @param[in] anon ID to use in case of nobody * @param[in] group Whether this a group lookup * @param[out] gss_gid Found GID * @param[out] gss_uid Found UID * @apram[out] gotgss_gid Found a GID. * @param[in] at Location of the @ * * @return true on success, false not making the grade */ static bool pwentname2id(char *name, size_t len, uint32_t *id, const uint32_t anon, bool group, gid_t *gid, bool *got_gid, char *at) { if (at != NULL) { if (strcmp(at + 1, owner_domain.addr) != 0) { /* We won't map what isn't even in the right domain */ return false; } *at = '\0'; } if (group) { int err; err = name_to_gid(name, id); if (err == 0) return true; else if (err != ENOENT) { LogWarn(COMPONENT_IDMAPPER, "getgrnam_r %s failed, error: %d", name, err); return false; } #ifndef USE_NFSIDMAP else { char *end = NULL; gid_t gid; gid = strtol(name, &end, 10); if (end && *end != '\0') return 0; *id = gid; return true; } #endif } else { struct passwd p; struct passwd *pres; int size = sysconf(_SC_GETPW_R_SIZE_MAX); char *buf; if (size == -1) size = PWENT_BEST_GUESS_LEN; buf = alloca(size); if (getpwnam_r(name, &p, buf, size, &pres) != 0) { LogInfo(COMPONENT_IDMAPPER, "getpwnam_r %s failed", name); return false; } else if (pres != NULL) { *id = pres->pw_uid; *gid = pres->pw_gid; *got_gid = true; return true; } #ifndef USE_NFSIDMAP else { char *end = NULL; uid_t uid; uid = strtol(name, &end, 10); if (end && *end != '\0') return 0; *id = uid; *got_gid = false; return true; } #endif } return false; } /** * @brief Lookup a name NFS ID Mapper * * @param[in] name C string of name * @param[in] len Length of name * @param[out] id ID found * @param[in] anon ID to use in case of nobody * @param[in] group Whether this a group lookup * @param[out] gss_gid Found GID * @param[out] gss_uid Found UID * @apram[out] gotgss_gid Found a GID. * @param[in] at Location of the @ * * @return true on success, false not making the grade */ static bool idmapname2id(char *name, size_t len, uint32_t *id, const uint32_t anon, bool group, gid_t *gid, bool *got_gid, char *at) { #ifdef USE_NFSIDMAP int rc; if (group) rc = nfs4_name_to_gid(name, id); else rc = nfs4_name_to_uid(name, id); if (rc == 0) { return true; } else { LogInfo(COMPONENT_IDMAPPER, "%s %s failed with %d, using anonymous.", (group ? "nfs4_name_to_gid" : "nfs4_name_to_uid"), name, -rc); return false; } #else /* USE_NFSIDMAP */ return false; #endif /* USE_NFSIDMAP */ } /** * @brief Convert a name to an ID * * @param[in] name The name of the user * @param[out] id The resulting id * @param[in] group True if this is a group name * @param[in] anon ID to return if look up fails * * @return true if successful, false otherwise */ static bool name2id(const struct gsh_buffdesc *name, uint32_t *id, bool group, const uint32_t anon) { bool success; PTHREAD_RWLOCK_rdlock(group ? &idmapper_group_lock : &idmapper_user_lock); if (group) success = idmapper_lookup_by_gname(name, id); else success = idmapper_lookup_by_uname(name, id, NULL, false); PTHREAD_RWLOCK_unlock(group ? &idmapper_group_lock : &idmapper_user_lock); if (success) return true; else { gid_t gid; bool got_gid = false; /* Something we can mutate and count on as terminated */ char *namebuff = alloca(name->len + 1); char *at; bool looked_up = false; memcpy(namebuff, name->addr, name->len); *(namebuff + name->len) = '\0'; at = memchr(namebuff, '@', name->len); if (at == NULL) { if (pwentname2id (namebuff, name->len, id, anon, group, &gid, &got_gid, NULL)) looked_up = true; else if (atless2id(namebuff, name->len, id, anon)) looked_up = true; else return false; } else if (nfs_param.nfsv4_param.use_getpwnam) { looked_up = pwentname2id(namebuff, name->len, id, anon, group, &gid, &got_gid, at); } else { looked_up = idmapname2id(namebuff, name->len, id, anon, group, &gid, &got_gid, at); } if (!looked_up) { LogInfo(COMPONENT_IDMAPPER, "All lookups failed for %s, using anonymous.", namebuff); *id = anon; } PTHREAD_RWLOCK_wrlock(group ? &idmapper_group_lock : &idmapper_user_lock); if (group) success = idmapper_add_group(name, *id); else success = idmapper_add_user(name, *id, got_gid ? &gid : NULL, false); PTHREAD_RWLOCK_unlock(group ? &idmapper_group_lock : &idmapper_user_lock); if (!success) LogMajor(COMPONENT_IDMAPPER, "%s(%s %u) failed", (group ? "gidmap_add" : "uidmap_add"), namebuff, *id); return true; } } /** * @brief Convert a name to a uid * * @param[in] name The name of the user * @param[out] uid The resulting UID * @param[in] anon The anonymous UID for this export * * @return true if successful, false otherwise * */ bool name2uid(const struct gsh_buffdesc *name, uid_t *uid, const uid_t anon) { return name2id(name, uid, false, anon); } /** * @brief Convert a name to a GID * * @param[in] name The name of the user * @param[out] gid The resulting GID * @param[in] anon The anonymous GID for this export * * @return true if successful, false otherwise */ bool name2gid(const struct gsh_buffdesc *name, gid_t *gid, const gid_t anon) { return name2id(name, gid, true, anon); } #ifdef _HAVE_GSSAPI #ifdef _MSPAC_SUPPORT /** * @brief Convert a principal (as returned by @c gss_display_name) to a UID * * @param[in] name The principal of the user * @param[out] uid The resulting UID * @param[in,out] gd GSS data * * @return true if successful, false otherwise */ bool principal2uid(char *principal, uid_t *uid, gid_t *gid, struct svc_rpc_gss_data *gd) #else /** * @brief Convert a principal (as returned by @c gss_display_name) to a UID * * @param[in] name The principal of the user * @param[out] uid The resulting UID * * @return true if successful, false otherwise */ bool principal2uid(char *principal, uid_t *uid, gid_t *gid) #endif { #ifdef USE_NFSIDMAP uid_t gss_uid = -1; gid_t gss_gid = -1; const gid_t *gss_gidres = NULL; int rc; bool success; struct gsh_buffdesc princbuff = { .addr = principal, .len = strlen(principal) }; #endif if (nfs_param.nfsv4_param.use_getpwnam) return false; #ifdef USE_NFSIDMAP PTHREAD_RWLOCK_rdlock(&idmapper_user_lock); success = idmapper_lookup_by_uname(&princbuff, &gss_uid, &gss_gidres, true); /* We do need uid and gid. If gid is not in the cache, treat it as a * failure. */ if (success && gss_gidres != NULL) gss_gid = *gss_gidres; else success = false; PTHREAD_RWLOCK_unlock(&idmapper_user_lock); if (unlikely(!success)) { if ((princbuff.len >= 4) && (!memcmp(princbuff.addr, "nfs/", 4) || !memcmp(princbuff.addr, "root/", 5) || !memcmp(princbuff.addr, "host/", 5))) { /* NFSv4 specific features: RPCSEC_GSS will * provide user like * * nfs/ * root/ * host/ * choice is made to map them to root */ /* This is a "root" request made from the hostbased nfs principal, use root */ *uid = 0; *gid = 0; return true; } /* nfs4_gss_princ_to_ids required to extract uid/gid from gss creds */ rc = nfs4_gss_princ_to_ids("krb5", principal, &gss_uid, &gss_gid); if (rc) { #ifdef _MSPAC_SUPPORT bool found_uid = false; bool found_gid = false; if (gd->flags & SVC_RPC_GSS_FLAG_MSPAC) { struct wbcAuthUserParams params; wbcErr wbc_err; struct wbcAuthUserInfo *info; struct wbcAuthErrorInfo *error = NULL; memset(¶ms, 0, sizeof(params)); params.level = WBC_AUTH_USER_LEVEL_PAC; params.password.pac.data = (uint8_t *) gd->pac.ms_pac.value; params.password.pac.length = gd->pac.ms_pac.length; wbc_err = wbcAuthenticateUserEx(¶ms, &info, &error); if (!WBC_ERROR_IS_OK(wbc_err)) { LogCrit(COMPONENT_IDMAPPER, "wbcAuthenticateUserEx returned %s", wbcErrorString(wbc_err)); return false; } if (error) { LogCrit(COMPONENT_IDMAPPER, "nt_status: %s, display_string %s", error->nt_string, error->display_string); wbcFreeMemory(error); return false; } /* 1st SID is account sid, see wbclient.h */ wbc_err = wbcSidToUid(&info->sids[0].sid, &gss_uid); if (!WBC_ERROR_IS_OK(wbc_err)) { LogCrit(COMPONENT_IDMAPPER, "wbcSidToUid for uid returned %s", wbcErrorString(wbc_err)); wbcFreeMemory(info); return false; } /* 2nd SID is primary_group sid, see wbclient.h */ wbc_err = wbcSidToGid(&info->sids[1].sid, &gss_gid); if (!WBC_ERROR_IS_OK(wbc_err)) { LogCrit(COMPONENT_IDMAPPER, "wbcSidToUid for gid returned %s\n", wbcErrorString(wbc_err)); wbcFreeMemory(info); return false; } wbcFreeMemory(info); found_uid = true; found_gid = true; } #endif /* _MSPAC_SUPPORT */ #ifdef _MSPAC_SUPPORT if ((found_uid == true) && (found_gid == true)) goto principal_found; #endif return false; } #ifdef _MSPAC_SUPPORT principal_found: #endif PTHREAD_RWLOCK_wrlock(&idmapper_user_lock); success = idmapper_add_user(&princbuff, gss_uid, &gss_gid, true); PTHREAD_RWLOCK_unlock(&idmapper_user_lock); if (!success) { LogMajor(COMPONENT_IDMAPPER, "idmapper_add_user(%s, %d, %d) failed", principal, gss_uid, gss_gid); } } *uid = gss_uid; *gid = gss_gid; return true; #else /* !USE_NFSIDMAP */ assert(!"prohibited by configuration"); return false; #endif } #endif /** @} */ nfs-ganesha-2.6.0/src/idmapper/idmapper_cache.c000066400000000000000000000456101324272410200213740ustar00rootroot00000000000000/* * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @addtogroup idmapper * @{ */ /** * @file idmapper_cache.c * @brief Id mapping cache functions */ #include "config.h" #include "log.h" #include "config_parsing.h" #include #include #include #include "gsh_intrinsic.h" #include "gsh_types.h" #include "common_utils.h" #include "avltree.h" #include "idmapper.h" #include "abstract_atomic.h" /** * @brief User entry in the IDMapper cache */ struct cache_user { struct gsh_buffdesc uname; /*< Username */ uid_t uid; /*< Corresponding UID */ gid_t gid; /*< Corresponding GID */ bool gid_set; /*< if the GID has been set */ struct avltree_node uname_node; /*< Node in the name tree */ struct avltree_node uid_node; /*< Node in the UID tree */ bool in_uidtree; /* true iff this is in uid_tree */ }; /** * @brief Group entry in the IDMapper cache */ struct cache_group { struct gsh_buffdesc gname; /*< Group name */ gid_t gid; /*< Group ID */ struct avltree_node gname_node; /*< Node in the name tree */ struct avltree_node gid_node; /*< Node in the GID tree */ }; /** * @brief Number of entires in the UID cache, should be prime. */ #define id_cache_size 1009 /** * @brief UID cache, may only be accessed with idmapper_user_lock * held. If idmapper_user_lock is held for read, it must be accessed * atomically. (For a write, normal fetch/store is sufficient since * others are kept out.) */ static struct avltree_node *uid_cache[id_cache_size]; /** * @brief GID cache, may only be accessed with idmapper_group_lock * held. If idmapper_group_lock is held for read, it must be accessed * atomically. (For a write, normal fetch/store is sufficient since * others are kept out.) */ static struct avltree_node *gid_cache[id_cache_size]; /** * @brief Lock that protects the idmapper user cache */ pthread_rwlock_t idmapper_user_lock = PTHREAD_RWLOCK_INITIALIZER; /** * @brief Lock that protects the idmapper group cache */ pthread_rwlock_t idmapper_group_lock = PTHREAD_RWLOCK_INITIALIZER; /** * @brief Tree of users, by name */ static struct avltree uname_tree; /** * @brief Tree of users, by ID */ static struct avltree uid_tree; /** * @brief Tree of groups, by name */ static struct avltree gname_tree; /** * @brief Tree of groups, by ID */ static struct avltree gid_tree; /** * @brief Compare two buffers * * Handle the case where one buffer is a left sub-buffer of another * buffer by counting the longer one as larger. * * @param[in] buff1 A buffer * @param[in] buffa Another buffer * * @retval -1 if buff1 is less than buffa * @retval 0 if buff1 and buffa are equal * @retval 1 if buff1 is greater than buffa */ static inline int buffdesc_comparator(const struct gsh_buffdesc *buffa, const struct gsh_buffdesc *buff1) { int mr = memcmp(buff1->addr, buffa->addr, MIN(buff1->len, buffa->len)); if (unlikely(mr == 0)) { if (buff1->len < buffa->len) return -1; else if (buff1->len > buffa->len) return 1; else return 0; } else { return mr; } } /** * @brief Comparison for user names * * @param[in] node1 A node * @param[in] nodea Another node * * @retval -1 if node1 is less than nodea * @retval 0 if node1 and nodea are equal * @retval 1 if node1 is greater than nodea */ static int uname_comparator(const struct avltree_node *node1, const struct avltree_node *nodea) { struct cache_user *user1 = avltree_container_of(node1, struct cache_user, uname_node); struct cache_user *usera = avltree_container_of(nodea, struct cache_user, uname_node); return buffdesc_comparator(&user1->uname, &usera->uname); } /** * @brief Comparison for UIDs * * @param[in] node1 A node * @param[in] nodea Another node * * @retval -1 if node1 is less than nodea * @retval 0 if node1 and nodea are equal * @retval 1 if node1 is greater than nodea */ static int uid_comparator(const struct avltree_node *node1, const struct avltree_node *nodea) { struct cache_user *user1 = avltree_container_of(node1, struct cache_user, uid_node); struct cache_user *usera = avltree_container_of(nodea, struct cache_user, uid_node); if (user1->uid < usera->uid) return -1; else if (user1->uid > usera->uid) return 1; else return 0; } /** * @brief Comparison for group names * * @param[in] node1 A node * @param[in] nodea Another node * * @retval -1 if node1 is less than nodea * @retval 0 if node1 and nodea are equal * @retval 1 if node1 is greater than nodea */ static inline int gname_comparator(const struct avltree_node *node1, const struct avltree_node *nodea) { struct cache_group *group1 = avltree_container_of(node1, struct cache_group, gname_node); struct cache_group *groupa = avltree_container_of(nodea, struct cache_group, gname_node); return buffdesc_comparator(&group1->gname, &groupa->gname); } /** * @brief Comparison for GIDs * * @param[in] node1 A node * @param[in] nodea Another node * * @retval -1 if node1 is less than nodea * @retval 0 if node1 and nodea are equal * @retval 1 if node1 is greater than nodea */ static int gid_comparator(const struct avltree_node *node1, const struct avltree_node *nodea) { struct cache_group *group1 = avltree_container_of(node1, struct cache_group, gid_node); struct cache_group *groupa = avltree_container_of(nodea, struct cache_group, gid_node); if (group1->gid < groupa->gid) return -1; else if (group1->gid > groupa->gid) return 1; else return 0; } /** * @brief Initialize the IDMapper cache */ void idmapper_cache_init(void) { avltree_init(&uname_tree, uname_comparator, 0); avltree_init(&uid_tree, uid_comparator, 0); memset(uid_cache, 0, id_cache_size * sizeof(struct avltree_node *)); avltree_init(&gname_tree, gname_comparator, 0); avltree_init(&gid_tree, gid_comparator, 0); memset(gid_cache, 0, id_cache_size * sizeof(struct avltree_node *)); } /** * @brief Add a user entry to the cache * * @note The caller must hold idmapper_user_lock for write. * * @param[in] name The user name * @param[in] uid The user ID * @param[in] gid Optional. Set to NULL if no gid is known. * @param[in] gss_princ true when name is gss principal. * The uid to name map is not added for gss principals. * * @retval true on success. * @retval false if our reach exceeds our grasp. */ bool idmapper_add_user(const struct gsh_buffdesc *name, uid_t uid, const gid_t *gid, bool gss_princ) { struct avltree_node *found_name; struct avltree_node *found_id; struct cache_user *old; struct cache_user *new; new = gsh_malloc(sizeof(struct cache_user) + name->len); new->uname.addr = (char *)new + sizeof(struct cache_user); new->uname.len = name->len; new->uid = uid; memcpy(new->uname.addr, name->addr, name->len); if (gid) { new->gid = *gid; new->gid_set = true; } else { new->gid = -1; new->gid_set = false; } new->in_uidtree = (gss_princ) ? false : true; /* * There are 3 cases why we find an existing cache entry. * * Case 1: * The threads that lookup by-name or by-id use the read lock. * If they don't find an entry, then they release the read lock, * acquire the write lock and then add the entry. So it is * possible that multiple threads may fail to find an entry at * one point and they all try to add. In this case, we will be * trying to insert same name,id mapping. * * Case 2: * It is also possible that name got a different id or an id got * a different name causing us to find an existing entry when we * are trying to add an entry. This case calls for removing the * stale entry and update with this new entry. * * Case 3: * The username to id mapping could be from plain nfs idmapping * in which case we will not have a valid gid. If this is for a * kerberos principal mapping, we will have uid and gid but we * will not have "uid to name" cache entry (the reverse * mapping). This case requires us to combine the old entry and * the new entry! * * Note that the 3rd case happens if and only if IDMAPD_DOMAIN * and LOCAL_REALMS are set to the same value! */ found_name = avltree_insert(&new->uname_node, &uname_tree); if (unlikely(found_name)) { old = avltree_container_of(found_name, struct cache_user, uname_node); /* Combine old into new if uid's match */ if (old->uid == new->uid) { if (!new->gid_set && old->gid_set) { new->gid = old->gid; new->gid_set = true; } if (!new->in_uidtree && old->in_uidtree) new->in_uidtree = true; } /* Remove the old and insert the new */ avltree_remove(found_name, &uname_tree); if (old->in_uidtree) { uid_cache[old->uid % id_cache_size] = NULL; avltree_remove(&old->uid_node, &uid_tree); } gsh_free(old); found_name = avltree_insert(&new->uname_node, &uname_tree); assert(found_name == NULL); } if (!new->in_uidtree) /* all done */ return true; found_id = avltree_insert(&new->uid_node, &uid_tree); if (unlikely(found_id)) { old = avltree_container_of(found_id, struct cache_user, uid_node); uid_cache[old->uid % id_cache_size] = NULL; avltree_remove(found_id, &uid_tree); avltree_remove(&old->uname_node, &uname_tree); gsh_free(old); found_id = avltree_insert(&new->uid_node, &uid_tree); assert(found_id == NULL); } uid_cache[uid % id_cache_size] = &new->uid_node; return true; } /** * @brief Add a group entry to the cache * * @note The caller must hold idmapper_group_lock for write. * * @param[in] name The user name * @param[in] gid The group id * * @retval true on success. * @retval false if our reach exceeds our grasp. */ bool idmapper_add_group(const struct gsh_buffdesc *name, const gid_t gid) { struct avltree_node *found_name; struct avltree_node *found_id; struct cache_group *tmp; struct cache_group *new; new = gsh_malloc(sizeof(struct cache_group) + name->len); new->gname.addr = (char *)new + sizeof(struct cache_group); new->gname.len = name->len; new->gid = gid; memcpy(new->gname.addr, name->addr, name->len); /* * The threads that lookup by-name or by-id use the read lock. If * they don't find an entry, then they release the read lock, * acquire the write lock and then add the entry. So it is * possible that multiple threads may fail to find an entry at * one point and they all try to add. In this case, we will be * trying to insert same name,id mapping. It is also possible * that name got a different id or an id got a different name * causing us to find an existing entry when we are trying to * add an entry! * * If we find an existing entry, we remove it from both the name * and the id AVL trees, and then add the new entry. */ found_name = avltree_insert(&new->gname_node, &gname_tree); if (unlikely(found_name)) { tmp = avltree_container_of(found_name, struct cache_group, gname_node); avltree_remove(found_name, &gname_tree); avltree_remove(&tmp->gid_node, &gid_tree); gid_cache[tmp->gid % id_cache_size] = NULL; gsh_free(tmp); found_name = avltree_insert(&new->gname_node, &gname_tree); assert(found_name == NULL); } found_id = avltree_insert(&new->gid_node, &gid_tree); if (unlikely(found_id)) { tmp = avltree_container_of(found_id, struct cache_group, gid_node); gid_cache[tmp->gid % id_cache_size] = NULL; avltree_remove(found_id, &gid_tree); avltree_remove(&tmp->gname_node, &gname_tree); gsh_free(tmp); found_id = avltree_insert(&new->gid_node, &gid_tree); assert(found_id == NULL); } gid_cache[gid % id_cache_size] = &new->gid_node; return true; } /** * @brief Look up a user by name * * @note The caller must hold idmapper_user_lock for read. * * @param[in] name The user name to look up. * @param[out] uid The user ID found. May be NULL if the caller * isn't interested in the UID. (This seems * unlikely.) * @param[out] gid The GID for the user, or NULL if there is * none. The caller may specify NULL if it isn't * interested. * * @retval true on success. * @retval false if we need to try, try again. */ bool idmapper_lookup_by_uname(const struct gsh_buffdesc *name, uid_t *uid, const gid_t **gid, bool gss_princ) { struct cache_user prototype = { .uname = *name }; struct avltree_node *found_node = avltree_lookup(&prototype.uname_node, &uname_tree); struct cache_user *found_user; void **cache_slot; if (unlikely(!found_node)) return false; found_user = avltree_container_of(found_node, struct cache_user, uname_node); if (!gss_princ) { /* I assume that if someone likes this user enough to look it up by name, they'll like it enough to look it up by ID later. If the name is gss principal it does not have entry in uid tree */ cache_slot = (void **) &uid_cache[found_user->uid % id_cache_size]; atomic_store_voidptr(cache_slot, &found_user->uid_node); } if (likely(uid)) *uid = found_user->uid; if (unlikely(gid)) *gid = (found_user->gid_set ? &found_user->gid : NULL); return true; } /** * @brief Look up a user by ID * * @note The caller must hold idmapper_user_lock for read. * * @param[in] uid The user ID to look up. * @param[out] name The user name to look up. (May be NULL if the user * doesn't care about the name.) * @param[out] gid The GID for the user, or NULL if there is * none. The caller may specify NULL if it isn't * interested. * * @retval true on success. * @retval false if we weren't so successful. */ bool idmapper_lookup_by_uid(const uid_t uid, const struct gsh_buffdesc **name, const gid_t **gid) { struct cache_user prototype = { .uid = uid }; void **cache_slot = (void **)&uid_cache[uid % id_cache_size]; struct avltree_node *found_node = atomic_fetch_voidptr(cache_slot); struct cache_user *found_user; bool found = false; if (likely(found_node)) { found_user = avltree_container_of(found_node, struct cache_user, uid_node); if (found_user->uid == uid) found = true; } if (unlikely(!found)) { found_node = avltree_lookup(&prototype.uid_node, &uid_tree); if (unlikely(!found_node)) return false; atomic_store_voidptr(cache_slot, found_node); found_user = avltree_container_of(found_node, struct cache_user, uid_node); } if (likely(name)) *name = &found_user->uname; if (gid) *gid = (found_user->gid_set ? &found_user->gid : NULL); return true; } /** * @brief Lookup a group by name * * @note The caller must hold idmapper_group_lock for read. * * @param[in] name The user name to look up. * @param[out] gid The group ID found. May be NULL if the caller * isn't interested in the GID. (This seems * unlikely, since you can't get anything else from * this function.) * * @retval true on success. * @retval false if we need to try, try again. */ bool idmapper_lookup_by_gname(const struct gsh_buffdesc *name, uid_t *gid) { struct cache_group prototype = { .gname = *name }; struct avltree_node *found_node = avltree_lookup(&prototype.gname_node, &gname_tree); struct cache_group *found_group; void **cache_slot; if (unlikely(!found_node)) return false; found_group = avltree_container_of(found_node, struct cache_group, gname_node); /* I assume that if someone likes this group enough to look it up by name, they'll like it enough to look it up by ID later. */ cache_slot = (void **)&gid_cache[found_group->gid % id_cache_size]; atomic_store_voidptr(cache_slot, &found_group->gid_node); if (likely(gid)) *gid = found_group->gid; else LogDebug(COMPONENT_IDMAPPER, "Caller is being weird."); return true; } /** * @brief Look up a group by ID * * @note The caller must hold idmapper_group_lock for read. * * @param[in] gid The group ID to look up. * @param[out] name The user name to look up. (May be NULL if the user * doesn't care about the name, which would be weird.) * * @retval true on success. * @retval false if we're most unfortunate. */ bool idmapper_lookup_by_gid(const gid_t gid, const struct gsh_buffdesc **name) { struct cache_group prototype = { .gid = gid }; void **cache_slot = (void **)&gid_cache[gid % id_cache_size]; struct avltree_node *found_node = atomic_fetch_voidptr(cache_slot); struct cache_group *found_group; bool found = false; if (likely(found_node)) { found_group = avltree_container_of(found_node, struct cache_group, gid_node); if (found_group->gid == gid) found = true; } if (unlikely(!found)) { found_node = avltree_lookup(&prototype.gid_node, &gid_tree); if (unlikely(!found_node)) return false; atomic_store_voidptr(cache_slot, found_node); found_group = avltree_container_of(found_node, struct cache_group, gid_node); } if (likely(name)) *name = &found_group->gname; else LogDebug(COMPONENT_IDMAPPER, "Caller is being weird."); return true; } /** * @brief Wipe out the idmapper cache */ void idmapper_clear_cache(void) { struct avltree_node *node; PTHREAD_RWLOCK_wrlock(&idmapper_user_lock); PTHREAD_RWLOCK_wrlock(&idmapper_group_lock); memset(uid_cache, 0, id_cache_size * sizeof(struct avltree_node *)); memset(gid_cache, 0, id_cache_size * sizeof(struct avltree_node *)); for (node = avltree_first(&uname_tree); node != NULL; node = avltree_first(&uname_tree)) { struct cache_user *user; user = avltree_container_of(node, struct cache_user, uname_node); avltree_remove(&user->uname_node, &uname_tree); avltree_remove(&user->uid_node, &uid_tree); gsh_free(user); } assert(avltree_first(&uid_tree) == NULL); for (node = avltree_first(&gname_tree); node != NULL; node = avltree_first(&gname_tree)) { struct cache_group *group; group = avltree_container_of(node, struct cache_group, gname_node); avltree_remove(&group->gname_node, &gname_tree); avltree_remove(&group->gid_node, &gid_tree); gsh_free(group); } assert(avltree_first(&gid_tree) == NULL); PTHREAD_RWLOCK_unlock(&idmapper_group_lock); PTHREAD_RWLOCK_unlock(&idmapper_user_lock); } /** @} */ nfs-ganesha-2.6.0/src/include/000077500000000000000000000000001324272410200161205ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/include/.gitignore000066400000000000000000000000461324272410200201100ustar00rootroot00000000000000config.h* !config-h.in.cmake stamp-h1 nfs-ganesha-2.6.0/src/include/9p.h000066400000000000000000000567501324272410200166360ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2011) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /* * Copied from 2.6.38-rc2 kernel, taken from diod sources * ( http://code.google.com/p/diod/ ) then adapted to ganesha */ #ifndef _9P_H #define _9P_H #include #include #include #include #include #include #include "9p_types.h" #include "fsal_types.h" #include "sal_data.h" #ifdef _USE_9P_RDMA #include #include #include "mooshika.h" #endif #define NB_PREALLOC_HASH_9P 100 #define NB_PREALLOC_FID_9P 100 #define PRIME_9P 17 #define _9P_LOCK_CLIENT_LEN 64 #define _9P_FID_PER_CONN 1024 /* _9P_MSG_SIZE: maximum message size for 9P/TCP */ #define _9P_MSG_SIZE 70000 #define _9P_HDR_SIZE 4 #define _9P_TYPE_SIZE 1 #define _9P_TAG_SIZE 2 #define _9P_STD_HDR_SIZE (_9P_HDR_SIZE + _9P_TYPE_SIZE + _9P_TAG_SIZE) /* _9P_BLK_SIZE: (fake) filesystem block size that we return in getattr() */ #define _9P_BLK_SIZE 4096 #define _9P_IOUNIT 0 /** * enum _9p_msg_t - 9P message types * @_9P_TLERROR: not used * @_9P_RLERROR: response for any failed request for 9P2000.L * @_9P_TSTATFS: file system status request * @_9P_RSTATFS: file system status response * @_9P_TSYMLINK: make symlink request * @_9P_RSYMLINK: make symlink response * @_9P_TMKNOD: create a special file object request * @_9P_RMKNOD: create a special file object response * @_9P_TLCREATE: prepare a handle for I/O on an new file for 9P2000.L * @_9P_RLCREATE: response with file access information for 9P2000.L * @_9P_TRENAME: rename request * @_9P_RRENAME: rename response * @_9P_TMKDIR: create a directory request * @_9P_RMKDIR: create a directory response * @_9P_TVERSION: version handshake request * @_9P_RVERSION: version handshake response * @_9P_TAUTH: request to establish authentication channel * @_9P_RAUTH: response with authentication information * @_9P_TATTACH: establish user access to file service * @_9P_RATTACH: response with top level handle to file hierarchy * @_9P_TERROR: not used * @_9P_RERROR: response for any failed request * @_9P_TFLUSH: request to abort a previous request * @_9P_RFLUSH: response when previous request has been cancelled * @_9P_TWALK: descend a directory hierarchy * @_9P_RWALK: response with new handle for position within hierarchy * @_9P_TOPEN: prepare a handle for I/O on an existing file * @_9P_ROPEN: response with file access information * @_9P_TCREATE: prepare a handle for I/O on a new file * @_9P_RCREATE: response with file access information * @_9P_TREAD: request to transfer data from a file or directory * @_9P_RREAD: response with data requested * @_9P_TWRITE: reuqest to transfer data to a file * @_9P_RWRITE: response with out much data was transfered to file * @_9P_TCLUNK: forget about a handle to an entity within the file system * @_9P_RCLUNK: response when server has forgotten about the handle * @_9P_TREMOVE: request to remove an entity from the hierarchy * @_9P_RREMOVE: response when server has removed the entity * @_9P_TSTAT: request file entity attributes * @_9P_RSTAT: response with file entity attributes * @_9P_TWSTAT: request to update file entity attributes * @_9P_RWSTAT: response when file entity attributes are updated * * There are 14 basic operations in 9P2000, paired as * requests and responses. The one special case is ERROR * as there is no @_9P_TERROR request for clients to transmit to * the server, but the server may respond to any other request * with an @_9P_RERROR. * * See Also: http://plan9.bell-labs.com/sys/man/5/INDEX.html */ enum _9p_msg_t { _9P_TLERROR = 6, _9P_RLERROR, _9P_TSTATFS = 8, _9P_RSTATFS, _9P_TLOPEN = 12, _9P_RLOPEN, _9P_TLCREATE = 14, _9P_RLCREATE, _9P_TSYMLINK = 16, _9P_RSYMLINK, _9P_TMKNOD = 18, _9P_RMKNOD, _9P_TRENAME = 20, _9P_RRENAME, _9P_TREADLINK = 22, _9P_RREADLINK, _9P_TGETATTR = 24, _9P_RGETATTR, _9P_TSETATTR = 26, _9P_RSETATTR, _9P_TXATTRWALK = 30, _9P_RXATTRWALK, _9P_TXATTRCREATE = 32, _9P_RXATTRCREATE, _9P_TREADDIR = 40, _9P_RREADDIR, _9P_TFSYNC = 50, _9P_RFSYNC, _9P_TLOCK = 52, _9P_RLOCK, _9P_TGETLOCK = 54, _9P_RGETLOCK, _9P_TLINK = 70, _9P_RLINK, _9P_TMKDIR = 72, _9P_RMKDIR, _9P_TRENAMEAT = 74, _9P_RRENAMEAT, _9P_TUNLINKAT = 76, _9P_RUNLINKAT, _9P_TVERSION = 100, _9P_RVERSION, _9P_TAUTH = 102, _9P_RAUTH, _9P_TATTACH = 104, _9P_RATTACH, _9P_TERROR = 106, _9P_RERROR, _9P_TFLUSH = 108, _9P_RFLUSH, _9P_TWALK = 110, _9P_RWALK, _9P_TOPEN = 112, _9P_ROPEN, _9P_TCREATE = 114, _9P_RCREATE, _9P_TREAD = 116, _9P_RREAD, _9P_TWRITE = 118, _9P_RWRITE, _9P_TCLUNK = 120, _9P_RCLUNK, _9P_TREMOVE = 122, _9P_RREMOVE, _9P_TSTAT = 124, _9P_RSTAT, _9P_TWSTAT = 126, _9P_RWSTAT, }; /* Arbitrary max xattr size, 64k is the limit for VFS given in man xattr(7) */ #define _9P_XATTR_MAX_SIZE 65535 /** * 9p internal flags for xattrs: set as guard for read/write and actual * setxattr "flush" call */ enum _9p_xattr_write { _9P_XATTR_READ_ONLY, _9P_XATTR_CAN_WRITE, _9P_XATTR_DID_WRITE }; /** * enum _9p_qid_t - QID types * @_9P_QTDIR: directory * @_9P_QTAPPEND: append-only * @_9P_QTEXCL: excluse use (only one open handle allowed) * @_9P_QTMOUNT: mount points * @_9P_QTAUTH: authentication file * @_9P_QTTMP: non-backed-up files * @_9P_QTSYMLINK: symbolic links (9P2000.u) * @_9P_QTLINK: hard-link (9P2000.u) * @_9P_QTFILE: normal files * * QID types are a subset of permissions - they are primarily * used to differentiate semantics for a file system entity via * a jump-table. Their value is also the most signifigant 16 bits * of the permission_t * * See Also: http://plan9.bell-labs.com/magic/man2html/2/stat */ enum _9p_qid__ { _9P_QTDIR = 0x80, _9P_QTAPPEND = 0x40, _9P_QTEXCL = 0x20, _9P_QTMOUNT = 0x10, _9P_QTAUTH = 0x08, _9P_QTTMP = 0x04, _9P_QTSYMLINK = 0x02, _9P_QTLINK = 0x01, _9P_QTFILE = 0x00, }; /* 9P Magic Numbers */ #define _9P_NOTAG (u16)(~0) #define _9P_NOFID (u32)(~0) #define _9P_NONUNAME (u32)(~0) #define _9P_MAXWELEM 16 /* Various header lengths to check message sizes: */ /* size[4] Rread tag[2] count[4] data[count] */ #define _9P_ROOM_RREAD (_9P_STD_HDR_SIZE + 4) /* size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count] */ #define _9P_ROOM_TWRITE (_9P_STD_HDR_SIZE + 4 + 8 + 4) /* size[4] Rreaddir tag[2] count[4] data[count] */ #define _9P_ROOM_RREADDIR (_9P_STD_HDR_SIZE + 4) /** * @brief Length prefixed string type * * The protocol uses length prefixed strings for all * string data, so we replicate that for our internal * string members. */ struct _9p_str { u16 len; /*< Length of the string */ char *str; /*< The string */ }; /** * @brief file system entity information * * qids are /identifiers used by 9P servers to track file system * entities. The type is used to differentiate semantics for operations * on the entity (ie. read means something different on a directory than * on a file). The path provides a server unique index for an entity * (roughly analogous to an inode number), while the version is updated * every time a file is modified and can be used to maintain cache * coherency between clients and serves. * Servers will often differentiate purely synthetic entities by setting * their version to 0, signaling that they should never be cached and * should be accessed synchronously. * * See Also://plan9.bell-labs.com/magic/man2html/2/stat */ struct _9p_qid { u8 type; /*< Type */ u32 version; /*< Monotonically incrementing version number */ u64 path; /*< Per-server-unique ID * for a file system element */ }; /** * @brief Internal 9P structure containing client credentials. * * This structure wraps struct user_cred, adding a refcounter to know when it * should be released (it is shared between several op_ctx and fids). */ struct _9p_user_cred { struct user_cred creds; /*< Credentials. */ int64_t refcount; /*< Counter of references (to the container or the * creds field). */ }; /** * * @brief Internal 9P structure for xattr operations, linked in fid * * This structure is allocated as needed (xattrwalk/create) and freed * on clunk */ struct _9p_xattr_desc { char xattr_name[MAXNAMLEN + 1]; u64 xattr_size; u64 xattr_offset; enum _9p_xattr_write xattr_write; char xattr_content[]; }; struct _9p_fid { u32 fid; /** Ganesha export of the file (refcounted). */ struct gsh_export *export; struct _9p_user_cred *ucred; /*< Client credentials (refcounted). */ struct group_data *gdata; struct fsal_obj_handle *pentry; struct _9p_qid qid; struct state_t *state; struct fsal_obj_handle *ppentry; char name[MAXNAMLEN+1]; u32 opens; struct _9p_xattr_desc *xattr; }; enum _9p_trans_type { _9P_TCP, _9P_RDMA }; struct flush_condition; /* flush hook: * * We use this to insert the request in a list * so it can be found later during a TFLUSH. * The goal is to wait until a request has been fully * processed and the reply sent before we send a RFLUSH. * * When a TFLUSH arrives, its thread will fill `condition' * so we can wake it up later, after we have sent the reply * to the original request. */ struct _9p_flush_hook { int tag; struct flush_condition *condition; unsigned long sequence; struct glist_head list; }; struct _9p_flush_bucket { pthread_mutex_t lock; struct glist_head list; }; #define FLUSH_BUCKETS 32 struct _9p_conn { union trans_data { long int sockfd; #ifdef _USE_9P_RDMA msk_trans_t *rdma_trans; #endif } trans_data; enum _9p_trans_type trans_type; uint32_t refcount; struct gsh_client *client; struct timeval birth; /* This is useful if same sockfd is reused on socket's close/open */ struct _9p_fid *fids[_9P_FID_PER_CONN]; struct _9p_flush_bucket flush_buckets[FLUSH_BUCKETS]; unsigned long sequence; pthread_mutex_t sock_lock; struct sockaddr_storage addrpeer; struct export_perms export_perms; unsigned int msize; }; #ifdef _USE_9P_RDMA struct _9p_outqueue { msk_data_t *data; pthread_mutex_t lock; pthread_cond_t cond; }; struct _9p_rdma_priv_pernic { struct ibv_mr *outmr; struct ibv_mr *inmr; uint8_t *rdmabuf; msk_data_t *rdata; }; struct _9p_rdma_priv { struct _9p_conn *pconn; struct _9p_outqueue *outqueue; struct _9p_rdma_priv_pernic *pernic; }; #define _9p_rdma_priv_of(x) ((struct _9p_rdma_priv *)x->private_data) #endif struct _9p_request_data { char *_9pmsg; struct _9p_conn *pconn; #ifdef _USE_9P_RDMA msk_data_t *data; #endif struct _9p_flush_hook flush_hook; }; typedef int (*_9p_function_t) (struct _9p_request_data *req9p, u32 *plenout, char *preply); struct _9p_function_desc { _9p_function_t service_function; char *funcname; }; extern const struct _9p_function_desc _9pfuncdesc[]; #define _9p_getptr(__cursor, __pvar, __type) \ do { \ __pvar = (__type *)__cursor; \ __cursor += sizeof(__type); \ } while (0) #define _9p_getstr(__cursor, __len, __str) \ do { \ __len = (u16 *)__cursor; \ __cursor += sizeof(u16); \ __str = __cursor; \ __cursor += *__len; \ } while (0) #define _9p_setptr(__cursor, __pvar, __type) \ do { \ *((__type *)__cursor) = *__pvar; \ __cursor += sizeof(__type); \ } while (0) #define _9p_setvalue(__cursor, __var, __type) \ do { \ *((__type *)__cursor) = __var; \ __cursor += sizeof(__type); \ } while (0) #define _9p_savepos(__cursor, __savedpos, __type) \ do { \ __savedpos = __cursor; \ __cursor += sizeof(__type); \ } while (0) /* Insert a qid */ #define _9p_setqid(__cursor, __qid) \ do { \ *((u8 *)__cursor) = __qid.type; \ __cursor += sizeof(u8); \ *((u32 *)__cursor) = __qid.version; \ __cursor += sizeof(u32); \ *((u64 *)__cursor) = __qid.path; \ __cursor += sizeof(u64); \ } while (0) /* Insert a non-null terminated string */ #define _9p_setstr(__cursor, __len, __str) \ do { \ *((u16 *)__cursor) = __len; \ __cursor += sizeof(u16); \ memcpy(__cursor, __str, __len); \ __cursor += __len; \ } while (0) /* _9p_setbuffer: * Copy data from __buffer into the reply, * with a length u32 header. */ #define _9p_setbuffer(__cursor, __len, __buffer) \ do { \ *((u32 *)__cursor) = __len; \ __cursor += sizeof(u32); \ memcpy(__cursor, __buffer, __len); \ __cursor += __len; \ } while (0) /* _9p_setfilledbuffer: * Data has already been copied into the reply. * Only move the cursor and set the length. */ #define _9p_setfilledbuffer(__cursor, __len) \ do { \ *((u32 *)__cursor) = __len; \ __cursor += sizeof(u32) + __len; \ } while (0) /* _9p_getbuffertofill: * Get a pointer where to copy data in the reply. * This leaves room in the reply for a u32 len header */ #define _9p_getbuffertofill(__cursor) (((char *) (__cursor)) + sizeof(u32)) #define _9p_setinitptr(__cursor, __start, __reqtype) \ do { \ __cursor = __start + _9P_HDR_SIZE; \ *((u8 *)__cursor) = __reqtype; \ __cursor += sizeof(u8); \ } while (0) /* _9p_setendptr: * Calculate message size, and write this value in the * header of the 9p message. */ #define _9p_setendptr(__cursor, __start) \ (*((u32 *)__start) = (u32)(__cursor - __start)) /* _9p_checkbound: * Check that the message size is less than *__maxlen, * AND set *__maxlen to actual message size. */ #define _9p_checkbound(__cursor, __start, __maxlen) \ do { \ if ((u32)(__cursor - __start) > *__maxlen) \ return -1; \ else \ *__maxlen = (u32)(__cursor - __start); \ } while (0) /* Bit values for getattr valid field. */ #define _9P_GETATTR_MODE 0x00000001ULL #define _9P_GETATTR_NLINK 0x00000002ULL #define _9P_GETATTR_UID 0x00000004ULL #define _9P_GETATTR_GID 0x00000008ULL #define _9P_GETATTR_RDEV 0x00000010ULL #define _9P_GETATTR_ATIME 0x00000020ULL #define _9P_GETATTR_MTIME 0x00000040ULL #define _9P_GETATTR_CTIME 0x00000080ULL #define _9P_GETATTR_INO 0x00000100ULL #define _9P_GETATTR_SIZE 0x00000200ULL #define _9P_GETATTR_BLOCKS 0x00000400ULL #define _9P_GETATTR_BTIME 0x00000800ULL #define _9P_GETATTR_GEN 0x00001000ULL #define _9P_GETATTR_DATA_VERSION 0x00002000ULL #define _9P_GETATTR_BASIC 0x000007ffULL /* Mask for fields * up to BLOCKS */ #define _9P_GETATTR_ALL 0x00003fffULL /* Mask for all fields above */ /* Bit values for setattr valid field from . */ #define _9P_SETATTR_MODE 0x00000001UL #define _9P_SETATTR_UID 0x00000002UL #define _9P_SETATTR_GID 0x00000004UL #define _9P_SETATTR_SIZE 0x00000008UL #define _9P_SETATTR_ATIME 0x00000010UL #define _9P_SETATTR_MTIME 0x00000020UL #define _9P_SETATTR_CTIME 0x00000040UL #define _9P_SETATTR_ATIME_SET 0x00000080UL #define _9P_SETATTR_MTIME_SET 0x00000100UL /* Bit values for lock type. */ #define _9P_LOCK_TYPE_RDLCK 0 #define _9P_LOCK_TYPE_WRLCK 1 #define _9P_LOCK_TYPE_UNLCK 2 /* Bit values for lock status. */ #define _9P_LOCK_SUCCESS 0 #define _9P_LOCK_BLOCKED 1 #define _9P_LOCK_ERROR 2 #define _9P_LOCK_GRACE 3 /* Bit values for lock flags. */ #define _9P_LOCK_FLAGS_BLOCK 1 #define _9P_LOCK_FLAGS_RECLAIM 2 /** * @defgroup config_9p Structure and defaults for _9P * * @{ */ /** * @brief Default value for _9p_tcp_port */ #define _9P_TCP_PORT 564 /** * @brief Default value for _9p_rdma_port */ #define _9P_RDMA_PORT 5640 /** * @brief Default value for _9p_tcp_msize */ #define _9P_TCP_MSIZE 65536 /** * @brief Default value for _9p_rdma_msize */ #define _9P_RDMA_MSIZE 1048576 /** * @brief Default number of receive buffer per nic */ #define _9P_RDMA_INPOOL_SIZE 64 /** * @brief Default number of send buffer (total, not per nic) * * shared pool for sends - optimal when set oh-so-slightly * higher than the number of worker threads */ #define _9P_RDMA_OUTPOOL_SIZE 32 /** * @brief Default rdma connection backlog * (number of pending connection requests) */ #define _9P_RDMA_BACKLOG 10 /** * @brief 9p configuration */ struct _9p_param { /** TCP port for 9p operations. Defaults to _9P_TCP_PORT, settable by _9P_TCP_Port */ uint16_t _9p_tcp_port; /** RDMA port for 9p operations. Defaults to _9P_RDMA_PORT, settable by _9P_RDMA_Port */ uint16_t _9p_rdma_port; /** Msize for 9P operation on tcp. Defaults to _9P_TCP_MSIZE, settable by _9P_TCP_Msize */ uint32_t _9p_tcp_msize; /** Msize for 9P operation on rdma. Defaults to _9P_RDMA_MSIZE, settable by _9P_RDMA_Msize */ uint32_t _9p_rdma_msize; /** Backlog for 9P rdma connections. Defaults to _9P_RDMA_BACKLOG, settable by _9P_RDMA_Backlog */ uint16_t _9p_rdma_backlog; /** Input buffer pool size for 9P rdma connections. Defaults to _9P_RDMA_INPOOL_SIZE, settable by _9P_RDMA_Inpool_Size */ uint16_t _9p_rdma_inpool_size; /** Output buffer pool size for 9P rdma connections. Defaults to _9P_RDMA_OUTPOOL_SIZE, settable by _9P_RDMA_OutPool_Size */ uint16_t _9p_rdma_outpool_size; }; /** @} */ /* protocol parameter tables */ extern struct _9p_param _9p_param; extern struct config_block _9p_param_blk; /* service functions */ int _9p_init(void); /* Tools functions */ /** * @brief Increment the refcounter of a _9p_user_cred structure. * * @param creds Reference that is being copied. */ void get_9p_user_cred_ref(struct _9p_user_cred *creds); /** * @brief Release a reference to an _9p_user_cred structure. * * This function decrements the refcounter of the containing _9p_user_cred * structure. If this counter reaches 0, the structure is freed. * * @param creds The reference that is released. */ void release_9p_user_cred_ref(struct _9p_user_cred *creds); /** * @brief Initialize op_ctx for the current request. * * op_ctx must point to an allocated structure. * * @param pfid fid used to initialize the context. * @param req9p request date used to initialize export_perms. It can be NULL, * in this case export_perms will be uninitialized. */ void _9p_init_opctx(struct _9p_fid *pfid, struct _9p_request_data *req9p); /** * @brief Release resources taken by _9p_init_opctx. * * op_ctx contains several pointers to refcounted objects. This function * decrements these counters and sets the associated fields to NULL. */ void _9p_release_opctx(void); /** * @brief Free this fid after releasing its resources. * * This function can be used to free a partially allocated fid, when an error * occurs. To release a valid fid, use _9p_tools_clunk instead. * * @param[in,out] pfid pointer to fid entry. */ void free_fid(struct _9p_fid *pfid); int _9p_tools_get_req_context_by_uid(u32 uid, struct _9p_fid *pfid); int _9p_tools_get_req_context_by_name(int uname_len, char *uname_str, struct _9p_fid *pfid); int _9p_tools_errno(fsal_status_t fsal_status); void _9p_openflags2FSAL(u32 *inflags, fsal_openflags_t *outflags); int _9p_tools_clunk(struct _9p_fid *pfid); void _9p_cleanup_fids(struct _9p_conn *conn); static inline unsigned int _9p_openflags_to_share_access(u32 *inflags) { switch ((*inflags) & O_ACCMODE) { case O_RDONLY: return OPEN4_SHARE_ACCESS_READ; case O_WRONLY: return OPEN4_SHARE_ACCESS_WRITE; case O_RDWR: return OPEN4_SHARE_ACCESS_READ | OPEN4_SHARE_ACCESS_WRITE; default: return 0; } } #ifdef _USE_9P_RDMA /* 9P/RDMA callbacks */ void *_9p_rdma_handle_trans(void *arg); void _9p_rdma_callback_recv(msk_trans_t *trans, msk_data_t *pdata, void *arg); void _9p_rdma_callback_disconnect(msk_trans_t *trans); void _9p_rdma_callback_send(msk_trans_t *trans, msk_data_t *pdata, void *arg); void _9p_rdma_callback_recv_err(msk_trans_t *trans, msk_data_t *pdata, void *arg); void _9p_rdma_callback_send_err(msk_trans_t *trans, msk_data_t *pdata, void *arg); #endif void _9p_AddFlushHook(struct _9p_request_data *req, int tag, unsigned long sequence); void _9p_FlushFlushHook(struct _9p_conn *conn, int tag, unsigned long sequence); int _9p_LockAndTestFlushHook(struct _9p_request_data *req); void _9p_ReleaseFlushHook(struct _9p_request_data *req); void _9p_DiscardFlushHook(struct _9p_request_data *req); /* Protocol functions */ int _9p_not_2000L(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_clunk(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_attach(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_auth(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_lcreate(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_flush(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_getattr(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_getlock(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_link(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_lock(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_lopen(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_mkdir(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_mknod(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_read(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_readdir(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_readlink(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_setattr(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_symlink(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_remove(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_rename(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_renameat(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_statfs(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_fsync(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_unlinkat(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_version(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_walk(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_write(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_xattrcreate(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_xattrwalk(struct _9p_request_data *req9p, u32 *plenout, char *preply); int _9p_rerror(struct _9p_request_data *req9p, u16 *msgtag, u32 err, u32 *plenout, char *preply); #endif /* _9P_H */ nfs-ganesha-2.6.0/src/include/9p_types.h000066400000000000000000000023361324272410200200510ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright  2014 CohortFS, LLC. * Author: William Allen Simpson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ #ifndef _9P_TYPES_H #define _9P_TYPES_H #if 0 /* problematic redefinitions that may cause compiler complications */ typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; #else /* cleanly defined replacement */ #define u8 uint8_t #define u16 uint16_t #define u32 uint32_t #define u64 uint64_t #endif #endif /* _9P_TYPES_H */ nfs-ganesha-2.6.0/src/include/Connectathon_config_parsing.h000066400000000000000000000014621324272410200237670ustar00rootroot00000000000000#ifndef _CONFIG_PARSING_H #define _CONFIG_PARSING_H enum test_number { ONE = 1, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE }; struct btest { enum test_number num; enum test_number num2; int levels; int files; int dirs; int count; int size; int blocksize; char *bigfile; char *fname; char *dname; char *nname; char *sname; struct btest *nextbtest; }; struct testparam { char *dirtest; char *logfile; struct btest *btest; }; void btest_init_defaults(struct btest *b); void testparam_init_defaults(struct testparam *t); void free_testparam(struct testparam *t); char *get_test_directory(struct testparam *t); char *get_log_file(struct testparam *t); struct btest *get_btest_args(struct testparam *param, enum test_number k); struct testparam *readin_config(char *fname); #endif nfs-ganesha-2.6.0/src/include/FSAL/000077500000000000000000000000001324272410200166455ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/include/FSAL/access_check.h000066400000000000000000000024401324272410200214140ustar00rootroot00000000000000#ifndef _ACCESS_CHECK_H #define _ACCESS_CHECK_H /* A few headers required to have "struct stat" */ #include #include #include #include "fsal_api.h" /* fsal_test_access * common (default) access check method for fsal_obj_handle objects. */ fsal_status_t fsal_test_access(struct fsal_obj_handle *obj_hdl, fsal_accessflags_t access_type, fsal_accessflags_t *allowed, fsal_accessflags_t *denied, bool owner_skip); int display_fsal_v4mask(struct display_buffer *dspbuf, fsal_aceperm_t v4mask, bool is_dir); void fsal_set_credentials(const struct user_cred *creds); void fsal_save_ganesha_credentials(void); void fsal_restore_ganesha_credentials(void); void fsal_print_ace_int(log_components_t component, log_levels_t debug, fsal_ace_t *ace, char *file, int line, char *function); #define fsal_print_ace(component, debug, ace) \ fsal_print_ace_int((component), (debug), (ace), \ (char *) __FILE__, __LINE__, (char *) __func__) void fsal_print_acl_int(log_components_t component, log_levels_t debug, fsal_acl_t *acl, char *file, int line, char *function); #define fsal_print_acl(component, debug, acl) \ fsal_print_acl_int((component), (debug), (acl), \ (char *) __FILE__, __LINE__, (char *) __func__) #endif nfs-ganesha-2.6.0/src/include/FSAL/fsal_commonlib.h000066400000000000000000000161331324272410200220060ustar00rootroot00000000000000/* * * * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * @defgroup FSAL File-System Abstraction Layer * @{ */ /** * @file fsal_commomnlib.h * @brief Miscelaneous FSAL common library routines */ #ifndef FSAL_COMMONLIB_H #define FSAL_COMMONLIB_H #include "fsal_api.h" #include "sal_data.h" /* * fsal common utility functions */ /* fsal_module to fsal_export helpers */ int fsal_attach_export(struct fsal_module *fsal_hdl, struct glist_head *obj_link); void fsal_detach_export(struct fsal_module *fsal_hdl, struct glist_head *obj_link); /* fsal_export common methods */ void fsal_export_init(struct fsal_export *export); void fsal_export_stack(struct fsal_export *sub_export, struct fsal_export *super_export); void free_export_ops(struct fsal_export *exp_hdl); /* fsal_obj_handle common methods */ void fsal_obj_handle_init(struct fsal_obj_handle *, struct fsal_export *, object_file_type_t); void fsal_obj_handle_fini(struct fsal_obj_handle *obj); /* * pNFS DS Helpers */ void fsal_pnfs_ds_init(struct fsal_pnfs_ds *pds, struct fsal_module *fsal); void fsal_pnfs_ds_fini(struct fsal_pnfs_ds *pds); void fsal_ds_handle_init(struct fsal_ds_handle *dsh, struct fsal_pnfs_ds *pds); void fsal_ds_handle_fini(struct fsal_ds_handle *dsh); int open_dir_by_path_walk(int first_fd, const char *path, struct stat *stat); struct avltree avl_fsid; struct avltree avl_dev; struct glist_head posix_file_systems; pthread_rwlock_t fs_lock; void free_fs(struct fsal_filesystem *fs); int populate_posix_file_systems(bool force); int resolve_posix_filesystem(const char *path, struct fsal_module *fsal, struct fsal_export *exp, claim_filesystem_cb claim, unclaim_filesystem_cb unclaim, struct fsal_filesystem **root_fs); void release_posix_file_systems(void); int re_index_fs_fsid(struct fsal_filesystem *fs, enum fsid_type fsid_type, struct fsal_fsid__ *fsid); int re_index_fs_dev(struct fsal_filesystem *fs, struct fsal_dev__ *dev); int change_fsid_type(struct fsal_filesystem *fs, enum fsid_type fsid_type); struct fsal_filesystem *lookup_fsid_locked(struct fsal_fsid__ *fsid, enum fsid_type fsid_type); struct fsal_filesystem *lookup_dev_locked(struct fsal_dev__ *dev); struct fsal_filesystem *lookup_fsid(struct fsal_fsid__ *fsid, enum fsid_type fsid_type); struct fsal_filesystem *lookup_dev(struct fsal_dev__ *dev); void unclaim_fs(struct fsal_filesystem *this); int claim_posix_filesystems(const char *path, struct fsal_module *fsal, struct fsal_export *exp, claim_filesystem_cb claim, unclaim_filesystem_cb unclaim, struct fsal_filesystem **root_fs); int encode_fsid(char *buf, int max, struct fsal_fsid__ *fsid, enum fsid_type fsid_type); int decode_fsid(char *buf, int max, struct fsal_fsid__ *fsid, enum fsid_type fsid_type); fsal_errors_t fsal_inherit_acls(struct attrlist *attrs, fsal_acl_t *sacl, fsal_aceflag_t inherit); fsal_status_t fsal_remove_access(struct fsal_obj_handle *dir_hdl, struct fsal_obj_handle *rem_hdl, bool isdir); fsal_status_t fsal_rename_access(struct fsal_obj_handle *old_dir_hdl, struct fsal_obj_handle *src_obj_hdl, struct fsal_obj_handle *new_dir_hdl, struct fsal_obj_handle *dst_obj_hdl, bool isdir); fsal_status_t fsal_mode_to_acl(struct attrlist *attrs, fsal_acl_t *sacl); fsal_status_t fsal_acl_to_mode(struct attrlist *attrs); void set_common_verifier(struct attrlist *attrs, fsal_verifier_t verifier); void update_share_counters(struct fsal_share *share, fsal_openflags_t old_openflags, fsal_openflags_t new_openflags); fsal_status_t check_share_conflict(struct fsal_share *share, fsal_openflags_t openflags, bool bypass); fsal_status_t merge_share(struct fsal_share *orig_share, struct fsal_share *dupe_share); /** * @brief Function to open an fsal_obj_handle's global file descriptor. * * @param[in] obj_hdl File on which to operate * @param[in] openflags Mode for open * @param[out] fd File descriptor that is to be used * * @return FSAL status. */ typedef fsal_status_t (*fsal_open_func)(struct fsal_obj_handle *obj_hdl, fsal_openflags_t openflags, struct fsal_fd *fd); /** * @brief Function to close an fsal_obj_handle's global file descriptor. * * @param[in] obj_hdl File on which to operate * @param[in] fd File handle to close * * @return FSAL status. */ typedef fsal_status_t (*fsal_close_func)(struct fsal_obj_handle *obj_hdl, struct fsal_fd *fd); fsal_status_t fsal_reopen_obj(struct fsal_obj_handle *obj_hdl, bool check_share, bool bypass, fsal_openflags_t openflags, struct fsal_fd *my_fd, struct fsal_share *share, fsal_open_func open_func, fsal_close_func close_func, struct fsal_fd **out_fd, bool *has_lock, bool *closefd); fsal_status_t fsal_find_fd(struct fsal_fd **out_fd, struct fsal_obj_handle *obj_hdl, struct fsal_fd *my_fd, struct fsal_share *share, bool bypass, struct state_t *state, fsal_openflags_t openflags, fsal_open_func open_func, fsal_close_func close_func, bool *has_lock, bool *closefd, bool open_for_locks, bool *reusing_open_state_fd); /** * @brief Initialize a state_t structure * * @param[in] state The state to initialize * @param[in] exp_hdl Export state_t will be associated with * @param[in] state_type Type of state to allocate * @param[in] related_state Related state if appropriate * * @returns the state structure for streamlined coding. */ static inline struct state_t *init_state(struct state_t *state, struct fsal_export *exp_hdl, enum state_type state_type, struct state_t *related_state) { state->state_exp = exp_hdl; state->state_type = state_type; if (state_type == STATE_TYPE_LOCK || state_type == STATE_TYPE_NLM_LOCK) state->state_data.lock.openstate = related_state; return state; } bool check_verifier_stat(struct stat *st, fsal_verifier_t verifier); bool check_verifier_attrlist(struct attrlist *attrs, fsal_verifier_t verifier); #endif /* FSAL_COMMONLIB_H */ nfs-ganesha-2.6.0/src/include/FSAL/fsal_config.h000066400000000000000000000015021324272410200212660ustar00rootroot00000000000000/* * configuration structure management functions */ bool fsal_supports(struct fsal_staticfsinfo_t *info, fsal_fsinfo_options_t option); uint64_t fsal_maxfilesize(struct fsal_staticfsinfo_t *info); uint32_t fsal_maxlink(struct fsal_staticfsinfo_t *info); uint32_t fsal_maxnamelen(struct fsal_staticfsinfo_t *info); uint32_t fsal_maxpathlen(struct fsal_staticfsinfo_t *info); struct timespec fsal_lease_time(struct fsal_staticfsinfo_t *info); fsal_aclsupp_t fsal_acl_support(struct fsal_staticfsinfo_t *info); attrmask_t fsal_supported_attrs(struct fsal_staticfsinfo_t *info); uint32_t fsal_maxread(struct fsal_staticfsinfo_t *info); uint32_t fsal_maxwrite(struct fsal_staticfsinfo_t *info); uint32_t fsal_umask(struct fsal_staticfsinfo_t *info); uint32_t fsal_xattr_access_rights(struct fsal_staticfsinfo_t *info); nfs-ganesha-2.6.0/src/include/FSAL/fsal_init.h000066400000000000000000000017151324272410200207720ustar00rootroot00000000000000/** * @file fsal_init.h * @author Jim Lieb * @brief Module initialization */ /** * @brief Initializer macro * * Every FSAL module has an initializer. any function labeled as * MODULE_INIT will be called in order after the module is loaded and * before dlopen returns. This is where you register your fsal. * * The initializer function should use register_fsal to initialize * public data and get the default operation vectors, then override * them with module-specific methods. */ #define MODULE_INIT __attribute__((constructor)) /** * @brief Finalizer macro * * Every FSAL module *must* have a destructor to free any resources. * the function should assert() that the module can be safely unloaded. * However, the core should do the same check prior to attempting an * unload. The function must be defined as void foo(void), i.e. no args * passed and no returns evaluated. */ #define MODULE_FINI __attribute__((destructor)) nfs-ganesha-2.6.0/src/include/abstract_atomic.h000066400000000000000000001651151324272410200214410ustar00rootroot00000000000000/* * Copyright © 2012 Linux Box Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file abstract_atomic.h * @author Adam C. Emerson * @author Frank S. Filz * @brief Shim for compiler or library supplied atomic operations * * This file provides inline functions that provide atomic operations * appropriate to the compiler being used. (Someone can add support * for an appropriate library later on.) * * The types functions are provided for are: * * ptrdiff_t (fetch and store only) * time_t (fetch and store only) * void* (fetch and store only) * uintptr_t (fetch and store only) * int64_t * uint64_t * int32_t * uint21_t * int16_t * uint16_t * int8_t * uint8_t * size_t * * The functions provided are (using int64_t for example): * * int64_t atomic_add_int64_t(int64_t *augend, int64_t addend) * int64_t atomic_inc_int64_t(int64_t *var) * int64_t atomic_sub_int64_t(int64_t *minuend, int64_t subtrahend) * int64_t atomic_dec_int64_t(int64_t *var) * int64_t atomic_postadd_int64_t(int64_t *augend, int64_t addend) * int64_t atomic_postinc_int64_t(int64_t *var) * int64_t atomic_postsub_int64_t(int64_t *minuend, int64_t subtrahend) * int64_t atomic_postdec_int64_t(int64_t *var) * int64_t atomic_fetch_int64_t(int64_t *var) * void atomic_store_int64_t(int64_t *var, int64_t val) * * The following bit mask operations are provided for * uint64_t, uint32_t, uint_16t, and uint8_t: * * uint64_t atomic_clear_uint64_t_bits(uint64_t *var, uint64_t bits) * uint64_t atomic_set_uint64_t_bits(uint64_t *var, uint64_t bits) * uint64_t atomic_postclear_uint64_t_bits(uint64_t *var, * uint64_t atomic_postset_uint64_t_bits(uint64_t *var, * */ #ifndef _ABSTRACT_ATOMIC_H #define _ABSTRACT_ATOMIC_H #include #include #include #undef GCC_SYNC_FUNCTIONS #undef GCC_ATOMIC_FUNCTIONS #ifndef __GNUC__ #error Please edit abstract_atomic.h and implement support for \ non-GNU compilers. #else /* __GNUC__ */ #define ATOMIC_GCC_VERSION (__GNUC__ * 10000 \ + __GNUC_MINOR__ * 100 \ + __GNUC_PATCHLEVEL__) #if ((ATOMIC_GCC_VERSION) >= 40700) #define GCC_ATOMIC_FUNCTIONS 1 #elif defined(__APPLE__) && defined(__x86_64__) #include "atomic_x86_64.h" #elif ((ATOMIC_GCC_VERSION) >= 40100) #define GCC_SYNC_FUNCTIONS 1 #else #error This verison of GCC does not support atomics. #endif /* Version check */ #endif /* __GNUC__ */ /* * Preaddition, presubtraction, preincrement, predecrement (return the * value after the operation, by analogy with the ++n preincrement * operator.) */ /** * @brief Atomically add to an int64_t * * This function atomically adds to the supplied value. * * @param[in,out] augend Number to be added to * @param[in] addend Number to add * * @return The value after addition. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline int64_t atomic_add_int64_t(int64_t *augend, int64_t addend) { return __atomic_add_fetch(augend, addend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline int64_t atomic_add_int64_t(int64_t *augend, int64_t addend) { return __sync_add_and_fetch(augend, addend); } #endif /** * @brief Atomically increment an int64_t * * This function atomically adds 1 to the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value after increment. */ static inline int64_t atomic_inc_int64_t(int64_t *var) { return atomic_add_int64_t(var, 1); } /** * @brief Atomically subtract from an int64_t * * This function atomically subtracts from the supplied value. * * @param[in,out] minuend Number to be subtracted from * @param[in] subtrahend Number to subtract * * @return The value after subtraction. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline int64_t atomic_sub_int64_t(int64_t *minuend, int64_t subtrahend) { return __atomic_sub_fetch(minuend, subtrahend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline int64_t atomic_sub_int64_t(int64_t *minuend, int64_t subtrahend) { return __sync_sub_and_fetch(minuend, subtrahend); } #endif /** * @brief Atomically decrement an int64_t * * This function atomically subtracts 1 from the supplied value. * * @param[in,out] var Pointer to the variable to modify */ static inline int64_t atomic_dec_int64_t(int64_t *var) { return atomic_sub_int64_t(var, 1); } /** * @brief Atomically add to an int64_t * * This function atomically adds to the supplied value. * * @param[in,out] augend Number to be added to * @param[in] addend Number to add * * @return The value after addition. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint64_t atomic_add_uint64_t(uint64_t *augend, uint64_t addend) { return __atomic_add_fetch(augend, addend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint64_t atomic_add_uint64_t(uint64_t *augend, uint64_t addend) { return __sync_add_and_fetch(augend, addend); } #endif /** * @brief Atomically increment a uint64_t * * This function atomically adds 1 to the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value after increment. */ static inline uint64_t atomic_inc_uint64_t(uint64_t *var) { return atomic_add_uint64_t(var, 1); } /** * @brief Atomically subtract from a uint64_t * * This function atomically subtracts from the supplied value. * * @param[in,out] minuend Number to be subtracted from * @param[in] subtrahend Number to subtract * * @return The value after subtraction. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint64_t atomic_sub_uint64_t(uint64_t *minuend, uint64_t subtrahend) { return __atomic_sub_fetch(minuend, subtrahend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint64_t atomic_sub_uint64_t(uint64_t *minuend, uint64_t subtrahend) { return __sync_sub_and_fetch(minuend, subtrahend); } #endif /** * @brief Atomically decrement a uint64_t * * This function atomically subtracts 1 from the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value after decrement. */ static inline uint64_t atomic_dec_uint64_t(uint64_t *var) { return atomic_sub_uint64_t(var, 1); } /** * @brief Atomically add to an int32_t * * This function atomically adds to the supplied value. * * @param[in,out] augend Number to be added to * @param[in] addend Number to add * * @return The value after addition. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline int32_t atomic_add_int32_t(int32_t *augend, int32_t addend) { return __atomic_add_fetch(augend, addend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline int32_t atomic_add_int32_t(int32_t *augend, int32_t addend) { return __sync_add_and_fetch(augend, addend); } #endif /** * @brief Atomically increment an int32_t * * This function atomically adds 1 to the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value after increment. */ static inline int32_t atomic_inc_int32_t(int32_t *var) { return atomic_add_int32_t(var, 1); } /** * @brief Atomically subtract from an int32_t * * This function atomically subtracts from the supplied value. * * @param[in,out] minuend Number to be subtracted from * @param[in] subtrahend Number to subtract * * @return The value after subtraction. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline int32_t atomic_sub_int32_t(int32_t *minuend, int32_t subtrahend) { return __atomic_sub_fetch(minuend, subtrahend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline int32_t atomic_sub_int32_t(int32_t *minuend, int32_t subtrahend) { return __sync_sub_and_fetch(minuend, subtrahend); } #endif /** * @brief Atomically decrement an int32_t * * This function atomically subtracts 1 from the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value after decrement. */ static inline int32_t atomic_dec_int32_t(int32_t *var) { return atomic_sub_int32_t(var, 1); } /** * @brief Atomically add to a uint32_t * * This function atomically adds to the supplied value. * * @param[in,out] augend Number to be added to * @param[in] addend Number to add * * @return The value after addition. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint32_t atomic_add_uint32_t(uint32_t *augend, uint32_t addend) { return __atomic_add_fetch(augend, addend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint32_t atomic_add_uint32_t(uint32_t *augend, uint32_t addend) { return __sync_add_and_fetch(augend, addend); } #endif /** * @brief Atomically increment a uint32_t * * This function atomically adds 1 to the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value after increment. */ static inline uint32_t atomic_inc_uint32_t(uint32_t *var) { return atomic_add_uint32_t(var, 1); } /** * @brief Atomically subtract from a uint32_t * * This function atomically subtracts from the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value after subtraction. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint32_t atomic_sub_uint32_t(uint32_t *var, uint32_t sub) { return __atomic_sub_fetch(var, sub, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint32_t atomic_sub_uint32_t(uint32_t *var, uint32_t sub) { return __sync_sub_and_fetch(var, sub); } #endif /** * @brief Atomically decrement a uint32_t * * This function atomically subtracts 1 from the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value after decrement. */ static inline uint32_t atomic_dec_uint32_t(uint32_t *var) { return atomic_sub_uint32_t(var, 1); } /** * @brief Atomically add to an int16_t * * This function atomically adds to the supplied value. * * @param[in,out] augend Number to be added to * @param[in] addend Number to add * * @return The value after addition. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline int16_t atomic_add_int16_t(int16_t *augend, int16_t addend) { return __atomic_add_fetch(augend, addend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline int16_t atomic_add_int16_t(int16_t *augend, int16_t addend) { return __sync_add_and_fetch(augend, addend); } #endif /** * @brief Atomically increment an int16_t * * This function atomically adds 1 to the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value after increment. */ static inline int16_t atomic_inc_int16_t(int16_t *var) { return atomic_add_int16_t(var, 1); } /** * @brief Atomically subtract from an int16_t * * This function atomically subtracts from the supplied value. * * @param[in,out] minuend Number to be subtracted from * @param[in] subtrahend Number to subtract * * @return The value after subtraction. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline int16_t atomic_sub_int16_t(int16_t *minuend, int16_t subtrahend) { return __atomic_sub_fetch(minuend, subtrahend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline int16_t atomic_sub_int16_t(int16_t *minuend, int16_t subtrahend) { return __sync_sub_and_fetch(minuend, subtrahend); } #endif /** * @brief Atomically decrement an int16_t * * This function atomically subtracts 1 from the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value after decrement. */ static inline int16_t atomic_dec_int16_t(int16_t *var) { return atomic_sub_int16_t(var, 1); } /** * @brief Atomically add to a uint16_t * * This function atomically adds to the supplied value. * * @param[in,out] augend Number to be added to * @param[in] addend Number to add * * @return The value after addition. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint16_t atomic_add_uint16_t(uint16_t *augend, uint16_t addend) { return __atomic_add_fetch(augend, addend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint16_t atomic_add_uint16_t(uint16_t *augend, uint16_t addend) { return __sync_add_and_fetch(augend, addend); } #endif /** * @brief Atomically increment a uint16_t * * This function atomically adds 1 to the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value after increment. */ static inline uint16_t atomic_inc_uint16_t(uint16_t *var) { return atomic_add_uint16_t(var, 1); } /** * @brief Atomically subtract from a uint16_t * * This function atomically subtracts from the supplied value. * * @param[in,out] minuend Number to be subtracted from * @param[in] subtrahend Number to subtract * * @return The value after subtraction. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint16_t atomic_sub_uint16_t(uint16_t *minuend, uint16_t subtrahend) { return __atomic_sub_fetch(minuend, subtrahend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint16_t atomic_sub_uint16_t(uint16_t *minuend, uint16_t subtrahend) { return __sync_sub_and_fetch(minuend, subtrahend); } #endif /** * @brief Atomically decrement a uint16_t * * This function atomically subtracts 1 from the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value after decrement. */ static inline uint16_t atomic_dec_uint16_t(uint16_t *var) { return atomic_sub_uint16_t(var, 1); } /** * @brief Atomically add to an int8_t * * This function atomically adds to the supplied value. * * @param[in,out] augend Number to be added to * @param[in] addend Number to add * * @return The value after addition. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline int8_t atomic_add_int8_t(int8_t *augend, int8_t addend) { return __atomic_add_fetch(augend, addend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline int8_t atomic_add_int8_t(int8_t *augend, int8_t addend) { return __sync_add_and_fetch(augend, addend); } #endif /** * @brief Atomically increment an int8_t * * This function atomically adds 1 to the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value after increment. */ static inline int8_t atomic_inc_int8_t(int8_t *var) { return atomic_add_int8_t(var, 1); } /** * @brief Atomically subtract from an int8_t * * This function atomically subtracts from the supplied value. * * @param[in,out] minuend Number to be subtracted from * @param[in] subtrahend Number to subtract * * @return The value after subtraction. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline int8_t atomic_sub_int8_t(int8_t *minuend, int8_t subtrahend) { return __atomic_sub_fetch(minuend, subtrahend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline int8_t atomic_sub_int8_t(int8_t *minuend, int8_t subtrahend) { return __sync_sub_and_fetch(minuend, subtrahend); } #endif /** * @brief Atomically decrement an int8_t * * This function atomically subtracts 1 from the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value after decrement. */ static inline int8_t atomic_dec_int8_t(int8_t *var) { return atomic_sub_int8_t(var, 1); } /** * @brief Atomically add to a uint8_t * * This function atomically adds to the supplied value. * * @param[in,out] augend Number to be added to * @param[in] addend Number to add * * @return The value after addition. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint8_t atomic_add_uint8_t(uint8_t *augend, int8_t addend) { return __atomic_add_fetch(augend, addend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint8_t atomic_add_uint8_t(uint8_t *augend, int8_t addend) { return __sync_add_and_fetch(augend, addend); } #endif /** * @brief Atomically increment a uint8_t * * This function atomically adds 1 to the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value after increment. */ static inline uint8_t atomic_inc_uint8_t(uint8_t *var) { return atomic_add_uint8_t(var, 1); } /** * @brief Atomically subtract from a uint8_t * * This function atomically subtracts from the supplied value. * * @param[in,out] minuend Number to be subtracted from * @param[in] subtrahend Number to subtract * * @return The value after subtraction. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint8_t atomic_sub_uint8_t(uint8_t *minuend, uint8_t subtrahend) { return __atomic_sub_fetch(minuend, subtrahend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint8_t atomic_sub_uint8_t(uint8_t *minuend, uint8_t subtrahend) { return __sync_sub_and_fetch(minuend, subtrahend); } #endif /** * @brief Atomically decrement a uint8_t * * This function atomically subtracts 1 from the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value after decrement. */ static inline uint8_t atomic_dec_uint8_t(uint8_t *var) { return atomic_sub_uint8_t(var, 1); } /** * @brief Atomically add to a size_t * * This function atomically adds to the supplied value. * * @param[in,out] augend Number to be added to * @param[in] addend Number to add * * @return The value after addition. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline size_t atomic_add_size_t(size_t *augend, size_t addend) { return __atomic_add_fetch(augend, addend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline size_t atomic_add_size_t(size_t *augend, size_t addend) { return __sync_add_and_fetch(augend, addend); } #endif /** * @brief Atomically increment a size_t * * This function atomically adds 1 to the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value after increment. */ static inline size_t atomic_inc_size_t(size_t *var) { return atomic_add_size_t(var, 1); } /** * @brief Atomically subtract from a size_t * * This function atomically subtracts from the supplied value. * * @param[in,out] minuend Number to be subtracted from * @param[in] subtrahend Number to subtract * * @return The value after subtraction. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline size_t atomic_sub_size_t(size_t *minuend, size_t subtrahend) { return __atomic_sub_fetch(minuend, subtrahend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline size_t atomic_sub_size_t(size_t *minuend, size_t subtrahend) { return __sync_sub_and_fetch(minuend, subtrahend); } #endif /** * @brief Atomically decrement a size_t * * This function atomically subtracts 1 from the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value after decrement. */ static inline size_t atomic_dec_size_t(size_t *var) { return atomic_sub_size_t(var, 1); } /* * Postaddition, postsubtraction, postincrement, postdecrement (return the * value before the operation, by analogy with the n++ postincrement * operator.) */ /** * @brief Atomically add to an int64_t * * This function atomically adds to the supplied value. * * @param[in,out] augend Number to be added to * @param[in] addend Number to add * * @return The value before addition. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline int64_t atomic_postadd_int64_t(int64_t *augend, uint64_t addend) { return __atomic_fetch_add(augend, addend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline int64_t atomic_postadd_int64_t(int64_t *augend, int64_t addend) { return __sync_fetch_and_add(augend, addend); } #endif /** * @brief Atomically increment an int64_t * * This function atomically adds 1 to the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value before increment. */ static inline int64_t atomic_postinc_int64_t(int64_t *var) { return atomic_postadd_int64_t(var, 1); } /** * @brief Atomically subtract from an int64_t * * This function atomically subtracts from the supplied value. * * @param[in,out] minuend Number to be subtracted from * @param[in] subtrahend Number to subtract * * @return The value before subtraction. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline int64_t atomic_postsub_int64_t(int64_t *minuend, int64_t subtrahend) { return __atomic_fetch_sub(minuend, subtrahend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline int64_t atomic_postsub_int64_t(int64_t *minuend, int64_t subtrahend) { return __sync_fetch_and_sub(minuend, subtrahend); } #endif /** * @brief Atomically decrement an int64_t * * This function atomically subtracts 1 from the supplied value. * * @param[in,out] var Pointer to the variable to modify */ static inline int64_t atomic_postdec_int64_t(int64_t *var) { return atomic_postsub_int64_t(var, 1); } /** * @brief Atomically add to an int64_t * * This function atomically adds to the supplied value. * * @param[in,out] augend Number to be added to * @param[in] addend Number to add * * @return The value before addition. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint64_t atomic_postadd_uint64_t(uint64_t *augend, uint64_t addend) { return __atomic_fetch_add(augend, addend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint64_t atomic_postadd_uint64_t(uint64_t *augend, uint64_t addend) { return __sync_fetch_and_add(augend, addend); } #endif /** * @brief Atomically increment a uint64_t * * This function atomically adds 1 to the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value before increment. */ static inline uint64_t atomic_postinc_uint64_t(uint64_t *var) { return atomic_postadd_uint64_t(var, 1); } /** * @brief Atomically subtract from a uint64_t * * This function atomically subtracts from the supplied value. * * @param[in,out] minuend Number to be subtracted from * @param[in] subtrahend Number to subtract * * @return The value before subtraction. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint64_t atomic_postsub_uint64_t(uint64_t *minuend, uint64_t subtrahend) { return __atomic_fetch_sub(minuend, subtrahend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint64_t atomic_postsub_uint64_t(uint64_t *minuend, uint64_t subtrahend) { return __sync_fetch_and_sub(minuend, subtrahend); } #endif /** * @brief Atomically decrement a uint64_t * * This function atomically subtracts 1 from the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value before decrement. */ static inline uint64_t atomic_postdec_uint64_t(uint64_t *var) { return atomic_postsub_uint64_t(var, 1); } /** * @brief Atomically add to an int32_t * * This function atomically adds to the supplied value. * * @param[in,out] augend Number to be added to * @param[in] addend Number to add * * @return The value before addition. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline int32_t atomic_postadd_int32_t(int32_t *augend, int32_t addend) { return __atomic_fetch_add(augend, addend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline int32_t atomic_postadd_int32_t(int32_t *augend, int32_t addend) { return __sync_fetch_and_add(augend, addend); } #endif /** * @brief Atomically increment an int32_t * * This function atomically adds 1 to the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value before increment. */ static inline int32_t atomic_postinc_int32_t(int32_t *var) { return atomic_postadd_int32_t(var, 1); } /** * @brief Atomically subtract from an int32_t * * This function atomically subtracts from the supplied value. * * @param[in,out] minuend Number to be subtracted from * @param[in] subtrahend Number to subtract * * @return The value before subtraction. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline int32_t atomic_postsub_int32_t(int32_t *minuend, int32_t subtrahend) { return __atomic_fetch_sub(minuend, subtrahend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline int32_t atomic_postsub_int32_t(int32_t *minuend, int32_t subtrahend) { return __sync_fetch_and_sub(minuend, subtrahend); } #endif /** * @brief Atomically decrement an int32_t * * This function atomically subtracts 1 from the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value before decrement. */ static inline int32_t atomic_postdec_int32_t(int32_t *var) { return atomic_postsub_int32_t(var, 1); } /** * @brief Atomically add to a uint32_t * * This function atomically adds to the supplied value. * * @param[in,out] augend Number to be added to * @param[in] addend Number to add * * @return The value before addition. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint32_t atomic_postadd_uint32_t(uint32_t *augend, uint32_t addend) { return __atomic_fetch_add(augend, addend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint32_t atomic_postadd_uint32_t(uint32_t *augend, uint32_t addend) { return __sync_fetch_and_add(augend, addend); } #endif /** * @brief Atomically increment a uint32_t * * This function atomically adds 1 to the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value before increment. */ static inline uint32_t atomic_postinc_uint32_t(uint32_t *var) { return atomic_postadd_uint32_t(var, 1); } /** * @brief Atomically subtract from a uint32_t * * This function atomically subtracts from the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value before subtraction. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint32_t atomic_postsub_uint32_t(uint32_t *var, uint32_t sub) { return __atomic_fetch_sub(var, sub, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint32_t atomic_postsub_uint32_t(uint32_t *var, uint32_t sub) { return __sync_fetch_and_sub(var, sub); } #endif /** * @brief Atomically decrement a uint32_t * * This function atomically subtracts 1 from the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value before decrement. */ static inline uint32_t atomic_postdec_uint32_t(uint32_t *var) { return atomic_postsub_uint32_t(var, 1); } /** * @brief Atomically add to an int16_t * * This function atomically adds to the supplied value. * * @param[in,out] augend Number to be added to * @param[in] addend Number to add * * @return The value before addition. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline int16_t atomic_postadd_int16_t(int16_t *augend, int16_t addend) { return __atomic_fetch_add(augend, addend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline int16_t atomic_postadd_int16_t(int16_t *augend, int16_t addend) { return __sync_fetch_and_add(augend, addend); } #endif /** * @brief Atomically increment an int16_t * * This function atomically adds 1 to the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value before increment. */ static inline int16_t atomic_postinc_int16_t(int16_t *var) { return atomic_postadd_int16_t(var, 1); } /** * @brief Atomically subtract from an int16_t * * This function atomically subtracts from the supplied value. * * @param[in,out] minuend Number to be subtracted from * @param[in] subtrahend Number to subtract * * @return The value before subtraction. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline int16_t atomic_postsub_int16_t(int16_t *minuend, int16_t subtrahend) { return __atomic_fetch_sub(minuend, subtrahend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline int16_t atomic_postsub_int16_t(int16_t *minuend, int16_t subtrahend) { return __sync_fetch_and_sub(minuend, subtrahend); } #endif /** * @brief Atomically decrement an int16_t * * This function atomically subtracts 1 from the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value before decrement. */ static inline int16_t atomic_postdec_int16_t(int16_t *var) { return atomic_postsub_int16_t(var, 1); } /** * @brief Atomically add to a uint16_t * * This function atomically adds to the supplied value. * * @param[in,out] augend Number to be added to * @param[in] addend Number to add * * @return The value before addition. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint16_t atomic_postadd_uint16_t(uint16_t *augend, uint16_t addend) { return __atomic_fetch_add(augend, addend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint16_t atomic_postadd_uint16_t(uint16_t *augend, uint16_t addend) { return __sync_fetch_and_add(augend, addend); } #endif /** * @brief Atomically increment a uint16_t * * This function atomically adds 1 to the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value before increment. */ static inline uint16_t atomic_postinc_uint16_t(uint16_t *var) { return atomic_postadd_uint16_t(var, 1); } /** * @brief Atomically subtract from a uint16_t * * This function atomically subtracts from the supplied value. * * @param[in,out] minuend Number to be subtracted from * @param[in] subtrahend Number to subtract * * @return The value before subtraction. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint16_t atomic_postsub_uint16_t(uint16_t *minuend, uint16_t subtrahend) { return __atomic_fetch_sub(minuend, subtrahend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint16_t atomic_postsub_uint16_t(uint16_t *minuend, uint16_t subtrahend) { return __sync_fetch_and_sub(minuend, subtrahend); } #endif /** * @brief Atomically decrement a uint16_t * * This function atomically subtracts 1 from the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value before decrement. */ static inline uint16_t atomic_postdec_uint16_t(uint16_t *var) { return atomic_postsub_uint16_t(var, 1); } /** * @brief Atomically add to an int8_t * * This function atomically adds to the supplied value. * * @param[in,out] augend Number to be added to * @param[in] addend Number to add * * @return The value before addition. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline int8_t atomic_postadd_int8_t(int8_t *augend, int8_t addend) { return __atomic_fetch_add(augend, addend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline int8_t atomic_postadd_int8_t(int8_t *augend, int8_t addend) { return __sync_fetch_and_add(augend, addend); } #endif /** * @brief Atomically increment an int8_t * * This function atomically adds 1 to the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value before increment. */ static inline int8_t atomic_postinc_int8_t(int8_t *var) { return atomic_postadd_int8_t(var, 1); } /** * @brief Atomically subtract from an int8_t * * This function atomically subtracts from the supplied value. * * @param[in,out] minuend Number to be subtracted from * @param[in] subtrahend Number to subtract * * @return The value before subtraction. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline int8_t atomic_postsub_int8_t(int8_t *minuend, int8_t subtrahend) { return __atomic_fetch_sub(minuend, subtrahend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline int8_t atomic_postsub_int8_t(int8_t *minuend, int8_t subtrahend) { return __sync_fetch_and_sub(minuend, subtrahend); } #endif /** * @brief Atomically decrement an int8_t * * This function atomically subtracts 1 from the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value before decrement. */ static inline int8_t atomic_postdec_int8_t(int8_t *var) { return atomic_postsub_int8_t(var, 1); } /** * @brief Atomically add to a uint8_t * * This function atomically adds to the supplied value. * * @param[in,out] augend Number to be added to * @param[in] addend Number to add * * @return The value before addition. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint8_t atomic_postadd_uint8_t(uint8_t *augend, uint8_t addend) { return __atomic_fetch_add(augend, addend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint8_t atomic_postadd_uint8_t(uint8_t *augend, uint8_t addend) { return __sync_fetch_and_add(augend, addend); } #endif /** * @brief Atomically increment a uint8_t * * This function atomically adds 1 to the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value before increment. */ static inline uint8_t atomic_postinc_uint8_t(uint8_t *var) { return atomic_postadd_uint8_t(var, 1); } /** * @brief Atomically subtract from a uint8_t * * This function atomically subtracts from the supplied value. * * @param[in,out] minuend Number to be subtracted from * @param[in] subtrahend Number to subtract * * @return The value before subtraction. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint8_t atomic_postsub_uint8_t(uint8_t *minuend, uint8_t subtrahend) { return __atomic_fetch_sub(minuend, subtrahend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint8_t atomic_postsub_uint8_t(uint8_t *minuend, uint8_t subtrahend) { return __sync_fetch_and_sub(minuend, subtrahend); } #endif /** * @brief Atomically decrement a uint8_t * * This function atomically subtracts 1 from the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value before decrement. */ static inline uint8_t atomic_postdec_uint8_t(uint8_t *var) { return atomic_postsub_uint8_t(var, 1); } /** * @brief Atomically add to a size_t * * This function atomically adds to the supplied value. * * @param[in,out] augend Number to be added to * @param[in] addend Number to add * * @return The value before addition. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline size_t atomic_postadd_size_t(size_t *augend, size_t addend) { return __atomic_fetch_add(augend, addend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline size_t atomic_postadd_size_t(size_t *augend, size_t addend) { return __sync_fetch_and_add(augend, addend); } #endif /** * @brief Atomically increment a size_t * * This function atomically adds 1 to the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value before increment. */ static inline size_t atomic_postinc_size_t(size_t *var) { return atomic_postadd_size_t(var, 1); } /** * @brief Atomically subtract from a size_t * * This function atomically subtracts from the supplied value. * * @param[in,out] minuend Number to be subtracted from * @param[in] subtrahend Number to subtract * * @return The value before subtraction. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline size_t atomic_postsub_size_t(size_t *minuend, size_t subtrahend) { return __atomic_fetch_sub(minuend, subtrahend, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline size_t atomic_postsub_size_t(size_t *minuend, size_t subtrahend) { return __sync_fetch_and_sub(minuend, subtrahend); } #endif /** * @brief Atomically decrement a size_t * * This function atomically subtracts 1 from the supplied value. * * @param[in,out] var Pointer to the variable to modify * * @return The value before decrement. */ static inline size_t atomic_postdec_size_t(size_t *var) { return atomic_postsub_size_t(var, 1); } /* * Preclear and preset bits (return the value after the operation, by * analogy with the ++n preincrement operator.) */ /** * @brief Atomically clear bits in a uint64_t * * This function atomic clears the bits indicated. * * @param[in,out] var Pointer to the value to modify * @param[in] bits Bits to clear * * @return The value after clearing. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint64_t atomic_clear_uint64_t_bits(uint64_t *var, uint64_t bits) { return __atomic_and_fetch(var, ~bits, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint64_t atomic_clear_uint64_t_bits(uint64_t *var, uint64_t bits) { return __sync_and_and_fetch(var, ~bits); } #endif /** * @brief Atomically set bits in a uint64_t * * This function atomic clears the bits indicated. * * @param[in,out] var Pointer to the value to modify * @param[in] bits Bits to set * * @return The value after setting. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint64_t atomic_set_uint64_t_bits(uint64_t *var, uint64_t bits) { return __atomic_or_fetch(var, bits, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint64_t atomic_set_uint64_t_bits(uint64_t *var, uint64_t bits) { return __sync_or_and_fetch(var, bits); } #endif /** * @brief Atomically clear bits in a uint32_t * * This function atomic clears the bits indicated. * * @param[in,out] var Pointer to the value to modify * @param[in] bits Bits to clear * * @return The value after clearing. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint32_t atomic_clear_uint32_t_bits(uint32_t *var, uint32_t bits) { return __atomic_and_fetch(var, ~bits, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint32_t atomic_clear_uint32_t_bits(uint32_t *var, uint32_t bits) { return __sync_and_and_fetch(var, ~bits); } #endif /** * @brief Atomically set bits in a uint32_t * * This function atomic clears the bits indicated. * * @param[in,out] var Pointer to the value to modify * @param[in] bits Bits to set * * @return The value after setting. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint32_t atomic_set_uint32_t_bits(uint32_t *var, uint32_t bits) { return __atomic_or_fetch(var, bits, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint32_t atomic_set_uint32_t_bits(uint32_t *var, uint32_t bits) { return __sync_or_and_fetch(var, bits); } #endif /** * @brief Atomically clear bits in a uint16_t * * This function atomic clears the bits indicated. * * @param[in,out] var Pointer to the value to modify * @param[in] bits Bits to clear * * @return The value after clearing. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint16_t atomic_clear_uint16_t_bits(uint16_t *var, uint16_t bits) { return __atomic_and_fetch(var, ~bits, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint16_t atomic_clear_uint16_t_bits(uint16_t *var, uint16_t bits) { return __sync_and_and_fetch(var, ~bits); } #endif /** * @brief Atomically set bits in a uint16_t * * This function atomic clears the bits indicated. * * @param[in,out] var Pointer to the value to modify * @param[in] bits Bits to set * * @return The value after setting. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint16_t atomic_set_uint16_t_bits(uint16_t *var, uint16_t bits) { return __atomic_or_fetch(var, bits, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint16_t atomic_set_uint16_t_bits(uint16_t *var, uint16_t bits) { return __sync_or_and_fetch(var, bits); } #endif /** * @brief Atomically clear bits in a uint8_t * * This function atomic clears the bits indicated. * * @param[in,out] var Pointer to the value to modify * @param[in] bits Bits to clear * * @return The value after clearing. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint8_t atomic_clear_uint8_t_bits(uint8_t *var, uint8_t bits) { return __atomic_and_fetch(var, ~bits, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint8_t atomic_clear_uint8_t_bits(uint8_t *var, uint8_t bits) { return __sync_and_and_fetch(var, ~bits); } #endif /** * @brief Atomically set bits in a uint8_t * * This function atomic clears the bits indicated. * * @param[in,out] var Pointer to the value to modify * @param[in] bits Bits to set * * @return The value after setting. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint8_t atomic_set_uint8_t_bits(uint8_t *var, uint8_t bits) { return __atomic_or_fetch(var, bits, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint8_t atomic_set_uint8_t_bits(uint8_t *var, uint8_t bits) { return __sync_or_and_fetch(var, bits); } #endif /* * Postclear and postset bits (return the value before the operation, * by analogy with the n++ postincrement operator.) */ /** * @brief Atomically clear bits in a uint64_t * * This function atomic clears the bits indicated. * * @param[in,out] var Pointer to the value to modify * @param[in] bits Bits to clear * * @return The value before clearing. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint64_t atomic_postclear_uint64_t_bits(uint64_t *var, uint64_t bits) { return __atomic_fetch_and(var, ~bits, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint64_t atomic_postclear_uint64_t_bits(uint64_t *var, uint64_t bits) { return __sync_fetch_and_and(var, ~bits); } #endif /** * @brief Atomically set bits in a uint64_t * * This function atomic clears the bits indicated. * * @param[in,out] var Pointer to the value to modify * @param[in] bits Bits to set * * @return The value before setting. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint64_t atomic_postset_uint64_t_bits(uint64_t *var, uint64_t bits) { return __atomic_fetch_or(var, bits, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint64_t atomic_postset_uint64_t_bits(uint64_t *var, uint64_t bits) { return __sync_fetch_and_or(var, bits); } #endif /** * @brief Atomically clear bits in a uint32_t * * This function atomic clears the bits indicated. * * @param[in,out] var Pointer to the value to modify * @param[in] bits Bits to clear * * @return The value before clearing. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint32_t atomic_postclear_uint32_t_bits(uint32_t *var, uint32_t bits) { return __atomic_fetch_and(var, ~bits, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint32_t atomic_postclear_uint32_t_bits(uint32_t *var, uint32_t bits) { return __sync_fetch_and_and(var, ~bits); } #endif /** * @brief Atomically set bits in a uint32_t * * This function atomic clears the bits indicated. * * @param[in,out] var Pointer to the value to modify * @param[in] bits Bits to set * * @return The value before setting. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint32_t atomic_postset_uint32_t_bits(uint32_t *var, uint32_t bits) { return __atomic_fetch_or(var, bits, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint32_t atomic_postset_uint32_t_bits(uint32_t *var, uint32_t bits) { return __sync_fetch_and_or(var, bits); } #endif /** * @brief Atomically clear bits in a uint16_t * * This function atomic clears the bits indicated. * * @param[in,out] var Pointer to the value to modify * @param[in] bits Bits to clear * * @return The value before clearing. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint16_t atomic_postclear_uint16_t_bits(uint16_t *var, uint16_t bits) { return __atomic_fetch_and(var, ~bits, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint16_t atomic_postclear_uint16_t_bits(uint16_t *var, uint16_t bits) { return __sync_fetch_and_and(var, ~bits); } #endif /** * @brief Atomically set bits in a uint16_t * * This function atomic clears the bits indicated. * * @param[in,out] var Pointer to the value to modify * @param[in] bits Bits to set * * @return The value before setting. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint16_t atomic_postset_uint16_t_bits(uint16_t *var, uint16_t bits) { return __atomic_fetch_or(var, bits, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint16_t atomic_postset_uint16_t_bits(uint16_t *var, uint16_t bits) { return __sync_fetch_and_or(var, bits); } #endif /** * @brief Atomically clear bits in a uint8_t * * This function atomic clears the bits indicated. * * @param[in,out] var Pointer to the value to modify * @param[in] bits Bits to clear * * @return The value before clearing. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint8_t atomic_postclear_uint8_t_bits(uint8_t *var, uint8_t bits) { return __atomic_fetch_and(var, ~bits, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint8_t atomic_postclear_uint8_t_bits(uint8_t *var, uint8_t bits) { return __sync_fetch_and_and(var, ~bits); } #endif /** * @brief Atomically set bits in a uint8_t * * This function atomic clears the bits indicated. * * @param[in,out] var Pointer to the value to modify * @param[in] bits Bits to set * * @return The value before setting. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint8_t atomic_postset_uint8_t_bits(uint8_t *var, uint8_t bits) { return __atomic_fetch_or(var, bits, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint8_t atomic_postset_uint8_t_bits(uint8_t *var, uint8_t bits) { return __sync_fetch_and_or(var, bits); } #endif /* * Fetch and store */ /** * @brief Atomically fetch a size_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to fetch * * @return the value pointed to by var. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline size_t atomic_fetch_size_t(size_t *var) { return __atomic_load_n(var, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline size_t atomic_fetch_size_t(size_t *var) { return __sync_fetch_and_add(var, 0); } #endif /** * @brief Atomically store a size_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to modify * @param[in] val The value to store */ #ifdef GCC_ATOMIC_FUNCTIONS static inline void atomic_store_size_t(size_t *var, size_t val) { __atomic_store_n(var, val, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline void atomic_store_size_t(size_t *var, size_t val) { (void)__sync_lock_test_and_set(var, val); } #endif /** * @brief Atomically fetch a ptrdiff_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to fetch * * @return the value pointed to by var. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline ptrdiff_t atomic_fetch_ptrdiff_t(ptrdiff_t *var) { return __atomic_load_n(var, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline ptrdiff_t atomic_fetch_ptrdiff_t(ptrdiff_t *var) { return __sync_fetch_and_add(var, 0); } #endif /** * @brief Atomically store a ptrdiff_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to modify * @param[in] val The value to store */ #ifdef GCC_ATOMIC_FUNCTIONS static inline void atomic_store_ptrdiff_t(ptrdiff_t *var, ptrdiff_t val) { __atomic_store_n(var, val, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline void atomic_store_ptrdiff_t(ptrdiff_t *var, ptrdiff_t val) { (void)__sync_lock_test_and_set(var, val); } #endif /** * @brief Atomically fetch a time_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to fetch * * @return the value pointed to by var. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline time_t atomic_fetch_time_t(time_t *var) { return __atomic_load_n(var, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline time_t atomic_fetch_time_t(time_t *var) { return __sync_fetch_and_add(var, 0); } #endif /** * @brief Atomically store a time_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to modify * @param[in] val The value to store */ #ifdef GCC_ATOMIC_FUNCTIONS static inline void atomic_store_time_t(time_t *var, time_t val) { __atomic_store_n(var, val, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline void atomic_store_time_t(time_t *var, time_t val) { (void)__sync_lock_test_and_set(var, val); } #endif /** * @brief Atomically fetch a uintptr_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to fetch * * @return the value pointed to by var. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uintptr_t atomic_fetch_uintptr_t(uintptr_t *var) { return __atomic_load_n(var, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uintptr_t atomic_fetch_uintptr_t(uintptr_t *var) { return __sync_fetch_and_add(var, 0); } #endif /** * @brief Atomically store a uintptr_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to modify * @param[in] val The value to store */ #ifdef GCC_ATOMIC_FUNCTIONS static inline void atomic_store_uintptr_t(uintptr_t *var, uintptr_t val) { __atomic_store_n(var, val, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline void atomic_store_uintptr_t(uintptr_t *var, uintptr_t val) { (void)__sync_lock_test_and_set(var, 0); } #endif /** * @brief Atomically fetch a void * * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to fetch * * @return the value pointed to by var. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline void *atomic_fetch_voidptr(void **var) { return __atomic_load_n(var, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline void *atomic_fetch_voidptr(void **var) { return __sync_fetch_and_add(var, 0); } #endif /** * @brief Atomically store a void * * * This function atomically stores the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to modify * @param[in] val The value to store */ #ifdef GCC_ATOMIC_FUNCTIONS static inline void atomic_store_voidptr(void **var, void *val) { __atomic_store_n(var, val, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline void atomic_store_voidptr(void **var, void *val) { (void)__sync_lock_test_and_set(var, val); } #endif /** * @brief Atomically fetch an int64_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to fetch * * @return the value pointed to by var. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline int64_t atomic_fetch_int64_t(int64_t *var) { return __atomic_load_n(var, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline int64_t atomic_fetch_int64_t(int64_t *var) { return __sync_fetch_and_add(var, 0); } #endif /** * @brief Atomically store an int64_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to modify * @param[in] val The value to store */ #ifdef GCC_ATOMIC_FUNCTIONS static inline void atomic_store_int64_t(int64_t *var, int64_t val) { __atomic_store_n(var, val, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline void atomic_store_int64_t(int64_t *var, int64_t val) { (void)__sync_lock_test_and_set(var, val); } #endif /** * @brief Atomically fetch a uint64_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to fetch * * @return the value pointed to by var. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint64_t atomic_fetch_uint64_t(uint64_t *var) { return __atomic_load_n(var, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint64_t atomic_fetch_uint64_t(uint64_t *var) { return __sync_fetch_and_add(var, 0); } #endif /** * @brief Atomically store a uint64_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to modify * @param[in] val The value to store */ #ifdef GCC_ATOMIC_FUNCTIONS static inline void atomic_store_uint64_t(uint64_t *var, uint64_t val) { __atomic_store_n(var, val, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline void atomic_store_uint64_t(uint64_t *var, uint64_t val) { (void)__sync_lock_test_and_set(var, val); } #endif /** * @brief Atomically fetch an int32_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to fetch * * @return the value pointed to by var. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline int32_t atomic_fetch_int32_t(int32_t *var) { return __atomic_load_n(var, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline int32_t atomic_fetch_int32_t(int32_t *var) { return __sync_fetch_and_add(var, 0); } #endif /** * @brief Atomically store an int32_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to modify * @param[in] val The value to store */ #ifdef GCC_ATOMIC_FUNCTIONS static inline void atomic_store_int32_t(int32_t *var, int32_t val) { __atomic_store_n(var, val, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline void atomic_store_int32_t(int32_t *var, int32_t val) { (void)__sync_lock_test_and_set(var, val); } #endif /** * @brief Atomically fetch a uint32_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to fetch * * @return the value pointed to by var. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint32_t atomic_fetch_uint32_t(uint32_t *var) { return __atomic_load_n(var, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint32_t atomic_fetch_uint32_t(uint32_t *var) { return __sync_fetch_and_add(var, 0); } #endif /** * @brief Atomically store a uint32_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to modify * @param[in] val The value to store */ #ifdef GCC_ATOMIC_FUNCTIONS static inline void atomic_store_uint32_t(uint32_t *var, uint32_t val) { __atomic_store_n(var, val, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline void atomic_store_uint32_t(uint32_t *var, uint32_t val) { (void)__sync_lock_test_and_set(var, val); } #endif /** * @brief Atomically fetch an int16_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to fetch * * @return the value pointed to by var. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline int16_t atomic_fetch_int16_t(int16_t *var) { return __atomic_load_n(var, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline int16_t atomic_fetch_int16_t(int16_t *var) { return __sync_fetch_and_add(var, 0); } #endif /** * @brief Atomically store an int16_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to modify * @param[in] val The value to store */ #ifdef GCC_ATOMIC_FUNCTIONS static inline void atomic_store_int16_t(int16_t *var, int16_t val) { __atomic_store_n(var, val, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline void atomic_store_int16_t(int16_t *var, int16_t val) { (void)__sync_lock_test_and_set(var, val); } #endif /** * @brief Atomically fetch a uint16_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to fetch * * @return the value pointed to by var. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint16_t atomic_fetch_uint16_t(uint16_t *var) { return __atomic_load_n(var, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint16_t atomic_fetch_uint16_t(uint16_t *var) { return __sync_fetch_and_add(var, 0); } #endif /** * @brief Atomically store a uint16_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to modify * @param[in] val The value to store */ #ifdef GCC_ATOMIC_FUNCTIONS static inline void atomic_store_uint16_t(uint16_t *var, uint16_t val) { __atomic_store_n(var, val, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline void atomic_store_uint16_t(uint16_t *var, uint16_t val) { (void)__sync_lock_test_and_set(var, val); } #endif /** * @brief Atomically fetch a int8_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to fetch * * @return the value pointed to by var. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline int8_t atomic_fetch_int8_t(int8_t *var) { return __atomic_load_n(var, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline int8_t atomic_fetch_int8_t(int8_t *var) { return __sync_fetch_and_add(var, 0); } #endif /** * @brief Atomically store a int8_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to modify * @param[in] val The value to store */ #ifdef GCC_ATOMIC_FUNCTIONS static inline void atomic_store_int8_t(int8_t *var, int8_t val) { __atomic_store_n(var, val, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline void atomic_store_int8_t(int8_t *var, int8_t val) { (void)__sync_lock_test_and_set(var, val); } #endif /** * @brief Atomically fetch a uint8_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to fetch * * @return the value pointed to by var. */ #ifdef GCC_ATOMIC_FUNCTIONS static inline uint8_t atomic_fetch_uint8_t(uint8_t *var) { return __atomic_load_n(var, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline uint8_t atomic_fetch_uint8_t(uint8_t *var) { return __sync_fetch_and_add(var, 0); } #endif /** * @brief Atomically store a uint8_t * * This function atomically fetches the value indicated by the * supplied pointer. * * @param[in,out] var Pointer to the variable to modify * @param[in] val The value to store */ #ifdef GCC_ATOMIC_FUNCTIONS static inline void atomic_store_uint8_t(uint8_t *var, uint8_t val) { __atomic_store_n(var, val, __ATOMIC_SEQ_CST); } #elif defined(GCC_SYNC_FUNCTIONS) static inline void atomic_store_uint8_t(uint8_t *var, uint8_t val) { (void)__sync_lock_test_and_set(var, val); } #endif #endif /* !_ABSTRACT_ATOMIC_H */ nfs-ganesha-2.6.0/src/include/abstract_mem.h000066400000000000000000000274151324272410200207430ustar00rootroot00000000000000/* * * * Copyright © Linux box Corporation, 2012 * Author: Adam C. Emerson * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file abstract_mem.h * @author Adam C. Emerson * @brief Abstract memory shims to allow swapping out allocators * * This file's purpose is to allow us to easily replace the memory * allocator used by Ganesha. Further, it provides a pool abstraction * that may be implemented in terms of the normal allocator that may * be expanded at a later date. These are intended to be thin * wrappers, but conditionally compiled trace information could be * added. */ #ifndef ABSTRACT_MEM_H #define ABSTRACT_MEM_H #include #include #include #include "log.h" /** * @page GeneralAllocator General Allocator Shim * * These functions provide an interface akin to the standard libc * allocation functions. Currently they call the functions malloc, * free, and so forth, with changes in functionality being provided by * linking in alternate allocator libraries (tcmalloc and jemalloc, at * present.) So long as the interface remains the same, these * functions can be switched out using ifdef for versions that do more * memory tracking or that call allocators with other names. */ /** * @brief Allocate memory * * This function allocates a block of memory no less than the given * size. The block of memory allocated must be released with gsh_free. * * This function aborts if no memory is available. * * @param[in] n Number of bytes to allocate * @param[in] file Calling source file * @param[in] line Calling source line * @param[in] function Calling source function * * @return Pointer to a block of memory. */ static inline void * gsh_malloc__(size_t n, const char *file, int line, const char *function) { void *p = malloc(n); if (p == NULL) { LogMallocFailure(file, line, function, "gsh_malloc"); abort(); } return p; } #define gsh_malloc(n) gsh_malloc__(n, __FILE__, __LINE__, __func__) /** * @brief Allocate aligned memory * * This function allocates a block of memory to the given alignment. * Failure may indicate either insufficient memory or an invalid * alignment. * * @param[in] a Block alignment * @param[in] n Number of bytes to allocate * @param[in] file Calling source file * @param[in] line Calling source line * @param[in] function Calling source function * * @return Pointer to a block of memory or NULL. */ static inline void * gsh_malloc_aligned__(size_t a, size_t n, const char *file, int line, const char *function) { void *p; #ifdef __APPLE__ p = valloc(n); #else if (posix_memalign(&p, a, n) != 0) p = NULL; #endif if (p == NULL) { LogMallocFailure(file, line, function, "gsh_malloc_aligned"); abort(); } return p; } #define gsh_malloc_aligned(a, n) \ gsh_malloc_aligned__(a, n, __FILE__, __LINE__, __func__) /** * @brief Allocate zeroed memory * * This function allocates a block of memory that is guaranteed to be * zeroed. The block of memory allocated must be released with gsh_free. * * This function aborts if no memory is available. * * @param[in] n Number of objects in block * @param[in] s Size of object * * @return Pointer to a block of zeroed memory. */ static inline void * gsh_calloc__(size_t n, size_t s, const char *file, int line, const char *function) { void *p = calloc(n, s); if (p == NULL) { LogMallocFailure(file, line, function, "gsh_calloc"); abort(); } return p; } #define gsh_calloc(n, s) gsh_calloc__(n, s, __FILE__, __LINE__, __func__) /** * @brief Resize a block of memory * * This function resizes the buffer indicated by the supplied pointer * to the given size. The block may be moved in this process. On * failure, the original block is retained at its original address. * * This function aborts if no memory is available to resize. * * @param[in] p Block of memory to resize * @param[in] n New size * @param[in] file Calling source file * @param[in] line Calling source line * @param[in] function Calling source function * * @return Pointer to the address of the resized block. */ static inline void * gsh_realloc__(void *p, size_t n, const char *file, int line, const char *function) { void *p2 = realloc(p, n); if (n != 0 && p2 == NULL) { LogMallocFailure(file, line, function, "gsh_realloc"); abort(); } return p2; } #define gsh_realloc(p, n) gsh_realloc__(p, n, __FILE__, __LINE__, __func__) /** * @brief Duplicate a string to newly allocated memory * * This function allocates a new block of memory sufficient to contain * the supplied string, then copies the string into that buffer. * * This function aborts if no memory is available. * * @param[in] s String to duplicate * @param[in] file Calling source file * @param[in] line Calling source line * @param[in] function Calling source function * * @return Pointer to new copy of string. */ static inline char * gsh_strdup__(const char *s, const char *file, int line, const char *function) { char *p = strdup(s); if (p == NULL) { LogMallocFailure(file, line, function, "gsh_strdup"); abort(); } return p; } #define gsh_strdup(s) gsh_strdup__(s, __FILE__, __LINE__, __func__) /** * @brief Duplicate a string to newly allocated memory (bounded) * * This function allocates a new block of memory sufficient to contain * the supplied string, then copies the string into that buffer. * * This function aborts if no memory is available. * * The returned copied value includes the terminating NUL. * * @param[in] s String to duplicate * @param[in] length Size of the returned string shall be <= length+1 * @param[out] copied Number of bytes copied * @param[in] file Calling source file * @param[in] line Calling source line * @param[in] function Calling source function * * @return Pointer to new copy of string. */ static inline char * gsh_strldup__(const char *s, size_t length, size_t *copied, const char *file, int line, const char *function) { char *p = (char *) gsh_malloc__(length+1, file, line, function); if (p == NULL) { LogMallocFailure(file, line, function, "gsh_strldup"); abort(); } memcpy(p, s, length); p[length] = '\0'; *copied = length + 1; return p; } #define gsh_strldup(s, l, n) gsh_strldup__(s, l, n, __FILE__, __LINE__, \ __func__) /** * @brief Free a block of memory * * This function frees a block of memory allocated with gsh_malloc, * gsh_malloc_aligned, gsh_calloc, gsh_realloc, or gsh_strdup. * * @param[in] p Block of memory to free. */ static inline void gsh_free(void *p) { free(p); } /** * @brief Free a block of memory with size * * This function exists to be passed to TIRPC when setting * allocators. It should not be used by anyone else. New shim layers * should not redefine it. * * @param[in] p Block of memory to free. * @param[in] n Size of block (unused) */ static inline void gsh_free_size(void *p, size_t n __attribute__ ((unused))) { free(p); } /** * @brief Type representing a pool * * This type represents a memory pool. it should be treated, by all * callers, as a completely abstract type. The pointer should only be * stored or passed to pool functions. The pointer should never be * referenced. No assumptions about the size of the pointed-to type * should be made. * * This allows for flexible growth in the future. */ typedef struct pool { char *name; /*< The name of the pool */ size_t object_size; /*< The size of the objects created */ } pool_t; /** * @brief Create a basic object pool * * This function creates a new object pool, given a name, object size, * constructor and destructor. * * This particular implementation throws the name away, but other * implementations that do tracking or keep counts of allocated or * de-allocated objects will likely wish to use it in log messages. * * This initializer function is expected to abort if it fails. * * @param[in] name The name of this pool * @param[in] object_size The size of objects to allocate * @param[in] file Calling source file * @param[in] line Calling source line * @param[in] function Calling source function * * @return A pointer to the pool object. This pointer must not be * dereferenced. It may be stored or supplied as an argument * to the other pool functions. It must not be supplied as an * argument to gsh_free, rather it must be disposed of with * pool_destroy. */ static inline pool_t * pool_basic_init__(const char *name, size_t object_size, const char *file, int line, const char *function) { pool_t *pool = (pool_t *) gsh_malloc__(sizeof(pool_t), file, line, function); pool->object_size = object_size; if (name) pool->name = gsh_strdup__(name, file, line, function); else pool->name = NULL; return pool; } #define pool_basic_init(name, object_size) \ pool_basic_init__(name, object_size, __FILE__, __LINE__, __func__) /** * @brief Destroy a memory pool * * This function destroys a memory pool. All objects must be returned * to the pool before this function is called. * * @param[in] pool The pool to be destroyed. */ static inline void pool_destroy(pool_t *pool) { gsh_free(pool->name); gsh_free(pool); } /** * @brief Allocate an object from a pool * * This function allocates a single object from the pool and returns a * pointer to it. If a constructor was specified at pool creation, it * is called on that pointer. This function must be thread safe. If * the underlying pool abstraction requires a lock, this function must * take and release it. * * This function returns void pointers. Programmers who wish for more * type safety can easily create static inline wrappers (alloc_client * or similar) to return pointers of a specific type (and omitting the * pool parameter). * * This function aborts if no memory is available. * * @param[in] pool The pool from which to allocate * @param[in] file Calling source file * @param[in] line Calling source line * @param[in] function Calling source function * * @return A pointer to the allocated pool item. */ static inline void * pool_alloc__(pool_t *pool, const char *file, int line, const char *function) { return gsh_calloc__(1, pool->object_size, file, line, function); } #define pool_alloc(pool) \ pool_alloc__(pool, __FILE__, __LINE__, __func__) /** * @brief Return an entry to a pool * * This function returns a single object to the pool. If a destructor * was defined at pool creation time, it is called before the object * is freed. This function must be thread-safe. If the underlying * pool abstract requires a lock, this function must take and release * it. * * @param[in] pool Pool to which to return the object * @param[in] object Object to return. This is a void pointer. * Programmers wishing more type safety could create * a static inline wrapper taking an object of a * specific type (and omitting the pool parameter.) */ static inline void pool_free(pool_t *pool, void *object) { gsh_free(object); } #endif /* ABSTRACT_MEM_H */ nfs-ganesha-2.6.0/src/include/atomic_x86_64.h000066400000000000000000000360171324272410200205720ustar00rootroot00000000000000/* * Copyright © 2012 Paul Sheer * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ #define atomic_fetch_size_t(a) atomic_postadd_size_t(a,0) #define atomic_fetch_int64_t(a) atomic_postadd_int64_t(a,0) #define atomic_fetch_uint64_t(a) atomic_postadd_uint64_t(a,0) #define atomic_fetch_int32_t(a) atomic_postadd_int32_t(a,0) #define atomic_fetch_uint32_t(a) atomic_postadd_uint32_t(a,0) #define atomic_fetch_int16_t(a) atomic_postadd_int16_t(a,0) #define atomic_fetch_uint16_t(a) atomic_postadd_uint16_t(a,0) #define atomic_fetch_int8_t(a) atomic_postadd_int8_t(a,0) #define atomic_fetch_uint8_t(a) atomic_postadd_uint8_t(a,0) #if defined(__LP64__) || defined(__LP64) /* ADD U64 */ static inline size_t atomic_postadd_size_t(size_t * p, size_t v) { asm volatile ("lock\n\txaddq %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v; } static inline size_t atomic_add_size_t(size_t * p, size_t v) { size_t _v = v; asm volatile ("lock\n\txaddq %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v + _v; } /* SUB U64 */ static inline size_t atomic_postsub_size_t(size_t * p, size_t v) { v = -v; asm volatile ("lock\n\txaddq %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v; } static inline size_t atomic_sub_size_t(size_t * p, size_t v) { size_t _v; v = -v; _v = v; asm volatile ("lock\n\txaddq %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v + _v; } #else #error what is the sizeof your size_t? #endif /* STORE 64 */ static inline void atomic_store_int64_t(int64_t * p, int64_t v) { asm volatile ("lock\n\txchgq %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); } /* ADD 64 */ static inline int64_t atomic_postadd_int64_t(int64_t * p, uint64_t v) { asm volatile ("lock\n\txaddq %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v; } static inline int64_t atomic_add_int64_t(int64_t * p, uint64_t v) { uint64_t _v = v; asm volatile ("lock\n\txaddq %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v + _v; } /* SUB 64 */ static inline int64_t atomic_postsub_int64_t(int64_t * p, uint64_t v) { v = -v; asm volatile ("lock\n\txaddq %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v; } static inline int64_t atomic_sub_int64_t(int64_t * p, uint64_t v) { uint64_t _v; v = -v; _v = v; asm volatile ("lock\n\txaddq %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v + _v; } /* STORE U64 */ static inline void atomic_store_uint64_t(uint64_t * p, uint64_t v) { asm volatile ("lock\n\txchgq %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); } /* ADD U64 */ static inline uint64_t atomic_postadd_uint64_t(uint64_t * p, uint64_t v) { asm volatile ("lock\n\txaddq %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v; } static inline uint64_t atomic_add_uint64_t(uint64_t * p, uint64_t v) { uint64_t _v = v; asm volatile ("lock\n\txaddq %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v + _v; } /* SUB U64 */ static inline uint64_t atomic_postsub_uint64_t(uint64_t * p, uint64_t v) { v = -v; asm volatile ("lock\n\txaddq %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v; } static inline uint64_t atomic_sub_uint64_t(uint64_t * p, uint64_t v) { uint64_t _v; v = -v; _v = v; asm volatile ("lock\n\txaddq %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v + _v; } /* OR 64 */ static inline uint64_t atomic_postset_uint64_t_bits(uint64_t * p, uint64_t v) { uint64_t t; asm volatile ("\n\tmovq %0, %1\n\ 1:\n\ \tmovq %1, %%rcx\n\ \tmovq %1, %%rdx\n\ \torq %2, %%rdx\n\ \tlock cmpxchgq %%rdx, %0\n\ \tjne 1b\n\ \tmovq %%rcx, %1\n":"=m" (*p), "=a"(t), "=r"(v) :"2"(v) :"memory", "%rcx", "%rdx"); return t; } static inline uint64_t atomic_set_uint64_t_bits(uint64_t * p, uint64_t v) { uint64_t t; asm volatile ("\n\tmovq %0, %1\n\ 1:\n\ \tmovq %1, %%rdx\n\ \torq %2, %%rdx\n\ \tlock cmpxchgq %%rdx, %0\n\ \tjne 1b\n\ \tmovq %%rdx, %1\n":"=m" (*p), "=a"(t), "=r"(v) :"2"(v) :"memory", "%rdx"); return t; } /* AND 64 */ static inline uint64_t atomic_postclear_uint64_t_bits(uint64_t * p, uint64_t v) { uint64_t t; v = ~v; asm volatile ("\n\tmovq %0, %1\n\ 1:\n\ \tmovq %1, %%rcx\n\ \tmovq %1, %%rdx\n\ \tandq %2, %%rdx\n\ \tlock cmpxchgq %%rdx, %0\n\ \tjne 1b\n\ \tmovq %%rcx, %1\n":"=m" (*p), "=a"(t), "=r"(v) :"2"(v) :"memory", "%rcx", "%rdx"); return t; } static inline uint64_t atomic_clear_uint64_t_bits(uint64_t * p, uint64_t v) { uint64_t t; v = ~v; asm volatile ("\n\tmovq %0, %1\n\ 1:\n\ \tmovq %1, %%rdx\n\ \tandq %2, %%rdx\n\ \tlock cmpxchgq %%rdx, %0\n\ \tjne 1b\n\ \tmovq %%rdx, %1\n":"=m" (*p), "=a"(t), "=r"(v) :"2"(v) :"memory", "%rdx"); return t; } /* STORE 32 */ static inline void atomic_store_int32_t(int32_t * p, int32_t v) { *p = v; /* is atomic on Intel */ } /* ADD 32 */ static inline int32_t atomic_postadd_int32_t(int32_t * p, uint32_t v) { asm volatile ("lock\n\txaddl %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v; } static inline int32_t atomic_add_int32_t(int32_t * p, uint32_t v) { uint32_t _v = v; asm volatile ("lock\n\txaddl %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v + _v; } /* SUB 32 */ static inline int32_t atomic_postsub_int32_t(int32_t * p, uint32_t v) { v = -v; asm volatile ("lock\n\txaddl %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v; } static inline int32_t atomic_sub_int32_t(int32_t * p, uint32_t v) { uint32_t _v; v = -v; _v = v; asm volatile ("lock\n\txaddl %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v + _v; } /* STORE U32 */ static inline void atomic_store_uint32_t(uint32_t * p, uint32_t v) { *p = v; /* is atomic on Intel */ } /* ADD U32 */ static inline uint32_t atomic_postadd_uint32_t(uint32_t * p, uint32_t v) { asm volatile ("lock\n\txaddl %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v; } static inline uint32_t atomic_add_uint32_t(uint32_t * p, uint32_t v) { uint32_t _v = v; asm volatile ("lock\n\txaddl %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v + _v; } /* SUB U32 */ static inline uint32_t atomic_postsub_uint32_t(uint32_t * p, uint32_t v) { v = -v; asm volatile ("lock\n\txaddl %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v; } static inline uint32_t atomic_sub_uint32_t(uint32_t * p, uint32_t v) { uint32_t _v; v = -v; _v = v; asm volatile ("lock\n\txaddl %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v + _v; } /* OR 32 */ static inline uint32_t atomic_postset_uint32_t_bits(uint32_t * p, uint32_t v) { uint32_t t; asm volatile ("\n\tmovl %0, %1\n\ 1:\n\ \tmovl %1, %%ecx\n\ \tmovl %1, %%edx\n\ \torl %2, %%edx\n\ \tlock cmpxchgl %%edx, %0\n\ \tjne 1b\n\ \tmovl %%ecx, %1\n":"=m" (*p), "=a"(t), "=r"(v) :"2"(v) :"memory", "%ecx", "%edx"); return t; } static inline uint32_t atomic_set_uint32_t_bits(uint32_t * p, uint32_t v) { uint32_t t; asm volatile ("\n\tmovl %0, %1\n\ 1:\n\ \tmovl %1, %%edx\n\ \torl %2, %%edx\n\ \tlock cmpxchgl %%edx, %0\n\ \tjne 1b\n\ \tmovl %%edx, %1\n":"=m" (*p), "=a"(t), "=r"(v) :"2"(v) :"memory", "%edx"); return t; } /* AND 32 */ static inline uint32_t atomic_postclear_uint32_t_bits(uint32_t * p, uint32_t v) { uint32_t t; v = ~v; asm volatile ("\n\tmovl %0, %1\n\ 1:\n\ \tmovl %1, %%ecx\n\ \tmovl %1, %%edx\n\ \tandl %2, %%edx\n\ \tlock cmpxchgl %%edx, %0\n\ \tjne 1b\n\ \tmovl %%ecx, %1\n":"=m" (*p), "=a"(t), "=r"(v) :"2"(v) :"memory", "%ecx", "%edx"); return t; } static inline uint32_t atomic_clear_uint32_t_bits(uint32_t * p, uint32_t v) { uint32_t t; v = ~v; asm volatile ("\n\tmovl %0, %1\n\ 1:\n\ \tmovl %1, %%edx\n\ \tandl %2, %%edx\n\ \tlock cmpxchgl %%edx, %0\n\ \tjne 1b\n\ \tmovl %%edx, %1\n":"=m" (*p), "=a"(t), "=r"(v) :"2"(v) :"memory", "%edx"); return t; } /* STORE 16 */ static inline void atomic_store_int16_t(int16_t * p, int16_t v) { *p = v; /* is atomic on Intel */ } /* ADD 16 */ static inline int16_t atomic_postadd_int16_t(int16_t * p, uint16_t v) { asm volatile ("lock\n\txaddw %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v; } static inline int16_t atomic_add_int16_t(int16_t * p, uint16_t v) { uint16_t _v = v; asm volatile ("lock\n\txaddw %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v + _v; } /* SUB 16 */ static inline int16_t atomic_postsub_int16_t(int16_t * p, uint16_t v) { v = -v; asm volatile ("lock\n\txaddw %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v; } static inline int16_t atomic_sub_int16_t(int16_t * p, uint16_t v) { uint16_t _v; v = -v; _v = v; asm volatile ("lock\n\txaddw %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v + _v; } /* STORE U16 */ static inline void atomic_store_uint16_t(uint16_t * p, uint16_t v) { *p = v; /* is atomic on Intel */ } /* ADD U16 */ static inline uint16_t atomic_postadd_uint16_t(uint16_t * p, uint16_t v) { asm volatile ("lock\n\txaddw %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v; } static inline uint16_t atomic_add_uint16_t(uint16_t * p, uint16_t v) { uint16_t _v = v; asm volatile ("lock\n\txaddw %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v + _v; } /* SUB U16 */ static inline uint16_t atomic_postsub_uint16_t(uint16_t * p, uint16_t v) { v = -v; asm volatile ("lock\n\txaddw %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v; } static inline uint16_t atomic_sub_uint16_t(uint16_t * p, uint16_t v) { uint16_t _v; v = -v; _v = v; asm volatile ("lock\n\txaddw %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v + _v; } /* OR 16 */ static inline uint16_t atomic_postset_uint16_t_bits(uint16_t * p, uint16_t v_) { uint32_t t, v; v = v_; asm volatile ("\n\tmovzwl %0, %1\n\ 1:\n\ \tmovl %1, %%ecx\n\ \tmovl %1, %%edx\n\ \torl %2, %%edx\n\ \tlock cmpxchgw %%dx, %0\n\ \tjne 1b\n\ \tmovl %%ecx, %1\n":"=m" (*p), "=a"(t), "=r"(v) :"2"(v) :"memory", "%ecx", "%edx"); return t; } static inline uint16_t atomic_set_uint16_t_bits(uint16_t * p, uint16_t v_) { uint32_t t, v; v = v_; asm volatile ("\n\tmovzwl %0, %1\n\ 1:\n\ \tmovl %1, %%edx\n\ \torl %2, %%edx\n\ \tlock cmpxchgw %%dx, %0\n\ \tjne 1b\n\ \tmovl %%edx, %1\n":"=m" (*p), "=a"(t), "=r"(v) :"2"(v) :"memory", "%edx"); return t; } /* AND 16 */ static inline uint16_t atomic_postclear_uint16_t_bits(uint16_t * p, uint16_t v_) { uint32_t t, v; v = ~v_; asm volatile ("\n\tmovzwl %0, %1\n\ 1:\n\ \tmovl %1, %%ecx\n\ \tmovl %1, %%edx\n\ \tandl %2, %%edx\n\ \tlock cmpxchgw %%dx, %0\n\ \tjne 1b\n\ \tmovl %%ecx, %1\n":"=m" (*p), "=a"(t), "=r"(v) :"2"(v) :"memory", "%ecx", "%edx"); return t; } static inline uint16_t atomic_clear_uint16_t_bits(uint16_t * p, uint16_t v_) { uint32_t t, v; v = ~v_; asm volatile ("\n\tmovzwl %0, %1\n\ 1:\n\ \tmovl %1, %%edx\n\ \tandl %2, %%edx\n\ \tlock cmpxchgw %%dx, %0\n\ \tjne 1b\n\ \tmovl %%edx, %1\n":"=m" (*p), "=a"(t), "=r"(v) :"2"(v) :"memory", "%edx"); return t; } /* STORE 8 */ static inline void atomic_store_int8_t(int8_t * p, int8_t v) { *p = v; /* is atomic on Intel */ } /* ADD 8 */ static inline int8_t atomic_postadd_int8_t(int8_t * p, uint8_t v) { asm volatile ("lock\n\txaddb %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v; } static inline int8_t atomic_add_int8_t(int8_t * p, uint8_t v) { uint8_t _v = v; asm volatile ("lock\n\txaddb %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v + _v; } /* SUB 8 */ static inline int8_t atomic_postsub_int8_t(int8_t * p, uint8_t v) { v = -v; asm volatile ("lock\n\txaddb %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v; } static inline int8_t atomic_sub_int8_t(int8_t * p, uint8_t v) { uint8_t _v; v = -v; _v = v; asm volatile ("lock\n\txaddb %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v + _v; } /* STORE U8 */ static inline void atomic_store_uint8_t(uint16_t * p, uint8_t v) { *p = v; /* is atomic on Intel */ } /* ADD U8 */ static inline uint8_t atomic_postadd_uint8_t(uint8_t * p, uint8_t v) { asm volatile ("lock\n\txaddb %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v; } static inline uint8_t atomic_add_uint8_t(uint8_t * p, uint8_t v) { uint8_t _v = v; asm volatile ("lock\n\txaddb %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v + _v; } /* SUB U8 */ static inline uint8_t atomic_postsub_uint8_t(uint8_t * p, uint8_t v) { v = -v; asm volatile ("lock\n\txaddb %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v; } static inline uint8_t atomic_sub_uint8_t(uint8_t * p, uint8_t v) { uint8_t _v; v = -v; _v = v; asm volatile ("lock\n\txaddb %0,%1":"=r" (v), "=m"(*p) :"0"(v) :"memory"); return v + _v; } /* OR 8 */ static inline uint8_t atomic_postset_uint8_t_bits(uint8_t * p, uint8_t v_) { uint32_t t, v; v = v_; asm volatile ("\n\tmovzbl %0, %1\n\ 1:\n\ \tmovl %1, %%ecx\n\ \tmovl %1, %%edx\n\ \torl %2, %%edx\n\ \tlock cmpxchgb %%dl, %0\n\ \tjne 1b\n\ \tmovl %%ecx, %1\n":"=m" (*p), "=a"(t), "=r"(v) :"2"(v) :"memory", "%ecx", "%edx"); return t; } static inline uint8_t atomic_set_uint8_t_bits(uint8_t * p, uint8_t v_) { uint32_t t, v; v = v_; asm volatile ("\n\tmovzbl %0, %1\n\ 1:\n\ \tmovl %1, %%edx\n\ \torl %2, %%edx\n\ \tlock cmpxchgb %%dl, %0\n\ \tjne 1b\n\ \tmovl %%edx, %1\n":"=m" (*p), "=a"(t), "=r"(v) :"2"(v) :"memory", "%edx"); return t; } /* AND 8 */ static inline uint8_t atomic_postclear_uint8_t_bits(uint8_t * p, uint8_t v_) { uint32_t t, v; v = ~v_; asm volatile ("\n\tmovzbl %0, %1\n\ 1:\n\ \tmovl %1, %%ecx\n\ \tmovl %1, %%edx\n\ \tandl %2, %%edx\n\ \tlock cmpxchgb %%dl, %0\n\ \tjne 1b\n\ \tmovl %%ecx, %1\n":"=m" (*p), "=a"(t), "=r"(v) :"2"(v) :"memory", "%ecx", "%edx"); return t; } static inline uint8_t atomic_clear_uint8_t_bits(uint8_t * p, uint8_t v_) { uint32_t t, v; v = ~v_; asm volatile ("\n\tmovzbl %0, %1\n\ 1:\n\ \tmovl %1, %%edx\n\ \tandl %2, %%edx\n\ \tlock cmpxchgb %%dl, %0\n\ \tjne 1b\n\ \tmovl %%edx, %1\n":"=m" (*p), "=a"(t), "=r"(v) :"2"(v) :"memory", "%edx"); return t; } nfs-ganesha-2.6.0/src/include/avltree.h000066400000000000000000000231711324272410200177370ustar00rootroot00000000000000/* * libtree.h - this file is part of Libtree. * * Copyright (C) 2010-2014 Franck Bui-Huu * * This file is part of libtree which is free software; you can * redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software * Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the LICENSE file for license rights and limitations. */ #ifndef _LIBTREE_H #define _LIBTREE_H #include #include /* * The definition has been stolen from the Linux kernel. */ #ifdef __GNUC__ #define bstree_container_of(node, type, member) ({ \ const struct bstree_node *__mptr = (node); \ (type *)( (char *)__mptr - offsetof(type,member) );}) #define rbtree_container_of(node, type, member) ({ \ const struct rbtree_node *__mptr = (node); \ (type *)( (char *)__mptr - offsetof(type,member) );}) #define avltree_container_of(node, type, member) ({ \ const struct avltree_node *__mptr = (node); \ (type *)( (char *)__mptr - offsetof(type,member) );}) #define splaytree_container_of(node, type, member) ({ \ const struct splaytree_node *__mptr = (node); \ (type *)( (char *)__mptr - offsetof(type,member) );}) #else #define bstree_container_of(node, type, member) \ ((type *)((char *)(node) - offsetof(type, member))) #define rbtree_container_of(node, type, member) \ ((type *)((char *)(node) - offsetof(type, member))) #define avltree_container_of(node, type, member) \ ((type *)((char *)(node) - offsetof(type, member))) #define splaytree_container_of(node, type, member) \ ((type *)((char *)(node) - offsetof(type, member))) #endif /* __GNUC__ */ /* * Threaded binary search tree */ #ifdef UINTPTR_MAX struct bstree_node { uintptr_t left, right; } __attribute__ ((aligned(2))); #else struct bstree_node { struct bstree_node *left, *right; unsigned left_is_thread:1; unsigned right_is_thread:1; }; #endif /* UINTPTR_MAX */ typedef int (*bstree_cmp_fn_t) (const struct bstree_node *, const struct bstree_node *); struct bstree { struct bstree_node *root; bstree_cmp_fn_t cmp_fn; struct bstree_node *first, *last; uint64_t reserved[4]; }; struct bstree_node *bstree_first(const struct bstree *tree); struct bstree_node *bstree_last(const struct bstree *tree); struct bstree_node *bstree_next(const struct bstree_node *node); struct bstree_node *bstree_prev(const struct bstree_node *node); struct bstree_node *bstree_lookup(const struct bstree_node *key, const struct bstree *tree); struct bstree_node *bstree_insert(struct bstree_node *node, struct bstree *tree); void bstree_remove(struct bstree_node *node, struct bstree *tree); void bstree_replace(struct bstree_node *old, struct bstree_node *newe, struct bstree *tree); int bstree_init(struct bstree *tree, bstree_cmp_fn_t cmp, unsigned long flags); /* * Red-black tree */ enum rb_color { RB_BLACK, RB_RED, }; #ifdef UINTPTR_MAX struct rbtree_node { struct rbtree_node *left, *right; uintptr_t parent; } __attribute__ ((aligned(2))); #else struct rbtree_node { struct rbtree_node *left, *right; struct rbtree_node *parent; enum rb_color color; }; #endif /* UINTPTR_MAX */ typedef int (*rbtree_cmp_fn_t) (const struct rbtree_node *, const struct rbtree_node *); struct rbtree { struct rbtree_node *root; rbtree_cmp_fn_t cmp_fn; struct rbtree_node *first, *last; uint64_t reserved[4]; }; struct rbtree_node *rbtree_first(const struct rbtree *tree); struct rbtree_node *rbtree_last(const struct rbtree *tree); struct rbtree_node *rbtree_next(const struct rbtree_node *node); struct rbtree_node *rbtree_prev(const struct rbtree_node *node); struct rbtree_node *rbtree_lookup(const struct rbtree_node *key, const struct rbtree *tree); struct rbtree_node *rbtree_insert(struct rbtree_node *node, struct rbtree *tree); void rbtree_remove(struct rbtree_node *node, struct rbtree *tree); void rbtree_replace(struct rbtree_node *old, struct rbtree_node *newe, struct rbtree *tree); int rbtree_init(struct rbtree *tree, rbtree_cmp_fn_t cmp, unsigned long flags); /* * AVL tree */ #if defined UINTPTR_MAX && UINTPTR_MAX == UINT64_MAX struct avltree_node { struct avltree_node *left, *right; uintptr_t parent; /* balance factor [0:4] */ } __attribute__ ((aligned(8))); static inline signed int get_balance(struct avltree_node *node) { return (int)(node->parent & 7) - 2; } #else struct avltree_node { struct avltree_node *left, *right; struct avltree_node *parent; signed balance:3; /* balance factor [-2:+2] */ }; static inline signed int get_balance(struct avltree_node *node) { return node->balance; } #endif typedef int (*avltree_cmp_fn_t) (const struct avltree_node *, const struct avltree_node *); struct avltree { struct avltree_node *root; avltree_cmp_fn_t cmp_fn; int height; struct avltree_node *first, *last; uint64_t size; #if 0 uint64_t reserved[4]; #endif }; /** * @brief Perform a lookup in an AVL tree, returning useful bits for * subsequent inser. * * 'pparent', 'unbalanced' and 'is_left' are only used for * insertions. Normally GCC will notice this and get rid of them for * lookups. * * @param[in] key Key to look for * @param[in] tree AVL tree to look in * @param[in,out] pparent Parent of key * @param[in,out] unbalanced Unbalanced parent * @param[in,out] is_left True if key would be to left of parent * @param[in] cmp_fn Comparison function to use * * @returns The node found if any */ static inline struct avltree_node *avltree_do_lookup(const struct avltree_node *key, const struct avltree *tree, struct avltree_node **pparent, struct avltree_node **unbalanced, int *is_left, avltree_cmp_fn_t cmp_fn) { struct avltree_node *node = tree->root; int res = 0; *pparent = NULL; *unbalanced = node; *is_left = 0; while (node) { if (get_balance(node) != 0) *unbalanced = node; res = cmp_fn(node, key); if (res == 0) return node; *pparent = node; *is_left = res > 0; if (*is_left) node = node->left; else node = node->right; } return NULL; } static inline struct avltree_node *avltree_inline_lookup(const struct avltree_node *key, const struct avltree *tree, avltree_cmp_fn_t cmp_fn) { struct avltree_node *parent, *unbalanced; int is_left; return avltree_do_lookup(key, tree, &parent, &unbalanced, &is_left, cmp_fn); } static inline struct avltree_node *avltree_lookup(const struct avltree_node *key, const struct avltree *tree) { return avltree_inline_lookup(key, tree, tree->cmp_fn); } void avltree_do_insert(struct avltree_node *node, struct avltree *tree, struct avltree_node *parent, struct avltree_node *unbalanced, int is_left); static inline struct avltree_node *avltree_inline_insert(struct avltree_node *node, struct avltree *tree, avltree_cmp_fn_t cmp_fn) { struct avltree_node *found, *parent, *unbalanced; int is_left; found = avltree_do_lookup(node, tree, &parent, &unbalanced, &is_left, cmp_fn); if (found) return found; avltree_do_insert(node, tree, parent, unbalanced, is_left); return NULL; } static inline struct avltree_node *avltree_insert(struct avltree_node *node, struct avltree *tree) { return avltree_inline_insert(node, tree, tree->cmp_fn); } static inline struct avltree_node *avltree_first(const struct avltree *tree) { return tree->first; } static inline struct avltree_node *avltree_last(const struct avltree *tree) { return tree->last; } struct avltree_node *avltree_next(const struct avltree_node *node); struct avltree_node *avltree_prev(const struct avltree_node *node); uint64_t avltree_size(const struct avltree *tree); struct avltree_node *avltree_inf(const struct avltree_node *key, const struct avltree *tree); struct avltree_node *avltree_sup(const struct avltree_node *key, const struct avltree *tree); void avltree_remove(struct avltree_node *node, struct avltree *tree); void avltree_replace(struct avltree_node *old, struct avltree_node *newe, struct avltree *tree); int avltree_init(struct avltree *tree, avltree_cmp_fn_t cmp, unsigned long flags); /* * Splay tree */ #ifdef UINTPTR_MAX struct splaytree_node { uintptr_t left, right; } __attribute__ ((aligned(2))); #else struct splaytree_node { struct splaytree_node *left, *right; unsigned left_is_thread:1; unsigned right_is_thread:1; }; #endif typedef int (*splaytree_cmp_fn_t) (const struct splaytree_node *, const struct splaytree_node *); struct splaytree { struct splaytree_node *root; struct splaytree_node *first, *last; splaytree_cmp_fn_t cmp_fn; uint64_t reserved[4]; }; struct splaytree_node *splaytree_first(const struct splaytree *tree); struct splaytree_node *splaytree_last(const struct splaytree *tree); struct splaytree_node *splaytree_next(const struct splaytree_node *node); struct splaytree_node *splaytree_prev(const struct splaytree_node *node); struct splaytree_node *splaytree_lookup(const struct splaytree_node *key, struct splaytree *tree); struct splaytree_node *splaytree_insert(struct splaytree_node *node, struct splaytree *tree); void splaytree_remove(struct splaytree_node *node, struct splaytree *tree); void splaytree_replace(struct splaytree_node *old, struct splaytree_node *newe, struct splaytree *tree); int splaytree_init(struct splaytree *tree, splaytree_cmp_fn_t cmp, unsigned long flags); #endif /* _LIBTREE_H */ nfs-ganesha-2.6.0/src/include/bsd-base64.h000066400000000000000000000051121324272410200201220ustar00rootroot00000000000000/* * Copyright (c) 1996 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for * any purpose with or without fee is hereby granted, provided that * the above copyright notice and this permission notice appear in all * copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT * SHALL INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1995 by International Business Machines, Inc. * * International Business Machines, Inc. (hereinafter called IBM) * grants permission under its copyrights to use, copy, modify, and * distribute this Software with or without fee, provided that the * above copyright notice and all paragraphs of this notice appear in * all copies, and that the name of IBM not be used in connection with * the marketing of any product incorporating the Software or * modifications thereof, without specific, written prior permission. * * To the extent it has a right to do so, IBM grants an immunity from * suit under its patents, if any, for the use, sale or manufacture of * products to the extent that such products are used for performing * Domain Name System dynamic updates in TCP/IP networks by means of * the Software. No immunity is granted for any product per se or for * any other function of any product. * * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE, EVEN IF IBM IS APPRISED OF THE * POSSIBILITY OF SUCH DAMAGES. */ #ifndef _BSD_BASE64_H #define _BSD_BASE64_H #include int b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize); int b64_pton(char const *src, u_char *target, size_t targsize); int base64url_encode(u_char const *src, size_t srclength, char *target, size_t targsize); #define __b64_ntop b64_ntop #define __b64_pton b64_pton #endif /* _BSD_BINRESVPORT_H */ nfs-ganesha-2.6.0/src/include/cidr.h000066400000000000000000000062251324272410200172170ustar00rootroot00000000000000/* * libcidr.h - Header file for libCIDR */ #ifndef __LIBCIDR_H #define __LIBCIDR_H /* We need the fixed-size int types. See discussion below. */ #include /* We need the struct in[6]_addr defs */ #include /* CONSTANTS */ /* String forms (cidr_to_str()) */ #define CIDR_NOFLAGS (0) #define CIDR_NOCOMPACT (1) /* Don't do :: compaction */ #define CIDR_VERBOSE (1<<1) /* Don't minimize leading zeros */ #define CIDR_USEV6 (1<<2) /* Use v6 form for v4 addresses */ #define CIDR_USEV4COMPAT (1<<3) /* Use v4-compat rather than v4-mapped */ #define CIDR_NETMASK (1<<4) /* Show netmask instead of pflen */ #define CIDR_ONLYADDR (1<<5) /* Only show the address */ #define CIDR_ONLYPFLEN (1<<6) /* Only show the pf/mask */ #define CIDR_WILDCARD (1<<7) /* Show wildcard-mask instead of netmask */ #define CIDR_FORCEV6 (1<<8) /* Force treating as v6 address */ #define CIDR_FORCEV4 (1<<9) /* Force treating as v4 address */ #define CIDR_REVERSE (1<<10) /* Return a DNS PTR name */ /* Protocols */ #define CIDR_NOPROTO 0 #define CIDR_IPV4 1 #define CIDR_IPV6 2 /* Versioning info */ #define CIDR_VERSION "1.1" #define CIDR_RELEASE "release" #define CIDR_REVISION " (fullermd@over-yonder.net-20061125141312-f6mjjptgl4zqh6wt)" #define CIDR_VERSION_STR (CIDR_VERSION "-" CIDR_RELEASE CIDR_REVISION) /* DATA STRUCTURES */ /* * Discussion: * uint*_t are defined by POSIX and C99. We only probably NEED stdint.h * defines, since we don't need the various output stuff. However, for * now, we'll get all of inttypes.h because some older platforms only * have it, and define the uint*_t's in there (FreeBSD 4.x being the most * obvious one I care about). Revisit this down the line if necessary. * * Note that you should almost certainly not be messing with this * structure directly from external programs. Use the cidr_get_*() * functions to get a copy to work with. */ struct cidr_addr { int version; uint8_t addr[16]; uint8_t mask[16]; int proto; }; typedef struct cidr_addr CIDR; /* PROTOTYPES */ CIDR *cidr_addr_broadcast(const CIDR *); CIDR *cidr_addr_hostmax(const CIDR *); CIDR *cidr_addr_hostmin(const CIDR *); CIDR *cidr_addr_network(const CIDR *); CIDR *cidr_alloc(void); int cidr_contains(const CIDR *, const CIDR *); CIDR *cidr_dup(const CIDR *); int cidr_equals(const CIDR *, const CIDR *); void cidr_free(CIDR *); CIDR *cidr_from_inaddr(const struct in_addr *); CIDR *cidr_from_in6addr(const struct in6_addr *); CIDR *cidr_from_str(const char *); uint8_t *cidr_get_addr(const CIDR *); uint8_t *cidr_get_mask(const CIDR *); int cidr_get_pflen(const CIDR *); int cidr_get_proto(const CIDR *); int cidr_is_v4mapped(const CIDR *); CIDR **cidr_net_subnets(const CIDR *); CIDR *cidr_net_supernet(const CIDR *); const char *cidr_numaddr(const CIDR *); const char *cidr_numaddr_pflen(int); const char *cidr_numhost(const CIDR *); const char *cidr_numhost_pflen(int); struct in_addr *cidr_to_inaddr(const CIDR *, struct in_addr *); struct in6_addr *cidr_to_in6addr(const CIDR *, struct in6_addr *); char *cidr_to_str(const CIDR *, int); const char *cidr_version(void); #endif /* __LIBCIDR_H */ nfs-ganesha-2.6.0/src/include/city.h000066400000000000000000000063701324272410200172470ustar00rootroot00000000000000// city.h - cityhash-c // CityHash on C // Copyright (c) 2011-2012, Alexander Nusov // // - original copyright notice - // Copyright (c) 2011 Google, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // // CityHash, by Geoff Pike and Jyrki Alakuijala // // This file provides a few functions for hashing strings. On x86-64 // hardware in 2011, CityHash64() is faster than other high-quality // hash functions, such as Murmur. This is largely due to higher // instruction-level parallelism. CityHash64() and CityHash128() also perform // well on hash-quality tests. // // CityHash128() is optimized for relatively long strings and returns // a 128-bit hash. For strings more than about 2000 bytes it can be // faster than CityHash64(). // // Functions in the CityHash family are not suitable for cryptography. // // WARNING: This code has not been tested on big-endian platforms! // It is known to work well on little-endian platforms that have a small penalty // for unaligned reads, such as current Intel and AMD moderate-to-high-end CPUs. // // By the way, for some hash functions, given strings a and b, the hash // of a+b is easily derived from the hashes of a and b. This property // doesn't hold for any hash functions in this file. #ifndef CITY_HASH_H_ #define CITY_HASH_H_ #include #include typedef uint8_t uint8; typedef uint32_t uint32; typedef uint64_t uint64; typedef struct _uint128 uint128; struct _uint128 { uint64 first; uint64 second; }; #define Uint128Low64(x) (x).first #define Uint128High64(x) (x).second // Hash function for a byte array. uint64 CityHash64(const char *buf, size_t len); // Hash function for a byte array. For convenience, a 64-bit seed is also // hashed into the result. uint64 CityHash64WithSeed(const char *buf, size_t len, uint64 seed); // Hash function for a byte array. For convenience, two seeds are also // hashed into the result. uint64 CityHash64WithSeeds(const char *buf, size_t len, uint64 seed0, uint64 seed1); // Hash function for a byte array. uint128 CityHash128(const char *s, size_t len); // Hash function for a byte array. For convenience, a 128-bit seed is also // hashed into the result. uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed); #endif // CITY_HASH_H_ nfs-ganesha-2.6.0/src/include/citycrc.h000066400000000000000000000037041324272410200177350ustar00rootroot00000000000000// citycrc.h - cityhash-c // CityHash on C // Copyright (c) 2011-2012, Alexander Nusov // // - original copyright notice - // Copyright (c) 2011 Google, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // // CityHash, by Geoff Pike and Jyrki Alakuijala // // This file declares the subset of the CityHash functions that require // _mm_crc32_u64(). See the CityHash README for details. // // Functions in the CityHash family are not suitable for cryptography. #ifndef CITY_HASH_CRC_H_ #define CITY_HASH_CRC_H_ #include "city.h" // Hash function for a byte array. uint128 CityHashCrc128(const char *s, size_t len); // Hash function for a byte array. For convenience, a 128-bit seed is also // hashed into the result. uint128 CityHashCrc128WithSeed(const char *s, size_t len, uint128 seed); // Hash function for a byte array. Sets result[0] ... result[3]. void CityHashCrc256(const char *s, size_t len, uint64 * result); #endif // CITY_HASH_CRC_H_ nfs-ganesha-2.6.0/src/include/client_mgr.h000066400000000000000000000036451324272410200204240ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2013 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ /** * @defgroup Client host management * @{ */ /** * @file client_mgr.h * @author Jim Lieb * @brief Client manager */ #ifndef CLIENT_MGR_H #define CLIENT_MGR_H #include #include #include "avltree.h" #include "gsh_types.h" struct gsh_client { struct avltree_node node_k; pthread_rwlock_t lock; struct gsh_buffdesc addr; int64_t refcnt; nsecs_elapsed_t last_update; char *hostaddr_str; unsigned char addrbuf[]; }; static inline int64_t inc_gsh_client_refcount(struct gsh_client *client) { return atomic_inc_int64_t(&client->refcnt); } void client_pkginit(void); #ifdef USE_DBUS void dbus_client_init(void); #endif struct gsh_client *get_gsh_client(sockaddr_t *client_ipaddr, bool lookup_only); void put_gsh_client(struct gsh_client *client); int foreach_gsh_client(bool(*cb) (struct gsh_client *cl, void *state), void *state); #endif /* !CLIENT_MGR_H */ /** @} */ nfs-ganesha-2.6.0/src/include/common_utils.h000066400000000000000000000330251324272410200210040ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: */ /** * @file common_utils.h * @brief Common tools for printing, parsing, .... */ #ifndef COMMON_UTILS_H #define COMMON_UTILS_H #include #include #include #include #include #include #include "gsh_types.h" #include "log.h" /** * BUILD_BUG_ON - break compile if a condition is true. * @condition: the condition which the compiler should know is false. * * If you have some code which relies on certain constants being equal, or * other compile-time-evaluated condition, you should use BUILD_BUG_ON to * detect if someone changes it. * * The implementation uses gcc's reluctance to create a negative array, but * gcc (as of 4.4) only emits that error for obvious cases (eg. not arguments * to inline functions). So as a fallback we use the optimizer; if it can't * prove the condition is false, it will cause a link error on the undefined * "__build_bug_on_failed". This error message can be harder to track down * though, hence the two different methods. * * Blatantly stolen from kernel source, include/linux/kernel.h:651 */ #ifndef __OPTIMIZE__ #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) #else extern int __build_bug_on_failed; #define BUILD_BUG_ON(condition) \ do { \ ((void)sizeof(char[1 - 2*!!(condition)])); \ if (condition) \ __build_bug_on_failed = 1; \ } while (0) #endif /* Most machines scandir callback requires a const. But not all */ #define SCANDIR_CONST const /* Most machines have mntent.h. */ #define HAVE_MNTENT_H 1 /* String parsing functions */ #ifndef HAVE_STRLCPY extern size_t strlcpy(char *dst, const char *src, size_t siz); #endif #ifndef HAVE_STRNLEN #define strnlen(a, b) gsh_strnlen(a, b) /* prefix with gsh_ to prevent library conflict -- will fix properly with new build system */ extern size_t gsh_strnlen(const char *s, size_t max); #endif #if defined(__APPLE__) #define clock_gettime(a, ts) portable_clock_gettime(ts) extern int portable_clock_gettime(struct timespec *ts); #define pthread_yield() pthread_yield_np() #undef SCANDIR_CONST #define SCANDIR_CONST #undef HAVE_MNTENT_H #endif #if defined(__FreeBSD__) #undef SCANDIR_CONST #define SCANDIR_CONST #endif /** * @brief Logging rwlock initialization * * @param[in,out] _lock The rwlock to initialize * @param[in,out] _attr The attributes used while initializing the lock */ #define PTHREAD_RWLOCK_init(_lock, _attr) \ do { \ int rc; \ \ rc = pthread_rwlock_init(_lock, _attr); \ if (rc == 0) { \ LogFullDebug(COMPONENT_RW_LOCK, \ "Init rwlock %p (%s) at %s:%d", \ _lock, #_lock, \ __FILE__, __LINE__); \ } else { \ LogCrit(COMPONENT_RW_LOCK, \ "Error %d, Init rwlock %p (%s) " \ "at %s:%d", rc, _lock, #_lock, \ __FILE__, __LINE__); \ abort(); \ } \ } while (0) /** * @brief Logging rwlock destroy * * @param[in,out] _lock The rwlock to destroy */ #define PTHREAD_RWLOCK_destroy(_lock) \ do { \ int rc; \ \ rc = pthread_rwlock_destroy(_lock); \ if (rc == 0) { \ LogFullDebug(COMPONENT_RW_LOCK, \ "Destroy mutex %p (%s) at %s:%d", \ _lock, #_lock, \ __FILE__, __LINE__); \ } else { \ LogCrit(COMPONENT_RW_LOCK, \ "Error %d, Destroy mutex %p (%s) " \ "at %s:%d", rc, _lock, #_lock, \ __FILE__, __LINE__); \ abort(); \ } \ } while (0) /** * @brief Logging write-lock * * @param[in,out] _lock Read-write lock */ #define PTHREAD_RWLOCK_wrlock(_lock) \ do { \ int rc; \ \ rc = pthread_rwlock_wrlock(_lock); \ if (rc == 0) { \ LogFullDebug(COMPONENT_RW_LOCK, \ "Got write lock on %p (%s) " \ "at %s:%d", _lock, #_lock, \ __FILE__, __LINE__); \ } else { \ LogCrit(COMPONENT_RW_LOCK, \ "Error %d, write locking %p (%s) " \ "at %s:%d", rc, _lock, #_lock, \ __FILE__, __LINE__); \ abort(); \ } \ } while (0) \ /** * @brief Logging read-lock * * @param[in,out] _lock Read-write lock */ #define PTHREAD_RWLOCK_rdlock(_lock) \ do { \ int rc; \ \ rc = pthread_rwlock_rdlock(_lock); \ if (rc == 0) { \ LogFullDebug(COMPONENT_RW_LOCK, \ "Got read lock on %p (%s) " \ "at %s:%d", _lock, #_lock, \ __FILE__, __LINE__); \ } else { \ LogCrit(COMPONENT_RW_LOCK, \ "Error %d, read locking %p (%s) " \ "at %s:%d", rc, _lock, #_lock, \ __FILE__, __LINE__); \ abort(); \ } \ } while (0) \ /** * @brief Logging read-write lock unlock * * @param[in,out] _lock Read-write lock */ #define PTHREAD_RWLOCK_unlock(_lock) \ do { \ int rc; \ \ rc = pthread_rwlock_unlock(_lock); \ if (rc == 0) { \ LogFullDebug(COMPONENT_RW_LOCK, \ "Unlocked %p (%s) at %s:%d", \ _lock, #_lock, \ __FILE__, __LINE__); \ } else { \ LogCrit(COMPONENT_RW_LOCK, \ "Error %d, unlocking %p (%s) at %s:%d", \ rc, _lock, #_lock, \ __FILE__, __LINE__); \ abort(); \ } \ } while (0) \ /** * @brief Logging mutex lock * * @param[in,out] _mtx The mutex to acquire */ #define PTHREAD_MUTEX_lock(_mtx) \ do { \ int rc; \ \ rc = pthread_mutex_lock(_mtx); \ if (rc == 0) { \ LogFullDebug(COMPONENT_RW_LOCK, \ "Acquired mutex %p (%s) at %s:%d", \ _mtx, #_mtx, \ __FILE__, __LINE__); \ } else{ \ LogCrit(COMPONENT_RW_LOCK, \ "Error %d, acquiring mutex %p (%s) " \ "at %s:%d", rc, _mtx, #_mtx, \ __FILE__, __LINE__); \ abort(); \ } \ } while (0) /** * @brief Logging mutex unlock * * @param[in,out] _mtx The mutex to relinquish */ #define PTHREAD_MUTEX_unlock(_mtx) \ do { \ int rc; \ \ rc = pthread_mutex_unlock(_mtx); \ if (rc == 0) { \ LogFullDebug(COMPONENT_RW_LOCK, \ "Released mutex %p (%s) at %s:%d", \ _mtx, #_mtx, \ __FILE__, __LINE__); \ } else{ \ LogCrit(COMPONENT_RW_LOCK, \ "Error %d, releasing mutex %p (%s) " \ "at %s:%d", rc, _mtx, #_mtx, \ __FILE__, __LINE__); \ abort(); \ } \ } while (0) /** * @brief Logging mutex initialization * * @param[in,out] _mtx The mutex to initialize * @param[in,out] _attr The attributes used while initializing the mutex */ #define PTHREAD_MUTEX_init(_mtx, _attr) \ do { \ int rc; \ \ rc = pthread_mutex_init(_mtx, _attr); \ if (rc == 0) { \ LogFullDebug(COMPONENT_RW_LOCK, \ "Init mutex %p (%s) at %s:%d", \ _mtx, #_mtx, \ __FILE__, __LINE__); \ } else { \ LogCrit(COMPONENT_RW_LOCK, \ "Error %d, Init mutex %p (%s) " \ "at %s:%d", rc, _mtx, #_mtx, \ __FILE__, __LINE__); \ abort(); \ } \ } while (0) /** * @brief Logging mutex destroy * * @param[in,out] _mtx The mutex to destroy */ #define PTHREAD_MUTEX_destroy(_mtx) \ do { \ int rc; \ \ rc = pthread_mutex_destroy(_mtx); \ if (rc == 0) { \ LogFullDebug(COMPONENT_RW_LOCK, \ "Destroy mutex %p (%s) at %s:%d", \ _mtx, #_mtx, \ __FILE__, __LINE__); \ } else { \ LogCrit(COMPONENT_RW_LOCK, \ "Error %d, Destroy mutex %p (%s) " \ "at %s:%d", rc, _mtx, #_mtx, \ __FILE__, __LINE__); \ abort(); \ } \ } while (0) /** * @brief Logging condition variable initialization * * @param[in,out] _cond The condition variable to initialize * @param[in,out] _attr The attributes used while initializing the * condition variable */ #define PTHREAD_COND_init(_cond, _attr) \ do { \ int rc; \ \ rc = pthread_cond_init(_cond, _attr); \ if (rc == 0) { \ LogFullDebug(COMPONENT_RW_LOCK, \ "Init cond %p (%s) at %s:%d", \ _cond, #_cond, \ __FILE__, __LINE__); \ } else { \ LogCrit(COMPONENT_RW_LOCK, \ "Error %d, Init cond %p (%s) " \ "at %s:%d", rc, _cond, #_cond, \ __FILE__, __LINE__); \ abort(); \ } \ } while (0) /** * @brief Logging condtion variable destroy * * @param[in,out] _cond The condition variable to destroy */ #define PTHREAD_COND_destroy(_cond) \ do { \ int rc; \ \ rc = pthread_cond_destroy(_cond); \ if (rc == 0) { \ LogFullDebug(COMPONENT_RW_LOCK, \ "Destroy cond %p (%s) at %s:%d", \ _cond, #_cond, \ __FILE__, __LINE__); \ } else { \ LogCrit(COMPONENT_RW_LOCK, \ "Error %d, Destroy cond %p (%s) " \ "at %s:%d", rc, _cond, #_cond, \ __FILE__, __LINE__); \ abort(); \ } \ } while (0) /** * @brief Inline functions for timespec math * * This is for timespec math. If you want to do * do the same kinds of math on timeval values, * See timeradd(3) in GLIBC. * * The primary purpose of nsecs_elapsed_t is for a compact * and quick way to handle time issues relative to server * start and server EPOCH (which is not quite the same thing * but too complicated to explain here). */ /** * @brief Get the abs difference between two timespecs in nsecs * * useful for cheap time calculation. Works with Dr. Who... * * @param[in] start timespec of before end * @param[in] end timespec after start time * * @return Elapsed time in nsecs */ static inline nsecs_elapsed_t timespec_diff(const struct timespec *start, const struct timespec *end) { if ((end->tv_sec > start->tv_sec) || (end->tv_sec == start->tv_sec && end->tv_nsec >= start->tv_nsec)) { return (end->tv_sec - start->tv_sec) * NS_PER_SEC + (end->tv_nsec - start->tv_nsec); } else { return (start->tv_sec - end->tv_sec) * NS_PER_SEC + (start->tv_nsec - end->tv_nsec); } } /** * @brief Convert a timespec to an elapsed time interval * * This will work for wallclock time until 2554. */ static inline nsecs_elapsed_t timespec_to_nsecs(struct timespec *timespec) { return timespec->tv_sec * NS_PER_SEC + timespec->tv_nsec; } /** * @brief Convert an elapsed time interval to a timespec */ static inline void nsecs_to_timespec(nsecs_elapsed_t interval, struct timespec *timespec) { timespec->tv_sec = interval / NS_PER_SEC; timespec->tv_nsec = interval % NS_PER_SEC; } /** * @brief Add an interval to a timespec * * @param[in] interval Nanoseconds to add * @param[in,out] timespec Time */ static inline void timespec_add_nsecs(nsecs_elapsed_t interval, struct timespec *timespec) { timespec->tv_sec += (interval / NS_PER_SEC); timespec->tv_nsec += (interval % NS_PER_SEC); if ((nsecs_elapsed_t)timespec->tv_nsec > NS_PER_SEC) { timespec->tv_sec += (timespec->tv_nsec / NS_PER_SEC); timespec->tv_nsec = timespec->tv_nsec % NS_PER_SEC; } } /** * @brief Subtract an interval from a timespec * * @param[in] interval Nanoseconds to subtract * @param[in,out] timespec Time */ static inline void timespec_sub_nsecs(nsecs_elapsed_t interval, struct timespec *t) { struct timespec ts; nsecs_to_timespec(interval, &ts); if (ts.tv_nsec > t->tv_nsec) { t->tv_sec -= (ts.tv_sec + 1); t->tv_nsec = ts.tv_nsec - t->tv_nsec; } else { t->tv_sec -= ts.tv_sec; t->tv_nsec -= ts.tv_nsec; } } /** * @brief Compare two times * * Determine if @c t1 is less-than, equal-to, or greater-than @c t2. * * @param[in] t1 First time * @param[in] t2 Second time * * @retval -1 @c t1 is less-than @c t2 * @retval 0 @c t1 is equal-to @c t2 * @retval 1 @c t1 is greater-than @c t2 */ static inline int gsh_time_cmp(const struct timespec *t1, const struct timespec *t2) { if (t1->tv_sec < t2->tv_sec) { return -1; } else if (t1->tv_sec > t2->tv_sec) { return 1; } else { if (t1->tv_nsec < t2->tv_nsec) return -1; else if (t1->tv_nsec > t2->tv_nsec) return 1; } return 0; } /** * @brief Get the time right now as a timespec * * @param[out] ts Timespec struct */ static inline void now(struct timespec *ts) { int rc; rc = clock_gettime(CLOCK_REALTIME, ts); if (rc != 0) { LogCrit(COMPONENT_MAIN, "Failed to get timestamp"); assert(0); /* if this is broken, we are toast so die */ } } /** * @brief Copy a string into a buffer safely * * This function doesn't overflow and and and makes sure the buffer is * null terminated. * * @param[out] dest Destination buffer * @param[in] src Source string * @param[in] dest_size Total size of dest * * @retval 0 on success. * @retval -1 if the buffer would overflow (the buffer is not modified) */ static inline int strmaxcpy(char *dest, const char *src, size_t dest_size) { size_t len = strlen(src); if (len >= dest_size) return -1; memcpy(dest, src, len + 1); return 0; } /** * @brief Append a string to buffer safely * * This function doesn't overflow the buffer, and makes sure the * buffer is null terminated. * * @param[in,out] dest Destination buffer * @param[in] src Source string * @param[in] dest_size Total size of dest * * @retval 0 on success. * @retval -1 if the buffer would overflow (the buffer is not modified). */ static inline int strmaxcat(char *dest, const char *src, size_t dest_size) { int destlen = strlen(dest); int remain = dest_size - destlen; int srclen = strlen(src); if (remain <= srclen) return -1; memcpy(dest + destlen, src, srclen + 1); return 0; } #endif /* !COMMON_UTILS_H */ nfs-ganesha-2.6.0/src/include/conf_url.h000066400000000000000000000027531324272410200201070ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- * Copyright (C) 2017, Red Hat, Inc. * contributeur : Matt Benjamin mbenjamin@redhat.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * --------------------------------------- */ #ifndef CONF_URL_H #define CONF_URL_H #include #include "gsh_list.h" struct gsh_url_provider { struct glist_head link; const char *name; void (*url_init)(void); /* XXX needs config info */ void (*url_shutdown)(void); int (*url_fetch)(const char *url, FILE **f, char **fbuf); }; /** @brief package initializer */ void config_url_init(void); int register_url_provider(struct gsh_url_provider *nurl_p); int config_url_fetch(const char *url, FILE **f, char **fbuf); void config_url_release(FILE *f, char *fbuf); #endif /* CONF_URL_H */ nfs-ganesha-2.6.0/src/include/config-h.in.cmake000066400000000000000000000050131324272410200212200ustar00rootroot00000000000000/* config.h file expanded by Cmake for build */ #ifndef CONFIG_H #define CONFIG_H #define GANESHA_VERSION_MAJOR @GANESHA_MAJOR_VERSION@ #define GANESHA_VERSION_MINOR @GANESHA_MINOR_VERSION@ #define GANESHA_PATCH_LEVEL @GANESHA_PATCH_LEVEL@ #define GANESHA_EXTRA_VERSION @GANESHA_EXTRA_VERSION@ #define GANESHA_VERSION "@GANESHA_VERSION@" #define GANESHA_BUILD_RELEASE @GANESHA_BUILD_RELEASE@ #define VERSION GANESHA_VERSION #define VERSION_COMMENT "@VERSION_COMMENT@" #define _GIT_HEAD_COMMIT "@_GIT_HEAD_COMMIT@" #define _GIT_DESCRIBE "@_GIT_DESCRIBE@" #define BUILD_HOST "@BUILD_HOST_NAME@" #define FSAL_MODULE_LOC "@FSAL_DESTINATION@" /* Build controls */ #cmakedefine _MSPAC_SUPPORT 1 #cmakedefine USE_NFSIDMAP 1 #cmakedefine USE_DBUS 1 #cmakedefine _USE_CB_SIMULATOR 1 #cmakedefine USE_CAPS 1 #cmakedefine USE_BLKID 1 #cmakedefine PROXY_HANDLE_MAPPING 1 #cmakedefine _USE_9P 1 #cmakedefine _USE_9P_RDMA 1 #cmakedefine _USE_NFS_RDMA 1 #cmakedefine _USE_NFS3 1 #cmakedefine _USE_NLM 1 #cmakedefine DEBUG_SAL 1 #cmakedefine _VALGRIND_MEMCHECK 1 #cmakedefine _NO_MOUNT_LIST 1 #cmakedefine _NO_TCP_REDISTER 1 #cmakedefine _NO_PORTMAPPER 1 #cmakedefine HAVE_STDBOOL_H 1 #cmakedefine HAVE_KRB5 1 #cmakedefine KRB5_VERSION @KRB5_VERSION@ #cmakedefine HAVE_HEIMDAL 1 #cmakedefine USE_GSS_KRB5_CCACHE_NAME 1 #cmakedefine LINUX 1 #cmakedefine FREEBSD 1 #cmakedefine APPLE 1 #cmakedefine _HAVE_GSSAPI 1 #cmakedefine HAVE_STRING_H 1 #cmakedefine HAVE_STRINGS_H 1 #cmakedefine HAVE_STRNLEN 1 #cmakedefine LITTLEEND 1 #cmakedefine BIGEND 1 #cmakedefine HAVE_XATTR_H 1 #cmakedefine HAVE_DAEMON 1 #cmakedefine USE_LTTNG 1 #cmakedefine ENABLE_VFS_DEBUG_ACL 1 #cmakedefine ENABLE_RFC_ACL 1 #cmakedefine USE_GLUSTER_SYMLINK_MOUNT 1 #cmakedefine USE_GLUSTER_XREADDIRPLUS 1 #cmakedefine USE_GLUSTER_UPCALL_REGISTER 1 #cmakedefine USE_FSAL_CEPH_MKNOD 1 #cmakedefine USE_FSAL_CEPH_SETLK 1 #cmakedefine USE_FSAL_CEPH_LL_LOOKUP_ROOT 1 #cmakedefine USE_FSAL_CEPH_STATX 1 #cmakedefine USE_FSAL_CEPH_LL_DELEGATION 1 #cmakedefine USE_FSAL_RGW_MOUNT2 1 #cmakedefine ENABLE_LOCKTRACE 1 #cmakedefine SANITIZE_ADDRESS 1 #cmakedefine DEBUG_MDCACHE 1 #cmakedefine USE_RADOS_RECOV 1 #cmakedefine RADOS_URLS 1 #define NFS_GANESHA 1 #define GANESHA_CONFIG_PATH "@SYSCONFDIR@/ganesha/ganesha.conf" #define GANESHA_PIDFILE_PATH "@SYSSTATEDIR@/run/ganesha.pid" #define NFS_V4_RECOV_ROOT "@SYSSTATEDIR@/lib/nfs/ganesha" /** * @brief Default value for krb5_param.ccache_dir */ #define DEFAULT_NFS_CCACHE_DIR "@SYSSTATEDIR@/run/ganesha" /* We're LGPL'd */ #define _LGPL_SOURCE 1 #endif /* CONFIG_H */ nfs-ganesha-2.6.0/src/include/config_parsing.h000066400000000000000000000646511324272410200212750ustar00rootroot00000000000000/* ---------------------------------------------------------------------------- * Copyright CEA/DAM/DIF (2007) * contributeur : Thomas LEIBOVICI thomas.leibovici@cea.fr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ #ifndef _CONFIG_PARSING_H #define _CONFIG_PARSING_H #include #include #include #include #include #include #include /* opaque type */ typedef caddr_t config_file_t; typedef enum { CONFIG_ITEM_BLOCK = 1, CONFIG_ITEM_VAR } config_item_type; /** * @brief Data structures for config parse tree processing */ enum term_type { TERM_TOKEN = 1, TERM_REGEX, TERM_PATH, TERM_STRING, TERM_DQUOTE, TERM_SQUOTE, TERM_TRUE, TERM_FALSE, TERM_DECNUM, TERM_HEXNUM, TERM_OCTNUM, TERM_V4_ANY, TERM_V4ADDR, TERM_V4CIDR, TERM_V6ADDR, TERM_V6CIDR, TERM_FSID, TERM_NETGROUP }; enum config_type { CONFIG_NULL = 0, CONFIG_INT16, CONFIG_UINT16, CONFIG_INT32, CONFIG_UINT32, CONFIG_INT64, CONFIG_UINT64, CONFIG_ANON_ID, CONFIG_FSID, CONFIG_STRING, CONFIG_PATH, CONFIG_LIST, CONFIG_ENUM, CONFIG_TOKEN, CONFIG_BOOL, CONFIG_BOOLBIT, CONFIG_IP_ADDR, CONFIG_BLOCK, CONFIG_PROC }; #define CONFIG_UNIQUE 0x001 /*< only one instance allowed */ #define CONFIG_MANDATORY 0x002 /*< param must be present */ #define CONFIG_MODE 0x004 /*< this param is octal "mode" */ #define CONFIG_RELAX 0x008 /*< this block has extra params * so don't complain about them */ #define CONFIG_MARK_SET 0x010 /*< Mark this param as set */ /** * @brief Config file processing error type * * This is a better way than a bunch of mask bits... * Examination of the error type lets the calling code decide * just how bad and messed up the config file is. * * NOTE: If you add an error here, update err_type_str() and friends * as well. */ struct config_error_type { bool scan:1; /*< lexer/scanner */ bool parse:1; /*< parser rules */ bool init:1; /*< block initialization */ bool fsal:1; /*< fsal load failure */ bool export_:1; /*< export create failure */ bool resource:1; /*< system resource */ bool unique:1; /*< unique block/param */ bool invalid:1; /*< invalid param value */ bool missing:1; /*< missing mandatory parameter */ bool validate:1; /*< commit param validation */ bool exists:1; /*< block already exists */ bool internal:1; /*< internal error */ bool bogus:1; /*< bogus (deprecated?) param */ bool dispose:1; /*< Not actually an error, but we need to dispose of the config item anyway. */ uint32_t errors; /*< cumulative error count for parse+proc */ char *diag_buf; /*< buffer for scan+parse+processing msgs */ size_t diag_buf_size; /*< size of diag buffer used by memstream */ FILE *fp; /*< FILE * for memstream */ }; /** @brief Error detail decoders */ /** * @brief Test for errors that require us to exit the server */ static inline bool config_error_is_fatal(struct config_error_type *err_type) { return err_type->scan || err_type->parse || err_type->init || err_type->fsal || err_type->resource; } /** * @brief Test for errors that make the processed block unuseable */ static inline bool config_error_is_crit(struct config_error_type *err_type) { return config_error_is_fatal(err_type) || err_type->internal || err_type->invalid || err_type->export_ || err_type->missing; } /** * @brief Test for errors that will not cause problems */ static inline bool config_error_is_harmless(struct config_error_type *err_type) { return !(config_error_is_crit(err_type) || err_type->unique || err_type->exists || err_type->dispose); } /** * @brief Test that there are no errors at all * * NOTE: This is valid so long as sizeof(struct config_error_type) * == sizeof(uint16_t). Use uint32_t if this expands beyond 16 bools. * It could be a union here but that makes for messy code all * over the place. Handle with care and it won't bite you. */ static inline bool config_error_no_error(struct config_error_type *err_type) { return *(uint16_t *)err_type == 0; } /** * @brief Collect/combine errors */ static inline void config_error_comb_errors(struct config_error_type *err_type, struct config_error_type *more_errs) { *(uint16_t *)err_type |= *(uint16_t *)more_errs; } struct config_block; struct config_item; /** * @brief token list for CSV options */ struct config_item_list { const char *token; uint32_t value; }; #define CONFIG_LIST_TOK(_token_, _flags_) \ { .token = _token_, .value = _flags_} #define CONFIG_LIST_EOL { .token = NULL, .value = 0} /** * @brief A config file parameter * * These are structured as an initialized array with * CONFIG_EOL as the last initializer. * * The union wraps up minimum, maximum, and default values. * The type field is used to both validate the node type * and to switch the union. Each type has conversion functions * either inline or as separate functions. * * The CONFIG_BLOCK has special handling because it may have to * allocate memory for the structure and later link it to the * link_mem or other structures. Two functions provide this linkage. * * The following two parameters are opaque pointers to the config * parsing functions. They only make semantic sense to the 'init' * and 'commit' functions. * * link_mem * This is an opaque pointer to the data structure member in the * structure being filled by the enclosing block. This is typically * a glist_head, or in the simpler case, a struct pointer. * * self_struct * This is an opaque pointer the data structure that will be filled * by this block. * * init * The init function takes two void * arguments that are used as * follows: * * link_mem == NULL, self_struct != NULL * This call is during a do_block_init where the members of the * structure are being initialized to their defaults. For a * block, this may mean the initialization of things like glist * heads which can be done only once. The return self_struct on success * and NULL for errors. * * link_mem != NULL, self_struct == NULL * This call can potentially allocate space for the structure defined * by the parameter list. The link_mem argument is passed for reference. * Some data structures are related but not linked. For example, two * structures within an enclosing structure where a container_of the * link_mem references the enclosing which can now be used to dereference * the "self_struct" structure of interest. It should initialize any members * that are NOT initialized by the do_block_init pass. It should not * link the self_struct structure to the link_mem or initialize things like * mutexes or other linked lists. See commit. An example here are * non-settable FSAL parameters. It returns a pointer to the space. * * link_mem != NULL, self_struct != NULL * This call is to free or release any resouces in this allocated * or referenced block. The link_mem argument is passed so that * dereferences as above are possible. It should not attempt to * change the link_mem such as doing glist removes. This is only * called on errors. return NULL; * * link_mem == NULL, self_struct == NULL * This is asserted as not possible. * * commit * The commit function has two functions. First, it (optionally) * validates the completed data structure. If the validation fails, * it returns non-zero error count. If the validation succeeds, it * can then do any structure specific linkage or state setting for * the structure. This state includes other linked lists and system * resources like mutexes. * * The node arg is provided for the case where the commit needs to reference * the parse tree. This is an opaque pointer that only the config_parse * know how to use. The link_mem is provided for cases where the link_mem * has as glist head that the self_struct is added to. It returns 0 to * indicate success. */ struct config_item { char *name; enum config_type type; /* switches union */ int flags; union { struct { /* CONFIG_BOOL */ bool def; } b; struct { /* CONFIG_STRING | CONFIG_PATH */ int minsize; int maxsize; const char *def; } str; struct { /* CONFIG_IP_ADDR */ const char *def; } ip; struct { /* CONFIG_INT16 */ int16_t minval; int16_t maxval; int16_t def; } i16; struct { /* CONFIG_UINT16 */ uint16_t minval; uint16_t maxval; uint16_t def; } ui16; struct { /* CONFIG_INT32 */ int32_t minval; int32_t maxval; int32_t def; uint32_t bit; size_t set_off; } i32; struct { /* CONFIG_UINT32 */ uint32_t minval; uint32_t maxval; uint32_t def; } ui32; struct { /* CONFIG_INT64 */ int64_t minval; int64_t maxval; int64_t def; uint32_t bit; size_t set_off; } i64; struct { /* CONFIG_UINT64 */ uint64_t minval; uint64_t maxval; uint64_t def; uint32_t bit; size_t set_off; } ui64; struct { /* CONFIG_FSID */ int64_t def_maj; int64_t def_min; uint32_t bit; size_t set_off; } fsid; struct { /* CONFIG_LIST | CONFIG_ENUM | CONFIG_ENUM_SET | CONFIG_LIST_BITS | CONFIG_ENUM_BITS */ uint32_t def; uint32_t mask; struct config_item_list *tokens; size_t set_off; } lst; struct { /* CONFIG_BOOLBIT */ bool def; uint32_t bit; size_t set_off; } bit; struct { /* CONFIG_BLOCK */ void *(*init)(void *link_mem, void *self_struct); struct config_item *params; int (*commit)(void *node, void *link_mem, void *self_struct, struct config_error_type *err_type); void (*display)(const char *step, void *node, void *link_mem, void *self_struct); } blk; struct { /* CONFIG_PROC */ size_t set_off; void *(*init)(void *link_mem, void *self_struct); int (*handler)(const char *token, enum term_type type_hint, struct config_item *item, void *param_addr, void *cnode, struct config_error_type *err_type); } proc; } u; size_t off; /* offset into struct pointed to by opaque_dest */ }; /** * @brief Macros for defining arrays of config items. * * A config_item array is defined with one or more of the following * macros with the last entry being CONFIG_EOL which will supply * the necessary NULL pointer to terminate the walk. * * The naming has the form: * CONF__ * * where "something special" is: * * ITEM - generic entry * * MAND - This is a mandatory entry and will throw an error if there * is no config file entry for it. * * UNIQ - This is a unique entry. Multiple definitions are an error. * * RELAX - a block where unrecognized parameters are not reported errors. * * The "type" field is used for decoding and for storage. These match * the target structure members. This set defines what is currently used. * * NOOP - Used to indicate a parameter name is expected but that it is * used/processed elsewhere. * * FSID - A filesystem id, a uint64_t '.' uint64_t * * LIST - a comma separated list of bit flags * * ENUM - a single token and its enumerated type * * BLOCK - a sub-block. It points to another item list etc. * * BOOLBIT - Similar to a LIST but it is a boolean that sets flag bits * * BOOL - a boolean * * STR - A string that must have a size >= min and <= max size * * PATH - a string defining a filesystem path * * I - A signed integer of 'size' bits * * UI - an unsigned integer of 'size' bits * * MODE - an octal integer used as the 'mode' bits of an inode * * PROC - Calls a function to process the token value * * There are a few specialized item entries * * CONF_ITEM_IP_ADDR processes an IP (both v4 and v6) address specification * * CONF_ITEM_INET_PORT processes an unsigned 16 bit integer in * network byte order. * */ #define CONF_ITEM_NOOP(_name_) \ { .name = _name_, \ .type = CONFIG_NULL, \ } #define CONF_ITEM_FSID(_name_, _def_maj_, _def_min_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_FSID, \ .u.fsid.def_maj = _def_maj_, \ .u.fsid.def_min = _def_min_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_ITEM_FSID_SET(_name_, _def_maj_, _def_min_, _struct_, \ _mem_, _bit_, _set_) \ { .name = _name_, \ .type = CONFIG_FSID, \ .flags = CONFIG_MARK_SET, \ .u.fsid.def_maj = _def_maj_, \ .u.fsid.def_min = _def_min_, \ .u.fsid.bit = _bit_, \ .u.fsid.set_off = offsetof(struct _struct_, _set_), \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_ITEM_BLOCK(_name_, _params_, _init_, _commit_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_BLOCK, \ .u.blk.init = _init_, \ .u.blk.params = _params_, \ .u.blk.commit = _commit_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_RELAX_BLOCK(_name_, _params_, _init_, _commit_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_BLOCK, \ .flags = CONFIG_RELAX, \ .u.blk.init = _init_, \ .u.blk.params = _params_, \ .u.blk.commit = _commit_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_ITEM_PROC(_name_, _init_, _handler_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_PROC, \ .u.proc.init = _init_, \ .u.proc.handler = _handler_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_ITEM_LIST(_name_, _def_, _tokens_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_LIST, \ .u.lst.def = _def_, \ .u.lst.mask = UINT32_MAX, \ .u.lst.set_off = UINT32_MAX, \ .u.lst.tokens = _tokens_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_ITEM_LIST_BITS(_name_, _def_, _mask_, _tokens_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_LIST, \ .u.lst.def = _def_, \ .u.lst.mask = _mask_, \ .u.lst.set_off = UINT32_MAX, \ .u.lst.tokens = _tokens_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_ITEM_LIST_BITS_SET(_name_, _def_, _mask_, _tokens_, _struct_, \ _mem_, _set_) \ { .name = _name_, \ .type = CONFIG_LIST, \ .flags = CONFIG_MARK_SET, \ .u.lst.def = _def_, \ .u.lst.mask = _mask_, \ .u.lst.set_off = offsetof(struct _struct_, _set_), \ .u.lst.tokens = _tokens_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_ITEM_BOOLBIT(_name_, _def_, _bit_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_BOOLBIT, \ .u.bit.def = _def_, \ .u.bit.bit = _bit_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_ITEM_BOOLBIT_SET(_name_, _def_, _bit_, _struct_, _mem_, _set_) \ { .name = _name_, \ .type = CONFIG_BOOLBIT, \ .flags = CONFIG_MARK_SET, \ .u.bit.def = _def_, \ .u.bit.bit = _bit_, \ .u.bit.set_off = offsetof(struct _struct_, _set_), \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_MAND_LIST(_name_, _def_, _tokens_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_LIST, \ .flags = CONFIG_MANDATORY, \ .u.lst.def = _def_, \ .u.lst.tokens = _tokens_, \ .off = offsetof(struct _struct_, _mem_) \ } /* Use CONF_ITEM_TOKEN for a variable that is set to a single enum * value. The CONF_ITEM_ENUM_* macros are for setting one or more * bits within a field (I know, a bit confusing...). */ #define CONF_ITEM_ENUM_BITS(_name_, _def_, _mask_, _tokens_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_ENUM, \ .u.lst.def = _def_, \ .u.lst.mask = _mask_, \ .u.lst.tokens = _tokens_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_ITEM_ENUM_BITS_SET(_name_, _def_, _mask_, _tokens_, _struct_, \ _mem_, _set_) \ { .name = _name_, \ .type = CONFIG_ENUM, \ .flags = CONFIG_MARK_SET, \ .u.lst.def = _def_, \ .u.lst.mask = _mask_, \ .u.lst.set_off = offsetof(struct _struct_, _set_), \ .u.lst.tokens = _tokens_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_ITEM_TOKEN(_name_, _def_, _tokens_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_TOKEN, \ .u.lst.def = _def_, \ .u.lst.tokens = _tokens_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_INDEX_TOKEN(_name_, _def_, _tokens_, _idx_, _sizeof_) \ { .name = _name_, \ .type = CONFIG_TOKEN, \ .u.lst.def = _def_, \ .u.lst.tokens = _tokens_, \ .off = (sizeof(_sizeof_) * _idx_) \ } #define CONF_ITEM_BOOL(_name_, _def_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_BOOL, \ .u.b.def = _def_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_ITEM_STR(_name_, _minsize_, _maxsize_, _def_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_STRING, \ .u.str.minsize = _minsize_, \ .u.str.maxsize = _maxsize_, \ .u.str.def = _def_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_MAND_STR(_name_, _minsize_, _maxsize_, _def_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_STRING, \ .flags = CONFIG_UNIQUE|CONFIG_MANDATORY, \ .u.str.minsize = _minsize_, \ .u.str.maxsize = _maxsize_, \ .u.str.def = _def_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_ITEM_PATH(_name_, _minsize_, _maxsize_, _def_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_PATH, \ .u.str.minsize = _minsize_, \ .u.str.maxsize = _maxsize_, \ .u.str.def = _def_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_MAND_PATH(_name_, _minsize_, _maxsize_, _def_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_PATH, \ .flags = CONFIG_UNIQUE|CONFIG_MANDATORY, \ .u.str.minsize = _minsize_, \ .u.str.maxsize = _maxsize_, \ .u.str.def = _def_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_UNIQ_PATH(_name_, _minsize_, _maxsize_, _def_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_PATH, \ .flags = CONFIG_UNIQUE, \ .u.str.minsize = _minsize_, \ .u.str.maxsize = _maxsize_, \ .u.str.def = _def_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_ITEM_IP_ADDR(_name_, _def_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_IP_ADDR, \ .u.ip.def = _def_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_MAND_IP_ADDR(_name_, _def_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_IP_ADDR, \ .flags = CONFIG_UNIQUE|CONFIG_MANDATORY, \ .u.ip.def = _def_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_ITEM_I16(_name_, _min_, _max_, _def_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_INT16, \ .u.ui16.minval = _min_, \ .u.ui16.maxval = _max_, \ .u.ui16.def = _def_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_ITEM_UI16(_name_, _min_, _max_, _def_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_UINT16, \ .u.ui16.minval = _min_, \ .u.ui16.maxval = _max_, \ .u.ui16.def = _def_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_MAND_UI16(_name_, _min_, _max_, _def_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_UINT16, \ .flags = CONFIG_UNIQUE|CONFIG_MANDATORY, \ .u.ui16.minval = _min_, \ .u.ui16.maxval = _max_, \ .u.ui16.def = _def_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_ITEM_I32(_name_, _min_, _max_, _def_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_INT32, \ .u.i32.minval = _min_, \ .u.i32.maxval = _max_, \ .u.i32.def = _def_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_ITEM_I32_SET(_name_, _min_, _max_, _def_, _struct_, _mem_, \ _bit_, _set_) \ { .name = _name_, \ .type = CONFIG_INT32, \ .flags = CONFIG_MARK_SET, \ .u.i32.minval = _min_, \ .u.i32.maxval = _max_, \ .u.i32.def = _def_, \ .u.i32.bit = _bit_, \ .u.i32.set_off = offsetof(struct _struct_, _set_), \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_ITEM_ANON_ID_SET(_name_, _def_, _struct_, _mem_, \ _bit_, _set_) \ { .name = _name_, \ .type = CONFIG_ANON_ID, \ .flags = CONFIG_MARK_SET, \ .u.i64.minval = INT32_MIN, \ .u.i64.maxval = UINT32_MAX, \ .u.i64.def = _def_, \ .u.i64.bit = _bit_, \ .u.i64.set_off = offsetof(struct _struct_, _set_), \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_ITEM_UI32(_name_, _min_, _max_, _def_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_UINT32, \ .u.ui32.minval = _min_, \ .u.ui32.maxval = _max_, \ .u.ui32.def = _def_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_MAND_UI32(_name_, _min_, _max_, _def_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_UINT32, \ .flags = CONFIG_UNIQUE|CONFIG_MANDATORY, \ .u.ui32.minval = _min_, \ .u.ui32.maxval = _max_, \ .u.ui32.def = _def_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_ITEM_MODE(_name_, _def_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_UINT32, \ .flags = CONFIG_MODE, \ .u.ui32.minval = 0, \ .u.ui32.maxval = 0777, \ .u.ui32.def = _def_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_ITEM_I64(_name_, _min_, _max_, _def_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_INT64, \ .u.i64.minval = _min_, \ .u.i64.maxval = _max_, \ .u.i64.def = _def_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_ITEM_UI64(_name_, _min_, _max_, _def_, _struct_, _mem_) \ { .name = _name_, \ .type = CONFIG_UINT64, \ .u.ui64.minval = _min_, \ .u.ui64.maxval = _max_, \ .u.ui64.def = _def_, \ .off = offsetof(struct _struct_, _mem_) \ } #define CONF_ITEM_UI64_SET(_name_, _min_, _max_, _def_, _struct_, \ _mem_, _bit_, _set_) \ { .name = _name_, \ .type = CONFIG_UINT64, \ .flags = CONFIG_MARK_SET, \ .u.ui64.minval = _min_, \ .u.ui64.maxval = _max_, \ .u.ui64.def = _def_, \ .u.ui64.bit = _bit_, \ .u.ui64.set_off = offsetof(struct _struct_, _set_), \ .off = offsetof(struct _struct_, _mem_) \ } #define CONFIG_EOL {.name = NULL, .type = CONFIG_NULL} /** * @brief Configuration Block * * This is used for both config file parse tree processing * and DBus property settings. */ struct config_block { char *dbus_interface_name; struct config_item blk_desc; }; /** * @brief Check whether a given value is prime or not * * @param[in] v A given integer * * @return Whether it's prime or not. */ static inline bool is_prime(int v) { int i, m; if (v <= 1) return false; if (v == 2) return true; if (v % 2 == 0) return false; /* dont link with libm just for this */ #ifdef LINK_LIBM m = (int)sqrt(v); #else m = v - 1; #endif for (i = 3; i <= m; i += 2) { if (v % i == 0) return false; } return true; } /** * @brief Parse the content of a configuration file into a parse tree. * * @param file_path [IN] local path to the config file * @param err_type [OUT] Error type. Check this for success. * * @return pointer to parse tree. Must be freed if != NULL */ config_file_t config_ParseFile(char *file_path, struct config_error_type *err_type); /** * Return the first node in the global config block list with * name == block_name */ void *config_GetBlockNode(const char *block_name); /** * config_Print: * Print the content of the syntax tree * to a file. */ void config_Print(FILE *output, config_file_t config); /* Free the memory structure that store the configuration. */ void config_Free(config_file_t config); /* Find the root of the parse tree given a TYPE_BLOCK node */ config_file_t get_parse_root(void *node); struct config_node_list { void *tree_node; struct config_node_list *next; }; /* find a node in the parse tree using expression */ int find_config_nodes(config_file_t config, char *expr, struct config_node_list **node_list, struct config_error_type *err_type); /* fill configuration structure from parse tree */ int load_config_from_node(void *tree_node, struct config_block *conf_blk, void *param, bool unique, struct config_error_type *err_type); /* fill configuration structure from parse tree */ int load_config_from_parse(config_file_t config, struct config_block *conf_blk, void *param, bool unique, struct config_error_type *err_type); /* translate err_type values to log/dbus error string*/ const char *config_term_name(enum term_type type); const char *config_term_desc(enum term_type type); char *err_type_str(struct config_error_type *err_type); bool init_error_type(struct config_error_type *err_type); void config_errs_to_log(char *err, void *, struct config_error_type *err_type); void config_proc_error(void *cnode, struct config_error_type *err_type, char *format, ...); void report_config_errors(struct config_error_type *err_type, void *dest, void (*logger)(char *msg, void *dest, struct config_error_type *err_type)); /** * @brief NOOP config initializer and commit functions. * Most config blocks refer to static structures that don't * need either allocation and sometimes validation/commit */ void *noop_conf_init(void *link_mem, void *self_struct); int noop_conf_commit(void *node, void *link_mem, void *self_struct, struct config_error_type *err_type); #endif nfs-ganesha-2.6.0/src/include/delayed_exec.h000066400000000000000000000036351324272410200207130ustar00rootroot00000000000000/* * Copyright (c) 2013, The Linux Box Corporation * * Some portions copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup delayed Delayed Execution * * This provides a simple system allowing tasks to be submitted along * with a delay. * This is similar to the thread fridge, however there is a lot of * complication in the thread fridge that would make no sense here, * and the delay structure here is very different from the thread * fridge's task queue. Someone could merge the two together, but it * would make the internal logic rather snarly and the initialization * parameters even more recondite. * * @{ */ /** * @file delayed_exec.h * @author Adam C. Emerson * @brief Header for the delayed execution system */ #ifndef DELAYED_EXEC_H #define DELAYED_EXEC_H #include #include #include "gsh_types.h" void delayed_start(void); void delayed_shutdown(void); int delayed_submit(void (*)(void *), void *, nsecs_elapsed_t); #endif /* DELAYED_EXEC_H */ /** @} */ nfs-ganesha-2.6.0/src/include/display.h000066400000000000000000000146551324272410200177510ustar00rootroot00000000000000/* * Copyright IBM Corporation, 2012 * Contributor: Frank Filz * * -------------------------- * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * */ /** * @defgroup Display display_buffer implementation * @{ */ /** * @file display.h * @author Frank Filz * @brief Implementation of a buffer for constructing string messages. * * This file provides a buffer descriptor for string messages that * contains a current position as well as the buffer pointer and size. * A variety of functions are provided to manipulate the buffer and * append various strings to the buffer. */ #ifndef _DISPLAY_H #define _DISPLAY_H #include #include /** * @page Display Safe display buffers * * A struct display_buffer describes a string buffer and the current position * within it so that a string can be built of various components. This is * especially useful for nested display functions for data types, where * the top level display function may call display functions for sub-data types. * * While building a complex string, users SHOULD check the return value from * each display function and exit if it is <= 0, however, continuing to call * display functions will be totally safe. * * The only things that MUST be done if building a new display primitive is * to call display_start at the beginning and display_finish at the end. A * display primitive is a function that uses a non-display function (such as * strcat, memcpy, sprintf) to copy bytes into the buffer. Such primitives must * assure that any such routines do not overflow the buffer, and then the * primitive must manage the b_current. display_finish will handle proper * indication of a full buffer or buffer overflow. * * A display function that is not a primitive (only uses display functions * themselves) SHOULD call display_start to make sure the buffer isn't already * full. It also assures the buffer will not wind up without a NUL terminator * should it not actually make any display calls. * * The core routines: * * display_start validate and prepare to start appending to the buffer. * display_finish wrap up after appending to the buffer. * display_reset_buffer reset a buffer for re-use to build a new string. * display_printf append to the string using printf formatting * display_opaque_value format an opaque value into the buffer * display_cat append a simple string to the buffer * * There are variants of these functions. */ /** * @brief Descriptor for display buffers. * * This structure defines a display buffer. * Buffer may be allocated global, on the stack, or by malloc. */ struct display_buffer { size_t b_size; /*< Size of the buffer, will hold b_size - 1 chars plus a '\0' */ char *b_current; /*< Current position in the buffer, where the next string will be appended */ char *b_start; /*< Start of the buffer */ }; int display_buffer_remain(struct display_buffer *dspbuf); int display_start(struct display_buffer *dspbuf); int display_finish(struct display_buffer *dspbuf); int display_force_overflow(struct display_buffer *dspbuf); /** * @brief Reset current position in buffer to start. * * @param[in,out] dspbuf The buffer. * */ static inline void display_reset_buffer(struct display_buffer *dspbuf) { /* To re-use a buffer, all we need to do is roll b_current back to * b_start and make it empty. */ dspbuf->b_current = dspbuf->b_start; *dspbuf->b_current = '\0'; } /** * @brief Compute the string length of the buffer. * * @param[in] dspbuf The buffer to finish up. * * @return the length. * * This function is more efficient than strlen if the buffer hasn't overflowed. * */ static inline size_t display_buffer_len(struct display_buffer *dspbuf) { size_t len = dspbuf->b_current - dspbuf->b_start; if (len == dspbuf->b_size) { /* Buffer has overflowed, due to forced overflow or partial * UTF-8 fixup, the actual string length might actually be less * than the full length of the buffer. Just use strlen. */ return strlen(dspbuf->b_start); } else { return len; } } int display_vprintf(struct display_buffer *dspbuf, const char *fmt, va_list args); /** * @brief Format a string into the buffer. * * @param[in,out] dspbuf The buffer. * @param[in] fmt the format string * @param[in] ... the args * * @return the bytes remaining in the buffer. * */ static inline int display_printf(struct display_buffer *dspbuf, const char *fmt, ...) { va_list args; int b_left; va_start(args, fmt); b_left = display_vprintf(dspbuf, fmt, args); va_end(args); return b_left; } int display_opaque_bytes(struct display_buffer *dspbuf, void *value, int len); int display_opaque_value_max(struct display_buffer *dspbuf, void *value, int len, int max); /** * @brief Display a number of opaque bytes as a hex string. * * @param[in,out] dspbuf The buffer. * @param[in] value The bytes to display * @param[in] len The number of bytes in the opaque value * * @return the bytes remaining in the buffer. * * This routine just calls display_opaque_value_max with max = len. * */ static inline int display_opaque_value(struct display_buffer *dspbuf, void *value, int len) { return display_opaque_value_max(dspbuf, value, len, len); } int display_len_cat(struct display_buffer *dspbuf, char *str, int len); /** * @brief Append a null delimited string to the buffer. * * @param[in,out] dspbuf The buffer. * @param[in] str The string * * @return the bytes remaining in the buffer. * */ static inline int display_cat(struct display_buffer *dspbuf, char *str) { return display_len_cat(dspbuf, str, strlen(str)); } int display_cat_trunc(struct display_buffer *dspbuf, char *str, size_t max); /** @} */ #endif /* _DISPLAY_H */ nfs-ganesha-2.6.0/src/include/err_inject.h000066400000000000000000000020621324272410200204150ustar00rootroot00000000000000/* * Copyright IBM Corporation, 2011 * Contributor: Frank Filz * * -------------------------- * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * */ #ifndef ERR_INJECT_H #define ERR_INJECT_H #ifdef _ERROR_INJECTION extern int worker_delay_time; extern int next_worker_delay_time; int init_error_injector(void); #endif #endif /* ERR_INJECT_H */ nfs-ganesha-2.6.0/src/include/export_mgr.h000066400000000000000000000171231324272410200204630ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2013 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ /** * @defgroup Filesystem export management * @{ */ /** * @file export_mgr.h * @author Jim Lieb * @brief Export manager */ #include "gsh_list.h" #include "avltree.h" #include "abstract_atomic.h" #include "fsal.h" #ifndef EXPORT_MGR_H #define EXPORT_MGR_H enum export_status { EXPORT_READY, /*< searchable, usable */ EXPORT_STALE, /*< export is no longer valid */ }; /** * @brief Represents an export. * * CFG: Identifies those fields that are associated with configuration. * */ struct gsh_export { /** List of all exports */ struct glist_head exp_list; /** gsh_exports are kept in an AVL tree by export_id */ struct avltree_node node_k; /** List of NFS v4 state belonging to this export */ struct glist_head exp_state_list; /** List of locks belonging to this export */ struct glist_head exp_lock_list; /** List of NLM shares belonging to this export */ struct glist_head exp_nlm_share_list; /** List of exports rooted on the same inode */ struct glist_head exp_root_list; /** List of exports to be mounted or cleaned up */ struct glist_head exp_work; /** List of exports mounted on this export */ struct glist_head mounted_exports_list; /** This export is a node in the list of mounted_exports */ struct glist_head mounted_exports_node; /** Entry for the root of this export, protected by lock */ struct fsal_obj_handle *exp_root_obj; /** CFG Allowed clients - update protected by lock */ struct glist_head clients; /** Entry for the junction of this export. Protected by lock */ struct fsal_obj_handle *exp_junction_obj; /** The export this export sits on. Protected by lock */ struct gsh_export *exp_parent_exp; /** Pointer to the fsal_export associated with this export */ struct fsal_export *fsal_export; /** CFG: Exported path - static option */ char *fullpath; /** CFG: PseudoFS path for export - static option */ char *pseudopath; /** CFG: Tag for direct NFS v3 mounting of export - static option */ char *FS_tag; /** Node id this is mounted on. Protected by lock */ uint64_t exp_mounted_on_file_id; /** CFG: Max Read for this entry - atomic changeable option */ uint64_t MaxRead; /** CFG: Max Write for this entry - atomic changeable option */ uint64_t MaxWrite; /** CFG: Preferred Read size - atomic changeable option */ uint64_t PrefRead; /** CFG: Preferred Write size - atomic changeable option */ uint64_t PrefWrite; /** CFG: Preferred Readdir size - atomic changeable option */ uint64_t PrefReaddir; /** CFG: Maximum Offset allowed for write - atomic changeable option */ uint64_t MaxOffsetWrite; /** CFG: Maximum Offset allowed for read - atomic changeable option */ uint64_t MaxOffsetRead; /** CFG: Filesystem ID for overriding fsid from FSAL - ????? */ fsal_fsid_t filesystem_id; /** References to this export */ int64_t refcnt; /** Read/Write lock protecting export */ pthread_rwlock_t lock; /** CFG: available mount options - update protected by lock */ struct export_perms export_perms; /** The last time the export stats were updated */ nsecs_elapsed_t last_update; /** CFG: Export non-permission options - atomic changeable option */ uint32_t options; /** CFG: Export non-permission options set - atomic changeable option */ uint32_t options_set; /** CFG: Expiration time interval in seconds for attributes. Settable with Attr_Expiration_Time. - atomic changeable option */ int32_t expire_time_attr; /** CFG: Export_Id for this export - static option */ uint16_t export_id; uint8_t export_status; /*< current condition */ bool has_pnfs_ds; /*< id_servers matches export_id */ }; /* Use macro to define this to get around include file order. */ #define export_path(export) \ ((nfs_param.core_param.mount_path_pseudo) \ ? ((export)->pseudopath) \ : ((export)->fullpath)) /* If op_ctx request is NFS_V4 always use pseudopath, otherwise use fullpath * for export. */ #define op_ctx_export_path(export) \ ((op_ctx->nfs_vers == NFS_V4) || \ (nfs_param.core_param.mount_path_pseudo) \ ? ((export)->pseudopath) \ : ((export)->fullpath)) static inline bool op_ctx_export_has_option(uint32_t option) { return atomic_fetch_uint32_t(&op_ctx->ctx_export->options) & option; } static inline bool op_ctx_export_has_option_set(uint32_t option) { return atomic_fetch_uint32_t(&op_ctx->ctx_export->options_set) & option; } void export_pkginit(void); #ifdef USE_DBUS void dbus_export_init(void); #endif struct gsh_export *alloc_export(void); void free_export(struct gsh_export *a_export); bool insert_gsh_export(struct gsh_export *a_export); struct gsh_export *get_gsh_export(uint16_t export_id); struct gsh_export *get_gsh_export_by_path(char *path, bool exact_match); struct gsh_export *get_gsh_export_by_path_locked(char *path, bool exact_match); struct gsh_export *get_gsh_export_by_pseudo(char *path, bool exact_match); struct gsh_export *get_gsh_export_by_pseudo_locked(char *path, bool exact_match); struct gsh_export *get_gsh_export_by_tag(char *tag); bool mount_gsh_export(struct gsh_export *exp); void remove_gsh_export(uint16_t export_id); bool foreach_gsh_export(bool(*cb) (struct gsh_export *exp, void *state), bool wrlock, void *state); /** * @brief Advisory check of export readiness. * * This function does not guarantee the export is reachable at the point of * the test, it is just used to allow a function to take a shortcut if the * export has gone stale, usually when the function is about to take an * additional reference based on some object having a pointer and reference * to the export. * * @param[in] export The export to test for readiness. * * @retval true if the export is ready */ static inline bool export_ready(struct gsh_export *a_export) { return a_export->export_status == EXPORT_READY; } void _get_gsh_export_ref(struct gsh_export *a_export, char *file, int line, char *function); #define get_gsh_export_ref(a_export) \ _get_gsh_export_ref(a_export, \ (char *) __FILE__, __LINE__, (char *) __func__) void _put_gsh_export(struct gsh_export *a_export, char *file, int line, char *function); #define put_gsh_export(a_export) \ _put_gsh_export(a_export, \ (char *) __FILE__, __LINE__, (char *) __func__) void export_revert(struct gsh_export *a_export); void export_add_to_mount_work(struct gsh_export *a_export); void export_add_to_unexport_work_locked(struct gsh_export *a_export); void export_add_to_unexport_work(struct gsh_export *a_export); struct gsh_export *export_take_mount_work(void); struct gsh_export *export_take_unexport_work(void); extern struct config_block add_export_param; extern struct config_block update_export_param; void remove_all_exports(void); #endif /* !EXPORT_MGR_H */ /** @} */ nfs-ganesha-2.6.0/src/include/extended_types.h000066400000000000000000000034641324272410200213240ustar00rootroot00000000000000/* * * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file include/extended_types.h * @brief Extended types, platform dependant. * */ #ifndef _EXTENDED_TYPES_H #define _EXTENDED_TYPES_H #include "config.h" #include #ifdef LINUX #include #elif FREEBSD #include #endif /* Added extended types, often missing */ typedef long long longlong_t; typedef unsigned long long u_longlong_t; typedef unsigned int uint_t; /* conflict between sys/xattr.h and attr/xattr.h * this comes from xfs/linux.h. Very bad form but we are * stuck with it. If we didn't pick it up somewhere else * make is so here. * Danger Will Robinson!! this is an overlay of another errno... */ #ifndef ENOATTR /* ENOATTR is a BSD-ism, it does not exist on Linux. ENODATA is used instead */ #define ENOATTR ENODATA /* No such attribute */ #endif #endif /* _EXTENDED_TYPES_H */ nfs-ganesha-2.6.0/src/include/fridgethr.h000066400000000000000000000206051324272410200202520ustar00rootroot00000000000000/* * * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup fridgethr Thread Fridge * * The thread fridge provides a simple implementation of a thread pool * built atop POSIX threading capabilities. * * @{ */ /** * @file fridgethr.c * @brief Header for the thread fridge */ #ifndef FRIDGETHR_H #define FRIDGETHR_H #include #include #include #include "gsh_list.h" #include "gsh_wait_queue.h" struct fridgethr; /*< Decoder thread pool */ extern struct fridgethr *req_fridge; /** * @brief Per-worker data. Some of this will be destroyed. */ typedef struct nfs_worker_data { wait_q_entry_t wqe; /*< Queue for coordinating with decoder */ unsigned int worker_index; /*< Index for log messages */ } nfs_worker_data_t; /** * @brief A given thread in the fridge */ struct fridgethr_entry { /** * @brief Thread context */ struct fridgethr_context { struct nfs_worker_data wd; /*< Work queue data */ pthread_mutex_t mtx; /*< Mutex for fiddling this thread */ pthread_cond_t cv; /*< Condition variable to wait for sync */ sigset_t sigmask; /*< This thread's signal mask */ void *thread_info; /*< Information belonging to the user and associated with the thread. Never modified by the fridgethr code. */ void (*func)(struct fridgethr_context *); /*< Function being executed */ void *arg; /*< Functions argument */ pthread_t id; /*< Thread ID */ uint32_t uflags; /*< Flags (for any use) */ bool woke; /*< Set to false on first run and if wait in fridgethr_freeze didn't time out. */ } ctx; uint32_t flags; /*< Thread-fridge flags (for handoff) */ bool frozen; /*< Thread is frozen */ struct timespec timeout; /*< Wait timeout */ struct glist_head thread_link; /*< Link in the list of all threads */ struct glist_head idle_link; /*< Link in the idle queue */ struct fridgethr *fr; /*< The fridge we belong to */ }; /** * @brief Fridge flavor, governing style of operation. */ typedef enum { fridgethr_flavor_worker = 0, /*< Take submitted jobs, do them, and then wait for more work to be submitted. */ fridgethr_flavor_looper = 1 /*< Each thread takes a single job and repeats it. */ } fridgethr_flavor_t; /** * @brief Enumeration governing requests when the fridge is full */ typedef enum { fridgethr_defer_fail = 0, /*< If the fridge is full, return an error immediately. This is the only allowable value for fridgethr_flavor_looper. */ fridgethr_defer_queue = 1, /*< If the fridge is full, queue requests for later and return immediately. */ fridgethr_defer_block = 2 /*< if the fridge is full, wait for a thread to become available and execute on it. Optionally, return an error on timeout. */ } fridgethr_defer_t; /** * @brief Parameters set at fridgethr_init */ struct fridgethr_params { uint32_t thr_max; /*< Maximum number of threads */ uint32_t thr_min; /*< Low watermark for threads. Do not expire threads out if we have this many or fewer. */ time_t thread_delay; /*< Time frozen threads will wait after performing work. For fridgethr_flavor_worker fridges, threads exit if they are above the low water mark and no work is available after timeout. For fridgethr_flavor_looper fridges, sleep for this period before re-executing the supplied function. */ fridgethr_flavor_t flavor; /*< Execution flavor for this fridge. */ fridgethr_defer_t deferment; /*< Deferment strategy for this fridge */ time_t block_delay; /*< How long to wait before a thread becomes available. */ /** * If non-NULL, run after every submitted job. */ void (*task_cleanup)(struct fridgethr_context *); /** * If non-NULL, called on thread creation just before we start * work, but after we set the function name (so it can be * overridden.) */ void (*thread_initialize)(struct fridgethr_context *); /** * If non-NULL, called on thread exit, just before the context * is freed. */ void (*thread_finalize)(struct fridgethr_context *); /** * Use this function to wake up all threads on a state * transition. Specifying this function implies that the * worker in a thread will wait for more work on its own. The * run function must be written either so that it exits after * any given piece of work or such that it calls @c * fridgethr_you_should_break before waiting. */ void (*wake_threads)(void *); /* Argument for wake_threads */ void *wake_threads_arg; }; /** * @brief Queued requests */ struct fridgethr_work { struct glist_head link; /*< Link in the work queue */ void (*func)(struct fridgethr_context *); /*< Function being executed */ void *arg; /*< Functions argument */ }; /** * @brief Commands a caller can issue */ typedef enum { fridgethr_comm_run, /*< Demand all threads execute */ fridgethr_comm_pause, /*< Demand all threads suspend */ fridgethr_comm_stop /*< Demand all threads exit */ } fridgethr_comm_t; /** * @brief Structure representing a group of threads */ struct fridgethr { char *s; /*< Name for this fridge */ struct fridgethr_params p; /*< Parameters */ pthread_mutex_t mtx; /*< Mutex */ pthread_attr_t attr; /*< Creation attributes */ struct glist_head thread_list; /*< List of threads */ uint32_t nthreads; /*< Number of threads in fridge */ struct glist_head idle_q; /*< Idle threads */ uint32_t nidle; /*< Number of idle threads */ uint32_t flags; /*< Fridge-wide flags */ fridgethr_comm_t command; /*< Command state */ void (*cb_func)(void *); /*< Callback on command completion */ void *cb_arg; /*< Argument for completion callback */ pthread_mutex_t *cb_mtx; /*< Mutex for completion condition variable */ pthread_cond_t *cb_cv; /*< Condition variable, signalled on completion */ bool transitioning; /*< Changing state */ union { struct glist_head work_q; /*< Work queued */ struct { pthread_cond_t cond; /*< Condition variable on which we wait for a thread to become available. */ uint32_t waiters; /*< Requests blocked waiting for a thread. */ } block; } deferment; }; #define fridgethr_flag_none 0x0000 /*< Null flag */ #define fridgethr_flag_available 0x0001 /*< I am available to be dispatched */ #define fridgethr_flag_dispatched 0x0002 /*< You have been dispatched */ int fridgethr_init(struct fridgethr **, const char *, const struct fridgethr_params *); void fridgethr_destroy(struct fridgethr *); int fridgethr_submit(struct fridgethr *, void (*)(struct fridgethr_context *), void *); int fridgethr_wake(struct fridgethr *); int fridgethr_pause(struct fridgethr *, pthread_mutex_t *, pthread_cond_t *, void (*)(void *), void *); int fridgethr_stop(struct fridgethr *, pthread_mutex_t *, pthread_cond_t *, void (*)(void *), void *); int fridgethr_start(struct fridgethr *, pthread_mutex_t *, pthread_cond_t *, void (*)(void *), void *); int fridgethr_sync_command(struct fridgethr *, fridgethr_comm_t, time_t); bool fridgethr_you_should_break(struct fridgethr_context *); int fridgethr_populate(struct fridgethr *, void (*)(struct fridgethr_context *), void *); void fridgethr_setwait(struct fridgethr_context *ctx, time_t thread_delay); time_t fridgethr_getwait(struct fridgethr_context *ctx); void fridgethr_cancel(struct fridgethr *fr); extern struct fridgethr *general_fridge; int general_fridge_init(void); int general_fridge_shutdown(void); #endif /* FRIDGETHR_H */ /** @} */ nfs-ganesha-2.6.0/src/include/fsal.h000066400000000000000000000435261324272410200172300ustar00rootroot00000000000000/* * * * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * @defgroup FSAL File-System Abstraction Layer * @{ */ /** * @file fsal.h * @brief Main FSAL externs and functions * @note not called by other header files. */ /** * @brief Thread Local Storage (TLS). * * TLS variables look like globals but since they are global only in the * context of a single thread, they do not require locks. This is true * of all thread either within or separate from a/the fridge. * * All thread local storage is declared extern here. The actual * storage declaration is in fridgethr.c. */ /** * @brief Operation context (op_ctx). * * This carries everything relevant to a protocol operation. * Space for the struct itself is allocated elsewhere. * Test/assert opctx != NULL first (or let the SEGV kill you) */ extern __thread struct req_op_context *op_ctx; #ifndef FSAL_H #define FSAL_H #include "fsal_api.h" #include "nfs23.h" #include "nfs4_acls.h" /** * @brief If we don't know how big a buffer we want for a link, use * this value. */ #define fsal_default_linksize (4096) /** * @brief Pointer to FSAL module by number. * This is actually defined in common_pnfs.c */ extern struct fsal_module *pnfs_fsal[]; /** * @brief Delegations types list for the Delegations parameter in FSAL. * This is actually defined in exports.c */ extern struct config_item_list deleg_types[]; /* Export permissions for root op context, defined in protocol layer */ extern uint32_t root_op_export_options; extern uint32_t root_op_export_set; /** * @brief node id used to construct recovery directory in * cluster implementation. */ extern int g_nodeid; /** * @brief Ops context for asynch and not protocol tasks that need to use * subsystems that depend on op_ctx. */ struct root_op_context { struct req_op_context req_ctx; struct req_op_context *old_op_ctx; struct user_cred creds; struct export_perms export_perms; }; extern size_t open_fd_count; static inline void init_root_op_context(struct root_op_context *ctx, struct gsh_export *exp, struct fsal_export *fsal_exp, uint32_t nfs_vers, uint32_t nfs_minorvers, uint32_t req_type) { /* Initialize req_ctx. * Note that a zeroed creds works just fine as root creds. */ memset(ctx, 0, sizeof(*ctx)); ctx->req_ctx.creds = &ctx->creds; ctx->req_ctx.nfs_vers = nfs_vers; ctx->req_ctx.nfs_minorvers = nfs_minorvers; ctx->req_ctx.req_type = req_type; ctx->req_ctx.ctx_export = exp; ctx->req_ctx.fsal_export = fsal_exp; if (fsal_exp) ctx->req_ctx.fsal_module = fsal_exp->fsal; else if (op_ctx) ctx->req_ctx.fsal_module = op_ctx->fsal_module; ctx->req_ctx.export_perms = &ctx->export_perms; ctx->export_perms.set = root_op_export_set; ctx->export_perms.options = root_op_export_options; ctx->old_op_ctx = op_ctx; op_ctx = &ctx->req_ctx; } static inline void release_root_op_context(void) { struct root_op_context *ctx; ctx = container_of(op_ctx, struct root_op_context, req_ctx); op_ctx = ctx->old_op_ctx; } /****************************************************** * Structure used to define a fsal ******************************************************/ #include "FSAL/access_check.h" /* rethink where this should go */ /** * Global fsal manager functions * used by nfs_main to initialize fsal modules. */ /* Called only within MODULE_INIT and MODULE_FINI functions of a fsal * module */ /** * @brief Register a FSAL * * This function registers an FSAL with ganesha and initializes the * public portion of the FSAL data structure, including providing * default operation vectors. * * @param[in,out] fsal_hdl The FSAL module to register. * @param[in] name The FSAL's name * @param[in] major_version Major version fo the API against which * the FSAL was written * @param[in] minor_version Minor version of the API against which * the FSAL was written. * * @return 0 on success. * @return EINVAL on version mismatch. */ int register_fsal(struct fsal_module *fsal_hdl, const char *name, uint32_t major_version, uint32_t minor_version, uint8_t fsal_id); /** * @brief Unregister an FSAL * * This function unregisters an FSAL from Ganesha. It should be * called from the module finalizer as part of unloading. * * @param[in] fsal_hdl The FSAL to unregister * * @return 0 on success. * @return EBUSY if outstanding references or exports exist. */ int unregister_fsal(struct fsal_module *fsal_hdl); /** * @brief Find and take a reference on an FSAL * * This function finds an FSAL by name and increments its reference * count. It is used as part of export setup. The @c put method * should be used to release the reference before unloading. */ struct fsal_module *lookup_fsal(const char *name); int load_fsal(const char *name, struct fsal_module **fsal_hdl); int fsal_load_init(void *node, const char *name, struct fsal_module **fsal_hdl_p, struct config_error_type *err_type); struct fsal_args { char *name; }; void *fsal_init(void *link_mem, void *self_struct); struct subfsal_args { char *name; void *fsal_node; }; int subfsal_commit(void *node, void *link_mem, void *self_struct, struct config_error_type *err_type); void destroy_fsals(void); void emergency_cleanup_fsals(void); void start_fsals(void); void display_fsinfo(struct fsal_staticfsinfo_t *info); int display_attrlist(struct display_buffer *dspbuf, struct attrlist *attr, bool is_obj); void log_attrlist(log_components_t component, log_levels_t level, const char *reason, struct attrlist *attr, bool is_obj, char *file, int line, char *function); #define LogAttrlist(component, level, reason, attr, is_obj) \ do { \ if (unlikely(isLevel(component, level))) \ log_attrlist(component, level, reason, attr, is_obj, \ (char *) __FILE__, __LINE__, \ (char *) __func__); \ } while (0) const char *msg_fsal_err(fsal_errors_t fsal_err); #define fsal_err_txt(s) msg_fsal_err((s).major) /* * FSAL helpers */ enum cb_state { CB_ORIGINAL, CB_JUNCTION, CB_PROBLEM, }; typedef fsal_errors_t (*helper_readdir_cb) (void *opaque, struct fsal_obj_handle *obj, const struct attrlist *attr, uint64_t mounted_on_fileid, uint64_t cookie, enum cb_state cb_state); /** * Indicate whether this is a read or write operation, for fsal_rdwr. */ typedef enum io_direction__ { FSAL_IO_READ = 1, /*< Reading */ FSAL_IO_WRITE = 2, /*< Writing */ FSAL_IO_READ_PLUS = 3, /*< Reading plus */ FSAL_IO_WRITE_PLUS = 4 /*< Writing plus */ } fsal_io_direction_t; /** * @brief Type of callback for fsal_readdir * * This callback provides the upper level protocol handling function * with one directory entry at a time. It may use the opaque to keep * track of the structure it is filling, space used, and so forth. * * This function should return true if the entry has been added to the * caller's responde, or false if the structure is fulled and the * structure has not been added. */ struct fsal_readdir_cb_parms { void *opaque; /*< Protocol specific parms */ const char *name; /*< Dir entry name */ bool attr_allowed; /*< True if caller has perm to getattr */ bool in_result; /*< true if the entry has been added to the *< caller's responde, or false if the *< structure is filled and the entry has not *< been added. */ }; fsal_status_t fsal_setattr(struct fsal_obj_handle *obj, bool bypass, struct state_t *state, struct attrlist *attr); /** * * @brief Checks the permissions on an object * * This function returns success if the supplied credentials possess * permission required to meet the specified access. * * @param[in] obj The object to be checked * @param[in] access_type The kind of access to be checked * * @return FSAL status * */ static inline fsal_status_t fsal_access(struct fsal_obj_handle *obj, fsal_accessflags_t access_type) { return obj->obj_ops.test_access(obj, access_type, NULL, NULL, false); } fsal_status_t fsal_link(struct fsal_obj_handle *obj, struct fsal_obj_handle *dest_dir, const char *name); fsal_status_t fsal_readlink(struct fsal_obj_handle *obj, struct gsh_buffdesc *link_content); fsal_status_t fsal_lookup(struct fsal_obj_handle *parent, const char *name, struct fsal_obj_handle **obj, struct attrlist *attrs_out); fsal_status_t fsal_lookupp(struct fsal_obj_handle *obj, struct fsal_obj_handle **parent, struct attrlist *attrs_out); fsal_status_t fsal_create(struct fsal_obj_handle *parent, const char *name, object_file_type_t type, struct attrlist *attrs, const char *link_content, struct fsal_obj_handle **obj, struct attrlist *attrs_out); void fsal_create_set_verifier(struct attrlist *sattr, uint32_t verf_hi, uint32_t verf_lo); bool fsal_create_verify(struct fsal_obj_handle *obj, uint32_t verf_hi, uint32_t verf_lo); fsal_status_t fsal_read2(struct fsal_obj_handle *obj, bool bypass, struct state_t *state, uint64_t offset, size_t io_size, size_t *bytes_moved, void *buffer, bool *eof, struct io_info *info); fsal_status_t fsal_write2(struct fsal_obj_handle *obj, bool bypass, struct state_t *state, uint64_t offset, size_t io_size, size_t *bytes_moved, void *buffer, bool *sync, struct io_info *info); fsal_status_t fsal_readdir(struct fsal_obj_handle *directory, uint64_t cookie, unsigned int *nbfound, bool *eod_met, attrmask_t attrmask, helper_readdir_cb cb, void *opaque); fsal_status_t fsal_remove(struct fsal_obj_handle *parent, const char *name); fsal_status_t fsal_rename(struct fsal_obj_handle *dir_src, const char *oldname, struct fsal_obj_handle *dir_dest, const char *newname); fsal_status_t fsal_open2(struct fsal_obj_handle *in_obj, struct state_t *state, fsal_openflags_t openflags, enum fsal_create_mode createmode, const char *name, struct attrlist *attr, fsal_verifier_t verifier, struct fsal_obj_handle **obj, struct attrlist *attrs_out); fsal_status_t fsal_reopen2(struct fsal_obj_handle *obj, struct state_t *state, fsal_openflags_t openflags, bool check_permission); fsal_status_t get_optional_attrs(struct fsal_obj_handle *obj_hdl, struct attrlist *attrs_out); /** * @brief Close a file * * This handles both support_ex case and regular case (in case of * support_ex, close method is expected to manage whether file is * actually open or not, in old API case, close method should only * be closed if the file is open). * * In a change to the old way, non-regular files are just ignored. * * @param[in] obj File to close * @return FSAL status */ static inline fsal_status_t fsal_close(struct fsal_obj_handle *obj_hdl) { if (obj_hdl->type != REGULAR_FILE) { /* Can only close a regular file */ return fsalstat(ERR_FSAL_NO_ERROR, 0); } /* Return the result of close method. */ fsal_status_t status = obj_hdl->obj_ops.close(obj_hdl); if (status.major != ERR_FSAL_NOT_OPENED) { (void) atomic_dec_size_t(&open_fd_count); } else { /* Wasn't open. Not an error, but shouldn't decrement */ status = fsalstat(ERR_FSAL_NO_ERROR, 0); } return status; } fsal_status_t fsal_statfs(struct fsal_obj_handle *obj, fsal_dynamicfsinfo_t *dynamicinfo); /** * @brief Commit a section of a file to storage * * @param[in] obj File to commit * @param[in] offset Offset for start of commit * @param[in] len Length of commit * @return FSAL status */ static inline fsal_status_t fsal_commit(struct fsal_obj_handle *obj, off_t offset, size_t len) { if ((uint64_t) len > ~(uint64_t) offset) return fsalstat(ERR_FSAL_INVAL, 0); return obj->obj_ops.commit2(obj, offset, len); } fsal_status_t fsal_verify2(struct fsal_obj_handle *obj, fsal_verifier_t verifier); /** * @brief Pepare an attrlist for fetching attributes. * * @param[in,out] attrs The attrlist to work with * @param[in] The mask to use for the fetch * */ static inline void fsal_prepare_attrs(struct attrlist *attrs, attrmask_t request_mask) { memset(attrs, 0, sizeof(*attrs)); attrs->request_mask = request_mask; } /** * @brief Release any extra resources from an attrlist. * * @param[in] attrs The attrlist to work with * */ static inline void fsal_release_attrs(struct attrlist *attrs) { if (attrs->acl != NULL) { int acl_status; acl_status = nfs4_acl_release_entry(attrs->acl); if (acl_status != NFS_V4_ACL_SUCCESS) LogCrit(COMPONENT_FSAL, "Failed to release old acl, status=%d", acl_status); /* Poison the acl since we no longer hold a reference. */ attrs->acl = NULL; attrs->valid_mask &= ~ATTR_ACL; } } /** * @brief Copy a set of attributes * * If ACL is requested in dest->request_mask, then ACL reference is acquired, * otherwise acl pointer is set to NULL. * * @param[in,out] dest The attrlist to receive the copy (mask must be set) * @param[in] src The attrlist to make a copy of * @param[in] pass_refs If true, pass the ACL reference to dest. * */ static inline void fsal_copy_attrs(struct attrlist *dest, struct attrlist *src, bool pass_refs) { attrmask_t save_request_mask = dest->request_mask; /* Copy source to dest, but retain dest->request_mask */ *dest = *src; dest->request_mask = save_request_mask; if (pass_refs && ((save_request_mask & ATTR_ACL) != 0)) { /* Pass any ACL reference to the dest, so remove from src * without adjusting the refcount. */ src->acl = NULL; src->valid_mask &= ~ATTR_ACL; } else if (dest->acl != NULL && ((save_request_mask & ATTR_ACL) != 0)) { /* Take reference on ACL if necessary */ nfs4_acl_entry_inc_ref(dest->acl); } else { /* Make sure acl is NULL and don't pass a ref back (so * caller when calling fsal_release_attrs will not have to * release the ACL reference). */ dest->acl = NULL; dest->valid_mask &= ~ATTR_ACL; } } /** * @brief Return a changeid4 for this file. * * @param[in] obj The file to query. * * @return A changeid4 indicating the last modification of the file. */ static inline changeid4 fsal_get_changeid4(struct fsal_obj_handle *obj) { struct attrlist attrs; fsal_status_t status; changeid4 change; fsal_prepare_attrs(&attrs, ATTR_CHANGE | ATTR_CHGTIME); status = obj->obj_ops.getattrs(obj, &attrs); if (FSAL_IS_ERROR(status)) return 0; change = (changeid4) attrs.change; /* Done with the attrs */ fsal_release_attrs(&attrs); return change; } static inline enum fsal_create_mode nfs4_createmode_to_fsal(createmode4 createmode) { return (enum fsal_create_mode) (1 + (unsigned int) createmode); } static inline enum fsal_create_mode nfs3_createmode_to_fsal(createmode3 createmode) { return (enum fsal_create_mode) (1 + (unsigned int) createmode); } /** * @brief Determine if the openflags associated with an fd indicate it * is not open in a mode usable by the caller. * * The caller may pass FSAL_O_ANY to indicate any mode of open (RDONLY, * WRONLY, or RDWR is useable - often just to fetch attributes or something). * * @param[in] fd_openflags The openflags describing the fd * @param[in] to_openflags The openflags describing the desired mode */ static inline bool not_open_usable(fsal_openflags_t fd_openflags, fsal_openflags_t to_openflags) { /* 1. fd_openflags will NEVER be FSAL_O_ANY. * 2. If to_openflags == FSAL_O_ANY, the first half will be true if the * file is closed, and the second half MUST be true (per statement 1) * 3. If to_openflags is anything else, the first half will be true and * the second half will be true if fd_openflags does not include * the requested modes. */ return (to_openflags != FSAL_O_ANY || fd_openflags == FSAL_O_CLOSED) && ((fd_openflags & to_openflags) != to_openflags); } /** * @brief Determine if the openflags associated with an fd indicate it * is open in a mode usable by the caller. * * The caller may pass FSAL_O_ANY to indicate any mode of open (RDONLY, * WRONLY, or RDWR is useable - often just to fetch attributes or something). * * Note that this function is not just an inversion of the above function * because O_SYNC is not considered. * * @param[in] fd_openflags The openflags describing the fd * @param[in] to_openflags The openflags describing the desired mode */ static inline bool open_correct(fsal_openflags_t fd_openflags, fsal_openflags_t to_openflags) { return (to_openflags == FSAL_O_ANY && fd_openflags != FSAL_O_CLOSED) || (to_openflags != FSAL_O_ANY && (fd_openflags & to_openflags & FSAL_O_RDWR) == (to_openflags & FSAL_O_RDWR)); } /** * @brief "fsal_op_stats" struct useful for all the fsals which are going to * implement support for FSAL specific statistics */ struct fsal_op_stats { uint16_t op_code; uint64_t resp_time; uint64_t num_ops; uint64_t resp_time_max; uint64_t resp_time_min; }; struct fsal_stats { uint16_t total_ops; struct fsal_op_stats *op_stats; }; #endif /* !FSAL_H */ /** @} */ nfs-ganesha-2.6.0/src/include/fsal_api.h000066400000000000000000003066721324272410200200650ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @defgroup FSAL File-System Abstraction Layer * @{ */ /** * @file fsal_api.h * @author Jim Lieb * @brief The object-oriented FSAL API */ #ifndef FSAL_API #define FSAL_API #include "fsal_types.h" #include "fsal_pnfs.h" #include "sal_shared.h" #include "config_parsing.h" #include "avltree.h" #include "abstract_atomic.h" /** ** Forward declarations to resolve circular dependency conflicts */ struct gsh_client; struct gsh_export; struct fsal_up_vector; /* From fsal_up.h */ struct state_t; /** * @page newapi New FSAL API * * @section structs Public and Private Data Structures * * Shared FSAL data structures have two definitions, one that is * global and passed around by the core, the other private which * included the global definition within it. * * All these data structures are passed back to the core with the * global pointer and dereferenced with container_of within the FSAL * itself like so: * * @code{.c} * * struct private_obj_handle * { * [private stuff] * struct fsal_obj_handle *pub; * } * * fsal_getattr(struct fsal_obj_handle handle_pub) * { * struct private_obj_handle *handle; * * handle = container_of(handle_pub, * struct private_obj_handle, pub); * [ do stuff ] * } * @endcode * * The @c container_of macro takes the public pointer/handle @c * handle_pub which is indicated as the element @c pub of structure * type @c private_obj_handle. Throughout the function, where private * elements are dereferenced, the @c handle pointer is used. The @c * handle_pub pointer is used in the public case. * * @section usage Object usage * * Mutex locks and reference counts are used to manage both concurrent * usage and state. The reference counts are use to determine when * the object is "free". Current use is for managing ref counts and * lists. This will be expanded, though many cases are already * handled by the locks in Cache inode. * * Since we cannot create objects out of thin air, there is an order * based on one object being the "context" in which the other is * created. In other words, a @c fsal_export is created from the @c * fsal_module that connects it to the backing store (filesystem). The * same applies to a @c fsal_obj_handle that only makes sense for a * specific 'fsal_export'. * * When an object is created, it is returned with a reference already * taken. The callee of the creating method must then either keep a * persistent reference to it or @c put it back. For example, a @c * fsal_export gets created for each export in the configuration. A * pointer to it gets saved in @c gsh_export and it has a reference * to reflect this. It is now safe to use it to do a @c lookup which * will return a @c fsal_obj_handle which can then be kept in a cache * inode entry. If we had done a @c put on the export, it could be * freed at any point and make a @c lookup using it unsafe. * * In addition to a reference count, object that create other objects * have a list of all the objects they create. This serves two * purposes. The obvious case is to keep the object "busy" until all * of its children are freed. Second, it provides a means to visit * all of the objects it creates. * * Every object has a pointer to its parent. This is used for such * things as managing the object list and for calling methods on the * parent. * * @section versioning Versioning * * One intent in this API is to be able to support fsals that are built * out-of-tree and possibly out of synch with the core of Ganesha. This * is managed by version numbers in this file that are validated at load * time for the fsal. There are major and minor version numbers which are * monotonically increasing numbers ( V1 < V2 means V2 is newer). * * API guarantee: * * * If major version numbers differ, the fsal will not be loaded because * the api has changed enough to make it unsafe. * * * If the major versions are equal, the minor version determines loadability. * * - A fsal that is older than the Ganesha core can safely load and run. * * - A fsal that is newer than the Ganesha core is not safe and will not * be loaded. * * * @section vector Operation Vectors * * Each structure carries with it an @c ops pointer. Default * operation vectors are created at FSAL moduel initialziation time, * and may be overridden there. Individual exports or handles may * have different operations vectors, but they should all be derived * from the module operations vector. * * This vector is used to access methods e.g.: * * @code{.c} * exp_hdl->exp_ops.lookup(exp_hdl, name, ...); * @endcode * * Note that exp_hdl is used to dereference the method and it is also * *always* the first argument to the method/function. Think of it as * the 'this' argument. * * @section Operation Context * * Protocol operations have lots of state such as user creds, the * export currently in use etc. Rather than pass all this down the * stack we take advantage of the design decision that a protocol * operation runs to completion in the thread that dequeued the * request from the RPC. All of the operation state (other than * some intermediate results passed as function args) are pointed * to by the thread local 'op_ctx'. This will always point to a * valid and initialized 'struct req_op_context'. * * Method code can reference through 'op_ctx' e.g. * * @code{.c} * if (op_ctx->req_type == 9P) { ... } * @endcode * */ /** * @page handles File-Handles and You * * Overview * ======== * * In the FSAL, file handles can take four forms. There is the full, * internal handle structure, compose of the @c fsal_obj_handle and * the FSAL-private structure that contains it. * * There is the wire-handle, the FSAL-generated portion of the * file handles exchanged between Ganesha and its clients through the * FS protocol. The wire-handle should contain everything necessary * to find and use the file even if the file has been completely * purged from cache or Ganesha has restarted from nothing. There may * be multiple wire-handles per @c fsal_obj_handle. The wire-handle * is produced by the @c handle_to_wire method on @c fsal_obj_handle. * FSALs can produce big endian or little endian wire-handles depending * on the architecture. * * A host-handle is a verion of the wire handle in host endian format. It is * produced from wire-handle by @c wire_to_host to the host's native endian * type. @c fsal_export operation @c create_handle produces a new @c * fsal_obj_handle from a host-handle. * * Finally, there is the handle-key, a portion of the handle that contains * all and only information that uniquely identifies the object within * the entire FSAL (it is insufficient if it only identifies it within * the export or within a filesystem.) There are two functions that * generate a handle-key, one is the @c host_to_key method on @c * fsal_export. It is used to get the key from a host-handle so that * it can be looked up in the cache. The other is @c handle_to_key on * @c fsal_obj_handle. This is used after lookup or some other * operation that produces a @c fsal_obj_handle so that it can be * stored or looked up in the cache. * * The invariant to be maintained is that given an @c fsal_obj_handle, * obj_hdl, exp_ops.host_to_key(wire_to_host(handle_to_wire(obj_hdl))) * is equal to obj_ops.handle_to_key(obj_hdl). * * History and Details * =================== * * The terminology is confusing here. The old function names were * kept (up to a point), but the semantics differ in ways both subtle * and catastrophic. Making matters worse, that the first FSAL written * was VFS, where the internal @c file_handle for the syscalls is the * whole of the key, opaque, and syscall arg. This does not imply any * equivalence. * * In the old regime, the only place available to store _anything_ was * the handle array in @c cache_entry_t. People overloaded it with * all kinds of rubbish as a result, and the wire-handle, the * handle-key, and other stuff get mushed together. To sort things * out, * * 1. The wire-handle opaque _must_ be enough to re-acquire the cache * entry and its associated @c fsal_obj_handle. Other than that, * it doesn't matter a whit. The client treats the whole protocol * handle (including what is in the opaque) as an opaque token. * * 2. The purpose of the @c export_id in the protocol "handle" is to * locate the FSAL that knows what is inside the opaque. The @c * wire_to_host is an export method for that purpose. It should * be able to take the wire-handle opaque and translate it into * a host-handle. handle-key should be derived from @c host_to_key * that MDCACHE can use to find an entry. * * 3. OBSOLETE - cache_inode_get takes an fh_desc argument which is not a * handle but a _key_. It is used to generate the hash and to do * the secondary key compares. That is all it is used for. The * end result _must_ be a cache entry and its associated * @c fsal_obj_handle. See how @c cache_inode_get transitions to * cache_inode_new to see how this works. * * 4. The @c handle_to_key method, a @c fsal_obj_handle method, * generates a key for the MDCACHE hash table from the contents * of the @c fsal_obj_handle. It is an analogue of fsal export * @c host_to_key method. Note where it is called to see why it is * there. * * 5. The @c handle_to_wire method is similar in scope but it is the * inverse of @c wire_to_host. It's job is to fill in the opaque * part of a protocol handle. Note that it gets passed a @c gsh_buffdesc * that describes the full opaque storage in whatever protocol * specific structure is used. It's job is to put whatever it * takes into the opaque so the second and third items in this list * work. * * 6. Unlike the old API, a @c fsal_obj_handle is part of a FSAL * private structure for the object. Note that there is no handle * member of this public structure. The bits necessary to both * create a wire handle and use a filesystem handle go into this * private structure. You can put whatever is required into the * private part. Since both @c fsal_export and @c fsal_obj_handle * have private object storage, you could even do things like have * a container anchored in the export object that maps the * FSAL-external handle to the filesystem data needed to talk to * the filesystem. If you need more info to deal with handles * differing due to hard-links, this is where you would put * it. You would also have some other context in this private data * to do the right thing. Just make sure there is a way to * disambiguate the multiple cases. We do have to observe UNIX * semantics here. * * The upper layers don't care about the private handle data. All * they want is to be able to get something out from the object * (result of a lookup) so it can find the object again later. The * obvious case is what you describe in @c nfs[34]_FhandleToCache. These * various methods make that happen. * */ /** * @brief Major Version * * Increment this whenever any part of the existing API is changed, * e.g. the argument list changed or a method is removed. * * This has been bumped to 5.0 even though the old non-support_ex API * still exists. This is because we expect all FSALs to convert to the * new API. The old API will be removed early in the V2.6 development * cycle at which point we will move to 6.0. */ #define FSAL_MAJOR_VERSION 7 /** * @brief Minor Version * * Increment this whenever a new method is appended to the m_ops vector. * The remainder of the API is unchanged. * * If the major version is incremented, reset the minor to 0 (zero). * * If new members are appended to struct req_op_context (following its own * rules), increment the minor version */ #define FSAL_MINOR_VERSION 0 /* Forward references for object methods */ struct fsal_module; struct fsal_export; struct fsal_obj_handle; struct fsal_filesystem; struct fsal_pnfs_ds; struct fsal_pnfs_ds_ops; struct fsal_ds_handle; struct fsal_dsh_ops; #ifndef SEEK_SET #define SEEK_SET 0 #endif #ifndef SEEK_CUR #define SEEK_CUR 1 #endif #ifndef SEEK_END #define SEEK_END 2 #endif #ifndef SEEK_DATA #define SEEK_DATA 3 #endif #ifndef SEEK_HOLE #define SEEK_HOLE 4 #endif struct io_info { contents io_content; uint32_t io_advise; bool_t io_eof; }; struct io_hints { offset4 offset; length4 count; uint32_t hints; }; /** * @brief request op context * * This is created early in the operation with the context of the * operation. The difference between "context" and request parameters * or arguments is that the context is derived information such as * the resolved credentials, socket (network and client host) data * and other bits of environment associated with the request. It gets * passed down the call chain only as far as it needs to go for the op * i.e. don't put it in the function/method proto "just because". * * The lifetime of this structure and all the data it points to is the * operation for V2,3 and the compound for V4+. All elements and what * they point to are invariant for the lifetime. * * NOTE: This is an across-the-api shared structure. It must survive with * older consumers of its contents. Future development can change * this struct so long as it follows the rules: * * 1. New elements are appended at the end, never inserted in the middle. * * 2. This structure _only_ contains pointers and simple scalar values. * * 3. Changing an already defined struct pointer is strictly not allowed. * * 4. This struct is always passed by reference, never by value. * * 5. This struct is never copied/saved. * * 6. Code changes are first introduced in the core. Assume the fsal * module does not know and the code will still do the right thing. */ struct req_op_context { struct user_cred *creds; /*< resolved user creds from request */ struct user_cred original_creds; /*< Saved creds */ struct group_data *caller_gdata; gid_t *caller_garray_copy; /*< Copied garray from AUTH_SYS */ gid_t *managed_garray_copy; /*< Copied garray from managed gids */ int cred_flags; /* Various cred flags */ sockaddr_t *caller_addr; /*< IP connection info */ const uint64_t *clientid; /*< Client ID of caller, NULL if unknown/not applicable. */ uint32_t nfs_vers; /*< NFS protocol version of request */ uint32_t nfs_minorvers; /*< NFSv4 minor version */ uint32_t req_type; /*< request_type NFS | 9P */ struct gsh_client *client; /*< client host info including stats */ struct gsh_export *ctx_export; /*< current export */ struct fsal_export *fsal_export; /*< current fsal export */ struct export_perms *export_perms; /*< Effective export perms */ nsecs_elapsed_t start_time; /*< start time of this op/request */ nsecs_elapsed_t queue_wait; /*< time in wait queue */ void *fsal_private; /*< private for FSAL use */ struct fsal_module *fsal_module; /*< current fsal module */ struct fsal_pnfs_ds *fsal_pnfs_ds; /*< current pNFS DS */ /* add new context members here */ }; /** * @brief FSAL module methods */ struct fsal_ops { /**@{*/ /** * Base methods for loading and lifetime. */ /** * @brief Unload a module * * This function unloads the FSL module. It should not be overridden. * * @param[in] fsal_hdl The module to unload. * * @retval 0 On success. * @retval EBUSY If there are outstanding references or exports. */ int (*unload)(struct fsal_module *fsal_hdl); /**@}*/ /**@{*/ /** * Subclass/instance methods in each fsal */ /** * @brief Initialize the configuration * * Given the root of the Ganesha configuration structure, initialize * the FSAL parameters. * * @param[in] fsal_hdl The FSAL module * @param[in] config_struct Parsed ganesha configuration file * @param[out]err_type config error processing state * * @return FSAL status. */ fsal_status_t (*init_config)(struct fsal_module *fsal_hdl, config_file_t config_struct, struct config_error_type *err_type); /** * @brief Dump configuration * * This function dumps a human readable representation of the FSAL * configuration. * * @param[in] fsal_hdl The FSAL module. * @param[in] log_fd File descriptor to which to output the dump */ void (*dump_config)(struct fsal_module *fsal_hdl, int log_fd); /** * @brief Create a new export * * This function creates a new export in the FSAL using the supplied * path and options. The function is expected to allocate its own * export (the full, private structure). It must then initialize the * public portion like so: * * @code{.c} * fsal_export_init(&private_export_handle->pub); * @endcode * * After doing other private initialization, it must attach the export * to the module, like so: * * * @code{.c} * fsal_attach_export(fsal_hdl, * &private_export->pub.exports); * * @endcode * * And create the parent link with: * * @code{.c} * private_export->pub.fsal = fsal_hdl; * @endcode * * @note This seems like something that fsal_attach_export should * do. -- ACE. * * @param[in] fsal_hdl FSAL module * @param[in] parse_node opaque pointer to parse tree node for * export options to be passed to * load_config_from_node * @param[out] err_type config proocessing error reporting * @param[in] up_ops Upcall ops * * @return FSAL status. */ fsal_status_t (*create_export)(struct fsal_module *fsal_hdl, void *parse_node, struct config_error_type *err_type, const struct fsal_up_vector *up_ops); /** * @brief Minimal emergency cleanup on error * * This method is called only in the event of a catastrophic * failure. Currently, it will be called if some detail of the orderly * shutdown fails, so that FSALs will have the opportunity to leave * their underlying filesystems in a consistent state. It may at some * later time be called in the event of a crash. The majority of FSALs * will have no need to implement this call and should not do so. * * This function should, if implemented: * * 1. Do the bare minimum necessary to allow access to the each * underlying filesystem it serves. (the equivalent of a clean * unmount, so that a future instance of Ganesha or other tool can * mount the filesystem without difficulty.) How the FSAL defines * 'underlying filesystem' is FSAL specific. The FSAL handle itself * has a list of attached exports and that can be traversed if * suitable. * * 2. It /must not/ take any mutices, reader-writer locks, spinlocks, * sleep on any condition variables, or similar. Since other threads * may have crashed or been cancelled, locks may be left held, * overwritten with random garbage, or be similarly awful. The point * is to shut down cleanly, and you can't shut down cleanly if you're * hung. This does not create a race condition, since other threads in * Ganesha will have been cancelled by this point. * * 3. If it is at all possible to avoid, do not allocate memory on the * heap or use other services that require the user space to be in a * consistent state. If this is called from a crash handler, the Arena * may be corrupt. If you know that your FSAL *will* require memory, * you should either allocate it statically, or dynamically at * initialization time. */ void (*emergency_cleanup)(void); /** * pNFS functions */ /** * @brief Get information about a pNFS device * * When this function is called, the FSAL should write device * information to the @c da_addr_body stream. * * @param[in] fsal_hdl FSAL module * @param[out] da_addr_body An XDR stream to which the FSAL is to * write the layout type-specific information * corresponding to the deviceid. * @param[in] type The type of layout that specified the * device * @param[in] deviceid The device to look up * * @return Valid error codes in RFC 5661, p. 365. */ nfsstat4(*getdeviceinfo)(struct fsal_module *fsal_hdl, XDR * da_addr_body, const layouttype4 type, const struct pnfs_deviceid *deviceid); /** * @brief Max Size of the buffer needed for da_addr_body in getdeviceinfo * * This function sets policy for XDR buffer allocation in getdeviceinfo. * If FSAL has a const size, just return it here. If it is dependent on * what the client can take return ~0UL. In any case the buffer allocated will * not be bigger than client's requested maximum. * * @param[in] exp_hdl Filesystem to interrogate * * @return Max size of the buffer needed for a da_addr_body */ size_t (*fs_da_addr_size)(struct fsal_module *fsal_hdl); /** * @brief Create a FSAL pNFS data server * * @param[in] fsal_hdl FSAL module * @param[in] parse_node opaque pointer to parse tree node for * export options to be passed to * load_config_from_node * @param[out] handle FSAL pNFS DS * * @return FSAL status. */ fsal_status_t (*fsal_pnfs_ds)(struct fsal_module *const fsal_hdl, void *parse_node, struct fsal_pnfs_ds **const handle); /** * @brief Initialize FSAL specific values for pNFS data server * * @param[in] ops FSAL pNFS Data Server operations vector */ void (*fsal_pnfs_ds_ops)(struct fsal_pnfs_ds_ops *ops); /** * @brief Provides function to extract FSAL stats * * @param[in] fsal_hdl FSAL module * @param[in] iter opaque pointer to DBusMessageIter */ void (*fsal_extract_stats)(struct fsal_module *const fsal_hdl, void *iter); /** * @brief FSAL function to reset FSAL stats * * @param[in] fsal_hdl FSAL module */ void (*fsal_reset_stats)(struct fsal_module *const fsal_hdl); /**@}*/ }; /** * @brief Export operations */ struct export_ops { /**@{*/ /** * Export information */ /** * @brief Get the name of the FSAL provisioning the export * * This function is used to find the name of the ultimate FSAL providing the * filesystem. If FSALs are stacked, then the super-FSAL may want to pass this * through to the sub-FSAL to get the name, or add the sub-FSAL's name onto it's * own name. * * @param[in] exp_hdl The export to query. * @return Name of FSAL provisioning export */ const char *(*get_name)(struct fsal_export *exp_hdl); /**@}*/ /**@{*/ /** * Export lifecycle management. */ /** * @brief Clean up an export when it's unexported * * This function is called when the export is unexported. It should release any * working data that is not necessary when unexported, but not free the export * itself, as there are still references to it. * * @param[in] exp_hdl The export to unexport. * @param[in] root_obj The root object of the export */ void (*unexport)(struct fsal_export *exp_hdl, struct fsal_obj_handle *root_obj); /** * @brief Finalize an export * * This function is called as part of cleanup when the last reference to * an export is released and it is no longer part of the list. It * should clean up all private resources and destroy the object. * * @param[in] exp_hdl The export to release. */ void (*release)(struct fsal_export *exp_hdl); /**@}*/ /**@{*/ /** * Create an object handles within this export */ /** * @brief Look up a path * * This function looks up a path within the export, it is typically * used to get a handle for the root directory of the export. * * The caller will set the request_mask in attrs_out to indicate the attributes * of interest. ATTR_ACL SHOULD NOT be requested and need not be provided. If * not all the requested attributes can be provided, this method MUST return * an error unless the ATTR_RDATTR_ERR bit was set in the request_mask. * * Since this method instantiates a new fsal_obj_handle, it will be forced * to fetch at least some attributes in order to even know what the object * type is (as well as it's fileid and fsid). For this reason, the operation * as a whole can be expected to fail if the attributes were not able to be * fetched. * * @param[in] exp_hdl The export in which to look up * @param[in] path The path to look up * @param[out] handle The object found * @param[in,out] attrs_out Optional attributes for newly created object * * @note On success, @a handle has been ref'd * * @return FSAL status. */ fsal_status_t (*lookup_path)(struct fsal_export *exp_hdl, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out); /** * @brief Look up a junction * * This function returns a handle for the directory behind a junction * object. * * @deprecated This function is not implemented by any FSAL nor is it * called. It exists here as a placeholder for implementation in 2.1 * as part of the PseudoFSAL work. Its argument structure will almost * certainly change. * * @param[in] exp_hdl Export in which to look up * @param[in] junction The junction object * @param[out] handle The underlying directory handle * * @return FSAL status. */ fsal_status_t (*lookup_junction)(struct fsal_export *exp_hdl, struct fsal_obj_handle *junction, struct fsal_obj_handle **handle); /** * @brief Convert a wire handle to a host handle * * This function extracts a host handle from a wire handle. That * is, when given a handle as passed to a client, this method will * extract the handle to create objects. * * @param[in] exp_hdl Export handle * @param[in] in_type Protocol through which buffer was received. * @param[in] flags Flags to describe the wire handle. Example, if * the handle is a big endian handle. * @param[in,out] fh_desc Buffer descriptor. The address of the * buffer is given in @c fh_desc->buf and must * not be changed. @c fh_desc->len is the * length of the data contained in the buffer, * @c fh_desc->len must be updated to the correct * host handle size. * * @return FSAL type. */ fsal_status_t (*wire_to_host)(struct fsal_export *exp_hdl, fsal_digesttype_t in_type, struct gsh_buffdesc *fh_desc, int flags); /** * @brief extract "key" from a host handle * * This function extracts a "key" from a host handle. That is, when * given a handle that is extracted from wire_to_host() above, this * method will extract the unique bits used to index the inode cache. * * @param[in] exp_hdl Export handle * @param[in,out] fh_desc Buffer descriptor. The address of the * buffer is given in @c fh_desc->buf and must * not be changed. @c fh_desc->len is the length * of the data contained in the buffer, @c * fh_desc->len must be updated to the correct * size. In other words, the key has to be placed * at the beginning of the buffer! */ fsal_status_t (*host_to_key)(struct fsal_export *exp_hdl, struct gsh_buffdesc *fh_desc); /** * @brief Create a FSAL object handle from a host handle * * This function creates a FSAL object handle from a host handle * (when an object is no longer in cache but the client still remembers * the handle). * * The caller will set the request_mask in attrs_out to indicate the attributes * of interest. ATTR_ACL SHOULD NOT be requested and need not be provided. If * not all the requested attributes can be provided, this method MUST return * an error unless the ATTR_RDATTR_ERR bit was set in the request_mask. * * Since this method instantiates a new fsal_obj_handle, it will be forced * to fetch at least some attributes in order to even know what the object * type is (as well as it's fileid and fsid). For this reason, the operation * as a whole can be expected to fail if the attributes were not able to be * fetched. * * @param[in] exp_hdl The export in which to create the handle * @param[in] hdl_desc Buffer descriptor for the host handle * @param[out] handle FSAL object handle * @param[in,out] attrs_out Optional attributes for newly created object * * @note On success, @a handle has been ref'd * * @return FSAL status. */ fsal_status_t (*create_handle)(struct fsal_export *exp_hdl, struct gsh_buffdesc *fh_desc, struct fsal_obj_handle **handle, struct attrlist *attrs_out); /**@}*/ /**@{*/ /** * Statistics and configuration for this filesystem */ /** * @brief Get filesystem statistics * * This function gets information on inodes and space in use and free * for a filesystem. See @c fsal_dynamicinfo_t for details of what to * fill out. * * @param[in] exp_hdl Export handle to interrogate * @param[in] obj_hdl Directory * @param[out] info Buffer to fill with information * * @retval FSAL status. */ fsal_status_t (*get_fs_dynamic_info)(struct fsal_export *exp_hdl, struct fsal_obj_handle *obj_hdl, fsal_dynamicfsinfo_t *info); /** * @brief Export feature test * * This function checks whether a feature is supported on this * filesystem. The features that can be interrogated are given in the * @c fsal_fsinfo_options_t enumeration. * * @param[in] exp_hdl The export to interrogate * @param[in] option The feature to query * * @retval true if the feature is supported. * @retval false if the feature is unsupported or unknown. */ bool (*fs_supports)(struct fsal_export *exp_hdl, fsal_fsinfo_options_t option); /** * @brief Get the greatest file size supported * * @param[in] exp_hdl Filesystem to interrogate * * @return Greatest file size supported. */ uint64_t (*fs_maxfilesize)(struct fsal_export *exp_hdl); /** * @brief Get the greatest read size supported * * @param[in] exp_hdl Filesystem to interrogate * * @return Greatest read size supported. */ uint32_t (*fs_maxread)(struct fsal_export *exp_hdl); /** * @brief Get the greatest write size supported * * @param[in] exp_hdl Filesystem to interrogate * * @return Greatest write size supported. */ uint32_t (*fs_maxwrite)(struct fsal_export *exp_hdl); /** * @brief Get the greatest link count supported * * @param[in] exp_hdl Filesystem to interrogate * * @return Greatest link count supported. */ uint32_t (*fs_maxlink)(struct fsal_export *exp_hdl); /** * @brief Get the greatest name length supported * * @param[in] exp_hdl Filesystem to interrogate * * @return Greatest name length supported. */ uint32_t (*fs_maxnamelen)(struct fsal_export *exp_hdl); /** * @brief Get the greatest path length supported * * @param[in] exp_hdl Filesystem to interrogate * * @return Greatest path length supported. */ uint32_t (*fs_maxpathlen)(struct fsal_export *exp_hdl); /** * @brief Get the lease time for this filesystem * * @note Currently this value has no effect, with lease time being * configured globally for all filesystems at once. * * @param[in] exp_hdl Filesystem to interrogate * * @return Lease time. */ struct timespec (*fs_lease_time)(struct fsal_export *exp_hdl); /** * @brief Get supported ACL types * * This function returns a bitmask indicating whether it supports * ALLOW, DENY, neither, or both types of ACL. * * @note Could someone with more ACL support tell me if this is sane? * Is it legitimate for an FSAL supporting ACLs to support just ALLOW * or just DENY without supporting the other? It seems fishy to * me. -- ACE * * @param[in] exp_hdl Filesystem to interrogate * * @return supported ACL types. */ fsal_aclsupp_t (*fs_acl_support)(struct fsal_export *exp_hdl); /** * @brief Get supported attributes * * This function returns a list of all attributes that this FSAL will * support. Be aware that this is specifically the attributes in * struct attrlist, other NFS attributes (fileid and so forth) are * supported through other means. * * @param[in] exp_hdl Filesystem to interrogate * * @return supported attributes. */ attrmask_t (*fs_supported_attrs)(struct fsal_export *exp_hdl); /** * @brief Get umask applied to created files * * @note This seems fishy to me. Is this actually supported properly? * And is it something we want the FSAL being involved in? We already * have the functions in Protocol/NFS specifying a default mode. -- ACE * * @param[in] exp_hdl Filesystem to interrogate * * @return creation umask. */ uint32_t (*fs_umask)(struct fsal_export *exp_hdl); /** * @brief Get permissions applied to names attributes * * @note This doesn't make sense to me as an export-level parameter. * Permissions on named attributes could reasonably vary with * permission and ownership of the associated file, and some * attributes may be read/write while others are read-only. -- ACE * * @param[in] exp_hdl Filesystem to interrogate * * @return permissions on named attributes. */ uint32_t (*fs_xattr_access_rights)(struct fsal_export *exp_hdl); /**@}*/ /**@{*/ /** * Quotas are managed at the file system (export) level. Someone who * uses quotas, please look over these comments to check/expand them. */ /** * @brief Check if quotas allow an operation * * This function checks to see if a user has overrun a quota and * should be disallowed from performing an operation that would * consume blocks or inodes. * * @param[in] exp_hdl The export to interrogate * @param[in] filepath The path within the export to check * @param[in] quota_type Whether we are checking inodes or blocks * * @return FSAL types. */ fsal_status_t (*check_quota)(struct fsal_export *exp_hdl, const char *filepath, int quota_type); /** * @brief Get a user's quota * * This function retrieves a given user's quota. * * @param[in] exp_hdl The export to interrogate * @param[in] filepath The path within the export to check * @param[in] quota_type Whether we are checking inodes or blocks * @param[in] quota_id Id for which quota is set * @param[out] quota The user's quota * * @return FSAL types. */ fsal_status_t (*get_quota)(struct fsal_export *exp_hdl, const char *filepath, int quota_type, int quota_id, fsal_quota_t *quota); /** * @brief Set a user's quota * * This function sets a user's quota. * * @param[in] exp_hdl The export to interrogate * @param[in] filepath The path within the export to check * @param[in] quota_type Whether we are checking inodes or blocks * @param[in] quota_id Id for which quota is set * @param[in] quota The values to set for the quota * @param[out] resquota New values set (optional) * * @return FSAL types. */ fsal_status_t (*set_quota)(struct fsal_export *exp_hdl, const char *filepath, int quota_type, int quota_id, fsal_quota_t *quota, fsal_quota_t *resquota); /**@}*/ /**@{*/ /** * pNFS functions */ /** * @brief Get list of available devices * * This function should populate calls @c cb @c values representing the * low quad of deviceids it wishes to make the available to the * caller. it should continue calling @c cb until @c cb returns false * or it runs out of deviceids to make available. If @c cb returns * false, it should assume that @c cb has not stored the most recent * deviceid and set @c res->cookie to a value that will begin witht he * most recently provided. * * If it wishes to return no deviceids, it may set @c res->eof to true * without calling @c cb at all. * * @param[in] exp_hdl Export handle * @param[in] type Type of layout to get devices for * @param[in] cb Function taking device ID halves * @param[in,out] res In/out and output arguments of the function * * @return Valid error codes in RFC 5661, pp. 365-6. */ nfsstat4(*getdevicelist)(struct fsal_export *exp_hdl, layouttype4 type, void *opaque, bool (*cb)(void *opaque, const uint64_t id), struct fsal_getdevicelist_res *res); /** * @brief Get layout types supported by export * * This function is the handler of the NFS4.1 FATTR4_FS_LAYOUT_TYPES file * attribute. (See RFC) * * @param[in] exp_hdl Filesystem to interrogate * @param[out] count Number of layout types in array * @param[out] types Static array of layout types that must not be * freed or modified and must not be dereferenced * after export reference is relinquished */ void (*fs_layouttypes)(struct fsal_export *exp_hdl, int32_t *count, const layouttype4 **types); /** * @brief Get layout block size for export * * This function is the handler of the NFS4.1 FATTR4_LAYOUT_BLKSIZE f-attribute. * * This is the preferred read/write block size. Clients are requested * (but don't have to) read and write in multiples. * * NOTE: The linux client only asks for this in blocks-layout, where this is the * filesystem wide block-size. (Minimum write size and alignment) * * @param[in] exp_hdl Filesystem to interrogate * * @return The preferred layout block size. */ uint32_t (*fs_layout_blocksize)(struct fsal_export *exp_hdl); /** * @brief Maximum number of segments we will use * * This function returns the maximum number of segments that will be * used to construct the response to any single layoutget call. Bear * in mind that current clients only support 1 segment. * * @param[in] exp_hdl Filesystem to interrogate * * @return The Maximum number of layout segments in a campound layoutget. */ uint32_t (*fs_maximum_segments)(struct fsal_export *exp_hdl); /** * @brief Size of the buffer needed for loc_body at layoutget * * This function sets policy for XDR buffer allocation in layoutget vector * below. If FSAL has a const size, just return it here. If it is dependent on * what the client can take return ~0UL. In any case the buffer allocated will * not be bigger than client's requested maximum. * * @param[in] exp_hdl Filesystem to interrogate * * @return Max size of the buffer needed for a loc_body */ size_t (*fs_loc_body_size)(struct fsal_export *exp_hdl); /** * @brief Get write verifier * * This function is called by write and commit to match the commit verifier * with the one returned on write. * * @param[in] exp_hdl Export to query * @param[in,out] verf_desc Address and length of verifier */ void (*get_write_verifier)(struct fsal_export *exp_hdl, struct gsh_buffdesc *verf_desc); /**@}*/ /** * @brief Allocate a state_t structure * * Note that this is not expected to fail since memory allocation is * expected to abort on failure. * * @param[in] exp_hdl Export state_t will be associated with * @param[in] state_type Type of state to allocate * @param[in] related_state Related state if appropriate * * @returns a state structure. */ struct state_t *(*alloc_state)(struct fsal_export *exp_hdl, enum state_type state_type, struct state_t *related_state); /** * @brief Free a state_t structure * * @param[in] exp_hdl Export state_t is associated with * @param[in] state state_t structure to free. * * @returns NULL on failure otherwise a state structure. */ void (*free_state)(struct fsal_export *exp_hdl, struct state_t *state); /** * @brief Check to see if a user is superuser * * @param[in] exp_hdl Export state_t is associated with * @param[in] creds Credentials to check for superuser * * @returns NULL on failure otherwise a state structure. */ bool (*is_superuser)(struct fsal_export *exp_hdl, const struct user_cred *creds); }; /** * @brief Filesystem operations */ typedef int (*claim_filesystem_cb)(struct fsal_filesystem *fs, struct fsal_export *exp); typedef void (*unclaim_filesystem_cb)(struct fsal_filesystem *fs); enum fsid_type { FSID_NO_TYPE, FSID_ONE_UINT64, FSID_MAJOR_64, FSID_TWO_UINT64, FSID_TWO_UINT32, FSID_DEVICE }; static inline uint64_t squash_fsid(const struct fsal_fsid__ *fsid) { return fsid->major ^ (fsid->minor << 32 | fsid->minor >> 32); } static inline int sizeof_fsid(enum fsid_type type) { switch (type) { case FSID_NO_TYPE: return 0; case FSID_ONE_UINT64: case FSID_MAJOR_64: return sizeof(uint64_t); case FSID_TWO_UINT64: return 2 * sizeof(uint64_t); case FSID_TWO_UINT32: case FSID_DEVICE: return 2 * sizeof(uint32_t); } return -1; } /** * @brief Directory cookie */ typedef uint64_t fsal_cookie_t; /* Cookie values 0, 1, and 2 are reserved by NFS: * 0 is "start from beginning" * 1 is the cookie associated with the "." entry * 2 is the cookie associated with the ".." entry * * FSALs that support compute_readdir_cookie that are for some reason unable * to compute the cookie for the very first entry (other than . and ..) * should return FIRST_COOKIE. Caching layers such as MDCACHE should treat an * insert of an entry with cookie 3 as inserting a new first entry, and then * compute a new cookie for the old first entry - they can safely assume the * sort order doesn't change which may allow for optimization of things like' * AVL trees. */ #define FIRST_COOKIE 3 enum fsal_dir_result { /** Continue readdir, call back with another dirent. */ DIR_CONTINUE, /** Continue supplying entries if readahead is supported, otherwise * stop providing entries. */ DIR_READAHEAD, /** Terminate readdir. */ DIR_TERMINATE, }; const char *fsal_dir_result_str(enum fsal_dir_result result); /** * @brief Callback to provide readdir caller with each directory entry * * The called function will indicate if readdir should continue, terminate, * terminate and mark cookie, or continue and mark cookie. In the last case, * the called function may also return a cookie if requested in the ret_cookie * parameter (which may be NULL if the caller doesn't need to mark cookies). * If ret_cookie is 0, the caller had no cookie to return. * * @param[in] name The name of the entry * @param[in] obj The fsal_obj_handle describing the entry * @param[in] attrs The requested attribues for the entry (see * readdir attrmask parameter) * @param[in] dir_state Opaque pointer to be passed to callback * @param[in] cookie An FSAL generated cookie for the entry * * @returns fsal_dir_result above */ typedef enum fsal_dir_result (*fsal_readdir_cb)( const char *name, struct fsal_obj_handle *obj, struct attrlist *attrs, void *dir_state, fsal_cookie_t cookie); /** * @brief FSAL object operations vector */ struct fsal_obj_ops { /**@{*/ /** * Lifecycle management */ /** * @brief Get a reference to a handle * * Refcounting is required for all FSALs. An FSAL that will have FSAL_MDCACHE * stacked on top need not handle this as FSAL_MDCACHE will handle it. * * @param[in] obj_hdl Handle to release */ void (*get_ref)(struct fsal_obj_handle *obj_hdl); /** * @brief Put a reference to a handle * * Refcounting is required for all FSALs. An FSAL that will have FSAL_MDCACHE * stacked on top need not handle this as FSAL_MDCACHE will handle it. * * @param[in] obj_hdl Handle to release */ void (*put_ref)(struct fsal_obj_handle *obj_hdl); /** * @brief Clean up a filehandle * * This function cleans up private resources associated with a * filehandle and deallocates it. Implement this method or you will * leak. Refcount (if used) should be 1 * * @param[in] obj_hdl Handle to release */ void (*release)(struct fsal_obj_handle *obj_hdl); /** * @brief Merge a duplicate handle with an original handle * * This function is used if an upper layer detects that a duplicate * object handle has been created. It allows the FSAL to merge anything * from the duplicate back into the original. * * The caller must release the object (the caller may have to close * files if the merge is unsuccessful). * * @param[in] orig_hdl Original handle * @param[in] dupe_hdl Handle to merge into original * * @return FSAL status. * */ fsal_status_t (*merge)(struct fsal_obj_handle *orig_hdl, struct fsal_obj_handle *dupe_hdl); /**@}*/ /**@{*/ /** * Directory operations */ /** * @brief Look up a filename * * This function looks up the given name in the supplied directory. * * @note The old version of the FSAL had a special case for this * function, such that if the directory handle and path were both * NULL, a handle to the root of the export was returned. This * special case is no longer supported and should not be implemented. * * The caller will set the request_mask in attrs_out to indicate the attributes * of interest. ATTR_ACL SHOULD NOT be requested and need not be provided. If * not all the requested attributes can be provided, this method MUST return * an error unless the ATTR_RDATTR_ERR bit was set in the request_mask. * * Since this method instantiates a new fsal_obj_handle, it will be forced * to fetch at least some attributes in order to even know what the object * type is (as well as it's fileid and fsid). For this reason, the operation * as a whole can be expected to fail if the attributes were not able to be * fetched. * * @param[in] dir_hdl Directory to search * @param[in] path Name to look up * @param[out] handle Object found * @param[in,out] attrs_out Optional attributes for newly created object * * @note On success, @a handle has been ref'd * * @return FSAL status. */ fsal_status_t (*lookup)(struct fsal_obj_handle *dir_hdl, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out); /** * @brief Read a directory * * This function reads directory entries from the FSAL and supplies * them to a callback. * * @param[in] dir_hdl Directory to read * @param[in] whence Point at which to start reading. NULL to * start at beginning. * @param[in] dir_state Opaque pointer to be passed to callback * @param[in] cb Callback to receive names * @param[in] attrmask Indicate which attributes the caller is interested in * @param[out] eof true if the last entry was reached * * @return FSAL status. */ fsal_status_t (*readdir)(struct fsal_obj_handle *dir_hdl, fsal_cookie_t *whence, void *dir_state, fsal_readdir_cb cb, attrmask_t attrmask, bool *eof); /** * @brief Compute the readdir cookie for a given filename. * * Some FSALs are able to compute the cookie for a filename deterministically * from the filename. They also have a defined order of entries in a directory * based on the name (could be strcmp sort, could be strict alpha sort, could * be deterministic order based on cookie - in any case, the dirent_cmp method * will also be provided. * * The returned cookie is the cookie that can be passed as whence to FIND that * directory entry. This is different than the cookie passed in the readdir * callback (which is the cookie of the NEXT entry). * * @param[in] parent Directory file name belongs to. * @param[in] name File name to produce the cookie for. * * @retval 0 if not supported. * @returns The cookie value. */ fsal_cookie_t (*compute_readdir_cookie)(struct fsal_obj_handle *parent, const char *name); /** * @brief Help sort dirents. * * For FSALs that are able to compute the cookie for a filename * deterministically from the filename, there must also be a defined order of * entries in a directory based on the name (could be strcmp sort, could be * strict alpha sort, could be deterministic order based on cookie). * * Although the cookies could be computed, the caller will already have them * and thus will provide them to save compute time. * * @param[in] parent Directory entries belong to. * @param[in] name1 File name of first dirent * @param[in] cookie1 Cookie of first dirent * @param[in] name2 File name of second dirent * @param[in] cookie2 Cookie of second dirent * * @retval < 0 if name1 sorts before name2 * @retval == 0 if name1 sorts the same as name2 * @retval >0 if name1 sorts after name2 */ int (*dirent_cmp)(struct fsal_obj_handle *parent, const char *name1, fsal_cookie_t cookie1, const char *name2, fsal_cookie_t cookie2); /**@}*/ /**@{*/ /** * Creation operations */ /** * @brief Create a directory * * This function creates a new directory. * * For support_ex, this method will handle attribute setting. The caller * MUST include the mode attribute and SHOULD NOT include the owner or * group attributes if they are the same as the op_ctx->cred. * * The caller is expected to invoke fsal_release_attrs to release any * resources held by the set attributes. The FSAL layer MAY have added an * inherited ACL. * * The caller will set the request_mask in attrs_out to indicate the attributes * of interest. ATTR_ACL SHOULD NOT be requested and need not be provided. If * not all the requested attributes can be provided, this method MUST return * an error unless the ATTR_RDATTR_ERR bit was set in the request_mask. * * Since this method instantiates a new fsal_obj_handle, it will be forced * to fetch at least some attributes in order to even know what the object * type is (as well as it's fileid and fsid). For this reason, the operation * as a whole can be expected to fail if the attributes were not able to be * fetched. * * @param[in] dir_hdl Directory in which to create the directory * @param[in] name Name of directory to create * @param[in] attrs_in Attributes to set on newly created object * @param[out] new_obj Newly created object * @param[in,out] attrs_out Optional attributes for newly created object * * @note On success, @a new_obj has been ref'd * * @return FSAL status. */ fsal_status_t (*mkdir)(struct fsal_obj_handle *dir_hdl, const char *name, struct attrlist *attrs_in, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out); /** * @brief Create a special file * * This function creates a new special file. * * For support_ex, this method will handle attribute setting. The caller * MUST include the mode attribute and SHOULD NOT include the owner or * group attributes if they are the same as the op_ctx->cred. * * If the node type has rawdev info, then @a attrs_in MUST have the rawdev field * set. * * The caller is expected to invoke fsal_release_attrs to release any * resources held by the set attributes. The FSAL layer MAY have added an * inherited ACL. * * The caller will set the request_mask in attrs_out to indicate the attributes * of interest. ATTR_ACL SHOULD NOT be requested and need not be provided. If * not all the requested attributes can be provided, this method MUST return * an error unless the ATTR_RDATTR_ERR bit was set in the request_mask. * * Since this method instantiates a new fsal_obj_handle, it will be forced * to fetch at least some attributes in order to even know what the object * type is (as well as it's fileid and fsid). For this reason, the operation * as a whole can be expected to fail if the attributes were not able to be * fetched. * * @param[in] dir_hdl Directory in which to create the object * @param[in] name Name of object to create * @param[in] nodetype Type of special file to create * @param[in] attrs_in Attributes to set on newly created object * @param[out] new_obj Newly created object * @param[in,out] attrs_out Optional attributes for newly created object * * @note On success, @a new_obj has been ref'd * * @return FSAL status. */ fsal_status_t (*mknode)(struct fsal_obj_handle *dir_hdl, const char *name, object_file_type_t nodetype, struct attrlist *attrs_in, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out); /** * @brief Create a symbolic link * * This function creates a new symbolic link. * * For support_ex, this method will handle attribute setting. The caller * MUST include the mode attribute and SHOULD NOT include the owner or * group attributes if they are the same as the op_ctx->cred. * * The caller is expected to invoke fsal_release_attrs to release any * resources held by the set attributes. The FSAL layer MAY have added an * inherited ACL. * * The caller will set the request_mask in attrs_out to indicate the attributes * of interest. ATTR_ACL SHOULD NOT be requested and need not be provided. If * not all the requested attributes can be provided, this method MUST return * an error unless the ATTR_RDATTR_ERR bit was set in the request_mask. * * Since this method instantiates a new fsal_obj_handle, it will be forced * to fetch at least some attributes in order to even know what the object * type is (as well as it's fileid and fsid). For this reason, the operation * as a whole can be expected to fail if the attributes were not able to be * fetched. * * @param[in] dir_hdl Directory in which to create the object * @param[in] name Name of object to create * @param[in] link_path Content of symbolic link * @param[in] attrs_in Attributes to set on newly created object * @param[out] new_obj Newly created object * @param[in,out] attrs_out Optional attributes for newly created object * * @note On success, @a new_obj has been ref'd * * @return FSAL status. */ fsal_status_t (*symlink)(struct fsal_obj_handle *dir_hdl, const char *name, const char *link_path, struct attrlist *attrs_in, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out); /**@}*/ /**@{*/ /** * File object operations */ /** * @brief Read the content of a link * * This function reads the content of a symbolic link. The FSAL will * allocate a buffer and store its address and the link length in the * link_content gsh_buffdesc. The caller *must* free this buffer with * gsh_free. * * The symlink content passed back *must* be null terminated and the * length indicated in the buffer description *must* include the * terminator. * * @param[in] obj_hdl Link to read * @param[out] link_content Buffdesc to which the FSAL will store * the address of the buffer holding the * link and the link length. * @param[out] refresh true if the content are to be retrieved * from the underlying filesystem rather * than cache * * @return FSAL status. */ fsal_status_t (*readlink)(struct fsal_obj_handle *obj_hdl, struct gsh_buffdesc *link_content, bool refresh); /** * @brief Check access for a given user against a given object * * This function checks whether a given user is allowed to perform the * specified operations against the supplied file. The goal is to * allow filesystem specific semantics to be applied to cached * metadata. * * This method must read attributes and/or get them from a cache. * * @param[in] obj_hdl Handle to check * @param[in] access_type Access requested * @param[out] allowed Returned access that could be granted * @param[out] denied Returned access that would be granted * @param[in] owner_skip Skip test if op_ctx->creds is owner * * @return FSAL status. */ fsal_status_t (*test_access)(struct fsal_obj_handle *obj_hdl, fsal_accessflags_t access_type, fsal_accessflags_t *allowed, fsal_accessflags_t *denied, bool owner_skip); /** * @brief Get attributes * * This function fetches the attributes for the object. The attributes * requested in the mask are copied out (though other attributes might * be copied out). * * The caller will set the request_mask in attrs_out to indicate the attributes * of interest. ATTR_ACL SHOULD NOT be requested and need not be provided. If * not all the requested attributes can be provided, this method MUST return * an error unless the ATTR_RDATTR_ERR bit was set in the request_mask. * * The caller MUST call fsal_release_attrs when done with the copied * out attributes. This will release any attributes that might take * additional memory. * * @param[in] obj_hdl Object to query * @param[out] attrs_out Attribute list for file * * @return FSAL status. */ fsal_status_t (*getattrs)(struct fsal_obj_handle *obj_hdl, struct attrlist *attrs_out); /** * @brief Create a new link * * This function creates a new name for an existing object. * * @param[in] obj_hdl Object to be linked to * @param[in] destdir_hdl Directory in which to create the link * @param[in] name Name for link * * @return FSAL status */ fsal_status_t (*link)(struct fsal_obj_handle *obj_hdl, struct fsal_obj_handle *destdir_hdl, const char *name); /** * @brief get fs_locations * * This function returns the fs locations for an object. * * @param[in] obj_hdl Object to get fs locations for * @param[out] fs_locations4 fs locations * * @return FSAL status */ fsal_status_t (*fs_locations)(struct fsal_obj_handle *obj_hdl, struct fs_locations4 *fs_locs); /** * @brief Rename a file * * This function renames a file (technically it changes the name of * one link, which may be the only link to the file.) * * @param[in] olddir_hdl Old parent directory * @param[in] old_name Old name * @param[in] newdir_hdl New parent directory * @param[in] new_name New name * * @return FSAL status */ fsal_status_t (*rename)(struct fsal_obj_handle *obj_hdl, struct fsal_obj_handle *olddir_hdl, const char *old_name, struct fsal_obj_handle *newdir_hdl, const char *new_name); /** * @brief Remove a name from a directory * * This function removes a name from a directory and possibly deletes * the file so named. * * @param[in] dir_hdl The directory from which to remove the name * @param[in] obj_hdl The object being removed * @param[in] name The name to remove * * @return FSAL status. */ fsal_status_t (*unlink)(struct fsal_obj_handle *dir_hdl, struct fsal_obj_handle *obj_hdl, const char *name); /**@}*/ /**@{*/ /** * I/O management */ /** * @brief Seek to data or hole * * This function seek to data or hole in a file. * * @param[in] obj_hdl File to be written * @param[in,out] info Information about the data * * @return FSAL status. */ fsal_status_t (*seek)(struct fsal_obj_handle *obj_hdl, struct io_info *info); /** * @brief IO Advise * * This function give hints to fs. * * @param[in] obj_hdl File to be written * @param[in,out] info Information about the data * * @return FSAL status. */ fsal_status_t (*io_advise)(struct fsal_obj_handle *obj_hdl, struct io_hints *hints); /** * @brief Close a file * * This function closes a file. It is protected by the Cache inode * content lock. This should return ERR_FSAL_NOT_OPENED if the global FD for * this obj was not open. * * @param[in] obj_hdl File to close * * @return FSAL status. */ fsal_status_t (*close)(struct fsal_obj_handle *obj_hdl); /**@}*/ /**@{*/ /** * Extended attribute management */ /** * @brief List extended attributes on a file * * This function gets a list of attributes on a given file. * * @param[in] obj_hdl File to interrogate * @param[in] cookie Attribute at which to start * @param[out] xattrs_tab Array to which to write attributes * @param[in] xattrs_tabsize Size of array * @param[out] nb_returned Number of entries returned * * @return FSAL status. */ fsal_status_t (*list_ext_attrs)(struct fsal_obj_handle *obj_hdl, unsigned int cookie, struct fsal_xattrent *xattrs_tab, unsigned int xattrs_tabsize, unsigned int *nb_returned, int *end_of_list); /** * @brief Get a number for an attribute name * * This function returns an index for a given attribute specified by * name. * * @param[in] obj_hdl File to look up * @param[in] name Name to look up * @param[out] xattr_id Number uniquely identifying the attribute * within the scope of the file * * @return FSAL status. */ fsal_status_t (*getextattr_id_by_name)(struct fsal_obj_handle *obj_hdl, const char *xattr_name, unsigned int *xattr_id); /** * @brief Get content of an attribute by name * * This function returns the value of an extended attribute as * specified by name. * * As a special rule, because it is implemented that way in the linux * getxattr call, giving a buffer_size of 0 is allowed and should set * output_size appropriately to fit the xattr. * * Please note that the xattr could change between the query-size call * and that actual fetch, so this is not fail-proof. * * @param[in] obj_hdl File to interrogate * @param[in] xattr_name Name of attribute * @param[out] buffer_addr Buffer to store content * @param[in] buffer_size Buffer size * @param[out] output_size Size of content * * @return FSAL status. */ fsal_status_t (*getextattr_value_by_name)(struct fsal_obj_handle * obj_hdl, const char *xattr_name, caddr_t buffer_addr, size_t buffer_size, size_t *output_size); /** * @brief Get content of an attribute by id * * This function returns the value of an extended attribute as * specified by id. * * @param[in] obj_hdl File to interrogate * @param[in] xattr_id ID of attribute * @param[out] buffer_addr Buffer to store content * @param[in] buffer_size Buffer size * @param[out] output_size Size of content * * @return FSAL status. */ fsal_status_t (*getextattr_value_by_id)(struct fsal_obj_handle * obj_hdl, unsigned int xattr_id, caddr_t buffer_addr, size_t buffer_size, size_t *output_size); /** * @brief Set content of an attribute * * This function sets the value of an extended attribute. * * @param[in] obj_hdl File to modify * @param[in] xattr_name Name of attribute * @param[in] buffer_addr Content to set * @param[in] buffer_size Content size * @param[in] create true if attribute is to be created * * @return FSAL status. */ fsal_status_t (*setextattr_value)(struct fsal_obj_handle *obj_hdl, const char *xattr_name, caddr_t buffer_addr, size_t buffer_size, int create); /** * @brief Set content of an attribute by id * * This function sets the value of an extended attribute by id. * * @param[in] obj_hdl File to modify * @param[in] xattr_id ID of attribute * @param[in] buffer_addr Content to set * @param[in] buffer_size Content size * * @return FSAL status. */ fsal_status_t (*setextattr_value_by_id)(struct fsal_obj_handle * obj_hdl, unsigned int xattr_id, caddr_t buffer_addr, size_t buffer_size); /** * @brief Remove an extended attribute by id * * This function removes an extended attribute as specified by ID. * * @param[in] obj_hdl File to modify * @param[in] xattr_id ID of attribute * * @return FSAL status. */ fsal_status_t (*remove_extattr_by_id)(struct fsal_obj_handle *obj_hdl, unsigned int xattr_id); /** * @brief Remove an extended attribute by name * * This function removes an extended attribute as specified by name. * * @param[in] obj_hdl File to modify * @param[in] xattr_name Name of attribute to remove * * @return FSAL status. */ fsal_status_t (*remove_extattr_by_name)(struct fsal_obj_handle * obj_hdl, const char *xattr_name); /**@}*/ /**@{*/ /** * Handle operations */ /** * @brief Test handle type * * This function tests that a handle is of the specified type. * * @retval true if it is. * @retval false if it isn't. */ bool (*handle_is)(struct fsal_obj_handle *obj_hdl, object_file_type_t type); /** * @brief Write wire handle * * This function writes a "wire" handle or file ID to the given * buffer. * * @param[in] obj_hdl The handle to digest * @param[in] output_type The type of digest to write * @param[in,out] fh_desc Buffer descriptor to which to write * digest. Set fh_desc->len to final * output length. * * @return FSAL status */ fsal_status_t (*handle_to_wire)(const struct fsal_obj_handle *obj_hdl, fsal_digesttype_t output_type, struct gsh_buffdesc *fh_desc); /** * @brief Get key for handle * * Indicate the unique part of the handle that should be used for * hashing. * * @param[in] obj_hdl Handle whose key is to be got * @param[out] fh_desc Address and length giving sub-region of handle * to be used as key */ void (*handle_to_key)(struct fsal_obj_handle *obj_hdl, struct gsh_buffdesc *fh_desc); /** * @brief Compare two handles * * This function compares two handles to see if they reference the same file * * @param[in] obj_hdl1 The first handle to compare * @param[in] obj_hdl2 The second handle to compare * * @return True if match, false otherwise */ bool (*handle_cmp)(struct fsal_obj_handle *obj_hdl1, struct fsal_obj_handle *obj_hdl2); /**@}*/ /**@{*/ /** * pNFS functions */ /** * @brief Grant a layout segment. * * This function is called by nfs41_op_layoutget. It may be called * multiple times, to satisfy a request with multiple segments. The * FSAL may track state (what portion of the request has been or * remains to be satisfied or any other information it wishes) in the * bookkeeper member of res. Each segment may have FSAL-specific * information associated with it its segid. This segid will be * supplied to the FSAL when the segment is committed or returned. * When the granting the last segment it intends to grant, the FSAL * must set the last_segment flag in res. * * @param[in] obj_hdl The handle of the file on which the layout is * requested. * @param[in] req_ctx Request context * @param[out] loc_body An XDR stream to which the FSAL must encode * the layout specific portion of the granted * layout segment. * @param[in] arg Input arguments of the function * @param[in,out] res In/out and output arguments of the function * * @return Valid error codes in RFC 5661, pp. 366-7. */ nfsstat4(*layoutget)(struct fsal_obj_handle *obj_hdl, struct req_op_context *req_ctx, XDR * loc_body, const struct fsal_layoutget_arg *arg, struct fsal_layoutget_res *res); /** * @brief Potentially return one layout segment * * This function is called once on each segment matching the IO mode * and intersecting the range specified in a LAYOUTRETURN operation or * for all layouts corresponding to a given stateid on last close, * leas expiry, or a layoutreturn with a return-type of FSID or ALL. * Whther it is called in the former or latter case is indicated by * the synthetic flag in the arg structure, with synthetic being true * in the case of last-close or lease expiry. * * If arg->dispose is true, all resources associated with the * layout must be freed. * * @param[in] obj_hdl The object on which a segment is to be returned * @param[in] req_ctx Request context * @param[in] lrf_body In the case of a non-synthetic return, this is * an XDR stream corresponding to the layout * type-specific argument to LAYOUTRETURN. In * the case of a synthetic or bulk return, * this is a NULL pointer. * @param[in] arg Input arguments of the function * * @return Valid error codes in RFC 5661, p. 367. */ nfsstat4(*layoutreturn)(struct fsal_obj_handle *obj_hdl, struct req_op_context *req_ctx, XDR * lrf_body, const struct fsal_layoutreturn_arg *arg); /** * @brief Commit a segment of a layout * * This function is called once on every segment of a layout. The * FSAL may avoid being called again after it has finished all tasks * necessary for the commit by setting res->commit_done to true. * * The calling function does not inspect or act on the value of * size_supplied or new_size until after the last call to * FSAL_layoutcommit. * * @param[in] obj_hdl The object on which to commit * @param[in] req_ctx Request context * @param[in] lou_body An XDR stream containing the layout * type-specific portion of the LAYOUTCOMMIT * arguments. * @param[in] arg Input arguments of the function * @param[in,out] res In/out and output arguments of the function * * @return Valid error codes in RFC 5661, p. 366. */ nfsstat4(*layoutcommit)(struct fsal_obj_handle *obj_hdl, struct req_op_context *req_ctx, XDR * lou_body, const struct fsal_layoutcommit_arg *arg, struct fsal_layoutcommit_res *res); /** * @brief Get Extended Attribute * * This function gets an extended attribute of an object. * * @param[in] obj_hdl Input object to query * @param[in] xa_name Input xattr name * @param[out] xa_value Output xattr value * * @return FSAL status. */ fsal_status_t (*getxattrs)(struct fsal_obj_handle *obj_hdl, xattrname4 *xa_name, xattrvalue4 *xa_value); /** * @brief Set Extended Attribute * * This function sets an extended attribute of an object. * * @param[in] obj_hdl Input object to set * @param[in] xa_type Input xattr type * @param[in] xa_name Input xattr name to set * @param[in] xa_value Input xattr value to set * * @return FSAL status. */ fsal_status_t (*setxattrs)(struct fsal_obj_handle *obj_hdl, setxattr_type4 sa_type, xattrname4 *xa_name, xattrvalue4 *xa_value); /** * @brief Remove Extended Attribute * * This function remove an extended attribute of an object. * * @param[in] obj_hdl Input object to set * @param[in] xa_name Input xattr name to remove * * @return FSAL status. */ fsal_status_t (*removexattrs)(struct fsal_obj_handle *obj_hdl, xattrname4 *xa_name); /** * @brief List Extended Attributes * * This function list the extended attributes of an object. * * @param[in] obj_hdl Input object to list * @param[in] la_maxcount Input maximum number of bytes for names * @param[in,out] la_cookie In/out cookie * @param[in,out] la_cookieverf In/out cookie verifier * @param[out] lr_eof Output eof set if no more extended attributes * @param[out] lr_names Output list of extended attribute names * this buffer size is double the size of * la_maxcount to allow for component4 overhead * * @return FSAL status. */ fsal_status_t (*listxattrs)(struct fsal_obj_handle *obj_hdl, count4 la_maxcount, nfs_cookie4 *la_cookie, verifier4 *la_cookieverf, bool_t *lr_eof, xattrlist4 * lr_names); /**@}*/ /**@{*/ /** * Extended API functions. * * With these new operations, the FSAL becomes responsible for managing * share reservations. The FSAL is also granted more control over the * state of a "file descriptor" and has more control of what a "file * descriptor" even is. Ultimately, it is whatever the FSAL needs in * order to manage the share reservations and lock state. * * The open2 method also allows atomic create/setattr/open (just like the * NFS v4 OPEN operation). * */ /** * @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. * * The caller is expected to invoke fsal_release_attrs to release any * resources held by the set attributes. The FSAL layer MAY have added an * inherited ACL. * * The caller will set the request_mask in attrs_out to indicate the attributes * of interest. ATTR_ACL SHOULD NOT be requested and need not be provided. If * not all the requested attributes can be provided, this method MUST return * an error unless the ATTR_RDATTR_ERR bit was set in the request_mask. * * Since this method may instantiate a new fsal_obj_handle, it will be forced * to fetch at least some attributes in order to even know what the object * type is (as well as it's fileid and fsid). For this reason, the operation * as a whole can be expected to fail if the attributes were not able to be * fetched. * * The attributes will not be returned if this is an open by object as * opposed to an open by name. * * @note If the file was created, @a new_obj has been ref'd * * @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] attrs_in 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 Optional attributes for newly created object * @param[in,out] caller_perm_check The caller must do a permission check * * @return FSAL status. */ fsal_status_t (*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 *attrs_in, fsal_verifier_t verifier, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out, bool *caller_perm_check); /** * @brief Check the exclusive create verifier for a file. * * @param[in] obj_hdl File to check verifier * @param[in] verifier Verifier to use for exclusive create * * @retval true if verifier matches */ bool (*check_verifier)(struct fsal_obj_handle *obj_hdl, fsal_verifier_t verifier); /** * @brief Return open status of a state. * * This function returns open flags representing the current open * status for a state. The state_lock must be held. * * @param[in] obj_hdl File owning state * @param[in] state File state to interrogate * * @retval Flags representing current open status */ fsal_openflags_t (*status2)(struct fsal_obj_handle *obj_hdl, struct state_t *state); /** * @brief Re-open a file that may be already opened * * This function supports changing the access mode of a share reservation and * thus should only be called with a share state. The state_lock must be held. * * This MAY be used to open a file the first time if there is no need for * open by name or create semantics. One example would be 9P lopen. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * @param[in] openflags Mode for re-open * * @return FSAL status. */ fsal_status_t (*reopen2)(struct fsal_obj_handle *obj_hdl, struct state_t *state, fsal_openflags_t openflags); /** * @brief Read data from a file * * This function reads data from the given file. The FSAL must be able to * perform the read whether a state is presented or not. This function also * is expected to handle properly bypassing or not share reservations. * * @param[in] obj_hdl File on which to operate * @param[in] bypass If state doesn't indicate a share reservation, * bypass any deny read * @param[in] state state_t to use for this operation * @param[in] offset Position from which to read * @param[in] buffer_size Amount of data to read * @param[out] buffer Buffer to which data are to be copied * @param[out] read_amount Amount of data read * @param[out] end_of_file true if the end of file has been reached * @param[in,out] info more information about the data * * @return FSAL status. */ fsal_status_t (*read2)(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buffer_size, void *buffer, size_t *read_amount, bool *end_of_file, struct io_info *info); /** * @brief Write data to a file * * This function writes data to a file. The FSAL must be able to * perform the write whether a state is presented or not. This function also * is expected to handle properly bypassing or not share reservations. Even * with bypass == true, it will enforce a mandatory (NFSv4) deny_write if * an appropriate state is not passed). * * The FSAL is expected to enforce sync if necessary. * * @param[in] obj_hdl File on which to operate * @param[in] bypass If state doesn't indicate a share reservation, * bypass any non-mandatory deny write * @param[in] state state_t to use for this operation * @param[in] offset Position at which to write * @param[in] buffer_size Amount of data to write * @param[in] buffer Data to be written * @param[in,out] fsal_stable In, if on, the fsal is requested to write data * to stable store. Out, the fsal reports what * it did. * @param[in,out] info more information about the data * * @return FSAL status. */ fsal_status_t (*write2)(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, uint64_t offset, size_t buffer_size, void *buffer, size_t *wrote_amount, bool *fsal_stable, struct io_info *info); /** * @brief Seek to data or hole * * This function seek to data or hole in a file. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * @param[in,out] info Information about the data * * @return FSAL status. */ fsal_status_t (*seek2)(struct fsal_obj_handle *obj_hdl, struct state_t *state, struct io_info *info); /** * @brief IO Advise * * This function give hints to fs. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * @param[in,out] info Information about the data * * @return FSAL status. */ fsal_status_t (*io_advise2)(struct fsal_obj_handle *obj_hdl, struct state_t *state, struct io_hints *hints); /** * @brief Commit written data * * This function flushes possibly buffered data to a file. This method * differs from commit due to the need to interact with share reservations * and the fact that the FSAL manages the state of "file descriptors". The * FSAL must be able to perform this operation without being passed a specific * state. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * @param[in] offset Start of range to commit * @param[in] len Length of range to commit * * @return FSAL status. */ fsal_status_t (*commit2)(struct fsal_obj_handle *obj_hdl, off_t offset, size_t len); /** * @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". * * @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 (*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); /** * @brief Acquire or Release delegation * * This functions acquires/releases delegation/lease_lock. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * @param[in] owner Opaque state owner token * @param[in] deleg Requested delegation state * * @return FSAL status. */ fsal_status_t (*lease_op2)(struct fsal_obj_handle *obj_hdl, struct state_t *state, void *owner, fsal_deleg_t deleg); /** * @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. * * The caller is expected to invoke fsal_release_attrs to release any * resources held by the set attributes. The FSAL layer MAY have added an * inherited ACL. * * @param[in] obj_hdl File on which to operate * @param[in] bypass If state doesn't indicate a share reservation, * bypass any non-mandatory deny write * @param[in] state state_t to use for this operation * @param[in] attrib_set Attributes to set * * @return FSAL status. */ fsal_status_t (*setattr2)(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, struct attrlist *attrib_set); /** * @brief Manage closing a file when a state is no longer needed. * * When the upper layers are ready to dispense with a state, this method is * called to allow the FSAL to close any file descriptors or release any other * resources associated with the state. A call to free_state should be assumed * to follow soon. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * * @return FSAL status. */ fsal_status_t (*close2)(struct fsal_obj_handle *obj_hdl, struct state_t *state); /**@}*/ }; /** * @brief FSAL pNFS Data Server operations vector */ struct fsal_pnfs_ds_ops { /**@{*/ /** * Lifecycle management. */ /** * @brief Clean up a server * * This function cleans up private resources associated with a * server and deallocates it. A default is supplied. * * This function should not be called directly. * * @param[in] pds FSAL pNFS DS to release */ void (*release)(struct fsal_pnfs_ds *const pds); /** * @brief Initialize FSAL specific permissions per pNFS DS * * @param[in] pds FSAL pNFS DS * @param[in] req Incoming request. * * @return NFSv4.1 error codes: * NFS4_OK, NFS4ERR_ACCESS, NFS4ERR_WRONGSEC. */ nfsstat4(*permissions)(struct fsal_pnfs_ds *const pds, struct svc_req *req); /**@}*/ /**@{*/ /** * @brief Create a FSAL data server handle from a wire handle * * This function creates a FSAL data server handle from a client * supplied "wire" handle. * * @param[in] pds FSAL pNFS DS * @param[in] hdl_desc Buffer from which to create the struct * @param[out] handle FSAL DS handle * * @return NFSv4.1 error codes. */ nfsstat4(*make_ds_handle)(struct fsal_pnfs_ds *const pds, const struct gsh_buffdesc * const hdl_desc, struct fsal_ds_handle **const handle, int flags); /** * @brief Initialize FSAL specific values for DS handle * * @param[in] ops FSAL DS handle operations vector */ void (*fsal_dsh_ops)(struct fsal_dsh_ops *ops); /**@}*/ }; /** * @brief FSAL DS handle operations vector */ struct fsal_dsh_ops { /**@{*/ /** * Lifecycle management. */ /** * @brief Clean up a DS handle * * This function cleans up private resources associated with a * filehandle and deallocates it. Implement this method or you will * leak. This function should not be called directly. * * @param[in] ds_hdl Handle to release */ void (*release)(struct fsal_ds_handle *const ds_hdl); /**@}*/ /**@{*/ /** * I/O Functions */ /** * @brief Read from a data-server handle. * * NFSv4.1 data server handles are disjount from normal * filehandles (in Ganesha, there is a ds_flag in the filehandle_v4_t * structure) and do not get loaded into cache_inode or processed the * normal way. * * @param[in] ds_hdl FSAL DS handle * @param[in] req_ctx Credentials * @param[in] stateid The stateid supplied with the READ operation, * for validation * @param[in] offset The offset at which to read * @param[in] requested_length Length of read requested (and size of buffer) * @param[out] buffer The buffer to which to store read data * @param[out] supplied_length Length of data read * @param[out] eof true on end of file * * @return An NFSv4.1 status code. */ nfsstat4(*read)(struct fsal_ds_handle *const ds_hdl, struct req_op_context *const req_ctx, const stateid4 * stateid, const offset4 offset, const count4 requested_length, void *const buffer, count4 * const supplied_length, bool *const end_of_file); /** * @brief Read plus from a data-server handle. * * NFSv4.2 data server handles are disjount from normal * filehandles (in Ganesha, there is a ds_flag in the filehandle_v4_t * structure) and do not get loaded into cache_inode or processed the * normal way. * * @param[in] ds_hdl FSAL DS handle * @param[in] req_ctx Credentials * @param[in] stateid The stateid supplied with the READ operation, * for validation * @param[in] offset The offset at which to read * @param[in] requested_length Length of read requested (and size of buffer) * @param[out] buffer The buffer to which to store read data * @param[out] supplied_length Length of data read * @param[out] eof true on end of file * @param[out] info IO info * * @return An NFSv4.2 status code. */ nfsstat4(*read_plus)(struct fsal_ds_handle *const ds_hdl, struct req_op_context *const req_ctx, const stateid4 * stateid, const offset4 offset, const count4 requested_length, void *const buffer, const count4 supplied_length, bool *const end_of_file, struct io_info *info); /** * * @brief Write to a data-server handle. * * NFSv4.1 data server filehandles are disjount from normal * filehandles (in Ganesha, there is a ds_flag in the filehandle_v4_t * structure) and do not get loaded into cache_inode or processed the * normal way. * * @param[in] ds_hdl FSAL DS handle * @param[in] req_ctx Credentials * @param[in] stateid The stateid supplied with the READ operation, * for validation * @param[in] offset The offset at which to read * @param[in] write_length Length of write requested (and size of buffer) * @param[out] buffer The buffer to which to store read data * @param[in] stability wanted Stability of write * @param[out] written_length Length of data written * @param[out] writeverf Write verifier * @param[out] stability_got Stability used for write (must be as * or more stable than request) * * @return An NFSv4.1 status code. */ nfsstat4(*write)(struct fsal_ds_handle *const ds_hdl, struct req_op_context *const req_ctx, const stateid4 * stateid, const offset4 offset, const count4 write_length, const void *buffer, const stable_how4 stability_wanted, count4 * const written_length, verifier4 * const writeverf, stable_how4 * const stability_got); /** * * @brief Write plus to a data-server handle. * * NFSv4.2 data server filehandles are disjount from normal * filehandles (in Ganesha, there is a ds_flag in the filehandle_v4_t * structure) and do not get loaded into cache_inode or processed the * normal way. * * @param[in] ds_hdl FSAL DS handle * @param[in] req_ctx Credentials * @param[in] stateid The stateid supplied with the READ operation, * for validation * @param[in] offset The offset at which to read * @param[in] write_length Length of write requested (and size of buffer) * @param[out] buffer The buffer to which to store read data * @param[in] stability wanted Stability of write * @param[out] written_length Length of data written * @param[out] writeverf Write verifier * @param[out] stability_got Stability used for write (must be as * or more stable than request) * @param[in/out] info IO info * * @return An NFSv4.2 status code. */ nfsstat4(*write_plus)(struct fsal_ds_handle *const ds_hdl, struct req_op_context *const req_ctx, const stateid4 * stateid, const offset4 offset, const count4 write_length, const void *buffer, const stable_how4 stability_wanted, count4 * const written_length, verifier4 * const writeverf, stable_how4 * const stability_got, struct io_info *info); /** * @brief Commit a byte range to a DS handle. * * NFSv4.1 data server filehandles are disjount from normal * filehandles (in Ganesha, there is a ds_flag in the filehandle_v4_t * structure) and do not get loaded into cache_inode or processed the * normal way. * * @param[in] ds_hdl FSAL DS handle * @param[in] req_ctx Credentials * @param[in] offset Start of commit window * @param[in] count Length of commit window * @param[out] writeverf Write verifier * * @return An NFSv4.1 status code. */ nfsstat4(*commit)(struct fsal_ds_handle *const ds_hdl, struct req_op_context *const req_ctx, const offset4 offset, const count4 count, verifier4 * const writeverf); /**@}*/ }; /** * @brief FSAL object definition * * This structure is the base FSAL instance definition, providing the * public face to a single, loaded FSAL. */ struct fsal_module { struct glist_head fsals; /*< link in list of loaded fsals */ struct glist_head exports; /*< Head of list of exports from this FSAL */ struct glist_head handles; /*< Head of list of object handles */ struct glist_head servers; /*< Head of list of Data Servers */ char *path; /*< Path to .so file */ char *name; /*< Name set from .so and/or config */ void *dl_handle; /*< Handle to the dlopen()d shared library. NULL if statically linked */ struct fsal_ops m_ops; /*< FSAL module methods vector */ pthread_rwlock_t lock; /*< Lock to be held when manipulating its lists (above). */ int32_t refcount; /*< Reference count */ struct fsal_stats *stats; /*< for storing the FSAL specific stats */ }; /** * @brief Get a reference to a module * * @param[in] fsal_hdl FSAL on which to acquire reference. */ static inline void fsal_get(struct fsal_module *fsal_hdl) { (void) atomic_inc_int32_t(&fsal_hdl->refcount); assert(fsal_hdl->refcount > 0); } /** * @brief Relinquish a reference to the module * * This function relinquishes one reference to the FSAL. After the * reference count falls to zero, the FSAL may be freed and unloaded. * * @param[in] fsal_hdl FSAL on which to release reference. */ static inline void fsal_put(struct fsal_module *fsal_hdl) { int32_t refcount; refcount = atomic_dec_int32_t (&fsal_hdl->refcount); assert(refcount >= 0); if (refcount == 0) { LogInfo(COMPONENT_FSAL, "FSAL %s now unused", fsal_hdl->name); } } /** * @brief Export object * * This structure is created by the @c create_export method on the * FSAL module. It is stored as part of the export list and is used * to manage individual exports, interrogate properties of the * filesystem, and create individual file handle objects. */ struct fsal_export { struct glist_head exports; /*< Link in list of exports from the same FSAL. */ struct fsal_module *fsal; /*< Link back to the FSAL module */ const struct fsal_up_vector *up_ops; /*< Upcall operations */ struct export_ops exp_ops; /*< Vector of operations */ struct fsal_export *sub_export; /*< Sub export for stacking */ struct fsal_export *super_export;/*< Super export for stacking */ uint16_t export_id; /*< Export ID copied from gsh_export, initialized by fsal_export_init */ }; /** * @brief Public structure for filesystem descriptions * * This stucture is provided along with a general interface to support those * FSALs that map into a traditional file system model. Note that * fsal_obj_handles do not link to an fsal_filesystem, that linkage is reserved * for FSAL's private obj handle if appropriate. * */ struct fsal_filesystem { struct glist_head filesystems; /*< List of file systems */ struct glist_head children; /*< Child file systems */ struct glist_head siblings; /*< Entry in list of parent's child file systems */ struct fsal_filesystem *parent; /*< Parent file system */ struct fsal_module *fsal; /*< Link back to fsal module */ void *private_data; /*< Private data for owning FSAL */ char *path; /*< Path to root of this file system */ char *device; /*< Path to block device */ char *type; /*< fs type */ unclaim_filesystem_cb unclaim; /*< Call back to unclaim this fs */ uint32_t pathlen; /*< Length of path */ uint32_t namelen; /*< Name length from statfs */ struct avltree_node avl_fsid; /*< AVL indexed by fsid */ struct avltree_node avl_dev; /*< AVL indexed by dev */ struct fsal_fsid__ fsid; /*< file system id */ fsal_dev_t dev; /*< device filesystem is on */ enum fsid_type fsid_type; /*< type of fsid present */ bool in_fsid_avl; /*< true if inserted in fsid avl */ bool in_dev_avl; /*< true if inserted in dev avl */ bool exported; /*< true if explicitly exported */ }; /** * @brief Public structure for filesystem objects * * This structure is used for files of all types including directories * and anything else that can be operated on via NFS. * * All functions that create a a new object handle should allocate * memory for the complete (public and private) handle and perform any * private initialization. They should fill the * @c fsal_obj_handle::attributes structure. They should also call the * @c fsal_obj_handle_init function with the public object handle, * object handle operations vector, public export, and file type. * * @note Do we actually need a lock and ref count on the fsal object * handle, since cache_inode is managing life cycle and concurrency? * That is, do we expect fsal_obj_handle to have a reference count * that would be separate from that managed by cache_inode_lru? */ struct fsal_obj_handle { struct glist_head handles; /*< Link in list of handles under the same FSAL. */ struct fsal_filesystem *fs; /*< Owning filesystem */ struct fsal_module *fsal; /*< Link back to fsal module */ struct fsal_obj_ops obj_ops; /*< Operations vector */ pthread_rwlock_t obj_lock; /*< Lock on handle */ /** Pointer to the cached attributes. * * This pointer should be set by the fsal when the handle is created. * Its value should not be changed once initialized. The release * of this field is also done by the FSAL. * * Typically the attributes are part of the FSAL's private handle, * however, some FSALs, particularly stackable FSALs may point to * some other field (for example, the underlying FSAL's attributes * in the case of FSAL_NULL). */ /* Static attributes */ object_file_type_t type; /*< Object file type */ fsal_fsid_t fsid; /*< Filesystem on which this object is stored */ uint64_t fileid; /*< Unique identifier for this object within the scope of the fsid, (e.g. inode number) */ struct state_hdl *state_hdl; /*< State related to this handle */ }; /** * @brief Public structure for pNFS Data Servers * * This structure is used for files of all types including directories * and anything else that can be operated on via NFS. Having an * independent reference count and lock here makes sense, since there * is no caching infrastructure overlaying this system. * */ enum pnfs_ds_status { PNFS_DS_READY, /*< searchable, usable */ PNFS_DS_STALE, /*< is no longer valid */ }; /** * @brief PNFS Data Server * * This represents a Data Server for PNFS. It may be stand-alone, or may be * associated with an export (which represents an MDS). */ struct fsal_pnfs_ds { struct glist_head ds_list; /**< Entry in list of all DSs */ struct glist_head server; /**< Link in list of Data Servers under the same FSAL. */ struct glist_head ds_handles; /**< Head of list of DS handles */ struct fsal_module *fsal; /**< Link back to fsal module */ struct fsal_pnfs_ds_ops s_ops; /**< Operations vector */ struct gsh_export *mds_export; /**< related export */ struct fsal_export *mds_fsal_export; /**< related FSAL export (avoids MDS stacking) */ struct avltree_node ds_node; /**< Node in tree of all Data Servers */ pthread_rwlock_t lock; /**< Lock to be held when manipulating its list (above). */ int32_t refcount; /**< Reference count */ uint16_t id_servers; /**< Identifier */ uint8_t pnfs_ds_status; /**< current condition */ }; /** * @brief Public structure for DS file handles * * This structure is used for files of all types including directories * and anything else that can be operated on via NFS. Having an * independent reference count and lock here makes sense, since there * is no caching infrastructure overlaying this system. * */ struct fsal_ds_handle { struct glist_head ds_handle; /*< Link in list of DS handles under the same pDS. */ struct fsal_pnfs_ds *pds; /*< Link back to pDS */ struct fsal_dsh_ops dsh_ops; /*< Operations vector */ int64_t refcount; /*< Reference count */ }; /** * @brief Get a reference on a DS handle * * This function increments the reference count on a DS handle. * * @param[in] ds_hdl The handle to reference */ static inline void ds_handle_get_ref(struct fsal_ds_handle *const ds_hdl) { (void) atomic_inc_int64_t (&ds_hdl->refcount); } /** * @brief Release a reference on a DS handle * * This function releases a reference to a DS handle. Once a caller's * reference is released they should make no attempt to access the * handle or even dereference a pointer to it. * * @param[in] ds_hdl The handle to relinquish */ static inline void ds_handle_put(struct fsal_ds_handle *const ds_hdl) { int64_t refcount = atomic_dec_int64_t (&ds_hdl->refcount); if (refcount != 0) { assert(refcount > 0); return; } ds_hdl->dsh_ops.release(ds_hdl); } /** ** Resolve forward declarations */ #include "client_mgr.h" #include "export_mgr.h" #include "fsal_up.h" #endif /* !FSAL_API */ /** @} */ nfs-ganesha-2.6.0/src/include/fsal_convert.h000066400000000000000000000055561324272410200207710ustar00rootroot00000000000000/* * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup FSAL File-System Abstraction Layer * @{ */ /** * @file fsal_convert.h * @brief FSAL conversion function. */ #ifndef FSAL_CONVERT_H #define FSAL_CONVERT_H #include #include #include #include #include "fsal_types.h" /* convert error codes */ fsal_errors_t posix2fsal_error(int posix_errorcode); static inline fsal_status_t posix2fsal_status(int posix_errorcode) { return fsalstat(posix2fsal_error(posix_errorcode), posix_errorcode); } /** converts an fsal open flag to a POSIX open flag. */ void fsal2posix_openflags(fsal_openflags_t fsal_flags, int *p_posix_flags); /** converts an FSAL permission test to a Posix permission test. */ int fsal2posix_testperm(fsal_accessflags_t testperm); /** * Converts POSIX attributes (struct stat) to FSAL attributes * (fsal_attrib_list_t) */ void posix2fsal_attributes(const struct stat *buffstat, struct attrlist *fsalattr_out); void posix2fsal_attributes_all(const struct stat *buffstat, struct attrlist *fsalattr_out); /** converts FSAL access mode to unix mode. */ mode_t fsal2unix_mode(uint32_t fsal_mode); /** converts unix access mode to fsal mode. */ uint32_t unix2fsal_mode(mode_t unix_mode); /** converts POSIX object type to fsal object type. */ object_file_type_t posix2fsal_type(mode_t posix_type_in); /** converts posix fsid to fsal FSid. */ fsal_fsid_t posix2fsal_fsid(dev_t posix_devid); /** * posix2fsal_time: * Convert POSIX time structure (time_t) * to FSAL time type (now struct timespec). */ static inline struct timespec posix2fsal_time(time_t tsec, time_t nsec) { struct timespec ts = {.tv_sec = tsec, .tv_nsec = nsec}; return ts; } const char *object_file_type_to_str(object_file_type_t type); #define my_high32m(a) ((unsigned int)(a >> 32)) #define my_low32m(a) ((unsigned int)a) extern size_t open_fd_count; fsal_dev_t posix2fsal_devt(dev_t posix_devid); #endif /* !FSAL_CONVERT_H */ /** @} */ nfs-ganesha-2.6.0/src/include/fsal_handle_syscalls.h000066400000000000000000000045151324272410200224530ustar00rootroot00000000000000/* * Copyright (C) International Business Machines Corp., 2010 * Author(s): Aneesh Kumar K.V * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * @defgroup FSAL File-System Abstraction Layer * @{ */ /** * @file include/fsal_handle_syscalls.h * @brief System calls for the platform dependent handle calls * * @todo This file should be in FSAL_VFS, not in the top-level include * directory. */ #ifndef HANDLE_H #define HANDLE_H #include "config.h" #include #include #include #include #include #include #include #include #include #include /* For having offsetof defined */ #define VFS_HANDLE_LEN 59 typedef struct vfs_file_handle { uint8_t handle_len; /* does not go on the wire */ uint8_t handle_data[VFS_HANDLE_LEN]; } vfs_file_handle_t; static inline bool vfs_handle_invalid(struct gsh_buffdesc *desc) { return desc->len > VFS_HANDLE_LEN; } #define vfs_alloc_handle(fh) \ do { \ (fh) = alloca(sizeof(struct vfs_file_handle)); \ memset((fh), 0, (sizeof(struct vfs_file_handle))); \ (fh)->handle_len = VFS_HANDLE_LEN; \ } while (0) #define vfs_malloc_handle(fh) \ do { \ (fh) = gsh_calloc(1, sizeof(struct vfs_file_handle)); \ (fh)->handle_len = VFS_HANDLE_LEN; \ } while (0) #ifdef LINUX #include "os/linux/fsal_handle_syscalls.h" #elif FREEBSD #include "os/freebsd/fsal_handle_syscalls.h" #else #error "No by-handle syscalls defined on this platform." #endif #ifndef AT_FDCWD #error "Very old kernel and/or glibc" #endif #endif /** @} */ nfs-ganesha-2.6.0/src/include/fsal_pnfs.h000066400000000000000000000266651324272410200202630ustar00rootroot00000000000000/* * * Copyright (C) 2011 Linux Box Corporation * Author: Adam C. Emerson * Boaz Harrosh * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * --------------------------------------- */ /** * @defgroup FSAL File-System Abstraction Layer * @{ */ /** * @file fsal_pnfs.h * @brief pNFS functions and structures used at the FSAL level */ #ifndef FSAL_PNFS_H #define FSAL_PNFS_H #include "nfs4.h" /** * @page FSAL_PNFS How to pNFS enable your FSAL * * Meta-data server * ================ * * Your FSAL must indicate to callers that it supports pNFS. Ensure * that the @c fs_supports method returns @c true when queried with * @c fso_pnfs_mds_supported. * * You must implement @c getdeviceinfo on the export and may impelment * @c getdevicelist, if you wish. To let clients know what layouts * they may request, be sure to implement @c fs_layouttypes. You * should implement @c fs_maximum_segments to inform the protocol * layer the maximum number of segments you will ever provide for a * single layoutget call. (The current Linux kernel only supports one * segment per LAYOUTGET, unfortunately, so that's a good maximum for * now.) Other hints for the protocol layer are @c fs_loc_body_size * (to determine how much space it will allocate for your loc_body XDR * stream) and @c fs_da_addr_size (the same thing for da_addr). * * On @c fsal_obj_handle, you should implement @c layoutget, @c * layoutreturn, and @c layoutcommit. If you want to be able to * recall layouts, you will need to send a request of the type * @c FSAL_UP_EVENT_LAYOUTRECALL with @c fsal_up_submit. For details, * see the documentation for the FSAL Upcall System. * * Data server * =========== * * This is only relevant if you are using the LAYOUT4_NFSV4_1_FILES * layouts. If you are using OSD or Object layouts, or plan to use an * spNFS-like configuration employing naíve data servers, you do not * need to worry about this. * * Your FSAL must indicate to callers that it supports pNFS DS * operations. Ensure that the @c fs_supports method returns @c true * when queried with @c fso_pnfs_ds_supported. * * You must implement the @c make_ds_handle method on the pNFS DS. * This must create an object of type @c fsal_ds_handle from the NFS * handle supplied as part of your layout. See the @c fsal_ds_handle * documentation for details. You must implement the @c release, @c * read, @c write, and @c commit methods. */ /****************************************************** * Basic in-memory types ******************************************************/ /** * @brief Represent a layout segment * * This structure not only represents segments granted by the FSAL or * being committed or returned, but also selectors as used in * LAYOUTRETURN4_FILE. */ struct pnfs_segment { /** The IO mode (must be read or write) */ layoutiomode4 io_mode; /** The offset of the segment */ uint64_t offset; /** The length of the segment */ uint64_t length; }; enum fsal_id { /** The following FSAL_ID implies no PNFS support. */ FSAL_ID_NO_PNFS = 0, /** The following ID is to be used by out of tree implementations * during an experimental phase before we are able to add an * official FSAL_ID. */ FSAL_ID_EXPERIMENTAL = 1, FSAL_ID_PANFS = 2, FSAL_ID_GPFS = 3, FSAL_ID_CEPH = 4, FSAL_ID_GLUSTER = 6, FSAL_ID_VFS = 7, FSAL_ID_RGW = 8, FSAL_ID_COUNT }; /** * @brief FSAL view of the NFSv4.1 deviceid4. * * Note that this will be encoded as an opaque, thus the byte order on the wire * will be host order NOT network order. */ struct pnfs_deviceid { /** FSAL_ID - to dispatch getdeviceinfo based on */ uint8_t fsal_id; /** Break up the remainder into useful chunks */ uint8_t device_id1; uint16_t device_id2; uint32_t device_id4; uint64_t devid; }; #define DEVICE_ID_INIT_ZERO(fsal_id) {fsal_id, 0, 0, 0, 0} /****************************************************** * FSAL MDS function argument structs ******************************************************/ /** * @brief Input parameters to FSAL_layoutget */ struct fsal_layoutget_arg { /** The type of layout being requested */ layouttype4 type; /** The minimum length that must be granted if a layout is to be * granted at all. */ uint64_t minlength; /** Ths FSAL must use this value (in network byte order) as the * high quad of any deviceid4 it returns in the loc_body. */ uint64_t export_id; /** The maximum number of bytes the client is willing to accept in the response, including XDR overhead. */ uint32_t maxcount; }; /** * In/out and output parameters to FSAL_layoutget */ struct fsal_layoutget_res { /** As input, the offset, length, and iomode requested by the * caller. As output, the offset, length, and iomode of a given * segment granted by the FSAL. */ struct pnfs_segment segment; /** Whatever value the FSAL stores here is saved with the segment * and supplied to it on future calls to LAYOUTCOMMIT and * LAYOUTRETURN. Any memory allocated must be freed on layout * disposal. */ void *fsal_seg_data; /** Whether the layout should be returned on last close. Note * that this flag being set on one segment makes all layout * segments associated with the same stateid return_on_close. */ bool return_on_close; /** This pointer is NULL on the first call FSAL_layoutget. The * FSAL may store a pointer to any data it wishes, and this * pointer will be supplied to future calls to FSAL_layoutget * that serve the same LAYOUTGET operation. The FSAL must * de-allocate any memory it allocated when it sets the * last_segment flag */ void *context; /** The FSAL must set this to true when it has granted the last * segment to satisfy this operation. Currently, no production * clients support multiple segments granted by a single * LAYOUTGET operation, so FSALs should grant a single segment * and set this value on the first call. */ bool last_segment; /** On input, this field signifies a request by the client to be * signaled when a requested but unavailable layout becomes * available. In output, it signifies the FSAL's willingness to * make a callback when the layout becomes available. We do not * yet implement callbacks, so it should always be set to * false. */ bool signal_available; }; /** * @brief Circumstance that triggered the layoutreturn */ enum fsal_layoutreturn_circumstance { /** Return initiated by client call. */ circumstance_client, /** Indicates that the client is performing a return of a * layout it held prior to a server reboot. As such, * cur_segment is meaningless (no record of the layout having * been granted exists). */ circumstance_reclaim, /** This is a return following from the last close on a file with return_on_close layouts. */ circumstance_roc, /** The client has behaved badly and we are taking its layout away forcefully. */ circumstance_revoke, /** The client forgot this layout and requested a new layout on the same file without an layout stateid. */ circumstance_forgotten, /** This layoutrecall is a result of system shutdown */ circumstance_shutdown }; /** * Input parameters to FSAL_layoutreturn */ struct fsal_layoutreturn_arg { /** The type of layout being returned */ layouttype4 lo_type; /** The return type of the LAYOUTRETURN call. Meaningless if fsal_layoutreturn_synthetic is set. */ layoutreturn_type4 return_type; /** The circumstances under which the return was triggered. */ enum fsal_layoutreturn_circumstance circumstance; /** Layout for specified for return. This need not match any * actual granted layout. Offset and length are set to 0 and * NFS4_UINT64_MAX in the case of bulk or synthetic returns. * For synthetic returns, the io_mode is set to * LAYOUTIOMODE4_ANY. */ struct pnfs_segment spec_segment; /** The current segment in the return iteration which is to be * returned. */ struct pnfs_segment cur_segment; /** Pointer to layout specific data supplied by LAYOUTGET. If * dispose is true, any memory allocated for this value must be * freed. */ void *fsal_seg_data; /** If true, the FSAL must free all resources associated with * res.segment. */ bool dispose; /** After this return, there will be no more layouts associated * with this layout state (that is, there will be no more * layouts for this (clientid, handle, layout type) triple. */ bool last_segment; /** Count of recall tokens. 0 if no LAYOUTRECALLs are * satisfied. */ size_t ncookies; /** Array of pointers to layout specific data supplied by * LAYOUTRECALL. If this LAYOUTRETURN completly satisfies * one or more invoked LAYOUTRECALLs, the tokens of the * recalls will be supplied. */ const void *recall_cookies[1]; }; /** * Input parameters to FSAL_layoutcommit */ struct fsal_layoutcommit_arg { /** The type of the layout being committed */ layouttype4 type; /** The segment being committed on this call */ struct pnfs_segment segment; /** Pointer to layout specific data supplied by LAYOUTGET. */ void *fsal_seg_data; /** True if this is a reclaim commit */ bool reclaim; /** True if the client has suggested a new offset */ bool new_offset; /** The offset of the last byte written, if new_offset if set, * otherwise undefined. */ uint64_t last_write; /** True if the client provided a new value for mtime */ bool time_changed; /** If new_time is true, the client-supplied modification tiem * for the file. otherwise, undefined. */ nfstime4 new_time; }; /** * In/out and output parameters to FSAL_layoutcommit */ struct fsal_layoutcommit_res { /** A pointer, NULL on the first call to FSAL_layoutcommit. The * FSAL may store whatever it wishes in this field and it will * be supplied on all subsequent calls. If the FSAL has * allocated any memory, this memory must be freed if * commit_done is set. */ void *context; /** True if the FSAL is returning a new file size */ bool size_supplied; /** The new file size returned by the FSAL */ uint64_t new_size; /** The FSAL has completed the LAYOUTCOMMIT operation and * FSAL_layoutcommit need not be called again, even if more * segments are left in the layout. */ bool commit_done; }; /** * In/out and output parameters to FSAL_getdevicelist */ struct fsal_getdevicelist_res { /** Input, cookie indicating position in device list from which * to begin. Output, cookie that may be supplied to get the * entry after the alst one returned. Undefined if EOF is * set. */ uint64_t cookie; /** For any non-zero cookie, this must be the verifier returned * from a previous call to getdevicelist. The FSAL may use this * value to verify that the cookie is not out of date. A cookie * verifier may be supplied by the FSAL on output. */ uint64_t cookieverf; /** True if the last deviceid has been returned. */ bool eof; }; #endif /* !FSAL_PNFS_H */ /** @} */ nfs-ganesha-2.6.0/src/include/fsal_types.h000066400000000000000000000674411324272410200204560ustar00rootroot00000000000000/* * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup FSAL File-System Abstraction Layer * @{ */ /** * @file include/fsal_types.h */ #ifndef _FSAL_TYPES_H #define _FSAL_TYPES_H #include #include #include #include /* for MAXNAMLEN */ #include "uid2grp.h" #include "nfsv41.h" /* Cookie to be used in FSAL_ListXAttrs() to bypass RO xattr */ #define FSAL_XATTR_RW_COOKIE (~0) /** * @brief Object file type within the system */ typedef enum { NO_FILE_TYPE = 0, /* sanity check to ignore type */ REGULAR_FILE = 1, CHARACTER_FILE = 2, BLOCK_FILE = 3, SYMBOLIC_LINK = 4, SOCKET_FILE = 5, FIFO_FILE = 6, DIRECTORY = 7, EXTENDED_ATTR = 8 } object_file_type_t; /* --------------- * FS dependant : * --------------*/ /* export object * Created by fsal and referenced by the export list */ /* handle descriptor * used primarily to extract the bits of a file object handle from * protocol buffers and for calculating hashes. * This points into a buffer allocated and passed by the caller. * len is set to the buffer size when passed. It is updated to * the actual copy length on return. */ /** object name. */ /* Used to record the uid and gid of the client that made a request. */ struct user_cred { uid_t caller_uid; gid_t caller_gid; unsigned int caller_glen; gid_t *caller_garray; }; struct export_perms { uid_t anonymous_uid; /* root uid when no root access is available * uid when access is available but all users * are being squashed. */ gid_t anonymous_gid; /* root gid when no root access is available * gid when access is available but all users * are being squashed. */ uint32_t options; /* available export options */ uint32_t set; /* Options that have been set */ }; /* Define bit values for cred_flags */ #define CREDS_LOADED 0x01 #define CREDS_ANON 0x02 #define UID_SQUASHED 0x04 #define GID_SQUASHED 0x08 #define GARRAY_SQUASHED 0x10 #define MANAGED_GIDS 0x20 /** filesystem identifier */ typedef struct fsal_fsid__ { uint64_t major; uint64_t minor; } fsal_fsid_t; /** raw device spec */ typedef struct fsal_dev__ { uint64_t major; uint64_t minor; } fsal_dev_t; /* constants for specifying which ACL types are supported */ typedef uint16_t fsal_aclsupp_t; #define FSAL_ACLSUPPORT_ALLOW 0x01 #define FSAL_ACLSUPPORT_DENY 0x02 /** ACE types */ typedef uint32_t fsal_acetype_t; #define FSAL_ACE_TYPE_ALLOW 0 #define FSAL_ACE_TYPE_DENY 1 #define FSAL_ACE_TYPE_AUDIT 2 #define FSAL_ACE_TYPE_ALARM 3 #define FSAL_ACE_TYPE_MAX 4 /** ACE flag */ typedef uint32_t fsal_aceflag_t; #define FSAL_ACE_FLAG_FILE_INHERIT 0x00000001 #define FSAL_ACE_FLAG_DIR_INHERIT 0x00000002 #define FSAL_ACE_FLAG_NO_PROPAGATE 0x00000004 #define FSAL_ACE_FLAG_INHERIT_ONLY 0x00000008 #define FSAL_ACE_FLAG_SUCCESSFUL 0x00000010 #define FSAL_ACE_FLAG_FAILED 0x00000020 #define FSAL_ACE_FLAG_GROUP_ID 0x00000040 #define FSAL_ACE_FLAG_INHERITED 0x00000080 /** ACE internal flags */ #define FSAL_ACE_IFLAG_MODE_GEN 0x10000000 #define FSAL_ACE_IFLAG_EXCLUDE_FILES 0x40000000 #define FSAL_ACE_IFLAG_EXCLUDE_DIRS 0x20000000 #define FSAL_ACE_IFLAG_SPECIAL_ID 0x80000000 #define FSAL_ACE_FLAG_INHERIT (FSAL_ACE_FLAG_FILE_INHERIT | \ FSAL_ACE_FLAG_DIR_INHERIT | \ FSAL_ACE_FLAG_INHERIT_ONLY) /** ACE permissions */ typedef uint32_t fsal_aceperm_t; #define FSAL_ACE_PERM_READ_DATA 0x00000001 #define FSAL_ACE_PERM_LIST_DIR 0x00000001 #define FSAL_ACE_PERM_WRITE_DATA 0x00000002 #define FSAL_ACE_PERM_ADD_FILE 0x00000002 #define FSAL_ACE_PERM_APPEND_DATA 0x00000004 #define FSAL_ACE_PERM_ADD_SUBDIRECTORY 0x00000004 #define FSAL_ACE_PERM_READ_NAMED_ATTR 0x00000008 #define FSAL_ACE_PERM_WRITE_NAMED_ATTR 0x00000010 #define FSAL_ACE_PERM_EXECUTE 0x00000020 #define FSAL_ACE_PERM_DELETE_CHILD 0x00000040 #define FSAL_ACE_PERM_READ_ATTR 0x00000080 #define FSAL_ACE_PERM_WRITE_ATTR 0x00000100 #define FSAL_ACE_PERM_DELETE 0x00010000 #define FSAL_ACE_PERM_READ_ACL 0x00020000 #define FSAL_ACE_PERM_WRITE_ACL 0x00040000 #define FSAL_ACE_PERM_WRITE_OWNER 0x00080000 #define FSAL_ACE_PERM_SYNCHRONIZE 0x00100000 /** ACE who */ #define FSAL_ACE_NORMAL_WHO 0 #define FSAL_ACE_SPECIAL_OWNER 1 #define FSAL_ACE_SPECIAL_GROUP 2 #define FSAL_ACE_SPECIAL_EVERYONE 3 typedef struct fsal_ace__ { fsal_acetype_t type; fsal_aceperm_t perm; fsal_aceflag_t flag; fsal_aceflag_t iflag; /* Internal flags. */ union { uid_t uid; gid_t gid; } who; } fsal_ace_t; typedef struct fsal_acl__ { uint32_t naces; fsal_ace_t *aces; pthread_rwlock_t lock; uint32_t ref; } fsal_acl_t; typedef struct fsal_acl_data__ { uint32_t naces; fsal_ace_t *aces; } fsal_acl_data_t; /* Macros for NFS4 ACE flags, masks, and special who values. */ #define GET_FSAL_ACE_TYPE(ACE) ((ACE).type) #define GET_FSAL_ACE_PERM(ACE) ((ACE).perm) #define GET_FSAL_ACE_FLAG(ACE) ((ACE).flag) #define GET_FSAL_ACE_IFLAG(ACE) ((ACE).iflag) #define GET_FSAL_ACE_USER(ACE) ((ACE).who.uid) #define GET_FSAL_ACE_GROUP(ACE) ((ACE).who.gid) #define IS_FSAL_ACE_BIT(WORD, BIT) (0 != ((WORD) & (BIT))) #define IS_FSAL_ACE_ALL_BITS(WORD, BITS) (BITS == ((WORD) & (BITS))) #define IS_FSAL_ACE_TYPE(ACE, VALUE) \ ((GET_FSAL_ACE_TYPE(ACE)) == (VALUE)) #define IS_FSAL_ACE_USER(ACE, VALUE) \ ((GET_FSAL_ACE_USER(ACE)) == (VALUE)) #define IS_FSAL_ACE_GROUP(ACE, VALUE) \ ((GET_FSAL_ACE_GROUP(ACE)) == (VALUE)) #define IS_FSAL_ACE_ALLOW(ACE) \ IS_FSAL_ACE_TYPE(ACE, FSAL_ACE_TYPE_ALLOW) #define IS_FSAL_ACE_DENY(ACE) \ IS_FSAL_ACE_TYPE(ACE, FSAL_ACE_TYPE_DENY) #define IS_FSAL_ACE_AUDIT(ACE) \ IS_FSAL_ACE_TYPE(ACE, FSAL_ACE_TYPE_AUDIT) #define IS_FSAL_ACE_ALRAM(ACE) \ IS_FSAL_ACE_TYPE(ACE, FSAL_ACE_TYPE_ALARM) #define IS_FSAL_ACE_PERM(ACE) \ (IS_FSAL_ACE_ALLOW(ACE) || IS_FSAL_ACE_DENY(ACE)) #define IS_FSAL_ACE_FLAG(ACE, BIT) \ IS_FSAL_ACE_BIT(GET_FSAL_ACE_FLAG(ACE), (BIT)) #define IS_FSAL_ACE_FILE_INHERIT(ACE) \ IS_FSAL_ACE_FLAG(ACE, FSAL_ACE_FLAG_FILE_INHERIT) #define IS_FSAL_ACE_DIR_INHERIT(ACE) \ IS_FSAL_ACE_FLAG(ACE, FSAL_ACE_FLAG_DIR_INHERIT) #define IS_FSAL_ACE_NO_PROPAGATE(ACE) \ IS_FSAL_ACE_FLAG(ACE, FSAL_ACE_FLAG_NO_PROPAGATE) #define IS_FSAL_ACE_INHERIT_ONLY(ACE) \ IS_FSAL_ACE_FLAG(ACE, FSAL_ACE_FLAG_INHERIT_ONLY) #define IS_FSAL_ACE_FLAG_SUCCESSFUL(ACE) \ IS_FSAL_ACE_FLAG(ACE, FSAL_ACE_FLAG_SUCCESSFUL) #define IS_FSAL_ACE_AUDIT_FAILURE(ACE) \ IS_FSAL_ACE_FLAG(ACE, FSAL_ACE_FLAG_FAILED) #define IS_FSAL_ACE_GROUP_ID(ACE) \ IS_FSAL_ACE_FLAG(ACE, FSAL_ACE_FLAG_GROUP_ID) #define IS_FSAL_ACE_INHERIT(ACE) \ IS_FSAL_ACE_FLAG(ACE, FSAL_ACE_FLAG_INHERIT) #define IS_FSAL_ACE_INHERTED(ACE) \ IS_FSAL_ACE_FLAG(ACE, FSAL_ACE_FLAG_INHERITED) #define GET_FSAL_ACE_WHO_TYPE(ACE) \ (IS_FSAL_ACE_GROUP_ID(ACE) ? "gid" : "uid") #define GET_FSAL_ACE_WHO(ACE) \ (IS_FSAL_ACE_GROUP_ID(ACE) ? (ACE).who.gid : (ACE).who.uid) #define IS_FSAL_ACE_SPECIAL_OWNER(ACE) \ IS_FSAL_ACE_USER(ACE, FSAL_ACE_SPECIAL_OWNER) #define IS_FSAL_ACE_SPECIAL_GROUP(ACE) \ IS_FSAL_ACE_USER(ACE, FSAL_ACE_SPECIAL_GROUP) #define IS_FSAL_ACE_SPECIAL_EVERYONE(ACE) \ IS_FSAL_ACE_USER(ACE, FSAL_ACE_SPECIAL_EVERYONE) #define IS_FSAL_ACE_SPECIAL(ACE) \ (IS_FSAL_ACE_SPECIAL_OWNER(ACE) || \ IS_FSAL_ACE_SPECIAL_GROUP(ACE) || \ IS_FSAL_ACE_SPECIAL_EVERYONE(ACE)) /* Macros for internal NFS4 ACE flags. */ #define IS_FSAL_ACE_MODE_GEN(ACE) \ IS_FSAL_ACE_BIT(GET_FSAL_ACE_IFLAG(ACE), \ FSAL_ACE_IFLAG_MODE_GEN) #define IS_FSAL_ACE_SPECIAL_ID(ACE) \ IS_FSAL_ACE_BIT(GET_FSAL_ACE_IFLAG(ACE), \ FSAL_ACE_IFLAG_SPECIAL_ID) #define IS_FSAL_FILE_APPLICABLE(ACE) \ (!IS_FSAL_ACE_BIT(GET_FSAL_ACE_IFLAG(ACE), \ FSAL_ACE_IFLAG_EXCLUDE_FILES)) #define IS_FSAL_DIR_APPLICABLE(ACE) \ (!IS_FSAL_ACE_BIT(GET_FSAL_ACE_IFLAG(ACE), \ FSAL_ACE_IFLAG_EXCLUDE_DIRS)) /* Macros for NFS4 ACE permissions. */ #define IS_FSAL_ACE_READ_DATA(ACE) \ IS_FSAL_ACE_BIT(GET_FSAL_ACE_PERM(ACE), FSAL_ACE_PERM_READ_DATA) #define IS_FSAL_ACE_LIST_DIR(ACE) \ IS_FSAL_ACE_BIT(GET_FSAL_ACE_PERM(ACE), FSAL_ACE_PERM_LIST_DIR) #define IS_FSAL_ACE_WRITE_DATA(ACE) \ IS_FSAL_ACE_BIT(GET_FSAL_ACE_PERM(ACE), FSAL_ACE_PERM_WRITE_DATA) #define IS_FSAL_ACE_ADD_FIILE(ACE) \ IS_FSAL_ACE_BIT(GET_FSAL_ACE_PERM(ACE), FSAL_ACE_PERM_ADD_FILE) #define IS_FSAL_ACE_APPEND_DATA(ACE) \ IS_FSAL_ACE_BIT(GET_FSAL_ACE_PERM(ACE), FSAL_ACE_PERM_APPEND_DATA) #define IS_FSAL_ACE_ADD_SUBDIRECTORY(ACE) \ IS_FSAL_ACE_BIT(GET_FSAL_ACE_PERM(ACE), FSAL_ACE_PERM_ADD_SUBDIRECTORY) #define IS_FSAL_ACE_READ_NAMED_ATTR(ACE) \ IS_FSAL_ACE_BIT(GET_FSAL_ACE_PERM(ACE), FSAL_ACE_PERM_READ_NAMED_ATTR) #define IS_FSAL_ACE_WRITE_NAMED_ATTR(ACE) \ IS_FSAL_ACE_BIT(GET_FSAL_ACE_PERM(ACE), FSAL_ACE_PERM_WRITE_NAMED_ATTR) #define IS_FSAL_ACE_EXECUTE(ACE) \ IS_FSAL_ACE_BIT(GET_FSAL_ACE_PERM(ACE), FSAL_ACE_PERM_EXECUTE) #define IS_FSAL_ACE_DELETE_CHILD(ACE) \ IS_FSAL_ACE_BIT(GET_FSAL_ACE_PERM(ACE), FSAL_ACE_PERM_DELETE_CHILD) #define IS_FSAL_ACE_READ_ATTR(ACE) \ IS_FSAL_ACE_BIT(GET_FSAL_ACE_PERM(ACE), FSAL_ACE_PERM_READ_ATTR) #define IS_FSAL_ACE_WRITE_ATTR(ACE) \ IS_FSAL_ACE_BIT(GET_FSAL_ACE_PERM(ACE), FSAL_ACE_PERM_WRITE_ATTR) #define IS_FSAL_ACE_DELETE(ACE) \ IS_FSAL_ACE_BIT(GET_FSAL_ACE_PERM(ACE), FSAL_ACE_PERM_DELETE) #define IS_FSAL_ACE_READ_ACL(ACE) \ IS_FSAL_ACE_BIT(GET_FSAL_ACE_PERM(ACE), FSAL_ACE_PERM_READ_ACL) #define IS_FSAL_ACE_WRITE_ACL(ACE) \ IS_FSAL_ACE_BIT(GET_FSAL_ACE_PERM(ACE), FSAL_ACE_PERM_WRITE_ACL) #define IS_FSAL_ACE_WRITE_OWNER(ACE) \ IS_FSAL_ACE_BIT(GET_FSAL_ACE_PERM(ACE), FSAL_ACE_PERM_WRITE_OWNER) #define IS_FSAL_ACE_SYNCHRONIZE(ACE) \ IS_FSAL_ACE_BIT(GET_FSAL_ACE_PERM(ACE), FSAL_ACE_PERM_SYNCHRONIZE) /** * Defines an attribute mask. * * Do not just use OR and AND to test these, use the macros. */ typedef uint64_t attrmask_t; /** For stackable FSALs that just pass through dealings with attributes */ #define ALL_ATTRIBUTES UINT64_MAX /** * Attribute masks. */ /* supported attributes (Obsolete) #define ATTR_SUPPATTR 0x0000000000000001 */ /* file type */ #define ATTR_TYPE 0x0000000000000002LL /* file size */ #define ATTR_SIZE 0x0000000000000004LL /* filesystem id */ #define ATTR_FSID 0x0000000000000008LL /* file space reserve */ #define ATTR4_SPACE_RESERVED 0x0000000000000010LL /* ACL */ #define ATTR_ACL 0x0000000000000020LL /* file id */ #define ATTR_FILEID 0x0000000000000040LL /* Access permission flag */ #define ATTR_MODE 0x0000000000000080LL /* Number of hard links */ #define ATTR_NUMLINKS 0x0000000000000100LL /* owner ID */ #define ATTR_OWNER 0x0000000000000200LL /* group ID */ #define ATTR_GROUP 0x0000000000000400LL /* ID of device for block special or character special files*/ #define ATTR_RAWDEV 0x0000000000000800LL /* Access time */ #define ATTR_ATIME 0x0000000000001000LL /* Creation time */ #define ATTR_CREATION 0x0000000000002000LL /* Metadata modification time */ #define ATTR_CTIME 0x0000000000004000LL /* data modification time */ #define ATTR_MTIME 0x0000000000008000LL /* space used by this file. */ #define ATTR_SPACEUSED 0x0000000000010000LL /* NFS4 change_time like attribute */ #define ATTR_CHGTIME 0x0000000000040000LL /* This bit indicates that an error occured during getting object attributes */ #define ATTR_RDATTR_ERR 0x8000000000000000LL /* Generation number */ #define ATTR_GENERATION 0x0000000000080000LL /* Change attribute */ #define ATTR_CHANGE 0x0000000000100000LL /* Set atime to server time */ #define ATTR_ATIME_SERVER 0x0000000000200000LL /* Set mtime to server time */ #define ATTR_MTIME_SERVER 0x0000000000400000LL /* Set fs locations */ #define ATTR4_FS_LOCATIONS 0x0000000000800000LL /* xattr supported */ #define ATTR4_XATTR 0x0000000001000000LL /* attributes that used for NFS v3 */ #define ATTRS_NFS3 (ATTR_MODE | ATTR_FILEID | \ ATTR_TYPE | ATTR_RAWDEV | \ ATTR_NUMLINKS | ATTR_OWNER | \ ATTR_GROUP | ATTR_SIZE | \ ATTR_ATIME | ATTR_MTIME | \ ATTR_CTIME | ATTR_SPACEUSED | \ ATTR_FSID) #define ATTRS_TIME (ATTR_ATIME | ATTR_MTIME | ATTR_CTIME) #define ATTRS_CREDS (ATTR_OWNER | ATTR_GROUP) #define CREATE_MASK_NON_REG_NFS3 (ATTRS_TIME) #define CREATE_MASK_NON_REG_NFS4 (ATTRS_TIME | ATTR_ACL) #define CREATE_MASK_REG_NFS3 (CREATE_MASK_NON_REG_NFS3 | ATTR_SIZE) #define CREATE_MASK_REG_NFS4 (CREATE_MASK_NON_REG_NFS4 | ATTR_SIZE) #define ATTRS_SET_TIME (ATTR_ATIME | ATTR_MTIME | \ ATTR_ATIME_SERVER | ATTR_MTIME_SERVER) /** Define the set of attributes contained or derrived from struct stat that * are supplied by posix2fsal_attributes. */ #define ATTRS_POSIX (ATTR_TYPE | ATTR_SIZE | ATTR_FSID | ATTR_FILEID | \ ATTR_MODE | ATTR_NUMLINKS | ATTR_OWNER | ATTR_GROUP | \ ATTR_ATIME | ATTR_CTIME | ATTR_MTIME | ATTR_CHGTIME | \ ATTR_CHANGE | ATTR_SPACEUSED | ATTR_RAWDEV) /** * @brief A list of FS object's attributes. */ struct attrlist { attrmask_t request_mask; /*< Indicates the requested from the FSAL. */ attrmask_t valid_mask; /*< Indicates the attributes to be set or that have been filled in by the FSAL. */ attrmask_t supported; /*< Indicates which attributes the FSAL supports. */ object_file_type_t type; /*< Type of this object */ uint64_t filesize; /*< Logical size (amount of data that can be read) */ fsal_fsid_t fsid; /*< Filesystem on which this object is stored */ fsal_acl_t *acl; /*< ACL for this object */ uint64_t fileid; /*< Unique identifier for this object within the scope of the fsid, (e.g. inode number) */ uint32_t mode; /*< POSIX access mode */ uint32_t numlinks; /*< Number of links to this file */ uint64_t owner; /*< Owner ID */ uint64_t group; /*< Group ID */ fsal_dev_t rawdev; /*< Major/minor device number (only meaningful for character/block special files.) */ struct timespec atime; /*< Time of last access */ struct timespec creation; /*< Creation time */ struct timespec ctime; /*< Inode modification time (a la stat. NOT creation.) */ struct timespec mtime; /*< Time of last modification */ struct timespec chgtime; /*< Time of last 'change' */ uint64_t spaceused; /*< Space used on underlying filesystem */ uint64_t change; /*< A 'change id' */ uint64_t generation; /*< Generation number for this file */ int32_t expire_time_attr; /*< Expiration time interval in seconds for attributes. Settable by FSAL. */ }; /****************************************************** * Attribute mask management. ******************************************************/ /** this macro tests if an attribute is set * example : * FSAL_TEST_MASK( attrib_list.mask, FSAL_ATTR_CREATION ) */ #define FSAL_TEST_MASK(_attrib_mask_, _attr_const_) \ ((_attrib_mask_) & (_attr_const_)) /** this macro sets an attribute * example : * FSAL_SET_MASK( attrib_list.mask, FSAL_ATTR_CREATION ) */ #define FSAL_SET_MASK(_attrib_mask_, _attr_const_) \ ((_attrib_mask_) |= (_attr_const_)) #define FSAL_UNSET_MASK(_attrib_mask_, _attr_const_) \ ((_attrib_mask_) &= ~(_attr_const_)) /** this macro clears the attribute mask * example : * FSAL_CLEAR_MASK( attrib_list.asked_attributes ) */ #define FSAL_CLEAR_MASK(_attrib_mask_) \ ((_attrib_mask_) = 0LL) /****************************************************** * FSAL extended attributes management. ******************************************************/ /** An extented attribute entry */ typedef struct fsal_xattrent { uint64_t xattr_id; /*< xattr index */ uint64_t xattr_cookie; /*< cookie for the next entry */ char xattr_name[MAXNAMLEN + 1]; /*< attribute name */ } fsal_xattrent_t; /* generic definitions for extended attributes */ #define XATTR_FOR_FILE 0x00000001 #define XATTR_FOR_DIR 0x00000002 #define XATTR_FOR_SYMLINK 0x00000004 #define XATTR_FOR_ALL 0x0000000F #define XATTR_RO 0x00000100 #define XATTR_RW 0x00000200 /* function for getting an attribute value */ #define XATTR_RW_COOKIE ~0 /* Flags representing if an FSAL supports read or write delegations */ #define FSAL_OPTION_FILE_READ_DELEG 0x00000001 /*< File read delegations */ #define FSAL_OPTION_FILE_WRITE_DELEG 0x00000002 /*< File write delegations */ #define FSAL_OPTION_FILE_DELEGATIONS (FSAL_OPTION_FILE_READ_DELEG | \ FSAL_OPTION_FILE_WRITE_DELEG) #define FSAL_OPTION_NO_DELEGATIONS 0 /** Mask for permission testing. Both mode and ace4 mask are encoded. */ typedef enum { FSAL_R_OK = 0x04000000, /*< Test for Read permission */ FSAL_W_OK = 0x02000000, /*< Test for Write permission */ FSAL_X_OK = 0x01000000, /*< Test for execute permission */ FSAL_ACCESS_OK = 0x00000000, /*< Allow */ FSAL_ACCESS_FLAG_BIT_MASK = 0x80000000, FSAL_MODE_BIT_MASK = 0x07000000, FSAL_ACE4_BIT_MASK = 0x50FFFFFF, FSAL_MODE_MASK_FLAG = 0x00000000, FSAL_ACE4_MASK_FLAG = 0x80000000, FSAL_ACE4_PERM_CONTINUE = 0x40000000, /*< Indicate ACL evaluation * should continue */ FSAL_ACE4_REQ_FLAG = 0x10000000, /*< Indicate required ACL allow */ } fsal_accessflags_t; static inline fsal_accessflags_t FSAL_MODE_MASK(fsal_accessflags_t access) { unsigned long acc = access & FSAL_MODE_BIT_MASK; return (fsal_accessflags_t) acc; } static inline fsal_accessflags_t FSAL_ACE4_MASK(fsal_accessflags_t access) { unsigned long acc = access & FSAL_ACE4_BIT_MASK; return (fsal_accessflags_t) acc; } #define FSAL_MODE_MASK_SET(access) (access | FSAL_MODE_MASK_FLAG) #define FSAL_ACE4_MASK_SET(access) (access | FSAL_ACE4_MASK_FLAG) #define IS_FSAL_MODE_MASK_VALID(access) \ ((access & FSAL_ACCESS_FLAG_BIT_MASK) == FSAL_MODE_MASK_FLAG) #define IS_FSAL_ACE4_MASK_VALID(access) \ ((access & FSAL_ACCESS_FLAG_BIT_MASK) == FSAL_ACE4_MASK_FLAG) #define IS_FSAL_ACE4_REQ(access) (access & FSAL_ACE4_REQ_FLAG) #define FSAL_WRITE_ACCESS (FSAL_MODE_MASK_SET(FSAL_W_OK) | \ FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_WRITE_DATA | \ FSAL_ACE_PERM_APPEND_DATA)) #define FSAL_READ_ACCESS (FSAL_MODE_MASK_SET(FSAL_R_OK) | \ FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_READ_DATA)) #define FSAL_EXECUTE_ACCESS (FSAL_MODE_MASK_SET(FSAL_X_OK) | \ FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_EXECUTE)) /* Object handle LRU resource actions */ /** * @todo ACE: These should probably be moved to cache_inode_lru.h */ typedef enum { LRU_CLOSE_FILES = 0x1, LRU_FREE_MEMORY = 0x2 } lru_actions_t; /** FSAL_open behavior. */ typedef uint16_t fsal_openflags_t; #define FSAL_O_CLOSED 0x0000 /* Closed */ #define FSAL_O_READ 0x0001 /* read */ #define FSAL_O_WRITE 0x0002 /* write */ #define FSAL_O_RDWR (FSAL_O_READ|FSAL_O_WRITE) /* read/write: both flags * explicitly or'd together * so that FSAL_O_RDWR can * be used as a mask */ #define FSAL_O_RECLAIM 0x0008 /* open reclaim */ #define FSAL_O_REOPEN 0x0010 /* re-open */ #define FSAL_O_ANY 0x0020 /* any open file descriptor is usable */ #define FSAL_O_TRUNC 0x0040 /* Truncate file on open */ #define FSAL_O_DENY_READ 0x0100 #define FSAL_O_DENY_WRITE 0x0200 #define FSAL_O_DENY_WRITE_MAND 0x0400 /* Mandatory deny-write (i.e. NFSv4) */ #define FSAL_O_DENY_NONE 0x0000 enum fsal_create_mode { FSAL_NO_CREATE = 0, FSAL_UNCHECKED = 1, FSAL_GUARDED = 2, FSAL_EXCLUSIVE = 3, FSAL_EXCLUSIVE_41 = 4, FSAL_EXCLUSIVE_9P, }; /** File system static info. */ /* enums for accessing * boolean fields of staticfsinfo */ typedef enum enum_fsal_fsinfo_options { fso_no_trunc, fso_chown_restricted, fso_case_insensitive, fso_case_preserving, fso_link_support, fso_symlink_support, fso_lock_support, fso_lock_support_async_block, fso_named_attr, fso_unique_handles, fso_cansettime, fso_homogenous, fso_auth_exportpath_xdev, fso_delegations_r, fso_delegations_w, fso_pnfs_ds_supported, fso_pnfs_mds_supported, fso_grace_method, fso_link_supports_permission_checks, fso_rename_changes_key, fso_compute_readdir_cookie, fso_whence_is_name, } fsal_fsinfo_options_t; /* The largest maxread and maxwrite value */ #define FSAL_MAXIOSIZE XDR_BYTES_MAXLEN_IO typedef struct fsal_staticfsinfo_t { uint64_t maxfilesize; /*< maximum allowed filesize */ uint32_t maxlink; /*< maximum hard links on a file */ uint32_t maxnamelen; /*< maximum name length */ uint32_t maxpathlen; /*< maximum path length */ bool no_trunc; /*< is it errorneous when name>maxnamelen? */ bool chown_restricted; /*< is chown limited to super-user. */ bool case_insensitive; /*< case insensitive FS? */ bool case_preserving; /*< FS preserves case? */ bool link_support; /*< FS supports hardlinks? */ bool symlink_support; /*< FS supports symlinks? */ bool lock_support; /*< FS supports file locking? */ bool lock_support_async_block; /*< FS supports blocking locks? */ bool named_attr; /*< FS supports named attributes. */ bool unique_handles; /*< Handles are unique and persistent. */ struct timespec lease_time; /*< Duration of lease at FS in secs */ fsal_aclsupp_t acl_support; /*< what type of ACLs are supported */ bool cansettime; /*< Is it possible to change file times using SETATTR. */ bool homogenous; /*< Are supported attributes the same for all objects of this filesystem. */ attrmask_t supported_attrs; /*< If the FS is homogenous, this indicates the set of supported attributes. */ uint64_t maxread; /*< Max read size */ uint64_t maxwrite; /*< Max write size */ uint32_t umask; /*< This mask is applied to the mode of created objects */ bool auth_exportpath_xdev; /*< This flag indicates weither it is possible to cross junctions for resolving an NFS export path. */ uint32_t xattr_access_rights; /*< This indicates who is allowed to read/modify xattrs value. */ uint32_t delegations; /*< fsal supports delegations */ bool pnfs_mds; /*< fsal supports file pnfs MDS */ bool pnfs_ds; /*< fsal supports file pnfs DS */ bool fsal_trace; /*< fsal trace supports */ bool fsal_grace; /*< fsal will handle grace */ bool link_supports_permission_checks; bool rename_changes_key;/*< Handle key is changed across rename */ bool compute_readdir_cookie; bool whence_is_name; } fsal_staticfsinfo_t; /** * @brief The return error values of FSAL calls. */ typedef enum fsal_errors_t { ERR_FSAL_NO_ERROR = 0, ERR_FSAL_PERM = 1, ERR_FSAL_NOENT = 2, ERR_FSAL_IO = 5, ERR_FSAL_NXIO = 6, ERR_FSAL_NOMEM = 12, ERR_FSAL_ACCESS = 13, ERR_FSAL_FAULT = 14, ERR_FSAL_EXIST = 17, ERR_FSAL_XDEV = 18, ERR_FSAL_NOTDIR = 20, ERR_FSAL_ISDIR = 21, ERR_FSAL_INVAL = 22, ERR_FSAL_FBIG = 27, ERR_FSAL_NOSPC = 28, ERR_FSAL_ROFS = 30, ERR_FSAL_MLINK = 31, ERR_FSAL_DQUOT = 49, ERR_FSAL_NO_DATA = 61, ERR_FSAL_NAMETOOLONG = 78, ERR_FSAL_NOTEMPTY = 93, ERR_FSAL_STALE = 151, ERR_FSAL_BADHANDLE = 10001, ERR_FSAL_BADCOOKIE = 10003, ERR_FSAL_NOTSUPP = 10004, ERR_FSAL_TOOSMALL = 10005, ERR_FSAL_SERVERFAULT = 10006, ERR_FSAL_BADTYPE = 10007, ERR_FSAL_DELAY = 10008, ERR_FSAL_LOCKED = 10012, ERR_FSAL_FHEXPIRED = 10014, ERR_FSAL_SHARE_DENIED = 10015, ERR_FSAL_SYMLINK = 10029, ERR_FSAL_ATTRNOTSUPP = 10032, ERR_FSAL_BAD_RANGE = 10042, ERR_FSAL_NOT_INIT = 20001, ERR_FSAL_ALREADY_INIT = 20002, ERR_FSAL_BAD_INIT = 20003, ERR_FSAL_SEC = 20004, ERR_FSAL_NO_QUOTA = 20005, ERR_FSAL_NOT_OPENED = 20010, ERR_FSAL_DEADLOCK = 20011, ERR_FSAL_OVERFLOW = 20012, ERR_FSAL_INTERRUPT = 20013, ERR_FSAL_BLOCKED = 20014, ERR_FSAL_TIMEOUT = 20015, ERR_FSAL_FILE_OPEN = 10046, ERR_FSAL_UNION_NOTSUPP = 10094, ERR_FSAL_IN_GRACE = 10095, ERR_FSAL_NO_ACE = 10096, ERR_FSAL_CROSS_JUNCTION = 10097, ERR_FSAL_BADNAME = 10098, } fsal_errors_t; /** * @brief The return status of FSAL calls. */ typedef struct fsal_status__ { fsal_errors_t major; /*< FSAL status code */ int minor; /*< Other error code (usually POSIX) */ } fsal_status_t; /****************************************************** * FSAL Returns macros ******************************************************/ /** * fsalstat (was ReturnCode) : * Macro for returning a fsal_status_t without trace nor stats increment. */ static inline fsal_status_t fsalstat(fsal_errors_t major, int minor) { fsal_status_t status = {major, minor}; return status; } /****************************************************** * FSAL Errors handling. ******************************************************/ /** Tests whether the returned status is erroneous. * Example : * if (FSAL_IS_ERROR(status = FSAL_call(...))) { * printf("ERROR status = %d, %d\n", status.major,status.minor); * } */ #define FSAL_IS_SUCCESS(_status_) ((_status_).major == ERR_FSAL_NO_ERROR) #define FSAL_IS_ERROR(_status_) (!FSAL_IS_SUCCESS(_status_)) /** * @brief File system dynamic info. */ typedef struct fsal_dynamicfsinfo__ { uint64_t total_bytes; uint64_t free_bytes; uint64_t avail_bytes; uint64_t total_files; uint64_t free_files; uint64_t avail_files; uint64_t maxread; uint64_t maxwrite; struct timespec time_delta; } fsal_dynamicfsinfo_t; /** * Status of FSAL operations */ /* quotas */ typedef struct fsal_quota__ { uint64_t bhardlimit; uint64_t bsoftlimit; uint64_t curblocks; uint64_t fhardlimit; uint64_t fsoftlimit; uint64_t curfiles; uint64_t btimeleft; uint64_t ftimeleft; uint64_t bsize; } fsal_quota_t; typedef enum { FSAL_QUOTA_BLOCKS = 1, FSAL_QUOTA_INODES = 2 } fsal_quota_type_t; /** * @brief Digest types */ typedef enum fsal_digesttype_t { /* NFS handles */ FSAL_DIGEST_NFSV3, FSAL_DIGEST_NFSV4, } fsal_digesttype_t; typedef enum { FSAL_OP_LOCKT, /*< test if this lock may be applied */ FSAL_OP_LOCK, /*< request a non-blocking lock */ FSAL_OP_LOCKB, /*< request a blocking lock (NEW) */ FSAL_OP_UNLOCK, /*< release a lock */ FSAL_OP_CANCEL /*< cancel a blocking lock (NEW) */ } fsal_lock_op_t; typedef enum { FSAL_LOCK_R, FSAL_LOCK_W, FSAL_NO_LOCK } fsal_lock_t; enum fsal_sle_type { FSAL_POSIX_LOCK, FSAL_LEASE_LOCK }; typedef struct fsal_lock_param_t { enum fsal_sle_type lock_sle_type; fsal_lock_t lock_type; uint64_t lock_start; uint64_t lock_length; bool lock_reclaim; } fsal_lock_param_t; typedef struct fsal_share_param_t { uint32_t share_access; uint32_t share_deny; bool share_reclaim; } fsal_share_param_t; typedef enum { FSAL_DELEG_NONE, FSAL_DELEG_RD, FSAL_DELEG_WR } fsal_deleg_t; typedef char fsal_verifier_t[NFS4_VERIFIER_SIZE]; /** * @brief Generic file handle. */ struct fsal_fd { /** The open and share mode etc. This MUST be first in every * file descriptor structure. */ fsal_openflags_t openflags; }; /** * @brief The ref counted share reservation state. * * Each field represents the count of instances of that flag being present * in a share reservation. * * There is a separate count of mandatory deny write flags so that they can be * enforced against all writes (non-mandatory deny write is only enforced * against indicated operations). */ struct fsal_share { unsigned int share_access_read; unsigned int share_access_write; unsigned int share_deny_read; unsigned int share_deny_write; /**< Count of mandatory share deny write */ unsigned int share_deny_write_mand; }; #endif /* _FSAL_TYPES_H */ /** @} */ nfs-ganesha-2.6.0/src/include/fsal_up.h000066400000000000000000000246641324272410200177360ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * *--------------------------------------- */ /** * @defgroup fsal_up Upcalls for the FSAL * @{ * * These functions allow a filesystem realization to modify the cache * and trigger recalls without it having to gain personal, intimate * knowledge of the rest of Ganesha. * * These calls are *synchronous*, meaning they immediately do whatever * they're going to do and return to the caller. They are intended to * be called from a notification or other thread. Specifically, this * means that you *must not* call layoutrecall from within layoutget. * * If you need to call one of these methods from within an FSAL * method, use the delayed executor interface in delayed_exec.h with a * delay of 0. If you don't want to share, you could have the FSAL * spawn a thread fridge of its own. * * If people find themselves needing it, generally, I'll rebuild the * asynchronous upcall interface on top of this synchronous one. */ /** * @file fsal_up.h * @brief Definitions for FSAL upcalls */ #ifndef FSAL_UP_H #define FSAL_UP_H #include "gsh_status.h" #include "fsal_api.h" enum { /* empty flags */ fsal_up_update_null = 0x0000, /* Update the filesize only if the new size is greater * than that currently set */ fsal_up_update_filesize_inc = 0x0001, /* Update the atime only if the new time is later than * the currently set time. */ fsal_up_update_atime_inc = 0x0002, /* Update the creation time only if the new time is * later than the currently set time. */ fsal_up_update_creation_inc = 0x0004, /* Update the ctime only if the new time is later * than that currently * set */ fsal_up_update_ctime_inc = 0x0008, /* Update the mtime only if the new time is later * than that currently set. */ fsal_up_update_mtime_inc = 0x0010, /* Update the chgtime only if the new time is later * than that currently set. */ fsal_up_update_chgtime_inc = 0x0020, /* Update the spaceused only if the new size is greater * than that currently set. */ fsal_up_update_spaceused_inc = 0x0040, /* The file link count is zero. */ fsal_up_nlink = 0x0080, }; /** * @brief Optional stuff for layoutreturn * @{ */ enum layoutrecall_howspec { layoutrecall_howspec_exactly, layoutrecall_howspec_complement, layoutrecall_not_specced }; struct layoutrecall_spec { enum layoutrecall_howspec how; union { clientid4 client; } u; }; /** @} */ static const uint32_t FSAL_UP_INVALIDATE_ATTRS = 0x01; static const uint32_t FSAL_UP_INVALIDATE_ACL = 0x02; static const uint32_t FSAL_UP_INVALIDATE_CONTENT = 0x04; static const uint32_t FSAL_UP_INVALIDATE_DIR_POPULATED = 0x08; static const uint32_t FSAL_UP_INVALIDATE_DIR_CHUNKS = 0x10; static const uint32_t FSAL_UP_INVALIDATE_CLOSE = 0x100; #define FSAL_UP_INVALIDATE_CACHE ( \ FSAL_UP_INVALIDATE_ATTRS | \ FSAL_UP_INVALIDATE_ACL | \ FSAL_UP_INVALIDATE_CONTENT | \ FSAL_UP_INVALIDATE_DIR_POPULATED | \ FSAL_UP_INVALIDATE_DIR_CHUNKS) /** * @brief Possible upcall functions * * This structure holds pointers to upcall functions. Each FSAL * should call through the vector in its export. * * For FSAL stacking, the 'higher' FSAL should copy its vector, * over-ride whatever methods it wishes, and pass the new vector to * the lower FSAL. It may then pass through, surround, or override as * it wishes. * * Note that all these functions take keys, not fsal object handles. * This is because the FSAL will always, by definition, be able to * know the key by which it identifies an object, but cannot know the * address of the handle stored in the cache. */ struct fsal_up_vector { /** The gsh_export this vector lives in */ struct gsh_export *up_gsh_export; /** The fsal_export this vector lives in */ struct fsal_export *up_fsal_export; /** ready to take upcalls condition */ bool up_ready; bool up_cancel; pthread_mutex_t up_mutex; pthread_cond_t up_cond; /** Invalidate some or all of a cache entry * * @param[in] vec Up ops vector * @param[in] obj The file to invalidate * @param[in] flags FSAL_UP_INVALIDATE_* * * @return FSAL status * */ fsal_status_t (*invalidate)(const struct fsal_up_vector *vec, struct gsh_buffdesc *obj, uint32_t flags); /** Update cached attributes * * @param[in] vec Up ops vector * @param[in] obj The file to update * @param[in] attr List of attributes to update. Note that the * @c type, @c fsid, @c fileid, @c rawdev, and * @c generation fields must not be updated and * the corresponding bits in the mask must not * be set, nor may the ATTR_RDATA_ERR bit be set. * @param[in] flags Flags requesting special update handling * */ fsal_status_t (*update)(const struct fsal_up_vector *vec, struct gsh_buffdesc *obj, struct attrlist *attr, uint32_t flags); /** Grant a lock to a client * * @param[in] vec Up ops vector * @param[in] file The file in question * @param[in] owner The lock owner * @param[in] lock_param A description of the lock * */ state_status_t (*lock_grant)(const struct fsal_up_vector *vec, struct gsh_buffdesc *file, void *owner, fsal_lock_param_t *lock_param); /** Signal lock availability * * @param[in] vec Up ops vector * @param[in] file The file in question * @param[in] owner The lock owner * @param[in] lock_param A description of the lock * */ state_status_t (*lock_avail)(const struct fsal_up_vector *vec, struct gsh_buffdesc *file, void *owner, fsal_lock_param_t *lock_param); /** Perform a layoutrecall on a single file * * @param[in] vec Up ops vector * @param[in] handle Handle on which the layout is held * @param[in] layout_type The type of layout to recall * @param[in] changed Whether the layout has changed and the * client ought to finish writes through MDS * @param[in] segment Segment to recall * @param[in] cookie A cookie returned with the return that * completely satisfies a recall * @param[in] spec Lets us be fussy about what clients we send * to. May be NULL. * */ state_status_t (*layoutrecall)(const struct fsal_up_vector *vec, struct gsh_buffdesc *handle, layouttype4 layout_type, bool changed, const struct pnfs_segment *segment, void *cookie, struct layoutrecall_spec *spec); /** Remove or change a deviceid * * @param[in] notify_type Change or remove * @param[in] layout_type The layout type affected * @param[in] devid The deviceid * @param[in] immediate Whether the change is immediate * */ state_status_t (*notify_device)(notify_deviceid_type4 notify_type, layouttype4 layout_type, struct pnfs_deviceid devid, bool immediate); /** Recall a delegation * * @param[in] vec Up ops vector * @param[in] handle Handle on which the delegation is held */ state_status_t (*delegrecall)(const struct fsal_up_vector *vec, struct gsh_buffdesc *handle); /** Invalidate some or all of a cache entry and close if open * * This version should NOT be used if an FSAL supports extended * operations, instead, the FSAL may directly close the file as * necessary. * * @param[in] vec Up ops vector * @param[in] obj The file to invalidate * @param[in] flags Flags governing invalidation * * @return FSAL status * */ fsal_status_t (*invalidate_close)(const struct fsal_up_vector *vec, struct gsh_buffdesc *obj, uint32_t flags); }; extern struct fsal_up_vector fsal_up_top; /** * @{ * @brief Asynchronous upcall wrappers */ fsal_status_t up_async_invalidate(struct fridgethr *fr, const struct fsal_up_vector *vec, struct gsh_buffdesc *obj, uint32_t flags, void (*cb)(void *, fsal_status_t), void *cb_arg); fsal_status_t up_async_update(struct fridgethr *fr, const struct fsal_up_vector *vec, struct gsh_buffdesc *obj, struct attrlist *attr, uint32_t flags, void (*cb)(void *, fsal_status_t), void *cb_arg); fsal_status_t up_async_lock_grant(struct fridgethr *fr, const struct fsal_up_vector *vec, struct gsh_buffdesc *file, void *owner, fsal_lock_param_t *lock_param, void (*cb)(void *, state_status_t), void *cb_arg); fsal_status_t up_async_lock_avail(struct fridgethr *fr, const struct fsal_up_vector *vec, struct gsh_buffdesc *file, void *owner, fsal_lock_param_t *lock_param, void (*cb)(void *, state_status_t), void *cb_arg); fsal_status_t up_async_layoutrecall(struct fridgethr *fr, const struct fsal_up_vector *vec, struct gsh_buffdesc *handle, layouttype4 layout_type, bool changed, const struct pnfs_segment *segment, void *cookie, struct layoutrecall_spec *spec, void (*cb)(void *, state_status_t), void *cb_arg); fsal_status_t up_async_notify_device(struct fridgethr *fr, const struct fsal_up_vector *vec, notify_deviceid_type4 notify_type, layouttype4 layout_type, struct pnfs_deviceid *devid, bool immediate, void (*cb)(void *, state_status_t), void *cb_arg); fsal_status_t up_async_delegrecall(struct fridgethr *fr, const struct fsal_up_vector *vec, struct gsh_buffdesc *handle, void (*cb)(void *, state_status_t), void *cb_arg); /** @} */ int async_delegrecall(struct fridgethr *fr, struct fsal_obj_handle *obj); void up_ready_init(struct fsal_up_vector *up_ops); void up_ready_set(struct fsal_up_vector *up_ops); void up_ready_wait(struct fsal_up_vector *up_ops); void up_ready_cancel(struct fsal_up_vector *up_ops); #endif /* FSAL_UP_H */ /** @} */ nfs-ganesha-2.6.0/src/include/gsh_config.h000066400000000000000000000363261324272410200204110ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup config Ganesha Configuration * * Ganesha configuration is contained in a global structure that is * populated with defaults, then modified from a configuration file. * This structure informs all behaviors of the daemon. * * @{ */ /** * @file nfs_core.h * @brief Configuration structure and defaults for NFS Ganesha */ #ifndef GSH_CONFIG_H #define GSH_CONFIG_H #include #include #include "nfs4.h" #include "gsh_rpc.h" /** * @brief An enumeration of protocols in the NFS family */ typedef enum protos { P_NFS, /*< NFS, of course. */ P_MNT, /*< Mount (for v3) */ P_NLM, /*< NLM (for v3) */ P_RQUOTA, /*< RQUOTA (for v3) */ P_NFS_VSOCK, /*< NFS over vmware, qemu vmci sockets */ P_NFS_RDMA, /*< NFS over RPC/RDMA */ P_COUNT /*< Number of protocols */ } protos; /** * @defgroup config_core Structure and defaults for NFS_Core_Param * * @{ */ /** * @brief Default NFS Port. */ #define NFS_PORT 2049 /** * @brief Default RQUOTA port. */ #define RQUOTA_PORT 875 /** * @brief Default value for core_param.nb_worker */ #define NB_WORKER_THREAD_DEFAULT 256 /** * @brief Default value for core_param.drc.tcp.npart */ #define DRC_TCP_NPART 1 /** * @brief Default value for core_param.drc.tcp.size */ #define DRC_TCP_SIZE 1024 /** * @brief Default value for core_param.drc.tcp.cachesz */ #define DRC_TCP_CACHESZ 127 /* make prime */ /** * @brief Default value for core_param.drc.tcp.hiwat */ #define DRC_TCP_HIWAT 64 /* 1/2(size) */ /** * @brief Default value for core_param.drc.tcp.recycle_npart */ #define DRC_TCP_RECYCLE_NPART 7 /** * @brief Default value for core_param.drc.tcp.expire_s */ #define DRC_TCP_RECYCLE_EXPIRE_S 600 /* 10m */ /** * @brief Default value for core_param.drc.tcp.checkstum */ #define DRC_TCP_CHECKSUM true /** * @brief Default value for core_param.drc.udp.npart */ #define DRC_UDP_NPART 7 /** * @brief Default value for core_param.drc.udp.size */ #define DRC_UDP_SIZE 32768 /** * @brief Default value for core_param.drc.udp.cachesz */ #define DRC_UDP_CACHESZ 599 /* make prime */ /** * @brief Default value for core_param.drc.udp.hiwat */ #define DRC_UDP_HIWAT 16384 /* 1/2(size) */ /** * @brief Default value for core_param.drc.udp.checksum */ #define DRC_UDP_CHECKSUM true /** * Default value for core_param.rpc.max_send_buffer_size */ #define NFS_DEFAULT_SEND_BUFFER_SIZE 1048576 /** * Default value for core_param.rpc.max_recv_buffer_size */ #define NFS_DEFAULT_RECV_BUFFER_SIZE 1048576 /** * @brief Support NFSv3 */ #define CORE_OPTION_NFSV3 0x00000001 /*< NFSv3 operations are supported */ /** * @brief Support NFSv4 */ #define CORE_OPTION_NFSV4 0x00000002 /*< NFSv4 operations are supported */ /** * @brief Support 9p */ #define CORE_OPTION_9P 0x00000004 /*< 9P operations are supported */ /** * @brief NFS AF_VSOCK */ #define CORE_OPTION_NFS_VSOCK 0x00000008 /*< AF_VSOCK NFS listener */ /** * @brief Support RPC/RDMA v1 */ #define CORE_OPTION_NFS_RDMA 0x00000010 /*< RPC/RDMA v1 NFS listener */ /** * @brief Support NFSv3 and NFSv4. */ #define CORE_OPTION_ALL_NFS_VERS (CORE_OPTION_NFSV3 | CORE_OPTION_NFSV4) /** * @brief Support all protocols */ #define CORE_OPTION_ALL_VERS (CORE_OPTION_NFSV3 | \ CORE_OPTION_NFSV4 | \ CORE_OPTION_NFS_VSOCK | \ CORE_OPTION_NFS_RDMA | \ CORE_OPTION_9P) typedef struct nfs_core_param { /** An array of port numbers, one for each protocol. Set by the NFS_Port, MNT_Port, NLM_Port, and Rquota_Port options. */ uint16_t port[P_COUNT]; /** The address to which to bind for our listening port. IPv4 only, for now. Set by the Bind_Addr option. */ struct sockaddr_storage bind_addr; /** An array of RPC program numbers. The correct values, by default, they may be set to incorrect values with the NFS_Program, MNT_Program, NLM_Program, and Rquota_Program. It is debatable whether this is a worthwhile option to have. */ uint32_t program[P_COUNT]; /** Number of worker threads. Set to NB_WORKER_DEFAULT by default and changed with the Nb_Worker option. */ uint32_t nb_worker; /** For NFSv3, whether to drop rather than reply to requests yielding I/O errors. True by default and settable with Drop_IO_Errors. As this generally results in client retry, this seems like a dubious idea. */ bool drop_io_errors; /** For NFSv3, whether to drop rather than reply to requests yielding invalid argument errors. False by default and settable with Drop_Inval_Errors. As this generally results in client retry, this seems like a really awful idea. */ bool drop_inval_errors; /** For NFSv3, whether to drop rather than reply to requests yielding delay errors. True by default and settable with Drop_Delay_Errors. As this generally results in client retry and there is no NFSERR_DELAY, this seems like an excellent idea. */ bool drop_delay_errors; /** Parameters controlling the Duplicate Request Cache. */ struct { /** Whether to disable the DRC entirely. Defaults to false, settable by DRC_Disabled. */ bool disabled; /* Parameters controlling TCP specific DRC behavior. */ struct { /** Number of partitions in the tree for the TCP DRC. Defaults to DRC_TCP_NPART, settable by DRC_TCP_Npart. */ uint32_t npart; /** Maximum number of requests in a transport's DRC. Defaults to DRC_TCP_SIZE and settable by DRC_TCP_Size. */ uint32_t size; /** Number of entries in the O(1) front-end cache to a TCP Duplicate Request Cache. Defaults to DRC_TCP_CACHESZ and settable by DRC_TCP_Cachesz. */ uint32_t cachesz; /** High water mark for a TCP connection's DRC at which to start retiring entries if we can. Defaults to DRC_TCP_HIWAT and settable by DRC_TCP_Hiwat. */ uint32_t hiwat; /** Number of partitions in the recycle tree that holds per-connection DRCs so they can be used on reconnection (or recycled.) Defaults to DRC_TCP_RECYCLE_NPART and settable by DRC_TCP_Recycle_Npart. */ uint32_t recycle_npart; /** How long to wait (in seconds) before freeing the DRC of a disconnected client. Defaults to DRC_TCP_RECYCLE_EXPIRE_S and settable by DRC_TCP_Recycle_Expire_S. */ uint32_t recycle_expire_s; /** Whether to use a checksum to match requests as well as the XID. Defaults to DRC_TCP_CHECKSUM and settable by DRC_TCP_Checksum. */ bool checksum; } tcp; /** Parameters controlling UDP DRC behavior. */ struct { /** Number of partitions in the tree for the UDP DRC. Defaults to DRC_UDP_NPART, settable by DRC_UDP_Npart. */ uint32_t npart; /** Maximum number of requests in the UDP DRC. Defaults to DRC_UDP_SIZE and settable by DRC_UDP_Size. */ uint32_t size; /** Number of entries in the O(1) front-end cache to the UDP Duplicate Request Cache. Defaults to DRC_UDP_CACHESZ and settable by DRC_UDP_Cachesz. */ uint32_t cachesz; /** High water mark for the UDP DRC at which to start retiring entries if we can. Defaults to DRC_UDP_HIWAT and settable by DRC_UDP_Hiwat. */ uint32_t hiwat; /** Whether to use a checksum to match requests as well as the XID. Defaults to DRC_UDP_CHECKSUM and settable by DRC_UDP_Checksum. */ bool checksum; } udp; } drc; /** Parameters affecting the relation with TIRPC. */ struct { /** Maximum number of connections for TIRPC. Defaults to 1024 and settable by RPC_Max_Connections. */ uint32_t max_connections; /** Size of RPC send buffer. Defaults to NFS_DEFAULT_SEND_BUFFER_SIZE and is settable by MaxRPCSendBufferSize. */ uint32_t max_send_buffer_size; /** Size of RPC receive buffer. Defaults to NFS_DEFAULT_RECV_BUFFER_SIZE and is settable by MaxRPCRecvBufferSize. */ uint32_t max_recv_buffer_size; /** Idle timeout (seconds). Defaults to 5m */ uint32_t idle_timeout_s; /** TIRPC ioq max simultaneous io threads. Defaults to 200 and settable by RPC_Ioq_ThrdMax. */ uint32_t ioq_thrd_max; struct { /** Partitions in GSS ctx cache table (default 13). */ uint32_t ctx_hash_partitions; /** Max GSS contexts in cache (i.e., * max GSS clients, default 16K) */ uint32_t max_ctx; /** Max entries to expire in one idle * check (default 200) */ uint32_t max_gc; } gss; } rpc; /** Polling interval for blocked lock polling thread. */ time_t blocked_lock_poller_interval; /** Protocols to support. Should probably be renamed. Defaults to CORE_OPTION_ALL_VERS and is settable with NFS_Protocols (as a comma-separated list of 3 and 4.) */ unsigned int core_options; /** Whether to use the supplied name rather than the IP address in NSM operations. Settable with NSM_Use_Caller_Name. */ bool nsm_use_caller_name; /** Whether this Ganesha is part of a cluster of Ganeshas. This is somewhat vendor-specific and should probably be moved somewhere else. Settable with Clustered. */ bool clustered; /** Whether to support the Network Lock Manager protocol. Defaults to true and is settable with Enable_NLM. */ bool enable_NLM; /** Whether to support the Remote Quota protocol. Defaults to true and is settable with Enable_RQUOTA. */ bool enable_RQUOTA; /** Whether to collect NFS stats. Defaults to true. */ bool enable_NFSSTATS; /** Whether to use fast stats. Defaults to false. */ bool enable_FASTSTATS; /** Whether to collect FSAL stats. Defaults to false. */ bool enable_FSALSTATS; /** Whether tcp sockets should use SO_KEEPALIVE */ bool enable_tcp_keepalive; /** Maximum number of TCP probes before dropping the connection */ uint32_t tcp_keepcnt; /** Idle time before TCP starts to send keepalive probes */ uint32_t tcp_keepidle; /** Time between each keepalive probe */ uint32_t tcp_keepintvl; /** Whether to use short NFS file handle to accommodate VMware NFS client. Enable this if you have a VMware NFSv3 client. VMware NFSv3 client has a max limit of 56 byte file handles! Defaults to false. */ bool short_file_handle; /** How long the server will trust information it got by calling getgroups() when "Manage_Gids = TRUE" is used in a export entry. */ time_t manage_gids_expiration; /** Path to the directory containing server specific modules. In particular, this is where FSALs live. */ char *ganesha_modules_loc; /** Frequency of dbus health heartbeat in ms. Set to 0 to disable */ uint32_t heartbeat_freq; /** Whether to use device major/minor for fsid. Defaults to false. */ bool fsid_device; /** Whether to use Pseudo (true) or Path (false) for NFS v3 and 9P mounts. */ bool mount_path_pseudo; /** DBus name prefix. Required if one wants to run multiple ganesha instances on single host. The prefix should be different for every ganesha instance. If this is set, dbus name will be .org.ganesha.nfsd */ char *dbus_name_prefix; } nfs_core_parameter_t; /** @} */ /** * @defgroup config_nfsv4 Structure and defaults for NFSv4 * * @{ */ /** * @brief Default value for lease_lifetime */ #define LEASE_LIFETIME_DEFAULT 60 /** * @brief Default value for grace period */ #define GRACE_PERIOD_DEFAULT 90 /** * @brief Default value of domainname. */ #define DOMAINNAME_DEFAULT "localdomain" /** * @brief Default value of idmapconf. */ #define IDMAPCONF_DEFAULT "/etc/idmapd.conf" /** * @brief Default value of deleg_recall_retry_delay. */ #define DELEG_RECALL_RETRY_DELAY_DEFAULT 1 /** * @brief Default value of recovery_backend. */ #define RECOVERY_BACKEND_DEFAULT "fs" /** * @brief NFSv4 minor versions */ #define NFSV4_MINOR_VERSION_ZERO (1 << 0) #define NFSV4_MINOR_VERSION_ONE (1 << 1) #define NFSV4_MINOR_VERSION_TWO (1 << 2) #define NFSV4_MINOR_VERSION_ALL (NFSV4_MINOR_VERSION_ZERO | \ NFSV4_MINOR_VERSION_ONE | \ NFSV4_MINOR_VERSION_TWO) typedef struct nfs_version4_parameter { /** Whether to disable the NFSv4 grace period. Defaults to false and settable with Graceless. */ bool graceless; /** The NFSv4 lease lifetime. Defaults to LEASE_LIFETIME_DEFAULT and is settable with Lease_Lifetime. */ uint32_t lease_lifetime; /** The NFS grace period. Defaults to GRACE_PERIOD_DEFAULT and is settable with Grace_Period. */ uint32_t grace_period; /** Domain to use if we aren't using the nfsidmap. Defaults to DOMAINNAME_DEFAULT and is set with DomainName. */ char *domainname; /** Path to the idmap configuration file. Defaults to IDMAPCONF_DEFAULT, settable with IdMapConf */ char *idmapconf; /** Whether to use local password (PAM, on Linux) rather than nfsidmap. Defaults to false if nfsidmap support is compiled in and true if it isn't. Settable with UseGetpwnam. */ bool use_getpwnam; /** Whether to allow bare numeric IDs in NFSv4 owner and group identifiers. Defaults to true and is settable with Allow_Numeric_Owners. */ bool allow_numeric_owners; /** Whether to ONLY use bare numeric IDs in NFSv4 owner and group identifiers. Defaults to false and is settable with Only_Numeric_Owners. NB., this is permissible for a server implementation (RFC 5661). */ bool only_numeric_owners; /** Whether to allow delegations. Defaults to false and settable with Delegations */ bool allow_delegations; /** Delay after which server will retry a recall in case of failures */ uint32_t deleg_recall_retry_delay; /** Whether this a pNFS MDS server. Defaults to false */ bool pnfs_mds; /** Whether this a pNFS DS server. Defaults to false */ bool pnfs_ds; /** Recovery backend */ char *recovery_backend; /** List of supported NFSV4 minor versions */ unsigned int minor_versions; /** Number of allowed slots in the 4.1 slot table */ uint32_t nb_slots; } nfs_version4_parameter_t; /** @} */ typedef struct nfs_param { /** NFS Core parameters, settable in the NFS_Core_Param stanza. */ nfs_core_parameter_t core_param; /** NFSv4 specific parameters, settable in the NFSv4 stanza. */ nfs_version4_parameter_t nfsv4_param; #ifdef _HAVE_GSSAPI /** kerberos configuration. Settable in the NFS_KRB5 stanza. */ nfs_krb5_parameter_t krb5_param; #endif /* _HAVE_GSSAPI */ } nfs_parameter_t; extern nfs_parameter_t nfs_param; #endif /* GSH_CONFIG_H */ /** @} */ nfs-ganesha-2.6.0/src/include/gsh_dbus.h000066400000000000000000000126331324272410200200740ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) 2012, The Linux Box Corporation * Contributor : Matt Benjamin * * Some portions Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ #ifndef GSH_DBUS_H #define GSH_DBUS_H #include "config.h" #include #include "log.h" #ifdef _USE_9P #include "9p_types.h" #endif /** * * \file gsh_dbus.h * \author Matt Benjamin and Lee Dobryden * \brief Low-level DBUS message server and callout framework. * * \section DESCRIPTION * * This module implements a very simple service provider interface for a * shared DBUS event loop. * * To use the service, a client implements the gsh_dbus_method_t interface, * then registers its callout routine(s) with gsh_dbus_register_method. */ /** * @brief Introspection contents and method dispatches. * * An interface array is passed when a path is registered by * a facility inside the server. A NULL interface pointer * terminates the list. * * Each interface has NULL terminated arrays of properties, methods, * and signals. */ #define HEARTBEAT_NAME "heartbeat" #define DBUS_PATH "/org/ganesha/nfsd/" #define DBUS_ADMIN_IFACE "org.ganesha.nfsd.admin" #define HEARTBEAT_ARG \ { \ .name = "isHealthy", \ .type = "b", \ .direction = "out" \ } #define STATUS_REPLY \ { \ .name = "status", \ .type = "b", \ .direction = "out"\ }, \ { \ .name = "error", \ .type = "s", \ .direction = "out"\ } #define MESSAGE_REPLY \ { \ .name = "message", \ .type = "s", \ .direction = "out" \ } #define END_ARG_LIST {NULL, NULL, NULL} #define IPADDR_ARG \ { \ .name = "ipaddr",\ .type = "s", \ .direction = "in"\ } #define ID_ARG \ { \ .name = "id", \ .type = "q", \ .direction = "in" \ } #define PATH_ARG \ { \ .name = "path", \ .type = "s", \ .direction = "in" \ } #define EXPR_ARG \ { \ .name = "expr", \ .type = "s", \ .direction = "in" \ } #define FSAL_ARG \ { \ .name = "fsal", \ .type = "s", \ .direction = "in" \ } #define STAT_TYPE_ARG \ { \ .name = "stat_type", \ .type = "s", \ .direction = "in" \ } /* Properties list helper macros */ #define END_ARG_LIST {NULL, NULL, NULL} #define END_PROPS_LIST {NULL, DBUS_PROP_READ, "", NULL, NULL} typedef enum { DBUS_PROP_READ = 0, DBUS_PROP_WRITE, DBUS_PROP_READWRITE } dbus_prop_access_t; struct gsh_dbus_prop { const char *name; dbus_prop_access_t access; const char *type; bool (*get)(DBusMessageIter *reply); bool (*set)(DBusMessageIter *args); }; struct gsh_dbus_arg { const char *name; const char *type; const char *direction; /* not used for signals */ }; struct gsh_dbus_method { const char *name; bool (*method)(DBusMessageIter *args, DBusMessage *reply, DBusError *error); struct gsh_dbus_arg args[]; }; struct gsh_dbus_signal { const char *name; bool (*signal)(DBusMessageIter *args, DBusMessage *reply); struct gsh_dbus_arg args[]; }; struct gsh_dbus_interface { const char *name; bool signal_props; struct gsh_dbus_prop **props; struct gsh_dbus_method **methods; struct gsh_dbus_signal **signals; }; /** * @brief Default value for heartbeat frequency in ms */ #define HEARTBEAT_FREQ_DEFAULT 1000 #define BCAST_FOREVER -1 #define BCAST_STATUS_OK 0x00 #define BCAST_STATUS_WARN 0x01 #define BCAST_STATUS_FATAL 0x02 typedef int (*dbus_bcast_callback)(void *); struct dbus_bcast_item { struct timespec next_bcast_time; uint32_t bcast_interval; uint32_t count; void *bcast_arg; dbus_bcast_callback bcast_callback; struct glist_head dbus_bcast_q; }; struct dbus_bcast_item *add_dbus_broadcast( dbus_bcast_callback bcast_callback, void *bcast_arg, uint32_t bcast_interval, int count); void del_dbus_broadcast(struct dbus_bcast_item *to_remove); /* heartbeat function call back */ int dbus_heartbeat_cb(void *arg); void init_heartbeat(void); void gsh_dbus_pkginit(void); void gsh_dbus_pkgshutdown(void); void *gsh_dbus_thread(void *arg); /* callout method */ void dbus_append_timestamp(DBusMessageIter *iterp, struct timespec *ts); void dbus_status_reply(DBusMessageIter *iter, bool success, char *errormsg); int32_t gsh_dbus_register_path(const char *name, struct gsh_dbus_interface **interfaces); int gsh_dbus_broadcast(char *obj_name, char *int_name, char *sig_name, int type, ...); /* more to come */ #ifdef _USE_9P bool arg_9p_op(DBusMessageIter *args, u8 *opcode, char **errormsg); #endif #endif /* GSH_DBUS_H */ nfs-ganesha-2.6.0/src/include/gsh_intrinsic.h000066400000000000000000000027721324272410200211440ustar00rootroot00000000000000/* * Copyright (C) 2010, Linux Box Corporation * All Rights Reserved * * Contributor: Matt Benjamin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ /** * * \file gsh_intrinsic.h * \author Matt Benjamin * \brief Compiler intrinsics * * \section DESCRIPTION * * Compiler intrinsics. */ #ifndef _GSH_INTRINSIC_H #define _GSH_INTRINSIC_H #if __GLIBC__ #ifndef likely #define likely(x) __builtin_expect (!!(x), 1) #define unlikely(x) __builtin_expect (!!(x), 0) #endif #else #ifndef likely #define likely(x) (x) #define unlikely(x) (x) #endif #endif #if defined(__PPC64__) #define GSH_CACHE_LINE_SIZE 128 #else /* __x86_64__, __i386__ and others */ #define GSH_CACHE_LINE_SIZE 64 #endif #define GSH_CACHE_PAD(_n) char __pad ## _n[GSH_CACHE_LINE_SIZE] #endif /* _GSH_INTRINSIC_H */ nfs-ganesha-2.6.0/src/include/gsh_list.h000066400000000000000000000165441324272410200201170ustar00rootroot00000000000000/* * Copyright IBM Corporation, 2010 * Contributor: Aneesh Kumar K.v * * * This software is a server that implements the NFS protocol. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- * * */ #ifndef _GANESHA_LIST_H #define _GANESHA_LIST_H #include struct glist_head { struct glist_head *next; struct glist_head *prev; }; /* * @brief Compare routine that is used by glist_insert_sorted * * This routine can be defined by the calling function * to enable a sorted insert * * @param struct glist_head *: The first element to compare * @param struct glist_head *: The second element to compare * * @return negative if the 1st element should appear before the 2nd element * 0 if the 1st and 2nd element are equal * positive if the 1st element should appear after the 2nd element */ typedef int (*glist_compare) (struct glist_head *, struct glist_head *); /** * @brief List head initialization * * These macros and functions are only for list heads, * not nodes. The head always points to something and * if the list is empty, it points to itself. */ #define GLIST_HEAD_INIT(name) { &(name), &(name) } #define GLIST_HEAD(name) \ struct glist_head name = GLIST_HEAD_INIT(name) static inline void glist_init(struct glist_head *head) { /* XXX glist_init? */ head->next = head; head->prev = head; } /* Add the new element between left and right */ static inline void __glist_add(struct glist_head *left, struct glist_head *right, struct glist_head *elt) { elt->prev = left; elt->next = right; left->next = elt; right->prev = elt; } static inline void glist_add_tail(struct glist_head *head, struct glist_head *elt) { __glist_add(head->prev, head, elt); } /* add after the specified entry*/ static inline void glist_add(struct glist_head *head, struct glist_head *elt) { __glist_add(head, head->next, elt); } static inline void glist_del(struct glist_head *node) { struct glist_head *left = node->prev; struct glist_head *right = node->next; if (left != NULL) left->next = right; if (right != NULL) right->prev = left; node->next = NULL; node->prev = NULL; } /** * @brief Test if the list in this head is empty */ static inline int glist_empty(struct glist_head *head) { return head->next == head; } /** * @brief Test if this node is not on a list. * * NOT to be confused with glist_empty which is just * for heads. We poison with NULL for disconnected nodes. */ static inline int glist_null(struct glist_head *head) { return (head->next == NULL) && (head->prev == NULL); } static inline void glist_add_list_tail(struct glist_head *list, struct glist_head *elt) { struct glist_head *first = elt->next; struct glist_head *last = elt->prev; if (glist_empty(elt)) { /* nothing to add */ return; } first->prev = list->prev; list->prev->next = first; last->next = list; list->prev = last; } /* Move all of src onto the tail of tgt. Clears src. */ static inline void glist_splice_tail(struct glist_head *tgt, struct glist_head *src) { if (glist_empty(src)) return; src->next->prev = tgt->prev; tgt->prev->next = src->next; src->prev->next = tgt; tgt->prev = src->prev; glist_init(src); } static inline void glist_swap_lists(struct glist_head *l1, struct glist_head *l2) { struct glist_head temp; if (glist_empty(l1)) { /* l1 was empty, so splice tail will accomplish swap. */ glist_splice_tail(l1, l2); return; } if (glist_empty(l2)) { /* l2 was empty, so reverse splice tail will accomplish swap. */ glist_splice_tail(l2, l1); return; } /* Both lists are non-empty */ /* First swap the list pointers. */ temp = *l1; *l1 = *l2; *l2 = temp; /* Then fixup first entry in each list prev to point to it's new head */ l1->next->prev = l1; l2->next->prev = l2; /* And fixup the last entry in each list next to point to it's new head */ l1->prev->next = l1; l2->prev->next = l2; } /** * @brief Split list list1 into list2 at element. * * @note list2 is expected to be empty. list1 is expected to be non-empty (i.e. * element is NOT list1). * * @param[in,out] list1 Source list. * @param[in,out] list2 Destination list. * @param[in,out] element List element to become first element in list2. * */ static inline void glist_split(struct glist_head *list1, struct glist_head *list2, struct glist_head *element) { /* Set up list2 to contain element to the end. */ list2->next = element; list2->prev = list1->prev; /* Fixup the last element of list1 to be the last element of list2, even * if it was element. */ list2->prev->next = list2; /* Now fixup list1 even if element was first element of list1. */ list1->prev = element->prev; /* Now fixup prev of element, even if element was first element of * list1. */ element->prev->next = list1; /* Now fixup element */ element->prev = list2; } #define glist_for_each(node, head) \ for (node = (head)->next; node != head; node = node->next) #define glist_for_each_next(start, node, head) \ for (node = (start)->next; node != head; node = node->next) static inline size_t glist_length(struct glist_head *head) { size_t length = 0; struct glist_head *dummy = NULL; glist_for_each(dummy, head) { ++length; } return length; } #define container_of(addr, type, member) ({ \ const typeof(((type *) 0)->member) * __mptr = (addr); \ (type *)((char *) __mptr - offsetof(type, member)); }) #define glist_first_entry(head, type, member) \ ((head)->next != (head) ? \ container_of((head)->next, type, member) : NULL) #define glist_last_entry(head, type, member) \ ((head)->prev != (head) ? \ container_of((head)->prev, type, member) : NULL) #define glist_entry(node, type, member) \ container_of(node, type, member) #define glist_for_each_safe(node, noden, head) \ for (node = (head)->next, noden = node->next; \ node != (head); \ node = noden, noden = node->next) #define glist_for_each_next_safe(start, node, noden, head) \ for (node = (start)->next, noden = node->next; \ node != (head); \ node = noden, noden = node->next) /* Return the next entry in the list after node if any. */ #define glist_next_entry(head, type, member, node) \ ((node)->next != (head) ? \ container_of((node)->next, type, member) : NULL) static inline void glist_insert_sorted(struct glist_head *head, struct glist_head *elt, glist_compare compare) { struct glist_head *next = NULL; if (glist_empty(head)) { glist_add_tail(head, elt); return; } glist_for_each(next, head) { if (compare(next, elt) > 0) break; } __glist_add(next->prev, next, elt); } #endif /* _GANESHA_LIST_H */ nfs-ganesha-2.6.0/src/include/gsh_lttng/000077500000000000000000000000001324272410200201115ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/include/gsh_lttng/fsal_mem.h000066400000000000000000000244241324272410200220530ustar00rootroot00000000000000#undef TRACEPOINT_PROVIDER #define TRACEPOINT_PROVIDER fsalmem #if !defined(GANESHA_LTTNG_FSALMEM_TP_H) || \ defined(TRACEPOINT_HEADER_MULTI_READ) #define GANESHA_LTTNG_FSALMEM_TP_H #include #include /** * @brief Trace an allocation of an obj * * @param[in] function Name of function taking ref * @param[in] line Line number of call * @param[in] obj Address of obj * @param[in] name File name */ TRACEPOINT_EVENT( fsalmem, mem_alloc, TP_ARGS(const char *, function, int, line, void *, obj, const char *, name), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, obj, obj) ctf_string(name, name) ) ) TRACEPOINT_LOGLEVEL( fsalmem, mem_alloc, TRACE_INFO) /** * @brief Trace a free of an obj * * @param[in] function Name of function releasing ref * @param[in] line Line number of call * @param[in] obj Address of obj * @param[in] name File name */ TRACEPOINT_EVENT( fsalmem, mem_free, TP_ARGS(const char *, function, int, line, void *, obj, const char *, name), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, obj, obj) ctf_string(name, name) ) ) TRACEPOINT_LOGLEVEL( fsalmem, mem_free, TRACE_INFO) /** * @brief Trace a lookup of an obj * * @param[in] function Name of function releasing ref * @param[in] line Line number of call * @param[in] obj Address of obj * @param[in] name File name */ TRACEPOINT_EVENT( fsalmem, mem_lookup, TP_ARGS(const char *, function, int, line, void *, obj, const char *, name), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, obj, obj) ctf_string(name, name) ) ) TRACEPOINT_LOGLEVEL( fsalmem, mem_lookup, TRACE_INFO) /** * @brief Trace a failed free of an obj * * @param[in] function Name of function releasing ref * @param[in] line Line number of call * @param[in] obj Address of obj * @param[in] inavl True if inavl */ TRACEPOINT_EVENT( fsalmem, mem_inuse, TP_ARGS(const char *, function, int, line, void *, obj, int, numlinks, int, inavl), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, obj, obj) ctf_integer(int, numlinks, numlinks) ctf_integer(int, inavl, inavl) ) ) TRACEPOINT_LOGLEVEL( fsalmem, mem_inuse, TRACE_INFO) /** * @brief Trace a getattrs call * * @param[in] function Name of function * @param[in] line Line number of call * @param[in] obj Address of obj * @param[in] name File name * @param[in] size Size of file * @param[in] numlinks Number of links * @param[in] chg Change counter */ TRACEPOINT_EVENT( fsalmem, mem_getattrs, TP_ARGS(const char *, function, int, line, void *, obj, const char *, name, uint64_t, size, uint32_t, numlinks, uint64_t, chg), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, obj, obj) ctf_string(name, name) ctf_integer(uint64_t, size, size) ctf_integer(uint32_t, numlinks, numlinks) ctf_integer(uint64_t, chg, chg) ) ) TRACEPOINT_LOGLEVEL( fsalmem, mem_getattrs, TRACE_INFO) /** * @brief Trace a setattrs call * * @param[in] function Name of function * @param[in] line Line number of call * @param[in] obj Address of obj * @param[in] name File name * @param[in] size Size of file * @param[in] numlinks Number of links * @param[in] chg Change counter */ TRACEPOINT_EVENT( fsalmem, mem_setattrs, TP_ARGS(const char *, function, int, line, void *, obj, const char *, name, uint64_t, size, uint32_t, numlinks, uint64_t, chg), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, obj, obj) ctf_string(name, name) ctf_integer(uint64_t, size, size) ctf_integer(uint32_t, numlinks, numlinks) ctf_integer(uint64_t, chg, chg) ) ) TRACEPOINT_LOGLEVEL( fsalmem, mem_setattrs, TRACE_INFO) /** * @brief Trace a write call * * @param[in] function Name of function * @param[in] line Line number of call * @param[in] obj Address of obj * @param[in] name File name * @param[in] size Size of file * @param[in] dsize Data size of file */ TRACEPOINT_EVENT( fsalmem, mem_write, TP_ARGS(const char *, function, int, line, void *, obj, const char *, name, void *, state, uint64_t, size, uint64_t, dsize), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, obj, obj) ctf_string(name, name) ctf_integer_hex(void *, state, state) ctf_integer(uint64_t, size, size) ctf_integer(uint64_t, dsize, dsize) ) ) TRACEPOINT_LOGLEVEL( fsalmem, mem_write, TRACE_INFO) /** * @brief Trace a read call * * @param[in] function Name of function * @param[in] line Line number of call * @param[in] obj Address of obj * @param[in] name File name * @param[in] size Size of file * @param[in] dsize Data size of file */ TRACEPOINT_EVENT( fsalmem, mem_read, TP_ARGS(const char *, function, int, line, void *, obj, const char *, name, void *, state, uint64_t, size, uint64_t, dsize), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, obj, obj) ctf_string(name, name) ctf_integer_hex(void *, state, state) ctf_integer(uint64_t, size, size) ctf_integer(uint64_t, dsize, dsize) ) ) TRACEPOINT_LOGLEVEL( fsalmem, mem_read, TRACE_INFO) /** * @brief Trace an open call * * @param[in] function Name of function * @param[in] line Line number of call * @param[in] obj Address of obj * @param[in] name File name * @param[in] state Address of state * @param[in] truncated True if truncated * @param[in] setattrs Pointer of setattrs */ TRACEPOINT_EVENT( fsalmem, mem_open, TP_ARGS(const char *, function, int, line, void *, obj, const char *, name, void *, state, uint32_t, truncated, uint32_t, setattrs), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, obj, obj) ctf_string(name, name) ctf_integer_hex(void *, state, state) ctf_integer(uint32_t, truncated, truncated) ctf_integer(uint32_t, setattrs, setattrs) ) ) TRACEPOINT_LOGLEVEL( fsalmem, mem_open, TRACE_INFO) /** * @brief Trace a close call * * @param[in] function Name of function * @param[in] line Line number of call * @param[in] obj Address of obj * @param[in] name File name * @param[in] state Address of state * @param[in] truncated True if truncated * @param[in] setattrs Pointer of setattrs */ TRACEPOINT_EVENT( fsalmem, mem_close, TP_ARGS(const char *, function, int, line, void *, obj, const char *, name, void *, state), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, obj, obj) ctf_string(name, name) ctf_integer_hex(void *, state, state) ) ) TRACEPOINT_LOGLEVEL( fsalmem, mem_close, TRACE_INFO) /** * @brief Trace an creat_handle * * @param[in] function Name of function * @param[in] line Line number of call * @param[in] obj Address of obj * @param[in] name File name */ TRACEPOINT_EVENT( fsalmem, mem_create_handle, TP_ARGS(const char *, function, int, line, void *, obj, const char *, name), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, obj, obj) ctf_string(name, name) ) ) TRACEPOINT_LOGLEVEL( fsalmem, mem_create_handle, TRACE_INFO) /** * @brief Trace an alloc_state * * @param[in] function Name of function * @param[in] line Line number of call * @param[in] state Address of state */ TRACEPOINT_EVENT( fsalmem, mem_alloc_state, TP_ARGS(const char *, function, int, line, void *, state), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, state, state) ) ) TRACEPOINT_LOGLEVEL( fsalmem, mem_alloc_state, TRACE_INFO) /** * @brief Trace a rename * * @param[in] function Name of function * @param[in] line Line number of call * @param[in] olddir Name of old directory * @param[in] oldname Name of old file * @param[in] newdir Name of new directory * @param[in] newname Name of new file */ TRACEPOINT_EVENT( fsalmem, mem_rename, TP_ARGS(const char *, function, int, line, void *, obj, const char *, olddir, const char *, oldname, const char *, newdir, const char *, newname), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, obj, obj) ctf_string(olddir, olddir) ctf_string(oldname, oldname) ctf_string(newdir, newdir) ctf_string(newname, newname) ) ) TRACEPOINT_LOGLEVEL( fsalmem, mem_rename, TRACE_INFO) /** * @brief Trace a link * * @param[in] function Name of function * @param[in] line Line number of call * @param[in] olddir Name of old directory * @param[in] oldname Name of old file * @param[in] newdir Name of new directory * @param[in] newname Name of new file */ TRACEPOINT_EVENT( fsalmem, mem_link, TP_ARGS(const char *, function, int, line, void *, dir, const char *, dirname, void *, file, const char *, filename, const char *, newname, uint32_t, numlinks), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, dir, dir) ctf_string(dirname, dirname) ctf_integer_hex(void *, file, file) ctf_string(filename, filename) ctf_string(newname, newname) ctf_integer(uint32_t, numlinks, numlinks) ) ) TRACEPOINT_LOGLEVEL( fsalmem, mem_link, TRACE_INFO) /** * @brief Trace a unlink * * @param[in] function Name of function * @param[in] line Line number of call * @param[in] olddir Name of old directory * @param[in] oldname Name of old file * @param[in] newdir Name of new directory * @param[in] newname Name of new file */ TRACEPOINT_EVENT( fsalmem, mem_unlink, TP_ARGS(const char *, function, int, line, void *, dir, const char *, dirname, void *, file, const char *, filename, uint32_t, numlinks), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, dir, dir) ctf_string(dirname, dirname) ctf_integer_hex(void *, file, file) ctf_string(filename, filename) ctf_integer(uint32_t, numlinks, numlinks) ) ) TRACEPOINT_LOGLEVEL( fsalmem, mem_unlink, TRACE_INFO) #endif /* GANESHA_LTTNG_FSALMEM_TP_H */ #undef TRACEPOINT_INCLUDE #define TRACEPOINT_INCLUDE "gsh_lttng/fsal_mem.h" #include nfs-ganesha-2.6.0/src/include/gsh_lttng/logger.h000066400000000000000000000015111324272410200215370ustar00rootroot00000000000000 #undef TRACEPOINT_PROVIDER #define TRACEPOINT_PROVIDER ganesha_logger #if !defined(GANESHA_LTTNG_LOG_TP_H) || defined(TRACEPOINT_HEADER_MULTI_READ) #define GANESHA_LTTNG_LOG_TP_H #include TRACEPOINT_EVENT( ganesha_logger, log, TP_ARGS(unsigned char, component, unsigned char, level, const char *, file, unsigned int, line, const char *, function, char *, message), TP_FIELDS( ctf_integer(unsigned char, component, component) ctf_integer(unsigned char, level, level) ctf_string(file, file) ctf_integer(unsigned int, line, line) ctf_string(fnc, function) ctf_string(msg, message) ) ) TRACEPOINT_LOGLEVEL( ganesha_logger, log, TRACE_INFO) #endif /* GANESHA_LTTNG_LOG_TP_H */ #undef TRACEPOINT_INCLUDE #define TRACEPOINT_INCLUDE "gsh_lttng/logger.h" #include nfs-ganesha-2.6.0/src/include/gsh_lttng/mdcache.h000066400000000000000000000146601324272410200216550ustar00rootroot00000000000000 #undef TRACEPOINT_PROVIDER #define TRACEPOINT_PROVIDER mdcache #if !defined(GANESHA_LTTNG_MDCACHE_TP_H) || \ defined(TRACEPOINT_HEADER_MULTI_READ) #define GANESHA_LTTNG_MDCACHE_TP_H #include /** * @brief Trace an increase in refcount of an entry * * @param[in] function Name of function taking ref * @param[in] line Line number of call * @param[in] entry Address of entry * @param[in] refcnt Refcount after increase */ TRACEPOINT_EVENT( mdcache, mdc_lru_ref, TP_ARGS(const char *, function, int, line, void *, entry, int32_t, refcnt), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, entry, entry) ctf_integer(int32_t, refcnt, refcnt) ) ) TRACEPOINT_LOGLEVEL( mdcache, mdc_lru_ref, TRACE_INFO) /** * @brief Trace a decrease in refcount of an entry * * @param[in] function Name of function releasing ref * @param[in] line Line number of call * @param[in] entry Address of entry * @param[in] refcnt Refcount after decrease */ TRACEPOINT_EVENT( mdcache, mdc_lru_unref, TP_ARGS(const char *, function, int, line, void *, entry, int32_t, refcnt), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, entry, entry) ctf_integer(int32_t, refcnt, refcnt) ) ) TRACEPOINT_LOGLEVEL( mdcache, mdc_lru_unref, TRACE_INFO) /** * @brief Trace a QLOCK event * * @param[in] function Name of function taking ref * @param[in] line Line number of call */ TRACEPOINT_EVENT( mdcache, qlock, TP_ARGS(const char *, function, int, line, void *, qlane), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, qlane, qlane) ) ) TRACEPOINT_LOGLEVEL( mdcache, qlock, TRACE_INFO) /** * @brief Trace a QUNLOCK event * * @param[in] function Name of function taking ref * @param[in] line Line number of call */ TRACEPOINT_EVENT( mdcache, qunlock, TP_ARGS(const char *, function, int, line, void *, qlane), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, qlane, qlane) ) ) TRACEPOINT_LOGLEVEL( mdcache, qunlock, TRACE_INFO) /** * @brief Trace a reap (reuse) of an entry * * @param[in] entry Address of entry * @param[in] refcnt Reference count of entry */ TRACEPOINT_EVENT( mdcache, mdc_lru_reap, TP_ARGS(const char *, function, int, line, void *, entry, int32_t, refcnt), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, entry, entry) ctf_integer(int32_t, refcnt, refcnt) ) ) TRACEPOINT_LOGLEVEL( mdcache, mdc_lru_reap, TRACE_INFO) /** * @brief Trace a alloc of a new entry * * @param[in] entry Address of entry */ TRACEPOINT_EVENT( mdcache, mdc_lru_get, TP_ARGS(const char *, function, int, line, void *, entry, int32_t, refcnt), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, entry, entry) ctf_integer(int32_t, refcnt, refcnt) ) ) /** * @brief Trace a reap (reuse) of a chunk * * @param[in] parent Address of parent * @param[in] chunk Address of chunk */ TRACEPOINT_EVENT( mdcache, mdc_lru_reap_chunk, TP_ARGS(const char *, function, int, line, void *, parent, void *, chunk), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, parent, parent) ctf_integer_hex(void *, chunk, chunk) ) ) TRACEPOINT_LOGLEVEL( mdcache, mdc_lru_get, TRACE_INFO) /** * @brief Trace insertion of an entry in the LRU * * @param[in] entry Address of entry */ TRACEPOINT_EVENT( mdcache, mdc_lru_insert, TP_ARGS(const char *, function, int, line, void *, entry, int32_t, refcnt), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, entry, entry) ctf_integer(int32_t, refcnt, refcnt) ) ) TRACEPOINT_LOGLEVEL( mdcache, mdc_lru_insert, TRACE_INFO) /** * @brief Trace removal of an entry from the LRU * * @param[in] entry Address of entry */ TRACEPOINT_EVENT( mdcache, mdc_lru_remove, TP_ARGS(const char *, function, int, line, void *, entry, int32_t, refcnt), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, entry, entry) ctf_integer(int32_t, refcnt, refcnt) ) ) TRACEPOINT_LOGLEVEL( mdcache, mdc_lru_remove, TRACE_INFO) /** * @brief Trace killing of entry * * @param[in] entry Address of entry */ TRACEPOINT_EVENT( mdcache, mdc_kill_entry, TP_ARGS(const char *, function, int, line, void *, entry, int32_t, refcnt, int32_t, freed), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, entry, entry) ctf_integer(int32_t, refcnt, refcnt) ctf_integer(int32_t, freed, freed) ) ) TRACEPOINT_LOGLEVEL( mdcache, mdc_kill_entry, TRACE_INFO) /** * @brief Trace readdir cache populate * * @param[in] entry Address of entry */ TRACEPOINT_EVENT( mdcache, mdc_readdir_populate, TP_ARGS(const char *, function, int, line, void *, entry, int32_t, refcnt), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, entry, entry) ctf_integer(int32_t, refcnt, refcnt) ) ) TRACEPOINT_LOGLEVEL( mdcache, mdc_readdir_populate, TRACE_INFO) /** * @brief Trace readdir * * @param[in] entry Address of entry */ TRACEPOINT_EVENT( mdcache, mdc_readdir, TP_ARGS(const char *, function, int, line, void *, entry, int32_t, refcnt), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, entry, entry) ctf_integer(int32_t, refcnt, refcnt) ) ) TRACEPOINT_LOGLEVEL( mdcache, mdc_readdir, TRACE_INFO) /** * @brief Trace lookup * * @param[in] function Name of function * @param[in] line Line number of call * @param[in] parent Address of parent entry * @param[in] name Name to lookup * @param[in] entry Address of entry (if found) */ TRACEPOINT_EVENT( mdcache, mdc_lookup, TP_ARGS(const char *, function, int, line, void *, parent, const char *, name, void *, entry), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, parent, parent) ctf_string(name, name) ctf_integer_hex(void *, entry, entry) ) ) TRACEPOINT_LOGLEVEL( mdcache, mdc_lookup, TRACE_INFO) #endif /* GANESHA_LTTNG_MDCACHE_TP_H */ #undef TRACEPOINT_INCLUDE #define TRACEPOINT_INCLUDE "gsh_lttng/mdcache.h" #include nfs-ganesha-2.6.0/src/include/gsh_lttng/nfs_rpc.h000066400000000000000000000055551324272410200217260ustar00rootroot00000000000000 #undef TRACEPOINT_PROVIDER #define TRACEPOINT_PROVIDER nfs_rpc #if !defined(GANESHA_LTTNG_NFS_RPC_H) || defined(TRACEPOINT_HEADER_MULTI_READ) #define GANESHA_LTTNG_NFS_RPC_H #include /** * @brief Trace the start of the rpc_execute function * * @param req - the address of request we are handling */ TRACEPOINT_EVENT( nfs_rpc, start, TP_ARGS(request_data_t *, req), TP_FIELDS( ctf_integer_hex(request_data_t *, req, req) ) ) TRACEPOINT_LOGLEVEL( nfs_rpc, start, TRACE_INFO) /** * @brief Trace the exit of the rpc_execute function * * The timestamp difference is the latency of the request * * @param req - the address of the request we just handled */ TRACEPOINT_EVENT( nfs_rpc, end, TP_ARGS(request_data_t *, req), TP_FIELDS( ctf_integer_hex(request_data_t *, req, req) ) ) TRACEPOINT_LOGLEVEL( nfs_rpc, end, TRACE_INFO) /** * @brief Trace the start of the rpc_execute function * * @param req - the address of request we are handling */ TRACEPOINT_EVENT( nfs_rpc, op_start, TP_ARGS(request_data_t *, req, const char *, op_name, int, export_id), TP_FIELDS( ctf_integer_hex(request_data_t *, req, req) ctf_string(op_name, op_name) ctf_integer(int, export_id, export_id) ) ) TRACEPOINT_LOGLEVEL( nfs_rpc, op_start, TRACE_INFO) /** * @brief Trace the exit of the rpc_execute function * * The timestamp difference is the latency of the request * * @param req - the address of the request we just handled */ TRACEPOINT_EVENT( nfs_rpc, op_end, TP_ARGS(request_data_t *, req), TP_FIELDS( ctf_integer_hex(request_data_t *, req, req) ) ) TRACEPOINT_LOGLEVEL( nfs_rpc, op_end, TRACE_INFO) /** * @brief Trace the start of the NFSv4 op function * * @param op_num - Op number within compound * @param op_code - Numerical opcode of op * @param op_name - Text name of op */ TRACEPOINT_EVENT( nfs_rpc, v4op_start, TP_ARGS(int, op_num, int, op_code, const char *, op_name), TP_FIELDS( ctf_integer(int, op_num, op_num) ctf_integer(int, op_code, op_code) ctf_string(op_name, op_name) ) ) TRACEPOINT_LOGLEVEL( nfs_rpc, v4op_start, TRACE_INFO) /** * @brief Trace the exit of the NFSv4 op function * * The timestamp difference is the latency of the request * * @param op_num - Op number within compound * @param op_code - Numerical opcode of op * @param op_name - Text name of op * @param status - Result of op */ TRACEPOINT_EVENT( nfs_rpc, v4op_end, TP_ARGS(int, op_num, int, op_code, const char *, op_name, const char *, status), TP_FIELDS( ctf_integer(int, op_num, op_num) ctf_integer(int, op_code, op_code) ctf_string(op_name, op_name) ctf_string(status, status) ) ) TRACEPOINT_LOGLEVEL( nfs_rpc, v4op_end, TRACE_INFO) #endif /* GANESHA_LTTNG_NFS_RPC_H */ #undef TRACEPOINT_INCLUDE #define TRACEPOINT_INCLUDE "gsh_lttng/nfs_rpc.h" #include nfs-ganesha-2.6.0/src/include/gsh_lttng/state.h000066400000000000000000000026431324272410200214070ustar00rootroot00000000000000 #undef TRACEPOINT_PROVIDER #define TRACEPOINT_PROVIDER state #if !defined(GANESHA_LTTNG_STATE_TP_H) || \ defined(TRACEPOINT_HEADER_MULTI_READ) #define GANESHA_LTTNG_STATE_TP_H #include /** * @brief Trace a state add event * * @param[in] function Name of function adding state * @param[in] line Line number of call * @param[in] obj obj state is added to * @param[in] state state being added */ TRACEPOINT_EVENT( state, add, TP_ARGS(const char *, function, int, line, void *, obj, void *, state), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, obj, obj) ctf_integer_hex(void *, state, state) ) ) TRACEPOINT_LOGLEVEL( state, add, TRACE_INFO) /** * @brief Trace a state delete event * * @param[in] function Name of function deleting state * @param[in] line Line number of call * @param[in] obj obj state is deleted from * @param[in] state state being deleted */ TRACEPOINT_EVENT( state, delete, TP_ARGS(const char *, function, int, line, void *, obj, void *, state), TP_FIELDS( ctf_string(function, function) ctf_integer(int, line, line) ctf_integer_hex(void *, obj, obj) ctf_integer_hex(void *, state, state) ) ) TRACEPOINT_LOGLEVEL( state, delete, TRACE_INFO) #endif /* GANESHA_LTTNG_STATE_TP_H */ #undef TRACEPOINT_INCLUDE #define TRACEPOINT_INCLUDE "gsh_lttng/state.h" #include nfs-ganesha-2.6.0/src/include/gsh_rpc.h000066400000000000000000000104611324272410200177200ustar00rootroot00000000000000/* This is a central clearing house for RPC definitions. Nothing should included anything related to RPC except this file */ #ifndef GSH_RPC_H #define GSH_RPC_H #include "config.h" #include /* Ganesha project has abstract_atomic.h file and tirpc also has a * similar header with the same name. We want to include ganesha * project's header here, so include it here before including any * RPC headers. */ #include "abstract_atomic.h" #include #include #include #include #include #ifdef _HAVE_GSSAPI #include #include /* XXX */ #endif #include #include #include "common_utils.h" #include "abstract_mem.h" #include "gsh_list.h" #include "log.h" #include "fridgethr.h" #define NFS_LOOKAHEAD_NONE 0x0000 #define NFS_LOOKAHEAD_MOUNT 0x0001 #define NFS_LOOKAHEAD_OPEN 0x0002 #define NFS_LOOKAHEAD_CLOSE 0x0004 #define NFS_LOOKAHEAD_READ 0x0008 #define NFS_LOOKAHEAD_WRITE 0x0010 #define NFS_LOOKAHEAD_COMMIT 0x0020 #define NFS_LOOKAHEAD_CREATE 0x0040 #define NFS_LOOKAHEAD_REMOVE 0x0080 #define NFS_LOOKAHEAD_RENAME 0x0100 #define NFS_LOOKAHEAD_LOCK 0x0200 /* !_U types */ #define NFS_LOOKAHEAD_READDIR 0x0400 #define NFS_LOOKAHEAD_LAYOUTCOMMIT 0x0040 #define NFS_LOOKAHEAD_SETATTR 0x0080 #define NFS_LOOKAHEAD_SETCLIENTID 0x0100 #define NFS_LOOKAHEAD_SETCLIENTID_CONFIRM 0x0200 #define NFS_LOOKAHEAD_LOOKUP 0x0400 #define NFS_LOOKAHEAD_READLINK 0x0800 /* ... */ struct nfs_request_lookahead { uint32_t flags; uint16_t read; uint16_t write; }; #define NFS_LOOKAHEAD_HIGH_LATENCY(lkhd) \ (((lkhd).flags & (NFS_LOOKAHEAD_READ | \ NFS_LOOKAHEAD_WRITE | \ NFS_LOOKAHEAD_COMMIT | \ NFS_LOOKAHEAD_LAYOUTCOMMIT | \ NFS_LOOKAHEAD_READDIR))) #define XDR_ARRAY_MAXLEN 1024 #define XDR_BYTES_MAXLEN (1024*1024) #define XDR_BYTES_MAXLEN_IO (64*1024*1024) #define XDR_STRING_MAXLEN (8*1024) typedef struct sockaddr_storage sockaddr_t; #define SOCK_NAME_MAX 128 struct netconfig *getnetconfigent(const char *); void freenetconfigent(struct netconfig *); /** * @addtogroup config * * @{ */ /** * @defgroup config_krb5 Structure and defaults for NFS_KRB5 * @brief Constants and csturctures for KRB5 configuration * * @{ */ /** * @brief Default value for krb5_param.gss.principal */ #define DEFAULT_NFS_PRINCIPAL "nfs" /** * @brief default value for krb5_param.keytab * * The empty string lets GSSAPI use keytab specified in /etc/krb5.conf */ #define DEFAULT_NFS_KEYTAB "" #ifdef _HAVE_GSSAPI /** * @brief Kerberos 5 parameters */ typedef struct nfs_krb5_param { /** Kerberos keytab. Defaults to DEFAULT_NFS_KEYTAB, settable with KeytabPath. */ char *keytab; /** The ganesha credential cache. Defautls to DEFAULT_NFS_CCACHE_DIR, unsettable by user. */ char *ccache_dir; /** * @note representation of GSSAPI service, independent of GSSRPC or * TI-RPC global variables. Initially, use it just for * callbacks. */ struct { /** Principal used in callbacks, set to DEFAULT_NFS_PRINCIPAL and unsettable by user. */ char *principal; /** Expanded gss name from principal, equal to principal/host\@domain. Unsettable by user. */ gss_name_t gss_name; } svc; /** Whether to activate Kerberos 5. Defaults to true (if Kerberos support is compiled in) and settable with Active_krb5 */ bool active_krb5; } nfs_krb5_parameter_t; /** @} */ /** @} */ void log_sperror_gss(char *, OM_uint32, OM_uint32); const char *str_gc_proc(rpc_gss_proc_t); #endif /* _HAVE_GSSAPI */ bool copy_xprt_addr(sockaddr_t *, SVCXPRT *); int display_sockaddr(struct display_buffer *dspbuf, sockaddr_t *addr); static inline void sprint_sockaddr(sockaddr_t *addr, char *buf, size_t len) { struct display_buffer dspbuf = {len, buf, buf}; buf[0] = '\0'; display_sockaddr(&dspbuf, addr); } int sprint_sockip(sockaddr_t *, char *, int); const char *xprt_type_to_str(xprt_type_t); int cmp_sockaddr(sockaddr_t *, sockaddr_t *, bool); int sockaddr_cmpf(sockaddr_t *, sockaddr_t *, bool); uint64_t hash_sockaddr(sockaddr_t *, bool); in_addr_t get_in_addr(sockaddr_t *); int get_port(sockaddr_t *); /* Returns an EAI value, accepts only numeric strings */ extern int ipstring_to_sockaddr(const char *, sockaddr_t *); extern tirpc_pkg_params ntirpc_pp; #endif /* GSH_RPC_H */ nfs-ganesha-2.6.0/src/include/gsh_status.h000066400000000000000000000045041324272410200204600ustar00rootroot00000000000000/* * Copyright © 2014 CohortFS, LLC. * Author: William Allen Simpson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ /** * @file gsh_status.h * @author William Allen Simpson * @author Daniel Gryniewicz * @date Mon Nov 17 14:11:17 2014 * * @brief Ganesha Unified Status */ #ifndef GSH_STATUS_H #define GSH_STATUS_H /** * @brief Possible Errors from SAL Code * * @note A lot of these errors don't make sense in the context of the * SAL and ought to be pruned. */ typedef enum state_status { STATE_SUCCESS, STATE_MALLOC_ERROR, STATE_POOL_MUTEX_INIT_ERROR, STATE_GET_NEW_LRU_ENTRY, STATE_INIT_ENTRY_FAILED, STATE_FSAL_ERROR, STATE_LRU_ERROR, STATE_HASH_SET_ERROR, STATE_NOT_A_DIRECTORY, STATE_INCONSISTENT_ENTRY, STATE_BAD_TYPE, STATE_ENTRY_EXISTS, STATE_DIR_NOT_EMPTY, STATE_NOT_FOUND, STATE_INVALID_ARGUMENT, STATE_INSERT_ERROR, STATE_HASH_TABLE_ERROR, STATE_FSAL_EACCESS, STATE_IS_A_DIRECTORY, STATE_FSAL_EPERM, STATE_NO_SPACE_LEFT, STATE_READ_ONLY_FS, STATE_IO_ERROR, STATE_ESTALE, STATE_FSAL_ERR_SEC, STATE_LOCKED, STATE_QUOTA_EXCEEDED, STATE_ASYNC_POST_ERROR, STATE_NOT_SUPPORTED, STATE_STATE_ERROR, STATE_FSAL_DELAY, STATE_NAME_TOO_LONG, STATE_LOCK_CONFLICT, STATE_LOCK_BLOCKED, STATE_LOCK_DEADLOCK, STATE_BAD_COOKIE, STATE_FILE_BIG, STATE_GRACE_PERIOD, STATE_CACHE_INODE_ERR, STATE_SIGNAL_ERROR, STATE_FILE_OPEN, STATE_MLINK, STATE_SERVERFAULT, STATE_TOOSMALL, STATE_XDEV, STATE_SHARE_DENIED, STATE_IN_GRACE, STATE_BADHANDLE, STATE_BAD_RANGE, } state_status_t; #define STATE_FSAL_ESTALE STATE_ESTALE #endif /* !GSH_STATUS_H */ nfs-ganesha-2.6.0/src/include/gsh_types.h000066400000000000000000000034441324272410200203030ustar00rootroot00000000000000/* * Copyright © CohortFS, LLC. * Author: Adam C. Emerson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ /** * @file gsh_types.h * @author Adam C. Emerson * @date Mon Jul 9 16:59:11 2012 * * @brief Miscelalneous types used throughout Ganesha. * * This file contains miscellaneous types used through multiple layers * in Ganesha. */ #ifndef GSH_TYPES_H #define GSH_TYPES_H #include /* An elapsed time in nanosecs works because an unsigned * 64 bit can hold ~584 years of nanosecs. If any code I have * ever written stays up that long, I would be amazed (and dead * a very long time...) */ typedef uint64_t nsecs_elapsed_t; #define NS_PER_USEC ((nsecs_elapsed_t) 1000) #define NS_PER_MSEC ((nsecs_elapsed_t) 1000000) #define NS_PER_SEC ((nsecs_elapsed_t) 1000000000) /** * @brief Buffer descriptor * * This structure is used to describe a counted buffer as an * address/length pair. */ struct gsh_buffdesc { void *addr; /*< First octet/byte of the buffer */ size_t len; /*< Length of the buffer */ }; #endif /* !GSH_TYPES_H */ nfs-ganesha-2.6.0/src/include/gsh_wait_queue.h000066400000000000000000000046301324272410200213050ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) 2012, The Linux Box Corporation * Contributor : Matt Benjamin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * * @file wait_queue.h * @author Matt Benjamin * @brief Pthreads-based wait queue package * * @section DESCRIPTION * * This module provides simple wait queues using pthreads primitives. */ #ifndef GSH_WAIT_QUEUE_H #define GSH_WAIT_QUEUE_H #include #include #include "gsh_list.h" typedef struct wait_entry { pthread_mutex_t mtx; pthread_cond_t cv; } wait_entry_t; #define Wqe_LFlag_None 0x0000 #define Wqe_LFlag_WaitSync 0x0001 #define Wqe_LFlag_SyncDone 0x0002 /* thread wait queue */ typedef struct wait_q_entry { uint32_t flags; uint32_t waiters; wait_entry_t lwe; /* left */ wait_entry_t rwe; /* right */ struct glist_head waitq; } wait_q_entry_t; static inline int gsh_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *a __attribute__ ((unused))) { pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, #if defined(__linux__) PTHREAD_MUTEX_ADAPTIVE_NP #else PTHREAD_MUTEX_DEFAULT #endif ); return pthread_mutex_init(m, &attr); } static inline void init_wait_entry(wait_entry_t *we) { gsh_mutex_init(&we->mtx, NULL); pthread_cond_init(&we->cv, NULL); } static inline void init_wait_q_entry(wait_q_entry_t *wqe) { glist_init(&wqe->waitq); init_wait_entry(&wqe->lwe); init_wait_entry(&wqe->rwe); } static inline void thread_delay_ms(time_t ms) { struct timespec then = { .tv_sec = ms / 1000, .tv_nsec = (ms % 1000) * 1000000U }; nanosleep(&then, NULL); } #endif /* GSH_WAIT_QUEUE_H */ nfs-ganesha-2.6.0/src/include/gss_credcache.h000066400000000000000000000036271324272410200210560ustar00rootroot00000000000000/* Copyright (c) 2004 The Regents of the University of Michigan. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef GSS_CREDCACHE_H #define GSS_CREDCACHE_H #include #include #include #include #include #include #include struct gssd_k5_kt_princ; extern char *ccachesearch[]; int gssd_check_mechs(void); int gssd_refresh_krb5_machine_credential(char *, struct gssd_k5_kt_princ *, char *); #endif /* !GSS_CREDCACHE */ nfs-ganesha-2.6.0/src/include/hashtable.h000066400000000000000000000247511324272410200202350ustar00rootroot00000000000000/* * * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup hashtable A non-intrusive, partitioned hash-keyed tree * @{ */ /** * @file HashTable.h * @brief Header for hash functionality * * This file declares the functions and data structures for use with * the Ganesha red-black tree based, concurrent hash store. */ #ifndef HASHTABLE_H #define HASHTABLE_H #include #include #include #include #include "log.h" #include "abstract_mem.h" #include "gsh_types.h" /** * @brief A pair of buffer descriptors * * This is used internally to represent a single hash datum within the * table. */ struct hash_data { struct gsh_buffdesc key; /*< The lookup key */ struct gsh_buffdesc val; /*< The stored value */ }; /* Forward declaration */ typedef struct hash_param hash_parameter_t; typedef uint32_t(*index_function_t)(struct hash_param *, struct gsh_buffdesc *); typedef uint64_t(*rbthash_function_t)(struct hash_param *, struct gsh_buffdesc *); typedef int (*both_function_t)(struct hash_param *, struct gsh_buffdesc *, uint32_t *, uint64_t *); typedef int (*hash_comparator_t)(struct gsh_buffdesc *, struct gsh_buffdesc *); typedef int (*key_display_function_t)(struct gsh_buffdesc *, char *); typedef int (*val_display_function_t)(struct gsh_buffdesc *, char *); #define HT_FLAG_NONE 0x0000 /*< Null hash table flags */ #define HT_FLAG_CACHE 0x0001 /*< Indicates that caching should be enabled */ /** * @brief Hash parameters * * This structure defines parameters determining the behaviour of a * given hash table. */ struct hash_param { uint32_t flags; /*< Create flags */ uint32_t cache_entry_count; /*< 2^10 <= Power of 2 <= 2^15 */ uint32_t index_size; /*< Number of partition trees, this MUST be a prime number. */ index_function_t hash_func_key; /*< Partition function, returns an integer from 0 to (index_size - 1). This should be something fairly simple and fast with a uniform distribution. */ rbthash_function_t hash_func_rbt; /*< The actual hash value, termining location within the partition tree. This should be a high quality hash function such as 64 bit Lookup3 or Murmur. */ both_function_t hash_func_both; /*< Index and partition calcualtor. Returns false on failure. A single function may replace the partition and hash funcions. */ hash_comparator_t compare_key;/*< Function to compare two keys. This function returns 0 on equality. */ key_display_function_t key_to_str; /*< Function to convert a key to a string. */ val_display_function_t val_to_str; /*< Function to convert a value to a string. */ char *ht_name; /*< Name of this hash table. */ log_components_t ht_log_component; /*< Log component to use for this hash table */ }; /** * @brief Hash stats * * This structure defines various statistics for a hash table. */ typedef struct hash_stat { size_t entries; /*< Number of entries in the hash table */ size_t min_rbt_num_node; /*< Minimum size (in number of nodes) of the rbt used. */ size_t max_rbt_num_node; /*< Maximum size (in number of nodes) of the rbt used. */ size_t average_rbt_num_node; /*< Average size (in number of nodes) of the rbt used. */ } hash_stat_t; /** * @brief Represents an individual partition * * This structure holds the per-subtree data making up each partition in * a hash table. */ struct hash_partition { size_t count; /*< Numer of entries in this partition */ struct rbt_head rbt; /*< The red-black tree */ pthread_rwlock_t lock; /*< Lock for this partition */ struct rbt_node **cache; /*< Expected entry cache */ }; /** * @brief A hash table * * This structure defines an entire hash table. */ typedef struct hash_table { struct hash_param parameter; /*< Definitive parameter for the HashTable */ pool_t *node_pool; /*< Pool of RBT nodes */ pool_t *data_pool; /*< Pool of buffer pairs */ struct hash_partition partitions[]; /*< Parameter.index_size partitions of the hash table. */ } hash_table_t; /** * @brief A 'latching' lock * * This structure defines a 'latching' lock for subsequent operations * on a hash table after an initial lookup. */ struct hash_latch { struct rbt_node *locator; /*< Saved location in the tree */ uint64_t rbt_hash; /*< Saved red-black hash */ uint32_t index; /*< Saved partition index */ }; typedef enum hash_set_how { HASHTABLE_SET_HOW_TEST_ONLY = 1, HASHTABLE_SET_HOW_SET_OVERWRITE = 2, HASHTABLE_SET_HOW_SET_NO_OVERWRITE = 3 } hash_set_how_t; /* How many character used to display a key or value */ #define HASHTABLE_DISPLAY_STRLEN 8192 /* Possible errors */ typedef enum hash_error { HASHTABLE_SUCCESS, HASHTABLE_UNKNOWN_HASH_TYPE, HASHTABLE_ERROR_NO_SUCH_KEY, HASHTABLE_ERROR_KEY_ALREADY_EXISTS, HASHTABLE_ERROR_INVALID_ARGUMENT, HASHTABLE_ERROR_DELALL_FAIL, HASHTABLE_NOT_DELETED, HASHTABLE_OVERWRITTEN, } hash_error_t; const char *hash_table_err_to_str(hash_error_t err); /* These are the primitives of the hash table */ struct hash_table *hashtable_init(struct hash_param *); hash_error_t hashtable_destroy(struct hash_table *, int (*)(struct gsh_buffdesc, struct gsh_buffdesc)); hash_error_t hashtable_getlatch(struct hash_table *, const struct gsh_buffdesc *, struct gsh_buffdesc *, bool, struct hash_latch *); hash_error_t hashtable_acquire_latch(struct hash_table *ht, const struct gsh_buffdesc *key, struct hash_latch *latch); void hashtable_releaselatched(struct hash_table *, struct hash_latch *); hash_error_t hashtable_setlatched(struct hash_table *, struct gsh_buffdesc *, struct gsh_buffdesc *, struct hash_latch *, int, struct gsh_buffdesc *, struct gsh_buffdesc *); void hashtable_deletelatched(struct hash_table *, const struct gsh_buffdesc *, struct hash_latch *, struct gsh_buffdesc *, struct gsh_buffdesc *); hash_error_t hashtable_delall(struct hash_table *, int (*)(struct gsh_buffdesc, struct gsh_buffdesc)); void hashtable_log(log_components_t, struct hash_table *); /* These are very simple wrappers around the primitives */ /** * @brief Look up a value * * This function attempts to locate a key in the hash store and return * the associated value. It is implemented as a wrapper around * the hashtable_getlatched function. * * @param[in] ht The hash store to be searched * @param[in] key A buffer descriptor locating the key to find * @param[out] val A buffer descriptor locating the value found * * @return Same possibilities as HahTable_GetLatch */ static inline hash_error_t HashTable_Get(struct hash_table *ht, const struct gsh_buffdesc *key, struct gsh_buffdesc *val) { return hashtable_getlatch(ht, key, val, false, NULL); } /** * @brief Set a pair (key,value) into the Hash Table * * This function sets a value into the hash table with no overwrite. * * The previous version of this function would overwrite, but having * overwrite as the only value for a function that doesn't return the * original buffers is a bad idea and can lead to leaks. * * @param[in,out] ht The hashtable to test or alter * @param[in] key The key to be set * @param[in] val The value to be stored * * @retval HASHTABLE_SUCCESS if successfull * @retval HASHTABLE_KEY_ALREADY_EXISTS if the key already exists */ static inline hash_error_t HashTable_Set(struct hash_table *ht, struct gsh_buffdesc *key, struct gsh_buffdesc *val) { /* structure to hold retained state */ struct hash_latch latch; /* Stored return code */ hash_error_t rc = HASHTABLE_SUCCESS; rc = hashtable_getlatch(ht, key, NULL, true, &latch); if ((rc != HASHTABLE_SUCCESS) && (rc != HASHTABLE_ERROR_NO_SUCH_KEY)) return rc; rc = hashtable_setlatched(ht, key, val, &latch, false, NULL, NULL); return rc; } /* HashTable_Set */ /** * @brief Remove an entry from the hash table * * This function deletes an entry from the hash table. * * @param[in,out] ht The hashtable to be modified * @param[in] key The key corresponding to the entry to delete * @param[out] stored_key If non-NULL, a buffer descriptor * specifying the key as stored in the hash table * @param[out] stored_val If non-NULL, a buffer descriptor * specifying the key as stored in the hash table * * @retval HASHTABLE_SUCCESS on deletion */ static inline hash_error_t HashTable_Del(struct hash_table *ht, const struct gsh_buffdesc *key, struct gsh_buffdesc *stored_key, struct gsh_buffdesc *stored_val) { /* Structure to hold retained state */ struct hash_latch latch; /* Stored return code */ hash_error_t rc = HASHTABLE_SUCCESS; rc = hashtable_getlatch(ht, key, NULL, true, &latch); switch (rc) { case HASHTABLE_SUCCESS: hashtable_deletelatched(ht, key, &latch, stored_key, stored_val); /* Fall through to release latch */ case HASHTABLE_ERROR_NO_SUCH_KEY: hashtable_releaselatched(ht, &latch); /* Fall through to return */ default: return rc; } } /* These are the prototypes for large wrappers implementing more complex semantics on top of the primitives. */ hash_error_t hashtable_test_and_set(struct hash_table *, struct gsh_buffdesc *, struct gsh_buffdesc *, enum hash_set_how); hash_error_t hashtable_getref(struct hash_table *, struct gsh_buffdesc *, struct gsh_buffdesc *, void (*)(struct gsh_buffdesc *)); /** @} */ #endif /* HASHTABLE_H */ nfs-ganesha-2.6.0/src/include/idmapper.h000066400000000000000000000051421324272410200200740ustar00rootroot00000000000000/* * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup idmapper ID Mapper * * The ID Mapper module provides mapping between numerical user and * group IDs and NFSv4 style owner and group strings. * * @{ */ /** * @file idmapper.c * @brief Id mapping functions */ #ifndef IDMAPPER_H #define IDMAPPER_H #include #include #include #include "gsh_rpc.h" #include "gsh_types.h" /* Arbitrary string buffer lengths */ #define PWENT_BEST_GUESS_LEN 1024 /** * @brief Shared between idmapper.c and idmapper_cache.c. If you * aren't in idmapper.c, leave these symbols alone. * * @{ */ extern pthread_rwlock_t idmapper_user_lock; extern pthread_rwlock_t idmapper_group_lock; void idmapper_cache_init(void); bool idmapper_add_user(const struct gsh_buffdesc *, uid_t, const gid_t *, bool); bool idmapper_add_group(const struct gsh_buffdesc *, gid_t); bool idmapper_lookup_by_uname(const struct gsh_buffdesc *, uid_t *, const gid_t **, bool); bool idmapper_lookup_by_uid(const uid_t, const struct gsh_buffdesc **, const gid_t **); bool idmapper_lookup_by_gname(const struct gsh_buffdesc *, uid_t *); bool idmapper_lookup_by_gid(const gid_t, const struct gsh_buffdesc **); /** @} */ bool idmapper_init(void); void idmapper_clear_cache(void); bool xdr_encode_nfs4_owner(XDR *, uid_t); bool xdr_encode_nfs4_group(XDR *, gid_t); bool name2uid(const struct gsh_buffdesc *, uid_t *, const uid_t); bool name2gid(const struct gsh_buffdesc *, gid_t *, const gid_t); #ifdef _HAVE_GSSAPI #ifdef _MSPAC_SUPPORT bool principal2uid(char *, uid_t *, gid_t *, struct svc_rpc_gss_data *); #else bool principal2uid(char *, uid_t *, gid_t *); #endif #endif #endif /* IDMAPPER_H */ /** @} */ nfs-ganesha-2.6.0/src/include/log.h000066400000000000000000000305401324272410200170540ustar00rootroot00000000000000/* * definition des codes d'error * * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- * * */ #ifndef _LOGS_H #define _LOGS_H #include #include #include #include #include #include #ifndef LIBLOG_NO_THREAD #include #include #endif #include "gsh_intrinsic.h" #include "config_parsing.h" #include "display.h" /* The maximum size of a log buffer */ #define LOG_BUFF_LEN 2048 /* * Log message severity constants */ typedef enum log_levels { NIV_NULL, NIV_FATAL, NIV_MAJ, NIV_CRIT, NIV_WARN, NIV_EVENT, NIV_INFO, NIV_DEBUG, NIV_MID_DEBUG, NIV_FULL_DEBUG, NB_LOG_LEVEL } log_levels_t; /* * Log components used throughout the code. */ typedef enum log_components { COMPONENT_ALL = 0, /* Used for changing logging for all * components */ COMPONENT_LOG, /* Keep this first, some code depends on it * being the first component */ COMPONENT_MEM_ALLOC, COMPONENT_MEMLEAKS, COMPONENT_FSAL, COMPONENT_NFSPROTO, COMPONENT_NFS_V4, COMPONENT_EXPORT, COMPONENT_FILEHANDLE, COMPONENT_DISPATCH, COMPONENT_CACHE_INODE, COMPONENT_CACHE_INODE_LRU, COMPONENT_HASHTABLE, COMPONENT_HASHTABLE_CACHE, COMPONENT_DUPREQ, COMPONENT_INIT, COMPONENT_MAIN, COMPONENT_IDMAPPER, COMPONENT_NFS_READDIR, COMPONENT_NFS_V4_LOCK, COMPONENT_CONFIG, COMPONENT_CLIENTID, COMPONENT_SESSIONS, COMPONENT_PNFS, COMPONENT_RW_LOCK, COMPONENT_NLM, COMPONENT_RPC, COMPONENT_TIRPC, COMPONENT_NFS_CB, COMPONENT_THREAD, COMPONENT_NFS_V4_ACL, COMPONENT_STATE, COMPONENT_9P, COMPONENT_9P_DISPATCH, COMPONENT_FSAL_UP, COMPONENT_DBUS, COMPONENT_NFS_MSK, COMPONENT_COUNT } log_components_t; /* previously at log_macros.h */ typedef void (*cleanup_function) (void); struct cleanup_list_element { struct cleanup_list_element *next; cleanup_function clean; }; /* Allocates buffer containing debug info to be printed. * Returned buffer needs to be freed. Returns number of * characters in size if size != NULL. */ char *get_debug_info(int *size); /* Function prototypes */ void SetNamePgm(const char *nom); void SetNameHost(const char *nom); void SetNameFunction(const char *nom); /* thread safe */ void SetClientIP(char *ip_str); void init_logging(const char *log_path, const int debug_level); int ReturnLevelAscii(const char *LevelInAscii); char *ReturnLevelInt(int level); /* previously at log_macros.h */ void RegisterCleanup(struct cleanup_list_element *clean); void Cleanup(void); void Fatal(void); /* This function is primarily for setting log level from config, it will * not override log level set from environment. */ void SetComponentLogLevel(log_components_t component, int level_to_set); void DisplayLogComponentLevel(log_components_t component, const char *file, int line, const char *function, log_levels_t level, const char *format, ...) __attribute__ ((format(printf, 6, 7))); /* 6=format 7=params */ void LogMallocFailure(const char *file, int line, const char *function, const char *allocator); int read_log_config(config_file_t in_config, struct config_error_type *err_type); /* These functions display a timeval or timespec into the display buffer * in the same format used for logging timestamp. */ int display_timeval(struct display_buffer *dspbuf, struct timeval *tv); int display_timespec(struct display_buffer *dspbuf, struct timespec *ts); typedef enum log_type { SYSLOG = 0, FILELOG, STDERRLOG, STDOUTLOG, TESTLOG } log_type_t; typedef enum log_header_t { LH_NONE, LH_COMPONENT, LH_ALL, NB_LH_TYPES } log_header_t; /** * @brief Prototype for special log facility logging functions */ typedef int (lf_function_t) (log_header_t headers, void *priv, log_levels_t level, struct display_buffer *buffer, char *compstr, char *message); int create_log_facility(const char *name, lf_function_t *log_func, log_levels_t max_level, log_header_t header, void *private_data); void release_log_facility(const char *name); int enable_log_facility(const char *name); int disable_log_facility(const char *name); int set_log_destination(const char *name, char *dest); int set_log_level(const char *name, log_levels_t max_level); void set_const_log_str(void); struct log_component_info { const char *comp_name; /* component name */ const char *comp_str; /* shorter, more useful name */ bool comp_env_set; /* level was set by env(), now RO */ }; extern log_levels_t *component_log_level; extern struct log_component_info LogComponents[COMPONENT_COUNT]; #define LogAlways(component, format, args...) \ DisplayLogComponentLevel(component, __FILE__, \ __LINE__, \ __func__, \ NIV_NULL, format, ## args) #define LogTest(format, args...) \ DisplayLogComponentLevel(COMPONENT_ALL, __FILE__, \ __LINE__, __func__, \ NIV_NULL, format, ## args) #define LogFatal(component, format, args...) \ DisplayLogComponentLevel(component, __FILE__, \ __LINE__, \ __func__, \ NIV_FATAL, format, ## args) #define LogMajor(component, format, args...) \ do { \ if (likely(component_log_level[component] \ >= NIV_MAJ)) \ DisplayLogComponentLevel(component, __FILE__, \ __LINE__, \ __func__, \ NIV_MAJ, format, ## args); \ } while (0) #define LogCrit(component, format, args...) \ do { \ if (likely(component_log_level[component] \ >= NIV_CRIT)) \ DisplayLogComponentLevel(component, __FILE__, \ __LINE__, \ __func__, \ NIV_CRIT, format, ## args); \ } while (0) #define LogWarn(component, format, args...) \ do { \ if (likely(component_log_level[component] \ >= NIV_WARN)) \ DisplayLogComponentLevel(component, __FILE__, \ __LINE__, \ __func__, \ NIV_WARN, format, ## args); \ } while (0) #define LogWarnOnce(component, format, args...) \ do { \ static bool warned; \ if (unlikely(!warned) && likely(component_log_level[component] \ >= NIV_WARN)) { \ warned = true; \ DisplayLogComponentLevel(component, __FILE__, \ __LINE__, \ __func__, \ NIV_WARN, format, ## args); \ } \ } while (0) #define LogEvent(component, format, args...) \ do { \ if (likely(component_log_level[component] \ >= NIV_EVENT)) \ DisplayLogComponentLevel(component, __FILE__,\ __LINE__, \ __func__, \ NIV_EVENT, format, ## args); \ } while (0) #define LogInfo(component, format, args...) \ do { \ if (unlikely(component_log_level[component] \ >= NIV_INFO)) \ DisplayLogComponentLevel(component, __FILE__,\ __LINE__, \ __func__, \ NIV_INFO, format, ## args); \ } while (0) #define LogDebug(component, format, args...) \ do { \ if (unlikely(component_log_level[component] \ >= NIV_DEBUG)) \ DisplayLogComponentLevel(component, __FILE__,\ __LINE__, \ __func__, \ NIV_DEBUG, format, ## args); \ } while (0) #define LogMidDebug(component, format, args...) \ do { \ if (unlikely(component_log_level[component] \ >= NIV_MID_DEBUG)) \ DisplayLogComponentLevel(component, __FILE__,\ __LINE__, \ __func__, \ NIV_MID_DEBUG, \ format, ## args); \ } while (0) #define LogFullDebug(component, format, args...) \ do { \ if (unlikely(component_log_level[component] \ >= NIV_FULL_DEBUG)) \ DisplayLogComponentLevel(component, __FILE__,\ __LINE__, \ __func__, \ NIV_FULL_DEBUG, \ format, ## args); \ } while (0) #define \ LogFullDebugOpaque(component, format, buf_size, value, length, args...) \ do { \ if (unlikely(component_log_level[component] \ >= NIV_FULL_DEBUG)) { \ char buf[buf_size]; \ struct display_buffer dspbuf = {buf_size, buf, buf}; \ \ (void) display_opaque_value(&dspbuf, value, length); \ \ DisplayLogComponentLevel(component, __FILE__,\ __LINE__, \ __func__, \ NIV_FULL_DEBUG, \ format, buf, ## args); \ } \ } while (0) #define LogFullDebugBytes(component, format, buf_size, value, length, args...) \ do { \ if (unlikely(component_log_level[component] \ >= NIV_FULL_DEBUG)) { \ char buf[buf_size]; \ struct display_buffer dspbuf = {buf_size, buf, buf}; \ \ (void) display_opaque_bytes(&dspbuf, value, length); \ \ DisplayLogComponentLevel(component, __FILE__, \ __LINE__, \ __func__, \ NIV_FULL_DEBUG, \ format, buf, ## args); \ } \ } while (0) #define LogAtLevel(component, level, format, args...) \ do { \ if (unlikely(component_log_level[component] \ >= level)) \ DisplayLogComponentLevel(component, __FILE__,\ __LINE__, \ __func__, \ level, format, ## args); \ } while (0) #define isLevel(component, level) \ (unlikely(component_log_level[component] >= level)) #define isInfo(component) \ (unlikely(component_log_level[component] >= NIV_INFO)) #define isDebug(component) \ (unlikely(component_log_level[component] >= NIV_DEBUG)) #define isMidDebug(component) \ (unlikely(component_log_level[component] >= NIV_MID_DEBUG)) #define isFullDebug(component) \ (unlikely(component_log_level[component] >= NIV_FULL_DEBUG)) /* Use either the first component, or if it is not at least at level, * use the second component. */ #define LogInfoAlt(comp1, comp2, format, args...) \ do { \ if (unlikely(component_log_level[comp1] \ >= NIV_INFO) || \ unlikely(component_log_level[comp2] \ >= NIV_INFO)) { \ log_components_t component = \ component_log_level[comp1] \ >= NIV_INFO ? comp1 : comp2; \ \ DisplayLogComponentLevel(component, __FILE__, \ __LINE__, \ __func__, \ NIV_INFO, \ "%s: INFO: " format, \ LogComponents[component] \ .comp_str, ## args); \ } \ } while (0) #define LogDebugAlt(comp1, comp2, format, args...) \ do { \ if (unlikely(component_log_level[comp1] \ >= NIV_DEBUG) || \ unlikely(component_log_level[comp2] \ >= NIV_DEBUG)) { \ log_components_t component = \ component_log_level[comp1] \ >= NIV_DEBUG ? comp1 : comp2; \ \ DisplayLogComponentLevel(component, __FILE__, \ __LINE__, \ __func__, \ NIV_DEBUG, \ "%s: DEBUG: " format, \ LogComponents[component] \ .comp_str, ## args); \ } \ } while (0) #define LogMidDebugAlt(comp1, comp2, format, args...) \ do { \ if (unlikely(component_log_level[comp1] \ >= NIV_MID_DEBUG) || \ unlikely(component_log_level[comp2] \ >= NIV_MID_DEBUG)) { \ log_components_t component = \ component_log_level[comp1] \ >= NIV_MID_DEBUG ? comp1 : comp2; \ \ DisplayLogComponentLevel(component, __FILE__, \ __LINE__, \ __func__, \ NIV_MID_DEBUG, \ "%s: MID DEBUG: " format, \ LogComponents[component] \ .comp_str, ## args); \ } \ } while (0) #define LogFullDebugAlt(comp1, comp2, format, args...) \ do { \ if (unlikely(component_log_level[comp1] \ >= NIV_FULL_DEBUG) || \ unlikely(component_log_level[comp2] \ >= NIV_FULL_DEBUG)) { \ log_components_t component = \ component_log_level[comp1] \ >= NIV_FULL_DEBUG ? comp1 : comp2; \ \ DisplayLogComponentLevel(component, __FILE__, \ __LINE__, \ __func__, \ NIV_FULL_DEBUG, \ "%s: FULLDEBUG: " format, \ LogComponents[component] \ .comp_str, ## args); \ } \ } while (0) /* * Re-export component logging to TI-RPC internal logging */ void rpc_warnx(/* const */ char *fmt, ...); #ifdef USE_DBUS extern struct gsh_dbus_interface log_interface; #endif #endif nfs-ganesha-2.6.0/src/include/mdcache.h000066400000000000000000000033051324272410200176560ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright 2015-2016 Red Hat, Inc. and/or its affiliates. * Author: Daniel Gryniewicz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ /** * @file mdcache_int.h * @brief MDCache main internal interface. * * Main data structures and profiles for MDCache */ #ifndef MDCACHE_H #define MDCACHE_H #include "config.h" #include "fsal_types.h" #include "fsal_up.h" /* Create an MDCACHE instance at the top of a stack */ fsal_status_t mdcache_fsal_create_export(struct fsal_module *fsal_hdl, void *parse_node, struct config_error_type *err_type, const struct fsal_up_vector *super_up_ops); /* Clean up on init failure */ void mdcache_export_uninit(void); /* Initialize the MDCACHE package. */ fsal_status_t mdcache_pkginit(void); /* Parse mdcache config */ int mdcache_set_param_from_conf(config_file_t parse_tree, struct config_error_type *err_type); bool mdcache_lru_fds_available(void); void init_fds_limit(void); #endif /* MDCACHE_H */ nfs-ganesha-2.6.0/src/include/mount.h000066400000000000000000000050321324272410200174330ustar00rootroot00000000000000/* * Please do not edit this file. * It was generated using rpcgen. */ #ifndef _MOUNT_H_RPCGEN #define _MOUNT_H_RPCGEN #define MNTPATHLEN 1024 #define MNTNAMLEN 255 #define NB_AUTH_FLAVOR 10 /* RPCSEC_GSS flavor, for MOUNT */ #define MNT_RPC_GSS_NONE 390003 #define MNT_RPC_GSS_INTEGRITY 390004 #define MNT_RPC_GSS_PRIVACY 390005 enum mountstat3 { MNT3_OK = 0, MNT3ERR_PERM = 1, MNT3ERR_NOENT = 2, MNT3ERR_IO = 5, MNT3ERR_ACCES = 13, MNT3ERR_NOTDIR = 20, MNT3ERR_INVAL = 22, MNT3ERR_NAMETOOLONG = 63, MNT3ERR_NOTSUPP = 10004, MNT3ERR_SERVERFAULT = 10006 }; typedef enum mountstat3 mountstat3; /** * @todo * Danger Will Robinson!! * this struct is overlayed with nfs_fh3 in nfs23.h!! * This needs to be fixed */ typedef struct { u_int fhandle3_len; char *fhandle3_val; } fhandle3; typedef char *dirpath; typedef char *name; typedef struct groupnode *groups; struct groupnode { name gr_name; groups gr_next; }; typedef struct groupnode groupnode; typedef struct exportnode *exports; struct exportnode { dirpath ex_dir; groups ex_groups; exports ex_next; }; typedef struct exportnode exportnode; typedef struct mountbody *mountlist; struct mountbody { name ml_hostname; dirpath ml_directory; mountlist ml_next; }; typedef struct mountbody mountbody; struct mountres3_ok { fhandle3 fhandle; struct { u_int auth_flavors_len; int *auth_flavors_val; } auth_flavors; }; typedef struct mountres3_ok mountres3_ok; struct mountres3 { mountstat3 fhs_status; union { mountres3_ok mountinfo; } mountres3_u; }; typedef struct mountres3 mountres3; #define MOUNTPROG 100005 #define MOUNT_V1 1 #define MOUNTPROC2_NULL 0 #define MOUNTPROC2_MNT 1 #define MOUNTPROC2_DUMP 2 #define MOUNTPROC2_UMNT 3 #define MOUNTPROC2_UMNTALL 4 #define MOUNTPROC2_EXPORT 5 #define MOUNT_V3 3 #define MOUNTPROC3_NULL 0 #define MOUNTPROC3_MNT 1 #define MOUNTPROC3_DUMP 2 #define MOUNTPROC3_UMNT 3 #define MOUNTPROC3_UMNTALL 4 #define MOUNTPROC3_EXPORT 5 /* the xdr functions */ extern bool xdr_mountstat3(XDR *, mountstat3 *); extern bool xdr_fhandle3(XDR *, fhandle3 *); extern bool xdr_dirpath(XDR *, dirpath *); extern bool xdr_name(XDR *, name *); extern bool xdr_groups(XDR *, groups *); extern bool xdr_groupnode(XDR *, groupnode *); extern bool xdr_exports(XDR *, exports *); extern bool xdr_exportnode(XDR *, exportnode *); extern bool xdr_mountlist(XDR *, mountlist *); extern bool xdr_mountbody(XDR *, mountbody *); extern bool xdr_mountres3_ok(XDR *, mountres3_ok *); extern bool xdr_mountres3(XDR *, mountres3 *); #endif /* !_MOUNT_H_RPCGEN */ nfs-ganesha-2.6.0/src/include/murmur3.h000066400000000000000000000013661324272410200177110ustar00rootroot00000000000000//----------------------------------------------------------------------------- // MurmurHash3 was written by Austin Appleby, and is placed in the // public domain. The author hereby disclaims copyright to this source // code. #ifndef _MURMURHASH3_H_ #define _MURMURHASH3_H_ #include //----------------------------------------------------------------------------- void MurmurHash3_x86_32(const void *key, int len, uint32_t seed, void *out); void MurmurHash3_x86_128(const void *key, int len, uint32_t seed, void *out); void MurmurHash3_x64_128(const void *key, int len, uint32_t seed, void *out); //----------------------------------------------------------------------------- #endif // _MURMURHASH3_H_ nfs-ganesha-2.6.0/src/include/netgroup_cache.h000066400000000000000000000021101324272410200212510ustar00rootroot00000000000000/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /* Returns true if the given host is in the given netgroup. Uses * innetgr() to accomplish the task, and also caches such results. */ #ifndef NETGROUP_CACHE_H #define NETGROUP_CACHE_H void ng_cache_init(void); void ng_clear_cache(void); bool ng_innetgr(const char *group, const char *host); #endif nfs-ganesha-2.6.0/src/include/nfs23.h000066400000000000000000000606771324272410200172440ustar00rootroot00000000000000/* * Please do not edit this file. * It was generated using rpcgen. */ #ifndef _NFS23_H_RPCGEN #define _NFS23_H_RPCGEN #include "gsh_rpc.h" #include "extended_types.h" #include "mount.h" #define NFS2_MAXDATA 8192 #define NFS2_MAXPATHLEN 1024 #define NFS2_MAXNAMLEN 255 #define NFS2_COOKIESIZE 4 #define NFS2_FHSIZE 32 #define NFS2_MNTPATHLEN 1024 #define NFS2_MNTNAMLEN 255 #define NFS3_FHSIZE 64 #define NFS3_COOKIEVERFSIZE 8 #define NFS3_CREATEVERFSIZE 8 #define NFS3_WRITEVERFSIZE 8 #define NFS2_MAX_FILESIZE (2147483647) /* 0x7fffffff */ typedef char fhandle2[NFS2_FHSIZE]; struct fhstatus2 { u_int status; union { fhandle2 directory; } fhstatus2_u; }; typedef struct fhstatus2 fhstatus2; typedef char *nfspath2; typedef char *filename2; typedef struct { u_int nfsdata2_len; char *nfsdata2_val; } nfsdata2; enum nfsstat2 { NFS_OK = 0, NFSERR_PERM = 1, NFSERR_NOENT = 2, NFSERR_IO = 5, NFSERR_NXIO = 6, NFSERR_ACCES = 13, NFSERR_EXIST = 17, NFSERR_NODEV = 19, NFSERR_NOTDIR = 20, NFSERR_ISDIR = 21, NFSERR_FBIG = 27, NFSERR_NOSPC = 28, NFSERR_ROFS = 30, NFSERR_NAMETOOLONG = 63, NFSERR_NOTEMPTY = 66, NFSERR_DQUOT = 69, NFSERR_STALE = 70, NFSERR_WFLUSH = 99 }; typedef char nfscookie2[NFS2_COOKIESIZE]; typedef u_longlong_t nfs3_uint64; typedef longlong_t nfs3_int64; typedef u_int nfs3_uint32; typedef int nfs3_int32; typedef char *filename3; typedef char *nfspath3; typedef nfs3_uint64 fileid3; typedef nfs3_uint64 cookie3; typedef char cookieverf3[8]; typedef char createverf3[8]; typedef char writeverf3[8]; typedef nfs3_uint32 uid3; typedef nfs3_uint32 gid3; typedef nfs3_uint64 size3; typedef nfs3_uint64 offset3; typedef nfs3_uint32 mode3; typedef nfs3_uint32 count3; enum nfsstat3 { NFS3_OK = 0, NFS3ERR_PERM = 1, NFS3ERR_NOENT = 2, NFS3ERR_IO = 5, NFS3ERR_NXIO = 6, NFS3ERR_ACCES = 13, NFS3ERR_EXIST = 17, NFS3ERR_XDEV = 18, NFS3ERR_NODEV = 19, NFS3ERR_NOTDIR = 20, NFS3ERR_ISDIR = 21, NFS3ERR_INVAL = 22, NFS3ERR_FBIG = 27, NFS3ERR_NOSPC = 28, NFS3ERR_ROFS = 30, NFS3ERR_MLINK = 31, NFS3ERR_NAMETOOLONG = 63, NFS3ERR_NOTEMPTY = 66, NFS3ERR_DQUOT = 69, NFS3ERR_STALE = 70, NFS3ERR_REMOTE = 71, NFS3ERR_BADHANDLE = 10001, NFS3ERR_NOT_SYNC = 10002, NFS3ERR_BAD_COOKIE = 10003, NFS3ERR_NOTSUPP = 10004, NFS3ERR_TOOSMALL = 10005, NFS3ERR_SERVERFAULT = 10006, NFS3ERR_BADTYPE = 10007, NFS3ERR_JUKEBOX = 10008 }; typedef enum nfsstat3 nfsstat3; enum ftype3 { NF3REG = 1, NF3DIR = 2, NF3BLK = 3, NF3CHR = 4, NF3LNK = 5, NF3SOCK = 6, NF3FIFO = 7 }; typedef enum ftype3 ftype3; struct specdata3 { nfs3_uint32 specdata1; nfs3_uint32 specdata2; }; typedef struct specdata3 specdata3; /** * @todo Danger Will Robinson!! * this struct is overlayed with fhandle3 in mount.h!! * This needs to be fixed. */ struct nfs_fh3 { struct { u_int data_len; char *data_val; } data; }; typedef struct nfs_fh3 nfs_fh3; struct nfstime3 { nfs3_uint32 tv_sec; nfs3_uint32 tv_nsec; }; typedef struct nfstime3 nfstime3; struct fattr3 { ftype3 type; mode3 mode; nfs3_uint32 nlink; uid3 uid; gid3 gid; size3 size; size3 used; specdata3 rdev; nfs3_uint64 fsid; fileid3 fileid; nfstime3 atime; nfstime3 mtime; nfstime3 ctime; }; typedef struct fattr3 fattr3; struct post_op_attr { bool_t attributes_follow; union { fattr3 attributes; } post_op_attr_u; }; typedef struct post_op_attr post_op_attr; struct wcc_attr { size3 size; nfstime3 mtime; nfstime3 ctime; }; typedef struct wcc_attr wcc_attr; struct pre_op_attr { bool_t attributes_follow; union { wcc_attr attributes; } pre_op_attr_u; }; typedef struct pre_op_attr pre_op_attr; struct wcc_data { pre_op_attr before; post_op_attr after; }; typedef struct wcc_data wcc_data; struct post_op_fh3 { bool_t handle_follows; union { nfs_fh3 handle; } post_op_fh3_u; }; typedef struct post_op_fh3 post_op_fh3; enum time_how { DONT_CHANGE = 0, SET_TO_SERVER_TIME = 1, SET_TO_CLIENT_TIME = 2 }; typedef enum time_how time_how; struct set_mode3 { bool_t set_it; union { mode3 mode; } set_mode3_u; }; typedef struct set_mode3 set_mode3; struct set_uid3 { bool_t set_it; union { uid3 uid; } set_uid3_u; }; typedef struct set_uid3 set_uid3; struct set_gid3 { bool_t set_it; union { gid3 gid; } set_gid3_u; }; typedef struct set_gid3 set_gid3; struct set_size3 { bool_t set_it; union { size3 size; } set_size3_u; }; typedef struct set_size3 set_size3; struct set_atime { time_how set_it; union { nfstime3 atime; } set_atime_u; }; typedef struct set_atime set_atime; struct set_mtime { time_how set_it; union { nfstime3 mtime; } set_mtime_u; }; typedef struct set_mtime set_mtime; struct sattr3 { set_mode3 mode; set_uid3 uid; set_gid3 gid; set_size3 size; set_atime atime; set_mtime mtime; }; typedef struct sattr3 sattr3; struct diropargs3 { nfs_fh3 dir; filename3 name; }; typedef struct diropargs3 diropargs3; struct GETATTR3args { nfs_fh3 object; }; typedef struct GETATTR3args GETATTR3args; struct GETATTR3resok { fattr3 obj_attributes; }; typedef struct GETATTR3resok GETATTR3resok; struct GETATTR3res { nfsstat3 status; union { GETATTR3resok resok; } GETATTR3res_u; }; typedef struct GETATTR3res GETATTR3res; struct sattrguard3 { bool_t check; union { nfstime3 obj_ctime; } sattrguard3_u; }; typedef struct sattrguard3 sattrguard3; struct SETATTR3args { nfs_fh3 object; sattr3 new_attributes; sattrguard3 guard; }; typedef struct SETATTR3args SETATTR3args; struct SETATTR3resok { wcc_data obj_wcc; }; typedef struct SETATTR3resok SETATTR3resok; struct SETATTR3resfail { wcc_data obj_wcc; }; typedef struct SETATTR3resfail SETATTR3resfail; struct SETATTR3res { nfsstat3 status; union { SETATTR3resok resok; SETATTR3resfail resfail; } SETATTR3res_u; }; typedef struct SETATTR3res SETATTR3res; struct LOOKUP3args { diropargs3 what; }; typedef struct LOOKUP3args LOOKUP3args; struct LOOKUP3resok { nfs_fh3 object; post_op_attr obj_attributes; post_op_attr dir_attributes; }; typedef struct LOOKUP3resok LOOKUP3resok; struct LOOKUP3resfail { post_op_attr dir_attributes; }; typedef struct LOOKUP3resfail LOOKUP3resfail; struct LOOKUP3res { nfsstat3 status; union { LOOKUP3resok resok; LOOKUP3resfail resfail; } LOOKUP3res_u; }; typedef struct LOOKUP3res LOOKUP3res; #define ACCESS3_READ 0x0001 #define ACCESS3_LOOKUP 0x0002 #define ACCESS3_MODIFY 0x0004 #define ACCESS3_EXTEND 0x0008 #define ACCESS3_DELETE 0x0010 #define ACCESS3_EXECUTE 0x0020 struct ACCESS3args { nfs_fh3 object; nfs3_uint32 access; }; typedef struct ACCESS3args ACCESS3args; struct ACCESS3resok { post_op_attr obj_attributes; nfs3_uint32 access; }; typedef struct ACCESS3resok ACCESS3resok; struct ACCESS3resfail { post_op_attr obj_attributes; }; typedef struct ACCESS3resfail ACCESS3resfail; struct ACCESS3res { nfsstat3 status; union { ACCESS3resok resok; ACCESS3resfail resfail; } ACCESS3res_u; }; typedef struct ACCESS3res ACCESS3res; struct READLINK3args { nfs_fh3 symlink; }; typedef struct READLINK3args READLINK3args; struct READLINK3resok { post_op_attr symlink_attributes; nfspath3 data; }; typedef struct READLINK3resok READLINK3resok; struct READLINK3resfail { post_op_attr symlink_attributes; }; typedef struct READLINK3resfail READLINK3resfail; struct READLINK3res { nfsstat3 status; union { READLINK3resok resok; READLINK3resfail resfail; } READLINK3res_u; }; typedef struct READLINK3res READLINK3res; struct READ3args { nfs_fh3 file; offset3 offset; count3 count; }; typedef struct READ3args READ3args; struct READ3resok { post_op_attr file_attributes; count3 count; bool_t eof; struct { u_int data_len; char *data_val; } data; }; typedef struct READ3resok READ3resok; struct READ3resfail { post_op_attr file_attributes; }; typedef struct READ3resfail READ3resfail; struct READ3res { nfsstat3 status; union { READ3resok resok; READ3resfail resfail; } READ3res_u; }; typedef struct READ3res READ3res; enum stable_how { UNSTABLE = 0, DATA_SYNC = 1, FILE_SYNC = 2 }; typedef enum stable_how stable_how; struct WRITE3args { nfs_fh3 file; offset3 offset; count3 count; stable_how stable; struct { u_int data_len; char *data_val; } data; }; typedef struct WRITE3args WRITE3args; struct WRITE3resok { wcc_data file_wcc; count3 count; stable_how committed; writeverf3 verf; }; typedef struct WRITE3resok WRITE3resok; struct WRITE3resfail { wcc_data file_wcc; }; typedef struct WRITE3resfail WRITE3resfail; struct WRITE3res { nfsstat3 status; union { WRITE3resok resok; WRITE3resfail resfail; } WRITE3res_u; }; typedef struct WRITE3res WRITE3res; enum createmode3 { UNCHECKED = 0, GUARDED = 1, EXCLUSIVE = 2 }; typedef enum createmode3 createmode3; struct createhow3 { createmode3 mode; union { sattr3 obj_attributes; createverf3 verf; } createhow3_u; }; typedef struct createhow3 createhow3; struct CREATE3args { diropargs3 where; createhow3 how; }; typedef struct CREATE3args CREATE3args; struct CREATE3resok { post_op_fh3 obj; post_op_attr obj_attributes; wcc_data dir_wcc; }; typedef struct CREATE3resok CREATE3resok; struct CREATE3resfail { wcc_data dir_wcc; }; typedef struct CREATE3resfail CREATE3resfail; struct CREATE3res { nfsstat3 status; union { CREATE3resok resok; CREATE3resfail resfail; } CREATE3res_u; }; typedef struct CREATE3res CREATE3res; struct MKDIR3args { diropargs3 where; sattr3 attributes; }; typedef struct MKDIR3args MKDIR3args; struct MKDIR3resok { post_op_fh3 obj; post_op_attr obj_attributes; wcc_data dir_wcc; }; typedef struct MKDIR3resok MKDIR3resok; struct MKDIR3resfail { wcc_data dir_wcc; }; typedef struct MKDIR3resfail MKDIR3resfail; struct MKDIR3res { nfsstat3 status; union { MKDIR3resok resok; MKDIR3resfail resfail; } MKDIR3res_u; }; typedef struct MKDIR3res MKDIR3res; struct symlinkdata3 { sattr3 symlink_attributes; nfspath3 symlink_data; }; typedef struct symlinkdata3 symlinkdata3; struct SYMLINK3args { diropargs3 where; symlinkdata3 symlink; }; typedef struct SYMLINK3args SYMLINK3args; struct SYMLINK3resok { post_op_fh3 obj; post_op_attr obj_attributes; wcc_data dir_wcc; }; typedef struct SYMLINK3resok SYMLINK3resok; struct SYMLINK3resfail { wcc_data dir_wcc; }; typedef struct SYMLINK3resfail SYMLINK3resfail; struct SYMLINK3res { nfsstat3 status; union { SYMLINK3resok resok; SYMLINK3resfail resfail; } SYMLINK3res_u; }; typedef struct SYMLINK3res SYMLINK3res; struct devicedata3 { sattr3 dev_attributes; specdata3 spec; }; typedef struct devicedata3 devicedata3; struct mknoddata3 { ftype3 type; union { devicedata3 device; sattr3 pipe_attributes; } mknoddata3_u; }; typedef struct mknoddata3 mknoddata3; struct MKNOD3args { diropargs3 where; mknoddata3 what; }; typedef struct MKNOD3args MKNOD3args; struct MKNOD3resok { post_op_fh3 obj; post_op_attr obj_attributes; wcc_data dir_wcc; }; typedef struct MKNOD3resok MKNOD3resok; struct MKNOD3resfail { wcc_data dir_wcc; }; typedef struct MKNOD3resfail MKNOD3resfail; struct MKNOD3res { nfsstat3 status; union { MKNOD3resok resok; MKNOD3resfail resfail; } MKNOD3res_u; }; typedef struct MKNOD3res MKNOD3res; struct REMOVE3args { diropargs3 object; }; typedef struct REMOVE3args REMOVE3args; struct REMOVE3resok { wcc_data dir_wcc; }; typedef struct REMOVE3resok REMOVE3resok; struct REMOVE3resfail { wcc_data dir_wcc; }; typedef struct REMOVE3resfail REMOVE3resfail; struct REMOVE3res { nfsstat3 status; union { REMOVE3resok resok; REMOVE3resfail resfail; } REMOVE3res_u; }; typedef struct REMOVE3res REMOVE3res; struct RMDIR3args { diropargs3 object; }; typedef struct RMDIR3args RMDIR3args; struct RMDIR3resok { wcc_data dir_wcc; }; typedef struct RMDIR3resok RMDIR3resok; struct RMDIR3resfail { wcc_data dir_wcc; }; typedef struct RMDIR3resfail RMDIR3resfail; struct RMDIR3res { nfsstat3 status; union { RMDIR3resok resok; RMDIR3resfail resfail; } RMDIR3res_u; }; typedef struct RMDIR3res RMDIR3res; struct RENAME3args { diropargs3 from; diropargs3 to; }; typedef struct RENAME3args RENAME3args; struct RENAME3resok { wcc_data fromdir_wcc; wcc_data todir_wcc; }; typedef struct RENAME3resok RENAME3resok; struct RENAME3resfail { wcc_data fromdir_wcc; wcc_data todir_wcc; }; typedef struct RENAME3resfail RENAME3resfail; struct RENAME3res { nfsstat3 status; union { RENAME3resok resok; RENAME3resfail resfail; } RENAME3res_u; }; typedef struct RENAME3res RENAME3res; struct LINK3args { nfs_fh3 file; diropargs3 link; }; typedef struct LINK3args LINK3args; struct LINK3resok { post_op_attr file_attributes; wcc_data linkdir_wcc; }; typedef struct LINK3resok LINK3resok; struct LINK3resfail { post_op_attr file_attributes; wcc_data linkdir_wcc; }; typedef struct LINK3resfail LINK3resfail; struct LINK3res { nfsstat3 status; union { LINK3resok resok; LINK3resfail resfail; } LINK3res_u; }; typedef struct LINK3res LINK3res; struct READDIR3args { nfs_fh3 dir; cookie3 cookie; cookieverf3 cookieverf; count3 count; }; typedef struct READDIR3args READDIR3args; struct entry3 { fileid3 fileid; filename3 name; cookie3 cookie; struct entry3 *nextentry; }; typedef struct entry3 entry3; struct dirlist3 { entry3 *entries; bool_t eof; }; typedef struct dirlist3 dirlist3; struct READDIR3resok { post_op_attr dir_attributes; cookieverf3 cookieverf; dirlist3 reply; }; typedef struct READDIR3resok READDIR3resok; struct READDIR3resfail { post_op_attr dir_attributes; }; typedef struct READDIR3resfail READDIR3resfail; struct READDIR3res { nfsstat3 status; union { READDIR3resok resok; READDIR3resfail resfail; } READDIR3res_u; }; typedef struct READDIR3res READDIR3res; struct READDIRPLUS3args { nfs_fh3 dir; cookie3 cookie; cookieverf3 cookieverf; count3 dircount; count3 maxcount; }; typedef struct READDIRPLUS3args READDIRPLUS3args; struct entryplus3 { fileid3 fileid; filename3 name; cookie3 cookie; post_op_attr name_attributes; post_op_fh3 name_handle; struct entryplus3 *nextentry; }; typedef struct entryplus3 entryplus3; struct dirlistplus3 { entryplus3 *entries; bool_t eof; }; typedef struct dirlistplus3 dirlistplus3; struct READDIRPLUS3resok { post_op_attr dir_attributes; cookieverf3 cookieverf; dirlistplus3 reply; }; typedef struct READDIRPLUS3resok READDIRPLUS3resok; struct READDIRPLUS3resfail { post_op_attr dir_attributes; }; typedef struct READDIRPLUS3resfail READDIRPLUS3resfail; struct READDIRPLUS3res { nfsstat3 status; union { READDIRPLUS3resok resok; READDIRPLUS3resfail resfail; } READDIRPLUS3res_u; }; typedef struct READDIRPLUS3res READDIRPLUS3res; struct FSSTAT3args { nfs_fh3 fsroot; }; typedef struct FSSTAT3args FSSTAT3args; struct FSSTAT3resok { post_op_attr obj_attributes; size3 tbytes; size3 fbytes; size3 abytes; size3 tfiles; size3 ffiles; size3 afiles; nfs3_uint32 invarsec; }; typedef struct FSSTAT3resok FSSTAT3resok; struct FSSTAT3resfail { post_op_attr obj_attributes; }; typedef struct FSSTAT3resfail FSSTAT3resfail; struct FSSTAT3res { nfsstat3 status; union { FSSTAT3resok resok; FSSTAT3resfail resfail; } FSSTAT3res_u; }; typedef struct FSSTAT3res FSSTAT3res; #define FSF3_LINK 0x0001 #define FSF3_SYMLINK 0x0002 #define FSF3_HOMOGENEOUS 0x0008 #define FSF3_CANSETTIME 0x0010 struct FSINFO3args { nfs_fh3 fsroot; }; typedef struct FSINFO3args FSINFO3args; struct FSINFO3resok { post_op_attr obj_attributes; nfs3_uint32 rtmax; nfs3_uint32 rtpref; nfs3_uint32 rtmult; nfs3_uint32 wtmax; nfs3_uint32 wtpref; nfs3_uint32 wtmult; nfs3_uint32 dtpref; size3 maxfilesize; nfstime3 time_delta; nfs3_uint32 properties; }; typedef struct FSINFO3resok FSINFO3resok; struct FSINFO3resfail { post_op_attr obj_attributes; }; typedef struct FSINFO3resfail FSINFO3resfail; struct FSINFO3res { nfsstat3 status; union { FSINFO3resok resok; FSINFO3resfail resfail; } FSINFO3res_u; }; typedef struct FSINFO3res FSINFO3res; struct PATHCONF3args { nfs_fh3 object; }; typedef struct PATHCONF3args PATHCONF3args; struct PATHCONF3resok { post_op_attr obj_attributes; nfs3_uint32 linkmax; nfs3_uint32 name_max; bool_t no_trunc; bool_t chown_restricted; bool_t case_insensitive; bool_t case_preserving; }; typedef struct PATHCONF3resok PATHCONF3resok; struct PATHCONF3resfail { post_op_attr obj_attributes; }; typedef struct PATHCONF3resfail PATHCONF3resfail; struct PATHCONF3res { nfsstat3 status; union { PATHCONF3resok resok; PATHCONF3resfail resfail; } PATHCONF3res_u; }; typedef struct PATHCONF3res PATHCONF3res; struct COMMIT3args { nfs_fh3 file; offset3 offset; count3 count; }; typedef struct COMMIT3args COMMIT3args; struct COMMIT3resok { wcc_data file_wcc; writeverf3 verf; }; typedef struct COMMIT3resok COMMIT3resok; struct COMMIT3resfail { wcc_data file_wcc; }; typedef struct COMMIT3resfail COMMIT3resfail; struct COMMIT3res { nfsstat3 status; union { COMMIT3resok resok; COMMIT3resfail resfail; } COMMIT3res_u; }; typedef struct COMMIT3res COMMIT3res; #define NFS_PROGRAM 100003 #define NFS_V2 2 #define NFSPROC_NULL 0 #define NFSPROC_GETATTR 1 #define NFSPROC_SETATTR 2 #define NFSPROC_ROOT 3 #define NFSPROC_LOOKUP 4 #define NFSPROC_READLINK 5 #define NFSPROC_READ 6 #define NFSPROC_WRITECACHE 7 #define NFSPROC_WRITE 8 #define NFSPROC_CREATE 9 #define NFSPROC_REMOVE 10 #define NFSPROC_RENAME 11 #define NFSPROC_LINK 12 #define NFSPROC_SYMLINK 13 #define NFSPROC_MKDIR 14 #define NFSPROC_RMDIR 15 #define NFSPROC_READDIR 16 #define NFSPROC_STATFS 17 #define NFS_V3 3 #define NFSPROC3_NULL 0 #define NFSPROC3_GETATTR 1 #define NFSPROC3_SETATTR 2 #define NFSPROC3_LOOKUP 3 #define NFSPROC3_ACCESS 4 #define NFSPROC3_READLINK 5 #define NFSPROC3_READ 6 #define NFSPROC3_WRITE 7 #define NFSPROC3_CREATE 8 #define NFSPROC3_MKDIR 9 #define NFSPROC3_SYMLINK 10 #define NFSPROC3_MKNOD 11 #define NFSPROC3_REMOVE 12 #define NFSPROC3_RMDIR 13 #define NFSPROC3_RENAME 14 #define NFSPROC3_LINK 15 #define NFSPROC3_READDIR 16 #define NFSPROC3_READDIRPLUS 17 #define NFSPROC3_FSSTAT 18 #define NFSPROC3_FSINFO 19 #define NFSPROC3_PATHCONF 20 #define NFSPROC3_COMMIT 21 /* the xdr functions */ extern bool xdr_nfs3_uint64(XDR *, nfs3_uint64 *); extern bool xdr_nfs3_int64(XDR *, nfs3_int64 *); extern bool xdr_nfs3_uint32(XDR *, nfs3_uint32 *); extern bool xdr_nfs3_int32(XDR *, nfs3_int32 *); extern bool xdr_filename3(XDR *, filename3 *); extern bool xdr_nfspath3(XDR *, nfspath3 *); extern bool xdr_fileid3(XDR *, fileid3 *); extern bool xdr_cookie3(XDR *, cookie3 *); extern bool xdr_fhandle3(XDR *, fhandle3 *); extern bool xdr_cookieverf3(XDR *, cookieverf3); extern bool xdr_createverf3(XDR *, createverf3); extern bool xdr_writeverf3(XDR *, writeverf3); extern bool xdr_uid3(XDR *, uid3 *); extern bool xdr_gid3(XDR *, gid3 *); extern bool xdr_size3(XDR *, size3 *); extern bool xdr_offset3(XDR *, offset3 *); extern bool xdr_mode3(XDR *, mode3 *); extern bool xdr_count3(XDR *, count3 *); extern bool xdr_nfsstat3(XDR *, nfsstat3 *); extern bool xdr_ftype3(XDR *, ftype3 *); extern bool xdr_specdata3(XDR *, specdata3 *); extern bool xdr_nfs_fh3(XDR *, nfs_fh3 *); extern bool xdr_nfstime3(XDR *, nfstime3 *); extern bool xdr_fattr3(XDR *, fattr3 *); extern bool xdr_post_op_attr(XDR *, post_op_attr *); extern bool xdr_wcc_attr(XDR *, wcc_attr *); extern bool xdr_pre_op_attr(XDR *, pre_op_attr *); extern bool xdr_wcc_data(XDR *, wcc_data *); extern bool xdr_post_op_fh3(XDR *, post_op_fh3 *); extern bool xdr_time_how(XDR *, time_how *); extern bool xdr_set_mode3(XDR *, set_mode3 *); extern bool xdr_set_uid3(XDR *, set_uid3 *); extern bool xdr_set_gid3(XDR *, set_gid3 *); extern bool xdr_set_size3(XDR *, set_size3 *); extern bool xdr_set_atime(XDR *, set_atime *); extern bool xdr_set_mtime(XDR *, set_mtime *); extern bool xdr_sattr3(XDR *, sattr3 *); extern bool xdr_diropargs3(XDR *, diropargs3 *); extern bool xdr_GETATTR3args(XDR *, GETATTR3args *); extern bool xdr_GETATTR3resok(XDR *, GETATTR3resok *); extern bool xdr_GETATTR3res(XDR *, GETATTR3res *); extern bool xdr_sattrguard3(XDR *, sattrguard3 *); extern bool xdr_SETATTR3args(XDR *, SETATTR3args *); extern bool xdr_SETATTR3resok(XDR *, SETATTR3resok *); extern bool xdr_SETATTR3resfail(XDR *, SETATTR3resfail *); extern bool xdr_SETATTR3res(XDR *, SETATTR3res *); extern bool xdr_LOOKUP3args(XDR *, LOOKUP3args *); extern bool xdr_LOOKUP3resok(XDR *, LOOKUP3resok *); extern bool xdr_LOOKUP3resfail(XDR *, LOOKUP3resfail *); extern bool xdr_LOOKUP3res(XDR *, LOOKUP3res *); extern bool xdr_ACCESS3args(XDR *, ACCESS3args *); extern bool xdr_ACCESS3resok(XDR *, ACCESS3resok *); extern bool xdr_ACCESS3resfail(XDR *, ACCESS3resfail *); extern bool xdr_ACCESS3res(XDR *, ACCESS3res *); extern bool xdr_READLINK3args(XDR *, READLINK3args *); extern bool xdr_READLINK3resok(XDR *, READLINK3resok *); extern bool xdr_READLINK3resfail(XDR *, READLINK3resfail *); extern bool xdr_READLINK3res(XDR *, READLINK3res *); extern bool xdr_READ3args(XDR *, READ3args *); extern bool xdr_READ3resok(XDR *, READ3resok *); extern bool xdr_READ3resfail(XDR *, READ3resfail *); extern bool xdr_READ3res(XDR *, READ3res *); extern bool xdr_stable_how(XDR *, stable_how *); extern bool xdr_WRITE3args(XDR *, WRITE3args *); extern bool xdr_WRITE3resok(XDR *, WRITE3resok *); extern bool xdr_WRITE3resfail(XDR *, WRITE3resfail *); extern bool xdr_WRITE3res(XDR *, WRITE3res *); extern bool xdr_createmode3(XDR *, createmode3 *); extern bool xdr_createhow3(XDR *, createhow3 *); extern bool xdr_CREATE3args(XDR *, CREATE3args *); extern bool xdr_CREATE3resok(XDR *, CREATE3resok *); extern bool xdr_CREATE3resfail(XDR *, CREATE3resfail *); extern bool xdr_CREATE3res(XDR *, CREATE3res *); extern bool xdr_MKDIR3args(XDR *, MKDIR3args *); extern bool xdr_MKDIR3resok(XDR *, MKDIR3resok *); extern bool xdr_MKDIR3resfail(XDR *, MKDIR3resfail *); extern bool xdr_MKDIR3res(XDR *, MKDIR3res *); extern bool xdr_symlinkdata3(XDR *, symlinkdata3 *); extern bool xdr_SYMLINK3args(XDR *, SYMLINK3args *); extern bool xdr_SYMLINK3resok(XDR *, SYMLINK3resok *); extern bool xdr_SYMLINK3resfail(XDR *, SYMLINK3resfail *); extern bool xdr_SYMLINK3res(XDR *, SYMLINK3res *); extern bool xdr_devicedata3(XDR *, devicedata3 *); extern bool xdr_mknoddata3(XDR *, mknoddata3 *); extern bool xdr_MKNOD3args(XDR *, MKNOD3args *); extern bool xdr_MKNOD3resok(XDR *, MKNOD3resok *); extern bool xdr_MKNOD3resfail(XDR *, MKNOD3resfail *); extern bool xdr_MKNOD3res(XDR *, MKNOD3res *); extern bool xdr_REMOVE3args(XDR *, REMOVE3args *); extern bool xdr_REMOVE3resok(XDR *, REMOVE3resok *); extern bool xdr_REMOVE3resfail(XDR *, REMOVE3resfail *); extern bool xdr_REMOVE3res(XDR *, REMOVE3res *); extern bool xdr_RMDIR3args(XDR *, RMDIR3args *); extern bool xdr_RMDIR3resok(XDR *, RMDIR3resok *); extern bool xdr_RMDIR3resfail(XDR *, RMDIR3resfail *); extern bool xdr_RMDIR3res(XDR *, RMDIR3res *); extern bool xdr_RENAME3args(XDR *, RENAME3args *); extern bool xdr_RENAME3resok(XDR *, RENAME3resok *); extern bool xdr_RENAME3resfail(XDR *, RENAME3resfail *); extern bool xdr_RENAME3res(XDR *, RENAME3res *); extern bool xdr_LINK3args(XDR *, LINK3args *); extern bool xdr_LINK3resok(XDR *, LINK3resok *); extern bool xdr_LINK3resfail(XDR *, LINK3resfail *); extern bool xdr_LINK3res(XDR *, LINK3res *); extern bool xdr_READDIR3args(XDR *, READDIR3args *); extern bool xdr_entry3(XDR *, entry3 *); extern bool xdr_dirlist3(XDR *, dirlist3 *); extern bool xdr_READDIR3resok(XDR *, READDIR3resok *); extern bool xdr_READDIR3resfail(XDR *, READDIR3resfail *); extern bool xdr_READDIR3res(XDR *, READDIR3res *); extern bool xdr_READDIRPLUS3args(XDR *, READDIRPLUS3args *); extern bool xdr_entryplus3(XDR *, entryplus3 *); extern bool xdr_dirlistplus3(XDR *, dirlistplus3 *); extern bool xdr_READDIRPLUS3resok(XDR *, READDIRPLUS3resok *); extern bool xdr_READDIRPLUS3resfail(XDR *, READDIRPLUS3resfail *); extern bool xdr_READDIRPLUS3res(XDR *, READDIRPLUS3res *); extern bool xdr_FSSTAT3args(XDR *, FSSTAT3args *); extern bool xdr_FSSTAT3resok(XDR *, FSSTAT3resok *); extern bool xdr_FSSTAT3resfail(XDR *, FSSTAT3resfail *); extern bool xdr_FSSTAT3res(XDR *, FSSTAT3res *); extern bool xdr_FSINFO3args(XDR *, FSINFO3args *); extern bool xdr_FSINFO3resok(XDR *, FSINFO3resok *); extern bool xdr_FSINFO3resfail(XDR *, FSINFO3resfail *); extern bool xdr_FSINFO3res(XDR *, FSINFO3res *); extern bool xdr_PATHCONF3args(XDR *, PATHCONF3args *); extern bool xdr_PATHCONF3resok(XDR *, PATHCONF3resok *); extern bool xdr_PATHCONF3resfail(XDR *, PATHCONF3resfail *); extern bool xdr_PATHCONF3res(XDR *, PATHCONF3res *); extern bool xdr_COMMIT3args(XDR *, COMMIT3args *); extern bool xdr_COMMIT3resok(XDR *, COMMIT3resok *); extern bool xdr_COMMIT3resfail(XDR *, COMMIT3resfail *); extern bool xdr_COMMIT3res(XDR *, COMMIT3res *); extern bool xdr_fhandle2(XDR *, fhandle2); extern bool xdr_fhstatus2(XDR *, fhstatus2 *); #endif /* !_NFS23_H_RPCGEN */ nfs-ganesha-2.6.0/src/include/nfs4.h000066400000000000000000000004121324272410200171400ustar00rootroot00000000000000/* * Local RPC definitions, especially the GSS switch and * compensating definitions if we don't have GSS. */ #include "gsh_rpc.h" /* Now the NFS stuff we're looking for */ #include "nfsv41.h" #ifndef NFS4_MAX_DOMAIN_LEN #define NFS4_MAX_DOMAIN_LEN 512 #endif nfs-ganesha-2.6.0/src/include/nfs4_acls.h000066400000000000000000000032501324272410200201450ustar00rootroot00000000000000/* * Copyright IBM Corporation, 2012 * Contributor: Frank Filz * * -------------------------- * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * */ #ifndef _NFS4_ACLS_H #define _NFS4_ACLS_H #include "fsal_types.h" /* Define the return value of ACL operation. */ typedef int fsal_acl_status_t; #define NFS_V4_ACL_SUCCESS 0 #define NFS_V4_ACL_ERROR 1 #define NFS_V4_ACL_EXISTS 2 #define NFS_V4_ACL_INTERNAL_ERROR 3 #define NFS_V4_ACL_UNAPPROPRIATED_KEY 4 #define NFS_V4_ACL_HASH_SET_ERROR 5 #define NFS_V4_ACL_INIT_ENTRY_FAILED 6 #define NFS_V4_ACL_NOT_FOUND 7 fsal_acl_t *nfs4_acl_alloc(); fsal_ace_t *nfs4_ace_alloc(int nace); void nfs4_acl_free(fsal_acl_t *acl); void nfs4_ace_free(fsal_ace_t *pace); void nfs4_acl_entry_inc_ref(fsal_acl_t *pacl); fsal_acl_t *nfs4_acl_new_entry(fsal_acl_data_t *pacldata, fsal_acl_status_t *pstatus); fsal_acl_status_t nfs4_acl_release_entry(fsal_acl_t *pacl); int nfs4_acls_init(void); #endif /* _NFS4_ACLS_H */ nfs-ganesha-2.6.0/src/include/nfs_convert.h000066400000000000000000000036011324272410200206170ustar00rootroot00000000000000/* * * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_convert.h * @brief Prototypes for miscellaneous conversion routines. * * nfs_convert.h : Prototypes for miscellaneous conversion routines. * * */ #ifndef _NFS_CONVERT_H #define _NFS_CONVERT_H #include "nfs23.h" #include "nfs4.h" #include "mount.h" #include "fsal.h" char *nfsstat3_to_str(nfsstat3 code); char *nfsstat4_to_str(nfsstat4 code); char *nfstype3_to_str(ftype3 code); const char *auth_stat2str(enum auth_stat); uint64_t nfs_htonl64(uint64_t); uint64_t nfs_ntohl64(uint64_t); /* Error conversion routines */ nfsstat4 nfs4_Errno_verbose(fsal_errors_t, const char *); #define nfs4_Errno(e) nfs4_Errno_verbose(e, __func__) #define nfs4_Errno_status(e) nfs4_Errno_verbose(e.major, __func__) #ifdef _USE_NFS3 nfsstat3 nfs3_Errno_verbose(fsal_errors_t, const char *); #define nfs3_Errno(e) nfs3_Errno_verbose(e, __func__) #define nfs3_Errno_status(e) nfs3_Errno_verbose(e.major, __func__) #endif #endif /* _NFS_CONVERT_H */ nfs-ganesha-2.6.0/src/include/nfs_core.h000066400000000000000000000113011324272410200200630ustar00rootroot00000000000000/* * * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_core.h * @brief Prototypes for the different threads in the nfs core */ #ifndef NFS_CORE_H #define NFS_CORE_H #include #include #include #include #include #include "sal_data.h" #include "gsh_config.h" #include "gsh_wait_queue.h" #ifdef _USE_9P #include "9p.h" #endif #ifdef _ERROR_INJECTION #include "err_inject.h" #endif /* Delegation client cache limits */ #define DELEG_SPACE_LIMIT_FILESZ 102400 /* just 100K, revisit? */ #define DELEG_SPACE_LIMIT_BLOCKS 200 #define XATTR_BUFFERSIZE 4096 char *host_name; /* * Bind protocol family, pending a richer interface model. */ #define P_FAMILY AF_INET6 typedef struct __nfs4_compound { union { int type; struct { CB_COMPOUND4args args; CB_COMPOUND4res res; } v4; } v_u; } nfs4_compound_t; typedef struct _rpc_call rpc_call_t; typedef void (*rpc_call_func) (rpc_call_t *call); #ifdef _HAVE_GSSAPI extern gss_OID_desc krb5oid; #endif /* _HAVE_GSSAPI */ struct _rpc_call { struct clnt_req call_req; rpc_call_channel_t *chan; rpc_call_func call_hook; void *call_arg; void *call_user_data[2]; nfs4_compound_t cbt; uint32_t states; uint32_t flags; }; typedef enum request_type { UNKNOWN_REQUEST, NFS_CALL, NFS_REQUEST, #ifdef _USE_9P _9P_REQUEST, #endif /* _USE_9P */ } request_type_t; typedef struct request_data { union request_content { rpc_call_t call; nfs_request_t req; #ifdef _USE_9P struct _9p_request_data _9p; #endif } r_u; struct glist_head req_q; /* chaining of pending requests */ struct timespec time_queued; /*< The time at which a request was * added to the worker thread queue. */ request_type_t rtype; } request_data_t; /* in nfs_init.c */ extern pool_t *request_pool; struct _nfs_health { uint64_t enqueued_reqs; uint64_t dequeued_reqs; }; extern struct _nfs_health health; bool nfs_health(void); /* ServerEpoch is ServerBootTime unless overriden by -E command line option */ extern struct timespec ServerBootTime; extern time_t ServerEpoch; extern verifier4 NFS4_write_verifier; /*< NFS V4 write verifier */ extern writeverf3 NFS3_write_verifier; /*< NFS V3 write verifier */ extern char *config_path; extern char *pidfile_path; /* * Thread entry functions */ #ifdef _USE_9P void *_9p_dispatcher_thread(void *arg); void _9p_tcp_process_request(struct _9p_request_data *req9p); int _9p_process_buffer(struct _9p_request_data *req9p, char *replydata, u32 *poutlen); int _9p_worker_init(void); int _9p_worker_shutdown(void); void DispatchWork9P(request_data_t *req); #endif #ifdef _USE_9P_RDMA void *_9p_rdma_dispatcher_thread(void *arg); void _9p_rdma_process_request(struct _9p_request_data *req9p); void _9p_rdma_cleanup_conn(msk_trans_t *trans); #endif /* in nfs_rpc_dispatcher_thread.c */ void Clean_RPC(void); void nfs_Init_svc(void); void nfs_rpc_dispatch_stop(void); /* Config parsing routines */ extern config_file_t config_struct; extern struct config_block nfs_core; extern struct config_block nfs_ip_name; #ifdef _HAVE_GSSAPI extern struct config_block krb5_param; #endif extern struct config_block version4_param; /* in nfs_admin_thread.c */ void nfs_Init_admin_thread(void); void *admin_thread(void *UnusedArg); void admin_halt(void); /* Tools */ int compare_state_id(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2); /* used in DBUS-api diagnostic functions (e.g., serialize sessionid) */ int b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize); int b64_pton(char const *src, u_char *target, size_t targsize); unsigned int nfs_core_select_worker_queue(unsigned int avoid_index); int nfs_Init_ip_name(void); void nfs_rpc_destroy_chan(rpc_call_channel_t *chan); int reaper_init(void); int reaper_shutdown(void); #endif /* !NFS_CORE_H */ nfs-ganesha-2.6.0/src/include/nfs_creds.h000066400000000000000000000034651324272410200202470ustar00rootroot00000000000000/* * * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_creds.h * @brief Prototypes for the RPC credentials used in NFS. * * nfs_creds.h : Prototypes for the RPC credentials used in NFS. * * */ #ifndef _NFS_CREDS_H #define _NFS_CREDS_H #include #include #include #include "fsal_types.h" #include "sal_data.h" void init_credentials(void); void clean_credentials(void); void squash_setattr(struct attrlist *attr); nfsstat4 nfs_req_creds(struct svc_req *req); nfsstat4 nfs4_export_check_access(struct svc_req *req); fsal_errors_t nfs_access_op(struct fsal_obj_handle *hdl, uint32_t requested_access, uint32_t *granted_access, uint32_t *supported_access); bool nfs_compare_clientcred(nfs_client_cred_t *cred1, nfs_client_cred_t *cred2); int nfs_rpc_req2client_cred(struct svc_req *req, nfs_client_cred_t *pcred); #endif /* _NFS_CREDS_H */ nfs-ganesha-2.6.0/src/include/nfs_dupreq.h000066400000000000000000000070611324272410200204430ustar00rootroot00000000000000/* * * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_dupreq.h * @brief Prototypes for duplicate requst cache */ #ifndef NFS_DUPREQ_H #define NFS_DUPREQ_H #include #include #include "nfs_core.h" #include "nfs23.h" #include "nfs4.h" #include "nfs_core.h" #include #include enum drc_type { DRC_TCP_V4, /*< safe to use an XID-based, per-connection DRC */ DRC_TCP_V3, /*< a shared, checksummed DRC per address */ DRC_UDP_V234 /*< UDP is strongly discouraged in RFC 3530bis */ }; #define DRC_FLAG_NONE 0x0000 #define DRC_FLAG_HASH 0x0001 #define DRC_FLAG_CKSUM 0x0002 #define DRC_FLAG_ADDR 0x0004 #define DRC_FLAG_PORT 0x0008 #define DRC_FLAG_LOCKED 0x0010 #define DRC_FLAG_RECYCLE 0x0020 #define DRC_FLAG_RELEASE 0x0040 typedef struct drc { enum drc_type type; struct rbtree_x xt; /* Define the tail queue */ TAILQ_HEAD(drc_tailq, dupreq_entry) dupreq_q; pthread_mutex_t mtx; uint32_t npart; uint32_t cachesz; uint32_t size; uint32_t maxsize; uint32_t hiwat; uint32_t flags; uint32_t refcnt; /* call path refs */ uint32_t retwnd; union { struct { sockaddr_t addr; struct opr_rbtree_node recycle_k; TAILQ_ENTRY(drc) recycle_q; /* XXX drc */ time_t recycle_time; uint64_t hk; /* hash key */ } tcp; } d_u; } drc_t; typedef enum dupreq_state { DUPREQ_START = 0, DUPREQ_COMPLETE, DUPREQ_DELETED } dupreq_state_t; struct dupreq_entry { struct opr_rbtree_node rbt_k; /* Define the tail queue */ TAILQ_ENTRY(dupreq_entry) fifo_q; pthread_mutex_t mtx; struct { drc_t *drc; sockaddr_t addr; struct { uint32_t rq_xid; uint64_t checksum; } tcp; uint32_t rq_prog; uint32_t rq_vers; uint32_t rq_proc; } hin; uint64_t hk; /* hash key */ dupreq_state_t state; uint32_t refcnt; nfs_res_t *res; time_t timestamp; }; typedef struct dupreq_entry dupreq_entry_t; extern pool_t *nfs_res_pool; static inline nfs_res_t *alloc_nfs_res(void) { return pool_alloc(nfs_res_pool); } static inline void free_nfs_res(nfs_res_t *res) { pool_free(nfs_res_pool, res); } typedef enum dupreq_status { DUPREQ_SUCCESS = 0, DUPREQ_INSERT_MALLOC_ERROR, DUPREQ_BEING_PROCESSED, DUPREQ_EXISTS, DUPREQ_ERROR, } dupreq_status_t; void dupreq2_pkginit(void); void dupreq2_pkgshutdown(void); drc_t *drc_get_tcp_drc(struct svc_req *); void drc_release_tcp_drc(drc_t *); void nfs_dupreq_put_drc(drc_t *drc, uint32_t flags); dupreq_status_t nfs_dupreq_start(nfs_request_t *, struct svc_req *); dupreq_status_t nfs_dupreq_finish(struct svc_req *, nfs_res_t *); dupreq_status_t nfs_dupreq_delete(struct svc_req *); void nfs_dupreq_rele(struct svc_req *, const nfs_function_desc_t *); #endif /* NFS_DUPREQ_H */ nfs-ganesha-2.6.0/src/include/nfs_exports.h000066400000000000000000000202741324272410200206500ustar00rootroot00000000000000/* * * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_exports.h * @brief Prototypes for what's related to export list management. * @note not called by other header files. * * This file contains prototypes and data structures for related to * export list management and the NFSv4 compound. */ #ifndef NFS_EXPORTS_H #define NFS_EXPORTS_H #include #include #ifdef _HAVE_GSSAPI #include #include #endif #include "config_parsing.h" #include "export_mgr.h" #include "fsal_types.h" #include "log.h" #include "cidr.h" /* * Export List structure */ #define EXPORT_KEY_SIZE 8 #define ANON_UID -2 #define ANON_GID -2 #define EXPORT_LINESIZE 1024 #define INPUT_SIZE 1024 typedef enum exportlist_client_type__ { PROTO_CLIENT = 0, NETWORK_CLIENT = 1, NETGROUP_CLIENT = 2, WILDCARDHOST_CLIENT = 3, GSSPRINCIPAL_CLIENT = 4, MATCH_ANY_CLIENT = 5, BAD_CLIENT = 6 } exportlist_client_type_t; struct global_export_perms { struct export_perms def; struct export_perms conf; /** Expiration time interval in seconds for attributes. Settable with Attr_Expiration_Time. */ int32_t expire_time_attr; }; #define GSS_DEFINE_LEN_TEMP 255 typedef struct exportlist_client_entry__ { struct glist_head cle_list; exportlist_client_type_t type; union { struct { CIDR *cidr; } network; struct { char *netgroupname; } netgroup; struct { char *wildcard; } wildcard; struct { char *princname; } gssprinc; } client; struct export_perms client_perms; /*< Available mount options */ } exportlist_client_entry_t; /* Constants for export options masks */ #define EXPORT_OPTION_FSID_SET 0x00000001 /* Set if Filesystem_id is set */ #define EXPORT_OPTION_USE_COOKIE_VERIFIER 0x00000002 /* Use cookie verifier */ #define EXPORT_OPTION_EXPIRE_SET 0x00000004 /*< Inode expire was set */ /** Controls whether a directory's dirent cache is trusted for negative results. */ #define EXPORT_OPTION_TRUST_READIR_NEGATIVE_CACHE 0x00000008 #define EXPORT_OPTION_MAXREAD_SET 0x00000010 /* Set if MaxRead was specified */ #define EXPORT_OPTION_MAXWRITE_SET 0x00000020 /* Set if MaxWrite was specified */ #define EXPORT_OPTION_PREFREAD_SET 0x00000040 /* Set if PrefRead was specified */ #define EXPORT_OPTION_PREFWRITE_SET 0x00000080 /* Set if PrefWrite was specified */ /* Constants for export permissions masks */ #define EXPORT_OPTION_ROOT 0 /*< Allow root access as root uid */ #define EXPORT_OPTION_ROOT_ID_SQUASH 0x00000001 /*< Disallow root access as root uid but preserve alt_groups */ #define EXPORT_OPTION_ROOT_SQUASH 0x00000002 /*< Disallow root access as root uid */ #define EXPORT_OPTION_ALL_ANONYMOUS 0x00000004 /*< all users are squashed to anonymous */ #define EXPORT_OPTION_SQUASH_TYPES (EXPORT_OPTION_ROOT_SQUASH | \ EXPORT_OPTION_ROOT_ID_SQUASH | \ EXPORT_OPTION_ALL_ANONYMOUS) /*< All squash types */ #define EXPORT_OPTION_ANON_UID_SET 0x00000008 /*< Indicates Anon_uid was set */ #define EXPORT_OPTION_ANON_GID_SET 0x00000010 /*< Indicates Anon_gid was set */ #define EXPORT_OPTION_READ_ACCESS 0x00000020 /*< R_Access= option specified */ #define EXPORT_OPTION_WRITE_ACCESS 0x00000040 /*< RW_Access= option specified */ #define EXPORT_OPTION_RW_ACCESS (EXPORT_OPTION_READ_ACCESS | \ EXPORT_OPTION_WRITE_ACCESS) #define EXPORT_OPTION_MD_READ_ACCESS 0x00000080 /*< MDONLY_RO_Access= option specified */ #define EXPORT_OPTION_MD_WRITE_ACCESS 0x00000100 /*< MDONLY_Access= option specified */ #define EXPORT_OPTION_MD_ACCESS (EXPORT_OPTION_MD_WRITE_ACCESS | \ EXPORT_OPTION_MD_READ_ACCESS) #define EXPORT_OPTION_MODIFY_ACCESS (EXPORT_OPTION_WRITE_ACCESS | \ EXPORT_OPTION_MD_WRITE_ACCESS) #define EXPORT_OPTION_ACCESS_MASK (EXPORT_OPTION_READ_ACCESS | \ EXPORT_OPTION_WRITE_ACCESS | \ EXPORT_OPTION_MD_WRITE_ACCESS | \ EXPORT_OPTION_MD_READ_ACCESS) #define EXPORT_OPTION_NO_ACCESS 0 /*< Access_Type = None */ #define EXPORT_OPTION_PRIVILEGED_PORT 0x00000200 /*< Clients use only privileged port */ #define EXPORT_OPTION_COMMIT 0x00000400 /*< NFS Commit writes */ #define EXPORT_OPTION_DISABLE_ACL 0x00000800 /*< ACL is disabled */ /* @todo BUGAZOMEU : Mettre au carre les flags des flavors */ #define EXPORT_OPTION_AUTH_NONE 0x00001000 /*< Auth None authentication supported */ #define EXPORT_OPTION_AUTH_UNIX 0x00002000 /*< Auth Unix authentication supported */ #define EXPORT_OPTION_RPCSEC_GSS_NONE 0x00004000 /*< RPCSEC_GSS_NONE supported */ #define EXPORT_OPTION_RPCSEC_GSS_INTG 0x00008000 /*< RPCSEC_GSS INTEGRITY supported */ #define EXPORT_OPTION_RPCSEC_GSS_PRIV 0x00010000 /*< RPCSEC_GSS PRIVACY supported */ #define EXPORT_OPTION_AUTH_TYPES (EXPORT_OPTION_AUTH_NONE | \ EXPORT_OPTION_AUTH_UNIX | \ EXPORT_OPTION_RPCSEC_GSS_NONE | \ EXPORT_OPTION_RPCSEC_GSS_INTG | \ EXPORT_OPTION_RPCSEC_GSS_PRIV) #define EXPORT_OPTION_AUTH_DEFAULTS (EXPORT_OPTION_AUTH_NONE | \ EXPORT_OPTION_AUTH_UNIX) /* Protocol flags */ #define EXPORT_OPTION_NFSV3 0x00100000 /*< NFSv3 operations are supported */ #define EXPORT_OPTION_NFSV4 0x00200000 /*< NFSv4 operations are supported */ #define EXPORT_OPTION_9P 0x00400000 /*< 9P operations are supported */ #define EXPORT_OPTION_UDP 0x01000000 /*< UDP protocol is supported */ #define EXPORT_OPTION_TCP 0x02000000 /*< TCP protocol is supported */ #define EXPORT_OPTION_RDMA 0x04000000 /*< RDMA protocol is supported */ #define EXPORT_OPTION_PROTOCOLS (EXPORT_OPTION_NFSV3 | \ EXPORT_OPTION_NFSV4 | \ EXPORT_OPTION_9P) #define EXPORT_OPTION_PROTO_DEFAULTS (EXPORT_OPTION_NFSV3 | \ EXPORT_OPTION_NFSV4) #define EXPORT_OPTION_TRANSPORTS (EXPORT_OPTION_UDP | \ EXPORT_OPTION_TCP | \ EXPORT_OPTION_RDMA) #define EXPORT_OPTION_XPORT_DEFAULTS (EXPORT_OPTION_UDP | \ EXPORT_OPTION_TCP) #define EXPORT_OPTION_READ_DELEG 0x10000000 /*< Enable read delegations */ #define EXPORT_OPTION_WRITE_DELEG 0x20000000 /*< Using write delegations */ #define EXPORT_OPTION_DELEGATIONS (EXPORT_OPTION_READ_DELEG | \ EXPORT_OPTION_WRITE_DELEG) #define EXPORT_OPTION_NO_DELEGATIONS 0 #define EXPORT_OPTION_MANAGE_GIDS 0x40000000 /*< Do not trust altgrp in AUTH_SYS creds */ #define EXPORT_OPTION_NO_READDIR_PLUS 0x80000000 /*< Disallow readdir plus */ /* Export list related functions */ uid_t get_anonymous_uid(void); gid_t get_anonymous_gid(void); void export_check_access(void); bool export_check_security(struct svc_req *req); int init_export_root(struct gsh_export *exp); fsal_status_t nfs_export_get_root_entry(struct gsh_export *exp, struct fsal_obj_handle **obj); void unexport(struct gsh_export *exp); /* XXX */ /*void kill_export_root_entry(cache_entry_t *entry);*/ /*void kill_export_junction_entry(cache_entry_t *entry);*/ int ReadExports(config_file_t in_config, struct config_error_type *err_type); int reread_exports(config_file_t in_config, struct config_error_type *err_type); void free_export_resources(struct gsh_export *exp); void exports_pkginit(void); #endif /* !NFS_EXPORTS_H */ nfs-ganesha-2.6.0/src/include/nfs_fh.h000066400000000000000000000046571324272410200175500ustar00rootroot00000000000000/* * * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * Eshel Marc eshel@us.ibm.com * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_fh.h * @brief Prototypes for the file handle in v3 and v4 */ #ifndef NFS_FH_H #define NFS_FH_H #ifndef __FreeBSD__ #include "byteswap.h" #endif /* * Structure of the filehandle * these structures must be naturally aligned. The xdr buffer from/to which * they come/go are 4 byte aligned. */ #define GANESHA_FH_VERSION 0x43 #define FILE_HANDLE_V4_FLAG_DS 0x01 /*< handle for a DS */ #define FH_FSAL_BIG_ENDIAN 0x40 /*< FSAL FH is big endian */ /** * @brief An NFSv3 handle * * This may be up to 64 bytes long, aligned on 32 bits */ typedef struct file_handle_v3 { uint8_t fhversion; /*< Set to GANESHA_FH_VERSION */ uint8_t fhflags1; /*< To replace things like ds_flag */ uint16_t exportid; /*< Must be correlated to exportlist_t::id */ uint8_t fs_len; /*< Actual length of opaque handle */ uint8_t fsopaque[]; /*< Persistent part of FSAL handle, <= 59 bytes */ } file_handle_v3_t; /** * @brief An NFSv4 filehandle * * This may be up to 128 bytes, aligned on 32 bits. */ typedef struct __attribute__ ((__packed__)) file_handle_v4 { uint8_t fhversion; /*< Set to 0x41 to separate from Linux knfsd */ uint8_t fhflags1; /*< To replace things like ds_flag */ union { uint16_t exports; /*< FSAL exports, export_by_id */ uint16_t servers; /*< FSAL servers, server_by_id */ } id; uint8_t fs_len; /*< Length of opaque handle */ uint8_t fsopaque[]; /*< FSAL handle */ } file_handle_v4_t; #endif /* NFS_FH_H */ nfs-ganesha-2.6.0/src/include/nfs_file_handle.h000066400000000000000000000137351324272410200214020ustar00rootroot00000000000000/* * * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_file_handle.h * @brief Prototypes for the file handle in v3 and v4 */ #ifndef NFS_FILE_HANDLE_H #define NFS_FILE_HANDLE_H #include #include #include "log.h" #include "sal_data.h" #include "export_mgr.h" #include "nfs_fh.h" /** * @brief Get the actual size of a v3 handle based on the sized fsopaque * * @return The filehandle size */ static inline size_t nfs3_sizeof_handle(struct file_handle_v3 *hdl) { int hsize; int aligned_hsize; hsize = offsetof(struct file_handle_v3, fsopaque) + hdl->fs_len; /* correct packet's fh length so it's divisible by 4 to trick dNFS into working. This is essentially sending the padding. */ aligned_hsize = roundup(hsize, 4); if (aligned_hsize <= NFS3_FHSIZE) hsize = aligned_hsize; return hsize; } /** * * @brief Allocates a buffer to be used for storing a NFSv4 filehandle. * * Allocates a buffer to be used for storing a NFSv3 filehandle. * * @param fh [INOUT] the filehandle to manage. * */ static inline void nfs3_AllocateFH(nfs_fh3 *fh) { /* Allocating the filehandle in memory */ fh->data.data_len = NFS3_FHSIZE; fh->data.data_val = gsh_calloc(1, NFS3_FHSIZE); } static inline void nfs3_freeFH(nfs_fh3 *fh) { fh->data.data_len = 0; gsh_free(fh->data.data_val); fh->data.data_val = NULL; } /** * * @brief Allocates a buffer to be used for storing a NFSv4 filehandle. * * Allocates a buffer to be used for storing a NFSv4 filehandle. * * @param fh [INOUT] the filehandle to manage. * */ static inline void nfs4_AllocateFH(nfs_fh4 *fh) { /* Allocating the filehandle in memory */ fh->nfs_fh4_len = NFS4_FHSIZE; fh->nfs_fh4_val = gsh_calloc(1, NFS4_FHSIZE); } static inline void nfs4_freeFH(nfs_fh4 *fh) { fh->nfs_fh4_len = 0; gsh_free(fh->nfs_fh4_val); fh->nfs_fh4_val = NULL; } /** * @brief Get the actual size of a v4 handle based on the sized fsopaque * * @return The filehandle size */ static inline size_t nfs4_sizeof_handle(struct file_handle_v4 *hdl) { return offsetof(struct file_handle_v4, fsopaque)+hdl->fs_len; } #define LEN_FH_STR 1024 /* File handle translation utility */ #ifdef _USE_NFS3 struct fsal_obj_handle *nfs3_FhandleToCache(nfs_fh3 *, nfsstat3 *, int *); #endif bool nfs4_FSALToFhandle(bool allocate, nfs_fh4 *fh4, const struct fsal_obj_handle *fsalhandle, struct gsh_export *exp); bool nfs3_FSALToFhandle(bool allocate, nfs_fh3 *fh3, const struct fsal_obj_handle *fsalhandle, struct gsh_export *exp); /* nfs3 validation */ int nfs3_Is_Fh_Invalid(nfs_fh3 *); /** * * nfs3_FhandleToExportId * * This routine extracts the export id from the file handle NFSv3 * * @param pfh3 [IN] file handle to manage. * * @return the export id. * */ static inline int nfs3_FhandleToExportId(nfs_fh3 *pfh3) { file_handle_v3_t *pfile_handle; if (nfs3_Is_Fh_Invalid(pfh3) != NFS4_OK) return -1; /* Badly formed argument */ pfile_handle = (file_handle_v3_t *) (pfh3->data.data_val); /*exportid is in network byte order in nfs_fh3*/ return ntohs(pfile_handle->exportid); } /* nfs3_FhandleToExportId */ static inline int nlm4_FhandleToExportId(netobj *pfh3) { nfs_fh3 fh3; if (pfh3 == NULL) return nfs3_FhandleToExportId(NULL); fh3.data.data_val = pfh3->n_bytes; fh3.data.data_len = pfh3->n_len; return nfs3_FhandleToExportId(&fh3); } /** * * @brief Test if an NFS v4 file handle is empty. * * This routine is used to test if a fh is empty (contains no data). * * @param pfh [IN] file handle to test. * * @return NFS4_OK if successfull, NFS4ERR_NOFILEHANDLE is fh is empty. * */ static inline int nfs4_Is_Fh_Empty(nfs_fh4 *pfh) { if (pfh == NULL) { LogMajor(COMPONENT_FILEHANDLE, "INVALID HANDLE: pfh=NULL"); return NFS4ERR_NOFILEHANDLE; } if (pfh->nfs_fh4_len == 0) { LogInfo(COMPONENT_FILEHANDLE, "INVALID HANDLE: empty"); return NFS4ERR_NOFILEHANDLE; } return NFS4_OK; } /* nfs4_Is_Fh_Empty */ /* NFSv4 specific FH related functions */ int nfs4_Is_Fh_Invalid(nfs_fh4 *); int nfs4_Is_Fh_DSHandle(nfs_fh4 *); nfsstat4 nfs4_sanity_check_FH(compound_data_t *data, object_file_type_t required_type, bool ds_allowed); nfsstat4 nfs4_sanity_check_saved_FH(compound_data_t *data, int required_type, bool ds_allowed); /* File handle print function (mostly used for debugging) */ void print_fhandle3(log_components_t, nfs_fh3 *); void print_fhandle4(log_components_t, nfs_fh4 *); void print_fhandle_nlm(log_components_t, netobj *); void print_buff(log_components_t, char *, int); void LogCompoundFH(compound_data_t *); void sprint_fhandle3(char *str, nfs_fh3 *fh); void sprint_fhandle4(char *str, nfs_fh4 *fh); void sprint_fhandle_nlm(char *str, netobj *fh); void sprint_buff(char *str, char *buff, int len); void sprint_mem(char *str, char *buff, int len); void nfs_FhandleToStr(u_long rq_vers, nfs_fh3 *pfh3, nfs_fh4 *pfh4, char *str); #define LogHandleNFS4(label, fh4) \ do { \ if (isFullDebug(COMPONENT_NFS_V4)) { \ char str[LEN_FH_STR]; \ sprint_fhandle4(str, fh4); \ LogFullDebug(COMPONENT_NFS_V4, "%s%s", label, str); \ } \ } while (0) #endif /* NFS_FILE_HANDLE_H */ nfs-ganesha-2.6.0/src/include/nfs_init.h000066400000000000000000000062031324272410200201030ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_init.h * @brief NFSd initialization prototypes. */ #ifndef NFS_INIT_H #define NFS_INIT_H #include "log.h" #include "nfs_core.h" #include "gsh_rpc.h" typedef struct __nfs_start_info { int dump_default_config; int lw_mark_trigger; bool drop_caps; } nfs_start_info_t; struct nfs_init { pthread_mutex_t init_mutex; pthread_cond_t init_cond; bool init_complete; }; extern struct nfs_init nfs_init; pthread_t gsh_dbus_thrid; void nfs_init_init(void); void nfs_init_complete(void); void nfs_init_wait(void); /** * nfs_prereq_init: * Initialize NFSd prerequisites: memory management, logging, ... */ void nfs_prereq_init(char *program_name, char *host_name, int debug_level, char *log_path, bool dump_trace); /** * nfs_set_param_from_conf: * Load parameters from config file. */ int nfs_set_param_from_conf(config_file_t config_struct, nfs_start_info_t *p_start_info, struct config_error_type *err_type); /** * Initialization that needs config file parse but must be done * before any services actually start (exports, net sockets...) */ int init_server_pkgs(void); /** * nfs_start: * start NFS service */ void nfs_start(nfs_start_info_t *p_start_info); /** * check for useable malloc implementation */ static inline void nfs_check_malloc(void) { /* Check malloc(0) - Ganesha assumes malloc(0) returns non-NULL pointer. * Note we use malloc and calloc directly here and not gsh_malloc and * gsh_calloc because we don't want those functions to abort(), we * want to log a descriptive message. */ void *p; p = malloc(0); if (p == NULL) LogFatal(COMPONENT_MAIN, "Ganesha assumes malloc(0) returns a non-NULL pointer."); free(p); p = calloc(0, 0); if (p == NULL) LogFatal(COMPONENT_MAIN, "Ganesha assumes calloc(0, 0) returns a non-NULL pointer."); free(p); } /* in nfs_rpc_dispatcher_thread.c */ int free_nfs_request(request_data_t *); /* in nfs_worker_thread.c */ enum xprt_stat nfs_rpc_valid_NFS(struct svc_req *); enum xprt_stat nfs_rpc_valid_NLM(struct svc_req *); enum xprt_stat nfs_rpc_valid_MNT(struct svc_req *); enum xprt_stat nfs_rpc_valid_RQUOTA(struct svc_req *); #endif /* !NFS_INIT_H */ nfs-ganesha-2.6.0/src/include/nfs_ip_stats.h000066400000000000000000000022001324272410200207570ustar00rootroot00000000000000#ifndef _NFS_IP_STATS_H #define _NFS_IP_STATS_H #include #include #include "gsh_rpc.h" #include /* for having MAXHOSTNAMELEN */ #include "hashtable.h" /* IP/name cache error */ #define IP_NAME_SUCCESS 0 #define IP_NAME_INSERT_MALLOC_ERROR 1 #define IP_NAME_NOT_FOUND 2 #define IP_NAME_NETDB_ERROR 3 #define IP_NAME_PREALLOC_SIZE 200 /* NFS IPaddr cache entry structure */ typedef struct nfs_ip_name__ { time_t timestamp; char hostname[MAXHOSTNAMELEN + 1]; } nfs_ip_name_t; int nfs_ip_name_get(sockaddr_t *ipaddr, char *hostname, size_t size); int nfs_ip_name_add(sockaddr_t *ipaddr, char *hostname, size_t size); int nfs_ip_name_remove(sockaddr_t *ipaddr); int display_ip_name_key(struct gsh_buffdesc *pbuff, char *str); int display_ip_name_val(struct gsh_buffdesc *pbuff, char *str); int compare_ip_name(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2); uint64_t ip_name_rbt_hash_func(hash_parameter_t *p_hparam, struct gsh_buffdesc *buffclef); uint32_t ip_name_value_hash_func(hash_parameter_t *p_hparam, struct gsh_buffdesc *buffclef); #endif nfs-ganesha-2.6.0/src/include/nfs_lib.h000066400000000000000000000023431324272410200177070ustar00rootroot00000000000000/* * * * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_lib.h * @brief an minimal library interface to the ganesha server */ #ifndef NFS_LIB_H #define NFS_LIB_H extern char *config_path; extern int nfs_libmain( const char *config_path, const char *log_path, const int debug_level); #endif /* !NFS_LIB_H */ /** @} */ nfs-ganesha-2.6.0/src/include/nfs_proto_data.h000066400000000000000000000271741324272410200213060ustar00rootroot00000000000000/* * Copyright (C) 2014 CohortFS, LLC. * Author: William Allen Simpson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ /** * @defgroup NFS compound data * @{ */ /** * @file nfs_proto_data.h * @author William Allen Simpson * @date Wed Nov 19 14:11:19 2014 * * @brief NFS compound data, function arguments, and return status * * @note called only from sal_data.h */ #ifndef NFS_PROTO_DATA_H #define NFS_PROTO_DATA_H #include "fsal_api.h" #include "rquota.h" /* * mount was autogenerated, and requires several headers to compile; * rather than scattered through the code, consolidated in nfs23.h. * (Used by the mass compound unions here.) */ #include "nfs23.h" #include "nfs4.h" #include "nlm4.h" /* ------------------------------ Typedefs and structs----------------------- */ typedef union nfs_arg__ { GETATTR3args arg_getattr3; SETATTR3args arg_setattr3; LOOKUP3args arg_lookup3; ACCESS3args arg_access3; READLINK3args arg_readlink3; READ3args arg_read3; WRITE3args arg_write3; CREATE3args arg_create3; MKDIR3args arg_mkdir3; SYMLINK3args arg_symlink3; MKNOD3args arg_mknod3; REMOVE3args arg_remove3; RMDIR3args arg_rmdir3; RENAME3args arg_rename3; LINK3args arg_link3; READDIR3args arg_readdir3; READDIRPLUS3args arg_readdirplus3; FSSTAT3args arg_fsstat3; FSINFO3args arg_fsinfo3; PATHCONF3args arg_pathconf3; COMMIT3args arg_commit3; COMPOUND4args arg_compound4; /* mnt */ dirpath arg_mnt; /* nlm */ nlm4_testargs arg_nlm4_test; nlm4_lockargs arg_nlm4_lock; nlm4_cancargs arg_nlm4_cancel; nlm4_shareargs arg_nlm4_share; nlm4_unlockargs arg_nlm4_unlock; nlm4_sm_notifyargs arg_nlm4_sm_notify; nlm4_free_allargs arg_nlm4_free_allargs; nlm4_res arg_nlm4_res; /* Rquota */ getquota_args arg_rquota_getquota; getquota_args arg_rquota_getactivequota; setquota_args arg_rquota_setquota; setquota_args arg_rquota_setactivequota; /* Ext Rquota */ ext_getquota_args arg_ext_rquota_getquota; ext_getquota_args arg_ext_rquota_getactivequota; ext_setquota_args arg_ext_rquota_setquota; ext_setquota_args arg_ext_rquota_setactivequota; } nfs_arg_t; struct COMPOUND4res_extended { COMPOUND4res res_compound4; bool res_cached; }; typedef union nfs_res__ { GETATTR3res res_getattr3; SETATTR3res res_setattr3; LOOKUP3res res_lookup3; ACCESS3res res_access3; READLINK3res res_readlink3; READ3res res_read3; WRITE3res res_write3; CREATE3res res_create3; MKDIR3res res_mkdir3; SYMLINK3res res_symlink3; MKNOD3res res_mknod3; REMOVE3res res_remove3; RMDIR3res res_rmdir3; RENAME3res res_rename3; LINK3res res_link3; READDIR3res res_readdir3; READDIRPLUS3res res_readdirplus3; FSSTAT3res res_fsstat3; FSINFO3res res_fsinfo3; PATHCONF3res res_pathconf3; COMMIT3res res_commit3; COMPOUND4res res_compound4; struct COMPOUND4res_extended res_compound4_extended; /* mount */ fhstatus2 res_mnt1; exports res_mntexport; mountres3 res_mnt3; mountlist res_dump; /* nlm4 */ nlm4_testres res_nlm4test; nlm4_res res_nlm4; nlm4_shareres res_nlm4share; /* Rquota */ getquota_rslt res_rquota_getquota; getquota_rslt res_rquota_getactivequota; setquota_rslt res_rquota_setquota; setquota_rslt res_rquota_setactivequota; /* Ext Rquota */ getquota_rslt res_ext_rquota_getquota; getquota_rslt res_ext_rquota_getactivequota; setquota_rslt res_ext_rquota_setquota; setquota_rslt res_ext_rquota_setactivequota; } nfs_res_t; /* flags related to the behaviour of the requests * (to be stored in the dispatch behaviour field) */ #define NOTHING_SPECIAL 0x0000 /* Nothing to be done for this kind of request */ #define MAKES_WRITE 0x0001 /* The function modifyes the FSAL (not permitted for RO FS) */ #define NEEDS_CRED 0x0002 /* A credential is needed for this operation */ #define CAN_BE_DUP 0x0004 /* Handling of dup request can be done for this request */ #define SUPPORTS_GSS 0x0008 /* Request may be authenticated by RPCSEC_GSS */ #define MAKES_IO 0x0010 /* Request may do I/O (not allowed on MD ONLY exports */ typedef int (*nfs_protocol_function_t) (nfs_arg_t *, struct svc_req *, nfs_res_t *); typedef int (*nfsremote_protocol_function_t) (CLIENT *, nfs_arg_t *, nfs_res_t *); typedef void (*nfs_protocol_free_t) (nfs_res_t *); typedef struct nfs_function_desc__ { nfs_protocol_function_t service_function; nfs_protocol_free_t free_function; xdrproc_t xdr_decode_func; xdrproc_t xdr_encode_func; char *funcname; unsigned int dispatch_behaviour; } nfs_function_desc_t; typedef struct nfs_request { struct svc_req svc; struct nfs_request_lookahead lookahead; nfs_arg_t arg_nfs; nfs_res_t *res_nfs; const nfs_function_desc_t *funcdesc; } nfs_request_t; enum rpc_chan_type { RPC_CHAN_V40, RPC_CHAN_V41 }; typedef struct rpc_call_channel { enum rpc_chan_type type; pthread_mutex_t mtx; uint32_t states; union { nfs_client_id_t *clientid; nfs41_session_t *session; } source; time_t last_called; CLIENT *clnt; AUTH *auth; #ifdef _HAVE_GSSAPI struct rpc_gss_sec gss_sec; #endif /* _HAVE_GSSAPI */ } rpc_call_channel_t; /** * @todo Matt: this is automatically redundant, but in fact upstream * TI-RPC is not up-to-date with RFC 5665, will fix (Matt) * * @copyright 2012-2017, Linux Box Corp */ enum rfc_5665_nc_type { _NC_ERR, _NC_TCP, _NC_TCP6, _NC_RDMA, _NC_RDMA6, _NC_SCTP, _NC_SCTP6, _NC_UDP, _NC_UDP6, }; typedef enum rfc_5665_nc_type nc_type; struct __netid_nc_table { const char *netid; int netid_len; /* nc_type */ const nc_type nc; int af; }; extern const struct __netid_nc_table netid_nc_table[9]; nc_type nfs_netid_to_nc(const char *netid); void nfs_set_client_location(nfs_client_id_t *pclientid, const clientaddr4 *addr4); /* end TI-RPC */ typedef struct gsh_addr { nc_type nc; struct sockaddr_storage ss; uint32_t port; } gsh_addr_t; /* NFS4 specific structures */ typedef struct nfs_client_cred_gss { unsigned int svc; unsigned int qop; #ifdef _HAVE_GSSAPI struct svc_rpc_gss_data *gd; #endif } nfs_client_cred_gss_t; typedef struct nfs_client_cred__ { unsigned int flavor; unsigned int length; union { struct authunix_parms auth_unix; nfs_client_cred_gss_t auth_gss; } auth_union; } nfs_client_cred_t; /** * @brief NFS v4 Compound Data * * This structure contains the necessary stuff for keeping the state * of a V4 compound request. */ /** * @brief Compound data * * This structure contains the necessary stuff for keeping the state * of a V4 compound request. */ typedef struct compound_data { nfs_fh4 currentFH; /*< Current filehandle */ nfs_fh4 savedFH; /*< Saved filehandle */ stateid4 current_stateid; /*< Current stateid */ bool current_stateid_valid; /*< Current stateid is valid */ stateid4 saved_stateid; /*< Saved stateid */ bool saved_stateid_valid; /*< Saved stateid is valid */ unsigned int minorversion; /*< NFSv4 minor version */ struct fsal_obj_handle *current_obj; /*< Current object handle */ struct fsal_obj_handle *saved_obj; /*< saved object handle */ struct fsal_ds_handle *current_ds; /*< current ds handle */ struct fsal_ds_handle *saved_ds; /*< Saved DS handle */ object_file_type_t current_filetype; /*< File type of current obj */ object_file_type_t saved_filetype; /*< File type of saved entry */ struct gsh_export *saved_export; /*< Export entry related to the savedFH */ struct export_perms saved_export_perms; /*< Permissions for export for savedFH */ struct svc_req *req; /*< RPC Request related to the compound */ nfs_client_cred_t credential; /*< Raw RPC credentials */ nfs_client_id_t *preserved_clientid; /*< clientid that has lease reserved, if any */ struct COMPOUND4res_extended *cached_res; /*< NFv41: pointer to cached RPC result in a session's slot */ bool use_drc; /*< Set to true if session DRC is to be used */ uint32_t oppos; /*< Position of the operation within the request processed */ nfs41_session_t *session; /*< Related session (found by OP_SEQUENCE) */ sequenceid4 sequence; /*< Sequence ID of the current compound (if applicable) */ slotid4 slot; /*< Slot ID of the current compound (if applicable) */ } compound_data_t; typedef int (*nfs4_op_function_t) (struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); /** * @brief Set the current entry in the context * * This manages refcounting on the object being stored in data. This means it * takes a ref on a new object, and releases it's ref on any old object. If the * caller has it's own ref, it must release it itself. * * @param[in] data Compound data to set entry in * @param[in] obj Object to set */ static inline void set_current_entry(compound_data_t *data, struct fsal_obj_handle *obj) { /* Mark current_stateid as invalid */ data->current_stateid_valid = false; /* Clear out the current_ds */ if (data->current_ds) { ds_handle_put(data->current_ds); data->current_ds = NULL; } if (data->current_obj) { /* Release ref on old object */ data->current_obj->obj_ops.put_ref(data->current_obj); } data->current_obj = obj; if (obj == NULL) { data->current_filetype = NO_FILE_TYPE; return; } /* Get our ref on the new object */ data->current_obj->obj_ops.get_ref(data->current_obj); /* Set the current file type */ data->current_filetype = obj->type; } /** * @brief Set the saved entry in the context * * This manages refcounting on the object being stored in data. This means it * takes a ref on a new object, and releases it's ref on any old object. If the * caller has it's own ref, it must release it itself. * * @param[in] data Compound data to set entry in * @param[in] obj Object to set */ static inline void set_saved_entry(compound_data_t *data, struct fsal_obj_handle *obj) { struct gsh_export *current_export = op_ctx->ctx_export; struct export_perms current_export_perms = *op_ctx->export_perms; bool restore_op_ctx = false; if (data->saved_ds != NULL || data->saved_obj != NULL) { /* Setup correct op_ctx for releasing old saved */ op_ctx->ctx_export = data->saved_export; op_ctx->fsal_export = data->saved_export ? data->saved_export->fsal_export : NULL; *op_ctx->export_perms = data->saved_export_perms; restore_op_ctx = true; } /* Mark saved_stateid as invalid */ data->saved_stateid_valid = false; /* Clear out the saved_ds */ if (data->saved_ds) { ds_handle_put(data->saved_ds); data->saved_ds = NULL; } if (data->saved_obj) { /* Release ref on old object */ data->saved_obj->obj_ops.put_ref(data->saved_obj); } if (restore_op_ctx) { /* Restore op_ctx */ op_ctx->ctx_export = current_export; op_ctx->fsal_export = current_export ? current_export->fsal_export : NULL; *op_ctx->export_perms = current_export_perms; } data->saved_obj = obj; if (obj == NULL) { data->saved_filetype = NO_FILE_TYPE; return; } /* Get our ref on the new object */ data->saved_obj->obj_ops.get_ref(data->saved_obj); /* Set the saved file type */ data->saved_filetype = obj->type; } #endif /* NFS_PROTO_DATA_H */ /** @} */ nfs-ganesha-2.6.0/src/include/nfs_proto_functions.h000066400000000000000000000400541324272410200223750ustar00rootroot00000000000000/* * * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_proto_functions.h * @brief Prototypes for NFS protocol functions. * @note not called by other header files. */ #ifndef NFS_PROTO_FUNCTIONS_H #define NFS_PROTO_FUNCTIONS_H #include #include #include #include "sal_data.h" extern const nfs_function_desc_t nfs3_func_desc[]; extern const nfs_function_desc_t nfs4_func_desc[]; extern const nfs_function_desc_t mnt1_func_desc[]; extern const nfs_function_desc_t mnt3_func_desc[]; extern const nfs_function_desc_t nlm4_func_desc[]; extern const nfs_function_desc_t rquota1_func_desc[]; extern const nfs_function_desc_t rquota2_func_desc[]; int mnt_Null(nfs_arg_t *, struct svc_req *, nfs_res_t *); int mnt_Mnt(nfs_arg_t *, struct svc_req *, nfs_res_t *); int mnt_Dump(nfs_arg_t *, struct svc_req *, nfs_res_t *); int mnt_Umnt(nfs_arg_t *, struct svc_req *, nfs_res_t *); int mnt_UmntAll(nfs_arg_t *, struct svc_req *, nfs_res_t *); int mnt_Export(nfs_arg_t *, struct svc_req *, nfs_res_t *); /* @} * -- End of MNT protocol functions. -- */ int nlm_Null(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nlm4_Test(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nlm4_Lock(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nlm4_Cancel(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nlm4_Unlock(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nlm4_Sm_Notify(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nlm4_Test_Message(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nlm4_Cancel_Message(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nlm4_Lock_Message(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nlm4_Unlock_Message(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nlm4_Granted_Res(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nlm4_Share(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nlm4_Unshare(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nlm4_Free_All(nfs_arg_t *, struct svc_req *, nfs_res_t *); /* @} * -- End of NLM protocol functions. -- */ int rquota_Null(nfs_arg_t *, struct svc_req *, nfs_res_t *); int rquota_getquota(nfs_arg_t *, struct svc_req *, nfs_res_t *); int rquota_getactivequota(nfs_arg_t *, struct svc_req *, nfs_res_t *); int rquota_setquota(nfs_arg_t *, struct svc_req *, nfs_res_t *); int rquota_setactivequota(nfs_arg_t *, struct svc_req *, nfs_res_t *); /* @} * * -- End of RQUOTA protocol functions. -- * */ int nfs_null(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nfs3_getattr(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nfs3_setattr(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nfs3_lookup(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nfs3_readlink(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nfs3_read(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nfs3_write(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nfs3_create(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nfs3_remove(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nfs3_rename(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nfs3_link(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nfs3_symlink(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nfs3_mkdir(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nfs3_rmdir(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nfs3_readdir(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nfs3_fsstat(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nfs3_access(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nfs3_readdirplus(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nfs3_fsinfo(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nfs3_pathconf(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nfs3_commit(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nfs3_mknod(nfs_arg_t *, struct svc_req *, nfs_res_t *); /* Functions needed for nfs v4 */ int nfs4_Compound(nfs_arg_t *, struct svc_req *, nfs_res_t *); int nfs4_op_access(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_close(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_commit(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_create(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_delegpurge(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_delegreturn(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_getattr(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_getfh(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_link(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_lock(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_lockt(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_locku(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_lookup(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_lookupp(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_nverify(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_open(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_open_confirm(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_open_downgrade(struct nfs_argop4 *, compound_data_t *data, struct nfs_resop4 *); int nfs4_op_openattr(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_putfh(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_putpubfh(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_putrootfh(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_read(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_readdir(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_remove(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_renew(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_rename(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_restorefh(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_readlink(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_savefh(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_secinfo(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_secinfo_no_name(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_setattr(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_setclientid(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_setclientid_confirm(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_verify(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_release_lockowner(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_illegal(struct nfs_argop4 *, compound_data_t *data, struct nfs_resop4 *); int nfs4_op_notsupp(struct nfs_argop4 *, compound_data_t *data, struct nfs_resop4 *); int nfs4_op_exchange_id(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_create_session(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_getdevicelist(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_free_stateid(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_getdeviceinfo(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_destroy_clientid(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_destroy_session(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_layoutget(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_layoutcommit(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_layoutreturn(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_reclaim_complete(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_sequence(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_set_ssv(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_test_stateid(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); int nfs4_op_write(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); /* NFSv4.2 */ int nfs4_op_write_same(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); void nfs4_op_write_same_Free(nfs_resop4 *resp); int nfs4_op_read_plus(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); void nfs4_op_read_plus_Free(nfs_resop4 *resp); int nfs4_op_allocate(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); void nfs4_op_allocate_Free(nfs_resop4 *resp); int nfs4_op_deallocate(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); void nfs4_op_deallocate_Free(nfs_resop4 *resp); int nfs4_op_seek(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); void nfs4_op_seek_Free(nfs_resop4 *resp); int nfs4_op_io_advise(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); void nfs4_op_io_advise_Free(nfs_resop4 *resp); int nfs4_op_layouterror(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); void nfs4_op_layouterror_Free(nfs_resop4 *resp); int nfs4_op_layoutstats(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); void nfs4_op_layoutstats_Free(nfs_resop4 *resp); /* NFSv4.3 */ int nfs4_op_getxattr(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); void nfs4_op_getxattr_Free(nfs_resop4 *resp); int nfs4_op_setxattr(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); void nfs4_op_setxattr_Free(nfs_resop4 *resp); int nfs4_op_listxattr(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); void nfs4_op_listxattr_Free(nfs_resop4 *resp); int nfs4_op_removexattr(struct nfs_argop4 *, compound_data_t *, struct nfs_resop4 *); void nfs4_op_removexattr_Free(nfs_resop4 *resp); /* @} * -- End of NFS protocols functions. -- */ #define NFS_REQ_OK 0 #define NFS_REQ_DROP 1 /* Free functions */ void mnt1_Mnt_Free(nfs_res_t *); void mnt3_Mnt_Free(nfs_res_t *); void mnt_Dump_Free(nfs_res_t *); void mnt_Export_Free(nfs_res_t *); void mnt_Null_Free(nfs_res_t *); void mnt_Umnt_Free(nfs_res_t *); void mnt_UmntAll_Free(nfs_res_t *); void nlm_Null_Free(nfs_res_t *); void nlm4_Test_Free(nfs_res_t *); void nlm4_Lock_Free(nfs_res_t *); void nlm4_NM_Lock_Free(nfs_res_t *); void nlm4_Share_Free(nfs_res_t *); void nlm4_Unshare_Free(nfs_res_t *); void nlm4_Cancel_Free(nfs_res_t *); void nlm4_Unlock_Free(nfs_res_t *); void nlm4_Sm_Notify_Free(nfs_res_t *); void nlm4_Granted_Res_Free(nfs_res_t *); void nlm4_Free_All_Free(nfs_res_t *); void rquota_Null_Free(nfs_res_t *); void rquota_getquota_Free(nfs_res_t *); void rquota_getactivequota_Free(nfs_res_t *); void rquota_setquota_Free(nfs_res_t *); void rquota_setactivequota_Free(nfs_res_t *); void nfs_null_free(nfs_res_t *); void nfs3_getattr_free(nfs_res_t *); void nfs3_setattr_free(nfs_res_t *); void nfs3_lookup_free(nfs_res_t *); void nfs3_access_free(nfs_res_t *); void nfs3_readlink_free(nfs_res_t *); void nfs3_write_free(nfs_res_t *); void nfs3_create_free(nfs_res_t *); void nfs3_mkdir_free(nfs_res_t *); void nfs3_symlink_free(nfs_res_t *); void nfs3_mknod_free(nfs_res_t *); void nfs3_remove_free(nfs_res_t *); void nfs3_rmdir_free(nfs_res_t *); void nfs3_rename_free(nfs_res_t *); void nfs3_link_free(nfs_res_t *); void nfs3_readdir_free(nfs_res_t *); void nfs3_readdirplus_free(nfs_res_t *); void nfs3_fsstat_free(nfs_res_t *); void nfs3_fsinfo_free(nfs_res_t *); void nfs3_pathconf_free(nfs_res_t *); void nfs3_commit_free(nfs_res_t *); void nfs3_read_free(nfs_res_t *); void nfs4_Compound_FreeOne(nfs_resop4 *); void nfs4_Compound_Free(nfs_res_t *); void nfs4_Compound_CopyResOne(nfs_resop4 *, nfs_resop4 *); void nfs4_Compound_CopyRes(nfs_res_t *, nfs_res_t *); void nfs4_op_access_Free(nfs_resop4 *); void nfs4_op_close_Free(nfs_resop4 *); void nfs4_op_commit_Free(nfs_resop4 *); void nfs4_op_create_Free(nfs_resop4 *); void nfs4_op_delegreturn_Free(nfs_resop4 *); void nfs4_op_delegpurge_Free(nfs_resop4 *); void nfs4_op_getattr_Free(nfs_resop4 *); void nfs4_op_getfh_Free(nfs_resop4 *); void nfs4_op_illegal_Free(nfs_resop4 *); void nfs4_op_notsupp_Free(nfs_resop4 *); void nfs4_op_link_Free(nfs_resop4 *); void nfs4_op_lock_Free(nfs_resop4 *); void nfs4_op_lockt_Free(nfs_resop4 *); void nfs4_op_locku_Free(nfs_resop4 *); void nfs4_op_lookup_Free(nfs_resop4 *); void nfs4_op_lookupp_Free(nfs_resop4 *); void nfs4_op_nverify_Free(nfs_resop4 *); void nfs4_op_open_Free(nfs_resop4 *); void nfs4_op_open_confirm_Free(nfs_resop4 *); void nfs4_op_open_downgrade_Free(nfs_resop4 *); void nfs4_op_openattr_Free(nfs_resop4 *); void nfs4_op_openattr_Free(nfs_resop4 *); void nfs4_op_putfh_Free(nfs_resop4 *); void nfs4_op_putpubfh_Free(nfs_resop4 *); void nfs4_op_putrootfh_Free(nfs_resop4 *); void nfs4_op_read_Free(nfs_resop4 *); void nfs4_op_readdir_Free(nfs_resop4 *); void nfs4_op_readlink_Free(nfs_resop4 *); void nfs4_op_release_lockowner_Free(nfs_resop4 *); void nfs4_op_rename_Free(nfs_resop4 *); void nfs4_op_remove_Free(nfs_resop4 *); void nfs4_op_renew_Free(nfs_resop4 *); void nfs4_op_restorefh_Free(nfs_resop4 *); void nfs4_op_savefh_Free(nfs_resop4 *); void nfs4_op_secinfo_Free(nfs_resop4 *); void nfs4_op_secinfo_no_name_Free(nfs_resop4 *); void nfs4_op_setattr_Free(nfs_resop4 *); void nfs4_op_setclientid_Free(nfs_resop4 *); void nfs4_op_setclientid_confirm_Free(nfs_resop4 *); void nfs4_op_verify_Free(nfs_resop4 *); void nfs4_op_write_Free(nfs_resop4 *); void nfs4_op_close_CopyRes(CLOSE4res *, CLOSE4res *); void nfs4_op_lock_CopyRes(LOCK4res *, LOCK4res *); void nfs4_op_locku_CopyRes(LOCKU4res *, LOCKU4res *); void nfs4_op_open_CopyRes(OPEN4res *, OPEN4res *); void nfs4_op_open_confirm_CopyRes(OPEN_CONFIRM4res *, OPEN_CONFIRM4res *); void nfs4_op_open_downgrade_CopyRes(OPEN_DOWNGRADE4res *, OPEN_DOWNGRADE4res *); void nfs4_op_exchange_id_Free(nfs_resop4 *); void nfs4_op_close_Free(nfs_resop4 *); void nfs4_op_create_session_Free(nfs_resop4 *); void nfs4_op_getdevicelist_Free(nfs_resop4 *); void nfs4_op_getdeviceinfo_Free(nfs_resop4 *); void nfs4_op_free_stateid_Free(nfs_resop4 *); void nfs4_op_destroy_session_Free(nfs_resop4 *); void nfs4_op_lock_Free(nfs_resop4 *); void nfs4_op_lockt_Free(nfs_resop4 *); void nfs4_op_locku_Free(nfs_resop4 *); void nfs4_op_read_Free(nfs_resop4 *); void nfs4_op_sequence_Free(nfs_resop4 *); void nfs4_op_set_ssv_Free(nfs_resop4 *); void nfs4_op_test_stateid_Free(nfs_resop4 *); void nfs4_op_write_Free(nfs_resop4 *); void nfs4_op_destroy_clientid_Free(nfs_resop4 *); void nfs4_op_reclaim_complete_Free(nfs_resop4 *); void compound_data_Free(compound_data_t *); /* Pseudo FS functions */ bool pseudo_mount_export(struct gsh_export *exp); void create_pseudofs(void); void pseudo_unmount_export(struct gsh_export *exp); #endif /* NFS_PROTO_FUNCTIONS_H */ nfs-ganesha-2.6.0/src/include/nfs_proto_tools.h000066400000000000000000000217241324272410200215300ustar00rootroot00000000000000/* * * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- * * @file nfs_proto_tools.c * @brief A set of functions used to managed NFS. * * nfs_proto_tools.c - A set of functions used to managed NFS. * * */ #ifndef _NFS_PROTO_TOOLS_H #define _NFS_PROTO_TOOLS_H #include #include #include #include #include /* for having FNDELAY */ #include "hashtable.h" #include "log.h" #include "nfs_file_handle.h" #include "sal_data.h" #include "fsal.h" /* Hard and soft limit for nfsv4 quotas */ #define NFS_V4_MAX_QUOTA_SOFT 4294967296LL /* 4 GB */ #define NFS_V4_MAX_QUOTA_HARD 17179869184LL /* 16 GB */ #define NFS_V4_MAX_QUOTA 34359738368LL /* 32 GB */ #define NFS4_ATTRVALS_BUFFLEN 1024 /* * Definition of an array for the characteristics of each GETATTR sub-operations */ #define FATTR4_ATTR_READ 0x00001 #define FATTR4_ATTR_WRITE 0x00010 #define FATTR4_ATTR_READ_WRITE 0x00011 typedef enum { FATTR_XDR_NOOP, FATTR_XDR_SUCCESS, FATTR_XDR_SUCCESS_EXP, FATTR_XDR_FAILED, FATTR_BADOWNER } fattr_xdr_result; struct xdr_attrs_args { struct attrlist *attrs; nfs_fh4 *hdl4; uint32_t rdattr_error; uint64_t mounted_on_fileid; /*< If this is the root directory of a filesystem, the fileid of the directory on which the filesystem is mounted. */ /* Static attributes */ object_file_type_t type; /*< Object file type */ fsal_fsid_t fsid; /*< Filesystem on which this object is stored */ uint64_t fileid; /*< Unique identifier for this object within the scope of the fsid, (e.g. inode number) */ int nfs_status; compound_data_t *data; bool statfscalled; fsal_dynamicfsinfo_t *dynamicinfo; }; typedef struct fattr4_dent { char *name; /* The name of the operation */ unsigned int supported; /* Is this action supported? */ unsigned int size_fattr4; /* The size of the dedicated attr subtype */ unsigned int access; /* The access type for this attributes */ attrmask_t attrmask; /* attr bit for decoding to attrs */ attrmask_t exp_attrmask; /* attr bit for decoding to attrs in case of exepction */ fattr_xdr_result(*encode) (XDR * xdr, struct xdr_attrs_args *args); fattr_xdr_result(*decode) (XDR * xdr, struct xdr_attrs_args *args); fattr_xdr_result(*compare) (XDR * xdr1, XDR * xdr2); } fattr4_dent_t; extern const struct fattr4_dent fattr4tab[]; #define WORD0_FATTR4_RDATTR_ERROR (1 << FATTR4_RDATTR_ERROR) #define WORD1_FATTR4_MOUNTED_ON_FILEID (1 << (FATTR4_MOUNTED_ON_FILEID - 32)) static inline int check_for_wrongsec_ok_attr(struct bitmap4 *attr_request) { if (attr_request->bitmap4_len < 1) return true; if ((attr_request->map[0] & ~WORD0_FATTR4_RDATTR_ERROR) != 0) return false; if (attr_request->bitmap4_len < 2) return true; if ((attr_request->map[1] & ~WORD1_FATTR4_MOUNTED_ON_FILEID) != 0) return false; if (attr_request->bitmap4_len < 3) return true; if (attr_request->map[2] != 0) return false; return true; } static inline int check_for_rdattr_error(struct bitmap4 *attr_request) { if (attr_request->bitmap4_len < 1) return false; if ((attr_request->map[0] & WORD0_FATTR4_RDATTR_ERROR) != 0) return true; return false; } /** * * Attribute bitmap decoders */ /* bitmap is up to 3 x uint32_t. * * Structure of the bitmap is as follows * * 0 1 2 * +-------+---------+----------+----------+ * | count | 31 .. 0 | 63 .. 32 | 95 .. 64 | * +-------+---------+----------+----------+ * * One bit is set for every possible attributes. The bits are packed together * in a uint32_T (XDR alignment reason probably) * As said in the RFC3530, the n-th bit is with the uint32_t #(n/32), * and its position with the uint32_t is n % 32 * * Example * 1st bit = FATTR4_TYPE = 1 * 2nd bit = FATTR4_LINK_SUPPORT = 5 * 3rd bit = FATTR4_SYMLINK_SUPPORT = 6 * * Juste one uint32_t is necessay: 2**1 + 2**5 + 2**6 = 2 + 32 + 64 = 98 * +---+----+ * | 1 | 98 | * +---+----+ * * Other Example * * 1st bit = FATTR4_LINK_SUPPORT = 5 * 2nd bit = FATTR4_SYMLINK_SUPPORT = 6 * 3rd bit = FATTR4_MODE = 33 * 4th bit = FATTR4_OWNER = 36 * * Two uint32_t will be necessary there: * #1 = 2**5 + 2**6 = 32 + 64 = 96 # #2 = 2**(33-32) + 2**(36-32) = 2**1 + 2**4 = 2 + 16 = 18 * +---+----+----+ * | 2 | 98 | 18 | * +---+----+----+ * */ static inline int next_attr_from_bitmap(struct bitmap4 *bits, int last_attr) { int offset, bit; for (offset = (last_attr + 1) / 32; offset >= 0 && offset < bits->bitmap4_len; offset++) { if ((bits->map[offset] & (-1 << ((last_attr + 1) % 32))) != 0) { for (bit = (last_attr + 1) % 32; bit < 32; bit++) { if (bits->map[offset] & (1 << bit)) return offset * 32 + bit; } } last_attr = -1; } return -1; } static inline bool attribute_is_set(struct bitmap4 *bits, int attr) { int offset = attr / 32; if (offset >= bits->bitmap4_len) return false; return (bits->map[offset] & (1 << (attr % 32))) != 0; } static inline bool set_attribute_in_bitmap(struct bitmap4 *bits, int attr) { int offset = attr / 32; if (offset >= 3) return false; /* over upper bound */ if (offset >= bits->bitmap4_len) bits->bitmap4_len = offset + 1; /* roll into the next word */ bits->map[offset] |= (1 << (attr % 32)); return true; } static inline bool clear_attribute_in_bitmap(struct bitmap4 *bits, int attr) { int offset = attr / 32; if (offset >= bits->bitmap4_len) return false; bits->map[offset] &= ~(1 << (attr % 32)); return true; } #ifdef _USE_NFS3 void nfs_SetWccData(const struct pre_op_attr *before_attr, struct fsal_obj_handle *entry, wcc_data * pwcc_data); void nfs_SetPostOpAttr(struct fsal_obj_handle *entry, post_op_attr *attr, struct attrlist *attrs); void nfs_SetPreOpAttr(struct fsal_obj_handle *entry, pre_op_attr *attr); #endif bool nfs_RetryableError(fsal_errors_t fsal_errors); int nfs3_Sattr_To_FSAL_attr(struct attrlist *pFSALattr, sattr3 *psattr); void nfs4_Fattr_Free(fattr4 *fattr); nfsstat4 nfs4_return_one_state(struct fsal_obj_handle *obj, layoutreturn_type4 return_type, enum fsal_layoutreturn_circumstance circumstance, state_t *layout_state, struct pnfs_segment spec_segment, size_t body_len, const void *body_val, bool *deleted); typedef enum { UTF8_SCAN_NONE = 0, /* do no validation other than size */ UTF8_SCAN_NOSLASH = 1, /* disallow '/' */ UTF8_SCAN_NODOT = 2, /* disallow '.' and '..' */ UTF8_SCAN_CKUTF8 = 4, /* validate utf8 */ UTF8_SCAN_PATH = 8, /* validate path */ UTF8_SCAN_NAME = 3, /* a name (no embedded /, "." or "..") */ UTF8_SCAN_ALL = 7, /* do the whole thing, name+valid utf8 */ UTF8_SCAN_SYMLINK = 12 /* a symlink, allow '/', ".", "..", utf8 */ } utf8_scantype_t; nfsstat4 nfs4_utf8string2dynamic(const utf8string *input, utf8_scantype_t scan, char **obj_name); int bitmap4_to_attrmask_t(bitmap4 *bitmap4, attrmask_t *mask); nfsstat4 file_To_Fattr(compound_data_t *data, attrmask_t mask, struct attrlist *attr, fattr4 *Fattr, struct bitmap4 *Bitmap); bool nfs4_Fattr_Check_Access(fattr4 *, int); bool nfs4_Fattr_Check_Access_Bitmap(struct bitmap4 *, int); bool nfs4_Fattr_Supported(fattr4 *); int nfs4_Fattr_cmp(fattr4 *, fattr4 *); bool nfs3_FSALattr_To_Fattr(struct fsal_obj_handle *obj, const struct attrlist *FSAL_attr, fattr3 *Fattr); bool is_sticky_bit_set(struct fsal_obj_handle *obj, const struct attrlist *attr); bool nfs3_Sattr_To_FSALattr(struct attrlist *, sattr3 *); int nfs4_Fattr_To_FSAL_attr(struct attrlist *, fattr4 *, compound_data_t *); int nfs4_Fattr_To_fsinfo(fsal_dynamicfsinfo_t *, fattr4 *); int nfs4_Fattr_Fill_Error(fattr4 *, nfsstat4); int nfs4_FSALattr_To_Fattr(struct xdr_attrs_args *, struct bitmap4 *, fattr4 *); void nfs4_bitmap4_Remove_Unsupported(struct bitmap4 *); enum nfs4_minor_vers { NFS4_MINOR_VERS_0, NFS4_MINOR_VERS_1, NFS4_MINOR_VERS_2 }; void nfs4_pathname4_alloc(pathname4 *, char *); void nfs4_pathname4_free(pathname4 *); #endif /* _NFS_PROTO_TOOLS_H */ nfs-ganesha-2.6.0/src/include/nfs_req_queue.h000066400000000000000000000051131324272410200211320ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) 2012, The Linux Box Corporation * Copyright (c) 2012-2017 Red Hat, Inc. and/or its affiliates. * Contributor : Matt Benjamin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @file nfs_req_queue.h * @author Matt Benjamin * @brief NFS request queue package * * This module defines an infrastructure for classification and * dispatch of incoming protocol requests using a forward queueing * model, with priority and isolation partitions. */ #ifndef NFS_REQ_QUEUE_H #define NFS_REQ_QUEUE_H #include "gsh_list.h" #include "gsh_wait_queue.h" struct req_q { pthread_spinlock_t sp; struct glist_head q; /* LIFO */ uint32_t size; uint32_t max; uint32_t waiters; }; struct req_q_pair { const char *s; GSH_CACHE_PAD(0); struct req_q producer; /* from decoder */ GSH_CACHE_PAD(1); struct req_q consumer; /* to executor */ GSH_CACHE_PAD(2); }; enum req_q_e { REQ_Q_LOW_LATENCY, /*< GETATTR, RENEW, etc */ N_REQ_QUEUES }; struct req_q_set { struct req_q_pair qset[N_REQ_QUEUES]; }; struct nfs_req_st { struct { uint32_t ctr; struct req_q_set nfs_request_q; uint64_t size; pthread_spinlock_t sp; struct glist_head wait_list; uint32_t waiters; } reqs; GSH_CACHE_PAD(1); }; static inline void nfs_rpc_q_init(struct req_q *q) { glist_init(&q->q); pthread_spin_init(&q->sp, PTHREAD_PROCESS_PRIVATE); q->size = 0; q->waiters = 0; } static inline void nfs_rpc_queue_awaken(void *arg) { struct nfs_req_st *st = arg; struct glist_head *g = NULL; struct glist_head *n = NULL; pthread_spin_lock(&st->reqs.sp); glist_for_each_safe(g, n, &st->reqs.wait_list) { wait_q_entry_t *wqe = glist_entry(g, wait_q_entry_t, waitq); pthread_cond_signal(&wqe->lwe.cv); pthread_cond_signal(&wqe->rwe.cv); } pthread_spin_unlock(&st->reqs.sp); } #endif /* NFS_REQ_QUEUE_H */ nfs-ganesha-2.6.0/src/include/nfs_rpc_callback.h000066400000000000000000000071201324272410200215370ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) 2012, The Linux Box Corporation * Copyright (c) 2012-2017 Red Hat, Inc. and/or its affiliates. * Contributor : Matt Benjamin * William Allen Simpson * * Some portions Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ #ifndef NFS_RPC_CALLBACK_H #define NFS_RPC_CALLBACK_H #include "config.h" #include "log.h" #include "nfs_core.h" /** * @file nfs_rpc_callback.h * @author Matt Benjamin * @author Lee Dobryden * @brief RPC callback dispatch package * * This module implements APIs for submission, and dispatch of NFSv4.0 * and NFSv4.1 format callbacks. */ /* Definition in sal_data.h */ struct state_refer; /* XXX move? */ typedef struct nfs4_cb_tag { int32_t ix; char *val; int32_t len; } nfs4_cb_tag_t; /* CB compound tags */ #define NFS4_CB_TAG_DEFAULT 0 void cb_compound_init_v4(nfs4_compound_t *cbt, uint32_t n_ops, uint32_t minorversion, uint32_t ident, char *tag, uint32_t tag_len); void cb_compound_add_op(nfs4_compound_t *cbt, nfs_cb_argop4 *src); void cb_compound_free(nfs4_compound_t *cbt); #define NFS_CB_FLAG_NONE 0x0000 #define NFS_RPC_FLAG_NONE 0x0000 enum nfs_cb_call_states { NFS_CB_CALL_DISPATCH, NFS_CB_CALL_FINISHED, NFS_CB_CALL_ABORTED, }; rpc_call_t *alloc_rpc_call(); void free_rpc_call(rpc_call_t *call); static inline nfs_cb_argop4 *alloc_cb_argop(uint32_t cnt) { return gsh_calloc(cnt, sizeof(nfs_cb_argop4)); } static inline nfs_cb_resop4 *alloc_cb_resop(uint32_t cnt) { return gsh_calloc(cnt, sizeof(nfs_cb_resop4)); } static inline void free_cb_argop(nfs_cb_argop4 *ptr) { gsh_free(ptr); } static inline void free_cb_resop(nfs_cb_resop4 *ptr) { gsh_free(ptr); } static inline bool get_cb_chan_down(struct nfs_client_id_t *clid) { return clid->cid_cb.v40.cb_chan_down; } static inline void set_cb_chan_down(struct nfs_client_id_t *clid, bool down) { clid->cid_cb.v40.cb_chan_down = down; } rpc_call_channel_t *nfs_rpc_get_chan(nfs_client_id_t *pclientid, uint32_t flags); void nfs_rpc_cb_pkginit(void); void nfs_rpc_cb_pkgshutdown(void); int nfs_rpc_create_chan_v40(nfs_client_id_t *pclientid, uint32_t flags); int nfs_rpc_create_chan_v41(nfs41_session_t *session, int num_sec_parms, callback_sec_parms4 *sec_parms); #define NFS_RPC_CALL_NONE 0x0000 enum clnt_stat nfs_rpc_call(rpc_call_t *call, uint32_t flags); int nfs_rpc_cb_single(nfs_client_id_t *clientid, nfs_cb_argop4 *op, struct state_refer *refer, void (*completion)(rpc_call_t *), void *completion_arg); void nfs41_release_single(rpc_call_t *call); enum clnt_stat nfs_test_cb_chan(nfs_client_id_t *); #endif /* !NFS_RPC_CALLBACK_H */ nfs-ganesha-2.6.0/src/include/nfs_rpc_callback_simulator.h000066400000000000000000000034331324272410200236410ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) 2012, The Linux Box Corporation * Contributor : Matt Benjamin * * Some portions Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ #ifndef _NFS_RPC_CALLBACK_SIMULATOR_H #define _NFS_RPC_CALLBACK_SIMULATOR_H #include "config.h" #include "log.h" /** * * \file nfs_rpc_callback_simulator.h * \author Matt Benjamin and Lee Dobryden * \brief RPC callback dispatch package * * \section DESCRIPTION * * This module implements APIs for submission, and dispatch of NFSv4.0 * and (soon) NFSv4.1 format callbacks. * * Planned strategy is to deal with all backchannels from a small number of * service threads, initially 1, using non-blocking socket operations. This * may change, as NFSv4.1 bi-directional support is integrated. * */ void nfs_rpc_cbsim_pkginit(void); void nfs_rpc_cbsim_pkgshutdown(void); #endif /* _NFS_RPC_CALLBACK_SIMULATOR_H */ nfs-ganesha-2.6.0/src/include/nfsv41.h000066400000000000000000007144661324272410200174340ustar00rootroot00000000000000/* * The content of this file is a mix of rpcgen-generated * and hand-edited program text. It is not automatically * generated by, e.g., build processes. * * This file is under version control. */ #ifndef _NFSV41_H_RPCGEN #define _NFSV41_H_RPCGEN #ifdef __cplusplus extern "C" { #endif #ifndef RPCSEC_GSS #define RPCSEC_GSS 6 #endif #ifndef _AUTH_SYS_DEFINE_FOR_NFSv41 #define _AUTH_SYS_DEFINE_FOR_NFSv41 #include "gsh_rpc.h" #include "nfs_fh.h" typedef struct authsys_parms authsys_parms; #endif /* _AUTH_SYS_DEFINE_FOR_NFSv41 */ #define NFS4_FHSIZE 128 #define NFS4_VERIFIER_SIZE 8 #define NFS4_OPAQUE_LIMIT 1024 #define NFS4_SESSIONID_SIZE 16 #define NFS4_INT64_MAX 0x7fffffffffffffffLL #define NFS4_UINT64_MAX 0xffffffffffffffffLL #define NFS4_INT32_MAX 0x7fffffff #define NFS4_UINT32_MAX 0xffffffff #define NFS4_MAXFILELEN 0xffffffffffffffff #define NFS4_MAXFILEOFF 0xfffffffffffffffe #define BITMAP4_MAPLEN 3 enum nfs_ftype4 { NF4REG = 1, NF4DIR = 2, NF4BLK = 3, NF4CHR = 4, NF4LNK = 5, NF4SOCK = 6, NF4FIFO = 7, NF4ATTRDIR = 8, NF4NAMEDATTR = 9, }; typedef enum nfs_ftype4 nfs_ftype4; enum nfsstat4 { NFS4_OK = 0, NFS4ERR_PERM = 1, NFS4ERR_NOENT = 2, NFS4ERR_IO = 5, NFS4ERR_NXIO = 6, NFS4ERR_ACCESS = 13, NFS4ERR_EXIST = 17, NFS4ERR_XDEV = 18, NFS4ERR_NOTDIR = 20, NFS4ERR_ISDIR = 21, NFS4ERR_INVAL = 22, NFS4ERR_FBIG = 27, NFS4ERR_NOSPC = 28, NFS4ERR_ROFS = 30, NFS4ERR_MLINK = 31, NFS4ERR_NAMETOOLONG = 63, NFS4ERR_NOTEMPTY = 66, NFS4ERR_DQUOT = 69, NFS4ERR_STALE = 70, NFS4ERR_BADHANDLE = 10001, NFS4ERR_BAD_COOKIE = 10003, NFS4ERR_NOTSUPP = 10004, NFS4ERR_TOOSMALL = 10005, NFS4ERR_SERVERFAULT = 10006, NFS4ERR_BADTYPE = 10007, NFS4ERR_DELAY = 10008, NFS4ERR_SAME = 10009, NFS4ERR_DENIED = 10010, NFS4ERR_EXPIRED = 10011, NFS4ERR_LOCKED = 10012, NFS4ERR_GRACE = 10013, NFS4ERR_FHEXPIRED = 10014, NFS4ERR_SHARE_DENIED = 10015, NFS4ERR_WRONGSEC = 10016, NFS4ERR_CLID_INUSE = 10017, NFS4ERR_RESOURCE = 10018, NFS4ERR_MOVED = 10019, NFS4ERR_NOFILEHANDLE = 10020, NFS4ERR_MINOR_VERS_MISMATCH = 10021, NFS4ERR_STALE_CLIENTID = 10022, NFS4ERR_STALE_STATEID = 10023, NFS4ERR_OLD_STATEID = 10024, NFS4ERR_BAD_STATEID = 10025, NFS4ERR_BAD_SEQID = 10026, NFS4ERR_NOT_SAME = 10027, NFS4ERR_LOCK_RANGE = 10028, NFS4ERR_SYMLINK = 10029, NFS4ERR_RESTOREFH = 10030, NFS4ERR_LEASE_MOVED = 10031, NFS4ERR_ATTRNOTSUPP = 10032, NFS4ERR_NO_GRACE = 10033, NFS4ERR_RECLAIM_BAD = 10034, NFS4ERR_RECLAIM_CONFLICT = 10035, NFS4ERR_BADXDR = 10036, NFS4ERR_LOCKS_HELD = 10037, NFS4ERR_OPENMODE = 10038, NFS4ERR_BADOWNER = 10039, NFS4ERR_BADCHAR = 10040, NFS4ERR_BADNAME = 10041, NFS4ERR_BAD_RANGE = 10042, NFS4ERR_LOCK_NOTSUPP = 10043, NFS4ERR_OP_ILLEGAL = 10044, NFS4ERR_DEADLOCK = 10045, NFS4ERR_FILE_OPEN = 10046, NFS4ERR_ADMIN_REVOKED = 10047, NFS4ERR_CB_PATH_DOWN = 10048, NFS4ERR_BADIOMODE = 10049, NFS4ERR_BADLAYOUT = 10050, NFS4ERR_BAD_SESSION_DIGEST = 10051, NFS4ERR_BADSESSION = 10052, NFS4ERR_BADSLOT = 10053, NFS4ERR_COMPLETE_ALREADY = 10054, NFS4ERR_CONN_NOT_BOUND_TO_SESSION = 10055, NFS4ERR_DELEG_ALREADY_WANTED = 10056, NFS4ERR_BACK_CHAN_BUSY = 10057, NFS4ERR_LAYOUTTRYLATER = 10058, NFS4ERR_LAYOUTUNAVAILABLE = 10059, NFS4ERR_NOMATCHING_LAYOUT = 10060, NFS4ERR_RECALLCONFLICT = 10061, NFS4ERR_UNKNOWN_LAYOUTTYPE = 10062, NFS4ERR_SEQ_MISORDERED = 10063, NFS4ERR_SEQUENCE_POS = 10064, NFS4ERR_REQ_TOO_BIG = 10065, NFS4ERR_REP_TOO_BIG = 10066, NFS4ERR_REP_TOO_BIG_TO_CACHE = 10067, NFS4ERR_RETRY_UNCACHED_REP = 10068, NFS4ERR_UNSAFE_COMPOUND = 10069, NFS4ERR_TOO_MANY_OPS = 10070, NFS4ERR_OP_NOT_IN_SESSION = 10071, NFS4ERR_HASH_ALG_UNSUPP = 10072, NFS4ERR_CLIENTID_BUSY = 10074, NFS4ERR_PNFS_IO_HOLE = 10075, NFS4ERR_SEQ_FALSE_RETRY = 10076, NFS4ERR_BAD_HIGH_SLOT = 10077, NFS4ERR_DEADSESSION = 10078, NFS4ERR_ENCR_ALG_UNSUPP = 10079, NFS4ERR_PNFS_NO_LAYOUT = 10080, NFS4ERR_NOT_ONLY_OP = 10081, NFS4ERR_WRONG_CRED = 10082, NFS4ERR_WRONG_TYPE = 10083, NFS4ERR_DIRDELEG_UNAVAIL = 10084, NFS4ERR_REJECT_DELEG = 10085, NFS4ERR_RETURNCONFLICT = 10086, NFS4ERR_DELEG_REVOKED = 10087, /* NFSv4.2 */ NFS4ERR_PARTNER_NOTSUPP = 10088, NFS4ERR_PARTNER_NO_AUTH = 10089, NFS4ERR_UNION_NOTSUPP = 10090, NFS4ERR_OFFLOAD_DENIED = 10091, NFS4ERR_WRONG_LFS = 10092, NFS4ERR_BADLABEL = 10093, NFS4ERR_OFFLOAD_NO_REQS = 10094, NFS4ERR_REPLAY = 11001, }; typedef enum nfsstat4 nfsstat4; typedef struct { u_int attrlist4_len; char *attrlist4_val; } attrlist4; struct bitmap4 { u_int bitmap4_len; uint32_t map[BITMAP4_MAPLEN]; }; typedef struct bitmap4 bitmap4; typedef uint64_t changeid4; typedef uint64_t clientid4; typedef uint32_t count4; typedef uint64_t length4; typedef uint32_t mode4; typedef uint64_t nfs_cookie4; typedef struct { u_int nfs_fh4_len; char *nfs_fh4_val; } nfs_fh4; typedef uint64_t offset4; typedef uint32_t qop4; typedef struct { u_int sec_oid4_len; char *sec_oid4_val; } sec_oid4; typedef uint32_t sequenceid4; typedef uint32_t seqid4; typedef char sessionid4[NFS4_SESSIONID_SIZE]; typedef uint32_t slotid4; typedef struct { u_int utf8string_len; char *utf8string_val; } utf8string; typedef utf8string utf8str_cis; typedef utf8string utf8str_cs; typedef utf8string utf8str_mixed; typedef utf8str_cs component4; typedef utf8str_cs linktext4; typedef struct { u_int pathname4_len; component4 *pathname4_val; } pathname4; typedef char verifier4[NFS4_VERIFIER_SIZE]; struct nfstime4 { int64_t seconds; uint32_t nseconds; }; typedef struct nfstime4 nfstime4; enum time_how4 { SET_TO_SERVER_TIME4 = 0, SET_TO_CLIENT_TIME4 = 1, }; typedef enum time_how4 time_how4; struct settime4 { time_how4 set_it; union { nfstime4 time; } settime4_u; }; typedef struct settime4 settime4; typedef uint32_t nfs_lease4; struct fsid4 { uint64_t major; uint64_t minor; }; typedef struct fsid4 fsid4; struct change_policy4 { uint64_t cp_major; uint64_t cp_minor; }; typedef struct change_policy4 change_policy4; struct fs_location4 { struct { u_int server_len; utf8str_cis *server_val; } server; pathname4 rootpath; }; typedef struct fs_location4 fs_location4; struct fs_locations4 { pathname4 fs_root; struct { u_int locations_len; fs_location4 *locations_val; } locations; }; typedef struct fs_locations4 fs_locations4; #define ACL4_SUPPORT_ALLOW_ACL 0x00000001 #define ACL4_SUPPORT_DENY_ACL 0x00000002 #define ACL4_SUPPORT_AUDIT_ACL 0x00000004 #define ACL4_SUPPORT_ALARM_ACL 0x00000008 typedef uint32_t acetype4; #define ACE4_ACCESS_ALLOWED_ACE_TYPE 0x00000000 #define ACE4_ACCESS_DENIED_ACE_TYPE 0x00000001 #define ACE4_SYSTEM_AUDIT_ACE_TYPE 0x00000002 #define ACE4_SYSTEM_ALARM_ACE_TYPE 0x00000003 typedef uint32_t aceflag4; #define ACE4_FILE_INHERIT_ACE 0x00000001 #define ACE4_DIRECTORY_INHERIT_ACE 0x00000002 #define ACE4_NO_PROPAGATE_INHERIT_ACE 0x00000004 #define ACE4_INHERIT_ONLY_ACE 0x00000008 #define ACE4_SUCCESSFUL_ACCESS_ACE_FLAG 0x00000010 #define ACE4_FAILED_ACCESS_ACE_FLAG 0x00000020 #define ACE4_IDENTIFIER_GROUP 0x00000040 #define ACE4_INHERITED_ACE 0x00000080 typedef uint32_t acemask4; #define ACE4_READ_DATA 0x00000001 #define ACE4_LIST_DIRECTORY 0x00000001 #define ACE4_WRITE_DATA 0x00000002 #define ACE4_ADD_FILE 0x00000002 #define ACE4_APPEND_DATA 0x00000004 #define ACE4_ADD_SUBDIRECTORY 0x00000004 #define ACE4_READ_NAMED_ATTRS 0x00000008 #define ACE4_WRITE_NAMED_ATTRS 0x00000010 #define ACE4_EXECUTE 0x00000020 #define ACE4_DELETE_CHILD 0x00000040 #define ACE4_READ_ATTRIBUTES 0x00000080 #define ACE4_WRITE_ATTRIBUTES 0x00000100 #define ACE4_WRITE_RETENTION 0x00000200 #define ACE4_WRITE_RETENTION_HOLD 0x00000400 #define ACE4_DELETE 0x00010000 #define ACE4_READ_ACL 0x00020000 #define ACE4_WRITE_ACL 0x00040000 #define ACE4_WRITE_OWNER 0x00080000 #define ACE4_SYNCHRONIZE 0x00100000 #define ACE4_GENERIC_READ 0x00120081 #define ACE4_GENERIC_WRITE 0x00160106 #define ACE4_GENERIC_EXECUTE 0x001200A0 #define ACE4_READ_XATTRS 0x00200000 #define ACE4_WRITE_XATTRS 0x00400000 struct nfsace4 { acetype4 type; aceflag4 flag; acemask4 access_mask; utf8str_mixed who; }; typedef struct nfsace4 nfsace4; typedef uint32_t aclflag4; #define ACL4_AUTO_INHERIT 0x00000001 #define ACL4_PROTECTED 0x00000002 #define ACL4_DEFAULTED 0x00000004 struct nfsacl41 { aclflag4 na41_flag; struct { u_int na41_aces_len; nfsace4 *na41_aces_val; } na41_aces; }; typedef struct nfsacl41 nfsacl41; #define MODE4_SUID 0x800 #define MODE4_SGID 0x400 #define MODE4_SVTX 0x200 #define MODE4_RUSR 0x100 #define MODE4_WUSR 0x080 #define MODE4_XUSR 0x040 #define MODE4_RGRP 0x020 #define MODE4_WGRP 0x010 #define MODE4_XGRP 0x008 #define MODE4_ROTH 0x004 #define MODE4_WOTH 0x002 #define MODE4_XOTH 0x001 struct mode_masked4 { mode4 mm_value_to_set; mode4 mm_mask_bits; }; typedef struct mode_masked4 mode_masked4; struct specdata4 { uint32_t specdata1; uint32_t specdata2; }; typedef struct specdata4 specdata4; #define FH4_PERSISTENT 0x00000000 #define FH4_NOEXPIRE_WITH_OPEN 0x00000001 #define FH4_VOLATILE_ANY 0x00000002 #define FH4_VOL_MIGRATION 0x00000004 #define FH4_VOL_RENAME 0x00000008 struct netaddr4 { char *r_netid; char *r_addr; }; typedef struct netaddr4 netaddr4; struct nfs_impl_id4 { utf8str_cis nii_domain; utf8str_cs nii_name; nfstime4 nii_date; }; typedef struct nfs_impl_id4 nfs_impl_id4; struct stateid4 { uint32_t seqid; char other[12]; }; typedef struct stateid4 stateid4; enum layouttype4 { LAYOUT4_NFSV4_1_FILES = 0x1, LAYOUT4_OSD2_OBJECTS = 0x2, LAYOUT4_BLOCK_VOLUME = 0x3, LAYOUT4_FLEX_FILES = 0x4, }; typedef enum layouttype4 layouttype4; struct layout_content4 { layouttype4 loc_type; struct { u_int loc_body_len; char *loc_body_val; } loc_body; }; typedef struct layout_content4 layout_content4; /* * LAYOUT4_OSD2_OBJECTS loc_body description * is in a separate .x file */ /* * LAYOUT4_BLOCK_VOLUME loc_body description * is in a separate .x file */ struct layouthint4 { layouttype4 loh_type; struct { u_int loh_body_len; char *loh_body_val; } loh_body; }; typedef struct layouthint4 layouthint4; enum layoutiomode4 { LAYOUTIOMODE4_READ = 1, LAYOUTIOMODE4_RW = 2, LAYOUTIOMODE4_ANY = 3, }; typedef enum layoutiomode4 layoutiomode4; struct layout4 { offset4 lo_offset; length4 lo_length; layoutiomode4 lo_iomode; layout_content4 lo_content; }; typedef struct layout4 layout4; #define NFS4_DEVICEID4_SIZE 16 typedef char deviceid4[NFS4_DEVICEID4_SIZE]; struct device_addr4 { layouttype4 da_layout_type; struct { u_int da_addr_body_len; char *da_addr_body_val; } da_addr_body; }; typedef struct device_addr4 device_addr4; struct layoutupdate4 { layouttype4 lou_type; struct { u_int lou_body_len; char *lou_body_val; } lou_body; }; typedef struct layoutupdate4 layoutupdate4; #define LAYOUT4_RET_REC_FILE 1 #define LAYOUT4_RET_REC_FSID 2 #define LAYOUT4_RET_REC_ALL 3 enum layoutreturn_type4 { LAYOUTRETURN4_FILE = LAYOUT4_RET_REC_FILE, LAYOUTRETURN4_FSID = LAYOUT4_RET_REC_FSID, LAYOUTRETURN4_ALL = LAYOUT4_RET_REC_ALL, }; typedef enum layoutreturn_type4 layoutreturn_type4; /* layouttype4 specific data */ struct layoutreturn_file4 { offset4 lrf_offset; length4 lrf_length; stateid4 lrf_stateid; struct { u_int lrf_body_len; char *lrf_body_val; } lrf_body; }; typedef struct layoutreturn_file4 layoutreturn_file4; struct layoutreturn4 { layoutreturn_type4 lr_returntype; union { layoutreturn_file4 lr_layout; } layoutreturn4_u; }; typedef struct layoutreturn4 layoutreturn4; enum fs4_status_type { STATUS4_FIXED = 1, STATUS4_UPDATED = 2, STATUS4_VERSIONED = 3, STATUS4_WRITABLE = 4, STATUS4_REFERRAL = 5, }; typedef enum fs4_status_type fs4_status_type; struct fs4_status { bool_t fss_absent; fs4_status_type fss_type; utf8str_cs fss_source; utf8str_cs fss_current; int32_t fss_age; nfstime4 fss_version; }; typedef struct fs4_status fs4_status; #define TH4_READ_SIZE 0 #define TH4_WRITE_SIZE 1 #define TH4_READ_IOSIZE 2 #define TH4_WRITE_IOSIZE 3 typedef length4 threshold4_read_size; typedef length4 threshold4_write_size; typedef length4 threshold4_read_iosize; typedef length4 threshold4_write_iosize; struct threshold_item4 { layouttype4 thi_layout_type; struct bitmap4 thi_hintset; struct { u_int thi_hintlist_len; char *thi_hintlist_val; } thi_hintlist; }; typedef struct threshold_item4 threshold_item4; struct mdsthreshold4 { struct { u_int mth_hints_len; threshold_item4 *mth_hints_val; } mth_hints; }; typedef struct mdsthreshold4 mdsthreshold4; #define RET4_DURATION_INFINITE 0xffffffffffffffff struct retention_get4 { uint64_t rg_duration; struct { u_int rg_begin_time_len; nfstime4 *rg_begin_time_val; } rg_begin_time; }; typedef struct retention_get4 retention_get4; struct retention_set4 { bool_t rs_enable; struct { u_int rs_duration_len; uint64_t *rs_duration_val; } rs_duration; }; typedef struct retention_set4 retention_set4; #define FSCHARSET_CAP4_CONTAINS_NON_UTF8 0x1 #define FSCHARSET_CAP4_ALLOWS_ONLY_UTF8 0x2 typedef uint32_t fs_charset_cap4; typedef struct bitmap4 fattr4_supported_attrs; typedef nfs_ftype4 fattr4_type; typedef uint32_t fattr4_fh_expire_type; typedef changeid4 fattr4_change; typedef uint64_t fattr4_size; typedef bool_t fattr4_link_support; typedef bool_t fattr4_symlink_support; typedef bool_t fattr4_named_attr; typedef fsid4 fattr4_fsid; typedef bool_t fattr4_unique_handles; typedef nfs_lease4 fattr4_lease_time; typedef nfsstat4 fattr4_rdattr_error; typedef struct { u_int fattr4_acl_len; nfsace4 *fattr4_acl_val; } fattr4_acl; typedef uint32_t fattr4_aclsupport; typedef bool_t fattr4_archive; typedef bool_t fattr4_cansettime; typedef bool_t fattr4_case_insensitive; typedef bool_t fattr4_case_preserving; typedef bool_t fattr4_chown_restricted; typedef uint64_t fattr4_fileid; typedef uint64_t fattr4_files_avail; typedef nfs_fh4 fattr4_filehandle; typedef uint64_t fattr4_files_free; typedef uint64_t fattr4_files_total; typedef fs_locations4 fattr4_fs_locations; typedef bool_t fattr4_hidden; typedef bool_t fattr4_homogeneous; typedef uint64_t fattr4_maxfilesize; typedef uint32_t fattr4_maxlink; typedef uint32_t fattr4_maxname; typedef uint64_t fattr4_maxread; typedef uint64_t fattr4_maxwrite; typedef utf8str_cs fattr4_mimetype; typedef mode4 fattr4_mode; typedef mode_masked4 fattr4_mode_set_masked; typedef uint64_t fattr4_mounted_on_fileid; typedef bool_t fattr4_no_trunc; typedef uint32_t fattr4_numlinks; typedef utf8str_mixed fattr4_owner; typedef utf8str_mixed fattr4_owner_group; typedef uint64_t fattr4_quota_avail_hard; typedef uint64_t fattr4_quota_avail_soft; typedef uint64_t fattr4_quota_used; typedef specdata4 fattr4_rawdev; typedef uint64_t fattr4_space_avail; typedef uint64_t fattr4_space_free; typedef uint64_t fattr4_space_total; typedef uint64_t fattr4_space_used; typedef bool_t fattr4_system; typedef nfstime4 fattr4_time_access; typedef settime4 fattr4_time_access_set; typedef nfstime4 fattr4_time_backup; typedef nfstime4 fattr4_time_create; typedef nfstime4 fattr4_time_delta; typedef nfstime4 fattr4_time_metadata; typedef nfstime4 fattr4_time_modify; typedef settime4 fattr4_time_modify_set; typedef struct bitmap4 fattr4_suppattr_exclcreat; typedef nfstime4 fattr4_dir_notif_delay; typedef nfstime4 fattr4_dirent_notif_delay; typedef struct { u_int fattr4_fs_layout_types_len; layouttype4 *fattr4_fs_layout_types_val; } fattr4_fs_layout_types; typedef fs4_status fattr4_fs_status; typedef fs_charset_cap4 fattr4_fs_charset_cap; typedef uint32_t fattr4_layout_alignment; typedef uint32_t fattr4_layout_blksize; typedef layouthint4 fattr4_layout_hint; typedef struct { u_int fattr4_layout_types_len; layouttype4 *fattr4_layout_types_val; } fattr4_layout_types; typedef mdsthreshold4 fattr4_mdsthreshold; typedef retention_get4 fattr4_retention_get; typedef retention_set4 fattr4_retention_set; typedef retention_get4 fattr4_retentevt_get; typedef retention_set4 fattr4_retentevt_set; typedef uint64_t fattr4_retention_hold; typedef nfsacl41 fattr4_dacl; typedef nfsacl41 fattr4_sacl; typedef change_policy4 fattr4_change_policy; /* * REQUIRED Attributes */ #define FATTR4_SUPPORTED_ATTRS 0 #define FATTR4_TYPE 1 #define FATTR4_FH_EXPIRE_TYPE 2 #define FATTR4_CHANGE 3 #define FATTR4_SIZE 4 #define FATTR4_LINK_SUPPORT 5 #define FATTR4_SYMLINK_SUPPORT 6 #define FATTR4_NAMED_ATTR 7 #define FATTR4_FSID 8 #define FATTR4_UNIQUE_HANDLES 9 #define FATTR4_LEASE_TIME 10 #define FATTR4_RDATTR_ERROR 11 #define FATTR4_FILEHANDLE 19 /* new to NFSV4.1 */ #define FATTR4_SUPPATTR_EXCLCREAT 75 /* * RECOMMENDED Attributes */ #define FATTR4_ACL 12 #define FATTR4_ACLSUPPORT 13 #define FATTR4_ARCHIVE 14 #define FATTR4_CANSETTIME 15 #define FATTR4_CASE_INSENSITIVE 16 #define FATTR4_CASE_PRESERVING 17 #define FATTR4_CHOWN_RESTRICTED 18 #define FATTR4_FILEID 20 #define FATTR4_FILES_AVAIL 21 #define FATTR4_FILES_FREE 22 #define FATTR4_FILES_TOTAL 23 #define FATTR4_FS_LOCATIONS 24 #define FATTR4_HIDDEN 25 #define FATTR4_HOMOGENEOUS 26 #define FATTR4_MAXFILESIZE 27 #define FATTR4_MAXLINK 28 #define FATTR4_MAXNAME 29 #define FATTR4_MAXREAD 30 #define FATTR4_MAXWRITE 31 #define FATTR4_MIMETYPE 32 #define FATTR4_MODE 33 #define FATTR4_NO_TRUNC 34 #define FATTR4_NUMLINKS 35 #define FATTR4_OWNER 36 #define FATTR4_OWNER_GROUP 37 #define FATTR4_QUOTA_AVAIL_HARD 38 #define FATTR4_QUOTA_AVAIL_SOFT 39 #define FATTR4_QUOTA_USED 40 #define FATTR4_RAWDEV 41 #define FATTR4_SPACE_AVAIL 42 #define FATTR4_SPACE_FREE 43 #define FATTR4_SPACE_TOTAL 44 #define FATTR4_SPACE_USED 45 #define FATTR4_SYSTEM 46 #define FATTR4_TIME_ACCESS 47 #define FATTR4_TIME_ACCESS_SET 48 #define FATTR4_TIME_BACKUP 49 #define FATTR4_TIME_CREATE 50 #define FATTR4_TIME_DELTA 51 #define FATTR4_TIME_METADATA 52 #define FATTR4_TIME_MODIFY 53 #define FATTR4_TIME_MODIFY_SET 54 #define FATTR4_MOUNTED_ON_FILEID 55 /* new to NFSV4.1 */ #define FATTR4_DIR_NOTIF_DELAY 56 #define FATTR4_DIRENT_NOTIF_DELAY 57 #define FATTR4_DACL 58 #define FATTR4_SACL 59 #define FATTR4_CHANGE_POLICY 60 #define FATTR4_FS_STATUS 61 #define FATTR4_FS_LAYOUT_TYPES 62 #define FATTR4_LAYOUT_HINT 63 #define FATTR4_LAYOUT_TYPES 64 #define FATTR4_LAYOUT_BLKSIZE 65 #define FATTR4_LAYOUT_ALIGNMENT 66 #define FATTR4_FS_LOCATIONS_INFO 67 #define FATTR4_MDSTHRESHOLD 68 #define FATTR4_RETENTION_GET 69 #define FATTR4_RETENTION_SET 70 #define FATTR4_RETENTEVT_GET 71 #define FATTR4_RETENTEVT_SET 72 #define FATTR4_RETENTION_HOLD 73 #define FATTR4_MODE_SET_MASKED 74 #define FATTR4_FS_CHARSET_CAP 76 /* NFSv4.2 */ #define FATTR4_CLONE_BLKSIZE 77 #define FATTR4_SPACE_FREED 78 #define FATTR4_CHANGE_ATTR_TYPE 79 #define FATTR4_SEC_LABEL 80 /* NFSv4.3 */ #define FATTR4_XATTR_SUPPORT 82 struct fattr4 { struct bitmap4 attrmask; attrlist4 attr_vals; }; typedef struct fattr4 fattr4; struct change_info4 { bool_t atomic; changeid4 before; changeid4 after; }; typedef struct change_info4 change_info4; typedef netaddr4 clientaddr4; struct cb_client4 { uint32_t cb_program; netaddr4 cb_location; }; typedef struct cb_client4 cb_client4; struct nfs_client_id4 { verifier4 verifier; struct { u_int id_len; char *id_val; } id; }; typedef struct nfs_client_id4 nfs_client_id4; struct client_owner4 { verifier4 co_verifier; struct { u_int co_ownerid_len; char *co_ownerid_val; } co_ownerid; }; typedef struct client_owner4 client_owner4; struct server_owner4 { uint64_t so_minor_id; struct { u_int so_major_id_len; char *so_major_id_val; } so_major_id; }; typedef struct server_owner4 server_owner4; struct state_owner4 { clientid4 clientid; struct { u_int owner_len; char *owner_val; } owner; }; typedef struct state_owner4 state_owner4; typedef state_owner4 open_owner4; typedef state_owner4 lock_owner4; enum nfs_lock_type4 { READ_LT = 1, WRITE_LT = 2, READW_LT = 3, WRITEW_LT = 4, }; typedef enum nfs_lock_type4 nfs_lock_type4; /* Input for computing subkeys */ enum ssv_subkey4 { SSV4_SUBKEY_MIC_I2T = 1, SSV4_SUBKEY_MIC_T2I = 2, SSV4_SUBKEY_SEAL_I2T = 3, SSV4_SUBKEY_SEAL_T2I = 4, }; typedef enum ssv_subkey4 ssv_subkey4; /* Input for computing smt_hmac */ struct ssv_mic_plain_tkn4 { uint32_t smpt_ssv_seq; struct { u_int smpt_orig_plain_len; char *smpt_orig_plain_val; } smpt_orig_plain; }; typedef struct ssv_mic_plain_tkn4 ssv_mic_plain_tkn4; /* SSV GSS PerMsgToken token */ struct ssv_mic_tkn4 { uint32_t smt_ssv_seq; struct { u_int smt_hmac_len; char *smt_hmac_val; } smt_hmac; }; typedef struct ssv_mic_tkn4 ssv_mic_tkn4; /* Input for computing ssct_encr_data and ssct_hmac */ struct ssv_seal_plain_tkn4 { struct { u_int sspt_confounder_len; char *sspt_confounder_val; } sspt_confounder; uint32_t sspt_ssv_seq; struct { u_int sspt_orig_plain_len; char *sspt_orig_plain_val; } sspt_orig_plain; struct { u_int sspt_pad_len; char *sspt_pad_val; } sspt_pad; }; typedef struct ssv_seal_plain_tkn4 ssv_seal_plain_tkn4; /* SSV GSS SealedMessage token */ struct ssv_seal_cipher_tkn4 { uint32_t ssct_ssv_seq; struct { u_int ssct_iv_len; char *ssct_iv_val; } ssct_iv; struct { u_int ssct_encr_data_len; char *ssct_encr_data_val; } ssct_encr_data; struct { u_int ssct_hmac_len; char *ssct_hmac_val; } ssct_hmac; }; typedef struct ssv_seal_cipher_tkn4 ssv_seal_cipher_tkn4; struct fs_locations_server4 { int32_t fls_currency; struct { u_int fls_info_len; char *fls_info_val; } fls_info; utf8str_cis fls_server; }; typedef struct fs_locations_server4 fs_locations_server4; #define FSLI4BX_GFLAGS 0 #define FSLI4BX_TFLAGS 1 #define FSLI4BX_CLSIMUL 2 #define FSLI4BX_CLHANDLE 3 #define FSLI4BX_CLFILEID 4 #define FSLI4BX_CLWRITEVER 5 #define FSLI4BX_CLCHANGE 6 #define FSLI4BX_CLREADDIR 7 #define FSLI4BX_READRANK 8 #define FSLI4BX_WRITERANK 9 #define FSLI4BX_READORDER 10 #define FSLI4BX_WRITEORDER 11 #define FSLI4GF_WRITABLE 0x01 #define FSLI4GF_CUR_REQ 0x02 #define FSLI4GF_ABSENT 0x04 #define FSLI4GF_GOING 0x08 #define FSLI4GF_SPLIT 0x10 #define FSLI4TF_RDMA 0x01 struct fs_locations_item4 { struct { u_int fli_entries_len; fs_locations_server4 *fli_entries_val; } fli_entries; pathname4 fli_rootpath; }; typedef struct fs_locations_item4 fs_locations_item4; struct fs_locations_info4 { uint32_t fli_flags; int32_t fli_valid_for; pathname4 fli_fs_root; struct { u_int fli_items_len; fs_locations_item4 *fli_items_val; } fli_items; }; typedef struct fs_locations_info4 fs_locations_info4; #define FSLI4IF_VAR_SUB 0x00000001 typedef fs_locations_info4 fattr4_fs_locations_info; #define NFL4_UFLG_MASK 0x0000003F #define NFL4_UFLG_DENSE 0x00000001 #define NFL4_UFLG_COMMIT_THRU_MDS 0x00000002 #define NFL4_UFLG_STRIPE_UNIT_SIZE_MASK 0xFFFFFFC0 typedef uint32_t nfl_util4; enum filelayout_hint_care4 { NFLH4_CARE_DENSE = NFL4_UFLG_DENSE, NFLH4_CARE_COMMIT_THRU_MDS = NFL4_UFLG_COMMIT_THRU_MDS, NFLH4_CARE_STRIPE_UNIT_SIZE = 0x00000040, NFLH4_CARE_STRIPE_COUNT = 0x00000080, }; typedef enum filelayout_hint_care4 filelayout_hint_care4; /* Encoded in the loh_body field of data type layouthint4: */ struct nfsv4_1_file_layouthint4 { uint32_t nflh_care; nfl_util4 nflh_util; count4 nflh_stripe_count; }; typedef struct nfsv4_1_file_layouthint4 nfsv4_1_file_layouthint4; typedef struct { u_int multipath_list4_len; netaddr4 *multipath_list4_val; } multipath_list4; /* * Encoded in the da_addr_body field of * data type device_addr4: */ struct nfsv4_1_file_layout_ds_addr4 { struct { u_int nflda_stripe_indices_len; uint32_t *nflda_stripe_indices_val; } nflda_stripe_indices; struct { u_int nflda_multipath_ds_list_len; multipath_list4 *nflda_multipath_ds_list_val; } nflda_multipath_ds_list; }; typedef struct nfsv4_1_file_layout_ds_addr4 nfsv4_1_file_layout_ds_addr4; /* * Encoded in the loc_body field of * data type layout_content4: */ struct nfsv4_1_file_layout4 { deviceid4 nfl_deviceid; nfl_util4 nfl_util; uint32_t nfl_first_stripe_index; offset4 nfl_pattern_offset; struct { u_int nfl_fh_list_len; nfs_fh4 *nfl_fh_list_val; } nfl_fh_list; }; typedef struct nfsv4_1_file_layout4 nfsv4_1_file_layout4; /* * Encoded in the lou_body field of data type layoutupdate4: * Nothing. lou_body is a zero length array of bytes. */ /* * Encoded in the lrf_body field of * data type layoutreturn_file4: * Nothing. lrf_body is a zero length array of bytes. */ #define ACCESS4_READ 0x00000001 #define ACCESS4_LOOKUP 0x00000002 #define ACCESS4_MODIFY 0x00000004 #define ACCESS4_EXTEND 0x00000008 #define ACCESS4_DELETE 0x00000010 #define ACCESS4_EXECUTE 0x00000020 #define ACCESS4_XAREAD 0x00000040 #define ACCESS4_XAWRITE 0x00000080 struct ACCESS4args { uint32_t access; }; typedef struct ACCESS4args ACCESS4args; struct ACCESS4resok { uint32_t supported; uint32_t access; }; typedef struct ACCESS4resok ACCESS4resok; struct ACCESS4res { nfsstat4 status; union { ACCESS4resok resok4; } ACCESS4res_u; }; typedef struct ACCESS4res ACCESS4res; struct CLOSE4args { seqid4 seqid; stateid4 open_stateid; }; typedef struct CLOSE4args CLOSE4args; struct CLOSE4res { nfsstat4 status; union { stateid4 open_stateid; } CLOSE4res_u; }; typedef struct CLOSE4res CLOSE4res; struct COMMIT4args { offset4 offset; count4 count; }; typedef struct COMMIT4args COMMIT4args; struct COMMIT4resok { verifier4 writeverf; }; typedef struct COMMIT4resok COMMIT4resok; struct COMMIT4res { nfsstat4 status; union { COMMIT4resok resok4; } COMMIT4res_u; }; typedef struct COMMIT4res COMMIT4res; struct createtype4 { nfs_ftype4 type; union { linktext4 linkdata; specdata4 devdata; } createtype4_u; }; typedef struct createtype4 createtype4; struct CREATE4args { createtype4 objtype; component4 objname; fattr4 createattrs; }; typedef struct CREATE4args CREATE4args; struct CREATE4resok { change_info4 cinfo; struct bitmap4 attrset; }; typedef struct CREATE4resok CREATE4resok; struct CREATE4res { nfsstat4 status; union { CREATE4resok resok4; } CREATE4res_u; }; typedef struct CREATE4res CREATE4res; struct DELEGPURGE4args { clientid4 clientid; }; typedef struct DELEGPURGE4args DELEGPURGE4args; struct DELEGPURGE4res { nfsstat4 status; }; typedef struct DELEGPURGE4res DELEGPURGE4res; struct DELEGRETURN4args { stateid4 deleg_stateid; }; typedef struct DELEGRETURN4args DELEGRETURN4args; struct DELEGRETURN4res { nfsstat4 status; }; typedef struct DELEGRETURN4res DELEGRETURN4res; struct GETATTR4args { struct bitmap4 attr_request; }; typedef struct GETATTR4args GETATTR4args; struct GETATTR4resok { fattr4 obj_attributes; }; typedef struct GETATTR4resok GETATTR4resok; struct GETATTR4res { nfsstat4 status; union { GETATTR4resok resok4; } GETATTR4res_u; }; typedef struct GETATTR4res GETATTR4res; struct GETFH4resok { nfs_fh4 object; }; typedef struct GETFH4resok GETFH4resok; struct GETFH4res { nfsstat4 status; union { GETFH4resok resok4; } GETFH4res_u; }; typedef struct GETFH4res GETFH4res; struct LINK4args { component4 newname; }; typedef struct LINK4args LINK4args; struct LINK4resok { change_info4 cinfo; }; typedef struct LINK4resok LINK4resok; struct LINK4res { nfsstat4 status; union { LINK4resok resok4; } LINK4res_u; }; typedef struct LINK4res LINK4res; struct open_to_lock_owner4 { seqid4 open_seqid; stateid4 open_stateid; seqid4 lock_seqid; lock_owner4 lock_owner; }; typedef struct open_to_lock_owner4 open_to_lock_owner4; struct exist_lock_owner4 { stateid4 lock_stateid; seqid4 lock_seqid; }; typedef struct exist_lock_owner4 exist_lock_owner4; struct locker4 { bool_t new_lock_owner; union { open_to_lock_owner4 open_owner; exist_lock_owner4 lock_owner; } locker4_u; }; typedef struct locker4 locker4; struct LOCK4args { nfs_lock_type4 locktype; bool_t reclaim; offset4 offset; length4 length; locker4 locker; }; typedef struct LOCK4args LOCK4args; struct LOCK4denied { offset4 offset; length4 length; nfs_lock_type4 locktype; lock_owner4 owner; }; typedef struct LOCK4denied LOCK4denied; struct LOCK4resok { stateid4 lock_stateid; }; typedef struct LOCK4resok LOCK4resok; struct LOCK4res { nfsstat4 status; union { LOCK4resok resok4; LOCK4denied denied; } LOCK4res_u; }; typedef struct LOCK4res LOCK4res; struct LOCKT4args { nfs_lock_type4 locktype; offset4 offset; length4 length; lock_owner4 owner; }; typedef struct LOCKT4args LOCKT4args; struct LOCKT4res { nfsstat4 status; union { LOCK4denied denied; } LOCKT4res_u; }; typedef struct LOCKT4res LOCKT4res; struct LOCKU4args { nfs_lock_type4 locktype; seqid4 seqid; stateid4 lock_stateid; offset4 offset; length4 length; }; typedef struct LOCKU4args LOCKU4args; struct LOCKU4res { nfsstat4 status; union { stateid4 lock_stateid; } LOCKU4res_u; }; typedef struct LOCKU4res LOCKU4res; struct LOOKUP4args { component4 objname; }; typedef struct LOOKUP4args LOOKUP4args; struct LOOKUP4res { nfsstat4 status; }; typedef struct LOOKUP4res LOOKUP4res; struct LOOKUPP4res { nfsstat4 status; }; typedef struct LOOKUPP4res LOOKUPP4res; struct NVERIFY4args { fattr4 obj_attributes; }; typedef struct NVERIFY4args NVERIFY4args; struct NVERIFY4res { nfsstat4 status; }; typedef struct NVERIFY4res NVERIFY4res; enum createmode4 { UNCHECKED4 = 0, GUARDED4 = 1, EXCLUSIVE4 = 2, EXCLUSIVE4_1 = 3, }; typedef enum createmode4 createmode4; struct creatverfattr { verifier4 cva_verf; fattr4 cva_attrs; }; typedef struct creatverfattr creatverfattr; struct createhow4 { createmode4 mode; union { fattr4 createattrs; verifier4 createverf; creatverfattr ch_createboth; } createhow4_u; }; typedef struct createhow4 createhow4; enum opentype4 { OPEN4_NOCREATE = 0, OPEN4_CREATE = 1, }; typedef enum opentype4 opentype4; struct openflag4 { opentype4 opentype; union { createhow4 how; } openflag4_u; }; typedef struct openflag4 openflag4; enum limit_by4 { NFS_LIMIT_SIZE = 1, NFS_LIMIT_BLOCKS = 2, }; typedef enum limit_by4 limit_by4; struct nfs_modified_limit4 { uint32_t num_blocks; uint32_t bytes_per_block; }; typedef struct nfs_modified_limit4 nfs_modified_limit4; struct nfs_space_limit4 { limit_by4 limitby; union { uint64_t filesize; nfs_modified_limit4 mod_blocks; } nfs_space_limit4_u; }; typedef struct nfs_space_limit4 nfs_space_limit4; #define OPEN4_SHARE_ACCESS_READ 0x00000001 #define OPEN4_SHARE_ACCESS_WRITE 0x00000002 #define OPEN4_SHARE_ACCESS_BOTH 0x00000003 #define OPEN4_SHARE_DENY_NONE 0x00000000 #define OPEN4_SHARE_DENY_READ 0x00000001 #define OPEN4_SHARE_DENY_WRITE 0x00000002 #define OPEN4_SHARE_DENY_BOTH 0x00000003 #define OPEN4_SHARE_ACCESS_WANT_DELEG_MASK 0xFF00 #define OPEN4_SHARE_ACCESS_WANT_NO_PREFERENCE 0x0000 #define OPEN4_SHARE_ACCESS_WANT_READ_DELEG 0x0100 #define OPEN4_SHARE_ACCESS_WANT_WRITE_DELEG 0x0200 #define OPEN4_SHARE_ACCESS_WANT_ANY_DELEG 0x0300 #define OPEN4_SHARE_ACCESS_WANT_NO_DELEG 0x0400 #define OPEN4_SHARE_ACCESS_WANT_CANCEL 0x0500 #define OPEN4_SHARE_ACCESS_WANT_SIGNAL_DELEG_WHEN_RESRC_AVAIL 0x10000 #define OPEN4_SHARE_ACCESS_WANT_PUSH_DELEG_WHEN_UNCONTENDED 0x20000 enum open_delegation_type4 { OPEN_DELEGATE_NONE = 0, OPEN_DELEGATE_READ = 1, OPEN_DELEGATE_WRITE = 2, OPEN_DELEGATE_NONE_EXT = 3, }; typedef enum open_delegation_type4 open_delegation_type4; enum open_claim_type4 { CLAIM_NULL = 0, CLAIM_PREVIOUS = 1, CLAIM_DELEGATE_CUR = 2, CLAIM_DELEGATE_PREV = 3, CLAIM_FH = 4, CLAIM_DELEG_CUR_FH = 5, CLAIM_DELEG_PREV_FH = 6, }; typedef enum open_claim_type4 open_claim_type4; struct open_claim_delegate_cur4 { stateid4 delegate_stateid; component4 file; }; typedef struct open_claim_delegate_cur4 open_claim_delegate_cur4; struct open_claim4 { open_claim_type4 claim; union { component4 file; open_delegation_type4 delegate_type; open_claim_delegate_cur4 delegate_cur_info; component4 file_delegate_prev; stateid4 oc_delegate_stateid; } open_claim4_u; }; typedef struct open_claim4 open_claim4; struct OPEN4args { seqid4 seqid; uint32_t share_access; uint32_t share_deny; open_owner4 owner; openflag4 openhow; open_claim4 claim; }; typedef struct OPEN4args OPEN4args; struct open_read_delegation4 { stateid4 stateid; bool_t recall; nfsace4 permissions; }; typedef struct open_read_delegation4 open_read_delegation4; struct open_write_delegation4 { stateid4 stateid; bool_t recall; nfs_space_limit4 space_limit; nfsace4 permissions; }; typedef struct open_write_delegation4 open_write_delegation4; enum why_no_delegation4 { WND4_NOT_WANTED = 0, WND4_CONTENTION = 1, WND4_RESOURCE = 2, WND4_NOT_SUPP_FTYPE = 3, WND4_WRITE_DELEG_NOT_SUPP_FTYPE = 4, WND4_NOT_SUPP_UPGRADE = 5, WND4_NOT_SUPP_DOWNGRADE = 6, WND4_CANCELLED = 7, WND4_IS_DIR = 8, }; typedef enum why_no_delegation4 why_no_delegation4; struct open_none_delegation4 { why_no_delegation4 ond_why; union { bool_t ond_server_will_push_deleg; bool_t ond_server_will_signal_avail; } open_none_delegation4_u; }; typedef struct open_none_delegation4 open_none_delegation4; struct open_delegation4 { open_delegation_type4 delegation_type; union { open_read_delegation4 read; open_write_delegation4 write; open_none_delegation4 od_whynone; } open_delegation4_u; }; typedef struct open_delegation4 open_delegation4; #define OPEN4_RESULT_CONFIRM 0x00000002 #define OPEN4_RESULT_LOCKTYPE_POSIX 0x00000004 #define OPEN4_RESULT_PRESERVE_UNLINKED 0x00000008 #define OPEN4_RESULT_MAY_NOTIFY_LOCK 0x00000020 struct OPEN4resok { stateid4 stateid; change_info4 cinfo; uint32_t rflags; struct bitmap4 attrset; open_delegation4 delegation; }; typedef struct OPEN4resok OPEN4resok; struct OPEN4res { nfsstat4 status; union { OPEN4resok resok4; } OPEN4res_u; }; typedef struct OPEN4res OPEN4res; struct OPENATTR4args { bool_t createdir; }; typedef struct OPENATTR4args OPENATTR4args; struct OPENATTR4res { nfsstat4 status; }; typedef struct OPENATTR4res OPENATTR4res; struct OPEN_CONFIRM4args { stateid4 open_stateid; seqid4 seqid; }; typedef struct OPEN_CONFIRM4args OPEN_CONFIRM4args; struct OPEN_CONFIRM4resok { stateid4 open_stateid; }; typedef struct OPEN_CONFIRM4resok OPEN_CONFIRM4resok; struct OPEN_CONFIRM4res { nfsstat4 status; union { OPEN_CONFIRM4resok resok4; } OPEN_CONFIRM4res_u; }; typedef struct OPEN_CONFIRM4res OPEN_CONFIRM4res; struct OPEN_DOWNGRADE4args { stateid4 open_stateid; seqid4 seqid; uint32_t share_access; uint32_t share_deny; }; typedef struct OPEN_DOWNGRADE4args OPEN_DOWNGRADE4args; struct OPEN_DOWNGRADE4resok { stateid4 open_stateid; }; typedef struct OPEN_DOWNGRADE4resok OPEN_DOWNGRADE4resok; struct OPEN_DOWNGRADE4res { nfsstat4 status; union { OPEN_DOWNGRADE4resok resok4; } OPEN_DOWNGRADE4res_u; }; typedef struct OPEN_DOWNGRADE4res OPEN_DOWNGRADE4res; struct PUTFH4args { nfs_fh4 object; }; typedef struct PUTFH4args PUTFH4args; struct PUTFH4res { nfsstat4 status; }; typedef struct PUTFH4res PUTFH4res; struct PUTPUBFH4res { nfsstat4 status; }; typedef struct PUTPUBFH4res PUTPUBFH4res; struct PUTROOTFH4res { nfsstat4 status; }; typedef struct PUTROOTFH4res PUTROOTFH4res; struct READ4args { stateid4 stateid; offset4 offset; count4 count; }; typedef struct READ4args READ4args; struct READ4resok { bool_t eof; struct { u_int data_len; char *data_val; } data; }; typedef struct READ4resok READ4resok; struct READ4res { nfsstat4 status; union { READ4resok resok4; } READ4res_u; }; typedef struct READ4res READ4res; struct READDIR4args { nfs_cookie4 cookie; verifier4 cookieverf; count4 dircount; count4 maxcount; struct bitmap4 attr_request; }; typedef struct READDIR4args READDIR4args; struct entry4 { nfs_cookie4 cookie; component4 name; fattr4 attrs; struct entry4 *nextentry; }; typedef struct entry4 entry4; struct dirlist4 { entry4 *entries; bool_t eof; }; typedef struct dirlist4 dirlist4; struct READDIR4resok { verifier4 cookieverf; dirlist4 reply; }; typedef struct READDIR4resok READDIR4resok; struct READDIR4res { nfsstat4 status; union { READDIR4resok resok4; } READDIR4res_u; }; typedef struct READDIR4res READDIR4res; struct READLINK4resok { linktext4 link; }; typedef struct READLINK4resok READLINK4resok; struct READLINK4res { nfsstat4 status; union { READLINK4resok resok4; } READLINK4res_u; }; typedef struct READLINK4res READLINK4res; struct REMOVE4args { component4 target; }; typedef struct REMOVE4args REMOVE4args; struct REMOVE4resok { change_info4 cinfo; }; typedef struct REMOVE4resok REMOVE4resok; struct REMOVE4res { nfsstat4 status; union { REMOVE4resok resok4; } REMOVE4res_u; }; typedef struct REMOVE4res REMOVE4res; struct RENAME4args { component4 oldname; component4 newname; }; typedef struct RENAME4args RENAME4args; struct RENAME4resok { change_info4 source_cinfo; change_info4 target_cinfo; }; typedef struct RENAME4resok RENAME4resok; struct RENAME4res { nfsstat4 status; union { RENAME4resok resok4; } RENAME4res_u; }; typedef struct RENAME4res RENAME4res; struct RENEW4args { clientid4 clientid; }; typedef struct RENEW4args RENEW4args; struct RENEW4res { nfsstat4 status; }; typedef struct RENEW4res RENEW4res; struct RESTOREFH4res { nfsstat4 status; }; typedef struct RESTOREFH4res RESTOREFH4res; struct SAVEFH4res { nfsstat4 status; }; typedef struct SAVEFH4res SAVEFH4res; struct SECINFO4args { component4 name; }; typedef struct SECINFO4args SECINFO4args; #ifdef _HAVE_GSSAPI struct rpcsec_gss_info { sec_oid4 oid; qop4 qop; rpc_gss_svc_t service; }; typedef struct rpcsec_gss_info rpcsec_gss_info; #endif /* _HAVE_GSSAPI */ struct secinfo4 { uint32_t flavor; union { #ifdef _HAVE_GSSAPI rpcsec_gss_info flavor_info; #endif /* _HAVE_GSSAPI */ uint32_t unused; } secinfo4_u; }; typedef struct secinfo4 secinfo4; typedef struct { u_int SECINFO4resok_len; secinfo4 *SECINFO4resok_val; } SECINFO4resok; struct SECINFO4res { nfsstat4 status; union { SECINFO4resok resok4; } SECINFO4res_u; }; typedef struct SECINFO4res SECINFO4res; struct SETATTR4args { stateid4 stateid; fattr4 obj_attributes; }; typedef struct SETATTR4args SETATTR4args; struct SETATTR4res { nfsstat4 status; struct bitmap4 attrsset; }; typedef struct SETATTR4res SETATTR4res; struct SETCLIENTID4args { nfs_client_id4 client; cb_client4 callback; uint32_t callback_ident; }; typedef struct SETCLIENTID4args SETCLIENTID4args; struct SETCLIENTID4resok { clientid4 clientid; verifier4 setclientid_confirm; }; typedef struct SETCLIENTID4resok SETCLIENTID4resok; struct SETCLIENTID4res { nfsstat4 status; union { SETCLIENTID4resok resok4; clientaddr4 client_using; } SETCLIENTID4res_u; }; typedef struct SETCLIENTID4res SETCLIENTID4res; struct SETCLIENTID_CONFIRM4args { clientid4 clientid; verifier4 setclientid_confirm; }; typedef struct SETCLIENTID_CONFIRM4args SETCLIENTID_CONFIRM4args; struct SETCLIENTID_CONFIRM4res { nfsstat4 status; }; typedef struct SETCLIENTID_CONFIRM4res SETCLIENTID_CONFIRM4res; struct VERIFY4args { fattr4 obj_attributes; }; typedef struct VERIFY4args VERIFY4args; struct VERIFY4res { nfsstat4 status; }; typedef struct VERIFY4res VERIFY4res; enum stable_how4 { UNSTABLE4 = 0, DATA_SYNC4 = 1, FILE_SYNC4 = 2, }; typedef enum stable_how4 stable_how4; struct WRITE4args { stateid4 stateid; offset4 offset; stable_how4 stable; struct { u_int data_len; char *data_val; } data; }; typedef struct WRITE4args WRITE4args; struct WRITE4resok { count4 count; stable_how4 committed; verifier4 writeverf; }; typedef struct WRITE4resok WRITE4resok; struct WRITE4res { nfsstat4 status; union { WRITE4resok resok4; } WRITE4res_u; }; typedef struct WRITE4res WRITE4res; struct RELEASE_LOCKOWNER4args { lock_owner4 lock_owner; }; typedef struct RELEASE_LOCKOWNER4args RELEASE_LOCKOWNER4args; struct RELEASE_LOCKOWNER4res { nfsstat4 status; }; typedef struct RELEASE_LOCKOWNER4res RELEASE_LOCKOWNER4res; struct ILLEGAL4res { nfsstat4 status; }; typedef struct ILLEGAL4res ILLEGAL4res; typedef struct { u_int gsshandle4_t_len; char *gsshandle4_t_val; } gsshandle4_t; struct gss_cb_handles4 { #ifdef _HAVE_GSSAPI rpc_gss_svc_t gcbp_service; #endif /* _HAVE_GSSAPI */ gsshandle4_t gcbp_handle_from_server; gsshandle4_t gcbp_handle_from_client; }; typedef struct gss_cb_handles4 gss_cb_handles4; struct callback_sec_parms4 { uint32_t cb_secflavor; union { struct authunix_parms cbsp_sys_cred; gss_cb_handles4 cbsp_gss_handles; } callback_sec_parms4_u; }; typedef struct callback_sec_parms4 callback_sec_parms4; struct BACKCHANNEL_CTL4args { uint32_t bca_cb_program; struct { u_int bca_sec_parms_len; callback_sec_parms4 *bca_sec_parms_val; } bca_sec_parms; }; typedef struct BACKCHANNEL_CTL4args BACKCHANNEL_CTL4args; struct BACKCHANNEL_CTL4res { nfsstat4 bcr_status; }; typedef struct BACKCHANNEL_CTL4res BACKCHANNEL_CTL4res; enum channel_dir_from_client4 { CDFC4_FORE = 0x1, CDFC4_BACK = 0x2, CDFC4_FORE_OR_BOTH = 0x3, CDFC4_BACK_OR_BOTH = 0x7, }; typedef enum channel_dir_from_client4 channel_dir_from_client4; struct BIND_CONN_TO_SESSION4args { sessionid4 bctsa_sessid; channel_dir_from_client4 bctsa_dir; bool_t bctsa_use_conn_in_rdma_mode; }; typedef struct BIND_CONN_TO_SESSION4args BIND_CONN_TO_SESSION4args; enum channel_dir_from_server4 { CDFS4_FORE = 0x1, CDFS4_BACK = 0x2, CDFS4_BOTH = 0x3, }; typedef enum channel_dir_from_server4 channel_dir_from_server4; struct BIND_CONN_TO_SESSION4resok { sessionid4 bctsr_sessid; channel_dir_from_server4 bctsr_dir; bool_t bctsr_use_conn_in_rdma_mode; }; typedef struct BIND_CONN_TO_SESSION4resok BIND_CONN_TO_SESSION4resok; struct BIND_CONN_TO_SESSION4res { nfsstat4 bctsr_status; union { BIND_CONN_TO_SESSION4resok bctsr_resok4; } BIND_CONN_TO_SESSION4res_u; }; typedef struct BIND_CONN_TO_SESSION4res BIND_CONN_TO_SESSION4res; #define EXCHGID4_FLAG_SUPP_MOVED_REFER 0x00000001 #define EXCHGID4_FLAG_SUPP_MOVED_MIGR 0x00000002 #define EXCHGID4_FLAG_BIND_PRINC_STATEID 0x00000100 #define EXCHGID4_FLAG_USE_NON_PNFS 0x00010000 #define EXCHGID4_FLAG_USE_PNFS_MDS 0x00020000 #define EXCHGID4_FLAG_USE_PNFS_DS 0x00040000 #define EXCHGID4_FLAG_MASK_PNFS 0x00070000 #define EXCHGID4_FLAG_UPD_CONFIRMED_REC_A 0x40000000 #define EXCHGID4_FLAG_CONFIRMED_R 0x80000000 struct state_protect_ops4 { struct bitmap4 spo_must_enforce; struct bitmap4 spo_must_allow; }; typedef struct state_protect_ops4 state_protect_ops4; struct ssv_sp_parms4 { state_protect_ops4 ssp_ops; struct { u_int ssp_hash_algs_len; sec_oid4 *ssp_hash_algs_val; } ssp_hash_algs; struct { u_int ssp_encr_algs_len; sec_oid4 *ssp_encr_algs_val; } ssp_encr_algs; uint32_t ssp_window; uint32_t ssp_num_gss_handles; }; typedef struct ssv_sp_parms4 ssv_sp_parms4; enum state_protect_how4 { SP4_NONE = 0, SP4_MACH_CRED = 1, SP4_SSV = 2, }; typedef enum state_protect_how4 state_protect_how4; struct state_protect4_a { state_protect_how4 spa_how; union { state_protect_ops4 spa_mach_ops; ssv_sp_parms4 spa_ssv_parms; } state_protect4_a_u; }; typedef struct state_protect4_a state_protect4_a; struct EXCHANGE_ID4args { client_owner4 eia_clientowner; uint32_t eia_flags; state_protect4_a eia_state_protect; struct { u_int eia_client_impl_id_len; nfs_impl_id4 *eia_client_impl_id_val; } eia_client_impl_id; }; typedef struct EXCHANGE_ID4args EXCHANGE_ID4args; struct ssv_prot_info4 { state_protect_ops4 spi_ops; uint32_t spi_hash_alg; uint32_t spi_encr_alg; uint32_t spi_ssv_len; uint32_t spi_window; struct { u_int spi_handles_len; gsshandle4_t *spi_handles_val; } spi_handles; }; typedef struct ssv_prot_info4 ssv_prot_info4; struct state_protect4_r { state_protect_how4 spr_how; union { state_protect_ops4 spr_mach_ops; ssv_prot_info4 spr_ssv_info; } state_protect4_r_u; }; typedef struct state_protect4_r state_protect4_r; struct EXCHANGE_ID4resok { clientid4 eir_clientid; sequenceid4 eir_sequenceid; uint32_t eir_flags; state_protect4_r eir_state_protect; server_owner4 eir_server_owner; struct { u_int eir_server_scope_len; char *eir_server_scope_val; } eir_server_scope; struct { u_int eir_server_impl_id_len; nfs_impl_id4 *eir_server_impl_id_val; } eir_server_impl_id; }; typedef struct EXCHANGE_ID4resok EXCHANGE_ID4resok; struct EXCHANGE_ID4res { nfsstat4 eir_status; union { EXCHANGE_ID4resok eir_resok4; } EXCHANGE_ID4res_u; }; typedef struct EXCHANGE_ID4res EXCHANGE_ID4res; struct channel_attrs4 { count4 ca_headerpadsize; count4 ca_maxrequestsize; count4 ca_maxresponsesize; count4 ca_maxresponsesize_cached; count4 ca_maxoperations; count4 ca_maxrequests; struct { u_int ca_rdma_ird_len; uint32_t *ca_rdma_ird_val; } ca_rdma_ird; }; typedef struct channel_attrs4 channel_attrs4; #define CREATE_SESSION4_FLAG_PERSIST 0x00000001 #define CREATE_SESSION4_FLAG_CONN_BACK_CHAN 0x00000002 #define CREATE_SESSION4_FLAG_CONN_RDMA 0x00000004 struct CREATE_SESSION4args { clientid4 csa_clientid; sequenceid4 csa_sequence; uint32_t csa_flags; channel_attrs4 csa_fore_chan_attrs; channel_attrs4 csa_back_chan_attrs; uint32_t csa_cb_program; struct { u_int csa_sec_parms_len; callback_sec_parms4 *csa_sec_parms_val; } csa_sec_parms; }; typedef struct CREATE_SESSION4args CREATE_SESSION4args; struct CREATE_SESSION4resok { sessionid4 csr_sessionid; sequenceid4 csr_sequence; uint32_t csr_flags; channel_attrs4 csr_fore_chan_attrs; channel_attrs4 csr_back_chan_attrs; }; typedef struct CREATE_SESSION4resok CREATE_SESSION4resok; struct CREATE_SESSION4res { nfsstat4 csr_status; union { CREATE_SESSION4resok csr_resok4; } CREATE_SESSION4res_u; }; typedef struct CREATE_SESSION4res CREATE_SESSION4res; struct DESTROY_SESSION4args { sessionid4 dsa_sessionid; }; typedef struct DESTROY_SESSION4args DESTROY_SESSION4args; struct DESTROY_SESSION4res { nfsstat4 dsr_status; }; typedef struct DESTROY_SESSION4res DESTROY_SESSION4res; struct FREE_STATEID4args { stateid4 fsa_stateid; }; typedef struct FREE_STATEID4args FREE_STATEID4args; struct FREE_STATEID4res { nfsstat4 fsr_status; }; typedef struct FREE_STATEID4res FREE_STATEID4res; typedef nfstime4 attr_notice4; struct GET_DIR_DELEGATION4args { bool_t gdda_signal_deleg_avail; struct bitmap4 gdda_notification_types; attr_notice4 gdda_child_attr_delay; attr_notice4 gdda_dir_attr_delay; struct bitmap4 gdda_child_attributes; struct bitmap4 gdda_dir_attributes; }; typedef struct GET_DIR_DELEGATION4args GET_DIR_DELEGATION4args; struct GET_DIR_DELEGATION4resok { verifier4 gddr_cookieverf; stateid4 gddr_stateid; struct bitmap4 gddr_notification; struct bitmap4 gddr_child_attributes; struct bitmap4 gddr_dir_attributes; }; typedef struct GET_DIR_DELEGATION4resok GET_DIR_DELEGATION4resok; enum gddrnf4_status { GDD4_OK = 0, GDD4_UNAVAIL = 1, }; typedef enum gddrnf4_status gddrnf4_status; struct GET_DIR_DELEGATION4res_non_fatal { gddrnf4_status gddrnf_status; union { GET_DIR_DELEGATION4resok gddrnf_resok4; bool_t gddrnf_will_signal_deleg_avail; } GET_DIR_DELEGATION4res_non_fatal_u; }; typedef struct GET_DIR_DELEGATION4res_non_fatal GET_DIR_DELEGATION4res_non_fatal; struct GET_DIR_DELEGATION4res { nfsstat4 gddr_status; union { GET_DIR_DELEGATION4res_non_fatal gddr_res_non_fatal4; } GET_DIR_DELEGATION4res_u; }; typedef struct GET_DIR_DELEGATION4res GET_DIR_DELEGATION4res; struct GETDEVICEINFO4args { deviceid4 gdia_device_id; layouttype4 gdia_layout_type; count4 gdia_maxcount; struct bitmap4 gdia_notify_types; }; typedef struct GETDEVICEINFO4args GETDEVICEINFO4args; struct GETDEVICEINFO4resok { device_addr4 gdir_device_addr; struct bitmap4 gdir_notification; }; typedef struct GETDEVICEINFO4resok GETDEVICEINFO4resok; struct GETDEVICEINFO4res { nfsstat4 gdir_status; union { GETDEVICEINFO4resok gdir_resok4; count4 gdir_mincount; } GETDEVICEINFO4res_u; }; typedef struct GETDEVICEINFO4res GETDEVICEINFO4res; struct GETDEVICELIST4args { layouttype4 gdla_layout_type; count4 gdla_maxdevices; nfs_cookie4 gdla_cookie; verifier4 gdla_cookieverf; }; typedef struct GETDEVICELIST4args GETDEVICELIST4args; struct GETDEVICELIST4resok { nfs_cookie4 gdlr_cookie; verifier4 gdlr_cookieverf; struct { u_int gdlr_deviceid_list_len; deviceid4 *gdlr_deviceid_list_val; } gdlr_deviceid_list; bool_t gdlr_eof; }; typedef struct GETDEVICELIST4resok GETDEVICELIST4resok; struct GETDEVICELIST4res { nfsstat4 gdlr_status; union { GETDEVICELIST4resok gdlr_resok4; } GETDEVICELIST4res_u; }; typedef struct GETDEVICELIST4res GETDEVICELIST4res; struct newtime4 { bool_t nt_timechanged; union { nfstime4 nt_time; } newtime4_u; }; typedef struct newtime4 newtime4; struct newoffset4 { bool_t no_newoffset; union { offset4 no_offset; } newoffset4_u; }; typedef struct newoffset4 newoffset4; struct LAYOUTCOMMIT4args { offset4 loca_offset; length4 loca_length; bool_t loca_reclaim; stateid4 loca_stateid; newoffset4 loca_last_write_offset; newtime4 loca_time_modify; layoutupdate4 loca_layoutupdate; }; typedef struct LAYOUTCOMMIT4args LAYOUTCOMMIT4args; struct newsize4 { bool_t ns_sizechanged; union { length4 ns_size; } newsize4_u; }; typedef struct newsize4 newsize4; struct LAYOUTCOMMIT4resok { newsize4 locr_newsize; }; typedef struct LAYOUTCOMMIT4resok LAYOUTCOMMIT4resok; struct LAYOUTCOMMIT4res { nfsstat4 locr_status; union { LAYOUTCOMMIT4resok locr_resok4; } LAYOUTCOMMIT4res_u; }; typedef struct LAYOUTCOMMIT4res LAYOUTCOMMIT4res; struct LAYOUTGET4args { bool_t loga_signal_layout_avail; layouttype4 loga_layout_type; layoutiomode4 loga_iomode; offset4 loga_offset; length4 loga_length; length4 loga_minlength; stateid4 loga_stateid; count4 loga_maxcount; }; typedef struct LAYOUTGET4args LAYOUTGET4args; struct LAYOUTGET4resok { bool_t logr_return_on_close; stateid4 logr_stateid; struct { u_int logr_layout_len; layout4 *logr_layout_val; } logr_layout; }; typedef struct LAYOUTGET4resok LAYOUTGET4resok; struct LAYOUTGET4res { nfsstat4 logr_status; union { LAYOUTGET4resok logr_resok4; bool_t logr_will_signal_layout_avail; } LAYOUTGET4res_u; }; typedef struct LAYOUTGET4res LAYOUTGET4res; struct LAYOUTRETURN4args { bool_t lora_reclaim; layouttype4 lora_layout_type; layoutiomode4 lora_iomode; layoutreturn4 lora_layoutreturn; }; typedef struct LAYOUTRETURN4args LAYOUTRETURN4args; struct layoutreturn_stateid { bool_t lrs_present; union { stateid4 lrs_stateid; } layoutreturn_stateid_u; }; typedef struct layoutreturn_stateid layoutreturn_stateid; struct LAYOUTRETURN4res { nfsstat4 lorr_status; union { layoutreturn_stateid lorr_stateid; } LAYOUTRETURN4res_u; }; typedef struct LAYOUTRETURN4res LAYOUTRETURN4res; enum secinfo_style4 { SECINFO_STYLE4_CURRENT_FH = 0, SECINFO_STYLE4_PARENT = 1, }; typedef enum secinfo_style4 secinfo_style4; typedef secinfo_style4 SECINFO_NO_NAME4args; typedef SECINFO4res SECINFO_NO_NAME4res; struct SEQUENCE4args { sessionid4 sa_sessionid; sequenceid4 sa_sequenceid; slotid4 sa_slotid; slotid4 sa_highest_slotid; bool_t sa_cachethis; }; typedef struct SEQUENCE4args SEQUENCE4args; #define SEQ4_STATUS_CB_PATH_DOWN 0x00000001 #define SEQ4_STATUS_CB_GSS_CONTEXTS_EXPIRING 0x00000002 #define SEQ4_STATUS_CB_GSS_CONTEXTS_EXPIRED 0x00000004 #define SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED 0x00000008 #define SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED 0x00000010 #define SEQ4_STATUS_ADMIN_STATE_REVOKED 0x00000020 #define SEQ4_STATUS_RECALLABLE_STATE_REVOKED 0x00000040 #define SEQ4_STATUS_LEASE_MOVED 0x00000080 #define SEQ4_STATUS_RESTART_RECLAIM_NEEDED 0x00000100 #define SEQ4_STATUS_CB_PATH_DOWN_SESSION 0x00000200 #define SEQ4_STATUS_BACKCHANNEL_FAULT 0x00000400 #define SEQ4_STATUS_DEVID_CHANGED 0x00000800 #define SEQ4_STATUS_DEVID_DELETED 0x00001000 struct SEQUENCE4resok { sessionid4 sr_sessionid; sequenceid4 sr_sequenceid; slotid4 sr_slotid; slotid4 sr_highest_slotid; slotid4 sr_target_highest_slotid; uint32_t sr_status_flags; }; typedef struct SEQUENCE4resok SEQUENCE4resok; struct SEQUENCE4res { nfsstat4 sr_status; union { SEQUENCE4resok sr_resok4; } SEQUENCE4res_u; }; typedef struct SEQUENCE4res SEQUENCE4res; struct ssa_digest_input4 { SEQUENCE4args sdi_seqargs; }; typedef struct ssa_digest_input4 ssa_digest_input4; struct SET_SSV4args { struct { u_int ssa_ssv_len; char *ssa_ssv_val; } ssa_ssv; struct { u_int ssa_digest_len; char *ssa_digest_val; } ssa_digest; }; typedef struct SET_SSV4args SET_SSV4args; struct ssr_digest_input4 { SEQUENCE4res sdi_seqres; }; typedef struct ssr_digest_input4 ssr_digest_input4; struct SET_SSV4resok { struct { u_int ssr_digest_len; char *ssr_digest_val; } ssr_digest; }; typedef struct SET_SSV4resok SET_SSV4resok; struct SET_SSV4res { nfsstat4 ssr_status; union { SET_SSV4resok ssr_resok4; } SET_SSV4res_u; }; typedef struct SET_SSV4res SET_SSV4res; struct TEST_STATEID4args { struct { u_int ts_stateids_len; stateid4 *ts_stateids_val; } ts_stateids; }; typedef struct TEST_STATEID4args TEST_STATEID4args; struct TEST_STATEID4resok { struct { u_int tsr_status_codes_len; nfsstat4 *tsr_status_codes_val; } tsr_status_codes; }; typedef struct TEST_STATEID4resok TEST_STATEID4resok; struct TEST_STATEID4res { nfsstat4 tsr_status; union { TEST_STATEID4resok tsr_resok4; } TEST_STATEID4res_u; }; typedef struct TEST_STATEID4res TEST_STATEID4res; struct deleg_claim4 { open_claim_type4 dc_claim; union { open_delegation_type4 dc_delegate_type; } deleg_claim4_u; }; typedef struct deleg_claim4 deleg_claim4; struct WANT_DELEGATION4args { uint32_t wda_want; deleg_claim4 wda_claim; }; typedef struct WANT_DELEGATION4args WANT_DELEGATION4args; struct WANT_DELEGATION4res { nfsstat4 wdr_status; union { open_delegation4 wdr_resok4; } WANT_DELEGATION4res_u; }; typedef struct WANT_DELEGATION4res WANT_DELEGATION4res; struct DESTROY_CLIENTID4args { clientid4 dca_clientid; }; typedef struct DESTROY_CLIENTID4args DESTROY_CLIENTID4args; struct DESTROY_CLIENTID4res { nfsstat4 dcr_status; }; typedef struct DESTROY_CLIENTID4res DESTROY_CLIENTID4res; struct RECLAIM_COMPLETE4args { bool_t rca_one_fs; }; typedef struct RECLAIM_COMPLETE4args RECLAIM_COMPLETE4args; struct RECLAIM_COMPLETE4res { nfsstat4 rcr_status; }; typedef struct RECLAIM_COMPLETE4res RECLAIM_COMPLETE4res; /* NFSv4.2 */ enum netloc_type4 { NL4_NAME = 0, NL4_URL = 1, NL4_NETADDR = 2 }; typedef enum netloc_type4 netloc_type4; enum data_content4 { NFS4_CONTENT_DATA = 0, NFS4_CONTENT_HOLE = 1, NFS4_CONTENT_ALLOCATE = 2, NFS4_CONTENT_DEALLOCATE = 4 }; typedef enum data_content4 data_content4; typedef struct { offset4 d_offset; struct { u_int data_len; char *data_val; } d_data; } data4; typedef struct { offset4 adb_offset; length4 adb_block_size; length4 adb_block_count; length4 adb_reloff_blocknum; count4 adb_block_num; length4 adb_reloff_pattern; struct { u_int data_len; char *data_val; } adb_data; } app_data_block4; typedef struct { offset4 di_offset; length4 di_length; } data_info4; typedef struct { count4 wr_ids; stateid4 wr_callback_id; length4 wr_count; stable_how4 wr_committed; verifier4 wr_writeverf; } write_response4; typedef struct { data_content4 what; union { data4 data; app_data_block4 adb; data_info4 hole; }; } contents; typedef struct { bool_t rpr_eof; count4 rpr_contents_count; contents rpr_contents; } read_plus_res4; typedef struct { bool_t sr_eof; offset4 sr_offset; } seek_res4; typedef struct OFFLOAD_STATUS4resok { length4 osr_bytes_copied; count4 osr_count_complete; nfsstat4 osr_complete; } OFFLOAD_STATUS4resok; struct COPY_NOTIFY4args { stateid4 cna_stateid; netloc_type4 cna_type; union { utf8str_cis cna_name; utf8str_cis cna_url; netaddr4 cna_addr; }; }; typedef struct COPY_NOTIFY4args COPY_NOTIFY4args; struct COPY_NOTIFY4res { nfstime4 cnr_time; netloc_type4 cna_type; union { utf8str_cis cnr_name; utf8str_cis cnr_url; netaddr4 cnr_addr; }; }; typedef struct COPY_NOTIFY4res COPY_NOTIFY4res; struct OFFLOAD_REVOKE4args { netloc_type4 ora_type; union { utf8str_cis ora_name; utf8str_cis ora_url; netaddr4 ora_addr; }; }; typedef struct OFFLOAD_REVOKE4args OFFLOAD_REVOKE4args; struct OFFLOAD_REVOKE4res { netloc_type4 orr_type; }; typedef struct OFFLOAD_REVOKE4res OFFLOAD_REVOKE4res; struct COPY4args { stateid4 ca_src_stateid; stateid4 ca_dst_stateid; offset4 ca_src_offset; offset4 ca_dst_offset; length4 ca_count; netloc_type4 ca_type; union { utf8str_cis ca_name; utf8str_cis ca_url; netaddr4 ca_addr; }; }; typedef struct COPY4args COPY4args; struct COPY4res { nfsstat4 cr_status; union { write_response4 cr_resok4; length4 cr_bytes_copied; } COPY4res_u; }; typedef struct COPY4res COPY4res; struct OFFLOAD_ABORT4args { stateid4 oaa_stateid; }; typedef struct OFFLOAD_ABORT4args OFFLOAD_ABORT4args; struct OFFLOAD_ABORT4res { stateid4 oar_stateid; }; typedef struct OFFLOAD_ABORT4res OFFLOAD_ABORT4res; struct OFFLOAD_STATUS4args { stateid4 osa_stateid; }; typedef struct OFFLOAD_STATUS4args OFFLOAD_STATUS4args; struct OFFLOAD_STATUS4res { nfsstat4 osr_status; union { OFFLOAD_STATUS4resok osr_resok4; } OFFLOAD_STATUS4res_u; }; typedef struct OFFLOAD_STATUS4res OFFLOAD_STATUS4res; struct WRITE_SAME4args { stateid4 wp_stateid; stable_how4 wp_stable; app_data_block4 wp_adb; }; typedef struct WRITE_SAME4args WRITE_SAME4args; struct WRITE_SAME4res { nfsstat4 wpr_status; union { write_response4 wpr_resok4; }; }; typedef struct WRITE_SAME4res WRITE_SAME4res; struct READ_PLUS4args { stateid4 rpa_stateid; offset4 rpa_offset; count4 rpa_count; }; typedef struct READ_PLUS4args READ_PLUS4args; struct READ_PLUS4res { nfsstat4 rpr_status; union { read_plus_res4 rpr_resok4; }; }; typedef struct READ_PLUS4res READ_PLUS4res; struct ALLOCATE4args { stateid4 aa_stateid; offset4 aa_offset; length4 aa_length; }; typedef struct ALLOCATE4args ALLOCATE4args; struct ALLOCATE4res { nfsstat4 ar_status; }; typedef struct ALLOCATE4res ALLOCATE4res; struct DEALLOCATE4args { stateid4 da_stateid; offset4 da_offset; length4 da_length; }; typedef struct DEALLOCATE4args DEALLOCATE4args; struct DEALLOCATE4res { nfsstat4 dr_status; }; typedef struct DEALLOCATE4res DEALLOCATE4res; struct SEEK4args { stateid4 sa_stateid; offset4 sa_offset; data_content4 sa_what; }; typedef struct SEEK4args SEEK4args; struct SEEK4res { nfsstat4 sr_status; union { seek_res4 sr_resok4; }; }; typedef struct SEEK4res SEEK4res; enum IO_ADVISE_type4 { IO_ADVISE4_NORMAL = 0, IO_ADVISE4_SEQUENTIAL = 1, IO_ADVISE4_SEQUENTIAL_BACKWARDS = 2, IO_ADVISE4_RANDOM = 3, IO_ADVISE4_WILLNEED = 4, IO_ADVISE4_WILLNEED_OPPORTUNISTIC = 5, IO_ADVISE4_DONTNEED = 6, IO_ADVISE4_NOREUSE = 7, IO_ADVISE4_READ = 8, IO_ADVISE4_WRITE = 9, IO_ADVISE4_INIT_PROXIMITY = 10 }; struct IO_ADVISE4args { stateid4 iaa_stateid; offset4 iaa_offset; length4 iaa_count; bitmap4 iaa_hints; }; typedef struct IO_ADVISE4args IO_ADVISE4args; struct IO_ADVISE4res { nfsstat4 iaa_status; union { bitmap4 iaa_hints; }; }; typedef struct IO_ADVISE4res IO_ADVISE4res; /* new operations for NFSv4.1 */ enum nfs_opnum4 { NFS4_OP_ACCESS = 3, NFS4_OP_CLOSE = 4, NFS4_OP_COMMIT = 5, NFS4_OP_CREATE = 6, NFS4_OP_DELEGPURGE = 7, NFS4_OP_DELEGRETURN = 8, NFS4_OP_GETATTR = 9, NFS4_OP_GETFH = 10, NFS4_OP_LINK = 11, NFS4_OP_LOCK = 12, NFS4_OP_LOCKT = 13, NFS4_OP_LOCKU = 14, NFS4_OP_LOOKUP = 15, NFS4_OP_LOOKUPP = 16, NFS4_OP_NVERIFY = 17, NFS4_OP_OPEN = 18, NFS4_OP_OPENATTR = 19, NFS4_OP_OPEN_CONFIRM = 20, NFS4_OP_OPEN_DOWNGRADE = 21, NFS4_OP_PUTFH = 22, NFS4_OP_PUTPUBFH = 23, NFS4_OP_PUTROOTFH = 24, NFS4_OP_READ = 25, NFS4_OP_READDIR = 26, NFS4_OP_READLINK = 27, NFS4_OP_REMOVE = 28, NFS4_OP_RENAME = 29, NFS4_OP_RENEW = 30, NFS4_OP_RESTOREFH = 31, NFS4_OP_SAVEFH = 32, NFS4_OP_SECINFO = 33, NFS4_OP_SETATTR = 34, NFS4_OP_SETCLIENTID = 35, NFS4_OP_SETCLIENTID_CONFIRM = 36, NFS4_OP_VERIFY = 37, NFS4_OP_WRITE = 38, NFS4_OP_RELEASE_LOCKOWNER = 39, /* NFSv4.1 */ NFS4_OP_BACKCHANNEL_CTL = 40, NFS4_OP_BIND_CONN_TO_SESSION = 41, NFS4_OP_EXCHANGE_ID = 42, NFS4_OP_CREATE_SESSION = 43, NFS4_OP_DESTROY_SESSION = 44, NFS4_OP_FREE_STATEID = 45, NFS4_OP_GET_DIR_DELEGATION = 46, NFS4_OP_GETDEVICEINFO = 47, NFS4_OP_GETDEVICELIST = 48, NFS4_OP_LAYOUTCOMMIT = 49, NFS4_OP_LAYOUTGET = 50, NFS4_OP_LAYOUTRETURN = 51, NFS4_OP_SECINFO_NO_NAME = 52, NFS4_OP_SEQUENCE = 53, NFS4_OP_SET_SSV = 54, NFS4_OP_TEST_STATEID = 55, NFS4_OP_WANT_DELEGATION = 56, NFS4_OP_DESTROY_CLIENTID = 57, NFS4_OP_RECLAIM_COMPLETE = 58, /* NFSv4.2 */ NFS4_OP_ALLOCATE = 59, NFS4_OP_COPY = 60, NFS4_OP_COPY_NOTIFY = 61, NFS4_OP_DEALLOCATE = 62, NFS4_OP_IO_ADVISE = 63, NFS4_OP_LAYOUTERROR = 64, NFS4_OP_LAYOUTSTATS = 65, NFS4_OP_OFFLOAD_CANCEL = 66, NFS4_OP_OFFLOAD_STATUS = 67, NFS4_OP_READ_PLUS = 68, NFS4_OP_SEEK = 69, NFS4_OP_WRITE_SAME = 70, NFS4_OP_CLONE = 71, /* NFSv4.3 */ NFS4_OP_GETXATTR = 72, NFS4_OP_SETXATTR = 73, NFS4_OP_LISTXATTR = 74, NFS4_OP_REMOVEXATTR = 75, NFS4_OP_LAST_ONE = 76, NFS4_OP_ILLEGAL = 10044, }; typedef enum nfs_opnum4 nfs_opnum4; typedef component4 xattrname4; typedef component4 xattrvalue4; enum setxattr_type4 { SETXATTR4_CREATE = 0, SETXATTR4_REPLACE = 1, }; typedef enum setxattr_type4 setxattr_type4; struct xattr4 { xattrname4 xa_name; xattrvalue4 xa_value; }; typedef struct xattr4 xattr4; struct xattrlist4 { count4 entryCount; component4 *entries; }; typedef struct xattrlist4 xattrlist4; struct GETXATTR4args { xattrname4 ga_name; }; typedef struct GETXATTR4args GETXATTR4args; struct GETXATTR4resok { xattrvalue4 gr_value; }; typedef struct GETXATTR4resok GETXATTR4resok; struct GETXATTR4res { nfsstat4 status; union { GETXATTR4resok resok4; } GETXATTR4res_u; }; typedef struct GETXATTR4res GETXATTR4res; struct SETXATTR4args { setxattr_type4 sa_type; xattr4 sa_xattr; }; typedef struct SETXATTR4args SETXATTR4args; struct SETXATTR4resok { change_info4 sr_info; }; typedef struct SETXATTR4resok SETXATTR4resok; struct SETXATTR4res { nfsstat4 status; union { SETXATTR4resok resok4; } SETXATTR4res_u; }; typedef struct SETXATTR4res SETXATTR4res; struct LISTXATTR4args { nfs_cookie4 la_cookie; verifier4 la_cookieverf; count4 la_maxcount; }; typedef struct LISTXATTR4args LISTXATTR4args; struct LISTXATTR4resok { nfs_cookie4 lr_cookie; verifier4 lr_cookieverf; bool_t lr_eof; xattrlist4 lr_names; }; typedef struct LISTXATTR4resok LISTXATTR4resok; struct LISTXATTR4res { nfsstat4 status; union { LISTXATTR4resok resok4; } LISTXATTR4res_u; }; typedef struct LISTXATTR4res LISTXATTR4res; struct REMOVEXATTR4args { xattrname4 ra_name; }; typedef struct REMOVEXATTR4args REMOVEXATTR4args; struct REMOVEXATTR4resok { change_info4 rr_info; }; typedef struct REMOVEXATTR4resok REMOVEXATTR4resok; struct REMOVEXATTR4res { nfsstat4 status; union { REMOVEXATTR4resok resok4; } REMOVEXATTR4res_u; }; typedef struct REMOVEXATTR4res REMOVEXATTR4res; typedef struct { deviceid4 de_deviceid; nfsstat4 de_status; nfs_opnum4 de_opnum; } device_error4; struct LAYOUTERROR4args { offset4 lea_offset; length4 lea_length; stateid4 lea_stateid; device_error4 lea_errors; }; typedef struct LAYOUTERROR4args LAYOUTERROR4args; struct LAYOUTERROR4res { nfsstat4 ler_status; }; typedef struct LAYOUTERROR4res LAYOUTERROR4res; struct io_info4 { uint32_t ii_count; uint64_t ii_bytes; }; typedef struct io_info4 io_info4; struct LAYOUTSTATS4args { offset4 lsa_offset; length4 lsa_length; stateid4 lsa_stateid; io_info4 lsa_read; io_info4 lsa_write; layoutupdate4 lsa_layoutupdate; }; typedef struct LAYOUTSTATS4args LAYOUTSTATS4args; struct LAYOUTSTATS4res { nfsstat4 lsr_status; }; typedef struct LAYOUTSTATS4res LAYOUTSTATS4res; struct ff_device_versions4 { uint32_t ffdv_version; uint32_t ffdv_minorversion; uint32_t ffdv_rsize; uint32_t ffdv_wsize; bool_t ffdv_tightly_coupled; }; typedef struct ff_device_versions4 ff_device_versions4; struct ff_device_addr4 { multipath_list4 ffda_netaddrs; struct { u_int ffda_versions_len; ff_device_versions4 *ffda_versions_val; } ffda_versions; }; typedef struct ff_device_addr4 ff_device_addr4; struct ff_data_server4 { deviceid4 ffds_deviceid; uint32_t ffds_efficiency; stateid4 ffds_stateid; struct { u_int ffds_fh_vers_len; nfs_fh4 *ffds_fh_vers_val; } ffds_fh_vers; fattr4_owner ffds_user; fattr4_owner_group ffds_group; }; typedef struct ff_data_server4 ff_data_server4; struct ff_mirror4 { struct { u_int ffm_data_servers_len; ff_data_server4 *ffm_data_servers_val; } ffm_data_servers; }; typedef struct ff_mirror4 ff_mirror4; struct ff_layout4 { length4 ffl_stripe_unit; struct { u_int ffl_mirrors_len; ff_mirror4 *ffl_mirrors_val; } ffl_mirrors; }; typedef struct ff_layout4 ff_layout4; struct ff_ioerr4 { offset4 ffie_offset; length4 ffie_length; stateid4 ffie_stateid; struct { u_int ffie_errors_len; device_error4 *ffie_errors_val; } ffie_errors; }; typedef struct ff_ioerr4 ff_ioerr4; struct ff_io_latency4 { nfstime4 ffil_min; nfstime4 ffil_max; nfstime4 ffil_avg; uint32_t ffil_count; }; typedef struct ff_io_latency4 ff_io_latency4; struct ff_layoutupdate4 { netaddr4 ffl_addr; nfs_fh4 ffl_fhandle; ff_io_latency4 ffl_read; ff_io_latency4 ffl_write; nfstime4 ffl_duration; bool_t ffl_local; }; typedef struct ff_layoutupdate4 ff_layoutupdate4; struct ff_iostats4 { offset4 ffis_offset; length4 ffis_length; stateid4 ffis_stateid; io_info4 ffis_read; io_info4 ffis_write; deviceid4 ffis_deviceid; ff_layoutupdate4 ffis_layoutupdate; }; typedef struct ff_iostats4 ff_iostats4; struct ff_layoutreturn4 { struct { u_int fflr_ioerr_report_len; ff_ioerr4 *fflr_ioerr_report_val; } fflr_ioerr_report; struct { u_int fflr_iostats_report_len; ff_iostats4 *fflr_iostats_report_val; } fflr_iostats_report; }; typedef struct ff_layoutreturn4 ff_layoutreturn4; struct ff_mirrors_hint { bool_t ffmc_valid; union { uint32_t ffmc_mirrors; } ff_mirrors_hint_u; }; typedef struct ff_mirrors_hint ff_mirrors_hint; struct ff_layouthint4 { ff_mirrors_hint fflh_mirrors_hint; }; typedef struct ff_layouthint4 ff_layouthint4; enum ff_cb_recall_any_mask { FF_RCA4_TYPE_MASK_READ = -2, FF_RCA4_TYPE_MASK_RW = -1, }; typedef enum ff_cb_recall_any_mask ff_cb_recall_any_mask; struct nfs_argop4 { nfs_opnum4 argop; union { ACCESS4args opaccess; CLOSE4args opclose; COMMIT4args opcommit; CREATE4args opcreate; DELEGPURGE4args opdelegpurge; DELEGRETURN4args opdelegreturn; GETATTR4args opgetattr; LINK4args oplink; LOCK4args oplock; LOCKT4args oplockt; LOCKU4args oplocku; LOOKUP4args oplookup; NVERIFY4args opnverify; OPEN4args opopen; OPENATTR4args opopenattr; OPEN_CONFIRM4args opopen_confirm; OPEN_DOWNGRADE4args opopen_downgrade; PUTFH4args opputfh; READ4args opread; READDIR4args opreaddir; REMOVE4args opremove; RENAME4args oprename; RENEW4args oprenew; SECINFO4args opsecinfo; SETATTR4args opsetattr; SETCLIENTID4args opsetclientid; SETCLIENTID_CONFIRM4args opsetclientid_confirm; VERIFY4args opverify; WRITE4args opwrite; RELEASE_LOCKOWNER4args oprelease_lockowner; BACKCHANNEL_CTL4args opbackchannel_ctl; BIND_CONN_TO_SESSION4args opbind_conn_to_session; EXCHANGE_ID4args opexchange_id; CREATE_SESSION4args opcreate_session; DESTROY_SESSION4args opdestroy_session; FREE_STATEID4args opfree_stateid; GET_DIR_DELEGATION4args opget_dir_delegation; GETDEVICEINFO4args opgetdeviceinfo; GETDEVICELIST4args opgetdevicelist; LAYOUTCOMMIT4args oplayoutcommit; LAYOUTGET4args oplayoutget; LAYOUTRETURN4args oplayoutreturn; SECINFO_NO_NAME4args opsecinfo_no_name; SEQUENCE4args opsequence; SET_SSV4args opset_ssv; TEST_STATEID4args optest_stateid; WANT_DELEGATION4args opwant_delegation; DESTROY_CLIENTID4args opdestroy_clientid; RECLAIM_COMPLETE4args opreclaim_complete; /* NFSv4.2 */ COPY_NOTIFY4args opoffload_notify; OFFLOAD_REVOKE4args opcopy_revoke; COPY4args opcopy; OFFLOAD_ABORT4args opoffload_abort; OFFLOAD_STATUS4args opoffload_status; WRITE_SAME4args opwrite_plus; ALLOCATE4args opallocate; DEALLOCATE4args opdeallocate; READ_PLUS4args opread_plus; SEEK4args opseek; IO_ADVISE4args opio_advise; LAYOUTERROR4args oplayouterror; LAYOUTSTATS4args oplayoutstats; /* NFSv4.3 */ GETXATTR4args opgetxattr; SETXATTR4args opsetxattr; LISTXATTR4args oplistxattr; REMOVEXATTR4args opremovexattr; } nfs_argop4_u; }; typedef struct nfs_argop4 nfs_argop4; struct nfs_resop4 { nfs_opnum4 resop; union { ACCESS4res opaccess; CLOSE4res opclose; COMMIT4res opcommit; CREATE4res opcreate; DELEGPURGE4res opdelegpurge; DELEGRETURN4res opdelegreturn; GETATTR4res opgetattr; GETFH4res opgetfh; LINK4res oplink; LOCK4res oplock; LOCKT4res oplockt; LOCKU4res oplocku; LOOKUP4res oplookup; LOOKUPP4res oplookupp; NVERIFY4res opnverify; OPEN4res opopen; OPENATTR4res opopenattr; OPEN_CONFIRM4res opopen_confirm; OPEN_DOWNGRADE4res opopen_downgrade; PUTFH4res opputfh; PUTPUBFH4res opputpubfh; PUTROOTFH4res opputrootfh; READ4res opread; READDIR4res opreaddir; READLINK4res opreadlink; REMOVE4res opremove; RENAME4res oprename; RENEW4res oprenew; RESTOREFH4res oprestorefh; SAVEFH4res opsavefh; SECINFO4res opsecinfo; SETATTR4res opsetattr; SETCLIENTID4res opsetclientid; SETCLIENTID_CONFIRM4res opsetclientid_confirm; VERIFY4res opverify; WRITE4res opwrite; RELEASE_LOCKOWNER4res oprelease_lockowner; BACKCHANNEL_CTL4res opbackchannel_ctl; BIND_CONN_TO_SESSION4res opbind_conn_to_session; EXCHANGE_ID4res opexchange_id; CREATE_SESSION4res opcreate_session; DESTROY_SESSION4res opdestroy_session; FREE_STATEID4res opfree_stateid; GET_DIR_DELEGATION4res opget_dir_delegation; GETDEVICEINFO4res opgetdeviceinfo; GETDEVICELIST4res opgetdevicelist; LAYOUTCOMMIT4res oplayoutcommit; LAYOUTGET4res oplayoutget; LAYOUTRETURN4res oplayoutreturn; SECINFO_NO_NAME4res opsecinfo_no_name; SEQUENCE4res opsequence; SET_SSV4res opset_ssv; TEST_STATEID4res optest_stateid; WANT_DELEGATION4res opwant_delegation; DESTROY_CLIENTID4res opdestroy_clientid; RECLAIM_COMPLETE4res opreclaim_complete; /* NFSv4.2 */ COPY_NOTIFY4res opoffload_notify; OFFLOAD_REVOKE4res opcopy_revoke; COPY4res opcopy; OFFLOAD_ABORT4res opoffload_abort; OFFLOAD_STATUS4res opoffload_status; WRITE_SAME4res opwrite_plus; ALLOCATE4res opallocate; DEALLOCATE4res opdeallocate; READ_PLUS4res opread_plus; SEEK4res opseek; IO_ADVISE4res opio_advise; LAYOUTERROR4res oplayouterror; LAYOUTSTATS4res oplayoutstats; /* NFSv4.3 */ GETXATTR4res opgetxattr; SETXATTR4res opsetxattr; LISTXATTR4res oplistxattr; REMOVEXATTR4res opremovexattr; ILLEGAL4res opillegal; } nfs_resop4_u; }; typedef struct nfs_resop4 nfs_resop4; struct COMPOUND4args { utf8str_cs tag; uint32_t minorversion; struct { u_int argarray_len; nfs_argop4 *argarray_val; } argarray; }; typedef struct COMPOUND4args COMPOUND4args; struct COMPOUND4res { nfsstat4 status; utf8str_cs tag; struct { u_int resarray_len; nfs_resop4 *resarray_val; } resarray; }; typedef struct COMPOUND4res COMPOUND4res; struct CB_GETATTR4args { nfs_fh4 fh; struct bitmap4 attr_request; }; typedef struct CB_GETATTR4args CB_GETATTR4args; struct CB_GETATTR4resok { fattr4 obj_attributes; }; typedef struct CB_GETATTR4resok CB_GETATTR4resok; struct CB_GETATTR4res { nfsstat4 status; union { CB_GETATTR4resok resok4; } CB_GETATTR4res_u; }; typedef struct CB_GETATTR4res CB_GETATTR4res; struct CB_RECALL4args { stateid4 stateid; bool_t truncate; nfs_fh4 fh; }; typedef struct CB_RECALL4args CB_RECALL4args; struct CB_RECALL4res { nfsstat4 status; }; typedef struct CB_RECALL4res CB_RECALL4res; struct CB_ILLEGAL4res { nfsstat4 status; }; typedef struct CB_ILLEGAL4res CB_ILLEGAL4res; enum layoutrecall_type4 { LAYOUTRECALL4_FILE = LAYOUT4_RET_REC_FILE, LAYOUTRECALL4_FSID = LAYOUT4_RET_REC_FSID, LAYOUTRECALL4_ALL = LAYOUT4_RET_REC_ALL, }; typedef enum layoutrecall_type4 layoutrecall_type4; struct layoutrecall_file4 { nfs_fh4 lor_fh; offset4 lor_offset; length4 lor_length; stateid4 lor_stateid; }; typedef struct layoutrecall_file4 layoutrecall_file4; struct layoutrecall4 { layoutrecall_type4 lor_recalltype; union { layoutrecall_file4 lor_layout; fsid4 lor_fsid; } layoutrecall4_u; }; typedef struct layoutrecall4 layoutrecall4; struct CB_LAYOUTRECALL4args { layouttype4 clora_type; layoutiomode4 clora_iomode; bool_t clora_changed; layoutrecall4 clora_recall; }; typedef struct CB_LAYOUTRECALL4args CB_LAYOUTRECALL4args; struct CB_LAYOUTRECALL4res { nfsstat4 clorr_status; }; typedef struct CB_LAYOUTRECALL4res CB_LAYOUTRECALL4res; enum notify_type4 { NOTIFY4_CHANGE_CHILD_ATTRS = 0, NOTIFY4_CHANGE_DIR_ATTRS = 1, NOTIFY4_REMOVE_ENTRY = 2, NOTIFY4_ADD_ENTRY = 3, NOTIFY4_RENAME_ENTRY = 4, NOTIFY4_CHANGE_COOKIE_VERIFIER = 5, }; typedef enum notify_type4 notify_type4; struct notify_entry4 { component4 ne_file; fattr4 ne_attrs; }; typedef struct notify_entry4 notify_entry4; struct prev_entry4 { notify_entry4 pe_prev_entry; nfs_cookie4 pe_prev_entry_cookie; }; typedef struct prev_entry4 prev_entry4; struct notify_remove4 { notify_entry4 nrm_old_entry; nfs_cookie4 nrm_old_entry_cookie; }; typedef struct notify_remove4 notify_remove4; struct notify_add4 { struct { u_int nad_old_entry_len; notify_remove4 *nad_old_entry_val; } nad_old_entry; notify_entry4 nad_new_entry; struct { u_int nad_new_entry_cookie_len; nfs_cookie4 *nad_new_entry_cookie_val; } nad_new_entry_cookie; struct { u_int nad_prev_entry_len; prev_entry4 *nad_prev_entry_val; } nad_prev_entry; bool_t nad_last_entry; }; typedef struct notify_add4 notify_add4; struct notify_attr4 { notify_entry4 na_changed_entry; }; typedef struct notify_attr4 notify_attr4; struct notify_rename4 { notify_remove4 nrn_old_entry; notify_add4 nrn_new_entry; }; typedef struct notify_rename4 notify_rename4; struct notify_verifier4 { verifier4 nv_old_cookieverf; verifier4 nv_new_cookieverf; }; typedef struct notify_verifier4 notify_verifier4; typedef struct { u_int notifylist4_len; char *notifylist4_val; } notifylist4; struct notify4 { struct bitmap4 notify_mask; notifylist4 notify_vals; }; typedef struct notify4 notify4; struct CB_NOTIFY4args { stateid4 cna_stateid; nfs_fh4 cna_fh; struct { u_int cna_changes_len; notify4 *cna_changes_val; } cna_changes; }; typedef struct CB_NOTIFY4args CB_NOTIFY4args; struct CB_NOTIFY4res { nfsstat4 cnr_status; }; typedef struct CB_NOTIFY4res CB_NOTIFY4res; struct CB_PUSH_DELEG4args { nfs_fh4 cpda_fh; open_delegation4 cpda_delegation; }; typedef struct CB_PUSH_DELEG4args CB_PUSH_DELEG4args; struct CB_PUSH_DELEG4res { nfsstat4 cpdr_status; }; typedef struct CB_PUSH_DELEG4res CB_PUSH_DELEG4res; #define RCA4_TYPE_MASK_RDATA_DLG 0 #define RCA4_TYPE_MASK_WDATA_DLG 1 #define RCA4_TYPE_MASK_DIR_DLG 2 #define RCA4_TYPE_MASK_FILE_LAYOUT 3 #define RCA4_TYPE_MASK_BLK_LAYOUT 4 #define RCA4_TYPE_MASK_OBJ_LAYOUT_MIN 8 #define RCA4_TYPE_MASK_OBJ_LAYOUT_MAX 9 #define RCA4_TYPE_MASK_OTHER_LAYOUT_MIN 12 #define RCA4_TYPE_MASK_OTHER_LAYOUT_MAX 15 struct CB_RECALL_ANY4args { uint32_t craa_objects_to_keep; struct bitmap4 craa_type_mask; }; typedef struct CB_RECALL_ANY4args CB_RECALL_ANY4args; struct CB_RECALL_ANY4res { nfsstat4 crar_status; }; typedef struct CB_RECALL_ANY4res CB_RECALL_ANY4res; typedef CB_RECALL_ANY4args CB_RECALLABLE_OBJ_AVAIL4args; struct CB_RECALLABLE_OBJ_AVAIL4res { nfsstat4 croa_status; }; typedef struct CB_RECALLABLE_OBJ_AVAIL4res CB_RECALLABLE_OBJ_AVAIL4res; struct CB_RECALL_SLOT4args { slotid4 rsa_target_highest_slotid; }; typedef struct CB_RECALL_SLOT4args CB_RECALL_SLOT4args; struct CB_RECALL_SLOT4res { nfsstat4 rsr_status; }; typedef struct CB_RECALL_SLOT4res CB_RECALL_SLOT4res; struct referring_call4 { sequenceid4 rc_sequenceid; slotid4 rc_slotid; }; typedef struct referring_call4 referring_call4; struct referring_call_list4 { sessionid4 rcl_sessionid; struct { u_int rcl_referring_calls_len; referring_call4 *rcl_referring_calls_val; } rcl_referring_calls; }; typedef struct referring_call_list4 referring_call_list4; struct CB_SEQUENCE4args { sessionid4 csa_sessionid; sequenceid4 csa_sequenceid; slotid4 csa_slotid; slotid4 csa_highest_slotid; bool_t csa_cachethis; struct { u_int csa_referring_call_lists_len; referring_call_list4 *csa_referring_call_lists_val; } csa_referring_call_lists; }; typedef struct CB_SEQUENCE4args CB_SEQUENCE4args; struct CB_SEQUENCE4resok { sessionid4 csr_sessionid; sequenceid4 csr_sequenceid; slotid4 csr_slotid; slotid4 csr_highest_slotid; slotid4 csr_target_highest_slotid; }; typedef struct CB_SEQUENCE4resok CB_SEQUENCE4resok; struct CB_SEQUENCE4res { nfsstat4 csr_status; union { CB_SEQUENCE4resok csr_resok4; } CB_SEQUENCE4res_u; }; typedef struct CB_SEQUENCE4res CB_SEQUENCE4res; struct CB_WANTS_CANCELLED4args { bool_t cwca_contended_wants_cancelled; bool_t cwca_resourced_wants_cancelled; }; typedef struct CB_WANTS_CANCELLED4args CB_WANTS_CANCELLED4args; struct CB_WANTS_CANCELLED4res { nfsstat4 cwcr_status; }; typedef struct CB_WANTS_CANCELLED4res CB_WANTS_CANCELLED4res; struct CB_NOTIFY_LOCK4args { nfs_fh4 cnla_fh; lock_owner4 cnla_lock_owner; }; typedef struct CB_NOTIFY_LOCK4args CB_NOTIFY_LOCK4args; struct CB_NOTIFY_LOCK4res { nfsstat4 cnlr_status; }; typedef struct CB_NOTIFY_LOCK4res CB_NOTIFY_LOCK4res; enum notify_deviceid_type4 { NOTIFY_DEVICEID4_CHANGE = 1, NOTIFY_DEVICEID4_DELETE = 2, }; typedef enum notify_deviceid_type4 notify_deviceid_type4; #define NOTIFY_DEVICEID4_CHANGE_MASK (1 << NOTIFY_DEVICEID4_CHANGE) #define NOTIFY_DEVICEID4_DELETE_MASK (1 << NOTIFY_DEVICEID4_DELETE) struct notify_deviceid_delete4 { layouttype4 ndd_layouttype; deviceid4 ndd_deviceid; }; typedef struct notify_deviceid_delete4 notify_deviceid_delete4; struct notify_deviceid_change4 { layouttype4 ndc_layouttype; deviceid4 ndc_deviceid; bool_t ndc_immediate; }; typedef struct notify_deviceid_change4 notify_deviceid_change4; struct CB_NOTIFY_DEVICEID4args { struct { u_int cnda_changes_len; notify4 *cnda_changes_val; } cnda_changes; }; typedef struct CB_NOTIFY_DEVICEID4args CB_NOTIFY_DEVICEID4args; struct CB_NOTIFY_DEVICEID4res { nfsstat4 cndr_status; }; typedef struct CB_NOTIFY_DEVICEID4res CB_NOTIFY_DEVICEID4res; /* Callback operations new to NFSv4.1 */ enum nfs_cb_opnum4 { NFS4_OP_CB_GETATTR = 3, NFS4_OP_CB_RECALL = 4, NFS4_OP_CB_LAYOUTRECALL = 5, NFS4_OP_CB_NOTIFY = 6, NFS4_OP_CB_PUSH_DELEG = 7, NFS4_OP_CB_RECALL_ANY = 8, NFS4_OP_CB_RECALLABLE_OBJ_AVAIL = 9, NFS4_OP_CB_RECALL_SLOT = 10, NFS4_OP_CB_SEQUENCE = 11, NFS4_OP_CB_WANTS_CANCELLED = 12, NFS4_OP_CB_NOTIFY_LOCK = 13, NFS4_OP_CB_NOTIFY_DEVICEID = 14, NFS4_OP_CB_ILLEGAL = 10044, }; typedef enum nfs_cb_opnum4 nfs_cb_opnum4; struct nfs_cb_argop4 { u_int argop; union { CB_GETATTR4args opcbgetattr; CB_RECALL4args opcbrecall; CB_LAYOUTRECALL4args opcblayoutrecall; CB_NOTIFY4args opcbnotify; CB_PUSH_DELEG4args opcbpush_deleg; CB_RECALL_ANY4args opcbrecall_any; CB_RECALLABLE_OBJ_AVAIL4args opcbrecallable_obj_avail; CB_RECALL_SLOT4args opcbrecall_slot; CB_SEQUENCE4args opcbsequence; CB_WANTS_CANCELLED4args opcbwants_cancelled; CB_NOTIFY_LOCK4args opcbnotify_lock; CB_NOTIFY_DEVICEID4args opcbnotify_deviceid; } nfs_cb_argop4_u; }; typedef struct nfs_cb_argop4 nfs_cb_argop4; struct nfs_cb_resop4 { u_int resop; union { CB_GETATTR4res opcbgetattr; CB_RECALL4res opcbrecall; CB_LAYOUTRECALL4res opcblayoutrecall; CB_NOTIFY4res opcbnotify; CB_PUSH_DELEG4res opcbpush_deleg; CB_RECALL_ANY4res opcbrecall_any; CB_RECALLABLE_OBJ_AVAIL4res opcbrecallable_obj_avail; CB_RECALL_SLOT4res opcbrecall_slot; CB_SEQUENCE4res opcbsequence; CB_WANTS_CANCELLED4res opcbwants_cancelled; CB_NOTIFY_LOCK4res opcbnotify_lock; CB_NOTIFY_DEVICEID4res opcbnotify_deviceid; CB_ILLEGAL4res opcbillegal; } nfs_cb_resop4_u; }; typedef struct nfs_cb_resop4 nfs_cb_resop4; struct CB_COMPOUND4args { utf8str_cs tag; uint32_t minorversion; uint32_t callback_ident; struct { u_int argarray_len; nfs_cb_argop4 *argarray_val; } argarray; }; typedef struct CB_COMPOUND4args CB_COMPOUND4args; struct CB_COMPOUND4res { nfsstat4 status; utf8str_cs tag; struct { u_int resarray_len; nfs_cb_resop4 *resarray_val; } resarray; }; typedef struct CB_COMPOUND4res CB_COMPOUND4res; #define NFS4_PROGRAM 100003 #define NFS_V4 4 #if defined(__STDC__) || defined(__cplusplus) #define NFSPROC4_NULL 0 extern void *nfsproc4_null_4(void *, CLIENT *cl); extern void *nfsproc4_null_4_svc(void *, struct svc_req *); #define NFSPROC4_COMPOUND 1 extern COMPOUND4res *nfsproc4_compound_4(COMPOUND4args *, CLIENT *cl); extern COMPOUND4res *nfsproc4_compound_4_svc(COMPOUND4args *, struct svc_req *r); extern int nfs4_program_4_freeresult(SVCXPRT *, xdrproc_t, caddr_t); #else /* K&R C */ #define NFSPROC4_NULL 0 extern void *nfsproc4_null_4(); extern void *nfsproc4_null_4_svc(); #define NFSPROC4_COMPOUND 1 extern COMPOUND4res *nfsproc4_compound_4(); extern COMPOUND4res *nfsproc4_compound_4_svc(); extern int nfs4_program_4_freeresult(); #endif /* K&R C */ #define NFS4_CALLBACK 0x40000000 #define NFS_CB 1 #if defined(__STDC__) || defined(__cplusplus) #define CB_NULL 0 extern void *cb_null_1(void *, CLIENT *cl); extern void *cb_null_1_svc(void *, struct svc_req *r); #define CB_COMPOUND 1 extern CB_COMPOUND4res *cb_compound_1(CB_COMPOUND4args *, CLIENT *); extern CB_COMPOUND4res *cb_compound_1_svc(CB_COMPOUND4args *, struct svc_req *); extern int nfs4_callback_1_freeresult(SVCXPRT *, xdrproc_t, caddr_t); #else /* K&R C */ #define CB_NULL 0 extern void *cb_null_1(); extern void *cb_null_1_svc(); #define CB_COMPOUND 1 extern CB_COMPOUND4res *cb_compound_1(); extern CB_COMPOUND4res *cb_compound_1_svc(); extern int nfs4_callback_1_freeresult(); #endif /* K&R C */ /* the xdr functions */ #if defined(__STDC__) || defined(__cplusplus) static inline bool xdr_nfs_ftype4(XDR * xdrs, nfs_ftype4 *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_nfsstat4(XDR * xdrs, nfsstat4 *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_attrlist4(XDR * xdrs, attrlist4 *objp) { if (!inline_xdr_bytes (xdrs, (char **)&objp->attrlist4_val, &objp->attrlist4_len, XDR_BYTES_MAXLEN)) return false; return true; } static inline bool xdr_bitmap4(XDR * xdrs, struct bitmap4 *objp) { u_int32_t *map = objp->map; u_int i, mapsize; /* short circuit the free pass (done at the end) because we don't * allocate an array in the "conventional" sense here. There is * nothing to free and trying to free here would crash - bail out. */ if (xdrs->x_op == XDR_FREE) return true; /* for the same reason, calling xdr_array doesn't work for us (we need * to accept bitmaps bigger than BITMAP4_MAPLEN, but throw the rest away * so manually do the looping and skip the end */ if (!inline_xdr_u_int(xdrs, &objp->bitmap4_len)) return false; mapsize = MIN(objp->bitmap4_len, BITMAP4_MAPLEN); for (i = 0; i < mapsize; i++) if (!inline_xdr_u_int32_t(xdrs, &map[i])) return false; /* skip any further elements and lie on bitmap len */ for (i = mapsize; i < objp->bitmap4_len; i++) { u_int crud = 0; if (!inline_xdr_u_int32_t(xdrs, &crud)) return false; } objp->bitmap4_len = mapsize; return true; } static inline bool xdr_changeid4(XDR * xdrs, changeid4 *objp) { if (!inline_xdr_u_int64_t(xdrs, objp)) return false; return true; } static inline bool xdr_clientid4(XDR * xdrs, clientid4 *objp) { if (!inline_xdr_u_int64_t(xdrs, objp)) return false; return true; } static inline bool xdr_count4(XDR * xdrs, count4 *objp) { if (!inline_xdr_u_int32_t(xdrs, objp)) return false; return true; } static inline bool xdr_length4(XDR * xdrs, length4 *objp) { if (!inline_xdr_u_int64_t(xdrs, objp)) return false; return true; } static inline bool xdr_mode4(XDR * xdrs, mode4 *objp) { if (!inline_xdr_u_int32_t(xdrs, objp)) return false; return true; } static inline bool xdr_nfs_cookie4(XDR * xdrs, nfs_cookie4 *objp) { if (!inline_xdr_u_int64_t(xdrs, objp)) return false; return true; } static inline bool xdr_nfs_fh4(XDR * xdrs, nfs_fh4 *objp) { if (!inline_xdr_bytes (xdrs, (char **) &objp->nfs_fh4_val, &objp->nfs_fh4_len, NFS4_FHSIZE)) return false; return true; } static inline bool xdr_offset4(XDR * xdrs, offset4 *objp) { if (!inline_xdr_u_int64_t(xdrs, objp)) return false; return true; } static inline bool xdr_qop4(XDR * xdrs, qop4 *objp) { if (!inline_xdr_u_int32_t(xdrs, objp)) return false; return true; } static inline bool xdr_sec_oid4(XDR * xdrs, sec_oid4 *objp) { if (!inline_xdr_bytes (xdrs, (char **)&objp->sec_oid4_val, &objp->sec_oid4_len, XDR_BYTES_MAXLEN)) return false; return true; } static inline bool xdr_sequenceid4(XDR * xdrs, sequenceid4 *objp) { if (!inline_xdr_u_int32_t(xdrs, objp)) return false; return true; } static inline bool xdr_seqid4(XDR * xdrs, seqid4 *objp) { if (!inline_xdr_u_int32_t(xdrs, objp)) return false; return true; } static inline bool xdr_sessionid4(XDR * xdrs, sessionid4 objp) { if (!xdr_opaque(xdrs, objp, NFS4_SESSIONID_SIZE)) return false; return true; } static inline bool xdr_slotid4(XDR * xdrs, slotid4 *objp) { if (!inline_xdr_u_int32_t(xdrs, objp)) return false; return true; } static inline bool xdr_utf8string(XDR * xdrs, utf8string *objp) { if (!inline_xdr_bytes (xdrs, &objp->utf8string_val, &objp->utf8string_len, XDR_STRING_MAXLEN)) { return false; } return true; } static inline bool xdr_utf8str_cis(XDR * xdrs, utf8str_cis *objp) { if (!xdr_utf8string(xdrs, objp)) return false; return true; } static inline bool xdr_utf8str_cs(XDR * xdrs, utf8str_cs *objp) { if (!xdr_utf8string(xdrs, objp)) return false; return true; } static inline bool xdr_utf8str_mixed(XDR * xdrs, utf8str_mixed *objp) { if (!xdr_utf8string(xdrs, objp)) return false; return true; } static inline bool xdr_component4(XDR * xdrs, component4 *objp) { if (!xdr_utf8str_cs(xdrs, objp)) return false; return true; } static inline bool xdr_linktext4(XDR * xdrs, linktext4 *objp) { if (!xdr_utf8str_cs(xdrs, objp)) return false; return true; } static inline bool xdr_pathname4(XDR * xdrs, pathname4 *objp) { if (!xdr_array (xdrs, (char **)&objp->pathname4_val, &objp->pathname4_len, XDR_ARRAY_MAXLEN, sizeof(component4), (xdrproc_t) xdr_component4)) return false; return true; } static inline bool xdr_verifier4(XDR * xdrs, verifier4 objp) { if (!xdr_opaque(xdrs, objp, NFS4_VERIFIER_SIZE)) return false; return true; } static inline bool xdr_nfstime4(XDR * xdrs, nfstime4 *objp) { if (!xdr_int64_t(xdrs, &objp->seconds)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->nseconds)) return false; return true; } static inline bool xdr_time_how4(XDR * xdrs, time_how4 *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_settime4(XDR * xdrs, settime4 *objp) { if (!xdr_time_how4(xdrs, &objp->set_it)) return false; switch (objp->set_it) { case SET_TO_CLIENT_TIME4: if (!xdr_nfstime4(xdrs, &objp->settime4_u.time)) return false; break; default: break; } return true; } static inline bool xdr_nfs_lease4(XDR * xdrs, nfs_lease4 *objp) { if (!inline_xdr_u_int32_t(xdrs, objp)) return false; return true; } static inline bool xdr_fsid4(XDR * xdrs, fsid4 *objp) { if (!inline_xdr_u_int64_t(xdrs, &objp->major)) return false; if (!inline_xdr_u_int64_t(xdrs, &objp->minor)) return false; return true; } static inline bool xdr_change_policy4(XDR * xdrs, change_policy4 *objp) { if (!inline_xdr_u_int64_t(xdrs, &objp->cp_major)) return false; if (!inline_xdr_u_int64_t(xdrs, &objp->cp_minor)) return false; return true; } static inline bool xdr_fs_location4(XDR * xdrs, fs_location4 *objp) { if (!xdr_array (xdrs, (char **)&objp->server.server_val, &objp->server.server_len, XDR_ARRAY_MAXLEN, sizeof(utf8str_cis), (xdrproc_t) xdr_utf8str_cis)) return false; if (!xdr_pathname4(xdrs, &objp->rootpath)) return false; return true; } static inline bool xdr_fs_locations4(XDR * xdrs, fs_locations4 *objp) { if (!xdr_pathname4(xdrs, &objp->fs_root)) return false; if (!xdr_array (xdrs, (char **)&objp->locations.locations_val, &objp->locations.locations_len, XDR_ARRAY_MAXLEN, sizeof(fs_location4), (xdrproc_t) xdr_fs_location4)) return false; return true; } static inline bool xdr_acetype4(XDR * xdrs, acetype4 *objp) { if (!inline_xdr_u_int32_t(xdrs, objp)) return false; return true; } static inline bool xdr_aceflag4(XDR * xdrs, aceflag4 *objp) { if (!inline_xdr_u_int32_t(xdrs, objp)) return false; return true; } static inline bool xdr_acemask4(XDR * xdrs, acemask4 *objp) { if (!inline_xdr_u_int32_t(xdrs, objp)) return false; return true; } static inline bool xdr_nfsace4(XDR * xdrs, nfsace4 *objp) { if (!xdr_acetype4(xdrs, &objp->type)) return false; if (!xdr_aceflag4(xdrs, &objp->flag)) return false; if (!xdr_acemask4(xdrs, &objp->access_mask)) return false; if (!xdr_utf8str_mixed(xdrs, &objp->who)) return false; return true; } static inline bool xdr_aclflag4(XDR * xdrs, aclflag4 *objp) { if (!inline_xdr_u_int32_t(xdrs, objp)) return false; return true; } static inline bool xdr_nfsacl41(XDR * xdrs, nfsacl41 *objp) { if (!xdr_aclflag4(xdrs, &objp->na41_flag)) return false; if (!xdr_array (xdrs, (char **)&objp->na41_aces.na41_aces_val, &objp->na41_aces.na41_aces_len, XDR_ARRAY_MAXLEN, sizeof(nfsace4), (xdrproc_t) xdr_nfsace4)) return false; return true; } static inline bool xdr_mode_masked4(XDR * xdrs, mode_masked4 *objp) { if (!xdr_mode4(xdrs, &objp->mm_value_to_set)) return false; if (!xdr_mode4(xdrs, &objp->mm_mask_bits)) return false; return true; } static inline bool xdr_specdata4(XDR * xdrs, specdata4 *objp) { if (!inline_xdr_u_int32_t(xdrs, &objp->specdata1)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->specdata2)) return false; return true; } static inline bool xdr_netaddr4(XDR * xdrs, netaddr4 *objp) { if (!inline_xdr_string(xdrs, &objp->r_netid, XDR_STRING_MAXLEN)) return false; if (!inline_xdr_string(xdrs, &objp->r_addr, XDR_STRING_MAXLEN)) return false; return true; } static inline bool xdr_nfs_impl_id4(XDR * xdrs, nfs_impl_id4 *objp) { if (!xdr_utf8str_cis(xdrs, &objp->nii_domain)) return false; if (!xdr_utf8str_cs(xdrs, &objp->nii_name)) return false; if (!xdr_nfstime4(xdrs, &objp->nii_date)) return false; return true; } static inline bool xdr_stateid4(XDR * xdrs, stateid4 *objp) { if (!inline_xdr_u_int32_t(xdrs, &objp->seqid)) return false; if (!xdr_opaque(xdrs, objp->other, 12)) return false; return true; } static inline bool xdr_layouttype4(XDR * xdrs, layouttype4 *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_layout_content4(XDR * xdrs, layout_content4 *objp) { if (!xdr_layouttype4(xdrs, &objp->loc_type)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->loc_body.loc_body_val, &objp->loc_body.loc_body_len, XDR_BYTES_MAXLEN)) return false; return true; } /* * LAYOUT4_OSD2_OBJECTS loc_body description * is in a separate .x file */ /* * LAYOUT4_FLEX_FILES loc_body description * is in a separate .x file */ /* * LAYOUT4_BLOCK_VOLUME loc_body description * is in a separate .x file */ static inline bool xdr_layouthint4(XDR * xdrs, layouthint4 *objp) { if (!xdr_layouttype4(xdrs, &objp->loh_type)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->loh_body.loh_body_val, &objp->loh_body.loh_body_len, XDR_BYTES_MAXLEN)) return false; return true; } static inline bool xdr_layoutiomode4(XDR * xdrs, layoutiomode4 *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_layout4(XDR * xdrs, layout4 *objp) { if (!xdr_offset4(xdrs, &objp->lo_offset)) return false; if (!xdr_length4(xdrs, &objp->lo_length)) return false; if (!xdr_layoutiomode4(xdrs, &objp->lo_iomode)) return false; if (!xdr_layout_content4(xdrs, &objp->lo_content)) return false; return true; } static inline bool xdr_deviceid4(XDR * xdrs, deviceid4 objp) { if (!xdr_opaque(xdrs, objp, NFS4_DEVICEID4_SIZE)) return false; return true; } static inline bool xdr_device_addr4(XDR * xdrs, device_addr4 *objp) { if (!xdr_layouttype4(xdrs, &objp->da_layout_type)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->da_addr_body.da_addr_body_val, &objp->da_addr_body.da_addr_body_len, XDR_BYTES_MAXLEN)) return false; return true; } static inline bool xdr_layoutupdate4(XDR * xdrs, layoutupdate4 *objp) { if (!xdr_layouttype4(xdrs, &objp->lou_type)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->lou_body.lou_body_val, &objp->lou_body.lou_body_len, XDR_BYTES_MAXLEN)) return false; return true; } static inline bool xdr_layoutreturn_type4(XDR * xdrs, layoutreturn_type4 *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } /* layouttype4 specific data */ static inline bool xdr_layoutreturn_file4(XDR * xdrs, layoutreturn_file4 *objp) { if (!xdr_offset4(xdrs, &objp->lrf_offset)) return false; if (!xdr_length4(xdrs, &objp->lrf_length)) return false; if (!xdr_stateid4(xdrs, &objp->lrf_stateid)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->lrf_body.lrf_body_val, &objp->lrf_body.lrf_body_len, XDR_BYTES_MAXLEN)) return false; return true; } static inline bool xdr_layoutreturn4(XDR * xdrs, layoutreturn4 *objp) { if (!xdr_layoutreturn_type4(xdrs, &objp->lr_returntype)) return false; switch (objp->lr_returntype) { case LAYOUTRETURN4_FILE: if (!xdr_layoutreturn_file4 (xdrs, &objp->layoutreturn4_u.lr_layout)) return false; break; default: break; } return true; } static inline bool xdr_fs4_status_type(XDR * xdrs, fs4_status_type *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_fs4_status(XDR * xdrs, fs4_status *objp) { if (!xdr_bool(xdrs, &objp->fss_absent)) return false; if (!xdr_fs4_status_type(xdrs, &objp->fss_type)) return false; if (!xdr_utf8str_cs(xdrs, &objp->fss_source)) return false; if (!xdr_utf8str_cs(xdrs, &objp->fss_current)) return false; if (!xdr_int32_t(xdrs, &objp->fss_age)) return false; if (!xdr_nfstime4(xdrs, &objp->fss_version)) return false; return true; } static inline bool xdr_threshold4_read_size(XDR * xdrs, threshold4_read_size * objp) { if (!xdr_length4(xdrs, objp)) return false; return true; } static inline bool xdr_threshold4_write_size(XDR * xdrs, threshold4_write_size * objp) { if (!xdr_length4(xdrs, objp)) return false; return true; } static inline bool xdr_threshold4_read_iosize(XDR * xdrs, threshold4_read_iosize * objp) { if (!xdr_length4(xdrs, objp)) return false; return true; } static inline bool xdr_threshold4_write_iosize(XDR * xdrs, threshold4_write_iosize * objp) { if (!xdr_length4(xdrs, objp)) return false; return true; } static inline bool xdr_threshold_item4(XDR * xdrs, threshold_item4 *objp) { if (!xdr_layouttype4(xdrs, &objp->thi_layout_type)) return false; if (!xdr_bitmap4(xdrs, &objp->thi_hintset)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->thi_hintlist.thi_hintlist_val, &objp->thi_hintlist.thi_hintlist_len, XDR_BYTES_MAXLEN)) return false; return true; } static inline bool xdr_mdsthreshold4(XDR * xdrs, mdsthreshold4 *objp) { if (!xdr_array (xdrs, (char **)&objp->mth_hints.mth_hints_val, &objp->mth_hints.mth_hints_len, XDR_ARRAY_MAXLEN, sizeof(threshold_item4), (xdrproc_t) xdr_threshold_item4)) return false; return true; } static inline bool xdr_retention_get4(XDR * xdrs, retention_get4 *objp) { if (!inline_xdr_u_int64_t(xdrs, &objp->rg_duration)) return false; if (!xdr_array (xdrs, (char **)&objp->rg_begin_time.rg_begin_time_val, (u_int *) &objp->rg_begin_time.rg_begin_time_len, 1, sizeof(nfstime4), (xdrproc_t) xdr_nfstime4)) return false; return true; } static inline bool xdr_retention_set4(XDR * xdrs, retention_set4 *objp) { if (!inline_xdr_bool(xdrs, &objp->rs_enable)) return false; if (!xdr_array (xdrs, (char **)&objp->rs_duration.rs_duration_val, (u_int *) &objp->rs_duration.rs_duration_len, 1, sizeof(uint64_t), (xdrproc_t) inline_xdr_u_int64_t)) return false; return true; } static inline bool xdr_fs_charset_cap4(XDR * xdrs, fs_charset_cap4 *objp) { if (!inline_xdr_u_int32_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_supported_attrs(XDR * xdrs, fattr4_supported_attrs * objp) { if (!xdr_bitmap4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_type(XDR * xdrs, fattr4_type *objp) { if (!xdr_nfs_ftype4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_fh_expire_type(XDR * xdrs, fattr4_fh_expire_type * objp) { if (!inline_xdr_u_int32_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_change(XDR * xdrs, fattr4_change *objp) { if (!xdr_changeid4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_size(XDR * xdrs, fattr4_size *objp) { if (!inline_xdr_u_int64_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_link_support(XDR * xdrs, fattr4_link_support *objp) { if (!inline_xdr_bool(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_symlink_support(XDR * xdrs, fattr4_symlink_support * objp) { if (!inline_xdr_bool(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_named_attr(XDR * xdrs, fattr4_named_attr *objp) { if (!inline_xdr_bool(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_fsid(XDR * xdrs, fattr4_fsid *objp) { if (!xdr_fsid4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_unique_handles(XDR * xdrs, fattr4_unique_handles * objp) { if (!inline_xdr_bool(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_lease_time(XDR * xdrs, fattr4_lease_time *objp) { if (!xdr_nfs_lease4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_rdattr_error(XDR * xdrs, fattr4_rdattr_error *objp) { if (!xdr_nfsstat4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_acl(XDR * xdrs, fattr4_acl *objp) { if (!xdr_array (xdrs, (char **)&objp->fattr4_acl_val, &objp->fattr4_acl_len, XDR_ARRAY_MAXLEN, sizeof(nfsace4), (xdrproc_t) xdr_nfsace4)) return false; return true; } static inline bool xdr_fattr4_aclsupport(XDR * xdrs, fattr4_aclsupport *objp) { if (!inline_xdr_u_int32_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_archive(XDR * xdrs, fattr4_archive *objp) { if (!inline_xdr_bool(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_cansettime(XDR * xdrs, fattr4_cansettime *objp) { if (!inline_xdr_bool(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_case_insensitive(XDR * xdrs, fattr4_case_insensitive * objp) { if (!inline_xdr_bool(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_case_preserving(XDR * xdrs, fattr4_case_preserving * objp) { if (!inline_xdr_bool(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_chown_restricted(XDR * xdrs, fattr4_chown_restricted * objp) { if (!inline_xdr_bool(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_fileid(XDR * xdrs, fattr4_fileid *objp) { if (!inline_xdr_u_int64_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_files_avail(XDR * xdrs, fattr4_files_avail *objp) { if (!inline_xdr_u_int64_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_filehandle(XDR * xdrs, fattr4_filehandle *objp) { if (!xdr_nfs_fh4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_files_free(XDR * xdrs, fattr4_files_free *objp) { if (!inline_xdr_u_int64_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_files_total(XDR * xdrs, fattr4_files_total *objp) { if (!inline_xdr_u_int64_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_fs_locations(XDR * xdrs, fattr4_fs_locations *objp) { if (!xdr_fs_locations4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_hidden(XDR * xdrs, fattr4_hidden *objp) { if (!inline_xdr_bool(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_homogeneous(XDR * xdrs, fattr4_homogeneous *objp) { if (!inline_xdr_bool(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_maxfilesize(XDR * xdrs, fattr4_maxfilesize *objp) { if (!inline_xdr_u_int64_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_maxlink(XDR * xdrs, fattr4_maxlink *objp) { if (!inline_xdr_u_int32_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_maxname(XDR * xdrs, fattr4_maxname *objp) { if (!inline_xdr_u_int32_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_maxread(XDR * xdrs, fattr4_maxread *objp) { if (!inline_xdr_u_int64_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_maxwrite(XDR * xdrs, fattr4_maxwrite *objp) { if (!inline_xdr_u_int64_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_mimetype(XDR * xdrs, fattr4_mimetype *objp) { if (!xdr_utf8str_cs(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_mode(XDR * xdrs, fattr4_mode *objp) { if (!xdr_mode4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_mode_set_masked(XDR * xdrs, fattr4_mode_set_masked * objp) { if (!xdr_mode_masked4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_mounted_on_fileid(XDR * xdrs, fattr4_mounted_on_fileid *objp) { if (!inline_xdr_u_int64_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_no_trunc(XDR * xdrs, fattr4_no_trunc *objp) { if (!inline_xdr_bool(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_numlinks(XDR * xdrs, fattr4_numlinks *objp) { if (!inline_xdr_u_int32_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_owner(XDR * xdrs, fattr4_owner *objp) { if (!xdr_utf8str_mixed(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_owner_group(XDR * xdrs, fattr4_owner_group *objp) { if (!xdr_utf8str_mixed(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_quota_avail_hard(XDR * xdrs, fattr4_quota_avail_hard * objp) { if (!inline_xdr_u_int64_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_quota_avail_soft(XDR * xdrs, fattr4_quota_avail_soft * objp) { if (!inline_xdr_u_int64_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_quota_used(XDR * xdrs, fattr4_quota_used *objp) { if (!inline_xdr_u_int64_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_rawdev(XDR * xdrs, fattr4_rawdev *objp) { if (!xdr_specdata4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_space_avail(XDR * xdrs, fattr4_space_avail *objp) { if (!inline_xdr_u_int64_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_space_free(XDR * xdrs, fattr4_space_free *objp) { if (!inline_xdr_u_int64_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_space_total(XDR * xdrs, fattr4_space_total *objp) { if (!inline_xdr_u_int64_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_space_used(XDR * xdrs, fattr4_space_used *objp) { if (!inline_xdr_u_int64_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_system(XDR * xdrs, fattr4_system *objp) { if (!inline_xdr_bool(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_time_access(XDR * xdrs, fattr4_time_access *objp) { if (!xdr_nfstime4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_time_access_set(XDR * xdrs, fattr4_time_access_set * objp) { if (!xdr_settime4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_time_backup(XDR * xdrs, fattr4_time_backup *objp) { if (!xdr_nfstime4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_time_create(XDR * xdrs, fattr4_time_create *objp) { if (!xdr_nfstime4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_time_delta(XDR * xdrs, fattr4_time_delta *objp) { if (!xdr_nfstime4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_time_metadata(XDR * xdrs, fattr4_time_metadata * objp) { if (!xdr_nfstime4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_time_modify(XDR * xdrs, fattr4_time_modify *objp) { if (!xdr_nfstime4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_time_modify_set(XDR * xdrs, fattr4_time_modify_set * objp) { if (!xdr_settime4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_suppattr_exclcreat( XDR * xdrs, fattr4_suppattr_exclcreat *objp) { if (!xdr_bitmap4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_dir_notif_delay(XDR * xdrs, fattr4_dir_notif_delay * objp) { if (!xdr_nfstime4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_dirent_notif_delay( XDR * xdrs, fattr4_dirent_notif_delay *objp) { if (!xdr_nfstime4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_fs_layout_types(XDR * xdrs, fattr4_fs_layout_types * objp) { if (!xdr_array (xdrs, (char **)&objp->fattr4_fs_layout_types_val, &objp->fattr4_fs_layout_types_len, XDR_ARRAY_MAXLEN, sizeof(layouttype4), (xdrproc_t) xdr_layouttype4)) return false; return true; } static inline bool xdr_fattr4_fs_status(XDR * xdrs, fattr4_fs_status *objp) { if (!xdr_fs4_status(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_fs_charset_cap(XDR * xdrs, fattr4_fs_charset_cap * objp) { if (!xdr_fs_charset_cap4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_layout_alignment(XDR * xdrs, fattr4_layout_alignment * objp) { if (!inline_xdr_u_int32_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_layout_blksize(XDR * xdrs, fattr4_layout_blksize * objp) { if (!inline_xdr_u_int32_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_layout_hint(XDR * xdrs, fattr4_layout_hint *objp) { if (!xdr_layouthint4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_layout_types(XDR * xdrs, fattr4_layout_types *objp) { if (!xdr_array (xdrs, (char **)&objp->fattr4_layout_types_val, &objp->fattr4_layout_types_len, XDR_ARRAY_MAXLEN, sizeof(layouttype4), (xdrproc_t) xdr_layouttype4)) return false; return true; } static inline bool xdr_fattr4_mdsthreshold(XDR * xdrs, fattr4_mdsthreshold *objp) { if (!xdr_mdsthreshold4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_retention_get(XDR * xdrs, fattr4_retention_get * objp) { if (!xdr_retention_get4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_retention_set(XDR * xdrs, fattr4_retention_set * objp) { if (!xdr_retention_set4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_retentevt_get(XDR * xdrs, fattr4_retentevt_get * objp) { if (!xdr_retention_get4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_retentevt_set(XDR * xdrs, fattr4_retentevt_set * objp) { if (!xdr_retention_set4(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_retention_hold(XDR * xdrs, fattr4_retention_hold * objp) { if (!inline_xdr_u_int64_t(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_dacl(XDR * xdrs, fattr4_dacl *objp) { if (!xdr_nfsacl41(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_sacl(XDR * xdrs, fattr4_sacl *objp) { if (!xdr_nfsacl41(xdrs, objp)) return false; return true; } static inline bool xdr_fattr4_change_policy(XDR * xdrs, fattr4_change_policy * objp) { if (!xdr_change_policy4(xdrs, objp)) return false; return true; } /* * REQUIRED Attributes */ /* new to NFSV4.1 */ /* * RECOMMENDED Attributes */ /* new to NFSV4.1 */ static inline bool xdr_fattr4(XDR * xdrs, fattr4 *objp) { if (!xdr_bitmap4(xdrs, &objp->attrmask)) return false; if (!xdr_attrlist4(xdrs, &objp->attr_vals)) return false; return true; } static inline bool xdr_change_info4(XDR * xdrs, change_info4 *objp) { if (!inline_xdr_bool(xdrs, &objp->atomic)) return false; if (!xdr_changeid4(xdrs, &objp->before)) return false; if (!xdr_changeid4(xdrs, &objp->after)) return false; return true; } static inline bool xdr_clientaddr4(XDR * xdrs, clientaddr4 *objp) { if (!xdr_netaddr4(xdrs, objp)) return false; return true; } static inline bool xdr_cb_client4(XDR * xdrs, cb_client4 *objp) { if (!inline_xdr_u_int32_t(xdrs, &objp->cb_program)) return false; if (!xdr_netaddr4(xdrs, &objp->cb_location)) return false; return true; } static inline bool xdr_nfs_client_id4(XDR * xdrs, nfs_client_id4 *objp) { if (!xdr_verifier4(xdrs, objp->verifier)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->id.id_val, (u_int *) &objp->id.id_len, NFS4_OPAQUE_LIMIT)) return false; return true; } static inline bool xdr_client_owner4(XDR * xdrs, client_owner4 *objp) { if (!xdr_verifier4(xdrs, objp->co_verifier)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->co_ownerid.co_ownerid_val, (u_int *) &objp->co_ownerid.co_ownerid_len, NFS4_OPAQUE_LIMIT)) return false; return true; } static inline bool xdr_server_owner4(XDR * xdrs, server_owner4 *objp) { if (!inline_xdr_u_int64_t(xdrs, &objp->so_minor_id)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->so_major_id.so_major_id_val, (u_int *) &objp->so_major_id.so_major_id_len, NFS4_OPAQUE_LIMIT)) return false; return true; } static inline bool xdr_state_owner4(XDR * xdrs, state_owner4 *objp) { if (!xdr_clientid4(xdrs, &objp->clientid)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->owner.owner_val, (u_int *) &objp->owner.owner_len, NFS4_OPAQUE_LIMIT)) return false; return true; } static inline bool xdr_open_owner4(XDR * xdrs, open_owner4 *objp) { if (!xdr_state_owner4(xdrs, objp)) return false; return true; } static inline bool xdr_lock_owner4(XDR * xdrs, lock_owner4 *objp) { if (!xdr_state_owner4(xdrs, objp)) return false; return true; } static inline bool xdr_nfs_lock_type4(XDR * xdrs, nfs_lock_type4 *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } /* Input for computing subkeys */ static inline bool xdr_ssv_subkey4(XDR * xdrs, ssv_subkey4 *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } /* Input for computing smt_hmac */ static inline bool xdr_ssv_mic_plain_tkn4(XDR * xdrs, ssv_mic_plain_tkn4 *objp) { if (!inline_xdr_u_int32_t(xdrs, &objp->smpt_ssv_seq)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->smpt_orig_plain.smpt_orig_plain_val, &objp->smpt_orig_plain.smpt_orig_plain_len, XDR_BYTES_MAXLEN)) return false; return true; } /* SSV GSS PerMsgToken token */ static inline bool xdr_ssv_mic_tkn4(XDR * xdrs, ssv_mic_tkn4 *objp) { if (!inline_xdr_u_int32_t(xdrs, &objp->smt_ssv_seq)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->smt_hmac.smt_hmac_val, &objp->smt_hmac.smt_hmac_len, XDR_BYTES_MAXLEN)) return false; return true; } /* Input for computing ssct_encr_data and ssct_hmac */ static inline bool xdr_ssv_seal_plain_tkn4(XDR * xdrs, ssv_seal_plain_tkn4 *objp) { if (!inline_xdr_bytes (xdrs, (char **)&objp->sspt_confounder.sspt_confounder_val, &objp->sspt_confounder.sspt_confounder_len, XDR_BYTES_MAXLEN)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->sspt_ssv_seq)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->sspt_orig_plain.sspt_orig_plain_val, &objp->sspt_orig_plain.sspt_orig_plain_len, XDR_BYTES_MAXLEN)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->sspt_pad.sspt_pad_val, &objp->sspt_pad.sspt_pad_len, XDR_BYTES_MAXLEN)) return false; return true; } /* SSV GSS SealedMessage token */ static inline bool xdr_ssv_seal_cipher_tkn4(XDR * xdrs, ssv_seal_cipher_tkn4 * objp) { if (!inline_xdr_u_int32_t(xdrs, &objp->ssct_ssv_seq)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->ssct_iv.ssct_iv_val, &objp->ssct_iv.ssct_iv_len, XDR_BYTES_MAXLEN)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->ssct_encr_data.ssct_encr_data_val, &objp->ssct_encr_data.ssct_encr_data_len, XDR_BYTES_MAXLEN)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->ssct_hmac.ssct_hmac_val, &objp->ssct_hmac.ssct_hmac_len, XDR_BYTES_MAXLEN)) return false; return true; } static inline bool xdr_fs_locations_server4(XDR * xdrs, fs_locations_server4 * objp) { if (!xdr_int32_t(xdrs, &objp->fls_currency)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->fls_info.fls_info_val, &objp->fls_info.fls_info_len, XDR_BYTES_MAXLEN)) return false; if (!xdr_utf8str_cis(xdrs, &objp->fls_server)) return false; return true; } static inline bool xdr_fs_locations_item4(XDR * xdrs, fs_locations_item4 *objp) { if (!xdr_array (xdrs, (char **)&objp->fli_entries.fli_entries_val, &objp->fli_entries.fli_entries_len, XDR_ARRAY_MAXLEN, sizeof(fs_locations_server4), (xdrproc_t) xdr_fs_locations_server4)) return false; if (!xdr_pathname4(xdrs, &objp->fli_rootpath)) return false; return true; } static inline bool xdr_fs_locations_info4(XDR * xdrs, fs_locations_info4 *objp) { if (!inline_xdr_u_int32_t(xdrs, &objp->fli_flags)) return false; if (!xdr_int32_t(xdrs, &objp->fli_valid_for)) return false; if (!xdr_pathname4(xdrs, &objp->fli_fs_root)) return false; if (!xdr_array (xdrs, (char **)&objp->fli_items.fli_items_val, &objp->fli_items.fli_items_len, XDR_ARRAY_MAXLEN, sizeof(fs_locations_item4), (xdrproc_t) xdr_fs_locations_item4)) return false; return true; } static inline bool xdr_fattr4_fs_locations_info(XDR * xdrs, fattr4_fs_locations_info *objp) { if (!xdr_fs_locations_info4(xdrs, objp)) return false; return true; } static inline bool xdr_nfl_util4(XDR * xdrs, nfl_util4 *objp) { if (!inline_xdr_u_int32_t(xdrs, objp)) return false; return true; } static inline bool xdr_filelayout_hint_care4(XDR * xdrs, filelayout_hint_care4 * objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } /* Encoded in the loh_body field of data type layouthint4: */ static inline bool xdr_nfsv4_1_file_layouthint4(XDR * xdrs, nfsv4_1_file_layouthint4 *objp) { if (!inline_xdr_u_int32_t(xdrs, &objp->nflh_care)) return false; if (!xdr_nfl_util4(xdrs, &objp->nflh_util)) return false; if (!xdr_count4(xdrs, &objp->nflh_stripe_count)) return false; return true; } static inline bool xdr_multipath_list4(XDR * xdrs, multipath_list4 *objp) { if (!xdr_array (xdrs, (char **)&objp->multipath_list4_val, &objp->multipath_list4_len, XDR_ARRAY_MAXLEN, sizeof(netaddr4), (xdrproc_t) xdr_netaddr4)) return false; return true; } /* * Encoded in the da_addr_body field of * data type device_addr4: */ static inline bool xdr_nfsv4_1_file_layout_ds_addr4( XDR * xdrs, nfsv4_1_file_layout_ds_addr4 *objp) { if (!xdr_array( xdrs, (char **)&objp->nflda_stripe_indices.nflda_stripe_indices_val, &objp->nflda_stripe_indices.nflda_stripe_indices_len, XDR_ARRAY_MAXLEN, sizeof(uint32_t), (xdrproc_t) xdr_uint32_t)) return false; if (!xdr_array( xdrs, (char **)&objp->nflda_multipath_ds_list.nflda_multipath_ds_list_val, &objp->nflda_multipath_ds_list.nflda_multipath_ds_list_len, XDR_ARRAY_MAXLEN, sizeof(multipath_list4), (xdrproc_t) xdr_multipath_list4)) return false; return true; } /* * Encoded in the loc_body field of * data type layout_content4: */ static inline bool xdr_nfsv4_1_file_layout4(XDR * xdrs, nfsv4_1_file_layout4 * objp) { if (!xdr_deviceid4(xdrs, objp->nfl_deviceid)) return false; if (!xdr_nfl_util4(xdrs, &objp->nfl_util)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->nfl_first_stripe_index)) return false; if (!xdr_offset4(xdrs, &objp->nfl_pattern_offset)) return false; if (!xdr_array (xdrs, (char **)&objp->nfl_fh_list.nfl_fh_list_val, &objp->nfl_fh_list.nfl_fh_list_len, XDR_ARRAY_MAXLEN, sizeof(nfs_fh4), (xdrproc_t) xdr_nfs_fh4)) return false; return true; } /* * Encoded in the lou_body field of data type layoutupdate4: * Nothing. lou_body is a zero length array of bytes. */ /* * Encoded in the lrf_body field of * data type layoutreturn_file4: * Nothing. lrf_body is a zero length array of bytes. */ static inline bool xdr_ACCESS4args(XDR * xdrs, ACCESS4args *objp) { if (!inline_xdr_u_int32_t(xdrs, &objp->access)) return false; return true; } static inline bool xdr_ACCESS4resok(XDR * xdrs, ACCESS4resok *objp) { if (!inline_xdr_u_int32_t(xdrs, &objp->supported)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->access)) return false; return true; } static inline bool xdr_ACCESS4res(XDR * xdrs, ACCESS4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_ACCESS4resok(xdrs, &objp->ACCESS4res_u.resok4)) return false; break; default: break; } return true; } static inline bool xdr_CLOSE4args(XDR * xdrs, CLOSE4args *objp) { if (!xdr_seqid4(xdrs, &objp->seqid)) return false; if (!xdr_stateid4(xdrs, &objp->open_stateid)) return false; return true; } static inline bool xdr_CLOSE4res(XDR * xdrs, CLOSE4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_stateid4 (xdrs, &objp->CLOSE4res_u.open_stateid)) return false; break; default: break; } return true; } static inline bool xdr_COMMIT4args(XDR * xdrs, COMMIT4args *objp) { if (!xdr_offset4(xdrs, &objp->offset)) return false; if (!xdr_count4(xdrs, &objp->count)) return false; return true; } static inline bool xdr_COMMIT4resok(XDR * xdrs, COMMIT4resok *objp) { if (!xdr_verifier4(xdrs, objp->writeverf)) return false; return true; } static inline bool xdr_COMMIT4res(XDR * xdrs, COMMIT4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_COMMIT4resok(xdrs, &objp->COMMIT4res_u.resok4)) return false; break; default: break; } return true; } static inline bool xdr_createtype4(XDR * xdrs, createtype4 *objp) { if (!xdr_nfs_ftype4(xdrs, &objp->type)) return false; switch (objp->type) { case NF4LNK: if (!xdr_linktext4(xdrs, &objp->createtype4_u.linkdata)) return false; break; case NF4BLK: case NF4CHR: if (!xdr_specdata4(xdrs, &objp->createtype4_u.devdata)) return false; break; case NF4SOCK: case NF4FIFO: case NF4DIR: break; default: break; } return true; } static inline bool xdr_CREATE4args(XDR * xdrs, CREATE4args *objp) { if (!xdr_createtype4(xdrs, &objp->objtype)) return false; if (!xdr_component4(xdrs, &objp->objname)) return false; if (!xdr_fattr4(xdrs, &objp->createattrs)) return false; return true; } static inline bool xdr_CREATE4resok(XDR * xdrs, CREATE4resok *objp) { if (!xdr_change_info4(xdrs, &objp->cinfo)) return false; if (!xdr_bitmap4(xdrs, &objp->attrset)) return false; return true; } static inline bool xdr_CREATE4res(XDR * xdrs, CREATE4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_CREATE4resok(xdrs, &objp->CREATE4res_u.resok4)) return false; break; default: break; } return true; } static inline bool xdr_DELEGPURGE4args(XDR * xdrs, DELEGPURGE4args *objp) { if (!xdr_clientid4(xdrs, &objp->clientid)) return false; return true; } static inline bool xdr_DELEGPURGE4res(XDR * xdrs, DELEGPURGE4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; return true; } static inline bool xdr_DELEGRETURN4args(XDR * xdrs, DELEGRETURN4args *objp) { if (!xdr_stateid4(xdrs, &objp->deleg_stateid)) return false; return true; } static inline bool xdr_DELEGRETURN4res(XDR * xdrs, DELEGRETURN4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; return true; } /* NFSv4.2 */ static inline bool xdr_GETXATTR4args(XDR * xdrs, GETXATTR4args *objp) { if (!xdr_component4(xdrs, &objp->ga_name)) return false; return true; } static inline bool xdr_GETXATTR4res(XDR * xdrs, GETXATTR4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_component4(xdrs, &objp->GETXATTR4res_u.resok4.gr_value)) return false; break; default: break; } return true; } static inline bool xdr_SETXATTR4args(XDR * xdrs, SETXATTR4args *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) &objp->sa_type)) return false; if (!xdr_component4(xdrs, &objp->sa_xattr.xa_name)) return false; if (!xdr_component4(xdrs, &objp->sa_xattr.xa_value)) return false; return true; } static inline bool xdr_SETXATTR4res(XDR * xdrs, SETXATTR4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_change_info4(xdrs, &objp->SETXATTR4res_u.resok4.sr_info)) return false; break; default: break; } return true; } static inline bool xdr_LISTXATTR4args(XDR * xdrs, LISTXATTR4args *objp) { if (!xdr_nfs_cookie4(xdrs, &objp->la_cookie)) return false; if (!xdr_verifier4(xdrs, objp->la_cookieverf)) return false; if (!xdr_count4(xdrs, &objp->la_maxcount)) return false; return true; } static inline bool xdr_listxattr4(XDR * xdrs, xattrlist4 *objp) { if (!xdr_array (xdrs, (char **)&objp->entries, &objp->entryCount, XDR_ARRAY_MAXLEN, sizeof(component4), (xdrproc_t) xdr_component4)) return false; return true; } static inline bool xdr_LISTXATTR4resok(XDR * xdrs, LISTXATTR4resok *objp) { if (!xdr_nfs_cookie4(xdrs, &objp->lr_cookie)) return false; if (!xdr_verifier4(xdrs, objp->lr_cookieverf)) return false; if (!inline_xdr_bool(xdrs, &objp->lr_eof)) return false; if (!xdr_listxattr4(xdrs, &objp->lr_names)) return false; return true; } static inline bool xdr_LISTXATTR4res(XDR * xdrs, LISTXATTR4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_LISTXATTR4resok (xdrs, &objp->LISTXATTR4res_u.resok4)) return false; break; default: break; } return true; } static inline bool xdr_REMOVEXATTR4args(XDR * xdrs, REMOVEXATTR4args *objp) { if (!xdr_component4(xdrs, &objp->ra_name)) return false; return true; } static inline bool xdr_REMOVEXATTR4res(XDR * xdrs, REMOVEXATTR4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_change_info4(xdrs, &objp->REMOVEXATTR4res_u.resok4.rr_info)) return false; break; default: break; } return true; } static inline bool xdr_GETATTR4args(XDR * xdrs, GETATTR4args *objp) { if (!xdr_bitmap4(xdrs, &objp->attr_request)) return false; return true; } static inline bool xdr_GETATTR4resok(XDR * xdrs, GETATTR4resok *objp) { if (!xdr_fattr4(xdrs, &objp->obj_attributes)) return false; return true; } static inline bool xdr_GETATTR4res(XDR * xdrs, GETATTR4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_GETATTR4resok (xdrs, &objp->GETATTR4res_u.resok4)) return false; break; default: break; } return true; } static inline bool xdr_GETFH4resok(XDR * xdrs, GETFH4resok *objp) { if (!xdr_nfs_fh4(xdrs, &objp->object)) return false; return true; } static inline bool xdr_GETFH4res(XDR * xdrs, GETFH4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_GETFH4resok(xdrs, &objp->GETFH4res_u.resok4)) return false; break; default: break; } return true; } static inline bool xdr_LINK4args(XDR * xdrs, LINK4args *objp) { if (!xdr_component4(xdrs, &objp->newname)) return false; return true; } static inline bool xdr_LINK4resok(XDR * xdrs, LINK4resok *objp) { if (!xdr_change_info4(xdrs, &objp->cinfo)) return false; return true; } static inline bool xdr_LINK4res(XDR * xdrs, LINK4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_LINK4resok(xdrs, &objp->LINK4res_u.resok4)) return false; break; default: break; } return true; } static inline bool xdr_open_to_lock_owner4(XDR * xdrs, open_to_lock_owner4 *objp) { if (!xdr_seqid4(xdrs, &objp->open_seqid)) return false; if (!xdr_stateid4(xdrs, &objp->open_stateid)) return false; if (!xdr_seqid4(xdrs, &objp->lock_seqid)) return false; if (!xdr_lock_owner4(xdrs, &objp->lock_owner)) return false; return true; } static inline bool xdr_exist_lock_owner4(XDR * xdrs, exist_lock_owner4 *objp) { if (!xdr_stateid4(xdrs, &objp->lock_stateid)) return false; if (!xdr_seqid4(xdrs, &objp->lock_seqid)) return false; return true; } static inline bool xdr_locker4(XDR * xdrs, locker4 *objp) { if (!inline_xdr_bool(xdrs, &objp->new_lock_owner)) return false; switch (objp->new_lock_owner) { case true: if (!xdr_open_to_lock_owner4 (xdrs, &objp->locker4_u.open_owner)) return false; break; case false: if (!xdr_exist_lock_owner4 (xdrs, &objp->locker4_u.lock_owner)) return false; break; default: return false; } return true; } static inline bool xdr_LOCK4args(XDR * xdrs, LOCK4args *objp) { if (!xdr_nfs_lock_type4(xdrs, &objp->locktype)) return false; if (!inline_xdr_bool(xdrs, &objp->reclaim)) return false; if (!xdr_offset4(xdrs, &objp->offset)) return false; if (!xdr_length4(xdrs, &objp->length)) return false; if (!xdr_locker4(xdrs, &objp->locker)) return false; return true; } static inline bool xdr_LOCK4denied(XDR * xdrs, LOCK4denied *objp) { if (!xdr_offset4(xdrs, &objp->offset)) return false; if (!xdr_length4(xdrs, &objp->length)) return false; if (!xdr_nfs_lock_type4(xdrs, &objp->locktype)) return false; if (!xdr_lock_owner4(xdrs, &objp->owner)) return false; return true; } static inline bool xdr_LOCK4resok(XDR * xdrs, LOCK4resok *objp) { if (!xdr_stateid4(xdrs, &objp->lock_stateid)) return false; return true; } static inline bool xdr_LOCK4res(XDR * xdrs, LOCK4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_LOCK4resok(xdrs, &objp->LOCK4res_u.resok4)) return false; break; case NFS4ERR_DENIED: if (!xdr_LOCK4denied(xdrs, &objp->LOCK4res_u.denied)) return false; break; default: break; } return true; } static inline bool xdr_LOCKT4args(XDR * xdrs, LOCKT4args *objp) { if (!xdr_nfs_lock_type4(xdrs, &objp->locktype)) return false; if (!xdr_offset4(xdrs, &objp->offset)) return false; if (!xdr_length4(xdrs, &objp->length)) return false; if (!xdr_lock_owner4(xdrs, &objp->owner)) return false; return true; } static inline bool xdr_LOCKT4res(XDR * xdrs, LOCKT4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4ERR_DENIED: if (!xdr_LOCK4denied(xdrs, &objp->LOCKT4res_u.denied)) return false; break; case NFS4_OK: break; default: break; } return true; } static inline bool xdr_LOCKU4args(XDR * xdrs, LOCKU4args *objp) { if (!xdr_nfs_lock_type4(xdrs, &objp->locktype)) return false; if (!xdr_seqid4(xdrs, &objp->seqid)) return false; if (!xdr_stateid4(xdrs, &objp->lock_stateid)) return false; if (!xdr_offset4(xdrs, &objp->offset)) return false; if (!xdr_length4(xdrs, &objp->length)) return false; return true; } static inline bool xdr_LOCKU4res(XDR * xdrs, LOCKU4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_stateid4 (xdrs, &objp->LOCKU4res_u.lock_stateid)) return false; break; default: break; } return true; } static inline bool xdr_LOOKUP4args(XDR * xdrs, LOOKUP4args *objp) { if (!xdr_component4(xdrs, &objp->objname)) return false; return true; } static inline bool xdr_LOOKUP4res(XDR * xdrs, LOOKUP4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; return true; } static inline bool xdr_LOOKUPP4res(XDR * xdrs, LOOKUPP4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; return true; } static inline bool xdr_NVERIFY4args(XDR * xdrs, NVERIFY4args *objp) { if (!xdr_fattr4(xdrs, &objp->obj_attributes)) return false; return true; } static inline bool xdr_NVERIFY4res(XDR * xdrs, NVERIFY4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; return true; } static inline bool xdr_createmode4(XDR * xdrs, createmode4 *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_creatverfattr(XDR * xdrs, creatverfattr *objp) { if (!xdr_verifier4(xdrs, objp->cva_verf)) return false; if (!xdr_fattr4(xdrs, &objp->cva_attrs)) return false; return true; } static inline bool xdr_createhow4(XDR * xdrs, createhow4 *objp) { if (!xdr_createmode4(xdrs, &objp->mode)) return false; switch (objp->mode) { case UNCHECKED4: case GUARDED4: if (!xdr_fattr4(xdrs, &objp->createhow4_u.createattrs)) return false; break; case EXCLUSIVE4: if (!xdr_verifier4(xdrs, objp->createhow4_u.createverf)) return false; break; case EXCLUSIVE4_1: if (!xdr_creatverfattr (xdrs, &objp->createhow4_u.ch_createboth)) return false; break; default: return false; } return true; } static inline bool xdr_opentype4(XDR * xdrs, opentype4 *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_openflag4(XDR * xdrs, openflag4 *objp) { if (!xdr_opentype4(xdrs, &objp->opentype)) return false; switch (objp->opentype) { case OPEN4_CREATE: if (!xdr_createhow4(xdrs, &objp->openflag4_u.how)) return false; break; default: break; } return true; } static inline bool xdr_limit_by4(XDR * xdrs, limit_by4 *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_nfs_modified_limit4(XDR * xdrs, nfs_modified_limit4 *objp) { if (!inline_xdr_u_int32_t(xdrs, &objp->num_blocks)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->bytes_per_block)) return false; return true; } static inline bool xdr_nfs_space_limit4(XDR * xdrs, nfs_space_limit4 *objp) { if (!xdr_limit_by4(xdrs, &objp->limitby)) return false; switch (objp->limitby) { case NFS_LIMIT_SIZE: if (!inline_xdr_u_int64_t (xdrs, &objp->nfs_space_limit4_u.filesize)) return false; break; case NFS_LIMIT_BLOCKS: if (!xdr_nfs_modified_limit4 (xdrs, &objp->nfs_space_limit4_u.mod_blocks)) return false; break; default: return false; } return true; } static inline bool xdr_open_delegation_type4(XDR * xdrs, open_delegation_type4 * objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_open_claim_type4(XDR * xdrs, open_claim_type4 *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_open_claim_delegate_cur4(XDR * xdrs, open_claim_delegate_cur4 *objp) { if (!xdr_stateid4(xdrs, &objp->delegate_stateid)) return false; if (!xdr_component4(xdrs, &objp->file)) return false; return true; } static inline bool xdr_open_claim4(XDR * xdrs, open_claim4 *objp) { if (!xdr_open_claim_type4(xdrs, &objp->claim)) return false; switch (objp->claim) { case CLAIM_NULL: if (!xdr_component4(xdrs, &objp->open_claim4_u.file)) return false; break; case CLAIM_PREVIOUS: if (!xdr_open_delegation_type4 (xdrs, &objp->open_claim4_u.delegate_type)) return false; break; case CLAIM_DELEGATE_CUR: if (!xdr_open_claim_delegate_cur4 (xdrs, &objp->open_claim4_u.delegate_cur_info)) return false; break; case CLAIM_DELEGATE_PREV: if (!xdr_component4 (xdrs, &objp->open_claim4_u.file_delegate_prev)) return false; break; case CLAIM_FH: break; case CLAIM_DELEG_PREV_FH: break; case CLAIM_DELEG_CUR_FH: if (!xdr_stateid4 (xdrs, &objp->open_claim4_u.oc_delegate_stateid)) return false; break; default: return false; } return true; } static inline bool xdr_OPEN4args(XDR * xdrs, OPEN4args *objp) { if (!xdr_seqid4(xdrs, &objp->seqid)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->share_access)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->share_deny)) return false; if (!xdr_open_owner4(xdrs, &objp->owner)) return false; if (!xdr_openflag4(xdrs, &objp->openhow)) return false; if (!xdr_open_claim4(xdrs, &objp->claim)) return false; return true; } static inline bool xdr_open_read_delegation4(XDR * xdrs, open_read_delegation4 * objp) { if (!xdr_stateid4(xdrs, &objp->stateid)) return false; if (!inline_xdr_bool(xdrs, &objp->recall)) return false; if (!xdr_nfsace4(xdrs, &objp->permissions)) return false; return true; } static inline bool xdr_open_write_delegation4(XDR * xdrs, open_write_delegation4 * objp) { if (!xdr_stateid4(xdrs, &objp->stateid)) return false; if (!inline_xdr_bool(xdrs, &objp->recall)) return false; if (!xdr_nfs_space_limit4(xdrs, &objp->space_limit)) return false; if (!xdr_nfsace4(xdrs, &objp->permissions)) return false; return true; } static inline bool xdr_why_no_delegation4(XDR * xdrs, why_no_delegation4 *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_open_none_delegation4(XDR * xdrs, open_none_delegation4 * objp) { if (!xdr_why_no_delegation4(xdrs, &objp->ond_why)) return false; switch (objp->ond_why) { case WND4_CONTENTION: if (!inline_xdr_bool (xdrs, &objp->open_none_delegation4_u. ond_server_will_push_deleg)) return false; break; case WND4_RESOURCE: if (!inline_xdr_bool (xdrs, &objp->open_none_delegation4_u. ond_server_will_signal_avail)) return false; break; default: break; } return true; } static inline bool xdr_open_delegation4(XDR * xdrs, open_delegation4 *objp) { if (!xdr_open_delegation_type4(xdrs, &objp->delegation_type)) return false; switch (objp->delegation_type) { case OPEN_DELEGATE_NONE: break; case OPEN_DELEGATE_READ: if (!xdr_open_read_delegation4 (xdrs, &objp->open_delegation4_u.read)) return false; break; case OPEN_DELEGATE_WRITE: if (!xdr_open_write_delegation4 (xdrs, &objp->open_delegation4_u.write)) return false; break; case OPEN_DELEGATE_NONE_EXT: if (!xdr_open_none_delegation4 (xdrs, &objp->open_delegation4_u.od_whynone)) return false; break; default: return false; } return true; } static inline bool xdr_OPEN4resok(XDR * xdrs, OPEN4resok *objp) { if (!xdr_stateid4(xdrs, &objp->stateid)) return false; if (!xdr_change_info4(xdrs, &objp->cinfo)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->rflags)) return false; if (!xdr_bitmap4(xdrs, &objp->attrset)) return false; if (!xdr_open_delegation4(xdrs, &objp->delegation)) return false; return true; } static inline bool xdr_OPEN4res(XDR * xdrs, OPEN4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_OPEN4resok(xdrs, &objp->OPEN4res_u.resok4)) return false; break; default: break; } return true; } static inline bool xdr_OPENATTR4args(XDR * xdrs, OPENATTR4args *objp) { if (!inline_xdr_bool(xdrs, &objp->createdir)) return false; return true; } static inline bool xdr_OPENATTR4res(XDR * xdrs, OPENATTR4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; return true; } static inline bool xdr_OPEN_CONFIRM4args(XDR * xdrs, OPEN_CONFIRM4args *objp) { if (!xdr_stateid4(xdrs, &objp->open_stateid)) return false; if (!xdr_seqid4(xdrs, &objp->seqid)) return false; return true; } static inline bool xdr_OPEN_CONFIRM4resok(XDR * xdrs, OPEN_CONFIRM4resok *objp) { if (!xdr_stateid4(xdrs, &objp->open_stateid)) return false; return true; } static inline bool xdr_OPEN_CONFIRM4res(XDR * xdrs, OPEN_CONFIRM4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_OPEN_CONFIRM4resok (xdrs, &objp->OPEN_CONFIRM4res_u.resok4)) return false; break; default: break; } return true; } static inline bool xdr_OPEN_DOWNGRADE4args(XDR * xdrs, OPEN_DOWNGRADE4args *objp) { if (!xdr_stateid4(xdrs, &objp->open_stateid)) return false; if (!xdr_seqid4(xdrs, &objp->seqid)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->share_access)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->share_deny)) return false; return true; } static inline bool xdr_OPEN_DOWNGRADE4resok(XDR * xdrs, OPEN_DOWNGRADE4resok * objp) { if (!xdr_stateid4(xdrs, &objp->open_stateid)) return false; return true; } static inline bool xdr_OPEN_DOWNGRADE4res(XDR * xdrs, OPEN_DOWNGRADE4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_OPEN_DOWNGRADE4resok (xdrs, &objp->OPEN_DOWNGRADE4res_u.resok4)) return false; break; default: break; } return true; } static inline bool xdr_PUTFH4args(XDR * xdrs, PUTFH4args *objp) { if (!xdr_nfs_fh4(xdrs, &objp->object)) return false; return true; } static inline bool xdr_PUTFH4res(XDR * xdrs, PUTFH4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; return true; } static inline bool xdr_PUTPUBFH4res(XDR * xdrs, PUTPUBFH4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; return true; } static inline bool xdr_PUTROOTFH4res(XDR * xdrs, PUTROOTFH4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; return true; } static inline bool xdr_READ4args(XDR * xdrs, READ4args *objp) { if (!xdr_stateid4(xdrs, &objp->stateid)) return false; if (!xdr_offset4(xdrs, &objp->offset)) return false; if (!xdr_count4(xdrs, &objp->count)) return false; return true; } static inline bool xdr_READ4resok(XDR * xdrs, READ4resok *objp) { if (!inline_xdr_bool(xdrs, &objp->eof)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->data.data_val, &objp->data.data_len, XDR_BYTES_MAXLEN_IO)) return false; return true; } static inline bool xdr_READ4res(XDR * xdrs, READ4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_READ4resok(xdrs, &objp->READ4res_u.resok4)) return false; break; default: break; } return true; } static inline bool xdr_READDIR4args(XDR * xdrs, READDIR4args *objp) { if (!xdr_nfs_cookie4(xdrs, &objp->cookie)) return false; if (!xdr_verifier4(xdrs, objp->cookieverf)) return false; if (!xdr_count4(xdrs, &objp->dircount)) return false; if (!xdr_count4(xdrs, &objp->maxcount)) return false; if (!xdr_bitmap4(xdrs, &objp->attr_request)) return false; return true; } static inline bool xdr_entry4(XDR * xdrs, entry4 *objp) { if (!xdr_nfs_cookie4(xdrs, &objp->cookie)) return false; if (!xdr_component4(xdrs, &objp->name)) return false; if (!xdr_fattr4(xdrs, &objp->attrs)) return false; if (!xdr_pointer (xdrs, (char **)&objp->nextentry, sizeof(entry4), (xdrproc_t) xdr_entry4)) return false; return true; } static inline bool xdr_dirlist4(XDR * xdrs, dirlist4 *objp) { if (!xdr_pointer (xdrs, (char **)&objp->entries, sizeof(entry4), (xdrproc_t) xdr_entry4)) return false; if (!inline_xdr_bool(xdrs, &objp->eof)) return false; return true; } static inline bool xdr_READDIR4resok(XDR * xdrs, READDIR4resok *objp) { if (!xdr_verifier4(xdrs, objp->cookieverf)) return false; if (!xdr_dirlist4(xdrs, &objp->reply)) return false; return true; } static inline bool xdr_READDIR4res(XDR * xdrs, READDIR4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_READDIR4resok (xdrs, &objp->READDIR4res_u.resok4)) return false; break; default: break; } return true; } static inline bool xdr_READLINK4resok(XDR * xdrs, READLINK4resok *objp) { if (!xdr_linktext4(xdrs, &objp->link)) return false; return true; } static inline bool xdr_READLINK4res(XDR * xdrs, READLINK4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_READLINK4resok (xdrs, &objp->READLINK4res_u.resok4)) return false; break; default: break; } return true; } static inline bool xdr_REMOVE4args(XDR * xdrs, REMOVE4args *objp) { if (!xdr_component4(xdrs, &objp->target)) return false; return true; } static inline bool xdr_REMOVE4resok(XDR * xdrs, REMOVE4resok *objp) { if (!xdr_change_info4(xdrs, &objp->cinfo)) return false; return true; } static inline bool xdr_REMOVE4res(XDR * xdrs, REMOVE4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_REMOVE4resok(xdrs, &objp->REMOVE4res_u.resok4)) return false; break; default: break; } return true; } static inline bool xdr_RENAME4args(XDR * xdrs, RENAME4args *objp) { if (!xdr_component4(xdrs, &objp->oldname)) return false; if (!xdr_component4(xdrs, &objp->newname)) return false; return true; } static inline bool xdr_RENAME4resok(XDR * xdrs, RENAME4resok *objp) { if (!xdr_change_info4(xdrs, &objp->source_cinfo)) return false; if (!xdr_change_info4(xdrs, &objp->target_cinfo)) return false; return true; } static inline bool xdr_RENAME4res(XDR * xdrs, RENAME4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_RENAME4resok(xdrs, &objp->RENAME4res_u.resok4)) return false; break; default: break; } return true; } static inline bool xdr_RENEW4args(XDR * xdrs, RENEW4args *objp) { if (!xdr_clientid4(xdrs, &objp->clientid)) return false; return true; } static inline bool xdr_RENEW4res(XDR * xdrs, RENEW4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; return true; } static inline bool xdr_RESTOREFH4res(XDR * xdrs, RESTOREFH4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; return true; } static inline bool xdr_SAVEFH4res(XDR * xdrs, SAVEFH4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; return true; } static inline bool xdr_SECINFO4args(XDR * xdrs, SECINFO4args *objp) { if (!xdr_component4(xdrs, &objp->name)) return false; return true; } #ifdef _HAVE_GSSAPI static inline bool xdr_rpc_gss_svc_t(XDR * xdrs, rpc_gss_svc_t *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_rpcsec_gss_info(XDR * xdrs, rpcsec_gss_info *objp) { if (!xdr_sec_oid4(xdrs, &objp->oid)) return false; if (!xdr_qop4(xdrs, &objp->qop)) return false; if (!xdr_rpc_gss_svc_t(xdrs, &objp->service)) return false; return true; } #endif /* _HAVE_GSSAPI */ static inline bool xdr_secinfo4(XDR * xdrs, secinfo4 *objp) { if (!inline_xdr_u_int32_t(xdrs, &objp->flavor)) return false; switch (objp->flavor) { #ifdef _HAVE_GSSAPI case RPCSEC_GSS: if (!xdr_rpcsec_gss_info (xdrs, &objp->secinfo4_u.flavor_info)) return false; break; default: #endif /* _HAVE_GSSAPI */ break; } return true; } static inline bool xdr_SECINFO4resok(XDR * xdrs, SECINFO4resok *objp) { if (!xdr_array (xdrs, (char **)&objp->SECINFO4resok_val, &objp->SECINFO4resok_len, XDR_ARRAY_MAXLEN, sizeof(secinfo4), (xdrproc_t) xdr_secinfo4)) return false; return true; } static inline bool xdr_SECINFO4res(XDR * xdrs, SECINFO4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_SECINFO4resok (xdrs, &objp->SECINFO4res_u.resok4)) return false; break; default: break; } return true; } static inline bool xdr_SETATTR4args(XDR * xdrs, SETATTR4args *objp) { if (!xdr_stateid4(xdrs, &objp->stateid)) return false; if (!xdr_fattr4(xdrs, &objp->obj_attributes)) return false; return true; } static inline bool xdr_SETATTR4res(XDR * xdrs, SETATTR4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; if (!xdr_bitmap4(xdrs, &objp->attrsset)) return false; return true; } static inline bool xdr_SETCLIENTID4args(XDR * xdrs, SETCLIENTID4args *objp) { if (!xdr_nfs_client_id4(xdrs, &objp->client)) return false; if (!xdr_cb_client4(xdrs, &objp->callback)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->callback_ident)) return false; return true; } static inline bool xdr_SETCLIENTID4resok(XDR * xdrs, SETCLIENTID4resok *objp) { if (!xdr_clientid4(xdrs, &objp->clientid)) return false; if (!xdr_verifier4(xdrs, objp->setclientid_confirm)) return false; return true; } static inline bool xdr_SETCLIENTID4res(XDR * xdrs, SETCLIENTID4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_SETCLIENTID4resok (xdrs, &objp->SETCLIENTID4res_u.resok4)) return false; break; case NFS4ERR_CLID_INUSE: if (!xdr_clientaddr4 (xdrs, &objp->SETCLIENTID4res_u.client_using)) return false; break; default: break; } return true; } static inline bool xdr_SETCLIENTID_CONFIRM4args(XDR * xdrs, SETCLIENTID_CONFIRM4args *objp) { if (!xdr_clientid4(xdrs, &objp->clientid)) return false; if (!xdr_verifier4(xdrs, objp->setclientid_confirm)) return false; return true; } static inline bool xdr_SETCLIENTID_CONFIRM4res(XDR * xdrs, SETCLIENTID_CONFIRM4res * objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; return true; } static inline bool xdr_VERIFY4args(XDR * xdrs, VERIFY4args *objp) { if (!xdr_fattr4(xdrs, &objp->obj_attributes)) return false; return true; } static inline bool xdr_VERIFY4res(XDR * xdrs, VERIFY4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; return true; } static inline bool xdr_stable_how4(XDR * xdrs, stable_how4 *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_WRITE4args(XDR * xdrs, WRITE4args *objp) { if (!xdr_stateid4(xdrs, &objp->stateid)) return false; if (!xdr_offset4(xdrs, &objp->offset)) return false; if (!xdr_stable_how4(xdrs, &objp->stable)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->data.data_val, &objp->data.data_len, XDR_BYTES_MAXLEN_IO)) return false; return true; } static inline bool xdr_WRITE4resok(XDR * xdrs, WRITE4resok *objp) { if (!xdr_count4(xdrs, &objp->count)) return false; if (!xdr_stable_how4(xdrs, &objp->committed)) return false; if (!xdr_verifier4(xdrs, objp->writeverf)) return false; return true; } static inline bool xdr_WRITE4res(XDR * xdrs, WRITE4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_WRITE4resok(xdrs, &objp->WRITE4res_u.resok4)) return false; break; default: break; } return true; } static inline bool xdr_RELEASE_LOCKOWNER4args(XDR * xdrs, RELEASE_LOCKOWNER4args * objp) { if (!xdr_lock_owner4(xdrs, &objp->lock_owner)) return false; return true; } static inline bool xdr_RELEASE_LOCKOWNER4res(XDR * xdrs, RELEASE_LOCKOWNER4res * objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; return true; } static inline bool xdr_ILLEGAL4res(XDR * xdrs, ILLEGAL4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; return true; } static inline bool xdr_gsshandle4_t(XDR * xdrs, gsshandle4_t *objp) { if (!inline_xdr_bytes (xdrs, (char **)&objp->gsshandle4_t_val, &objp->gsshandle4_t_len, XDR_BYTES_MAXLEN)) return false; return true; } #ifdef _HAVE_GSSAPI static inline bool xdr_gss_cb_handles4(XDR * xdrs, gss_cb_handles4 *objp) { if (!xdr_rpc_gss_svc_t(xdrs, &objp->gcbp_service)) return false; if (!xdr_gsshandle4_t(xdrs, &objp->gcbp_handle_from_server)) return false; if (!xdr_gsshandle4_t(xdrs, &objp->gcbp_handle_from_client)) return false; return true; } #endif /* _HAVE_GSSAPI */ static inline bool xdr_callback_sec_parms4(XDR * xdrs, callback_sec_parms4 *objp) { if (!inline_xdr_u_int32_t(xdrs, &objp->cb_secflavor)) return false; switch (objp->cb_secflavor) { case AUTH_NONE: break; case AUTH_SYS: if (!xdr_authunix_parms (xdrs, &objp->callback_sec_parms4_u.cbsp_sys_cred)) return false; break; #ifdef _HAVE_GSSAPI case RPCSEC_GSS: if (!xdr_gss_cb_handles4 (xdrs, &objp->callback_sec_parms4_u.cbsp_gss_handles)) return false; break; #endif /* _HAVE_GSSAPI */ default: return false; } return true; } static inline bool xdr_BACKCHANNEL_CTL4args(XDR * xdrs, BACKCHANNEL_CTL4args * objp) { if (!inline_xdr_u_int32_t(xdrs, &objp->bca_cb_program)) return false; if (!xdr_array (xdrs, (char **)&objp->bca_sec_parms.bca_sec_parms_val, &objp->bca_sec_parms.bca_sec_parms_len, XDR_ARRAY_MAXLEN, sizeof(callback_sec_parms4), (xdrproc_t) xdr_callback_sec_parms4)) return false; return true; } static inline bool xdr_BACKCHANNEL_CTL4res(XDR * xdrs, BACKCHANNEL_CTL4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->bcr_status)) return false; return true; } static inline bool xdr_channel_dir_from_client4( XDR * xdrs, channel_dir_from_client4 *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_BIND_CONN_TO_SESSION4args( XDR * xdrs, BIND_CONN_TO_SESSION4args *objp) { if (!xdr_sessionid4(xdrs, objp->bctsa_sessid)) return false; if (!xdr_channel_dir_from_client4(xdrs, &objp->bctsa_dir)) return false; if (!inline_xdr_bool(xdrs, &objp->bctsa_use_conn_in_rdma_mode)) return false; return true; } static inline bool xdr_channel_dir_from_server4(XDR * xdrs, channel_dir_from_server4 *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_BIND_CONN_TO_SESSION4resok( XDR * xdrs, BIND_CONN_TO_SESSION4resok *objp) { if (!xdr_sessionid4(xdrs, objp->bctsr_sessid)) return false; if (!xdr_channel_dir_from_server4(xdrs, &objp->bctsr_dir)) return false; if (!inline_xdr_bool(xdrs, &objp->bctsr_use_conn_in_rdma_mode)) return false; return true; } static inline bool xdr_BIND_CONN_TO_SESSION4res(XDR * xdrs, BIND_CONN_TO_SESSION4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->bctsr_status)) return false; switch (objp->bctsr_status) { case NFS4_OK: if (!xdr_BIND_CONN_TO_SESSION4resok (xdrs, &objp->BIND_CONN_TO_SESSION4res_u.bctsr_resok4)) return false; break; default: break; } return true; } static inline bool xdr_state_protect_ops4(XDR * xdrs, state_protect_ops4 *objp) { if (!xdr_bitmap4(xdrs, &objp->spo_must_enforce)) return false; if (!xdr_bitmap4(xdrs, &objp->spo_must_allow)) return false; return true; } static inline bool xdr_ssv_sp_parms4(XDR * xdrs, ssv_sp_parms4 *objp) { if (!xdr_state_protect_ops4(xdrs, &objp->ssp_ops)) return false; if (!xdr_array (xdrs, (char **)&objp->ssp_hash_algs.ssp_hash_algs_val, &objp->ssp_hash_algs.ssp_hash_algs_len, XDR_ARRAY_MAXLEN, sizeof(sec_oid4), (xdrproc_t) xdr_sec_oid4)) return false; if (!xdr_array (xdrs, (char **)&objp->ssp_encr_algs.ssp_encr_algs_val, &objp->ssp_encr_algs.ssp_encr_algs_len, XDR_ARRAY_MAXLEN, sizeof(sec_oid4), (xdrproc_t) xdr_sec_oid4)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->ssp_window)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->ssp_num_gss_handles)) return false; return true; } static inline bool xdr_state_protect_how4(XDR * xdrs, state_protect_how4 *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_state_protect4_a(XDR * xdrs, state_protect4_a *objp) { if (!xdr_state_protect_how4(xdrs, &objp->spa_how)) return false; switch (objp->spa_how) { case SP4_NONE: break; case SP4_MACH_CRED: if (!xdr_state_protect_ops4 (xdrs, &objp->state_protect4_a_u.spa_mach_ops)) return false; break; case SP4_SSV: if (!xdr_ssv_sp_parms4 (xdrs, &objp->state_protect4_a_u.spa_ssv_parms)) return false; break; default: return false; } return true; } static inline bool xdr_EXCHANGE_ID4args(XDR * xdrs, EXCHANGE_ID4args *objp) { if (!xdr_client_owner4(xdrs, &objp->eia_clientowner)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->eia_flags)) return false; if (!xdr_state_protect4_a(xdrs, &objp->eia_state_protect)) return false; if (!xdr_array (xdrs, (char **)&objp->eia_client_impl_id.eia_client_impl_id_val, (u_int *) &objp->eia_client_impl_id. eia_client_impl_id_len, 1, sizeof(nfs_impl_id4), (xdrproc_t) xdr_nfs_impl_id4)) return false; return true; } static inline bool xdr_ssv_prot_info4(XDR * xdrs, ssv_prot_info4 *objp) { if (!xdr_state_protect_ops4(xdrs, &objp->spi_ops)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->spi_hash_alg)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->spi_encr_alg)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->spi_ssv_len)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->spi_window)) return false; if (!xdr_array (xdrs, (char **)&objp->spi_handles.spi_handles_val, &objp->spi_handles.spi_handles_len, XDR_ARRAY_MAXLEN, sizeof(gsshandle4_t), (xdrproc_t) xdr_gsshandle4_t)) return false; return true; } static inline bool xdr_state_protect4_r(XDR * xdrs, state_protect4_r *objp) { if (!xdr_state_protect_how4(xdrs, &objp->spr_how)) return false; switch (objp->spr_how) { case SP4_NONE: break; case SP4_MACH_CRED: if (!xdr_state_protect_ops4 (xdrs, &objp->state_protect4_r_u.spr_mach_ops)) return false; break; case SP4_SSV: if (!xdr_ssv_prot_info4 (xdrs, &objp->state_protect4_r_u.spr_ssv_info)) return false; break; default: return false; } return true; } static inline bool xdr_EXCHANGE_ID4resok(XDR * xdrs, EXCHANGE_ID4resok *objp) { if (!xdr_clientid4(xdrs, &objp->eir_clientid)) return false; if (!xdr_sequenceid4(xdrs, &objp->eir_sequenceid)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->eir_flags)) return false; if (!xdr_state_protect4_r(xdrs, &objp->eir_state_protect)) return false; if (!xdr_server_owner4(xdrs, &objp->eir_server_owner)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->eir_server_scope.eir_server_scope_val, (u_int *) &objp->eir_server_scope.eir_server_scope_len, NFS4_OPAQUE_LIMIT)) return false; if (!xdr_array (xdrs, (char **)&objp->eir_server_impl_id.eir_server_impl_id_val, (u_int *) &objp->eir_server_impl_id. eir_server_impl_id_len, 1, sizeof(nfs_impl_id4), (xdrproc_t) xdr_nfs_impl_id4)) return false; return true; } static inline bool xdr_EXCHANGE_ID4res(XDR * xdrs, EXCHANGE_ID4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->eir_status)) return false; switch (objp->eir_status) { case NFS4_OK: if (!xdr_EXCHANGE_ID4resok (xdrs, &objp->EXCHANGE_ID4res_u.eir_resok4)) return false; break; default: break; } return true; } static inline bool xdr_channel_attrs4(XDR * xdrs, channel_attrs4 *objp) { if (!xdr_count4(xdrs, &objp->ca_headerpadsize)) return false; if (!xdr_count4(xdrs, &objp->ca_maxrequestsize)) return false; if (!xdr_count4(xdrs, &objp->ca_maxresponsesize)) return false; if (!xdr_count4(xdrs, &objp->ca_maxresponsesize_cached)) return false; if (!xdr_count4(xdrs, &objp->ca_maxoperations)) return false; if (!xdr_count4(xdrs, &objp->ca_maxrequests)) return false; if (!xdr_array (xdrs, (char **)&objp->ca_rdma_ird.ca_rdma_ird_val, (u_int *) &objp->ca_rdma_ird.ca_rdma_ird_len, 1, sizeof(uint32_t), (xdrproc_t) xdr_uint32_t)) return false; return true; } static inline bool xdr_CREATE_SESSION4args(XDR * xdrs, CREATE_SESSION4args *objp) { if (!xdr_clientid4(xdrs, &objp->csa_clientid)) return false; if (!xdr_sequenceid4(xdrs, &objp->csa_sequence)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->csa_flags)) return false; if (!xdr_channel_attrs4(xdrs, &objp->csa_fore_chan_attrs)) return false; if (!xdr_channel_attrs4(xdrs, &objp->csa_back_chan_attrs)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->csa_cb_program)) return false; if (!xdr_array (xdrs, (char **)&objp->csa_sec_parms.csa_sec_parms_val, &objp->csa_sec_parms.csa_sec_parms_len, XDR_ARRAY_MAXLEN, sizeof(callback_sec_parms4), (xdrproc_t) xdr_callback_sec_parms4)) return false; return true; } static inline bool xdr_CREATE_SESSION4resok(XDR * xdrs, CREATE_SESSION4resok * objp) { if (!xdr_sessionid4(xdrs, objp->csr_sessionid)) return false; if (!xdr_sequenceid4(xdrs, &objp->csr_sequence)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->csr_flags)) return false; if (!xdr_channel_attrs4(xdrs, &objp->csr_fore_chan_attrs)) return false; if (!xdr_channel_attrs4(xdrs, &objp->csr_back_chan_attrs)) return false; return true; } static inline bool xdr_CREATE_SESSION4res(XDR * xdrs, CREATE_SESSION4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->csr_status)) return false; switch (objp->csr_status) { case NFS4_OK: if (!xdr_CREATE_SESSION4resok (xdrs, &objp->CREATE_SESSION4res_u.csr_resok4)) return false; break; default: break; } return true; } static inline bool xdr_DESTROY_SESSION4args(XDR * xdrs, DESTROY_SESSION4args * objp) { if (!xdr_sessionid4(xdrs, objp->dsa_sessionid)) return false; return true; } static inline bool xdr_DESTROY_SESSION4res(XDR * xdrs, DESTROY_SESSION4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->dsr_status)) return false; return true; } static inline bool xdr_FREE_STATEID4args(XDR * xdrs, FREE_STATEID4args *objp) { if (!xdr_stateid4(xdrs, &objp->fsa_stateid)) return false; return true; } static inline bool xdr_FREE_STATEID4res(XDR * xdrs, FREE_STATEID4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->fsr_status)) return false; return true; } static inline bool xdr_attr_notice4(XDR * xdrs, attr_notice4 *objp) { if (!xdr_nfstime4(xdrs, objp)) return false; return true; } static inline bool xdr_GET_DIR_DELEGATION4args(XDR * xdrs, GET_DIR_DELEGATION4args * objp) { if (!inline_xdr_bool(xdrs, &objp->gdda_signal_deleg_avail)) return false; if (!xdr_bitmap4(xdrs, &objp->gdda_notification_types)) return false; if (!xdr_attr_notice4(xdrs, &objp->gdda_child_attr_delay)) return false; if (!xdr_attr_notice4(xdrs, &objp->gdda_dir_attr_delay)) return false; if (!xdr_bitmap4(xdrs, &objp->gdda_child_attributes)) return false; if (!xdr_bitmap4(xdrs, &objp->gdda_dir_attributes)) return false; return true; } static inline bool xdr_GET_DIR_DELEGATION4resok(XDR * xdrs, GET_DIR_DELEGATION4resok *objp) { if (!xdr_verifier4(xdrs, objp->gddr_cookieverf)) return false; if (!xdr_stateid4(xdrs, &objp->gddr_stateid)) return false; if (!xdr_bitmap4(xdrs, &objp->gddr_notification)) return false; if (!xdr_bitmap4(xdrs, &objp->gddr_child_attributes)) return false; if (!xdr_bitmap4(xdrs, &objp->gddr_dir_attributes)) return false; return true; } static inline bool xdr_gddrnf4_status(XDR * xdrs, gddrnf4_status *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_GET_DIR_DELEGATION4res_non_fatal( XDR * xdrs, GET_DIR_DELEGATION4res_non_fatal *objp) { if (!xdr_gddrnf4_status(xdrs, &objp->gddrnf_status)) return false; switch (objp->gddrnf_status) { case GDD4_OK: if (!xdr_GET_DIR_DELEGATION4resok (xdrs, &objp->GET_DIR_DELEGATION4res_non_fatal_u. gddrnf_resok4)) return false; break; case GDD4_UNAVAIL: if (!inline_xdr_bool (xdrs, &objp->GET_DIR_DELEGATION4res_non_fatal_u. gddrnf_will_signal_deleg_avail)) return false; break; default: return false; } return true; } static inline bool xdr_GET_DIR_DELEGATION4res( XDR * xdrs, GET_DIR_DELEGATION4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->gddr_status)) return false; switch (objp->gddr_status) { case NFS4_OK: if (!xdr_GET_DIR_DELEGATION4res_non_fatal (xdrs, &objp->GET_DIR_DELEGATION4res_u. gddr_res_non_fatal4)) return false; break; default: break; } return true; } static inline bool xdr_GETDEVICEINFO4args(XDR * xdrs, GETDEVICEINFO4args *objp) { if (!xdr_deviceid4(xdrs, objp->gdia_device_id)) return false; if (!xdr_layouttype4(xdrs, &objp->gdia_layout_type)) return false; if (!xdr_count4(xdrs, &objp->gdia_maxcount)) return false; if (!xdr_bitmap4(xdrs, &objp->gdia_notify_types)) return false; return true; } static inline bool xdr_GETDEVICEINFO4resok(XDR * xdrs, GETDEVICEINFO4resok *objp) { if (!xdr_device_addr4(xdrs, &objp->gdir_device_addr)) return false; if (!xdr_bitmap4(xdrs, &objp->gdir_notification)) return false; return true; } static inline bool xdr_GETDEVICEINFO4res(XDR * xdrs, GETDEVICEINFO4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->gdir_status)) return false; switch (objp->gdir_status) { case NFS4_OK: if (!xdr_GETDEVICEINFO4resok (xdrs, &objp->GETDEVICEINFO4res_u.gdir_resok4)) return false; break; case NFS4ERR_TOOSMALL: if (!xdr_count4 (xdrs, &objp->GETDEVICEINFO4res_u.gdir_mincount)) return false; break; default: break; } return true; } static inline bool xdr_GETDEVICELIST4args(XDR * xdrs, GETDEVICELIST4args *objp) { if (!xdr_layouttype4(xdrs, &objp->gdla_layout_type)) return false; if (!xdr_count4(xdrs, &objp->gdla_maxdevices)) return false; if (!xdr_nfs_cookie4(xdrs, &objp->gdla_cookie)) return false; if (!xdr_verifier4(xdrs, objp->gdla_cookieverf)) return false; return true; } static inline bool xdr_GETDEVICELIST4resok(XDR * xdrs, GETDEVICELIST4resok *objp) { if (!xdr_nfs_cookie4(xdrs, &objp->gdlr_cookie)) return false; if (!xdr_verifier4(xdrs, objp->gdlr_cookieverf)) return false; if (!xdr_array( xdrs, (char **)&objp->gdlr_deviceid_list.gdlr_deviceid_list_val, &objp->gdlr_deviceid_list.gdlr_deviceid_list_len, XDR_ARRAY_MAXLEN, sizeof(deviceid4), (xdrproc_t) xdr_deviceid4)) return false; if (!inline_xdr_bool(xdrs, &objp->gdlr_eof)) return false; return true; } static inline bool xdr_GETDEVICELIST4res(XDR * xdrs, GETDEVICELIST4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->gdlr_status)) return false; switch (objp->gdlr_status) { case NFS4_OK: if (!xdr_GETDEVICELIST4resok (xdrs, &objp->GETDEVICELIST4res_u.gdlr_resok4)) return false; break; default: break; } return true; } static inline bool xdr_newtime4(XDR * xdrs, newtime4 *objp) { if (!inline_xdr_bool(xdrs, &objp->nt_timechanged)) return false; switch (objp->nt_timechanged) { case true: if (!xdr_nfstime4(xdrs, &objp->newtime4_u.nt_time)) return false; break; case false: break; default: return false; } return true; } static inline bool xdr_newoffset4(XDR * xdrs, newoffset4 *objp) { if (!inline_xdr_bool(xdrs, &objp->no_newoffset)) return false; switch (objp->no_newoffset) { case true: if (!xdr_offset4(xdrs, &objp->newoffset4_u.no_offset)) return false; break; case false: break; default: return false; } return true; } static inline bool xdr_LAYOUTCOMMIT4args(XDR * xdrs, LAYOUTCOMMIT4args *objp) { if (!xdr_offset4(xdrs, &objp->loca_offset)) return false; if (!xdr_length4(xdrs, &objp->loca_length)) return false; if (!inline_xdr_bool(xdrs, &objp->loca_reclaim)) return false; if (!xdr_stateid4(xdrs, &objp->loca_stateid)) return false; if (!xdr_newoffset4(xdrs, &objp->loca_last_write_offset)) return false; if (!xdr_newtime4(xdrs, &objp->loca_time_modify)) return false; if (!xdr_layoutupdate4(xdrs, &objp->loca_layoutupdate)) return false; return true; } static inline bool xdr_newsize4(XDR * xdrs, newsize4 *objp) { if (!inline_xdr_bool(xdrs, &objp->ns_sizechanged)) return false; switch (objp->ns_sizechanged) { case true: if (!xdr_length4(xdrs, &objp->newsize4_u.ns_size)) return false; break; case false: break; default: return false; } return true; } static inline bool xdr_LAYOUTCOMMIT4resok(XDR * xdrs, LAYOUTCOMMIT4resok *objp) { if (!xdr_newsize4(xdrs, &objp->locr_newsize)) return false; return true; } static inline bool xdr_LAYOUTCOMMIT4res(XDR * xdrs, LAYOUTCOMMIT4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->locr_status)) return false; switch (objp->locr_status) { case NFS4_OK: if (!xdr_LAYOUTCOMMIT4resok (xdrs, &objp->LAYOUTCOMMIT4res_u.locr_resok4)) return false; break; default: break; } return true; } static inline bool xdr_LAYOUTGET4args(XDR * xdrs, LAYOUTGET4args *objp) { if (!inline_xdr_bool(xdrs, &objp->loga_signal_layout_avail)) return false; if (!xdr_layouttype4(xdrs, &objp->loga_layout_type)) return false; if (!xdr_layoutiomode4(xdrs, &objp->loga_iomode)) return false; if (!xdr_offset4(xdrs, &objp->loga_offset)) return false; if (!xdr_length4(xdrs, &objp->loga_length)) return false; if (!xdr_length4(xdrs, &objp->loga_minlength)) return false; if (!xdr_stateid4(xdrs, &objp->loga_stateid)) return false; if (!xdr_count4(xdrs, &objp->loga_maxcount)) return false; return true; } static inline bool xdr_LAYOUTGET4resok(XDR * xdrs, LAYOUTGET4resok *objp) { if (!inline_xdr_bool(xdrs, &objp->logr_return_on_close)) return false; if (!xdr_stateid4(xdrs, &objp->logr_stateid)) return false; if (!xdr_array (xdrs, (char **)&objp->logr_layout.logr_layout_val, &objp->logr_layout.logr_layout_len, XDR_ARRAY_MAXLEN, sizeof(layout4), (xdrproc_t) xdr_layout4)) return false; return true; } static inline bool xdr_LAYOUTGET4res(XDR * xdrs, LAYOUTGET4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->logr_status)) return false; switch (objp->logr_status) { case NFS4_OK: if (!xdr_LAYOUTGET4resok (xdrs, &objp->LAYOUTGET4res_u.logr_resok4)) return false; break; case NFS4ERR_LAYOUTTRYLATER: if (!inline_xdr_bool (xdrs, &objp->LAYOUTGET4res_u. logr_will_signal_layout_avail)) return false; break; default: break; } return true; } static inline bool xdr_LAYOUTRETURN4args(XDR * xdrs, LAYOUTRETURN4args *objp) { if (!inline_xdr_bool(xdrs, &objp->lora_reclaim)) return false; if (!xdr_layouttype4(xdrs, &objp->lora_layout_type)) return false; if (!xdr_layoutiomode4(xdrs, &objp->lora_iomode)) return false; if (!xdr_layoutreturn4(xdrs, &objp->lora_layoutreturn)) return false; return true; } static inline bool xdr_layoutreturn_stateid(XDR * xdrs, layoutreturn_stateid * objp) { if (!inline_xdr_bool(xdrs, &objp->lrs_present)) return false; switch (objp->lrs_present) { case true: if (!xdr_stateid4 (xdrs, &objp->layoutreturn_stateid_u.lrs_stateid)) return false; break; case false: break; default: return false; } return true; } static inline bool xdr_LAYOUTRETURN4res(XDR * xdrs, LAYOUTRETURN4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->lorr_status)) return false; switch (objp->lorr_status) { case NFS4_OK: if (!xdr_layoutreturn_stateid (xdrs, &objp->LAYOUTRETURN4res_u.lorr_stateid)) return false; break; default: break; } return true; } static inline bool xdr_secinfo_style4(XDR * xdrs, secinfo_style4 *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_SECINFO_NO_NAME4args(XDR * xdrs, SECINFO_NO_NAME4args * objp) { if (!xdr_secinfo_style4(xdrs, objp)) return false; return true; } static inline bool xdr_SECINFO_NO_NAME4res(XDR * xdrs, SECINFO_NO_NAME4res *objp) { if (!xdr_SECINFO4res(xdrs, objp)) return false; return true; } static inline bool xdr_SEQUENCE4args(XDR * xdrs, SEQUENCE4args *objp) { if (!xdr_sessionid4(xdrs, objp->sa_sessionid)) return false; if (!xdr_sequenceid4(xdrs, &objp->sa_sequenceid)) return false; if (!xdr_slotid4(xdrs, &objp->sa_slotid)) return false; if (!xdr_slotid4(xdrs, &objp->sa_highest_slotid)) return false; if (!inline_xdr_bool(xdrs, &objp->sa_cachethis)) return false; return true; } static inline bool xdr_SEQUENCE4resok(XDR * xdrs, SEQUENCE4resok *objp) { if (!xdr_sessionid4(xdrs, objp->sr_sessionid)) return false; if (!xdr_sequenceid4(xdrs, &objp->sr_sequenceid)) return false; if (!xdr_slotid4(xdrs, &objp->sr_slotid)) return false; if (!xdr_slotid4(xdrs, &objp->sr_highest_slotid)) return false; if (!xdr_slotid4(xdrs, &objp->sr_target_highest_slotid)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->sr_status_flags)) return false; return true; } static inline bool xdr_SEQUENCE4res(XDR * xdrs, SEQUENCE4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->sr_status)) return false; switch (objp->sr_status) { case NFS4_OK: if (!xdr_SEQUENCE4resok (xdrs, &objp->SEQUENCE4res_u.sr_resok4)) return false; break; default: break; } return true; } static inline bool xdr_ssa_digest_input4(XDR * xdrs, ssa_digest_input4 *objp) { if (!xdr_SEQUENCE4args(xdrs, &objp->sdi_seqargs)) return false; return true; } static inline bool xdr_SET_SSV4args(XDR * xdrs, SET_SSV4args *objp) { if (!inline_xdr_bytes (xdrs, (char **)&objp->ssa_ssv.ssa_ssv_val, &objp->ssa_ssv.ssa_ssv_len, XDR_BYTES_MAXLEN)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->ssa_digest.ssa_digest_val, &objp->ssa_digest.ssa_digest_len, XDR_BYTES_MAXLEN)) return false; return true; } static inline bool xdr_ssr_digest_input4(XDR * xdrs, ssr_digest_input4 *objp) { if (!xdr_SEQUENCE4res(xdrs, &objp->sdi_seqres)) return false; return true; } static inline bool xdr_SET_SSV4resok(XDR * xdrs, SET_SSV4resok *objp) { if (!inline_xdr_bytes (xdrs, (char **)&objp->ssr_digest.ssr_digest_val, &objp->ssr_digest.ssr_digest_len, XDR_BYTES_MAXLEN)) return false; return true; } static inline bool xdr_SET_SSV4res(XDR * xdrs, SET_SSV4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->ssr_status)) return false; switch (objp->ssr_status) { case NFS4_OK: if (!xdr_SET_SSV4resok (xdrs, &objp->SET_SSV4res_u.ssr_resok4)) return false; break; default: break; } return true; } static inline bool xdr_TEST_STATEID4args(XDR * xdrs, TEST_STATEID4args *objp) { if (!xdr_array (xdrs, (char **)&objp->ts_stateids.ts_stateids_val, &objp->ts_stateids.ts_stateids_len, XDR_ARRAY_MAXLEN, sizeof(stateid4), (xdrproc_t) xdr_stateid4)) return false; return true; } static inline bool xdr_TEST_STATEID4resok(XDR * xdrs, TEST_STATEID4resok *objp) { if (!xdr_array (xdrs, (char **)&objp->tsr_status_codes.tsr_status_codes_val, &objp->tsr_status_codes.tsr_status_codes_len, XDR_ARRAY_MAXLEN, sizeof(nfsstat4), (xdrproc_t) xdr_nfsstat4)) return false; return true; } static inline bool xdr_TEST_STATEID4res(XDR * xdrs, TEST_STATEID4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->tsr_status)) return false; switch (objp->tsr_status) { case NFS4_OK: if (!xdr_TEST_STATEID4resok (xdrs, &objp->TEST_STATEID4res_u.tsr_resok4)) return false; break; default: break; } return true; } static inline bool xdr_deleg_claim4(XDR * xdrs, deleg_claim4 *objp) { if (!xdr_open_claim_type4(xdrs, &objp->dc_claim)) return false; switch (objp->dc_claim) { case CLAIM_FH: break; case CLAIM_DELEG_PREV_FH: break; case CLAIM_PREVIOUS: if (!xdr_open_delegation_type4 (xdrs, &objp->deleg_claim4_u.dc_delegate_type)) return false; break; default: return false; } return true; } static inline bool xdr_WANT_DELEGATION4args(XDR * xdrs, WANT_DELEGATION4args * objp) { if (!inline_xdr_u_int32_t(xdrs, &objp->wda_want)) return false; if (!xdr_deleg_claim4(xdrs, &objp->wda_claim)) return false; return true; } static inline bool xdr_WANT_DELEGATION4res(XDR * xdrs, WANT_DELEGATION4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->wdr_status)) return false; switch (objp->wdr_status) { case NFS4_OK: if (!xdr_open_delegation4 (xdrs, &objp->WANT_DELEGATION4res_u.wdr_resok4)) return false; break; default: break; } return true; } static inline bool xdr_DESTROY_CLIENTID4args(XDR * xdrs, DESTROY_CLIENTID4args * objp) { if (!xdr_clientid4(xdrs, &objp->dca_clientid)) return false; return true; } static inline bool xdr_DESTROY_CLIENTID4res(XDR * xdrs, DESTROY_CLIENTID4res * objp) { if (!xdr_nfsstat4(xdrs, &objp->dcr_status)) return false; return true; } static inline bool xdr_RECLAIM_COMPLETE4args(XDR * xdrs, RECLAIM_COMPLETE4args * objp) { if (!inline_xdr_bool(xdrs, &objp->rca_one_fs)) return false; return true; } static inline bool xdr_RECLAIM_COMPLETE4res(XDR * xdrs, RECLAIM_COMPLETE4res * objp) { if (!xdr_nfsstat4(xdrs, &objp->rcr_status)) return false; return true; } /* NFSv4.2 */ static inline bool xdr_WRITE_SAME4args(XDR * xdrs, WRITE_SAME4args *objp) { if (!xdr_stateid4(xdrs, &objp->wp_stateid)) return false; if (!xdr_stable_how4(xdrs, &objp->wp_stable)) return false; if (!xdr_offset4(xdrs, &objp->wp_adb.adb_offset)) return false; if (!xdr_length4(xdrs, &objp->wp_adb.adb_block_size)) return false; if (!xdr_length4(xdrs, &objp->wp_adb.adb_block_count)) return false; if (!xdr_length4(xdrs, &objp->wp_adb.adb_reloff_blocknum)) return false; if (!xdr_count4(xdrs, &objp->wp_adb.adb_block_num)) return false; if (!xdr_length4(xdrs, &objp->wp_adb.adb_reloff_pattern)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->wp_adb.adb_data.data_val, &objp->wp_adb.adb_data.data_len, XDR_BYTES_MAXLEN)) return false; return true; } static inline bool xdr_WRITE_SAME4resok(XDR * xdrs, write_response4 *objp) { if (!xdr_count4(xdrs, &objp->wr_ids)) return false; if (objp->wr_ids > 1) return false; if (objp->wr_ids == 1) if (!xdr_stateid4(xdrs, &objp->wr_callback_id)) return false; if (!xdr_length4(xdrs, &objp->wr_count)) return false; if (!xdr_stable_how4(xdrs, &objp->wr_committed)) return false; if (!xdr_verifier4(xdrs, objp->wr_writeverf)) return false; return true; } static inline bool xdr_READ_PLUS4args(XDR * xdrs, READ_PLUS4args *objp) { if (!xdr_stateid4(xdrs, &objp->rpa_stateid)) return false; if (!xdr_offset4(xdrs, &objp->rpa_offset)) return false; if (!xdr_count4(xdrs, &objp->rpa_count)) return false; return true; } static inline bool xdr_READ_PLUS4resok(XDR * xdrs, read_plus_res4 *objp) { if (!inline_xdr_bool(xdrs, &objp->rpr_eof)) return false; if (objp->rpr_contents_count != 1) /* an array of 1 for now */ return false; if (!xdr_count4(xdrs, &objp->rpr_contents_count)) return false; if (!inline_xdr_enum(xdrs, (enum_t *)&objp->rpr_contents.what)) return false; if (objp->rpr_contents.what == NFS4_CONTENT_DATA) { if (!xdr_offset4(xdrs, &objp->rpr_contents.data.d_offset)) return false; if (!inline_xdr_bytes (xdrs, (char **)&objp->rpr_contents.data.d_data.data_val, &objp->rpr_contents.data.d_data.data_len, XDR_BYTES_MAXLEN_IO)) return false; return true; } if (objp->rpr_contents.what == NFS4_CONTENT_HOLE) { if (!xdr_offset4(xdrs, &objp->rpr_contents.hole.di_offset)) return false; if (!xdr_length4(xdrs, &objp->rpr_contents.hole.di_length)) return false; return true; } else return false; } static inline bool xdr_READ_PLUS4res(XDR * xdrs, READ_PLUS4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->rpr_status)) return false; switch (objp->rpr_status) { case NFS4_OK: if (!xdr_READ_PLUS4resok(xdrs, &objp->rpr_resok4)) return false; break; default: break; } return true; } static inline bool xdr_WRITE_SAME4res(XDR * xdrs, WRITE_SAME4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->wpr_status)) return false; switch (objp->wpr_status) { case NFS4_OK: if (!xdr_WRITE_SAME4resok(xdrs, &objp->wpr_resok4)) return false; break; default: break; } return true; } static inline bool xdr_SEEK4args(XDR * xdrs, SEEK4args *objp) { if (!xdr_stateid4(xdrs, &objp->sa_stateid)) return false; if (!xdr_offset4(xdrs, &objp->sa_offset)) return false; if (!inline_xdr_enum(xdrs, (enum_t *)&objp->sa_what)) return false; return true; } static inline bool xdr_ALLOCATE4args(XDR * xdrs, ALLOCATE4args *objp) { if (!xdr_stateid4(xdrs, &objp->aa_stateid)) return false; if (!xdr_offset4(xdrs, &objp->aa_offset)) return false; if (!xdr_length4(xdrs, &objp->aa_length)) return false; return true; } static inline bool xdr_DEALLOCATE4args(XDR * xdrs, DEALLOCATE4args *objp) { if (!xdr_stateid4(xdrs, &objp->da_stateid)) return false; if (!xdr_offset4(xdrs, &objp->da_offset)) return false; if (!xdr_length4(xdrs, &objp->da_length)) return false; return true; } static inline bool xdr_data_contents(XDR * xdrs, contents *objp) { if (!inline_xdr_enum(xdrs, (enum_t *)&objp->what)) return false; if (objp->what == NFS4_CONTENT_DATA) { if (!xdr_offset4(xdrs, &objp->hole.di_offset)) return false; if (!xdr_length4(xdrs, &objp->hole.di_length)) return false; return true; } if (objp->what == NFS4_CONTENT_HOLE) { if (!xdr_offset4(xdrs, &objp->hole.di_offset)) return false; if (!xdr_length4(xdrs, &objp->hole.di_length)) return false; return true; } else return false; } static inline bool xdr_SEEK4resok(XDR * xdrs, seek_res4 *objp) { if (!inline_xdr_bool(xdrs, &objp->sr_eof)) return false; if (!xdr_offset4(xdrs, &objp->sr_offset)) return false; return true; } static inline bool xdr_SEEK4res(XDR * xdrs, SEEK4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->sr_status)) return false; switch (objp->sr_status) { case NFS4_OK: if (!xdr_SEEK4resok(xdrs, &objp->sr_resok4)) return false; break; default: break; } return true; } static inline bool xdr_ALLOCATE4res(XDR * xdrs, ALLOCATE4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->ar_status)) return false; return true; } static inline bool xdr_DEALLOCATE4res(XDR * xdrs, DEALLOCATE4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->dr_status)) return false; return true; } static inline bool xdr_IO_ADVISE4args(XDR * xdrs, IO_ADVISE4args *objp) { if (!xdr_stateid4(xdrs, &objp->iaa_stateid)) return false; if (!xdr_offset4(xdrs, &objp->iaa_offset)) return false; if (!xdr_length4(xdrs, &objp->iaa_count)) return false; if (!xdr_bitmap4(xdrs, &objp->iaa_hints)) return false; return true; } static inline bool xdr_IO_ADVISE4res(XDR * xdrs, IO_ADVISE4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->iaa_status)) return false; switch (objp->iaa_status) { case NFS4_OK: if (!xdr_bitmap4(xdrs, &objp->iaa_hints)) return false; break; default: break; } return true; } static inline bool xdr_LAYOUTERROR4args(XDR * xdrs, LAYOUTERROR4args *objp) { if (!xdr_offset4(xdrs, &objp->lea_offset)) return false; if (!xdr_length4(xdrs, &objp->lea_length)) return false; if (!xdr_stateid4(xdrs, &objp->lea_stateid)) return false; if (!xdr_deviceid4(xdrs, objp->lea_errors.de_deviceid)) return false; if (!xdr_nfsstat4(xdrs, &objp->lea_errors.de_status)) return false; if (!inline_xdr_enum(xdrs, (enum_t *)&objp->lea_errors.de_opnum)) return false; return true; } static inline bool xdr_LAYOUTERROR4res(XDR * xdrs, LAYOUTERROR4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->ler_status)) return false; return true; } static inline bool xdr_LAYOUTSTATS4args(XDR * xdrs, LAYOUTSTATS4args *objp) { if (!xdr_offset4(xdrs, &objp->lsa_offset)) return false; if (!xdr_length4(xdrs, &objp->lsa_length)) return false; if (!xdr_stateid4(xdrs, &objp->lsa_stateid)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->lsa_read.ii_count)) return false; if (!inline_xdr_u_int64_t(xdrs, &objp->lsa_read.ii_bytes)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->lsa_write.ii_count)) return false; if (!inline_xdr_u_int64_t(xdrs, &objp->lsa_write.ii_bytes)) return false; if (!xdr_layoutupdate4(xdrs, &objp->lsa_layoutupdate)) return false; return true; } static inline bool xdr_LAYOUTSTATS4res(XDR * xdrs, LAYOUTSTATS4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->lsr_status)) return false; return true; } /* new operations for NFSv4.1 */ static inline bool xdr_nfs_opnum4(XDR * xdrs, nfs_opnum4 *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_nfs_argop4(XDR * xdrs, nfs_argop4 *objp) { struct nfs_request_lookahead slhd = { .flags = 0, .read = 0, .write = 0 }; struct nfs_request_lookahead *lkhd = xdrs->x_public ? (struct nfs_request_lookahead *)xdrs-> x_public : &slhd; if (!xdr_nfs_opnum4(xdrs, &objp->argop)) return false; switch (objp->argop) { case NFS4_OP_ACCESS: if (!xdr_ACCESS4args (xdrs, &objp->nfs_argop4_u.opaccess)) return false; break; case NFS4_OP_CLOSE: if (!xdr_CLOSE4args(xdrs, &objp->nfs_argop4_u.opclose)) return false; lkhd->flags |= NFS_LOOKAHEAD_CLOSE; break; case NFS4_OP_COMMIT: if (!xdr_COMMIT4args (xdrs, &objp->nfs_argop4_u.opcommit)) return false; lkhd->flags |= NFS_LOOKAHEAD_COMMIT; break; case NFS4_OP_CREATE: if (!xdr_CREATE4args (xdrs, &objp->nfs_argop4_u.opcreate)) return false; lkhd->flags |= NFS_LOOKAHEAD_CREATE; break; case NFS4_OP_DELEGPURGE: if (!xdr_DELEGPURGE4args (xdrs, &objp->nfs_argop4_u.opdelegpurge)) return false; break; case NFS4_OP_DELEGRETURN: if (!xdr_DELEGRETURN4args (xdrs, &objp->nfs_argop4_u.opdelegreturn)) return false; break; case NFS4_OP_GETATTR: if (!xdr_GETATTR4args (xdrs, &objp->nfs_argop4_u.opgetattr)) return false; break; case NFS4_OP_GETFH: break; case NFS4_OP_LINK: if (!xdr_LINK4args(xdrs, &objp->nfs_argop4_u.oplink)) return false; break; case NFS4_OP_LOCK: if (!xdr_LOCK4args(xdrs, &objp->nfs_argop4_u.oplock)) return false; lkhd->flags |= NFS_LOOKAHEAD_LOCK; break; case NFS4_OP_LOCKT: if (!xdr_LOCKT4args(xdrs, &objp->nfs_argop4_u.oplockt)) return false; break; case NFS4_OP_LOCKU: if (!xdr_LOCKU4args(xdrs, &objp->nfs_argop4_u.oplocku)) return false; lkhd->flags |= NFS_LOOKAHEAD_LOCK; break; case NFS4_OP_LOOKUP: if (!xdr_LOOKUP4args (xdrs, &objp->nfs_argop4_u.oplookup)) return false; lkhd->flags |= NFS_LOOKAHEAD_LOOKUP; break; case NFS4_OP_LOOKUPP: lkhd->flags |= NFS_LOOKAHEAD_LOOKUP; break; case NFS4_OP_NVERIFY: if (!xdr_NVERIFY4args (xdrs, &objp->nfs_argop4_u.opnverify)) return false; break; case NFS4_OP_OPEN: if (!xdr_OPEN4args(xdrs, &objp->nfs_argop4_u.opopen)) return false; lkhd->flags |= NFS_LOOKAHEAD_OPEN; if (objp->nfs_argop4_u.opopen.openhow.opentype == OPEN4_CREATE) lkhd->flags |= NFS_LOOKAHEAD_CREATE; break; case NFS4_OP_OPENATTR: if (!xdr_OPENATTR4args (xdrs, &objp->nfs_argop4_u.opopenattr)) return false; break; case NFS4_OP_OPEN_CONFIRM: if (!xdr_OPEN_CONFIRM4args (xdrs, &objp->nfs_argop4_u.opopen_confirm)) return false; lkhd->flags |= NFS_LOOKAHEAD_OPEN; break; case NFS4_OP_OPEN_DOWNGRADE: if (!xdr_OPEN_DOWNGRADE4args (xdrs, &objp->nfs_argop4_u.opopen_downgrade)) return false; lkhd->flags |= NFS_LOOKAHEAD_OPEN; break; case NFS4_OP_PUTFH: if (!xdr_PUTFH4args(xdrs, &objp->nfs_argop4_u.opputfh)) return false; break; case NFS4_OP_PUTPUBFH: break; case NFS4_OP_PUTROOTFH: break; case NFS4_OP_READ: if (!xdr_READ4args(xdrs, &objp->nfs_argop4_u.opread)) return false; lkhd->flags |= NFS_LOOKAHEAD_READ; (lkhd->read)++; break; case NFS4_OP_READDIR: if (!xdr_READDIR4args (xdrs, &objp->nfs_argop4_u.opreaddir)) return false; lkhd->flags |= NFS_LOOKAHEAD_READDIR; break; case NFS4_OP_READLINK: lkhd->flags |= NFS_LOOKAHEAD_READLINK; break; case NFS4_OP_REMOVE: if (!xdr_REMOVE4args (xdrs, &objp->nfs_argop4_u.opremove)) return false; lkhd->flags |= NFS_LOOKAHEAD_REMOVE; break; case NFS4_OP_RENAME: if (!xdr_RENAME4args (xdrs, &objp->nfs_argop4_u.oprename)) return false; lkhd->flags |= NFS_LOOKAHEAD_RENAME; break; case NFS4_OP_RENEW: if (!xdr_RENEW4args(xdrs, &objp->nfs_argop4_u.oprenew)) return false; break; case NFS4_OP_RESTOREFH: break; case NFS4_OP_SAVEFH: break; case NFS4_OP_SECINFO: if (!xdr_SECINFO4args (xdrs, &objp->nfs_argop4_u.opsecinfo)) return false; break; case NFS4_OP_SETATTR: if (!xdr_SETATTR4args (xdrs, &objp->nfs_argop4_u.opsetattr)) return false; lkhd->flags |= NFS_LOOKAHEAD_SETATTR; break; case NFS4_OP_SETCLIENTID: if (!xdr_SETCLIENTID4args (xdrs, &objp->nfs_argop4_u.opsetclientid)) return false; lkhd->flags |= NFS_LOOKAHEAD_SETCLIENTID; break; case NFS4_OP_SETCLIENTID_CONFIRM: if (!xdr_SETCLIENTID_CONFIRM4args (xdrs, &objp->nfs_argop4_u.opsetclientid_confirm)) return false; lkhd->flags |= NFS_LOOKAHEAD_SETCLIENTID_CONFIRM; break; case NFS4_OP_VERIFY: if (!xdr_VERIFY4args (xdrs, &objp->nfs_argop4_u.opverify)) return false; break; case NFS4_OP_WRITE: if (!xdr_WRITE4args(xdrs, &objp->nfs_argop4_u.opwrite)) return false; lkhd->flags |= NFS_LOOKAHEAD_WRITE; (lkhd->write)++; break; case NFS4_OP_RELEASE_LOCKOWNER: if (!xdr_RELEASE_LOCKOWNER4args (xdrs, &objp->nfs_argop4_u.oprelease_lockowner)) return false; break; case NFS4_OP_BACKCHANNEL_CTL: if (!xdr_BACKCHANNEL_CTL4args (xdrs, &objp->nfs_argop4_u.opbackchannel_ctl)) return false; break; case NFS4_OP_BIND_CONN_TO_SESSION: if (!xdr_BIND_CONN_TO_SESSION4args (xdrs, &objp->nfs_argop4_u.opbind_conn_to_session)) return false; break; case NFS4_OP_EXCHANGE_ID: if (!xdr_EXCHANGE_ID4args (xdrs, &objp->nfs_argop4_u.opexchange_id)) return false; break; case NFS4_OP_CREATE_SESSION: if (!xdr_CREATE_SESSION4args (xdrs, &objp->nfs_argop4_u.opcreate_session)) return false; break; case NFS4_OP_DESTROY_SESSION: if (!xdr_DESTROY_SESSION4args (xdrs, &objp->nfs_argop4_u.opdestroy_session)) return false; break; case NFS4_OP_FREE_STATEID: if (!xdr_FREE_STATEID4args (xdrs, &objp->nfs_argop4_u.opfree_stateid)) return false; break; case NFS4_OP_GET_DIR_DELEGATION: if (!xdr_GET_DIR_DELEGATION4args (xdrs, &objp->nfs_argop4_u.opget_dir_delegation)) return false; break; case NFS4_OP_GETDEVICEINFO: if (!xdr_GETDEVICEINFO4args (xdrs, &objp->nfs_argop4_u.opgetdeviceinfo)) return false; break; case NFS4_OP_GETDEVICELIST: if (!xdr_GETDEVICELIST4args (xdrs, &objp->nfs_argop4_u.opgetdevicelist)) return false; break; case NFS4_OP_LAYOUTCOMMIT: if (!xdr_LAYOUTCOMMIT4args (xdrs, &objp->nfs_argop4_u.oplayoutcommit)) return false; lkhd->flags |= NFS_LOOKAHEAD_LAYOUTCOMMIT; break; case NFS4_OP_LAYOUTGET: if (!xdr_LAYOUTGET4args (xdrs, &objp->nfs_argop4_u.oplayoutget)) return false; break; case NFS4_OP_LAYOUTRETURN: if (!xdr_LAYOUTRETURN4args (xdrs, &objp->nfs_argop4_u.oplayoutreturn)) return false; break; case NFS4_OP_SECINFO_NO_NAME: if (!xdr_SECINFO_NO_NAME4args (xdrs, &objp->nfs_argop4_u.opsecinfo_no_name)) return false; break; case NFS4_OP_SEQUENCE: if (!xdr_SEQUENCE4args (xdrs, &objp->nfs_argop4_u.opsequence)) return false; break; case NFS4_OP_SET_SSV: if (!xdr_SET_SSV4args (xdrs, &objp->nfs_argop4_u.opset_ssv)) return false; break; case NFS4_OP_TEST_STATEID: if (!xdr_TEST_STATEID4args (xdrs, &objp->nfs_argop4_u.optest_stateid)) return false; break; case NFS4_OP_WANT_DELEGATION: if (!xdr_WANT_DELEGATION4args (xdrs, &objp->nfs_argop4_u.opwant_delegation)) return false; break; case NFS4_OP_DESTROY_CLIENTID: if (!xdr_DESTROY_CLIENTID4args (xdrs, &objp->nfs_argop4_u.opdestroy_clientid)) return false; break; case NFS4_OP_RECLAIM_COMPLETE: if (!xdr_RECLAIM_COMPLETE4args (xdrs, &objp->nfs_argop4_u.opreclaim_complete)) return false; break; /* NFSv4.2 */ case NFS4_OP_WRITE_SAME: if (!xdr_WRITE_SAME4args(xdrs, &objp->nfs_argop4_u.opwrite_plus)) return false; lkhd->flags |= NFS_LOOKAHEAD_WRITE; (lkhd->write)++; break; case NFS4_OP_READ_PLUS: if (!xdr_READ_PLUS4args(xdrs, &objp->nfs_argop4_u.opread_plus)) return false; lkhd->flags |= NFS_LOOKAHEAD_READ; (lkhd->read)++; break; case NFS4_OP_SEEK: if (!xdr_SEEK4args(xdrs, &objp->nfs_argop4_u.opseek)) return false; break; case NFS4_OP_ALLOCATE: if (!xdr_ALLOCATE4args(xdrs, &objp->nfs_argop4_u.opallocate)) return false; break; case NFS4_OP_DEALLOCATE: if (!xdr_DEALLOCATE4args(xdrs, &objp->nfs_argop4_u.opdeallocate)) return false; break; case NFS4_OP_IO_ADVISE: if (!xdr_IO_ADVISE4args(xdrs, &objp->nfs_argop4_u.opio_advise)) return false; break; case NFS4_OP_LAYOUTERROR: if (!xdr_LAYOUTERROR4args(xdrs, &objp->nfs_argop4_u.oplayouterror)) return false; break; case NFS4_OP_LAYOUTSTATS: if (!xdr_LAYOUTSTATS4args(xdrs, &objp->nfs_argop4_u.oplayoutstats)) return false; break; case NFS4_OP_COPY: case NFS4_OP_COPY_NOTIFY: case NFS4_OP_OFFLOAD_CANCEL: case NFS4_OP_OFFLOAD_STATUS: case NFS4_OP_CLONE: break; /* NFSv4.3 */ case NFS4_OP_GETXATTR: if (!xdr_GETXATTR4args(xdrs, &objp->nfs_argop4_u.opgetxattr)) return false; break; case NFS4_OP_SETXATTR: if (!xdr_SETXATTR4args(xdrs, &objp->nfs_argop4_u.opsetxattr)) return false; break; case NFS4_OP_LISTXATTR: if (!xdr_LISTXATTR4args(xdrs, &objp->nfs_argop4_u.oplistxattr)) return false; break; case NFS4_OP_REMOVEXATTR: if (!xdr_REMOVEXATTR4args(xdrs, &objp->nfs_argop4_u.opremovexattr)) return false; break; case NFS4_OP_ILLEGAL: break; default: /* Avoid transforming unknown opcodes as RPC * decoder errors */ objp->argop = NFS4_OP_ILLEGAL; break; } return true; } static inline bool xdr_nfs_resop4(XDR * xdrs, nfs_resop4 *objp) { if (!xdr_nfs_opnum4(xdrs, &objp->resop)) return false; switch (objp->resop) { case NFS4_OP_ACCESS: if (!xdr_ACCESS4res(xdrs, &objp->nfs_resop4_u.opaccess)) return false; break; case NFS4_OP_CLOSE: if (!xdr_CLOSE4res(xdrs, &objp->nfs_resop4_u.opclose)) return false; break; case NFS4_OP_COMMIT: if (!xdr_COMMIT4res(xdrs, &objp->nfs_resop4_u.opcommit)) return false; break; case NFS4_OP_CREATE: if (!xdr_CREATE4res(xdrs, &objp->nfs_resop4_u.opcreate)) return false; break; case NFS4_OP_DELEGPURGE: if (!xdr_DELEGPURGE4res (xdrs, &objp->nfs_resop4_u.opdelegpurge)) return false; break; case NFS4_OP_DELEGRETURN: if (!xdr_DELEGRETURN4res (xdrs, &objp->nfs_resop4_u.opdelegreturn)) return false; break; case NFS4_OP_GETATTR: if (!xdr_GETATTR4res (xdrs, &objp->nfs_resop4_u.opgetattr)) return false; break; case NFS4_OP_GETFH: if (!xdr_GETFH4res(xdrs, &objp->nfs_resop4_u.opgetfh)) return false; break; case NFS4_OP_LINK: if (!xdr_LINK4res(xdrs, &objp->nfs_resop4_u.oplink)) return false; break; case NFS4_OP_LOCK: if (!xdr_LOCK4res(xdrs, &objp->nfs_resop4_u.oplock)) return false; break; case NFS4_OP_LOCKT: if (!xdr_LOCKT4res(xdrs, &objp->nfs_resop4_u.oplockt)) return false; break; case NFS4_OP_LOCKU: if (!xdr_LOCKU4res(xdrs, &objp->nfs_resop4_u.oplocku)) return false; break; case NFS4_OP_LOOKUP: if (!xdr_LOOKUP4res(xdrs, &objp->nfs_resop4_u.oplookup)) return false; break; case NFS4_OP_LOOKUPP: if (!xdr_LOOKUPP4res (xdrs, &objp->nfs_resop4_u.oplookupp)) return false; break; case NFS4_OP_NVERIFY: if (!xdr_NVERIFY4res (xdrs, &objp->nfs_resop4_u.opnverify)) return false; break; case NFS4_OP_OPEN: if (!xdr_OPEN4res(xdrs, &objp->nfs_resop4_u.opopen)) return false; break; case NFS4_OP_OPENATTR: if (!xdr_OPENATTR4res (xdrs, &objp->nfs_resop4_u.opopenattr)) return false; break; case NFS4_OP_OPEN_CONFIRM: if (!xdr_OPEN_CONFIRM4res (xdrs, &objp->nfs_resop4_u.opopen_confirm)) return false; break; case NFS4_OP_OPEN_DOWNGRADE: if (!xdr_OPEN_DOWNGRADE4res (xdrs, &objp->nfs_resop4_u.opopen_downgrade)) return false; break; case NFS4_OP_PUTFH: if (!xdr_PUTFH4res(xdrs, &objp->nfs_resop4_u.opputfh)) return false; break; case NFS4_OP_PUTPUBFH: if (!xdr_PUTPUBFH4res (xdrs, &objp->nfs_resop4_u.opputpubfh)) return false; break; case NFS4_OP_PUTROOTFH: if (!xdr_PUTROOTFH4res (xdrs, &objp->nfs_resop4_u.opputrootfh)) return false; break; case NFS4_OP_READ: if (!xdr_READ4res(xdrs, &objp->nfs_resop4_u.opread)) return false; break; case NFS4_OP_READDIR: if (!xdr_READDIR4res (xdrs, &objp->nfs_resop4_u.opreaddir)) return false; break; case NFS4_OP_READLINK: if (!xdr_READLINK4res (xdrs, &objp->nfs_resop4_u.opreadlink)) return false; break; case NFS4_OP_REMOVE: if (!xdr_REMOVE4res(xdrs, &objp->nfs_resop4_u.opremove)) return false; break; case NFS4_OP_RENAME: if (!xdr_RENAME4res(xdrs, &objp->nfs_resop4_u.oprename)) return false; break; case NFS4_OP_RENEW: if (!xdr_RENEW4res(xdrs, &objp->nfs_resop4_u.oprenew)) return false; break; case NFS4_OP_RESTOREFH: if (!xdr_RESTOREFH4res (xdrs, &objp->nfs_resop4_u.oprestorefh)) return false; break; case NFS4_OP_SAVEFH: if (!xdr_SAVEFH4res(xdrs, &objp->nfs_resop4_u.opsavefh)) return false; break; case NFS4_OP_SECINFO: if (!xdr_SECINFO4res (xdrs, &objp->nfs_resop4_u.opsecinfo)) return false; break; case NFS4_OP_SETATTR: if (!xdr_SETATTR4res (xdrs, &objp->nfs_resop4_u.opsetattr)) return false; break; case NFS4_OP_SETCLIENTID: if (!xdr_SETCLIENTID4res (xdrs, &objp->nfs_resop4_u.opsetclientid)) return false; break; case NFS4_OP_SETCLIENTID_CONFIRM: if (!xdr_SETCLIENTID_CONFIRM4res (xdrs, &objp->nfs_resop4_u.opsetclientid_confirm)) return false; break; case NFS4_OP_VERIFY: if (!xdr_VERIFY4res(xdrs, &objp->nfs_resop4_u.opverify)) return false; break; case NFS4_OP_WRITE: if (!xdr_WRITE4res(xdrs, &objp->nfs_resop4_u.opwrite)) return false; break; case NFS4_OP_RELEASE_LOCKOWNER: if (!xdr_RELEASE_LOCKOWNER4res (xdrs, &objp->nfs_resop4_u.oprelease_lockowner)) return false; break; case NFS4_OP_BACKCHANNEL_CTL: if (!xdr_BACKCHANNEL_CTL4res (xdrs, &objp->nfs_resop4_u.opbackchannel_ctl)) return false; break; case NFS4_OP_BIND_CONN_TO_SESSION: if (!xdr_BIND_CONN_TO_SESSION4res (xdrs, &objp->nfs_resop4_u.opbind_conn_to_session)) return false; break; case NFS4_OP_EXCHANGE_ID: if (!xdr_EXCHANGE_ID4res (xdrs, &objp->nfs_resop4_u.opexchange_id)) return false; break; case NFS4_OP_CREATE_SESSION: if (!xdr_CREATE_SESSION4res (xdrs, &objp->nfs_resop4_u.opcreate_session)) return false; break; case NFS4_OP_DESTROY_SESSION: if (!xdr_DESTROY_SESSION4res (xdrs, &objp->nfs_resop4_u.opdestroy_session)) return false; break; case NFS4_OP_FREE_STATEID: if (!xdr_FREE_STATEID4res (xdrs, &objp->nfs_resop4_u.opfree_stateid)) return false; break; case NFS4_OP_GET_DIR_DELEGATION: if (!xdr_GET_DIR_DELEGATION4res (xdrs, &objp->nfs_resop4_u.opget_dir_delegation)) return false; break; case NFS4_OP_GETDEVICEINFO: if (!xdr_GETDEVICEINFO4res (xdrs, &objp->nfs_resop4_u.opgetdeviceinfo)) return false; break; case NFS4_OP_GETDEVICELIST: if (!xdr_GETDEVICELIST4res (xdrs, &objp->nfs_resop4_u.opgetdevicelist)) return false; break; case NFS4_OP_LAYOUTCOMMIT: if (!xdr_LAYOUTCOMMIT4res (xdrs, &objp->nfs_resop4_u.oplayoutcommit)) return false; break; case NFS4_OP_LAYOUTGET: if (!xdr_LAYOUTGET4res (xdrs, &objp->nfs_resop4_u.oplayoutget)) return false; break; case NFS4_OP_LAYOUTRETURN: if (!xdr_LAYOUTRETURN4res (xdrs, &objp->nfs_resop4_u.oplayoutreturn)) return false; break; case NFS4_OP_SECINFO_NO_NAME: if (!xdr_SECINFO_NO_NAME4res (xdrs, &objp->nfs_resop4_u.opsecinfo_no_name)) return false; break; case NFS4_OP_SEQUENCE: if (!xdr_SEQUENCE4res (xdrs, &objp->nfs_resop4_u.opsequence)) return false; break; case NFS4_OP_SET_SSV: if (!xdr_SET_SSV4res (xdrs, &objp->nfs_resop4_u.opset_ssv)) return false; break; case NFS4_OP_TEST_STATEID: if (!xdr_TEST_STATEID4res (xdrs, &objp->nfs_resop4_u.optest_stateid)) return false; break; case NFS4_OP_WANT_DELEGATION: if (!xdr_WANT_DELEGATION4res (xdrs, &objp->nfs_resop4_u.opwant_delegation)) return false; break; case NFS4_OP_DESTROY_CLIENTID: if (!xdr_DESTROY_CLIENTID4res (xdrs, &objp->nfs_resop4_u.opdestroy_clientid)) return false; break; case NFS4_OP_RECLAIM_COMPLETE: if (!xdr_RECLAIM_COMPLETE4res (xdrs, &objp->nfs_resop4_u.opreclaim_complete)) return false; break; /* NFSv4.2 */ case NFS4_OP_WRITE_SAME: if (!xdr_WRITE_SAME4res (xdrs, &objp->nfs_resop4_u.opwrite_plus)) return false; break; case NFS4_OP_READ_PLUS: if (!xdr_READ_PLUS4res (xdrs, &objp->nfs_resop4_u.opread_plus)) return false; break; case NFS4_OP_SEEK: if (!xdr_SEEK4res (xdrs, &objp->nfs_resop4_u.opseek)) return false; break; case NFS4_OP_ALLOCATE: if (!xdr_ALLOCATE4res (xdrs, &objp->nfs_resop4_u.opallocate)) return false; break; case NFS4_OP_DEALLOCATE: if (!xdr_DEALLOCATE4res (xdrs, &objp->nfs_resop4_u.opdeallocate)) return false; break; case NFS4_OP_IO_ADVISE: if (!xdr_IO_ADVISE4res (xdrs, &objp->nfs_resop4_u.opio_advise)) return false; break; case NFS4_OP_LAYOUTERROR: if (!xdr_LAYOUTERROR4res(xdrs, &objp->nfs_resop4_u.oplayouterror)) return false; break; case NFS4_OP_LAYOUTSTATS: if (!xdr_LAYOUTSTATS4res(xdrs, &objp->nfs_resop4_u.oplayoutstats)) return false; break; case NFS4_OP_COPY: case NFS4_OP_COPY_NOTIFY: case NFS4_OP_OFFLOAD_CANCEL: case NFS4_OP_OFFLOAD_STATUS: case NFS4_OP_CLONE: /* NFSv4.3 */ case NFS4_OP_GETXATTR: if (!xdr_GETXATTR4res(xdrs, &objp->nfs_resop4_u.opgetxattr)) return false; break; case NFS4_OP_SETXATTR: if (!xdr_SETXATTR4res(xdrs, &objp->nfs_resop4_u.opsetxattr)) return false; break; case NFS4_OP_LISTXATTR: if (!xdr_LISTXATTR4res(xdrs, &objp->nfs_resop4_u.oplistxattr)) return false; break; case NFS4_OP_REMOVEXATTR: if (!xdr_REMOVEXATTR4res(xdrs, &objp->nfs_resop4_u.opremovexattr)) return false; break; case NFS4_OP_ILLEGAL: if (!xdr_ILLEGAL4res (xdrs, &objp->nfs_resop4_u.opillegal)) return false; break; default: return false; } return true; } static inline bool xdr_COMPOUND4args(XDR * xdrs, COMPOUND4args *objp) { if (!xdr_utf8str_cs(xdrs, &objp->tag)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->minorversion)) return false; /* decoder hint */ if (objp->minorversion > 0) xdrs->x_flags &= ~XDR_FLAG_CKSUM; if (!xdr_array (xdrs, (char **)&objp->argarray.argarray_val, &objp->argarray.argarray_len, XDR_ARRAY_MAXLEN, sizeof(nfs_argop4), (xdrproc_t) xdr_nfs_argop4)) return false; return true; } static inline bool xdr_COMPOUND4res(XDR * xdrs, COMPOUND4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; if (!xdr_utf8str_cs(xdrs, &objp->tag)) return false; if (!xdr_array (xdrs, (char **)&objp->resarray.resarray_val, &objp->resarray.resarray_len, XDR_ARRAY_MAXLEN, sizeof(nfs_resop4), (xdrproc_t) xdr_nfs_resop4)) return false; return true; } static inline bool xdr_CB_GETATTR4args(XDR * xdrs, CB_GETATTR4args *objp) { if (!xdr_nfs_fh4(xdrs, &objp->fh)) return false; if (!xdr_bitmap4(xdrs, &objp->attr_request)) return false; return true; } static inline bool xdr_CB_GETATTR4resok(XDR * xdrs, CB_GETATTR4resok *objp) { if (!xdr_fattr4(xdrs, &objp->obj_attributes)) return false; return true; } static inline bool xdr_CB_GETATTR4res(XDR * xdrs, CB_GETATTR4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; switch (objp->status) { case NFS4_OK: if (!xdr_CB_GETATTR4resok (xdrs, &objp->CB_GETATTR4res_u.resok4)) return false; break; default: break; } return true; } static inline bool xdr_CB_RECALL4args(XDR * xdrs, CB_RECALL4args *objp) { if (!xdr_stateid4(xdrs, &objp->stateid)) return false; if (!inline_xdr_bool(xdrs, &objp->truncate)) return false; if (!xdr_nfs_fh4(xdrs, &objp->fh)) return false; return true; } static inline bool xdr_CB_RECALL4res(XDR * xdrs, CB_RECALL4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; return true; } static inline bool xdr_CB_ILLEGAL4res(XDR * xdrs, CB_ILLEGAL4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; return true; } static inline bool xdr_layoutrecall_type4(XDR * xdrs, layoutrecall_type4 *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_layoutrecall_file4(XDR * xdrs, layoutrecall_file4 *objp) { if (!xdr_nfs_fh4(xdrs, &objp->lor_fh)) return false; if (!xdr_offset4(xdrs, &objp->lor_offset)) return false; if (!xdr_length4(xdrs, &objp->lor_length)) return false; if (!xdr_stateid4(xdrs, &objp->lor_stateid)) return false; return true; } static inline bool xdr_layoutrecall4(XDR * xdrs, layoutrecall4 *objp) { if (!xdr_layoutrecall_type4(xdrs, &objp->lor_recalltype)) return false; switch (objp->lor_recalltype) { case LAYOUTRECALL4_FILE: if (!xdr_layoutrecall_file4 (xdrs, &objp->layoutrecall4_u.lor_layout)) return false; break; case LAYOUTRECALL4_FSID: if (!xdr_fsid4(xdrs, &objp->layoutrecall4_u.lor_fsid)) return false; break; case LAYOUTRECALL4_ALL: break; default: return false; } return true; } static inline bool xdr_CB_LAYOUTRECALL4args(XDR * xdrs, CB_LAYOUTRECALL4args * objp) { if (!xdr_layouttype4(xdrs, &objp->clora_type)) return false; if (!xdr_layoutiomode4(xdrs, &objp->clora_iomode)) return false; if (!inline_xdr_bool(xdrs, &objp->clora_changed)) return false; if (!xdr_layoutrecall4(xdrs, &objp->clora_recall)) return false; return true; } static inline bool xdr_CB_LAYOUTRECALL4res(XDR * xdrs, CB_LAYOUTRECALL4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->clorr_status)) return false; return true; } static inline bool xdr_notify_type4(XDR * xdrs, notify_type4 *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_notify_entry4(XDR * xdrs, notify_entry4 *objp) { if (!xdr_component4(xdrs, &objp->ne_file)) return false; if (!xdr_fattr4(xdrs, &objp->ne_attrs)) return false; return true; } static inline bool xdr_prev_entry4(XDR * xdrs, prev_entry4 *objp) { if (!xdr_notify_entry4(xdrs, &objp->pe_prev_entry)) return false; if (!xdr_nfs_cookie4(xdrs, &objp->pe_prev_entry_cookie)) return false; return true; } static inline bool xdr_notify_remove4(XDR * xdrs, notify_remove4 *objp) { if (!xdr_notify_entry4(xdrs, &objp->nrm_old_entry)) return false; if (!xdr_nfs_cookie4(xdrs, &objp->nrm_old_entry_cookie)) return false; return true; } static inline bool xdr_notify_add4(XDR * xdrs, notify_add4 *objp) { if (!xdr_array (xdrs, (char **)&objp->nad_old_entry.nad_old_entry_val, (u_int *) &objp->nad_old_entry.nad_old_entry_len, 1, sizeof(notify_remove4), (xdrproc_t) xdr_notify_remove4)) return false; if (!xdr_notify_entry4(xdrs, &objp->nad_new_entry)) return false; if (!xdr_array (xdrs, (char **)&objp->nad_new_entry_cookie. nad_new_entry_cookie_val, (u_int *) &objp->nad_new_entry_cookie. nad_new_entry_cookie_len, 1, sizeof(nfs_cookie4), (xdrproc_t) xdr_nfs_cookie4)) return false; if (!xdr_array (xdrs, (char **)&objp->nad_prev_entry.nad_prev_entry_val, (u_int *) &objp->nad_prev_entry.nad_prev_entry_len, 1, sizeof(prev_entry4), (xdrproc_t) xdr_prev_entry4)) return false; if (!inline_xdr_bool(xdrs, &objp->nad_last_entry)) return false; return true; } static inline bool xdr_notify_attr4(XDR * xdrs, notify_attr4 *objp) { if (!xdr_notify_entry4(xdrs, &objp->na_changed_entry)) return false; return true; } static inline bool xdr_notify_rename4(XDR * xdrs, notify_rename4 *objp) { if (!xdr_notify_remove4(xdrs, &objp->nrn_old_entry)) return false; if (!xdr_notify_add4(xdrs, &objp->nrn_new_entry)) return false; return true; } static inline bool xdr_notify_verifier4(XDR * xdrs, notify_verifier4 *objp) { if (!xdr_verifier4(xdrs, objp->nv_old_cookieverf)) return false; if (!xdr_verifier4(xdrs, objp->nv_new_cookieverf)) return false; return true; } static inline bool xdr_notify_deviceid_delete4(XDR * xdrs, notify_deviceid_delete4 * objp) { if (!xdr_layouttype4(xdrs, &objp->ndd_layouttype)) return false; if (!xdr_deviceid4(xdrs, objp->ndd_deviceid)) return false; return true; } static inline bool xdr_notify_deviceid_change4(XDR * xdrs, notify_deviceid_change4 * objp) { if (!xdr_layouttype4(xdrs, &objp->ndc_layouttype)) return false; if (!xdr_deviceid4(xdrs, objp->ndc_deviceid)) return false; if (!inline_xdr_bool(xdrs, &objp->ndc_immediate)) return false; return true; } static inline bool xdr_notifylist4(XDR * xdrs, notifylist4 *objp) { if (!inline_xdr_bytes (xdrs, (char **)&objp->notifylist4_val, &objp->notifylist4_len, XDR_BYTES_MAXLEN)) return false; return true; } static inline bool xdr_notifylist_dev(XDR * xdrs, notifylist4 *objp, int type) { if (!xdr_count4(xdrs, &objp->notifylist4_len)) return false; if (type == NOTIFY_DEVICEID4_DELETE_MASK) { if (!xdr_notify_deviceid_delete4 (xdrs, (notify_deviceid_delete4 *) objp->notifylist4_val)) return false; } else { if (!xdr_notify_deviceid_change4 (xdrs, (notify_deviceid_change4 *) objp->notifylist4_val)) return false; } return true; } static inline bool xdr_notify4(XDR * xdrs, notify4 *objp) { if (!xdr_bitmap4(xdrs, &objp->notify_mask)) return false; if (!xdr_notifylist4(xdrs, &objp->notify_vals)) return false; return true; } static inline bool xdr_notify_dev(XDR * xdrs, notify4 *objp) { if (!xdr_bitmap4(xdrs, &objp->notify_mask)) return false; if (!xdr_notifylist_dev (xdrs, &objp->notify_vals, objp->notify_mask.map[0])) return false; return true; } static inline bool xdr_CB_NOTIFY4args(XDR * xdrs, CB_NOTIFY4args *objp) { if (!xdr_stateid4(xdrs, &objp->cna_stateid)) return false; if (!xdr_nfs_fh4(xdrs, &objp->cna_fh)) return false; if (!xdr_array (xdrs, (char **)&objp->cna_changes.cna_changes_val, &objp->cna_changes.cna_changes_len, XDR_ARRAY_MAXLEN, sizeof(notify4), (xdrproc_t) xdr_notify4)) return false; return true; } static inline bool xdr_CB_NOTIFY4res(XDR * xdrs, CB_NOTIFY4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->cnr_status)) return false; return true; } static inline bool xdr_CB_PUSH_DELEG4args(XDR * xdrs, CB_PUSH_DELEG4args *objp) { if (!xdr_nfs_fh4(xdrs, &objp->cpda_fh)) return false; if (!xdr_open_delegation4(xdrs, &objp->cpda_delegation)) return false; return true; } static inline bool xdr_CB_PUSH_DELEG4res(XDR * xdrs, CB_PUSH_DELEG4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->cpdr_status)) return false; return true; } static inline bool xdr_CB_RECALL_ANY4args(XDR * xdrs, CB_RECALL_ANY4args *objp) { if (!inline_xdr_u_int32_t(xdrs, &objp->craa_objects_to_keep)) return false; if (!xdr_bitmap4(xdrs, &objp->craa_type_mask)) return false; return true; } static inline bool xdr_CB_RECALL_ANY4res(XDR * xdrs, CB_RECALL_ANY4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->crar_status)) return false; return true; } static inline bool xdr_CB_RECALLABLE_OBJ_AVAIL4args( XDR * xdrs, CB_RECALLABLE_OBJ_AVAIL4args *objp) { if (!xdr_CB_RECALL_ANY4args(xdrs, objp)) return false; return true; } static inline bool xdr_CB_RECALLABLE_OBJ_AVAIL4res( XDR * xdrs, CB_RECALLABLE_OBJ_AVAIL4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->croa_status)) return false; return true; } static inline bool xdr_CB_RECALL_SLOT4args(XDR * xdrs, CB_RECALL_SLOT4args *objp) { if (!xdr_slotid4(xdrs, &objp->rsa_target_highest_slotid)) return false; return true; } static inline bool xdr_CB_RECALL_SLOT4res(XDR * xdrs, CB_RECALL_SLOT4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->rsr_status)) return false; return true; } static inline bool xdr_referring_call4(XDR * xdrs, referring_call4 *objp) { if (!xdr_sequenceid4(xdrs, &objp->rc_sequenceid)) return false; if (!xdr_slotid4(xdrs, &objp->rc_slotid)) return false; return true; } static inline bool xdr_referring_call_list4(XDR * xdrs, referring_call_list4 * objp) { if (!xdr_sessionid4(xdrs, objp->rcl_sessionid)) return false; if (!xdr_array( xdrs, (char **)&objp->rcl_referring_calls.rcl_referring_calls_val, &objp->rcl_referring_calls.rcl_referring_calls_len, XDR_ARRAY_MAXLEN, sizeof(referring_call4), (xdrproc_t) xdr_referring_call4)) return false; return true; } /* * XDR functions for FLEX_FILES loc_body * */ static inline bool xdr_ff_device_versions4(XDR *xdrs, ff_device_versions4 *objp) { if (!xdr_uint32_t(xdrs, &objp->ffdv_version)) return false; if (!xdr_uint32_t(xdrs, &objp->ffdv_minorversion)) return false; if (!xdr_uint32_t(xdrs, &objp->ffdv_rsize)) return false; if (!xdr_uint32_t(xdrs, &objp->ffdv_wsize)) return false; if (!xdr_bool(xdrs, &objp->ffdv_tightly_coupled)) return false; return true; } static inline bool xdr_ff_device_addr4(XDR *xdrs, ff_device_addr4 *objp) { if (!xdr_multipath_list4 (xdrs, &objp->ffda_netaddrs)) return false; if (!xdr_array( xdrs, (char **)&objp->ffda_versions.ffda_versions_val, &objp->ffda_versions.ffda_versions_len, XDR_ARRAY_MAXLEN, sizeof(ff_device_versions4), (xdrproc_t)xdr_ff_device_versions4)) return false; return true; } static inline bool xdr_ff_data_server4(XDR *xdrs, ff_data_server4 *objp) { if (!xdr_deviceid4 (xdrs, objp->ffds_deviceid)) return false; if (!xdr_uint32_t (xdrs, &objp->ffds_efficiency)) return false; if (!xdr_stateid4(xdrs, &objp->ffds_stateid)) return false; if (!xdr_array(xdrs, (char **)&objp->ffds_fh_vers.ffds_fh_vers_val, &objp->ffds_fh_vers.ffds_fh_vers_len, XDR_ARRAY_MAXLEN, sizeof(nfs_fh4), (xdrproc_t) xdr_nfs_fh4)) return false; if (!xdr_fattr4_owner (xdrs, &objp->ffds_user)) return false; if (!xdr_fattr4_owner_group (xdrs, &objp->ffds_group)) return false; return true; } static inline bool xdr_ff_mirror4(XDR *xdrs, ff_mirror4 *objp) { if (!xdr_array( xdrs, (char **)&objp->ffm_data_servers.ffm_data_servers_val, &objp->ffm_data_servers.ffm_data_servers_len, XDR_ARRAY_MAXLEN, sizeof(ff_data_server4), (xdrproc_t) xdr_ff_data_server4)) return false; return true; } static inline bool xdr_ff_layout4(XDR *xdrs, ff_layout4 *objp) { if (!xdr_length4(xdrs, &objp->ffl_stripe_unit)) return false; if (!xdr_array(xdrs, (char **)&objp->ffl_mirrors.ffl_mirrors_val, &objp->ffl_mirrors.ffl_mirrors_len, XDR_ARRAY_MAXLEN, sizeof(ff_mirror4), (xdrproc_t) xdr_ff_mirror4)) return false; return true; } static inline bool xdr_device_error4(XDR *xdrs, device_error4 *objp) { if (!xdr_deviceid4(xdrs, objp->de_deviceid)) return false; if (!xdr_nfsstat4(xdrs, &objp->de_status)) return false; if (!xdr_nfs_opnum4(xdrs, &objp->de_opnum)) return false; return true; } static inline bool xdr_ff_ioerr4(XDR *xdrs, ff_ioerr4 *objp) { if (!xdr_offset4(xdrs, &objp->ffie_offset)) return false; if (!xdr_length4(xdrs, &objp->ffie_length)) return false; if (!xdr_stateid4(xdrs, &objp->ffie_stateid)) return false; if (!xdr_array(xdrs, (char **)&objp->ffie_errors.ffie_errors_val, &objp->ffie_errors.ffie_errors_len, XDR_ARRAY_MAXLEN, sizeof(device_error4), (xdrproc_t) xdr_device_error4)) return false; return true; } static inline bool xdr_ff_io_latency4(XDR *xdrs, ff_io_latency4 *objp) { if (!xdr_nfstime4 (xdrs, &objp->ffil_min)) return false; if (!xdr_nfstime4 (xdrs, &objp->ffil_max)) return false; if (!xdr_nfstime4 (xdrs, &objp->ffil_avg)) return false; if (!xdr_uint32_t (xdrs, &objp->ffil_count)) return false; return true; } static inline bool xdr_ff_layoutupdate4(XDR *xdrs, ff_layoutupdate4 *objp) { if (!xdr_netaddr4(xdrs, &objp->ffl_addr)) return false; if (!xdr_nfs_fh4(xdrs, &objp->ffl_fhandle)) return false; if (!xdr_ff_io_latency4(xdrs, &objp->ffl_read)) return false; if (!xdr_ff_io_latency4(xdrs, &objp->ffl_write)) return false; if (!xdr_nfstime4 (xdrs, &objp->ffl_duration)) return false; if (!xdr_bool (xdrs, &objp->ffl_local)) return false; return true; } static inline bool xdr_io_info4 (XDR *xdrs, io_info4 *objp) { if (!xdr_uint32_t(xdrs, &objp->ii_count)) return FALSE; if (!xdr_uint64_t(xdrs, &objp->ii_bytes)) return FALSE; return TRUE; } static inline bool xdr_ff_iostats4 (XDR *xdrs, ff_iostats4 *objp) { if (!xdr_offset4(xdrs, &objp->ffis_offset)) return false; if (!xdr_length4(xdrs, &objp->ffis_length)) return false; if (!xdr_stateid4(xdrs, &objp->ffis_stateid)) return false; if (!xdr_io_info4(xdrs, &objp->ffis_read)) return false; if (!xdr_io_info4(xdrs, &objp->ffis_write)) return false; if (!xdr_deviceid4(xdrs, objp->ffis_deviceid)) return false; if (!xdr_ff_layoutupdate4(xdrs, &objp->ffis_layoutupdate)) return false; return true; } static inline bool xdr_ff_layoutreturn4(XDR *xdrs, ff_layoutreturn4 *objp) { if (!xdr_array( xdrs, (char **)&objp->fflr_ioerr_report.fflr_ioerr_report_val, &objp->fflr_ioerr_report.fflr_ioerr_report_len, XDR_ARRAY_MAXLEN, sizeof(ff_ioerr4), (xdrproc_t) xdr_ff_ioerr4)) return false; if (!xdr_array( xdrs, (char **)&objp->fflr_iostats_report.fflr_iostats_report_val, &objp->fflr_iostats_report.fflr_iostats_report_len, XDR_ARRAY_MAXLEN, sizeof(ff_iostats4), (xdrproc_t)xdr_ff_iostats4)) return false; return true; } static inline bool xdr_ff_mirrors_hint(XDR *xdrs, ff_mirrors_hint *objp) { if (!xdr_bool (xdrs, &objp->ffmc_valid)) return false; switch (objp->ffmc_valid) { case TRUE: if (!xdr_uint32_t( xdrs, &objp->ff_mirrors_hint_u.ffmc_mirrors)) return false; break; case FALSE: break; default: return false; } return true; } static inline bool xdr_ff_layouthint4(XDR * xdrs, ff_layouthint4 *objp) { if (!xdr_ff_mirrors_hint(xdrs, &objp->fflh_mirrors_hint)) return false; return true; } static inline bool xdr_ff_cb_recall_any_mask( XDR * xdrs, ff_cb_recall_any_mask *objp) { if (!xdr_enum(xdrs, (enum_t *)objp)) return false; return true; } static inline bool xdr_CB_SEQUENCE4args(XDR * xdrs, CB_SEQUENCE4args *objp) { if (!xdr_sessionid4(xdrs, objp->csa_sessionid)) return false; if (!xdr_sequenceid4(xdrs, &objp->csa_sequenceid)) return false; if (!xdr_slotid4(xdrs, &objp->csa_slotid)) return false; if (!xdr_slotid4(xdrs, &objp->csa_highest_slotid)) return false; if (!inline_xdr_bool(xdrs, &objp->csa_cachethis)) return false; if (!xdr_array( xdrs, (char **)&objp->csa_referring_call_lists.csa_referring_call_lists_val, &objp->csa_referring_call_lists.csa_referring_call_lists_len, XDR_ARRAY_MAXLEN, sizeof(referring_call_list4), (xdrproc_t) xdr_referring_call_list4)) return false; return true; } static inline bool xdr_CB_SEQUENCE4resok(XDR * xdrs, CB_SEQUENCE4resok *objp) { if (!xdr_sessionid4(xdrs, objp->csr_sessionid)) return false; if (!xdr_sequenceid4(xdrs, &objp->csr_sequenceid)) return false; if (!xdr_slotid4(xdrs, &objp->csr_slotid)) return false; if (!xdr_slotid4(xdrs, &objp->csr_highest_slotid)) return false; if (!xdr_slotid4(xdrs, &objp->csr_target_highest_slotid)) return false; return true; } static inline bool xdr_CB_SEQUENCE4res(XDR * xdrs, CB_SEQUENCE4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->csr_status)) return false; switch (objp->csr_status) { case NFS4_OK: if (!xdr_CB_SEQUENCE4resok (xdrs, &objp->CB_SEQUENCE4res_u.csr_resok4)) return false; break; default: break; } return true; } static inline bool xdr_CB_WANTS_CANCELLED4args(XDR * xdrs, CB_WANTS_CANCELLED4args * objp) { if (!inline_xdr_bool (xdrs, &objp->cwca_contended_wants_cancelled)) return false; if (!inline_xdr_bool (xdrs, &objp->cwca_resourced_wants_cancelled)) return false; return true; } static inline bool xdr_CB_WANTS_CANCELLED4res(XDR * xdrs, CB_WANTS_CANCELLED4res * objp) { if (!xdr_nfsstat4(xdrs, &objp->cwcr_status)) return false; return true; } static inline bool xdr_CB_NOTIFY_LOCK4args(XDR * xdrs, CB_NOTIFY_LOCK4args *objp) { if (!xdr_nfs_fh4(xdrs, &objp->cnla_fh)) return false; if (!xdr_lock_owner4(xdrs, &objp->cnla_lock_owner)) return false; return true; } static inline bool xdr_CB_NOTIFY_LOCK4res(XDR * xdrs, CB_NOTIFY_LOCK4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->cnlr_status)) return false; return true; } static inline bool xdr_notify_deviceid_type4(XDR * xdrs, notify_deviceid_type4 * objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_CB_NOTIFY_DEVICEID4args(XDR * xdrs, CB_NOTIFY_DEVICEID4args * objp) { if (!xdr_array (xdrs, (char **)&objp->cnda_changes.cnda_changes_val, &objp->cnda_changes.cnda_changes_len, XDR_ARRAY_MAXLEN, sizeof(notify4), (xdrproc_t) xdr_notify_dev)) return false; return true; } static inline bool xdr_CB_NOTIFY_DEVICEID4res(XDR * xdrs, CB_NOTIFY_DEVICEID4res * objp) { if (!xdr_nfsstat4(xdrs, &objp->cndr_status)) return false; return true; } /* Callback operations new to NFSv4.1 */ static inline bool xdr_nfs_cb_opnum4(XDR * xdrs, nfs_cb_opnum4 *objp) { if (!inline_xdr_enum(xdrs, (enum_t *) objp)) return false; return true; } static inline bool xdr_nfs_cb_argop4(XDR * xdrs, nfs_cb_argop4 *objp) { if (!inline_xdr_u_int(xdrs, &objp->argop)) return false; switch (objp->argop) { case NFS4_OP_CB_GETATTR: if (!xdr_CB_GETATTR4args (xdrs, &objp->nfs_cb_argop4_u.opcbgetattr)) return false; break; case NFS4_OP_CB_RECALL: if (!xdr_CB_RECALL4args (xdrs, &objp->nfs_cb_argop4_u.opcbrecall)) return false; break; case NFS4_OP_CB_LAYOUTRECALL: if (!xdr_CB_LAYOUTRECALL4args (xdrs, &objp->nfs_cb_argop4_u.opcblayoutrecall)) return false; break; case NFS4_OP_CB_NOTIFY: if (!xdr_CB_NOTIFY4args (xdrs, &objp->nfs_cb_argop4_u.opcbnotify)) return false; break; case NFS4_OP_CB_PUSH_DELEG: if (!xdr_CB_PUSH_DELEG4args (xdrs, &objp->nfs_cb_argop4_u.opcbpush_deleg)) return false; break; case NFS4_OP_CB_RECALL_ANY: if (!xdr_CB_RECALL_ANY4args (xdrs, &objp->nfs_cb_argop4_u.opcbrecall_any)) return false; break; case NFS4_OP_CB_RECALLABLE_OBJ_AVAIL: if (!xdr_CB_RECALLABLE_OBJ_AVAIL4args (xdrs, &objp->nfs_cb_argop4_u.opcbrecallable_obj_avail)) return false; break; case NFS4_OP_CB_RECALL_SLOT: if (!xdr_CB_RECALL_SLOT4args (xdrs, &objp->nfs_cb_argop4_u.opcbrecall_slot)) return false; break; case NFS4_OP_CB_SEQUENCE: if (!xdr_CB_SEQUENCE4args (xdrs, &objp->nfs_cb_argop4_u.opcbsequence)) return false; break; case NFS4_OP_CB_WANTS_CANCELLED: if (!xdr_CB_WANTS_CANCELLED4args (xdrs, &objp->nfs_cb_argop4_u.opcbwants_cancelled)) return false; break; case NFS4_OP_CB_NOTIFY_LOCK: if (!xdr_CB_NOTIFY_LOCK4args (xdrs, &objp->nfs_cb_argop4_u.opcbnotify_lock)) return false; break; case NFS4_OP_CB_NOTIFY_DEVICEID: if (!xdr_CB_NOTIFY_DEVICEID4args (xdrs, &objp->nfs_cb_argop4_u.opcbnotify_deviceid)) return false; break; case NFS4_OP_CB_ILLEGAL: break; default: return false; } return true; } static inline bool xdr_nfs_cb_resop4(XDR * xdrs, nfs_cb_resop4 *objp) { if (!inline_xdr_u_int(xdrs, &objp->resop)) return false; switch (objp->resop) { case NFS4_OP_CB_GETATTR: if (!xdr_CB_GETATTR4res (xdrs, &objp->nfs_cb_resop4_u.opcbgetattr)) return false; break; case NFS4_OP_CB_RECALL: if (!xdr_CB_RECALL4res (xdrs, &objp->nfs_cb_resop4_u.opcbrecall)) return false; break; case NFS4_OP_CB_LAYOUTRECALL: if (!xdr_CB_LAYOUTRECALL4res (xdrs, &objp->nfs_cb_resop4_u.opcblayoutrecall)) return false; break; case NFS4_OP_CB_NOTIFY: if (!xdr_CB_NOTIFY4res (xdrs, &objp->nfs_cb_resop4_u.opcbnotify)) return false; break; case NFS4_OP_CB_PUSH_DELEG: if (!xdr_CB_PUSH_DELEG4res (xdrs, &objp->nfs_cb_resop4_u.opcbpush_deleg)) return false; break; case NFS4_OP_CB_RECALL_ANY: if (!xdr_CB_RECALL_ANY4res (xdrs, &objp->nfs_cb_resop4_u.opcbrecall_any)) return false; break; case NFS4_OP_CB_RECALLABLE_OBJ_AVAIL: if (!xdr_CB_RECALLABLE_OBJ_AVAIL4res (xdrs, &objp->nfs_cb_resop4_u.opcbrecallable_obj_avail)) return false; break; case NFS4_OP_CB_RECALL_SLOT: if (!xdr_CB_RECALL_SLOT4res (xdrs, &objp->nfs_cb_resop4_u.opcbrecall_slot)) return false; break; case NFS4_OP_CB_SEQUENCE: if (!xdr_CB_SEQUENCE4res (xdrs, &objp->nfs_cb_resop4_u.opcbsequence)) return false; break; case NFS4_OP_CB_WANTS_CANCELLED: if (!xdr_CB_WANTS_CANCELLED4res (xdrs, &objp->nfs_cb_resop4_u.opcbwants_cancelled)) return false; break; case NFS4_OP_CB_NOTIFY_LOCK: if (!xdr_CB_NOTIFY_LOCK4res (xdrs, &objp->nfs_cb_resop4_u.opcbnotify_lock)) return false; break; case NFS4_OP_CB_NOTIFY_DEVICEID: if (!xdr_CB_NOTIFY_DEVICEID4res (xdrs, &objp->nfs_cb_resop4_u.opcbnotify_deviceid)) return false; break; case NFS4_OP_CB_ILLEGAL: if (!xdr_CB_ILLEGAL4res (xdrs, &objp->nfs_cb_resop4_u.opcbillegal)) return false; break; default: return false; } return true; } static inline bool xdr_CB_COMPOUND4args(XDR * xdrs, CB_COMPOUND4args *objp) { if (!xdr_utf8str_cs(xdrs, &objp->tag)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->minorversion)) return false; if (!inline_xdr_u_int32_t(xdrs, &objp->callback_ident)) return false; if (!xdr_array (xdrs, (char **)&objp->argarray.argarray_val, &objp->argarray.argarray_len, XDR_ARRAY_MAXLEN, sizeof(nfs_cb_argop4), (xdrproc_t) xdr_nfs_cb_argop4)) return false; return true; } static inline bool xdr_CB_COMPOUND4res(XDR * xdrs, CB_COMPOUND4res *objp) { if (!xdr_nfsstat4(xdrs, &objp->status)) return false; if (!xdr_utf8str_cs(xdrs, &objp->tag)) return false; if (!xdr_array (xdrs, (char **)&objp->resarray.resarray_val, &objp->resarray.resarray_len, XDR_ARRAY_MAXLEN, sizeof(nfs_cb_resop4), (xdrproc_t) xdr_nfs_cb_resop4)) return false; return true; } #endif /* K&R C */ #ifdef __cplusplus } #endif #endif /* !_NFSV41_H_RPCGEN */ nfs-ganesha-2.6.0/src/include/nlm4.h000066400000000000000000000254171324272410200171540ustar00rootroot00000000000000/* * Please do not edit this file. * It was generated using rpcgen. */ #ifndef _NLM4_H_RPCGEN #define _NLM4_H_RPCGEN #ifdef __cplusplus extern "C" { #endif #define LM_MAXSTRLEN 1024 #define LM_MAXNAMELEN 1025 #define MAXNETOBJ_SZ 1024 #define SM_MAXSTRLEN 1024 #define SM_PRIV_SZ 16 typedef int int32_t; typedef u_int uint32_t; typedef quad_t int64_t; typedef u_quad_t uint64_t; enum nlm4_stats { NLM4_GRANTED = 0, NLM4_DENIED = 1, NLM4_DENIED_NOLOCKS = 2, NLM4_BLOCKED = 3, NLM4_DENIED_GRACE_PERIOD = 4, NLM4_DEADLCK = 5, NLM4_ROFS = 6, NLM4_STALE_FH = 7, NLM4_FBIG = 8, NLM4_FAILED = 9, }; typedef enum nlm4_stats nlm4_stats; struct nlm4_stat { nlm4_stats stat; }; typedef struct nlm4_stat nlm4_stat; struct nlm4_res { netobj cookie; nlm4_stat stat; }; typedef struct nlm4_res nlm4_res; struct nlm4_holder { bool_t exclusive; int32_t svid; netobj oh; uint64_t l_offset; uint64_t l_len; }; typedef struct nlm4_holder nlm4_holder; struct nlm4_testrply { nlm4_stats stat; union { struct nlm4_holder holder; } nlm4_testrply_u; }; typedef struct nlm4_testrply nlm4_testrply; struct nlm4_testres { netobj cookie; nlm4_testrply test_stat; }; typedef struct nlm4_testres nlm4_testres; struct nlm4_lock { char *caller_name; netobj fh; netobj oh; int32_t svid; uint64_t l_offset; uint64_t l_len; }; typedef struct nlm4_lock nlm4_lock; struct nlm4_lockargs { netobj cookie; bool_t block; bool_t exclusive; struct nlm4_lock alock; bool_t reclaim; int32_t state; }; typedef struct nlm4_lockargs nlm4_lockargs; struct nlm4_cancargs { netobj cookie; bool_t block; bool_t exclusive; struct nlm4_lock alock; }; typedef struct nlm4_cancargs nlm4_cancargs; struct nlm4_testargs { netobj cookie; bool_t exclusive; struct nlm4_lock alock; }; typedef struct nlm4_testargs nlm4_testargs; struct nlm4_unlockargs { netobj cookie; struct nlm4_lock alock; }; typedef struct nlm4_unlockargs nlm4_unlockargs; enum fsh4_mode { fsm_DN = 0, fsm_DR = 1, fsm_DW = 2, fsm_DRW = 3, }; typedef enum fsh4_mode fsh4_mode; enum fsh4_access { fsa_NONE = 0, fsa_R = 1, fsa_W = 2, fsa_RW = 3, }; typedef enum fsh4_access fsh4_access; struct nlm4_share { char *caller_name; netobj fh; netobj oh; fsh4_mode mode; fsh4_access access; }; typedef struct nlm4_share nlm4_share; struct nlm4_shareargs { netobj cookie; nlm4_share share; bool_t reclaim; }; typedef struct nlm4_shareargs nlm4_shareargs; struct nlm4_shareres { netobj cookie; nlm4_stats stat; int32_t sequence; }; typedef struct nlm4_shareres nlm4_shareres; struct nlm4_free_allargs { char *name; uint32_t state; }; typedef struct nlm4_free_allargs nlm4_free_allargs; struct nlm4_sm_notifyargs { char *name; int32_t state; char priv[SM_PRIV_SZ]; }; typedef struct nlm4_sm_notifyargs nlm4_sm_notifyargs; extern void nlm_init(void); #define NLMPROG 100021 #define NLM4_VERS 4 #if defined(__STDC__) || defined(__cplusplus) #define NLMPROC4_NULL 0 extern void *nlmproc4_null_4(void *, CLIENT *); extern void *nlmproc4_null_4_svc(void *, struct svc_req *); #define NLMPROC4_TEST 1 extern nlm4_testres *nlmproc4_test_4(nlm4_testargs *, CLIENT *); extern nlm4_testres *nlmproc4_test_4_svc(nlm4_testargs *, struct svc_req *); #define NLMPROC4_LOCK 2 extern nlm4_res *nlmproc4_lock_4(nlm4_lockargs *, CLIENT *); extern nlm4_res *nlmproc4_lock_4_svc(nlm4_lockargs *, struct svc_req *); #define NLMPROC4_CANCEL 3 extern nlm4_res *nlmproc4_cancel_4(nlm4_cancargs *, CLIENT *); extern nlm4_res *nlmproc4_cancel_4_svc(nlm4_cancargs *, struct svc_req *); #define NLMPROC4_UNLOCK 4 extern nlm4_res *nlmproc4_unlock_4(nlm4_unlockargs *, CLIENT *); extern nlm4_res *nlmproc4_unlock_4_svc(nlm4_unlockargs *, struct svc_req *); #define NLMPROC4_GRANTED 5 extern nlm4_res *nlmproc4_granted_4(nlm4_testargs *, CLIENT *); extern nlm4_res *nlmproc4_granted_4_svc(nlm4_testargs *, struct svc_req *); #define NLMPROC4_TEST_MSG 6 extern void *nlmproc4_test_msg_4(nlm4_testargs *, CLIENT *); extern void *nlmproc4_test_msg_4_svc(nlm4_testargs *, struct svc_req *); #define NLMPROC4_LOCK_MSG 7 extern void *nlmproc4_lock_msg_4(nlm4_lockargs *, CLIENT *); extern void *nlmproc4_lock_msg_4_svc(nlm4_lockargs *, struct svc_req *); #define NLMPROC4_CANCEL_MSG 8 extern void *nlmproc4_cancel_msg_4(nlm4_cancargs *, CLIENT *); extern void *nlmproc4_cancel_msg_4_svc(nlm4_cancargs *, struct svc_req *); #define NLMPROC4_UNLOCK_MSG 9 extern void *nlmproc4_unlock_msg_4(nlm4_unlockargs *, CLIENT *); extern void *nlmproc4_unlock_msg_4_svc(nlm4_unlockargs *, struct svc_req *); #define NLMPROC4_GRANTED_MSG 10 extern void *nlmproc4_granted_msg_4(nlm4_testargs *, CLIENT *); extern void *nlmproc4_granted_msg_4_svc(nlm4_testargs *, struct svc_req *); #define NLMPROC4_TEST_RES 11 extern void *nlmproc4_test_res_4(nlm4_testres *, CLIENT *); extern void *nlmproc4_test_res_4_svc(nlm4_testres *, struct svc_req *); #define NLMPROC4_LOCK_RES 12 extern void *nlmproc4_lock_res_4(nlm4_res *, CLIENT *); extern void *nlmproc4_lock_res_4_svc(nlm4_res *, struct svc_req *); #define NLMPROC4_CANCEL_RES 13 extern void *nlmproc4_cancel_res_4(nlm4_res *, CLIENT *); extern void *nlmproc4_cancel_res_4_svc(nlm4_res *, struct svc_req *); #define NLMPROC4_UNLOCK_RES 14 extern void *nlmproc4_unlock_res_4(nlm4_res *, CLIENT *); extern void *nlmproc4_unlock_res_4_svc(nlm4_res *, struct svc_req *); #define NLMPROC4_GRANTED_RES 15 extern void *nlmproc4_granted_res_4(nlm4_res *, CLIENT *); extern void *nlmproc4_granted_res_4_svc(nlm4_res *, struct svc_req *); #define NLMPROC4_SM_NOTIFY 16 extern void *nlmproc4_sm_notify_4(nlm4_sm_notifyargs *, CLIENT *); extern void *nlmproc4_sm_notify_4_svc(nlm4_sm_notifyargs *, struct svc_req *); #define NLMPROC4_SHARE 20 extern nlm4_shareres *nlmproc4_share_4(nlm4_shareargs *, CLIENT *); extern nlm4_shareres *nlmproc4_share_4_svc(nlm4_shareargs *, struct svc_req *); #define NLMPROC4_UNSHARE 21 extern nlm4_shareres *nlmproc4_unshare_4(nlm4_shareargs *, CLIENT *); extern nlm4_shareres *nlmproc4_unshare_4_svc(nlm4_shareargs *, struct svc_req *); #define NLMPROC4_NM_LOCK 22 extern nlm4_res *nlmproc4_nm_lock_4(nlm4_lockargs *, CLIENT *); extern nlm4_res *nlmproc4_nm_lock_4_svc(nlm4_lockargs *, struct svc_req *); #define NLMPROC4_FREE_ALL 23 extern void *nlmproc4_free_all_4(nlm4_free_allargs *, CLIENT *); extern void *nlmproc4_free_all_4_svc(nlm4_free_allargs *, struct svc_req *); extern int nlmprog_4_freeresult(SVCXPRT *, xdrproc_t, caddr_t); #else /* K&R C */ #define NLMPROC4_NULL 0 extern void *nlmproc4_null_4(); extern void *nlmproc4_null_4_svc(); #define NLMPROC4_TEST 1 extern nlm4_testres *nlmproc4_test_4(); extern nlm4_testres *nlmproc4_test_4_svc(); #define NLMPROC4_LOCK 2 extern nlm4_res *nlmproc4_lock_4(); extern nlm4_res *nlmproc4_lock_4_svc(); #define NLMPROC4_CANCEL 3 extern nlm4_res *nlmproc4_cancel_4(); extern nlm4_res *nlmproc4_cancel_4_svc(); #define NLMPROC4_UNLOCK 4 extern nlm4_res *nlmproc4_unlock_4(); extern nlm4_res *nlmproc4_unlock_4_svc(); #define NLMPROC4_GRANTED 5 extern nlm4_res *nlmproc4_granted_4(); extern nlm4_res *nlmproc4_granted_4_svc(); #define NLMPROC4_TEST_MSG 6 extern void *nlmproc4_test_msg_4(); extern void *nlmproc4_test_msg_4_svc(); #define NLMPROC4_LOCK_MSG 7 extern void *nlmproc4_lock_msg_4(); extern void *nlmproc4_lock_msg_4_svc(); #define NLMPROC4_CANCEL_MSG 8 extern void *nlmproc4_cancel_msg_4(); extern void *nlmproc4_cancel_msg_4_svc(); #define NLMPROC4_UNLOCK_MSG 9 extern void *nlmproc4_unlock_msg_4(); extern void *nlmproc4_unlock_msg_4_svc(); #define NLMPROC4_GRANTED_MSG 10 extern void *nlmproc4_granted_msg_4(); extern void *nlmproc4_granted_msg_4_svc(); #define NLMPROC4_TEST_RES 11 extern void *nlmproc4_test_res_4(); extern void *nlmproc4_test_res_4_svc(); #define NLMPROC4_LOCK_RES 12 extern void *nlmproc4_lock_res_4(); extern void *nlmproc4_lock_res_4_svc(); #define NLMPROC4_CANCEL_RES 13 extern void *nlmproc4_cancel_res_4(); extern void *nlmproc4_cancel_res_4_svc(); #define NLMPROC4_UNLOCK_RES 14 extern void *nlmproc4_unlock_res_4(); extern void *nlmproc4_unlock_res_4_svc(); #define NLMPROC4_GRANTED_RES 15 extern void *nlmproc4_granted_res_4(); extern void *nlmproc4_granted_res_4_svc(); #define NLMPROC4_SM_NOTIFY 16 extern void *nlmproc4_sm_notify_4(); extern void *nlmproc4_sm_notify_4_svc(); #define NLMPROC4_SHARE 20 extern nlm4_shareres *nlmproc4_share_4(); extern nlm4_shareres *nlmproc4_share_4_svc(); #define NLMPROC4_UNSHARE 21 extern nlm4_shareres *nlmproc4_unshare_4(); extern nlm4_shareres *nlmproc4_unshare_4_svc(); #define NLMPROC4_NM_LOCK 22 extern nlm4_res *nlmproc4_nm_lock_4(); extern nlm4_res *nlmproc4_nm_lock_4_svc(); #define NLMPROC4_FREE_ALL 23 extern void *nlmproc4_free_all_4(); extern void *nlmproc4_free_all_4_svc(); extern int nlmprog_4_freeresult(); #endif /* K&R C */ /* the xdr functions */ #if defined(__STDC__) || defined(__cplusplus) extern bool xdr_int64_t(XDR *, int64_t *); extern bool xdr_uint64_t(XDR *, uint64_t *); extern bool xdr_nlm4_stats(XDR *, nlm4_stats *); extern bool xdr_nlm4_stat(XDR *, nlm4_stat *); extern bool xdr_nlm4_res(XDR *, nlm4_res *); extern bool xdr_nlm4_holder(XDR *, nlm4_holder *); extern bool xdr_nlm4_testrply(XDR *, nlm4_testrply *); extern bool xdr_nlm4_testres(XDR *, nlm4_testres *); extern bool xdr_nlm4_lock(XDR *, nlm4_lock *); extern bool xdr_nlm4_lockargs(XDR *, nlm4_lockargs *); extern bool xdr_nlm4_cancargs(XDR *, nlm4_cancargs *); extern bool xdr_nlm4_testargs(XDR *, nlm4_testargs *); extern bool xdr_nlm4_unlockargs(XDR *, nlm4_unlockargs *); extern bool xdr_fsh4_mode(XDR *, fsh4_mode *); extern bool xdr_fsh4_access(XDR *, fsh4_access *); extern bool xdr_nlm4_share(XDR *, nlm4_share *); extern bool xdr_nlm4_shareargs(XDR *, nlm4_shareargs *); extern bool xdr_nlm4_shareres(XDR *, nlm4_shareres *); extern bool xdr_nlm4_free_allargs(XDR *, nlm4_free_allargs *); extern bool xdr_nlm4_sm_notifyargs(XDR *, nlm4_sm_notifyargs *); #else /* K&R C */ extern bool xdr_int64_t(); extern bool xdr_uint64_t(); extern bool xdr_nlm4_stats(); extern bool xdr_nlm4_stat(); extern bool xdr_nlm4_res(); extern bool xdr_nlm4_holder(); extern bool xdr_nlm4_testrply(); extern bool xdr_nlm4_testres(); extern bool xdr_nlm4_lock(); extern bool xdr_nlm4_lockargs(); extern bool xdr_nlm4_cancargs(); extern bool xdr_nlm4_testargs(); extern bool xdr_nlm4_unlockargs(); extern bool xdr_fsh4_mode(); extern bool xdr_fsh4_access(); extern bool xdr_nlm4_share(); extern bool xdr_nlm4_shareargs(); extern bool xdr_nlm4_shareres(); extern bool xdr_nlm4_free_allargs(); extern bool xdr_nlm4_sm_notifyargs(); #endif /* K&R C */ #ifdef __cplusplus } #endif #endif /* !_NLM4_H_RPCGEN */ nfs-ganesha-2.6.0/src/include/nlm_async.h000066400000000000000000000030261324272410200202550ustar00rootroot00000000000000/* * Copyright IBM Corporation, 2010 * Contributor: Aneesh Kumar K.v * * -------------------------- * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * */ #ifndef NLM_ASYNC_H #define NLM_ASYNC_H #include #include "sal_data.h" extern pthread_mutex_t nlm_async_resp_mutex; extern pthread_cond_t nlm_async_resp_cond; int nlm_async_callback_init(void); int nlm_send_async_res_nlm4(state_nlm_client_t *host, state_async_func_t func, nfs_res_t *pres); int nlm_send_async_res_nlm4test(state_nlm_client_t *host, state_async_func_t func, nfs_res_t *pres); /* Client routine to send the asynchrnous response, key is used to wait for * a response */ int nlm_send_async(int proc, state_nlm_client_t *host, void *inarg, void *key); void nlm_signal_async_resp(void *key); #endif /* NLM_ASYNC_H */ nfs-ganesha-2.6.0/src/include/nlm_util.h000066400000000000000000000061021324272410200201130ustar00rootroot00000000000000/* * Copyright IBM Corporation, 2010 * Contributor: Aneesh Kumar K.v * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- * * */ #ifndef NLM_UTIL_H #define NLM_UTIL_H #include "gsh_list.h" #include "nlm4.h" #include "sal_data.h" extern const char *lock_result_str(int rc); extern void copy_netobj(netobj *dst, netobj *src); extern void netobj_free(netobj *obj); extern void netobj_to_string(netobj *obj, char *buffer, int maxlen); /** * process_nlm_parameters: Process NLM parameters * * Returns -1 for a request that needs processing, otherwise returns an NLM * status * * preq: passed in so interface doesn't need to change when NLM Client * uses IP address * exclusive: TRUE if lock is a write lock * alock: nlm4_lock request structure * plock: cache_lock_desc_t to fill in from alock * ppobj: FSAL obj pointer to fill in * pexport: the export of interest * pclient: cache inode client * care: TRUE if this caller cares if an owner is found (otherwise * return NLM4_GRANTED * because the caller will have nothing to do) * ppnlm_client: NLM Client to fill in, returns a reference to the client * ppowner: NLM Owner to fill in, returns a reference to the owner * ppblock_data: Data required to make a call back to the client to grant a * blocked lock */ int nlm_process_parameters(struct svc_req *req, bool exclusive, nlm4_lock *alock, fsal_lock_param_t *plock, struct fsal_obj_handle **ppobj, care_t care, state_nsm_client_t **ppnsm_client, state_nlm_client_t **ppnlm_client, state_owner_t **ppowner, state_block_data_t **block_data, int32_t nsm_state, state_t **state); int nlm_process_share_parms(struct svc_req *req, nlm4_share *share, struct fsal_export *exp_hdl, struct fsal_obj_handle **ppobj, care_t care, state_nsm_client_t **ppnsm_client, state_nlm_client_t **ppnlm_client, state_owner_t **ppowner, state_t **state); void nlm_process_conflict(nlm4_holder *nlm_holder, state_owner_t *holder, fsal_lock_param_t *conflict); nlm4_stats nlm_convert_state_error(state_status_t status); state_status_t nlm_granted_callback(struct fsal_obj_handle *obj, state_lock_entry_t *lock_entry); #endif /* NLM_UTIL_H */ nfs-ganesha-2.6.0/src/include/nsm.h000066400000000000000000000034521324272410200170720ustar00rootroot00000000000000/* * Please do not edit this file. * It was generated using rpcgen. */ #ifndef _NSM_H_RPCGEN #define _NSM_H_RPCGEN #include "config.h" #include "gsh_rpc.h" #include "sal_data.h" #ifdef __cplusplus extern "C" { #endif #define SM_MAXSTRLEN 1024 #define SM_PROG 100024 #define SM_VERS 1 #define SM_MON 2 #define SM_UNMON 3 #define SM_UNMON_ALL 4 #define SM_NOTIFY 6 enum res { STAT_SUCC = 0, STAT_FAIL = 1, }; typedef enum res res; struct sm_stat_res { res res_stat; int state; }; typedef struct sm_stat_res sm_stat_res; struct sm_stat { int state; }; typedef struct sm_stat sm_stat; struct my_id { char *my_name; int my_prog; int my_vers; int my_proc; }; typedef struct my_id my_id; struct mon_id { char *mon_name; struct my_id my_id; }; typedef struct mon_id mon_id; struct mon { struct mon_id mon_id; char priv[16]; }; typedef struct mon mon; struct notify { char *my_name; int state; }; typedef struct notify notify; extern bool nsm_monitor(state_nsm_client_t *host); extern bool nsm_unmonitor(state_nsm_client_t *host); extern void nsm_unmonitor_all(void); extern int nsm_notify(char *host, int state); /* the xdr functions */ #if defined(__STDC__) || defined(__cplusplus) extern bool xdr_res(XDR *, res *); extern bool xdr_sm_stat_res(XDR *, sm_stat_res *); extern bool xdr_sm_stat(XDR *, sm_stat *); extern bool xdr_my_id(XDR *, my_id *); extern bool xdr_mon_id(XDR *, mon_id *); extern bool xdr_mon(XDR *, mon *); extern bool xdr_notify (XDR *, notify*); #else /* K&R C */ extern bool xdr_res(); extern bool xdr_sm_stat_res(); extern bool xdr_sm_stat(); extern bool xdr_my_id(); extern bool xdr_mon_id(); extern bool xdr_mon(); extern bool xdr_notify (); #endif /* K&R C */ #ifdef __cplusplus } #endif #endif /* !_NSM_H_RPCGEN */ nfs-ganesha-2.6.0/src/include/os/000077500000000000000000000000001324272410200165415ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/include/os/freebsd/000077500000000000000000000000001324272410200201535ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/include/os/freebsd/extended_types.h000066400000000000000000000017741324272410200233610ustar00rootroot00000000000000/* * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file os/freebsd/extended_types.h * @brief Extended type, FreeBSD version. */ #ifndef _EXTENDED_TYPES_FREEBSD_H #define _EXTENDED_TYPES_FREEBSD_H #include #endif /* _EXTENDED_TYPES_FREEBSD_H */ nfs-ganesha-2.6.0/src/include/os/freebsd/fsal_handle_syscalls.h000066400000000000000000000041051324272410200245010ustar00rootroot00000000000000/* * Copyright (C) Panasas, Inc. 2011 * Author(s): Brent Welch Sachin Bhamare * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later * version. * * This library can be distributed with a BSD license as well, just * ask. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA */ /** * @file include/os/freebsd/fsal_handle_syscalls.h * @brief System calls for the FreeBSD handle calls */ #ifndef HANDLE_FREEBSD_H #define HANDLE_FREEBSD_H #include #include "syscalls.h" #ifndef O_PATH #define O_PATH 0 #endif #ifndef O_DIRECTORY #define O_DIRECTORY 0 #endif #ifndef O_NOACCESS #define O_NOACCESS 0 #endif #ifndef AT_EMPTY_PATH #define AT_EMPTY_PATH 0x1000 #endif static inline int vfs_stat_by_handle(int mountfd, struct stat *buf) { int ret; /* BSD doesn't (yet) have AT_EMPTY_PATH support, so just use fstat() */ ret = fstat(mountfd, buf); return ret; } static inline int vfs_link_by_handle(vfs_file_handle_t *fh, int srcfd, int destdirfd, const char *dname) { struct fhandle *handle = (struct fhandle *)fh->handle_data; return fhlink(handle, destdirfd, dname); } static inline int vfs_readlink_by_handle(vfs_file_handle_t *fh, int srcfd, const char *sname, char *buf, size_t bufsize) { struct fhandle *handle = (struct fhandle *)fh->handle_data; return fhreadlink(handle, buf, bufsize); } #endif /* HANDLE_FREEBSD_H */ /** @} */ nfs-ganesha-2.6.0/src/include/os/freebsd/memstream.h000066400000000000000000000024161324272410200223210ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Contributeur: Sachin Bhamare sbhamare@panasas.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @file os/freebsd/memstream.h * @brief Set of functions to provide platform dependent memstream APIs */ #ifndef MEMSTREAM_FREEBSD_H #define MEMSTREAM_FREEBSD_H #include #include #include #include #include struct memstream { char **cp; size_t *lenp; size_t offset; }; FILE *open_memstream(char **cp, size_t *lenp); #endif /* MEMSTREAM_FREEBSD_H */ nfs-ganesha-2.6.0/src/include/os/freebsd/mntent.h000066400000000000000000000036001324272410200216300ustar00rootroot00000000000000/* * mntent * mntent.h - compatability header for FreeBSD * * Copyright (c) 2001 David Rufino * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _MNTENT_FREEBSD_H #define _MNTENT_FREEBSD_H #include #define MOUNTED "dummy" #define MNTTYPE_NFS "nfs" struct mntent { char *mnt_fsname; char *mnt_dir; char *mnt_type; char *mnt_opts; int mnt_freq; int mnt_passno; }; #define setmntent(x, y) ((FILE *)0x1) extern struct mntent *getmntent __P((FILE *fp)); char *hasmntopt __P((const struct mntent *mnt, const char *option)); #define endmntent(x) ((void)(int)1) #endif /* _MNTENT_FREEBSD_H */ nfs-ganesha-2.6.0/src/include/os/freebsd/quota.h000066400000000000000000000035141324272410200214600ustar00rootroot00000000000000/* * Copyright (C) Panasas, Inc. 2011 * Author(s): Sachin Bhamare * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later * version. * * This library can be distributed with a BSD license as well, just * ask. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA */ #ifndef QUOTA_FREEBSD_H #define QUOTA_FREEBSD_H #include #define QUOTACTL(cmd, path, id, addr) \ quotactl(path, (cmd), id, (void *)addr) /* * kludge to account for differently named member variable * (dqb_curspace Vs dqb_curblocks) in struct dqblk on Linux and * FreeBSD platforms */ struct dqblk_os { u_int64_t dqb_bhardlimit; /* absolute limit on disk blks alloc */ u_int64_t dqb_bsoftlimit; /* preferred limit on disk blks */ u_int64_t dqb_curspace; /* current block count */ u_int64_t dqb_ihardlimit; /* maximum # allocated inodes + 1 */ u_int64_t dqb_isoftlimit; /* preferred inode limit */ u_int64_t dqb_curinodes; /* current # allocated inodes */ int64_t dqb_btime; /* time limit for excessive disk use */ int64_t dqb_itime; /* time limit for excessive files */ }; #if __FreeBSD_cc_version >= 800001 #undef dqblk #endif #define dqblk dqblk_os #endif /* QUOTA_FREEBSD_H */ nfs-ganesha-2.6.0/src/include/os/freebsd/syscalls.h000066400000000000000000000056751324272410200221760ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Sachin Bhamare sbhamare@panasas.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * */ /** * @file syscalls.h * @brief platform dependent syscalls */ #ifndef _SYSCALLS_FREEBSD_H #define _SYSCALLS_FREEBSD_H #include #include #ifndef AT_FDCWD #define AT_FDCWD -100 #define AT_SYMLINK_NOFOLLOW 0x200 /* Do not follow symbolic links */ #define AT_SYMLINK_FOLLOW 0x400 /* Follow symbolic link */ #define AT_REMOVEDIR 0x800 /* Remove directory instead of file */ #endif /* AT_FDCWD */ #if __FreeBSD_cc_version >= 800001 /* getfhat() is not implemented in FreeBSD kernel yet */ int getfhat(int fd, char *path, fhandle_t *fhp, int flag); int fhlink(struct fhandle *fhp, int tofd, const char *to); int fhreadlink(struct fhandle *fhp, char *buf, size_t bufsize); #endif /* __FreeBSD_cc_version */ #ifndef SYS_openat int openat(int dir_fd, const char *file, int oflag, mode_t mode); int fchownat(int dir_fd, const char *file, uid_t owner, gid_t group, int flag); int futimesat(int dir_fd, char *filename, struct timeval *utimes); int fstatat(int dir_fd, const char *file, struct stat *st, int flag); int getfhat(int dir_fd, char *fname, struct fhandle *fhp, int flag); int fhopenat(int dir_fd, const struct fhandle *u_fhp, int flags); int fchmodat(int dir_fd, const char *filename, mode_t mode, int flags); int faccessat(int dir_fd, char *filename, int mode, int flags); int linkat(int fromfd, const char *from, int tofd, const char *to, int flags); int mkdirat(int dir_fd, const char *file, mode_t mode); int mkfifoat(int dir_fd, char *file, mode_t mode); int mknodat(int dir_fd, const char *file, mode_t mode, dev_t dev); int unlinkat(int dir_fd, const char *file, int flag); int readlinkat(int fd, const char *path, char *buf, size_t len); int symlinkat(const char *from, int tofd, const char *to); int renameat(int oldfd, const char *old, int newfd, const char *new); int utimensat(int dir_fd, char *path, struct timespec *times, int flags); int fhlink(struct fhandle *fhp, int tofd, const char *to); int fhreadlink(struct fhandle *fhp, char *buf, size_t bufsize); #endif /* SYS_openat */ #endif /* _SYSCALLS_FREEBSD_H */ nfs-ganesha-2.6.0/src/include/os/freebsd/xattr.h000066400000000000000000000027161324272410200214740ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Sachin Bhamare sbhamare@panasas.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @file os/freebsd/xattr.h * @brief Platform dependant utils for xattr support on FreeBSD * */ #ifndef _XATTR_FREEBSD_H #define _XATTR_FREEBSD_H #include #include #define XATTR_CREATE 0x1 #define XATTR_REPLACE 0x2 extern ssize_t fgetxattr(int fd, const char *name, void *value, size_t size); extern ssize_t fsetxattr(int fd, const char *name, void *value, size_t size, int flags); extern ssize_t flistxattr(int fd, const char *list, size_t size); extern ssize_t fremovexattr(int fd, const char *name); #endif /* _XATTR_FREEBSD_H */ nfs-ganesha-2.6.0/src/include/os/linux/000077500000000000000000000000001324272410200177005ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/include/os/linux/extended_types.h000066400000000000000000000022541324272410200231000ustar00rootroot00000000000000/* * * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file os/linux/extended_types.h * @brief Extended type, Linux version. */ #ifndef _EXTENDED_TYPES_LINUX_H #define _EXTENDED_TYPES_LINUX_H #include #include #endif /* _EXTENDED_TYPES_LINUX_H */ nfs-ganesha-2.6.0/src/include/os/linux/fsal_handle_syscalls.h000066400000000000000000000066721324272410200242410ustar00rootroot00000000000000/* * Copyright (C) International Business Machines Corp., 2010 * Author(s): Aneesh Kumar K.V * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * @file include/os/linux/fsal_handle_syscalls.h * @brief System calls for the Linux handle calls * */ #ifndef HANDLE_LINUX_H #define HANDLE_LINUX_H #ifndef AT_EMPTY_PATH #define AT_EMPTY_PATH 0x1000 #endif /* * This is the Linux-specific variation for the by-handle or "at" syscalls. * The vfs_file_handle_t is a redefinition of struct file_handle so * the code here just overlays the two structures on top of each other. */ #ifndef MAX_HANDLE_SZ /* syscalls introduced in 2.6.39 and enabled in glibc 2.14 * if we are not building against 2.14, create our own versions * as inlines. Glibc versions are externs to glibc... */ #define MAX_HANDLE_SZ 128 typedef unsigned int __u32; struct file_handle { __u32 handle_bytes; int handle_type; /* file identifier */ unsigned char f_handle[0]; }; #if defined(__i386__) #define __NR_name_to_handle_at 341 #define __NR_open_by_handle_at 342 #elif defined(__x86_64__) #define __NR_name_to_handle_at 303 #define __NR_open_by_handle_at 304 #elif defined(__PPC64__) #define __NR_name_to_handle_at 345 #define __NR_open_by_handle_at 346 #endif static inline int name_to_handle_at(int mdirfd, const char *name, struct file_handle *handle, int *mnt_id, int flags) { return syscall(__NR_name_to_handle_at, mdirfd, name, handle, mnt_id, flags); } static inline int open_by_handle_at(int mdirfd, struct file_handle *handle, int flags) { return syscall(__NR_open_by_handle_at, mdirfd, handle, flags); } #endif /* MAX_HANDLE_SZ */ #ifndef O_PATH #define O_PATH 010000000 #endif #ifndef AT_EACCESS #define AT_EACCESS 0x200 #endif #ifndef O_NOACCESS #define O_NOACCESS O_ACCMODE #endif static inline int vfs_stat_by_handle(int mountfd, struct stat *buf) { /* Must use fstatat() even though fstat() seems like it might * work, the Linux version rejects the file descriptor we've * obtained with the O_NOACCESS flag */ return fstatat(mountfd, "", buf, AT_EMPTY_PATH); } static inline int vfs_link_by_handle(vfs_file_handle_t *fh, int srcfd, int destdirfd, const char *dname) { return linkat(srcfd, "", destdirfd, dname, AT_EMPTY_PATH); } static inline int vfs_readlink_by_handle(vfs_file_handle_t *fh, int srcfd, const char *sname, char *buf, size_t bufsize) { return readlinkat(srcfd, sname, buf, bufsize); } /* If not otherwise defined, define OFD locks */ #ifndef F_OFD_GETLK #define F_OFD_GETLK 36 #endif #ifndef F_OFD_SETLK #define F_OFD_SETLK 37 #endif #ifndef F_OFD_SETLKW #define F_OFD_SETLKW 38 #endif #endif /* HANDLE_LINUX_H */ /** @} */ nfs-ganesha-2.6.0/src/include/os/linux/quota.h000066400000000000000000000021531324272410200212030ustar00rootroot00000000000000/* * Copyright (C) Panasas, Inc. 2011 * Author(s): Sachin Bhamare * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later * version. * * This library can be distributed with a BSD license as well, just * ask. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA */ #ifndef QUOTA_LINUX_H #define QUOTA_LINUX_H #include #define QUOTACTL(cmd, path, id, addr) \ quotactl((cmd), path, id, (caddr_t)addr) #endif /* QUOTA_LINUX_H */ nfs-ganesha-2.6.0/src/include/os/memstream.h000066400000000000000000000021121324272410200207000ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Contributeur: Sachin Bhamare sbhamare@panasas.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ /** * @file os/memstream.h * @brief Set of functions to provide platform dependent memstream APIs */ #ifndef _OS_MEMSTREAM_H #define _OS_MEMSTREAM_H #ifdef FREEBSD #include #endif #endif /* _OS_MEMSTREAM_H */ nfs-ganesha-2.6.0/src/include/os/mntent.h000066400000000000000000000021261324272410200202200ustar00rootroot00000000000000/* * * contributeur : Sachin Bhamare sbhamare@panasas.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file os/mntent.h * @brief Platform dependant utilities for reading/writing fstab, mtab, etc.. */ #ifndef MNTENT_H #define MNTENT_H #ifdef LINUX #include #elif FREEBSD #include #endif #endif /* MNTENT_H */ nfs-ganesha-2.6.0/src/include/os/quota.h000066400000000000000000000021321324272410200200410ustar00rootroot00000000000000/* * Copyright (C) Panasas, Inc. 2011 * Author(s): Sachin Bhamare * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later * version. * * This library can be distributed with a BSD license as well, just * ask. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA */ #ifndef _QUOTA_OS_H #define _QUOTA_OS_H #ifdef LINUX #include #elif FREEBSD #include #endif #endif /* _QUOTA_OS_H */ nfs-ganesha-2.6.0/src/include/os/subr.h000066400000000000000000000032231324272410200176650ustar00rootroot00000000000000/* * * contributeur : Sachin Bhamare sbhamare@panasas.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file subr.h * @author Sachin Bhamare * @brief platform dependant subroutine type definitions */ #ifndef SUBR_OS_H #define SUBR_OS_H #include #include #ifndef UTIME_NOW #define UTIME_NOW -1 #define UTIME_OMIT -2 #endif int vfs_utimesat(int fd, const char *path, const struct timespec times[2], int flags); int vfs_utimes(int fd, const struct timespec *times); struct vfs_dirent { uint64_t vd_ino; uint32_t vd_reclen; uint32_t vd_type; off_t vd_offset; char *vd_name; }; int vfs_readents(int fd, char *buf, unsigned int bcount, off_t *basepp); bool to_vfs_dirent(char *buf, int bpos, struct vfs_dirent *vd, off_t base); uid_t setuser(uid_t uid); gid_t setgroup(gid_t gid); int set_threadgroups(size_t size, const gid_t *list); #endif/* SUBR_OS_H */ nfs-ganesha-2.6.0/src/include/os/xattr.h000066400000000000000000000021651324272410200200600ustar00rootroot00000000000000/* * contributeur : Sachin Bhamare sbhamare@panasas.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file os/xattr.h * @author Sachin Bhamare * @brief Platform dependant utils for xattr support */ #ifndef _XATTR_OS_H #define _XATTR_OS_H #ifdef LINUX #include #elif FREEBSD #include #endif #endif /* _XATTR_OS_H */ nfs-ganesha-2.6.0/src/include/pnfs_utils.h000066400000000000000000000157011324272410200204630ustar00rootroot00000000000000/* * * Copyright (C) 2011 Linux Box Corporation * Author: Adam C. Emerson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file pnfs_utils.h * @brief Common utility functions for pNFS * * pNFS utility functions used all over Ganesha. */ #ifndef PNFS_UTILS_H #define PNFS_UTILS_H #include #include "nfs4.h" #include "fsal_pnfs.h" #include "fsal_api.h" /* The next 3 line are mandatory for proper autotools based management */ #include "config.h" /****************************************************** * Utility functions for ranges ******************************************************/ /** * @brief Test for overlap and compatible io_mode of segments * * @param segment1 [IN] A layout segment * @param segmenta [IN] A layout segment * * @return True if there is one or more byte contained in both both * segments and the io_modes are compatible. */ static inline bool pnfs_segments_overlap(const struct pnfs_segment *segment1, const struct pnfs_segment *segmenta) { if (!(segment1->io_mode & segmenta->io_mode)) { return false; } else if ((segment1->length == 0) || (segmenta->length == 0)) { return false; } else if (segment1->offset < segmenta->offset) { if (segment1->length == NFS4_UINT64_MAX) { return true; } else if (segment1->offset + segment1->length < segmenta->offset) { return false; } else { return true; } } else if (segmenta->offset < segment1->offset) { if (segmenta->length == NFS4_UINT64_MAX) { return true; } else if ((segmenta->offset + segmenta->length) < segment1->offset) { return false; } else { return true; } } else { return true; } } /** * @brief Check if one segment contains the other * * This function checks whether segment2 is subsegment (not * necessarily proper) of segment1. * * @param segment1 [IN] The putative supersegment * @param segment2 [IN] The putative subsugment * * @return True if segment2 is completely contained within segment1 */ static inline bool pnfs_segment_contains(const struct pnfs_segment *segment1, const struct pnfs_segment *segment2) { if (!(segment1->io_mode & segment2->io_mode)) { return false; } else if (segment1->length == 0) { return false; } else if (segment1->offset <= segment2->offset) { if (segment1->length == NFS4_UINT64_MAX) { return true; } else if (segment2->length == NFS4_UINT64_MAX) { return false; } else if ((segment2->offset + segment2->length) <= (segment1->offset + segment1->length)) { return true; } else { return false; } } else { return false; } } /** * @brief Subtract the second segment from the first * * In the case that the subtrahend completely contains the minuend, * the return value has a length and offset of 0. If the IO modes of * the two arguments are incompatible, the minuend is returned * unchanged. If the subtrahend is a proper subset of the minuend, * the minuend is returned unchanged. This is incorrect, but to * handle splitting a segment, we need to add split and merge support * to FSALs. * * @param minuend [IN] The putative supersegment * @param subtrahend [IN] The putative subsugment * * @return A layout segment that is the difference between the two * segments. */ static inline struct pnfs_segment pnfs_segment_difference(const struct pnfs_segment *minuend, const struct pnfs_segment *subtrahend) { if (!(minuend->io_mode & subtrahend->io_mode)) { return *minuend; } else if (pnfs_segment_contains(subtrahend, minuend)) { struct pnfs_segment null = { .io_mode = minuend->io_mode, .offset = 0, .length = 0 }; return null; } else if (!(pnfs_segments_overlap(minuend, subtrahend))) { return *minuend; } else if (minuend->offset <= subtrahend->offset) { if (minuend->length == NFS4_UINT64_MAX) { if (subtrahend->length == NFS4_UINT64_MAX) { struct pnfs_segment difference = { .io_mode = minuend->io_mode, .offset = minuend->offset, .length = (subtrahend->offset - minuend->offset) }; return difference; } else { return *minuend; } } else { if ((minuend->length + minuend->offset) > (subtrahend->length + subtrahend->offset)) { return *minuend; } else { struct pnfs_segment difference = { .io_mode = minuend->io_mode, .offset = minuend->offset, .length = (minuend->offset - subtrahend->offset) }; return difference; } } } else { struct pnfs_segment difference = { .io_mode = minuend->io_mode, .offset = subtrahend->offset + subtrahend->length - 1, .length = minuend->length }; return difference; } } /****************************************************** * Common functions for every pNFS implementation ******************************************************/ /* ** in FSAL/common_pnfs.c */ bool xdr_fsal_deviceid(XDR *xdrs, struct pnfs_deviceid *deviceid); nfsstat4 FSAL_encode_ipv4_netaddr(XDR *xdrs, uint16_t proto, uint32_t addr, uint16_t port); /** * This type exists soleley so arrays of hosts can be passed to * FSAL_encode_multipath_list. */ typedef struct fsal_multipath_member { uint16_t proto; /*< Protocool number */ uint32_t addr; /*< IPv4 address */ uint16_t port; /*< Port */ } fsal_multipath_member_t; nfsstat4 FSAL_encode_file_layout(XDR *xdrs, const struct pnfs_deviceid *deviceid, nfl_util4 util, const uint32_t first_idx, const offset4 ptrn_ofst, const uint16_t *ds_ids, const uint32_t num_fhs, const struct gsh_buffdesc *fhs); nfsstat4 FSAL_encode_v4_multipath(XDR *xdrs, const uint32_t num_hosts, const fsal_multipath_member_t *hosts); nfsstat4 posix2nfs4_error(int posix_errorcode); /* ** in support/ds.c */ struct fsal_pnfs_ds *pnfs_ds_alloc(void); void pnfs_ds_free(struct fsal_pnfs_ds *pds); bool pnfs_ds_insert(struct fsal_pnfs_ds *pds); struct fsal_pnfs_ds *pnfs_ds_get(uint16_t id_servers); static inline void pnfs_ds_get_ref(struct fsal_pnfs_ds *pds) { (void) atomic_inc_int32_t(&pds->refcount); } void pnfs_ds_put(struct fsal_pnfs_ds *pds); void pnfs_ds_remove(uint16_t id_servers, bool final); int ReadDataServers(config_file_t in_config, struct config_error_type *err_type); void remove_all_dss(void); void server_pkginit(void); #endif /* PNFS_UTILS_H */ nfs-ganesha-2.6.0/src/include/rbt_node.h000066400000000000000000000117361324272410200200750ustar00rootroot00000000000000/** * @addtogroup hashtable * @{ */ /** * @file rbt_node.h * @brief Red-black tree node structure */ /* * Implementation of RedBlack trees * Definitions and structures */ /* * This implementation of RedBlack trees was copied from * the STL library and adapted to a C environment. */ /* * RB tree implementation -*- C++ -*- * Copyright (C) 2001 Free Software Foundation, Inc. * * This file is part of the GNU ISO C++ Library. This library is free * software; you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) * any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License along * with this library; see the file COPYING. If not, write to the Free * Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * As a special exception, you may use this file as part of a free software * library without restriction. Specifically, if other files instantiate * templates or use macros or inline functions from this file, or you compile * this file and link it with other files to produce an executable, this * file does not by itself cause the resulting executable to be covered by * the GNU General Public License. This exception does not however * invalidate any other reasons why the executable file might be covered by * the GNU General Public License. */ /* * * Copyright (c) 1996,1997 * Silicon Graphics Computer Systems, Inc. * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. Silicon Graphics makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * * * Copyright (c) 1994 * Hewlett-Packard Company * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. Hewlett-Packard Company makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * * */ /* NOTE: This is an internal header file, included by other STL headers. * You should not attempt to use it directly. */ /* Red-black tree class, designed for use in implementing STL associative containers (set, multiset, map, and multimap). The insertion and deletion algorithms are based on those in Cormen, Leiserson, and Rivest, Introduction to Algorithms (MIT Press, 1990), except that (1) the header cell is maintained with links not only to the root but also to the leftmost node of the tree, to enable constant time begin(), and to the rightmost node of the tree, to enable linear time performance when used with the generic set algorithms (set_union, etc.); (2) when a node being deleted has two children its successor node is relinked into its place, rather than copied, so that the only iterators invalidated are those referring to the deleted node. */ #ifndef RBT_NODE_H #define RBT_NODE_H #include /* * allocation parameters */ #define RBT_NUM 16 /* allocate nodes RBT_NUM at a time */ /* * rbt_head is the head structure. * There is one rbt_head structure per tree. */ struct rbt_head { struct rbt_node *root; /* root node */ struct rbt_node *leftmost; /* leftmost node */ struct rbt_node *rightmost; /* rightmost nodei */ unsigned int rbt_num_node; /* number of nodes */ }; /* * rbt_node is the node structure. * There is one rbt_tree structure per node. * * Usually, rbt_node is part of a bigger structure. * In this case, rbt_opaq point to this bigger structure. * * The field anchor is never NULL. It points to a pointer * containing the rbt_node address. This pointer is either * - the field left in the parent node * - the field next in the parent node * - the field root in the rbt_head structure (for the root node) */ typedef struct rbt_node { unsigned int rbt_flags; struct rbt_node **anchor; /* anchor for this node */ struct rbt_node *parent; /* parent node or NULL for root */ struct rbt_node *left; /* left node */ struct rbt_node *next; /* "right" node */ uint64_t rbt_value; /* used for order */ void *rbt_opaq; /* pointer for external object */ } rbt_node_t; /* * flags for rbt_node */ #define RBT_RED 01 /* red node */ #endif /* RBT_NODE_H */ /** @} */ nfs-ganesha-2.6.0/src/include/rbt_tree.h000066400000000000000000000471151324272410200201070ustar00rootroot00000000000000/** * @addtogroup hashtable * @{ */ /** * @file rbt_tree.h * @brief Red-Black Tree */ /* * Implementation of RedBlack trees * Macros and algorithms */ /* * This implementation of RedBlack trees was copied from * the STL library and adapted to a C environment. */ /* * RB tree implementation -*- C++ -*- * Copyright (C) 2001 Free Software Foundation, Inc. * * This file is part of the GNU ISO C++ Library. This library is free * software; you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) * any later version. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License along * with this library; see the file COPYING. If not, write to the Free * Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * As a special exception, you may use this file as part of a free software * library without restriction. Specifically, if other files instantiate * templates or use macros or inline functions from this file, or you compile * this file and link it with other files to produce an executable, this * file does not by itself cause the resulting executable to be covered by * the GNU General Public License. This exception does not however * invalidate any other reasons why the executable file might be covered by * the GNU General Public License. */ /* * * Copyright (c) 1996,1997 * Silicon Graphics Computer Systems, Inc. * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. Silicon Graphics makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * * * Copyright (c) 1994 * Hewlett-Packard Company * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. Hewlett-Packard Company makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * * */ /* NOTE: This is an internal header file, included by other STL headers. * You should not attempt to use it directly. */ /* Red-black tree class, designed for use in implementing STL associative containers (set, multiset, map, and multimap). The insertion and deletion algorithms are based on those in Cormen, Leiserson, and Rivest, Introduction to Algorithms (MIT Press, 1990), except that (1) the header cell is maintained with links not only to the root but also to the leftmost node of the tree, to enable constant time begin(), and to the rightmost node of the tree, to enable linear time performance when used with the generic set algorithms (set_union, etc.); (2) when a node being deleted has two children its successor node is relinked into its place, rather than copied, so that the only iterators invalidated are those referring to the deleted node. */ #ifndef RBT_TREE_H #define RBT_TREE_H /* * For RBT_HEAD_INIT, RBT_COUNT, RBT_RIGHTMOST and RBT_LEFTMOST : * __header is the header */ #define RBT_HEAD_INIT(__header) \ ((__header)->root = 0, \ (__header)->leftmost = 0, \ (__header)->rightmost = 0, \ (__header)->rbt_num_node = 0) #define RBT_COUNT(__header) ((__header)->rbt_num_node) #define RBT_RIGHTMOST(__header) ((__header)->rightmost) #define RBT_LEFTMOST(__header) ((__header)->leftmost) /* * For RBT_VALUE : * __node is any node */ #define RBT_VALUE(__node) ((__node)->rbt_value) /* * For RBT_OPAQ : * __node is any node */ #define RBT_OPAQ(__node) ((__node)->rbt_opaq) /* * For RBT_INCREMENT and RBT_DECREMENT : * __node is the starting node * __x is a temporary variable * __node is modified to point to the next/previous node */ #define RBT_INCREMENT(__node) ({ \ if ((__node)->next) { \ __node = (__node)->next; \ while ((__node)->left) \ (__node) = (__node)->left; \ } else { \ struct rbt_node *__x; \ do { \ __x = (__node); \ } while ((((__node) = (__node)->parent)) && \ ((__node)->next == __x)); \ } \ }) #define RBT_DECREMENT(__node) ({ \ if ((__node)->left) { \ __node = (__node)->left; \ while ((__node)->next) \ (__node) = (__node)->next; \ } else { \ struct rbt_node *__x; \ do { \ __x = (__node); \ } while ((((__node) = (__node)->parent)) && \ ((__node)->left == __x)); \ } \ }) /* * For RBT_LOOP and RBT_LOOP_REVERSE : * __header is the header * __it is the iterator (type rbt_node *) * * These macros must be used with, respectively, * RBT_INCREMENT and RBT_DECREMENT. */ #define RBT_LOOP(__header, __it) \ for ((__it) = (__header)->leftmost; (__it);) #define RBT_LOOP_REVERSE(__header, __it) \ for ((__it) = (__header)->rightmost; (__it);) /* * For RBT_ROTATE_LEFT and RBT_ROTATE_RIGHT : * __xx is pointer to the pivotal node * __yy is a temporary variable * the pivotal node is not modified except its links in the tree * For RBT_ROTATE_LEFT, (__xx)->next must not be zero. * For RBT_ROTATE_RIGHT, (__xx)->left must not be zero. */ #define RBT_ROTATE_LEFT(__xx) ({ \ struct rbt_node *__yy; \ __yy = (__xx)->next; \ (__xx)->next = __yy->left; \ if (((__xx)->next)) { \ __yy->left->parent = (__xx); \ __yy->left->anchor = &(__xx)->next; \ } \ __yy->parent = (__xx)->parent; \ __yy->left = (__xx); \ __yy->anchor = (__xx)->anchor; \ (__xx)->parent = __yy; \ (__xx)->anchor = &__yy->left; \ *__yy->anchor = __yy; \ }) #define RBT_ROTATE_RIGHT(__xx) ({ \ struct rbt_node *__yy; \ __yy = (__xx)->left; \ (__xx)->left = __yy->next; \ if ((__xx)->left) { \ __yy->next->parent = (__xx); \ __yy->next->anchor = &(__xx)->left; \ } \ __yy->parent = (__xx)->parent; \ __yy->next = (__xx); \ __yy->anchor = (__xx)->anchor; \ (__xx)->parent = __yy; \ (__xx)->anchor = &__yy->next; \ *__yy->anchor = __yy; \ }) /* * For RBT_INSERT : * __node is the new node to be inserted * __par is the node which will be the first parent of __node * __node and __par are not modified * *__node and *__par are modified * __header is the header node * __x and __y are temporary variables * * __par must have been returned by RBT_FIND (successful or not). * If RBT_FIND was not successful, __par cannot have two children : * - If __node->rbt_value > __par->rbt_value, then __par->next is NULL. * __node will be installed at __par->next. * - If __node->rbt_value < __par->rbt_value, then __par->left is NULL. * __node will be installed at __par->next. * If RBT_FIND was successful : * - If __par has two children, search the previous node and replace * __par by this previous node. Then insert __node at __par->next. * - If __par->left is free, install __node at __par->left. * - Otherwise, install __node at __par->next. * * If this insertion unbalances the tree, __node may end in a different * position. */ #define RBT_INSERT(__header, __node, __par) ({ \ struct rbt_node *__x, *__y; \ (__header)->rbt_num_node++; \ __y = (__par); \ if (__y == 0) { \ (__node)->anchor = &(__header)->root; \ (__header)->root = (__node); \ (__header)->rightmost = (__node); \ (__header)->leftmost = (__node); \ } else if (((__node)->rbt_value == __y->rbt_value) && \ __y->next && __y->left) { \ __y = __y->left; \ while (__y->next) \ __y = __y->next; \ __y->next = (__node); \ (__node)->anchor = &__y->next; \ } else if (((__node)->rbt_value > __y->rbt_value) || \ (((__node)->rbt_value == __y->rbt_value) \ && __y->left)) { \ __y->next = (__node); \ (__node)->anchor = &__y->next; \ if (__y == (__header)->rightmost) { \ (__header)->rightmost = (__node); \ } \ } else { \ __y->left = (__node); \ (__node)->anchor = &__y->left; \ if (__y == (__header)->leftmost) { \ (__header)->leftmost = (__node); \ } \ } \ (__node)->rbt_flags = 0; \ (__node)->parent = __y; \ (__node)->left = 0; \ (__node)->next = 0; \ __x = (__node); \ while (__x->parent) { \ __x->rbt_flags |= RBT_RED; \ if ((__x->parent->rbt_flags & RBT_RED) == 0) \ break; \ if (__x->parent == __x->parent->parent->left) { \ __y = __x->parent->parent->next; \ if ((__y == 0) || ((__y->rbt_flags & RBT_RED) \ == 0)) { \ if (__x == __x->parent->next) { \ __x = __x->parent; \ RBT_ROTATE_LEFT(__x); \ } \ __x->parent->rbt_flags &= ~RBT_RED; \ __x = __x->parent->parent; \ __x->rbt_flags |= RBT_RED; \ RBT_ROTATE_RIGHT(__x); \ break; \ } \ } else { \ __y = __x->parent->parent->left; \ if ((__y == 0) || ((__y->rbt_flags & RBT_RED) \ == 0)) { \ if (__x == __x->parent->left) { \ __x = __x->parent; \ RBT_ROTATE_RIGHT(__x); \ } \ __x->parent->rbt_flags &= ~RBT_RED; \ __x = __x->parent->parent; \ __x->rbt_flags |= RBT_RED; \ RBT_ROTATE_LEFT(__x); \ break; \ } \ } \ __x->parent->rbt_flags &= ~RBT_RED; \ __y->rbt_flags &= ~RBT_RED; \ __x = __x->parent->parent; \ } \ }) /* * For RBT_UNLINK : * __node is the node to unlink * __header is the header node * __x, __z and __y are temporary variables * __node->rbt_flags may be modified * otherwise, __node is not modified */ #define RBT_UNLINK(__header, __node) ({ \ struct rbt_node *__x, *__y, *__z; \ (__header)->rbt_num_node--; \ if ((__node)->left && (__node)->next) { \ __y = (__node)->next; \ while (__y->left) \ __y = __y->left; \ if (((__node)->rbt_flags & RBT_RED) != \ (__y->rbt_flags & RBT_RED)) { \ (__node)->rbt_flags ^= RBT_RED; \ __y->rbt_flags ^= RBT_RED; \ } \ __x = __y->next; \ (__node)->left->parent = __y; \ (__node)->left->anchor = &__y->left; \ __y->left = (__node)->left; \ if (__y == (__node)->next) { \ __z = __y; \ } else { \ __z = __y->parent; \ if (__x) { \ __x->parent = __z; \ __x->anchor = &__z->left; \ } \ __z->left = __x; /* __y was a child of left */ \ __y->next = (__node)->next; \ (__node)->next->parent = __y; \ (__node)->next->anchor = &__y->next; \ } \ __y->parent = (__node)->parent; \ __y->anchor = (__node)->anchor; \ *(__node)->anchor = __y; \ } else { \ __z = (__node)->parent; \ __x = (__node)->next; /* __x might be NULL */ \ if (__x == 0) \ __x = (__node)->left; \ if (__x) { \ __x->parent = __z; \ __x->anchor = (__node)->anchor; \ } \ if ((__header)->leftmost == (__node)) { \ if (__x) { \ __y = __x; \ while (__y->left) \ __y = __y->left; \ (__header)->leftmost = __y; \ } else { \ (__header)->leftmost = __z; \ } \ } \ if ((__header)->rightmost == (__node)) { \ if (__x) { \ __y = __x; \ while (__y->next) \ __y = __y->next; \ (__header)->rightmost = __y; \ } else { \ (__header)->rightmost = __z; \ } \ } \ *(__node)->anchor = __x; \ } \ if (!((__node)->rbt_flags & RBT_RED)) { \ while ((__z) && \ ((__x == 0) || !(__x->rbt_flags & RBT_RED))) { \ if (__x == __z->left) { \ __y = __z->next; \ if (__y->rbt_flags & RBT_RED) { \ __y->rbt_flags &= ~RBT_RED; \ __z->rbt_flags |= RBT_RED; \ RBT_ROTATE_LEFT(__z); \ __y = __z->next; \ } \ if ((__y->left == 0 || \ !(__y->left->rbt_flags & RBT_RED)) \ && (__y->next == 0 || \ !(__y->next->rbt_flags & \ RBT_RED))) { \ __y->rbt_flags |= RBT_RED; \ __x = __z; \ __z = __z->parent; \ } else { \ if (__y->next == 0 || \ !(__y->next->rbt_flags & \ RBT_RED)) { \ if (__y->left) \ __y->left->rbt_flags \ &= ~RBT_RED; \ __y->rbt_flags |= RBT_RED; \ RBT_ROTATE_RIGHT(__y); \ __y = __z->next; \ } \ __y->rbt_flags &= ~RBT_RED; \ __y->rbt_flags |= \ __z->rbt_flags & RBT_RED; \ __z->rbt_flags &= ~RBT_RED; \ if (__y->next) \ __y->next->rbt_flags \ &= ~RBT_RED; \ RBT_ROTATE_LEFT(__z); \ break; \ } \ } else { \ __y = __z->left; \ if (__y->rbt_flags & RBT_RED) { \ __y->rbt_flags &= ~RBT_RED; \ __z->rbt_flags |= RBT_RED; \ RBT_ROTATE_RIGHT(__z); \ __y = __z->left; \ } \ if ((__y->left == 0 || \ !(__y->left->rbt_flags & RBT_RED)) \ && (__y->next == 0 || \ !(__y->next->rbt_flags & \ RBT_RED))) { \ __y->rbt_flags |= RBT_RED; \ __x = __z; \ __z = __z->parent; \ } else { \ if (__y->left == 0 || \ !(__y->left->rbt_flags & \ RBT_RED)) { \ if (__y->next) \ __y->next->rbt_flags \ &= ~RBT_RED; \ __y->rbt_flags |= RBT_RED; \ RBT_ROTATE_LEFT(__y); \ __y = __z->left; \ } \ __y->rbt_flags &= ~RBT_RED; \ __y->rbt_flags |= __z->rbt_flags\ & RBT_RED; \ __z->rbt_flags &= ~RBT_RED; \ if (__y->left) \ __y->left->rbt_flags \ &= ~RBT_RED; \ RBT_ROTATE_RIGHT(__z); \ break; \ } \ } \ } \ if (__x) \ __x->rbt_flags &= ~RBT_RED; \ } \ }) /* * For RBT_FIND * __header is the header node * __node will contain the found node * __val is a uint64_t and contains the value to search * __x is a temporary variable * No nodes are modified * __node is modified * * When RBT_FIND returns, __node points to the node whose value is __val. * If multiple nodes have the value __val, only one is returned. * If no node has the value __val, __node points to the preceeding * or the following node and __node cannot have two children. * After the call, if __node is NULL, the tree is empty. * To check for success : * if (((__node) != 0) && (RBT_VALUE(__node) == (__val))) { * -- node found -- * ... * } * * RBT_FIND must be called before inserting a node using RBT_INSERT. */ #define RBT_FIND(__header, __node, __val) ({ \ struct rbt_node *__x; \ (__node) = (__header)->root; \ __x = (__header)->root; \ while (__x) { \ (__node) = __x; \ if (__x->rbt_value > (__val)) { \ __x = __x->left; \ } else if (__x->rbt_value < (__val)) { \ __x = __x->next; \ } else { \ break; \ } \ } \ }) /* * For RBT_FIND_LEFT * __header is the header node * __node will contain the found node * __val is a uint64_t and contains the value to search * __x is a temporary variable * No nodes are modified * __node is modified * * When RBT_FIND_LEFT returns, __node points to the leftmost node * whose value is __val. * If multiple nodes have the value __val, only one is returned. * If no node has the value __val, __node is NULL. * This is different from RBT_FIND. RBT_FIND_LEFT cannot be used * to insert a new node. * To check for success : * if ((__node) != 0) { * -- node found -- * ... * } */ #define RBT_FIND_LEFT(__header, __node, __val) ({ \ struct rbt_node *__x; \ (__node) = 0; \ __x = (__header)->root; \ while (__x) { \ if (__x->rbt_value > (__val)) { \ __x = __x->left; \ } else if (__x->rbt_value < (__val)) { \ __x = __x->next; \ } else { \ (__node) = __x; \ while (__x) { \ while ((__x = __x->left)) { \ if (__x->rbt_value < (__val)) \ break; \ (__node) = __x; \ } \ if (__x == 0) \ break; \ while ((__x = __x->next)) { \ if (__x->rbt_value == \ (__val)) { \ (__node) = __x; \ break; \ } \ } \ } \ break; \ } \ } \ }) /* * RBT_BLACK_COUNT counts the number of black nodes in the parents of a node */ #define RBT_BLACK_COUNT(__node, __sum) ({ \ for ((__sum) = 0; (__node); (__node) = (__node)->parent) { \ if (!((__node)->rbt_flags & RBT_RED)) \ ++(__sum); \ } \ }) #define RBT_VERIFY(__header, __it, __error) ({ \ int __len, __num, __sum; \ struct rbt_node *__L, *__R; \ (__error) = 0; \ if ((__header)->rbt_num_node == 0) { \ if (((__header)->leftmost) || \ ((__header)->rightmost) || \ ((__header)->root)) { \ (__error) = 1; \ (__it) = 0; \ } \ } else { \ __L = (__header)->leftmost; \ RBT_BLACK_COUNT(__L, __len) \ __num = 0; \ RBT_LOOP((__header), (__it)) { \ if ((__it)->parent == 0) { \ if (((__it) != (__header)->root) || \ ((__it)->anchor != \ &(__header)->root)) { \ (__error) = 2; \ break; \ } \ } else { \ if ((((__it) == (__it)->parent->next) && \ ((__it)->anchor != \ &(__it)->parent->next)) || \ (((__it) == (__it)->parent->left) && \ ((__it)->anchor != \ &(__it)->parent->left))) { \ (__error) = 2; \ break; \ } \ } \ __L = (__it)->left; \ __R = (__it)->next; \ if (((__it)->rbt_flags & RBT_RED) && \ ((__L && (__L->rbt_flags & RBT_RED)) || \ (__R && (__R->rbt_flags & RBT_RED)))) { \ (__error) = 3; \ break; \ } \ if (__L && (__L->rbt_value > (__it)->rbt_value)) { \ (__error) = 4; \ break; \ } \ if (__R && (__R->rbt_value < (__it)->rbt_value)) { \ (__error) = 5; \ break; \ } \ if (!__L && !__R) { \ __L = (__it); \ RBT_BLACK_COUNT(__L, __sum) \ if (__sum != __len) { \ (__error) = 6; \ break; \ } \ } \ __num++; \ RBT_INCREMENT(__it) \ } \ if (((__error) == 0) && (__num != \ (__header)->rbt_num_node)) { \ (__error) = 7; \ (__it) = 0; \ } \ /* test RBT_DECREMENT */ \ __num = 0; \ RBT_LOOP_REVERSE(__header, __it) { \ __num++; \ RBT_DECREMENT(__it) \ } \ if (((__error) == 0) && (__num != \ (__header)->rbt_num_node)) { \ (__error) = 8; \ (__it) = 0; \ } \ if ((__error) == 0) { \ __L = (__header)->root; \ while ((__L)->left) \ (__L) = (__L)->left; \ __R = (__header)->root; \ while ((__R)->next) \ (__R) = (__R)->next; \ if ((__L != (__header)->leftmost) || \ (__R != (__header)->rightmost)) { \ (__error) = 9; \ (__it) = 0; \ } \ } \ if (((__error) == 0) && ((__header)->root) && \ !((__header)->root->parent == 0)) { \ (__error) = 10; \ (__it) = 0; \ } \ } \ }) #endif /* RBT_TREE_H */ /** @{ */ nfs-ganesha-2.6.0/src/include/rquota.h000066400000000000000000000107621324272410200176120ustar00rootroot00000000000000/* * The content of this file is a mix of rpcgen-generated * and hand-edited program text. It is not automatically * generated by, e.g., build processes. * * This file is under version control. */ #ifndef _RQUOTA_H_RPCGEN #define _RQUOTA_H_RPCGEN #include "gsh_rpc.h" #include "extended_types.h" #ifdef __cplusplus extern "C" { #endif #define RQ_PATHLEN 1024 struct sq_dqblk { u_int rq_bhardlimit; u_int rq_bsoftlimit; u_int rq_curblocks; u_int rq_fhardlimit; u_int rq_fsoftlimit; u_int rq_curfiles; u_int rq_btimeleft; u_int rq_ftimeleft; }; typedef struct sq_dqblk sq_dqblk; struct getquota_args { char *gqa_pathp; int gqa_uid; }; typedef struct getquota_args getquota_args; struct setquota_args { int sqa_qcmd; char *sqa_pathp; int sqa_id; sq_dqblk sqa_dqblk; }; typedef struct setquota_args setquota_args; struct ext_getquota_args { char *gqa_pathp; int gqa_type; int gqa_id; }; typedef struct ext_getquota_args ext_getquota_args; struct ext_setquota_args { int sqa_qcmd; char *sqa_pathp; int sqa_id; int sqa_type; sq_dqblk sqa_dqblk; }; typedef struct ext_setquota_args ext_setquota_args; struct rquota { int rq_bsize; bool_t rq_active; u_int rq_bhardlimit; u_int rq_bsoftlimit; u_int rq_curblocks; u_int rq_fhardlimit; u_int rq_fsoftlimit; u_int rq_curfiles; u_int rq_btimeleft; u_int rq_ftimeleft; }; typedef struct rquota rquota; enum qr_status { Q_OK = 1, Q_NOQUOTA = 2, Q_EPERM = 3, }; typedef enum qr_status qr_status; struct getquota_rslt { qr_status status; union { rquota gqr_rquota; } getquota_rslt_u; }; typedef struct getquota_rslt getquota_rslt; struct setquota_rslt { qr_status status; union { rquota sqr_rquota; } setquota_rslt_u; }; typedef struct setquota_rslt setquota_rslt; #define RQUOTAPROG 100011 #define RQUOTAVERS 1 #if defined(__STDC__) || defined(__cplusplus) #define RQUOTAPROC_GETQUOTA 1 extern getquota_rslt *rquotaproc_getquota_1(getquota_args *, CLIENT *); extern getquota_rslt *rquotaproc_getquota_1_svc(getquota_args *, struct svc_req *); #define RQUOTAPROC_GETACTIVEQUOTA 2 extern getquota_rslt *rquotaproc_getactivequota_1(getquota_args *, CLIENT *); extern getquota_rslt *rquotaproc_getactivequota_1_svc(getquota_args *, struct svc_req *); #define RQUOTAPROC_SETQUOTA 3 extern setquota_rslt *rquotaproc_setquota_1(setquota_args *, CLIENT *); extern setquota_rslt *rquotaproc_setquota_1_svc(setquota_args *, struct svc_req *); #define RQUOTAPROC_SETACTIVEQUOTA 4 extern setquota_rslt *rquotaproc_setactivequota_1(setquota_args *, CLIENT *); extern setquota_rslt *rquotaproc_setactivequota_1_svc(setquota_args *, struct svc_req *); extern int rquotaprog_1_freeresult(SVCXPRT *, xdrproc_t, caddr_t); extern char *check_handle_lead_slash(char *, char *, size_t); #else /* K&R C */ #define RQUOTAPROC_GETQUOTA 1 extern getquota_rslt *rquotaproc_getquota_1(); extern getquota_rslt *rquotaproc_getquota_1_svc(); #define RQUOTAPROC_GETACTIVEQUOTA 2 extern getquota_rslt *rquotaproc_getactivequota_1(); extern getquota_rslt *rquotaproc_getactivequota_1_svc(); #define RQUOTAPROC_SETQUOTA 3 extern setquota_rslt *rquotaproc_setquota_1(); extern setquota_rslt *rquotaproc_setquota_1_svc(); #define RQUOTAPROC_SETACTIVEQUOTA 4 extern setquota_rslt *rquotaproc_setactivequota_1(); extern setquota_rslt *rquotaproc_setactivequota_1_svc(); extern int rquotaprog_1_freeresult(); extern char *check_handle_lead_slash(); #endif /* K&R C */ #define EXT_RQUOTAVERS 2 /* the xdr functions */ #if defined(__STDC__) || defined(__cplusplus) extern bool xdr_sq_dqblk(XDR *, sq_dqblk *); extern bool xdr_getquota_args(XDR *, getquota_args *); extern bool xdr_setquota_args(XDR *, setquota_args *); extern bool xdr_ext_getquota_args(XDR *, ext_getquota_args *); extern bool xdr_ext_setquota_args(XDR *, ext_setquota_args *); extern bool xdr_rquota(XDR *, rquota *); extern bool xdr_qr_status(XDR *, qr_status *); extern bool xdr_getquota_rslt(XDR *, getquota_rslt *); extern bool xdr_setquota_rslt(XDR *, setquota_rslt *); #else /* K&R C */ extern bool_t xdr_sq_dqblk(); extern bool_t xdr_getquota_args(); extern bool_t xdr_setquota_args(); extern bool_t xdr_ext_getquota_args(); extern bool_t xdr_ext_setquota_args(); extern bool_t xdr_rquota(); extern bool_t xdr_qr_status(); extern bool_t xdr_getquota_rslt(); extern bool_t xdr_setquota_rslt(); #endif /* K&R C */ #ifdef __cplusplus } #endif #endif /* !_RQUOTA_H_RPCGEN */ nfs-ganesha-2.6.0/src/include/sal_data.h000066400000000000000000000774221324272410200200550ustar00rootroot00000000000000/* * * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup SAL State abstraction layer * @{ */ /** * @file sal_data.h * @brief Data structures for state management. */ #ifndef SAL_DATA_H #define SAL_DATA_H #include #include #include #include #include #include /* For having MAXNAMLEN */ #include "abstract_atomic.h" #include "abstract_mem.h" #include "hashtable.h" #include "fsal_pnfs.h" #include "config_parsing.h" #ifdef _USE_9P /* define u32 and related types independent of SAL and 9P */ #include "9p_types.h" #endif /* _USE_9P */ /** ** Forward declarations to avoid circular dependency conflicts */ #include "gsh_status.h" typedef struct nfs_client_id_t nfs_client_id_t; typedef struct nfs41_session nfs41_session_t; /** ** Consolidated circular dependencies */ #include "nfs_proto_data.h" /** ** Forward references to types */ typedef struct nfs_argop4_state nfs_argop4_state; typedef struct nfs_client_record_t nfs_client_record_t; typedef struct state_async_queue_t state_async_queue_t; typedef struct state_block_data_t state_block_data_t; typedef struct state_cookie_entry_t state_cookie_entry_t; typedef struct state_lock_entry_t state_lock_entry_t; typedef struct state_nfs4_owner_t state_nfs4_owner_t; typedef struct state_nlm_client_t state_nlm_client_t; typedef struct state_owner_t state_owner_t; typedef struct state_t state_t; /** * @brief Number of errors before giving up on recovery * * We set a maximum because the recovery routines need to terminate at * some point. */ #define STATE_ERR_MAX 100 /** * @brief Indicate that lock extends to the entire range of the file * * This is true no matter what the beginning of the lock range is. */ #define STATE_LOCK_OFFSET_EOF 0xFFFFFFFFFFFFFFFFLL extern struct fridgethr *state_async_fridge; /***************************************************************************** * * NFSv4.1 Session data * *****************************************************************************/ extern pool_t *nfs41_session_pool; /** * @param Session ID hash */ extern hash_table_t *ht_session_id; /** * @brief Default number of forechannel slots in a session * * This is also the maximum number of backchannel slots we'll use, * even if the client offers more. */ #define NFS41_NB_SLOTS_DEF 64 /** * @brief Members in the slot table */ typedef struct nfs41_session_slot__ { sequenceid4 sequence; /*< Sequence number of this operation */ pthread_mutex_t lock; /*< Lock on the slot */ struct COMPOUND4res_extended cached_result; /*< NFv41: pointer to cached RPC result in a session's slot */ unsigned int cache_used; /*< If we cached the result */ } nfs41_session_slot_t; /** * @brief Bookkeeping for callback slots on the client */ typedef struct nfs41_cb_session_slot { sequenceid4 sequence; /*< Sequence number of last call */ bool in_use; /*< Set if a call if in progress on this slot */ } nfs41_cb_session_slot_t; /** * Flag indicating that the backchannel is up */ enum { session_bc_up = 0x01, session_bc_fault = 0x02, /* not actually used anywhere */ }; /** * @brief Structure representing an NFSv4.1 session */ struct nfs41_session { char session_id[NFS4_SESSIONID_SIZE]; /*< Session ID */ struct glist_head session_link; /*< Link in the list of sessions for this clientid */ channel_attrs4 fore_channel_attrs; /*< Fore-channel attributes */ channel_attrs4 back_channel_attrs; /*< Back-channel attributes */ struct rpc_call_channel cb_chan; /*< Back channel */ pthread_mutex_t cb_mutex; /*< Protects the cb slot table, when searching for a free slot */ pthread_cond_t cb_cond; /*< Condition variable on which we wait if the slot table is full and on which we signal when we free an entry. */ SVCXPRT *xprt; /*< Referenced pointer to transport */ nfs_client_id_t *clientid_record; /*< Client record correspinding to ID */ clientid4 clientid; /*< Clientid owning this session */ uint32_t cb_program; /*< Callback program ID */ uint32_t flags; /*< Flags pertaining to this session */ int32_t refcount; uint32_t nb_slots; /**< Number of slots in this session */ nfs41_session_slot_t *fc_slots; /**< Forechannel slot table*/ nfs41_cb_session_slot_t *bc_slots; /**< Backchannel slot table */ }; /** * @brief Track the call that creates a state, for NFSv4.1. */ struct state_refer { sessionid4 session; /*< The session of the creating call. */ sequenceid4 sequence; /*< The sequence ID of the creating call. */ slotid4 slot; /*< The slot ID of the creating call. */ }; /****************************************************************************** * * NFSv4 Recovery data * *****************************************************************************/ /** * @brief Revoked filehandle list */ typedef struct rdel_fh { struct glist_head rdfh_list; char *rdfh_handle_str; } rdel_fh_t; /** * @brief A client entry */ typedef struct clid_entry { struct glist_head cl_list; /*< Link in the list */ struct glist_head cl_rfh_list; bool cl_reclaim_complete; char cl_name[PATH_MAX]; /*< Client name */ } clid_entry_t; extern char v4_old_dir[PATH_MAX]; extern char v4_recov_dir[PATH_MAX]; /****************************************************************************** * * NFSv4 State data * *****************************************************************************/ extern hash_table_t *ht_state_id; #include "sal_shared.h" /** * @brief Data for an NFS v4 share reservation/open */ struct state_share { struct glist_head share_lockstates; /*< Lock states for this open state This field MUST be first */ unsigned int share_access; /*< The NFSv4 Share Access state */ unsigned int share_deny; /*< The NFSv4 Share Deny state */ unsigned int share_access_prev; /*< Previous share access state */ unsigned int share_deny_prev; /*< Previous share deny state */ }; /** * @brief Data for an NLM share reservation/open */ struct state_nlm_share { struct glist_head share_perclient; /*< Lock states for this open state This field MUST be first */ unsigned int share_access; /*< The NFSv4 Share Access state */ unsigned int share_deny; /*< The NFSv4 Share Deny state */ unsigned int share_access_prev; /*< Previous share access state */ unsigned int share_deny_prev; /*< Previous share deny state */ }; /** * @brief Data for a set of locks */ struct state_lock { struct glist_head state_locklist; /*< List of locks owned by this stateid This field MUST be first */ struct glist_head state_sharelist; /*< List of states related to a share */ state_t *openstate; /*< The related open-state */ }; /** * @brief Data for a 9p fid */ struct state_9p_fid { struct glist_head state_locklist; /*< List of locks owned by this fid This field MUST be first */ unsigned int share_access; /*< The 9p Access state */ unsigned int share_deny; /*< Will always be 0 */ }; /** * @brief Stats for client-file delegation heuristics */ /* @brief Per client, per file stats */ struct cf_deleg_stats { time_t cfd_rs_time; /* time when the client responsed NFS4_OK for a recall. */ time_t cfd_r_time; /* time of the recall attempt */ }; /** * @brief States of a delegation * * Different states a delegation can be in during its lifetime */ enum deleg_state { DELEG_GRANTED = 1, /* Granted */ DELEG_RECALL_WIP, /* Recall in progress */ }; /** * @brief Data for a delegation */ struct state_deleg { open_delegation_type4 sd_type; enum deleg_state sd_state; struct cf_deleg_stats sd_clfile_stats; /* client specific */ }; /** * @brief Data for a set of layouts of a given type */ struct state_layout { struct glist_head state_segments; /*< List of segments */ layouttype4 state_layout_type; /*< The type of layout this state represents */ uint32_t granting; /*< Number of LAYOUTGETs in progress */ bool state_return_on_close; /*< Whether this layout should be returned on last close. */ }; /** * @brief Type specific state data */ union state_data { struct state_share share; struct state_nlm_share nlm_share; struct state_lock lock; struct state_deleg deleg; struct state_layout layout; struct state_9p_fid fid; uint32_t io_advise; }; /** * @brief The number of bytes in the stateid.other * * The value 12 is fixed by RFC 3530/RFC 5661. */ #define OTHERSIZE 12 extern char all_zero[OTHERSIZE]; extern char all_ones[OTHERSIZE]; /** * @brief Structure representing a single NFSv4 state * * Each state is identified by stateid and represents some resource or * set of resources. * * The lists are protected by the state_lock */ struct state_t { struct glist_head state_list; /**< List of states on a file */ struct glist_head state_owner_list; /**< List of states for an owner */ struct glist_head state_export_list; /**< List of states on the same export */ #ifdef DEBUG_SAL struct glist_head state_list_all; /**< Global list of all stateids */ #endif pthread_mutex_t state_mutex; /**< Mutex protecting following pointers */ struct gsh_export *state_export; /**< Export this entry belongs to */ /* Don't re-order or move these next two. They are used for hashing */ state_owner_t *state_owner; /**< State Owner related to state */ struct fsal_obj_handle *state_obj; /**< owning object */ struct fsal_export *state_exp; /**< FSAL export */ union state_data state_data; enum state_type state_type; u_int32_t state_seqid; /**< The NFSv4 Sequence id */ int32_t state_refcount; /**< Refcount for state_t objects */ char stateid_other[OTHERSIZE]; /**< "Other" part of state id, used as hash key */ struct state_refer state_refer; /**< For NFSv4.1, track the call that created a state. */ }; /* Macros to compare and copy state_t to a struct stateid4 */ #define SAME_STATEID(id4, state) \ ((id4)->seqid == (state)->state_seqid && \ memcmp((id4)->other, (state)->stateid_other, OTHERSIZE) == 0) #define COPY_STATEID(id4, state) \ do { \ (id4)->seqid = (state)->state_seqid; \ (void)memcpy((id4)->other, (state)->stateid_other, OTHERSIZE); \ } while (0) /***************************************************************************** * * NFS Owner data * *****************************************************************************/ typedef void (state_owner_init_t) (state_owner_t *powner); extern hash_table_t *ht_nlm_owner; #ifdef _USE_9P extern hash_table_t *ht_9p_owner; #endif extern hash_table_t *ht_nfs4_owner; extern struct glist_head cached_open_owners; extern pthread_mutex_t cached_open_owners_lock; /** * @brief A structure identifying the owner of an NFSv4 open or lock state * * This serves as the key to the NFSv4 owner table, and is generated * from either an open or lock owner. */ typedef struct state_nfs4_owner_name_t { unsigned int son_owner_len; char *son_owner_val; } state_nfs4_owner_name_t; /** * @brief The type of entity responsible for a state * * For NLM and 9P, one kind of owner owns every kind of state. For * NFSv4.1 open owners and lock owners are in disjoint spaces, and all * non-open and non-lock states are associated with a given client. */ typedef enum state_owner_type_t { STATE_LOCK_OWNER_UNKNOWN, /*< Unknown */ #ifdef _USE_NLM STATE_LOCK_OWNER_NLM, /*< An NLM client */ #endif /* _USE_NLM */ #ifdef _USE_9P STATE_LOCK_OWNER_9P, /*< A 9P client */ #endif STATE_OPEN_OWNER_NFSV4, /*< An NFSv4 owner of an open */ STATE_LOCK_OWNER_NFSV4, /*< An NFSv4 owner of a set of locks */ STATE_CLIENTID_OWNER_NFSV4 /*< An NFSv4 client, owns all states but opens and locks. */ } state_owner_type_t; /** * @brief Specifies interest in a client for NLN/NSM. */ typedef enum care_t { CARE_NOT, /*< Do not care about client's status */ CARE_OWNER, /*< Don't care about nsm state but do need an owner. */ CARE_ALWAYS, /*< Always care about client's status */ CARE_NO_MONITOR, /*< Care, but will not actively monitor */ CARE_MONITOR /*< Will actively monitor client status */ } care_t; extern hash_table_t *ht_nsm_client; extern hash_table_t *ht_nlm_client; /** * @brief NSM (rpc.statd) state for a given client. */ typedef struct state_nsm_client_t { pthread_mutex_t ssc_mutex; /*< Mutex protecting this structure */ struct glist_head ssc_lock_list; /*< All locks held by client */ struct glist_head ssc_share_list; /*< All share reservations */ struct gsh_client *ssc_client; /*< The client involved */ int32_t ssc_refcount; /*< Reference count to protect structure */ int32_t ssc_monitored; /*< If this client is actively monitored */ int32_t ssc_nlm_caller_name_len; /*< Length of identifier */ char *ssc_nlm_caller_name; /*< Client identifier */ } state_nsm_client_t; /** * @brief Represent a single client accessing us through NLM */ struct state_nlm_client_t { state_nsm_client_t *slc_nsm_client; /*< Related NSM client */ xprt_type_t slc_client_type; /*< The transport type to this client */ int32_t slc_refcount; /*< Reference count for disposal */ struct sockaddr_storage slc_server_addr; /*< local addr when request is made */ int32_t slc_nlm_caller_name_len; /*< Length of client name */ char *slc_nlm_caller_name; /*< Client name */ CLIENT *slc_callback_clnt; /*< Callback for blocking locks */ AUTH *slc_callback_auth; /*< Authentication for callback */ }; /** * @brief Owner of an NLM lock or share. */ typedef struct state_nlm_owner_t { state_nlm_client_t *so_client; /*< Structure for this client */ int32_t so_nlm_svid; /*< Owner within client */ struct glist_head so_nlm_shares; /*< Share reservations */ } state_nlm_owner_t; /** * @brief 9P lock owner */ #ifdef _USE_9P typedef struct state_9p_owner_t { u32 proc_id; /*< PID on the client */ struct sockaddr_storage client_addr; /*< Network address of client */ } state_9p_owner_t; #endif /* _USE_9P */ /** * @brief Share and lock operations for NFSv4.0 * * This structure saves the arguments to the most recent operation on * an open or lock owner associated with an NFSv4.0 client. This is * part of the At-Most Once semantics and not used under NFSv4.1. */ struct nfs_argop4_state { nfs_opnum4 argop; /*< Operation being saved */ union { CLOSE4args opclose; /*< CLOSE */ LOCK4args oplock; /*< LOCK */ LOCKU4args oplocku; /*< LOCKU */ OPEN4args opopen; /*< OPEN */ OPEN_CONFIRM4args opopen_confirm; /*< OPEN_CONFIRM */ OPEN_DOWNGRADE4args opopen_downgrade; /*< OPEN_DOWNGRADE */ } nfs_argop4_u; }; /** * @brief A structure supporting all NFSv4 owners. */ struct state_nfs4_owner_t { clientid4 so_clientid; /*< Owning clientid */ nfs_client_id_t *so_clientrec; /*< Owning client id record */ bool so_confirmed; /*< Confirmation (NFSv4.0 only) */ seqid4 so_seqid; /*< Seqid for serialization of operations on owner (NFSv4.0 only) */ nfs_argop4_state so_args; /*< Saved args */ void *so_last_entry; /*< Last file operated on by this state owner * we don't keep a reference so this is a * void * to prevent it's dereferencing because * the pointer might become invalid if cache * inode flushes the entry out. But it sufices * for the purposes of detecting replayed * operations. */ nfs_resop4 so_resp; /*< Saved response */ state_owner_t *so_related_owner; /*< For lock-owners, the open-owner under which the lock owner was created */ struct glist_head so_state_list; /*< This is the head of a list of states owned by this owner. so_mutex MUST be held when accessing this field. */ struct glist_head so_perclient; /*< open owner entry to be linked to client */ struct glist_head so_cache_entry; /*< Entry on cached_open_owners */ time_t so_cache_expire; /* time cached OPEN owner will expire. If non-zero, so_cache_entry is in cached_open_owners list. cached_open_owners_lock MUST be held when accessing this field.*/ }; /** * @brief General state owner * * This structure encodes the owner of any state, protocol specific * information is contained within the union. */ struct state_owner_t { state_owner_type_t so_type; /*< Owner type */ struct glist_head so_lock_list; /*< Locks for this owner */ #ifdef DEBUG_SAL struct glist_head so_all_owners; /**< Global list of all state owners */ #endif /* _DEBUG_MEMLEAKS */ pthread_mutex_t so_mutex; /*< Mutex on this owner */ int32_t so_refcount; /*< Reference count for lifecyce management */ int so_owner_len; /*< Length of owner name */ char *so_owner_val; /*< Owner name */ union { state_nfs4_owner_t so_nfs4_owner; /*< All NFSv4 state owners */ state_nlm_owner_t so_nlm_owner; /*< NLM lock and share owners */ #ifdef _USE_9P state_9p_owner_t so_9p_owner; /*< 9P lock owners */ #endif } so_owner; }; /* Test if the lock owner type is 9P */ #ifdef _USE_9P #define LOCK_OWNER_9P(owner) ((owner)->so_type == STATE_LOCK_OWNER_9P) #else #define LOCK_OWNER_9P(owner) (0) #endif extern state_owner_t unknown_owner; /****************************************************************************** * * NFSv4 Clientid data * *****************************************************************************/ /** * @brief State of a clientid record. */ typedef enum nfs_clientid_confirm_state__ { UNCONFIRMED_CLIENT_ID, CONFIRMED_CLIENT_ID, EXPIRED_CLIENT_ID, STALE_CLIENT_ID } nfs_clientid_confirm_state_t; /** * @brief Errors from the clientid functions */ typedef enum clientid_status { CLIENT_ID_SUCCESS = 0, /*< Success */ CLIENT_ID_INSERT_MALLOC_ERROR, /*< Unable to allocate memory */ CLIENT_ID_INVALID_ARGUMENT, /*< Invalid argument */ CLIENT_ID_EXPIRED, /*< requested client id expired */ CLIENT_ID_STALE /*< requested client id stale */ } clientid_status_t; /** * @brief Record associated with a clientid * * This record holds Ganesha's state on an NFSv4.0 client. */ struct nfs_client_id_t { clientid4 cid_clientid; /*< The clientid */ verifier4 cid_verifier; /*< Known verifier */ verifier4 cid_incoming_verifier; /*< Most recently supplied verifier */ time_t cid_last_renew; /*< Time of last renewal */ nfs_clientid_confirm_state_t cid_confirmed; /*< Confirm/expire state */ bool cid_allow_reclaim; /*< Can still reclaim state? */ nfs_client_cred_t cid_credential; /*< Client credential */ char *cid_recov_tag; /*< Recovery tag */ nfs_client_record_t *cid_client_record; /*< Record for managing confirmation and replacement */ struct glist_head cid_openowners; /*< All open owners */ struct glist_head cid_lockowners; /*< All lock owners */ pthread_mutex_t cid_mutex; /*< Mutex for this client */ union { struct { /** Callback channel */ struct rpc_call_channel cb_chan; /** Decoded address */ gsh_addr_t cb_addr; /** Callback identifier */ uint32_t cb_callback_ident; /** Universal address */ char cb_client_r_addr[SOCK_NAME_MAX + 1]; /** Callback program */ uint32_t cb_program; bool cb_chan_down; /* Callback channel state */ } v40; /*< v4.0 callback information */ struct { bool cid_reclaim_complete; /*< reclaim complete indication */ /** All sessions */ struct glist_head cb_session_list; } v41; /*< v4.1 callback information */ } cid_cb; /*< Version specific callback information */ time_t first_path_down_resp_time; /* Time when the server first sent NFS4ERR_CB_PATH_DOWN */ unsigned int cid_nb_session; /*< Number of sessions stored */ uint32_t cid_create_session_sequence; /*< Sequence number for session creation. */ CREATE_SESSION4res cid_create_session_slot; /*< Cached response to last CREATE_SESSION */ state_owner_t cid_owner; /*< Owner for per-client state */ int32_t cid_refcount; /*< Reference count for lifecycle */ int cid_lease_reservations; /*< Counted lease reservations, to spare this clientid from the reaper */ uint32_t cid_minorversion; uint32_t cid_stateid_counter; uint32_t curr_deleg_grants; /* current num of delegations owned by this client */ uint32_t num_revokes; /* Num revokes for the client */ struct gsh_client *gsh_client; /* for client specific statistics. */ }; /** * @brief Client owner record for verification and replacement * * @note The cr_mutex should never be acquired while holding a * cid_mutex. */ struct nfs_client_record_t { int32_t cr_refcount; /*< Reference count for lifecycle */ pthread_mutex_t cr_mutex; /*< Mutex protecting this structure */ nfs_client_id_t *cr_confirmed_rec; /*< The confirmed record associated with this owner (if there is one.) */ nfs_client_id_t *cr_unconfirmed_rec; /*< The unconfirmed record associated with this client name, if there is one. */ uint32_t cr_server_addr; /*< Server IP address the client connected to */ uint32_t cr_pnfs_flags; /*< pNFS flags. RFC 5661 allows us to treat identical co_owners with different pNFS flags as disjoint. Linux client stupidity forces us to do so. */ int cr_client_val_len; /*< Length of owner */ char cr_client_val[]; /*< Suplied co_owner */ }; extern pool_t *client_id_pool; extern hash_table_t *ht_client_record; extern hash_table_t *ht_confirmed_client_id; extern hash_table_t *ht_unconfirmed_client_id; /****************************************************************************** * * Lock Data * *****************************************************************************/ /** * @brief Blocking lock type and state */ typedef enum state_blocking_t { STATE_NON_BLOCKING, STATE_NLM_BLOCKING, STATE_NFSV4_BLOCKING, STATE_GRANTING, STATE_CANCELED } state_blocking_t; /** * @brief Grant callback * * The granted call back is responsible for acquiring a reference to * the lock entry if needed. */ typedef state_status_t(*granted_callback_t) (struct fsal_obj_handle *obj, state_lock_entry_t *lock_entry); /** * @brief NLM specific Blocking lock data */ typedef struct state_nlm_block_data_t { netobj sbd_nlm_fh; /*< Filehandle */ char sbd_nlm_fh_buf[NFS3_FHSIZE]; /*< Statically allocated FH buffer */ } state_nlm_block_data_t; extern struct glist_head state_blocked_locks; /** * @brief Grant types */ typedef enum state_grant_type_t { STATE_GRANT_NONE, /*< No grant */ STATE_GRANT_INTERNAL, /*< Grant generated by SAL */ STATE_GRANT_FSAL, /*< FSAL granting lock */ STATE_GRANT_FSAL_AVAILABLE, /*< FSAL signalling lock availability */ STATE_GRANT_POLL, /*< Poll this lock */ } state_grant_type_t; typedef enum state_block_type_t { /** Not blocking */ STATE_BLOCK_NONE, /** Lock is blocked by an internal lock. */ STATE_BLOCK_INTERNAL, /** Lock is blocked and FSAL supports async blocking locks. */ STATE_BLOCK_ASYNC, /** Lock is blocked and SAL will poll the lock. */ STATE_BLOCK_POLL, } state_block_type_t; /** * @brief Blocking lock data */ struct state_block_data_t { struct glist_head sbd_list; /*< Lost of blocking locks */ state_grant_type_t sbd_grant_type; /*< Type of grant */ state_block_type_t sbd_block_type; /*< Type of block */ granted_callback_t sbd_granted_callback; /*< Callback for grant */ state_cookie_entry_t *sbd_blocked_cookie; /*< Blocking lock cookie */ state_lock_entry_t *sbd_lock_entry; /*< Details of lock */ union { state_nlm_block_data_t sbd_nlm; /*< NLM block data */ void *sbd_v4; /*< NFSv4 block data */ } sbd_prot; }; struct state_lock_entry_t { struct glist_head sle_list; /*< Locks on this file */ struct glist_head sle_owner_locks; /*< Link on the owner lock list */ struct glist_head sle_client_locks; /*< Locks on this client */ struct glist_head sle_state_locks; /*< Locks on this state */ #ifdef DEBUG_SAL struct glist_head sle_all_locks; /*< Link on the global lock list */ #endif /* DEBUG_SAL */ struct glist_head sle_export_locks; /*< Link on the export lock list */ struct gsh_export *sle_export; struct fsal_obj_handle *sle_obj; /*< File being locked */ state_block_data_t *sle_block_data; /*< Blocking lock data */ state_owner_t *sle_owner; /* Lock owner */ state_t *sle_state; /*< Associated lock state */ state_blocking_t sle_blocked; /*< Blocking status */ int32_t sle_ref_count; /*< Reference count */ fsal_lock_param_t sle_lock; /*< Lock description */ pthread_mutex_t sle_mutex; /*< Mutex to protect the structure */ }; /** * @brief Stats for file-specific and client-file delegation heuristics */ struct file_deleg_stats { uint32_t fds_curr_delegations; /* number of delegations on file */ open_delegation_type4 fds_deleg_type; /* delegation type */ uint32_t fds_delegation_count; /* times file has been delegated */ uint32_t fds_recall_count; /* times file has been recalled */ time_t fds_avg_hold; /* avg amount of time deleg held */ time_t fds_last_delegation; time_t fds_last_recall; uint32_t fds_num_opens; /* total num of opens so far. */ time_t fds_first_open; /* time that we started recording num_opens */ }; /** * @brief Per-file state lists * * To be used by FSALs */ struct state_file { /** File owning state */ struct fsal_obj_handle *obj; /** NFSv4 states on this file. Protected by state_lock */ struct glist_head list_of_states; /** Layout recalls on this file. Protected by state_lock */ struct glist_head layoutrecall_list; /** Pointers for lock list. Protected by state_lock */ struct glist_head lock_list; /** Pointers for NLM share list. Protected by state_lock */ struct glist_head nlm_share_list; /** true iff write delegated */ bool write_delegated; /** Delegation statistics. Protected by state_lock */ struct file_deleg_stats fdeleg_stats; uint32_t anon_ops; /* number of anonymous operations * happening at the moment which * prevents delegations from being * granted */ }; /** * @brief Per-directory state lists * * To be used by FSALs/SAL */ struct state_dir { /** If this is a junction, the export this node points to. * Protected by state_lock. */ struct gsh_export *junction_export; /** List of exports that have this cache inode as their root. * Protected by state_lock */ struct glist_head export_roots; /** There is one export root reference counted for each export for which this entry is a root for. This field is used with the atomic inc/dec/fetch routines. */ int32_t exp_root_refcount; }; struct state_hdl { /** Lock protecting state */ pthread_rwlock_t state_lock; bool no_cleanup; union { struct state_file file; struct state_dir dir; }; }; /** * @brief Description of a layout segment */ typedef struct state_layout_segment { struct glist_head sls_state_segments; /*< Link on the per-layout-state segment list */ state_t *sls_state; /*< Associated layout state */ struct pnfs_segment sls_segment; /*< Segment descriptor */ void *sls_fsal_data; /*< FSAL data */ } state_layout_segment_t; /** * @brief An entry in a list of states affected by a recall * * We make a non-intrusive list so that a state can be affected by any * number of recalls. */ struct recall_state_list { struct glist_head link; /*< Link to the next layout state in the queue */ state_t *state; /*< State on which to recall */ }; /** * @brief Processing for a LAYOUTRECALL from the FSAL. */ struct state_layout_recall_file { struct glist_head entry_link; /*< List of recalls on a file */ struct fsal_obj_handle *obj; /*< Related file */ layouttype4 type; /*< Type of layout being recalled */ struct pnfs_segment segment; /*< Segment to recall */ struct glist_head state_list; /*< List of states affected by this layout */ void *recall_cookie; /*< Cookie returned to FSAL on return of last segment satisfying the layout */ }; /** * @brief Blocking lock cookie entry * * Management of lce_refcount * ========================== * * * state_add_grant_cookie * * creates a reference. * * * state_find_grant * * gets a reference * * * state_complete_grant * * always releases 1 reference it releases a 2nd reference when the * call instance is the first to actually try and complete the grant * * * state_release_grant * * always releases 1 reference it releases a 2nd reference when the * call instance is the first to actually try and release the grant * * * state_cancel_grant * * calls cancel_blocked_lock, which will release the initial * reference * * * cancel_blocked_lock * * releases 1 reference if cookie exists called by * state_cancel_grant also called by unlock, cancel, sm_notify */ struct state_cookie_entry_t { struct fsal_obj_handle *sce_obj; /*< Associated file */ state_lock_entry_t *sce_lock_entry; /*< Associated lock */ void *sce_cookie; /*< Cookie data */ int sce_cookie_size; /*< Length of cookie */ }; /* * Structures for state async processing * */ /** * @brief Asynchronous state function */ typedef void (state_async_func_t) (state_async_queue_t *arg); /** * @brief Data for asynchronous NLM calls */ typedef struct state_nlm_async_data_t { state_nlm_client_t *nlm_async_host; /*< The client */ void *nlm_async_key; /*< Identifying key */ union { nfs_res_t nlm_async_res; /*< Asynchronous response */ nlm4_testargs nlm_async_grant; /*< Arguments for grant */ } nlm_async_args; } state_nlm_async_data_t; /** * @brief Asynchronous blocking lock data */ typedef struct state_async_block_data_t { state_lock_entry_t *state_async_lock_entry; /*< Associated lock */ } state_async_block_data_t; /** * @brief Queue of asynchronous events */ struct state_async_queue_t { struct glist_head state_async_glist; /*< List of events */ state_async_func_t *state_async_func; /*< Processing function */ union { state_nlm_async_data_t state_nlm_async_data; /*< Data for operation */ void *state_no_data; /*< Dummy pointer */ } state_async_data; }; /** * @brief Start of grace period * * @note This looks specific to SONAS and ought not to be in the top * level SAL files. (The SAL needs more A to support this kind * of thing.) */ #define EVENT_JUST_GRACE 0 /* Just start grace period */ #define EVENT_CLEAR_BLOCKED 1 /* Start grace period, and clear blocked locks */ #define EVENT_RELEASE_IP 2 /* Start grace period, clear blocked locks, and release all locks */ #define EVENT_UPDATE_CLIENTS 3 /* Start grace period, clear blocked locks, release all locks, and update clients list. */ #define EVENT_TAKE_NODEID 4 /* Start grace period, clear blocked locks, release all locks, and update clients using node id. */ #define EVENT_TAKE_IP 5 /* Start grace period, clear blocked locks, release all locks, and update clients using IP address. */ typedef struct nfs_grace_start { int event; /*< Reason for grace period, see EVENT_nnn */ int nodeid; /*< Node from which we are taking over */ char *ipaddr; /*< IP of failed node */ } nfs_grace_start_t; /* Memory pools */ extern pool_t *state_owner_pool; /*< Pool for NFSv4 files's open owner */ #ifdef DEBUG_SAL extern struct glist_head state_v4_all; extern struct glist_head state_owners_all; #endif #endif /* SAL_DATA_H */ /** @} */ nfs-ganesha-2.6.0/src/include/sal_functions.h000066400000000000000000000760771324272410200211610ustar00rootroot00000000000000/* * * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup SAL State abstraction layer * @{ */ /** * @file sal_functions.h * @brief Routines in the state abstraction layer * @note not called by other header files. */ #ifndef SAL_FUNCTIONS_H #define SAL_FUNCTIONS_H #include #include "sal_data.h" #include "fsal.h" /** * @brief Divisions in state and clientid tables. */ #define PRIME_STATE 17 /***************************************************************************** * * Misc functions * *****************************************************************************/ const char *state_err_str(state_status_t err); state_status_t state_error_convert(fsal_status_t fsal_status); nfsstat4 nfs4_Errno_state(state_status_t error); nfsstat3 nfs3_Errno_state(state_status_t error); const char *state_owner_type_to_str(state_owner_type_t type); bool different_owners(state_owner_t *owner1, state_owner_t *owner2); int display_owner(struct display_buffer *dspbuf, state_owner_t *owner); void inc_state_owner_ref(state_owner_t *owner); bool hold_state_owner(state_owner_t *owner); void dec_state_owner_ref(state_owner_t *owner); void free_state_owner(state_owner_t *owner); #define LogStateOwner(note, owner) \ do { \ if (isFullDebug(COMPONENT_STATE)) { \ char str[LOG_BUFF_LEN]; \ struct display_buffer dspbuf = { \ sizeof(str), str, str}; \ display_owner(&dspbuf, owner); \ LogFullDebug(COMPONENT_STATE, "%s%s", note, str); \ } \ } while (0) state_owner_t *get_state_owner(care_t care, state_owner_t *pkey, state_owner_init_t init_owner, bool_t *isnew); void state_wipe_file(struct fsal_obj_handle *obj); #ifdef DEBUG_SAL void dump_all_owners(void); #endif void state_release_export(struct gsh_export *exp); bool state_unlock_err_ok(state_status_t status); /** * @brief Initialize a state handle * * @param[in,out] ostate State handle to initialize * @param[in] type Type of handle * @param[in] obj Object owning handle * @return Return description */ static inline void state_hdl_init(struct state_hdl *ostate, object_file_type_t type, struct fsal_obj_handle *obj) { memset(ostate, 0, sizeof(*ostate)); PTHREAD_RWLOCK_init(&ostate->state_lock, NULL); switch (type) { case REGULAR_FILE: glist_init(&ostate->file.list_of_states); glist_init(&ostate->file.layoutrecall_list); glist_init(&ostate->file.lock_list); glist_init(&ostate->file.nlm_share_list); ostate->file.obj = obj; break; case DIRECTORY: glist_init(&ostate->dir.export_roots); break; default: break; } } /***************************************************************************** * * 9P State functions * *****************************************************************************/ #ifdef _USE_9P int compare_9p_owner(state_owner_t *owner1, state_owner_t *owner2); int compare_9p_owner_key(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2); int display_9p_owner(struct display_buffer *dspbuf, state_owner_t *owner); int display_9p_owner_key(struct gsh_buffdesc *buff, char *str); int display_9p_owner_val(struct gsh_buffdesc *buff, char *str); uint32_t _9p_owner_value_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key); uint64_t _9p_owner_rbt_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key); state_owner_t *get_9p_owner(struct sockaddr_storage *client_addr, uint32_t proc_id); int Init_9p_hash(void); #endif /****************************************************************************** * * NLM Owner functions * ******************************************************************************/ void free_nsm_client(state_nsm_client_t *client); /* These refcount functions must not be called holding the ssc_mutex */ void inc_nsm_client_ref(state_nsm_client_t *client); void dec_nsm_client_ref(state_nsm_client_t *client); int display_nsm_client(struct display_buffer *dspbuf, state_nsm_client_t *key); int display_nsm_client_key(struct gsh_buffdesc *buff, char *str); int compare_nsm_client(state_nsm_client_t *client1, state_nsm_client_t *client2); int compare_nsm_client_key(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2); uint32_t nsm_client_value_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key); uint64_t nsm_client_rbt_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key); state_nsm_client_t *get_nsm_client(care_t care, SVCXPRT *xprt, char *caller_name); void inc_nlm_client_ref(state_nlm_client_t *client); void dec_nlm_client_ref(state_nlm_client_t *client); int display_nlm_client_val(struct gsh_buffdesc *buff, char *str); int display_nlm_client_key(struct gsh_buffdesc *buff, char *str); int compare_nlm_client(state_nlm_client_t *client1, state_nlm_client_t *client2); int compare_nlm_client_key(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2); uint32_t nlm_client_value_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key); uint64_t nlm_client_rbt_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key); state_nlm_client_t *get_nlm_client(care_t care, SVCXPRT *xprt, state_nsm_client_t *nsm_client, char *caller_name); void free_nlm_owner(state_owner_t *powner); int display_nlm_owner(struct display_buffer *dspbuf, state_owner_t *owner); int display_nlm_owner_val(struct gsh_buffdesc *buff, char *str); int display_nlm_owner_key(struct gsh_buffdesc *buff, char *str); int compare_nlm_owner(state_owner_t *owner1, state_owner_t *owner2); int compare_nlm_owner_key(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2); uint32_t nlm_owner_value_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key); uint64_t nlm_owner_rbt_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key); state_owner_t *get_nlm_owner(care_t care, state_nlm_client_t *client, netobj *oh, uint32_t svid); int Init_nlm_hash(void); /****************************************************************************** * * NLM State functions * ******************************************************************************/ int display_nlm_state(struct display_buffer *dspbuf, state_t *key); int compare_nlm_state(state_t *state1, state_t *state2); int Init_nlm_state_hash(void); void dec_nlm_state_ref(state_t *state); int get_nlm_state(enum state_type state_type, struct fsal_obj_handle *state_obj, state_owner_t *state_owner, care_t care, uint32_t nsm_state, state_t **pstate); /****************************************************************************** * * NFS4 Client ID functions * ******************************************************************************/ nfsstat4 clientid_error_to_nfsstat(clientid_status_t err); static inline nfsstat4 clientid_error_to_nfsstat_no_expire(clientid_status_t err) { nfsstat4 rc = clientid_error_to_nfsstat(err); if (rc == NFS4ERR_EXPIRED) rc = NFS4ERR_STALE_CLIENTID; return rc; } const char *clientid_error_to_str(clientid_status_t err); int nfs_Init_client_id(void); clientid_status_t nfs_client_id_get_unconfirmed(clientid4 clientid, nfs_client_id_t **pclient_rec); clientid_status_t nfs_client_id_get_confirmed(clientid4 clientid, nfs_client_id_t **client_rec); nfs_client_id_t *create_client_id(clientid4 clientid, nfs_client_record_t *client_record, nfs_client_cred_t *credential, uint32_t minorversion); clientid_status_t nfs_client_id_insert(nfs_client_id_t *clientid); int remove_confirmed_client_id(nfs_client_id_t *clientid); int remove_unconfirmed_client_id(nfs_client_id_t *clientid); clientid_status_t nfs_client_id_confirm(nfs_client_id_t *clientid, log_components_t component); bool clientid_has_state(nfs_client_id_t *clientid); bool nfs_client_id_expire(nfs_client_id_t *clientid, bool make_stale); #define DISPLAY_CLIENTID_SIZE 36 int display_clientid(struct display_buffer *dspbuf, clientid4 clientid); clientid4 new_clientid(void); void new_clientid_verifier(char *verf); int display_client_id_key(struct gsh_buffdesc *buff, char *str); int display_client_id_val(struct gsh_buffdesc *buff, char *str); int compare_client_id(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2); uint64_t client_id_rbt_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key); uint32_t client_id_value_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key); int display_client_id_rec(struct display_buffer *dspbuf, nfs_client_id_t *clientid); #define CLIENTNAME_BUFSIZE (NFS4_OPAQUE_LIMIT * 2 + 1) int display_clientid_name(struct display_buffer *dspbuf, nfs_client_id_t *clientid); void free_client_id(nfs_client_id_t *clientid); void nfs41_foreach_client_callback(bool(*cb) (nfs_client_id_t *cl, void *state), void *state); bool client_id_has_state(nfs_client_id_t *clientid); int32_t inc_client_id_ref(nfs_client_id_t *clientid); int32_t dec_client_id_ref(nfs_client_id_t *clientid); int display_client_record(struct display_buffer *dspbuf, nfs_client_record_t *record); void free_client_record(nfs_client_record_t *record); int32_t inc_client_record_ref(nfs_client_record_t *record); int32_t dec_client_record_ref(nfs_client_record_t *record); int display_client_record_key(struct gsh_buffdesc *buff, char *str); int display_client_record_val(struct gsh_buffdesc *buff, char *str); int compare_client_record(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2); uint64_t client_record_rbt_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key); uint32_t client_record_value_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key); nfs_client_record_t *get_client_record(const char *const value, const size_t len, const uint32_t pnfs_flags, const uint32_t server_addr); /****************************************************************************** * * NFS4.1 Session ID functions * ******************************************************************************/ int display_session_id(struct display_buffer *dspbuf, char *session_id); int display_session(struct display_buffer *dspbuf, nfs41_session_t *session); int32_t inc_session_ref(nfs41_session_t *session); int32_t dec_session_ref(nfs41_session_t *session); int display_session_id_key(struct gsh_buffdesc *buff, char *str); int display_session_id_val(struct gsh_buffdesc *buff, char *str); int compare_session_id(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2); uint32_t session_id_value_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key); uint64_t session_id_rbt_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key); int nfs41_Init_session_id(void); int nfs41_Session_Set(nfs41_session_t *session_data); int nfs41_Session_Get_Pointer(char sessionid[NFS4_SESSIONID_SIZE], nfs41_session_t **session_data); int nfs41_Session_Del(char sessionid[NFS4_SESSIONID_SIZE]); void nfs41_Build_sessionid(clientid4 *clientid, char *sessionid); void nfs41_Session_PrintAll(void); /****************************************************************************** * * NFSv4 Stateid functions * ******************************************************************************/ #define DISPLAY_STATEID_OTHER_SIZE (DISPLAY_CLIENTID_SIZE + 72) int display_stateid_other(struct display_buffer *dspbuf, char *other); int display_stateid(struct display_buffer *dspbuf, state_t *state); /* 17 is 7 for " seqid=" and 10 for MAX_UINT32 digits */ #define DISPLAY_STATEID4_SIZE (DISPLAY_STATEID_OTHER_SIZE + 17) int display_stateid4(struct display_buffer *dspbuf, stateid4 *stateid); void nfs4_BuildStateId_Other(nfs_client_id_t *clientid, char *other); #define STATEID_NO_SPECIAL 0 /*< No special stateids */ #define STATEID_SPECIAL_ALL_0 2 /*< Allow anonymous */ #define STATEID_SPECIAL_ALL_1 4 /*< Allow read-bypass */ #define STATEID_SPECIAL_CURRENT 8 /*< Allow current */ /* The following flag tells nfs4_Check_Stateid this is a close call * and to ignore stateid that have valid clientid portion, but the * counter portion doesn't reference a currently open file. */ #define STATEID_SPECIAL_CLOSE_40 0x40 #define STATEID_SPECIAL_CLOSE_41 0x80 #define STATEID_SPECIAL_ANY 0x3F #define STATEID_SPECIAL_FOR_LOCK (STATEID_SPECIAL_CURRENT) #define STATEID_SPECIAL_FOR_CLOSE_40 (STATEID_SPECIAL_CLOSE_40) #define STATEID_SPECIAL_FOR_CLOSE_41 (STATEID_SPECIAL_CLOSE_41 | \ STATEID_SPECIAL_CURRENT) nfsstat4 nfs4_Check_Stateid(stateid4 *stateid, struct fsal_obj_handle *fsal_obj, state_t **state, compound_data_t *data, int flags, seqid4 owner_seqid, bool check_seqid, const char *tag); void update_stateid(state_t *state, stateid4 *stateid, compound_data_t *data, const char *tag); int nfs4_Init_state_id(void); /** * @brief Take a reference on state_t * * @param[in] state The state_t to ref */ static inline void inc_state_t_ref(struct state_t *state) { (void) atomic_inc_int32_t(&state->state_refcount); } void dec_nfs4_state_ref(struct state_t *state); /** * @brief Relinquish a reference on any State * * @param[in] state The NLM State to release */ static inline void dec_state_t_ref(struct state_t *state) { #ifdef _USE_NLM if (state->state_type == STATE_TYPE_NLM_LOCK || state->state_type == STATE_TYPE_NLM_SHARE) dec_nlm_state_ref(state); else #endif /* _USE_NLM */ dec_nfs4_state_ref(state); } state_status_t nfs4_State_Set(state_t *state_data); struct state_t *nfs4_State_Get_Pointer(char *other); bool nfs4_State_Del(state_t *state); void nfs_State_PrintAll(void); struct state_t *nfs4_State_Get_Obj(struct fsal_obj_handle *obj, state_owner_t *owner); int display_state_id_val(struct gsh_buffdesc *buff, char *str); int display_state_id_key(struct gsh_buffdesc *buff, char *str); uint32_t state_id_value_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key); uint64_t state_id_rbt_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key); /****************************************************************************** * * NFSv4 Lease functions * ******************************************************************************/ int reserve_lease(nfs_client_id_t *clientid); void update_lease(nfs_client_id_t *clientid); bool valid_lease(nfs_client_id_t *clientid); /****************************************************************************** * * NFSv4 Owner functions * ******************************************************************************/ void uncache_nfs4_owner(struct state_nfs4_owner_t *nfs4_owner); void free_nfs4_owner(state_owner_t *owner); int display_nfs4_owner(struct display_buffer *dspbuf, state_owner_t *owner); int display_nfs4_owner_val(struct gsh_buffdesc *buff, char *str); int display_nfs4_owner_key(struct gsh_buffdesc *buff, char *str); int compare_nfs4_owner(state_owner_t *owner1, state_owner_t *owner2); int compare_nfs4_owner_key(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2); uint32_t nfs4_owner_value_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key); uint64_t nfs4_owner_rbt_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key); /** * @brief Convert an open_owner to an owner name * * @param[in] nfsowner Open owner as specified in NFS * @param[out] name_owner Name used as key in owner table */ static inline void convert_nfs4_open_owner(open_owner4 *nfsowner, state_nfs4_owner_name_t *name_owner) { name_owner->son_owner_len = nfsowner->owner.owner_len; name_owner->son_owner_val = nfsowner->owner.owner_val; } /** * @brief Convert a lock_owner to an owner name * * @param[in] nfsowner Open owner as specified in NFS * @param[out] name_owner Name used as key in owner table */ static inline void convert_nfs4_lock_owner(lock_owner4 *nfsowner, state_nfs4_owner_name_t *name_owner) { name_owner->son_owner_len = nfsowner->owner.owner_len; name_owner->son_owner_val = nfsowner->owner.owner_val; } void nfs4_owner_PrintAll(void); state_owner_t *create_nfs4_owner(state_nfs4_owner_name_t *name, nfs_client_id_t *clientid, state_owner_type_t type, state_owner_t *related_owner, unsigned int init_seqid, bool_t *pisnew, care_t care, bool_t confirm); int Init_nfs4_owner(void); void Process_nfs4_conflict(/* NFS v4 Lock4denied structure to fill in */ LOCK4denied * denied, /* owner that holds conflicting lock */ state_owner_t *holder, /* description of conflicting lock */ fsal_lock_param_t *conflict); void Release_nfs4_denied(LOCK4denied *denied); void Copy_nfs4_denied(LOCK4denied *denied_dst, LOCK4denied *denied_src); void Copy_nfs4_state_req(state_owner_t *owner, seqid4 seqid, nfs_argop4 *args, struct fsal_obj_handle *obj, nfs_resop4 *resp, const char *tag); bool Check_nfs4_seqid(state_owner_t *owner, seqid4 seqid, nfs_argop4 *args, struct fsal_obj_handle *obj, nfs_resop4 *resp, const char *tag); /** * @brief Determine if an NFS v4 owner has state associated with it * * Note that this function is racy and is only suitable for calling * from places that should not have other activity pending against * the owner. Currently it is only called from setclientid which should * be fine. * * @param[in] owner The owner of interest * * @retval true if the owner has state */ static inline bool owner_has_state(state_owner_t *owner) { bool live_state; struct state_nfs4_owner_t *nfs4_owner = &owner->so_owner.so_nfs4_owner; /* If the owner is on the cached owners list, there can't be * active state. */ if (atomic_fetch_time_t(&nfs4_owner->so_cache_expire) != 0) return false; PTHREAD_MUTEX_lock(&owner->so_mutex); live_state = !glist_empty(&nfs4_owner->so_state_list); PTHREAD_MUTEX_unlock(&owner->so_mutex); return live_state; } /****************************************************************************** * * Lock functions * ******************************************************************************/ static inline int display_lock_cookie(struct display_buffer *dspbuf, struct gsh_buffdesc *buff) { return display_opaque_value(dspbuf, buff->addr, buff->len); } int display_lock_cookie_key(struct gsh_buffdesc *buff, char *str); int display_lock_cookie_val(struct gsh_buffdesc *buff, char *str); int compare_lock_cookie_key(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2); uint32_t lock_cookie_value_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key); uint64_t lock_cookie_rbt_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *key); state_status_t state_lock_init(void); void log_lock(log_components_t component, log_levels_t debug, const char *reason, struct fsal_obj_handle *obj, state_owner_t *owner, fsal_lock_param_t *lock, char *file, int line, char *function); #define LogLock(component, debug, reason, obj, owner, lock) \ log_lock(component, debug, reason, obj, owner, lock, \ (char *) __FILE__, __LINE__, (char *) __func__) void dump_all_locks(const char *label); state_status_t state_add_grant_cookie(struct fsal_obj_handle *obj, void *cookie, int cookie_size, state_lock_entry_t *lock_entry, state_cookie_entry_t **cookie_entry); state_status_t state_find_grant(void *cookie, int cookie_size, state_cookie_entry_t **cookie_entry); void state_complete_grant(state_cookie_entry_t *cookie_entry); state_status_t state_cancel_grant(state_cookie_entry_t *cookie_entry); state_status_t state_release_grant(state_cookie_entry_t *cookie_entry); state_status_t state_test(struct fsal_obj_handle *obj, state_t *state, state_owner_t *owner, fsal_lock_param_t *lock, /* owner that holds conflicting lock */ state_owner_t **holder, /* description of conflicting lock */ fsal_lock_param_t *conflict); state_status_t state_lock(struct fsal_obj_handle *obj, state_owner_t *owner, state_t *state, state_blocking_t blocking, state_block_data_t *block_data, fsal_lock_param_t *lock, /* owner that holds conflicting lock */ state_owner_t **holder, /* description of conflicting lock */ fsal_lock_param_t *conflict); state_status_t state_unlock(struct fsal_obj_handle *obj, state_t *state, state_owner_t *owner, bool state_applies, int32_t nsm_state, fsal_lock_param_t *lock); state_status_t state_cancel(struct fsal_obj_handle *obj, state_owner_t *owner, fsal_lock_param_t *lock); state_status_t state_nlm_notify(state_nsm_client_t *nsmclient, bool state_applies, int32_t state); void state_nfs4_owner_unlock_all(state_owner_t *owner); void state_export_unlock_all(void); bool state_lock_wipe(struct state_hdl *hstate); void cancel_all_nlm_blocked(void); /****************************************************************************** * * NFSv4 State Management functions * ******************************************************************************/ #define state_add_impl(o, t, d, i, s, r) \ _state_add_impl(o, t, d, i, s, r, __func__, __LINE__) state_status_t _state_add_impl(struct fsal_obj_handle *obj, enum state_type state_type, union state_data *state_data, state_owner_t *owner_input, state_t **state, struct state_refer *refer, const char *func, int line); #define state_add(o, t, d, i, s, r) \ _state_add(o, t, d, i, s, r, __func__, __LINE__) state_status_t _state_add(struct fsal_obj_handle *obj, enum state_type state_type, union state_data *state_data, state_owner_t *owner_input, state_t **state, struct state_refer *refer, const char *func, int line); state_status_t state_set(state_t *state); #define state_del_locked(s) _state_del_locked(s, __func__, __LINE__) void _state_del_locked(state_t *state, const char *func, int line); void state_del(state_t *state); /** * @brief Get a reference to the obj owning a state * * @note state_mutex MUST be held * * @param[in] state State to get from * @return obj handle on success */ static inline struct fsal_obj_handle *get_state_obj_ref_locked(state_t *state) { if (state->state_obj) { state->state_obj->obj_ops.get_ref(state->state_obj); } return state->state_obj; } /** * @brief Get a reference to the obj owning a state * * Takes state_mutex, so it should not be held. * * @param[in] state State to get from * @return obj handle on success */ static inline struct fsal_obj_handle *get_state_obj_ref(state_t *state) { struct fsal_obj_handle *obj; PTHREAD_MUTEX_lock(&state->state_mutex); obj = get_state_obj_ref_locked(state); PTHREAD_MUTEX_unlock(&state->state_mutex); return obj; } static inline struct gsh_export *get_state_export_ref(state_t *state) { struct gsh_export *export = NULL; PTHREAD_MUTEX_lock(&state->state_mutex); if (state->state_export != NULL && export_ready(state->state_export)) { get_gsh_export_ref(state->state_export); export = state->state_export; } PTHREAD_MUTEX_unlock(&state->state_mutex); return export; } static inline bool state_same_export(state_t *state, struct gsh_export *export) { bool same = false; PTHREAD_MUTEX_lock(&state->state_mutex); if (state->state_export != NULL) same = state->state_export == export; PTHREAD_MUTEX_unlock(&state->state_mutex); return same; } static inline uint16_t state_export_id(state_t *state) { uint16_t export_id = UINT16_MAX; PTHREAD_MUTEX_lock(&state->state_mutex); if (state->state_export != NULL) export_id = state->state_export->export_id; PTHREAD_MUTEX_unlock(&state->state_mutex); return export_id; } static inline state_owner_t *get_state_owner_ref(state_t *state) { state_owner_t *owner = NULL; PTHREAD_MUTEX_lock(&state->state_mutex); if (state->state_owner != NULL) { owner = state->state_owner; inc_state_owner_ref(owner); } PTHREAD_MUTEX_unlock(&state->state_mutex); return owner; } static inline bool state_owner_confirmed(state_t *state) { bool confirmed = false; PTHREAD_MUTEX_lock(&state->state_mutex); if (state->state_owner != NULL) { confirmed = state->state_owner->so_owner.so_nfs4_owner.so_confirmed; } PTHREAD_MUTEX_unlock(&state->state_mutex); return confirmed; } static inline bool state_same_owner(state_t *state, state_owner_t *owner) { bool same = false; PTHREAD_MUTEX_lock(&state->state_mutex); if (state->state_owner != NULL) same = state->state_owner == owner; PTHREAD_MUTEX_unlock(&state->state_mutex); return same; } bool get_state_obj_export_owner_refs(state_t *state, struct fsal_obj_handle **obj, struct gsh_export **export, state_owner_t **owner); void state_nfs4_state_wipe(struct state_hdl *ostate); enum nfsstat4 release_lock_owner(state_owner_t *owner); void release_openstate(state_owner_t *owner); void state_export_release_nfs4_state(void); void revoke_owner_delegs(state_owner_t *client_owner); #ifdef DEBUG_SAL void dump_all_states(void); #endif /****************************************************************************** * * Delegations functions * ******************************************************************************/ state_status_t acquire_lease_lock(struct state_hdl *ostate, state_owner_t *owner, state_t *state); state_status_t release_lease_lock(struct fsal_obj_handle *obj, state_t *state); bool init_deleg_heuristics(struct fsal_obj_handle *obj); bool deleg_supported(struct fsal_obj_handle *obj, struct fsal_export *fsal_export, struct export_perms *export_perms, uint32_t share_access); bool can_we_grant_deleg(struct state_hdl *ostate, state_t *open_state); bool should_we_grant_deleg(struct state_hdl *ostate, nfs_client_id_t *client, state_t *open_state, OPEN4args *args, OPEN4resok *resok, state_owner_t *owner, bool *prerecall); void init_new_deleg_state(union state_data *deleg_state, open_delegation_type4 sd_type, nfs_client_id_t *clientid); void deleg_heuristics_recall(struct fsal_obj_handle *obj, state_owner_t *owner, struct state_t *deleg); void get_deleg_perm(nfsace4 *permissions, open_delegation_type4 type); void update_delegation_stats(struct state_hdl *ostate, state_owner_t *owner, struct state_t *deleg); state_status_t delegrecall_impl(struct fsal_obj_handle *obj); nfsstat4 deleg_revoke(struct fsal_obj_handle *obj, struct state_t *deleg_state); void state_deleg_revoke(struct fsal_obj_handle *obj, state_t *state); bool state_deleg_conflict(struct fsal_obj_handle *obj, bool write); /****************************************************************************** * * Layout functions * ******************************************************************************/ state_status_t state_add_segment(state_t *state, struct pnfs_segment *segment, void *fsal_data, bool return_on_close); state_status_t state_delete_segment(state_layout_segment_t *segment); state_status_t state_lookup_layout_state(struct fsal_obj_handle *obj, state_owner_t *owner, layouttype4 type, state_t **state); void revoke_owner_layouts(state_owner_t *client_owner); /****************************************************************************** * * Share functions * ******************************************************************************/ #define OPEN4_SHARE_ACCESS_NONE 0 enum share_bypass_modes { SHARE_BYPASS_NONE, SHARE_BYPASS_READ, SHARE_BYPASS_V3_WRITE }; state_status_t state_nlm_share(struct fsal_obj_handle *obj, int share_access, int share_deny, state_owner_t *owner, state_t *state, bool reclaim, bool unshare); void state_share_wipe(struct state_hdl *ostate); void state_export_unshare_all(void); /****************************************************************************** * * Async functions * ******************************************************************************/ /* Schedule Async Work */ state_status_t state_async_schedule(state_async_queue_t *arg); /* Schedule lock notifications */ state_status_t state_block_schedule(state_block_data_t *block); /* Signal Async Work */ void signal_async_work(void); state_status_t state_async_init(void); state_status_t state_async_shutdown(void); void grant_blocked_lock_upcall(struct fsal_obj_handle *obj, void *owner, fsal_lock_param_t *lock); void available_blocked_lock_upcall(struct fsal_obj_handle *obj, void *owner, fsal_lock_param_t *lock); void process_blocked_lock_upcall(state_block_data_t *block_data); void blocked_lock_polling(struct fridgethr_context *ctx); /****************************************************************************** * * NFSv4 Recovery functions * ******************************************************************************/ void nfs_start_grace(nfs_grace_start_t *gsp); bool nfs_in_grace(void); void nfs_try_lift_grace(void); void nfs4_add_clid(nfs_client_id_t *); void nfs4_rm_clid(nfs_client_id_t *); void nfs4_recovery_reclaim_complete(nfs_client_id_t *clientid); void nfs4_chk_clid(nfs_client_id_t *); void nfs4_recovery_cleanup(void); void nfs4_recovery_init(void); void nfs4_record_revoke(nfs_client_id_t *, nfs_fh4 *); bool nfs4_check_deleg_reclaim(nfs_client_id_t *, nfs_fh4 *); /** * @brief Check to see if an object is a junction * * @param[in] obj Object to check * @return true if junction, false otherwise */ static inline bool obj_is_junction(struct fsal_obj_handle *obj) { bool res = false; if (obj->type != DIRECTORY) return false; PTHREAD_RWLOCK_rdlock(&obj->state_hdl->state_lock); if ((obj->state_hdl->dir.junction_export != NULL || atomic_fetch_int32_t(&obj->state_hdl->dir.exp_root_refcount) != 0)) res = true; PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); return res; } typedef clid_entry_t * (*add_clid_entry_hook)(char *); typedef rdel_fh_t * (*add_rfh_entry_hook)(clid_entry_t *, char *); struct nfs4_recovery_backend { void (*recovery_init)(void); void (*recovery_cleanup)(void); void (*recovery_read_clids)(nfs_grace_start_t *gsp, add_clid_entry_hook add_clid, add_rfh_entry_hook add_rfh); void (*add_clid)(nfs_client_id_t *); void (*rm_clid)(nfs_client_id_t *); void (*add_revoke_fh)(nfs_client_id_t *, nfs_fh4 *); }; void fs_backend_init(struct nfs4_recovery_backend **); void fs_ng_backend_init(struct nfs4_recovery_backend **); #ifdef USE_RADOS_RECOV int rados_kv_set_param_from_conf(config_file_t, struct config_error_type *); void rados_kv_backend_init(struct nfs4_recovery_backend **); void rados_ng_backend_init(struct nfs4_recovery_backend **); #endif #endif /* SAL_FUNCTIONS_H */ /** @} */ nfs-ganesha-2.6.0/src/include/sal_shared.h000066400000000000000000000026161324272410200204030ustar00rootroot00000000000000/* * * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup SAL State abstraction layer * @{ */ /** * @file sal_shared.h * @brief Data structures for state management. */ #ifndef SAL_SHARED_H #define SAL_SHARED_H /** * @brief Type of state */ enum state_type { STATE_TYPE_NONE = 0, STATE_TYPE_SHARE = 1, STATE_TYPE_DELEG = 2, STATE_TYPE_LOCK = 3, STATE_TYPE_LAYOUT = 4, STATE_TYPE_NLM_LOCK = 5, STATE_TYPE_NLM_SHARE = 6, STATE_TYPE_9P_FID = 7, }; #endif /* SAL_SHARED_H */ /** @} */ nfs-ganesha-2.6.0/src/include/server_stats.h000066400000000000000000000041241324272410200210160ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2013 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ /** * @defgroup Server statistics management * @{ */ /** * @file server_stats.h * @author Jim Lieb * @brief Server statistics */ #ifndef SERVER_STATS_H #define SERVER_STATS_H #include void server_stats_nfs_done(request_data_t *reqdata, int rc, bool dup); #ifdef _USE_9P void server_stats_9p_done(u8 msgtype, struct _9p_request_data *req9p); #endif void server_stats_io_done(size_t requested, size_t transferred, bool success, bool is_write); void server_stats_compound_done(int num_ops, int status); void server_stats_nfsv4_op_done(int proto_op, nsecs_elapsed_t start_time, int status); void server_stats_transport_done(struct gsh_client *client, uint64_t rx_bytes, uint64_t rx_pkt, uint64_t rx_err, uint64_t tx_bytes, uint64_t tx_pkt, uint64_t tx_err); /* For delegations */ void inc_grants(struct gsh_client *client); void dec_grants(struct gsh_client *client); void inc_revokes(struct gsh_client *client); void inc_recalls(struct gsh_client *client); void inc_failed_recalls(struct gsh_client *client); #endif /* !SERVER_STATS_H */ /** @} */ nfs-ganesha-2.6.0/src/include/server_stats_private.h000066400000000000000000000156651324272410200225640ustar00rootroot00000000000000 /* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2013 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * ------------- */ /** * @defgroup Server statistics management * @{ */ /** * @file server_stats.h * @author Jim Lieb * @brief Server statistics private interfaces */ #ifndef SERVER_STATS_PRIVATE_H #define SERVER_STATS_PRIVATE_H #include "sal_data.h" /** * @brief Server request statistics * * These are the stats we keep */ /* Forward references to build pointers to private defs. */ struct nfsv3_stats; struct mnt_stats; struct nlmv4_stats; struct rquota_stats; struct nfsv40_stats; struct nfsv41_stats; struct nfsv42_stats; struct deleg_stats; struct _9p_stats; struct gsh_stats { struct nfsv3_stats *nfsv3; struct mnt_stats *mnt; struct nlmv4_stats *nlm4; struct rquota_stats *rquota; struct nfsv40_stats *nfsv40; struct nfsv41_stats *nfsv41; struct nfsv41_stats *nfsv42; struct deleg_stats *deleg; struct _9p_stats *_9p; }; /** * @brief Server by client IP statistics * * toplevel structure for statistics gathering. This is * only shared between client_mgr.c and server_stats.c * client_mgr.c needs to know about it to properly size * the allocation. * * NOTE: see below. The client member must be the * last because the gsh_client structure has * a variable length array at the end (for the * key). */ struct server_stats { struct gsh_stats st; struct gsh_client client; /* must be last element! */ }; /** * @brief Server by export id statistics * * Top level structure only shared between export_mgr.c and * server_stats.c */ struct export_stats { struct gsh_stats st; struct gsh_export export; }; #ifdef USE_DBUS /* Bits for introspect arg structures */ #define EXPORT_ID_ARG \ { \ .name = "exp_id",\ .type = "q", \ .direction = "in"\ } #define TIMESTAMP_REPLY \ { \ .name = "time", \ .type = "(tt)", \ .direction = "out" \ } #define IOSTATS_REPLY \ { \ .name = "read", \ .type = "(tttttt)",\ .direction = "out" \ }, \ { \ .name = "write", \ .type = "(tttttt)",\ .direction = "out" \ } #define TRANSPORT_REPLY \ { \ .name = "rx_bytes",\ .type = "(t)", \ .direction = "out" \ }, \ { \ .name = "rx_pkt", \ .type = "(t)", \ .direction = "out" \ }, \ { \ .name = "rx_err", \ .type = "(t)", \ .direction = "out" \ }, \ { \ .name = "tx_bytes",\ .type = "(t)", \ .direction = "out" \ }, \ { \ .name = "tx_pkt", \ .type = "(t)", \ .direction = "out" \ }, \ { \ .name = "tx_err", \ .type = "(t)", \ .direction = "out" \ } #define TOTAL_OPS_REPLY \ { \ .name = "op", \ .type = "a(st)", \ .direction = "out" \ } #define FSAL_OPS_REPLY \ { \ .name = "op", \ .type = "(qa(sq(tdtt)))", \ .direction = "out" \ } #define LAYOUTS_REPLY \ { \ .name = "getdevinfo", \ .type = "(ttt)", \ .direction = "out" \ }, \ { \ .name = "layout_get", \ .type = "(ttt)", \ .direction = "out" \ }, \ { \ .name = "layout_commit",\ .type = "(ttt)", \ .direction = "out" \ }, \ { \ .name = "layout_return",\ .type = "(ttt)", \ .direction = "out" \ }, \ { \ .name = "layout_recall",\ .type = "(ttt)", \ .direction = "out" \ } /* number of delegations, number of sent recalls, * number of failed recalls, number of revokes */ #define DELEG_REPLY \ { \ .name = "delegation_stats", \ .type = "(tttt)", \ .direction = "out" \ } #define NFS_ALL_IO_REPLY_ARRAY_TYPE "(qs(tttttt)(tttttt))" #define NFS_ALL_IO_REPLY \ { \ .name = "iostats", \ .type = DBUS_TYPE_ARRAY_AS_STRING \ NFS_ALL_IO_REPLY_ARRAY_TYPE, \ .direction = "out" \ } \ #define _9P_OP_ARG \ { \ .name = "_9p_opname",\ .type = "s", \ .direction = "in" \ } #define OP_STATS_REPLY \ { \ .name = "op_stats", \ .type = "(tt)", \ .direction = "out" \ } void server_stats_summary(DBusMessageIter * iter, struct gsh_stats *st); void server_dbus_v3_iostats(struct nfsv3_stats *v3p, DBusMessageIter *iter); void server_dbus_v40_iostats(struct nfsv40_stats *v40p, DBusMessageIter *iter); void server_dbus_v41_iostats(struct nfsv41_stats *v41p, DBusMessageIter *iter); void server_dbus_v41_layouts(struct nfsv41_stats *v41p, DBusMessageIter *iter); void server_dbus_v42_iostats(struct nfsv41_stats *v42p, DBusMessageIter *iter); void server_dbus_v42_layouts(struct nfsv41_stats *v42p, DBusMessageIter *iter); void server_dbus_delegations(struct deleg_stats *ds, DBusMessageIter *iter); void server_dbus_all_iostats(struct export_stats *export_statistics, DBusMessageIter *iter); void server_dbus_total_ops(struct export_stats *export_st, DBusMessageIter *iter); void global_dbus_total_ops(DBusMessageIter *iter); void server_dbus_fast_ops(DBusMessageIter *iter); void mdcache_dbus_show(DBusMessageIter *iter); void server_reset_stats(DBusMessageIter *iter); void reset_export_stats(void); void reset_client_stats(void); void reset_gsh_stats(struct gsh_stats *st); #ifdef _USE_9P void server_dbus_9p_iostats(struct _9p_stats *_9pp, DBusMessageIter *iter); void server_dbus_9p_transstats(struct _9p_stats *_9pp, DBusMessageIter *iter); void server_dbus_9p_tcpstats(struct _9p_stats *_9pp, DBusMessageIter *iter); void server_dbus_9p_rdmastats(struct _9p_stats *_9pp, DBusMessageIter *iter); void server_dbus_9p_opstats(struct _9p_stats *_9pp, u8 opcode, DBusMessageIter *iter); #endif extern struct glist_head fsal_list; #endif /* USE_DBUS */ void server_stats_free(struct gsh_stats *statsp); void server_stats_init(void); #endif /* !SERVER_STATS_PRIVATE_H */ /** @} */ nfs-ganesha-2.6.0/src/include/uid2grp.h000066400000000000000000000045141324272410200176510ustar00rootroot00000000000000/* * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @defgroup idmapper ID Mapper * * The ID Mapper module provides mapping between numerical user and * group IDs and NFSv4 style owner and group strings. * * @{ */ /** * @file idmapper.c * @brief Id mapping functions */ #ifndef UID2GRP_H #define UID2GRP_H #include #include #include #include "gsh_rpc.h" #include "gsh_types.h" /** * @brief Shared between idmapper.c and uid2grp_cache.c. If you * aren't in idmapper.c, leave these symbols alone. * * @{ */ typedef struct group_data { uid_t uid; struct gsh_buffdesc uname; gid_t gid; time_t epoch; int nbgroups; unsigned int refcount; pthread_mutex_t lock; gid_t *groups; } group_data_t; extern pthread_rwlock_t uid2grp_user_lock; void uid2grp_cache_init(void); void uid2grp_add_user(struct group_data *); bool uid2grp_lookup_by_uname(const struct gsh_buffdesc *, uid_t *, struct group_data **); bool uid2grp_lookup_by_uid(const uid_t, struct group_data **); void uid2grp_remove_by_uname(const struct gsh_buffdesc *); void uid2grp_remove_by_uid(const uid_t); void uid2grp_clear_cache(void); bool uid2grp(uid_t uid, struct group_data **); bool name2grp(const struct gsh_buffdesc *name, struct group_data **gdata); void uid2grp_unref(struct group_data *gdata); void uid2grp_hold_group_data(struct group_data *); void uid2grp_release_group_data(struct group_data *); #endif /* UID2GRP_H */ /** @} */ nfs-ganesha-2.6.0/src/libganeshaNFS.pc.cmake000066400000000000000000000005501324272410200205440ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=${prefix} libdir=${exec_prefix}/lib@LIB_SUFFIX@ includedir=${prefix}/include Name: libganeshaNFS Description: NFS-GANESHA's "fuselike" library Requires: Version: @GANESHA_MAJOR_VERSION@.@GANESHA_MINOR_VERSION@.@GANESHA_PATCH_LEVEL@ Libs: -L${exec_prefix}/lib@LIB_SUFFIX@ -lganeshaNFS Cflags: -I@INCLUDE_INSTALL_DIR@ nfs-ganesha-2.6.0/src/libntirpc/000077500000000000000000000000001324272410200164635ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/log/000077500000000000000000000000001324272410200152565ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/log/.gitignore000066400000000000000000000000141324272410200172410ustar00rootroot00000000000000test_liblog nfs-ganesha-2.6.0/src/log/CMakeLists.txt000066400000000000000000000007661324272410200200270ustar00rootroot00000000000000add_definitions( -D__USE_GNU -D _GNU_SOURCE ) if(USE_DBUS) include_directories( ${DBUS_INCLUDE_DIRS} ) endif(USE_DBUS) if(USE_LTTNG) include_directories( ${LTTNG_INCLUDE_DIR} ) endif(USE_LTTNG) ########### next target ############### SET(log_STAT_SRCS display.c log_functions.c ) add_library(log STATIC ${log_STAT_SRCS}) add_sanitizers(log) ########### next target ############### SET(test_liblog_SRCS test_display.c ) ########### install files ############### nfs-ganesha-2.6.0/src/log/display.c000066400000000000000000000365011324272410200170740ustar00rootroot00000000000000/* * Copyright IBM Corporation, 2012 * Contributor: Frank Filz * * -------------------------- * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * */ /** * @defgroup Display display_buffer implementation * @{ */ /** * @file display.c * @author Frank Filz * @brief Implementation of a buffer for constructing string messages. * * A variety of functions are provided to manipulate display buffers and * append various strings to the buffer. */ #include #include #include #include #include #include "display.h" /** * @brief Internal routine to compute the bytes remaining in a buffer. * * @param[in] dspbuf The buffer. * * @return the number of bytes remaining in the buffer. */ static inline int _display_buffer_remain(struct display_buffer *dspbuf) { /* Compute number of bytes remaining in buffer * (including space for null). */ return dspbuf->b_size - (dspbuf->b_current - dspbuf->b_start); } /** * @brief Compute the bytes remaining in a buffer. * * @param[in,out] dspbuf The buffer. * * @retval -1 if there is some problem rendering the buffer unusable. * @retval 0 if the buffer has overflowed. * @retval >0 indicates the bytes remaining (inlcuding one byte for '\0'). */ int display_buffer_remain(struct display_buffer *dspbuf) { /* If no buffer, indicate problem. */ if (dspbuf == NULL || dspbuf->b_start == NULL || dspbuf->b_size == 0) { errno = EFAULT; return -1; } /* If b_current is invalid, set it to b_start */ if (dspbuf->b_current == NULL || dspbuf->b_current < dspbuf->b_start || dspbuf->b_current > (dspbuf->b_start + dspbuf->b_size)) dspbuf->b_current = dspbuf->b_start; /* Buffer is too small, just make it an emptry * string and mark the buffer as overrun. */ if (dspbuf->b_size < 4) { dspbuf->b_start[0] = '\0'; dspbuf->b_current = dspbuf->b_start + dspbuf->b_size; return 0; } /* Compute number of bytes remaining in buffer * (including space for null). */ return _display_buffer_remain(dspbuf); } /** * @brief Finish up a buffer after overflowing it. * * @param[in,out] dspbuf The buffer. * @param[in] ptr Proposed position in the buffer for the "..." string. * * This routine will validate the final character that will remain in the buffer * prior to the "..." to make sure it is not a partial UTF-8 character. If so, * it will place the "..." such that it replaces the partial UTF-8 character. * The result will be a proper UTF-8 string (assuming the rest of the string is * valid UTF-8...). * * Caller will make sure sufficent room is available in buffer for the "..." * string. This leaves it up to the caller whether prt must be backed up * from b_current, or if the caller knows the next item won't fit in the buffer * but that there is room for the "..." string. */ void _display_complete_overflow(struct display_buffer *dspbuf, char *ptr) { int utf8len; char *end; /* ptr argument points after last byte that we will retain * set 'end' to that and 'ptr' to inspect previous byte if possible */ end = ptr; if (ptr > dspbuf->b_start) ptr--; /* Now ptr points to last byte that will remain part of string. * Next we need to check if this byte is the end of a valid UTF-8 * character. */ while ((ptr > dspbuf->b_start) && ((*ptr & 0xc0) == 0x80)) ptr--; /* Now ptr points to the start of a valid UTF-8 character or the string * was rather corrupt, there is no valid start of a UTF-8 character. */ /* Compute the length of the last UTF-8 character */ utf8len = end - ptr; /* Check if last character is valid UTF-8, for multibyte characters the * first byte is a string of 1 bits followed by a 0 bit. So for example, * a 2 byte character leads off with 110xxxxxxx, so we mask with * 11100000 (0xe0) and test for 11000000 (0xc0). */ if ((((*ptr & 0x80) == 0x00) && (utf8len == 1)) || (((*ptr & 0xe0) == 0xc0) && (utf8len == 2)) || (((*ptr & 0xf0) == 0xe0) && (utf8len == 3)) || (((*ptr & 0xf8) == 0xf0) && (utf8len == 4)) || (((*ptr & 0xfc) == 0xf8) && (utf8len == 5)) || (((*ptr & 0xfe) == 0xfc) && (utf8len == 6))) { /* Last character before end is valid, increment ptr past it. */ ptr = end; } /* else last character is not valid, leave ptr to strip it. */ /* Now we know where to place the elipsis... */ strcpy(ptr, "..."); } /** * @brief Prepare to append to buffer. * * @param[in,out] dspbuf The buffer. * * @return the bytes remaining in the buffer. * * This routine validates the buffer, then checks if the buffer is already full * in which case it will mark the buffer as overflowed and finish up the buffer. * */ int display_start(struct display_buffer *dspbuf) { int b_left = display_buffer_remain(dspbuf); /* If buffer has already overflowed, or is invalid, return that. */ if (b_left <= 0) return b_left; /* If buffer is already full, indicate overflow now, and indicate * no space is left (so caller doesn't bother to do anything. */ if (b_left == 1) { /* Increment past end and finish buffer. */ dspbuf->b_current++; b_left--; /* Back up 4 bytes before last byte (note that b_current * points PAST the last byte of the buffer since the * buffer has overflowed). */ _display_complete_overflow(dspbuf, dspbuf->b_current - 4); } else { /* Some display functions might not put anything in the * buffer... */ *dspbuf->b_current = '\0'; } /* Indicate buffer is ok by returning b_left. */ return b_left; } /** * @brief Finish up a buffer after appending to it. * * @param[in,out] dspbuf The buffer. * * @return the bytes remaining in the buffer. * * After a buffer has been appended to, check for overflow. * * This should be called by every routine that actually copies bytes into a * display_buffer. It must not be called by routines that use other display * routines to build a buffer (since the last such routine executed will * have called this routine). * */ int display_finish(struct display_buffer *dspbuf) { /* display_buffer_remain will return the current number of bytes left in * the buffer. If this is 0, and we just appended to the buffer (i.e. * display_buffer_remain was NOT 0 before appending), then the last * append just overflowed the buffer (note that if it exactly filled the * buffer, display_buffer_remain would have returned 1). Since the * buffer just overflowed, the overflow will be indicated by truncating * the string to allow space for a three character "..." sequence. */ int b_left = display_buffer_remain(dspbuf); if (b_left != 0) return b_left; /* We validated above that buffer is at least 4 bytes... */ /* Back up 4 bytes before last byte (note that b_current points * PAST the last byte of the buffer since the buffer has overflowed). */ _display_complete_overflow(dspbuf, dspbuf->b_current - 4); return 0; } /** * @brief Force overflow on a buffer after appending to it. * * @param[in,out] dspbuf The buffer. * * @return the bytes remaining in the buffer. * * After a buffer has been appended to, check for overflow. * */ int display_force_overflow(struct display_buffer *dspbuf) { int b_left = display_buffer_remain(dspbuf); if (b_left <= 0) return b_left; if (b_left < 4) { /* There aren't at least 4 characters left, back up to allow for * them. If there aren't room for 3 more non-0 bytes in the * buffer, then (baring multi-byte UTF-8 charts), the "..." will * always be at the very end of the buffer, that is determined * by b_start + b_size, b_current is currently * b_start + b_size - b_left so instead of using b_current, we * just back up 4 bytes from the end of the buffer to make the * space. _display_complete_overflow will deal with the * possibility that a UTF-8 character ended up truncated as a * result. */ _display_complete_overflow(dspbuf, dspbuf->b_start + dspbuf->b_size - 4); } else { /* Otherwise just put the "..." at b_current */ _display_complete_overflow(dspbuf, dspbuf->b_current); } /* Mark buffer as overflowed. */ dspbuf->b_current = dspbuf->b_start + dspbuf->b_size; return 0; } /** * @brief Format a string into the buffer. * * @param[in,out] dspbuf The buffer. * @param[in] fmt The format string * @param[in] args The va_list args * * @return the bytes remaining in the buffer. * */ int display_vprintf(struct display_buffer *dspbuf, const char *fmt, va_list args) { int len; int b_left = display_start(dspbuf); if (b_left <= 0) return b_left; /* snprintf into the buffer no more than b_left bytes. snprintf assures * the buffer is null terminated (so will copy at most b_left * characters). */ len = vsnprintf(dspbuf->b_current, b_left, fmt, args); if (len >= b_left) { /* snprintf indicated that if the full string was printed, it * would have overflowed. By incrementing b_current by b_left, * b_current now points beyond the buffer and clearly marks the * buffer as full. */ dspbuf->b_current += b_left; } else { /* No overflow, move b_current to the end of the printf. */ dspbuf->b_current += len; } /* Finish up */ return display_finish(dspbuf); } /** * @brief Display a number of opaque bytes as a hex string. * * @param[in,out] dspbuf The buffer. * @param[in] value The bytes to display * @param[in] len The number of bytes to display * * @return the bytes remaining in the buffer. * */ int display_opaque_bytes(struct display_buffer *dspbuf, void *value, int len) { unsigned int i = 0; int b_left = display_start(dspbuf); if (b_left <= 0) return b_left; /* Check that the length is ok */ if (len < 0) return display_printf(dspbuf, "(invalid len=%d)", len); /* If the value is NULL, display NULL value. */ if (value == NULL) return display_cat(dspbuf, "(NULL)"); /* If the value is empty, display EMPTY value. */ if (len == 0) return display_cat(dspbuf, "(EMPTY)"); /* Indicate the value is a hex string. */ b_left = display_cat(dspbuf, "0x"); /* Display the value one hex byte at a time. */ for (i = 0; i < len && b_left > 0; i++) b_left = display_printf(dspbuf, "%02x", ((unsigned char *)value)[i]); /* Finish up */ return display_finish(dspbuf); } /** * @brief Display a number of opaque bytes as a hex string, limiting the number * of bytes used from the opaque value. * * @param[in,out] dspbuf The buffer. * @param[in] value The bytes to display * @param[in] len The number of bytes to display * @param[in] max Max number of bytes from the opaque value to display * * @return the bytes remaining in the buffer. * * This routine also attempts to detect a printable value and if so, displays * that instead of converting value to a hex string. It uses min(len,max) as * the number of bytes to use from the opaque value. * */ int display_opaque_value_max(struct display_buffer *dspbuf, void *value, int len, int max) { unsigned int i = 0; int b_left = display_start(dspbuf); int cpy = len; if (b_left <= 0) return b_left; /* Check that the length is ok */ if (len < 0) return display_printf(dspbuf, "(invalid len=%d)", len); /* If the value is NULL, display NULL value. */ if (value == NULL) return display_cat(dspbuf, "(NULL)"); /* If the value is empty, display EMPTY value. */ if (len == 0) return display_cat(dspbuf, "(EMPTY)"); /* Display the length of the value. */ b_left = display_printf(dspbuf, "(%d:", len); if (b_left <= 0) return b_left; if (len > max) cpy = max; /* Determine if the value is entirely printable characters. */ for (i = 0; i < len; i++) if (!isprint(((char *)value)[i])) break; if (i == len) { /* Entirely printable character, so we will just copy the * characters into the buffer (to the extent there is room * for them). */ b_left = display_len_cat(dspbuf, value, cpy); } else { b_left = display_opaque_bytes(dspbuf, value, cpy); } if (b_left <= 0) return b_left; if (len > max) return display_cat(dspbuf, "...)"); else return display_cat(dspbuf, ")"); } /** * @brief Append a length delimited string to the buffer. * * @param[in,out] dspbuf The buffer. * @param[in] str The string * @param[in] len The length of the string * * @return the bytes remaining in the buffer. * */ int display_len_cat(struct display_buffer *dspbuf, char *str, int len) { int b_left = display_start(dspbuf); int cpy; if (b_left <= 0) return b_left; /* Check if string would overflow dspbuf. */ if (len >= b_left) { /* Don't copy more bytes than will fit. */ cpy = b_left - 1; } else { /* Copy the entire string including null. */ cpy = len; } /* Copy characters and null terminate. */ memcpy(dspbuf->b_current, str, cpy); dspbuf->b_current[cpy] = '\0'; if (len >= b_left) { /* Overflow, indicate by moving b_current past end of buffer. */ dspbuf->b_current += b_left; } else { /* Didn't overflow, just increment b_current. */ dspbuf->b_current += len; } return display_finish(dspbuf); } /** * @brief Append a null delimited string to the buffer, truncating it. * * @param[in,out] dspbuf The buffer. * @param[in] str The string * @param[in] max Truncate the string to this maximum length * * @return the bytes remaining in the buffer. * * This routine is useful when the caller wishes to append a string to * the buffer, but rather than truncating the string at the end of the buffer, * the caller desires the string to be truncated to some shorter length (max). * * If the string is truncated, that will be indicated with "..." characters. * Basically this routine makes a sub-display buffer of max+1 bytes and uses * display_cat to achieve the truncation. * */ int display_cat_trunc(struct display_buffer *dspbuf, char *str, size_t max) { struct display_buffer catbuf; int b_left = display_start(dspbuf); if (b_left <= 0) return b_left; /* If there isn't room left in dspbuf after max, then just use * display_cat so that dspbuf will be properly finished. */ if ((max + 1) >= b_left) return display_cat(dspbuf, str); /* Make a sub-buffer so we can properly truncate the string. */ catbuf.b_current = dspbuf->b_current; catbuf.b_start = dspbuf->b_current; catbuf.b_size = max + 1; b_left = display_cat(&catbuf, str); /* Did we overflow the catbuf? * NOTE b_left can't be -1 because catbuf is declared on the stack. */ if (b_left == 0) { /* Overflowed catbuf, catbuf.b_current points past the * terminating null. Roll back to point at the null. */ dspbuf->b_current = catbuf.b_current - 1; } else { /* Update dspbuf */ dspbuf->b_current = catbuf.b_current; } /* we know dspbuf itself can not have overflowed * so just return the new remaing buffer space. */ return _display_buffer_remain(dspbuf); } /** @} */ nfs-ganesha-2.6.0/src/log/log_functions.c000066400000000000000000002104411324272410200202750ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- * * All the display functions and error handling. * */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "gsh_list.h" #include "gsh_rpc.h" #include "common_utils.h" #include "abstract_mem.h" #ifdef USE_DBUS #include "gsh_dbus.h" #endif #include "nfs_core.h" #include "config_parsing.h" #ifdef USE_LTTNG #include "gsh_lttng/logger.h" #endif /* * The usual PTHREAD_RWLOCK_xxx macros log messages for tracing if FULL * DEBUG is enabled. If such a macro is called from this logging file as * part of logging a message, it generates endless loop of lock tracing * messages. The following code redefines these lock macros to avoid the * loop. */ #ifdef PTHREAD_RWLOCK_wrlock #undef PTHREAD_RWLOCK_wrlock #endif #define PTHREAD_RWLOCK_wrlock(_lock) \ do { \ if (pthread_rwlock_wrlock(_lock) != 0) \ assert(0); \ } while (0) #ifdef PTHREAD_RWLOCK_rdlock #undef PTHREAD_RWLOCK_rdlock #endif #define PTHREAD_RWLOCK_rdlock(_lock) \ do { \ if (pthread_rwlock_rdlock(_lock) != 0) \ assert(0); \ } while (0) #ifdef PTHREAD_RWLOCK_unlock #undef PTHREAD_RWLOCK_unlock #endif #define PTHREAD_RWLOCK_unlock(_lock) \ do { \ if (pthread_rwlock_unlock(_lock) != 0) \ assert(0); \ } while (0) pthread_rwlock_t log_rwlock = PTHREAD_RWLOCK_INITIALIZER; /* Variables to control log fields */ /** * @brief Define an index each of the log fields that are configurable. * * Ganesha log messages have several "header" fields used in every * message. Some of those fields may be configured (mostly display or * not display). * */ enum log_flag_index_t { LF_DATE, /*< Date field. */ LF_TIME, /*< Time field. */ LF_EPOCH, /*< Server Epoch field (distinguishes server instance. */ LF_CLIENTIP, /* next = cleanup_list; cleanup_list = clean; } void Cleanup(void) { struct cleanup_list_element *c = cleanup_list; while (c != NULL) { c->clean(); c = c->next; } } void Fatal(void) { Cleanup(); exit(2); } #ifdef _DONT_HAVE_LOCALTIME_R /* Localtime is not reentrant... * So we are obliged to have a mutex for calling it. * pffff.... */ static pthread_mutex_t mutex_localtime = PTHREAD_MUTEX_INITIALIZER; /* thread-safe and PORTABLE version of localtime */ static struct tm *Localtime_r(const time_t *p_time, struct tm *p_tm) { struct tm *p_tmp_tm; if (!p_tm) { errno = EFAULT; return NULL; } PTHREAD_MUTEX_lock(&mutex_localtime); p_tmp_tm = localtime(p_time); /* copy the result */ (*p_tm) = (*p_tmp_tm); PTHREAD_MUTEX_unlock(&mutex_localtime); return p_tm; } #else #define Localtime_r localtime_r #endif /* * Convert a numeral log level in ascii to * the numeral value. */ int ReturnLevelAscii(const char *LevelInAscii) { int i = 0; for (i = 0; i < ARRAY_SIZE(tabLogLevel); i++) if (tabLogLevel[i].str != NULL && (!strcasecmp(tabLogLevel[i].str, LevelInAscii) || !strcasecmp(tabLogLevel[i].str + 4, LevelInAscii) || !strcasecmp(tabLogLevel[i].short_str, LevelInAscii))) return i; /* If nothing found, return -1 */ return -1; } /* ReturnLevelAscii */ char *ReturnLevelInt(int level) { if (level >= 0 && level < NB_LOG_LEVEL) return tabLogLevel[level].str; /* If nothing is found, return NULL. */ return NULL; } /* ReturnLevelInt */ /* * Set the name of this program. */ void SetNamePgm(const char *nom) { /* This function isn't thread-safe because the name of the program * is common among all the threads. */ if (strmaxcpy(program_name, nom, sizeof(program_name)) == -1) LogFatal(COMPONENT_LOG, "Program name %s too long", nom); } /* SetNamePgm */ /* * Set the hostname. */ void SetNameHost(const char *name) { if (strmaxcpy(hostname, name, sizeof(hostname)) == -1) LogFatal(COMPONENT_LOG, "Host name %s too long", name); } /* SetNameHost */ /* Set the function name in progress. */ void SetNameFunction(const char *nom) { strncpy(thread_name, nom, sizeof(thread_name)); thread_name[sizeof(thread_name)-1] = '\0'; if (strlen(nom) >= sizeof(thread_name)) LogWarn(COMPONENT_LOG, "Thread name %s too long truncated to %s", nom, thread_name); clientip = NULL; } /* * Sets the IP of the Client for this thread. * Make sure ip_str is valid for the duration of the thread */ void SetClientIP(char *ip_str) { clientip = ip_str; } /* Installs a signal handler */ static void ArmSignal(int signal, void (*action) ()) { struct sigaction act; /* Placing fields of struct sigaction */ act.sa_flags = 0; act.sa_handler = action; sigemptyset(&act.sa_mask); if (sigaction(signal, &act, NULL) == -1) LogCrit(COMPONENT_LOG, "Failed to arm signal %d, error %d (%s)", signal, errno, strerror(errno)); } /* ArmSignal */ /* * Five functions to manage debug level throug signal * handlers. * * InitLogging * IncrementLevelDebug * DecrementLevelDebug * SetLevelDebug * ReturnLevelDebug */ static void _SetLevelDebug(int level_to_set) { int i; if (level_to_set < NIV_NULL) level_to_set = NIV_NULL; if (level_to_set >= NB_LOG_LEVEL) level_to_set = NB_LOG_LEVEL - 1; /* COMPONENT_ALL is a pseudo component, handle it separately */ component_log_level[COMPONENT_ALL] = level_to_set; for (i = COMPONENT_ALL + 1; i < COMPONENT_COUNT; i++) { SetComponentLogLevel(i, level_to_set); } } /* _SetLevelDebug */ static void SetLevelDebug(int level_to_set) { _SetLevelDebug(level_to_set); LogChanges("Setting log level for all components to %s", ReturnLevelInt(component_log_level[COMPONENT_ALL])); } static void SetNTIRPCLogLevel(int level_to_set) { switch (level_to_set) { case NIV_NULL: case NIV_FATAL: ntirpc_pp.debug_flags = 0; /* disable all flags */ break; case NIV_CRIT: case NIV_MAJ: ntirpc_pp.debug_flags = TIRPC_DEBUG_FLAG_ERROR; break; case NIV_WARN: ntirpc_pp.debug_flags = TIRPC_DEBUG_FLAG_ERROR | TIRPC_DEBUG_FLAG_WARN; break; case NIV_EVENT: ntirpc_pp.debug_flags = TIRPC_DEBUG_FLAG_ERROR | TIRPC_DEBUG_FLAG_WARN | TIRPC_DEBUG_FLAG_EVENT; break; case NIV_DEBUG: case NIV_MID_DEBUG: /* set by log_conf_commit() */ break; case NIV_FULL_DEBUG: ntirpc_pp.debug_flags = 0xFFFFFFFF; /* enable all flags */ break; default: ntirpc_pp.debug_flags = TIRPC_DEBUG_FLAG_DEFAULT; break; } if (!tirpc_control(TIRPC_SET_DEBUG_FLAGS, &ntirpc_pp.debug_flags)) LogCrit(COMPONENT_CONFIG, "Setting nTI-RPC debug_flags failed"); } void SetComponentLogLevel(log_components_t component, int level_to_set) { assert(level_to_set >= NIV_NULL); assert(level_to_set < NB_LOG_LEVEL); assert(component != COMPONENT_ALL); if (LogComponents[component].comp_env_set) { LogWarn(COMPONENT_CONFIG, "LOG %s level %s from config is ignored because %s was set in environment", LogComponents[component].comp_name, ReturnLevelInt(level_to_set), ReturnLevelInt(component_log_level[component])); return; } if (component_log_level[component] == level_to_set) return; LogChanges("Changing log level of %s from %s to %s", LogComponents[component].comp_name, ReturnLevelInt(component_log_level[component]), ReturnLevelInt(level_to_set)); component_log_level[component] = level_to_set; if (component == COMPONENT_TIRPC) SetNTIRPCLogLevel(level_to_set); } static inline int ReturnLevelDebug(void) { return component_log_level[COMPONENT_ALL]; } /* ReturnLevelDebug */ static void IncrementLevelDebug(void) { _SetLevelDebug(ReturnLevelDebug() + 1); LogChanges("SIGUSR1 Increasing log level for all components to %s", ReturnLevelInt(component_log_level[COMPONENT_ALL])); } /* IncrementLevelDebug */ static void DecrementLevelDebug(void) { _SetLevelDebug(ReturnLevelDebug() - 1); LogChanges("SIGUSR2 Decreasing log level for all components to %s", ReturnLevelInt(component_log_level[COMPONENT_ALL])); } /* DecrementLevelDebug */ void set_const_log_str(void) { struct display_buffer dspbuf = { sizeof(const_log_str), const_log_str, const_log_str }; struct display_buffer tdfbuf = { sizeof(date_time_fmt), date_time_fmt, date_time_fmt }; int b_left = display_start(&dspbuf); const_log_str[0] = '\0'; if (b_left > 0 && logfields->disp_epoch) b_left = display_printf(&dspbuf, ": epoch %08x ", ServerEpoch); if (b_left > 0 && logfields->disp_host) b_left = display_printf(&dspbuf, ": %s ", hostname); if (b_left > 0 && logfields->disp_prog) b_left = display_printf(&dspbuf, ": %s", program_name); if (b_left > 0 && logfields->disp_prog && logfields->disp_pid) b_left = display_cat(&dspbuf, "-"); if (b_left > 0 && logfields->disp_pid) b_left = display_printf(&dspbuf, "%d", getpid()); if (b_left > 0 && (logfields->disp_prog || logfields->disp_pid) && !logfields->disp_threadname) (void) display_cat(&dspbuf, " "); b_left = display_start(&tdfbuf); if (b_left <= 0) return; if (logfields->datefmt == TD_LOCAL && logfields->timefmt == TD_LOCAL) { b_left = display_cat(&tdfbuf, "%c "); } else { switch (logfields->datefmt) { case TD_GANESHA: b_left = display_cat(&tdfbuf, "%d/%m/%Y "); break; case TD_8601: b_left = display_cat(&tdfbuf, "%F "); break; case TD_LOCAL: b_left = display_cat(&tdfbuf, "%x "); break; case TD_SYSLOG: b_left = display_cat(&tdfbuf, "%b %e "); break; case TD_SYSLOG_USEC: if (logfields->timefmt == TD_SYSLOG_USEC) b_left = display_cat(&tdfbuf, "%F"); else b_left = display_cat(&tdfbuf, "%F "); break; case TD_USER: b_left = display_printf(&tdfbuf, "%s ", logfields->user_date_fmt); break; case TD_NONE: default: break; } if (b_left <= 0) return; switch (logfields->timefmt) { case TD_GANESHA: b_left = display_cat(&tdfbuf, "%H:%M:%S "); break; case TD_SYSLOG: case TD_8601: case TD_LOCAL: b_left = display_cat(&tdfbuf, "%X "); break; case TD_SYSLOG_USEC: b_left = display_cat(&tdfbuf, "T%H:%M:%S.%%06u%z "); break; case TD_USER: b_left = display_printf(&tdfbuf, "%s ", logfields->user_time_fmt); break; case TD_NONE: default: break; } } /* Trim trailing blank from date time format. */ if (date_time_fmt[0] != '\0' && date_time_fmt[strlen(date_time_fmt) - 1] == ' ') date_time_fmt[strlen(date_time_fmt) - 1] = '\0'; } static void set_logging_from_env(void) { char *env_value; int newlevel, component, oldlevel; for (component = COMPONENT_ALL; component < COMPONENT_COUNT; component++) { env_value = getenv(LogComponents[component].comp_name); if (env_value == NULL) continue; newlevel = ReturnLevelAscii(env_value); if (newlevel == -1) { LogCrit(COMPONENT_LOG, "Environment variable %s exists, but the value %s is not a valid log level.", LogComponents[component].comp_name, env_value); continue; } oldlevel = component_log_level[component]; if (component == COMPONENT_ALL) _SetLevelDebug(newlevel); else SetComponentLogLevel(component, newlevel); LogComponents[component].comp_env_set = true; LogChanges( "Using environment variable to switch log level for %s from %s to %s", LogComponents[component].comp_name, ReturnLevelInt(oldlevel), ReturnLevelInt(newlevel)); } } /* InitLogging */ /** * * @brief Finds a log facility by name * * Must be called under the rwlock * * @param[in] name The name of the facility to be found * * @retval NULL No facility by that name * @retval non-NULL Pointer to the facility structure * */ static struct log_facility *find_log_facility(const char *name) { struct glist_head *glist; struct log_facility *facility; glist_for_each(glist, &facility_list) { facility = glist_entry(glist, struct log_facility, lf_list); if (!strcasecmp(name, facility->lf_name)) return facility; } return NULL; } /** * @brief Create a logging facility * * A logging facility outputs log messages using the helper function * log_func. See below for enabling/disabling. * * @param name [IN] the name of the new logger * @param log_func [IN] function pointer to the helper * @param max_level [IN] maximum message level this logger will handle. * @param header [IN] detail level for header part of messages * @param private [IN] logger specific argument. * * @return 0 on success, -errno for failure */ int create_log_facility(const char *name, lf_function_t *log_func, log_levels_t max_level, log_header_t header, void *private) { struct log_facility *facility; if (name == NULL || *name == '\0') return -EINVAL; if (max_level < NIV_NULL || max_level >= NB_LOG_LEVEL) return -EINVAL; if (log_func == log_to_file && private != NULL) { char *dir; int rc; if (*(char *)private == '\0' || strlen(private) >= MAXPATHLEN) { LogCrit(COMPONENT_LOG, "New log file path empty or too long"); return -EINVAL; } dir = alloca(strlen(private) + 1); strcpy(dir, private); dir = dirname(dir); rc = access(dir, W_OK); if (rc != 0) { rc = errno; LogCrit(COMPONENT_LOG, "Cannot create new log file (%s), because: %s", (char *)private, strerror(rc)); return -rc; } } PTHREAD_RWLOCK_wrlock(&log_rwlock); facility = find_log_facility(name); if (facility != NULL) { PTHREAD_RWLOCK_unlock(&log_rwlock); LogInfo(COMPONENT_LOG, "Facility %s already exists", name); return -EEXIST; } facility = gsh_calloc(1, sizeof(*facility)); facility->lf_name = gsh_strdup(name); facility->lf_func = log_func; facility->lf_max_level = max_level; facility->lf_headers = header; if (log_func == log_to_file && private != NULL) facility->lf_private = gsh_strdup(private); else facility->lf_private = private; glist_add_tail(&facility_list, &facility->lf_list); PTHREAD_RWLOCK_unlock(&log_rwlock); LogInfo(COMPONENT_LOG, "Created log facility %s", facility->lf_name); return 0; } /** * @brief Release a logger facility * * Release the named facility and all its resources. * disable it first if it is active. It will refuse to * release the default logger because that could leave the server * with no place to send messages. * * @param name [IN] name of soon to be deceased logger * * @returns always. The logger is not disabled or released on errors */ void release_log_facility(const char *name) { struct log_facility *facility; PTHREAD_RWLOCK_wrlock(&log_rwlock); facility = find_log_facility(name); if (facility == NULL) { PTHREAD_RWLOCK_unlock(&log_rwlock); LogCrit(COMPONENT_LOG, "Attempting release of non-existant log facility (%s)", name); return; } if (facility == default_facility) { PTHREAD_RWLOCK_unlock(&log_rwlock); LogCrit(COMPONENT_LOG, "Attempting to release default log facility (%s)", name); return; } if (!glist_null(&facility->lf_active)) glist_del(&facility->lf_active); glist_del(&facility->lf_list); PTHREAD_RWLOCK_unlock(&log_rwlock); if (facility->lf_func == log_to_file && facility->lf_private != NULL) gsh_free(facility->lf_private); gsh_free(facility->lf_name); gsh_free(facility); } /** * @brief Enable the named logger * * Enabling a logger adds it to the list of facilites that will be * used to report messages. * * @param name [IN] the name of the logger to enable * * @return 0 on success, -errno on errors. */ int enable_log_facility(const char *name) { struct log_facility *facility; if (name == NULL || *name == '\0') return -EINVAL; PTHREAD_RWLOCK_wrlock(&log_rwlock); facility = find_log_facility(name); if (facility == NULL) { PTHREAD_RWLOCK_unlock(&log_rwlock); LogInfo(COMPONENT_LOG, "Facility %s does not exist", name); return -ENOENT; } if (glist_null(&facility->lf_active)) glist_add_tail(&active_facility_list, &facility->lf_active); if (facility->lf_headers > max_headers) max_headers = facility->lf_headers; PTHREAD_RWLOCK_unlock(&log_rwlock); return 0; } /** * @brief Disable the named logger * * Disabling a logger ends logging output to that facility. * Disabling the default logger is not allowed. Another facility * must be set instead. Loggers can be re-enabled at any time. * * @param name [IN] the name of the logger to enable * * @return 0 on success, -errno on errors. */ int disable_log_facility(const char *name) { struct log_facility *facility; if (name == NULL || *name == '\0') return -EINVAL; PTHREAD_RWLOCK_wrlock(&log_rwlock); facility = find_log_facility(name); if (facility == NULL) { PTHREAD_RWLOCK_unlock(&log_rwlock); LogInfo(COMPONENT_LOG, "Facility %s does not exist", name); return -ENOENT; } if (glist_null(&facility->lf_active)) { PTHREAD_RWLOCK_unlock(&log_rwlock); LogDebug(COMPONENT_LOG, "Log facility (%s) is already disabled", name); return 0; } if (facility == default_facility) { PTHREAD_RWLOCK_unlock(&log_rwlock); LogCrit(COMPONENT_LOG, "Cannot disable the default logger (%s)", default_facility->lf_name); return -EPERM; } glist_del(&facility->lf_active); if (facility->lf_headers == max_headers) { struct glist_head *glist; struct log_facility *found; max_headers = LH_NONE; glist_for_each(glist, &active_facility_list) { found = glist_entry(glist, struct log_facility, lf_active); if (found->lf_headers > max_headers) max_headers = found->lf_headers; } } PTHREAD_RWLOCK_unlock(&log_rwlock); return 0; } /** * @brief Set the named logger as the default logger * * The default logger can not be released sp we set another one as * the default instead. The previous default logger is disabled. * * @param name [IN] the name of the logger to enable * * @return 0 on success, -errno on errors. */ static int set_default_log_facility(const char *name) { struct log_facility *facility; if (name == NULL || *name == '\0') return -EINVAL; PTHREAD_RWLOCK_wrlock(&log_rwlock); facility = find_log_facility(name); if (facility == NULL) { PTHREAD_RWLOCK_unlock(&log_rwlock); LogCrit(COMPONENT_LOG, "Facility %s does not exist", name); return -ENOENT; } if (facility == default_facility) goto out; if (glist_null(&facility->lf_active)) glist_add_tail(&active_facility_list, &facility->lf_active); if (default_facility != NULL) { assert(!glist_null(&default_facility->lf_active)); glist_del(&default_facility->lf_active); if (facility->lf_headers != max_headers) { struct glist_head *glist; struct log_facility *found; max_headers = LH_NONE; glist_for_each(glist, &active_facility_list) { found = glist_entry(glist, struct log_facility, lf_active); if (found->lf_headers > max_headers) max_headers = found->lf_headers; } } } else if (facility->lf_headers > max_headers) max_headers = facility->lf_headers; default_facility = facility; out: PTHREAD_RWLOCK_unlock(&log_rwlock); return 0; } /** * @brief Set the destination for logger * * This function only works if the facility outputs to files. * * @param name [IN] the name of the facility * @param dest [IN] "stdout", "stderr", "syslog", or a file path * * @return 0 on success, -errno on errors */ int set_log_destination(const char *name, char *dest) { struct log_facility *facility; int rc; if (name == NULL || *name == '\0') return -EINVAL; if (dest == NULL || *dest == '\0' || strlen(dest) >= MAXPATHLEN) { LogCrit(COMPONENT_LOG, "New log file path empty or too long"); return -EINVAL; } PTHREAD_RWLOCK_wrlock(&log_rwlock); facility = find_log_facility(name); if (facility == NULL) { PTHREAD_RWLOCK_unlock(&log_rwlock); LogCrit(COMPONENT_LOG, "No such log facility (%s)", name); return -ENOENT; } if (facility->lf_func == log_to_file) { char *logfile, *dir; dir = alloca(strlen(dest) + 1); strcpy(dir, dest); dir = dirname(dir); rc = access(dir, W_OK); if (rc != 0) { PTHREAD_RWLOCK_unlock(&log_rwlock); LogCrit(COMPONENT_LOG, "Cannot create new log file (%s), because: %s", dest, strerror(errno)); return -errno; } logfile = gsh_strdup(dest); gsh_free(facility->lf_private); facility->lf_private = logfile; } else if (facility->lf_func == log_to_stream) { FILE *out; if (strcasecmp(dest, "stdout") == 0) { out = stdout; } else if (strcasecmp(dest, "stderr") == 0) { out = stderr; } else { PTHREAD_RWLOCK_unlock(&log_rwlock); LogCrit(COMPONENT_LOG, "Expected STDERR or STDOUT, not (%s)", dest); return -EINVAL; } facility->lf_private = out; } else { PTHREAD_RWLOCK_unlock(&log_rwlock); LogCrit(COMPONENT_LOG, "Log facility %s destination is not changeable", facility->lf_name); return -EINVAL; } PTHREAD_RWLOCK_unlock(&log_rwlock); return 0; } /** * @brief Set maximum logging level for a facilty * * @param name [IN] the name of the facility * @param max_level [IN] Maximum level * * * @return 0 on success, -errno on errors */ int set_log_level(const char *name, log_levels_t max_level) { struct log_facility *facility; if (name == NULL || *name == '\0') return -EINVAL; if (max_level < NIV_NULL || max_level >= NB_LOG_LEVEL) return -EINVAL; PTHREAD_RWLOCK_wrlock(&log_rwlock); facility = find_log_facility(name); if (facility == NULL) { PTHREAD_RWLOCK_unlock(&log_rwlock); LogCrit(COMPONENT_LOG, "No such log facility (%s)", name); return -ENOENT; } facility->lf_max_level = max_level; PTHREAD_RWLOCK_unlock(&log_rwlock); return 0; } /** * @brief Initialize Logging * * Called very early in server init to make logging available as * soon as possible. Create a logger to stderr first and make it * the default. We are forced to fprintf to stderr by hand until * this happens. Once this is up, the logger is working. * We then get stdout and syslog loggers init'd. * If log_path (passed in via -L on the command line), we get a * FILE logger going and make it our default logger. Otherwise, * we use syslog as the default. * * @param log_path [IN] optarg from -L, otherwise NULL * @param debug_level [IN] global debug level from -N optarg */ void init_logging(const char *log_path, const int debug_level) { int rc; /* Finish initialization of and register log facilities. */ glist_init(&facility_list); glist_init(&active_facility_list); /* Initialize const_log_str to defaults. Ganesha can start logging * before the LOG config is processed (in fact, LOG config can itself * issue log messages to indicate errors. */ set_const_log_str(); rc = create_log_facility("STDERR", log_to_stream, NIV_FULL_DEBUG, LH_ALL, stderr); if (rc != 0) { fprintf(stderr, "Create error (%s) for STDERR log facility!", strerror(-rc)); Fatal(); } rc = set_default_log_facility("STDERR"); if (rc != 0) { fprintf(stderr, "Enable error (%s) for STDERR log facility!", strerror(-rc)); Fatal(); } rc = create_log_facility("STDOUT", log_to_stream, NIV_FULL_DEBUG, LH_ALL, stdout); if (rc != 0) LogFatal(COMPONENT_LOG, "Create error (%s) for STDOUT log facility!", strerror(-rc)); rc = create_log_facility("SYSLOG", log_to_syslog, NIV_FULL_DEBUG, LH_COMPONENT, NULL); if (rc != 0) LogFatal(COMPONENT_LOG, "Create error (%s) for SYSLOG log facility!", strerror(-rc)); if (log_path) { if ((strcmp(log_path, "STDERR") == 0) || (strcmp(log_path, "SYSLOG") == 0) || (strcmp(log_path, "STDOUT") == 0)) { rc = set_default_log_facility(log_path); if (rc != 0) LogFatal(COMPONENT_LOG, "Enable error (%s) for %s logging!", strerror(-rc), log_path); } else { rc = create_log_facility("FILE", log_to_file, NIV_FULL_DEBUG, LH_ALL, (void *)log_path); if (rc != 0) LogFatal(COMPONENT_LOG, "Create error (%s) for FILE (%s) logging!", strerror(-rc), log_path); rc = set_default_log_facility("FILE"); if (rc != 0) LogFatal(COMPONENT_LOG, "Enable error (%s) for FILE (%s) logging!", strerror(-rc), log_path); } } else { /* Fall back to SYSLOG as the first default facility */ rc = set_default_log_facility("SYSLOG"); if (rc != 0) LogFatal(COMPONENT_LOG, "Enable error (%s) for SYSLOG logging!", strerror(-rc)); } if (debug_level >= 0) SetLevelDebug(debug_level); set_logging_from_env(); ArmSignal(SIGUSR1, IncrementLevelDebug); ArmSignal(SIGUSR2, DecrementLevelDebug); } /* * Routines for managing error messages */ static int log_to_syslog(log_header_t headers, void *private, log_levels_t level, struct display_buffer *buffer, char *compstr, char *message) { if (!syslog_opened) { openlog("nfs-ganesha", LOG_PID, LOG_USER); syslog_opened = 1; } /* Writing to syslog. */ syslog(tabLogLevel[level].syslog_level, "%s", compstr); return 0; } static int log_to_file(log_header_t headers, void *private, log_levels_t level, struct display_buffer *buffer, char *compstr, char *message) { int fd, my_status, len, rc = 0; char *path = private; len = display_buffer_len(buffer); /* Add newline to end of buffer */ buffer->b_start[len] = '\n'; buffer->b_start[len + 1] = '\0'; fd = open(path, O_WRONLY | O_APPEND | O_CREAT, log_mask); if (fd != -1) { rc = write(fd, buffer->b_start, len + 1); if (rc < (len + 1)) { if (rc >= 0) my_status = ENOSPC; else my_status = errno; (void)close(fd); goto error; } rc = close(fd); if (rc == 0) goto out; } my_status = errno; error: fprintf(stderr, "Error: couldn't complete write to the log file %s status=%d (%s) message was:\n%s", path, my_status, strerror(my_status), buffer->b_start); out: /* Remove newline from buffer */ buffer->b_start[len] = '\0'; return rc; } static int log_to_stream(log_header_t headers, void *private, log_levels_t level, struct display_buffer *buffer, char *compstr, char *message) { FILE *stream = private; int rc; char *msg = buffer->b_start; int len; len = display_buffer_len(buffer); /* Add newline to end of buffer */ buffer->b_start[len] = '\n'; buffer->b_start[len + 1] = '\0'; switch (headers) { case LH_NONE: msg = message; break; case LH_COMPONENT: msg = compstr; break; case LH_ALL: msg = buffer->b_start; break; default: msg = "Somehow header level got messed up!!"; } rc = fputs(msg, stream); if (rc != EOF) rc = fflush(stream); /* Remove newline from buffer */ buffer->b_start[len] = '\0'; if (rc == EOF) return -1; else return 0; } int display_timeval(struct display_buffer *dspbuf, struct timeval *tv) { char *fmt = date_time_fmt; int b_left = display_start(dspbuf); struct tm the_date; char tbuf[MAX_TD_FMT_LEN]; time_t tm = tv->tv_sec; if (b_left <= 0) return b_left; if (logfields->datefmt == TD_NONE && logfields->timefmt == TD_NONE) fmt = "%c "; Localtime_r(&tm, &the_date); /* Earlier we build the date/time format string in * date_time_fmt, now use that to format the time and/or date. * If time format is TD_SYSLOG_USEC, then we need an additional * step to add the microseconds (since strftime just takes a * struct tm which was filled in from a time_t and thus does not * have microseconds. */ if (strftime(tbuf, sizeof(tbuf), fmt, &the_date) != 0) { if (logfields->timefmt == TD_SYSLOG_USEC) b_left = display_printf(dspbuf, tbuf, tv->tv_usec); else b_left = display_cat(dspbuf, tbuf); } return b_left; } int display_timespec(struct display_buffer *dspbuf, struct timespec *ts) { char *fmt = date_time_fmt; int b_left = display_start(dspbuf); struct tm the_date; char tbuf[MAX_TD_FMT_LEN]; time_t tm = ts->tv_sec; if (b_left <= 0) return b_left; if (logfields->datefmt == TD_NONE && logfields->timefmt == TD_NONE) fmt = "%c "; Localtime_r(&tm, &the_date); /* Earlier we build the date/time format string in * date_time_fmt, now use that to format the time and/or date. * If time format is TD_SYSLOG_USEC, then we need an additional * step to add the microseconds (since strftime just takes a * struct tm which was filled in from a time_t and thus does not * have microseconds. */ if (strftime(tbuf, sizeof(tbuf), fmt, &the_date) != 0) { if (logfields->timefmt == TD_SYSLOG_USEC) b_left = display_printf(dspbuf, tbuf, ts->tv_nsec); else b_left = display_cat(dspbuf, tbuf); } return b_left; } static int display_log_header(struct display_buffer *dsp_log) { int b_left = display_start(dsp_log); if (b_left <= 0 || max_headers < LH_ALL) return b_left; /* Print date and/or time if either flag is enabled. */ if (b_left > 0 && (logfields->datefmt != TD_NONE || logfields->timefmt != TD_NONE)) { struct timeval tv; if (logfields->timefmt == TD_SYSLOG_USEC) { gettimeofday(&tv, NULL); } else { tv.tv_sec = time(NULL); tv.tv_usec = 0; } b_left = display_timeval(dsp_log, &tv); if (b_left > 0) b_left = display_cat(dsp_log, " "); } if (b_left > 0 && const_log_str[0] != '\0') b_left = display_cat(dsp_log, const_log_str); /* If thread name will not follow, need a : separator */ if (b_left > 0 && !logfields->disp_threadname) b_left = display_cat(dsp_log, ": "); /* If we overflowed the buffer with the header, just skip it. */ if (b_left == 0) { display_reset_buffer(dsp_log); b_left = display_start(dsp_log); } /* The message will now start at dsp_log.b_current */ return b_left; } static int display_log_component(struct display_buffer *dsp_log, log_components_t component, const char *file, int line, const char *function, int level) { int b_left = display_start(dsp_log); if (b_left <= 0 || max_headers < LH_COMPONENT) return b_left; if (b_left > 0 && logfields->disp_clientip) { if (clientip) b_left = display_printf(dsp_log, "[%s] ", clientip); else b_left = display_printf(dsp_log, "[none] "); } if (b_left > 0 && logfields->disp_threadname) { if (thread_name[0] != '\0') b_left = display_printf(dsp_log, "[%s] ", thread_name); else b_left = display_printf(dsp_log, "[%p] ", thread_name); } if (b_left > 0 && logfields->disp_filename) { if (logfields->disp_linenum) b_left = display_printf(dsp_log, "%s:", file); else b_left = display_printf(dsp_log, "%s :", file); } if (b_left > 0 && logfields->disp_linenum) b_left = display_printf(dsp_log, "%d :", line); if (b_left > 0 && logfields->disp_funct) b_left = display_printf(dsp_log, "%s :", function); if (b_left > 0 && logfields->disp_comp) b_left = display_printf(dsp_log, "%s :", LogComponents[component].comp_str); if (b_left > 0 && logfields->disp_level) b_left = display_printf(dsp_log, "%s :", tabLogLevel[level].short_str); /* If we overflowed the buffer with the header, just skip it. */ if (b_left == 0) { display_reset_buffer(dsp_log); b_left = display_start(dsp_log); } return b_left; } void display_log_component_level(log_components_t component, const char *file, int line, const char *function, log_levels_t level, const char *format, va_list arguments) { char *compstr; char *message; int b_left; struct glist_head *glist; struct log_facility *facility; struct display_buffer dsp_log = {sizeof(log_buffer), log_buffer, log_buffer}; /* Build up the messsage and capture the various positions in it. */ b_left = display_log_header(&dsp_log); if (b_left > 0) compstr = dsp_log.b_current; else compstr = dsp_log.b_start; if (b_left > 0) b_left = display_log_component(&dsp_log, component, file, line, function, level); if (b_left > 0) message = dsp_log.b_current; else message = dsp_log.b_start; if (b_left > 0) b_left = display_vprintf(&dsp_log, format, arguments); #ifdef USE_LTTNG tracepoint(ganesha_logger, log, component, level, file, line, function, message); #endif PTHREAD_RWLOCK_rdlock(&log_rwlock); glist_for_each(glist, &active_facility_list) { facility = glist_entry(glist, struct log_facility, lf_active); if (level <= facility->lf_max_level && facility->lf_func != NULL) facility->lf_func(facility->lf_headers, facility->lf_private, level, &dsp_log, compstr, message); } PTHREAD_RWLOCK_unlock(&log_rwlock); if (level == NIV_FATAL) Fatal(); } /** * @brief Default logging levels * * These are for early initialization and whenever we * have to fall back to something that will at least work... */ static log_levels_t default_log_levels[] = { [COMPONENT_ALL] = NIV_NULL, [COMPONENT_LOG] = NIV_EVENT, [COMPONENT_MEM_ALLOC] = NIV_EVENT, [COMPONENT_MEMLEAKS] = NIV_EVENT, [COMPONENT_FSAL] = NIV_EVENT, [COMPONENT_NFSPROTO] = NIV_EVENT, [COMPONENT_NFS_V4] = NIV_EVENT, [COMPONENT_EXPORT] = NIV_EVENT, [COMPONENT_FILEHANDLE] = NIV_EVENT, [COMPONENT_DISPATCH] = NIV_EVENT, [COMPONENT_CACHE_INODE] = NIV_EVENT, [COMPONENT_CACHE_INODE_LRU] = NIV_EVENT, [COMPONENT_HASHTABLE] = NIV_EVENT, [COMPONENT_HASHTABLE_CACHE] = NIV_EVENT, [COMPONENT_DUPREQ] = NIV_EVENT, [COMPONENT_INIT] = NIV_EVENT, [COMPONENT_MAIN] = NIV_EVENT, [COMPONENT_IDMAPPER] = NIV_EVENT, [COMPONENT_NFS_READDIR] = NIV_EVENT, [COMPONENT_NFS_V4_LOCK] = NIV_EVENT, [COMPONENT_CONFIG] = NIV_EVENT, [COMPONENT_CLIENTID] = NIV_EVENT, [COMPONENT_SESSIONS] = NIV_EVENT, [COMPONENT_PNFS] = NIV_EVENT, [COMPONENT_RW_LOCK] = NIV_EVENT, [COMPONENT_NLM] = NIV_EVENT, [COMPONENT_RPC] = NIV_EVENT, [COMPONENT_TIRPC] = NIV_EVENT, [COMPONENT_NFS_CB] = NIV_EVENT, [COMPONENT_THREAD] = NIV_EVENT, [COMPONENT_NFS_V4_ACL] = NIV_EVENT, [COMPONENT_STATE] = NIV_EVENT, [COMPONENT_9P] = NIV_EVENT, [COMPONENT_9P_DISPATCH] = NIV_EVENT, [COMPONENT_FSAL_UP] = NIV_EVENT, [COMPONENT_DBUS] = NIV_EVENT, [COMPONENT_NFS_MSK] = NIV_EVENT }; log_levels_t *component_log_level = default_log_levels; struct log_component_info LogComponents[COMPONENT_COUNT] = { [COMPONENT_ALL] = { .comp_name = "COMPONENT_ALL", .comp_str = "",}, [COMPONENT_LOG] = { .comp_name = "COMPONENT_LOG", .comp_str = "LOG",}, [COMPONENT_MEM_ALLOC] = { .comp_name = "COMPONENT_MEM_ALLOC", .comp_str = "MEM ALLOC",}, [COMPONENT_MEMLEAKS] = { .comp_name = "COMPONENT_MEMLEAKS", .comp_str = "LEAKS",}, [COMPONENT_FSAL] = { .comp_name = "COMPONENT_FSAL", .comp_str = "FSAL",}, [COMPONENT_NFSPROTO] = { .comp_name = "COMPONENT_NFSPROTO", .comp_str = "NFS3",}, [COMPONENT_NFS_V4] = { .comp_name = "COMPONENT_NFS_V4", .comp_str = "NFS4",}, [COMPONENT_EXPORT] = { .comp_name = "COMPONENT_EXPORT", .comp_str = "EXPORT",}, [COMPONENT_FILEHANDLE] = { .comp_name = "COMPONENT_FILEHANDLE", .comp_str = "FH",}, [COMPONENT_DISPATCH] = { .comp_name = "COMPONENT_DISPATCH", .comp_str = "DISP",}, [COMPONENT_CACHE_INODE] = { .comp_name = "COMPONENT_CACHE_INODE", .comp_str = "INODE",}, [COMPONENT_CACHE_INODE_LRU] = { .comp_name = "COMPONENT_CACHE_INODE_LRU", .comp_str = "INODE LRU",}, [COMPONENT_HASHTABLE] = { .comp_name = "COMPONENT_HASHTABLE", .comp_str = "HT",}, [COMPONENT_HASHTABLE_CACHE] = { .comp_name = "COMPONENT_HASHTABLE_CACHE", .comp_str = "HT CACHE",}, [COMPONENT_DUPREQ] = { .comp_name = "COMPONENT_DUPREQ", .comp_str = "DUPREQ",}, [COMPONENT_INIT] = { .comp_name = "COMPONENT_INIT", .comp_str = "NFS STARTUP",}, [COMPONENT_MAIN] = { .comp_name = "COMPONENT_MAIN", .comp_str = "MAIN",}, [COMPONENT_IDMAPPER] = { .comp_name = "COMPONENT_IDMAPPER", .comp_str = "ID MAPPER",}, [COMPONENT_NFS_READDIR] = { .comp_name = "COMPONENT_NFS_READDIR", .comp_str = "NFS READDIR",}, [COMPONENT_NFS_V4_LOCK] = { .comp_name = "COMPONENT_NFS_V4_LOCK", .comp_str = "NFS4 LOCK",}, [COMPONENT_CONFIG] = { .comp_name = "COMPONENT_CONFIG", .comp_str = "CONFIG",}, [COMPONENT_CLIENTID] = { .comp_name = "COMPONENT_CLIENTID", .comp_str = "CLIENT ID",}, [COMPONENT_SESSIONS] = { .comp_name = "COMPONENT_SESSIONS", .comp_str = "SESSIONS",}, [COMPONENT_PNFS] = { .comp_name = "COMPONENT_PNFS", .comp_str = "PNFS",}, [COMPONENT_RW_LOCK] = { .comp_name = "COMPONENT_RW_LOCK", .comp_str = "RW LOCK",}, [COMPONENT_NLM] = { .comp_name = "COMPONENT_NLM", .comp_str = "NLM",}, [COMPONENT_RPC] = { .comp_name = "COMPONENT_RPC", .comp_str = "RPC",}, [COMPONENT_TIRPC] = { .comp_name = "COMPONENT_TIRPC", .comp_str = "TIRPC",}, [COMPONENT_NFS_CB] = { .comp_name = "COMPONENT_NFS_CB", .comp_str = "NFS CB",}, [COMPONENT_THREAD] = { .comp_name = "COMPONENT_THREAD", .comp_str = "THREAD",}, [COMPONENT_NFS_V4_ACL] = { .comp_name = "COMPONENT_NFS_V4_ACL", .comp_str = "NFS4 ACL",}, [COMPONENT_STATE] = { .comp_name = "COMPONENT_STATE", .comp_str = "STATE",}, [COMPONENT_9P] = { .comp_name = "COMPONENT_9P", .comp_str = "9P",}, [COMPONENT_9P_DISPATCH] = { .comp_name = "COMPONENT_9P_DISPATCH", .comp_str = "9P DISP",}, [COMPONENT_FSAL_UP] = { .comp_name = "COMPONENT_FSAL_UP", .comp_str = "FSAL_UP",}, [COMPONENT_DBUS] = { .comp_name = "COMPONENT_DBUS", .comp_str = "DBUS",}, [COMPONENT_NFS_MSK] = { .comp_name = "COMPONENT_NFS_MSK", .comp_str = "NFS_MSK",} }; void DisplayLogComponentLevel(log_components_t component, const char *file, int line, const char *function, log_levels_t level, const char *format, ...) { va_list arguments; va_start(arguments, format); display_log_component_level(component, file, line, function, level, format, arguments); va_end(arguments); } void LogMallocFailure(const char *file, int line, const char *function, const char *allocator) { DisplayLogComponentLevel(COMPONENT_MEM_ALLOC, (char *) file, line, (char *)function, NIV_NULL, "Aborting %s due to out of memory", allocator); } /* * Re-export component logging to TI-RPC internal logging */ void rpc_warnx(char *fmt, ...) { va_list ap; if (component_log_level[COMPONENT_TIRPC] <= NIV_FATAL) return; va_start(ap, fmt); display_log_component_level(COMPONENT_TIRPC, "", 0, "rpc", component_log_level[COMPONENT_TIRPC], fmt, ap); va_end(ap); } #ifdef USE_DBUS static bool dbus_prop_get(log_components_t component, DBusMessageIter *reply) { char *level_code; level_code = ReturnLevelInt(component_log_level[component]); if (level_code == NULL) return false; if (!dbus_message_iter_append_basic (reply, DBUS_TYPE_STRING, &level_code)) return false; return true; } static bool dbus_prop_set(log_components_t component, DBusMessageIter *arg) { char *level_code; int log_level; if (dbus_message_iter_get_arg_type(arg) != DBUS_TYPE_STRING) return false; dbus_message_iter_get_basic(arg, &level_code); log_level = ReturnLevelAscii(level_code); if (log_level == -1) { LogDebug(COMPONENT_DBUS, "Invalid log level: '%s' given for component %s", level_code, LogComponents[component].comp_name); return false; } if (component == COMPONENT_ALL) { _SetLevelDebug(log_level); LogChanges("Dbus set log level for all components to %s", level_code); } else { LogChanges("Dbus set log level for %s from %s to %s.", LogComponents[component].comp_name, ReturnLevelInt(component_log_level[component]), ReturnLevelInt(log_level)); SetComponentLogLevel(component, log_level); } return true; } /* Macros to make mapping properties table to components enum etc. easier * expands to table entries and shim functions. */ #define HANDLE_PROP(component) \ static bool dbus_prop_get_COMPONENT_##component(DBusMessageIter *reply) \ { \ return dbus_prop_get(COMPONENT_##component, reply);\ } \ \ static bool dbus_prop_set_COMPONENT_##component(DBusMessageIter *args) \ { \ return dbus_prop_set(COMPONENT_##component, args);\ } \ \ static struct gsh_dbus_prop COMPONENT_##component##_prop = { \ .name = "COMPONENT_" #component, \ .access = DBUS_PROP_READWRITE, \ .type = "s", \ .get = dbus_prop_get_COMPONENT_##component, \ .set = dbus_prop_set_COMPONENT_##component \ } #define LOG_PROPERTY_ITEM(component) (&COMPONENT_##component##_prop) /** * @brief Log property handlers. * * Expands to get/set functions that match dbus_prop_get/set protos * and call common handler with component enum as arg. * There is one line per log_components_t enum. * These must also match LOG_PROPERTY_ITEM */ HANDLE_PROP(ALL); HANDLE_PROP(LOG); HANDLE_PROP(MEM_ALLOC); HANDLE_PROP(MEMLEAKS); HANDLE_PROP(FSAL); HANDLE_PROP(NFSPROTO); HANDLE_PROP(NFS_V4); HANDLE_PROP(EXPORT); HANDLE_PROP(FILEHANDLE); HANDLE_PROP(DISPATCH); HANDLE_PROP(CACHE_INODE); HANDLE_PROP(CACHE_INODE_LRU); HANDLE_PROP(HASHTABLE); HANDLE_PROP(HASHTABLE_CACHE); HANDLE_PROP(DUPREQ); HANDLE_PROP(INIT); HANDLE_PROP(MAIN); HANDLE_PROP(IDMAPPER); HANDLE_PROP(NFS_READDIR); HANDLE_PROP(NFS_V4_LOCK); HANDLE_PROP(CONFIG); HANDLE_PROP(CLIENTID); HANDLE_PROP(SESSIONS); HANDLE_PROP(PNFS); HANDLE_PROP(RW_LOCK); HANDLE_PROP(NLM); HANDLE_PROP(RPC); HANDLE_PROP(TIRPC); HANDLE_PROP(NFS_CB); HANDLE_PROP(THREAD); HANDLE_PROP(NFS_V4_ACL); HANDLE_PROP(STATE); HANDLE_PROP(9P); HANDLE_PROP(9P_DISPATCH); HANDLE_PROP(FSAL_UP); HANDLE_PROP(DBUS); HANDLE_PROP(NFS_MSK); static struct gsh_dbus_prop *log_props[] = { LOG_PROPERTY_ITEM(ALL), LOG_PROPERTY_ITEM(LOG), LOG_PROPERTY_ITEM(MEM_ALLOC), LOG_PROPERTY_ITEM(MEMLEAKS), LOG_PROPERTY_ITEM(FSAL), LOG_PROPERTY_ITEM(NFSPROTO), LOG_PROPERTY_ITEM(NFS_V4), LOG_PROPERTY_ITEM(EXPORT), LOG_PROPERTY_ITEM(FILEHANDLE), LOG_PROPERTY_ITEM(DISPATCH), LOG_PROPERTY_ITEM(CACHE_INODE), LOG_PROPERTY_ITEM(CACHE_INODE_LRU), LOG_PROPERTY_ITEM(HASHTABLE), LOG_PROPERTY_ITEM(HASHTABLE_CACHE), LOG_PROPERTY_ITEM(DUPREQ), LOG_PROPERTY_ITEM(INIT), LOG_PROPERTY_ITEM(MAIN), LOG_PROPERTY_ITEM(IDMAPPER), LOG_PROPERTY_ITEM(NFS_READDIR), LOG_PROPERTY_ITEM(NFS_V4_LOCK), LOG_PROPERTY_ITEM(CONFIG), LOG_PROPERTY_ITEM(CLIENTID), LOG_PROPERTY_ITEM(SESSIONS), LOG_PROPERTY_ITEM(PNFS), LOG_PROPERTY_ITEM(RW_LOCK), LOG_PROPERTY_ITEM(NLM), LOG_PROPERTY_ITEM(RPC), LOG_PROPERTY_ITEM(TIRPC), LOG_PROPERTY_ITEM(NFS_CB), LOG_PROPERTY_ITEM(THREAD), LOG_PROPERTY_ITEM(NFS_V4_ACL), LOG_PROPERTY_ITEM(STATE), LOG_PROPERTY_ITEM(9P), LOG_PROPERTY_ITEM(9P_DISPATCH), LOG_PROPERTY_ITEM(FSAL_UP), LOG_PROPERTY_ITEM(DBUS), LOG_PROPERTY_ITEM(NFS_MSK), NULL }; struct gsh_dbus_interface log_interface = { .name = "org.ganesha.nfsd.log.component", .signal_props = false, .props = log_props, .methods = NULL, .signals = NULL }; #endif /* USE_DBUS */ enum facility_state { FAC_IDLE, FAC_ACTIVE, FAC_DEFAULT }; struct facility_config { struct glist_head fac_list; char *facility_name; char *dest; enum facility_state state; lf_function_t *func; log_header_t headers; log_levels_t max_level; void *lf_private; }; /** * @brief Logger config block parameters */ struct logger_config { struct glist_head facility_list; struct logfields *logfields; log_levels_t *comp_log_level; log_levels_t default_level; uint32_t rpc_debug_flags; }; /** * @brief Enumerated time and date format parameters */ static struct config_item_list timeformats[] = { CONFIG_LIST_TOK("ganesha", TD_GANESHA), CONFIG_LIST_TOK("true", TD_GANESHA), CONFIG_LIST_TOK("local", TD_LOCAL), CONFIG_LIST_TOK("8601", TD_8601), CONFIG_LIST_TOK("ISO-8601", TD_8601), CONFIG_LIST_TOK("ISO 8601", TD_8601), CONFIG_LIST_TOK("ISO", TD_8601), CONFIG_LIST_TOK("syslog", TD_SYSLOG), CONFIG_LIST_TOK("syslog_usec", TD_SYSLOG_USEC), CONFIG_LIST_TOK("false", TD_NONE), CONFIG_LIST_TOK("none", TD_NONE), CONFIG_LIST_TOK("user_defined", TD_USER), CONFIG_LIST_EOL }; /** * @brief Logging format parameters */ static struct config_item format_options[] = { CONF_ITEM_TOKEN("date_format", TD_GANESHA, timeformats, logfields, datefmt), CONF_ITEM_TOKEN("time_format", TD_GANESHA, timeformats, logfields, timefmt), CONF_ITEM_STR("user_date_format", 1, MAX_TD_FMT_LEN, NULL, logfields, user_date_fmt), CONF_ITEM_STR("user_time_format", 1, MAX_TD_FMT_LEN, NULL, logfields, user_time_fmt), CONF_ITEM_BOOL("EPOCH", true, logfields, disp_epoch), CONF_ITEM_BOOL("CLIENTIP", false, logfields, disp_clientip), CONF_ITEM_BOOL("HOSTNAME", true, logfields, disp_host), CONF_ITEM_BOOL("PROGNAME", true, logfields, disp_prog), CONF_ITEM_BOOL("PID", true, logfields, disp_pid), CONF_ITEM_BOOL("THREAD_NAME", true, logfields, disp_threadname), CONF_ITEM_BOOL("FILE_NAME", true, logfields, disp_filename), CONF_ITEM_BOOL("LINE_NUM", true, logfields, disp_linenum), CONF_ITEM_BOOL("FUNCTION_NAME", true, logfields, disp_funct), CONF_ITEM_BOOL("COMPONENT", true, logfields, disp_comp), CONF_ITEM_BOOL("LEVEL", true, logfields, disp_level), CONFIG_EOL }; /** * @brief Initialize the log message format parameters */ static void *format_init(void *link_mem, void *self_struct) { assert(link_mem != NULL || self_struct != NULL); if (link_mem == NULL) return NULL; if (self_struct == NULL) return gsh_calloc(1, sizeof(struct logfields)); else { struct logfields *lf = self_struct; if (lf->user_date_fmt != NULL) gsh_free(lf->user_date_fmt); if (lf->user_time_fmt != NULL) gsh_free(lf->user_time_fmt); gsh_free(lf); return NULL; } } /** * @brief Commit the log format parameters * * I'd prefer that Date_format and Time_format be enums but they are not. * They are enums except when they are not and we do hope that whatever * that is can be digested by printf... */ static int format_commit(void *node, void *link_mem, void *self_struct, struct config_error_type *err_type) { struct logfields *log = (struct logfields *)self_struct; struct logfields **logp = link_mem; struct logger_config *logger; int errcnt = 0; if (log->datefmt == TD_USER && log->user_date_fmt == NULL) { LogCrit(COMPONENT_CONFIG, "Date is \"user_set\" with empty date format."); err_type->validate = true; errcnt++; } if (log->datefmt != TD_USER && log->user_date_fmt != NULL) { LogCrit(COMPONENT_CONFIG, "Set user date format (%s) but not \"user_set\" format", log->user_date_fmt); err_type->validate = true; errcnt++; } if (log->timefmt == TD_USER && log->user_time_fmt == NULL) { LogCrit(COMPONENT_CONFIG, "Time is \"user_set\" with empty time format."); err_type->validate = true; errcnt++; } if (log->timefmt != TD_USER && log->user_time_fmt != NULL) { LogCrit(COMPONENT_CONFIG, "Set time format string (%s) but not \"user_set\" format", log->user_time_fmt); err_type->validate = true; errcnt++; } if (errcnt == 0) { logger = container_of(logp, struct logger_config, logfields); logger->logfields = log; } return errcnt; } /** * @brief Log component levels */ static struct config_item_list log_levels[] = { CONFIG_LIST_TOK("NULL", NIV_NULL), CONFIG_LIST_TOK("FATAL", NIV_FATAL), CONFIG_LIST_TOK("MAJ", NIV_MAJ), CONFIG_LIST_TOK("CRIT", NIV_CRIT), CONFIG_LIST_TOK("WARN", NIV_WARN), CONFIG_LIST_TOK("EVENT", NIV_EVENT), CONFIG_LIST_TOK("INFO", NIV_INFO), CONFIG_LIST_TOK("DEBUG", NIV_DEBUG), CONFIG_LIST_TOK("MID_DEBUG", NIV_MID_DEBUG), CONFIG_LIST_TOK("M_DBG", NIV_MID_DEBUG), CONFIG_LIST_TOK("FULL_DEBUG", NIV_FULL_DEBUG), CONFIG_LIST_TOK("F_DBG", NIV_FULL_DEBUG), CONFIG_LIST_EOL }; /** * @brief Logging components */ static struct config_item component_levels[] = { CONF_INDEX_TOKEN("ALL", NB_LOG_LEVEL, log_levels, COMPONENT_ALL, int), CONF_INDEX_TOKEN("LOG", NB_LOG_LEVEL, log_levels, COMPONENT_LOG, int), CONF_INDEX_TOKEN("MEM_ALLOC", NB_LOG_LEVEL, log_levels, COMPONENT_MEM_ALLOC, int), CONF_INDEX_TOKEN("MEMLEAKS", NB_LOG_LEVEL, log_levels, COMPONENT_MEMLEAKS, int), CONF_INDEX_TOKEN("LEAKS", NB_LOG_LEVEL, log_levels, COMPONENT_MEMLEAKS, int), CONF_INDEX_TOKEN("FSAL", NB_LOG_LEVEL, log_levels, COMPONENT_FSAL, int), CONF_INDEX_TOKEN("NFSPROTO", NB_LOG_LEVEL, log_levels, COMPONENT_NFSPROTO, int), CONF_INDEX_TOKEN("NFS3", NB_LOG_LEVEL, log_levels, COMPONENT_NFSPROTO, int), CONF_INDEX_TOKEN("NFS_V4", NB_LOG_LEVEL, log_levels, COMPONENT_NFS_V4, int), CONF_INDEX_TOKEN("NFS4", NB_LOG_LEVEL, log_levels, COMPONENT_NFS_V4, int), CONF_INDEX_TOKEN("EXPORT", NB_LOG_LEVEL, log_levels, COMPONENT_EXPORT, int), CONF_INDEX_TOKEN("FILEHANDLE", NB_LOG_LEVEL, log_levels, COMPONENT_FILEHANDLE, int), CONF_INDEX_TOKEN("FH", NB_LOG_LEVEL, log_levels, COMPONENT_FILEHANDLE, int), CONF_INDEX_TOKEN("DISPATCH", NB_LOG_LEVEL, log_levels, COMPONENT_DISPATCH, int), CONF_INDEX_TOKEN("DISP", NB_LOG_LEVEL, log_levels, COMPONENT_DISPATCH, int), CONF_INDEX_TOKEN("CACHE_INODE", NB_LOG_LEVEL, log_levels, COMPONENT_CACHE_INODE, int), CONF_INDEX_TOKEN("INODE", NB_LOG_LEVEL, log_levels, COMPONENT_CACHE_INODE, int), CONF_INDEX_TOKEN("CACHE_INODE_LRU", NB_LOG_LEVEL, log_levels, COMPONENT_CACHE_INODE_LRU, int), CONF_INDEX_TOKEN("INODE_LRU", NB_LOG_LEVEL, log_levels, COMPONENT_CACHE_INODE_LRU, int), CONF_INDEX_TOKEN("HASHTABLE", NB_LOG_LEVEL, log_levels, COMPONENT_HASHTABLE, int), CONF_INDEX_TOKEN("HT", NB_LOG_LEVEL, log_levels, COMPONENT_HASHTABLE, int), CONF_INDEX_TOKEN("HASHTABLE_CACHE", NB_LOG_LEVEL, log_levels, COMPONENT_HASHTABLE_CACHE, int), CONF_INDEX_TOKEN("HT_CACHE", NB_LOG_LEVEL, log_levels, COMPONENT_HASHTABLE_CACHE, int), CONF_INDEX_TOKEN("DUPREQ", NB_LOG_LEVEL, log_levels, COMPONENT_DUPREQ, int), CONF_INDEX_TOKEN("INIT", NB_LOG_LEVEL, log_levels, COMPONENT_INIT, int), CONF_INDEX_TOKEN("NFS_STARTUP", NB_LOG_LEVEL, log_levels, COMPONENT_INIT, int), CONF_INDEX_TOKEN("MAIN", NB_LOG_LEVEL, log_levels, COMPONENT_MAIN, int), CONF_INDEX_TOKEN("IDMAPPER", NB_LOG_LEVEL, log_levels, COMPONENT_IDMAPPER, int), CONF_INDEX_TOKEN("NFS_READDIR", NB_LOG_LEVEL, log_levels, COMPONENT_NFS_READDIR, int), CONF_INDEX_TOKEN("NFS_V4_LOCK", NB_LOG_LEVEL, log_levels, COMPONENT_NFS_V4_LOCK, int), CONF_INDEX_TOKEN("NFS4_LOCK", NB_LOG_LEVEL, log_levels, COMPONENT_NFS_V4_LOCK, int), CONF_INDEX_TOKEN("CONFIG", NB_LOG_LEVEL, log_levels, COMPONENT_CONFIG, int), CONF_INDEX_TOKEN("CLIENTID", NB_LOG_LEVEL, log_levels, COMPONENT_CLIENTID, int), CONF_INDEX_TOKEN("SESSIONS", NB_LOG_LEVEL, log_levels, COMPONENT_SESSIONS, int), CONF_INDEX_TOKEN("PNFS", NB_LOG_LEVEL, log_levels, COMPONENT_PNFS, int), CONF_INDEX_TOKEN("RW_LOCK", NB_LOG_LEVEL, log_levels, COMPONENT_RW_LOCK, int), CONF_INDEX_TOKEN("NLM", NB_LOG_LEVEL, log_levels, COMPONENT_NLM, int), CONF_INDEX_TOKEN("RPC", NB_LOG_LEVEL, log_levels, COMPONENT_RPC, int), CONF_INDEX_TOKEN("TIRPC", NB_LOG_LEVEL, log_levels, COMPONENT_TIRPC, int), CONF_INDEX_TOKEN("NFS_CB", NB_LOG_LEVEL, log_levels, COMPONENT_NFS_CB, int), CONF_INDEX_TOKEN("THREAD", NB_LOG_LEVEL, log_levels, COMPONENT_THREAD, int), CONF_INDEX_TOKEN("NFS_V4_ACL", NB_LOG_LEVEL, log_levels, COMPONENT_NFS_V4_ACL, int), CONF_INDEX_TOKEN("NFS4_ACL", NB_LOG_LEVEL, log_levels, COMPONENT_NFS_V4_ACL, int), CONF_INDEX_TOKEN("STATE", NB_LOG_LEVEL, log_levels, COMPONENT_STATE, int), CONF_INDEX_TOKEN("_9P", NB_LOG_LEVEL, log_levels, COMPONENT_9P, int), CONF_INDEX_TOKEN("_9P_DISPATCH", NB_LOG_LEVEL, log_levels, COMPONENT_9P_DISPATCH, int), CONF_INDEX_TOKEN("_9P_DISP", NB_LOG_LEVEL, log_levels, COMPONENT_9P_DISPATCH, int), CONF_INDEX_TOKEN("FSAL_UP", NB_LOG_LEVEL, log_levels, COMPONENT_FSAL_UP, int), CONF_INDEX_TOKEN("DBUS", NB_LOG_LEVEL, log_levels, COMPONENT_DBUS, int), CONF_INDEX_TOKEN("NFS_MSK", NB_LOG_LEVEL, log_levels, COMPONENT_NFS_MSK, int), CONFIG_EOL }; /** * @brief Initialize the log level array * * We allocate an array here even for the global case so as to * preserve something that works (default_log_levels) during config * processing. If the parse errors out, we just throw it away... * */ static void *component_init(void *link_mem, void *self_struct) { assert(link_mem != NULL || self_struct != NULL); if (link_mem == NULL) return NULL; if (self_struct == NULL) return gsh_calloc(COMPONENT_COUNT, sizeof(log_levels_t)); else { gsh_free(self_struct); return NULL; } } /** * @brief Commit the component levels * * COMPONENT_ALL is a magic component. It gets statically initialized * to NIV_NULL (no output) but the initialize pass changes that to * NB_LOG_LEVEL which is +1 the last valid level. This is used to detect * if COMPONENT_ALL has been set. If ALL is set, it overrides all * components including any that were set in the block. * * We also set the default for all components to be NB_LOG_LEVELS which * gets changed to the LOG { default_log_level ...} or NIV_EVENT if it * was not changed by the config. */ static int component_commit(void *node, void *link_mem, void *self_struct, struct config_error_type *err_type) { log_levels_t **log_lvls = link_mem; struct logger_config *logger; log_levels_t *log_level = self_struct; if (log_level[COMPONENT_ALL] != NB_LOG_LEVEL) { SetLevelDebug(log_level[COMPONENT_ALL]); } else { int comp; logger = container_of(log_lvls, struct logger_config, comp_log_level); if (logger->default_level == NB_LOG_LEVEL) logger->default_level = NIV_EVENT; for (comp = COMPONENT_LOG; comp < COMPONENT_COUNT; comp++) if (log_level[comp] == NB_LOG_LEVEL) log_level[comp] = logger->default_level; log_level[COMPONENT_ALL] = NIV_NULL; logger->comp_log_level = log_level; } return 0; } static struct config_item_list header_options[] = { CONFIG_LIST_TOK("none", LH_NONE), CONFIG_LIST_TOK("component", LH_COMPONENT), CONFIG_LIST_TOK("all", LH_ALL), CONFIG_LIST_EOL }; static struct config_item_list enable_options[] = { CONFIG_LIST_TOK("idle", FAC_IDLE), CONFIG_LIST_TOK("active", FAC_ACTIVE), CONFIG_LIST_TOK("default", FAC_DEFAULT), CONFIG_LIST_EOL }; static struct config_item facility_params[] = { CONF_ITEM_STR("name", 1, 20, NULL, facility_config, facility_name), CONF_MAND_STR("destination", 1, MAXPATHLEN, NULL, facility_config, dest), CONF_ITEM_TOKEN("max_level", NB_LOG_LEVEL, log_levels, facility_config, max_level), CONF_ITEM_TOKEN("headers", NB_LH_TYPES, header_options, facility_config, headers), CONF_ITEM_TOKEN("enable", FAC_IDLE, enable_options, facility_config, state), CONFIG_EOL }; /** * @brief Initialize a Facility block. * * This block is allocated just to capture the fields. It's members * are used to create/modify a facility at which point it gets freed. */ static void *facility_init(void *link_mem, void *self_struct) { struct facility_config *facility; assert(link_mem != NULL || self_struct != NULL); if (link_mem == NULL) { struct glist_head *facility_list; struct logger_config *logger; facility_list = self_struct; logger = container_of(facility_list, struct logger_config, facility_list); glist_init(&logger->facility_list); return self_struct; } else if (self_struct == NULL) { facility = gsh_calloc(1, sizeof(struct facility_config)); return facility; } else { facility = self_struct; assert(glist_null(&facility->fac_list)); if (facility->facility_name != NULL) gsh_free(facility->facility_name); if (facility->dest != NULL) gsh_free(facility->dest); gsh_free(self_struct); } return NULL; } /** * @brief Commit a facility block * * It can create a stream, syslog, or file facility and modify any * existing one. Special loggers must be created elsewhere. * Note that you cannot use a log { facility {... }} to modify one * of these special loggers because log block parsing is done first * at server initialization. */ static int facility_commit(void *node, void *link_mem, void *self_struct, struct config_error_type *err_type) { struct facility_config *conf = self_struct; struct glist_head *fac_list; int errcnt = 0; if (conf->facility_name == NULL) { LogCrit(COMPONENT_LOG, "No facility name given"); err_type->missing = true; errcnt++; return errcnt; } if (conf->dest != NULL) { if (strcasecmp(conf->dest, "stderr") == 0) { conf->func = log_to_stream; conf->lf_private = stderr; if (conf->headers == NB_LH_TYPES) conf->headers = LH_ALL; } else if (strcasecmp(conf->dest, "stdout") == 0) { conf->func = log_to_stream; conf->lf_private = stdout; if (conf->headers == NB_LH_TYPES) conf->headers = LH_ALL; } else if (strcasecmp(conf->dest, "syslog") == 0) { conf->func = log_to_syslog; if (conf->headers == NB_LH_TYPES) conf->headers = LH_COMPONENT; } else { conf->func = log_to_file; conf->lf_private = conf->dest; if (conf->headers == NB_LH_TYPES) conf->headers = LH_ALL; } } else { LogCrit(COMPONENT_LOG, "No facility destination given for (%s)", conf->facility_name); err_type->missing = true; errcnt++; return errcnt; } if (conf->func != log_to_syslog && conf->headers < LH_ALL) LogWarn(COMPONENT_CONFIG, "Headers setting for %s could drop some format fields!", conf->facility_name); if (conf->max_level == NB_LOG_LEVEL) conf->max_level = NIV_FULL_DEBUG; fac_list = link_mem; glist_add_tail(fac_list, &conf->fac_list); return 0; } static void *log_conf_init(void *link_mem, void *self_struct) { struct logger_config *logger = self_struct; assert(link_mem != NULL || self_struct != NULL); if (link_mem == NULL) return self_struct; else if (self_struct == NULL) return link_mem; else { if (logger->comp_log_level) { (void)component_init(&logger->comp_log_level, logger->comp_log_level); logger->comp_log_level = NULL; } if (!glist_empty(&logger->facility_list)) { struct glist_head *glist, *glistn; glist_for_each_safe(glist, glistn, &logger->facility_list) { struct facility_config *conf; conf = glist_entry(glist, struct facility_config, fac_list); glist_del(&conf->fac_list); (void)facility_init(&logger->facility_list, conf); } } if (logger->logfields != NULL) { (void)format_init(&logger->logfields, logger->logfields); logger->logfields = NULL; } } return NULL; } static int log_conf_commit(void *node, void *link_mem, void *self_struct, struct config_error_type *err_type) { struct logger_config *logger = self_struct; struct glist_head *glist, *glistn; int errcnt = 0; int rc; glist_for_each_safe(glist, glistn, &logger->facility_list) { struct facility_config *conf; bool facility_exists; conf = glist_entry(glist, struct facility_config, fac_list); glist_del(&conf->fac_list); if (errcnt) { LogEvent(COMPONENT_CONFIG, "Skipping facility (%s) due to errors", conf->facility_name); goto done; } rc = create_log_facility(conf->facility_name, conf->func, conf->max_level, conf->headers, conf->lf_private); if (rc != 0 && rc != -EEXIST) { LogCrit(COMPONENT_CONFIG, "Failed to create facility (%s), (%s)", conf->facility_name, strerror(-rc)); err_type->resource = true; errcnt++; goto done; } facility_exists = (rc == -EEXIST); if (facility_exists && conf->dest != NULL) { rc = set_log_destination(conf->facility_name, conf->dest); if (rc < 0) { LogCrit(COMPONENT_LOG, "Could not set destination for (%s) because (%s)", conf->facility_name, strerror(-rc)); err_type->resource = true; errcnt++; goto done; } } if (facility_exists && conf->max_level != NB_LOG_LEVEL) { rc = set_log_level(conf->facility_name, conf->max_level); if (rc < 0) { LogCrit(COMPONENT_LOG, "Could not set severity level for (%s) because (%s)", conf->facility_name, strerror(-rc)); err_type->resource = true; errcnt++; goto done; } } if (conf->state == FAC_ACTIVE) { rc = enable_log_facility(conf->facility_name); if (rc != 0) { LogCrit(COMPONENT_CONFIG, "Could not enable (%s) because (%s)", conf->facility_name, strerror(-rc)); err_type->resource = true; errcnt++; } } else if (conf->state == FAC_DEFAULT) { struct log_facility *old_def = default_facility; rc = set_default_log_facility(conf->facility_name); if (rc != 0) { LogCrit(COMPONENT_CONFIG, "Could not make (%s) the default because (%s)", conf->facility_name, strerror(-rc)); err_type->resource = true; errcnt++; } else if (old_def != default_facility) LogEvent(COMPONENT_CONFIG, "Switched default logger from %s to %s", old_def->lf_name, default_facility->lf_name); } if (errcnt > 0 && !facility_exists) { LogCrit(COMPONENT_CONFIG, "Releasing new logger (%s) because of errors", conf->facility_name); release_log_facility(conf->facility_name); } done: (void)facility_init(&logger->facility_list, conf); } if (errcnt == 0) { if (logger->logfields != NULL) { LogEvent(COMPONENT_CONFIG, "Changing definition of log fields"); if (logfields != &default_logfields) { if (logfields->user_date_fmt != NULL) gsh_free(logfields->user_date_fmt); if (logfields->user_time_fmt != NULL) gsh_free(logfields->user_time_fmt); gsh_free(logfields); } logfields = logger->logfields; /* rebuild const_log_str with new format params. */ set_const_log_str(); } if (logger->comp_log_level != NULL) { LogEvent(COMPONENT_CONFIG, "Switching to new component log levels"); if (component_log_level != default_log_levels) gsh_free(component_log_level); component_log_level = logger->comp_log_level; } ntirpc_pp.debug_flags = logger->rpc_debug_flags; SetNTIRPCLogLevel(component_log_level[COMPONENT_TIRPC]); } else { if (logger->logfields != NULL) { struct logfields *lf = logger->logfields; if (lf->user_date_fmt != NULL) gsh_free(lf->user_date_fmt); if (lf->user_time_fmt != NULL) gsh_free(lf->user_time_fmt); gsh_free(lf); } if (logger->comp_log_level != NULL) gsh_free(logger->comp_log_level); } logger->logfields = NULL; logger->comp_log_level = NULL; return errcnt; } static struct config_item logging_params[] = { CONF_ITEM_TOKEN("Default_log_level", NB_LOG_LEVEL, log_levels, logger_config, default_level), CONF_ITEM_UI32("RPC_Debug_Flags", 0, UINT32_MAX, TIRPC_DEBUG_FLAG_DEFAULT, logger_config, rpc_debug_flags), CONF_ITEM_BLOCK("Facility", facility_params, facility_init, facility_commit, logger_config, facility_list), CONF_ITEM_BLOCK("Format", format_options, format_init, format_commit, logger_config, logfields), CONF_ITEM_BLOCK("Components", component_levels, component_init, component_commit, logger_config, comp_log_level), CONFIG_EOL }; struct config_block logging_param = { .dbus_interface_name = "org.ganesha.nfsd.config.log", .blk_desc.name = "LOG", .blk_desc.type = CONFIG_BLOCK, .blk_desc.flags = CONFIG_UNIQUE, /* too risky to have more */ .blk_desc.u.blk.init = log_conf_init, .blk_desc.u.blk.params = logging_params, .blk_desc.u.blk.commit = log_conf_commit }; /** * * @brief Process the config parse tree for the logging component. * * Switch from the default component levels only if we found one * @param in_config [IN] configuration file handle * * @return 0 if ok, -1 if failed, * */ int read_log_config(config_file_t in_config, struct config_error_type *err_type) { struct logger_config logger; memset(&logger, 0, sizeof(struct logger_config)); (void)load_config_from_parse(in_config, &logging_param, &logger, true, err_type); if (config_error_is_harmless(err_type)) return 0; else return -1; } /* read_log_config */ nfs-ganesha-2.6.0/src/log/maketest.conf000066400000000000000000000022471324272410200177470ustar00rootroot00000000000000# # Fichier de configuration de test # # $Header: /cea/home/cvs/cvs/SHERPA/BaseCvs/GANESHA/src/Log/maketest.conf,v 1.4 2005/02/17 12:58:28 leibovic Exp $ # # $Log: maketest.conf,v $ # Revision 1.4 2005/02/17 12:58:28 leibovic # Added multhithread test. # # Revision 1.3 2004/11/18 10:36:20 deniel # Dependence corrected in the GNUmakefiles # # Revision 1.2 2004/10/22 09:24:30 deniel # No more dynamic libraries compiled # # Revision 1.1 2004/09/30 14:08:42 deniel # Ajout des log en anglais (a partir des logs aglae) # # # Test Test_liblog_Standard { Product = Static version of the log library Command = ./test_liblog_STD.sh Comment = Check for correctness of the log Failure BadStatus { STATUS != 0 } Success TestOk { STDOUT =~ /PASSED/ AND STATUS == 0 } } Test Test_liblog_Multithread { Product = Static version of the log library Command = ./test_liblog_MT.sh Comment = Check for correctness of the log Failure BadStatus { STATUS != 0 } Success TestOk { STATUS == 0 } } nfs-ganesha-2.6.0/src/log/test_display.c000066400000000000000000000065661324272410200201430ustar00rootroot00000000000000#include "display.h" void show_display_buffer(struct display_buffer *dspbuf, char *cmt) { printf("%s size=%d len=%d buffer=%s\n", cmt, dspbuf->b_size, strlen(dspbuf->b_start), dspbuf->b_start); } int main(int argc, char **argv) { char *opaque1 = "this-is-opaque"; char *opaque2 = "\3\4\012\65\0"; char *opaque3 = "\3\4\012\65\0\55\66"; char *opaque4 = "aaa\012\0"; char buffer[10]; struct display_buffer display = { sizeof(buffer), buffer, buffer }; char buffer2[200]; struct display_buffer display2 = { sizeof(buffer2), buffer2, buffer2 }; char buffer3[14]; struct display_buffer display3 = { sizeof(buffer3), buffer3, buffer3 }; (void)display_printf(&display, "%s", "foo"); (void)display_printf(&display, "%s", "foo"); show_display_buffer(&display, "first test (foo, foo)"); display_reset_buffer(&display); (void)display_printf(&display, "%s", "foo"); (void)display_printf(&display, "%s", "foo"); (void)display_printf(&display, "%s", "food"); (void)display_printf(&display, "%s", "foo"); show_display_buffer(&display, "second test (foo, foo, food, foo)"); display_reset_buffer(&display); (void)display_printf(&display, "%s", "foo"); (void)display_printf(&display, "%s", "foo"); (void)display_printf(&display, "%s", "foo"); show_display_buffer(&display, "third test (foo, foo, foo)"); display_reset_buffer(&display); display_reset_buffer(&display); (void)display_printf(&display, "%s", "foo"); (void)display_printf(&display, "%s", "foo"); (void)display_printf(&display, "%s", "foo"); (void)display_printf(&display, "%s", "f"); show_display_buffer(&display, "fourth test (foo, foo, foo, f)"); display_reset_buffer(&display); (void)display_printf(&display, "%d %d", 5, 50000000); (void)display_printf(&display2, "%d %d", 5, 50000000); show_display_buffer(&display, "fifth test (%d %d)"); show_display_buffer(&display2, "fifth test (%d %d)"); display_reset_buffer(&display); display_reset_buffer(&display2); (void)display_opaque_value(&display, opaque1, strlen(opaque1)); show_display_buffer(&display, "opaque1"); display_reset_buffer(&display); (void)display_opaque_value(&display, opaque2, strlen(opaque2)); show_display_buffer(&display, "opaque2"); display_reset_buffer(&display); (void)display_opaque_value(&display, opaque3, strlen(opaque3) + 3); show_display_buffer(&display, "opaque3"); display_reset_buffer(&display2); (void)display_opaque_value(&display2, opaque1, strlen(opaque1)); show_display_buffer(&display2, "opaque1"); display_reset_buffer(&display2); (void)display_opaque_value(&display2, opaque2, strlen(opaque2)); show_display_buffer(&display2, "opaque2"); display_reset_buffer(&display2); (void)display_opaque_value(&display2, opaque3, strlen(opaque3) + 3); show_display_buffer(&display2, "opaque3"); display_reset_buffer(&display2); (void)display_opaque_value(&display2, opaque4, strlen(opaque4)); show_display_buffer(&display2, "opaque4"); display_reset_buffer(&display2); display_reset_buffer(&display3); (void)display_opaque_value(&display3, opaque1, strlen(opaque1)); show_display_buffer(&display3, "opaque1"); display_reset_buffer(&display3); (void)display_opaque_value(&display3, opaque2, strlen(opaque2)); show_display_buffer(&display3, "opaque2"); display_reset_buffer(&display3); (void)display_opaque_value(&display3, opaque3, strlen(opaque3) + 3); show_display_buffer(&display3, "opaque3"); display_reset_buffer(&display3); return 0; } nfs-ganesha-2.6.0/src/nfs-ganesha.spec-in.cmake000066400000000000000000000527361324272410200212430ustar00rootroot00000000000000%define __arch_install_post /usr/lib/rpm/check-rpaths /usr/lib/rpm/check-buildroot %if 0%{?fedora} >= 15 || 0%{?rhel} >= 7 %global with_nfsidmap 1 %else %global with_nfsidmap 0 %endif %if ( 0%{?fedora} >= 18 || 0%{?rhel} >= 7 ) %global with_systemd 1 %else %global with_systemd 0 %endif %if ( 0%{?suse_version} ) BuildRequires: distribution-release %if ( ! 0%{?is_opensuse} ) BuildRequires: sles-release >= 12 Requires: sles-release >= 12 %else BuildRequires: openSUSE-release Requires: openSUSE-release %endif %global with_systemd 1 %global with_nfsidmap 1 %endif # Conditionally enable some FSALs, disable others. # # 1. rpmbuild accepts these options (gpfs as example): # --with gpfs # --without gpfs %define on_off_switch() %%{?with_%1:ON}%%{!?with_%1:OFF} # A few explanation about %bcond_with and %bcond_without # /!\ be careful: this syntax can be quite messy # %bcond_with means you add a "--with" option, default = without this feature # %bcond_without adds a"--without" so the feature is enabled by default @BCOND_NULLFS@ nullfs %global use_fsal_null %{on_off_switch nullfs} @BCOND_MEM@ mem %global use_fsal_mem %{on_off_switch mem} @BCOND_GPFS@ gpfs %global use_fsal_gpfs %{on_off_switch gpfs} @BCOND_XFS@ xfs %global use_fsal_xfs %{on_off_switch xfs} @BCOND_CEPH@ ceph %global use_fsal_ceph %{on_off_switch ceph} @BCOND_RGW@ rgw %global use_fsal_rgw %{on_off_switch rgw} @BCOND_GLUSTER@ gluster %global use_fsal_gluster %{on_off_switch gluster} @BCOND_PANFS@ panfs %global use_fsal_panfs %{on_off_switch panfs} @BCOND_RDMA@ rdma %global use_rdma %{on_off_switch rdma} @BCOND_JEMALLOC@ jemalloc @BCOND_LTTNG@ lttng %global use_lttng %{on_off_switch lttng} @BCOND_UTILS@ utils %global use_utils %{on_off_switch utils} @BCOND_GUI_UTILS@ gui_utils %global use_gui_utils %{on_off_switch gui_utils} @BCOND_NTIRPC@ system_ntirpc %global use_system_ntirpc %{on_off_switch system_ntirpc} @BCOND_MAN_PAGE@ man_page %global use_man_page %{on_off_switch man_page} @BCOND_RADOS_RECOV@ rados_recov %global use_rados_recov %{on_off_switch rados_recov} @BCOND_RADOS_URLS@ rados_urls %global use_rados_urls %{on_off_switch rados_urls} %global dev_version %{lua: s = string.gsub('@GANESHA_EXTRA_VERSION@', '^%-', ''); s2 = string.gsub(s, '%-', '.'); print((s2 ~= nil and s2 ~= '') and s2 or "0.1") } %define sourcename @CPACK_SOURCE_PACKAGE_FILE_NAME@ Name: nfs-ganesha Version: @GANESHA_BASE_VERSION@ Release: %{dev_version}%{?dist} Summary: NFS-Ganesha is a NFS Server running in user space Group: Applications/System License: LGPLv3+ Url: https://github.com/nfs-ganesha/nfs-ganesha/wiki Source: %{sourcename}.tar.gz BuildRequires: cmake BuildRequires: bison flex BuildRequires: flex BuildRequires: pkgconfig BuildRequires: krb5-devel %if ( 0%{?suse_version} >= 1330 ) BuildRequires: libnsl-devel %endif %if ( 0%{?suse_version} ) BuildRequires: dbus-1-devel Requires: dbus-1 %else BuildRequires: dbus-devel Requires: dbus %endif %if ( 0%{?suse_version} ) BuildRequires: systemd-rpm-macros %endif BuildRequires: libcap-devel BuildRequires: libblkid-devel BuildRequires: libuuid-devel BuildRequires: gcc-c++ %if %{with system_ntirpc} BuildRequires: libntirpc-devel >= @NTIRPC_MIN_VERSION@ %else Requires: libntirpc = @NTIRPC_VERSION_EMBED@ %endif Requires: nfs-utils %if ( 0%{?fedora} ) || ( 0%{?rhel} && 0%{?rhel} >= 6 ) || ( 0%{?suse_version} ) Requires: rpcbind %else Requires: portmap %endif %if %{with_nfsidmap} %if ( 0%{?suse_version} ) BuildRequires: nfsidmap-devel %else BuildRequires: libnfsidmap-devel %endif %else BuildRequires: nfs-utils-lib-devel %endif %if %{with rdma} BuildRequires: libmooshika-devel >= 0.6-0 %endif %if %{with jemalloc} BuildRequires: jemalloc-devel %endif %if %{with_systemd} BuildRequires: systemd Requires(post): systemd Requires(preun): systemd Requires(postun): systemd %else BuildRequires: initscripts %endif %if %{with man_page} BuildRequires: python-sphinx %endif Requires(post): psmisc Requires(pre): shadow-utils # Use CMake variables %description nfs-ganesha : NFS-GANESHA is a NFS Server running in user space. It comes with various back-end modules (called FSALs) provided as shared objects to support different file systems and name-spaces. %package mount-9P Summary: a 9p mount helper Group: Applications/System %description mount-9P This package contains the mount.9P script that clients can use to simplify mounting to NFS-GANESHA. This is a 9p mount helper. %package vfs Summary: The NFS-GANESHA's VFS FSAL Group: Applications/System BuildRequires: libattr-devel Requires: nfs-ganesha = %{version}-%{release} %description vfs This package contains a FSAL shared object to be used with NFS-Ganesha to support VFS based filesystems %package proxy Summary: The NFS-GANESHA's PROXY FSAL Group: Applications/System BuildRequires: libattr-devel Requires: nfs-ganesha = %{version}-%{release} %description proxy This package contains a FSAL shared object to be used with NFS-Ganesha to support PROXY based filesystems %if %{with utils} %package utils Summary: The NFS-GANESHA's util scripts Group: Applications/System %if ( 0%{?suse_version} ) Requires: dbus-1-python, python-gobject2, python-pyparsing %else Requires: dbus-python, pygobject2, pyparsing %endif %if %{with gui_utils} %if ( 0%{?suse_version} ) BuildRequires: python-qt4-devel Requires: python-qt4 %else BuildRequires: PyQt4-devel Requires: PyQt4 %endif %endif %if ( 0%{?suse_version} ) BuildRequires: python-devel Requires: nfs-ganesha = %{version}-%{release}, python %else BuildRequires: python2-devel Requires: nfs-ganesha = %{version}-%{release}, python2 %endif %description utils This package contains utility scripts for managing the NFS-GANESHA server %endif %if %{with lttng} %package lttng Summary: The NFS-GANESHA's library for use with LTTng Group: Applications/System BuildRequires: lttng-ust-devel >= 2.3 Requires: nfs-ganesha = %{version}-%{release}, lttng-tools >= 2.3, lttng-ust >= 2.3 %description lttng This package contains the libganesha_trace.so library. When preloaded to the ganesha.nfsd server, it makes it possible to trace using LTTng. %endif %if %{with rados_recov} %package rados Summary: The NFS-GANESHA's library for recovery backend Group: Applications/System BuildRequires: librados-devel >= 0.61 Requires: nfs-ganesha = %{version}-%{release} %description rados This package contains the librados.so library. Ganesha uses it to store client tracking data in ceph cluster. %endif # Option packages start here. use "rpmbuild --with gpfs" (or equivalent) # for activating this part of the spec file # NULL %if %{with nullfs} %package nullfs Summary: The NFS-GANESHA's NULLFS Stackable FSAL Group: Applications/System Requires: nfs-ganesha = %{version}-%{release} %description nullfs This package contains a Stackable FSAL shared object to be used with NFS-Ganesha. This is mostly a template for future (more sophisticated) stackable FSALs %endif # MEM %if %{with mem} %package mem Summary: The NFS-GANESHA's Memory backed testing FSAL Group: Applications/System Requires: nfs-ganesha = %{version}-%{release} %description mem This package contains a FSAL shared object to be used with NFS-Ganesha. This is used for speed and latency testing. %endif # GPFS %if %{with gpfs} %package gpfs Summary: The NFS-GANESHA's GPFS FSAL Group: Applications/System Requires: nfs-ganesha = %{version}-%{release} %description gpfs This package contains a FSAL shared object to be used with NFS-Ganesha to support GPFS backend %endif # CEPH %if %{with ceph} %package ceph Summary: The NFS-GANESHA's CephFS FSAL Group: Applications/System Requires: nfs-ganesha = %{version}-%{release} BuildRequires: libcephfs1-devel >= 10.2.0 %description ceph This package contains a FSAL shared object to be used with NFS-Ganesha to support CephFS %endif # RGW %if %{with rgw} %package rgw Summary: The NFS-GANESHA's Ceph RGW FSAL Group: Applications/System Requires: nfs-ganesha = %{version}-%{release} BuildRequires: librgw2-devel >= 10.2.0 %description rgw This package contains a FSAL shared object to be used with NFS-Ganesha to support Ceph RGW %endif # XFS %if %{with xfs} %package xfs Summary: The NFS-GANESHA's XFS FSAL Group: Applications/System Requires: nfs-ganesha = %{version}-%{release} BuildRequires: libattr-devel xfsprogs-devel %description xfs This package contains a shared object to be used with FSAL_VFS to support XFS correctly %endif # PANFS %if %{with panfs} %package panfs Summary: The NFS-GANESHA's PANFS FSAL Group: Applications/System Requires: nfs-ganesha = %{version}-%{release} %description panfs This package contains a FSAL shared object to be used with NFS-Ganesha to support PANFS %endif # GLUSTER %if %{with gluster} %package gluster Summary: The NFS-GANESHA's GLUSTER FSAL Group: Applications/System Requires: nfs-ganesha = %{version}-%{release} BuildRequires: glusterfs-api-devel >= 3.8 BuildRequires: libattr-devel, libacl-devel %description gluster This package contains a FSAL shared object to be used with NFS-Ganesha to support Gluster %endif # NTIRPC (if built-in) %if ! %{with system_ntirpc} %package -n libntirpc Summary: New Transport Independent RPC Library Group: System Environment/Libraries License: BSD Version: @NTIRPC_VERSION_EMBED@ Url: https://github.com/nfs-ganesha/ntirpc # libtirpc has /etc/netconfig, most machines probably have it anyway # for NFS client Requires: libtirpc %description -n libntirpc This package contains a new implementation of the original libtirpc, transport-independent RPC (TI-RPC) library for NFS-Ganesha. It has the following features not found in libtirpc: 1. Bi-directional operation 2. Full-duplex operation on the TCP (vc) transport 3. Thread-safe operating modes 3.1 new locking primitives and lock callouts (interface change) 3.2 stateless send/recv on the TCP transport (interface change) 4. Flexible server integration support 5. Event channels (remove static arrays of xprt handles, new EPOLL/KEVENT integration) %package -n libntirpc-devel Summary: Development headers for libntirpc Requires: libntirpc = @NTIRPC_VERSION_EMBED@ Group: System Environment/Libraries License: BSD Version: @NTIRPC_VERSION_EMBED@ Url: https://github.com/nfs-ganesha/ntirpc %description -n libntirpc-devel Development headers and auxiliary files for developing with %{name}. %endif %prep %setup -q -n %{sourcename} %build cmake . -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_CONFIG=rpmbuild \ -DUSE_FSAL_NULL=%{use_fsal_null} \ -DUSE_FSAL_MEM=%{use_fsal_mem} \ -DUSE_FSAL_XFS=%{use_fsal_xfs} \ -DUSE_FSAL_CEPH=%{use_fsal_ceph} \ -DUSE_FSAL_RGW=%{use_fsal_rgw} \ -DUSE_FSAL_GPFS=%{use_fsal_gpfs} \ -DUSE_FSAL_PANFS=%{use_fsal_panfs} \ -DUSE_FSAL_GLUSTER=%{use_fsal_gluster} \ -DUSE_SYSTEM_NTIRPC=%{use_system_ntirpc} \ -DUSE_9P_RDMA=%{use_rdma} \ -DUSE_LTTNG=%{use_lttng} \ -DUSE_ADMIN_TOOLS=%{use_utils} \ -DUSE_GUI_ADMIN_TOOLS=%{use_gui_utils} \ -DUSE_RADOS_RECOV=%{use_rados_recov} \ -DRADOS_URLS=%{use_rados_urls} \ -DUSE_FSAL_VFS=ON \ -DUSE_FSAL_PROXY=ON \ -DUSE_DBUS=ON \ -DUSE_9P=ON \ -DDISTNAME_HAS_GIT_DATA=OFF \ -DUSE_MAN_PAGE=%{use_man_page} \ %if %{with jemalloc} -DALLOCATOR=jemalloc %endif make %{?_smp_mflags} || make %{?_smp_mflags} || make %install mkdir -p %{buildroot}%{_sysconfdir}/ganesha/ mkdir -p %{buildroot}%{_sysconfdir}/dbus-1/system.d mkdir -p %{buildroot}%{_sysconfdir}/sysconfig mkdir -p %{buildroot}%{_sysconfdir}/logrotate.d mkdir -p %{buildroot}%{_bindir} mkdir -p %{buildroot}%{_sbindir} mkdir -p %{buildroot}%{_libdir}/ganesha mkdir -p %{buildroot}%{_localstatedir}/run/ganesha mkdir -p %{buildroot}%{_libexecdir}/ganesha install -m 644 config_samples/logrotate_ganesha %{buildroot}%{_sysconfdir}/logrotate.d/ganesha install -m 644 scripts/ganeshactl/org.ganesha.nfsd.conf %{buildroot}%{_sysconfdir}/dbus-1/system.d install -m 755 scripts/nfs-ganesha-config.sh %{buildroot}%{_libexecdir}/ganesha install -m 755 tools/mount.9P %{buildroot}%{_sbindir}/mount.9P install -m 644 config_samples/vfs.conf %{buildroot}%{_sysconfdir}/ganesha %if %{with_systemd} mkdir -p %{buildroot}%{_unitdir} install -m 644 scripts/systemd/nfs-ganesha.service.el7 %{buildroot}%{_unitdir}/nfs-ganesha.service install -m 644 scripts/systemd/nfs-ganesha-lock.service.el7 %{buildroot}%{_unitdir}/nfs-ganesha-lock.service install -m 644 scripts/systemd/nfs-ganesha-config.service %{buildroot}%{_unitdir}/nfs-ganesha-config.service install -m 644 scripts/systemd/sysconfig/nfs-ganesha %{buildroot}%{_sysconfdir}/sysconfig/ganesha %if 0%{?_tmpfilesdir:1} mkdir -p %{buildroot}%{_tmpfilesdir} install -m 644 scripts/systemd/tmpfiles.d/ganesha.conf %{buildroot}%{_tmpfilesdir} %endif mkdir -p %{buildroot}%{_localstatedir}/log/ganesha %else mkdir -p %{buildroot}%{_sysconfdir}/init.d install -m 755 scripts/init.d/nfs-ganesha.el6 %{buildroot}%{_sysconfdir}/init.d/nfs-ganesha install -m 644 scripts/init.d/sysconfig/ganesha %{buildroot}%{_sysconfdir}/sysconfig/ganesha %endif %if %{with pt} install -m 644 config_samples/pt.conf %{buildroot}%{_sysconfdir}/ganesha %endif %if %{with xfs} install -m 644 config_samples/xfs.conf %{buildroot}%{_sysconfdir}/ganesha %endif %if %{with ceph} install -m 644 config_samples/ceph.conf %{buildroot}%{_sysconfdir}/ganesha %endif %if %{with rgw} install -m 644 config_samples/rgw.conf %{buildroot}%{_sysconfdir}/ganesha install -m 644 config_samples/rgw_bucket.conf %{buildroot}%{_sysconfdir}/ganesha %endif %if %{with gluster} install -m 644 config_samples/logrotate_fsal_gluster %{buildroot}%{_sysconfdir}/logrotate.d/ganesha-gfapi %endif %if %{with gpfs} install -m 755 scripts/gpfs-epoch %{buildroot}%{_libexecdir}/ganesha install -m 644 config_samples/gpfs.conf %{buildroot}%{_sysconfdir}/ganesha install -m 644 config_samples/gpfs.ganesha.nfsd.conf %{buildroot}%{_sysconfdir}/ganesha install -m 644 config_samples/gpfs.ganesha.main.conf %{buildroot}%{_sysconfdir}/ganesha install -m 644 config_samples/gpfs.ganesha.log.conf %{buildroot}%{_sysconfdir}/ganesha install -m 644 config_samples/gpfs.ganesha.exports.conf %{buildroot}%{_sysconfdir}/ganesha %if ! %{with_systemd} mkdir -p %{buildroot}%{_sysconfdir}/init.d install -m 755 scripts/init.d/nfs-ganesha.gpfs %{buildroot}%{_sysconfdir}/init.d/nfs-ganesha-gpfs %endif %endif make DESTDIR=%{buildroot} install %post %if ( 0%{?suse_version} ) %service_add_post nfs-ganesha.service nfs-ganesha-lock.service nfs-ganesha-config.service %else %if ( 0%{?fedora} || ( 0%{?rhel} && 0%{?rhel} > 6 ) ) semanage fcontext -a -t ganesha_var_log_t %{_localstatedir}/log/ganesha > /dev/null 2>&1 || : semanage fcontext -a -t ganesha_var_log_t %{_localstatedir}/log/ganesha/ganesha.log > /dev/null 2>&1 || : %if %{with gluster} semanage fcontext -a -t ganesha_var_log_t %{_localstatedir}/log/ganesha/ganesha-gfapi.log > /dev/null 2>&1 || : %endif restorecon %{_localstatedir}/log/ganesha %endif %if %{with_systemd} %systemd_post nfs-ganesha.service %systemd_post nfs-ganesha-lock.service %systemd_post nfs-ganesha-config.service %endif %endif killall -SIGHUP dbus-daemon >/dev/null 2>&1 || : %pre getent group ganesha > /dev/null || groupadd -r ganesha getent passwd ganesha > /dev/null || useradd -r -g ganesha -d /var/run/ganesha -s /sbin/nologin -c "NFS-Ganesha Daemon" ganesha exit 0 %preun %if ( 0%{?suse_version} ) %service_del_preun nfs-ganesha-lock.service %else %if %{with_systemd} %systemd_preun nfs-ganesha-lock.service %endif %endif %postun %if ( 0%{?suse_version} ) %service_del_postun nfs-ganesha-lock.service %debug_package %else %if %{with_systemd} %systemd_postun_with_restart nfs-ganesha-lock.service %endif %endif %files %defattr(-,root,root,-) %{_bindir}/ganesha.nfsd %config %{_sysconfdir}/dbus-1/system.d/org.ganesha.nfsd.conf %config(noreplace) %{_sysconfdir}/sysconfig/ganesha %config(noreplace) %{_sysconfdir}/logrotate.d/ganesha %dir %{_sysconfdir}/ganesha/ %config(noreplace) %{_sysconfdir}/ganesha/ganesha.conf %dir %{_defaultdocdir}/ganesha/ %{_defaultdocdir}/ganesha/* %dir %{_localstatedir}/run/ganesha %dir %{_libexecdir}/ganesha %{_libexecdir}/ganesha/nfs-ganesha-config.sh %dir %attr(0775,ganesha,root) %{_localstatedir}/log/ganesha %if %{with_systemd} %{_unitdir}/nfs-ganesha.service %{_unitdir}/nfs-ganesha-lock.service %{_unitdir}/nfs-ganesha-config.service %if 0%{?_tmpfilesdir:1} %{_tmpfilesdir}/ganesha.conf %endif %else %{_sysconfdir}/init.d/nfs-ganesha %endif %if %{with man_page} %{_mandir}/*/ganesha-config.8.gz %{_mandir}/*/ganesha-core-config.8.gz %{_mandir}/*/ganesha-export-config.8.gz %{_mandir}/*/ganesha-cache-config.8.gz %{_mandir}/*/ganesha-log-config.8.gz %endif %files mount-9P %defattr(-,root,root,-) %{_sbindir}/mount.9P %if %{with man_page} %{_mandir}/*/ganesha-9p-config.8.gz %endif %files vfs %defattr(-,root,root,-) %{_libdir}/ganesha/libfsalvfs* %config(noreplace) %{_sysconfdir}/ganesha/vfs.conf %if %{with man_page} %{_mandir}/*/ganesha-vfs-config.8.gz %endif %files proxy %defattr(-,root,root,-) %{_libdir}/ganesha/libfsalproxy* %if %{with man_page} %{_mandir}/*/ganesha-proxy-config.8.gz %endif # Optional packages %if %{with nullfs} %files nullfs %defattr(-,root,root,-) %{_libdir}/ganesha/libfsalnull* %endif %if %{with mem} %files mem %defattr(-,root,root,-) %{_libdir}/ganesha/libfsalmem* %endif %if %{with gpfs} %files gpfs %defattr(-,root,root,-) %{_libdir}/ganesha/libfsalgpfs* %config(noreplace) %{_sysconfdir}/ganesha/gpfs.conf %config(noreplace) %{_sysconfdir}/ganesha/gpfs.ganesha.nfsd.conf %config(noreplace) %{_sysconfdir}/ganesha/gpfs.ganesha.main.conf %config(noreplace) %{_sysconfdir}/ganesha/gpfs.ganesha.log.conf %config(noreplace) %{_sysconfdir}/ganesha/gpfs.ganesha.exports.conf %{_libexecdir}/ganesha/gpfs-epoch %if ! %{with_systemd} %{_sysconfdir}/init.d/nfs-ganesha-gpfs %endif %if %{with man_page} %{_mandir}/*/ganesha-gpfs-config.8.gz %endif %endif %if %{with xfs} %files xfs %defattr(-,root,root,-) %{_libdir}/ganesha/libfsalxfs* %config(noreplace) %{_sysconfdir}/ganesha/xfs.conf %if %{with man_page} %{_mandir}/*/ganesha-xfs-config.8.gz %endif %endif %if %{with ceph} %files ceph %defattr(-,root,root,-) %{_libdir}/ganesha/libfsalceph* %config(noreplace) %{_sysconfdir}/ganesha/ceph.conf %if %{with man_page} %{_mandir}/*/ganesha-ceph-config.8.gz %endif %endif %if %{with rgw} %files rgw %defattr(-,root,root,-) %{_libdir}/ganesha/libfsalrgw* %config(noreplace) %{_sysconfdir}/ganesha/rgw.conf %config(noreplace) %{_sysconfdir}/ganesha/rgw_bucket.conf %if %{with man_page} %{_mandir}/*/ganesha-rgw-config.8.gz %endif %endif %if %{with gluster} %files gluster %defattr(-,root,root,-) %config(noreplace) %{_sysconfdir}/logrotate.d/ganesha-gfapi %{_libdir}/ganesha/libfsalgluster* %if %{with man_page} %{_mandir}/*/ganesha-gluster-config.8.gz %endif %endif %if ! %{with system_ntirpc} %files -n libntirpc %defattr(-,root,root,-) %{_libdir}/libntirpc.so.@NTIRPC_VERSION_EMBED@ %{_libdir}/libntirpc.so.@NTIRPC_ABI_EMBED@ %{_libdir}/libntirpc.so %{!?_licensedir:%global license %%doc} %license libntirpc/COPYING %doc libntirpc/NEWS libntirpc/README %files -n libntirpc-devel %{_libdir}/pkgconfig/libntirpc.pc %dir %{_includedir}/ntirpc %{_includedir}/ntirpc/* %endif %if %{with panfs} %files panfs %defattr(-,root,root,-) %{_libdir}/ganesha/libfsalpanfs* %endif %if %{with pt} %files pt %defattr(-,root,root,-) %{_libdir}/ganesha/libfsalpt* %config(noreplace) %{_sysconfdir}/ganesha/pt.conf %endif %if %{with lttng} %files lttng %defattr(-,root,root,-) %{_libdir}/ganesha/libganesha_trace* %endif %if %{with utils} %files utils %defattr(-,root,root,-) %if ( 0%{?suse_version} ) %{python_sitelib}/Ganesha/* %{python_sitelib}/ganeshactl-*-info %else %{python2_sitelib}/Ganesha/* %{python2_sitelib}/ganeshactl-*-info %endif %if %{with gui_utils} %{_bindir}/ganesha-admin %{_bindir}/manage_clients %{_bindir}/manage_exports %{_bindir}/manage_logger %{_bindir}/ganeshactl %{_bindir}/client_stats_9pOps %{_bindir}/export_stats_9pOps %endif %{_bindir}/fake_recall %{_bindir}/get_clientids %{_bindir}/grace_period %{_bindir}/purge_gids %{_bindir}/ganesha_stats %{_bindir}/sm_notify.ganesha %{_bindir}/ganesha_mgr %{_bindir}/ganesha_conf %{_mandir}/*/ganesha_conf.8.gz %endif %changelog * Tue Apr 21 2015 Philippe DENIEL 2.2 - Ganesha supports granting delegations - There have been numerous config changes - Ganesha now includes systemd scripts - Improved packaging for RPM and Debian - Major stability improvements - non-QT based python tools - Support for Ganesha to be a pNFS DS only, no MDS - SECINFO in preferred order - LTTng support - NFS v4.2 support - Major improvements in 9p support - Code cleanup (checkpatch and Coverity) - ntirpc improvements - FSAL_GLUSTER updated with pNFS and ACL support and more * Fri Jun 27 2014 Philippe DENIEL 2.1 - Exports are now dynamic. They can be added or removed via DBus commands. - The Pseudo filesystem has been re-written as a FSAL - The configuration file processing has been rewritten to improve error checking and logging. - GIDs can now be managed to use external authentication sources. Altgroups with AUTH_SYS can be larger than 16. - RPM packaging has been restructured and updated. The DBus tools are now packaged. * Thu Nov 21 2013 Philippe DENIEL 2.O - FSALs (filesystem backends) are now loadable shared objects. - The server can support multiple backends at runtime. - NFSv4.1 pNFS is supported. - DBus is now the administration tool. - All the significant bugfixes from the 1.5.x branch have been backported - The server passes all of the cthonv4 and pynfs 4.0 tests. - All of the significant (non-delegation) pynfs 4.1 tests also pass. - NFSv2 support has been deprecated. - NFSv3 still supports the older version of the MNT protocol for compatibility - The build process has been converted to Cmake - The codebase has been reformatted to conform to Linux kernel coding style. nfs-ganesha-2.6.0/src/os/000077500000000000000000000000001324272410200151165ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/os/CMakeLists.txt000066400000000000000000000011641324272410200176600ustar00rootroot00000000000000# All we need to do here is control the # build of chosen platform if(FREEBSD) SET(gos_STAT_SRCS freebsd/atsyscalls.c freebsd/mntent_compat.c freebsd/subr.c freebsd/xattr.c freebsd/memstream.c ) endif(FREEBSD) if(LINUX) SET(gos_STAT_SRCS linux/subr.c ) endif(LINUX) add_library(gos STATIC ${gos_STAT_SRCS}) add_sanitizers(gos) ########### install files ############### # This is GCC specific to force PIC compiles. # cmake 2.8.9 has a portable POSITION_INDEPENDENT_CODE property that can be # used when it is available set_target_properties(gos PROPERTIES COMPILE_FLAGS "-fPIC") nfs-ganesha-2.6.0/src/os/freebsd/000077500000000000000000000000001324272410200165305ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/os/freebsd/atsyscalls.c000066400000000000000000000132231324272410200210570ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Sachin Bhamare sbhamare@panasas.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * */ /** * @file atsyscalls.c * @brief platform dependent syscalls */ #include #include #include #include #include "syscalls.h" #include #include #if __FreeBSD_cc_version >= 800001 /* Fllowing syscalls are not yet implemented in vanilla FreeBSD kernels */ int getfhat(int dir_fd, char *fname, struct fhandle *fhp, int flag) { int mod, err; int syscall_num; struct module_stat stat; stat.version = sizeof(stat); /* modstat will retrieve the module_stat structure for our module named * syscall (see the SYSCALL_MODULE macro which sets the name to syscall) */ mod = modfind("sys/getfhat"); if (mod == -1) return errno; err = modstat(mod, &stat); if (err) return errno; /* extract the slot (syscall) number */ syscall_num = stat.data.intval; /* int getfhat(int fd, char *path, struct fhandle *fhp, int flag); */ return syscall(syscall_num, dir_fd, fname, fhp, flag); } int fhlink(struct fhandle *fhp, int tofd, const char *to) { int mod, err; int syscall_num; struct module_stat stat; stat.version = sizeof(stat); /* modstat will retrieve the module_stat structure for our module named * syscall (see the SYSCALL_MODULE macro which sets the name to syscall) */ mod = modfind("sys/fhlink"); if (mod == -1) return errno; err = modstat(mod, &stat); if (err) return errno; /* extract the slot (syscall) number */ syscall_num = stat.data.intval; /* int fhlink(struct fhandle *fhp, int tofd, const char *to); */ return syscall(syscall_num, fhp, tofd, to); } int fhreadlink(struct fhandle *fhp, char *buf, size_t bufsize) { int mod, err; int syscall_num; struct module_stat stat; stat.version = sizeof(stat); /* modstat will retrieve the module_stat structure for our module named * syscall (see the SYSCALL_MODULE macro which sets the name to syscall) */ mod = modfind("sys/fhreadlink"); if (mod == -1) return errno; err = modstat(mod, &stat); if (err) return errno; /* extract the slot (syscall) number */ syscall_num = stat.data.intval; /* int fhreadlink(struct fhandle *fhp, char *buf, size_t bufsize); */ return syscall(syscall_num, fhp, buf, bufsize); } #endif #ifndef SYS_openat /* * Allow compliation (only) on FreeBSD versions without these syscalls * These numbers match the modified FreeBSD 10.1 used by Panasas * */ #define SYS_faccessat 512 #define SYS_fchmodat 513 #define SYS_fchownat 514 #define SYS_fstatat 515 #define SYS_futimesat 516 #define SYS_linkat 517 #define SYS_mkdirat 518 #define SYS_mkfifoat 519 #define SYS_mknodat 520 #define SYS_openat 521 #define SYS_readlinkat 522 #define SYS_renameat 523 #define SYS_symlinkat 524 #define SYS_unlinkat 525 #define SYS_getfhat 526 #define SYS_fhlink 527 #define SYS_fhreadlink 528 int openat(int dir_fd, const char *file, int oflag, mode_t mode) { return syscall(SYS_openat, dir_fd, file, oflag, mode); } int mkdirat(int dir_fd, const char *file, mode_t mode) { return syscall(SYS_mkdirat, dir_fd, file, mode); } int mknodat(int dir_fd, const char *file, mode_t mode, dev_t dev) { return syscall(SYS_mknodat, dir_fd, file, mode, dev); } int fchownat(int dir_fd, const char *file, uid_t owner, gid_t group, int flag) { return syscall(SYS_fchownat, dir_fd, file, owner, group, flag); } int futimesat(int dir_fd, char *filename, struct timeval *utimes) { return syscall(SYS_futimesat, dir_fd, filename, utimes); } int fstatat(int dir_fd, const char *file, struct stat *st, int flag) { return syscall(SYS_fstatat, dir_fd, file, st, flag); } int unlinkat(int dir_fd, const char *file, int flag) { return syscall(SYS_unlinkat, dir_fd, file, flag); } int renameat(int oldfd, const char *old, int newfd, const char *new) { return syscall(SYS_renameat, oldfd, old, newfd, new); } int linkat(int fromfd, const char *from, int tofd, const char *to, int flags) { return syscall(SYS_linkat, fromfd, from, tofd, to, flags); } int symlinkat(const char *from, int tofd, const char *to) { return syscall(SYS_symlinkat, from, tofd, to); } int readlinkat(int fd, const char *path, char *buf, size_t len) { return syscall(SYS_readlinkat, fd, path, buf, len); } int fchmodat(int dir_fd, const char *filename, mode_t mode, int flags) { return syscall(SYS_fchmodat, dir_fd, filename, mode, flags); } int faccessat(int dir_fd, char *filename, int mode, int flags) { return syscall(SYS_faccessat, dir_fd, filename, mode, flags); } int getfhat(int dir_fd, char *fname, struct fhandle *fhp, int flag) { return syscall(SYS_getfhat, dir_fd, fname, fhp, flag); } int fhlink(struct fhandle *fhp, int tofd, const char *to) { return syscall(SYS_fhlink, fhp, tofd, to); } int fhreadlink(struct fhandle *fhp, char *buf, size_t bufsize) { return syscall(SYS_fhreadlink, fhp, buf, bufsize); } #endif /* SYS_openat */ nfs-ganesha-2.6.0/src/os/freebsd/memstream.c000066400000000000000000000067171324272410200207010ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Contributeur: Sachin Bhamare sbhamare@panasas.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @file memstream.c * @brief Set of function to provide open_memstream() API on FreeBSD * * Following set of function facilitate open_memstream API which * really is a wrapper around funopen() call on FreeBSD * platform. These are taken from the implementation at * http://people.freebsd.org/~jhb/mcelog/memstream.c and all the * relevant licences apply. */ #include #include #include #include #include #include "abstract_mem.h" struct memstream { char **cp; size_t *lenp; size_t offset; }; static void memstream_grow(struct memstream *ms, size_t newsize) { char *buf; if (newsize > *ms->lenp) { buf = gsh_realloc(*ms->cp, newsize + 1); #ifdef DEBUG fprintf(stderr, "MS: %p growing from %zd to %zd\n", ms, *ms->lenp, newsize); #endif memset(buf + *ms->lenp + 1, 0, newsize - *ms->lenp); *ms->cp = buf; *ms->lenp = newsize; } } static int memstream_read(void *cookie, char *buf, int len) { struct memstream *ms; int tocopy; ms = cookie; memstream_grow(ms, ms->offset + len); tocopy = *ms->lenp - ms->offset; if (len < tocopy) tocopy = len; memcpy(buf, *ms->cp + ms->offset, tocopy); ms->offset += tocopy; #ifdef DEBUG fprintf(stderr, "MS: read(%p, %d) = %d\n", ms, len, tocopy); #endif return tocopy; } static int memstream_write(void *cookie, const char *buf, int len) { struct memstream *ms; int tocopy; ms = cookie; memstream_grow(ms, ms->offset + len); tocopy = *ms->lenp - ms->offset; if (len < tocopy) tocopy = len; memcpy(*ms->cp + ms->offset, buf, tocopy); ms->offset += tocopy; #ifdef DEBUG fprintf(stderr, "MS: write(%p, %d) = %d\n", ms, len, tocopy); #endif return tocopy; } static fpos_t memstream_seek(void *cookie, fpos_t pos, int whence) { struct memstream *ms; #ifdef DEBUG size_t old; #endif ms = cookie; #ifdef DEBUG old = ms->offset; #endif switch (whence) { case SEEK_SET: ms->offset = pos; break; case SEEK_CUR: ms->offset += pos; break; case SEEK_END: ms->offset = *ms->lenp + pos; break; } #ifdef DEBUG fprintf(stderr, "MS: seek(%p, %zd, %d) %zd -> %zd\n", ms, pos, whence, old, ms->offset); #endif return ms->offset; } static int memstream_close(void *cookie) { gsh_free(cookie); return 0; } FILE *open_memstream(char **cp, size_t *lenp) { struct memstream *ms; int save_errno; FILE *fp; *cp = NULL; *lenp = 0; ms = gsh_malloc(sizeof(*ms)); ms->cp = cp; ms->lenp = lenp; ms->offset = 0; fp = funopen(ms, memstream_read, memstream_write, memstream_seek, memstream_close); if (fp == NULL) { save_errno = errno; gsh_free(ms); errno = save_errno; } return fp; } nfs-ganesha-2.6.0/src/os/freebsd/mntent_compat.c000066400000000000000000000112641324272410200215500ustar00rootroot00000000000000/* * Copyright (c) 1980, 1989, 1993, 1994 * The Regents of the University of California. All rights reserved. * Copyright (c) 2001 * David Rufino * Copyright (c) 2006 * Stanislav Sedov * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* most of this was ripped from the mount(3) source */ #include "config.h" #include #include #include #include #include #include #include #include "abstract_mem.h" #include "log.h" static int pos = -1; static int mntsize = -1; static struct statfs *_mntbuf; static struct mntent _mntent; struct { int m_flag; const char *m_option; } mntoptions[] = { { MNT_ASYNC, "async"}, { MNT_NOATIME, "noatime"}, { MNT_NOEXEC, "noexec"}, { MNT_NOSUID, "nosuid"}, { MNT_NOSYMFOLLOW, "nosymfollow"}, { MNT_SYNCHRONOUS, "sync"}, { MNT_UNION, "union"}, { MNT_NOCLUSTERR, "noclusterr"}, { MNT_NOCLUSTERW, "noclusterw"}, { MNT_SUIDDIR, "suiddir"}, #ifdef MNT_SNAPSHOT { MNT_SNAPSHOT, "snapshot"}, #endif #ifdef MNT_MULTILABEL { MNT_MULTILABEL, "multilabel"}, #endif #ifdef MNT_ACLS { MNT_ACLS, "acls"}, #endif #ifdef MNT_NODEV { MNT_NODEV, "nodev"}, #endif }; #define N_OPTS (sizeof(mntoptions) / sizeof(*mntoptions)) char *hasmntopt(const struct mntent *mnt, const char *option) { int found; char *opt, *optbuf; optbuf = gsh_strdup(mnt->mnt_opts); found = 0; for (opt = optbuf; (opt = strtok(opt, " ")) != NULL; opt = NULL) { if (!strcasecmp(opt, option)) { opt = opt - optbuf + mnt->mnt_opts; gsh_free(optbuf); return opt; } } gsh_free(optbuf); return NULL; } static char *catopt(char *s0, const char *s1) { size_t newlen; char *cp; if (s1 == NULL || *s1 == '\0') return s0; if (s0 != NULL) { newlen = strlen(s0) + strlen(s1) + 1 + 1; cp = gsh_realloc(s0, newlen); strcat(cp, " "); strcat(cp, s1); } else cp = gsh_strdup(s1); return cp; } static char *flags2opts(int flags) { char *res = NULL; int i; res = catopt(res, (flags & MNT_RDONLY) ? "ro" : "rw"); for (i = 0; i < N_OPTS; i++) if (flags & mntoptions[i].m_flag) res = catopt(res, mntoptions[i].m_option); return res; } static struct mntent *statfs_to_mntent(struct statfs *mntbuf) { static char opts_buf[40], *tmp; _mntent.mnt_fsname = mntbuf->f_mntfromname; _mntent.mnt_dir = mntbuf->f_mntonname; _mntent.mnt_type = mntbuf->f_fstypename; tmp = flags2opts(mntbuf->f_flags); if (tmp) { opts_buf[sizeof(opts_buf) - 1] = '\0'; strncpy(opts_buf, tmp, sizeof(opts_buf) - 1); gsh_free(tmp); } else { *opts_buf = '\0'; } _mntent.mnt_opts = opts_buf; _mntent.mnt_freq = _mntent.mnt_passno = 0; return &_mntent; } struct mntent *getmntent(FILE *fp) { int i; if (pos == -1 || mntsize == -1) mntsize = getmntinfo(&_mntbuf, MNT_WAIT); for (i = 0; i < mntsize; i++) LogFullDebug(COMPONENT_FSAL, "%s", _mntbuf[i].f_mntfromname); ++pos; if (pos == mntsize) { pos = mntsize = -1; return NULL; } return statfs_to_mntent(&_mntbuf[pos]); } nfs-ganesha-2.6.0/src/os/freebsd/subr.c000066400000000000000000000107041324272410200176510ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Sachin Bhamare sbhamare@panasas.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @file os/freebsd/subr.c * @author Sachin Bhamare * @brief Platform dependant subroutines for FreeBSD */ #include #include #include #include #include #include #include #include "syscalls.h" /** * @brief Read system directory entries into the buffer * * @param[in] fd File descriptor of open directory * @param[in] buf The buffer * @param[in] bcount Buffer size * @param[in,out] basepp Offset into "file" after this read */ int vfs_readents(int fd, char *buf, unsigned int bcount, off_t *basepp) { return getdirentries(fd, buf, bcount, basepp); } /** * @brief Mash a FreeBSD directory entry into the generic form * * @param[in] buf Pointer into buffer read by vfs_readents * @param[in] bpos Offset into buffer * @param[in] vd Pointer to the generic struct * @param[in] base File offset for this entry in buffer. * * @return true if entry valid, false if not (empty) */ bool to_vfs_dirent(char *buf, int bpos, struct vfs_dirent *vd, off_t base) { struct dirent *dp = (struct dirent *)(buf + bpos); vd->vd_ino = dp->d_fileno; vd->vd_reclen = dp->d_reclen; vd->vd_type = dp->d_type; vd->vd_offset = base + bpos + dp->d_reclen; vd->vd_name = dp->d_name; return (dp->d_fileno != 0) ? true : false; } /** * @brief Following functions exist to compensate for lack of *times() APIs with * nanosecond granularity on FreeBSD platform. These functions also take care of * timespec <--> timeval conversion. */ #define TIMEVAL_TO_TIMESPEC(tv, ts) \ do { \ (ts)->tv_sec = (tv)->tv_sec; \ (ts)->tv_nsec = (tv)->tv_usec * 1000; \ } while (0) #define TIMESPEC_TO_TIMEVAL(tv, ts) \ do { \ (tv)->tv_sec = (ts)->tv_sec; \ (tv)->tv_usec = (ts)->tv_nsec / 1000; \ } while (0) int vfs_utimesat(int fd, const char *path, const struct timespec ts[2], int flags) { struct timeval tv[2]; if (ts[0].tv_nsec == UTIME_OMIT || ts[1].tv_nsec == UTIME_OMIT) { /* nothing to do */ return 0; } else if (ts[0].tv_nsec == UTIME_NOW || ts[1].tv_nsec == UTIME_NOW) { /* set to the current timestamp. achieve this by passing NULL * timeval to kernel */ return futimesat(fd, (char *)path, NULL); } TIMESPEC_TO_TIMEVAL(&tv[0], &ts[0]); TIMESPEC_TO_TIMEVAL(&tv[1], &ts[1]); return futimesat(fd, (char *)path, tv); } int vfs_utimes(int fd, const struct timespec *ts) { struct timeval tv[2]; if (ts[0].tv_nsec == UTIME_OMIT || ts[1].tv_nsec == UTIME_OMIT) { /* nothing to do */ return 0; } else if (ts[0].tv_nsec == UTIME_NOW || ts[1].tv_nsec == UTIME_NOW) { /* set to the current timestamp. achieve this by passing NULL * timeval to kernel */ return futimes(fd, NULL); } TIMESPEC_TO_TIMEVAL(&tv[0], &ts[0]); TIMESPEC_TO_TIMEVAL(&tv[1], &ts[1]); return futimes(fd, tv); } uid_t setuser(uid_t uid) { int rc = 0; uid_t orig_uid = syscall(SYS_getuid); rc = syscall(SYS_seteuid, uid); if (rc != 0) LogCrit(COMPONENT_FSAL, "Could not set user identity"); return orig_uid; } gid_t setgroup(gid_t gid) { int rc = 0; gid_t orig_gid = syscall(SYS_getgid); rc = syscall(SYS_setegid, gid); if (rc != 0) LogCrit(COMPONENT_FSAL, "Could not set group identity"); return orig_gid; } int set_threadgroups(size_t size, const gid_t *list) { return syscall(SYS_setgroups, size, list); } nfs-ganesha-2.6.0/src/os/freebsd/xattr.c000066400000000000000000000037101324272410200200370ustar00rootroot00000000000000/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2011 * Author: Sachin Bhamare sbhamare@panasas.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * */ /* xattr.c * VFS FSAL xattr support on FreeBSD platform */ #include #include #define EXTATTR_MAXNAMELEN 255 ssize_t fgetxattr(int fd, const char *name, void *value, size_t size) { return extattr_get_fd(fd, EXTATTR_NAMESPACE_SYSTEM, name, value, size); } ssize_t fsetxattr(int fd, const char *name, void *value, size_t size, int flags) { char buff[EXTATTR_MAXNAMELEN]; ssize_t attr_size = 0; errno = 0; attr_size = extattr_get_fd(fd, EXTATTR_NAMESPACE_SYSTEM, name, buff, size); if (attr_size != size && errno == ENOATTR) { /* attr we are trying to set doesn't exist. check if * XATTR_REPLACE was set */ if (flags & XATTR_REPLACE) return ENOATTR; } else { if (flags & XATTR_CREATE) return EEXIST; } return extattr_set_fd(fd, EXTATTR_NAMESPACE_SYSTEM, name, value, size); } ssize_t flistxattr(int fd, const char *list, size_t size) { return extattr_list_fd(fd, EXTATTR_NAMESPACE_SYSTEM, (void *)list, size); } ssize_t fremovexattr(int fd, const char *name) { return extattr_delete_fd(fd, EXTATTR_NAMESPACE_SYSTEM, name); } nfs-ganesha-2.6.0/src/os/linux/000077500000000000000000000000001324272410200162555ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/os/linux/subr.c000066400000000000000000000065371324272410200174070ustar00rootroot00000000000000/* * contributeur : Sachin Bhamare sbhamare@panasas.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file os/linux/subr.c * @author Sachin Bhamare * @brief Platform dependant subroutines for Linux * */ #include "fsal.h" #include #include #include #include #include #include "os/subr.h" /** * @brief Read system directory entries into the buffer * * @param[in] fd File descriptor of open directory * @param[in] buf The buffer * @param[in] bcount Buffer size * @param[in,out] basepp Offset into "file" after this read */ int vfs_readents(int fd, char *buf, unsigned int bcount, off_t *basepp) { int retval = 0; retval = syscall(SYS_getdents64, fd, buf, bcount); if (retval >= 0) *basepp += retval; return retval; } /** * @brief Mash a FreeBSD directory entry into the generic form * * @param buf [in] pointer into buffer read by vfs_readents * @param bpos [in] byte offset into buf to decode * @param vd [in] pointer to the generic struct * @param base [in] base file offset for this buffer - not used * * @return true. Linux entries are never empty. */ bool to_vfs_dirent(char *buf, int bpos, struct vfs_dirent *vd, off_t base) { struct dirent64 *dp = (struct dirent64 *)(buf + bpos); char type; vd->vd_ino = dp->d_ino; vd->vd_reclen = dp->d_reclen; type = buf[dp->d_reclen - 1]; vd->vd_type = type; vd->vd_offset = dp->d_off; vd->vd_name = dp->d_name; return true; } /** * @brief Platform specific wrapper for utimensat(). * * @param[in] fd File descriptor * @param[in] path Name of a file * @param[in] ts Array of struct timespec * @param[in] flags Flags * * @return 0 on success, -1 on error (errno set to indicate the error). */ int vfs_utimesat(int fd, const char *path, const struct timespec ts[2], int flags) { return utimensat(fd, path, ts, flags); } /** * @brief Platform specific wrapper fro futimens(). * * @param[in] fd File descriptor * @param[in] ts Array of struct timespec * * @return 0 on success, -1 on error (errno set to indicate the error). */ int vfs_utimes(int fd, const struct timespec *ts) { return futimens(fd, ts); } uid_t setuser(uid_t uid) { uid_t orig_uid = setfsuid(uid); if (uid != setfsuid(uid)) LogCrit(COMPONENT_FSAL, "Could not set user identity"); return orig_uid; } gid_t setgroup(gid_t gid) { gid_t orig_gid = setfsgid(gid); if (gid != setfsgid(gid)) LogCrit(COMPONENT_FSAL, "Could not set group identity"); return orig_gid; } int set_threadgroups(size_t size, const gid_t *list) { return syscall(__NR_setgroups, size, list); } nfs-ganesha-2.6.0/src/rpm_changelog000066400000000000000000000021121324272410200172210ustar00rootroot00000000000000* Thu Mar 15 2012 Philippe Deniel 1.4.0-1 - Bug Fix: Memory leak fixed in RPC's DRC - Bug Fix: Bad computation of rbt_value in RPC's DRC's hashtable - New FSAL_CEPH (dedicated to the ceph filesystem) - pNFS code refurbished with CEPH specific code and layout file support - All RPCs are now based on TIRPC - Bug Fix: in nfs_Write, for clean handle of umask, the server should allow the owner of a file to write to it, even if the file is read-only - Bug Fix: bad memory padding in fsal_handle_t and fsal_cookie_t - Git HEAD for this version : 4f0f982a04d76dda5372aa27f9a4221aba41bac5 - Git tag for this version: 1.4.0 * Fri Dec 23 2011 Philippe Deniel 1.3.0-1 - Lustre FSAL: exporting several Lustre filesystem with the same server instance - A bug was found and fix in OP4_READDIRPLUS (produced missing entries) - Directory content cached is now managed as a tree. - Lock support for FSAL_LUSTRE - FSAL_LUSTRE/FSAL_XFS/FSAL_FUSELIKE/FSAL_ZFS: regression in readdir detected and fixed - export list now support IP ranges using the CIDR format nfs-ganesha-2.6.0/src/scripts/000077500000000000000000000000001324272410200161645ustar00rootroot00000000000000nfs-ganesha-2.6.0/src/scripts/CMakeLists.txt000066400000000000000000000001121324272410200207160ustar00rootroot00000000000000if(USE_ADMIN_TOOLS) add_subdirectory(ganeshactl) endif(USE_ADMIN_TOOLS) nfs-ganesha-2.6.0/src/scripts/checkpatch.conf000066400000000000000000000050171324272410200211330ustar00rootroot00000000000000# This isn't actually a Linux kernel tree --no-tree # Errors that make no sense if we aren't the Linux kernel --ignore MODIFIED_INCLUDE_ASM --ignore LO_MACRO --ignore HI_MACRO --ignore CSYNC --ignore SSYNC --ignore UAPI_INCLUDE --ignore IN_ATOMIC --ignore LOCKDEP # Warnings specitifc to the Linux kernel --ignore USE_RELATIVE_PATH --ignore CONFIG_DESCRIPTION --ignore CONFIG_EXPERIMENTAL --ignore DEPRECATED_VARIABLE --ignore NETWORKING_BLOCK_COMMENT_STYLE --ignore HOTPLUG_SECTION --ignore EXPORT_SYMBOL --ignore DEFINE_PCI_DEVICE_TABLE --ignore LINUX_VERSION_CODE --ignore PRINTK_RATELIMITED --ignore PRINTK_WITHOUT_KERN_LEVEL --ignore PREFER_PR_LEVEL --ignore USE_NEGATIVE_ERRNO --ignore INCLUDE_LINUX --ignore MISSING_VMLINUX --ignore MSLEEP --ignore PREFER_PACKED --ignore PREFER_ALIGNED --ignore PREFER_PRINTF --ignore PREFER_SCANF --ignore USE_SPINLOCK_T --ignore PREFER_SEQ_PUTS --ignore USLEEP_RANGE --ignore KREALLOC_ARG_REUSE --ignore YIELD --ignore CONSIDER_KSTRTO --ignore USE_DEVICE_INITCALL --ignore NR_CPUS --ignore PRINTF_L --ignore EXPORTED_WORLD_WRITABLE --ignore FILE_PATH_CHANGES # Checks specitifc to the Linux kernel --ignore ARCH_INCLUDE_LINUX --ignore UNCOMMENTED_DEFINITION --ignore UNCOMMENTED_BARRIER --ignore ARCH_DEFINES --ignore UNDOCUMENTED_SETUP --ignore NEW_TYPEDEFS # Don't flag this, since we have LogThis and FSAL_that. --ignore CAMELCASE # Do not consider gerrit changeid as an error --ignore GERRIT_CHANGE_ID # Not being in the kernel, we don't have ARRAY_SIZE macro so some places # define it. --ignore ARRAY_SIZE # our coding style has too many if-then-else that have return in the middle # and thus create unnecessary else --ignore UNNECESSARY_ELSE # Allow janitor patches to cleanup checkpatch errors --ignore EMAIL_SUBJECT # We use full commit id when referencing other commits --ignore GIT_COMMIT_ID # We have the FSF address in a lot of our headers at the moment, # should clean that up one day but ignoring for now. --ignore FSF_MAILING_ADDRESS # We will no longer get grumpy about single line braces --ignore BRACES # @todo FSF work on removing these ones if they make sense... --ignore BLOCK_COMMENT_STYLE #--ignore GLOBAL_INITIALISERS #--ignore UNSPECIFIED_INT #--ignore LONG_LINE #--ignore SUSPECT_CODE_INDENT #--ignore CONSTANT_COMPARISON #--ignore LINE_SPACING #--ignore TYPECAST_INT_CONSTANT # allow split strings --ignore SPLIT_STRING # we like symbolic permissions --ignore SYMBOLIC_PERMS # we are ok with function prototypes without identifier names --ignore FUNCTION_ARGUMENTS --ignore CONST_STRUCT nfs-ganesha-2.6.0/src/scripts/checkpatch.pl000077500000000000000000005667761324272410200206540ustar00rootroot00000000000000#!/usr/bin/env perl # (c) 2001, Dave Jones. (the file handling bit) # (c) 2005, Joel Schopp (the ugly bit) # (c) 2007,2008, Andy Whitcroft (new conditions, test suite) # (c) 2008-2010 Andy Whitcroft # Licensed under the terms of the GNU GPL License version 2 use strict; use warnings; use POSIX; use File::Basename; use Cwd 'abs_path'; use Term::ANSIColor qw(:constants); my $P = $0; my $D = dirname(abs_path($P)); my $V = '0.32'; use Getopt::Long qw(:config no_auto_abbrev); my $quiet = 0; my $tree = 1; my $chk_signoff = 1; my $chk_patch = 1; my $tst_only; my $emacs = 0; my $terse = 0; my $showfile = 0; my $file = 0; my $git = 0; my %git_commits = (); my $check = 0; my $check_orig = 0; my $summary = 1; my $mailback = 0; my $summary_file = 0; my $show_types = 0; my $list_types = 0; my $fix = 0; my $fix_inplace = 0; my $root; my %debug; my %camelcase = (); my %use_type = (); my @use = (); my %ignore_type = (); my @ignore = (); my $help = 0; my $configuration_file = ".checkpatch.conf"; my $max_line_length = 80; my $ignore_perl_version = 0; my $minimum_perl_version = 5.10.0; my $min_conf_desc_length = 4; my $spelling_file = "$D/spelling.txt"; my $codespell = 0; my $codespellfile = "/usr/share/codespell/dictionary.txt"; my $conststructsfile = "$D/const_structs.checkpatch"; my $typedefsfile = ""; my $color = "auto"; my $allow_c99_comments = 1; sub help { my ($exitcode) = @_; print << "EOM"; Usage: $P [OPTION]... [FILE]... Version: $V Options: -q, --quiet quiet --no-tree run without a kernel tree --no-signoff do not check for 'Signed-off-by' line --patch treat FILE as patchfile (default) --emacs emacs compile window format --terse one line per report --showfile emit diffed file position, not input file position -g, --git treat FILE as a single commit or git revision range single git commit with: ^ ~n multiple git commits with: .. ... - git merges are ignored -f, --file treat FILE as regular source file --subjective, --strict enable more subjective tests --list-types list the possible message types --types TYPE(,TYPE2...) show only these comma separated message types --ignore TYPE(,TYPE2...) ignore various comma separated message types --show-types show the specific message type in the output --max-line-length=n set the maximum line length, if exceeded, warn --min-conf-desc-length=n set the min description length, if shorter, warn --root=PATH PATH to the kernel tree root --no-summary suppress the per-file summary --mailback only produce a report in case of warnings/errors --summary-file include the filename in summary --debug KEY=[0|1] turn on/off debugging of KEY, where KEY is one of 'values', 'possible', 'type', and 'attr' (default is all off) --test-only=WORD report only warnings/errors containing WORD literally --fix EXPERIMENTAL - may create horrible results If correctable single-line errors exist, create ".EXPERIMENTAL-checkpatch-fixes" with potential errors corrected to the preferred checkpatch style --fix-inplace EXPERIMENTAL - may create horrible results Is the same as --fix, but overwrites the input file. It's your fault if there's no backup or git --ignore-perl-version override checking of perl version. expect runtime errors. --codespell Use the codespell dictionary for spelling/typos (default:/usr/share/codespell/dictionary.txt) --codespellfile Use this codespell dictionary --typedefsfile Read additional types from this file --color[=WHEN] Use colors 'always', 'never', or only when output is a terminal ('auto'). Default is 'auto'. -h, --help, --version display this help and exit When FILE is - read standard input. EOM exit($exitcode); } sub uniq { my %seen; return grep { !$seen{$_}++ } @_; } sub list_types { my ($exitcode) = @_; my $count = 0; local $/ = undef; open(my $script, '<', abs_path($P)) or die "$P: Can't read '$P' $!\n"; my $text = <$script>; close($script); my @types = (); # Also catch when type or level is passed through a variable for ($text =~ /(?:(?:\bCHK|\bWARN|\bERROR|&\{\$msg_level})\s*\(|\$msg_type\s*=)\s*"([^"]+)"/g) { push (@types, $_); } @types = sort(uniq(@types)); print("#\tMessage type\n\n"); foreach my $type (@types) { print(++$count . "\t" . $type . "\n"); } exit($exitcode); } my $conf = which_conf($configuration_file); if (-f $conf) { my @conf_args; open(my $conffile, '<', "$conf") or warn "$P: Can't find a readable $configuration_file file $!\n"; while (<$conffile>) { my $line = $_; $line =~ s/\s*\n?$//g; $line =~ s/^\s*//g; $line =~ s/\s+/ /g; next if ($line =~ m/^\s*#/); next if ($line =~ m/^\s*$/); my @words = split(" ", $line); foreach my $word (@words) { last if ($word =~ m/^#/); push (@conf_args, $word); } } close($conffile); unshift(@ARGV, @conf_args) if @conf_args; } # Perl's Getopt::Long allows options to take optional arguments after a space. # Prevent --color by itself from consuming other arguments foreach (@ARGV) { if ($_ eq "--color" || $_ eq "-color") { $_ = "--color=$color"; } } GetOptions( 'q|quiet+' => \$quiet, 'tree!' => \$tree, 'signoff!' => \$chk_signoff, 'patch!' => \$chk_patch, 'emacs!' => \$emacs, 'terse!' => \$terse, 'showfile!' => \$showfile, 'f|file!' => \$file, 'g|git!' => \$git, 'subjective!' => \$check, 'strict!' => \$check, 'ignore=s' => \@ignore, 'types=s' => \@use, 'show-types!' => \$show_types, 'list-types!' => \$list_types, 'max-line-length=i' => \$max_line_length, 'min-conf-desc-length=i' => \$min_conf_desc_length, 'root=s' => \$root, 'summary!' => \$summary, 'mailback!' => \$mailback, 'summary-file!' => \$summary_file, 'fix!' => \$fix, 'fix-inplace!' => \$fix_inplace, 'ignore-perl-version!' => \$ignore_perl_version, 'debug=s' => \%debug, 'test-only=s' => \$tst_only, 'codespell!' => \$codespell, 'codespellfile=s' => \$codespellfile, 'typedefsfile=s' => \$typedefsfile, 'color=s' => \$color, 'no-color' => \$color, #keep old behaviors of -nocolor 'nocolor' => \$color, #keep old behaviors of -nocolor 'h|help' => \$help, 'version' => \$help ) or help(1); help(0) if ($help); list_types(0) if ($list_types); $fix = 1 if ($fix_inplace); $check_orig = $check; my $exit = 0; if ($^V && $^V lt $minimum_perl_version) { printf "$P: requires at least perl version %vd\n", $minimum_perl_version; if (!$ignore_perl_version) { exit(1); } } #if no filenames are given, push '-' to read patch from stdin if ($#ARGV < 0) { push(@ARGV, '-'); } if ($color =~ /^[01]$/) { $color = !$color; } elsif ($color =~ /^always$/i) { $color = 1; } elsif ($color =~ /^never$/i) { $color = 0; } elsif ($color =~ /^auto$/i) { $color = (-t STDOUT); } else { die "Invalid color mode: $color\n"; } sub hash_save_array_words { my ($hashRef, $arrayRef) = @_; my @array = split(/,/, join(',', @$arrayRef)); foreach my $word (@array) { $word =~ s/\s*\n?$//g; $word =~ s/^\s*//g; $word =~ s/\s+/ /g; $word =~ tr/[a-z]/[A-Z]/; next if ($word =~ m/^\s*#/); next if ($word =~ m/^\s*$/); $hashRef->{$word}++; } } sub hash_show_words { my ($hashRef, $prefix) = @_; if (keys %$hashRef) { print "\nNOTE: $prefix message types:"; foreach my $word (sort keys %$hashRef) { print " $word"; } print "\n"; } } hash_save_array_words(\%ignore_type, \@ignore); hash_save_array_words(\%use_type, \@use); my $dbg_values = 0; my $dbg_possible = 0; my $dbg_type = 0; my $dbg_attr = 0; for my $key (keys %debug) { ## no critic eval "\${dbg_$key} = '$debug{$key}';"; die "$@" if ($@); } my $rpt_cleaners = 0; if ($terse) { $emacs = 1; $quiet++; } if ($tree) { if (defined $root) { if (!top_of_kernel_tree($root)) { die "$P: $root: --root does not point at a valid tree\n"; } } else { if (top_of_kernel_tree('.')) { $root = '.'; } elsif ($0 =~ m@(.*)/scripts/[^/]*$@ && top_of_kernel_tree($1)) { $root = $1; } } if (!defined $root) { print "Must be run from the top-level dir. of a kernel tree\n"; exit(2); } } my $emitted_corrupt = 0; our $Ident = qr{ [A-Za-z_][A-Za-z\d_]* (?:\s*\#\#\s*[A-Za-z_][A-Za-z\d_]*)* }x; our $Storage = qr{extern|static|asmlinkage}; our $Sparse = qr{ __user| __kernel| __force| __iomem| __must_check| __init_refok| __kprobes| __ref| __rcu| __private }x; our $InitAttributePrefix = qr{__(?:mem|cpu|dev|net_|)}; our $InitAttributeData = qr{$InitAttributePrefix(?:initdata\b)}; our $InitAttributeConst = qr{$InitAttributePrefix(?:initconst\b)}; our $InitAttributeInit = qr{$InitAttributePrefix(?:init\b)}; our $InitAttribute = qr{$InitAttributeData|$InitAttributeConst|$InitAttributeInit}; # Notes to $Attribute: # We need \b after 'init' otherwise 'initconst' will cause a false positive in a check our $Attribute = qr{ const| __percpu| __nocast| __safe| __bitwise| __packed__| __packed2__| __naked| __maybe_unused| __always_unused| __noreturn| __used| __cold| __pure| __noclone| __deprecated| __read_mostly| __kprobes| $InitAttribute| ____cacheline_aligned| ____cacheline_aligned_in_smp| ____cacheline_internodealigned_in_smp| __weak }x; our $Modifier; our $Inline = qr{inline|__always_inline|noinline|__inline|__inline__}; our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]}; our $Lval = qr{$Ident(?:$Member)*}; our $Int_type = qr{(?i)llu|ull|ll|lu|ul|l|u}; our $Binary = qr{(?i)0b[01]+$Int_type?}; our $Hex = qr{(?i)0x[0-9a-f]+$Int_type?}; our $Int = qr{[0-9]+$Int_type?}; our $Octal = qr{0[0-7]+$Int_type?}; our $String = qr{"[X\t]*"}; our $Float_hex = qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?}; our $Float_dec = qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?}; our $Float_int = qr{(?i)[0-9]+e-?[0-9]+[fl]?}; our $Float = qr{$Float_hex|$Float_dec|$Float_int}; our $Constant = qr{$Float|$Binary|$Octal|$Hex|$Int}; our $Assignment = qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=}; our $Compare = qr{<=|>=|==|!=|<|(?}; our $Arithmetic = qr{\+|-|\*|\/|%}; our $Operators = qr{ <=|>=|==|!=| =>|->|<<|>>|<|>|!|~| &&|\|\||,|\^|\+\+|--|&|\||$Arithmetic }x; our $c90_Keywords = qr{do|for|while|if|else|return|goto|continue|switch|default|case|break}x; our $BasicType; our $NonptrType; our $NonptrTypeMisordered; our $NonptrTypeWithAttr; our $Type; our $TypeMisordered; our $Declare; our $DeclareMisordered; our $NON_ASCII_UTF8 = qr{ [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 }x; our $UTF8 = qr{ [\x09\x0A\x0D\x20-\x7E] # ASCII | $NON_ASCII_UTF8 }x; our $typeC99Typedefs = qr{(?:__)?(?:[us]_?)?int_?(?:8|16|32|64)_t}; our $typeOtherOSTypedefs = qr{(?x: u_(?:char|short|int|long) | # bsd u(?:nchar|short|int|long) # sysv )}; our $typeKernelTypedefs = qr{(?x: (?:__)?(?:u|s|be|le)(?:8|16|32|64)| atomic_t )}; our $typeTypedefs = qr{(?x: $typeC99Typedefs\b| $typeOtherOSTypedefs\b| $typeKernelTypedefs\b )}; our $zero_initializer = qr{(?:(?:0[xX])?0+$Int_type?|NULL|false)\b}; our $logFunctions = qr{(?x: printk(?:_ratelimited|_once|_deferred_once|_deferred|)| (?:[a-z0-9]+_){1,2}(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)| TP_printk| WARN(?:_RATELIMIT|_ONCE|)| panic| MODULE_[A-Z_]+| seq_vprintf|seq_printf|seq_puts )}; our $signature_tags = qr{(?xi: Signed-off-by:| Acked-by:| Tested-by:| Reviewed-by:| Reported-by:| Suggested-by:| To:| Cc: )}; our @typeListMisordered = ( qr{char\s+(?:un)?signed}, qr{int\s+(?:(?:un)?signed\s+)?short\s}, qr{int\s+short(?:\s+(?:un)?signed)}, qr{short\s+int(?:\s+(?:un)?signed)}, qr{(?:un)?signed\s+int\s+short}, qr{short\s+(?:un)?signed}, qr{long\s+int\s+(?:un)?signed}, qr{int\s+long\s+(?:un)?signed}, qr{long\s+(?:un)?signed\s+int}, qr{int\s+(?:un)?signed\s+long}, qr{int\s+(?:un)?signed}, qr{int\s+long\s+long\s+(?:un)?signed}, qr{long\s+long\s+int\s+(?:un)?signed}, qr{long\s+long\s+(?:un)?signed\s+int}, qr{long\s+long\s+(?:un)?signed}, qr{long\s+(?:un)?signed}, ); our @typeList = ( qr{void}, qr{(?:(?:un)?signed\s+)?char}, qr{(?:(?:un)?signed\s+)?short\s+int}, qr{(?:(?:un)?signed\s+)?short}, qr{(?:(?:un)?signed\s+)?int}, qr{(?:(?:un)?signed\s+)?long\s+int}, qr{(?:(?:un)?signed\s+)?long\s+long\s+int}, qr{(?:(?:un)?signed\s+)?long\s+long}, qr{(?:(?:un)?signed\s+)?long}, qr{(?:un)?signed}, qr{float}, qr{double}, qr{bool}, qr{struct\s+$Ident}, qr{union\s+$Ident}, qr{enum\s+$Ident}, qr{${Ident}_t}, qr{${Ident}_handler}, qr{${Ident}_handler_fn}, @typeListMisordered, ); our $C90_int_types = qr{(?x: long\s+long\s+int\s+(?:un)?signed| long\s+long\s+(?:un)?signed\s+int| long\s+long\s+(?:un)?signed| (?:(?:un)?signed\s+)?long\s+long\s+int| (?:(?:un)?signed\s+)?long\s+long| int\s+long\s+long\s+(?:un)?signed| int\s+(?:(?:un)?signed\s+)?long\s+long| long\s+int\s+(?:un)?signed| long\s+(?:un)?signed\s+int| long\s+(?:un)?signed| (?:(?:un)?signed\s+)?long\s+int| (?:(?:un)?signed\s+)?long| int\s+long\s+(?:un)?signed| int\s+(?:(?:un)?signed\s+)?long| int\s+(?:un)?signed| (?:(?:un)?signed\s+)?int )}; our @typeListFile = (); our @typeListWithAttr = ( @typeList, qr{struct\s+$InitAttribute\s+$Ident}, qr{union\s+$InitAttribute\s+$Ident}, ); our @modifierList = ( qr{fastcall}, ); our @modifierListFile = (); our @mode_permission_funcs = ( ["module_param", 3], ["module_param_(?:array|named|string)", 4], ["module_param_array_named", 5], ["debugfs_create_(?:file|u8|u16|u32|u64|x8|x16|x32|x64|size_t|atomic_t|bool|blob|regset32|u32_array)", 2], ["proc_create(?:_data|)", 2], ["(?:CLASS|DEVICE|SENSOR|SENSOR_DEVICE|IIO_DEVICE)_ATTR", 2], ["IIO_DEV_ATTR_[A-Z_]+", 1], ["SENSOR_(?:DEVICE_|)ATTR_2", 2], ["SENSOR_TEMPLATE(?:_2|)", 3], ["__ATTR", 2], ); #Create a search pattern for all these functions to speed up a loop below our $mode_perms_search = ""; foreach my $entry (@mode_permission_funcs) { $mode_perms_search .= '|' if ($mode_perms_search ne ""); $mode_perms_search .= $entry->[0]; } our $mode_perms_world_writable = qr{ S_IWUGO | S_IWOTH | S_IRWXUGO | S_IALLUGO | 0[0-7][0-7][2367] }x; our %mode_permission_string_types = ( "S_IRWXU" => 0700, "S_IRUSR" => 0400, "S_IWUSR" => 0200, "S_IXUSR" => 0100, "S_IRWXG" => 0070, "S_IRGRP" => 0040, "S_IWGRP" => 0020, "S_IXGRP" => 0010, "S_IRWXO" => 0007, "S_IROTH" => 0004, "S_IWOTH" => 0002, "S_IXOTH" => 0001, "S_IRWXUGO" => 0777, "S_IRUGO" => 0444, "S_IWUGO" => 0222, "S_IXUGO" => 0111, ); #Create a search pattern for all these strings to speed up a loop below our $mode_perms_string_search = ""; foreach my $entry (keys %mode_permission_string_types) { $mode_perms_string_search .= '|' if ($mode_perms_string_search ne ""); $mode_perms_string_search .= $entry; } our $allowed_asm_includes = qr{(?x: irq| memory| time| reboot )}; # memory.h: ARM has a custom one # Load common spelling mistakes and build regular expression list. my $misspellings; my %spelling_fix; if (open(my $spelling, '<', $spelling_file)) { while (<$spelling>) { my $line = $_; $line =~ s/\s*\n?$//g; $line =~ s/^\s*//g; next if ($line =~ m/^\s*#/); next if ($line =~ m/^\s*$/); my ($suspect, $fix) = split(/\|\|/, $line); $spelling_fix{$suspect} = $fix; } close($spelling); } else { warn "No typos will be found - file '$spelling_file': $!\n"; } if ($codespell) { if (open(my $spelling, '<', $codespellfile)) { while (<$spelling>) { my $line = $_; $line =~ s/\s*\n?$//g; $line =~ s/^\s*//g; next if ($line =~ m/^\s*#/); next if ($line =~ m/^\s*$/); next if ($line =~ m/, disabled/i); $line =~ s/,.*$//; my ($suspect, $fix) = split(/->/, $line); $spelling_fix{$suspect} = $fix; } close($spelling); } else { warn "No codespell typos will be found - file '$codespellfile': $!\n"; } } $misspellings = join("|", sort keys %spelling_fix) if keys %spelling_fix; sub read_words { my ($wordsRef, $file) = @_; if (open(my $words, '<', $file)) { while (<$words>) { my $line = $_; $line =~ s/\s*\n?$//g; $line =~ s/^\s*//g; next if ($line =~ m/^\s*#/); next if ($line =~ m/^\s*$/); if ($line =~ /\s/) { print("$file: '$line' invalid - ignored\n"); next; } $$wordsRef .= '|' if ($$wordsRef ne ""); $$wordsRef .= $line; } close($file); return 1; } return 0; } my $const_structs = ""; read_words(\$const_structs, $conststructsfile) or warn "No structs that should be const will be found - file '$conststructsfile': $!\n"; my $typeOtherTypedefs = ""; if (length($typedefsfile)) { read_words(\$typeOtherTypedefs, $typedefsfile) or warn "No additional types will be considered - file '$typedefsfile': $!\n"; } $typeTypedefs .= '|' . $typeOtherTypedefs if ($typeOtherTypedefs ne ""); sub build_types { my $mods = "(?x: \n" . join("|\n ", (@modifierList, @modifierListFile)) . "\n)"; my $all = "(?x: \n" . join("|\n ", (@typeList, @typeListFile)) . "\n)"; my $Misordered = "(?x: \n" . join("|\n ", @typeListMisordered) . "\n)"; my $allWithAttr = "(?x: \n" . join("|\n ", @typeListWithAttr) . "\n)"; $Modifier = qr{(?:$Attribute|$Sparse|$mods)}; $BasicType = qr{ (?:$typeTypedefs\b)| (?:${all}\b) }x; $NonptrType = qr{ (?:$Modifier\s+|const\s+)* (?: (?:typeof|__typeof__)\s*\([^\)]*\)| (?:$typeTypedefs\b)| (?:${all}\b) ) (?:\s+$Modifier|\s+const)* }x; $NonptrTypeMisordered = qr{ (?:$Modifier\s+|const\s+)* (?: (?:${Misordered}\b) ) (?:\s+$Modifier|\s+const)* }x; $NonptrTypeWithAttr = qr{ (?:$Modifier\s+|const\s+)* (?: (?:typeof|__typeof__)\s*\([^\)]*\)| (?:$typeTypedefs\b)| (?:${allWithAttr}\b) ) (?:\s+$Modifier|\s+const)* }x; $Type = qr{ $NonptrType (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)? (?:\s+$Inline|\s+$Modifier)* }x; $TypeMisordered = qr{ $NonptrTypeMisordered (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)? (?:\s+$Inline|\s+$Modifier)* }x; $Declare = qr{(?:$Storage\s+(?:$Inline\s+)?)?$Type}; $DeclareMisordered = qr{(?:$Storage\s+(?:$Inline\s+)?)?$TypeMisordered}; } build_types(); our $Typecast = qr{\s*(\(\s*$NonptrType\s*\)){0,1}\s*}; # Using $balanced_parens, $LvalOrFunc, or $FuncArg # requires at least perl version v5.10.0 # Any use must be runtime checked with $^V our $balanced_parens = qr/(\((?:[^\(\)]++|(?-1))*\))/; our $LvalOrFunc = qr{((?:[\&\*]\s*)?$Lval)\s*($balanced_parens{0,1})\s*}; our $FuncArg = qr{$Typecast{0,1}($LvalOrFunc|$Constant|$String)}; our $declaration_macros = qr{(?x: (?:$Storage\s+)?(?:[A-Z_][A-Z0-9]*_){0,2}(?:DEFINE|DECLARE)(?:_[A-Z0-9]+){1,6}\s*\(| (?:$Storage\s+)?[HLP]?LIST_HEAD\s*\(| (?:$Storage\s+)?${Type}\s+uninitialized_var\s*\( )}; sub deparenthesize { my ($string) = @_; return "" if (!defined($string)); while ($string =~ /^\s*\(.*\)\s*$/) { $string =~ s@^\s*\(\s*@@; $string =~ s@\s*\)\s*$@@; } $string =~ s@\s+@ @g; return $string; } sub seed_camelcase_file { my ($file) = @_; return if (!(-f $file)); local $/; open(my $include_file, '<', "$file") or warn "$P: Can't read '$file' $!\n"; my $text = <$include_file>; close($include_file); my @lines = split('\n', $text); foreach my $line (@lines) { next if ($line !~ /(?:[A-Z][a-z]|[a-z][A-Z])/); if ($line =~ /^[ \t]*(?:#[ \t]*define|typedef\s+$Type)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)/) { $camelcase{$1} = 1; } elsif ($line =~ /^\s*$Declare\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[\(\[,;]/) { $camelcase{$1} = 1; } elsif ($line =~ /^\s*(?:union|struct|enum)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[;\{]/) { $camelcase{$1} = 1; } } } sub is_maintained_obsolete { my ($filename) = @_; return 0 if (!$tree || !(-e "$root/scripts/get_maintainer.pl")); my $status = `perl $root/scripts/get_maintainer.pl --status --nom --nol --nogit --nogit-fallback -f $filename 2>&1`; return $status =~ /obsolete/i; } my $camelcase_seeded = 0; sub seed_camelcase_includes { return if ($camelcase_seeded); my $files; my $camelcase_cache = ""; my @include_files = (); $camelcase_seeded = 1; if (-e ".git") { my $git_last_include_commit = `git log --no-merges --pretty=format:"%h%n" -1 -- include`; chomp $git_last_include_commit; $camelcase_cache = ".checkpatch-camelcase.git.$git_last_include_commit"; } else { my $last_mod_date = 0; $files = `find $root/include -name "*.h"`; @include_files = split('\n', $files); foreach my $file (@include_files) { my $date = POSIX::strftime("%Y%m%d%H%M", localtime((stat $file)[9])); $last_mod_date = $date if ($last_mod_date < $date); } $camelcase_cache = ".checkpatch-camelcase.date.$last_mod_date"; } if ($camelcase_cache ne "" && -f $camelcase_cache) { open(my $camelcase_file, '<', "$camelcase_cache") or warn "$P: Can't read '$camelcase_cache' $!\n"; while (<$camelcase_file>) { chomp; $camelcase{$_} = 1; } close($camelcase_file); return; } if (-e ".git") { $files = `git ls-files "include/*.h"`; @include_files = split('\n', $files); } foreach my $file (@include_files) { seed_camelcase_file($file); } if ($camelcase_cache ne "") { unlink glob ".checkpatch-camelcase.*"; open(my $camelcase_file, '>', "$camelcase_cache") or warn "$P: Can't write '$camelcase_cache' $!\n"; foreach (sort { lc($a) cmp lc($b) } keys(%camelcase)) { print $camelcase_file ("$_\n"); } close($camelcase_file); } } sub git_commit_info { my ($commit, $id, $desc) = @_; return ($id, $desc) if ((which("git") eq "") || !(-e ".git")); my $output = `git log --no-color --format='%H %s' -1 $commit 2>&1`; $output =~ s/^\s*//gm; my @lines = split("\n", $output); return ($id, $desc) if ($#lines < 0); if ($lines[0] =~ /^error: short SHA1 $commit is ambiguous\./) { # Maybe one day convert this block of bash into something that returns # all matching commit ids, but it's very slow... # # echo "checking commits $1..." # git rev-list --remotes | grep -i "^$1" | # while read line ; do # git log --format='%H %s' -1 $line | # echo "commit $(cut -c 1-12,41-)" # done } elsif ($lines[0] =~ /^fatal: ambiguous argument '$commit': unknown revision or path not in the working tree\./) { $id = undef; } else { $id = substr($lines[0], 0, 12); $desc = substr($lines[0], 41); } return ($id, $desc); } $chk_signoff = 0 if ($file); my @rawlines = (); my @lines = (); my @fixed = (); my @fixed_inserted = (); my @fixed_deleted = (); my $fixlinenr = -1; # If input is git commits, extract all commits from the commit expressions. # For example, HEAD-3 means we need check 'HEAD, HEAD~1, HEAD~2'. die "$P: No git repository found\n" if ($git && !-e ".git"); if ($git) { my @commits = (); foreach my $commit_expr (@ARGV) { my $git_range; if ($commit_expr =~ m/^(.*)-(\d+)$/) { $git_range = "-$2 $1"; } elsif ($commit_expr =~ m/\.\./) { $git_range = "$commit_expr"; } else { $git_range = "-1 $commit_expr"; } my $lines = `git log --no-color --no-merges --pretty=format:'%H %s' $git_range`; foreach my $line (split(/\n/, $lines)) { $line =~ /^([0-9a-fA-F]{40,40}) (.*)$/; next if (!defined($1) || !defined($2)); my $sha1 = $1; my $subject = $2; unshift(@commits, $sha1); $git_commits{$sha1} = $subject; } } die "$P: no git commits after extraction!\n" if (@commits == 0); @ARGV = @commits; } my $vname; for my $filename (@ARGV) { my $FILE; if ($git) { open($FILE, '-|', "git format-patch -M --stdout -1 $filename") || die "$P: $filename: git format-patch failed - $!\n"; } elsif ($file) { open($FILE, '-|', "diff -u /dev/null $filename") || die "$P: $filename: diff failed - $!\n"; } elsif ($filename eq '-') { open($FILE, '<&STDIN'); } else { open($FILE, '<', "$filename") || die "$P: $filename: open failed - $!\n"; } if ($filename eq '-') { $vname = 'Your patch'; } elsif ($git) { $vname = "Commit " . substr($filename, 0, 12) . ' ("' . $git_commits{$filename} . '")'; } else { $vname = $filename; } while (<$FILE>) { chomp; push(@rawlines, $_); } close($FILE); if ($#ARGV > 0 && $quiet == 0) { print '-' x length($vname) . "\n"; print "$vname\n"; print '-' x length($vname) . "\n"; } if (!process($filename)) { $exit = 1; } @rawlines = (); @lines = (); @fixed = (); @fixed_inserted = (); @fixed_deleted = (); $fixlinenr = -1; @modifierListFile = (); @typeListFile = (); build_types(); } if (!$quiet) { hash_show_words(\%use_type, "Used"); hash_show_words(\%ignore_type, "Ignored"); if ($^V lt 5.10.0) { print << "EOM" NOTE: perl $^V is not modern enough to detect all possible issues. An upgrade to at least perl v5.10.0 is suggested. EOM } if ($exit) { print << "EOM" NOTE: If any of the errors are false positives, please report them to the maintainer, see CHECKPATCH in MAINTAINERS. EOM } } exit($exit); sub top_of_kernel_tree { my ($root) = @_; my @tree_check = ( "COPYING", "CREDITS", "Kbuild", "MAINTAINERS", "Makefile", "README", "Documentation", "arch", "include", "drivers", "fs", "init", "ipc", "kernel", "lib", "scripts", ); foreach my $check (@tree_check) { if (! -e $root . '/' . $check) { return 0; } } return 1; } sub parse_email { my ($formatted_email) = @_; my $name = ""; my $address = ""; my $comment = ""; if ($formatted_email =~ /^(.*)<(\S+\@\S+)>(.*)$/) { $name = $1; $address = $2; $comment = $3 if defined $3; } elsif ($formatted_email =~ /^\s*<(\S+\@\S+)>(.*)$/) { $address = $1; $comment = $2 if defined $2; } elsif ($formatted_email =~ /(\S+\@\S+)(.*)$/) { $address = $1; $comment = $2 if defined $2; $formatted_email =~ s/$address.*$//; $name = $formatted_email; $name = trim($name); $name =~ s/^\"|\"$//g; # If there's a name left after stripping spaces and # leading quotes, and the address doesn't have both # leading and trailing angle brackets, the address # is invalid. ie: # "joe smith joe@smith.com" bad # "joe smith ]+>$/) { $name = ""; $address = ""; $comment = ""; } } $name = trim($name); $name =~ s/^\"|\"$//g; $address = trim($address); $address =~ s/^\<|\>$//g; if ($name =~ /[^\w \-]/i) { ##has "must quote" chars $name =~ s/(?"; } return $formatted_email; } sub which { my ($bin) = @_; foreach my $path (split(/:/, $ENV{PATH})) { if (-e "$path/$bin") { return "$path/$bin"; } } return ""; } sub which_conf { my ($conf) = @_; foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) { if (-e "$path/$conf") { return "$path/$conf"; } } return ""; } sub expand_tabs { my ($str) = @_; my $res = ''; my $n = 0; for my $c (split(//, $str)) { if ($c eq "\t") { $res .= ' '; $n++; for (; ($n % 8) != 0; $n++) { $res .= ' '; } next; } $res .= $c; $n++; } return $res; } sub copy_spacing { (my $res = shift) =~ tr/\t/ /c; return $res; } sub line_stats { my ($line) = @_; # Drop the diff line leader and expand tabs $line =~ s/^.//; $line = expand_tabs($line); # Pick the indent from the front of the line. my ($white) = ($line =~ /^(\s*)/); return (length($line), length($white)); } my $sanitise_quote = ''; sub sanitise_line_reset { my ($in_comment) = @_; if ($in_comment) { $sanitise_quote = '*/'; } else { $sanitise_quote = ''; } } sub sanitise_line { my ($line) = @_; my $res = ''; my $l = ''; my $qlen = 0; my $off = 0; my $c; # Always copy over the diff marker. $res = substr($line, 0, 1); for ($off = 1; $off < length($line); $off++) { $c = substr($line, $off, 1); # Comments we are wacking completly including the begin # and end, all to $;. if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') { $sanitise_quote = '*/'; substr($res, $off, 2, "$;$;"); $off++; next; } if ($sanitise_quote eq '*/' && substr($line, $off, 2) eq '*/') { $sanitise_quote = ''; substr($res, $off, 2, "$;$;"); $off++; next; } if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') { $sanitise_quote = '//'; substr($res, $off, 2, $sanitise_quote); $off++; next; } # A \ in a string means ignore the next character. if (($sanitise_quote eq "'" || $sanitise_quote eq '"') && $c eq "\\") { substr($res, $off, 2, 'XX'); $off++; next; } # Regular quotes. if ($c eq "'" || $c eq '"') { if ($sanitise_quote eq '') { $sanitise_quote = $c; substr($res, $off, 1, $c); next; } elsif ($sanitise_quote eq $c) { $sanitise_quote = ''; } } #print "c<$c> SQ<$sanitise_quote>\n"; if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") { substr($res, $off, 1, $;); } elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") { substr($res, $off, 1, $;); } elsif ($off != 0 && $sanitise_quote && $c ne "\t") { substr($res, $off, 1, 'X'); } else { substr($res, $off, 1, $c); } } if ($sanitise_quote eq '//') { $sanitise_quote = ''; } # The pathname on a #include may be surrounded by '<' and '>'. if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) { my $clean = 'X' x length($1); $res =~ s@\<.*\>@<$clean>@; # The whole of a #error is a string. } elsif ($res =~ /^.\s*\#\s*(?:error|warning)\s+(.*)\b/) { my $clean = 'X' x length($1); $res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@; } if ($allow_c99_comments && $res =~ m@(//.*$)@) { my $match = $1; $res =~ s/\Q$match\E/"$;" x length($match)/e; } return $res; } sub get_quoted_string { my ($line, $rawline) = @_; return "" if ($line !~ m/($String)/g); return substr($rawline, $-[0], $+[0] - $-[0]); } sub ctx_statement_block { my ($linenr, $remain, $off) = @_; my $line = $linenr - 1; my $blk = ''; my $soff = $off; my $coff = $off - 1; my $coff_set = 0; my $loff = 0; my $type = ''; my $level = 0; my @stack = (); my $p; my $c; my $len = 0; my $remainder; while (1) { @stack = (['', 0]) if ($#stack == -1); #warn "CSB: blk<$blk> remain<$remain>\n"; # If we are about to drop off the end, pull in more # context. if ($off >= $len) { for (; $remain > 0; $line++) { last if (!defined $lines[$line]); next if ($lines[$line] =~ /^-/); $remain--; $loff = $len; $blk .= $lines[$line] . "\n"; $len = length($blk); $line++; last; } # Bail if there is no further context. #warn "CSB: blk<$blk> off<$off> len<$len>\n"; if ($off >= $len) { last; } if ($level == 0 && substr($blk, $off) =~ /^.\s*#\s*define/) { $level++; $type = '#'; } } $p = $c; $c = substr($blk, $off, 1); $remainder = substr($blk, $off); #warn "CSB: c<$c> type<$type> level<$level> remainder<$remainder> coff_set<$coff_set>\n"; # Handle nested #if/#else. if ($remainder =~ /^#\s*(?:ifndef|ifdef|if)\s/) { push(@stack, [ $type, $level ]); } elsif ($remainder =~ /^#\s*(?:else|elif)\b/) { ($type, $level) = @{$stack[$#stack - 1]}; } elsif ($remainder =~ /^#\s*endif\b/) { ($type, $level) = @{pop(@stack)}; } # Statement ends at the ';' or a close '}' at the # outermost level. if ($level == 0 && $c eq ';') { last; } # An else is really a conditional as long as its not else if if ($level == 0 && $coff_set == 0 && (!defined($p) || $p =~ /(?:\s|\}|\+)/) && $remainder =~ /^(else)(?:\s|{)/ && $remainder !~ /^else\s+if\b/) { $coff = $off + length($1) - 1; $coff_set = 1; #warn "CSB: mark coff<$coff> soff<$soff> 1<$1>\n"; #warn "[" . substr($blk, $soff, $coff - $soff + 1) . "]\n"; } if (($type eq '' || $type eq '(') && $c eq '(') { $level++; $type = '('; } if ($type eq '(' && $c eq ')') { $level--; $type = ($level != 0)? '(' : ''; if ($level == 0 && $coff < $soff) { $coff = $off; $coff_set = 1; #warn "CSB: mark coff<$coff>\n"; } } if (($type eq '' || $type eq '{') && $c eq '{') { $level++; $type = '{'; } if ($type eq '{' && $c eq '}') { $level--; $type = ($level != 0)? '{' : ''; if ($level == 0) { if (substr($blk, $off + 1, 1) eq ';') { $off++; } last; } } # Preprocessor commands end at the newline unless escaped. if ($type eq '#' && $c eq "\n" && $p ne "\\") { $level--; $type = ''; $off++; last; } $off++; } # We are truly at the end, so shuffle to the next line. if ($off == $len) { $loff = $len + 1; $line++; $remain--; } my $statement = substr($blk, $soff, $off - $soff + 1); my $condition = substr($blk, $soff, $coff - $soff + 1); #warn "STATEMENT<$statement>\n"; #warn "CONDITION<$condition>\n"; #print "coff<$coff> soff<$off> loff<$loff>\n"; return ($statement, $condition, $line, $remain + 1, $off - $loff + 1, $level); } sub statement_lines { my ($stmt) = @_; # Strip the diff line prefixes and rip blank lines at start and end. $stmt =~ s/(^|\n)./$1/g; $stmt =~ s/^\s*//; $stmt =~ s/\s*$//; my @stmt_lines = ($stmt =~ /\n/g); return $#stmt_lines + 2; } sub statement_rawlines { my ($stmt) = @_; my @stmt_lines = ($stmt =~ /\n/g); return $#stmt_lines + 2; } sub statement_block_size { my ($stmt) = @_; $stmt =~ s/(^|\n)./$1/g; $stmt =~ s/^\s*{//; $stmt =~ s/}\s*$//; $stmt =~ s/^\s*//; $stmt =~ s/\s*$//; my @stmt_lines = ($stmt =~ /\n/g); my @stmt_statements = ($stmt =~ /;/g); my $stmt_lines = $#stmt_lines + 2; my $stmt_statements = $#stmt_statements + 1; if ($stmt_lines > $stmt_statements) { return $stmt_lines; } else { return $stmt_statements; } } sub ctx_statement_full { my ($linenr, $remain, $off) = @_; my ($statement, $condition, $level); my (@chunks); # Grab the first conditional/block pair. ($statement, $condition, $linenr, $remain, $off, $level) = ctx_statement_block($linenr, $remain, $off); #print "F: c<$condition> s<$statement> remain<$remain>\n"; push(@chunks, [ $condition, $statement ]); if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:if|else|do)\b/s)) { return ($level, $linenr, @chunks); } # Pull in the following conditional/block pairs and see if they # could continue the statement. for (;;) { ($statement, $condition, $linenr, $remain, $off, $level) = ctx_statement_block($linenr, $remain, $off); #print "C: c<$condition> s<$statement> remain<$remain>\n"; last if (!($remain > 0 && $condition =~ /^(?:\s*\n[+-])*\s*(?:else|do)\b/s)); #print "C: push\n"; push(@chunks, [ $condition, $statement ]); } return ($level, $linenr, @chunks); } sub ctx_block_get { my ($linenr, $remain, $outer, $open, $close, $off) = @_; my $line; my $start = $linenr - 1; my $blk = ''; my @o; my @c; my @res = (); my $level = 0; my @stack = ($level); for ($line = $start; $remain > 0; $line++) { next if ($rawlines[$line] =~ /^-/); $remain--; $blk .= $rawlines[$line]; # Handle nested #if/#else. if ($lines[$line] =~ /^.\s*#\s*(?:ifndef|ifdef|if)\s/) { push(@stack, $level); } elsif ($lines[$line] =~ /^.\s*#\s*(?:else|elif)\b/) { $level = $stack[$#stack - 1]; } elsif ($lines[$line] =~ /^.\s*#\s*endif\b/) { $level = pop(@stack); } foreach my $c (split(//, $lines[$line])) { ##print "C<$c>L<$level><$open$close>O<$off>\n"; if ($off > 0) { $off--; next; } if ($c eq $close && $level > 0) { $level--; last if ($level == 0); } elsif ($c eq $open) { $level++; } } if (!$outer || $level <= 1) { push(@res, $rawlines[$line]); } last if ($level == 0); } return ($level, @res); } sub ctx_block_outer { my ($linenr, $remain) = @_; my ($level, @r) = ctx_block_get($linenr, $remain, 1, '{', '}', 0); return @r; } sub ctx_block { my ($linenr, $remain) = @_; my ($level, @r) = ctx_block_get($linenr, $remain, 0, '{', '}', 0); return @r; } sub ctx_statement { my ($linenr, $remain, $off) = @_; my ($level, @r) = ctx_block_get($linenr, $remain, 0, '(', ')', $off); return @r; } sub ctx_block_level { my ($linenr, $remain) = @_; return ctx_block_get($linenr, $remain, 0, '{', '}', 0); } sub ctx_statement_level { my ($linenr, $remain, $off) = @_; return ctx_block_get($linenr, $remain, 0, '(', ')', $off); } sub ctx_locate_comment { my ($first_line, $end_line) = @_; # Catch a comment on the end of the line itself. my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@); return $current_comment if (defined $current_comment); # Look through the context and try and figure out if there is a # comment. my $in_comment = 0; $current_comment = ''; for (my $linenr = $first_line; $linenr < $end_line; $linenr++) { my $line = $rawlines[$linenr - 1]; #warn " $line\n"; if ($linenr == $first_line and $line =~ m@^.\s*\*@) { $in_comment = 1; } if ($line =~ m@/\*@) { $in_comment = 1; } if (!$in_comment && $current_comment ne '') { $current_comment = ''; } $current_comment .= $line . "\n" if ($in_comment); if ($line =~ m@\*/@) { $in_comment = 0; } } chomp($current_comment); return($current_comment); } sub ctx_has_comment { my ($first_line, $end_line) = @_; my $cmt = ctx_locate_comment($first_line, $end_line); ##print "LINE: $rawlines[$end_line - 1 ]\n"; ##print "CMMT: $cmt\n"; return ($cmt ne ''); } sub raw_line { my ($linenr, $cnt) = @_; my $offset = $linenr - 1; $cnt++; my $line; while ($cnt) { $line = $rawlines[$offset++]; next if (defined($line) && $line =~ /^-/); $cnt--; } return $line; } sub cat_vet { my ($vet) = @_; my ($res, $coded); $res = ''; while ($vet =~ /([^[:cntrl:]]*)([[:cntrl:]]|$)/g) { $res .= $1; if ($2 ne '') { $coded = sprintf("^%c", unpack('C', $2) + 64); $res .= $coded; } } $res =~ s/$/\$/; return $res; } my $av_preprocessor = 0; my $av_pending; my @av_paren_type; my $av_pend_colon; sub annotate_reset { $av_preprocessor = 0; $av_pending = '_'; @av_paren_type = ('E'); $av_pend_colon = 'O'; } sub annotate_values { my ($stream, $type) = @_; my $res; my $var = '_' x length($stream); my $cur = $stream; print "$stream\n" if ($dbg_values > 1); while (length($cur)) { @av_paren_type = ('E') if ($#av_paren_type < 0); print " <" . join('', @av_paren_type) . "> <$type> <$av_pending>" if ($dbg_values > 1); if ($cur =~ /^(\s+)/o) { print "WS($1)\n" if ($dbg_values > 1); if ($1 =~ /\n/ && $av_preprocessor) { $type = pop(@av_paren_type); $av_preprocessor = 0; } } elsif ($cur =~ /^(\(\s*$Type\s*)\)/ && $av_pending eq '_') { print "CAST($1)\n" if ($dbg_values > 1); push(@av_paren_type, $type); $type = 'c'; } elsif ($cur =~ /^($Type)\s*(?:$Ident|,|\)|\(|\s*$)/) { print "DECLARE($1)\n" if ($dbg_values > 1); $type = 'T'; } elsif ($cur =~ /^($Modifier)\s*/) { print "MODIFIER($1)\n" if ($dbg_values > 1); $type = 'T'; } elsif ($cur =~ /^(\#\s*define\s*$Ident)(\(?)/o) { print "DEFINE($1,$2)\n" if ($dbg_values > 1); $av_preprocessor = 1; push(@av_paren_type, $type); if ($2 ne '') { $av_pending = 'N'; } $type = 'E'; } elsif ($cur =~ /^(\#\s*(?:undef\s*$Ident|include\b))/o) { print "UNDEF($1)\n" if ($dbg_values > 1); $av_preprocessor = 1; push(@av_paren_type, $type); } elsif ($cur =~ /^(\#\s*(?:ifdef|ifndef|if))/o) { print "PRE_START($1)\n" if ($dbg_values > 1); $av_preprocessor = 1; push(@av_paren_type, $type); push(@av_paren_type, $type); $type = 'E'; } elsif ($cur =~ /^(\#\s*(?:else|elif))/o) { print "PRE_RESTART($1)\n" if ($dbg_values > 1); $av_preprocessor = 1; push(@av_paren_type, $av_paren_type[$#av_paren_type]); $type = 'E'; } elsif ($cur =~ /^(\#\s*(?:endif))/o) { print "PRE_END($1)\n" if ($dbg_values > 1); $av_preprocessor = 1; # Assume all arms of the conditional end as this # one does, and continue as if the #endif was not here. pop(@av_paren_type); push(@av_paren_type, $type); $type = 'E'; } elsif ($cur =~ /^(\\\n)/o) { print "PRECONT($1)\n" if ($dbg_values > 1); } elsif ($cur =~ /^(__attribute__)\s*\(?/o) { print "ATTR($1)\n" if ($dbg_values > 1); $av_pending = $type; $type = 'N'; } elsif ($cur =~ /^(sizeof)\s*(\()?/o) { print "SIZEOF($1)\n" if ($dbg_values > 1); if (defined $2) { $av_pending = 'V'; } $type = 'N'; } elsif ($cur =~ /^(if|while|for)\b/o) { print "COND($1)\n" if ($dbg_values > 1); $av_pending = 'E'; $type = 'N'; } elsif ($cur =~/^(case)/o) { print "CASE($1)\n" if ($dbg_values > 1); $av_pend_colon = 'C'; $type = 'N'; } elsif ($cur =~/^(return|else|goto|typeof|__typeof__)\b/o) { print "KEYWORD($1)\n" if ($dbg_values > 1); $type = 'N'; } elsif ($cur =~ /^(\()/o) { print "PAREN('$1')\n" if ($dbg_values > 1); push(@av_paren_type, $av_pending); $av_pending = '_'; $type = 'N'; } elsif ($cur =~ /^(\))/o) { my $new_type = pop(@av_paren_type); if ($new_type ne '_') { $type = $new_type; print "PAREN('$1') -> $type\n" if ($dbg_values > 1); } else { print "PAREN('$1')\n" if ($dbg_values > 1); } } elsif ($cur =~ /^($Ident)\s*\(/o) { print "FUNC($1)\n" if ($dbg_values > 1); $type = 'V'; $av_pending = 'V'; } elsif ($cur =~ /^($Ident\s*):(?:\s*\d+\s*(,|=|;))?/) { if (defined $2 && $type eq 'C' || $type eq 'T') { $av_pend_colon = 'B'; } elsif ($type eq 'E') { $av_pend_colon = 'L'; } print "IDENT_COLON($1,$type>$av_pend_colon)\n" if ($dbg_values > 1); $type = 'V'; } elsif ($cur =~ /^($Ident|$Constant)/o) { print "IDENT($1)\n" if ($dbg_values > 1); $type = 'V'; } elsif ($cur =~ /^($Assignment)/o) { print "ASSIGN($1)\n" if ($dbg_values > 1); $type = 'N'; } elsif ($cur =~/^(;|{|})/) { print "END($1)\n" if ($dbg_values > 1); $type = 'E'; $av_pend_colon = 'O'; } elsif ($cur =~/^(,)/) { print "COMMA($1)\n" if ($dbg_values > 1); $type = 'C'; } elsif ($cur =~ /^(\?)/o) { print "QUESTION($1)\n" if ($dbg_values > 1); $type = 'N'; } elsif ($cur =~ /^(:)/o) { print "COLON($1,$av_pend_colon)\n" if ($dbg_values > 1); substr($var, length($res), 1, $av_pend_colon); if ($av_pend_colon eq 'C' || $av_pend_colon eq 'L') { $type = 'E'; } else { $type = 'N'; } $av_pend_colon = 'O'; } elsif ($cur =~ /^(\[)/o) { print "CLOSE($1)\n" if ($dbg_values > 1); $type = 'N'; } elsif ($cur =~ /^(-(?![->])|\+(?!\+)|\*|\&\&|\&)/o) { my $variant; print "OPV($1)\n" if ($dbg_values > 1); if ($type eq 'V') { $variant = 'B'; } else { $variant = 'U'; } substr($var, length($res), 1, $variant); $type = 'N'; } elsif ($cur =~ /^($Operators)/o) { print "OP($1)\n" if ($dbg_values > 1); if ($1 ne '++' && $1 ne '--') { $type = 'N'; } } elsif ($cur =~ /(^.)/o) { print "C($1)\n" if ($dbg_values > 1); } if (defined $1) { $cur = substr($cur, length($1)); $res .= $type x length($1); } } return ($res, $var); } sub possible { my ($possible, $line) = @_; my $notPermitted = qr{(?: ^(?: $Modifier| $Storage| $Type| DEFINE_\S+ )$| ^(?: goto| return| case| else| asm|__asm__| do| \#| \#\#| )(?:\s|$)| ^(?:typedef|struct|enum)\b )}x; warn "CHECK<$possible> ($line)\n" if ($dbg_possible > 2); if ($possible !~ $notPermitted) { # Check for modifiers. $possible =~ s/\s*$Storage\s*//g; $possible =~ s/\s*$Sparse\s*//g; if ($possible =~ /^\s*$/) { } elsif ($possible =~ /\s/) { $possible =~ s/\s*$Type\s*//g; for my $modifier (split(' ', $possible)) { if ($modifier !~ $notPermitted) { warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible); push(@modifierListFile, $modifier); } } } else { warn "POSSIBLE: $possible ($line)\n" if ($dbg_possible); push(@typeListFile, $possible); } build_types(); } else { warn "NOTPOSS: $possible ($line)\n" if ($dbg_possible > 1); } } my $prefix = ''; sub show_type { my ($type) = @_; $type =~ tr/[a-z]/[A-Z]/; return defined $use_type{$type} if (scalar keys %use_type > 0); return !defined $ignore_type{$type}; } sub report { my ($level, $type, $msg) = @_; if (!show_type($type) || (defined $tst_only && $msg !~ /\Q$tst_only\E/)) { return 0; } my $output = ''; if ($color) { if ($level eq 'ERROR') { $output .= RED; } elsif ($level eq 'WARNING') { $output .= YELLOW; } else { $output .= GREEN; } } $output .= $prefix . $level . ':'; if ($show_types) { $output .= BLUE if ($color); $output .= "$type:"; } $output .= RESET if ($color); $output .= ' ' . $msg . "\n"; if ($showfile) { my @lines = split("\n", $output, -1); splice(@lines, 1, 1); $output = join("\n", @lines); } $output = (split('\n', $output))[0] . "\n" if ($terse); push(our @report, $output); return 1; } sub report_dump { our @report; } sub fixup_current_range { my ($lineRef, $offset, $length) = @_; if ($$lineRef =~ /^\@\@ -\d+,\d+ \+(\d+),(\d+) \@\@/) { my $o = $1; my $l = $2; my $no = $o + $offset; my $nl = $l + $length; $$lineRef =~ s/\+$o,$l \@\@/\+$no,$nl \@\@/; } } sub fix_inserted_deleted_lines { my ($linesRef, $insertedRef, $deletedRef) = @_; my $range_last_linenr = 0; my $delta_offset = 0; my $old_linenr = 0; my $new_linenr = 0; my $next_insert = 0; my $next_delete = 0; my @lines = (); my $inserted = @{$insertedRef}[$next_insert++]; my $deleted = @{$deletedRef}[$next_delete++]; foreach my $old_line (@{$linesRef}) { my $save_line = 1; my $line = $old_line; #don't modify the array if ($line =~ /^(?:\+\+\+|\-\-\-)\s+\S+/) { #new filename $delta_offset = 0; } elsif ($line =~ /^\@\@ -\d+,\d+ \+\d+,\d+ \@\@/) { #new hunk $range_last_linenr = $new_linenr; fixup_current_range(\$line, $delta_offset, 0); } while (defined($deleted) && ${$deleted}{'LINENR'} == $old_linenr) { $deleted = @{$deletedRef}[$next_delete++]; $save_line = 0; fixup_current_range(\$lines[$range_last_linenr], $delta_offset--, -1); } while (defined($inserted) && ${$inserted}{'LINENR'} == $old_linenr) { push(@lines, ${$inserted}{'LINE'}); $inserted = @{$insertedRef}[$next_insert++]; $new_linenr++; fixup_current_range(\$lines[$range_last_linenr], $delta_offset++, 1); } if ($save_line) { push(@lines, $line); $new_linenr++; } $old_linenr++; } return @lines; } sub fix_insert_line { my ($linenr, $line) = @_; my $inserted = { LINENR => $linenr, LINE => $line, }; push(@fixed_inserted, $inserted); } sub fix_delete_line { my ($linenr, $line) = @_; my $deleted = { LINENR => $linenr, LINE => $line, }; push(@fixed_deleted, $deleted); } sub ERROR { my ($type, $msg) = @_; if (report("ERROR", $type, $msg)) { our $clean = 0; our $cnt_error++; return 1; } return 0; } sub WARN { my ($type, $msg) = @_; if (report("WARNING", $type, $msg)) { our $clean = 0; our $cnt_warn++; return 1; } return 0; } sub CHK { my ($type, $msg) = @_; if ($check && report("CHECK", $type, $msg)) { our $clean = 0; our $cnt_chk++; return 1; } return 0; } sub check_absolute_file { my ($absolute, $herecurr) = @_; my $file = $absolute; ##print "absolute<$absolute>\n"; # See if any suffix of this path is a path within the tree. while ($file =~ s@^[^/]*/@@) { if (-f "$root/$file") { ##print "file<$file>\n"; last; } } if (! -f _) { return 0; } # It is, so see if the prefix is acceptable. my $prefix = $absolute; substr($prefix, -length($file)) = ''; ##print "prefix<$prefix>\n"; if ($prefix ne ".../") { WARN("USE_RELATIVE_PATH", "use relative pathname instead of absolute in changelog text\n" . $herecurr); } } sub trim { my ($string) = @_; $string =~ s/^\s+|\s+$//g; return $string; } sub ltrim { my ($string) = @_; $string =~ s/^\s+//; return $string; } sub rtrim { my ($string) = @_; $string =~ s/\s+$//; return $string; } sub string_find_replace { my ($string, $find, $replace) = @_; $string =~ s/$find/$replace/g; return $string; } sub tabify { my ($leading) = @_; my $source_indent = 8; my $max_spaces_before_tab = $source_indent - 1; my $spaces_to_tab = " " x $source_indent; #convert leading spaces to tabs 1 while $leading =~ s@^([\t]*)$spaces_to_tab@$1\t@g; #Remove spaces before a tab 1 while $leading =~ s@^([\t]*)( {1,$max_spaces_before_tab})\t@$1\t@g; return "$leading"; } sub pos_last_openparen { my ($line) = @_; my $pos = 0; my $opens = $line =~ tr/\(/\(/; my $closes = $line =~ tr/\)/\)/; my $last_openparen = 0; if (($opens == 0) || ($closes >= $opens)) { return -1; } my $len = length($line); for ($pos = 0; $pos < $len; $pos++) { my $string = substr($line, $pos); if ($string =~ /^($FuncArg|$balanced_parens)/) { $pos += length($1) - 1; } elsif (substr($line, $pos, 1) eq '(') { $last_openparen = $pos; } elsif (index($string, '(') == -1) { last; } } return length(expand_tabs(substr($line, 0, $last_openparen))) + 1; } sub process { my $filename = shift; my $linenr=0; my $prevline=""; my $prevrawline=""; my $stashline=""; my $stashrawline=""; my $length; my $indent; my $previndent=0; my $stashindent=0; our $clean = 1; my $signoff = 0; my $is_patch = 0; my $in_header_lines = $file ? 0 : 1; my $in_commit_log = 0; #Scanning lines before patch my $has_commit_log = 0; #Encountered lines before patch my $commit_log_possible_stack_dump = 0; my $commit_log_long_line = 0; my $commit_log_has_diff = 0; my $reported_maintainer_file = 0; my $non_utf8_charset = 0; my $last_blank_line = 0; my $last_coalesced_string_linenr = -1; our @report = (); our $cnt_lines = 0; our $cnt_error = 0; our $cnt_warn = 0; our $cnt_chk = 0; # Trace the real file/line as we go. my $realfile = ''; my $realline = 0; my $realcnt = 0; my $here = ''; my $context_function; #undef'd unless there's a known function my $in_comment = 0; my $comment_edge = 0; my $first_line = 0; my $p1_prefix = ''; my $prev_values = 'E'; # suppression flags my %suppress_ifbraces; my %suppress_whiletrailers; my %suppress_export; my $suppress_statement = 0; my %signatures = (); # Pre-scan the patch sanitizing the lines. # Pre-scan the patch looking for any __setup documentation. # my @setup_docs = (); my $setup_docs = 0; my $camelcase_file_seeded = 0; sanitise_line_reset(); my $line; foreach my $rawline (@rawlines) { $linenr++; $line = $rawline; push(@fixed, $rawline) if ($fix); if ($rawline=~/^\+\+\+\s+(\S+)/) { $setup_docs = 0; if ($1 =~ m@Documentation/admin-guide/kernel-parameters.rst$@) { $setup_docs = 1; } #next; } if ($rawline =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { $realline=$1-1; if (defined $2) { $realcnt=$3+1; } else { $realcnt=1+1; } $in_comment = 0; # Guestimate if this is a continuing comment. Run # the context looking for a comment "edge". If this # edge is a close comment then we must be in a comment # at context start. my $edge; my $cnt = $realcnt; for (my $ln = $linenr + 1; $cnt > 0; $ln++) { next if (defined $rawlines[$ln - 1] && $rawlines[$ln - 1] =~ /^-/); $cnt--; #print "RAW<$rawlines[$ln - 1]>\n"; last if (!defined $rawlines[$ln - 1]); if ($rawlines[$ln - 1] =~ m@(/\*|\*/)@ && $rawlines[$ln - 1] !~ m@"[^"]*(?:/\*|\*/)[^"]*"@) { ($edge) = $1; last; } } if (defined $edge && $edge eq '*/') { $in_comment = 1; } # Guestimate if this is a continuing comment. If this # is the start of a diff block and this line starts # ' *' then it is very likely a comment. if (!defined $edge && $rawlines[$linenr] =~ m@^.\s*(?:\*\*+| \*)(?:\s|$)@) { $in_comment = 1; } ##print "COMMENT:$in_comment edge<$edge> $rawline\n"; sanitise_line_reset($in_comment); } elsif ($realcnt && $rawline =~ /^(?:\+| |$)/) { # Standardise the strings and chars within the input to # simplify matching -- only bother with positive lines. $line = sanitise_line($rawline); } push(@lines, $line); if ($realcnt > 1) { $realcnt-- if ($line =~ /^(?:\+| |$)/); } else { $realcnt = 0; } #print "==>$rawline\n"; #print "-->$line\n"; if ($setup_docs && $line =~ /^\+/) { push(@setup_docs, $line); } } $prefix = ''; $realcnt = 0; $linenr = 0; $fixlinenr = -1; foreach my $line (@lines) { $linenr++; $fixlinenr++; my $sline = $line; #copy of $line $sline =~ s/$;/ /g; #with comments as spaces my $rawline = $rawlines[$linenr - 1]; #extract the line range in the file after the patch is applied if (!$in_commit_log && $line =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@(.*)/) { my $context = $4; $is_patch = 1; $first_line = $linenr + 1; $realline=$1-1; if (defined $2) { $realcnt=$3+1; } else { $realcnt=1+1; } annotate_reset(); $prev_values = 'E'; %suppress_ifbraces = (); %suppress_whiletrailers = (); %suppress_export = (); $suppress_statement = 0; if ($context =~ /\b(\w+)\s*\(/) { $context_function = $1; } else { undef $context_function; } next; # track the line number as we move through the hunk, note that # new versions of GNU diff omit the leading space on completely # blank context lines so we need to count that too. } elsif ($line =~ /^( |\+|$)/) { $realline++; $realcnt-- if ($realcnt != 0); # Measure the line length and indent. ($length, $indent) = line_stats($rawline); # Track the previous line. ($prevline, $stashline) = ($stashline, $line); ($previndent, $stashindent) = ($stashindent, $indent); ($prevrawline, $stashrawline) = ($stashrawline, $rawline); #warn "line<$line>\n"; } elsif ($realcnt == 1) { $realcnt--; } my $hunk_line = ($realcnt != 0); $here = "#$linenr: " if (!$file); $here = "#$realline: " if ($file); my $found_file = 0; # extract the filename as it passes if ($line =~ /^diff --git.*?(\S+)$/) { $realfile = $1; $realfile =~ s@^([^/]*)/@@ if (!$file); $in_commit_log = 0; $found_file = 1; } elsif ($line =~ /^\+\+\+\s+(\S+)/) { $realfile = $1; $realfile =~ s@^([^/]*)/@@ if (!$file); $in_commit_log = 0; $p1_prefix = $1; if (!$file && $tree && $p1_prefix ne '' && -e "$root/$p1_prefix") { WARN("PATCH_PREFIX", "patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n"); } if ($realfile =~ m@^include/asm/@) { ERROR("MODIFIED_INCLUDE_ASM", "do not modify files in include/asm, change architecture specific files in include/asm-\n" . "$here$rawline\n"); } $found_file = 1; } #make up the handle for any error we report on this line if ($showfile) { $prefix = "$realfile:$realline: " } elsif ($emacs) { if ($file) { $prefix = "$filename:$realline: "; } else { $prefix = "$filename:$linenr: "; } } if ($found_file) { if (is_maintained_obsolete($realfile)) { WARN("OBSOLETE", "$realfile is marked as 'obsolete' in the MAINTAINERS hierarchy. No unnecessary modifications please.\n"); } if ($realfile =~ m@^(?:drivers/net/|net/|drivers/staging/)@) { $check = 1; } else { $check = $check_orig; } next; } $here .= "FILE: $realfile:$realline:" if ($realcnt != 0); my $hereline = "$here\n$rawline\n"; my $herecurr = "$here\n$rawline\n"; my $hereprev = "$here\n$prevrawline\n$rawline\n"; $cnt_lines++ if ($realcnt != 0); # Check if the commit log has what seems like a diff which can confuse patch if ($in_commit_log && !$commit_log_has_diff && (($line =~ m@^\s+diff\b.*a/[\w/]+@ && $line =~ m@^\s+diff\b.*a/([\w/]+)\s+b/$1\b@) || $line =~ m@^\s*(?:\-\-\-\s+a/|\+\+\+\s+b/)@ || $line =~ m/^\s*\@\@ \-\d+,\d+ \+\d+,\d+ \@\@/)) { ERROR("DIFF_IN_COMMIT_MSG", "Avoid using diff content in the commit message - patch(1) might not work\n" . $herecurr); $commit_log_has_diff = 1; } # Check for incorrect file permissions if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) { my $permhere = $here . "FILE: $realfile\n"; if ($realfile !~ m@scripts/@ && $realfile !~ /\.(py|pl|awk|sh)$/) { ERROR("EXECUTE_PERMISSIONS", "do not set execute permissions for source files\n" . $permhere); } } # Check the patch for a signoff: if ($line =~ /^\s*signed-off-by:/i) { $signoff++; $in_commit_log = 0; } # Check if MAINTAINERS is being updated. If so, there's probably no need to # emit the "does MAINTAINERS need updating?" message on file add/move/delete if ($line =~ /^\s*MAINTAINERS\s*\|/) { $reported_maintainer_file = 1; } # Check signature styles if (!$in_header_lines && $line =~ /^(\s*)([a-z0-9_-]+by:|$signature_tags)(\s*)(.*)/i) { my $space_before = $1; my $sign_off = $2; my $space_after = $3; my $email = $4; my $ucfirst_sign_off = ucfirst(lc($sign_off)); if ($sign_off !~ /$signature_tags/) { WARN("BAD_SIGN_OFF", "Non-standard signature: $sign_off\n" . $herecurr); } if (defined $space_before && $space_before ne "") { if (WARN("BAD_SIGN_OFF", "Do not use whitespace before $ucfirst_sign_off\n" . $herecurr) && $fix) { $fixed[$fixlinenr] = "$ucfirst_sign_off $email"; } } if ($sign_off =~ /-by:$/i && $sign_off ne $ucfirst_sign_off) { if (WARN("BAD_SIGN_OFF", "'$ucfirst_sign_off' is the preferred signature form\n" . $herecurr) && $fix) { $fixed[$fixlinenr] = "$ucfirst_sign_off $email"; } } if (!defined $space_after || $space_after ne " ") { if (WARN("BAD_SIGN_OFF", "Use a single space after $ucfirst_sign_off\n" . $herecurr) && $fix) { $fixed[$fixlinenr] = "$ucfirst_sign_off $email"; } } my ($email_name, $email_address, $comment) = parse_email($email); my $suggested_email = format_email(($email_name, $email_address)); if ($suggested_email eq "") { ERROR("BAD_SIGN_OFF", "Unrecognized email address: '$email'\n" . $herecurr); } else { my $dequoted = $suggested_email; $dequoted =~ s/^"//; $dequoted =~ s/" $comment" ne $email && "$suggested_email$comment" ne $email) { WARN("BAD_SIGN_OFF", "email address '$email' might be better as '$suggested_email$comment'\n" . $herecurr); } } # Check for duplicate signatures my $sig_nospace = $line; $sig_nospace =~ s/\s//g; $sig_nospace = lc($sig_nospace); if (defined $signatures{$sig_nospace}) { WARN("BAD_SIGN_OFF", "Duplicate signature\n" . $herecurr); } else { $signatures{$sig_nospace} = 1; } } # Check email subject for common tools that don't need to be mentioned if ($in_header_lines && $line =~ /^Subject:.*\b(?:checkpatch|sparse|smatch)\b[^:]/i) { WARN("EMAIL_SUBJECT", "A patch subject line should describe the change not the tool that found it\n" . $herecurr); } # Check for old stable address if ($line =~ /^\s*cc:\s*.*?.*$/i) { ERROR("STABLE_ADDRESS", "The 'stable' address should be 'stable\@vger.kernel.org'\n" . $herecurr); } # Check for unwanted Gerrit info if ($in_commit_log && $line =~ /^\s*change-id:/i) { ERROR("GERRIT_CHANGE_ID", "Remove Gerrit Change-Id's before submitting upstream.\n" . $herecurr); } # Check if the commit log is in a possible stack dump if ($in_commit_log && !$commit_log_possible_stack_dump && ($line =~ /^\s*(?:WARNING:|BUG:)/ || $line =~ /^\s*\[\s*\d+\.\d{6,6}\s*\]/ || # timestamp $line =~ /^\s*\[\<[0-9a-fA-F]{8,}\>\]/)) { # stack dump address $commit_log_possible_stack_dump = 1; } # Check for line lengths > 75 in commit log, warn once if ($in_commit_log && !$commit_log_long_line && length($line) > 75 && !($line =~ /^\s*[a-zA-Z0-9_\/\.]+\s+\|\s+\d+/ || # file delta changes $line =~ /^\s*(?:[\w\.\-]+\/)++[\w\.\-]+:/ || # filename then : $line =~ /^\s*(?:Fixes:|Link:)/i || # A Fixes: or Link: line $commit_log_possible_stack_dump)) { WARN("COMMIT_LOG_LONG_LINE", "Possible unwrapped commit description (prefer a maximum 75 chars per line)\n" . $herecurr); $commit_log_long_line = 1; } # Reset possible stack dump if a blank line is found if ($in_commit_log && $commit_log_possible_stack_dump && $line =~ /^\s*$/) { $commit_log_possible_stack_dump = 0; } # Check for git id commit length and improperly formed commit descriptions if ($in_commit_log && !$commit_log_possible_stack_dump && $line !~ /^\s*(?:Link|Patchwork|http|https|BugLink):/i && $line !~ /^This reverts commit [0-9a-f]{7,40}/ && ($line =~ /\bcommit\s+[0-9a-f]{5,}\b/i || ($line =~ /(?:\s|^)[0-9a-f]{12,40}(?:[\s"'\(\[]|$)/i && $line !~ /[\<\[][0-9a-f]{12,40}[\>\]]/i && $line !~ /\bfixes:\s*[0-9a-f]{12,40}/i))) { my $init_char = "c"; my $orig_commit = ""; my $short = 1; my $long = 0; my $case = 1; my $space = 1; my $hasdesc = 0; my $hasparens = 0; my $id = '0123456789ab'; my $orig_desc = "commit description"; my $description = ""; if ($line =~ /\b(c)ommit\s+([0-9a-f]{5,})\b/i) { $init_char = $1; $orig_commit = lc($2); } elsif ($line =~ /\b([0-9a-f]{12,40})\b/i) { $orig_commit = lc($1); } $short = 0 if ($line =~ /\bcommit\s+[0-9a-f]{12,40}/i); $long = 1 if ($line =~ /\bcommit\s+[0-9a-f]{41,}/i); $space = 0 if ($line =~ /\bcommit [0-9a-f]/i); $case = 0 if ($line =~ /\b[Cc]ommit\s+[0-9a-f]{5,40}[^A-F]/); if ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)"\)/i) { $orig_desc = $1; $hasparens = 1; } elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s*$/i && defined $rawlines[$linenr] && $rawlines[$linenr] =~ /^\s*\("([^"]+)"\)/) { $orig_desc = $1; $hasparens = 1; } elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("[^"]+$/i && defined $rawlines[$linenr] && $rawlines[$linenr] =~ /^\s*[^"]+"\)/) { $line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)$/i; $orig_desc = $1; $rawlines[$linenr] =~ /^\s*([^"]+)"\)/; $orig_desc .= " " . $1; $hasparens = 1; } ($id, $description) = git_commit_info($orig_commit, $id, $orig_desc); if (defined($id) && ($short || $long || $space || $case || ($orig_desc ne $description) || !$hasparens)) { ERROR("GIT_COMMIT_ID", "Please use git commit description style 'commit <12+ chars of sha1> (\"\")' - ie: '${init_char}ommit $id (\"$description\")'\n" . $herecurr); } } # Check for added, moved or deleted files if (!$reported_maintainer_file && !$in_commit_log && ($line =~ /^(?:new|deleted) file mode\s*\d+\s*$/ || $line =~ /^rename (?:from|to) [\w\/\.\-]+\s*$/ || ($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ && (defined($1) || defined($2))))) { $is_patch = 1; $reported_maintainer_file = 1; WARN("FILE_PATH_CHANGES", "added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr); } # Check for wrappage within a valid hunk of the file if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) { ERROR("CORRUPTED_PATCH", "patch seems to be corrupt (line wrapped?)\n" . $herecurr) if (!$emitted_corrupt++); } # UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php if (($realfile =~ /^$/ || $line =~ /^\+/) && $rawline !~ m/^$UTF8*$/) { my ($utf8_prefix) = ($rawline =~ /^($UTF8*)/); my $blank = copy_spacing($rawline); my $ptr = substr($blank, 0, length($utf8_prefix)) . "^"; my $hereptr = "$hereline$ptr\n"; CHK("INVALID_UTF8", "Invalid UTF-8, patch and commit message should be encoded in UTF-8\n" . $hereptr); } # Check if it's the start of a commit log # (not a header line and we haven't seen the patch filename) if ($in_header_lines && $realfile =~ /^$/ && !($rawline =~ /^\s+(?:\S|$)/ || $rawline =~ /^(?:commit\b|from\b|[\w-]+:)/i)) { $in_header_lines = 0; $in_commit_log = 1; $has_commit_log = 1; } # Check if there is UTF-8 in a commit log when a mail header has explicitly # declined it, i.e defined some charset where it is missing. if ($in_header_lines && $rawline =~ /^Content-Type:.+charset="(.+)".*$/ && $1 !~ /utf-8/i) { $non_utf8_charset = 1; } if ($in_commit_log && $non_utf8_charset && $realfile =~ /^$/ && $rawline =~ /$NON_ASCII_UTF8/) { WARN("UTF8_BEFORE_PATCH", "8-bit UTF-8 used in possible commit log\n" . $herecurr); } # Check for absolute kernel paths in commit message if ($tree && $in_commit_log) { while ($line =~ m{(?:^|\s)(/\S*)}g) { my $file = $1; if ($file =~ m{^(.*?)(?::\d+)+:?$} && check_absolute_file($1, $herecurr)) { # } else { check_absolute_file($file, $herecurr); } } } # Check for various typo / spelling mistakes if (defined($misspellings) && ($in_commit_log || $line =~ /^(?:\+|Subject:)/i)) { while ($rawline =~ /(?:^|[^a-z@])($misspellings)(?:\b|$|[^a-z@])/gi) { my $typo = $1; my $typo_fix = $spelling_fix{lc($typo)}; $typo_fix = ucfirst($typo_fix) if ($typo =~ /^[A-Z]/); $typo_fix = uc($typo_fix) if ($typo =~ /^[A-Z]+$/); my $msg_level = \&WARN; $msg_level = \&CHK if ($file); if (&{$msg_level}("TYPO_SPELLING", "'$typo' may be misspelled - perhaps '$typo_fix'?\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/(^|[^A-Za-z@])($typo)($|[^A-Za-z@])/$1$typo_fix$3/; } } } # ignore non-hunk lines and lines being removed next if (!$hunk_line || $line =~ /^-/); #trailing whitespace if ($line =~ /^\+.*\015/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; if (ERROR("DOS_LINE_ENDINGS", "DOS line endings\n" . $herevet) && $fix) { $fixed[$fixlinenr] =~ s/[\s\015]+$//; } } elsif ($rawline =~ /^\+.*\S\s+$/ || $rawline =~ /^\+\s+$/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; if (ERROR("TRAILING_WHITESPACE", "trailing whitespace\n" . $herevet) && $fix) { $fixed[$fixlinenr] =~ s/\s+$//; } $rpt_cleaners = 1; } # Check for FSF mailing addresses. if ($rawline =~ /\bwrite to the Free/i || $rawline =~ /\b675\s+Mass\s+Ave/i || $rawline =~ /\b59\s+Temple\s+Pl/i || $rawline =~ /\b51\s+Franklin\s+St/i) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; my $msg_level = \&ERROR; $msg_level = \&CHK if ($file); &{$msg_level}("FSF_MAILING_ADDRESS", "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet) } # check for Kconfig help text having a real description # Only applies when adding the entry originally, after that we do not have # sufficient context to determine whether it is indeed long enough. if ($realfile =~ /Kconfig/ && $line =~ /^\+\s*config\s+/) { my $length = 0; my $cnt = $realcnt; my $ln = $linenr + 1; my $f; my $is_start = 0; my $is_end = 0; for (; $cnt > 0 && defined $lines[$ln - 1]; $ln++) { $f = $lines[$ln - 1]; $cnt-- if ($lines[$ln - 1] !~ /^-/); $is_end = $lines[$ln - 1] =~ /^\+/; next if ($f =~ /^-/); last if (!$file && $f =~ /^\@\@/); if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate)\s*\"/) { $is_start = 1; } elsif ($lines[$ln - 1] =~ /^\+\s*(?:---)?help(?:---)?$/) { $length = -1; } $f =~ s/^.//; $f =~ s/#.*//; $f =~ s/^\s+//; next if ($f =~ /^$/); if ($f =~ /^\s*config\s/) { $is_end = 1; last; } $length++; } if ($is_start && $is_end && $length < $min_conf_desc_length) { WARN("CONFIG_DESCRIPTION", "please write a paragraph that describes the config symbol fully\n" . $herecurr); } #print "is_start<$is_start> is_end<$is_end> length<$length>\n"; } # check for MAINTAINERS entries that don't have the right form if ($realfile =~ /^MAINTAINERS$/ && $rawline =~ /^\+[A-Z]:/ && $rawline !~ /^\+[A-Z]:\t\S/) { if (WARN("MAINTAINERS_STYLE", "MAINTAINERS entries use one tab after TYPE:\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/^(\+[A-Z]):\s*/$1:\t/; } } # discourage the use of boolean for type definition attributes of Kconfig options if ($realfile =~ /Kconfig/ && $line =~ /^\+\s*\bboolean\b/) { WARN("CONFIG_TYPE_BOOLEAN", "Use of boolean is deprecated, please use bool instead.\n" . $herecurr); } if (($realfile =~ /Makefile.*/ || $realfile =~ /Kbuild.*/) && ($line =~ /\+(EXTRA_[A-Z]+FLAGS).*/)) { my $flag = $1; my $replacement = { 'EXTRA_AFLAGS' => 'asflags-y', 'EXTRA_CFLAGS' => 'ccflags-y', 'EXTRA_CPPFLAGS' => 'cppflags-y', 'EXTRA_LDFLAGS' => 'ldflags-y', }; WARN("DEPRECATED_VARIABLE", "Use of $flag is deprecated, please use \`$replacement->{$flag} instead.\n" . $herecurr) if ($replacement->{$flag}); } # check for DT compatible documentation if (defined $root && (($realfile =~ /\.dtsi?$/ && $line =~ /^\+\s*compatible\s*=\s*\"/) || ($realfile =~ /\.[ch]$/ && $line =~ /^\+.*\.compatible\s*=\s*\"/))) { my @compats = $rawline =~ /\"([a-zA-Z0-9\-\,\.\+_]+)\"/g; my $dt_path = $root . "/Documentation/devicetree/bindings/"; my $vp_file = $dt_path . "vendor-prefixes.txt"; foreach my $compat (@compats) { my $compat2 = $compat; $compat2 =~ s/\,[a-zA-Z0-9]*\-/\,<\.\*>\-/; my $compat3 = $compat; $compat3 =~ s/\,([a-z]*)[0-9]*\-/\,$1<\.\*>\-/; `grep -Erq "$compat|$compat2|$compat3" $dt_path`; if ( $? >> 8 ) { WARN("UNDOCUMENTED_DT_STRING", "DT compatible string \"$compat\" appears un-documented -- check $dt_path\n" . $herecurr); } next if $compat !~ /^([a-zA-Z0-9\-]+)\,/; my $vendor = $1; `grep -Eq "^$vendor\\b" $vp_file`; if ( $? >> 8 ) { WARN("UNDOCUMENTED_DT_STRING", "DT compatible string vendor \"$vendor\" appears un-documented -- check $vp_file\n" . $herecurr); } } } # check we are in a valid source file if not then ignore this hunk next if ($realfile !~ /\.(h|c|s|S|sh|dtsi|dts)$/); # line length limit (with some exclusions) # # There are a few types of lines that may extend beyond $max_line_length: # logging functions like pr_info that end in a string # lines with a single string # #defines that are a single string # # There are 3 different line length message types: # LONG_LINE_COMMENT a comment starts before but extends beyond $max_line_length # LONG_LINE_STRING a string starts before but extends beyond $max_line_length # LONG_LINE all other lines longer than $max_line_length # # if LONG_LINE is ignored, the other 2 types are also ignored # if ($line =~ /^\+/ && $length > $max_line_length) { my $msg_type = "LONG_LINE"; # Check the allowed long line types first # logging functions that end in a string that starts # before $max_line_length if ($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(?:KERN_\S+\s*|[^"]*))?($String\s*(?:|,|\)\s*;)\s*)$/ && length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { $msg_type = ""; # lines with only strings (w/ possible termination) # #defines with only strings } elsif ($line =~ /^\+\s*$String\s*(?:\s*|,|\)\s*;)\s*$/ || $line =~ /^\+\s*#\s*define\s+\w+\s+$String$/) { $msg_type = ""; # More special cases } elsif ($line =~ /^\+.*\bEFI_GUID\s*\(/ || $line =~ /^\+\s*(?:\w+)?\s*DEFINE_PER_CPU/) { $msg_type = ""; # Otherwise set the alternate message types # a comment starts before $max_line_length } elsif ($line =~ /($;[\s$;]*)$/ && length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { $msg_type = "LONG_LINE_COMMENT" # a quoted string starts before $max_line_length } elsif ($sline =~ /\s*($String(?:\s*(?:\\|,\s*|\)\s*;\s*))?)$/ && length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) { $msg_type = "LONG_LINE_STRING" } if ($msg_type ne "" && (show_type("LONG_LINE") || show_type($msg_type))) { WARN($msg_type, "line over $max_line_length characters\n" . $herecurr); } } # check for adding lines without a newline. if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) { WARN("MISSING_EOF_NEWLINE", "adding a line without newline at end of file\n" . $herecurr); } # Blackfin: use hi/lo macros if ($realfile =~ m@arch/blackfin/.*\.S$@) { if ($line =~ /\.[lL][[:space:]]*=.*&[[:space:]]*0x[fF][fF][fF][fF]/) { my $herevet = "$here\n" . cat_vet($line) . "\n"; ERROR("LO_MACRO", "use the LO() macro, not (... & 0xFFFF)\n" . $herevet); } if ($line =~ /\.[hH][[:space:]]*=.*>>[[:space:]]*16/) { my $herevet = "$here\n" . cat_vet($line) . "\n"; ERROR("HI_MACRO", "use the HI() macro, not (... >> 16)\n" . $herevet); } } # check we are in a valid source file C or perl if not then ignore this hunk next if ($realfile !~ /\.(h|c|pl|dtsi|dts)$/); # at the beginning of a line any tabs must come first and anything # more than 8 must use tabs. if ($rawline =~ /^\+\s* \t\s*\S/ || $rawline =~ /^\+\s* \s*/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; $rpt_cleaners = 1; if (ERROR("CODE_INDENT", "code indent should use tabs where possible\n" . $herevet) && $fix) { $fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e; } } # check for space before tabs. if ($rawline =~ /^\+/ && $rawline =~ / \t/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; if (WARN("SPACE_BEFORE_TAB", "please, no space before tabs\n" . $herevet) && $fix) { while ($fixed[$fixlinenr] =~ s/(^\+.*) {8,8}\t/$1\t\t/) {} while ($fixed[$fixlinenr] =~ s/(^\+.*) +\t/$1\t/) {} } } # check for && or || at the start of a line if ($rawline =~ /^\+\s*(&&|\|\|)/) { CHK("LOGICAL_CONTINUATIONS", "Logical continuations should be on the previous line\n" . $hereprev); } # check indentation starts on a tab stop if ($^V && $^V ge 5.10.0 && $sline =~ /^\+\t+( +)(?:$c90_Keywords\b|\{\s*$|\}\s*(?:else\b|while\b|\s*$))/) { my $indent = length($1); if ($indent % 8) { if (WARN("TABSTOP", "Statements should start on a tabstop\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s@(^\+\t+) +@$1 . "\t" x ($indent/8)@e; } } } # check multi-line statement indentation matches previous line if ($^V && $^V ge 5.10.0 && $prevline =~ /^\+([ \t]*)((?:$c90_Keywords(?:\s+if)\s*)|(?:$Declare\s*)?(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*|(?:\*\s*)*$Lval\s*=\s*$Ident\s*)\(.*(\&\&|\|\||,)\s*$/) { $prevline =~ /^\+(\t*)(.*)$/; my $oldindent = $1; my $rest = $2; my $pos = pos_last_openparen($rest); if ($pos >= 0) { $line =~ /^(\+| )([ \t]*)/; my $newindent = $2; my $goodtabindent = $oldindent . "\t" x ($pos / 8) . " " x ($pos % 8); my $goodspaceindent = $oldindent . " " x $pos; if ($newindent ne $goodtabindent && $newindent ne $goodspaceindent) { if (CHK("PARENTHESIS_ALIGNMENT", "Alignment should match open parenthesis\n" . $hereprev) && $fix && $line =~ /^\+/) { $fixed[$fixlinenr] =~ s/^\+[ \t]*/\+$goodtabindent/; } } } } # check for space after cast like "(int) foo" or "(struct foo) bar" # avoid checking a few false positives: # "sizeof(<type>)" or "__alignof__(<type>)" # function pointer declarations like "(*foo)(int) = bar;" # structure definitions like "(struct foo) { 0 };" # multiline macros that define functions # known attributes or the __attribute__ keyword if ($line =~ /^\+(.*)\(\s*$Type\s*\)([ \t]++)((?![={]|\\$|$Attribute|__attribute__))/ && (!defined($1) || $1 !~ /\b(?:sizeof|__alignof__)\s*$/)) { if (CHK("SPACING", "No space is necessary after a cast\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/(\(\s*$Type\s*\))[ \t]+/$1/; } } # Block comment styles # Networking with an initial /* if ($realfile =~ m@^(drivers/net/|net/)@ && $prevrawline =~ /^\+[ \t]*\/\*[ \t]*$/ && $rawline =~ /^\+[ \t]*\*/ && $realline > 2) { WARN("NETWORKING_BLOCK_COMMENT_STYLE", "networking block comments don't use an empty /* line, use /* Comment...\n" . $hereprev); } # Block comments use * on subsequent lines if ($prevline =~ /$;[ \t]*$/ && #ends in comment $prevrawline =~ /^\+.*?\/\*/ && #starting /* $prevrawline !~ /\*\/[ \t]*$/ && #no trailing */ $rawline =~ /^\+/ && #line is new $rawline !~ /^\+[ \t]*\*/) { #no leading * WARN("BLOCK_COMMENT_STYLE", "Block comments use * on subsequent lines\n" . $hereprev); } # Block comments use */ on trailing lines if ($rawline !~ m@^\+[ \t]*\*/[ \t]*$@ && #trailing */ $rawline !~ m@^\+.*/\*.*\*/[ \t]*$@ && #inline /*...*/ $rawline !~ m@^\+.*\*{2,}/[ \t]*$@ && #trailing **/ $rawline =~ m@^\+[ \t]*.+\*\/[ \t]*$@) { #non blank */ WARN("BLOCK_COMMENT_STYLE", "Block comments use a trailing */ on a separate line\n" . $herecurr); } # Block comment * alignment if ($prevline =~ /$;[ \t]*$/ && #ends in comment $line =~ /^\+[ \t]*$;/ && #leading comment $rawline =~ /^\+[ \t]*\*/ && #leading * (($prevrawline =~ /^\+.*?\/\*/ && #leading /* $prevrawline !~ /\*\/[ \t]*$/) || #no trailing */ $prevrawline =~ /^\+[ \t]*\*/)) { #leading * my $oldindent; $prevrawline =~ m@^\+([ \t]*/?)\*@; if (defined($1)) { $oldindent = expand_tabs($1); } else { $prevrawline =~ m@^\+(.*/?)\*@; $oldindent = expand_tabs($1); } $rawline =~ m@^\+([ \t]*)\*@; my $newindent = $1; $newindent = expand_tabs($newindent); if (length($oldindent) ne length($newindent)) { WARN("BLOCK_COMMENT_STYLE", "Block comments should align the * on each line\n" . $hereprev); } } # check for missing blank lines after struct/union declarations # with exceptions for various attributes and macros if ($prevline =~ /^[\+ ]};?\s*$/ && $line =~ /^\+/ && !($line =~ /^\+\s*$/ || $line =~ /^\+\s*EXPORT_SYMBOL/ || $line =~ /^\+\s*MODULE_/i || $line =~ /^\+\s*\#\s*(?:end|elif|else)/ || $line =~ /^\+[a-z_]*init/ || $line =~ /^\+\s*(?:static\s+)?[A-Z_]*ATTR/ || $line =~ /^\+\s*DECLARE/ || $line =~ /^\+\s*builtin_[\w_]*driver/ || $line =~ /^\+\s*__setup/)) { if (CHK("LINE_SPACING", "Please use a blank line after function/struct/union/enum declarations\n" . $hereprev) && $fix) { fix_insert_line($fixlinenr, "\+"); } } # check for multiple consecutive blank lines if ($prevline =~ /^[\+ ]\s*$/ && $line =~ /^\+\s*$/ && $last_blank_line != ($linenr - 1)) { if (CHK("LINE_SPACING", "Please don't use multiple blank lines\n" . $hereprev) && $fix) { fix_delete_line($fixlinenr, $rawline); } $last_blank_line = $linenr; } # check for missing blank lines after declarations if ($sline =~ /^\+\s+\S/ && #Not at char 1 # actual declarations ($prevline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || # function pointer declarations $prevline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || # foo bar; where foo is some local typedef or #define $prevline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || # known declaration macros $prevline =~ /^\+\s+$declaration_macros/) && # for "else if" which can look like "$Ident $Ident" !($prevline =~ /^\+\s+$c90_Keywords\b/ || # other possible extensions of declaration lines $prevline =~ /(?:$Compare|$Assignment|$Operators)\s*$/ || # not starting a section or a macro "\" extended line $prevline =~ /(?:\{\s*|\\)$/) && # looks like a declaration !($sline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || # function pointer declarations $sline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || # foo bar; where foo is some local typedef or #define $sline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || # known declaration macros $sline =~ /^\+\s+$declaration_macros/ || # start of struct or union or enum $sline =~ /^\+\s+(?:union|struct|enum|typedef)\b/ || # start or end of block or continuation of declaration $sline =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(\[])/ || # bitfield continuation $sline =~ /^\+\s+$Ident\s*:\s*\d+\s*[,;]/ || # other possible extensions of declaration lines $sline =~ /^\+\s+\(?\s*(?:$Compare|$Assignment|$Operators)/) && # indentation of previous and current line are the same (($prevline =~ /\+(\s+)\S/) && $sline =~ /^\+$1\S/)) { if (WARN("LINE_SPACING", "Missing a blank line after declarations\n" . $hereprev) && $fix) { fix_insert_line($fixlinenr, "\+"); } } # check for spaces at the beginning of a line. # Exceptions: # 1) within comments # 2) indented preprocessor commands # 3) hanging labels if ($rawline =~ /^\+ / && $line !~ /^\+ *(?:$;|#|$Ident:)/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; if (WARN("LEADING_SPACE", "please, no spaces at the start of a line\n" . $herevet) && $fix) { $fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e; } } # check we are in a valid C source file if not then ignore this hunk next if ($realfile !~ /\.(h|c)$/); # check for unusual line ending [ or ( if ($line =~ /^\+.*([\[\(])\s*$/) { CHK("OPEN_ENDED_LINE", "Lines should not end with a '$1'\n" . $herecurr); } # check if this appears to be the start function declaration, save the name if ($sline =~ /^\+\{\s*$/ && $prevline =~ /^\+(?:(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*)?($Ident)\(/) { $context_function = $1; } # check if this appears to be the end of function declaration if ($sline =~ /^\+\}\s*$/) { undef $context_function; } # check indentation of any line with a bare else # (but not if it is a multiple line "if (foo) return bar; else return baz;") # if the previous line is a break or return and is indented 1 tab more... if ($sline =~ /^\+([\t]+)(?:}[ \t]*)?else(?:[ \t]*{)?\s*$/) { my $tabs = length($1) + 1; if ($prevline =~ /^\+\t{$tabs,$tabs}break\b/ || ($prevline =~ /^\+\t{$tabs,$tabs}return\b/ && defined $lines[$linenr] && $lines[$linenr] !~ /^[ \+]\t{$tabs,$tabs}return/)) { WARN("UNNECESSARY_ELSE", "else is not generally useful after a break or return\n" . $hereprev); } } # check indentation of a line with a break; # if the previous line is a goto or return and is indented the same # of tabs if ($sline =~ /^\+([\t]+)break\s*;\s*$/) { my $tabs = $1; if ($prevline =~ /^\+$tabs(?:goto|return)\b/) { WARN("UNNECESSARY_BREAK", "break is not useful after a goto or return\n" . $hereprev); } } # check for RCS/CVS revision markers if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) { WARN("CVS_KEYWORD", "CVS style keyword markers, these will _not_ be updated\n". $herecurr); } # Blackfin: don't use __builtin_bfin_[cs]sync if ($line =~ /__builtin_bfin_csync/) { my $herevet = "$here\n" . cat_vet($line) . "\n"; ERROR("CSYNC", "use the CSYNC() macro in asm/blackfin.h\n" . $herevet); } if ($line =~ /__builtin_bfin_ssync/) { my $herevet = "$here\n" . cat_vet($line) . "\n"; ERROR("SSYNC", "use the SSYNC() macro in asm/blackfin.h\n" . $herevet); } # check for old HOTPLUG __dev<foo> section markings if ($line =~ /\b(__dev(init|exit)(data|const|))\b/) { WARN("HOTPLUG_SECTION", "Using $1 is unnecessary\n" . $herecurr); } # Check for potential 'bare' types my ($stat, $cond, $line_nr_next, $remain_next, $off_next, $realline_next); #print "LINE<$line>\n"; if ($linenr > $suppress_statement && $realcnt && $sline =~ /.\s*\S/) { ($stat, $cond, $line_nr_next, $remain_next, $off_next) = ctx_statement_block($linenr, $realcnt, 0); $stat =~ s/\n./\n /g; $cond =~ s/\n./\n /g; #print "linenr<$linenr> <$stat>\n"; # If this statement has no statement boundaries within # it there is no point in retrying a statement scan # until we hit end of it. my $frag = $stat; $frag =~ s/;+\s*$//; if ($frag !~ /(?:{|;)/) { #print "skip<$line_nr_next>\n"; $suppress_statement = $line_nr_next; } # Find the real next line. $realline_next = $line_nr_next; if (defined $realline_next && (!defined $lines[$realline_next - 1] || substr($lines[$realline_next - 1], $off_next) =~ /^\s*$/)) { $realline_next++; } my $s = $stat; $s =~ s/{.*$//s; # Ignore goto labels. if ($s =~ /$Ident:\*$/s) { # Ignore functions being called } elsif ($s =~ /^.\s*$Ident\s*\(/s) { } elsif ($s =~ /^.\s*else\b/s) { # declarations always start with types } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) { my $type = $1; $type =~ s/\s+/ /g; possible($type, "A:" . $s); # definitions in global scope can only start with types } elsif ($s =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?($Ident)\b\s*(?!:)/s) { possible($1, "B:" . $s); } # any (foo ... *) is a pointer cast, and foo is a type while ($s =~ /\(($Ident)(?:\s+$Sparse)*[\s\*]+\s*\)/sg) { possible($1, "C:" . $s); } # Check for any sort of function declaration. # int foo(something bar, other baz); # void (*store_gdt)(x86_descr_ptr *); if ($prev_values eq 'E' && $s =~ /^(.(?:typedef\s*)?(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*(?:\b$Ident|\(\*\s*$Ident\))\s*)\(/s) { my ($name_len) = length($1); my $ctx = $s; substr($ctx, 0, $name_len + 1, ''); $ctx =~ s/\)[^\)]*$//; for my $arg (split(/\s*,\s*/, $ctx)) { if ($arg =~ /^(?:const\s+)?($Ident)(?:\s+$Sparse)*\s*\**\s*(:?\b$Ident)?$/s || $arg =~ /^($Ident)$/s) { possible($1, "D:" . $s); } } } } # # Checks which may be anchored in the context. # # Check for switch () and associated case and default # statements should be at the same indent. if ($line=~/\bswitch\s*\(.*\)/) { my $err = ''; my $sep = ''; my @ctx = ctx_block_outer($linenr, $realcnt); shift(@ctx); for my $ctx (@ctx) { my ($clen, $cindent) = line_stats($ctx); if ($ctx =~ /^\+\s*(case\s+|default:)/ && $indent != $cindent) { $err .= "$sep$ctx\n"; $sep = ''; } else { $sep = "[...]\n"; } } if ($err ne '') { ERROR("SWITCH_CASE_INDENT_LEVEL", "switch and case should be at the same indent\n$hereline$err"); } } # if/while/etc brace do not go on next line, unless defining a do while loop, # or if that brace on the next line is for something else if ($line =~ /(.*)\b((?:if|while|for|switch|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) { my $pre_ctx = "$1$2"; my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0); if ($line =~ /^\+\t{6,}/) { WARN("DEEP_INDENTATION", "Too many leading tabs - consider code refactoring\n" . $herecurr); } my $ctx_cnt = $realcnt - $#ctx - 1; my $ctx = join("\n", @ctx); my $ctx_ln = $linenr; my $ctx_skip = $realcnt; while ($ctx_skip > $ctx_cnt || ($ctx_skip == $ctx_cnt && defined $lines[$ctx_ln - 1] && $lines[$ctx_ln - 1] =~ /^-/)) { ##print "SKIP<$ctx_skip> CNT<$ctx_cnt>\n"; $ctx_skip-- if (!defined $lines[$ctx_ln - 1] || $lines[$ctx_ln - 1] !~ /^-/); $ctx_ln++; } #print "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n"; #print "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n"; if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln - 1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) { ERROR("OPEN_BRACE", "that open brace { should be on the previous line\n" . "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); } if ($level == 0 && $pre_ctx !~ /}\s*while\s*\($/ && $ctx =~ /\)\s*\;\s*$/ && defined $lines[$ctx_ln - 1]) { my ($nlength, $nindent) = line_stats($lines[$ctx_ln - 1]); if ($nindent > $indent) { WARN("TRAILING_SEMICOLON", "trailing semicolon indicates no statements, indent implies otherwise\n" . "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); } } } # Check relative indent for conditionals and blocks. if ($line =~ /\b(?:(?:if|while|for|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|(?:do|else)\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { ($stat, $cond, $line_nr_next, $remain_next, $off_next) = ctx_statement_block($linenr, $realcnt, 0) if (!defined $stat); my ($s, $c) = ($stat, $cond); substr($s, 0, length($c), ''); # remove inline comments $s =~ s/$;/ /g; $c =~ s/$;/ /g; # Find out how long the conditional actually is. my @newlines = ($c =~ /\n/gs); my $cond_lines = 1 + $#newlines; # Make sure we remove the line prefixes as we have # none on the first line, and are going to readd them # where necessary. $s =~ s/\n./\n/gs; while ($s =~ /\n\s+\\\n/) { $cond_lines += $s =~ s/\n\s+\\\n/\n/g; } # We want to check the first line inside the block # starting at the end of the conditional, so remove: # 1) any blank line termination # 2) any opening brace { on end of the line # 3) any do (...) { my $continuation = 0; my $check = 0; $s =~ s/^.*\bdo\b//; $s =~ s/^\s*{//; if ($s =~ s/^\s*\\//) { $continuation = 1; } if ($s =~ s/^\s*?\n//) { $check = 1; $cond_lines++; } # Also ignore a loop construct at the end of a # preprocessor statement. if (($prevline =~ /^.\s*#\s*define\s/ || $prevline =~ /\\\s*$/) && $continuation == 0) { $check = 0; } my $cond_ptr = -1; $continuation = 0; while ($cond_ptr != $cond_lines) { $cond_ptr = $cond_lines; # If we see an #else/#elif then the code # is not linear. if ($s =~ /^\s*\#\s*(?:else|elif)/) { $check = 0; } # Ignore: # 1) blank lines, they should be at 0, # 2) preprocessor lines, and # 3) labels. if ($continuation || $s =~ /^\s*?\n/ || $s =~ /^\s*#\s*?/ || $s =~ /^\s*$Ident\s*:/) { $continuation = ($s =~ /^.*?\\\n/) ? 1 : 0; if ($s =~ s/^.*?\n//) { $cond_lines++; } } } my (undef, $sindent) = line_stats("+" . $s); my $stat_real = raw_line($linenr, $cond_lines); # Check if either of these lines are modified, else # this is not this patch's fault. if (!defined($stat_real) || $stat !~ /^\+/ && $stat_real !~ /^\+/) { $check = 0; } if (defined($stat_real) && $cond_lines > 1) { $stat_real = "[...]\n$stat_real"; } #print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n"; if ($check && $s ne '' && (($sindent % 8) != 0 || ($sindent < $indent) || ($sindent == $indent && ($s !~ /^\s*(?:\}|\{|else\b)/)) || ($sindent > $indent + 8))) { WARN("SUSPECT_CODE_INDENT", "suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n"); } } # Track the 'values' across context and added lines. my $opline = $line; $opline =~ s/^./ /; my ($curr_values, $curr_vars) = annotate_values($opline . "\n", $prev_values); $curr_values = $prev_values . $curr_values; if ($dbg_values) { my $outline = $opline; $outline =~ s/\t/ /g; print "$linenr > .$outline\n"; print "$linenr > $curr_values\n"; print "$linenr > $curr_vars\n"; } $prev_values = substr($curr_values, -1); #ignore lines not being added next if ($line =~ /^[^\+]/); # check for dereferences that span multiple lines if ($prevline =~ /^\+.*$Lval\s*(?:\.|->)\s*$/ && $line =~ /^\+\s*(?!\#\s*(?!define\s+|if))\s*$Lval/) { $prevline =~ /($Lval\s*(?:\.|->))\s*$/; my $ref = $1; $line =~ /^.\s*($Lval)/; $ref .= $1; $ref =~ s/\s//g; WARN("MULTILINE_DEREFERENCE", "Avoid multiple line dereference - prefer '$ref'\n" . $hereprev); } # check for declarations of signed or unsigned without int while ($line =~ m{\b($Declare)\s*(?!char\b|short\b|int\b|long\b)\s*($Ident)?\s*[=,;\[\)\(]}g) { my $type = $1; my $var = $2; $var = "" if (!defined $var); if ($type =~ /^(?:(?:$Storage|$Inline|$Attribute)\s+)*((?:un)?signed)((?:\s*\*)*)\s*$/) { my $sign = $1; my $pointer = $2; $pointer = "" if (!defined $pointer); if (WARN("UNSPECIFIED_INT", "Prefer '" . trim($sign) . " int" . rtrim($pointer) . "' to bare use of '$sign" . rtrim($pointer) . "'\n" . $herecurr) && $fix) { my $decl = trim($sign) . " int "; my $comp_pointer = $pointer; $comp_pointer =~ s/\s//g; $decl .= $comp_pointer; $decl = rtrim($decl) if ($var eq ""); $fixed[$fixlinenr] =~ s@\b$sign\s*\Q$pointer\E\s*$var\b@$decl$var@; } } } # TEST: allow direct testing of the type matcher. if ($dbg_type) { if ($line =~ /^.\s*$Declare\s*$/) { ERROR("TEST_TYPE", "TEST: is type\n" . $herecurr); } elsif ($dbg_type > 1 && $line =~ /^.+($Declare)/) { ERROR("TEST_NOT_TYPE", "TEST: is not type ($1 is)\n". $herecurr); } next; } # TEST: allow direct testing of the attribute matcher. if ($dbg_attr) { if ($line =~ /^.\s*$Modifier\s*$/) { ERROR("TEST_ATTR", "TEST: is attr\n" . $herecurr); } elsif ($dbg_attr > 1 && $line =~ /^.+($Modifier)/) { ERROR("TEST_NOT_ATTR", "TEST: is not attr ($1 is)\n". $herecurr); } next; } # check for initialisation to aggregates open brace on the next line if ($line =~ /^.\s*{/ && $prevline =~ /(?:^|[^=])=\s*$/) { if (ERROR("OPEN_BRACE", "that open brace { should be on the previous line\n" . $hereprev) && $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { fix_delete_line($fixlinenr - 1, $prevrawline); fix_delete_line($fixlinenr, $rawline); my $fixedline = $prevrawline; $fixedline =~ s/\s*=\s*$/ = {/; fix_insert_line($fixlinenr, $fixedline); $fixedline = $line; $fixedline =~ s/^(.\s*)\{\s*/$1/; fix_insert_line($fixlinenr, $fixedline); } } # # Checks which are anchored on the added line. # # check for malformed paths in #include statements (uses RAW line) if ($rawline =~ m{^.\s*\#\s*include\s+[<"](.*)[">]}) { my $path = $1; if ($path =~ m{//}) { ERROR("MALFORMED_INCLUDE", "malformed #include filename\n" . $herecurr); } if ($path =~ "^uapi/" && $realfile =~ m@\binclude/uapi/@) { ERROR("UAPI_INCLUDE", "No #include in ...include/uapi/... should use a uapi/ path prefix\n" . $herecurr); } } # no C99 // comments if ($line =~ m{//}) { if (ERROR("C99_COMMENTS", "do not use C99 // comments\n" . $herecurr) && $fix) { my $line = $fixed[$fixlinenr]; if ($line =~ /\/\/(.*)$/) { my $comment = trim($1); $fixed[$fixlinenr] =~ s@\/\/(.*)$@/\* $comment \*/@; } } } # Remove C99 comments. $line =~ s@//.*@@; $opline =~ s@//.*@@; # EXPORT_SYMBOL should immediately follow the thing it is exporting, consider # the whole statement. #print "APW <$lines[$realline_next - 1]>\n"; if (defined $realline_next && exists $lines[$realline_next - 1] && !defined $suppress_export{$realline_next} && ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/ || $lines[$realline_next - 1] =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { # Handle definitions which produce identifiers with # a prefix: # XXX(foo); # EXPORT_SYMBOL(something_foo); my $name = $1; if ($stat =~ /^(?:.\s*}\s*\n)?.([A-Z_]+)\s*\(\s*($Ident)/ && $name =~ /^${Ident}_$2/) { #print "FOO C name<$name>\n"; $suppress_export{$realline_next} = 1; } elsif ($stat !~ /(?: \n.}\s*$| ^.DEFINE_$Ident\(\Q$name\E\)| ^.DECLARE_$Ident\(\Q$name\E\)| ^.LIST_HEAD\(\Q$name\E\)| ^.(?:$Storage\s+)?$Type\s*\(\s*\*\s*\Q$name\E\s*\)\s*\(| \b\Q$name\E(?:\s+$Attribute)*\s*(?:;|=|\[|\() )/x) { #print "FOO A<$lines[$realline_next - 1]> stat<$stat> name<$name>\n"; $suppress_export{$realline_next} = 2; } else { $suppress_export{$realline_next} = 1; } } if (!defined $suppress_export{$linenr} && $prevline =~ /^.\s*$/ && ($line =~ /EXPORT_SYMBOL.*\((.*)\)/ || $line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { #print "FOO B <$lines[$linenr - 1]>\n"; $suppress_export{$linenr} = 2; } if (defined $suppress_export{$linenr} && $suppress_export{$linenr} == 2) { WARN("EXPORT_SYMBOL", "EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr); } # check for global initialisers. if ($line =~ /^\+$Type\s*$Ident(?:\s+$Modifier)*\s*=\s*($zero_initializer)\s*;/) { if (ERROR("GLOBAL_INITIALISERS", "do not initialise globals to $1\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/(^.$Type\s*$Ident(?:\s+$Modifier)*)\s*=\s*$zero_initializer\s*;/$1;/; } } # check for static initialisers. if ($line =~ /^\+.*\bstatic\s.*=\s*($zero_initializer)\s*;/) { if (ERROR("INITIALISED_STATIC", "do not initialise statics to $1\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/(\bstatic\s.*?)\s*=\s*$zero_initializer\s*;/$1;/; } } # check for misordered declarations of char/short/int/long with signed/unsigned while ($sline =~ m{(\b$TypeMisordered\b)}g) { my $tmp = trim($1); WARN("MISORDERED_TYPE", "type '$tmp' should be specified in [[un]signed] [short|int|long|long long] order\n" . $herecurr); } # check for static const char * arrays. if ($line =~ /\bstatic\s+const\s+char\s*\*\s*(\w+)\s*\[\s*\]\s*=\s*/) { WARN("STATIC_CONST_CHAR_ARRAY", "static const char * array should probably be static const char * const\n" . $herecurr); } # check for static char foo[] = "bar" declarations. if ($line =~ /\bstatic\s+char\s+(\w+)\s*\[\s*\]\s*=\s*"/) { WARN("STATIC_CONST_CHAR_ARRAY", "static char array declaration should probably be static const char\n" . $herecurr); } # check for const <foo> const where <foo> is not a pointer or array type if ($sline =~ /\bconst\s+($BasicType)\s+const\b/) { my $found = $1; if ($sline =~ /\bconst\s+\Q$found\E\s+const\b\s*\*/) { WARN("CONST_CONST", "'const $found const *' should probably be 'const $found * const'\n" . $herecurr); } elsif ($sline !~ /\bconst\s+\Q$found\E\s+const\s+\w+\s*\[/) { WARN("CONST_CONST", "'const $found const' should probably be 'const $found'\n" . $herecurr); } } # check for non-global char *foo[] = {"bar", ...} declarations. if ($line =~ /^.\s+(?:static\s+|const\s+)?char\s+\*\s*\w+\s*\[\s*\]\s*=\s*\{/) { WARN("STATIC_CONST_CHAR_ARRAY", "char * array declaration might be better as static const\n" . $herecurr); } # check for sizeof(foo)/sizeof(foo[0]) that could be ARRAY_SIZE(foo) if ($line =~ m@\bsizeof\s*\(\s*($Lval)\s*\)@) { my $array = $1; if ($line =~ m@\b(sizeof\s*\(\s*\Q$array\E\s*\)\s*/\s*sizeof\s*\(\s*\Q$array\E\s*\[\s*0\s*\]\s*\))@) { my $array_div = $1; if (WARN("ARRAY_SIZE", "Prefer ARRAY_SIZE($array)\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\Q$array_div\E/ARRAY_SIZE($array)/; } } } # check for function declarations without arguments like "int foo()" if ($line =~ /(\b$Type\s+$Ident)\s*\(\s*\)/) { if (ERROR("FUNCTION_WITHOUT_ARGS", "Bad function definition - $1() should probably be $1(void)\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/(\b($Type)\s+($Ident))\s*\(\s*\)/$2 $3(void)/; } } # check for new typedefs, only function parameters and sparse annotations # make sense. if ($line =~ /\btypedef\s/ && $line !~ /\btypedef\s+$Type\s*\(\s*\*?$Ident\s*\)\s*\(/ && $line !~ /\btypedef\s+$Type\s+$Ident\s*\(/ && $line !~ /\b$typeTypedefs\b/ && $line !~ /\b__bitwise\b/) { WARN("NEW_TYPEDEFS", "do not add new typedefs\n" . $herecurr); } # * goes on variable not on type # (char*[ const]) while ($line =~ m{(\($NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)\))}g) { #print "AA<$1>\n"; my ($ident, $from, $to) = ($1, $2, $2); # Should start with a space. $to =~ s/^(\S)/ $1/; # Should not end with a space. $to =~ s/\s+$//; # '*'s should not have spaces between. while ($to =~ s/\*\s+\*/\*\*/) { } ## print "1: from<$from> to<$to> ident<$ident>\n"; if ($from ne $to) { if (ERROR("POINTER_LOCATION", "\"(foo$from)\" should be \"(foo$to)\"\n" . $herecurr) && $fix) { my $sub_from = $ident; my $sub_to = $ident; $sub_to =~ s/\Q$from\E/$to/; $fixed[$fixlinenr] =~ s@\Q$sub_from\E@$sub_to@; } } } while ($line =~ m{(\b$NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)($Ident))}g) { #print "BB<$1>\n"; my ($match, $from, $to, $ident) = ($1, $2, $2, $3); # Should start with a space. $to =~ s/^(\S)/ $1/; # Should not end with a space. $to =~ s/\s+$//; # '*'s should not have spaces between. while ($to =~ s/\*\s+\*/\*\*/) { } # Modifiers should have spaces. $to =~ s/(\b$Modifier$)/$1 /; ## print "2: from<$from> to<$to> ident<$ident>\n"; if ($from ne $to && $ident !~ /^$Modifier$/) { if (ERROR("POINTER_LOCATION", "\"foo${from}bar\" should be \"foo${to}bar\"\n" . $herecurr) && $fix) { my $sub_from = $match; my $sub_to = $match; $sub_to =~ s/\Q$from\E/$to/; $fixed[$fixlinenr] =~ s@\Q$sub_from\E@$sub_to@; } } } # avoid BUG() or BUG_ON() if ($line =~ /\b(?:BUG|BUG_ON)\b/) { my $msg_level = \&WARN; $msg_level = \&CHK if ($file); &{$msg_level}("AVOID_BUG", "Avoid crashing the kernel - try using WARN_ON & recovery code rather than BUG() or BUG_ON()\n" . $herecurr); } # avoid LINUX_VERSION_CODE if ($line =~ /\bLINUX_VERSION_CODE\b/) { WARN("LINUX_VERSION_CODE", "LINUX_VERSION_CODE should be avoided, code should be for the version to which it is merged\n" . $herecurr); } # check for uses of printk_ratelimit if ($line =~ /\bprintk_ratelimit\s*\(/) { WARN("PRINTK_RATELIMITED", "Prefer printk_ratelimited or pr_<level>_ratelimited to printk_ratelimit\n" . $herecurr); } # printk should use KERN_* levels if ($line =~ /\bprintk\s*\(\s*(?!KERN_[A-Z]+\b)/) { WARN("PRINTK_WITHOUT_KERN_LEVEL", "printk() should include KERN_<LEVEL> facility level\n" . $herecurr); } if ($line =~ /\bprintk\s*\(\s*KERN_([A-Z]+)/) { my $orig = $1; my $level = lc($orig); $level = "warn" if ($level eq "warning"); my $level2 = $level; $level2 = "dbg" if ($level eq "debug"); WARN("PREFER_PR_LEVEL", "Prefer [subsystem eg: netdev]_$level2([subsystem]dev, ... then dev_$level2(dev, ... then pr_$level(... to printk(KERN_$orig ...\n" . $herecurr); } if ($line =~ /\bpr_warning\s*\(/) { if (WARN("PREFER_PR_LEVEL", "Prefer pr_warn(... to pr_warning(...\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\bpr_warning\b/pr_warn/; } } if ($line =~ /\bdev_printk\s*\(\s*KERN_([A-Z]+)/) { my $orig = $1; my $level = lc($orig); $level = "warn" if ($level eq "warning"); $level = "dbg" if ($level eq "debug"); WARN("PREFER_DEV_LEVEL", "Prefer dev_$level(... to dev_printk(KERN_$orig, ...\n" . $herecurr); } # ENOSYS means "bad syscall nr" and nothing else. This will have a small # number of false positives, but assembly files are not checked, so at # least the arch entry code will not trigger this warning. if ($line =~ /\bENOSYS\b/) { WARN("ENOSYS", "ENOSYS means 'invalid syscall nr' and nothing else\n" . $herecurr); } # function brace can't be on same line, except for #defines of do while, # or if closed on same line if (($line=~/$Type\s*$Ident\(.*\).*\s*{/) and !($line=~/\#\s*define.*do\s\{/) and !($line=~/}/)) { if (ERROR("OPEN_BRACE", "open brace '{' following function declarations go on the next line\n" . $herecurr) && $fix) { fix_delete_line($fixlinenr, $rawline); my $fixed_line = $rawline; $fixed_line =~ /(^..*$Type\s*$Ident\(.*\)\s*){(.*)$/; my $line1 = $1; my $line2 = $2; fix_insert_line($fixlinenr, ltrim($line1)); fix_insert_line($fixlinenr, "\+{"); if ($line2 !~ /^\s*$/) { fix_insert_line($fixlinenr, "\+\t" . trim($line2)); } } } # open braces for enum, union and struct go on the same line. if ($line =~ /^.\s*{/ && $prevline =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?\s*$/) { if (ERROR("OPEN_BRACE", "open brace '{' following $1 go on the same line\n" . $hereprev) && $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { fix_delete_line($fixlinenr - 1, $prevrawline); fix_delete_line($fixlinenr, $rawline); my $fixedline = rtrim($prevrawline) . " {"; fix_insert_line($fixlinenr, $fixedline); $fixedline = $rawline; $fixedline =~ s/^(.\s*)\{\s*/$1\t/; if ($fixedline !~ /^\+\s*$/) { fix_insert_line($fixlinenr, $fixedline); } } } # missing space after union, struct or enum definition if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident){1,2}[=\{]/) { if (WARN("SPACING", "missing space after $1 definition\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/^(.\s*(?:typedef\s+)?(?:enum|union|struct)(?:\s+$Ident){1,2})([=\{])/$1 $2/; } } # Function pointer declarations # check spacing between type, funcptr, and args # canonical declaration is "type (*funcptr)(args...)" if ($line =~ /^.\s*($Declare)\((\s*)\*(\s*)($Ident)(\s*)\)(\s*)\(/) { my $declare = $1; my $pre_pointer_space = $2; my $post_pointer_space = $3; my $funcname = $4; my $post_funcname_space = $5; my $pre_args_space = $6; # the $Declare variable will capture all spaces after the type # so check it for a missing trailing missing space but pointer return types # don't need a space so don't warn for those. my $post_declare_space = ""; if ($declare =~ /(\s+)$/) { $post_declare_space = $1; $declare = rtrim($declare); } if ($declare !~ /\*$/ && $post_declare_space =~ /^$/) { WARN("SPACING", "missing space after return type\n" . $herecurr); $post_declare_space = " "; } # unnecessary space "type (*funcptr)(args...)" # This test is not currently implemented because these declarations are # equivalent to # int foo(int bar, ...) # and this is form shouldn't/doesn't generate a checkpatch warning. # # elsif ($declare =~ /\s{2,}$/) { # WARN("SPACING", # "Multiple spaces after return type\n" . $herecurr); # } # unnecessary space "type ( *funcptr)(args...)" if (defined $pre_pointer_space && $pre_pointer_space =~ /^\s/) { WARN("SPACING", "Unnecessary space after function pointer open parenthesis\n" . $herecurr); } # unnecessary space "type (* funcptr)(args...)" if (defined $post_pointer_space && $post_pointer_space =~ /^\s/) { WARN("SPACING", "Unnecessary space before function pointer name\n" . $herecurr); } # unnecessary space "type (*funcptr )(args...)" if (defined $post_funcname_space && $post_funcname_space =~ /^\s/) { WARN("SPACING", "Unnecessary space after function pointer name\n" . $herecurr); } # unnecessary space "type (*funcptr) (args...)" if (defined $pre_args_space && $pre_args_space =~ /^\s/) { WARN("SPACING", "Unnecessary space before function pointer arguments\n" . $herecurr); } if (show_type("SPACING") && $fix) { $fixed[$fixlinenr] =~ s/^(.\s*)$Declare\s*\(\s*\*\s*$Ident\s*\)\s*\(/$1 . $declare . $post_declare_space . '(*' . $funcname . ')('/ex; } } # check for spacing round square brackets; allowed: # 1. with a type on the left -- int [] a; # 2. at the beginning of a line for slice initialisers -- [0...10] = 5, # 3. inside a curly brace -- = { [0...10] = 5 } while ($line =~ /(.*?\s)\[/g) { my ($where, $prefix) = ($-[1], $1); if ($prefix !~ /$Type\s+$/ && ($where != 0 || $prefix !~ /^.\s+$/) && $prefix !~ /[{,]\s+$/) { if (ERROR("BRACKET_SPACE", "space prohibited before open square bracket '['\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/^(\+.*?)\s+\[/$1\[/; } } } # check for spaces between functions and their parentheses. while ($line =~ /($Ident)\s+\(/g) { my $name = $1; my $ctx_before = substr($line, 0, $-[1]); my $ctx = "$ctx_before$name"; # Ignore those directives where spaces _are_ permitted. if ($name =~ /^(?: if|for|while|switch|return|case| volatile|__volatile__| __attribute__|format|__extension__| asm|__asm__)$/x) { # cpp #define statements have non-optional spaces, ie # if there is a space between the name and the open # parenthesis it is simply not a parameter group. } elsif ($ctx_before =~ /^.\s*\#\s*define\s*$/) { # cpp #elif statement condition may start with a ( } elsif ($ctx =~ /^.\s*\#\s*elif\s*$/) { # If this whole things ends with a type its most # likely a typedef for a function. } elsif ($ctx =~ /$Type$/) { } else { if (WARN("SPACING", "space prohibited between function name and open parenthesis '('\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\b$name\s+\(/$name\(/; } } } # Check operator spacing. if (!($line=~/\#\s*include/)) { my $fixed_line = ""; my $line_fixed = 0; my $ops = qr{ <<=|>>=|<=|>=|==|!=| \+=|-=|\*=|\/=|%=|\^=|\|=|&=| =>|->|<<|>>|<|>|=|!|~| &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%| \?:|\?|: }x; my @elements = split(/($ops|;)/, $opline); ## print("element count: <" . $#elements . ">\n"); ## foreach my $el (@elements) { ## print("el: <$el>\n"); ## } my @fix_elements = (); my $off = 0; foreach my $el (@elements) { push(@fix_elements, substr($rawline, $off, length($el))); $off += length($el); } $off = 0; my $blank = copy_spacing($opline); my $last_after = -1; for (my $n = 0; $n < $#elements; $n += 2) { my $good = $fix_elements[$n] . $fix_elements[$n + 1]; ## print("n: <$n> good: <$good>\n"); $off += length($elements[$n]); # Pick up the preceding and succeeding characters. my $ca = substr($opline, 0, $off); my $cc = ''; if (length($opline) >= ($off + length($elements[$n + 1]))) { $cc = substr($opline, $off + length($elements[$n + 1])); } my $cb = "$ca$;$cc"; my $a = ''; $a = 'V' if ($elements[$n] ne ''); $a = 'W' if ($elements[$n] =~ /\s$/); $a = 'C' if ($elements[$n] =~ /$;$/); $a = 'B' if ($elements[$n] =~ /(\[|\()$/); $a = 'O' if ($elements[$n] eq ''); $a = 'E' if ($ca =~ /^\s*$/); my $op = $elements[$n + 1]; my $c = ''; if (defined $elements[$n + 2]) { $c = 'V' if ($elements[$n + 2] ne ''); $c = 'W' if ($elements[$n + 2] =~ /^\s/); $c = 'C' if ($elements[$n + 2] =~ /^$;/); $c = 'B' if ($elements[$n + 2] =~ /^(\)|\]|;)/); $c = 'O' if ($elements[$n + 2] eq ''); $c = 'E' if ($elements[$n + 2] =~ /^\s*\\$/); } else { $c = 'E'; } my $ctx = "${a}x${c}"; my $at = "(ctx:$ctx)"; my $ptr = substr($blank, 0, $off) . "^"; my $hereptr = "$hereline$ptr\n"; # Pull out the value of this operator. my $op_type = substr($curr_values, $off + 1, 1); # Get the full operator variant. my $opv = $op . substr($curr_vars, $off, 1); # Ignore operators passed as parameters. if ($op_type ne 'V' && $ca =~ /\s$/ && $cc =~ /^\s*[,\)]/) { # # Ignore comments # } elsif ($op =~ /^$;+$/) { # ; should have either the end of line or a space or \ after it } elsif ($op eq ';') { if ($ctx !~ /.x[WEBC]/ && $cc !~ /^\\/ && $cc !~ /^;/) { if (ERROR("SPACING", "space required after that '$op' $at\n" . $hereptr)) { $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; $line_fixed = 1; } } # // is a comment } elsif ($op eq '//') { # : when part of a bitfield } elsif ($opv eq ':B') { # skip the bitfield test for now # No spaces for: # -> } elsif ($op eq '->') { if ($ctx =~ /Wx.|.xW/) { if (ERROR("SPACING", "spaces prohibited around that '$op' $at\n" . $hereptr)) { $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); if (defined $fix_elements[$n + 2]) { $fix_elements[$n + 2] =~ s/^\s+//; } $line_fixed = 1; } } # , must not have a space before and must have a space on the right. } elsif ($op eq ',') { my $rtrim_before = 0; my $space_after = 0; if ($ctx =~ /Wx./) { if (ERROR("SPACING", "space prohibited before that '$op' $at\n" . $hereptr)) { $line_fixed = 1; $rtrim_before = 1; } } if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) { if (ERROR("SPACING", "space required after that '$op' $at\n" . $hereptr)) { $line_fixed = 1; $last_after = $n; $space_after = 1; } } if ($rtrim_before || $space_after) { if ($rtrim_before) { $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); } else { $good = $fix_elements[$n] . trim($fix_elements[$n + 1]); } if ($space_after) { $good .= " "; } } # '*' as part of a type definition -- reported already. } elsif ($opv eq '*_') { #warn "'*' is part of type\n"; # unary operators should have a space before and # none after. May be left adjacent to another # unary operator, or a cast } elsif ($op eq '!' || $op eq '~' || $opv eq '*U' || $opv eq '-U' || $opv eq '&U' || $opv eq '&&U') { if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { if (ERROR("SPACING", "space required before that '$op' $at\n" . $hereptr)) { if ($n != $last_after + 2) { $good = $fix_elements[$n] . " " . ltrim($fix_elements[$n + 1]); $line_fixed = 1; } } } if ($op eq '*' && $cc =~/\s*$Modifier\b/) { # A unary '*' may be const } elsif ($ctx =~ /.xW/) { if (ERROR("SPACING", "space prohibited after that '$op' $at\n" . $hereptr)) { $good = $fix_elements[$n] . rtrim($fix_elements[$n + 1]); if (defined $fix_elements[$n + 2]) { $fix_elements[$n + 2] =~ s/^\s+//; } $line_fixed = 1; } } # unary ++ and unary -- are allowed no space on one side. } elsif ($op eq '++' or $op eq '--') { if ($ctx !~ /[WEOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) { if (ERROR("SPACING", "space required one side of that '$op' $at\n" . $hereptr)) { $good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " "; $line_fixed = 1; } } if ($ctx =~ /Wx[BE]/ || ($ctx =~ /Wx./ && $cc =~ /^;/)) { if (ERROR("SPACING", "space prohibited before that '$op' $at\n" . $hereptr)) { $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); $line_fixed = 1; } } if ($ctx =~ /ExW/) { if (ERROR("SPACING", "space prohibited after that '$op' $at\n" . $hereptr)) { $good = $fix_elements[$n] . trim($fix_elements[$n + 1]); if (defined $fix_elements[$n + 2]) { $fix_elements[$n + 2] =~ s/^\s+//; } $line_fixed = 1; } } # << and >> may either have or not have spaces both sides } elsif ($op eq '<<' or $op eq '>>' or $op eq '&' or $op eq '^' or $op eq '|' or $op eq '+' or $op eq '-' or $op eq '*' or $op eq '/' or $op eq '%') { if ($check) { if (defined $fix_elements[$n + 2] && $ctx !~ /[EW]x[EW]/) { if (CHK("SPACING", "spaces preferred around that '$op' $at\n" . $hereptr)) { $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; $fix_elements[$n + 2] =~ s/^\s+//; $line_fixed = 1; } } elsif (!defined $fix_elements[$n + 2] && $ctx !~ /Wx[OE]/) { if (CHK("SPACING", "space preferred before that '$op' $at\n" . $hereptr)) { $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]); $line_fixed = 1; } } } elsif ($ctx =~ /Wx[^WCE]|[^WCE]xW/) { if (ERROR("SPACING", "need consistent spacing around '$op' $at\n" . $hereptr)) { $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; if (defined $fix_elements[$n + 2]) { $fix_elements[$n + 2] =~ s/^\s+//; } $line_fixed = 1; } } # A colon needs no spaces before when it is # terminating a case value or a label. } elsif ($opv eq ':C' || $opv eq ':L') { if ($ctx =~ /Wx./) { if (ERROR("SPACING", "space prohibited before that '$op' $at\n" . $hereptr)) { $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); $line_fixed = 1; } } # All the others need spaces both sides. } elsif ($ctx !~ /[EWC]x[CWE]/) { my $ok = 0; # Ignore email addresses <foo@bar> if (($op eq '<' && $cc =~ /^\S+\@\S+>/) || ($op eq '>' && $ca =~ /<\S+\@\S+$/)) { $ok = 1; } # for asm volatile statements # ignore a colon with another # colon immediately before or after if (($op eq ':') && ($ca =~ /:$/ || $cc =~ /^:/)) { $ok = 1; } # messages are ERROR, but ?: are CHK if ($ok == 0) { my $msg_level = \&ERROR; $msg_level = \&CHK if (($op eq '?:' || $op eq '?' || $op eq ':') && $ctx =~ /VxV/); if (&{$msg_level}("SPACING", "spaces required around that '$op' $at\n" . $hereptr)) { $good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " "; if (defined $fix_elements[$n + 2]) { $fix_elements[$n + 2] =~ s/^\s+//; } $line_fixed = 1; } } } $off += length($elements[$n + 1]); ## print("n: <$n> GOOD: <$good>\n"); $fixed_line = $fixed_line . $good; } if (($#elements % 2) == 0) { $fixed_line = $fixed_line . $fix_elements[$#elements]; } if ($fix && $line_fixed && $fixed_line ne $fixed[$fixlinenr]) { $fixed[$fixlinenr] = $fixed_line; } } # check for whitespace before a non-naked semicolon if ($line =~ /^\+.*\S\s+;\s*$/) { if (WARN("SPACING", "space prohibited before semicolon\n" . $herecurr) && $fix) { 1 while $fixed[$fixlinenr] =~ s/^(\+.*\S)\s+;/$1;/; } } # check for multiple assignments if ($line =~ /^.\s*$Lval\s*=\s*$Lval\s*=(?!=)/) { CHK("MULTIPLE_ASSIGNMENTS", "multiple assignments should be avoided\n" . $herecurr); } ## # check for multiple declarations, allowing for a function declaration ## # continuation. ## if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ && ## $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) { ## ## # Remove any bracketed sections to ensure we do not ## # falsly report the parameters of functions. ## my $ln = $line; ## while ($ln =~ s/\([^\(\)]*\)//g) { ## } ## if ($ln =~ /,/) { ## WARN("MULTIPLE_DECLARATION", ## "declaring multiple variables together should be avoided\n" . $herecurr); ## } ## } #need space before brace following if, while, etc if (($line =~ /\(.*\)\{/ && $line !~ /\($Type\)\{/) || $line =~ /do\{/) { if (ERROR("SPACING", "space required before the open brace '{'\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/^(\+.*(?:do|\)))\{/$1 {/; } } ## # check for blank lines before declarations ## if ($line =~ /^.\t+$Type\s+$Ident(?:\s*=.*)?;/ && ## $prevrawline =~ /^.\s*$/) { ## WARN("SPACING", ## "No blank lines before declarations\n" . $hereprev); ## } ## # closing brace should have a space following it when it has anything # on the line if ($line =~ /}(?!(?:,|;|\)))\S/) { if (ERROR("SPACING", "space required after that close brace '}'\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/}((?!(?:,|;|\)))\S)/} $1/; } } # check spacing on square brackets if ($line =~ /\[\s/ && $line !~ /\[\s*$/) { if (ERROR("SPACING", "space prohibited after that open square bracket '['\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\[\s+/\[/; } } if ($line =~ /\s\]/) { if (ERROR("SPACING", "space prohibited before that close square bracket ']'\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\s+\]/\]/; } } # check spacing on parentheses if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ && $line !~ /for\s*\(\s+;/) { if (ERROR("SPACING", "space prohibited after that open parenthesis '('\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\(\s+/\(/; } } if ($line =~ /(\s+)\)/ && $line !~ /^.\s*\)/ && $line !~ /for\s*\(.*;\s+\)/ && $line !~ /:\s+\)/) { if (ERROR("SPACING", "space prohibited before that close parenthesis ')'\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\s+\)/\)/; } } # check unnecessary parentheses around addressof/dereference single $Lvals # ie: &(foo->bar) should be &foo->bar and *(foo->bar) should be *foo->bar while ($line =~ /(?:[^&]&\s*|\*)\(\s*($Ident\s*(?:$Member\s*)+)\s*\)/g) { my $var = $1; if (CHK("UNNECESSARY_PARENTHESES", "Unnecessary parentheses around $var\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\(\s*\Q$var\E\s*\)/$var/; } } # check for unnecessary parentheses around function pointer uses # ie: (foo->bar)(); should be foo->bar(); # but not "if (foo->bar) (" to avoid some false positives if ($line =~ /(\bif\s*|)(\(\s*$Ident\s*(?:$Member\s*)+\))[ \t]*\(/ && $1 !~ /^if/) { my $var = $2; if (CHK("UNNECESSARY_PARENTHESES", "Unnecessary parentheses around function pointer $var\n" . $herecurr) && $fix) { my $var2 = deparenthesize($var); $var2 =~ s/\s//g; $fixed[$fixlinenr] =~ s/\Q$var\E/$var2/; } } # check for unnecessary parentheses around comparisons in if uses if ($^V && $^V ge 5.10.0 && defined($stat) && $stat =~ /(^.\s*if\s*($balanced_parens))/) { my $if_stat = $1; my $test = substr($2, 1, -1); my $herectx; while ($test =~ /(?:^|[^\w\&\!\~])+\s*\(\s*([\&\!\~]?\s*$Lval\s*(?:$Compare\s*$FuncArg)?)\s*\)/g) { my $match = $1; # avoid parentheses around potential macro args next if ($match =~ /^\s*\w+\s*$/); if (!defined($herectx)) { $herectx = $here . "\n"; my $cnt = statement_rawlines($if_stat); for (my $n = 0; $n < $cnt; $n++) { my $rl = raw_line($linenr, $n); $herectx .= $rl . "\n"; last if $rl =~ /^[ \+].*\{/; } } CHK("UNNECESSARY_PARENTHESES", "Unnecessary parentheses around '$match'\n" . $herectx); } } #goto labels aren't indented, allow a single space however if ($line=~/^.\s+[A-Za-z\d_]+:(?![0-9]+)/ and !($line=~/^. [A-Za-z\d_]+:/) and !($line=~/^.\s+default:/)) { if (WARN("INDENTED_LABEL", "labels should not be indented\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/^(.)\s+/$1/; } } # return is not a function if (defined($stat) && $stat =~ /^.\s*return(\s*)\(/s) { my $spacing = $1; if ($^V && $^V ge 5.10.0 && $stat =~ /^.\s*return\s*($balanced_parens)\s*;\s*$/) { my $value = $1; $value = deparenthesize($value); if ($value =~ m/^\s*$FuncArg\s*(?:\?|$)/) { ERROR("RETURN_PARENTHESES", "return is not a function, parentheses are not required\n" . $herecurr); } } elsif ($spacing !~ /\s+/) { ERROR("SPACING", "space required before the open parenthesis '('\n" . $herecurr); } } # unnecessary return in a void function # at end-of-function, with the previous line a single leading tab, then return; # and the line before that not a goto label target like "out:" if ($sline =~ /^[ \+]}\s*$/ && $prevline =~ /^\+\treturn\s*;\s*$/ && $linenr >= 3 && $lines[$linenr - 3] =~ /^[ +]/ && $lines[$linenr - 3] !~ /^[ +]\s*$Ident\s*:/) { WARN("RETURN_VOID", "void function return statements are not generally useful\n" . $hereprev); } # if statements using unnecessary parentheses - ie: if ((foo == bar)) if ($^V && $^V ge 5.10.0 && $line =~ /\bif\s*((?:\(\s*){2,})/) { my $openparens = $1; my $count = $openparens =~ tr@\(@\(@; my $msg = ""; if ($line =~ /\bif\s*(?:\(\s*){$count,$count}$LvalOrFunc\s*($Compare)\s*$LvalOrFunc(?:\s*\)){$count,$count}/) { my $comp = $4; #Not $1 because of $LvalOrFunc $msg = " - maybe == should be = ?" if ($comp eq "=="); WARN("UNNECESSARY_PARENTHESES", "Unnecessary parentheses$msg\n" . $herecurr); } } # comparisons with a constant or upper case identifier on the left # avoid cases like "foo + BAR < baz" # only fix matches surrounded by parentheses to avoid incorrect # conversions like "FOO < baz() + 5" being "misfixed" to "baz() > FOO + 5" if ($^V && $^V ge 5.10.0 && $line =~ /^\+(.*)\b($Constant|[A-Z_][A-Z0-9_]*)\s*($Compare)\s*($LvalOrFunc)/) { my $lead = $1; my $const = $2; my $comp = $3; my $to = $4; my $newcomp = $comp; if ($lead !~ /(?:$Operators|\.)\s*$/ && $to !~ /^(?:Constant|[A-Z_][A-Z0-9_]*)$/ && WARN("CONSTANT_COMPARISON", "Comparisons should place the constant on the right side of the test\n" . $herecurr) && $fix) { if ($comp eq "<") { $newcomp = ">"; } elsif ($comp eq "<=") { $newcomp = ">="; } elsif ($comp eq ">") { $newcomp = "<"; } elsif ($comp eq ">=") { $newcomp = "<="; } $fixed[$fixlinenr] =~ s/\(\s*\Q$const\E\s*$Compare\s*\Q$to\E\s*\)/($to $newcomp $const)/; } } # Return of what appears to be an errno should normally be negative if ($sline =~ /\breturn(?:\s*\(+\s*|\s+)(E[A-Z]+)(?:\s*\)+\s*|\s*)[;:,]/) { my $name = $1; if ($name ne 'EOF' && $name ne 'ERROR') { WARN("USE_NEGATIVE_ERRNO", "return of an errno should typically be negative (ie: return -$1)\n" . $herecurr); } } # Need a space before open parenthesis after if, while etc if ($line =~ /\b(if|while|for|switch)\(/) { if (ERROR("SPACING", "space required before the open parenthesis '('\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\b(if|while|for|switch)\(/$1 \(/; } } # Check for illegal assignment in if conditional -- and check for trailing # statements after the conditional. if ($line =~ /do\s*(?!{)/) { ($stat, $cond, $line_nr_next, $remain_next, $off_next) = ctx_statement_block($linenr, $realcnt, 0) if (!defined $stat); my ($stat_next) = ctx_statement_block($line_nr_next, $remain_next, $off_next); $stat_next =~ s/\n./\n /g; ##print "stat<$stat> stat_next<$stat_next>\n"; if ($stat_next =~ /^\s*while\b/) { # If the statement carries leading newlines, # then count those as offsets. my ($whitespace) = ($stat_next =~ /^((?:\s*\n[+-])*\s*)/s); my $offset = statement_rawlines($whitespace) - 1; $suppress_whiletrailers{$line_nr_next + $offset} = 1; } } if (!defined $suppress_whiletrailers{$linenr} && defined($stat) && defined($cond) && $line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) { my ($s, $c) = ($stat, $cond); if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) { ERROR("ASSIGN_IN_IF", "do not use assignment in if condition\n" . $herecurr); } # Find out what is on the end of the line after the # conditional. substr($s, 0, length($c), ''); $s =~ s/\n.*//g; $s =~ s/$;//g; # Remove any comments if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ && $c !~ /}\s*while\s*/) { # Find out how long the conditional actually is. my @newlines = ($c =~ /\n/gs); my $cond_lines = 1 + $#newlines; my $stat_real = ''; $stat_real = raw_line($linenr, $cond_lines) . "\n" if ($cond_lines); if (defined($stat_real) && $cond_lines > 1) { $stat_real = "[...]\n$stat_real"; } ERROR("TRAILING_STATEMENTS", "trailing statements should be on next line\n" . $herecurr . $stat_real); } } # Check for bitwise tests written as boolean if ($line =~ / (?: (?:\[|\(|\&\&|\|\|) \s*0[xX][0-9]+\s* (?:\&\&|\|\|) | (?:\&\&|\|\|) \s*0[xX][0-9]+\s* (?:\&\&|\|\||\)|\]) )/x) { WARN("HEXADECIMAL_BOOLEAN_TEST", "boolean test with hexadecimal, perhaps just 1 \& or \|?\n" . $herecurr); } # if and else should not have general statements after it if ($line =~ /^.\s*(?:}\s*)?else\b(.*)/) { my $s = $1; $s =~ s/$;//g; # Remove any comments if ($s !~ /^\s*(?:\sif|(?:{|)\s*\\?\s*$)/) { ERROR("TRAILING_STATEMENTS", "trailing statements should be on next line\n" . $herecurr); } } # if should not continue a brace if ($line =~ /}\s*if\b/) { ERROR("TRAILING_STATEMENTS", "trailing statements should be on next line (or did you mean 'else if'?)\n" . $herecurr); } # case and default should not have general statements after them if ($line =~ /^.\s*(?:case\s*.*|default\s*):/g && $line !~ /\G(?: (?:\s*$;*)(?:\s*{)?(?:\s*$;*)(?:\s*\\)?\s*$| \s*return\s+ )/xg) { ERROR("TRAILING_STATEMENTS", "trailing statements should be on next line\n" . $herecurr); } # Check for }<nl>else {, these must be at the same # indent level to be relevant to each other. if ($prevline=~/}\s*$/ and $line=~/^.\s*else\s*/ && $previndent == $indent) { if (ERROR("ELSE_AFTER_BRACE", "else should follow close brace '}'\n" . $hereprev) && $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { fix_delete_line($fixlinenr - 1, $prevrawline); fix_delete_line($fixlinenr, $rawline); my $fixedline = $prevrawline; $fixedline =~ s/}\s*$//; if ($fixedline !~ /^\+\s*$/) { fix_insert_line($fixlinenr, $fixedline); } $fixedline = $rawline; $fixedline =~ s/^(.\s*)else/$1} else/; fix_insert_line($fixlinenr, $fixedline); } } if ($prevline=~/}\s*$/ and $line=~/^.\s*while\s*/ && $previndent == $indent) { my ($s, $c) = ctx_statement_block($linenr, $realcnt, 0); # Find out what is on the end of the line after the # conditional. substr($s, 0, length($c), ''); $s =~ s/\n.*//g; if ($s =~ /^\s*;/) { if (ERROR("WHILE_AFTER_BRACE", "while should follow close brace '}'\n" . $hereprev) && $fix && $prevline =~ /^\+/ && $line =~ /^\+/) { fix_delete_line($fixlinenr - 1, $prevrawline); fix_delete_line($fixlinenr, $rawline); my $fixedline = $prevrawline; my $trailing = $rawline; $trailing =~ s/^\+//; $trailing = trim($trailing); $fixedline =~ s/}\s*$/} $trailing/; fix_insert_line($fixlinenr, $fixedline); } } } #Specific variable tests while ($line =~ m{($Constant|$Lval)}g) { my $var = $1; #gcc binary extension if ($var =~ /^$Binary$/) { if (WARN("GCC_BINARY_CONSTANT", "Avoid gcc v4.3+ binary constant extension: <$var>\n" . $herecurr) && $fix) { my $hexval = sprintf("0x%x", oct($var)); $fixed[$fixlinenr] =~ s/\b$var\b/$hexval/; } } #CamelCase if ($var !~ /^$Constant$/ && $var =~ /[A-Z][a-z]|[a-z][A-Z]/ && #Ignore Page<foo> variants $var !~ /^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ && #Ignore SI style variants like nS, mV and dB (ie: max_uV, regulator_min_uA_show) $var !~ /^(?:[a-z_]*?)_?[a-z][A-Z](?:_[a-z_]+)?$/ && #Ignore some three character SI units explicitly, like MiB and KHz $var !~ /^(?:[a-z_]*?)_?(?:[KMGT]iB|[KMGT]?Hz)(?:_[a-z_]+)?$/) { while ($var =~ m{($Ident)}g) { my $word = $1; next if ($word !~ /[A-Z][a-z]|[a-z][A-Z]/); if ($check) { seed_camelcase_includes(); if (!$file && !$camelcase_file_seeded) { seed_camelcase_file($realfile); $camelcase_file_seeded = 1; } } if (!defined $camelcase{$word}) { $camelcase{$word} = 1; CHK("CAMELCASE", "Avoid CamelCase: <$word>\n" . $herecurr); } } } } #no spaces allowed after \ in define if ($line =~ /\#\s*define.*\\\s+$/) { if (WARN("WHITESPACE_AFTER_LINE_CONTINUATION", "Whitespace after \\ makes next lines useless\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\s+$//; } } # warn if <asm/foo.h> is #included and <linux/foo.h> is available and includes # itself <asm/foo.h> (uses RAW line) if ($tree && $rawline =~ m{^.\s*\#\s*include\s*\<asm\/(.*)\.h\>}) { my $file = "$1.h"; my $checkfile = "include/linux/$file"; if (-f "$root/$checkfile" && $realfile ne $checkfile && $1 !~ /$allowed_asm_includes/) { my $asminclude = `grep -Ec "#include\\s+<asm/$file>" $root/$checkfile`; if ($asminclude > 0) { if ($realfile =~ m{^arch/}) { CHK("ARCH_INCLUDE_LINUX", "Consider using #include <linux/$file> instead of <asm/$file>\n" . $herecurr); } else { WARN("INCLUDE_LINUX", "Use #include <linux/$file> instead of <asm/$file>\n" . $herecurr); } } } } # multi-statement macros should be enclosed in a do while loop, grab the # first statement and ensure its the whole macro if its not enclosed # in a known good container if ($realfile !~ m@/vmlinux.lds.h$@ && $line =~ /^.\s*\#\s*define\s*$Ident(\()?/) { my $ln = $linenr; my $cnt = $realcnt; my ($off, $dstat, $dcond, $rest); my $ctx = ''; my $has_flow_statement = 0; my $has_arg_concat = 0; ($dstat, $dcond, $ln, $cnt, $off) = ctx_statement_block($linenr, $realcnt, 0); $ctx = $dstat; #print "dstat<$dstat> dcond<$dcond> cnt<$cnt> off<$off>\n"; #print "LINE<$lines[$ln-1]> len<" . length($lines[$ln-1]) . "\n"; $has_flow_statement = 1 if ($ctx =~ /\b(goto|return)\b/); $has_arg_concat = 1 if ($ctx =~ /\#\#/ && $ctx !~ /\#\#\s*(?:__VA_ARGS__|args)\b/); $dstat =~ s/^.\s*\#\s*define\s+$Ident(\([^\)]*\))?\s*//; my $define_args = $1; my $define_stmt = $dstat; my @def_args = (); if (defined $define_args && $define_args ne "") { $define_args = substr($define_args, 1, length($define_args) - 2); $define_args =~ s/\s*//g; @def_args = split(",", $define_args); } $dstat =~ s/$;//g; $dstat =~ s/\\\n.//g; $dstat =~ s/^\s*//s; $dstat =~ s/\s*$//s; # Flatten any parentheses and braces while ($dstat =~ s/\([^\(\)]*\)/1/ || $dstat =~ s/\{[^\{\}]*\}/1/ || $dstat =~ s/.\[[^\[\]]*\]/1/) { } # Flatten any obvious string concatentation. while ($dstat =~ s/($String)\s*$Ident/$1/ || $dstat =~ s/$Ident\s*($String)/$1/) { } # Make asm volatile uses seem like a generic function $dstat =~ s/\b_*asm_*\s+_*volatile_*\b/asm_volatile/g; my $exceptions = qr{ $Declare| module_param_named| MODULE_PARM_DESC| DECLARE_PER_CPU| DEFINE_PER_CPU| __typeof__\(| union| struct| \.$Ident\s*=\s*| ^\"|\"$| ^\[ }x; #print "REST<$rest> dstat<$dstat> ctx<$ctx>\n"; $ctx =~ s/\n*$//; my $herectx = $here . "\n"; my $stmt_cnt = statement_rawlines($ctx); for (my $n = 0; $n < $stmt_cnt; $n++) { $herectx .= raw_line($linenr, $n) . "\n"; } if ($dstat ne '' && $dstat !~ /^(?:$Ident|-?$Constant),$/ && # 10, // foo(), $dstat !~ /^(?:$Ident|-?$Constant);$/ && # foo(); $dstat !~ /^[!~-]?(?:$Lval|$Constant)$/ && # 10 // foo() // !foo // ~foo // -foo // foo->bar // foo.bar->baz $dstat !~ /^'X'$/ && $dstat !~ /^'XX'$/ && # character constants $dstat !~ /$exceptions/ && $dstat !~ /^\.$Ident\s*=/ && # .foo = $dstat !~ /^(?:\#\s*$Ident|\#\s*$Constant)\s*$/ && # stringification #foo $dstat !~ /^do\s*$Constant\s*while\s*$Constant;?$/ && # do {...} while (...); // do {...} while (...) $dstat !~ /^for\s*$Constant$/ && # for (...) $dstat !~ /^for\s*$Constant\s+(?:$Ident|-?$Constant)$/ && # for (...) bar() $dstat !~ /^do\s*{/ && # do {... $dstat !~ /^\(\{/ && # ({... $ctx !~ /^.\s*#\s*define\s+TRACE_(?:SYSTEM|INCLUDE_FILE|INCLUDE_PATH)\b/) { if ($dstat =~ /^\s*if\b/) { ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE", "Macros starting with if should be enclosed by a do - while loop to avoid possible if/else logic defects\n" . "$herectx"); } elsif ($dstat =~ /;/) { ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE", "Macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx"); } else { ERROR("COMPLEX_MACRO", "Macros with complex values should be enclosed in parentheses\n" . "$herectx"); } } # Make $define_stmt single line, comment-free, etc my @stmt_array = split('\n', $define_stmt); my $first = 1; $define_stmt = ""; foreach my $l (@stmt_array) { $l =~ s/\\$//; if ($first) { $define_stmt = $l; $first = 0; } elsif ($l =~ /^[\+ ]/) { $define_stmt .= substr($l, 1); } } $define_stmt =~ s/$;//g; $define_stmt =~ s/\s+/ /g; $define_stmt = trim($define_stmt); # check if any macro arguments are reused (ignore '...' and 'type') foreach my $arg (@def_args) { next if ($arg =~ /\.\.\./); next if ($arg =~ /^type$/i); my $tmp_stmt = $define_stmt; $tmp_stmt =~ s/\b(typeof|__typeof__|__builtin\w+|typecheck\s*\(\s*$Type\s*,|\#+)\s*\(*\s*$arg\s*\)*\b//g; $tmp_stmt =~ s/\#+\s*$arg\b//g; $tmp_stmt =~ s/\b$arg\s*\#\#//g; my $use_cnt = $tmp_stmt =~ s/\b$arg\b//g; if ($use_cnt > 1) { CHK("MACRO_ARG_REUSE", "Macro argument reuse '$arg' - possible side-effects?\n" . "$herectx"); } # check if any macro arguments may have other precedence issues if ($tmp_stmt =~ m/($Operators)?\s*\b$arg\b\s*($Operators)?/m && ((defined($1) && $1 ne ',') || (defined($2) && $2 ne ','))) { CHK("MACRO_ARG_PRECEDENCE", "Macro argument '$arg' may be better as '($arg)' to avoid precedence issues\n" . "$herectx"); } } # check for macros with flow control, but without ## concatenation # ## concatenation is commonly a macro that defines a function so ignore those if ($has_flow_statement && !$has_arg_concat) { my $herectx = $here . "\n"; my $cnt = statement_rawlines($ctx); for (my $n = 0; $n < $cnt; $n++) { $herectx .= raw_line($linenr, $n) . "\n"; } WARN("MACRO_WITH_FLOW_CONTROL", "Macros with flow control statements should be avoided\n" . "$herectx"); } # check for line continuations outside of #defines, preprocessor #, and asm } else { if ($prevline !~ /^..*\\$/ && $line !~ /^\+\s*\#.*\\$/ && # preprocessor $line !~ /^\+.*\b(__asm__|asm)\b.*\\$/ && # asm $line =~ /^\+.*\\$/) { WARN("LINE_CONTINUATIONS", "Avoid unnecessary line continuations\n" . $herecurr); } } # do {} while (0) macro tests: # single-statement macros do not need to be enclosed in do while (0) loop, # macro should not end with a semicolon if ($^V && $^V ge 5.10.0 && $realfile !~ m@/vmlinux.lds.h$@ && $line =~ /^.\s*\#\s*define\s+$Ident(\()?/) { my $ln = $linenr; my $cnt = $realcnt; my ($off, $dstat, $dcond, $rest); my $ctx = ''; ($dstat, $dcond, $ln, $cnt, $off) = ctx_statement_block($linenr, $realcnt, 0); $ctx = $dstat; $dstat =~ s/\\\n.//g; $dstat =~ s/$;/ /g; if ($dstat =~ /^\+\s*#\s*define\s+$Ident\s*${balanced_parens}\s*do\s*{(.*)\s*}\s*while\s*\(\s*0\s*\)\s*([;\s]*)\s*$/) { my $stmts = $2; my $semis = $3; $ctx =~ s/\n*$//; my $cnt = statement_rawlines($ctx); my $herectx = $here . "\n"; for (my $n = 0; $n < $cnt; $n++) { $herectx .= raw_line($linenr, $n) . "\n"; } if (($stmts =~ tr/;/;/) == 1 && $stmts !~ /^\s*(if|while|for|switch)\b/) { WARN("SINGLE_STATEMENT_DO_WHILE_MACRO", "Single statement macros should not use a do {} while (0) loop\n" . "$herectx"); } if (defined $semis && $semis ne "") { WARN("DO_WHILE_MACRO_WITH_TRAILING_SEMICOLON", "do {} while (0) macros should not be semicolon terminated\n" . "$herectx"); } } elsif ($dstat =~ /^\+\s*#\s*define\s+$Ident.*;\s*$/) { $ctx =~ s/\n*$//; my $cnt = statement_rawlines($ctx); my $herectx = $here . "\n"; for (my $n = 0; $n < $cnt; $n++) { $herectx .= raw_line($linenr, $n) . "\n"; } WARN("TRAILING_SEMICOLON", "macros should not use a trailing semicolon\n" . "$herectx"); } } # make sure symbols are always wrapped with VMLINUX_SYMBOL() ... # all assignments may have only one of the following with an assignment: # . # ALIGN(...) # VMLINUX_SYMBOL(...) if ($realfile eq 'vmlinux.lds.h' && $line =~ /(?:(?:^|\s)$Ident\s*=|=\s*$Ident(?:\s|$))/) { WARN("MISSING_VMLINUX_SYMBOL", "vmlinux.lds.h needs VMLINUX_SYMBOL() around C-visible symbols\n" . $herecurr); } # check for redundant bracing round if etc if ($line =~ /(^.*)\bif\b/ && $1 !~ /else\s*$/) { my ($level, $endln, @chunks) = ctx_statement_full($linenr, $realcnt, 1); #print "chunks<$#chunks> linenr<$linenr> endln<$endln> level<$level>\n"; #print "APW: <<$chunks[1][0]>><<$chunks[1][1]>>\n"; if ($#chunks > 0 && $level == 0) { my @allowed = (); my $allow = 0; my $seen = 0; my $herectx = $here . "\n"; my $ln = $linenr - 1; for my $chunk (@chunks) { my ($cond, $block) = @{$chunk}; # If the condition carries leading newlines, then count those as offsets. my ($whitespace) = ($cond =~ /^((?:\s*\n[+-])*\s*)/s); my $offset = statement_rawlines($whitespace) - 1; $allowed[$allow] = 0; #print "COND<$cond> whitespace<$whitespace> offset<$offset>\n"; # We have looked at and allowed this specific line. $suppress_ifbraces{$ln + $offset} = 1; $herectx .= "$rawlines[$ln + $offset]\n[...]\n"; $ln += statement_rawlines($block) - 1; substr($block, 0, length($cond), ''); $seen++ if ($block =~ /^\s*{/); #print "cond<$cond> block<$block> allowed<$allowed[$allow]>\n"; if (statement_lines($cond) > 1) { #print "APW: ALLOWED: cond<$cond>\n"; $allowed[$allow] = 1; } if ($block =~/\b(?:if|for|while)\b/) { #print "APW: ALLOWED: block<$block>\n"; $allowed[$allow] = 1; } if (statement_block_size($block) > 1) { #print "APW: ALLOWED: lines block<$block>\n"; $allowed[$allow] = 1; } $allow++; } if ($seen) { my $sum_allowed = 0; foreach (@allowed) { $sum_allowed += $_; } if ($sum_allowed == 0) { WARN("BRACES", "braces {} are not necessary for any arm of this statement\n" . $herectx); } elsif ($sum_allowed != $allow && $seen != $allow) { CHK("BRACES", "braces {} should be used on all arms of this statement\n" . $herectx); } } } } if (!defined $suppress_ifbraces{$linenr - 1} && $line =~ /\b(if|while|for|else)\b/) { my $allowed = 0; # Check the pre-context. if (substr($line, 0, $-[0]) =~ /(\}\s*)$/) { #print "APW: ALLOWED: pre<$1>\n"; $allowed = 1; } my ($level, $endln, @chunks) = ctx_statement_full($linenr, $realcnt, $-[0]); # Check the condition. my ($cond, $block) = @{$chunks[0]}; #print "CHECKING<$linenr> cond<$cond> block<$block>\n"; if (defined $cond) { substr($block, 0, length($cond), ''); } if (statement_lines($cond) > 1) { #print "APW: ALLOWED: cond<$cond>\n"; $allowed = 1; } if ($block =~/\b(?:if|for|while)\b/) { #print "APW: ALLOWED: block<$block>\n"; $allowed = 1; } if (statement_block_size($block) > 1) { #print "APW: ALLOWED: lines block<$block>\n"; $allowed = 1; } # Check the post-context. if (defined $chunks[1]) { my ($cond, $block) = @{$chunks[1]}; if (defined $cond) { substr($block, 0, length($cond), ''); } if ($block =~ /^\s*\{/) { #print "APW: ALLOWED: chunk-1 block<$block>\n"; $allowed = 1; } } if ($level == 0 && $block =~ /^\s*\{/ && !$allowed) { my $herectx = $here . "\n"; my $cnt = statement_rawlines($block); for (my $n = 0; $n < $cnt; $n++) { $herectx .= raw_line($linenr, $n) . "\n"; } WARN("BRACES", "braces {} are not necessary for single statement blocks\n" . $herectx); } } # check for single line unbalanced braces if ($sline =~ /^.\s*\}\s*else\s*$/ || $sline =~ /^.\s*else\s*\{\s*$/) { CHK("BRACES", "Unbalanced braces around else statement\n" . $herecurr); } # check for unnecessary blank lines around braces if (($line =~ /^.\s*}\s*$/ && $prevrawline =~ /^.\s*$/)) { if (CHK("BRACES", "Blank lines aren't necessary before a close brace '}'\n" . $hereprev) && $fix && $prevrawline =~ /^\+/) { fix_delete_line($fixlinenr - 1, $prevrawline); } } if (($rawline =~ /^.\s*$/ && $prevline =~ /^..*{\s*$/)) { if (CHK("BRACES", "Blank lines aren't necessary after an open brace '{'\n" . $hereprev) && $fix) { fix_delete_line($fixlinenr, $rawline); } } # no volatiles please my $asm_volatile = qr{\b(__asm__|asm)\s+(__volatile__|volatile)\b}; if ($line =~ /\bvolatile\b/ && $line !~ /$asm_volatile/) { WARN("VOLATILE", "Use of volatile is usually wrong: see Documentation/process/volatile-considered-harmful.rst\n" . $herecurr); } # Check for user-visible strings broken across lines, which breaks the ability # to grep for the string. Make exceptions when the previous string ends in a # newline (multiple lines in one string constant) or '\t', '\r', ';', or '{' # (common in inline assembly) or is a octal \123 or hexadecimal \xaf value if ($line =~ /^\+\s*$String/ && $prevline =~ /"\s*$/ && $prevrawline !~ /(?:\\(?:[ntr]|[0-7]{1,3}|x[0-9a-fA-F]{1,2})|;\s*|\{\s*)"\s*$/) { if (WARN("SPLIT_STRING", "quoted string split across lines\n" . $hereprev) && $fix && $prevrawline =~ /^\+.*"\s*$/ && $last_coalesced_string_linenr != $linenr - 1) { my $extracted_string = get_quoted_string($line, $rawline); my $comma_close = ""; if ($rawline =~ /\Q$extracted_string\E(\s*\)\s*;\s*$|\s*,\s*)/) { $comma_close = $1; } fix_delete_line($fixlinenr - 1, $prevrawline); fix_delete_line($fixlinenr, $rawline); my $fixedline = $prevrawline; $fixedline =~ s/"\s*$//; $fixedline .= substr($extracted_string, 1) . trim($comma_close); fix_insert_line($fixlinenr - 1, $fixedline); $fixedline = $rawline; $fixedline =~ s/\Q$extracted_string\E\Q$comma_close\E//; if ($fixedline !~ /\+\s*$/) { fix_insert_line($fixlinenr, $fixedline); } $last_coalesced_string_linenr = $linenr; } } # check for missing a space in a string concatenation if ($prevrawline =~ /[^\\]\w"$/ && $rawline =~ /^\+[\t ]+"\w/) { WARN('MISSING_SPACE', "break quoted strings at a space character\n" . $hereprev); } # check for an embedded function name in a string when the function is known # This does not work very well for -f --file checking as it depends on patch # context providing the function name or a single line form for in-file # function declarations if ($line =~ /^\+.*$String/ && defined($context_function) && get_quoted_string($line, $rawline) =~ /\b$context_function\b/ && length(get_quoted_string($line, $rawline)) != (length($context_function) + 2)) { WARN("EMBEDDED_FUNCTION_NAME", "Prefer using '\"%s...\", __func__' to using '$context_function', this function's name, in a string\n" . $herecurr); } # check for spaces before a quoted newline if ($rawline =~ /^.*\".*\s\\n/) { if (WARN("QUOTED_WHITESPACE_BEFORE_NEWLINE", "unnecessary whitespace before a quoted newline\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/^(\+.*\".*)\s+\\n/$1\\n/; } } # concatenated string without spaces between elements if ($line =~ /$String[A-Z_]/ || $line =~ /[A-Za-z0-9_]$String/) { CHK("CONCATENATED_STRING", "Concatenated strings should use spaces between elements\n" . $herecurr); } # uncoalesced string fragments if ($line =~ /$String\s*"/) { WARN("STRING_FRAGMENTS", "Consecutive strings are generally better as a single string\n" . $herecurr); } # check for non-standard and hex prefixed decimal printf formats my $show_L = 1; #don't show the same defect twice my $show_Z = 1; while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) { my $string = substr($rawline, $-[1], $+[1] - $-[1]); $string =~ s/%%/__/g; # check for %L if ($show_L && $string =~ /%[\*\d\.\$]*L([diouxX])/) { WARN("PRINTF_L", "\%L$1 is non-standard C, use %ll$1\n" . $herecurr); $show_L = 0; } # check for %Z if ($show_Z && $string =~ /%[\*\d\.\$]*Z([diouxX])/) { WARN("PRINTF_Z", "%Z$1 is non-standard C, use %z$1\n" . $herecurr); $show_Z = 0; } # check for 0x<decimal> if ($string =~ /0x%[\*\d\.\$\Llzth]*[diou]/) { ERROR("PRINTF_0XDECIMAL", "Prefixing 0x with decimal output is defective\n" . $herecurr); } } # check for line continuations in quoted strings with odd counts of " if ($rawline =~ /\\$/ && $rawline =~ tr/"/"/ % 2) { WARN("LINE_CONTINUATIONS", "Avoid line continuations in quoted strings\n" . $herecurr); } # warn about #if 0 if ($line =~ /^.\s*\#\s*if\s+0\b/) { CHK("REDUNDANT_CODE", "if this code is redundant consider removing it\n" . $herecurr); } # check for needless "if (<foo>) fn(<foo>)" uses if ($prevline =~ /\bif\s*\(\s*($Lval)\s*\)/) { my $tested = quotemeta($1); my $expr = '\s*\(\s*' . $tested . '\s*\)\s*;'; if ($line =~ /\b(kfree|usb_free_urb|debugfs_remove(?:_recursive)?|(?:kmem_cache|mempool|dma_pool)_destroy)$expr/) { my $func = $1; if (WARN('NEEDLESS_IF', "$func(NULL) is safe and this check is probably not required\n" . $hereprev) && $fix) { my $do_fix = 1; my $leading_tabs = ""; my $new_leading_tabs = ""; if ($lines[$linenr - 2] =~ /^\+(\t*)if\s*\(\s*$tested\s*\)\s*$/) { $leading_tabs = $1; } else { $do_fix = 0; } if ($lines[$linenr - 1] =~ /^\+(\t+)$func\s*\(\s*$tested\s*\)\s*;\s*$/) { $new_leading_tabs = $1; if (length($leading_tabs) + 1 ne length($new_leading_tabs)) { $do_fix = 0; } } else { $do_fix = 0; } if ($do_fix) { fix_delete_line($fixlinenr - 1, $prevrawline); $fixed[$fixlinenr] =~ s/^\+$new_leading_tabs/\+$leading_tabs/; } } } } # check for unnecessary "Out of Memory" messages if ($line =~ /^\+.*\b$logFunctions\s*\(/ && $prevline =~ /^[ \+]\s*if\s*\(\s*(\!\s*|NULL\s*==\s*)?($Lval)(\s*==\s*NULL\s*)?\s*\)/ && (defined $1 || defined $3) && $linenr > 3) { my $testval = $2; my $testline = $lines[$linenr - 3]; my ($s, $c) = ctx_statement_block($linenr - 3, $realcnt, 0); # print("line: <$line>\nprevline: <$prevline>\ns: <$s>\nc: <$c>\n\n\n"); if ($s =~ /(?:^|\n)[ \+]\s*(?:$Type\s*)?\Q$testval\E\s*=\s*(?:\([^\)]*\)\s*)?\s*(?:devm_)?(?:[kv][czm]alloc(?:_node|_array)?\b|kstrdup|kmemdup|(?:dev_)?alloc_skb)/) { WARN("OOM_MESSAGE", "Possible unnecessary 'out of memory' message\n" . $hereprev); } } # check for logging functions with KERN_<LEVEL> if ($line !~ /printk(?:_ratelimited|_once)?\s*\(/ && $line =~ /\b$logFunctions\s*\(.*\b(KERN_[A-Z]+)\b/) { my $level = $1; if (WARN("UNNECESSARY_KERN_LEVEL", "Possible unnecessary $level\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\s*$level\s*//; } } # check for logging continuations if ($line =~ /\bprintk\s*\(\s*KERN_CONT\b|\bpr_cont\s*\(/) { WARN("LOGGING_CONTINUATION", "Avoid logging continuation uses where feasible\n" . $herecurr); } # check for mask then right shift without a parentheses if ($^V && $^V ge 5.10.0 && $line =~ /$LvalOrFunc\s*\&\s*($LvalOrFunc)\s*>>/ && $4 !~ /^\&/) { # $LvalOrFunc may be &foo, ignore if so WARN("MASK_THEN_SHIFT", "Possible precedence defect with mask then right shift - may need parentheses\n" . $herecurr); } # check for pointer comparisons to NULL if ($^V && $^V ge 5.10.0) { while ($line =~ /\b$LvalOrFunc\s*(==|\!=)\s*NULL\b/g) { my $val = $1; my $equal = "!"; $equal = "" if ($4 eq "!="); if (CHK("COMPARISON_TO_NULL", "Comparison to NULL could be written \"${equal}${val}\"\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\b\Q$val\E\s*(?:==|\!=)\s*NULL\b/$equal$val/; } } } # check for bad placement of section $InitAttribute (e.g.: __initdata) if ($line =~ /(\b$InitAttribute\b)/) { my $attr = $1; if ($line =~ /^\+\s*static\s+(?:const\s+)?(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*[=;]/) { my $ptr = $1; my $var = $2; if ((($ptr =~ /\b(union|struct)\s+$attr\b/ && ERROR("MISPLACED_INIT", "$attr should be placed after $var\n" . $herecurr)) || ($ptr !~ /\b(union|struct)\s+$attr\b/ && WARN("MISPLACED_INIT", "$attr should be placed after $var\n" . $herecurr))) && $fix) { $fixed[$fixlinenr] =~ s/(\bstatic\s+(?:const\s+)?)(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*([=;])\s*/"$1" . trim(string_find_replace($2, "\\s*$attr\\s*", " ")) . " " . trim(string_find_replace($3, "\\s*$attr\\s*", "")) . " $attr" . ("$4" eq ";" ? ";" : " = ")/e; } } } # check for $InitAttributeData (ie: __initdata) with const if ($line =~ /\bconst\b/ && $line =~ /($InitAttributeData)/) { my $attr = $1; $attr =~ /($InitAttributePrefix)(.*)/; my $attr_prefix = $1; my $attr_type = $2; if (ERROR("INIT_ATTRIBUTE", "Use of const init definition must use ${attr_prefix}initconst\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/$InitAttributeData/${attr_prefix}initconst/; } } # check for $InitAttributeConst (ie: __initconst) without const if ($line !~ /\bconst\b/ && $line =~ /($InitAttributeConst)/) { my $attr = $1; if (ERROR("INIT_ATTRIBUTE", "Use of $attr requires a separate use of const\n" . $herecurr) && $fix) { my $lead = $fixed[$fixlinenr] =~ /(^\+\s*(?:static\s+))/; $lead = rtrim($1); $lead = "$lead " if ($lead !~ /^\+$/); $lead = "${lead}const "; $fixed[$fixlinenr] =~ s/(^\+\s*(?:static\s+))/$lead/; } } # check for __read_mostly with const non-pointer (should just be const) if ($line =~ /\b__read_mostly\b/ && $line =~ /($Type)\s*$Ident/ && $1 !~ /\*\s*$/ && $1 =~ /\bconst\b/) { if (ERROR("CONST_READ_MOSTLY", "Invalid use of __read_mostly with const type\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\s+__read_mostly\b//; } } # don't use __constant_<foo> functions outside of include/uapi/ if ($realfile !~ m@^include/uapi/@ && $line =~ /(__constant_(?:htons|ntohs|[bl]e(?:16|32|64)_to_cpu|cpu_to_[bl]e(?:16|32|64)))\s*\(/) { my $constant_func = $1; my $func = $constant_func; $func =~ s/^__constant_//; if (WARN("CONSTANT_CONVERSION", "$constant_func should be $func\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\b$constant_func\b/$func/g; } } # prefer usleep_range over udelay if ($line =~ /\budelay\s*\(\s*(\d+)\s*\)/) { my $delay = $1; # ignore udelay's < 10, however if (! ($delay < 10) ) { CHK("USLEEP_RANGE", "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt\n" . $herecurr); } if ($delay > 2000) { WARN("LONG_UDELAY", "long udelay - prefer mdelay; see arch/arm/include/asm/delay.h\n" . $herecurr); } } # warn about unexpectedly long msleep's if ($line =~ /\bmsleep\s*\((\d+)\);/) { if ($1 < 20) { WARN("MSLEEP", "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt\n" . $herecurr); } } # check for comparisons of jiffies if ($line =~ /\bjiffies\s*$Compare|$Compare\s*jiffies\b/) { WARN("JIFFIES_COMPARISON", "Comparing jiffies is almost always wrong; prefer time_after, time_before and friends\n" . $herecurr); } # check for comparisons of get_jiffies_64() if ($line =~ /\bget_jiffies_64\s*\(\s*\)\s*$Compare|$Compare\s*get_jiffies_64\s*\(\s*\)/) { WARN("JIFFIES_COMPARISON", "Comparing get_jiffies_64() is almost always wrong; prefer time_after64, time_before64 and friends\n" . $herecurr); } # warn about #ifdefs in C files # if ($line =~ /^.\s*\#\s*if(|n)def/ && ($realfile =~ /\.c$/)) { # print "#ifdef in C files should be avoided\n"; # print "$herecurr"; # $clean = 0; # } # warn about spacing in #ifdefs if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) { if (ERROR("SPACING", "exactly one space required after that #$1\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/^(.\s*\#\s*(ifdef|ifndef|elif))\s{2,}/$1 /; } } # check for spinlock_t definitions without a comment. if ($line =~ /^.\s*(struct\s+mutex|spinlock_t)\s+\S+;/ || $line =~ /^.\s*(DEFINE_MUTEX)\s*\(/) { my $which = $1; if (!ctx_has_comment($first_line, $linenr)) { CHK("UNCOMMENTED_DEFINITION", "$1 definition without comment\n" . $herecurr); } } # check for memory barriers without a comment. my $barriers = qr{ mb| rmb| wmb| read_barrier_depends }x; my $barrier_stems = qr{ mb__before_atomic| mb__after_atomic| store_release| load_acquire| store_mb| (?:$barriers) }x; my $all_barriers = qr{ (?:$barriers)| smp_(?:$barrier_stems)| virt_(?:$barrier_stems) }x; if ($line =~ /\b(?:$all_barriers)\s*\(/) { if (!ctx_has_comment($first_line, $linenr)) { WARN("MEMORY_BARRIER", "memory barrier without comment\n" . $herecurr); } } my $underscore_smp_barriers = qr{__smp_(?:$barrier_stems)}x; if ($realfile !~ m@^include/asm-generic/@ && $realfile !~ m@/barrier\.h$@ && $line =~ m/\b(?:$underscore_smp_barriers)\s*\(/ && $line !~ m/^.\s*\#\s*define\s+(?:$underscore_smp_barriers)\s*\(/) { WARN("MEMORY_BARRIER", "__smp memory barriers shouldn't be used outside barrier.h and asm-generic\n" . $herecurr); } # check for waitqueue_active without a comment. if ($line =~ /\bwaitqueue_active\s*\(/) { if (!ctx_has_comment($first_line, $linenr)) { WARN("WAITQUEUE_ACTIVE", "waitqueue_active without comment\n" . $herecurr); } } # check of hardware specific defines if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) { CHK("ARCH_DEFINES", "architecture specific defines should be avoided\n" . $herecurr); } # check that the storage class is not after a type if ($line =~ /\b($Type)\s+($Storage)\b/) { WARN("STORAGE_CLASS", "storage class '$2' should be located before type '$1'\n" . $herecurr); } # Check that the storage class is at the beginning of a declaration if ($line =~ /\b$Storage\b/ && $line !~ /^.\s*$Storage/ && $line =~ /^.\s*(.+?)\$Storage\s/ && $1 !~ /[\,\)]\s*$/) { WARN("STORAGE_CLASS", "storage class should be at the beginning of the declaration\n" . $herecurr); } # check the location of the inline attribute, that it is between # storage class and type. if ($line =~ /\b$Type\s+$Inline\b/ || $line =~ /\b$Inline\s+$Storage\b/) { ERROR("INLINE_LOCATION", "inline keyword should sit between storage class and type\n" . $herecurr); } # Check for __inline__ and __inline, prefer inline if ($realfile !~ m@\binclude/uapi/@ && $line =~ /\b(__inline__|__inline)\b/) { if (WARN("INLINE", "plain inline is preferred over $1\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\b(__inline__|__inline)\b/inline/; } } # Check for __attribute__ packed, prefer __packed if ($realfile !~ m@\binclude/uapi/@ && $line =~ /\b__attribute__\s*\(\s*\(.*\bpacked\b/) { WARN("PREFER_PACKED", "__packed is preferred over __attribute__((packed))\n" . $herecurr); } # Check for __attribute__ aligned, prefer __aligned if ($realfile !~ m@\binclude/uapi/@ && $line =~ /\b__attribute__\s*\(\s*\(.*aligned/) { WARN("PREFER_ALIGNED", "__aligned(size) is preferred over __attribute__((aligned(size)))\n" . $herecurr); } # Check for __attribute__ format(printf, prefer __printf if ($realfile !~ m@\binclude/uapi/@ && $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf/) { if (WARN("PREFER_PRINTF", "__printf(string-index, first-to-check) is preferred over __attribute__((format(printf, string-index, first-to-check)))\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf\s*,\s*(.*)\)\s*\)\s*\)/"__printf(" . trim($1) . ")"/ex; } } # Check for __attribute__ format(scanf, prefer __scanf if ($realfile !~ m@\binclude/uapi/@ && $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\b/) { if (WARN("PREFER_SCANF", "__scanf(string-index, first-to-check) is preferred over __attribute__((format(scanf, string-index, first-to-check)))\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\s*,\s*(.*)\)\s*\)\s*\)/"__scanf(" . trim($1) . ")"/ex; } } # Check for __attribute__ weak, or __weak declarations (may have link issues) if ($^V && $^V ge 5.10.0 && $line =~ /(?:$Declare|$DeclareMisordered)\s*$Ident\s*$balanced_parens\s*(?:$Attribute)?\s*;/ && ($line =~ /\b__attribute__\s*\(\s*\(.*\bweak\b/ || $line =~ /\b__weak\b/)) { ERROR("WEAK_DECLARATION", "Using weak declarations can have unintended link defects\n" . $herecurr); } # check for c99 types like uint8_t used outside of uapi/ and tools/ if ($realfile !~ m@\binclude/uapi/@ && $realfile !~ m@\btools/@ && $line =~ /\b($Declare)\s*$Ident\s*[=;,\[]/) { my $type = $1; if ($type =~ /\b($typeC99Typedefs)\b/) { $type = $1; my $kernel_type = 'u'; $kernel_type = 's' if ($type =~ /^_*[si]/); $type =~ /(\d+)/; $kernel_type .= $1; if (CHK("PREFER_KERNEL_TYPES", "Prefer kernel type '$kernel_type' over '$type'\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\b$type\b/$kernel_type/; } } } # check for cast of C90 native int or longer types constants if ($line =~ /(\(\s*$C90_int_types\s*\)\s*)($Constant)\b/) { my $cast = $1; my $const = $2; if (WARN("TYPECAST_INT_CONSTANT", "Unnecessary typecast of c90 int constant\n" . $herecurr) && $fix) { my $suffix = ""; my $newconst = $const; $newconst =~ s/${Int_type}$//; $suffix .= 'U' if ($cast =~ /\bunsigned\b/); if ($cast =~ /\blong\s+long\b/) { $suffix .= 'LL'; } elsif ($cast =~ /\blong\b/) { $suffix .= 'L'; } $fixed[$fixlinenr] =~ s/\Q$cast\E$const\b/$newconst$suffix/; } } # check for sizeof(&) if ($line =~ /\bsizeof\s*\(\s*\&/) { WARN("SIZEOF_ADDRESS", "sizeof(& should be avoided\n" . $herecurr); } # check for sizeof without parenthesis if ($line =~ /\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/) { if (WARN("SIZEOF_PARENTHESIS", "sizeof $1 should be sizeof($1)\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/"sizeof(" . trim($1) . ")"/ex; } } # check for struct spinlock declarations if ($line =~ /^.\s*\bstruct\s+spinlock\s+\w+\s*;/) { WARN("USE_SPINLOCK_T", "struct spinlock should be spinlock_t\n" . $herecurr); } # check for seq_printf uses that could be seq_puts if ($sline =~ /\bseq_printf\s*\(.*"\s*\)\s*;\s*$/) { my $fmt = get_quoted_string($line, $rawline); $fmt =~ s/%%//g; if ($fmt !~ /%/) { if (WARN("PREFER_SEQ_PUTS", "Prefer seq_puts to seq_printf\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\bseq_printf\b/seq_puts/; } } } # check for vsprintf extension %p<foo> misuses if ($^V && $^V ge 5.10.0 && defined $stat && $stat =~ /^\+(?![^\{]*\{\s*).*\b(\w+)\s*\(.*$String\s*,/s && $1 !~ /^_*volatile_*$/) { my $bad_extension = ""; my $lc = $stat =~ tr@\n@@; $lc = $lc + $linenr; for (my $count = $linenr; $count <= $lc; $count++) { my $fmt = get_quoted_string($lines[$count - 1], raw_line($count, 0)); $fmt =~ s/%%//g; if ($fmt =~ /(\%[\*\d\.]*p(?![\WFfSsBKRraEhMmIiUDdgVCbGNOx]).)/) { $bad_extension = $1; last; } } if ($bad_extension ne "") { my $stat_real = raw_line($linenr, 0); for (my $count = $linenr + 1; $count <= $lc; $count++) { $stat_real = $stat_real . "\n" . raw_line($count, 0); } WARN("VSPRINTF_POINTER_EXTENSION", "Invalid vsprintf pointer extension '$bad_extension'\n" . "$here\n$stat_real\n"); } } # Check for misused memsets if ($^V && $^V ge 5.10.0 && defined $stat && $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*$FuncArg\s*\)/) { my $ms_addr = $2; my $ms_val = $7; my $ms_size = $12; if ($ms_size =~ /^(0x|)0$/i) { ERROR("MEMSET", "memset to 0's uses 0 as the 2nd argument, not the 3rd\n" . "$here\n$stat\n"); } elsif ($ms_size =~ /^(0x|)1$/i) { WARN("MEMSET", "single byte memset is suspicious. Swapped 2nd/3rd argument?\n" . "$here\n$stat\n"); } } # Check for memcpy(foo, bar, ETH_ALEN) that could be ether_addr_copy(foo, bar) # if ($^V && $^V ge 5.10.0 && # defined $stat && # $stat =~ /^\+(?:.*?)\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { # if (WARN("PREFER_ETHER_ADDR_COPY", # "Prefer ether_addr_copy() over memcpy() if the Ethernet addresses are __aligned(2)\n" . "$here\n$stat\n") && # $fix) { # $fixed[$fixlinenr] =~ s/\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/ether_addr_copy($2, $7)/; # } # } # Check for memcmp(foo, bar, ETH_ALEN) that could be ether_addr_equal*(foo, bar) # if ($^V && $^V ge 5.10.0 && # defined $stat && # $stat =~ /^\+(?:.*?)\bmemcmp\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { # WARN("PREFER_ETHER_ADDR_EQUAL", # "Prefer ether_addr_equal() or ether_addr_equal_unaligned() over memcmp()\n" . "$here\n$stat\n") # } # check for memset(foo, 0x0, ETH_ALEN) that could be eth_zero_addr # check for memset(foo, 0xFF, ETH_ALEN) that could be eth_broadcast_addr # if ($^V && $^V ge 5.10.0 && # defined $stat && # $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { # # my $ms_val = $7; # # if ($ms_val =~ /^(?:0x|)0+$/i) { # if (WARN("PREFER_ETH_ZERO_ADDR", # "Prefer eth_zero_addr over memset()\n" . "$here\n$stat\n") && # $fix) { # $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_zero_addr($2)/; # } # } elsif ($ms_val =~ /^(?:0xff|255)$/i) { # if (WARN("PREFER_ETH_BROADCAST_ADDR", # "Prefer eth_broadcast_addr() over memset()\n" . "$here\n$stat\n") && # $fix) { # $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_broadcast_addr($2)/; # } # } # } # typecasts on min/max could be min_t/max_t if ($^V && $^V ge 5.10.0 && defined $stat && $stat =~ /^\+(?:.*?)\b(min|max)\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\)/) { if (defined $2 || defined $7) { my $call = $1; my $cast1 = deparenthesize($2); my $arg1 = $3; my $cast2 = deparenthesize($7); my $arg2 = $8; my $cast; if ($cast1 ne "" && $cast2 ne "" && $cast1 ne $cast2) { $cast = "$cast1 or $cast2"; } elsif ($cast1 ne "") { $cast = $cast1; } else { $cast = $cast2; } WARN("MINMAX", "$call() should probably be ${call}_t($cast, $arg1, $arg2)\n" . "$here\n$stat\n"); } } # check usleep_range arguments if ($^V && $^V ge 5.10.0 && defined $stat && $stat =~ /^\+(?:.*?)\busleep_range\s*\(\s*($FuncArg)\s*,\s*($FuncArg)\s*\)/) { my $min = $1; my $max = $7; if ($min eq $max) { WARN("USLEEP_RANGE", "usleep_range should not use min == max args; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n"); } elsif ($min =~ /^\d+$/ && $max =~ /^\d+$/ && $min > $max) { WARN("USLEEP_RANGE", "usleep_range args reversed, use min then max; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n"); } } # check for naked sscanf if ($^V && $^V ge 5.10.0 && defined $stat && $line =~ /\bsscanf\b/ && ($stat !~ /$Ident\s*=\s*sscanf\s*$balanced_parens/ && $stat !~ /\bsscanf\s*$balanced_parens\s*(?:$Compare)/ && $stat !~ /(?:$Compare)\s*\bsscanf\s*$balanced_parens/)) { my $lc = $stat =~ tr@\n@@; $lc = $lc + $linenr; my $stat_real = raw_line($linenr, 0); for (my $count = $linenr + 1; $count <= $lc; $count++) { $stat_real = $stat_real . "\n" . raw_line($count, 0); } WARN("NAKED_SSCANF", "unchecked sscanf return value\n" . "$here\n$stat_real\n"); } # check for simple sscanf that should be kstrto<foo> if ($^V && $^V ge 5.10.0 && defined $stat && $line =~ /\bsscanf\b/) { my $lc = $stat =~ tr@\n@@; $lc = $lc + $linenr; my $stat_real = raw_line($linenr, 0); for (my $count = $linenr + 1; $count <= $lc; $count++) { $stat_real = $stat_real . "\n" . raw_line($count, 0); } if ($stat_real =~ /\bsscanf\b\s*\(\s*$FuncArg\s*,\s*("[^"]+")/) { my $format = $6; my $count = $format =~ tr@%@%@; if ($count == 1 && $format =~ /^"\%(?i:ll[udxi]|[udxi]ll|ll|[hl]h?[udxi]|[udxi][hl]h?|[hl]h?|[udxi])"$/) { WARN("SSCANF_TO_KSTRTO", "Prefer kstrto<type> to single variable sscanf\n" . "$here\n$stat_real\n"); } } } # check for new externs in .h files. if ($realfile =~ /\.h$/ && $line =~ /^\+\s*(extern\s+)$Type\s*$Ident\s*\(/s) { if (CHK("AVOID_EXTERNS", "extern prototypes should be avoided in .h files\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/(.*)\bextern\b\s*(.*)/$1$2/; } } # check for new externs in .c files. if ($realfile =~ /\.c$/ && defined $stat && $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s) { my $function_name = $1; my $paren_space = $2; my $s = $stat; if (defined $cond) { substr($s, 0, length($cond), ''); } if ($s =~ /^\s*;/ && $function_name ne 'uninitialized_var') { WARN("AVOID_EXTERNS", "externs should be avoided in .c files\n" . $herecurr); } if ($paren_space =~ /\n/) { WARN("FUNCTION_ARGUMENTS", "arguments for function declarations should follow identifier\n" . $herecurr); } } elsif ($realfile =~ /\.c$/ && defined $stat && $stat =~ /^.\s*extern\s+/) { WARN("AVOID_EXTERNS", "externs should be avoided in .c files\n" . $herecurr); } # check for function declarations that have arguments without identifier names if (defined $stat && $stat =~ /^.\s*(?:extern\s+)?$Type\s*(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*\(\s*([^{]+)\s*\)\s*;/s && $1 ne "void") { my $args = trim($1); while ($args =~ m/\s*($Type\s*(?:$Ident|\(\s*\*\s*$Ident?\s*\)\s*$balanced_parens)?)/g) { my $arg = trim($1); if ($arg =~ /^$Type$/ && $arg !~ /enum\s+$Ident$/) { WARN("FUNCTION_ARGUMENTS", "function definition argument '$arg' should also have an identifier name\n" . $herecurr); } } } # check for function definitions if ($^V && $^V ge 5.10.0 && defined $stat && $stat =~ /^.\s*(?:$Storage\s+)?$Type\s*($Ident)\s*$balanced_parens\s*{/s) { $context_function = $1; # check for multiline function definition with misplaced open brace my $ok = 0; my $cnt = statement_rawlines($stat); my $herectx = $here . "\n"; for (my $n = 0; $n < $cnt; $n++) { my $rl = raw_line($linenr, $n); $herectx .= $rl . "\n"; $ok = 1 if ($rl =~ /^[ \+]\{/); $ok = 1 if ($rl =~ /\{/ && $n == 0); last if $rl =~ /^[ \+].*\{/; } if (!$ok) { ERROR("OPEN_BRACE", "open brace '{' following function definitions go on the next line\n" . $herectx); } } # checks for new __setup's if ($rawline =~ /\b__setup\("([^"]*)"/) { my $name = $1; if (!grep(/$name/, @setup_docs)) { CHK("UNDOCUMENTED_SETUP", "__setup appears un-documented -- check Documentation/admin-guide/kernel-parameters.rst\n" . $herecurr); } } # check for pointless casting of kmalloc return if ($line =~ /\*\s*\)\s*[kv][czm]alloc(_node){0,1}\b/) { WARN("UNNECESSARY_CASTS", "unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr); } # alloc style # p = alloc(sizeof(struct foo), ...) should be p = alloc(sizeof(*p), ...) if ($^V && $^V ge 5.10.0 && $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*([kv][mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) { CHK("ALLOC_SIZEOF_STRUCT", "Prefer $3(sizeof(*$1)...) over $3($4...)\n" . $herecurr); } # check for k[mz]alloc with multiplies that could be kmalloc_array/kcalloc if ($^V && $^V ge 5.10.0 && defined $stat && $stat =~ /^\+\s*($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*,/) { my $oldfunc = $3; my $a1 = $4; my $a2 = $10; my $newfunc = "kmalloc_array"; $newfunc = "kcalloc" if ($oldfunc eq "kzalloc"); my $r1 = $a1; my $r2 = $a2; if ($a1 =~ /^sizeof\s*\S/) { $r1 = $a2; $r2 = $a1; } if ($r1 !~ /^sizeof\b/ && $r2 =~ /^sizeof\s*\S/ && !($r1 =~ /^$Constant$/ || $r1 =~ /^[A-Z_][A-Z0-9_]*$/)) { my $ctx = ''; my $herectx = $here . "\n"; my $cnt = statement_rawlines($stat); for (my $n = 0; $n < $cnt; $n++) { $herectx .= raw_line($linenr, $n) . "\n"; } if (WARN("ALLOC_WITH_MULTIPLY", "Prefer $newfunc over $oldfunc with multiply\n" . $herectx) && $cnt == 1 && $fix) { $fixed[$fixlinenr] =~ s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1 . ' = ' . "$newfunc(" . trim($r1) . ', ' . trim($r2)/e; } } } # check for krealloc arg reuse if ($^V && $^V ge 5.10.0 && $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*krealloc\s*\(\s*\1\s*,/) { WARN("KREALLOC_ARG_REUSE", "Reusing the krealloc arg is almost always a bug\n" . $herecurr); } # check for alloc argument mismatch if ($line =~ /\b(kcalloc|kmalloc_array)\s*\(\s*sizeof\b/) { WARN("ALLOC_ARRAY_ARGS", "$1 uses number as first arg, sizeof is generally wrong\n" . $herecurr); } # check for multiple semicolons if ($line =~ /;\s*;\s*$/) { if (WARN("ONE_SEMICOLON", "Statements terminations use 1 semicolon\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/(\s*;\s*){2,}$/;/g; } } # check for #defines like: 1 << <digit> that could be BIT(digit), it is not exported to uapi if ($realfile !~ m@^include/uapi/@ && $line =~ /#\s*define\s+\w+\s+\(?\s*1\s*([ulUL]*)\s*\<\<\s*(?:\d+|$Ident)\s*\)?/) { my $ull = ""; $ull = "_ULL" if (defined($1) && $1 =~ /ll/i); if (CHK("BIT_MACRO", "Prefer using the BIT$ull macro\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\(?\s*1\s*[ulUL]*\s*<<\s*(\d+|$Ident)\s*\)?/BIT${ull}($1)/; } } # check for #if defined CONFIG_<FOO> || defined CONFIG_<FOO>_MODULE if ($line =~ /^\+\s*#\s*if\s+defined(?:\s*\(?\s*|\s+)(CONFIG_[A-Z_]+)\s*\)?\s*\|\|\s*defined(?:\s*\(?\s*|\s+)\1_MODULE\s*\)?\s*$/) { my $config = $1; if (WARN("PREFER_IS_ENABLED", "Prefer IS_ENABLED(<FOO>) to CONFIG_<FOO> || CONFIG_<FOO>_MODULE\n" . $herecurr) && $fix) { $fixed[$fixlinenr] = "\+#if IS_ENABLED($config)"; } } # check for case / default statements not preceded by break/fallthrough/switch if ($line =~ /^.\s*(?:case\s+(?:$Ident|$Constant)\s*|default):/) { my $has_break = 0; my $has_statement = 0; my $count = 0; my $prevline = $linenr; while ($prevline > 1 && ($file || $count < 3) && !$has_break) { $prevline--; my $rline = $rawlines[$prevline - 1]; my $fline = $lines[$prevline - 1]; last if ($fline =~ /^\@\@/); next if ($fline =~ /^\-/); next if ($fline =~ /^.(?:\s*(?:case\s+(?:$Ident|$Constant)[\s$;]*|default):[\s$;]*)*$/); $has_break = 1 if ($rline =~ /fall[\s_-]*(through|thru)/i); next if ($fline =~ /^.[\s$;]*$/); $has_statement = 1; $count++; $has_break = 1 if ($fline =~ /\bswitch\b|\b(?:break\s*;[\s$;]*$|exit\s*\(\b|return\b|goto\b|continue\b)/); } if (!$has_break && $has_statement) { WARN("MISSING_BREAK", "Possible switch case/default not preceded by break or fallthrough comment\n" . $herecurr); } } # check for switch/default statements without a break; if ($^V && $^V ge 5.10.0 && defined $stat && $stat =~ /^\+[$;\s]*(?:case[$;\s]+\w+[$;\s]*:[$;\s]*|)*[$;\s]*\bdefault[$;\s]*:[$;\s]*;/g) { my $ctx = ''; my $herectx = $here . "\n"; my $cnt = statement_rawlines($stat); for (my $n = 0; $n < $cnt; $n++) { $herectx .= raw_line($linenr, $n) . "\n"; } WARN("DEFAULT_NO_BREAK", "switch default: should use break\n" . $herectx); } # check for gcc specific __FUNCTION__ if ($line =~ /\b__FUNCTION__\b/) { if (WARN("USE_FUNC", "__func__ should be used instead of gcc specific __FUNCTION__\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\b__FUNCTION__\b/__func__/g; } } # check for uses of __DATE__, __TIME__, __TIMESTAMP__ while ($line =~ /\b(__(?:DATE|TIME|TIMESTAMP)__)\b/g) { ERROR("DATE_TIME", "Use of the '$1' macro makes the build non-deterministic\n" . $herecurr); } # check for use of yield() if ($line =~ /\byield\s*\(\s*\)/) { WARN("YIELD", "Using yield() is generally wrong. See yield() kernel-doc (sched/core.c)\n" . $herecurr); } # check for comparisons against true and false if ($line =~ /\+\s*(.*?)\b(true|false|$Lval)\s*(==|\!=)\s*(true|false|$Lval)\b(.*)$/i) { my $lead = $1; my $arg = $2; my $test = $3; my $otype = $4; my $trail = $5; my $op = "!"; ($arg, $otype) = ($otype, $arg) if ($arg =~ /^(?:true|false)$/i); my $type = lc($otype); if ($type =~ /^(?:true|false)$/) { if (("$test" eq "==" && "$type" eq "true") || ("$test" eq "!=" && "$type" eq "false")) { $op = ""; } CHK("BOOL_COMPARISON", "Using comparison to $otype is error prone\n" . $herecurr); ## maybe suggesting a correct construct would better ## "Using comparison to $otype is error prone. Perhaps use '${lead}${op}${arg}${trail}'\n" . $herecurr); } } # check for semaphores initialized locked if ($line =~ /^.\s*sema_init.+,\W?0\W?\)/) { WARN("CONSIDER_COMPLETION", "consider using a completion\n" . $herecurr); } # recommend kstrto* over simple_strto* and strict_strto* if ($line =~ /\b((simple|strict)_(strto(l|ll|ul|ull)))\s*\(/) { WARN("CONSIDER_KSTRTO", "$1 is obsolete, use k$3 instead\n" . $herecurr); } # check for __initcall(), use device_initcall() explicitly or more appropriate function please if ($line =~ /^.\s*__initcall\s*\(/) { WARN("USE_DEVICE_INITCALL", "please use device_initcall() or more appropriate function instead of __initcall() (see include/linux/init.h)\n" . $herecurr); } # check for various structs that are normally const (ops, kgdb, device_tree) # and avoid what seem like struct definitions 'struct foo {' if ($line !~ /\bconst\b/ && $line =~ /\bstruct\s+($const_structs)\b(?!\s*\{)/) { WARN("CONST_STRUCT", "struct $1 should normally be const\n" . $herecurr); } # use of NR_CPUS is usually wrong # ignore definitions of NR_CPUS and usage to define arrays as likely right if ($line =~ /\bNR_CPUS\b/ && $line !~ /^.\s*\s*#\s*if\b.*\bNR_CPUS\b/ && $line !~ /^.\s*\s*#\s*define\b.*\bNR_CPUS\b/ && $line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/ && $line !~ /\[[^\]]*\.\.\.[^\]]*NR_CPUS[^\]]*\]/ && $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/) { WARN("NR_CPUS", "usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr); } # Use of __ARCH_HAS_<FOO> or ARCH_HAVE_<BAR> is wrong. if ($line =~ /\+\s*#\s*define\s+((?:__)?ARCH_(?:HAS|HAVE)\w*)\b/) { ERROR("DEFINE_ARCH_HAS", "#define of '$1' is wrong - use Kconfig variables or standard guards instead\n" . $herecurr); } # likely/unlikely comparisons similar to "(likely(foo) > 0)" if ($^V && $^V ge 5.10.0 && $line =~ /\b((?:un)?likely)\s*\(\s*$FuncArg\s*\)\s*$Compare/) { WARN("LIKELY_MISUSE", "Using $1 should generally have parentheses around the comparison\n" . $herecurr); } # whine mightly about in_atomic if ($line =~ /\bin_atomic\s*\(/) { if ($realfile =~ m@^drivers/@) { ERROR("IN_ATOMIC", "do not use in_atomic in drivers\n" . $herecurr); } elsif ($realfile !~ m@^kernel/@) { WARN("IN_ATOMIC", "use of in_atomic() is incorrect outside core kernel code\n" . $herecurr); } } # whine about ACCESS_ONCE if ($^V && $^V ge 5.10.0 && $line =~ /\bACCESS_ONCE\s*$balanced_parens\s*(=(?!=))?\s*($FuncArg)?/) { my $par = $1; my $eq = $2; my $fun = $3; $par =~ s/^\(\s*(.*)\s*\)$/$1/; if (defined($eq)) { if (WARN("PREFER_WRITE_ONCE", "Prefer WRITE_ONCE(<FOO>, <BAR>) over ACCESS_ONCE(<FOO>) = <BAR>\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\bACCESS_ONCE\s*\(\s*\Q$par\E\s*\)\s*$eq\s*\Q$fun\E/WRITE_ONCE($par, $fun)/; } } else { if (WARN("PREFER_READ_ONCE", "Prefer READ_ONCE(<FOO>) over ACCESS_ONCE(<FOO>)\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/\bACCESS_ONCE\s*\(\s*\Q$par\E\s*\)/READ_ONCE($par)/; } } } # check for mutex_trylock_recursive usage if ($line =~ /mutex_trylock_recursive/) { ERROR("LOCKING", "recursive locking is bad, do not use this ever.\n" . $herecurr); } # check for lockdep_set_novalidate_class if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ || $line =~ /__lockdep_no_validate__\s*\)/ ) { if ($realfile !~ m@^kernel/lockdep@ && $realfile !~ m@^include/linux/lockdep@ && $realfile !~ m@^drivers/base/core@) { ERROR("LOCKDEP", "lockdep_no_validate class is reserved for device->mutex.\n" . $herecurr); } } if ($line =~ /debugfs_create_\w+.*\b$mode_perms_world_writable\b/ || $line =~ /DEVICE_ATTR.*\b$mode_perms_world_writable\b/) { WARN("EXPORTED_WORLD_WRITABLE", "Exporting world writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr); } # Mode permission misuses where it seems decimal should be octal # This uses a shortcut match to avoid unnecessary uses of a slow foreach loop if ($^V && $^V ge 5.10.0 && defined $stat && $line =~ /$mode_perms_search/) { foreach my $entry (@mode_permission_funcs) { my $func = $entry->[0]; my $arg_pos = $entry->[1]; my $lc = $stat =~ tr@\n@@; $lc = $lc + $linenr; my $stat_real = raw_line($linenr, 0); for (my $count = $linenr + 1; $count <= $lc; $count++) { $stat_real = $stat_real . "\n" . raw_line($count, 0); } my $skip_args = ""; if ($arg_pos > 1) { $arg_pos--; $skip_args = "(?:\\s*$FuncArg\\s*,\\s*){$arg_pos,$arg_pos}"; } my $test = "\\b$func\\s*\\(${skip_args}($FuncArg(?:\\|\\s*$FuncArg)*)\\s*[,\\)]"; if ($stat =~ /$test/) { my $val = $1; $val = $6 if ($skip_args ne ""); if (($val =~ /^$Int$/ && $val !~ /^$Octal$/) || ($val =~ /^$Octal$/ && length($val) ne 4)) { ERROR("NON_OCTAL_PERMISSIONS", "Use 4 digit octal (0777) not decimal permissions\n" . "$here\n" . $stat_real); } if ($val =~ /^$Octal$/ && (oct($val) & 02)) { ERROR("EXPORTED_WORLD_WRITABLE", "Exporting writable files is usually an error. Consider more restrictive permissions.\n" . "$here\n" . $stat_real); } } } } # check for uses of S_<PERMS> that could be octal for readability if ($line =~ /\b$mode_perms_string_search\b/) { my $val = ""; my $oval = ""; my $to = 0; my $curpos = 0; my $lastpos = 0; while ($line =~ /\b(($mode_perms_string_search)\b(?:\s*\|\s*)?\s*)/g) { $curpos = pos($line); my $match = $2; my $omatch = $1; last if ($lastpos > 0 && ($curpos - length($omatch) != $lastpos)); $lastpos = $curpos; $to |= $mode_permission_string_types{$match}; $val .= '\s*\|\s*' if ($val ne ""); $val .= $match; $oval .= $omatch; } $oval =~ s/^\s*\|\s*//; $oval =~ s/\s*\|\s*$//; my $octal = sprintf("%04o", $to); if (WARN("SYMBOLIC_PERMS", "Symbolic permissions '$oval' are not preferred. Consider using octal permissions '$octal'.\n" . $herecurr) && $fix) { $fixed[$fixlinenr] =~ s/$val/$octal/; } } # validate content of MODULE_LICENSE against list from include/linux/module.h if ($line =~ /\bMODULE_LICENSE\s*\(\s*($String)\s*\)/) { my $extracted_string = get_quoted_string($line, $rawline); my $valid_licenses = qr{ GPL| GPL\ v2| GPL\ and\ additional\ rights| Dual\ BSD/GPL| Dual\ MIT/GPL| Dual\ MPL/GPL| Proprietary }x; if ($extracted_string !~ /^"(?:$valid_licenses)"$/x) { WARN("MODULE_LICENSE", "unknown module license " . $extracted_string . "\n" . $herecurr); } } } # If we have no input at all, then there is nothing to report on # so just keep quiet. if ($#rawlines == -1) { exit(0); } # In mailback mode only produce a report in the negative, for # things that appear to be patches. if ($mailback && ($clean == 1 || !$is_patch)) { exit(0); } # This is not a patch, and we are are in 'no-patch' mode so # just keep quiet. if (!$chk_patch && !$is_patch) { exit(0); } if (!$is_patch && $filename !~ /cover-letter\.patch$/) { ERROR("NOT_UNIFIED_DIFF", "Does not appear to be a unified-diff format patch\n"); } if ($is_patch && $has_commit_log && $chk_signoff && $signoff == 0) { ERROR("MISSING_SIGN_OFF", "Missing Signed-off-by: line(s)\n"); } print report_dump(); if ($summary && !($clean == 1 && $quiet == 1)) { print "$filename " if ($summary_file); print "total: $cnt_error errors, $cnt_warn warnings, " . (($check)? "$cnt_chk checks, " : "") . "$cnt_lines lines checked\n"; } if ($quiet == 0) { # If there were any defects found and not already fixing them if (!$clean and !$fix) { print << "EOM" NOTE: For some of the reported defects, checkpatch may be able to mechanically convert to the typical style using --fix or --fix-inplace. EOM } # If there were whitespace errors which cleanpatch can fix # then suggest that. if ($rpt_cleaners) { $rpt_cleaners = 0; print << "EOM" NOTE: Whitespace errors detected. You may wish to use scripts/cleanpatch or scripts/cleanfile EOM } } if ($clean == 0 && $fix && ("@rawlines" ne "@fixed" || $#fixed_inserted >= 0 || $#fixed_deleted >= 0)) { my $newfile = $filename; $newfile .= ".EXPERIMENTAL-checkpatch-fixes" if (!$fix_inplace); my $linecount = 0; my $f; @fixed = fix_inserted_deleted_lines(\@fixed, \@fixed_inserted, \@fixed_deleted); open($f, '>', $newfile) or die "$P: Can't open $newfile for write\n"; foreach my $fixed_line (@fixed) { $linecount++; if ($file) { if ($linecount > 3) { $fixed_line =~ s/^\+//; print $f $fixed_line . "\n"; } } else { print $f $fixed_line . "\n"; } } close($f); if (!$quiet) { print << "EOM"; Wrote EXPERIMENTAL --fix correction(s) to '$newfile' Do _NOT_ trust the results written to this file. Do _NOT_ submit these changes without inspecting them for correctness. This EXPERIMENTAL file is simply a convenience to help rewrite patches. No warranties, expressed or implied... EOM } } if ($quiet == 0) { print "\n"; if ($clean == 1) { print "$vname has no obvious style problems and is ready for submission.\n"; } else { print "$vname has style problems, please review.\n"; } } return $clean; } ��nfs-ganesha-2.6.0/src/scripts/docker/���������������������������������������������������������������0000775�0000000�0000000�00000000000�13242724102�0017433�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/docker/Dockerfile-in.cmake��������������������������������������������0000664�0000000�0000000�00000001076�13242724102�0023114�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# CEPH BASE IMAGE # CEPH VERSION: Hammer # CEPH VERSION DETAIL: 0.94.x FROM @DOCKER_DISTRO@:@DOCKER_DISTRO_VERSION@ MAINTAINER Daniel Gryniewicz "dang@redhat.com" #ENV ETCDCTL_VERSION v2.2.0 #ENV ETCDCTL_ARCH linux-amd64 #ENV CEPH_VERSION 10.0.3-2378.gd3db533 # Install prerequisites RUN dnf install -y tar redhat-lsb-core # Install deps RUN dnf install -y libcap libblkid libuuid dbus nfs-utils rpcbind libnfsidmap libattr #install ganesha ADD root/ / RUN mkdir -p @CMAKE_INSTALL_PREFIX@/var/log/ganesha ADD entrypoint.sh /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/docker/entrypoint.sh-in.cmake�����������������������������������������0000775�0000000�0000000�00000001324�13242724102�0023670�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash # # Entrypoint for the Ganesha docker container. If "shell" is given as the # argument, then a shell is run; otherwise, Ganseha is started. # These are the options for starting Ganesha. Set them in the environment. : ${GANESHA_LOGFILE:="@SYSSTATEDIR@/log/ganesha.log"} : ${GANESHA_CONFFILE:="@SYSCONFDIR@/ganesha/ganesha.conf"} : ${GANESHA_OPTIONS:="-N NIV_EVENT"} : ${GANESHA_EPOCH:=""} : ${GANESHA_LIBPATH:="@LIB_INSTALL_DIR@"} function rpc_init { rpcbind rpc.statd -L rpc.idmapd } if [ "$1" == "shell" ]; then /bin/bash else rpc_init LD_LIBRARY_PATH="${GANESHA_LIBPATH}" @CMAKE_INSTALL_PREFIX@/bin/ganesha.nfsd -F -L ${GANESHA_LOGFILE} -f ${GANESHA_CONFFILE} ${GANESHA_OPTIONS} ${GANESHA_EPOCH} fi ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/�����������������������������������������������������������0000775�0000000�0000000�00000000000�13242724102�0020275�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/CMakeLists.txt���������������������������������������������0000664�0000000�0000000�00000007655�13242724102�0023052�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Building and packaging DBUS scripts for ganesha find_program(PYTHON "python") # PyQT based class modules set(GANESHA_BASE_SRCS Ganesha/__init__.py Ganesha/admin.py Ganesha/client_mgr.py Ganesha/QtUI/clients_table.py Ganesha/export_mgr.py Ganesha/QtUI/exports_table.py Ganesha/log_mgr.py Ganesha/QtUI/log_settings.py Ganesha/io_stats.py Ganesha/glib_dbus_stats.py Ganesha/ganesha_mgr_utils.py Ganesha/config_editor.py ) # Command line scripts set(SCRIPT_SRC fake_recall.py get_clientids.py grace_period.py purge_gids.py ganesha_mgr.py ganesha_stats.py ganesha_conf.py ) set(GUI_SCRIPT_SRC ganeshactl.py ganesha-admin.py manage_clients.py manage_logger.py manage_exports.py client_stats_9pOps.py export_stats_9pOps.py ) # Qt Designer files that get compiled into .py scripts set(UI_SRC Ganesha/QtUI/ui_main_window.ui Ganesha/QtUI/ui_log_dialog.ui ) if(PYTHON) set(SETUP_PY_IN "${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in") set(SETUP_PY "${CMAKE_CURRENT_BINARY_DIR}/setup.py") set(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/build/timestamp") # Generate rules for compiling .ui -> .py set(GANESHA_SRCS) set(UI_PY_FILES) if(USE_GUI_ADMIN_TOOLS) set(GANESHA_SRCS ${GANESHA_BASE_SRCS}) foreach(ui_file ${UI_SRC}) string(REPLACE ".ui" ".py" py_file ${ui_file}) add_custom_command( OUTPUT ${py_file} COMMAND mkdir -p build/lib/Ganesha/QtUI COMMAND ${PYUIC} "${CMAKE_CURRENT_SOURCE_DIR}/${ui_file}" -o "build/lib/${py_file}" DEPENDS ${ui_file} ) set(UI_PY_FILES ${UI_PY_FILES} "${py_file}") list(APPEND GANESHA_SRCS ${py_file}) endforeach() endif() # Generate rules to copy command line scripts from src to build, # stripping .py along the way set(SCRIPTS) foreach(src_file ${SCRIPT_SRC}) string(REPLACE ".py" "" script_file ${src_file}) add_custom_command( OUTPUT ${script_file} COMMAND cp "${CMAKE_CURRENT_SOURCE_DIR}/${src_file}" ${script_file} DEPENDS ${src_file} ) list(APPEND SCRIPTS ${script_file}) endforeach() if(USE_GUI_ADMIN_TOOLS) foreach(src_file ${GUI_SCRIPT_SRC}) string(REPLACE ".py" "" script_file ${src_file}) add_custom_command( OUTPUT ${script_file} COMMAND cp "${CMAKE_CURRENT_SOURCE_DIR}/${src_file}" ${script_file} DEPENDS ${src_file} ) list(APPEND SCRIPTS ${script_file}) endforeach() endif() # Build up the string for the configure substitution in setup.py set(SCRIPTS_STRING) foreach(script_py ${SCRIPT_SRC}) string(REPLACE ".py" "" script ${script_py}) if("${SCRIPTS_STRING}" STREQUAL "") set(SCRIPTS_STRING "'${script}'") else() set(SCRIPTS_STRING "${SCRIPTS_STRING}, '${script}'") endif() endforeach() if(USE_GUI_ADMIN_TOOLS) foreach(script_py ${GUI_SCRIPT_SRC}) string(REPLACE ".py" "" script ${script_py}) if("${SCRIPTS_STRING}" STREQUAL "") set(SCRIPTS_STRING "'${script}'") else() set(SCRIPTS_STRING "${SCRIPTS_STRING}, '${script}'") endif() endforeach() endif() configure_file(${SETUP_PY_IN} ${SETUP_PY}) add_custom_command( OUTPUT ${OUTPUT} COMMAND ${PYTHON} "${SETUP_PY}" build COMMAND ${CMAKE_COMMAND} -E touch ${OUTPUT} DEPENDS ${GANESHA_SRCS} ${UI_PY_FILES} ${SCRIPTS} ) add_custom_target(target ALL DEPENDS ${OUTPUT}) install( CODE "execute_process(WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMAND ${PYTHON} ${SETUP_PY} install --skip-build --prefix=\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX})" ) endif(PYTHON) # Man page set(man8_file ganesha_conf.man) set(man8_page ganesha_conf.8) add_custom_command( OUTPUT ${man8_page} COMMAND cp "${CMAKE_CURRENT_SOURCE_DIR}/${man8_file}" ${man8_page} DEPENDS ${man8_file} ) add_custom_target(man ALL DEPENDS ${man8_page}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${man8_page} DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man8/) �����������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/Ganesha/���������������������������������������������������0000775�0000000�0000000�00000000000�13242724102�0021643�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/Ganesha/QtUI/����������������������������������������������0000775�0000000�0000000�00000000000�13242724102�0022465�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/Ganesha/QtUI/__init__.py�����������������������������������0000664�0000000�0000000�00000000246�13242724102�0024600�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Ganesha Qt GUI classes __all__ = ["exports_table", "clients_table", "ui_log_dialog", "log_settings", "ui_main_window"] ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/Ganesha/QtUI/clients_table.py������������������������������0000664�0000000�0000000�00000011310�13242724102�0025643�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # clients_table.py - ClientTableModel model class. # # Copyright (C) 2014 Panasas Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see <http://www.gnu.org/licenses/>. # # Author: Jim Lieb <jlieb@panasas.com> #-*- coding: utf-8 -*- import sys, time from PyQt4.QtCore import * from PyQt4 import QtDBus, QtGui from PyQt4.QtGui import QColor class ClientTableModel(QAbstractTableModel): ''' Clients Table Model to match its table view ''' def __init__(self, clientmgr, parent=None): super(ClientTableModel, self).__init__(parent) self.header = ['Client IP', 'NFSv3', 'MNT', 'NLMv4', 'RQUOTA', 'NFSv4.0', 'NFSv4.1', '9P', 'Last Stats Update'] self.clientmgr = clientmgr self.clientmgr.show_clients.connect(self.FetchClients_done) self.clients = [] self.ts = (0L, 0L) # Fetch current clients def FetchClients(self): self.clientmgr.ShowClients() def FetchClients_done(self, ts, clients): if len(self.clients) != len(clients): if len(self.clients) > 0: self.removeRows(0, len(self.clients)) self.insertRows(0, len(clients)) for i in xrange(len(clients)): exp = clients[i] for j in xrange(len(exp)): if isinstance(exp[j], bool): if exp[j]: val = "yes" else: val = "no" elif isinstance(exp[j], tuple): val = time.ctime(exp[j][0]) else: val = str(exp[j]) self.setData(self.createIndex(i, j), val) # model abstract methods def setData(self, index, value, role = Qt.EditRole): if role == Qt.EditRole: row = index.row() col = index.column() t = self.clients[row] if row > self.rowCount() or col > self.columnCount(): return False t[col] = value self.emit(SIGNAL('dataChanged'), index, index) return True return False def insertRow(self, row, parent=QModelIndex()): self.insertRows(self, row, 1, parent) def insertRows(self, row, count, parent=QModelIndex()): self.beginInsertRows(parent, row, row + count - 1) for i in xrange(count): self.clients.insert(row, ['',]*self.columnCount()) self.endInsertRows() return True def removeRow(self, row, parent=QModelIndex()): self.removeRows(self, row, 1, parent) def removeRows(self, row, count, parent=QModelIndex()): self.beginRemoveRows(parent, row, row + count -1) for i in reversed(xrange(count)): self.clients.pop(row + i) self.endRemoveRows() def rowCount(self, parent=QModelIndex()): return len(self.clients) def columnCount(self, parent=QModelIndex()): return len(self.header) def headerData(self, col, orientation, role): if orientation == Qt.Horizontal and role == Qt.DisplayRole: return QVariant(self.header[col]) return QVariant() def flags(self, index): return Qt.NoItemFlags def data(self, index, role): if not index.isValid(): return QVariant() elif role == Qt.DisplayRole: return QVariant(self.clients[index.row()][index.column()]) elif role == Qt.TextAlignmentRole: align = Qt.AlignVCenter if index.column() == 0: align = align + Qt.AlignRight elif index.column() == 9: align = align + Qt.AlignLeft else: align = align + Qt.AlignCenter return QVariant(align) elif role == Qt.BackgroundRole: if index.row() % 2 == 0: return QVariant(QColor(Qt.gray)) else: return QVariant(QColor(Qt.lightGray)) elif role == Qt.ForegroundRole: return QVariant(QColor(Qt.black)) return QVariant() ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/Ganesha/QtUI/exports_table.py������������������������������0000664�0000000�0000000�00000011377�13242724102�0025723�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # exports_table.py - ExportTableModel class. # # Copyright (C) 2014 Panasas Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see <http://www.gnu.org/licenses/>. # # Author: Jim Lieb <jlieb@panasas.com> #-*- coding: utf-8 -*- import sys, time from PyQt4.QtCore import * from PyQt4 import QtDBus, QtGui from PyQt4.QtGui import QColor class ExportTableModel(QAbstractTableModel): ''' Exports Table Model to match its table view ''' def __init__(self, exportmgr, parent=None): super(ExportTableModel, self).__init__(parent) self.header = ['Export ID', 'Export Path', 'NFSv3', 'MNT', 'NLMv4', 'RQUOTA', 'NFSv4.0', 'NFSv4.1', '9P', 'Last Stats Update'] self.exportmgr = exportmgr self.exportmgr.show_exports.connect(self.FetchExports_done) self.exports = [] self.ts = (0L, 0L) # Fetch current exports def FetchExports(self): self.exportmgr.ShowExports() def FetchExports_done(self, ts, exports): if len(self.exports) != len(exports): if len(self.exports) > 0: self.removeRows(0, len(self.exports)) self.insertRows(0, len(exports)) for i in xrange(len(exports)): exp = exports[i] for j in xrange(len(exp)): if isinstance(exp[j], bool): if exp[j]: val = "yes" else: val = "no" elif isinstance(exp[j], tuple): val = time.ctime(exp[j][0]) else: val = str(exp[j]) self.setData(self.createIndex(i, j), val) # model abstract methods def setData(self, index, value, role = Qt.EditRole): if role == Qt.EditRole: row = index.row() col = index.column() t = self.exports[row] if row > self.rowCount() or col > self.columnCount(): return False t[col] = value self.emit(SIGNAL('dataChanged'), index, index) return True return False def insertRow(self, row, parent=QModelIndex()): self.insertRows(self, row, 1, parent) def insertRows(self, row, count, parent=QModelIndex()): self.beginInsertRows(parent, row, row + count - 1) for i in xrange(count): self.exports.insert(row, ['',]*self.columnCount()) self.endInsertRows() return True def removeRow(self, row, parent=QModelIndex()): self.removeRows(self, row, 1, parent) def removeRows(self, row, count, parent=QModelIndex()): self.beginRemoveRows(parent, row, row + count -1) for i in reversed(xrange(count)): self.exports.pop(row + i) self.endRemoveRows() def rowCount(self, parent=QModelIndex()): return len(self.exports) def columnCount(self, parent=QModelIndex()): return len(self.header) def headerData(self, col, orientation, role): if orientation == Qt.Horizontal and role == Qt.DisplayRole: return QVariant(self.header[col]) return QVariant() def flags(self, index): return Qt.NoItemFlags def data(self, index, role): if not index.isValid(): return QVariant() elif role == Qt.DisplayRole: return QVariant(self.exports[index.row()][index.column()]) elif role == Qt.TextAlignmentRole: align = Qt.AlignVCenter if index.column() == 0: align = align + Qt.AlignRight elif index.column() == 1 or index.column() == 9: align = align + Qt.AlignLeft else: align = align + Qt.AlignCenter return QVariant(align) elif role == Qt.BackgroundRole: if index.row() % 2 == 0: return QVariant(QColor(Qt.gray)) else: return QVariant(QColor(Qt.lightGray)) elif role == Qt.ForegroundRole: return QVariant(QColor(Qt.black)) return QVariant() �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/Ganesha/QtUI/log_settings.py�������������������������������0000664�0000000�0000000�00000016150�13242724102�0025543�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # log_settings.py - DebugLevelDelegate class. # # Copyright (C) 2014 Panasas Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see <http://www.gnu.org/licenses/>. # # Author: Jim Lieb <jlieb@panasas.com> #-*- coding: utf-8 -*- from PyQt4.QtCore import * from PyQt4 import QtGui from Ganesha.QtUI.ui_log_dialog import Ui_LogSettings class DebugLevelDelegate(QtGui.QItemDelegate): ''' Log level combo box editor ''' # Copied from log_levels_t in src/include/log.h:69 loglevels = ['NIV_NULL', 'NIV_FATAL', 'NIV_MAJ', 'NIV_CRIT', 'NIV_WARN', 'NIV_EVENT', 'NIV_INFO', 'NIV_DEBUG', 'NIV_MID_DEBUG', 'NIV_FULL_DEBUG'] def __init__(self, parent = None): super(DebugLevelDelegate, self).__init__(parent) def createEditor(self, parent, option, index): editor = QtGui.QComboBox(parent) editor.addItems(self.loglevels) editor.setCurrentIndex(0) editor.installEventFilter(self) return editor def setEditorData(self, editor, index): value = index.data(Qt.DisplayRole).toString() combo_index = editor.findText(value) editor.setCurrentIndex(combo_index) def setModelData(self, editor, model, index): text = editor.currentText() model.setData(index, text, Qt.EditRole) def updateEditorGeometry(self, editor, option, index): editor.setGeometry(option.rect) class LogSettingsModel(QAbstractTableModel): ''' Table of log settings as an editable form. Radio buttons are used for setting levels ''' def __init__(self, logmanager, parent=None): super(LogSettingsModel, self).__init__(parent) self.header = ['Log Component', 'Logging Level'] self.logmanager = logmanager self.logmanager.show_components.connect(self.getComponents_done) self.log_components = [] def getComponents(self): self.logmanager.GetAll() def getComponents_done(self, comp_dict): if len(self.log_components) != len(comp_dict): if len(self.log_components) > 0: self.removeRows(0, len(self.log_components)) self.insertRows(0,len(comp_dict)) comps = comp_dict.keys() comps.sort() # Populate the table in sorted order by hand. # in order to avoid the signal in setData which would # go back to ganesha and set what we just got... for i in xrange(len(comps)): self.log_components[i] = [comps[i], comp_dict[comps[i]]] def updateSetting(self, ULIndex, LRIndex): comp = self.log_components[ULIndex.row()][0] level = self.log_components[ULIndex.row()][1] self.logmanager.Set(comp, level) # Refresh the table because things like "All" whack everybody... self.logmanager.GetAll() # model abstract methods def setData(self, index, value, role = Qt.EditRole): if role == Qt.EditRole: row = index.row() col = index.column() comp = self.log_components[row] comp[col] = value self.dataChanged.emit(index, index) return True else: return False def insertRow(self, row, parent=QModelIndex()): self.insertRows(self, row, 1, parent) def insertRows(self, row, count, parent=QModelIndex()): self.beginInsertRows(parent, row, row + count -1) for row in xrange(count): self.log_components.insert(row, ['',]*self.columnCount()) self.endInsertRows() return True def removeRow(self, row, parent=QModelIndex()): self.removeRows(self, row, 1, parent) def removeRows(self, row, count, parent=QModelIndex()): self.beginRemoveRows(parent, row, count + count -1) for i in reversed(xrange(count)): self.log_comp_levels.pop(row + i) self.endRemoveRows() def rowCount(self, parent=QModelIndex()): return len(self.log_components) def columnCount(self, parent=QModelIndex()): return len(self.header) def headerData(self, section, orientation, role): if role == Qt.DisplayRole: if orientation == Qt.Horizontal: return QVariant(self.header[section]) else: return QVariant() def flags(self, index): if not index.isValid(): return Qt.ItemIsEnabled if index.column() == 1: return Qt.ItemFlags(QAbstractTableModel.flags(self, index) | Qt.ItemIsEditable) else: return Qt.ItemFlags(QAbstractTableModel.flags(self, index)) def data(self, index, role): if not index.isValid(): return QVariant() elif role == Qt.DisplayRole: if index.column() == 0: comp = self.log_components[index.row()][0] return QVariant(comp) else: return QVariant(self.log_components[index.row()][1]) elif role == Qt.EditRole: if index.column() == 0: comp = self.log_components[index.row()][0] print "edit partitioned comp", comp return QVariant(comp) else: return QVariant(self.log_components[index.row()][1]) elif role == Qt.TextAlignmentRole: align = Qt.AlignCenter if index.column() == 0: align = Qt.AlignLeft return QVariant(align) class LogSetDialog(QtGui.QDialog): ''' Manage popup of log setting dialog ''' def __init__(self, log_mgr, parent=None): super(LogSetDialog, self).__init__(parent) self.log_ui = Ui_LogSettings() self.log_ui.setupUi(self) self.log_mgr = log_mgr self.log_setting_model = LogSettingsModel(self.log_mgr) self.level_edit_delegate = DebugLevelDelegate() self.log_ui.log_levels.setModel(self.log_setting_model) self.log_ui.log_levels.resizeColumnsToContents() self.log_ui.log_levels.verticalHeader().setVisible(False) self.log_ui.log_levels.setItemDelegateForColumn(1, self.level_edit_delegate) self.log_ui.log_done.clicked.connect(self.close_logsetting_dialog) self.log_setting_model.getComponents() self.log_setting_model.dataChanged.connect(self.log_setting_model.updateSetting) def show_logsetting_dialog(self): self.show() def close_logsetting_dialog(self): self.hide() ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/Ganesha/QtUI/ui_log_dialog.ui������������������������������0000664�0000000�0000000�00000002147�13242724102�0025625�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>LogSettings</class> <widget class="QDialog" name="LogSettings"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>400</width> <height>300</height> </rect> </property> <property name="windowTitle"> <string>Log Settings</string> </property> <widget class="QWidget" name="verticalLayoutWidget"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>391</width> <height>241</height> </rect> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QTableView" name="log_levels"> <property name="alternatingRowColors"> <bool>true</bool> </property> </widget> </item> </layout> </widget> <widget class="QPushButton" name="log_done"> <property name="geometry"> <rect> <x>280</x> <y>260</y> <width>100</width> <height>24</height> </rect> </property> <property name="text"> <string>Done</string> </property> </widget> </widget> <resources/> <connections/> </ui> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/Ganesha/QtUI/ui_main_window.ui�����������������������������0000664�0000000�0000000�00000013566�13242724102�0026047�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>MainWindow</class> <widget class="QMainWindow" name="MainWindow"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>800</width> <height>600</height> </rect> </property> <property name="windowTitle"> <string>NFS Ganesha</string> </property> <widget class="QWidget" name="centralwidget"> <layout class="QGridLayout" name="gridLayout"> <item row="0" column="0"> <widget class="QTabWidget" name="tabWidget"> <property name="currentIndex"> <number>0</number> </property> <widget class="QWidget" name="exports_tab"> <attribute name="title"> <string>Exports</string> </attribute> <layout class="QGridLayout" name="gridLayout_2"> <item row="0" column="0"> <widget class="QScrollArea" name="scrollArea"> <property name="widgetResizable"> <bool>true</bool> </property> <widget class="QWidget" name="scrollAreaWidgetContents"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>770</width> <height>507</height> </rect> </property> <layout class="QGridLayout" name="gridLayout_5"> <item row="0" column="0"> <widget class="QTableView" name="exports"/> </item> </layout> </widget> </widget> </item> </layout> </widget> <widget class="QWidget" name="clients_tab"> <attribute name="title"> <string>Clients</string> </attribute> <layout class="QGridLayout" name="gridLayout_3"> <item row="0" column="0"> <widget class="QScrollArea" name="scrollArea_2"> <property name="widgetResizable"> <bool>true</bool> </property> <widget class="QWidget" name="scrollAreaWidgetContents_2"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>770</width> <height>507</height> </rect> </property> <layout class="QGridLayout" name="gridLayout_4"> <item row="0" column="0"> <widget class="QTableView" name="clients"/> </item> </layout> </widget> </widget> </item> </layout> </widget> </widget> </item> </layout> </widget> <widget class="QMenuBar" name="menubar"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>800</width> <height>21</height> </rect> </property> <widget class="QMenu" name="menuFile"> <property name="title"> <string>File</string> </property> <addaction name="actionDBus_connect"/> <addaction name="actionQuit"/> </widget> <widget class="QMenu" name="menuManager"> <property name="title"> <string>Manage</string> </property> <widget class="QMenu" name="menuClients"> <property name="title"> <string>Clients</string> </property> <addaction name="actionAdd_Client"/> <addaction name="actionRemove_Client"/> </widget> <widget class="QMenu" name="menuAdmin"> <property name="title"> <string>Admin</string> </property> <addaction name="actionReset_Grace"/> <addaction name="actionShutdown"/> <addaction name="actionReload"/> </widget> <addaction name="actionLog_Settings"/> <addaction name="menuClients"/> <addaction name="actionExports"/> <addaction name="menuAdmin"/> </widget> <widget class="QMenu" name="menuView"> <property name="title"> <string>View</string> </property> <addaction name="actionStatistics"/> <addaction name="actionViewExports"/> <addaction name="actionViewClients"/> </widget> <widget class="QMenu" name="menuHelp"> <property name="title"> <string>Help</string> </property> <addaction name="actionAbout"/> </widget> <addaction name="menuFile"/> <addaction name="menuManager"/> <addaction name="menuView"/> <addaction name="menuHelp"/> </widget> <widget class="QStatusBar" name="statusbar"/> <action name="actionDBus_connect"> <property name="text"> <string>Connect to Ganesha</string> </property> </action> <action name="actionQuit"> <property name="text"> <string>Quit</string> </property> </action> <action name="actionExports"> <property name="text"> <string>Exports</string> </property> </action> <action name="actionStatistics"> <property name="text"> <string>Statistics</string> </property> </action> <action name="actionLog_Levels"> <property name="text"> <string>Log Levels</string> </property> </action> <action name="actionViewExports"> <property name="text"> <string>Exports</string> </property> </action> <action name="actionAbout"> <property name="text"> <string>About</string> </property> </action> <action name="actionReset_Grace"> <property name="text"> <string>Reset Grace Period</string> </property> </action> <action name="actionShutdown"> <property name="text"> <string>Shutdown</string> </property> </action> <action name="actionReload"> <property name="text"> <string>Reload</string> </property> </action> <action name="actionViewClients"> <property name="text"> <string>Clients</string> </property> </action> <action name="actionAdd_Client"> <property name="text"> <string>Add Client</string> </property> </action> <action name="actionRemove_Client"> <property name="text"> <string>Remove Client</string> </property> </action> <action name="actionLog_Settings"> <property name="text"> <string>Log Settings</string> </property> </action> </widget> <resources/> <connections/> </ui> ������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/Ganesha/__init__.py����������������������������������������0000664�0000000�0000000�00000000221�13242724102�0023747�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Ganesha management classes __all__ = ["admin", "io_stats", "export_mgr", "client_mgr", "log_mgr"] �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/Ganesha/admin.py�������������������������������������������0000664�0000000�0000000�00000004416�13242724102�0023312�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # admin.py - AdminInterface DBUS object class. # # Copyright (C) 2014 Panasas Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see <http://www.gnu.org/licenses/>. # # Author: Jim Lieb <jlieb@panasas.com> #-*- coding: utf-8 -*- from PyQt4 import QtCore, QtDBus class AdminInterface(QtDBus.QDBusAbstractInterface): ''' org.ganesha.nfsd.admin interface ''' def __init__(self, service, path, connection, show_status, parent=None): super(AdminInterface, self).__init__(service, path, 'org.ganesha.nfsd.admin', connection, parent) self.show_status = show_status def grace(self, ipaddr): async = self.asyncCall("grace", ipaddr) status = QtDBus.QDBusPendingCallWatcher(async, self) status.finished.connect(self.admin_done) def reload(self): async = self.asyncCall("reload") status = QtDBus.QDBusPendingCallWatcher(async, self) status.finished.connect(self.admin_done) def shutdown(self): async = self.asyncCall("shutdown") status = QtDBus.QDBusPendingCallWatcher(async, self) status.finished.connect(self.admin_done) # catch the reply and forward it to the UI def admin_done(self, call): reply = QtDBus.QDBusPendingReply(call) if reply.isError(): self.show_status.emit(False, "DBUS error:" + str(reply.error().message())) else: status = reply.argumentAt(0).toPyObject() msg = reply.argumentAt(1).toPyObject() self.show_status.emit(status, msg) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/Ganesha/client_mgr.py��������������������������������������0000664�0000000�0000000�00000012660�13242724102�0024345�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # client_mgr.py - ClientMgr DBus object class. # # Copyright (C) 2014 Panasas Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see <http://www.gnu.org/licenses/>. # # Author: Jim Lieb <jlieb@panasas.com> #-*- coding: utf-8 -*- from PyQt4 import QtCore, QtDBus from collections import namedtuple Client = namedtuple('Client', ['ClientIP', 'HasNFSv3', 'HasMNT', 'HasNLM4', 'HasRQUOTA', 'HasNFSv40', 'HasNFSv41', 'HasNFSv42', 'Has9P', 'LastTime']) class ClientMgr(QtDBus.QDBusAbstractInterface): ''' org.ganesha.nfsd.clientmgr ''' show_clients = QtCore.pyqtSignal(tuple, list) def __init__(self, service, path, connection, show_status, parent=None): super(ClientMgr, self).__init__(service, path, 'org.ganesha.nfsd.clientmgr', connection, parent) self.show_status = show_status def AddClient(self, ipaddr): async = self.asyncCall("AddClient", ipaddr) status = QtDBus.QDBusPendingCallWatcher(async, self) status.finished.connect(self.clientmgr_done) def RemoveClient(self, ipaddr): async = self.asyncCall("RemoveClient", ipaddr) status = QtDBus.QDBusPendingCallWatcher(async, self) status.finished.connect(self.clientmgr_done) def ShowClients(self): async = self.asyncCall("ShowClients") status = QtDBus.QDBusPendingCallWatcher(async, self) status.finished.connect(self.clientshow_done) # catch the reply and forward it to the UI def clientmgr_done(self, call): reply = QtDBus.QDBusPendingReply(call) if reply.isError(): self.show_status.emit(False, "DBUS error:" + str(reply.error().message())) else: status = reply.argumentAt(0).toPyObject() msg = reply.argumentAt(1).toPyObject() self.show_status.emit(status, msg) def clientshow_done(self, call): reply = QtDBus.QDBusPendingReply(call) if reply.isError(): self.show_status.emit(False, "DBUS error:" + str(reply.error().message())) else: ts = (reply.argumentAt(0).toPyObject()[0].toULongLong()[0], reply.argumentAt(0).toPyObject()[1].toULongLong()[0]) interval_nsecs = ts[0] * 1000000000L + ts[1] clients = [] for client in reply.argumentAt(1).toPyObject(): cl = client.toPyObject() lasttime = cl[9].toPyObject() clt = Client(ClientIP = str(cl[0].toString()), HasNFSv3 = cl[1].toBool(), HasMNT = cl[2].toBool(), HasNLM4 = cl[3].toBool(), HasRQUOTA = cl[4].toBool(), HasNFSv40 = cl[5].toBool(), HasNFSv41 = cl[6].toBool(), HasNFSv42 = cl[7].toBool(), Has9P = cl[8].toBool(), LastTime = (lasttime[0].toPyObject(), lasttime[1].toPyObject())) clients.append(clt) self.show_clients.emit(ts, clients) class ClientStats(QtDBus.QDBusAbstractInterface): ''' org.ganesha.nfsd.clientstats ''' def __init__(self, service, path, connection, status_handler, parent=None): super(ClientStats, self).__init__(service, path, 'org.ganesha.nfsd.clientstats', connection, parent) self.status_handler = status_handler def GetNFSv3IO(self, ipaddr): async = self.asyncCall("GetNFSv3IO", ipaddr) status = QtDBus.QDBusPendingCallWatcher(async, self) status.finished.connect(self.io_done) def GetNFSv40IO(self, ipaddr): async = self.asyncCall("GetNFSv40IO", ipaddr) status = QtDBus.QDBusPendingCallWatcher(async, self) status.finished.connect(self.io_done) def GetNFSv41IO(self, ipaddr): async = self.asyncCall("GetNFSv41IO", ipaddr) status = QtDBus.QDBusPendingCallWatcher(async, self) status.finished.connect(self.io_done) def GetNFSv41Layouts(self, ipaddr): async = self.asyncCall("GetNFSv41Layouts", ipaddr) status = QtDBus.QDBusPendingCallWatcher(async, self) status.finished.connect(self.layout_done) def io_done(self, call): pass def layout_done(self, call): pass ��������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/Ganesha/config_editor.py�����������������������������������0000664�0000000�0000000�00000024401�13242724102�0025031�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python import re, sys import pyparsing as pp import logging, pprint LBRACE = pp.Literal("{").suppress() RBRACE = pp.Literal("}").suppress() SEMICOLON = pp.Literal(";").suppress() EQUAL = pp.Literal("=").suppress() KEY = pp.Word(pp.alphas, pp.alphanums+"_") VALUE = pp.CharsNotIn(';') # doesn't skip whitespace! BLOCKNAME = pp.Word(pp.alphas, pp.alphanums+"_") KEYPAIR = KEY + EQUAL + VALUE + SEMICOLON # block is recursively defined. It starts with name, left brace, then a # list of "key value" pairs followed by a list of blocks, and finally # ends with a right brace. We assume that all "key value" pairs ,if any, # precede any sub-blocks. Ganesha daemon itself allows "key value" pairs # after sub-blocks or in between sub-blocks, but we don't allow for # simplification. # # We construct a 3 element list in python for every block! The first # element is the name of block itself, the second element is the list of # "key value" pairs, and the last element is the list of sub-block. The # first element must be the name of the block and the remaining two # elements could be empty lists! This recursive list is usually named as # r3. # block definition for pyparsing. ppblock = pp.Forward() KEYPAIR_GROUP = pp.Group(pp.ZeroOrMore(pp.Group(KEYPAIR))) SUBS_GROUP = pp.Group(pp.ZeroOrMore(pp.Group(ppblock))) ppblock << BLOCKNAME + LBRACE + KEYPAIR_GROUP + SUBS_GROUP + RBRACE class BLOCK(object): def __init__(self, blocknames): self.blocknames = blocknames def set_keys(self, s, opairs): validate_blocknames(self.blocknames) validate_opt_pairs(opairs) match = ppblock.parseWithTabs().scanString(s) block_found = False for ppr, start, end in match: if block_match(self.blocknames, ppr[0], ppr[1]): block_found = True break; if block_found: begin_part = s[:start] end_part = s[end:] r3 = ppr.asList() logging.debug("%s", pprint.pformat(r3)) self.set_process(r3, self.blocknames, opairs) text = r3_to_text(r3, 0) logging.debug("%s", pprint.pformat(text)) assert text[-1] == "\n" if end_part[0] == "\n": text = text[:-1] # remove the last new line else: begin_part = s end_part = "" r3 = make_r3(self.blocknames) self.set_process(r3, self.blocknames, opairs) text = r3_to_text(r3, 0) return begin_part + text + end_part def del_keys(self, s, okeys): validate_blocknames(self.blocknames) validate_opt_keys(okeys) match = ppblock.parseWithTabs().scanString(s) block_found = False for ppr, start, end in match: if block_match(self.blocknames, ppr[0], ppr[1]): block_found = True break; if block_found: begin_part = s[:start] end_part = s[end:] r3 = ppr.asList() logging.debug("%s", pprint.pformat(r3)) self.del_process(r3, self.blocknames, okeys) text = r3_to_text(r3, 0) logging.debug("%s", pprint.pformat(text)) # if we remove this entire block, remove the last new line # character associated with this block. # # @todo: should we remove other white space also? if end_part[0] == "\n": end_part = end_part[1:] else: logging.debug("block not found") sys.exit("block not found") return begin_part + text + end_part def set_process(self, r3, blocknames, opairs): logging.debug("names: %s, r3: %s", pprint.pformat(blocknames), pprint.pformat(r3)) name, pairs, subs = r3[0], r3[1], r3[2] assert block_match(blocknames, name, pairs) # If last block, add given key value opairs subnames = next_subnames(blocknames) if not subnames: for key, value in opairs: key_found = False for idx, pair in enumerate(pairs): if key.lower() == pair[0].lower(): key_found = True pairs[idx] = [key, value] if not key_found: pairs.append([key, value]) return block_found = False for sub in subs: name2, pairs2, subs2 = sub[0], sub[1], sub[2] if block_match(subnames, name2, pairs2): block_found = True break; if block_found: self.set_process(sub, subnames, opairs) else: new_r3 = make_r3(subnames) subs.append(new_r3) self.set_process(new_r3, subnames, opairs) def del_process(self, r3, blocknames, okeys): logging.debug("names: %s, r3: %s", pprint.pformat(blocknames), pprint.pformat(r3)) name, pairs, subs = r3[0], r3[1], r3[2] assert block_match(blocknames, name, pairs) # If last block, delete given okeys subnames = next_subnames(blocknames) if not subnames: for key in okeys: key_found = False for pair in pairs[:]: if key.lower() == pair[0].lower(): key_found = True pairs.remove(pair) if not key_found: # @todo: exception to report sys.exit("key to delete is not found") # export and client blocks can't exist without some # key pairs identifying them. So remove the whole # block. @todo: shall we do this for regular blocks # also? if not pairs and (blocknames[0].lower() == "export" or blocknames[0].lower() == "client"): r3[:] = [] if not okeys: # remove the whole block r3[:] = [] return block_found = False for sub in subs: name, keypairs, subs2 = sub[0], sub[1], sub[2] if block_match(subnames, name, keypairs): block_found = True break if block_found: self.del_process(sub, subnames, okeys) else: logging.debug("block not found") sys.exit("block not found") # Given a block as recursive 3 element list, and the indentation level, # produce a corresponding text that can be written to config file! def r3_to_text(r3, level): logging.debug("%s", pprint.pformat(r3)) if not r3: return "" name, keypairs, subs = r3[0], r3[1], r3[2] indent = level * "\t" s = indent + name + " {\n" for keypair in keypairs: key, value = keypair[0], keypair[1] s += indent + "\t" + "%s = %s;\n" % (key, value.strip()) for sub in subs: s += r3_to_text(sub, level+1) s += indent + "}\n" return s # Exception for arguments and options class ArgError(Exception): def __init__(self, error): self.error = error def validate_key(key): # We allow any identifier as a block name or a key name # except it can't start with an underscore! key_re = re.compile(r"^[a-zA-Z]\w*$") if not key_re.search(key): raise ArgError("'%s' is not a valid key" % key) def validate_value(value): # value should be any printable but should NOT contain a semicolon import string for char in value: if char not in string.printable: raise ArgError("'%s' has non printable characters" % value) if char == ';': raise ArgError("'%s' has semicolon which is not allowed" % value) def validate_opt_pairs(opairs): for key, value in opairs: validate_key(key) validate_value(value) def validate_opt_keys(okeys): for key in okeys: validate_key(key) def validate_blocknames(blocknames): if not blocknames: raise ArgError("no blocknames given") while blocknames: validate_blockname(blocknames) blocknames = next_subnames(blocknames) def validate_blockname(blocknames): name_re = re.compile(r"^[a-zA-Z]\w*$") if not name_re.search(blocknames[0]): raise ArgError("'%s' is not a valid blockname" % blocknames[0]) # export and client blocks require a key and a value to identify them if blocknames[0].lower() == "export" or blocknames[0].lower() == "client": if len(blocknames) < 3: err = "'%s' block requires 2 additional arguments" % blocknames[0] raise ArgError(err) key = blocknames[1] value = blocknames[2] if blocknames[0].lower() == "export": valid_keys = ["export_id", "pseudo", "path"] else: valid_keys = ["clients"] if key.lower() not in valid_keys: err = "'%s' is not in %s" % (key, pprint.pformat(valid_keys)) raise ArgError(err) validate_value(value) def next_subnames(blocknames): assert blocknames if blocknames[0].lower() == "export" or blocknames[0].lower() == "client": # export and client blocks require a key and a value to identify them return blocknames[3:] else: return blocknames[1:] def block_match(blocknames, name, pairs): logging.debug("names:%s, name:%s, pairs:%s", pprint.pformat(blocknames), name, pprint.pformat(pairs)) if blocknames[0].lower() == "export" or blocknames[0].lower() == "client": if blocknames[0].lower() != name.lower(): return False key = blocknames[1] value = blocknames[2] if blocknames[0].lower() == "export": valid_keys = ["export_id", "pseudo", "path"] else: valid_keys = ["clients"] assert key.lower() in valid_keys, "key:%s valid_keys:%s" % (key, pprint.pformat(valid_keys)) for pair in pairs: if pair[0].lower() == key.lower() and pair[1].strip() == value: return True return False # neither export nor client block return blocknames[0].lower() == name.lower() # Make a new r3 list from blockname def make_r3(blocknames): if blocknames[0].lower() == "export" or blocknames[0].lower() == "client": pairs = [[blocknames[1], blocknames[2]]] else: pairs = [] return [blocknames[0], pairs, []] ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/Ganesha/export_mgr.py��������������������������������������0000664�0000000�0000000�00000015416�13242724102�0024412�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # export_mgr.py - ExportMgr DBUS object class. # # Copyright (C) 2014 Panasas Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see <http://www.gnu.org/licenses/>. # # Author: Jim Lieb <jlieb@panasas.com> #-*- coding: utf-8 -*- import sys, time from PyQt4.QtCore import * from PyQt4 import QtDBus, QtGui from collections import namedtuple Export = namedtuple('Export', ['ExportID', 'ExportPath', 'HasNFSv3', 'HasMNT', 'HasNLM4', 'HasRQUOTA', 'HasNFSv40', 'HasNFSv41', 'HasNFSv42', 'Has9P', 'LastTime']) class ExportMgr(QtDBus.QDBusAbstractInterface): ''' org.ganesha.nfsd.exportmgr ''' show_exports = pyqtSignal(tuple, list) display_export = pyqtSignal(int, str, str, str) def __init__(self, service, path, connection, show_status, parent=None): super(ExportMgr, self).__init__(service, path, 'org.ganesha.nfsd.exportmgr', connection, parent) self.show_status = show_status def AddExport(self, conf_path, exp_expr): async = self.asyncCall("AddExport", conf_path, exp_expr) status = QtDBus.QDBusPendingCallWatcher(async, self) status.finished.connect(self.exportadd_done) def UpdateExport(self, conf_path, exp_expr): async = self.asyncCall("UpdateExport", conf_path, exp_expr) status = QtDBus.QDBusPendingCallWatcher(async, self) status.finished.connect(self.exportadd_done) def RemoveExport(self, exp_id): async = self.asyncCall("RemoveExport", int(exp_id)) status = QtDBus.QDBusPendingCallWatcher(async, self) status.finished.connect(self.exportrm_done) def DisplayExport(self, exp_id): async = self.asyncCall("DisplayExport", int(exp_id)) status = QtDBus.QDBusPendingCallWatcher(async, self) status.finished.connect(self.exportdisplay_done) def ShowExports(self): async = self.asyncCall("ShowExports") status = QtDBus.QDBusPendingCallWatcher(async, self) status.finished.connect(self.exportshow_done) def exportadd_done(self, call): reply = QtDBus.QDBusPendingReply(call) if reply.isError(): self.show_status.emit(False, "Error:" + str(reply.error().message())) else: message = reply.argumentAt(0).toPyObject() self.show_status.emit(True, "Done: " + message) def exportrm_done(self, call): reply = QtDBus.QDBusPendingReply(call) if reply.isError(): self.show_status.emit(False, "Error:" + str(reply.error().message())) else: self.show_status.emit(True, "Done") def exportdisplay_done(self, call): reply = QtDBus.QDBusPendingReply(call) if reply.isError(): self.show_status.emit(False, "Error:" + str(reply.error().message())) else: id = reply.argumentAt(0).toPyObject() fullpath = reply.argumentAt(1).toPyObject() pseudopath = reply.argumentAt(2).toPyObject() tag = reply.argumentAt(3).toPyObject() self.display_export.emit(id, fullpath, pseudopath, tag) def exportshow_done(self, call): reply = QtDBus.QDBusPendingReply(call) if reply.isError(): self.show_status.emit(False, "DBUS error:" + str(reply.error().message())) else: ts = (reply.argumentAt(0).toPyObject()[0].toULongLong()[0], reply.argumentAt(0).toPyObject()[1].toULongLong()[0]) exports = [] for export in reply.argumentAt(1).toPyObject(): ex = export.toPyObject() lasttime = ex[10].toPyObject() exp = Export(ExportID = ex[0].toInt()[0], ExportPath = str(ex[1].toString()), HasNFSv3 = ex[2].toBool(), HasMNT = ex[3].toBool(), HasNLM4 = ex[4].toBool(), HasRQUOTA = ex[5].toBool(), HasNFSv40 = ex[6].toBool(), HasNFSv41 = ex[7].toBool(), HasNFSv42 = ex[8].toBool(), Has9P = ex[9].toBool(), LastTime = (lasttime[0].toPyObject(), lasttime[1].toPyObject())) exports.append(exp) self.show_exports.emit(ts, exports) class ExportStats(QtDBus.QDBusAbstractInterface): ''' org.ganesha.nfsd.exportstats ''' def __init__(self, service, path, connection, stats_handler, parent=None): super(ExportStats, self).__init__(service, path, 'org.ganesha.nfsd.exportstats', connection, parent) self.stats_handler = stats_handler def GetNFSv3IO(self, exportid): async = self.asyncCall("GetNFSv3IO", exportid) status = QtDBus.QDBusPendingCallWatcher(async, self) status.finished.connect(self.io_done) def GetNFSv40IO(self, exportid): async = self.asyncCall("GetNFSv40IO", exportid) status = QtDBus.QDBusPendingCallWatcher(async, self) status.finished.connect(self.io_done) def GetNFSv41IO(self, exportid): async = self.asyncCall("GetNFSv41IO", exportid) status = QtDBus.QDBusPendingCallWatcher(async, self) status.finished.connect(self.io_done) def GetNFSv41Layouts(self, exportid): async = self.asyncCall("GetNFSv41Layouts", exportid) status = QtDBus.QDBusPendingCallWatcher(async, self) status.finished.connect(self.layout_done) def io_done(self, call): pass def layout_done(self, call): pass ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/Ganesha/ganesha_mgr_utils.py�������������������������������0000664�0000000�0000000�00000025114�13242724102�0025713�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python # # ganesha_mgr_utils.py - commandline tool utils for managing nfs-ganesha. # # Copyright (C) 2014 IBM. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see <http://www.gnu.org/licenses/>. # # Author: Allison Henderson <achender@vnet.linux.ibm.com> #-*- coding: utf-8 -*- import gobject import sys import time import traceback import dbus.mainloop.glib import dbus from dbus import glib from collections import namedtuple gobject.threads_init() glib.init_threads() Client = namedtuple('Client', ['ClientIP', 'HasNFSv3', 'HasMNT', 'HasNLM4', 'HasRQUOTA', 'HasNFSv40', 'HasNFSv41', 'HasNFSv42', 'Has9P', 'LastTime']) class ClientMgr(): def __init__(self, service, path, interface): self.dbus_service_name = service self.dbus_path = path self.dbus_interface = interface self.bus = dbus.SystemBus() try: self.dbusobj = self.bus.get_object(self.dbus_service_name, self.dbus_path) except: sys.exit("Error: Can't talk to ganesha service on d-bus." \ " Looks like Ganesha is down") def AddClient(self, ipaddr): add_client_method = self.dbusobj.get_dbus_method("AddClient", self.dbus_interface) try: reply = add_client_method(ipaddr) except dbus.exceptions.DBusException as e: return False, e status = reply[0] msg = reply[1] return status, msg def RemoveClient(self, ipaddr): remove_client_method = self.dbusobj.get_dbus_method("RemoveClient", self.dbus_interface) try: reply = remove_client_method(ipaddr) except dbus.exceptions.DBusException as e: return False, e status = reply[0] msg = reply[1] return status, msg def ShowClients(self): show_client_method = self.dbusobj.get_dbus_method("ShowClients", self.dbus_interface) try: reply = show_client_method() except dbus.exceptions.DBusException as e: return False, e, [] time = reply[0] client_array = reply[1] ts = (time[0], time[1]) clients = [] for client in client_array: cl = client lasttime = cl[9] clt = Client(ClientIP = str(cl[0]), HasNFSv3 = cl[1], HasMNT = cl[2], HasNLM4 = cl[3], HasRQUOTA = cl[4], HasNFSv40 = cl[5], HasNFSv41 = cl[6], HasNFSv42 = cl[7], Has9P = cl[8], LastTime = (lasttime[0], lasttime[1])) clients.append(clt) return True, "Done", [ts, clients] Export = namedtuple('Export', ['ExportID', 'ExportPath', 'HasNFSv3', 'HasMNT', 'HasNLM4', 'HasRQUOTA', 'HasNFSv40', 'HasNFSv41', 'HasNFSv42', 'Has9P', 'LastTime']) class ExportMgr(): ''' org.ganesha.nfsd.exportmgr ''' def __init__(self, service, path, interface): self.dbus_service_name = service self.dbus_path = path self.dbus_interface = interface self.bus = dbus.SystemBus() try: self.dbusobj = self.bus.get_object(self.dbus_service_name, self.dbus_path) except: sys.exit("Error: Can't talk to ganesha service on d-bus." \ " Looks like Ganesha is down") def AddExport(self, conf_path, exp_expr): add_export_method = self.dbusobj.get_dbus_method("AddExport", self.dbus_interface) try: msg = add_export_method(conf_path, exp_expr) except dbus.exceptions.DBusException as e: return False, e return True, "Done: "+msg def UpdateExport(self, conf_path, exp_expr): update_export_method = self.dbusobj.get_dbus_method("UpdateExport", self.dbus_interface) try: msg = update_export_method(conf_path, exp_expr) except dbus.exceptions.DBusException as e: return False, e return True, "Done: "+msg def RemoveExport(self, exp_id): rm_export_method = self.dbusobj.get_dbus_method("RemoveExport", self.dbus_interface) try: rm_export_method(int(exp_id)) except dbus.exceptions.DBusException as e: return False, e return True, "Done" def DisplayExport(self, exp_id): display_export_method = self.dbusobj.get_dbus_method("DisplayExport", self.dbus_interface) try: id, fullpath, pseudopath, tag = display_export_method(int(exp_id)) except dbus.exceptions.DBusException as e: return False, e, [] return True, "Done", [id, fullpath, pseudopath, tag] def ShowExports(self): show_export_method = self.dbusobj.get_dbus_method("ShowExports", self.dbus_interface) try: reply = show_export_method() except dbus.exceptions.DBusException as e: return False, e, [] time = reply[0] export_array = reply[1] ts = (time[0], time[1]) exports = [] for export in export_array: ex = export lasttime = ex[10] exp = Export(ExportID = ex[0], ExportPath = str(ex[1]), HasNFSv3 = ex[2], HasMNT = ex[3], HasNLM4 = ex[4], HasRQUOTA = ex[5], HasNFSv40 = ex[6], HasNFSv41 = ex[7], HasNFSv42 = ex[8], Has9P = ex[9], LastTime = (lasttime[0], lasttime[1])) exports.append(exp) return True, "Done", [ts, exports] class AdminInterface(): ''' org.ganesha.nfsd.admin interface ''' def __init__(self, service, path, interface): self.dbus_service_name = service self.dbus_path = path self.dbus_interface = interface self.bus = dbus.SystemBus() try: self.dbusobj = self.bus.get_object(self.dbus_service_name, self.dbus_path) except: sys.exit("Error: Can't talk to ganesha service on d-bus." \ " Looks like Ganesha is down") def grace(self, ipaddr): grace_method = self.dbusobj.get_dbus_method("grace", self.dbus_interface) try: reply = grace_method(ipaddr) except dbus.exceptions.DBusException as e: return False, e status = reply[0] msg = reply[1] return status, msg def shutdown(self): shutdown_method = self.dbusobj.get_dbus_method("shutdown", self.dbus_interface) try: reply = shutdown_method() except dbus.exceptions.DBusException as e: return False, e status = reply[0] msg = reply[1] return status, msg def purge_netgroups(self): method = self.dbusobj.get_dbus_method("purge_netgroups", self.dbus_interface) try: reply = method() except dbus.exceptions.DBusException as e: return False, e status = reply[0] msg = reply[1] return status, msg LOGGER_PROPS = 'org.ganesha.nfsd.log.component' class LogManager(): ''' org.ganesha.nfsd.log.component ''' def __init__(self, service, path, interface): self.dbus_service_name = service self.dbus_path = path self.dbus_interface = interface self.bus = dbus.SystemBus() try: self.dbusobj = self.bus.get_object(self.dbus_service_name, self.dbus_path) except: sys.exit("Error: Can't talk to ganesha service on d-bus. " \ " Looks like Ganesha is down") def GetAll(self): getall_method = self.dbusobj.get_dbus_method("GetAll", self.dbus_interface) try: dictionary = getall_method(LOGGER_PROPS) except dbus.exceptions.DBusException as e: return False, e, {} prop_dict = {} for key in dictionary.keys(): prop_dict[key] = dictionary[key] return True, "Done", prop_dict def Get(self, property): get_method = self.dbusobj.get_dbus_method("Get", self.dbus_interface) try: level = get_method(LOGGER_PROPS, property) except dbus.exceptions.DBusException as e: return False, e, 0 return True, "Done", level def Set(self, property, setval): set_method = self.dbusobj.get_dbus_method("Set", self.dbus_interface) try: set_method(LOGGER_PROPS, property, setval) except dbus.exceptions.DBusException as e: return False, e return True, "Done" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/Ganesha/glib_dbus_stats.py���������������������������������0000775�0000000�0000000�00000043023�13242724102�0025372�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python # You must initialize the gobject/dbus support for threading # before doing anything. import gobject import sys import time gobject.threads_init() from dbus import glib glib.init_threads() # Create a session bus. import dbus class RetrieveExportStats(): def __init__(self): self.dbus_service_name = "org.ganesha.nfsd" self.dbus_exportstats_name = "org.ganesha.nfsd.exportstats" self.dbus_exportmgr_name = "org.ganesha.nfsd.exportmgr" self.export_interface = "/org/ganesha/nfsd/ExportMgr" self.bus = dbus.SystemBus() try: self.exportmgrobj = self.bus.get_object(self.dbus_service_name, self.export_interface) except: print "Error: Can't talk to ganesha service on d-bus. Looks like Ganesha is down" sys.exit() # NFSv3/NFSv4/NLM/MNT/QUOTA stats over all exports def fast_stats(self): stats_op = self.exportmgrobj.get_dbus_method("GetFastOPS", self.dbus_exportstats_name) return FastStats(stats_op()) # NFSv3/NFSv40/NFSv41/NFSv42/NLM4/MNTv1/MNTv3/RQUOTA totalled over all exports def global_stats(self): stats_op = self.exportmgrobj.get_dbus_method("GetGlobalOPS", self.dbus_exportstats_name) return GlobalStats(stats_op()) # cache inode stats def inode_stats(self): stats_op = self.exportmgrobj.get_dbus_method("ShowCacheInode", self.dbus_exportstats_name) return InodeStats(stats_op()) # list of all exports def export_stats(self): stats_op = self.exportmgrobj.get_dbus_method("ShowExports", self.dbus_exportmgr_name) return ExportStats(stats_op()) # NFSv3/NFSv4/NLM/MNT/QUOTA stats totalled for a single export def total_stats(self, export_id): stats_op = self.exportmgrobj.get_dbus_method("GetTotalOPS", self.dbus_exportstats_name) stats_dict = {} if export_id < 0: export_list = self.export_stats() for exportid in export_list.exportids(): stats_dict[exportid] = stats_op(exportid) else: stats_dict[export_id] = stats_op(int(export_id)) return TotalStats(stats_dict) def io_stats(self, stats_op, export_id): stats_dict = {} if export_id < 0: export_list = self.export_stats() for exportid in export_list.exportids(): stats_dict[exportid] = stats_op(exportid) return stats_dict else: stats_dict[export_id] = stats_op(int(export_id)) return stats_dict def v3io_stats(self, export_id): stats_op = self.exportmgrobj.get_dbus_method("GetNFSv3IO", self.dbus_exportstats_name) return ExportIOv3Stats(self.io_stats(stats_op, export_id)) def v4io_stats(self, export_id): stats_op = self.exportmgrobj.get_dbus_method("GetNFSv40IO", self.dbus_exportstats_name) return ExportIOv4Stats(self.io_stats(stats_op, export_id)) def pnfs_stats(self, export_id): stats_op = self.exportmgrobj.get_dbus_method("GetNFSv41Layouts", self.dbus_exportstats_name) stats_dict = {} if export_id < 0: export_list = self.export_stats() for exportid in export_list.exportids(): stats_dict[exportid] = stats_op(exportid) return PNFSStats(stats_dict) else: stats_dict[export_id] = stats_op(int(export_id)) return PNFSStats(stats_dict) # Reset the statistics counters for all def reset_stats(self): stats_state = self.exportmgrobj.get_dbus_method("ResetStats", self.dbus_exportstats_name) return StatsReset(stats_state()) # fsal stats def fsal_stats(self, fsal): stats_op = self.exportmgrobj.get_dbus_method("GetFSALStats", self.dbus_exportstats_name) return DumpFSALStats(stats_op(fsal)) # enable stats def enable_stats(self, stat_type): stats_state = self.exportmgrobj.get_dbus_method("EnableStats", self.dbus_exportstats_name) return StatsEnable(stats_state(stat_type)) # disable stats def disable_stats(self, stat_type): stats_state = self.exportmgrobj.get_dbus_method("DisableStats", self.dbus_exportstats_name) return StatsDisable(stats_state(stat_type)) class RetrieveClientStats(): def __init__(self): self.dbus_service_name = "org.ganesha.nfsd" self.dbus_clientstats_name = "org.ganesha.nfsd.clientstats" self.dbus_clientmgr_name = "org.ganesha.nfsd.clientmgr" self.client_interface = "/org/ganesha/nfsd/ClientMgr" self.bus = dbus.SystemBus() try: self.clientmgrobj = self.bus.get_object(self.dbus_service_name, self.client_interface) except: print "Error: Can't talk to ganesha service on d-bus. Looks like Ganesha is down" sys.exit() # delegation stats related to a single client ip def deleg_stats(self, ip): stats_op = self.clientmgrobj.get_dbus_method("GetDelegations", self.dbus_clientstats_name) return DelegStats(stats_op(ip)) def list_clients(self): stats_op = self.clientmgrobj.get_dbus_method("ShowClients", self.dbus_clientmgr_name) return Clients(stats_op()) class Clients(): def __init__(self, clients): self._clients = clients def __str__(self): output = ("\nTimestamp: " + time.ctime(self._clients[0][0]) + str(self._clients[0][1]) + " nsecs" + "\nClient List:\n" ) for client in self._clients[1]: output += ("\n\nAddress: " + client[0] + "\nNFSv3 stats available: " + str(client[1]) + "\nMNT stats available: " + str(client[2]) + "\nNLM4 stats available: " + str(client[3]) + "\nRQUOTA stats available: " + str(client[4]) + "\nNFSv4.0 stats available " + str(client[5]) + "\nNFSv4.1 stats available: " + str(client[6]) + "\nNFSv4.2 stats available: " + str(client[7]) + "\n9P stats available: " + str(client[8]) ) return output class DelegStats(): def __init__(self, stats): self.status = stats[1] if stats[1] == "OK": self.timestamp = (stats[2][0], stats[2][1]) self.curr_deleg = stats[3][0] self.curr_recall = stats[3][1] self.fail_recall = stats[3][2] self.num_revokes = stats[3][3] def __str__(self): if self.status != "OK": return ("GANESHA RESPONSE STATUS: " + self.status) else: return ( "GANESHA RESPONSE STATUS: " + self.status + "\nTimestamp: " + time.ctime(self.timestamp[0]) + str(self.timestamp[1]) + " nsecs" + "\nCurrent Delegations: " + str(self.curr_deleg) + "\nCurrent Recalls: " + str(self.curr_recall) + "\nCurrent Failed Recalls: " + str(self.fail_recall) + "\nCurrent Number of Revokes: " + str(self.num_revokes) ) class Export(): def __init__(self, export): self.exportid = export[0] self.path = export[1] self.nfsv3_stats_avail = export[2] self.nfsv40_stats_avail = export[6] self.nfsv41_stats_avail = export[7] self.nfsv42_stats_avail = export[8] self.mnt_stats_avail = export[3] self.nlmv4_stats_avail = export[4] self.rquota_stats_avail = export[5] self._9p_stats_avail = export[9] def __str__(self): return ("\nExport id: " + str(self.exportid) + "\nPath: " + self.path + "\nNFSv3 stats available: " + str(self.nfsv3_stats_avail) + "\nNFSv4.0 stats available: " + str(self.nfsv40_stats_avail) + "\nNFSv4.1 stats available: " + str(self.nfsv41_stats_avail) + "\nNFSv4.2 stats available: " + str(self.nfsv42_stats_avail) + "\nMNT stats available: " + str(self.mnt_stats_avail) + "\nNLMv4 stats available: " + str(self.nlmv4_stats_avail) + "\nRQUOTA stats available: " + str(self.rquota_stats_avail) + "\n9p stats available: " + str(self._9p_stats_avail) + "\n") class ExportStats(): def __init__(self, exports): self.timestamp = (exports[0][0], exports[0][1]) self.exports = {} for export in exports[1]: exportid = export[0] self.exports[exportid] = Export(export) def __str__(self): output = ( "Timestamp: " + time.ctime(self.timestamp[0]) + str(self.timestamp[1]) + " nsecs") for exportid in self.exports: output += str(self.exports[exportid]) return output def exportids(self): return self.exports.keys() class GlobalStats(): def __init__(self, stats): self.success = stats[0] self.status = stats[1] if self.success: self.timestamp = (stats[2][0], stats[2][1]) self.nfsv3_total = stats[3][1] self.nfsv40_total = stats[3][3] self.nfsv41_total = stats[3][5] self.nfsv42_total = stats[3][7] def __str__(self): output = "" if not self.success: return "No NFS activity, GANESHA RESPONSE STATUS: " + self.status if self.status != "OK": output += self.status + "\n" output += ("Timestamp: " + time.ctime(self.timestamp[0]) + str(self.timestamp[1]) + " nsecs" + "\nTotal NFSv3 ops: " + str(self.nfsv3_total) + "\nTotal NFSv4.0 ops: " + str(self.nfsv40_total) + "\nTotal NFSv4.1 ops: " + str(self.nfsv41_total) + "\nTotal NFSv4.2 ops: " + str(self.nfsv42_total)) return output class InodeStats(): def __init__(self, stats): self.status = stats[1] if stats[1] != "OK": return self.timestamp = (stats[2][0], stats[2][1]) self.cache_requests = stats[3][1] self.cache_hits = stats[3][3] self.cache_miss = stats[3][5] self.cache_conflict = stats[3][7] self.cache_add = stats[3][9] self.cache_mapping = stats[3][11] def __str__(self): if self.status != "OK": return "No NFS activity, GANESHA RESPONSE STATUS: " + self.status return ( "Timestamp: " + time.ctime(self.timestamp[0]) + str(self.timestamp[1]) + " nsecs" + "\nInode Cache Requests: " + str(self.cache_requests) + "\nInode Cache Hits: " + str(self.cache_hits) + "\nInode Cache Misses: " + str(self.cache_miss) + "\nInode Cache Conflicts:: " + str(self.cache_conflict) + "\nInode Cache Adds: " + str(self.cache_add) + "\nInode Cache Mapping: " + str(self.cache_mapping) ) class FastStats(): def __init__(self, stats): self.stats = stats def __str__(self): if not self.stats[0]: return "No NFS activity, GANESHA RESPONSE STATUS: " + self.stats[1] else: if self.stats[1] != "OK": output = self.stats[1]+ "\n" else: output = "" output += ("Timestamp: " + time.ctime(self.stats[2][0]) + str(self.stats[2][1]) + " nsecs" + "\nGlobal ops:\n" ) # NFSv3, NFSv4, NLM, MNT, QUOTA self.stats for i in range(0,len(self.stats[3])-1): if ":" in str(self.stats[3][i]): output += self.stats[3][i] + "\n" elif str(self.stats[3][i]).isdigit(): output += "\t%s" % (str(self.stats[3][i]).rjust(8)) + "\n" else: output += "%s: " % (self.stats[3][i].ljust(20)) return output class ExportIOv3Stats(): def __init__(self, stats): self.stats = stats def __str__(self): output = "" for key in self.stats: if not self.stats[key][0]: output += "EXPORT %s: %s\n" % (key, self.stats[key][1]) continue if self.stats[key][1] != "OK": output += self.stats[key][1] + "\n" output += ( "\nEXPORT %s:" % (key) + "\n\t\trequested\ttransferred\t total\t errors\t latency\tqueue wait" + "\nREADv3: " ) for stat in self.stats[key][3]: output += "\t" + str(stat).rjust(8) output += "\nWRITEv3: " for stat in self.stats[key][4]: output += "\t" + str(stat).rjust(8) return output class ExportIOv4Stats(): def __init__(self, stats): self.stats = stats def __str__(self): output = "" for key in self.stats: if not self.stats[key][0]: output += "\nEXPORT %s: %s\n" % (key, self.stats[key][1]) continue if self.stats[key][1] != "OK": output += self.stats[key][1] + "\n" output += ("EXPORT %s:" % (key) + "\n\t\trequested\ttransferred\t total\t errors\t latency\tqueue wait" + "\nREADv4: ") for stat in self.stats[key][3]: output += "\t" + str(stat).rjust(8) output += "\nWRITEv4: " for stat in self.stats[key][4]: output += "\t" + str(stat).rjust(8) output += "\n\n" return output class TotalStats(): def __init__(self, stats): self.stats = stats def __str__(self): output = "" for key in self.stats: if not self.stats[key][0]: return "No NFS activity, GANESHA RESPONSE STATUS: " + self.stats[key][1] if self.stats[key][1] != "OK": output += self.stats[key][1] + "\n" output += ("Total stats for export id: " + str(key) + "\nTimestamp: " + time.ctime(self.stats[key][2][0]) + str(self.stats[key][2][1]) + " nsecs\n") for i in range(0,len(self.stats[key][3])-1, 2): output += "%s: %s\n" % (self.stats[key][3][i], self.stats[key][3][i+1]) return output class PNFSStats(): def __init__(self, stats): self.stats = stats def __str__(self): for key in self.stats: if self.stats[key][1] != "OK": return "No NFS activity, GANESHA RESPONSE STATUS: \n" + self.stats[key][1] output = ("Total stats for export id" + str(key) + "\nTimestamp: " + time.ctime(self.stats[key][2][0]) + str(self.stats[key][2][1]) + " nsecs" + "\nStatistics for:" + str(exports[1]) + "\n\t\ttotal\terrors\tdelays" + + "getdevinfo ") for stat in self.stats[key][3]: output += "\t" + stat output += "layout_get " for stat in self.stats[key][4]: output += "\t" + stat output += "layout_commit " for stat in self.stats[key][5]: output += "\t" + stat output += "layout_return " for stat in self.stats[key][6]: output += "\t" + stat output += "recall " for stat in self.stats[key][7]: output += "\t" + stat return output class StatsReset(): def __init__(self, status): self.status = status def __str__(self): if self.status[1] != "OK": return "Failed to reset statistics, GANESHA RESPONSE STATUS: " + self.status[1] else: return "Successfully resetted statistics counters" class DumpFSALStats(): def __init__(self, stats): self.stats = stats def __str__(self): output = "" if not self.stats[0]: return ("GANESHA RESPONSE STATUS: " + self.stats[1]) else: if self.stats[1] != "OK": output += self.stats[1] + "\n" output += ("Timestamp: " + time.ctime(self.stats[2][0]) + str(self.stats[2][1]) + " nsecs\n") output += "FSAL Stats: \n Total Supported Ops : " + "%s\n" % (str(self.stats[3][0]).rjust(8)) output += "\t Op-Name Op-Code Total Res:Avg Min Max \n" i = 1 tot_len = len(self.stats[3])-1 while i < tot_len: output += "\n" + (self.stats[3][i+0]).ljust(20) + "%s" % (str(self.stats[3][i+1]).rjust(4)) if (i+2) < tot_len: if str(self.stats[3][i+2]).isdigit(): if (i+6) < tot_len: output += "%s" % (str(self.stats[3][i+2]).rjust(8)) output += " %8.2f" % (self.stats[3][i+3]) output += " %s" % (str(self.stats[3][i+4]).rjust(8)) output += " %s" % (str(self.stats[3][i+5]).rjust(8)) i += 6 else: break else: i += 2 else: break return output class StatsEnable(): def __init__(self, status): self.status = status def __str__(self): if self.status[1] != "OK": return "Failed to enable statistics counting, GANESHA RESPONSE STATUS: " + self.status[1] else: return "Successfully enabled statistics counting" class StatsDisable(): def __init__(self, status): self.status = status def __str__(self): if self.status[1] != "OK": return "Failed to disable statistics counting, GANESHA RESPONSE STATUS: " + self.status[1] else: return "Successfully disabled statistics counting" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/Ganesha/io_stats.py����������������������������������������0000664�0000000�0000000�00000003453�13242724102�0024047�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # io_stats.py - IOstat table object. # # Copyright (C) 2014 Panasas Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see <http://www.gnu.org/licenses/>. # # Author: Jim Lieb <jlieb@panasas.com> #-*- coding: utf-8 -*- from collections import namedtuple # Basic I/O reply and io stats structs BasicIO = namedtuple('BasicIO', [requested, transferred, total_ops, errors, latency, queue_wait]) IOReply = namedtuple('IOReply', [status, errormsg, sampletime, read, write]) # pNFS layout stats reply and stats structs Layout = namedtuple('Layout', [total_ops, errors, delays]) pNFSReply = namedtuple('pNFSReply', [status, errormsg, sampletime, getdevinfo, layout_get, layout_commit, layout_return, layout_recall]) class IOstat(Object): def __init__(self, stat): pass ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/Ganesha/log_mgr.py�����������������������������������������0000664�0000000�0000000�00000007444�13242724102�0023654�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # log_mgr.py - LogManager DBus object class. # # Copyright (C) 2014 Panasas Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see <http://www.gnu.org/licenses/>. # # Author: Jim Lieb <jlieb@panasas.com> #-*- coding: utf-8 -*- from PyQt4 import QtCore, QtDBus ADMIN_OBJECT = '/org/ganesha/nfsd/admin' PROP_INTERFACE = 'org.freedesktop.DBus.Properties' LOGGER_PROPS = 'org.ganesha.nfsd.log.component' class LogManager(QtDBus.QDBusAbstractInterface): ''' org.ganesha.nfsd.log.component ''' show_components = QtCore.pyqtSignal(dict) show_level = QtCore.pyqtSignal(str) def __init__(self, service, connection, show_status, parent=None): super(LogManager, self).__init__(service, ADMIN_OBJECT, PROP_INTERFACE, connection, parent) self.show_status = show_status def GetAll(self): async = self.asyncCall("GetAll", LOGGER_PROPS) status = QtDBus.QDBusPendingCallWatcher(async, self) status.finished.connect(self.GetAll_done) def GetAll_done(self, call): reply = QtDBus.QDBusPendingReply(call) if reply.isError(): self.show_status.emit(False, "DBus error:" + str(reply.error().message())) else: # what follows is DBus+Qt magic. We get a Variant object back # which contains a "map", aka "dict" in python. Each item in # the map has a variant as a key and a variant as the value # first unwrap the top variant into d... # then walk d, unwrap the variant key to store the unwrapped # variant value into a string value. prop_dict = {} d = reply.value().toPyObject() for key in d.keys(): prop_dict[str(key.toString())] = str(d[key].toPyObject().toString()) self.show_components.emit(prop_dict) def Get(self, property): async = self.asyncCall("Get", LOGGER_PROPS, property) status = QtDBus.QDBusPendingCallWatcher(async, self) status.finished.connect(self.Get_done) def Get_done(self, call): reply = QtDBus.QDBusPendingReply(call) if reply.isError(): self.show_status.emit(False, "DBUS error:" + str(reply.error().message())) else: level = str(reply.value().toPyObject().toString()) self.show_level.emit(level) def Set(self, property, setval): qval = QtDBus.QDBusVariant() qval.setVariant(str(str(setval))) async = self.asyncCall("Set", LOGGER_PROPS, property, qval) status = QtDBus.QDBusPendingCallWatcher(async, self) status.finished.connect(self.Set_done) def Set_done(self, call): reply = QtDBus.QDBusPendingReply(call) if reply.isError(): self.show_status.emit(False, "DBUS error:" + str(reply.error().message() + str(reply.error().name()))) else: self.show_status.emit(True, "Done") ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/README.txt�������������������������������������������������0000664�0000000�0000000�00000011205�13242724102�0021772�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������This README is addressed to developers who plan to either improve this reference implementation of DBus control for Ganesha or to use it as a reference for their own implementation. Those who are looking for information about setting up DBus and Ganesha in a running system should consult the installation and administration pages of the project wiki. This directory contains a reference implementation of tools for managing NFS Ganesha through DBus. Everything here is in Python 2.7+ and PyQt 4.2+. Ganeshactl is a PyQt GUI application for managing NFS Ganesha through DBus. There are also python scripts that can be called from the shell to do most of the management functions. Under both is a set of python classes that implement the details of the DBus transactions. The GUI is a basic Qt application. I'm sure the useability people will be all over this thing complaining about my poor design taste and useability ignorance. Note two things. First, this is a reference implementation to have a functioning set of core classes. Second, see the next section, make it useable according to your designer sensibilities, and commit the resulting .ui file(s). Editing the UI -------------- The UI files (in python) are generated from XML files that are created by "designer-qt4". DO NOT edit the .py files directly. Only the *.ui files are committed to git and the .py files are overwritten by the build process. The process of changing/enhancing the UI follows some simple steps: 1. Use designer-qt4 to edit the .ui file. This tool will manage all of the details like buttons and dialog boxes. 2. Generate the corresponding .py file with the 'pyuic4' tool. This takes the XML that describes the UI and generates the .py. 3. Edit ganeshact.py to connect up logic to the UI. It helps here to examine the pyuic4 output. A good start is to have the new methods display something to the status bar. This ensures that your new GUI's events are wired properly. 4. If you add a new class for a new DBus interface, get an instance of it in the main window's __init__ method so it can be referenced properly. Script Runtime -------------- The python "distutils" packaging is now in place to make these scripts installable. However, there are some limitations that users/developers should be aware of. 1. The scripts work properly if they are invoked from src/scripts/ganeshactl 2. They will also work properly if installed by the RPM tools. 3. They will _not_ work if installed in /usr/local without intervention. The issue here is how Python manages how it finds its modules. The following script fragment illustrates the issue: from Ganesha.export_mgr import ExportMgr This tells the python interpreter to search for the module in its "known" locations in the filesystem. The python developers have made some infrastructure design decisions that make the mix of system packaging, such as RPM or Debian difficult. The built-in assumption in Python that its packages and the interpreter are in the same directory tree makes the mix of '/usr/local' and distribution packaging incompatible without intervention. The case for (1) works because python does have a search order, i.e. first search the directory tree where the script is located followed by the system location which, in the case of RPM would be /usr. The case for (2) works the same way because the Ganesha modules would be installed in the system location. The case for (3) breaks because python cannot find the modules after looking in the script directory tree and the system location. Note that one cannot invoke a script in /usr/local/bin by its full path. This is because python expects a different directory structure in the local directory case. This is confusing and broken but there has been lots of discussion on the topic over the years but this is one of the things that are baked-in deeply in the python ecosystem. For those users/developers who install NFS-Ganesha from source and install in /usr/local, some extra, manual effort is required. This is not really a satisfactory solution, as noted in the python forums and listserv archives, but it is a workaround for those who don't really want to carve up the python code or their python installation. Do the following in the shell or shell scripts: # export PYTHONPATH=/usr/local/lib/python2.7/site-packages # manage_exports display 79 This shell fragment sets up the environment to run these scripts located in /usr/local/bin. There are side-effects of setting PYTHONPATH in the environment and if one wants a further explanation of all this, do a search for "PYTHONPATH". There is a lot more detail in the forums than can be condensed into this README. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/client_stats_9pOps.py��������������������������������������0000775�0000000�0000000�00000003107�13242724102�0024441�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python # You must initialize the gobject/dbus support for threading # before doing anything. import gobject import sys gobject.threads_init() from dbus import glib glib.init_threads() # Create a session bus. import dbus bus = dbus.SystemBus() # Create an object that will proxy for a particular remote object. try: admin = bus.get_object("org.ganesha.nfsd", "/org/ganesha/nfsd/ClientMgr") except: # catch *all* exceptions print "Error: Can't talk to ganesha service on d-bus. Looks like Ganesha is down" exit(1) # call method ganesha_9pOpstats = admin.get_dbus_method('Get9pOpStats', 'org.ganesha.nfsd.clientstats') # get parameters if len(sys.argv) != 2: print "Usage: %s client_ipaddr" % sys.argv[0] exit(1) client_ipaddr=sys.argv[1] # for each 9p protocol operation OpNames=("_9P_TSTATFS", "_9P_TLOPEN", "_9P_TLCREATE", "_9P_TSYMLINK", "_9P_TMKNOD", "_9P_TRENAME", "_9P_TREADLINK", "_9P_TGETATTR", "_9P_TSETATTR", "_9P_TXATTRWALK", "_9P_TXATTRCREATE", "_9P_TREADDIR", "_9P_TFSYNC", "_9P_TLOCK", "_9P_TGETLOCK", "_9P_TLINK", "_9P_TMKDIR", "_9P_TRENAMEAT", "_9P_TUNLINKAT", "_9P_TVERSION", "_9P_TAUTH", "_9P_TATTACH", "_9P_TFLUSH", "_9P_TWALK", "_9P_TOPEN", "_9P_TCREATE", "_9P_TREAD", "_9P_TWRITE", "_9P_TCLUNK", "_9P_TREMOVE", "_9P_TSTAT", "_9P_TWSTAT") for opname in OpNames: opstats=ganesha_9pOpstats(client_ipaddr, opname) status=opstats[0] errmsg=opstats[1] if (not(status)): print errmsg break total=opstats[3][0] if (total != 0): print "%-16s\t%ld" % (opname, total) sys.exit(0); ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/export_stats_9pOps.py��������������������������������������0000775�0000000�0000000�00000003146�13242724102�0024507�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python # You must initialize the gobject/dbus support for threading # before doing anything. import gobject import sys gobject.threads_init() from dbus import glib glib.init_threads() # Create a session bus. import dbus bus = dbus.SystemBus() # Create an object that will proxy for a particular remote object. try: admin = bus.get_object("org.ganesha.nfsd", "/org/ganesha/nfsd/ExportMgr") except: # catch *all* exceptions print "Error: Can't talk to ganesha service on d-bus. Looks like Ganesha is down" exit(1) # call method ganesha_9pOpstats = admin.get_dbus_method('Get9pOpStats', 'org.ganesha.nfsd.exportstats') # get parameters if len(sys.argv) != 2 or not(sys.argv[1].isdigit()): print "Usage: %s export_id" % sys.argv[0] exit(1) export_id=dbus.UInt16(sys.argv[1]) # for each 9p protocol operation OpNames=("_9P_TSTATFS", "_9P_TLOPEN", "_9P_TLCREATE", "_9P_TSYMLINK", "_9P_TMKNOD", "_9P_TRENAME", "_9P_TREADLINK", "_9P_TGETATTR", "_9P_TSETATTR", "_9P_TXATTRWALK", "_9P_TXATTRCREATE", "_9P_TREADDIR", "_9P_TFSYNC", "_9P_TLOCK", "_9P_TGETLOCK", "_9P_TLINK", "_9P_TMKDIR", "_9P_TRENAMEAT", "_9P_TUNLINKAT", "_9P_TVERSION", "_9P_TAUTH", "_9P_TATTACH", "_9P_TFLUSH", "_9P_TWALK", "_9P_TOPEN", "_9P_TCREATE", "_9P_TREAD", "_9P_TWRITE", "_9P_TCLUNK", "_9P_TREMOVE", "_9P_TSTAT", "_9P_TWSTAT") for opname in OpNames: opstats=ganesha_9pOpstats(export_id, opname) status=opstats[0] errmsg=opstats[1] if (not(status)): print errmsg break total=opstats[3][0] if (total != 0): print "%-16s\t%ld" % (opname, total) sys.exit(0); ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/fake_recall.py���������������������������������������������0000775�0000000�0000000�00000001657�13242724102�0023113�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python import getopt, sys import gobject gobject.threads_init() from dbus import glib glib.init_threads() import dbus def usage(): print "fake_recall <clientid>" def main(): try: opts, args = getopt.getopt(sys.argv[1:], "c", []) if len(args) < 1: usage() sys.exit(2) clientid = args[0] print clientid bus = dbus.SystemBus() cbsim = bus.get_object("org.ganesha.nfsd", "/org/ganesha/nfsd/CBSIM") print cbsim.Introspect() # call method fake_recall = cbsim.get_dbus_method('fake_recall', 'org.ganesha.nfsd.cbsim') print fake_recall(dbus.UInt64(clientid)) except getopt.GetoptError, err: print str(err) # will print something like "option -a not recognized" usage() sys.exit(2) if __name__ == "__main__": main() ���������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/ganesha-admin.py�������������������������������������������0000775�0000000�0000000�00000004451�13242724102�0023352�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python # # ganesha-admin.py - commandline tool for admin of nfs-ganesha. # # Copyright (C) 2014 Panasas Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see <http://www.gnu.org/licenses/>. # # Author: Jim Lieb <jlieb@panasas.com> #-*- coding: utf-8 -*- import sys from PyQt4 import QtCore, QtDBus from PyQt4.QtGui import QApplication from dbus.mainloop.qt import DBusQtMainLoop from Ganesha.admin import AdminInterface SERVICE = 'org.ganesha.nfsd' class ServerAdmin(QtCore.QObject): show_status = QtCore.pyqtSignal(bool, str) def __init__(self, sysbus, parent=None): super(ServerAdmin, self).__init__() self.admin = AdminInterface(SERVICE, '/org/ganesha/nfsd/admin', sysbus, self.show_status) self.show_status.connect(self.status_message) def shutdown(self): self.admin.shutdown() print "Shutting down server." def reload(self): self.admin.reload() print "Reload server configuration." def grace(self, ipaddr): self.admin.grace(ipaddr) print "Start grace period." def status_message(self, status, errormsg): print "Returns: status = %s, %s" % (str(status), errormsg) sys.exit() # Main if __name__ == '__main__': app = QApplication(sys.argv) loop = DBusQtMainLoop(set_as_default=True) sysbus = QtDBus.QDBusConnection.systemBus() ganesha = ServerAdmin(sysbus) if sys.argv[1] == "shutdown": ganesha.shutdown() elif sys.argv[1] == "reload": ganesha.reload() elif sys.argv[1] == "grace": ganesha.grace(argv[2]) else: print "Unknown/missing command" sys.exit() app.exec_() �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/ganesha_conf.man�������������������������������������������0000664�0000000�0000000�00000012211�13242724102�0023402�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������.\" This is a comment .\" This is a comment .TH ganesha_conf 8 "21 Mar 2017" .SH NAME ganesha_conf \- Ganesha configuration editor .SH SYNOPSIS .B ganesha_conf set .I <block-description> .B [--key1 value1 [--key2 value2]...] .br .B ganesha_conf del .I <block-description> .B [--key1 [--key2 ]...] .SH DESCRIPTION .PP The .B set command adds or changes the given key value parameters in the block. It also creates the block if the block corresponding to the given .I block description (see section .B BLOCK DESCRIPTION below) is not present in the configuration. .PP The .B del command deletes given keys from the ganesha configuration block described by the block description. It will delete the block itself if no keys are provided. .SH BLOCK DESCRIPTION: .PP .I Block description is a list of block names and possibly some key value parameters that uniquely identify a ganesha configuration block. .PP NFS Ganesha configuration file contains a list of blocks. Each block starts with a block .B name followed by a .B left brace, then a list of .B key = value; entries. The block may optionally have .B sub blocks (note the recursive definition!). Finally, the block ends with a .B right brace. (Note that .B key = value; entries can come after a sub block as well, but we don't allow this with ganesha_conf editor! All key value entries should come before any sub blocks.) An example of a ganesha configuration block: .PP .in +4n .nf nfs_core_param { Nb_Worker = 256; Clustered = TRUE; NFS_Protocols = 3,4; } .fi .in .PP Since there should be only one .B nfs_core_param block, we just need the name of the block to uniquely identify it. So "nfs_core_param" would be its block description! .PP An example of a ganesha configuration block with a couple sub blocks: .PP .in +4n .nf log { default_log_level = EVENT; format { date_format = ISO-8601; time_format = ISO-8601; thread_name = TRUE; } components { all = EVENT; } } .fi .in .PP Ganesha configuration should have only one .B log block as well, so "log" would identify the log block. To identify .B format sub block inside the .B log block, "log format" would be the block description for the above .B format sub block. Similarly "log components" would be the block description for the above .B components sub block. .PP An .B export block is special in that there can be many export blocks, one for each export. A .B client block is also special. There can be many .B client blocks and they are always sub blocks inside .B export blocks. .I export_id key value uniquely identifies an .B export block. .I clients key value uniquely identifies a .B client block inside a given .B export block. .PP Here are couple .B export blocks with couple .B client blocks in them: .PP .in +4n .nf export { export_id = 1; path = /fs1/export1; pseudo = /fs1/export1; manage_gids = true; client { clients = 192.168.122.31; access_type = RW; } client { clients = *; access_type = RO; } } export { export_id = 2; path = /fs1/export2; pseudo = /fs1/export2; manage_gids = true; client { clients = 192.168.122.31; access_type = RW; } client { clients = 192.168.122.32; access_type = RO; } } .fi .in .PP To identify the correct .B export block, we need to supply its .I export_id. For example "export export_id 2" identifies the second .B export block above. .B export blocks can be uniquely identified by .I pseudo or .I path keys in some environments. One could specify "export path /fs1/export2" to identify the second .B export block as well. Similarly, a .B client block needs additional .I clients key value to identify the correct .B client block. For example, "export export_id 2 client clients 192.168.122.31" identifies the first .B client block in the second .B export block above! .SH EXAMPLES: 1. To change number of ganesha worker threads: .PP ganesha_conf set nfs_core_param --nb_wroker 256 .PP 2. To change the date and time format of ganesha log messages: .PP ganesha_conf set log format --date_format ISO-8601 --time_format ISO-8601 .PP 3. Create an export and allow client with IP address 192.168.122.31 to be able to do read write and all other clients to do read only: .PP ganesha_conf set export path /fs1/export2 --export_id 2 --pseudo /fs1/export2 --manage_gids true .br ganesha_conf set export path /fs1/export2 client clients 192.168.122.31 --access_type RW .br ganesha_conf set export path /fs1/export2 client clients 192.168.122.32 --access_type RO .SH NOTES: .B ganesha_conf by default uses /etc/ganesha/ganesha.conf file as the configuration file. If your environment uses a different file (or set of files), you can use .B CONFFILE environment variable to override the default configuration file. For example, "CONFFILE=/etc/ganesha/ganesha.main.conf ganesha_conf set nfs_core_param --nb_wroker 256" will use /etc/ganesha/ganesha.main.conf file for changing the worker threads. .PP .B ganesha_conf can't handle comments within a block at this point. .PP Neither block descriptions nor key value parameters are verified to be valid ganesha configuration blocks or parameter values currently. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/ganesha_conf.py��������������������������������������������0000775�0000000�0000000�00000005546�13242724102�0023277�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python import os, sys from Ganesha.config_editor import BLOCK, ArgError import logging, pprint # Modify the file with the given data atomically def modify_file(filename, data): from tempfile import NamedTemporaryFile f = NamedTemporaryFile(dir=os.path.dirname(filename), delete=False) f.write(data) f.flush() os.fsync(f.fileno()) # If filename exists, get its stats and apply them to the temp file try: stat = os.stat(filename) os.chown(f.name, stat.st_uid, stat.st_gid) os.chmod(f.name, stat.st_mode) except: pass os.rename(f.name, filename) # Get block names and key value options as separate lists def get_blocks(args): key_found = False for i, arg in enumerate(args): if arg.startswith("--"): key_found = True break if key_found: return (args[0:i], args[i:]) else: return (args, []) usage = """ Usage: %s set <block-descriptor> [--param value] %s del <block-descriptor> [--param] where <block-descriptor> is a list of blocknames with possible key value pair identifying the block. For example, %s set log --default_log_level DEBUG %s set log components --FSAL FULL_DEBUG %s set export export_id 14 --pseudo "/nfsroot/export1" %s set export export_id 14 client clients '*' --manage-gids true """ % (6 * (sys.argv[0],)) if len(sys.argv) < 2: sys.exit(usage); FORMAT = "[%(filename)s:%(lineno)d-%(funcName)s()] %(message)s" #logging.basicConfig(format=FORMAT, level=logging.DEBUG) opcode = sys.argv[1] if opcode == "set": (names, pairs) = get_blocks(sys.argv[2:]) if not names: sys.exit("no block names") if len(pairs) % 2 != 0: sys.exit("odd number for key value pairs") for key in pairs[::2]: if not key.startswith("--"): sys.exit("some keys are not with -- prefix") # remove "--" prefix of keys keys = [key[2:] for key in pairs[::2]] values = [value for value in pairs[1::2]] opairs = zip(keys, values) block = BLOCK(names) elif opcode == "del": (names, keys) = get_blocks(sys.argv[2:]) if not names: sys.exit("no block names") for key in keys: if not key.startswith("--"): sys.exit("some key not with -- prefix") # remove "--" prefix of keys keys = [key[2:] for key in keys] if not keys: keys = [] block = BLOCK(names) else: sys.exit(usage) conffile = os.environ.get("CONFFILE", "/etc/ganesha/ganesha.conf") old = open(conffile).read() try: if (opcode == "set"): logging.debug("opairs: %s", pprint.pformat(opairs)) new = block.set_keys(old, opairs) else: logging.debug("keys: %s", pprint.pformat(keys)) new = block.del_keys(old, keys) except ArgError as e: sys.exit(e.error) modify_file(conffile, new) ����������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/ganesha_mgr.py���������������������������������������������0000664�0000000�0000000�00000031420�13242724102�0023122�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python # # ganesha_mgr.py - commandline tool for managing nfs-ganesha. # # Copyright (C) 2014 IBM. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see <http://www.gnu.org/licenses/>. # # Author: Allison Henderson <achender@vnet.linux.ibm.com> #-*- coding: utf-8 -*- import sys import time from Ganesha.ganesha_mgr_utils import ClientMgr from Ganesha.ganesha_mgr_utils import ExportMgr from Ganesha.ganesha_mgr_utils import AdminInterface from Ganesha.ganesha_mgr_utils import LogManager SERVICE = 'org.ganesha.nfsd' class ManageClients(): def __init__(self, parent=None): self.clientmgr = ClientMgr(SERVICE, '/org/ganesha/nfsd/ClientMgr', 'org.ganesha.nfsd.clientmgr') def status_message(self, status, errormsg): print "Returns: status = %s, %s" % (str(status), errormsg) def addclient(self, ipaddr): print "Add a client %s" % (ipaddr) status, errormsg = self.clientmgr.AddClient(ipaddr) self.status_message(status, errormsg) def removeclient(self, ipaddr): print "Remove a client %s" % (ipaddr) status, errormsg = self.clientmgr.RemoveClient(ipaddr) self.status_message(status, errormsg) def showclients(self): print "Show clients" status, errormsg, reply = self.clientmgr.ShowClients() if status == True: ts = reply[0] clients = reply[1] self.proc_clients(ts, clients) else: self.status_message(status, errormsg) def proc_clients(self, ts, clients): print "Timestamp: ", time.ctime(ts[0]), ts[1], " nsecs" if len(clients) == 0: print "No clients" else: print "Clients:" print " IP addr, nfsv3, mnt, nlm4, rquota,nfsv40, nfsv41, nfsv42, 9p, last" for client in clients: print (" %s, %s, %s, %s, %s, %s, %s, %s, %s, %s %d nsecs" % (client.ClientIP, client.HasNFSv3, client.HasMNT, client.HasNLM4, client.HasRQUOTA, client.HasNFSv40, client.HasNFSv41, client.HasNFSv42, client.Has9P, time.ctime(client.LastTime[0]), client.LastTime[1])) class ShowExports(): def __init__(self, parent=None): self.exportmgr = ExportMgr(SERVICE, '/org/ganesha/nfsd/ExportMgr', 'org.ganesha.nfsd.exportmgr') def showexports(self): print "Show exports" status, msg, reply = self.exportmgr.ShowExports() if status == True: ts = reply[0] exports = reply[1] self.proc_exports(ts, exports) else: self.status_message(status, msg) def addexport(self, conf_path, exp_expr): print "Add Export in %s" % conf_path status, msg = self.exportmgr.AddExport(conf_path, exp_expr) self.status_message(status, msg) def removeexport(self, exp_id): print "Remove Export with id %d" % int(exp_id) self.exportmgr.RemoveExport(exp_id) def updateexport(self, conf_path, exp_expr): print "Update Export in %s" % conf_path status, msg = self.exportmgr.UpdateExport(conf_path, exp_expr) self.status_message(status, msg) def displayexport(self, exp_id): print "Display export with id %d" % int(exp_id) status, msg, reply = self.exportmgr.DisplayExport(exp_id) if status == True: id = reply[0] path = reply[1] pseudo = reply[2] tag = reply[3] self.proc_export(id, path, pseudo, tag) else: self.status_message(status, msg) def proc_export(self, id, path, pseudo, tag): print "export %d: path = %s, pseudo = %s, tag = %s" % (id, path, pseudo, tag) def proc_exports(self, ts, exports): print "Timestamp: ", time.ctime(ts[0]), ts[1], " nsecs" if len(exports) == 0: print "No exports" else: print "Exports:" print " Id, path, nfsv3, mnt, nlm4, rquota,nfsv40, nfsv41, nfsv42, 9p, last" for export in exports: print (" %d, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %d nsecs" % (export.ExportID, export.ExportPath, export.HasNFSv3, export.HasMNT, export.HasNLM4, export.HasRQUOTA, export.HasNFSv40, export.HasNFSv41, export.HasNFSv42, export.Has9P, time.ctime(export.LastTime[0]), export.LastTime[1])) def status_message(self, status, errormsg): print "Returns: status = %s, %s" % (str(status), errormsg) class ServerAdmin(): def __init__(self, parent=None): self.admin = AdminInterface(SERVICE, '/org/ganesha/nfsd/admin', 'org.ganesha.nfsd.admin') def shutdown(self): print "Shutting down server." status, msg = self.admin.shutdown() self.status_message(status, msg) def grace(self, ipaddr): print "Start grace period." status, msg = self.admin.grace(ipaddr) self.status_message(status, msg) def purge_netgroups(self): print("Purging netgroups cache") status, msg = self.admin.purge_netgroups() self.status_message(status, msg) def status_message(self, status, errormsg): print "Returns: status = %s, %s" % (str(status), errormsg) class ManageLogs(): def __init__(self, parent=None): self.logmgr = LogManager(SERVICE, '/org/ganesha/nfsd/admin', 'org.freedesktop.DBus.Properties') def set(self, property, value): print "Set log %s to %s" % (property, value) status, msg = self.logmgr.Set(property, value) self.status_message(status, msg) def get(self, property): print "Get property %s" % (property) status, msg, level = self.logmgr.Get(property) if status == True: self.show_loglevel(level) else: self.status_message(status, msg) def getall(self): print "Get all" status, msg, properties = self.logmgr.GetAll() if status == True: self.print_components(properties) else: self.status_message(status, msg) def show_loglevel(self, level): print "Log level: %s"% (str(level)) def status_message(self, status, errormsg): print "Returns: status = %s, %s" % (str(status), errormsg) def print_components(self, properties): for prop in properties: print str(prop) # Main if __name__ == '__main__': exportmgr = ShowExports() clientmgr = ManageClients() ganesha = ServerAdmin() logmgr = ManageLogs() USAGE = \ "\nganesha_mgr.py command [OPTIONS]\n\n" \ "COMMANDS\n\n" \ " add_client ipaddr: Adds the client with the given IP\n\n" \ " remove_client ipaddr: Removes the client with the given IP\n\n" \ " show_client: Shows the current clients\n\n" \ " display_export export_id: \n" \ " Displays the export with the given ID\n\n" \ " show_exports: Displays all current exports\n\n" \ " add_export conf expr:\n" \ " Adds an export from the given config file that contains\n" \ " the given expression\n" \ " Example: \n" \ " add_export /etc/ganesha/gpfs.conf \"EXPORT(Export_ID=77)\"\n\n"\ " remove_export id: Removes the export with the given id \n\n" \ " update_export conf expr:\n" \ " Updates an export from the given config file that contains\n" \ " the given expression\n" \ " Example: \n" \ " update_export /etc/ganesha/gpfs.conf \"EXPORT(Export_ID=77)\"\n\n"\ " shutdown: Shuts down the ganesha nfs server\n\n" \ " purge netgroups: Purges netgroups cache\n\n" \ " grace ipaddr: Begins grace for the given IP\n\n" \ " get_log component: Gets the log level for the given component\n\n"\ " set_log component level: \n" \ " Sets the given log level to the given component\n\n" \ " getall_logs: Prints all log components\n\n" if len(sys.argv) < 2: print "Too few arguments."\ " Try \"ganesha_mgr.py help\" for more info" sys.exit(1) elif sys.argv[1] == "add_client": if len(sys.argv) < 3: print "add_client requires an IP."\ " Try \"ganesha_mgr.py help\" for more info" sys.exit(1) clientmgr.addclient(sys.argv[2]) elif sys.argv[1] == "remove_client": if len(sys.argv) < 3: print "remove_client requires an IP."\ " Try \"ganesha_mgr.py help\" for more info" sys.exit(1) clientmgr.removeclient(sys.argv[2]) elif sys.argv[1] == "show_client": clientmgr.showclients() elif sys.argv[1] == "add_export": if len(sys.argv) < 4: print "add_export requires a config file and an expression."\ " Try \"ganesha_mgr.py help\" for more info" sys.exit(1) exportmgr.addexport(sys.argv[2], sys.argv[3]) elif sys.argv[1] == "remove_export": if len(sys.argv) < 3: print "remove_export requires an export ID."\ " Try \"ganesha_mgr.py help\" for more info" sys.exit(1) exportmgr.removeexport(sys.argv[2]) elif sys.argv[1] == "update_export": if len(sys.argv) < 4: print "update_export requires a config file and an expression."\ " Try \"ganesha_mgr.py help\" for more info" sys.exit(1) exportmgr.updateexport(sys.argv[2], sys.argv[3]) elif sys.argv[1] == "display_export": if len(sys.argv) < 3: print "display_export requires an export ID."\ " Try \"ganesha_mgr.py help\" for more info" sys.exit(1) exportmgr.displayexport(sys.argv[2]) elif sys.argv[1] == "show_exports": exportmgr.showexports() elif sys.argv[1] == "shutdown": ganesha.shutdown() elif sys.argv[1] == "purge": if len(sys.argv) < 3: msg = 'purge requires a cache name to purge, ' msg += 'Try "ganesha_mgr.py help" for more info' sys.exit(msg) if sys.argv[2] != 'netgroups': msg = "Purging '%s' is not supported" % sys.argv[2] sys.exit(msg) ganesha.purge_netgroups() elif sys.argv[1] == "grace": if len(sys.argv) < 3: print "grace requires an IP."\ " Try \"ganesha_mgr.py help\" for more info" sys.exit(1) ganesha.grace(sys.argv[2]) elif sys.argv[1] == "set_log": if len(sys.argv) < 4: print "set_log requires a component and a log level."\ " Try \"ganesha_mgr.py help\" for more info" sys.exit(1) logmgr.set(sys.argv[2], sys.argv[3]) elif sys.argv[1] == "get_log": if len(sys.argv) < 3: print "get_log requires a component."\ " Try \"ganesha_mgr.py help\" for more info" sys.exit(1) logmgr.get(sys.argv[2]) elif sys.argv[1] == "getall_logs": logmgr.getall() elif sys.argv[1] == "help": print USAGE else: print "Unknown/missing command."\ " Try \"ganesha_mgr.py help\" for more info" ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/ganesha_stats.py�������������������������������������������0000775�0000000�0000000�00000006562�13242724102�0023507�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python # # This command receives statistics from Ganesha over DBus. The format for a command is: # ./ganesha_stats.py <command> <option> # eg. ./ganesha_stats.py deleg ::ffff:192.168.122.94 # To get a list of clients and client ips use the "list_clients " command. # import gobject import sys import time import re import Ganesha.glib_dbus_stats def usage(): message = "Command displays global stats by default.\n" message += "To display stat counters use \n" message += "%s [list_clients | deleg <ip address> | " % (sys.argv[0]) message += "inode | iov3 [export id] | iov4 [export id] | export |" message += " total [export id] | fast | pnfs [export id] |" message += " fsal <fsal name> ] \n" message += "To reset stat counters use \n" message += "%s reset \n" % (sys.argv[0]) message += "To enable/disable stat counters use \n" message += "%s [enable | disable] [all | nfs | fsal] " % (sys.argv[0]) sys.exit(message) if len(sys.argv) < 2: command = 'global' else: command = sys.argv[1] # check arguments commands = ('help', 'list_clients', 'deleg', 'global', 'inode', 'iov3', 'iov4', 'export', 'total', 'fast', 'pnfs', 'fsal', 'reset', 'enable', 'disable') if command not in commands: print "Option \"%s\" is not correct." % (command) usage() # requires an IP address elif command in ('deleg'): if not len(sys.argv) == 3: print "Option \"%s\" must be followed by an ip address." % (command) usage() command_arg = sys.argv[2] # optionally accepts an export id elif command in ('iov3', 'iov4', 'total', 'pnfs'): if (len(sys.argv) == 2): command_arg = -1 elif (len(sys.argv) == 3) and sys.argv[2].isdigit(): command_arg = sys.argv[2] else: usage() elif command == "help": usage() # requires fsal name elif command in ('fsal'): if not len(sys.argv) == 3: print "Option \"%s\" must be followed by fsal name." % (command) usage() command_arg = sys.argv[2] elif command in ('enable', 'disable'): if not len(sys.argv) == 3: print "Option \"%s\" must be followed by all/nfs/fsal." % (command) usage() command_arg = sys.argv[2] if command_arg not in ('all', 'nfs', 'fsal'): print "Option \"%s\" must be followed by all/nfs/fsal." % (command) usage() # retrieve and print stats exp_interface = Ganesha.glib_dbus_stats.RetrieveExportStats() cl_interface = Ganesha.glib_dbus_stats.RetrieveClientStats() if command == "global": print exp_interface.global_stats() elif command == "export": print exp_interface.export_stats() elif command == "inode": print exp_interface.inode_stats() elif command == "fast": print exp_interface.fast_stats() elif command == "list_clients": print cl_interface.list_clients() elif command == "deleg": print cl_interface.deleg_stats(command_arg) elif command == "iov3": print exp_interface.v3io_stats(command_arg) elif command == "iov4": print exp_interface.v4io_stats(command_arg) elif command == "total": print exp_interface.total_stats(command_arg) elif command == "pnfs": print exp_interface.pnfs_stats(command_arg) elif command == "reset": print exp_interface.reset_stats() elif command == "fsal": print exp_interface.fsal_stats(command_arg) elif command == "enable": print exp_interface.enable_stats(command_arg) elif command == "disable": print exp_interface.disable_stats(command_arg) ����������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/ganeshactl.py����������������������������������������������0000664�0000000�0000000�00000015660�13242724102�0022770�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#! /usr/bin/python # # ganeshactl.py - PyQt4 GUI tool for admin of nfs-ganesha. # # Copyright (C) 2014 Panasas Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see <http://www.gnu.org/licenses/>. # # Author: Jim Lieb <jlieb@panasas.com> #-*- coding: utf-8 -*- """ NFS Ganesha administration tool """ import sys from PyQt4 import QtCore, QtGui, QtDBus from Ganesha.QtUI.ui_main_window import Ui_MainWindow from Ganesha.admin import AdminInterface from Ganesha.export_mgr import ExportMgr from Ganesha.QtUI.exports_table import ExportTableModel from Ganesha.client_mgr import ClientMgr from Ganesha.QtUI.clients_table import ClientTableModel from Ganesha.log_mgr import LogManager from Ganesha.QtUI.log_settings import LogSetDialog SERVICE = 'org.ganesha.nfsd' class MainWindow(QtGui.QMainWindow): show_status = QtCore.pyqtSignal(bool, str) def __init__(self, sysbus, parent=None): QtGui.QWidget.__init__(self, parent) # Init ourself from the Designer UI self.ui = Ui_MainWindow() self.ui.setupUi(self) # light up the admin interface self.admin = AdminInterface(SERVICE, '/org/ganesha/nfsd/admin', sysbus, self.show_status) self.exportmgr = ExportMgr(SERVICE, '/org/ganesha/nfsd/ExportMgr', sysbus, self.show_status) self.clientmgr = ClientMgr(SERVICE, '/org/ganesha/nfsd/ClientMgr', sysbus, self.show_status) self.logmanager = LogManager(SERVICE, sysbus, self.show_status) self.logdialog = LogSetDialog(self.logmanager) self.show_status.connect(self.status_message) # Connect up the ui menubar #File self.ui.actionDBus_connect.triggered.connect(self.connect_gsh) self.ui.actionQuit.triggered.connect(self.quit) #Manage #Manage->Clients self.ui.actionAdd_Client.triggered.connect(self.add_client) self.ui.actionRemove_Client.triggered.connect(self.remove_client) #Manage->Exports self.ui.actionExports.triggered.connect(self.export_mgr) #Manage->Log Levels self.ui.actionLog_Settings.triggered.connect(self.logsettings) #Manage->Admin self.ui.actionReset_Grace.triggered.connect(self.reset_grace) self.ui.actionShutdown.triggered.connect(self.shutdown) self.ui.actionReload.triggered.connect(self.reload) #View self.ui.actionStatistics.triggered.connect(self.stats) self.ui.actionViewExports.triggered.connect(self.view_exports) self.ui.actionViewClients.triggered.connect(self.view_clients) #Help self.ui.actionAbout.triggered.connect(self.help) # Dbus data models self.exports_show_model = ExportTableModel(self.exportmgr) self.clients_show_model = ClientTableModel(self.clientmgr) # Tabs, tables, and views self.ui.exports.setModel(self.exports_show_model) self.ui.exports.resizeColumnsToContents() self.ui.exports.verticalHeader().setVisible(False) self.ui.clients.setModel(self.clients_show_model) self.ui.clients.resizeColumnsToContents() self.ui.clients.verticalHeader().setVisible(False) # actions to real work... def quit(self): self.statusBar().showMessage("Bye bye kiddies, quitting") quit() def connect_gsh(self): self.statusBar().showMessage("Connecting to nfs-ganesha...") def add_client(self): ipaddr, ok = QtGui.QInputDialog.getText(self, 'Add a Client', 'IP Address (N.N.N.N) of client: ') if ok: self.clientmgr.AddClient(ipaddr) def remove_client(self): ipaddr, ok = QtGui.QInputDialog.getText(self, 'Remove a Client', 'IP Address (N.N.N.N) of client: ') if ok: self.clientmgr.RemoveClient(ipaddr) def export_mgr(self): self.statusBar().showMessage("Export manager") def logsettings(self): self.logdialog.show_logsetting_dialog() def reset_grace(self): ipaddr, ok = QtGui.QInputDialog.getText(self, 'Grace Period', 'IP Address (N.N.N.N) of client: ') if ok: self.admin.grace(ipaddr) def shutdown(self): reply = QtGui.QMessageBox.question(self, 'Warning!!!', "Do you really want to shut down the server?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: self.admin.shutdown() def reload(self): reply = QtGui.QMessageBox.question(self, 'Warning!!!', "Do you really want to reload exports?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: self.admin.reload() def stats(self): self.statusBar().showMessage("stats go here") def view_exports(self): self.exports_show_model.FetchExports() def view_clients(self): self.clients_show_model.FetchClients() def help(self): self.statusBar().showMessage("Help! Help!!") def status_message(self, status, errormsg): if status: str = "Success: " else: str = "Failed: " self.statusBar().showMessage(str + errormsg) # Main if __name__ == '__main__': app = QtGui.QApplication(sys.argv) sysbus = QtDBus.QDBusConnection.systemBus() mw = MainWindow(sysbus) mw.show() sys.exit(app.exec_()) ��������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/get_clientids.py�������������������������������������������0000775�0000000�0000000�00000001321�13242724102�0023464�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python # You must initialize the gobject/dbus support for threading # before doing anything. import gobject gobject.threads_init() from dbus import glib glib.init_threads() # Create a session bus. import dbus bus = dbus.SystemBus() # Create an object that will proxy for a particular remote object. cbsim = bus.get_object("org.ganesha.nfsd", "/org/ganesha/nfsd/CBSIM") print "introspection data" introspect = dbus.Interface( cbsim, dbus.INTROSPECTABLE_IFACE, ) print introspect.Introspect() # call method get_client_ids = cbsim.get_dbus_method('get_client_ids', 'org.ganesha.nfsd.cbsim') print "client ids:" print get_client_ids() ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/grace_period.py��������������������������������������������0000775�0000000�0000000�00000001572�13242724102�0023302�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python # You must initialize the gobject/dbus support for threading # before doing anything. import gobject import sys ipaddr=sys.argv[1] print 'event:ip_addr=', ipaddr gobject.threads_init() from dbus import glib glib.init_threads() # Create a session bus. import dbus bus = dbus.SystemBus() # Create an object that will proxy for a particular remote object. try: admin = bus.get_object("org.ganesha.nfsd", "/org/ganesha/nfsd/admin") except dbus.exceptions.DBusException as e: sys.exit("Error: Can't talk to ganesha service on d-bus." \ " Looks like Ganesha is down") # call method ganesha_grace = admin.get_dbus_method('grace', 'org.ganesha.nfsd.admin') print "Start grace period." try: print ganesha_grace(ipaddr) except dbus.exceptions.DBusException as e: sys.exit("Error: Failed to start grace period") ��������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/manage_clients.py������������������������������������������0000664�0000000�0000000�00000006365�13242724102�0023632�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python # # manage_clients.py - commandline tool for managing clients of nfs-ganesha. # # Copyright (C) 2014 Panasas Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see <http://www.gnu.org/licenses/>. # # Author: Jim Lieb <jlieb@panasas.com> #-*- coding: utf-8 -*- import sys import time from PyQt4 import QtCore, QtDBus from PyQt4.QtGui import QApplication from dbus.mainloop.qt import DBusQtMainLoop from Ganesha.client_mgr import ClientMgr SERVICE = 'org.ganesha.nfsd' class ManageClients(QtCore.QObject): show_status = QtCore.pyqtSignal(bool, str) def __init__(self, sysbus, parent=None): super(ManageClients, self).__init__() self.clientmgr = ClientMgr(SERVICE, '/org/ganesha/nfsd/ClientMgr', sysbus, self.show_status) self.show_status.connect(self.status_message) self.clientmgr.show_clients.connect(self.proc_clients) def addclient(self, ipaddr): self.clientmgr.AddClient(ipaddr) print "Add a client %s" % (ipaddr) def removeclient(self, ipaddr): self.clientmgr.RemoveClient(ipaddr) print "Remove a client %s" % (ipaddr) def showclients(self): self.clientmgr.ShowClients() print "Show clients" def proc_clients(self, ts, clients): print "Timestamp: ", time.ctime(ts[0]), ts[1], " nsecs" if len(clients) == 0: print "No clients" else: print "Clients:" print " IP addr, nfsv3, mnt, nlm4, rquota,nfsv40, nfsv41, 9p, last" for client in clients: print (" %s, %s, %s, %s, %s, %s, %s, %s, %s %d nsecs" % (client.ClientIP, client.HasNFSv3, client.HasMNT, client.HasNLM4, client.HasRQUOTA, client.HasNFSv40, client.HasNFSv41, client.Has9P, time.ctime(client.LastTime[0]), client.LastTime[1])) sys.exit() def status_message(self, status, errormsg): print "Error: status = %s, %s" % (str(status), errormsg) sys.exit() # Main if __name__ == '__main__': app = QApplication(sys.argv) loop = DBusQtMainLoop(set_as_default=True) sysbus = QtDBus.QDBusConnection.systemBus() clientmgr = ManageClients(sysbus) if sys.argv[1] == "add": clientmgr.addclient(sys.argv[2]) elif sys.argv[1] == "remove": clientmgr.removeclient(sys.argv[2]) elif sys.argv[1] == "show": clientmgr.showclients() else: print "unknown/missing command" sys.exit() app.exec_() ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/manage_exports.py������������������������������������������0000775�0000000�0000000�00000010001�13242724102�0023656�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python # # manage_exports.py - commandline tool for managing exports in nfs-ganesha. # # Copyright (C) 2014 Panasas Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see <http://www.gnu.org/licenses/>. # # Author: Jim Lieb <jlieb@panasas.com> #-*- coding: utf-8 -*- import sys import time from PyQt4 import QtCore, QtDBus from PyQt4.QtGui import QApplication from dbus.mainloop.qt import DBusQtMainLoop from Ganesha.export_mgr import ExportMgr SERVICE = 'org.ganesha.nfsd' class ShowExports(QtCore.QObject): show_status = QtCore.pyqtSignal(bool, str) def __init__(self, sysbus, parent=None): super(ShowExports, self).__init__() self.exportmgr = ExportMgr(SERVICE, '/org/ganesha/nfsd/ExportMgr', sysbus, self.show_status) self.show_status.connect(self.status_message) self.exportmgr.show_exports.connect(self.proc_exports) self.exportmgr.display_export.connect(self.proc_export) def showexports(self): self.exportmgr.ShowExports() print "Show exports" def addexport(self, conf_path, exp_expr): self.exportmgr.AddExport(conf_path, exp_expr) print "Add Export in %s" % conf_path def updateexport(self, conf_path, exp_expr): self.exportmgr.UpdateExport(conf_path, exp_expr) print "Update Export in %s" % conf_path def removeexport(self, exp_id): self.exportmgr.RemoveExport(exp_id) print "Remove Export with id %d" % int(exp_id) def displayexport(self, exp_id): self.exportmgr.DisplayExport(exp_id) print "Display export with id %d" % int(exp_id) def proc_export(self, id, path, pseudo, tag): print "export %d: path = %s, pseudo = %s, tag = %s" % (id, path, pseudo, tag) sys.exit() def proc_exports(self, ts, exports): print "Timestamp: ", time.ctime(ts[0]), ts[1], " nsecs" if len(exports) == 0: print "No exports" else: print "Exports:" print " Id, path, nfsv3, mnt, nlm4, rquota,nfsv40, nfsv41, 9p, last" for export in exports: print (" %d, %s, %s, %s, %s, %s, %s, %s, %s, %s %d nsecs" % (export.ExportID, export.ExportPath, export.HasNFSv3, export.HasMNT, export.HasNLM4, export.HasRQUOTA, export.HasNFSv40, export.HasNFSv41, export.Has9P, time.ctime(export.LastTime[0]), export.LastTime[1])) sys.exit() def status_message(self, status, errormsg): print "Error: status = %s, %s" % (str(status), errormsg) sys.exit() # Main if __name__ == '__main__': app = QApplication(sys.argv) loop = DBusQtMainLoop(set_as_default=True) sysbus = QtDBus.QDBusConnection.systemBus() exportmgr = ShowExports(sysbus) if sys.argv[1] == "add": exportmgr.addexport(sys.argv[2], sys.argv[3]) elif sys.argv[1] == "update": exportmgr.updateexport(sys.argv[2], sys.argv[3]) elif sys.argv[1] == "remove": exportmgr.removeexport(sys.argv[2]) elif sys.argv[1] == "display": exportmgr.displayexport(sys.argv[2]) elif sys.argv[1] == "show": exportmgr.showexports() else: print "Unknown/missing command" sys.exit() app.exec_() �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/manage_logger.py�������������������������������������������0000664�0000000�0000000�00000005411�13242724102�0023437�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python # # manage_logger.py - commandline tool for setting log levels of nfs-ganesha. # # Copyright (C) 2014 Panasas Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see <http://www.gnu.org/licenses/>. # # Author: Jim Lieb <jlieb@panasas.com> #-*- coding: utf-8 -*- import sys import time from PyQt4 import QtCore, QtDBus from PyQt4.QtGui import QApplication from dbus.mainloop.qt import DBusQtMainLoop from Ganesha.log_mgr import LogManager SERVICE = 'org.ganesha.nfsd' class ManageLogger(QtCore.QObject): show_status = QtCore.pyqtSignal(bool, str) def __init__(self, sysbus, parent=None): super(ManageLogger, self).__init__() self.logmgr = LogManager(SERVICE, sysbus, self.show_status) self.show_status.connect(self.status_message) self.logmgr.show_level.connect(self.proc_level) self.logmgr.show_components.connect(self.proc_components) def get_level(self, component): self.logmgr.Get(component) print "Getting log level for %s" % (component) def set_level(self, component, level): self.logmgr.Set(component, level) print "Setting log level for %s to %s" % (component, level) def getall(self): self.logmgr.GetAll() print "Fetching component log levels" def proc_level(self, level): print "Level = %s" % (level) sys.exit() def proc_components(self, components): print "dict of levels:" for comp in components.keys(): print "Component %s is at %s" % (comp, components[comp]) sys.exit() def status_message(self, status, errormsg): print "Error: status = %s, %s" % (str(status), errormsg) sys.exit() # Main if __name__ == '__main__': app = QApplication(sys.argv) loop = DBusQtMainLoop(set_as_default=True) sysbus = QtDBus.QDBusConnection.systemBus() logger = ManageLogger(sysbus) if sys.argv[1] == "get": logger.get_level(sys.argv[2]) elif sys.argv[1] == "set": logger.set_level(sys.argv[2], sys.argv[3]) elif sys.argv[1] == "getall": logger.getall() else: print "unknown/missing command" sys.exit() app.exec_() �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/org.ganesha.nfsd.conf��������������������������������������0000664�0000000�0000000�00000001305�13242724102�0024270�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- --> <!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> <busconfig> <!-- Only root can own the service --> <policy user="root"> <allow own="org.ganesha.nfsd"/> <allow send_destination="org.ganesha.nfsd"/> <allow send_destination="org.ganesha.nfsd" send_interface="org.freedesktop.DBus.Introspectable"/> <allow send_destination="org.ganesha.nfsd" send_interface="org.ganesha.nfsd.CBSIM"/> <allow send_destination="org.ganesha.nfsd" send_interface="org.ganesha.nfsd.admin"/> </policy> </busconfig> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/purge_gids.py����������������������������������������������0000775�0000000�0000000�00000001100�13242724102�0022772�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python # You must initialize the gobject/dbus support for threading # before doing anything. import sys, gobject, dbus, dbus.glib gobject.threads_init() dbus.glib.init_threads() # Create a session bus. bus = dbus.SystemBus() # Create an object that will proxy for a particular remote object. admin = bus.get_object("org.ganesha.nfsd", "/org/ganesha/nfsd/admin") # call method purge_gids = admin.get_dbus_method('purge_gids', 'org.ganesha.nfsd.admin') (purged, msg) = purge_gids() if not purged: print("Purging gids cache failed: %s" % msg) sys.exit(1) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/ganeshactl/setup.py.in������������������������������������������������0000664�0000000�0000000�00000000655�13242724102�0022422�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*-Python-*- from distutils.core import setup, Extension if __name__ == '__main__': setup(name='ganeshactl', version='${GANESHA_VERSION}', author = "Jim Lieb", maintainer_email = "jlieb@panasas.com", description = "NFS Ganesha Administration tools", package_dir = { '': '${CMAKE_CURRENT_SOURCE_DIR}' }, packages = ['Ganesha', 'Ganesha.QtUI'], scripts = [${SCRIPTS_STRING}]) �����������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/gerrit/���������������������������������������������������������������0000775�0000000�0000000�00000000000�13242724102�0017460�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/gerrit/checkpatch-to-gerrit-json.py�����������������������������������0000664�0000000�0000000�00000001720�13242724102�0025010�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python from __future__ import print_function import sys,json,re comments={} while 1: line = sys.stdin.readline() fileline = sys.stdin.readline() if not fileline.strip(): break while 1: newline = sys.stdin.readline() if not newline.strip(): break line += newline filere = re.search('FILE: (.*):([0-9]*):', fileline) if comments.has_key(filere.group(1)): comments[filere.group(1)] += [ { 'line': filere.group(2), 'message': line.strip()} ] else: comments[filere.group(1)] = [ { 'line': filere.group(2), 'message': line.strip()} ] if comments: #output = { 'comments': comments, 'message': line.strip(), 'labels': {'Code-Review': -1 }} output = { 'comments': comments, 'message': "Checkpatch %s" % (line.strip()) } else: #output = {'message': 'Checkpatch OK', 'labels': {'Code-Review': +1 }} output = {'message': 'Checkpatch OK'} print(json.dumps(output)) ������������������������������������������������nfs-ganesha-2.6.0/src/scripts/gerrit/gerrit-checkpatch.sh�������������������������������������������0000775�0000000�0000000�00000004055�13242724102�0023412�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash -u # configuration GERRIT_SERVER=review.gerrithub.io GERRIT_USER=ganesha-triggers GERRIT_KEYFILE=$HOME/.ssh/id_rsa_ganesha-triggers PROJECT=ffilz/nfs-ganesha # host box configuration needed: # - checkpatch.conf configured (.checkpatch.conf in homedir) # - current repository has a remote called 'gerrit' setup # - ssh configuration that said 'gerrit' remote doesn't need a password # - awk script for oneshot query requires awk >= 4 # internal variables SSH_GERRIT="ssh -p 29418 -i $GERRIT_KEYFILE -l $GERRIT_USER $GERRIT_SERVER" DRYRUN="" ONESHOT="" while getopts :nqch opt; do case "$opt" in n) DRYRUN=1 ;; q) ONESHOT=query ;; c) ONESHOT=cat ;; *) cat << EOF Usage: $0 [-n] [-o] -n dry-run mode, do not actually push reviews -q one-shot queries gerrit and tries to catch up on open patchset that were not commented -c one-shot cat, reads one line at a time "ref commit" e.g "refs/change/x/y/z 0123456789abcdef" EOF exit ;; esac done input_loop() { case "$ONESHOT" in "query") $SSH_GERRIT "gerrit query --comments --patch-sets \ is:open project:$PROJECT" | \ awk -f gerrit-query.awk ;; "cat") cat ;; *) while date 1>&2; do $SSH_GERRIT "gerrit stream-events" | \ python gerrit-stream-filter.py $PROJECT done ;; esac } commit_review() { if [[ -z "$DRYRUN" ]]; then tee >($SSH_GERRIT "gerrit review --json \ --project $PROJECT $COMMIT") 1>&2 else echo "Would have submit:" cat fi } input_loop | \ while read REF COMMIT; do echo "got ref $REF, commit $COMMIT" git fetch gerrit $REF >/dev/null 2>&1 git show --format=email $COMMIT | \ ../checkpatch.pl -q - | \ python checkpatch-to-gerrit-json.py | \ commit_review done �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/gerrit/gerrit-query.awk�����������������������������������������������0000664�0000000�0000000�00000001216�13242724102�0022623�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/awk -f # run with -v debug=1 for verbose printing /^change / { for (i in patchSets) { if (debug) for (j in patchSets[i]) { print i, j, patchSets[i][j] } if (!patchSets[i]["reviewed"]) { print patchSets[i]["ref"] " " patchSets[i]["commit"] } } delete patchSets; curSet=0; } /^ *username:/ { username=$2 } /^ *message: Patch Set/ { if (username == "ganesha-triggers") { sub(":", "", $4); patchSets[$4]["reviewed"] = "ok" } } /^ *number: / { curSet=$2 } /^ *ref: / { patchSets[curSet]["ref"]=$2 } /^ *revision: / { patchSets[curSet]["commit"]=$2 } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/gerrit/gerrit-stream-filter.py����������������������������������������0000664�0000000�0000000�00000001371�13242724102�0024104�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python from __future__ import print_function import json,sys if len(sys.argv) > 1: GERRIT_PROJECT = sys.argv[1] else: GERRIT_PROJECT = u'ffilz/nfs-ganesha' debug = False for line in sys.stdin: try: obj=json.loads(line) except ValueError: # silently skip what wasn't json continue if obj[u'type'] != 'patchset-created' or obj[u'change'][u'project'] != GERRIT_PROJECT: # skip events we don't care about continue if debug: print(json.dumps(obj, indent=4), file=sys.stderr) # format stuff so we can read it in shell easily print("%s %s" % (obj[u'patchSet'][u'ref'], obj[u'patchSet'][u'revision'])) # need to flush if printing to a pipe sys.stdout.flush() �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/git_hooks/������������������������������������������������������������0000775�0000000�0000000�00000000000�13242724102�0020152�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/git_hooks/commit-msg��������������������������������������������������0000775�0000000�0000000�00000011003�13242724102�0022147�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # From Gerrit Code Review 2.10-rc1-988-g333a9dd # Edited by Dominique Martinet to add Signed-off-by as we used to # # Part of Gerrit Code Review (http://code.google.com/p/gerrit/) # # Copyright (C) 2009 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # unset GREP_OPTIONS CHANGE_ID_AFTER="Bug|Issue" MSG="$1" # Check for, and add if missing, a unique Change-Id # add_ChangeId() { clean_message=`sed -e ' /^diff --git .*/{ s/// q } /^Signed-off-by:/d /^#/d ' "$MSG" | git stripspace` if test -z "$clean_message" then return fi if test "false" = "`git config --bool --get gerrit.createChangeId`" then return fi # Does Change-Id: already exist? if so, exit (no change). if grep -i '^Change-Id:' "$MSG" >/dev/null then return fi id=`_gen_ChangeId` T="$MSG.tmp.$$" AWK=awk if [ -x /usr/xpg4/bin/awk ]; then # Solaris AWK is just too broken AWK=/usr/xpg4/bin/awk fi # How this works: # - parse the commit message as (textLine+ blankLine*)* # - assume textLine+ to be a footer until proven otherwise # - exception: the first block is not footer (as it is the title) # - read textLine+ into a variable # - then count blankLines # - once the next textLine appears, print textLine+ blankLine* as these # aren't footer # - in END, the last textLine+ block is available for footer parsing $AWK ' BEGIN { # while we start with the assumption that textLine+ # is a footer, the first block is not. isFooter = 0 footerComment = 0 blankLines = 0 } # Skip lines starting with "#" without any spaces before it. /^#/ { next } # Skip the line starting with the diff command and everything after it, # up to the end of the file, assuming it is only patch data. # If more than one line before the diff was empty, strip all but one. /^diff --git / { blankLines = 0 while (getline) { } next } # Count blank lines outside footer comments /^$/ && (footerComment == 0) { blankLines++ next } # Catch footer comment /^\[[a-zA-Z0-9-]+:/ && (isFooter == 1) { footerComment = 1 } /]$/ && (footerComment == 1) { footerComment = 2 } # We have a non-blank line after blank lines. Handle this. (blankLines > 0) { print lines for (i = 0; i < blankLines; i++) { print "" } lines = "" blankLines = 0 isFooter = 1 footerComment = 0 } # Detect that the current block is not the footer (footerComment == 0) && (!/^\[?[a-zA-Z0-9-]+:/ || /^[a-zA-Z0-9-]+:\/\//) { isFooter = 0 } { # We need this information about the current last comment line if (footerComment == 2) { footerComment = 0 } if (lines != "") { lines = lines "\n"; } lines = lines $0 } # Footer handling: # If the last block is considered a footer, splice in the Change-Id at the # right place. # Look for the right place to inject Change-Id by considering # CHANGE_ID_AFTER. Keys listed in it (case insensitive) come first, # then Change-Id, then everything else (eg. Signed-off-by:). # # Otherwise just print the last block, a new line and the Change-Id as a # block of its own. END { unprinted = 1 if (isFooter == 0) { print lines "\n" lines = "" } changeIdAfter = "^(" tolower("'"$CHANGE_ID_AFTER"'") "):" numlines = split(lines, footer, "\n") for (line = 1; line <= numlines; line++) { if (unprinted && match(tolower(footer[line]), changeIdAfter) != 1) { unprinted = 0 print "Change-Id: I'"$id"'" } print footer[line] } if (unprinted) { print "Change-Id: I'"$id"'" } }' "$MSG" > "$T" && mv "$T" "$MSG" || rm -f "$T" } _gen_ChangeIdInput() { echo "tree `git write-tree`" if parent=`git rev-parse "HEAD^0" 2>/dev/null` then echo "parent $parent" fi echo "author `git var GIT_AUTHOR_IDENT`" echo "committer `git var GIT_COMMITTER_IDENT`" echo printf '%s' "$clean_message" } _gen_ChangeId() { _gen_ChangeIdInput | git hash-object -t commit --stdin } add_SignOffBy() { SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') grep -qs "^$SOB" "$MSG" || echo "$SOB" >> "$MSG" } add_ChangeId add_SignOffBy �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/git_hooks/install_git_hooks.sh����������������������������������������0000775�0000000�0000000�00000000623�13242724102�0024226�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh CURDIR=$(dirname "$(readlink -m "$0")") TOPDIR=$(git rev-parse --show-toplevel) HOOKDIR=$TOPDIR/.git/hooks # Link checkpatch script configuration file to top level working # directory. ln -sf ./src/scripts/checkpatch.conf "$TOPDIR/.checkpatch.conf" cp -f "$CURDIR/pre-commit" "$HOOKDIR" chmod +x "$HOOKDIR/pre-commit" cp -f "$CURDIR/commit-msg" "$HOOKDIR" chmod +x "$HOOKDIR/commit-msg" �������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/git_hooks/pre-commit��������������������������������������������������0000775�0000000�0000000�00000003301�13242724102�0022151�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash # Ganesha pre-commit hook # # 1. Run checkpatch on the commit # 2. Check to see if a submodule is not being updated # define colors for use in output green='\033[0;32m' no_color='\033[0m' grey='\033[0;90m' if git rev-parse --verify HEAD 2>/dev/null >/dev/null then against=HEAD else # Initial commit: diff against an empty tree object against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 fi topdir=$(git rev-parse --show-toplevel) git diff --cached $against | $topdir/src/scripts/checkpatch.pl \ --no-signoff -q - if [ $? != 0 ]; then exit 1 fi # Check whether any submodule is about to be updated with the # commit. Ask the user for confirmation. echo -e "Checking submodules ${grey}(pre-commit hook)${no_color} " # Jump to the current project's root directory (the one containing # .git/) ROOT_DIR=$(git rev-parse --show-cdup) SUBMODULES=$(grep path ${ROOT_DIR}.gitmodules | sed 's/^.*path = //') # Finding the submodules that have been modified MOD_SUBMODULES=$(git diff --cached --name-only | grep -F "$SUBMODULES") # If no modified submodules, exit with status code 0, else prompt the # user and exit accordingly if [[ -n "$MOD_SUBMODULES" ]]; then echo "Submodules to be committed:" echo " (use \"git reset HEAD <file>...\" to unstage)" echo for SUB in $MOD_SUBMODULES do echo -e "\t${green}modified:\t$SUB${no_color}" done echo echo -n -e "Continue with commit? ${grey}[N|y]${no_color} " read -n 1 reply </dev/tty echo if [[ "$reply" == "y" || "$reply" == "Y" ]]; then echo "Permitting submodules to be committed..." exit 0 else echo "Aborting commit due to submodule update." exit 1 fi fi exit 0 �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/gpfs-epoch������������������������������������������������������������0000775�0000000�0000000�00000005350�13242724102�0020150�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python # Usually ganesha daemon start up time is used as its epoch. This is # fine for a single node configurations, but in clustered environment, # ganesha daemon will have to distinguish itself from instances running # on other nodes. This is not possible if ganesha daemon is started at # the same time on multiple cluster nodes. # # The epoch will have to be a 32 bit number. We use nodeid in the most # significant 16 bits and a generation number in the least significant # 16 bits. Current generation number is written to epoch_file. import os, sys, re, fcntl from subprocess import Popen, PIPE from ctypes import c_int, c_long, pointer, POINTER, Structure epoch_file = "/var/lib/nfs/ganesha/gpfs-epoch" def main(): genid = get_genid() + 1 # next genid genid &= 0xFFFF # handle 16 bit overflow put_genid(genid) # store the genid nodeid = get_nodeid() epoch = nodeid << 16 | genid print(epoch) def get_genid(): try: with open(epoch_file, "r") as f: genid = int(f.read()) except Exception: genid = 0 return genid def put_genid(genid): with open(epoch_file, "w+") as f: f.write("%s" % genid) def get_mount(): output = Popen("mount", shell=True, stdout=PIPE).communicate()[0] for line in output.splitlines(): # the mount output line format is # # dev on mountpoint type fstype (options) # # Easier with split, but let us handle mount point names that # may include strange characters like space! Take anything from # "on" to "type gpfs" as a mount point! mo = re.search(r"\bon\b(.+)\btype gpfs\b", line) if mo: return mo.group(1).strip() return None # From gpfs headers! C code is probably less error prone for this! # # struct kxArgs { signed long arg1; signed long arg2 } # struct grace_period_arg { int mountdirfd, int grace_sec } # Note that struct grace_period_arg is used for GET_NODEID! class GracePeriodArg(Structure): _fields_ = [("mountdirfd", c_int), ("grace_sec", c_int)] class KxArgs(Structure): _fields_ = [("arg1", c_long), ("arg2", POINTER(GracePeriodArg))] def get_nodeid(): # GPFS FSAL constants GPFS_DEVNAMEX = "/dev/ss0" kGanesha = 140 OPENHANDLE_GET_NODEID = 125 gpfs_fd = os.open(GPFS_DEVNAMEX, os.O_RDONLY) gpfs_mount = get_mount() mountdirfd = os.open(gpfs_mount, os.O_RDONLY|os.O_DIRECTORY) gpa = GracePeriodArg(mountdirfd, 0) kxarg = KxArgs(c_long(OPENHANDLE_GET_NODEID), pointer(gpa)) return fcntl.ioctl(gpfs_fd, kGanesha, kxarg) if __name__ == "__main__": import traceback import syslog try: main() except: syslog.syslog(traceback.format_exc()) sys.exit(1) ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/init.d/���������������������������������������������������������������0000775�0000000�0000000�00000000000�13242724102�0017351�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/init.d/nfs-ganesha.debian8��������������������������������������������0000664�0000000�0000000�00000004774�13242724102�0023013�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash # # chkconfig: 2345 50 50 # description: GANESHA NFS Daemon # # processname: /usr/bin/ganesha.nfsd # config: /etc/ganesha/ganesha.nfsd.conf # pidfile: /var/run/ganesha.nfsd.pid # ### BEGIN INIT INFO # Provides: ganesha # Required-Start: $local_fs $named $time $network # Required-Stop: $local_fs $named $time $network # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: start and stop ganesha daemon # Description: NFS-GANESHA ### END INIT INFO # source function library #. /etc/rc.d/init.d/functions . /lib/lsb/init-functions PATHPROG=/usr/bin/ganesha.nfsd # Default Ganesha options LOGFILE=/var/log/ganesha/ganesha.log CONFFILE=/etc/ganesha/ganesha.conf prog=ganesha.nfsd PID_FILE=${PID_FILE:=/var/run/${prog}.pid} LOCK_FILE=${LOCK_FILE:=/var/lock/subsys/${prog}} [ -f /etc/sysconfig/ganesha ] && . /etc/sysconfig/ganesha [ -z "$NOFILE" ] && NOFILE=1048576 OPTIONS="-L $LOGFILE -f $CONFFILE -N NIV_EVENT -p $PID_FILE " RETVAL=0 start() { echo -n $"Starting $prog: " if [ $UID -ne 0 ]; then RETVAL=1 failure else ulimit -n $NOFILE log_daemon_msg "Starting nfs-ganesha daemon" nfs-ganesha if ! start-stop-daemon --start --oknodo --quiet --pidfile $PID_FILE --exec $PATHPROG -- $OPTIONS; then log_end_msg 1 exit 1 fi touch $LOCK_FILE log_end_msg 0 fi echo } stop() { echo -n $"Stopping $prog: " if [ $UID -ne 0 ]; then RETVAL=1 failure else log_daemon_msg "Stopping nfs-ganesha daemon" nfs-ganesha #start-stop-daemon --stop --quiet --pidfile killproc $PATHPROG RETVAL=$? if [ $RETVAL -eq 0 ]; then log_end_msg 0 rm -rf $LOCK_FILE else log_end_msg 1 fi fi echo } case "$1" in start) start ;; stop) stop ;; restart) if [ -f $LOCK_FILE ] ; then stop #avoid race sleep 3 fi start ;; reload) echo -n $"Reloading $prog: " if [ -f $LOCK_FILE ] ; then killproc -p $PID_FILE $prog -HUP RETVAL=0 else RETVAL=1 fi echo ;; force-reload) stop start ;; try-restart) if [ -f $LOCK_FILE ] ; then stop #avoid race sleep 3 start fi ;; status) if [ $RETVAL -eq 5 ] ; then RETVAL=3 else status -p $PID_FILE $PATHPROG RETVAL=$? fi ;; *) echo $"Usage: $0 {start|stop|restart|reload|try-restart|status}" RETVAL=1 esac exit $RETVAL ����nfs-ganesha-2.6.0/src/scripts/init.d/nfs-ganesha.el6������������������������������������������������0000664�0000000�0000000�00000004735�13242724102�0022164�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash # # chkconfig: 2345 50 50 # description: GANESHA NFS Daemon # # processname: /usr/bin/ganesha.nfsd # config: /etc/ganesha/ganesha.nfsd.conf # pidfile: /var/run/ganesha.nfsd.pid # ### BEGIN INIT INFO # Provides: ganesha # Required-Start: $local_fs $named $time $network # Required-Stop: $local_fs $named $time $network # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: start and stop ganesha daemon # Description: NFS-GANESHA ### END INIT INFO # source function library . /etc/rc.d/init.d/functions PATHPROG=/usr/bin/ganesha.nfsd # Default Ganesha options LOGFILE=/var/log/ganesha/ganesha.log CONFFILE=/etc/ganesha/ganesha.conf prog=ganesha.nfsd PID_FILE=${PID_FILE:=/var/run/${prog}.pid} LOCK_FILE=${LOCK_FILE:=/var/lock/subsys/${prog}} # Generate and read config values [ -x /usr/libexec/ganesha/nfs-ganesha-config.sh ] && /usr/libexec/ganesha/nfs-ganesha-config.sh [ -f /run/sysconfig/ganesha ] && . /run/sysconfig/ganesha [ -z "$NOFILE" ] && NOFILE=1048576 OPTIONS="-L $LOGFILE -f $CONFFILE -N NIV_EVENT -p $PID_FILE $EPOCH" RETVAL=0 start() { echo -n $"Starting $prog: " if [ $UID -ne 0 ]; then RETVAL=1 failure else ulimit -n $NOFILE daemon --pidfile $PID_FILE $PATHPROG $OPTIONS RETVAL=$? if [ $RETVAL -eq 0 ]; then touch $LOCK_FILE else RETVAL=1 fi fi echo } stop() { echo -n $"Stopping $prog: " if [ $UID -ne 0 ]; then RETVAL=1 failure else killproc $PATHPROG RETVAL=$? if [ $RETVAL -eq 0 ]; then rm -f $LOCK_FILE success else failure fi fi echo return $RETVAL } case "$1" in start) start ;; stop) stop ;; restart) if [ -f $LOCK_FILE ] ; then stop #avoid race sleep 3 fi start ;; reload) echo -n $"Reloading $prog: " if [ -f $LOCK_FILE ] ; then killproc -p $PID_FILE $prog -HUP RETVAL=0 else RETVAL=1 fi echo ;; force-reload) stop start ;; try-restart) if [ -f $LOCK_FILE ] ; then stop #avoid race sleep 3 start fi ;; status) if [ $RETVAL -eq 5 ] ; then RETVAL=3 else status -p $PID_FILE $PATHPROG RETVAL=$? fi ;; *) echo $"Usage: $0 {start|stop|restart|reload|try-restart|status}" RETVAL=1 esac exit $RETVAL �����������������������������������nfs-ganesha-2.6.0/src/scripts/init.d/nfs-ganesha.gpfs�����������������������������������������������0000775�0000000�0000000�00000022375�13242724102�0022440�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash # init file for ganesha # # chkconfig: - 50 50 # description: GANESHA NFS Daemon # # processname: /usr/bin/ganesha.nfsd # config: /etc/ganesha/gpfs.ganesha.nfsd.conf # pidfile: /var/run/ganesha.nfsd.pid # ### BEGIN INIT INFO # Provides: ganesha.gpfgpfs.s # Required-Start: $local_fs $network # Required-Stop: $local_fs $network # Should-Start: # Should-Stop: # Default-Start: # Default-Stop: # Short-Description: start and stop ganesha daemon # Description: NFS-GANESHA ### END INIT INFO # source function library . /etc/init.d/functions # Source networking configuration. [ -f /etc/sysconfig/network ] && . /etc/sysconfig/network # Check for and source configuration file otherwise set defaults [ -f /etc/sysconfig/nfs ] && . /etc/sysconfig/nfs PATHPROG=/usr/bin/ganesha.nfsd PROGONLY=ganesha.nfsd #LOGFILE=/var/log/ganesha/ganesha.gpfs.log LOGFILE=SYSLOG #LOGFILE=STDERR #LOGFILE=STDOUT EPOCHFILE=/var/lib/nfs/ganesha-epoch PIDFILE=/var/run/ganesha.nfsd.pid CONFFILE=/etc/ganesha/gpfs.ganesha.nfsd.conf # Remote quota server [ -z "$RQUOTAD" ] && RQUOTAD=`type -path rpc.rquotad` ########################################################### OPTIONS="-f $CONFFILE -L $LOGFILE -N INFO -p $PIDFILE" RETVAL=0 prog="ganesha.nfsd" CMD_LOGGER="/usr/bin/logger -i -t ganesha.gpfs-init " GANRECDIR="/var/lib/nfs/ganesha_local" which sm-notify &> /dev/null HAVE_SMNOTIFY=`echo $?` check_hang() { PID=$1 MAXREDS=2 MAXSTALL=120 FORCECORE=0; NUMREDS=$(ls $GANRECDIR | grep "red" | wc -l) LASTONE=$(ls -t $GANRECDIR | sed 's/_/ /' | awk 'NR > 1 {next} {printf $1} ') # Beware of startup if [ -z $LASTONE ] ; then LASTONE=$(date +"%s") fi TNOW=$(date +"%s") TSTALL=$(($TNOW - $LASTONE)) if [ $NUMREDS -ge $MAXREDS ] ; then $CMD_LOGGER "Deadlock detected: No replies to RPC requests" FORCECORE=1; fi if [ $TSTALL -ge $MAXSTALL ] ; then $CMD_LOGGER "Deadlock detected: stalled for $TSTALL" FORCECORE=1; fi return $FORCECORE } start() { # Start statd, rquotad and Ganesha daemon. # If any of the three daemons are already running, then doesn't restart it. start_statd=1 start_rquotad=0 start_ganesha=1 # Check if the user is root if [ $UID -ne 0 ]; then echo "Ganesha must be started by the root user." RETVAL=1 failure echo return $RETVAL fi if [ ! -x /sbin/rpc.statd ]; then echo "statd is not available" RETVAL=1 failure echo return $RETVAL fi # Make sure the rpc.statd is not already running. if status rpc.statd > /dev/null ; then echo "statd already running." start_statd=0 fi # Make sure the rpc.rquotad is not already running. if status rpc.rquotad > /dev/null ; then echo "rquotad already running." start_rquotad=0 fi # Check if a Ganesha configuration file is available if [ ! -f "$CONFFILE" ]; then echo "The following configuration file is required but does not exist: $CONFFILE" RETVAL=1 failure echo return $RETVAL fi # Check if Ganesha is in zombie state # # Put "stat" format specifier before "comm" to avoid dealing # with blanks in comm! Also specify column width for "comm" # field to have enough chars for matching with 'ganesha' result=`ps -eLo pid,stat,comm:30|awk '$2 ~ /[Zz]/ && $3 ~ /ganesha/ {print $1}'` if [ "$result" ]; then echo "GPFS Ganesha is in zombie state." echo "$result" | while read -r PID; do for TID in /proc/$PID/task/*; do echo "vvvvvvvv $TID vvvvvvvvv" cat $TID/stack | while read -r RL; do echo "$RL" done; done; echo "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" done; start_ganesha=0 fi # Check if Ganesha is already running result=`pidof "$PATHPROG"` if [ ! $result ]; then result=`pidof "$PROGONLY"` fi if [ $result ]; then echo "Ganesha is already running." start_ganesha=0 fi # start the statd daemon only if it is not running if [ $start_statd -eq 1 ]; then rm -f /var/lock/subsys/rpc.statd # Make sure locks are recovered if [ $HAVE_SMNOTIFY -eq 0 ]; then rm -f /var/run/sm-notify.pid fi echo -n $"Starting NFS statd: " # Set statd's local hostname if defined [ -n "${STATD_HOSTNAME}" ] && STATDARG="$STATDARG -n ${STATD_HOSTNAME}" # See if a statd's ports has been defined [ -n "$STATD_PORT" ] && STATDARG="$STATDARG -p $STATD_PORT" [ -n "$STATD_OUTGOING_PORT" ] \ && STATDARG="$STATDARG -o $STATD_OUTGOING_PORT" # See if we have an HA-callout program specified [ -n "$STATD_HA_CALLOUT" ] \ && STATDARG="$STATDARG -H $STATD_HA_CALLOUT" daemon rpc.statd "$STATDARG" RETVAL=$? echo [ $RETVAL -eq 0 ] && touch /var/lock/subsys/rpc.statd if [ $RETVAL -ne 0 ]; then failure return $RETVAL fi fi # start the rquotad daemon only if it is not running if [ $start_rquotad -eq 1 ]; then if [ -n "$RQUOTAD" ] ; then echo -n $"Starting rquotad: " [ -n "$RQUOTAD_PORT" ] \ && RPCRQUOTADOPTS="$RPCRQUOTADOPTS -p $RQUOTAD_PORT" daemon rpc.rquotad $RPCRQUOTADOPTS RETVAL=$? echo if [ $RETVAL -ne 0 ]; then failure echo " " return $RETVAL fi fi fi # Start the Ganesha daemon only if it is not running if [ $start_ganesha -eq 1 ]; then which ctdb &> /dev/null if [ $? -eq 0 ]; then prev_cntr=0 [ -f $EPOCHFILE ] && prev_cntr=$(cat $EPOCHFILE) [ -z $prev_cntr ] && prev_cntr=0 new_cntr=$(( prev_cntr + 1 )) #Epoch = (ctdb node number << 16) | (new_cntr & 0xFFFF) ganesha_epoch=$(( `ctdb pnn | awk -F : '{print $2}'` << 16 | new_cntr & 0xFFFF )) echo $new_cntr > $EPOCHFILE OPTIONS="$OPTIONS -E $ganesha_epoch" fi echo -n $"Starting $prog: " ulimit -n 1048576 #Remove the status tracking files rm -rf /var/lib/nfs/ganesha_local/* daemon numactl -i all $PATHPROG $OPTIONS RETVAL=$? if [ $HAVE_SMNOTIFY -eq 0 ]; then sm-notify -f fi # Call CTDB's smnotify [ -n "${STATD_HOSTNAME}" ] \ && STATD_CALLOUT="`echo $STATD_HOSTNAME | xargs -n 3 -d " "| awk '{print $3}'`" if [ -n "$STATD_CALLOUT" ]; then $STATD_CALLOUT notify fi # Sleeping here gives Ganesha an adequate amount of time to # initialize the server and fail if anything goes wrong. # Without this sleep and the server will be in the midst of # initializing. It may fail immediately after this script runs. sleep 2 # Check if Ganesha is still running result=`pidof "$PATHPROG"` if [ ! $result ]; then result=`pidof "$PROGONLY"` fi if [ ! $result ]; then failure echo return $RETVAL fi [ $RETVAL -eq 0 ] && touch /var/lock/subsys/ganesha.nfsd fi echo return $RETVAL } stop() { # Check if user is root if [ $UID -ne 0 ]; then echo "Ganesha must be stopped by the root user." RETVAL=1 failure echo return $RETVAL fi echo -n $"Stopping rquotad: " killproc rpc.rquotad RETVAL=$? echo echo -n $"Stopping statd: " killproc rpc.statd RETVAL=$? rm -f /var/lock/subsys/rpc.statd if [ $HAVE_SMNOTIFY -eq 0 ]; then rm -f /var/run/sm-notify.pid fi echo # Kill the Ganesha process # pidof on some systems use only the process's name # pidof on other systems use the full pathname of the process binary echo -n $"Stopping $prog: " pid=`pidof "$PATHPROG"` if [ ! $pid ]; then PROCNAME=$PROGONLY pid=`pidof "$PROGONLY"` else PROCNAME=$PATHPROG fi if [ $pid ]; then # Ganesha is running. Stop it. kill -TERM $pid >/dev/null 2>&1 # Unconditionally delete this file. rm -f /var/lock/subsys/ganesha.nfsd #Sleep for 1 second before checking the status, If the process exists #even after 30 iterations, send Kill waitcnt=0 while true; do if checkpid $pid && sleep 1; then waitcnt=$(( waitcnt + 1 )) if [ $waitcnt -eq 30 ]; then kill -KILL $pid >/dev/null 2>&1 break fi else break fi done # Give Ganesha time to quit result=`pidof "$PROCNAME"` while true; do if [ $result ] && [ $result -eq $pid ]; then echo "Waiting for ganesha to stop ..." sleep .1 result=`pidof "$PROCNAME"` else break fi done fi echo return $RETVAL } restart(){ stop start return $RETVAL } condrestart(){ [ -e /var/lock/subsys/ganesha.nfsd ] && restart return 0 } reload() { result=`pidof "$PATHPROG"` if [ $result ]; then echo "Reloading Ganesha Exports" kill -HUP $result; fi } ganesha_status() { result=`pidof "$PATHPROG"` if [ $result ]; then echo "Ganesha is running." RETVAL=0 else echo "Ganesha is stopped" RETVAL=1 fi status rpc.statd if [ -n "$RQUOTAD" ] ; then status rpc.rquotad fi return $RETVAL } case "$1" in start) start ;; stop) stop ;; restart) restart ;; reload) reload ;; condrestart) condrestart ;; status) ganesha_status ;; *) echo $"Usage: $0 {start|stop|status|restart|condrestart|reload}" RETVAL=1 esac exit $RETVAL �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/init.d/sysconfig/�����������������������������������������������������0000775�0000000�0000000�00000000000�13242724102�0021355�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/init.d/sysconfig/ganesha����������������������������������������������0000664�0000000�0000000�00000000126�13242724102�0022705�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������LOGFILE=/var/log/ganesha.log CONFFILE=/etc/ganesha/ganesha.conf EPOCH_EXEC=/bin/true ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/misc/�����������������������������������������������������������������0000775�0000000�0000000�00000000000�13242724102�0017117�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/misc/pb_linkcount.ksh�������������������������������������������������0000664�0000000�0000000�00000000472�13242724102�0022320�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/ksh ##### LE TEST SUPPOSE QUE LE FILESYSTEM EST MONTE SUR LE REPERTOIRE /mnt ##### set -x trace TEST_REP=/mnt/dir.$$ # creation d'une respertoire vide mkdir $TEST_REP # on se place dedans cd $TEST_REP # on cree un nouveau repertoire mkdir toto # find gueule car le linkcount n'est pas bon find . -ls ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/misc/pb_stale_create.ksh����������������������������������������������0000664�0000000�0000000�00000002401�13242724102�0022737�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/ksh MNT_1=$1 if [[ ! -d $MNT_1 ]]; then echo "Usage $0 <mnt_dir>" exit 1 fi DIR_1="$MNT_1/TEST_STALE.$$" #creation du repertoire mkdir "$DIR_1" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding mkdir($DIR_1)" exit 1 fi # creation fichier1 touch "$DIR_1/file.1" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding touch($DIR_1/file.1)" exit 1 fi ls -l "$DIR_1" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding ls ($DIR_1)" exit 1 fi ls -l "$MNT_1" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding ls ($DIR_1)" exit 1 fi echo "###############################################" echo echo "Maintenant, supprimer le repertoire $DIR_1" echo "directement dans le filesystem (via scrub)" echo "(commande 'unlink TEST_STALE.$$ recurse top')" echo echo "Taper ensuite <enter> pour continuer" echo echo "###############################################" read input echo "On tente de creer un fichier dans le repertoire supprime" touch "$DIR_1/file.2" echo "Entree correspondante dans le parent" ls -l "$MNT_1" | grep "TEST_STALE.$$" echo "On attend 5s et on liste a nouveau" sleep 5 ls -l "$MNT_1" | grep "TEST_STALE.$$" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/misc/pb_stale_rename1.ksh���������������������������������������������0000664�0000000�0000000�00000003264�13242724102�0023034�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/ksh MNT_1=$1 if [[ ! -d $MNT_1 ]]; then echo "Usage $0 <mnt_dir>" exit 1 fi DIR_1="$MNT_1/TEST_STALE.$$" DIR_2="$MNT_1/TEST_STALE.$$_bis" #creation du repertoire 1 mkdir "$DIR_1" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding mkdir($DIR_1)" exit 1 fi # creation fichier1 touch "$DIR_1/file.1" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding touch($DIR_1/file.1)" exit 1 fi ls -l "$DIR_1" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding ls ($DIR_1)" exit 1 fi ls -l "$MNT_1" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding ls ($DIR_1)" exit 1 fi #creation du repertoire 2 mkdir "$DIR_2" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding mkdir($DIR_2)" exit 1 fi ls -l "$DIR_2" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding ls ($DIR_2)" exit 1 fi echo "###############################################" echo echo "Maintenant, supprimer le repertoire $DIR_2" echo "directement dans le filesystem (via scrub)" echo "(commande 'unlink TEST_STALE.$$_bis recurse top')" echo echo "Taper ensuite <enter> pour continuer" echo echo "###############################################" read input echo "On tente de deplacer un fichier dans le repertoire supprime" mv "$DIR_1/file.1" "$DIR_2/file.1" echo "L'entree source :" ls -l "$DIR_1/file.1" echo "L'entree destination :" ls -l "$DIR_2/file.1" echo "Entrees dans le repertoire parent" ls -l "$MNT_1" | grep "TEST_STALE.$$" echo "On attend 5s et on liste a nouveau" sleep 5 ls -l "$MNT_1" | grep "TEST_STALE.$$" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/misc/pb_stale_rename2.ksh���������������������������������������������0000664�0000000�0000000�00000003243�13242724102�0023032�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/ksh MNT_1=$1 if [[ ! -d $MNT_1 ]]; then echo "Usage $0 <mnt_dir>" exit 1 fi DIR_1="$MNT_1/TEST_STALE.$$" DIR_2="$MNT_1/TEST_STALE.$$_bis" #creation du repertoire 1 mkdir "$DIR_1" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding mkdir($DIR_1)" exit 1 fi # creation fichier1 touch "$DIR_1/file.1" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding touch($DIR_1/file.1)" exit 1 fi ls -l "$DIR_1" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding ls ($DIR_1)" exit 1 fi ls -l "$MNT_1" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding ls ($DIR_1)" exit 1 fi #creation du repertoire 2 mkdir "$DIR_2" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding mkdir($DIR_2)" exit 1 fi ls -l "$DIR_2" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding ls ($DIR_2)" exit 1 fi echo "###############################################" echo echo "Maintenant, supprimer le fichier $DIR_1/file.1" echo "directement dans le filesystem (via scrub)" echo "(commande 'unlink TEST_STALE.$$/file.1')" echo echo "Taper ensuite <enter> pour continuer" echo echo "###############################################" read input echo "On tente de deplacer un fichier source supprime" mv "$DIR_1/file.1" "$DIR_2/file.1" echo "L'entree source :" ls -l "$DIR_1/file.1" echo "L'entree destination :" ls -l "$DIR_2/file.1" echo "Entrees dans le repertoire parent" ls -l "$MNT_1" | grep "TEST_STALE.$$" echo "On attend 5s et on liste a nouveau" sleep 5 ls -l "$MNT_1" | grep "TEST_STALE.$$" �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/misc/pb_stale_setattr.ksh���������������������������������������������0000664�0000000�0000000�00000002365�13242724102�0023173�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/ksh MNT_1=$1 if [[ ! -d $MNT_1 ]]; then echo "Usage $0 <mnt_dir>" exit 1 fi DIR_1="$MNT_1/TEST_STALE.$$" #creation du repertoire mkdir "$DIR_1" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding mkdir($DIR_1)" exit 1 fi # creation fichier1 touch "$DIR_1/file.1" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding touch($DIR_1/file.1)" exit 1 fi ls -l "$DIR_1" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding ls ($DIR_1)" exit 1 fi ls -l "$MNT_1" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding ls ($DIR_1)" exit 1 fi echo "###############################################" echo echo "Maintenant, supprimer le repertoire $DIR_1" echo "directement dans le filesystem (via scrub)" echo "(commande 'unlink TEST_STALE.$$ recurse top')" echo echo "Taper ensuite <enter> pour continuer" echo echo "###############################################" read input echo "On tente de faire un 'touch' le repertoire supprime" touch "$DIR_1" echo "Entree correspondante dans le parent" ls -l "$MNT_1" | grep "TEST_STALE.$$" echo "On attend 5s et on liste a nouveau" sleep 5 ls -l "$MNT_1" | grep "TEST_STALE.$$" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/misc/pb_stale_unlink.ksh����������������������������������������������0000664�0000000�0000000�00000002711�13242724102�0023000�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/ksh MNT_1=$1 if [[ ! -d $MNT_1 ]]; then echo "Usage $0 <mnt_dir>" exit 1 fi DIR_1="$MNT_1/TEST_STALE.$$" #creation du repertoire mkdir "$DIR_1" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding mkdir($DIR_1)" exit 1 fi # creation fichier touch "$DIR_1/file.1" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding touch($DIR_1/file.1)" exit 1 fi touch "$DIR_1/file.2" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding touch($DIR_1/file.2)" exit 1 fi ls -l "$DIR_1" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding ls ($DIR_1)" exit 1 fi ls -l "$MNT_1" > /dev/null status=$? if (( $status != 0 )); then echo "ERROR status proceeding ls ($DIR_1)" exit 1 fi echo "###############################################" echo echo "Maintenant, supprimer le repertoire $DIR_1" echo "directement dans le filesystem (via scrub)" echo "(commande 'unlink TEST_STALE.$$ recurse top')" echo echo "Taper ensuite <enter> pour continuer" echo echo "###############################################" read input echo "On tente de supprimer le repertoire STALE" rm -rf "$DIR_1" # listing dans le second repertoire echo "On tente de lister le repertoire STALE" ls -l "$DIR_1" echo "Entree dans le parent" ls -l "$MNT_1" | grep "TEST_STALE.$$" echo "On attend 5s et on liste a nouveau" sleep 5 ls -l "$MNT_1" | grep "TEST_STALE.$$" �������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/nfs-ganesha-config.sh�������������������������������������������������0000664�0000000�0000000�00000001515�13242724102�0022157�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # # Extract configuration from /etc/sysconfig/ganesha and # copy or generate new environment variables to # /run/sysconfig/ganesha to be used by nfs-ganesha service # CONFIGFILE=/etc/sysconfig/ganesha RUNCONFIG=/run/sysconfig/ganesha if [ ! -e $(command dirname ${CONFIGFILE} 2>/dev/null) ]; then # Debian/Ubuntu CONFIGFILE=/etc/ganesha/nfs-ganesha RUNCONFIG=/etc/default/nfs-ganesha fi if [ -r ${CONFIGFILE} ]; then . ${CONFIGFILE} [ -x ${EPOCH_EXEC} ] && EPOCHVALUE=`${EPOCH_EXEC}` mkdir -p $(command dirname ${RUNCONFIG} 2>/dev/null) { cat ${CONFIGFILE} [ -n "${EPOCHVALUE}" ] && echo EPOCH=\"-E $EPOCHVALUE\" # Set NUMA options if numactl is present NUMACTL=$(command -v numactl 2>/dev/null) if [ -n "${NUMACTL}" ]; then echo NUMACTL=${NUMACTL} echo NUMAOPTS=--interleave=all fi } > ${RUNCONFIG} fi �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/pycheckpatch����������������������������������������������������������0000775�0000000�0000000�00000005716�13242724102�0020571�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/env python # Run checkpatch against commits specified. It just passes the arguments # to "git rev-list" command to get the list of commits except "-a" option. # # If "-a" option is given, it would run checkpatch against all commits. # Otherwise, it would stop at the very first commit that fails # checkpatch. # # Example: # pycheckpatch V2.2-dev-15..HEAD # pycheckpatch -a V2.2-dev-15..HEAD import os, sys, subprocess checkpatch = "./src/scripts/checkpatch.pl" if len(sys.argv) < 2: sys.exit('\tUsage: %s [-a] [rev-list-opts] {rev-list-args}' % sys.argv[0]) rev_list_args = sys.argv[1:] check_all = False if "-a" in rev_list_args: rev_list_args.remove("-a") check_all = True # Python 2.6 lacks check_output method. Following code copied from # Python2.7 source code to work with python2.6 or lower versions. if "check_output" not in dir(subprocess): def f(*popenargs, **kwargs): if 'stdout' in kwargs: raise ValueError('stdout argument not allowed, it will be overridden.') process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs) output, unused_err = process.communicate() retcode = process.poll() if retcode: cmd = kwargs.get("args") if cmd is None: cmd = popenargs[0] error = subprocess.CalledProcessError(retcode, cmd) error.output = output raise error return output subprocess.check_output = f cmd = "git rev-parse --show-toplevel" output = subprocess.check_output(cmd, shell=True) # change the cwd to top level git directory for checkpatch config file. os.chdir(output.strip()) cmd = ['git', 'rev-list', '--no-merges'] + rev_list_args output = subprocess.check_output(cmd) commits = output.splitlines() if len(commits) == 0: sys.exit("'%s' resulted in zero commits to check" % " ".join(rev_list_args)) result = [] for commit in commits: cmd = "git show %s --format=email | %s -" % (commit, checkpatch) try: subprocess.check_output(cmd, shell=True) result.append((commit, True, "")) except subprocess.CalledProcessError as e: result.append((commit, False, e.output)) no_tree_error = "Must be run from the top-level dir. of a kernel tree" if no_tree_error in e.output: out = """ You don't have ganesha checkpatch config file placed in the top level git directory. "cp ./src/scripts/checkpatch.conf .checkpatch.conf" should fix the error! checkpatch.pl failed with the following error message: %s""" % e.output sys.exit(out) elif not check_all: break # check all statuses status = [s for c,s,o in result] if all(status): print("All commits passed checkpatch!") else: print("Not all commits passed checkpatch! :-(") for commit,status,out in result: s = "passed" if status else "failed" print("COMMIT %s %s" % (commit, s)) if not status: print(out) sys.exit(1) ��������������������������������������������������nfs-ganesha-2.6.0/src/scripts/reindenture�����������������������������������������������������������0000775�0000000�0000000�00000000767�13242724102�0020450�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh if [ $# -eq 0 ]; then echo usage: reindenture dir1 [dir2 ... dirn] 1>&2 exit 1 fi; while [ $# -gt 0 ]; do dir=$1 echo Reindenting files under $dir env VERSION_CONTROL=simple \ find $dir -type f \( -name \*.c -o -name \*.h \) -print0 | \ xargs -0 indent -nbad -bap -nbc -bbo -hnl -br -brs -c33 -cd33 \ -ncdb -ce -ci4 -cli0 -d0 -di1 -nfc1 -i8 -ip0 -l80 -lp -npcs \ -nprs -npsl -sai -saf -saw -ncs -nsc -sob -nfca -cp33 -ss -ts8 \ -il1 --ignore-newlines shift done ���������nfs-ganesha-2.6.0/src/scripts/runcp.sh��������������������������������������������������������������0000775�0000000�0000000�00000022350�13242724102�0017654�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh #------------------------------------------------------------------------------- # Copyright Panasas, 2013 # Contributor: Frank Filz <ffilzlnx@mindspring.com> # # # This software is a server that implements the NFS protocol. # # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 3 of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #------------------------------------------------------------------------------- # Find directory where runcp.sh lives for checkpatch will be there too # We keep that in CURDIR CURDIR=$(dirname $(readlink -m $0)) check_one_file() { FDIR=`echo $1 | sed -e "s@$DIR\(.*\)/.*@$ODIR\1@"` OUTFILE=`echo $1 | sed -e "s@$DIR\(.*\)@$ODIR\1.cp@"` #echo FIDR=$FIDR DIR=$DIR FILE=$1 OUTFILE=$OUTFILE if [ $ONEFILE -eq 1 ] then OUTFILE=$TEMP elif [ ! -d $FDIR ] then if [ -e $FDIR ] then echo "Oops, $FDIR is not a directory" return fi mkdir -p $FDIR fi EXTRA_OPT="" if [ $NO_SPACING -eq 1 ] then echo $1 > $OUTFILE egrep $NO_SPACING_FILES $OUTFILE 2>&1 >/dev/null RC=$? if [ $RC -eq 0 ] then EXTRA_OPT="$EXTRA_OPT --ignore SPACING" fi egrep $NO_MACRO_W_FLOW_FILES $OUTFILE 2>&1 >/dev/null RC=$? if [ $RC -eq 0 ] then EXTRA_OPT="$EXTRA_OPT --ignore MACRO_WITH_FLOW_CONTROL" fi egrep $NO_COMPLEX_MACRO_FILES $OUTFILE 2>&1 >/dev/null RC=$? if [ $RC -eq 0 ] then EXTRA_OPT="$EXTRA_OPT --ignore COMPLEX_MACRO" fi egrep $NO_DEEP_INDENTATION_FILES $OUTFILE 2>&1 >/dev/null RC=$? if [ $RC -eq 0 ] then EXTRA_OPT="$EXTRA_OPT --ignore DEEP_INDENTATION" fi egrep $NO_BRACKET_SPACE_FILES $OUTFILE 2>&1 >/dev/null RC=$? if [ $RC -eq 0 ] then EXTRA_OPT="$EXTRA_OPT --ignore BRACKET_SPACE" fi egrep $NO_DATE_TIME_FILES $OUTFILE 2>&1 >/dev/null RC=$? if [ $RC -eq 0 ] then EXTRA_OPT="$EXTRA_OPT --ignore DATE_TIME" fi egrep $NO_STATIC_CONST_CHAR_ARRAY_FILES $OUTFILE 2>&1 >/dev/null RC=$? if [ $RC -eq 0 ] then EXTRA_OPT="$EXTRA_OPT --ignore STATIC_CONST_CHAR_ARRAY" fi egrep $NO_ENOSYS_FILES $OUTFILE 2>&1 >/dev/null RC=$? if [ $RC -eq 0 ] then EXTRA_OPT="$EXTRA_OPT --ignore ENOSYS" fi fi $CURDIR/checkpatch.pl $TYPEDEF $EXTRA_OPT \ --file $1 > $OUTFILE 2> $ERROR_FILE RESULT=`grep '^total:' $OUTFILE` if [ -n "$REPORT_FILT" ] then grep "$REPORT_FILT" $OUTFILE > /dev/null 2>&1 RC=$? else RC=1 fi if [ $RC -eq 1 ] || [ -s $ERROR_FILE ] then if [ $ONEFILE -eq 1 ] then echo "=========================================================================================================================" >>$REPORT_FILE if [ $NO_CRUFT -eq 1 ] then cat $OUTFILE | \ egrep -v "NOTE: Ignored message|If any of these errors|them to the maintainer" \ >>$REPORT_FILE else cat $OUTFILE >>$REPORT_FILE fi cat $ERROR_FILE >>$REPORT_FILE else echo $1 $RESULT >>$REPORT_FILE cat $ERROR_FILE | sed -e "s@\(.*\)@$1 \1@" >>$REPORT_FILE cat $ERROR_FILE >>$OUTFILE fi if [ $QUIET -eq 0 ] then echo $1 $RESULT cat $ERROR_FILE | sed -e "s@\(.*\)@$1 \1@" fi fi } check_files() { while [ -n "$1" ] do if [ -s "$1" ] then check_one_file $1 fi shift done } check_find() { check_files `find $DIR -name '*.[ch]' | egrep -v "$EXCLUDE" | sort` } check_git_files() { while [ -n "$1" ] do check_one_file $DIR/$1 shift done } check_git() { echo "diff --name-only $COMMIT | egrep -v $EXCLUDE" git diff --name-only $COMMIT | egrep -v "$EXCLUDE" check_git_files `git diff --name-only $COMMIT | egrep -v "$EXCLUDE" | sort` } show_help() { cat << ... Options: -c Don't show \"Clean\" files (files with no errors and no warnings) -w Don't show files that had no errors (even if they have warnings) -c prevails over -w -q Quiet, don't show report in progress -1 Put the results of each check into a separate file in the output dir for example, results for checkpatch of foo/test.c would show up in /tmp/checkpatch/foo/test.c.cp -d {dir} Directory to check (including sub-directories), defaults to current working directory: -x Exclude files egrep expression (libtirpc|libntirpc is prepended to the expression) -v turn off: --ignore SPACING on RPC program header files (like nfs23.h) and --ignore COMPLEX_MACRO on certain files --ignore BRACKET_SPACE in certain files --ignore DEEP_INDENTATION in certain files -i Include files agreed on to ignore (config_parsing|Protocols/XDR -e Include files from external prohects (murmur3|cidr|atomic_x86|city) -g Use git-diff --name-only instead of find (-d will be ignored) -k Specify commit for git-diff, default is HEAD -o {dir} Directory to direct output files to, defaults to /tmp/checkpatch -r Output report at end -t Ignore typedefs ... } CLEAN=0 DIR="." ALWAYS="libtirpc|libntirpc|CMakeFiles|tools/test_findlog.c|include/config.h" ALWAYS="$ALWAYS|nfsv41.h|nlm4.h|nsm.h|rquota.h" EXTERNAL="murmur3.h|cidr.h|cidr/|atomic_x86_64.h|include/city|avltree.h" EXTERNAL="$EXTERNAL|test/test_atomic_x86_86.c|avl/|FSAL/FSAL_GPFS/include" NO_EXTERNAL=0 IGNORE="config_parsing|Protocols/XDR" IGNORE="$IGNORE|include/gsh_intrinsic.h" NO_IGNORE=0 EXCLUDE="" QUIET=0 ODIR=/tmp/checkpatch NOWARN=0 ONEFILE=1 REPORT=0 FIND=check_find COMMIT=HEAD TYPEDEF="" NO_CRUFT=0 NO_SPACING_FILES="nfs23.h|nfsv41.h|nlm4.h|nsm.h|rquota.h" NO_COMPLEX_MACRO_FILES="include/ganesha_dbus.h|include/server_stats_private.h" NO_COMPLEX_MACRO_FILES="$NO_COMPLEX_MACRO_FILES|include/gsh_intrinsic.h" NO_COMPLEX_MACRO_FILES="$NO_COMPLEX_MACRO_FILES|support/exports.c" NO_COMPLEX_MACRO_FILES="$NO_COMPLEX_MACRO_FILES|support/export_mgr.c" NO_COMPLEX_MACRO_FILES="$NO_COMPLEX_MACRO_FILES|include/gsh_dbus.h" NO_MACRO_W_FLOW_FILES="FSAL/FSAL_PROXY/handle_mapping/handle_mapping_db.c" NO_MACRO_W_FLOW_FILES="$NO_MACRO_W_FLOW_FILES|include/9p.h" NO_MACRO_W_FLOW_FILES="$NO_MACRO_W_FLOW_FILES|multilock/ml_functions.c" NO_DEEP_INDENTATION_FILES="cache_inode/cache_inode_lru.c|include/rbt_tree.h" NO_BRACKET_SPACE_FILES="include/nfs_req_queue.h" NO_DATE_TIME_FILES="MainNFSD/nfs_main.c" NO_STATIC_CONST_CHAR_ARRAY_FILES="FSAL_CEPH/main.c" NO_ENOSYS_FILES="FSAL_GPFS/fsal_up.c|FSAL_GPFS/gpfsext.c" NO_SPACING=1 while getopts "cd:x:qo:?w1rgk:tievK" OPT; do case $OPT in c) CLEAN=1 ;; w) NOWARN=1 ;; q) QUIET=1 ;; 1) ONEFILE=0 ;; d) DIR=$OPTARG ;; x) EXCLUDE="$OPTARG" ;; i) NO_IGNORE=1 ;; e) NO_EXTERNAL=1 ;; g) FIND=check_git ;; k) COMMIT=$OPTARG ;; o) ODIR=$OPTARG ;; r) REPORT=1 ;; t) TYPEDEF="--ignore NEW_TYPEDEFS" ;; v) NO_SPACING=0 ;; K) NO_CRUFT=1 ;; ?) show_help exit ;; esac done if [ $DIR = '.' ] then DIR=`pwd` fi if [ ! -d $ODIR ] then if [ -e $ODIR ] then echo "Oops, $ODIR is not a directory" return fi mkdir -p $ODIR fi if [ $FIND = check_git ] then DIR=`git rev-parse --show-toplevel` fi REPORT_FILE=$ODIR/results.cp TEMP=$ODIR/results.temp ERROR_FILE=$ODIR/results.err date > $REPORT_FILE if [ $FIND = check_git ] then echo "FILES: git-diff --name-only $COMMIT" >> $REPORT_FILE else echo "FILES: find $DIR -name '*.[ch]'" >> $REPORT_FILE fi echo "CLEAN=$CLEAN NOWARN=$NOWARN TYPEDEF=$TYPEDEF" >> $REPORT_FILE if [ $NO_SPACING -eq 1 ] then echo >> $REPORT_FILE echo "NO_SPACING_FILES=$NO_SPACING_FILES" >> $REPORT_FILE echo "NO_COMPLEX_MACRO_FILES=$NO_COMPLEX_MACRO_FILES" >> $REPORT_FILE echo "NO_MACRO_W_FLOW_FILES=$NO_MACRO_W_FLOW_FILES" >> $REPORT_FILE echo "NO_DEEP_INDENTATION_FILES=$NO_DEEP_INDENTATION_FILES" >> $REPORT_FILE echo "NO_BRACKET_SPACE_FILES=$NO_BRACKET_SPACE_FILES" >> $REPORT_FILE echo "NO_DATE_TIME_FILES=$NO_DATE_TIME_FILES" >> $REPORT_FILE echo "NO_STATIC_CONST_CHAR_ARRAY_FILES=$NO_STATIC_CONST_CHAR_ARRAY_FILES" >> $REPORT_FILE echo "NO_ENOSYS_FILES=$NO_ENOSYS_FILES" >> $REPORT_FILE fi if [ -n "$EXCLUDE" ] then echo >> $REPORT_FILE echo "EXCLUDE=$EXCLUDE" >> $REPORT_FILE fi if [ $NO_IGNORE -eq 0 ] then if [ -n "$EXCLUDE" ] then EXCLUDE="$IGNORE|$EXCLUDE" else EXCLUDE="$IGNORE" fi echo >> $REPORT_FILE echo "IGNORE=$IGNORE" >> $REPORT_FILE fi if [ $NO_EXTERNAL -eq 0 ] then if [ -n "$EXCLUDE" ] then EXCLUDE="$EXTERNAL|$EXCLUDE" else EXCLUDE="$EXTERNAL" fi echo >> $REPORT_FILE echo "EXTERNAL=$EXTERNAL" >> $REPORT_FILE fi EXCLUDE="$ALWAYS|$EXCLUDE" echo >> $REPORT_FILE if [ $QUIET -eq 0 ] then cat $REPORT_FILE fi if [ $CLEAN -eq 1 ] then REPORT_FILT="^total: 0 errors, 0 warnings" elif [ $NOWARN -eq 1 ] then REPORT_FILT="^total: 0 errors" else REPORT_FILT="" fi $FIND if [ -e $TEMP ] then rm -f $TEMP rm -f $ERROR_FILE fi if [ $REPORT -eq 1 ] then cat $REPORT_FILE fi ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/spelling.txt����������������������������������������������������������0000664�0000000�0000000�00000051422�13242724102�0020546�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Originally from Debian's Lintian tool. Various false positives have been # removed, and various additions have been made as they've been discovered # in the kernel source. # # License: GPLv2 # # The format of each line is: # mistake||correction # abandonning||abandoning abigious||ambiguous abitrate||arbitrate abov||above abreviated||abbreviated absense||absence absolut||absolute absoulte||absolute acccess||access acceleratoin||acceleration accelleration||acceleration accesing||accessing accesnt||accent accessable||accessible accesss||access accidentaly||accidentally accidentually||accidentally accoding||according accomodate||accommodate accomodates||accommodates accordign||according accoring||according accout||account accquire||acquire accquired||acquired acessable||accessible acess||access achitecture||architecture acient||ancient acitions||actions acitve||active acknowldegement||acknowldegement acknowledgement||acknowledgment ackowledge||acknowledge ackowledged||acknowledged acording||according activete||activate acumulating||accumulating adapater||adapter addional||additional additionaly||additionally addres||address addreses||addresses addresss||address aditional||additional aditionally||additionally aditionaly||additionally adminstrative||administrative adress||address adresses||addresses adviced||advised afecting||affecting agaist||against albumns||albums alegorical||allegorical algorith||algorithm algorithmical||algorithmically algoritm||algorithm algoritms||algorithms algorrithm||algorithm algorritm||algorithm allign||align allocatrd||allocated allocte||allocate allpication||application alocate||allocate alogirhtms||algorithms alogrithm||algorithm alot||a lot alow||allow alows||allows altough||although alue||value ambigious||ambiguous amoung||among amout||amount analysator||analyzer ang||and anniversery||anniversary annoucement||announcement anomolies||anomalies anomoly||anomaly anway||anyway aplication||application appearence||appearance applicaion||application appliction||application applictions||applications appplications||applications appropiate||appropriate appropriatly||appropriately approriate||appropriate approriately||appropriately aquainted||acquainted aquired||acquired arbitary||arbitrary architechture||architecture arguement||argument arguements||arguments aritmetic||arithmetic arne't||aren't arraival||arrival artifical||artificial artillary||artillery assiged||assigned assigment||assignment assigments||assignments assistent||assistant assocation||association associcated||associated assotiated||associated assum||assume assumtpion||assumption asuming||assuming asycronous||asynchronous asynchnous||asynchronous atomatically||automatically atomicly||atomically attachement||attachment attched||attached attemps||attempts attruibutes||attributes authentification||authentication automaticaly||automatically automaticly||automatically automatize||automate automatized||automated automatizes||automates autonymous||autonomous auxilliary||auxiliary avaiable||available avaible||available availabe||available availabled||available availablity||availability availale||available availavility||availability availble||available availiable||available avalable||available avaliable||available aysnc||async backgroud||background backword||backward backwords||backwards bahavior||behavior bakup||backup baloon||balloon baloons||balloons bandwith||bandwidth batery||battery beacuse||because becasue||because becomming||becoming becuase||because beeing||being befor||before begining||beginning beter||better betweeen||between bianries||binaries bitmast||bitmask boardcast||broadcast borad||board boundry||boundary brievely||briefly broadcat||broadcast cacluated||calculated caculation||calculation calender||calendar calle||called calucate||calculate calulate||calculate cancelation||cancellation capabilites||capabilities capabitilies||capabilities capatibilities||capabilities carefuly||carefully cariage||carriage catagory||category challange||challenge challanges||challenges chanell||channel changable||changeable channle||channel channnel||channel charachter||character charachters||characters charactor||character charater||character charaters||characters charcter||character checksuming||checksumming childern||children childs||children chiled||child chked||checked chnage||change chnages||changes chnnel||channel choosen||chosen chouse||chose circumvernt||circumvent claread||cleared clared||cleared closeing||closing clustred||clustered collapsable||collapsible colorfull||colorful comand||command comit||commit commerical||commercial comming||coming comminucation||communication commited||committed commiting||committing committ||commit commoditiy||commodity compability||compatibility compaibility||compatibility compatability||compatibility compatable||compatible compatibiliy||compatibility compatibilty||compatibility compilant||compliant compleatly||completely completly||completely complient||compliant componnents||components compres||compress compresion||compression comression||compression comunication||communication conbination||combination conditionaly||conditionally conected||connected configuratoin||configuration configuraton||configuration configuretion||configuration conider||consider conjuction||conjunction connectinos||connections connnection||connection connnections||connections consistancy||consistency consistant||consistent containes||contains containts||contains contaisn||contains contant||contact contence||contents continous||continuous continously||continuously continueing||continuing contraints||constraints controled||controlled controler||controller controll||control contruction||construction contry||country convertion||conversion convertor||converter convienient||convenient convinient||convenient corected||corrected correponding||corresponding correponds||corresponds correspoding||corresponding cotrol||control couter||counter coutner||counter cryptocraphic||cryptographic cunter||counter curently||currently dafault||default deafult||default deamon||daemon decompres||decompress decription||description defailt||default defferred||deferred definate||definite definately||definitely defintion||definition defualt||default defult||default deivce||device delared||declared delare||declare delares||declares delaring||declaring delemiter||delimiter dependancies||dependencies dependancy||dependency dependant||dependent depreacted||deprecated depreacte||deprecate desactivate||deactivate desciptors||descriptors descrition||description descritptor||descriptor desctiptor||descriptor desriptor||descriptor desriptors||descriptors destory||destroy destoryed||destroyed destorys||destroys destroied||destroyed detabase||database develope||develop developement||development developped||developed developpement||development developper||developer developpment||development deveolpment||development devided||divided deviece||device diable||disable dictionnary||dictionary diferent||different differrence||difference difinition||definition diplay||display direectly||directly disapear||disappear disapeared||disappeared disappared||disappeared disconnet||disconnect discontinous||discontinuous dispertion||dispersion dissapears||disappears distiction||distinction docuentation||documentation documantation||documentation documentaion||documentation documment||document dorp||drop dosen||doesn downlad||download downlads||downloads druing||during dynmaic||dynamic easilly||easily ecspecially||especially edditable||editable editting||editing efficently||efficiently ehther||ether eigth||eight eletronic||electronic enabledi||enabled enchanced||enhanced encorporating||incorporating encrupted||encrypted encrypiton||encryption endianess||endianness enhaced||enhanced enlightnment||enlightenment enocded||encoded enterily||entirely enviroiment||environment enviroment||environment environement||environment environent||environment eqivalent||equivalent equiped||equipped equivelant||equivalent equivilant||equivalent eror||error estbalishment||establishment etsablishment||establishment etsbalishment||establishment excecutable||executable exceded||exceeded excellant||excellent existance||existence existant||existent exixt||exist exlcude||exclude exlcusive||exclusive exmaple||example expecially||especially explicite||explicit explicitely||explicitly explict||explicit explictly||explicitly expresion||expression exprimental||experimental extened||extended extensability||extensibility extention||extension extracter||extractor faild||failed faill||fail failue||failure failuer||failure faireness||fairness faliure||failure familar||familiar fatser||faster feauture||feature feautures||features fetaure||feature fetaures||features fileystem||filesystem finanize||finalize findn||find finilizes||finalizes finsih||finish flusing||flushing folloing||following followign||following follwing||following forseeable||foreseeable forse||force fortan||fortran forwardig||forwarding framwork||framework frequncy||frequency frome||from fucntion||function fuction||function fuctions||functions funcion||function functionallity||functionality functionaly||functionally functionnality||functionality functonality||functionality funtion||function funtions||functions furthur||further futhermore||furthermore futrue||future gaurenteed||guaranteed generiously||generously genric||generic globel||global grabing||grabbing grahical||graphical grahpical||graphical grapic||graphic guage||gauge guarentee||guarantee halfs||halves hander||handler handfull||handful hanled||handled harware||hardware heirarchically||hierarchically helpfull||helpful hierachy||hierarchy hierarchie||hierarchy howver||however hsould||should hypter||hyper identidier||identifier imblance||imbalance immeadiately||immediately immedaite||immediate immediatelly||immediately immediatly||immediately immidiate||immediate impelentation||implementation impementated||implemented implemantation||implementation implemenation||implementation implementaiton||implementation implementated||implemented implemention||implementation implemetation||implementation implemntation||implementation implentation||implementation implmentation||implementation implmenting||implementing incomming||incoming incompatabilities||incompatibilities incompatable||incompatible inconsistant||inconsistent increas||increase incrment||increment indendation||indentation indended||intended independant||independent independantly||independently independed||independent indiate||indicate inexpect||inexpected infomation||information informatiom||information informations||information informtion||information infromation||information ingore||ignore inital||initial initalised||initialized initalise||initialize initalize||initialize initation||initiation initators||initiators initializiation||initialization initialzed||initialized initilization||initialization initilize||initialize inofficial||unofficial instal||install inteface||interface integreated||integrated integrety||integrity integrey||integrity intendet||intended intented||intended interanl||internal interchangable||interchangeable interferring||interfering interger||integer intermittant||intermittent internel||internal interoprability||interoperability interrface||interface interrrupt||interrupt interrup||interrupt interrups||interrupts interruptted||interrupted interupted||interrupted interupt||interrupt intial||initial intialized||initialized intialize||initialize intregral||integral intrrupt||interrupt intuative||intuitive invaid||invalid invalde||invald invalide||invalid invididual||individual invokation||invocation invokations||invocations irrelevent||irrelevant isssue||issue itslef||itself jave||java jeffies||jiffies juse||just jus||just kown||known langage||language langauage||language langauge||language langugage||language lauch||launch leightweight||lightweight lengh||length lenght||length lenth||length lesstiff||lesstif libaries||libraries libary||library librairies||libraries libraris||libraries licenceing||licencing loggging||logging loggin||login logile||logfile loosing||losing losted||lost machinary||machinery maintainance||maintenance maintainence||maintenance maintan||maintain makeing||making malplaced||misplaced malplace||misplace managable||manageable managment||management mangement||management manoeuvering||maneuvering mappping||mapping mathimatical||mathematical mathimatic||mathematic mathimatics||mathematics maxium||maximum mechamism||mechanism meetign||meeting ment||meant mergable||mergeable mesage||message messags||messages messgaes||messages messsage||message messsages||messages microprocesspr||microprocessor milliseonds||milliseconds minumum||minimum miscelleneous||miscellaneous misformed||malformed mispelled||misspelled mispelt||misspelt miximum||maximum mmnemonic||mnemonic mnay||many modeled||modelled modulues||modules monochorome||monochrome monochromo||monochrome monocrome||monochrome mopdule||module mroe||more mulitplied||multiplied multidimensionnal||multidimensional multple||multiple mumber||number muticast||multicast mutiple||multiple mutli||multi nams||names navagating||navigating nead||need neccecary||necessary neccesary||necessary neccessary||necessary necesary||necessary negaive||negative negoitation||negotiation negotation||negotiation nerver||never nescessary||necessary nessessary||necessary noticable||noticeable notications||notifications notifed||notified numebr||number numner||number obtaion||obtain occassionally||occasionally occationally||occasionally occurance||occurrence occurances||occurrences occured||occurred occurence||occurrence occure||occurred occuring||occurring offet||offset omitt||omit ommiting||omitting ommitted||omitted onself||oneself ony||only operatione||operation opertaions||operations optionnal||optional optmizations||optimizations orientatied||orientated orientied||oriented otherise||otherwise ouput||output overaall||overall overhread||overhead overlaping||overlapping overriden||overridden overun||overrun pacakge||package pachage||package packacge||package packege||package packge||package packtes||packets pakage||package pallette||palette paln||plan paramameters||parameters paramater||parameter parametes||parameters parametised||parametrised paramter||parameter paramters||parameters particuarly||particularly particularily||particularly pased||passed passin||passing pathes||paths pecularities||peculiarities peformance||performance peice||piece pendantic||pedantic peprocessor||preprocessor perfoming||performing permissons||permissions peroid||period persistance||persistence persistant||persistent platfrom||platform plattform||platform pleaes||please ploting||plotting plugable||pluggable poinnter||pointer poiter||pointer posible||possible positon||position possibilites||possibilities powerfull||powerful preceeded||preceded preceeding||preceding preceed||precede precendence||precedence precission||precision prefered||preferred prefferably||preferably premption||preemption prepaired||prepared pressre||pressure primative||primitive princliple||principle priorty||priority privilaged||privileged privilage||privilege priviledge||privilege priviledges||privileges probaly||probably procceed||proceed proccesors||processors procesed||processed proces||process processessing||processing processess||processes processpr||processor processsed||processed processsing||processing procteted||protected prodecure||procedure progams||programs progess||progress programers||programmers programm||program programms||programs progresss||progress promps||prompts pronnounced||pronounced prononciation||pronunciation pronouce||pronounce pronunce||pronounce propery||property propigate||propagate propigation||propagation propogate||propagate prosess||process protable||portable protcol||protocol protecion||protection protocoll||protocol psudo||pseudo psuedo||pseudo psychadelic||psychedelic pwoer||power quering||querying raoming||roaming reasearcher||researcher reasearchers||researchers reasearch||research recepient||recipient receving||receiving recieved||received recieve||receive reciever||receiver recieves||receives recogniced||recognised recognizeable||recognizable recommanded||recommended recyle||recycle redircet||redirect redirectrion||redirection refcounf||refcount refence||reference refered||referred referenace||reference refering||referring refernces||references refernnce||reference refrence||reference registerd||registered registeresd||registered registes||registers registraration||registration regster||register regualar||regular reguator||regulator regulamentations||regulations reigstration||registration releated||related relevent||relevant remoote||remote remore||remote removeable||removable repectively||respectively replacable||replaceable replacments||replacements replys||replies reponse||response representaion||representation reqeust||request requiere||require requirment||requirement requred||required requried||required requst||request reseting||resetting resizeable||resizable resouces||resources resoures||resources ressizes||resizes ressource||resource ressources||resources retransmited||retransmitted retreived||retrieved retreive||retrieve retrive||retrieve retuned||returned reuest||request reuqest||request reutnred||returned rmeoved||removed rmeove||remove rmeoves||removes rountine||routine routins||routines rquest||request runing||running runned||ran runnning||running runtine||runtime sacrifying||sacrificing safly||safely safty||safety savable||saveable scaned||scanned scaning||scanning scarch||search seach||search searchs||searches secquence||sequence secund||second segement||segment senarios||scenarios sentivite||sensitive separatly||separately sepcify||specify sepc||spec seperated||separated seperately||separately seperate||separate seperatly||separately seperator||separator sepperate||separate sequece||sequence sequencial||sequential serveral||several setts||sets settting||setting shotdown||shutdown shoud||should shoule||should shrinked||shrunk siginificantly||significantly signabl||signal similary||similarly similiar||similar simlar||similar simliar||similar simpified||simplified singaled||signaled singal||signal singed||signed sleeped||slept softwares||software speach||speech specfic||specific speciefied||specified specifc||specific specifed||specified specificatin||specification specificaton||specification specifing||specifying specifiying||specifying speficied||specified speicify||specify speling||spelling spinlcok||spinlock spinock||spinlock splitted||split spreaded||spread sructure||structure stablilization||stabilization staically||statically staion||station standardss||standards standartization||standardization standart||standard staticly||statically stoped||stopped stoppped||stopped straming||streaming struc||struct structres||structures stuct||struct sturcture||structure subdirectoires||subdirectories suble||subtle succesfully||successfully succesful||successful successfull||successful sucessfully||successfully sucess||success superflous||superfluous superseeded||superseded suplied||supplied suported||supported suport||support suppored||supported supportin||supporting suppoted||supported suppported||supported suppport||support supress||suppress surpresses||suppresses susbsystem||subsystem suspicously||suspiciously swaping||swapping switchs||switches symetric||symmetric synax||syntax synchonized||synchronized syncronize||synchronize syncronizing||synchronizing syncronus||synchronous syste||system sytem||system sythesis||synthesis taht||that targetted||targeted targetting||targeting teh||the temorary||temporary temproarily||temporarily thier||their threds||threads threshhold||threshold throught||through thses||these tiggered||triggered tipically||typically tmis||this torerable||tolerable tramsmitted||transmitted tramsmit||transmit tranfer||transfer transciever||transceiver transferd||transferrd transfered||transferred transfering||transferring transision||transition transmittd||transmitted transormed||transformed trasmission||transmission treshold||threshold trigerring||triggering trun||turn ture||true tyep||type udpate||update uesd||used unconditionaly||unconditionally underun||underrun unecessary||unnecessary unexecpted||unexpected unexpectd||unexpected unexpeted||unexpected unfortunatelly||unfortunately unifiy||unify unknonw||unknown unknow||unknown unkown||unknown unneedingly||unnecessarily unresgister||unregister unsinged||unsigned unstabel||unstable unsuccessfull||unsuccessful unsuported||unsupported untill||until unuseful||useless upate||update usefule||useful usefull||useful usege||usage usera||users usualy||usually utilites||utilities utillities||utilities utilties||utilities utiltity||utility utitity||utility utitlty||utility vaid||valid vaild||valid valide||valid variantions||variations varient||variant vaule||value verbse||verbose verisons||versions verison||version verson||version vicefersa||vice-versa virtal||virtual virtaul||virtual virtiual||virtual visiters||visitors vitual||virtual wating||waiting whataver||whatever whenver||whenever wheter||whether whe||when wierd||weird wiil||will wirte||write withing||within wnat||want workarould||workaround writeing||writing writting||writing zombe||zombie zomebie||zombie ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/systemd/��������������������������������������������������������������0000775�0000000�0000000�00000000000�13242724102�0017654�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/systemd/nfs-ganesha-config.service-in.cmake���������������������������0000664�0000000�0000000�00000000231�13242724102�0026352�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������[Unit] Description=Process NFS-Ganesha configuration DefaultDependencies=no [Service] Type=oneshot ExecStart=@LIBEXECDIR@/ganesha/nfs-ganesha-config.sh �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/systemd/nfs-ganesha-lock.service.debian8������������������������������0000664�0000000�0000000�00000001523�13242724102�0025670�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This file is part of nfs-ganesha. # # rpc.statd is started by the nfs-lock.service, but that also loads the 'lockd' # kernel module in 'ExecStartPre'. The 'lockd' kernel module will register # itself as 'nlockmgr' which conflicts with the nfs-ganesha locking # implementation. # # This unit includes all the nfs-lock.service settings and details, but # overrides the 'ExecStartPre' and 'ExecStartPost' options. # # When this unit is started, the original nfs-lock.service is stopped (due to # the 'Conflicts' directive). With stopping the nfs-lock.service, 'lockd' gets # instructed to unregister 'nlockmgr' from the portmapper. # # The nfs-ganesha.service depends on this unit. # .include /lib/systemd/system/rpc-statd.service [Unit] Before=nfs-ganesha.service Conflicts=nfs-lock.service rpc-statd.service [Service] ExecStartPre= ExecStopPost= �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/systemd/nfs-ganesha-lock.service.el7����������������������������������0000664�0000000�0000000�00000001527�13242724102�0025051�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This file is part of nfs-ganesha. # # rpc.statd is started by the nfs-lock.service, but that also loads the 'lockd' # kernel module in 'ExecStartPre'. The 'lockd' kernel module will register # itself as 'nlockmgr' which conflicts with the nfs-ganesha locking # implementation. # # This unit includes all the nfs-lock.service settings and details, but # overrides the 'ExecStartPre' and 'ExecStartPost' options. # # When this unit is started, the original nfs-lock.service is stopped (due to # the 'Conflicts' directive). With stopping the nfs-lock.service, 'lockd' gets # instructed to unregister 'nlockmgr' from the portmapper. # # The nfs-ganesha.service depends on this unit. # .include /usr/lib/systemd/system/rpc-statd.service [Unit] Before=nfs-ganesha.service Conflicts=nfs-lock.service rpc-statd.service [Service] ExecStartPre= ExecStopPost= �������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/systemd/nfs-ganesha.service.debian8�����������������������������������0000664�0000000�0000000�00000003075�13242724102�0024746�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This file is part of nfs-ganesha. # # There can only be one NFS-server active on a system. When NFS-Ganesha is # started, the kernel NFS-server should have been stopped. This is achieved by # the 'Conflicts' directive in this unit. # # The Network Locking Manager (rpc.statd) is provided by the nfs-utils package. # NFS-Ganesha comes with its own nfs-ganesha-lock.service to resolve potential # conflicts in starting multiple rpc.statd processes. See the comments in the # nfs-ganesha-lock.service for more details. # [Unit] Description=NFS-Ganesha file server Documentation=http://github.com/nfs-ganesha/nfs-ganesha/wiki After=rpcbind.service nfs-ganesha-lock.service Wants=rpcbind.service nfs-ganesha-lock.service Conflicts=nfs.target After=nfs-ganesha-config.service Wants=nfs-ganesha-config.service [Service] Type=forking Environment="NOFILE=1048576" EnvironmentFile=-/etc/default/nfs-ganesha ExecStart=/bin/bash -c "${NUMACTL} ${NUMAOPTS} /usr/bin/ganesha.nfsd ${OPTIONS} ${EPOCH}" ExecStartPost=-/bin/bash -c "prlimit --pid $MAINPID --nofile=$NOFILE:$NOFILE" ExecStartPost=-/bin/bash -c "/bin/sleep 2 && /usr/bin/dbus-send --system --dest=org.ganesha.nfsd --type=method_call /org/ganesha/nfsd/admin org.ganesha.nfsd.admin.init_fds_limit" ExecReload=/usr/bin/dbus-send --system --dest=org.ganesha.nfsd --type=method_call /org/ganesha/nfsd/admin org.ganesha.nfsd.admin.reload ExecStop=/usr/bin/dbus-send --system --dest=org.ganesha.nfsd --type=method_call /org/ganesha/nfsd/admin org.ganesha.nfsd.admin.shutdown [Install] WantedBy=multi-user.target Also=nfs-ganesha-lock.service �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/systemd/nfs-ganesha.service.el7���������������������������������������0000664�0000000�0000000�00000003063�13242724102�0024120�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# This file is part of nfs-ganesha. # # There can only be one NFS-server active on a system. When NFS-Ganesha is # started, the kernel NFS-server should have been stopped. This is achieved by # the 'Conflicts' directive in this unit. # # The Network Locking Manager (rpc.statd) is provided by the nfs-utils package. # NFS-Ganesha comes with its own nfs-ganesha-lock.service to resolve potential # conflicts in starting multiple rpc.statd processes. See the comments in the # nfs-ganesha-lock.service for more details. # [Unit] Description=NFS-Ganesha file server Documentation=http://github.com/nfs-ganesha/nfs-ganesha/wiki After=rpcbind.service nfs-ganesha-lock.service Wants=rpcbind.service nfs-ganesha-lock.service Conflicts=nfs.target After=nfs-ganesha-config.service Wants=nfs-ganesha-config.service [Service] Type=forking Environment="NOFILE=1048576" EnvironmentFile=-/run/sysconfig/ganesha ExecStart=/bin/bash -c "${NUMACTL} ${NUMAOPTS} /usr/bin/ganesha.nfsd ${OPTIONS} ${EPOCH}" ExecStartPost=-/bin/bash -c "prlimit --pid $MAINPID --nofile=$NOFILE:$NOFILE" ExecStartPost=-/bin/bash -c "/usr/bin/sleep 2 && /bin/dbus-send --system --dest=org.ganesha.nfsd --type=method_call /org/ganesha/nfsd/admin org.ganesha.nfsd.admin.init_fds_limit" ExecReload=/bin/dbus-send --system --dest=org.ganesha.nfsd --type=method_call /org/ganesha/nfsd/admin org.ganesha.nfsd.admin.reload ExecStop=/bin/dbus-send --system --dest=org.ganesha.nfsd --type=method_call /org/ganesha/nfsd/admin org.ganesha.nfsd.admin.shutdown [Install] WantedBy=multi-user.target Also=nfs-ganesha-lock.service �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/systemd/sysconfig/����������������������������������������������������0000775�0000000�0000000�00000000000�13242724102�0021660�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/systemd/sysconfig/nfs-ganesha�����������������������������������������0000664�0000000�0000000�00000000153�13242724102�0023774�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������OPTIONS="-L /var/log/ganesha/ganesha.log -f /etc/ganesha/ganesha.conf -N NIV_EVENT" EPOCH_EXEC="/bin/true" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/systemd/tmpfiles.d/���������������������������������������������������0000775�0000000�0000000�00000000000�13242724102�0021721�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/systemd/tmpfiles.d/ganesha.conf���������������������������������������0000664�0000000�0000000�00000000130�13242724102�0024170�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������D /var/run/ganesha 0755 ganesha ganesha - D /var/lib/nfs/ganesha 0755 ganesha ganesha - ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/test_pynfs/�����������������������������������������������������������0000775�0000000�0000000�00000000000�13242724102�0020362�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/test_pynfs/test.cd_junction.pynfs�������������������������������������0000664�0000000�0000000�00000000127�13242724102�0024720�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/ksh ../pynfs/nfs4client.py -u pinatubo1 << EOF cd /prot/test/root quit EOF �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/test_pynfs/test.proxy.pynfs�������������������������������������������0000775�0000000�0000000�00000001326�13242724102�0023607�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/ksh ../pynfs/nfs4client.py -u -p pinatubo1:2047/ << EOF attr_request = nfs4lib.list2attrmask([FATTR4_TIME_ACCESS,FATTR4_TIME_METADATA,FATTR4_TIME_MODIFY,FATTR4_FILEID,FATTR4_SIZE]) putrootfhop = c.ncl.putrootfh_op() lookup1op = c.ncl.lookup_op( "users" ) ; lookup2op = c.ncl.lookup_op( "thomas" ) ; lookup3op = c.ncl.lookup_op( "CL1" ) ; lookup4op = c.ncl.lookup_op( "connproxy" ) ; lookup5op = c.ncl.lookup_op( "fichier" ) ; getfhop = c.ncl.getfh_op() ; getattrop = c.ncl.getattr_op( attr_request ) ; res = c.ncl.compound([putrootfhop, getattrop, lookup1op, lookup2op, lookup3op, lookup4op, lookup5op, getfhop, getattrop]) nfs4lib.fattr2dict( res.resarray[-1].arm.arm.obj_attributes ) quit EOF ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/test_pynfs/test_get_root_entry_all_type_for_pseudofs.pynfs������������0000664�0000000�0000000�00000005235�13242724102�0032221�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/ksh ../pynfs/nfs4client.py -u -p pinatubo1 << EOF putrootfhop = c.ncl.putrootfh_op() attr_request = nfs4lib.list2attrmask([FATTR4_TYPE,FATTR4_FH_EXPIRE_TYPE,FATTR4_CHANGE,FATTR4_LINK_SUPPORT,FATTR4_SYMLINK_SUPPORT,FATTR4_NAMED_ATTR,FATTR4_SIZE]) getattrop = c.ncl.getattr_op( attr_request ) res = c.ncl.compound([putrootfhop, getattrop]) nfs4lib.fattr2dict( res.resarray[-1].arm.arm.obj_attributes ) putrootfhop = c.ncl.putrootfh_op() attr_request = nfs4lib.list2attrmask([FATTR4_FSID,FATTR4_UNIQUE_HANDLES,FATTR4_LEASE_TIME,FATTR4_RDATTR_ERROR,FATTR4_ACL,FATTR4_ACLSUPPORT,FATTR4_ARCHIVE,FATTR4_CANSETTIME]) getattrop = c.ncl.getattr_op( attr_request ) res = c.ncl.compound([putrootfhop, getattrop]) nfs4lib.fattr2dict( res.resarray[-1].arm.arm.obj_attributes ) putrootfhop = c.ncl.putrootfh_op() attr_request = nfs4lib.list2attrmask([FATTR4_CASE_INSENSITIVE,FATTR4_CASE_PRESERVING,FATTR4_CHOWN_RESTRICTED,FATTR4_FILEID,FATTR4_FILES_AVAIL,FATTR4_FILES_FREE,FATTR4_FILES_TOTAL]) getattrop = c.ncl.getattr_op( attr_request ) res = c.ncl.compound([putrootfhop, getattrop]) nfs4lib.fattr2dict( res.resarray[-1].arm.arm.obj_attributes ) putrootfhop = c.ncl.putrootfh_op() attr_request = nfs4lib.list2attrmask([FATTR4_HIDDEN,FATTR4_HOMOGENEOUS,FATTR4_MAXFILESIZE,FATTR4_MAXLINK,FATTR4_MAXNAME,FATTR4_MAXREAD,FATTR4_MAXWRITE]) getattrop = c.ncl.getattr_op( attr_request ) res = c.ncl.compound([putrootfhop, getattrop]) nfs4lib.fattr2dict( res.resarray[-1].arm.arm.obj_attributes ) putrootfhop = c.ncl.putrootfh_op() attr_request = nfs4lib.list2attrmask([FATTR4_RAWDEV]) getattrop = c.ncl.getattr_op( attr_request ) res = c.ncl.compound([putrootfhop, getattrop]) nfs4lib.fattr2dict( res.resarray[-1].arm.arm.obj_attributes ) putrootfhop = c.ncl.putrootfh_op() attr_request = nfs4lib.list2attrmask([FATTR4_MODE,FATTR4_NO_TRUNC,FATTR4_NUMLINKS,FATTR4_OWNER,FATTR4_OWNER_GROUP]) getattrop = c.ncl.getattr_op( attr_request ) res = c.ncl.compound([putrootfhop, getattrop]) nfs4lib.fattr2dict( res.resarray[-1].arm.arm.obj_attributes ) putrootfhop = c.ncl.putrootfh_op() attr_request = nfs4lib.list2attrmask([FATTR4_SPACE_AVAIL,FATTR4_SPACE_FREE,FATTR4_SPACE_TOTAL,FATTR4_SPACE_USED,FATTR4_SYSTEM]) getattrop = c.ncl.getattr_op( attr_request ) res = c.ncl.compound([putrootfhop, getattrop]) nfs4lib.fattr2dict( res.resarray[-1].arm.arm.obj_attributes ) putrootfhop = c.ncl.putrootfh_op() attr_request = nfs4lib.list2attrmask([FATTR4_TIME_ACCESS,FATTR4_TIME_DELTA,FATTR4_TIME_METADATA,FATTR4_TIME_MODIFY,FATTR4_MOUNTED_ON_FILEID,FATTR4_FILEHANDLE]) getattrop = c.ncl.getattr_op( attr_request ) res = c.ncl.compound([putrootfhop, getattrop]) nfs4lib.fattr2dict( res.resarray[-1].arm.arm.obj_attributes ) quit EOF �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/test_pynfs/test_get_root_entry_rawdev.pynfs���������������������������0000664�0000000�0000000�00000000531�13242724102�0027114�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/ksh ../pynfs/nfs4client.py -u -p pinatubo1 << EOF putrootfhop = c.ncl.putrootfh_op() attr_request = nfs4lib.list2attrmask([FATTR4_RAWDEV,FATTR4_TYPE,FATTR4_SPACE_AVAIL]) getattrop = c.ncl.getattr_op( attr_request ) res = c.ncl.compound([putrootfhop, getattrop]) nfs4lib.fattr2dict( res.resarray[-1].arm.arm.obj_attributes ) quit EOF �����������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/test_pynfs/test_get_root_entry_supported_attrs.pynfs������������������0000664�0000000�0000000�00000000503�13242724102�0031065�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/ksh ../pynfs/nfs4client.py -u -p pinatubo1 << EOF putrootfhop = c.ncl.putrootfh_op() attr_request = nfs4lib.list2attrmask([FATTR4_SUPPORTED_ATTRS]) getattrop = c.ncl.getattr_op( attr_request ) res = c.ncl.compound([putrootfhop, getattrop]) nfs4lib.fattr2dict( res.resarray[-1].arm.arm.obj_attributes ) quit EOF ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/test_pynfs/test_get_root_entry_type.pynfs�����������������������������0000664�0000000�0000000�00000000470�13242724102�0026607�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/ksh ../pynfs/nfs4client.py -u -p pinatubo1 << EOF putrootfhop = c.ncl.putrootfh_op() attr_request = nfs4lib.list2attrmask([FATTR4_TYPE]) getattrop = c.ncl.getattr_op( attr_request ) res = c.ncl.compound([putrootfhop, getattrop]) nfs4lib.fattr2dict( res.resarray[-1].arm.arm.obj_attributes ) quit EOF ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/test_pynfs/test_nfs4_fs_localtions_request.pynfs����������������������0000664�0000000�0000000�00000000535�13242724102�0030046�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/ksh ../pynfs/nfs4client.py -u -p pinatubo1 << EOF putrootfhop = c.ncl.putrootfh_op() getfhop = c.ncl.getfh_op() ; attr_request = nfs4lib.list2attrmask([FATTR4_FS_LOCATIONS]) getattrop = c.ncl.getattr_op( attr_request ) res = c.ncl.compound([putrootfhop, getattrop]) nfs4lib.fattr2dict( res.resarray[-1].arm.arm.obj_attributes ) quit EOF �������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/test_pynfs/test_nfs4_mount_request.pynfs������������������������������0000664�0000000�0000000�00000001040�13242724102�0026341�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/ksh ../pynfs/nfs4client.py -u -p pinatubo1 << EOF putrootfhop = c.ncl.putrootfh_op() getfhop = c.ncl.getfh_op() ; attr_request = nfs4lib.list2attrmask([FATTR4_TYPE,FATTR4_CHANGE,FATTR4_SIZE,FATTR4_FSID,FATTR4_FILEID,FATTR4_MODE,FATTR4_NUMLINKS,FATTR4_OWNER,FATTR4_OWNER_GROUP,FATTR4_RAWDEV,FATTR4_SPACE_USED,FATTR4_TIME_ACCESS,FATTR4_TIME_METADATA,FATTR4_TIME_MODIFY]) getattrop = c.ncl.getattr_op( attr_request ) res = c.ncl.compound([putrootfhop, getattrop]) nfs4lib.fattr2dict( res.resarray[-1].arm.arm.obj_attributes ) quit EOF ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/test_through_mountpoint/����������������������������������������������0000775�0000000�0000000�00000000000�13242724102�0023177�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/test_through_mountpoint/test_create.sh��������������������������������0000775�0000000�0000000�00000003074�13242724102�0026044�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # ce test cree plein d'entrees dans un repertoire # et les rename une par une TEST_DIR=$1 SUB_DIR1="create_rename-$$" FILENAME_1="HDep-n=Temps_u=s+n=NumSDom_g=0200x0203.v=f0000000000000000-v=f3f9f16208bfc1f8d" FILENAME_2="HDep-n=Temps_u=s+n=NumSDom_g=0184x0187.v=f0000000000000000-v=f3f74bbcde85767c5" NB_ENTREES=500 if [[ $TEST_DIR = "" ]]; then echo "usage : $0 <test_dir>" exit 1 fi if [[ ! -d $TEST_DIR ]]; then echo "$1 n'existe pas ou n'est pas un repertoire"; exit 1 fi #creation de l'arborescence initiale mkdir -p "$TEST_DIR/$SUB_DIR1" ERR=0 echo "creating files..." I=0 while (( $I < $NB_ENTREES )) ; do printf "#" touch "$TEST_DIR/$SUB_DIR1/$FILENAME_1-$I" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME_1-$I" fi (( I = $I + 1 )) done echo echo "renaming files..." I=0 while (( $I < $NB_ENTREES )) ; do printf "#" mv "$TEST_DIR/$SUB_DIR1/$FILENAME_1-$I" "$TEST_DIR/$SUB_DIR1/$FILENAME_2-$I" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME_1-$I" "$TEST_DIR/$SUB_DIR1/$FILENAME_2-$I" fi (( I = $I + 1 )) done echo echo "renaming files..." I=0 while (( $I < $NB_ENTREES )) ; do printf "#" mv "$TEST_DIR/$SUB_DIR1/$FILENAME_2-$I" "$TEST_DIR/$SUB_DIR1/$FILENAME_1-$I" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME_2-$I" "$TEST_DIR/$SUB_DIR1/$FILENAME_1-$I" fi (( I = $I + 1 )) done echo echo "cleaning test directory..." rm -rf "$TEST_DIR/$SUB_DIR1" echo "Test termine. $ERR erreurs." ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/test_through_mountpoint/test_create_ls.sh�����������������������������0000775�0000000�0000000�00000002160�13242724102�0026535�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # ce test cree plein d'entrees dans un repertoire # et les rename une par une TEST_DIR=$1 SUB_DIR1="create_ls-$$" FILENAME_1="HDep-n=Temps_u=s+n=NumSDom_g=0200x0203.v=f0000000000000000-v=f3f9f16208bfc1f8d" FILENAME_2="HDep-n=Temps_u=s+n=NumSDom_g=0184x0187.v=f0000000000000000-v=f3f74bbcde85767c5" NB_ENTREES=100 if [[ $TEST_DIR = "" ]]; then echo "usage : $0 <test_dir>" exit 1 fi if [[ ! -d $TEST_DIR ]]; then echo "$1 n'existe pas ou n'est pas un repertoire"; exit 1 fi #creation de l'arborescence initiale mkdir -p "$TEST_DIR/$SUB_DIR1" ERR=0 echo "creating files..." I=0 while (( $I < $NB_ENTREES )) ; do printf "#" touch "$TEST_DIR/$SUB_DIR1/$FILENAME_1-$I" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME_1-$I" fi (( I = $I + 1 )) done echo echo "ls" I=0 while (( $I < $NB_ENTREES )) ; do printf "#" ls "$TEST_DIR/$SUB_DIR1" >/dev/null if (( $? != 0 )); then ((ERR=$ERR+1)) fi sleep 10 (( I = $I + 1 )) done echo echo "cleaning test directory..." rm -rf "$TEST_DIR/$SUB_DIR1" echo "Test termine. $ERR erreurs." ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/test_through_mountpoint/test_create_rm.sh�����������������������������0000775�0000000�0000000�00000005206�13242724102�0026541�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # ce test cree plein d'entrees dans un repertoire # et les rename une par une TEST_DIR=$1 SUB_DIR1="touch_rm-$$" FILENAME_1="HDep-n=Temps_u=s+n=NumSDom_g=0200x0203.v=f0000000000000000-v=f3f9f16208bfc1f8d" FILENAME_2="HDep-n=Temps_u=s+n=NumSDom_g=0184x0187.v=f0000000000000000-v=f3f74bbcde85767c5" NB_LOOP=100 if [[ $TEST_DIR = "" ]]; then echo "usage : $0 <test_dir>" exit 1 fi if [[ ! -d $TEST_DIR ]]; then echo "$1 n'existe pas ou n'est pas un repertoire"; exit 1 fi #creation de l'arborescence initiale mkdir -p "$TEST_DIR/$SUB_DIR1" ERR=0 echo "touch/rm sequence... ($NB_LOOP times)" I=0 while (( $I < $NB_LOOP )) ; do printf "#" touch "$TEST_DIR/$SUB_DIR1/$FILENAME_1" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME_1" fi rm "$TEST_DIR/$SUB_DIR1/$FILENAME_1" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME_1" fi touch "$TEST_DIR/$SUB_DIR1/$FILENAME_2" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME_2" fi rm "$TEST_DIR/$SUB_DIR1/$FILENAME_2" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME_2" fi (( I = $I + 1 )) done echo echo "touch/mv/rm sequence... ($NB_LOOP times)" I=0 while (( $I < $NB_LOOP )) ; do printf "#" touch "$TEST_DIR/$SUB_DIR1/$FILENAME_1" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME_1" fi mv "$TEST_DIR/$SUB_DIR1/$FILENAME_1" "$TEST_DIR/$SUB_DIR1/$FILENAME_2" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME_1" "$TEST_DIR/$SUB_DIR1/$FILENAME_2" fi rm "$TEST_DIR/$SUB_DIR1/$FILENAME_2" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME_2" fi (( I = $I + 1 )) done echo echo "touch/ls/mv/rm sequence... ($NB_LOOP times)" I=0 while (( $I < $NB_LOOP )) ; do printf "#" touch "$TEST_DIR/$SUB_DIR1/$FILENAME_1" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME_1" fi ls -l "$TEST_DIR/$SUB_DIR1" >/dev/null if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1" fi mv "$TEST_DIR/$SUB_DIR1/$FILENAME_1" "$TEST_DIR/$SUB_DIR1/$FILENAME_2" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME_1" "$TEST_DIR/$SUB_DIR1/$FILENAME_2" fi rm "$TEST_DIR/$SUB_DIR1/$FILENAME_2" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME_2" fi (( I = $I + 1 )) done echo "cleaning test directory..." rm -rf "$TEST_DIR/$SUB_DIR1" echo "Test termine. $ERR erreurs." ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/test_through_mountpoint/test_createrenameunlink.sh��������������������0000775�0000000�0000000�00000007541�13242724102�0030460�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # ce test cree plein d'entrees dans un repertoire # et les rename une par une TEST_DIR=$1 SUB_DIR1="create_rename_unlink-$$" FILENAME1="kjdfslkjflskdjflsdkjflksdjflsdkjf" FILENAME2="mdslkmdf_lkdsjfksedf_9879" NB_ITER_1=100 NB_ITER_2=10 if [[ $TEST_DIR = "" ]]; then echo "usage : $0 <test_dir>" exit 1 fi if [[ ! -d $TEST_DIR ]]; then echo "$1 n'existe pas ou n'est pas un repertoire"; exit 1 fi #creation de l'arborescence initiale mkdir -p "$TEST_DIR/$SUB_DIR1" ERR=0 J=0 while (( $J < $NB_ITER_2 )) ; do echo "create/rename/unlink sequence..." I=0 while (( $I < $NB_ITER_1 )) ; do printf "#" # sequence 1 = create, rename unlink if [ -e "$TEST_DIR/$SUB_DIR1/$FILENAME1" ]; then echo "Error : $TEST_DIR/$SUB_DIR1/$FILENAME1 already exists" >&2 ((ERR=$ERR+1)) else touch "$TEST_DIR/$SUB_DIR1/$FILENAME1" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME1" fi fi if [ -e "$TEST_DIR/$SUB_DIR1/$FILENAME2" ]; then echo "Error : $TEST_DIR/$SUB_DIR1/$FILENAME2 already exists" >&2 ((ERR=$ERR+1)) else mv "$TEST_DIR/$SUB_DIR1/$FILENAME1" "$TEST_DIR/$SUB_DIR1/$FILENAME2" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME1" "$TEST_DIR/$SUB_DIR1/$FILENAME2" fi fi rm -f "$TEST_DIR/$SUB_DIR1/$FILENAME2" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME1" "$TEST_DIR/$SUB_DIR1/$FILENAME2" fi # sequence 2 = create, rename, rename, unlink if [ -e "$TEST_DIR/$SUB_DIR1/$FILENAME1" ]; then echo "Error : $TEST_DIR/$SUB_DIR1/$FILENAME1 already exists" >&2 ((ERR=$ERR+1)) else touch "$TEST_DIR/$SUB_DIR1/$FILENAME1" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME1" fi fi if [ -e "$TEST_DIR/$SUB_DIR1/$FILENAME2" ]; then echo "Error : $TEST_DIR/$SUB_DIR1/$FILENAME2 already exists" >&2 ((ERR=$ERR+1)) else mv "$TEST_DIR/$SUB_DIR1/$FILENAME1" "$TEST_DIR/$SUB_DIR1/$FILENAME2" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME1" "$TEST_DIR/$SUB_DIR1/$FILENAME2" fi fi if [ -e "$TEST_DIR/$SUB_DIR1/$FILENAME1" ]; then echo "Error : $TEST_DIR/$SUB_DIR1/$FILENAME1 already exists" >&2 ((ERR=$ERR+1)) else mv "$TEST_DIR/$SUB_DIR1/$FILENAME2" "$TEST_DIR/$SUB_DIR1/$FILENAME1" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME1" "$TEST_DIR/$SUB_DIR1/$FILENAME2" fi fi rm -f "$TEST_DIR/$SUB_DIR1/$FILENAME1" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME1" "$TEST_DIR/$SUB_DIR1/$FILENAME2" fi (( I = $I + 1 )) done printf "#" if [ -e "$TEST_DIR/$SUB_DIR1/$FILENAME2" ]; then echo "Error : $TEST_DIR/$SUB_DIR1/$FILENAME2 already exists" >&2 ((ERR=$ERR+1)) else touch "$TEST_DIR/$SUB_DIR1/$FILENAME2" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME2" fi fi echo echo "readdir/lookup/getattr sequence..." I=0 while (( $I < $NB_ITER_1 )) ; do printf "#" ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME2" > /dev/null if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME1" "$TEST_DIR/$SUB_DIR1/$FILENAME2" fi ls -li "$TEST_DIR/$SUB_DIR1" > /dev/null if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1" fi (( I = $I + 1 )) done echo echo "removing file..." rm -f "$TEST_DIR/$SUB_DIR1/$FILENAME2" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME2" fi (( J = $J + 1 )) done echo "Test termine. $ERR erreurs." ���������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/test_through_mountpoint/test_createunlink.sh��������������������������0000775�0000000�0000000�00000004030�13242724102�0027256�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # ce test cree plein d'entrees dans un repertoire # et les rename une par une TEST_DIR=$1 SUB_DIR1="create_unlink-$$" FILENAME="kjdfslkjflskdjflsdkjflksdjflsdkjf" NB_ITER_1=100 NB_ITER_2=10 if [[ $TEST_DIR = "" ]]; then echo "usage : $0 <test_dir>" exit 1 fi if [[ ! -d $TEST_DIR ]]; then echo "$1 n'existe pas ou n'est pas un repertoire"; exit 1 fi #creation de l'arborescence initiale mkdir -p "$TEST_DIR/$SUB_DIR1" ERR=0 J=0 while (( $J < $NB_ITER_2 )) ; do echo "create/unlink sequence..." I=0 while (( $I < $NB_ITER_1 )) ; do printf "#" if [ -e "$TEST_DIR/$SUB_DIR1/$FILENAME" ]; then echo "Error : $TEST_DIR/$SUB_DIR1/$FILENAME already exists" >&2 ((ERR=$ERR+1)) else touch "$TEST_DIR/$SUB_DIR1/$FILENAME" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME" fi fi rm -f "$TEST_DIR/$SUB_DIR1/$FILENAME" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME" fi (( I = $I + 1 )) done printf "#" if [ -e "$TEST_DIR/$SUB_DIR1/$FILENAME" ]; then echo "Error : $TEST_DIR/$SUB_DIR1/$FILENAME already exists" >&2 ((ERR=$ERR+1)) else touch "$TEST_DIR/$SUB_DIR1/$FILENAME" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME" fi fi echo echo "readdir/lookup/getattr sequence..." I=0 while (( $I < $NB_ITER_1 )) ; do printf "#" ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME" > /dev/null if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME" fi ls -li "$TEST_DIR/$SUB_DIR1" > /dev/null if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1" fi (( I = $I + 1 )) done echo echo "removing file..." rm -f "$TEST_DIR/$SUB_DIR1/$FILENAME" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME" fi (( J = $J + 1 )) done echo "Test termine. $ERR erreurs." ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/test_through_mountpoint/test_mkdircascade.sh��������������������������0000775�0000000�0000000�00000006531�13242724102�0027214�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # ce test cree plein d'entrees dans un repertoire # et les rename une par une TEST_DIR=$1 SUB_DIR1="mkdir_cascade-$$" NAME="TEST_ENTRY" NB_ITER=5000 ((SEED=1999*$$+`date +%s`)) ((RAND=((1999*$SEED + 857))%715827883)) LAST_VAL=0 if [[ $TEST_DIR = "" ]]; then echo "usage : $0 <test_dir>" exit 1 fi if [[ ! -d $TEST_DIR ]]; then echo "$1 n'existe pas ou n'est pas un repertoire"; exit 1 fi #creation de l'arborescence initiale mkdir -p "$TEST_DIR/$SUB_DIR1" function random { MODULUS=$1 ((RAND=((1999*$RAND + 857))%715827883)) ((value=$RAND%$MODULUS)) while (( $value < 0 )); do ((value=$value+$MODULUS)) done LAST_VAL=$value } function create_file { chemin=$1 touch "$chemin/$NAME" status=$? if (( $status != 0 )); then echo "Error $status creating file $chemin/$NAME" >&2 exit 1 fi } function create_dir { chemin=$1 mkdir "$chemin/$NAME" status=$? if (( $status != 0 )); then echo "Error $status creating subdirectory $chemin/$NAME" >&2 exit 1 fi } function remove_file { chemin=$1 rm -f "$chemin/$NAME" status=$? if (( $status != 0 )); then echo "Error $status removing file $chemin/$NAME" >&2 exit 1 fi } function remove_dir { chemin=$1 rmdir "$chemin/$NAME" status=$? if (( $status != 0 )); then echo "Error $status removing directory $chemin/$NAME" >&2 exit 1 fi } function go_up { chemin=$1 upper=`dirname $chemin` echo "$upper" } function go_in { chemin=$1 echo "$chemin/$NAME" } # on choisi aleatoirement une sequence des actions suivantes: # *si etat = vide : # - on peut creer un fichier => etat=fichier # - on peut creer un sous repertoire => etat=directory # - on peut remonter d'un cran (si depth > 0 ) # *si etat = fichier : # - on peut supprimer le fichier => etat = vide # *si etat = directory : # - on peut descendre dans ce repertoire => etat=vide # - on peut supprimer ce repertoire => etat=vide depth=0 path="$TEST_DIR/$SUB_DIR1" etat=vide I=0 while (( $I < $NB_ITER )) ; do case $etat in vide) random 4 case $LAST_VAL in 0) echo "Creating file $path/$NAME" create_file $path etat=fichier ;; 1) echo "Creating dir $path/$NAME" create_dir $path etat=directory ;; 2|3) # go up more often than down if (( $depth > 0 )); then path=`go_up $path` echo "Going back to $path" etat=directory ((depth=$depth - 1)) fi # sinon (depth=0) : on ne fait rien ;; esac ;; fichier) echo "Removing file $path/$NAME" remove_file $path etat=vide ;; directory) random 2 case $LAST_VAL in 0) path=`go_in $path` echo "Entering in $path" etat=vide ((depth=$depth + 1)) ;; 1) echo "Removing dir $path/$NAME" remove_dir $path etat=vide ;; esac ;; esac (( I = $I +1 )) done echo "Cleaning all..." rm -rf "$TEST_DIR/$SUB_DIR1" status=$? if (( $status != 0 )); then echo "Error $status removing $TEST_DIR/$SUB_DIR1" >&2 exit 1 fi exit 0 �����������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/test_through_mountpoint/test_read_write.sh����������������������������0000775�0000000�0000000�00000002030�13242724102�0026715�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh COMMANDE_CONTENU="find /etc -ls" REPERTOIRE=$1 DATE=`date +"%d%m%Y"` if [[ ! -d $REPERTOIRE ]]; then echo "$REPERTOIRE N'EST PAS UN REPERTOIRE" exit 1 fi FICH_TEST="$REPERTOIRE/TEST_RW.$DATE" echo "Creation d'un fichier temoin..." TEMOIN="/tmp/TEST_RW.$DATE" $COMMANDE_CONTENU > $TEMOIN echo "Copie dans Ganesha (cp)..." cp $TEMOIN $FICH_TEST ls -l $TEMOIN $FICH_TEST echo "Comparaison du contenu..." diff $TEMOIN $FICH_TEST echo "Copie de Ganesha vers /tmp (cp)..." cp $FICH_TEST $TEMOIN.2 echo "Comparaison du temoin et du fichier exporte..." diff $TEMOIN $TEMOIN.2 echo "Suppression du fichier dans Ganesha..." rm $FICH_TEST echo "Copie dans Ganesha (cp -p)..." cp -p $TEMOIN $FICH_TEST ls -l $TEMOIN $FICH_TEST echo "Comparaison du contenu..." diff $TEMOIN $FICH_TEST echo "Copie de Ganesha vers /tmp (cp -p)..." cp -p $FICH_TEST $TEMOIN.2 echo "Comparaison du temoin et du fichier exporte..." diff $TEMOIN $TEMOIN.2 echo "Suppression des fichiers du test..." rm $FICH_TEST rm $TEMOIN rm $TEMOIN.2 ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/test_through_mountpoint/test_rename.sh��������������������������������0000775�0000000�0000000�00000004753�13242724102�0026055�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # de test cree simplement deux fichiers dans un repertoire # et les rename TEST_DIR=$1 SUB_DIR1="hercule-$$/depouillement" SUB_DIR2="hercule-$$/protections" FILENAME_1="HDep-n=Temps_u=s+n=NumSDom_g=0200x0203.v=f0000000000000000-v=f3f9f16208bfc1f8d" FILENAME_2="HDep-n=Temps_u=s+n=NumSDom_g=0184x0187.v=f0000000000000000-v=f3f74bbcde85767c5" NB_LOOP_1=10 NB_LOOP_2=100 if [[ $TEST_DIR = "" ]]; then echo "usage : $0 <test_dir>" exit 1 fi if [[ ! -d $TEST_DIR ]]; then echo "$1 n'existe pas ou n'est pas un repertoire"; exit 1 fi #creation de l'arborescence initiale mkdir -p "$TEST_DIR/$SUB_DIR1" mkdir -p "$TEST_DIR/$SUB_DIR2" touch "$TEST_DIR/$SUB_DIR1/$FILENAME_1" I=0 ERR=0 while (( $I < $NB_LOOP_1 )) ; do J=0 echo "passe 1 - $I" while (( $J < $NB_LOOP_2 )); do mv "$TEST_DIR/$SUB_DIR1/$FILENAME_1" "$TEST_DIR/$SUB_DIR1/$FILENAME_2" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME_1" "$TEST_DIR/$SUB_DIR1/$FILENAME_2" exit 1 fi mv "$TEST_DIR/$SUB_DIR1/$FILENAME_2" "$TEST_DIR/$SUB_DIR1/$FILENAME_1" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME_2" "$TEST_DIR/$SUB_DIR1/$FILENAME_1" exit 1 fi (( J = $J + 1)) done echo "passe 2 - $I" mv "$TEST_DIR/$SUB_DIR1/$FILENAME_1" "$TEST_DIR/$SUB_DIR2/$FILENAME_1" if (( $? != 0 )); then ((ERR=$ERR+1)); fi J=0 while (( $J < $NB_LOOP_2 )); do mv "$TEST_DIR/$SUB_DIR2/$FILENAME_1" "$TEST_DIR/$SUB_DIR2/$FILENAME_2" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR2/$FILENAME_1" "$TEST_DIR/$SUB_DIR2/$FILENAME_2" exit 1 fi mv "$TEST_DIR/$SUB_DIR2/$FILENAME_2" "$TEST_DIR/$SUB_DIR1/$FILENAME_2" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR2/$FILENAME_2" "$TEST_DIR/$SUB_DIR1/$FILENAME_2" exit 1 fi mv "$TEST_DIR/$SUB_DIR1/$FILENAME_2" "$TEST_DIR/$SUB_DIR2/$FILENAME_1" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME_2" "$TEST_DIR/$SUB_DIR2/$FILENAME_1" exit 1 fi (( J = $J + 1)) done mv "$TEST_DIR/$SUB_DIR2/$FILENAME_1" "$TEST_DIR/$SUB_DIR1/$FILENAME_1" if (( $? != 0 )); then ((ERR=$ERR+1)); fi (( I = $I + 1 )) done rm -rf "$TEST_DIR/$SUB_DIR1" rm -rf "$TEST_DIR/$SUB_DIR2" (( NB_RENAME = 5*$NB_LOOP_2 + 2 )) (( NB_RENAME = $NB_LOOP_1 * $NB_RENAME )) echo "Test termine. $NB_RENAME rename effectues. $ERR erreurs." ���������������������nfs-ganesha-2.6.0/src/scripts/test_through_mountpoint/test_rename2.sh�������������������������������0000775�0000000�0000000�00000005615�13242724102�0026135�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # de test cree simplement deux fichiers dans un repertoire # et les rename TEST_DIR=$1 SUB_DIR1="hercule-$$/depouillement" SUB_DIR2="hercule-$$/protections" FILENAME_1="HDep-n=Temps_u=s+n=NumSDom_g=0200x0203.v=f0000000000000000-v=f3f9f16208bfc1f8d" FILENAME_2="HDep-n=Temps_u=s+n=NumSDom_g=0184x0187.v=f0000000000000000-v=f3f74bbcde85767c5" NB_LOOP_1=10 NB_LOOP_2=100 NB_FILES=100 if [[ $TEST_DIR = "" ]]; then echo "usage : $0 <test_dir>" exit 1 fi if [[ ! -d $TEST_DIR ]]; then echo "$1 n'existe pas ou n'est pas un repertoire"; exit 1 fi #creation de l'arborescence initiale mkdir -p "$TEST_DIR/$SUB_DIR1" mkdir -p "$TEST_DIR/$SUB_DIR2" F=0 while (( $F < $NB_FILES )) ; do touch "$TEST_DIR/$SUB_DIR1/$FILENAME_1.$F" (( F = $F + 1 )) done I=0 ERR=0 while (( $I < $NB_LOOP_1 )) ; do echo "test 1" F=0 while (( $F < $NB_FILES )) ; do printf "." J=0 while (( $J < $NB_LOOP_2 )); do mv "$TEST_DIR/$SUB_DIR1/$FILENAME_1.$F" "$TEST_DIR/$SUB_DIR1/$FILENAME_2.$F" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME_1.$F" "$TEST_DIR/$SUB_DIR1/$FILENAME_2.$F" exit 1 fi mv "$TEST_DIR/$SUB_DIR1/$FILENAME_2.$F" "$TEST_DIR/$SUB_DIR1/$FILENAME_1.$F" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME_2.$F" "$TEST_DIR/$SUB_DIR1/$FILENAME_1.$F" exit 1 fi (( J = $J + 1)) done (( F = $F + 1 )) done echo echo "test 2" F=0 while (( $F < $NB_FILES )) ; do printf "." mv "$TEST_DIR/$SUB_DIR1/$FILENAME_1.$F" "$TEST_DIR/$SUB_DIR2/$FILENAME_1.$F" if (( $? != 0 )); then ((ERR=$ERR+1)); fi J=0 while (( $J < $NB_LOOP_2 )); do mv "$TEST_DIR/$SUB_DIR2/$FILENAME_1.$F" "$TEST_DIR/$SUB_DIR2/$FILENAME_2.$F" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR2/$FILENAME_1.$F" "$TEST_DIR/$SUB_DIR2/$FILENAME_2.$F" exit 1 fi mv "$TEST_DIR/$SUB_DIR2/$FILENAME_2.$F" "$TEST_DIR/$SUB_DIR1/$FILENAME_2.$F" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR2/$FILENAME_2.$F" "$TEST_DIR/$SUB_DIR1/$FILENAME_2.$F" exit 1 fi mv "$TEST_DIR/$SUB_DIR1/$FILENAME_2.$F" "$TEST_DIR/$SUB_DIR2/$FILENAME_1.$F" if (( $? != 0 )); then ((ERR=$ERR+1)) ls -li "$TEST_DIR/$SUB_DIR1/$FILENAME_2.$F" "$TEST_DIR/$SUB_DIR2/$FILENAME_1.$F" exit 1 fi (( J = $J + 1)) done mv "$TEST_DIR/$SUB_DIR2/$FILENAME_1.$F" "$TEST_DIR/$SUB_DIR1/$FILENAME_1.$F" if (( $? != 0 )); then ((ERR=$ERR+1)); fi (( F = $F + 1 )) done echo (( I = $I + 1 )) done rm -rf "$TEST_DIR/$SUB_DIR1" rm -rf "$TEST_DIR/$SUB_DIR2" (( NB_RENAME = 5*$NB_LOOP_2 + 2 )) (( NB_RENAME = $NB_LOOP_1 * $NB_RENAME * $NB_FILES )) echo "Test termine. $NB_RENAME rename effectues. $ERR erreurs." �������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/scripts/test_through_mountpoint/test_rm_stat_readdir.sh�����������������������0000775�0000000�0000000�00000011345�13242724102�0027744�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh # This test create entries, check they are returned by readdir and getattr # then, it removes the entries and check getattr returns ENOENT and readdir don't display it TEST_DIR=$1 SUB_DIR1="TESTDIR_RMSTAT-$$" FILENAME_1="FILEabcdefghijklm" FILENAME_2="FILEsqklsjdlqsjll" NB_FILES=1000 if [[ $TEST_DIR = "" ]]; then echo "usage : $0 <test_dir>" exit 1 fi if [[ ! -d $TEST_DIR ]]; then echo "$1 does not exist or is not a directory"; exit 1 fi #creation de l'arborescence initiale mkdir -p "$TEST_DIR/$SUB_DIR1" ERR=0 echo "create/getattr/readdir sequence... ($NB_FILES files)" I=0 while (( $I < (($NB_FILES/2)) )) ; do printf "#" # create file PATH1="$TEST_DIR/$SUB_DIR1/${FILENAME_1}_$I" touch $PATH1 if (( $? != 0 )); then ((ERR=$ERR+1)) echo "Error creating file $PATH1" ls -li $PATH1 fi # first, getattr stat $PATH1 > /dev/null if (( $? != 0 )); then ((ERR=$ERR+1)) echo "Error getting file attributes for $PATH1" ls -li $PATH1 fi # then, check readdir ls $TEST_DIR/$SUB_DIR1 | egrep -e "^${FILENAME_1}_$I$" > /dev/null if (( $? != 0 )); then ((ERR=$ERR+1)) echo "Error: ${FILENAME_1}_$I does not appear in readdir($TEST_DIR/$SUB_DIR1)" ls $TEST_DIR/$SUB_DIR1 fi # create a second file PATH2="$TEST_DIR/$SUB_DIR1/${FILENAME_2}_$I" touch $PATH2 if (( $? != 0 )); then ((ERR=$ERR+1)) echo "Error creating file $PATH2" ls -li $PATH2 fi # for this file, we first check readdir ls $TEST_DIR/$SUB_DIR1 | egrep -e "^${FILENAME_2}_$I$" > /dev/null if (( $? != 0 )); then ((ERR=$ERR+1)) echo "Error: ${FILENAME_2}_$I does not appear in readdir($TEST_DIR/$SUB_DIR1)" ls $TEST_DIR/$SUB_DIR1 fi # then, getattr stat $PATH2 > /dev/null if (( $? != 0 )); then ((ERR=$ERR+1)) echo "Error getting file attributes for $PATH2" ls -li $PATH2 fi (( I = $I + 1 )) done echo echo "rm/stat/readdir sequence..." I=0 while (( $I < (($NB_FILES/2)) )) ; do printf "#" # remove file PATH1="$TEST_DIR/$SUB_DIR1/${FILENAME_1}_$I" rm $PATH1 if (( $? != 0 )); then ((ERR=$ERR+1)) echo "Error removing file $PATH1" ls -li $PATH1 fi # try to getattr ls -l $PATH1 > /dev/null 2>&1 val=$? if (( $val != 2 )); then ((ERR=$ERR+1)) echo "Error: getattr after rm should return ENOENT ($val returned)" ls -li $PATH1 fi # then, check readdir ls $TEST_DIR/$SUB_DIR1 | egrep -e "^${FILENAME_1}_$I$" > /dev/null if (( $? == 0 )); then ((ERR=$ERR+1)) echo "Error: ${FILENAME_1}_$I still appears in readdir($TEST_DIR/$SUB_DIR1)" ls $TEST_DIR/$SUB_DIR1 fi # remove file PATH2="$TEST_DIR/$SUB_DIR1/${FILENAME_2}_$I" rm $PATH2 if (( $? != 0 )); then ((ERR=$ERR+1)) echo "Error removing file $PATH2" ls -li $PATH2 fi # check readdir ls $TEST_DIR/$SUB_DIR1 | egrep -e "^${FILENAME_2}_$I$" > /dev/null if (( $? == 0 )); then ((ERR=$ERR+1)) echo "Error: ${FILENAME_2}_$I still appears in readdir($TEST_DIR/$SUB_DIR1)" ls $TEST_DIR/$SUB_DIR1 fi # then, try to getattr ls -l $PATH2 > /dev/null 2>&1 val=$? if (( $val != 2 )); then ((ERR=$ERR+1)) echo "Error: getattr after rm should return ENOENT ($val returned)" ls -li $PATH2 fi (( I = $I + 1 )) done echo echo "create/getattr/readdir sequence AGAIN... ($NB_FILES files)" I=0 while (( $I < (($NB_FILES/2)) )) ; do printf "#" # create file PATH1="$TEST_DIR/$SUB_DIR1/${FILENAME_1}_$I" touch $PATH1 if (( $? != 0 )); then ((ERR=$ERR+1)) echo "Error creating file $PATH1" ls -li $PATH1 fi # first, getattr stat $PATH1 > /dev/null if (( $? != 0 )); then ((ERR=$ERR+1)) echo "Error getting file attributes for $PATH1" ls -li $PATH1 fi # then, check readdir ls $TEST_DIR/$SUB_DIR1 | egrep -e "^${FILENAME_1}_$I$" > /dev/null if (( $? != 0 )); then ((ERR=$ERR+1)) echo "Error: ${FILENAME_1}_$I does not appear in readdir($TEST_DIR/$SUB_DIR1)" ls $TEST_DIR/$SUB_DIR1 fi # create a second file PATH2="$TEST_DIR/$SUB_DIR1/${FILENAME_2}_$I" touch $PATH2 if (( $? != 0 )); then ((ERR=$ERR+1)) echo "Error creating file $PATH2" ls -li $PATH2 fi # for this file, we first check readdir ls $TEST_DIR/$SUB_DIR1 | egrep -e "^${FILENAME_2}_$I$" > /dev/null if (( $? != 0 )); then ((ERR=$ERR+1)) echo "Error: ${FILENAME_2}_$I does not appear in readdir($TEST_DIR/$SUB_DIR1)" ls $TEST_DIR/$SUB_DIR1 fi # then, getattr stat $PATH2 > /dev/null if (( $? != 0 )); then ((ERR=$ERR+1)) echo "Error getting file attributes for $PATH2" ls -li $PATH2 fi (( I = $I + 1 )) done echo echo "cleaning test directory..." rm -rf "$TEST_DIR/$SUB_DIR1" echo "Test termine. $ERR erreurs." �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/support/����������������������������������������������������������������������0000775�0000000�0000000�00000000000�13242724102�0016211�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/support/CMakeLists.txt��������������������������������������������������������0000664�0000000�0000000�00000002524�13242724102�0020754�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������if(USE_DBUS) include_directories( ${DBUS_INCLUDE_DIRS} ) endif(USE_DBUS) # string utilities. This should eventually go into src/os # because BSD has them and Linux doesn't set(string_utils_STAT_SRCS strlcpy.c strnlen.c ) add_library(string_utils STATIC ${string_utils_STAT_SRCS}) add_sanitizers(string_utils) # hash function libraries set(hash_SRCS murmur3.c city.c ) add_library(hash STATIC ${hash_SRCS}) add_sanitizers(hash) # uid2grp mapper set( uid2grp_SRCS uid2grp.c uid2grp_cache.c ) add_library(uid2grp STATIC ${uid2grp_SRCS}) add_sanitizers(uid2grp) # netgroup cache set(netgroup_cache_SRCS netgroup_cache.c ) add_library(netgroup_cache STATIC ${netgroup_cache_SRCS}) add_sanitizers(netgroup_cache) ########### next target ############### SET(support_STAT_SRCS nfs4_acls.c nfs_creds.c nfs_filehandle_mgmt.c nfs_read_conf.c nfs_convert.c nfs_ip_name.c ds.c exports.c fridgethr.c delayed_exec.c misc.c bsd-base64.c server_stats.c export_mgr.c ) if(ERROR_INJECTION) set(support_STAT_SRCS ${support_STAT_SRCS} err_inject.c ) endif(ERROR_INJECTION) if(APPLE) set(support_STAT_SRCS ${support_STAT_SRCS} misc.c ) endif(APPLE) add_library(support STATIC ${support_STAT_SRCS}) add_sanitizers(support) ########### install files ############### ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/support/bsd-base64.c����������������������������������������������������������0000664�0000000�0000000�00000023600�13242724102�0020210�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* $OpenBSD: base64.c,v 1.3 1997/11/08 20:46:55 deraadt Exp $ */ /* * Copyright (c) 1996 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1995 by International Business Machines, Inc. * * International Business Machines, Inc. (hereinafter called IBM) grants * permission under its copyrights to use, copy, modify, and distribute this * Software with or without fee, provided that the above copyright notice and * all paragraphs of this notice appear in all copies, and that the name of IBM * not be used in connection with the marketing of any product incorporating * the Software or modifications thereof, without specific, written prior * permission. * * To the extent it has a right to do so, IBM grants an immunity from suit * under its patents, if any, for the use, sale or manufacture of products to * the extent that such products are used for performing Domain Name System * dynamic updates in TCP/IP networks by means of the Software. No immunity is * granted for any product per se or for any other function of any product. * * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. */ #include "config.h" #if !defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP) #include <sys/types.h> #include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "bsd-base64.h" #define Assert(Cond) do { if (!(Cond)) abort(); } while (0) static const char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static const char Pad64 = '='; /* * URL or file name safe encoding, see * http://tools.ietf.org/html/rfc4648 for mapping details */ static const char Base64url[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; /* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) The following encoding technique is taken from RFC 1521 by Borenstein and Freed. It is reproduced here in a slightly edited form for convenience. A 65-character subset of US-ASCII is used, enabling 6 bits to be represented per printable character. (The extra 65th character, "=", is used to signify a special processing function.) The encoding process represents 24-bit groups of input bits as output strings of 4 encoded characters. Proceeding from left to right, a 24-bit input group is formed by concatenating 3 8-bit input groups. These 24 bits are then treated as 4 concatenated 6-bit groups, each of which is translated into a single digit in the base64 alphabet. Each 6-bit group is used as an index into an array of 64 printable characters. The character referenced by the index is placed in the output string. Table 1: The Base64 Alphabet Value Encoding Value Encoding Value Encoding Value Encoding 0 A 17 R 34 i 51 z 1 B 18 S 35 j 52 0 2 C 19 T 36 k 53 1 3 D 20 U 37 l 54 2 4 E 21 V 38 m 55 3 5 F 22 W 39 n 56 4 6 G 23 X 40 o 57 5 7 H 24 Y 41 p 58 6 8 I 25 Z 42 q 59 7 9 J 26 a 43 r 60 8 10 K 27 b 44 s 61 9 11 L 28 c 45 t 62 + 12 M 29 d 46 u 63 / 13 N 30 e 47 v 14 O 31 f 48 w (pad) = 15 P 32 g 49 x 16 Q 33 h 50 y Special processing is performed if fewer than 24 bits are available at the end of the data being encoded. A full encoding quantum is always completed at the end of a quantity. When fewer than 24 input bits are available in an input group, zero bits are added (on the right) to form an integral number of 6-bit groups. Padding at the end of the data is performed using the '=' character. Since all base64 input is an integral number of octets, only the ------------------------------------------------- following cases can arise: (1) the final quantum of encoding input is an integral multiple of 24 bits; here, the final unit of encoded output will be an integral multiple of 4 characters with no "=" padding, (2) the final quantum of encoding input is exactly 8 bits; here, the final unit of encoded output will be two characters followed by two "=" padding characters, or (3) the final quantum of encoding input is exactly 16 bits; here, the final unit of encoded output will be three characters followed by one "=" padding character. */ int b64_enc(u_char const *src, size_t srclength, char *target, size_t targsize, const char *map) { size_t datalength = 0; u_char input[3]; u_char output[4]; int i; while (srclength > 2) { input[0] = *src++; input[1] = *src++; input[2] = *src++; srclength -= 3; output[0] = input[0] >> 2; output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); output[3] = input[2] & 0x3f; Assert(output[0] < 64); Assert(output[1] < 64); Assert(output[2] < 64); Assert(output[3] < 64); if (datalength + 4 > targsize) return -1; target[datalength++] = map[output[0]]; target[datalength++] = map[output[1]]; target[datalength++] = map[output[2]]; target[datalength++] = map[output[3]]; } /* Now we worry about padding. */ if (srclength != 0) { /* Get what's left. */ input[0] = input[1] = input[2] = '\0'; for (i = 0; i < srclength; i++) input[i] = *src++; output[0] = input[0] >> 2; output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); Assert(output[0] < 64); Assert(output[1] < 64); Assert(output[2] < 64); if (datalength + 4 > targsize) return -1; target[datalength++] = map[output[0]]; target[datalength++] = map[output[1]]; if (srclength == 1) target[datalength++] = Pad64; else target[datalength++] = map[output[2]]; target[datalength++] = Pad64; } if (datalength >= targsize) return -1; target[datalength] = '\0'; /* Returned value doesn't count \0. */ return datalength; } int b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize) { return b64_enc(src, srclength, target, targsize, Base64); } int base64url_encode(u_char const *src, size_t srclength, char *target, size_t targsize) { return b64_enc(src, srclength, target, targsize, Base64url); } /* skips all whitespace anywhere. converts characters, four at a time, starting at (or after) src from base - 64 numbers into three 8 bit bytes in the target area. it returns the number of data bytes stored at the target, or -1 on error. */ int b64_pton(char const *src, u_char *target, size_t targsize) { int tarindex, state, ch; char *pos; state = 0; tarindex = 0; while ((ch = *src++) != '\0') { if (isspace(ch)) /* Skip whitespace anywhere. */ continue; if (ch == Pad64) break; pos = strchr(Base64, ch); if (pos == 0) /* A non-base64 character. */ return -1; switch (state) { case 0: if (target) { if (tarindex >= targsize) return -1; target[tarindex] = (pos - Base64) << 2; } state = 1; break; case 1: if (target) { if (tarindex + 1 >= targsize) return -1; target[tarindex] |= (pos - Base64) >> 4; target[tarindex + 1] = ((pos - Base64) & 0x0f) << 4; } tarindex++; state = 2; break; case 2: if (target) { if (tarindex + 1 >= targsize) return -1; target[tarindex] |= (pos - Base64) >> 2; target[tarindex + 1] = ((pos - Base64) & 0x03) << 6; } tarindex++; state = 3; break; case 3: if (target) { if (tarindex >= targsize) return -1; target[tarindex] |= (pos - Base64); } tarindex++; state = 0; break; } } /* * We are done decoding Base-64 chars. Let's see if we ended * on a byte boundary, and/or with erroneous trailing characters. */ if (ch == Pad64) { /* We got a pad char. */ ch = *src++; /* Skip it, get next. */ switch (state) { case 0: /* Invalid = in first position */ case 1: /* Invalid = in second position */ return -1; case 2: /* Valid, means one byte of info */ /* Skip any number of spaces. */ for (; ch != '\0'; ch = *src++) if (!isspace(ch)) break; /* Make sure there is another trailing = sign. */ if (ch != Pad64) return -1; ch = *src++; /* Skip the = */ /* Fall through to "single trailing =" case. */ /* FALLTHROUGH */ case 3: /* Valid, means two bytes of info */ /* * We know this char is an =. Is there anything but * whitespace after it? */ for (; ch != '\0'; ch = *src++) if (!isspace(ch)) return -1; /* * Now make sure for cases 2 and 3 that the "extra" * bits that slopped past the last full byte were * zeros. If we don't check them, they become a * subliminal channel. */ if (target && target[tarindex] != 0) return -1; } } else { /* * We ended by seeing the end of the string. Make sure we * have no partial bytes lying around. */ if (state != 0) return -1; } return tarindex; } #endif /* !defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP) */ ��������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/support/city-test.c�����������������������������������������������������������0000664�0000000�0000000�00000456252�13242724102�0020320�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* city-test.c - cityhash-c * CityHash on C * Copyright (c) 2011-2012, Alexander Nusov * * - original copyright notice - * Copyright (c) 2011 Google, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include <string.h> #include <stdio.h> #include "city.h" #ifdef __SSE4_2__ #include "citycrc.h" #endif static const uint64 k0 = 0xc3a5c85c97cb3127ULL; static const uint64 kSeed0 = 1234567; static const uint64 kSeed1 = 0xc3a5c85c97cb3127ULL; static const uint128 kSeed128 = { 1234567, 0xc3a5c85c97cb3127ULL }; static const int kDataSize = 1 << 20; static const int kTestSize = 300; static char data[1 << 20]; static int errors; /* global error count */ /* Initialize data to pseudorandom values. */ void setup(void) { uint64 a = 9; uint64 b = 777; int i; for (i = 0; i < kDataSize; i++) { a = (a ^ (a >> 41)) * k0 + b; b = (b ^ (b >> 41)) * k0 + i; uint8 u = b >> 37; memcpy(data + i, &u, 1); /* uint8 -> char */ } } #define C(x) (0x ## x ## ULL) static const uint64 testdata[300][15] = { {C(9 ae16a3b2f90404f), C(75106 db890237a4a), C(3f eac5f636039766), C(3df 09df c64c09a2b), C(3 cb540c392e51e29), C(6 b56343feac0663), C(5 b7bc50fd8e8ad92), C(3df 09df c64c09a2b), C(3 cb540c392e51e29), C(6 b56343feac0663), C(5 b7bc50fd8e8ad92), C(889f 555 a0f5b2dc0), C(7767800902 c8a8ce), C(bcd2a808f4cb4a44), C(e9024dba8f94f2f3)}, {C(75e9 dee28ded761d), C(931992 c1b14334c5), C(245ee b25ba2c172e), C(1290f 0e8 a5caa74d), C(ca4c6bf7583f5cda), C(e1d60d51632c536d), C(cbc54a1db641910a), C(1290f 0e8 a5caa74d), C(ca4c6bf7583f5cda), C(e1d60d51632c536d), C(cbc54a1db641910a), C(9866 d68d17c2c08e), C(8 d84ba63eb4d020a), C(df0ad99c78cbce44), C(7 c98593ef62573ed)}, {C(75 de892fdc5ba914), C(f89832e71f764c86), C(39 a82df1f278a297), C(b4af8ae673acb930), C(992 b7acb203d8885), C(57 b533f3f8b94d50), C(bbb69298a5dcf1a1), C(b4af8ae673acb930), C(992 b7acb203d8885), C(57 b533f3f8b94d50), C(bbb69298a5dcf1a1), C(433495196 af9ac4f), C(53445 c0896ae1fe6), C(f7b939315f6fb56f), C(ac1b05e5a2e0335e)}, {C(69 cfe9fca1cc683a), C(e65f2a81e19b8067), C(20575ea6370 a9d14), C(8f 52532f c6f005b7), C(4eb e60df371ec129), C(c6ef8a7f8deb8116), C(83df 17e3 c9bb9a67), C(8f 52532f c6f005b7), C(4eb e60df371ec129), C(c6ef8a7f8deb8116), C(83df 17e3 c9bb9a67), C(6 a0aaf51016e19cd), C(fb0d1e89f39dbf6a), C(c73095102872943a), C(405ea97456 c28a75)}, {C(675 b04c582a34966), C(53624 b5ef8cd4f45), C(c412e0931ac8c9b1), C(798637e677 c65a3), C(83e3 b06adc4cd3ff), C(f3e76e8a7135852f), C(111e66 cfbb05366d), C(798637e677 c65a3), C(83e3 b06adc4cd3ff), C(f3e76e8a7135852f), C(111e66 cfbb05366d), C(29 c4f84aa48e8682), C(b77a8685750c94d0), C(7 cab65571969123f), C(fb1dbd79f68a8519)}, {C(46f a817397ea8b68), C(cc960c1c15ce2d20), C(e5f9f947bafb9e79), C(b342cdf0d7ac4b2a), C(66914 d44b373b232), C(261194e76 cb43966), C(45 a0010190365048), C(b342cdf0d7ac4b2a), C(66914 d44b373b232), C(261194e76 cb43966), C(45 a0010190365048), C(e2586947ca8eac83), C(6650 daf2d9677cdc), C(2f 9533 d8f4951a9), C(a5bdc0f3edc4bd7b)}, {C(406e959 cdffadec7), C(e80dc125dca28ed1), C(e5beb146d4b79a21), C(e66d5c1bb441541a), C(d14961bc1fd265a2), C(e4cc669d4fc0577f), C(abf4a51e36da2702), C(e66d5c1bb441541a), C(d14961bc1fd265a2), C(e4cc669d4fc0577f), C(abf4a51e36da2702), C(21236 d12df338f75), C(54 b8c4a5ad2ae4a4), C(202 d50ef9c2d4465), C(5ec c6a128e51a797)}, {C(46663908 b4169b95), C(4e7 e90b5c426bf1d), C(dc660b58daaf8b2c), C(b298265ebd1bd55f), C(4 a5f6838b55c0b08), C(fc003c97aa05d397), C(2f b5adad3380c3bc), C(b298265ebd1bd55f), C(4 a5f6838b55c0b08), C(fc003c97aa05d397), C(2f b5adad3380c3bc), C(c46fd01d253b4a0b), C(4 c799235c2a33188), C(7e21 bc57487a11bf), C(e1392bb1994bd4f2)}, {C(f214b86cffeab596), C(5f ccb0b132da564f), C(86e7 aa8b4154b883), C(763529 c8d4189ea8), C(860 d77e7fef74ca3), C(3 b1ba41191219b6b), C(722 b25dfa6d0a04b), C(763529 c8d4189ea8), C(860 d77e7fef74ca3), C(3 b1ba41191219b6b), C(722 b25dfa6d0a04b), C(5f 7 b463094e22a91), C(75 d6f57376b31bd7), C(d253c7f89efec8e6), C(efe56ac880a2b8a3)}, {C(eba670441d1a4f7d), C(eb6b272502d975fa), C(69f 8 d424d50c083e), C(313 d49cb51b8cd2c), C(6e982 d8b4658654a), C(dd59629a17e5492d), C(81 cb23bdab95e30e), C(313 d49cb51b8cd2c), C(6e982 d8b4658654a), C(dd59629a17e5492d), C(81 cb23bdab95e30e), C(1e6 c3e6c454c774f), C(177655172666 d5ea), C(9 cc67e0d38d80886), C(36 a2d64d7bc58d22)}, {C(172 c17ff21dbf88d), C(1f 5104e320f 0 c815), C(1e34 e9f1fa63bcef), C(3506 ae8fae368d2a), C(59f a2b2de5306203), C(67 d1119dcfa6007e), C(1f 7190 c648ad9aef), C(3506 ae8fae368d2a), C(59f a2b2de5306203), C(67 d1119dcfa6007e), C(1f 7190 c648ad9aef), C(7e8 b1e689137b637), C(cbe373368a31db3c), C(dbc79d82cd49c671), C(641399520 c452c99)}, {C(5 a0838df8a019b8c), C(73f c859b4952923), C(45e39 daf153491bd), C(a9b91459a5fada46), C(de0fbf8800a2da3), C(21800e4 b5af9dedb), C(517 c3726ae0dbae7), C(a9b91459a5fada46), C(de0fbf8800a2da3), C(21800e4 b5af9dedb), C(517 c3726ae0dbae7), C(1 ccffbd74acf9d66), C(cbb08cf95e7eda99), C(61444f 09e2 a29587), C(35 c0d15745f96455)}, {C(8f 42 b1fbb2fc0302), C(5 ae31626076ab6ca), C(b87f0cb67cb75d28), C(2498586 ac2e1fab2), C(e683f9cbea22809a), C(a9728d0b2bbe377c), C(46 baf5cae53dc39a), C(2498586 ac2e1fab2), C(e683f9cbea22809a), C(a9728d0b2bbe377c), C(46 baf5cae53dc39a), C(806f 4352 c99229e), C(d4643728fc71754a), C(998 c1647976bc893), C(d8094fdc2d6bb032)}, {C(72085e82 d70dcea9), C(32f 502 c43349ba16), C(5eb c98c3645a018f), C(c7fa762238fd90ac), C(8 d03b5652d615677), C(a3f5226e51d42217), C(46 d5010a7cae8c1e), C(c7fa762238fd90ac), C(8 d03b5652d615677), C(a3f5226e51d42217), C(46 d5010a7cae8c1e), C(4293122580 db7f5f), C(3df 6042f 39 c6d487), C(439124809 cf5c90e), C(90 b704e4f71d0ccf)}, {C(32 b75fc2223b5032), C(246f ff80eb230868), C(a6fdbc82c9aeecc0), C(c089498074167021), C(ab094a9f9ab81c23), C(4f acf3d9466bcb03), C(57 aa9c67938cf3eb), C(c089498074167021), C(ab094a9f9ab81c23), C(4f acf3d9466bcb03), C(57 aa9c67938cf3eb), C(79 a769ca1c762117), C(9 c8dee60337f87a8), C(dabf1b96535a3abb), C(f87e9fbb590ba446)}, {C(e1dd010487d2d647), C(12352858295 d2167), C(acc5e9b6f6b02dbb), C(1 c66ceea473413df), C(dc3f70a124b25a40), C(66 a6dfe54c441cd8), C(b436dabdaaa37121), C(1 c66ceea473413df), C(dc3f70a124b25a40), C(66 a6dfe54c441cd8), C(b436dabdaaa37121), C(6 d95aa6890f51674), C(42 c6c0fc7ab3c107), C(83 b9dfe082e76140), C(939 cdbd3614d6416)}, {C(2994f 9245194 a7e2), C(b7cd7249d6db6c0c), C(2170 a7d119c5c6c3), C(8505 c996b70ee9fc), C(b92bba6b5d778eb7), C(4 db4c57f3a7a4aee), C(3 cfd441cb222d06f), C(8505 c996b70ee9fc), C(b92bba6b5d778eb7), C(4 db4c57f3a7a4aee), C(3 cfd441cb222d06f), C(4 d940313c96ac6bd), C(43762837 c9ffac4b), C(480f cf58920722e3), C(4 bbd1e1a1d06752f)}, {C(32e2 ed6fa03e5b22), C(58 baf09d7c71c62b), C(a9c599f3f8f50b5b), C(1660 a2c4972d0fa1), C(1 a1538d6b50a57c), C(8 a5362485bbc9363), C(e8eec3c84fd9f2f8), C(1660 a2c4972d0fa1), C(1 a1538d6b50a57c), C(8 a5362485bbc9363), C(e8eec3c84fd9f2f8), C(2562514461 d373da), C(33857675f ed52b4), C(e58d2a17057f1943), C(fe7d3f30820e4925)}, {C(37 a72b6e89410c9f), C(139f ec53b78cee23), C(4f ccd8f0da7575c3), C(3 a5f04166518ac75), C(f49afe05a44fc090), C(cb01b4713cfda4bd), C(9027 bd37ffc0a5de), C(3 a5f04166518ac75), C(f49afe05a44fc090), C(cb01b4713cfda4bd), C(9027 bd37ffc0a5de), C(e15144d3ad46ec1b), C(736f d99679a5ae78), C(b3d7ed9ed0ddfe57), C(cef60639457867d7)}, {C(10836563 cb8ff3a1), C(d36f67e2dfc085f7), C(edc1bb6a3dcba8df), C(bd4f3a0566df3bed), C(81f c8230c163dcbe), C(4168 bc8417a8281b), C(7100 c9459827c6a6), C(bd4f3a0566df3bed), C(81f c8230c163dcbe), C(4168 bc8417a8281b), C(7100 c9459827c6a6), C(21 cad59eaf79e72f), C(61 c8af6fb71469f3), C(b0dfc42ce4f578b), C(33ea34 ccea305d4e)}, {C(4 dabcb5c1d382e5c), C(9 a868c608088b7a4), C(7 b2b6c389b943be5), C(c914b925ab69fda0), C(6 bafe864647c94d7), C(7 a48682dd4afa22), C(40f e01210176ba10), C(c914b925ab69fda0), C(6 bafe864647c94d7), C(7 a48682dd4afa22), C(40f e01210176ba10), C(88dd 28f 33ec31388), C(c6db60abf1d45381), C(7 b94c447298824d5), C(6 b2a5e05ad0b9fc0)}, {C(296 afb509046d945), C(c38fe9eb796bd4be), C(d7b17535df110279), C(dd2482b87d1ade07), C(662785 d2e3e78ddf), C(eae39994375181bb), C(9994500 c077ee1db), C(dd2482b87d1ade07), C(662785 d2e3e78ddf), C(eae39994375181bb), C(9994500 c077ee1db), C(a275489f8c6bb289), C(30695ea31df 1 a369), C(1 aeeb31802d701b5), C(7799 d5a6d5632838)}, {C(f7c0257efde772ea), C(af6af9977ecf7bff), C(1 cdff4bd07e8d973), C(fab1f4acd2cd4ab4), C(b4e19ba52b566bd), C(7f 1 db45725fe2881), C(70276f f8763f8396), C(fab1f4acd2cd4ab4), C(b4e19ba52b566bd), C(7f 1 db45725fe2881), C(70276f f8763f8396), C(1 b0f2b546dddd16b), C(aa066984b5fd5144), C(7 c3f9386c596a5a8), C(e5befdb24b665d5f)}, {C(61e021 c8da344ba1), C(cf9c720676244755), C(354f fa8e9d3601f6), C(44e40 a03093fbd92), C(bda9481cc5b93cae), C(986 b589cbc0cf617), C(210f 59f 074044831), C(44e40 a03093fbd92), C(bda9481cc5b93cae), C(986 b589cbc0cf617), C(210f 59f 074044831), C(ac32cbbb6f50245a), C(afa6f712efb22075), C(47289f 7 af581719f), C(31 b6e75d3aa0e54b)}, {C(c0a86ed83908560b), C(440 c8b6f97bd1749), C(a99bf2891726ea93), C(ac0c0b84df66df9d), C(3ee2337 b437eb264), C(8 a341daed9a25f98), C(cc665499aa38c78c), C(ac0c0b84df66df9d), C(3ee2337 b437eb264), C(8 a341daed9a25f98), C(cc665499aa38c78c), C(af7275299d79a727), C(874f a8434b45d0e), C(ca7b67388950dd33), C(2 db5cd3675ec58f7)}, {C(35 c9cf87e4accbf3), C(2267eb4 d2191b2a3), C(80217695666 b2c9), C(cd43a24abbaae6d), C(a88abf0ea1b2a8ff), C(e297ff01427e2a9d), C(935 d545695b2b41d), C(cd43a24abbaae6d), C(a88abf0ea1b2a8ff), C(e297ff01427e2a9d), C(935 d545695b2b41d), C(6 accd4dbcb52e849), C(261295 acb662fd49), C(f9d91f1ac269a8a2), C(5e45f 39df 355e395)}, {C(e74c366b3091e275), C(522e657 c5da94b06), C(ca9afa806f1a54ac), C(b545042f67929471), C(90 d10e75ed0e75d8), C(3ea60f 8f 158df 77e), C(8863eff 3 c2d670b7), C(b545042f67929471), C(90 d10e75ed0e75d8), C(3ea60f 8f 158df 77e), C(8863eff 3 c2d670b7), C(5799296e97f 144 a7), C(1 d6e517c12a88271), C(da579e9e1add90ef), C(942f b4cdbc3a4da)}, {C(a3f2ca45089ad1a6), C(13f 6270f e56fbce4), C(1f 93 a534bf03e705), C(aaea14288ae2d90c), C(1 be3cd51ef0f15e8), C(e8b47c84d5a4aac1), C(297 d27d55b766782), C(aaea14288ae2d90c), C(1 be3cd51ef0f15e8), C(e8b47c84d5a4aac1), C(297 d27d55b766782), C(e922d1d8bb2afd0), C(b4481c4fa2e7d8d5), C(691e21538 af794d5), C(9 bd4fb0a53962a72)}, {C(e5181466d8e60e26), C(cf31f3a2d582c4f3), C(d9cee87cb71f75b2), C(4750 ca6050a2d726), C(d6e6dd8940256849), C(f3b3749fdab75b0), C(c55d8a0f85ba0ccf), C(4750 ca6050a2d726), C(d6e6dd8940256849), C(f3b3749fdab75b0), C(c55d8a0f85ba0ccf), C(47f 134f 9544 c6da6), C(e1cdd9cb74ad764), C(3 ce2d096d844941e), C(321f e62f608d2d4e)}, {C(fb528a8dd1e48ad7), C(98 c4fd149c8a63dd), C(4 abd8fc3377ae1f), C(d7a9304abbb47cc5), C(7f 2 b9a27aa57f99), C(353 ab332d4ef9f18), C(47 d56b8d6c8cf578), C(d7a9304abbb47cc5), C(7f 2 b9a27aa57f99), C(353 ab332d4ef9f18), C(47 d56b8d6c8cf578), C(df55f58ae09f311f), C(dba9511784fa86e3), C(c43ce0288858a47e), C(62971e94270 b78e1)}, {C(da6d2b7ea9d5f9b6), C(57 b11153ee3b4cc8), C(7 d3bd1256037142f), C(90 b16ff331b719b5), C(fc294e7ad39e01e6), C(d2145386bab41623), C(7045 a63d44d76011), C(90 b16ff331b719b5), C(fc294e7ad39e01e6), C(d2145386bab41623), C(7045 a63d44d76011), C(a232222ed0fe2fa4), C(e6c17dff6c323a8a), C(bbcb079be123ac6c), C(4121f e2c5de7d850)}, {C(61 d95225bc2293e), C(f6c52cb6be9889a8), C(91 a0667a7ed6a113), C(441133 d221486a3d), C(fb9c5a40e19515b), C(6 c967b6c69367c2d), C(145 bd9ef258c4099), C(441133 d221486a3d), C(fb9c5a40e19515b), C(6 c967b6c69367c2d), C(145 bd9ef258c4099), C(a0197657160c686e), C(91 ada0871c23f379), C(2f d74fceccb5c80c), C(bf04f24e2dc17913)}, {C(81247 c01ab6a9cc1), C(fbccea953e810636), C(ae18965000c31be0), C(15 bb46383daec2a5), C(716294063 b4ba089), C(f3bd691ce02c3014), C(14 ccaad685a20764), C(15 bb46383daec2a5), C(716294063 b4ba089), C(f3bd691ce02c3014), C(14 ccaad685a20764), C(5 db25914279d6f24), C(25 c451fce3b2ed06), C(e6bacb43ba1ddb9a), C(6 d77493a2e6fd76)}, {C(c17f3ebd3257cb8b), C(e9e68c939c118c8d), C(72 a5572be35bfc1b), C(f6916c341cb31f2a), C(591 da1353ee5f31c), C(f1313c98a836b407), C(e0b8473eada48cd1), C(f6916c341cb31f2a), C(591 da1353ee5f31c), C(f1313c98a836b407), C(e0b8473eada48cd1), C(ac5c2fb40b09ba46), C(3 a3e8a9344eb6548), C(3 bf9349a9d8483a6), C(c30dd0d9b15e92d0)}, {C(9802438969 c3043b), C(6 cd07575c948dd82), C(83e26 b6830ea8640), C(d52f1fa190576961), C(11 d182e4f0d419cc), C(5 d9ccf1b56617424), C(c8a16debb585e452), C(d52f1fa190576961), C(11 d182e4f0d419cc), C(5 d9ccf1b56617424), C(c8a16debb585e452), C(2158 a752d2686d40), C(b93c1fdf54789e8c), C(3 a9a435627b2a30b), C(de6e5e551e7e5ad5)}, {C(3dd 8ed248 a03d754), C(d8c1fcf001cb62e0), C(87 a822141ed64927), C(4 bfaf6fd26271f47), C(aefeae8222ad3c77), C(cfb7b24351a60585), C(8678904e9 e890b8f), C(4 bfaf6fd26271f47), C(aefeae8222ad3c77), C(cfb7b24351a60585), C(8678904e9 e890b8f), C(968dd 1 aa4d7dcf31), C(7 ac643b015007a39), C(d1e1bac3d94406ec), C(babfa52474a404fa)}, {C(c5bf48d7d3e9a5a3), C(8f 0249 b5c5996341), C(c6d2c8a606f45125), C(fd1779db740e2c48), C(1950ef50f efab3f8), C(e4536426a6196809), C(699556 c502a01a6a), C(fd1779db740e2c48), C(1950ef50f efab3f8), C(e4536426a6196809), C(699556 c502a01a6a), C(2f 49 d268bb57b946), C(b205baa6c66ebfa5), C(ab91ebe4f48b0da1), C(c7e0718ccc360328)}, {C(bc4a21d00cf52288), C(28df 3eb5 a533fa87), C(6081 bbc2a18dd0d), C(8ee d355d219e58b9), C(2 d7b9f1a3d645165), C(5758 d1aa8d85f7b2), C(9 c90c65920041dff), C(8ee d355d219e58b9), C(2 d7b9f1a3d645165), C(5758 d1aa8d85f7b2), C(9 c90c65920041dff), C(3 c5c4ea46645c7f1), C(346879ec c0e2eb90), C(8434f ec461bb5a0f), C(783 ccede50ef5ce9)}, {C(172 c8674913ff413), C(1815 a22400e832bf), C(7e011f 9467 a06650), C(161 be43353a31dd0), C(79 a8afddb0642ac3), C(df43af54e3e16709), C(6e12553 a75b43f07), C(161 be43353a31dd0), C(79 a8afddb0642ac3), C(df43af54e3e16709), C(6e12553 a75b43f07), C(3 ac1b1121e87d023), C(2 d47d33df7b9b027), C(e2d3f71f4e817ff5), C(70 b3a11ca85f8a39)}, {C(17 a361dbdaaa7294), C(c67d368223a3b83c), C(f49cf8d51ab583d2), C(666eb21 e2eaa596), C(778f 3e1 b6650d56), C(3f 6 be451a668fe2d), C(5452892 b0b101388), C(666eb21 e2eaa596), C(778f 3e1 b6650d56), C(3f 6 be451a668fe2d), C(5452892 b0b101388), C(cc867fceaeabdb95), C(f238913c18aaa101), C(f5236b44f324cea1), C(c507cc892ff83dd1)}, {C(5 cc268bac4bd55f), C(232717 a35d5b2f1), C(38 da1393365c961d), C(2 d187f89c16f7b62), C(4eb504204f a1be8), C(222 bd53d2efe5fa), C(a4dcd6d721ddb187), C(2 d187f89c16f7b62), C(4eb504204f a1be8), C(222 bd53d2efe5fa), C(a4dcd6d721ddb187), C(d86bbe67666eca70), C(c8bbae99d8e6429f), C(41 dac4ceb2cb6b10), C(2f 90 c331755f6c48)}, {C(db04969cc06547f1), C(fcacc8a75332f120), C(967 ccec4ed0c977e), C(ac5d1087e454b6cd), C(c1f8b2e284d28f6c), C(cc3994f4a9312cfa), C(8 d61606dbc4e060d), C(ac5d1087e454b6cd), C(c1f8b2e284d28f6c), C(cc3994f4a9312cfa), C(8 d61606dbc4e060d), C(17315 af3202a1307), C(850775145e01163 a), C(96f 10e7357f 930 d2), C(abf27049cf07129)}, {C(25 bd8d3ca1b375b2), C(4 ad34c2c865816f9), C(9 be30ad32f8f28aa), C(7755ea02 dbccad6a), C(cb8aaf8886247a4a), C(8f 6966 ce7ea1b6e6), C(3f 2863090f a45a70), C(7755ea02 dbccad6a), C(cb8aaf8886247a4a), C(8f 6966 ce7ea1b6e6), C(3f 2863090f a45a70), C(1e46 d73019c9fb06), C(af37f39351616f2c), C(657efdf ff20ea2ed), C(93 bdf4c58ada3ecb)}, {C(166 c11fbcbc89fd8), C(cce1af56c48a48aa), C(78908959 b8ede084), C(19032925 ba2c951a), C(a53ed6e81b67943a), C(edc871a9e8ef4bdf), C(ae66cf46a8371aba), C(19032925 ba2c951a), C(a53ed6e81b67943a), C(edc871a9e8ef4bdf), C(ae66cf46a8371aba), C(a37b97790fe75861), C(eda28c8622708b98), C(3f 0 a23509d3d5c9d), C(5787 b0e7976c97cf)}, {C(3565 bcc4ca4ce807), C(ec35bfbe575819d5), C(6 a1f690d886e0270), C(1 ab8c584625f6a04), C(ccfcdafb81b572c4), C(53 b04ba39fef5af9), C(64 ce81828eefeed4), C(1 ab8c584625f6a04), C(ccfcdafb81b572c4), C(53 b04ba39fef5af9), C(64 ce81828eefeed4), C(131 af99997fc662c), C(8 d9081192fae833c), C(2828064791 cb2eb), C(80554 d2e8294065c)}, {C(b7897fd2f274307d), C(6 d43a9e5dd95616d), C(31 a2218e64d8fce0), C(664e581f c1cf769b), C(415110942f c97022), C(7 a5d38fee0bfa763), C(dc87ddb4d7495b6c), C(664e581f c1cf769b), C(415110942f c97022), C(7 a5d38fee0bfa763), C(dc87ddb4d7495b6c), C(7 c3b66372e82e64b), C(1 c89c0ceeeb2dd1), C(dad76d2266214dbd), C(744783486e43 cc61)}, {C(aba98113ab0e4a16), C(287f 883 aede0274d), C(3ec d2a607193ba3b), C(e131f6cc9e885c28), C(b399f98d827e4958), C(6eb90 c8ed6c9090c), C(ec89b378612a2b86), C(e131f6cc9e885c28), C(b399f98d827e4958), C(6eb90 c8ed6c9090c), C(ec89b378612a2b86), C(cfc0e126e2f860c0), C(a9a8ab5dec95b1c), C(d06747f372f7c733), C(fbd643f943a026d3)}, {C(17f 7796e0 d4b636c), C(ddba5551d716137b), C(65f 9735375df 1 ada), C(a39e946d02e14ec2), C(1 c88cc1d3822a193), C(663f 8074 a5172bb4), C(8 ad2934942e4cb9c), C(a39e946d02e14ec2), C(1 c88cc1d3822a193), C(663f 8074 a5172bb4), C(8 ad2934942e4cb9c), C(3 da03b033a95f16c), C(54 a52f1932a1749d), C(779ee b734199bc25), C(359 ce8c8faccc57b)}, {C(33 c0128e62122440), C(b23a588c8c37ec2b), C(f2608199ca14c26a), C(acab0139dc4f36df), C(9502 b1605ca1345a), C(32174ef1 e06a5e9c), C(d824b7869258192b), C(acab0139dc4f36df), C(9502 b1605ca1345a), C(32174ef1 e06a5e9c), C(d824b7869258192b), C(681 d021b52064762), C(30 b6c735f80ac371), C(6 a12d8d7f78896b3), C(157111657 a972144)}, {C(988 bc5d290b97aef), C(6754 bb647eb47666), C(44 b5cf8b5b8106a8), C(a1c5ba961937f723), C(32 d6bc7214dfcb9b), C(6863397e0f 4 c6758), C(e644bcb87e3eef70), C(a1c5ba961937f723), C(32 d6bc7214dfcb9b), C(6863397e0f 4 c6758), C(e644bcb87e3eef70), C(bf25ae22e7aa7c97), C(f5f3177da5756312), C(56 a469cb0dbb58cd), C(5233184 bb6130470)}, {C(23 c8c25c2ab72381), C(d6bc672da4175fba), C(6 aef5e6eb4a4eb10), C(3df 880 c945e68aed), C(5e08 a75e956d456f), C(f984f088d1a322d7), C(7 d44a1b597b7a05e), C(3df 880 c945e68aed), C(5e08 a75e956d456f), C(f984f088d1a322d7), C(7 d44a1b597b7a05e), C(cbd7d157b7fcb020), C(2e2945 e90749c2aa), C(a86a13c934d8b1bb), C(fbe3284bb4eab95f)}, {C(450f e4acc4ad3749), C(3111 b29565e4f852), C(db570fc2abaf13a9), C(35107 d593ba38b22), C(fd8212a125073d88), C(72805 d6e015bfacf), C(6 b22ae1a29c4b853), C(35107 d593ba38b22), C(fd8212a125073d88), C(72805 d6e015bfacf), C(6 b22ae1a29c4b853), C(df2401f5c3c1b633), C(c72307e054c81c8f), C(3ef bfe65bd2922c0), C(b4f632e240b3190c)}, {C(48e1 eff032d90c50), C(dee0fe333d962b62), C(c845776990c96775), C(8ea71758346 b71c9), C(d84258cab79431fd), C(af566b4975cce10a), C(5 c5c7e70a91221d2), C(8ea71758346 b71c9), C(d84258cab79431fd), C(af566b4975cce10a), C(5 c5c7e70a91221d2), C(c33202c7be49ea6f), C(e8ade53b6cbf4caf), C(102ea04f c82ce320), C(c1f7226614715e5e)}, {C(c048604ba8b6c753), C(21ea6 d24b417fdb6), C(4e40 a127ad2d6834), C(5234231 bf173c51), C(62319525583eaf 29), C(87632ef a9144cc04), C(1749 de70c8189067), C(5234231 bf173c51), C(62319525583eaf 29), C(87632ef a9144cc04), C(1749 de70c8189067), C(29672240923e8207), C(11dd 247 a815f6d0d), C(8 d64e16922487ed0), C(9f a6f45d50d83627)}, {C(67f f1cbe469ebf84), C(3 a828ac9e5040eb0), C(85 bf1ad6b363a14b), C(2f c6c0783390d035), C(ef78307f5be5524e), C(a46925b7a1a77905), C(fea37470f9a51514), C(2f c6c0783390d035), C(ef78307f5be5524e), C(a46925b7a1a77905), C(fea37470f9a51514), C(9 d6504cf6d3947ce), C(174 cc006b8e96e7), C(d653a06d8a009836), C(7 d22b5399326a76c)}, {C(b45c7536bd7a5416), C(e2d17c16c4300d3c), C(b70b641138765ff5), C(a5a859ab7d0ddcfc), C(8730164 a0b671151), C(af93810c10348dd0), C(7256010 c74f5d573), C(a5a859ab7d0ddcfc), C(8730164 a0b671151), C(af93810c10348dd0), C(7256010 c74f5d573), C(e22a335be6cd49f3), C(3 bc9c8b40c9c397a), C(18 da5c08e28d3fb5), C(f58ea5a00404a5c9)}, {C(215 c2eaacdb48f6f), C(33 b09acf1bfa2880), C(78 c4e94ba9f28bf), C(981 b7219224443d1), C(1f 476f c4344d7bba), C(abad36e07283d3a5), C(831 bf61190eaaead), C(981 b7219224443d1), C(1f 476f c4344d7bba), C(abad36e07283d3a5), C(831 bf61190eaaead), C(4 c90729f62432254), C(2f fadc94c89f47b3), C(677e790 b43d20e9a), C(bb0a1686e7c3ae5f)}, {C(241 baf16d80e0fe8), C(b6b3c5b53a3ce1d), C(6 ae6b36209eecd70), C(a560b6a4aa3743a4), C(b3e04f202b7a99b), C(3 b3b1573f4c97d9f), C(ccad8715a65af186), C(a560b6a4aa3743a4), C(b3e04f202b7a99b), C(3 b3b1573f4c97d9f), C(ccad8715a65af186), C(d0c93a838b0c37e7), C(7150 aa1be7eb1aad), C(755 b1e60b84d8d), C(51916e77 b1b05ba9)}, {C(d10a9743b5b1c4d1), C(f16e0e147ff9ccd6), C(fbd20a91b6085ed3), C(43 d309eb00b771d5), C(a6d1f26105c0f61b), C(d37ad62406e5c37e), C(75 d9b28c717c8cf7), C(43 d309eb00b771d5), C(a6d1f26105c0f61b), C(d37ad62406e5c37e), C(75 d9b28c717c8cf7), C(8f 5f 118 b425b57cd), C(5 d806318613275f3), C(8150848 bcf89d009), C(d5531710d53e1462)}, {C(919ef9 e209f2edd1), C(684 c33fb726a720a), C(540353f 94e8033), C(26 da1a143e7d4ec4), C(55095ea e445aacf4), C(31ef ad866d075938), C(f9b580cff4445f94), C(26 da1a143e7d4ec4), C(55095ea e445aacf4), C(31ef ad866d075938), C(f9b580cff4445f94), C(b1bea6b8716d9c48), C(9ed2 a3df4a15dc53), C(11f 1 be58843eb8e9), C(d9899ecaaef3c77c)}, {C(b5f9519b6c9280b), C(7823 a2fe2e103803), C(d379a205a3bd4660), C(466ec55 ee4b4302a), C(714f 1 b9985deeaf0), C(728595f 26e633 cf7), C(25ec d0738e1bee2b), C(466ec55 ee4b4302a), C(714f 1 b9985deeaf0), C(728595f 26e633 cf7), C(25ec d0738e1bee2b), C(db51771ad4778278), C(763e5742 ac55639e), C(df040e92d38aa785), C(5df 997 d298499bf1)}, {C(77 a75e89679e6757), C(25 d31fee616b5dd0), C(d81f2dfd08890060), C(7598df 8911dd 40 a4), C(3 b6dda517509b41b), C(7 dae29d248dfffae), C(6697 c427733135f), C(7598df 8911dd 40 a4), C(3 b6dda517509b41b), C(7 dae29d248dfffae), C(6697 c427733135f), C(834 d6c0444c90899), C(c790675b3cd53818), C(28 bb4c996ecadf18), C(92 c648513e6e6064)}, {C(9 d709e1b086aabe2), C(4 d6d6a6c543e3fec), C(df73b01acd416e84), C(d54f613658e35418), C(fcc88fd0567afe77), C(d18f2380980db355), C(ec3896137dfbfa8b), C(d54f613658e35418), C(fcc88fd0567afe77), C(d18f2380980db355), C(ec3896137dfbfa8b), C(eb48dbd9a1881600), C(ca7bd7415ab43ca9), C(e6c5a362919e2351), C(2f 4e4 bd2d5267c21)}, {C(91 c89971b3c20a8a), C(87 b82b1d55780b5), C(bc47bb80dfdaefcd), C(87e11 c0f44454863), C(2df 1 aedb5871cc4b), C(ba72fd91536382c8), C(52 cebef9e6ea865d), C(87e11 c0f44454863), C(2df 1 aedb5871cc4b), C(ba72fd91536382c8), C(52 cebef9e6ea865d), C(5 befc3fc66bc7fc5), C(b128bbd735a89061), C(f8f500816fa012b3), C(f828626c9612f04)}, {C(16468 c55a1b3f2b4), C(40 b1e8d6c63c9ff4), C(143 adc6fee592576), C(4 caf4deeda66a6ee), C(264720f 6f 35f 7840), C(71 c3aef9e59e4452), C(97886 ca1cb073c55), C(4 caf4deeda66a6ee), C(264720f 6f 35f 7840), C(71 c3aef9e59e4452), C(97886 ca1cb073c55), C(16155f ef16fc08e8), C(9 d0c1d1d5254139a), C(246513 bf2ac95ee2), C(22 c8440f59925034)}, {C(1 a2bd6641870b0e4), C(e4126e928f4a7314), C(1e9227 d52aab00b2), C(d82489179f16d4e8), C(a3c59f65e2913cc5), C(36 cbaecdc3532b3b), C(f1b454616cfeca41), C(d82489179f16d4e8), C(a3c59f65e2913cc5), C(36 cbaecdc3532b3b), C(f1b454616cfeca41), C(99393e31 e3eefc16), C(3 ca886eac5754cdf), C(c11776fc3e4756dd), C(ca118f7059198ba)}, {C(1 d2f92f23d3e811a), C(e0812edbcd475412), C(92 d2d6ad29c05767), C(fd7feb3d2956875e), C(d7192a886b8b01b6), C(16e71 dba55f5b85a), C(93 dabd3ff22ff144), C(fd7feb3d2956875e), C(d7192a886b8b01b6), C(16e71 dba55f5b85a), C(93 dabd3ff22ff144), C(14f f0a5444c272c9), C(fb024d3bb8d915c2), C(1 bc3229a94cab5fe), C(6f 6f 1f b3c0dccf09)}, {C(a47c08255da30ca8), C(cf6962b7353f4e68), C(2808051ea18946 b1), C(b5b472960ece11ec), C(13935 c99b9abbf53), C(3e80 d95687f0432c), C(3516 ab536053be5), C(b5b472960ece11ec), C(13935 c99b9abbf53), C(3e80 d95687f0432c), C(3516 ab536053be5), C(748 ce6a935755e20), C(2961 b51d61b0448c), C(864624113 aae88d2), C(a143805366f91338)}, {C(efb3b0262c9cd0c), C(1273901e9 e7699b3), C(58633f 4 ad0dcd5bb), C(62e33 ba258712d51), C(fa085c15d779c0e), C(2 c15d9142308c5ad), C(feb517011f27be9e), C(62e33 ba258712d51), C(fa085c15d779c0e), C(2 c15d9142308c5ad), C(feb517011f27be9e), C(1 b2b049793b9eedb), C(d26be505fabc5a8f), C(adc483e42a5c36c5), C(c81ff37d56d3b00b)}, {C(5029700 a7773c3a4), C(d01231e97e300d0f), C(397 cdc80f1f0ec58), C(e4041579de57c879), C(bbf513cb7bab5553), C(66 ad0373099d5fa0), C(44 bb6b21b87f3407), C(e4041579de57c879), C(bbf513cb7bab5553), C(66 ad0373099d5fa0), C(44 bb6b21b87f3407), C(a8108c43b4daba33), C(c0b5308c311e865e), C(cdd265ada48f6fcf), C(efbc1dae0a95ac0a)}, {C(71 c8287225d96c9a), C(eb836740524735c4), C(4777522 d0e09846b), C(16f de90d02a1343b), C(ad14e0ed6e165185), C(8df 6e0 b2f24085dd), C(caa8a47292d50263), C(16f de90d02a1343b), C(ad14e0ed6e165185), C(8df 6e0 b2f24085dd), C(caa8a47292d50263), C(a020413ba660359d), C(9 de401413f7c8a0c), C(20 bfb965927a7c85), C(b52573e5f817ae27)}, {C(4e8 b9ad9347d7277), C(c0f195eeee7641cf), C(dbd810bee1ad5e50), C(8459801016414808), C(6f bf75735353c2d1), C(6e69 aaf2d93ed647), C(85 bb5b90167cce5e), C(8459801016414808), C(6f bf75735353c2d1), C(6e69 aaf2d93ed647), C(85 bb5b90167cce5e), C(39 d79ee490d890cc), C(ac9f31f7ec97deb0), C(3 bdc1cae4ed46504), C(eb5c63cfaee05622)}, {C(1 d5218d6ee2e52ab), C(cb25025c4daeff3b), C(aaf107566f31bf8c), C(aad20d70e231582b), C(eab92d70d9a22e54), C(cc5ab266375580c0), C(85091463e3630 dce), C(aad20d70e231582b), C(eab92d70d9a22e54), C(cc5ab266375580c0), C(85091463e3630 dce), C(b830b617a4690089), C(9 dacf13cd76f13cf), C(d47cc5224265c68f), C(f04690880202b002)}, {C(162360 be6c293c8b), C(ff672b4a831953c8), C(dda57487ab6f78b5), C(38 a42e0db55a4275), C(585971 da56bb56d6), C(cd957009adc1482e), C(d6a96021e427567d), C(38 a42e0db55a4275), C(585971 da56bb56d6), C(cd957009adc1482e), C(d6a96021e427567d), C(8e2 b1a5a63cd96fe), C(426ef8 ce033d722d), C(c4d1c3d8acdda5f), C(4e694 c9be38769b2)}, {C(31459914f 13 c8867), C(ef96f4342d3bef53), C(a4e944ee7a1762fc), C(3526 d9b950a1d910), C(a58ba01135bca7c0), C(cbad32e86d60a87c), C(adde1962aad3d730), C(3526 d9b950a1d910), C(a58ba01135bca7c0), C(cbad32e86d60a87c), C(adde1962aad3d730), C(55f aade148929704), C(bfc06376c72a2968), C(97762698 b87f84be), C(117483 d4828cbaf7)}, {C(6 b4e8fca9b3aecff), C(3ea0 a33def0a296c), C(901f cb5fe05516f5), C(7 c909e8cd5261727), C(c5acb3d5fbdc832e), C(54eff 5 c782ad3cdd), C(9 d54397f3caf5bfa), C(7 c909e8cd5261727), C(c5acb3d5fbdc832e), C(54eff 5 c782ad3cdd), C(9 d54397f3caf5bfa), C(6 b53ce24c4fc3092), C(2789 abfdd4c9a14d), C(94 d6a2261637276c), C(648 aa4a2a1781f25)}, {C(dd3271a46c7aec5d), C(fb1dcb0683d711c3), C(240332e9 ebe5da44), C(479f 936 b6d496dca), C(dc2dc93d63739d4a), C(27e4151 c3870498c), C(3 a3a22ba512d13ba), C(479f 936 b6d496dca), C(dc2dc93d63739d4a), C(27e4151 c3870498c), C(3 a3a22ba512d13ba), C(5 da92832f96d3cde), C(439 b9ad48c4e7644), C(d2939279030accd9), C(6829f 920e2950 dbe)}, {C(109 b226238347d6e), C(e27214c32c43b7e7), C(eb71b0afaf0163ef), C(464f 1 adf4c68577), C(acf3961e1c9d897f), C(985 b01ab89b41fe1), C(6972 d6237390aac0), C(464f 1 adf4c68577), C(acf3961e1c9d897f), C(985 b01ab89b41fe1), C(6972 d6237390aac0), C(122 d89898e256a0e), C(ac830561bd8be599), C(5744312574f bf0ad), C(7 bff7f480a924ce9)}, {C(cc920608aa94cce4), C(d67efe9e097bce4f), C(5687727 c2c9036a9), C(8 af42343888843c), C(191433f fcbab7800), C(7eb45f c94f88a71), C(31 bc5418ffb88fa8), C(8 af42343888843c), C(191433f fcbab7800), C(7eb45f c94f88a71), C(31 bc5418ffb88fa8), C(4 b53a37d8f446cb7), C(a6a7dfc757a60d28), C(a074be7bacbc013a), C(cc6db5f270de7adc)}, {C(901f f46f22283dbe), C(9dd 59794 d049a066), C(3 c7d9c3b0e77d2c6), C(dc46069eec17bfdf), C(cacb63fe65d9e3e), C(362f b57287d530c6), C(5854 a4fbe1762d9), C(dc46069eec17bfdf), C(cacb63fe65d9e3e), C(362f b57287d530c6), C(5854 a4fbe1762d9), C(3197427495021ef c), C(5f abf34386aa4205), C(ca662891de36212), C(21f 603e4 d39bca84)}, {C(11 b3bdda68b0725d), C(2366 bf0aa97a00bd), C(55 dc4a4f6bf47e2b), C(69437142 dae5a255), C(f2980cc4816965ac), C(dbbe76ba1d9adfcf), C(49 c18025c0a8b0b5), C(69437142 dae5a255), C(f2980cc4816965ac), C(dbbe76ba1d9adfcf), C(49 c18025c0a8b0b5), C(fe25c147c9001731), C(38 b99cad0ca30c81), C(c7ff06ac47eb950), C(a10f92885a6b3c02)}, {C(9f 5f 03e84 a40d232), C(1151 a9ff99da844), C(d6f2e7c559ac4657), C(5e351 e20f30377bf), C(91 b3805daf12972c), C(94417f a6452a265e), C(bfa301a26765a7c), C(5e351 e20f30377bf), C(91 b3805daf12972c), C(94417f a6452a265e), C(bfa301a26765a7c), C(6924e2 a053297d13), C(ed4a7904ed30d77e), C(d734abaad66d6eab), C(ce373e6c09e6e8a1)}, {C(39eef f4f60f439be), C(1f 7559 c118517c70), C(6139 d2492237a36b), C(fd39b7642cecf78f), C(104f 1 af4e9201df5), C(ab1a3cc7eaeab609), C(cee3363f210a3d8b), C(fd39b7642cecf78f), C(104f 1 af4e9201df5), C(ab1a3cc7eaeab609), C(cee3363f210a3d8b), C(51490f 65f e56c884), C(6 a8c8322cda993c), C(1f 90609 a017de1f0), C(9f 3 acea480a41edf)}, {C(9 b9e0126fe4b8b04), C(6 a6190d520886c41), C(69640 b27c16b3ed8), C(18865f f87619fd8f), C(dec5293e665663d8), C(ea07c345872d3201), C(6f ce64da038a17ab), C(18865f f87619fd8f), C(dec5293e665663d8), C(ea07c345872d3201), C(6f ce64da038a17ab), C(ad48f3c826c6a83e), C(70 a1ff080a4da737), C(ecdac686c7d7719), C(700338424 b657470)}, {C(3ec4 b8462b36df47), C(ff8de4a1cbdb7e37), C(4ed e0449884716ac), C(b5f630ac75a8ce03), C(7 cf71ae74fa8566a), C(e068f2b4618df5d), C(369df 952 ad3fd0b8), C(b5f630ac75a8ce03), C(7 cf71ae74fa8566a), C(e068f2b4618df5d), C(369df 952 ad3fd0b8), C(5e1 ba38fea018eb6), C(5ea5 edce48e3da30), C(9 b3490c941069dcb), C(e17854a44cc2fff)}, {C(5e3f d9298fe7009f), C(d2058a44222d5a1d), C(cc25df39bfeb005c), C(1 b0118c5c60a99c7), C(6 ae919ef932301b8), C(cde25defa089c2fc), C(c2a3776e3a7716c4), C(1 b0118c5c60a99c7), C(6 ae919ef932301b8), C(cde25defa089c2fc), C(c2a3776e3a7716c4), C(2557 bf65fb26269e), C(b2edabba58f2ae4f), C(264144e9f 0e632 cb), C(ad6481273c979566)}, {C(7504ec b4727b274e), C(f698cfed6bc11829), C(71 b62c425ecd348e), C(2 a5e555fd35627db), C(55 d5da439c42f3b8), C(a758e451732a1c6f), C(18 caa6b46664b484), C(2 a5e555fd35627db), C(55 d5da439c42f3b8), C(a758e451732a1c6f), C(18 caa6b46664b484), C(6ec1 c7d1524bbad7), C(1 cc3531dc422529d), C(61 a6eeb29c0e5110), C(9 cc8652016784a6a)}, {C(4 bdedc104d5eaed5), C(531 c4bb4fd721e5d), C(1 d860834e94a219f), C(1944ec723253392 b), C(7ea6 aa6a2f278ea5), C(5f f786af8113b3d5), C(194832eb9 b0b8d0f), C(1944ec723253392 b), C(7ea6 aa6a2f278ea5), C(5f f786af8113b3d5), C(194832eb9 b0b8d0f), C(56 ab0396ed73fd38), C(2 c88725b3dfbf89d), C(7f f57adf6275c816), C(b32f7630bcdb218)}, {C(da0b4a6fb26a4748), C(8 a3165320ae1af74), C(4803664ee3 d61d09), C(81 d90ddff0d00fdb), C(2 c8c7ce1173b5c77), C(18 c6b6c8d3f91dfb), C(415 d5cbbf7d9f717), C(81 d90ddff0d00fdb), C(2 c8c7ce1173b5c77), C(18 c6b6c8d3f91dfb), C(415 d5cbbf7d9f717), C(b683e956f1eb3235), C(43166dd e2b64d11f), C(f9689c90f5aad771), C(ca0ebc253c2eec38)}, {C(bad6dd64d1b18672), C(6 d4c4b91c68bd23f), C(d8f1507176822db7), C(381068e0f 65f 708 b), C(b4f3762e451b12a6), C(6 d61ed2f6d4e741), C(8 b3b9df537b91a2c), C(381068e0f 65f 708 b), C(b4f3762e451b12a6), C(6 d61ed2f6d4e741), C(8 b3b9df537b91a2c), C(b0759e599a91575c), C(9e7 adbcc77212239), C(cf0eba98436555fe), C(b1fcc9c42c4cd1e6)}, {C(98 da3fe388d5860e), C(14 a9fda8b3adb103), C(d85f5f798637994b), C(6e8 e8ff107799274), C(24 a2ef180891b531), C(c0eaf33a074bcb9d), C(1f a399a82974e17e), C(6e8 e8ff107799274), C(24 a2ef180891b531), C(c0eaf33a074bcb9d), C(1f a399a82974e17e), C(e7c116bef933725d), C(859908 c7d17b93de), C(f6cfa27113af4a72), C(edf41c5d83c721a8)}, {C(ef243a576431d7ac), C(92 a32619ecfae0a5), C(fb34d2c062dc803a), C(f5f8b21ec30bd3a0), C(80 a442fd5c6482a8), C(4f de11e5ccde5169), C(55671451f 661 a885), C(f5f8b21ec30bd3a0), C(80 a442fd5c6482a8), C(4f de11e5ccde5169), C(55671451f 661 a885), C(94f 27 bc2d5d8d63e), C(2156968 b87f084dc), C(b591bcae146f6fea), C(f57f4c01e41ac7fe)}, {C(97854 de6f22c97b6), C(1292 ac07b0f426bb), C(9 a099a28b22d3a38), C(caac64f5865d87f3), C(771 b9fdbd3aa4bd2), C(88446393 c3606c2d), C(bc3d3dcd5b7d6d7f), C(caac64f5865d87f3), C(771 b9fdbd3aa4bd2), C(88446393 c3606c2d), C(bc3d3dcd5b7d6d7f), C(56e22512 b832d3ee), C(bbc677fe5ce0b665), C(f1914b0f070e5c32), C(c10d40362472dcd1)}, {C(d26ce17bfc1851d), C(db30fb632c7da294), C(26 cb7b1a465400a5), C(401 a0581221957e2), C(fc04e99ae3a283ce), C(fe895303ab2d1e3e), C(35 ab7c498403975b), C(401 a0581221957e2), C(fc04e99ae3a283ce), C(fe895303ab2d1e3e), C(35 ab7c498403975b), C(c6e4c8dc6f52fb11), C(63f 0 b484c2c7502f), C(93693 da3439bdbe9), C(1264 dbaaaaf6b7f1)}, {C(97477 bac0ba4c7f1), C(788ef8729 dca29ac), C(63 d88e226d36132c), C(330 b7e93663affbd), C(3 c59913fcf0d603f), C(e207e6572672fd0a), C(8 a5dc17019c8a667), C(330 b7e93663affbd), C(3 c59913fcf0d603f), C(e207e6572672fd0a), C(8 a5dc17019c8a667), C(5 c8f47ade659d40), C(6e0838 e5a808e9a2), C(8 a2d9a0afcd48b19), C(d1c9d5af7b48418d)}, {C(f6bbcba92b11f5c8), C(72 cf221cad20f191), C(a04726593764122d), C(77f bb70409d316e2), C(c864432c5208e583), C(d3f593922668c184), C(23307562648 bdb54), C(77f bb70409d316e2), C(c864432c5208e583), C(d3f593922668c184), C(23307562648 bdb54), C(b03e0b274f848a74), C(c6121e3af71f4281), C(2e48dd 2 a16ca63ec), C(f4cd44c69ae024df)}, {C(1 ac8b67c1c82132), C(7536 db9591be9471), C(42f 18f be7141e565), C(20085827 a39ff749), C(42e6 c504df174606), C(839 da16331fea7ac), C(7f d768552b10ffc6), C(20085827 a39ff749), C(42e6 c504df174606), C(839 da16331fea7ac), C(7f d768552b10ffc6), C(d1c53c90fde72640), C(c61ae7cf4e266556), C(127561e440 e4c156), C(f329cae8c26af3e1)}, {C(9 cd716ca0eee52fa), C(67 c1076e1ef11f93), C(927342024f 36f 5 d7), C(d0884af223fd056b), C(bb33aafc7b80b3e4), C(36 b722fea81a4c88), C(6e72 e3022c0ed97), C(d0884af223fd056b), C(bb33aafc7b80b3e4), C(36 b722fea81a4c88), C(6e72 e3022c0ed97), C(5 db446a3ba66e0ba), C(2e138f b81b28ad9), C(16e8 e82995237c85), C(9730 dbfb072fbf03)}, {C(1909f 39123 d9ad44), C(c0bdd71c5641fdb7), C(112e5 d19abda9b14), C(984 cf3f611546e28), C(d7d9c9c4e7efb5d7), C(b3152c389532b329), C(1 c168b512ec5f659), C(984 cf3f611546e28), C(d7d9c9c4e7efb5d7), C(b3152c389532b329), C(1 c168b512ec5f659), C(eca67cc49e26069a), C(73 cb0b224d36d541), C(df8379190ae6c5fe), C(e0f6bde7c4726211)}, {C(1 d206f99f535efeb), C(882e15548 afc3422), C(c94f203775c8c634), C(24940 a3adac420b8), C(5 adf73051c52bce0), C(1 aa5030247ed3d32), C(e1ae74ab6804c08b), C(24940 a3adac420b8), C(5 adf73051c52bce0), C(1 aa5030247ed3d32), C(e1ae74ab6804c08b), C(95217 bf71b0da84c), C(ca9bb91c0126a36e), C(741 b9a99ea470974), C(2 adc4e34b8670f41)}, {C(b38c3a83042eb802), C(ea134be7c6e0c326), C(81 d396c683df4f35), C(2 a55645640911e27), C(4f ac2eefbd36e26f), C(79 ad798fb4c5835c), C(359 aa2faec050131), C(2 a55645640911e27), C(4f ac2eefbd36e26f), C(79 ad798fb4c5835c), C(359 aa2faec050131), C(5 b802dcec21a7157), C(6ec de915b75ede0a), C(f2e653587e89058b), C(a661be80528d3385)}, {C(488 d6b45d927161b), C(f5cac66d869a8aaf), C(c326d56c643a214e), C(10 a7228693eb083e), C(1054f b19cbacf01c), C(a8f389d24587ebd8), C(afcb783a39926dba), C(10 a7228693eb083e), C(1054f b19cbacf01c), C(a8f389d24587ebd8), C(afcb783a39926dba), C(fe83e658532edf8f), C(6f dcf97f147dc4db), C(dc5e487845abef4b), C(137693f 4ea b77e27)}, {C(3 d6aaa43af5d4f86), C(44 c7d370910418d8), C(d099515f7c5c4eca), C(39756960441f be2f), C(fb68e5fedbe3d874), C(3f f380fbdd27b8e), C(f48832fdda648998), C(39756960441f be2f), C(fb68e5fedbe3d874), C(3f f380fbdd27b8e), C(f48832fdda648998), C(270dd bf2327058c9), C(9ee ad83a8319d0c4), C(b4c3356e162b086d), C(88f 013588f 411 b7)}, {C(e5c40a6381e43845), C(312 a18e66bbceaa3), C(31365186 c2059563), C(cba4c10e65410ba0), C(3 c250c8b2d72c1b6), C(177e82f 415595117), C(8 c8dcfb9e73d3f6), C(cba4c10e65410ba0), C(3 c250c8b2d72c1b6), C(177e82f 415595117), C(8 c8dcfb9e73d3f6), C(c017a797e49c0f7), C(ea2b233b2e7d5aea), C(878 d204c55a56cb1), C(7 b1b62cc0dfdc523)}, {C(86f b323e5a4b710b), C(710 c1092c23a79e0), C(bd2c6d3fc949402e), C(951f 2078 aa4b8099), C(e68b7fefa1cfd190), C(41525 a4990ba6d4a), C(c373552ef4b51712), C(951f 2078 aa4b8099), C(e68b7fefa1cfd190), C(41525 a4990ba6d4a), C(c373552ef4b51712), C(73eb44 c6122bdf5a), C(58047289 a314b013), C(e31d30432521705b), C(6 cf856774873faa4)}, {C(7930 c09adaf6e62e), C(f230d3311593662c), C(a795b9bf6c37d211), C(b57ec44bc7101b96), C(6 cb710e77767a25a), C(2f 446152 d5e3a6d0), C(cd69172f94543ce3), C(b57ec44bc7101b96), C(6 cb710e77767a25a), C(2f 446152 d5e3a6d0), C(cd69172f94543ce3), C(e6c2483cf425f072), C(2060 d5d4379d6d5a), C(86 a3c04c2110d893), C(561 d3b8a509313c6)}, {C(e505e86f0eff4ecd), C(cf31e1ccb273b9e6), C(d8efb8e9d0fe575), C(ed094f47671e359d), C(d9ebdb047d57611a), C(1 c620e4d301037a3), C(df6f401c172f68e8), C(ed094f47671e359d), C(d9ebdb047d57611a), C(1 c620e4d301037a3), C(df6f401c172f68e8), C(af0a2c7f72388ec7), C(6 d4c4a087fa4564a), C(411 b30def69700a), C(67e5 c84557a47e01)}, {C(dedccb12011e857), C(d831f899174feda8), C(ee4bcdb5804c582a), C(5 d765af4e88f3277), C(d2abe1c63ad4d103), C(342 a8ce0bc7af6e4), C(31 bfda956f3e5058), C(5 d765af4e88f3277), C(d2abe1c63ad4d103), C(342 a8ce0bc7af6e4), C(31 bfda956f3e5058), C(4 c7a1fec9af54bbb), C(84 a88f0655899bf4), C(66f b60d0582ac601), C(be0dd1ffe967bd4a)}, {C(4 d679bda26f5555f), C(7 deb387eb7823c1c), C(a65ef3b4fecd6888), C(a6814d3dc578b9df), C(3372111 a3292b691), C(e97589c81d92b513), C(74ed d943d1b9b5bf), C(a6814d3dc578b9df), C(3372111 a3292b691), C(e97589c81d92b513), C(74ed d943d1b9b5bf), C(889e38 b0af80bb7a), C(a416349af3c5818b), C(f5f5bb25576221c1), C(3 be023fa6912c32e)}, {C(e47cd22995a75a51), C(3686350 c2569a162), C(861 afcb185b8efd9), C(63672 de7951e1853), C(3 ca0c763273b99db), C(29e04f a994cccb98), C(b02587d792be5ee8), C(63672 de7951e1853), C(3 ca0c763273b99db), C(29e04f a994cccb98), C(b02587d792be5ee8), C(c85ada4858f7e4fc), C(3f 280 ab7d5864460), C(4109822f 92f 68326), C(2 d73f61314a2f630)}, {C(92 ba8e12e0204f05), C(4e29321580273802), C(aa83b675ed74a851), C(a16cd2e8b445a3fd), C(f0d4f9fb613c38ef), C(eee7755d444d8f2f), C(b530591eb67ae30d), C(a16cd2e8b445a3fd), C(f0d4f9fb613c38ef), C(eee7755d444d8f2f), C(b530591eb67ae30d), C(6f b3031a6edf8fec), C(65118 d08aecf56d8), C(9 a2117bbef1faa8), C(97055 c5fd310aa93)}, {C(bb3a8427c64f8939), C(b5902af2ec095a04), C(89f 1 b440667b2a28), C(5386ef0 b438d0330), C(d39e03c686f8a2da), C(9555249 bb9073d78), C(8 c0b3623fdf0b156), C(5386ef0 b438d0330), C(d39e03c686f8a2da), C(9555249 bb9073d78), C(8 c0b3623fdf0b156), C(354f c5d3a5504e5e), C(b2fd7391719aa614), C(13 cd4ce3dfe27b3d), C(a2d63a85dc3cae4b)}, {C(998988f 7 d6dacc43), C(5f 2 b853d841152db), C(d76321badc5cb978), C(e381f24ee1d9a97d), C(7 c5d95b2a3af2e08), C(ca714acc461cdc93), C(1 a8ee94bc847aa3e), C(e381f24ee1d9a97d), C(7 c5d95b2a3af2e08), C(ca714acc461cdc93), C(1 a8ee94bc847aa3e), C(ee59ee4c21a36f47), C(d476e8bba5bf5143), C(22 a03cb5900f6ec8), C(19 d954e14f35d7a8)}, {C(3f 1049221dd 72 b98), C(8 d9200d7a0664c37), C(3925704 c83a5f406), C(4 cbef49086e62678), C(d77dfecc2819ef19), C(c327e4deaf4c7e72), C(b4d58c73a262a32d), C(4 cbef49086e62678), C(d77dfecc2819ef19), C(c327e4deaf4c7e72), C(b4d58c73a262a32d), C(78 cd002324861653), C(7 c3f3977576efb88), C(d1c9975fd4a4cc26), C(3e3 cbc90a9baa442)}, {C(419e4f f78c3e06f3), C(aa8ff514c8a141d7), C(5 bb176e21f89f10d), C(becb065dc12d8b4e), C(ebee135492a2018), C(d3f07e65bcd9e13a), C(85 c933e85382e9f9), C(becb065dc12d8b4e), C(ebee135492a2018), C(d3f07e65bcd9e13a), C(85 c933e85382e9f9), C(2 c19ab7c419ebaca), C(982375 b2999bdb46), C(652 ca1c6325d9296), C(e9c790fa8561940a)}, {C(9 ba090af14171317), C(b0445c5232d7be53), C(72 cc929d1577ddb8), C(bc944c1b5ba2184d), C(ab3d57e5e60e9714), C(5 d8d27e7dd0a365a), C(4dd 809e11740 af1a), C(bc944c1b5ba2184d), C(ab3d57e5e60e9714), C(5 d8d27e7dd0a365a), C(4dd 809e11740 af1a), C(6f 42 d856faad44df), C(5118 dc58d7eaf56e), C(829 bbc076a43004), C(1747f bbfaca6da98)}, {C(6 ad739e4ada9a340), C(2 c6c4fb3a2e9b614), C(ab58620e94ca8a77), C(aaa144fbe3e6fda2), C(52 a9291d1e212bc5), C(2 b4c68291f26b570), C(45351 ab332855267), C(aaa144fbe3e6fda2), C(52 a9291d1e212bc5), C(2 b4c68291f26b570), C(45351 ab332855267), C(1149f 55400 bc9799), C(8 c6ec1a0c617771f), C(e9966cc03f3bec05), C(3e6889140 ccd2646)}, {C(8ecf f07fd67e4abd), C(f1b8029b17006ece), C(21 d96d5859229a61), C(b8c18d66154ac51), C(5807350371 ad7388), C(81f 783f 4f 5 ab2b8), C(fa4e659f90744de7), C(b8c18d66154ac51), C(5807350371 ad7388), C(81f 783f 4f 5 ab2b8), C(fa4e659f90744de7), C(809 da4baa51cad2c), C(88 d5c11ff5598342), C(7 c7125b0681d67d0), C(1 b5ba6124bfed8e8)}, {C(497 ca8dbfee8b3a7), C(58 c708155d70e20e), C(90428 a7e349d6949), C(b744f5056e74ca86), C(88 aa27b96f3d84a5), C(b4b1ee0470ac3826), C(aeb46264f4e15d4f), C(b744f5056e74ca86), C(88 aa27b96f3d84a5), C(b4b1ee0470ac3826), C(aeb46264f4e15d4f), C(14921 b1ee856bc55), C(a341d74aaba00a02), C(4f 50 aa8e3d08a919), C(75 a148668ff3869e)}, {C(a929cd66daa65b0a), C(7 c0150a2d9ca564d), C(46dd ec37e2ec0a6d), C(4323852 cc57e4af3), C(1f 5f 638 bbf9d2e5b), C(578f b6ac89a31d9), C(7792536 d9ac4bf12), C(4323852 cc57e4af3), C(1f 5f 638 bbf9d2e5b), C(578f b6ac89a31d9), C(7792536 d9ac4bf12), C(60 be62e795ef5798), C(c276cc5b44febefe), C(519 ba0b9f6d1be95), C(1f dce3561ed35bb8)}, {C(4107 c4156bc8d4bc), C(1 cda0c6f3f0f48af), C(cf11a23299cf7181), C(766 b71bff7d6f461), C(b004f2c910a6659e), C(4 c0eb3848e1a7c8), C(3f 90439 d05c3563b), C(766 b71bff7d6f461), C(b004f2c910a6659e), C(4 c0eb3848e1a7c8), C(3f 90439 d05c3563b), C(4 a2a013f4bc2c1d7), C(888779 ab0c272548), C(ae0f8462d89a4241), C(c5c85b7c44679abd)}, {C(15 b38dc0e40459d1), C(344f edcfc00fff43), C(b9215c5a0fcf17df), C(d178444a236c1f2d), C(5576 deee27f3f103), C(943611 bb5b1b0736), C(a0fde17cb5c2316d), C(d178444a236c1f2d), C(5576 deee27f3f103), C(943611 bb5b1b0736), C(a0fde17cb5c2316d), C(feaa1a047f4375f3), C(5435f 313e84767 e), C(522e4333 cd0330c1), C(7e6 b609b0ea9e91f)}, {C(e5e5370ed3186f6c), C(4592e75 db47ea35d), C(355 d452b82250e83), C(7 a265e37da616168), C(6 a1f06c34bafa27), C(fbae175e7ed22a9c), C(b144e84f6f33c098), C(7 a265e37da616168), C(6 a1f06c34bafa27), C(fbae175e7ed22a9c), C(b144e84f6f33c098), C(bd444561b0db41fc), C(2072 c85731e7b0b0), C(ce1b1fac436b51f3), C(4f 5 d44f31a3dcdb9)}, {C(ea2785c8f873e28f), C(3e257272f 4464f 5f), C(9267e7 e0cc9c7fb5), C(9f d4d9362494cbbc), C(e562bc615befb1b9), C(8096808 d8646cfde), C(c4084a587b9776ec), C(9f d4d9362494cbbc), C(e562bc615befb1b9), C(8096808 d8646cfde), C(c4084a587b9776ec), C(a9135db8a850d8e4), C(fffc4f8b1a11f5af), C(c50e9173c2c6fe64), C(a32630581df4ceda)}, {C(e7bf98235fc8a4a8), C(4042ef2 aae400e64), C(6538 ba9ffe72dd70), C(c84bb7b3881ab070), C(36f e6c51023fbda0), C(d62838514bb87ea4), C(9ee b5e7934373d86), C(c84bb7b3881ab070), C(36f e6c51023fbda0), C(d62838514bb87ea4), C(9ee b5e7934373d86), C(5f 8480 d0a2750a96), C(40 afa38506456ad9), C(e4012b7ef2e0ddea), C(659 da200a011836b)}, {C(b94e261a90888396), C(1f 468 d07e853294c), C(cb2c9b863a5317b9), C(4473 c8e2a3458ee0), C(258053945 ab4a39a), C(f8d745ca41962817), C(7 afb6d40df9b8f71), C(4473 c8e2a3458ee0), C(258053945 ab4a39a), C(f8d745ca41962817), C(7 afb6d40df9b8f71), C(9030 c2349604f677), C(f544dcd593087faf), C(77 a3b0efe6345d12), C(fff4e398c05817cc)}, {C(4 b0226e5f5cdc9c), C(a836ae7303dc4301), C(8505e1 b628bac101), C(b5f52041a698da7), C(29864874 b5f1936d), C(49 b3a0c6d78f98da), C(93 a1a8c7d90de296), C(b5f52041a698da7), C(29864874 b5f1936d), C(49 b3a0c6d78f98da), C(93 a1a8c7d90de296), C(ed62288423c17b7f), C(685 afa2cfba09660), C(6 d9b6f59585452c6), C(e505535c4010efb9)}, {C(e07edbe7325c718c), C(9 db1eda964f06827), C(2f 245 ad774e4cb1b), C(664ec3f ad8521859), C(406f 082 beb9ca29a), C(b6b0fb3a7981c7c8), C(3eb d280b598a9721), C(664ec3f ad8521859), C(406f 082 beb9ca29a), C(b6b0fb3a7981c7c8), C(3eb d280b598a9721), C(d9a6ceb072eab22a), C(d5bc5df5eb2ff6f1), C(488 db3cab48daa0b), C(9916f 14f a5672f37)}, {C(f4b56421eae4c4e7), C(5 da0070cf40937a0), C(aca4a5e01295984a), C(5414e385f 5677 a6d), C(41ef105f 8 a682a28), C(4 cd2e95ea7f5e7b0), C(775 bb1e0d57053b2), C(5414e385f 5677 a6d), C(41ef105f 8 a682a28), C(4 cd2e95ea7f5e7b0), C(775 bb1e0d57053b2), C(8919017805e84 b3f), C(15402f 44e0 e2b259), C(483 b1309e1403c87), C(85 c7b4232d45b0d9)}, {C(c07fcb8ae7b4e480), C(4eb cad82e0b53976), C(8643 c63d6c78a6ce), C(d4bd358fed3e6aa5), C(8 a1ba396356197d9), C(7 afc2a54733922cc), C(b813bdac4c7c02ef), C(d4bd358fed3e6aa5), C(8 a1ba396356197d9), C(7 afc2a54733922cc), C(b813bdac4c7c02ef), C(f6c610cf7e7c955), C(dab6a53e1c0780f8), C(837 c9ffec33e5d48), C(8 cb8c20032af152d)}, {C(3ed ad9568a9aaab), C(23891 bbaeb3a17bc), C(4eb7238738 b0c51a), C(db0c32f76f5b7fc1), C(5e41 b711f0abd1a0), C(bcb758f01ded0a11), C(7 d15f7d87955e28b), C(db0c32f76f5b7fc1), C(5e41 b711f0abd1a0), C(bcb758f01ded0a11), C(7 d15f7d87955e28b), C(cd2dc1f0b05939b), C(9f d6d680462e4c47), C(95 d5846e993bc8ff), C(f0b3cafc2697b8a8)}, {C(fcabde8700de91e8), C(63784 d19c60bf366), C(8f 3 af9a056b1a1c8), C(32 d3a29cf49e2dc9), C(3079 c0b0c2269bd0), C(ed76ba44f04e7b82), C(6ee e76a90b83035f), C(32 d3a29cf49e2dc9), C(3079 c0b0c2269bd0), C(ed76ba44f04e7b82), C(6ee e76a90b83035f), C(4 a9286f545bbc09), C(bd36525be4dd1b51), C(5f 7 a9117228fdee5), C(543 c96a08f03151c)}, {C(362f c5ba93e8eb31), C(7549 ae99fa609d61), C(47e4 cf524e37178f), C(a54eaa5d7f3a7227), C(9 d26922965d54727), C(27 d22acb31a194d4), C(e9b8e68771db0da6), C(a54eaa5d7f3a7227), C(9 d26922965d54727), C(27 d22acb31a194d4), C(e9b8e68771db0da6), C(16f d0e006209abe8), C(81 d3f72987a6a81a), C(74e96 e4044817bc7), C(924 ca5f08572fef9)}, {C(e323b1c5b55a4dfb), C(719993 d7d1ad77fb), C(555 ca6c6166e989c), C(ea37f61c0c2f6d53), C(9 b0c2174f14a01f5), C(7 bbe6921e26293f3), C(2 ab6c72235b6c98a), C(ea37f61c0c2f6d53), C(9 b0c2174f14a01f5), C(7 bbe6921e26293f3), C(2 ab6c72235b6c98a), C(2 c6e7668f37f6d23), C(3e8 edb057a57c2dd), C(2595f c79768c8b34), C(ffc541f5efed9c43)}, {C(9461913 a153530ef), C(83f c6d9ed7d1285a), C(73df 90 bdc50807cf), C(a32c192f6e3c3f66), C(8f 10077 b8a902d00), C(61 a227f2faac29b4), C(1 a71466fc005a61d), C(a32c192f6e3c3f66), C(8f 10077 b8a902d00), C(61 a227f2faac29b4), C(1 a71466fc005a61d), C(12545812f 3 d01a92), C(aece72f823ade07d), C(52634 cdd5f9e5260), C(cb48f56805c08e98)}, {C(ec2332acc6df0c41), C(59f 5ee17 e20a8263), C(1087 d756afcd8e7b), C(a82a7bb790678fc9), C(d197682c421e4373), C(dd78d25c7f0f935a), C(9850 cb6fbfee520f), C(a82a7bb790678fc9), C(d197682c421e4373), C(dd78d25c7f0f935a), C(9850 cb6fbfee520f), C(2590847398688 a46), C(ad266f08713ca5fe), C(25 b978be91e830b5), C(2996 c8f2b4c8f231)}, {C(aae00b3a289bc82), C(4f 6 d69f5a5a5b659), C(3f f5abc145614e3), C(33322363 b5f45216), C(7e83f 1f e4189e843), C(df384b2adfc35b03), C(396 ce7790a5ada53), C(33322363 b5f45216), C(7e83f 1f e4189e843), C(df384b2adfc35b03), C(396 ce7790a5ada53), C(c3286e44108b8d36), C(6 db8716c498d703f), C(d1db09466f37f4e7), C(56 c98e7f68a41388)}, {C(4 c842e732fcd25f), C(e7dd7b953cf9c2b2), C(911ee248 a76ae3), C(33 c6690937582317), C(fe6d61a77985d7bb), C(97 b153d04a115535), C(d3fde02e42cfe6df), C(33 c6690937582317), C(fe6d61a77985d7bb), C(97 b153d04a115535), C(d3fde02e42cfe6df), C(d1c7d1efa52a016), C(1 d6ed137f4634c), C(1 a260ec505097081), C(8 d1e70861a1c7db6)}, {C(40e23 ca5817a91f3), C(353e2935809 b7ad1), C(f7820021b86391bb), C(f3d41b3d4717eb83), C(2670 d457dde68842), C(19707 a6732c49278), C(5 d0f05a83569ba26), C(f3d41b3d4717eb83), C(2670 d457dde68842), C(19707 a6732c49278), C(5 d0f05a83569ba26), C(6f e5bc84e528816a), C(94df 3 dca91a29ace), C(420196ed097 e8b6f), C(7 c52da0e1f043ad6)}, {C(2564527f ad710b8d), C(2 bdcca8d57f890f), C(81f 7 bfcd9ea5a532), C(dd70e407984cfa80), C(66996 d6066db6e1a), C(36 a812bc418b97c9), C(18ea2 c63da57f36e), C(dd70e407984cfa80), C(66996 d6066db6e1a), C(36 a812bc418b97c9), C(18ea2 c63da57f36e), C(937f d7ad09be1a8f), C(163 b12cab35d5d15), C(3606 c3e441335cce), C(949f 6ea5 bb241ae8)}, {C(6 bf70df9d15a2bf6), C(81 cad17764b8e0dd), C(58 b349a9ba22a7ef), C(9432536dd 9f 65229), C(192 dc54522da3e3d), C(274 c6019e0227ca9), C(160 abc932a4e4f35), C(9432536dd 9f 65229), C(192 dc54522da3e3d), C(274 c6019e0227ca9), C(160 abc932a4e4f35), C(1204f 2f b5aa79dc6), C(2536ed af890f0760), C(6f 2 b561f44ff46b4), C(8 c7b3e95baa8d984)}, {C(45e6f 446eb6 bbcf5), C(98 ab0ef06f1a7d84), C(85 ae96bacca50de6), C(b9aa5bead3352801), C(8 a6d9e02a19a4229), C(c352f5b6d5ee1d9d), C(ce562bdb0cfa84fb), C(b9aa5bead3352801), C(8 a6d9e02a19a4229), C(c352f5b6d5ee1d9d), C(ce562bdb0cfa84fb), C(d47b768a85283981), C(1f e72557be57a11b), C(95 d8afe4af087d51), C(2f 59 c4e383f30045)}, {C(620 d3fe4b8849c9e), C(975 a15812a429ec2), C(437 c453593dcaf13), C(8 d8e7c63385df78e), C(16 d55add72a5e25e), C(aa6321421dd87eb5), C(6f 27f 62e785f 0203), C(8 d8e7c63385df78e), C(16 d55add72a5e25e), C(aa6321421dd87eb5), C(6f 27f 62e785f 0203), C(829030 a61078206e), C(ae1f30fcfa445cc8), C(f61f21c9df4ef68d), C(1e5 b1945f858dc4c)}, {C(535 aa7340b3c168f), C(bed5d3c3cd87d48a), C(266 d40ae10f0cbc1), C(ce218d5b44f7825a), C(2 ae0c64765800d3a), C(f22dc1ae0728fc01), C(48 a171bc666d227f), C(ce218d5b44f7825a), C(2 ae0c64765800d3a), C(f22dc1ae0728fc01), C(48 a171bc666d227f), C(e7367aff24203c97), C(da39d2be1db3a58d), C(85 ce86523003933a), C(dfd4ef2ae83f138a)}, {C(dd3e761d4eada300), C(893 d7e4c3bea5bb6), C(cc6d6783bf43eea), C(eb8eed7c391f0044), C(b58961c3abf80753), C(3 d75ea687191521), C(389 be7bbd8e478f3), C(eb8eed7c391f0044), C(b58961c3abf80753), C(3 d75ea687191521), C(389 be7bbd8e478f3), C(917070 a07441ee47), C(d78efa8cd65b313), C(a8a16f4c1c08c8a1), C(b69cb8ee549eb113)}, {C(4 ac1902ccde06545), C(2 c44aeb0983a7a07), C(b566035215b309f9), C(64 c136fe9404a7b3), C(99f 3 d8c98a399d5e), C(6319 c7cb14180185), C(fbacdbd277d33f4c), C(64 c136fe9404a7b3), C(99f 3 d8c98a399d5e), C(6319 c7cb14180185), C(fbacdbd277d33f4c), C(a96a5626c2adda86), C(39ea72f d2ad133ed), C(b5583f2f736df73e), C(ef2c63619782b7ba)}, {C(aee339a23bb00a5e), C(cbb402255318f919), C(9922948e99 aa0781), C(df367034233fedc4), C(dcbe14db816586e5), C(f4b1cb814adf21d3), C(f4690695102fa00a), C(df367034233fedc4), C(dcbe14db816586e5), C(f4b1cb814adf21d3), C(f4690695102fa00a), C(6 b4f01dd6b76dafc), C(b79388676b50da5d), C(cb64f8bde5ed3393), C(9 b422781f13219d3)}, {C(627599e91148df 4f), C(3e2 d01e8baab062b), C(2 daab20edb245251), C(9 a958bc3a895a223), C(331058dd 6 c5d2064), C(46 c4d962072094fa), C(e6207c19160e58eb), C(9 a958bc3a895a223), C(331058dd 6 c5d2064), C(46 c4d962072094fa), C(e6207c19160e58eb), C(5655e4 dbf7272728), C(67 b217b1f56c747d), C(3 ac0be79691b9a0d), C(9 d0954dd0b57073)}, {C(cfb04cf00cfed6b3), C(5f e75fc559af22fa), C(c440a935d72cdc40), C(3 ab0d0691b251b8b), C(47181 a443504a819), C(9 bcaf1253f99f499), C(8ee002 b89c1b6b3f), C(3 ab0d0691b251b8b), C(47181 a443504a819), C(9 bcaf1253f99f499), C(8ee002 b89c1b6b3f), C(55df e8eedcd1ec5e), C(1 bf50f0bbad796a5), C(9044369 a042d7fd6), C(d423df3e3738ba8f)}, {C(942631 c47a26889), C(427962 c82d8a6e00), C(224071 a6592537ff), C(d3e96f4fb479401), C(68 b3f2ec11de9368), C(cb51b01083acad4f), C(500 cec4564d62aeb), C(d3e96f4fb479401), C(68 b3f2ec11de9368), C(cb51b01083acad4f), C(500 cec4564d62aeb), C(4 ce547491e732887), C(9423883 a9a11df4c), C(1 a0fc7a14214360), C(9e837914505 da6ed)}, {C(4 c9eb4e09726b47e), C(fd927483a2b38cf3), C(6 d7e56407d1ba870), C(9f 5 dc7db69fa1e29), C(f42fff56934533d5), C(92 d768c230a53918), C(f3360ff11642136c), C(9f 5 dc7db69fa1e29), C(f42fff56934533d5), C(92 d768c230a53918), C(f3360ff11642136c), C(9e989932 eb86d1b5), C(449 a77f69a8a9d65), C(efabaf8a7789ed9a), C(2798eb4 c50c826fd)}, {C(cf7f208ef20e887a), C(f4ce4edeadcaf1a1), C(7ee15226 eaf4a74d), C(17 ab41ab2ae0705d), C(9dd 56694 aa2dcd4e), C(dd4fa2add9baced2), C(7 ad99099c9e199a3), C(17 ab41ab2ae0705d), C(9dd 56694 aa2dcd4e), C(dd4fa2add9baced2), C(7 ad99099c9e199a3), C(a59112144accef0e), C(5838df 47e38 d251d), C(8750f e45760331e5), C(4 b2ce14732e0312a)}, {C(a8dc4687bcf27f4), C(c4aadd7802553f15), C(5401eb9912 be5269), C(5 c2a2b5b0657a928), C(1e1968 ebb38fcb99), C(a082d0e067c4a59c), C(18 b616495ad9bf5d), C(5 c2a2b5b0657a928), C(1e1968 ebb38fcb99), C(a082d0e067c4a59c), C(18 b616495ad9bf5d), C(18 c5dc6c78a7f9ed), C(b3cc94fe34b68aa1), C(3 b77e91292be38cc), C(61 d1786ec5097971)}, {C(daed638536ed19df), C(1 a762ea5d7ac6f7e), C(48 a1cc07a798b84f), C(7f 15 bdaf50d550f9), C(4 c1d48aa621a037e), C(2 b1d7a389d497ee0), C(81 c6775d46f4b517), C(7f 15 bdaf50d550f9), C(4 c1d48aa621a037e), C(2 b1d7a389d497ee0), C(81 c6775d46f4b517), C(35296005 cbba3ebe), C(db1642f825b53532), C(3e07588 a9fd829a4), C(60f 13 b5446bc7638)}, {C(90 a04b11ee1e4af3), C(ab09a35f8f2dff95), C(d7cbe82231ae1e83), C(3262e9017 bb788c4), C(1612017731 c997bc), C(e789d66134aff5e1), C(275642f d17048af1), C(3262e9017 bb788c4), C(1612017731 c997bc), C(e789d66134aff5e1), C(275642f d17048af1), C(99255 b68d0b46b51), C(74 a6f1ad4b2bb296), C(4164222761 af840e), C(54 d59bf6211a8fe6)}, {C(511f 29e1 b732617d), C(551 cb47a9a83d769), C(df6f56fbda20e7a), C(f27583a930221d44), C(d7d2c46de69b2ed8), C(add24ddd2be4a850), C(5 cf2f688dbb93585), C(f27583a930221d44), C(d7d2c46de69b2ed8), C(add24ddd2be4a850), C(5 cf2f688dbb93585), C(a7f8e42d5dd4aa00), C(72 dc11fd76b4dea9), C(8886f 194e6f 8e3f f), C(7e8 ead04a0e0b1ef)}, {C(95567f 03939e651f), C(62 a426f09d81d884), C(15 cb96e36a8e712c), C(1 a2f43bdeaea9c28), C(bca2fd840831291f), C(83446 d4a1f7dcc1a), C(449 a211df83b6187), C(1 a2f43bdeaea9c28), C(bca2fd840831291f), C(83446 d4a1f7dcc1a), C(449 a211df83b6187), C(553 ce97832b2f695), C(3110 a2ba303db75), C(b91d6d399a02f453), C(3 cb148561e0ef2bb)}, {C(248 a32ad10e76bc3), C(dac39c8b036985e9), C(79 d38c4af2958b56), C(cc954b4e56275f54), C(700 cd864e04e8aaa), C(d6ba03cbff7cc34b), C(da297d7891c9c046), C(cc954b4e56275f54), C(700 cd864e04e8aaa), C(d6ba03cbff7cc34b), C(da297d7891c9c046), C(c05d2be8f8ee8114), C(7f 4541 cbe2ec0025), C(8f 0 a7a70af6ea926), C(3837dd ce693781b5)}, {C(f9f05a2a892242eb), C(de00b6b2e0998460), C(f1f4bd817041497a), C(3 deac49eb42a1e26), C(642f 77f 7 c57e84b7), C(2f 2 c231222651e8b), C(380202ec06 bdc29e), C(3 deac49eb42a1e26), C(642f 77f 7 c57e84b7), C(2f 2 c231222651e8b), C(380202ec06 bdc29e), C(59 abc4ff54765e66), C(8561ea1dd dd1f742), C(9 ca1f94b0d3f3875), C(b7fa93c3a9fa4ec4)}, {C(3 a015cea8c3f5bdf), C(5583521 b852fc3ac), C(53 d5cd66029a1014), C(ac2eeca7bb04412a), C(daba45cb16ccff2b), C(ddd90b51209e414), C(d90e74ee28cb6271), C(ac2eeca7bb04412a), C(daba45cb16ccff2b), C(ddd90b51209e414), C(d90e74ee28cb6271), C(117027648 ca9db68), C(29 c1dba39bbcf072), C(787f 6 bb010a34cd9), C(e099f487e09b847)}, {C(670e43506 aa1f71b), C(1 cd7929573e54c05), C(cbb00a0aaba5f20a), C(f779909e3d5688d1), C(88211 b9117678271), C(59f 44f 73759 a8bc6), C(ef14f73c405123b4), C(f779909e3d5688d1), C(88211 b9117678271), C(59f 44f 73759 a8bc6), C(ef14f73c405123b4), C(78775601f 11186f), C(fc4641d676fbeed9), C(669 ca96b5a2ae5b), C(67 b5f0d072025f8d)}, {C(977 bb79b58bbd984), C(26 d45cfcfb0e9756), C(df8885db518d5f6a), C(6 a1d2876488bed06), C(ae35d83c3afb5769), C(33667427 d99f9f4e), C(d84c31c17495e3ba), C(6 a1d2876488bed06), C(ae35d83c3afb5769), C(33667427 d99f9f4e), C(d84c31c17495e3ba), C(31357 cded7495ffc), C(295e2 eefcd383a2e), C(25063ef4 a24c29ae), C(88 c694170fcbf0b7)}, {C(e6264fbccd93a530), C(c92f420494e99a7d), C(c14001a298cf976), C(5 c8685fee2e4ce55), C(228 c49268d6a4345), C(3 b04ee2861baec6d), C(7334878 a00e96e72), C(5 c8685fee2e4ce55), C(228 c49268d6a4345), C(3 b04ee2861baec6d), C(7334878 a00e96e72), C(7317164 b2ce711bb), C(e645447e363e8ca1), C(d326d129ad7b4e7f), C(58 b9b76d5c2eb272)}, {C(54e4 d0cab7ec5c27), C(31 ca61d2262a9acc), C(30 bd3a50d8082ff6), C(46 b3b963bf7e2847), C(b319d04e16ad10b0), C(76 c8dd82e6f5a0eb), C(2070363 cefb488bc), C(46 b3b963bf7e2847), C(b319d04e16ad10b0), C(76 c8dd82e6f5a0eb), C(2070363 cefb488bc), C(6f 9 dbacb2bdc556d), C(88 a5fb0b293c1e22), C(cb131d9b9abd84b7), C(21 db6f0e147a0040)}, {C(882 a598e98cf5416), C(36 c8dca4a80d9788), C(c386480f07591cfe), C(5 b517bcf2005fd9c), C(b9b8f8e5f90e7025), C(2 a833e6199e21708), C(bcb7549de5fda812), C(5 b517bcf2005fd9c), C(b9b8f8e5f90e7025), C(2 a833e6199e21708), C(bcb7549de5fda812), C(44f c96a3cafa1c34), C(fb7724d4899ec7c7), C(4662e3 b87df93e13), C(bcf22545acbcfd4e)}, {C(7 c37a5376c056d55), C(e0cce8936a06b6f6), C(d32f933fdbec4c7d), C(7 ac50423e2be4703), C(546 d4b42340d6dc7), C(624f 56ee027f 12 bf), C(5f 7f 65 d1e90c30f9), C(7 ac50423e2be4703), C(546 d4b42340d6dc7), C(624f 56ee027f 12 bf), C(5f 7f 65 d1e90c30f9), C(d6f15c19625d2621), C(c7afd12394f24b50), C(2 c6adde5d249bcd0), C(6 c857e6aa07b9fd2)}, {C(21 c5e9616f24be97), C(ba3536c86e4b6fe9), C(6 d3a65cfe3a9ae06), C(2113903eb d760a31), C(e561f76a5eac8beb), C(86 b5b3e76392e166), C(68 c8004ccc53e049), C(2113903eb d760a31), C(e561f76a5eac8beb), C(86 b5b3e76392e166), C(68 c8004ccc53e049), C(b51a28fe4251dd79), C(fd9c2d4d2a84c3c7), C(5 bf2ec8a470d2553), C(135 a52cdc76241c9)}, {C(a6eaefe74fa7d62b), C(cb34669c751b10eb), C(80 da952ad8abd5f3), C(3368262 b0e172d82), C(1 d51f6c982476285), C(4497675 ac57228a9), C(2 a71766a71d0b83f), C(3368262 b0e172d82), C(1 d51f6c982476285), C(4497675 ac57228a9), C(2 a71766a71d0b83f), C(79 ad94d1e9c1dedd), C(cbf1a1c9f23bfa40), C(3ebf 24e068 cd638b), C(be8e63472edfb462)}, {C(764 af88ed4b0b828), C(36946775f 20457 ce), C(d4bc88ac8281c22e), C(3 b2104d68dd9ac02), C(2ec a14fcdc0892d0), C(7913 b0c09329cd47), C(9373f 458938688 c8), C(3 b2104d68dd9ac02), C(2ec a14fcdc0892d0), C(7913 b0c09329cd47), C(9373f 458938688 c8), C(b4448f52a5bf9425), C(9f 8 c8b90b61ed532), C(78f 6774f 48e72961), C(e47c00bf9c1206f4)}, {C(5f 55 a694fb173ea3), C(7 db02b80ef5a918b), C(d87ff079f476ca3a), C(1 d11117374e0da3), C(744 bfbde42106439), C(93 a99fab10bb1789), C(246 ba292a85d8d7c), C(1 d11117374e0da3), C(744 bfbde42106439), C(93 a99fab10bb1789), C(246 ba292a85d8d7c), C(e5bd7838e9edd53a), C(d9c0b104c79d9019), C(ee3dcc7a8e565de5), C(619 c9e0a9cf3596d)}, {C(86 d086738b0a7701), C(d2402313a4280dda), C(b327aa1a25278366), C(49efdd e5d1f98163), C(cbcffcee90f22824), C(951 aec1daeb79bab), C(7055e2 c70d2eeb4c), C(49efdd e5d1f98163), C(cbcffcee90f22824), C(951 aec1daeb79bab), C(7055e2 c70d2eeb4c), C(1f c0de9399bacb96), C(dab7bbe67901959e), C(375805ec cf683ef0), C(bbb6f465c4bae04e)}, {C(acfc8be97115847b), C(c8f0d887bf8d9d1), C(e698fbc6d39bf837), C(61f d1d6b13c1ea77), C(527ed97f f4ae24f0), C(af51a9ebb322c0), C(14f 7 c25058864825), C(61f d1d6b13c1ea77), C(527ed97f f4ae24f0), C(af51a9ebb322c0), C(14f 7 c25058864825), C(f40b2bbeaf9f021d), C(80 d827160dfdc2d2), C(77 baea2e3650486e), C(5 de2d256740a1a97)}, {C(dc5ad3c016024d4), C(a0235e954da1a152), C(6 daa8a4ed194cc43), C(185e650 afc8d39f8), C(adba03a4d40de998), C(9975 c776b499b26f), C(9770 c59368a43a2), C(185e650 afc8d39f8), C(adba03a4d40de998), C(9975 c776b499b26f), C(9770 c59368a43a2), C(d2776f0cf0e4f66c), C(38ea aabfb743f7f6), C(c066f03d959b3f07), C(9 d91c2d52240d546)}, {C(a0e91182f03277f7), C(15 c6ebef7376556), C(516f 887657 ab5a), C(f95050524c7f4b84), C(460 dcebbaaa09ae3), C(a9f7a9f0b1b2a961), C(5f 8 dc5e198e34539), C(f95050524c7f4b84), C(460 dcebbaaa09ae3), C(a9f7a9f0b1b2a961), C(5f 8 dc5e198e34539), C(9 c49227ffcff07cb), C(a29388e9fcb794c8), C(475867910 d110cba), C(8 c9a5cee480b5bac)}, {C(767f 1 dbd1dba673b), C(1e466 a3848a5b01e), C(483ea def1347cd6e), C(a67645c72f54fe24), C(c7a5562c69bd796b), C(e14201a35b55e4a6), C(b3a6d89f19d8f774), C(a67645c72f54fe24), C(c7a5562c69bd796b), C(e14201a35b55e4a6), C(b3a6d89f19d8f774), C(bb4d607ac22bebe5), C(792030ed eaa924e0), C(138730 dcb60f7e32), C(699 d9dcc326c72dc)}, {C(a5e30221500dcd53), C(3 a1058d71c9fad93), C(510520710 c6444e8), C(a6a5e60c2c1d0108), C(45 c8ea4e14bf8c6b), C(213 a7235416b86df), C(c186072f80d56ad3), C(a6a5e60c2c1d0108), C(45 c8ea4e14bf8c6b), C(213 a7235416b86df), C(c186072f80d56ad3), C(2e7 be098db59d832), C(d5fa382f3717a0ee), C(b168b26921d243d), C(61601 a60c2addfbb)}, {C(ebaed82e48e18ce4), C(cfe6836b65ebe7c7), C(504 d9d388684d449), C(bd9c744ee9e3308e), C(faefbb8d296b65d4), C(eba051fe2404c25f), C(250 c8510b8931f87), C(bd9c744ee9e3308e), C(faefbb8d296b65d4), C(eba051fe2404c25f), C(250 c8510b8931f87), C(3 c4a49150dc5676f), C(6 c28793c565345c4), C(9df 6dd 8829 a6d8fb), C(760 d3a023fab72e7)}, {C(ffa50913362b118d), C(626 d52251a8ec3e0), C(76 ce4b9dde2e8c5e), C(fc57418d92e52355), C(6 b46c559e67a063), C(3f 5 c269e10690c5c), C(6870 de8d49e65349), C(fc57418d92e52355), C(6 b46c559e67a063), C(3f 5 c269e10690c5c), C(6870 de8d49e65349), C(88737e5 c672de296), C(ca71fca5f4c4f1ce), C(42f ca3fa7f60e031), C(4 a70246d0d4c2bd8)}, {C(256186 bcda057f54), C(fb059b012049fd8e), C(304e07418 b5f739b), C(3e166f 9f ac2eec0b), C(82 bc11707ec4a7a4), C(e29acd3851ce36b6), C(9765 ca9323d30046), C(3e166f 9f ac2eec0b), C(82 bc11707ec4a7a4), C(e29acd3851ce36b6), C(9765 ca9323d30046), C(dab63e7790017f7c), C(b9559988bff0f170), C(48 d9ef8aea13eee8), C(e31e47857c511ec2)}, {C(382 b15315e84f28b), C(f9a2578b79590b72), C(708936 af6d4450e8), C(76 a9d4843df75c1c), C(2 c33447da3f2c70a), C(5e4 dcf2eaeace0d6), C(2 ae1727aa7220634), C(76 a9d4843df75c1c), C(2 c33447da3f2c70a), C(5e4 dcf2eaeace0d6), C(2 ae1727aa7220634), C(a122f6b52e1130ba), C(a17ae9a21f345e91), C(ff67313f1d0906a9), C(bb16dc0acd6ebecc)}, {C(9983 a9cc5576d967), C(29e37689 a173109f), C(c526073a91f2808c), C(fe9a9d4a799cf817), C(7 ca841999012c0d1), C(8 b3abfa4bd2aa28e), C(4ed49274526602 eb), C(fe9a9d4a799cf817), C(7 ca841999012c0d1), C(8 b3abfa4bd2aa28e), C(4ed49274526602 eb), C(40995df 99063f e23), C(7f 51 b7ceded05144), C(743 c89732b265bf2), C(10 c8e1fd835713fd)}, {C(c2c58a843f733bdb), C(516 c47c97b4ba886), C(abc3cae0339517db), C(be29af0dad5c9d27), C(70f 802599 d97fe08), C(23 af3f67d941e52b), C(a031edd8b3a008fb), C(be29af0dad5c9d27), C(70f 802599 d97fe08), C(23 af3f67d941e52b), C(a031edd8b3a008fb), C(43431336 b198f8fd), C(7 c4b60284e1c2245), C(51ee580dd abae1b3), C(ca99bd13845d8f7f)}, {C(648f f27fabf93521), C(d7fba33cbc153035), C(3 dbcdcf87ad06c9e), C(52dd bdc9dfd26990), C(d46784cd2aeabb28), C(bd3a15e5e4eb7177), C(b5d7632e19a2cd), C(52dd bdc9dfd26990), C(d46784cd2aeabb28), C(bd3a15e5e4eb7177), C(b5d7632e19a2cd), C(8007450f a355dc04), C(41 ca59f64588bb5c), C(66f 2 ca6b7487499d), C(8098716530 db9bea)}, {C(99 be55475dcb3461), C(d94ffa462f6ba8dc), C(dbab2b456bdf13bb), C(f28f496e15914b2d), C(1171 ce20f49cc87d), C(1 b5f514bc1b377a9), C(8 a02cb12ec4d6397), C(f28f496e15914b2d), C(1171 ce20f49cc87d), C(1 b5f514bc1b377a9), C(8 a02cb12ec4d6397), C(1 c6540740c128d79), C(d085b67114969f41), C(af8c1988085306f3), C(4681f 415 d9ce8038)}, {C(e16fbb9303dd6d92), C(4 d92b99dd164db74), C(3f 98f 2 c9da4f5ce3), C(c65b38c5a47eeed0), C(5 c5301c8ee3923a6), C(51 bf9f9eddec630e), C(b1cbf1a68be455c2), C(c65b38c5a47eeed0), C(5 c5301c8ee3923a6), C(51 bf9f9eddec630e), C(b1cbf1a68be455c2), C(c356f5f98499bdb8), C(d897df1ad63fc1d4), C(9 bf2a3a69982e93a), C(a2380d43e271bcc8)}, {C(4 a57a4899834e4c0), C(836 c4df2aac32257), C(cdb66b29e3e12147), C(c734232cbda1eb4c), C(30 a3cffff6b9dda0), C(d199313e17cca1ed), C(594 d99e4c1360d82), C(c734232cbda1eb4c), C(30 a3cffff6b9dda0), C(d199313e17cca1ed), C(594 d99e4c1360d82), C(ccc37662829a65b7), C(cae30ff4d2343ce9), C(54 da907f7aade4fa), C(5 d6e4a0272958922)}, {C(f658958cdf49f149), C(de8e4a622b7a16b), C(a227ebf448c80415), C(3 de9e38b3a369785), C(84 d160d688c573a9), C(8f 562593 add0ad54), C(4446 b762cc34e6bf), C(3 de9e38b3a369785), C(84 d160d688c573a9), C(8f 562593 add0ad54), C(4446 b762cc34e6bf), C(2f 795f 1594 c7d598), C(29e05 bd1e0dceaff), C(a9a88f2962b49589), C(4 b9c86c141ac120b)}, {C(ae1befc65d3ea04d), C(cfd9bc0388c8fd00), C(522f 2e1f 6 cdb31af), C(585447eb e078801a), C(14 a31676ec4a2cbd), C(b274e7e6af86a5e1), C(2 d487019570bedce), C(585447eb e078801a), C(14 a31676ec4a2cbd), C(b274e7e6af86a5e1), C(2 d487019570bedce), C(ea1dc9ef3c7b2fcc), C(bde99d4af2f4ee8c), C(64e4 c43cd7c43442), C(9 b5262ee2eed2f99)}, {C(2f c8f9fc5946296d), C(6 a2b94c6765ebfa2), C(f4108b8c79662fd8), C(3 a48de4a1e994623), C(6318e6 e1ff7bc092), C(84 aee2ea26a048fb), C(cf3c393fdad7b184), C(3 a48de4a1e994623), C(6318e6 e1ff7bc092), C(84 aee2ea26a048fb), C(cf3c393fdad7b184), C(28 b265bd8985a71e), C(bd3d97dbd76d89a5), C(b04ba1623c0937d), C(b6de821229693515)}, {C(efdb4dc26e84dce4), C(9 ce45b6172dffee8), C(c15ad8c8bcaced19), C(f10cc2bcf0475411), C(1126f 457 c160d8f5), C(34 c67f6ea249d5cc), C(3 ab7633f4557083), C(f10cc2bcf0475411), C(1126f 457 c160d8f5), C(34 c67f6ea249d5cc), C(3 ab7633f4557083), C(3 b2e4d8611a03bd7), C(3103 d6e63d71c3c9), C(43 a56a0b93bb9d53), C(50 aa3ae25803c403)}, {C(e84a123b3e1b0c91), C(735 cc1d493c5e524), C(287030 af8f4ac951), C(fb46abaf4713dda0), C(e8835b9a08cf8cb2), C(3 b85a40e6bee4cce), C(eea02a3930757200), C(fb46abaf4713dda0), C(e8835b9a08cf8cb2), C(3 b85a40e6bee4cce), C(eea02a3930757200), C(fe7057d5fb18ee87), C(723 d258b36eada2a), C(67641393692 a716c), C(c8539a48dae2e539)}, {C(686 c22d2863c48a6), C(1ee6804 e3ddde627), C(8 d66184dd34ddac8), C(35 ac1bc76c11976), C(fed58f898503280d), C(ab6fcb01c630071e), C(edabf3ec7663c3c9), C(35 ac1bc76c11976), C(fed58f898503280d), C(ab6fcb01c630071e), C(edabf3ec7663c3c9), C(591ec5025592 b76e), C(918 a77179b072163), C(25421 d9db4c81e1a), C(96f 1 b3be51f0b548)}, {C(2 c5c1c9fa0ecfde0), C(266 a71b430afaec3), C(53 ab2d731bd8184a), C(5722f 16 b15e7f206), C(35 bb5922c0946610), C(b8d72c08f927f2aa), C(65f 2 c378cb9e8c51), C(5722f 16 b15e7f206), C(35 bb5922c0946610), C(b8d72c08f927f2aa), C(65f 2 c378cb9e8c51), C(cd42fec772c2d221), C(10 ccd5d7bacffdd9), C(a75ecb52192f60e2), C(a648f5fe45e5c164)}, {C(7 a0ac8dd441c9a9d), C(4 a4315964b7377f0), C(24092991 c8f27459), C(9 c6868d561691eb6), C(78 b7016996f98828), C(651e072f 06 c9e7b7), C(fed953d1251ae90), C(9 c6868d561691eb6), C(78 b7016996f98828), C(651e072f 06 c9e7b7), C(fed953d1251ae90), C(7 a4d19fdd89e368c), C(d8224d83b6b9a753), C(3 a93520a455ee9c9), C(159942 bea42b999c)}, {C(c6f9a31dfc91537c), C(b3a250ae029272f8), C(d065fc76d79ec222), C(d2baa99749c71d52), C(5f 90 a2cfc2a3f637), C(79e4 aca7c8bb0998), C(981633149 c85c0ba), C(d2baa99749c71d52), C(5f 90 a2cfc2a3f637), C(79e4 aca7c8bb0998), C(981633149 c85c0ba), C(5 ded415df904b2ee), C(d37d1fc032ebca94), C(ed5b024594967bf7), C(ed7ae636d467e961)}, {C(2 d12010eaf7d8d3d), C(eaec74ccd9b76590), C(541338571 d45608b), C(e97454e4191065f3), C(afb357655f2a5d1c), C(521 ac1614653c130), C(c8a8cac96aa7f32c), C(e97454e4191065f3), C(afb357655f2a5d1c), C(521 ac1614653c130), C(c8a8cac96aa7f32c), C(196 d7f3f386dfd29), C(1 dcd2da5227325cc), C(10e3 b9fa712d3405), C(fdf7864ede0856c0)}, {C(f46de22b2d79a5bd), C(e3e198ba766c0a29), C(828 d8c137216b797), C(bafdb732c8a29420), C(2ed0 b9f4548a9ac3), C(f1ed2d5417d8d1f7), C(451462f 90354 d097), C(bafdb732c8a29420), C(2ed0 b9f4548a9ac3), C(f1ed2d5417d8d1f7), C(451462f 90354 d097), C(bdd091094408851a), C(c4c1731c1ea46c2c), C(615 a2348d60409a8), C(fbc2f058d5539bcc)}, {C(2 ce2f3e89fa141fe), C(ac588fe6ab2b719), C(59 b848c80739487d), C(423722957 b566d10), C(ae4be02664998dc6), C(64017 aacfa69ef80), C(28076dd dbf65a40a), C(423722957 b566d10), C(ae4be02664998dc6), C(64017 aacfa69ef80), C(28076dd dbf65a40a), C(873 bc41acb810f94), C(ac0edafb574b7c0d), C(937 d5d5fd95330bf), C(4ea91171 e208bd7e)}, {C(8 aa75419d95555dd), C(bdb046419d0bf1b0), C(aadf49f217b153da), C(c3cbbe7eb0f5e126), C(fd1809c329311bf6), C(9 c26cc255714d79d), C(67093 aeb89f5d8c8), C(c3cbbe7eb0f5e126), C(fd1809c329311bf6), C(9 c26cc255714d79d), C(67093 aeb89f5d8c8), C(265954 c61009eaf7), C(a5703e8073eaf83f), C(855382 b1aed9c128), C(a6652d5a53d4a008)}, {C(1f bf19dd9207e6aa), C(722834f 3 c5e43cb7), C(e3c13578c5a69744), C(db9120bc83472135), C(f3d9f715e669cfd5), C(63f acc852f487dda), C(9f 08f d85a3a78111), C(db9120bc83472135), C(f3d9f715e669cfd5), C(63f acc852f487dda), C(9f 08f d85a3a78111), C(6 c1e5c694b51b7ca), C(bbceb2e47d44f6a1), C(2eb472 efe06f8330), C(1844408e2 bb87ee)}, {C(6f 11f 9 c1131f1182), C(6f 90740 debc7bad2), C(8 d6e4e2d46ee614b), C(403e3793f 0805 ac3), C(6278 da3d8667a055), C(98ec eadb4f237978), C(4 daa96284c847b0), C(403e3793f 0805 ac3), C(6278 da3d8667a055), C(98ec eadb4f237978), C(4 daa96284c847b0), C(ab119ac9f803d770), C(ab893fe847208376), C(f9d9968ae4472ac3), C(b149ff3b35874201)}, {C(92e896 d8bfdebdb5), C(2 d5c691a0acaeba7), C(377 d7f86b7cb2f8b), C(b8a0738135dde772), C(57f b6c9033fc5f35), C(20e628f 266e63 e1), C(1 ad6647eaaa153a3), C(b8a0738135dde772), C(57f b6c9033fc5f35), C(20e628f 266e63 e1), C(1 ad6647eaaa153a3), C(10005 c85a89e601a), C(cc9088ed03a78e4a), C(c8d3049b8c0d26a1), C(26e8 c0e936cf8cce)}, {C(369 ba54df3c534d1), C(972 c7d2be5f62834), C(112 c8d0cfcc8b1e), C(bcddd22a14192678), C(446 cf170a4f05e72), C(c9e992c7a79ce219), C(fa4762e60a93cf84), C(bcddd22a14192678), C(446 cf170a4f05e72), C(c9e992c7a79ce219), C(fa4762e60a93cf84), C(b2e11a375a352f), C(a70467d0fd624cf1), C(776 b638246febf88), C(e7d1033f7faa39b5)}, {C(bcc4229e083e940e), C(7 a42ebe9e8f526b5), C(bb8d1f389b0769ee), C(ae6790e9fe24c57a), C(659 a16feab53eb5), C(6f d4cfade750bf16), C(31 b1acd328815c81), C(ae6790e9fe24c57a), C(659 a16feab53eb5), C(6f d4cfade750bf16), C(31 b1acd328815c81), C(8 a711090a6ccfd44), C(363240 c31681b80e), C(ad791f19de0b07e9), C(d512217d21c7c370)}, {C(17 c648f416fb15ca), C(fe4d070d14d71a1d), C(ff22eac66f7eb0d3), C(fa4c10f92facc6c7), C(94 cad9e4daecfd58), C(6f fcf829a275d7ef), C(2 a35d2436894d549), C(fa4c10f92facc6c7), C(94 cad9e4daecfd58), C(6f fcf829a275d7ef), C(2 a35d2436894d549), C(c9ea25549513f5a), C(93f 7 cf06df2d0206), C(ef0da319d38fe57c), C(f715dc84df4f4a75)}, {C(8 b752dfa2f9fa592), C(ca95e87b662fe94d), C(34 da3aadfa49936d), C(bf1696df6e61f235), C(9724f ac2c03e3859), C(d9fd1463b07a8b61), C(f8e397251053d8ca), C(bf1696df6e61f235), C(9724f ac2c03e3859), C(d9fd1463b07a8b61), C(f8e397251053d8ca), C(c6d26d868c9e858e), C(2f 4 a1cb842ed6105), C(6 cc48927bd59d1c9), C(469e836 d0b7901e1)}, {C(3ed da5262a7869bf), C(a15eab8c522050c9), C(ba0853c48707207b), C(4 d751c1a836dcda3), C(9747 a6e96f1dd82c), C(3 c986fc5c9dc9755), C(a9d04f3a92844ecd), C(4 d751c1a836dcda3), C(9747 a6e96f1dd82c), C(3 c986fc5c9dc9755), C(a9d04f3a92844ecd), C(2 da9c6cede185e36), C(fae575ef03f987d6), C(b4a6a620b2bee11a), C(8 acba91c5813c424)}, {C(b5776f9ceaf0dba2), C(546ee e4cee927b0a), C(ce70d774c7b1cf77), C(7f 707785 c2d807d7), C(1ea8247 d40cdfae9), C(4945806ea c060028), C(1 a14948790321c37), C(7f 707785 c2d807d7), C(1ea8247 d40cdfae9), C(4945806ea c060028), C(1 a14948790321c37), C(ba3327bf0a6ab79e), C(54e2939592862 de8), C(b7d4651234fa11c7), C(d122970552454def)}, {C(313161f 3 ce61ec83), C(c6c5acb78303987d), C(f00761c6c6e44cee), C(ea660b39d2528951), C(e84537f81a44826a), C(b850bbb69593c26d), C(22499793145e1209), C(ea660b39d2528951), C(e84537f81a44826a), C(b850bbb69593c26d), C(22499793145e1209), C(4 c61b993560bbd58), C(636 d296abe771743), C(f1861b17b8bc3146), C(cd5fca4649d30f8a)}, {C(6e23080 c57f4bcb), C(5f 4 dad6078644535), C(f1591bc445804407), C(46 ca76959d0d4824), C(200 b16bb4031e6a5), C(3 d0e4718ed5363d2), C(4 c8cfcc96382106f), C(46 ca76959d0d4824), C(200 b16bb4031e6a5), C(3 d0e4718ed5363d2), C(4 c8cfcc96382106f), C(8 d6258d795b8097b), C(23 ae7cd1cab4b141), C(cbe74e8fd420afa), C(d553da4575629c63)}, {C(a194c120f440fd48), C(ac0d985eef446947), C(5df 9f a7d97244438), C(fce2269035535eba), C(2 d9b4b2010a90960), C(2 b0952b893dd72f0), C(9 a51e8462c1111de), C(fce2269035535eba), C(2 d9b4b2010a90960), C(2 b0952b893dd72f0), C(9 a51e8462c1111de), C(8682 b5e0624432a4), C(de8500edda7c67a9), C(4821 b171f562c5a2), C(ecb17dea1002e2df)}, {C(3 c78f67ee87b62fe), C(274 c83c73f20f662), C(25 a94c36d3763332), C(7e053f 1 b873bed61), C(d1c343547cd9c816), C(4 deee69b90a52394), C(14038f 0f 3128 ca46), C(7e053f 1 b873bed61), C(d1c343547cd9c816), C(4 deee69b90a52394), C(14038f 0f 3128 ca46), C(ebbf836e38c70747), C(c3c1077b9a7598d0), C(e73c720a27b07ba7), C(ec57f8a9a75af4d9)}, {C(b7d2aee81871e3ac), C(872 ac6546cc94ff2), C(a1b0d2f507ad2d8f), C(bdd983653b339252), C(c02783d47ab815f8), C(36 c5dc27d64d776c), C(5193988ee a7df808), C(bdd983653b339252), C(c02783d47ab815f8), C(36 c5dc27d64d776c), C(5193988ee a7df808), C(8 d8cca9c605cdb4a), C(334904f d32a1f934), C(dbfc15742057a47f), C(f3f92db42ec0cba1)}, {C(41ec0382933 e8f72), C(bd5e52d651bf3a41), C(cbf51a6873d4b29e), C(1 c8c650bfed2c546), C(9 c9085c070350c27), C(e82305be3bded854), C(cf56326bab3d685d), C(1 c8c650bfed2c546), C(9 c9085c070350c27), C(e82305be3bded854), C(cf56326bab3d685d), C(f94db129adc6cecc), C(1f 80871ec4 b35deb), C(c0dc1a4c74d63d0), C(d3cac509f998c174)}, {C(7f e4e777602797f0), C(626e62f 39f 7 c575d), C(d15d6185215fee2f), C(f82ef80641514b70), C(e2702de53389d34e), C(9950592 b7f2da8d8), C(d6b960bf3503f893), C(f82ef80641514b70), C(e2702de53389d34e), C(9950592 b7f2da8d8), C(d6b960bf3503f893), C(95 de69e4f131a9b), C(ee6f56eeff9cdefa), C(28f 4f 86 c2b856b72), C(b73d2decaac56b5b)}, {C(aa71127fd91bd68a), C(960f 6304500f 8069), C(5 cfa9758933beba8), C(dcbbdeb1f56b0ac5), C(45164 c603d084ce4), C(85693f 4ef7 e34314), C(e3a3e3a5ec1f6252), C(dcbbdeb1f56b0ac5), C(45164 c603d084ce4), C(85693f 4ef7 e34314), C(e3a3e3a5ec1f6252), C(91f 4711 c59532bab), C(5e5 a61d26f97200b), C(ffa65a1a41da5883), C(5f 0e712235371 eef)}, {C(677 b53782a8af152), C(90 d76ef694361f72), C(fa2cb9714617a9e0), C(72 c8667cc1e45aa9), C(3 a0aa035bbcd1ef6), C(588e89 b034fde91b), C(f62e4e1d81c1687), C(72 c8667cc1e45aa9), C(3 a0aa035bbcd1ef6), C(588e89 b034fde91b), C(f62e4e1d81c1687), C(1ea81508 efa11e09), C(1 cf493a4dcd49aad), C(8217 d0fbe8226130), C(607 b979c0eb297dd)}, {C(8f 97 bb03473c860f), C(e23e420f9a32e4a2), C(3432 c97895fea7cf), C(69 cc85dac0991c6c), C(4 a6c529f94e9c36a), C(e5865f8da8c887df), C(27e8 c77da38582e0), C(69 cc85dac0991c6c), C(4 a6c529f94e9c36a), C(e5865f8da8c887df), C(27e8 c77da38582e0), C(8e60596 b4e327dbc), C(955 cf21baa1ddb18), C(c24a8eb9360370aa), C(70 d75fd116c2cab1)}, {C(fe50ea9f58e4de6f), C(f0a085b814230ce7), C(89407f 0548f 90e9 d), C(6 c595ea139648eba), C(efe867c726ab2974), C(26f 48ec c1c3821cf), C(55 c63c1b3d0f1549), C(6 c595ea139648eba), C(efe867c726ab2974), C(26f 48ec c1c3821cf), C(55 c63c1b3d0f1549), C(552e5f 78e1 d87a69), C(c9bfe2747a4eedf0), C(d5230acb6ef95a1), C(1e812f 3 c0d9962bd)}, {C(56eb0f cb9852bd27), C(c817b9a578c7b12), C(45427842795 bfa84), C(8 dccc5f52a65030c), C(f89ffa1f4fab979), C(7 d94da4a61305982), C(1 ba6839d59f1a07a), C(8 dccc5f52a65030c), C(f89ffa1f4fab979), C(7 d94da4a61305982), C(1 ba6839d59f1a07a), C(e0162ec1f40d583e), C(6 abf0b85552c7c33), C(f14bb021a875867d), C(c12a569c8bfe3ba7)}, {C(6 be2903d8f07af90), C(26 aaf7b795987ae8), C(44 a19337cb53fdeb), C(f0e14afc59e29a3a), C(a4d0084172a98c0d), C(275998 a345d04f0f), C(db73704d81680e8d), C(f0e14afc59e29a3a), C(a4d0084172a98c0d), C(275998 a345d04f0f), C(db73704d81680e8d), C(351388 cf7529b1b1), C(a3155d0237571da5), C(355231 b516da2890), C(263 c5a3d498c1cc)}, {C(58668066 da6bfc4), C(a4ea2eb7212df3dd), C(481f 64f 7 ca220524), C(11 b3b649b1cea339), C(57f 4 ad5b54d71118), C(feeb30bec803ab49), C(6ed9 bcc1973d9bf9), C(11 b3b649b1cea339), C(57f 4 ad5b54d71118), C(feeb30bec803ab49), C(6ed9 bcc1973d9bf9), C(bf2859d9964a70c8), C(d31ab162ca25f24e), C(70349336f f55d5d5), C(9 a2fa97115ef4409)}, {C(2 d04d1fbab341106), C(efe0c5b2878b444c), C(882 a2a889b5e8e71), C(18 cc96be09e5455), C(1 ad58fd26919e409), C(76593521 c4a0006b), C(f1361f348fa7cbfb), C(18 cc96be09e5455), C(1 ad58fd26919e409), C(76593521 c4a0006b), C(f1361f348fa7cbfb), C(205 bc68e660b0560), C(74360e11f 9f c367e), C(a88b7b0fa86caf), C(a982d749b30d4e4c)}, {C(d366b37bcd83805b), C(a6d16fea50466886), C(cb76dfa8eaf74d70), C(389 c44e423749aa), C(a30d802bec4e5430), C(9 ac1279f92bea800), C(686ef471 c2624025), C(389 c44e423749aa), C(a30d802bec4e5430), C(9 ac1279f92bea800), C(686ef471 c2624025), C(2 c21a72f8e3a3423), C(df5ab83f0918646a), C(cd876e0cb4df80fa), C(5 abbb92679b3ea36)}, {C(bbb9bc819ab65946), C(25e0 c756c95803e2), C(82 a73a1e1cc9bf6a), C(671 b931b702519a3), C(61609e7 dc0dd9488), C(9 cb329b8cab5420), C(3 c64f8ea340096ca), C(671 b931b702519a3), C(61609e7 dc0dd9488), C(9 cb329b8cab5420), C(3 c64f8ea340096ca), C(1690 afe3befd3afb), C(4 d3c18a846602740), C(a6783133a31dd64d), C(ecf4665e6bc76729)}, {C(8e994 eac99bbc61), C(84 de870b6f3c114e), C(150ef c95ce7b0cd2), C(4 c5d48abf41185e3), C(86049 a83c7cdcc70), C(ad828ff609277b93), C(f60fe028d582ccc7), C(4 c5d48abf41185e3), C(86049 a83c7cdcc70), C(ad828ff609277b93), C(f60fe028d582ccc7), C(464e0 b174da0cbd4), C(eadf1df69041b06e), C(48 cb9c96a9df1cdc), C(b7e5ee62809223a1)}, {C(364 cabf6585e2f7d), C(3 be1cc452509807e), C(1236 ce85788680d4), C(4 cea77c54fc3583a), C(9 a2a64766fd77614), C(63e6 c9254b5dc4db), C(26 af12ba3bf5988e), C(4 cea77c54fc3583a), C(9 a2a64766fd77614), C(63e6 c9254b5dc4db), C(26 af12ba3bf5988e), C(4 a821aca3ffa26a1), C(99 aa9aacbb3d08e3), C(619 ac77b52e8a823), C(68 c745a1ce4b7adb)}, {C(e878e2200893d775), C(76 b1e0a25867a803), C(9 c14d6d91f5ae2c5), C(ac0ffd8d64e242ed), C(e1673ee2dd997587), C(8 cdf3e9369d61003), C(c37c9a5258b98eba), C(ac0ffd8d64e242ed), C(e1673ee2dd997587), C(8 cdf3e9369d61003), C(c37c9a5258b98eba), C(f252b2e7b67dd012), C(47f c1eb088858f28), C(59 c42e4af1353223), C(e05b6c61c19eb26e)}, {C(6f 6 a014b9a861926), C(269e13 a120277867), C(37f c8a181e78711b), C(33dd 054 c41f3aef2), C(4f c8ab1a2ef3da7b), C(597178 c3756a06dc), C(748f 8 aadc540116f), C(33dd 054 c41f3aef2), C(4f c8ab1a2ef3da7b), C(597178 c3756a06dc), C(748f 8 aadc540116f), C(78e3 be34de99461e), C(28 b7b60d90dddab4), C(e47475fa9327a619), C(88 b17629e6265924)}, {C(da52b64212e8149b), C(121e713 c1692086f), C(f3d63cfa03850a02), C(f0d82bafec3c564c), C(37 dece35b549a1ce), C(5f b28f6078c4a2bd), C(b69990b7d9405710), C(f0d82bafec3c564c), C(37 dece35b549a1ce), C(5f b28f6078c4a2bd), C(b69990b7d9405710), C(3 af5223132071100), C(56 d5bb35f3bb5d2a), C(fcad4a4d5d3a1bc7), C(f17bf3d8853724d0)}, {C(1100f 797 ce53a629), C(f528c6614a1a30c2), C(30e49f b56bec67fa), C(f991664844003cf5), C(d54f5f6c8c7cf835), C(ca9cc4437c591ef3), C(d5871c77cf8fb424), C(f991664844003cf5), C(d54f5f6c8c7cf835), C(ca9cc4437c591ef3), C(d5871c77cf8fb424), C(5 cf90f1e617b750c), C(1648f 825 ab986232), C(936 cf225126a60), C(90f a5311d6f2445c)}, {C(4f 00655 b76e9cfda), C(9 dc5c707772ed283), C(b0f885f1e01927ec), C(6e4 d6843289dfb47), C(357 b41c6e5fd561f), C(491e386 bacb6df3c), C(86 be1b64ecd9945c), C(6e4 d6843289dfb47), C(357 b41c6e5fd561f), C(491e386 bacb6df3c), C(86 be1b64ecd9945c), C(be9547e3cfd85fae), C(f9e26ac346b430a8), C(38508 b84b0e68cff), C(a28d49dbd5562703)}, {C(d970198b6ca854db), C(92e3 d1786ae556a0), C(99 a165d7f0d85cf1), C(6548910 c5f668397), C(a5c8d20873e7de65), C(5 b7c4ecfb8e38e81), C(6 aa50a5531dad63e), C(6548910 c5f668397), C(a5c8d20873e7de65), C(5 b7c4ecfb8e38e81), C(6 aa50a5531dad63e), C(ab903d724449e003), C(ea3cc836c28fef88), C(4 b250d6c7200949d), C(13 a110654fa916c0)}, {C(76 c850754f28803), C(a4bffed2982cb821), C(6710e352247 caf63), C(d9cbf5b9c31d964e), C(25 c8f890178b97ae), C(e7c46064676cde9f), C(d8bb5eeb49c06336), C(d9cbf5b9c31d964e), C(25 c8f890178b97ae), C(e7c46064676cde9f), C(d8bb5eeb49c06336), C(962 b35ae89d5f4c1), C(c49083801ac2c21), C(2 db46ddec36ff33b), C(da48992ab8da284)}, {C(9 c98da9763f0d691), C(f5437139a3d40401), C(6f 493 c26c42f91e2), C(e857e4ab2d124d5), C(6417 bb2f363f36da), C(adc36c9c92193bb1), C(d35bd456172df3df), C(e857e4ab2d124d5), C(6417 bb2f363f36da), C(adc36c9c92193bb1), C(d35bd456172df3df), C(577 da94064d3a3d6), C(23f 13 d7532ea496a), C(6e09392 d80b8e85b), C(2e05f f6f23663892)}, {C(22f 8f 6869 a5f325), C(a0e7a96180772c26), C(cb71ea6825fa3b77), C(39 d3dec4e718e903), C(900 c9fbdf1ae2428), C(305301 da2584818), C(c6831f674e1fdb1f), C(39 d3dec4e718e903), C(900 c9fbdf1ae2428), C(305301 da2584818), C(c6831f674e1fdb1f), C(8 ad0e38ffe71babf), C(554 ac85a8a837e64), C(9900 c582cf401356), C(169f 646 b01ed7762)}, {C(9 ae7575fc14256bb), C(ab9c5a397fabc1b3), C(1 d3f582aaa724b2e), C(94412f 598ef156), C(15 bf1a588f25b327), C(5756646 bd68ce022), C(f062a7d29be259a5), C(94412f 598ef156), C(15 bf1a588f25b327), C(5756646 bd68ce022), C(f062a7d29be259a5), C(aa99c683cfb60b26), C(9e3 b7d4b17f91273), C(301 d3f5422dd34cf), C(53 d3769127253551)}, {C(540040e79752 b619), C(670327e237 c88cb3), C(50962f 261 bcc31d9), C(9 a8ea2b68b2847ec), C(bc24ab7d4cbbda31), C(df5aff1cd42a9b57), C(db47d368295f4628), C(9 a8ea2b68b2847ec), C(bc24ab7d4cbbda31), C(df5aff1cd42a9b57), C(db47d368295f4628), C(9 a66c221d1bf3f3), C(7 ae74ee1281de8ee), C(a4e173e2c787621f), C(5 b51062d10ae472)}, {C(34 cbf85722d897b1), C(6208 cb2a0fff4eba), C(e926cbc7e86f544e), C(883706 c4321efee0), C(8f d5d3d84c7827e4), C(a5c80e455a7ccaaa), C(3515f 41164654591), C(883706 c4321efee0), C(8f d5d3d84c7827e4), C(a5c80e455a7ccaaa), C(3515f 41164654591), C(2 c08bfc75dbfd261), C(6e9 eadf14f8c965e), C(18783f 5770 cd19a3), C(a6c7f2f1aa7b59ea)}, {C(46 afa66366bf5989), C(aa0d424ac649008b), C(97 a9108b3cd9c5c9), C(6 ca08e09227a9630), C(8 b11f73a8e5b80eb), C(2391 bb535dc7ce02), C(e43e2529cf36f4b9), C(6 ca08e09227a9630), C(8 b11f73a8e5b80eb), C(2391 bb535dc7ce02), C(e43e2529cf36f4b9), C(c9bd6d82b7a73d9d), C(b2ed9bae888447ac), C(bd22bb13af0cd06d), C(62781441785 b355b)}, {C(e15074b077c6e560), C(7 c8f2173fcc34afa), C(8 aad55bc3bd38370), C(d407ecdbfb7cb138), C(642442eff 44578 af), C(d3e9fdaf71a5b79e), C(c87c53eda46aa860), C(d407ecdbfb7cb138), C(642442eff 44578 af), C(d3e9fdaf71a5b79e), C(c87c53eda46aa860), C(8462310 a2c76ff51), C(1 bc17a2e0976665e), C(6ec446 b13b4d79cf), C(388 c7a904b4264c1)}, {C(9740 b2b2d6d06c6), C(e738265f9de8dafc), C(fdc947c1fca8be9e), C(d6936b41687c1e3d), C(a1a2deb673345994), C(91501e58 b17168bd), C(b8edee2b0b708dfc), C(d6936b41687c1e3d), C(a1a2deb673345994), C(91501e58 b17168bd), C(b8edee2b0b708dfc), C(ddf4b43dafd17445), C(44015 d050a04ce5c), C(1019f d9ab82c4655), C(c803aea0957bcdd1)}, {C(f1431889f2db1bff), C(85257 aa1dc6bd0d0), C(1 abbdea0edda5be4), C(775 aa89d278f26c3), C(a542d20265e3ef09), C(933 bdcac58a33090), C(c43614862666ca42), C(775 aa89d278f26c3), C(a542d20265e3ef09), C(933 bdcac58a33090), C(c43614862666ca42), C(4 c5e54d481a9748d), C(65 ce3cd0db838b26), C(9 ccbb4005c7f09d2), C(e6dda9555dde899a)}, {C(e2dd273a8d28c52d), C(8 cd95915fdcfd96b), C(67 c0f5b1025f0699), C(cbc94668d48df4d9), C(7e3 d656e49d632d1), C(8329e30 cac7a61d4), C(38e6 cd1e2034e668), C(cbc94668d48df4d9), C(7e3 d656e49d632d1), C(8329e30 cac7a61d4), C(38e6 cd1e2034e668), C(41e0 bce03ed9394b), C(7 be48d0158b9834a), C(9ea8 d5d1a976b18b), C(606 c424c33617e7a)}, {C(e0f79029834cc6ac), C(f2b1dcb87cc5e94c), C(4210 bc221fe5e70a), C(fd4a4301d4e2ac67), C(8f 84358 d25b2999b), C(6 c4b7d8a5a22ccbb), C(25df 606 bb23c9d40), C(fd4a4301d4e2ac67), C(8f 84358 d25b2999b), C(6 c4b7d8a5a22ccbb), C(25df 606 bb23c9d40), C(915298 b0eaadf85b), C(5ec23 cc4c6a74e62), C(d640a4ff99763439), C(1603753f b34ad427)}, {C(9 dc0a29830bcbec1), C(ec4a01dbd52d96a0), C(cd49c657eff87b05), C(ea487fe948c399e1), C(f5de9b2e59192609), C(4604 d9b3248b3a5), C(1929878 a22c86a1d), C(ea487fe948c399e1), C(f5de9b2e59192609), C(4604 d9b3248b3a5), C(1929878 a22c86a1d), C(3 cf6cd7c19dfa1ef), C(46e404 ee4af2d726), C(613 ab0588a5527b5), C(73e39385 ced7e684)}, {C(d10b70dde60270a6), C(be0f3b256e23422a), C(6 c601297a3739826), C(e327ffc477cd2467), C(ebebba63911f32b2), C(2 c2c5c24cf4970a2), C(a3cd2c192c1b8bf), C(e327ffc477cd2467), C(ebebba63911f32b2), C(2 c2c5c24cf4970a2), C(a3cd2c192c1b8bf), C(94 cb02c94aaf250b), C(30 ca38d5e3dac579), C(d68598a91dc597b5), C(162 b050e8de2d92)}, {C(58 d2459f094d075c), C(b4df247528d23251), C(355283f 2128 a9e71), C(d046198e4df506c2), C(c61bb9705786ae53), C(b360200380d10da8), C(59942 bf009ee7bc), C(d046198e4df506c2), C(c61bb9705786ae53), C(b360200380d10da8), C(59942 bf009ee7bc), C(95806 d027f8d245e), C(32df 87487ed9 d0f4), C(e2c5bc224ce97a98), C(9 a47c1e33cfb1cc5)}, {C(68 c600cdd42d9f65), C(bdf0c331f039ff25), C(1354 ac1d98944023), C(b5cdfc0b06fd1bd9), C(71f 0 ce33b183efab), C(d8ae4f9d4b949755), C(877 da19d6424f6b3), C(b5cdfc0b06fd1bd9), C(71f 0 ce33b183efab), C(d8ae4f9d4b949755), C(877 da19d6424f6b3), C(f7cc5cbf76bc6006), C(c93078f44b98efdb), C(3 d482142c727e8bc), C(8e23f 92e0616 d711)}, {C(9f c0bd876cb975da), C(80f 41015045 d1ade), C(5 cbf601fc55c809a), C(7 d9c567075001705), C(a2fafeed0df46d5d), C(a70b82990031da8f), C(8611 c76abf697e56), C(7 d9c567075001705), C(a2fafeed0df46d5d), C(a70b82990031da8f), C(8611 c76abf697e56), C(806911617e1 ee53), C(1 ce82ae909fba503), C(52df 85f ea9e404bd), C(dbd184e5d9a11a3e)}, {C(7 b3e8c267146c361), C(c6ad095af345b726), C(af702ddc731948bd), C(7 ca4c883bded44b5), C(c90beb31ee9b699a), C(2 cdb4aba3d59b8a3), C(df0d4fa685e938f0), C(7 ca4c883bded44b5), C(c90beb31ee9b699a), C(2 cdb4aba3d59b8a3), C(df0d4fa685e938f0), C(cc0e568e91aaa382), C(70 ca583a464dbea), C(b7a5859b44710e1a), C(ad141467fdf9a83a)}, {C(6 c49c6b3c9dd340f), C(897 c41d89af37bd1), C(52df 69e0 e2c68a8d), C(eec4be1f65531a50), C(bf23d928f20f1b50), C(c642009b9c593940), C(c5e59e6ca9e96f85), C(eec4be1f65531a50), C(bf23d928f20f1b50), C(c642009b9c593940), C(c5e59e6ca9e96f85), C(7f bd53343e7da499), C(dd87e7b88afbd251), C(92696e7683 b9f322), C(60f f51ef02c24652)}, {C(47324327 a4cf1732), C(6044753 d211e1dd5), C(1ec ae46d75192d3b), C(b6d6315a902807e3), C(ccc8312c1b488e5d), C(b933a7b48a338ec), C(9 d6753cd83422074), C(b6d6315a902807e3), C(ccc8312c1b488e5d), C(b933a7b48a338ec), C(9 d6753cd83422074), C(5714 bd5c0efdc7a8), C(221585e2 c88068ca), C(303342 b25678904), C(8 c174a03e69a76e)}, {C(1e984 ef53c5f6aae), C(99ea10 dac804298b), C(a3f8c241100fb14d), C(259eb3 c63a9c9be6), C(f8991532947c7037), C(a16d20b3fc29cfee), C(493 c2e91a775af8c), C(259eb3 c63a9c9be6), C(f8991532947c7037), C(a16d20b3fc29cfee), C(493 c2e91a775af8c), C(275f ccf4acb08abc), C(d13fb6ea3eeaf070), C(505283e5 b702b9ea), C(64 c092f9f8df1901)}, {C(b88f5c9b8b854cc6), C(54f c5d39825b446), C(a12fc1546eac665d), C(ab90eb7fa58b280c), C(dda26598356aa599), C(64191 d63f2586e52), C(cada0075c34e8b02), C(ab90eb7fa58b280c), C(dda26598356aa599), C(64191 d63f2586e52), C(cada0075c34e8b02), C(e7de6532b691d87c), C(a28fec86e368624), C(796 c280eebd0241a), C(acfcecb641fdbeee)}, {C(9f cb3fdb09e7a63a), C(7 a115c9ded150112), C(e9ba629108852f37), C(9 b03c7c218c192a), C(93 c1dd563f46308e), C(f9553625917ea800), C(e0a52f8a5024c59), C(9 b03c7c218c192a), C(93 c1dd563f46308e), C(f9553625917ea800), C(e0a52f8a5024c59), C(2 bb3a9e8b053e490), C(8 b97936723cd8ff6), C(bf3f835246d02722), C(c8e033da88ecd724)}, {C(d58438d62089243), C(d8c19375b228e9d3), C(13042546ed96 e790), C(4 a42ef343514138c), C(549e62449 e225cf1), C(dd8260e2808f68e8), C(69580f c81fcf281b), C(4 a42ef343514138c), C(549e62449 e225cf1), C(dd8260e2808f68e8), C(69580f c81fcf281b), C(fc0e30d682e87289), C(f44b784248d6107b), C(df25119527fdf209), C(cc265612588171a8)}, {C(7ea73 b6b74c8cd0b), C(e07188dd9b5bf3ca), C(6ef62f f2dd008ed4), C(acd94b3038342152), C(1 b0ed99c9b7ba297), C(b794a93f4c895939), C(97 a60cd93021206d), C(acd94b3038342152), C(1 b0ed99c9b7ba297), C(b794a93f4c895939), C(97 a60cd93021206d), C(9e0 c0e6da5001b07), C(5f 5 b817de5d2a391), C(35 b8a8702acdd533), C(3 bbcfef344f455)}, {C(e42ffdf6278bb21), C(59df 3e5 ca582ff9d), C(f3108785599dbde9), C(f78e8a2d4aba6a1d), C(700473f b0d8380fc), C(d0a0d68061ac74b2), C(11650612f a426e5a), C(f78e8a2d4aba6a1d), C(700473f b0d8380fc), C(d0a0d68061ac74b2), C(11650612f a426e5a), C(e39ceb5b2955710c), C(f559ff201f8cebaa), C(1f bc182809e829a0), C(295 c7fc82fa6fb5b)}, {C(9 ad37fcd49fe4aa0), C(76 d40da71930f708), C(bea08b630f731623), C(797292108901 a81f), C(3 b94127b18fae49c), C(688247179f 144f 1 b), C(48 a507a1625d13d7), C(797292108901 a81f), C(3 b94127b18fae49c), C(688247179f 144f 1 b), C(48 a507a1625d13d7), C(452322 aaad817005), C(51 d730d973e13d44), C(c883eb30176652ea), C(8 d338fd678b2404d)}, {C(27 b7ff391136696e), C(60 db94a18593438c), C(b5e46d79c4dafbad), C(ad56fd25a6f15289), C(68 a0ec7c0179df80), C(a0aacfc36620957), C(87 a0762a09e2e1c1), C(ad56fd25a6f15289), C(68 a0ec7c0179df80), C(a0aacfc36620957), C(87 a0762a09e2e1c1), C(d50ace99460f0be3), C(7f 1f e5653ae0d999), C(3870899 d9d6c22c), C(df5f952dd90d5a09)}, {C(76 bd077e42692ddf), C(c14b60958c2c7a85), C(fd9f3b0b3b1e2738), C(273 d2c51a8e65e71), C(ac531423f670bf34), C(7f 40 c6bfb8c5758a), C(5f de65b433a10b02), C(273 d2c51a8e65e71), C(ac531423f670bf34), C(7f 40 c6bfb8c5758a), C(5f de65b433a10b02), C(dbda6c4252b0a75c), C(5 d4cfd8f937b23d9), C(3895f 478e1 c29c9d), C(e3e7c1fd1199aec6)}, {C(81 c672225442e053), C(927 c3f6c8964050e), C(cb59f8f2bb36fac5), C(298f 3583326f d942), C(b85602a9a2e2f97c), C(65 c849bfa3191459), C(bf21329dfb496c0d), C(298f 3583326f d942), C(b85602a9a2e2f97c), C(65 c849bfa3191459), C(bf21329dfb496c0d), C(ea7b7b44c596aa18), C(c18bfb6e9a36d59c), C(1 b55f03e8a38cc0a), C(b6a94cd47bbf847f)}, {C(37 b9e308747448ca), C(513f 39f 5545 b1bd), C(145 b32114ca00f9c), C(cce24b9910eb0489), C(af4ac64668ac57d9), C(ea0e44c13a9a5d5e), C(b224fb0c680455f4), C(cce24b9910eb0489), C(af4ac64668ac57d9), C(ea0e44c13a9a5d5e), C(b224fb0c680455f4), C(a7714bbba8699be7), C(fecad6e0e0092204), C(c1ce8bd5ac247eb4), C(3993 aef5c07cdca2)}, {C(dab71695950a51d4), C(9e98 e4dfa07566fe), C(fab3587513b84ec0), C(2409f 60f 0854f 305), C(b17f6e6c8ff1894c), C(62f a048551dc7ad6), C(d99f4fe2799bad72), C(2409f 60f 0854f 305), C(b17f6e6c8ff1894c), C(62f a048551dc7ad6), C(d99f4fe2799bad72), C(4 a38e7f2f4a669d3), C(53173510 ca91f0e3), C(cc9096c0df860b0), C(52ed637026 a4a0d5)}, {C(28630288285 c747b), C(a165a5bf51aaec95), C(927 d211f27370016), C(727 c782893d30c22), C(742706852989 c247), C(c546494c3bb5e7e2), C(1f b2a5d1570f5dc0), C(727 c782893d30c22), C(742706852989 c247), C(c546494c3bb5e7e2), C(1f b2a5d1570f5dc0), C(71e498804df 91 b76), C(4 a6a5aa6f7e5621), C(871 a63730d13a544), C(63f 77 c8f371cc2f8)}, {C(4 b591ad5160b6c1b), C(e8f85ddd5a1143f7), C(377e18171476 d64), C(829481773 cce2cb1), C(c9d9fb4e25e4d243), C(c1fff894f0cf713b), C(69ed d73ec20984b0), C(829481773 cce2cb1), C(c9d9fb4e25e4d243), C(c1fff894f0cf713b), C(69ed d73ec20984b0), C(7f b1132262925f4a), C(a292e214fe56794f), C(915 bfee68e16f46f), C(98 bcc857bb6d31e7)}, {C(7e02f 7 a5a97dd3df), C(9724 a88ac8c30809), C(d8dee12589eeaf36), C(c61f8fa31ad1885b), C(3e3744 e04485ff9a), C(939335 b37f34c7a2), C(faa5de308dbbbc39), C(c61f8fa31ad1885b), C(3e3744 e04485ff9a), C(939335 b37f34c7a2), C(faa5de308dbbbc39), C(f5996b1be7837a75), C(4f cb12d267f5af4f), C(39 be67b8cd132169), C(5 c39e3819198b8a1)}, {C(ff66660873521fb2), C(d82841f7e714ce03), C(c830d273f005e378), C(66990 c8c54782228), C(4f 28 bea83dda97c), C(6 a24c64698688de0), C(69721141111 da99b), C(66990 c8c54782228), C(4f 28 bea83dda97c), C(6 a24c64698688de0), C(69721141111 da99b), C(d5c771fade83931b), C(8094ed75 e6feb396), C(7 a79d4de8efd1a2c), C(5f 9e50167693 e363)}, {C(ef3c4dd60fa37412), C(e8d2898c86d11327), C(8 c883d860aafacfe), C(a4ace72ba19d6de5), C(4 cae26627dfc5511), C(38e496 de9f677b05), C(558770996e1906 d6), C(a4ace72ba19d6de5), C(4 cae26627dfc5511), C(38e496 de9f677b05), C(558770996e1906 d6), C(40df 30e332 ceca69), C(8f 106 cbd94166c42), C(332 b6ab4f4c1014e), C(7 c0bc3092ad850e5)}, {C(a7b07bcb1a1333ba), C(9 d007956720914c3), C(4751f 60ef2 b15545), C(77 ac4dcee10c9023), C(e90235108fa20e56), C(1 d3ea38535215800), C(5ed1 ccfff26bc64), C(77 ac4dcee10c9023), C(e90235108fa20e56), C(1 d3ea38535215800), C(5ed1 ccfff26bc64), C(789 a1c352bf5c61e), C(860 a119056da8252), C(a6c268a238699086), C(4 d70f5cccf4ef2eb)}, {C(89858f c94ee25469), C(f72193b78aeaa896), C(7 dba382760727c27), C(846 b72f372f1685a), C(f708db2fead5433c), C(c04e121770ee5dc), C(4619793 b67d0daa4), C(846 b72f372f1685a), C(f708db2fead5433c), C(c04e121770ee5dc), C(4619793 b67d0daa4), C(79f 80506f 152285f), C(5300074926f ccd56), C(7f bbff6cc418fce6), C(b908f77c676b32e4)}, {C(e6344d83aafdca2e), C(6e147816 e6ebf87), C(8508 c38680732caf), C(f4ce36d3a375c981), C(9 d67e5572f8d7bf4), C(900 d63d9ec79e477), C(5251 c85ab52839a3), C(f4ce36d3a375c981), C(9 d67e5572f8d7bf4), C(900 d63d9ec79e477), C(5251 c85ab52839a3), C(92ec4 b3952e38027), C(40 b2dc421a518cbf), C(661ea97 b2331a070), C(8 d428a4a9485179b)}, {C(3dd bb400198d3d4d), C(fe73de3ada21af5c), C(cd7df833dacd8da3), C(162 be779eea87bf8), C(7 d62d36edf759e6d), C(dc20f528362e37b2), C(1 a902edfe4a5824e), C(162 be779eea87bf8), C(7 d62d36edf759e6d), C(dc20f528362e37b2), C(1 a902edfe4a5824e), C(e6a258d30fa817ba), C(c5d73adf6fb196fd), C(475 b7a6286a207fb), C(d35f96363e8eba95)}, {C(79 d4c20cf83a7732), C(651ea0 a6ab059bcd), C(94631144f 363 cdef), C(894 a0ee0c1f87a22), C(4e682573f 8 b38f25), C(89803f c082816289), C(71613963 a02d90e1), C(894 a0ee0c1f87a22), C(4e682573f 8 b38f25), C(89803f c082816289), C(71613963 a02d90e1), C(4 c6cc0e5a737c910), C(a3765b5da16bccd9), C(8 bf483c4d735ec96), C(7f d7c8ba1934afec)}, {C(5 aaf0d7b669173b5), C(19661 ca108694547), C(5 d03d681639d71fe), C(7 c422f4a12fd1a66), C(aa561203e7413665), C(e99d8d202a04d573), C(6090357ec6f 1f 1), C(7 c422f4a12fd1a66), C(aa561203e7413665), C(e99d8d202a04d573), C(6090357ec6f 1f 1), C(dbfe89f01f0162e), C(49 aa89da4f1e389b), C(7119 a6f4514efb22), C(56593f 6 b4e7318d9)}, {C(35 d6cc883840170c), C(444694 c4f8928732), C(98500f 14 b8741c6), C(5021 ac9480077dd), C(44 c2ebc11cfb9837), C(e5d310c4b5c1d9fd), C(a577102c33ac773c), C(5021 ac9480077dd), C(44 c2ebc11cfb9837), C(e5d310c4b5c1d9fd), C(a577102c33ac773c), C(a00d2efd2effa3cf), C(c2c33ffcda749df6), C(d172099d3b6f2986), C(f308fe33fcd23338)}, {C(b07eead7a57ff2fe), C(c1ffe295ca7dbf47), C(ef137b125cfa8851), C(8f 8ee c5cde7a490a), C(79916 d20a405760b), C(3 c30188c6d38c43c), C(b17e3c3ff7685e8d), C(8f 8ee c5cde7a490a), C(79916 d20a405760b), C(3 c30188c6d38c43c), C(b17e3c3ff7685e8d), C(ac8aa3cd0790c4c9), C(78 ca60d8bf10f670), C(26f 522 be4fbc1184), C(55 bc7688083326d4)}, {C(20f ba36c76380b18), C(95 c39353c2a3477d), C(4f 362902 cf9117ad), C(89816ec851 e3f405), C(65258396f 932858 d), C(b7dcaf3cc57a0017), C(b368f482afc90506), C(89816ec851 e3f405), C(65258396f 932858 d), C(b7dcaf3cc57a0017), C(b368f482afc90506), C(88f 08 c74465015f1), C(94eb af209d59099d), C(c1b7ff7304b0a87), C(56 bf8235257d4435)}, {C(c7e9e0c45afeab41), C(999 d95f41d9ee841), C(55ef15 ac11ea010), C(cc951b8eab5885d), C(956 c702c88ac056b), C(de355f324a37e3c0), C(ed09057eb60bd463), C(cc951b8eab5885d), C(956 c702c88ac056b), C(de355f324a37e3c0), C(ed09057eb60bd463), C(1f 44 b6d04a43d088), C(53631822 a26ba96d), C(90305f c2d21f8d28), C(60693 a9a6093351a)}, {C(69 a8e59c1577145d), C(cb04a6e309ebc626), C(9 b3326a5b250e9b1), C(d805f665265fd867), C(82 b2b019652c19c6), C(f0df7738353c82a6), C(6 a9acf124383ca5f), C(d805f665265fd867), C(82 b2b019652c19c6), C(f0df7738353c82a6), C(6 a9acf124383ca5f), C(6838374508 a7a99f), C(7 b6719db8d3e40af), C(1 a22666cf0dcb7cf), C(989 a9cf7f46b434d)}, {C(6638191337226509), C(42 b55e08e4894870), C(a7696f5fbd51878e), C(433 bbdd27481d85d), C(ee32136b5a47bbec), C(769 a77f346d82f4e), C(38 b91b1cb7e34be), C(433 bbdd27481d85d), C(ee32136b5a47bbec), C(769 a77f346d82f4e), C(38 b91b1cb7e34be), C(cb10fd95c0e43875), C(ce9744efd6f11427), C(946 b32bddada6a13), C(35 d544690b99e3b6)}, {C(c44e8c33ff6c5e13), C(1f 128 a22aab3007f), C(6 a8b41bf04cd593), C(1 b9b0deaf126522a), C(cc51d382baedc2eb), C(8df 8831 bb2e75daa), C(de4e7a4b5de99588), C(1 b9b0deaf126522a), C(cc51d382baedc2eb), C(8df 8831 bb2e75daa), C(de4e7a4b5de99588), C(55 a2707103a9f968), C(e0063f4e1649702d), C(7e82f 5 b440e74043), C(649 b44a27f00219d)}, {C(68125009158 bded7), C(563 a9a62753fc088), C(b97a9873a352cf6a), C(237 d1de15ae56127), C(b96445f758ba57d), C(b842628a9f9938eb), C(70313 d232dc2cd0d), C(237 d1de15ae56127), C(b96445f758ba57d), C(b842628a9f9938eb), C(70313 d232dc2cd0d), C(8 bfe1f78cb40ad5b), C(a5bde811d49f56e1), C(1 acd0cf913ded507), C(820 b3049fa5e6786)}, {C(e0dd644db35a62d6), C(292889772752 ab42), C(b80433749dbb8793), C(7032f e67035f95db), C(d8076d1fda17eb8d), C(115 ca1775560f946), C(92 da1e16f396bf61), C(7032f e67035f95db), C(d8076d1fda17eb8d), C(115 ca1775560f946), C(92 da1e16f396bf61), C(17 c8bc7f6d23a639), C(fb28a2afa4d562a9), C(6 c59c95fa2450d5f), C(fe0d41d5ebfbce2a)}, {C(21 ce9eab220aaf87), C(27 d20caec922d708), C(610 c51f976cb1d30), C(6052f 97 a1e02d2ba), C(836ee a7ce63dea17), C(e1f8efb81b443b45), C(ddbdbbe717570246), C(6052f 97 a1e02d2ba), C(836ee a7ce63dea17), C(e1f8efb81b443b45), C(ddbdbbe717570246), C(69551045 b0e56f60), C(625 a435960ba7466), C(9 cdb004e8b11405c), C(d6284db99a3b16af)}, {C(83 b54046fdca7c1e), C(e3709e9153c01626), C(f306b5edc2682490), C(88f 14 b0b554fba02), C(a0ec13fac0a24d0), C(f468ebbc03b05f47), C(a9cc417c8dad17f0), C(88f 14 b0b554fba02), C(a0ec13fac0a24d0), C(f468ebbc03b05f47), C(a9cc417c8dad17f0), C(4 c1ababa96d42275), C(c112895a2b751f17), C(5dd 7 d9fa55927aa9), C(ca09db548d91cd46)}, {C(dd3b2ce7dabb22fb), C(64888 c62a5cb46ee), C(f004e8b4b2a97362), C(31831 cf3efc20c84), C(901 ba53808e677ae), C(4 b36895c097d0683), C(7 d93ad993f9179aa), C(31831 cf3efc20c84), C(901 ba53808e677ae), C(4 b36895c097d0683), C(7 d93ad993f9179aa), C(a4c5ea29ae78ba6b), C(9 cf637af6d607193), C(5731 bd261d5b3adc), C(d59a9e4f796984f3)}, {C(9ee08f c7a86b0ea6), C(5 c8d17dff5768e66), C(18859672 bafd1661), C(d3815c5f595e513e), C(44 b3bdbdc0fe061f), C(f5f43b2a73ad2df5), C(7 c0e6434c8d7553c), C(d3815c5f595e513e), C(44 b3bdbdc0fe061f), C(f5f43b2a73ad2df5), C(7 c0e6434c8d7553c), C(8 c05859060821002), C(73629 a0d275008ce), C(860 c012879e6f00f), C(d48735a120d2c37c)}, {C(4e2 a10f1c409dfa5), C(6e684591f 5 da86bd), C(ff8c9305d447cadb), C(c43ae49df25b1c86), C(d4f42115cee1ac8), C(a0e6a714471b975c), C(a40089dec5fe07b0), C(c43ae49df25b1c86), C(d4f42115cee1ac8), C(a0e6a714471b975c), C(a40089dec5fe07b0), C(18 c3d8f967915e10), C(739 c747dbe05adfb), C(4 b0397b596e16230), C(3 c57d7e1de9e58d1)}, {C(bdf3383d7216ee3c), C(eed3a37e4784d324), C(247 cff656d081ba0), C(76059e4 cb25d4700), C(e0af815fe1fa70ed), C(5 a6ccb4f36c5b3df), C(391 a274cd5f5182d), C(76059e4 cb25d4700), C(e0af815fe1fa70ed), C(5 a6ccb4f36c5b3df), C(391 a274cd5f5182d), C(ff1579baa6a2b511), C(c385fc5062e8a728), C(e940749739a37c78), C(a093523a5b5edee5)}, {C(a22e8f6681f0267d), C(61e79 bc120729914), C(86ec13 c84c1600d3), C(1614811 d59dcab44), C(d1ddcca9a2675c33), C(f3c551d5fa617763), C(5 c78d4181402e98c), C(1614811 d59dcab44), C(d1ddcca9a2675c33), C(f3c551d5fa617763), C(5 c78d4181402e98c), C(b43b4a9caa6f5d4c), C(f112829bca2df8f3), C(87e5 c85db80d06c3), C(8eb4 bac85453409)}, {C(6997121 cae0ce362), C(ba3594cbcc299a07), C(7e4 b71c7de25a5e4), C(16 ad89e66db557ba), C(a43c401140ffc77d), C(3780 a8b3fd91e68), C(48190678248 a06b5), C(16 ad89e66db557ba), C(a43c401140ffc77d), C(3780 a8b3fd91e68), C(48190678248 a06b5), C(d10deb97b651ad42), C(3 a69f3f29046a24f), C(f7179735f2c6dab4), C(ac82965ad3b67a02)}, {C(9 bfc2c3e050a3c27), C(dc434110e1059ff3), C(5426055 da178decd), C(cb44d00207e16f99), C(9 d9e99afedc8107f), C(56907 c4fb7b3bc01), C(bcff1472bb01f85a), C(cb44d00207e16f99), C(9 d9e99afedc8107f), C(56907 c4fb7b3bc01), C(bcff1472bb01f85a), C(516f 800f 74 ad0985), C(f93193ade9614da4), C(9f 4 a7845355b75b7), C(423 c17045824dea5)}, {C(a3f37e415aedf14), C(8 d21c92bfa0dc545), C(a2715ebb07deaf80), C(98 ce1ff2b3f99f0f), C(162 acfd3b47c20bf), C(62 b9a25fd39dc6c0), C(c165c3c95c878dfe), C(98 ce1ff2b3f99f0f), C(162 acfd3b47c20bf), C(62 b9a25fd39dc6c0), C(c165c3c95c878dfe), C(2 b9a7e1f055bd27c), C(e91c8099cafaa75d), C(37e38 d64ef0263b), C(a46e89f47a1a70d5)}, {C(cef3c748045e7618), C(41dd 44f aef4ca301), C(6 add718a88f383c6), C(1197ec a317e70a93), C(61f 9497e6 cc4a33), C(22e7178 d1e57af73), C(5df 95 da0ff1c6435), C(1197ec a317e70a93), C(61f 9497e6 cc4a33), C(22e7178 d1e57af73), C(5df 95 da0ff1c6435), C(934327643705e56 c), C(11eb0 eec553137c9), C(1e6 b9b57ac5283ec), C(6934785 db184b2e4)}, {C(fe2b841766a4d212), C(42 cf817e58fe998c), C(29f 7f 493 ba9cbe6c), C(2 a9231d98b441827), C(fca55e769df78f6c), C(da87ea680eb14df4), C(e0b77394b0fd2bcc), C(2 a9231d98b441827), C(fca55e769df78f6c), C(da87ea680eb14df4), C(e0b77394b0fd2bcc), C(f36a2a3c73ab371a), C(d52659d36d93b71), C(3 d3b7d2d2fafbb14), C(b4b7b317d9266711)}, {C(d6131e688593a181), C(5 b658b282688ccd3), C(b9f7c066beed1204), C(e9dd79bad89f6b19), C(b420092bae6aaf41), C(515f 9 bbd06069d77), C(80664957 a02cbc29), C(e9dd79bad89f6b19), C(b420092bae6aaf41), C(515f 9 bbd06069d77), C(80664957 a02cbc29), C(f9dc7a744a56d9b3), C(7eb2 bdcd6667f383), C(c5914296fbdaf9d1), C(af0d5a8fec374fc4)}, {C(91288884ebf cf145), C(3df fd892d36403af), C(7 c4789db82755080), C(634 acbe037edec27), C(878 a97fab822d804), C(fcb042af908f0577), C(4 cbafc318bb90a2e), C(634 acbe037edec27), C(878 a97fab822d804), C(fcb042af908f0577), C(4 cbafc318bb90a2e), C(68 a96d589d5e5654), C(a752cb250bca1bc0), C(8f 228f 406024 aa7e), C(fc5408cf22a080b5)}, {C(754 c7e98ae3495ea), C(2030124 a22512c19), C(ec241579c626c39d), C(e682b5c87fa8e41b), C(6 cfa4baff26337ac), C(4 d66358112f09b2a), C(58889 d3f50ffa99c), C(33f c6ffd1ffb8676), C(36 db7617b765f6e2), C(8df 41 c03c514a9dc), C(6707 cc39a809bb74), C(3f 27 d7bb79e31984), C(a39dc6ac6cb0b0a8), C(33f c6ffd1ffb8676), C(36 db7617b765f6e2)}, }; void Check(uint64 expected, uint64 actual) { if (expected != actual) { fprintf(stderr, "ERROR: expected %llx, but got %llx", expected, actual); ++errors; } } void Test(const uint64 *expected, int offset, int len) { const uint128 u = CityHash128(data + offset, len); const uint128 v = CityHash128WithSeed(data + offset, len, kSeed128); Check(expected[0], CityHash64(data + offset, len)); Check(expected[1], CityHash64WithSeed(data + offset, len, kSeed0)); Check(expected[2], CityHash64WithSeeds(data + offset, len, kSeed0, kSeed1)); Check(expected[3], Uint128Low64(u)); Check(expected[4], Uint128High64(u)); Check(expected[5], Uint128Low64(v)); Check(expected[6], Uint128High64(v)); #ifdef __SSE4_2__ const uint128 y = CityHashCrc128(data + offset, len); const uint128 z = CityHashCrc128WithSeed(data + offset, len, kSeed128); uint64 crc256_results[4]; CityHashCrc256(data + offset, len, crc256_results); Check(expected[7], Uint128Low64(y)); Check(expected[8], Uint128High64(y)); Check(expected[9], Uint128Low64(z)); Check(expected[10], Uint128High64(z)); int i; for (i = 0; i < 4; i++) Check(expected[11 + i], crc256_results[i]); #endif } int main(int argc, char **argv) { setup(); int i; for (i = 0; i < kTestSize - 1; i++) Test(testdata[i], i * i, i); Test(testdata[i], 0, kDataSize); return errors > 0; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/support/city.c����������������������������������������������������������������0000664�0000000�0000000�00000035413�13242724102�0017333�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* city.c - cityhash-c * CityHash on C * Copyright (c) 2011-2012, Alexander Nusov * * - original copyright notice - * Copyright (c) 2011 Google, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * CityHash, by Geoff Pike and Jyrki Alakuijala * * This file provides CityHash64() and related functions. * * It's probably possible to create even faster hash functions by * writing a program that systematically explores some of the space of * possible hash functions, by using SIMD instructions, or by * compromising on hash quality. */ #include <string.h> #include "city.h" static uint64 UNALIGNED_LOAD64(const char *p) { uint64 result; memcpy(&result, p, sizeof(result)); return result; } static uint32 UNALIGNED_LOAD32(const char *p) { uint32 result; memcpy(&result, p, sizeof(result)); return result; } #if !defined(WORDS_BIGENDIAN) #define uint32_in_expected_order(x) (x) #define uint64_in_expected_order(x) (x) #else #ifdef _MSC_VER #include <stdlib.h> #define bswap_32(x) _byteswap_ulong(x) #define bswap_64(x) _byteswap_uint64(x) #elif defined(__APPLE__) /* Mac OS X / Darwin features */ #include <libkern/OSByteOrder.h> #define bswap_32(x) OSSwapInt32(x) #define bswap_64(x) OSSwapInt64(x) #else #include <byteswap.h> #endif #define uint32_in_expected_order(x) (bswap_32(x)) #define uint64_in_expected_order(x) (bswap_64(x)) #endif /* WORDS_BIGENDIAN */ #if !defined(LIKELY) #if HAVE_BUILTIN_EXPECT #define LIKELY(x) (__builtin_expect(!!(x), 1)) #else #define LIKELY(x) (x) #endif #endif static uint64 Fetch64(const char *p) { return uint64_in_expected_order(UNALIGNED_LOAD64(p)); } static uint32 Fetch32(const char *p) { return uint32_in_expected_order(UNALIGNED_LOAD32(p)); } /* Some primes between 2^63 and 2^64 for various uses. */ static const uint64 k0 = 0xc3a5c85c97cb3127ULL; static const uint64 k1 = 0xb492b66fbe98f273ULL; static const uint64 k2 = 0x9ae16a3b2f90404fULL; static const uint64 k3 = 0xc949d7c7509e6557ULL; /* Hash 128 input bits down to 64 bits of output. * This is intended to be a reasonably good hash function. */ static inline uint64 Hash128to64(const uint128 x) { /* Murmur-inspired hashing. */ const uint64 kMul = 0x9ddfea08eb382d69ULL; uint64 a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul; a ^= (a >> 47); uint64 b = (Uint128High64(x) ^ a) * kMul; b ^= (b >> 47); b *= kMul; return b; } /* Bitwise right rotate. Normally this will compile to a single * instruction, especially if the shift is a manifest constant. */ static uint64 Rotate(uint64 val, int shift) { /* Avoid shifting by 64: doing so yields an undefined result. */ return shift == 0 ? val : ((val >> shift) | (val << (64 - shift))); } /* Equivalent to Rotate(), but requires the second arg to be non-zero. * On x86-64, and probably others, it's possible for this to compile * to a single instruction if both args are already in registers. */ static uint64 RotateByAtLeast1(uint64 val, int shift) { return (val >> shift) | (val << (64 - shift)); } static uint64 ShiftMix(uint64 val) { return val ^ (val >> 47); } static uint64 HashLen16(uint64 u, uint64 v) { uint128 result; result.first = u; result.second = v; return Hash128to64(result); } static uint64 HashLen0to16(const char *s, size_t len) { if (len > 8) { uint64 a = Fetch64(s); uint64 b = Fetch64(s + len - 8); return HashLen16(a, RotateByAtLeast1(b + len, len)) ^ b; } if (len >= 4) { uint64 a = Fetch32(s); return HashLen16(len + (a << 3), Fetch32(s + len - 4)); } if (len > 0) { uint8 a = s[0]; uint8 b = s[len >> 1]; uint8 c = s[len - 1]; uint32 y = (uint32) (a) + ((uint32) (b) << 8); uint32 z = len + ((uint32) (c) << 2); return ShiftMix(y * k2 ^ z * k3) * k2; } return k2; } /* This probably works well for 16-byte strings as well, but it may be overkill * in that case. */ static uint64 HashLen17to32(const char *s, size_t len) { uint64 a = Fetch64(s) * k1; uint64 b = Fetch64(s + 8); uint64 c = Fetch64(s + len - 8) * k2; uint64 d = Fetch64(s + len - 16) * k0; return HashLen16(Rotate(a - b, 43) + Rotate(c, 30) + d, a + Rotate(b ^ k3, 20) - c + len); } /* Return a 16-byte hash for 48 bytes. Quick and dirty. * Callers do best to use "random-looking" values for a and b. * static pair<uint64, uint64> WeakHashLen32WithSeeds( */ uint128 WeakHashLen32WithSeeds6(uint64 w, uint64 x, uint64 y, uint64 z, uint64 a, uint64 b) { a += w; b = Rotate(b + a + z, 21); uint64 c = a; a += x; a += y; b += Rotate(a, 44); uint128 result; result.first = (uint64) (a + z); result.second = (uint64) (b + c); return result; } /* Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty. * static pair<uint64, uint64> WeakHashLen32WithSeeds( */ uint128 WeakHashLen32WithSeeds(const char *s, uint64 a, uint64 b) { return WeakHashLen32WithSeeds6(Fetch64(s), Fetch64(s + 8), Fetch64(s + 16), Fetch64(s + 24), a, b); } /* Return an 8-byte hash for 33 to 64 bytes. */ static uint64 HashLen33to64(const char *s, size_t len) { uint64 z = Fetch64(s + 24); uint64 a = Fetch64(s) + (len + Fetch64(s + len - 16)) * k0; uint64 b = Rotate(a + z, 52); uint64 c = Rotate(a, 37); a += Fetch64(s + 8); c += Rotate(a, 7); a += Fetch64(s + 16); uint64 vf = a + z; uint64 vs = b + Rotate(a, 31) + c; a = Fetch64(s + 16) + Fetch64(s + len - 32); z = Fetch64(s + len - 8); b = Rotate(a + z, 52); c = Rotate(a, 37); a += Fetch64(s + len - 24); c += Rotate(a, 7); a += Fetch64(s + len - 16); uint64 wf = a + z; uint64 ws = b + Rotate(a, 31) + c; uint64 r = ShiftMix((vf + ws) * k2 + (wf + vs) * k0); return ShiftMix(r * k0 + vs) * k2; } uint64 CityHash64(const char *s, size_t len) { if (len <= 32) { if (len <= 16) return HashLen0to16(s, len); else return HashLen17to32(s, len); } else if (len <= 64) return HashLen33to64(s, len); /* For strings over 64 bytes we hash the end first, and then as we * loop we keep 56 bytes of state: v, w, x, y, and z. */ uint64 x = Fetch64(s + len - 40); uint64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56); uint64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24)); uint64 temp; uint128 v = WeakHashLen32WithSeeds(s + len - 64, len, z); uint128 w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x); x = x * k1 + Fetch64(s); /* Decrease len to the nearest multiple of 64, * and operate on 64-byte chunks. */ len = (len - 1) & ~(size_t) (63); do { x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; x ^= w.second; y += v.first + Fetch64(s + 40); z = Rotate(z + w.first, 33) * k1; v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); temp = z; z = x; x = temp; s += 64; len -= 64; } while (len != 0); return HashLen16(HashLen16(v.first, w.first) + ShiftMix(y) * k1 + z, HashLen16(v.second, w.second) + x); } uint64 CityHash64WithSeed(const char *s, size_t len, uint64 seed) { return CityHash64WithSeeds(s, len, k2, seed); } uint64 CityHash64WithSeeds(const char *s, size_t len, uint64 seed0, uint64 seed1) { return HashLen16(CityHash64(s, len) - seed0, seed1); } /* A subroutine for CityHash128(). Returns a decent 128-bit hash for strings * of any length representable in signed long. Based on City and Murmur. */ static uint128 CityMurmur(const char *s, size_t len, uint128 seed) { uint64 a = Uint128Low64(seed); uint64 b = Uint128High64(seed); uint64 c = 0; uint64 d = 0; signed long l = len - 16; if (l <= 0) { /* len <= 16 */ a = ShiftMix(a * k1) * k1; c = b * k1 + HashLen0to16(s, len); d = ShiftMix(a + (len >= 8 ? Fetch64(s) : c)); } else { /* len > 16 */ c = HashLen16(Fetch64(s + len - 8) + k1, a); d = HashLen16(b + len, c + Fetch64(s + len - 16)); a += d; do { a ^= ShiftMix(Fetch64(s) * k1) * k1; a *= k1; b ^= a; c ^= ShiftMix(Fetch64(s + 8) * k1) * k1; c *= k1; d ^= c; s += 16; l -= 16; } while (l > 0); } a = HashLen16(a, c); b = HashLen16(d, b); uint128 result; result.first = (uint64) (a ^ b); result.second = (uint64) (HashLen16(b, a)); return result; } uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed) { if (len < 128) return CityMurmur(s, len, seed); /* We expect len >= 128 to be the common case. Keep 56 bytes of state: * v, w, x, y, and z. */ uint128 v, w; uint64 x = Uint128Low64(seed); uint64 y = Uint128High64(seed); uint64 z = len * k1; uint64 temp; v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s); v.second = Rotate(v.first, 42) * k1 + Fetch64(s + 8); w.first = Rotate(y + z, 35) * k1 + x; w.second = Rotate(x + Fetch64(s + 88), 53) * k1; /* This is the same inner loop as CityHash64(), manually unrolled. */ do { x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; x ^= w.second; y += v.first + Fetch64(s + 40); z = Rotate(z + w.first, 33) * k1; v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); temp = z; z = x; x = temp; s += 64; x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; x ^= w.second; y += v.first + Fetch64(s + 40); z = Rotate(z + w.first, 33) * k1; v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); temp = z; z = x; x = temp; s += 64; len -= 128; } while (LIKELY(len >= 128)); x += Rotate(v.first + z, 49) * k0; z += Rotate(w.first, 37) * k0; /* If 0 < len < 128, hash up to 4 chunks * of 32 bytes each from the end of s. */ size_t tail_done; for (tail_done = 0; tail_done < len;) { tail_done += 32; y = Rotate(x + y, 42) * k0 + v.second; w.first += Fetch64(s + len - tail_done + 16); x = x * k0 + w.first; z += w.second + Fetch64(s + len - tail_done); w.second += v.first; v = WeakHashLen32WithSeeds(s + len - tail_done, v.first + z, v.second); } /* At this point our 56 bytes of state should contain more than * enough information for a strong 128-bit hash. We use two * different 56-byte-to-8-byte hashes to get a 16-byte final result. */ x = HashLen16(x, v.first); y = HashLen16(y + z, w.first); uint128 result; result.first = (uint64) (HashLen16(x + v.second, w.second) + y); result.second = (uint64) HashLen16(x + w.second, y + v.second); return result; } uint128 CityHash128(const char *s, size_t len) { uint128 r; if (len >= 16) { r.first = (uint64) (Fetch64(s) ^ k3); r.second = (uint64) (Fetch64(s + 8)); return CityHash128WithSeed(s + 16, len - 16, r); } else if (len >= 8) { r.first = (uint64) (Fetch64(s) ^ (len * k0)); r.second = (uint64) (Fetch64(s + len - 8) ^ k1); return CityHash128WithSeed(NULL, 0, r); } else { r.first = (uint64) k0; r.second = (uint64) k1; return CityHash128WithSeed(s, len, r); } } #ifdef __SSE4_2__ #include "citycrc.h" #include <nmmintrin.h> /* Requires len >= 240. */ static void CityHashCrc256Long(const char *s, size_t len, uint32 seed, uint64 *result) { uint64 a = Fetch64(s + 56) + k0; uint64 b = Fetch64(s + 96) + k0; uint64 c = result[0] = HashLen16(b, len); uint64 d = result[1] = Fetch64(s + 120) * k0 + len; uint64 e = Fetch64(s + 184) + seed; uint64 f = seed; uint64 g = 0; uint64 h = 0; uint64 i = 0; uint64 j = 0; uint64 t = c + d; /* 240 bytes of input per iter. */ size_t iters = len / 240; len -= iters * 240; do { #define CHUNK(multiplier, z) \ do { \ uint64 old_a = a; \ \ a = Rotate(b, 41 ^ z) * multiplier + Fetch64(s); \ b = Rotate(c, 27 ^ z) * multiplier + Fetch64(s + 8); \ c = Rotate(d, 41 ^ z) * multiplier + Fetch64(s + 16); \ d = Rotate(e, 33 ^ z) * multiplier + Fetch64(s + 24); \ e = Rotate(t, 25 ^ z) * multiplier + Fetch64(s + 32); \ t = old_a; \ f = _mm_crc32_u64(f, a); \ g = _mm_crc32_u64(g, b); \ h = _mm_crc32_u64(h, c); \ i = _mm_crc32_u64(i, d); \ j = _mm_crc32_u64(j, e); \ s += 40; \ } while (0) CHUNK(1, 1); CHUNK(k0, 0); CHUNK(1, 1); CHUNK(k0, 0); CHUNK(1, 1); CHUNK(k0, 0); } while (--iters > 0); while (len >= 40) { CHUNK(k0, 0); len -= 40; } if (len > 0) { s = s + len - 40; CHUNK(k0, 0); } j += i << 32; a = HashLen16(a, j); h += g << 32; b += h; c = HashLen16(c, f) + i; d = HashLen16(d, e + result[0]); j += e; i += HashLen16(h, t); e = HashLen16(a, d) + j; f = HashLen16(b, c) + a; g = HashLen16(j, i) + c; result[0] = e + f + g + h; a = ShiftMix((a + g) * k0) * k0 + b; result[1] += a + result[0]; a = ShiftMix(a * k0) * k0 + c; result[2] = a + result[1]; a = ShiftMix((a + e) * k0) * k0; result[3] = a + result[2]; } /* Requires len < 240. */ static void CityHashCrc256Short(const char *s, size_t len, uint64 *result) { char buf[240]; memcpy(buf, s, len); memset(buf + len, 0, 240 - len); CityHashCrc256Long(buf, 240, ~(uint32) (len), result); } void CityHashCrc256(const char *s, size_t len, uint64 *result) { if (LIKELY(len >= 240)) CityHashCrc256Long(s, len, 0, result); else CityHashCrc256Short(s, len, result); } uint128 CityHashCrc128WithSeed(const char *s, size_t len, uint128 seed) { if (len <= 900) { return CityHash128WithSeed(s, len, seed); } else { uint64 result[4]; CityHashCrc256(s, len, result); uint64 u = Uint128High64(seed) + result[0]; uint64 v = Uint128Low64(seed) + result[1]; uint128 crc; crc.first = (uint64) (HashLen16(u, v + result[2])); crc.second = (uint64) (HashLen16(Rotate(v, 32), u * k0 + result[3])); return crc; } } uint128 CityHashCrc128(const char *s, size_t len) { if (len <= 900) { return CityHash128(s, len); } else { uint64 result[4]; CityHashCrc256(s, len, result); uint128 crc; crc.first = (uint64) result[2]; crc.second = (uint64) result[3]; return crc; } } #endif �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/support/client_mgr.c����������������������������������������������������������0000664�0000000�0000000�00000062406�13242724102�0020510�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2013 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @defgroup clntmmt Client management * @{ */ /** * @file client_mgr.c * @author Jim Lieb <jlieb@panasas.com> * @brief Protocol client manager */ #include "config.h" #include <time.h> #include <unistd.h> #include <sys/socket.h> #ifdef RPC_VSOCK #include <linux/vm_sockets.h> #endif /* VSOCK */ #include <sys/types.h> #include <sys/param.h> #include <pthread.h> #include <assert.h> #include <arpa/inet.h> #include "gsh_list.h" #include "fsal.h" #include "nfs_core.h" #include "log.h" #include "avltree.h" #include "gsh_types.h" #ifdef USE_DBUS #include "gsh_dbus.h" #endif #include "client_mgr.h" #include "export_mgr.h" #include "server_stats_private.h" #include "abstract_atomic.h" #include "gsh_intrinsic.h" #include "server_stats.h" #include "sal_functions.h" /* Clients are stored in an AVL tree */ struct client_by_ip { struct avltree t; pthread_rwlock_t lock; struct avltree_node **cache; uint32_t cache_sz; }; static struct client_by_ip client_by_ip; /** * @brief Compute cache slot for an entry * * This function computes a hash slot, taking an address modulo the * number of cache slotes (which should be prime). * * @param wt [in] The table * @param ptr [in] Entry address * * @return The computed offset. */ static inline uint32_t eip_cache_offsetof(struct client_by_ip *eid, uint32_t k) { return k % eid->cache_sz; } /** * @brief IP address comparator for AVL tree walk * * We tell the difference between IPv4 and IPv6 addresses * by size (4 vs. 16). IPv4 addresses are "lower", left, sorted * first. */ static int client_ip_cmpf(const struct avltree_node *lhs, const struct avltree_node *rhs) { struct gsh_client *lk, *rk; lk = avltree_container_of(lhs, struct gsh_client, node_k); rk = avltree_container_of(rhs, struct gsh_client, node_k); if (lk->addr.len != rk->addr.len) return (lk->addr.len < rk->addr.len) ? -1 : 1; else return memcmp(lk->addr.addr, rk->addr.addr, lk->addr.len); } /** * @brief Lookup the client manager struct for this client IP * * Lookup the client manager struct by client host IP address. * IPv4 and IPv6 addresses both handled. Sets a reference on the * block. * * @param[in] client_ipaddr The sockaddr struct with the v4/v6 address * @param[in] lookup_only If true, only look up, don't create * * @return pointer to ref locked stats block */ struct gsh_client *get_gsh_client(sockaddr_t *client_ipaddr, bool lookup_only) { struct avltree_node *node = NULL; struct gsh_client *cl; struct server_stats *server_st; struct gsh_client v; char hoststr[SOCK_NAME_MAX]; uint8_t *addr = NULL; uint32_t ipaddr; int addr_len = 0; void **cache_slot; switch (client_ipaddr->ss_family) { case AF_INET: addr = (uint8_t *) &((struct sockaddr_in *)client_ipaddr)-> sin_addr; addr_len = 4; memcpy(&ipaddr, (uint8_t *) &((struct sockaddr_in *)client_ipaddr)-> sin_addr, sizeof(ipaddr)); break; case AF_INET6: addr = (uint8_t *) &((struct sockaddr_in6 *)client_ipaddr)-> sin6_addr; addr_len = 16; memcpy(&ipaddr, (uint8_t *) &((struct sockaddr_in6 *)client_ipaddr)-> sin6_addr, sizeof(ipaddr)); break; #ifdef RPC_VSOCK case AF_VSOCK: { struct sockaddr_vm *svm; /* XXX checkpatch bs */ svm = (struct sockaddr_vm *)client_ipaddr; addr = (uint8_t *)&(svm->svm_cid); addr_len = sizeof(svm->svm_cid); ipaddr = svm->svm_cid; } break; #endif /* VSOCK */ default: assert(0); } v.addr.addr = addr; v.addr.len = addr_len; PTHREAD_RWLOCK_rdlock(&client_by_ip.lock); /* check cache */ cache_slot = (void **) &(client_by_ip.cache[eip_cache_offsetof(&client_by_ip, ipaddr)]); node = (struct avltree_node *)atomic_fetch_voidptr(cache_slot); if (node) { if (client_ip_cmpf(&v.node_k, node) == 0) { /* got it in 1 */ LogDebug(COMPONENT_HASHTABLE_CACHE, "client_mgr cache hit slot %d", eip_cache_offsetof(&client_by_ip, ipaddr)); cl = avltree_container_of(node, struct gsh_client, node_k); goto out; } } /* fall back to AVL */ node = avltree_lookup(&v.node_k, &client_by_ip.t); if (node) { cl = avltree_container_of(node, struct gsh_client, node_k); /* update cache */ atomic_store_voidptr(cache_slot, node); goto out; } else if (lookup_only) { PTHREAD_RWLOCK_unlock(&client_by_ip.lock); return NULL; } PTHREAD_RWLOCK_unlock(&client_by_ip.lock); server_st = gsh_calloc(1, (sizeof(struct server_stats) + addr_len)); cl = &server_st->client; memcpy(cl->addrbuf, addr, addr_len); cl->addr.addr = cl->addrbuf; cl->addr.len = addr_len; cl->refcnt = 0; /* we will hold a ref starting out... */ sprint_sockip(client_ipaddr, hoststr, SOCK_NAME_MAX); cl->hostaddr_str = gsh_strdup(hoststr); PTHREAD_RWLOCK_wrlock(&client_by_ip.lock); node = avltree_insert(&cl->node_k, &client_by_ip.t); if (node) { gsh_free(cl->hostaddr_str); gsh_free(server_st); /* somebody beat us to it */ cl = avltree_container_of(node, struct gsh_client, node_k); } else { PTHREAD_RWLOCK_init(&cl->lock, NULL); } out: inc_gsh_client_refcount(cl); PTHREAD_RWLOCK_unlock(&client_by_ip.lock); return cl; } /** * @brief Release the client management struct * * We are done with it, let it go. */ void put_gsh_client(struct gsh_client *client) { int64_t new_refcnt; new_refcnt = atomic_dec_int64_t(&client->refcnt); assert(new_refcnt >= 0); } /** * @brief Remove a client from the AVL and free its resources * * @param client_ipaddr [IN] sockaddr (key) to remove * * @retval 0 if removed * @retval ENOENT if not found * @retval EBUSY if in use */ int remove_gsh_client(sockaddr_t *client_ipaddr) { struct avltree_node *node = NULL; struct avltree_node *cnode = NULL; struct gsh_client *cl = NULL; struct server_stats *server_st; struct gsh_client v; uint8_t *addr = NULL; uint32_t ipaddr; int addr_len = 0; int removed = 0; void **cache_slot; switch (client_ipaddr->ss_family) { case AF_INET: addr = (uint8_t *) &((struct sockaddr_in *)client_ipaddr)-> sin_addr; addr_len = 4; memcpy(&ipaddr, (uint8_t *) &((struct sockaddr_in *)client_ipaddr)-> sin_addr, sizeof(ipaddr)); break; case AF_INET6: addr = (uint8_t *) &((struct sockaddr_in6 *)client_ipaddr)-> sin6_addr; addr_len = 16; memcpy(&ipaddr, (uint8_t *) &((struct sockaddr_in6 *)client_ipaddr)-> sin6_addr, sizeof(ipaddr)); break; #ifdef RPC_VSOCK case AF_VSOCK: { struct sockaddr_vm *svm; /* XXX checkpatch horror */ svm = (struct sockaddr_vm *)client_ipaddr; addr = (uint8_t *)&(svm->svm_cid); addr_len = sizeof(svm->svm_cid); ipaddr = svm->svm_cid; } break; #endif /* VSOCK */ default: assert(0); } v.addr.addr = addr; v.addr.len = addr_len; PTHREAD_RWLOCK_wrlock(&client_by_ip.lock); node = avltree_lookup(&v.node_k, &client_by_ip.t); if (node) { cl = avltree_container_of(node, struct gsh_client, node_k); if (atomic_fetch_int64_t(&cl->refcnt) > 0) { removed = EBUSY; goto out; } cache_slot = (void **) &(client_by_ip.cache[eip_cache_offsetof( &client_by_ip, ipaddr)]); cnode = (struct avltree_node *)atomic_fetch_voidptr(cache_slot); if (node == cnode) atomic_store_voidptr(cache_slot, NULL); avltree_remove(node, &client_by_ip.t); } else { removed = ENOENT; } out: PTHREAD_RWLOCK_unlock(&client_by_ip.lock); if (removed == 0) { server_st = container_of(cl, struct server_stats, client); server_stats_free(&server_st->st); if (cl->hostaddr_str != NULL) gsh_free(cl->hostaddr_str); gsh_free(server_st); } return removed; } /** * @ Walk the tree and do the callback on each node * * @param cb [IN] Callback function * @param state [IN] param block to pass */ int foreach_gsh_client(bool(*cb) (struct gsh_client *cl, void *state), void *state) { struct avltree_node *client_node; struct gsh_client *cl; int cnt = 0; PTHREAD_RWLOCK_rdlock(&client_by_ip.lock); for (client_node = avltree_first(&client_by_ip.t); client_node != NULL; client_node = avltree_next(client_node)) { cl = avltree_container_of(client_node, struct gsh_client, node_k); if (!cb(cl, state)) break; cnt++; } PTHREAD_RWLOCK_unlock(&client_by_ip.lock); return cnt; } #ifdef USE_DBUS /* DBUS helpers */ /* parse the ipaddr string in args */ static bool arg_ipaddr(DBusMessageIter *args, sockaddr_t *sp, char **errormsg) { char *client_addr; unsigned char addrbuf[16]; bool success = true; /* XXX AF_VSOCK addresses are not self-describing--and one might * question whether inet addresses really are, either...so? */ if (args == NULL) { success = false; *errormsg = "message has no arguments"; } else if (dbus_message_iter_get_arg_type(args) != DBUS_TYPE_STRING) { success = false; *errormsg = "arg not a string"; } else { dbus_message_iter_get_basic(args, &client_addr); if (inet_pton(AF_INET, client_addr, addrbuf) == 1) { sp->ss_family = AF_INET; memcpy(&(((struct sockaddr_in *)sp)->sin_addr), addrbuf, 4); } else if (inet_pton(AF_INET6, client_addr, addrbuf) == 1) { sp->ss_family = AF_INET6; memcpy(&(((struct sockaddr_in6 *)sp)->sin6_addr), addrbuf, 16); } else { success = false; *errormsg = "can't decode client address"; } } return success; } /* DBUS interface(s) */ /* org.ganesha.nfsd.clienttmgr interface */ /** * @brief Add a client into the client manager via DBUS * * DBUS interface method call * * @param args [IN] dbus argument stream from the message * @param reply [OUT] dbus reply stream for method to fill */ static bool gsh_client_addclient(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { struct gsh_client *client; sockaddr_t sockaddr; bool success = true; char *errormsg = "OK"; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); success = arg_ipaddr(args, &sockaddr, &errormsg); if (success) { client = get_gsh_client(&sockaddr, false); if (client != NULL) { put_gsh_client(client); } else { success = false; errormsg = "No memory to insert client"; } } dbus_status_reply(&iter, success, errormsg); return true; } static struct gsh_dbus_method cltmgr_add_client = { .name = "AddClient", .method = gsh_client_addclient, .args = {IPADDR_ARG, STATUS_REPLY, END_ARG_LIST} }; static bool gsh_client_removeclient(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { sockaddr_t sockaddr; bool success = false; char *errormsg = "OK"; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); if (arg_ipaddr(args, &sockaddr, &errormsg)) { switch (remove_gsh_client(&sockaddr)) { case 0: errormsg = "OK"; success = true; break; case ENOENT: errormsg = "Client with that address not found"; break; case EBUSY: errormsg = "Client with that address is in use (busy)"; break; default: errormsg = "Unexpected error"; } } dbus_status_reply(&iter, success, errormsg); return true; } static struct gsh_dbus_method cltmgr_remove_client = { .name = "RemoveClient", .method = gsh_client_removeclient, .args = {IPADDR_ARG, STATUS_REPLY, END_ARG_LIST} }; struct showclients_state { DBusMessageIter client_iter; }; static bool client_to_dbus(struct gsh_client *cl_node, void *state) { struct showclients_state *iter_state = (struct showclients_state *)state; struct server_stats *cl; char ipaddr[64]; const char *addrp; int addr_type; DBusMessageIter struct_iter; struct timespec last_as_ts = ServerBootTime; cl = container_of(cl_node, struct server_stats, client); addr_type = (cl_node->addr.len == 4) ? AF_INET : AF_INET6; addrp = inet_ntop(addr_type, cl_node->addr.addr, ipaddr, sizeof(ipaddr)); timespec_add_nsecs(cl_node->last_update, &last_as_ts); dbus_message_iter_open_container(&iter_state->client_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &addrp); server_stats_summary(&struct_iter, &cl->st); dbus_append_timestamp(&struct_iter, &last_as_ts); dbus_message_iter_close_container(&iter_state->client_iter, &struct_iter); return true; } static bool gsh_client_showclients(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { DBusMessageIter iter; struct showclients_state iter_state; struct timespec timestamp; now(×tamp); /* create a reply from the message */ dbus_message_iter_init_append(reply, &iter); dbus_append_timestamp(&iter, ×tamp); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sbbbbbbbb(tt))", &iter_state.client_iter); (void)foreach_gsh_client(client_to_dbus, (void *)&iter_state); dbus_message_iter_close_container(&iter, &iter_state.client_iter); return true; } static struct gsh_dbus_method cltmgr_show_clients = { .name = "ShowClients", .method = gsh_client_showclients, .args = {TIMESTAMP_REPLY, { .name = "clients", .type = "a(sbbbbbbbb(tt))", .direction = "out"}, END_ARG_LIST} }; /* Reset Client specific stats counters */ void reset_client_stats(void) { struct avltree_node *client_node; struct gsh_client *cl; struct server_stats *clnt; PTHREAD_RWLOCK_rdlock(&client_by_ip.lock); for (client_node = avltree_first(&client_by_ip.t); client_node != NULL; client_node = avltree_next(client_node)) { cl = avltree_container_of(client_node, struct gsh_client, node_k); clnt = container_of(cl, struct server_stats, client); reset_gsh_stats(&clnt->st); } PTHREAD_RWLOCK_unlock(&client_by_ip.lock); } static struct gsh_dbus_method *cltmgr_client_methods[] = { &cltmgr_add_client, &cltmgr_remove_client, &cltmgr_show_clients, NULL }; static struct gsh_dbus_interface cltmgr_client_table = { .name = "org.ganesha.nfsd.clientmgr", .props = NULL, .methods = cltmgr_client_methods, .signals = NULL }; /* org.ganesha.nfsd.clientstats interface */ /* DBUS client manager stats helpers */ static struct gsh_client *lookup_client(DBusMessageIter *args, char **errormsg) { sockaddr_t sockaddr; struct gsh_client *client = NULL; bool success = true; success = arg_ipaddr(args, &sockaddr, errormsg); if (success) { client = get_gsh_client(&sockaddr, true); if (client == NULL) *errormsg = "Client IP address not found"; } return client; } /** * DBUS method to report NFSv3 I/O statistics * */ static bool get_nfsv3_stats_io(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { struct gsh_client *client = NULL; struct server_stats *server_st = NULL; bool success = true; char *errormsg = NULL; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); if (!nfs_param.core_param.enable_NFSSTATS) errormsg = "NFS stat counting disabled"; client = lookup_client(args, &errormsg); if (client == NULL) { success = false; if (errormsg == NULL) errormsg = "Client IP address not found"; } else { server_st = container_of(client, struct server_stats, client); if (server_st->st.nfsv3 == NULL) { success = false; errormsg = "Client does not have any NFSv3 activity"; } } dbus_status_reply(&iter, success, errormsg); if (success) server_dbus_v3_iostats(server_st->st.nfsv3, &iter); if (client != NULL) put_gsh_client(client); return true; } static struct gsh_dbus_method cltmgr_show_v3_io = { .name = "GetNFSv3IO", .method = get_nfsv3_stats_io, .args = {IPADDR_ARG, STATUS_REPLY, TIMESTAMP_REPLY, IOSTATS_REPLY, END_ARG_LIST} }; /** * DBUS method to report NFSv40 I/O statistics * */ static bool get_nfsv40_stats_io(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { struct gsh_client *client = NULL; struct server_stats *server_st = NULL; bool success = true; char *errormsg = "OK"; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); if (!nfs_param.core_param.enable_NFSSTATS) errormsg = "NFS stat counting disabled"; client = lookup_client(args, &errormsg); if (client == NULL) { success = false; if (errormsg == NULL) errormsg = "Client IP address not found"; } else { server_st = container_of(client, struct server_stats, client); if (server_st->st.nfsv40 == NULL) { success = false; errormsg = "Client does not have any NFSv4.0 activity"; } } dbus_status_reply(&iter, success, errormsg); if (success) server_dbus_v40_iostats(server_st->st.nfsv40, &iter); if (client != NULL) put_gsh_client(client); return true; } static struct gsh_dbus_method cltmgr_show_v40_io = { .name = "GetNFSv40IO", .method = get_nfsv40_stats_io, .args = {IPADDR_ARG, STATUS_REPLY, TIMESTAMP_REPLY, IOSTATS_REPLY, END_ARG_LIST} }; /** * DBUS method to report NFSv41 I/O statistics * */ static bool get_nfsv41_stats_io(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { struct gsh_client *client = NULL; struct server_stats *server_st = NULL; bool success = true; char *errormsg = "OK"; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); if (!nfs_param.core_param.enable_NFSSTATS) errormsg = "NFS stat counting disabled"; client = lookup_client(args, &errormsg); if (client == NULL) { success = false; if (errormsg == NULL) errormsg = "Client IP address not found"; } else { server_st = container_of(client, struct server_stats, client); if (server_st->st.nfsv41 == NULL) { success = false; errormsg = "Client does not have any NFSv4.1 activity"; } } dbus_status_reply(&iter, success, errormsg); if (success) server_dbus_v41_iostats(server_st->st.nfsv41, &iter); if (client != NULL) put_gsh_client(client); return true; } static struct gsh_dbus_method cltmgr_show_v41_io = { .name = "GetNFSv41IO", .method = get_nfsv41_stats_io, .args = {IPADDR_ARG, STATUS_REPLY, TIMESTAMP_REPLY, IOSTATS_REPLY, END_ARG_LIST} }; /** * DBUS method to report NFSv41 layout statistics * */ static bool get_nfsv41_stats_layouts(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { struct gsh_client *client = NULL; struct server_stats *server_st = NULL; bool success = true; char *errormsg = "OK"; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); if (!nfs_param.core_param.enable_NFSSTATS) errormsg = "NFS stat counting disabled"; client = lookup_client(args, &errormsg); if (client == NULL) { success = false; if (errormsg == NULL) errormsg = "Client IP address not found"; } else { server_st = container_of(client, struct server_stats, client); if (server_st->st.nfsv41 == NULL) { success = false; errormsg = "Client does not have any NFSv4.1 activity"; } } dbus_status_reply(&iter, success, errormsg); if (success) server_dbus_v41_layouts(server_st->st.nfsv41, &iter); if (client != NULL) put_gsh_client(client); return true; } static struct gsh_dbus_method cltmgr_show_v41_layouts = { .name = "GetNFSv41Layouts", .method = get_nfsv41_stats_layouts, .args = {IPADDR_ARG, STATUS_REPLY, TIMESTAMP_REPLY, LAYOUTS_REPLY, END_ARG_LIST} }; /** * DBUS method to report NFSv4 delegation statistics */ static bool get_stats_delegations(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { char *errormsg = "OK"; struct gsh_client *client = NULL; struct server_stats *server_st = NULL; bool success = true; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); client = lookup_client(args, &errormsg); if (client == NULL) { success = false; errormsg = "Client IP address not found"; } else { server_st = container_of(client, struct server_stats, client); if (server_st->st.deleg == NULL) { success = false; errormsg = "Client does not have any Delegation activity"; } } dbus_status_reply(&iter, success, errormsg); if (success) server_dbus_delegations(server_st->st.deleg, &iter); if (client != NULL) put_gsh_client(client); return true; } static struct gsh_dbus_method cltmgr_show_delegations = { .name = "GetDelegations", .method = get_stats_delegations, .args = {IPADDR_ARG, STATUS_REPLY, TIMESTAMP_REPLY, DELEG_REPLY, END_ARG_LIST} }; #ifdef _USE_9P /** * DBUS method to report 9p I/O statistics * */ static bool get_9p_stats_io(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { struct gsh_client *client = NULL; struct server_stats *server_st = NULL; bool success = true; char *errormsg = "OK"; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); client = lookup_client(args, &errormsg); if (client == NULL) { success = false; if (errormsg == NULL) errormsg = "Client IP address not found"; } else { server_st = container_of(client, struct server_stats, client); if (server_st->st._9p == NULL) { success = false; errormsg = "Client does not have any 9p activity"; } } dbus_status_reply(&iter, success, errormsg); if (success) server_dbus_9p_iostats(server_st->st._9p, &iter); if (client != NULL) put_gsh_client(client); return true; } static struct gsh_dbus_method cltmgr_show_9p_io = { .name = "Get9pIO", .method = get_9p_stats_io, .args = {IPADDR_ARG, STATUS_REPLY, TIMESTAMP_REPLY, IOSTATS_REPLY, END_ARG_LIST} }; /** * DBUS method to report 9p transport statistics * */ static bool get_9p_stats_trans(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { struct gsh_client *client = NULL; struct server_stats *server_st = NULL; bool success = true; char *errormsg = "OK"; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); client = lookup_client(args, &errormsg); if (client == NULL) { success = false; if (errormsg == NULL) errormsg = "Client IP address not found"; } else { server_st = container_of(client, struct server_stats, client); if (server_st->st._9p == NULL) { success = false; errormsg = "Client does not have any 9p activity"; } } dbus_status_reply(&iter, success, errormsg); if (success) server_dbus_9p_transstats(server_st->st._9p, &iter); if (client != NULL) put_gsh_client(client); return true; } static struct gsh_dbus_method cltmgr_show_9p_trans = { .name = "Get9pTrans", .method = get_9p_stats_trans, .args = {IPADDR_ARG, STATUS_REPLY, TIMESTAMP_REPLY, TRANSPORT_REPLY, END_ARG_LIST} }; /** * DBUS method to report 9p protocol operation statistics * */ static bool get_9p_client_op_stats(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { struct gsh_client *client = NULL; struct server_stats *server_st = NULL; u8 opcode; bool success = true; char *errormsg = "OK"; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); client = lookup_client(args, &errormsg); if (client == NULL) { success = false; } else { server_st = container_of(client, struct server_stats, client); if (server_st->st._9p == NULL) { success = false; errormsg = "Client does not have any 9p activity"; } } dbus_message_iter_next(args); if (success) success = arg_9p_op(args, &opcode, &errormsg); dbus_status_reply(&iter, success, errormsg); if (success) server_dbus_9p_opstats(server_st->st._9p, opcode, &iter); if (client != NULL) put_gsh_client(client); return true; } static struct gsh_dbus_method cltmgr_show_9p_op_stats = { .name = "Get9pOpStats", .method = get_9p_client_op_stats, .args = {IPADDR_ARG, _9P_OP_ARG, STATUS_REPLY, TIMESTAMP_REPLY, OP_STATS_REPLY, END_ARG_LIST} }; #endif static struct gsh_dbus_method *cltmgr_stats_methods[] = { &cltmgr_show_v3_io, &cltmgr_show_v40_io, &cltmgr_show_v41_io, &cltmgr_show_v41_layouts, &cltmgr_show_delegations, #ifdef _USE_9P &cltmgr_show_9p_io, &cltmgr_show_9p_trans, &cltmgr_show_9p_op_stats, #endif NULL }; static struct gsh_dbus_interface cltmgr_stats_table = { .name = "org.ganesha.nfsd.clientstats", .props = NULL, .methods = cltmgr_stats_methods, .signals = NULL }; /* DBUS list of interfaces on /org/ganesha/nfsd/ClientMgr */ static struct gsh_dbus_interface *cltmgr_interfaces[] = { &cltmgr_client_table, &cltmgr_stats_table, NULL }; void dbus_client_init(void) { gsh_dbus_register_path("ClientMgr", cltmgr_interfaces); } #endif /* USE_DBUS */ /** * @brief Initialize client manager */ void client_pkginit(void) { pthread_rwlockattr_t rwlock_attr; pthread_rwlockattr_init(&rwlock_attr); #ifdef GLIBC pthread_rwlockattr_setkind_np( &rwlock_attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP); #endif PTHREAD_RWLOCK_init(&client_by_ip.lock, &rwlock_attr); avltree_init(&client_by_ip.t, client_ip_cmpf, 0); client_by_ip.cache_sz = 32767; client_by_ip.cache = gsh_calloc(client_by_ip.cache_sz, sizeof(struct avltree_node *)); } /** @} */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/support/delayed_exec.c��������������������������������������������������������0000664�0000000�0000000�00000022012�13242724102�0020765�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 2013, The Linux Box Corporation * * Some portions copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @addtogroup delayed * @{ */ /** * @file delayed_exec.c * @author Adam C. Emerson <aemerson@linuxbox.com> * @brief Implementation of the delayed execution system */ #include "config.h" #include <pthread.h> #ifdef LINUX #include <sys/signal.h> #elif FREEBSD #include <signal.h> #endif #include "abstract_mem.h" #include "delayed_exec.h" #include "log.h" #include "avltree.h" #include "misc/queue.h" #include "gsh_intrinsic.h" #include "common_utils.h" /** * @brief A list of tasks */ LIST_HEAD(delayed_tasklist, delayed_task); /** * @brief All tasks executing at a given time. */ struct delayed_multi { struct timespec realtime; /*< A wallclock time at which to perform the given task. */ struct delayed_tasklist list; /*< The linked list of tasks to perform. */ struct avltree_node node; /*< Our node in the tree */ }; /** * @brief An individual delayed task */ struct delayed_task { /** Function for delayed task */ void (*func)(void *); /** Argument for delayed task */ void *arg; /** Link in the task list. */ LIST_ENTRY(delayed_task) link; }; /** * @brief A list of threads */ LIST_HEAD(delayed_threadlist, delayed_thread); /** * @brief An individual executor thread */ struct delayed_thread { pthread_t id; /*< Thread id */ LIST_ENTRY(delayed_thread) link; /*< Link in the thread list. */ }; /** * @{ * Delayed execution state. */ /** list of all threads */ static struct delayed_threadlist thread_list; /** Mutex for delayed execution */ static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; /** Condition variable for delayed execution */ static pthread_cond_t cv = PTHREAD_COND_INITIALIZER; /** The timer tree */ static struct avltree tree; /** * @brief Posssible states for the delayed executor */ enum delayed_state { delayed_running, /*< Executor is running */ delayed_stopping /*< Executor is stopping */ }; /** State for the executor */ static enum delayed_state delayed_state; enum delayed_employment { delayed_employed, /*< Work is available, do it. */ delayed_on_break, /*< Work is available, but wait for it */ delayed_unemployed /*< No work is available, wait indefinitely */ }; /** @} */ static int comparator(const struct avltree_node *a, const struct avltree_node *b) { struct delayed_multi *p = avltree_container_of(a, struct delayed_multi, node); struct delayed_multi *q = avltree_container_of(b, struct delayed_multi, node); return gsh_time_cmp(&p->realtime, &q->realtime); } /** * @brief Get a task to perform * * This function must be called with the mutex held. * * @param[out] when The time for which to wait, if there is one * @param[out] func The function to execute, if there is one * @param[out] arg The argument to supply, if there is one * * @retval delayed_employed if there is a task to perform now. Its * argument and function are stored in @c arg and @c func. * @retval delayed_on_break if there is work but it is not to be * performed yet. The time at which it is to be performed is * set in *when. * @retval delayed_unemployed if there is no work at all. The caller * should wait indefinitely to be signalled. */ static enum delayed_employment delayed_get_work(struct timespec *when, void (**func) (void *), void **arg) { struct avltree_node *first = avltree_first(&tree); struct delayed_multi *mul; struct timespec current; struct delayed_task *task; if (first == NULL) return delayed_unemployed; now(¤t); mul = avltree_container_of(first, struct delayed_multi, node); if (gsh_time_cmp(&mul->realtime, ¤t) > 0) { *when = mul->realtime; return delayed_on_break; } task = LIST_FIRST(&mul->list); *func = task->func; *arg = task->arg; LIST_REMOVE(task, link); gsh_free(task); if (LIST_EMPTY(&mul->list)) { avltree_remove(first, &tree); gsh_free(mul); } return delayed_employed; } /** * @brief Thread function to execute delayed tasks * * @param[in] arg The thread entry (cast to void) * * @return NULL, always and forever. */ void *delayed_thread(void *arg) { struct delayed_thread *thr = arg; int old_type = 0; int old_state = 0; sigset_t old_sigmask; SetNameFunction("Async"); /* Excplicitly and definitely enable cancellation */ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_state); /** * @see fridgethr_start_routine on asynchronous cancellation */ pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old_type); pthread_sigmask(SIG_SETMASK, NULL, &old_sigmask); PTHREAD_MUTEX_lock(&mtx); while (delayed_state == delayed_running) { struct timespec then; void (*func)(void *); void *arg; switch (delayed_get_work(&then, &func, &arg)) { case delayed_unemployed: pthread_cond_wait(&cv, &mtx); break; case delayed_on_break: pthread_cond_timedwait(&cv, &mtx, &then); break; case delayed_employed: PTHREAD_MUTEX_unlock(&mtx); func(arg); PTHREAD_MUTEX_lock(&mtx); break; } } LIST_REMOVE(thr, link); if (LIST_EMPTY(&thread_list)) pthread_cond_broadcast(&cv); PTHREAD_MUTEX_unlock(&mtx); gsh_free(thr); return NULL; } /** * @brief Initialize and start the delayed execution system */ void delayed_start(void) { /* Make this a parameter later */ const size_t threads_to_start = 1; /* Thread attributes */ pthread_attr_t attr; /* Thread index */ int i; LIST_INIT(&thread_list); avltree_init(&tree, comparator, 0); if (threads_to_start == 0) { LogFatal(COMPONENT_THREAD, "You can't execute tasks with zero threads."); } if (pthread_attr_init(&attr) != 0) LogFatal(COMPONENT_THREAD, "can't init pthread's attributes"); if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0) LogFatal(COMPONENT_THREAD, "can't set pthread's join state"); PTHREAD_MUTEX_lock(&mtx); delayed_state = delayed_running; for (i = 0; i < threads_to_start; ++i) { struct delayed_thread *thread = gsh_malloc(sizeof(struct delayed_thread)); int rc = 0; rc = pthread_create(&thread->id, &attr, delayed_thread, thread); if (rc != 0) { LogFatal(COMPONENT_THREAD, "Unable to start delayed executor: %d", rc); } LIST_INSERT_HEAD(&thread_list, thread, link); } PTHREAD_MUTEX_unlock(&mtx); } /** * @brief Shut down the delayed executor */ void delayed_shutdown(void) { int rc = -1; struct timespec then; now(&then); then.tv_sec += 120; PTHREAD_MUTEX_lock(&mtx); delayed_state = delayed_stopping; pthread_cond_broadcast(&cv); while ((rc != ETIMEDOUT) && !LIST_EMPTY(&thread_list)) rc = pthread_cond_timedwait(&cv, &mtx, &then); if (!LIST_EMPTY(&thread_list)) { struct delayed_thread *thr; LogMajor(COMPONENT_THREAD, "Delayed executor threads not shutting down cleanly, taking harsher measures."); while ((thr = LIST_FIRST(&thread_list)) != NULL) { LIST_REMOVE(thr, link); pthread_cancel(thr->id); gsh_free(thr); } } PTHREAD_MUTEX_unlock(&mtx); } /** * @brief Submit a new task * * @param[in] func The function to run * @param[in] arg The argument to run it with * @param[in] delay The dleay in nanoseconds * * @retval 0 on success. * @retval ENOMEM on inability to allocate memory causing other than success. */ int delayed_submit(void (*func) (void *), void *arg, nsecs_elapsed_t delay) { struct delayed_multi *mul = NULL; struct delayed_task *task = NULL; struct avltree_node *collision = NULL; struct avltree_node *first = NULL; mul = gsh_malloc(sizeof(struct delayed_multi)); task = gsh_malloc(sizeof(struct delayed_task)); now(&mul->realtime); timespec_add_nsecs(delay, &mul->realtime); PTHREAD_MUTEX_lock(&mtx); first = avltree_first(&tree); collision = avltree_insert(&mul->node, &tree); if (unlikely(collision)) { gsh_free(mul); /* There is already a node for this exact time in the tree. Add our task to its list. */ mul = avltree_container_of(collision, struct delayed_multi, node); } else { LIST_INIT(&mul->list); } task->func = func; task->arg = arg; LIST_INSERT_HEAD(&mul->list, task, link); if (!first || comparator(&mul->node, first) < 0) pthread_cond_broadcast(&cv); PTHREAD_MUTEX_unlock(&mtx); return 0; } /** @} */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/support/ds.c������������������������������������������������������������������0000664�0000000�0000000�00000031756�13242724102�0016777�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) CohortFS (2014) * contributor : William Allen Simpson <bill@CohortFS.com> * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file ds.c * @brief Data Server parsing and management */ #include "config.h" #include "config_parsing.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "FSAL/fsal_commonlib.h" #include "pnfs_utils.h" /** * @brief Servers are stored in an AVL tree with front-end cache. * * @note number of cache slots should be prime. */ #define SERVER_BY_ID_CACHE_SIZE 193 struct server_by_id { pthread_rwlock_t lock; struct avltree t; struct avltree_node *cache[SERVER_BY_ID_CACHE_SIZE]; }; static struct server_by_id server_by_id; /** List of all active data servers, * protected by server_by_id.lock */ static struct glist_head dslist; /** * @brief Compute cache slot for an entry * * This function computes a hash slot, taking an address modulo the * number of cache slots (which should be prime). * * @param k [in] Entry index value * * @return The computed offset. */ static inline uint16_t id_cache_offsetof(uint16_t k) { return k % SERVER_BY_ID_CACHE_SIZE; } /** * @brief Server id comparator for AVL tree walk * */ static int server_id_cmpf(const struct avltree_node *lhs, const struct avltree_node *rhs) { struct fsal_pnfs_ds *lk, *rk; lk = avltree_container_of(lhs, struct fsal_pnfs_ds, ds_node); rk = avltree_container_of(rhs, struct fsal_pnfs_ds, ds_node); if (lk->id_servers != rk->id_servers) return (lk->id_servers < rk->id_servers) ? -1 : 1; else return 0; } /** * @brief Allocate the pDS entry. * * @return pointer to fsal_pnfs_ds. * NULL on allocation errors. */ struct fsal_pnfs_ds *pnfs_ds_alloc(void) { return gsh_calloc(1, sizeof(struct fsal_pnfs_ds)); } /** * @brief Free the pDS entry. */ void pnfs_ds_free(struct fsal_pnfs_ds *pds) { if (!pds->refcount) return; gsh_free(pds); } /** * @brief Insert the pDS entry into the AVL tree. * * @param exp [IN] the server entry * * @return false on failure. */ bool pnfs_ds_insert(struct fsal_pnfs_ds *pds) { struct avltree_node *node; void **cache_slot = (void **) &(server_by_id.cache[id_cache_offsetof(pds->id_servers)]); /* we will hold a ref starting out... */ assert(pds->refcount == 1); PTHREAD_RWLOCK_wrlock(&server_by_id.lock); node = avltree_insert(&pds->ds_node, &server_by_id.t); if (node) { /* somebody beat us to it */ PTHREAD_RWLOCK_unlock(&server_by_id.lock); return false; } /* update cache */ atomic_store_voidptr(cache_slot, &pds->ds_node); glist_add_tail(&dslist, &pds->ds_list); pnfs_ds_get_ref(pds); /* == 2 */ if (pds->mds_export != NULL) { /* also bump related export for duration */ get_gsh_export_ref(pds->mds_export); pds->mds_export->has_pnfs_ds = true; } PTHREAD_RWLOCK_unlock(&server_by_id.lock); return true; } /** * @brief Lookup the fsal_pnfs_ds struct for this server id * * Lookup the fsal_pnfs_ds struct by id_servers. * Server ids are assigned by the config file and carried about * by file handles. * * @param id_servers [IN] the server id extracted from the handle * * @return pointer to ref locked server */ struct fsal_pnfs_ds *pnfs_ds_get(uint16_t id_servers) { struct fsal_pnfs_ds v; struct avltree_node *node; struct fsal_pnfs_ds *pds; void **cache_slot = (void **) &(server_by_id.cache[id_cache_offsetof(id_servers)]); v.id_servers = id_servers; PTHREAD_RWLOCK_rdlock(&server_by_id.lock); /* check cache */ node = (struct avltree_node *)atomic_fetch_voidptr(cache_slot); if (node) { pds = avltree_container_of(node, struct fsal_pnfs_ds, ds_node); if (pds->id_servers == id_servers) { /* got it in 1 */ LogDebug(COMPONENT_HASHTABLE_CACHE, "server_by_id cache hit slot %d", id_cache_offsetof(id_servers)); goto out; } } /* fall back to AVL */ node = avltree_lookup(&v.ds_node, &server_by_id.t); if (node) { pds = avltree_container_of(node, struct fsal_pnfs_ds, ds_node); /* update cache */ atomic_store_voidptr(cache_slot, node); } else { PTHREAD_RWLOCK_unlock(&server_by_id.lock); return NULL; } out: pnfs_ds_get_ref(pds); if (pds->mds_export != NULL) /* also bump related export for duration */ get_gsh_export_ref(pds->mds_export); PTHREAD_RWLOCK_unlock(&server_by_id.lock); return pds; } /** * @brief Release the fsal_pnfs_ds struct * * @param exp [IN] the server entry */ void pnfs_ds_put(struct fsal_pnfs_ds *pds) { int32_t refcount = atomic_dec_int32_t(&pds->refcount); if (refcount != 0) { assert(refcount > 0); return; } /* free resources */ fsal_pnfs_ds_fini(pds); gsh_free(pds); } /** * @brief Remove the pDS entry from the AVL tree. * * @param id_servers [IN] the server id extracted from the handle * @param final [IN] Also drop from FSAL. */ void pnfs_ds_remove(uint16_t id_servers, bool final) { struct fsal_pnfs_ds v; struct avltree_node *node; struct fsal_pnfs_ds *pds = NULL; void **cache_slot = (void **) &(server_by_id.cache[id_cache_offsetof(id_servers)]); v.id_servers = id_servers; PTHREAD_RWLOCK_wrlock(&server_by_id.lock); node = avltree_lookup(&v.ds_node, &server_by_id.t); if (node) { struct avltree_node *cnode = (struct avltree_node *) atomic_fetch_voidptr(cache_slot); /* Remove from the AVL cache and tree */ if (node == cnode) atomic_store_voidptr(cache_slot, NULL); avltree_remove(node, &server_by_id.t); pds = avltree_container_of(node, struct fsal_pnfs_ds, ds_node); /* Remove the DS from the DS list */ glist_del(&pds->ds_list); /* Eliminate repeated locks during draining. Idempotent. */ pds->pnfs_ds_status = PNFS_DS_STALE; } PTHREAD_RWLOCK_unlock(&server_by_id.lock); /* removal has a once-only semantic */ if (pds != NULL) { if (pds->mds_export != NULL) /* special case: avoid lookup of related export. * get_gsh_export_ref() was bumped in pnfs_ds_insert() * * once-only, so no need for lock here. * do not pre-clear related export (mds_export). * always check pnfs_ds_status instead. */ put_gsh_export(pds->mds_export); /* Release table reference to the server. * Release of resources will occur on last reference. * Which may or may not be from this call. */ pnfs_ds_put(pds); if (final) { /* Also drop from FSAL. Instead of pDS thread, * relying on export cleanup thread. */ pnfs_ds_put(pds); } } } /** * @brief Remove all DSs left in the system * * Make sure all DSs are freed on shutdown. This will catch all DSs not * associated with an export. * */ void remove_all_dss(void) { struct glist_head tmplist, *glist, *glistn; struct fsal_pnfs_ds *pds; glist_init(&tmplist); /* pnfs_ds_remove() take the lock, so move the entire list to a tmp head * under the lock, then process it outside the lock. */ PTHREAD_RWLOCK_wrlock(&server_by_id.lock); glist_splice_tail(&tmplist, &dslist); PTHREAD_RWLOCK_unlock(&server_by_id.lock); /* Now we can safely process the list without the lock */ glist_for_each_safe(glist, glistn, &tmplist) { pds = glist_entry(glist, struct fsal_pnfs_ds, ds_list); pnfs_ds_remove(pds->id_servers, true); } } /** * @brief Commit a FSAL sub-block * * Use the Name parameter passed in via the self_struct to lookup the * fsal. If the fsal is not loaded (yet), load it and call its init. * * Create the pDS and pass the FSAL sub-block to it so that the * fsal method can process the rest of the parameters in the block */ static int fsal_cfg_commit(void *node, void *link_mem, void *self_struct, struct config_error_type *err_type) { struct fsal_args *fp = self_struct; struct fsal_module **pds_fsal = link_mem; struct fsal_pnfs_ds *pds = container_of(pds_fsal, struct fsal_pnfs_ds, fsal); struct fsal_module *fsal; struct root_op_context root_op_context; fsal_status_t status; int errcnt; /* Initialize req_ctx */ init_root_op_context(&root_op_context, NULL, NULL, 0, 0, UNKNOWN_REQUEST); errcnt = fsal_load_init(node, fp->name, &fsal, err_type); if (errcnt > 0) goto err; status = fsal->m_ops.fsal_pnfs_ds(fsal, node, &pds); if (status.major != ERR_FSAL_NO_ERROR) { fsal_put(fsal); LogCrit(COMPONENT_CONFIG, "Could not create pNFS DS"); LogFullDebug(COMPONENT_FSAL, "FSAL %s refcount %"PRIu32, fsal->name, atomic_fetch_int32_t(&fsal->refcount)); err_type->init = true; errcnt++; } LogEvent(COMPONENT_CONFIG, "DS %d fsal config commit at FSAL (%s) with path (%s)", pds->id_servers, pds->fsal->name, pds->fsal->path); err: release_root_op_context(); /* Don't leak the FSAL block */ err_type->dispose = true; return errcnt; } /** * @brief pNFS DS block handlers */ /** * @brief Initialize the DS block */ static void *pds_init(void *link_mem, void *self_struct) { static struct fsal_pnfs_ds special_ds; if (link_mem == (void *)~0UL) { /* This is the special case of no config. We cannot malloc * this, as it's never committed, so it's leaked. */ memset(&special_ds, 0, sizeof(special_ds)); return &special_ds; } else if (self_struct == NULL) { return pnfs_ds_alloc(); } else { /* free resources case */ pnfs_ds_free(self_struct); return NULL; } } /** * @brief Commit the DS block * * Validate the DS level parameters? fsal and client * parameters are already done. */ static int pds_commit(void *node, void *link_mem, void *self_struct, struct config_error_type *err_type) { struct fsal_pnfs_ds *pds = self_struct; struct fsal_pnfs_ds *probe = pnfs_ds_get(pds->id_servers); /* redundant probe before insert??? */ if (probe != NULL) { LogDebug(COMPONENT_CONFIG, "Server %d already exists!", pds->id_servers); pnfs_ds_put(probe); err_type->exists = true; return 1; } if (!pnfs_ds_insert(pds)) { LogCrit(COMPONENT_CONFIG, "Server id %d already in use.", pds->id_servers); err_type->exists = true; return 1; } LogEvent(COMPONENT_CONFIG, "DS %d created at FSAL (%s) with path (%s)", pds->id_servers, pds->fsal->name, pds->fsal->path); return 0; } /** * @brief Display the DS block */ static void pds_display(const char *step, void *node, void *link_mem, void *self_struct) { struct fsal_pnfs_ds *pds = self_struct; struct fsal_module *fsal = pds->fsal; LogMidDebug(COMPONENT_CONFIG, "%s %p DS %d FSAL (%s) with path (%s)", step, pds, pds->id_servers, fsal->name, fsal->path); } /** * @brief Table of FSAL sub-block parameters * * NOTE: this points to a struct that is private to * fsal_cfg_commit. */ static struct config_item fsal_params[] = { CONF_ITEM_STR("Name", 1, 10, NULL, fsal_args, name), /* cheater union */ CONFIG_EOL }; /** * @brief Table of DS block parameters * * NOTE: the Client and FSAL sub-blocks must be the *last* * two entries in the list. This is so all other * parameters have been processed before these sub-blocks * are processed. */ static struct config_item pds_items[] = { CONF_ITEM_UI16("Number", 0, UINT16_MAX, 0, fsal_pnfs_ds, id_servers), CONF_RELAX_BLOCK("FSAL", fsal_params, fsal_init, fsal_cfg_commit, fsal_pnfs_ds, fsal), CONFIG_EOL }; /** * @brief Top level definition for each DS block */ static struct config_block pds_block = { .dbus_interface_name = "org.ganesha.nfsd.config.ds.%d", .blk_desc.name = "DS", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = pds_init, .blk_desc.u.blk.params = pds_items, .blk_desc.u.blk.commit = pds_commit, .blk_desc.u.blk.display = pds_display }; /** * @brief Read the DS blocks from the parsed configuration file. * * @param[in] in_config The file that contains the DS list * * @return A negative value on error; * otherwise, the number of DS blocks. */ int ReadDataServers(config_file_t in_config, struct config_error_type *err_type) { int rc; rc = load_config_from_parse(in_config, &pds_block, NULL, false, err_type); if (!config_error_is_harmless(err_type)) return -1; return rc; } /** * @brief Initialize server tree */ void server_pkginit(void) { pthread_rwlockattr_t rwlock_attr; pthread_rwlockattr_init(&rwlock_attr); #ifdef GLIBC pthread_rwlockattr_setkind_np( &rwlock_attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP); #endif PTHREAD_RWLOCK_init(&server_by_id.lock, &rwlock_attr); avltree_init(&server_by_id.t, server_id_cmpf, 0); glist_init(&dslist); memset(&server_by_id.cache, 0, sizeof(server_by_id.cache)); } ������������������nfs-ganesha-2.6.0/src/support/err_inject.c����������������������������������������������������������0000664�0000000�0000000�00000004641�13242724102�0020506�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright IBM Corporation, 2011 * Contributor: Frank Filz <ffilzlnx@us.ibm.com> * * -------------------------- * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * */ #include "config.h" #include <stdio.h> #include <string.h> #include <pthread.h> #include <sys/stat.h> #include <time.h> #include "nfs_core.h" #include "nfs_exports.h" #include "external_tools.h" #include "common_utils.h" #include "log.h" int worker_delay_time; int next_worker_delay_time; /** * @TODO convert these to admin dbus methods */ #if 0 int getErrInjectInteger(snmp_adm_type_union *param, void *opt) { long option = (long)opt; switch (option) { case 0: param->integer = worker_delay_time; break; case 1: param->integer = next_worker_delay_time; break; default: return 1; } return 0; } int setErrInjectInteger(const snmp_adm_type_union *param, void *opt) { long option = (long)opt; switch (option) { case 0: worker_delay_time = param->integer; break; case 1: next_worker_delay_time = param->integer; break; default: return 1; } return 0; } static register_get_set snmp_error_injection[] = { {"worker_delay", "Delay for each request processed by worker threads", SNMP_ADM_INTEGER, SNMP_ADM_ACCESS_RW, getErrInjectInteger, setErrInjectInteger, (void *)0}, {"next_worker_delay", "Delay for next request processed by worker threads", SNMP_ADM_INTEGER, SNMP_ADM_ACCESS_RW, getErrInjectInteger, setErrInjectInteger, (void *)1}, }; #define SNMPADM_ERROR_INJECTION_COUNT 2 int init_error_injector(void) { if (snmp_adm_register_get_set_function (INJECT_OID, snmp_error_injection, SNMPADM_ERROR_INJECTION_COUNT)) { LogCrit(COMPONENT_INIT, "Error registering error injection to SNMP"); return 2; } return 0; } #endif �����������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/support/export_mgr.c����������������������������������������������������������0000664�0000000�0000000�00000154427�13242724102�0020560�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2013 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @defgroup Filesystem export management * @{ */ /** * @file export_mgr.c * @author Jim Lieb <jlieb@panasas.com> * @brief export manager */ #include "config.h" #include <time.h> #include <unistd.h> #include <stdint.h> #include <sys/types.h> #include <sys/param.h> #include <pthread.h> #include <assert.h> #include <arpa/inet.h> #include "fsal.h" #include "nfs_core.h" #include "log.h" #include "avltree.h" #include "gsh_types.h" #ifdef USE_DBUS #include "gsh_dbus.h" #endif #include "export_mgr.h" #include "client_mgr.h" #include "server_stats_private.h" #include "server_stats.h" #include "abstract_atomic.h" #include "gsh_intrinsic.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "pnfs_utils.h" /** * @brief Exports are stored in an AVL tree with front-end cache. * * @note number of cache slots should be prime. */ #define EXPORT_BY_ID_CACHE_SIZE 769 struct export_by_id { pthread_rwlock_t lock; struct avltree t; struct avltree_node *cache[EXPORT_BY_ID_CACHE_SIZE]; }; static struct export_by_id export_by_id; /** List of all active exports, * protected by export_by_id.lock */ static struct glist_head exportlist; /** List of exports to be mounted in PseudoFS, * protected by export_by_id.lock */ static struct glist_head mount_work; /** List of exports to be cleaned up on unexport, * protected by export_by_id.lock */ static struct glist_head unexport_work; void export_add_to_mount_work(struct gsh_export *export) { PTHREAD_RWLOCK_wrlock(&export_by_id.lock); glist_add_tail(&mount_work, &export->exp_work); PTHREAD_RWLOCK_unlock(&export_by_id.lock); } void export_add_to_unexport_work_locked(struct gsh_export *export) { glist_add_tail(&unexport_work, &export->exp_work); } void export_add_to_unexport_work(struct gsh_export *export) { PTHREAD_RWLOCK_wrlock(&export_by_id.lock); export_add_to_unexport_work_locked(export); PTHREAD_RWLOCK_unlock(&export_by_id.lock); } struct gsh_export *export_take_mount_work(void) { struct gsh_export *export; PTHREAD_RWLOCK_wrlock(&export_by_id.lock); export = glist_first_entry(&mount_work, struct gsh_export, exp_work); if (export != NULL) glist_del(&export->exp_work); PTHREAD_RWLOCK_unlock(&export_by_id.lock); return export; } struct gsh_export *export_take_unexport_work(void) { struct gsh_export *export; PTHREAD_RWLOCK_wrlock(&export_by_id.lock); export = glist_first_entry(&unexport_work, struct gsh_export, exp_work); if (export != NULL) { glist_del(&export->exp_work); get_gsh_export_ref(export); } PTHREAD_RWLOCK_unlock(&export_by_id.lock); return export; } /** * @brief Compute cache slot for an entry * * This function computes a hash slot, taking an address modulo the * number of cache slotes (which should be prime). * * @param k [in] Entry index value * * @return The computed offset. */ static inline uint16_t eid_cache_offsetof(uint16_t k) { return k % EXPORT_BY_ID_CACHE_SIZE; } /** * @brief Revert export_commit() * * @param export [in] the export just inserted/committed */ void export_revert(struct gsh_export *export) { struct avltree_node *cnode; void **cache_slot = (void **) &(export_by_id.cache[eid_cache_offsetof(export->export_id)]); PTHREAD_RWLOCK_wrlock(&export_by_id.lock); cnode = (struct avltree_node *)atomic_fetch_voidptr(cache_slot); if (&export->node_k == cnode) atomic_store_voidptr(cache_slot, NULL); avltree_remove(&export->node_k, &export_by_id.t); glist_del(&export->exp_list); glist_del(&export->exp_work); PTHREAD_RWLOCK_unlock(&export_by_id.lock); if (export->has_pnfs_ds) { /* once-only, so no need for lock here */ export->has_pnfs_ds = false; pnfs_ds_remove(export->export_id, true); } put_gsh_export(export); /* Release sentinel ref */ } /** * @brief Export id comparator for AVL tree walk * */ static int export_id_cmpf(const struct avltree_node *lhs, const struct avltree_node *rhs) { struct gsh_export *lk, *rk; lk = avltree_container_of(lhs, struct gsh_export, node_k); rk = avltree_container_of(rhs, struct gsh_export, node_k); if (lk->export_id != rk->export_id) return (lk->export_id < rk->export_id) ? -1 : 1; else return 0; } /** * @brief Allocate a gsh_export entry. * * This is the ONLY function that should allocate gsh_exports * * @return pointer to gsh_export. * */ struct gsh_export *alloc_export(void) { struct export_stats *export_st; struct gsh_export *export; export_st = gsh_calloc(1, sizeof(struct export_stats)); export = &export_st->export; LogFullDebug(COMPONENT_EXPORT, "Allocated export %p", export); glist_init(&export->exp_state_list); glist_init(&export->exp_lock_list); glist_init(&export->exp_nlm_share_list); glist_init(&export->mounted_exports_list); glist_init(&export->clients); PTHREAD_RWLOCK_init(&export->lock, NULL); return export; } /** * @brief Free an exportlist entry. * * This is for returning exportlists not yet in the export manager. * Once they get inserted into the export manager, it will release it. */ void free_export(struct gsh_export *export) { struct export_stats *export_st; assert(export->refcnt == 0); /* free resources */ free_export_resources(export); export_st = container_of(export, struct export_stats, export); server_stats_free(&export_st->st); PTHREAD_RWLOCK_destroy(&export->lock); gsh_free(export_st); } /** * @brief Insert an export list entry into the export manager * * WARNING! This takes a pointer to the container of the exportlist. * The struct exports_stats is the container lots of stuff besides * the exportlist so only pass an object you got from alloc_exportlist. * * @param exp [IN] the exportlist entry to insert * * @retval false on error */ bool insert_gsh_export(struct gsh_export *export) { struct avltree_node *node; void **cache_slot = (void **) &(export_by_id.cache[eid_cache_offsetof(export->export_id)]); PTHREAD_RWLOCK_wrlock(&export_by_id.lock); node = avltree_insert(&export->node_k, &export_by_id.t); if (node) { /* somebody beat us to it */ PTHREAD_RWLOCK_unlock(&export_by_id.lock); return false; } /* we will hold a ref starting out... */ get_gsh_export_ref(export); /* update cache */ atomic_store_voidptr(cache_slot, &export->node_k); glist_add_tail(&exportlist, &export->exp_list); get_gsh_export_ref(export); /* == 2 */ PTHREAD_RWLOCK_unlock(&export_by_id.lock); return true; } /** * @brief Lookup the export manager struct for this export id * * Lookup the export manager struct by export id. * Export ids are assigned by the config file and carried about * by file handles. * * @param export_id [IN] the export id extracted from the handle * * @return pointer to ref locked export */ struct gsh_export *get_gsh_export(uint16_t export_id) { struct gsh_export v; struct avltree_node *node; struct gsh_export *exp; void **cache_slot = (void **) &(export_by_id.cache[eid_cache_offsetof(export_id)]); v.export_id = export_id; PTHREAD_RWLOCK_rdlock(&export_by_id.lock); /* check cache */ node = (struct avltree_node *)atomic_fetch_voidptr(cache_slot); if (node) { exp = avltree_container_of(node, struct gsh_export, node_k); if (exp->export_id == export_id) { /* got it in 1 */ LogDebug(COMPONENT_HASHTABLE_CACHE, "export_mgr cache hit slot %d", eid_cache_offsetof(export_id)); goto out; } } /* fall back to AVL */ node = avltree_lookup(&v.node_k, &export_by_id.t); if (node) { exp = avltree_container_of(node, struct gsh_export, node_k); /* update cache */ atomic_store_voidptr(cache_slot, node); } else { PTHREAD_RWLOCK_unlock(&export_by_id.lock); return NULL; } out: get_gsh_export_ref(exp); PTHREAD_RWLOCK_unlock(&export_by_id.lock); return exp; } /** * @brief Lookup the export manager struct by export path * * Gets an export entry from its path using a substring match and * linear search of the export list, assumes being called with * export manager lock held (such as from within foreach_gsh_export. * If path has a trailing '/', ignore it. * * @param path [IN] the path for the entry to be found. * @param exact_match [IN] the path must match exactly * * @return pointer to ref counted export */ struct gsh_export *get_gsh_export_by_path_locked(char *path, bool exact_match) { struct gsh_export *export; struct glist_head *glist; int len_path = strlen(path); int len_export; struct gsh_export *ret_exp = NULL; int len_ret = 0; if (len_path > 1 && path[len_path - 1] == '/') len_path--; LogFullDebug(COMPONENT_EXPORT, "Searching for export matching path %s", path); glist_for_each(glist, &exportlist) { export = glist_entry(glist, struct gsh_export, exp_list); len_export = strlen(export->fullpath); if (len_path == 0 && len_export == 1) { /* Special case for root match */ ret_exp = export; len_ret = len_export; break; } /* A path shorter than the full path cannot match. * Also skip if this export has a shorter path than * the previous match. */ if (len_path < len_export || len_export < len_ret) continue; /* If partial match is not allowed, lengths must be the same */ if (exact_match && len_path != len_export) continue; /* if the char in fullpath just after the end of path is not '/' * it is a name token longer, i.e. /mnt/foo != /mnt/foob/ */ if (len_export > 1 && path[len_export] != '/' && path[len_export] != '\0') continue; /* we agree on size, now compare the leading substring */ if (strncmp(export->fullpath, path, len_export) == 0) { ret_exp = export; len_ret = len_export; /* If we have found an exact match, exit loop. */ if (len_export == len_path) break; } } if (ret_exp != NULL) get_gsh_export_ref(ret_exp); return ret_exp; } /** * @brief Lookup the export manager struct by export path * * Gets an export entry from its path using a substring match and * linear search of the export list. * If path has a trailing '/', ignore it. * * @param path [IN] the path for the entry to be found. * @param exact_match [IN] the path must match exactly * * @return pointer to ref counted export */ struct gsh_export *get_gsh_export_by_path(char *path, bool exact_match) { struct gsh_export *exp; PTHREAD_RWLOCK_rdlock(&export_by_id.lock); exp = get_gsh_export_by_path_locked(path, exact_match); PTHREAD_RWLOCK_unlock(&export_by_id.lock); return exp; } /** * @brief Lookup the export manager struct by export pseudo path * * Gets an export entry from its pseudo (if it exists), assumes * being called with export manager lock held (such as from within * foreach_gsh_export. * * @param path [IN] the path for the entry to be found. * @param exact_match [IN] the path must match exactly * * @return pointer to ref counted export */ struct gsh_export *get_gsh_export_by_pseudo_locked(char *path, bool exact_match) { struct gsh_export *export; struct glist_head *glist; int len_path = strlen(path); int len_export; struct gsh_export *ret_exp = NULL; int len_ret = 0; /* Ignore trailing slash in path */ if (len_path > 1 && path[len_path - 1] == '/') len_path--; LogFullDebug(COMPONENT_EXPORT, "Searching for export matching pseudo path %s", path); glist_for_each(glist, &exportlist) { export = glist_entry(glist, struct gsh_export, exp_list); if (export->pseudopath == NULL) continue; len_export = strlen(export->pseudopath); LogFullDebug(COMPONENT_EXPORT, "Comparing %s %d to %s %d", path, len_path, export->pseudopath, len_export); if (len_path == 0 && len_export == 1) { /* Special case for Pseudo root match */ ret_exp = export; len_ret = len_export; break; } /* A path shorter than the full path cannot match. * Also skip if this export has a shorter path than * the previous match. */ if (len_path < len_export || len_export < len_ret) continue; /* If partial match is not allowed, lengths must be the same */ if (exact_match && len_path != len_export) continue; /* if the char in pseudopath just after the end of path is not * '/' it is a name token longer, i.e. /mnt/foo != /mnt/foob/ */ if (len_export > 1 && path[len_export] != '/' && path[len_export] != '\0') continue; /* we agree on size, now compare the leading substring */ if (strncmp(export->pseudopath, path, len_export) == 0) { ret_exp = export; len_ret = len_export; /* If we have found an exact match, exit loop. */ if (len_export == len_path) break; } } if (ret_exp != NULL) get_gsh_export_ref(ret_exp); return ret_exp; } /** * @brief Lookup the export manager struct by export pseudo path * * Gets an export entry from its pseudo (if it exists) * * @param path [IN] the path for the entry to be found. * @param exact_match [IN] the path must match exactly * * @return pointer to ref counted export */ struct gsh_export *get_gsh_export_by_pseudo(char *path, bool exact_match) { struct gsh_export *exp; PTHREAD_RWLOCK_rdlock(&export_by_id.lock); exp = get_gsh_export_by_pseudo_locked(path, exact_match); PTHREAD_RWLOCK_unlock(&export_by_id.lock); return exp; } /** * @brief Lookup the export manager struct by export tag * * Gets an export entry from its pseudo (if it exists) * * @param path [IN] the path for the entry to be found. * * @return pointer to ref locked export */ struct gsh_export *get_gsh_export_by_tag(char *tag) { struct gsh_export *export; struct glist_head *glist; PTHREAD_RWLOCK_rdlock(&export_by_id.lock); glist_for_each(glist, &exportlist) { export = glist_entry(glist, struct gsh_export, exp_list); if (export->FS_tag != NULL && !strcmp(export->FS_tag, tag)) goto out; } PTHREAD_RWLOCK_unlock(&export_by_id.lock); return NULL; out: get_gsh_export_ref(export); PTHREAD_RWLOCK_unlock(&export_by_id.lock); return export; } /** * @brief mount the export in pseudo FS * */ bool mount_gsh_export(struct gsh_export *exp) { struct root_op_context root_op_context; bool rc = true; /* Initialize req_ctx */ init_root_op_context(&root_op_context, NULL, NULL, NFS_V4, 0, NFS_REQUEST); if (!pseudo_mount_export(exp)) rc = false; release_root_op_context(); return rc; } /** * @brief Take a reference to an export. */ void _get_gsh_export_ref(struct gsh_export *a_export, char *file, int line, char *function) { int64_t refcount = atomic_inc_int64_t(&a_export->refcnt); if (isFullDebug(COMPONENT_EXPORT)) { DisplayLogComponentLevel(COMPONENT_EXPORT, file, line, function, NIV_FULL_DEBUG, "get export ref for id %" PRIu16 " %s, refcount = %" PRIi64, a_export->export_id, export_path(a_export), refcount); } } /** * @brief Release the export management struct * * We are done with it, let it go. */ void _put_gsh_export(struct gsh_export *export, char *file, int line, char *function) { int64_t refcount = atomic_dec_int64_t(&export->refcnt); if (isFullDebug(COMPONENT_EXPORT)) { DisplayLogComponentLevel(COMPONENT_EXPORT, file, line, function, NIV_FULL_DEBUG, "put export ref for id %" PRIu16 " %s, refcount = %" PRIi64, export->export_id, export_path(export), refcount); } if (refcount != 0) { assert(refcount > 0); return; } /* Releasing last reference */ free_export(export); } /** * @brief Remove the export management struct * * Remove it from the AVL tree. */ void remove_gsh_export(uint16_t export_id) { struct gsh_export v; struct avltree_node *node; struct gsh_export *export = NULL; void **cache_slot = (void **) &(export_by_id.cache[eid_cache_offsetof(export_id)]); v.export_id = export_id; PTHREAD_RWLOCK_wrlock(&export_by_id.lock); node = avltree_lookup(&v.node_k, &export_by_id.t); if (node) { struct avltree_node *cnode = (struct avltree_node *) atomic_fetch_voidptr(cache_slot); /* Remove from the AVL cache and tree */ if (node == cnode) atomic_store_voidptr(cache_slot, NULL); avltree_remove(node, &export_by_id.t); export = avltree_container_of(node, struct gsh_export, node_k); /* Remove the export from the export list */ glist_del(&export->exp_list); /* No new references will be granted. Idempotent. */ export->export_status = EXPORT_STALE; } PTHREAD_RWLOCK_unlock(&export_by_id.lock); /* removal has a once-only semantic */ if (export != NULL) { if (export->has_pnfs_ds) { /* once-only, so no need for lock here */ export->has_pnfs_ds = false; pnfs_ds_remove(export->export_id, true); } /* Release sentinel reference to the export. * Release of resources will occur on last reference. * Which may or may not be from this call. */ put_gsh_export(export); } } /** * @ Walk the tree and do the callback on each node * * @param cb [IN] Callback function * @param state [IN] param block to pass */ bool foreach_gsh_export(bool(*cb) (struct gsh_export *exp, void *state), bool wrlock, void *state) { struct glist_head *glist, *glistn; struct gsh_export *export; int rc = true; if (wrlock) PTHREAD_RWLOCK_wrlock(&export_by_id.lock); else PTHREAD_RWLOCK_rdlock(&export_by_id.lock); glist_for_each_safe(glist, glistn, &exportlist) { export = glist_entry(glist, struct gsh_export, exp_list); rc = cb(export, state); if (!rc) break; } PTHREAD_RWLOCK_unlock(&export_by_id.lock); return rc; } bool remove_one_export(struct gsh_export *export, void *state) { export_add_to_unexport_work_locked(export); return true; } /** * @brief Bring down all exports in an orderly fashion. */ void remove_all_exports(void) { struct gsh_export *export; struct root_op_context root_op_context; /* Initialize req_ctx */ init_root_op_context(&root_op_context, NULL, NULL, NFS_V4, 0, NFS_REQUEST); /* Get a reference to the PseudoFS Root Export */ export = get_gsh_export_by_pseudo("/", true); op_ctx->ctx_export = export; op_ctx->fsal_export = export->fsal_export; /* Clean up the whole PseudoFS */ pseudo_unmount_export(export); put_gsh_export(export); op_ctx->ctx_export = NULL; op_ctx->fsal_export = NULL; /* Put all exports on the unexport work list. * Ignore return since remove_one_export can't fail. */ (void) foreach_gsh_export(remove_one_export, true, NULL); /* Now process all the unexports */ while (true) { export = export_take_unexport_work(); if (export == NULL) break; op_ctx->ctx_export = export; op_ctx->fsal_export = export->fsal_export; unexport(export); put_gsh_export(export); op_ctx->ctx_export = NULL; op_ctx->fsal_export = NULL; } release_root_op_context(); } #ifdef USE_DBUS /* DBUS interfaces */ /** * @brief Return all IO stats of an export * DBUS_TYPE_ARRAY, "qs(tttttt)(tttttt)" */ static bool get_all_export_io(struct gsh_export *export_node, void *array_iter) { struct export_stats *export_statistics; LogFullDebug(COMPONENT_DBUS, "export id: %i, path: %s", export_node->export_id, export_node->fullpath); export_statistics = container_of(export_node, struct export_stats, export); server_dbus_all_iostats(export_statistics, (DBusMessageIter *) array_iter); return true; } /* parse the export_id in args */ static bool arg_export_id(DBusMessageIter *args, uint16_t *export_id, char **errormsg) { bool success = true; if (args == NULL) { success = false; *errormsg = "message has no arguments"; } else if (dbus_message_iter_get_arg_type(args) != DBUS_TYPE_UINT16) { success = false; *errormsg = "arg not a 16 bit integer"; } else { dbus_message_iter_get_basic(args, export_id); } return success; } /* DBUS export manager stats helpers */ static struct gsh_export *lookup_export(DBusMessageIter *args, char **errormsg) { uint16_t export_id; struct gsh_export *export = NULL; bool success = true; success = arg_export_id(args, &export_id, errormsg); if (success) { export = get_gsh_export(export_id); if (export == NULL) *errormsg = "Export id not found"; } return export; } /* Private state for config_errs_to_dbus to convert * parsing error stream in to a string of '\n' terminated * message lines. */ struct error_detail { char *buf; size_t bufsize; FILE *fp; }; /** * @brief Report processing errors to the DBUS client. * * For now, they just go to the log (as before). */ static void config_errs_to_dbus(char *err, void *dest, struct config_error_type *err_type) { struct error_detail *err_dest = dest; if (err_dest->fp == NULL) { err_dest->fp = open_memstream(&err_dest->buf, &err_dest->bufsize); if (err_dest->fp == NULL) { LogCrit(COMPONENT_EXPORT, "Unable to allocate space for parse errors"); return; } } fprintf(err_dest->fp, "%s\n", err); } struct showexports_state { DBusMessageIter export_iter; }; /** * @brief Add an export either before or after the ID'd export * * This method passes a pathname in the server's local filesystem * that should be parsed and processed by the config_parsing module. * The resulting export entry is then added before or after the ID'd * export entry. Params are in the args iter * * @param "path" [IN] A local path to a file with only an EXPORT {...} * * @return true for success, false with error filled out for failure */ static bool gsh_export_addexport(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { int rc, exp_cnt = 0; bool status = true; char *file_path = NULL; char *export_expr = NULL; config_file_t config_struct = NULL; struct config_node_list *config_list, *lp, *lp_next; struct config_error_type err_type; DBusMessageIter iter; char *err_detail = NULL; struct error_detail conf_errs = {NULL, 0, NULL}; /* Get path */ if (dbus_message_iter_get_arg_type(args) == DBUS_TYPE_STRING) dbus_message_iter_get_basic(args, &file_path); else { dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "Pathname is not a string. It is a (%c)", dbus_message_iter_get_arg_type(args)); status = false; goto out; } if (dbus_message_iter_next(args) && dbus_message_iter_get_arg_type(args) == DBUS_TYPE_STRING) dbus_message_iter_get_basic(args, &export_expr); else { dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "expression is not a string. It is a (%c)", dbus_message_iter_get_arg_type(args)); status = false; goto out; } LogInfo(COMPONENT_EXPORT, "Adding export from file: %s with %s", file_path, export_expr); /* Create a memstream for parser+processing error messages */ if (!init_error_type(&err_type)) goto out; config_struct = config_ParseFile(file_path, &err_type); if (!config_error_is_harmless(&err_type)) { err_detail = err_type_str(&err_type); LogCrit(COMPONENT_EXPORT, "Error while parsing %s", file_path); report_config_errors(&err_type, &conf_errs, config_errs_to_dbus); if (conf_errs.fp != NULL) fclose(conf_errs.fp); dbus_set_error(error, DBUS_ERROR_INVALID_FILE_CONTENT, "Error while parsing %s because of %s errors. Details:\n%s", file_path, err_detail != NULL ? err_detail : "unknown", conf_errs.buf); status = false; goto out; } rc = find_config_nodes(config_struct, export_expr, &config_list, &err_type); if (rc != 0) { LogCrit(COMPONENT_EXPORT, "Error finding exports: %s because %s", export_expr, strerror(rc)); report_config_errors(&err_type, &conf_errs, config_errs_to_dbus); if (conf_errs.fp != NULL) fclose(conf_errs.fp); dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "Error finding exports: %s because %s", export_expr, strerror(rc)); status = false; goto out; } /* Load export entries from list */ for (lp = config_list; lp != NULL; lp = lp_next) { lp_next = lp->next; if (status) { rc = load_config_from_node(lp->tree_node, &add_export_param, NULL, false, &err_type); if (rc == 0 || config_error_is_harmless(&err_type)) exp_cnt++; else if (!err_type.exists) status = false; } gsh_free(lp); } report_config_errors(&err_type, &conf_errs, config_errs_to_dbus); if (conf_errs.fp != NULL) fclose(conf_errs.fp); if (status) { if (exp_cnt > 0) { size_t msg_size = sizeof("%d exports added") + 10; char *message; if (conf_errs.buf != NULL && strlen(conf_errs.buf) > 0) { msg_size += (strlen(conf_errs.buf) + strlen(". Errors found:\n")); message = gsh_calloc(1, msg_size); snprintf(message, msg_size, "%d exports added. Errors found:\n%s", exp_cnt, conf_errs.buf); } else { message = gsh_calloc(1, msg_size); snprintf(message, msg_size, "%d exports added", exp_cnt); } dbus_message_iter_init_append(reply, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &message); gsh_free(message); } else if (err_type.exists) { LogWarn(COMPONENT_EXPORT, "Selected entries in %s already active!!!", file_path); dbus_set_error(error, DBUS_ERROR_INVALID_FILE_CONTENT, "Selected entries in %s already active!!!", file_path); status = false; } else { LogWarn(COMPONENT_EXPORT, "No usable export entry found in %s!!!", file_path); dbus_set_error(error, DBUS_ERROR_INVALID_FILE_CONTENT, "No new export entries found in %s", file_path); status = false; } goto out; } else { err_detail = err_type_str(&err_type); LogCrit(COMPONENT_EXPORT, "%d export entries in %s added because %s errors", exp_cnt, file_path, err_detail != NULL ? err_detail : "unknown"); dbus_set_error(error, DBUS_ERROR_INVALID_FILE_CONTENT, "%d export entries in %s added because %s errors. Details:\n%s", exp_cnt, file_path, err_detail != NULL ? err_detail : "unknown", conf_errs.buf); } out: if (conf_errs.buf) gsh_free(conf_errs.buf); if (err_detail != NULL) gsh_free(err_detail); config_Free(config_struct); return status; } static struct gsh_dbus_method export_add_export = { .name = "AddExport", .method = gsh_export_addexport, .args = {PATH_ARG, EXPR_ARG, MESSAGE_REPLY, END_ARG_LIST} }; /** * @brief Remove an export * * @param "id" [IN] the id of the export to remove * * @return As above, use DBusError to return errors. */ static bool gsh_export_removeexport(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { struct gsh_export *export = NULL; char *errormsg; bool rc = true; bool op_ctx_set = false; struct root_op_context ctx; export = lookup_export(args, &errormsg); if (export == NULL) { LogDebug(COMPONENT_EXPORT, "lookup_export failed with %s", errormsg); dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "lookup_export failed with %s", errormsg); rc = false; goto out; } if (export->export_id == 0) { LogDebug(COMPONENT_EXPORT, "Cannot remove export with id 0"); put_gsh_export(export); rc = false; dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "Cannot remove export with id 0"); goto out; } /* Lots of obj_ops may be called during cleanup; make sure that an * op_ctx exists */ if (!op_ctx) { init_root_op_context(&ctx, export, export->fsal_export, 0, 0, UNKNOWN_REQUEST); op_ctx_set = true; } unexport(export); LogInfo(COMPONENT_EXPORT, "Removed export with id %d", export->export_id); put_gsh_export(export); if (op_ctx_set) release_root_op_context(); out: return rc; } static struct gsh_dbus_method export_remove_export = { .name = "RemoveExport", .method = gsh_export_removeexport, .args = {ID_ARG, END_ARG_LIST} }; #define DISP_EXP_REPLY \ { \ .name = "id", \ .type = "q", \ .direction = "out" \ }, \ { \ .name = "fullpath", \ .type = "s", \ .direction = "out" \ }, \ { \ .name = "pseudopath", \ .type = "s", \ .direction = "out" \ }, \ { \ .name = "tag", \ .type = "s", \ .direction = "out" \ } /** * @brief Display the contents of an export * * NOTE: this is probably better done as properties. * the interfaces are set up for it. This is here for now * but should not be considered a permanent method */ static bool gsh_export_displayexport(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { DBusMessageIter iter; struct gsh_export *export = NULL; char *errormsg; bool rc = true; char *path; export = lookup_export(args, &errormsg); if (export == NULL) { LogDebug(COMPONENT_EXPORT, "lookup_export failed with %s", errormsg); dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "lookup_export failed with %s", errormsg); rc = false; goto out; } /* create a reply from the message */ dbus_message_iter_init_append(reply, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT16, &export->export_id); path = (export->fullpath != NULL) ? export->fullpath : ""; dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &path); path = (export->pseudopath != NULL) ? export->pseudopath : ""; dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &path); path = (export->FS_tag != NULL) ? export->FS_tag : ""; dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &path); put_gsh_export(export); out: return rc; } static struct gsh_dbus_method export_display_export = { .name = "DisplayExport", .method = gsh_export_displayexport, .args = {ID_ARG, DISP_EXP_REPLY, END_ARG_LIST} }; static bool export_to_dbus(struct gsh_export *exp_node, void *state) { struct showexports_state *iter_state = (struct showexports_state *)state; struct export_stats *exp; DBusMessageIter struct_iter; struct timespec last_as_ts = ServerBootTime; const char *path; exp = container_of(exp_node, struct export_stats, export); path = (exp_node->pseudopath != NULL) ? exp_node->pseudopath : exp_node->fullpath; timespec_add_nsecs(exp_node->last_update, &last_as_ts); dbus_message_iter_open_container(&iter_state->export_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT16, &exp_node->export_id); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &path); server_stats_summary(&struct_iter, &exp->st); dbus_append_timestamp(&struct_iter, &last_as_ts); dbus_message_iter_close_container(&iter_state->export_iter, &struct_iter); return true; } static bool gsh_export_showexports(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { DBusMessageIter iter; struct showexports_state iter_state; struct timespec timestamp; now(×tamp); /* create a reply from the message */ dbus_message_iter_init_append(reply, &iter); dbus_append_timestamp(&iter, ×tamp); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(qsbbbbbbbb(tt))", &iter_state.export_iter); (void)foreach_gsh_export(export_to_dbus, false, (void *)&iter_state); dbus_message_iter_close_container(&iter, &iter_state.export_iter); return true; } static struct gsh_dbus_method export_show_exports = { .name = "ShowExports", .method = gsh_export_showexports, .args = {TIMESTAMP_REPLY, { .name = "exports", .type = "a(qsbbbbbbbb(tt))", .direction = "out"}, END_ARG_LIST} }; /** * @brief Reset stat counters for all exports */ void reset_export_stats(void) { struct glist_head *glist; struct gsh_export *export; struct export_stats *exp; PTHREAD_RWLOCK_rdlock(&export_by_id.lock); glist_for_each(glist, &exportlist) { export = glist_entry(glist, struct gsh_export, exp_list); exp = container_of(export, struct export_stats, export); reset_gsh_stats(&exp->st); } PTHREAD_RWLOCK_unlock(&export_by_id.lock); } /** * @brief Update an export * * This method passes a pathname in the server's local filesystem * that should be parsed and processed by the config_parsing module. * The resulting export entry is then updated. Params are in the args iter * * @param "path" [IN] A local path to a file with only an EXPORT {...} * * @return true for success, false with error filled out for failure */ static bool gsh_export_update_export(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { int rc, exp_cnt = 0; bool status = true; char *file_path = NULL; char *export_expr = NULL; config_file_t config_struct = NULL; struct config_node_list *config_list, *lp, *lp_next; struct config_error_type err_type; DBusMessageIter iter; char *err_detail = NULL; struct error_detail conf_errs = {NULL, 0, NULL}; /* Get path */ if (dbus_message_iter_get_arg_type(args) == DBUS_TYPE_STRING) dbus_message_iter_get_basic(args, &file_path); else { dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "Pathname is not a string. It is a (%c)", dbus_message_iter_get_arg_type(args)); status = false; goto out; } if (dbus_message_iter_next(args) && dbus_message_iter_get_arg_type(args) == DBUS_TYPE_STRING) dbus_message_iter_get_basic(args, &export_expr); else { dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "expression is not a string. It is a (%c)", dbus_message_iter_get_arg_type(args)); status = false; goto out; } LogInfo(COMPONENT_EXPORT, "Adding export from file: %s with %s", file_path, export_expr); /* Create a memstream for parser+processing error messages */ if (!init_error_type(&err_type)) goto out; config_struct = config_ParseFile(file_path, &err_type); if (!config_error_is_harmless(&err_type)) { err_detail = err_type_str(&err_type); LogCrit(COMPONENT_EXPORT, "Error while parsing %s", file_path); report_config_errors(&err_type, &conf_errs, config_errs_to_dbus); if (conf_errs.fp != NULL) fclose(conf_errs.fp); dbus_set_error(error, DBUS_ERROR_INVALID_FILE_CONTENT, "Error while parsing %s because of %s errors. Details:\n%s", file_path, err_detail != NULL ? err_detail : "unknown", conf_errs.buf); status = false; goto out; } rc = find_config_nodes(config_struct, export_expr, &config_list, &err_type); if (rc != 0) { LogCrit(COMPONENT_EXPORT, "Error finding exports: %s because %s", export_expr, strerror(rc)); report_config_errors(&err_type, &conf_errs, config_errs_to_dbus); if (conf_errs.fp != NULL) fclose(conf_errs.fp); dbus_set_error(error, DBUS_ERROR_INVALID_ARGS, "Error finding exports: %s because %s", export_expr, strerror(rc)); status = false; goto out; } /* Update export entries from list */ for (lp = config_list; lp != NULL; lp = lp_next) { lp_next = lp->next; if (status) { rc = load_config_from_node(lp->tree_node, &update_export_param, NULL, false, &err_type); if (rc == 0 || config_error_is_harmless(&err_type)) exp_cnt++; else if (!err_type.exists) status = false; } gsh_free(lp); } report_config_errors(&err_type, &conf_errs, config_errs_to_dbus); if (conf_errs.fp != NULL) fclose(conf_errs.fp); if (status) { if (exp_cnt > 0) { size_t msg_size = sizeof("%d exports updated") + 10; char *message; if (conf_errs.buf != NULL && strlen(conf_errs.buf) > 0) { msg_size += (strlen(conf_errs.buf) + strlen(". Errors found:\n")); message = gsh_calloc(1, msg_size); snprintf(message, msg_size, "%d exports updated. Errors found:\n%s", exp_cnt, conf_errs.buf); } else { message = gsh_calloc(1, msg_size); snprintf(message, msg_size, "%d exports updated", exp_cnt); } dbus_message_iter_init_append(reply, &iter); dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &message); gsh_free(message); } else if (err_type.exists) { LogWarn(COMPONENT_EXPORT, "Selected entries in %s already active!!!", file_path); dbus_set_error(error, DBUS_ERROR_INVALID_FILE_CONTENT, "Selected entries in %s already active!!!", file_path); status = false; } else { LogWarn(COMPONENT_EXPORT, "No usable export entry found in %s!!!", file_path); dbus_set_error(error, DBUS_ERROR_INVALID_FILE_CONTENT, "No new export entries found in %s", file_path); status = false; } goto out; } else { err_detail = err_type_str(&err_type); LogCrit(COMPONENT_EXPORT, "%d export entries in %s updated because %s errors", exp_cnt, file_path, err_detail != NULL ? err_detail : "unknown"); dbus_set_error(error, DBUS_ERROR_INVALID_FILE_CONTENT, "%d export entries in %s updated because %s errors. Details:\n%s", exp_cnt, file_path, err_detail != NULL ? err_detail : "unknown", conf_errs.buf); } out: if (conf_errs.buf) gsh_free(conf_errs.buf); if (err_detail != NULL) gsh_free(err_detail); config_Free(config_struct); return status; } static struct gsh_dbus_method export_update_export = { .name = "UpdateExport", .method = gsh_export_update_export, .args = {PATH_ARG, EXPR_ARG, MESSAGE_REPLY, END_ARG_LIST} }; static struct gsh_dbus_method *export_mgr_methods[] = { &export_add_export, &export_remove_export, &export_display_export, &export_show_exports, &export_update_export, NULL }; /* org.ganesha.nfsd.exportmgr interface */ static struct gsh_dbus_interface export_mgr_table = { .name = "org.ganesha.nfsd.exportmgr", .props = NULL, .methods = export_mgr_methods, .signals = NULL }; /* org.ganesha.nfsd.exportstats interface */ /** * DBUS method to report NFSv3 I/O statistics * */ static bool get_nfsv3_export_io(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { struct gsh_export *export = NULL; struct export_stats *export_st = NULL; bool success = true; char *errormsg = "OK"; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); if (!nfs_param.core_param.enable_NFSSTATS) errormsg = "NFS stat counting disabled"; export = lookup_export(args, &errormsg); if (export == NULL) { success = false; errormsg = "No export available"; } else { export_st = container_of(export, struct export_stats, export); if (export_st->st.nfsv3 == NULL) { success = false; errormsg = "Export does not have any NFSv3 activity"; } } dbus_status_reply(&iter, success, errormsg); if (success) server_dbus_v3_iostats(export_st->st.nfsv3, &iter); if (export != NULL) put_gsh_export(export); return true; } static struct gsh_dbus_method export_show_v3_io = { .name = "GetNFSv3IO", .method = get_nfsv3_export_io, .args = {EXPORT_ID_ARG, STATUS_REPLY, TIMESTAMP_REPLY, IOSTATS_REPLY, END_ARG_LIST} }; /** * DBUS method to report NFSv40 I/O statistics * */ static bool get_nfsv40_export_io(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { struct gsh_export *export = NULL; struct export_stats *export_st = NULL; bool success = true; char *errormsg = "OK"; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); export = lookup_export(args, &errormsg); if (!nfs_param.core_param.enable_NFSSTATS) errormsg = "NFS stat counting disabled"; if (export == NULL) { success = false; } else { export_st = container_of(export, struct export_stats, export); if (export_st->st.nfsv40 == NULL) { success = false; errormsg = "Export does not have any NFSv4.0 activity"; } } dbus_status_reply(&iter, success, errormsg); if (success) server_dbus_v40_iostats(export_st->st.nfsv40, &iter); if (export != NULL) put_gsh_export(export); return true; } static struct gsh_dbus_method export_show_v40_io = { .name = "GetNFSv40IO", .method = get_nfsv40_export_io, .args = {EXPORT_ID_ARG, STATUS_REPLY, TIMESTAMP_REPLY, IOSTATS_REPLY, END_ARG_LIST} }; /** * DBUS method to report NFSv41 I/O statistics * */ static bool get_nfsv41_export_io(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { struct gsh_export *export = NULL; struct export_stats *export_st = NULL; bool success = true; char *errormsg = "OK"; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); export = lookup_export(args, &errormsg); if (!nfs_param.core_param.enable_NFSSTATS) errormsg = "NFS stat counting disabled"; if (export == NULL) { success = false; } else { export_st = container_of(export, struct export_stats, export); if (export_st->st.nfsv41 == NULL) { success = false; errormsg = "Export does not have any NFSv4.1 activity"; } } dbus_status_reply(&iter, success, errormsg); if (success) server_dbus_v41_iostats(export_st->st.nfsv41, &iter); if (export != NULL) put_gsh_export(export); return true; } static struct gsh_dbus_method export_show_v41_io = { .name = "GetNFSv41IO", .method = get_nfsv41_export_io, .args = {EXPORT_ID_ARG, STATUS_REPLY, TIMESTAMP_REPLY, IOSTATS_REPLY, END_ARG_LIST} }; /** * DBUS method to report NFSv41 layout statistics * */ static bool get_nfsv41_export_layouts(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { struct gsh_export *export = NULL; struct export_stats *export_st = NULL; bool success = true; char *errormsg = "OK"; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); export = lookup_export(args, &errormsg); if (!nfs_param.core_param.enable_NFSSTATS) errormsg = "NFS stat counting disabled"; if (export == NULL) { success = false; } else { export_st = container_of(export, struct export_stats, export); if (export_st->st.nfsv41 == NULL) { success = false; errormsg = "Export does not have any NFSv4.1 activity"; } } dbus_status_reply(&iter, success, errormsg); if (success) server_dbus_v41_layouts(export_st->st.nfsv41, &iter); if (export != NULL) put_gsh_export(export); return true; } /** * DBUS method to report total ops statistics * */ static bool get_nfsv_export_total_ops(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { struct gsh_export *export = NULL; struct export_stats *export_st = NULL; bool success = true; char *errormsg = "OK"; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); if (!nfs_param.core_param.enable_NFSSTATS) errormsg = "NFS stat counting disabled"; export = lookup_export(args, &errormsg); if (export != NULL) { export_st = container_of(export, struct export_stats, export); dbus_status_reply(&iter, success, errormsg); server_dbus_total_ops(export_st, &iter); put_gsh_export(export); } else { success = false; errormsg = "Export does not have any activity"; dbus_status_reply(&iter, success, errormsg); } return true; } static bool get_nfsv_global_total_ops(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { bool success = true; char *errormsg = "OK"; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); if (!nfs_param.core_param.enable_NFSSTATS) errormsg = "NFS stat counting disabled"; dbus_status_reply(&iter, success, errormsg); if (success) global_dbus_total_ops(&iter); return true; } static bool get_nfsv_global_fast_ops(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { bool success = true; char *errormsg = "OK"; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); if (!nfs_param.core_param.enable_NFSSTATS) errormsg = "NFS stat counting disabled"; dbus_status_reply(&iter, success, errormsg); if (success) server_dbus_fast_ops(&iter); return true; } static bool show_cache_inode_stats(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { bool success = true; char *errormsg = "OK"; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); dbus_status_reply(&iter, success, errormsg); mdcache_dbus_show(&iter); return true; } static struct gsh_dbus_method export_show_v41_layouts = { .name = "GetNFSv41Layouts", .method = get_nfsv41_export_layouts, .args = {EXPORT_ID_ARG, STATUS_REPLY, TIMESTAMP_REPLY, LAYOUTS_REPLY, END_ARG_LIST} }; /* Reset FSAL stats */ static void reset_fsal_stats(void) { /* Module iterator */ struct glist_head *mi = NULL; /* Next module */ struct glist_head *mn = NULL; glist_for_each_safe(mi, mn, &fsal_list) { /* The module to reset stats */ struct fsal_module *m = glist_entry(mi, struct fsal_module, fsals); if (m->stats != NULL) m->m_ops.fsal_reset_stats(m); } } /** * DBUS method to reset all ops statistics * */ static bool stats_reset(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { bool success = true; char *errormsg = "OK"; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); dbus_status_reply(&iter, success, errormsg); reset_fsal_stats(); server_reset_stats(&iter); return true; } static struct gsh_dbus_method reset_statistics = { .name = "ResetStats", .method = stats_reset, .args = {STATUS_REPLY, TIMESTAMP_REPLY, END_ARG_LIST} }; /** * DBUS method to disable statistics counting * */ static bool stats_disable(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { bool success = true; char *errormsg = "OK"; char *stat_type = NULL; DBusMessageIter iter; struct timespec timestamp; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_get_basic(args, &stat_type); if (strcmp(stat_type, "all") == 0) { nfs_param.core_param.enable_NFSSTATS = false; nfs_param.core_param.enable_FSALSTATS = false; } if (strcmp(stat_type, "nfs") == 0) nfs_param.core_param.enable_NFSSTATS = false; if (strcmp(stat_type, "fsal") == 0) nfs_param.core_param.enable_FSALSTATS = false; dbus_status_reply(&iter, success, errormsg); now(×tamp); dbus_append_timestamp(&iter, ×tamp); return true; } static struct gsh_dbus_method disable_statistics = { .name = "DisableStats", .method = stats_disable, .args = {STAT_TYPE_ARG, STATUS_REPLY, TIMESTAMP_REPLY, END_ARG_LIST} }; /** * DBUS method to enable statistics counting * */ static bool stats_enable(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { bool success = true; char *errormsg = "OK"; char *stat_type = NULL; DBusMessageIter iter; struct timespec timestamp; dbus_message_iter_init_append(reply, &iter); dbus_message_iter_get_basic(args, &stat_type); if (strcmp(stat_type, "all") == 0) { nfs_param.core_param.enable_NFSSTATS = true; nfs_param.core_param.enable_FSALSTATS = true; } if (strcmp(stat_type, "nfs") == 0) nfs_param.core_param.enable_NFSSTATS = true; if (strcmp(stat_type, "fsal") == 0) nfs_param.core_param.enable_FSALSTATS = true; dbus_status_reply(&iter, success, errormsg); now(×tamp); dbus_append_timestamp(&iter, ×tamp); return true; } static struct gsh_dbus_method enable_statistics = { .name = "EnableStats", .method = stats_enable, .args = {STAT_TYPE_ARG, STATUS_REPLY, TIMESTAMP_REPLY, END_ARG_LIST} }; /** * DBUS method to gather FSAL statistics * */ static bool stats_fsal(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { bool success = true; char *errormsg = "OK"; char *fsal_name; DBusMessageIter iter; struct fsal_module *fsal_hdl; struct root_op_context root_op_context; dbus_message_iter_init_append(reply, &iter); if (!nfs_param.core_param.enable_FSALSTATS) errormsg = "FSAL stat counting disabled"; dbus_message_iter_get_basic(args, &fsal_name); init_root_op_context(&root_op_context, NULL, NULL, 0, 0, UNKNOWN_REQUEST); fsal_hdl = lookup_fsal(fsal_name); release_root_op_context(); if (fsal_hdl == NULL) { success = false; errormsg = "Incorrect FSAL name"; dbus_status_reply(&iter, success, errormsg); return true; } if (fsal_hdl->stats == NULL) { success = false; errormsg = "FSAL do not support stats counting"; dbus_status_reply(&iter, success, errormsg); } else { dbus_status_reply(&iter, success, errormsg); fsal_hdl->m_ops.fsal_extract_stats(fsal_hdl, &iter); } return true; } static struct gsh_dbus_method fsal_statistics = { .name = "GetFSALStats", .method = stats_fsal, .args = {FSAL_ARG, STATUS_REPLY, TIMESTAMP_REPLY, FSAL_OPS_REPLY, END_ARG_LIST} }; #ifdef _USE_9P /** * DBUS method to report 9p I/O statistics * */ static bool get_9p_export_io(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { struct gsh_export *export = NULL; struct export_stats *export_st = NULL; bool success = true; char *errormsg = "OK"; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); export = lookup_export(args, &errormsg); if (export == NULL) { success = false; } else { export_st = container_of(export, struct export_stats, export); if (export_st->st._9p == NULL) { success = false; errormsg = "Export does not have any 9p activity"; } } dbus_status_reply(&iter, success, errormsg); if (success) server_dbus_9p_iostats(export_st->st._9p, &iter); if (export != NULL) put_gsh_export(export); return true; } static struct gsh_dbus_method export_show_9p_io = { .name = "Get9pIO", .method = get_9p_export_io, .args = {EXPORT_ID_ARG, STATUS_REPLY, TIMESTAMP_REPLY, IOSTATS_REPLY, END_ARG_LIST} }; /** * DBUS method to report 9p protocol operation statistics * */ static bool get_9p_export_op_stats(DBusMessageIter *args, DBusMessage *reply, DBusError *error) { struct gsh_export *export = NULL; struct export_stats *export_st = NULL; u8 opcode; bool success = true; char *errormsg = "OK"; DBusMessageIter iter; dbus_message_iter_init_append(reply, &iter); export = lookup_export(args, &errormsg); if (export == NULL) { success = false; } else { export_st = container_of(export, struct export_stats, export); if (export_st->st._9p == NULL) { success = false; errormsg = "Export does not have any 9p activity"; } } dbus_message_iter_next(args); if (success) success = arg_9p_op(args, &opcode, &errormsg); dbus_status_reply(&iter, success, errormsg); if (success) server_dbus_9p_opstats(export_st->st._9p, opcode, &iter); if (export != NULL) put_gsh_export(export); return true; } static struct gsh_dbus_method export_show_9p_op_stats = { .name = "Get9pOpStats", .method = get_9p_export_op_stats, .args = {EXPORT_ID_ARG, _9P_OP_ARG, STATUS_REPLY, TIMESTAMP_REPLY, OP_STATS_REPLY, END_ARG_LIST} }; #endif static struct gsh_dbus_method export_show_total_ops = { .name = "GetTotalOPS", .method = get_nfsv_export_total_ops, .args = {EXPORT_ID_ARG, STATUS_REPLY, TIMESTAMP_REPLY, TOTAL_OPS_REPLY, END_ARG_LIST} }; static struct gsh_dbus_method global_show_total_ops = { .name = "GetGlobalOPS", .method = get_nfsv_global_total_ops, .args = {STATUS_REPLY, TIMESTAMP_REPLY, TOTAL_OPS_REPLY, END_ARG_LIST} }; static struct gsh_dbus_method global_show_fast_ops = { .name = "GetFastOPS", .method = get_nfsv_global_fast_ops, .args = {STATUS_REPLY, TIMESTAMP_REPLY, TOTAL_OPS_REPLY, END_ARG_LIST} }; static struct gsh_dbus_method cache_inode_show = { .name = "ShowCacheInode", .method = show_cache_inode_stats, .args = {STATUS_REPLY, TIMESTAMP_REPLY, TOTAL_OPS_REPLY, END_ARG_LIST} }; /** * @brief Report all IO stats of all exports in one call * * @return * status * error message * time * array of ( * export id * string containing the protocol version * read statistics structure * (requested, transferred, total, errors, latency, * queue wait) * write statistics structure * (requested, transferred, total, errors, latency, * queue wait) * ) */ static bool get_nfs_io(DBusMessageIter *args, DBusMessage *message, DBusError *error) { bool success = true; char *errormsg = "OK"; DBusMessageIter reply_iter, array_iter; struct timespec timestamp; /* create a reply iterator from the message */ dbus_message_iter_init_append(message, &reply_iter); if (!nfs_param.core_param.enable_NFSSTATS) errormsg = "NFS stat counting disabled"; /* status and timestamp reply */ dbus_status_reply(&reply_iter, success, errormsg); now(×tamp); dbus_append_timestamp(&reply_iter, ×tamp); /* create an array container iterator and loop over all exports */ dbus_message_iter_open_container(&reply_iter, DBUS_TYPE_ARRAY, NFS_ALL_IO_REPLY_ARRAY_TYPE, &array_iter); (void) foreach_gsh_export(&get_all_export_io, false, (void *) &array_iter); dbus_message_iter_close_container(&reply_iter, &array_iter); return true; } static struct gsh_dbus_method export_show_all_io = { .name = "GetNFSIO", .method = get_nfs_io, .args = {STATUS_REPLY, TIMESTAMP_REPLY, NFS_ALL_IO_REPLY, END_ARG_LIST} }; static struct gsh_dbus_method *export_stats_methods[] = { &export_show_v3_io, &export_show_v40_io, &export_show_v41_io, &export_show_v41_layouts, &export_show_total_ops, #ifdef _USE_9P &export_show_9p_io, &export_show_9p_op_stats, #endif &global_show_total_ops, &global_show_fast_ops, &cache_inode_show, &export_show_all_io, &reset_statistics, &fsal_statistics, &enable_statistics, &disable_statistics, NULL }; static struct gsh_dbus_interface export_stats_table = { .name = "org.ganesha.nfsd.exportstats", .methods = export_stats_methods }; /* DBUS list of interfaces on /org/ganesha/nfsd/ExportMgr */ static struct gsh_dbus_interface *export_interfaces[] = { &export_mgr_table, &export_stats_table, NULL }; void dbus_export_init(void) { gsh_dbus_register_path("ExportMgr", export_interfaces); } #endif /* USE_DBUS */ /** * @brief Initialize export manager */ void export_pkginit(void) { pthread_rwlockattr_t rwlock_attr; pthread_rwlockattr_init(&rwlock_attr); #ifdef GLIBC pthread_rwlockattr_setkind_np( &rwlock_attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP); #endif PTHREAD_RWLOCK_init(&export_by_id.lock, &rwlock_attr); avltree_init(&export_by_id.t, export_id_cmpf, 0); memset(&export_by_id.cache, 0, sizeof(export_by_id.cache)); glist_init(&exportlist); glist_init(&mount_work); glist_init(&unexport_work); } /** @} */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/support/exports.c�������������������������������������������������������������0000664�0000000�0000000�00000234365�13242724102�0020076�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file exports.c * @brief Export parsing and management */ #include "config.h" #include "cidr.h" #include "log.h" #include "fsal.h" #include "nfs_core.h" #include "nfs_file_handle.h" #include "nfs_exports.h" #include "nfs_ip_stats.h" #include "nfs_proto_functions.h" #include "nfs_dupreq.h" #include "config_parsing.h" #include "common_utils.h" #include <stdlib.h> #include <fnmatch.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <strings.h> #include <ctype.h> #include "export_mgr.h" #include "fsal_up.h" #include "sal_functions.h" #include "pnfs_utils.h" #include "netgroup_cache.h" #include "mdcache.h" /** * @brief Protect EXPORT_DEFAULTS structure for dynamic update. * * If an export->lock is also held by the code, this lock MUST be * taken AFTER the export->lock to avoid ABBA deadlock. * */ pthread_rwlock_t export_opt_lock = PTHREAD_RWLOCK_INITIALIZER; #define GLOBAL_EXPORT_PERMS_INITIALIZER \ .def.anonymous_uid = ANON_UID, \ .def.anonymous_gid = ANON_GID, \ /* Note: Access_Type defaults to None on purpose */ \ .def.options = EXPORT_OPTION_ROOT_SQUASH | \ EXPORT_OPTION_NO_ACCESS | \ EXPORT_OPTION_AUTH_DEFAULTS | \ EXPORT_OPTION_PROTO_DEFAULTS | \ EXPORT_OPTION_XPORT_DEFAULTS | \ EXPORT_OPTION_NO_DELEGATIONS, \ .def.set = UINT32_MAX, \ .expire_time_attr = 60, struct global_export_perms export_opt = { GLOBAL_EXPORT_PERMS_INITIALIZER }; /* A second copy used in configuration, so we can atomically update the * primary set. */ struct global_export_perms export_opt_cfg = { GLOBAL_EXPORT_PERMS_INITIALIZER }; static void FreeClientList(struct glist_head *clients); static int StrExportOptions(struct display_buffer *dspbuf, struct export_perms *p_perms) { int b_left = display_start(dspbuf); if (b_left <= 0) return b_left; b_left = display_printf(dspbuf, "options=%08"PRIx32, p_perms->options); if (b_left <= 0) return b_left; if ((p_perms->set & EXPORT_OPTION_SQUASH_TYPES) != 0) { if ((p_perms->options & EXPORT_OPTION_ROOT_SQUASH) != 0) b_left = display_cat(dspbuf, "root_squash "); if (b_left <= 0) return b_left; if ((p_perms->options & EXPORT_OPTION_ROOT_ID_SQUASH) != 0) b_left = display_cat(dspbuf, "root_id_squash"); if (b_left <= 0) return b_left; if ((p_perms->options & EXPORT_OPTION_ALL_ANONYMOUS) != 0) b_left = display_cat(dspbuf, "all_squash "); if (b_left <= 0) return b_left; if ((p_perms->options & EXPORT_OPTION_SQUASH_TYPES) == 0) b_left = display_cat(dspbuf, "no_root_squash"); } else b_left = display_cat(dspbuf, " "); if (b_left <= 0) return b_left; if ((p_perms->set & EXPORT_OPTION_ACCESS_MASK) != 0) { if ((p_perms->options & EXPORT_OPTION_READ_ACCESS) != 0) b_left = display_cat(dspbuf, ", R"); else b_left = display_cat(dspbuf, ", -"); if (b_left <= 0) return b_left; if ((p_perms->options & EXPORT_OPTION_WRITE_ACCESS) != 0) b_left = display_cat(dspbuf, "W"); else b_left = display_cat(dspbuf, "-"); if (b_left <= 0) return b_left; if ((p_perms->options & EXPORT_OPTION_MD_READ_ACCESS) != 0) b_left = display_cat(dspbuf, "r"); else b_left = display_cat(dspbuf, "-"); if (b_left <= 0) return b_left; if ((p_perms->options & EXPORT_OPTION_MD_WRITE_ACCESS) != 0) b_left = display_cat(dspbuf, "w"); else b_left = display_cat(dspbuf, "-"); } else b_left = display_cat(dspbuf, ", "); if (b_left <= 0) return b_left; if ((p_perms->set & EXPORT_OPTION_PROTOCOLS) != 0) { if ((p_perms->options & EXPORT_OPTION_NFSV3) != 0) b_left = display_cat(dspbuf, ", 3"); else b_left = display_cat(dspbuf, ", -"); if (b_left <= 0) return b_left; if ((p_perms->options & EXPORT_OPTION_NFSV4) != 0) b_left = display_cat(dspbuf, "4"); else b_left = display_cat(dspbuf, "-"); if (b_left <= 0) return b_left; if ((p_perms->options & EXPORT_OPTION_9P) != 0) b_left = display_cat(dspbuf, "9"); else b_left = display_cat(dspbuf, "-"); } else b_left = display_cat(dspbuf, ", "); if (b_left <= 0) return b_left; if ((p_perms->set & EXPORT_OPTION_TRANSPORTS) != 0) { if ((p_perms->options & EXPORT_OPTION_UDP) != 0) b_left = display_cat(dspbuf, ", UDP"); else b_left = display_cat(dspbuf, ", ---"); if (b_left <= 0) return b_left; if ((p_perms->options & EXPORT_OPTION_TCP) != 0) b_left = display_cat(dspbuf, ", TCP"); else b_left = display_cat(dspbuf, ", ---"); if (b_left <= 0) return b_left; if ((p_perms->options & EXPORT_OPTION_RDMA) != 0) b_left = display_cat(dspbuf, ", RDMA"); else b_left = display_cat(dspbuf, ", ----"); } else b_left = display_cat(dspbuf, ", "); if (b_left <= 0) return b_left; if ((p_perms->set & EXPORT_OPTION_MANAGE_GIDS) == 0) b_left = display_cat(dspbuf, ", "); else if ((p_perms->options & EXPORT_OPTION_MANAGE_GIDS) != 0) b_left = display_cat(dspbuf, ", Manage_Gids "); else b_left = display_cat(dspbuf, ", No Manage_Gids"); if (b_left <= 0) return b_left; if ((p_perms->set & EXPORT_OPTION_DELEGATIONS) != 0) { if ((p_perms->options & EXPORT_OPTION_READ_DELEG) != 0) b_left = display_cat(dspbuf, ", R"); else b_left = display_cat(dspbuf, ", -"); if (b_left <= 0) return b_left; if ((p_perms->options & EXPORT_OPTION_WRITE_DELEG) != 0) b_left = display_cat(dspbuf, "W Deleg"); else b_left = display_cat(dspbuf, "- Deleg"); } else b_left = display_cat(dspbuf, ", "); if (b_left <= 0) return b_left; if ((p_perms->set & EXPORT_OPTION_ANON_UID_SET) != 0) b_left = display_printf(dspbuf, ", anon_uid=%6d", (int)p_perms->anonymous_uid); else b_left = display_cat(dspbuf, ", "); if (b_left <= 0) return b_left; if ((p_perms->set & EXPORT_OPTION_ANON_GID_SET) != 0) b_left = display_printf(dspbuf, ", anon_gid=%6d", (int)p_perms->anonymous_gid); else b_left = display_cat(dspbuf, ", "); if (b_left <= 0) return b_left; if ((p_perms->set & EXPORT_OPTION_AUTH_TYPES) != 0) { if ((p_perms->options & EXPORT_OPTION_AUTH_NONE) != 0) b_left = display_cat(dspbuf, ", none"); if (b_left <= 0) return b_left; if ((p_perms->options & EXPORT_OPTION_AUTH_UNIX) != 0) b_left = display_cat(dspbuf, ", sys"); if (b_left <= 0) return b_left; if ((p_perms->options & EXPORT_OPTION_RPCSEC_GSS_NONE) != 0) b_left = display_cat(dspbuf, ", krb5"); if (b_left <= 0) return b_left; if ((p_perms->options & EXPORT_OPTION_RPCSEC_GSS_INTG) != 0) b_left = display_cat(dspbuf, ", krb5i"); if (b_left <= 0) return b_left; if ((p_perms->options & EXPORT_OPTION_RPCSEC_GSS_PRIV) != 0) b_left = display_cat(dspbuf, ", krb5p"); } return b_left; } static char *client_types[] = { [PROTO_CLIENT] = "PROTO_CLIENT", [NETWORK_CLIENT] = "NETWORK_CLIENT", [NETGROUP_CLIENT] = "NETGROUP_CLIENT", [WILDCARDHOST_CLIENT] = "WILDCARDHOST_CLIENT", [GSSPRINCIPAL_CLIENT] = "GSSPRINCIPAL_CLIENT", [MATCH_ANY_CLIENT] = "MATCH_ANY_CLIENT", [BAD_CLIENT] = "BAD_CLIENT" }; void LogClientListEntry(log_levels_t level, log_components_t component, int line, char *func, char *tag, exportlist_client_entry_t *entry) { char perms[1024] = "\0"; struct display_buffer dspbuf = {sizeof(perms), perms, perms}; char addr[INET6_ADDRSTRLEN]; char *paddr = addr; char *client_type; if (!isLevel(component, level)) return; if (entry->type > BAD_CLIENT) { sprintf(paddr, "0x%08x", entry->type); client_type = "UNKNOWN_CLIENT_TYPE"; } else { client_type = client_types[entry->type]; } (void) StrExportOptions(&dspbuf, &entry->client_perms); switch (entry->type) { case NETWORK_CLIENT: paddr = cidr_to_str(entry->client.network.cidr, CIDR_NOFLAGS); break; case NETGROUP_CLIENT: paddr = entry->client.netgroup.netgroupname; break; case WILDCARDHOST_CLIENT: paddr = entry->client.wildcard.wildcard; break; case GSSPRINCIPAL_CLIENT: paddr = entry->client.gssprinc.princname; break; case MATCH_ANY_CLIENT: paddr = "*"; break; case PROTO_CLIENT: case BAD_CLIENT: paddr = "<unknown>"; break; } DisplayLogComponentLevel(component, (char *) __FILE__, line, func, level, "%s%p %s: %s (%s)", tag, entry, client_type, paddr, perms); } static void display_clients(struct gsh_export *export) { struct glist_head *glist; PTHREAD_RWLOCK_rdlock(&export->lock); glist_for_each(glist, &export->clients) { exportlist_client_entry_t *client; client = glist_entry(glist, exportlist_client_entry_t, cle_list); LogClientListEntry(NIV_MID_DEBUG, COMPONENT_EXPORT, __LINE__, (char *) __func__, "", client); } PTHREAD_RWLOCK_unlock(&export->lock); } /** * @brief Expand the client name token into one or more client entries * * @param client_list[IN] the client list this gets linked to (in tail order) * @param client_tok [IN] the name string. We modify it. * @param type_hint [IN] type hint from parser for client_tok * @param perms [IN] pointer to the permissions to copy into each * @param cnode [IN] opaque pointer needed for config_proc_error() * @param err_type [OUT] error handling ref * * @returns 0 on success, error count on failure */ static int add_client(struct glist_head *client_list, const char *client_tok, enum term_type type_hint, struct export_perms *perms, void *cnode, struct config_error_type *err_type) { struct exportlist_client_entry__ *cli; int errcnt = 0; struct addrinfo *info; CIDR *cidr; int rc; cli = gsh_calloc(1, sizeof(struct exportlist_client_entry__)); cli->client.network.cidr = NULL; glist_init(&cli->cle_list); switch (type_hint) { case TERM_V4_ANY: cli->type = MATCH_ANY_CLIENT; break; case TERM_NETGROUP: if (strlen(client_tok) > MAXHOSTNAMELEN) { config_proc_error(cnode, err_type, "netgroup (%s) name too long", client_tok); err_type->invalid = true; errcnt++; goto out; } cli->client.netgroup.netgroupname = gsh_strdup(client_tok + 1); cli->type = NETGROUP_CLIENT; break; case TERM_V4CIDR: case TERM_V6CIDR: case TERM_V4ADDR: case TERM_V6ADDR: cidr = cidr_from_str(client_tok); if (cidr == NULL) { switch (type_hint) { case TERM_V4CIDR: config_proc_error(cnode, err_type, "Expected a IPv4 CIDR address, got (%s)", client_tok); break; case TERM_V6CIDR: config_proc_error(cnode, err_type, "Expected a IPv6 CIDR address, got (%s)", client_tok); break; case TERM_V4ADDR: config_proc_error(cnode, err_type, "IPv4 addr (%s) not in presentation format", client_tok); break; case TERM_V6ADDR: config_proc_error(cnode, err_type, "IPv6 addr (%s) not in presentation format", client_tok); break; default: break; } err_type->invalid = true; errcnt++; goto out; } cli->client.network.cidr = cidr; cli->type = NETWORK_CLIENT; break; case TERM_REGEX: if (strlen(client_tok) > MAXHOSTNAMELEN) { config_proc_error(cnode, err_type, "Wildcard client (%s) name too long", client_tok); err_type->invalid = true; errcnt++; goto out; } cli->client.wildcard.wildcard = gsh_strdup(client_tok); cli->type = WILDCARDHOST_CLIENT; break; case TERM_TOKEN: /* only dns names now. */ rc = getaddrinfo(client_tok, NULL, NULL, &info); if (rc == 0) { struct addrinfo *ap, *ap_last = NULL; struct in_addr in_addr_last; struct in6_addr in6_addr_last; for (ap = info; ap != NULL; ap = ap->ai_next) { LogFullDebug(COMPONENT_CONFIG, "flags=%d family=%d socktype=%d protocol=%d addrlen=%d name=%s", ap->ai_flags, ap->ai_family, ap->ai_socktype, ap->ai_protocol, (int) ap->ai_addrlen, ap->ai_canonname); if (cli == NULL) { cli = gsh_calloc(1, sizeof(struct exportlist_client_entry__)); glist_init(&cli->cle_list); } if (ap->ai_family == AF_INET && (ap->ai_socktype == SOCK_STREAM || ap->ai_socktype == SOCK_DGRAM)) { struct in_addr infoaddr = ((struct sockaddr_in *) ap->ai_addr)->sin_addr; if (ap_last != NULL && ap_last->ai_family == ap->ai_family && memcmp(&infoaddr, &in_addr_last, sizeof(struct in_addr)) == 0) continue; cli->client.network.cidr = cidr_from_inaddr(&infoaddr); cli->type = NETWORK_CLIENT; ap_last = ap; in_addr_last = infoaddr; } else if (ap->ai_family == AF_INET6 && (ap->ai_socktype == SOCK_STREAM || ap->ai_socktype == SOCK_DGRAM)) { struct in6_addr infoaddr = ((struct sockaddr_in6 *) ap->ai_addr)->sin6_addr; if (ap_last != NULL && ap_last->ai_family == ap->ai_family && !memcmp(&infoaddr, &in6_addr_last, sizeof(struct in6_addr))) continue; /* IPv6 address */ cli->client.network.cidr = cidr_from_in6addr(&infoaddr); cli->type = NETWORK_CLIENT; ap_last = ap; in6_addr_last = infoaddr; } else continue; cli->client_perms = *perms; LogClientListEntry(NIV_MID_DEBUG, COMPONENT_CONFIG, __LINE__, (char *) __func__, "", cli); glist_add_tail(client_list, &cli->cle_list); cli = NULL; /* let go of it */ } freeaddrinfo(info); goto out; } else { config_proc_error(cnode, err_type, "Client (%s)not found because %s", client_tok, gai_strerror(rc)); err_type->bogus = true; errcnt++; } break; default: config_proc_error(cnode, err_type, "Expected a client, got a %s for (%s)", config_term_desc(type_hint), client_tok); err_type->bogus = true; errcnt++; goto out; } cli->client_perms = *perms; LogClientListEntry(NIV_MID_DEBUG, COMPONENT_CONFIG, __LINE__, (char *) __func__, "", cli); glist_add_tail(client_list, &cli->cle_list); cli = NULL; out: if (cli != NULL) gsh_free(cli); return errcnt; } /** * @brief Commit and FSAL sub-block init/commit helpers */ /** * @brief Init for CLIENT sub-block of an export. * * Allocate one exportlist_client structure for parameter * processing. The client_commit will allocate additional * exportlist_client__ storage for each of its enumerated * clients and free the initial block. We only free that * resource here on errors. */ static void *client_init(void *link_mem, void *self_struct) { struct exportlist_client_entry__ *cli; assert(link_mem != NULL || self_struct != NULL); if (link_mem == NULL) { return self_struct; } else if (self_struct == NULL) { cli = gsh_calloc(1, sizeof(struct exportlist_client_entry__)); glist_init(&cli->cle_list); cli->type = PROTO_CLIENT; return cli; } else { /* free resources case */ cli = self_struct; if (!glist_empty(&cli->cle_list)) FreeClientList(&cli->cle_list); assert(glist_empty(&cli->cle_list)); gsh_free(cli); return NULL; } } /** * @brief Commit this client block * * Validate "clients" token(s) and perms. We enter with a client entry * allocated by proc_block. Since we expand the clients token both * here and in add_client, we allocate new client entries and free * what was passed to us rather than try and link it in. * * @param node [IN] the config_node **not used** * @param link_mem [IN] the exportlist entry. add_client adds to its glist. * @param self_struct [IN] the filled out client entry with a PROTO_CLIENT * * @return 0 on success, error count for failure. */ static int client_commit(void *node, void *link_mem, void *self_struct, struct config_error_type *err_type) { struct exportlist_client_entry__ *cli; struct gsh_export *export; int errcnt = 0; export = container_of(link_mem, struct gsh_export, clients); cli = self_struct; assert(cli->type == PROTO_CLIENT); if (glist_empty(&cli->cle_list)) { LogCrit(COMPONENT_CONFIG, "No clients specified"); err_type->invalid = true; errcnt++; } else { glist_splice_tail(&export->clients, &cli->cle_list); } if (errcnt == 0) client_init(link_mem, self_struct); return errcnt; } /** * @brief Clean up EXPORT path strings */ void clean_export_paths(struct gsh_export *export) { /* Some admins stuff a '/' at the end for some reason. * chomp it so we have a /dir/path/basename to work * with. But only if it's a non-root path starting * with /. */ if (export->fullpath && export->fullpath[0] == '/') { int pathlen; pathlen = strlen(export->fullpath); while ((export->fullpath[pathlen - 1] == '/') && (pathlen > 1)) pathlen--; export->fullpath[pathlen] = '\0'; } /* Remove trailing slash */ if (export->pseudopath && export->pseudopath[0] == '/') { int pathlen; pathlen = strlen(export->pseudopath); while ((export->pseudopath[pathlen - 1] == '/') && (pathlen > 1)) pathlen--; export->pseudopath[pathlen] = '\0'; } } /** * @brief Commit a FSAL sub-block * * Use the Name parameter passed in via the link_mem to lookup the * fsal. If the fsal is not loaded (yet), load it and call its init. * * Create an export and pass the FSAL sub-block to it so that the * fsal method can process the rest of the parameters in the block */ static int fsal_cfg_commit(void *node, void *link_mem, void *self_struct, struct config_error_type *err_type) { struct fsal_export **exp_hdl = link_mem; struct gsh_export *export = container_of(exp_hdl, struct gsh_export, fsal_export); struct fsal_args *fp = self_struct; struct fsal_module *fsal; struct root_op_context root_op_context; uint64_t MaxRead, MaxWrite; fsal_status_t status; int errcnt; /* Initialize req_ctx */ init_root_op_context(&root_op_context, export, NULL, 0, 0, UNKNOWN_REQUEST); errcnt = fsal_load_init(node, fp->name, &fsal, err_type); if (errcnt > 0) goto err; clean_export_paths(export); /* The handle cache (currently MDCACHE) must be at the top of the stack * of FSALs. To achieve this, call directly into MDCACHE, passing the * sub-FSAL's fsal_module. MDCACHE will stack itself on top of that * FSAL, continuing down the chain. */ status = mdcache_fsal_create_export(fsal, node, err_type, &fsal_up_top); PTHREAD_RWLOCK_rdlock(&export_opt_lock); if ((export->options_set & EXPORT_OPTION_EXPIRE_SET) == 0) export->expire_time_attr = export_opt.expire_time_attr; PTHREAD_RWLOCK_unlock(&export_opt_lock); if (FSAL_IS_ERROR(status)) { fsal_put(fsal); LogCrit(COMPONENT_CONFIG, "Could not create export for (%s) to (%s)", export->pseudopath, export->fullpath); LogFullDebug(COMPONENT_FSAL, "FSAL %s refcount %"PRIu32, fsal->name, atomic_fetch_int32_t(&fsal->refcount)); err_type->export_ = true; errcnt++; goto err; } assert(root_op_context.req_ctx.fsal_export != NULL); export->fsal_export = root_op_context.req_ctx.fsal_export; /* We are connected up to the fsal side. Now * validate maxread/write etc with fsal params */ MaxRead = export->fsal_export->exp_ops.fs_maxread(export->fsal_export); MaxWrite = export->fsal_export->exp_ops.fs_maxwrite(export->fsal_export); if (export->MaxRead > MaxRead && MaxRead != 0) { LogInfo(COMPONENT_CONFIG, "Readjusting MaxRead to FSAL, %" PRIu64 " -> %" PRIu64, export->MaxRead, MaxRead); export->MaxRead = MaxRead; } if (export->MaxWrite > MaxWrite && MaxWrite != 0) { LogInfo(COMPONENT_CONFIG, "Readjusting MaxWrite to FSAL, %"PRIu64" -> %"PRIu64, export->MaxWrite, MaxWrite); export->MaxWrite = MaxWrite; } err: release_root_op_context(); /* Don't leak the FSAL block */ err_type->dispose = true; return errcnt; } /** * @brief Commit a FSAL sub-block for export update * * Use the Name parameter passed in via the link_mem to lookup the * fsal. If the fsal is not loaded (yet), load it and call its init. * * Create an export and pass the FSAL sub-block to it so that the * fsal method can process the rest of the parameters in the block */ static int fsal_update_cfg_commit(void *node, void *link_mem, void *self_struct, struct config_error_type *err_type) { struct fsal_export **exp_hdl = link_mem; struct gsh_export *probe_exp; struct gsh_export *export = container_of(exp_hdl, struct gsh_export, fsal_export); struct root_op_context root_op_context; uint64_t MaxRead, MaxWrite; /* Determine if this is actually an update */ probe_exp = get_gsh_export(export->export_id); if (probe_exp == NULL) { /* Export not found by ID, assume it's a new export. */ return fsal_cfg_commit(node, link_mem, self_struct, err_type); } /** @todo - we really should verify update of FSAL options in * export, at the moment any changes there will be * ignored. In fact, we can't even process the * FSAL name... */ /* We have to clean the export paths so we can properly compare them * later. */ clean_export_paths(export); PTHREAD_RWLOCK_rdlock(&export_opt_lock); if ((export->options_set & EXPORT_OPTION_EXPIRE_SET) == 0) export->expire_time_attr = export_opt.expire_time_attr; PTHREAD_RWLOCK_unlock(&export_opt_lock); /* We don't assign export->fsal_export because we don't have a new * fsal_export to later release... */ /* Initialize req_ctx from the probe_exp */ init_root_op_context(&root_op_context, probe_exp, probe_exp->fsal_export, 0, 0, UNKNOWN_REQUEST); /* Now validate maxread/write etc with fsal params based on the * original export, which will then allow us to validate the * possibly changed values in the new export config. */ MaxRead = probe_exp->fsal_export->exp_ops.fs_maxread(probe_exp->fsal_export); MaxWrite = probe_exp->fsal_export->exp_ops.fs_maxwrite(probe_exp->fsal_export); release_root_op_context(); if (export->MaxRead > MaxRead && MaxRead != 0) { LogInfo(COMPONENT_CONFIG, "Readjusting MaxRead to FSAL, %" PRIu64 " -> %" PRIu64, export->MaxRead, MaxRead); export->MaxRead = MaxRead; } if (export->MaxWrite > MaxWrite && MaxWrite != 0) { LogInfo(COMPONENT_CONFIG, "Readjusting MaxWrite to FSAL, %"PRIu64" -> %"PRIu64, export->MaxWrite, MaxWrite); export->MaxWrite = MaxWrite; } LogDebug(COMPONENT_CONFIG, "Export %d FSAL config update processed", export->export_id); put_gsh_export(probe_exp); return 0; } /** * @brief EXPORT block handlers */ /** * @brief Initialize an export block * * There is no link_mem init required because we are allocating * here and doing an insert_gsh_export at the end of export_commit * to attach it to the export manager. * * Use free_exportlist here because in this case, we have not * gotten far enough to hand it over to the export manager. */ static void *export_init(void *link_mem, void *self_struct) { struct gsh_export *export; if (self_struct == NULL) { export = alloc_export(); return export; } else { /* free resources case */ export = self_struct; /* As part of create_export(), FSAL shall take * reference to the export if it supports pNFS. */ if (export->has_pnfs_ds) { assert(export->refcnt == 1); /* export is not yet added to the export * manager. Hence there shall not be any * other thread racing here. So no need * to take lock. */ export->has_pnfs_ds = false; pnfs_ds_remove(export->export_id, true); } else { assert(export->refcnt == 0); free_export(export); } return NULL; } } static inline int strcmp_null(const char *s1, const char *s2) { if (s1 == s2) { /* Both strings are NULL or both are same pointer */ return 0; } if (s1 == NULL) { /* First string is NULL, consider that LESS than */ return -1; } if (s2 == NULL) { /* Second string is NULL, consider that GREATER than */ return 1; } return strcmp(s1, s2); } static inline void update_atomic_fields(struct gsh_export *export, struct gsh_export *src) { atomic_store_uint64_t(&export->MaxRead, src->MaxRead); atomic_store_uint64_t(&export->MaxWrite, src->MaxWrite); atomic_store_uint64_t(&export->PrefRead, src->PrefRead); atomic_store_uint64_t(&export->PrefWrite, src->PrefWrite); atomic_store_uint64_t(&export->PrefReaddir, src->PrefReaddir); atomic_store_uint64_t(&export->MaxOffsetWrite, src->MaxOffsetWrite); atomic_store_uint64_t(&export->MaxOffsetRead, src->MaxOffsetRead); atomic_store_uint32_t(&export->options, src->options); atomic_store_uint32_t(&export->options_set, src->options_set); atomic_store_int32_t(&export->expire_time_attr, src->expire_time_attr); } /** * @brief Commit an export block * * Validate the export level parameters. fsal and client * parameters are already done. */ enum export_commit_type { initial_export, add_export, update_export, }; static int export_commit_common(void *node, void *link_mem, void *self_struct, struct config_error_type *err_type, enum export_commit_type commit_type) { struct gsh_export *export = self_struct, *probe_exp; int errcnt = 0; char perms[1024] = "\0"; struct display_buffer dspbuf = {sizeof(perms), perms, perms}; LogFullDebug(COMPONENT_EXPORT, "Processing %p", export); /* validate the export now */ if (export->export_perms.options & EXPORT_OPTION_NFSV4) { if (export->pseudopath == NULL) { LogCrit(COMPONENT_CONFIG, "Exporting to NFSv4 but no Pseudo path defined"); err_type->invalid = true; errcnt++; return errcnt; } else if (export->export_id == 0 && strcmp(export->pseudopath, "/") != 0) { LogCrit(COMPONENT_CONFIG, "Export id 0 can only export \"/\" not (%s)", export->pseudopath); err_type->invalid = true; errcnt++; return errcnt; } } /* If we are using mount_path_pseudo = true we MUST have a Pseudo Path. */ if (nfs_param.core_param.mount_path_pseudo && export->pseudopath == NULL) { LogCrit(COMPONENT_CONFIG, "NFS_CORE_PARAM mount_path_pseudo is TRUE but no Pseudo path defined"); err_type->invalid = true; errcnt++; return errcnt; } if (export->pseudopath != NULL && export->pseudopath[0] != '/') { LogCrit(COMPONENT_CONFIG, "A Pseudo path must be an absolute path"); err_type->invalid = true; errcnt++; } if (export->export_id == 0) { if (export->pseudopath == NULL) { LogCrit(COMPONENT_CONFIG, "Pseudo path must be \"/\" for export id 0"); err_type->invalid = true; errcnt++; } else if (export->pseudopath[1] != '\0') { LogCrit(COMPONENT_CONFIG, "Pseudo path must be \"/\" for export id 0"); err_type->invalid = true; errcnt++; } if ((export->export_perms.options & EXPORT_OPTION_PROTOCOLS) != EXPORT_OPTION_NFSV4) { LogCrit(COMPONENT_CONFIG, "Export id 0 must include 4 in Protocols"); err_type->invalid = true; errcnt++; } } if (errcnt) return errcnt; /* have basic errors. don't even try more... */ /* Note: need to check export->fsal_export AFTER we have checked for * duplicate export_id. That is because an update export WILL NOT * have fsal_export attached. */ probe_exp = get_gsh_export(export->export_id); if (commit_type == update_export && probe_exp != NULL) { /* We have an actual update case, probe_exp is the target * to update. Check all the options that MUST match. * Note that Path/fullpath will not be NULL, but we compare * the same way as the other string options for code * consistency. */ LogFullDebug(COMPONENT_EXPORT, "Updating %p", probe_exp); LogMidDebug(COMPONENT_EXPORT, "Old Client List"); display_clients(probe_exp); LogMidDebug(COMPONENT_EXPORT, "New Client List"); display_clients(export); if (strcmp_null(export->FS_tag, probe_exp->FS_tag) != 0) { /* Tag does not match, currently not a candidate for * update. */ LogCrit(COMPONENT_CONFIG, "Tag for export update %d doesn't match", export->export_id); err_type->invalid = true; errcnt++; } if (strcmp_null(export->pseudopath, probe_exp->pseudopath) != 0) { /* Pseudo does not match, currently not a candidate for * update. */ LogCrit(COMPONENT_CONFIG, "Pseudo for export update %d doesn't match", export->export_id); err_type->invalid = true; errcnt++; } if (strcmp_null(export->fullpath, probe_exp->fullpath) != 0) { /* Path does not match, currently not a candidate for * update. */ LogCrit(COMPONENT_CONFIG, "Path for export update %d doesn't match", export->export_id); err_type->invalid = true; errcnt++; } /* At present Filesystem_Id is not updateable, check that * it did not change. */ if (probe_exp->filesystem_id.major != export->filesystem_id.major || probe_exp->filesystem_id.minor != export->filesystem_id.minor) { LogCrit(COMPONENT_CONFIG, "Filesystem_Id for export update %d doesn't match", export->export_id); err_type->invalid = true; errcnt++; } /* We can't compare the FSAL names because we don't actually * have an fsal_export for "export". */ if (errcnt > 0) { put_gsh_export(probe_exp); return errcnt; } /* Update atomic fields */ update_atomic_fields(probe_exp, export); /* Now take lock and swap out client list and export_perms... */ PTHREAD_RWLOCK_wrlock(&probe_exp->lock); /* Copy the export perms into the existing export. */ probe_exp->export_perms = export->export_perms; /* Swap the client list from the new export and the existing * export. When we then dispose of the new export, the * old client list will also be disposed of. */ LogFullDebug(COMPONENT_EXPORT, "Original clients = (%p,%p) New clients = (%p,%p)", probe_exp->clients.next, probe_exp->clients.prev, export->clients.next, export->clients.prev); glist_swap_lists(&probe_exp->clients, &export->clients); PTHREAD_RWLOCK_unlock(&probe_exp->lock); /* We will need to dispose of the config export since we * updated the existing export. */ err_type->dispose = true; /* Release the reference to the updated export. */ put_gsh_export(probe_exp); goto success; } if (commit_type == update_export) { /* We found a new export during export update, consider it * an add_export for the rest of configuration. */ commit_type = add_export; } if (probe_exp != NULL) { LogDebug(COMPONENT_CONFIG, "Export %d already exists", export->export_id); put_gsh_export(probe_exp); err_type->exists = true; errcnt++; } /* export->fsal_export is valid iff fsal_cfg_commit succeeds. * Config code calls export_commit even if fsal_cfg_commit fails at * the moment, so error out here if fsal_cfg_commit failed. */ if (export->fsal_export == NULL) { err_type->validate = true; errcnt++; return errcnt; } if (export->FS_tag != NULL) { probe_exp = get_gsh_export_by_tag(export->FS_tag); if (probe_exp != NULL) { put_gsh_export(probe_exp); LogCrit(COMPONENT_CONFIG, "Tag (%s) is a duplicate", export->FS_tag); if (!err_type->exists) err_type->invalid = true; errcnt++; } } if (export->pseudopath != NULL) { probe_exp = get_gsh_export_by_pseudo(export->pseudopath, true); if (probe_exp != NULL) { LogCrit(COMPONENT_CONFIG, "Pseudo path (%s) is a duplicate", export->pseudopath); if (!err_type->exists) err_type->invalid = true; errcnt++; put_gsh_export(probe_exp); } } probe_exp = get_gsh_export_by_path(export->fullpath, true); if (probe_exp != NULL) { if (export->pseudopath == NULL && export->FS_tag == NULL) { LogCrit(COMPONENT_CONFIG, "Duplicate path (%s) without unique tag or Pseudo path", export->fullpath); err_type->invalid = true; errcnt++; } /* If unique Tag and/or Pseudo, there is no error, but we still * need to release the export reference. */ put_gsh_export(probe_exp); } if (errcnt) { if (err_type->exists && !err_type->invalid) LogDebug(COMPONENT_CONFIG, "Duplicate export id = %d", export->export_id); else LogCrit(COMPONENT_CONFIG, "Duplicate export id = %d", export->export_id); return errcnt; /* have errors. don't init or load a fsal */ } if (commit_type != initial_export) { /* add_export or update_export with new export_id. */ int rc = init_export_root(export); if (rc) { switch (rc) { case EINVAL: err_type->invalid = true; break; case EFAULT: err_type->internal = true; break; default: err_type->resource = true; } errcnt++; return errcnt; } if (!mount_gsh_export(export)) { err_type->internal = true; errcnt++; return errcnt; } } if (!insert_gsh_export(export)) { LogCrit(COMPONENT_CONFIG, "Export id %d already in use.", export->export_id); err_type->exists = true; errcnt++; return errcnt; } /* add_export_commit shouldn't add this export to mount work as * add_export_commit deals with creating pseudo mount directly. * So add this export to mount work only if NFSv4 exported and * is not a dynamically added export. */ if (commit_type == initial_export && export->export_perms.options & EXPORT_OPTION_NFSV4) export_add_to_mount_work(export); display_clients(export); success: (void) StrExportOptions(&dspbuf, &export->export_perms); LogInfo(COMPONENT_CONFIG, "Export %d %s at pseudo (%s) with path (%s) and tag (%s) perms (%s)", export->export_id, commit_type == update_export ? "updated" : "created", export->pseudopath, export->fullpath, export->FS_tag, perms); LogInfo(COMPONENT_CONFIG, "Export %d has %zd defined clients", export->export_id, glist_length(&export->clients)); if (commit_type != update_export) { /* For initial or add export, insert_gsh_export gave out * two references, a sentinel reference for the export's * presence in the export table, and one reference for our * use here, drop that second reference now. * * In the case of update_export, we already dropped the * reference to the updated export, and this export has * no references and will be freed by the config code. */ put_gsh_export(export); } return errcnt; } static int export_commit(void *node, void *link_mem, void *self_struct, struct config_error_type *err_type) { return export_commit_common(node, link_mem, self_struct, err_type, initial_export); } /** * @brief Display an export block * * Validate the export level parameters. fsal and client * parameters are already done. */ static void export_display(const char *step, void *node, void *link_mem, void *self_struct) { struct gsh_export *export = self_struct; char perms[1024] = "\0"; struct display_buffer dspbuf = {sizeof(perms), perms, perms}; (void) StrExportOptions(&dspbuf, &export->export_perms); LogMidDebug(COMPONENT_CONFIG, "%s %p Export %d pseudo (%s) with path (%s) and tag (%s) perms (%s)", step, export, export->export_id, export->pseudopath, export->fullpath, export->FS_tag, perms); } /** * @brief Commit an add export * commit the export * init export root and mount it in pseudo fs */ static int add_export_commit(void *node, void *link_mem, void *self_struct, struct config_error_type *err_type) { return export_commit_common(node, link_mem, self_struct, err_type, add_export); } /** * @brief Commit an update export * commit the export * init export root and mount it in pseudo fs */ static int update_export_commit(void *node, void *link_mem, void *self_struct, struct config_error_type *err_type) { return export_commit_common(node, link_mem, self_struct, err_type, update_export); } /** * @brief Initialize an EXPORT_DEFAULTS block * */ static void *export_defaults_init(void *link_mem, void *self_struct) { if (self_struct == NULL) return &export_opt_cfg; else return NULL; } /** * @brief Commit an EXPORT_DEFAULTS block * * Validate the export level parameters. fsal and client * parameters are already done. */ static int export_defaults_commit(void *node, void *link_mem, void *self_struct, struct config_error_type *err_type) { char perms[1024] = "\0"; struct display_buffer dspbuf = {sizeof(perms), perms, perms}; (void) StrExportOptions(&dspbuf, &export_opt_cfg.conf); LogInfo(COMPONENT_CONFIG, "Export Defaults now (%s)", perms); /* Update under lock. */ PTHREAD_RWLOCK_wrlock(&export_opt_lock); export_opt = export_opt_cfg; PTHREAD_RWLOCK_unlock(&export_opt_lock); return 0; } /** * @brief Display an EXPORT_DEFAULTS block * * Validate the export level parameters. fsal and client * parameters are already done. */ static void export_defaults_display(const char *step, void *node, void *link_mem, void *self_struct) { struct export_perms *defaults = self_struct; char perms[1024] = "\0"; struct display_buffer dspbuf = {sizeof(perms), perms, perms}; (void) StrExportOptions(&dspbuf, defaults); LogMidDebug(COMPONENT_CONFIG, "%s Export Defaults (%s)", step, perms); } /** * @brief Configuration processing tables for EXPORT blocks */ /** * @brief Access types list for the Access_type parameter */ static struct config_item_list access_types[] = { CONFIG_LIST_TOK("NONE", 0), CONFIG_LIST_TOK("RW", (EXPORT_OPTION_RW_ACCESS | EXPORT_OPTION_MD_ACCESS)), CONFIG_LIST_TOK("RO", (EXPORT_OPTION_READ_ACCESS | EXPORT_OPTION_MD_READ_ACCESS)), CONFIG_LIST_TOK("MDONLY", EXPORT_OPTION_MD_ACCESS), CONFIG_LIST_TOK("MDONLY_RO", EXPORT_OPTION_MD_READ_ACCESS), CONFIG_LIST_EOL }; /** * @brief Protocols options list for NFS_Protocols parameter */ static struct config_item_list nfs_protocols[] = { CONFIG_LIST_TOK("3", EXPORT_OPTION_NFSV3), CONFIG_LIST_TOK("4", EXPORT_OPTION_NFSV4), CONFIG_LIST_TOK("NFS3", EXPORT_OPTION_NFSV3), CONFIG_LIST_TOK("NFS4", EXPORT_OPTION_NFSV4), CONFIG_LIST_TOK("V3", EXPORT_OPTION_NFSV3), CONFIG_LIST_TOK("V4", EXPORT_OPTION_NFSV4), CONFIG_LIST_TOK("NFSV3", EXPORT_OPTION_NFSV3), CONFIG_LIST_TOK("NFSV4", EXPORT_OPTION_NFSV4), CONFIG_LIST_TOK("9P", EXPORT_OPTION_9P), CONFIG_LIST_EOL }; /** * @brief Transport type options list for Transport_Protocols parameter */ static struct config_item_list transports[] = { CONFIG_LIST_TOK("UDP", EXPORT_OPTION_UDP), CONFIG_LIST_TOK("TCP", EXPORT_OPTION_TCP), CONFIG_LIST_EOL }; /** * @brief Security options list for SecType parameter */ static struct config_item_list sec_types[] = { CONFIG_LIST_TOK("none", EXPORT_OPTION_AUTH_NONE), CONFIG_LIST_TOK("sys", EXPORT_OPTION_AUTH_UNIX), CONFIG_LIST_TOK("krb5", EXPORT_OPTION_RPCSEC_GSS_NONE), CONFIG_LIST_TOK("krb5i", EXPORT_OPTION_RPCSEC_GSS_INTG), CONFIG_LIST_TOK("krb5p", EXPORT_OPTION_RPCSEC_GSS_PRIV), CONFIG_LIST_EOL }; /** * @brief Client UID squash item list for Squash parameter */ static struct config_item_list squash_types[] = { CONFIG_LIST_TOK("Root", EXPORT_OPTION_ROOT_SQUASH), CONFIG_LIST_TOK("Root_Squash", EXPORT_OPTION_ROOT_SQUASH), CONFIG_LIST_TOK("RootSquash", EXPORT_OPTION_ROOT_SQUASH), CONFIG_LIST_TOK("All", EXPORT_OPTION_ALL_ANONYMOUS), CONFIG_LIST_TOK("All_Squash", EXPORT_OPTION_ALL_ANONYMOUS), CONFIG_LIST_TOK("AllSquash", EXPORT_OPTION_ALL_ANONYMOUS), CONFIG_LIST_TOK("All_Anonymous", EXPORT_OPTION_ALL_ANONYMOUS), CONFIG_LIST_TOK("AllAnonymous", EXPORT_OPTION_ALL_ANONYMOUS), CONFIG_LIST_TOK("No_Root_Squash", EXPORT_OPTION_ROOT), CONFIG_LIST_TOK("None", EXPORT_OPTION_ROOT), CONFIG_LIST_TOK("NoIdSquash", EXPORT_OPTION_ROOT), CONFIG_LIST_TOK("RootId", EXPORT_OPTION_ROOT_ID_SQUASH), CONFIG_LIST_TOK("Root_Id_Squash", EXPORT_OPTION_ROOT_ID_SQUASH), CONFIG_LIST_TOK("RootIdSquash", EXPORT_OPTION_ROOT_ID_SQUASH), CONFIG_LIST_EOL }; /** * @brief Delegations types list for the Delegations parameter */ static struct config_item_list delegations[] = { CONFIG_LIST_TOK("NONE", EXPORT_OPTION_NO_DELEGATIONS), CONFIG_LIST_TOK("Read", EXPORT_OPTION_READ_DELEG), CONFIG_LIST_TOK("Write", EXPORT_OPTION_WRITE_DELEG), CONFIG_LIST_TOK("Readwrite", EXPORT_OPTION_DELEGATIONS), CONFIG_LIST_TOK("R", EXPORT_OPTION_READ_DELEG), CONFIG_LIST_TOK("W", EXPORT_OPTION_WRITE_DELEG), CONFIG_LIST_TOK("RW", EXPORT_OPTION_DELEGATIONS), CONFIG_LIST_EOL }; struct config_item_list deleg_types[] = { CONFIG_LIST_TOK("NONE", FSAL_OPTION_NO_DELEGATIONS), CONFIG_LIST_TOK("Read", FSAL_OPTION_FILE_READ_DELEG), CONFIG_LIST_TOK("Write", FSAL_OPTION_FILE_WRITE_DELEG), CONFIG_LIST_TOK("Readwrite", FSAL_OPTION_FILE_DELEGATIONS), CONFIG_LIST_TOK("R", FSAL_OPTION_FILE_READ_DELEG), CONFIG_LIST_TOK("W", FSAL_OPTION_FILE_WRITE_DELEG), CONFIG_LIST_TOK("RW", FSAL_OPTION_FILE_DELEGATIONS), CONFIG_LIST_EOL }; #define CONF_EXPORT_PERMS(_struct_, _perms_) \ /* Note: Access_Type defaults to None on purpose */ \ CONF_ITEM_ENUM_BITS_SET("Access_Type", \ EXPORT_OPTION_NO_ACCESS, \ EXPORT_OPTION_ACCESS_MASK, \ access_types, _struct_, _perms_.options, _perms_.set), \ CONF_ITEM_LIST_BITS_SET("Protocols", \ EXPORT_OPTION_PROTO_DEFAULTS, EXPORT_OPTION_PROTOCOLS, \ nfs_protocols, _struct_, _perms_.options, _perms_.set), \ CONF_ITEM_LIST_BITS_SET("Transports", \ EXPORT_OPTION_XPORT_DEFAULTS, EXPORT_OPTION_TRANSPORTS, \ transports, _struct_, _perms_.options, _perms_.set), \ CONF_ITEM_ANON_ID_SET("Anonymous_uid", \ ANON_UID, _struct_, _perms_.anonymous_uid, \ EXPORT_OPTION_ANON_UID_SET, _perms_.set), \ CONF_ITEM_ANON_ID_SET("Anonymous_gid", \ ANON_GID, _struct_, _perms_.anonymous_gid, \ EXPORT_OPTION_ANON_GID_SET, _perms_.set), \ CONF_ITEM_LIST_BITS_SET("SecType", \ EXPORT_OPTION_AUTH_DEFAULTS, EXPORT_OPTION_AUTH_TYPES, \ sec_types, _struct_, _perms_.options, _perms_.set), \ CONF_ITEM_BOOLBIT_SET("PrivilegedPort", \ false, EXPORT_OPTION_PRIVILEGED_PORT, \ _struct_, _perms_.options, _perms_.set), \ CONF_ITEM_BOOLBIT_SET("Manage_Gids", \ false, EXPORT_OPTION_MANAGE_GIDS, \ _struct_, _perms_.options, _perms_.set), \ CONF_ITEM_LIST_BITS_SET("Squash", \ EXPORT_OPTION_ROOT_SQUASH, EXPORT_OPTION_SQUASH_TYPES, \ squash_types, _struct_, _perms_.options, _perms_.set), \ CONF_ITEM_BOOLBIT_SET("NFS_Commit", \ false, EXPORT_OPTION_COMMIT, \ _struct_, _perms_.options, _perms_.set), \ CONF_ITEM_ENUM_BITS_SET("Delegations", \ EXPORT_OPTION_NO_DELEGATIONS, EXPORT_OPTION_DELEGATIONS,\ delegations, _struct_, _perms_.options, _perms_.set) /** * @brief Process a list of clients for a client block * * CONFIG_PROC handler that gets called for each token in the term list. * Create a exportlist_client_entry__ for each token and link it into * the proto client's cle_list list head. We will pass that head to the * export in commit. * * NOTES: this is the place to expand a node list with perhaps moving the * call to add_client into the expander rather than build a list there * to be then walked here... * * @param token [IN] pointer to token string from parse tree * @param type_hint [IN] a type hint from what the parser recognized * @param item [IN] pointer to the config item table entry * @param param_addr [IN] pointer to prototype client entry * @param err_type [OUT] error handling * @return error count */ static int client_adder(const char *token, enum term_type type_hint, struct config_item *item, void *param_addr, void *cnode, struct config_error_type *err_type) { struct exportlist_client_entry__ *proto_cli; int rc; proto_cli = container_of(param_addr, struct exportlist_client_entry__, cle_list); LogMidDebug(COMPONENT_CONFIG, "Adding client %s", token); rc = add_client(&proto_cli->cle_list, token, type_hint, &proto_cli->client_perms, cnode, err_type); return rc; } /** * @brief Table of client sub-block parameters * * NOTE: node discovery is ordered by this table! * "Clients" is last because we must have all other params processed * before we walk the list of accessing clients! */ static struct config_item client_params[] = { CONF_EXPORT_PERMS(exportlist_client_entry__, client_perms), CONF_ITEM_PROC("Clients", noop_conf_init, client_adder, exportlist_client_entry__, cle_list), CONFIG_EOL }; /** * @brief Table of DEXPORT_DEFAULTS block parameters * * NOTE: node discovery is ordered by this table! */ static struct config_item export_defaults_params[] = { CONF_EXPORT_PERMS(global_export_perms, conf), CONFIG_EOL }; /** * @brief Table of FSAL sub-block parameters * * NOTE: this points to a struct that is private to * fsal_cfg_commit. */ static struct config_item fsal_params[] = { CONF_ITEM_STR("Name", 1, 10, NULL, fsal_args, name), /* cheater union */ CONFIG_EOL }; /** * @brief Common EXPORT block parameters */ #define CONF_EXPORT_PARAMS(_struct_) \ CONF_MAND_UI16("Export_id", 0, UINT16_MAX, 1, \ _struct_, export_id), \ CONF_MAND_PATH("Path", 1, MAXPATHLEN, NULL, \ _struct_, fullpath), /* must chomp '/' */ \ CONF_UNIQ_PATH("Pseudo", 1, MAXPATHLEN, NULL, \ _struct_, pseudopath), \ CONF_ITEM_UI64_SET("MaxRead", 512, FSAL_MAXIOSIZE, \ FSAL_MAXIOSIZE, _struct_, MaxRead, \ EXPORT_OPTION_MAXREAD_SET, options_set), \ CONF_ITEM_UI64_SET("MaxWrite", 512, FSAL_MAXIOSIZE, \ FSAL_MAXIOSIZE, _struct_, MaxWrite, \ EXPORT_OPTION_MAXWRITE_SET, options_set), \ CONF_ITEM_UI64_SET("PrefRead", 512, FSAL_MAXIOSIZE, \ FSAL_MAXIOSIZE, _struct_, PrefRead, \ EXPORT_OPTION_PREFREAD_SET, options_set), \ CONF_ITEM_UI64_SET("PrefWrite", 512, FSAL_MAXIOSIZE, \ FSAL_MAXIOSIZE, _struct_, PrefWrite, \ EXPORT_OPTION_PREFWRITE_SET, options_set), \ CONF_ITEM_UI64("PrefReaddir", 512, FSAL_MAXIOSIZE, 16384, \ _struct_, PrefReaddir), \ CONF_ITEM_FSID_SET("Filesystem_id", 666, 666, \ _struct_, filesystem_id, /* major.minor */ \ EXPORT_OPTION_FSID_SET, options_set), \ CONF_ITEM_STR("Tag", 1, MAXPATHLEN, NULL, \ _struct_, FS_tag), \ CONF_ITEM_UI64("MaxOffsetWrite", 512, UINT64_MAX, UINT64_MAX, \ _struct_, MaxOffsetWrite), \ CONF_ITEM_UI64("MaxOffsetRead", 512, UINT64_MAX, UINT64_MAX, \ _struct_, MaxOffsetRead), \ CONF_ITEM_BOOLBIT_SET("UseCookieVerifier", \ false, EXPORT_OPTION_USE_COOKIE_VERIFIER, \ _struct_, options, options_set), \ CONF_ITEM_BOOLBIT_SET("DisableReaddirPlus", \ false, EXPORT_OPTION_NO_READDIR_PLUS, \ _struct_, options, options_set), \ CONF_ITEM_BOOLBIT_SET("Trust_Readdir_Negative_Cache", \ false, EXPORT_OPTION_TRUST_READIR_NEGATIVE_CACHE, \ _struct_, options, options_set), \ CONF_ITEM_BOOLBIT_SET("Disable_ACL", \ false, EXPORT_OPTION_DISABLE_ACL, \ _struct_, options, options_set), \ CONF_ITEM_I32_SET("Attr_Expiration_Time", -1, INT32_MAX, 60, \ _struct_, expire_time_attr, \ EXPORT_OPTION_EXPIRE_SET, options_set) /** * @brief Table of EXPORT block parameters */ static struct config_item export_params[] = { CONF_EXPORT_PARAMS(gsh_export), CONF_EXPORT_PERMS(gsh_export, export_perms), /* NOTE: the Client and FSAL sub-blocks must be the *last* * two entries in the list. This is so all other * parameters have been processed before these sub-blocks * are processed. */ CONF_ITEM_BLOCK("Client", client_params, client_init, client_commit, gsh_export, clients), CONF_RELAX_BLOCK("FSAL", fsal_params, fsal_init, fsal_cfg_commit, gsh_export, fsal_export), CONFIG_EOL }; /** * @brief Table of EXPORT update block parameters */ static struct config_item export_update_params[] = { CONF_EXPORT_PARAMS(gsh_export), CONF_EXPORT_PERMS(gsh_export, export_perms), /* NOTE: the Client and FSAL sub-blocks must be the *last* * two entries in the list. This is so all other * parameters have been processed before these sub-blocks * are processed. */ CONF_ITEM_BLOCK("Client", client_params, client_init, client_commit, gsh_export, clients), CONF_RELAX_BLOCK("FSAL", fsal_params, fsal_init, fsal_update_cfg_commit, gsh_export, fsal_export), CONFIG_EOL }; /** * @brief Top level definition for an EXPORT block */ static struct config_block export_param = { .dbus_interface_name = "org.ganesha.nfsd.config.%d", .blk_desc.name = "EXPORT", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = export_init, .blk_desc.u.blk.params = export_params, .blk_desc.u.blk.commit = export_commit, .blk_desc.u.blk.display = export_display }; /** * @brief Top level definition for an ADD EXPORT block */ struct config_block add_export_param = { .dbus_interface_name = "org.ganesha.nfsd.config.%d", .blk_desc.name = "EXPORT", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = export_init, .blk_desc.u.blk.params = export_params, .blk_desc.u.blk.commit = add_export_commit, .blk_desc.u.blk.display = export_display }; /** * @brief Top level definition for an UPDATE EXPORT block */ struct config_block update_export_param = { .dbus_interface_name = "org.ganesha.nfsd.config.%d", .blk_desc.name = "EXPORT", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = export_init, .blk_desc.u.blk.params = export_update_params, .blk_desc.u.blk.commit = update_export_commit, .blk_desc.u.blk.display = export_display }; /** * @brief Top level definition for an EXPORT_DEFAULTS block */ struct config_block export_defaults_param = { .dbus_interface_name = "org.ganesha.nfsd.config.defaults", .blk_desc.name = "EXPORT_DEFAULTS", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = export_defaults_init, .blk_desc.u.blk.params = export_defaults_params, .blk_desc.u.blk.commit = export_defaults_commit, .blk_desc.u.blk.display = export_defaults_display }; /** * @brief builds an export entry for '/' with default parameters * * If export_id = 0 has not been specified, and not other export * for Pseudo "/" has been specified, build an FSAL_PSEUDO export * for the root of the Pseudo FS. * * @return -1 on error, 0 if we already have one, 1 if created one */ static int build_default_root(struct config_error_type *err_type) { struct gsh_export *export; struct fsal_module *fsal_hdl = NULL; struct root_op_context root_op_context; /* See if export_id = 0 has already been specified */ export = get_gsh_export(0); if (export != NULL) { /* export_id = 0 has already been specified */ LogDebug(COMPONENT_CONFIG, "Export 0 already exists"); put_gsh_export(export); return 0; } /* See if another export with Pseudo = "/" has already been specified. */ export = get_gsh_export_by_pseudo("/", true); if (export != NULL) { /* Pseudo = / has already been specified */ LogDebug(COMPONENT_CONFIG, "Pseudo root already exists"); put_gsh_export(export); return 0; } /* allocate and initialize the exportlist part with the id */ LogDebug(COMPONENT_CONFIG, "Allocating Pseudo root export"); export = alloc_export(); /* Initialize req_ctx */ init_root_op_context(&root_op_context, export, NULL, 0, 0, UNKNOWN_REQUEST); export->filesystem_id.major = 152; export->filesystem_id.minor = 152; export->MaxWrite = FSAL_MAXIOSIZE; export->MaxRead = FSAL_MAXIOSIZE; export->PrefWrite = FSAL_MAXIOSIZE; export->PrefRead = FSAL_MAXIOSIZE; export->PrefReaddir = 16384; /*Don't set anonymous uid and gid, they will actually be ignored */ /* Support only NFS v4 and TCP. * Root is allowed * MD Read Access * Allow use of default auth types * * Allow non-privileged client ports to access pseudo export. */ export->export_perms.options = EXPORT_OPTION_ROOT | EXPORT_OPTION_MD_READ_ACCESS | EXPORT_OPTION_NFSV4 | EXPORT_OPTION_AUTH_TYPES | EXPORT_OPTION_TCP; export->export_perms.set = EXPORT_OPTION_SQUASH_TYPES | EXPORT_OPTION_ACCESS_MASK | EXPORT_OPTION_PROTOCOLS | EXPORT_OPTION_TRANSPORTS | EXPORT_OPTION_AUTH_TYPES | EXPORT_OPTION_PRIVILEGED_PORT; export->options = EXPORT_OPTION_USE_COOKIE_VERIFIER; export->options_set = EXPORT_OPTION_FSID_SET | EXPORT_OPTION_USE_COOKIE_VERIFIER | EXPORT_OPTION_MAXREAD_SET | EXPORT_OPTION_MAXWRITE_SET | EXPORT_OPTION_PREFREAD_SET | EXPORT_OPTION_PREFWRITE_SET; /* Set the fullpath to "/" */ export->fullpath = gsh_strdup("/"); /* Set Pseudo Path to "/" */ export->pseudopath = gsh_strdup("/"); /* Assign FSAL_PSEUDO */ fsal_hdl = lookup_fsal("PSEUDO"); if (fsal_hdl == NULL) { LogCrit(COMPONENT_CONFIG, "FSAL PSEUDO is not loaded!"); goto err_out; } else { fsal_status_t rc; rc = mdcache_fsal_create_export(fsal_hdl, NULL, err_type, &fsal_up_top); if (FSAL_IS_ERROR(rc)) { fsal_put(fsal_hdl); LogCrit(COMPONENT_CONFIG, "Could not create FSAL export for %s", export->fullpath); LogFullDebug(COMPONENT_FSAL, "FSAL %s refcount %"PRIu32, fsal_hdl->name, atomic_fetch_int32_t(&fsal_hdl->refcount)); goto err_out; } } assert(root_op_context.req_ctx.fsal_export != NULL); export->fsal_export = root_op_context.req_ctx.fsal_export; if (!insert_gsh_export(export)) { export->fsal_export->exp_ops.release(export->fsal_export); fsal_put(fsal_hdl); LogCrit(COMPONENT_CONFIG, "Failed to insert pseudo root In use??"); LogFullDebug(COMPONENT_FSAL, "FSAL %s refcount %"PRIu32, fsal_hdl->name, atomic_fetch_int32_t(&fsal_hdl->refcount)); goto err_out; } /* This export must be mounted to the PseudoFS */ export_add_to_mount_work(export); LogInfo(COMPONENT_CONFIG, "Export 0 (/) successfully created"); put_gsh_export(export); /* all done, let go */ release_root_op_context(); return 1; err_out: free_export(export); release_root_op_context(); return -1; } /** * @brief Read the export entries from the parsed configuration file. * * @param[in] in_config The file that contains the export list * * @return A negative value on error, * the number of export entries else. */ int ReadExports(config_file_t in_config, struct config_error_type *err_type) { int rc, num_exp; rc = load_config_from_parse(in_config, &export_defaults_param, NULL, false, err_type); if (rc < 0) { LogCrit(COMPONENT_CONFIG, "Export defaults block error"); return -1; } num_exp = load_config_from_parse(in_config, &export_param, NULL, false, err_type); if (num_exp < 0) { LogCrit(COMPONENT_CONFIG, "Export block error"); return -1; } rc = build_default_root(err_type); if (rc < 0) { LogCrit(COMPONENT_CONFIG, "No pseudo root!"); return -1; } return num_exp; } /** * @brief Reread the export entries from the parsed configuration file. * * @param[in] in_config The file that contains the export list * * @return A negative value on error, * the number of export entries else. */ int reread_exports(config_file_t in_config, struct config_error_type *err_type) { int rc, num_exp; LogInfo(COMPONENT_CONFIG, "Reread exports"); rc = load_config_from_parse(in_config, &export_defaults_param, NULL, false, err_type); if (rc < 0) { LogCrit(COMPONENT_CONFIG, "Export defaults block error"); return -1; } num_exp = load_config_from_parse(in_config, &update_export_param, NULL, false, err_type); if (num_exp < 0) { LogCrit(COMPONENT_CONFIG, "Export block error"); return -1; } return num_exp; } static void FreeClientList(struct glist_head *clients) { struct glist_head *glist; struct glist_head *glistn; glist_for_each_safe(glist, glistn, clients) { exportlist_client_entry_t *client; client = glist_entry(glist, exportlist_client_entry_t, cle_list); glist_del(&client->cle_list); if (client->type == NETGROUP_CLIENT && client->client.netgroup.netgroupname != NULL) gsh_free(client->client.netgroup.netgroupname); if (client->type == WILDCARDHOST_CLIENT && client->client.wildcard.wildcard != NULL) gsh_free(client->client.wildcard.wildcard); if (client->type == GSSPRINCIPAL_CLIENT && client->client.gssprinc.princname != NULL) gsh_free(client->client.gssprinc.princname); gsh_free(client); } } /** * @brief Free resources attached to an export * * @param export [IN] pointer to export * * @return true if all went well */ void free_export_resources(struct gsh_export *export) { FreeClientList(&export->clients); if (export->fsal_export != NULL) { struct fsal_module *fsal = export->fsal_export->fsal; export->fsal_export->exp_ops.release(export->fsal_export); fsal_put(fsal); LogFullDebug(COMPONENT_FSAL, "FSAL %s refcount %"PRIu32, fsal->name, atomic_fetch_int32_t(&fsal->refcount)); } export->fsal_export = NULL; /* free strings here */ if (export->fullpath != NULL) gsh_free(export->fullpath); if (export->pseudopath != NULL) gsh_free(export->pseudopath); if (export->FS_tag != NULL) gsh_free(export->FS_tag); } /** * @brief pkginit callback to initialize exports from nfs_init * * Assumes being called with the export_by_id.lock held. * true on success */ static bool init_export_cb(struct gsh_export *exp, void *state) { struct glist_head *errlist = state; if (init_export_root(exp)) { glist_del(&exp->exp_list); glist_add(errlist, &exp->exp_list); } return true; } /** * @brief Initialize exports over a live cache inode and fsal layer */ void exports_pkginit(void) { struct glist_head errlist; struct glist_head *glist, *glistn; struct gsh_export *export; glist_init(&errlist); foreach_gsh_export(init_export_cb, true, &errlist); glist_for_each_safe(glist, glistn, &errlist) { export = glist_entry(glist, struct gsh_export, exp_list); export_revert(export); } } /** * @brief Return a reference to the root object of the export * * Must be called with the caller holding a reference to the export. * * Returns with an additional reference to the obj held for use by the * caller. * * @param export [IN] the aforementioned export * @param entry [IN/OUT] call by ref pointer to store obj * * @return FSAL status */ fsal_status_t nfs_export_get_root_entry(struct gsh_export *export, struct fsal_obj_handle **obj) { PTHREAD_RWLOCK_rdlock(&export->lock); if (export->exp_root_obj) export->exp_root_obj->obj_ops.get_ref(export->exp_root_obj); PTHREAD_RWLOCK_unlock(&export->lock); *obj = export->exp_root_obj; if (!(*obj)) return fsalstat(ERR_FSAL_NOENT, 0); if ((*obj)->type != DIRECTORY) return fsalstat(ERR_FSAL_NOTDIR, 0); return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * @brief Set file systems max read write sizes in the export * * @param export [IN] the export * @param maxread [IN] maxread size * @param maxwrite [IN] maxwrite size */ static void set_fs_max_rdwr_size(struct gsh_export *export, uint64_t maxread, uint64_t maxwrite) { if (maxread != 0) { if (!op_ctx_export_has_option_set(EXPORT_OPTION_MAXREAD_SET)) { LogInfo(COMPONENT_EXPORT, "Readjusting MaxRead to %" PRIu64, maxread); export->MaxRead = maxread; } if (!op_ctx_export_has_option_set(EXPORT_OPTION_PREFREAD_SET) || (export->PrefRead > export->MaxRead)) { LogInfo(COMPONENT_EXPORT, "Readjusting PrefRead to %"PRIu64, export->MaxRead); export->PrefRead = export->MaxRead; } } if (maxwrite != 0) { if (!op_ctx_export_has_option_set(EXPORT_OPTION_MAXWRITE_SET)) { LogInfo(COMPONENT_EXPORT, "Readjusting MaxWrite to %"PRIu64, maxwrite); export->MaxWrite = maxwrite; } if (!op_ctx_export_has_option_set(EXPORT_OPTION_PREFWRITE_SET) || (export->PrefWrite > export->MaxWrite)) { LogInfo(COMPONENT_EXPORT, "Readjusting PrefWrite to %"PRIu64, export->MaxWrite); export->PrefWrite = export->MaxWrite; } } } /** * @brief Initialize the root cache inode for an export. * * Assumes being called with the export_by_id.lock held. * * @param exp [IN] the export * * @return 0 if successful otherwise err. */ int init_export_root(struct gsh_export *export) { fsal_status_t fsal_status; struct fsal_obj_handle *obj; struct root_op_context root_op_context; int my_status; /* Initialize req_ctx */ init_root_op_context(&root_op_context, export, export->fsal_export, 0, 0, UNKNOWN_REQUEST); /* Lookup for the FSAL Path */ LogDebug(COMPONENT_EXPORT, "About to lookup_path for ExportId=%u Path=%s", export->export_id, export->fullpath); /* This takes a reference, which will keep the root object around for * the lifetime of the export. */ fsal_status = export->fsal_export->exp_ops.lookup_path(export->fsal_export, export->fullpath, &obj, NULL); if (FSAL_IS_ERROR(fsal_status)) { my_status = EINVAL; LogCrit(COMPONENT_EXPORT, "Lookup failed on path, ExportId=%u Path=%s FSAL_ERROR=(%s,%u)", export->export_id, export->fullpath, msg_fsal_err(fsal_status.major), fsal_status.minor); goto out; } if (!op_ctx_export_has_option_set(EXPORT_OPTION_MAXREAD_SET) || !op_ctx_export_has_option_set(EXPORT_OPTION_MAXWRITE_SET) || !op_ctx_export_has_option_set(EXPORT_OPTION_PREFREAD_SET) || !op_ctx_export_has_option_set(EXPORT_OPTION_PREFWRITE_SET)) { fsal_dynamicfsinfo_t dynamicinfo; dynamicinfo.maxread = 0; dynamicinfo.maxwrite = 0; fsal_status = export->fsal_export->exp_ops.get_fs_dynamic_info( export->fsal_export, obj, &dynamicinfo); if (!FSAL_IS_ERROR(fsal_status)) { set_fs_max_rdwr_size(export, dynamicinfo.maxread, dynamicinfo.maxwrite); } } PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); PTHREAD_RWLOCK_wrlock(&export->lock); /* Pass ref off to export */ export->exp_root_obj = obj; glist_add_tail(&obj->state_hdl->dir.export_roots, &export->exp_root_list); /* Protect this entry from removal (unlink) */ (void) atomic_inc_int32_t(&obj->state_hdl->dir.exp_root_refcount); PTHREAD_RWLOCK_unlock(&export->lock); PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); if (isDebug(COMPONENT_EXPORT)) { LogDebug(COMPONENT_EXPORT, "Added root obj %p FSAL %s for path %s on export_id=%d", obj, obj->fsal->name, export->fullpath, export->export_id); } else { LogInfo(COMPONENT_EXPORT, "Added root obj for path %s on export_id=%d", export->fullpath, export->export_id); } my_status = 0; out: release_root_op_context(); return my_status; } /** * @brief Release all the export state, including the root object * * @param exp [IN] the export */ static void release_export(struct gsh_export *export) { struct fsal_obj_handle *obj = NULL; fsal_status_t fsal_status; /* Get a reference to the root entry */ fsal_status = nfs_export_get_root_entry(export, &obj); if (FSAL_IS_ERROR(fsal_status)) { /* No more root entry, bail out, this export is * probably about to be destroyed. */ LogInfo(COMPONENT_CACHE_INODE, "Export root for export id %d status %s", export->export_id, msg_fsal_err(fsal_status.major)); return; } /* Make the export unreachable as a root object */ PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock); PTHREAD_RWLOCK_wrlock(&export->lock); glist_del(&export->exp_root_list); export->exp_root_obj->obj_ops.put_ref(export->exp_root_obj); export->exp_root_obj = NULL; (void) atomic_dec_int32_t(&obj->state_hdl->dir.exp_root_refcount); PTHREAD_RWLOCK_unlock(&export->lock); PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock); LogDebug(COMPONENT_EXPORT, "Released root obj %p for path %s on export_id=%d", obj, export->fullpath, export->export_id); /* Make export unreachable via pseudo fs. * We keep the export in the export hash table through the following * so that the underlying FSALs have access to the export while * performing the various cleanup operations. */ pseudo_unmount_export(export); /* Release state belonging to this export */ state_release_export(export); /* Flush FSAL-specific state */ export->fsal_export->exp_ops.unexport(export->fsal_export, obj); /* Remove the mapping to the export now that cleanup is complete. */ remove_gsh_export(export->export_id); /* Release ref taken above */ obj->obj_ops.put_ref(obj); } void unexport(struct gsh_export *export) { bool op_ctx_set = false; struct root_op_context ctx; /* Make the export unreachable */ LogDebug(COMPONENT_EXPORT, "Unexport %s, Pseduo %s", export->fullpath, export->pseudopath); /* Lots of obj_ops may be called during cleanup; make sure that an * op_ctx exists */ if (!op_ctx) { init_root_op_context(&ctx, export, export->fsal_export, 0, 0, UNKNOWN_REQUEST); op_ctx_set = true; } release_export(export); if (op_ctx_set) release_root_op_context(); } /** * @brief Match a specific option in the client export list * * @param[in] hostaddr Host to search for * @param[in] clients Client list to search * @param[out] client_found Matching entry * @param[in] export_option Option to search for * * @return true if found, false otherwise. */ static exportlist_client_entry_t *client_match(sockaddr_t *hostaddr, struct gsh_export *export) { struct glist_head *glist; int rc; int ipvalid = -1; /* -1 need to print, 0 - invalid, 1 - ok */ char hostname[MAXHOSTNAMELEN + 1]; char ipstring[SOCK_NAME_MAX + 1]; CIDR *host_prefix = NULL; exportlist_client_entry_t *client; glist_for_each(glist, &export->clients) { client = glist_entry(glist, exportlist_client_entry_t, cle_list); LogClientListEntry(NIV_MID_DEBUG, COMPONENT_EXPORT, __LINE__, (char *) __func__, "Match V4: ", client); switch (client->type) { case NETWORK_CLIENT: if (host_prefix == NULL) { if (hostaddr->ss_family == AF_INET6) { host_prefix = cidr_from_in6addr( &((struct sockaddr_in6 *) hostaddr)->sin6_addr); } else { host_prefix = cidr_from_inaddr( &((struct sockaddr_in *) hostaddr)->sin_addr); } } if (cidr_contains(client->client.network.cidr, host_prefix) == 0) { goto out; } break; case NETGROUP_CLIENT: /* Try to get the entry from th IP/name cache */ rc = nfs_ip_name_get(hostaddr, hostname, sizeof(hostname)); if (rc == IP_NAME_NOT_FOUND) { /* IPaddr was not cached, add it to the cache */ rc = nfs_ip_name_add(hostaddr, hostname, sizeof(hostname)); } if (rc != IP_NAME_SUCCESS) break; /* Fatal failure */ /* At this point 'hostname' should contain the * name that was found */ if (ng_innetgr(client->client.netgroup.netgroupname, hostname)) { goto out; } break; case WILDCARDHOST_CLIENT: /* Now checking for IP wildcards */ if (ipvalid < 0) ipvalid = sprint_sockip(hostaddr, ipstring, sizeof(ipstring)); if (ipvalid && (fnmatch(client->client.wildcard.wildcard, ipstring, FNM_PATHNAME) == 0)) { goto out; } /* Try to get the entry from th IP/name cache */ rc = nfs_ip_name_get(hostaddr, hostname, sizeof(hostname)); if (rc == IP_NAME_NOT_FOUND) { /* IPaddr was not cached, add it to the cache */ /** @todo this change from 1.5 is not IPv6 * useful. come back to this and use the * string from client mgr inside req_ctx... */ rc = nfs_ip_name_add(hostaddr, hostname, sizeof(hostname)); } if (rc != IP_NAME_SUCCESS) break; /* At this point 'hostname' should contain the * name that was found */ if (fnmatch (client->client.wildcard.wildcard, hostname, FNM_PATHNAME) == 0) { goto out; } break; case GSSPRINCIPAL_CLIENT: /** @todo BUGAZOMEU a completer lors de l'integration de RPCSEC_GSS */ LogCrit(COMPONENT_EXPORT, "Unsupported type GSS_PRINCIPAL_CLIENT"); break; case MATCH_ANY_CLIENT: goto out; case BAD_CLIENT: default: continue; } } client = NULL; out: if (host_prefix != NULL) cidr_free(host_prefix); /* no export found for this option */ return client; } /** * @brief Checks if request security flavor is suffcient for the requested * export * * @param[in] req Related RPC request. * * @return true if the request flavor exists in the matching export * false otherwise */ bool export_check_security(struct svc_req *req) { switch (req->rq_msg.cb_cred.oa_flavor) { case AUTH_NONE: if ((op_ctx->export_perms->options & EXPORT_OPTION_AUTH_NONE) == 0) { LogInfo(COMPONENT_EXPORT, "Export %s does not support AUTH_NONE", op_ctx_export_path(op_ctx->ctx_export)); return false; } break; case AUTH_UNIX: if ((op_ctx->export_perms->options & EXPORT_OPTION_AUTH_UNIX) == 0) { LogInfo(COMPONENT_EXPORT, "Export %s does not support AUTH_UNIX", op_ctx_export_path(op_ctx->ctx_export)); return false; } break; #ifdef _HAVE_GSSAPI case RPCSEC_GSS: if ((op_ctx->export_perms->options & (EXPORT_OPTION_RPCSEC_GSS_NONE | EXPORT_OPTION_RPCSEC_GSS_INTG | EXPORT_OPTION_RPCSEC_GSS_PRIV)) == 0) { LogInfo(COMPONENT_EXPORT, "Export %s does not support RPCSEC_GSS", op_ctx_export_path(op_ctx->ctx_export)); return false; } else { struct rpc_gss_cred *gc = (struct rpc_gss_cred *) req->rq_msg.rq_cred_body; rpc_gss_svc_t svc = gc->gc_svc; LogFullDebug(COMPONENT_EXPORT, "Testing svc %d", (int)svc); switch (svc) { case RPCSEC_GSS_SVC_NONE: if ((op_ctx->export_perms->options & EXPORT_OPTION_RPCSEC_GSS_NONE) == 0) { LogInfo(COMPONENT_EXPORT, "Export %s does not support RPCSEC_GSS_SVC_NONE", op_ctx_export_path( op_ctx->ctx_export)); return false; } break; case RPCSEC_GSS_SVC_INTEGRITY: if ((op_ctx->export_perms->options & EXPORT_OPTION_RPCSEC_GSS_INTG) == 0) { LogInfo(COMPONENT_EXPORT, "Export %s does not support RPCSEC_GSS_SVC_INTEGRITY", op_ctx_export_path( op_ctx->ctx_export)); return false; } break; case RPCSEC_GSS_SVC_PRIVACY: if ((op_ctx->export_perms->options & EXPORT_OPTION_RPCSEC_GSS_PRIV) == 0) { LogInfo(COMPONENT_EXPORT, "Export %s does not support RPCSEC_GSS_SVC_PRIVACY", op_ctx_export_path( op_ctx->ctx_export)); return false; } break; default: LogInfo(COMPONENT_EXPORT, "Export %s does not support unknown RPCSEC_GSS_SVC %d", op_ctx_export_path(op_ctx->ctx_export), (int)svc); return false; } } break; #endif default: LogInfo(COMPONENT_EXPORT, "Export %s does not support unknown oa_flavor %d", op_ctx_export_path(op_ctx->ctx_export), (int)req->rq_msg.cb_cred.oa_flavor); return false; } return true; } static char ten_bytes_all_0[10]; sockaddr_t *convert_ipv6_to_ipv4(sockaddr_t *ipv6, sockaddr_t *ipv4) { struct sockaddr_in *paddr = (struct sockaddr_in *)ipv4; struct sockaddr_in6 *psockaddr_in6 = (struct sockaddr_in6 *)ipv6; /* If the client socket is IPv4, then it is wrapped into a * ::ffff:a.b.c.d IPv6 address. We check this here. * This kind of adress is shaped like this: * |---------------------------------------------------------------| * | 80 bits = 10 bytes | 16 bits = 2 bytes | 32 bits = 4 bytes | * |---------------------------------------------------------------| * | 0 | FFFF | IPv4 address | * |---------------------------------------------------------------| */ if ((ipv6->ss_family == AF_INET6) && !memcmp(psockaddr_in6->sin6_addr.s6_addr, ten_bytes_all_0, 10) && (psockaddr_in6->sin6_addr.s6_addr[10] == 0xFF) && (psockaddr_in6->sin6_addr.s6_addr[11] == 0xFF)) { void *ab; memset(ipv4, 0, sizeof(*ipv4)); ab = &(psockaddr_in6->sin6_addr.s6_addr[12]); paddr->sin_port = psockaddr_in6->sin6_port; paddr->sin_addr.s_addr = *(in_addr_t *) ab; ipv4->ss_family = AF_INET; if (isFullDebug(COMPONENT_EXPORT)) { char ipstring4[SOCK_NAME_MAX]; char ipstring6[SOCK_NAME_MAX]; sprint_sockip(ipv6, ipstring6, sizeof(ipstring6)); sprint_sockip(ipv4, ipstring4, sizeof(ipstring4)); LogMidDebug(COMPONENT_EXPORT, "Converting IPv6 encapsulated IPv4 address %s to IPv4 %s", ipstring6, ipstring4); } return ipv4; } else { return ipv6; } } /** * @brief Get the best anonymous uid available. * * This is safe if there is no op_ctx or there is one but there is no * export_perms attached. * */ uid_t get_anonymous_uid(void) { uid_t anon_uid; if (op_ctx != NULL && op_ctx->export_perms != NULL) { /* We have export_perms, use it. */ return op_ctx->export_perms->anonymous_uid; } PTHREAD_RWLOCK_rdlock(&export_opt_lock); if ((export_opt.conf.set & EXPORT_OPTION_ANON_UID_SET) != 0) { /* Option was set in EXPORT_DEFAULTS */ anon_uid = export_opt.conf.anonymous_uid; } else { /* Default to code default. */ anon_uid = export_opt.def.anonymous_uid; } PTHREAD_RWLOCK_unlock(&export_opt_lock); return anon_uid; } /** * @brief Get the best anonymous gid available. * * This is safe if there is no op_ctx or there is one but there is no * export_perms attached. * */ gid_t get_anonymous_gid(void) { /* Default to code default. */ gid_t anon_gid = export_opt.def.anonymous_gid; if (op_ctx != NULL && op_ctx->export_perms != NULL) { /* We have export_perms, use it. */ return op_ctx->export_perms->anonymous_gid; } PTHREAD_RWLOCK_rdlock(&export_opt_lock); if ((export_opt.conf.set & EXPORT_OPTION_ANON_GID_SET) != 0) { /* Option was set in EXPORT_DEFAULTS */ anon_gid = export_opt.conf.anonymous_gid; } else { /* Default to code default. */ anon_gid = export_opt.def.anonymous_gid; } PTHREAD_RWLOCK_unlock(&export_opt_lock); return anon_gid; } /** * @brief Checks if a machine is authorized to access an export entry * * Permissions in the op context get updated based on export and client. * * Takes the export->lock in read mode to protect the client list and * export permissions while performing this work. */ void export_check_access(void) { exportlist_client_entry_t *client = NULL; sockaddr_t alt_hostaddr; sockaddr_t *hostaddr = NULL; assert(op_ctx != NULL); assert(op_ctx->export_perms != NULL); /* Initialize permissions to allow nothing, anonymous_uid and * anonymous_gid will get set farther down. */ memset(op_ctx->export_perms, 0, sizeof(*op_ctx->export_perms)); if (op_ctx->ctx_export != NULL) { /* Take lock */ PTHREAD_RWLOCK_rdlock(&op_ctx->ctx_export->lock); } else { /* Shortcut if no export */ goto no_export; } hostaddr = convert_ipv6_to_ipv4(op_ctx->caller_addr, &alt_hostaddr); if (isMidDebug(COMPONENT_EXPORT)) { char ipstring[SOCK_NAME_MAX]; ipstring[0] = '\0'; (void) sprint_sockip(hostaddr, ipstring, sizeof(ipstring)); LogMidDebug(COMPONENT_EXPORT, "Check for address %s for export id %u path %s", ipstring, op_ctx->ctx_export->export_id, op_ctx_export_path(op_ctx->ctx_export)); } /* Does the client match anyone on the client list? */ client = client_match(hostaddr, op_ctx->ctx_export); if (client != NULL) { /* Take client options */ op_ctx->export_perms->options = client->client_perms.options & client->client_perms.set; if (client->client_perms.set & EXPORT_OPTION_ANON_UID_SET) op_ctx->export_perms->anonymous_uid = client->client_perms.anonymous_uid; if (client->client_perms.set & EXPORT_OPTION_ANON_GID_SET) op_ctx->export_perms->anonymous_gid = client->client_perms.anonymous_gid; op_ctx->export_perms->set = client->client_perms.set; } /* Any options not set by the client, take from the export */ op_ctx->export_perms->options |= op_ctx->ctx_export->export_perms.options & op_ctx->ctx_export->export_perms.set & ~op_ctx->export_perms->set; if ((op_ctx->export_perms->set & EXPORT_OPTION_ANON_UID_SET) == 0 && (op_ctx->ctx_export->export_perms.set & EXPORT_OPTION_ANON_UID_SET) != 0) op_ctx->export_perms->anonymous_uid = op_ctx->ctx_export->export_perms.anonymous_uid; if ((op_ctx->export_perms->set & EXPORT_OPTION_ANON_GID_SET) == 0 && (op_ctx->ctx_export->export_perms.set & EXPORT_OPTION_ANON_GID_SET) != 0) op_ctx->export_perms->anonymous_gid = op_ctx->ctx_export->export_perms.anonymous_gid; op_ctx->export_perms->set |= op_ctx->ctx_export->export_perms.set; no_export: PTHREAD_RWLOCK_rdlock(&export_opt_lock); /* Any options not set by the client or export, take from the * EXPORT_DEFAULTS block. */ op_ctx->export_perms->options |= export_opt.conf.options & export_opt.conf.set & ~op_ctx->export_perms->set; if ((op_ctx->export_perms->set & EXPORT_OPTION_ANON_UID_SET) == 0 && (export_opt.conf.set & EXPORT_OPTION_ANON_UID_SET) != 0) op_ctx->export_perms->anonymous_uid = export_opt.conf.anonymous_uid; if ((op_ctx->export_perms->set & EXPORT_OPTION_ANON_GID_SET) == 0 && (export_opt.conf.set & EXPORT_OPTION_ANON_GID_SET) != 0) op_ctx->export_perms->anonymous_gid = export_opt.conf.anonymous_gid; op_ctx->export_perms->set |= export_opt.conf.set; /* And finally take any options not yet set from global defaults */ op_ctx->export_perms->options |= export_opt.def.options & ~op_ctx->export_perms->set; if ((op_ctx->export_perms->set & EXPORT_OPTION_ANON_UID_SET) == 0) op_ctx->export_perms->anonymous_uid = export_opt.def.anonymous_uid; if ((op_ctx->export_perms->set & EXPORT_OPTION_ANON_GID_SET) == 0) op_ctx->export_perms->anonymous_gid = export_opt.def.anonymous_gid; op_ctx->export_perms->set |= export_opt.def.set; if (isMidDebug(COMPONENT_EXPORT)) { char perms[1024] = "\0"; struct display_buffer dspbuf = {sizeof(perms), perms, perms}; if (client != NULL) { (void) StrExportOptions(&dspbuf, &client->client_perms); LogMidDebug(COMPONENT_EXPORT, "CLIENT (%s)", perms); display_reset_buffer(&dspbuf); } if (op_ctx->ctx_export != NULL) { (void) StrExportOptions( &dspbuf, &op_ctx->ctx_export->export_perms); LogMidDebug(COMPONENT_EXPORT, "EXPORT (%s)", perms); display_reset_buffer(&dspbuf); } (void) StrExportOptions(&dspbuf, &export_opt.conf); LogMidDebug(COMPONENT_EXPORT, "EXPORT_DEFAULTS (%s)", perms); display_reset_buffer(&dspbuf); (void) StrExportOptions(&dspbuf, &export_opt.def); LogMidDebug(COMPONENT_EXPORT, "default options (%s)", perms); display_reset_buffer(&dspbuf); (void) StrExportOptions(&dspbuf, op_ctx->export_perms); LogMidDebug(COMPONENT_EXPORT, "Final options (%s)", perms); } PTHREAD_RWLOCK_unlock(&export_opt_lock); if (op_ctx->ctx_export != NULL) { /* Release lock */ PTHREAD_RWLOCK_unlock(&op_ctx->ctx_export->lock); } } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/support/fridgethr.c�����������������������������������������������������������0000664�0000000�0000000�00000115764�13242724102�0020351�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @addtogroup fridgethr * @{ */ /** * @file fridgethr.c * @brief Implementation of the thread fridge * */ #include "config.h" #include "log.h" #include <stdlib.h> #include <string.h> #include <pthread.h> #ifdef LINUX #include <sys/signal.h> #elif FREEBSD #include <signal.h> #endif #include "abstract_mem.h" #include "fridgethr.h" #include "nfs_core.h" /** * @brief Initialize a thread fridge * * @note It is more robust to initialize the parameters to 0 than set * specifically what is desired, otherwise uninitialized memory could * provoke unexpected behaviour when new parameters are added. * * @param[out] frout The fridge to initialize * @param[in] s The name of the fridge * @param[in] p Fridge parameters * * @return 0 on success, POSIX errors on failure. */ int fridgethr_init(struct fridgethr **frout, const char *s, const struct fridgethr_params *p) { /* The fridge under construction */ struct fridgethr *frobj = gsh_malloc(sizeof(struct fridgethr)); /* The return code for this function */ int rc = 0; /* True if the thread attributes have been initialized */ bool attrinit = false; /* True if the fridge mutex has been initialized */ bool mutexinit = false; if ((p->thr_min > p->thr_max) && p->thr_max != 0) { LogMajor(COMPONENT_THREAD, "Minimum of %d is greater than maximum of %d in fridge %s", p->thr_min, p->thr_max, s); rc = EINVAL; goto out; } if ((p->wake_threads != NULL) && (p->flavor != fridgethr_flavor_looper)) { LogMajor(COMPONENT_THREAD, "Wake function only allowed on loopers: %s", s); rc = EINVAL; goto out; } *frout = NULL; frobj->p = *p; frobj->s = NULL; frobj->nthreads = 0; frobj->nidle = 0; frobj->flags = fridgethr_flag_none; /* This always succeeds on Linux, but it might fail on other systems or future versions of Linux. */ rc = pthread_attr_init(&frobj->attr); if (rc != 0) { LogMajor(COMPONENT_THREAD, "Unable to initialize thread attributes for fridge %s: %d", s, rc); goto out; } attrinit = true; rc = pthread_attr_setscope(&frobj->attr, PTHREAD_SCOPE_SYSTEM); if (rc != 0) { LogMajor(COMPONENT_THREAD, "Unable to set thread scope for fridge %s: %d", s, rc); goto out; } rc = pthread_attr_setdetachstate(&frobj->attr, PTHREAD_CREATE_DETACHED); if (rc != 0) { LogMajor(COMPONENT_THREAD, "Unable to set threads detached for fridge %s: %d", s, rc); goto out; } /* This always succeeds on Linux (if you believe the manual), but SUS defines errors. */ rc = pthread_mutex_init(&frobj->mtx, NULL); if (rc != 0) { LogMajor(COMPONENT_THREAD, "Unable to initialize mutex for fridge %s: %d", s, rc); goto out; } mutexinit = true; frobj->s = gsh_strdup(s); frobj->command = fridgethr_comm_run; frobj->transitioning = false; /* Thread list */ glist_init(&frobj->thread_list); /* Idle threads queue */ glist_init(&frobj->idle_q); /* Flavor */ if (frobj->p.flavor == fridgethr_flavor_worker) { /* Deferment */ switch (frobj->p.deferment) { case fridgethr_defer_queue: glist_init(&frobj->deferment.work_q); break; case fridgethr_defer_block: PTHREAD_COND_init(&frobj->deferment.block.cond, NULL); frobj->deferment.block.waiters = 0; break; case fridgethr_defer_fail: /* Failing is easy. */ break; default: LogMajor(COMPONENT_THREAD, "Invalid value fridgethr_defer_t of %d in %s", frobj->p.deferment, s); rc = EINVAL; goto out; } } else if (frobj->p.flavor == fridgethr_flavor_looper) { if (frobj->p.deferment != fridgethr_defer_fail) { LogMajor(COMPONENT_THREAD, "Deferment is not allowed in looper fridges: In fridge %s, requested deferment of %d.", s, frobj->p.deferment); rc = EINVAL; goto out; } } else { LogMajor(COMPONENT_THREAD, "Thread flavor of %d is disallowed in fridge: %s", frobj->p.flavor, s); rc = EINVAL; goto out; } *frout = frobj; rc = 0; out: if (rc != 0) { if (mutexinit) { PTHREAD_MUTEX_destroy(&frobj->mtx); mutexinit = false; } if (attrinit) { pthread_attr_destroy(&frobj->attr); attrinit = false; } if (frobj) { if (frobj->s) { gsh_free(frobj->s); frobj->s = NULL; } gsh_free(frobj); frobj = NULL; } } return rc; } /** * @brief Destroy a thread fridge * * @param[in] fr The fridge to destroy */ void fridgethr_destroy(struct fridgethr *fr) { PTHREAD_MUTEX_destroy(&fr->mtx); pthread_attr_destroy(&fr->attr); gsh_free(fr->s); gsh_free(fr); } /** * @brief Finish a transition * * Notify whoever cares that we're done and mark the transition as * complete. The fridge lock must be held when calling this function. * * @param[in,out] fr The fridge * @param[in] locked The completion mutex is already locked. * Neither acquire nor release it. */ static void fridgethr_finish_transition(struct fridgethr *fr, bool locked) { if (!fr->transitioning) return; if (fr->cb_mtx && !locked) PTHREAD_MUTEX_lock(fr->cb_mtx); if (fr->cb_func != NULL) fr->cb_func(fr->cb_arg); if (fr->cb_cv) pthread_cond_broadcast(fr->cb_cv); if (fr->cb_mtx && !locked) PTHREAD_MUTEX_unlock(fr->cb_mtx); if (!locked) { fr->cb_mtx = NULL; fr->cb_cv = NULL; } fr->cb_func = NULL; fr->cb_arg = NULL; fr->transitioning = false; } /** * @brief Test whether the fridge has deferred work waiting * * @note This function must be called with the fridge mutex held. * * @return true if deferred work is waiting. */ static bool fridgethr_deferredwork(struct fridgethr *fr) { bool res = false; switch (fr->p.deferment) { case fridgethr_defer_queue: res = !glist_empty(&fr->deferment.work_q); break; case fridgethr_defer_block: res = (fr->deferment.block.waiters > 0); break; case fridgethr_defer_fail: res = false; break; } return res; } /** * @brief Get deferred work * * This function only does something in the case of a queueing * fridge. If work is available, it loads it into the thread context * and returns true. If work is not available (or the fridge is not a * queueing fridge) it returns false and leaves the context untouched. * * @param[in,out] fr Fridge * @param[in,out] fe Fridge entry * * @note This function must be called with the fridge mutex held. * * @return true if deferred work has been dequeued. */ static bool fridgethr_getwork(struct fridgethr *fr, struct fridgethr_entry *fe) { if ((fr->p.deferment == fridgethr_defer_block) || (fr->p.deferment == fridgethr_defer_fail) || glist_empty(&fr->deferment.work_q)) { return false; } else { struct fridgethr_work *q = glist_first_entry(&fr->deferment.work_q, struct fridgethr_work, link); glist_del(&q->link); fe->ctx.func = q->func; fe->ctx.arg = q->arg; gsh_free(q); return true; } } /** * @brief Wait for more work * * This function, called by a worker thread, will cause it to wait for * more work (or exit). * * @note To dispatch a task to a sleeping thread, that is, to load a * function and argument into its context and have them executed, * fridgethr_flag_dispatched must be set. If the thread awakes and * firdgethr_flag_dispatched is not set, it will decide what to do on * its own based on the current command and queue. * * @retval true if we have more work to do. * @retval false if we need to go away. */ static bool fridgethr_freeze(struct fridgethr *fr, struct fridgethr_context *thr_ctx) { /* Entry for this thread */ struct fridgethr_entry *fe = container_of(thr_ctx, struct fridgethr_entry, ctx); /* Return code from system calls */ int rc = 0; PTHREAD_MUTEX_lock(&fr->mtx); restart: /* If we are not paused and there is work left to do in the queue, do it. */ if (!(fr->command == fridgethr_comm_pause) && fridgethr_getwork(fr, fe)) { PTHREAD_MUTEX_unlock(&fr->mtx); return true; } /* rc would have been set in the while loop below */ if (((rc == ETIMEDOUT) && (fr->nthreads > fr->p.thr_min)) || (fr->command == fridgethr_comm_stop)) { /* We do this here since we already have the fridge lock. */ --(fr->nthreads); glist_del(&fe->thread_link); if ((fr->nthreads == 0) && (fr->command == fridgethr_comm_stop) && (fr->transitioning) && !fridgethr_deferredwork(fr)) { /* We're the last thread to exit, signal the transition to pause complete. */ fridgethr_finish_transition(fr, false); } PTHREAD_MUTEX_lock(&fe->ctx.mtx); PTHREAD_MUTEX_unlock(&fe->ctx.mtx); PTHREAD_MUTEX_unlock(&fr->mtx); return false; } assert(fr->command != fridgethr_comm_stop); glist_add_tail(&fr->idle_q, &fe->idle_link); ++(fr->nidle); if ((fr->nidle == fr->nthreads) && (fr->command == fridgethr_comm_pause) && (fr->transitioning)) { /* We're the last thread to suspend, signal the transition to pause complete. */ fridgethr_finish_transition(fr, false); } PTHREAD_MUTEX_lock(&fe->ctx.mtx); fe->frozen = true; fe->flags |= fridgethr_flag_available; /* Not ideal, but no ideal factoring occurred to me. */ if ((fr->p.deferment == fridgethr_defer_block) && (fr->deferment.block.waiters > 0)) { pthread_cond_signal(&fr->deferment.block.cond); } PTHREAD_MUTEX_unlock(&fr->mtx); /* It is a state machine, keep going until we have a transition that gets us out. */ while (true) { if ((fr->p.wake_threads == NULL) || (fr->command != fridgethr_comm_run)) { if (fr->p.thread_delay > 0) { clock_gettime(CLOCK_REALTIME, &fe->timeout); fe->timeout.tv_sec += fr->p.thread_delay; rc = pthread_cond_timedwait(&fe->ctx.cv, &fe->ctx.mtx, &fe->timeout); } else rc = pthread_cond_wait(&fe->ctx.cv, &fe->ctx.mtx); } if (rc == ETIMEDOUT) fe->ctx.woke = false; else fe->ctx.woke = true; /* Clear this while we have the lock, we can set it again before continuing */ fe->frozen = false; /* It's repetition, but it saves us from having to drop and then reacquire the lock later. */ if (fe->flags & fridgethr_flag_dispatched) { fe->flags &= ~(fridgethr_flag_available | fridgethr_flag_dispatched); PTHREAD_MUTEX_unlock(&fe->ctx.mtx); break; } /* Clear available so we won't be dispatched while we're acquiring the fridge lock. */ fe->flags &= ~fridgethr_flag_available; PTHREAD_MUTEX_unlock(&fe->ctx.mtx); PTHREAD_MUTEX_lock(&fr->mtx); /* Nothing to do, loop around. */ if (fr->command != fridgethr_comm_stop && ((fr->command == fridgethr_comm_pause) || fridgethr_deferredwork(fr)) && (fr->p.flavor == fridgethr_flavor_worker)) { PTHREAD_MUTEX_lock(&fe->ctx.mtx); fe->frozen = true; fe->flags |= fridgethr_flag_available; /* Not ideal, but no ideal factoring occurred to me. */ if ((fr->p.deferment == fridgethr_defer_block) && (fr->deferment.block.waiters > 0)) { pthread_cond_signal(&fr->deferment.block.cond); } PTHREAD_MUTEX_unlock(&fr->mtx); continue; } --(fr->nidle); glist_del(&fe->idle_link); if (fr->p.flavor == fridgethr_flavor_worker) goto restart; else { PTHREAD_MUTEX_unlock(&fr->mtx); break; } } /* We were already unfrozen and taken off the idle queue, so there's nothing more to do than: */ return true; } /** * @brief Operation context. * * This carries everything relevant to a protocol operation * Since it is a thread local, it is exclusively in the thread context * and cannot be shared with another thread. * * This will always point to a valid structure. When its contents go out * of scope this is set to NULL but since dereferencing with this expectation, * a SEGV will result. This will point to one of three structures: * * 1. The req_ctx declared in rpc_execute(). This is the state for any NFS op. * * 2. The op_context declared/referenced in a 9P fid. * Same as req_ctx but for 9P operations. * * 3. A root context which is used for upcalls, exports bashing, and async * events that call functions that expect a context set up. */ __thread struct req_op_context *op_ctx; /** * @brief Initialization of a new thread in the fridge * * This routine calls the procedure that implements the actual * functionality wanted by a thread in a loop, handling rescheduling. * * @param[in] arg The fridge entry for this thread * * @return NULL. */ static void *fridgethr_start_routine(void *arg) { struct fridgethr_entry *fe = arg; struct fridgethr *fr = fe->fr; bool reschedule; int rc = 0; int old_type = 0; int old_state = 0; SetNameFunction(fr->s); /* Excplicitly and definitely enable cancellation */ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_state); /* The only time a thread would be cancelled is if it were to fail to honor a more civil timeout request that times out. In these cases we assume the thread has gone into an infinite loop or deadlocked or otherwise experienced some unfortunate state. Since deferred cancellation is effective on condition waits, may be effective on read-write locks and won't be effective on mutices, asynchronous seems the way to go. We would only do this on the way to taking down the system in any case. */ pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old_type); rc = pthread_sigmask(SIG_SETMASK, NULL, &fe->ctx.sigmask); /* The only allowable errors are EFAULT and EINVAL, both of which would indicate bugs in the code. */ assert(rc == 0); if (fr->p.thread_initialize) fr->p.thread_initialize(&fe->ctx); do { fe->ctx.func(&fe->ctx); if (fr->p.task_cleanup) fr->p.task_cleanup(&fe->ctx); reschedule = fridgethr_freeze(fr, &fe->ctx); } while (reschedule); if (fr->p.thread_finalize) fr->p.thread_finalize(&fe->ctx); PTHREAD_MUTEX_destroy(&fe->ctx.mtx); PTHREAD_COND_destroy(&fe->ctx.cv); gsh_free(fe); fe = NULL; /* At this point the fridge entry no longer exists and must not be accessed. */ return NULL; } /** * @brief Do the actual work of spawning a thread * * @note This function must be called with the fridge mutex held and * it releases the fridge mutex. * * @param[in] fr The fridge in which to spawn the thread * @param[in] func The thing to do * @param[in] arg The thing to do it to * * @return 0 on success or POSIX error codes. */ static int fridgethr_spawn(struct fridgethr *fr, void (*func)(struct fridgethr_context *), void *arg) { /* Return code */ int rc = 0; /* Newly created thread entry */ struct fridgethr_entry *fe = NULL; /* The mutex has/not been initialized */ bool mutexed = false; /* The condition variable has/not been initialized */ bool conditioned = false; fe = gsh_calloc(1, sizeof(struct fridgethr_entry)); glist_init(&fe->thread_link); fe->fr = fr; rc = pthread_mutex_init(&fe->ctx.mtx, NULL); if (rc != 0) { LogMajor(COMPONENT_THREAD, "Unable to initialize mutex for new thread in fridge %s: %d", fr->s, rc); goto create_err; } mutexed = true; rc = pthread_cond_init(&fe->ctx.cv, NULL); if (rc != 0) { LogMajor(COMPONENT_THREAD, "Unable to initialize condition variable for new thread in fridge %s: %d", fr->s, rc); goto create_err; } conditioned = true; fe->ctx.func = func; fe->ctx.arg = arg; fe->frozen = false; rc = pthread_create(&fe->ctx.id, &fr->attr, fridgethr_start_routine, fe); if (rc != 0) { LogMajor(COMPONENT_THREAD, "Unable to create new thread in fridge %s: %d", fr->s, rc); goto create_err; } #ifdef LINUX /* pthread_t is a 'pointer to struct' on FreeBSD vs 'unsigned long' on Linux */ LogFullDebug(COMPONENT_THREAD, "fr %p created thread %u (nthreads %u nidle %u)", fr, (unsigned int)fe->ctx.id, fr->nthreads, fr->nidle); #endif /* Make a new thread */ ++(fr->nthreads); glist_add_tail(&fr->thread_list, &fe->thread_link); PTHREAD_MUTEX_unlock(&fr->mtx); return rc; create_err: if (conditioned) PTHREAD_COND_destroy(&fe->ctx.cv); if (mutexed) PTHREAD_MUTEX_destroy(&fe->ctx.mtx); gsh_free(fe); PTHREAD_MUTEX_unlock(&fr->mtx); return rc; } /** * @brief Queue a request * * Put a request on the queue and return immediately. * * @note This function must be called with the fridge lock held. * * @param[in] fr The fridge in which to find a thread * @param[in] func The thing to do * @param[in] arg The thing to do it to * * @return 0 or POSIX errors. */ static int fridgethr_queue(struct fridgethr *fr, void (*func)(struct fridgethr_context *), void *arg) { /* Queue */ struct fridgethr_work *q; assert(fr->p.deferment == fridgethr_defer_queue); q = gsh_malloc(sizeof(struct fridgethr_work)); glist_init(&q->link); q->func = func; q->arg = arg; glist_add_tail(&fr->deferment.work_q, &q->link); return 0; } /** * @brief Dispatch a job to an idle queue * * @note The fridge lock must be held when calling this routine. * * @param[in] fr The fridge in which to find a thread * @param[in] func The thing to do * @param[in] arg The thing to do it to * * @return true if the job was successfully dispatched. */ static bool fridgethr_dispatch(struct fridgethr *fr, void (*func)(struct fridgethr_context *), void *arg) { /* The entry for the found thread */ struct fridgethr_entry *fe; /* Iterator over the list */ struct glist_head *g = NULL; /* Saved pointer so we don't trash iteration */ struct glist_head *n = NULL; /* If we successfully dispatched */ bool dispatched = false; /* Try to grab a thread */ glist_for_each_safe(g, n, &fr->idle_q) { fe = container_of(g, struct fridgethr_entry, idle_link); PTHREAD_MUTEX_lock(&fe->ctx.mtx); /* Get rid of a potential race condition where the thread wakes up and exits or otherwise redirects itself */ if (fe->flags & fridgethr_flag_available) { glist_del(&fe->idle_link); --(fr->nidle); fe->ctx.func = func; fe->ctx.arg = arg; fe->frozen = false; fe->flags |= fridgethr_flag_dispatched; pthread_cond_signal(&fe->ctx.cv); PTHREAD_MUTEX_unlock(&fe->ctx.mtx); dispatched = true; break; } PTHREAD_MUTEX_unlock(&fe->ctx.mtx); } return dispatched; } /** * @brief Block a request * * Block on thread availability and schedule a thread when one becomes * available. * * @note This function must be called with the fridge lock held. * * @param[in] fr The fridge in which to find a thread * @param[in] func The thing to do * @param[in] arg The thing to do it to * * @return 0 or POSIX errors. */ static int fridgethr_block(struct fridgethr *fr, void (*func)(struct fridgethr_context *), void *arg) { /* Successfully dispatched */ bool dispatched = true; /* Return code */ int rc = 0; ++(fr->deferment.block.waiters); do { if (fr->p.block_delay > 0) { struct timespec t; clock_gettime(CLOCK_REALTIME, &t); t.tv_sec += fr->p.block_delay > 0; rc = pthread_cond_timedwait(&fr->deferment.block.cond, &fr->mtx, &t); } else { rc = pthread_cond_wait(&fr->deferment.block.cond, &fr->mtx); } if (rc == 0) { switch (fr->command) { case fridgethr_comm_run: dispatched = fridgethr_dispatch(fr, func, arg); break; case fridgethr_comm_stop: rc = EPIPE; break; case fridgethr_comm_pause: /* Nothing, just go through the loop again. */ break; } } } while (!dispatched && (rc == 0)); --(fr->deferment.block.waiters); /* We check here, too, in case we get around to falling out after the last thread exited. */ if ((fr->nthreads == 0) && (fr->command == fridgethr_comm_stop) && (fr->transitioning) && !fridgethr_deferredwork(fr)) { /* We're the last thread to exit, signal the transition to pause complete. */ fridgethr_finish_transition(fr, false); } return rc; } /** * @brief Schedule a thread to perform a function * * This function finds an idle thread to perform func, creating one if * no thread is idle and we have not reached maxthreads. If we have * reached maxthreads, defer the request in accord with the fridge's * deferment policy. * * @param[in] fr The fridge in which to find a thread * @param[in] func The thing to do * @param[in] arg The thing to do it to * * @retval 0 on success. * @retval EPIPE if the fridge is stopped. * @retval EWOULDBLOCK if no threads are available. * @retval Other POSIX return codes. */ int fridgethr_submit(struct fridgethr *fr, void (*func)(struct fridgethr_context *), void *arg) { /* Return code */ int rc = 0; if (fr == NULL) { LogMajor(COMPONENT_THREAD, "Attempt to schedule job with no fridge thread"); return EPIPE; } PTHREAD_MUTEX_lock(&fr->mtx); if (fr->command == fridgethr_comm_stop) { LogMajor(COMPONENT_THREAD, "Attempt to schedule job in stopped fridge %s.", fr->s); PTHREAD_MUTEX_unlock(&fr->mtx); return EPIPE; } if (fr->command == fridgethr_comm_pause) { LogFullDebug(COMPONENT_THREAD, "Attempt to schedule job in paused fridge %s, pausing.", fr->s); goto defer; } if (fr->nidle > 0) { if (fridgethr_dispatch(fr, func, arg)) { PTHREAD_MUTEX_unlock(&fr->mtx); return 0; } } if ((fr->p.thr_max == 0) || (fr->nthreads < fr->p.thr_max)) { rc = fridgethr_spawn(fr, func, arg); } else { defer: switch (fr->p.deferment) { case fridgethr_defer_queue: rc = fridgethr_queue(fr, func, arg); break; case fridgethr_defer_fail: rc = EWOULDBLOCK; break; case fridgethr_defer_block: rc = fridgethr_block(fr, func, arg); }; PTHREAD_MUTEX_unlock(&fr->mtx); } return rc; } /** * @brief Wake idle threads * * This function is intended for use in fridgethr_flavor_looper * fridges, but nothing bad happens if you call it for * fridgethr_flavor_worker fridges. It wakes all idle threads and * exits. * * @note If there are no idle threads we successfully do nothing. * * @param[in] fr The fridge in which to find a thread * * @retval 0 all idle threads woke. * @retval EPIPE fridge is stopped or paused. */ int fridgethr_wake(struct fridgethr *fr) { /* Iterator over the list */ struct glist_head *g = NULL; PTHREAD_MUTEX_lock(&fr->mtx); if (fr->command != fridgethr_comm_run) { LogMajor(COMPONENT_THREAD, "Attempt to wake stopped/paused fridge %s.", fr->s); PTHREAD_MUTEX_unlock(&fr->mtx); return EPIPE; } /* Wake the threads */ glist_for_each(g, &fr->idle_q) { /* The entry for the found thread */ struct fridgethr_entry *fe = container_of(g, struct fridgethr_entry, idle_link); PTHREAD_MUTEX_lock(&fe->ctx.mtx); pthread_cond_signal(&fe->ctx.cv); PTHREAD_MUTEX_unlock(&fe->ctx.mtx); } PTHREAD_MUTEX_unlock(&fr->mtx); return 0; } /** * @brief Suspend execution in the fridge * * Simply change the state to pause. If everything is already paused, * call the callback. * * @note Both @c mtx and @c cv may be NULL if you want to manage * synchrony without any help from the fridge. * * @param[in,out] fr The fridge to pause * @param[in] mtx Mutex (must be held when this function is called) * @param[in] cv Condition variable to be signalled on completion. * @param[in] cb Function to call once all threads are paused * @param[in] arg Argument to supply * * @retval 0 on success. * @retval EBUSY if a state transition is in progress. * @retval EALREADY if the fridge is already paused. * @retval EINVAL if an invalid transition (from stopped to paused) * was requested or one of @c mtx and @c cv was NULL but not * both. */ int fridgethr_pause(struct fridgethr *fr, pthread_mutex_t *mtx, pthread_cond_t *cv, void (*cb)(void *), void *arg) { PTHREAD_MUTEX_lock(&fr->mtx); if (fr->transitioning) { PTHREAD_MUTEX_unlock(&fr->mtx); LogMajor(COMPONENT_THREAD, "Transition requested during transition in fridge %s", fr->s); return EBUSY; } if ((mtx && !cv) || (cv && !mtx)) { PTHREAD_MUTEX_unlock(&fr->mtx); LogMajor(COMPONENT_THREAD, "Iff, if you please: %s", fr->s); return EINVAL; } if (fr->command == fridgethr_comm_pause) { PTHREAD_MUTEX_unlock(&fr->mtx); LogMajor(COMPONENT_THREAD, "Do not pause that which is already paused: %s", fr->s); return EALREADY; } if (fr->command == fridgethr_comm_stop) { PTHREAD_MUTEX_unlock(&fr->mtx); LogMajor(COMPONENT_THREAD, "Invalid transition, stop to pause: %s", fr->s); return EINVAL; } fr->command = fridgethr_comm_pause; fr->transitioning = true; fr->cb_mtx = mtx; fr->cb_cv = cv; fr->cb_func = cb; fr->cb_arg = arg; if (fr->nthreads == fr->nidle) fridgethr_finish_transition(fr, true); if (fr->p.wake_threads != NULL) fr->p.wake_threads(fr->p.wake_threads_arg); PTHREAD_MUTEX_unlock(&fr->mtx); return 0; } /** * @brief Slightly stupid workaround for an unlikely case * * @param[in] dummy Ignored */ static void fridgethr_noop(struct fridgethr_context *dummy) { /* return */ } /** * @brief Stop execution in the fridge * * Change state to stopped. Wake up all the idlers so they stop, * too. If there are no threads and the idle queue is empty, start * one up to finish any pending jobs. (This can happen if we go * straight from paused to stopped.) * * @note Both @c mtx and @c cv may be NULL if you want to manage * synchrony without any help from the fridge. * * @param[in,out] fr The fridge to pause * @param[in] mtx Mutex (must be held when this function is called) * @param[in] cv Condition variable to be signalled on completion. * @param[in] cb Function to call once all threads are paused * @param[in] arg Argument to supply * * @retval 0 on success. * @retval EBUSY if a state transition is in progress. * @retval EALREADY if the fridge is already paused. * @retval EINVAL if one of @c mtx and @c cv was NULL but not both. */ int fridgethr_stop(struct fridgethr *fr, pthread_mutex_t *mtx, pthread_cond_t *cv, void (*cb)(void *), void *arg) { int rc = 0; PTHREAD_MUTEX_lock(&fr->mtx); if (fr->transitioning) { PTHREAD_MUTEX_unlock(&fr->mtx); LogMajor(COMPONENT_THREAD, "Transition requested during transition in fridge %s", fr->s); return EBUSY; } if (fr->command == fridgethr_comm_stop) { PTHREAD_MUTEX_unlock(&fr->mtx); LogMajor(COMPONENT_THREAD, "Do not stop that which is already stopped: %s", fr->s); return EALREADY; } if ((mtx && !cv) || (cv && !mtx)) { PTHREAD_MUTEX_unlock(&fr->mtx); LogMajor(COMPONENT_THREAD, "Iff, if you please: %s", fr->s); return EINVAL; } fr->command = fridgethr_comm_stop; fr->transitioning = true; fr->cb_mtx = mtx; fr->cb_cv = cv; fr->cb_func = cb; fr->cb_arg = arg; if ((fr->nthreads == 0) && !fridgethr_deferredwork(fr)) { fridgethr_finish_transition(fr, true); PTHREAD_MUTEX_unlock(&fr->mtx); return 0; } /* If we're a blocking fridge, let everyone know it's time to fail. */ if ((fr->p.deferment == fridgethr_defer_block) && (fr->deferment.block.waiters > 0)) { pthread_cond_broadcast(&fr->deferment.block.cond); } if (fr->nthreads > 0) { /* Wake the idle! */ /* Iterator over the list */ struct glist_head *g = NULL; glist_for_each(g, &fr->idle_q) { struct fridgethr_entry *fe; fe = container_of(g, struct fridgethr_entry, idle_link); PTHREAD_MUTEX_lock(&fe->ctx.mtx); /* We don't dispatch or anything, just wake them all up and let them grab work off the queue or terminate. */ pthread_cond_signal(&fe->ctx.cv); PTHREAD_MUTEX_unlock(&fe->ctx.mtx); if (fr->p.wake_threads != NULL) fr->p.wake_threads(fr->p.wake_threads_arg); } PTHREAD_MUTEX_unlock(&fr->mtx); } else { /* Well, this is embarrassing. */ assert(fr->p.deferment != fridgethr_defer_fail); if (fr->p.deferment == fridgethr_defer_queue) { struct fridgethr_work *q = glist_first_entry(&fr->deferment.work_q, struct fridgethr_work, link); glist_del(&q->link); rc = fridgethr_spawn(fr, q->func, q->arg); gsh_free(q); } else { /* Spawn a dummy to clean out the queue */ rc = fridgethr_spawn(fr, fridgethr_noop, NULL); } PTHREAD_MUTEX_unlock(&fr->mtx); } return rc; } /** * @brief Start execution in the fridge * * Change state to running. Wake up all the idlers. If there's work * queued and we're below maxthreads, start some more threads. * * @note Both @c mtx and @c cv may be NULL if you want to manage * synchrony without any help from the fridge. * * @param[in,out] fr The fridge to pause * @param[in] mtx Mutex (must be held when this function is called) * @param[in] cv Condition variable to be signalled on completion. * @param[in] cb Function to call once all threads are paused * @param[in] arg Argument to supply * * @retval 0 on success. * @retval EBUSY if a state transition is in progress. * @retval EALREADY if the fridge is already paused. */ int fridgethr_start(struct fridgethr *fr, pthread_mutex_t *mtx, pthread_cond_t *cv, void (*cb)(void *), void *arg) { /* Return code */ int rc = 0; /* Cap on the number of threads to spawn, just so we know we can terminate. */ int maybe_spawn = 50; PTHREAD_MUTEX_lock(&fr->mtx); if (fr->transitioning) { PTHREAD_MUTEX_unlock(&fr->mtx); LogMajor(COMPONENT_THREAD, "Transition requested during transition in fridge %s", fr->s); return EBUSY; } if (fr->command == fridgethr_comm_run) { PTHREAD_MUTEX_unlock(&fr->mtx); LogMajor(COMPONENT_THREAD, "Do not start that which is already started: %s", fr->s); return EALREADY; } if ((mtx && !cv) || (cv && !mtx)) { PTHREAD_MUTEX_unlock(&fr->mtx); LogMajor(COMPONENT_THREAD, "Iff, if you please: %s", fr->s); return EINVAL; } fr->command = fridgethr_comm_run; fr->transitioning = true; fr->cb_mtx = mtx; fr->cb_cv = cv; fr->cb_func = cb; fr->cb_arg = arg; if ((fr->nthreads == 0) && !fridgethr_deferredwork(fr)) { /* No work scheduled and no threads running, but ready to accept requests once more. */ fridgethr_finish_transition(fr, true); PTHREAD_MUTEX_unlock(&fr->mtx); return 0; } if (fr->nidle > 0) { /* Iterator over the list */ struct glist_head *g = NULL; glist_for_each(g, &fr->idle_q) { struct fridgethr_entry *fe; fe = container_of(g, struct fridgethr_entry, idle_link); PTHREAD_MUTEX_lock(&fe->ctx.mtx); /* We don't dispatch or anything, just wake them all up and let them grab work off the queue or terminate. */ pthread_cond_signal(&fe->ctx.cv); PTHREAD_MUTEX_unlock(&fe->ctx.mtx); } } while (fridgethr_deferredwork(fr) && (maybe_spawn-- > 0) && ((fr->nthreads < fr->p.thr_max) || (fr->p.thr_max == 0))) { assert(fr->p.deferment != fridgethr_defer_block); /* Start some threads to finish the work */ if (fr->p.deferment == fridgethr_defer_queue) { struct fridgethr_work *q = glist_first_entry(&fr->deferment.work_q, struct fridgethr_work, link); glist_del(&q->link); rc = fridgethr_spawn(fr, q->func, q->arg); gsh_free(q); PTHREAD_MUTEX_lock(&fr->mtx); if (rc != 0) break; } else { rc = fridgethr_spawn(fr, fridgethr_noop, NULL); PTHREAD_MUTEX_lock(&fr->mtx); if (rc != 0) break; } } if (fr->p.wake_threads != NULL) fr->p.wake_threads(fr->p.wake_threads_arg); PTHREAD_MUTEX_unlock(&fr->mtx); return rc; } /** * @brief Set a flag to true, to prevent racing condition variable * * @param[in,out] flag Flag to set */ static void fridgethr_trivial_syncer(void *flag) { *(bool *) flag = true; } /** * @brief Synchronously change the state of the fridge * * A convenience function that issues a state change and waits for it * to complete. * * @param[in,out] fr The fridge to change * @param[in] command Command to issue * @param[in] timeout Number of seconds to wait for change or 0 * to wait forever. * * @retval 0 Success. * @retval EINVAL invalid state change requested. * @retval EALREADY fridge already in requested state. * @retval EBUSY fridge currently in transition. * @retval ETIMEDOUT timed out on wait. */ int fridgethr_sync_command(struct fridgethr *fr, fridgethr_comm_t command, time_t timeout) { pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cv = PTHREAD_COND_INITIALIZER; bool done = false; int rc = 0; struct timespec ts; PTHREAD_MUTEX_lock(&mtx); switch (command) { case fridgethr_comm_run: rc = fridgethr_start(fr, &mtx, &cv, fridgethr_trivial_syncer, &done); break; case fridgethr_comm_pause: rc = fridgethr_pause(fr, &mtx, &cv, fridgethr_trivial_syncer, &done); break; case fridgethr_comm_stop: rc = fridgethr_stop(fr, &mtx, &cv, fridgethr_trivial_syncer, &done); break; default: rc = EINVAL; } if (rc != 0) { PTHREAD_MUTEX_unlock(&mtx); return rc; } if (timeout != 0) { clock_gettime(CLOCK_REALTIME, &ts); ts.tv_sec += timeout; } while (!done) { if (timeout == 0) { rc = pthread_cond_wait(&cv, &mtx); assert(rc == 0); } else { rc = pthread_cond_timedwait(&cv, &mtx, &ts); if (rc == ETIMEDOUT) { LogMajor(COMPONENT_THREAD, "Sync command seems to be stalled"); /* we timed out and the callback * was not triggered, therefore, * we must exit the loop manually. */ break; } else assert(rc == 0); } } PTHREAD_MUTEX_unlock(&mtx); return rc; } /** * @brief Return true if a looper function should return * * For the moment, this checks if we're in the middle of a state * transition. * * @param[in] ctx The thread context * * @retval true if you should break. * @retval false if you don't have to. You still can if you want to. */ bool fridgethr_you_should_break(struct fridgethr_context *ctx) { /* Entry for this thread */ struct fridgethr_entry *fe = container_of(ctx, struct fridgethr_entry, ctx); struct fridgethr *fr = fe->fr; /* No locking is needed as it is only read */ return fr->transitioning; } /** * @brief Populate a fridge with threads all running the same thing * * @param[in,out] fr Fridge to populate * @param[in] func Function each thread should run * @param[in] arg Argument supplied for that function * * @retval 0 on success. * @retval EINVAL if there is no well-defined thread count. * @retval Other codes from thread creation. */ int fridgethr_populate(struct fridgethr *fr, void (*func)(struct fridgethr_context *), void *arg) { int threads_to_run; int i; PTHREAD_MUTEX_lock(&fr->mtx); if (fr->p.thr_min != 0) { threads_to_run = fr->p.thr_min; } else if (fr->p.thr_max != 0) { threads_to_run = fr->p.thr_max; } else { PTHREAD_MUTEX_unlock(&fr->mtx); LogMajor(COMPONENT_THREAD, "Cannot populate fridge with undefined number of threads: %s", fr->s); return EINVAL; } for (i = 0; i < threads_to_run; ++i) { struct fridgethr_entry *fe = NULL; int rc = 0; fe = gsh_calloc(1, sizeof(struct fridgethr_entry)); /* Make a new thread */ ++(fr->nthreads); glist_add_tail(&fr->thread_list, &fe->thread_link); fe->fr = fr; rc = pthread_mutex_init(&fe->ctx.mtx, NULL); if (rc != 0) { LogMajor(COMPONENT_THREAD, "Unable to initialize mutex for new thread in fridge %s: %d", fr->s, rc); PTHREAD_MUTEX_unlock(&fr->mtx); return rc; } rc = pthread_cond_init(&fe->ctx.cv, NULL); if (rc != 0) { LogMajor(COMPONENT_THREAD, "Unable to initialize condition variable for new thread in fridge %s: %d", fr->s, rc); PTHREAD_MUTEX_unlock(&fr->mtx); return rc; } fe->ctx.func = func; fe->ctx.arg = arg; fe->frozen = false; rc = pthread_create(&fe->ctx.id, &fr->attr, fridgethr_start_routine, fe); if (rc != 0) { LogMajor(COMPONENT_THREAD, "Unable to create new thread in fridge %s: %d", fr->s, rc); PTHREAD_MUTEX_unlock(&fr->mtx); return rc; } } PTHREAD_MUTEX_unlock(&fr->mtx); return 0; } /** * @brief Set the wait time of a running fridge * * @param[in] ctx Thread context * @param[in] thread_delay New time delay */ void fridgethr_setwait(struct fridgethr_context *ctx, time_t thread_delay) { struct fridgethr_entry *fe = container_of(ctx, struct fridgethr_entry, ctx); struct fridgethr *fr = fe->fr; PTHREAD_MUTEX_lock(&fr->mtx); fr->p.thread_delay = thread_delay; PTHREAD_MUTEX_unlock(&fr->mtx); } /** * @brief Get the wait time of a running fridge * * @param[in] ctx Thread context */ time_t fridgethr_getwait(struct fridgethr_context *ctx) { struct fridgethr_entry *fe = container_of(ctx, struct fridgethr_entry, ctx); struct fridgethr *fr = fe->fr; time_t thread_delay = 0; PTHREAD_MUTEX_lock(&fr->mtx); thread_delay = fr->p.thread_delay; PTHREAD_MUTEX_unlock(&fr->mtx); return thread_delay; } /** * @brief Cancel all of the threads in the fridge * * This function is done only on Ganesha shutdown and only if a * shutdown request has been ignored. We make no attempt to free the * fridge entries, since the threads are set detached and we're on the * way out anyway. * * @param[in,out] fr Fridge to cancel */ void fridgethr_cancel(struct fridgethr *fr) { /* Thread iterator */ struct glist_head *ti = NULL; /* Next thread link */ struct glist_head *tn = NULL; PTHREAD_MUTEX_lock(&fr->mtx); LogEvent(COMPONENT_THREAD, "Cancelling %d threads from fridge %s.", fr->nthreads, fr->s); glist_for_each_safe(ti, tn, &fr->thread_list) { struct fridgethr_entry *t = glist_entry(ti, struct fridgethr_entry, thread_link); /* The only error we can get is no such thread. Which means the thread isn't running. Which is good enough for me. */ pthread_cancel(t->ctx.id); glist_del(&t->thread_link); --(fr->nthreads); } PTHREAD_MUTEX_unlock(&fr->mtx); LogEvent(COMPONENT_THREAD, "All threads in %s cancelled.", fr->s); } struct fridgethr *general_fridge; int general_fridge_init(void) { struct fridgethr_params frp; int rc = 0; memset(&frp, 0, sizeof(struct fridgethr_params)); frp.thr_max = 4; frp.thr_min = 0; frp.flavor = fridgethr_flavor_worker; frp.deferment = fridgethr_defer_queue; rc = fridgethr_init(&general_fridge, "Gen_Fridge", &frp); if (rc != 0) { LogMajor(COMPONENT_THREAD, "Unable to initialize general fridge, error code %d.", rc); return rc; } return 0; } int general_fridge_shutdown(void) { int rc = fridgethr_sync_command(general_fridge, fridgethr_comm_stop, 120); if (rc == ETIMEDOUT) { LogMajor(COMPONENT_THREAD, "Shutdown timed out, cancelling threads."); fridgethr_cancel(general_fridge); } else if (rc != 0) { LogMajor(COMPONENT_THREAD, "Failed shutting down general fridge: %d", rc); } return rc; } /** @} */ ������������nfs-ganesha-2.6.0/src/support/misc.c����������������������������������������������������������������0000664�0000000�0000000�00000000327�13242724102�0017312�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ #include <stdlib.h> #include <sys/time.h> int portable_clock_gettime(struct timespec *ts) { struct timeval tv; gettimeofday(&tv, NULL); ts->tv_sec = tv.tv_sec; ts->tv_nsec = tv.tv_usec * 1000UL; return 0; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/support/murmur3.c�������������������������������������������������������������0000664�0000000�0000000�00000016131�13242724102�0017771�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*----------------------------------------------------------------------------- * MurmurHash3 was written by Austin Appleby, and is placed in the public * domain. The author hereby disclaims copyright to this source code. * * Note - The x86 and x64 versions do _not_ produce the same results, as the * algorithms are optimized for their respective platforms. You can still * compile and run any of them on any platform, but your performance with the * non-native version will be less than optimal. */ #include "config.h" #include "murmur3.h" /*------------------------------------------------------------------------------ * Platform-specific functions and macros */ inline uint32_t rotl32(uint32_t x, int8_t r) { return (x << r) | (x >> (32 - r)); } inline uint64_t rotl64(uint64_t x, int8_t r) { return (x << r) | (x >> (64 - r)); } #define ROTL32(x, y) rotl32(x, y) #define ROTL64(x, y) rotl64(x, y) #define BIG_CONSTANT(x) (x##LLU) /*------------------------------------------------------------------------------ * Block read - if your platform needs to do endian-swapping or can only * handle aligned reads, do the conversion here */ #define getblock(p, i) (p[i]) /*------------------------------------------------------------------------------ * Finalization mix - force all bits of a hash block to avalanche */ static inline __attribute__((always_inline)) uint32_t fmix32(uint32_t h) { h ^= h >> 16; h *= 0x85ebca6b; h ^= h >> 13; h *= 0xc2b2ae35; h ^= h >> 16; return h; } static inline __attribute__((always_inline)) uint64_t fmix64(uint64_t k) { k ^= k >> 33; k *= BIG_CONSTANT(0xff51afd7ed558ccd); k ^= k >> 33; k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53); k ^= k >> 33; return k; } /*----------------------------------------------------------------------------*/ void MurmurHash3_x86_32(const void *key, int len, uint32_t seed, void *out) { const uint8_t *data = (const uint8_t *)key; const int nblocks = len / 4; int i; uint32_t h1 = seed; uint32_t c1 = 0xcc9e2d51; uint32_t c2 = 0x1b873593; /* body */ const uint32_t *blocks = (const uint32_t *)(data + nblocks * 4); for (i = -nblocks; i; i++) { uint32_t k1 = getblock(blocks, i); k1 *= c1; k1 = ROTL32(k1, 15); k1 *= c2; h1 ^= k1; h1 = ROTL32(h1, 13); h1 = h1 * 5 + 0xe6546b64; } /* tail */ const uint8_t *tail = (const uint8_t *)(data + nblocks * 4); uint32_t k1 = 0; switch (len & 3) { case 3: k1 ^= tail[2] << 16; case 2: k1 ^= tail[1] << 8; case 1: k1 ^= tail[0]; k1 *= c1; k1 = ROTL32(k1, 15); k1 *= c2; h1 ^= k1; }; /* finalization */ h1 ^= len; h1 = fmix32(h1); *(uint32_t *) out = h1; } /*----------------------------------------------------------------------------*/ void MurmurHash3_x86_128(const void *key, const int len, uint32_t seed, void *out) { const uint8_t *data = (const uint8_t *)key; const int nblocks = len / 16; int i; uint32_t h1 = seed; uint32_t h2 = seed; uint32_t h3 = seed; uint32_t h4 = seed; uint32_t c1 = 0x239b961b; uint32_t c2 = 0xab0e9789; uint32_t c3 = 0x38b34ae5; uint32_t c4 = 0xa1e38b93; /* body */ const uint32_t *blocks = (const uint32_t *)(data + nblocks * 16); for (i = -nblocks; i; i++) { uint32_t k1 = getblock(blocks, i * 4 + 0); uint32_t k2 = getblock(blocks, i * 4 + 1); uint32_t k3 = getblock(blocks, i * 4 + 2); uint32_t k4 = getblock(blocks, i * 4 + 3); k1 *= c1; k1 = ROTL32(k1, 15); k1 *= c2; h1 ^= k1; h1 = ROTL32(h1, 19); h1 += h2; h1 = h1 * 5 + 0x561ccd1b; k2 *= c2; k2 = ROTL32(k2, 16); k2 *= c3; h2 ^= k2; h2 = ROTL32(h2, 17); h2 += h3; h2 = h2 * 5 + 0x0bcaa747; k3 *= c3; k3 = ROTL32(k3, 17); k3 *= c4; h3 ^= k3; h3 = ROTL32(h3, 15); h3 += h4; h3 = h3 * 5 + 0x96cd1c35; k4 *= c4; k4 = ROTL32(k4, 18); k4 *= c1; h4 ^= k4; h4 = ROTL32(h4, 13); h4 += h1; h4 = h4 * 5 + 0x32ac3b17; } /* tail */ const uint8_t *tail = (const uint8_t *)(data + nblocks * 16); uint32_t k1 = 0; uint32_t k2 = 0; uint32_t k3 = 0; uint32_t k4 = 0; switch (len & 15) { case 15: k4 ^= tail[14] << 16; case 14: k4 ^= tail[13] << 8; case 13: k4 ^= tail[12] << 0; k4 *= c4; k4 = ROTL32(k4, 18); k4 *= c1; h4 ^= k4; case 12: k3 ^= tail[11] << 24; case 11: k3 ^= tail[10] << 16; case 10: k3 ^= tail[9] << 8; case 9: k3 ^= tail[8] << 0; k3 *= c3; k3 = ROTL32(k3, 17); k3 *= c4; h3 ^= k3; case 8: k2 ^= tail[7] << 24; case 7: k2 ^= tail[6] << 16; case 6: k2 ^= tail[5] << 8; case 5: k2 ^= tail[4] << 0; k2 *= c2; k2 = ROTL32(k2, 16); k2 *= c3; h2 ^= k2; case 4: k1 ^= tail[3] << 24; case 3: k1 ^= tail[2] << 16; case 2: k1 ^= tail[1] << 8; case 1: k1 ^= tail[0] << 0; k1 *= c1; k1 = ROTL32(k1, 15); k1 *= c2; h1 ^= k1; }; /* finalization */ h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len; h1 += h2; h1 += h3; h1 += h4; h2 += h1; h3 += h1; h4 += h1; h1 = fmix64(h1); h2 = fmix64(h2); h3 = fmix64(h3); h4 = fmix64(h4); h1 += h2; h1 += h3; h1 += h4; h2 += h1; h3 += h1; h4 += h1; ((uint32_t *) out)[0] = h1; ((uint32_t *) out)[1] = h2; ((uint32_t *) out)[2] = h3; ((uint32_t *) out)[3] = h4; } /*----------------------------------------------------------------------------*/ void MurmurHash3_x64_128(const void *key, const int len, const uint32_t seed, void *out) { const uint8_t *data = (const uint8_t *)key; const int nblocks = len / 16; int i; uint64_t h1 = seed; uint64_t h2 = seed; uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5); uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f); /* body */ const uint64_t *blocks = (const uint64_t *)(data); for (i = 0; i < nblocks; i++) { uint64_t k1 = getblock(blocks, i * 2 + 0); uint64_t k2 = getblock(blocks, i * 2 + 1); k1 *= c1; k1 = ROTL64(k1, 31); k1 *= c2; h1 ^= k1; h1 = ROTL64(h1, 27); h1 += h2; h1 = h1 * 5 + 0x52dce729; k2 *= c2; k2 = ROTL64(k2, 33); k2 *= c1; h2 ^= k2; h2 = ROTL64(h2, 31); h2 += h1; h2 = h2 * 5 + 0x38495ab5; } /* tail */ const uint8_t *tail = (const uint8_t *)(data + nblocks * 16); uint64_t k1 = 0; uint64_t k2 = 0; switch (len & 15) { case 15: k2 ^= (uint64_t) (tail[14]) << 48; case 14: k2 ^= (uint64_t) (tail[13]) << 40; case 13: k2 ^= (uint64_t) (tail[12]) << 32; case 12: k2 ^= (uint64_t) (tail[11]) << 24; case 11: k2 ^= (uint64_t) (tail[10]) << 16; case 10: k2 ^= (uint64_t) (tail[9]) << 8; case 9: k2 ^= (uint64_t) (tail[8]) << 0; k2 *= c2; k2 = ROTL64(k2, 33); k2 *= c1; h2 ^= k2; case 8: k1 ^= (uint64_t) (tail[7]) << 56; case 7: k1 ^= (uint64_t) (tail[6]) << 48; case 6: k1 ^= (uint64_t) (tail[5]) << 40; case 5: k1 ^= (uint64_t) (tail[4]) << 32; case 4: k1 ^= (uint64_t) (tail[3]) << 24; case 3: k1 ^= (uint64_t) (tail[2]) << 16; case 2: k1 ^= (uint64_t) (tail[1]) << 8; case 1: k1 ^= (uint64_t) (tail[0]) << 0; k1 *= c1; k1 = ROTL64(k1, 31); k1 *= c2; h1 ^= k1; }; /* finalization */ h1 ^= len; h2 ^= len; h1 += h2; h2 += h1; h1 = fmix64(h1); h2 = fmix64(h2); h1 += h2; h2 += h1; ((uint64_t *) out)[0] = h1; ((uint64_t *) out)[1] = h2; } /*----------------------------------------------------------------------------*/ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/support/netgroup_cache.c������������������������������������������������������0000664�0000000�0000000�00000020016�13242724102�0021342�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ #include "config.h" #include "log.h" #include "config_parsing.h" #include <string.h> #include <unistd.h> #include "gsh_intrinsic.h" #include "gsh_types.h" #include "common_utils.h" #include "avltree.h" #include "abstract_atomic.h" #include "netdb.h" #include "abstract_mem.h" #include "netgroup_cache.h" /* Netgroup cache information */ struct ng_cache_info { struct avltree_node ng_node; struct gsh_buffdesc ng_group; struct gsh_buffdesc ng_host; time_t ng_epoch; }; #define NG_CACHE_SIZE 1009 /* Uses FNV hash */ #define FNV_PRIME32 16777619 #define FNV_OFFSET32 2166136261U static int ng_hash_key(struct ng_cache_info *info) { uint32_t hash = FNV_OFFSET32; char *bp, *end; bp = info->ng_host.addr; end = bp + info->ng_host.len; while (bp < end) { hash ^= *bp++; hash *= FNV_PRIME32; } bp = info->ng_group.addr; end = bp + info->ng_group.len; while (bp < end) { hash ^= *bp++; hash *= FNV_PRIME32; } return hash % NG_CACHE_SIZE; } static struct avltree_node *ng_cache[NG_CACHE_SIZE]; pthread_rwlock_t ng_lock = PTHREAD_RWLOCK_INITIALIZER; /* Positive and negative cache trees */ static struct avltree pos_ng_tree; static struct avltree neg_ng_tree; static inline int buffdesc_comparator(const struct gsh_buffdesc *buff1, const struct gsh_buffdesc *buff2) { if (buff1->len < buff2->len) return -1; if (buff1->len > buff2->len) return 1; return memcmp(buff1->addr, buff2->addr, buff1->len); } static int ng_comparator(const struct avltree_node *node1, const struct avltree_node *node2) { int rc; struct ng_cache_info *info1; struct ng_cache_info *info2; info1 = avltree_container_of(node1, struct ng_cache_info, ng_node); info2 = avltree_container_of(node2, struct ng_cache_info, ng_node); /* compare host followed by group if needed */ rc = buffdesc_comparator(&info1->ng_host, &info2->ng_host); if (rc == 0) rc = buffdesc_comparator(&info1->ng_group, &info2->ng_group); return rc; } static bool ng_expired(struct avltree_node *node) { struct ng_cache_info *info; info = avltree_container_of(node, struct ng_cache_info, ng_node); /* Hardcoded to 30 minutes for now */ if (time(NULL) - info->ng_epoch > 30 * 60) return true; return false; } /** * @brief Initialize the netgroups cache */ void ng_cache_init(void) { avltree_init(&pos_ng_tree, ng_comparator, 0); avltree_init(&neg_ng_tree, ng_comparator, 0); memset(ng_cache, 0, NG_CACHE_SIZE * sizeof(struct avltree_node *)); } static void ng_free(struct ng_cache_info *info) { gsh_free(info->ng_group.addr); gsh_free(info->ng_host.addr); gsh_free(info); } /* The caller must hold ng_lock for write */ static void ng_remove(struct ng_cache_info *info, bool negative) { if (negative) { avltree_remove(&info->ng_node, &neg_ng_tree); } else { ng_cache[ng_hash_key(info)] = NULL; avltree_remove(&info->ng_node, &pos_ng_tree); } } /* The caller must hold ng_lock for write */ static void ng_add(const char *group, const char *host, bool negative) { struct ng_cache_info *info; struct avltree_node *found_node; struct ng_cache_info *found_info; info = gsh_malloc(sizeof(struct ng_cache_info)); if (!info) LogFatal(COMPONENT_IDMAPPER, "memory alloc failed"); info->ng_group.addr = gsh_strdup(group); info->ng_group.len = strlen(group)+1; info->ng_host.addr = gsh_strdup(host); info->ng_host.len = strlen(host)+1; info->ng_epoch = time(NULL); if (negative) { /* @todo check positive cache first? */ found_node = avltree_insert(&info->ng_node, &neg_ng_tree); /* If an already existing entry is found, keep the old * entry, and free the current entry */ if (found_node) { found_info = avltree_container_of(found_node, struct ng_cache_info, ng_node); found_info->ng_epoch = info->ng_epoch; ng_free(info); } } else { /* @todo delete from negative cache if there? */ found_node = avltree_insert(&info->ng_node, &pos_ng_tree); /* If an already existing entry is found, keep the old * entry, and free the current entry */ if (found_node) { found_info = avltree_container_of(found_node, struct ng_cache_info, ng_node); ng_cache[ng_hash_key(found_info)] = found_node; found_info->ng_epoch = info->ng_epoch; ng_free(info); } else { ng_cache[ng_hash_key(info)] = &info->ng_node; } } } /* The caller must hold ng_lock for read */ static bool ng_lookup(const char *group, const char *host, bool negative) { struct ng_cache_info prototype = { .ng_group.addr = (char *)group, .ng_group.len = strlen(group)+1, .ng_host.addr = (char *)host, .ng_host.len = strlen(host)+1 }; struct avltree_node *node; struct ng_cache_info *info; void **cache_slot; if (negative) { node = avltree_lookup(&prototype.ng_node, &neg_ng_tree); if (!node) return false; if (!ng_expired(node)) return true; goto expired; } /* Positive lookups are stored in the cache */ cache_slot = (void **)&ng_cache[ng_hash_key(&prototype)]; node = atomic_fetch_voidptr(cache_slot); if (node && ng_comparator(node, &prototype.ng_node) == 0) { if (!ng_expired(node)) return true; goto expired; } /* cache miss, search AVL tree */ node = avltree_lookup(&prototype.ng_node, &pos_ng_tree); if (!node) return false; if (ng_expired(node)) goto expired; atomic_store_voidptr(cache_slot, node); return true; expired: /* entry expired, acquire write mode lock for removal */ PTHREAD_RWLOCK_unlock(&ng_lock); PTHREAD_RWLOCK_wrlock(&ng_lock); /* Since we dropped the read mode lock and acquired write mode * lock, make sure that the entry is still in the tree. */ if (negative) node = avltree_lookup(&prototype.ng_node, &neg_ng_tree); else node = avltree_lookup(&prototype.ng_node, &pos_ng_tree); if (node) { info = avltree_container_of(node, struct ng_cache_info, ng_node); ng_remove(info, negative); ng_free(info); } PTHREAD_RWLOCK_unlock(&ng_lock); PTHREAD_RWLOCK_rdlock(&ng_lock); return false; } /** * @brief Verify if the given host is in the given netgroup or not */ bool ng_innetgr(const char *group, const char *host) { int rc; /* Check positive lookup and then negative lookup. If absent in * both, then do a real innetgr call and cache the results. */ PTHREAD_RWLOCK_rdlock(&ng_lock); if (ng_lookup(group, host, false)) { /* positive lookup */ PTHREAD_RWLOCK_unlock(&ng_lock); return true; } if (ng_lookup(group, host, true)) { /* negative lookup */ PTHREAD_RWLOCK_unlock(&ng_lock); return false; } PTHREAD_RWLOCK_unlock(&ng_lock); rc = innetgr(group, host, NULL, NULL); PTHREAD_RWLOCK_wrlock(&ng_lock); if (rc) ng_add(group, host, false); /* positive lookup */ else ng_add(group, host, true); /* negative lookup */ PTHREAD_RWLOCK_unlock(&ng_lock); return rc; } /** * @brief Wipe out the netgroup cache */ void ng_clear_cache(void) { struct avltree_node *node; struct ng_cache_info *info; PTHREAD_RWLOCK_wrlock(&ng_lock); while ((node = avltree_first(&pos_ng_tree))) { info = avltree_container_of(node, struct ng_cache_info, ng_node); ng_remove(info, false); ng_free(info); } while ((node = avltree_first(&neg_ng_tree))) { info = avltree_container_of(node, struct ng_cache_info, ng_node); ng_remove(info, true); ng_free(info); } assert(avltree_first(&pos_ng_tree) == NULL); assert(avltree_first(&neg_ng_tree) == NULL); PTHREAD_RWLOCK_unlock(&ng_lock); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/support/nfs4_acls.c�����������������������������������������������������������0000664�0000000�0000000�00000016752�13242724102�0020244�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include "config.h" #include "abstract_mem.h" #include "fsal_types.h" #include "hashtable.h" #include "log.h" #include "nfs4_acls.h" #include "city.h" #include "common_utils.h" pool_t *fsal_acl_pool; static int fsal_acl_hash_both(hash_parameter_t *, struct gsh_buffdesc *, uint32_t *, uint64_t *); static int compare_fsal_acl(struct gsh_buffdesc *, struct gsh_buffdesc *); /* DEFAULT PARAMETERS for hash table */ static hash_parameter_t fsal_acl_hash_config = { .index_size = 67, .hash_func_key = NULL, .hash_func_rbt = NULL, .hash_func_both = fsal_acl_hash_both, .compare_key = compare_fsal_acl, .ht_name = "ACL Table", .flags = HT_FLAG_CACHE, .ht_log_component = COMPONENT_NFS_V4_ACL }; static hash_table_t *fsal_acl_hash; /* hash table functions */ static int fsal_acl_hash_both(hash_parameter_t *hparam, struct gsh_buffdesc *key, uint32_t *index, uint64_t *rbthash) { *rbthash = CityHash64(key->addr, key->len); *index = *rbthash % hparam->index_size; return 1; } static int compare_fsal_acl(struct gsh_buffdesc *key1, struct gsh_buffdesc *keya) { if (key1->len != keya->len) return -1; return memcmp(key1->addr, keya->addr, key1->len); } fsal_ace_t *nfs4_ace_alloc(int nace) { fsal_ace_t *ace = NULL; ace = gsh_calloc(nace, sizeof(fsal_ace_t)); return ace; } fsal_acl_t *nfs4_acl_alloc() { return pool_alloc(fsal_acl_pool); } void nfs4_ace_free(fsal_ace_t *ace) { if (!ace) return; LogDebug(COMPONENT_NFS_V4_ACL, "free ace %p", ace); gsh_free(ace); } void nfs4_acl_free(fsal_acl_t *acl) { if (!acl) return; if (acl->aces) nfs4_ace_free(acl->aces); pool_free(fsal_acl_pool, acl); } void nfs4_acl_entry_inc_ref(fsal_acl_t *acl) { /* Increase ref counter */ PTHREAD_RWLOCK_wrlock(&acl->lock); acl->ref++; LogDebug(COMPONENT_NFS_V4_ACL, "(acl, ref) = (%p, %u)", acl, acl->ref); PTHREAD_RWLOCK_unlock(&acl->lock); } /* Should be called with lock held. */ static void nfs4_acl_entry_dec_ref(fsal_acl_t *acl) { /* Decrease ref counter */ acl->ref--; LogDebug(COMPONENT_NFS_V4_ACL, "(acl, ref) = (%p, %u)", acl, acl->ref); } fsal_acl_t *nfs4_acl_new_entry(fsal_acl_data_t *acldata, fsal_acl_status_t *status) { fsal_acl_t *acl = NULL; struct gsh_buffdesc key; struct gsh_buffdesc value; int rc; struct hash_latch latch; /* Set the return default to NFS_V4_ACL_SUCCESS */ *status = NFS_V4_ACL_SUCCESS; key.addr = acldata->aces; key.len = acldata->naces * sizeof(fsal_ace_t); /* Check if the entry already exists */ rc = hashtable_getlatch(fsal_acl_hash, &key, &value, true, &latch); if (rc == HASHTABLE_SUCCESS) { /* Entry is already in the cache, do not add it */ acl = value.addr; *status = NFS_V4_ACL_EXISTS; nfs4_ace_free(acldata->aces); nfs4_acl_entry_inc_ref(acl); hashtable_releaselatched(fsal_acl_hash, &latch); return acl; } /* Any other result other than no such key is an error */ if (rc != HASHTABLE_ERROR_NO_SUCH_KEY) { *status = NFS_V4_ACL_INIT_ENTRY_FAILED; nfs4_ace_free(acldata->aces); return NULL; } /* Adding the entry in the cache */ acl = nfs4_acl_alloc(); if (pthread_rwlock_init(&(acl->lock), NULL) != 0) { nfs4_acl_free(acl); LogCrit(COMPONENT_NFS_V4_ACL, "New ACL RW lock init returned %d (%s)", errno, strerror(errno)); *status = NFS_V4_ACL_INIT_ENTRY_FAILED; nfs4_ace_free(acldata->aces); hashtable_releaselatched(fsal_acl_hash, &latch); return NULL; } acl->naces = acldata->naces; acl->aces = acldata->aces; acl->ref = 1; /* We give out one reference */ /* Build the value */ value.addr = acl; value.len = sizeof(fsal_acl_t); rc = hashtable_setlatched(fsal_acl_hash, &key, &value, &latch, false, NULL, NULL); if (rc != HASHTABLE_SUCCESS) { /* Put the entry back in its pool */ nfs4_acl_free(acl); LogWarn(COMPONENT_NFS_V4_ACL, "New ACL entry could not be added to hash, rc=%s", hash_table_err_to_str(rc)); *status = NFS_V4_ACL_HASH_SET_ERROR; return NULL; } return acl; } fsal_acl_status_t nfs4_acl_release_entry(fsal_acl_t *acl) { struct gsh_buffdesc key, old_key; struct gsh_buffdesc old_value; int rc; struct hash_latch latch; fsal_acl_status_t status = NFS_V4_ACL_SUCCESS; if (!acl) return status; PTHREAD_RWLOCK_wrlock(&acl->lock); if (acl->ref > 1) { nfs4_acl_entry_dec_ref(acl); PTHREAD_RWLOCK_unlock(&acl->lock); return status; } else LogDebug(COMPONENT_NFS_V4_ACL, "Free ACL %p", acl); key.addr = acl->aces; key.len = acl->naces * sizeof(fsal_ace_t); PTHREAD_RWLOCK_unlock(&acl->lock); /* Get the hash table entry and hold latch */ rc = hashtable_getlatch(fsal_acl_hash, &key, &old_value, true, &latch); switch (rc) { case HASHTABLE_ERROR_NO_SUCH_KEY: hashtable_releaselatched(fsal_acl_hash, &latch); return status; case HASHTABLE_SUCCESS: PTHREAD_RWLOCK_wrlock(&acl->lock); nfs4_acl_entry_dec_ref(acl); if (acl->ref != 0) { /* Did not actually release last reference */ hashtable_releaselatched(fsal_acl_hash, &latch); PTHREAD_RWLOCK_unlock(&acl->lock); return status; } /* use the key to delete the entry */ hashtable_deletelatched(fsal_acl_hash, &key, &latch, &old_key, &old_value); /* Release the latch */ hashtable_releaselatched(fsal_acl_hash, &latch); break; default: LogCrit(COMPONENT_NFS_V4_ACL, "ACL entry could not be deleted, status=%s", hash_table_err_to_str(rc)); return NFS_V4_ACL_ERROR; } /* Sanity check: old_value.addr is expected to be equal to acl, * and is released later in this function */ assert(old_value.addr == acl); PTHREAD_RWLOCK_unlock(&acl->lock); /* Release acl */ nfs4_acl_free(acl); return status; } static void nfs4_acls_test(void) { int i = 0; fsal_acl_data_t acldata, acldata2; fsal_ace_t *ace = NULL; fsal_acl_t *acl = NULL; fsal_acl_status_t status; acldata.naces = 3; acldata.aces = nfs4_ace_alloc(3); LogDebug(COMPONENT_NFS_V4_ACL, "acldata.aces = %p", acldata.aces); ace = acldata.aces; for (i = 0; i < 3; i++) { ace->type = i; ace->perm = i; ace->flag = i; ace->who.uid = i; ace++; } acl = nfs4_acl_new_entry(&acldata, &status); PTHREAD_RWLOCK_rdlock(&acl->lock); LogDebug(COMPONENT_NFS_V4_ACL, "acl = %p, ref = %u, status = %u", acl, acl->ref, status); PTHREAD_RWLOCK_unlock(&acl->lock); acldata2.naces = 3; acldata2.aces = nfs4_ace_alloc(3); LogDebug(COMPONENT_NFS_V4_ACL, "acldata2.aces = %p", acldata2.aces); ace = acldata2.aces; for (i = 0; i < 3; i++) { ace->type = i; ace->perm = i; ace->flag = i; ace->who.uid = i; ace++; } acl = nfs4_acl_new_entry(&acldata2, &status); PTHREAD_RWLOCK_rdlock(&acl->lock); LogDebug(COMPONENT_NFS_V4_ACL, "re-access: acl = %p, ref = %u, status = %u", acl, acl->ref, status); PTHREAD_RWLOCK_unlock(&acl->lock); status = nfs4_acl_release_entry(acl); PTHREAD_RWLOCK_rdlock(&acl->lock); LogDebug(COMPONENT_NFS_V4_ACL, "release: acl = %p, ref = %u, status = %u", acl, acl->ref, status); PTHREAD_RWLOCK_unlock(&acl->lock); status = nfs4_acl_release_entry(acl); } int nfs4_acls_init(void) { LogDebug(COMPONENT_NFS_V4_ACL, "Initialize NFSv4 ACLs"); LogDebug(COMPONENT_NFS_V4_ACL, "sizeof(fsal_ace_t)=%zu, sizeof(fsal_acl_t)=%zu", sizeof(fsal_ace_t), sizeof(fsal_acl_t)); /* Initialize memory pool of ACLs. */ fsal_acl_pool = pool_basic_init("acl_pool", sizeof(fsal_acl_t)); /* Create hash table. */ fsal_acl_hash = hashtable_init(&fsal_acl_hash_config); if (!fsal_acl_hash) { LogCrit(COMPONENT_NFS_V4_ACL, "ERROR creating hash table for NFSv4 ACLs"); return NFS_V4_ACL_INTERNAL_ERROR; } nfs4_acls_test(); return NFS_V4_ACL_SUCCESS; } ����������������������nfs-ganesha-2.6.0/src/support/nfs_convert.c���������������������������������������������������������0000664�0000000�0000000�00000042504�13242724102�0020710�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_convert.c * @brief NFS conversion tools. */ #include <string.h> #include "nfs_convert.h" #include "nfs23.h" #include "nfs4.h" #include "mount.h" char *nfsstat3_to_str(nfsstat3 code) { switch (code) { /* no nead for break statments, * because we "return". */ case NFS3_OK: return "NFS3_OK"; case NFS3ERR_PERM: return "NFS3ERR_PERM"; case NFS3ERR_NOENT: return "NFS3ERR_NOENT"; case NFS3ERR_IO: return "NFS3ERR_IO"; case NFS3ERR_NXIO: return "NFS3ERR_NXIO"; case NFS3ERR_ACCES: return "NFS3ERR_ACCES"; case NFS3ERR_EXIST: return "NFS3ERR_EXIST"; case NFS3ERR_XDEV: return "NFS3ERR_XDEV"; case NFS3ERR_NODEV: return "NFS3ERR_NODEV"; case NFS3ERR_NOTDIR: return "NFS3ERR_NOTDIR"; case NFS3ERR_ISDIR: return "NFS3ERR_ISDIR"; case NFS3ERR_INVAL: return "NFS3ERR_INVAL"; case NFS3ERR_FBIG: return "NFS3ERR_FBIG"; case NFS3ERR_NOSPC: return "NFS3ERR_NOSPC"; case NFS3ERR_ROFS: return "NFS3ERR_ROFS"; case NFS3ERR_MLINK: return "NFS3ERR_MLINK"; case NFS3ERR_NAMETOOLONG: return "NFS3ERR_NAMETOOLONG"; case NFS3ERR_NOTEMPTY: return "NFS3ERR_NOTEMPTY"; case NFS3ERR_DQUOT: return "NFS3ERR_DQUOT"; case NFS3ERR_STALE: return "NFS3ERR_STALE"; case NFS3ERR_REMOTE: return "NFS3ERR_REMOTE"; case NFS3ERR_BADHANDLE: return "NFS3ERR_BADHANDLE"; case NFS3ERR_NOT_SYNC: return "NFS3ERR_NOT_SYNC"; case NFS3ERR_BAD_COOKIE: return "NFS3ERR_BAD_COOKIE"; case NFS3ERR_NOTSUPP: return "NFS3ERR_NOTSUPP"; case NFS3ERR_TOOSMALL: return "NFS3ERR_TOOSMALL"; case NFS3ERR_SERVERFAULT: return "NFS3ERR_SERVERFAULT"; case NFS3ERR_BADTYPE: return "NFS3ERR_BADTYPE"; case NFS3ERR_JUKEBOX: return "NFS3ERR_JUKEBOX"; } return "UNKNOWN NFSv3 ERROR CODE"; } char *nfsstat4_to_str(nfsstat4 code) { switch (code) { case NFS4_OK: return "NFS4_OK"; case NFS4ERR_PERM: return "NFS4ERR_PERM"; case NFS4ERR_NOENT: return "NFS4ERR_NOENT"; case NFS4ERR_IO: return "NFS4ERR_IO"; case NFS4ERR_NXIO: return "NFS4ERR_NXIO"; case NFS4ERR_ACCESS: return "NFS4ERR_ACCESS"; case NFS4ERR_EXIST: return "NFS4ERR_EXIST"; case NFS4ERR_XDEV: return "NFS4ERR_XDEV"; case NFS4ERR_NOTDIR: return "NFS4ERR_NOTDIR"; case NFS4ERR_ISDIR: return "NFS4ERR_ISDIR"; case NFS4ERR_INVAL: return "NFS4ERR_INVAL"; case NFS4ERR_FBIG: return "NFS4ERR_FBIG"; case NFS4ERR_NOSPC: return "NFS4ERR_NOSPC"; case NFS4ERR_ROFS: return "NFS4ERR_ROFS"; case NFS4ERR_MLINK: return "NFS4ERR_MLINK"; case NFS4ERR_NAMETOOLONG: return "NFS4ERR_NAMETOOLONG"; case NFS4ERR_NOTEMPTY: return "NFS4ERR_NOTEMPTY"; case NFS4ERR_DQUOT: return "NFS4ERR_DQUOT"; case NFS4ERR_STALE: return "NFS4ERR_STALE"; case NFS4ERR_BADHANDLE: return "NFS4ERR_BADHANDLE"; case NFS4ERR_BAD_COOKIE: return "NFS4ERR_BAD_COOKIE"; case NFS4ERR_NOTSUPP: return "NFS4ERR_NOTSUPP"; case NFS4ERR_TOOSMALL: return "NFS4ERR_TOOSMALL"; case NFS4ERR_SERVERFAULT: return "NFS4ERR_SERVERFAULT"; case NFS4ERR_BADTYPE: return "NFS4ERR_BADTYPE"; case NFS4ERR_DELAY: return "NFS4ERR_DELAY"; case NFS4ERR_SAME: return "NFS4ERR_SAME"; case NFS4ERR_DENIED: return "NFS4ERR_DENIED"; case NFS4ERR_EXPIRED: return "NFS4ERR_EXPIRED"; case NFS4ERR_LOCKED: return "NFS4ERR_LOCKED"; case NFS4ERR_GRACE: return "NFS4ERR_GRACE"; case NFS4ERR_FHEXPIRED: return "NFS4ERR_FHEXPIRED"; case NFS4ERR_SHARE_DENIED: return "NFS4ERR_SHARE_DENIED"; case NFS4ERR_WRONGSEC: return "NFS4ERR_WRONGSEC"; case NFS4ERR_CLID_INUSE: return "NFS4ERR_CLID_INUSE"; case NFS4ERR_RESOURCE: return "NFS4ERR_RESOURCE"; case NFS4ERR_MOVED: return "NFS4ERR_MOVED"; case NFS4ERR_NOFILEHANDLE: return "NFS4ERR_NOFILEHANDLE"; case NFS4ERR_MINOR_VERS_MISMATCH: return "NFS4ERR_MINOR_VERS_MISMATCH"; case NFS4ERR_STALE_CLIENTID: return "NFS4ERR_STALE_CLIENTID"; case NFS4ERR_STALE_STATEID: return "NFS4ERR_STALE_STATEID"; case NFS4ERR_OLD_STATEID: return "NFS4ERR_OLD_STATEID"; case NFS4ERR_BAD_STATEID: return "NFS4ERR_BAD_STATEID"; case NFS4ERR_BAD_SEQID: return "NFS4ERR_BAD_SEQID"; case NFS4ERR_NOT_SAME: return "NFS4ERR_NOT_SAME"; case NFS4ERR_LOCK_RANGE: return "NFS4ERR_LOCK_RANGE"; case NFS4ERR_SYMLINK: return "NFS4ERR_SYMLINK"; case NFS4ERR_RESTOREFH: return "NFS4ERR_RESTOREFH"; case NFS4ERR_LEASE_MOVED: return "NFS4ERR_LEASE_MOVED"; case NFS4ERR_ATTRNOTSUPP: return "NFS4ERR_ATTRNOTSUPP"; case NFS4ERR_NO_GRACE: return "NFS4ERR_NO_GRACE"; case NFS4ERR_RECLAIM_BAD: return "NFS4ERR_RECLAIM_BAD"; case NFS4ERR_RECLAIM_CONFLICT: return "NFS4ERR_RECLAIM_CONFLICT"; case NFS4ERR_BADXDR: return "NFS4ERR_BADXDR"; case NFS4ERR_LOCKS_HELD: return "NFS4ERR_LOCKS_HELD"; case NFS4ERR_OPENMODE: return "NFS4ERR_OPENMODE"; case NFS4ERR_BADOWNER: return "NFS4ERR_BADOWNER"; case NFS4ERR_BADCHAR: return "NFS4ERR_BADCHAR"; case NFS4ERR_BADNAME: return "NFS4ERR_BADNAME"; case NFS4ERR_BAD_RANGE: return "NFS4ERR_BAD_RANGE"; case NFS4ERR_LOCK_NOTSUPP: return "NFS4ERR_LOCK_NOTSUPP"; case NFS4ERR_OP_ILLEGAL: return "NFS4ERR_OP_ILLEGAL"; case NFS4ERR_DEADLOCK: return "NFS4ERR_DEADLOCK"; case NFS4ERR_FILE_OPEN: return "NFS4ERR_FILE_OPEN"; case NFS4ERR_ADMIN_REVOKED: return "NFS4ERR_ADMIN_REVOKED"; case NFS4ERR_CB_PATH_DOWN: return "NFS4ERR_CB_PATH_DOWN"; case NFS4ERR_BADIOMODE: return "NFS4ERR_BADIOMODE"; case NFS4ERR_BADLAYOUT: return "NFS4ERR_BADLAYOUT"; case NFS4ERR_BAD_SESSION_DIGEST: return "NFS4ERR_BAD_SESSION_DIGEST"; case NFS4ERR_BADSESSION: return "NFS4ERR_BADSESSION"; case NFS4ERR_BADSLOT: return "NFS4ERR_BADSLOT"; case NFS4ERR_COMPLETE_ALREADY: return "NFS4ERR_COMPLETE_ALREADY"; case NFS4ERR_CONN_NOT_BOUND_TO_SESSION: return "NFS4ERR_CONN_NOT_BOUND_TO_SESSION"; case NFS4ERR_DELEG_ALREADY_WANTED: return "NFS4ERR_DELEG_ALREADY_WANTED"; case NFS4ERR_BACK_CHAN_BUSY: return "NFS4ERR_BACK_CHAN_BUSY"; case NFS4ERR_LAYOUTTRYLATER: return "NFS4ERR_LAYOUTTRYLATER"; case NFS4ERR_LAYOUTUNAVAILABLE: return "NFS4ERR_LAYOUTUNAVAILABLE"; case NFS4ERR_NOMATCHING_LAYOUT: return "NFS4ERR_NOMATCHING_LAYOUT"; case NFS4ERR_RECALLCONFLICT: return "NFS4ERR_RECALLCONFLICT"; case NFS4ERR_UNKNOWN_LAYOUTTYPE: return "NFS4ERR_UNKNOWN_LAYOUTTYPE"; case NFS4ERR_SEQ_MISORDERED: return "NFS4ERR_SEQ_MISORDERED"; case NFS4ERR_SEQUENCE_POS: return "NFS4ERR_SEQUENCE_POS"; case NFS4ERR_REQ_TOO_BIG: return "NFS4ERR_REQ_TOO_BIG"; case NFS4ERR_REP_TOO_BIG: return "NFS4ERR_REP_TOO_BIG"; case NFS4ERR_REP_TOO_BIG_TO_CACHE: return "NFS4ERR_REP_TOO_BIG_TO_CACHE"; case NFS4ERR_RETRY_UNCACHED_REP: return "NFS4ERR_RETRY_UNCACHED_REP"; case NFS4ERR_UNSAFE_COMPOUND: return "NFS4ERR_UNSAFE_COMPOUND"; case NFS4ERR_TOO_MANY_OPS: return "NFS4ERR_TOO_MANY_OPS"; case NFS4ERR_OP_NOT_IN_SESSION: return "NFS4ERR_OP_NOT_IN_SESSION"; case NFS4ERR_HASH_ALG_UNSUPP: return "NFS4ERR_HASH_ALG_UNSUPP"; case NFS4ERR_CLIENTID_BUSY: return "NFS4ERR_CLIENTID_BUSY"; case NFS4ERR_PNFS_IO_HOLE: return "NFS4ERR_PNFS_IO_HOLE"; case NFS4ERR_SEQ_FALSE_RETRY: return "NFS4ERR_SEQ_FALSE_RETRY"; case NFS4ERR_BAD_HIGH_SLOT: return "NFS4ERR_BAD_HIGH_SLOT"; case NFS4ERR_DEADSESSION: return "NFS4ERR_DEADSESSION"; case NFS4ERR_ENCR_ALG_UNSUPP: return "NFS4ERR_ENCR_ALG_UNSUPP"; case NFS4ERR_PNFS_NO_LAYOUT: return "NFS4ERR_PNFS_NO_LAYOUT"; case NFS4ERR_NOT_ONLY_OP: return "NFS4ERR_NOT_ONLY_OP"; case NFS4ERR_WRONG_CRED: return "NFS4ERR_WRONG_CRED"; case NFS4ERR_WRONG_TYPE: return "NFS4ERR_WRONG_TYPE"; case NFS4ERR_DIRDELEG_UNAVAIL: return "NFS4ERR_DIRDELEG_UNAVAIL"; case NFS4ERR_REJECT_DELEG: return "NFS4ERR_REJECT_DELEG"; case NFS4ERR_RETURNCONFLICT: return "NFS4ERR_RETURNCONFLICT"; case NFS4ERR_DELEG_REVOKED: return "NFS4ERR_DELEG_REVOKED"; /* NFSv4.2 */ case NFS4ERR_PARTNER_NOTSUPP: return "NFS4ERR_PARTNER_NOTSUPP"; case NFS4ERR_PARTNER_NO_AUTH: return "NFS4ERR_PARTNER_NO_AUTH"; case NFS4ERR_OFFLOAD_DENIED: return "NFS4ERR_OFFLOAD_DENIED"; case NFS4ERR_WRONG_LFS: return "NFS4ERR_WRONG_LFS"; case NFS4ERR_BADLABEL: return "NFS4ERR_BADLABEL"; case NFS4ERR_OFFLOAD_NO_REQS: return "NFS4ERR_OFFLOAD_NO_REQS"; case NFS4ERR_UNION_NOTSUPP: return "NFS4ERR_UNION_NOTSUPP"; case NFS4ERR_REPLAY: return "NFS4ERR_REPLAY"; } return "UNKNOWN NFSv4 ERROR CODE"; } char *nfstype3_to_str(ftype3 code) { switch (code) { /* no nead for break statments, * because we "return". */ case NF3REG: return "NF3REG"; case NF3DIR: return "NF3DIR"; case NF3BLK: return "NF3BLK"; case NF3CHR: return "NF3CHR"; case NF3LNK: return "NF3LNK"; case NF3SOCK: return "NF3SOCK"; case NF3FIFO: return "NF3FIFO"; } return "UNKNOWN NFSv3 TYPE"; } /** * @brief Same as htonl, but on 64 bits. * * @param[in] arg64 Value in host byte order * * @return Value in network byte order */ uint64_t nfs_htonl64(uint64_t arg64) { uint64_t res64; #ifdef LITTLEEND uint32_t low = (uint32_t) (arg64 & 0x00000000FFFFFFFFLL); uint32_t high = (uint32_t) ((arg64 & 0xFFFFFFFF00000000LL) >> 32); low = htonl(low); high = htonl(high); res64 = (uint64_t) high + (((uint64_t) low) << 32); #else res64 = arg64; #endif return res64; } /** * @brief Same as ntohl, but on 64 bits. * * @param[in] arg64 Value in network byte order * * @return value in host byte order. */ uint64_t nfs_ntohl64(uint64_t arg64) { uint64_t res64; #ifdef LITTLEEND uint32_t low = (uint32_t) (arg64 & 0x00000000FFFFFFFFLL); uint32_t high = (uint32_t) ((arg64 & 0xFFFFFFFF00000000LL) >> 32); low = ntohl(low); high = ntohl(high); res64 = (uint64_t) high + (((uint64_t) low) << 32); #else res64 = arg64; #endif return res64; } /** * @brief Converts an auth_stat enum to a string * * @param[in] why The stat to convert * * @return String describing the status */ const char *auth_stat2str(enum auth_stat why) { switch (why) { case AUTH_OK: return "AUTH_OK"; case AUTH_BADCRED: return "AUTH_BADCRED"; case AUTH_REJECTEDCRED: return "AUTH_REJECTEDCRED"; case AUTH_BADVERF: return "AUTH_BADVERF"; case AUTH_REJECTEDVERF: return "AUTH_REJECTEDVERF"; case AUTH_TOOWEAK: return "AUTH_TOOWEAK"; case AUTH_INVALIDRESP: return "AUTH_INVALIDRESP"; case AUTH_FAILED: return "AUTH_FAILED"; case RPCSEC_GSS_CREDPROBLEM: return "RPCSEC_GSS_CREDPROBLEM"; case RPCSEC_GSS_CTXPROBLEM: return "RPCSEC_GSS_CTXPROBLEM"; } return "UNKNOWN AUTH"; } /* Error conversion routines */ /** * @brief Convert a FSAL error to a nfsv4 status. * * @param[in] error The FSAL error * @param[in] where String identifying the caller * * @return the converted NFSv4 status. * */ nfsstat4 nfs4_Errno_verbose(fsal_errors_t error, const char *where) { nfsstat4 nfserror = NFS4ERR_INVAL; switch (error) { case ERR_FSAL_NO_ERROR: nfserror = NFS4_OK; break; case ERR_FSAL_NOMEM: nfserror = NFS4ERR_SERVERFAULT; break; case ERR_FSAL_SYMLINK: case ERR_FSAL_BADTYPE: case ERR_FSAL_INVAL: case ERR_FSAL_OVERFLOW: nfserror = NFS4ERR_INVAL; break; case ERR_FSAL_NOTDIR: nfserror = NFS4ERR_NOTDIR; break; case ERR_FSAL_EXIST: nfserror = NFS4ERR_EXIST; break; case ERR_FSAL_NOTEMPTY: nfserror = NFS4ERR_NOTEMPTY; break; case ERR_FSAL_NOENT: nfserror = NFS4ERR_NOENT; break; case ERR_FSAL_NOT_OPENED: case ERR_FSAL_BLOCKED: case ERR_FSAL_INTERRUPT: case ERR_FSAL_NOT_INIT: case ERR_FSAL_ALREADY_INIT: case ERR_FSAL_BAD_INIT: case ERR_FSAL_TIMEOUT: case ERR_FSAL_IO: case ERR_FSAL_NXIO: LogCrit(COMPONENT_NFS_V4, "Error %s in %s converted to NFS4ERR_IO but was set non-retryable", msg_fsal_err(error), where); nfserror = NFS4ERR_IO; break; case ERR_FSAL_ACCESS: nfserror = NFS4ERR_ACCESS; break; case ERR_FSAL_PERM: case ERR_FSAL_SEC: nfserror = NFS4ERR_PERM; break; case ERR_FSAL_NOSPC: nfserror = NFS4ERR_NOSPC; break; case ERR_FSAL_ISDIR: nfserror = NFS4ERR_ISDIR; break; case ERR_FSAL_ROFS: nfserror = NFS4ERR_ROFS; break; case ERR_FSAL_NAMETOOLONG: nfserror = NFS4ERR_NAMETOOLONG; break; case ERR_FSAL_STALE: case ERR_FSAL_FHEXPIRED: nfserror = NFS4ERR_STALE; break; case ERR_FSAL_DQUOT: case ERR_FSAL_NO_QUOTA: nfserror = NFS4ERR_DQUOT; break; case ERR_FSAL_NOTSUPP: case ERR_FSAL_ATTRNOTSUPP: nfserror = NFS4ERR_NOTSUPP; break; case ERR_FSAL_UNION_NOTSUPP: nfserror = NFS4ERR_UNION_NOTSUPP; break; case ERR_FSAL_DELAY: nfserror = NFS4ERR_DELAY; break; case ERR_FSAL_FBIG: nfserror = NFS4ERR_FBIG; break; case ERR_FSAL_FILE_OPEN: nfserror = NFS4ERR_FILE_OPEN; break; case ERR_FSAL_BADCOOKIE: nfserror = NFS4ERR_BAD_COOKIE; break; case ERR_FSAL_TOOSMALL: nfserror = NFS4ERR_TOOSMALL; break; case ERR_FSAL_NO_DATA: case ERR_FSAL_FAULT: case ERR_FSAL_SERVERFAULT: nfserror = NFS4ERR_SERVERFAULT; break; case ERR_FSAL_DEADLOCK: nfserror = NFS4ERR_DEADLOCK; break; case ERR_FSAL_XDEV: nfserror = NFS4ERR_XDEV; break; case ERR_FSAL_BADHANDLE: nfserror = NFS4ERR_BADHANDLE; break; case ERR_FSAL_MLINK: nfserror = NFS4ERR_MLINK; break; case ERR_FSAL_SHARE_DENIED: nfserror = NFS4ERR_SHARE_DENIED; break; case ERR_FSAL_LOCKED: nfserror = NFS4ERR_LOCKED; break; case ERR_FSAL_IN_GRACE: nfserror = NFS4ERR_GRACE; break; case ERR_FSAL_BAD_RANGE: nfserror = NFS4ERR_BAD_RANGE; break; case ERR_FSAL_BADNAME: nfserror = NFS4ERR_BADNAME; break; case ERR_FSAL_CROSS_JUNCTION: case ERR_FSAL_NO_ACE: /* Should not occur */ LogDebug(COMPONENT_NFS_V4, "Line %u should never be reached in nfs4_Errno from %s for cache_status=%u", __LINE__, where, error); nfserror = NFS4ERR_INVAL; break; } return nfserror; } /** * * @brief Convert a FSAL error status to a nfsv3 status. * * @param[in] error Input FSAL error * @param[in] where String identifying caller * * @return the converted NFSv3 status. * */ #ifdef _USE_NFS3 nfsstat3 nfs3_Errno_verbose(fsal_errors_t error, const char *where) { nfsstat3 nfserror = NFS3ERR_INVAL; switch (error) { case ERR_FSAL_NO_ERROR: nfserror = NFS3_OK; break; case ERR_FSAL_NOMEM: case ERR_FSAL_FILE_OPEN: case ERR_FSAL_NOT_OPENED: case ERR_FSAL_IO: case ERR_FSAL_NXIO: LogCrit(COMPONENT_NFSPROTO, "Error %s in %s converted to NFS3ERR_IO but was set non-retryable", msg_fsal_err(error), where); nfserror = NFS3ERR_IO; break; case ERR_FSAL_INVAL: case ERR_FSAL_OVERFLOW: nfserror = NFS3ERR_INVAL; break; case ERR_FSAL_NOTDIR: nfserror = NFS3ERR_NOTDIR; break; case ERR_FSAL_EXIST: nfserror = NFS3ERR_EXIST; break; case ERR_FSAL_NOTEMPTY: nfserror = NFS3ERR_NOTEMPTY; break; case ERR_FSAL_NOENT: nfserror = NFS3ERR_NOENT; break; case ERR_FSAL_ACCESS: nfserror = NFS3ERR_ACCES; break; case ERR_FSAL_PERM: case ERR_FSAL_SEC: nfserror = NFS3ERR_PERM; break; case ERR_FSAL_NOSPC: nfserror = NFS3ERR_NOSPC; break; case ERR_FSAL_ISDIR: nfserror = NFS3ERR_ISDIR; break; case ERR_FSAL_ROFS: nfserror = NFS3ERR_ROFS; break; case ERR_FSAL_STALE: case ERR_FSAL_FHEXPIRED: nfserror = NFS3ERR_STALE; break; case ERR_FSAL_DQUOT: case ERR_FSAL_NO_QUOTA: nfserror = NFS3ERR_DQUOT; break; case ERR_FSAL_SYMLINK: case ERR_FSAL_BADTYPE: nfserror = NFS3ERR_BADTYPE; break; case ERR_FSAL_NOTSUPP: case ERR_FSAL_ATTRNOTSUPP: case ERR_FSAL_UNION_NOTSUPP: nfserror = NFS3ERR_NOTSUPP; break; case ERR_FSAL_DELAY: case ERR_FSAL_SHARE_DENIED: case ERR_FSAL_LOCKED: nfserror = NFS3ERR_JUKEBOX; break; case ERR_FSAL_NAMETOOLONG: nfserror = NFS3ERR_NAMETOOLONG; break; case ERR_FSAL_FBIG: nfserror = NFS3ERR_FBIG; break; case ERR_FSAL_BADCOOKIE: nfserror = NFS3ERR_BAD_COOKIE; break; case ERR_FSAL_TOOSMALL: nfserror = NFS3ERR_TOOSMALL; break; case ERR_FSAL_NO_DATA: case ERR_FSAL_FAULT: case ERR_FSAL_SERVERFAULT: case ERR_FSAL_DEADLOCK: nfserror = NFS3ERR_SERVERFAULT; break; case ERR_FSAL_XDEV: nfserror = NFS3ERR_XDEV; break; case ERR_FSAL_BADNAME: nfserror = NFS3ERR_INVAL; break; case ERR_FSAL_BADHANDLE: nfserror = NFS3ERR_BADHANDLE; break; case ERR_FSAL_MLINK: nfserror = NFS3ERR_MLINK; break; case ERR_FSAL_IN_GRACE: nfserror = NFS3ERR_JUKEBOX; break; case ERR_FSAL_CROSS_JUNCTION: case ERR_FSAL_BLOCKED: case ERR_FSAL_INTERRUPT: case ERR_FSAL_NOT_INIT: case ERR_FSAL_ALREADY_INIT: case ERR_FSAL_BAD_INIT: case ERR_FSAL_TIMEOUT: case ERR_FSAL_NO_ACE: case ERR_FSAL_BAD_RANGE: /* Should not occur */ LogDebug(COMPONENT_NFSPROTO, "Line %u should never be reached in nfs3_Errno from %s for FSAL error=%s", __LINE__, where, msg_fsal_err(error)); nfserror = NFS3ERR_INVAL; break; } return nfserror; } /* nfs3_Errno */ #endif ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/support/nfs_creds.c�����������������������������������������������������������0000664�0000000�0000000�00000060727�13242724102�0020337�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_export_list.c * @brief Routines for managing the export list. */ #include "config.h" #include <stdio.h> #include <sys/types.h> #include <ctype.h> /* for having isalnum */ #include <stdlib.h> /* for having atoi */ #include <dirent.h> /* for having MAXNAMLEN */ #include <netdb.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <pthread.h> #include <fcntl.h> #include <sys/file.h> /* for having FNDELAY */ #include <pwd.h> #include <grp.h> #include "log.h" #include "gsh_rpc.h" #include "nfs_core.h" #include "nfs23.h" #include "nfs4.h" #include "fsal.h" #include "nfs_exports.h" #include "nfs_creds.h" #include "nfs_file_handle.h" #include "idmapper.h" #include "export_mgr.h" #include "uid2grp.h" #include "client_mgr.h" /* Export permissions for root op context */ uint32_t root_op_export_options = EXPORT_OPTION_ROOT | EXPORT_OPTION_ACCESS_MASK | EXPORT_OPTION_AUTH_TYPES | EXPORT_OPTION_PROTOCOLS | EXPORT_OPTION_TRANSPORTS; uint32_t root_op_export_set = EXPORT_OPTION_SQUASH_TYPES | EXPORT_OPTION_ANON_UID_SET | EXPORT_OPTION_ANON_GID_SET | EXPORT_OPTION_ACCESS_MASK | EXPORT_OPTION_AUTH_TYPES | EXPORT_OPTION_PROTOCOLS | EXPORT_OPTION_TRANSPORTS; void squash_setattr(struct attrlist *attr) { if (attr->valid_mask & ATTR_OWNER && op_ctx->export_perms->anonymous_uid != 0) { if (op_ctx->export_perms->options & EXPORT_OPTION_ALL_ANONYMOUS) attr->owner = op_ctx->export_perms->anonymous_uid; else if (((op_ctx->export_perms->options & EXPORT_OPTION_ROOT_SQUASH) || (op_ctx->export_perms->options & EXPORT_OPTION_ROOT_ID_SQUASH)) && (attr->owner == 0) && ((op_ctx->cred_flags & UID_SQUASHED) != 0)) attr->owner = op_ctx->export_perms->anonymous_uid; } if (attr->valid_mask & ATTR_GROUP && op_ctx->export_perms->anonymous_gid != 0) { /* If all squashed, then always squash the owner_group. * * If root squashed, then squash owner_group if * caller_gid has been squashed or one of the caller's * alternate groups has been squashed. */ if (op_ctx->export_perms->options & EXPORT_OPTION_ALL_ANONYMOUS) attr->group = op_ctx->export_perms->anonymous_gid; else if (((op_ctx->export_perms->options & EXPORT_OPTION_ROOT_SQUASH) || (op_ctx->export_perms->options & EXPORT_OPTION_ROOT_ID_SQUASH)) && (attr->group == 0) && ((op_ctx->cred_flags & (GID_SQUASHED | GARRAY_SQUASHED)) != 0)) attr->group = op_ctx->export_perms->anonymous_gid; } } /** * @brief Compares two RPC creds * * @param[in] cred1 First RPC cred * @param[in] cred2 Second RPC cred * * @return true if same, false otherwise */ bool nfs_compare_clientcred(nfs_client_cred_t *cred1, nfs_client_cred_t *cred2) { #ifdef _HAVE_GSSAPI gss_name_t cred1_cred_name; gss_name_t cred2_cred_name; OM_uint32 maj_stat, min_stat; int status; #endif if (cred1 == NULL) return false; if (cred2 == NULL) return false; if (cred1->flavor != cred2->flavor) return false; switch (cred1->flavor) { case AUTH_UNIX: if (cred1->auth_union.auth_unix.aup_uid != cred2->auth_union.auth_unix.aup_uid) return false; if (cred1->auth_union.auth_unix.aup_gid != cred2->auth_union.auth_unix.aup_gid) return false; break; #ifdef _HAVE_GSSAPI case RPCSEC_GSS: maj_stat = gss_inquire_context(&min_stat, cred1->auth_union.auth_gss.gd->ctx, &cred1_cred_name, NULL, NULL, NULL, NULL, NULL, NULL); if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTEXT_EXPIRED) return false; maj_stat = gss_inquire_context(&min_stat, cred2->auth_union.auth_gss.gd->ctx, &cred2_cred_name, NULL, NULL, NULL, NULL, NULL, NULL); if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTEXT_EXPIRED) return false; maj_stat = gss_compare_name(&min_stat, cred1_cred_name, cred2_cred_name, &status); if (maj_stat != GSS_S_COMPLETE) return false; if (status == 0) return false; break; #endif default: if (memcmp (&cred1->auth_union, &cred2->auth_union, cred1->length)) return false; break; } /* If this point is reached, structures are the same */ return true; } int nfs_rpc_req2client_cred(struct svc_req *req, nfs_client_cred_t *pcred) { /* Structure for managing basic AUTH_UNIX authentication */ struct authunix_parms *aup = NULL; /* Stuff needed for managing RPCSEC_GSS */ #ifdef _HAVE_GSSAPI struct svc_rpc_gss_data *gd = NULL; #endif pcred->length = req->rq_msg.cb_cred.oa_length; pcred->flavor = req->rq_msg.cb_cred.oa_flavor; switch (req->rq_msg.cb_cred.oa_flavor) { case AUTH_NONE: /* Do nothing... */ break; case AUTH_UNIX: aup = (struct authunix_parms *)req->rq_msg.rq_cred_body; pcred->auth_union.auth_unix.aup_uid = aup->aup_uid; pcred->auth_union.auth_unix.aup_gid = aup->aup_gid; pcred->auth_union.auth_unix.aup_time = aup->aup_time; break; #ifdef _HAVE_GSSAPI case RPCSEC_GSS: /* Extract the information from the RPCSEC_GSS * opaque structure */ gd = SVCAUTH_PRIVATE(req->rq_auth); pcred->auth_union.auth_gss.svc = (unsigned int)(gd->sec.svc); pcred->auth_union.auth_gss.qop = (unsigned int)(gd->sec.qop); pcred->auth_union.auth_gss.gd = gd; break; #endif default: /* Unsupported authentication flavour */ return -1; } return 1; } /** * @brief Get numeric credentials from request * * @todo This MUST be refactored to not use TI-RPC private structures. * Instead, export appropriate functions from lib(n)tirpc. * * fills out creds in op_ctx * * @param[in] req Incoming request. * * @return NFS4_OK if successful, NFS4ERR_ACCESS otherwise. * */ nfsstat4 nfs_req_creds(struct svc_req *req) { unsigned int i; const char *auth_label = "UNKNOWN"; gid_t **garray_copy = &op_ctx->caller_garray_copy; #ifdef _HAVE_GSSAPI struct svc_rpc_gss_data *gd = NULL; char principal[MAXNAMLEN + 1]; #endif /* Make sure we clear out all the cred_flags except CREDS_LOADED and * CREDS_ANON. */ op_ctx->cred_flags &= CREDS_LOADED | CREDS_ANON; switch (req->rq_msg.cb_cred.oa_flavor) { case AUTH_NONE: /* Nothing to be done here... */ op_ctx->cred_flags |= CREDS_LOADED | CREDS_ANON; auth_label = "AUTH_NONE"; break; case AUTH_SYS: if ((op_ctx->cred_flags & CREDS_LOADED) == 0) { struct authunix_parms *creds = (struct authunix_parms *) req->rq_msg.rq_cred_body; /* We map the rq_cred to Authunix_parms */ op_ctx->original_creds.caller_uid = creds->aup_uid; op_ctx->original_creds.caller_gid = creds->aup_gid; op_ctx->original_creds.caller_glen = creds->aup_len; op_ctx->original_creds.caller_garray = creds->aup_gids; op_ctx->cred_flags |= CREDS_LOADED; } /* Copy original_creds creds */ *op_ctx->creds = op_ctx->original_creds; /* Do we trust AUTH_SYS creds for groups or not ? */ if ((op_ctx->export_perms->options & EXPORT_OPTION_MANAGE_GIDS) != 0) { op_ctx->cred_flags |= MANAGED_GIDS; garray_copy = &op_ctx->managed_garray_copy; } auth_label = "AUTH_SYS"; break; #ifdef _HAVE_GSSAPI case RPCSEC_GSS: if ((op_ctx->cred_flags & CREDS_LOADED) == 0) { /* Get the gss data to process them */ gd = SVCAUTH_PRIVATE(req->rq_auth); memcpy(principal, gd->cname.value, gd->cname.length); principal[gd->cname.length] = 0; LogMidDebug(COMPONENT_DISPATCH, "Mapping RPCSEC_GSS principal %s to uid/gid", principal); /* Convert to uid */ #if _MSPAC_SUPPORT if (!principal2uid(principal, &op_ctx->original_creds.caller_uid, &op_ctx->original_creds.caller_gid, gd)) { #else if (!principal2uid(principal, &op_ctx->original_creds.caller_uid, &op_ctx->original_creds.caller_gid) ) { #endif LogWarn(COMPONENT_IDMAPPER, "Could not map principal %s to uid", principal); /* For compatibility with Linux knfsd, we set * the uid/gid to anonymous when a name->uid * mapping can't be found. */ op_ctx->cred_flags |= CREDS_ANON | CREDS_LOADED; auth_label = "RPCSEC_GSS (no mapping)"; break; } op_ctx->cred_flags |= CREDS_LOADED; } auth_label = "RPCSEC_GSS"; op_ctx->cred_flags |= MANAGED_GIDS; garray_copy = &op_ctx->managed_garray_copy; break; #endif /* _USE_GSSRPC */ default: LogMidDebug(COMPONENT_DISPATCH, "FAILURE: Request xid=%" PRIu32 ", has unsupported authentication %" PRIu32, req->rq_msg.rm_xid, req->rq_msg.cb_cred.oa_flavor); /* Reject the request for weak authentication and * return to worker */ return NFS4ERR_ACCESS; break; } /****************************************************************/ /* Now check for anon creds or id squashing */ /****************************************************************/ if ((op_ctx->cred_flags & CREDS_ANON) != 0 || ((op_ctx->export_perms->options & EXPORT_OPTION_ALL_ANONYMOUS) != 0) || ((op_ctx->export_perms->options & EXPORT_OPTION_ROOT_SQUASH) != 0 && op_ctx->fsal_export->exp_ops.is_superuser(op_ctx->fsal_export, &op_ctx->original_creds))) { /* Squash uid, gid, and discard groups */ op_ctx->creds->caller_uid = op_ctx->export_perms->anonymous_uid; op_ctx->creds->caller_gid = op_ctx->export_perms->anonymous_gid; op_ctx->creds->caller_glen = 0; LogMidDebugAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "%s creds squashed to uid=%u, gid=%u", auth_label, op_ctx->creds->caller_uid, op_ctx->creds->caller_gid); op_ctx->cred_flags |= UID_SQUASHED | GID_SQUASHED; return NFS4_OK; } else if ((op_ctx->export_perms->options & EXPORT_OPTION_ROOT_ID_SQUASH) != 0 && op_ctx->fsal_export->exp_ops.is_superuser( op_ctx->fsal_export, &op_ctx->original_creds)) { /* Only squash root id, leave gid and groups alone for now */ op_ctx->creds->caller_uid = op_ctx->export_perms->anonymous_uid; op_ctx->cred_flags |= UID_SQUASHED; } else { /* Use original_creds uid */ op_ctx->creds->caller_uid = op_ctx->original_creds.caller_uid; } /****************************************************************/ /* Now sqush group or use original_creds gid */ /****************************************************************/ if (((op_ctx->export_perms->options & EXPORT_OPTION_ROOT_SQUASH) != 0 || (op_ctx->export_perms->options & EXPORT_OPTION_ROOT_ID_SQUASH) != 0) && op_ctx->original_creds.caller_gid == 0) { /* Squash gid */ op_ctx->creds->caller_gid = op_ctx->export_perms->anonymous_gid; op_ctx->cred_flags |= GID_SQUASHED; } else { /* Use original_creds gid */ op_ctx->creds->caller_gid = op_ctx->original_creds.caller_gid; } /****************************************************************/ /* Check if we have manage_gids. */ /****************************************************************/ if ((op_ctx->cred_flags & MANAGED_GIDS) != 0) { /* Fetch the group data if required */ if (op_ctx->caller_gdata == NULL && !uid2grp(op_ctx->original_creds.caller_uid, &op_ctx->caller_gdata)) { /** @todo: do we really want to bail here? */ LogInfo(COMPONENT_DISPATCH, "Attempt to fetch managed_gids failed"); return NFS4ERR_ACCESS; } op_ctx->creds->caller_glen = op_ctx->caller_gdata->nbgroups; op_ctx->creds->caller_garray = op_ctx->caller_gdata->groups; } else { /* Use the original_creds group list */ op_ctx->creds->caller_glen = op_ctx->original_creds.caller_glen; op_ctx->creds->caller_garray = op_ctx->original_creds.caller_garray; } /****************************************************************/ /* Check the garray for gid 0 to squash */ /****************************************************************/ /* If no root squashing in caller_garray, return now */ if ((op_ctx->export_perms->options & EXPORT_OPTION_SQUASH_TYPES) == 0 || op_ctx->creds->caller_glen == 0) goto out; for (i = 0; i < op_ctx->creds->caller_glen; i++) { if (op_ctx->creds->caller_garray[i] == 0) { /* Meed to make a copy, or use the old copy */ if ((*garray_copy) == NULL) { /* Make a copy of the active garray */ (*garray_copy) = gsh_malloc(op_ctx->creds->caller_glen * sizeof(gid_t)); memcpy((*garray_copy), op_ctx->creds->caller_garray, op_ctx->creds->caller_glen * sizeof(gid_t)); } /* Now squash the root id. Since the original copy is * always the same, any root ids in it were still in * the same place, so even if using a copy that had a * different anonymous_gid, we're fine. */ (*garray_copy)[i] = op_ctx->export_perms->anonymous_gid; /* Indicate we squashed the caller_garray */ op_ctx->cred_flags |= GARRAY_SQUASHED; } } /* If we squashed the caller_garray, use the squashed copy */ if ((op_ctx->cred_flags & GARRAY_SQUASHED) != 0) op_ctx->creds->caller_garray = *garray_copy; out: LogMidDebugAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "%s creds mapped to uid=%u%s, gid=%u%s, glen=%d%s", auth_label, op_ctx->creds->caller_uid, (op_ctx->cred_flags & UID_SQUASHED) != 0 ? " (squashed)" : "", op_ctx->creds->caller_gid, (op_ctx->cred_flags & GID_SQUASHED) != 0 ? " (squashed)" : "", op_ctx->creds->caller_glen, (op_ctx->cred_flags & MANAGED_GIDS) != 0 ? ((op_ctx->cred_flags & GARRAY_SQUASHED) != 0 ? " (managed and squashed)" : " (managed)") : ((op_ctx->cred_flags & GARRAY_SQUASHED) != 0 ? " (squashed)" : "")); return NFS4_OK; } /** * @brief Initialize request context and credentials. * */ void init_credentials(void) { memset(op_ctx->creds, 0, sizeof(*op_ctx->creds)); memset(&op_ctx->original_creds, 0, sizeof(op_ctx->original_creds)); op_ctx->creds->caller_uid = op_ctx->export_perms->anonymous_uid; op_ctx->creds->caller_gid = op_ctx->export_perms->anonymous_gid; op_ctx->caller_gdata = NULL; op_ctx->caller_garray_copy = NULL; op_ctx->managed_garray_copy = NULL; op_ctx->cred_flags = 0; } /** * @brief Release temporary credential resources. * */ void clean_credentials(void) { /* If Manage_gids is used, unref the group list. */ if (op_ctx->caller_gdata != NULL) uid2grp_unref(op_ctx->caller_gdata); /* Have we made a local copy of the managed_gids garray? */ if (op_ctx->managed_garray_copy != NULL) gsh_free(op_ctx->managed_garray_copy); /* Have we made a local copy of the AUTH_SYS garray? */ if (op_ctx->caller_garray_copy != NULL) gsh_free(op_ctx->caller_garray_copy); /* Prepare the request context and creds for re-use */ init_credentials(); } /** * @brief Validate export permissions * * @param[in] req Incoming request. * * @return NFS4_OK if successful, NFS4ERR_ACCESS or NFS4ERR_WRONGSEC otherwise. * */ nfsstat4 nfs4_export_check_access(struct svc_req *req) { xprt_type_t xprt_type = svc_get_xprt_type(req->rq_xprt); int port = get_port(op_ctx->caller_addr); LogMidDebugAlt(COMPONENT_NFS_V4, COMPONENT_EXPORT, "about to call export_check_access"); export_check_access(); /* Check if any access at all */ if ((op_ctx->export_perms->options & EXPORT_OPTION_ACCESS_MASK) == 0) { LogInfoAlt(COMPONENT_NFS_V4, COMPONENT_EXPORT, "Access not allowed on Export_Id %d %s for client %s", op_ctx->ctx_export->export_id, op_ctx->ctx_export->pseudopath, op_ctx->client ? op_ctx->client->hostaddr_str : "unknown client"); return NFS4ERR_ACCESS; } /* Check protocol version */ if ((op_ctx->export_perms->options & EXPORT_OPTION_NFSV4) == 0) { LogInfoAlt(COMPONENT_NFS_V4, COMPONENT_EXPORT, "NFS4 not allowed on Export_Id %d %s for client %s", op_ctx->ctx_export->export_id, op_ctx->ctx_export->pseudopath, op_ctx->client ? op_ctx->client->hostaddr_str : "unknown client"); return NFS4ERR_ACCESS; } /* Check transport type */ if (((xprt_type == XPRT_UDP) && ((op_ctx->export_perms->options & EXPORT_OPTION_UDP) == 0)) || ((xprt_type == XPRT_TCP) && ((op_ctx->export_perms->options & EXPORT_OPTION_TCP) == 0))) { LogInfoAlt(COMPONENT_NFS_V4, COMPONENT_EXPORT, "NFS4 over %s not allowed on Export_Id %d %s for client %s", xprt_type_to_str(xprt_type), op_ctx->ctx_export->export_id, op_ctx->ctx_export->pseudopath, op_ctx->client ? op_ctx->client->hostaddr_str : "unknown client"); return NFS4ERR_ACCESS; } /* Check if client is using a privileged port. */ if (((op_ctx->export_perms->options & EXPORT_OPTION_PRIVILEGED_PORT) != 0) && (port >= IPPORT_RESERVED)) { LogInfoAlt(COMPONENT_NFS_V4, COMPONENT_EXPORT, "Non-reserved Port %d is not allowed on Export_Id %d %s for client %s", port, op_ctx->ctx_export->export_id, op_ctx->ctx_export->pseudopath, op_ctx->client ? op_ctx->client->hostaddr_str : "unknown client"); return NFS4ERR_ACCESS; } /* Test if export allows the authentication provided */ if (export_check_security(req) == false) { LogInfoAlt(COMPONENT_NFS_V4, COMPONENT_EXPORT, "NFS4 auth not allowed on Export_Id %d %s for client %s", op_ctx->ctx_export->export_id, op_ctx->ctx_export->pseudopath, op_ctx->client ? op_ctx->client->hostaddr_str : "unknown client"); return NFS4ERR_WRONGSEC; } /* Get creds */ return nfs_req_creds(req); } /** * @brief Perform version independent ACCESS operation. * * This function wraps a call to fsal_access, determining the appropriate * access_mask to use to check all the requested access bits. It requests the * allowed and denied access so that it can respond for each requested access * with a single access call. * * @param[in] obj Object handle to check access for * @param[in] requested_access The ACCESS3 or ACCESS4 bits requested * @param[out] granted_access The bits granted * @param[out] supported_access The bits supported for this inode * * @return FSAL error * @retval ERR_FSAL_NO_ERROR all access was granted * @retval ERR_FSAL_ACCESS one or more access bits were denied * @retval other values indicate a FSAL failure * */ fsal_errors_t nfs_access_op(struct fsal_obj_handle *obj, uint32_t requested_access, uint32_t *granted_access, uint32_t *supported_access) { fsal_status_t fsal_status; fsal_accessflags_t access_mask; fsal_accessflags_t access_allowed; fsal_accessflags_t access_denied; uint32_t granted_mask = requested_access; access_mask = 0; *granted_access = 0; LogDebugAlt(COMPONENT_NFSPROTO, COMPONENT_NFS_V4_ACL, "Requested ACCESS=%s,%s,%s,%s,%s,%s", FSAL_TEST_MASK(requested_access, ACCESS3_READ) ? "READ" : "-", FSAL_TEST_MASK(requested_access, ACCESS3_LOOKUP) ? "LOOKUP" : "-", FSAL_TEST_MASK(requested_access, ACCESS3_MODIFY) ? "MODIFY" : "-", FSAL_TEST_MASK(requested_access, ACCESS3_EXTEND) ? "EXTEND" : "-", FSAL_TEST_MASK(requested_access, ACCESS3_DELETE) ? "DELETE" : "-", FSAL_TEST_MASK(requested_access, ACCESS3_EXECUTE) ? "EXECUTE" : "-"); /* Set mode for read. * NOTE: FSAL_ACE_PERM_LIST_DIR and FSAL_ACE_PERM_READ_DATA have * the same bit value so we don't bother looking at file type. */ if (requested_access & ACCESS3_READ) access_mask |= FSAL_R_OK | FSAL_ACE_PERM_READ_DATA; if (requested_access & ACCESS3_LOOKUP) { if (obj->type == DIRECTORY) access_mask |= FSAL_X_OK | FSAL_ACE_PERM_EXECUTE; else granted_mask &= ~ACCESS3_LOOKUP; } if (requested_access & ACCESS3_MODIFY) { if (obj->type == DIRECTORY) access_mask |= FSAL_W_OK | FSAL_ACE_PERM_DELETE_CHILD; else access_mask |= FSAL_W_OK | FSAL_ACE_PERM_WRITE_DATA; } if (requested_access & ACCESS3_EXTEND) { if (obj->type == DIRECTORY) access_mask |= FSAL_W_OK | FSAL_ACE_PERM_ADD_FILE | FSAL_ACE_PERM_ADD_SUBDIRECTORY; else access_mask |= FSAL_W_OK | FSAL_ACE_PERM_APPEND_DATA; } if (requested_access & ACCESS3_DELETE) { if (obj->type == DIRECTORY) access_mask |= FSAL_W_OK | FSAL_ACE_PERM_DELETE_CHILD; else granted_mask &= ~ACCESS3_DELETE; } if (requested_access & ACCESS3_EXECUTE) { if (obj->type != DIRECTORY) access_mask |= FSAL_X_OK | FSAL_ACE_PERM_EXECUTE; else granted_mask &= ~ACCESS3_EXECUTE; } if (access_mask != 0) access_mask |= FSAL_MODE_MASK_FLAG | FSAL_ACE4_MASK_FLAG | FSAL_ACE4_PERM_CONTINUE; LogDebugAlt(COMPONENT_NFSPROTO, COMPONENT_NFS_V4_ACL, "access_mask = mode(%c%c%c) ACL(%s,%s,%s,%s,%s)", FSAL_TEST_MASK(access_mask, FSAL_R_OK) ? 'r' : '-', FSAL_TEST_MASK(access_mask, FSAL_W_OK) ? 'w' : '-', FSAL_TEST_MASK(access_mask, FSAL_X_OK) ? 'x' : '-', FSAL_TEST_MASK(access_mask, FSAL_ACE_PERM_READ_DATA) ? obj->type == DIRECTORY ? "list_dir" : "read_data" : "-", FSAL_TEST_MASK(access_mask, FSAL_ACE_PERM_WRITE_DATA) ? obj->type == DIRECTORY ? "add_file" : "write_data" : "-", FSAL_TEST_MASK(access_mask, FSAL_ACE_PERM_EXECUTE) ? "execute" : "-", FSAL_TEST_MASK(access_mask, FSAL_ACE_PERM_ADD_SUBDIRECTORY) ? "add_subdirectory" : "-", FSAL_TEST_MASK(access_mask, FSAL_ACE_PERM_DELETE_CHILD) ? "delete_child" : "-"); fsal_status = obj->obj_ops.test_access(obj, access_mask, &access_allowed, &access_denied, false); if (fsal_status.major == ERR_FSAL_NO_ERROR || fsal_status.major == ERR_FSAL_ACCESS) { /* Define granted access based on granted mode bits. */ if (access_allowed & FSAL_R_OK) *granted_access |= ACCESS3_READ; if (access_allowed & FSAL_W_OK) *granted_access |= ACCESS3_MODIFY | ACCESS3_EXTEND | ACCESS3_DELETE; if (access_allowed & FSAL_X_OK) *granted_access |= ACCESS3_LOOKUP | ACCESS3_EXECUTE; /* Define granted access based on granted ACL bits. */ if (access_allowed & FSAL_ACE_PERM_READ_DATA) *granted_access |= ACCESS3_READ; if (obj->type == DIRECTORY) { if (access_allowed & FSAL_ACE_PERM_DELETE_CHILD) *granted_access |= ACCESS3_MODIFY | ACCESS3_DELETE; if (access_allowed & FSAL_ACE_PERM_ADD_FILE) *granted_access |= ACCESS3_EXTEND; if (access_allowed & FSAL_ACE_PERM_ADD_SUBDIRECTORY) *granted_access |= ACCESS3_EXTEND; } else { if (access_allowed & FSAL_ACE_PERM_WRITE_DATA) *granted_access |= ACCESS3_MODIFY; if (access_allowed & FSAL_ACE_PERM_APPEND_DATA) *granted_access |= ACCESS3_EXTEND; } if (access_allowed & FSAL_ACE_PERM_EXECUTE) *granted_access |= ACCESS3_LOOKUP | ACCESS3_EXECUTE; /* Don't allow any bits that weren't set on request or * allowed by the file type. */ *granted_access &= granted_mask; if (supported_access != NULL) *supported_access = granted_mask; LogDebugAlt(COMPONENT_NFSPROTO, COMPONENT_NFS_V4_ACL, "Supported ACCESS=%s,%s,%s,%s,%s,%s", FSAL_TEST_MASK(granted_mask, ACCESS3_READ) ? "READ" : "-", FSAL_TEST_MASK(granted_mask, ACCESS3_LOOKUP) ? "LOOKUP" : "-", FSAL_TEST_MASK(granted_mask, ACCESS3_MODIFY) ? "MODIFY" : "-", FSAL_TEST_MASK(granted_mask, ACCESS3_EXTEND) ? "EXTEND" : "-", FSAL_TEST_MASK(granted_mask, ACCESS3_DELETE) ? "DELETE" : "-", FSAL_TEST_MASK(granted_mask, ACCESS3_EXECUTE) ? "EXECUTE" : "-"); LogDebugAlt(COMPONENT_NFSPROTO, COMPONENT_NFS_V4_ACL, "Granted ACCESS=%s,%s,%s,%s,%s,%s", FSAL_TEST_MASK(*granted_access, ACCESS3_READ) ? "READ" : "-", FSAL_TEST_MASK(*granted_access, ACCESS3_LOOKUP) ? "LOOKUP" : "-", FSAL_TEST_MASK(*granted_access, ACCESS3_MODIFY) ? "MODIFY" : "-", FSAL_TEST_MASK(*granted_access, ACCESS3_EXTEND) ? "EXTEND" : "-", FSAL_TEST_MASK(*granted_access, ACCESS3_DELETE) ? "DELETE" : "-", FSAL_TEST_MASK(*granted_access, ACCESS3_EXECUTE) ? "EXECUTE" : "-"); } return fsal_status.major; } �����������������������������������������nfs-ganesha-2.6.0/src/support/nfs_filehandle_mgmt.c�������������������������������������������������0000664�0000000�0000000�00000045044�13242724102�0022351�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_filehandle_mgmt.c * @brief Some tools for managing the file handles * */ #include "config.h" #include "log.h" #include "nfs_core.h" #include "nfs23.h" #include "nfs4.h" #include "fsal.h" #include "nfs_exports.h" #include "nfs_file_handle.h" #include "nfs_proto_functions.h" #include "nfs_proto_tools.h" #include "nfs_convert.h" #include "export_mgr.h" #include "fsal_convert.h" #ifdef _USE_NFS3 /** * * nfs3_FhandleToCache: gets the FSAL obj from the NFSv3 file handle. * * Validates and Converts a V3 file handle and then gets the FSAL obj. * * @param fh3 [IN] pointer to the file handle to be converted * @param exp_list [IN] export fsal to use * @param status [OUT] protocol status * @param rc [OUT] operation status * * @return FSAL obj or NULL on failure * */ struct fsal_obj_handle *nfs3_FhandleToCache(nfs_fh3 *fh3, nfsstat3 *status, int *rc) { fsal_status_t fsal_status; file_handle_v3_t *v3_handle; struct fsal_export *export; struct fsal_obj_handle *obj = NULL; struct gsh_buffdesc fh_desc; /* Default behaviour */ *rc = NFS_REQ_OK; /* validate the filehandle */ *status = nfs3_Is_Fh_Invalid(fh3); if (*status != NFS3_OK) goto badhdl; /* Cast the fh as a non opaque structure */ v3_handle = (file_handle_v3_t *) (fh3->data.data_val); assert(ntohs(v3_handle->exportid) == op_ctx->ctx_export->export_id); export = op_ctx->fsal_export; /* Give the export a crack at it */ fh_desc.len = v3_handle->fs_len; fh_desc.addr = &v3_handle->fsopaque; /* adjust the wire handle opaque into a host-handle */ fsal_status = export->exp_ops.wire_to_host(export, FSAL_DIGEST_NFSV3, &fh_desc, v3_handle->fhflags1); if (!FSAL_IS_ERROR(fsal_status)) fsal_status = export->exp_ops.create_handle(export, &fh_desc, &obj, NULL); if (FSAL_IS_ERROR(fsal_status)) { *status = nfs3_Errno_status(fsal_status); if (nfs_RetryableError(fsal_status.major)) *rc = NFS_REQ_DROP; } badhdl: return obj; } #endif /* _USE_NFS3 */ /** * @brief Converts an FSAL object to an NFSv4 file handle * * @param[out] fh4 The extracted file handle * @param[in] fsalhandle The FSAL handle to be converted * * @return true if successful, false otherwise */ bool nfs4_FSALToFhandle(bool allocate, nfs_fh4 *fh4, const struct fsal_obj_handle *fsalhandle, struct gsh_export *exp) { file_handle_v4_t *file_handle; struct gsh_buffdesc fh_desc; if (allocate) { /* Allocating the filehandle in memory */ nfs4_AllocateFH(fh4); } else { /* reset the buffer to be used as handle */ fh4->nfs_fh4_len = NFS4_FHSIZE; memset(fh4->nfs_fh4_val, 0, NFS4_FHSIZE); } file_handle = (file_handle_v4_t *) fh4->nfs_fh4_val; /* Fill in the fs opaque part */ fh_desc.addr = &file_handle->fsopaque; fh_desc.len = fh4->nfs_fh4_len - offsetof(file_handle_v4_t, fsopaque); if (FSAL_IS_ERROR(fsalhandle->obj_ops.handle_to_wire(fsalhandle, FSAL_DIGEST_NFSV4, &fh_desc))) { LogDebug(COMPONENT_FILEHANDLE, "handle_to_wire FSAL_DIGEST_NFSV4 failed"); if (allocate) nfs4_freeFH(fh4); return false; } file_handle->fhversion = GANESHA_FH_VERSION; #if (BYTE_ORDER == BIG_ENDIAN) file_handle->fhflags1 = FH_FSAL_BIG_ENDIAN; #endif file_handle->fs_len = fh_desc.len; /* set the actual size */ /* keep track of the export id network byte order for nfs_fh4*/ file_handle->id.exports = htons(exp->export_id); /* Set the len */ fh4->nfs_fh4_len = nfs4_sizeof_handle(file_handle); LogFullDebug(COMPONENT_FILEHANDLE, "NFS4 Handle 0x%X export id %d", file_handle->fhflags1, ntohs(file_handle->id.exports)); LogFullDebugOpaque(COMPONENT_FILEHANDLE, "NFS4 Handle %s", LEN_FH_STR, fh4->nfs_fh4_val, fh4->nfs_fh4_len); return true; } /** * @brief Converts an FSAL object to an NFSv3 file handle * * @param[out] fh3 The extracted file handle * @param[in] fsalhandle The FSAL handle to be converted * @param[in] exp The gsh_export that this handle belongs to * * @return true if successful, false otherwise * * @todo Do we have to worry about buffer alignment and memcpy to * compensate?? */ bool nfs3_FSALToFhandle(bool allocate, nfs_fh3 *fh3, const struct fsal_obj_handle *fsalhandle, struct gsh_export *exp) { file_handle_v3_t *file_handle; struct gsh_buffdesc fh_desc; if (allocate) { /* Allocating the filehandle in memory */ nfs3_AllocateFH(fh3); } else { /* reset the buffer to be used as handle */ fh3->data.data_len = NFS3_FHSIZE; memset(fh3->data.data_val, 0, NFS3_FHSIZE); } file_handle = (file_handle_v3_t *) fh3->data.data_val; /* Fill in the fs opaque part */ fh_desc.addr = &file_handle->fsopaque; fh_desc.len = NFS3_FHSIZE - offsetof(file_handle_v3_t, fsopaque); if (FSAL_IS_ERROR(fsalhandle->obj_ops.handle_to_wire(fsalhandle, FSAL_DIGEST_NFSV3, &fh_desc))) { LogDebug(COMPONENT_FILEHANDLE, "handle_to_wire FSAL_DIGEST_NFSV3 failed"); if (allocate) nfs3_freeFH(fh3); return false; } file_handle->fhversion = GANESHA_FH_VERSION; #if (BYTE_ORDER == BIG_ENDIAN) file_handle->fhflags1 = FH_FSAL_BIG_ENDIAN; #endif file_handle->fs_len = fh_desc.len; /* set the actual size */ /* keep track of the export id in network byte order*/ file_handle->exportid = htons(exp->export_id); /* Set the len */ /* re-adjust to as built */ fh3->data.data_len = nfs3_sizeof_handle(file_handle); LogFullDebugOpaque(COMPONENT_FILEHANDLE, "NFS3 Handle %s", LEN_FH_STR, fh3->data.data_val, fh3->data.data_len); /* Check VMware NFSv3 client's 56 byte file handle size restriction */ if (nfs_param.core_param.short_file_handle && fh3->data.data_len > 56) LogWarnOnce(COMPONENT_FILEHANDLE, "Short file handle option is enabled but file handle size computed is: %d", fh3->data.data_len); return true; } /** * * nfs4_Is_Fh_DSHandle * * This routine is used to test if a fh is a DS fh * * @param fh [IN] file handle to test. * * @return true if DS fh, false otherwise * */ int nfs4_Is_Fh_DSHandle(nfs_fh4 *fh) { file_handle_v4_t *fhandle4; if (fh == NULL) return 0; fhandle4 = (file_handle_v4_t *) (fh->nfs_fh4_val); return (fhandle4->fhflags1 & FILE_HANDLE_V4_FLAG_DS) != 0; } /** * @brief Test if a filehandle is invalid * * @param[in] fh File handle to test. * * @return NFS4_OK if successfull. */ int nfs4_Is_Fh_Invalid(nfs_fh4 *fh) { file_handle_v4_t *pfile_handle; if (fh == NULL) { LogMajor(COMPONENT_FILEHANDLE, "INVALID HANDLE: fh==NULL"); return NFS4ERR_BADHANDLE; } LogFullDebugOpaque(COMPONENT_FILEHANDLE, "NFS4 Handle %s", LEN_FH_STR, fh->nfs_fh4_val, fh->nfs_fh4_len); /* Cast the fh as a non opaque structure */ pfile_handle = (file_handle_v4_t *) (fh->nfs_fh4_val); LogFullDebug(COMPONENT_FILEHANDLE, "NFS4 Handle 0x%X export id %d", pfile_handle->fhflags1, ntohs(pfile_handle->id.exports)); /* validate the filehandle */ if (pfile_handle == NULL || fh->nfs_fh4_len == 0 || pfile_handle->fhversion != GANESHA_FH_VERSION || fh->nfs_fh4_len < offsetof(struct file_handle_v4, fsopaque) || fh->nfs_fh4_len > NFS4_FHSIZE || fh->nfs_fh4_len != nfs4_sizeof_handle(pfile_handle)) { if (isInfo(COMPONENT_FILEHANDLE)) { if (pfile_handle == NULL) { LogInfo(COMPONENT_FILEHANDLE, "INVALID HANDLE: nfs_fh4_val=NULL"); } else if (fh->nfs_fh4_len == 0) { LogInfo(COMPONENT_FILEHANDLE, "INVALID HANDLE: zero length handle"); } else if (pfile_handle->fhversion != GANESHA_FH_VERSION) { LogInfo(COMPONENT_FILEHANDLE, "INVALID HANDLE: not a Ganesha handle, fhversion=%d", pfile_handle->fhversion); } else if (fh->nfs_fh4_len < offsetof(struct file_handle_v4, fsopaque)) { LogInfo(COMPONENT_FILEHANDLE, "INVALID HANDLE: data.data_len=%d is less than %d", fh->nfs_fh4_len, (int)offsetof(struct file_handle_v4, fsopaque)); } else if (fh->nfs_fh4_len > NFS4_FHSIZE) { LogInfo(COMPONENT_FILEHANDLE, "INVALID HANDLE: data.data_len=%d is greater than %d", fh->nfs_fh4_len, (int)NFS4_FHSIZE); } else if (fh->nfs_fh4_len != nfs4_sizeof_handle(pfile_handle)) { LogInfo(COMPONENT_FILEHANDLE, "INVALID HANDLE: nfs_fh4_len=%d, should be %d", fh->nfs_fh4_len, (int)nfs4_sizeof_handle(pfile_handle)); } else { LogInfo(COMPONENT_FILEHANDLE, "INVALID HANDLE: is_pseudofs=%d", ntohs(pfile_handle->id.exports) == 0); } } return NFS4ERR_BADHANDLE; /* Bad FH */ } return NFS4_OK; } /* nfs4_Is_Fh_Invalid */ /** * @brief Test if a filehandle is invalid. * * @param[in] fh3 File handle to test. * * @return NFS4_OK if successfull. * */ int nfs3_Is_Fh_Invalid(nfs_fh3 *fh3) { file_handle_v3_t *pfile_handle; if (fh3 == NULL) { LogMajor(COMPONENT_FILEHANDLE, "INVALID HANDLE: fh3==NULL"); return NFS3ERR_BADHANDLE; } LogFullDebugOpaque(COMPONENT_FILEHANDLE, "NFS3 Handle %s", LEN_FH_STR, fh3->data.data_val, fh3->data.data_len); /* Cast the fh as a non opaque structure */ pfile_handle = (file_handle_v3_t *) (fh3->data.data_val); /* validate the filehandle */ if (pfile_handle == NULL || fh3->data.data_len == 0 || pfile_handle->fhversion != GANESHA_FH_VERSION || fh3->data.data_len < offsetof(file_handle_v3_t, fsopaque) || fh3->data.data_len > NFS3_FHSIZE || fh3->data.data_len != nfs3_sizeof_handle(pfile_handle)) { if (isInfo(COMPONENT_FILEHANDLE)) { if (pfile_handle == NULL) { LogInfo(COMPONENT_FILEHANDLE, "INVALID HANDLE: data.data_val=NULL"); } else if (fh3->data.data_len == 0) { LogInfo(COMPONENT_FILEHANDLE, "INVALID HANDLE: zero length handle"); } else if (pfile_handle->fhversion != GANESHA_FH_VERSION) { LogInfo(COMPONENT_FILEHANDLE, "INVALID HANDLE: not a Ganesha handle, fhversion=%d", pfile_handle->fhversion); } else if (fh3->data.data_len < sizeof(file_handle_v3_t)) { LogInfo(COMPONENT_FILEHANDLE, "INVALID HANDLE: data.data_len=%d is less than %d", fh3->data.data_len, (int)offsetof(file_handle_v3_t, fsopaque)); } else if (fh3->data.data_len > NFS3_FHSIZE) { LogInfo(COMPONENT_FILEHANDLE, "INVALID HANDLE: data.data_len=%d is greater than %d", fh3->data.data_len, (int)NFS3_FHSIZE); } else if (fh3->data.data_len != nfs3_sizeof_handle(pfile_handle)) { LogInfo(COMPONENT_FILEHANDLE, "INVALID HANDLE: data.data_len=%d, should be %d", fh3->data.data_len, (int)nfs3_sizeof_handle(pfile_handle)); } } return NFS3ERR_BADHANDLE; /* Bad FH */ } return NFS3_OK; } /* nfs3_Is_Fh_Invalid */ /** * @brief Print an NFSv3 file handle * * @param[in] component Subsystem component ID * @param[in] fh File handle to prin */ void print_fhandle3(log_components_t component, nfs_fh3 *fh) { if (isFullDebug(component)) { char str[LEN_FH_STR]; sprint_fhandle3(str, fh); LogFullDebug(component, "%s", str); } } void sprint_fhandle3(char *str, nfs_fh3 *fh) { char *tmp = str + sprintf(str, "File Handle V3: Len=%u ", fh->data.data_len); sprint_mem(tmp, fh->data.data_val, fh->data.data_len); } /** * @brief Print an NFSv4 file handle * * @param[in] component Subsystem component ID * @param[in] fh File handle to print */ void print_fhandle4(log_components_t component, nfs_fh4 *fh) { if (isFullDebug(component)) { char str[LEN_FH_STR]; sprint_fhandle4(str, fh); LogFullDebug(component, "%s", str); } } void sprint_fhandle4(char *str, nfs_fh4 *fh) { char *tmp = str + sprintf(str, "File Handle V4: Len=%u ", fh->nfs_fh4_len); sprint_mem(tmp, fh->nfs_fh4_val, fh->nfs_fh4_len); } /** * @brief Print an NLM netobj * * @param[in] component Subsystem component ID * @param[in] fh File handle to print */ void print_fhandle_nlm(log_components_t component, netobj *fh) { if (isFullDebug(component)) { char str[LEN_FH_STR]; sprint_fhandle_nlm(str, fh); LogFullDebug(component, "%s", str); } } /* print_fhandle_nlm */ void sprint_fhandle_nlm(char *str, netobj *fh) { char *tmp = str + sprintf(str, "File Handle V3: Len=%u ", fh->n_len); sprint_mem(tmp, fh->n_bytes, fh->n_len); } /* sprint_fhandle_nlm */ /** * @brief Print the content of a buffer. * * @param[in] component Logging subsystem ID * @param[in] buff Buffer to print. * @param[in] len Length of the buffer. */ void print_buff(log_components_t component, char *buff, int len) { if (isFullDebug(component)) { char str[1024]; sprint_buff(str, buff, len); LogFullDebug(component, "%s", str); } } /* print_buff */ void sprint_buff(char *str, char *buff, int len) { char *tmp = str + sprintf(str, " Len=%u Buff=%p Val: ", len, buff); sprint_mem(tmp, buff, len); } /* sprint_buff */ void sprint_mem(char *str, char *buff, int len) { int i; if (buff == NULL) sprintf(str, "<null>"); else for (i = 0; i < len; i++) sprintf(str + i * 2, "%02x", (unsigned char)buff[i]); } /** * @brief Convert a file handle to a string representation * * @param rq_vers [IN] version of the NFS protocol to be used * @param fh3 [IN] NFSv3 file handle or NULL * @param fh4 [IN] NFSv4 file handle or NULL * @param str [OUT] string version of handle * */ void nfs_FhandleToStr(u_long rq_vers, nfs_fh3 *fh3, nfs_fh4 *fh4, char *str) { switch (rq_vers) { case NFS_V4: sprint_fhandle4(str, fh4); break; case NFS_V3: sprint_fhandle3(str, fh3); break; } } /* nfs_FhandleToStr */ /** * * print_compound_fh * * This routine prints all the file handle within a compoud request's data * structure. * * @param data [IN] compound's data to manage. */ void LogCompoundFH(compound_data_t *data) { if (isFullDebug(COMPONENT_FILEHANDLE)) { char str[LEN_FH_STR]; sprint_fhandle4(str, &data->currentFH); LogFullDebug(COMPONENT_FILEHANDLE, "Current FH %s", str); sprint_fhandle4(str, &data->savedFH); LogFullDebug(COMPONENT_FILEHANDLE, "Saved FH %s", str); } } /** * @brief Do basic checks on the CurrentFH * * This function performs basic checks to make sure the supplied * filehandle is sane for a given operation. * * @param data [IN] Compound_data_t for the operation to check * @param required_type [IN] The file type this operation requires. * Set to 0 to allow any type. * @param ds_allowed [IN] true if DS handles are allowed. * * @return NFSv4.1 status codes */ nfsstat4 nfs4_sanity_check_FH(compound_data_t *data, object_file_type_t required_type, bool ds_allowed) { int fh_status; /* If there is no FH */ fh_status = nfs4_Is_Fh_Empty(&data->currentFH); if (fh_status != NFS4_OK) return fh_status; assert(data->current_obj != NULL && data->current_filetype != NO_FILE_TYPE); /* If the filehandle is invalid */ fh_status = nfs4_Is_Fh_Invalid(&data->currentFH); if (fh_status != NFS4_OK) return fh_status; /* Check for the correct file type */ if (required_type != NO_FILE_TYPE && data->current_filetype != required_type) { LogDebug(COMPONENT_NFS_V4, "Wrong file type expected %s actual %s", object_file_type_to_str(required_type), object_file_type_to_str(data->current_filetype)); if (required_type == DIRECTORY) { if (data->current_filetype == SYMBOLIC_LINK) return NFS4ERR_SYMLINK; else return NFS4ERR_NOTDIR; } else if (required_type == SYMBOLIC_LINK) return NFS4ERR_INVAL; switch (data->current_filetype) { case DIRECTORY: return NFS4ERR_ISDIR; default: return NFS4ERR_INVAL; } } if (nfs4_Is_Fh_DSHandle(&data->currentFH) && !ds_allowed) { LogDebug(COMPONENT_NFS_V4, "DS Handle"); return NFS4ERR_INVAL; } return NFS4_OK; } /* nfs4_sanity_check_FH */ /** * @brief Do basic checks on the SavedFH * * This function performs basic checks to make sure the supplied * filehandle is sane for a given operation. * * @param data [IN] Compound_data_t for the operation to check * @param required_type [IN] The file type this operation requires. * Set to 0 to allow any type. A negative value * indicates any type BUT that type is allowed. * @param ds_allowed [IN] true if DS handles are allowed. * * @return NFSv4.1 status codes */ nfsstat4 nfs4_sanity_check_saved_FH(compound_data_t *data, int required_type, bool ds_allowed) { int fh_status; /* If there is no FH */ fh_status = nfs4_Is_Fh_Empty(&data->savedFH); if (fh_status != NFS4_OK) return fh_status; /* If the filehandle is invalid */ fh_status = nfs4_Is_Fh_Invalid(&data->savedFH); if (fh_status != NFS4_OK) return fh_status; if (nfs4_Is_Fh_DSHandle(&data->savedFH) && !ds_allowed) { LogDebug(COMPONENT_NFS_V4, "DS Handle"); return NFS4ERR_INVAL; } /* Check for the correct file type */ if (required_type < 0) { if (-required_type == data->saved_filetype) { LogDebug(COMPONENT_NFS_V4, "Wrong file type expected not to be %s was %s", object_file_type_to_str((object_file_type_t) -required_type), object_file_type_to_str( data->current_filetype)); if (-required_type == DIRECTORY) { return NFS4ERR_ISDIR; return NFS4ERR_INVAL; } } } else if (required_type != NO_FILE_TYPE && data->saved_filetype != required_type) { LogDebug(COMPONENT_NFS_V4, "Wrong file type expected %s was %s", object_file_type_to_str((object_file_type_t) required_type), object_file_type_to_str(data->current_filetype)); if (required_type == DIRECTORY) { if (data->current_filetype == SYMBOLIC_LINK) return NFS4ERR_SYMLINK; else return NFS4ERR_NOTDIR; } else if (required_type == SYMBOLIC_LINK) return NFS4ERR_INVAL; switch (data->saved_filetype) { case DIRECTORY: return NFS4ERR_ISDIR; default: return NFS4ERR_INVAL; } } return NFS4_OK; } /* nfs4_sanity_check_saved_FH */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/support/nfs_ip_name.c���������������������������������������������������������0000664�0000000�0000000�00000024673�13242724102�0020647�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_ip_name.c * @brief The management of the IP/name cache. */ #include "config.h" #include "hashtable.h" #include "log.h" #include "nfs_core.h" #include "nfs_exports.h" #include "nfs_ip_stats.h" #include "config_parsing.h" #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> /* Hashtable used to cache the hostname, accessed by their IP addess */ hash_table_t *ht_ip_name; unsigned int expiration_time; /** * @name Compute the hash value for the entry in IP/name cache * * @param[in] hparam Hash table parameter. * @param[in] buffcleff The hash key buffer * * @return the computed hash value. * * @see hashtable_init * */ uint32_t ip_name_value_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *buffclef) { return hash_sockaddr(buffclef->addr, true) % hparam->index_size; } /** * @brief Compute the rbt value for the entry in IP/name cache * * @param[in] hparam Hash table parameter * @param[in] buffclef Hash key buffer * * @return the computed rbt value. * * @see hashtable_init * */ uint64_t ip_name_rbt_hash_func(hash_parameter_t *hparam, struct gsh_buffdesc *buffclef) { return hash_sockaddr(buffclef->addr, true); } /** * * compare_ip_name: compares the ip address stored in the key buffers. * * compare the ip address stored in the key buffers. This function is to be used * as 'compare_key' field in the hashtable storing the nfs duplicated requests. * * @param buff1 [IN] first key * @param buff2 [IN] second key * * @return 0 if keys are identifical, 1 if they are different. * */ int compare_ip_name(struct gsh_buffdesc *buff1, struct gsh_buffdesc *buff2) { return (cmp_sockaddr(buff1->addr, buff2->addr, true) != 0) ? 0 : 1; } /** * @brief Display the ip_name stored in the buffer * * @param[in] pbuff Buffer to display * @param[out] str Output string * * @return number of character written. */ int display_ip_name_key(struct gsh_buffdesc *pbuff, char *str) { sockaddr_t *addr = (sockaddr_t *) (pbuff->addr); sprint_sockip(addr, str, HASHTABLE_DISPLAY_STRLEN); return strlen(str); } /** * @brief Display the ip_name stored in the buffer * * @param[in] pbuff Buffer to display * @param[out] str Output string * * @return number of character written */ int display_ip_name_val(struct gsh_buffdesc *pbuff, char *str) { nfs_ip_name_t *nfs_ip_name = (pbuff->addr); return snprintf(str, HASHTABLE_DISPLAY_STRLEN, "%s", nfs_ip_name->hostname); } /** * * nfs_ip_name_add: adds an entry into IP/name cache. * * Adds an entry in the duplicate requests cache. * * @param ipaddr[IN] the ipaddr to be used as key * @param hostname[OUT] the hostname added (found by using getnameinfo) * * @return IP_NAME_SUCCESS if successful * @return IP_NAME_INSERT_MALLOC_ERROR if an error occurred during the insertion * process * @return IP_NAME_NETDB_ERROR if an error occurred during the netdb query (via * gethostbyaddr). * */ int nfs_ip_name_add(sockaddr_t *ipaddr, char *hostname, size_t size) { struct gsh_buffdesc buffkey; struct gsh_buffdesc buffdata; nfs_ip_name_t *nfs_ip_name = NULL; sockaddr_t *pipaddr = NULL; struct timeval tv0, tv1, dur; int rc; char ipstring[SOCK_NAME_MAX + 1]; nfs_ip_name = gsh_malloc(sizeof(nfs_ip_name_t)); pipaddr = gsh_malloc(sizeof(sockaddr_t)); /* I have to keep an integer as key, I wil use the pointer buffkey->addr * for this, this also means that buffkey->len will be 0 */ memcpy(pipaddr, ipaddr, sizeof(sockaddr_t)); buffkey.addr = (caddr_t) pipaddr; buffkey.len = sizeof(sockaddr_t); gettimeofday(&tv0, NULL); rc = getnameinfo((struct sockaddr *)pipaddr, sizeof(sockaddr_t), nfs_ip_name->hostname, sizeof(nfs_ip_name->hostname), NULL, 0, 0); gettimeofday(&tv1, NULL); timersub(&tv1, &tv0, &dur); sprint_sockip(pipaddr, ipstring, sizeof(ipstring)); /* display warning if DNS resolution took more that 1.0s */ if (dur.tv_sec >= 1) { LogEvent(COMPONENT_DISPATCH, "Warning: long DNS query for %s: %u.%06u sec", ipstring, (unsigned int)dur.tv_sec, (unsigned int)dur.tv_usec); } /* Ask for the name to be cached */ if (rc != 0) { strmaxcpy(nfs_ip_name->hostname, ipstring, sizeof(nfs_ip_name->hostname)); LogEvent(COMPONENT_DISPATCH, "Cannot resolve address %s, error %s, using %s as hostname", ipstring, gai_strerror(rc), nfs_ip_name->hostname); } LogDebug(COMPONENT_DISPATCH, "Inserting %s->%s to addr cache", ipstring, nfs_ip_name->hostname); /* I build the data with the request pointer * that should be in state 'IN USE' */ nfs_ip_name->timestamp = time(NULL); buffdata.addr = (caddr_t) nfs_ip_name; buffdata.len = sizeof(nfs_ip_name_t); if (HashTable_Set(ht_ip_name, &buffkey, &buffdata) != HASHTABLE_SUCCESS) return IP_NAME_INSERT_MALLOC_ERROR; /* Copy the value for the caller */ strmaxcpy(hostname, nfs_ip_name->hostname, size); return IP_NAME_SUCCESS; } /* nfs_ip_name_add */ /** * * nfs_ip_name_get: Tries to get an entry for ip_name cache. * * Tries to get an entry for ip_name cache. * * @param ipaddr [IN] the ip address requested * @param hostname [OUT] the hostname * * @return the result previously set if *pstatus == IP_NAME_SUCCESS * */ int nfs_ip_name_get(sockaddr_t *ipaddr, char *hostname, size_t size) { struct gsh_buffdesc buffkey; struct gsh_buffdesc buffval; nfs_ip_name_t *nfs_ip_name; char ipstring[SOCK_NAME_MAX + 1]; sprint_sockip(ipaddr, ipstring, sizeof(ipstring)); buffkey.addr = (caddr_t) ipaddr; buffkey.len = sizeof(sockaddr_t); if (HashTable_Get(ht_ip_name, &buffkey, &buffval) == HASHTABLE_SUCCESS) { nfs_ip_name = buffval.addr; strmaxcpy(hostname, nfs_ip_name->hostname, size); LogFullDebug(COMPONENT_DISPATCH, "Cache get hit for %s->%s", ipstring, nfs_ip_name->hostname); return IP_NAME_SUCCESS; } LogFullDebug(COMPONENT_DISPATCH, "Cache get miss for %s", ipstring); return IP_NAME_NOT_FOUND; } /* nfs_ip_name_get */ /** * * nfs_ip_name_remove: Tries to remove an entry for ip_name cache * * Tries to remove an entry for ip_name cache. * * @param ipaddr [IN] the ip address to be uncached. * * @return the result previously set if *pstatus == IP_NAME_SUCCESS * */ int nfs_ip_name_remove(sockaddr_t *ipaddr) { struct gsh_buffdesc buffkey, old_value; nfs_ip_name_t *nfs_ip_name = NULL; char ipstring[SOCK_NAME_MAX + 1]; sprint_sockip(ipaddr, ipstring, sizeof(ipstring)); buffkey.addr = (caddr_t) ipaddr; buffkey.len = sizeof(sockaddr_t); if (HashTable_Del(ht_ip_name, &buffkey, NULL, &old_value) == HASHTABLE_SUCCESS) { nfs_ip_name = (nfs_ip_name_t *) old_value.addr; LogFullDebug(COMPONENT_DISPATCH, "Cache remove hit for %s->%s", ipstring, nfs_ip_name->hostname); gsh_free(nfs_ip_name); return IP_NAME_SUCCESS; } LogFullDebug(COMPONENT_DISPATCH, "Cache remove miss for %s", ipstring); return IP_NAME_NOT_FOUND; } /* nfs_ip_name_remove */ /** * @defgroup config_ipnamemap Structure and defaults for NFS_IP_Name * * @{ */ /** * @brief Default index size for IP-Name hash */ #define PRIME_IP_NAME 17 /** * @brief Default value for ip_name_param.expiration-time */ #define IP_NAME_EXPIRATION 3600 /** @} */ /** * @brief NFS_IP_Name configuration stanza */ struct ip_name_cache { /** Configuration for hash table for NFS Name/IP map. Defautl index size is PRIME_IP_NAME, settable with Index_Size. */ hash_parameter_t hash_param; /** Expiration time for ip-name mappings. Defautls to IP_NAME_Expiration, and settable with Expiration_Time. */ uint32_t expiration_time; }; static struct ip_name_cache ip_name_cache = { .hash_param.hash_func_key = ip_name_value_hash_func, .hash_param.hash_func_rbt = ip_name_rbt_hash_func, .hash_param.compare_key = compare_ip_name, .hash_param.key_to_str = display_ip_name_key, .hash_param.val_to_str = display_ip_name_val, .hash_param.flags = HT_FLAG_NONE, }; /** * @brief IP name cache parameters */ static struct config_item ip_name_params[] = { CONF_ITEM_UI32("Index_Size", 1, 51, PRIME_IP_NAME, ip_name_cache, hash_param.index_size), CONF_ITEM_UI32("Expiration_Time", 1, 60*60*24, IP_NAME_EXPIRATION, ip_name_cache, expiration_time), CONFIG_EOL }; static void *ip_name_init(void *link_mem, void *self_struct) { if (self_struct == NULL) return &ip_name_cache; else return NULL; } static int ip_name_commit(void *node, void *link_mem, void *self_struct, struct config_error_type *err_type) { struct ip_name_cache *params = self_struct; if (!is_prime(params->hash_param.index_size)) { LogCrit(COMPONENT_CONFIG, "IP name cache index size must be a prime."); return 1; } return 0; } struct config_block nfs_ip_name = { .dbus_interface_name = "org.ganesha.nfsd.config.ip_name", .blk_desc.name = "NFS_IP_Name", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = ip_name_init, .blk_desc.u.blk.params = ip_name_params, .blk_desc.u.blk.commit = ip_name_commit }; /** * * nfs_Init_ip_name: Init the hashtable for IP/name cache. * * Perform all the required initialization for hashtable IP/name cache * * @return 0 if successful, -1 otherwise * */ int nfs_Init_ip_name(void) { ht_ip_name = hashtable_init(&ip_name_cache.hash_param); if (ht_ip_name == NULL) { LogCrit(COMPONENT_INIT, "NFS IP_NAME: Cannot init IP/name cache"); return -1; } /* Set the expiration time */ expiration_time = ip_name_cache.expiration_time; return IP_NAME_SUCCESS; } /* nfs_Init_ip_name */ ���������������������������������������������������������������������nfs-ganesha-2.6.0/src/support/nfs_read_conf.c�������������������������������������������������������0000775�0000000�0000000�00000025422�13242724102�0021153�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @file nfs_read_conf.c * @brief This file tables required for parsing the NFS specific parameters. */ #include "config.h" #include <stdio.h> #include <string.h> #include <pthread.h> #include <fcntl.h> #include <sys/file.h> /* for having FNDELAY */ #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <ctype.h> #include "log.h" #include "gsh_rpc.h" #include "fsal.h" #include "nfs23.h" #include "nfs4.h" #include "mount.h" #include "nfs_core.h" #include "nfs_file_handle.h" #include "nfs_exports.h" #include "nfs_proto_functions.h" #include "nfs_dupreq.h" #include "config_parsing.h" /** * @brief Core configuration parameters */ static struct config_item_list protocols[] = { CONFIG_LIST_TOK("3", CORE_OPTION_NFSV3), CONFIG_LIST_TOK("v3", CORE_OPTION_NFSV3), CONFIG_LIST_TOK("nfs3", CORE_OPTION_NFSV3), CONFIG_LIST_TOK("nfsv3", CORE_OPTION_NFSV3), CONFIG_LIST_TOK("4", CORE_OPTION_NFSV4), CONFIG_LIST_TOK("v4", CORE_OPTION_NFSV4), CONFIG_LIST_TOK("nfs4", CORE_OPTION_NFSV4), CONFIG_LIST_TOK("nfsv4", CORE_OPTION_NFSV4), CONFIG_LIST_TOK("nfsvsock", CORE_OPTION_NFS_VSOCK), CONFIG_LIST_TOK("nfsrdma", CORE_OPTION_NFS_RDMA), CONFIG_LIST_TOK("rpcrdma", CORE_OPTION_NFS_RDMA), CONFIG_LIST_TOK("9p", CORE_OPTION_9P), CONFIG_LIST_EOL }; static struct config_item core_params[] = { CONF_ITEM_UI16("NFS_Port", 0, UINT16_MAX, NFS_PORT, nfs_core_param, port[P_NFS]), CONF_ITEM_UI16("MNT_Port", 0, UINT16_MAX, 0, nfs_core_param, port[P_MNT]), CONF_ITEM_UI16("NLM_Port", 0, UINT16_MAX, 0, nfs_core_param, port[P_NLM]), CONF_ITEM_UI16("Rquota_Port", 0, UINT16_MAX, RQUOTA_PORT, nfs_core_param, port[P_RQUOTA]), CONF_ITEM_IP_ADDR("Bind_Addr", "0.0.0.0", nfs_core_param, bind_addr), CONF_ITEM_UI32("NFS_Program", 1, INT32_MAX, NFS_PROGRAM, nfs_core_param, program[P_NFS]), CONF_ITEM_UI32("MNT_Program", 1, INT32_MAX, MOUNTPROG, nfs_core_param, program[P_MNT]), CONF_ITEM_UI32("NLM_Program", 1, INT32_MAX, NLMPROG, nfs_core_param, program[P_NLM]), CONF_ITEM_UI32("Rquota_Program", 1, INT32_MAX, RQUOTAPROG, nfs_core_param, program[P_RQUOTA]), CONF_ITEM_UI32("Nb_Worker", 1, 1024*128, NB_WORKER_THREAD_DEFAULT, nfs_core_param, nb_worker), CONF_ITEM_BOOL("Drop_IO_Errors", false, nfs_core_param, drop_io_errors), CONF_ITEM_BOOL("Drop_Inval_Errors", false, nfs_core_param, drop_inval_errors), CONF_ITEM_BOOL("Drop_Delay_Errors", false, nfs_core_param, drop_delay_errors), CONF_ITEM_BOOL("DRC_Disabled", false, nfs_core_param, drc.disabled), CONF_ITEM_UI32("DRC_TCP_Npart", 1, 20, DRC_TCP_NPART, nfs_core_param, drc.tcp.npart), CONF_ITEM_UI32("DRC_TCP_Size", 1, 32767, DRC_TCP_SIZE, nfs_core_param, drc.tcp.size), CONF_ITEM_UI32("DRC_TCP_Cachesz", 1, 255, DRC_TCP_CACHESZ, nfs_core_param, drc.tcp.cachesz), CONF_ITEM_UI32("DRC_TCP_Hiwat", 1, 256, DRC_TCP_HIWAT, nfs_core_param, drc.tcp.hiwat), CONF_ITEM_UI32("DRC_TCP_Recycle_Npart", 1, 20, DRC_TCP_RECYCLE_NPART, nfs_core_param, drc.tcp.recycle_npart), CONF_ITEM_UI32("DRC_TCP_Recycle_Expire_S", 0, 60*60, 600, nfs_core_param, drc.tcp.recycle_expire_s), CONF_ITEM_BOOL("DRC_TCP_Checksum", DRC_TCP_CHECKSUM, nfs_core_param, drc.tcp.checksum), CONF_ITEM_UI32("DRC_UDP_Npart", 1, 100, DRC_UDP_NPART, nfs_core_param, drc.udp.npart), CONF_ITEM_UI32("DRC_UDP_Size", 512, 32768, DRC_UDP_SIZE, nfs_core_param, drc.udp.size), CONF_ITEM_UI32("DRC_UDP_Cachesz", 1, 2047, DRC_UDP_CACHESZ, nfs_core_param, drc.udp.cachesz), CONF_ITEM_UI32("DRC_UDP_Hiwat", 1, 32768, DRC_UDP_HIWAT, nfs_core_param, drc.udp.hiwat), CONF_ITEM_BOOL("DRC_UDP_Checksum", DRC_UDP_CHECKSUM, nfs_core_param, drc.udp.checksum), CONF_ITEM_UI32("RPC_Max_Connections", 1, 10000, 1024, nfs_core_param, rpc.max_connections), CONF_ITEM_UI32("RPC_Idle_Timeout_S", 0, 60*60, 300, nfs_core_param, rpc.idle_timeout_s), CONF_ITEM_UI32("MaxRPCSendBufferSize", 1, 1048576*9, NFS_DEFAULT_SEND_BUFFER_SIZE, nfs_core_param, rpc.max_send_buffer_size), CONF_ITEM_UI32("MaxRPCRecvBufferSize", 1, 1048576*9, NFS_DEFAULT_RECV_BUFFER_SIZE, nfs_core_param, rpc.max_recv_buffer_size), CONF_ITEM_UI32("RPC_Ioq_ThrdMax", 1, 1024*128, 200, nfs_core_param, rpc.ioq_thrd_max), CONF_ITEM_UI32("RPC_GSS_Npart", 1, 1021, 13, nfs_core_param, rpc.gss.ctx_hash_partitions), CONF_ITEM_UI32("RPC_GSS_Max_Ctx", 1, 1024*1024, 16384, nfs_core_param, rpc.gss.max_ctx), CONF_ITEM_UI32("RPC_GSS_Max_GC", 1, 1024*1024, 200, nfs_core_param, rpc.gss.max_gc), CONF_ITEM_I64("Blocked_Lock_Poller_Interval", 0, 180, 10, nfs_core_param, blocked_lock_poller_interval), CONF_ITEM_LIST("NFS_Protocols", CORE_OPTION_ALL_VERS, protocols, nfs_core_param, core_options), CONF_ITEM_LIST("Protocols", CORE_OPTION_ALL_VERS, protocols, nfs_core_param, core_options), CONF_ITEM_BOOL("NSM_Use_Caller_Name", false, nfs_core_param, nsm_use_caller_name), CONF_ITEM_BOOL("Clustered", true, nfs_core_param, clustered), CONF_ITEM_BOOL("Enable_NLM", true, nfs_core_param, enable_NLM), CONF_ITEM_BOOL("Enable_RQUOTA", true, nfs_core_param, enable_RQUOTA), CONF_ITEM_BOOL("Enable_TCP_keepalive", true, nfs_core_param, enable_tcp_keepalive), CONF_ITEM_UI32("TCP_KEEPCNT", 0, 255, 0, nfs_core_param, tcp_keepcnt), CONF_ITEM_UI32("TCP_KEEPIDLE", 0, 65535, 0, nfs_core_param, tcp_keepidle), CONF_ITEM_UI32("TCP_KEEPINTVL", 0, 65535, 0, nfs_core_param, tcp_keepintvl), CONF_ITEM_BOOL("Enable_NFS_Stats", true, nfs_core_param, enable_NFSSTATS), CONF_ITEM_BOOL("Enable_Fast_Stats", false, nfs_core_param, enable_FASTSTATS), CONF_ITEM_BOOL("Enable_FSAL_Stats", false, nfs_core_param, enable_FSALSTATS), CONF_ITEM_BOOL("Short_File_Handle", false, nfs_core_param, short_file_handle), CONF_ITEM_I64("Manage_Gids_Expiration", 0, 7*24*60*60, 30*60, nfs_core_param, manage_gids_expiration), CONF_ITEM_PATH("Plugins_Dir", 1, MAXPATHLEN, FSAL_MODULE_LOC, nfs_core_param, ganesha_modules_loc), CONF_ITEM_UI32("heartbeat_freq", 0, 5000, 1000, nfs_core_param, heartbeat_freq), CONF_ITEM_BOOL("fsid_device", false, nfs_core_param, fsid_device), CONF_ITEM_BOOL("mount_path_pseudo", false, nfs_core_param, mount_path_pseudo), CONF_ITEM_STR("Dbus_Name_Prefix", 1, 255, NULL, nfs_core_param, dbus_name_prefix), CONFIG_EOL }; struct config_block nfs_core = { .dbus_interface_name = "org.ganesha.nfsd.config.core", .blk_desc.name = "NFS_Core_Param", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = noop_conf_init, .blk_desc.u.blk.params = core_params, .blk_desc.u.blk.commit = noop_conf_commit }; /** * @brief Kerberos/GSSAPI parameters */ #ifdef _HAVE_GSSAPI static struct config_item krb5_params[] = { CONF_ITEM_STR("PrincipalName", 1, MAXPATHLEN, DEFAULT_NFS_PRINCIPAL, nfs_krb5_param, svc.principal), CONF_ITEM_PATH("KeytabPath", 1, MAXPATHLEN, DEFAULT_NFS_KEYTAB, nfs_krb5_param, keytab), CONF_ITEM_PATH("CCacheDir", 1, MAXPATHLEN, DEFAULT_NFS_CCACHE_DIR, nfs_krb5_param, ccache_dir), CONF_ITEM_BOOL("Active_krb5", true, nfs_krb5_param, active_krb5), CONFIG_EOL }; struct config_block krb5_param = { .dbus_interface_name = "org.ganesha.nfsd.config.krb5", .blk_desc.name = "NFS_KRB5", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = noop_conf_init, .blk_desc.u.blk.params = krb5_params, .blk_desc.u.blk.commit = noop_conf_commit }; #endif #ifdef USE_NFSIDMAP #define GETPWNAMDEF false #else #define GETPWNAMDEF true #endif /** * @brief NFSv4 specific parameters */ static struct config_item_list minor_versions[] = { CONFIG_LIST_TOK("0", NFSV4_MINOR_VERSION_ZERO), CONFIG_LIST_TOK("1", NFSV4_MINOR_VERSION_ONE), CONFIG_LIST_TOK("2", NFSV4_MINOR_VERSION_TWO), CONFIG_LIST_EOL }; static struct config_item version4_params[] = { CONF_ITEM_BOOL("Graceless", false, nfs_version4_parameter, graceless), CONF_ITEM_UI32("Lease_Lifetime", 0, 120, LEASE_LIFETIME_DEFAULT, nfs_version4_parameter, lease_lifetime), CONF_ITEM_UI32("Grace_Period", 0, 180, GRACE_PERIOD_DEFAULT, nfs_version4_parameter, grace_period), CONF_ITEM_STR("DomainName", 1, MAXPATHLEN, DOMAINNAME_DEFAULT, nfs_version4_parameter, domainname), CONF_ITEM_PATH("IdmapConf", 1, MAXPATHLEN, IDMAPCONF_DEFAULT, nfs_version4_parameter, idmapconf), CONF_ITEM_BOOL("UseGetpwnam", GETPWNAMDEF, nfs_version4_parameter, use_getpwnam), CONF_ITEM_BOOL("Allow_Numeric_Owners", true, nfs_version4_parameter, allow_numeric_owners), CONF_ITEM_BOOL("Only_Numeric_Owners", false, nfs_version4_parameter, only_numeric_owners), CONF_ITEM_BOOL("Delegations", false, nfs_version4_parameter, allow_delegations), CONF_ITEM_UI32("Deleg_Recall_Retry_Delay", 0, 10, DELEG_RECALL_RETRY_DELAY_DEFAULT, nfs_version4_parameter, deleg_recall_retry_delay), CONF_ITEM_BOOL("PNFS_MDS", true, nfs_version4_parameter, pnfs_mds), CONF_ITEM_BOOL("PNFS_DS", true, nfs_version4_parameter, pnfs_ds), CONF_ITEM_STR("RecoveryBackend", 1, MAXPATHLEN, RECOVERY_BACKEND_DEFAULT, nfs_version4_parameter, recovery_backend), CONF_ITEM_LIST("minor_versions", NFSV4_MINOR_VERSION_ALL, minor_versions, nfs_version4_parameter, minor_versions), CONF_ITEM_UI32("slot_table_size", 1, 1024, NFS41_NB_SLOTS_DEF, nfs_version4_parameter, nb_slots), CONFIG_EOL }; struct config_block version4_param = { .dbus_interface_name = "org.ganesha.nfsd.config.nfsv4", .blk_desc.name = "NFSv4", .blk_desc.type = CONFIG_BLOCK, .blk_desc.u.blk.init = noop_conf_init, .blk_desc.u.blk.params = version4_params, .blk_desc.u.blk.commit = noop_conf_commit }; ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/support/server_stats.c��������������������������������������������������������0000664�0000000�0000000�00000167425�13242724102�0021120�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * vim:noexpandtab:shiftwidth=8:tabstop=8: * * Copyright (C) Panasas Inc., 2013 * Author: Jim Lieb jlieb@panasas.com * * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * ------------- */ /** * @defgroup Server statistics management * @{ */ /** * @file server_stats.c * @author Jim Lieb <jlieb@panasas.com> * @brief FSAL module manager */ #include "config.h" #include <time.h> #include <unistd.h> #include <sys/types.h> #include <stdint.h> #include <sys/param.h> #include <pthread.h> #include <assert.h> #include <arpa/inet.h> #include "fsal.h" #include "nfs_core.h" #include "log.h" #include "avltree.h" #include "gsh_types.h" #ifdef USE_DBUS #include "gsh_dbus.h" #endif #include "client_mgr.h" #include "export_mgr.h" #include "server_stats.h" #include <abstract_atomic.h> #include "nfs_proto_functions.h" #define NFS_V3_NB_COMMAND (NFSPROC3_COMMIT + 1) #define NFS_V4_NB_COMMAND 2 #define MNT_V1_NB_COMMAND (MOUNTPROC3_EXPORT + 1) #define MNT_V3_NB_COMMAND (MOUNTPROC3_EXPORT + 1) #define NLM_V4_NB_OPERATION (NLMPROC4_FREE_ALL + 1) #define RQUOTA_NB_COMMAND (RQUOTAPROC_SETACTIVEQUOTA + 1) #define NFS_V40_NB_OPERATION (NFS4_OP_RELEASE_LOCKOWNER + 1) #define NFS_V41_NB_OPERATION (NFS4_OP_RECLAIM_COMPLETE + 1) #define NFS_V42_NB_OPERATION (NFS4_OP_WRITE_SAME + 1) #define _9P_NB_COMMAND 33 #define NFS_pcp nfs_param.core_param #define NFS_program NFS_pcp.program #ifdef USE_DBUS struct op_name { char *name; }; static const struct op_name optqta[] = { [RQUOTAPROC_GETQUOTA] = {.name = "GETQUOTA", }, [RQUOTAPROC_GETACTIVEQUOTA] = {.name = "GETACTIVEQUOTA", }, [RQUOTAPROC_SETQUOTA] = {.name = "SETQUOTA", }, [RQUOTAPROC_SETACTIVEQUOTA] = {.name = "SETACTIVEQUOTA", }, }; static const struct op_name optmnt[] = { [MOUNTPROC3_NULL] = {.name = "NULL", }, [MOUNTPROC3_MNT] = {.name = "MNT", }, [MOUNTPROC3_DUMP] = {.name = "DUMP", }, [MOUNTPROC3_UMNT] = {.name = "UMNT", }, [MOUNTPROC3_UMNTALL] = {.name = "UMNTALL", }, [MOUNTPROC3_EXPORT] = {.name = "EXPORT", }, }; static const struct op_name optnlm[] = { [NLMPROC4_NULL] = {.name = "NULL", }, [NLMPROC4_TEST] = {.name = "TEST", }, [NLMPROC4_LOCK] = {.name = "LOCK", }, [NLMPROC4_CANCEL] = {.name = "CANCEL", }, [NLMPROC4_UNLOCK] = {.name = "UNLOCK", }, [NLMPROC4_GRANTED] = {.name = "GRANTED", }, [NLMPROC4_TEST_MSG] = {.name = "TEST_MSG", }, [NLMPROC4_LOCK_MSG] = {.name = "LOCK_MSG", }, [NLMPROC4_CANCEL_MSG] = {.name = "CANCEL_MSG", }, [NLMPROC4_UNLOCK_MSG] = {.name = "UNLOCK_MSG", }, [NLMPROC4_GRANTED_MSG] = {.name = "GRANTED_MSG", }, [NLMPROC4_TEST_RES] = {.name = "TEST_RES ", }, [NLMPROC4_LOCK_RES] = {.name = "LOCK_RES", }, [NLMPROC4_CANCEL_RES] = {.name = "CANCEL_RES", }, [NLMPROC4_UNLOCK_RES] = {.name = "UNLOCK_RES", }, [NLMPROC4_GRANTED_RES] = {.name = "GRANTED_RES", }, [NLMPROC4_SM_NOTIFY] = {.name = "SM_NOTIFY", }, [NLMPROC4_SHARE] = {.name = "SHARE", }, [NLMPROC4_UNSHARE] = {.name = "UNSHARE", }, [NLMPROC4_NM_LOCK] = {.name = "NM_LOCK", }, [NLMPROC4_FREE_ALL] = {.name = "FREE_ALL", }, }; static const struct op_name optabv3[] = { [NFSPROC3_NULL] = {.name = "NULL", }, [NFSPROC3_GETATTR] = {.name = "GETATTR", }, [NFSPROC3_SETATTR] = {.name = "SETATTR", }, [NFSPROC3_LOOKUP] = {.name = "LOOKUP", }, [NFSPROC3_ACCESS] = {.name = "ACCESS", }, [NFSPROC3_READLINK] = {.name = "READLINK", }, [NFSPROC3_READ] = {.name = "READ", }, [NFSPROC3_WRITE] = {.name = "WRITE", }, [NFSPROC3_CREATE] = {.name = "CREATE", }, [NFSPROC3_MKDIR] = {.name = "MKDIR", }, [NFSPROC3_SYMLINK] = {.name = "SYMLINK", }, [NFSPROC3_MKNOD] = {.name = "MKNOD", }, [NFSPROC3_REMOVE] = {.name = "REMOVE", }, [NFSPROC3_RMDIR] = {.name = "RMDIR", }, [NFSPROC3_RENAME] = {.name = "RENAME", }, [NFSPROC3_LINK] = {.name = "LINK", }, [NFSPROC3_READDIR] = {.name = "READDIR", }, [NFSPROC3_READDIRPLUS] = {.name = "READDIRPLUS", }, [NFSPROC3_FSSTAT] = {.name = "FSSTAT", }, [NFSPROC3_FSINFO] = {.name = "FSINFO ", }, [NFSPROC3_PATHCONF] = {.name = "PATHCONF", }, [NFSPROC3_COMMIT] = {.name = "COMMIT", }, }; static const struct op_name optabv4[] = { [0] = {.name = "ILLEGAL", }, [1] = {.name = "ILLEGAL",}, [2] = {.name = "ILLEGAL",}, [NFS4_OP_ACCESS] = {.name = "ACCESS",}, [NFS4_OP_CLOSE] = {.name = "CLOSE",}, [NFS4_OP_COMMIT] = {.name = "COMMIT",}, [NFS4_OP_CREATE] = {.name = "CREATE",}, [NFS4_OP_DELEGPURGE] = {.name = "DELEGPURGE",}, [NFS4_OP_DELEGRETURN] = {.name = "DELEGRETURN",}, [NFS4_OP_GETATTR] = {.name = "GETATTR",}, [NFS4_OP_GETFH] = {.name = "GETFH",}, [NFS4_OP_LINK] = {.name = "LINK",}, [NFS4_OP_LOCK] = {.name = "LOCK",}, [NFS4_OP_LOCKT] = {.name = "LOCKT",}, [NFS4_OP_LOCKU] = {.name = "LOCKU",}, [NFS4_OP_LOOKUP] = {.name = "LOOKUP",}, [NFS4_OP_LOOKUPP] = {.name = "LOOKUPP",}, [NFS4_OP_NVERIFY] = {.name = "NVERIFY",}, [NFS4_OP_OPEN] = {.name = "OPEN",}, [NFS4_OP_OPENATTR] = {.name = "OPENATTR",}, [NFS4_OP_OPEN_CONFIRM] = {.name = "OPEN_CONFIRM",}, [NFS4_OP_OPEN_DOWNGRADE] = {.name = "OPEN_DOWNGRADE",}, [NFS4_OP_PUTFH] = {.name = "PUTFH",}, [NFS4_OP_PUTPUBFH] = {.name = "PUTPUBFH",}, [NFS4_OP_PUTROOTFH] = {.name = "PUTROOTFH",}, [NFS4_OP_READ] = {.name = "READ",}, [NFS4_OP_READDIR] = {.name = "READDIR",}, [NFS4_OP_READLINK] = {.name = "READLINK",}, [NFS4_OP_REMOVE] = {.name = "REMOVE",}, [NFS4_OP_RENAME] = {.name = "RENAME",}, [NFS4_OP_RENEW] = {.name = "RENEW",}, [NFS4_OP_RESTOREFH] = {.name = "RESTOREFH",}, [NFS4_OP_SAVEFH] = {.name = "SAVEFH",}, [NFS4_OP_SECINFO] = {.name = "SECINFO",}, [NFS4_OP_SETATTR] = {.name = "SETATTR",}, [NFS4_OP_SETCLIENTID] = {.name = "SETCLIENTID",}, [NFS4_OP_SETCLIENTID_CONFIRM] = {.name = "SETCLIENTID_CONFIRM",}, [NFS4_OP_VERIFY] = {.name = "VERIFY",}, [NFS4_OP_WRITE] = {.name = "WRITE",}, [NFS4_OP_RELEASE_LOCKOWNER] = {.name = "RELEASE_LOCKOWNER",}, [NFS4_OP_BACKCHANNEL_CTL] = {.name = "BACKCHANNEL_CTL",}, [NFS4_OP_BIND_CONN_TO_SESSION] = {.name = "BIND_CONN_TO_SESSION",}, [NFS4_OP_EXCHANGE_ID] = {.name = "EXCHANGE_ID",}, [NFS4_OP_CREATE_SESSION] = {.name = "CREATE_SESSION",}, [NFS4_OP_DESTROY_SESSION] = {.name = "DESTROY_SESSION",}, [NFS4_OP_FREE_STATEID] = {.name = "FREE_STATEID",}, [NFS4_OP_GET_DIR_DELEGATION] = {.name = "GET_DIR_DELEGATION",}, [NFS4_OP_GETDEVICEINFO] = {.name = "GETDEVICEINFO",}, [NFS4_OP_GETDEVICELIST] = {.name = "GETDEVICELIST",}, [NFS4_OP_LAYOUTCOMMIT] = {.name = "LAYOUTCOMMIT",}, [NFS4_OP_LAYOUTGET] = {.name = "LAYOUTGET",}, [NFS4_OP_LAYOUTRETURN] = {.name = "LAYOUTRETURN",}, [NFS4_OP_SECINFO_NO_NAME] = {.name = "SECINFO_NO_NAME",}, [NFS4_OP_SEQUENCE] = {.name = "SEQUENCE",}, [NFS4_OP_SET_SSV] = {.name = "SET_SSV",}, [NFS4_OP_TEST_STATEID] = {.name = "TEST_STATEID",}, [NFS4_OP_WANT_DELEGATION] = {.name = "WANT_DELEGATION",}, [NFS4_OP_DESTROY_CLIENTID] = {.name = "DESTROY_CLIENTID",}, [NFS4_OP_RECLAIM_COMPLETE] = {.name = "RECLAIM_COMPLETE",}, /* NFSv4.2 */ [NFS4_OP_ALLOCATE] = {.name = "ALLOCATE",}, [NFS4_OP_COPY] = {.name = "COPY",}, [NFS4_OP_COPY_NOTIFY] = {.name = "COPY_NOTIFY",}, [NFS4_OP_DEALLOCATE] = {.name = "DEALLOCATE",}, [NFS4_OP_IO_ADVISE] = {.name = "IO_ADVISE",}, [NFS4_OP_LAYOUTERROR] = {.name = "LAYOUTERROR",}, [NFS4_OP_OFFLOAD_CANCEL] = {.name = "OFFLOAD_CANCEL",}, [NFS4_OP_OFFLOAD_STATUS] = {.name = "OFFLOAD_STATUS",}, [NFS4_OP_READ_PLUS] = {.name = "READ_PLUS",}, [NFS4_OP_SEEK] = {.name = "SEEK",}, [NFS4_OP_WRITE_SAME] = {.name = "WRITE_SAME",}, [NFS4_OP_CLONE] = {.name = "OP_CLONE",}, /* NFSv4.3 */ [NFS4_OP_GETXATTR] = {.name = "OP_GETXATTR",}, [NFS4_OP_SETXATTR] = {.name = "OP_SETXATTR",}, [NFS4_OP_LISTXATTR] = {.name = "OP_LISTXATTR",}, [NFS4_OP_REMOVEXATTR] = {.name = "OP_REMOVEXATTR",}, }; #endif /* Classify protocol ops for stats purposes */ enum proto_op_type { GENERAL_OP = 0, /* default for array init */ READ_OP, WRITE_OP, LAYOUT_OP }; static const uint32_t nfsv3_optype[NFS_V3_NB_COMMAND] = { [NFSPROC3_READ] = READ_OP, [NFSPROC3_WRITE] = WRITE_OP, }; static const uint32_t nfsv40_optype[NFS_V40_NB_OPERATION] = { [NFS4_OP_READ] = READ_OP, [NFS4_OP_WRITE] = WRITE_OP, }; static const uint32_t nfsv41_optype[NFS_V41_NB_OPERATION] = { [NFS4_OP_READ] = READ_OP, [NFS4_OP_WRITE] = WRITE_OP, [NFS4_OP_GETDEVICEINFO] = LAYOUT_OP, [NFS4_OP_GETDEVICELIST] = LAYOUT_OP, [NFS4_OP_LAYOUTCOMMIT] = LAYOUT_OP, [NFS4_OP_LAYOUTGET] = LAYOUT_OP, [NFS4_OP_LAYOUTRETURN] = LAYOUT_OP, }; static const uint32_t nfsv42_optype[NFS_V42_NB_OPERATION] = { [NFS4_OP_READ] = READ_OP, [NFS4_OP_WRITE] = WRITE_OP, [NFS4_OP_GETDEVICEINFO] = LAYOUT_OP, [NFS4_OP_GETDEVICELIST] = LAYOUT_OP, [NFS4_OP_LAYOUTCOMMIT] = LAYOUT_OP, [NFS4_OP_LAYOUTGET] = LAYOUT_OP, [NFS4_OP_LAYOUTRETURN] = LAYOUT_OP, [NFS4_OP_WRITE_SAME] = WRITE_OP, [NFS4_OP_READ_PLUS] = READ_OP, }; /* latency stats */ struct op_latency { uint64_t latency; uint64_t min; uint64_t max; }; /* v3 ops */ struct nfsv3_ops { uint64_t op[NFSPROC3_COMMIT+1]; }; /* quota ops */ struct qta_ops { uint64_t op[RQUOTAPROC_SETACTIVEQUOTA+1]; }; /* nlm ops */ struct nlm_ops { uint64_t op[NLMPROC4_FREE_ALL+1]; }; /* mount ops */ struct mnt_ops { uint64_t op[MOUNTPROC3_EXPORT+1]; }; /* v4 ops */ struct nfsv4_ops { uint64_t op[NFS4_OP_LAST_ONE]; }; /* basic op counter */ struct proto_op { uint64_t total; /* total of any kind */ uint64_t errors; /* ! NFS_OK */ uint64_t dups; /* detected dup requests */ struct op_latency latency; /* either executed ops latency */ struct op_latency dup_latency; /* or latency (runtime) to replay */ struct op_latency queue_latency; /* queue wait time */ }; /* basic I/O transfer counter */ struct xfer_op { struct proto_op cmd; uint64_t requested; uint64_t transferred; }; /* pNFS Layout counters */ struct layout_op { uint64_t total; /* total ops */ uint64_t errors; /* ! NFS4_OK && !NFS4ERR_DELAY */ uint64_t delays; /* NFS4ERR_DELAY */ }; /* NFSv3 statistics counters */ struct nfsv3_stats { struct proto_op cmds; /* non-I/O ops = cmds - (read+write) */ struct xfer_op read; struct xfer_op write; }; /* Mount statistics counters */ struct mnt_stats { struct proto_op v1_ops; struct proto_op v3_ops; }; /* lock manager counters */ struct nlmv4_stats { struct proto_op ops; }; /* Quota counters */ struct rquota_stats { struct proto_op ops; struct proto_op ext_ops; }; /* NFSv4 statistics counters */ struct nfsv40_stats { struct proto_op compounds; uint64_t ops_per_compound; /* avg = total / ops_per */ struct xfer_op read; struct xfer_op write; }; struct nfsv41_stats { struct proto_op compounds; uint64_t ops_per_compound; /* for size averaging */ struct xfer_op read; struct xfer_op write; struct layout_op getdevinfo; struct layout_op layout_get; struct layout_op layout_commit; struct layout_op layout_return; struct layout_op recall; }; struct transport_stats { uint64_t rx_bytes; uint64_t rx_pkt; uint64_t rx_err; uint64_t tx_bytes; uint64_t tx_pkt; uint64_t tx_err; }; #ifdef _USE_9P struct _9p_stats { struct proto_op cmds; /* non-I/O ops */ struct xfer_op read; struct xfer_op write; struct transport_stats trans; struct proto_op *opcodes[_9P_RWSTAT+1]; }; #endif struct global_stats { struct nfsv3_stats nfsv3; struct mnt_stats mnt; struct nlmv4_stats nlm4; struct rquota_stats rquota; struct nfsv40_stats nfsv40; struct nfsv41_stats nfsv41; struct nfsv41_stats nfsv42; /* Uses v41 stats */ struct nfsv3_ops v3; struct nfsv4_ops v4; struct nlm_ops lm; struct mnt_ops mn; struct qta_ops qt; }; struct deleg_stats { uint32_t curr_deleg_grants; /* current num of delegations owned by this client */ uint32_t tot_recalls; /* total num of times client was asked to recall */ uint32_t failed_recalls; /* times client failed to process recall */ uint32_t num_revokes; /* Num revokes for the client */ }; static struct global_stats global_st; /* include the top level server_stats struct definition */ #include "server_stats_private.h" /** * @brief Get stats struct helpers * * These functions dereference the protocol specific struct * silently calloc the struct on first use. * * @param stats [IN] the stats structure to dereference in * @param lock [IN] the lock in the stats owning struct * * @return pointer to proto struct * * @TODO make them inlines for release */ static struct nfsv3_stats *get_v3(struct gsh_stats *stats, pthread_rwlock_t *lock) { if (unlikely(stats->nfsv3 == NULL)) { PTHREAD_RWLOCK_wrlock(lock); if (stats->nfsv3 == NULL) stats->nfsv3 = gsh_calloc(1, sizeof(struct nfsv3_stats)); PTHREAD_RWLOCK_unlock(lock); } return stats->nfsv3; } static struct mnt_stats *get_mnt(struct gsh_stats *stats, pthread_rwlock_t *lock) { if (unlikely(stats->mnt == NULL)) { PTHREAD_RWLOCK_wrlock(lock); if (stats->mnt == NULL) stats->mnt = gsh_calloc(1, sizeof(struct mnt_stats)); PTHREAD_RWLOCK_unlock(lock); } return stats->mnt; } static struct nlmv4_stats *get_nlm4(struct gsh_stats *stats, pthread_rwlock_t *lock) { if (unlikely(stats->nlm4 == NULL)) { PTHREAD_RWLOCK_wrlock(lock); if (stats->nlm4 == NULL) stats->nlm4 = gsh_calloc(1, sizeof(struct nlmv4_stats)); PTHREAD_RWLOCK_unlock(lock); } return stats->nlm4; } static struct rquota_stats *get_rquota(struct gsh_stats *stats, pthread_rwlock_t *lock) { if (unlikely(stats->rquota == NULL)) { PTHREAD_RWLOCK_wrlock(lock); if (stats->rquota == NULL) stats->rquota = gsh_calloc(1, sizeof(struct rquota_stats)); PTHREAD_RWLOCK_unlock(lock); } return stats->rquota; } static struct nfsv40_stats *get_v40(struct gsh_stats *stats, pthread_rwlock_t *lock) { if (unlikely(stats->nfsv40 == NULL)) { PTHREAD_RWLOCK_wrlock(lock); if (stats->nfsv40 == NULL) stats->nfsv40 = gsh_calloc(1, sizeof(struct nfsv40_stats)); PTHREAD_RWLOCK_unlock(lock); } return stats->nfsv40; } static struct nfsv41_stats *get_v41(struct gsh_stats *stats, pthread_rwlock_t *lock) { if (unlikely(stats->nfsv41 == NULL)) { PTHREAD_RWLOCK_wrlock(lock); if (stats->nfsv41 == NULL) stats->nfsv41 = gsh_calloc(1, sizeof(struct nfsv41_stats)); PTHREAD_RWLOCK_unlock(lock); } return stats->nfsv41; } static struct nfsv41_stats *get_v42(struct gsh_stats *stats, pthread_rwlock_t *lock) { if (unlikely(stats->nfsv42 == NULL)) { PTHREAD_RWLOCK_wrlock(lock); if (stats->nfsv42 == NULL) stats->nfsv42 = gsh_calloc(1, sizeof(struct nfsv41_stats)); PTHREAD_RWLOCK_unlock(lock); } return stats->nfsv42; } #ifdef _USE_9P static struct _9p_stats *get_9p(struct gsh_stats *stats, pthread_rwlock_t *lock) { if (unlikely(stats->_9p == NULL)) { PTHREAD_RWLOCK_wrlock(lock); if (stats->_9p == NULL) stats->_9p = gsh_calloc(1, sizeof(struct _9p_stats)); PTHREAD_RWLOCK_unlock(lock); } return stats->_9p; } #endif /* Functions for recording statistics */ /** * @brief Record latency stats * * @param op [IN] protocol op stats struct * @param request_time [IN] time consumed by request * @param qwait_time [IN] time sitting on queue * @param dup [IN] detected this was a dup request */ void record_latency(struct proto_op *op, nsecs_elapsed_t request_time, nsecs_elapsed_t qwait_time, bool dup) { /* dup latency is counted separately */ if (likely(!dup)) { (void)atomic_add_uint64_t(&op->latency.latency, request_time); if (op->latency.min == 0L || op->latency.min > request_time) (void)atomic_store_uint64_t(&op->latency.min, request_time); if (op->latency.max == 0L || op->latency.max < request_time) (void)atomic_store_uint64_t(&op->latency.max, request_time); } else { (void)atomic_add_uint64_t(&op->dup_latency.latency, request_time); if (op->dup_latency.min == 0L || op->dup_latency.min > request_time) (void)atomic_store_uint64_t(&op->dup_latency.min, request_time); if (op->dup_latency.max == 0L || op->dup_latency.max < request_time) (void)atomic_store_uint64_t(&op->dup_latency.max, request_time); } /* record how long it was laying around waiting ... */ (void)atomic_add_uint64_t(&op->queue_latency.latency, qwait_time); if (op->queue_latency.min == 0L || op->queue_latency.min > qwait_time) (void)atomic_store_uint64_t(&op->queue_latency.min, qwait_time); if (op->queue_latency.max == 0L || op->queue_latency.max < qwait_time) (void)atomic_store_uint64_t(&op->queue_latency.max, qwait_time); } /** * @brief count the i/o stats * * We do the transfer counts here. Latency is done later at * operation/compound completion. * * @param iop [IN] transfer stats struct * @param requested [IN] bytes requested * @param transferred [IN] bytes actually transferred * @param success [IN] the op returned OK (or error) */ static void record_io(struct xfer_op *iop, size_t requested, size_t transferred, bool success) { (void)atomic_inc_uint64_t(&iop->cmd.total); if (success) { (void)atomic_add_uint64_t(&iop->requested, requested); (void)atomic_add_uint64_t(&iop->transferred, transferred); } else { (void)atomic_inc_uint64_t(&iop->cmd.errors); } /* somehow we must record latency */ } /** * @brief record i/o stats by protocol */ static void record_io_stats(struct gsh_stats *gsh_st, pthread_rwlock_t *lock, size_t requested, size_t transferred, bool success, bool is_write) { struct xfer_op *iop = NULL; if (op_ctx->req_type == NFS_REQUEST) { if (op_ctx->nfs_vers == NFS_V3) { struct nfsv3_stats *sp = get_v3(gsh_st, lock); iop = is_write ? &sp->write : &sp->read; } else if (op_ctx->nfs_vers == NFS_V4) { if (op_ctx->nfs_minorvers == 0) { struct nfsv40_stats *sp = get_v40(gsh_st, lock); iop = is_write ? &sp->write : &sp->read; } else if (op_ctx->nfs_minorvers == 1) { struct nfsv41_stats *sp = get_v41(gsh_st, lock); iop = is_write ? &sp->write : &sp->read; } else if (op_ctx->nfs_minorvers == 2) { struct nfsv41_stats *sp = get_v42(gsh_st, lock); iop = is_write ? &sp->write : &sp->read; } /* the frightening thought is someday minor == 3 */ } else { return; } #ifdef _USE_9P } else if (op_ctx->req_type == _9P_REQUEST) { struct _9p_stats *sp = get_9p(gsh_st, lock); iop = is_write ? &sp->write : &sp->read; #endif } else { return; } record_io(iop, requested, transferred, success); } /** * @brief count the protocol operation * * Use atomic ops to avoid locks. We don't lock for the max * and min because if there is a collision, over the long haul, * the error is near zero... * * @param op [IN] pointer to specific protocol struct * @param request_time [IN] wallclock time (nsecs) for this op * @param qwait_time [IN] wallclock time (nsecs) waiting for service * @param success [IN] protocol error code == OK * @param dup [IN] true if op was detected duplicate */ static void record_op(struct proto_op *op, nsecs_elapsed_t request_time, nsecs_elapsed_t qwait_time, bool success, bool dup) { /* count the op */ (void)atomic_inc_uint64_t(&op->total); /* also count it as an error if protocol not happy */ if (!success) (void)atomic_inc_uint64_t(&op->errors); if (unlikely(dup)) (void)atomic_inc_uint64_t(&op->dups); record_latency(op, request_time, qwait_time, dup); } #ifdef USE_DBUS /** * @brief reset the counts for protocol operation * Use atomic ops to avoid locks. * @param op [IN] pointer to specific protocol struct */ static void reset_op(struct proto_op *op) { (void)atomic_store_uint64_t(&op->total, 0); (void)atomic_store_uint64_t(&op->errors, 0); (void)atomic_store_uint64_t(&op->dups, 0); /* reset latency related counters */ (void)atomic_store_uint64_t(&op->latency.latency, 0); (void)atomic_store_uint64_t(&op->latency.min, 0); (void)atomic_store_uint64_t(&op->latency.max, 0); (void)atomic_store_uint64_t(&op->dup_latency.latency, 0); (void)atomic_store_uint64_t(&op->dup_latency.min, 0); (void)atomic_store_uint64_t(&op->dup_latency.max, 0); (void)atomic_store_uint64_t(&op->queue_latency.latency, 0); (void)atomic_store_uint64_t(&op->queue_latency.min, 0); (void)atomic_store_uint64_t(&op->queue_latency.max, 0); } /** * @brief reset the counts for xfer protocol operation * Use atomic ops to avoid locks. * @param xfer [IN] pointer to specific xfer protocol struct */ static void reset_xfer_op(struct xfer_op *xfer) { reset_op(&xfer->cmd); (void)atomic_store_uint64_t(&xfer->requested, 0); (void)atomic_store_uint64_t(&xfer->transferred, 0); } /** * @brief reset the counts related to layout * Use atomic ops to avoid locks. * @param lo [IN] pointer to specific layout struct */ static void reset_layout_op(struct layout_op *lo) { (void)atomic_store_uint64_t(&lo->total, 0); (void)atomic_store_uint64_t(&lo->errors, 0); (void)atomic_store_uint64_t(&lo->delays, 0); } /** * @brief reset the counts nfsv3_stats * Use atomic ops to avoid locks. * @param nfsv3 [IN] pointer to nfsv3_stats struct */ static void reset_nfsv3_stats(struct nfsv3_stats *nfsv3) { /* Reset stats counter for nfsv3 protocol */ reset_op(&nfsv3->cmds); reset_xfer_op(&nfsv3->read); reset_xfer_op(&nfsv3->write); } /** * @brief reset the counts nfsv40_stats * Use atomic ops to avoid locks. * @param nfsv40 [IN] pointer to nfsv40_stats struct */ static void reset_nfsv40_stats(struct nfsv40_stats *nfsv40) { /* Reset stats counter for nfsv4 protocol */ reset_op(&nfsv40->compounds); (void)atomic_store_uint64_t(&nfsv40->ops_per_compound, 0); reset_xfer_op(&nfsv40->read); reset_xfer_op(&nfsv40->write); } /** * @brief reset the counts nfsv41_stats * Use atomic ops to avoid locks. * @param nfsv41 [IN] pointer to nfsv41_stats struct */ static void reset_nfsv41_stats(struct nfsv41_stats *nfsv41) { /* Reset stats counter for nfsv41 protocol */ reset_op(&nfsv41->compounds); (void)atomic_store_uint64_t(&nfsv41->ops_per_compound, 0); reset_xfer_op(&nfsv41->read); reset_xfer_op(&nfsv41->write); reset_layout_op(&nfsv41->getdevinfo); reset_layout_op(&nfsv41->layout_get); reset_layout_op(&nfsv41->layout_commit); reset_layout_op(&nfsv41->layout_return); reset_layout_op(&nfsv41->recall); } /** * @brief reset the counts mnt_stats * Use atomic ops to avoid locks. * @param mnt [IN] pointer to mnt_stats struct */ static void reset_mnt_stats(struct mnt_stats *mnt) { /* Reset stats counter for mount protocol */ reset_op(&mnt->v1_ops); reset_op(&mnt->v3_ops); } /** * @brief reset the counts rquota_stats * Use atomic ops to avoid locks. * @param rquota [IN] pointer to rquota_stats struct */ static void reset_rquota_stats(struct rquota_stats *rquota) { /* Reset stats counter for quota */ reset_op(&rquota->ops); reset_op(&rquota->ext_ops); } /** * @brief reset the counts nlmv4_stats * Use atomic ops to avoid locks. * @param nlmv4 [IN] pointer to nlmv4_stats struct */ static void reset_nlmv4_stats(struct nlmv4_stats *nlmv4) { /* Reset stats counter for nlmv4 */ reset_op(&nlmv4->ops); } /** * @brief reset the counts nlmv4_stats * Use atomic ops to avoid locks. * @param nlmv4 [IN] pointer to nlmv4_stats struct */ static void reset_deleg_stats(struct deleg_stats *deleg) { /* Reset stats counter for deleg */ (void)atomic_store_uint32_t(&deleg->curr_deleg_grants, 0); (void)atomic_store_uint32_t(&deleg->tot_recalls, 0); (void)atomic_store_uint32_t(&deleg->failed_recalls, 0); (void)atomic_store_uint32_t(&deleg->num_revokes, 0); } #ifdef _USE_9P static void reset__9P_stats(struct _9p_stats *_9p) { u8 opc; reset_op(&_9p->cmds); reset_xfer_op(&_9p->read); reset_xfer_op(&_9p->write); (void)atomic_store_uint64_t(&_9p->trans.rx_bytes, 0); (void)atomic_store_uint64_t(&_9p->trans.rx_pkt, 0); (void)atomic_store_uint64_t(&_9p->trans.rx_err, 0); (void)atomic_store_uint64_t(&_9p->trans.tx_bytes, 0); (void)atomic_store_uint64_t(&_9p->trans.tx_pkt, 0); (void)atomic_store_uint64_t(&_9p->trans.tx_err, 0); for (opc = 0; opc <= _9P_RWSTAT; opc++) { if (_9p->opcodes[opc] != NULL) reset_op(_9p->opcodes[opc]); } } #endif #endif /* USE_DBUS */ /** * @brief record V4.1 layout op stats * * @param sp [IN] stats block to update * @param proto_op [IN] protocol op * @param status [IN] operation status */ static void record_layout(struct nfsv41_stats *sp, int proto_op, int status) { struct layout_op *lp; if (proto_op == NFS4_OP_GETDEVICEINFO) lp = &sp->getdevinfo; else if (proto_op == NFS4_OP_GETDEVICELIST) lp = &sp->getdevinfo; else if (proto_op == NFS4_OP_LAYOUTGET) lp = &sp->layout_get; else if (proto_op == NFS4_OP_LAYOUTCOMMIT) lp = &sp->layout_commit; else if (proto_op == NFS4_OP_LAYOUTRETURN) lp = &sp->layout_return; else return; (void)atomic_inc_uint64_t(&lp->total); if (status == NFS4ERR_DELAY) (void)atomic_inc_uint64_t(&lp->delays); else if (status != NFS4_OK) (void)atomic_inc_uint64_t(&lp->errors); } /** * @brief Record NFS V4 compound stats */ static void record_nfsv4_op(struct gsh_stats *gsh_st, pthread_rwlock_t *lock, int proto_op, int minorversion, nsecs_elapsed_t request_time, nsecs_elapsed_t qwait_time, int status) { if (minorversion == 0) { struct nfsv40_stats *sp = get_v40(gsh_st, lock); /* record stuff */ switch (nfsv40_optype[proto_op]) { case READ_OP: record_latency(&sp->read.cmd, request_time, qwait_time, false); break; case WRITE_OP: record_latency(&sp->write.cmd, request_time, qwait_time, false); break; default: record_op(&sp->compounds, request_time, qwait_time, status == NFS4_OK, false); } } else if (minorversion == 1) { struct nfsv41_stats *sp = get_v41(gsh_st, lock); /* record stuff */ switch (nfsv41_optype[proto_op]) { case READ_OP: record_latency(&sp->read.cmd, request_time, qwait_time, false); break; case WRITE_OP: record_latency(&sp->write.cmd, request_time, qwait_time, false); break; case LAYOUT_OP: record_layout(sp, proto_op, status); break; default: record_op(&sp->compounds, request_time, qwait_time, status == NFS4_OK, false); } } else if (minorversion == 2) { struct nfsv41_stats *sp = get_v42(gsh_st, lock); /* record stuff */ switch (nfsv42_optype[proto_op]) { case READ_OP: record_latency(&sp->read.cmd, request_time, qwait_time, false); break; case WRITE_OP: record_latency(&sp->write.cmd, request_time, qwait_time, false); break; case LAYOUT_OP: record_layout(sp, proto_op, status); break; default: record_op(&sp->compounds, request_time, qwait_time, status == NFS4_OK, false); } } } /** * @brief Record NFS V4 compound stats */ static void record_compound(struct gsh_stats *gsh_st, pthread_rwlock_t *lock, int minorversion, uint64_t num_ops, nsecs_elapsed_t request_time, nsecs_elapsed_t qwait_time, bool success) { if (minorversion == 0) { struct nfsv40_stats *sp = get_v40(gsh_st, lock); /* record stuff */ record_op(&sp->compounds, request_time, qwait_time, success, false); (void)atomic_add_uint64_t(&sp->ops_per_compound, num_ops); } else if (minorversion == 1) { struct nfsv41_stats *sp = get_v41(gsh_st, lock); /* record stuff */ record_op(&sp->compounds, request_time, qwait_time, success, false); (void)atomic_add_uint64_t(&sp->ops_per_compound, num_ops); } else if (minorversion == 2) { struct nfsv41_stats *sp = get_v42(gsh_st, lock); /* record stuff */ record_op(&sp->compounds, request_time, qwait_time, success, false); (void)atomic_add_uint64_t(&sp->ops_per_compound, num_ops); } } /** * @brief Record request statistics (V3 era protos only) * * Decode the protocol and find the proto specific stats struct. * Once we found the stats block, do the update(s). * * @param gsh_st [IN] stats struct from client or export * @param lock [IN] lock on client|export for malloc * @param reqdata [IN] info about the proto request * @param success [IN] the op returned OK (or error) * @param request_time [IN] time consumed by request * @param qwait_time [IN] time sitting on queue * @param dup [IN] detected this was a dup request */ static void record_stats(struct gsh_stats *gsh_st, pthread_rwlock_t *lock, request_data_t *reqdata, nsecs_elapsed_t request_time, nsecs_elapsed_t qwait_time, bool success, bool dup, bool global) { struct svc_req *req = &reqdata->r_u.req.svc; uint32_t proto_op = req->rq_msg.cb_proc; uint32_t program_op = req->rq_msg.cb_prog; if (program_op == NFS_program[P_NFS]) { if (proto_op == 0) return; /* we don't count NULL ops */ if (req->rq_msg.cb_vers == NFS_V3) { struct nfsv3_stats *sp = get_v3(gsh_st, lock); /* record stuff */ if (global) record_op(&global_st.nfsv3.cmds, request_time, qwait_time, success, dup); switch (nfsv3_optype[proto_op]) { case READ_OP: record_latency(&sp->read.cmd, request_time, qwait_time, dup); break; case WRITE_OP: record_latency(&sp->write.cmd, request_time, qwait_time, dup); break; default: record_op(&sp->cmds, request_time, qwait_time, success, dup); } } else { /* We don't do V4 here and V2 is toast */ return; } } else if (program_op == NFS_program[P_MNT]) { struct mnt_stats *sp = get_mnt(gsh_st, lock); if (global && req->rq_msg.cb_vers == MOUNT_V1) record_op(&global_st.mnt.v1_ops, request_time, qwait_time, success, dup); else if (global) record_op(&global_st.mnt.v3_ops, request_time, qwait_time, success, dup); /* record stuff */ if (req->rq_msg.cb_vers == MOUNT_V1) record_op(&sp->v1_ops, request_time, qwait_time, success, dup); else record_op(&sp->v3_ops, request_time, qwait_time, success, dup); } else if (program_op == NFS_program[P_NLM]) { struct nlmv4_stats *sp = get_nlm4(gsh_st, lock); if (global) record_op(&global_st.nlm4.ops, request_time, qwait_time, success, dup); /* record stuff */ record_op(&sp->ops, request_time, qwait_time, success, dup); } else if (program_op == NFS_program[P_RQUOTA]) { struct rquota_stats *sp = get_rquota(gsh_st, lock); if (global) record_op(&global_st.rquota.ops, request_time, qwait_time, success, dup); /* record stuff */ if (req->rq_msg.cb_vers == RQUOTAVERS) record_op(&sp->ops, request_time, qwait_time, success, dup); else record_op(&sp->ext_ops, request_time, qwait_time, success, dup); } } #ifdef _USE_9P /** * @brief Record transport stats * */ static void record_transport_stats(struct transport_stats *t_st, uint64_t rx_bytes, uint64_t rx_pkt, uint64_t rx_err, uint64_t tx_bytes, uint64_t tx_pkt, uint64_t tx_err) { if (rx_bytes) atomic_add_uint64_t(&t_st->rx_bytes, rx_bytes); if (rx_pkt) atomic_add_uint64_t(&t_st->rx_pkt, rx_pkt); if (rx_err) atomic_add_uint64_t(&t_st->rx_err, rx_err); if (tx_bytes) atomic_add_uint64_t(&t_st->tx_bytes, tx_bytes); if (tx_pkt) atomic_add_uint64_t(&t_st->tx_pkt, tx_pkt); if (tx_err) atomic_add_uint64_t(&t_st->tx_err, tx_err); } #endif #ifdef _USE_9P /** * @brief record 9P tcp transport stats * * Called from 9P functions doing send/recv */ void server_stats_transport_done(struct gsh_client *client, uint64_t rx_bytes, uint64_t rx_pkt, uint64_t rx_err, uint64_t tx_bytes, uint64_t tx_pkt, uint64_t tx_err) { struct server_stats *server_st = container_of(client, struct server_stats, client); struct _9p_stats *sp = get_9p(&server_st->st, &client->lock); if (sp != NULL) record_transport_stats(&sp->trans, rx_bytes, rx_pkt, rx_err, tx_bytes, tx_pkt, tx_err); } /** * @bried record 9p operation stats * * Called from 9P interpreter at operation completion */ void server_stats_9p_done(u8 opc, struct _9p_request_data *req9p) { struct gsh_client *client; struct gsh_export *export; struct _9p_stats *sp; client = req9p->pconn->client; if (client) { struct server_stats *server_st; server_st = container_of(client, struct server_stats, client); sp = get_9p(&server_st->st, &client->lock); if (sp->opcodes[opc] == NULL) sp->opcodes[opc] = gsh_calloc(1, sizeof(struct proto_op)); record_op(sp->opcodes[opc], 0, 0, true, false); } if (op_ctx->ctx_export) { struct export_stats *exp_st; export = op_ctx->ctx_export; exp_st = container_of(export, struct export_stats, export); sp = get_9p(&exp_st->st, &export->lock); if (sp->opcodes[opc] == NULL) sp->opcodes[opc] = gsh_calloc(1, sizeof(struct proto_op)); record_op(sp->opcodes[opc], 0, 0, true, false); } } #endif /** * @brief record NFS op finished * * Called from nfs_rpc_process_request at operation/command completion */ void server_stats_nfs_done(request_data_t *reqdata, int rc, bool dup) { struct gsh_client *client = op_ctx->client; struct timespec current_time; nsecs_elapsed_t stop_time; struct svc_req *req = &reqdata->r_u.req.svc; uint32_t proto_op = req->rq_msg.cb_proc; uint32_t program_op = req->rq_msg.cb_prog; if (!nfs_param.core_param.enable_NFSSTATS) return; if (program_op == NFS_PROGRAM && op_ctx->nfs_vers == NFS_V3) global_st.v3.op[proto_op]++; else if (program_op == NFS_program[P_NLM]) global_st.lm.op[proto_op]++; else if (program_op == NFS_program[P_MNT]) global_st.mn.op[proto_op]++; else if (program_op == NFS_program[P_RQUOTA]) global_st.qt.op[proto_op]++; if (nfs_param.core_param.enable_FASTSTATS) return; now(¤t_time); stop_time = timespec_diff(&ServerBootTime, ¤t_time); if (client != NULL) { struct server_stats *server_st; server_st = container_of(client, struct server_stats, client); record_stats(&server_st->st, &client->lock, reqdata, stop_time - op_ctx->start_time, op_ctx->queue_wait, rc == NFS_REQ_OK, dup, true); (void)atomic_store_uint64_t(&client->last_update, stop_time); } if (!dup && op_ctx->ctx_export != NULL) { struct export_stats *exp_st; exp_st = container_of(op_ctx->ctx_export, struct export_stats, export); record_stats(&exp_st->st, &op_ctx->ctx_export->lock, reqdata, stop_time - op_ctx->start_time, op_ctx->queue_wait, rc == NFS_REQ_OK, dup, false); (void)atomic_store_uint64_t(&op_ctx->ctx_export->last_update, stop_time); } } /** * @brief record NFS V4 compound finished * * Called from nfs4_compound at compound loop completion */ void server_stats_nfsv4_op_done(int proto_op, nsecs_elapsed_t start_time, int status) { struct gsh_client *client = op_ctx->client; struct timespec current_time; nsecs_elapsed_t stop_time; if (!nfs_param.core_param.enable_NFSSTATS) return; if (op_ctx->nfs_vers == NFS_V4) global_st.v4.op[proto_op]++; if (nfs_param.core_param.enable_FASTSTATS) return; now(¤t_time); stop_time = timespec_diff(&ServerBootTime, ¤t_time); if (client != NULL) { struct server_stats *server_st; server_st = container_of(client, struct server_stats, client); record_nfsv4_op(&server_st->st, &client->lock, proto_op, op_ctx->nfs_minorvers, stop_time - start_time, op_ctx->queue_wait, status); (void)atomic_store_uint64_t(&client->last_update, stop_time); } if (op_ctx->nfs_minorvers == 0) record_op(&global_st.nfsv40.compounds, stop_time - start_time, op_ctx->queue_wait, status == NFS4_OK, false); else if (op_ctx->nfs_minorvers == 1) record_op(&global_st.nfsv41.compounds, stop_time - start_time, op_ctx->queue_wait, status == NFS4_OK, false); else if (op_ctx->nfs_minorvers == 2) record_op(&global_st.nfsv42.compounds, stop_time - start_time, op_ctx->queue_wait, status == NFS4_OK, false); if (op_ctx->ctx_export != NULL) { struct export_stats *exp_st; exp_st = container_of(op_ctx->ctx_export, struct export_stats, export); record_nfsv4_op(&exp_st->st, &op_ctx->ctx_export->lock, proto_op, op_ctx->nfs_minorvers, stop_time - start_time, op_ctx->queue_wait, status); (void)atomic_store_uint64_t(&op_ctx->ctx_export->last_update, stop_time); } } /** * @brief record NFS V4 compound finished * * Called from nfs4_compound at compound loop completion */ void server_stats_compound_done(int num_ops, int status) { struct gsh_client *client = op_ctx->client; struct timespec current_time; nsecs_elapsed_t stop_time; if (!nfs_param.core_param.enable_NFSSTATS) return; now(¤t_time); stop_time = timespec_diff(&ServerBootTime, ¤t_time); if (client != NULL) { struct server_stats *server_st; server_st = container_of(client, struct server_stats, client); record_compound(&server_st->st, &client->lock, op_ctx->nfs_minorvers, num_ops, stop_time - op_ctx->start_time, op_ctx->queue_wait, status == NFS4_OK); (void)atomic_store_uint64_t(&client->last_update, stop_time); } if (op_ctx->ctx_export != NULL) { struct export_stats *exp_st; exp_st = container_of(op_ctx->ctx_export, struct export_stats, export); record_compound(&exp_st->st, &op_ctx->ctx_export->lock, op_ctx->nfs_minorvers, num_ops, stop_time - op_ctx->start_time, op_ctx->queue_wait, status == NFS4_OK); (void)atomic_store_uint64_t(&op_ctx->ctx_export->last_update, stop_time); } } /** * @brief Record I/O stats for protocol read/write * * Called from protocol operation/command handlers to record * transfers */ void server_stats_io_done(size_t requested, size_t transferred, bool success, bool is_write) { if (!nfs_param.core_param.enable_NFSSTATS) return; if (op_ctx->client != NULL) { struct server_stats *server_st; server_st = container_of(op_ctx->client, struct server_stats, client); record_io_stats(&server_st->st, &op_ctx->client->lock, requested, transferred, success, is_write); } if (op_ctx->ctx_export != NULL) { struct export_stats *exp_st; exp_st = container_of(op_ctx->ctx_export, struct export_stats, export); record_io_stats(&exp_st->st, &op_ctx->ctx_export->lock, requested, transferred, success, is_write); } } /** * @brief record Delegation stats * * Called from a bunch of places. */ void check_deleg_struct(struct gsh_stats *stats, pthread_rwlock_t *lock) { if (unlikely(stats->deleg == NULL)) { PTHREAD_RWLOCK_wrlock(lock); if (stats->deleg == NULL) stats->deleg = gsh_calloc(1, sizeof(struct deleg_stats)); PTHREAD_RWLOCK_unlock(lock); } } void inc_grants(struct gsh_client *client) { if (client != NULL) { struct server_stats *server_st; server_st = container_of(client, struct server_stats, client); check_deleg_struct(&server_st->st, &client->lock); server_st->st.deleg->curr_deleg_grants++; } } void dec_grants(struct gsh_client *client) { if (client != NULL) { struct server_stats *server_st; server_st = container_of(client, struct server_stats, client); check_deleg_struct(&server_st->st, &client->lock); server_st->st.deleg->curr_deleg_grants++; } } void inc_revokes(struct gsh_client *client) { if (client != NULL) { struct server_stats *server_st; server_st = container_of(client, struct server_stats, client); check_deleg_struct(&server_st->st, &client->lock); server_st->st.deleg->num_revokes++; } } void inc_recalls(struct gsh_client *client) { if (client != NULL) { struct server_stats *server_st; server_st = container_of(client, struct server_stats, client); check_deleg_struct(&server_st->st, &client->lock); server_st->st.deleg->tot_recalls++; } } void inc_failed_recalls(struct gsh_client *client) { if (client != NULL) { struct server_stats *server_st; server_st = container_of(client, struct server_stats, client); check_deleg_struct(&server_st->st, &client->lock); server_st->st.deleg->failed_recalls++; } } #ifdef USE_DBUS /* Functions for marshalling statistics to DBUS */ /** * @brief Report Stats availability as members of a struct * * struct available_stats { * ... * bool nfsv3; * bool mnt; * bool nlm4; * bool rquota; * bool nfsv40; * bool nfsv41; * bool nfsv42; * bool _9p; * ... * } * * @param name [IN] name of export or IP address as string * @param stats [IN] pointer to server stats struct * @param last_update [IN] elapsed timestamp of last activity * @param iter [IN] iterator to stuff struct into */ void server_stats_summary(DBusMessageIter *iter, struct gsh_stats *st) { dbus_bool_t stats_available; stats_available = st->nfsv3 != 0; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &stats_available); stats_available = st->mnt != 0; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &stats_available); stats_available = st->nlm4 != 0; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &stats_available); stats_available = st->rquota != 0; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &stats_available); stats_available = st->nfsv40 != 0; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &stats_available); stats_available = st->nfsv41 != 0; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &stats_available); stats_available = st->nfsv42 != 0; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &stats_available); stats_available = st->_9p != 0; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &stats_available); } #ifdef _USE_9P /** @brief Report protocol operation statistics * * struct proto_op { * uint64_t total; * uint64_t errors; * ... * } * * @param op [IN] pointer to proto op sub-structure of interest * @param iter [IN] interator in reply stream to fill */ static void server_dbus_op_stats(struct proto_op *op, DBusMessageIter *iter) { DBusMessageIter struct_iter; uint64_t zero = 0; dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &struct_iter); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, op == NULL ? &zero : &op->total); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, op == NULL ? &zero : &op->errors); dbus_message_iter_close_container(iter, &struct_iter); } #endif /** * @brief Report I/O statistics as a struct * * struct iostats { * uint64_t bytes_requested; * uint64_t bytes_transferred; * uint64_t total_ops; * uint64_t errors; * uint64_t latency; * uint64_t queue_wait; * } * * @param iop [IN] pointer to xfer op sub-structure of interest * @param iter [IN] interator in reply stream to fill */ static void server_dbus_iostats(struct xfer_op *iop, DBusMessageIter *iter) { DBusMessageIter struct_iter; dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &struct_iter); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &iop->requested); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &iop->transferred); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &iop->cmd.total); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &iop->cmd.errors); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &iop->cmd.latency.latency); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &iop->cmd.queue_latency.latency); dbus_message_iter_close_container(iter, &struct_iter); } #ifdef _USE_9P static void server_dbus_transportstats(struct transport_stats *tstats, DBusMessageIter *iter) { DBusMessageIter struct_iter; dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &struct_iter); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &tstats->rx_bytes); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &tstats->rx_pkt); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &tstats->rx_err); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &tstats->tx_bytes); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &tstats->tx_pkt); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &tstats->tx_err); dbus_message_iter_close_container(iter, &struct_iter); } #endif void server_dbus_total(struct export_stats *export_st, DBusMessageIter *iter) { DBusMessageIter struct_iter; uint64_t total = 0; char *version; dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &struct_iter); version = "NFSv3"; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &version); if (export_st->st.nfsv3 == NULL) dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &total); else dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &export_st->st.nfsv3->cmds.total); version = "NFSv40"; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &version); if (export_st->st.nfsv40 == NULL) dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &total); else dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &export_st->st.nfsv40->compounds.total); version = "NFSv41"; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &version); if (export_st->st.nfsv41 == NULL) dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &total); else dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &export_st->st.nfsv41->compounds.total); version = "NFSv42"; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &version); if (export_st->st.nfsv42 == NULL) dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &total); else dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &export_st->st.nfsv42->compounds.total); dbus_message_iter_close_container(iter, &struct_iter); } void global_dbus_total(DBusMessageIter *iter) { DBusMessageIter struct_iter; char *version; dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &struct_iter); version = "NFSv3"; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &version); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &global_st.nfsv3.cmds.total); version = "NFSv40"; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &version); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &global_st.nfsv40.compounds.total); version = "NFSv41"; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &version); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &global_st.nfsv41.compounds.total); version = "NFSv42"; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &version); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &global_st.nfsv42.compounds.total); version = "NLM4"; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &version); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &global_st.nlm4.ops.total); version = "MNTv1"; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &version); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &global_st.mnt.v1_ops.total); version = "MNTv3"; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &version); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &global_st.mnt.v3_ops.total); version = "RQUOTA"; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &version); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &global_st.rquota.ops.total); dbus_message_iter_close_container(iter, &struct_iter); } void global_dbus_fast(DBusMessageIter *iter) { DBusMessageIter struct_iter; char *version; char *op; int i; dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &struct_iter); version = "NFSv3:"; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &version); for (i = 0; i < NFSPROC3_COMMIT; i++) { if (global_st.v3.op[i] > 0) { op = optabv3[i].name; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &op); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &global_st.v3.op[i]); } } version = "\nNFSv4:"; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &version); for (i = 0; i < NFS4_OP_LAST_ONE; i++) { if (global_st.v4.op[i] > 0) { op = optabv4[i].name; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &op); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &global_st.v4.op[i]); } } version = "\nNLM:"; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &version); for (i = 0; i < NLM4_FAILED; i++) { if (global_st.lm.op[i] > 0) { op = optnlm[i].name; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &op); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &global_st.lm.op[i]); } } version = "\nMNT:"; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &version); for (i = 0; i < MOUNTPROC3_EXPORT; i++) { if (global_st.mn.op[i] > 0) { op = optmnt[i].name; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &op); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &global_st.mn.op[i]); } } version = "\nQUOTA:"; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &version); for (i = 0; i < RQUOTAPROC_SETACTIVEQUOTA; i++) { if (global_st.qt.op[i] > 0) { op = optqta[i].name; dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &op); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &global_st.qt.op[i]); } } dbus_message_iter_close_container(iter, &struct_iter); } void server_dbus_v3_iostats(struct nfsv3_stats *v3p, DBusMessageIter *iter) { struct timespec timestamp; now(×tamp); dbus_append_timestamp(iter, ×tamp); server_dbus_iostats(&v3p->read, iter); server_dbus_iostats(&v3p->write, iter); } void server_dbus_v40_iostats(struct nfsv40_stats *v40p, DBusMessageIter *iter) { struct timespec timestamp; now(×tamp); dbus_append_timestamp(iter, ×tamp); server_dbus_iostats(&v40p->read, iter); server_dbus_iostats(&v40p->write, iter); } void server_dbus_v41_iostats(struct nfsv41_stats *v41p, DBusMessageIter *iter) { struct timespec timestamp; now(×tamp); dbus_append_timestamp(iter, ×tamp); server_dbus_iostats(&v41p->read, iter); server_dbus_iostats(&v41p->write, iter); } void server_dbus_v42_iostats(struct nfsv41_stats *v42p, DBusMessageIter *iter) { struct timespec timestamp; now(×tamp); dbus_append_timestamp(iter, ×tamp); server_dbus_iostats(&v42p->read, iter); server_dbus_iostats(&v42p->write, iter); } void server_dbus_fill_io(DBusMessageIter *array_iter, uint16_t *export_id, const char *protocolversion, struct xfer_op *read, struct xfer_op *write) { DBusMessageIter struct_iter; LogFullDebug(COMPONENT_DBUS, " Found %s I/O stats for export ID %d", protocolversion, *export_id); /* create a structure container iterator for the export statistics */ dbus_message_iter_open_container(array_iter, DBUS_TYPE_STRUCT, NULL, &struct_iter); /* append export statistics */ dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT16, export_id); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_STRING, &(protocolversion)); server_dbus_iostats(read, &struct_iter); server_dbus_iostats(write, &struct_iter); /* close the structure container */ dbus_message_iter_close_container(array_iter, &struct_iter); } /** * @brief Return all IO stats of an export * * @reply DBUS_TYPE_ARRAY, "qs(tttttt)(tttttt)" * export id * string containing the protocol version * read statistics structure * (requested, transferred, total, errors, latency, queue wait) * write statistics structure * (requested, transferred, total, errors, latency, queue wait) */ void server_dbus_all_iostats(struct export_stats *export_statistics, DBusMessageIter *array_iter) { if (export_statistics->st.nfsv3 != NULL) { server_dbus_fill_io(array_iter, &(export_statistics->export.export_id), "NFSv3", &(export_statistics->st.nfsv3->read), &(export_statistics->st.nfsv3->write)); } if (export_statistics->st.nfsv40 != NULL) { server_dbus_fill_io(array_iter, &(export_statistics->export.export_id), "NFSv40", &(export_statistics->st.nfsv40->read), &(export_statistics->st.nfsv40->write)); } if (export_statistics->st.nfsv41 != NULL) { server_dbus_fill_io(array_iter, &(export_statistics->export.export_id), "NFSv41", &(export_statistics->st.nfsv41->read), &(export_statistics->st.nfsv41->write)); } if (export_statistics->st.nfsv42 != NULL) { server_dbus_fill_io(array_iter, &(export_statistics->export.export_id), "NFSv42", &(export_statistics->st.nfsv42->read), &(export_statistics->st.nfsv42->write)); } } void reset_gsh_stats(struct gsh_stats *st) { if (st->nfsv3) reset_nfsv3_stats(st->nfsv3); if (st->nfsv40) reset_nfsv40_stats(st->nfsv40); if (st->nfsv41) reset_nfsv41_stats(st->nfsv41); if (st->nfsv42) reset_nfsv41_stats(st->nfsv42); /* Uses v41 stats */ if (st->mnt) reset_mnt_stats(st->mnt); if (st->rquota) reset_rquota_stats(st->rquota); if (st->nlm4) reset_nlmv4_stats(st->nlm4); if (st->deleg) reset_deleg_stats(st->deleg); #ifdef _USE_9P if (st->_9p) reset__9P_stats(st->_9p); #endif } void reset_global_stats(void) { int i; /* Reset all ops counters of nfsv3 */ for (i = 0; i < NFSPROC3_COMMIT; i++) { (void)atomic_store_uint64_t(&global_st.v3.op[i], 0); } /* Reset all ops counters of nfsv4 */ for (i = 0; i < NFS4_OP_LAST_ONE; i++) { (void)atomic_store_uint64_t(&global_st.v4.op[i], 0); } /* Reset all ops counters of lock manager */ for (i = 0; i < NLM4_FAILED; i++) { (void)atomic_store_uint64_t(&global_st.lm.op[i], 0); } /* Reset all ops counters of mountd */ for (i = 0; i < MOUNTPROC3_EXPORT; i++) { (void)atomic_store_uint64_t(&global_st.mn.op[i], 0); } /* Reset all ops counters of rquotad */ for (i = 0; i < RQUOTAPROC_SETACTIVEQUOTA; i++) { (void)atomic_store_uint64_t(&global_st.qt.op[i], 0); } reset_nfsv3_stats(&global_st.nfsv3); reset_nfsv40_stats(&global_st.nfsv40); reset_nfsv41_stats(&global_st.nfsv41); reset_nfsv41_stats(&global_st.nfsv42); /* Uses v41 stats */ reset_mnt_stats(&global_st.mnt); reset_rquota_stats(&global_st.rquota); reset_nlmv4_stats(&global_st.nlm4); } void global_dbus_reset_stats(DBusMessageIter *iter) { reset_global_stats(); reset_export_stats(); reset_client_stats(); } void server_dbus_total_ops(struct export_stats *export_st, DBusMessageIter *iter) { struct timespec timestamp; now(×tamp); dbus_append_timestamp(iter, ×tamp); server_dbus_total(export_st, iter); } void server_dbus_fast_ops(DBusMessageIter *iter) { struct timespec timestamp; now(×tamp); dbus_append_timestamp(iter, ×tamp); global_dbus_fast(iter); } void global_dbus_total_ops(DBusMessageIter *iter) { struct timespec timestamp; now(×tamp); dbus_append_timestamp(iter, ×tamp); global_dbus_total(iter); } void server_reset_stats(DBusMessageIter *iter) { struct timespec timestamp; now(×tamp); dbus_append_timestamp(iter, ×tamp); global_dbus_reset_stats(iter); } #ifdef _USE_9P void server_dbus_9p_iostats(struct _9p_stats *_9pp, DBusMessageIter *iter) { struct timespec timestamp; now(×tamp); dbus_append_timestamp(iter, ×tamp); server_dbus_iostats(&_9pp->read, iter); server_dbus_iostats(&_9pp->write, iter); } void server_dbus_9p_transstats(struct _9p_stats *_9pp, DBusMessageIter *iter) { struct timespec timestamp; now(×tamp); dbus_append_timestamp(iter, ×tamp); server_dbus_transportstats(&_9pp->trans, iter); } void server_dbus_9p_opstats(struct _9p_stats *_9pp, u8 opcode, DBusMessageIter *iter) { struct timespec timestamp; now(×tamp); dbus_append_timestamp(iter, ×tamp); server_dbus_op_stats(_9pp->opcodes[opcode], iter); } #endif /** * @brief Report layout statistics as a struct * * struct layout { * uint64_t total_layouts; * uint64_t errors; * uint64_t delays; * } * * @param iop [IN] pointer to xfer op sub-structure of interest * @param iter [IN] interator in reply stream to fill */ static void server_dbus_layouts(struct layout_op *lop, DBusMessageIter *iter) { DBusMessageIter struct_iter; dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &struct_iter); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &lop->total); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &lop->errors); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT64, &lop->delays); dbus_message_iter_close_container(iter, &struct_iter); } void server_dbus_v41_layouts(struct nfsv41_stats *v41p, DBusMessageIter *iter) { struct timespec timestamp; now(×tamp); dbus_append_timestamp(iter, ×tamp); server_dbus_layouts(&v41p->getdevinfo, iter); server_dbus_layouts(&v41p->layout_get, iter); server_dbus_layouts(&v41p->layout_commit, iter); server_dbus_layouts(&v41p->layout_return, iter); server_dbus_layouts(&v41p->recall, iter); } void server_dbus_v42_layouts(struct nfsv41_stats *v42p, DBusMessageIter *iter) { struct timespec timestamp; now(×tamp); dbus_append_timestamp(iter, ×tamp); server_dbus_layouts(&v42p->getdevinfo, iter); server_dbus_layouts(&v42p->layout_get, iter); server_dbus_layouts(&v42p->layout_commit, iter); server_dbus_layouts(&v42p->layout_return, iter); server_dbus_layouts(&v42p->recall, iter); } /** * @brief Report delegation statistics as a struct * * @param iop [IN] pointer to xfer op sub-structure of interest * @param iter [IN] interator in reply stream to fill */ void server_dbus_delegations(struct deleg_stats *ds, DBusMessageIter *iter) { struct timespec timestamp; DBusMessageIter struct_iter; now(×tamp); dbus_append_timestamp(iter, ×tamp); dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &struct_iter); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT32, &ds->curr_deleg_grants); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT32, &ds->tot_recalls); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT32, &ds->failed_recalls); dbus_message_iter_append_basic(&struct_iter, DBUS_TYPE_UINT32, &ds->num_revokes); dbus_message_iter_close_container(iter, &struct_iter); } #endif /* USE_DBUS */ /** * @brief Free statistics storage * * The struct itself is not freed because it is a member * of either the client manager struct or the export struct. * * @param statsp [IN] pointer to stats to be cleaned */ void server_stats_free(struct gsh_stats *statsp) { if (statsp->nfsv3 != NULL) { gsh_free(statsp->nfsv3); statsp->nfsv3 = NULL; } if (statsp->mnt != NULL) { gsh_free(statsp->mnt); statsp->mnt = NULL; } if (statsp->nlm4 != NULL) { gsh_free(statsp->nlm4); statsp->nlm4 = NULL; } if (statsp->rquota != NULL) { gsh_free(statsp->rquota); statsp->rquota = NULL; } if (statsp->nfsv40 != NULL) { gsh_free(statsp->nfsv40); statsp->nfsv40 = NULL; } if (statsp->nfsv41 != NULL) { gsh_free(statsp->nfsv41); statsp->nfsv41 = NULL; } if (statsp->nfsv42 != NULL) { gsh_free(statsp->nfsv42); statsp->nfsv42 = NULL; } #ifdef _USE_9P if (statsp->_9p != NULL) { u8 opc; for (opc = 0; opc <= _9P_RWSTAT; opc++) { if (statsp->_9p->opcodes[opc] != NULL) gsh_free(statsp->_9p->opcodes[opc]); } gsh_free(statsp->_9p); statsp->_9p = NULL; } #endif } /** @} */ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/support/strlcpy.c�������������������������������������������������������������0000664�0000000�0000000�00000003112�13242724102�0020052�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef HAVE_STRLCPY #include <sys/types.h> /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t strlcpy(char *dst, const char *src, size_t siz) { register char *d = dst; register const char *s = src; register size_t n = siz; /* Copy as many bytes as will fit */ if (n != 0 && --n != 0) { do { *d++ = *s; if (*s++ == 0) break; } while (--n != 0); } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) { /* NUL-terminate dst */ *d = '\0'; } /* Search for NUL in string */ while (*s++) { /* do nothing */ } } /* count does not include NUL */ return s - src - 1; } #endif ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/support/strnlen.c�������������������������������������������������������������0000664�0000000�0000000�00000000335�13242724102�0020043�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ #ifndef HAVE_STRNLEN #include <sys/types.h> #include <stdlib.h> size_t gsh_strnlen(const char *s, size_t max) { register const char *p; for (p = s; *p && max--; ++p) { /* do nothing */ } return p - s; } #endif ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/support/uid2grp.c�������������������������������������������������������������0000664�0000000�0000000�00000022344�13242724102�0017736�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @addtogroup uid2grp * @{ */ /** * @file uid2grp.c * @brief Uid to group list conversion */ #include "config.h" #include "gsh_rpc.h" #include "nfs_core.h" #include <unistd.h> /* for using gethostname */ #include <stdlib.h> /* for using exit */ #include <strings.h> #include <string.h> #include <sys/types.h> #include <pwd.h> #include <grp.h> #include <stdint.h> #include <stdbool.h> #include "common_utils.h" #include "uid2grp.h" /* group_data has a reference counter. If it goes to zero, it implies * that it is out of the cache (AVL trees) and should be freed. The * reference count is 1 when we put it into AVL trees. We decrement when * we take it out of AVL trees. Also incremented when we pass this to * out siders (uid2grp and friends) and decremented when they are done * (in uid2grp_unref()). * * When a group_data needs to be removed or expired after a certain * timeout, we take it out of the cache (AVL trees). When everyone using * the group_data are done, the refcount will go to zero at which point * we free group_data as well as the buffer holding supplementary * groups. */ void uid2grp_hold_group_data(struct group_data *gdata) { PTHREAD_MUTEX_lock(&gdata->lock); gdata->refcount++; PTHREAD_MUTEX_unlock(&gdata->lock); } void uid2grp_release_group_data(struct group_data *gdata) { unsigned int refcount; PTHREAD_MUTEX_lock(&gdata->lock); refcount = --gdata->refcount; PTHREAD_MUTEX_unlock(&gdata->lock); if (refcount == 0) { gsh_free(gdata->groups); gsh_free(gdata); } else if (refcount == (unsigned int)-1) { LogAlways(COMPONENT_IDMAPPER, "negative refcount on gdata: %p", gdata); } } /* Allocate supplementary groups buffer */ static bool my_getgrouplist_alloc(char *user, gid_t gid, struct group_data *gdata) { int ngroups = 0; gid_t *groups = NULL; /* We call getgrouplist() with 0 ngroups first. This should always * return -1, and ngroups should be set to the actual number of * groups the user is in. The manpage doesn't say anything * about errno value, it was usually zero but was set to 34 * (ERANGE) under some environments. ngroups was set correctly * no matter what the errno value is! * * We assume that ngroups is correctly set, no matter what the * errno value is. The man page says, "The ngroups argument * is a value-result argument: on return it always contains * the number of groups found for user." */ (void)getgrouplist(user, gid, NULL, &ngroups); /* Allocate gdata->groups with the right size then call * getgrouplist() a second time to get the actual group list. */ if (ngroups > 0) groups = gsh_malloc(ngroups * sizeof(gid_t)); if (getgrouplist(user, gid, groups, &ngroups) == -1) { LogEvent(COMPONENT_IDMAPPER, "getgrouplist for user: %s failed retrying", user); gsh_free(groups); /* Try with the largest ngroups we support */ ngroups = 1000; groups = gsh_malloc(ngroups * sizeof(gid_t)); if (getgrouplist(user, gid, groups, &ngroups) == -1) { LogWarn(COMPONENT_IDMAPPER, "getgrouplist for user:%s failed, ngroups: %d", user, ngroups); gsh_free(groups); return false; } if (ngroups != 0) { /* Resize the buffer, if it fails, gsh_realloc will * abort. */ groups = gsh_realloc(groups, ngroups * sizeof(gid_t)); } else { /* We need to free groups because later code may not. */ gsh_free(groups); groups = NULL; } } gdata->groups = groups; gdata->nbgroups = ngroups; return true; } /* Allocate and fill in group_data structure */ static struct group_data *uid2grp_allocate_by_name( const struct gsh_buffdesc *name) { struct passwd p; struct passwd *pp; char *namebuff = alloca(name->len + 1); struct group_data *gdata = NULL; char *buff; long buff_size; int retval; memcpy(namebuff, name->addr, name->len); *(namebuff + name->len) = '\0'; buff_size = sysconf(_SC_GETPW_R_SIZE_MAX); if (buff_size == -1) { LogMajor(COMPONENT_IDMAPPER, "sysconf failure: %d", errno); return NULL; } buff = alloca(buff_size); retval = getpwnam_r(namebuff, &p, buff, buff_size, &pp); if (retval != 0) { LogEvent(COMPONENT_IDMAPPER, "getpwnam_r for %s failed, error %d", namebuff, retval); return gdata; } if (pp == NULL) { LogEvent(COMPONENT_IDMAPPER, "No matching password record found for name %s", namebuff); return gdata; } gdata = gsh_malloc(sizeof(struct group_data) + strlen(p.pw_name)); gdata->uname.len = strlen(p.pw_name); gdata->uname.addr = (char *)gdata + sizeof(struct group_data); memcpy(gdata->uname.addr, p.pw_name, gdata->uname.len); gdata->uid = p.pw_uid; gdata->gid = p.pw_gid; if (!my_getgrouplist_alloc(p.pw_name, p.pw_gid, gdata)) { gsh_free(gdata); return NULL; } PTHREAD_MUTEX_init(&gdata->lock, NULL); gdata->epoch = time(NULL); gdata->refcount = 0; return gdata; } /* Allocate and fill in group_data structure */ static struct group_data *uid2grp_allocate_by_uid(uid_t uid) { struct passwd p; struct passwd *pp; struct group_data *gdata = NULL; char *buff; long buff_size; int retval; buff_size = sysconf(_SC_GETPW_R_SIZE_MAX); if (buff_size == -1) { LogMajor(COMPONENT_IDMAPPER, "sysconf failure: %d", errno); return NULL; } buff = alloca(buff_size); retval = getpwuid_r(uid, &p, buff, buff_size, &pp); if (retval != 0) { LogEvent(COMPONENT_IDMAPPER, "getpwuid_r for uid %u failed, error %d", uid, retval); return gdata; } if (pp == NULL) { LogInfo(COMPONENT_IDMAPPER, "No matching password record found for uid %u", uid); return gdata; } gdata = gsh_malloc(sizeof(struct group_data) + strlen(p.pw_name)); gdata->uname.len = strlen(p.pw_name); gdata->uname.addr = (char *)gdata + sizeof(struct group_data); memcpy(gdata->uname.addr, p.pw_name, gdata->uname.len); gdata->uid = p.pw_uid; gdata->gid = p.pw_gid; if (!my_getgrouplist_alloc(p.pw_name, p.pw_gid, gdata)) { gsh_free(gdata); return NULL; } PTHREAD_MUTEX_init(&gdata->lock, NULL); gdata->epoch = time(NULL); gdata->refcount = 0; return gdata; } /** * @brief Get supplementary groups given uname * * @param[in] name The name of the user * @param[out] group_data * * @return true if successful, false otherwise */ #define uid2grp_expired(gdata) (time(NULL) - (gdata)->epoch > \ nfs_param.core_param.manage_gids_expiration) bool name2grp(const struct gsh_buffdesc *name, struct group_data **gdata) { bool success = false; uid_t uid = -1; PTHREAD_RWLOCK_rdlock(&uid2grp_user_lock); success = uid2grp_lookup_by_uname(name, &uid, gdata); /* Handle common case first */ if (success && !uid2grp_expired(*gdata)) { uid2grp_hold_group_data(*gdata); PTHREAD_RWLOCK_unlock(&uid2grp_user_lock); return success; } PTHREAD_RWLOCK_unlock(&uid2grp_user_lock); if (success) { /* Cache entry is expired */ PTHREAD_RWLOCK_wrlock(&uid2grp_user_lock); uid2grp_remove_by_uname(name); PTHREAD_RWLOCK_unlock(&uid2grp_user_lock); } *gdata = uid2grp_allocate_by_name(name); PTHREAD_RWLOCK_wrlock(&uid2grp_user_lock); if (*gdata) uid2grp_add_user(*gdata); success = uid2grp_lookup_by_uname(name, &uid, gdata); if (success) uid2grp_hold_group_data(*gdata); PTHREAD_RWLOCK_unlock(&uid2grp_user_lock); return success; } /** * @brief Get supplementary groups given uid * * @param[in] uid The uid of the user * @param[out] group_data * * @return true if successful, false otherwise */ bool uid2grp(uid_t uid, struct group_data **gdata) { bool success = false; PTHREAD_RWLOCK_rdlock(&uid2grp_user_lock); success = uid2grp_lookup_by_uid(uid, gdata); /* Handle common case first */ if (success && !uid2grp_expired(*gdata)) { uid2grp_hold_group_data(*gdata); PTHREAD_RWLOCK_unlock(&uid2grp_user_lock); return success; } PTHREAD_RWLOCK_unlock(&uid2grp_user_lock); if (success) { /* Cache entry is expired */ PTHREAD_RWLOCK_wrlock(&uid2grp_user_lock); uid2grp_remove_by_uid(uid); PTHREAD_RWLOCK_unlock(&uid2grp_user_lock); } *gdata = uid2grp_allocate_by_uid(uid); PTHREAD_RWLOCK_wrlock(&uid2grp_user_lock); if (*gdata) uid2grp_add_user(*gdata); success = uid2grp_lookup_by_uid(uid, gdata); if (success) uid2grp_hold_group_data(*gdata); PTHREAD_RWLOCK_unlock(&uid2grp_user_lock); return success; } /* * All callers of uid2grp() and uname2grp must call this * when they are done accessing supplementary groups */ void uid2grp_unref(struct group_data *gdata) { uid2grp_release_group_data(gdata); } /** @} */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/support/uid2grp_cache.c�������������������������������������������������������0000664�0000000�0000000�00000024534�13242724102�0021064�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright CEA/DAM/DIF (2008) * contributeur : Philippe DENIEL philippe.deniel@cea.fr * Thomas LEIBOVICI thomas.leibovici@cea.fr * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA * * --------------------------------------- */ /** * @addtogroup idmapper * @{ */ /** * @file uid_grplist_cache.c * @brief Uid->Group List mapping cache functions */ #include "config.h" #include "log.h" #include "config_parsing.h" #include <string.h> #include <pwd.h> #include <grp.h> #include <unistd.h> #include "gsh_intrinsic.h" #include "gsh_types.h" #include "common_utils.h" #include "avltree.h" #include "uid2grp.h" #include "abstract_atomic.h" /** * @brief User entry in the IDMapper cache */ struct cache_info { uid_t uid; /*< Corresponding UID */ struct gsh_buffdesc uname; struct group_data *gdata; struct avltree_node uname_node; /*< Node in the name tree */ struct avltree_node uid_node; /*< Node in the UID tree */ }; /** * @brief Number of entires in the UID cache, should be prime. */ #define id_cache_size 1009 /** * @brief UID cache, may only be accessed with uid2grp_user_lock * held. If uid2grp_user_lock is held for read, it must be accessed * atomically. (For a write, normal fetch/store is sufficient since * others are kept out.) */ static struct avltree_node *uid_grplist_cache[id_cache_size]; /** * @brief Lock that protects the idmapper user cache */ pthread_rwlock_t uid2grp_user_lock = PTHREAD_RWLOCK_INITIALIZER; /** * @brief Tree of users, by name */ static struct avltree uname_tree; /** * @brief Tree of users, by ID */ static struct avltree uid_tree; /** * @brief Compare two buffers * * Handle the case where one buffer is a left sub-buffer of another * buffer by counting the longer one as larger. * * @param[in] buff1 A buffer * @param[in] buffa Another buffer * * @retval -1 if buff1 is less than buffa * @retval 0 if buff1 and buffa are equal * @retval 1 if buff1 is greater than buffa */ static inline int buffdesc_comparator(const struct gsh_buffdesc *buffa, const struct gsh_buffdesc *buff1) { int mr = memcmp(buff1->addr, buffa->addr, MIN(buff1->len, buffa->len)); if (unlikely(mr == 0)) { if (buff1->len < buffa->len) return -1; else if (buff1->len > buffa->len) return 1; else return 0; } else { return mr; } } /** * @brief Comparison for user names * * @param[in] node1 A node * @param[in] nodea Another node * * @retval -1 if node1 is less than nodea * @retval 0 if node1 and nodea are equal * @retval 1 if node1 is greater than nodea */ static int uname_comparator(const struct avltree_node *node1, const struct avltree_node *nodea) { struct cache_info *user1 = avltree_container_of(node1, struct cache_info, uname_node); struct cache_info *usera = avltree_container_of(nodea, struct cache_info, uname_node); return buffdesc_comparator(&user1->uname, &usera->uname); } /** * @brief Comparison for UIDs * * @param[in] node1 A node * @param[in] nodea Another node * * @retval -1 if node1 is less than nodea * @retval 0 if node1 and nodea are equal * @retval 1 if node1 is greater than nodea */ static int uid_comparator(const struct avltree_node *node1, const struct avltree_node *nodea) { struct cache_info *user1 = avltree_container_of(node1, struct cache_info, uid_node); struct cache_info *usera = avltree_container_of(nodea, struct cache_info, uid_node); if (user1->uid < usera->uid) return -1; else if (user1->uid > usera->uid) return 1; else return 0; } /** * @brief Initialize the IDMapper cache */ void uid2grp_cache_init(void) { avltree_init(&uname_tree, uname_comparator, 0); avltree_init(&uid_tree, uid_comparator, 0); memset(uid_grplist_cache, 0, id_cache_size * sizeof(struct avltree_node *)); } /* Remove given user/cache_info from the AVL trees * * @note The caller must hold uid2grp_user_lock for write. */ static void uid2grp_remove_user(struct cache_info *info) { uid_grplist_cache[info->uid % id_cache_size] = NULL; avltree_remove(&info->uid_node, &uid_tree); avltree_remove(&info->uname_node, &uname_tree); /* We decrement hold on group data when it is * removed from cache trees. */ uid2grp_release_group_data(info->gdata); gsh_free(info); } /** * @brief Add a user entry to the cache * * @note The caller must hold uid2grp_user_lock for write. * * @param[in] group_data that has supplementary groups allocated * * @retval true on success. * @retval false if our reach exceeds our grasp. */ void uid2grp_add_user(struct group_data *gdata) { struct avltree_node *name_node; struct avltree_node *id_node; struct avltree_node *name_node2 = NULL; struct avltree_node *id_node2 = NULL; struct cache_info *info; struct cache_info *tmp; info = gsh_malloc(sizeof(struct cache_info)); info->uid = gdata->uid; info->uname.addr = gdata->uname.addr; info->uname.len = gdata->uname.len; info->gdata = gdata; /* The refcount on group_data should be 1 when we put it in * AVL trees. */ uid2grp_hold_group_data(gdata); /* We may have lost the race to insert. We remove existing * entry and insert this new entry if so! */ name_node = avltree_insert(&info->uname_node, &uname_tree); if (unlikely(name_node)) { tmp = avltree_container_of(name_node, struct cache_info, uname_node); uid2grp_remove_user(tmp); name_node2 = avltree_insert(&info->uname_node, &uname_tree); } id_node = avltree_insert(&info->uid_node, &uid_tree); if (unlikely(id_node)) { /* We should not come here unless someone changed uid of * a user. Remove old entry and re-insert the new * entry. */ tmp = avltree_container_of(id_node, struct cache_info, uid_node); uid2grp_remove_user(tmp); id_node2 = avltree_insert(&info->uid_node, &uid_tree); } uid_grplist_cache[info->uid % id_cache_size] = &info->uid_node; if (name_node && id_node) LogWarn(COMPONENT_IDMAPPER, "shouldn't happen, internal error"); if ((name_node && name_node2) || (id_node && id_node2)) LogWarn(COMPONENT_IDMAPPER, "shouldn't happen, internal error"); } static bool lookup_by_uname(const struct gsh_buffdesc *name, struct cache_info **info) { struct cache_info prototype = { .uname = *name }; struct avltree_node *found_node = avltree_lookup(&prototype.uname_node, &uname_tree); struct cache_info *found_info; void **cache_slot; if (unlikely(!found_node)) return false; found_info = avltree_container_of(found_node, struct cache_info, uname_node); /* I assume that if someone likes this user enough to look it up by name, they'll like it enough to look it up by ID later. */ cache_slot = (void **) &uid_grplist_cache[found_info->uid % id_cache_size]; atomic_store_voidptr(cache_slot, &found_info->uid_node); *info = found_info; return true; } static bool lookup_by_uid(const uid_t uid, struct cache_info **info) { struct cache_info prototype = { .uid = uid }; void **cache_slot = (void **) &uid_grplist_cache[prototype.uid % id_cache_size]; struct avltree_node *found_node = atomic_fetch_voidptr(cache_slot); struct cache_info *found_info; bool found = false; /* Verify that the node found in the cache array is in fact what we * want. */ if (likely(found_node)) { found_info = avltree_container_of(found_node, struct cache_info, uid_node); if (found_info->uid == uid) found = true; } if (unlikely(!found)) { found_node = avltree_lookup(&prototype.uid_node, &uid_tree); if (unlikely(!found_node)) return false; atomic_store_voidptr(cache_slot, found_node); found_info = avltree_container_of(found_node, struct cache_info, uid_node); } *info = found_info; return true; } /** * @brief Look up a user by name * * @note The caller must hold uid2grp_user_lock for read. * * @param[in] name The user name to look up. * @param[out] uid The user ID found. May be NULL if the caller * isn't interested in the UID. (This seems * unlikely.) * @gdata[out] group_data containing supplementary groups. * * @retval true on success. * @retval false if we need to try, try again. */ bool uid2grp_lookup_by_uname(const struct gsh_buffdesc *name, uid_t *uid, struct group_data **gdata) { struct cache_info *info; bool success; success = lookup_by_uname(name, &info); if (success) { *gdata = info->gdata; *uid = info->gdata->uid; } return success; } /** * @brief Look up a user by ID * * @note The caller must hold uid2grp_user_lock for read. * * @param[in] uid The user ID to look up. * @gdata[out] group_data containing supplementary groups. * * @retval true on success. * @retval false if we weren't so successful. */ bool uid2grp_lookup_by_uid(const uid_t uid, struct group_data **gdata) { struct cache_info *info; bool success; success = lookup_by_uid(uid, &info); if (success) *gdata = info->gdata; return success; } void uid2grp_remove_by_uid(const uid_t uid) { struct cache_info *info; bool success; success = lookup_by_uid(uid, &info); if (success) uid2grp_remove_user(info); } void uid2grp_remove_by_uname(const struct gsh_buffdesc *name) { struct cache_info *info; bool success; success = lookup_by_uname(name, &info); if (success) uid2grp_remove_user(info); } /** * @brief Wipe out the uid2grp cache */ void uid2grp_clear_cache(void) { struct avltree_node *node; PTHREAD_RWLOCK_wrlock(&uid2grp_user_lock); while ((node = avltree_first(&uname_tree))) { struct cache_info *info = avltree_container_of(node, struct cache_info, uname_node); uid2grp_remove_user(info); } assert(avltree_first(&uid_tree) == NULL); PTHREAD_RWLOCK_unlock(&uid2grp_user_lock); } /** @} */ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/test/�������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13242724102�0015454�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/test/.gitignore���������������������������������������������������������������0000664�0000000�0000000�00000000035�13242724102�0017442�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������test_glist test_mesure_temps ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/test/CMakeLists.txt�����������������������������������������������������������0000664�0000000�0000000�00000001260�13242724102�0020213�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ SET(test_avl_SRCS test_avl.c ) add_executable(test_avl EXCLUDE_FROM_ALL ${test_avl_SRCS}) target_link_libraries(test_avl ${CMAKE_THREAD_LIBS_INIT}) SET(test_mh_avl_SRCS test_mh_avl.c ../support/murmur3.c ) add_executable(test_mh_avl EXCLUDE_FROM_ALL ${test_mh_avl_SRCS}) target_link_libraries(test_mh_avl ${CMAKE_THREAD_LIBS_INIT}) SET(test_glist_SRCS test_glist.c ) add_executable(test_glist EXCLUDE_FROM_ALL ${test_glist_SRCS}) target_link_libraries(test_glist ${CMAKE_THREAD_LIBS_INIT}) SET(test_url_regex_SRCS test_url_regex.c ) add_executable(test_url_regex EXCLUDE_FROM_ALL ${test_url_regex_SRCS}) target_link_libraries(test_url_regex ${CMAKE_THREAD_LIBS_INIT}) ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/test/test_atomic_x86_86.c�����������������������������������������������������0000664�0000000�0000000�00000032436�13242724102�0021165�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include <stdio.h> #include <stdlib.h> #include <assert.h> #if 0 typedef char int8_t; typedef short int16_t; typedef int int32_t; typedef long long int64_t; #endif #if 1 typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; typedef unsigned long long uint64_t; #endif #define inline #include "../include/atomic_x86_64.h" /* ADD 64 */ int64_t x1__atomic_postadd_int64_t(int64_t *p, uint64_t v) { return __sync_fetch_and_add(p, v); } int64_t x2__atomic_postadd_int64_t(int64_t *p, uint64_t v) { return atomic_postadd_int64_t(p, v); } int64_t x1__atomic_add_int64_t(int64_t *p, uint64_t v) { return __sync_add_and_fetch(p, v); } int64_t x2__atomic_add_int64_t(int64_t *p, uint64_t v) { return atomic_add_int64_t(p, v); } /* SUB 64 */ int64_t x1__atomic_postsub_int64_t(int64_t *p, uint64_t v) { return __sync_fetch_and_sub(p, v); } int64_t x2__atomic_postsub_int64_t(int64_t *p, uint64_t v) { return atomic_postsub_int64_t(p, v); } int64_t x1__atomic_sub_int64_t(int64_t *p, uint64_t v) { return __sync_sub_and_fetch(p, v); } int64_t x2__atomic_sub_int64_t(int64_t *p, uint64_t v) { return atomic_sub_int64_t(p, v); } /* ADD U64 */ uint64_t x1__atomic_postadd_uint64_t(uint64_t *p, uint64_t v) { return __sync_fetch_and_add(p, v); } uint64_t x2__atomic_postadd_uint64_t(uint64_t *p, uint64_t v) { return atomic_postadd_uint64_t(p, v); } uint64_t x1__atomic_add_uint64_t(uint64_t *p, uint64_t v) { return __sync_add_and_fetch(p, v); } uint64_t x2__atomic_add_uint64_t(uint64_t *p, uint64_t v) { return atomic_add_uint64_t(p, v); } /* SUB U64 */ uint64_t x1__atomic_postsub_uint64_t(uint64_t *p, uint64_t v) { return __sync_fetch_and_sub(p, v); } uint64_t x2__atomic_postsub_uint64_t(uint64_t *p, uint64_t v) { return atomic_postsub_uint64_t(p, v); } uint64_t x1__atomic_sub_uint64_t(uint64_t *p, uint64_t v) { return __sync_sub_and_fetch(p, v); } uint64_t x2__atomic_sub_uint64_t(uint64_t *p, uint64_t v) { return atomic_sub_uint64_t(p, v); } /* OR 64 */ uint64_t x1__atomic_postset_uint64_t_bits(uint64_t *p, uint64_t v) { return __sync_fetch_and_or(p, v); } uint64_t x2__atomic_postset_uint64_t_bits(uint64_t *p, uint64_t v) { return atomic_postset_uint64_t_bits(p, v); } uint64_t x1__atomic_set_uint64_t_bits(uint64_t *p, uint64_t v) { return __sync_or_and_fetch(p, v); } uint64_t x2__atomic_set_uint64_t_bits(uint64_t *p, uint64_t v) { return atomic_set_uint64_t_bits(p, v); } /* AND 64 */ uint64_t x1__atomic_postclear_uint64_t_bits(uint64_t *p, uint64_t v) { return __sync_fetch_and_and(p, ~v); } uint64_t x2__atomic_postclear_uint64_t_bits(uint64_t *p, uint64_t v) { return atomic_postclear_uint64_t_bits(p, v); } uint64_t x1__atomic_clear_uint64_t_bits(uint64_t *p, uint64_t v) { return __sync_and_and_fetch(p, ~v); } uint64_t x2__atomic_clear_uint64_t_bits(uint64_t *p, uint64_t v) { return atomic_clear_uint64_t_bits(p, v); } /* ADD 32 */ int32_t x1__atomic_postadd_int32_t(int32_t *p, uint32_t v) { return __sync_fetch_and_add(p, v); } int32_t x2__atomic_postadd_int32_t(int32_t *p, uint32_t v) { return atomic_postadd_int32_t(p, v); } int32_t x1__atomic_add_int32_t(int32_t *p, uint32_t v) { return __sync_add_and_fetch(p, v); } int32_t x2__atomic_add_int32_t(int32_t *p, uint32_t v) { return atomic_add_int32_t(p, v); } /* SUB 32 */ int32_t x1__atomic_postsub_int32_t(int32_t *p, uint32_t v) { return __sync_fetch_and_sub(p, v); } int32_t x2__atomic_postsub_int32_t(int32_t *p, uint32_t v) { return atomic_postsub_int32_t(p, v); } int32_t x1__atomic_sub_int32_t(int32_t *p, uint32_t v) { return __sync_sub_and_fetch(p, v); } int32_t x2__atomic_sub_int32_t(int32_t *p, uint32_t v) { return atomic_sub_int32_t(p, v); } /* ADD U32 */ uint32_t x1__atomic_postadd_uint32_t(uint32_t *p, uint32_t v) { return __sync_fetch_and_add(p, v); } uint32_t x2__atomic_postadd_uint32_t(uint32_t *p, uint32_t v) { return atomic_postadd_uint32_t(p, v); } uint32_t x1__atomic_add_uint32_t(uint32_t *p, uint32_t v) { return __sync_add_and_fetch(p, v); } uint32_t x2__atomic_add_uint32_t(uint32_t *p, uint32_t v) { return atomic_add_uint32_t(p, v); } /* SUB U32 */ uint32_t x1__atomic_postsub_uint32_t(uint32_t *p, uint32_t v) { return __sync_fetch_and_sub(p, v); } uint32_t x2__atomic_postsub_uint32_t(uint32_t *p, uint32_t v) { return atomic_postsub_uint32_t(p, v); } uint32_t x1__atomic_sub_uint32_t(uint32_t *p, uint32_t v) { return __sync_sub_and_fetch(p, v); } uint32_t x2__atomic_sub_uint32_t(uint32_t *p, uint32_t v) { return atomic_sub_uint32_t(p, v); } /* OR 32 */ uint32_t x1__atomic_postset_uint32_t_bits(uint32_t *p, uint32_t v) { return __sync_fetch_and_or(p, v); } uint32_t x2__atomic_postset_uint32_t_bits(uint32_t *p, uint32_t v) { return atomic_postset_uint32_t_bits(p, v); } uint32_t x1__atomic_set_uint32_t_bits(uint32_t *p, uint32_t v) { return __sync_or_and_fetch(p, v); } uint32_t x2__atomic_set_uint32_t_bits(uint32_t *p, uint32_t v) { return atomic_set_uint32_t_bits(p, v); } /* AND 32 */ uint32_t x1__atomic_postclear_uint32_t_bits(uint32_t *p, uint32_t v) { return __sync_fetch_and_and(p, ~v); } uint32_t x2__atomic_postclear_uint32_t_bits(uint32_t *p, uint32_t v) { return atomic_postclear_uint32_t_bits(p, v); } uint32_t x1__atomic_clear_uint32_t_bits(uint32_t *p, uint32_t v) { return __sync_and_and_fetch(p, ~v); } uint32_t x2__atomic_clear_uint32_t_bits(uint32_t *p, uint32_t v) { return atomic_clear_uint32_t_bits(p, v); } /* ADD 16 */ int16_t x1__atomic_postadd_int16_t(int16_t *p, uint16_t v) { return __sync_fetch_and_add(p, v); } int16_t x2__atomic_postadd_int16_t(int16_t *p, uint16_t v) { return atomic_postadd_int16_t(p, v); } int16_t x1__atomic_add_int16_t(int16_t *p, uint16_t v) { return __sync_add_and_fetch(p, v); } int16_t x2__atomic_add_int16_t(int16_t *p, uint16_t v) { return atomic_add_int16_t(p, v); } /* SUB 16 */ int16_t x1__atomic_postsub_int16_t(int16_t *p, uint16_t v) { return __sync_fetch_and_sub(p, v); } int16_t x2__atomic_postsub_int16_t(int16_t *p, uint16_t v) { return atomic_postsub_int16_t(p, v); } int16_t x1__atomic_sub_int16_t(int16_t *p, uint16_t v) { return __sync_sub_and_fetch(p, v); } int16_t x2__atomic_sub_int16_t(int16_t *p, uint16_t v) { return atomic_sub_int16_t(p, v); } /* ADD U16 */ uint16_t x1__atomic_postadd_uint16_t(uint16_t *p, uint16_t v) { return __sync_fetch_and_add(p, v); } uint16_t x2__atomic_postadd_uint16_t(uint16_t *p, uint16_t v) { return atomic_postadd_uint16_t(p, v); } uint16_t x1__atomic_add_uint16_t(uint16_t *p, uint16_t v) { return __sync_add_and_fetch(p, v); } uint16_t x2__atomic_add_uint16_t(uint16_t *p, uint16_t v) { return atomic_add_uint16_t(p, v); } /* SUB U16 */ uint16_t x1__atomic_postsub_uint16_t(uint16_t *p, uint16_t v) { return __sync_fetch_and_sub(p, v); } uint16_t x2__atomic_postsub_uint16_t(uint16_t *p, uint16_t v) { return atomic_postsub_uint16_t(p, v); } uint16_t x1__atomic_sub_uint16_t(uint16_t *p, uint16_t v) { return __sync_sub_and_fetch(p, v); } uint16_t x2__atomic_sub_uint16_t(uint16_t *p, uint16_t v) { return atomic_sub_uint16_t(p, v); } /* OR 16 */ uint16_t x1__atomic_postset_uint16_t_bits(uint16_t *p, uint16_t v) { return __sync_fetch_and_or(p, v); } uint16_t x2__atomic_postset_uint16_t_bits(uint16_t *p, uint16_t v) { return atomic_postset_uint16_t_bits(p, v); } uint16_t x1__atomic_set_uint16_t_bits(uint16_t *p, uint16_t v) { return __sync_or_and_fetch(p, v); } uint16_t x2__atomic_set_uint16_t_bits(uint16_t *p, uint16_t v) { return atomic_set_uint16_t_bits(p, v); } /* AND 16 */ uint16_t x1__atomic_postclear_uint16_t_bits(uint16_t *p, uint16_t v) { return __sync_fetch_and_and(p, ~v); } uint16_t x2__atomic_postclear_uint16_t_bits(uint16_t *p, uint16_t v) { return atomic_postclear_uint16_t_bits(p, v); } uint16_t x1__atomic_clear_uint16_t_bits(uint16_t *p, uint16_t v) { return __sync_and_and_fetch(p, ~v); } uint16_t x2__atomic_clear_uint16_t_bits(uint16_t *p, uint16_t v) { return atomic_clear_uint16_t_bits(p, v); } /* ADD 8 */ int8_t x1__atomic_postadd_int8_t(int8_t *p, uint8_t v) { return __sync_fetch_and_add(p, v); } int8_t x2__atomic_postadd_int8_t(int8_t *p, uint8_t v) { return atomic_postadd_int8_t(p, v); } int8_t x1__atomic_add_int8_t(int8_t *p, uint8_t v) { return __sync_add_and_fetch(p, v); } int8_t x2__atomic_add_int8_t(int8_t *p, uint8_t v) { return atomic_add_int8_t(p, v); } /* SUB 8 */ int8_t x1__atomic_postsub_int8_t(int8_t *p, uint8_t v) { return __sync_fetch_and_sub(p, v); } int8_t x2__atomic_postsub_int8_t(int8_t *p, uint8_t v) { return atomic_postsub_int8_t(p, v); } int8_t x1__atomic_sub_int8_t(int8_t *p, uint8_t v) { return __sync_sub_and_fetch(p, v); } int8_t x2__atomic_sub_int8_t(int8_t *p, uint8_t v) { return atomic_sub_int8_t(p, v); } /* ADD U8 */ uint8_t x1__atomic_postadd_uint8_t(uint8_t *p, uint8_t v) { return __sync_fetch_and_add(p, v); } uint8_t x2__atomic_postadd_uint8_t(uint8_t *p, uint8_t v) { return atomic_postadd_uint8_t(p, v); } uint8_t x1__atomic_add_uint8_t(uint8_t *p, uint8_t v) { return __sync_add_and_fetch(p, v); } uint8_t x2__atomic_add_uint8_t(uint8_t *p, uint8_t v) { return atomic_add_uint8_t(p, v); } /* SUB U8 */ uint8_t x1__atomic_postsub_uint8_t(uint8_t *p, uint8_t v) { return __sync_fetch_and_sub(p, v); } uint8_t x2__atomic_postsub_uint8_t(uint8_t *p, uint8_t v) { return atomic_postsub_uint8_t(p, v); } uint8_t x1__atomic_sub_uint8_t(uint8_t *p, uint8_t v) { return __sync_sub_and_fetch(p, v); } uint8_t x2__atomic_sub_uint8_t(uint8_t *p, uint8_t v) { return atomic_sub_uint8_t(p, v); } /* OR 8 */ uint8_t x1__atomic_postset_uint8_t_bits(uint8_t *p, uint8_t v) { return __sync_fetch_and_or(p, v); } uint8_t x2__atomic_postset_uint8_t_bits(uint8_t *p, uint8_t v) { return atomic_postset_uint8_t_bits(p, v); } uint8_t x1__atomic_set_uint8_t_bits(uint8_t *p, uint8_t v) { return __sync_or_and_fetch(p, v); } uint8_t x2__atomic_set_uint8_t_bits(uint8_t *p, uint8_t v) { return atomic_set_uint8_t_bits(p, v); } /* AND 8 */ uint8_t x1__atomic_postclear_uint8_t_bits(uint8_t *p, uint8_t v) { return __sync_fetch_and_and(p, ~v); } uint8_t x2__atomic_postclear_uint8_t_bits(uint8_t *p, uint8_t v) { return atomic_postclear_uint8_t_bits(p, v); } uint8_t x1__atomic_clear_uint8_t_bits(uint8_t *p, uint8_t v) { return __sync_and_and_fetch(p, ~v); } uint8_t x2__atomic_clear_uint8_t_bits(uint8_t *p, uint8_t v) { return atomic_clear_uint8_t_bits(p, v); } int main(int argc, char **argv) { int i; int64_t p1 = 0; uint64_t p2 = 0; int32_t p3 = 0; uint32_t p4 = 0; int16_t p5 = 0; uint16_t p6 = 0; int8_t p7 = 0; uint16_t p8 = 0; atomic_store_int64_t(&p1, 55); atomic_store_uint64_t(&p2, 55); atomic_store_int32_t(&p3, 55); atomic_store_uint32_t(&p4, 55); atomic_store_int16_t(&p5, 55); atomic_store_uint16_t(&p6, 55); atomic_store_int8_t(&p7, 55); atomic_store_uint8_t(&p8, 55); assert(p1 == 55); assert(p2 == 55); assert(p3 == 55); assert(p4 == 55); assert(p5 == 55); assert(p6 == 55); assert(p7 == 55); assert(p8 == 55); #define test(t,f) \ do {\ for (i = 0; i < 500000; i++) { \ t res1; \ t res2; \ t x1; \ t x2; \ t f1; \ t f2; \ \ res1 = res2 = random(); \ x1 = x2 = random(); \ f1 = f2 = random(); \ \ x1 = x1__##f(&res1, f1); \ x2 = x2__##f(&res2, f2); \ \ fprintf(stderr, "%d: %lld == %lld, %lld == %lld\n", \ __LINE__, (long long) x1, \ (long long) x2, (long long) res1, \ (long long) res2); \ assert(x1 == x2); \ assert(res1 == res2); \ } \ } while (0) test(int64_t, atomic_postadd_int64_t); test(int64_t, atomic_add_int64_t); test(int64_t, atomic_postsub_int64_t); test(int64_t, atomic_sub_int64_t); test(uint64_t, atomic_postadd_uint64_t); test(uint64_t, atomic_add_uint64_t); test(uint64_t, atomic_postsub_uint64_t); test(uint64_t, atomic_sub_uint64_t); test(uint64_t, atomic_postset_uint64_t_bits); test(uint64_t, atomic_set_uint64_t_bits); test(uint64_t, atomic_postclear_uint64_t_bits); test(uint64_t, atomic_clear_uint64_t_bits); test(int32_t, atomic_postadd_int32_t); test(int32_t, atomic_add_int32_t); test(int32_t, atomic_postsub_int32_t); test(int32_t, atomic_sub_int32_t); test(uint32_t, atomic_postadd_uint32_t); test(uint32_t, atomic_add_uint32_t); test(uint32_t, atomic_postsub_uint32_t); test(uint32_t, atomic_sub_uint32_t); test(uint32_t, atomic_postset_uint32_t_bits); test(uint32_t, atomic_set_uint32_t_bits); test(uint32_t, atomic_postclear_uint32_t_bits); test(uint32_t, atomic_clear_uint32_t_bits); test(int16_t, atomic_postadd_int16_t); test(int16_t, atomic_add_int16_t); test(int16_t, atomic_postsub_int16_t); test(int16_t, atomic_sub_int16_t); test(uint16_t, atomic_postadd_uint16_t); test(uint16_t, atomic_add_uint16_t); test(uint16_t, atomic_postsub_uint16_t); test(uint16_t, atomic_sub_uint16_t); test(uint16_t, atomic_postset_uint16_t_bits); test(uint16_t, atomic_set_uint16_t_bits); test(uint16_t, atomic_postclear_uint16_t_bits); test(uint16_t, atomic_clear_uint16_t_bits); test(int8_t, atomic_postadd_int8_t); test(int8_t, atomic_add_int8_t); test(int8_t, atomic_postsub_int8_t); test(int8_t, atomic_sub_int8_t); test(uint8_t, atomic_postadd_uint8_t); test(uint8_t, atomic_add_uint8_t); test(uint8_t, atomic_postsub_uint8_t); test(uint8_t, atomic_sub_uint8_t); test(uint8_t, atomic_postset_uint8_t_bits); test(uint8_t, atomic_set_uint8_t_bits); test(uint8_t, atomic_postclear_uint8_t_bits); test(uint8_t, atomic_clear_uint8_t_bits); return 0; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/test/test_avl.c���������������������������������������������������������������0000664�0000000�0000000�00000045120�13242724102�0017443�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include <limits.h> #include <stdio.h> #include <stdlib.h> #include <stddef.h> #include <string.h> #include "CUnit/Basic.h" #include "avltree.h" #define DEBUG 1 /* STATICS we use across multiple tests */ struct avltree avl_tree_1; struct avltree avl_tree_2; struct avltree avl_tree_100; struct avltree avl_tree_10000; typedef struct avl_unit_val { int refs; struct avltree_node node_k; unsigned long key; unsigned long val; } avl_unit_val_t; int avl_unit_cmpf(const struct avltree_node *lhs, const struct avltree_node *rhs) { avl_unit_val_t *lk, *rk; lk = avltree_container_of(lhs, avl_unit_val_t, node_k); rk = avltree_container_of(rhs, avl_unit_val_t, node_k); if (lk->key < rk->key) return -1; if (lk->key == rk->key) return 0; return 1; } avl_unit_val_t *avl_unit_new_val(unsigned long intval) { avl_unit_val_t *v = gsh_malloc(sizeof(avl_unit_val_t)); memset(v, 0, sizeof(avl_unit_val_t)); v->val = (intval + 1); return v; } void avl_unit_free_val(avl_unit_val_t *v) { gsh_free(v); } void avl_unit_clear_tree(struct avltree *t) { avl_unit_val_t *v; struct avltree_node *node, *next_node; if (avltree_size(t) < 1) return; node = avltree_first(t); while (node) { next_node = avltree_next(node); v = avltree_container_of(node, avl_unit_val_t, node_k); avltree_remove(&v->node_k, &avl_tree_1); avl_unit_free_val(v); node = next_node; } } /* dne */ void avltree_destroy(struct avltree *t) { /* return */ } void avl_unit_clear_and_destroy_tree(struct avltree *t) { avl_unit_clear_tree(t); avltree_destroy(t); } /* * BEGIN SUITE INITIALIZATION and CLEANUP FUNCTIONS */ void avl_unit_PkgInit(void) { /* nothing */ } /* * The suite initialization function. * Initializes resources to be shared across tests. * Returns zero on success, non-zero otherwise. * */ int init_suite1(void) { avltree_init(&avl_tree_1, avl_unit_cmpf, 0 /* flags */); return 0; } /* The suite cleanup function. * Closes the temporary resources used by the tests. * Returns zero on success, non-zero otherwise. */ int clean_suite1(void) { if (avltree_size(&avl_tree_1) > 0) avl_unit_clear_tree(&avl_tree_1); avltree_destroy(&avl_tree_1); return 0; } /* * The suite initialization function. * Initializes resources to be shared across tests. * Returns zero on success, non-zero otherwise. * */ int init_suite2(void) { avltree_init(&avl_tree_2, avl_unit_cmpf, 0 /* flags */); return 0; } /* The suite cleanup function. * Closes the temporary resources used by the tests. * Returns zero on success, non-zero otherwise. */ int clean_suite2(void) { avltree_destroy(&avl_tree_2); return 0; } /* * The suite initialization function. * Initializes resources to be shared across tests. * Returns zero on success, non-zero otherwise. * */ int init_suite100(void) { avltree_init(&avl_tree_100, avl_unit_cmpf, 0 /* flags */); return 0; } /* The suite cleanup function. * Closes the temporary resources used by the tests. * Returns zero on success, non-zero otherwise. */ int clean_suite100(void) { avltree_destroy(&avl_tree_100); return 0; } /* * The suite initialization function. * Initializes resources to be shared across tests. * Returns zero on success, non-zero otherwise. * */ int init_suite10000(void) { avltree_init(&avl_tree_10000, avl_unit_cmpf, 0 /* flags */); return 0; } /* The suite cleanup function. * Closes the temporary resources used by the tests. * Returns zero on success, non-zero otherwise. */ int clean_suite10000(void) { avltree_destroy(&avl_tree_10000); return 0; } /* * The suite initialization function. * Initializes resources to be shared across tests. * Returns zero on success, non-zero otherwise. * */ int init_supremum(void) { avltree_init(&avl_tree_2, avl_unit_cmpf, 0 /* flags */); return 0; } /* The suite cleanup function. * Closes the temporary resources used by the tests. * Returns zero on success, non-zero otherwise. */ int clean_supremum(void) { avltree_destroy(&avl_tree_2); return 0; } /* * END SUITE INITIALIZATION and CLEANUP FUNCTIONS */ /* * BEGIN BASIC TESTS */ void inserts_tree_1(void) { avl_unit_val_t *v; int ix; for (ix = 1; ix < 2; ++ix) { /* new k, v */ v = avl_unit_new_val(ix); /* if actual key cannot be marshalled as a pointer */ v->key = ix; /* insert mapping */ avltree_insert(&v->node_k, &avl_tree_1); } } void check_tree_1(void) { int code = 0; CU_ASSERT_EQUAL(code, 0); } void lookups_tree_1(void) { struct avltree_node *node; avl_unit_val_t *v2, *v = avl_unit_new_val(0); int ix; for (ix = 1; ix < 2; ++ix) { /* reuse v */ v->key = ix; /* lookup mapping */ node = avltree_lookup(&v->node_k, &avl_tree_1); v2 = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT((unsigned long)v2->val == (ix + 1)); } /* free v */ avl_unit_free_val(v); } void deletes_tree_1(void) { struct avltree_node *node; avl_unit_val_t *v2, *v = avl_unit_new_val(0); int ix; for (ix = 1; ix < 2; ++ix) { /* reuse key */ v->key = ix; /* find mapping */ node = avltree_lookup(&v->node_k, &avl_tree_1); v2 = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT(v2->val == (ix + 1)); /* and remove it */ avltree_remove(&v2->node_k, &avl_tree_1); avl_unit_free_val(v2); } /* free search k */ avl_unit_free_val(v); } void inserts_tree_2(void) { avl_unit_val_t *v; int ix; for (ix = 1; ix < 4; ++ix) { /* new k, v */ v = avl_unit_new_val(ix); /* if actual key cannot be marshalled as a pointer */ v->key = ix; /* insert mapping */ avltree_insert(&v->node_k, &avl_tree_2); } } void inserts_tree_2r(void) { avl_unit_val_t *v; int ix; for (ix = 3; ix > 0; --ix) { /* new k, v */ v = avl_unit_new_val(ix); /* if actual key cannot be marshalled as a pointer */ v->key = ix; /* insert mapping */ avltree_insert(&v->node_k, &avl_tree_2); } } void check_tree_2(void) { int code = 0; CU_ASSERT_EQUAL(code, 0); } void lookups_tree_2(void) { struct avltree_node *node; avl_unit_val_t *v2, *v = avl_unit_new_val(0); int ix; for (ix = 1; ix < 4; ++ix) { /* reuse v */ v->key = ix; /* lookup mapping */ node = avltree_lookup(&v->node_k, &avl_tree_2); v2 = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT((unsigned long)v2->val == (ix + 1)); } /* free v */ avl_unit_free_val(v); } void deletes_tree_2(void) { struct avltree_node *node; avl_unit_val_t *v2, *v = avl_unit_new_val(0); int ix; for (ix = 1; ix < 4; ++ix) { /* reuse key */ v->key = ix; /* find mapping */ node = avltree_lookup(&v->node_k, &avl_tree_2); v2 = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT(v2->val == (ix + 1)); /* and remove it */ avltree_remove(&v2->node_k, &avl_tree_2); avl_unit_free_val(v2); } /* free search k */ avl_unit_free_val(v); } void inserts_supremum(void) { avl_unit_val_t *v; int ix; for (ix = 100; ix < 1000; ix += 100) { /* new k, v */ v = avl_unit_new_val(ix); /* if actual key cannot be marshalled as a pointer */ v->key = ix; /* insert mapping */ avltree_insert(&v->node_k, &avl_tree_2); } } void checks_supremum(void) { struct avltree_node *node; avl_unit_val_t *v2, *v = avl_unit_new_val(0); int ix; for (ix = 100; ix < 1000; ix += 100) { /* reuse v */ v->key = (ix - 2); /* a value -just less than ix- */ /* lookup mapping */ node = avltree_sup(&v->node_k, &avl_tree_2); CU_ASSERT(node != NULL); if (node) { v2 = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT((unsigned long)v2->val == (ix + 1)); } /* ok, now find the -infimum- */ v->key = ix + 2; /* a value just above ix */ /* lookup mapping */ node = avltree_inf(&v->node_k, &avl_tree_2); CU_ASSERT(node != NULL); if (node) { v2 = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT((unsigned long)v2->val == (ix + 1)); } } /* now check the boundary case for supremum */ v->key = 500; node = avltree_sup(&v->node_k, &avl_tree_2); CU_ASSERT(node != NULL); if (node) { v2 = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT((unsigned long)v2->val == (v->key + 1)); } /* and infimum */ node = avltree_inf(&v->node_k, &avl_tree_2); CU_ASSERT(node != NULL); if (node) { v2 = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT((unsigned long)v2->val == (v->key + 1)); } /* free v */ avl_unit_free_val(v); } void deletes_supremum(void) { struct avltree_node *node; avl_unit_val_t *v2, *v = avl_unit_new_val(0); int ix; for (ix = 100; ix < 1000; ix += 100) { /* reuse key */ v->key = ix; /* find mapping */ node = avltree_lookup(&v->node_k, &avl_tree_2); v2 = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT(v2->val == (ix + 1)); /* and remove it */ avltree_remove(&v2->node_k, &avl_tree_2); avl_unit_free_val(v2); } /* free search k */ avl_unit_free_val(v); } void inserts_tree_100(void) { avl_unit_val_t *v; int ix; for (ix = 1; ix < 101; ++ix) { /* new k, v */ v = avl_unit_new_val(ix); /* if actual key cannot be marshalled as a pointer */ v->key = ix; /* insert mapping */ avltree_insert(&v->node_k, &avl_tree_100); } } void check_tree_100(void) { int code = 0; CU_ASSERT_EQUAL(code, 0); } void lookups_tree_100(void) { struct avltree_node *node; avl_unit_val_t *v2, *v = avl_unit_new_val(0); int ix; for (ix = 1; ix < 2; ++ix) { /* reuse v */ v->key = ix; /* lookup mapping */ node = avltree_lookup(&v->node_k, &avl_tree_100); v2 = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT((unsigned long)v2->val == (ix + 1)); } /* free v */ avl_unit_free_val(v); } void trav_tree_100(void) { int ntrav = 0; struct avltree_node *node; avl_unit_val_t *v; node = avltree_first(&avl_tree_100); while (node) { ntrav++; v = avltree_container_of(node, avl_unit_val_t, node_k); if ((ntrav % 10) == 0) printf("Node at %p key: %lu val: %lu (%d)\n", v, v->key, v->val, ntrav); node = avltree_next(node); } CU_ASSERT_EQUAL(ntrav, 100); } void deletes_tree_100(void) { struct avltree_node *node; avl_unit_val_t *v2, *v = avl_unit_new_val(0); int ix; for (ix = 1; ix < 101; ++ix) { /* reuse key */ v->key = ix; /* find mapping */ node = avltree_lookup(&v->node_k, &avl_tree_100); v2 = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT(v2->val == (ix + 1)); /* and remove it */ avltree_remove(&v2->node_k, &avl_tree_100); avl_unit_free_val(v2); } /* free search k */ avl_unit_free_val(v); } void inserts_tree_10000(void) { avl_unit_val_t *v; int ix; for (ix = 1; ix < 10001; ++ix) { /* new k, v */ v = avl_unit_new_val(ix); /* if actual key cannot be marshalled as a pointer */ v->key = ix; /* insert mapping */ avltree_insert(&v->node_k, &avl_tree_10000); } } void check_tree_10000(void) { int code = 0; CU_ASSERT_EQUAL(code, 0); } void lookups_tree_10000(void) { struct avltree_node *node; avl_unit_val_t *v2, *v = avl_unit_new_val(0); int ix; for (ix = 1; ix < 2; ++ix) { /* reuse v */ v->key = ix; /* lookup mapping */ node = avltree_lookup(&v->node_k, &avl_tree_10000); v2 = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT((unsigned long)v2->val == (ix + 1)); } /* free v */ avl_unit_free_val(v); } void trav_tree_10000(void) { int ntrav = 0; struct avltree_node *node; avl_unit_val_t *v; node = avltree_first(&avl_tree_10000); while (node) { ntrav++; v = avltree_container_of(node, avl_unit_val_t, node_k); if ((ntrav % 1000) == 0) printf("Node at %p key: %lu val: %lu (%d)\n", v, v->key, v->val, ntrav); node = avltree_next(node); } CU_ASSERT_EQUAL(ntrav, 10000); } void deletes_tree_10000(void) { struct avltree_node *node; avl_unit_val_t *v2, *v = avl_unit_new_val(0); int ix; for (ix = 1; ix < 10001; ++ix) { /* reuse key */ v->key = ix; /* find mapping */ node = avltree_lookup(&v->node_k, &avl_tree_10000); v2 = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT(v2->val == (ix + 1)); /* and remove it */ avltree_remove(&v2->node_k, &avl_tree_10000); avl_unit_free_val(v2); } /* free search k */ avl_unit_free_val(v); } void insert_long_val(struct avltree *t, unsigned long l) { avl_unit_val_t *v; /* new k, v */ v = avl_unit_new_val(l); /* if actual key cannot be marshalled as a pointer */ v->key = l; /* insert mapping */ avltree_insert(&v->node_k, t); } void insert_long_val_safe(struct avltree *t, unsigned long l) { struct avltree_node *node; avl_unit_val_t *v; /* new k, v */ v = avl_unit_new_val(l); v->key = l; node = avltree_lookup(&v->node_k, t); if (node == NULL) avltree_insert(&v->node_k, t); else avl_unit_free_val(v); } void delete_long_val(struct avltree *t, unsigned long l) { struct avltree_node *node; avl_unit_val_t *v, *v2; /* new key, v */ v = avl_unit_new_val(l); v->key = l; /* find mapping */ node = avltree_lookup(&v->node_k, t); v2 = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT(v2->key == l); /* delete mapping */ avltree_remove(&v2->node_k, t); /* free original v */ avl_unit_free_val(v2); /* free search k, v */ avl_unit_free_val(v); } void check_delete_1(void) { struct avltree_node *node; avl_unit_val_t *v, *v2; avl_unit_clear_and_destroy_tree(&avl_tree_1); avltree_init(&avl_tree_1, avl_unit_cmpf, 0 /* flags */); insert_long_val(&avl_tree_1, 4); insert_long_val(&avl_tree_1, 1); insert_long_val(&avl_tree_1, 10010); insert_long_val(&avl_tree_1, 267); insert_long_val(&avl_tree_1, 3382); insert_long_val(&avl_tree_1, 22); insert_long_val(&avl_tree_1, 82); insert_long_val(&avl_tree_1, 3); node = avltree_first(&avl_tree_1); v2 = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT(v2->val == (1 + 1)); delete_long_val(&avl_tree_1, 1); /* new key */ v = avl_unit_new_val(4); v->key = 4; node = avltree_lookup(&v->node_k, &avl_tree_1); v2 = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT(v2->val == (4 + 1)); delete_long_val(&avl_tree_1, 267); v->key = 3382; node = avltree_lookup(&v->node_k, &avl_tree_1); v2 = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT(v2->val == (3382 + 1)); avl_unit_free_val(v); } void check_min_1(void) { avl_unit_val_t *v; struct avltree_node *node; avl_unit_clear_and_destroy_tree(&avl_tree_1); avltree_init(&avl_tree_1, avl_unit_cmpf, 0 /* flags */); insert_long_val(&avl_tree_1, 4); insert_long_val(&avl_tree_1, 10); insert_long_val(&avl_tree_1, 10010); insert_long_val(&avl_tree_1, 267); insert_long_val(&avl_tree_1, 3382); insert_long_val(&avl_tree_1, 22); insert_long_val(&avl_tree_1, 82); node = avltree_first(&avl_tree_1); v = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT(v->val == (4 + 1)); /* insert new min */ insert_long_val(&avl_tree_1, 3); node = avltree_first(&avl_tree_1); v = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT(v->val == (3 + 1)); /* delete current min */ delete_long_val(&avl_tree_1, 3); node = avltree_first(&avl_tree_1); v = avltree_container_of(node, avl_unit_val_t, node_k); CU_ASSERT(v->val == (4 + 1)); } void check_min_2(void) { avl_unit_val_t *v; unsigned long mval, rv; struct avltree_node *node; int ix; srand(time(0)); avl_unit_clear_and_destroy_tree(&avl_tree_1); avltree_init(&avl_tree_1, avl_unit_cmpf, 0 /* flags */); mval = ULONG_MAX; for (ix = 0; ix < 100000; ix++) { rv = rand(); /* in solaris avl, inserting an value that compares equal * to an already inserted value is illegal */ insert_long_val_safe(&avl_tree_1, rv); if ((mval < 0) || (rv < mval)) mval = rv; } node = avltree_first(&avl_tree_1); v = avltree_container_of(node, avl_unit_val_t, node_k); printf("rv: %lu mval: %lu val: %lu\n", rv, mval, v->val - 1); CU_ASSERT(v->val == (mval + 1)); } /* The main() function for setting up and running the tests. * Returns a CUE_SUCCESS on successful running, another * CUnit error code on failure. */ int main(int argc, char *argv[]) { /* initialize the CUnit test registry... get this party started */ if (CU_initialize_registry() != CUE_SUCCESS) return CU_get_error(); /* General avl_tree test. */ CU_TestInfo avl_tree_unit_1_arr[] = { {"Tree insertions 1.", inserts_tree_1} , {"Tree check 1.", check_tree_1} , {"Tree lookups 1.", lookups_tree_1} , {"Tree deletes 1.", deletes_tree_1} , CU_TEST_INFO_NULL, }; CU_TestInfo avl_tree_unit_2_arr[] = { {"Tree insertions 2.", inserts_tree_2} , {"Tree check 2.", check_tree_2} , {"Tree lookups 2.", lookups_tree_2} , {"Tree deletes 2.", deletes_tree_2} , CU_TEST_INFO_NULL, }; CU_TestInfo avl_tree_unit_2r_arr[] = { {"Tree insertions 2.", inserts_tree_2r} , {"Tree check 2.", check_tree_2} , {"Tree lookups 2.", lookups_tree_2} , {"Tree deletes 2.", deletes_tree_2} , CU_TEST_INFO_NULL, }; CU_TestInfo avl_tree_unit_100_arr[] = { {"Tree insertions 100.", inserts_tree_100} , {"Tree check 100.", check_tree_100} , {"Tree lookups 100.", lookups_tree_100} , {"Tree traverse 100.", trav_tree_100} , {"Tree deletes 100.", deletes_tree_100} , CU_TEST_INFO_NULL, }; CU_TestInfo avl_tree_unit_10000_arr[] = { {"Tree insertions 10000.", inserts_tree_10000} , {"Tree lookups 10000.", lookups_tree_10000} , {"Tree check 10000.", check_tree_10000} , {"Tree traverse 10000.", trav_tree_10000} , {"Tree deletes 10000.", deletes_tree_10000} , CU_TEST_INFO_NULL, }; CU_TestInfo avl_tree_unit_min_1_arr[] = { {"Check min after inserts, deletes.", check_min_1} , {"Check lookup after delete.", check_delete_1} , #if 1 /* skews perf */ {"Random min check.", check_min_2} , #endif CU_TEST_INFO_NULL, }; CU_TestInfo avl_tree_unit_supremum[] = { {"Inserts supremum.", inserts_supremum} , {"Checks supremum (and infimum).", checks_supremum} , {"Deletes supremum.", deletes_supremum} , CU_TEST_INFO_NULL, }; CU_SuiteInfo suites[] = { {"Avl operations 1", init_suite1, clean_suite1, avl_tree_unit_1_arr} , {"Avl operations 2", init_suite2, clean_suite2, avl_tree_unit_2_arr} , {"Avl operations 2 R", init_suite2, clean_suite2, avl_tree_unit_2r_arr} , {"Avl operations 100", init_suite100, clean_suite100, avl_tree_unit_100_arr} , {"Avl operations 10000", init_suite10000, clean_suite10000, avl_tree_unit_10000_arr} , {"Check min 1", init_suite1, clean_suite1, avl_tree_unit_min_1_arr} , {"Check supremum", init_supremum, clean_supremum, avl_tree_unit_supremum} , CU_SUITE_INFO_NULL, }; CU_ErrorCode error = CU_register_suites(suites); /* Initialize the avl_tree package */ avl_unit_PkgInit(); /* Run all tests using the CUnit Basic interface */ CU_basic_set_mode(CU_BRM_VERBOSE); CU_basic_run_tests(); CU_cleanup_registry(); return CU_get_error(); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/test/test_glist.c�������������������������������������������������������������0000664�0000000�0000000�00000006373�13242724102�0020012�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright IBM Corporation, 2010 * Contributor: Aneesh Kumar K.v <aneesh.kumar@linux.vnet.ibm.com> * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * --------------------------------------- */ #include <stdio.h> #include "gsh_list.h" struct myteststruct { int value; struct glist_head glist; }; struct glist_head mytestglist; struct glist_head mytestglist_new; static void print_glist(struct glist_head *head) { struct myteststruct *entry; struct glist_head *glist; glist_for_each(glist, head) { entry = glist_entry(glist, struct myteststruct, glist); printf("The value is %d\n", entry->value); } } void basic_test(void) { struct myteststruct node1; struct myteststruct node2; struct myteststruct node3; struct myteststruct node4; struct myteststruct node1_new; struct myteststruct node2_new; glist_init(&mytestglist); glist_init(&mytestglist_new); node1.value = 10; node2.value = 11; node3.value = 12; glist_add(&mytestglist, &node1.glist); glist_add(&mytestglist, &node2.glist); glist_add(&mytestglist, &node3.glist); print_glist(&mytestglist); printf("Now test tail add\n"); node4.value = 13; glist_add_tail(&mytestglist, &node4.glist); print_glist(&mytestglist); printf("Delete test\n"); glist_del(&node2.glist); print_glist(&mytestglist); node1_new.value = 15; node2_new.value = 16; glist_add(&mytestglist_new, &node1_new.glist); glist_add(&mytestglist_new, &node2_new.glist); printf("Add the below two list\n"); printf("list1\n"); print_glist(&mytestglist); printf("list2\n"); print_glist(&mytestglist_new); glist_add_list_tail(&mytestglist, &mytestglist_new); printf("combined list\n"); print_glist(&mytestglist); } void splice_tail_test(void) { struct myteststruct nodes[10]; int ix; glist_init(&mytestglist); glist_init(&mytestglist_new); for (ix = 0; ix < 10; ++ix) { struct myteststruct *node = &nodes[ix]; node->value = ix + 1; /* add nodes 1-5 to mytestglist */ if (ix < 5) { glist_add_tail(&mytestglist, &node->glist); } else { /* and 6-10 to mytestglist_new */ glist_add_tail(&mytestglist_new, &node->glist); } } printf("List mytestglist should have nodes 1..5\n"); print_glist(&mytestglist); printf("List mytestglist_new should have nodes 6..10\n"); print_glist(&mytestglist_new); printf( "Now after glist_splice_tail mytestglist should have all 10 nodes:\n"); glist_splice_tail(&mytestglist, &mytestglist_new); print_glist(&mytestglist); printf("And mytestglist_new no nodes:\n"); print_glist(&mytestglist_new); } int main(int argc, char *argv[]) { basic_test(); splice_tail_test(); return 0; } ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/test/test_mh_avl.c������������������������������������������������������������0000664�0000000�0000000�00000022046�13242724102�0020131�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include <limits.h> #include <stdio.h> #include <stdlib.h> #include <stddef.h> #include <string.h> #include <assert.h> #include "CUnit/Basic.h" #include "avltree.h" #include "murmur3.h" #define DEBUG 1 /* STATICS we use across multiple tests */ struct avltree avl_tree_1; /* dirent-like structure */ typedef struct avl_unit_val { struct avltree_node node_n; struct avltree_node node_hk; struct { uint64_t k; uint32_t p; /* nprobes , eff. metric */ } hk; char *name; uint64_t fsal_cookie; } avl_unit_val_t; static inline int avl_unit_hk_cmpf(const struct avltree_node *lhs, const struct avltree_node *rhs) { avl_unit_val_t *lk, *rk; lk = avltree_container_of(lhs, avl_unit_val_t, node_hk); rk = avltree_container_of(rhs, avl_unit_val_t, node_hk); if (lk->hk.k < rk->hk.k) return -1; if (lk->hk.k == rk->hk.k) return 0; return 1; } avl_unit_val_t *avl_unit_new_val(const char *name) { avl_unit_val_t *v = gsh_malloc(sizeof(avl_unit_val_t)); memset(v, 0, sizeof(avl_unit_val_t)); v->name = (char *)name; return v; } static int qp_avl_insert(struct avltree *t, avl_unit_val_t *v) { /* * Insert with quadatic, linear probing. A unique k is assured for * any k whenever size(t) < max(uint64_t). * * First try quadratic probing, with coeff. 2 (since m = 2^n.) * A unique k is not assured, since the codomain is not prime. * If this fails, fall back to linear probing from hk.k+1. * * On return, the stored key is in v->hk.k, the iteration * count in v->hk.p. **/ struct avltree_node *tmpnode; uint32_t j, j2; uint32_t hk[4]; assert(avltree_size(t) < UINT64_MAX); MurmurHash3_x64_128(v->name, strlen(v->name), 67, hk); memcpy(&v->hk.k, hk, 8); for (j = 0; j < UINT64_MAX; j++) { v->hk.k = (v->hk.k + (j * 2)); tmpnode = avltree_insert(&v->node_hk, t); if (!tmpnode) { /* success, note iterations and return */ v->hk.p = j; return 0; } } /* warn debug */ memcpy(&v->hk.k, hk, 8); for (j2 = 1 /* tried j=0 */; j2 < UINT64_MAX; j2++) { v->hk.k = v->hk.k + j2; tmpnode = avltree_insert(&v->node_hk, t); if (!tmpnode) { /* success, note iterations and return */ v->hk.p = j + j2; return 0; } j2++; } /* warn crit */ return -1; } /* This permits inlining, also elides some locally-scoped stores in * do_lookup. */ static inline struct avltree_node * avltree_inline_lookup(const struct avltree_node *key, const struct avltree *tree) { struct avltree_node *node = tree->root; int is_left = 0, res = 0; while (node) { res = avl_unit_hk_cmpf(node, key); /* may inline */ if (res == 0) return node; is_left = res; if (is_left > 0) node = node->left; else node = node->right; } return NULL; } static avl_unit_val_t *qp_avl_lookup_s(struct avltree *t, avl_unit_val_t *v, int maxj) { struct avltree_node *node; avl_unit_val_t *v2; uint32_t j, j2; uint32_t hk[4]; assert(avltree_size(t) < UINT64_MAX); MurmurHash3_x64_128(v->name, strlen(v->name), 67, hk); memcpy(&v->hk.k, hk, 8); for (j = 0; j < maxj; j++) { v->hk.k = (v->hk.k + (j * 2)); node = avltree_inline_lookup(&v->node_hk, t); if (node) { /* it's almost but not entirely certain that node is * related to v. in the general case, j is also not * constrained to be v->hk.p */ v2 = avltree_container_of(node, avl_unit_val_t, node_hk); if (!strcmp(v->name, v2->name)) return v2; } } /* warn crit */ return NULL; } static struct dir_data { char *name; } dir_data[] = { ".gitignore", "Makefile", "Makefile.gate", "acpi-ext.c", "acpi-processor.c", "acpi.c", "asm-offsets.c", "audit.c", "brl_emu.c", "cpufreq", "crash.c", "crash_dump.c", "cyclone.c", "dma-mapping.c", "efi.c", "efi_stub.S", "entry.S", "entry.h", "err_inject.c", "esi.c", "esi_stub.S", "fsys.S", "fsyscall_gtod_data.h", "ftrace.c", "gate-data.S", "gate.S", "gate.lds.S", "head.S", "ia64_ksyms.c", "init_task.c", "iosapic.c", "irq.c", "irq_ia64.c", "irq_lsapic.c", "ivt.S", "jprobes.S", "kprobes.c", "machine_kexec.c", "machvec.c", "mca.c", "mca_asm.S", "mca_drv.c", "mca_drv.h", "mca_drv_asm.S", "minstate.h", "module.c", "msi_ia64.c", "nr-irqs.c", "numa.c", "pal.S", "palinfo.c", "paravirt.c", "paravirt_inst.h", "paravirt_patch.c", "paravirt_patchlist.c", "paravirt_patchlist.h", "paravirtentry.S", "patch.c", "pci-dma.c", "pci-swiotlb.c", "perfmon.c", "perfmon_default_smpl.c", "perfmon_generic.h", "perfmon_itanium.h", "perfmon_mckinley.h", "perfmon_montecito.h", "process.c", "ptrace.c", "relocate_kernel.S", "sal.c", "salinfo.c", "setup.c", "sigframe.h", "signal.c", "smp.c", "smpboot.c", "sys_ia64.c", "time.c", "topology.c", "traps.c", "unaligned.c", "uncached.c", "unwind.c", "unwind_decoder.c", "unwind_i.h", "vmlinux.lds.S", 0}; void avl_unit_free_val(avl_unit_val_t *v) { gsh_free(v); } void avl_unit_clear_tree(struct avltree *t) { avl_unit_val_t *v; struct avltree_node *node, *next_node; if (avltree_size(t) < 1) return; node = avltree_first(t); while (node) { next_node = avltree_next(node); v = avltree_container_of(node, avl_unit_val_t, node_hk); avltree_remove(&v->node_hk, &avl_tree_1); gsh_free(v->name); avl_unit_free_val(v); node = next_node; } } /* dne */ void avltree_destroy(struct avltree *t) { /* return */ } void avl_unit_clear_and_destroy_tree(struct avltree *t) { avl_unit_clear_tree(t); avltree_destroy(t); } /* * BEGIN SUITE INITIALIZATION and CLEANUP FUNCTIONS */ void avl_unit_PkgInit(void) { /* nothing */ } /* * The suite initialization function. * Initializes resources to be shared across tests. * Returns zero on success, non-zero otherwise. * */ int init_suite1(void) { avltree_init(&avl_tree_1, avl_unit_hk_cmpf, 0 /* flags */); return 0; } /* The suite cleanup function. * Closes the temporary resources used by the tests. * Returns zero on success, non-zero otherwise. */ int clean_suite1(void) { if (avltree_size(&avl_tree_1) > 0) avl_unit_clear_tree(&avl_tree_1); avltree_destroy(&avl_tree_1); return 0; } /* * END SUITE INITIALIZATION and CLEANUP FUNCTIONS */ /* * BEGIN BASIC TESTS */ void inserts_tree_1(void) { avl_unit_val_t *v; char *s; int ix, code; ix = 0; while ((s = dir_data[ix].name) != NULL) { v = avl_unit_new_val(gsh_strdup(s)); code = qp_avl_insert(&avl_tree_1, v); if (code == -1) abort(); if (v->hk.p > 0) printf("%d positive p %d %s\n", ix, v->hk.p, s); ++ix; } } void check_tree_1(void) { int code = 0; CU_ASSERT_EQUAL(code, 0); } void lookups_tree_1(void) { struct avltree_node *node; avl_unit_val_t *v2, *v; char *s; int ix; ix = 0; while ((s = dir_data[ix].name) != NULL) { v = avl_unit_new_val(s); /* lookup mapping */ v2 = qp_avl_lookup_s(&avl_tree_1, v, 1); if (!v2) { abort(); } else { /* printf("%d %d %s\n", ix, v2->hk.p, v2->name); */ } ++ix; } /* free v */ avl_unit_free_val(v); } void deletes_tree_1(void) { avl_unit_clear_tree(&avl_tree_1); avltree_init(&avl_tree_1, avl_unit_hk_cmpf, 0 /* flags */); } void inserts_tree_2(void) { avl_unit_val_t *v; char s[256]; int ix, code; for (ix = 0; ix < 100000; ++ix) { sprintf(s, "file%d", ix); v = avl_unit_new_val(gsh_strdup(s)); code = qp_avl_insert(&avl_tree_1, v); if (code == -1) abort(); if (v->hk.p > 0) printf("%d positive p %d %s\n", ix, v->hk.p, s); } } void check_tree_2(void) { int code = 0; CU_ASSERT_EQUAL(code, 0); } void lookups_tree_2(void) { struct avltree_node *node = NULL; avl_unit_val_t *v2, *v; char s[256]; int ix; /* attempts 100K mits, 100K misses */ for (ix = 0; ix < 200000; ++ix) { sprintf(s, "file%d", ix); v = avl_unit_new_val(s); v2 = qp_avl_lookup_s(&avl_tree_1, v, 1); if (!v2) { if (ix < 100000) { abort(); } else { /* printf("%d %d %s\n", ix, v2->hk.p, v2->name); */ } } } /* free v */ avl_unit_free_val(v); } void deletes_tree_2(void) { avl_unit_clear_tree(&avl_tree_1); } /* The main() function for setting up and running the tests. * Returns a CUE_SUCCESS on successful running, another * CUnit error code on failure. */ int main(int argc, char *argv[]) { /* initialize the CUnit test registry... get this party started */ if (CU_initialize_registry() != CUE_SUCCESS) return CU_get_error(); /* General avl_tree test. */ CU_TestInfo avl_tree_unit_1_arr[] = { {"Tree insertions 1.", inserts_tree_1} , {"Tree check 1.", check_tree_1} , {"Tree lookups 1.", lookups_tree_1} , {"Tree deletes 1.", deletes_tree_1} , {"Tree insertions 2.", inserts_tree_2} , {"Tree check 2.", check_tree_2} , {"Tree lookups 2.", lookups_tree_2} , {"Tree deletes 2.", deletes_tree_2} , CU_TEST_INFO_NULL, }; CU_SuiteInfo suites[] = { {"Rb tree operations 1", init_suite1, clean_suite1, avl_tree_unit_1_arr} , CU_SUITE_INFO_NULL, }; CU_ErrorCode error = CU_register_suites(suites); /* Initialize the avl_tree package */ avl_unit_PkgInit(); /* Run all tests using the CUnit Basic interface */ CU_basic_set_mode(CU_BRM_VERBOSE); CU_basic_run_tests(); CU_cleanup_registry(); return CU_get_error(); } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/test/test_url_regex.c���������������������������������������������������������0000664�0000000�0000000�00000005734�13242724102�0020664�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/param.h> #include <regex.h> /* decompose RADOS URL into (<pool>/)object */ #define RADOS_URL_REGEX \ "([-a-zA-Z0-9_&=]+)/?([-a-zA-Z0-9_&=/]+)?" #define URL1 "my_rados_object" #define URL2 "mypool_baby/myobject_baby" #define URL3 "mypool-baby/myobject-baby" /* match general URL with optional enclosing quotes */ #define CONFIG_URL_REGEX \ "^\"?(rados)://([^\"]+)\"?" #define CONF_URL1 "rados://mypool-baby/myobject-baby" #define CONF_URL2 "\"rados://mypool-baby/myobject-baby\"" #define gsh_malloc malloc static regex_t url_regex; static regex_t conf_url_regex; static inline char *match_dup(regmatch_t *m, char *in) { char *s = NULL; if (m->rm_so >= 0) { int size; size = m->rm_eo - m->rm_so + 1; s = (char *)gsh_malloc(size); snprintf(s, size, "%s", in + m->rm_so); } return s; } void split_pool(char *url) { regmatch_t match[3]; char *x0, *x1, *x2; int code; printf("%s url: %s\n", __func__, url); code = regexec(&url_regex, url, 3, match, 0); if (!code) { /* matched */ regmatch_t *m = &(match[0]); /* matched url pattern is NUL-terminated */ x0 = match_dup(m, url); printf("match0: %s\n", x0); m = &(match[1]); x1 = match_dup(m, url); printf("match1: %s\n", x1); m = &(match[2]); x2 = match_dup(m, url); printf("match2: %s\n", x2); free(x0); free(x1); free(x2); } else if (code == REG_NOMATCH) { printf("%s: Failed to match %s as a config URL\n", __func__, url); } else { char ebuf[100]; regerror(code, &url_regex, ebuf, sizeof(ebuf)); printf("%s: Error in regexec: %s\n", __func__, ebuf); } } void split_url(char *url) { regmatch_t match[3]; char *x0, *x1, *x2; int code; printf("%s url: %s\n", __func__, url); code = regexec(&conf_url_regex, url, 3, match, 0); if (!code) { /* matched */ regmatch_t *m = &(match[0]); /* matched url pattern is NUL-terminated */ x0 = match_dup(m, url); printf("match0: %s\n", x0); m = &(match[1]); x1 = match_dup(m, url); printf("match1: %s\n", x1); m = &(match[2]); x2 = match_dup(m, url); printf("match2: %s\n", x2); free(x0); free(x1); free(x2); } else if (code == REG_NOMATCH) { printf("%s: Failed to match %s as a config URL\n", __func__, url); } else { char ebuf[100]; regerror(code, &url_regex, ebuf, sizeof(ebuf)); printf("%s: Error in regexec: %s\n", __func__, ebuf); } } int main(int argc, char **argv) { printf("hi\n"); int r; r = regcomp(&url_regex, RADOS_URL_REGEX, REG_EXTENDED); if (!!r) { char ebuf[100]; regerror(r, &url_regex, ebuf, sizeof(ebuf)); printf("Error initializing rados url regex %s", ebuf); exit(1); } split_pool(URL1); split_pool(URL2); split_pool(URL3); r = regcomp(&conf_url_regex, CONFIG_URL_REGEX, REG_EXTENDED); if (!!r) { char ebuf[100]; regerror(r, &url_regex, ebuf, sizeof(ebuf)); printf("Error initializing rados url regex %s", ebuf); exit(1); } split_url(CONF_URL1); split_url(CONF_URL2); return 0; } ������������������������������������nfs-ganesha-2.6.0/src/tools/������������������������������������������������������������������������0000775�0000000�0000000�00000000000�13242724102�0015635�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/CMakeLists.txt����������������������������������������������������������0000664�0000000�0000000�00000000173�13242724102�0020376�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ########### install files ############### if(USE_TOOL_MULTILOCK) add_subdirectory(multilock) endif(USE_TOOL_MULTILOCK) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/findlog.sh��������������������������������������������������������������0000775�0000000�0000000�00000012121�13242724102�0017613�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh #------------------------------------------------------------------------------- # # Copyright IBM Corporation, 2011 # Contributor: Frank Filz <ffilz@us.ibm.com> # # # This software is a server that implements the NFS protocol. # # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 3 of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # #------------------------------------------------------------------------------- # # This script is primarily intented to extract all the LogXXX functions calls # from the source code, however it can also be used to find other function calls # given certain limitations. # # Each function call found will have all of it's parameters pulled onto a single # output line, with some white space trimming, and the output line will be # tagged with the file name and line number. # # Limitation 1: # # There must be a blank or tab before the function name. # # Limitation 2: # # Code like th following will stump this script somewhat: # # #define DEFINED_TWICE_WARNING( _str_ ) \ # LogWarn(COMPONENT_CONFIG, \ # "NFS READ_EXPORT: WARNING: %s defined twice !!! (ignored)", _str_ ) # # The result will be to pull in extra code # # Note: # # The file tools/test_findlog.c can be used to see the types of things this # script is expected to deal with. If you find a new case the script doesn't # deal with, add it to this file, and possibly fix the script. #------------------------------------------------------------------------------- # NOTES: FUNC="Log[a-zA-Z][a-zA-Z0-9]*" PRINTF="[vsnf]*printf" FILES="" TODO="find_func_in_file" CSCOPE=0 FINAL="final_massage" LINESONLY=0 DIR="." find_funcs() { sed -ne " :again /[^a-zA-Z_]$FUNC[ \t\]*(.*\*\//{ = p s/\*/\*/ t end } /[^a-zA-Z_]$FUNC[ \t]*(.*)[ \t]*;[ \t\\]*$/{ = p s/;/;/ t end } /[^a-zA-Z_]$FUNC[ \t]*(.*)[ \t]*{[ \t\\]*$/{ = p s/{/{/ t end } /[^a-zA-Z_]$FUNC[ \t]*(.*$/{ = N s/\n[ \t]*/ / s/[ \t\\]*;[ \t\\]*$/;/ t again } /[^a-zA-Z_]$FUNC[ \t]*[\[\=]/d /[^a-zA-Z_]$FUNC[^)]*)/d /[^a-zA-Z_]$FUNC[^;()]*;/d /[^a-zA-Z_]$FUNC[ \t\\]*$/{ = N s/[ \t\\]*;[ \t\\]*$/;/ s/\n[ \t]*(/(/ t again } :end" } debug_mode() { cat $1 | find_funcs } no_xref() { cat $1 | find_funcs | grep -v '^[0-9][0-9]*$' | sed -e "s@.*\($FUNC.*\)@\1@" } final_massage() { sed -e "s@\([0-9][0-9]*\):[0-9:]*[ \t]*\(.*$FUNC.*\)@$1:\1: \2@" } final_lines_only() { sed -e "s@\([0-9][0-9]*\):[0-9:]*[ \t]*\(.*$FUNC.*\)@$1:\1@" } find_func_in_file() { cat $1 | find_funcs | sed -ne ' :done /[0-9][0-9]*:.*\*\//{ p d } /[0-9][0-9]*:.*[{;][ \t\\]*$/{ p d } /[0-9][0-9]*/{ N s/\n/:/ s/;/;/ t done } ' \ | $FINAL $1 } find_files() { while read line; do $TODO $line done } while getopts ":f:l:cp\?k:dxsnD:" OPT; do case $OPT in f) FUNC="$OPTARG" ;; l) FILES="$OPTARG" ;; c) FILES="cscope.files" ;; p) FUNC="$PRINTF" ;; d) TODO="debug_mode" ;; D) DIR="$OPTARG" ;; s) CSCOPE=1 ;; x) TODO="no_xref" ;; n) LINESONLY=1 FINAL="final_lines_only" ;; \?) echo "Usage: findlog.sh [-f function] [-l file] [-p] [-c] [-s] [-x] [-n] [list of files]" echo echo "Extract all examples of specified function calls from the files, combining all" echo "lines of the function call onto one line" echo echo " -f function grep pattern defining function, default is \"$FUNC\"" echo " -l file file containing list of files to search" echo " -D dir search in directory" echo " -c equivalent of -l cscope.files" echo " -p search for printf, equivalent of -f \"$PRINTF\"" echo " -d debug mode, don't massage the output" echo " -s call cscope instead of using script" echo " -x don't output file names and line numbers" echo " -n output file names and line numbers only" echo echo "if no files are specified, will do a find . -name '*.[ch]'" exit ;; esac done if [ $CSCOPE -eq 1 ] then if [ $LINESONLY -eq 0 ] then cscope -d -L -0 $FUNC | sed -e "s@\([^ ][^ ]*\) [^ ][^ ]* \([0-9][0-9]*\) \(Log.*\)@\1:\2:\3@" else cscope -d -L -0 $FUNC | sed -e "s@\([^ ][^ ]*\) [^ ][^ ]* \([0-9][0-9]*\) \(Log.*\)@\1:\2@" fi exit fi shift $(($OPTIND - 1)) if [ -n "$FILES" ] then cat $FILES | find_files if [ -z "$1" ] then exit fi fi if [ -z "$1" ] then find $DIR -name '*.[ch]' | find_files fi while [ -n "$1" ] do $TODO $1 shift done �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/helgrind-suppressions���������������������������������������������������0000664�0000000�0000000�00000013537�13242724102�0022140�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# run with valgrind --suppressions=tools/helgrind-suppressions # to auto-generate more run with --gen-suppressions=all ############################################################################################### # # Ignore known ntirpc issues # ############################################################################################### { <tirpc_control> Helgrind:Race fun:tirpc_control fun:nfs_Init_svc fun:nfs_Init fun:nfs_start fun:main } { <svc_rqst_thrd_run_epoll> Helgrind:Race fun:svc_rqst_thrd_run_epoll fun:svc_rqst_thrd_run fun:rpc_dispatcher_thread fun:mythread_wrapper fun:start_thread fun:clone } { <work_pool_submit> Helgrind:Race fun:work_pool_submit fun:svc_ioq_append fun:svc_vc_reply fun:svc_sendreply fun:nfs_rpc_execute fun:worker_run fun:fridgethr_start_routine fun:mythread_wrapper fun:start_thread fun:clone } { <work_pool_thread> Helgrind:Race fun:work_pool_thread fun:mythread_wrapper fun:start_thread fun:clone } { <svc_rqst_handle_event> Helgrind:Race fun:svc_rqst_handle_event fun:svc_rqst_thrd_run_epoll fun:svc_rqst_thrd_run fun:rpc_dispatcher_thread fun:mythread_wrapper fun:start_thread fun:clone } { <nfs_rpc_getreq_ng> Helgrind:Race fun:nfs_rpc_getreq_ng fun:svc_rqst_handle_event fun:svc_rqst_thrd_run_epoll fun:svc_rqst_thrd_run fun:rpc_dispatcher_thread fun:mythread_wrapper fun:start_thread fun:clone } { <gsh_xprt_decoder_guard> Helgrind:Race fun:gsh_xprt_decoder_guard fun:nfs_rpc_getreq_ng fun:svc_rqst_handle_event fun:svc_rqst_thrd_run_epoll fun:svc_rqst_thrd_run fun:rpc_dispatcher_thread fun:mythread_wrapper fun:start_thread fun:clone } { <nfs_rpc_cond_stall_xprt> Helgrind:Race fun:nfs_rpc_cond_stall_xprt fun:nfs_rpc_getreq_ng fun:svc_rqst_handle_event fun:svc_rqst_thrd_run_epoll fun:svc_rqst_thrd_run fun:rpc_dispatcher_thread fun:mythread_wrapper fun:start_thread fun:clone } { <svc_rqst_rearm_events> Helgrind:Race fun:svc_rqst_rearm_events fun:thr_decode_rpc_requests fun:fridgethr_start_routine fun:mythread_wrapper fun:start_thread fun:clone } { <svc_vc_override_ops> Helgrind:Race fun:svc_vc_override_ops fun:rendezvous_request fun:thr_decode_rpc_request fun:thr_decode_rpc_requests fun:fridgethr_start_routine fun:mythread_wrapper fun:start_thread fun:clone } # This one should be fixed { <thread-still-holds-lock-should-be-fixed> Helgrind:Misc obj:/usr/lib64/libpthread-2.17.so fun:svc_read_vc fun:generic_read_vc fun:fill_input_buf fun:get_input_bytes fun:set_input_fragment fun:xdr_inrec_getbytes fun:xdr_inrec_getlong fun:xdr_getuint32 fun:xdr_dplx_decode fun:svc_vc_recv fun:thr_decode_rpc_request } # This one should be fixed { <evchan_unreg_impl-needs-fixing> Helgrind:LockOrder fun:mutex_lock_WRK fun:pthread_mutex_lock fun:evchan_unreg_impl fun:xprt_unregister fun:svc_vc_destroy fun:svc_release_it fun:gsh_xprt_unref fun:thr_decode_rpc_requests fun:fridgethr_start_routine fun:mythread_wrapper fun:start_thread fun:clone } ############################################################################################### # # Ignore some issues in stats collection # # These are probably harmless but may need a bit more investigation # ############################################################################################### { <record_latency> Helgrind:Race fun:record_latency ... } { <server_stats_nfsv4_op_done> Helgrind:Race fun:server_stats_nfsv4_op_done fun:nfs4_Compound fun:nfs_rpc_execute fun:worker_run fun:fridgethr_start_routine fun:mythread_wrapper fun:start_thread fun:clone } { <Copy_nfs4_state_req-this-one-needs-owner-serialization> Helgrind:Race fun:Copy_nfs4_state_req fun:nfs4_op_close fun:nfs4_Compound fun:nfs_rpc_execute fun:worker_run fun:fridgethr_start_routine fun:mythread_wrapper fun:start_thread fun:clone } ############################################################################################### # # The following are issues where a variable is used in a possibly racy way in a log # message. It is not worth adding a lock just to protect these. # ############################################################################################### { <reserve_lease> Helgrind:Race fun:reserve_lease ... } { <update_lease> Helgrind:Race fun:update_lease ... } { <display_client_id_rec> Helgrind:Race ... fun:display_client_id_rec ... } { <display_nfs4_owner> Helgrind:Race ... fun:display_nfs4_owner ... } ############################################################################################### # # Atomic variables should not contribute to data races. This MIGHT obscure a situation # where an atomic variable is used without an atomic primitive. However, there are many # places where sometimes the atomic is accessed under a lock, and sometimes not, and helgrind # doesn't properly identify that as safe. # ############################################################################################### { <atomic_fetch_uint16_t> Helgrind:Race fun:atomic_fetch_uint16_t ... } { <atomic_fetch_uint32_t> Helgrind:Race fun:atomic_fetch_uint32_t ... } { <atomic_store_uint32_t> Helgrind:Race fun:atomic_store_uint32_t ... } { <atomic_store_uint64_t> Helgrind:Race fun:atomic_store_uint64_t ... } { <atomic_fetch_voidptr> Helgrind:Race fun:atomic_fetch_voidptr ... } { <atomic_store_voidptr> Helgrind:Race fun:atomic_store_voidptr ... } { <atomic_fetch_time_t> Helgrind:Race fun:atomic_fetch_time_t ... } { <atomic_store_time_t> Helgrind:Race fun:atomic_store_time_t ... } �����������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/mount.9P����������������������������������������������������������������0000775�0000000�0000000�00000005230�13242724102�0017214�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash # # Usage: # /sbin/mount.9P spec dir [-fnrsvw] [-o options] restricted=1 if [ $UID -eq 0 ] && [ $UID -eq $EUID ]; then restricted=0 fi # mount(8) in restricted mode (for non-root users) does not allow to use any # mount options, types or so on command line. We have to call mount(8) with # mountpoint only. All necessary options have to be defined in /etc/fstab. # if [ $restricted -eq 1 ]; then exec /bin/mount -i "$@" fi # "Custom" default values (different from kernel default) access="user" uname="root" msize="65560" # Command line building variables opts="" switch="" IPADDR="" MNT="" # Options can sneak in anywhere, e.g. mount -o foo device -o bar dir -o meh # This allows for various flags and takes the first two non-options as # device and dir accordingly while [[ $# -ge $OPTIND ]]; do if getopts ":o:fnrsvwh" opt; then case "$opt" in o) IN_OPTIONS="${OPTARG//,/ }" for OPT in $IN_OPTIONS; do case "${OPT%%=*}" in # pass known options through... version|ro|rw|remount|posixacl|debug|trans|rq|sq|timeout| \ port|cache|loose|mmap|cachetag|noextend|privport|context) opts+="${OPT}," ;; access) access="${OPT#*=}" ;; uname) uname="${OPT#*=}" ;; msize) msize="${OPT#*=}" ;; *) echo "Invalid 9p option: $OPT" exit 1 ;; esac done ;; f|n|r|s|w) switch+="-$opt " ;; v) switch+="-$opt " VERBOSE=1 ;; h) echo "Usage: $0 remotehost:remotedir dir [-fnrsvw] [-o options]" exit 1 ;; \?) echo "Invalid mount option: -$OPTARG" exit 1 ;; esac elif [[ -z "$IPADDR" ]]; then shift $((OPTIND-1)) OPTIND=1 REMOTE="$1" shift if [[ "$REMOTE" != *:* ]]; then echo "Mount destination $REMOTE should be server:path" exit 1 fi SERVERID="${REMOTE%%:*}" aname="${REMOTE#*:}" if ! IPADDR=$(getent ahostsv4 "$SERVERID" 2>&1 | awk '/STREAM/ { print $1; exit }'); then echo "Hostname could not be resolved" exit 1 fi elif [[ -z "$MNT" ]]; then shift $((OPTIND-1)) OPTIND=1 MNT="$1" shift else echo "Extra argument: ${!OPTIND}" exit 1 fi done # the "-i" is mandatory to avoid looping on this helper EXEC_STRING="/bin/mount -i $switch -t 9p $IPADDR $MNT -o ${opts}access=${access},uname=${uname},msize=${msize},aname=${aname}" if [[ -n "$VERBOSE" ]]; then echo "$EXEC_STRING" fi exec $EXEC_STRING ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/multilock/��������������������������������������������������������������0000775�0000000�0000000�00000000000�13242724102�0017640�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/multilock/CMakeLists.txt������������������������������������������������0000664�0000000�0000000�00000001745�13242724102�0022407�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������if(CEPH_FS_CEPH_STATX) add_definitions( -D_FILE_OFFSET_BITS=64 ) include_directories(${CEPHFS_INCLUDE_DIR}) endif(CEPH_FS_CEPH_STATX) SET(multilock_SRCS ml_functions.c multilock.h ) add_executable(ml_console ml_console.c ${multilock_SRCS} ) target_link_libraries(ml_console m ${SYSTEM_LIBRARIES}) add_executable(ml_posix_client ml_posix_client.c ${multilock_SRCS} ) target_link_libraries(ml_posix_client m pthread ${SYSTEM_LIBRARIES}) if(CEPH_FS_CEPH_STATX) add_executable(ml_cephfs_client ml_cephfs_client.c ${multilock_SRCS} ) target_link_libraries(ml_cephfs_client m pthread ${CEPHFS_LIBRARIES} ${SYSTEM_LIBRARIES}) endif(CEPH_FS_CEPH_STATX) if(USE_FSAL_GLUSTER AND USE_LKOWNER) include_directories(${GFAPI_INCLUDE_DIRS}) add_executable(ml_glusterfs_client ml_glusterfs_client.c ${multilock_SRCS} ) target_link_libraries(ml_glusterfs_client m pthread ${GFAPI_LIBRARIES} ${SYSTEM_LIBRARIES}) endif(USE_FSAL_GLUSTER AND USE_LKOWNER) ���������������������������nfs-ganesha-2.6.0/src/tools/multilock/README��������������������������������������������������������0000664�0000000�0000000�00000052604�13242724102�0020527�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������multilock test tool Copyright IBM Corporation, 2012 Contributor: Frank Filz <ffilz@us.ibm.com> This software is a server that implements the NFS protocol. This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA OVERVIEW -------- Multilock is a test tool for testing functionality of POSIX locks. It is directed at testing NFS server and client implementations. The architecture of the tool is a console program (ml_console) and client programs (currently ml_posix_chold). The console directs any number of clients in a sequence of lock operations. The communication between the console and clients is a TCP connection, so the clients may be run on multiple hosts (for example, multiple clients of the same NFS server, or even a local process on the NFS server itself). Other remote file system protocols may also be tested (any remote file system that can be mounted on Linux and provides byte range locks via POSIX fcntl may be exercised using ml_posix_client). The communication protocol with the clients is documented below so additional clients may be implemented to implement other OS flavors (such as Windows), or even directly drive protocols (such as NFS v3/NLM, NFS v4, or CIFS/Samba). The client programs are reasonably capable of being run using ssh which allows them to be used by an automated test case. ml_posix_client is also capable of being run standalone (the protocol used over TCP is plain text so it is trivial to read input from stdin instead of a TCP socket). EXECUTING THE PROGRAMS ---------------------- ml_console --------- ml_console has two modes, scripted and interractive. Several command line options allow some control over output and how errors are handled. Usage: ml_console [-p port] [-s] [-f] [-q] [-x script] [-d] -p port - specify the port to listen to clients on -s - specify strict mode (clients are not polled without EXPECT) -f - specify errors are fatal mode -q - speficy quiet mode -d - speficy dup errors mode (errors are sent to stdout and stderr) -x script - specify script to run -k - syntax check only -e - non-fatal errors, full accounting of errors to stderr, everything to stdout Since there is no method of automatic port discovery, the -p option is reccomended so that the clients can be told which port to contact the server on. The -k option when combined with -x to specify a script will result in the script just being syntax checked. Whenever a script is specified, it is completely syntax checked before the console starts up and runs the script. The -e option results in stderr showing the command leading to the failure, the expected output, and the actual response. This is useful for reporting problems. The -e option also duplicates stderr to stdout just as per -d. ml_posix_client -------------- ml_posix_client has three modes, interractive (standalone), scripted (standalone), and console. There is almost no difference between scripted mode and interracive mode where a stdin is redirected from a file. Usage: ml_posix_client -s server -p port -n name [-q] [-d] [-c path] ml_posix_client -x script [-q] [-d] [-c path] ml_posix_client [-q] [-d] [-c path] ml_posix_client may be run in three modes - In the first mode, the client will be driven by a console. - In the second mode, the client is driven by a script. - In the third mode, the client interractive. -s server - specify the console's hostname or IP address -p port - specify the console's port number -n name - specify the client's name -x script - specify the name of a script to execute -q - specify quiet mode -d - specify dup errors mode (errors are sent to stdout and stderr) -c path - chdir In console mode, the server's address and port must be specified. Also the client must be given a name (which the console will use to identify which client commands are intended for and from which responses are expected). The -c option allows the client to execute in a specific directory. This would allow a test client machine to have several different mounts to the same server using different protocols and have the script executed from the console easily direct testing to any of those mounts without the script needing to be modified (for example, the script can just refer to files by file name without any path). THE COMMAND PROTOCOL -------------------- The following documents the protocol the console uses to send commands to the client. Since this is a plain text protocol, it is also the command language of the clients in interractive mode. Note that the console doesn't necessarily utilize all the flexibility of this protocol (for example, the console will always tag commands, and will always quote strings). Note that the protocol is almost exclusively case insensitive (except for strings). Strings that are at the end of a command need not be quoted. Any leading blanks will not be part of the string. Strings must be quoted if they might be mistaken for an optional parameter. Strings currently can not contain new-line characters. The general format of a command is: [tag] command parameters Commands are terminated with a new-line. The tag is a numeric label for the command. The purpose of the tag is to allow the console to correlate responses with specific commands for verification. The commands and specific parameters are: [tag] OPEN file_pos rw|ro|wo|O_RDWR|O_RDONLY|O_WRONLY [create|creat|O_CREAT] [POSIX|OFD] "file-name" [tag] CLOSE file_pos [tag] READ file_pos length [tag] READ file_pos "string" [tag] WRITE file_pos "data" [tag] SEEK file_pos offset [tag] LOCK file_pos read|write|shared|exclusive|F_RDLCK|F_WRLCK start length [tag] LOCKW file_pos read|write|shared|exclusive|F_RDLCK|F_WRLCK start length [tag] UNLOCK file_pos start length [tag] TEST file_pos read|write|shared|exclusive|F_RDLCK|F_WRLCK start length [tag] LIST file_pos start length [tag] HOP file_pos read|write|shared|exclusive|F_RDLCK|F_WRLCK start length [tag] UNHOP file_pos start length [tag] COMMENT "string" [tag] HELLO "name" [tag] FORK "name" [tag] ALARM seconds [tag] QUIT OPEN ---- The client maintains an array of file references, file_pos specifies which entry is to be used to reference the file for other commands. It operates much like a file descriptor. Files may be opened read-write, read-only, or write-only. Files may also be created on open (other open flags may be supported in the future). The OFD option indicates that Open File Description locks (or equivalent) will be used on this file descriptor. file-name is allowed to be as long as PATH_MAX - 1. CLOSE ----- Closes a file and frees the file_pos for re-use. READ ---- Read data from the current position in the file (use SEEK to set the position). This is fundamentally a record read, though the record contents are expected to be ascii strings. If the second form of the command is given, the length of the string is used as the read length (this form is to be used with console scripting command OK). At most 1024 characters may be read. Note that the file data is expected to be characters and should not have 0 bytes within. WRITE ----- Write the string to the current position in the file (use SEEK to set the position). This is fundamentally a record write, though the record contents are expected to be ascii strings. At most 1024 characters may be written. Note that the file data is expected to be characters and should not have 0 bytes within. SEEK ---- Seek will set the current file position. Use this to select the record to read or write. LOCK ---- Lock requests a non-blocking lock. If the lock can not be granted, the command will return immediately with DENIED status. Locks may be read or write locks (aka shared or exclusive). LOCKW ----- LockW requests a blocking lock. If the lock can not be immediately granted, the command will block. Locks may be read or write locks (aka shared or exclusive). Implementation Note: ml_posix_client sets a default alarm of 5 seconds before this command if an alarm isn't already set. This will prevent script hangs if a lock will never be granted. If a script expects a longer delay, it should set an explicit alarm. TEST ---- Test will test for lock conflict, and if a lock conflict is found, details of a conflicting lock will be returned. Locks may be read or write locks (aka shared or exclusive). LIST ---- List will return a list of locks overlapping the range specified. The expectation is that effectively a series of TEST operations for write locks will will be used to discover the list of locks. COMMENT ------- Comment is a do nothing command. It allows the console to send some commentary to the clients. A comment is at most 1024 bytes. HELLO ----- This is another do nothing command. It's real purpose is a response place holder for the console to be able to receive initial "HELLO" communications from the clients. The name is expected to be no more than 1024 characters. FORK ----- Causes the client to fork, open a new socket back to the console and send a HELLO response on that socket with the forked name. The name may not be longer than 1024 characters. ALARM ----- Alarm is used to set an alarm, it may interrupt a blocked lock (which in fact is it's primary purpose). If an alarm is already running, the existing alarm will be cancelled and a new alarm set. A seconds value of 0 will cancel any existing alarm and not set a new one. QUIT ---- Quit instructs a client to release all locks, close all files, and exit. THE RESPONSE PROTOCOL --------------------- The following documents the protocol the clients uses to send responses to the console. Note that the clients don't utilize all the flexibility of this protocol (for example, ml_posix_client will always quote strings). Note that the protocol is almost exclusively case insensitive (except for strings). Strings that are at the end of a response need not be quoted. Any leading blanks will not be part of the string. Strings must be quoted if they might be mistaken for an optional parameter. Strings currently can not contain new-line characters. The general format is: tag COMMAND STATUS results The specific responses are: tag OPEN OK file_pos file_number tag CLOSE OK file_pos tag READ OK file_pos length "data" tag WRITE OK file_pos length tag SEEK OK file_pos tag LOCK GRANTED file_pos read|write|shared|exclusive|F_RDLCK|F_WRLCK start length tag LOCK DENIED file_pos read|write|shared|exclusive|F_RDLCK|F_WRLCK start length tag LOCKW GRANTED file_pos read|write|shared|exclusive|F_RDLCK|F_WRLCK start length tag LOCKW CANCELED file_pos read|write|shared|exclusive|F_RDLCK|F_WRLCK start length tag LOCKW DEADLOCK file_pos read|write|shared|exclusive|F_RDLCK|F_WRLCK start length tag UNLOCK GRANTED file_pos unlock|F_UNLCK start length tag TEST AVAILABLE file_pos read|write|shared|exclusive|F_RDLCK|F_WRLCK start length tag TEST CONFLICT file_pos pid read|write|shared|exclusive|F_RDLCK|F_WRLCK start length tag LIST AVAILABLE file_pos start length tag LIST DENIED file_pos start length tag LIST CONFLICT file_pos pid read|write|shared|exclusive|F_RDLCK|F_WRLCK start length tag HOP GRANTED file_pos read|write|shared|exclusive|F_RDLCK|F_WRLCK start length tag HOP DENIED file_pos read|write|shared|exclusive|F_RDLCK|F_WRLCK start length tag UNHOP GRANTED file_pos unlock|F_UNLCK start length tag COMMENT OK "string" tag HELLO OK "name" tag ALARM OK seconds tag ALARM CANCELED remain tag ALARM COMPLETED tag QUIT OK tag cmd ERRNO value "string" If a command results in an error, an errno and description of the error will be returned in the response. Currently ml_console doesn't really do anything to analyze these errors. They will cause failure of scripts of course. OPEN ---- Returns OK or ERRNO. A successful OPEN will respond with the file number that results from opening the file. This should not be relied on to be any specific value and is merely provided for edification. CLOSE ----- Returns OK or ERRNO. READ ---- Returns OK or ERRNO. If successful, the length of data read and the actual data will be returned. WRITE ----- Returns OK or ERRNO. If successful, the actual length of data written wil be returned. SEEK ---- Returns OK or ERRNO. LOCK ---- Returns GRANTED, DENIED, or ERRNO. LOCKW ----- Returns GRANTED, CANCELED, DEADLOCK, or ERRNO. If a blocked lock is canceled (for example by an alarm triggering), the CANCELED status will be returned. If a deadlock is detected, DEADLOCK will be returned. UNLOCK ------ Returns GRANTED or ERRNO. TEST ---- Returns AVAILABLE, CONLFICT or ERRNO. If there is a conflicting lock, one conflicting lock will be returned. LIST ---- Returns AVAILABLE, DENIED or ERRNO. Prior to returning DENIED, a list will return one or more CONFLICT responses corresponding to locks held that overlap the range. An AVAILABLE response indicates an empty list while a DENIED response indicates no more CONFLICT responses are forthcoming. It should be noted that LIST will only be able to return one instance of a read lock if there are multiple read locks on the same range. LIST may or may not return two read locks if there is one read lock and a second larger read lock that completely overlaps the first one. LIST should return two read locks if they partially overlap. For example: owner1 read 1-3, owner2 read 1-3, owner3 read 1-3 can only return one of those. onwer1 read 1-3, owner2 read 1-6 may return just owner2 read 1-6 or both. owner1 read 1-6, owner2 read 3-9 should return both. This is due to how fcntl with F_GETLK is implemented (or NFS v3 TEST or NFS v4 LOCKT). HOP --- Returns GRANTED, DENIED, or ERRNO. HOP is similar to LOCK, except the lock is acquired one byte at a time in an alternating pattern (for example, HOP read 0 5 is acquired in order 0, 2, 1, 4, 3). UNHOP ----- Returns GRANTED or ERRNO. UNHOP is similar to UNLOCK, except the lock is released one byte at a time in an alternating pattern (for example, UNHOP 0 5 is released in order 0, 2, 1, 4, 3). COMMENT ------- Returns OK and reflects back the original comment. HELLO ----- Returns OK and reflects back the original comment. Clients are expected to send a HELLO response to identify themselves when first connecting to the console. ALARM ----- Returns OK and CANCELED or COMPLETE. If the alarm is canceled before completion, a CANCELED response will be sent. If the alarm triggers, a COMPLETE response will be sent. QUIT ---- Returns OK. A client should response to QUIT before closing the connection with the console. MASTER SCRIPTING ---------------- -------------------------------------------------------------------------------- The console implements a simple script language which allows commands to be directed to multiple clients. The scripting language also allows for verification of client responses. The console script commands are: # QUIT STRICT on|off FATAL on|off SLEEP seconds name tag command parameters EXPECT name tag command results OK name command parameters GRANTED name command parameters AVAILABLE name command parameters DENIED name command parameters DEADLOCK name command parameters CLIENTS name name... FORK name1 name2 { } There are some special tag values that may be used. $ references the line number of a command. When used in an EXPECT, it uses the line number of the last command. $A to $Z increment the variable when used in a command, and store the tag into a slot. When used in an EXPECT, $A to $Z use the value out of the slot. This simplifies script writing while still allowing responses to be correlated with earlier commands (for example a LOCK, LOCKW (blocks), UNLOCK, GRANTED sequence). # - This allows comments to be placed in scripts. QUIT ---- This ends a script. QUIT will be sent to each connected client. Any outstanding responses from the clients will be indicated as unexpected and may result in script failure. STRICT ------ This turns strict mode on and off. It is only effective in interractive mode and will make interractive mode function like scripted mode where client responses are only processed after EXPECT, }, OK, or GRANTED commands. FATAL ----- This turns fatal mode on and off. When fatal mode is on, any error will cause ml_console to exit (after issuing a QUIT command) and indicate script failure. SLEEP ----- This inserts a delay into a script where no responses are expected. In fatal mode, any response received during this time will cause failure. name ---- The "default" command is to send a command to the named client. Note that the console does not block waiting for a response to one of these commands. EXPECT ------ This command waits for a client response and compares it to the results expected. An "*" may be used to indicate don't care for any parameters (for numeric parameters, -1 has the same effect). If any of the parameters in the response don't match, an error will be reported (and failure in fatal mode). OK, AVAILABLE, GRANTED, DENIED, and DEADLOCK -------------------------------------------- These commands issue a command to the named client and expect the appropriate response. These commands will block awaiting a response. OK is only valid for the following commands: OPEN, CLOSE, SEEK, READ, WRITE, COMMENT, ALARM, HELLO, and QUIT. READ must specify the expected string, and the length of that string is sent to the client as the expected length. OPEN accepts any file_number. GRANTED is only valid with LOCK, and UNLOCK. AVAILABLE is only valid for TEST and LIST. For LIST, an empty list is expected. DENIED is only valid with LOCK. DEADLOCK is only valus with LOCKW. CLIENTS ------- This command creates a list of EXPECT {name} * HELLO OK {name} for each client named on the command. FORK ---- This command sents a FORK message to client name1 with name2 as the parameter. It then creates a list: EXPECT {name1} * FORK OK {name2} EXPECT {name2} * HELLO OK {name2} { and } ------- These paired commands are used to group a set of EXPECT commands where the responses might arrive in any order. All responses are required for completion of this command. There is no restriction to what commands may be included inside a pair of braces, however, note that all OK and GRANTED commands will immediately block waiting for a response. IMPLEMENTATION NOTES FOR ml_console ---------------------------------- ml_console uses select to wait for input from all the clients (and the console in interractive mode). ml_console will respond to SIGINT (ctrl-c) and SIGTERM by cleanly exiting. These should be able to interrupt most hung scripts for a clean exit and failure. IMPLEMENTATION NOTES FOR ml_posix_client --------------------------------------- ml_posix_client is single threaded and if a command (primarily LOCKW) blocks, the client will not receive new commands or respond to the console. ml_posix_client uses ALARM and SIGALRM to interrupt a blocked lock. THOUGHTS ON POSSIBLE OTHER CLIENT IMPLEMENTATIONS ------------------------------------------------- Note that all the parsing and response formatting functions are implemented in functions.c and separated from the implementation of commands in ml_posix_client.c. This would make it easy to implement a client that could, for example, send NFS v3 and NLM requests corresponding to the commands. Such a client should have a command line option to specify the server's address and path for initial MOUNT. An OPEN command would do a LOOKUP (if the asumption was scripts that utilized files in a single directory, the LOOKUP implementation would not have to deal with path walking. Such a client of course need not actually block for a LOCKW command, allowing the same lock owner to have multiple blocking locks outstanding. A FEW NOTES ON THE FUNCTIONS IN ml_functions.c ---------------------------------------------- char * parse_response(char * line, response_t * resp); This function is used by the console to parse out a response. Then use int compare_responses(response_t * expected, response_t * received); to verify that response against an expected response (using parse_response to parse the EXPECT command also). void add_response(response_t * resp, response_t ** list); response_t * check_expected_responses(response_t *expected_responses, response_t * client_resp); These functions implement checking against a list of responses (used by {...}) char * parse_request(char * line, response_t * req, int no_tag); This would be the center of an alternate client implementation since it will parse the command stream. ml_console also uses this to syntax check commands and the built request is also used as the basis of the expected response for OK and GRANTED. void respond(response_t * resp); This function is used by a client to send a response to the output stream (console in interractive mode and console socket in console mode). void send_cmd(response_t * req); This function is used by the console to send commands to clients. ����������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/multilock/ml_cephfs_client.c��������������������������������������������0000664�0000000�0000000�00000074147�13242724102�0023317�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright IBM Corporation, 2012 * Contributor: Frank Filz <ffilzlnx@mindspring.com> * * * This software is a server that implements the NFS protocol. * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * */ #include <fcntl.h> #include <cephfs/libcephfs.h> #include <pthread.h> #include <stdlib.h> #include <assert.h> #include <libgen.h> #include "multilock.h" #include "../../include/gsh_list.h" /* command line syntax */ char options[] = "c:qdx:s:n:p:h?C:"; char usage[] = "Usage: ml_cephfs_client -C ceph-config -s server -p port -n name [-q] [-d] [-c path]\n" " ml_cephfs_client -C ceph-config -x script [-q] [-d] [-c path]\n" " ml_cephfs_client -C ceph-config [-q] [-d] [-c path]\n" "\n" " ml_cephfs_client may be run in three modes\n" " - In the first mode, the client will be driven by a master.\n" " - In the second mode, the client is driven by a script.\n" " - In the third mode, the client interractive.\n" "\n" " -s server - specify the master's hostname or IP address\n" " -p port - specify the master's port number\n" " -n name - specify the client's name\n" " -x script - specify the name of a script to execute\n" " -q - specify quiet mode\n" " -d - specify dup errors mode (errors are sent to stdout and stderr)\n" " -c path - chdir\n" " -C ceph-config - path to the ceph config file\n"; #define NUM_WORKER 4 #define POLL_DELAY 10 enum thread_type { THREAD_NONE, THREAD_MAIN, THREAD_WORKER, THREAD_POLL, THREAD_CANCEL }; struct work_item { struct glist_head queue; struct glist_head fno_work; struct response resp; enum thread_type work_owner; pthread_t work_thread; time_t next_poll; }; char server[MAXSTR]; char name[MAXSTR]; char portstr[MAXSTR]; int port; char line[MAXXFER]; long int alarmtag; Inode *inodes[MAXFPOS + 1]; Fh *filehandles[MAXFPOS + 1]; enum lock_mode lock_mode[MAXFPOS + 1]; char ceph_config[PATH_MAX]; char ceph_path[PATH_MAX] = "/"; /* Global variables to manage work list */ struct glist_head fno_work[MAXFPOS + 1]; struct glist_head work_queue = GLIST_HEAD_INIT(work_queue); struct glist_head poll_queue = GLIST_HEAD_INIT(poll_queue); pthread_t threads[NUM_WORKER + 1]; pthread_mutex_t work_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t work_cond = PTHREAD_COND_INITIALIZER; enum thread_type a_worker = THREAD_WORKER; enum thread_type a_poller = THREAD_POLL; /* The CephFS mount */ struct ceph_mount_info *cmount; /* Default mount UserPerm */ UserPerm *cephperms; void openserver(void) { struct addrinfo *addr; int rc; struct addrinfo hint; int sock; struct response resp; if (!quiet) fprintf(stdout, "server=%s port=%d name=%s\n", server, port, name); memset(&hint, 0, sizeof(hint)); hint.ai_family = AF_INET; hint.ai_socktype = SOCK_STREAM; rc = getaddrinfo(server, portstr, &hint, &addr); if (rc != 0) fatal("getaddrinfo error %d \"%s\"\n", rc, gai_strerror(rc)); rc = socket(addr[0].ai_family, SOCK_STREAM, 0); if (rc == -1) fatal("socket failed with ERRNO %d \"%s\"\n", errno, strerror(errno)); sock = rc; rc = connect(sock, addr[0].ai_addr, addr[0].ai_addrlen); if (rc == -1) fatal("connect failed with ERRNO %d \"%s\"\n", errno, strerror(errno)); input = fdopen(sock, "r"); if (input == NULL) fatal( "Could not create input stream from socket ERROr %d \"%s\"\n", errno, strerror(errno)); output = fdopen(sock, "w"); if (output == NULL) fatal( "Could not create output stream from socket ERROr %d \"%s\"\n", errno, strerror(errno)); if (!quiet) fprintf(stdout, "connected to server %s:%d\n", server, port); resp.r_cmd = CMD_HELLO; resp.r_status = STATUS_OK; resp.r_tag = 0; array_strcpy(resp.r_data, name); respond(&resp); } bool do_fork(struct response *resp, bool use_server) { pid_t forked; if (!use_server) { fprintf_stderr("FORK may only be used in server mode\n"); return false; } forked = fork(); if (forked < 0) { /* Error */ resp->r_status = STATUS_OK; resp->r_errno = errno; if (!quiet) fprintf(stdout, "fork failed %d (%s)\n", (int) resp->r_errno, strerror(resp->r_errno)); return true; } else if (forked == 0) { /* Parent sends a FORK response */ array_strcpy(resp->r_data, name); resp->r_status = STATUS_OK; if (!quiet) fprintf(stdout, "fork succeeded\n"); return true; } if (!quiet) fprintf(stdout, "forked\n"); /* This is the forked process */ /* First close the old output. */ fclose(output); /* Setup the new client name. */ array_strcpy(name, resp->r_data); /* Then open a new connection to the server. */ openserver(); /* Response already sent. */ return false; } void command(void) { } void sighandler(int sig) { struct response resp; switch (sig) { case SIGALRM: resp.r_cmd = CMD_ALARM; resp.r_tag = alarmtag; resp.r_status = STATUS_COMPLETED; alarmtag = 0; respond(&resp); break; case SIGPIPE: case SIGIO: break; } } void do_alarm(struct response *resp) { unsigned int remain; remain = alarm(resp->r_secs); if (remain != 0) { struct response resp2; resp2.r_cmd = CMD_ALARM; resp2.r_tag = alarmtag; resp2.r_secs = remain; resp2.r_status = STATUS_CANCELED; respond(&resp2); } if (resp->r_secs != 0) alarmtag = resp->r_tag; else alarmtag = 0; resp->r_status = STATUS_OK; } void do_open(struct response *resp) { int rc; Fh *filehandle = NULL; Inode *inode = NULL, *parent = NULL; struct ceph_statx stx; if (filehandles[resp->r_fpos] != NULL) { resp->r_status = STATUS_ERRNO; resp->r_errno = EINVAL; array_strcpy(errdetail, "fpos in use"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } if ((resp->r_flags & O_CREAT) != 0) { char fullpath[PATH_MAX]; char *name; char *path; array_strcpy(fullpath, resp->r_data); name = basename(fullpath); path = dirname(fullpath); fprintf_stderr("path = '%s' name = '%s'\n", path, name); rc = ceph_ll_walk(cmount, path, &parent, &stx, 0, AT_NO_ATTR_SYNC, cephperms); if (rc >= 0) { rc = ceph_ll_create(cmount, parent, name, resp->r_mode, resp->r_flags, &inode, &filehandle, &stx, 0, AT_NO_ATTR_SYNC, cephperms); /* Release the parent directory. */ ceph_ll_put(cmount, parent); if (rc < 0) array_strcpy(errdetail, "ceph_ll_create"); } else { array_sprintf(errdetail, "ceph_ll_walk %s", path); } } else { rc = ceph_ll_walk(cmount, resp->r_data, &inode, &stx, 0, AT_NO_ATTR_SYNC, cephperms); if (rc >= 0) { rc = ceph_ll_open(cmount, inode, resp->r_flags, &filehandle, cephperms); if (rc < 0) array_strcpy(errdetail, "ceph_ll_open"); } else { array_strcpy(errdetail, "ceph_ll_walk"); } } if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(badtoken, resp->r_data); return; } inodes[resp->r_fpos] = inode; filehandles[resp->r_fpos] = filehandle; lock_mode[resp->r_fpos] = (enum lock_mode) resp->r_lock_type; resp->r_fno = resp->r_fpos; resp->r_status = STATUS_OK; } void do_write(struct response *resp) { long long int rc; if (resp->r_fpos != 0 && filehandles[resp->r_fpos] == NULL) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } rc = ceph_ll_write(cmount, filehandles[resp->r_fpos], -1, resp->r_length, resp->r_data); if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Write failed"); array_sprintf(badtoken, "%lld", resp->r_length); return; } if (rc != resp->r_length) { resp->r_status = STATUS_ERRNO; resp->r_errno = EIO; array_strcpy(errdetail, "Short write"); array_sprintf(badtoken, "%lld", rc); return; } resp->r_status = STATUS_OK; } void do_read(struct response *resp) { long long int rc; if (resp->r_fpos != 0 && filehandles[resp->r_fpos] == NULL) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } if (resp->r_length > MAXSTR) resp->r_length = MAXSTR; rc = ceph_ll_read(cmount, filehandles[resp->r_fpos], -1, resp->r_length, resp->r_data); if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Read failed"); array_sprintf(badtoken, "%lld", resp->r_length); return; } resp->r_data[rc] = '\0'; resp->r_length = strlen(resp->r_data); resp->r_status = STATUS_OK; } void do_seek(struct response *resp) { int rc; if (resp->r_fpos != 0 && filehandles[resp->r_fpos] == NULL) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } rc = ceph_ll_lseek(cmount, filehandles[resp->r_fpos], resp->r_start, SEEK_SET); if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Seek failed"); array_sprintf(badtoken, "%lld", resp->r_start); return; } resp->r_status = STATUS_OK; } void free_work(struct work_item *work) { /* Make sure the item isn't on any queues */ glist_del(&work->fno_work); glist_del(&work->queue); free(work); } void cancel_work_item(struct work_item *work) { switch (work->work_owner) { case THREAD_NONE: case THREAD_MAIN: case THREAD_CANCEL: /* nothing special to do */ break; case THREAD_WORKER: case THREAD_POLL: /* Mark the work item to be canceled */ work->work_owner = THREAD_CANCEL; pthread_kill(work->work_thread, SIGIO); /* Wait for thread to be done or cancel the work */ while (work->work_owner != THREAD_NONE) pthread_cond_wait(&work_cond, &work_mutex); } /* Done with the work item, free the memory. */ free_work(work); } static inline long long int lock_end(struct response *req) { if (req->r_length == 0) return LLONG_MAX; return req->r_start + req->r_length; } void cancel_work(struct response *req) { struct glist_head cancel_work = GLIST_HEAD_INIT(cancel_work); struct glist_head *glist; struct work_item *work; bool start_over = true; pthread_mutex_lock(&work_mutex); while (start_over) { start_over = false; glist_for_each(glist, fno_work + req->r_fpos) { work = glist_entry(glist, struct work_item, fno_work); if (work->resp.r_start >= req->r_start && lock_end(&work->resp) <= lock_end(req)) { /* Do something */ cancel_work_item(work); /* List may be messed up */ start_over = true; break; } } } pthread_mutex_unlock(&work_mutex); } /* Must only be called from main thread...*/ int schedule_work(struct response *resp) { struct work_item *work = calloc(1, sizeof(*work)); if (work == NULL) { errno = ENOMEM; return -1; } pthread_mutex_lock(&work_mutex); work->resp = *resp; work->work_owner = THREAD_NONE; glist_add_tail(&work_queue, &work->queue); glist_add_tail(fno_work + resp->r_fpos, &work->fno_work); /* Signal to the worker and polling threads there is new work */ pthread_cond_broadcast(&work_cond); pthread_mutex_unlock(&work_mutex); return 0; } bool do_lock(struct response *resp, enum thread_type thread_type) { int rc; struct flock lock; bool retry = false; uint64_t owner; bool sleep; if (resp->r_fpos != 0 && filehandles[resp->r_fpos] == NULL) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return true; } switch (lock_mode[resp->r_fpos]) { case LOCK_MODE_POSIX: if (resp->r_cmd == CMD_LOCKW) { if (thread_type != THREAD_WORKER) { retry = true; sleep = false; } else { sleep = true; } } else { sleep = false; } owner = getpid(); break; case LOCK_MODE_OFD: if (resp->r_cmd == CMD_LOCKW) { if (thread_type != THREAD_WORKER) { retry = true; } else { sleep = true; } } else { sleep = false; } owner = resp->r_fpos; break; } lock.l_whence = SEEK_SET; lock.l_type = resp->r_lock_type; lock.l_start = resp->r_start; lock.l_len = resp->r_length; lock.l_pid = 0; rc = ceph_ll_setlk(cmount, filehandles[resp->r_fpos], &lock, owner, sleep); if (rc == -EAGAIN && retry && thread_type == THREAD_MAIN) { /* We need to schedule OFD blocked lock */ rc = schedule_work(resp); /* Check for scheduling success */ if (rc >= 0) return false; } if (rc < 0) { if (rc == -EAGAIN) { if (retry) { /* Let caller know we didn't complete */ return false; } resp->r_status = STATUS_DENIED; } else if (rc == -EINTR) { resp->r_status = STATUS_CANCELED; } else if (rc == -EDEADLK) { resp->r_status = STATUS_DEADLOCK; } else { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Lock failed"); array_sprintf(badtoken, "%s %lld %lld", str_lock_type(lock.l_type), resp->r_start, resp->r_length); } } else resp->r_status = STATUS_GRANTED; return true; } void do_hop(struct response *resp) { int rc; int pos; struct flock lock; uint64_t owner; if (resp->r_fpos != 0 && filehandles[resp->r_fpos] == NULL) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } switch (lock_mode[resp->r_fpos]) { case LOCK_MODE_POSIX: owner = getpid(); break; case LOCK_MODE_OFD: owner = resp->r_fpos; break; } for (pos = resp->r_start; pos < resp->r_start + resp->r_length; pos++) { if ((pos == 0) || (pos == (resp->r_start + resp->r_length - 1))) lock.l_start = pos; else if ((pos & 1) == 0) lock.l_start = pos - 1; else lock.l_start = pos + 1; lock.l_whence = SEEK_SET; lock.l_type = resp->r_lock_type; lock.l_len = 1; lock.l_pid = 0; rc = ceph_ll_setlk(cmount, filehandles[resp->r_fpos], &lock, owner, false); if (rc < 0) { if (rc == -EAGAIN) { resp->r_start = lock.l_start; resp->r_length = 1; resp->r_status = STATUS_DENIED; break; } else { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Hop failed"); array_sprintf(badtoken, "%s %ld", str_lock_type(resp->r_lock_type), lock.l_start); break; } } else resp->r_status = STATUS_GRANTED; } if (resp->r_status != STATUS_GRANTED) { lock.l_whence = SEEK_SET; lock.l_type = F_UNLCK; lock.l_start = resp->r_start; lock.l_len = resp->r_length; lock.l_pid = 0; rc = ceph_ll_setlk(cmount, filehandles[resp->r_fpos], &lock, owner, false); if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Hop Unlock failed"); array_sprintf(badtoken, "%lld %lld", resp->r_start, resp->r_length); } } } void do_unhop(struct response *resp) { int rc; int pos; struct flock lock; uint64_t owner; if (resp->r_fpos != 0 && filehandles[resp->r_fpos] == NULL) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } switch (lock_mode[resp->r_fpos]) { case LOCK_MODE_POSIX: owner = getpid(); break; case LOCK_MODE_OFD: owner = resp->r_fpos; break; } for (pos = resp->r_start; pos < resp->r_start + resp->r_length; pos++) { if ((pos == 0) || (pos == (resp->r_start + resp->r_length - 1))) lock.l_start = pos; else if ((pos & 1) == 0) lock.l_start = pos - 1; else lock.l_start = pos + 1; lock.l_whence = SEEK_SET; lock.l_type = resp->r_lock_type; lock.l_len = 1; lock.l_pid = 0; rc = ceph_ll_setlk(cmount, filehandles[resp->r_fpos], &lock, owner, false); if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Unhop failed"); array_sprintf(badtoken, "%s %ld", str_lock_type(resp->r_lock_type), lock.l_start); break; } else resp->r_status = STATUS_GRANTED; } if (resp->r_status != STATUS_GRANTED) { lock.l_whence = SEEK_SET; lock.l_type = F_UNLCK; lock.l_start = resp->r_start; lock.l_len = resp->r_length; lock.l_pid = 0; rc = ceph_ll_setlk(cmount, filehandles[resp->r_fpos], &lock, owner, false); if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Unhop Unlock failed"); array_sprintf(badtoken, "%lld %lld", resp->r_start, resp->r_length); } } } void do_unlock(struct response *resp) { int rc; struct flock lock; uint64_t owner; if (resp->r_fpos != 0 && filehandles[resp->r_fpos] == NULL) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } /* If this fpos has a blocking lock, cancel it. */ cancel_work(resp); switch (lock_mode[resp->r_fpos]) { case LOCK_MODE_POSIX: owner = getpid(); break; case LOCK_MODE_OFD: owner = resp->r_fpos; break; } lock.l_whence = SEEK_SET; lock.l_type = resp->r_lock_type; lock.l_start = resp->r_start; lock.l_len = resp->r_length; lock.l_pid = 0; rc = ceph_ll_setlk(cmount, filehandles[resp->r_fpos], &lock, owner, false); if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Unlock failed"); array_sprintf(badtoken, "%lld %lld", resp->r_start, resp->r_length); return; } resp->r_status = STATUS_GRANTED; } void do_test(struct response *resp) { int rc; struct flock lock; uint64_t owner; if (resp->r_fpos != 0 && filehandles[resp->r_fpos] == NULL) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } switch (lock_mode[resp->r_fpos]) { case LOCK_MODE_POSIX: owner = getpid(); break; case LOCK_MODE_OFD: owner = resp->r_fpos; break; } lock.l_whence = SEEK_SET; lock.l_type = resp->r_lock_type; lock.l_start = resp->r_start; lock.l_len = resp->r_length; lock.l_pid = 0; fprintf(stdout, "TEST lock type %s\n", str_lock_type(lock.l_type)); rc = ceph_ll_getlk(cmount, filehandles[resp->r_fpos], &lock, owner); if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Test failed"); array_sprintf(badtoken, "%s %lld %lld", str_lock_type(lock.l_type), resp->r_start, resp->r_length); return; } if (lock.l_type == F_UNLCK) { fprintf(stdout, "GRANTED TEST lock type %s\n", str_lock_type(lock.l_type)); resp->r_status = STATUS_GRANTED; } else { resp->r_lock_type = lock.l_type; resp->r_pid = lock.l_pid; resp->r_start = lock.l_start; resp->r_length = lock.l_len; resp->r_status = STATUS_CONFLICT; } } void do_close(struct response *resp) { int rc; if (resp->r_fpos != 0 && filehandles[resp->r_fpos] == NULL) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } rc = ceph_ll_close(cmount, filehandles[resp->r_fpos]); ceph_ll_put(cmount, inodes[resp->r_fpos]); filehandles[resp->r_fpos] = NULL; inodes[resp->r_fpos] = NULL; if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Close failed"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } resp->r_status = STATUS_OK; } struct test_list { struct test_list *tl_next; long long int tl_start; long long int tl_end; }; struct test_list *tl_head; struct test_list *tl_tail; void remove_test_list_head(void) { struct test_list *item = tl_head; if (item == NULL) fatal("Corruption in test list\n"); tl_head = item->tl_next; if (tl_tail == item) tl_tail = NULL; free(item); } void make_test_item(long long int start, long long int end) { struct test_list *item = malloc(sizeof(*item)); if (item == NULL) fatal("Could not allocate test list item\n"); item->tl_next = NULL; item->tl_start = start; item->tl_end = end; if (tl_head == NULL) tl_head = item; if (tl_tail != NULL) tl_tail->tl_next = item; tl_tail = item; } int list_locks(long long int start, long long int end, struct response *resp) { long long int conf_end; struct flock lock; int rc; lock.l_whence = SEEK_SET; lock.l_type = F_WRLCK; lock.l_start = start; lock.l_pid = 0; if (end == INT64_MAX) lock.l_len = 0; else lock.l_len = end - start; rc = ceph_ll_getlk(cmount, filehandles[resp->r_fpos], &lock, 0); if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Test failed"); array_sprintf(badtoken, "%s %lld %lld", str_lock_type(lock.l_type), resp->r_start, resp->r_length); respond(resp); return false; } /* Our test succeeded */ if (lock.l_type == F_UNLCK) return false; resp->r_status = STATUS_CONFLICT; resp->r_lock_type = lock.l_type; resp->r_pid = lock.l_pid; resp->r_start = lock.l_start; resp->r_length = lock.l_len; respond(resp); conf_end = lock_end(resp); if (lock.l_start > start) make_test_item(start, lock.l_start); if (conf_end < end) make_test_item(conf_end, end); return true; } void do_list(struct response *resp) { long long int start = resp->r_start; long long int length = resp->r_length; int conflict = false; if (resp->r_fpos != 0 && filehandles[resp->r_fpos] == NULL) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } resp->r_lock_type = F_WRLCK; make_test_item(start, lock_end(resp)); while (tl_head != NULL) { conflict |= list_locks(tl_head->tl_start, tl_head->tl_end, resp); remove_test_list_head(); } if (conflict) resp->r_status = STATUS_DENIED; else resp->r_status = STATUS_AVAILABLE; resp->r_lock_type = F_WRLCK; resp->r_start = start; resp->r_length = length; } struct work_item *get_work(enum thread_type thread_type) { struct work_item *work, *poll; while (true) { /* Check poll list first */ poll = glist_first_entry(&poll_queue, struct work_item, queue); work = poll; /* If we didn't find work on the poll list or this is a polling * thread and the work on the poll list isn't due for polling * yet, then look for work on the work list. */ if (work == NULL || (thread_type == THREAD_POLL && work->next_poll > time(NULL))) { /* Check work list now */ work = glist_first_entry(&work_queue, struct work_item, queue); } /* Assign the work to ourself and remove from the queue and * return to the caller. */ if (work != NULL) { work->work_owner = thread_type; work->work_thread = pthread_self(); glist_del(&work->queue); return work; } /* No work, decide what kind of wait to do. */ if (thread_type == THREAD_POLL && poll != NULL) { /* Since there is polling work to do, determine next * time to poll and wait that long for signal. */ struct timespec twait = {poll->next_poll - time(NULL), 0}; pthread_cond_timedwait(&work_cond, &work_mutex, &twait); } else { /* Wait for signal */ pthread_cond_wait(&work_cond, &work_mutex); } } } void *worker(void *t_type) { struct work_item *work = NULL; bool complete, cancelled = false; enum thread_type thread_type = *((enum thread_type *) t_type); pthread_mutex_lock(&work_mutex); while (true) { /* Look for work */ work = get_work(thread_type); pthread_mutex_unlock(&work_mutex); assert(work != NULL); /* Do the work */ switch (work->resp.r_cmd) { case CMD_LOCKW: complete = do_lock(&work->resp, thread_type); break; default: work->resp.r_status = STATUS_ERRNO; work->resp.r_errno = EINVAL; complete = true; break; } if (complete) respond(&work->resp); pthread_mutex_lock(&work_mutex); if (complete) { /* Remember if the main thread was trying to cancel * the work. */ cancelled = work->work_owner == THREAD_CANCEL; /* Indicate this work is complete */ work->work_owner = THREAD_NONE; work->work_thread = 0; if (cancelled) { /* Signal that work has been canceled or * completed. */ pthread_cond_broadcast(&work_cond); } else { /* The work is done, and may be freed, except * that if the work was being cancelled by * cancel_work, then the main thread is waiting * for this thread to be done with the work * item, and thus we can not free it, * cancel_work_item will be responsible to free * the work item. This assures that the request * that caused the cancellation is blocked until * the cancellation has completed. */ free_work(work); } } else { /* This can ONLY happen for a polling thread. * Put this work back in the queue, with a new * next polling time. */ work->work_owner = THREAD_NONE; work->work_thread = 0; work->next_poll = time(NULL) + POLL_DELAY; glist_add_tail(&poll_queue, &work->queue); /* And let worker threads know there may be * more work to do. */ pthread_cond_broadcast(&work_cond); } } } int main(int argc, char **argv) { int opt; int len, rc, i; struct sigaction sigact; char *rest; int oflags = 0; int no_tag; /* Init the lists of work for each fno */ for (i = 0; i <= MAXFPOS; i++) glist_init(fno_work + i); /* Start the worker and polling threads */ for (i = 0; i <= NUM_WORKER; i++) { rc = pthread_create(&threads[i], NULL, worker, i == 0 ? &a_poller : &a_worker); if (rc == -1) { fprintf(stderr, "pthread_create failed %s\n", strerror(errno)); exit(1); } } /* Initialize the signal handling */ memset(&sigact, 0, sizeof(sigact)); sigact.sa_handler = sighandler; if (sigaction(SIGALRM, &sigact, NULL) == -1) return 1; if (sigaction(SIGPIPE, &sigact, NULL) == -1) return 1; if (sigaction(SIGIO, &sigact, NULL) == -1) return 1; input = stdin; output = stdout; /* now parsing options with getopt */ while ((opt = getopt(argc, argv, options)) != EOF) { switch (opt) { case 'c': array_strcpy(ceph_path, optarg); break; case 'q': quiet = true; break; case 'd': duperrors = true; break; case 's': if (oflags > 7) show_usage(1, "Can not combine -x and -s\n"); oflags |= 1; script = true; array_strcpy(server, optarg); break; case 'x': if ((oflags & 7) != 0) show_usage(1, "Can not combine -x and -s/-p/-n\n"); oflags |= 8; script = true; input = fopen(optarg, "r"); if (input == NULL) fatal("Could not open %s\n", optarg); break; case 'n': if (oflags > 7) show_usage(1, "Can not combine -x and -n\n"); oflags |= 2; array_strcpy(name, optarg); break; case 'p': if (oflags > 7) show_usage(1, "Can not combine -x and -p\n"); oflags |= 4; array_strcpy(portstr, optarg); port = atoi(optarg); break; case 'C': array_strcpy(ceph_config, optarg); break; case '?': case 'h': default: /* display the help */ show_usage(0, "Help\n"); } } if (ceph_config[0] == '\0') show_usage(1, "Must specifiy -C ceph-config\n"); if (oflags > 0 && oflags < 7) show_usage(1, "Must specify -s, -p, and -n together\n"); if (oflags == 7) openserver(); rc = ceph_create(&cmount, NULL); if (rc != 0) fatal("Unable to create ceph mount context\n"); rc = ceph_conf_read_file(cmount, ceph_config); if (rc != 0) fatal("Unable to read ceph config\n"); rc = ceph_mount(cmount, NULL); if (rc != 0) { fprintf_stderr("Unable to mount Ceph cluster %s(%d)\n", strerror(-rc), rc); fatal("Unable to mount Ceph cluster"); } cephperms = ceph_mount_perms(cmount); while (1) { len = readln(input, line, sizeof(line)); if (len < 0) { if (script) fatal("End of file on input\n"); else break; } else { struct response resp; bool complete = true; lno++; memset(&resp, 0, sizeof(resp)); rest = SkipWhite(line, REQUIRES_MORE, "Invalid line"); /* Skip totally blank line */ if (rest == NULL) continue; if (script && !quiet) fprintf(stdout, "%s\n", rest); /* If line doesn't start with a tag, that's ok */ no_tag = (!isdigit(*rest) && (*rest != '$') && (*rest != '-')); /* Parse request into response structure */ rest = parse_request(rest, &resp, no_tag); if (rest == NULL) { resp.r_status = STATUS_ERRNO; resp.r_errno = errno; } else { /* Make sure default status is ok */ resp.r_status = STATUS_OK; if (*rest != '\0' && *rest != '#') fprintf_stderr( "Command line not consumed, rest=\"%s\"\n", rest); switch (resp.r_cmd) { case CMD_OPEN: do_open(&resp); break; case CMD_CLOSE: do_close(&resp); break; case CMD_LOCKW: complete = do_lock(&resp, THREAD_MAIN); break; case CMD_LOCK: complete = do_lock(&resp, THREAD_MAIN); break; case CMD_UNLOCK: do_unlock(&resp); break; case CMD_TEST: do_test(&resp); break; case CMD_LIST: do_list(&resp); break; case CMD_HOP: do_hop(&resp); break; case CMD_UNHOP: do_unhop(&resp); break; case CMD_SEEK: do_seek(&resp); break; case CMD_READ: do_read(&resp); break; case CMD_WRITE: do_write(&resp); break; case CMD_ALARM: do_alarm(&resp); break; case CMD_FORK: complete = do_fork(&resp, oflags == 7); break; case CMD_HELLO: case CMD_COMMENT: case CMD_QUIT: resp.r_status = STATUS_OK; break; case NUM_COMMANDS: fprintf_stderr("Invalid command %s\n", line); continue; } } if (complete) respond(&resp); if (resp.r_cmd == CMD_QUIT) exit(0); } } return 0; } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/multilock/ml_console.c��������������������������������������������������0000664�0000000�0000000�00000066514�13242724102�0022152�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright IBM Corporation, 2012 * Contributor: Frank Filz <ffilzlnx@mindspring.com> * * * This software is a server that implements the NFS protocol. * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * */ #include "multilock.h" #include "assert.h" /* command line syntax */ char options[] = "ekdqfsp:h?x:"; char usage[] = "Usage: ml_master [-p port] [-s] [-f] [-q] [-x script] [-d]\n" "\n" " -p port - specify the port to listen to clients on\n" " -s - specify strict mode (clients are not polled without EXPECT)\n" " -f - specify errors are fatal mode\n" " -q - speficy quiet mode\n" " -d - speficy dup errors mode (errors are sent to stdout and stderr)\n" " -x script - specify script to run\n" " -k - syntax check only\n" " -e - non-fatal errors, full accounting of errors to stderr, everything\n" " to stdout\n" ; int port, listensock; struct sockaddr_in addr; int maxfd; struct response *expected_responses; fd_set sockets; int num_errors; bool terminate; bool err_accounting; sigset_t full_signal_set; sigset_t original_signal_set; void open_socket(void) { int rc; rc = socket(AF_INET, SOCK_STREAM, 0); if (rc == -1) fatal("socket failed with ERRNO %d \"%s\"\n", errno, strerror(errno)); listensock = rc; addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = INADDR_ANY; rc = bind(listensock, (struct sockaddr *)&addr, sizeof(addr)); if (rc == -1) fatal("bind failed with ERRNO %d \"%s\"\n", errno, strerror(errno)); FD_ZERO(&sockets); FD_SET(listensock, &sockets); maxfd = listensock; rc = listen(listensock, 10); if (rc == -1) fatal("listen failed with ERRNO %d \"%s\"\n", errno, strerror(errno)); } void do_accept(void) { struct client *client = malloc(sizeof(*client)); socklen_t len; int rc; if (client == NULL) fatal("Accept malloc failed\n"); memset(client, 0, sizeof(*client)); len = sizeof(client->c_addr); client->c_socket = accept(listensock, &client->c_addr, &len); if (client->c_socket == -1) fatal("Accept failed ERRNO %d \"%s\"\n", errno, strerror(errno)); FD_SET(client->c_socket, &sockets); array_sprintf(client->c_name, "<UNKNOWN_%d>", client->c_socket); if (client->c_socket > maxfd) maxfd = client->c_socket; if (!quiet) fprintf(output, "Accept for socket %d\n", client->c_socket); client->c_input = fdopen(client->c_socket, "r"); if (client->c_input == NULL) fatal("Accept fdopen for input failed ERRNO %d \"%s\"\n", errno, strerror(errno)); rc = setvbuf(client->c_input, NULL, _IONBF, 0); if (rc != 0) fatal("Accept setvbuf for input failed ERRNO %d \"%s\"\n", errno, strerror(errno)); client->c_output = fdopen(client->c_socket, "w"); if (client->c_output == NULL) fatal("Accept fdopen for output failed ERRNO %d \"%s\"\n", errno, strerror(errno)); rc = setvbuf(client->c_output, NULL, _IONBF, 0); if (rc != 0) fatal("Accept setvbuf for output failed ERRNO %d \"%s\"\n", errno, strerror(errno)); client->c_refcount++; client->c_next = client_list; if (client_list != NULL) client_list->c_prev = client; client_list = client; } void close_client(struct client *client) { close(client->c_socket); if (!quiet) fprintf(output, "Closed client socket %d\n", client->c_socket); FD_CLR(client->c_socket, &sockets); client->c_socket = 0; client->c_refcount--; } struct client *find_client_by_fd(int socket) { struct client *client = client_list; while (client != NULL && client->c_socket != socket) client = client->c_next; return client; } struct client *find_client_by_name(const char *name) { struct client *client = client_list; while (client != NULL && strcasecmp(client->c_name, name) != 0) client = client->c_next; return client; } int receive(bool watchin, long int timeout_secs) { fd_set readfds, exceptfds; struct timespec timeout; int rc, i; int timeend = 0; if (timeout_secs > 0) timeend = time(NULL) + timeout_secs; while (1) { if (timeout_secs > 0) { timeout.tv_nsec = 0; timeout.tv_sec = timeend - time(NULL); if (timeout.tv_sec == 0) return -2; } else if (timeout_secs == 0) { timeout.tv_nsec = 0; timeout.tv_sec = 0; } readfds = sockets; if (watchin) FD_SET(0, &readfds); exceptfds = sockets; if (watchin) FD_SET(0, &exceptfds); if (watchin && !script) { fprintf(output, "> "); fflush(output); } if (!watchin && !quiet) { fprintf(output, "Waiting for clients\n"); fflush(output); } if (timeout_secs >= 0) { fprintf(output, "About to sleep for %d secs\n", (int)timeout.tv_sec); rc = pselect(maxfd + 1, &readfds, NULL, &exceptfds, &timeout, &original_signal_set); } else rc = pselect(maxfd + 1, &readfds, NULL, &exceptfds, NULL, &original_signal_set); if (rc == -1) { if (watchin && !script) { fprintf(output, "\n"); fflush(output); } if (errno == EINTR && !terminate) { if (timeout_secs >= 0) return -2; fprintf_stderr("select timed out\n"); return -1; } else if (errno == EINTR) { fprintf_stderr("select terminated by signal\n"); return -3; } else { fprintf_stderr ("select failed with ERRNO %d \"%s\"\n", errno, strerror(errno)); return -1; } } for (i = 0; i <= maxfd; i++) { if (FD_ISSET(i, &readfds)) { if (watchin && !quiet && i != 0) { fprintf(output, "\n"); fflush(output); } if (i == listensock) do_accept(); else return i; } if (FD_ISSET(i, &exceptfds)) { fprintf_stderr( "select received exception for socket %d\n", i); } } } } void error(void) { int len = strlen(errdetail); num_errors++; if (errdetail[len - 1] == '\n') errdetail[len - 1] = '\0'; if (errno == 0) fprintf_stderr("%s\n", errdetail); else fprintf_stderr("ERRNO %d \"%s\" \"%s\" bad token \"%s\"\n", errno, strerror(errno), errdetail, badtoken); } struct response *alloc_resp(struct client *client) { struct response *resp = malloc(sizeof(*resp)); if (resp == NULL) fatal("Could not allocate response\n"); memset(resp, 0, sizeof(*resp)); resp->r_client = client; if (client != NULL) client->c_refcount++; return resp; } struct response *process_client_response(struct client *client) { int len; struct response *client_resp; char *rest; char line[MAXXFER]; client_resp = alloc_resp(client); len = readln(client->c_input, line, sizeof(line)); if (len >= 0) { array_sprintf(client_resp->r_original, "%s %s", client->c_name, line); fprintf(output, "%s\n", client_resp->r_original); rest = parse_response(line, client_resp); if (rest == NULL) return client_resp; if (client_resp->r_cmd == CMD_HELLO) { assert(client_resp->r_length < sizeof(client->c_name)); array_strcpy(client->c_name, client_resp->r_data); } } else { fprintf(output, "%s -2 QUIT OK # socket closed\n", client->c_name); close_client(client); client_resp->r_cmd = CMD_QUIT; client_resp->r_tag = -2; client_resp->r_status = STATUS_OK; } return client_resp; } static void master_command(void); struct response *receive_response(bool watchin, long int timeout_secs) { int fd; struct client *client; fd = receive(watchin, timeout_secs); if (fd == -2 && timeout_secs >= 0) { /* Expected timeout */ return NULL; } else if (fd < 0) { struct response *resp = alloc_resp(NULL); if (fd == -3) { /* signal interrupted select */ fprintf_stderr("Receive interrupted - exiting...\n"); resp->r_tag = -1; resp->r_cmd = CMD_QUIT; resp->r_status = STATUS_CANCELED; array_strcpy(resp->r_original, "-1 QUIT CANCELED"); errno = 0; array_strcpy(errdetail, "Receive interrupted - exiting..."); } else { /* some other error occurred */ fprintf_stderr("Receive failed ERRNO %d \"%s\"\n", errno, strerror(errno)); resp->r_cmd = CMD_QUIT; resp->r_errno = errno; resp->r_tag = -1; array_strcpy(resp->r_data, "Receive failed"); array_sprintf(resp->r_original, "-1 QUIT ERRNO %d \"%s\" \"Receive failed\"", errno, strerror(errno)); array_strcpy(errdetail, "Receive failed"); array_strcpy(badtoken, ""); } return resp; } else if (watchin && fd == 0) { return NULL; } else { client = find_client_by_fd(fd); if (client == NULL) fatal("Could not find client for socket %d\n", fd); return process_client_response(client); } } enum master_cmd { MCMD_QUIT, MCMD_STRICT, MCMD_CLIENT_CMD, MCMD_EXPECT, MCMD_FATAL, MCMD_SLEEP, MCMD_OPEN_BRACE, MCMD_CLOSE_BRACE, MCMD_SIMPLE_OK, MCMD_SIMPLE_AVAILABLE, MCMD_SIMPLE_GRANTED, MCMD_SIMPLE_DENIED, MCMD_SIMPLE_DEADLOCK, MCMD_CLIENTS, MCMD_FORK, }; struct token master_commands[] = { {"QUIT", 4, MCMD_QUIT}, {"STRICT", 6, MCMD_STRICT}, {"EXPECT", 6, MCMD_EXPECT}, {"FATAL", 5, MCMD_FATAL}, {"SLEEP", 5, MCMD_SLEEP}, {"{", 1, MCMD_OPEN_BRACE}, {"}", 1, MCMD_CLOSE_BRACE}, {"OK", 2, MCMD_SIMPLE_OK}, {"AVAILABLE", 9, MCMD_SIMPLE_AVAILABLE}, {"GRANTED", 7, MCMD_SIMPLE_GRANTED}, {"DENIED", 6, MCMD_SIMPLE_DENIED}, {"DEADLOCK", 8, MCMD_SIMPLE_DEADLOCK}, {"CLIENTS", 7, MCMD_CLIENTS}, {"FORK", 4, MCMD_FORK}, {"", 0, MCMD_CLIENT_CMD} }; static void handle_quit(void); /* * wait_for_expected_responses * * Wait for a list of expected responses (in expected_responses). If any * unexpected response and this is not being called from handle_quit() force * fatal error. */ void wait_for_expected_responses(const char *label, int count, const char *last, bool could_quit) { struct response *expect_resp; struct response *client_resp; bool fatal = false; fprintf(output, "Waiting for %d %s...\n", count, label); while (expected_responses != NULL && (client_list != NULL || could_quit)) { client_resp = receive_response(false, -1); if (terminate && could_quit) { free_response(client_resp, NULL); break; } expect_resp = check_expected_responses(expected_responses, client_resp); if (expect_resp != NULL) { fprintf(output, "Matched %s\n", expect_resp->r_original); free_response(expect_resp, &expected_responses); free_response(client_resp, NULL); } else if (client_resp->r_cmd != CMD_QUIT) { errno = 0; if (err_accounting) fprintf(stderr, "%s\nResp: %s\n", last, client_resp->r_original); free_response(client_resp, NULL); array_strcpy(errdetail, "Unexpected response"); error(); /* If not called from handle_quit() dump list of * expected responses and quit if in error_is_fatal or * in a script. */ if (could_quit) { /* Error must be fatal if script since script * can't recover */ if (error_is_fatal || script) fatal = true; break; } } } /* Abandon any remaining responses */ while (expected_responses != NULL) { fprintf_stderr("Abandoning %s\n", expected_responses->r_original); free_response(expected_responses, &expected_responses); } if (fatal || terminate) handle_quit(); } void handle_quit(void) { struct response *expect_resp; struct client *client; int count = 0; char out[MAXSTR]; if (client_list != NULL) { for (client = client_list; client != NULL; client = client->c_next) { if (client->c_socket == 0) continue; array_sprintf(out, "%ld QUIT\n", ++global_tag); fputs(out, client->c_output); fflush(client->c_output); /* Build an EXPECT for -1 QUIT for this client */ expect_resp = alloc_resp(client); expect_resp->r_cmd = CMD_QUIT; expect_resp->r_status = STATUS_OK; expect_resp->r_tag = global_tag; array_sprintf(expect_resp->r_original, "EXPECT %s * QUIT OK", client->c_name); add_response(expect_resp, &expected_responses); count++; /* Build an EXPECT for -2 QUIT for this client */ expect_resp = alloc_resp(client); expect_resp->r_cmd = CMD_QUIT; expect_resp->r_status = STATUS_OK; expect_resp->r_tag = -2; array_sprintf(expect_resp->r_original, "EXPECT %s -2 QUIT OK", client->c_name); add_response(expect_resp, &expected_responses); count++; } wait_for_expected_responses("client_list", count, "QUIT", false); fprintf(output, "All clients exited\n"); } if (num_errors > 0) { fprintf_stderr("%d errors\n", num_errors); fprintf_stderr("FAIL\n"); } else { fprintf_stderr("SUCCESS\n"); } exit(num_errors > 0); } bool expect_one_response(struct response *expect_resp, const char *last) { struct response *client_resp; bool result; client_resp = receive_response(false, -1); if (terminate) result = true; else result = !compare_responses(expect_resp, client_resp); if (result) { if (err_accounting) fprintf(stderr, "%s\n%s\nResp: %s\n", last, expect_resp->r_original, client_resp->r_original); } else fprintf(output, "Matched\n"); free_response(expect_resp, NULL); free_response(client_resp, NULL); return result; } struct master_state { char *rest; char line[MAXXFER]; char out[MAXXFER]; char last[MAXXFER]; /* last command sent */ struct client *client; int len; int cmd; struct response *expect_resp; struct response *client_cmd; bool inbrace; int count; }; void mcmd_client_cmd(struct master_state *ms) { ms->rest = get_client(ms->line, &ms->client, syntax, REQUIRES_MORE); if (ms->rest == NULL) return; if (script) array_sprintf(ms->last, "Line %4ld: %s", lno, ms->line); else array_strcpy(ms->last, ms->line); ms->client_cmd = alloc_resp(ms->client); ms->rest = parse_request(ms->rest, ms->client_cmd, false); if (ms->rest != NULL && !syntax) send_cmd(ms->client_cmd); free_response(ms->client_cmd, NULL); } void mcmd_sleep(struct master_state *ms) { long int secs; int t_end, t_now; ms->rest = get_long(ms->rest, &secs, true, "Invalid sleep time"); if (ms->rest == NULL) return; if (syntax) return; t_now = time(NULL); t_end = t_now + secs; while (t_now <= t_end && !terminate) { struct response *client_resp; client_resp = receive_response(false, t_end - t_now); t_now = time(NULL); if (client_resp != NULL) { errno = 0; if (err_accounting) fprintf(stderr, "%s\n%s\n", ms->last, client_resp->r_original); array_strcpy(errdetail, "Unexpected response"); ms->rest = NULL; free_response(client_resp, NULL); } /* If sleep 0 or we have run out, just want single iteration */ if (t_now == t_end) break; } } void mcmd_open_brace(struct master_state *ms) { if (ms->inbrace) { errno = 0; array_strcpy(errdetail, "Illegal nested brace"); ms->rest = NULL; } ms->count = 0; ms->inbrace = true; } void mcmd_close_brace(struct master_state *ms) { if (!ms->inbrace) { errno = 0; array_strcpy(errdetail, "Unmatched close brace"); ms->rest = NULL; } else if (!syntax) { ms->inbrace = false; wait_for_expected_responses("responses", ms->count, ms->last, true); fprintf(output, "All responses received OK\n"); ms->count = 0; } else { ms->inbrace = false; } } void mcmd_clients(struct master_state *ms) { if (ms->inbrace) { errno = 0; array_strcpy(errdetail, "CLIENTS command not allowed inside brace"); ms->rest = NULL; return; } while (ms->rest != NULL && ms->rest[0] != '\0' && ms->rest[0] != '#') { /* Get the next client to expect */ ms->rest = get_client(ms->rest, &ms->client, true, REQUIRES_EITHER); if (ms->rest == NULL) return; /* Build an EXPECT client * HELLO OK "client" */ ms->expect_resp = alloc_resp(ms->client); ms->expect_resp->r_cmd = CMD_HELLO; ms->expect_resp->r_tag = -1; ms->expect_resp->r_status = STATUS_OK; array_strcpy(ms->expect_resp->r_data, ms->client->c_name); array_sprintf(ms->expect_resp->r_original, "EXPECT %s * HELLO OK \"%s\"", ms->client->c_name, ms->client->c_name); ms->count++; if (syntax) { free_response(ms->expect_resp, NULL); } else { /* Add response to list of expected responses */ add_response(ms->expect_resp, &expected_responses); } } if (ms->count == 0) { errno = 0; array_strcpy(errdetail, "Expected at least one client"); ms->rest = NULL; return; } if (!syntax) { wait_for_expected_responses("clients", ms->count, ms->last, true); fprintf(output, "All clients said HELLO OK\n"); } ms->count = 0; } void mcmd_fork(struct master_state *ms) { if (ms->inbrace) { errno = 0; array_strcpy(errdetail, "FORK command not allowed inside brace"); ms->rest = NULL; return; } /* Get the client to send FORK to */ ms->rest = get_client(ms->rest, &ms->client, syntax, REQUIRES_MORE); if (ms->rest == NULL) return; /* Build an EXPECT client * FORK OK "client" */ ms->client_cmd = alloc_resp(ms->client); ms->client_cmd->r_cmd = CMD_FORK; ms->client_cmd->r_tag = -1; ms->client_cmd->r_status = STATUS_OK; ms->count++; /* Get the client that will be created */ ms->rest = get_client(ms->rest, &ms->client, true, REQUIRES_NO_MORE); if (ms->rest == NULL) return; /* Build an EXPECT client * HELLO OK "client" */ ms->expect_resp = alloc_resp(ms->client); ms->expect_resp->r_cmd = CMD_HELLO; ms->expect_resp->r_tag = -1; ms->expect_resp->r_status = STATUS_OK; /* Use the created client's name as the FORK data */ array_strcpy(ms->client_cmd->r_data, ms->expect_resp->r_client->c_name); array_strcpy(ms->expect_resp->r_data, ms->expect_resp->r_client->c_name); array_sprintf(ms->client_cmd->r_original, "EXPECT %s * FORK OK \"%s\"", ms->client->c_name, ms->client->c_name); array_sprintf(ms->expect_resp->r_original, "EXPECT %s * HELLO OK \"%s\"", ms->client->c_name, ms->client->c_name); ms->count++; if (syntax) { free_response(ms->client_cmd, NULL); free_response(ms->expect_resp, NULL); } else { /* Send the command */ send_cmd(ms->client_cmd); /* Now fixup to expect client name as FORK OK data */ array_strcpy(ms->client_cmd->r_data, ms->client_cmd->r_client->c_name); /* Add responses to list of expected responses */ add_response(ms->client_cmd, &expected_responses); add_response(ms->expect_resp, &expected_responses); } if (!syntax) { wait_for_expected_responses("clients", ms->count, ms->last, true); fprintf(output, "All clients responded OK\n"); } ms->count = 0; } void mcmd_expect(struct master_state *ms) { ms->rest = get_client(ms->rest, &ms->client, true, REQUIRES_MORE); if (ms->rest == NULL) return; ms->expect_resp = alloc_resp(ms->client); if (script) { array_sprintf(ms->expect_resp->r_original, "Line %4ld: EXPECT %s %s", lno, ms->client->c_name, ms->rest); } else { array_sprintf(ms->expect_resp->r_original, "EXPECT %s %s", ms->client->c_name, ms->rest); } ms->rest = parse_response(ms->rest, ms->expect_resp); if (ms->rest == NULL || syntax) { free_response(ms->expect_resp, NULL); } else if (ms->inbrace) { add_response(ms->expect_resp, &expected_responses); ms->count++; } else if (expect_one_response(ms->expect_resp, ms->last)) ms->rest = NULL; } void mcmd_simple(struct master_state *ms) { array_strcpy(ms->last, ms->line); ms->rest = get_client(ms->rest, &ms->client, syntax, REQUIRES_MORE); if (ms->rest == NULL) return; ms->client_cmd = alloc_resp(ms->client); if (ms->cmd == MCMD_SIMPLE_OK) ms->client_cmd->r_status = STATUS_OK; else if (ms->cmd == MCMD_SIMPLE_AVAILABLE) ms->client_cmd->r_status = STATUS_AVAILABLE; else if (ms->cmd == MCMD_SIMPLE_GRANTED) ms->client_cmd->r_status = STATUS_GRANTED; else if (ms->cmd == MCMD_SIMPLE_DEADLOCK) ms->client_cmd->r_status = STATUS_DEADLOCK; else ms->client_cmd->r_status = STATUS_DENIED; ms->rest = parse_request(ms->rest, ms->client_cmd, true); if (ms->rest == NULL) { free_response(ms->client_cmd, NULL); return; } switch (ms->client_cmd->r_cmd) { case CMD_OPEN: case CMD_CLOSE: case CMD_SEEK: case CMD_WRITE: case CMD_COMMENT: case CMD_ALARM: case CMD_HELLO: case CMD_QUIT: if (ms->cmd != MCMD_SIMPLE_OK) { array_sprintf(errdetail, "Simple %s command expects OK", commands[ms->client_cmd->r_cmd].cmd_name); errno = 0; ms->rest = NULL; } break; case CMD_READ: if (ms->cmd != MCMD_SIMPLE_OK) { array_sprintf(errdetail, "Simple %s command expects OK", commands[ms->client_cmd->r_cmd].cmd_name); errno = 0; ms->rest = NULL; } else if (ms->client_cmd->r_length == 0 || ms->client_cmd->r_data[0] == '\0') { array_strcpy(errdetail, "Simple READ must have compare data"); errno = 0; ms->rest = NULL; } break; case CMD_LOCKW: if (ms->cmd != MCMD_SIMPLE_DEADLOCK) { array_sprintf(errdetail, "%s command can not be a simple command", commands[ms->client_cmd->r_cmd].cmd_name); errno = 0; ms->rest = NULL; } break; case CMD_LOCK: case CMD_HOP: if (ms->cmd != MCMD_SIMPLE_DENIED && ms->cmd != MCMD_SIMPLE_GRANTED) { array_sprintf(errdetail, "Simple %s command requires GRANTED or DENIED status", commands[ms->client_cmd->r_cmd]. cmd_name); errno = 0; ms->rest = NULL; } break; case CMD_TEST: case CMD_LIST: if (ms->cmd != MCMD_SIMPLE_AVAILABLE) { array_sprintf(errdetail, "Simple %s command requires AVAILABLE status", commands[ms->client_cmd->r_cmd].cmd_name); errno = 0; ms->rest = NULL; } break; case CMD_UNLOCK: case CMD_UNHOP: if (ms->cmd != MCMD_SIMPLE_GRANTED) { array_sprintf(errdetail, "Simple %s command requires GRANTED status", commands[ms->client_cmd->r_cmd].cmd_name); errno = 0; ms->rest = NULL; } break; case CMD_FORK: array_strcpy(errdetail, "FORK not compatible with a simple command"); break; case NUM_COMMANDS: array_strcpy(errdetail, "Invalid command"); errno = 0; ms->rest = NULL; break; } if (ms->rest == NULL || syntax) { free_response(ms->client_cmd, NULL); return; } send_cmd(ms->client_cmd); /* We can't know what file descriptor will be returned */ ms->client_cmd->r_fno = -1; sprintf_resp(ms->out, sizeof(ms->out), "EXPECT", ms->client_cmd); fprintf(output, "%s", ms->out); if (expect_one_response(ms->client_cmd, ms->last)) ms->rest = NULL; } void master_command(void) { struct master_state ms = { .inbrace = false, .count = 0,}; ms.last[0] = '\0'; while (1) { ms.len = readln(input, ms.line, sizeof(ms.line)); lno++; if (ms.len < 0) { array_sprintf(ms.line, "QUIT"); ms.len = strlen(ms.line); if (!syntax) fprintf(output, "QUIT\n"); } ms.rest = SkipWhite(ms.line, REQUIRES_MORE, "Invalid line"); /* Skip totally blank line and comments */ if (ms.rest == NULL || ms.rest[0] == '#') continue; if (script && !syntax) fprintf(output, "Line %4ld: %s\n", lno, ms.line); ms.rest = get_token_value(ms.rest, &ms.cmd, master_commands, true, REQUIRES_EITHER, "Invalid master command"); if (ms.rest != NULL) switch ((enum master_cmd) ms.cmd) { case MCMD_QUIT: if (syntax) return; else handle_quit(); break; case MCMD_STRICT: ms.rest = get_on_off(ms.rest, &strict); break; case MCMD_FATAL: ms.rest = get_on_off(ms.rest, &error_is_fatal); break; case MCMD_CLIENT_CMD: mcmd_client_cmd(&ms); break; case MCMD_SLEEP: mcmd_sleep(&ms); break; case MCMD_OPEN_BRACE: mcmd_open_brace(&ms); break; case MCMD_CLOSE_BRACE: mcmd_close_brace(&ms); break; case MCMD_CLIENTS: mcmd_clients(&ms); break; case MCMD_EXPECT: mcmd_expect(&ms); break; case MCMD_FORK: mcmd_fork(&ms); break; case MCMD_SIMPLE_OK: case MCMD_SIMPLE_AVAILABLE: case MCMD_SIMPLE_GRANTED: case MCMD_SIMPLE_DENIED: case MCMD_SIMPLE_DEADLOCK: mcmd_simple(&ms); break; } if (ms.rest == NULL) { error(); if (syntax) fprintf(output, "Line %4ld: %s\n", lno, ms.line); if ((error_is_fatal && !syntax) || terminate) handle_quit(); } if (!strict && !ms.inbrace && !script) break; if (!script) { fprintf(output, "> "); fflush(output); } } } void sighandler(int sig) { switch (sig) { case SIGINT: case SIGTERM: case SIGUSR1: terminate = true; break; case SIGPIPE: terminate = true; break; } } int main(int argc, char **argv) { int opt; struct response *resp; struct sigaction sigact; bool syntax_only = false; int rc; input = stdin; output = stdout; memset(&sigact, 0, sizeof(sigact)); sigact.sa_handler = sighandler; rc = sigaction(SIGINT, &sigact, NULL); if (rc == -1) fatal( "sigaction(SIGINT, &sigact, NULL) returned -1 errno %d \"%s\"\n", errno, strerror(errno)); rc = sigaction(SIGTERM, &sigact, NULL); if (rc == -1) fatal( "sigaction(SIGTERM, &sigact, NULL) returned -1 errno %d \"%s\"\n", errno, strerror(errno)); rc = sigaction(SIGUSR1, &sigact, NULL); if (rc == -1) fatal( "sigaction(SIGUSR1, &sigact, NULL) returned -1 errno %d \"%s\"\n", errno, strerror(errno)); rc = sigaction(SIGPIPE, &sigact, NULL); if (rc == -1) fatal( "sigaction(SIGPIPE, &sigact, NULL) returned -1 errno %d \"%s\"\n", errno, strerror(errno)); rc = sigfillset(&full_signal_set); if (rc == -1) fatal( "sigfillset(&full_signal_set) returned -1 errno %d \"%s\"\n", errno, strerror(errno)); sigprocmask(SIG_SETMASK, &full_signal_set, &original_signal_set); /* now parsing options with getopt */ while ((opt = getopt(argc, argv, options)) != EOF) { switch (opt) { case 'e': duperrors = true; err_accounting = true; break; case 'd': duperrors = true; break; case 'p': port = atoi(optarg); break; case 'q': quiet = true; break; case 's': strict = true; break; case 'x': input = fopen(optarg, "r"); if (input == NULL) fatal("Could not open %s\n", optarg); script = true; break; case 'k': syntax_only = true; break; case 'f': error_is_fatal = true; break; case '?': case 'h': default: /* display the help */ fprintf(stderr, "%s", usage); fflush(stderr); exit(0); break; } } open_socket(); if (script) { syntax = true; master_command(); if (num_errors != 0) { fprintf(stdout, "Syntax checks fail\n"); return 1; } if (syntax_only) { fprintf(stdout, "Syntax checks ok\n"); return 0; } syntax = false; global_tag = lno; lno = 0; rewind(input); master_command(); /* Never returns */ } while (1) { resp = receive_response(true, -1); if (strict && resp != NULL) { errno = 0; array_strcpy(errdetail, "Unexpected response"); free_response(resp, NULL); error(); if (error_is_fatal) handle_quit(); } free_response(resp, NULL); master_command(); } /* Never get here */ return 0; } ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/multilock/ml_functions.c������������������������������������������������0000664�0000000�0000000�00000103212�13242724102�0022503�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright IBM Corporation, 2012 * Contributor: Frank Filz <ffilzlnx@mindspring.com> * * * This software is a server that implements the NFS protocol. * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * */ #include "multilock.h" #include "assert.h" struct command_def commands[NUM_COMMANDS + 1] = { {"OPEN", 4}, {"CLOSE", 5}, {"LOCKW", 5}, {"LOCK", 4}, {"UNLOCK", 6}, {"TEST", 4}, {"LIST", 4}, {"HOP", 3}, {"UNHOP", 5}, {"SEEK", 4}, {"READ", 4}, {"WRITE", 5}, {"COMMENT", 7}, {"ALARM", 5}, {"HELLO", 5}, {"FORK", 4}, {"QUIT", 4}, {"UNKNOWN", 0}, }; char errdetail[MAXSTR * 2 + 1]; char badtoken[MAXSTR + 1]; struct client *client_list; FILE *input; FILE *output; bool script; bool quiet; bool duperrors; bool strict; bool error_is_fatal; long int global_tag; long int saved_tags[26]; bool syntax; long int lno; long int get_global_tag(bool increment) { if (script && increment) global_tag = lno; else if (increment) global_tag++; return global_tag; } struct token on_off[] = { {"on", 2, true}, {"off", 3, false}, {"", 0, true} }; struct token lock_types[] = { {"read", 4, F_RDLCK}, {"write", 5, F_WRLCK}, {"shared", 6, F_RDLCK}, {"exclusive", 9, F_WRLCK}, {"F_RDLCK", 7, F_RDLCK}, {"F_WRLCK", 7, F_WRLCK}, {"unlock", 6, F_UNLCK}, {"F_UNLCK", 7, F_UNLCK}, {"*", 1, -1}, {"", 0, 0} }; struct token read_write_flags[] = { {"rw", 2, O_RDWR}, {"ro", 2, O_RDONLY}, {"wo", 2, O_WRONLY}, {"O_RDWR", 6, O_RDWR}, {"O_RDONLY", 8, O_RDONLY}, {"O_WRONLY", 8, O_WRONLY}, {"", 0, 0} }; const char *str_read_write_flags(int flags) { switch (flags & O_ACCMODE) { case O_RDWR: return "rw"; case O_RDONLY: return "ro"; case O_WRONLY: return "wo"; } return "unknown"; } struct token open_flags[] = { {"create", 6, O_CREAT}, {"creat", 5, O_CREAT}, {"O_CREAT", 7, O_CREAT}, {"", 0, 0} }; void sprintf_open_flags(char **rest, int *left, int flags) { int i; int ex_flags = 0; for (i = 0; open_flags[i].t_len != 0; i++) { if ((ex_flags & open_flags[i].t_value) == 0 && (flags & open_flags[i].t_value) == open_flags[i].t_value) { /* Append open flag to line */ sprint_left(*rest, *left, " %s", open_flags[i].t_name); } ex_flags |= open_flags[i].t_value; } } struct token lock_modes[] = { {"POSIX", 5, LOCK_MODE_POSIX}, {"OFD", 3, LOCK_MODE_OFD}, {"", 0, 0} }; void sprintf_lock_modes(char **rest, int *left, int mode) { int i; for (i = 0; lock_modes[i].t_len != 0; i++) { if (mode == lock_modes[i].t_value) { /* Append lock mode to line */ sprint_left(*rest, *left, " %s", lock_modes[i].t_name); break; } } } const char *str_lock_mode(int lock_mode) { switch ((enum lock_mode) lock_mode) { case LOCK_MODE_POSIX: return "POSIX"; case LOCK_MODE_OFD: return "OFD"; } return "unknown"; } int readln(FILE *in, char *buf, int buflen) { int len; if (fgets(buf, buflen, in) != NULL) { len = strlen(buf); if (buf[len - 1] == '\n') buf[--len] = '\0'; return len; } else { return -1; } } char *SkipWhite(char *line, enum requires_more requires_more, const char *who) { char *c = line; /* Skip white space */ while (*c == ' ' || *c == '\t') c++; switch (requires_more) { case REQUIRES_MORE: if (*c == '\0' || *c == '#') { array_sprintf(errdetail, "Expected more characters on command %s", who); if (*c == '\0') array_strcpy(badtoken, "<NULL>"); else array_strcpy(badtoken, c); errno = EINVAL; return NULL; } break; case REQUIRES_NO_MORE: if (*c != '\0' && *c != '#') { array_sprintf(errdetail, "Extra characters on command %s", who); array_strcpy(badtoken, c); errno = EINVAL; return NULL; } break; case REQUIRES_EITHER: break; } /* if(*c == '#') c += strlen(c); */ return c; } char *get_token(char *line, char **token, int *len, bool optional, enum requires_more requires_more, const char *invalid) { char *c = line; if (optional) c = SkipWhite(c, REQUIRES_EITHER, invalid); else c = SkipWhite(c, REQUIRES_MORE, invalid); if (c == NULL) return c; if (optional && (*c == '\0' || *c == '#')) { *token = NULL; return c; } /* Skip token */ *token = c; while (*c != ' ' && *c != '\0' && *c != '#') c++; *len = c - *token; return c; } char *get_token_value(char *line, int *value, struct token *tokens, bool optional, enum requires_more requires_more, const char *invalid) { char *t; int len; char *c = get_token(line, &t, &len, optional, requires_more, invalid); struct token *tok; if (c == NULL) return c; if (optional && t == NULL) { tok = tokens; while (tok->t_len != 0) tok++; *value = tok->t_value; return c; } for (tok = tokens; tok->t_len != 0; tok++) { if (tok->t_len != len) continue; if (strncasecmp(t, tok->t_name, len) == 0) { *value = tok->t_value; return SkipWhite(c, requires_more, invalid); } } if (optional) { /* optional token not found, rewind to before token and * use the default value */ *value = tok->t_value; return t; } array_strcpy(errdetail, invalid); array_strncpy(badtoken, t, len); errno = EINVAL; return NULL; } char *get_client(char *line, struct client **pclient, bool create, enum requires_more requires_more) { char *c = line; char *t; struct client *client; int len; c = get_token(line, &t, &len, false, requires_more, "Invalid client"); if (c == NULL) return c; for (client = client_list; client != NULL; client = client->c_next) { if (strlen(client->c_name) != len) continue; if (strncmp(t, client->c_name, len) == 0) break; } *pclient = client; if (client == NULL) { if (create) { client = calloc(1, sizeof(*client)); if (client == NULL) { array_strcpy(errdetail, "Could not create client"); errno = ENOMEM; array_strncpy(badtoken, t, len); return NULL; } array_strncpy(client->c_name, t, len); *pclient = client; c = SkipWhite(c, requires_more, "get_client"); if (c == NULL) free(client); else if (!quiet && !syntax) fprintf(output, "Created temp client %s\n", client->c_name); return c; } else { array_strcpy(errdetail, "Could not find client"); errno = ENOENT; array_strncpy(badtoken, t, len); return NULL; } } return SkipWhite(c, requires_more, "get_client"); } char *get_command(char *line, enum commands *cmd) { enum commands i; char *t; char *c; int len; *cmd = NUM_COMMANDS; c = get_token(line, &t, &len, false, REQUIRES_EITHER, "Invalid command 1"); if (c == NULL) return c; for (i = CMD_OPEN; i < NUM_COMMANDS; i++) { if (len == commands[i].cmd_len && strncasecmp(t, commands[i].cmd_name, commands[i].cmd_len) == 0) { *cmd = i; if (i == CMD_QUIT) return SkipWhite(c, REQUIRES_EITHER, ""); else return SkipWhite(c, REQUIRES_MORE, "Invalid command 2"); } } array_strcpy(errdetail, "Invalid command 3"); array_strcpy(badtoken, line); return NULL; } char *get_long(char *line, long int *value, enum requires_more requires_more, const char *invalid) { char *c; char *t; char *e; int len; c = get_token(line, &t, &len, false, requires_more, invalid); if (c == NULL) return c; /* Extract value */ if (*t == '*' && len == 1) *value = -1; else { *value = strtol(t, &e, 0); if (e != NULL && e != c) { array_strcpy(errdetail, invalid); array_strncpy(badtoken, t, len); errno = EINVAL; return NULL; } } return SkipWhite(c, requires_more, invalid); } char *get_longlong(char *line, long long int *value, enum requires_more requires_more, const char *invalid) { char *c; char *t; char *e; int len; c = get_token(line, &t, &len, false, requires_more, invalid); if (c == NULL) return c; /* Extract value */ if (*t == '*' && len == 1) *value = -1; else { *value = strtoll(t, &e, 0); if (e != NULL && e != c) { array_strcpy(errdetail, invalid); array_strncpy(badtoken, t, len); errno = EINVAL; return NULL; } } return SkipWhite(c, requires_more, invalid); } char *get_lock_type(char *line, int *type) { return get_token_value(line, type, lock_types, false, REQUIRES_MORE, "Invalid lock type"); } char *get_on_off(char *line, bool *value) { int tvalue; char *rest; rest = get_token_value(line, &tvalue, on_off, true, REQUIRES_NO_MORE, "Invalid on/off"); *value = (bool) tvalue; return rest; } char *get_fpos(char *line, long int *fpos, enum requires_more requires_more) { char *c = line; /* Get fpos */ c = get_long(c, fpos, requires_more, "Invalid fpos"); if (c == NULL) return c; if (*fpos < 0 || *fpos > MAXFPOS) { array_strcpy(errdetail, "Invalid fpos"); array_sprintf(badtoken, "%ld", *fpos); errno = EINVAL; return NULL; } return c; } char *get_rdata(char *line, struct response *resp, int max, enum requires_more requires_more) { char *c = line; char *t; int quoted; int len; assert(max < sizeof(resp->r_data)); c = SkipWhite(c, REQUIRES_MORE, "get rdata 1"); if (c == NULL) return c; if (*c != '"') { if (requires_more != REQUIRES_NO_MORE) { errno = EINVAL; array_strcpy(errdetail, "Expected string"); array_strcpy(badtoken, c); return NULL; } quoted = false; } else { c++; quoted = true; } t = c; if (quoted) while (*c != '"' && *c != '\0') c++; else while (*c != '\0') c++; if (quoted && *c == '\0') { errno = EINVAL; array_strcpy(errdetail, "Unterminated string"); array_strcpy(badtoken, t - 1); return NULL; } len = c - t; if (len > max) { errno = EINVAL; array_sprintf(errdetail, "String length %d longer than %d", len, max); array_strcpy(badtoken, t - 1); return NULL; } resp->r_length = len; array_strncpy(resp->r_data, t, len); c++; return SkipWhite(c, requires_more, "get rdata 2"); } char *get_open_opts(char *line, long int *fpos, int *flags, int *mode, int *lock_mode) { char *c; int flag2 = -1; /* Set default mode */ *mode = S_IRUSR | S_IWUSR; /* Get fpos */ c = get_fpos(line, fpos, REQUIRES_MORE); if (c == NULL) return c; c = get_token_value(c, flags, read_write_flags, false, REQUIRES_MORE, "Invalid open flags"); if (c == NULL) return c; *flags |= O_SYNC; /* Check optional open flags */ while (flag2 != 0) { c = get_token_value(c, &flag2, open_flags, true, REQUIRES_MORE, "Invalid optional open flag"); if (c == NULL) return c; *flags |= flag2; } /* Check optional lock mode, default to POSIX */ *lock_mode = LOCK_MODE_POSIX; c = get_token_value(c, lock_mode, lock_modes, true, REQUIRES_MORE, "Invalid optional lock mode"); if (c == NULL) return c; return SkipWhite(c, REQUIRES_MORE, "get_open_opts"); } const char *str_status(enum status status) { switch (status) { case STATUS_OK: return "OK"; case STATUS_AVAILABLE: return "AVAILABLE"; case STATUS_GRANTED: return "GRANTED"; case STATUS_DENIED: return "DENIED"; case STATUS_DEADLOCK: return "DEADLOCK"; case STATUS_CONFLICT: return "CONFLICT"; case STATUS_CANCELED: return "CANCELED"; case STATUS_COMPLETED: return "COMPLETED"; case STATUS_ERRNO: return "ERRNO"; case STATUS_PARSE_ERROR: return "PARSE_ERROR"; case STATUS_ERROR: return "ERROR"; } return "unknown"; } char *get_status(char *line, struct response *resp) { enum status stat; char *t; int len; char *c = get_token(line, &t, &len, false, REQUIRES_EITHER, "Invalid status"); if (c == NULL) return c; for (stat = STATUS_OK; stat <= STATUS_ERROR; stat++) { const char *cmp_status = str_status(stat); if (strlen(cmp_status) != len) continue; if (strncasecmp(cmp_status, t, len) == 0) { enum requires_more requires_more = REQUIRES_MORE; resp->r_status = stat; if (stat == STATUS_COMPLETED || (resp->r_cmd == CMD_QUIT && stat == STATUS_OK)) requires_more = REQUIRES_NO_MORE; return SkipWhite(c, requires_more, "get_status"); } } array_strcpy(errdetail, "Invalid status"); array_strncpy(badtoken, t, len); errno = EINVAL; return NULL; } void free_client(struct client *client) { if (client == NULL) return; if (client->c_prev != NULL) client->c_prev->c_next = client->c_next; if (client->c_next != NULL) client->c_next->c_prev = client->c_prev; if (client_list == client) client_list = client->c_next; free(client); } void free_response(struct response *resp, struct response **list) { if (resp == NULL) return; if (list != NULL && *list == resp) *list = resp->r_next; if (resp->r_prev != NULL) resp->r_prev->r_next = resp->r_next; if (resp->r_next != NULL) resp->r_next->r_prev = resp->r_prev; if (resp->r_client != NULL && --resp->r_client->c_refcount == 0) { free_client(resp->r_client); resp->r_client = NULL; } free(resp); } const char *str_lock_type(int type) { switch (type) { case F_RDLCK: return "read"; case F_WRLCK: return "write"; case F_UNLCK: return "unlock"; } return "unknown"; } char *get_tag(char *line, struct response *resp, int required, enum requires_more requires_more) { if (*line == '$') { char *c = line + 1; if (tolower(*c) >= 'a' || tolower(*c) <= 'z') resp->r_tag = saved_tags[tolower(*c++) - 'a']; else resp->r_tag = get_global_tag(false); return SkipWhite(c, requires_more, "get_tag"); } if (required || (*line != '\0' && *line != '#')) return get_long(line, &resp->r_tag, requires_more, "Invalid tag"); resp->r_tag = -1; return line; } char *get_rq_tag(char *line, struct response *req, int required, enum requires_more requires_more) { if (*line == '$') { char *c = line + 1; req->r_tag = get_global_tag(true); if (tolower(*c) >= 'a' || tolower(*c) <= 'z') saved_tags[tolower(*c++) - 'a'] = get_global_tag(false); return SkipWhite(c, requires_more, "get_rq_tag"); } if (required || (*line != '\0' && *line != '#')) return get_long(line, &req->r_tag, requires_more, "Invalid tag"); req->r_tag = -1; return line; } void sprintf_resp(char *line, int size, const char *lead, struct response *resp) { char *rest = line; int left = size - 1; /* Leave room for terminating NUL */ if (lead != NULL) { const char *name = "<NULL>"; if (resp->r_client != NULL) name = resp->r_client->c_name; sprint_left(rest, left, "%s %s ", lead, name); } sprint_left(rest, left, "%ld %s %s", resp->r_tag, commands[resp->r_cmd].cmd_name, str_status(resp->r_status)); switch (resp->r_status) { case STATUS_OK: switch (resp->r_cmd) { case CMD_COMMENT: case CMD_HELLO: case CMD_FORK: sprint_left(rest, left, " \"%s\"\n", resp->r_data); break; case CMD_LOCKW: case CMD_LOCK: case CMD_UNLOCK: case CMD_TEST: case CMD_LIST: case CMD_HOP: case CMD_UNHOP: case NUM_COMMANDS: sprint_left(rest, left, " Unexpected Status\n"); break; case CMD_ALARM: sprint_left(rest, left, " %ld\n", resp->r_secs); break; case CMD_QUIT: sprint_left(rest, left, "\n"); break; case CMD_OPEN: sprint_left(rest, left, " %ld %ld\n", resp->r_fpos, resp->r_fno); break; case CMD_CLOSE: case CMD_SEEK: sprint_left(rest, left, " %ld\n", resp->r_fpos); break; case CMD_WRITE: sprint_left(rest, left, " %ld %lld\n", resp->r_fpos, resp->r_length); break; case CMD_READ: sprint_left(rest, left, " %ld %lld \"%s\"\n", resp->r_fpos, resp->r_length, resp->r_data); break; } break; case STATUS_AVAILABLE: case STATUS_GRANTED: case STATUS_DENIED: case STATUS_DEADLOCK: if (resp->r_cmd == CMD_LIST) { sprint_left(rest, left, " %ld %lld %lld\n", resp->r_fpos, resp->r_start, resp->r_length); } else { sprint_left(rest, left, " %ld %s %lld %lld\n", resp->r_fpos, str_lock_type(resp->r_lock_type), resp->r_start, resp->r_length); } break; case STATUS_CONFLICT: sprint_left(rest, left, " %ld %ld %s %lld %lld\n", resp->r_fpos, resp->r_pid, str_lock_type(resp->r_lock_type), resp->r_start, resp->r_length); break; case STATUS_CANCELED: if (resp->r_cmd == CMD_LOCKW) { sprint_left(rest, left, " %ld %s %lld %lld\n", resp->r_fpos, str_lock_type(resp->r_lock_type), resp->r_start, resp->r_length); } else if (resp->r_cmd == CMD_ALARM) { sprint_left(rest, left, " %ld\n", resp->r_secs); } break; case STATUS_COMPLETED: sprint_left(rest, left, "\n"); break; case STATUS_ERRNO: if (errno == 0) { sprint_left(rest, left, " %ld \"%s\"\n", resp->r_errno, errdetail); } else { sprint_left(rest, left, " %ld \"%s\" \"%s\" bad token \"%s\"\n", resp->r_errno, strerror(resp->r_errno), errdetail, badtoken); } break; case STATUS_PARSE_ERROR: break; case STATUS_ERROR: break; } /* Make sure we are NUL terminated even if we used the last byte. */ *rest = '\0'; } void respond(struct response *resp) { char line[MAXXFER]; sprintf_resp(line, sizeof(line), NULL, resp); if (output != stdout) { fputs(line, output); fflush(output); } if (resp->r_status >= STATUS_ERRNO) fprintf_stderr("%s", line); else if (!quiet) fprintf(stdout, "%s", line); } char *parse_response(char *line, struct response *resp) { char *rest; long long int verify_len; if (resp->r_original[0] == '\0') array_strcpy(resp->r_original, line); resp->r_cmd = NUM_COMMANDS; resp->r_tag = -1; rest = get_tag(line, resp, true, REQUIRES_MORE); if (rest == NULL) goto fail; rest = get_command(rest, &resp->r_cmd); if (rest == NULL) goto fail; rest = get_status(rest, resp); if (rest == NULL) goto fail; switch (resp->r_status) { case STATUS_OK: switch (resp->r_cmd) { case CMD_COMMENT: case CMD_HELLO: case CMD_FORK: rest = get_rdata(rest, resp, MAXSTR, REQUIRES_NO_MORE); break; case CMD_LOCKW: case CMD_LOCK: case CMD_UNLOCK: case CMD_TEST: case CMD_LIST: case CMD_HOP: case CMD_UNHOP: case NUM_COMMANDS: array_strcpy(errdetail, "Unexpected Status"); errno = EINVAL; array_sprintf(badtoken, "%s", str_status(resp->r_status)); goto fail; case CMD_ALARM: return get_long(rest, &resp->r_secs, REQUIRES_NO_MORE, "Invalid alarm time"); case CMD_QUIT: return rest; case CMD_OPEN: rest = get_fpos(rest, &resp->r_fpos, REQUIRES_MORE); if (rest == NULL) goto fail; rest = get_long(rest, &resp->r_fno, REQUIRES_NO_MORE, "Invalid file number"); break; case CMD_CLOSE: case CMD_SEEK: rest = get_fpos(rest, &resp->r_fpos, REQUIRES_NO_MORE); break; case CMD_WRITE: rest = get_fpos(rest, &resp->r_fpos, REQUIRES_MORE); if (rest == NULL) goto fail; rest = get_longlong(rest, &resp->r_length, REQUIRES_NO_MORE, "Invalid length"); break; case CMD_READ: rest = get_fpos(rest, &resp->r_fpos, REQUIRES_MORE); if (rest == NULL) goto fail; rest = get_longlong(rest, &resp->r_length, REQUIRES_MORE, "Invalid length"); if (rest == NULL) goto fail; verify_len = resp->r_length; rest = get_rdata(rest, resp, MAXSTR, REQUIRES_NO_MORE); if (verify_len != resp->r_length) { array_strcpy(errdetail, "Read length doesn't match"); errno = EINVAL; array_sprintf(badtoken, "%lld != %lld", verify_len, resp->r_length); goto fail; } break; } break; case STATUS_AVAILABLE: case STATUS_GRANTED: case STATUS_DENIED: case STATUS_DEADLOCK: rest = get_fpos(rest, &resp->r_fpos, REQUIRES_MORE); if (rest == NULL) goto fail; if (resp->r_cmd != CMD_LIST) { rest = get_lock_type(rest, &resp->r_lock_type); if (rest == NULL) goto fail; } rest = get_longlong(rest, &resp->r_start, REQUIRES_MORE, "Invalid lock start"); if (rest == NULL) goto fail; rest = get_longlong(rest, &resp->r_length, REQUIRES_NO_MORE, "Invalid lock length"); break; case STATUS_CONFLICT: rest = get_fpos(rest, &resp->r_fpos, REQUIRES_MORE); if (rest == NULL) goto fail; rest = get_long(rest, &resp->r_pid, REQUIRES_MORE, "Invalid conflict pid"); if (rest == NULL) goto fail; rest = get_lock_type(rest, &resp->r_lock_type); if (rest == NULL) goto fail; rest = get_longlong(rest, &resp->r_start, REQUIRES_MORE, "Invalid lock start"); if (rest == NULL) goto fail; rest = get_longlong(rest, &resp->r_length, REQUIRES_NO_MORE, "Invalid lock length"); break; case STATUS_CANCELED: if (resp->r_cmd == CMD_LOCKW) { rest = get_fpos(rest, &resp->r_fpos, REQUIRES_MORE); if (rest == NULL) goto fail; rest = get_lock_type(rest, &resp->r_lock_type); if (rest == NULL) goto fail; rest = get_longlong(rest, &resp->r_start, REQUIRES_MORE, "Invalid lock start"); if (rest == NULL) goto fail; rest = get_longlong(rest, &resp->r_length, REQUIRES_NO_MORE, "Invalid lock length"); } else if (resp->r_cmd == CMD_ALARM) { rest = get_long(rest, &resp->r_secs, REQUIRES_NO_MORE, "Invalid alarm time"); } break; case STATUS_COMPLETED: break; case STATUS_ERRNO: rest = get_long(rest, &resp->r_errno, REQUIRES_MORE, "Invalid errno"); if (rest == NULL) goto fail; array_strcpy(resp->r_data, rest); rest += strlen(rest); break; case STATUS_PARSE_ERROR: break; case STATUS_ERROR: break; } if (rest != NULL) return rest; fail: resp->r_status = STATUS_PARSE_ERROR; array_sprintf(resp->r_data, "%s %ld ERRNO %d \"%s\" \"%s\" bad token \"%s\"", commands[resp->r_cmd].cmd_name, resp->r_tag, errno, strerror(errno), errdetail, badtoken); resp->r_cmd = NUM_COMMANDS; return NULL; } #define return_if_ne_lock_type(expected, received) \ do { \ if (expected != -1 && \ expected != received) { \ array_sprintf(errdetail, \ "Unexpected lock type %s", \ lock_types[received].t_name); \ return false; \ } \ } while (0) #define return_if_ne_long(expected, received, fmt) \ do { \ if (expected != -1 && \ expected != received) { \ array_sprintf(errdetail, fmt " %ld", received); \ return false; \ } \ } while (0) #define return_if_ne_longlong(expected, received, fmt) \ do { \ if (expected != -1 && \ expected != received) { \ array_sprintf(errdetail, fmt " %lld", \ received); \ return false; \ } \ } while (0) #define return_if_ne_string(expected, received, fmt) \ do { \ if (strcmp(expected, "*") != 0 && \ strcmp(expected, received) != 0) { \ array_sprintf(errdetail, fmt " %s", received); \ return false; \ } \ } while (0) int compare_responses(struct response *expected, struct response *received) { errno = 0; if (received == NULL) { array_strcpy(errdetail, "Unexpected NULL response"); return false; } if (expected->r_client != received->r_client && strcmp(expected->r_client->c_name, received->r_client->c_name) != 0) { array_sprintf(errdetail, "Unexpected response from %s", received->r_client->c_name); return false; } if (expected->r_cmd != received->r_cmd) { array_sprintf(errdetail, "Unexpected command %s", commands[received->r_cmd].cmd_name); return false; } return_if_ne_long(expected->r_tag, received->r_tag, "Unexpected tag"); if (expected->r_status != received->r_status) { array_sprintf(errdetail, "Unexpected status %s", str_status(received->r_status)); return false; } switch (expected->r_status) { case STATUS_OK: switch (expected->r_cmd) { case CMD_COMMENT: case CMD_HELLO: case CMD_FORK: /* could check string, but not worth it - HELLO has * already set client name and that has been checked */ break; case CMD_LOCKW: case CMD_LOCK: case CMD_UNLOCK: case CMD_TEST: case CMD_LIST: case CMD_HOP: case CMD_UNHOP: case NUM_COMMANDS: array_sprintf(errdetail, "Unexpected Status %s for %s", str_status(received->r_status), commands[received->r_cmd].cmd_name); return false; case CMD_ALARM: return_if_ne_long(expected->r_secs, received->r_secs, "Unexpected secs"); break; case CMD_QUIT: break; case CMD_OPEN: return_if_ne_long(expected->r_fpos, received->r_fpos, "Unexpected fpos"); return_if_ne_long(expected->r_fno, received->r_fno, "Unexpected file number"); break; case CMD_CLOSE: case CMD_SEEK: return_if_ne_long(expected->r_fpos, received->r_fpos, "Unexpected fpos"); break; case CMD_WRITE: return_if_ne_long(expected->r_fpos, received->r_fpos, "Unexpected fpos"); return_if_ne_longlong(expected->r_length, received->r_length, "Unexpected length"); break; case CMD_READ: return_if_ne_long(expected->r_fpos, received->r_fpos, "Unexpected fpos"); return_if_ne_longlong(expected->r_length, received->r_length, "Unexpected length"); return_if_ne_string(expected->r_data, received->r_data, "Unexpected data"); break; } break; case STATUS_AVAILABLE: case STATUS_GRANTED: case STATUS_DENIED: case STATUS_DEADLOCK: return_if_ne_long(expected->r_fpos, received->r_fpos, "Unexpected fpos"); if (expected->r_cmd != CMD_LIST) return_if_ne_lock_type(expected->r_lock_type, received->r_lock_type); return_if_ne_longlong(expected->r_start, received->r_start, "Unexpected start"); return_if_ne_longlong(expected->r_length, received->r_length, "Unexpected length"); break; case STATUS_CONFLICT: return_if_ne_long(expected->r_fpos, received->r_fpos, "Unexpected fpos"); return_if_ne_long(expected->r_pid, received->r_pid, "Unexpected pid"); return_if_ne_lock_type(expected->r_lock_type, received->r_lock_type); return_if_ne_longlong(expected->r_start, received->r_start, "Unexpected start"); return_if_ne_longlong(expected->r_length, received->r_length, "Unexpected length"); break; case STATUS_CANCELED: if (expected->r_cmd == CMD_LOCKW) { return_if_ne_long(expected->r_fpos, received->r_fpos, "Unexpected fpos"); return_if_ne_lock_type(expected->r_lock_type, received->r_lock_type); return_if_ne_longlong(expected->r_start, received->r_start, "Unexpected start"); return_if_ne_longlong(expected->r_length, received->r_length, "Unexpected length"); } else if (expected->r_cmd == CMD_ALARM) { return_if_ne_long(expected->r_secs, received->r_secs, "Unexpected secs"); } break; case STATUS_COMPLETED: break; case STATUS_ERRNO: break; case STATUS_PARSE_ERROR: break; case STATUS_ERROR: break; } return true; } void add_response(struct response *resp, struct response **list) { resp->r_next = *list; if (*list != NULL) (*list)->r_prev = resp; *list = resp; } struct response *check_expected_responses(struct response *expected_responses, struct response *client_resp) { struct response *expected_resp = expected_responses; while (expected_resp != NULL && !compare_responses(expected_resp, client_resp)) expected_resp = expected_resp->r_next; return expected_resp; } char *parse_alarm(char *line, struct response *req) { return get_long(line, &req->r_secs, REQUIRES_NO_MORE, "Invalid secs"); } char *parse_open(char *line, struct response *req) { char *more = get_open_opts(line, &req->r_fpos, &req->r_flags, &req->r_mode, &req->r_lock_type); if (more == NULL) return more; return get_rdata(more, req, PATH_MAX - 1, REQUIRES_NO_MORE); } char *parse_write(char *line, struct response *req) { char *more; more = get_fpos(line, &req->r_fpos, REQUIRES_MORE); if (more == NULL) return more; return get_rdata(more, req, MAXSTR, REQUIRES_NO_MORE); } char *parse_read(char *line, struct response *req) { char *more; req->r_data[0] = '\0'; more = get_fpos(line, &req->r_fpos, REQUIRES_MORE); if (more == NULL) return more; if (*more == '"') return get_rdata(more, req, MAXSTR, REQUIRES_NO_MORE); else return get_longlong(more, &req->r_length, REQUIRES_NO_MORE, "Invalid len"); } char *parse_seek(char *line, struct response *req) { char *more; more = get_fpos(line, &req->r_fpos, REQUIRES_MORE); if (more == NULL) return more; return get_longlong(more, &req->r_start, REQUIRES_NO_MORE, "Invalid pos"); } char *parse_lock(char *line, struct response *req) { char *more; more = get_fpos(line, &req->r_fpos, REQUIRES_MORE); if (more == NULL) return more; more = get_lock_type(more, &req->r_lock_type); if (more == NULL) return more; if (req->r_lock_type != F_RDLCK && req->r_lock_type != F_WRLCK) { errno = EINVAL; array_strcpy(errdetail, "Invalid lock type"); array_sprintf(badtoken, "%s", str_lock_type(req->r_lock_type)); } more = get_longlong(more, &req->r_start, REQUIRES_MORE, "Invalid lock start"); if (more == NULL) return more; return get_longlong(more, &req->r_length, REQUIRES_NO_MORE, "Invalid lock len"); } char *parse_unlock(char *line, struct response *req) { char *more; req->r_lock_type = F_UNLCK; more = get_fpos(line, &req->r_fpos, REQUIRES_MORE); if (more == NULL) return more; more = get_longlong(more, &req->r_start, REQUIRES_MORE, "Invalid lock start"); if (more == NULL) return more; return get_longlong(more, &req->r_length, REQUIRES_NO_MORE, "Invalid lock len"); } char *parse_close(char *line, struct response *req) { return get_fpos(line, &req->r_fpos, REQUIRES_NO_MORE); } char *parse_list(char *line, struct response *req) { char *more; req->r_lock_type = F_WRLCK; more = get_fpos(line, &req->r_fpos, REQUIRES_MORE); if (more == NULL) return more; more = get_longlong(more, &req->r_start, REQUIRES_MORE, "Invalid lock start"); if (more == NULL) return more; return get_longlong(more, &req->r_length, REQUIRES_NO_MORE, "Invalid lock len"); } char *parse_string(char *line, struct response *req) { return get_rdata(line, req, MAXSTR, REQUIRES_NO_MORE); } char *parse_empty(char *line, struct response *req) { return line; } typedef char *(*parse_function_t) (char *line, struct response *req); parse_function_t parse_functions[NUM_COMMANDS] = { parse_open, parse_close, parse_lock, /* lock */ parse_lock, /* lockw */ parse_unlock, parse_lock, /* test */ parse_list, parse_lock, /* hop */ parse_unlock, /* unhop */ parse_seek, parse_read, parse_write, parse_string, /* comment */ parse_alarm, parse_string, /* hello */ parse_string, /* fork */ parse_empty, /* quit */ }; char *parse_request(char *line, struct response *req, int no_tag) { char *rest = line; req->r_cmd = NUM_COMMANDS; req->r_tag = -1; if (no_tag) req->r_tag = get_global_tag(true); else rest = get_rq_tag(rest, req, true, REQUIRES_MORE); if (rest == NULL) return rest; rest = get_command(rest, &req->r_cmd); if (rest == NULL) return rest; switch (req->r_cmd) { case CMD_OPEN: case CMD_CLOSE: case CMD_LOCKW: case CMD_LOCK: case CMD_UNLOCK: case CMD_TEST: case CMD_LIST: case CMD_HOP: case CMD_UNHOP: case CMD_SEEK: case CMD_READ: case CMD_WRITE: case CMD_HELLO: case CMD_FORK: case CMD_COMMENT: case CMD_ALARM: case CMD_QUIT: rest = parse_functions[req->r_cmd] (rest, req); break; case NUM_COMMANDS: break; } return rest; } void send_cmd(struct response *req) { char line[MAXXFER]; sprintf_req(line, sizeof(line), NULL, req); fputs(line, req->r_client->c_output); fflush(req->r_client->c_output); } void sprintf_req(char *line, int size, const char *lead, struct response *req) { char *rest = line; int left = size - 1; /* Leave room for terminating NUL */ if (lead != NULL) { const char *name = "<NULL>"; if (req->r_client != NULL) name = req->r_client->c_name; sprint_left(rest, left, "%s %s ", lead, name); } sprint_left(rest, left, "%ld %s", req->r_tag, commands[req->r_cmd].cmd_name); switch (req->r_cmd) { case CMD_COMMENT: case CMD_HELLO: case CMD_FORK: sprint_left(rest, left, " \"%s\"\n", req->r_data); break; case CMD_LOCKW: case CMD_LOCK: case CMD_TEST: case CMD_HOP: sprint_left(rest, left, " %ld %s %lld %lld\n", req->r_fpos, str_lock_type(req->r_lock_type), req->r_start, req->r_length); break; case CMD_UNLOCK: case CMD_LIST: case CMD_UNHOP: sprint_left(rest, left, " %ld %lld %lld\n", req->r_fpos, req->r_start, req->r_length); break; case NUM_COMMANDS: sprint_left(rest, left, " Unexpected Command\n"); break; case CMD_ALARM: sprint_left(rest, left, " %ld\n", req->r_secs); break; case CMD_QUIT: sprint_left(rest, left, "\n"); break; case CMD_OPEN: sprint_left(rest, left, " %ld %s", req->r_fpos, str_read_write_flags(req->r_flags)); sprintf_open_flags(&rest, &left, req->r_flags); sprintf_lock_modes(&rest, &left, req->r_lock_type); sprint_left(rest, left, " \"%s\"\n", req->r_data); break; case CMD_CLOSE: sprint_left(rest, left, " %ld\n", req->r_fpos); break; case CMD_SEEK: sprint_left(rest, left, " %ld %lld\n", req->r_fpos, req->r_start); break; case CMD_WRITE: sprint_left(rest, left, " %ld %s\n", req->r_fpos, req->r_data); break; case CMD_READ: sprint_left(rest, left, " %ld %lld\n", req->r_fpos, req->r_length); break; } /* Make sure we are NUL terminated even if we used the last byte. */ *rest = '\0'; } ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/multilock/ml_glusterfs_client.c�����������������������������������������0000664�0000000�0000000�00000100232�13242724102�0024046�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright (C) Red Hat Inc., 2017 * Contributor: Frank Filz <ffilzlnx@mindspring.com> * Contributor: Soumya koduri <skoduri@redhat.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * */ #include <fcntl.h> #include <pthread.h> #include <stdlib.h> #include <assert.h> #include <libgen.h> #include "multilock.h" #include "../../include/gsh_list.h" #include <glusterfs/api/glfs.h> #include <glusterfs/api/glfs-handles.h> /* command line syntax */ char options[] = "c:qdx:s:n:p:v:g:h?"; char usage[] = "Usage: ml_glusterfs_client -s server -v volname -p port -n name -g glusterserver [-q] [-d] [-c path]\n" " ml_glusterfs_client -v volname -g glusterserver -x script [-q] [-d] [-c path]\n" " ml_glusterfs_client -v volname -g glusterserver [-q] [-d] [-c path]\n" " ml_glusterfs_client may be run in three modes\n" " - In the first mode, the client will be driven by a master.\n" " - In the second mode, the client is driven by a script.\n" " - In the third mode, the client interractive.\n" " -s server - specify the master's hostname or IP address\n" " -p port - specify the master's port number\n" " -n name - specify the client's name\n" " -x script - specify the name of a script to execute\n" " -q - specify quiet mode\n" " -d - specify dup errors mode (errors are sent to stdout and stderr)\n" " -c path - chdir\n" " -g glusterserver -specify the hostname or IP address of glusterfs server\n" " -v volname - glusterfs volume name\n"; #define NUM_WORKER 4 #define POLL_DELAY 10 enum thread_type { THREAD_NONE, THREAD_MAIN, THREAD_WORKER, THREAD_POLL, THREAD_CANCEL }; struct work_item { struct glist_head queue; struct glist_head fno_work; struct response resp; enum thread_type work_owner; pthread_t work_thread; time_t next_poll; }; char server[MAXSTR]; char name[MAXSTR]; char portstr[MAXSTR]; char volname[MAXSTR]; char glusterserver[MAXSTR]; int port; char line[MAXXFER]; long int alarmtag; struct glfs_object *handles[MAXFPOS + 1]; struct glfs_fd *fds[MAXFPOS + 1]; enum lock_mode lock_mode[MAXFPOS + 1]; /* Global variables to manage work list */ struct glist_head fno_work[MAXFPOS + 1]; struct glist_head work_queue = GLIST_HEAD_INIT(work_queue); struct glist_head poll_queue = GLIST_HEAD_INIT(poll_queue); pthread_t threads[NUM_WORKER + 1]; pthread_mutex_t work_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t work_cond = PTHREAD_COND_INITIALIZER; enum thread_type a_worker = THREAD_WORKER; enum thread_type a_poller = THREAD_POLL; /* The CephFS mount */ struct ceph_mount_info *cmount; glfs_t *fs; struct glusterfs_fs *gl_fs; void openserver(void) { struct addrinfo *addr; int rc; struct addrinfo hint; int sock; struct response resp; if (!quiet) fprintf(stdout, "server=%s port=%d name=%s\n", server, port, name); memset(&hint, 0, sizeof(hint)); hint.ai_family = AF_INET; hint.ai_socktype = SOCK_STREAM; rc = getaddrinfo(server, portstr, &hint, &addr); if (rc != 0) fatal("getaddrinfo error %d \"%s\"\n", rc, gai_strerror(rc)); rc = socket(addr[0].ai_family, SOCK_STREAM, 0); if (rc == -1) fatal("socket failed with ERRNO %d \"%s\"\n", errno, strerror(errno)); sock = rc; rc = connect(sock, addr[0].ai_addr, addr[0].ai_addrlen); if (rc == -1) fatal("connect failed with ERRNO %d \"%s\"\n", errno, strerror(errno)); input = fdopen(sock, "r"); if (input == NULL) fatal( "Could not create input stream from socket ERROr %d \"%s\"\n", errno, strerror(errno)); output = fdopen(sock, "w"); if (output == NULL) fatal( "Could not create output stream from socket ERROr %d \"%s\"\n", errno, strerror(errno)); if (!quiet) fprintf(stdout, "connected to server %s:%d\n", server, port); resp.r_cmd = CMD_HELLO; resp.r_status = STATUS_OK; resp.r_tag = 0; array_strcpy(resp.r_data, name); respond(&resp); } bool do_fork(struct response *resp, bool use_server) { pid_t forked; if (!use_server) { fprintf_stderr("FORK may only be used in server mode\n"); return false; } forked = fork(); if (forked < 0) { /* Error */ resp->r_status = STATUS_OK; resp->r_errno = errno; if (!quiet) fprintf(stdout, "fork failed %d (%s)\n", (int) resp->r_errno, strerror(resp->r_errno)); return true; } else if (forked == 0) { /* Parent sends a FORK response */ array_strcpy(resp->r_data, name); resp->r_status = STATUS_OK; if (!quiet) fprintf(stdout, "fork succeeded\n"); return true; } if (!quiet) fprintf(stdout, "forked\n"); /* This is the forked process */ /* First close the old output. */ fclose(output); /* Setup the new client name. */ array_strcpy(name, resp->r_data); /* Then open a new connection to the server. */ openserver(); /* Response already sent. */ return false; } void command(void) { } void sighandler(int sig) { struct response resp; switch (sig) { case SIGALRM: resp.r_cmd = CMD_ALARM; resp.r_tag = alarmtag; resp.r_status = STATUS_COMPLETED; alarmtag = 0; respond(&resp); break; case SIGPIPE: case SIGIO: break; } } void do_alarm(struct response *resp) { unsigned int remain; remain = alarm(resp->r_secs); if (remain != 0) { struct response resp2; resp2.r_cmd = CMD_ALARM; resp2.r_tag = alarmtag; resp2.r_secs = remain; resp2.r_status = STATUS_CANCELED; respond(&resp2); } if (resp->r_secs != 0) alarmtag = resp->r_tag; else alarmtag = 0; resp->r_status = STATUS_OK; } void do_open(struct response *resp) { struct glfs_object *glhandle = NULL, *parent = NULL; struct stat sb; struct glfs_fd *glfd = NULL; if (fds[resp->r_fpos] != NULL) { resp->r_status = STATUS_ERRNO; resp->r_errno = EINVAL; array_strcpy(errdetail, "fpos in use"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } if ((resp->r_flags & O_CREAT) != 0) { char fullpath[PATH_MAX]; char *name; char *path; array_strcpy(fullpath, resp->r_data); name = basename(fullpath); path = dirname(fullpath); fprintf_stderr("path = '%s' name = '%s'\n", path, name); parent = glfs_h_lookupat(fs, NULL, path, &sb, 0); if (parent != NULL) { glhandle = glfs_h_creat(fs, parent, name, resp->r_flags, resp->r_mode, &sb); /* Release the parent directory. */ glfs_h_close(parent); if (!glhandle) { resp->r_status = STATUS_ERRNO; resp->r_errno = errno; array_strcpy(errdetail, "glfs_h_creat"); return; } } else { resp->r_status = STATUS_ERRNO; resp->r_errno = errno; array_sprintf(errdetail, "glfs_h_lookupat %s", path); return; } } else { glhandle = glfs_h_lookupat(fs, NULL, resp->r_data, &sb, 0); } if (glhandle != NULL) { glfd = glfs_h_open(fs, glhandle, resp->r_flags); if (!glfd) { resp->r_status = STATUS_ERRNO; resp->r_errno = errno; array_strcpy(errdetail, "glfs_h_open"); return; } } else { resp->r_status = STATUS_ERRNO; resp->r_errno = errno; array_strcpy(errdetail, "glfs_h_lookupat"); return; } if (!glfd) { resp->r_status = STATUS_ERRNO; resp->r_errno = errno; array_strcpy(badtoken, resp->r_data); return; } fds[resp->r_fpos] = glfd; handles[resp->r_fpos] = glhandle; lock_mode[resp->r_fpos] = (enum lock_mode) resp->r_lock_type; resp->r_fno = resp->r_fpos; resp->r_status = STATUS_OK; } void do_write(struct response *resp) { long long int rc; if (resp->r_fpos != 0 && fds[resp->r_fpos] == NULL) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } rc = glfs_write(fds[resp->r_fpos], resp->r_data, resp->r_length, 0); if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Write failed"); array_sprintf(badtoken, "%lld", resp->r_length); return; } if (rc != resp->r_length) { resp->r_status = STATUS_ERRNO; resp->r_errno = EIO; array_strcpy(errdetail, "Short write"); array_sprintf(badtoken, "%lld", rc); return; } resp->r_status = STATUS_OK; } void do_read(struct response *resp) { long long int rc; if (resp->r_fpos != 0 && fds[resp->r_fpos] == NULL) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } if (resp->r_length > MAXSTR) resp->r_length = MAXSTR; rc = glfs_read(fds[resp->r_fpos], resp->r_data, resp->r_length, 0); if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Read failed"); array_sprintf(badtoken, "%lld", resp->r_length); return; } resp->r_data[rc] = '\0'; resp->r_length = strlen(resp->r_data); resp->r_status = STATUS_OK; } void do_seek(struct response *resp) { int rc; if (resp->r_fpos != 0 && fds[resp->r_fpos] == NULL) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } rc = glfs_lseek(fds[resp->r_fpos], resp->r_start, SEEK_SET); if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Seek failed"); array_sprintf(badtoken, "%lld", resp->r_start); return; } resp->r_status = STATUS_OK; } void free_work(struct work_item *work) { /* Make sure the item isn't on any queues */ glist_del(&work->fno_work); glist_del(&work->queue); free(work); } void cancel_work_item(struct work_item *work) { switch (work->work_owner) { case THREAD_NONE: case THREAD_MAIN: case THREAD_CANCEL: /* nothing special to do */ break; case THREAD_WORKER: case THREAD_POLL: /* Mark the work item to be canceled */ work->work_owner = THREAD_CANCEL; pthread_kill(work->work_thread, SIGIO); /* Wait for thread to be done or cancel the work */ while (work->work_owner != THREAD_NONE) pthread_cond_wait(&work_cond, &work_mutex); } /* Done with the work item, free the memory. */ free_work(work); } static inline long long int lock_end(struct response *req) { if (req->r_length == 0) return LLONG_MAX; return req->r_start + req->r_length; } void cancel_work(struct response *req) { struct glist_head cancel_work = GLIST_HEAD_INIT(cancel_work); struct glist_head *glist; struct work_item *work; bool start_over = true; pthread_mutex_lock(&work_mutex); while (start_over) { start_over = false; glist_for_each(glist, fno_work + req->r_fpos) { work = glist_entry(glist, struct work_item, fno_work); if (work->resp.r_start >= req->r_start && lock_end(&work->resp) <= lock_end(req)) { /* Do something */ cancel_work_item(work); /* List may be messed up */ start_over = true; break; } } } pthread_mutex_unlock(&work_mutex); } /* Must only be called from main thread...*/ int schedule_work(struct response *resp) { struct work_item *work = calloc(1, sizeof(*work)); if (work == NULL) { errno = ENOMEM; return -1; } pthread_mutex_lock(&work_mutex); work->resp = *resp; work->work_owner = THREAD_NONE; glist_add_tail(&work_queue, &work->queue); glist_add_tail(fno_work + resp->r_fpos, &work->fno_work); /* Signal to the worker and polling threads there is new work */ pthread_cond_broadcast(&work_cond); pthread_mutex_unlock(&work_mutex); return 0; } bool do_lock(struct response *resp, enum thread_type thread_type) { int rc; struct flock lock; bool retry = false; uint64_t owner; if (resp->r_fpos != 0 && fds[resp->r_fpos] == NULL) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return true; } switch (lock_mode[resp->r_fpos]) { case LOCK_MODE_POSIX: if (resp->r_cmd == CMD_LOCKW) { retry = true; } owner = getpid(); break; case LOCK_MODE_OFD: if (resp->r_cmd == CMD_LOCKW) { retry = true; } owner = resp->r_fpos; break; } lock.l_whence = SEEK_SET; lock.l_type = resp->r_lock_type; lock.l_start = resp->r_start; lock.l_len = resp->r_length; lock.l_pid = 0; rc = glfs_fd_set_lkowner(fds[resp->r_fpos], (void *)&owner, sizeof(owner)); if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "setting lkowner failed"); array_sprintf(badtoken, "%lld %lld", resp->r_start, resp->r_length); return false; } rc = glfs_posix_lock(fds[resp->r_fpos], F_SETLK, &lock); if (rc < 0) rc = -errno; if (rc == -EAGAIN && retry && thread_type == THREAD_MAIN) { /* We need to schedule OFD blocked lock */ rc = schedule_work(resp); /* Check for scheduling success */ if (rc >= 0) return false; } if (rc < 0) { if (rc == -EAGAIN) { if (retry) { /* Let caller know we didn't complete */ return false; } resp->r_status = STATUS_DENIED; } else if (rc == -EINTR) { resp->r_status = STATUS_CANCELED; } else if (rc == -EDEADLK) { resp->r_status = STATUS_DEADLOCK; } else { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Lock failed"); array_sprintf(badtoken, "%s %lld %lld", str_lock_type(lock.l_type), resp->r_start, resp->r_length); } } else resp->r_status = STATUS_GRANTED; return true; } void do_hop(struct response *resp) { int rc; int pos; struct flock lock; uint64_t owner; if (resp->r_fpos != 0 && fds[resp->r_fpos] == NULL) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } switch (lock_mode[resp->r_fpos]) { case LOCK_MODE_POSIX: owner = getpid(); break; case LOCK_MODE_OFD: owner = resp->r_fpos; break; } for (pos = resp->r_start; pos < resp->r_start + resp->r_length; pos++) { if ((pos == 0) || (pos == (resp->r_start + resp->r_length - 1))) lock.l_start = pos; else if ((pos & 1) == 0) lock.l_start = pos - 1; else lock.l_start = pos + 1; lock.l_whence = SEEK_SET; lock.l_type = resp->r_lock_type; lock.l_len = 1; lock.l_pid = 0; rc = glfs_fd_set_lkowner(fds[resp->r_fpos], (void *)&owner, sizeof(owner)); if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "setting lkowner failed"); array_sprintf(badtoken, "%lld %lld", resp->r_start, resp->r_length); return; } rc = glfs_posix_lock(fds[resp->r_fpos], F_SETLK, &lock); if (rc < 0) rc = -errno; if (rc < 0) { if (rc == -EAGAIN) { resp->r_start = lock.l_start; resp->r_length = 1; resp->r_status = STATUS_DENIED; break; } else { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Hop failed"); array_sprintf(badtoken, "%s %ld", str_lock_type(resp->r_lock_type), lock.l_start); break; } } else resp->r_status = STATUS_GRANTED; } if (resp->r_status != STATUS_GRANTED) { lock.l_whence = SEEK_SET; lock.l_type = F_UNLCK; lock.l_start = resp->r_start; lock.l_len = resp->r_length; lock.l_pid = 0; rc = glfs_fd_set_lkowner(fds[resp->r_fpos], (void *)&owner, sizeof(owner)); if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "setting lkowner failed"); array_sprintf(badtoken, "%lld %lld", resp->r_start, resp->r_length); return; } rc = glfs_posix_lock(fds[resp->r_fpos], F_SETLK, &lock); if (rc < 0) rc = -errno; if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Hop Unlock failed"); array_sprintf(badtoken, "%lld %lld", resp->r_start, resp->r_length); } } } void do_unhop(struct response *resp) { int rc; int pos; struct flock lock; uint64_t owner; if (resp->r_fpos != 0 && fds[resp->r_fpos] == NULL) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } switch (lock_mode[resp->r_fpos]) { case LOCK_MODE_POSIX: owner = getpid(); break; case LOCK_MODE_OFD: owner = resp->r_fpos; break; } for (pos = resp->r_start; pos < resp->r_start + resp->r_length; pos++) { if ((pos == 0) || (pos == (resp->r_start + resp->r_length - 1))) lock.l_start = pos; else if ((pos & 1) == 0) lock.l_start = pos - 1; else lock.l_start = pos + 1; lock.l_whence = SEEK_SET; lock.l_type = resp->r_lock_type; lock.l_len = 1; lock.l_pid = 0; rc = glfs_posix_lock(fds[resp->r_fpos], F_SETLK, &lock); if (rc < 0) rc = -errno; if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Unhop failed"); array_sprintf(badtoken, "%s %ld", str_lock_type(resp->r_lock_type), lock.l_start); break; } else resp->r_status = STATUS_GRANTED; } if (resp->r_status != STATUS_GRANTED) { lock.l_whence = SEEK_SET; lock.l_type = F_UNLCK; lock.l_start = resp->r_start; lock.l_len = resp->r_length; lock.l_pid = 0; rc = glfs_fd_set_lkowner(fds[resp->r_fpos], (void *)&owner, sizeof(owner)); if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "setting lkowner failed"); array_sprintf(badtoken, "%lld %lld", resp->r_start, resp->r_length); return; } rc = glfs_posix_lock(fds[resp->r_fpos], F_SETLK, &lock); if (rc < 0) rc = -errno; if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Unhop Unlock failed"); array_sprintf(badtoken, "%lld %lld", resp->r_start, resp->r_length); } } } void do_unlock(struct response *resp) { int rc; struct flock lock; uint64_t owner; if (resp->r_fpos != 0 && fds[resp->r_fpos] == NULL) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } /* If this fpos has a blocking lock, cancel it. */ cancel_work(resp); switch (lock_mode[resp->r_fpos]) { case LOCK_MODE_POSIX: owner = getpid(); break; case LOCK_MODE_OFD: owner = resp->r_fpos; break; } lock.l_whence = SEEK_SET; lock.l_type = resp->r_lock_type; lock.l_start = resp->r_start; lock.l_len = resp->r_length; lock.l_pid = 0; rc = glfs_fd_set_lkowner(fds[resp->r_fpos], (void *)&owner, sizeof(owner)); if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "setting lkowner failed"); array_sprintf(badtoken, "%lld %lld", resp->r_start, resp->r_length); return; } rc = glfs_posix_lock(fds[resp->r_fpos], F_SETLK, &lock); if (rc < 0) rc = -errno; if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Unlock failed"); array_sprintf(badtoken, "%lld %lld", resp->r_start, resp->r_length); return; } resp->r_status = STATUS_GRANTED; } void do_test(struct response *resp) { int rc; struct flock lock; uint64_t owner; if (resp->r_fpos != 0 && fds[resp->r_fpos] == NULL) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } switch (lock_mode[resp->r_fpos]) { case LOCK_MODE_POSIX: owner = getpid(); break; case LOCK_MODE_OFD: owner = resp->r_fpos; break; } lock.l_whence = SEEK_SET; lock.l_type = resp->r_lock_type; lock.l_start = resp->r_start; lock.l_len = resp->r_length; lock.l_pid = 0; fprintf(stdout, "TEST lock type %s\n", str_lock_type(lock.l_type)); rc = glfs_fd_set_lkowner(fds[resp->r_fpos], (void *)&owner, sizeof(owner)); if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "setting lkowner failed"); array_sprintf(badtoken, "%lld %lld", resp->r_start, resp->r_length); return; } rc = glfs_posix_lock(fds[resp->r_fpos], F_GETLK, &lock); if (rc < 0) rc = -errno; if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Test failed"); array_sprintf(badtoken, "%s %lld %lld", str_lock_type(lock.l_type), resp->r_start, resp->r_length); return; } if (lock.l_type == F_UNLCK) { fprintf(stdout, "GRANTED TEST lock type %s\n", str_lock_type(lock.l_type)); resp->r_status = STATUS_GRANTED; } else { resp->r_lock_type = lock.l_type; resp->r_pid = lock.l_pid; resp->r_start = lock.l_start; resp->r_length = lock.l_len; resp->r_status = STATUS_CONFLICT; } } void do_close(struct response *resp) { int rc; if (resp->r_fpos != 0 && fds[resp->r_fpos] == NULL) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } rc = glfs_close(fds[resp->r_fpos]); glfs_h_close(handles[resp->r_fpos]); fds[resp->r_fpos] = NULL; handles[resp->r_fpos] = NULL; if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Close failed"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } resp->r_status = STATUS_OK; } struct test_list { struct test_list *tl_next; long long int tl_start; long long int tl_end; }; struct test_list *tl_head; struct test_list *tl_tail; void remove_test_list_head(void) { struct test_list *item = tl_head; if (item == NULL) fatal("Corruption in test list\n"); tl_head = item->tl_next; if (tl_tail == item) tl_tail = NULL; free(item); } void make_test_item(long long int start, long long int end) { struct test_list *item = malloc(sizeof(*item)); if (item == NULL) fatal("Could not allocate test list item\n"); item->tl_next = NULL; item->tl_start = start; item->tl_end = end; if (tl_head == NULL) tl_head = item; if (tl_tail != NULL) tl_tail->tl_next = item; tl_tail = item; } int list_locks(long long int start, long long int end, struct response *resp) { long long int conf_end; struct flock lock; int rc; lock.l_whence = SEEK_SET; lock.l_type = F_WRLCK; lock.l_start = start; lock.l_pid = 0; if (end == INT64_MAX) lock.l_len = 0; else lock.l_len = end - start; rc = glfs_posix_lock(fds[resp->r_fpos], F_GETLK, &lock); if (rc < 0) rc = -errno; if (rc < 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = -rc; array_strcpy(errdetail, "Test failed"); array_sprintf(badtoken, "%s %lld %lld", str_lock_type(lock.l_type), resp->r_start, resp->r_length); respond(resp); return false; } /* Our test succeeded */ if (lock.l_type == F_UNLCK) return false; resp->r_status = STATUS_CONFLICT; resp->r_lock_type = lock.l_type; resp->r_pid = lock.l_pid; resp->r_start = lock.l_start; resp->r_length = lock.l_len; respond(resp); conf_end = lock_end(resp); if (lock.l_start > start) make_test_item(start, lock.l_start); if (conf_end < end) make_test_item(conf_end, end); return true; } void do_list(struct response *resp) { long long int start = resp->r_start; long long int length = resp->r_length; int conflict = false; if (resp->r_fpos != 0 && fds[resp->r_fpos] == NULL) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } resp->r_lock_type = F_WRLCK; make_test_item(start, lock_end(resp)); while (tl_head != NULL) { conflict |= list_locks(tl_head->tl_start, tl_head->tl_end, resp); remove_test_list_head(); } if (conflict) resp->r_status = STATUS_DENIED; else resp->r_status = STATUS_AVAILABLE; resp->r_lock_type = F_WRLCK; resp->r_start = start; resp->r_length = length; } struct work_item *get_work(enum thread_type thread_type) { struct work_item *work, *poll; while (true) { /* Check poll list first */ poll = glist_first_entry(&poll_queue, struct work_item, queue); work = poll; /* If we didn't find work on the poll list or this is a polling * thread and the work on the poll list isn't due for polling * yet, then look for work on the work list. */ if (work == NULL || (thread_type == THREAD_POLL && work->next_poll > time(NULL))) { /* Check work list now */ work = glist_first_entry(&work_queue, struct work_item, queue); } /* Assign the work to ourself and remove from the queue and * return to the caller. */ if (work != NULL) { work->work_owner = thread_type; work->work_thread = pthread_self(); glist_del(&work->queue); return work; } /* No work, decide what kind of wait to do. */ if (thread_type == THREAD_POLL && poll != NULL) { /* Since there is polling work to do, determine next * time to poll and wait that long for signal. */ struct timespec twait = {poll->next_poll - time(NULL), 0}; pthread_cond_timedwait(&work_cond, &work_mutex, &twait); } else { /* Wait for signal */ pthread_cond_wait(&work_cond, &work_mutex); } } } void *worker(void *t_type) { struct work_item *work = NULL; bool complete, cancelled = false; enum thread_type thread_type = *((enum thread_type *) t_type); pthread_mutex_lock(&work_mutex); while (true) { /* Look for work */ work = get_work(thread_type); pthread_mutex_unlock(&work_mutex); assert(work != NULL); /* Do the work */ switch (work->resp.r_cmd) { case CMD_LOCKW: complete = do_lock(&work->resp, thread_type); break; default: work->resp.r_status = STATUS_ERRNO; work->resp.r_errno = EINVAL; complete = true; break; } if (complete) respond(&work->resp); pthread_mutex_lock(&work_mutex); if (complete) { /* Remember if the main thread was trying to cancel * the work. */ cancelled = work->work_owner == THREAD_CANCEL; /* Indicate this work is complete */ work->work_owner = THREAD_NONE; work->work_thread = 0; if (cancelled) { /* Signal that work has been canceled or * completed. */ pthread_cond_broadcast(&work_cond); } else { /* The work is done, and may be freed, except * that if the work was being cancelled by * cancel_work, then the main thread is waiting * for this thread to be done with the work * item, and thus we can not free it, * cancel_work_item will be responsible to free * the work item. This assures that the request * that caused the cancellation is blocked until * the cancellation has completed. */ free_work(work); } } else { /* This can ONLY happen for a polling thread. * Put this work back in the queue, with a new * next polling time. */ work->work_owner = THREAD_NONE; work->work_thread = 0; work->next_poll = time(NULL) + POLL_DELAY; glist_add_tail(&poll_queue, &work->queue); /* And let worker threads know there may be * more work to do. */ pthread_cond_broadcast(&work_cond); } } } int main(int argc, char **argv) { int opt; int len, rc, i; struct sigaction sigact; char *rest; int oflags = 0; int no_tag; /* Init the lists of work for each fno */ for (i = 0; i <= MAXFPOS; i++) glist_init(fno_work + i); /* Start the worker and polling threads */ for (i = 0; i <= NUM_WORKER; i++) { rc = pthread_create(&threads[i], NULL, worker, i == 0 ? &a_poller : &a_worker); if (rc == -1) { fprintf(stderr, "pthread_create failed %s\n", strerror(errno)); exit(1); } } /* Initialize the signal handling */ memset(&sigact, 0, sizeof(sigact)); sigact.sa_handler = sighandler; if (sigaction(SIGALRM, &sigact, NULL) == -1) return 1; if (sigaction(SIGPIPE, &sigact, NULL) == -1) return 1; if (sigaction(SIGIO, &sigact, NULL) == -1) return 1; input = stdin; output = stdout; /* now parsing options with getopt */ while ((opt = getopt(argc, argv, options)) != EOF) { switch (opt) { case 'c': rc = chdir(optarg); if (rc == -1) { fprintf(stderr, "Can not change dir to %s errno = %d \"%s\"\n", optarg, errno, strerror(errno)); exit(1); } break; case 'q': quiet = true; break; case 'd': duperrors = true; break; case 's': if (oflags > 31) show_usage(1, "Can not combine -x and -s\n"); oflags |= 1; script = true; array_strcpy(server, optarg); break; case 'x': if ((oflags & 31) != 0) show_usage(1, "Can not combine -x and -s/-p/-n/-g/-v\n"); oflags |= 32; script = true; input = fopen(optarg, "r"); if (input == NULL) fatal("Could not open %s\n", optarg); break; case 'n': if (oflags > 31) show_usage(1, "Can not combine -x and -n\n"); oflags |= 2; array_strcpy(name, optarg); break; case 'p': if (oflags > 31) show_usage(1, "Can not combine -x and -p\n"); oflags |= 4; array_strcpy(portstr, optarg); port = atoi(optarg); break; case 'g': if (oflags > 31) show_usage(1, "Can not combine -x and -g\n"); oflags |= 8; array_strcpy(glusterserver, optarg); break; case 'v': if (oflags > 31) show_usage(1, "Can not combine -x and -v\n"); oflags |= 16; array_strcpy(volname, optarg); break; case '?': case 'h': default: /* display the help */ show_usage(0, "Help\n"); } } if (oflags > 0 && oflags < 31) show_usage(1, "Must specify -s, -p, -n, -g and -v together\n"); if (oflags == 31) openserver(); fs = glfs_new(volname); if (!fs) { fatal("Unable to create new glfs. Volume: %s", volname); } rc = glfs_set_volfile_server(fs, "tcp", glusterserver, 24007); if (rc != 0) { fatal("Unable to set volume file. Volume: %s", volname); } rc = glfs_set_logging(fs, "stdout", 7); if (rc != 0) { fatal("Unable to set logging. Volume: %s", volname); } rc = glfs_init(fs); if (rc != 0) { fatal("Unable to initialize volume. Volume: %s", volname); } while (1) { len = readln(input, line, sizeof(line)); if (len < 0) { if (script) fatal("End of file on input\n"); else break; } else { struct response resp; bool complete = true; lno++; memset(&resp, 0, sizeof(resp)); rest = SkipWhite(line, REQUIRES_MORE, "Invalid line"); /* Skip totally blank line */ if (rest == NULL) continue; if (script && !quiet) fprintf(stdout, "%s\n", rest); /* If line doesn't start with a tag, that's ok */ no_tag = (!isdigit(*rest) && (*rest != '$') && (*rest != '-')); /* Parse request into response structure */ rest = parse_request(rest, &resp, no_tag); if (rest == NULL) { resp.r_status = STATUS_ERRNO; resp.r_errno = errno; } else { /* Make sure default status is ok */ resp.r_status = STATUS_OK; if (*rest != '\0' && *rest != '#') fprintf_stderr( "Command line not consumed, rest=\"%s\"\n", rest); switch (resp.r_cmd) { case CMD_OPEN: do_open(&resp); break; case CMD_CLOSE: do_close(&resp); break; case CMD_LOCKW: complete = do_lock(&resp, THREAD_MAIN); break; case CMD_LOCK: complete = do_lock(&resp, THREAD_MAIN); break; case CMD_UNLOCK: do_unlock(&resp); break; case CMD_TEST: do_test(&resp); break; case CMD_LIST: do_list(&resp); break; case CMD_HOP: do_hop(&resp); break; case CMD_UNHOP: do_unhop(&resp); break; case CMD_SEEK: do_seek(&resp); break; case CMD_READ: do_read(&resp); break; case CMD_WRITE: do_write(&resp); break; case CMD_ALARM: do_alarm(&resp); break; case CMD_FORK: complete = do_fork(&resp, oflags == 7); break; case CMD_HELLO: case CMD_COMMENT: case CMD_QUIT: resp.r_status = STATUS_OK; break; case NUM_COMMANDS: fprintf_stderr("Invalid command %s\n", line); continue; } } if (complete) respond(&resp); if (resp.r_cmd == CMD_QUIT) exit(0); } } return 0; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/multilock/ml_posix_client.c���������������������������������������������0000664�0000000�0000000�00000070240�13242724102�0023177�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright IBM Corporation, 2012 * Contributor: Frank Filz <ffilzlnx@mindspring.com> * * * This software is a server that implements the NFS protocol. * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * */ #include <pthread.h> #include <assert.h> #include "multilock.h" #include "../../include/gsh_list.h" /* command line syntax */ char options[] = "c:qdx:s:n:p:h?"; char usage[] = "Usage: ml_posix_client -s server -p port -n name [-q] [-d] [-c path]\n" " ml_posix_client -x script [-q] [-d] [-c path]\n" " ml_posix_client [-q] [-d] [-c path]\n" "\n" " ml_posix_client may be run in three modes\n" " - In the first mode, the client will be driven by a master.\n" " - In the second mode, the client is driven by a script.\n" " - In the third mode, the client interractive.\n" "\n" " -s server - specify the master's hostname or IP address\n" " -p port - specify the master's port number\n" " -n name - specify the client's name\n" " -x script - specify the name of a script to execute\n" " -q - specify quiet mode\n" " -d - specify dup errors mode (errors are sent to stdout and stderr)\n" " -c path - chdir\n"; #define NUM_WORKER 4 #define POLL_DELAY 10 enum thread_type { THREAD_NONE, THREAD_MAIN, THREAD_WORKER, THREAD_POLL, THREAD_CANCEL }; struct work_item { struct glist_head queue; struct glist_head fno_work; struct response resp; enum thread_type work_owner; pthread_t work_thread; time_t next_poll; }; char server[MAXSTR]; char name[MAXSTR]; char portstr[MAXSTR]; int port; char line[MAXXFER]; long int alarmtag; int fno[MAXFPOS + 1]; enum lock_mode lock_mode[MAXFPOS + 1]; /* Global variables to manage work list */ struct glist_head fno_work[MAXFPOS + 1]; struct glist_head work_queue = GLIST_HEAD_INIT(work_queue); struct glist_head poll_queue = GLIST_HEAD_INIT(poll_queue); pthread_t threads[NUM_WORKER + 1]; pthread_mutex_t work_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t work_cond = PTHREAD_COND_INITIALIZER; enum thread_type a_worker = THREAD_WORKER; enum thread_type a_poller = THREAD_POLL; void openserver(void) { struct addrinfo *addr; int rc; struct addrinfo hint; int sock; struct response resp; if (!quiet) fprintf(stdout, "server=%s port=%d name=%s\n", server, port, name); memset(&hint, 0, sizeof(hint)); hint.ai_family = AF_INET; hint.ai_socktype = SOCK_STREAM; rc = getaddrinfo(server, portstr, &hint, &addr); if (rc != 0) fatal("getaddrinfo error %d \"%s\"\n", rc, gai_strerror(rc)); rc = socket(addr[0].ai_family, SOCK_STREAM, 0); if (rc == -1) fatal("socket failed with ERRNO %d \"%s\"\n", errno, strerror(errno)); sock = rc; rc = connect(sock, addr[0].ai_addr, addr[0].ai_addrlen); if (rc == -1) fatal("connect failed with ERRNO %d \"%s\"\n", errno, strerror(errno)); input = fdopen(sock, "r"); if (input == NULL) fatal( "Could not create input stream from socket ERROr %d \"%s\"\n", errno, strerror(errno)); output = fdopen(sock, "w"); if (output == NULL) fatal( "Could not create output stream from socket ERROr %d \"%s\"\n", errno, strerror(errno)); if (!quiet) fprintf(stdout, "connected to server %s:%d\n", server, port); resp.r_cmd = CMD_HELLO; resp.r_status = STATUS_OK; resp.r_tag = 0; array_strcpy(resp.r_data, name); respond(&resp); } bool do_fork(struct response *resp, bool use_server) { pid_t forked; if (!use_server) { fprintf_stderr("FORK may only be used in server mode\n"); return false; } forked = fork(); if (forked < 0) { /* Error */ resp->r_status = STATUS_OK; resp->r_errno = errno; if (!quiet) fprintf(stdout, "fork failed %d (%s)\n", (int) resp->r_errno, strerror(resp->r_errno)); return true; } else if (forked == 0) { /* Parent sends a FORK response */ array_strcpy(resp->r_data, name); resp->r_status = STATUS_OK; if (!quiet) fprintf(stdout, "fork succeeded\n"); return true; } if (!quiet) fprintf(stdout, "forked\n"); /* This is the forked process */ /* First close the old output. */ fclose(output); /* Setup the new client name. */ array_strcpy(name, resp->r_data); /* Then open a new connection to the server. */ openserver(); /* Response already sent. */ return false; } void command(void) { } void sighandler(int sig) { struct response resp; switch (sig) { case SIGALRM: resp.r_cmd = CMD_ALARM; resp.r_tag = alarmtag; resp.r_status = STATUS_COMPLETED; alarmtag = 0; respond(&resp); break; case SIGPIPE: case SIGIO: break; } } void do_alarm(struct response *resp) { unsigned int remain; remain = alarm(resp->r_secs); if (remain != 0) { struct response resp2; resp2.r_cmd = CMD_ALARM; resp2.r_tag = alarmtag; resp2.r_secs = remain; resp2.r_status = STATUS_CANCELED; respond(&resp2); } if (resp->r_secs != 0) alarmtag = resp->r_tag; else alarmtag = 0; resp->r_status = STATUS_OK; } void do_open(struct response *resp) { int fd, rc; struct flock lock; if (fno[resp->r_fpos] != 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = EINVAL; array_strcpy(errdetail, "fpos in use"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } if ((resp->r_flags & O_CREAT) == 0) fd = open(resp->r_data, resp->r_flags); else fd = open(resp->r_data, resp->r_flags, resp->r_mode); if (fd == -1) { resp->r_status = STATUS_ERRNO; resp->r_errno = errno; array_strcpy(badtoken, resp->r_data); return; } /* Test lock mode */ switch ((enum lock_mode) resp->r_lock_type) { case LOCK_MODE_POSIX: break; case LOCK_MODE_OFD: lock.l_whence = SEEK_SET; lock.l_type = F_RDLCK; lock.l_start = 0; lock.l_len = 0; lock.l_pid = 0; rc = fcntl(fd, F_OFD_GETLK, &lock); if (rc == -1) { resp->r_status = STATUS_ERRNO; resp->r_errno = errno; array_strcpy(errdetail, "Open verify OFD locks failed"); array_strcpy(badtoken, resp->r_data); close(fd); return; } break; } fno[resp->r_fpos] = fd; lock_mode[resp->r_fpos] = (enum lock_mode) resp->r_lock_type; resp->r_fno = fd; resp->r_status = STATUS_OK; } void do_write(struct response *resp) { long long int rc; if (resp->r_fpos != 0 && fno[resp->r_fpos] == 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } rc = write(fno[resp->r_fpos], resp->r_data, resp->r_length); if (rc == -1) { resp->r_status = STATUS_ERRNO; resp->r_errno = errno; array_strcpy(errdetail, "Write failed"); array_sprintf(badtoken, "%lld", resp->r_length); return; } if (rc != resp->r_length) { resp->r_status = STATUS_ERRNO; resp->r_errno = EIO; array_strcpy(errdetail, "Short write"); array_sprintf(badtoken, "%lld", rc); return; } resp->r_status = STATUS_OK; } void do_read(struct response *resp) { long long int rc; if (resp->r_fpos != 0 && fno[resp->r_fpos] == 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } if (resp->r_length > MAXSTR) resp->r_length = MAXSTR; rc = read(fno[resp->r_fpos], resp->r_data, resp->r_length); if (rc == -1) { resp->r_status = STATUS_ERRNO; resp->r_errno = errno; array_strcpy(errdetail, "Read failed"); array_sprintf(badtoken, "%lld", resp->r_length); return; } resp->r_data[rc] = '\0'; resp->r_length = strlen(resp->r_data); resp->r_status = STATUS_OK; } void do_seek(struct response *resp) { int rc; if (resp->r_fpos != 0 && fno[resp->r_fpos] == 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } rc = lseek(fno[resp->r_fpos], resp->r_start, SEEK_SET); if (rc == -1) { resp->r_status = STATUS_ERRNO; resp->r_errno = errno; array_strcpy(errdetail, "Seek failed"); array_sprintf(badtoken, "%lld", resp->r_start); return; } resp->r_status = STATUS_OK; } void free_work(struct work_item *work) { /* Make sure the item isn't on any queues */ glist_del(&work->fno_work); glist_del(&work->queue); free(work); } void cancel_work_item(struct work_item *work) { switch (work->work_owner) { case THREAD_NONE: case THREAD_MAIN: case THREAD_CANCEL: /* nothing special to do */ break; case THREAD_WORKER: case THREAD_POLL: /* Mark the work item to be canceled */ work->work_owner = THREAD_CANCEL; pthread_kill(work->work_thread, SIGIO); /* Wait for thread to be done or cancel the work */ while (work->work_owner != THREAD_NONE) pthread_cond_wait(&work_cond, &work_mutex); } /* Done with the work item, free the memory. */ free_work(work); } static inline long long int lock_end(struct response *req) { if (req->r_length == 0) return LLONG_MAX; return req->r_start + req->r_length; } void cancel_work(struct response *req) { struct glist_head cancel_work = GLIST_HEAD_INIT(cancel_work); struct glist_head *glist; struct work_item *work; bool start_over = true; pthread_mutex_lock(&work_mutex); while (start_over) { start_over = false; glist_for_each(glist, fno_work + req->r_fpos) { work = glist_entry(glist, struct work_item, fno_work); if (work->resp.r_start >= req->r_start && lock_end(&work->resp) <= lock_end(req)) { /* Do something */ cancel_work_item(work); /* List may be messed up */ start_over = true; break; } } } pthread_mutex_unlock(&work_mutex); } /* Must only be called from main thread...*/ int schedule_work(struct response *resp) { struct work_item *work = calloc(1, sizeof(*work)); if (work == NULL) { errno = ENOMEM; return -1; } pthread_mutex_lock(&work_mutex); work->resp = *resp; work->work_owner = THREAD_NONE; glist_add_tail(&work_queue, &work->queue); glist_add_tail(fno_work + resp->r_fpos, &work->fno_work); /* Signal to the worker and polling threads there is new work */ pthread_cond_broadcast(&work_cond); pthread_mutex_unlock(&work_mutex); return 0; } bool do_lock(struct response *resp, enum thread_type thread_type) { int rc; struct flock lock; int cmd = -1; bool retry = false; if (resp->r_fpos != 0 && fno[resp->r_fpos] == 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return true; } switch (lock_mode[resp->r_fpos]) { case LOCK_MODE_POSIX: if (resp->r_cmd == CMD_LOCKW) { if (thread_type != THREAD_WORKER) { cmd = F_SETLK; retry = true; } else { cmd = F_SETLKW; } } else { cmd = F_SETLK; } break; case LOCK_MODE_OFD: if (resp->r_cmd == CMD_LOCKW) { if (thread_type != THREAD_WORKER) { cmd = F_OFD_SETLK; retry = true; } else { cmd = F_OFD_SETLKW; } } else { cmd = F_OFD_SETLK; } break; } lock.l_whence = SEEK_SET; lock.l_type = resp->r_lock_type; lock.l_start = resp->r_start; lock.l_len = resp->r_length; lock.l_pid = 0; rc = fcntl(fno[resp->r_fpos], cmd, &lock); if (rc == -1 && errno == EAGAIN && retry && thread_type == THREAD_MAIN) { /* We need to schedule OFD blocked lock */ rc = schedule_work(resp); /* Check for scheduling success */ if (rc == 0) return false; } if (rc == -1) { if (errno == EAGAIN) { if (retry) { /* Let caller know we didn't complete */ return false; } resp->r_status = STATUS_DENIED; } else if (errno == EINTR) { resp->r_status = STATUS_CANCELED; } else if (errno == EDEADLK) { resp->r_status = STATUS_DEADLOCK; } else { resp->r_status = STATUS_ERRNO; resp->r_errno = errno; array_strcpy(errdetail, "Lock failed"); array_sprintf(badtoken, "%s %lld %lld", str_lock_type(lock.l_type), resp->r_start, resp->r_length); } } else resp->r_status = STATUS_GRANTED; return true; } void do_hop(struct response *resp) { int rc; int pos; struct flock lock; int cmd = -1; if (resp->r_fpos != 0 && fno[resp->r_fpos] == 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } switch (lock_mode[resp->r_fpos]) { case LOCK_MODE_POSIX: cmd = F_SETLK; break; case LOCK_MODE_OFD: cmd = F_OFD_SETLK; break; } for (pos = resp->r_start; pos < resp->r_start + resp->r_length; pos++) { if ((pos == 0) || (pos == (resp->r_start + resp->r_length - 1))) lock.l_start = pos; else if ((pos & 1) == 0) lock.l_start = pos - 1; else lock.l_start = pos + 1; lock.l_whence = SEEK_SET; lock.l_type = resp->r_lock_type; lock.l_len = 1; lock.l_pid = 0; rc = fcntl(fno[resp->r_fpos], cmd, &lock); if (rc == -1) { if (errno == EAGAIN) { resp->r_start = lock.l_start; resp->r_length = 1; resp->r_status = STATUS_DENIED; break; } else { resp->r_status = STATUS_ERRNO; resp->r_errno = errno; array_strcpy(errdetail, "Hop failed"); array_sprintf(badtoken, "%s %ld", str_lock_type(resp->r_lock_type), lock.l_start); break; } } else resp->r_status = STATUS_GRANTED; } if (resp->r_status != STATUS_GRANTED) { lock.l_whence = SEEK_SET; lock.l_type = F_UNLCK; lock.l_start = resp->r_start; lock.l_len = resp->r_length; lock.l_pid = 0; rc = fcntl(fno[resp->r_fpos], cmd, &lock); if (rc == -1) { resp->r_status = STATUS_ERRNO; resp->r_errno = errno; array_strcpy(errdetail, "Hop Unlock failed"); array_sprintf(badtoken, "%lld %lld", resp->r_start, resp->r_length); } } } void do_unhop(struct response *resp) { int rc; int pos; struct flock lock; int cmd = -1; if (resp->r_fpos != 0 && fno[resp->r_fpos] == 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } switch (lock_mode[resp->r_fpos]) { case LOCK_MODE_POSIX: cmd = F_SETLK; break; case LOCK_MODE_OFD: cmd = F_OFD_SETLK; break; } for (pos = resp->r_start; pos < resp->r_start + resp->r_length; pos++) { if ((pos == 0) || (pos == (resp->r_start + resp->r_length - 1))) lock.l_start = pos; else if ((pos & 1) == 0) lock.l_start = pos - 1; else lock.l_start = pos + 1; lock.l_whence = SEEK_SET; lock.l_type = resp->r_lock_type; lock.l_len = 1; lock.l_pid = 0; rc = fcntl(fno[resp->r_fpos], cmd, &lock); if (rc == -1) { resp->r_status = STATUS_ERRNO; resp->r_errno = errno; array_strcpy(errdetail, "Unhop failed"); array_sprintf(badtoken, "%s %ld", str_lock_type(resp->r_lock_type), lock.l_start); break; } else resp->r_status = STATUS_GRANTED; } if (resp->r_status != STATUS_GRANTED) { lock.l_whence = SEEK_SET; lock.l_type = F_UNLCK; lock.l_start = resp->r_start; lock.l_len = resp->r_length; lock.l_pid = 0; rc = fcntl(fno[resp->r_fpos], cmd, &lock); if (rc == -1) { resp->r_status = STATUS_ERRNO; resp->r_errno = errno; array_strcpy(errdetail, "Unhop Unlock failed"); array_sprintf(badtoken, "%lld %lld", resp->r_start, resp->r_length); } } } void do_unlock(struct response *resp) { int rc; struct flock lock; int cmd = -1; if (resp->r_fpos != 0 && fno[resp->r_fpos] == 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } /* If this fpos has a blocking lock, cancel it. */ cancel_work(resp); switch (lock_mode[resp->r_fpos]) { case LOCK_MODE_POSIX: cmd = F_SETLK; break; case LOCK_MODE_OFD: cmd = F_OFD_SETLK; break; } lock.l_whence = SEEK_SET; lock.l_type = resp->r_lock_type; lock.l_start = resp->r_start; lock.l_len = resp->r_length; lock.l_pid = 0; rc = fcntl(fno[resp->r_fpos], cmd, &lock); if (rc == -1) { resp->r_status = STATUS_ERRNO; resp->r_errno = errno; array_strcpy(errdetail, "Unlock failed"); array_sprintf(badtoken, "%lld %lld", resp->r_start, resp->r_length); return; } resp->r_status = STATUS_GRANTED; } void do_test(struct response *resp) { int rc; struct flock lock; int cmd = -1; if (resp->r_fpos != 0 && fno[resp->r_fpos] == 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } switch (lock_mode[resp->r_fpos]) { case LOCK_MODE_POSIX: cmd = F_GETLK; break; case LOCK_MODE_OFD: cmd = F_OFD_GETLK; break; } lock.l_whence = SEEK_SET; lock.l_type = resp->r_lock_type; lock.l_start = resp->r_start; lock.l_len = resp->r_length; lock.l_pid = 0; fprintf(stdout, "TEST lock type %s\n", str_lock_type(lock.l_type)); rc = fcntl(fno[resp->r_fpos], cmd, &lock); if (rc == -1) { resp->r_status = STATUS_ERRNO; resp->r_errno = errno; array_strcpy(errdetail, "Test failed"); array_sprintf(badtoken, "%s %lld %lld", str_lock_type(lock.l_type), resp->r_start, resp->r_length); return; } if (lock.l_type == F_UNLCK) { fprintf(stdout, "GRANTED TEST lock type %s\n", str_lock_type(lock.l_type)); resp->r_status = STATUS_GRANTED; } else { resp->r_lock_type = lock.l_type; resp->r_pid = lock.l_pid; resp->r_start = lock.l_start; resp->r_length = lock.l_len; resp->r_status = STATUS_CONFLICT; } } void do_close(struct response *resp) { int rc; if (resp->r_fpos != 0 && fno[resp->r_fpos] == 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } rc = close(fno[resp->r_fpos]); fno[resp->r_fpos] = 0; if (rc == -1) { resp->r_status = STATUS_ERRNO; resp->r_errno = errno; array_strcpy(errdetail, "Close failed"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } resp->r_status = STATUS_OK; } struct test_list { struct test_list *tl_next; long long int tl_start; long long int tl_end; }; struct test_list *tl_head; struct test_list *tl_tail; void remove_test_list_head(void) { struct test_list *item = tl_head; if (item == NULL) fatal("Corruption in test list\n"); tl_head = item->tl_next; if (tl_tail == item) tl_tail = NULL; free(item); } void make_test_item(long long int start, long long int end) { struct test_list *item = malloc(sizeof(*item)); if (item == NULL) fatal("Could not allocate test list item\n"); item->tl_next = NULL; item->tl_start = start; item->tl_end = end; if (tl_head == NULL) tl_head = item; if (tl_tail != NULL) tl_tail->tl_next = item; tl_tail = item; } int list_locks(long long int start, long long int end, struct response *resp) { long long int conf_end; struct flock lock; int rc; lock.l_whence = SEEK_SET; lock.l_type = F_WRLCK; lock.l_start = start; lock.l_pid = 0; if (end == INT64_MAX) lock.l_len = 0; else lock.l_len = end - start; rc = fcntl(fno[resp->r_fpos], F_GETLK, &lock); if (rc == -1) { resp->r_status = STATUS_ERRNO; resp->r_errno = errno; array_strcpy(errdetail, "Test failed"); array_sprintf(badtoken, "%s %lld %lld", str_lock_type(lock.l_type), resp->r_start, resp->r_length); respond(resp); return false; } /* Our test succeeded */ if (lock.l_type == F_UNLCK) return false; resp->r_status = STATUS_CONFLICT; resp->r_lock_type = lock.l_type; resp->r_pid = lock.l_pid; resp->r_start = lock.l_start; resp->r_length = lock.l_len; respond(resp); conf_end = lock_end(resp); if (lock.l_start > start) make_test_item(start, lock.l_start); if (conf_end < end) make_test_item(conf_end, end); return true; } void do_list(struct response *resp) { long long int start = resp->r_start; long long int length = resp->r_length; int conflict = false; if (resp->r_fpos != 0 && fno[resp->r_fpos] == 0) { resp->r_status = STATUS_ERRNO; resp->r_errno = EBADF; array_strcpy(errdetail, "Invalid file number"); array_sprintf(badtoken, "%ld", resp->r_fpos); return; } resp->r_lock_type = F_WRLCK; make_test_item(start, lock_end(resp)); while (tl_head != NULL) { conflict |= list_locks(tl_head->tl_start, tl_head->tl_end, resp); remove_test_list_head(); } if (conflict) resp->r_status = STATUS_DENIED; else resp->r_status = STATUS_AVAILABLE; resp->r_lock_type = F_WRLCK; resp->r_start = start; resp->r_length = length; } struct work_item *get_work(enum thread_type thread_type) { struct work_item *work, *poll; while (true) { /* Check poll list first */ poll = glist_first_entry(&poll_queue, struct work_item, queue); work = poll; /* If we didn't find work on the poll list or this is a polling * thread and the work on the poll list isn't due for polling * yet, then look for work on the work list. */ if (work == NULL || (thread_type == THREAD_POLL && work->next_poll > time(NULL))) { /* Check work list now */ work = glist_first_entry(&work_queue, struct work_item, queue); } /* Assign the work to ourself and remove from the queue and * return to the caller. */ if (work != NULL) { work->work_owner = thread_type; work->work_thread = pthread_self(); glist_del(&work->queue); return work; } /* No work, decide what kind of wait to do. */ if (thread_type == THREAD_POLL && poll != NULL) { /* Since there is polling work to do, determine next * time to poll and wait that long for signal. */ struct timespec twait = {poll->next_poll - time(NULL), 0}; pthread_cond_timedwait(&work_cond, &work_mutex, &twait); } else { /* Wait for signal */ pthread_cond_wait(&work_cond, &work_mutex); } } } void *worker(void *t_type) { struct work_item *work = NULL; bool complete, cancelled = false; enum thread_type thread_type = *((enum thread_type *) t_type); pthread_mutex_lock(&work_mutex); while (true) { /* Look for work */ work = get_work(thread_type); pthread_mutex_unlock(&work_mutex); assert(work != NULL); /* Do the work */ switch (work->resp.r_cmd) { case CMD_LOCKW: complete = do_lock(&work->resp, thread_type); break; default: work->resp.r_status = STATUS_ERRNO; work->resp.r_errno = EINVAL; complete = true; break; } if (complete) respond(&work->resp); pthread_mutex_lock(&work_mutex); if (complete) { /* Remember if the main thread was trying to cancel * the work. */ cancelled = work->work_owner == THREAD_CANCEL; /* Indicate this work is complete */ work->work_owner = THREAD_NONE; work->work_thread = 0; if (cancelled) { /* Signal that work has been canceled or * completed. */ pthread_cond_broadcast(&work_cond); } else { /* The work is done, and may be freed, except * that if the work was beeing cancelled by * cancel_work, then the main thread is waiting * for this thread to be done with the work * item, and thus we can not free it, * cancel_work_item will be responsible to free * the work item. This assures that the request * that caused the cancellation is blocked until * the cancellation has completed. */ free_work(work); } } else { /* This can ONLY happen for a polling thread. * Put this work back in the queue, with a new * next polling time. */ work->work_owner = THREAD_NONE; work->work_thread = 0; work->next_poll = time(NULL) + POLL_DELAY; glist_add_tail(&poll_queue, &work->queue); /* And let worker threads know there may be * more work to do. */ pthread_cond_broadcast(&work_cond); } } } int main(int argc, char **argv) { int opt; int len, rc, i; struct sigaction sigact; char *rest; int oflags = 0; int no_tag; /* Init the lists of work for each fno */ for (i = 0; i <= MAXFPOS; i++) glist_init(fno_work + i); /* Start the worker and polling threads */ for (i = 0; i <= NUM_WORKER; i++) { rc = pthread_create(&threads[i], NULL, worker, i == 0 ? &a_poller : &a_worker); if (rc == -1) { fprintf(stderr, "pthread_create failed %s\n", strerror(errno)); exit(1); } } /* Initialize the signal handling */ memset(&sigact, 0, sizeof(sigact)); sigact.sa_handler = sighandler; if (sigaction(SIGALRM, &sigact, NULL) == -1) return 1; if (sigaction(SIGPIPE, &sigact, NULL) == -1) return 1; if (sigaction(SIGIO, &sigact, NULL) == -1) return 1; input = stdin; output = stdout; /* now parsing options with getopt */ while ((opt = getopt(argc, argv, options)) != EOF) { switch (opt) { case 'c': rc = chdir(optarg); if (rc == -1) { fprintf(stderr, "Can not change dir to %s errno = %d \"%s\"\n", optarg, errno, strerror(errno)); exit(1); } break; case 'q': quiet = true; break; case 'd': duperrors = true; break; case 's': if (oflags > 7) show_usage(1, "Can not combine -x and -s\n"); oflags |= 1; script = true; array_strcpy(server, optarg); break; case 'x': if ((oflags & 7) != 0) show_usage(1, "Can not combine -x and -s/-p/-n\n"); oflags |= 8; script = true; input = fopen(optarg, "r"); if (input == NULL) fatal("Could not open %s\n", optarg); break; case 'n': if (oflags > 7) show_usage(1, "Can not combine -x and -n\n"); oflags |= 2; array_strcpy(name, optarg); break; case 'p': if (oflags > 7) show_usage(1, "Can not combine -x and -p\n"); oflags |= 4; array_strcpy(portstr, optarg); port = atoi(optarg); break; case '?': case 'h': default: /* display the help */ show_usage(0, "Help\n"); } } if (oflags > 0 && oflags < 7) show_usage(1, "Must specify -s, -p, and -n together\n"); if (oflags == 7) openserver(); while (1) { len = readln(input, line, sizeof(line)); if (len < 0) { if (script) fatal("End of file on input\n"); else break; } else { struct response resp; bool complete = true; lno++; memset(&resp, 0, sizeof(resp)); rest = SkipWhite(line, REQUIRES_MORE, "Invalid line"); /* Skip totally blank line */ if (rest == NULL) continue; if (script && !quiet) fprintf(stdout, "%s\n", rest); /* If line doesn't start with a tag, that's ok */ no_tag = (!isdigit(*rest) && (*rest != '$') && (*rest != '-')); /* Parse request into response structure */ rest = parse_request(rest, &resp, no_tag); if (rest == NULL) { resp.r_status = STATUS_ERRNO; resp.r_errno = errno; } else { /* Make sure default status is ok */ resp.r_status = STATUS_OK; if (*rest != '\0' && *rest != '#') fprintf_stderr( "Command line not consumed, rest=\"%s\"\n", rest); switch (resp.r_cmd) { case CMD_OPEN: do_open(&resp); break; case CMD_CLOSE: do_close(&resp); break; case CMD_LOCKW: complete = do_lock(&resp, THREAD_MAIN); break; case CMD_LOCK: complete = do_lock(&resp, THREAD_MAIN); break; case CMD_UNLOCK: do_unlock(&resp); break; case CMD_TEST: do_test(&resp); break; case CMD_LIST: do_list(&resp); break; case CMD_HOP: do_hop(&resp); break; case CMD_UNHOP: do_unhop(&resp); break; case CMD_SEEK: do_seek(&resp); break; case CMD_READ: do_read(&resp); break; case CMD_WRITE: do_write(&resp); break; case CMD_ALARM: do_alarm(&resp); break; case CMD_FORK: complete = do_fork(&resp, oflags == 7); break; case CMD_HELLO: case CMD_COMMENT: case CMD_QUIT: resp.r_status = STATUS_OK; break; case NUM_COMMANDS: fprintf_stderr("Invalid command %s\n", line); continue; } } if (complete) respond(&resp); if (resp.r_cmd == CMD_QUIT) exit(0); } } return 0; } ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/multilock/multilock.h���������������������������������������������������0000664�0000000�0000000�00000021377�13242724102�0022026�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* * Copyright IBM Corporation, 2012 * Contributor: Frank Filz <ffilzlnx@mindspring.com> * * * This software is a server that implements the NFS protocol. * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * */ #ifndef _MULTILOCK_H #define _MULTILOCK_H #include <stdio.h> #include <stdlib.h> #include <limits.h> #include <unistd.h> #include <string.h> #include <strings.h> #include <errno.h> #include <ctype.h> #include <signal.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/socket.h> #include <sys/select.h> #include <netinet/in.h> #include <netdb.h> #include <time.h> #include <stdbool.h> #include <sys/param.h> #define MAXSTR 1024 #define MAXDATA MAX(PATH_MAX, MAXSTR + 1) /* Define the maximum request/response */ #define MAXXFER (MAXDATA + MAXSTR * 3) #define MAXFPOS 16 /* If not otherwise defined, define OFD locks */ #ifndef F_OFD_GETLK #define F_OFD_GETLK 36 #endif #ifndef F_OFD_SETLK #define F_OFD_SETLK 37 #endif #ifndef F_OFD_SETLKW #define F_OFD_SETLKW 38 #endif enum lock_mode { LOCK_MODE_POSIX = 0, LOCK_MODE_OFD = 1, }; int readln(FILE *in, char *buf, int buflen); struct response; struct command_def; struct client; struct token { const char *t_name; int t_len; int t_value; }; /* Commands to Client */ /* Responses use the same strings */ enum commands { CMD_OPEN, CMD_CLOSE, CMD_LOCKW, CMD_LOCK, CMD_UNLOCK, CMD_TEST, CMD_LIST, CMD_HOP, CMD_UNHOP, CMD_SEEK, CMD_READ, CMD_WRITE, CMD_COMMENT, CMD_ALARM, CMD_HELLO, CMD_FORK, CMD_QUIT, NUM_COMMANDS }; enum requires_more { REQUIRES_MORE, REQUIRES_NO_MORE, REQUIRES_EITHER, }; struct command_def { const char *cmd_name; int cmd_len; }; struct client { struct client *c_next; struct client *c_prev; int c_socket; struct sockaddr c_addr; char c_name[MAXSTR+1]; FILE *c_input; FILE *c_output; int c_refcount; }; enum status { STATUS_OK, STATUS_AVAILABLE, STATUS_GRANTED, STATUS_DENIED, STATUS_DEADLOCK, STATUS_CONFLICT, STATUS_CANCELED, STATUS_COMPLETED, STATUS_ERRNO, STATUS_PARSE_ERROR, STATUS_ERROR /* must be last */ }; extern char errdetail[MAXSTR * 2 + 1]; extern char badtoken[MAXSTR + 1]; extern struct client *client_list; extern FILE *input; extern FILE *output; extern bool script; extern bool quiet; extern bool duperrors; extern bool strict; extern bool error_is_fatal; extern bool syntax; extern long int lno; extern long int global_tag; struct response; long int get_global_tag(bool increment); #define array_strcpy(dest, src) \ do { \ strncpy(dest, src, sizeof(dest) - 1); \ dest[sizeof(dest) - 1] = '\0'; \ } while (0) #define array_strncpy(dest, src, len) \ do { \ if (len >= sizeof(dest)) \ len = sizeof(dest) - 1; \ \ memcpy(dest, src, len); \ dest[len] = '\0'; \ } while (0) #define array_sprintf(buf, fmt, args...) \ snprintf(buf, sizeof(buf) - 1, fmt, ## args) \ #define sprint_left(buf, left, fmt, args...) \ do { \ int lx = snprintf(buf, left, fmt, ## args); \ buf += lx; \ left -= lx; \ } while (0) #define fprintf_stderr(fmt, args...) \ do { \ if (duperrors && output != NULL) \ fprintf(output, fmt, ## args); \ fprintf(stderr, fmt, ## args); \ } while (0) #define fatal(str, args...) \ do { \ fprintf_stderr(str, ## args); \ fprintf_stderr("FAIL\n"); \ if (output != NULL) \ fflush(output); \ fflush(stderr); \ exit(1); \ } while (0) #define show_usage(ret, fmt, args...) \ do { \ fprintf_stderr(fmt, ## args); \ fprintf_stderr("%s", usage); \ fflush(stderr); \ fflush(stdout); \ exit(ret); \ } while (0) char *get_command(char *line, enum commands *cmd); char *get_tag(char *line, struct response *resp, int required, enum requires_more requires_more); char *get_rq_tag(char *line, struct response *req, int required, enum requires_more requires_more); char *get_long(char *line, long int *value, enum requires_more requires_more, const char *invalid); char *get_longlong(char *line, long long int *value, enum requires_more requires_more, const char *invalid); char *get_fpos(char *line, long int *fpos, enum requires_more requires_more); char *get_rdata(char *line, struct response *resp, int max, enum requires_more requires_more); char *get_lock_type(char *line, int *type); char *get_client(char *line, struct client **pclient, bool create, enum requires_more requires_more); char *get_token(char *line, char **token, int *len, bool optional, enum requires_more requires_more, const char *invalid); char *get_token_value(char *line, int *value, struct token *tokens, bool optional, enum requires_more requires_more, const char *invalid); char *get_status(char *line, struct response *resp); char *get_open_opts(char *line, long int *fpos, int *flags, int *mode, int *lock_mode); char *parse_response(char *line, struct response *resp); char *parse_request(char *line, struct response *req, int no_tag); char *get_on_off(char *line, bool *value); char *SkipWhite(char *line, enum requires_more requires_more, const char *who); void respond(struct response *resp); const char *str_lock_type(int type); void sprintf_resp(char *line, int size, const char *lead, struct response *resp); void sprintf_req(char *line, int size, const char *lead, struct response *req); void send_cmd(struct response *req); const char *str_status(enum status status); const char *str_read_write_flags(int flags); enum status parse_status(char *str, int len); void free_response(struct response *resp, struct response **list); void free_client(struct client *client); int compare_responses(struct response *expected, struct response *received); void add_response(struct response *resp, struct response **list); struct response *check_expected_responses(struct response *expected_responses, struct response *client_resp); struct response { struct response *r_next; struct response *r_prev; struct client *r_client; enum commands r_cmd; enum status r_status; long int r_tag; long int r_fpos; long int r_fno; long int r_secs; long long int r_start; long long int r_length; long int r_pid; int r_lock_type; int r_flags; int r_mode; long int r_errno; /** * @brief complex data for a request/response * * OPEN - name of the file to open * READ - data read (response) * WRITE - date to write (request) * COMMENT - the string * HELLO - name of the client * FORK - name of the client */ char r_data[MAXDATA]; char r_original[MAXXFER]; }; extern struct command_def commands[NUM_COMMANDS + 1]; /* Command forms * * General format * tag cmd [options] - tag is numeric sequence * tag OPEN fpos {ro|wo|rw} [create] filename * tag CLOSE fpos * tag LOCK fpos type start length - type is READ or WRITE * tag LOCKW fpos type start length * tag UNLOCK fpos start length * tag TEST fpos type start length * tag LIST fpos start length * tag SEEK fpos start * tag READ fpos length * tag WRITE fpos "string" * tag COMMENT "string" * tag ALARM seconds * tag HELLO "name" (command ignored, really just a response to server) * tag QUIT (tag is optional, if not present, tag = -1) */ /* Response forms * * tag cmd ERRNO value "string" - for all commands, result was an error * tag OPEN OK fpos fd * tag CLOSE OK fpos * tag LOCK GRANTED fpos type start length * tag LOCK DENIED fpos type start length * tag LOCKW GRANTED fpos type start length * tag LOCKW CANCELED fpos type start length * tag UNLOCK GRANTED fpos type start length * tag TEST GRANTED fpos type start length * tag TEST CONFLICT fpos pid type start length * tag LIST GRANTED fpos start length (returned if no locks to list) * tag LIST DENIED fpos start length (returned if list had locks) * tag LIST CONFLICT fpos pid type start length (returned for each lock in * list) * tag SEEK OK fpos * tag READ OK fpos len "data" * tag WRITE OK fpos len * tag COMMENT OK "string" * tag ALARM OK seconds * tag ALARM CANCELED remain * tag ALARM COMPLETED * 0 HELLO OK "name" * tag QUIT OK */ #endif /* _MULTILOCK_H */ �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/multilock/sample_tests/�������������������������������������������������0000775�0000000�0000000�00000000000�13242724102�0022343�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/multilock/sample_tests/deadlock�����������������������������������������0000664�0000000�0000000�00000006365�13242724102�0024046�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright IBM Corporation, 2012 # Contributor: Frank Filz <ffilz@us.ibm.com> # # # This software is a server that implements the NFS protocol. # # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 3 of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # need three clients for three way deadlock, but it doesn't work, so only # two clients #CLIENTS c1 c2 c3 CLIENTS c1 c2 OK c1 OPEN 1 rw create deadlock.file OK c2 OPEN 1 rw deadlock.file #OK c3 OPEN 1 rw deadlock.file #=============================================================================== # test 8 Test deadlock condition: owner-1 acquires read lock on 0-7, owner-2 # acquires read lock on 8-15, owner-1 requests write lock on 8-15 # (blocks), owner-2 requests write lock on 0-7 (deadlock (test case # should see EDEADLK return from lock call)) #=============================================================================== # The above description isn't quite right GRANTED c1 LOCK 1 read 0 100 GRANTED c2 LOCK 1 write 200 100 c2 $L LOCKW 1 write 0 100 DEADLOCK c1 LOCKW 1 read 200 100 DENIED c1 LOCK 1 read 200 100 c1 $U UNLOCK 1 0 100 { EXPECT c2 $L LOCKW GRANTED 1 write 0 100 EXPECT c1 $U UNLOCK GRANTED 1 unlock 0 100 } SLEEP 1 GRANTED c2 UNLOCK 1 0 100 #=============================================================================== # test 9 Complex Deadlock: owner-1 acquires read lock on 0-7, owner-2 acquires # read lock on 8-15, owner-3 acquires read lock on 16-23, owner-1 requests write # lock on 16-23 (blocks), owner-2 requests write-lock on 0-7 (blocks), owner-3 # requests write lock on 8-15 (deadlock). #=============================================================================== # Three way deadlock is not detected by kernel on totally local QUIT GRANTED c1 LOCK 1 read 0 8 GRANTED c2 LOCK 1 read 8 8 GRANTED c3 LOCK 1 read 16 8 c1 $L LOCKW 1 write 16 8 c2 $M LOCKW 1 write 0 8 SLEEP 1 DEADLOCK c3 LOCKW 1 write 8 8 c1 $U UNLOCK 1 0 8 { EXPECT c2 $M LOCKW GRANTED 1 write 0 8 EXPECT c1 $U UNLOCK GRANTED 1 unlock 0 8 } SLEEP 1 GRANTED c1 UNLOCK 1 0 8 c3 $U UNLOCK 1 16 8 { EXPECT c2 $L LOCKW GRANTED 1 write 16 8 EXPECT c1 $U UNLOCK GRANTED 1 unlock 16 8 } SLEEP 1 GRANTED c2 UNLOCK 1 16 8 GRANTED c2 UNLOCK 1 8 8 QUIT���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/multilock/sample_tests/design_tests�������������������������������������0000664�0000000�0000000�00000023461�13242724102�0024767�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright IBM Corporation, 2012 # Contributor: Frank Filz <ffilz@us.ibm.com> # # # This software is a server that implements the NFS protocol. # # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 3 of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # Tests from locking design CLIENTS c1 c2 OK c1 OPEN 1 rw create design.file OK c2 OPEN 1 rw design.file #=============================================================================== # test 1 owner-1 acquires a read lock, owner-2 acquires a read lock # (expect success) #=============================================================================== GRANTED c1 LOCK 1 read 100 100 GRANTED c2 LOCK 1 read 100 100 GRANTED c1 UNLOCK 1 100 100 GRANTED c2 UNLOCK 1 100 100 #=============================================================================== # test 2 owner-1 acquires a read lock, owner-2 requests a write lock (expect # 2nd lock to block) #=============================================================================== GRANTED c1 LOCK 1 read 200 100 DENIED c2 LOCK 1 write 200 100 c2 $L LOCKW 1 write 200 100 SLEEP 1 c1 $U UNLOCK 1 200 100 { EXPECT c2 $L LOCKW GRANTED 1 write 200 100 EXPECT c1 $U UNLOCK GRANTED 1 unlock 200 100 } SLEEP 1 GRANTED c2 UNLOCK 1 200 100 #=============================================================================== # test 3 owner-1 acquires a write lock, owner-2 requests a read lock (expect # 2nd lock to block) #=============================================================================== GRANTED c1 LOCK 1 write 300 100 DENIED c2 LOCK 1 read 300 100 c2 $L LOCKW 1 read 300 100 SLEEP 1 c1 $U UNLOCK 1 300 100 { EXPECT c2 $L LOCKW GRANTED 1 read 300 100 EXPECT c1 $U UNLOCK GRANTED 1 unlock 300 100 } SLEEP 1 GRANTED c2 UNLOCK 1 300 100 #=============================================================================== # test 4 owner-1 acquires a write lock, owner-2 requests a write lock (expect # 2nd lock to block) #=============================================================================== GRANTED c1 LOCK 1 write 300 100 DENIED c2 LOCK 1 write 300 100 c2 $L LOCKW 1 write 300 100 SLEEP 1 c1 $U UNLOCK 1 300 100 { EXPECT c2 $L LOCKW GRANTED 1 write 300 100 EXPECT c1 $U UNLOCK GRANTED 1 unlock 300 100 } SLEEP 1 GRANTED c2 UNLOCK 1 300 100 #=============================================================================== # test 5 Test lock splitting: acquire lock on range 0-15, release 0-7 (expect # to see 8-15 still locked), release 10-11 (expect to see 8-9 and 12-15 # still locked), release 14-15 (test to see 12-13 still locked) acquire # 16-31, acquire 32-63, release 30-33 (test to see 12-13, 16-29, 34-63 # still locked), release 0-EOF (represented as offset 0, length 0, # test to see all locks released) #=============================================================================== GRANTED c1 LOCK 1 read 0 16 DENIED c2 LOCK 1 write 0 16 GRANTED c1 UNLOCK 1 0 8 DENIED c2 LOCK 1 write 8 8 GRANTED c2 LOCK 1 write 0 8 GRANTED c2 UNLOCK 1 0 8 GRANTED c1 UNLOCK 1 10 2 DENIED c2 LOCK 1 write 8 2 DENIED c2 LOCK 1 write 12 4 GRANTED c2 LOCK 1 read 8 2 GRANTED c2 UNLOCK 1 8 2 GRANTED c1 UNLOCK 1 14 2 DENIED c2 LOCK 1 write 8 2 DENIED c2 LOCK 1 write 12 2 GRANTED c2 LOCK 1 write 14 0 GRANTED c2 UNLOCK 1 14 0 GRANTED c1 LOCK 1 read 16 16 DENIED c2 LOCK 1 write 16 16 GRANTED c2 LOCK 1 write 14 2 GRANTED c2 LOCK 1 write 32 0 GRANTED c2 UNLOCK 1 14 2 GRANTED c2 UNLOCK 1 32 0 GRANTED c1 LOCK 1 read 32 32 DENIED c2 LOCK 1 write 32 32 GRANTED c1 UNLOCK 1 30 4 DENIED c2 LOCK 1 write 12 2 DENIED c2 LOCK 1 write 16 14 DENIED c2 LOCK 1 write 34 30 GRANTED c2 LOCK 1 write 30 4 GRANTED c2 UNLOCK 1 30 4 GRANTED c1 UNLOCK 1 0 0 GRANTED c2 LOCK 1 write 0 0 GRANTED c2 UNLOCK 1 0 0 # test 5a try to combine different types of locks GRANTED c1 LOCK 1 write 0 4 GRANTED c1 LOCK 1 write 8 4 GRANTED c1 LOCK 1 read 4 4 DENIED c2 LOCK 1 write 0 4 DENIED c2 LOCK 1 write 4 4 DENIED c2 LOCK 1 write 8 4 DENIED c2 LOCK 1 write 0 12 GRANTED c2 LOCK 1 read 4 4 GRANTED c1 UNLOCK 1 0 0 GRANTED c2 UNLOCK 1 0 0 #=============================================================================== # test 6 acquire whole file lock one byte at a time in sequence 0-0, 2-2, 1-1, # 4-4, 3-3 (will verify no leaks in lock resources by combining locks - # there should never be more than two locks existing at once). Note that # this test will want to set some reasonable maximum since acquiring a # whole file lock on a 2 GB or larger file one byte at a time might take # forever. If this test passes without crashing Ganesha, Ganesha probably # isn't leaking locks... (we can also enable debug to verify - long term # we will need some objective ways for FVT to determine if Ganesha is # leaking memory) #=============================================================================== GRANTED c1 HOP 1 read 0 10000 DENIED c2 LOCK 1 write 0 10000 GRANTED c1 UNHOP 1 0 10000 GRANTED c1 HOP 1 write 0 10000 DENIED c2 LOCK 1 read 0 10000 GRANTED c1 UNHOP 1 0 10000 #=============================================================================== # test 7 Open and close several files, perform some lock TEST operations on # several files, acquire locks on several files, close files (some with # locks still active). #=============================================================================== # test currently not implemented because TEST operation is unreliable # also see split_lock test #=============================================================================== # test 8 Test deadlock condition: owner-1 acquires read lock on 0-7, owner-2 # acquires read lock on 8-15, owner-1 requests write lock on 8-15 # (blocks), owner-2 requests write lock on 0-7 (deadlock (test case # should see EDEADLK return from lock call)) #=============================================================================== # test kept separate as deadlock due to possibility test may not work for # all possible combinations of how c1 and c2 are run #=============================================================================== # test 9 Complex Deadlock: owner-1 acquires read lock on 0-7, owner-2 acquires # read lock on 8-15, owner-3 acquires read lock on 16-23, owner-1 requests write # lock on 16-23 (blocks), owner-2 requests write-lock on 0-7 (blocks), owner-3 # requests write lock on 8-15 (deadlock). #=============================================================================== # test kept separate as deadlock due to possibility test may not work for # all possible combinations of how c1 and c2 are run, also requires 3rd client. #=============================================================================== # test 10 Owner-1 acquires a read lock on 0-3, owner-2 acquires a write lock on # 4-7, owner-1 has two threads, owner-1 thread-1 requests a read lock on 4-7 # (which blocks), owner-1 thread-2 closes the file descriptor (test that all # owner-1 locks are released and the blocked lock is cleaned up) (this test is # only applicable to NFS v3) #=============================================================================== # Test not implemented, ml_posix_client is single threaded #=============================================================================== # test 11 owner-1 acquires a read lock on 0-3, owner-2 acquires a read lock on # 0-3, owner-1 does a test for read lock on 0-3 (should succeed), owner-1 does # a test for a write lock on 0-3 (should fail) #=============================================================================== # test currently not implemented because TEST operation is unreliable #=============================================================================== # test 12 owner-1 acquires a read lock on 0-3, owner-2 acquires a write lock on # 4-7, owner-1 does a test for a read lock on 0-3 (should succeed) #=============================================================================== # test currently not implemented because TEST operation is unreliable ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/multilock/sample_tests/fork���������������������������������������������0000664�0000000�0000000�00000001032�13242724102�0023223�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# CLIENTS client1 FORK client1 client2 OK client1 OPEN 1 rw create lock1 OK client2 OPEN 1 rw lock1 GRANTED client1 LOCK 1 write 0 100 GRANTED client2 LOCK 1 write 100 100 # Now test the locks client1 $ TEST 1 read 0 100 EXPECT client1 $ TEST GRANTED 1 read 0 100 client1 $ TEST 1 read 100 100 EXPECT client1 $ TEST CONFLICT 1 * write 100 100 client2 $ TEST 1 read 0 100 EXPECT client2 $ TEST CONFLICT 1 * write 0 100 client2 $ TEST 1 read 100 100 EXPECT client2 $ TEST GRANTED 1 read 100 100 OK client2 QUIT OK client1 QUIT QUIT ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/multilock/sample_tests/hoptest������������������������������������������0000664�0000000�0000000�00000002242�13242724102�0023754�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright IBM Corporation, 2012 # Contributor: Frank Filz <ffilz@us.ibm.com> # # # This software is a server that implements the NFS protocol. # # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 3 of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # This test case verifies the operation of the HOP and UNHOP commands CLIENTS c1 c2 OK c1 OPEN 1 rw create hoptest.file OK c2 OPEN 1 rw hoptest.file GRANTED c1 HOP 1 read 0 1000 c2 $ LIST 1 0 0 EXPECT c2 $ LIST CONFLICT 1 * read 0 1000 EXPECT c2 $ LIST DENIED 1 0 0 GRANTED c1 UNHOP 1 0 1000 QUIT ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/multilock/sample_tests/remote_run_example�������������������������������0000775�0000000�0000000�00000001710�13242724102�0026162�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright IBM Corporation, 2012 # Contributor: Frank Filz <ffilz@us.ibm.com> # # # This software is a server that implements the NFS protocol. # # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 3 of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ssh 192.168.0.107 /usr/src/testing/ml_posix_client -s 192.168.0.103 -c /mnt -n c2 -p 2051 ��������������������������������������������������������nfs-ganesha-2.6.0/src/tools/multilock/sample_tests/script1������������������������������������������0000664�0000000�0000000�00000002523�13242724102�0023655�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright IBM Corporation, 2012 # Contributor: Frank Filz <ffilz@us.ibm.com> # # # This software is a server that implements the NFS protocol. # # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 3 of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # simple script to test LIST command { # wait for 2 clients to start in any order EXPECT c1 * HELLO OK c1 EXPECT c2 * HELLO OK c2 } OK c1 OPEN 1 rw temp.out OK c2 OPEN 1 rw temp.out GRANTED c1 LOCK 1 write 10 20 GRANTED c1 LOCK 1 read 100 10 c2 $L LIST 1 0 0 { # Wait for two locks in any order EXPECT c2 $L LIST CONFLICT 1 * write 10 20 EXPECT c2 $L LIST CONFLICT 1 * read 100 10 } # List finished with a DENIED if any locks present EXPECT c2 $L LIST DENIED 1 0 0 QUIT �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/multilock/sample_tests/script2������������������������������������������0000664�0000000�0000000�00000015430�13242724102�0023657�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright IBM Corporation, 2012 # Contributor: Frank Filz <ffilz@us.ibm.com> # # # This software is a server that implements the NFS protocol. # # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 3 of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # demonstrate issues on GPFS with Ganesha and async blocking locks # # script requires running ml_posix_client on two NFS clients and # two GPFS nodes. client1 should mount the GPFS file system via # node1 and client2 from node2. # CLIENTS client1 client2 node1 node2 # open file on all clients and nodes OK client1 OPEN 1 rw create script2.file OK client2 OPEN 1 rw script2.file OK node1 OPEN 1 rw script2.file OK node2 OPEN 1 rw script2.file # get some locks GRANTED client1 LOCK 1 read 100 10 GRANTED client1 LOCK 1 write 150 10 GRANTED client2 LOCK 1 read 200 10 GRANTED client2 LOCK 1 write 250 10 GRANTED node1 LOCK 1 read 300 10 GRANTED node1 LOCK 1 write 350 10 GRANTED node2 LOCK 1 read 400 10 GRANTED node2 LOCK 1 write 450 10 # now test the locks with TEST from client1 client1 $ TEST 1 write 200 10 # test lock from client2 EXPECT client1 $ TEST CONFLICT 1 * read 200 10 client1 $ TEST 1 write 250 10 # test lock from client2 EXPECT client1 $ TEST CONFLICT 1 * write 250 10 client1 $ TEST 1 write 300 10 # test lock from node1 EXPECT client1 $ TEST CONFLICT 1 * read 300 10 client1 $ TEST 1 write 350 10 # test lock from node1 EXPECT client1 $ TEST CONFLICT 1 * write 350 10 client1 $ TEST 1 write 400 10 # test lock from node2 EXPECT client1 $ TEST CONFLICT 1 * read 400 10 client1 $ TEST 1 write 450 10 # test lock from node2 EXPECT client1 $ TEST CONFLICT 1 * write 450 10 # now test the locks with TEST from client2 client2 $ TEST 1 write 100 10 # test lock from client1 EXPECT client2 $ TEST CONFLICT 1 * read 100 10 client2 $ TEST 1 write 150 10 # test lock from client1 EXPECT client2 $ TEST CONFLICT 1 * write 150 10 client2 $ TEST 1 write 300 10 # test lock from node1 EXPECT client2 $ TEST CONFLICT 1 * read 300 10 client2 $ TEST 1 write 350 10 # test lock from node1 EXPECT client2 $ TEST CONFLICT 1 * write 350 10 client2 $ TEST 1 write 400 10 # test lock from node2 EXPECT client2 $ TEST CONFLICT 1 * read 400 10 client2 $ TEST 1 write 450 10 # test lock from node2 EXPECT client2 $ TEST CONFLICT 1 * write 450 10 # now test the locks with TEST from node1 node1 $ TEST 1 write 100 10 # test lock from client1 EXPECT node1 $ TEST CONFLICT 1 * read 100 10 node1 $ TEST 1 write 150 10 # test lock from client1 EXPECT node1 $ TEST CONFLICT 1 * write 150 10 node1 $ TEST 1 write 200 10 # test lock from client2 EXPECT node1 $ TEST CONFLICT 1 * read 200 10 node1 $ TEST 1 write 250 10 # test lock from client2 EXPECT node1 $ TEST CONFLICT 1 * write 250 10 node1 $ TEST 1 write 400 10 # test lock from node2 EXPECT node1 $ TEST CONFLICT 1 * read 400 10 node1 $ TEST 1 write 450 10 # test lock from node2 EXPECT node1 $ TEST CONFLICT 1 * write 450 10 # now test the locks with TEST from node2 node2 $ TEST 1 write 100 10 # test lock from client1 EXPECT node2 $ TEST CONFLICT 1 * read 100 10 node2 $ TEST 1 write 150 10 # test lock from client1 EXPECT node2 $ TEST CONFLICT 1 * write 150 10 node2 $ TEST 1 write 200 10 # test lock from client2 EXPECT node2 $ TEST CONFLICT 1 * read 200 10 node2 $ TEST 1 write 250 10 # test lock from client2 EXPECT node2 $ TEST CONFLICT 1 * write 250 10 node2 $ TEST 1 write 300 10 # test lock from node1 EXPECT node2 $ TEST CONFLICT 1 * read 300 10 node2 $ TEST 1 write 350 10 # test lock from node1 EXPECT node2 $ TEST CONFLICT 1 * write 350 10 # now test the locks with TEST from client1 (test larger lock) client1 $ TEST 1 write 200 12 # test lock from client2 EXPECT client1 $ TEST CONFLICT 1 * read 200 10 client1 $ TEST 1 write 250 12 # test lock from client2 EXPECT client1 $ TEST CONFLICT 1 * write 250 10 client1 $ TEST 1 write 300 12 # test lock from node1 EXPECT client1 $ TEST CONFLICT 1 * read 300 10 client1 $ TEST 1 write 350 12 # test lock from node1 EXPECT client1 $ TEST CONFLICT 1 * write 350 10 client1 $ TEST 1 write 400 12 # test lock from node2 EXPECT client1 $ TEST CONFLICT 1 * read 400 10 client1 $ TEST 1 write 450 12 # test lock from node2 EXPECT client1 $ TEST CONFLICT 1 * write 450 10 # now test the locks with TEST from client2 (test larger lock) client2 $ TEST 1 write 100 12 # test lock from client1 EXPECT client2 $ TEST CONFLICT 1 * read 100 10 client2 $ TEST 1 write 150 12 # test lock from client1 EXPECT client2 $ TEST CONFLICT 1 * write 150 10 client2 $ TEST 1 write 300 12 # test lock from node1 EXPECT client2 $ TEST CONFLICT 1 * read 300 10 client2 $ TEST 1 write 350 12 # test lock from node1 EXPECT client2 $ TEST CONFLICT 1 * write 350 10 client2 $ TEST 1 write 400 12 # test lock from node2 EXPECT client2 $ TEST CONFLICT 1 * read 400 10 client2 $ TEST 1 write 450 12 # test lock from node2 EXPECT client2 $ TEST CONFLICT 1 * write 450 10 # now test the locks with TEST from node1 (test larger lock) node1 $ TEST 1 write 100 12 # test lock from client1 EXPECT node1 $ TEST CONFLICT 1 * read 100 10 node1 $ TEST 1 write 150 12 # test lock from client1 EXPECT node1 $ TEST CONFLICT 1 * write 150 10 node1 $ TEST 1 write 200 12 # test lock from client2 EXPECT node1 $ TEST CONFLICT 1 * read 200 10 node1 $ TEST 1 write 250 12 # test lock from client2 EXPECT node1 $ TEST CONFLICT 1 * write 250 10 node1 $ TEST 1 write 400 12 # test lock from node2 EXPECT node1 $ TEST CONFLICT 1 * read 400 10 node1 $ TEST 1 write 450 12 # test lock from node2 EXPECT node1 $ TEST CONFLICT 1 * write 450 10 # now test the locks with TEST from node2 (test larger lock) node2 $ TEST 1 write 100 12 # test lock from client1 EXPECT node2 $ TEST CONFLICT 1 * read 100 10 node2 $ TEST 1 write 150 12 # test lock from client1 EXPECT node2 $ TEST CONFLICT 1 * write 150 10 node2 $ TEST 1 write 200 12 # test lock from client2 EXPECT node2 $ TEST CONFLICT 1 * read 200 10 node2 $ TEST 1 write 250 12 # test lock from client2 EXPECT node2 $ TEST CONFLICT 1 * write 250 10 node2 $ TEST 1 write 300 12 # test lock from node1 EXPECT node2 $ TEST CONFLICT 1 * read 300 10 node2 $ TEST 1 write 350 12 # test lock from node1 EXPECT node2 $ TEST CONFLICT 1 * write 350 10 # quit and exit (all clients, closing all files, releasing all locks) QUIT����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/multilock/sample_tests/simple_block�������������������������������������0000664�0000000�0000000�00000002421�13242724102�0024730�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright IBM Corporation, 2012 # Contributor: Frank Filz <ffilz@us.ibm.com> # # # This software is a server that implements the NFS protocol. # # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 3 of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # This test case attempts a simple blocking lock situation CLIENTS c1 c2 OK c1 OPEN 1 rw create temp.out OK c2 OPEN 1 rw temp.out GRANTED c1 LOCK 1 write 0 10 # wait for conflicting lock c2 $G LOCKW 1 write 0 10 sleep 2 # now release c1's lock so c2 can get lock c1 $R UNLOCK 1 0 10 { EXPECT c1 $R UNLOCK GRANTED 1 unlock 0 10 EXPECT c2 $G LOCKW GRANTED 1 write 0 10 } sleep 1 GRANTED c2 UNLOCK 1 0 10 QUIT �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/multilock/sample_tests/split_lock���������������������������������������0000664�0000000�0000000�00000015670�13242724102�0024442�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright IBM Corporation, 2012 # Contributor: Frank Filz <ffilz@us.ibm.com> # # # This software is a server that implements the NFS protocol. # # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 3 of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # This test case tests a variety of split lock situations CLIENTS c1 c2 # open a bunch of files and then close them # repeat several times OK c1 OPEN 1 rw create splitfile.1.file OK c1 OPEN 2 rw create splitfile.2.file OK c1 OPEN 3 rw create splitfile.3.file OK c1 OPEN 4 rw create splitfile.4.file OK c1 OPEN 5 rw create splitfile.5.file OK c1 OPEN 6 rw create splitfile.6.file OK c1 OPEN 7 rw create splitfile.7.file OK c1 OPEN 8 rw create splitfile.8.file OK c1 OPEN 9 rw create splitfile.9.file OK c1 OPEN 10 rw create splitfile.10.file OK c1 CLOSE 1 OK c1 CLOSE 2 OK c1 CLOSE 3 OK c1 CLOSE 4 OK c1 CLOSE 5 OK c1 CLOSE 6 OK c1 CLOSE 7 OK c1 CLOSE 8 OK c1 CLOSE 9 OK c1 CLOSE 10 OK c1 OPEN 1 rw splitfile.1.file OK c1 OPEN 2 rw splitfile.2.file OK c1 OPEN 3 rw splitfile.3.file OK c1 OPEN 4 rw splitfile.4.file OK c1 OPEN 5 rw splitfile.5.file OK c1 OPEN 6 rw splitfile.6.file OK c1 OPEN 7 rw splitfile.7.file OK c1 OPEN 8 rw splitfile.8.file OK c1 OPEN 9 rw splitfile.9.file OK c1 OPEN 10 rw splitfile.10.file OK c1 CLOSE 1 OK c1 CLOSE 2 OK c1 CLOSE 3 OK c1 CLOSE 4 OK c1 CLOSE 5 OK c1 CLOSE 6 OK c1 CLOSE 7 OK c1 CLOSE 8 OK c1 CLOSE 9 OK c1 CLOSE 10 OK c1 OPEN 1 rw splitfile.1.file OK c1 OPEN 2 rw splitfile.2.file OK c1 OPEN 3 rw splitfile.3.file OK c1 OPEN 4 rw splitfile.4.file OK c1 OPEN 5 rw splitfile.5.file OK c1 OPEN 6 rw splitfile.6.file OK c1 OPEN 7 rw splitfile.7.file OK c1 OPEN 8 rw splitfile.8.file OK c1 OPEN 9 rw splitfile.9.file OK c1 OPEN 10 rw splitfile.10.file OK c1 CLOSE 1 OK c1 CLOSE 2 OK c1 CLOSE 3 OK c1 CLOSE 4 OK c1 CLOSE 5 OK c1 CLOSE 6 OK c1 CLOSE 7 OK c1 CLOSE 8 OK c1 CLOSE 9 OK c1 CLOSE 10 OK c1 OPEN 1 rw splitfile.1.file OK c1 OPEN 2 rw splitfile.2.file OK c1 OPEN 3 rw splitfile.3.file OK c1 OPEN 4 rw splitfile.4.file OK c1 OPEN 5 rw splitfile.5.file OK c1 OPEN 6 rw splitfile.6.file OK c1 OPEN 7 rw splitfile.7.file OK c1 OPEN 8 rw splitfile.8.file OK c1 OPEN 9 rw splitfile.9.file OK c1 OPEN 10 rw splitfile.10.file OK c1 CLOSE 1 OK c1 CLOSE 2 OK c1 CLOSE 3 OK c1 CLOSE 4 OK c1 CLOSE 5 OK c1 CLOSE 6 OK c1 CLOSE 7 OK c1 CLOSE 8 OK c1 CLOSE 9 OK c1 CLOSE 10 # Now open a bunch of files and get locks on them OK c1 OPEN 1 rw splitfile.1.file OK c1 OPEN 2 rw splitfile.2.file OK c1 OPEN 3 rw splitfile.3.file OK c1 OPEN 4 rw splitfile.4.file OK c1 OPEN 5 rw splitfile.5.file OK c1 OPEN 6 rw splitfile.6.file OK c1 OPEN 7 rw splitfile.7.file OK c1 OPEN 8 rw splitfile.8.file OK c1 OPEN 9 rw splitfile.9.file OK c1 OPEN 10 rw splitfile.10.file OK c2 OPEN 1 rw splitfile.1.file OK c2 OPEN 2 rw splitfile.2.file OK c2 OPEN 3 rw splitfile.3.file OK c2 OPEN 4 rw splitfile.4.file OK c2 OPEN 5 rw splitfile.5.file OK c2 OPEN 6 rw splitfile.6.file OK c2 OPEN 7 rw splitfile.7.file OK c2 OPEN 8 rw splitfile.8.file OK c2 OPEN 9 rw splitfile.9.file OK c2 OPEN 10 rw splitfile.10.file GRANTED c1 LOCK 1 write 0 0 GRANTED c1 LOCK 2 write 0 0 GRANTED c1 LOCK 3 write 0 0 GRANTED c1 LOCK 4 write 0 0 GRANTED c1 LOCK 5 write 0 0 GRANTED c1 LOCK 6 write 0 0 GRANTED c1 LOCK 7 write 0 0 GRANTED c1 LOCK 8 write 0 0 GRANTED c1 LOCK 9 write 0 0 GRANTED c1 LOCK 10 write 0 0 # Now probe the locks DENIED c2 LOCK 1 write 0 0 DENIED c2 LOCK 2 write 0 0 DENIED c2 LOCK 3 write 0 0 DENIED c2 LOCK 4 write 0 0 DENIED c2 LOCK 5 write 0 0 DENIED c2 LOCK 6 write 0 0 DENIED c2 LOCK 7 write 0 0 DENIED c2 LOCK 8 write 0 0 DENIED c2 LOCK 9 write 0 0 DENIED c2 LOCK 10 write 0 0 #now release the locks GRANTED c1 UNLOCK 1 0 0 GRANTED c1 UNLOCK 2 0 0 GRANTED c1 UNLOCK 3 0 0 GRANTED c1 UNLOCK 4 0 0 GRANTED c1 UNLOCK 5 0 0 GRANTED c1 UNLOCK 6 0 0 GRANTED c1 UNLOCK 7 0 0 GRANTED c1 UNLOCK 8 0 0 GRANTED c1 UNLOCK 9 0 0 GRANTED c1 UNLOCK 10 0 0 # and close the files OK c1 CLOSE 1 OK c1 CLOSE 2 OK c1 CLOSE 3 OK c1 CLOSE 4 OK c1 CLOSE 5 OK c1 CLOSE 6 OK c1 CLOSE 7 OK c1 CLOSE 8 OK c1 CLOSE 9 OK c1 CLOSE 10 OK c2 CLOSE 1 OK c2 CLOSE 2 OK c2 CLOSE 3 OK c2 CLOSE 4 OK c2 CLOSE 5 OK c2 CLOSE 6 OK c2 CLOSE 7 OK c2 CLOSE 8 OK c2 CLOSE 9 OK c2 CLOSE 10 # Now do the split lock tests OK c1 OPEN 1 rw splitfile.1.file OK c2 OPEN 1 rw splitfile.1.file GRANTED c1 LOCK 1 read 0 4 GRANTED c1 LOCK 1 read 8 4 GRANTED c1 LOCK 1 read 16 4 GRANTED c1 LOCK 1 read 24 4 c2 $L LIST 1 0 0 { EXPECT c2 $L LIST CONFLICT 1 * read 0 4 EXPECT c2 $L LIST CONFLICT 1 * read 8 4 EXPECT c2 $L LIST CONFLICT 1 * read 16 4 EXPECT c2 $L LIST CONFLICT 1 * read 24 4 } EXPECT c2 $L LIST DENIED 1 0 0 DENIED c2 LOCK 1 write 0 4 DENIED c2 LOCK 1 write 8 4 DENIED c2 LOCK 1 write 16 4 DENIED c2 LOCK 1 write 24 4 GRANTED c2 LOCK 1 write 4 4 GRANTED c2 LOCK 1 write 12 4 GRANTED c2 LOCK 1 write 20 4 GRANTED c2 LOCK 1 write 28 0 GRANTED c2 LOCK 1 read 0 4 GRANTED c2 LOCK 1 read 8 4 GRANTED c2 LOCK 1 read 16 4 GRANTED c2 LOCK 1 read 24 4 GRANTED c1 UNLOCK 1 0 0 c1 $L LIST 1 0 0 { EXPECT c1 $L LIST CONFLICT 1 * read 0 4 EXPECT c1 $L LIST CONFLICT 1 * read 8 4 EXPECT c1 $L LIST CONFLICT 1 * read 16 4 EXPECT c1 $L LIST CONFLICT 1 * read 24 4 EXPECT c1 $L LIST CONFLICT 1 * write 4 4 EXPECT c1 $L LIST CONFLICT 1 * write 12 4 EXPECT c1 $L LIST CONFLICT 1 * write 20 4 EXPECT c1 $L LIST CONFLICT 1 * write 28 0 } EXPECT c1 $L LIST DENIED 1 0 0 GRANTED c2 UNLOCK 1 0 0 AVAILABLE c1 LIST 1 0 0 AVAILABLE c2 LIST 1 0 0 # now test splitting a lock GRANTED c1 LOCK 1 read 0 16 GRANTED c1 LOCK 1 write 32 16 DENIED c2 LOCK 1 write 0 16 DENIED c2 LOCK 1 write 32 16 c2 $L LIST 1 0 0 { EXPECT c2 $L LIST CONFLICT 1 * read 0 16 EXPECT c2 $L LIST CONFLICT 1 * write 32 16 } EXPECT c2 $L LIST DENIED 1 0 0 GRANTED c1 UNLOCK 1 4 4 GRANTED c1 UNLOCK 1 36 4 c2 $L LIST 1 0 0 { EXPECT c2 $L LIST CONFLICT 1 * read 0 4 EXPECT c2 $L LIST CONFLICT 1 * read 8 8 EXPECT c2 $L LIST CONFLICT 1 * write 32 4 EXPECT c2 $L LIST CONFLICT 1 * write 40 8 } EXPECT c2 $L LIST DENIED 1 0 0 # now combine locks GRANTED c1 LOCK 1 read 4 4 GRANTED c1 LOCK 1 write 36 4 c2 $L LIST 1 0 0 { EXPECT c2 $L LIST CONFLICT 1 * read 0 16 EXPECT c2 $L LIST CONFLICT 1 * write 32 16 } EXPECT c2 $L LIST DENIED 1 0 0 GRANTED c1 UNLOCK 1 0 0 AVAILABLE c1 LIST 1 0 0 AVAILABLE c2 LIST 1 0 0 QUIT ������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/multilock/sample_tests/split_lock_3�������������������������������������0000664�0000000�0000000�00000016046�13242724102�0024662�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright IBM Corporation, 2012 # Contributor: Frank Filz <ffilz@us.ibm.com> # # # This software is a server that implements the NFS protocol. # # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 3 of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # This test is similar to split_lock, but it uses a 3rd client to perform all LIST operations # The 3rd client should be run the server since LIST does not work with Linux client. CLIENTS c1 c2 c3 # open a bunch of files and then close them # repeat several times OK c1 OPEN 1 rw create splitfile.1.file OK c1 OPEN 2 rw create splitfile.2.file OK c1 OPEN 3 rw create splitfile.3.file OK c1 OPEN 4 rw create splitfile.4.file OK c1 OPEN 5 rw create splitfile.5.file OK c1 OPEN 6 rw create splitfile.6.file OK c1 OPEN 7 rw create splitfile.7.file OK c1 OPEN 8 rw create splitfile.8.file OK c1 OPEN 9 rw create splitfile.9.file OK c1 OPEN 10 rw create splitfile.10.file OK c1 CLOSE 1 OK c1 CLOSE 2 OK c1 CLOSE 3 OK c1 CLOSE 4 OK c1 CLOSE 5 OK c1 CLOSE 6 OK c1 CLOSE 7 OK c1 CLOSE 8 OK c1 CLOSE 9 OK c1 CLOSE 10 OK c1 OPEN 1 rw splitfile.1.file OK c1 OPEN 2 rw splitfile.2.file OK c1 OPEN 3 rw splitfile.3.file OK c1 OPEN 4 rw splitfile.4.file OK c1 OPEN 5 rw splitfile.5.file OK c1 OPEN 6 rw splitfile.6.file OK c1 OPEN 7 rw splitfile.7.file OK c1 OPEN 8 rw splitfile.8.file OK c1 OPEN 9 rw splitfile.9.file OK c1 OPEN 10 rw splitfile.10.file OK c1 CLOSE 1 OK c1 CLOSE 2 OK c1 CLOSE 3 OK c1 CLOSE 4 OK c1 CLOSE 5 OK c1 CLOSE 6 OK c1 CLOSE 7 OK c1 CLOSE 8 OK c1 CLOSE 9 OK c1 CLOSE 10 OK c1 OPEN 1 rw splitfile.1.file OK c1 OPEN 2 rw splitfile.2.file OK c1 OPEN 3 rw splitfile.3.file OK c1 OPEN 4 rw splitfile.4.file OK c1 OPEN 5 rw splitfile.5.file OK c1 OPEN 6 rw splitfile.6.file OK c1 OPEN 7 rw splitfile.7.file OK c1 OPEN 8 rw splitfile.8.file OK c1 OPEN 9 rw splitfile.9.file OK c1 OPEN 10 rw splitfile.10.file OK c1 CLOSE 1 OK c1 CLOSE 2 OK c1 CLOSE 3 OK c1 CLOSE 4 OK c1 CLOSE 5 OK c1 CLOSE 6 OK c1 CLOSE 7 OK c1 CLOSE 8 OK c1 CLOSE 9 OK c1 CLOSE 10 OK c1 OPEN 1 rw splitfile.1.file OK c1 OPEN 2 rw splitfile.2.file OK c1 OPEN 3 rw splitfile.3.file OK c1 OPEN 4 rw splitfile.4.file OK c1 OPEN 5 rw splitfile.5.file OK c1 OPEN 6 rw splitfile.6.file OK c1 OPEN 7 rw splitfile.7.file OK c1 OPEN 8 rw splitfile.8.file OK c1 OPEN 9 rw splitfile.9.file OK c1 OPEN 10 rw splitfile.10.file OK c1 CLOSE 1 OK c1 CLOSE 2 OK c1 CLOSE 3 OK c1 CLOSE 4 OK c1 CLOSE 5 OK c1 CLOSE 6 OK c1 CLOSE 7 OK c1 CLOSE 8 OK c1 CLOSE 9 OK c1 CLOSE 10 # Now open a bunch of files and get locks on them OK c1 OPEN 1 rw splitfile.1.file OK c1 OPEN 2 rw splitfile.2.file OK c1 OPEN 3 rw splitfile.3.file OK c1 OPEN 4 rw splitfile.4.file OK c1 OPEN 5 rw splitfile.5.file OK c1 OPEN 6 rw splitfile.6.file OK c1 OPEN 7 rw splitfile.7.file OK c1 OPEN 8 rw splitfile.8.file OK c1 OPEN 9 rw splitfile.9.file OK c1 OPEN 10 rw splitfile.10.file OK c2 OPEN 1 rw splitfile.1.file OK c2 OPEN 2 rw splitfile.2.file OK c2 OPEN 3 rw splitfile.3.file OK c2 OPEN 4 rw splitfile.4.file OK c2 OPEN 5 rw splitfile.5.file OK c2 OPEN 6 rw splitfile.6.file OK c2 OPEN 7 rw splitfile.7.file OK c2 OPEN 8 rw splitfile.8.file OK c2 OPEN 9 rw splitfile.9.file OK c2 OPEN 10 rw splitfile.10.file GRANTED c1 LOCK 1 write 0 0 GRANTED c1 LOCK 2 write 0 0 GRANTED c1 LOCK 3 write 0 0 GRANTED c1 LOCK 4 write 0 0 GRANTED c1 LOCK 5 write 0 0 GRANTED c1 LOCK 6 write 0 0 GRANTED c1 LOCK 7 write 0 0 GRANTED c1 LOCK 8 write 0 0 GRANTED c1 LOCK 9 write 0 0 GRANTED c1 LOCK 10 write 0 0 # Now probe the locks DENIED c2 LOCK 1 write 0 0 DENIED c2 LOCK 2 write 0 0 DENIED c2 LOCK 3 write 0 0 DENIED c2 LOCK 4 write 0 0 DENIED c2 LOCK 5 write 0 0 DENIED c2 LOCK 6 write 0 0 DENIED c2 LOCK 7 write 0 0 DENIED c2 LOCK 8 write 0 0 DENIED c2 LOCK 9 write 0 0 DENIED c2 LOCK 10 write 0 0 #now release the locks GRANTED c1 UNLOCK 1 0 0 GRANTED c1 UNLOCK 2 0 0 GRANTED c1 UNLOCK 3 0 0 GRANTED c1 UNLOCK 4 0 0 GRANTED c1 UNLOCK 5 0 0 GRANTED c1 UNLOCK 6 0 0 GRANTED c1 UNLOCK 7 0 0 GRANTED c1 UNLOCK 8 0 0 GRANTED c1 UNLOCK 9 0 0 GRANTED c1 UNLOCK 10 0 0 # and close the files OK c1 CLOSE 1 OK c1 CLOSE 2 OK c1 CLOSE 3 OK c1 CLOSE 4 OK c1 CLOSE 5 OK c1 CLOSE 6 OK c1 CLOSE 7 OK c1 CLOSE 8 OK c1 CLOSE 9 OK c1 CLOSE 10 OK c2 CLOSE 1 OK c2 CLOSE 2 OK c2 CLOSE 3 OK c2 CLOSE 4 OK c2 CLOSE 5 OK c2 CLOSE 6 OK c2 CLOSE 7 OK c2 CLOSE 8 OK c2 CLOSE 9 OK c2 CLOSE 10 # Now do the split lock tests OK c1 OPEN 1 rw splitfile.1.file OK c2 OPEN 1 rw splitfile.1.file OK c3 OPEN 1 rw splitfile.1.file GRANTED c1 LOCK 1 read 0 4 GRANTED c1 LOCK 1 read 8 4 GRANTED c1 LOCK 1 read 16 4 GRANTED c1 LOCK 1 read 24 4 c3 $L LIST 1 0 0 { EXPECT c3 $L LIST CONFLICT 1 * read 0 4 EXPECT c3 $L LIST CONFLICT 1 * read 8 4 EXPECT c3 $L LIST CONFLICT 1 * read 16 4 EXPECT c3 $L LIST CONFLICT 1 * read 24 4 } EXPECT c3 $L LIST DENIED 1 0 0 DENIED c2 LOCK 1 write 0 4 DENIED c2 LOCK 1 write 8 4 DENIED c2 LOCK 1 write 16 4 DENIED c2 LOCK 1 write 24 4 GRANTED c2 LOCK 1 write 4 4 GRANTED c2 LOCK 1 write 12 4 GRANTED c2 LOCK 1 write 20 4 GRANTED c2 LOCK 1 write 28 0 GRANTED c2 LOCK 1 read 0 4 GRANTED c2 LOCK 1 read 8 4 GRANTED c2 LOCK 1 read 16 4 GRANTED c2 LOCK 1 read 24 4 GRANTED c1 UNLOCK 1 0 0 c3 $L LIST 1 0 0 { EXPECT c3 $L LIST CONFLICT 1 * read 0 4 EXPECT c3 $L LIST CONFLICT 1 * read 8 4 EXPECT c3 $L LIST CONFLICT 1 * read 16 4 EXPECT c3 $L LIST CONFLICT 1 * read 24 4 EXPECT c3 $L LIST CONFLICT 1 * write 4 4 EXPECT c3 $L LIST CONFLICT 1 * write 12 4 EXPECT c3 $L LIST CONFLICT 1 * write 20 4 EXPECT c3 $L LIST CONFLICT 1 * write 28 0 } EXPECT c3 $L LIST DENIED 1 0 0 GRANTED c2 UNLOCK 1 0 0 AVAILABLE c3 LIST 1 0 0 # now test splitting a lock GRANTED c1 LOCK 1 read 0 16 GRANTED c1 LOCK 1 write 32 16 DENIED c2 LOCK 1 write 0 16 DENIED c2 LOCK 1 write 32 16 c3 $L LIST 1 0 0 { EXPECT c3 $L LIST CONFLICT 1 * read 0 16 EXPECT c3 $L LIST CONFLICT 1 * write 32 16 } EXPECT c3 $L LIST DENIED 1 0 0 GRANTED c1 UNLOCK 1 4 4 GRANTED c1 UNLOCK 1 36 4 c3 $L LIST 1 0 0 { EXPECT c3 $L LIST CONFLICT 1 * read 0 4 EXPECT c3 $L LIST CONFLICT 1 * read 8 8 EXPECT c3 $L LIST CONFLICT 1 * write 32 4 EXPECT c3 $L LIST CONFLICT 1 * write 40 8 } EXPECT c3 $L LIST DENIED 1 0 0 # now combine locks GRANTED c1 LOCK 1 read 4 4 GRANTED c1 LOCK 1 write 36 4 c3 $L LIST 1 0 0 { EXPECT c3 $L LIST CONFLICT 1 * read 0 16 EXPECT c3 $L LIST CONFLICT 1 * write 32 16 } EXPECT c3 $L LIST DENIED 1 0 0 GRANTED c1 UNLOCK 1 0 0 AVAILABLE c3 LIST 1 0 0 QUIT ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/multilock/sample_tests/split_lock_no_list�������������������������������0000664�0000000�0000000�00000013763�13242724102�0026172�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright IBM Corporation, 2012 # Contributor: Frank Filz <ffilz@us.ibm.com> # # # This software is a server that implements the NFS protocol. # # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 3 of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # This test is similar to split_lock, but doesn't use LIST at all CLIENTS c1 c2 # open a bunch of files and then close them # repeat several times OK c1 OPEN 1 rw create splitfile.1.file OK c1 OPEN 2 rw create splitfile.2.file OK c1 OPEN 3 rw create splitfile.3.file OK c1 OPEN 4 rw create splitfile.4.file OK c1 OPEN 5 rw create splitfile.5.file OK c1 OPEN 6 rw create splitfile.6.file OK c1 OPEN 7 rw create splitfile.7.file OK c1 OPEN 8 rw create splitfile.8.file OK c1 OPEN 9 rw create splitfile.9.file OK c1 OPEN 10 rw create splitfile.10.file OK c1 CLOSE 1 OK c1 CLOSE 2 OK c1 CLOSE 3 OK c1 CLOSE 4 OK c1 CLOSE 5 OK c1 CLOSE 6 OK c1 CLOSE 7 OK c1 CLOSE 8 OK c1 CLOSE 9 OK c1 CLOSE 10 OK c1 OPEN 1 rw splitfile.1.file OK c1 OPEN 2 rw splitfile.2.file OK c1 OPEN 3 rw splitfile.3.file OK c1 OPEN 4 rw splitfile.4.file OK c1 OPEN 5 rw splitfile.5.file OK c1 OPEN 6 rw splitfile.6.file OK c1 OPEN 7 rw splitfile.7.file OK c1 OPEN 8 rw splitfile.8.file OK c1 OPEN 9 rw splitfile.9.file OK c1 OPEN 10 rw splitfile.10.file OK c1 CLOSE 1 OK c1 CLOSE 2 OK c1 CLOSE 3 OK c1 CLOSE 4 OK c1 CLOSE 5 OK c1 CLOSE 6 OK c1 CLOSE 7 OK c1 CLOSE 8 OK c1 CLOSE 9 OK c1 CLOSE 10 OK c1 OPEN 1 rw splitfile.1.file OK c1 OPEN 2 rw splitfile.2.file OK c1 OPEN 3 rw splitfile.3.file OK c1 OPEN 4 rw splitfile.4.file OK c1 OPEN 5 rw splitfile.5.file OK c1 OPEN 6 rw splitfile.6.file OK c1 OPEN 7 rw splitfile.7.file OK c1 OPEN 8 rw splitfile.8.file OK c1 OPEN 9 rw splitfile.9.file OK c1 OPEN 10 rw splitfile.10.file OK c1 CLOSE 1 OK c1 CLOSE 2 OK c1 CLOSE 3 OK c1 CLOSE 4 OK c1 CLOSE 5 OK c1 CLOSE 6 OK c1 CLOSE 7 OK c1 CLOSE 8 OK c1 CLOSE 9 OK c1 CLOSE 10 OK c1 OPEN 1 rw splitfile.1.file OK c1 OPEN 2 rw splitfile.2.file OK c1 OPEN 3 rw splitfile.3.file OK c1 OPEN 4 rw splitfile.4.file OK c1 OPEN 5 rw splitfile.5.file OK c1 OPEN 6 rw splitfile.6.file OK c1 OPEN 7 rw splitfile.7.file OK c1 OPEN 8 rw splitfile.8.file OK c1 OPEN 9 rw splitfile.9.file OK c1 OPEN 10 rw splitfile.10.file OK c1 CLOSE 1 OK c1 CLOSE 2 OK c1 CLOSE 3 OK c1 CLOSE 4 OK c1 CLOSE 5 OK c1 CLOSE 6 OK c1 CLOSE 7 OK c1 CLOSE 8 OK c1 CLOSE 9 OK c1 CLOSE 10 # Now open a bunch of files and get locks on them OK c1 OPEN 1 rw splitfile.1.file OK c1 OPEN 2 rw splitfile.2.file OK c1 OPEN 3 rw splitfile.3.file OK c1 OPEN 4 rw splitfile.4.file OK c1 OPEN 5 rw splitfile.5.file OK c1 OPEN 6 rw splitfile.6.file OK c1 OPEN 7 rw splitfile.7.file OK c1 OPEN 8 rw splitfile.8.file OK c1 OPEN 9 rw splitfile.9.file OK c1 OPEN 10 rw splitfile.10.file OK c2 OPEN 1 rw splitfile.1.file OK c2 OPEN 2 rw splitfile.2.file OK c2 OPEN 3 rw splitfile.3.file OK c2 OPEN 4 rw splitfile.4.file OK c2 OPEN 5 rw splitfile.5.file OK c2 OPEN 6 rw splitfile.6.file OK c2 OPEN 7 rw splitfile.7.file OK c2 OPEN 8 rw splitfile.8.file OK c2 OPEN 9 rw splitfile.9.file OK c2 OPEN 10 rw splitfile.10.file GRANTED c1 LOCK 1 write 0 0 GRANTED c1 LOCK 2 write 0 0 GRANTED c1 LOCK 3 write 0 0 GRANTED c1 LOCK 4 write 0 0 GRANTED c1 LOCK 5 write 0 0 GRANTED c1 LOCK 6 write 0 0 GRANTED c1 LOCK 7 write 0 0 GRANTED c1 LOCK 8 write 0 0 GRANTED c1 LOCK 9 write 0 0 GRANTED c1 LOCK 10 write 0 0 # Now probe the locks DENIED c2 LOCK 1 write 0 0 DENIED c2 LOCK 2 write 0 0 DENIED c2 LOCK 3 write 0 0 DENIED c2 LOCK 4 write 0 0 DENIED c2 LOCK 5 write 0 0 DENIED c2 LOCK 6 write 0 0 DENIED c2 LOCK 7 write 0 0 DENIED c2 LOCK 8 write 0 0 DENIED c2 LOCK 9 write 0 0 DENIED c2 LOCK 10 write 0 0 #now release the locks GRANTED c1 UNLOCK 1 0 0 GRANTED c1 UNLOCK 2 0 0 GRANTED c1 UNLOCK 3 0 0 GRANTED c1 UNLOCK 4 0 0 GRANTED c1 UNLOCK 5 0 0 GRANTED c1 UNLOCK 6 0 0 GRANTED c1 UNLOCK 7 0 0 GRANTED c1 UNLOCK 8 0 0 GRANTED c1 UNLOCK 9 0 0 GRANTED c1 UNLOCK 10 0 0 # and close the files OK c1 CLOSE 1 OK c1 CLOSE 2 OK c1 CLOSE 3 OK c1 CLOSE 4 OK c1 CLOSE 5 OK c1 CLOSE 6 OK c1 CLOSE 7 OK c1 CLOSE 8 OK c1 CLOSE 9 OK c1 CLOSE 10 OK c2 CLOSE 1 OK c2 CLOSE 2 OK c2 CLOSE 3 OK c2 CLOSE 4 OK c2 CLOSE 5 OK c2 CLOSE 6 OK c2 CLOSE 7 OK c2 CLOSE 8 OK c2 CLOSE 9 OK c2 CLOSE 10 # Now do the split lock tests OK c1 OPEN 1 rw splitfile.1.file OK c2 OPEN 1 rw splitfile.1.file GRANTED c1 LOCK 1 read 0 4 GRANTED c1 LOCK 1 read 8 4 GRANTED c1 LOCK 1 read 16 4 GRANTED c1 LOCK 1 read 24 4 DENIED c2 LOCK 1 write 0 4 DENIED c2 LOCK 1 write 8 4 DENIED c2 LOCK 1 write 16 4 DENIED c2 LOCK 1 write 24 4 GRANTED c2 LOCK 1 write 4 4 GRANTED c2 LOCK 1 write 12 4 GRANTED c2 LOCK 1 write 20 4 GRANTED c2 LOCK 1 write 28 0 GRANTED c2 LOCK 1 read 0 4 GRANTED c2 LOCK 1 read 8 4 GRANTED c2 LOCK 1 read 16 4 GRANTED c2 LOCK 1 read 24 4 GRANTED c1 UNLOCK 1 0 0 GRANTED c2 UNLOCK 1 0 0 # now test splitting a lock GRANTED c1 LOCK 1 read 0 16 GRANTED c1 LOCK 1 write 32 16 DENIED c2 LOCK 1 write 0 16 DENIED c2 LOCK 1 write 32 16 DENIED c2 LOCK 1 write 4 4 DENIED c2 LOCK 1 write 36 4 GRANTED c1 UNLOCK 1 4 4 GRANTED c1 UNLOCK 1 36 4 GRANTED c2 LOCK 1 write 4 4 GRANTED c2 LOCK 1 write 36 4 GRANTED c2 UNLOCK 1 0 0 # now combine locks GRANTED c1 LOCK 1 read 4 4 GRANTED c1 LOCK 1 write 36 4 DENIED c2 LOCK 1 write 4 4 DENIED c2 LOCK 1 write 36 4 DENIED c2 LOCK 1 write 0 16 DENIED c2 LOCK 1 write 32 16 GRANTED c1 UNLOCK 1 0 0 QUIT �������������nfs-ganesha-2.6.0/src/tools/multilock/sample_tests/try_test�����������������������������������������0000664�0000000�0000000�00000002312�13242724102�0024141�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright IBM Corporation, 2012 # Contributor: Frank Filz <ffilz@us.ibm.com> # # # This software is a server that implements the NFS protocol. # # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 3 of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # This script requests a lock from one process and tries to test it from another process CLIENTS c1 c2 OK c1 OPEN 1 rw create try_test.file OK c2 OPEN 1 rw try_test.file GRANTED c1 LOCK 1 read 102030 123 c2 $ TEST 1 write 101020 1234 EXPECT c2 $ TEST CONFLICT 1 * read 102030 123 GRANTED c1 UNLOCK 1 102030 123 OK c1 CLOSE 1 OK c2 CLOSE 1 QUIT ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/remove_rpc.pl�����������������������������������������������������������0000775�0000000�0000000�00000004403�13242724102�0020337�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl use strict; my $var = <<END; #ifdef _USE_GSSRPC #include <gssrpc/rpc.h> #include <gssrpc/types.h> #else #include <rpc/rpc.h> #include <rpc/types.h> #endif END my $var2 = <<END; #ifdef _USE_GSSRPC #include <gssrpc/rpc.h> #include <gssrpc/types.h> #include <gssrpc/svc.h> #include <gssrpc/auth.h> #else #include <rpc/rpc.h> #include <rpc/types.h> #include <rpc/svc.h> #include <rpc/auth.h> #endif END my $var3 = <<END; #ifdef _USE_GSSRPC #include <gssrpc/rpc.h> #include <gssrpc/types.h> #include <gssrpc/auth.h> #include <gssrpc/pmap_clnt.h> #else #include <rpc/rpc.h> #include <rpc/types.h> #include <rpc/auth.h> #include <rpc/pmap_clnt.h> #endif END my $var4 = <<END; #ifdef _USE_GSSRPC #include <gssrpc/rpc.h> #include <gssrpc/rpc.h> #include <gssrpc/svc.h> #else #include <rpc/rpc.h> #include <rpc/types.h> #include <rpc/svc.h> #endif END my $var5 = <<END; #ifdef _USE_GSSRPC #include <gssapi/gssapi.h> #include <gssapi/gssapi_krb5.h> #include <gssrpc/rpc.h> #else #include <rpc/rpc.h> #endif END my $var6 = <<END; #ifdef _USE_GSSRPC #include <gssrpc/rpc.h> #include <gssrpc/rpc.h> #include <gssrpc/svc.h> #include <gssrpc/pmap_clnt.h> #else #include <rpc/rpc.h> #include <rpc/types.h> #include <rpc/svc.h> #include <rpc/pmap_clnt.h> #endif END my $var7 = <<END; #ifdef _USE_GSSRPC #include <gssapi/gssapi.h> #include <gssapi/gssapi_krb5.h> #include <gssrpc/rpc.h> #include <gssrpc/rpc.h> #include <gssrpc/svc.h> #else #include <rpc/rpc.h> #include <rpc/types.h> #include <rpc/svc.h> #endif END my $var8 = <<END; #ifdef _USE_GSSRPC #include <gssrpc/types.h> #include <gssrpc/rpc.h> #include <gssrpc/auth.h> #include <gssrpc/pmap_clnt.h> #else #include <rpc/types.h> #include <rpc/rpc.h> #include <rpc/auth.h> #include <rpc/pmap_clnt.h> #endif END foreach my $file (@ARGV) { open(IN, $file); undef $/; my $content = <IN>; close(IN); $content =~ s/$var/#include "rpc.h"/is; $content =~ s/$var2/#include "rpc.h"/is; $content =~ s/$var3/#include "rpc.h"/is; $content =~ s/$var4/#include "rpc.h"/is; $content =~ s/$var5/#include "rpc.h"/is; $content =~ s/$var6/#include "rpc.h"/is; $content =~ s/$var7/#include "rpc.h"/is; $content =~ s/$var8/#include "rpc.h"/is; open(OUT, ">$file"); print OUT $content; close(OUT); } �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tools/test_findlog.c����������������������������������������������������������0000664�0000000�0000000�00000005624�13242724102�0020471�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// This is not a valid C file, it's just a bunch of Log function examples to test findlog.sh /* LogTest("/* comment"); */ /* * Copyright IBM Corporation, 2011 * Contributor: Frank Filz <ffilz@us.ibm.com> * * * This software is a server that implements the NFS protocol. * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* LogTest("/* multiline comment"); */ LogTest("tab"); LogTest("space"); // LogTest("// comment"); // LogTest("2nd // comment"); LogTest("space before semi"); LogTest("space after semi"); LogTest("function with ; in quotes"); LogTest("parm 1", "parm 2"); LogTest("parm 1", "parm 2 with space after semi"); LogTest("parm 1 with ;", "parm 2 with space after semi"); LogTest("parm 1", "parm 2 with ;"); LogTest("parm 1", "parm 2"); LogTest("paren on new line"); LogVal = "dont find me" LogTest("But do find me"); #define MACRO \ LogWarn(COMPONENT_CONFIG, \ "MACRO") /* expected too much - need to figure out how to fix the script */ #define MACRO \ LogWarn(COMPONENT_CONFIG, \ "MACRO") \ { } /* expected too much - need to figure out how to fix the script */ LogComponents[dont find me]; LogAlways("but find me on line 53"); LogTest("and me", string, buff, compare); LogTest("and finally me"); #define MACRO1 \ LogTest("M1 A", \ parms); \ LogTest("M1 B", parms); \ } #define MACRO2 \ LogTest("M2 A", \ parms); \ LogTest("M2 B", parms); \ LogTest("M2 C", parms); \ } else if (!STRCMP(key_name, "LogFile not me 1")) LogFile = "not me 2"; else if (!STRCMP(key_name, "not me 3")) LogCrit(COMPONENT_CONFIG, "find me 1 on 76", key_name); else if (!STRCMP(key_name, "LogFile not me 5")) LogFile = "not me 6"; LogCrit(COMPONENT_CONFIG, "find me 2 on 82", key_name); (Logtest("in parens")); LogTest("foo"); if (LogTest("simple if")) foo; foo("oops shouldn't pick this up"); if (LogTest("if with braces on same line")) { foo; } foo("oops shouldn't pick this up"); if (LogTest("if with braces")) { foo; } foo("oops shouldn't pick this up"); if (LogTest("if with braces 2")) { foo(); } foo("oops shouldn't pick this up"); LogTest("fini"); ������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tracing/����������������������������������������������������������������������0000775�0000000�0000000�00000000000�13242724102�0016124�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tracing/CMakeLists.txt��������������������������������������������������������0000664�0000000�0000000�00000000560�13242724102�0020665�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������include_directories( ${LTTNG_INCLUDE_DIR} ) set(ganesha_trace_LIB_SRCS fsal_mem.c logger.c mdcache.c nfs_rpc.c state.c ) add_library(ganesha_trace MODULE ${ganesha_trace_LIB_SRCS}) add_sanitizers(ganesha_trace) target_link_libraries(ganesha_trace ${LTTNG_LIBRARIES} ) install(TARGETS ganesha_trace COMPONENT tracing DESTINATION ${FSAL_DESTINATION} ) ������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tracing/README.md�������������������������������������������������������������0000664�0000000�0000000�00000013071�13242724102�0017405�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Building and using LTTng an enabled server ========================================== LTTng is a big topic. Consult the online documentation for the details of running the tools. The development is being done using Fedora using their packages, version 2.3.0. Install the following RPMs on Fedora or, if on RHEL, use the EPEL repo. There are other lttng related packages but these are the relevant ones for this level. lttng-tools lttng-ust-devel lttng-ust babeltrace Build with `-DUSE_LTTNG=ON` to enable it. All of the tracepoints have an `#ifdev USE_LTTNG` around them so the default of 'OFF' creates no build or runtime dependencies on lttng. The build creates and installs an additional shared object. The `libganesha_trace.so` object which contains all the defined tracepoint code and the code in the application that uses it are constructed such that the tracepoint code does not require the shared object to be loaded. This means that the application can be built with LTTng code but it can be run on a system that has no LTTng support. Tracepoints are enabled once the module is loaded. See below for details. I use the following script to run: ``` #!/bin/sh DIR=/usr/local/lib64/ganesha lttng create lttng enable-event -u -a lttng start LD_PRELOAD=$DIR/libganesha_trace.so /usr/local/bin/ganesha.nfsd ${*} ``` In this case, I am preloading `libganesha_trace.so` which turns on tracing before before the server starts. If you do not preload, ganesha runs as if tracing is not there and the only overhead is the predicted missed branch to the tracer. LTTng supports the loading of the tracing module into a running application by loading (dlopen) the modul This would be useful for production environments but that feature is on the TODO list. There are never enough tracepoints. Like log messages, they get added from time to time as needed. There are two paths for adding tracepoints. - Sets of tracepoints are categorized in *components*. These should conform to the same general categories as logging components. All the tracepoints in a component should be functionally related in some way. - Finer grained tracing adds new tracepoints to an existing category. Creating a New Component ------------------------ Two files must be created and a number of other files must be edited to create a new tracepoint component. The tracepoint itself is created as a new include (.h) file in `src/include/gsh_lttng`. This is the bulk of the work. Note that the file has a specific form. It is best to copy and edit an existing file so as to get all the necessary definitions in the right places. There can be any number of tracepoint definitions but each one *must* have a `TRACEPOINT_EVENT` and `TRACEPOINT_LOGLEVEL` defined (after it). The tracepoint include file has a companion file in src/tracing that includes the header after defining `TRACEPOINT_CREATE_PROBES`. Add this file to the sources list in tracing/CMakeLists.txt so that it gets built into the tracing module. Every source file that will use these tracepoints must also include the specific include file(s) for the tracepoint component(s). Note that components are defined separately. Both the #include and the tracepoints themselves should be wrapped with an `#ifdef USE_LTTNG`. The CMakeLists.txt file of a source sub-directory should be edited to add a conditional `include_directories` command so that `LTTNG_INCLUDE_DIR` will added to the includes path. The last step in adding a component is to add an `#include` of the new header in `MainNFSD/nfs_main.c`. This bit is LTTng magic to set up the dynamic linkage. Every component header file is listed here ONLY ONCE. This includes any tracepoints that may be added to `nfs_main.c`. Adding New Tracepoints ---------------------- Adding a new tracepoint to an existing component is really simple. First, add the `TRACEPOINT_EVENT` and its `TRACEPOINT_LOGLEVEL` to the component's header file. This is all that is necessary to create the tracepoint. The next step is to add the new tracepoint into the code. There are a few extra bits to remember in adding the tracepoint. - If the tracepoint is added to a file in a subdirectory that had no tracepoints before, add the `include_directories` directive to the CMakeLists.txt file. - If the tracepoint category is new to the source file, add a `#include` for it. - Wrap the #include and all tracepoints with `USE_LTTNG`. Notes on Using Tracepoints ========================== All this trace point organization is for the "enable-event" command above. The `-a` turns them all on. If all you wanted was logging, you would change the `-a` to `ganesha_logger:log`. See the LTTng docs for the details. Tracepoint logs are placed in your `$HOME/lttng-traces` directory. Note that if you are running as 'root' which is necessary for running nfs-ganesha, this directory is actually `/root/lttng-traces`, not your regular `$HOME`. The lttng commands will report the subdirectory where the traces are placed. You can also specify a directory name if you like. If the lttng command reports `auto-20140804-102010` as the trace directory, you can find those files in `$HOME/lttng-traces/auto-20140804-102010`. The `babeltrace` tool is used to process and display traces. There are others but this is the simplest and most general tool. To do a simple dump of the trace from above, use: ``` $ babeltrace $HOME/lttng-traces/auto-20140804-102010 ``` This will dump a trace in text form. See the man page for all the options. There are a number of other tools that can also munch traces. Traces are in a common format that many tools can read and process/display them. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tracing/fsal_mem.c������������������������������������������������������������0000664�0000000�0000000�00000000101�13242724102�0020043�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#define TRACEPOINT_CREATE_PROBES #include "gsh_lttng/fsal_mem.h" ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tracing/logger.c��������������������������������������������������������������0000664�0000000�0000000�00000000077�13242724102�0017553�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#define TRACEPOINT_CREATE_PROBES #include "gsh_lttng/logger.h" �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tracing/mdcache.c�������������������������������������������������������������0000664�0000000�0000000�00000000100�13242724102�0017643�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#define TRACEPOINT_CREATE_PROBES #include "gsh_lttng/mdcache.h" ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tracing/nfs_rpc.c�������������������������������������������������������������0000664�0000000�0000000�00000000126�13242724102�0017721�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#define TRACEPOINT_CREATE_PROBES #include "nfs_core.h" #include "gsh_lttng/nfs_rpc.h" ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/tracing/state.c���������������������������������������������������������������0000664�0000000�0000000�00000000076�13242724102�0017413�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#define TRACEPOINT_CREATE_PROBES #include "gsh_lttng/state.h" ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������nfs-ganesha-2.6.0/src/valgrind.sh�������������������������������������������������������������������0000775�0000000�0000000�00000000143�13242724102�0016640�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/sh valgrind --leak-check=full --max-stackframe=3280592 --log-file=/tmp/valgrind.log $* �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������